数据宽度

1、定义

数学上的数字,是没有大小限制的,可以无限的大。但在计算机中,由于受硬件的制约,数据都是有长度限制的(我们称为数据宽度),超过最多宽度的数据会被丢弃

2、计算机中常见的数据宽度

1)位(BIT): 0 //1

2)字节(Byte): 00000000 //8

3)字(Word): 0000000000000000 //16

4)双字(Doubleword):00000000000000000000000000000000 //32

3、存储范围

字节(Byte): 0 ~ 0xFF

字(Word): 0 ~ 0xFFFF

双字(Doubleword): 0 ~ 0xFFFFFFFF

无符号数与有符号数

1、无符号数的编码规则

1 0 0 1 1 0 1 0

0x9A
154

2、有符号数的正数编码规则

0 0 0 1 1 0 1 0

有符号数的首位为符号位,0为正数,1为负数
0x1A
26

原码反码补码

1、有符号数的编码规则

  • 原码:最高位为符号位,其余各位为数值本身的绝对值
  • 反码:
    • ​正数:反码与原码相同
    • 负数:符号位为1,其余位对原码取反
  • 补码:
    • 正数:补码与原码相同
    • 复数:符号位为1,其余位对原码取反+1

2、举例说明

-1
​1 0 0 0 0 0 0 1 原码
​1 1 1 1 1 1 1 0 反码
​1 1 1 1 1 1 1 1 补码 //FF

-7
​1 0 0 0 0 1 1 1 原码
​1 1 1 1 1 0 0 0 反码
​1 1 1 1 1 0 0 1 补码 //F9

3、总结

1)正数原(补)码存储
2)负数补码存储

  • 假设数据宽度为1 BYTE(8 BIT)
    无符号数:0 1 2 3 … FF(10进制255)
    有符号数:
  • 正数:0 … 7F
  • ​负数:FF … 80

计算机运算

1、与运算

两个位都为1时,结果才为1
例如
​1011 0001
1101 1000 and(&)
-—————
​1001 0000

2、或运算

只要有一个为1就是1
例如
​1011 0001
1101 1000 or( | )
-—————
​1111 1001

3、异或运算

不一样时为1
例如
​1011 0001
1101 1000 xor(^)
-—————
​0110 1001

4、非运算

0就是1 1就是0
例如
1101 1000 not(~)
-—————
​0010 0111

5、左移

各二进制位全部左移若干位,高位丢弃,低位补0
例如
shl(<<) 1101 1000 左移2位:0110 0000

6、右移

各二进制位全部右移若干位,低位丢弃,高位补0或符号位
例如

shr(>>) 1101 0101 右移2位补0:0011 0101
C语言:

1
2
unsigned int a=10;
printf("%d\n",a>>2);

sar(>>) 1101 0101 右移2位补符号位:1111 0101
C语言:

1
2
int a=10;
printf("%d\n",a>>2);

7、总结

计算机只会做位运算

计算机加法

4+5=?的运算过程
​0000 0100
​0000 0101
-——–加
​0000 1001

计算机无法做加法,只能进行位运算:

1)异或
​0000 0100
0000 0101 xor(^)
-————–
​0000 0001

2)判断是否有进位
​0000 0100
0000 0101 and(&)
-—————
​0000 0100

3)继续异或
​0000 0001
0000 1000 xor(^) //and运算后的结果shl一位
-—————
​0000 1001

4)判断是否有进位
​0000 0001
0000 1000 and(&)
-—————
​0000 0000

结果:0000 1001

通用寄存器

1、寄存器

存储数据:
CPU > 内存 > 硬盘
32位CPU:8 16 32
64位CPU:8 16 32 64

2、通用寄存器

32位通用寄存器:
EAX ESP
ECX EBP
EDX ESI
EBX EDI

3、MOV指令

<1>立即数到寄存器
<2>寄存器到寄存器
mov 位置1,位置2 //将位置2的数据复制到位置1

4、通用寄存储器

通用寄存储器
32位 16位 8位
EAX AX AL
ECX CX CL
EDX DX DL
EBX BX BL
ESP SP AH
EBP BP CH
ESI SI DH
EDI DI BH

8位寄存器,L(low)低八位,H(high)高八位
同位寄存储器之间才可以相互复制(MOV)

内存

1、每个应用程序都会有自己的独立的4GB内存空间

进程A/B均为虚拟内存,真正使用时需投射到物理内存上发挥作用
1 Byte=8Bit 1KB=1024Byte 1MB=1024KB 1GB=1024MB

2、内存地址

<1>内存太大只能使用编号,当我们向内存中存储数据,或者从内存中读取数据时,必须用到这个编号
<2>这个编号又称为内存地址(32位,前面0可以省略)
[0x00000000]—[0xFFFFFFFF]
<3>一个内存地址通常只能存储一个字节(1 Byte=8 BIT)
<4>内存地址从小到大是从上到下排列,成一倒置的梯形玻璃杯状

3、MOV指令

<1>立即数到内存
<2>寄存器到内存
<3>内存到寄存器
//<4>内存到内存

MOV 数据宽度 (PTR DS) : 内存地址,位置1 //将相同宽度的位置1的数据复制到内存中

  • 内存地址是连续使用的

内存地址的5种形式

1、形式一[立即数]

  • 读取内存的值:
    MOV EAX,DWORD PTR DS:[0x13FFC4]
    //从内存地址0x13FFC4开始读取一个DWORD大小的数据存入EAX寄存器中
  • 向内存中写入数据:
    MOV DWORD PTR DS:[0x13FFC24],EAX

2、形式二[reg]

[reg]代表寄存器 可以是8个通用寄存器中的任意一个
寄存器中存储着内存地址

  • 读取内存的值:
    MOV ECX,0x13FFD0
    MOV EAX,DWORD PTR DS:[ECX]
  • 向内存中写入数据:
    MOV EDX,0x13FFD8
    MOV DWORD PTR DS:[EDX],0x87654321

3、形式三[reg+立即数]

  • 读取内存的值:
    MOV ECX,0x13FFD0
    MOV EAX,DWORD PTR DS:[ECX+4]
  • 向内存中写入数据:
    MOV EDX,0x13FFD8
    MOV DWORD PTR DS:[EDX+0xC],0x87654321

4、形式四[reg+reg*{1,2,4,8}]

  • 读取内存的值:
    MOV EAX,13FFC4
    MOV ECX,2
    MOV EDX,DWORD PTR DS:[EAX+ECX*4]
  • 向内存中写入数据:
    MOV EAX,13FFC4
    MOV ECX,2
    MOV EDX,DOWORD PTR DS:[EAX+ECX*4],87654321

5、形式五[reg+reg*{1,2,4,8}+立即数]

  • 读取内存的值:
    MOV EAX,13FFC4
    MOV ECX,2
    MOV EDX,DWORD PTR DS:[EAX+ECX*4+4]
  • 向内存中写入数据:
    MOV EAX,13FFC4
    MOV ECX,2
    MOV DWORD PTR DS:[EAX+ECX*4+4],87654321

小端存储模式

1、存储模式

  • 大端存储:数据高位在低位(内存地址),数据低位在高位
    0x1A2C3E4F→1A 2C 3E 4F
  • 小端存储:数据低位在低位(内存地址),数据高位在高位
    0x1A2C3E4F→4F 3E 2C 1A

常用汇编指令

字母含义表:

字母 含义
r 通用寄存器
m 内存
imm 立即数
r8 8位通用寄存器
m8 8位内存
imm8 8位立即数

1、MOV指令

1
2
3
4
5
6
7
8
9
10
11
1.MOV r/m8,r8
2.MOV r/m16,r16
3.MOV r/m32,r32

4.MOV r8,r/m8
5.MOV r16,r/m16
6.MOV r32,r/m32

7.MOV r8,imm8
8.MOV r16,imm16
9.MOV r32,imm32

2、ADD指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1.ADD r/m8,imm8
2.ADD r/m16,imm16
3.ADD r/m32,imm32

4.ADD r/m16,imm8
5.ADD r/m32,imm8

6.ADD r/m8,r8
7.ADD r/m16,r16
8.ADD r/m32,r32

9.ADD r8,r/m8
10.ADD r16,r/m16
11.ADD r32,r/m32

3、SUB指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1.SUB r/m8,imm8
2.SUB r/m16,imm16
3.SUB r/m32,imm32

4.SUB r/m16,imm8
5.SUB r/m32,imm8

6.SUB r/m8,r8
7.SUB r/m16,r16
8.SUB r/m32,r32

9.SUB r8,r/m8
10.SUB r16,r/m16
11.SUB r32,r/m32

4、AND指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1.AND r/m8,imm8
2.AND r/m16,imm16
3.AND r/m32,imm32

4.AND r/m16,imm8
5.AND r/m32,imm8

6.AND r/m8,r8
7.AND r/m16,r16
8.AND r/m32,r32

9.AND r8,r/m8
10.AND r16,r/m16
11.AND r32,r/m32

5、OR指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1.OR r/m8,imm8
2.OR r/m16,imm16
3.OR r/m32,imm32

4.OR r/m16,imm8
5.OR r/m32,imm8

6.OR r/m8,r8
7.OR r/m16,r16
8.OR r/m32,r32

9.OR r8,r/m8
10.OR r16,r/m16
11.OR r32,r/m32

6、XOR指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1.XOR r/m8,imm8
2.XOR r/m16,imm16
3.XOR r/m32,imm32

4.XOR r/m16,imm8
5.XOR r/m32,imm8

6.XOR r/m8,r8
7.XOR r/m16,r16
8.XOR r/m32,r32

9.XORr r8,r/m8
10.XOR r16,r/m16
11.XOR r32,r/m32

7、NOT指令

[!note]
对操作数中的每一位进行逻辑“非”运算

1
2
3
1.NOT r/m8
2.NOT r/m16
3.NOT r/m32

8、MOVS指令

移动数据 内存-内存

固定为 将ESI存储的内存地址中的数据移动到EDI对应的内存地址

1
2
3
MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]	 	  简写:MOVSB
MOVS WORD PTR ES:[EDI],BYTE PTR DS:[ESI] 简写:MOVSW
MOVS DWORD PTR ES:[EDI],BYTE PTR DS:[ESI] 简写:MOVSD

MOVS指令每执行一次,两个目标地址都会自增1/2/4 (Byte)

EFLAGS第10位:DF位为方向标志,==0时为+,1时为-==

9、STOS指令

寄存器EAX对应数据大小的值存储到[EDI]指定的内存单元

1
2
3
STOS BYTE PTR ES:[EDI]		     简写:stosb
STOS WORD PTR ES:[EDI] 简写:stosw
STOS DWORD PTR ES:[EDI] 简写:stosd

STOS指令每执行一次,目标地址都会自增
//与MOVS指令修改的寄存器相同,均为EDI寄存器

10、REP指令

计数寄存器(ECX)中指定的次数重复执行字符串指令
REP + 指令

堆栈相关指令

1、什么是堆栈

1)就是一块内存,操作系统在程序启动的时候已经分配好的,供程序执行时使用
//本质上就是内存,只不过是系统规划好的更加便于操作的内存区域
2)和数据结构的堆栈无关
3)查看堆栈

2、栈指针寄存器

ESP存储了当前的堆栈用到哪里

3、堆栈的使用

<1>存储数据
<2>修改栈顶指针(栈顶指针指向当前元素)

堆栈使用时是从大地址到小地址

  • 局部变量一定要赋初始值

4、PUSH指令

<1>向堆栈中压入数据
<2>修改栈顶指针ESP寄存器(变小,即向上移动)

1
2
3
4
5
6
7
1、PUSH r32
2、PUSH r16

3、PUSH m32
4、PUSH m16

5、PUSH imm8/16/32

5、POP指令

<1>将栈顶数据存储到寄存器/内存
<2>修改栈顶指针ESP寄存器(增加,即向下移动)

1
2
3
4
5
1、POP r32
2、POP r16

3、POP m32
4、POP m16

修改EIP指令

  • EIP中存储的值是CPU执行下一次命令的地址

1、JMP指令

1
MOV EIP,r/m/imm

简写为:JMP r/m/imm
//不影响堆栈

2、CALL指令

1
2
push下一行地址
MOV EIP,r/m/imm

简写为:CALL r/m/imm

  • 与JMP唯一区别:
    在堆栈中存储 反汇编窗口处CALL指令下一行的地址
    //可以结合RET指令,实现随时随地调用目标函数

3、RET指令

1
2
ADD ESP,4
MOV EIP,[ESP-4]

简写为RET //相当于POP EIP
RET imm效果等价于

1
2
RET
​ADD ESP,imm

*反调试之Fake F8

1、单步步入/过

单步步入(F7) 单步步过(F8)
<1>单步步入与单步步过的区别:
单步步入:设置EFLAGS的TF位,每一次执行设置一个断点
​单步步过:在下一行设置断点

<2>调试器实现原理:
断点:0xCC(int 3)

2、反调试思路

堆大量CALL指令,CALL指令中塞满无用代码(花指令)

汇编眼中的函数

1、什么是函数

函数就是一系列指令的集合,为了完成某个会重复使用的特定功能

2、函数的调用

<1>用JMP来执行函数
<2>用CALL来执行函数

3、参数与返回值

一般用EAX存储返回值
//参数就是需要操作的数据,返回值就是操作后的结果数据

堆栈传参_堆栈平衡

1
MOV EAX,DWORD PTR DS:[ESP+4]

EAX一定会取得堆栈中的参数


1
2
3
4
5
MOV EAX,DWORD PTR DS:[ESP+14]
MOV EAX,DWORD PTR DS:[ESP+10]
MOV EAX,DWORD PTR DS:[ESP+C]
MOV EAX,DWORD PTR DS:[ESP+8]
MOV EAX,DWORD PTR DS:[ESP+4]

实现多个参数的相加

堆栈平衡

1)如果要返回父程序,则当我们在堆栈中进行堆栈的操作的时候,一定要保证在RET这条指令之前,ESP指向的是我们压入栈中的地址(如CALL指令存储的地址)

2)如果通过堆栈传递参数了,那么在函数执行完毕后,要平衡参数导致的堆栈变化

ESP寻址

1、寄存器传参与堆栈传参

举例:
寄存器传参:MOV r,r
堆栈传参:push imm

2、ESP寻址

1
2
3
4
5
6
7
8
push 1
push 2
call 目标函数起始地址
add esp,8 //外平栈

mov eax,dword ptr ss:[esp+4]
add eax,dword ptr ss:[esp+8]
ret

3、ESP寻址的缺点

由于ESP指向栈顶的值,而堆栈会被频繁使用,受各种指令影响
导致ESP寻址不适用于过于复杂的代码

EBP寻址

ESP栈顶指针、EBP栈底指针
利用EBP存储原先ESP的值,再利用相对固定的EBP寻址,不会受到函数指令影响

1
2
3
4
5
6
7
8
9
10
11
12
13
14
push 1
push 2
call 目标函数起始地址
add esp,8 //外平栈

push ebp​ ​ //将ebp的值存到堆栈里
mov ebp,esp​​​ //将ebp指向esp的位置
sub esp,10 //上移esp

mov eax,dword ptr ss:[ebp+8]
add eax,dword ptr ss:[ebp+c]

mov esp,ebp //平衡esp<1>
pop ebp​ ​ ​ //平衡ebp,平衡esp<2>

JCC

1、标志寄存器EFLAGS

2、CF(bit 0)

[Carry flag]

若算术操作产生的结果在最高有效位发生进位或借位则将其置1,反之清零

[!tip] 这个标志通常用来指示无符号整型运算的溢出状态

[!example]+
​ MOV AL,0xFE
​ ADD AL,2​
​​
​ 或

​ MOV AL,0x7F
​ SUB AL,0xFF

3、PF(bit 2)

[Parity flag]

如果结果的最低有效字节(1 Byte)包含偶数个1位则该位置1,否则清零

[!tip] 利用PF可进行奇偶校验检查

[!example]+
MOV AL,0CE
ADD AL,0

4、AF(bit 4)

[Auxiliary Carry flag]

如果算术操作在结果的第3位发生进位或借位则将该标志置1,否则清零

[!tip] 这个标志在BCD算术运算中被使用

5、ZF(bit 6)

[Zero flag]

若结果为0则将其置1,反之清零

[!tip] 经常与CMP或者TEST等指令一起使用

[!example]+
判断两个值是否相等
MOV EAX,100
MOV ECX,100
CMP EAX,ECX
(CMP指令相当于SUB指令,但相减结果并不保存到第一个操作数中)

[!example]+
判断某个值是否为0
TEST EAX,EAX
(TEST指令相当于AND指令,但是与的结果并不保存到第一个操作数中)

6、SF(bit 7)

[sign flag]

该标志​被设置为有符号整型的最高有效位

[!tip] 0为正,1为负

[!example]+
MOV AL,0x7F
ADD AL,2

MOV AL,0xFE
ADD AL,2

7、OF(bit 11)

[Overflow flag]

溢出标志OF用于​反映有符号数加减运算所得结果是否溢出

[!tip]
如果是无符号数运算,是否溢出看CF位
​​​​如果是有符号数运算,是否溢出看OF位

[!example]+
MOV AL,0x7F
ADD AL,2

8、DF(bit 10)

[Direction flag]

这个方向标志控制串指令。设置DF标志使得串指令自动递减(从高地址向低地址方向处理字符串),清楚该标志则使得串指令自动递增

[!tip] STD以及CLD指令分别用于设置以及清楚DF标志

9、JCC指令

JCC指令仅看当前EFLAGS的值,与其他指令无关联
只要EFLAGS的值符合JCC指令条件,则跳转

JCC指令 条件 EFLAGS
JE/JZ 结果为零则跳转(相等时跳转) ZF=1
JNE/JNZ 结果不为零则跳转(不相等时跳转) ZF=0
JS 结果为负则跳转 SF=1
JNS 结果为非负则跳转 SF=0
JP/JPE 结果中1的个数为偶数则跳转 PF=1
JNP/JPO 结果中1的个数为偶数则跳转 PF=0
JO 结果溢出了则跳转 OF=1
JNO 结果没有溢出则跳转 OF=0
JB/JNAE 小于则跳转 (无符号数) CF=1
JNB/JAE 大于等于则跳转 (无符号数) CF=0
JBE/JNA 小于等于则跳转 (无符号数) CF=1 or ZF=1
JNBE/JA 大于则跳转(无符号数) CF=0 and ZF=0
JL/JNGE 小于则跳转 (有符号数) SF≠ OF
JNL/JGE 大于等于则跳转 (有符号数) SF=OF
JLE/JNG 小于等于则跳转 (有符号数) ZF=1 or SF≠ OF
JNLE/JG 大于则跳转(有符号数) ZF=0 and SF=OF