吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3340|回复: 4
收起左侧

[C&C++ 原创] 在Linux C/C++中对现有函数添加Hook的方法

[复制链接]
Rydia 发表于 2019-11-28 12:24
本帖最后由 Rydia 于 2019-12-11 15:44 编辑

在C++中,可按如下方式对现有的函数动态添加Hook:
  • 使用mprotect()去除函数代码所在内存页的只读保护;
  • 备份函数的前若干个字节 (需完整指令,长度能够覆盖下面第3步中的跳转指令即可);
  • 在函数代码起始位置使用一条跳转指令覆盖原函数代码,跳转到自定义的钩子函数。
  • 使用mprotect()恢复代码的只读保护。
  • 如果需要执行原函数,可将第2步中备份的原函数头部后面再拼接一条跳转指令,转向原函数的后续代码。
一个简单的实现如下:(该代码使用的Zydis库可从https://github.com/zyantific/zydis下载)
#include <dlfcn.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <Zydis/Zydis.h>

#define PAGE_SIZE         sysconf(_SC_PAGESIZE)
#define PAGE_MASK         (~(PAGE_SIZE-1))
#define PAGE_ALIGN(addr)  (unsigned char *)(((uintptr_t)(addr) + PAGE_SIZE - 1) & PAGE_MASK)
#define BYTES_SIZE_REL (1 + sizeof(uint32_t))
#define BYTES_SIZE     (5 + sizeof(uint64_t))

static void ConstructJmpRel(void* x, void* target) {
    ((uint8_t*)x)[0] = 0xe9;
    *(uint32_t*)((uint8_t*)x + 1) = (uintptr_t)target - ((uintptr_t)x + 5);
}

static void ConstructJmp(void* x, void* target) {
    ((uint8_t*)x)[0] = 0x49;
    ((uint8_t*)x)[1] = 0xbb;
    *(uint64_t*)((uint8_t*)x + 2) = (uint64_t)target;
    ((uint8_t*)x)[10] = 0x41;
    ((uint8_t*)x)[11] = 0xff;
    ((uint8_t*)x)[12] = 0xe3;
}

static int CodeCopy(void* dst, void* src, int min_len) {
    ZydisDecoder decoder;
    ZydisDecodedInstruction instruction;
    int offset = 0;
    const char* data = (const char*)src;
    ZydisFormatter formatter;
    ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL);
    ZydisFormatterSetProperty(&formatter, ZYDIS_FORMATTER_PROP_ADDR_FORMAT,
                              ZYDIS_ADDR_FORMAT_RELATIVE_SIGNED);
    ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64);

    while (ZYDIS_SUCCESS(ZydisDecoderDecodeBuffer(&decoder, data + offset, 4096, 0, &instruction))) {
        memcpy((char*)dst + offset, (char*)src + offset, instruction.length);
        char buffer[256];
        ZydisFormatterFormatInstruction(&formatter, &instruction, buffer, sizeof(buffer));
        if (strstr(buffer, "[rip+0x") != NULL) {
            if (instruction.length == 7 &&
                    (strncmp(buffer, "lea", 3) == 0 || strncmp(buffer, "mov", 3) == 0) &&
                    strstr(buffer, ", [rip+0x") != NULL) {
                // need to patch things like 'lea rax, [rip+0x13BCE]', which
                // happens a lot at the beginning of func
                int reladdr = *(int*)(data + offset + 3);
                int64_t new_reladdr = (uint64_t)src + reladdr - (uint64_t)dst;
                if (new_reladdr > 0xFFFFFFFFLL || new_reladdr < -0xFFFFFFFFLL) {
                    return 0; // cannot patch this
                }
                *(int*)((char*)dst + offset + 3) = (int)new_reladdr;
            } else {
                return 0; // not supported yet
            }
        } else if (buffer[0] == 'j') {
            return 0; // jump instructions not supported
        }

        offset += instruction.length;
        if (offset >= min_len) {
            return offset;
        }
    }
    return 0; // failed
}

void* InstallHook(void* func, void* new_func) {
    static int num_hooks = 0;
    static unsigned char* hook_buf = NULL;

    if (num_hooks >= 65536 / 64) {
        return NULL; // too many hooks
    }

    if (hook_buf == NULL) {
        hook_buf = (unsigned char*)mmap(NULL, 65536, PROT_READ | PROT_WRITE | PROT_EXEC,
            MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    }

    void* buf = (void*)(hook_buf + num_hooks * 64);
    mprotect(hook_buf, 65536, PROT_READ | PROT_WRITE | PROT_EXEC);
    int64_t size_diff = (int64_t)buf - (int64_t)func;
    int prot_size = PAGE_SIZE;
    if ((unsigned char*)func + BYTES_SIZE > PAGE_ALIGN(func)) {
        prot_size *= 2;
    }
    if (size_diff > 0xFFFFFFF0LL || size_diff < -0xFFFFFFF0LL) {
        int len = CodeCopy(buf, func, BYTES_SIZE);
        if (len == 0) {
            mprotect(hook_buf, 65536, PROT_READ | PROT_EXEC);
            return NULL;
        }
        mprotect(PAGE_ALIGN(func) - PAGE_SIZE, prot_size,
                 PROT_READ | PROT_WRITE | PROT_EXEC);
        ConstructJmp((unsigned char *)buf + len, (unsigned char*)func + len);
        ConstructJmp(func, new_func);
        mprotect(PAGE_ALIGN(func) - PAGE_SIZE, prot_size, PROT_READ | PROT_EXEC);
    } else {
        int len = CodeCopy(buf, func, BYTES_SIZE_REL);
        if (len == 0) {
            mprotect(hook_buf, 65536, PROT_READ | PROT_EXEC);
            return NULL;
        }
        mprotect(PAGE_ALIGN(func) - PAGE_SIZE, prot_size,
                 PROT_READ | PROT_WRITE | PROT_EXEC);
        ConstructJmpRel((unsigned char *)buf + len, (unsigned char*)func + len);
        ConstructJmpRel(func, new_func);
        mprotect(PAGE_ALIGN(func) - PAGE_SIZE, prot_size, PROT_READ | PROT_EXEC);
    }
    mprotect(hook_buf, 65536, PROT_READ | PROT_EXEC);
    num_hooks++;
    return buf;
}


使用示例:
void *test1_hook = NULL;
void Test1() {  printf("Test1() called!\n"); }

void Test1_HOOK() {
   printf("Test1_HOOK() called!\n");
   ((void (*)())test1_hook)();
}

int main(int argc, char *argv[]) {
  Test1();  printf("========\n");
   // 对Test1()函数添加Hook, 添加后再调用Test1()则会先调用Test1_HOOK()
  test1_hook = InstallHook((void *)&Test1, (void *)&Test1_HOOK);
  Test1();
}
以上程序运行结果:

Test1() called!
========
Test1_HOOK() called!
Test1() called!

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

苏紫方璇 发表于 2019-11-28 13:31
建议楼主重新编辑排版一下,代码可以使用插入代码功能
【公告】发帖代码插入以及添加链接教程(有福利)
https://www.52pojie.cn/thread-713042-1-1.html
(出处: 吾爱破解论坛)
smth 发表于 2019-12-6 14:45
ytfrdfiw 发表于 2020-1-1 17:24
9152pojie 发表于 2020-1-2 14:02
排版修改好了,看上去不错,用的比较原始,func入口修改跳转
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-16 19:35

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表