首页
社区
课程
招聘
[原创]逆向学习笔记之汇编
2021-4-15 11:17 8242

[原创]逆向学习笔记之汇编

2021-4-15 11:17
8242

初学逆向,学习滴水的课程整理出的笔记,不足之处敬请各位老师傅指正
十六进制可看作二进制的一种简写形式

数据宽度

计算机中数据有长度限制,超过最高宽度的数据会被丢弃。

1
2
3
4
1.位(bit):一个01
2.字节(byte):8
3.字(word):16
4.双字(doubleword):32

存储范围

1
2
3
1.字节:0~0xFF
2.字:0~0xFFFF
3.双字:0~0xFFFFFFFF

有符号数和无符号数

无符号数的编码规则

无符号数没有负数,全为正数,这个数是多少就存多少
例:1001 1010=0x9A

有符号数的编码规则

若有符号数最高位是0时为正数,最高位是1时为负数。

1
2
3
1.原码:最高位为符号位,其余各位为数值本身的绝对值
2.反码:正数:反码与原码相同。负数:符号位为1,其余位对原码取反
3.补码:正数:补码与原码相同。负数:符号位为1,其余位对原码取反后加1

总结:正数原码存储,负数补码存储。

这个圆,从下面的点开始,逆时针数右半圆为从0到127,顺时针数左半圆为-1到-128
对于一个字节
无符号数:0~FF
有符号数:正数:0~7F,负数:FF~80(因为正数最高位必为0,负数最高位必为1)

计算机运算

位运算

1
2
3
4
5
6
7
8
与运算(and &):两个位都为1时,结果才为1
或运算(or |):只要有一个为1就是1
异或运算(xor ^):不一样的时候是1
非运算(not ~):0就是11就是0
左移(shl <<):各二进制位全部左移若干位,高位丢弃,低位补0
右移:各二进制位全部右移若干位,低位丢弃,高位补0或者补符号位
    汇编:shr--高位补0,sar--高位补符号位
    C语言:>>,通过数据类型判断,无符号数补0,有符号数补符号位

四则运算

加法

1
2
3
4
5
1.异或运算(无进位时结果与按位加一样)
2.与运算(判断是否有进位,因为只有两个1才会进位)
3.异或运算(2的结果左移1位后和1的结果进行异或,左移是因为2判断了哪里需要进位)
4.与运算(再次判断是否有进位)
5.如此重复直到与运算的结果为0,最终结果即为最后一次异或运算的结果

减法

1
减法就是加法,例如4-5=4+-5

乘法

1
乘法本质就是循环的加法

除法

1
除法的本质是减法,就是看一个数能减去一个数多少次

寄存器

寄存器就是CPU内的存储器(cache)
存储格式:
32位CPU:8 16 32
64位CPU:8 16 32 64

通用寄存器

1
2
3
4
5
6
7
8
9
10
16位是32位的低位,两个8位组成一个16位。
32位   16位    8
EAX    AX    AH+AL
ECX    CX    CH+CL
EDX    DX    DH+DL
EBX    BX    BH+BL
ESP    SP    
EBP    BP    
ESI    SI    
EDI    DI

MOV指令

1
2
1.把数存到寄存器。例:MOV EAX,1
2.把寄存器的值存到另一寄存器。例:MOV EDX,EAX(注意是后面的值赋给前面)

内存

内存地址

每个内存地址对应1Byte,内存地址大小为32位,前面的0可以省略,表示时用十六进制。
每个应用程序都有独立的4GB空间就是这个大小,但是这并非是完全可用的,使用前需要先申请。
从0x00000000到0xFFFFFFFF

MOV指令

1
2
3
4
5
6
7
注意前后数据宽度一致,大于一个字节的数据是从给的编号开始,向后存储。
1.数到内存,例如:MOV BYTE PTR DS:[0018FFF0],1  注:内存空间是要被申请的才行
2.寄存器到内存,例如:MOV DWORD PTR DS:[0018FFFC],EAX  注:EAX是4个字节(DWORD),AX是2个字节(WORD),AL是1个字节(BYTE)
3.内存到寄存器,例如:MOV EAX,DWORD PTR DS:[0018FFF8]
4.MOV WORD PTR DS:[xxxxxxxx],1
在这个命令中,WORD PTR的作用是指明了要访问的内存单元的大小为2字节
DS为内存单元的段地址,因为命令中的内存地址实质上是偏移地址,仅靠偏移地址无法读取,还需要段地址。

汇编中绝大多数指令不允许从内存到内存。

内存地址的五种形式

1
2
3
4
5
6
前面MOV指令中[]的内容
1.立即数,例如:0x13FFC4
2.reg,reg为8个通用寄存器中的任意一个,例如:EAX,此时就是把EAX中的数据当做内存地址
3.reg+立即数,例如:EAX+4
4.reg+reg*{1,2,4,8},数组赋值的汇编一般是这种形式,例如:EAX+ECX*4
5.reg+reg*{1,2,4,8}+立即数,例如:EAX+ECX*4+4

数据的存储模式

1
2
3
大端模式:数据高位存低位,数据低位存高位
小端模式:数据高位存高位,数据低位存低位
X86大多采用小端模式存储,ARM大多采用大端模式存储

DTdebug命令

1
2
3
db/dw/dd xxxxxxxx
db是一个字节为单位查看,dw是两个字节为单位查看,dd是四个字节为单位查看。后跟内存地址
注意:想查看存储模式时,要用db,直接用dd看不出来

常用汇编指令

MOV指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
r代表通用寄存器,m代表内存,imm代表立即数,即r8代表8位通用寄存器,m8代表8位内存,imm8代表8位立即数
MOV 目标操作数,源操作数
MOV r/m8,r8
MOV r/m16,r16
MOV r/m32,r32
MOV r8,r/m8
MOV r16,r/m16
MOV r32,r/m32
MOV r8,imm8
MOV r16,imm16
MOV r16,imm32
源操作数:立即数,通用寄存器,段寄存器,内存单元
目标操作数:通用寄存器,段寄存器,内存单元
二者数据宽度要保持一致且不能同时为内存单元

ADD指令

1
2
3
4
5
6
7
8
9
10
11
12
加法指令,最后结果存在前者中
ADD r/m8,imm8
ADD r/m16,imm16
ADD r/m32,imm32
ADD r/m16,imm8
ADD r/m32,imm8
ADD r/m8,r8
ADD r/m16,r16
ADD r/m32,r32
ADD r8,r/m8
ADD r16,r/m16
ADD r32,r/m32

SUB指令

1
2
3
4
5
6
7
8
9
10
11
12
减法指令,前者减去后者,结果存入前者
SUB r/m8,imm8
SUB r/m16,imm16
SUB r/m32,imm32
SUB r/m16,imm8
SUB r/m32,imm8
SUB r/m8,r8
SUB r/m16,r16
SUB r/m32,r32
SUB r8,r/m8
SUB r16,r/m16
SUB r32,r/m32

AND指令

1
2
3
4
5
6
7
8
9
10
11
12
与运算,结果保存到前者
AND r/m8,imm8
AND r/m16,imm16
AND r/m32,imm32
AND r/m16,imm8
AND r/m32,imm8
AND r/m8,r8
AND r/m16,r16
AND r/m32,r32
AND r8,r/m8
AND r16,r/m16
AND r32,r/m32

OR指令

1
2
3
4
5
6
7
8
9
10
11
12
或运算,结果保留到前者
OR r/m8,imm8
OR r/m16,imm16
OR r/m32,imm32
OR r/m16,imm8
OR r/m32,imm8
OR r/m8,r8
OR r/m16,r16
OR r/m32,r32
OR r8,r/m8
OR r16,r/m16
OR r32,r/m32

XOR指令

1
2
3
4
5
6
7
8
9
10
11
12
异或运算,结果保留到前者
XOR r/m8,imm8
XOR r/m16,imm16
XOR r/m32,imm32
XOR r/m16,imm8
XOR r/m32,imm8
XOR r/m8,r8
XOR r/m16,r16
XOR r/m32,r32
XOR r8,r/m8
XOR r16,r/m16
XOR r32,r/m32

NOT指令

1
2
3
4
非运算
NOT r/m8
NOT r/m16
NOT r/m32

MOVS指令

1
2
3
4
内存与内存间移动数据,使用ESI和EDI寄存器,其中保存的是两个需要操作的内存地址。每次执行后,ESI与EDI都会改变。此改变是加还是减取决于DF为是0还是1,加减数取决于指令操作位数。
MOVS BYTE PTR ES:[EDI],BYTE PTR ES:[ESI] 简写为:MOVSB
MOVS WORD PTR ES:[EDI],WORD PTR ES:[ESI] 简写为:MOVSW
MOVS DWORD PTR ES:[EDI],DWORD PTR ES:[ESI] 简写为:MOVSD

STOS指令

1
2
3
4
将AL/AX/EAX中的值存储到[EDI]指定的内存单元
STOS BYTE PTR ES:[EDI] 简写为:STOSB,对应AL
STOS WORD PTR ES:[EDI] 简写为:STOSW,对应AX
STOS DWORD PTR ES:[EDI] 简写为:STOSD,对应EAX

REP指令

1
2
3
4
按计数寄存器(ECX)中指定的次数重复执行字符串命令
例如:
MOV ECX,10
REP MOVSD

堆栈

什么是堆栈?

1
2
程序执行的过程中由操作系统分配可使用的一块内存,要是用超了就是堆栈溢出。
ESP寄存器被称为栈指针寄存器,存储了当前的堆栈用到了哪里。

堆栈的使用

1
堆栈使用时是大地址往小地址用,每次使用后要修改栈顶指针ESP寄存器,防止数据被顶掉

PUSH指令

1
2
3
4
5
6
7
8
向堆栈压入数据,修改栈顶指针ESP寄存器(减)
PUSH imm8/imm16/imm32
PUSH r16/r32
PUSH m16/m32
 
例如:PUSH EAX等于
SUB ESP,4
MOV DWORD PTR DS:[ESP],EAX

POP指令

1
2
3
4
5
6
7
将栈顶数据存到寄存器或内存,修改栈顶指针ESP寄存器(加)
POP r16/r32
POP m16/m32
 
例如:POP EAX等于
MOV EAX,DWORD PTR DS:[ESP]
ADD ESP,4

修改EIP的指令

EIP中记录的是CPU下一次要执行的地址,不可用普通的MOV指令修改

JMP指令

1
2
修改EIP的值
JMP imm32/r32/m32

CALL指令

1
2
修改EIP的值,并把当前指令的下一行地址存入堆栈中(修改了堆栈,所以ESP的值也会改动)。
CALL imm32/r32/m32

RET指令

1
2
3
4
本质上就是
ADD ESP,4
MOV EIP,[ESP-4]
RET后面可以跟一个imm,表示ESP加上这个imm,即在函数返回后再修改一次栈顶指针寄存器,用途为释放函数数据占用的堆栈。

反调试

1
2
单步步入(F7)单步步过(F8)
CALL指令时F7是一步一步执行,F8是全执行完

调试器实现原理

1
2
3
4
5
6
7
8
断点:0xCC
单步步入:设置EFLAGS的TF位
单步步过:在下一行设置断点,即无论你CALL有多少指令,最后都要停在CALL的下一行
小小的一个反调试的方法(仅对单步步过有用):
在CALL跳转的位置写:
MOV DWORD PTR DS:[ESP],004183D7(内存地址随意)
RET
这样的话,相当于修改了栈顶的值,使得RET返回时不会回到CALL的下一行,而是跳转到你输入的那个内存地址。

反调试思路

1
写大量的CALL,大量的无意义代码,使得单步步入非常消耗耐心。

汇编眼中的函数

什么是函数?

函数就是一系列指令的集合,为了完成某个会重复使用的特定功能
调用函数:JMP和CALL指令,一般用CALL,因为用RET就能很方便的回来
参数:保存到堆栈,一般调用函数完的返回值保存到EAX

堆栈平衡

1
2
1.如果要返回父程序,则当我们在堆栈中进行堆栈的操作时,一定要保证在RET这条指令之前,ESP指向的是我们压入栈的地址,即RET能正确返回。
2.如果通过堆栈传递参数了,那么在函数执行完毕后,要平衡常数导致的堆栈变化。例如:先向堆栈压入了函数所用的数据,但是函数执行完成后却没有释放数据占用的堆栈,浪费空间。

ESP寻址

1
2
3
4
5
ESP--栈顶指针寄存器,存储了堆栈现在用到的地方
优点:调用方便
缺点:如果函数同时使用多个寄存器,则寄存器的值要保存入栈,此时ESP的值会发生改变
所以在读取堆栈中所需数据时,要灵活使用ESP+4,8,16
最后注意堆栈平衡!

EBP寻址

1
2
3
4
5
6
7
8
9
10
11
12
EBP--栈底指针寄存器
具体为在进入函数前让EBP的值等于ESP,这样后续存入的数据影响ESP的值
但是我们使用未受影响的EBP进行寻址,同时让ESP减去一个自然数,相当于开辟了一块空间专供此函数使用
PUSH EBP
MOV EBP,ESP
SUB ESP,10
XXXXXXXXXXXXXXXXX(函数内容)
MOV ESP,EBP
POP EBP
RET
优点:EBP不会随着你向堆栈添加数据而改变,位置相对固定。
注意:最后也要堆栈平衡。

标志寄存器(EFL)

几个重要的标志寄存器

  • 进位标志寄存器(CF carry Flag):如果运算结果最高位产生了一个进位或借位,那么其值位1,否则为0,这个标志通常用来指示无符号整型运算的溢出状态

  • 奇偶标志寄存器(PF Parity Flag):奇偶编制PF用于反映运算结果中“1”的个数的奇偶性,“1”为偶数个则该位置为1,奇数个则为0。通常用于传输数据过程中的奇偶校验。

  • 辅助进位标志寄存器(AF Auxiliary Carry Flag)

    • 一般用于BCD运算中
    • 如果算数操作在结果的第三位发生进位或者错位,则辅助进位标志AF的值
      被置为1,否则其值为0:
  • 零标志寄存器(ZF Zero Flag):零标志ZF用来反映运算结果是否为0。如果运算结果为0,则其值为0。在判断运算结果是否为0时,可使用此标志位。(经常与CMP或TEST等指令一起使用)

    • 附:CMP指令相当于SUB指令,但结果仅修改标志寄存器而不把结果保存到第一个操作数中,常用与判断两个值是否相同。TEST指令相当于AND指令,但结果仅修改标志寄存器而不把结果保存到第一个操作数中,常用与判断某个值是否为0.
  • 符号标志寄存器(SF Sign Flag):符号标志SF用来反映运算结果的符号位,它与运算结果的最高位相同

  • 溢出标志寄存器(OF Overflow Flag):溢出标志OF用于反映有符号数加减运算所得结果是否溢出。如果运算结果超过当前运算为数所能表示的范围,则成为溢出,OF的值被置为1,否则,OF的值被清为0

    • 最高位进位与溢出的区别:
      • 进位标志表示无符号数运算结果是否超出范围
      • 溢出标志表示有符号位运算结果是否超出范围
      • 溢出主要给有符号运算使用的,在有符号的运算中,有如下规律:
        • 正 + 正 = 正,如果结果为负数,则说明有溢出
        • 负 + 负 = 负,如果结果为正数,则说明有溢出
        • 正 + 负 永远都不会有溢出
        • 无符号、有符号都不溢出
  • 方向标志寄存器(DF Direction Flag):设置DF标志使得串指令自动递减(从高地址向低地址方向处理字符串),清除该标志则使得串指令自动递增。涉及指令:MOVS,CMPS,SCAS,LODS,STOS。STD及CLD指令分别用于设置及清除DF标志。

JCC指令


[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

最后于 2021-4-17 07:55 被PlumpBoy编辑 ,原因: 错别字
收藏
点赞5
打赏
分享
最新回复 (2)
雪    币: 93
活跃值: (1483)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wumnkx 2021-4-16 12:49
2
0
总结得比较好
雪    币: 1022
活跃值: (292)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
longbbyl 2021-4-28 14:22
3
0
收藏学习,努力中
游客
登录 | 注册 方可回帖
返回