我正在阅读有关学习Linux Binary Analysis的本书。在这本书中,作者介绍了ftrace,他将其放在github上并演示了如何使用它。他提供了一些代码来测试ftrace。
在其上运行ftrace时,没有任何反应。如果我自己运行可执行文件,我只会遇到段错误。 我正在像这样编译它:gcc -nostdlib test.c -o test
这是我的代码:
int foo(void) {
}
_start()
{
foo();
__asm__("leave");
}
预期结果表明ftrace在执行过程中跟踪了函数调用。
这是我要摘录的文字的图像:
这是我正在使用的ftrace:
https://github.com/elfmaster/ftrace
我想的问题是,我是否完全遗漏了一些东西,做错了什么,文本是否过时,或者正确的处理方法是什么?如果这是一个愚蠢的问题,我深表歉意,我只是在讲完文字。我还在VM上使用32位发行版进行了尝试,但没有做任何更改,但是仅尝试了一下,因为作者的一些示例在32位上进行。谢谢。
注意:当我使用不会引起段错误的程序运行他的ftrace时,我会得到
pid_read() failed: Input/output error <0x1>
答案 0 :(得分:3)
在_exit(0);
末尾致电exit_group(0)
或_start
。 (使用gcc -static -nostartfiles
而不是-nostdlib
进行链接,因此您可以调用libc系统调用包装函数;即使尚未运行so malloc or printf would crash的glibc初始化函数,它们也应该可以工作。)
或使用嵌入式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编写的整个_start
最小,它调用a正常的C main
函数。
这本书的代码由于两个原因而被普通破解。 (我不知道它在i386或x86-64上是如何工作的。对我来说似乎很奇怪。您确定它不只是应该崩溃的,而是您看看它在此之前发生了什么?)< / p>
_start
在Linux中不是功能;您(或编译器生成的代码)无法ret
使用它。您需要进行_exit
系统调用。堆栈 1 上没有返回地址。
在函数具有返回地址的位置,ELF入口点_start
具有argc
,如ABI文档中所指定。 (x86-64系统V或i386系统V取决于您构建的是64位还是gcc -m32
32位可执行文件。)
(将leave
/ mov %ebp, %esp
或等效的RBP / RSP插入pop %ebp
到编译器生成的代码中< / strong>在这里毫无意义。有点像额外的pop
,但是破坏了编译器的EBP
/ RBP
,因此如果碰巧选择leave
而不是pop %rbp
作为自己的序言,那么编译器生成的代码将出错。 (在静态链接的可执行文件中,进入_start
的RBP为0。或者在PIE可执行文件中,保留跳转到_start
之前在RBP中保留的任何动态链接器。)
但是最终,GCC会将_start
编译为常规函数,从而最终运行了ret
指令。任何地方都没有有效/有用的寄信人地址,因此ret
根本无法工作。
如果不进行优化就进行编译(默认设置),则gcc将默认设置为-fno-omit-frame-pointer
,因此它的函数序言将把EBP或RBP设置为帧指针,从而使leave
本身不会故障。如果您使用优化进行编译(-O1
和更高版本启用了-fomit-frame-pointer
),则gcc不会使RBP混乱,并且在运行leave
时它将为零,从而直接导致段错误。 (因为它执行RSP = RBP,然后将新的RSP用作pop %rbp
的堆栈指针。)
无论如何,如果没有错误,那将使堆栈指针再次指向argc
,直到编译器生成的pop %rbp
作为正常函数结尾的一部分。 因此,编译器生成的ret
将尝试返回到argv[0]
。由于默认情况下堆栈是不可执行的,因此会出现段错误。(而且它指向的是ASCII字符,可能无法解码为有用的x86-64机器代码。)
您可以通过单步使用GDB来自动找到该问题。 ({layout reg
并使用stepi
或si
)。
通常,您弄乱了编译器背后的堆栈指针和其他寄存器通常只会使事情崩溃。而且,如果在堆栈上有一个更高的返回地址,则pop %rcx
比leave
更有意义。
脚注1:
在进程的地址空间中甚至没有任何机器代码可以有用的返回地址 指向进行这样的系统调用,除非您将某些机器代码作为arg或环境注入变量。
您与-nostdlib
链接,因此没有libc链接。如果您确实动态链接了libc,但仍然编写了自己的_start
(例如,用gcc -nostartfiles
而不是完整的-nostdlib
),则ASLR表示libc _exit
函数处于某个运行时-可变地址。
如果您静态链接了libc(gcc -nostartfiles -static
),则_exit()
的代码将不会被复制到可执行文件中,除非您实际引用了该代码,否则该代码不会。但是您仍然需要以某种方式调用它。没有指向它的回信地址。