呵呵, 都是老代码了, 我怕发霉, 现在拿出来分享下吧, 这些都只是在我的技术爱好范围内的,不汲及其它的 以前了解到的都是dll注入到游戏中才能进行函数的拦截, 后来我想这个麻烦了, 就自己琢磨了一番, 终于经过自己的努力写出来了
下面是某游戏的一个无dll注入拦的源码, 发源码前先说说他的原理吧, 其实和dll注入差不别不大, 首先我们要写一个函数用来替换要拦截的函数, 而这个函数最终只能是汇编的, 呵呵, 这里我不多作解释了, 我的作法是写成函数, 然后导出, 再利用od反汇编这到一堆的机器码
复制出来整理成一个常量数组, 用od是比较方便, 难不成自己一个对照去翻译啊, 当然也可以利用现成的反汇编引擎搞定, 呵呵, 那个不在我这次的讨论范围了
看下面,这个是一个hook函数, 其中 call push jmp几个指令后面我都以0填充了, 主要是后面动态写入的
//最终需要跳转的代码
JmpHookCode : array[0..35] of Byte = ($6A, $FF, //push -1
$60, //pushad
$8B, $44, $24, $2C, //mov eax, dword ptr[esp + 2c]
$8B, $74, $24, $28, //mov esi, dword ptr[esp + 28]
$50, //push eax
$56, //push esi
$E8, $00, $00, $00, $00, //call xxxx
$61, //popad
$64, $A1, $00, $00, $00, $00,//mov eax, dword fs:[0]
$68, $00, $00, $00, $00, //push xxxx
$E9, $00, $00, $00, $00, //jmp xxxxx
$C3); //retn
00401019 6A FF push -1
0040101B 60 pushad
00401015 8B4424 2C mov eax, [dword esp+2C]
00401019 8B7424 28 mov esi, [dword esp+28] //11
0040101D 50 push eax
0040101E 56 push esi
00401024 E8 0B240408 call 08443434 //18
00401029 61 popad
0040102A 64:A1 00000000 mov eax, [dword fs:0] //25
00401030 68 5840A700 push 0A74058
00401035 - E9 0980E402 jmp 03249043 //35
好了,下面就要下那个 call的函数, 同理写好后编译导出,然后用od打开复制机器码再整理, 照着上面的将一个指定后面时行填0
SendDataCode : array[0..51] of Byte = ($55, //push ebp
$8B, $EC, //mov ebp, esp ;保存堆栈
$83, $C4, $F4, //add esp, -0C ;开辟 0C 大小栈空间
$8B, $45, $0C, //mov eax, dword ptr [ebp+C] ;将第二个参数放入eax
$83, $F8, $01, //cmp eax, 1 ;比较 if DataLen > 1 then
$76, $20, //jbe short 004AE342 ;不等于跳到结束位置
$33, $D2, //xor edx, edx ;初始化edx寄存器
$89, $55, $F4, //mov dword ptr [ebp-C], edx ;Data.dwData := 0;
$89, $45, $F8, //mov dword ptr [ebp-8], eax ;Data.cbData := DataLen
$8B, $45, $08, //mov eax, dword ptr [ebp+8] ;将第一个参数放入eax
$89, $45, $FC, //mov dword ptr[ebp-4], eax ;Data.lpData := DataPoint
$8D, $45, $F4, //lea eax, dword ptr[ebp-c] ;@Data
$50, //push eax ;lParam
$6A, $00, //push 0 ;wParam
$6A, $4A, //push 4A ;Msg = WM_COPYDATA
$68, $00, $00, $00, $00, //push xxxx ;Handle
$E8, $00, $00, $00, $00, //call SendMessageA ;SendMessage
$8B, $E5, //mov esp, ebp ;恢复堆栈
$5D, //pop ebp
$C2, $08, $00); //retn 8
004AE314 > 55 push ebp
004AE315 8BEC mov ebp, esp
004AE317 83C4 F4 add esp, -0C
004AE31A 8B45 0C mov eax, dword ptr [ebp+C]
004AE31D 83F8 01 cmp eax, 1
004AE320 76 20 jbe short 004AE342
004AE322 33D2 xor edx, edx
004AE324 8955 F4 mov dword ptr [ebp-C], edx
004AE327 8945 F8 mov dword ptr [ebp-8], eax
004AE32A 8B45 08 mov eax, dword ptr [ebp+8]
004AE32D 8945 FC mov dword ptr [ebp-4], eax
004AE330 8D45 F4 lea eax, dword ptr [ebp-C]
004AE333 50 push eax
004AE334 6A 00 push 0
004AE336 6A 4A push 4A
004AE338 68 A4000100 push 100A4
004AE33D E8 A693F5FF call
004AE342 8BE5 mov esp, ebp
004AE344 5D pop ebp
004AE345 C2 0800 retn 8
//第37个字节开始写入四字节的句柄
//第42个字节开始写入四字节的SendMessage函数地址 SendMessage - SendData- 42
//汇编代码还原后的函数
procedure SendData(DataPoint: Pointer; DataLen: DWORD);stdcall;
var
Data: TWMCopyDataStruct;
begin
if DataLen > 1 then
begin
Data.dwData := 0;
Data.cbData := DataLen;
Data.lpData := DataPoint;
SendMessage(Handle, WM_COPYDATA, 0, Integer(@Data));
end;
end;
接下来我们要干嘛呢? 我们要修在目标进程中分配一埠同上面两个函数大小的内存
然后将代码写进去,,,,,,,,,, 其它的就看注释部分吧, 程序退出别记了释放先前分配的内存哦
ReadMem(hProcess, Pointer(OldSendAddr), @Flags, 1);
if Flags <> $E9 then //检测当前位置标识是否为 jmp = $E9
begin
ReadMem(hProcess, Pointer(OldSendAddr), @OldHookCode, Length(OldHookCode));//备份代码
//在目标进程中为自己的函数分配空间
JmpHookFuncAddr := VirtualAllocEx(hProcess, nil, Length(JmpHookCode), MEM_COMMIT, PAGE_READWRITE);
MySendFuncAddr := VirtualAllocEx(hProcess, nil, Length(SendDataCode), MEM_COMMIT, PAGE_READWRITE);
//写入代码
if Assigned(JmpHookFuncAddr) then
begin
//写入自己的处理跳转的代码
WriteMem(hProcess, JmpHookFuncAddr, @JmpHookCode, Length(JmpHookCode));
//写入要CALL自己的函数地址, 并计算相对地址
TEMP := DWORD(MySendFuncAddr) - DWORD(JmpHookFuncAddr) - 18;
WriteMem(hProcess, Pointer(DWORD(JmpHookFuncAddr) + 14), @TEMP, 4);
// 写入相对地址
ReadMem (hProcess, Pointer(OldSendAddr + 9), @TEMP, 4);
WriteMem(hProcess, Pointer(DWORD(JmpHookFuncAddr) + 26), @TEMP, 4);
//写入处理完成后要跳转的代码
TEMP := (OldSendAddr - DWORD(JmpHookFuncAddr) - 35) + $D;
WriteMem(hProcess, Pointer(DWORD(JmpHookFuncAddr) + 31), @TEMP, 4);
end;
if Assigned(MySendFuncAddr) then
begin
//写入自己的处理函数代码
WriteMem(hProcess, MySendFuncAddr, @SendDataCode, Length(SendDataCode));
//写入当前句柄
TEMP := Handle;
WriteMem(hProcess, Pointer(DWORD(MySendFuncAddr) + 37), @TEMP, 4);
//写入SendMessage函数地址
SendMsg := GetFuncAddr('SendMessageA');
TEMP := DWORD(SendMsg) - DWORD(MySendFuncAddr) - 46;
WriteMem(hProcess, Pointer(DWORD(MySendFuncAddr) + 42), @TEMP, 4);
end;
//这里开始完成改写跳转代码让函数跳到我自己的函数内来
//inline hook
B := $E9;
WriteMem(hProcess, Pointer(OldSendAddr), @B, 1);
//写入自己的函数
TEMP := DWORD(JmpHookFuncAddr) - OldSendAddr - 5;
WriteMem(hProcess, Pointer(OldSendAddr + 1), @TEMP, 4);
//写入nop
B := $90;
WriteMem(hProcess, Pointer(OldSendAddr + 5), @B, 1);
IsHook := True;
end;
至此我们就剩下使用wm_copydata接收发送过来的数据, 是不是很爽啊