各种小巧的Hello World

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

版本一:

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

 

_start: call main movl $1, %eax xorl %ebx, %ebx int $0x80

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

版本二:

与版本一其实差不多,只是用shellcode来完成了,代码如下

typedef int (*sc_fun)(int,int,int,int,int,int,int); void _start(void) { char syscall[] = "/x60/x83/xc4/x24/x58/x5b/x59/x5a/x5e/x5f/x5d/xcd/x80/x83/xec/x40/x61/xc3"; ((sc_fun)syscall)(4, 0, "Hello, World/n", 13, 0, 0, 0); ((sc_fun)syscall)(1, 0, 0, 0, 0, 0, 0); }


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

版本三:

char *str = "Hello world!/n"; void print() { asm( "movl $13, %%edx /n/t" "movl %0, %%ecx /n/t" "movl $0, %%ebx /n/t" "movl $4, %%eax /n/t" "int $0x80 /n/t" :: "r"(str):"edx","ecx","ebx"); } void exit() { asm( "movl $42,%ebx /n/t" "movl $1,%eax /n/t" "int $0x80 /n/t"); } void nomain() { print(); exit(); }

关于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如下:

ENTRY(nomain) SECTIONS { . = 0x804800 + SIZEOF_HEADERS; tinytext : { *(.text) *(.data) *(.rodata) } /DISCARD/ : { *(.comment) } }

这是很简单的连接脚本,就是设置当前位置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能学到更多~

 

参考文献除开给出的连接,还有《程序员的自我修养》

OVER,转帖请注明出处

 

你可能感兴趣的:(c,linux,汇编,脚本,gcc,fun)