Delphi API HOOK(asm)及计算JMP距离

  1 library Project1;                  //DLL工程文件
  2 
  3 { Important note about DLL memory management: ShareMem must be the
  4   first unit in your library's USES clause AND your project's (select
  5   Project-View Source) USES clause if your DLL exports any procedures or
  6   functions that pass strings as parameters or function results. This
  7   applies to all strings passed to and from your DLL--even those that
  8   are nested in records and classes. ShareMem is the interface unit to
  9   the BORLNDMM.DLL shared memory manager, which must be deployed along
 10   with your DLL. To avoid using BORLNDMM.DLL, pass string information
 11   using PChar or ShortString parameters. }
 12 
 13 uses
 14   System.SysUtils,
 15   System.Classes,
 16   windows,                       //为了能使用系统API,必须添加windows单元
 17   Vcl.Dialogs;                   //为了能使用ShowMessage这个信息框,必须添加
 18 
 19 {$R *.res}
 20 
 21 
 22 var
 23  allocaddr:Integer;              //定义一个保存申请到的内存空间首地址的变量
 24  apiaddr:Integer;                //定义一个保存API地址的变量
 25  hp:Integer;                     //定义一个保存进程句柄的变量
 26  pid:Integer;                    //定义一个保存进程PID的变量
 27  实际写:NativeUInt;               //writeprocessmemory的第5个参数必须为NativeUint类型,否则写入失败
 28  写:Integer;                     //writeprocessmemoty的第4个参数,必须加@,表示取含有数据的变量的地址,为point类型
 29  jmps:integer;                   //jmp的字节码,16进制为E9,十进制为233
 30  asmcode:Integer;                //将下面的一维静态数据的每个元素数据,strtoint(字符串转数值)
 31  code:array [1..16]of string=('60','9C','C6','05','73','19','40','00','74','9D','61','55','8B','EC','6A','FF');  //定义一个从1开始到16,总计16个元素的一维数组,存放str类型的机器码(16进制)
32 function Lens(T, O: Integer): Integer; stdcall; //计算JMP距离的函数,参数T为终点位置,O为起跳位置,从O起跳到T 33 var 34 x: Integer; 35 y: Integer; 36 begin 37 x := T; 38 y := O; 39 Result := x - y - 5; //由于起跳点,比如起跳点为00401000(16进制),实际会占用5个字节{E9(jmp) 1个字节+4个字节的距离},实际起跳位置是00401005,故最终的位置会增加5个距离,所以需要减去, 40 end; //当前函数返回的是10进制,例:如果转成十六进制则需要倒转,比如FFFF01020304,需要倒转为04030201FFFF
62 procedure apihook; stdcall; //此为apihook的过程(过程无返回值) 63 var 64 i: Integer;                     //定义一个循环变量i,循环把数组的字符串转数值,再写入内存            65 oldpro: Cardinal;                 //virtualprotectex的第五个参数 66 begin 67 pid := GetCurrentProcessId(); //获取自进程id,dll获取的自进程dll是本Exe的进程ID,因为Delphi中,OpenProcess的第三个参数无法为-1,故用api函数获取 68 hp := OpenProcess(PROCESS_ALL_ACCESS, False, pid); //打开进程,获得进程句柄 69 ShowMessage('hp' + IntToStr(hp)); //显示进程句柄,如果hp=0则打开失败,需要提权 70 apiaddr := Integer(GetProcAddress(LoadLibrary('msvbvm60.dll'), 'ThunRTMain')); //GetprocAddress获得的地址是point,故用Integer转换成数值 71 // ShowMessage('apiaddr'+IntToStr(apiaddr)); 72 allocaddr := Integer(VirtualAllocEx(hp, nil, 1024, 4096, 4)); //申请一段内存空间,大小为1024,4096是新保护,表示该内存空间可读可写可执行,最后一个参数为旧保护,但是好像必须要写4,否则无法申请 73 写 := Lens(allocaddr, apiaddr); //此时开始计算新申请的内存空间的地址到api地址的距离 74 jmps := 233; //jmp的字节码E9的10进制就是233 75 // ShowMessage(IntToStr(写)); 76 WriteProcessMemory(hp, Pointer(apiaddr), Pointer(@jmps), 1, 实际写); //先写jmp 77 WriteProcessMemory(hp, Pointer(apiaddr + 1), Pointer(@写), 4, 实际写); // 再写距离,第二个参数为地址,第三个参数为要写入的内容,都是Point类型,但要写入的内容,必须要加@,表示保存内容的变量的地址,4表示写入的字节数,也就是大小 78 for i := 1 to 16 do //构造一个循环,从数组里面拿出数据写入申请的内存空间 79 begin 80 if i = 1 then 81 begin 82 asmcode := StrToInt('$' + code[i]); 83 WriteProcessMemory(hp, Pointer(allocaddr), Pointer(@asmcode), 1, 实际写); 84 end 85 else 86 begin 87 asmcode := StrToInt('$' + code[i]); 88 WriteProcessMemory(hp, Pointer(allocaddr + i - 1), 89 Pointer(@asmcode), 1, 实际写); 90 end; 91 WriteProcessMemory(hp, Pointer(allocaddr + 16), Pointer(@jmps), 1, 实际写); //这里是从申请的内存空间跳转回来到api,因为总共是16个字节,所以从第17个字节就是E9,也就是jmp,内存空间是从0开始,非从1开始 92 写 := Lens(apiaddr + 5, allocaddr + 16); 93 WriteProcessMemory(hp, Pointer(allocaddr + 17), Pointer(@写), 4, 实际写); //写入距离 94 VirtualProtectEx(hp, Pointer($401973), 1, 64, oldpro); //最后修改mov byte ptr ds:[0x401973], 0x74     Patch代码,将401973的内存空间 修改成可读可写,也就是64 95 end; 96 97 end; 98 99 exports //Dll的导出函数,使外部程序可以调用的函数 100 apihook; 101 102 begin104 apihook; 105 //这个begin和end之间,个人理解属于启动函数,DllMain 106 end.

 附asm代码:

660035A4 > 55 push ebp 函数头直接jmp
660035A5 8BEC mov ebp, esp
660035A7 6A FF push -0x1

660035A9 68 20980166 push 66019820 函数头下面的被jmp回来的地址


660198FE 0000 add byte ptr ds:[eax], al 空代码地址

00401973 /75 43 jnz short 004019B8

660198FE 60 pushad 保存寄存器
660198FF 9C pushfd 保存标志位
66019900 C605 73194000 7>mov byte ptr ds:[0x401973], 0x74 Patch代码
66019907 9D popfd 恢复标志位
66019908 61 popad 恢复寄存器
66019909 55 push ebp 把被Jmp的函数头还原回来
6601990A 8BEC mov ebp, esp
6601990C 6A FF push -0x1
6601990E ^ E9 969CFEFF jmp 660035A9 jmp回去
66019913 90 nop

60 9C C6 05 73 19 40 00 74 9D 61 55 8B EC 6A FF E9 96 9C FE FF 90

猜你喜欢

转载自www.cnblogs.com/qianqing/p/11137676.html