依稀记得第一次接触Hook的概念是在周伟民先生的书中-><<多任务下的数据结构与算法>>,当时觉得Hook的本质就是拦截,就算到现在也是如此认为。
本篇文章是在x86下测试与分析跳转+offset类型的Hook,并且逆推测出热补丁的简单用法,MinHook它的中心就是覆盖重写并且可以复原。知道大概的思路后后让我们先来具体的实现MinHook再去做测试。
首先是堆的申请,这是必要也必须做的,对于微软函数HeapCreate()就不再赘述,以下是实现与卸载
1 NTSTATUS WINAPI Initialize(VOID) 2 { 3 NTSTATUS Status = STATUS_SUCCESS; 4 5 EnterSpinLock(); 6 7 if (__HeapHandle == NULL) 8 { 9 __HeapHandle = HeapCreate(0,//申请堆栈 10 0, //提交 PAGE_SIZE 11 0); //If dwMaximumSize is 0, the heap can grow in size.自动增长 12 if (__HeapHandle != NULL) 13 { 14 //没有实现 15 } 16 else 17 { 18 Status = STATUS_MEMORY_NOT_ALLOCATED; 19 } 20 } 21 else 22 { 23 Status = STATUS_ADDRESS_ALREADY_EXISTS; 24 } 25 26 LeaveSpinLock(); 27 28 return Status; 29 } 30 31 NTSTATUS WINAPI Uninitialize(VOID) 32 { 33 NTSTATUS Status = STATUS_SUCCESS; 34 35 return Status; 36 }
当我们有了内存以后就可以大显身手了,所以显而易见的就要创建Hook即CreateHook,当然作为老油条不可避免的就要想到是不是要用到结构体来保存一些信息呢呢,显而易见是的,成员注释写在代码中
1 // Hook information. 2 typedef struct _HOOK_ENTRY 3 { 4 LPVOID TargetFunctionAddress; //目标地址 5 LPVOID FakeFunctionAddress; //Fake地址即覆盖地址 6 LPVOID TrampolineMemorySlot; // Address of the trampoline function. 7 UINT8 OriginalDataBackup[8]; // Original prologue of the target function.目标功能的原始序幕- //恢复Hook使用的存放原先数据 8 9 UINT8 PatchAbove : 1; // Uses the hot patch area. 位域:1位 10 UINT8 IsEnabled : 1; // Enabled.启用 11 UINT8 queueEnable : 1; // Queued for enabling/disabling when != isEnabled. 12 13 UINT IP : 4; // Count of the instruction boundaries. 想到汇编的IP就很明白了 14 UINT8 OldIPs[8]; // Instruction boundaries of the target function.原地址的字节变化就靠它了 15 UINT8 NewIPs[8]; // Instruction boundaries of the trampoline function 用在后续解释的MemorySlot中 16 } HOOK_ENTRY, *PHOOK_ENTRY; //44字节 17 18 19 typedef struct _HOOK_INFORMATION_ 20 { 21 PHOOK_ENTRY Items; // Data heap 22 UINT MaximumLength; // Size of allocated data heap, items 23 UINT Length; // Actual number of data items 24 }HOOK_INFORMATION,*PHOOK_INFORMATION;
好了让我们来写创建Hook的代码了,我们是严谨的编程者,当然要再次判断内存是否申请好了,是否可执行,判断是否已经Hook过了,但当已经Hook过后怎么办呢,当然是让他返回其所在位置,如下代码详解
1 UINT FindHookEntry(LPVOID FunctionAddress) 2 { 3 UINT i; 4 for (i = 0; i < __Hooks.Length; ++i) 5 { 6 if ((ULONG_PTR)FunctionAddress == (ULONG_PTR)__Hooks.Items[i].TargetFunctionAddress) 7 return i; 8 } 9 return STATUS_NOT_FOUND; 10 }
如果没Hook就来Hook吧,用到我们的跳板结构体TRAMPOLINE,为什么有它呢,我说过Hook的含义就是拦截,那拦截后怎么办呢,当然是一个跳转,到自己去更改的地方,所以有TRAMPOLINE结构体的出现,结构体里各个成员的解释与意义,就不再赘述。
1 typedef struct _TRAMPOLINE 2 { 3 LPVOID TargetFunctionAddress; // [In] Address of the target function. 4 LPVOID FakeFunctionAddress; // [In] Address of the detour function. 5 LPVOID MemorySlot; // MemorySlot 32字节原函数地址的前五个字节和跳转指令后的字节 6 7 #if defined(_M_X64) || defined(__x86_64__) 8 LPVOID pRelay; // [Out] Address of the relay function. 9 #endif 10 BOOL PatchAbove; // [Out] Should use the hot patch area? //Patch --->热补丁哦 //0xA 0xB 11 UINT IP; // [Out] Number of the instruction boundaries. 12 UINT8 OldIPs[8]; // [Out] Instruction boundaries of the target function. 13 UINT8 NewIPs[8]; // [Out] Instruction boundaries of the trampoline function. 14 } TRAMPOLINE, *PTRAMPOLINE;
当然在跳转的目标处就要先去开辟另一块内存去接受获得拷贝的以上结构体的数据,再去启动Hook的时候做一些事(这是编程的一个经验思想,不去这样做,直接在原数据动刀子怎么去复原呢,所以要去申请MemorySlot,然后做一些我们自己想做的操作),以下是MemorySlot定义结构体定义:
1 #define MEMORY_BLOCK_SIZE 0x1000 2 #if defined(_M_X64) || defined(__x86_64__) 3 #define MEMORY_SLOT_SIZE 64 4 #else 5 #define MEMORY_SLOT_SIZE 32 6 #endif 7 8 // Max range for seeking a memory block. (= 1024MB) 9 #define MAX_MEMORY_RANGE 0x40000000 10 11 typedef struct _MEMORY_SLOT 12 { 13 union 14 { 15 struct _MEMORY_SLOT *Flink;//下一指针 16 UINT8 BufferData[MEMORY_SLOT_SIZE]; 17 }; 18 } MEMORY_SLOT, *PMEMORY_SLOT; //32字节 19 20 typedef struct _MEMORY_BLOCK 21 { 22 _MEMORY_BLOCK* Flink; 23 PMEMORY_SLOT FreeMeorySlotHead; // First element of the free slot list.空闲插槽列表的第一个元素。 24 UINT UsedCount; 25 } MEMORY_BLOCK, *PMEMORY_BLOCK; //12字节
Hook的Target我们这里先使用MessageBoxW,作为一个详细的jmp跳转流程解释,然后我写了几个汇编程序去进行其他E8,Call等指令的跳转实现,不过它是怎么跳转的我会在下面跳转的时候贴出来,首先来玩X86下的MessageBoxW,先给出MessageBoxW的地址形式:
//对于出现的相对偏移地址,在跳板中都要给出新的相对地址
/*
74CA8B80 8B FF mov edi,edi
74CA8B82 55 push ebp
74CA8B83 8B EC mov ebp,esp
74CA8B85 6A 00 push 0
74CA8B87 FF 75 14 push dword ptr [ebp+14h]
74CA8B8A FF 75 10 push dword ptr [ebp+10h]
74CA8B8D FF 75 0C push dword ptr [ebp+0Ch]
74CA8B90 FF 75 08 push dword ptr [ebp+8]
74CA8B93 E8 F8 FC FF FF call [email protected] (74CA8890h)
*/
前面讲过我们是通过跳转加指令形式跳转到我们需要到的地址处,上面代码注释中我们了解到OldPos与NewPos是在MemorySlot创建过程对原函数地址的偏移字节的保存和已经写入FakeFunctionAddress函数的字节数,如下
1 ULONG_PTR OldInstance = (ULONG_PTR)Trampoline->TargetFunctionAddress + OldPos; 2 ULONG_PTR NewInstance = (ULONG_PTR)Trampoline->MemorySlot + NewPos; 3 //数据 4 //OldPos是指的指令的偏移字节 即5个字节中的第2345位.OldInstance地址 5 //指令长度
了解到一些后,我们就应该去真正的对MemorySlot去构建,他的构建用了一个超级大的do-While()循坏(因为实践了好几种跳转指令,心累),x86下的MessageBoxW跳转在5字节处,所以为了之后的恢复,我们需要把5字节的内容做一个保存,这就是所谓的OriginalDataBackup数组的作用->用来恢复也就是解除Hook,后面会逐步解析他的作用和位置,我们这里先记住即可
MemorySlot开始申请32字节的长度,(注释中有解释),我们利用反汇编引擎HDE计算出MessageBoxW函数基地址,从上面给出的MessageBoxW的地址内容中,我们可以看到到达5字节的加法是先加2个字节到下一地址,然后加1加2字节到跳转位置,记录在OldPos,NewPos中
CopyCodeLength = HDE_DISASM((LPVOID)OldInstance, &hde); if (hde.flags & F_ERROR) { return FALSE; } CopyCodeData = (LPVOID)OldInstance; ..... Trampoline->OldIPs[Trampoline->IP] = OldPos; Trampoline->NewIPs[Trampoline->IP] = NewPos; Trampoline->IP++;
到达5字节了,我们就可以去做跳回MessageBoxW基地址加5字节偏移跳转指令了,直接贴出三种在x86下的跳转方法热补丁的方法
1 else if (hde.opcode == 0xE8) 2 {/* 3 // Direct relative CALL 4 ULONG_PTR Destination = OldInstance + hde.len + (INT32)hde.imm.imm32;//4字节 5 #if defined(_M_X64) || defined(__x86_64__) 6 call.address = dest; 7 #else 8 //计算目标地址和Trampoline之间的偏移值 9 call.Operand = (UINT32)(Destination - (NewInstance + sizeof(call))); 10 #endif 11 //pCopySrc 被拷贝到Trampoline中保存的内容 12 CopyCodeData = &call; 13 CopyCodeLength= sizeof(call);*/ 14 } 15 else if ((hde.opcode & 0xFD) == 0xE9) //F 1111 D 1101 16 { //E 1110 9 1011 E 17 // Direct relative JMP (EB or E9) 18 ULONG_PTR Destination = OldInstance + hde.len; 19 20 if (hde.opcode == 0xEB) // isShort jmp 21 { 22 Destination += (INT8)hde.imm.imm8; 23 } 24 else 25 { 26 Destination += (INT32)hde.imm.imm32; 27 } 28 // Simply copy an internal jump. 29 if ((ULONG_PTR)Trampoline->TargetFunctionAddress <= Destination 30 && Destination < ((ULONG_PTR)Trampoline->TargetFunctionAddress + sizeof(JMP_REL)))//这里有点问题 31 { 32 //比较越界 33 if (JmpDestination <Destination) 34 JmpDestination = Destination;//下面有比较 35 } 36 else 37 { 38 /*#if defined(_M_X64) || defined(__x86_64__) 39 jmp.address = dest; 40 #else 41 jmp.Operand = (UINT32)(Destination (NewInstance + sizeof(jmp))); 42 #endif 43 CopyCodeData = &jmp; 44 CopyCodeLength = sizeof(jmp); 45 46 // Exit the function If it is not in the branch 47 IsLoop = (OldInstance >= JmpDestination);//当我设置为1的时候不对 只有后面追上前面的才行后一个槽追上前一个槽 48 //大于后退出循环*/ 49 } 50 51 } 52 else if ((hde.opcode & 0xF0) == 0x70 53 || (hde.opcode & 0xFC) == 0xE0 54 || (hde.opcode2 & 0xF0) == 0x80) 55 { 56 /* 57 & 0xF0 58 0x70 jo 后有一个字节的偏移 59 0x71 jno 后有一个字节的偏移 60 0x72 jb 后有一个字节的偏移 61 .. 62 .. 63 0x7F jg 后有一个字节的偏移 64 65 & 0xFC 66 0xE0 loopne 后有一个字节的偏移 67 0xE1 68 0xE2 69 0xE3 70 */ 71 // Direct relative Jcc 72 ULONG_PTR Destination = OldInstance + hde.len; 73 74 if ((hde.opcode & 0xF0) == 0x70 // Jcc 75 || (hde.opcode & 0xFC) == 0xE0) // LOOPNZ/LOOPZ/LOOP/JECXZ 76 { 77 Destination += (INT8)hde.imm.imm8;//1字节 78 } 79 else 80 { 81 Destination += (INT32)hde.imm.imm32; 82 } 83 84 // Simply copy an internal jump. 85 if ((ULONG_PTR)Trampoline->TargetFunctionAddress <= Destination 86 && Destination < ((ULONG_PTR)Trampoline->TargetFunctionAddress + sizeof(JMP_REL))) 87 { 88 if (JmpDestination < Destination) 89 JmpDestination = Destination; 90 } 91 else if ((hde.opcode & 0xFC) == 0xE0) 92 { 93 // LOOPNZ/LOOPZ/LOOP/JCXZ/JECXZ to the outside are not supported. 94 return FALSE; 95 } 96 else 97 { 98 UINT8 cond = ((hde.opcode != 0x0F ? hde.opcode : hde.opcode2) & 0x0F); 99 #if defined(_M_X64) || defined(__x86_64__) 100 // Invert the condition in x64 mode to simplify the conditional jump logic. 101 //jcc.Opcode = 0x71 ^ cond; 102 //jcc.Address = dest; 103 #else 104 jo.Opcode1 = 0x80 | cond; 105 jo.Operand = (UINT32)(Destination - (NewInstance + sizeof(jo))); 106 #endif 107 CopyCodeData = &jo; 108 CopyCodeLength = sizeof(jo); 109 } 110 } 111 else if ((hde.opcode & 0xFE) == 0xC2) 112 { 113 // ***!!!RET (C2 or C3) 114 115 // Complete the function if not in a branch. 116 //完成功能,如果不在分支中。 117 IsLoop = (OldInstance >= JmpDestination); 118 } 119 120 // Can‘t alter the instruction length in a branch. 121 if (OldInstance < JmpDestination && CopyCodeLength != hde.len) 122 return FALSE; 123 124 // Trampoline function is too large. 125 //if ((newPos + copySize) > TRAMPOLINE_MAX_SIZE) 126 // return FALSE; 127 128 // Trampoline function has too many instructions. 129 //if (ct->nIP >= ARRAYSIZE(ct->oldIPs)) 130 // return FALSE; 131 132 Trampoline->OldIPs[Trampoline->IP] = OldPos; 133 Trampoline->NewIPs[Trampoline->IP] = NewPos; 134 Trampoline->IP++; 135 136 // Avoid using memcpy to reduce the footprint. 137 #ifndef _MSC_VER 138 memcpy((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize); 139 #else 140 __movsb((LPBYTE)Trampoline->MemorySlot + NewPos, (const unsigned char*)CopyCodeData, CopyCodeLength);//复制 141 #endif 142 NewPos += CopyCodeLength; 143 OldPos += hde.len;
1 //这里是热补丁的判断 是否有足够的位置长跳转 2 if (OldPos < sizeof(JMP_REL) 3 && !IsCodePadding((LPBYTE)Trampoline->TargetFunctionAddress + OldPos, sizeof(JMP_REL) - OldPos)) 4 { 5 6 // Is there enough place for a short jump? 7 //没有有足够的位置长跳转,那是否有足够的位置短跳转? 8 if (OldPos < sizeof(JMP_REL_SHORT) 9 && !IsCodePadding((LPBYTE)Trampoline->TargetFunctionAddress + OldPos, sizeof(JMP_REL_SHORT) - OldPos)) 10 { 11 return FALSE; 12 } 13 //只能写短跳转,使用热补丁 14 // Can we place the long jump above the function? 15 //热补丁:目标地址之前地址是否可执行? 16 if (!SeIsExecutableAddress((LPBYTE)Trampoline->TargetFunctionAddress - sizeof(JMP_REL))) 17 return FALSE; 18 //目标地址之前是否是可被覆盖的空白 19 if (!IsCodePadding((LPBYTE)Trampoline->TargetFunctionAddress - sizeof(JMP_REL), sizeof(JMP_REL))) 20 return FALSE; 21 //标志可以热补丁 22 Trampoline->PatchAbove = TRUE;
做了这么多工作,无非是为了MemorySlot里有数据前五个字节和跳转回MessageBoxW基地址+5字节的的五字节偏移,也就是构建10个字节的MemorySlot,构造好后,我们的TRAPOLINE结构也就完成了,以下是我画的他们之间的转换图(图片上去有点问题,请包含一下)
之前破解了热补丁的简单用法,后面用汇编来实现。
好了做了这么多感觉都已经获得了很多知识,接下来就可以去添加Hook信息了(实际上TRAMPLIONE结构体就是一个过渡而已),我们需要再去创建一个HookEntry的结构体(变量意思可以看注释)去完成所有步骤:
1 // Hook information. 2 typedef struct _HOOK_ENTRY 3 { 4 LPVOID TargetFunctionAddress; //目标地址 5 LPVOID FakeFunctionAddress; //Fake地址即覆盖地址 6 LPVOID TrampolineMemorySlot; // Address of the trampoline function. 7 UINT8 OriginalDataBackup[8]; // Original prologue of the target function.目标功能的原始序幕- //恢复Hook使用的存放原先数据 8 9 UINT8 PatchAbove : 1; // Uses the hot patch area. 备份原函数的5字节,重要!!! 10 UINT8 IsEnabled : 1; // Enabled.启用或者关闭 11 UINT8 queueEnable : 1; // Queued for enabling/disabling when != isEnabled. 12 13 UINT IP : 4; // Count of the instruction boundaries.索引 想到汇编的IP就很明白了 14 UINT8 OldIPs[8]; // Instruction boundaries of the target function.原地址的字节变化就靠它了 15 UINT8 NewIPs[8]; // Instruction boundaries of the trampoline function 用在后续解释的MemorySlot中 16 } HOOK_ENTRY, *PHOOK_ENTRY; //44字节 17 18 19 typedef struct _HOOK_INFORMATION_ 20 { 21 PHOOK_ENTRY Items; // Data heap 22 UINT MaximumLength; // Size of allocated data heap, items 23 UINT Length; // Actual number of data items 24 }HOOK_INFORMATION,*PHOOK_INFORMATION;
当有了这个结构体后就可以去CreateHook了,下面是构建过程:
1 if (CreateTrampoline(&Tl)) 2 { 3 PHOOK_ENTRY HookEntry = AddHookEntry(); //填充一个HookInfo信息 4 if (HookEntry != NULL) 5 { 6 HookEntry->TargetFunctionAddress = Tl.TargetFunctionAddress; 7 #if defined(_M_X64) || defined(__x86_64__) 8 HookEntry->FakeFunctionAddress = Tl.pRelay;//跳转在trampoline 9 #else 10 HookEntry->FakeFunctionAddress = Tl.FakeFunctionAddress; 11 #endif 12 HookEntry->TrampolineMemorySlot = Tl.MemorySlot; 13 HookEntry->PatchAbove = Tl.PatchAbove 14 HookEntry->IsEnabled = FALSE; 15 //HookEntry->QueueEnable = FALSE; 16 HookEntry->IP = Tl.IP; 17 18 memcpy(HookEntry->OldIPs, Tl.OldIPs, ARRAYSIZE(Tl.OldIPs)); 19 memcpy(HookEntry->NewIPs, Tl.NewIPs, ARRAYSIZE(Tl.NewIPs)); 20 21 // Back up the target function. 22 23 if (Tl.PatchAbove)//这就是热补丁 24 { 25 memcpy( 26 HookEntry->OriginalDataBackup, 27 (LPBYTE)TargetFunctionAddress - sizeof(JMP_REL), 28 sizeof(JMP_REL) + sizeof(JMP_REL_SHORT)); 29 } 30 else 31 { //存储源函数的数据内容 32 memcpy(HookEntry->OriginalDataBackup, TargetFunctionAddress, sizeof(JMP_REL)); 33 } 34 if (OriginalWhitelist != NULL)//白名单,用来恢复 35 { 36 *OriginalWhitelist = HookEntry->TrampolineMemorySlot; 37 }
(真不是我的代码风格,博客园插入代码自动变样子,我对齐了一下请包涵)
到这里为止终于是创建了Hook,接下来就是启动Hook,函数名字叫做EnableHook,明日再写,先到这了,晚了。。
今天开始写EnableHook
EnableHook顾名思义就是启动Hook,显而易见得知它的作用无非就是覆盖原函数我们记录的那5字节,如下:
1 //SHELLCODE 2 PJMP_REL jmp = (PJMP_REL)PatchData; 3 jmp->Opcode = 0xE9;//跳转 4 jmp->Operand = (UINT32)((LPBYTE)HookEntry->FakeFunctionAddress - (PatchData + sizeof(JMP_REL))); 5
当需要解除Hook时候我们就可以用到在前面说过的OriginalDataBackup去恢复原函数,当然还有一种方法是直接调用MemorySlot中记录下的跳转地址,直接就可以调用了,我个人觉得MemorySlot构建真的不错,尤其是它的这种直接调用
1 else 2 { 3 memcpy(PatchData, HookEntry->OriginalDataBackup, sizeof(JMP_REL)); 4 }
当然,因为我们有好几种的汇编测试代码,如果多个一起EnableHook怎么办,通过遍历去覆盖,如下:
17 for (i = FirstAddress; i < __Hooks.Length; ++i) 18 { 19 if (__Hooks.Items[i].IsEnabled != IsEnable) 20 {............
PJMP_REL jmp = (PJMP_REL)PatchData; 3 jmp->Opcode = 0xE9;//跳转 4 jmp->Operand = (UINT32)((LPBYTE)HookEntry->FakeFunctionAddress - (PatchData + sizeof(JMP_REL))); 5 . . . } }
23 24
好了,接下来开始测试:
1 if (CreateHook(&MessageBoxW, &FakeMessageBox, 2 reinterpret_cast<LPVOID*>(&__OriginalMessageBoxW)) != STATUS_SUCCESS)//告知要hook成什么样子 3 { 4 return; 5 } 6 7 MessageBoxW(0, L"MessageBoxW", L"MessageBoxW", 0);//没有Hook还是原先,不要也行 8 if (EnableHook(MessageBoxW) != STATUS_SUCCESS) 9 { 10 printf("EnableHook is wrong\r\n"); 11 return; 12 } 13 MessageBoxW(NULL, L"CreateHook()", L"CreateHook()", 0);//启动Hook后,现在是FakeHOOK 14 15 printf("Input AnyKey To Exit\r\n"); 16 getchar(); 17 18 Uninitialize();//返回释放 19 } 20 21 int WINAPI FakeMessageBox( 22 _In_opt_ HWND DialogHwnd, 23 _In_opt_ WCHAR* DialogText, 24 _In_opt_ WCHAR* DialogCaption, 25 _In_ UINT Type 26 ) 27 { 28 __OriginalMessageBoxW(DialogHwnd, L"FakeMessageBox", L"FakeMessageBox", Type); 29 return 0; 30 }
编译运行后出结果啦,先是原先的MessageBoxW:
这是成功Hook后的:
一切顺利,没有白费功夫,下面是我对EB,call,热补丁的汇编源码,我们仿照MessageBoxW的形式在test.cpp中定义函数指针,与Fake函数的输出形式。
在这里花费了功夫探索出了热补丁的简单定义是申请5字节空的内存然后 mov edi,edi,能应用正确,汇编代码如下
1 20 Asm_1 PROC 21 push ebp 22 mov ebp, esp 23 sub esp, 40h 24 push ebx 25 push esi 26 push edi 27 xor eax, eax 28 ;013710D7 E9 24 07 00 00 jmp Sub_1 (01371800h) 29 mov eax,dword ptr[ebp+8h] 30 mov ebx,dword ptr[eax+1] 31 add eax,ebx 32 add eax,5 33 pop edi 34 pop esi 35 pop ebx 36 add esp, 40h 37 pop ebp 38 ret 39 Asm_1 ENDP 40 41 42 ;EB指令 43 Asm_3 PROC 44 jmp Label1 45 Label1: 46 jmp Label2 47 Label2: 48 mov eax,-3 49 ret 50 Asm_3 ENDP 51 52 ;call指令 53 Asm_4 PROC 54 55 call Label0 56 jmp Exit; 57 Label0: 58 push 0; 59 call Label1; //Call 60 db ‘H‘ 61 db 0 62 db ‘e‘ 63 db 0 64 db ‘l‘ 65 db 0 66 db ‘l‘ 67 db 0 68 db ‘o‘ 69 db 0 70 db ‘S‘ 71 db 0 72 db ‘u‘ 73 db 0 74 db ‘b‘ 75 db 0 76 db ‘_‘ 77 db 0 78 db ‘4‘ 79 db 0 80 db 0 81 db 0 82 Label1: 83 call Label2; 84 db ‘H‘ 85 db 0 86 db ‘e‘ 87 db 0 88 db ‘l‘ 89 db 0 90 db ‘l‘ 91 db 0 92 db ‘o‘ 93 db 0 94 db ‘S‘ 95 db 0 96 db ‘u‘ 97 db 0 98 db ‘b‘ 99 db 0 100 db ‘_‘ 101 db 0 102 db ‘4‘ 103 db 0 104 db 0 105 db 0 106 Label2: 107 push 0; 108 call MessageBoxW 109 ret 110 Exit: 111 ret 112 Asm_4 ENDP 113 114 ;热补丁 115 Asm_10 PROC 116 db 0CCh 117 db 0CCh 118 db 0CCh 119 db 0CCh 120 db 0CCh 121 122 mov edi,edi 123 ret 124 Asm_10 ENDP 125 126
1 //热补丁 2 PVOID v10 = Asm_1(Asm_10); 3 4 if (SeCreateHook((PVOID)((ULONG_PTR)v10 + 5), &FakeSub_10, 5 reinterpret_cast<LPVOID*>(&__OriginalSub_10)) != STATUS_SUCCESS) 6 { 7 return; 8 } 9 //对于热补丁函数调用 10 ((LPFN_SUB_10)(((ULONG_PTR)v10 + 5)))(); 11 if (SeEnableHook(ALL_HOOKS) != STATUS_SUCCESS) 12 { 13 printf("SeEnableHook() Error\r\n"); 14 return; 15 } 16 ((LPFN_SUB_10)(((ULONG_PTR)v10 + 5)))();
E9的测试只需要自写一个函数调用测试调用即可,如下面这样就行了
1 1 //E9指令,这样就行了 2 2 3 3 void Sub_2() 4 4 { 5 5 printf("Sub_2\n\r"); 6 6 }
下面是所有的正确输出结果:
好了,x86下的MiniHook终于是测试完了,写了一遍后又是更懂了,如果有什么差错,望大家纠正
具体的调试流程在下期文章贴出来,今天还有点事,抱歉
刘晓亮原创(一个执着探索计算机奥妙的人)