文章目录
1.Address Hook
地址hook顾名思义,就是将原函数地址直接替换成hook函数地址
1.1.PE的IAT
因为IAT具体指某个PE模块的IAT,所以他的作用范围只针对被Hook的模块,且必须在以静态链接的方式调用API时才会被Hook,在使用LoadLibrary或GetProcAddress进行动态调用时不受影响。
//指向IAT中pThunk的地址
PULONG_PTR g_PointerToIATThunk = NULL;
//MessageBoxA的函数原型
typedef int(WINAPI* PFNMessageBoxA)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
PFNMessageBoxA OldMessageBox = NULL;
//自定义的MessageBoxA函数
int WINAPI NEW_MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
//干别的事
//取得原函数地址
OldMessageBox(hWnd, "消息框", "测试", 0);
//调用原函数
int ret = OldMessageBox(hWnd, lpText, lpCaption, uType);
return ret;
}
BOOL InstallModuleIATHook(
HMODULE hModToHook,// IN
const char* szModuleName,// IN
const char* szFuncName,// IN
PVOID DetourFunc,// IN
PULONG_PTR* pThunkPointer,//OUT
ULONG_PTR* pOriginalFuncAddr//OUT
)
{
PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor;
PIMAGE_THUNK_DATA pThunkData;
ULONG ulSize;
HMODULE hModule = 0;
ULONG_PTR TargetFunAddr;
PULONG_PTR lpAddr;
char* szModName;
BOOL result = FALSE;
BOOL bRetn = FALSE;
hModule = LoadLibrary(szModuleName);
TargetFunAddr = (ULONG_PTR)GetProcAddress(hModule, szFuncName);
printf("[*]Address of %s:0x%p\n", szFuncName, TargetFunAddr);
printf("[*]Module To Hook at Base:0x%p\n", hModToHook);
pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hModToHook, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);;
printf("[*]Find ImportTable,Address:0x%p\n", pImportDescriptor);
while (pImportDescriptor->FirstThunk)
{
szModName = (char*)((PBYTE)hModToHook + pImportDescriptor->Name);
printf("[*]Cur Module Name:%s\n", szModName);
if (stricmp(szModName, szModuleName) != 0){
printf("[*]Module Name does not match, search next...\n");
pImportDescriptor++;
continue;
}
//程序的导入表处理完毕后OriginalFirstThunk可能是无效的,不能再根据名称来查找,而是遍历FirstThunk直接根据地址判断
pThunkData = (PIMAGE_THUNK_DATA)((BYTE*)hModToHook + pImportDescriptor->FirstThunk);
while (pThunkData->u1.Function)
{
lpAddr = (ULONG_PTR*)pThunkData;
//找到了地址
if ((*lpAddr) == TargetFunAddr){
printf("[*]Find target address!\n");
//通常情况下导入表所在内存页都是只读的,因此需要先修改内存页的属性为可写
DWORD dwOldProtect;
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(lpAddr, &mbi, sizeof(mbi));
bRetn = VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect);
if (bRetn){
//内存页属性修改成功,继续下一步操作,先保存原始数据
if (pThunkPointer != NULL)
*pThunkPointer = lpAddr;
if (pOriginalFuncAddr != NULL)
*pOriginalFuncAddr = *lpAddr;
//修改地址
*lpAddr = (ULONG_PTR)DetourFunc;
result = TRUE;
//恢复内存页的属性
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, dwOldProtect, 0);
printf("[*]Hook ok.\n");
}
break;
}
pThunkData++;
}
pImportDescriptor++;
}
FreeLibrary(hModule);
return result;
}
BOOL IAT_InstallHook()
{
BOOL bResult = FALSE;
HMODULE hCurExe = GetModuleHandle(NULL);
PULONG_PTR pt;
ULONG_PTR OrginalAddr;
bResult = InstallModuleIATHook(hCurExe, "user32.dll", "MessageBoxA", (PVOID)NEW_MessageBoxA, &pt, &OrginalAddr);
if (bResult)
{
printf("[*]Hook安装完毕! pThunk=0x%p OriginalAddr = 0x%p\n", pt, OrginalAddr);
g_PointerToIATThunk = pt;
OldMessageBox = (PFNMessageBoxA)OrginalAddr;
}
return bResult;
}
VOID IAT_UnInstallHook()
{
DWORD dwOLD;
MEMORY_BASIC_INFORMATION mbi;
if (g_PointerToIATThunk)
{
//查询并修改内存页的属性
VirtualQuery((LPCVOID)g_PointerToIATThunk, &mbi, sizeof(mbi));
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOLD);
//将原始的MessageBoxA地址填入IAT中
*g_PointerToIATThunk = (ULONG)OldMessageBox;
//恢复内存页的属性
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, dwOLD, 0);
}
}
1.2.PE的EAT
它存放的不是函数地址,而是函数地址的偏移,使用时需要加上模块基址。EAT并不直接起作用,它只能影响Hook之后对该函数的获取或者后续加载的模块IAT——看起来就像新加载的模块被安装了IAT Hook。
#include <windows.h>
#include <stdio.h>
#include <imagehlp.h>
#pragma comment(lib,"imagehlp.lib")
typedef int
(WINAPI* PFN_MessageBox)(
HWND hWnd, // handle of owner window
LPCTSTR lpText, // address of text in message box
LPCTSTR lpCaption, // address of title of message box
UINT uType // style of message box
);
int WINAPI My_MessageBox(
HWND hWnd, // handle of owner window
LPCTSTR lpText, // address of text in message box
LPCTSTR lpCaption, // address of title of message box
UINT uType // style of message box
);
BOOL InstallModuleEATHook(
HMODULE hModToHook,// IN
const char* szFuncName,// IN
PVOID ProxyFunc,// IN
PULONG_PTR* pAddrPointer,//OUT
ULONG_PTR* pOriginalFuncAddr//OUT
);
int main(int argc, char* argv[])
{
HMODULE hUser32 = LoadLibrary("user32.dll");
PULONG_PTR pEATPointer;
ULONG_PTR uOldRVA;
InstallModuleEATHook(hUser32, "MessageBoxA", My_MessageBox, &pEATPointer, &uOldRVA);
printf("pEATPointer = 0x%X OldRVA = 0x%X\n", pEATPointer, uOldRVA);
//Test
printf("Now Test the EAT Hook.\n");
PFN_MessageBox pMsgBox = (PFN_MessageBox)GetProcAddress(GetModuleHandle("user32.dll"), "MessageBoxA");
printf("dwAddr = 0x%p\n", pMsgBox);
pMsgBox(NULL, "EAT Hook", "Test", MB_OK);
//加载一个弹MessageBox的Dll做测试
#ifdef _WIN64
LoadLibrary("../Dll/MsgDll64.dll");
#else
LoadLibrary("../Dll/MsgDll.dll");
#endif
return 0;
}
int WINAPI My_MessageBox(
HWND hWnd, // handle of owner window
LPCTSTR lpText, // address of text in message box
LPCTSTR lpCaption, // address of title of message box
UINT uType // style of message box
)
{
char newMsg[400];
char newCation[] = "标题被我改了!";
int result;
if (lpText)
{
ZeroMemory(newMsg, 400);
lstrcpy(newMsg, lpText);
lstrcat(newMsg, "\n\tMessage Box hacked by pediy.com");
}
printf("有人调用MessageBox...\n");
result = MessageBoxA(hWnd, newMsg, newCation, uType);
return result;
}
BOOL InstallModuleEATHook(
HMODULE hModToHook,// IN
const char* szFuncName,// IN
PVOID ProxyFunc,// IN
PULONG_PTR* pAddrPointer,//OUT
ULONG_PTR* pOriginalFuncAddr//OUT
)
{
PIMAGE_EXPORT_DIRECTORY pExportDir;
ULONG ulSize;
ULONG_PTR TargetFunAddr;
BOOL result = FALSE;
ULONG nFuncCnt = 0;
ULONG i = 0;
ULONG_PTR TargetFunRVA = 0;
ULONG* funrav = NULL;
TargetFunAddr = (ULONG_PTR)GetProcAddress(hModToHook, szFuncName);
TargetFunRVA = (ULONG)(TargetFunAddr - (ULONG_PTR)hModToHook);
printf("[*]Address of %s:0x%p RVA = 0x%X\n", szFuncName, TargetFunAddr, TargetFunRVA);
printf("[*]Module To Hook at Base:0x%p\n", hModToHook);
pExportDir = (PIMAGE_EXPORT_DIRECTORY)ImageDirectoryEntryToData(hModToHook, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &ulSize);
printf("[*]Find ExportTable,Address:0x%p\n", pExportDir);
nFuncCnt = pExportDir->NumberOfFunctions;
funrav = (ULONG*)((BYTE*)hModToHook + pExportDir->AddressOfFunctions);
for (i = 0; i < nFuncCnt; i++)
{
if (funrav[i] == TargetFunRVA)
{
printf("[*]Find target address!\n");
//修改内存页的属性
DWORD dwOLD;
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(&funrav[i], &mbi, sizeof(mbi));
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOLD);
//保存原始数据
if (pAddrPointer)
{
*pAddrPointer = (PULONG_PTR)&funrav[i];
}
if (pOriginalFuncAddr)
{
*pOriginalFuncAddr = funrav[i];
}
//修改地址
funrav[i] = (ULONG)((ULONG_PTR)ProxyFunc - (ULONG_PTR)hModToHook);
result = TRUE;
//恢复内存页的属性
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, dwOLD, 0);
printf("[*]Hook ok.\n");
break;
}
}
return result;
}
1.3.PE的user32.dll的回调函数表
在user32.dll中有一个名为USER32!afpnDispatch的回调函数表,其中存放了用于GUI的回调函数,通常与内核中的KeUseModeCallBack函数配合使用
1.4.IDT
系统中断描述符表。IDT的表基址存放在idtr寄存器中,表内项目存放在idtl寄存器中。每个中断项的中断处理例程称为ISR
1.5.SSDT和ShadowSSDT
调用系统服务的时候要去SSDT系统服务描述符表寻找。
与IATHook相比,SSDT Hook似乎简单一点。在查找原函数地址的时候,SSDT Hook不需要逐项比较,可根据服务索引直接获取
1.6.C++类的虚函数表
在C++类如果有虚函数成员,那么其第一个元素就是虚函数表
在编译后,虚函数表就是一个固定的表了,它位于PE的.rdata段。
(1)确定TargetFun在TargetClass虚函数表中的位置及函数原型
(2)定义DetourClass和TrampolineClass,每个类都有一个与TargetFun相同的虚函数成员。(不能直接定义一个函数是因为类成员函数的调用约定是特殊的_thiscall,它使用ecx寄存器作为类this指针)
#include "stdafx.h"
#include "CClassHook.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/*
要解决两个问题
1.如何获取类成员函数的地址
2.如何使用普通函数替换类成员函数
*/
/
// The one and only application object
CWinApp theApp;
using namespace std;
class base
{
public:
virtual int Add(int a,int b);
virtual void g(){cout<<"base::g"<<endl;};
virtual void h(){cout<<"base::h"<<endl;};
void novirtual(){cout<<"base::not virtual"<<endl;};
};
int base::Add(int a,int b)
{
printf("base::Add\n");
return a + b ;
}
class DetourClass
{
public:
virtual int DetourFun(int a,int b);
};
class TrampolineClass
{
public:
virtual int TrampolineFun(int a,int b){printf("TrampolineClass\n");return 0 ;};//原型与被Hook函数相同
};
DetourClass Detour;
TrampolineClass Trampoline;
int DetourClass::DetourFun(int a,int b)
{
//TrampolineFun(); //由于这里的类实际上是base,所以调用TrampolineFun即调用第二个虚函数,相当于调用pbase->g()
TrampolineClass *pTrampoline = new TrampolineClass;
int result = pTrampoline->TrampolineFun(a,b);
printf("DetourClass:: OriginalFun returned %d\n",result);
result += 10 ;
delete pTrampoline;
return result;
}
typedef void (*pfun)();
LPVOID GetClassVirtualFnAddress(LPVOID pthis,int Index);
VOID HookClassMemberByAnotherClassMember();
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
// initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
cerr << _T("Fatal Error: MFC initialization failed") << endl;
nRetCode = 1;
}
else
{
// TODO: code your application's behavior here.
HookClassMemberByAnotherClassMember();
getchar();
}
return nRetCode;
}
void HookClassMemberByAnotherClassMember()
{
base b;
base *pbase=&b;
DWORD dwOLD;
MEMORY_BASIC_INFORMATION mbi;
printf("pbase = 0x%X\n",pbase);
ULONG_PTR *vfTableToHook = (ULONG_PTR*)*(ULONG_PTR*)pbase;
printf("vfTable = 0x%x\n",vfTableToHook);
ULONG_PTR *vfTableTrampoline = (ULONG_PTR*)*(ULONG_PTR*)&Trampoline;
//先将原函数的地址保存到当前类的表中,作为调用原函数的入口
VirtualQuery(vfTableTrampoline,&mbi,sizeof(mbi));
VirtualProtect(mbi.BaseAddress,mbi.RegionSize,PAGE_EXECUTE_READWRITE,&dwOLD);
//保存原始数据
//原函数位于第几个这里就是第几个,必须保证位置一样
vfTableTrampoline[0] = (ULONG_PTR)GetClassVirtualFnAddress(pbase,0);
printf("Base::Add() %p\n",vfTableTrampoline[0]);
TrampolineClass *p = &Trampoline;
//恢复内存页的属性
VirtualProtect(mbi.BaseAddress,mbi.RegionSize,dwOLD,0);
//修改内存页的属性
VirtualQuery(vfTableToHook,&mbi,sizeof(mbi));
VirtualProtect(mbi.BaseAddress,mbi.RegionSize,PAGE_EXECUTE_READWRITE,&dwOLD);
//保存原始数据
vfTableToHook[0] = (ULONG_PTR)GetClassVirtualFnAddress(&Detour,0);
printf("Detour::Add() %p\n",vfTableToHook[0]);
//恢复内存页的属性
VirtualProtect(mbi.BaseAddress,mbi.RegionSize,dwOLD,0);
int result = pbase->Add(1,2); //调用第3个虚函数,实际调用的是HookClass::DetourFun()
printf("result = %d \nafter call member fun.\n",result);
}
//获得类虚拟成员函数指针
LPVOID GetClassVirtualFnAddress(LPVOID pthis,int Index)
{
ULONG_PTR *vfTable = (ULONG_PTR*)*(ULONG_PTR*)pthis;
return (LPVOID)vfTable[Index];
}
2.Inline Hook
关键是转移程序的执行流程,主要有一下5中模式
(1)jmp xxxxxxxx(5字节)
(2)push xxxxxxxx/retn(6字节)
(3)mov eax,xxxxxxxx/jmp eax(7字节)
(4)call Hook(更换指令或输入表)
(5)HotPatch Hook(短跳+长跳)
//程序功能:对user32.dll导出的MessageBoxA进行Inline Hook
//本程序中写内存不再使用繁琐的VirtualProtect函数,直接使用WriteProcessMemory
#include <windows.h>
#include <stdio.h>
#include <CONIO.H>
//定义如下结构,保存一次InlineHook所需要的信息
typedef struct _HOOK_DATA{
char szApiName[128]; //待Hook的API名字
char szModuleName[64]; //待Hook的API所属模块的名字
int HookCodeLen; //Hook长度
BYTE oldEntry[16]; //保存Hook位置的原始指令
BYTE newEntry[16]; //保存要写入Hook位置的新指令
ULONG_PTR HookPoint; //待HOOK的位置
ULONG_PTR JmpBackAddr; //回跳到原函数中的位置
ULONG_PTR pfnTrampolineFun; //调用原始函数的通道
ULONG_PTR pfnDetourFun; //HOOK过滤函数
}HOOK_DATA,*PHOOK_DATA;
#define HOOKLEN (5) //要改写的指令的长度
HOOK_DATA MsgBoxHookData;
ULONG_PTR SkipJmpAddress(ULONG_PTR uAddress);
LPVOID GetAddress(char *,char *);
void makehookentry(PVOID HookPoint);
int WINAPI My_MessageBoxA(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType);
int WINAPI OriginalMessageBox(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType);
BOOL Inline_InstallHook(void);
BOOL Inline_UnInstallHook();
BOOL InstallCodeHook(PHOOK_DATA pHookData);
BOOL UninstallCodeHook(PHOOK_DATA pHookData);
int main(int argc, char* argv[])
{
MessageBoxA(NULL,"Before Inline Hook","Test",MB_OK);
Inline_InstallHook();
MessageBoxA(NULL,"After Inline Hook","Test",MB_OK);
Inline_UnInstallHook();
MessageBoxA(NULL,"After Inline Hook Unhooked","Test",MB_OK);
return 0;
}
//************************************
// Method: FakeMessageBox
// FullName: FakeMessageBox
// Purpose: 取代原始MessageBoxA的功能,HOOK后所有对MessageBoxA的调用将实际调用本函数
// Author: achillis
// Returns: int WINAPI
// Parameter: HWND hWnd
// Parameter: LPCTSTR lpText
// Parameter: LPCTSTR lpCaption
// Parameter: UINT uType
//************************************
//注意函数的定义和原始函数一定要一样,尤其是调用约定,否则函数返回后将出错
int WINAPI My_MessageBoxA(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType)
{
//在这里,你可以对原始参数进行任意操作
int ret;
char newText[1024]={0};
char newCaption[256]="pediy.com";
printf("有人调用MessageBox!\n");
//在调用原函数之前,可以对IN(输入类)参数进行干涉
lstrcpy(newText,lpText);//为防止原函数提供的缓冲区不够,这里复制到我们自己的一个缓冲区中再进行操作
lstrcat(newText,"\n\tMessageBox Hacked by pediy.com!");//篡改消息框内容
uType|=MB_ICONERROR;//增加一个错误图标
ret = OriginalMessageBox(hWnd,newText,newCaption,uType);//调用原MessageBox,并保存返回值
//调用原函数之后,可以继续对OUT(输出类)参数进行干涉,比如网络函数的recv,可以干涉返回的内容
return ret;//这里你还可以干涉原始函数的返回值
}
BOOL Inline_InstallHook()
{
//准备Hook
ZeroMemory(&MsgBoxHookData,sizeof(HOOK_DATA));
strcpy(MsgBoxHookData.szApiName , "MessageBoxA");
strcpy(MsgBoxHookData.szModuleName , "user32.dll");
MsgBoxHookData.HookCodeLen = 5;
MsgBoxHookData.HookPoint = (ULONG_PTR)GetAddress(MsgBoxHookData.szModuleName,MsgBoxHookData.szApiName);//HOOK的地址
MsgBoxHookData.pfnTrampolineFun = (ULONG_PTR)OriginalMessageBox;//调用原始函数的通道
MsgBoxHookData.pfnDetourFun = (ULONG_PTR)My_MessageBoxA;//Fake
return InstallCodeHook(&MsgBoxHookData);
}
BOOL Inline_UnInstallHook()
{
return UninstallCodeHook(&MsgBoxHookData);
}
/*
MessageBoxA的代码开头:
77D5050B > 8BFF mov edi,edi
77D5050D 55 push ebp
77D5050E 8BEC mov ebp,esp
77D50510 833D 1C04D777 00 cmp dword ptr ds:[gfEMIEnable],0
*/
//当需要调用原始的MessageBox时,直接调用此函数即可,参数完全相同
__declspec( naked )
int WINAPI OriginalMessageBox(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType)
{
_asm
{
//由于我们写入的Jmp指令破坏了原来的前3条指令,因此在这里执行原函数的前3条指令
mov edi,edi //这一句其实可以不要
push ebp
mov ebp,esp
jmp MsgBoxHookData.JmpBackAddr //跳到Hook代码之后的地方,绕过自己安装的HOOK
}
}
//获取指定模块中指定API的地址
LPVOID GetAddress(char *dllname,char *funname)
{
HMODULE hMod=0;
if (hMod=GetModuleHandle(dllname))
{
return GetProcAddress(hMod,funname);
}
else
{
hMod=LoadLibrary(dllname);
return GetProcAddress(hMod,funname);
}
}
/*
这里计算一下要填充的指令
使用的是5字节的Jmp
*/
void InitHookEntry(PHOOK_DATA pHookData)
{
if (pHookData==NULL
|| pHookData->pfnDetourFun==NULL
|| pHookData->HookPoint==NULL)
{
return;
}
pHookData->newEntry[0] = 0xE9; //Jmp
//计算跳转偏移并写入
*(ULONG*)(pHookData->newEntry+1) = (ULONG)pHookData->pfnDetourFun - (ULONG)pHookData->HookPoint - 5;//0xE9 式jmp的计算
}
ULONG_PTR SkipJmpAddress(ULONG_PTR uAddress)
{
ULONG_PTR TrueAddress = 0 ;
PBYTE pFn = (PBYTE)uAddress;
if (memcmp(pFn,"\xFF\x25",2) == 0)
{
TrueAddress = *(ULONG_PTR*)(pFn + 2);
return TrueAddress;
}
if (pFn[0] == 0xE9)
{
TrueAddress = (ULONG_PTR)pFn + *(ULONG_PTR*)(pFn + 1) + 5 ;
return TrueAddress;
}
if (pFn[0] == 0xEB)
{
TrueAddress = (ULONG_PTR)pFn + pFn[1] + 2 ;
return TrueAddress;
}
return (ULONG_PTR)uAddress;
}
BOOL InstallCodeHook(PHOOK_DATA pHookData)
{
DWORD dwBytesReturned=0;
HANDLE hProcess=GetCurrentProcess();
BOOL bResult=FALSE;
if (pHookData==NULL
|| pHookData->HookPoint==0
|| pHookData->pfnDetourFun == NULL
|| pHookData->pfnTrampolineFun ==NULL)
{
return FALSE;
}
pHookData->pfnTrampolineFun = SkipJmpAddress(pHookData->pfnTrampolineFun);
pHookData->HookPoint = SkipJmpAddress(pHookData->HookPoint); //如果函数开头是跳转,那么将其跳过
pHookData->JmpBackAddr = pHookData->HookPoint + pHookData->HookCodeLen ;
LPVOID OriginalAddr = (LPVOID)pHookData->HookPoint ;
printf("Address To HOOK=0x%08X\n",OriginalAddr);
InitHookEntry(pHookData);//填充Inline Hook代码
if(ReadProcessMemory(hProcess,OriginalAddr,pHookData->oldEntry,pHookData->HookCodeLen,&dwBytesReturned))
{
if (WriteProcessMemory(hProcess,OriginalAddr,pHookData->newEntry,pHookData->HookCodeLen,&dwBytesReturned))
{
printf("Install Hook write oK! WrittenCnt=%d\n",dwBytesReturned);
bResult=TRUE;
}
}
return bResult;
}
BOOL UninstallCodeHook(PHOOK_DATA HookData)
{
DWORD dwBytesReturned=0;
HANDLE hProcess=GetCurrentProcess();
BOOL bResult=FALSE;
LPVOID OriginalAddr;
if (HookData==NULL
|| HookData->HookPoint==0
|| HookData->oldEntry[0]==0)
{
return FALSE;
}
OriginalAddr=(LPVOID)HookData->HookPoint;
bResult = WriteProcessMemory(hProcess,OriginalAddr,HookData->oldEntry,HookData->HookCodeLen,&dwBytesReturned) ;
return bResult;
}
3.基于异常处理的HOOK实施过程
安装一个VectoredHandler过程,在要Hook的函数那里设置INT3断点,当程序执行到断点时产生异常,当该异常被VectoredHandler捕获后,对执行过程进行干预。
VectoredHandler函数原型
LONG CALLBACK VectoredHandler([in] PEXCEPTION_POINTERS ExceptionInfo);
/*-----------------------------------------------------------------------
第13章 Hook技术
《加密与解密(第四版)》
(c) 看雪学院 www.kanxue.com 2000-2018
-----------------------------------------------------------------------*/
// VEHHook.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
BOOL SetBreakPoint(PVOID pFuncAddr);
BOOL ClearBreakPoint(PVOID pFuncAddr);
BOOL InstallVEHHook(PVECTORED_EXCEPTION_HANDLER Handler);
VOID UnInstallVEHHook();
typedef int
(WINAPI *PFN_MessageBox)(
HWND hWnd, // handle of owner window
LPCTSTR lpText, // address of text in message box
LPCTSTR lpCaption, // address of title of message box
UINT uType // style of message box
);
int WINAPI My_MessageBox(
HWND hWnd, // handle of owner window
LPCTSTR lpText, // address of text in message box
LPCTSTR lpCaption, // address of title of message box
UINT uType // style of message box
);
LONG WINAPI VectoredHandler1(struct _EXCEPTION_POINTERS *ExceptionInfo);
LONG WINAPI VectoredHandler2(struct _EXCEPTION_POINTERS *ExceptionInfo);
LONG WINAPI VectoredHandler3(struct _EXCEPTION_POINTERS *ExceptionInfo);
VOID ShowMsgBox(LPCTSTR lpMsg);
ULONG_PTR InitTrampolineFun();
PFN_MessageBox g_OriginalMessageBoxA;
PVOID g_AddrofMessageBoxA = 0 ;
PVOID g_hVector;
BYTE g_OldCode[16]={0};
int main(int argc, char* argv[])
{
HMODULE hUser32 = LoadLibrary("user32.dll");
g_AddrofMessageBoxA = (PVOID)GetProcAddress(hUser32,"MessageBoxA");
printf("Address of MessageBoxA = 0x%p\n",g_AddrofMessageBoxA);
g_OriginalMessageBoxA = (PFN_MessageBox)InitTrampolineFun(); //跳过开头的Hook
printf("Addr of VectoredHandler1 = 0x%p\n",VectoredHandler1);
printf("Addr of VectoredHandler2 = 0x%p\n",VectoredHandler2);
printf("Addr of VectoredHandler3 = 0x%p\n",VectoredHandler3);
//选择安装一个进行测试
InstallVEHHook(VectoredHandler3);
//设置断点
SetBreakPoint(g_AddrofMessageBoxA);
//call
ShowMsgBox("VEH Hook Test.");
printf("All Finished!\n");
ClearBreakPoint(g_AddrofMessageBoxA);
UnInstallVEHHook();
ShowMsgBox("Hook Cleared");
return 0;
}
VOID ShowMsgBox(LPCTSTR lpMsg)
{
MessageBoxA(NULL,lpMsg,"Test",MB_OK);
}
ULONG_PTR InitTrampolineFun()
{
ULONG_PTR uResult = 0 ;
PBYTE pFun = NULL;
#ifdef _WIN64
//x64需要申请shellcode
/*
USER32!MessageBoxA:
00000000`779412b8 4883ec38 sub rsp,38h
00000000`779412bc 4533db xor r11d,r11d
00000000`779412bf 44391d760e0200 cmp dword ptr [USER32!gapfnScSendMessage+0x927c (00000000`7796213c)],r11d
*/
pFun = (PBYTE)VirtualAlloc(NULL,128,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
uResult = (ULONG_PTR)pFun;
memset(pFun,0,128);
memcpy(pFun,(PVOID)g_AddrofMessageBoxA,4); //拷贝第一条指令,4字节,推荐使用反汇编引擎来实际计算
pFun += 4 ; //下一条指令构造为jmp [xxxxxx]
pFun[0] = 0xFF;
pFun[1] = 0x25;
*(ULONG_PTR*)(pFun + 6) = (ULONG_PTR)g_AddrofMessageBoxA + 4 ; //跳回到原函数加4的地方
#else
//x86,第一条指令是mov edi,edi,直接跳过即可
uResult = (ULONG_PTR)g_AddrofMessageBoxA + 2;
#endif
return uResult;
}
//处理方式,修改参数并返回原函数继续执行
LONG WINAPI
VectoredHandler1(
struct _EXCEPTION_POINTERS *ExceptionInfo
)
{
char *szNewMsg = "[VectoredHandler1] Hacked by pediy.com";
LONG lResult = EXCEPTION_CONTINUE_SEARCH ;
PEXCEPTION_RECORD pExceptionRecord;
PCONTEXT pContextRecord;
int ret = 0 ;
pExceptionRecord = ExceptionInfo->ExceptionRecord ;
pContextRecord = ExceptionInfo->ContextRecord ;
ULONG_PTR* uESP = 0 ;
printf("Exception Address = %p\n",pExceptionRecord->ExceptionAddress);
if (pExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT
&& pExceptionRecord->ExceptionAddress == g_AddrofMessageBoxA)
{
#ifdef _WIN64
//x64上前四个参数依次为RCX,RDX,R8,R9
//修改第二个参数,即LpMsg
printf("lpText = 0x%p %s\n",pContextRecord->Rdx,(char*)pContextRecord->Rdx);
pContextRecord->Rdx = (ULONG_PTR)szNewMsg;
pContextRecord->Rip = (ULONG_PTR)g_OriginalMessageBoxA ; //跳到Trampoline继续执行
#else
/*
0012FF70 0040105A /CALL 到 MessageBoxA 来自 VEHHook.00401054
0012FF74 00000000 |hOwner = NULL
0012FF78 00407030 |Text = "VEH Hook"
0012FF7C 0040703C |Title = "Test"
0012FF80 00000000 \Style = MB_OK|MB_APPLMODAL
0012FF84 00401225 返回到 VEHHook.<ModuleEntryPoint>+0B4 来自 VEHHook.00401000
*/
printf("ESP = 0x%p\n",pContextRecord->Esp) ;
uESP = (ULONG_PTR*)pContextRecord->Esp ; //取中断时的ESP
uESP[2] = (ULONG_PTR)szNewMsg; //修改栈中的参数
pContextRecord->Eip = (ULONG_PTR)g_OriginalMessageBoxA ; //跳过函数开头
#endif
lResult = EXCEPTION_CONTINUE_EXECUTION ;
}
return lResult;
}
//处理方式:直接调用原函数并替原函数返回
LONG WINAPI
VectoredHandler2(
struct _EXCEPTION_POINTERS *ExceptionInfo
)
{
char *szNewMsg = "[VectoredHandler2] Hacked by pediy.com";
LONG lResult = EXCEPTION_CONTINUE_SEARCH ;
PEXCEPTION_RECORD pExceptionRecord;
PCONTEXT pContextRecord;
int ret = 0 ;
pExceptionRecord = ExceptionInfo->ExceptionRecord ;
pContextRecord = ExceptionInfo->ContextRecord ;
ULONG_PTR* uESP = 0 ;
if (pExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT
&& pExceptionRecord->ExceptionAddress == g_AddrofMessageBoxA)
{
#ifdef _WIN64
//x64上前四个参数依次为RCX,RDX,R8,R9
printf("RSP = 0x%p\n",pContextRecord->Rsp) ;
uESP = (ULONG_PTR*)pContextRecord->Rsp ;
printf("Return Address = 0x%p\n",uESP[0]);
ret = g_OriginalMessageBoxA((HWND)pContextRecord->Rcx,szNewMsg,(LPCTSTR)pContextRecord->R8,(int)pContextRecord->R9);
printf("ret = %d\n",ret);
//修正RSP
pContextRecord->Rsp += sizeof(ULONG_PTR);//参数在寄存器中,栈中无参数,仅需跳过返回地址
//直接返回到调用者处
pContextRecord->Rip = uESP[0] ;//设置EIP为返回地址
#else
/*
0012FF70 0040105A /CALL 到 MessageBoxA 来自 VEHHook.00401054
0012FF74 00000000 |hOwner = NULL
0012FF78 00407030 |Text = "VEH Hook"
0012FF7C 0040703C |Title = "Test"
0012FF80 00000000 \Style = MB_OK|MB_APPLMODAL
0012FF84 00401225 返回到 VEHHook.<ModuleEntryPoint>+0B4 来自 VEHHook.00401000
*/
printf("ESP = 0x%p\n",pContextRecord->Esp) ;
uESP = (ULONG_PTR*)pContextRecord->Esp ;
ret = g_OriginalMessageBoxA((HWND)uESP[1],szNewMsg,(LPCTSTR)uESP[3],(int)uESP[4]);
printf("ret = %d\n",ret);
//直接返回到调用者处
pContextRecord->Eip = uESP[0] ;//设置EIP为返回地址
pContextRecord->Esp += (4 + 1)*sizeof(ULONG_PTR); //4为参数个数,1为返回地址
#endif
lResult = EXCEPTION_CONTINUE_EXECUTION ;
}
return lResult;
}
//处理方式:直接返回,相当于过滤掉
LONG WINAPI
VectoredHandler3(
struct _EXCEPTION_POINTERS *ExceptionInfo
)
{
LONG lResult = EXCEPTION_CONTINUE_SEARCH ;
PEXCEPTION_RECORD pExceptionRecord = ExceptionInfo->ExceptionRecord ;
PCONTEXT pContextRecord = ExceptionInfo->ContextRecord ;
ULONG_PTR* uESP = 0 ;
if (pExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT
&& pExceptionRecord->ExceptionAddress == g_AddrofMessageBoxA)
{
/*
0012FF70 0040105A /CALL 到 MessageBoxA 来自 VEHHook.00401054
0012FF74 00000000 |hOwner = NULL
0012FF78 00407030 |Text = "VEH Hook"
0012FF7C 0040703C |Title = "Test"
0012FF80 00000000 \Style = MB_OK|MB_APPLMODAL
0012FF84 00401225 返回到 VEHHook.<ModuleEntryPoint>+0B4 来自 VEHHook.00401000
*/
//直接返回到调用者处
#ifdef _WIN64
printf("RSP = 0x%p\n",pContextRecord->Rsp) ;
uESP = (ULONG_PTR*)pContextRecord->Rsp ;
pContextRecord->Rip = uESP[0] ;//设置EIP为返回地址
pContextRecord->Rsp += sizeof(ULONG_PTR); //将压入栈内的参数和返回地址清理掉,4为参数个数,1为返回地址
#else
printf("ESP = 0x%X\n",pContextRecord->Esp) ;
uESP = (ULONG_PTR*)pContextRecord->Esp ;
pContextRecord->Eip = uESP[0] ;//设置EIP为返回地址
pContextRecord->Esp += (4 + 1)*sizeof(ULONG_PTR); //将压入栈内的参数和返回地址清理掉,4为参数个数,1为返回地址
#endif
lResult = EXCEPTION_CONTINUE_EXECUTION ;
}
return lResult;
}
BOOL InstallVEHHook(PVECTORED_EXCEPTION_HANDLER Handler)
{
printf("Current Handler Address = 0x%p\n",Handler);
g_hVector = AddVectoredExceptionHandler(1,Handler);
return g_hVector != NULL ;
}
VOID UnInstallVEHHook()
{
RemoveVectoredExceptionHandler(g_hVector);
}
/*
0:000> u user32!messageboxA
USER32!MessageBoxA:
77d507ea 8bff mov edi,edi
77d507ec 55 push ebp
77d507ed 8bec mov ebp,esp
*/
BOOL SetBreakPoint(PVOID pFuncAddr)
{
DWORD dwCnt = 0 ;
BYTE *pTarget = (BYTE*)pFuncAddr;
g_OldCode[0] = *pTarget;
printf("Original Fun Head Code = 0x%02X\n",g_OldCode[0]);
//修改内存页的属性
DWORD dwOLD;
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(pTarget,&mbi,sizeof(mbi));
VirtualProtect(mbi.BaseAddress,mbi.RegionSize,PAGE_EXECUTE_READWRITE,&dwOLD);
//写入int3
*pTarget = 0xCC ;
//恢复内存页的属性
VirtualProtect(mbi.BaseAddress,mbi.RegionSize,dwOLD,0);
return TRUE;
}
BOOL ClearBreakPoint(PVOID pFuncAddr)
{
BYTE *pTarget = (BYTE*)pFuncAddr;
//修改内存页的属性
DWORD dwOLD;
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(pTarget,&mbi,sizeof(mbi));
VirtualProtect(mbi.BaseAddress,mbi.RegionSize,PAGE_EXECUTE_READWRITE,&dwOLD);
*pTarget = g_OldCode[0] ;
//恢复内存页的属性
VirtualProtect(mbi.BaseAddress,mbi.RegionSize,dwOLD,0);
return TRUE;
}
int WINAPI My_MessageBox(
HWND hWnd, // handle of owner window
LPCTSTR lpText, // address of text in message box
LPCTSTR lpCaption, // address of title of message box
UINT uType // style of message box
)
{
char newMsg[400];
char newCation[]="标题被我改了!";
int result;
if (lpText)
{
ZeroMemory(newMsg,400);
lstrcpy(newMsg,lpText);
lstrcat(newMsg,"\n\tMessage Box hacked by pediy.com");
}
printf("有人调用MessageBox...\n");
result = g_OriginalMessageBoxA(hWnd,newMsg,newCation,uType);
return result;
}
4.Hook位置
在应用层上,对API进行IAT Hook和Inline Hook足以满足这个要求。在内核中,KiFastCallEntry和KeServiceDescriptorTable(含shadow)是两个绝佳的Hook位置。
5.钩子
钩子分为局部钩子和全局钩子。局部钩子是针对单一线程,而全局钩子针对当前桌面的所有线程。
钩子是基于消息机制的,只有触发相应的消息,钩子函数过程才有机会执行,系统才会加载钩子模块。
钩子实际上是一个处理消息的程序段,通过系统机制使其挂入系统。当产生特定消息时,在未到达目的窗口之前,钩子程序就先捕获该消息,取得控制权。
对于每种类型的钩子,系统都维护一个钩链。每次安装钩子时,都会向对应类型的链表添加一个结点项,用于记录钩子的函数过程、引用计数等信息。最近安装的钩子放在链表的开始位置,而最先安装的钩子放在最后。
安装钩子使用SetWindowsHookEx,函数原型定义如下:
HHOOK SetWindowHookEx(
int idHook, //钩子类型
HOOKPROC lpfn, //钩子函数过程
HINSTANCE hMod, //钩子模块实例句柄(即lpfn指定函数过程所在模块句柄)
DWORD dwThreadId //需要挂钩的线程ID
);
BOOL UnHookWindowsHookEx(
HHOOK hhk //钩子句柄
);
在应用程序中安装钩子,接收到对应消息后,可以在钩子的消息处理过程对消息进行处理,但通常需要使用CallNextHookEx函数来继续传递消息
LRESULT CallNextHookEx(
HHOOK hhk, //钩子句柄
int nCode, //钩子标识
WPARAM wParam, //根据具体钩子类型决定
LPARAM lParam, //根据具体钩子类型决定
);