不用[](){}<>写一个Hello World程序

不用{}<>写一个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 shellcode2c(shellcode):

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 shellcode2c(shellcode):

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()

附录