Stay Hungry.Stay Foolish.
借助编译器阅读耗子叔6个变态的C语言Hello World程序

hello1.c

#define _________ }
#define ________ putchar
#define _______ main
#define _(a) ________(a);
#define ______ _______(){
#define __ ______ _(0x48)_(0x65)_(0x6C)_(0x6C)
#define ___ _(0x6F)_(0x2C)_(0x20)_(0x77)_(0x6F)
#define ____ _(0x72)_(0x6C)_(0x64)_(0x21)
#define _____ __ ___ ____ _________
#include<stdio.h>
_____

这段代码初步一看,有点晕,但是如果耐心下来仔细读读,从一个安全从业者的角度来说,其实就是最为初级的加密,定义了一堆宏进行替换,很容易就可以把代码还原解读了,但是有一种更加快速不烧脑的方法,就是汇编。

gcc -std=c99 -S -o hello1.s -O2 -masm=intel -m64 hello1.c

汇编之后的结果:

hello1.s

    .file   "2.c"
    .intel_syntax noprefix
    .text
    .globl  main
main:
.LFB0:
    .cfi_startproc
    push    rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    mov rbp, rsp
    .cfi_def_cfa_register 6
    mov edi, 72
    call    putchar
    mov edi, 101
    call    putchar
    mov edi, 108
    call    putchar
    mov edi, 108
    call    putchar
    mov edi, 111
    call    putchar
    mov edi, 44
    call    putchar
    mov edi, 32
    call    putchar
    mov edi, 119
    call    putchar
    mov edi, 111
    call    putchar
    mov edi, 114
    call    putchar
    mov edi, 108
    call    putchar
    mov edi, 100
    call    putchar
    mov edi, 33
    call    putchar
    mov eax, 0
    pop rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (GNU) 4.9.2 20150212 (Red Hat 4.9.2-6)"
    .section    .note.GNU-stack,"",@progbits

如果懂汇编,这很容易读,不懂的也没事,简单的说就是,把字符串”hello,world!”对应的ascii码逐一的送入edi寄存器,64位操作系统函数的前留给参数分别保存在rdi,rsi,rdx,rcx,r8,r9,之后才是栈,压入之后,调用putchar函数输出字符到stdout。

再来看hello2.c

#include<stdio.h>
int main(){
      int x=0,y[14],*z=&y;*(z++)=0x48;*(z++)=y[x++]+0x1D;
      *(z++)=y[x++]+0x07;*(z++)=y[x++]+0x00;*(z++)=y[x++]+0x03;
      *(z++)=y[x++]-0x43;*(z++)=y[x++]-0x0C;*(z++)=y[x++]+0x57;
      *(z++)=y[x++]-0x08;*(z++)=y[x++]+0x03;*(z++)=y[x++]-0x06;
      *(z++)=y[x++]-0x08;*(z++)=y[x++]-0x43;*(z++)=y[x]-0x21;
      x=*(--z);while(y[x]!=NULL)putchar(y[x++]);
 }

这一段代码没用使用宏,主要是靠运算来混淆,也是看的头晕,我一直感觉加一减一这种问题很容易导致出错。。如果依然使用上面的方法通过gcc的O0不优化得到的汇编代码:

hello2.s

    .file   "2.c"
    .intel_syntax noprefix
    .text
    .globl  main
    .type   main, [[@function](http://my.oschina.net/u/569418)](http://my.oschina.net/u/569418)
main:
.LFB0:
    .cfi_startproc
    push    rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    mov rbp, rsp
    .cfi_def_cfa_register 6
    sub rsp, 80
    mov DWORD PTR [rbp-4], 0
    lea rax, [rbp-80]
    mov QWORD PTR [rbp-16], rax
    mov rax, QWORD PTR [rbp-16]
    lea rdx, [rax+4]
    mov QWORD PTR [rbp-16], rdx
    mov DWORD PTR [rax], 72
    mov rax, QWORD PTR [rbp-16]
    lea rdx, [rax+4]
    mov QWORD PTR [rbp-16], rdx
    mov edx, DWORD PTR [rbp-4]
    lea ecx, [rdx+1]
    mov DWORD PTR [rbp-4], ecx
    movsx   rdx, edx
    mov edx, DWORD PTR [rbp-80+rdx*4]
    add edx, 29
    mov DWORD PTR [rax], edx
    mov rax, QWORD PTR [rbp-16]
    lea rdx, [rax+4]
    mov QWORD PTR [rbp-16], rdx
    mov edx, DWORD PTR [rbp-4]
    lea ecx, [rdx+1]
    mov DWORD PTR [rbp-4], ecx
    movsx   rdx, edx
    mov edx, DWORD PTR [rbp-80+rdx*4]
    add edx, 7
    mov DWORD PTR [rax], edx
    mov rax, QWORD PTR [rbp-16]
    lea rdx, [rax+4]
    mov QWORD PTR [rbp-16], rdx
    mov edx, DWORD PTR [rbp-4]
    lea ecx, [rdx+1]
    mov DWORD PTR [rbp-4], ecx
    movsx   rdx, edx
    mov edx, DWORD PTR [rbp-80+rdx*4]
    mov DWORD PTR [rax], edx
    mov rax, QWORD PTR [rbp-16]
    lea rdx, [rax+4]
    mov QWORD PTR [rbp-16], rdx
    mov edx, DWORD PTR [rbp-4]
    lea ecx, [rdx+1]
    mov DWORD PTR [rbp-4], ecx
    movsx   rdx, edx
    mov edx, DWORD PTR [rbp-80+rdx*4]
    add edx, 3
    mov DWORD PTR [rax], edx
    mov rax, QWORD PTR [rbp-16]
    lea rdx, [rax+4]
    mov QWORD PTR [rbp-16], rdx
    mov edx, DWORD PTR [rbp-4]
    lea ecx, [rdx+1]
    mov DWORD PTR [rbp-4], ecx
    movsx   rdx, edx
    mov edx, DWORD PTR [rbp-80+rdx*4]
    sub edx, 67
    mov DWORD PTR [rax], edx
    mov rax, QWORD PTR [rbp-16]
    lea rdx, [rax+4]
    mov QWORD PTR [rbp-16], rdx
    mov edx, DWORD PTR [rbp-4]
    lea ecx, [rdx+1]
    mov DWORD PTR [rbp-4], ecx
    movsx   rdx, edx
    mov edx, DWORD PTR [rbp-80+rdx*4]
    sub edx, 12
    mov DWORD PTR [rax], edx
    mov rax, QWORD PTR [rbp-16]
    lea rdx, [rax+4]
    mov QWORD PTR [rbp-16], rdx
    mov edx, DWORD PTR [rbp-4]
    lea ecx, [rdx+1]
    mov DWORD PTR [rbp-4], ecx
    movsx   rdx, edx
    mov edx, DWORD PTR [rbp-80+rdx*4]
    add edx, 87
    mov DWORD PTR [rax], edx
    mov rax, QWORD PTR [rbp-16]
    lea rdx, [rax+4]
    mov QWORD PTR [rbp-16], rdx
    mov edx, DWORD PTR [rbp-4]
    lea ecx, [rdx+1]
    mov DWORD PTR [rbp-4], ecx
    movsx   rdx, edx
    mov edx, DWORD PTR [rbp-80+rdx*4]
    sub edx, 8
    mov DWORD PTR [rax], edx
    mov rax, QWORD PTR [rbp-16]
    lea rdx, [rax+4]
    mov QWORD PTR [rbp-16], rdx
    mov edx, DWORD PTR [rbp-4]
    lea ecx, [rdx+1]
    mov DWORD PTR [rbp-4], ecx
    movsx   rdx, edx
    mov edx, DWORD PTR [rbp-80+rdx*4]
    add edx, 3
    mov DWORD PTR [rax], edx
    mov rax, QWORD PTR [rbp-16]
    lea rdx, [rax+4]
    mov QWORD PTR [rbp-16], rdx
    mov edx, DWORD PTR [rbp-4]
    lea ecx, [rdx+1]
    mov DWORD PTR [rbp-4], ecx
    movsx   rdx, edx
    mov edx, DWORD PTR [rbp-80+rdx*4]
    sub edx, 6
    mov DWORD PTR [rax], edx
    mov rax, QWORD PTR [rbp-16]
    lea rdx, [rax+4]
    mov QWORD PTR [rbp-16], rdx
    mov edx, DWORD PTR [rbp-4]
    lea ecx, [rdx+1]
    mov DWORD PTR [rbp-4], ecx
    movsx   rdx, edx
    mov edx, DWORD PTR [rbp-80+rdx*4]
    sub edx, 8
    mov DWORD PTR [rax], edx
    mov rax, QWORD PTR [rbp-16]
    lea rdx, [rax+4]
    mov QWORD PTR [rbp-16], rdx
    mov edx, DWORD PTR [rbp-4]
    lea ecx, [rdx+1]
    mov DWORD PTR [rbp-4], ecx
    movsx   rdx, edx
    mov edx, DWORD PTR [rbp-80+rdx*4]
    sub edx, 67
    mov DWORD PTR [rax], edx
    mov rax, QWORD PTR [rbp-16]
    lea rdx, [rax+4]
    mov QWORD PTR [rbp-16], rdx
    mov edx, DWORD PTR [rbp-4]
    movsx   rdx, edx
    mov edx, DWORD PTR [rbp-80+rdx*4]
    sub edx, 33
    mov DWORD PTR [rax], edx
    sub QWORD PTR [rbp-16], 4
    mov rax, QWORD PTR [rbp-16]
    mov eax, DWORD PTR [rax]
    mov DWORD PTR [rbp-4], eax
    jmp .L2
.L3:
    mov eax, DWORD PTR [rbp-4]
    lea edx, [rax+1]
    mov DWORD PTR [rbp-4], edx
    cdqe
    mov eax, DWORD PTR [rbp-80+rax*4]
    mov edi, eax
    call    putchar
.L2:
    mov eax, DWORD PTR [rbp-4]
    cdqe
    mov eax, DWORD PTR [rbp-80+rax*4]
    cdqe
    test    rax, rax
    jne .L3
    mov eax, 0
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (GNU) 4.9.2 20150212 (Red Hat 4.9.2-6)"
    .section    .note.GNU-stack,"",@progbits

可以看到,挺难读的。。其实可以使用O2优化,执行:

gcc -std=c99 -S -o hello2.s -O2 -masm=intel -m64 hello2.c
    .file   "2.c"
    .intel_syntax noprefix
    .section    .text.unlikely,"ax",@progbits
.LCOLDB0:
    .section    .text.startup,"ax",@progbits
.LHOTB0:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB3:
    .cfi_startproc
    push    rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    push    rbx
    .cfi_def_cfa_offset 24
    .cfi_offset 3, -24
    mov edi, 72
    mov ebx, 101
    sub rsp, 72
    .cfi_def_cfa_offset 96
    mov DWORD PTR [rsp], 72
    mov DWORD PTR [rsp+4], 101
    lea rbp, [rsp+8]
    mov DWORD PTR [rsp+8], 108
    mov DWORD PTR [rsp+12], 108
    mov DWORD PTR [rsp+16], 111
    mov DWORD PTR [rsp+20], 44
    mov DWORD PTR [rsp+24], 32
    mov DWORD PTR [rsp+28], 119
    mov DWORD PTR [rsp+32], 111
    mov DWORD PTR [rsp+36], 114
    mov DWORD PTR [rsp+40], 108
    mov DWORD PTR [rsp+44], 100
    mov DWORD PTR [rsp+48], 33
    mov DWORD PTR [rsp+52], 0
    jmp .L2
    .p2align 4,,10
    .p2align 3
.L7:
    mov eax, DWORD PTR [rbp+0]
    mov edi, ebx
    add rbp, 4
    mov ebx, eax
.L2:
    mov rsi, QWORD PTR stdout[rip]
    call    _IO_putc
    test    ebx, ebx
    jne .L7
    add rsp, 72
    .cfi_def_cfa_offset 24
    xor eax, eax
    pop rbx
    .cfi_def_cfa_offset 16
    pop rbp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE3:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE0:
    .section    .text.startup
.LHOTE0:
    .ident  "GCC: (GNU) 4.9.2 20150212 (Red Hat 4.9.2-6)"
    .section    .note.GNU-stack,"",@progbits

这次代码量少了很多,好读多了,大概意思就是把hello,world!的ascii码送入rsp栈中,之后跳到L2,L2中逻辑是先调用系统调用_IO_PUTC输入rdi中的字符到stdout中,之后使用test指令,测试ebx是否为零,jnz和jne效果等同,根据ZF标志位来跳转,如果不为零,跳转到L7,直到遇到字符串结尾字符’\0’,ZF=0,jne .L7不成立往下执行程序结束。

其余的4段就不解析了,差不多。

自由转载-非商用-非衍生-保持署名(创意共享3.0许可证
评论

暂无评论~~