Reserve_1
在学习pwn的过程中难免会遇到形形色色的有关逆向的问题,且二进制与逆向互通性相对来说比较高,也可以说逆向是二进制的铺垫,因此,在此栏专门记录自己学习逆向的过程,争取两天一更(但愿自己不会成为鸽子)。
汇编
指令
功能 | 命令 | 功能 | 命令 |
---|---|---|---|
加法 | add | 与 | and |
减法 | sub | 或 | or |
乘法 | mul/imul | 非 | not |
除法 | div/idiv | 异或 | xor |
比较大小 | cmp | 测试 | test |
调用 | call | 返回 | ret |
系统调用 | sysenter/syscall | 中断返回 | iret |
系统调用返回 | sysexit/sysret | ||
压栈 | push | 寄存器批量入栈 | pushad |
出栈 | pop | 寄存器批量出栈 | popad |
普通赋值 | mov | 单字节赋值 | movsb |
双字节赋值 | movsw | 四字节赋值 | movsd |
取地址 | lea |
跳转类型的指令
功能 | 命令 | 功能 | 命令 |
---|---|---|---|
等于转移 | JE/JZ | 不等于时转移 | JNE/JNZ |
有进位时转移 | JC | 无进位时转移 | JNC |
不溢出时转移 | JNO | 奇偶性为奇数时转移 | JNP/JPO |
符号位为"0"时转移 | JNS | 溢出转移 | JO |
奇偶性为偶数时转移 | JP/JPE | 符号位为"1"时转移 | JS |
功能 | 命令 | 功能 | 命令 |
---|---|---|---|
大于转移 | JA/JNBE | 大于或等于转移 | JAE/JNB |
小于转移 | JB/JNAE | 小于或等于转移 | JBE/JNA |
以上四条,测试无符号整数运算的结果(标志C和Z).
功能 | 命令 | 功能 | 命令 |
---|---|---|---|
大于转移 | JG/JNLE | 大于或等于转移 | JGE/JNL |
小于转移 | JL/JNGE | 小于或等于转移 | JLE/JNG |
以上四条,测试带符号整数运算的结果(标志S,O和Z).
寄存器
通用寄存器
8bit | 16bit | 32bit | 64bit | 功能 |
---|---|---|---|---|
AL | AH/AX | EAX | RAX | 累加器(Extended Accumulator)乘除指令默认用EAX |
CL | CH/CX | ECX | RCX | 循环计数器 |
DL | DH/DX | EDX | RDX | I/O指针 |
BL | BH/BX | EBX | RBX | DS段的数据指针 |
SP | ESP | RSP | 堆栈指针(Extended Stack Poniter) | |
BP | EBP | RBP | 用于保存当前堆栈帧地址的堆栈基指针 | |
SI | ESI | RSI | 字符串操作的源指针;SS段的数据指针(Extended Source Index) | |
DI | EDI | RDI | 字符串操作的目标指针;ES段的数据指针(Extended Destinantion Index) | |
IP | EIP | RIP | 指令指针。保存程序计数器和下一条指令的地址。 |
段寄存器
register | 功能 |
---|---|
(Code Segment)代码段寄存器 | |
(Data Segment)数据段寄存器 | |
(Stack Segment)堆栈段寄存器 | |
ES(FS/GS) | (Extra Segment)附加段寄存器 |
注意: CS,DS,SS在调试方面基本上已经失去意义
标志寄存器
运算结果标志位
FLAGS寄存器是cpu8086处理器中的一种标志寄存器,和我们常见的其他寄存器相比有如下特点
- 每一个标志寄存器的大小都只有一个字节,(基本无法存储任何数据,除了0和1)
- 其内容会收到cpu运行的指令的影响,在特定指令运行后,有一个或几个flag的值更具运行结果发生改变
- 会对一些条件判断指令产生影响
FLAGS寄存器的格式如下
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
OF | DF | IF | TF | SF | ZF | AF | PF | CF |
CF(进位标志)
FLAGS的0位是CF,进位标志位
当无符号数在运算中发生进位,或者向前借位时,cpu将其置1。
- 如0x0f + 0x01=0x10,这时就发生了进位,CF被置1。
- 在减法中0x10-0x01=0xE,这里就发生借位(这里使用的是十六进制)
虽然加法和减法中一个看的是进位,一个看的是借位,但其实两者是一个道理,记录的都是进位。
我们不妨把减法看成一个正数加上一个负数,并换算成补码。0x01的补码是0xff,这个数加上0x10必定会发生进位,因此,CF置1。
这也解释了为什么,CF叫进位标志而不是借位标志。其实减法本质上也可以看成加法
PF(奇偶标志)
FLAGS的2位是PF,奇偶标志位。
PF的主要职责就是判断,指令执行的结果的所有位数中1的个数是奇数还是偶数,如果是偶数,置1,如果是奇数,置0。
这里的结果主要包括:加、减、乘、除运算,和逻辑运算等。规则如下
1 | mov ax, 0x1 |
AF(调整标志位)
FLAGS的第4位是AF标志位,调整标志位。
其主要作用是判断低半字节有没有向高半字节进位或者借位,如果有AF=1,否则AF=0。
低半字节和高半字节:
我们进行数据计算时,表示一个数的位数都是有限的,如ax寄存器位16位,而这16位又可以分为高8位和低八位,这里的高8位就是高半字节,低8位就是低半字节。
当我们用ax寄存器进行运算时,如果低8位向高8位有进位或借位的情况,AF=1,否则AF=0
ZF(零标志位)
FLAGS第6位是ZF,称为零标志位。
顾名思义,ZF标志位的作用就是判断运算结果是否为0,如果为0,ZF=1,否则ZF=0。
如下
1 | mov al, 0x1 |
al=0,zf=1
结果可以来自:加减乘除,和逻辑运算。也包括CMP运算。
SF(符号标志位)
SF,判断指令执行的结果是否为负,如果为负,SF=1,否则SF=0。
例如:
1 | mov al, 0xF1 |
这里al的结果是0xF2,换算成二进制11110010b,-14,SF=1
这看起来“好像”是,当结果为负时,SF置1,但如果我们把这个运算看成是无符号原算时会怎么样?
这时al的结果是,242,但这时SF还是置1,这看起来好像是个问题?
在指令执行过程中,CPU是无法判断一个数是正数还是负数的,它所能判断的只有,结果的最高位是否为1,如果为1无论是有符号运算还是无符号运算,SF都会置1。
所以,最终的结果就是,当我们把运算看成有符号运算时SF有意义,而当成无符号运算时SF没有意义
当然,如果说一定要把两者区分,那我们该怎么做呢,我的答案是,没有必要,因为在进行无符号原算时,SF根本用不到,只有在进行有符号运算时,SF才可能会用到。
OF溢出标志位
顾名思义,OF标志位判断的就是,运算结果是否溢出。如果溢出OF=1,否则OF=0。
OF的判断公式为:OF=Cn 异或 Cn-1
Cn:最高有效位进位
Cn-1:此稿有效位进位
如何定义溢出
1 | mov al, 0x70 |
从二进制的角度来看,01110000 + 01110000 = 11100000,两个整数相加,变成了一个负数,这就是溢出。
最高有效位没有进位,即Cn=0,而次高有效位进位,即Cn-1=1,因此,CF=1。
这里CPU用于判断,有符号运算的结果是否溢出的方法非常巧妙。不止两正数相加为负的情况,两负数相加为正的情况也同样可以判断出来。
如:
1 | mov al, 0x90 |
10010000+10010000=0010 0000,两个负数计算后变成了一个正数,溢出
Cn=1,Cn-1=0,CF=0,和实际情况完美对应。
1 | mov al, 1h |
状态控制标志位
TF(跟踪标志位)
flag的第8位是TF,跟踪标志位用于标识CPU是否允许单步中断,以进行程序调试。TF=0时,8086CPU处于正常状态;TF=1时,8086CPU处于单步状态,每执行一条指令就自动产生一次单步中断。
8086的debug功能依赖于8086CPU的单步调试功能。
IF(中断允许标志位)
flags的第9位是IF,中断允许标志位
IF置为0,禁止其他的可屏蔽中断;如果允许处理可屏蔽中断,则将IF置为1。
这是一个可以有用户控制的标志位,控制指令没有操作数
1 | STI ;将IF设置为1,允许可屏蔽中断。 |
DF(方向标志位)
flags的第10为,是方向标志位。可以控制数据读取的方向
这个标志位主要用于配合movsb,movsw,进行批量数据传输。
和IF标志位一样,其值可以由用户控制
1 | cld ;OF置0,进行正向传递,从低地址向高地址读 |
使用方式如下
1 | ;显示Label Offset: |
PE文件
全称Portable Executable
是基于Unix的coff文件发展来的,是可移植的执⾏体,是Windows平台组织程序代码的⼀种⽂件格式。常⻅的exe、dll、
sys、ocx、com都属于PE⽂件。
DOS头
DOS头为0x4D 5A也就是英文大写MZ
一般可以作为搜索内存PE文件的特征
PE头(NT头)
文件开头为0x50 45 00 00也就是ASCII字符的PE
导入导出表
1 | typedef struct _ IMAGE_OPTIONAL_ HEADER { |
SectionAlignment
内存中加载的节的对齐方式(以字节为单位)。 此值必须大于或
等于 FileAlignment 成员。 默认值是系统的页大小。
FileAlignment
图像文件中各部分的原始数据的对齐方式(以字节为单位)。 该
值应为介于 512 和 64K 之间的幂 ((含) )。 默认值为 512。 如
果 SectionAlignment 成员小于系统页面大小,则此成员必须与
SectionAlignment 相同。