Reverse Engineering for Beginners Chapter3学习记录

此逆向书是老外为初学者所写,全本完全免费提供下载。
官方主页: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可生成汇编文件

blob.png

打开汇编文件看看

blob.png

32位汇编还是比较好看懂的

还原一下就差不多是

#include<stdio.h>
const char $SG3830[]="hello, world";
int main()
{
printf($SG3830);
return 0;
}

linux下用gcc看看,下面我用的是64位系统编译的,其实是下一节的,不过没关系了

-S -masm=intel :生成Intel语法的汇编代

blob.png

	.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编译会出现两个字符串,

blob.png

作者说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的指令,哎不会就先过了吧

打赏作者
喜欢本博客,打赏让博客永久运行,多少你说了算

您的支持将鼓励我们继续创作!

[微信] 扫描二维码打赏

[支付宝] 扫描二维码打赏

发表评论

电子邮件地址不会被公开。 必填项已用*标注