首页 > 技术文章 > 各种小巧的Hello World

SZLLQ2000 2016-01-05 14:27 原文

在Reddit看到这篇文章:Hello from a libc-free world! ,觉得挺有趣,然后又想起以前看过的各种相关资料,在此做一个整理。注意所有实验环境都为Linux。

版本一:

实际上是用汇编重写_start入口,具体说明请看文章开头提供的连接,汇编代码如下,命名为stubstart.S

 

 

[cpp] view plaincopy
 
  1. _start:  
  2.     call main  
  3.     movl $1, %eax  
  4.     xorl %ebx, %ebx  
  5.     int $0x80  

 

然后与普通hello.c连接(hello.c)的代码我就不用写出来了吧。。命令如下:
gcc -nostdlib stubstart.S -o hello hello.c
OK,一个不需要libc的helloworld程序就完成了。只是简单的跳过了_start的各种初始化

版本二:

与版本一其实差不多,只是用shellcode来完成了,代码如下
[cpp] view plaincopy
 
  1. typedef int (*sc_fun)(int,int,int,int,int,int,int);  
  2.   
  3. void _start(void) {  
  4.   
  5.     char syscall[] = "/x60/x83/xc4/x24/x58/x5b/x59/x5a/x5e/x5f/x5d/xcd/x80/x83/xec/x40/x61/xc3";  
  6.   
  7.     ((sc_fun)syscall)(4, 0, "Hello, World/n", 13, 0, 0, 0);  
  8.   
  9.     ((sc_fun)syscall)(1, 0, 0, 0, 0, 0, 0);  
  10.   
  11. }  



连接命令如下:
gcc -o nostdlib hello.c -m32 -z execstack –nostdlib
嗯,完成了,也是-nostdlib,至于shellcode调用的是什么系统函数,我猜是write吧:)

版本三:

 

[cpp] view plaincopy
 
  1. char *str = "Hello world!/n";  
  2. void print()  
  3. {  
  4.     asm( "movl $13, %%edx /n/t"  
  5.         "movl %0, %%ecx /n/t"  
  6.         "movl $0, %%ebx /n/t"  
  7.         "movl $4, %%eax /n/t"  
  8.         "int $0x80 /n/t"  
  9.         :: "r"(str):"edx","ecx","ebx");  
  10. }  
  11. void exit()  
  12. {  
  13.     asm( "movl $42,%ebx /n/t"  
  14.          "movl $1,%eax /n/t"  
  15.          "int $0x80 /n/t");  
  16. }  
  17. void nomain()  
  18. {  
  19.     print();  
  20.     exit();  
  21. }  

 

关于gcc内联汇编,可参考下相关书籍,代码大概意思是nomain()是入口,然后调用print()函数,打印"Hello world”,接着调用exit()函数,结束进程。这里的print函数使用了Linux的WRITE系统调用,exit使用了EXIT系统调用,都是用内联汇编实现。

连接命令如下:

gcc –c hello.c

ld –static –e nomain –o hello hello.o

注意,这里控制了连接器的行为,用-e指定了入口函数为nomain

版本四:

接着版本三,我们用objdump来查看hello,会发现他有四个段:.text .rodata .data .comment。

那么可不可以把他们都合并到一个段里面,该段的属性是可执行,可读的,包含程序的数据和指令? 可以的,此时需要使用ld连接脚本创建脚本hello.lds如下:

 

[c-sharp] view plaincopy
 
  1. ENTRY(nomain)  
  2. SECTIONS  
  3. {  
  4.     . = 0x804800 + SIZEOF_HEADERS;  
  5.     tinytext : { *(.text) *(.data) *(.rodata) }  
  6.     /DISCARD/ : { *(.comment) }  
  7. }  

 

这是很简单的连接脚本,就是设置当前位置0x804800 + SIZEOF_HEADERS,后面紧跟着tinytext段,没有其他段了。使用入下命令连接

gcc –c  hello.c

ld –static – T hello.lds –o hello hello.o

OK,一个更小巧的HelloWorld完成了。

版本五:

版本四是最小的了吗?差远了。。。早就有人专门研究过最小的可执行文件了,从ELF文件的各个字节下手。。点这里:Size Is Everything 。。很牛B,很geek的东西。。理论上那就是最小的可执行文件了。

 

以上各个版本的helloworld大小,自己生成后用wc –c hello看吧:)。再配合objdump能学到更多~

推荐阅读