c - 在可执行文件中切换方法调用

标签 c assembly disassembly

我想知道如何将调用切换到可执行文件(在我的例子中是 .exe)中的另一个函数

这是我尝试使用的代码

#include <stdio.h>

void hello()
{
    printf("Hello world!");
}

void investigate()
{
    printf("Investigate all the things!");
}

main()
{
    hello();
}

一旦我编译了上面的代码(使用 gcc)并从中获得了一个可执行文件 (.exe),我想将“hello”调用切换为“investigate”。

--编辑--

我的环境:Windows 10(64 位),mingw with gcc/g++ 4.8.1

--编辑2--

我对 Linux 答案(任何 Ubuntu 或任何 OpenSuse 和任何架构)也很好,因为对我来说,拥有概念验证非常重要。

最佳答案

假设编译器没有完全忽略死函数,它没有内联函数并且调用不会通过 PLT,一旦编译了可执行文件,您可以简单地编辑调用指令。

请注意,这两个函数必须是“兼容的”,这里的兼容性概念是模糊的,意思是“新的必须至少满足编译器在调用旧的时所做的相同假设”。
ABI 当然是一种这样的假设,但它可能不是唯一的。

如果您的编译器遗漏了死函数,您将无法切换该函数(缺少一个)。

如果您的编译器内联调用,则无法切换函数(没有调用)。您可以对抗编译器并在调用点(在 C 源代码中)重写代码,这称为修补。

如果您的编译器使用了 PLT,您需要更改 PLT stub 使用的 GOT 条目。您可能需要稍微记录一下自己,但更改链接程序实际上是 PLT 机制的一个功能。

如果您的编译器没有做任何事情,那么在没有启用优化的情况下,对于这样一个简单的源代码应该是这种情况,您可以使用 objdump -d <file>找到新函数的调用点和地址:

000000000040051d <hello>:
  40051d:   55                      push   %rbp
  40051e:   48 89 e5                mov    %rsp,%rbp
  400521:   bf f0 05 40 00          mov    $0x4005f0,%edi
  400526:   b8 00 00 00 00          mov    $0x0,%eax
  40052b:   e8 d0 fe ff ff          callq  400400 <printf@plt>
  400530:   5d                      pop    %rbp
  400531:   c3                      retq   

0000000000400532 <investigate>:
  400532:   55                      push   %rbp
  400533:   48 89 e5                mov    %rsp,%rbp
  400536:   bf fd 05 40 00          mov    $0x4005fd,%edi
  40053b:   b8 00 00 00 00          mov    $0x0,%eax
  400540:   e8 bb fe ff ff          callq  400400 <printf@plt>
  400545:   5d                      pop    %rbp
  400546:   c3                      retq   

0000000000400547 <main>:
  400547:   55                      push   %rbp
  400548:   48 89 e5                mov    %rsp,%rbp
  40054b:   b8 00 00 00 00          mov    $0x0,%eax
  400550:   e8 c8 ff ff ff          callq  40051d <hello>
  400555:   b8 00 00 00 00          mov    $0x0,%eax
  40055a:   5d                      pop    %rbp
  40055b:   c3                      retq   
  40055c:   0f 1f 40 00             nopl   0x0(%rax)

然后改变call的立即值指令与目标地址和call结束后的地址之间的差异指令(只要两个地址相同,原点在哪里都无所谓)。

Target = 400532 
After the end of call = 400555
Difference = 400532 - 400555 = -23 = 0xFFFFFFDD

Change from:
400550: e8 c8 ff ff ff
to:
400550: e8 dd ff ff ff

请注意,立即数是小尾数。
您可以使用十六进制编辑器来编辑代码,找到文件中的偏移量,您可以使用 Sprite 阅读器并自己做一些数学运算,或者您可以简单地搜索调用指令的字节(也检查周围的字节电话确定)。

编辑后,二进制文件已被修补:

 0000000000400532 <investigate>:
  400532:   55                      push   %rbp
  400533:   48 89 e5                mov    %rsp,%rbp
  400536:   bf fd 05 40 00          mov    $0x4005fd,%edi
  40053b:   b8 00 00 00 00          mov    $0x0,%eax
  400540:   e8 bb fe ff ff          callq  400400 <printf@plt>
  400545:   5d                      pop    %rbp
  400546:   c3                      retq   

0000000000400547 <main>:
  400547:   55                      push   %rbp
  400548:   48 89 e5                mov    %rsp,%rbp
  40054b:   b8 00 00 00 00          mov    $0x0,%eax
  400550:   e8 dd ff ff ff          callq  400532 <investigate>
  400555:   b8 00 00 00 00          mov    $0x0,%eax
  40055a:   5d                      pop    %rbp
  40055b:   c3                      retq   

关于c - 在可执行文件中切换方法调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54884318/

相关文章:

c - 在 C 中创建 double 并将它们插入排序数组的循环

c - 二叉搜索树 - 插入时内存泄漏

assembly - 为什么 Apollo 任务的软件是用汇编而不是高级编程语言编写的?

c++ - 在什么情况下调试时使用反汇编语言很有用

c - 逆向工程c程序

c - 在 C 中用 printf 打印字符不会打印

C 字符串数组顺序操作

assembly - ARM 汇编中的 "Hello world"

c++ - 从德州仪器 C200 DSP 检索代码

compilation - 如何使用LLVM将基于堆栈的虚拟机字节码转换为SSA形式