工控系统安全之HOOK技术(上课教程)
分别采用IAT Hook和inline Hook技术对系统函数Hook
1. 利用IAT Hook来挂钩user32.dll中的GetTopWindow函数,实现调用GetTopWindow会弹出提示框
2. 利用Inline Hook技术实现对user32.dll中的MessageBoxA的Hook,实现弹框前,先Beep一声
提示:
考虑到inline Hook实现对新手较难,可以先使用mhook库或Detours库来实现
#include "windows.h"
typedef
struct _IATHOOK_DATA
{
DWORD dwValueAddr; //IAT项中保存函数地址的地址
DWORD dwOriginValue; //原始数据
DWORD dwNewValue; //新的数据
}IATHOOK_DATA, *PIATHOOK_DATA;
DWORD IATHook(CHAR szDllName[], CHAR szFuncName[], PIATHOOK_DATA pIATHookData);
DWORD IATUnHook(PIATHOOK_DATA pIATHookData);
HWND WINAPI MyGetTopWindow(HWND hWnd);
IATHOOK_DATA IATHookData;
int WINAPI WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in_opt LPSTR lpCmdLine, __in int nShowCmd )
{
IATHookData.dwNewValue = (DWORD)MyGetTopWindow;
IATHook("user32.dll", "GetTopWindow", &IATHookData);
GetTopWindow(NULL);
return 0;
}
typedef
HWND (WINAPI *GETTOPWINDOW)(HWND hWnd);
HWND WINAPI MyGetTopWindow(HWND hWnd)
{
GETTOPWINDOW OriginGetTopWindow = (GETTOPWINDOW)IATHookData.dwOriginValue;
Beep(3000, 100);
MessageBoxA(NULL, "GetTopWindow哦", "3170604037", MB_OK);
return OriginGetTopWindow(hWnd);
}
/************************************************************************/
/*
功能:利用修改导入函数表中地址来hook函数
参数:szDllName-导入函数的dll名,szFuncName-函数名,pIATHookData-hook使用的数据
原理:通过判断IMAGE_THUNK_DATA中函数名来得到对应IAT中地址,修改导入函数表中函数地址
*/
/************************************************************************/
DWORD IATHook(CHAR szDllName[], CHAR szFuncName[], PIATHOOK_DATA pIATHookData)
{
DWORD dwBaseAddr;
PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS pNtHeader;
PIMAGE_OPTIONAL_HEADER pOptHeaer;
PIMAGE_THUNK_DATA pThunk, pIAT;
PIMAGE_IMPORT_DESCRIPTOR pImportDes;
DWORD dwIndex;
DWORD dwOriginProtect;
if (NULL == szDllName || NULL == szFuncName || NULL == pIATHookData)
{
return ERROR_INVALID_PARAMETER;
}
//获取当前文件的基址
dwBaseAddr = (DWORD)GetModuleHandle(NULL);
if (NULL == dwBaseAddr)
{
return GetLastError();
}
//从头计算得到引入表的地址
pDosHeader = (PIMAGE_DOS_HEADER)dwBaseAddr;
pNtHeader = (PIMAGE_NT_HEADERS)(dwBaseAddr + pDosHeader->e_lfanew);
pOptHeaer = &(pNtHeader->OptionalHeader);
pImportDes = (PIMAGE_IMPORT_DESCRIPTOR)(dwBaseAddr + pOptHeaer->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
while (pImportDes->FirstThunk)
{
//判断是否是对应模块
if (_stricmp((CHAR*)(dwBaseAddr+pImportDes->Name), szDllName))
{
pImportDes ++;
continue;
}
//当PE文件被装载到内存时,PE装载器将查找IMAGE_THUNK_DATA 和 IMAGE_IMPORT_BY_NAME 这些结构数组,
//以此决定引入函数的地址。然后用引入函数真实地址来替代由FirstThunk指向的 IMAGE_THUNK_DATA 数组里的元素值
//若 OriginalFirstThunk 为0,就改用FirstThunk值。有些连接器生成PE文件时会置OriginalFirstThunk值为0,这应该算是个bug。
pIAT = (PIMAGE_THUNK_DATA)(dwBaseAddr + pImportDes->FirstThunk);
if (pImportDes->OriginalFirstThunk)
{
pThunk = (PIMAGE_THUNK_DATA)(dwBaseAddr + pImportDes->OriginalFirstThunk);
}
else
{
pThunk = pIAT;
}
dwIndex = 0;
do
{
//有些情况下一些函数仅由序数引出,对应该函数的 IMAGE_THUNK_DATA 值
//的低位字指示函数序数,而最高二进位 (MSB)设为1。
//32位时IMAGE_ORDINAL_FLAG = 0x80000000h。
dwIndex = *(DWORD*)pThunk;
if (dwIndex != 0)
{
if ((dwIndex & IMAGE_ORDINAL_FLAG) == 0)
{
//得到函数名,继续向后跳2字节
if (strcmp((PCHAR)(dwBaseAddr + dwIndex + 2), szFuncName) == 0)
{
//找到了,先保存原始数据
pIATHookData->dwValueAddr = (DWORD)pIAT;
pIATHookData->dwOriginValue = *(DWORD*)pIAT;
//修改IAT
VirtualProtect(pIAT, 4, PAGE_EXECUTE_READWRITE, &dwOriginProtect);
*(DWORD*)pIAT = pIATHookData->dwNewValue;
VirtualProtect(pIAT, 4, dwOriginProtect, &dwOriginProtect);
return 0;
}
}
}
//换下一个
pThunk ++;
pIAT ++;
} while (1);
//取下一模块
pImportDes ++;
}
return 0;
}
/************************************************************************/
/*
功能:修股
参数:
原理:
*/
/************************************************************************/
DWORD IATUnHook(PIATHOOK_DATA pIATHookData)
{
DWORD dwOriginProtect;
VirtualProtect((PVOID)pIATHookData->dwValueAddr, 4, PAGE_EXECUTE_READWRITE, &dwOriginProtect);
*(DWORD*)pIATHookData->dwValueAddr = pIATHookData->dwOriginValue;
VirtualProtect((PVOID)pIATHookData->dwValueAddr, 4, dwOriginProtect, &dwOriginProtect);
return 0;
}
实现结果
会听到电脑哔一声然后弹出窗口