【问题标题】:How to detour a function on Linux?如何绕过 Linux 上的函数?
【发布时间】:2020-08-10 07:46:17
【问题描述】:

我做了一个 32 位的测试程序,我试图绕过这个功能(内部):

void Function(int number)
{
    std::cout << "Your number is: " << number << std::endl;
}

使用我制作的这个 detour 函数(它会在 memcpy 上引发 SIGSEV 分段错误,即使内存是有效的并且受执行/读/写保护):

bool Detour(byte_t* src, byte_t* dst, size_t size)
{
    if(size < HOOK_MIN_SIZE) return false;
    mprotect(src, size, PROT_EXEC | PROT_READ | PROT_WRITE);
    mem_t jmpAddr = ((mem_t)dst - (mem_t)src) - HOOK_MIN_SIZE;
    byte_t CodeCave[] = { JMP, 0x0, 0x0, 0x0, 0x0 };
    *(mem_t*)((mem_t)CodeCave + sizeof(JMP)) = jmpAddr;
    memcpy(src, CodeCave, sizeof(CodeCave));
    return true;
}

它是这样调用的:

Detour((byte_t*)&Function, (byte_t*)&hkFunction, HOOK_SIZE);

HOOK_MIN_SIZE 等于 5,以适应程序集跳转 (0xE9 0x00 0x00 0x00 0x00),HOOK_SIZE 等于 7,因为它必须适应所有覆盖的指令(对于蹦床,但这没关系在这种情况下):

Dump of assembler code for function Function(int):
   0x5655631d <+0>:    push   ebp
   0x5655631e <+1>:    mov    ebp,esp
   0x56556320 <+3>:    push   ebx
   0x56556321 <+4>:    sub    esp,0x4
   0x56556324 <+7>:    call   0x56556220 <__x86.get_pc_thunk.bx>

GDB 错误:

Program received signal SIGSEGV, Segmentation fault.
0x565564c9 in Detour (
    src=0x5655631d <Function(int)> "U\211\345S\203\354\004\350\367\376\377\377\201\303\367+",
    dst=0x56556372 <hkFunction(int)> "U\211\345S\203\354\004\350\242\376\377\377\201â+", size=7) at main.cpp:51
51        memcpy(src, CodeCave, sizeof(CodeCave));

类型定义:

typedef unsigned int mem_t;
typedef unsigned char byte_t;

这个绕道功能有什么问题?我已经成功地在 Windows 上为 32 位和 64 位制作了自己的版本,但在 Linux 上似乎有所不同。

【问题讨论】:

  • 一个错误的事情是sizeof(JMP) 以字节为单位,但您在int* 指针算术中使用它。那不应该导致你的问题。验证mprotect 的返回值,看看它是否真的有效。注意 man mprotect“addr 必须与页面边界对齐”“EINVAL addr 不是有效指针,或者不是系统页面大小的倍数。”
  • 示例应该是可重现的。您能否提供您的hkFunctionmain 以及我们需要自己编译和尝试的任何其他代码?

标签: c++ linux assembly memory hook


【解决方案1】:

我成功了!问题是地址不是系统页面的倍数,我可以使用这个函数而不是 mprotect 来修复它:

int ProtectMemory(mem_t address, size_t size, int protection)
{
    long pagesize = sysconf(_SC_PAGE_SIZE);
    address = address - (address % pagesize);
    return mprotect((void*)address, size, protection);
}

Detour 函数现在如下所示:

bool Detour(byte_t* src, byte_t* dst, size_t size)
{
    if(size < HOOK_MIN_SIZE) return false;
    //mprotect(src, size, PROT_EXEC | PROT_READ | PROT_WRITE);
    int out = ProtectMemory((mem_t)src, size, PROT_EXEC | PROT_READ | PROT_WRITE);
    //std::cout << out << std::endl;
    mem_t jmpAddr = ((mem_t)dst - (mem_t)src) - HOOK_MIN_SIZE;
    byte_t CodeCave[] = { JMP, 0x0, 0x0, 0x0, 0x0 };
    *(mem_t*)((mem_t)CodeCave + sizeof(JMP)) = jmpAddr;
    memcpy(src, CodeCave, sizeof(CodeCave));
    return true;
}

【讨论】:

  • address = address - (address % pagesize); 有点过于复杂了。这将是 2 的幂,因此 address &amp;= -pagesize 向下舍入到页面的开头,即清除地址的所有页内偏移位。请注意,您假设 src .. src+5 不跨越页面边界。如果函数都按 16 个字节对齐,这将始终是正确的,就像编译器通常做的那样。
  • 另外,size 不是您应该传递给mprotect 的参数(尤其是在四舍五入地址之后)。无论函数有多大,您都希望只写 1 页(包含函数 start)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多