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

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

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

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

时间: 2024-10-13 08:42:13

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

[原创]MinHook测试与分析(x64下 E9,EB,CALL指令测试,且逆推测试微软热补丁)

依稀记得第一次接触Hook的概念是在周伟民先生的书中-><<多任务下的数据结构与算法>>,当时觉得Hook很奇妙,有机会要学习到,正好近段日子找来了MiniHook,就一起分享一下. 本篇文章是在x64下测试与分析jmp+offset类型的Hook,并且逆推测出热补丁的简单用法,MinHook它的中心就是覆盖重写并且可以复原.知道大概的思路后后让我们先来具体的实现MinHook再去做测试. 首先是堆的申请(申请PAGE_SIZE大小自动生长的堆),以下是实现与卸载 1 NTS

Jmeter测试结果分析(下)

前文再续,续接上一回.上一篇讲了如何利用Assertion将测试结果进行初步的筛选.那么,当我们拿到了测试结果之后,我们应该如何去看待它们呢?它们又是怎么来的呢? 一.Listener的使用 用过LoadRunner的人应该都知道,LoadRunner会为我们提供一大堆图标和曲线.但是在Jmeter里,我们只能找到几个可怜的Listener来方便我们查看测试结果.但是,对于初学者来说,一些简单的结果分析工具可以使我们更容易理解性能测试结果的分析原理.所以,千万别小看这几个简单的Listener啊

X86下,FF 25+ 导入表函数地址小测试

在X86下,JMP反汇编出来的FF 25加的是导入表的地址 测试代码如下: void JmpFunctionAddressOfImportTableInWinXP_X86() { DWORD dwOld = 0; ULONG_PTR v2 = 0; void* v1 = NULL; GetFunctionByImport_X86(GetModuleHandle(NULL), "MessageBoxA"); v1 = lpAddress; printf("%p\r\n"

Linux X86下的TLB机制分析

TLB - translation lookaside buffer 快表,直译为翻译后备缓冲器,也可以理解为页表缓冲,地址变换高速缓存. 由于页表存放在主存中,因此程序每次访存至少需要两次:一次访存获取物理地址,第二次访存才获得数据.提高访存性能的关键在于依靠页表的访问局部性.当一个转换的虚拟页号被使用时,它可能在不久的将来再次被使用到,. TLB是一种高速缓存,内存管理硬件使用它来改善虚拟地址到物理地址的转换速度.当前所有的个人桌面,笔记本和服务器处理器都使用TLB来进行虚拟地址到物理地址的

Google Test测试框架分析

Google Test测试框架分析 一.简介 Google Test是由Google主导的一个开源的C++自动化测试框架,简称GTest.GTest基于xUnit单元测试体系,和CppUint类似,可以看作是JUnit.PyUnit等对C++的移植. 下图是GTest测试框架的测试过程,表示的是GTest的两种测试方式. 下面将使用一个极其简单的例子表示xUnit测试的主要过程.如对Hummer的CTXString类的成员方法GetLength进行测试.详见下面GTest代码和注释说明. //

Loadrunner测试实例分析

LoadRunner性能测试结果分析是个复杂的过程,通常可以从结果摘要.并发数.平均事务响应时间.每秒点击数.业务成功率.系统资源.网页细分图.Web服务器资源.数据库服务器资源等几个方面分析,如图1- 1所示.性能测试结果分析的一个重要的原则是以性能测试的需求指标为导向.我们回顾一下本次性能测试的目的,正如 所列的指标,本次测试的要求是验证在30分钟内完成2000次用户登录系统,然后进行考勤业务,最后退出,在业务操作过程中页面的响应时间不超过3秒,并且服务器的CPU使用率.内存使用率分别不超过

LoadRunner测试结果分析01 转载至zhangzhe的新浪博客

LoadRunner测试结果分析之我见 LoadRunner生成测试结果并不代表着这次测试结果的结束,相反,这次测试结果的重头戏才刚刚开始.如何对测试结果进行分析,关系着这次测试的成功与否.网上关于LoadRunner测试结果如何分析的介绍相当匮乏,在总结他人的观点和自己的实验体会基础上来介绍如何进行LoadRunner测试结果分析. 1. LoadRunner测试结果分析的第一步应该是查看分析综述(Analysis Summary),其包括统计综述(Statistics Summary).事务

LoadRunner测试结果分析03 转载至zhangzhe的新浪博客

LoadRunner测试结果分析之我见 前面分析的Web Resource(网络资源)的测试情况,其主要关注的是服务器性能,而系统本身和环境都有可能存在问题,页面诊断(Web Page Diagnostics)主要就是关注这方面的问题.页面诊断可以很好地定位环境问题,如客户端问题.网络问题等,也可以很好的分析系统本身的问题,如网页问题. 1.Web Page Diagnostics (网页诊断)对测试过程中所有的页面进行一个 信息汇总,可以很容易地观察出哪个页面下载耗时,然后选择该页面得其页面分

测试缺陷分析务实篇

测试缺陷分析务实篇 作者:罗耀秋 来源:网络 摘要: 测试活动作为IT项目和产品开发一个重要的环节,通过发现产品或组件的缺陷,并反馈给开发组修复验证这些缺陷,从而在一定程度上保证了外发产品的质量.对这些测试活动发现的缺陷进行深入的分析,可以有助于我们进行质量预测.进行过程改进.量化的衡量产品质量. 关键词: 测试分析.过程改进.质量预测.过程能力.缺陷 正文: 项目研发过程中,我们通过单元测试.集成测试.系统测试发现了大量的缺陷.我们把这些Bug输入到Excel或者其他测试管理系统中,跟踪其解决