Hook 内核函数(KiReadyThread)检测进程
来源:百度文库 编辑:神马文学网 时间:2024/03/29 09:43:15
1. 介绍通用Hook内核函数的方法
当我们要拦截目标函数的时候,只要修改原函数头5个字节的机器代码为一个JMP XXXXXXXX(XXXXXXXX是距自己的Hook函数的偏移量)就行了。并且保存原来修改前的5个字节。在跳入原函数时,恢复那5个字节即可。
char JmpMyCode [] = {0xE9,0x00,0x00,0x00,0x00};//E9对应Jmp偏移量指令
*((ULONG*)(JmpMyCode+1))=(ULONG)MyFunc-(ULONG)OrgDestFunction-5;//获得偏移量
memcpy(OrgCode,(char*)OrgDestFunction,5);//保存原来的代码
memcpy((char*)OrgDestFunction,JmpMyCode,5);//覆盖前一个命令为一个跳转指令
在系统内核级中,MS的很多信息都没公开,包括函数的参数数目,每个参数的类型等。在系统内核中,访问了大量的寄存器,而很多寄存器的值,是上层调用者提供的。如果值改变系统就会变得不稳定。很可能出现不可想象的后果。另外有时候对需要Hook的函数的参数不了解,所以不能随便就去改变它的堆栈,如果不小心也有可能导致蓝屏。所以Hook的最佳原则是在自己的Hook函数中呼叫原函数的时候,所有的寄存器值,堆栈里面的值和Hook前的信息一样。这样就能保证在原函数中不会出错。一般我们自己的Hook的函数都是写在C文件里面的。例如Hook的目标函数KiReadyThread。那么一般就自己实现一个:
MyKiReadyThread(...)
{
......
call KiReadyThread
......
}
但是用C编译器编译出来的代码会出现一个堆栈帧:
Push ebp
mov ebp,esp
这就和我们的初衷不改变寄存器的数违背了。所以我们可以自己用汇编来实MyKiReadyThread。
_MyKiReadyThread @0 proc
pushad ;保存通用寄存器
call _cfunc@0 ;这里是在进入原来函数前进行的一些处理。
popad ;恢复通用寄存器
push eax
mov eax,[esp+4] ;得到系统在call 目标函数时入栈的返回地址。
mov ds:_OrgRet,eax ;保存在一个临时变量中
pop eax
mov [esp],retaddr ;把目标函数的返回地址改成自己的代码空间的返回地址,使其返回后能接手继续的处理
jmp _OrgDestFunction ;跳到原目标函数中
retaddr:
pushad ;原函数处理完后保存寄存器
call _HookDestFunction@0 ;再Hook
popad ;回复寄存器
jmp ds:_OrgRet ;跳到系统调用目标函数的下一条指令。
_MyKiReadyThread@0 endp
在实现了Hook过后在当调用原来的函数时(jmp _OrgDestFunction),这个时候所以寄存器的值和堆栈信息和没Hook的时候一样。在返回到系统的时候(jmp ds:_OrgRet),这个时候的堆栈信息和寄存器的值和没有Hook的时候也是一样。就说是中间Hook层对下面和上面都是透明的。
2. 检测隐藏进程
在线程调度抢占的的时候会调用KiReadyThread,它的原型为:
VOID FASTCALL KiReadyThread (IN PRKTHREAD Thread);
在进入KiReadyThread时,ecx指向Thread。所以完全可以Hook KiReadyThread 然后用ecx的值得到但前线程的进程信息。KiReadyThread没被ntosknrl.exe导出,所以通过硬编码来。在2000Sp4中地址为0x8043141f。
void cfunc (void)
{
ULONG PKHeader=0;
__asm
{
mov PKHeader,ecx //ecx寄存器是KiReadyThread中的PRKTHREAD参数
}
ResumeDestFunction(); //恢复头5个字节
if ( PKHeader != 0 )
{
DisplayName((PKTHREAD)PKHeader);
}
}
cfun是Hook函数调用用来得到当前线程抢占的进程信息的。
void DisplayName(PKTHREAD Thread)
{
PKPROCESS Process = Thread->ApcState.Process;
PEPROCESS pEprocess = (PEPROCESS)Process;
DbgPrint("ImageFileName = %s \n",pEprocess->ImageFileName);
}
void HookDestFunction() //设置头个字节为一个跳转指令,跳到自己的函数中去
{
DisableWriteProtect(&orgcr0);
memcpy((char*)OrgDestFunction,JmpMyCode,5);
EnableWriteProtect(orgcr0);
}
void ResumeDestFunction() //恢复头5个字节
{
DisableWriteProtect(&orgcr0);
memcpy((char*)OrgDestFunction,OrgCode,5);
EnableWriteProtect(orgcr0);
}
除了KiReadyThread其他还可以Hook其他内核函数,只有hook过后能得到线程或者是进程的ETHREAD或者是EPROCESS结构头地址。其Hook的方法都是一样的。Hook KiReadyThread基本原来说明了,详细实现可以见我的另外一篇文章《内核级利用通用Hook函数方法检测进程》。
当我们要拦截目标函数的时候,只要修改原函数头5个字节的机器代码为一个JMP XXXXXXXX(XXXXXXXX是距自己的Hook函数的偏移量)就行了。并且保存原来修改前的5个字节。在跳入原函数时,恢复那5个字节即可。
char JmpMyCode [] = {0xE9,0x00,0x00,0x00,0x00};//E9对应Jmp偏移量指令
*((ULONG*)(JmpMyCode+1))=(ULONG)MyFunc-(ULONG)OrgDestFunction-5;//获得偏移量
memcpy(OrgCode,(char*)OrgDestFunction,5);//保存原来的代码
memcpy((char*)OrgDestFunction,JmpMyCode,5);//覆盖前一个命令为一个跳转指令
在系统内核级中,MS的很多信息都没公开,包括函数的参数数目,每个参数的类型等。在系统内核中,访问了大量的寄存器,而很多寄存器的值,是上层调用者提供的。如果值改变系统就会变得不稳定。很可能出现不可想象的后果。另外有时候对需要Hook的函数的参数不了解,所以不能随便就去改变它的堆栈,如果不小心也有可能导致蓝屏。所以Hook的最佳原则是在自己的Hook函数中呼叫原函数的时候,所有的寄存器值,堆栈里面的值和Hook前的信息一样。这样就能保证在原函数中不会出错。一般我们自己的Hook的函数都是写在C文件里面的。例如Hook的目标函数KiReadyThread。那么一般就自己实现一个:
MyKiReadyThread(...)
{
......
call KiReadyThread
......
}
但是用C编译器编译出来的代码会出现一个堆栈帧:
Push ebp
mov ebp,esp
这就和我们的初衷不改变寄存器的数违背了。所以我们可以自己用汇编来实MyKiReadyThread。
_MyKiReadyThread @0 proc
pushad ;保存通用寄存器
call _cfunc@0 ;这里是在进入原来函数前进行的一些处理。
popad ;恢复通用寄存器
push eax
mov eax,[esp+4] ;得到系统在call 目标函数时入栈的返回地址。
mov ds:_OrgRet,eax ;保存在一个临时变量中
pop eax
mov [esp],retaddr ;把目标函数的返回地址改成自己的代码空间的返回地址,使其返回后能接手继续的处理
jmp _OrgDestFunction ;跳到原目标函数中
retaddr:
pushad ;原函数处理完后保存寄存器
call _HookDestFunction@0 ;再Hook
popad ;回复寄存器
jmp ds:_OrgRet ;跳到系统调用目标函数的下一条指令。
_MyKiReadyThread@0 endp
在实现了Hook过后在当调用原来的函数时(jmp _OrgDestFunction),这个时候所以寄存器的值和堆栈信息和没Hook的时候一样。在返回到系统的时候(jmp ds:_OrgRet),这个时候的堆栈信息和寄存器的值和没有Hook的时候也是一样。就说是中间Hook层对下面和上面都是透明的。
2. 检测隐藏进程
在线程调度抢占的的时候会调用KiReadyThread,它的原型为:
VOID FASTCALL KiReadyThread (IN PRKTHREAD Thread);
在进入KiReadyThread时,ecx指向Thread。所以完全可以Hook KiReadyThread 然后用ecx的值得到但前线程的进程信息。KiReadyThread没被ntosknrl.exe导出,所以通过硬编码来。在2000Sp4中地址为0x8043141f。
void cfunc (void)
{
ULONG PKHeader=0;
__asm
{
mov PKHeader,ecx //ecx寄存器是KiReadyThread中的PRKTHREAD参数
}
ResumeDestFunction(); //恢复头5个字节
if ( PKHeader != 0 )
{
DisplayName((PKTHREAD)PKHeader);
}
}
cfun是Hook函数调用用来得到当前线程抢占的进程信息的。
void DisplayName(PKTHREAD Thread)
{
PKPROCESS Process = Thread->ApcState.Process;
PEPROCESS pEprocess = (PEPROCESS)Process;
DbgPrint("ImageFileName = %s \n",pEprocess->ImageFileName);
}
void HookDestFunction() //设置头个字节为一个跳转指令,跳到自己的函数中去
{
DisableWriteProtect(&orgcr0);
memcpy((char*)OrgDestFunction,JmpMyCode,5);
EnableWriteProtect(orgcr0);
}
void ResumeDestFunction() //恢复头5个字节
{
DisableWriteProtect(&orgcr0);
memcpy((char*)OrgDestFunction,OrgCode,5);
EnableWriteProtect(orgcr0);
}
除了KiReadyThread其他还可以Hook其他内核函数,只有hook过后能得到线程或者是进程的ETHREAD或者是EPROCESS结构头地址。其Hook的方法都是一样的。Hook KiReadyThread基本原来说明了,详细实现可以见我的另外一篇文章《内核级利用通用Hook函数方法检测进程》。
Hook 内核函数(KiReadyThread)检测进程
HOOK(钩子)函数的详细说明
PHP通用检测函数集合
PHP通用检测函数集合
[原]UNIX内核(12):进程的结构——上下文
[原]UNIX内核(12):进程的结构——上下文 - 操作系统 - raof01-Hast...
RTX51 Tiny 2.02 中文手册 内核函数
利用伪造内核文件来绕过IceSword的检测
利用伪造内核文件来绕过IceSword的检测
linux内核的 等待队列 使用方法,wait_queue_head_t,进程休眠
Spring内核研究-set方法注入和构造函数注入
gcc内嵌函数__builtin_types_compatible_p 在内核中的一个实例...
关于Hook
[分享]未来函数详解、检测方法 ← 通达信论坛
对Linux内核中进程上下文和中断上下文的理解《转》
linux内核编程(一)
通过内核源码看函数调用之前世今生 - 极光 - CSDN博客
使用ftrace抓Linux内核函数调用轨迹 - ubuntu - On the way
浅谈api hook技术
跨进程API Hook
linux内核编程(一) - csdn 新闻
标函数(Cursor)
金字塔(引用函数)
与字符串数组有关的三个函数(Split函数、Join函数、Filter函数)