编辑 ELF 二进制调用指令

Posted

技术标签:

【中文标题】编辑 ELF 二进制调用指令【英文标题】:Editing ELF binary call instruction 【发布时间】:2014-12-27 14:52:14 【问题描述】:

我正在玩弄一个二进制的调用函数。我有以下代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void myfunc2(char *str2, char *str1);

    enter code here

void myfunc(char *str2, char *str1)

    memcpy(str2 + strlen(str2), str1, strlen(str1));


int main(int argc, char **argv)

    char str1[4] = "tim";
    char str2[10] = "hello ";

    myfunc((char *)&str2, (char *)&str1);

    printf("%s\n", str2);

        myfunc2((char *)&str2, (char *)&str1);

    printf("%s\n", str2);

    return 0;


void myfunc2(char *str2, char *str1)

    memcpy(str2, str1, strlen(str1));

我已经编译了二进制文件并使用 readelf 或 objdump 我可以看到我的两个函数位于:

46: 000000000040072c 52 FUNC 全局默认值 13 myfunc2**

54: 000000000040064d 77 FUNC 全局默认值 13 myfunc**

使用命令 objdump -D test(我的二进制文件名),我可以看到 main 有两个 callq 函数。我尝试使用上面的地址 72c 编辑第一个指向 myfunc2,但这不起作用;导致二进制文件失败。

000000000040069a <main>:
  40069a:   55                      push   %rbp
  40069b:   48 89 e5                mov    %rsp,%rbp
  40069e:   48 83 ec 40             sub    $0x40,%rsp
  4006a2:   89 7d cc                mov    %edi,-0x34(%rbp)
  4006a5:   48 89 75 c0             mov    %rsi,-0x40(%rbp)
  4006a9:   64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
  4006b0:   00 00 
  4006b2:   48 89 45 f8             mov    %rax,-0x8(%rbp)
  4006b6:   31 c0                   xor    %eax,%eax
  4006b8:   c7 45 d0 74 69 6d 00    movl   $0x6d6974,-0x30(%rbp)
  4006bf:   48 b8 68 65 6c 6c 6f    movabs $0x206f6c6c6568,%rax
  4006c6:   20 00 00 
  4006c9:   48 89 45 e0             mov    %rax,-0x20(%rbp)
  4006cd:   66 c7 45 e8 00 00       movw   $0x0,-0x18(%rbp)
  4006d3:   48 8d 55 d0             lea    -0x30(%rbp),%rdx
  4006d7:   48 8d 45 e0             lea    -0x20(%rbp),%rax
  4006db:   48 89 d6                mov    %rdx,%rsi
  4006de:   48 89 c7                mov    %rax,%rdi
  4006e1:   e8 67 ff ff ff          callq  40064d <myfunc>
  4006e6:   48 8d 45 e0             lea    -0x20(%rbp),%rax
  4006ea:   48 89 c7                mov    %rax,%rdi
  4006ed:   e8 0e fe ff ff          callq  400500 <puts@plt>
  4006f2:   48 8d 55 d0             lea    -0x30(%rbp),%rdx
  4006f6:   48 8d 45 e0             lea    -0x20(%rbp),%rax
  4006fa:   48 89 d6                mov    %rdx,%rsi
  4006fd:   48 89 c7                mov    %rax,%rdi
  400700:   e8 27 00 00 00          callq  40072c <myfunc2>
  400705:   48 8d 45 e0             lea    -0x20(%rbp),%rax
  400709:   48 89 c7                mov    %rax,%rdi
  40070c:   e8 ef fd ff ff          callq  400500 <puts@plt>
  400711:   b8 00 00 00 00          mov    $0x0,%eax
  400716:   48 8b 4d f8             mov    -0x8(%rbp),%rcx
  40071a:   64 48 33 0c 25 28 00    xor    %fs:0x28,%rcx
  400721:   00 00 
  400723:   74 05                   je     40072a <main+0x90>
  400725:   e8 f6 fd ff ff          callq  400520 <__stack_chk_fail@plt>
  40072a:   c9                      leaveq 
  40072b:   c3                      retq 

我怀疑我需要通过相对位置或使用 lea/mov 指令来计算地址信息。

任何帮助学习如何修改调用函数将不胜感激 - 请不要像大多数互联网上的howtos那样编辑字符串...

【问题讨论】:

您还应该使用例如编译您的 C 代码。 gcc -fverbose-asm -S 然后查看生成的汇编代码。顺便说一句,您的确切问题是什么? callq x86 机器指令的文档你仔细阅读了吗? 你为什么不写myfunc((char *)&amp;str2, (char *)&amp;str1);而不是myfunc(str2, str1);?这肯定更容易阅读吗?我永远不会理解这种在很多人似乎都有的地方投射的要求。 您还需要阅读x86-64 ABI 规范。 请编辑您的问题以改进它。你到底在问什么不清楚。 “任何帮助”对于 SO 来说太宽泛了! 你不能只是“重写”地址。您需要知道callq 指令编码的确切方式,并在其位置编码新指令。如果新编码的指令恰好大小不同,那你就没那么幸运了 :-) 【参考方案1】:

为了重写地址,你必须知道callq指令的编码方式。

我们来看看第一次调用的反汇编输出:

4006e1: e8 67 ff ff ff          callq  40064d <myfunc>
4006e6: ...

您可以清楚地看到该指令是用 5 个字节编码的。 e8 字节是指令操作码,67 ff ff ff 是要跳转到的地址。这时有人会问,67 ff ff ff0x40064d有什么关系?

嗯,答案是e8 编码了一个所谓的“相对调用”,跳转是相对于下一条指令的位置进行的。您必须计算4006e6 和被调用函数之间的距离才能重写地址。如果调用是绝对的(ff),您可以将函数地址放在这 4 个字节中。

为了证明确实如此,请考虑以下算术:

0x004006e6 + 0xffffff67 == 0x10040064d

【讨论】:

太好了,好吧,上面你说过,如果编码指令的大小不同——我可能就没那么幸运了。这是相同的指令,那可以吗? 在特殊情况下它不应该是一个问题 - 它可以很容易地再次用 5 个字节编码。但是,在一般情况下,如果您需要用更大的指令替换指令,则会破坏该指令下的所有代码。它下面的指令可能使用 PC 相对寻址或 call 寻址,就像这样,它们的所有地址都将被您引入的这种差异所抵消。如果新指令较小,您可以使用NOP 填充结尾来解决此问题。 好的,计算“相对位置”——这是从 main 中的 myfunc 调用到 myfunc2 的位置吗?这对我来说有点模棱两可 首先,找出myfuncmyfunc2的确切绝对地址(提示:40064d和40072c)。然后,找到要修补的指令的位置。计算函数地址与调用后的指令地址之间的差值。用该差异修补此指令中的 4 个字节(字节必须按 little-endian 顺序)。 非常感谢,我已经成功了。最后一个问题,假设我想调用一个地址低于我所在位置的函数。相对位置是有符号整数还是无符号整数(因此它可以处理反向备份堆栈)?

以上是关于编辑 ELF 二进制调用指令的主要内容,如果未能解决你的问题,请参考以下文章

包含 NEON 或 VFP3 指令的 ELF?

Android 逆向使用 Python 解析 ELF 文件 ( Capstone 反汇编 ELF 文件中的机器码数据 | 反汇编二进制机器码 | 打印反汇编数据 )

二进制安全:ELF文件深度分析Linux二进制代码审计

Android 逆向ELF 文件格式 ( 安装 010 Editor 二进制查看工具的 ELF.bt 插件模板 | 安装 ELF.bt 模板 | 打开 ELF 文件 )

Android 逆向ELF 文件格式 ( 安装 010 Editor 二进制查看工具的 ELF.bt 插件模板 | 安装 ELF.bt 模板 | 打开 ELF 文件 )

ELF应用程序二进制接口