本文主要讲解 HOOK API 的传统实现方式, 很多新手再玩HOOK的时候经常抄袭别人的代码,至于到底怎么实现的,可能看了半天没看明白。
所以高手请飘过~~~~~~~~~~~ 嘿嘿本人是搞破解的一直在加脱壳版里的,最近搞安全工作就来这里混了 请高手多多支持1
废话不说请看下文

注意:本文为了为了方便直接用 VirtualProtect 这个API的原型进行解释,而所有地址都只是为了方便讲解自行设定的,
你可以从 step1-9 个步骤看 jmp 的过程很容易就理解其真实原理,再不明白的话你只能改行了。

代码:
MyFunc:  00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00401000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00401010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

type
  TVirtualProtect = function(lpAddress: Pointer; dwSize, flNewProtect: DWORD; lpflOldProtect: Pointer): BOOL; stdcall;

var
  PVirtualProtect: TVirtualProtect;

function HookVirtualProtect(lpAddress: Pointer; dwSize, flNewProtect: DWORD; lpflOldProtect: Pointer): BOOL; stdcall;
begin
  Result:= PVirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect);   -> step8 返回 00010005 (转移后的原API开始)
  {HOOK CODE}                                               -> step9 这里自然就是你自己要写的代码了
end;
UnHook API 过程如下 比较容易理解

由原来返回的 00010005 处 -5 读取 10000000 (原 API 地址), 00010005 处 -1 读取 05 (原API指令长度)

RtlMoveMemory(10000000, 00010005, 05);

FreeMem, GlobalFree, HeapFree 释放堆(00010005-5);

当然还有过程中的 VirtualProtect 修改内存属性这里就不再叙述了

OnHOOK API 过程如下

代码:
Heap:原  00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00010000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00010010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00010000    00              DB 00                            -> step3 GetMem, GlobalAlloc, HeapAlloc 申请堆
00010001    00              DB 00
00010002    00              DB 00
00010003    00              DB 00
00010004    00              DB 00
00010005    00              DB 00
00010006    00              DB 00
00010007    00              DB 00
00010008    00              DB 00
00010009    00              DB 00
0001000A    00              DB 00
0001000B    00              DB 00
0001000C    00              DB 00
0001000D    00              DB 00
0001000E    00              DB 00

Heap:改  00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00010000 00 00 00 10 05 8B FF 55 8B EC E9 F6 FF FE 0F 00
00010010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

00010000    00000010        DD 10000000                     -> step4 CopyMemory, RtlMoveMemory, P^ 写入原API地址 DWORD
00010004    05              DB 05                           -> step7 写入原API指令长度
00010005    8BFF            MOV EDI,EDI                     -> step5 计算原API指令长度 并在此 00010005 位置写入
00010007    55              PUSH EBP
00010008    8BEC            MOV EBP,ESP
0001000A    E9 F6FFFE0F     JMP 10000005                    -> step6 10000005(原API继续) - 00010005(原API开始) -5(原API指令长度) -5(这个JMP用的指令长度) = 0FFEFFF6

指令地址 10000000

指令长度 2
存储长度 2
指令地址 10000002

指令长度 1
存储长度 3
指令地址 10000003

指令长度 2
存储长度 5
指令地址 10000005

堆栈地址 00010005

API:原   00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
10000000 8B FF 55 8B EC FF 75 14 FF 75 10 FF 75 0C FF 75
10000010 08 6A FF E8 75 FF FF FF 5D C2 10 00 90 90 90 90

10000000 >  8BFF            MOV EDI,EDI                      -> step1 GetProcAddress(LoadLibraryA('kernel32.dll'), 'VirtualProtect')
10000002    55              PUSH EBP
10000003    8BEC            MOV EBP,ESP
10000005    FF75 14         PUSH DWORD PTR SS:[EBP+14]
10000008    FF75 10         PUSH DWORD PTR SS:[EBP+10]
1000000B    FF75 0C         PUSH DWORD PTR SS:[EBP+C]
1000000E    FF75 08         PUSH DWORD PTR SS:[EBP+8]
10000011    6A FF           PUSH -1
10000013    E8 75FFFFFF     CALL kernel32.VirtualProtectEx
10000018    5D              POP EBP
10000019    C2 1000         RETN 10
1000001C    90              NOP
1000001D    90              NOP
1000001E    90              NOP
1000001F    90              NOP

API:改   00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
10000000 E9 FB 0F 40 F0 FF 75 14 FF 75 10 FF 75 0C FF 75
10000010 08 6A FF E8 75 FF FF FF 5D C2 10 00 90 90 90 90

10000000 >  E9 FB0F40F0     JMP 00401000                      -> step2 00401000(MyFunc地址) - 10000000(API地址) - 5(指令长度) = F0400FFB
10000005    FF75 14         PUSH DWORD PTR SS:[EBP+14]
10000008    FF75 10         PUSH DWORD PTR SS:[EBP+10]
1000000B    FF75 0C         PUSH DWORD PTR SS:[EBP+C]
1000000E    FF75 08         PUSH DWORD PTR SS:[EBP+8]
10000011    6A FF           PUSH -1
10000013    E8 75FFFFFF     CALL kernel32.VirtualProtectEx
10000018    5D              POP EBP
10000019    C2 1000         RETN 10
1000001C    90              NOP
1000001D    90              NOP
1000001E    90              NOP
1000001F    90              NOP

话外音:

简单实现 SetWindowsHookEx 钩子注入的检查法, 没有什么技术含量只是用于简单的对自身程序或自身程序创建的进程进行简单保护, 发现后直接 退出进程

0012FC28   77D28055  /CALL 到 LoadLibraryExW 来自 user32.77D2804F
0012FC2C   0012FC88  |FileName = "D:\QS0905\qsl.dll"
0012FC30   00000000  |hFile = NULL
0012FC34   00000008  \Flags = LOAD_WITH_ALTERED_SEARCH_PATH

首先对自身进程或创建进程的 LoadLibraryExW HOOK掉, 并实时监测, 检查达到此入口函数的 ESP 是否是 USER32.DLL 模块中, 并且检查 Flags = LOAD_WITH_ALTERED_SEARCH_PATH, 也可以检查 DLL 路径是否是自身程序或者创建进程的所在目录,如果满足,说明是被外部程序挂了钩子, 如果是被远程线程注入的话就比较难判断, 不过都在R3下的还是可以监测到的, R0的话只能说 佛祖保佑你. ok 就到这里咯