泰晓科技 -- 聚焦 Linux - 追本溯源,见微知著!
网站地址:http://tinylab.org
微信公众号关注我们知识星球
关注 @泰晓科技

与数百位一线 Linux 工程师做朋友,您准备好了吗?
周一到周五,天天有新文。529 页合集等着领!

开课啦:Linux 码农 5 万月薪必修课!
请稍侯

如何生成干净可阅读的汇编代码

Wu Zhangjin 创作于 2019/09/04

微信公众号知识星球
关注 @泰晓科技

与数百位一线 Linux 工程师做朋友,您准备好了吗?
周一到周五,天天有新文。529 页合集等着领!

By Falcon of TinyLab.org Aug 01, 2019

新版 gcc 默认开启了几个选项,导致学习汇编语言,尤其是入门的同学,很难。

以如下代码为例:

$ cat demo.c
#include <stdio.h>

int main(void)
{
	int i;
	char buffer[64];

	i = 1;
	buffer[0] = 'a';

	return 0;
}

下面这条指令可以生成比较干净简洁的代码:

$ gcc -fno-stack-protector -fomit-frame-pointer -fno-asynchronous-unwind-tables -S demo.c

结果如下:

$ cat demo.s
	.file	"demo.c"
	.text
	.globl	main
	.type	main, @function
main:
	movl	$1, -4(%rsp)
	movb	$97, -80(%rsp)
	movl	$0, %eax
	ret
	.size	main, .-main
	.ident	"GCC: (Ubuntu 8.3.0-16ubuntu3~16.04) 8.3.0"
	.section	.note.GNU-stack,"",@progbits

加个 -m32 参数就可以生成 32 位的:

$ gcc -fno-stack-protector -fomit-frame-pointer -fno-asynchronous-unwind-tables -m32 -S demo.c
$ cat demo.s
	.file	"demo.c"
	.text
	.globl	main
	.type	main, @function
main:
	subl	$80, %esp
	movl	$1, 76(%esp)
	movb	$97, 12(%esp)
	movl	$0, %eax
	addl	$80, %esp
	ret
	.size	main, .-main
	.ident	"GCC: (Ubuntu 8.3.0-16ubuntu3~16.04) 8.3.0"
	.section	.note.GNU-stack,"",@progbits

稍微做个解释:

  • -fno-stack-protector:去掉 stack 保护,stack protector 用于检查 stack 是否被踩
  • -fomit-frame-pointer:不用 fp 寄存器 rbp/ebp,直接用 stack 寄存器 rsp/esp 就好了
  • -fno-asynchronous-unwind-tables:消除 .eh_frame section

.eh_frame 是 DWARF-based unwinding 用来实现 backtrace(), __attribute__((__cleanup__(f))), __buildtin_return_address(n), pthread_cleanup_push 等,具体请参考 assembly - Why GCC compiled C program needs .eh_frame…。现在无论是否用到这些功能,gcc 都加了 .eh_frame,所以不用的时候直接删除掉也无妨。

另外,Stack Protector 不是看上去的那么强大,从原理上看,如果刚好跳过了预设了值的位置去踩的话,Stack Protector 其实是检测不出来的,当然,有总比没有好。

下面这种是可以检测出来的:

$ cat demo.c
#include <stdio.h>

int main(void)
{
	char buffer[2];
	int i;

	i = 1;
	buffer[0] = 'a';

	buffer[3] = 'b';

	printf("hello.world");

	return 0;
}

编译和运行,确保可以生成 coredump:

$ gcc -o demo demo.c
$ ulimit -c unlimited
$ ./demo
*** stack smashing detected ***: ./demo terminated
hello.worldAborted (core dumped)

用 gdb 分析 coredump:

$ gdb demo core
[New LWP 89783]
Core was generated by `./demo'.
Program terminated with signal SIGABRT, Aborted.
#0  0x00007f76507fc428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
54	../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  0x00007f76507fc428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1  0x00007f76507fe02a in __GI_abort () at abort.c:89
#2  0x00007f765083e7ea in __libc_message (do_abort=do_abort@entry=1,
    fmt=fmt@entry=0x7f765095649f "*** %s ***: %s terminated\n") at ../sysdeps/posix/libc_fatal.c:175
#3  0x00007f76508e015c in __GI___fortify_fail (msg=&lt;optimized out&gt;, msg@entry=0x7f7650956481 "stack smashing detected")
    at fortify_fail.c:37
#4  0x00007f76508e0100 in __stack_chk_fail () at stack_chk_fail.c:28
#5  0x00000000004005c0 in main ()
(gdb)

可以粗略定位到有 Stack Overflow 的函数,但不能定位到具体哪一行踩了数据。

本文的例子汇整在 Linux Lab: examples/c/hello

Read Related:

Read Latest: