第一章 代码模式
1.1.方法
作者学习C/C++的时候曾经花了大量时间写出代码然后查看反汇编后的汇编语言代码,以此来搞清楚这些C/C++代码究竟有什么作用。这是一个非常好的学习方法。
作者学习汇编的时候,尝试把一些简单的C函数用汇编语言写出来,而且是越简短越好。这对于理解汇编语言非常有帮助。
编译器的优化选项。三种选项:①没有优化。②优化汇编代码,让代码运行得更快。③加入调试信息,包含了一些程序的信息。在反汇编的时候尽量debug版本和release版本都进行分析。
1.2.基础
1.2.1.CPU的简介
每个CPU都有自己的指令集ISA。
通用寄存器:x86有8个,x86-64有16个,ARM有16个。可以视作没有类型的临时变量。
不同的ISA:x86 ISA(变长指令)、ARM(RISC,定长指令,分为4 bytes的ARM mode和2bytes的Thumb mode,ARMv7有Thumb-2是前两者的混合,还有4 bytes的ARM64,所以总共是三种ARM)、PowerPC、MIPS、Alpha AXP(此三种都是RISC,指令长度固定为32bits)。
1.2.2.计数系统
十进制、二进制、十六进制。
八进制:chmod对用户、组、所有人的权限限制(读/写/执行)。
1.3.最简单的函数
int f()
{
return 123;
}
1.3.1.x86
f:
mov eax, 123
ret
1.3.2.ARM
f PROC
MOV r0, #0x7b; 123
BX lr
ENDP
ARM是用R0返回值的,所以把123复制到R0里,0x7b就是十六进制的123。返回地址不是存储在局部栈里,而是在link寄存器,所以BX LR指令让执行跳转到那个地址,可以高效地返回调用者。显然LR代指link register。
1.3.3.MIPS
在MIPS中有两种命名寄存器的约定,一是以数字命名(从$0到$31),一种是伪名称($V0, $A0等)。
j $31
li $2, 123
如果用IDA反汇编:
jr $ra
li $v0, 0x7B
$2和$v0用来存储函数的返回值。LI表示加载立即值(LoadImmediate),与MOV指令差不多。J或者JR是跳转指令,返回控制流给调用函数,跳转到放在$31或$RA里的地址。
注意由于分支延迟槽的关系,加载指令和跳转指令换了位置。
MIPS指令通常用小写。
总结:无论是哪种指令集,其思路都是一样的,用一个约定好的寄存器存放返回值,返回地址存放在某处,用跳转或返回指令跳转到这个返回地址。