首页 > 其他 > 详细

MinHook测试与分析(x86下 E8,E9,EB,CALL指令测试,且逆推测试微软热补丁)

时间:2017-09-20 00:03:07      阅读:607      评论:0      收藏:0      [点我收藏+]

依稀记得第一次接触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 _MessageBoxExW@20 (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终于是测试完了,写了一遍后又是更懂了,如果有什么差错,望大家纠正

具体的调试流程在下期文章贴出来,今天还有点事,抱歉

刘晓亮原创(一个执着探索计算机奥妙的人)

 

MinHook测试与分析(x86下 E8,E9,EB,CALL指令测试,且逆推测试微软热补丁)

原文:http://www.cnblogs.com/L-Sunny/p/7522133.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!