当前位置:编程学习 > 网站相关 >>

利用Hook API实现进程守护

作者:Zorro [CUST&AD]
关于进程的隐藏有很多方式可以实现,黒防以前的杂志也讲过不少例子。我就是不想把进程隐藏,呵呵。那么有什么方法可以实现进程守护呢?前一阵子我研究了一个AV终结者的变种,进程守护的方式是通过两个进程相互关联实现的。这种方式虽然手工无法干掉,可是写个专杀采用多线程就能干掉。不完美啊,两个进程总让人感到别扭。经过研究,我通过HOOK API技术实现了进程守护。

原理简单分析
Hook API是个好东西,虽然微软不提倡Hook API,但应用却非常广泛,关于这方面的资料也很多。像金山词霸的屏幕取词翻译功能就是用这种技术实现的,它主要挂钩了TextOut函数,这样就能拦截到该函数的参数,也就是屏幕上的文字并进行翻译工作了。关于进程守护,如果一个进程试图结束另一个进程,不管用什么方法它都要先获取该进程的HANDLE,只要我们HOOK住了OpenProcess这个API,指向我们自定义的MY_OpenProcess函数,基本就能实现进程守护了。
有两种方法能够实现HOOK API,一种是“改引入表式”,另一种是“陷阱式”。我就使用第一种方法来实现,这种方法易于实现,但要求你熟悉PE文件结构。主要是定位到Kernel32.dll模块的OpenProcess函数地址,并用WriteProcessMemory函数改写函数地址。
  
DLL模块主要代码

unit HookAPI;

interface
……
//获取函数真实地址
function TrueFunctionAddress(Code: Pointer): Pointer;
var
func: PImportCode;
begin
Result := Code;
if Code = nil then exit;
try
func := code;
if (func.JumpInstruction=$25FF) then begin
Result := func.AddressOfPointerToFunction^;
end;
except
Result := nil;
end;
end;
//改引入表模块
Function WriteFunction(OldFunc, NewFunc: Pointer):Integer;
var
BeenDone: TList;

Function WriteAddrInModule(hModule: THandle; OldFunc,NewFunc: Pointer): Integer;
var
Dos : PImageDosHeader;
NT : PImageNTHeaders;
ImportDesc : PImage_Import_Entry;
RVA : DWORD;
Func : ^Pointer;
DLL : String;
f : Pointer;
written : DWORD;
begin
Result := 0;
Dos := Pointer(hModule);
if BeenDone.IndexOf(Dos) >= 0 then exit;
BeenDone.Add(Dos);
OldFunc := TrueFunctionAddress(OldFunc);//函数的实际地址
if IsBadReadPtr(Dos,SizeOf(TImageDosHeader)) then exit;

if Dos.e_magic <> IMAGE_DOS_SIGNATURE then exit;//IMAGE_DOS_SIGNATURE=MZ
{定位至NT Header}
NT := Pointer(Integer(Dos) + dos._lfanew);
{定位至引入函数表}
RVA := NT^.OptionalHeader.DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
if RVA = 0 then exit; //如果引入函数表为空,则退出
ImportDesc := pointer(integer(Dos)+RVA);

While(ImportDesc^.Name<>0) do
begin
DLL := PChar(Integer(Dos) + ImportDesc^.Name);
WriteAddrInModule(GetModuleHandle(PChar(DLL)),OldFunc,NewFunc);
//定位至被引入的下级DLL模块的函数表
Func := Pointer(Integer(DOS) + ImportDesc.LookupTable);
While Func^ <> nil do
begin
f := TrueFunctionAddress(Func^);
if f = OldFunc then //如果函数实际地址就是所要找的地址
begin
WriteProcessMemory(GetCurrentProcess,Func,@NewFunc,4,written);
//把新函数地址覆盖它
If Written > 0 then Inc(Result);
end;
Inc(Func); //如果函数实际地址就是所要找的地址
end;
Inc(ImportDesc); //下一个被引入的下级DLL模块
end;
end;

begin
BeenDone := TList.Create;
try
Result := WriteAddrInModule(GetModuleHandle(nil),OldFunc,NewFunc);
finally
BeenDone.Free;
end;
end;

//自定义My_OpenProcess 替换OpenProcess
Function My_OpenProcess(dwDesiredAccess:DWORD;bInheritHandle:boolean;dwProcessId:DWORD):thandle;stdcall;
var
hWnd:THandle;
ProcessId:DWORD;
begin
hWnd := FindWindow(nil, HOOK API); //MainEXE.exe窗体
GetWindowThreadProcessId(hWnd, ProcessId); //线程ID

if (dwProcessId <> ProcessId) then
//如果不是Main.exe进程则打开
RESULT:= OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId); //调用OpenProcess
end;
//---------------------------------------
Function HookFunction(nCode:Integer;WParam:WPARAM;LParam:LPARAM):LRESULT;stdcall;export;
var
OldFunction:Pointer;
begin
OldFunction:=GetProcAddress(GetModuleHandle(Kernel32.DLL),OpenProcess);//函数原地址
WriteFunction(OldFunction,@My_OpenProcess);
Result:=CallNextHookEx(h_hook, ncode, wParam, lParam);
end;
end.

需要注意的是,自定义的替换函数参数个数和类型以及返回值的类型必须与原API函数一致,否则会调用出错,可能会有意外的收获哦,呵呵!我在测试时经常出现调用出错直接导致死机甚至蓝屏。另外,如果检查到进程不是我们的进程ManiEXE.exe,需要调用函数打开,否则所有进程就都结束不了了。

主程序代码
这里我把DLL安装到了任务管理器进程中,并没有将DLL安装到系统全局。大家也可以自己选择插入的进程。添加一个Timer控件监视任务管理器,即安装钩子,程序执行后的效果如图1所示。尝试结束进程时无法结束,提示句柄无效,说明没有成功调用OpenProcess。主程序关键代码如下。

图1

procedure TForm1.Timer1Timer(Sender: TObject);
var
hHookDll,htaskmar,ThreadID:thandle;
pHookFunction:Pointer;
begin
htaskmar:=FindWindow(nil,Windows 任务管理器);
if htaskmar<>0 then
begin
//获得窗口线程ID
ThreadID:=GetWindowThreadProcessId(htaskmar,nil); //线程ID
ifThreadID<>0then
begin
hHookDll:=LoadLibrary(Hook.dll); //动态加载DLL
ifhHookDll<>0then
begin
pHookFunction:=GetProcAddress(hHookDll,HookFunction); //获取DLL导出函数地址
ifpHookFunction<>nilthen
begin
//安装钩子
h_Hook:=SetWindowsHookEx(3,pHookFunction,hHookDll,ThreadID);
end;
end;
end;
end
else
begin
ifh_Hook<>0then
UnhookWindowsHookEx(h_Hook); //卸载钩子
end;
end;
//----------------------------------
procedure TForm1.Button1Click(Sender: TObject);
begin
Timer1.Enabled:=True;
end;
end
补充:综合编程 , 安全编程 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,