c - 在简单程序上使用ftrace,内联汇编__asm __(“leave”)导致段错误

标签 c gcc x86 inline-assembly ftrace

我正在读这本关于学习Linux二进制分析的书。在这本书中,作者介绍了ftrace,他在github上有ftrace,并演示了如何使用它。他提供了一点测试ftrace的代码。
在上面运行ftrace时,什么都不会发生。如果我自己运行可执行文件,我只会得到一个seg错误。
我是这样编译的:gcc-nostdlib test.c-o test
这是我的密码:

int foo(void) {  
}

_start()
{
    foo();
    __asm__("leave");
}

预期的结果显示ftrace通过执行跟踪函数调用。
这是一张图片,来自于我要讲的内容:
这是我正在使用的ftrace:
https://github.com/elfmaster/ftrace
我想问题是,我是不是完全漏掉了什么,做错了什么,文本是不是过时了,或者正确的方法是什么?如果这是个愚蠢的问题,我很抱歉,我只是在发短信。我也尝试过在虚拟机上使用32位发行版,但没有任何改变,只是尝试了一下,因为作者的一些示例是在32位上使用的。谢谢您。
注意:当我用一个不会导致seg错误的程序运行他的ftrace时
pid_read() failed: Input/output error <0x1>

最佳答案

_exit(0);结束时呼叫exit_group(0)_start。(链接到gcc -static -nostartfiles而不是-nostdlib以便可以调用libc系统调用包装函数;即使glibc init函数尚未运行so malloc or printf would crash,它们也应该可以工作)。
或者使用内联asm手动进行exit_group(0)系统调用。在x86-64 Linux上:
asm("mov $231, %eax; xor %edi,%edi; syscall");
另请参见How Get arguments value using inline assembly in C without Glibc?以获取有关编写一个黑的x86-64_start来运行自己的C函数的更多信息。(但大多数回答都是关于破解访问argc/argv的调用约定的,这很讨厌,我不建议这么做。)Matteo在这个问题上的回答是用asm编写的,它调用一个普通的C_start函数。
这本书的代码完全被破坏了,原因有二。(我不知道它怎么能在i386或x86-64上工作。我觉得很奇怪。你确定它不只是应该崩溃,但你看看它在那之前做了什么
main不是Linux中的一个函数;您(或编译器生成的代码)不能从它中_start。您需要进行ret系统调用。堆栈1上没有返回地址。
如果函数有其返回地址,则ELF入口点_exit具有_start,如ABI文档中所指定。(x86-64系统V或i386系统V,具体取决于构建的是64位可执行文件还是32位可执行文件。)
在编译器生成的代码中插入argc(它确实是gcc -m32/leave或等效的RBP/RSP)在这里没有意义。这有点像一个额外的mov %ebp, %esp,但它破坏了编译器的pop %ebp/pop,因此如果它碰巧选择EBP而不是RBP作为它自己的序言,那么编译器生成的代码将出错。(在静态链接的可执行文件中,leave项上的RBP为0。或者在一个PIE可执行文件中跳转到pop %rbp之前保留RBP中的动态链接器。)
但最终,GCC将编译_start作为一个普通函数,从而最终运行_start指令。任何地方都没有有效/有用的返回地址,因此根本不可能_start工作。
如果编译时没有优化(默认),gcc将默认为ret,因此它的函数序言将设置EBP或RBP作为帧指针,使ret本身不出错成为可能。如果您使用优化(-fno-omit-frame-pointer和更高版本启用leave)编译,gcc不会干扰RBP,当您运行-O1时它将为零,从而直接导致segfault。(因为它执行RSP=RBP,然后使用新的RSP作为-fomit-frame-pointer的堆栈指针)
无论如何,如果它没有出现错误,那么在编译器生成leave作为正常函数结束语的一部分之前,堆栈指针将再次指向pop %rbp。因此生成的编译器将尝试返回到argc。由于堆栈在默认情况下是不可执行的,这将导致segfault。(它指向的是ASCII字符,可能无法解码为有用的x86-64机器代码。)
你可以用GDB单步启动asm来发现这一点。(pop %rbp并使用retakaargv[0])。
一般来说,在编译器后面处理堆栈指针和其他寄存器通常只会导致崩溃。如果在堆栈的上面有一个返回地址,layout regstepi更有意义。
脚注1:
在进程的地址空间中,甚至没有任何机器代码可以被有用的返回地址指向来进行这样的系统调用,除非您将某些机器代码作为arg或环境变量注入。
你链接了si所以没有libc链接。如果您确实动态链接了libc,但仍然编写了自己的pop %rcx(例如,使用leave而不是完整的-nostdlib),ASLR将意味着libc_start函数位于某个运行时变量地址。
如果静态链接libc(gcc -nostartfiles),则除非实际引用它,-nostdlib的代码将不会复制到可执行文件中,但这段代码不会引用它。但仍需要以某种方式调用它;没有指向它的返回地址。

关于c - 在简单程序上使用ftrace,内联汇编__asm __(“leave”)导致段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56782813/

相关文章:

c - 如何保持 pjsip 连接有效?

c - 即使源代码和编译器标志未修改,是什么导致目标文件在编译之间有所不同?

linux - CMP 命令无法正常工作

c++ - 为什么分配给堆栈中的局部变量的内存比 C++ 中所需的多?

winapi - gcc 将输入通过管道传输到其中时出现错误文件描述符错误

assembly - 如何根据 cmpss 输出(浮点比较)进行跳转?

c++ - 我有一个应用程序,其中 list 文件包含对 Visual Studio 2008 运行时的引用,但我想在 VS2010 express 中编译它

c - 如何从较大(11 字节)的唯一编号生成较小的唯一编号?将军C

c - 是否可以访问其自身大小的数组元素?

c - 指向字符串数组的指针在函数中可以,但在外部不行