内存Hook
功能: 把其他人写的函数或者库中的函数在调用的时候跳转到自己写的函数中
1.实现所用到的Windows api
WriteProcessMemory(
PVZ_handle,
(LPVOID)address,
date,
sizeof(date),
NULL);
BOOL ReadProcessMemory(
HANDLE hProcess,
PVOID pvAddressRemote,
PVOIDpvBufferLocal,
DWORD dwSize,
PDWORDpdwNumBytesRead
);
BOOL VirtualProtectEx(
HANDLE hProcess,
LPVOID lpAddress,
DWORD dwSize,
DWORD flNewProtect,
PDWORD lpflOldProtect
);
GetCurrentProcess()
2.实现的核心
思路:
2.1 修改所要hook函数的机器码,直接改成 jmp 相对地址
2.2 在汇编中jmp跳转的实际地址是 ip寄存器 + jmp后面的地址 所以是相对与当前的地址
2.3 汇编中的额jmp指令对应的机器码之一有一个是 ‘0xE9’ ,‘0xE9’后面可以跟一个4字节的地址,在32位程序中完全够用
2.4 ip寄存器指向的是下一个指令代码,在修改后 ‘E9 0x-------’占了5个字节。所以相对地址的计算方式是’目标地址 - hook的函数地址 - 5’
char write_data[5] = { 0xE9 };
unsigned long addr = (unsigned long)des_addr - (unsigned long)src_addr - 5;
memcpy(write_data + 1, &addr, 4);
WriteProcessMemory(GetCurrentProcess(), (LPVOID)src_addr,write_data,sizeof(write_data), NULL);
3. 实现代码
#ifndef __MEMHOOK_H__
#define __MEMHOOK_H__
#include <Windows.h>
#include <stdint.h>
#ifdef _WIN64
#define LENGTH 12
#else
#define LENGTH 5
#endif
#define SUCCESS 0
#define READMEM_ERROR 1
#define WRITEMEM_ERROR 2
#define EVISEMEM_ERROR 3
#define PARAMETER_ERROR 4
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct hook_struct
{
void *src_addr;
void *des_addr;
char buf[LENGTH];
int retcode;
}hook_struct;
int hook_init(struct hook_struct *hook, void *src_addr, void *des_addr);
int hook_undata(struct hook_struct *hook, void *src_addr, void *des_addr);
int hook_install(struct hook_struct *hook);
int hook_uninstall(struct hook_struct *hook);
int hook_uninit(struct hook_struct *hook);
#ifdef __cplusplus
}
#endif
#endif
重点写了32位 int hook_install(struct hook_struct *hook);int hook_uninstall(struct hook_struct *hook);的注释重点看懂核心部分就行了。由于x64实现和之前讲的不是一样的方法实现跳转并且暂时是不稳定的不建议使用(后面说明原因),x64我的实现思路是改为mov rax,0x----- jmp rax但是占了12个字节
#include "memhook.h"
#ifdef _WIN64
int hook_init(struct hook_struct *hook, void *src_addr, void *des_addr)
{
if (hook == NULL || src_addr == NULL || des_addr == NULL) return -PARAMETER_ERROR;
hook->src_addr = src_addr;
hook->des_addr = des_addr;
hook->retcode = 0;
memset(hook->buf, 0, sizeof(hook->buf));
return SUCCESS;
}
int hook_undata(struct hook_struct *hook, void *src_addr, void *des_addr)
{
return hook_init(hook, src_addr, des_addr);
}
int hook_install(struct hook_struct *hook)
{
if (hook == NULL) return -PARAMETER_ERROR;
DWORD OldProtect = 0;
if (VirtualProtectEx(GetCurrentProcess(), (LPVOID)hook->src_addr, LENGTH, PAGE_EXECUTE_READWRITE, &OldProtect) == FALSE)
{
hook->retcode = -EVISEMEM_ERROR;
return -EVISEMEM_ERROR;
}
SIZE_T length = 0;
if (ReadProcessMemory(GetCurrentProcess(), (PVOID)hook->src_addr, hook->buf, LENGTH, &length) == FALSE)
{
hook->retcode = -READMEM_ERROR;
return -READMEM_ERROR;
}
unsigned char write_data[LENGTH] = { 0x48,0xB8 };
memcpy(write_data + 2, &hook->des_addr, 8);
write_data[LENGTH - 2] = 0xFF;
write_data[LENGTH - 1] = 0xE0;
if (WriteProcessMemory(GetCurrentProcess(), (LPVOID)hook->src_addr, write_data, sizeof(write_data), NULL) == FALSE)
{
hook->retcode = -WRITEMEM_ERROR;
return -WRITEMEM_ERROR;
}
DWORD Protect = 0;
if (VirtualProtectEx(GetCurrentProcess(), (LPVOID)hook->src_addr, LENGTH, OldProtect, &Protect) == FALSE)
{
hook->retcode = -EVISEMEM_ERROR;
return -EVISEMEM_ERROR;
}
return hook->retcode;
}
int hook_uninstall(struct hook_struct *hook)
{
if (hook == NULL) return -PARAMETER_ERROR;
DWORD OldProtect = 0;
if (VirtualProtectEx(GetCurrentProcess(), (LPVOID)hook->src_addr, LENGTH, PAGE_EXECUTE_READWRITE, &OldProtect) == FALSE)
{
hook->retcode = -EVISEMEM_ERROR;
return -EVISEMEM_ERROR;
}
if (WriteProcessMemory(GetCurrentProcess(), (LPVOID)hook->src_addr, hook->buf, LENGTH, NULL) == FALSE)
{
hook->retcode = -WRITEMEM_ERROR;
return -WRITEMEM_ERROR;
}
DWORD Protect = 0;
if (VirtualProtectEx(GetCurrentProcess(), (LPVOID)hook->src_addr, LENGTH, OldProtect, &Protect) == FALSE)
{
hook->retcode = -EVISEMEM_ERROR;
return -EVISEMEM_ERROR;
}
return SUCCESS;
}
int hook_uninit(struct hook_struct *hook)
{
if (hook == NULL) return -PARAMETER_ERROR;
hook->src_addr = NULL;
hook->des_addr = NULL;
hook->retcode = 0;
memset(hook->buf, 0, sizeof(hook->buf));
return SUCCESS;
}
#else
int hook_init(struct hook_struct *hook, void *src_addr, void *des_addr)
{
if (hook == NULL || src_addr == NULL || des_addr == NULL) return -PARAMETER_ERROR;
hook->src_addr = src_addr;
hook->des_addr = des_addr;
hook->retcode = 0;
memset(hook->buf, 0, sizeof(hook->buf));
return SUCCESS;
}
int hook_undata(struct hook_struct *hook, void *src_addr, void *des_addr)
{
return hook_init(hook, src_addr, des_addr);
}
int hook_install(struct hook_struct *hook)
{
if (hook == NULL) return -PARAMETER_ERROR;
DWORD OldProtect = 0;
if (VirtualProtectEx(GetCurrentProcess(), (LPVOID)hook->src_addr, LENGTH, PAGE_EXECUTE_READWRITE, &OldProtect) == FALSE)
{
hook->retcode = -EVISEMEM_ERROR;
return -EVISEMEM_ERROR;
}
SIZE_T length = 0;
if (ReadProcessMemory(GetCurrentProcess(), (PVOID)hook->src_addr, hook->buf, LENGTH, &length) == FALSE)
{
hook->retcode = -READMEM_ERROR;
return -READMEM_ERROR;
}
char write_data[LENGTH] = { 0xE9 };
unsigned long addr = (unsigned long)hook->des_addr - (unsigned long)hook->src_addr - 5;
memcpy(write_data + 1, &addr, 4);
if (WriteProcessMemory(GetCurrentProcess(), (LPVOID)hook->src_addr, write_data, sizeof(write_data), NULL) == FALSE)
{
hook->retcode = -WRITEMEM_ERROR;
return -WRITEMEM_ERROR;
}
DWORD Protect = 0;
if (VirtualProtectEx(GetCurrentProcess(), (LPVOID)hook->src_addr, LENGTH, OldProtect, &Protect) == FALSE)
{
hook->retcode = -EVISEMEM_ERROR;
return -EVISEMEM_ERROR;
}
return hook->retcode;
}
int hook_uninstall(struct hook_struct *hook)
{
if (hook == NULL) return -PARAMETER_ERROR;
DWORD OldProtect = 0;
if (VirtualProtectEx(GetCurrentProcess(), (LPVOID)hook->src_addr, LENGTH, PAGE_EXECUTE_READWRITE, &OldProtect) == FALSE)
{
hook->retcode = -EVISEMEM_ERROR;
return -EVISEMEM_ERROR;
}
if (WriteProcessMemory(GetCurrentProcess(), (LPVOID)hook->src_addr, hook->buf, LENGTH, NULL) == FALSE)
{
hook->retcode = -WRITEMEM_ERROR;
return -WRITEMEM_ERROR;
}
DWORD Protect = 0;
if (VirtualProtectEx(GetCurrentProcess(), (LPVOID)hook->src_addr, LENGTH, OldProtect, &Protect) == FALSE)
{
hook->retcode = -EVISEMEM_ERROR;
return -EVISEMEM_ERROR;
}
return SUCCESS;
}
int hook_uninit(struct hook_struct *hook)
{
if (hook == NULL) return -PARAMETER_ERROR;
hook->src_addr = NULL;
hook->des_addr = NULL;
hook->retcode = 0;
memset(hook->buf, 0, sizeof(hook->buf));
return SUCCESS;
}
#endif
4.测试代码
我使用的环境是vs2017。大家可以把上面两段代码复制一下
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/c7ff12061451dfd53f7ce5c86daf4f66.png)
#include <iostream>
#include <string.h>
#include "memhook.h"
struct hook_struct hook;
char* __cdecl Mystrncpy(
char * _Str1,
char const* _Str2,
size_t _MaxCount
) {
std::cout << "Mystrncpy" << std::endl;
std::cout << _Str1 << "\n" << _Str2 << "\n" << std::endl;
return NULL;
}
void print_A()
{
printf("A-------------------\n");
}
void print_B()
{
printf("B-------------------\n");
}
int main(int argc,char *argv[])
{
hook_init(&hook, strncpy, Mystrncpy); // 把strncpy hook 跳转到自己定义的Mystrncpy
hook_install(&hook);//开始hook
char dbuf[] = "hello qweqweeqeqweqwe";
const char *sbuf = "hello world\n";
strncpy(dbuf, sbuf,strlen(sbuf)+1);//调用被hook的函数
std::cout << "hook 中" << std::endl;
std::cout << dbuf << "\n" << sbuf << "\n" << std::endl;
hook_uninstall(&hook);//解除hook
std::cout << "解除 hook " << std::endl;
strncpy(dbuf, sbuf, strlen(sbuf)+1);//再次调用
std::cout << dbuf << "\n" << sbuf << "\n" << std::endl;
hook_undata(&hook,print_A,print_B);
hook_install(&hook);//hook后调用两次print_A();
print_A();
hook_uninstall(&hook);
print_A();
hook_uninit(&hook);
return 0;
}
输出结果
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/eca39b96089f8b81e47b92f0ce4d0e06.png)
5. 利用vs中的反汇编查看我们改写的机器码
5.1 原来的机器码 strncpy
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/acd1974c8240739389ae9d4e11cc71bf.png)
修改后的
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/e37f665f128fed78e1f7490ba752ab74.png)
上面那段是用自己写的去hook库中的函数,下面我们来看一下两个自己写的函数
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/74de2255481e17ff27eb3e05cbd20f9d.png)
可以明显的看到我们hook成功了,并且没有改变的时候没有影响其他的内存。
特征:在vs中自己写的函数会先跳转到一个地方,那里有一堆跳转的指令,可以看到上图
5.2这个特征对64位程序hook的影响
首先还是看自己写的函数去hook库中的函数
原来的strncpy
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/56df0440c04839ecd431f5296a29e7be.png)
修改后的
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/d6aefb0d294144da7c5028f0bca58d24.png)
看起来好像没问题,那我们继续看 两个自己写的函数
原来的 print_A
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/1bbe07c8266fa673b146f37613ac103e.png)
修改后的
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/8544f08dddfff80052a32dd648faf23d.png)
现在可以看到问题了,我的64位修改思路是mov rax,0x----- jmp rax 一共12个字节,但是因为这个特征,如果在hook的时候恰好调用了_acrt_iob_func那么就会出现问题。有这个特征意味着写64位hook的时候要注意的问题。