都是很成熟的東西了,這幾天看了看,總結一下而已。 (refer to John Robbins, BugsLayerUtil.dll) 原理和 當一個
討論了Windows下hook函數的幾種方法。提供了一個hook TextOutA的完整例子。通過CreateRemoteThread的方法把hook dll注入到一個普通的應用程序中。Hooking Imported Functions by name調用 imported functions時的步驟/實現
個字節為
在程序中調用從其它模塊引入的函數的方法和普通的函數調用有所不同。對於普通的函數調用,直接使用
call address來調用即可,但是對於
imported functions
,在編譯的時候
compiler/link
並不知道實際的函數實現會被加載到那個地址,函數實現在那個地址在運行的時候才會確定。對於
imported functions
,首先是
call
引入表中的一個函數,在運行時再初始化引入表,使用
jmp
跳轉到真實的函數實現。
引入表:
The PE file IMAGE_IMPORT_DESCRIPTOR structure, which holds all the information about functions imported from a specific DLL, has pointers to two arrays in the executable. These arrays are called import address tables (IATs), or sometimes thunk data arrays. The first pointer references the real IAT, which the program loader fixes up when the executable is loaded. The second pointer references the original IAT, which is untouched by the loader and lists the imported functions.
實現原理
PE
文件的
Image_Import_Descriptor
結構
Original LAT
和
Real LAT.
hook
的函數的名字在
Original LAT
找到要
hook
的
imported function
在數組中的
index.
Real LAT
在相應
index
的
function address Hooking Imported Functions by ordinal
Hook Imported functions by name
一樣,只是是通過要
hook
的函數的
ordinal
在
original LAT
中找到
index. Hooking a function in this dll
DLL
是通過
LoadLibrary
載入的時候,我們無法通過
hook imported function
的方法的
hook
它中的
function
。有兩種可能的辦法處理這種情況:
第一種方法,遍歷進程空間,發現
call
指定函數的地方替換為
call hookFunction.
太麻煩,而且不安全。
第二種方法,改寫要
hook
的函數
FuncA
。比較好的方法
HookFuncA
,最後的實現墊入
n
個
nop.
hook
的函數
FuncA
的絕對地址,改寫前
5
jmp hookFuncA(
假定前
5
個字節為
n
個完整的指令
)
把
FuncA
的前
5
個字節拷貝到
hookFuncA
的後面,在加上一條指令 jmp funcA+5.
----Code of HookDLL.dll, 可以通過CreateRemoteThread的方法把hook dll注入到一個普通的應用程序中。
// HookDLL.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
#include "HookDLL.h"
#include "Log.h"
//forward declare.
LRESULT WINAPI InstallTextoutHook();
LRESULT WINAPI UninstallTextoutHook();
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
if (InstallTextoutHook())
{
WriteLog("Install hook success.
");
}else
{
WriteLog("Intall hook failed.
");
}
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
if (UninstallTextoutHook())
{
WriteLog("Uninstall hook success.
");
}else
{
WriteLog("Unintall hook failed.
");
}
break;
}
return TRUE;
}
#define DWORD_PTR DWORD*
#define __LOCAL_SIZE 40h
#define NAKED_PROLOG()
DWORD_PTR dwRet ;
DWORD_PTR dwESI ;
{
__asm PUSH EBP /* Set up the standard frame.*/
__asm MOV EBP , ESP
__asm SUB ESP , __LOCAL_SIZE /* Save room for the local */
/* variables. */
__asm MOV EAX , EBP /* EBP has the stack coming */
/* into the fn. in it. */
__asm ADD EAX , 4 /* Account for PUSH EBP */
__asm MOV EAX , [EAX] /* Get return address. */
__asm MOV [dwRet] , EAX /* Save return address. */
__asm MOV [dwESI] , ESI /* Save ESI so chkesp in dbg */
/* builds works. */
}// The common epilog part that can be shared between the stdcall and
// cdecl hook functions.
#define EPILOG_COMMON()
{
__asm MOV ESI , [dwESI] /* Restore ESI. */
__asm ADD ESP , __LOCAL_SIZE /* Take away local var space */
__asm MOV ESP, EBP /* Restore standard frame. */
__asm POP EBP
}
#define COPY_CODE_LENGTH 5
BYTE g_abOriCode[COPY_CODE_LENGTH];
BYTE g_abJmpCode[COPY_CODE_LENGTH];
PROC g_oriTextout;
BOOL g_blHooked = FALSE;
LRESULT WINAPI InstallTextoutHook()
{
if (g_blHooked)
return TRUE;
//Get TextOutAs address.
HMODULE hGdi32 = ::LoadLibrary(_T("Gdi32.dll"));
g_oriTextout = GetProcAddress(hGdi32, _T("TextOutA"));
if (NULL == g_oriTextout)
return FALSE;
//Get the hooka address.
HMODULE hModule = GetModuleHandle(_T("HookDLL.dll"));
if (NULL == hModule)
return FALSE;
DWORD dwHookAddr = NULL;
__asm
{
mov esi, offset HookLabel;
mov edi, 0x10000000;//0x10000000 is the dlls base address.
sub esi, edi;
add esi, hModule;
mov [dwHookAddr], esi;
}
//Get the NOPs address.
DWORD dwNOPAddr = NULL;
__asm
{
mov esi, offset NOPLabel;
mov edi, 0x10000000;//0x10000000 is the dlls base address.
sub esi, edi;
add esi, hModule;
mov [dwNOPAddr], esi;
}
//Save the first 5 byte of TextOutA to g_abOriCode
__asm
{
mov esi, g_oriTextout;
lea edi, g_abOriCode;
cld;
movsd;
movsb;
}
//Generate the jmp Hook function.
g_abJmpCode[0] = 0xe9;
__asm
{
mov eax, dwHookAddr;
mov ebx, g_oriTextout;
add ebx, 5;
sub eax, ebx;
mov dword ptr[g_abJmpCode+1], eax;
}
//Write the jump instruction to the textoutA.
DWORD dwProcessId = GetCurrentProcessId();
HANDLE hProcess = OpenProcess (PROCESS_ALL_ACCESS,
FALSE, dwProcessId);
if (NULL == hProcess)
return FALSE;
DWORD dwOldFlag;
VirtualProtectEx(hProcess, g_oriTextout, 5, PAGE_READWRITE, &dwOldFlag);
WriteProcessMemory(hProcess, g_oriTextout, g_abJmpCode, sizeof(g_abJmpCode), NULL