赞
踩
注入DLL的思路步骤:
1. 在目标进程中申请一块内存空间(使用VirtualAllocEx函数) 存放DLL的路径,方便后续执行LoadLibraryA
2. 将DLL路线写入到目标进程(使用WriteProcessMemory函数)
3. 获取LoadLibraryA函数地址(使用GetProcAddress),将其做为线程的回调函数
4. 在目标进程 创建线程并执行(使用CreateRemoteThread)
std::string temp = W2Astring(strDllPath); int dllLen = temp.size(); const char* pPath = temp.c_str(); // 1.目标进程申请空间 LPVOID pDLLPath = VirtualAllocEx(targetProc,NULL,dllLen,MEM_COMMIT,PAGE_READWRITE ); if( pDLLPath == NULL ) { TRACE(_T("CUtility::InjectDllToProc VirtualAllocEx failed\n")); return false; } SIZE_T wLen = 0; // 2.将DLL路径写进目标进程内存空间 int ret = WriteProcessMemory(targetProc,pDLLPath,pPath,dllLen,&wLen); // 这里pPath不能直接使用strDllPath if( ret == 0 ) { VirtualFreeEx(targetProc, pDLLPath, dllLen, MEM_DECOMMIT); TRACE(_T("CUtility::InjectDllToProc WriteProcessMemory failed\n")); return false; } // 3.获取LoadLibraryA函数地址 FARPROC myLoadLibrary = GetProcAddress(GetModuleHandleA("kernel32.dll"),"LoadLibraryA"); if( myLoadLibrary == NULL ) { VirtualFreeEx(targetProc, pDLLPath, dllLen, MEM_DECOMMIT); TRACE(_T("CUtility::InjectDllToProc GetProcAddress failed\n")); return false; } // 4.在目标进程执行LoadLibrary 注入指定的线程 HANDLE tHandle = CreateRemoteThread(targetProc,NULL,NULL, (LPTHREAD_START_ROUTINE)myLoadLibrary,pDLLPath,NULL,NULL); if(tHandle == NULL) { VirtualFreeEx(targetProc, pDLLPath, dllLen, MEM_DECOMMIT); TRACE(_T("CUtility::InjectDllToProc CreateRemoteThread failed\n")); return false; } WaitForSingleObject(tHandle,INFINITE); VirtualFreeEx(targetProc, pDLLPath, dllLen, MEM_DECOMMIT); CloseHandle(tHandle); return true; }
卸载钩子
/*
卸载步骤和注入DLL步骤实质差不多.
注入DLL是 在目标进程中执行LoadLibraryA
卸载DLL是 在目标进程中执行FreeLibrary函数,不同的是卸载不需要再目标进程中申请空间,
因为FreeLibrary参数为HMODULE 实际上就是一个指针值。这个句柄已经加载就已经存在。
*/
dll注入,入口
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { InlineHookIE(); } break; case DLL_PROCESS_DETACH: UnInlineHookIE(); break; } return TRUE; }
相当生成dll(main主方法-》InlinkHookie方法->g_inlineHookObj7類的方法hook(获取模块句柄、获取进程地址、读取当前进程内存,7个字节,并储存起来、修改进程内存7个字节))
相當注入器:把dll注入到exe
事件觸發執行dll注入到exe(// 获取到的每个EXE进程列表句柄都进行DLL注入)
注入DLL的思路步骤:
1. 在目标进程中申请一块内存空间(使用VirtualAllocEx函数) 存放DLL的路径,方便后续执行LoadLibraryA
2. 将DLL路线写入到目标进程(使用WriteProcessMemory函数)
3. 获取LoadLibraryA函数地址(使用GetProcAddress),将其做为线程的回调函数
4. 在目标进程 创建线程并执行(使用CreateRemoteThread)
*/
注入种类:
远程线程注入,消息钩子注入和输入法注入和注册表注入和eip注入和apc注入和内存注入(inlinkhook)iat注入
1、 Ring3 层的 Hook 基本上可以分为两种大的类型:
第一类即是 Windows 消息的 Hook,第二类则是 Windows API 的 Hook。
消息 Hook 是通过 SetWindowsHookEx 可以实现将自己的钩子插入到钩子链的最前端,
而对于发送给被 Hook 的窗口(也有可能是所有的窗口,即全局 Hook)的消息都会被我们的钩子处理函数所捕获到,
也就是我们可以优先于窗体先捕获到这些消息
(1)进程内消息 Hook:可以简单的将 Hook 处理函数直接写在这个进程内,即是自己 Hook 自己。
(2)全局消息HOOK:
需要将 Hook 处理函数写在一个 DLL 中,这样才可以让你的处理函数被所有的进程所加载(进程自动加载包含 Hook 消息处理函数的 DLL)。
2、Ring0 层的 Hook
(1)SSDT hook,一句话——Windows把需要调用的内核API地址全都保存在一个表中(System Service Dispatch Table),要想hook一个内核API,比较简单的办法就是把该内核API在表(SSDT)中保存的地址修改为自己撰写的函数地址。
(2)HOOK INT 2E 方法:IDT HOOK
IDT是中断描述表,可以替换其中的中断处理程序。
这种方法对于跟踪、分析系统调用来说用的比较多。原理是通过替换 IDT
表中的 INT 2E 中断,使之指向我们自己的中断服务处理例程来实现的。掌握
此方法需要你对保护模式有一定的基础。
(3) HOOK PE 方法:EAT HOOK
这种方法对于拦截、分析其他内核驱动的函数调用来说用的比较多。原理
是根据替换 PE 格式导出表中的相应函数来实现的。
EAT是可执行文件的导出表,记录DLL中可供其他程序使用的函数,可执行文件装载时会使用相应DLL的EAT表来初始化IAT表,通过替换EAT表中的函数地址,就可以使依赖于本DLL的程序得到一个假的地址。
(4)IAT HOOK (ring3 用)
IAT是可执行文件的导入表,记录可执行文件使用的其它DLL中的函数,通过替换IAT表中的函数地址,可以hook相应DLL中的函数调用。
(5)Inline Hook方法 (ring 0和ring3 都可以用)
Inline hook的工作流程:
1)验证内核API的版本(特征码匹配)。
2)撰写自己的函数,要完成以上三项任务。
2)获取自己函数的地址,覆盖内核API内存,供跳转。
Inline Hook的缺点:
1) 不够通用。各个windows版本中,内核API的开始一段不尽相同,要想通吃,就要多写几个版
本或者做一个特征码搜索(因为有的内核API在各个版本中非常相似,只是在“特征码”之前或之
后加一点东西)。
2) 已被一些检测工具列入检测范围,如果直接从内核API第一个字节开始覆盖,那么很容易被检
测,如果把覆盖范围往后推,并加以变形,也许能抵挡一气。具体情况,我才疏学浅,尚未试验
(6)SYSENTRY hook
为了性能的考虑,xp后的系统都改用sysentry命令来进入ring0,去调用SSDT中的服务,不再是通过IDT中的 int 2E。这也使得我们hook也变得相对容易了。
首先获得sysentry的地址,然后改之,不用再考虑IDT了
(7)IRP hook
IRP是 I/O request packets,驱动程序中有一系列分发例程来处理请求,这些例程保存在驱动设备对象的数据结构中的一个表中,也很容易替换。
三、HOOK(钩子)的工作原理
当创建一个钩子时,WINDOWS会先在内存中创建一个数据结构,该数据结构包含了钩子的相关信息,然后把该结构体加到已经存在的钩子链表中去。新的钩子将加到老的前面。当一个事件发生时,如果安装的是一个线程钩子,进程中的钩子函数将被调用。如果是一个系统钩子,系统就必须把钩子函数插入到其它进程的地址空间,要做到这一点要求钩子函数必须在一个动态链接库中。
当然有两个例外:工作日志钩子和工作日志回放钩子。这两个钩子的钩子函数必须在安装钩子的线程中。原因是:这两个钩子是用来监控比较底层的硬件事件的,既然是记录和回放,所有的事件就当然都是有先后次序的。所以如果把回调函数放在DLL中,输入的事件被放在几个线程中记录,所以我们无法保证得到正确的次序。故解决的办法是:把钩子函数放到单个的线程中,譬如安装钩子的线程。
几点需要说明的地方:
(1) 如果对于同一事件(如鼠标消息)既安装了线程钩子又安装了系统钩子,那么系统会自动先调用线程钩子,然后调用系统钩子。
(2) 对同一事件消息可安装多个钩子处理过程,这些钩子处理过程形成了钩子链。当前钩子处理结束后应把钩子信息传递给下一个钩子函数。而且最近安装的钩子放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。
(3) 钩子特别是系统钩子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装钩子,在使用完毕后要及时卸载。
消息HOOK
原理:
用户输入消息,消息被放到系统消息队列。
程序发生了某些需要获取输入的事件,就从系统消息队列拿出消息放到程序消息队列中。
应用程序检测到有新的消息进入到程序消息队列中后,调用相应的事件去处理该消息。
所以在系统消息队列与程序消息队列的中间安装hook,即可获取消息队列中的信息。
安装:
SetWindowsHookEx(键盘消息(WH_xxx),Hook函数(处理键盘输入的函数),句柄(hook函数所在的DLL的句柄),线程ID(要hook的线程ID,0为所有线程))
API在简单高效的同时也有一个弊端,就是它只能监视较少的消息,如:击键消息、鼠标移动消息、窗口消息。
SEH(调试)HOOK
原理:与调试器工作方式类似,让进程发生异常,然后自己捕获到异常,对于除于被调试状态下的级进行操作。
正常情况下,进程未被其他进程调试时,当进程发生异常事件,系统将捕获该事件,并进行事件处理。
当进程被其他进程调试时,处理该进程的异常事件的工作则交给了调试进程。(调试进程未处理或不关心的调试事件由系统处理)
调试HOOK的核心思路就是将API的第一个字节修改为0xCC(INT 3,留给调试工具的中断,调试工具运行完后,会将下一条指令手动替换回原先的代码),当API被调用时,由于触发了异常,控制权就被转交给调试器(调试进程)。
利用调试技术来HOOK API函数的相关步骤如下
· 1对想要钩取的进程进行附加操作,使之成为被调试者。
· 2将要钩取的API的起始地址的第一个字节修改为0xcc(或者使用硬件断点)。
· 3当调用目标API的时候,控制权就转移到调试器进程。
· 4执行需要的操作。
· 5脱钩,将API 函数的第一个字节恢复。
· 6运行相应的API。
注入HOOK
原理:Hook的核心思想就是修改API的代码,使用DLL注入技术,我们将Hook的代码写入一个DLL(或直接一个shellcode),将此DLL注入到目标进程中,此时因为DLL在目标进程的内存中,所以就有权限直接修改目标进程内存中的代码了。
shellcode:填充数据,利用软件漏洞而执行的代码,可在暂存器eip溢出后,塞入一段可让CPU执行的shellcode机器码,让电脑可以执行攻击者的任意指令。
API HOOK:又分为IAT HOOK和inline HOOK,都是改变函数以达到跳转到自己的HOOK API的目的。但又有差别。
一、
1.IAT HOOK:通过修改IAT(导入表)的函数地址,实现对API进行HOOK。在把原函数替换成目标函数,并在目标函数执行完后,必须要调用回原函数(HOOK前应保存原函数地址),这样才能保证功能的完整性。
· 1.计算出导入表的位置
· 2.在导入表中找到原函数的位置(即将被执行的函数),保存该函数的地址
· 3.将原函数地址改为目标函数,运行完目标函数后,调用回原函数。
注:该操作会将程序中所有调用被hook的函数变为调用hook后的函数,可加判断,如果为自己调用操作,则进行hook函数的处理,如果是系统调用,直接将数据调用给原函数去处理
2.inline HOOK:直接修改内存中任意函数的代码,将其劫持至Hook API。同时,它比IAT Hook的适用范围更广,因为只要是内存中有的函数它都能Hook,而后者只能Hook IAT表里存在的函数(有些程序会动态加载函数)。
inline HOOK的目标是系统函数,直接修改函数的前5字节,改为jmp目标函数地址,执行完后进行unhook(脱钩)操作,以便将原函数恢复。Hook的目的是当调用某个函数时,我们能劫持进程的执行流。现在我们已经劫持了进程的执行流,便可以恢复原函数代码,以便我们的恶意代码可以正常调用。
· 1.获取原API地址,保存起来(方便后续还原)
· 2.修改内存属性为RWX(即read读(编号4)write写(2)execute执行(1))
· 3.备份原代码(同1)
· 4.实时计算JMP的相对偏移
· 5.最后修改API前5字节的代码(跳转到目标函数地址)
· 6.恢复内存属性。
注:
自编inline hook:从hook的位置跳到自己的函数中执行想要的动作,但需要将原指令再执行一遍,再跳转回到被hook的位置+指令长度的位置。以达成栈平衡的目的。
如果hook的位置为跳转指令,则需要将该指令所跳转的目标地址给计算并保存起来(普通跳转指令(非FF15 FF25 push时),跳转的为偏移量,一旦hook了偏移量就不再适用了),并在自己的函数中跳转到目标地址。
mhook库:编写一个参数 返回类型与被HOOK函数一样的新函数,hook后,代替原函数被调用,注意该hook只能hook一整个函数,并且会将所以调用原函数的行为用于调用新函数,因此应该加个判断条件,判断是系统进行调用还是我们要hook的程序进行调用。
二、
HotFix HOOK
原理:Code Hook存在一个效率的问题,因为每次Code Hook都要进行“挂钩+脱钩”的操作(对API的前5字节修改两次),当要进行全局Hook的时候,系统运行效率会受影响。而且,当一个线程尝试运行某段代码时,若另一个线程正在对该段代码进行“写”操作,会程序冲突,最终引发一些错误。
API的起始代码上都有这样的特点,5个NOP(空)指令,1个“MOV EDI,EDI”(占2字节),这7字节的指令实际没有任何意义,因此可以通过修改这7字节来实现HOOK操作,这种方法可以使得进程处于运行状态时临时更改进程内存中的库文件,因此被称为打“热补丁”。
在上述5字节代码修改技术中,unhook(脱钩)是为了调用原函数,但使用HotFix HOOK API时,在API代码被修改的状态下仍然能够正常的调用原API(从[原API起始地址+2]开始,仍能正常调用原API,且 执行动作一致)。
· 1.将内存属性修改为RWX
· 2.计算HOOK函数与被HOOK函数之间的地址偏移
· 3.将JMP [得到的结果]写入原函数-5的位置(即5个NOP)
· 4.再将JMP-7写到原函数的位置(MOV EDI,EDI)
· 5.恢复内存属性。
由于HotFix Hook需要修改7个字节的代码,所以并不是所有API都适用这种方法,若不适用,请使用5字节代码修改技术。
SSDT HOOK
原理:SSDT Hook属于内核层Hook,也是最底层的Hook。由于用户层的API最后实质也是调用内核API(Kernel32->Ntdll->Ntoskrnl),所以该Hook方法最为强大。
SSDT(System Service Descriptor Table):系统服务描述符表
定义类型变量:
DD(Define Dword):双字类型,一个双字数据占4字节。DW:字类型,占2字节。DB:字节类型,占1字节
内核通过SSDT调用各种内核函数,SSDT就是一个函数表,只要得到一个索引值,就能根据这个索引值在该表中得到想要的函数地址。
SSDT所在地址后面的第一个32位数据即为SSDT的基地址,跳到基地址后,第一个位32位数据即为SSDT表中第一个函数的地址,对该地址反汇编后,就能得到该函数相关的信息(包括该函数的索引号)。
例:要找AABBCC函数的地址,先对该函数进行反汇编u nt!ZwAABBCC,得到它的索引号为0x12;那么它的地址为:基地址+0x12 对其反汇编后,即可得到该函数的详细信息。
· 1.修改内存属性为RWX(即read读(编号4)write写(2)execute执行(1))
· 2.实时计算JMP的相对偏移
· 3.备份原代码头5字节(同1)
· 4.将头5字节替换成2.的汇编码
· 5.运行完后还原头5字节
· 6.恢复内存属性。
例子及博主自身理解:
API HOOK——IAT HOOK实例
技巧:
》通过模块句柄,得到PE头:(PBYTE)PE头=(PBYTE)进程句柄
》通过PE头,获得dos头:PIMAGE_DOS_HEADER dos头=(PIMAGE_DOS_HEADER)PE头
》通过PE头和dos头,获得NT头 PIMAGE_NT_HEADERS NT头=(PIMAGE_NT_HEADERS)(PE头+ dos头->e_lfanew)
》通过NT头的结构成员的数组成员,获得导入表信息:
IMAGE_DATA_DIRECTORY 数据表=NT头->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
导入表的尺寸=数据表.size 起始地址(基址)=数据表.VirtualAddress
》 获取函数的地址(DWORD),GetProcAddress(GetModuleHandleA(DllName),ProcName)
》通过PE头+导入表基址定位到导入表:PIMAGE_IMPORT_DESCRIPTOR 导入表 = (PIMAGE_IMPORT_DESCRIPTOR)(PE头 + 导入表基址);
》通过对比DLL名 循环查找每个导入表,找到DLL所在的结构体
strcmp((char*)DLL名,(char*)(PE头+导入表-name))找到目标DLL所在结构体; PIMAGE_THUNK_DATA 结构体= (PIMAGE_THUNK_DATA)(PE头 + 导入表->FirstThunk);
》通过对比函数地址,循环查找每个结构体的u1.Function成员,找到目标函数结构体->u1.Function == (DWORD)要被替换的函数地址
!!切记,应在32位下的Release版本生成dll,否则替换新的函数地址处会出错
》找到目标函数位置后,替换成新的函数的地址,pthunk->u1.Function = NewFuncAddress;
然后将位置(这位置保存的是地址)保存起来,方便事后还原。(保存至全局变量)
DWORD g_dwIatAddr = (DWORD)&pthunk->u1.Function;(<-保存一个指针指向的变量所保存的地址)
事后还原:DWORD * pdwAddr = (DWORD*)g_dwIatAddr;//把旧地址所在的位置传递给一个指针
*pdwAddr = oldFuncAddress; //将旧地址放入该位置 达到还原
注意,每次更改导入表的函数地址时,都应该调用VirtualProtect来修改保护属性
VirtualProtect((LPVOID)&pthunk->u1.Function, 4, PAGE_EXECUTE_READWRITE, &oldprotect);
通过REG注入DLL (即通过注册表HOOK)
平时用程序来进行DLL注入时,需要被注入程序在运行起来之后才能进行注入,在某些初始化阶段就想通过HOOK来控制程序时,达不到效果。此时,我们可以通过注册表表进行注入,被注入的程序在运行前就已经被HOOK。注:被注入的进程与DLL必须同为32/64位。
REG注入原理:利用在Windows 系统中,当REG以下键值中存在有DLL文件路径时,会跟随EXE文件的启动加载这个DLL文件路径中的DLL文件。当如果遇到有多个DLL文件时,需要用逗号或者空格隔开多个DLL文件的路径。
1.通过WIN+R运行regedit打开注册表
2.分别打开:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows
把路径下的AppInit_DLLs改为将要注入的DLL路径;
3.并将LoadAppInit_DLLs注册表项的值修改为1:(64位改2个 32位改1个)
注:注入表注入的DLL由mHOOK生成,因为注入表HOOK会给所有进程HOOK
所以要加入限制条件:获取当前进程名,当进程名=目标进程名时,往下执行HOOK的操作
自写inline hook
1.自定义一个全局变量 让hook只运行一次
2.通过进程IDGetProcessId(HANDLE(-1))得到当前进程ID 和程序名(自定义),获得目标进程的模块入口地址(即HOOK进程的基地址)(要HOOK的地方属于哪个模块,就获取哪个模块的句柄)
2/1.直接GetModuleHandle(“进程名”);获得模块入口地址或者通过下面4个步骤
2-1.根据进程ID获得进程快照,该快照包含所有包含该进程ID的模块 CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
2-2.通过该快照,获得与该进程相关的第一个模块的信息 Module32First(2-1返回的句柄, &存放模块信息的结构)
2-3.通过对比,找到目标模块srcmp(存放模块信息的结构.szModule, 进程名(即1的程序名)) == 0) ,如果不匹配,Module32Nex(2-1返回的句柄, &存放模块信息的结构)查看下一个。(进程名可通过任务管理器-详细信息查看)
2-4.找到目标模块,返回模块句柄存放模块信息的结构.hModule
3.得到要替换的原指令的绝对偏移位置RVA=VA-IB(要被替换的第一条指令—模块入口地址) (可通过MDebug查看,如要HOOK的为自定义函数,则该模块名称为进程名.exe,如果要HOOK的为系统函数,则模块名为该系统函数所在的DLL)(找到合适的HOOK位置是逆向的关键)
4.得到当前要hook的指令所在位置。原指令的绝对偏移地址(3得到的)+基地址(不定,每次加载时都容易改变,所以要通过2来求得)
5.保存等下要返回的地址 返回地址= 当前要hook的指令所在位置+hook指令的长度(5字节或以上,替换掉了多长就加多长)
6.保存原始指令内容UCHAR szOldInstruct[指令长度]={指令内容},指令内容为从左到右从上到下保存。 如不还原,则可不保存
7.计算跳转位置。DWORD 跳转位置 = (DWORD)自己编写函数(汇编编写)- 要hook的指令的起始位置- 指令所占长度;这样跳转后即能略过原本指令,执行自定义指令(远跳转指令e9占5字节,近跳转EB占2字节,跳转的位置为偏移量)
偏移量=目标地址 - 指令地址 - 指令长度
8.编写跳转指令UCHAR 跳转指令[5] = { 0xe9, 0, 0, 0, 0 };(e9为跳转指令jmp),如果指令长于5字节,则在地址后填充nop空指令(0x90)
也可定死为5字节,因为跳转指令由e9+4字节地址组成,而返回地址自动跳过了该指令的位置,所以只改前面5字节数据亦可
memcpy(跳转指令 + 1(e9占1位), &(3得到的), sizeof(3得到的));
9.修改要hook的指令所在位置的保护属性 VirtualProtect((LPVOID)(4得到的), 5, PAGE_EXECUTE_READWRITE, &保存旧属性的变量);
10.将要hook的指令替换成要跳转的指令 memcpy((LPVOID)(4得到的), (8得到的), sizeof((8得到的)));
11.还原指令所在位置的保护属性 此时inline hook完成
注:跳转指令所指的函数必须为汇编指令编写
通过上面例子生成DLL后,通过注入来HOOK实例
在生成完DLL后(具体参照前面例子),如有.lib文件 需拉到同一目录并加载
_T在项目定义了UNICODE时,等价于L,即(_T"123")同理(L"123"); 否则就是多字节。
在数组中 TCHAR如果定义了宽字节UNICODE TCHAR就为wchar_t 否则为char
DLL所在地址可用UnICODE,方便处理汉字这种双字节字符。
1.搜索指定类名或窗口名(窗口标题)findwindow(类名,窗口名);,得到窗口句柄。
2.GetWindowThreadProcessId(窗口句柄,&保存ID的变量);得到该进程的ID。
3.OpenProcess(PROCESS_ALL_ACCESS,FALSE,进程ID);得到该进程的句柄
4.VirtualAllocEx(进程句柄,内存地址,内存大小,分配方式(MEM_COMMIT),权限(PAGE_READWRITE));在该进程中申请一个内存空间(用于打开DLL)
5.将DLL路径写入对方进程中
WriteProcessMemory (进程句柄(3),内存位置(4),DLL所在地址(绝对路径),路径所占空间,&写入字节数)
注意,此时的路径含有汉字,用的为宽字符声明:L"c\xxx",所以可用wcslen( )求得长度,长度*2+2(结束符) 即得到宽字符路径所占空间
6.创建远程线程,让目标进程调用LoadLibrary来打开注入的DLL
CreateRemoteThread(进程句柄,安全属性(NULL),线程大小(0,即默认),目标线程调用的函数名(函数名即函数起始位置),
传递给函数的参数(此处为4申请的空间,该空间存放着DLL的路径),线程创建标志(NULL),线程ID的指针(不需要保存则为NULL) )
这里传递的函数名为LoadLibrary 用来加载DLL
7.检测句柄的信号状态WaitForSingleObject (线程句柄(6),等待时间(-1无限));当参1现场为有信号状态,或者到了参2时间,该函数返回。否则挂起
8.释放目标进程的空间VirtualFreeEx(进程句柄(3),空间首地址(4),空间大小(与申请时一致),释放类型(MEM_DECOMMIT));
注意:可直接跳到步骤3开始执行 进程ID从任务管理器-详细信息-目标进程的PID中获得
小技巧:如果想实现嵌套hook 应该保存被hook的函数指令,然后在hook的函数里面 用函数再次实现该指令,如果为jmp指令,则需获得跳转指令后接的4字节偏移地址 :因为跳转指令后接的为偏移量,而hook位置的地址为该指令所在的地址,并非偏移量
具体操作如下:1.memcpy(g_szOldInstruct, (char*)(HOOK的位置), 5); //将hook位置的指令保存下来(指令多长就复制多长 如果是jmp的话一般都是5字节E9 或2字节EB)
2.lea eax, dword ptr[g_szOldInstruct]; //取出这行指令的值(为E9 XXXX XXXX)
3 add eax, 1; mov eax, dword ptr[eax]; //+1为跳过e9,然后取4字节则为取出跳转的偏移地址
得到了偏移地址 就可以求得被hook的指令所要实现的跳转位置 跳转的目标地址=偏移地址+跳转指令长度+跳转指令所在的位置(即被hook前 这行指令所在的位置)
调试DLL是否执行成功,可在生成DLL的函数的代码段添加__asm int 3;来进行调试
在下断点后,如果运行起来一直会回到断点处,说明系统在该程序运行时会一直调用断点处的函数,此时应在hook函数前加判断条件,如果是自己操作的HOOK下来,如果是系统调用的,就调用原函数
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。