目录
此逆向书是老外为初学者所写,全本完全免费提供下载。
官方主页:http://yurichev.com/RE-book.html
3.1 x86 and 3.2 x86-64
代码
#include <stdio.h> int main() { printf("hello, world"); return 0; };
用msvc即cl编译,/Fa可生成汇编文件
打开汇编文件看看
32位汇编还是比较好看懂的
还原一下就差不多是
#include<stdio.h> const char $SG3830[]="hello, world"; int main() { printf($SG3830); return 0; }
linux下用gcc看看,下面我用的是64位系统编译的,其实是下一节的,不过没关系了
-S -masm=intel :生成Intel语法的汇编代
.file "helloword.c" .intel_syntax noprefix .section .rodata .LC0: .string "hello, world" .text .globl main .type main, @function 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, OFFSET FLAT:.LC0 mov eax, 0 call printf mov eax, 0 pop rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4" .section .note.GNU-stack,"",@progbits
也差不多,因为默认没开优化 ,返回值就用了mov eax,1
还有64位参数少可以用寄存器传参(头6个参数使用RDI,RSI,RDX,RCX,R8,R9来传递的,剩下的要靠栈。),这里用了edi
字串的指针被放到EDI(RDI的低32位部)。为什么不是64位寄存器RDI那?
这是一个重点,在64位模式下,对低32位进行操作的时候,会清空高32位的内容。比如 MOV EAX,011223344h将会把值写到RAX里,并且清空RAX的高32位区域。 如果我们打开编译好的对象文件(object file(.o)),我们会看到所有的指令:
gcc -S helloword.c -o 1_1
看看AT&T汇编又怎样
.file "helloword.c" .section .rodata .LC0: .string "hello, world" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $.LC0, %edi movl $0, %eax call printf movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4" .section .note.GNU-stack,"",@progbits
可以看到mov的位置是不一样的,刚好反过来
AT&T: 在寄存器名之前需要写一个百分号(%)并且在数字前面需要美元符($)。方括号被圆括号替代
指令多了q,l后缀,寄存器有%,常量有$前缀
q——-quad(四倍的意思),64位
l——-long 32位
w——-word 16位
b——-byte 8位
3.3 GCC one more thing
#include <stdio.h> int f1() { printf ("world\n"); } int f2() { printf ("hello world\n"); } int main() { f1(); f2(); }
cl编译会出现两个字符串,
作者说gcc则只出现一次,但实践后发现并没有
3.4 ARM
未进行代码优化的Keil编译:ARM模式
一时找不到他那个编译器啊~~~
.text:00000000 main .text:00000000 10 40 2D E9 STMFD SP!, {R4,LR} .text:00000004 1E 0E 8F E2 ADR R0, aHelloWorld ; "hello, world" .text:00000008 15 19 00 EB BL __2printf .text:0000000C 00 00 A0 E3 MOV R0, #0 .text:00000010 10 80 BD E8 LDMFD SP!, {R4,PC} .text:000001EC 68 65 6C 6C+aHelloWorld DCB "hello, world",0 ; DATA XREF: main+4
STMFD和LDMFD就相当于push和pop吧,不过这里可以搞多个寄存器入栈
ADR 就将PC寄存器+helloworld的偏移地址(应该是相对PC的偏移地址?)给R0寄存器
BL就相当于call,先将返回地址给LR寄存器,再printf的地址给PC寄存器
未进行代码优化的Keil编译: thumb模式
.text:00000000 main .text:00000000 10 B5 PUSH {R4,LR} .text:00000002 C0 A0 ADR R0, aHelloWorld ; "hello, world" .text:00000004 06 F0 2E F9 BL __2printf .text:00000008 00 20 MOVS R0, #0 .text:0000000A 10 BD POP {R4,PC} .text:00000304 68 65 6C 6C+aHelloWorld DCB "hello, world",0 ; DATA XREF: main+2
这里就差不多,用了push和pop
开启代码优化的Xcode(LLVM)编译: ARM模式
Listing2.11:Optimizing Xcode(LLVM)+ARM mode __text:000028C4 _hello_world __text:000028C4 80 40 2D E9 STMFD SP!, {R7,LR} __text:000028C8 86 06 01 E3 MOV R0, #0x1686 __text:000028CC 0D 70 A0 E1 MOV R7, SP __text:000028D0 00 00 40 E3 MOVT R0, #0 __text:000028D4 00 00 8F E0 ADD R0, PC, R0 __text:000028D8 C3 05 00 EB BL _puts __text:000028DC 00 00 A0 E3 MOV R0, #0 __text:000028E0 80 80 BD E8 LDMFD SP!, {R7,PC} __cstring:00003F62 48 65 6C 6C+aHelloWorld_0 DCB "Hello world!",0
movt R0,#0 是将R0高位置0,其实MOV R0, #0x1686 可以将高位置0 的,这里是多余的,编译器的问题?
用puts代替了printf,可能速度更快吧,没有格式化操作
那个R7是作者说是 a frame pointer,是栈基的意思吗
毕竟是全英,chapter 3 后面有点难看啊,特别是MIPS的指令,哎不会就先过了吧