【文章标题】: 必备绝技--Hook大法(上)
【文章作者】: LvG
【作者邮箱】: LvG2008@gmail.com
【作者声明】: 这没有什么新鲜东西,其内容全部来自于前辈,姑且当作学习笔记。文字用自己的话写出,四段代码均出自别人(知道作者的,以注明),但短小精悍,就写在一起了,便于察看。欢迎指正。
--------------------------------------------------------------------------------
【详细过程】
hook概念:是一种通过更改程序的数据结构或代码结构从而改变程序运行路线的一种方法。(纯属本人自己观点)
分类:从上面的概念来看,一种是改变程序的数据结构,如:IAT-hook,Dll-inject及Direct Kernel Object Manipulation(DKOM)。一种是Inline Function Hooking。
用途:现在这种方法普遍运用于各类程序中,如加壳,杀软,病毒,Rootkits等等。
本文从难以程度上主要分三块详细介绍:一.用户模式Hook:IAT-hook,Dll-inject二.内核模式Hook:ssdt-hook,idt-hook,int 2e/sysenter-hook三.Inline Function Hook;
这次先来看第一部分
Ⅰ.用户模式Hook
一.IAT-hooking
(一)一般原理:IAT是Import Address Table(输入地址表)的简写,这需要你知道关于win PE格式的了解。现在应用程序中的大多数函数都是windows api,而这些函数一般都由几个系统dll导出,如user32.dll,kernel32.dll,advapi32.dll等。如果程序要运用这些函数,就的从这些dll文件中导入,程序会把导入的函数放到一个叫IAT的数据结构中。我们可以先找到自己需要hook的函数,然后把目标函数的地址改成我们自己的hook函数,最后在恢复到目标函数的地址。这样一来,目标函数被调用时,我们的hook函数也就别调用了。如果这个hook函数是病毒,是后门,是。。。。。。。。由于是在目标函数进程的空间内,所以这个hook函数也就不会被发现。
关于WIN PE格式的详细知识可参见:http://bbs.pediy.com/showthread.php?t=31840及<<加密与解密>>。
(二)大体框架:这里用伪码给个一般框架,以便有个大体印象。
文件1:myhookfun()
{
可以创建一个新的线程,去执行木马或后门等功能
}
文件2: include <文件1>
寻找目标模块(GetModuleHandle),目标函数(GetProcAddress)
if(目标函数找到)
根据pe结构,在目标模块中定位目标函数的IAT地址(这个地址在加载时就确定了)
if (目标函数在IAT中的地址找到)
用我们的myhookfun()地址取代
esle 退出
esle 退出
当然也可以合成一个文件,但这样分开的好处是可以实现模块化,可以分别关心各自的功能,也便于以后重用。
(三)代码实例:
.486
.model flat, stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
.data
szMsgTitle db "IAT Hook", 0
szModule db "user32.dll", 0
szTargetFunc db "GetForegroundWindow", 0
szHooked db "This is in the hooked function - Seems to have worked.", 0
szFail db "Failed.", 0
.data?
IATHook PROTO STDCALL :DWORD, :DWORD, :DWORD
HookProc PROTO STDCALL :LPVOID
.code
HookProc proc Arg1:LPVOID
invoke MessageBox, NULL, addr szHooked, addr szMsgTitle, MB_OK
ret
HookProc endp
IATHook proc pDLLName:LPVOID, pOldAddr:LPVOID, pNewAddr:LPVOID
LOCAL hModule:HANDLE
LOCAL dwVirtualAddr:DWORD
LOCAL dwOrigProtect:DWORD
LOCAL dwDllFound:DWORD
LOCAL dwFunctionFound:DWORD
;Local variables
.if pDLLName == NULL
;Check for NULL pointer
xor eax, eax
ret
.endif
.if pOldAddr == NULL
;Check for NULL pointer
xor eax, eax
ret
.endif
.if pOldAddr == NULL
;Check for NULL pointer
xor eax, eax
ret
.endif
mov dwDllFound, 0
mov dwFunctionFound, 0
;Initialize
invoke GetModuleHandle, NULL
;Get the main module's base address
mov hModule, eax
;Copy it into hModule
mov edi, hModule
assume edi:ptr IMAGE_DOS_HEADER
;Make edi act as IMAGE_DOS_HEADER struct
.if edi == NULL
;Check for NULL pointer
xor eax, eax
ret
;Return 0
.endif
.if [edi].e_magic != IMAGE_DOS_SIGNATURE
;0x4D 0x5A (MZ)
xor eax, eax
ret
;Return 0
.endif
add edi, [edi].e_lfanew
;pNtHeader = (IMAGE_NT_HEADERS*)((DWORD)pDosHeader + (DWORD)pDosHeader->e_lfanew);
assume edi:ptr IMAGE_NT_HEADERS
;Make edi act as IMAGE_NT_HEADERS struct
.if edi == NULL
;Check for NULL pointer
xor eax, eax
ret
;Return 0
.endif
.if [edi].Signature != IMAGE_NT_SIGNATURE
;If it's an invalid NT header
;0x50 0x45 0x00 0x00 (PE/0/0)
xor eax, eax
ret
;Return 0
.endif
mov edx, [edi].OptionalHeader.DataDirectory[sizeof IMAGE_DATA_DIRECTORY].VirtualAddress
mov dwVirtualAddr, edx
;Copy the VirtualAddress into dwVirtualAddr
.if dwVirtualAddr == 0
;Invalid virtual address
xor eax, eax
ret
;Return 0
.endif
mov edi, hModule
add edi, dwVirtualAddr
;pImportHeader = (IMAGE_IMPORT_DESCRIPTOR*)((DWORD)pDosHeader + dwVirtualAddr);
assume edi:ptr IMAGE_IMPORT_DESCRIPTOR
;Make edi act as IMAGE_IMPORT_DESCRIPTOR struct
.if edi == NULL
;Check for NULL pointer
xor eax, eax
ret
;Return 0
.endif
.while [edi].Name1 != NULL
mov ecx, hModule
add ecx, [edi].Name1
;pModuleLabel = (char*)((DWORD)pDosHeader + (DWORD)pImportHeader->Name);
mov edx, pDLLName
invoke lstrcmpi, ecx, edx
;Check if this is the DLL we are looking for
.if eax == 0
;This is the DLL we are looking for
mov dwDllFound, 1
;Set ecx to 0, so we know later if the DLL was found
.break
.endif
add edi, sizeof IMAGE_IMPORT_DESCRIPTOR
;Next DLL
.endw
.if dwDllFound != 1
;If the DLL wasn't found
xor eax, eax
ret
;Return 0
.endif
mov edi, [edi].FirstThunk
add edi, hModule
;pThunkData = (IMAGE_THUNK_DATA*)((DWORD)pDosHeader + (DWORD)pImportHeader->FirstThunk);
assume edi:ptr IMAGE_THUNK_DATA
;Make edi act as IMAGE_THUNK_DATA struct
.while [edi].u1.Function != NULL
mov ecx, hModule
add ecx, [edi].u1.Function
mov edx, [edi].u1.Function
;Copies the current functions address (in the IAT table) into edx
.if pOldAddr == edx
;If this is the function we are going to hook
lea ebx, [edi].u1.Function
;Copy the address in the table that the function is stored in, into ebx
invoke VirtualProtect, ebx, 4, PAGE_WRITECOPY, addr dwOrigProtect
;Unprotect the memory where we are going to overwrite (We need 4 bytes --- DWORD = 4 bytes)
mov eax, pNewAddr
;Copy the address we are going to replace it with into eax
mov [ebx], eax
;Patch the address
invoke VirtualProtect, ebx, 4, addr dwOrigProtect, NULL
;Restore the original protection level
mov dwFunctionFound, 1
;Set the value, for later
.break
.endif
add edi, sizeof IMAGE_THUNK_DATA
;Next thunk
.endw
.if dwFunctionFound != 1
;If the function wasn't found
xor eax, eax
ret
;Return 0
.endif
mov eax, 1
ret
;Return 1
;Success
IATHook endp
start:
invoke GetModuleHandle, addr szModule
invoke GetProcAddress, eax, addr szTargetFunc
mov ebx, HookProc
invoke IATHook, addr szModule, eax, ebx ;Redirect GetForegroundWindow (eax) to HookProc (ebx)
.if eax == 0
invoke MessageBox, NULL, addr szFail, addr szMsgTitle, MB_OK
invoke ExitProcess, 0
.endif
;Is now hooked, hopefully.. so lets call it
invoke GetForegroundWindow
invoke ExitProcess, 0
end start
(四)局限性:1当程序运用一种叫late-demand binding技术,函数被调用时才定位地址,这样以来就不能在IAT中定位目标函数地址了.2当目标程序用动态加载(LoadLibrary)时,这种方法也将失效.
二.Dll-Injecting
(一)通过注册表注入Dll
1.一般原理:Windows的注册表中有这样一个键值,
HKEY_LOCAL_MACHINE/Software/Microsoft/Windows NT/CurrentVersion/Windows/AppInit_Dlls
【文章作者】: LvG
【作者邮箱】: LvG2008@gmail.com
【作者声明】: 这没有什么新鲜东西,其内容全部来自于前辈,姑且当作学习笔记。文字用自己的话写出,四段代码均出自别人(知道作者的,以注明),但短小精悍,就写在一起了,便于察看。欢迎指正。
--------------------------------------------------------------------------------
【详细过程】
hook概念:是一种通过更改程序的数据结构或代码结构从而改变程序运行路线的一种方法。(纯属本人自己观点)
分类:从上面的概念来看,一种是改变程序的数据结构,如:IAT-hook,Dll-inject及Direct Kernel Object Manipulation(DKOM)。一种是Inline Function Hooking。
用途:现在这种方法普遍运用于各类程序中,如加壳,杀软,病毒,Rootkits等等。
本文从难以程度上主要分三块详细介绍:一.用户模式Hook:IAT-hook,Dll-inject二.内核模式Hook:ssdt-hook,idt-hook,int 2e/sysenter-hook三.Inline Function Hook;
这次先来看第一部分
Ⅰ.用户模式Hook
一.IAT-hooking
(一)一般原理:IAT是Import Address Table(输入地址表)的简写,这需要你知道关于win PE格式的了解。现在应用程序中的大多数函数都是windows api,而这些函数一般都由几个系统dll导出,如user32.dll,kernel32.dll,advapi32.dll等。如果程序要运用这些函数,就的从这些dll文件中导入,程序会把导入的函数放到一个叫IAT的数据结构中。我们可以先找到自己需要hook的函数,然后把目标函数的地址改成我们自己的hook函数,最后在恢复到目标函数的地址。这样一来,目标函数被调用时,我们的hook函数也就别调用了。如果这个hook函数是病毒,是后门,是。。。。。。。。由于是在目标函数进程的空间内,所以这个hook函数也就不会被发现。
关于WIN PE格式的详细知识可参见:http://bbs.pediy.com/showthread.php?t=31840及<<加密与解密>>。
(二)大体框架:这里用伪码给个一般框架,以便有个大体印象。
文件1:myhookfun()
{
可以创建一个新的线程,去执行木马或后门等功能
}
文件2: include <文件1>
寻找目标模块(GetModuleHandle),目标函数(GetProcAddress)
if(目标函数找到)
根据pe结构,在目标模块中定位目标函数的IAT地址(这个地址在加载时就确定了)
if (目标函数在IAT中的地址找到)
用我们的myhookfun()地址取代
esle 退出
esle 退出
当然也可以合成一个文件,但这样分开的好处是可以实现模块化,可以分别关心各自的功能,也便于以后重用。
(三)代码实例:
.486
.model flat, stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
.data
szMsgTitle db "IAT Hook", 0
szModule db "user32.dll", 0
szTargetFunc db "GetForegroundWindow", 0
szHooked db "This is in the hooked function - Seems to have worked.", 0
szFail db "Failed.", 0
.data?
IATHook PROTO STDCALL :DWORD, :DWORD, :DWORD
HookProc PROTO STDCALL :LPVOID
.code
HookProc proc Arg1:LPVOID
invoke MessageBox, NULL, addr szHooked, addr szMsgTitle, MB_OK
ret
HookProc endp
IATHook proc pDLLName:LPVOID, pOldAddr:LPVOID, pNewAddr:LPVOID
LOCAL hModule:HANDLE
LOCAL dwVirtualAddr:DWORD
LOCAL dwOrigProtect:DWORD
LOCAL dwDllFound:DWORD
LOCAL dwFunctionFound:DWORD
;Local variables
.if pDLLName == NULL
;Check for NULL pointer
xor eax, eax
ret
.endif
.if pOldAddr == NULL
;Check for NULL pointer
xor eax, eax
ret
.endif
.if pOldAddr == NULL
;Check for NULL pointer
xor eax, eax
ret
.endif
mov dwDllFound, 0
mov dwFunctionFound, 0
;Initialize
invoke GetModuleHandle, NULL
;Get the main module's base address
mov hModule, eax
;Copy it into hModule
mov edi, hModule
assume edi:ptr IMAGE_DOS_HEADER
;Make edi act as IMAGE_DOS_HEADER struct
.if edi == NULL
;Check for NULL pointer
xor eax, eax
ret
;Return 0
.endif
.if [edi].e_magic != IMAGE_DOS_SIGNATURE
;0x4D 0x5A (MZ)
xor eax, eax
ret
;Return 0
.endif
add edi, [edi].e_lfanew
;pNtHeader = (IMAGE_NT_HEADERS*)((DWORD)pDosHeader + (DWORD)pDosHeader->e_lfanew);
assume edi:ptr IMAGE_NT_HEADERS
;Make edi act as IMAGE_NT_HEADERS struct
.if edi == NULL
;Check for NULL pointer
xor eax, eax
ret
;Return 0
.endif
.if [edi].Signature != IMAGE_NT_SIGNATURE
;If it's an invalid NT header
;0x50 0x45 0x00 0x00 (PE/0/0)
xor eax, eax
ret
;Return 0
.endif
mov edx, [edi].OptionalHeader.DataDirectory[sizeof IMAGE_DATA_DIRECTORY].VirtualAddress
mov dwVirtualAddr, edx
;Copy the VirtualAddress into dwVirtualAddr
.if dwVirtualAddr == 0
;Invalid virtual address
xor eax, eax
ret
;Return 0
.endif
mov edi, hModule
add edi, dwVirtualAddr
;pImportHeader = (IMAGE_IMPORT_DESCRIPTOR*)((DWORD)pDosHeader + dwVirtualAddr);
assume edi:ptr IMAGE_IMPORT_DESCRIPTOR
;Make edi act as IMAGE_IMPORT_DESCRIPTOR struct
.if edi == NULL
;Check for NULL pointer
xor eax, eax
ret
;Return 0
.endif
.while [edi].Name1 != NULL
mov ecx, hModule
add ecx, [edi].Name1
;pModuleLabel = (char*)((DWORD)pDosHeader + (DWORD)pImportHeader->Name);
mov edx, pDLLName
invoke lstrcmpi, ecx, edx
;Check if this is the DLL we are looking for
.if eax == 0
;This is the DLL we are looking for
mov dwDllFound, 1
;Set ecx to 0, so we know later if the DLL was found
.break
.endif
add edi, sizeof IMAGE_IMPORT_DESCRIPTOR
;Next DLL
.endw
.if dwDllFound != 1
;If the DLL wasn't found
xor eax, eax
ret
;Return 0
.endif
mov edi, [edi].FirstThunk
add edi, hModule
;pThunkData = (IMAGE_THUNK_DATA*)((DWORD)pDosHeader + (DWORD)pImportHeader->FirstThunk);
assume edi:ptr IMAGE_THUNK_DATA
;Make edi act as IMAGE_THUNK_DATA struct
.while [edi].u1.Function != NULL
mov ecx, hModule
add ecx, [edi].u1.Function
mov edx, [edi].u1.Function
;Copies the current functions address (in the IAT table) into edx
.if pOldAddr == edx
;If this is the function we are going to hook
lea ebx, [edi].u1.Function
;Copy the address in the table that the function is stored in, into ebx
invoke VirtualProtect, ebx, 4, PAGE_WRITECOPY, addr dwOrigProtect
;Unprotect the memory where we are going to overwrite (We need 4 bytes --- DWORD = 4 bytes)
mov eax, pNewAddr
;Copy the address we are going to replace it with into eax
mov [ebx], eax
;Patch the address
invoke VirtualProtect, ebx, 4, addr dwOrigProtect, NULL
;Restore the original protection level
mov dwFunctionFound, 1
;Set the value, for later
.break
.endif
add edi, sizeof IMAGE_THUNK_DATA
;Next thunk
.endw
.if dwFunctionFound != 1
;If the function wasn't found
xor eax, eax
ret
;Return 0
.endif
mov eax, 1
ret
;Return 1
;Success
IATHook endp
start:
invoke GetModuleHandle, addr szModule
invoke GetProcAddress, eax, addr szTargetFunc
mov ebx, HookProc
invoke IATHook, addr szModule, eax, ebx ;Redirect GetForegroundWindow (eax) to HookProc (ebx)
.if eax == 0
invoke MessageBox, NULL, addr szFail, addr szMsgTitle, MB_OK
invoke ExitProcess, 0
.endif
;Is now hooked, hopefully.. so lets call it
invoke GetForegroundWindow
invoke ExitProcess, 0
end start
(四)局限性:1当程序运用一种叫late-demand binding技术,函数被调用时才定位地址,这样以来就不能在IAT中定位目标函数地址了.2当目标程序用动态加载(LoadLibrary)时,这种方法也将失效.
二.Dll-Injecting
(一)通过注册表注入Dll
1.一般原理:Windows的注册表中有这样一个键值,
HKEY_LOCAL_MACHINE/Software/Microsoft/Windows NT/CurrentVersion/Windows/AppInit_Dlls