CnMemorySearch.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  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 CnMemorySearch;
  21. {* |<PRE>
  22. ================================================================================
  23. * 软件名称:不可视工具组件包
  24. * 单元名称:跨进程内容搜索的组件实现单元
  25. * 单元作者:CodeGame
  26. * 备 注:给定特征码表对文件或者内存区域搜索出地址及相关信息逐一比对内存。
  27. * 备 注:已知问题:搜索模块时只搜本进程模块,未能遍历其他进程的模块
  28. * 开发平台:PWinXP + Delphi 2007
  29. * 兼容测试:暂无
  30. * 单元标识:$Id: CnMemorySearch.pas 1146 2012-10-24 06:25:41Z liuxiaoshanzhashu@gmail.com $
  31. * 修改记录:2013.09.02 v1.0
  32. * 移植单元
  33. ================================================================================
  34. |</PRE>}
  35. interface
  36. {$I CnPack.inc}
  37. uses
  38. Classes, SysUtils, Tlhelp32, Windows;
  39. type
  40. TSearchMethodList = (smlSearchMemory, smlSearchFile); //搜索方法
  41. TErrList = (elFileErr, elModuleErr); //错误类型
  42. TModuleInfo = packed record
  43. CodeHeadSize: Integer;
  44. CodeStartAddr: DWORD;
  45. CodeSize: Integer;
  46. CodeBaseAddr: DWORD;
  47. end;
  48. TResultType = (rtPointer, rtPointerData, rtPointerCall); //返回数据类型
  49. TtagData= array[0..15] of Byte; {标志内容 16byte}
  50. PModuleTag = ^TModuleTag;
  51. TModuleTag = packed record
  52. TagData:TtagData;{标志内容 16byte}
  53. Offset: Integer; {偏移地址}
  54. Len: Integer; {标志长度}
  55. end;
  56. PDataItem = ^TDataItem;
  57. TDataItem = packed record
  58. FileName: string[255];
  59. ConstStr: string[255];
  60. ModuleTag: array[0..4] of TModuleTag; {5组标志}
  61. TagCount: Integer; {ModuleTag标志数量}
  62. FileOffset: DWORD; {文件偏移地址}
  63. MemoryOffset: DWORD; {内存偏移地址}
  64. ResultType: TResultType; {返回数据类型 0:返回PointerData, 1:返回PointerCall}
  65. ResultData: DWORD; {返回的内存数据}
  66. end;
  67. TDataItemList = array of TDataItem;
  68. TSearchFindEvent = procedure(const Index: Integer; pData: TDataItem) of object;
  69. TSearchCompleteEvent = procedure(const Status: Boolean) of object;
  70. TSearchErrEvent = procedure(const Status: TErrList; Msg: string) of object;
  71. TCnMemorySearchThread = class;
  72. TCnMemorySearch = class(TComponent)
  73. private
  74. FSearchCallTH: TCnMemorySearchThread;
  75. FStartSearch: Boolean;
  76. FDirectory: string;
  77. FDataList: TDataItemList;
  78. FSearchMethod: TSearchMethodList;
  79. FSearchFind: TSearchFindEvent;
  80. FSearchComplete: TSearchCompleteEvent;
  81. FSearchErrEvent: TSearchErrEvent;
  82. procedure SetStartSearch(const Val: Boolean);
  83. protected
  84. procedure DoSearchFindEvent(const Index: Integer; pData: TDataItem);
  85. procedure DoSearchCompleteEvent(const Status: Boolean);
  86. procedure DoErrEvent(const Status: TErrList; Msg: string);
  87. public
  88. constructor Create(AOwner: TComponent); override;
  89. destructor Destroy; override;
  90. property DataList: TDataItemList read FDataList;
  91. procedure SetCount(const Count: Integer);
  92. function GetCount: Integer;
  93. property StartSearch: Boolean read FStartSearch write SetStartSearch;
  94. published
  95. property Directory: string read FDirectory write FDirectory;
  96. property SearchMethod: TSearchMethodList read FSearchMethod write FSearchMethod;
  97. property OnSearchFind: TSearchFindEvent read FSearchFind write FSearchFind;
  98. property OnSearchComplete: TSearchCompleteEvent read FSearchComplete write FSearchComplete;
  99. property OnSearchError: TSearchErrEvent read FSearchErrEvent write FSearchErrEvent;
  100. end;
  101. TCnMemorySearchThread = class(TThread)
  102. private
  103. FOwner: TCnMemorySearch;
  104. function GetModuleInfo(const aModuleName: string): TModuleInfo;
  105. function GetFileInfo(const aFileStream: TMemoryStream): TModuleInfo;
  106. function MemorySearch: Boolean;
  107. function FileSearch: Boolean;
  108. protected
  109. procedure Execute; override;
  110. public
  111. constructor Create(Suspended: Boolean; AOwner: TCnMemorySearch);
  112. destructor Destroy; override;
  113. end;
  114. implementation
  115. uses
  116. CnDebug;
  117. { TCnMemorySearchThread }
  118. procedure TCnMemorySearchThread.Execute;
  119. var
  120. _SearchRet: Boolean;
  121. begin
  122. { Place thread code here }
  123. _SearchRet := False;
  124. while not Terminated do
  125. begin
  126. Sleep(1);
  127. if not FOwner.FStartSearch then
  128. Continue;
  129. try
  130. case FOwner.SearchMethod of
  131. smlSearchMemory: _SearchRet := Self.MemorySearch; //搜索内存模块模式
  132. smlSearchFile: _SearchRet := Self.FileSearch; //搜索文件模式
  133. end;
  134. finally
  135. FOwner.DoSearchCompleteEvent(_SearchRet);
  136. Self.Terminate; //搜索完成
  137. FOwner.FStartSearch := False;
  138. end;
  139. end;
  140. end;
  141. function TCnMemorySearchThread.GetFileInfo(const aFileStream: TMemoryStream): TModuleInfo;
  142. var
  143. _DosHead: IMAGE_DOS_HEADER;
  144. _NtHead: IMAGE_NT_HEADERS;
  145. begin
  146. Result.CodeHeadSize := 0;
  147. Result.CodeStartAddr := 0;
  148. Result.CodeSize := 0;
  149. if not Assigned(aFileStream) then
  150. Exit;
  151. if aFileStream.Size < 0 then
  152. Exit;
  153. CopyMemory(@_DosHead, aFileStream.Memory, Sizeof(_DosHead));
  154. if _DosHead.e_magic = IMAGE_DOS_SIGNATURE then
  155. begin
  156. CopyMemory(@_NtHead, Pointer(DWORD(aFileStream.Memory) + _DosHead._lfanew), SizeOf(_NtHead));
  157. Result.CodeHeadSize := _NtHead.OptionalHeader.SizeOfHeaders;
  158. Result.CodeStartAddr := _NtHead.OptionalHeader.SizeOfHeaders + 1;
  159. Result.CodeSize := _NtHead.OptionalHeader.SizeOfCode;
  160. Result.CodeBaseAddr := _NtHead.OptionalHeader.ImageBase + _NtHead.OptionalHeader.BaseOfCode;
  161. end;
  162. end;
  163. function TCnMemorySearchThread.GetModuleInfo(const aModuleName: string): TModuleInfo;
  164. var
  165. _ModuleSnap: Cardinal;
  166. _PId: DWORD;
  167. _Me32: MODULEENTRY32;
  168. _Handle: thandle;
  169. _lpr: cardinal;
  170. _DosHead: IMAGE_DOS_HEADER;
  171. _NtHead: IMAGE_NT_HEADERS;
  172. LowModuleName: string;
  173. begin
  174. Result.CodeHeadSize := 0;
  175. Result.CodeStartAddr := 0;
  176. Result.CodeSize := 0;
  177. _PId := 0;
  178. _ModuleSnap := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, _PId);
  179. if (_ModuleSnap <> INVALID_HANDLE_VALUE) then
  180. begin
  181. ZeroMemory(@_Me32, sizeof(MODULEENTRY32));
  182. _Me32.dwSize := sizeof(MODULEENTRY32);
  183. if (Module32First(_ModuleSnap, _Me32)) then
  184. begin
  185. LowModuleName := LowerCase(LowModuleName);
  186. repeat
  187. CnDebugger.TraceMsg(_Me32.szModule);
  188. if LowerCase(_Me32.szModule) = LowModuleName then
  189. begin
  190. _Handle := OpenProcess(PROCESS_VM_READ, True, GetCurrentProcessID);
  191. ReadProcessMemory(_Handle,
  192. _Me32.modBaseAddr,
  193. @_DosHead, Sizeof(_DosHead),
  194. _lpr);
  195. if _DosHead.e_magic = IMAGE_DOS_SIGNATURE then
  196. begin
  197. ReadProcessMemory(_Handle,
  198. Pointer(DWORD(_Me32.modBaseAddr) + _DosHead._lfanew),
  199. @_NtHead, SizeOf(_NtHead),
  200. _lpr);
  201. Result.CodeHeadSize := _NtHead.OptionalHeader.SizeOfHeaders;
  202. Result.CodeStartAddr := _NtHead.OptionalHeader.ImageBase + _NtHead.OptionalHeader.BaseOfCode;
  203. Result.CodeSize := _NtHead.OptionalHeader.BaseOfData;
  204. end;
  205. Break;
  206. end;
  207. until (Module32Next(_ModuleSnap, _Me32) = False)
  208. end;
  209. end;
  210. end;
  211. function TCnMemorySearchThread.MemorySearch: Boolean;
  212. var
  213. _ItemIndex, _CodePosition, _TagIndex, _FindCount: Integer;
  214. _ModuleInfo: TModuleInfo;
  215. _FileName: string;
  216. begin
  217. Result := False;
  218. if FOwner.FDataList = nil then
  219. Exit;
  220. if FOwner.GetCount < 1 then
  221. Exit;
  222. for _ItemIndex := 0 to FOwner.GetCount - 1 do //枚举所有待查数据表内数据
  223. begin
  224. _FileName := FOwner.FDataList[_ItemIndex].FileName; //内存搜索不需要全路径名
  225. _ModuleInfo := GetModuleInfo(_FileName); //取得模块信息
  226. if (_ModuleInfo.CodeStartAddr <= 0) or (_ModuleInfo.CodeSize <= 0) then
  227. begin
  228. FOwner.DoErrEvent(elModuleErr, Format('%s,模块打开错误!', [FOwner.FDataList[_ItemIndex].FileName]));
  229. Continue; //下一条数据
  230. end;
  231. for _CodePosition := 0 to _ModuleInfo.CodeSize - 1 do //从文件开始搜索
  232. begin
  233. _FindCount := 0; //查到标志数量
  234. for _TagIndex := 0 to FOwner.FDataList[_ItemIndex].TagCount - 1 do
  235. begin
  236. if CompareMem(Pointer(_ModuleInfo.CodeStartAddr + FOwner.FDataList[_ItemIndex].ModuleTag[_TagIndex].Offset + _CodePosition),
  237. @FOwner.FDataList[_ItemIndex].ModuleTag[_TagIndex], FOwner.FDataList[_ItemIndex].ModuleTag[_TagIndex].Len) then Inc(_FindCount);
  238. end;
  239. if _FindCount = FOwner.FDataList[_ItemIndex].TagCount then
  240. begin
  241. FOwner.FDataList[_ItemIndex].MemoryOffset := _ModuleInfo.CodeStartAddr + _CodePosition;
  242. FOwner.FDataList[_ItemIndex].FileOffset := FOwner.FDataList[_ItemIndex].MemoryOffset - _ModuleInfo.CodeStartAddr + _ModuleInfo.CodeHeadSize; //文件地址
  243. case FOwner.FDataList[_ItemIndex].ResultType of
  244. rtPointer: FOwner.FDataList[_ItemIndex].ResultData := FOwner.FDataList[_ItemIndex].MemoryOffset;
  245. rtPointerData: FOwner.FDataList[_ItemIndex].ResultData := PDword(FOwner.FDataList[_ItemIndex].MemoryOffset)^;
  246. rtPointerCall: FOwner.FDataList[_ItemIndex].ResultData := FOwner.FDataList[_ItemIndex].MemoryOffset + PInteger(FOwner.FDataList[_ItemIndex].MemoryOffset)^ + 4;
  247. end;
  248. FOwner.DoSearchFindEvent(_ItemIndex, FOwner.FDataList[_ItemIndex]);
  249. Break; //下条数据
  250. end;
  251. end;
  252. end;
  253. Result := True;
  254. end;
  255. function TCnMemorySearchThread.FileSearch: Boolean;
  256. var
  257. _ItemIndex, _CodePosition, _TagIndex, _FindCount: Integer;
  258. _MemoryFile: TMemoryStream;
  259. _ModuleInfo: TModuleInfo;
  260. _FileName: string;
  261. begin
  262. Result := False;
  263. if FOwner.FDataList = nil then
  264. Exit;
  265. if FOwner.GetCount < 1 then
  266. Exit;
  267. _MemoryFile := TMemoryStream.Create;
  268. try
  269. for _ItemIndex := 0 to FOwner.GetCount - 1 do //枚举所有待查数据表内数据
  270. begin
  271. _FileName := FOwner.Directory + FOwner.FDataList[_ItemIndex].FileName;
  272. if not FileExists(_FileName) then //文件不存在
  273. begin
  274. FOwner.DoErrEvent(elFileErr, Format('%s,文件打开错误!', [FOwner.FDataList[_ItemIndex].FileName]));
  275. Continue; //下一条数据
  276. end;
  277. _MemoryFile.LoadFromFile(_FileName);
  278. _ModuleInfo := GetFileInfo(_MemoryFile); //取得模块信息
  279. if (_ModuleInfo.CodeSize = 0) or (_ModuleInfo.CodeStartAddr = 0) then
  280. begin
  281. FOwner.DoErrEvent(elFileErr, Format('%s,文件格式错误!', [FOwner.FDataList[_ItemIndex].FileName]));
  282. Continue; //下一条数据
  283. end;
  284. _MemoryFile.Position := 0;
  285. for _CodePosition := 0 to _ModuleInfo.CodeSize - 1 do //从文件开始搜索
  286. begin
  287. _FindCount := 0; //查到标志数量
  288. for _TagIndex := 0 to FOwner.FDataList[_ItemIndex].TagCount - 1 do
  289. begin
  290. if CompareMem(Pointer(DWORD(_MemoryFile.Memory) + _ModuleInfo.CodeStartAddr + FOwner.FDataList[_ItemIndex].ModuleTag[_TagIndex].Offset + _CodePosition),
  291. @FOwner.FDataList[_ItemIndex].ModuleTag[_TagIndex], FOwner.FDataList[_ItemIndex].ModuleTag[_TagIndex].Len) then Inc(_FindCount);
  292. end;
  293. if _FindCount = FOwner.FDataList[_ItemIndex].TagCount then
  294. begin
  295. FOwner.FDataList[_ItemIndex].FileOffset := _ModuleInfo.CodeStartAddr + _CodePosition;
  296. FOwner.FDataList[_ItemIndex].MemoryOffset := FOwner.FDataList[_ItemIndex].FileOffset + _ModuleInfo.CodeBaseAddr - _ModuleInfo.CodeHeadSize;
  297. case FOwner.FDataList[_ItemIndex].ResultType of
  298. rtPointer: FOwner.FDataList[_ItemIndex].ResultData := FOwner.FDataList[_ItemIndex].MemoryOffset;
  299. rtPointerData: FOwner.FDataList[_ItemIndex].ResultData := PDWord(FOwner.FDataList[_ItemIndex].FileOffset + DWORD(_MemoryFile.Memory))^;
  300. rtPointerCall: FOwner.FDataList[_ItemIndex].ResultData := FOwner.FDataList[_ItemIndex].MemoryOffset + PInteger(FOwner.FDataList[_ItemIndex].FileOffset + DWORD(_MemoryFile.Memory))^ + 4;
  301. end;
  302. FOwner.DoSearchFindEvent(_ItemIndex, FOwner.FDataList[_ItemIndex]);
  303. Break; //下条数据
  304. end;
  305. end;
  306. end;
  307. Result := True;
  308. finally
  309. FreeAndNil(_MemoryFile);
  310. end;
  311. end;
  312. constructor TCnMemorySearchThread.Create(Suspended: Boolean; AOwner: TCnMemorySearch);
  313. begin
  314. inherited Create(Suspended);
  315. FreeOnTerminate := True;
  316. FOwner := AOwner;
  317. end;
  318. destructor TCnMemorySearchThread.Destroy;
  319. begin
  320. {.....}
  321. FOwner := nil;
  322. inherited Destroy;
  323. end;
  324. { TCnMemorySearch }
  325. constructor TCnMemorySearch.Create(AOwner: TComponent);
  326. begin
  327. inherited Create(AOwner);
  328. end;
  329. destructor TCnMemorySearch.Destroy;
  330. begin
  331. inherited Destroy;
  332. end;
  333. procedure TCnMemorySearch.DoErrEvent(const Status: TErrList; Msg: string);
  334. begin
  335. if Assigned(FSearchErrEvent) then
  336. try
  337. FSearchErrEvent(Status, Msg);
  338. except
  339. ;
  340. end;
  341. end;
  342. procedure TCnMemorySearch.DoSearchCompleteEvent(const Status: Boolean);
  343. begin
  344. if Assigned(FSearchComplete) then
  345. try
  346. FSearchComplete(Status);
  347. except
  348. ;
  349. end;
  350. end;
  351. procedure TCnMemorySearch.DoSearchFindEvent(const Index: Integer; pData: TDataItem);
  352. begin
  353. if Assigned(FSearchFind) then
  354. try
  355. FSearchFind(Index, pData);
  356. except
  357. ;
  358. end;
  359. end;
  360. function TCnMemorySearch.GetCount: Integer;
  361. begin
  362. Result := Length(FDataList);
  363. end;
  364. procedure TCnMemorySearch.SetCount(const Count: Integer);
  365. begin
  366. SetLength(FDataList, Count);
  367. end;
  368. procedure TCnMemorySearch.SetStartSearch(const Val: Boolean);
  369. begin
  370. // if FStartSearch = Val then Exit;
  371. if Val then
  372. begin
  373. if Assigned(FSearchCallTH) then
  374. FSearchCallTH.Terminate;
  375. FSearchCallTH := TCnMemorySearchThread.Create(False, Self);
  376. end
  377. else
  378. begin
  379. if Assigned(FSearchCallTH) then
  380. FSearchCallTH.Terminate;
  381. FSearchCallTH := nil;
  382. end;
  383. FStartSearch := Val;
  384. end;
  385. end.