//这个是HookAPI的类,不是我写的,是delphi深入windows下编程那本书里的<br><br>unit UnitNt2000Hook;<br><br>interface<br><br>uses classes, Windows,SysUtils, messages,dialogs;<br><br>type<br> TImportCode = packed record<br> JumpInstruction: Word;<br> AddressOfPointerToFunction: PPointer;<br> end;<br> PImportCode = ^TImportCode;<br> PImage_Import_Entry = ^Image_Import_Entry;<br> Image_Import_Entry = record<br> Characteristics: DWORD;<br> TimeDateStamp: DWORD;<br> MajorVersion: Word;<br> MinorVersion: Word;<br> Name: DWORD;<br> LookupTable: DWORD;<br> end;<br> TLongJmp = packed record<br> JmpCode: ShortInt; {指令,用$E9来代替系统的指令}<br> FuncAddr: DWORD; {函数地址}<br> end;<br><br> THookClass = class<br> private<br> Trap:boolean; {调用方式:True陷阱式,False改引入表式}<br> hProcess: Cardinal; {进程句柄,只用于陷阱式}<br> AlreadyHook:boolean; {是否已安装Hook,只用于陷阱式}<br> AllowChange:boolean; {是否允许安装、卸载Hook,只用于改引入表式}<br> Oldcode: array[0..4]of byte; {系统函数原来的前5个字节}<br> Newcode: TLongJmp; {将要写在系统函数的前5个字节}<br> private<br> public<br> OldFunction,NewFunction
ointer;{被截函数、自定义函数}<br> constructor Create(IsTrap:boolean;OldFun,NewFun
ointer);<br> constructor Destroy;<br> procedure Restore;<br> procedure Change;<br> published<br> end;<br><br>implementation<br><br>{取函数的实际地址。如果函数的第一个指令是Jmp,则取出它的跳转地址(实际地址),这往往是由于程序中含有Debug调试信息引起的}<br>function FinalFunctionAddress(Code: Pointer): Pointer;<br>Var<br> func: PImportCode;<br>begin<br> Result:=Code;<br> if Code=nil then exit;<br> try<br> func:=code;<br> if (func.JumpInstruction=$25FF) then<br> {指令二进制码FF 25 汇编指令jmp [...]}<br> Func:=func.AddressOfPointerToFunction^;<br> result:=Func;<br> except<br> Result:=nil;<br> end;<br>end;<br><br>{更改引入表中指定函数的地址,只用于改引入表式}<br>function PatchAddressInModule(BeenDone:Tlist;hModule: THandle; OldFunc,NewFunc: Pointer):integer;<br>const<br> SIZE=4;<br>Var<br> Dos: PImageDosHeader;<br> NT: PImageNTHeaders;<br> ImportDesc: PImage_Import_Entry;<br> rva: DWORD;<br> Func: PPointer;<br> DLL: String;<br> f: Pointer;<br> written: DWORD;<br> mbi_thunk:TMemoryBasicInformation;<br> dwOldProtect
WORD;<br>begin<br> Result:=0;<br> if hModule=0 then exit;<br> Dos:=Pointer(hModule);<br> {如果这个DLL模块已经处理过,则退出。BeenDone包含已处理的DLL模块}<br> if BeenDone.IndexOf(Dos)>=0 then exit;<br> BeenDone.Add(Dos);{把DLL模块名加入BeenDone}<br> OldFunc:=FinalFunctionAddress(OldFunc);{取函数的实际地址}<br><br> {如果这个DLL模块的地址不能访问,则退出}<br> if IsBadReadPtr(Dos,SizeOf(TImageDosHeader)) then exit;<br> {如果这个模块不是以'MZ'开头,表明不是DLL,则退出}<br> if Dos.e_magic<>IMAGE_DOS_SIGNATURE then exit;{IMAGE_DOS_SIGNATURE='MZ'}<br><br> {定位至NT Header}<br> NT :=Pointer(Integer(Dos) + dos._lfanew);<br> {定位至引入函数表}<br> RVA:=NT^.OptionalHeader.<br> DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;<br> if RVA=0 then exit;{如果引入函数表为空,则退出}<br> {把函数引入表的相对地址RVA转换为绝对地址}<br> ImportDesc := pointer(DWORD(Dos)+RVA);{Dos是此DLL模块的首地址}<br><br> {遍历所有被引入的下级DLL模块}<br> While (ImportDesc^.Name<>0) do<br> begin<br> {被引入的下级DLL模块名字}<br> DLL:=PChar(DWORD(Dos)+ImportDesc^.Name);<br> {把被导入的下级DLL模块当做当前模块,进行递归调用}<br> PatchAddressInModule(BeenDone,GetModuleHandle(PChar(DLL)),OldFunc,NewFunc);<br><br> {定位至被引入的下级DLL模块的函数表}<br> Func:=Pointer(DWORD(DOS)+ImportDesc.LookupTable);<br> {遍历被引入的下级DLL模块的所有函数}<br> While Func^<>nil do<br> begin<br> f:=FinalFunctionAddress(Func^);{取实际地址}<br> if f=OldFunc then {如果函数实际地址就是所要找的地址}<br> begin<br> VirtualQuery(Func,mbi_thunk, sizeof(TMemoryBasicInformation));<br> VirtualProtect(Func,SIZE,PAGE_EXECUTE_WRITECOPY,mbi_thunk.Protect);{更改内存属性}<br> WriteProcessMemory(GetCurrentProcess,Func,@NewFunc,SIZE,written);{把新函数地址覆盖它}<br> VirtualProtect(Func, SIZE, mbi_thunk.Protect,dwOldProtect);{恢复内存属性}<br> end;<br> If Written=4 then Inc(Result);<br>// else showmessagefmt('error:%d',[Written]);<br> Inc(Func);{下一个功能函数}<br> end;<br> Inc(ImportDesc);{下一个被引入的下级DLL模块}<br> end;<br>end;<br><br>{HOOK的入口,其中IsTrap表示是否采用陷阱式}<br>constructor THookClass.Create(IsTrap:boolean;OldFun,NewFun
ointer);<br>begin<br> {求被截函数、自定义函数的实际地址}<br> OldFunction:=FinalFunctionAddress(OldFun);<br> NewFunction:=FinalFunctionAddress(NewFun);<br><br> Trap:=IsTrap;<br> if Trap then{如果是陷阱式}<br> begin<br> {以特权的方式来打开当前进程}<br> hProcess := OpenProcess(PROCESS_ALL_ACCESS,FALSE, GetCurrentProcessID);<br> {生成jmp xxxx的代码,共5字节}<br> Newcode.JmpCode := ShortInt($E9); {jmp指令的十六进制代码是E9}<br> NewCode.FuncAddr := DWORD(NewFunction) - DWORD(OldFunction) - 5;<br> {保存被截函数的前5个字节}<br> move(OldFunction^,OldCode,5);<br> {设置为还没有开始HOOK}<br> AlreadyHook:=false;<br> end;<br> {如果是改引入表式,将允许HOOK}<br> if not Trap then AllowChange:=true;<br> Change; {开始HOOK}<br> {如果是改引入表式,将暂时不允许HOOK}<br> if not Trap then AllowChange:=false;<br>end;<br><br>{HOOK的出口}<br>constructor THookClass.Destroy;<br>begin<br> {如果是改引入表式,将允许HOOK}<br> if not Trap then AllowChange:=true;<br> Restore; {停止HOOK}<br> if Trap then{如果是陷阱式}<br> CloseHandle(hProcess);<br>end;<br><br>{开始HOOK}<br>procedure THookClass.Change;<br>var<br> nCount: DWORD;<br> BeenDone: TList;<br>begin<br> if Trap then{如果是陷阱式}<br> begin<br> if (AlreadyHook)or (hProcess = 0) or (OldFunction = nil) or (NewFunction = nil) then<br> exit;<br> AlreadyHook:=true;{表示已经HOOK}<br> WriteProcessMemory(hProcess, OldFunction, @(Newcode), 5, nCount);<br> end<br> else begin{如果是改引入表式}<br> if (not AllowChange)or(OldFunction=nil)or(NewFunction=nil)then exit;<br> BeenDone:=TList.Create; {用于存放当前进程所有DLL模块的名字}<br> try<br> PatchAddressInModule(BeenDone,GetModuleHandle(nil),OldFunction,NewFunction);<br> finally<br> BeenDone.Free;<br> end;<br> end;<br>end;<br><br>{恢复系统函数的调用}<br>procedure THookClass.Restore;<br>var<br> nCount: DWORD;<br> BeenDone: TList;<br>begin<br> if Trap then{如果是陷阱式}<br> begin<br> if (not AlreadyHook) or (hProcess = 0) or (OldFunction = nil) or (NewFunction = nil) then<br> exit;<br> WriteProcessMemory(hProcess, OldFunction, @(Oldcode), 5, nCount);<br> AlreadyHook:=false;{表示退出HOOK}<br> end<br> else begin{如果是改引入表式}<br> if (not AllowChange)or(OldFunction=nil)or(NewFunction=nil)then exit;<br> BeenDone:=TList.Create;{用于存放当前进程所有DLL模块的名字}<br> try<br> PatchAddressInModule(BeenDone,GetModuleHandle(nil),NewFunction,OldFunction);<br> finally<br> BeenDone.Free;<br> end;<br> end;<br>end;<br><br>end.