IDA hook的编写
1. 实现hook的原理
在pe文件中有导入表。导入表中用函数名字和地址类似是key-value的形式,我们把这个地址改变做到hook的效果。以下所有代码是32位的只使用了c语言
之前分析过导入表,本文不在分析只实现hook
pe结构解析
实现所用到的Windows api
//用于获取当前模块的基地址 GetModuleHandleA(NULL)
HMODULE
WINAPI
GetModuleHandleA(
_In_opt_ LPCSTR lpModuleName
);
//用于修改内存属性,(可读、可写、可执行)
BOOL
WINAPI
VirtualProtectEx(
_In_ HANDLE hProcess,
_In_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flNewProtect,
_Out_ PDWORD lpflOldProtect
);
2. 加载当前模块的导入表的方法
char *image_base = GetModuleHandleA(NULL);
//dos头
IMAGE_DOS_HEADER *dos_head = (IMAGE_DOS_HEADER *)image_base;
//pe头
IMAGE_NT_HEADERS32 *pe_head = (IMAGE_NT_HEADERS32 *)((char *)image_base + dos_head->e_lfanew);
//导入表
IAT->import = (IMAGE_IMPORT_DESCRIPTOR *)((char *)image_base + pe_head->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
3.设计hook的结构
typedef struct IAT_string //string
{
char *str;
int len;
}IAT_string;
//这个结构的数据不会改变
typedef struct IAT_function_data //数组的数据
{
IAT_string function_name; //函数名字
DWORD function_addr; //函数地址所在内存的位置 image_base的偏移 FirstThunk
DWORD function_addr_data; //函数原来的地址 FirstThunk[i]
}IAT_function_data;
typedef struct IAT_function_data_arr //数组
{
struct IAT_function_data *arr;
int size; //当前的内存大小
int len; //当前数组的元素
}IAT_function_data_arr;
typedef struct IAT_struct
{
HMODULE image_base; //模块的基地址
IMAGE_IMPORT_DESCRIPTOR *import; //导入表
IAT_function_data_arr function; //当前模块的所有导入函数
IAT_function_data_arr hook_function; //已经hook的函数
}IAT_struct;
看完可能会不理解,我们一步一步来解析
4.数组的实现
有关于数组的api
//初始化IAT_function_data_arr结构
int init_function_data_arr(IAT_function_data_arr *arr);
//释放
int uninit_function_data_arr(IAT_function_data_arr *arr);
//添加一个元素
int add_function_data_arr(IAT_function_data_arr *arr, IAT_function_data *data);
//修改一个元素
int updata_function_data_arr(IAT_function_data_arr *arr, IAT_function_data *data, IAT_function_data *updata);
//删除一个元素
int del_function_data_arr(IAT_function_data_arr *arr, IAT_function_data *data);
4.1 初始化函数
int init_function_data_arr(IAT_function_data_arr *arr)
{
if (arr == NULL)
{
return -1;
}
arr->len = 0;
arr->size = 10;
arr->arr = (IAT_function_data *)malloc(sizeof(IAT_function_data) * 10);
if (arr->arr == NULL)
{
return -2;
}
return 0;
}
正常的初始化IAT_function_data_arr每一个元素,预分配10个元素的大小
4.2 释放函数
int uninit_function_data_arr(IAT_function_data_arr *arr)
{
if (arr == NULL)
{
return -1;
}
arr->len = 0;
arr->size = 0;
free(arr->arr);
return 0;
}
简单的清零,释放内存
4.3 添加一个元素
int add_function_data_arr(IAT_function_data_arr *arr, IAT_function_data *data)
{
if (arr == NULL || data == NULL)
{
return -1;
}
//元素个数没有超过预分配的大小
if (arr->size > arr->len)
{
memcpy(arr->arr + arr->len, data, sizeof(IAT_function_data));
arr->len++;
}
else
{
//内存不够扩大内存
arr->size *= 2;
arr->arr = (IAT_function_data *)realloc(arr->arr, sizeof(IAT_function_data)* arr->size);
memcpy(arr->arr + arr->len, data, sizeof(IAT_function_data));
arr->len++;
}
return 0;
}
4.4 修改一个元素
int updata_function_data_arr(IAT_function_data_arr *arr, IAT_function_data *data, IAT_function_data *updata)
{
if (arr == NULL || data == NULL)
{
return -1;
}
//遍历查找
for (int i = 0; i < arr->len; i++)
{
if (memcmp(arr->arr + i, data, sizeof(IAT_function_data)) == 0)
{
//修改
memcpy(arr->arr + i, updata, sizeof(IAT_function_data));
break;
}
}
return 0;
}
4.5 删除一个元素
int del_function_data_arr(IAT_function_data_arr *arr, IAT_function_data *data)
{
if (arr == NULL || data == NULL)
{
return -1;
}
//遍历查找
for (int i = 0; i < arr->len; i++)
{
if (memcmp(arr->arr + i, data, sizeof(IAT_function_data)) == 0)
{
//删除
arr->len--;
memcpy(arr->arr + i, arr->arr + i + 1, (arr->len - i) * sizeof(IAT_function_data));
break;
}
}
return 0;
}
//按照下标删除
int del_function_data_arr_num(IAT_function_data_arr *arr, int i)
{
if (arr == NULL || i < 0)
{
return -1;
}
if (arr->len > 0)
{
arr->len--;
memcpy(arr->arr + i, arr->arr + i + 1, (arr->len - i) * sizeof(IAT_function_data));
}
return 0;
}
删除思路是查找到当前位置,把后面的所有元素直接覆盖当前的位置
5. 实现hook
相关api
//添加所有导入表中的函数
int add_all_function(IAT_struct *IAT, IMAGE_IMPORT_DESCRIPTOR *import);
//初始化结构
int init_IAT(IAT_struct *IAT);
//通过函数名字查找地址,实现hook
int function_name_hook(IAT_struct *IAT, const char *name, DWORD obj_addr);
//解除hook
int function_name_unhook(IAT_struct *IAT, const char *name);
//释放结构
int uninit_IAT(IAT_struct *IAT);
//辅助函数,调试用的,用于查看所有的函数表
int show_function_dir(IAT_struct *IAT);
//查看已经hook的函数名
int show_hook_name(IAT_function_data_arr *arr);
5.1 添加导入表中的函数
int add_all_function(IAT_struct *IAT, IMAGE_IMPORT_DESCRIPTOR *import)
{
char end_buf[20] = { 0 };//判断结束标志
IAT_function_data data = {0};//临时添加元素的变量
for (int i = 0; memcmp(import + i, end_buf, 20); i++)
{
//获取函数名字的起始地址 和 函数地址的起始地址
DWORD *function_addr = (DWORD *)((char *)IAT->image_base + import[i].FirstThunk);
DWORD *function_name = (DWORD *)((char *)IAT->image_base + import[i].OriginalFirstThunk);
//添加所有的函数
for (int i = 0; function_addr[i] != 0; i++)
{
//函数名字
data.function_name.str = (char *)IAT->image_base + function_name[i] + 2;
data.function_name.len = strlen(data.function_name.str);
//函数地址所在内存的位置
data.function_addr = (DWORD)(function_addr + i);
//函数地址
data.function_addr_data = function_addr[i];
//添加在数组中
add_function_data_arr(&IAT->function,&data);
}
}
return 0;
}
5.2 初始化函数
int init_IAT(IAT_struct *IAT)
{
if (IAT == NULL)
{
return -1;
}
//获取模块基地址
IAT->image_base = GetModuleHandleA(NULL);
if (IAT->image_base == NULL)
{
return -2;
}
//初始化函数数组和hook数组
init_function_data_arr(&IAT->function);
init_function_data_arr(&IAT->hook_function);
//dos头
IMAGE_DOS_HEADER *dos_head = (IMAGE_DOS_HEADER *)IAT->image_base;
IMAGE_NT_HEADERS32 *pe_head = (IMAGE_NT_HEADERS32 *)((char *)IAT->image_base + dos_head->e_lfanew);//pe头
//导入表
IAT->import = (IMAGE_IMPORT_DESCRIPTOR *)((char *)IAT->image_base + pe_head->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
//添加所有导入表中的函数
add_all_function(IAT, IAT->import);
return 0;
}
初始化IAT结构的各个成员
5.3 释放函数
int uninit_IAT(IAT_struct *IAT)
{
if (IAT == NULL)
{
return -1;
}
//释放两个数组的内存
uninit_function_data_arr(&IAT->function);
uninit_function_data_arr(&IAT->hook_function);
return 0;
}
释放内存即可
5.4 核心函数(hook实现)
int function_name_hook(IAT_struct *IAT, const char *name, DWORD obj_addr)
{
if (IAT == NULL)
{
return -1;
}
IAT_function_data_arr functions = IAT->function;
//遍历函数表查找是否有这个函数
for (int i = 0; i < functions.len; i++)
{
//字符串匹配函数名
if (strstr(functions.arr[i].function_name.str, name))
{
//如果之前没有hook过就添加到hook数组里,,判断当前内存的值和一开始保存的值是否一样
if (functions.arr[i].function_addr_data == *((DWORD*)functions.arr[i].function_addr))
{
add_function_data_arr(&IAT->hook_function, functions.arr + i);
}
DWORD tem;
//修改内存属性
VirtualProtectEx(GetCurrentProcess(), (LPVOID)functions.arr[i].function_addr, 4, PAGE_EXECUTE_READWRITE,&tem);
//核心点改变导出表中的函数地址实现hook
*((DWORD *)functions.arr[i].function_addr) = obj_addr;
//改回内存属性
VirtualProtectEx(GetCurrentProcess(), (LPVOID)functions.arr[i].function_addr, 4, tem, NULL);
break;
}
}
return 0;
}
hook的核心函数,遍历之前添加的导入表中的函数,用字符串匹配找到对应的地址,把这个地址改成我们直接写的函数地址,从而实现函数hook。
5.5 解除hook函数
int function_name_unhook(IAT_struct *IAT, const char *name)
{
if (IAT == NULL)
{
return -1;
}
IAT_function_data_arr functions = IAT->hook_function;
for (int i = 0; i < functions.len; i++)
{
if (strstr(functions.arr[i].function_name.str,name))
{
DWORD tem;
VirtualProtectEx(GetCurrentProcess(), (LPVOID)functions.arr[i].function_addr, 4, PAGE_EXECUTE_READWRITE, &tem);
*((DWORD *)functions.arr[i].function_addr) = functions.arr[i].function_addr_data;//改回之前的地址
VirtualProtectEx(GetCurrentProcess(), (LPVOID)functions.arr[i].function_addr, 4, tem, NULL);
//从hook表中删除
del_function_data_arr_num(&IAT->hook_function, i);
break;
}
}
return 0;
}
6.实验hook
#include <stdio.h>
#include <Windows.h>
#include "IAT_hook.h"
size_t my_strlen(const char * _Str)
{
printf("\n\n%s\n\n", _Str);
return 0;
}
int my_strcmp(const char * _Str1, const char * _Str2)
{
printf("%s\n", _Str1);
printf("%s\n", _Str2);
return 0;
}
int my_strcmp2(const char * _Str1, const char * _Str2)
{
printf("1111111111111111111111\n");
return 0;
}
int main(int argc, char *argv[])
{
IAT_struct IAT;
init_IAT(&IAT);
//将strlen hook成 my_strlen
function_name_hook(&IAT, "strlen", (DWORD)my_strlen);
strlen("aaaaabbbb"); //会直接输出aaaaabbbb
show_hook_name(&IAT.hook_function);//查看hook表
function_name_hook(&IAT, "strcmp", (DWORD)my_strcmp);
strcmp("aaaaabbbb", "bbbbbbaa--");
show_hook_name(&IAT.hook_function);
function_name_hook(&IAT, "strcmp", (DWORD)my_strcmp2);
strcmp("aaaaabbbb", "bbbbbbaa--");
show_hook_name(&IAT.hook_function);
function_name_unhook(&IAT, "strcmp");//解除hook
strcmp("aaaaabbbb", "aaaaaabbb");//查看是否有输出
printf("%d\n", strcmp("aaaaabbbb", "aaaaaabbb"));//正常函数测试
show_hook_name(&IAT.hook_function);
uninit_IAT(&IAT);
getchar();
return 0;
}
输出结果:
可以看到我们hook成功了