CnInProcessAPIHook.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. {******************************************************************************}
  2. { CnPack For Delphi/C++Builder }
  3. { 中国人自己的开放源码第三方开发包 }
  4. { (C)Copyright 2001-2018 CnPack 开发组 }
  5. { ------------------------------------ }
  6. { }
  7. { 本开发包是开源的自由软件,您可以遵照 CnPack 的发布协议来修 }
  8. { 改和重新发布这一程序。 }
  9. { }
  10. { 发布这一开发包的目的是希望它有用,但没有任何担保。甚至没有 }
  11. { 适合特定目的而隐含的担保。更详细的情况请参阅 CnPack 发布协议。 }
  12. { }
  13. { 您应该已经和开发包一起收到一份 CnPack 发布协议的副本。如果 }
  14. { 还没有,可访问我们的网站: }
  15. { }
  16. { 网站地址:http://www.cnpack.org }
  17. { 电子邮件:master@cnpack.org }
  18. { }
  19. {******************************************************************************}
  20. unit CnInProcessAPIHook;
  21. {* |<PRE>
  22. ================================================================================
  23. * 软件名称:不可视工具组件包
  24. * 单元名称:进程内实现APIHook的单元
  25. * 单元作者:CodeGame
  26. * 备 注:
  27. * 开发平台:PWinXP + Delphi 2007
  28. * 兼容测试:暂无
  29. * 单元标识:$Id: CnInProcessAPIHook.pas 1146 2012-10-24 06:25:41Z liuxiaoshanzhashu@gmail.com $
  30. * 修改记录:2013.08.08 v1.0
  31. * 移植单元
  32. ================================================================================
  33. |</PRE>}
  34. interface
  35. {$I CnPack.inc}
  36. uses
  37. Windows, SysUtils, Classes;
  38. type
  39. THookType = (HT_None, HT_LONG_JMP, HT_LONG_CALL, HT_SHORT_JMP, HT_SHORT_CALL);
  40. PHT_LONG = ^HT_LONG;
  41. HT_LONG = packed record
  42. Long: Word;
  43. LongAddr: Cardinal;
  44. end;
  45. PHT_SHORT = ^HT_SHORT;
  46. HT_SHORT = packed record
  47. Short: Byte;
  48. ShortAddr: Cardinal;
  49. end;
  50. TOnAPIHookProc = function(const Params: array of Pointer): DWORD of object;
  51. { TCnHookCore }
  52. TCnHookCore = class(TObject)
  53. private
  54. FAddr: Pointer;
  55. FEvent: Pointer;
  56. FStyle: THookType;
  57. FAddrSize: Byte;
  58. FHooked: Boolean;
  59. procedure SetStyle(const Value: THookType);
  60. protected
  61. FDWSize: Cardinal;
  62. FCode: Word;
  63. FOldProtect: Cardinal;
  64. FBakCode: array[0..255] of Char;
  65. public
  66. constructor Create;
  67. procedure Hook;
  68. procedure UnHook;
  69. property Addr: Pointer read FAddr write FAddr;
  70. property AddrSize: Byte read FAddrSize write FAddrSize;
  71. property Event: Pointer read FEvent write FEvent;
  72. property Style: THookType read FStyle write SetStyle;
  73. property Hooked: Boolean read FHooked;
  74. end;
  75. PDynamicCode = ^DynamicCode;
  76. DynamicCode = packed record
  77. Push: Byte;
  78. Self: DWORD;
  79. Call: Word;
  80. CallAddr: Pointer;
  81. RetCode: Byte;
  82. RetXX: WORD;
  83. EventAddr: Pointer;
  84. ExtraData: Pointer;
  85. end;
  86. TOnHookProc = function(Data: PDynamicCode): DWORD of object;
  87. { TCnHookAddress }
  88. TCnHookAddress = class(TObject)
  89. private
  90. FHooker: TCnHookCore;
  91. FInit: Boolean;
  92. FHook: Boolean;
  93. FRetCount: Byte;
  94. FMutex: Boolean;
  95. FOnHookProc: TOnHookProc;
  96. FExtraData: Pointer;
  97. procedure SetInit(const Value: Boolean);
  98. procedure SetHook(const Value: Boolean);
  99. function GetInstructionAddr: Pointer;
  100. procedure SetInstructionAddr(const Value: Pointer);
  101. function GetInstructionSize: Byte;
  102. procedure SetInstructionSize(const Value: Byte);
  103. procedure InitHook;
  104. procedure UnInitHook;
  105. protected
  106. FHookMark: array[0..7] of Char;
  107. public
  108. constructor Create;
  109. destructor Destroy; override;
  110. property Init: Boolean read FInit write SetInit;
  111. property Hook: Boolean read FHook write SetHook;
  112. property RetCount: Byte read FRetCount write FRetCount;
  113. property InstructionAddr: Pointer read GetInstructionAddr write SetInstructionAddr;
  114. property InstructionSize: Byte read GetInstructionSize write SetInstructionSize;
  115. property Mutex: Boolean read FMutex write FMutex;
  116. property ExtraData: Pointer read FExtraData write FExtraData;
  117. property OnHookProc: TOnHookProc read FOnHookProc write FOnHookProc;
  118. end;
  119. { TCnInProcessAPIHook }
  120. TCnInProcessAPIHook = class(TComponent)
  121. private
  122. FHooker: TCnHookAddress;
  123. FDllFunction: string;
  124. FDllName: string;
  125. FActive: Boolean;
  126. FRestoreWhenOnHook: Boolean;
  127. FOnAPIHookProc: TOnAPIHookProc;
  128. function GetMutex: Boolean;
  129. procedure SetMutex(const Value: Boolean);
  130. function GetParamCount: Byte;
  131. procedure SetParamCount(const Value: Byte);
  132. procedure SetActive(const Value: Boolean);
  133. protected
  134. FHookMark: string;
  135. function OnHookProc(Data: PDynamicCode): DWORD;
  136. public
  137. constructor Create(AOwner: TComponent); override;
  138. destructor Destroy; override;
  139. published
  140. property DllFunction: string read FDllFunction write FDllFunction;
  141. property DllName: string read FDllName write FDllName;
  142. property ParamCount: Byte read GetParamCount write SetParamCount;
  143. property Mutex: Boolean read GetMutex write SetMutex;
  144. property RestoreWhenOnHook: Boolean read FRestoreWhenOnHook write FRestoreWhenOnHook;
  145. property Active: Boolean read FActive write SetActive;
  146. property OnAPIHookProc: TOnAPIHookProc read FOnAPIHookProc write FOnAPIHookProc;
  147. end;
  148. implementation
  149. { TCnHookCore }
  150. constructor TCnHookCore.Create;
  151. begin
  152. SetStyle(HT_LONG_JMP);
  153. FAddrSize := SizeOf(HT_LONG);
  154. end;
  155. procedure TCnHookCore.SetStyle(const Value: THookType);
  156. begin
  157. if Value <> FStyle then
  158. begin
  159. FStyle := Value;
  160. case FStyle of
  161. HT_LONG_JMP:
  162. begin
  163. FCode := $25FF;
  164. FDWSize := SizeOf(HT_LONG);
  165. end;
  166. HT_LONG_CALL:
  167. begin
  168. FCode := $15FF;
  169. FDWSize := SizeOf(HT_LONG);
  170. end;
  171. HT_SHORT_JMP:
  172. begin
  173. FCode := $E9;
  174. FDWSize := SizeOf(HT_SHORT);
  175. end;
  176. HT_SHORT_CALL:
  177. begin
  178. FCode := $E8;
  179. FDWSize := SizeOf(HT_SHORT);
  180. end;
  181. end;
  182. if FAddrSize < FDWSize then
  183. FAddrSize := FDWSize;
  184. end;
  185. end;
  186. procedure TCnHookCore.Hook;
  187. var
  188. Code1: PHT_LONG;
  189. Code2: PHT_SHORT;
  190. Addr: Cardinal;
  191. begin
  192. if not FHooked then
  193. begin
  194. if VirtualProtect(FAddr, FAddrSize, PAGE_EXECUTE_WRITECOPY, FOldProtect) then
  195. begin
  196. FillMemory(@FBakCode[0], 256, $90);
  197. //备份代码
  198. CopyMemory(@FBakCode[0], FAddr, FAddrSize);
  199. case FStyle of
  200. HT_LONG_JMP, HT_LONG_CALL:
  201. begin
  202. Code1 := FAddr;
  203. Code1^.Long := FCode;
  204. Code1^.LongAddr := Cardinal(@FEvent);
  205. end;
  206. HT_SHORT_JMP, HT_SHORT_CALL:
  207. begin
  208. //计算相对地址
  209. Addr := Cardinal(FEvent) - Cardinal(FAddr) - FDWSize;
  210. Code2 := FAddr;
  211. Code2^.Short := FCode;
  212. Code2^.ShortAddr := Addr;
  213. end;
  214. end;
  215. FHooked := True;
  216. end;
  217. end;
  218. end;
  219. procedure TCnHookCore.UnHook;
  220. begin
  221. if FHooked then
  222. begin
  223. CopyMemory(FAddr, @FBakCode[0], FAddrSize);
  224. VirtualProtect(FAddr, FAddrSize, FOldProtect, FOldProtect);
  225. FHooked := False;
  226. end;
  227. end;
  228. { TCnHookAddress }
  229. function DoOnHookProc(Self: TCnHookAddress): DWORD; stdcall;
  230. var
  231. hh: THandle;
  232. begin
  233. Result := 0;
  234. if Assigned(Self.OnHookProc) then
  235. begin
  236. if Self.FMutex then
  237. begin
  238. hh := OpenMutex(MUTEX_ALL_ACCESS, True, Self.FHookMark);
  239. //如果已经有就等待
  240. if hh <> 0 then
  241. WaitForSingleObject(hh, INFINITE)
  242. else
  243. hh := CreateMutex(nil, True, Self.FHookMark); //否则创建一个
  244. //执行事件
  245. Result := Self.OnHookProc(Self.FHooker.Event);
  246. //销毁事件
  247. ReleaseMutex(hh);
  248. CloseHandle(hh);
  249. end
  250. else
  251. //执行事件
  252. Result := Self.OnHookProc(Self.FHooker.Event);
  253. end;
  254. end;
  255. constructor TCnHookAddress.Create;
  256. begin
  257. FHooker := TCnHookCore.Create;
  258. end;
  259. destructor TCnHookAddress.Destroy;
  260. begin
  261. UnInitHook;
  262. FHooker.UnHook;
  263. FHooker.Free;
  264. inherited;
  265. end;
  266. function TCnHookAddress.GetInstructionAddr: Pointer;
  267. begin
  268. Result := FHooker.Addr;
  269. end;
  270. procedure TCnHookAddress.SetInstructionAddr(const Value: Pointer);
  271. begin
  272. FHooker.Addr := Value;
  273. end;
  274. function TCnHookAddress.GetInstructionSize: Byte;
  275. begin
  276. Result := FHooker.AddrSize;
  277. end;
  278. procedure TCnHookAddress.SetInstructionSize(const Value: Byte);
  279. begin
  280. FHooker.AddrSize := Value;
  281. end;
  282. procedure TCnHookAddress.SetInit(const Value: Boolean);
  283. begin
  284. if FInit = Value then
  285. Exit; //跟上次一样则什么都不做
  286. case Value of
  287. True: //初始化
  288. begin
  289. if FInit = False then
  290. begin
  291. InitHook;
  292. FInit := True;
  293. end;
  294. end;
  295. False: //反初始化
  296. begin
  297. if FInit then
  298. begin
  299. UnInitHook;
  300. FInit := False;
  301. end;
  302. end;
  303. end;
  304. end;
  305. procedure TCnHookAddress.SetHook(const Value: Boolean);
  306. begin
  307. if FHook = Value then
  308. Exit; //跟上次一样则什么都不做
  309. case Value of
  310. True: //初始化
  311. begin
  312. if FHook = False then
  313. begin
  314. FHooker.Hook;
  315. FHook := True;
  316. end;
  317. end;
  318. False: //反初始化
  319. begin
  320. if FHook then
  321. begin
  322. FHooker.UnHook;
  323. FHook := False;
  324. end;
  325. end;
  326. end;
  327. end;
  328. procedure TCnHookAddress.InitHook;
  329. type
  330. PStr = ^Str;
  331. Str = array[0..3] of Char;
  332. var
  333. FDynamicCode: PDynamicCode;
  334. Mark: string;
  335. Value1, Value2: DWORD;
  336. begin
  337. //制定类型
  338. FHooker.Style := HT_SHORT_JMP;
  339. //分配内存
  340. FDynamicCode := VirtualAlloc(nil, SizeOf(DynamicCode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  341. //保存地址
  342. FDynamicCode^.EventAddr := @DoOnHookProc;
  343. //写入相应语句
  344. FDynamicCode^.Push := $68; //PUSH
  345. FDynamicCode^.Self := DWORD(Self); //写上 Self
  346. FDynamicCode^.Call := $15FF; //CALL
  347. FDynamicCode^.CallAddr := @FDynamicCode^.EventAddr; //事件发生
  348. FDynamicCode^.RetCode := $C2; //RET
  349. FDynamicCode^.RetXX := FRetCount * 4; //RET XX
  350. FDynamicCode^.ExtraData := FExtraData; //额外数据
  351. //写入事件
  352. FHooker.Event := FDynamicCode;
  353. //制作 Mark
  354. Value1 := GetCurrentProcess;
  355. Value2 := DWORD(Self.InstructionAddr);
  356. Mark := PStr(@Value1)^ + pstr(@Value2)^;
  357. CopyMemory(@FHookMark[0], @Mark[1], 8);
  358. end;
  359. procedure TCnHookAddress.UnInitHook;
  360. var
  361. FDynamicCode: PDynamicCode;
  362. begin
  363. //释放 Hook
  364. Hook := False;
  365. FDynamicCode := FHooker.Event;
  366. FHooker.Event := nil;
  367. //释放内存
  368. VirtualFree(FDynamicCode, SizeOf(DynamicCode), MEM_DECOMMIT);
  369. end;
  370. { TCnInProcessAPIHook }
  371. constructor TCnInProcessAPIHook.Create(AOwner: TComponent);
  372. begin
  373. inherited;
  374. FRestoreWhenOnHook := True;
  375. FHooker := TCnHookAddress.Create;
  376. end;
  377. destructor TCnInProcessAPIHook.Destroy;
  378. begin
  379. FHooker.Free;
  380. inherited;
  381. end;
  382. function TCnInProcessAPIHook.GetMutex: Boolean;
  383. begin
  384. Result := FHooker.Mutex;
  385. end;
  386. procedure TCnInProcessAPIHook.SetMutex(const Value: Boolean);
  387. begin
  388. FHooker.Mutex := Value;
  389. end;
  390. function TCnInProcessAPIHook.GetParamCount: Byte;
  391. begin
  392. Result := FHooker.RetCount;
  393. end;
  394. procedure TCnInProcessAPIHook.SetParamCount(const Value: Byte);
  395. begin
  396. FHooker.RetCount := Value;
  397. end;
  398. procedure TCnInProcessAPIHook.SetActive(const Value: Boolean);
  399. type
  400. PStr = ^Str;
  401. Str = array[0..3] of Char;
  402. var
  403. Lib, Values: DWORD;
  404. tmp: string;
  405. begin
  406. if Value = FActive then
  407. Exit;
  408. FActive := Value;
  409. if not (csDesigning in ComponentState) then
  410. begin
  411. case Value of
  412. True:
  413. begin
  414. //取得地址
  415. Lib := LoadLibrary(PChar(FDllName));
  416. FHooker.InstructionAddr := GetProcAddress(Lib, PChar(FDllFunction));
  417. FreeLibrary(Lib);
  418. //固定长度
  419. FHooker.InstructionSize := 5;
  420. //事件
  421. FHooker.OnHookProc := OnHookProc;
  422. //Self
  423. FHooker.ExtraData := Self;
  424. //设置标记
  425. Values := GetCurrentProcess;
  426. tmp := PStr(@Values)^;
  427. FHookMark := CharUpper(PChar(FDllName + FDllFunction + tmp));
  428. //打开 Hook
  429. FHooker.Init := True;
  430. FHooker.Hook := True;
  431. end;
  432. False:
  433. begin
  434. //关闭 Hook
  435. FHooker.Hook := False;
  436. FHooker.Init := False;
  437. end;
  438. end;
  439. end;
  440. end;
  441. function TCnInProcessAPIHook.OnHookProc(Data: PDynamicCode): DWORD;
  442. var
  443. AESP: Pointer;
  444. OBJ: TCnInProcessAPIHook;
  445. Params: array of Pointer;
  446. Param: Pointer;
  447. _Handle: THandle;
  448. begin
  449. //取得 Self
  450. _Handle := 0;
  451. OBJ := Data^.ExtraData;
  452. if Assigned(OBJ.FOnAPIHookProc) then
  453. begin
  454. asm
  455. mov AESP, ESP
  456. end;
  457. Param := Pointer(DWORD(AESP) + $54); //参数开始,此处随编译器变动而变
  458. SetLength(Params, OBJ.ParamCount); //设置参数个数
  459. CopyMemory(@Params[0], Param, Obj.ParamCount * 4);
  460. if OBJ.Mutex then
  461. begin
  462. _Handle := OpenMutex(MUTEX_ALL_ACCESS, True, PChar(OBJ.FHookMark));
  463. //如果已经有就等待
  464. if _Handle <> 0 then
  465. WaitForSingleObject(_Handle, INFINITE)
  466. else
  467. _Handle := CreateMutex(nil, True, PChar(OBJ.FHookMark)); //否则创建一个
  468. end;
  469. //执行事件
  470. if FRestoreWhenOnHook then
  471. OBJ.FHooker.Hook := False;
  472. Result := OBJ.FOnAPIHookProc(Params);
  473. CopyMemory(Param, @Params[0], Obj.ParamCount * 4); //参数写回
  474. if FRestoreWhenOnHook then
  475. OBJ.FHooker.Hook := True;
  476. if OBJ.Mutex then
  477. begin
  478. //销毁事件
  479. ReleaseMutex(_Handle);
  480. CloseHandle(_Handle);
  481. end;
  482. end;
  483. end;
  484. end.