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

Posted    on 2017, Oct5, Thursday 03:52:29
Modified on 2017, Oct5, Thursday 03:52:33

不用<>写一个Hello World程序,本方法基于gcc的编译器

C语言中的函数

在C语言中,一个函数其实可以看做一个变量,假设如今定义了如下函数

int fun()
{
    return 0;
}

&fun 将会像普通变量一样取得这个函数所在的地址

gcc下main函数调用机制

一个程序,其实并不是以main为开始,而是以start函数为开始

随便将一个ELF文件拖入ida可以看见

.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这个变量上,执行上面的语句。

若是按照如下的方法编写代码

const char main[]="\x00\x01\x02\x03";

当执行 ___libc_start_main 后跳到main变量上时,就会执行 “\x00\x01\x02\x03” ,若是将汇编转为这个形式,则会执行这些汇编语句。同时,这样的语句有一种专业的叫法,叫shellcode。

注:之所以加const,首先这样可以防止\x00被编译器优化掉,其次是为了将这一段编译到 .rodata 段,这一个段默认是可以执行的。

但是为了通过这个题,还是需要更骚的操作,因为 [] 被禁用了。在gcc的实现中,相邻定义的变量,编译出来后,它们也是相邻的,所以上面的代码可以等价于

const char main=0x1, main1=0x1, main2=0x2, main3=0x3;

这样一来就可以完美的符合题意。

shellcode

详情见 shellcode

一键生成代码

下面的代码均基于pwntools

x32 linux

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

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

附录