不用 {}<>写一个Hello World程序,本方法基于gcc的编译器
C语言中的函数 在C语言中,一个函数其实可以看做一个变量,假设如今定义了如下函数
1 2 3 4 int fun () { return 0 ; }
则 &fun 将会像普通变量一样取得这个函数所在的地址
gcc下main函数调用机制 一个程序,其实并不是以main为开始,而是以start函数为开始
随便将一个ELF文件拖入ida可以看见
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 .text:00000000004006E0 _start proc near ; DATA XREF: LOAD:0000000000400018↑o .text:00000000004006E0 ; __unwind { .text:00000000004006E0 xor ebp, ebp .text:00000000004006E2 mov r9, rdx ; rtld_fini .text:00000000004006E5 pop rsi ; argc .text:00000000004006E6 mov rdx, rsp ; ubp_av .text:00000000004006E9 and rsp, 0FFFFFFFFFFFFFFF0h .text:00000000004006ED push rax .text:00000000004006EE push rsp ; stack_end .text:00000000004006EF mov r8, offset __libc_csu_fini ; fini .text:00000000004006F6 mov rcx, offset __libc_csu_init ; init .text:00000000004006FD mov rdi, offset main ; main .text:0000000000400704 call ___libc_start_main .text:0000000000400709 hlt .text:0000000000400709 ; } // starts at 4006E0 .text:0000000000400709 _start endp
在 .text:00000000004006FD 的位置,它将main这个变量的地址赋给rdi,即作为 ___libc_start_main 的第一个参数,随后该函数会调用main,即跳转到main这个变量上,执行上面的语句。
若是按照如下的方法编写代码
1 const char main[]="\x00\x01\x02\x03" ;
当执行 ___libc_start_main 后跳到main变量上时,就会执行 “\x00\x01\x02\x03” ,若是将汇编转为这个形式,则会执行这些汇编语句。同时,这样的语句有一种专业的叫法,叫shellcode。
注:之所以加const,首先这样可以防止\x00被编译器优化掉,其次是为了将这一段编译到 .rodata 段,这一个段默认是可以执行的。
但是为了通过这个题,还是需要更骚的操作,因为 [] 被禁用了。在gcc的实现中,相邻定义的变量,编译出来后,它们也是相邻的,所以上面的代码可以等价于
1 const char main=0x1 , main1=0x1 , main2=0x2 , main3=0x3 ;
这样一来就可以完美的符合题意。
shellcode 详情见 shellcode
一键生成代码 下面的代码均基于pwntools
x32 linux 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 from pwn import *def get_print_asm (s ): s1 = asm(''' xor eax, eax mov al, 0x4 xor ebx, ebx mov bl, 0x1 ''' ) s3 = asm (''' mov ecx, esp mov edx, %s int 0x80 ''' % hex (len (s))) hex_list = [] s2 = '' while len (s) >4 : hex_list.append(u32(s[:4 ])) s = s[4 :] s = s + '\x00' * (4 - len (s)) hex_list.append(u32(s)) hex_list.reverse() for each in hex_list: s2 += asm('push %s' % hex (each)) s4 = asm(''' xor eax, eax add esp, %s ret ''' % hex (len (hex_list) * 4 )) return s1 + s2 + s3 + s4 def main (): s = get_print_asm('Hello, World!' ) result = '' result += 'const char main=%s' % hex (ord (s[0 ])) for i in range (1 , len (s)): result += ', main%d=%s' % (i, hex (ord (s[i]))) result += ';' print result if __name__ == '__main__' : main()
x64 linux 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 from pwn import *context.arch = 'amd64' def get_print_asm (s ): s1 = asm(''' xor rax, rax mov al, 0x1 xor rdi, rdi mov dil, 0x1 ''' ) s3 = asm (''' mov rsi, rsp mov rdx, %s syscall ''' % hex (len (s))) hex_list = [] s2 = '' while len (s) >8 : hex_list.append(u64(s[:8 ])) s = s[8 :] s = s + '\x00' * (8 - len (s)) hex_list.append(u64(s)) hex_list.reverse() for each in hex_list: s2 += asm('mov rbx, %s' % hex (each)) s2 += asm('push rbx' ) s4 = asm(''' xor rax, rax add rsp, %s ret ''' % hex (len (hex_list) * 8 )) return s1 + s2 + s3 + s4 def main (): s = get_print_asm('Hello, World!' ) result = '' result += 'const char main=%s' % hex (ord (s[0 ])) for i in range (1 , len (s)): result += ', main%d=%s' % (i, hex (ord (s[i]))) result += ';' print result if __name__ == '__main__' : main()
附录