繁体   English   中英

如何找到C程序的“退出”

[英]How to find the “exit” of a C program

该测试是在32-bit x86 Linux上进行的。

因此,基本上,我试图通过在汇编代码中插入检测指令来记录已执行的基本块的信息。

我的策略是这样的:在globl数组中写入已执行的基本块的索引,并在数组已满(16M)时将其从内存刷新到磁盘。

这是我的问题。 当检测的二进制文件执行结束时,即使它没有达到16M边界,我也需要将数组刷新到磁盘。 但是,我只是不知道在哪里可以找到assembly程序的出口。

我尝试了这个:

  1. grep exit从目标汇编程序grep exit ,并在call exit指令之前刷新内存。 但是根据一些调试经验,目标C程序(例如md5sum二进制文件)在完成执行后不会调用exit

  2. main功能末尾刷新内存。 但是,在汇编代码中,我只是不知道main函数的确切结尾在哪里。 我可以采取保守的方法,例如,查找所有ret指令,但是在我看来,并非所有main功能都以ret指令结尾。

所以这是我的问题,如何识别assembly code的确切执行端,并在其中插入一些检测指令? 挂钩一些库代码对我来说很好。 我了解使用不同的输入,二进制可以在不同的位置退出,所以我想我需要一些保守的估计。 我清楚吗? 谢谢!

我相信您通常无法做到这一点。 首先,如果main正在返回某些代码,则它是一个退出代码(如果main没有显式return则最新的C标准要求编译器添加隐式 return 0; )。 然后,一个函数可以将exit地址存储在某些数据中(例如,全局函数, struct的字段等),而其他一些函数可以通过函数指针来间接调用它。 实际上,程序可以使用dlopen加载某些插件,并使用dlsym作为"exit"名称,或者只是在插件内部调用exit ,等等。。。AFAIU完全解决了该问题(从动态意义上寻找实际的exit调用)可以证明等同于停止问题 另请参见赖斯定理

在不要求详尽无遗的方法的情况下,我会提出其他建议(假设您对使用C或C ++等编码的程序感兴趣,...您可以使用其源代码)。 您可以使用MELT自定义GCC编译器,以更改在GCC中处理的基本块,以调用某些检测函数。 它不是很简单,但是是可行的...当然,您需要使用这种定制的GCC重新编译一些C代码以对其进行检测。

(免责声明,我是MELT的主要作者;请随时与我联系以获取更多信息...)

顺便说一句,您了解atexit(3)吗? 它可能对您的刷新问题有帮助...,并且您还可以使用LD_PRELOAD技巧(有关动态链接器的信息 ,请参阅ld-linux(8) )。

atexit()将正确处理95%以上的程序。 您可以修改其已注册处理程序链,也可以像其他模块一样对其进行检测。 但是,某些程序可能会通过使用不调用atexit处理程序的_exit()终止。 可能检测_exit来调用数据刷新并在其类似BSD的程序上安装atexit(或on_exit() )处理程序应覆盖几乎100%的程序。


附录:请注意, Linux基本规范 指出 C库启动应:

调用初始化函数(* init)()。
用适当的参数调用main()。
使用main()的返回值调用exit()。

每次都应该起作用的方法是创建一个共享内存部分,用于在其中存储数据。

您还创建了一个子进程,该进程正在等待调试的进程完成。

待调试的进程完成后,子进程将使用共享内存中的数据来完成写操作。

这应该适用于所有形式的退出,过程中断(例如Ctrl + C,关闭终端窗口等),或者即使该过程已使用“ kill”杀死。

但是根据一些调试经验,目标C程序(例如md5sum二进制文件)在完成执行后不会调用exit。

让我们看一下i686 GNU / Linux系统上的md5sum二进制文件:

在反汇编中( objdump -d /usr/bin/md5sum ),我们有:

Disassembly of section .text:

08048f50 <.text>:
 8048f50:       55                      push   %ebp
 8048f51:       89 e5                   mov    %esp,%ebp
 8048f53:       57                      push   %edi
 8048f54:       56                      push   %esi
 8048f55:       53                      push   %ebx
 8048f56:       83 e4 f0                and    $0xfffffff0,%esp
 8048f59:       81 ec c0 00 00 00       sub    $0xc0,%esp
 8048f5f:       8b 7d 0c                mov    0xc(%ebp),%edi

[ ... ]

 8049e8f:       68 b0 d6 04 08          push   $0x804d6b0
 8049e94:       68 40 d6 04 08          push   $0x804d640
 8049e99:       51                      push   %ecx
 8049e9a:       56                      push   %esi
 8049e9b:       68 50 8f 04 08          push   $0x8048f50
 8049ea0:       e8 4b ef ff ff          call   8048df0 <__libc_start_main@plt>
 8049ea5:       f4                      hlt    

这是所有启动样板代码。 实际程序的main调用在__libc_start_main调用内调用。 如果程序从那里返回,那么,看,有一条hlt指令。 那是你的目标。 查找该hlt指令并将其作为程序的结尾。

您可以尝试以下方法:

int main() 
bool keepGoing = true;
{
    while(keepGoing) {
        string x;
        cin >> x;
        if(x == "stop") {
            keepGoing = false;
        }
    }
}

即使它是原始的……我可能也砍掉了代码,但这只是一个概念。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM