inlinehook的核心就是:跳转、跳板。
1、什么是hook?
在2021年1月8日,美国国会山骚乱事件的两天后,米利再次给中国打了个电话:
“美国的情况“很稳定”,一切都很好,国会山骚乱仅仅只是“偶然事件””。
“你知道的,民主有时候就是有点乱糟糟的”。
同一天,米利在五角大楼里秘密召开了一次会议,告知其他高级将领:
“自今天之后,军队执行任何重大行动之前必须首先与他磋商。”
“不管谁给你们下达命令,你们都要按照“程序”来执行,而我就是这个“程序”!”
2、hook技术有什么用?
hook的实现有很多种,inlinehook是hook的一种方式。通过修改原函数开头的汇编指令,直接跳转到指定函数。
4.1、hook库代码
4.2、经典场景
5、inlinehook代码实现
场景一:初级、经典inlinehook
///////////////////////////////////////////////////////////
void hello_world()
{
printf("[call %s]n",__FUNCTION__);
}
void my_hello_world()
{
printf("[call %s]n", __FUNCTION__);
}void test_hook_jmp32()
{
// 修改被hook函数内存属性为可写
xx_mem_unprotect(hello_world, 4096);
// 在函数开头插入jmp语句,跳转到my_hello_world
xx_setjmp32(&hello_world, &my_hello_world);
// 测试一下
hello_world();
}
int main()
{
printf("nn======test_hook_jmp32=================n");
test_hook_jmp32();
}
[call my_hello_world]
可见,调用hello_world时,实际执行的是my_hello_world。
xx_mem_unprotect(hello_world, 4096);
一段内存有是否可读、是否可写、是否可执行等属性。代码段默认是不可写的(不可修改),所以需要先设置为可写才能修改。xx_mem_unprotect的windows实现,调用VirtualProtect系统api来实现(linux下的api是mmap)。
DWORD old_flags;
BOOL result = VirtualProtect(address,
size,
PAGE_EXECUTE_READWRITE,
&old_flags);
return result == TRUE;
}
xx_setjmp32(&hello_world, &my_hello_world);
hook以后,hello_world的汇编指令如下:
{
00007FF6642410B0 E9 8B 00 00 00 jmp my_hello_world (07FF664241140h)
00007FF6642410B5
class jmp_rel32
{
public:
struct asm_cmd {
uint8_t opcode_;
int32_t rel32_;
};
static void write(void* cmd_addr, int32_t rel32) {
auto* cmd = (asm_cmd*)cmd_addr;
cmd->opcode_ = 0xe9;
cmd->rel32_ = rel32;
}
static uint8_t size() { return sizeof(asm_cmd); }
};
static int64_t xx_get_offset(void* src, void* dst) {
return (char*)dst - (char*)src;
}// ret:返回从src jmp 到dst的偏移
static int64_t xx_get_jmp32_offset(void* src, void* dst) {
return xx_get_offset((char*)src + jmp_rel32::size(), dst);
}
// 写入汇编,32bit位移跳转,jmp到dst
// ret 5(修改5 byte)
static uint32_t xx_setjmp32(void* src, void* dst) {
int32_t offset = (int32_t)xx_get_jmp32_offset(src, dst);
jmp_rel32::write(src, offset);
return jmp_rel32::size();
}
场景二:64位系统的hook
///////////////////////////////////////////////////////////
int __cdecl my_fclose(
_Inout_ FILE* _Stream
) {
printf("[call %s]n", __FUNCTION__);
return 0;
}
void test_hook_jmp64()
{
// 判断偏移是否满足32位
int64_t offset = xx_get_offset(&fclose, &my_fclose);
bool need_far_jmp = xx_int32_overflow(offset);
printf("need_far_jmp=%u n", need_far_jmp);// 修改被hook函数内存属性为可写
xx_mem_unprotect(&fclose, 1024);
// 借助ret汇编指令实现64位跳转
xx_setjmp64(&fclose, &my_fclose);
// 测试一下
fclose(nullptr);
}
int main()
{
printf("nn======test_hook_jmp64=================n");
test_hook_jmp64();
}
need_far_jmp=1
[call my_fclose]
need_far_jmp=1代表需要“大跳”。
static bool xx_int32_overflow(int64_t val) {
return val < INT32_MIN || val > INT32_MAX;
}
00007FF97A2596A5 C7 44 24 04 F7 7F 00 00 mov dword ptr [rsp+4],7FF7h
00007FF97A2596AD C3 ret
push+mov指令负责把地址压栈,ret指令负责跳转。
class push_imm32
{
public:
struct asm_cmd {
uint8_t opcode_;
uint32_t imm32_;
};static void write(void* cmd_addr, uint32_t imm32) {
auto* cmd = (asm_cmd*)cmd_addr;
cmd->opcode_ = 0x68;//jmp
cmd->imm32_ = imm32;
}
static uint8_t size() { return sizeof(asm_cmd); }
};
// mov dword ptr[rsp + offset],imm32
class mov_rsp_ptr_imm32
{
public:
struct asm_cmd {
uint8_t opcode_;
uint8_t para1_;
uint8_t reg_type_;
int8_t offset_;
uint32_t imm32_;
};
static void write(void* cmd_addr, int8_t off, uint32_t imm32) {
auto* cmd = (asm_cmd*)cmd_addr;
cmd->opcode_ = 0xc7;//mov
cmd->para1_ = 0x44;// to reg ptr
cmd->reg_type_ = 0x24;//rsp
cmd->offset_ = off;
cmd->imm32_ = imm32;
}
static uint8_t size() { return sizeof(asm_cmd); }
};
// ret
class ret
{
public:
struct asm_cmd {
uint8_t opcode_;
};
static void write(void* cmd_addr) {
auto* cmd = (asm_cmd*)cmd_addr;
cmd->opcode_ = 0xc3;
}
static uint8_t size() { return sizeof(asm_cmd); }
};
// ret 14(修改14 byte)
static uint32_t xx_setjmp64(void* src, void* dst) {
char* cmd_addr = (char*)src;push_imm32::write(cmd_addr, (uint32_t)(uintptr_t)dst);
cmd_addr += push_imm32::size();
mov_rsp_ptr_imm32::write(cmd_addr, 4, (uint32_t)(((uintptr_t)dst) >> 32));
cmd_addr += mov_rsp_ptr_imm32::size();
ret::write(cmd_addr);
return push_imm32::size() + mov_rsp_ptr_imm32::size() + ret::size();
}
// ret 修改字节数
static uint32_t xx_setjmp(void* src, void* dst) {
// 64bit system,check jmp32 ok.
int64_t dis = xx_get_jmp32_offset(src, dst);
if (xx_int32_overflow(dis))
return xx_setjmp64(src, dst);
else
return xx_setjmp32(src, dst);
}
场景三:使用跳板,跳回原函数
///////////////////////////////////////////////////////////
char xx_trampoline[1024];
int sum(int a,int b)
{
return a + b;
}
int mysum(int a, int b)
{
printf("[call %s]!a=%d,b=%dn", __FUNCTION__,a,b);
//typedef int(sum_func_t)(int, int);
//sum_func_t *f = (sum_func_t*)((char*)xx_trampoline);
// 执行原逻辑
auto ori_func = xx_trampoline_to_func(&sum, xx_trampoline);
return (*ori_func)(a, b);;
}void test_trampoline()
{
// 制作跳板
xx_mem_unprotect(xx_trampoline,1024);
xx_make_trampoline(&sum, xx_trampoline, 4+4);
// hook
xx_mem_unprotect(&sum, 1024);
xx_setjmp(&sum, &mysum);
// test
int a = sum(1, 2);
printf("a=%un",a);
}
int main()
{
printf("nn======test_trampoline=================n");
test_trampoline();
}
[call mysum]!a=1,b=2
a=3
// ret 修改字节数
static uint32_t xx_setjmp(void* src, void* dst)然后观察原函数汇编代码
int sum(int a,int b)
{
00007FF728C411F0 89 54 24 10 mov dword ptr [b],edx
00007FF728C411F4 89 4C 24 08 mov dword ptr [a],ecx
00007FF728C411F8 8B 44 24 10 mov eax,dword ptr [b]
00007FF728C411FC 8B 4C 24 08 mov ecx,dword ptr [a]
// ret:跳板长度
static uint32_t xx_make_trampoline(void* src, void* trampoline, uint32_t copy_len) {
memcpy(trampoline, src, copy_len);
return copy_len + xx_setjmp((char*)trampoline + copy_len,(char*)src + copy_len);
}
xx_mem_unprotect(xx_trampoline,1024);
xx_make_trampoline(&sum, xx_trampoline, 4+4);
场景四:跳板偏移修复
char xx_trampoline2[1024];void flag_print() {
if (g_ready) {
printf("[call %s]!readyn",__FUNCTION__);
}
else {
printf("[call %s]!not readyn", __FUNCTION__);
}
}
void my_flag_print() {
printf("[call %s]!n", __FUNCTION__);
auto ori = xx_trampoline_to_func(&flag_print, xx_trampoline2);
(*ori)();
}
void test_trampoline_relocation() {
// 制作跳板
xx_mem_unprotect(xx_trampoline2, 1024);
xx_make_trampoline(&flag_print, xx_trampoline2, 4 + 7);
// 跳板偏移重定位
xx_reloc_offset(&flag_print, xx_trampoline2, 4 + 3 );
xx_setjmp(&flag_print, &my_flag_print);
flag_print();
}
00007FF635091070 48 83 EC 28 sub rsp,28h
00007FF635091074 0F B6 05 85 3F 00 00 movzx eax,byte ptr [g_ready (07FF635095000h)]
00007FF635095454 0F B6 05 85 3F 00 00 movzx eax,byte ptr [7FF6350993E0h]
00007FF63509545B E9 1B BC FF FF jmp flag_print+0Bh (07FF63509107Bh)
跳板
00007FF635095454 0F B6 05 85 3F 00 00 movzx eax,byte ptr [7FF6350993E0h]
// val_offset:偏移的位移
static bool xx_reloc_offset(void* src, void* trampoline, uint32_t val_offset)
{
// 获取绝对地址
int32_t* src_offset = (int32_t*)((char*)src + val_offset);
char* src_next_cmd = (char*)src + val_offset + sizeof(int32_t);
char* real_addr = src_next_cmd + *src_offset;// 判断trampoline相对于绝对地址的偏移是否超过int32
char* tra_next_cmd = (char*)trampoline + val_offset + sizeof(int32_t);
int64_t tra_offset_val = xx_get_offset(tra_next_cmd,real_addr);
if (xx_int32_overflow(tra_offset_val)) {
return false;
}
// trampoline重定位
int32_t* tra_offset = (int32_t*)((char*)trampoline + val_offset);
*tra_offset = (int32_t)tra_offset_val;
return true;
}
看雪ID:assqqq
https://bbs.kanxue.com/user-home-405858.htm
# 往期推荐
2、恶意木马历险记
球分享
原文始发于微信公众号(看雪学苑):inlinehook心得分享
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论