补码:等于源码取反再加1

基础知识点

  • 8086CPU地址总线有多少根?

    • 20根,2^20 = 1MB
  • 汇编指令的格式,有哪些构成

    • 操作码,操作数
      • 内存
      • 寄存器
      • 立即数
  • 8086寄存器有哪些,

    • AX,BX,CX,DX, AH,AL, BH,BL,CH,CL,DH,DL
    • SI,DI
    • SP,BP
    • IP
    • FLAGS
    • CS,DS,ES,SS
  • 80386的呢?

    • EAX,EBX,ECX,EDX
    • ESP,EBP
    • ESI,EDI
    • EIP
    • EFLAGS
    • CS,DS,ES,SS,FS,GS
  • 标志寄存器有哪些位?

    • CF:进位(借位)标志
    • OF: 溢出标志
    • ZF: 零标志
    • SF: 符号
    • PF:奇偶
    • AF:辅助进位
    • DF:方向标志
    • TF:陷阱标志
    • IF:中断标志
  • 8086为什么有分段机制?

  • 逻辑地址和物理地址如何表示?

    • 操作大小是16位,地址总线20根,能访问内存1MB
      • 逻辑地址:段基址:偏移
      • 物理地址:段基址 * 16 +偏移

寻址方式有哪些?

立即数寻址: mov eax, 0x10000

寄存器寻址: mov eax, ebx

存储器寻址: mov eax, [xxxxxx]

直接寻址: mov eax,[0x1000]

寄存器间接寻址: mov eax,[esi]

相对寄存器寻址: mov eax,[esi + 08]

基址变址寻址: mov eax ,[ebx + esi]

相对基址变址寻址: mov eax,[ebx+esi+0x80]

带比例存储器寻址: mov eax,[ebx+esi*4+0x80]

串操作有哪些?

movsb/w/d :

将原操作数 DS:[ESI] 拷贝到 ES:[EDI],根据DF来设置地址递增或递减 stosb/w/d :将al,ax,eax,填充到目的 ES:[EDI],根据DF来设置地址递增或递减

cmpsb/w/d :

将原操作数 DS:[ESI] 和目标ES:[EDI]比较

loadsb/w/d:

将DS:[ESI]指向内容拷贝到al,ax,eax rep : 与movs/stos/loads 结合使用,ecx作为循环次数 repe/repne:

cmps,scas,除了判断ecx是否为0,判断zf位

cmp指令的作用?

cmp eax, 5

将操作数1 - 操作数2 设置EFLAGS,丢弃结果 判断两个数相等,ZF 是否为1

无符号大小:

看CF是否有借位,如果CF=1,操作1 <操作数2

有符号大小:

看OF是否有溢出,有溢出SF需要取反,最后看SF,SF=1 ,操作1 <操作数2

jcc指令有哪些?

je/jz : 相等跳转 (zf)

jne/jnz: 不相等跳转(zf)

ja: 大于跳转 (CF)

jb: 小于跳转 (CF)

jg: 大于跳转 (OF==SF)

jge:大于等于跳转

OD中快捷键

  • CTRL + B :二进制搜索
  • CTRL + S :多条指令搜索
  • CTRL +F9 :运行到返回(ret)
  • CTRL + X : 复制地址
  • F7 : 单步步入
  • F8: 单步步过
  • F9: 运行

在函数中,打开堆栈后,堆栈的布局是怎么样的?

  • 参数在什么位置 [ebp + 8]
  • 返回地址什么位置 [ebp+4]
  • 局部变量什么位置 [ebp- xx]

总线

数据总线

(1) 是CPU与内存或其他器件之间的数据传送的通道。

(2)数据总线的宽度决定了CPU和外界的数据传送速度。

(3)每条传输线一次只能传输1位二进制数据。8根数据线一次可传送一个8位二进制数据(即一个字节)。

(4)数据总线是数据线数量之和。

数据总线数据总线是CPU与存储器、CPU与I/O接口设备之间传送数据信息(各种指令数据信息)的总线,这些信号通过数据总线往返于CPU与存储器、CPU与I/O接口设备之间,因此,数据总线上的信息是双向传输的。

地址总线

(1)CPU是通过地址总线来指定存储单元的。

(2)地址总线决定了cpu所能访问的最大内存空间的大小。10根地址线能访问的最大的内存为1024位二进制数据(1024个内存单元)(1B)

(3)地址总线是地址线数量之和。

地址总线( Address Bus)是一种计算机总线,是CPU或有DMA能力的单元,用来沟通这些单元想要访问(读取/写入)计算机内存组件/地方的物理地址。 它是单向的,只能从CPU传向外部 存储器 或I/O端口

有个说法:64位CPU装了64位操作系统,最大物理内存理论上=2的64次方;然而实际上地址总线只用到了35位,所以最大物理内存是32G大小

控制总线

(1)CPU通过控制总线对外部器件进行控制。

(2)控制总线的宽度决定了CPU对外部器件的控制能力。

控制总线,英文名称:ControlBus,简称:CB。控制总线主要用来传送控制信号和时序信号。控制信号中,有的是微处理器送往存储器和输入输出设备接口电路的,如读/写信号,片选信号、中断响应信号等;也有是其它部件反馈给CPU的

总结:如果CPU要读取内存中的某个数据的话、、、

​ 先通过地址总线知道这个数据在哪,然后通过控制总线跟内存说我要都这个数据,然后通过数据总线进行传输读取

基础汇编之寄存器

通用寄存器

数据寄存器

8086的16位通用寄存器是AX,BX,CX,DX,SI,DI,BP,SP,其中前四个数据寄存器都还可以分成高8位和低8位两个独立的寄存器,

8086的8位通用寄存器是AH,BH,CH,DH,AL,BL,CL,DL,其中某个8位的操作,并不影响另外对应的8位数据

指针寄存器

指针寄存器用于寻址内堆栈的数据

SP为堆栈指针寄存器,指示栈顶的偏移地址

SP不能用于其它目的

BP为基址指针寄存器,表示数据在堆栈段中的基地址

SP和BP寄存器与SS段寄存器联合使用用以确定堆栈中的存储单元地址

变址寄存器

变址寄存器常用于存储器寻址时提供地址

​ SI是源变址寄存器(Source)

​ DI是目的变址寄存器(Destination)

指令指针寄存器

指令指针寄存器IP,它与代码段寄存器CS联用,用远存储的是即将执行的指令地址,每执行完一条指令,CPU会根据CS:IP找到下一条指令,同时IP会被赋值为下一条指令的地址

计算机通过CS:IP寄存器来控制指令序列的执行流程

IP寄存器是一个专用寄存器,不能被直接赋值修改

标志寄存器

标志寄存器flag是一个16位的寄存器,其中每一个位单独使用的,

0 CF 进位 CY/NC
1
2 PF 奇偶 PE/PO
3
4 AF 辅助 AC/NA
5
6 ZF ZR/NZ
7 SF 符号 NG/PL
8 TF 陷阱
9 IF 中断 EI/DI
10 DF 方向 DN/UP
11 OF 溢出 OV/NV
12 真/假
13 DEBUG标志位状态
14

状态标志

用来记录程序运行结果的状态信息,许多指令的执行都将相应的设置它CF,ZF,SF,PF,OF,AF

控制标志

可由程序根据需要用的指令设置,用于控制处理的执行方式DF,IF,TF

其中比较重要的是CF,OF,ZF,SF

进位标志CF

进位标志CF( Carry Flag),当运算结果的最高有效位有进位(加法)或借位(减法)

时,进位标志置1,即CF=1否则CF=0

例子:

3A+7C=B6,没有进位:CF=0AA+7C=(1)26,有进位:CF=1

溢出标志

溢出标志OF( Overflow Flag),若算术运算的结果有溢出,则OF=1,否则OF=0

什么是溢出:16位的范围是(+32767~-32768),如果运算结果超出这个范围,就产生了溢出,有溢出,说明有符号数的运算结果不正确例子

3A+7C=B6,产生溢出:OF=1AA+7C=(1)26,没有溢出:OF=0溢出和进位

溢出标志F和进位标志CF是两个意义不同的标志.进位是针对无符号数而言,溢出是针对有符号数而言。在汇编指令运算时,大多数不区分有符号数还是无符号数,

都按照无符号去计算结果,然后根据无符号,有符号运算,设置相应标记位而已

例如:F+2=(1)01

按照无符号数来说,它进位了,会设置CF标记位按照有符号数来说,它没有溢出,所以不会设置F标记位

根据现象记住溢出与进位:

按照有符号数运算,同号相加才会产生溢出,异号相加,不会溢出

两个数相加,超过F,就会进位

做减法时,前面的数比后面的数小,会借位。进位和溢出没有逻辑上的联系

零标志ZF

零标志ZF( Zero Flag),若运算结果为0,则ZF=1:否则ZF=0(注意:ZF为1表

示的结果是0)

例子

3A+7C=B6,结果不是零:ZF=084+7C=(1)00,结果是零:ZF=1

符号标志

符号标志SF( Sign Flag),运算结果最高位为1,则SF=1;否则SF=0(有符号数据用最高有效位表示数据的符号,所以最高有效位就是符号标志的状态)例子

3A+7C=B6,最高位D2=1:SF84+7C=(1)00,最高位D2=0:SF

奇偶标志

奇偶标志P( Parity Flag),当运算结果最低8位中“1”的个数为零或偶数时,PF=1,否则PF=0(F标志仅反映最低8位中“1”的个数是偶或奇,即使是进行16位字操作

例子

3A+7C=B6=10110110B结果中有5个1,是奇数:PF=0

辅助进位标志

辅助进位标志F( Auxiliary Carry Flag),运算时D位(低半字节)有进位或借位时,AF=1,否则AF=0(这个标志主要由处理器内部使用,用于十进制算术运算调整指令中,用户一般不必关心)

例子:

3AH+7CH=B6H,D有进位:AF=1

方向标志

方向标志DF( Direction Flag),用于串操作指令中,控制地址的变化方向:设置F=0,存储器地址自动增加设置DF=1,存储器地址自动减少。

CLD指令用于复位方向标志,执行后DF=0STD指令用于置位方向标志,执行后DF=1

中断允许标志

中断允许标志IF( Interrupt- enable Flag),用于控制外部可屏蔽中断是否可以被

处理器响应:

设置IF=1,则允许中断设置IF=0,则禁止中断。例如

CLI指令用于复位中断标志,执行后IF=0ST指令用于置位中断标志,执行后IF=1

陷阱标志

陷阱标志TF( Trap Flag),用于控制处理器进入单步操作方式设置TF=0,处理器正常工作设置TF=1,处理器单步执行指令。

单步执行指令:处理器在每条指令执行结束时,便产生一个编号为1的内部中断这种内部中断称为单步中断,所以TF也称为单步标志,利用单步中断可对程序进行逐条指令的调试,这种逐条指令调试程序的方法就是单步调试

内存地址操作数

当操作数是一个内存地址的时候,如下所示

MOV [65h], 12h

地址操作数,需要在地址外包裹上[],此条指令代表要往地址所在的内存中写入数据。8086CPU有20条地址线,最大可寻址空间为2=1MB,物理地址范围从0000~FH

物理地址和逻辑地址

每个物理存储单元都有一个唯一的20位编号,就是物理地址,从0000~FFFH。那么8086-CPU的地址总线是20位,但是操作数最大只有16位。那么如何产生20位地址呢?8086-CPU采用了分段机制解决这个问题。分段后在用户编程时,采用逻辑地址,形式为:

段基地址:段内偏移地址

将逻辑地址中的段地址左移4位(相当于乘以16),加上偏移地址就得到20位物理地址,一个物理地址可以有多个逻辑地址,例如

逻辑地址1460:100、1380:F0物理地址1470014700物理地址的计算方法

物理地址=段基地址×16+偏移地址

段寄存器

通常我们使用内存的时候,提供的都是一个段内偏移地址。段寄存器中存储的是段基地

址。不同类型的段内偏移和不同的段寄存器默认关联。

CS(代码段)指明代码段的起始地址

SS(堆段)指明堆栈段的起始地址

DS(数据段)指明数据段的起始地址

ES(附加段)指明附加段的起始地址

FS 在内存中的镜像 20 进程PID 24 线程ID 2c 指向线程局部存储的指针(32新增)

GS 。。。(32新增)

访问存储器的方式 默认 偏移地址 例子
取指令 CS IP mov ax,[ip]
堆栈操作 SS SP mov ax,[sp]
一般数据访问 DS 有效地址EA mov ax,[15h] 、mov ax,[bx]
BP基址的寻址方式 SS 有效地址EA mov ax,[bp+6h]
串操作指令的源操作数 DS SI mov ax,[si]
串操作指令的目的操作数 ES DI mov ax,[di]

段超越

修改默认使用的段寄存器称为段超越没有段超越的指令实例: MOV AX,[2000:AX-DS:[2000从默认的DS数据段取出数据采用段超越前缀的指令实例 MOV AX,ES:[2000:AX-ES:[2000

:从指定的ES附加段取出数据

理解分段的误区

通常,初次接触分段机制的时候,大家都会有一个认识上的误区,认为内存被划分成了个一个的段,每一个段有一个段基址和范围。

其实:8086-CPU共有20位地址总线,能够表示的地址空间是1MB,这块空间在物理上是连续的。为了方便管理,我们在逻辑上将其划分成多个段。逻辑段与另一个逻辑段是可以覆盖或者重合的。我们可以通过一些总结,更好的理解分段

8086对逻辑段要求

段地址低4位均为0每段最大不超过64KB8086对逻辑段并不要求:

必须是64KB

各段之间完全分开(即可以重叠)1MB空间最多能分成多少个段?

每隔16个存储单元就可以开始一个段,所以IMB最多可以有:2÷16=2”=64K个段1MB空间最少能分成多少个段每隔64K个存储单元开始一个段所以1MB最少可以有:2÷2=16个段

内存操作数的多种书写形式

当内存地址作为指令操作数的时候,可以有多种书写形式

操作数形式 解释
mov ax,[0x1234] 从ds:1234中取出两个字节,存入ax中
mov ax,word ptr[0x1234] 从ds:1234中取出两个字节,存入ax中
mov al,[0x1234] 从ds:1234中取出一个字节,存入al中
mov al,byte ptr [0x1234] 从ds:1234中取出一个字节,存入al中
mov ax,ES:[0x1234] 从es:1234中取出两个字节,存入ax中
mov ax,word ptr CS:[0x1234] 从cs:1234中取出两个字节,存入ax中
mov ax,[bx][si] 等价于mov ax,[bx+si]
mov ax,12[si] 等价于mov ax,[si+12]

x86分段机制

在CPU进化到386时,操作系统普遍采用的时32位下的保护模式,分段机制发生了很大的变化,我们暂且认为32位程序中,所有的段基址都是0,段内偏移范围时0~4Gb,这也被称之位平坦模式

栈(Stack)是主存中一个特殊的区域,本质上不属于寄存器

它采用先进后出FILO(First In Last Out)或后进先出的原则尽心存取操作,而不是随机存取的操作方式

堆栈通常由处理器自动维持,在8086中,由堆栈段寄存器SS和堆栈指针寄存器SP共同指示

基础汇编指令

操作数类型

操作数简写 含义 举例
r8 任意八位通用寄存器 AH AL BH BL CH CL DH DL
r16 任意一个16位通用寄存器 AX BX CX DX SI DI BP SP
r32 任意一个32位的寄存器 EAX EBX ECX EDX ESI EDI EBP ESP
reg 代表所有r8或者r16或者r32 以上全部
seg 段寄存器 CS DS ES SS
m8 一个8位存储器操作单元 BYTE PTR DS:[0x12345678]
m16 一个16位存储器操作单元 WORD PTR DS:[0X12345678]
m32 一个32位存储器操作单元 DWORD PTR DS:[0X12345678]
mem 代表所有r8或者r16或者r32 以上全部
i8 8位数字
i16 16位数字
i32 32位数字
imm 一个数字

数据传输指令

指令 操作数1 操作数2 执行操作
mov reg reg/mem/imm 将操作数2的值传送到操作数1中
mem reg/imm
seg reg16/mem16
xchg reg reg/imm 将操作数1和操作数2的内容进行交换
mem reg
lea reg reg/mem 将源操作数的地址传送到目标操作数的寄存器中
push reg/mem/seg/imm 将操作数1中的内存放到堆栈中,esp自减4
pop reg/mem/seg 将栈顶的数据存放到操作数1中,esp自加4
pushf 将eflag存放到堆栈中,esp自减4
popf 将栈顶的数据存放到eflag中,esp自加4
pushad 将所有寄存器压入堆栈
popad 从栈顶开始,将数据存入各个寄存器

算数运算符

指令 操作数1 操作数2 操作数3 执行操作
add reg reg/mem/imm 将操作数1与操作数2相加,并将结果保存在目标操作数中
mem reg/imm
adc reg reg/mem/imm 将操作数1和操作数2相加并和标志寄存器CF位的值一起加到操作数1中
mem reg/imm
sub reg reg/mem/imm 将操作数1与源操作数2相减,将结果保存到操作数1中
mem reg/imm
sbb reg reg/mem/imm 将操作数1与源操作数2相减,再减去CF标志位,将结果保存到操作数1中
mem reg/imm 将目标操作数自加1,同时CF标志不变
dec reg/mem 减目标操作数自减1,同时CF标志不变
mul reg/mem 将AL/AXEAX和操作数1相乘,结果放置在EDX:EAX中
imul reg/mem add 将AL/AXEAX和操作数1相乘,结果放置在EDX:EAX中,此为有符号操作
reg reg/mem/imm 将操作数1和操作数2相乘,将乘积保存在操作数1中,此为有符号操作
reg reg/mem imm 将操作数1,2,3相乘,结果保存在1中
div reg/mem 将AX、DX:AX或EDX:EAX中的值除以操作数1,结果存储到AX(AH:AL)、DX:AX或EDX:EAX寄存器,此为无符号操作
idiv reg/mem 将AL、AX或EAX寄存器中的值除以源操作数,结果存储到AX、DX:AX或EDX:EAX寄存器,此为有符号操作

逻辑运算指令

指令 操作数1 操作数2 执行操作
and reg reg/mem/imm 将操作数1与操作数2进行按位与运算,结果存储到操作数1中
mem reg/imm
or reg reg/mem/imm 将操作数1与操作数2进行按位或运算,结果存储到操作数1中
mem regimm
xor reg reg/mem/imm 将操作数1与操作数2进行按位异或运算,结果存储到操作数1中
mem reg/imm
not reg/mem 对目标操作数执行按位取反结果存储到目标操作数位置
shl reg imm 对给定的目标操作数左移imm/CL次,每次移位时最高移入标志位CF中,最低为补0
reg CL
shr reg imm 对给定的目标操作数右移imm/CL次,每次位移最低位移至标志位CF中
reg CL

逻辑指令

逻辑运算符都不会得到结果,仅仅设置标志寄存中相应的标志位

指令 操作数1 操作数2 执行操作
cmp reg reg/mem/imm 用操作数1减去操作数2,并根据结果设置Eflag寄存器中的状态,然后丢弃结果
test reg reg/mem/imm 将操作数1与操作数2按位与操作,然后根据结果设置ZF,PF状态标志

串操作指令与控制转移

串操作指令

被用于操作某一内存区域中由相同类型数据构成一个整体(相当于一个数组),这个结构一般被用于保存字符串或其它连续存放的单一类型数据

指令 操作数1 操作数2 执行操作 类比
movsb/w/d ES:[EDI] DS:[ESI] 将操作数2的值直接传送到操作数1中,同时EDI与ESI自增1 memcpy
stosb/w/d DS:[EDI] 将AL、AX、或EAX寄存器的字节,字或双字存储到目标操作数 memset
lodsb/w/d DS:[ESI] 将源操作数中的字节,字或双字分别加载到AL、AX、EAX寄存器中
cmpsb/w/d ES:[EDI] DS:[ESI] 比较一个源操作数指定的字节、字或双字与第二个源操作数指定的字节、字或双字,根据结果设置eflag寄存器中的状态标志 memcmp
rep/repe 与串操作指令联用,重复执行串指令ecx次,每执行一次ecx减一,当ecx为0时或zf为0的时候结束重复
repne 与串操作指令联用,重复执行串指令ecx次,每执行一次ecx减一,当ecx为0时或zf为1的时候结束重复

控制转移指令

指令 操作数1 执行操作 类比
JMP reg/mem/imm 跳转到操作数1所指向的地址,从目标地址处执行指令 goto
LOOP imm 跳转到操作数1所指向的地址,同时ECS减1.当ECX为0时,不会跳转
CALL reg/mem/imm 跳转到操作数1所指向的地址,同时将CALL指令后面的指令所在的地址压入堆栈
RET imm 从栈顶获取数据当作地址并跳转,同时栈顶数据出栈,即ESP+4
指令 解释 条件
JA jump if above(大于) CF == 0 and ZF == 0
JAE jump if above or Equal(大于等于) CF == 1
JB < CF ==1
JBE <= CF == 1 OR ZF ==1
JC < CF ==1
JG 有符号 > ZF ==0 AND SF == OF
JGE 有符号 >= SF == OF
JL 有符号 < SF != OF
JLE 有符号 <= ZF ==1 OR SF !=OF
JO 溢出 OF==1
JNO 不溢出 OF==0
JNZ 不相等 != zf==0

例题

例1:拷贝字符串例

lea esi,[0x100];串拷贝的源地址

lea edi,[0x120]串拷贝的目的地址

mov ecx,0x10;拷贝的字节数为0x10

cld;清除方向标志DF,控制串操操作方向为递增

rep movsd;使用重复前缀rep重复执行

例2:填充字符串

lea edi,[0x120];串填充的目的地址

mov eax,0x31;串填充的内容31=1

mov ecx,0x10;串填充的执行次数

cld;清除方向标志DF,控制串操作方向为递增

rep stosd;使用重复前缀rep重复执行

例3:计算数组元素之和

lea es,[120];数组的起始地址例

mov ecx,10;控制loop指令的执行次数

xor eax,eax;将eax清空方便读取内容

xor edx,edx;将edx清空,保存累加结果

cld ;清除方向标志DF,控制串操作方向为递增;

XXX:

lodsd;将指定位置的字符读取到eax中

add edx,eax;做加法计算

loop xoxx ;跳转到“lodsb”指令处,并将 ecx减1,直到ecx为0

例4:比较字符串

ea esi,[0x100];串比较的源地址

lea edi,[0x120];串比较的目的地址

mov ecx,0x10;比较的字节数为0x10

cld ;清除方向标志DF,控制串操作方向为递增

repe cmpsb;循环比较直到cx为0或ZF为0时结束

lodsd

je XXX;如果相等,则跳转到后面xxx

mov bx,1;否则将bx置为1

OD使用

OD窗口

L : log 保存日志信息

E :程序的所有模块的信息(加载基址,大小,OEP,路径)

M :程序的内存映射视图

T : 线程信息

W :窗口信息

H :句柄表

C :CPU窗口(反汇编窗口)

/ :补丁信息 K :调用堆栈

B :软件断点列表

R :显示参考(数据引用等)

… : RUN跟踪

S : 源码显示窗口

OD快捷方式

快捷键 描述
F2 设置或取消断点
F4 执行到当前光标选中的指令
F7 单步步入,遇到call指令跟进
Ctrl + F7 重复F7,指定按Esc、F12或者遇到其它断点时停止
F8 单步步过,遇到call指令时,不跟进
Ctrl + F8 重复按F8,指定按Esc、F12或者遇到其它断点时停止
F9 运行程序
Ctrl + F9 直到出现ret指令时中断
Alt + F9 若进入到系统领域,此命令可回到应用程序领域
Ctrl + F12 重启调试
Ctrl + G 跳转到指定内存的地址
Ctrl + F 搜索指令
Ctrl + S 搜索指令(多条)
Ctrl + L 搜索下一条指令
Ctrl + A 分析代码
Ctrl + E 修改内存
Ctrl + B 二进制搜索
Ctrl + X 复制地址
Shift + X 复制二进制数据
Alt + B 打开断点窗口
-(减号) 回到之前单步的代码
双击EIB寄存器 让光标回到EIB所指向的语句
Enter 进入函数(跟踪指令)

函数栈布局

C代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
int Max(int a, int b)
{
if (a > b)
{
return a;
}
else
{
return b;
}
}
int main()
{
int m = 0;
m = Max(10, 20);
printf("%d", m);
return 0;
}

对应汇编代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
-------main函数------------
007137D0 55 PUSH EBP ; 保存旧EBP
007137D1 8BEC MOV EBP,ESP ; EBP和ESP相同,空栈
007137D3 81EC CC000000 SUB ESP,0xCC
007137D9 53 PUSH EBX ; 保存寄存器
007137DA 56 PUSH ESI
007137DB 57 PUSH EDI
007137DC 8DBD 34FFFFFF LEA EDI,DWORD PTR SS:[EBP-0xCC] ; 局部空间的栈顶
007137E2 B9 33000000 MOV ECX,0x33 ; 循环次数
007137E7 B8 CCCCCCCC MOV EAX,0xCCCCCCCC ; 填充数据
007137EC F3:AB REP STOS DWORD PTR ES:[EDI] ; 循环填充局部空间(0x33*4=
0xCC)
007137EE B9 03B07D00 MOV ECX,汇编语言.007DB003
007137F3 E8 93B7FFFF CALL 汇编语言.0070EF8B ;调试支持
007137F8 C745 F8 0000000>MOV DWORD PTR SS:[EBP-0x8],0x0 ;ebp-8代表 局部变量m
007137FF 6A 14 PUSH 0x14 ;参数2 :20
00713801 6A 0A PUSH 0xA ;参数1 :10
00713803 E8 109EFFFF CALL 汇编语言.0070D618 ;调用MAX
00713808 83C4 08 ADD ESP,0x8 ;平衡两个参数
0071380B 8945 F8 MOV DWORD PTR SS:[EBP-0x8],EAX ;m = eax
0071380E 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-0x8] ;printf
00713811 50 PUSH EAX ;eax: m值
00713812 68 501E7B00 PUSH 汇编语言.007B1E50 ;“%d”
00713817 E8 DB9BFFFF CALL 汇编语言.0070D3F7 ;printf函数
0071381C 83C4 08 ADD ESP,0x8 ;平衡堆栈
0071381F 33C0 XOR EAX,EAX ; eax清零
00713821 5F POP EDI ;回复寄存器
00713822 5E POP ESI
00713823 5B POP EBX
00713824 81C4 CC000000 ADD ESP,0xCC ;释放堆栈
0071382A 3BEC CMP EBP,ESP ;检查堆栈是否平衡
0071382C E8 13B8FFFF CALL 汇编语言.0070F044 ;检查堆栈函数
00713831 8BE5 MOV ESP,EBP ;回复栈
00713833 5D POP EBP ;回复旧EBP
00713834 C3 RETN ;返回
MAX函数-------------------------------------------
00901720 55 PUSH EBP
00901721 8BEC MOV EBP,ESP
00901723 83EC 40 SUB ESP,0x40
00901726 53 PUSH EBX
00901727 56 PUSH ESI
00901728 57 PUSH EDI
00901729 B9 03C09000 MOV ECX,汇编语言.0090C003
0090172E E8 E9FAFFFF CALL 汇编语言.0090121C
00901733 8B45 08 MOV EAX,DWORD PTR SS:[EBP+0x8] ;[EBP+0x8] 参数1 (10)
00901736 3B45 0C CMP EAX,DWORD PTR SS:[EBP+0xC] ; 比较参数1和参数2 [EBP+0xC] 参
数2 (20)
00901739 7E 07 JLE SHORT 汇编语言.00901742 ;小于等于 跳转
0090173B 8B45 08 MOV EAX,DWORD PTR SS:[EBP+0x8] ;返回参数1
0090173E EB 05 JMP SHORT 汇编语言.00901745 ;跳转返回
00901740 EB 03 JMP SHORT 汇编语言.00901745
00901742 8B45 0C MOV EAX,DWORD PTR SS:[EBP+0xC] ;返回参数2
00901745 5F POP EDI ; 回复寄存器
00901746 5E POP ESI
00901747 5B POP EBX
00901748 8BE5 MOV ESP,EBP ;释放栈空间
0090174A 5D POP EBP
0090174B C3 RETN

调用约定

约定 参数传递的方式 清理栈的方式 特点
_cdecl 由右向左 调用者清理
_stdcall 由右向左 被调用者清理
_fastcall 由右向左 被调用者清理 左边开始的两个不大于4字节的参数分别放在ecx和edx寄存器,其余参数自右向左压栈传送
_thiscall 由右向左 被调用清理 通过ecx传递指针

C代码演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include<stdio.h>
// cdecl调用约定
int _cdecl max1(int a, int b)
{
return a + b;
}
//stdcall调用约定
int _stdcall max2(int a, int b)
{
return a + b;
}
//_fastcall调用约定
int _fastcall max3(int a, int b,int c)
{
return a + b+c;
}
// _thiscall调用约定
class OBJECT {
public:
int max4(int a, int b)
{
return a + b;
}
};
int main()
{
// cdecl 调用约定
max1(10, 20);
max2(20, 30);
max3(40, 50,60);
OBJECT obj;
obj.max4(70, 80);
}

cdecl调用约定

1
2
3
4
5
6
max1(10, 20);
; 参数从右往左依次入栈 ,调用者平衡堆栈
00E718E2 6A 14 push 14h
00E718E4 6A 0A push 0Ah
00E718E6 E8 59 F9 FF FF call 00E71244
00E718EB 83 C4 08 add esp,8

stdcall调用约定

1
2
3
4
5
6
7
8
9
max2(20, 30);
; 参数从右往左依次入栈
00E718EE 6A 1E push 1Eh
00E718F0 6A 14 push 14h
00E718F2 E8 D6 F7 FF FF call 00E710CD
;被调用者平衡堆栈
00E717AE 8B E5 mov esp,ebp
00E717B0 5D pop ebp
00E717B1 C2 08 00 ret 8

fastcall调用约定

1
2
3
4
5
6
7
8
9
10
max3(40, 50,60);
; 参数从右往左依次入栈,前两个参数由ecx,edx传递
00E718F7 6A 3C push 3Ch
00E718F9 BA 32 00 00 00 mov edx,32h
00E718FE B9 28 00 00 00 mov ecx,28h
00E71903 E8 A2 F7 FF FF call 00E710AA
;被调用者平衡堆栈
00E71819 8B E5 mov esp,ebp
00E7181B 5D pop ebp
00E7181C C2 04 00 ret 4

thiscall调用约定

1
2
3
4
5
6
7
8
9
10
11
OBJECT obj;
obj.max4(70, 80);
; 参数从右往左依次入栈,ecx传递this指针
00E71908 6A 50 push 50h
00E7190A 6A 46 push 46h
00E7190C 8D 4D F7 lea ecx,[ebp-9]
00E7190F E8 44 F9 FF FF call 00E71258
;被调用者平衡堆栈
00E71883 8B E5 mov esp,ebp
00E71885 5D pop ebp
00E71886 C2 08 00 ret 8

三大结构

顺序结构

请忽略…

选择结构

if else

C代码

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
int main()
{
int a = 10;
if (a > 5)
{
printf("a > 5 \n");
}
else {
printf("a <= 5 \n");
}
}

汇编代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
00731810 55 push ebp
00731811 8B EC mov ebp,esp
00731813 81 EC CC 00 00 00 sub esp,0CCh
00731819 53 push ebx
0073181A 56 push esi
0073181B 57 push edi
0073181C 8D BD 34 FF FF FF lea edi,[ebp+FFFFFF34h]
00731822 B9 33 00 00 00 mov ecx,33h
00731827 B8 CC CC CC CC mov eax,0CCCCCCCCh
0073182C F3 AB rep stos dword ptr es:[edi]
0073182E B9 03 C0 73 00 mov ecx,73C003h
00731833 E8 DF F9 FF FF call 00731217
00731838 C7 45 F8 0A 00 00 00 mov dword ptr [ebp-8],0Ah ;int a
0073183F 83 7D F8 05 cmp dword ptr [ebp-8],5 ; a > 5
00731843 7E 0F jle 00731854 ;如果小于跳转
;a>5 大于执行下面代码
00731845 68 30 7B 73 00 push 737B30h ;"a > 5"
0073184A E8 F7 F7 FF FF call 00731046
0073184F 83 C4 04 add esp,4
00731852 EB 0D jmp 00731861
;a <=5 小于执行下面代码
00731854 68 3C 7B 73 00 push 737B3Ch ;"a <=5 "
00731859 E8 E8 F7 FF FF call 00731046 ;printf
0073185E 83 C4 04 add esp,4
00731861 33 C0 xor eax,eax ;返回值 0
00731863 5F pop edi
00731864 5E pop esi
00731865 5B pop ebx
00731866 81 C4 CC 00 00 00 add esp,0CCh
0073186C 3B EC cmp ebp,esp
0073186E E8 AE F9 FF FF call 00731221
00731873 8B E5 mov esp,ebp
00731875 5D pop ebp
00731876 C3 ret

switch case 1

C代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int a = 4;
switch (a)
{
case 1:
a = 1; break;
case 2:
a = 2; break;
case 3:
a = 3; break;
default:
a = 0;
break;
}

汇编代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
00801700 55 push ebp
00801701 8B EC mov ebp,esp
00801703 81 EC D0 00 00 00 sub esp,0D0h
00801709 53 push ebx
0080170A 56 push esi
0080170B 57 push edi
0080170C 8D BD 30 FF FF FF lea edi,[ebp+FFFFFF30h]
00801712 B9 34 00 00 00 mov ecx,34h
00801717 B8 CC CC CC CC mov eax,0CCCCCCCCh
0080171C F3 AB rep stos dword ptr es:[edi]
0080171E B9 03 C0 80 00 mov ecx,80C003h
00801723 E8 E0 FA FF FF call 00801208
00801728 C7 45 F8 04 00 00 00 mov dword ptr [ebp-8],4 ; int a =4
0080172F 8B 45 F8 mov eax,dword ptr [ebp-8] ; eax保存4
00801732 89 85 30 FF FF FF mov dword ptr [ebp+FFFFFF30h],eax ;定义局部变量保存a
00801738 83 BD 30 FF FF FF 01 cmp dword ptr [ebp+FFFFFF30h],1 ;比较case 1
0080173F 74 14 je 00801755
00801741 83 BD 30 FF FF FF 02 cmp dword ptr [ebp+FFFFFF30h],2 ;比较case 2
00801748 74 14 je 0080175E
0080174A 83 BD 30 FF FF FF 03 cmp dword ptr [ebp+FFFFFF30h],3 ;比较case 3
00801751 74 14 je 00801767
00801753 EB 1B jmp 00801770 ; 跳转到default
; case1:
00801755 C7 45 F8 01 00 00 00 mov dword ptr [ebp-8],1
0080175C EB 19 jmp 00801777
;case 2:
0080175E C7 45 F8 02 00 00 00 mov dword ptr [ebp-8],2
00801765 EB 10 jmp 00801777
; case 3:
00801767 C7 45 F8 03 00 00 00 mov dword ptr [ebp-8],3
0080176E EB 07 jmp 00801777
switch 2
; default:
00801770 C7 45 F8 00 00 00 00 mov dword ptr [ebp-8],0
00801777 33 C0 xor eax,eax
00801779 5F pop edi
0080177A 5E pop esi
0080177B 5B pop ebx
0080177C 81 C4 D0 00 00 00 add esp,0D0h
00801782 3B EC cmp ebp,esp
00801784 E8 89 FA FF FF call 00801212
00801789 8B E5 mov esp,ebp
0080178B 5D pop ebp
0080178C C3 ret

switch case 2

C代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int a = 4;
switch (a)
{
case 1:
a = 1; break;
case 2:
a = 2; break;
case 3:
a = 3; break;
case 4:
a = 3; break;
case 5:
a = 3; break;
default:
a = 0;
break;
}

汇编代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
00731700 55 push ebp
00731701 8B EC mov ebp,esp
00731703 81 EC D0 00 00 00 sub esp,0D0h
00731709 53 push ebx
0073170A 56 push esi
0073170B 57 push edi
0073170C 8D BD 30 FF FF FF lea edi,[ebp+FFFFFF30h]
00731712 B9 34 00 00 00 mov ecx,34h
00731717 B8 CC CC CC CC mov eax,0CCCCCCCCh
0073171C F3 AB rep stos dword ptr es:[edi]
0073171E B9 03 C0 73 00 mov ecx,73C003h
00731723 E8 E0 FA FF FF call 00731208
00731728 C7 45 F8 04 00 00 00 mov dword ptr [ebp-8],4 ; int a= 4
0073172F 8B 45 F8 mov eax,dword ptr [ebp-8]
00731732 89 85 30 FF FF FF mov dword ptr [ebp+FFFFFF30h],eax ; int i = a
00731738 8B 8D 30 FF FF FF mov ecx,dword ptr [ebp+FFFFFF30h] ; i -=1
0073173E 83 E9 01 sub ecx,1
00731741 89 8D 30 FF FF FF mov dword ptr [ebp+FFFFFF30h],ecx
;判断 i > 4 跳转到default
00731747 83 BD 30 FF FF FF 04 cmp dword ptr [ebp+FFFFFF30h],4
0073174E 77 3A ja 0073178A
; 跳转表
;0x007317A8 0073175d ].s.
;0x007317AC 00731766 f.s.
;0x007317B0 0073176f o.s.
;0x007317B4 00731778 x.s.
;0x007317B8 00731781 ?.s.
;0x007317BC cccccccc ????
;如果小于4那么
00731750 8B 95 30 FF FF FF mov edx,dword ptr [ebp+FFFFFF30h]
00731756 FF 24 95 A8 17 73 00 jmp dword ptr [edx*4+007317A8h]
$LN4:
0073175D C7 45 F8 01 00 00 00 mov dword ptr [ebp-8],1
00731764 EB 2B jmp 00731791
$LN5:
00731766 C7 45 F8 02 00 00 00 mov dword ptr [ebp-8],2
0073176D EB 22 jmp 00731791
$LN6:
0073176F C7 45 F8 03 00 00 00 mov dword ptr [ebp-8],3
00731776 EB 19 jmp 00731791
$LN7:
00731778 C7 45 F8 03 00 00 00 mov dword ptr [ebp-8],3
0073177F EB 10 jmp 00731791
$LN8:
00731781 C7 45 F8 03 00 00 00 mov dword ptr [ebp-8],3
00731788 EB 07 jmp 00731791
; defalut
0073178A C7 45 F8 00 00 00 00 mov dword ptr [ebp-8],0
00731791 33 C0 xor eax,eax
00731793 5F pop edi
00731794 5E pop esi
00731795 5B pop ebx
00731796 81 C4 D0 00 00 00 add esp,0D0h
0073179C 3B EC cmp ebp,esp
0073179E E8 6F FA FF FF call 00731212
007317A3 8B E5 mov esp,ebp
007317A5 5D pop ebp
007317A6 C3 ret

循环结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
00EE1810 55 push ebp
00EE1811 8B EC mov ebp,esp
00EE1813 81 EC D8 00 00 00 sub esp,0D8h
00EE1819 53 push ebx
00EE181A 56 push esi
00EE181B 57 push edi
00EE181C 8D BD 28 FF FF FF lea edi,[ebp+FFFFFF28h]
00EE1822 B9 36 00 00 00 mov ecx,36h
00EE1827 B8 CC CC CC CC mov eax,0CCCCCCCCh
00EE182C F3 AB rep stos dword ptr es:[edi]
00EE182E B9 03 C0 EE 00 mov ecx,0EEC003h
00EE1833 E8 DF F9 FF FF call 00EE1217
// for
for (int i = 0; i < 10; i++)
{
printf("%d", i);
}
; for 循环语句
00EE1838 C7 45 F8 00 00 00 00 mov dword ptr [ebp-8],0 ; int i =0;
00EE183F EB 09 jmp 00EE184A
;循环计数 i++
00EE1841 8B 45 F8 mov eax,dword ptr [ebp-8]
00EE1844 83 C0 01 add eax,1
00EE1847 89 45 F8 mov dword ptr [ebp-8],eax
00EE184A 83 7D F8 0A cmp dword ptr [ebp-8],0Ah ;if i >=10 循环结束
00EE184E 7D 13 jge 00EE1863
; 循环体
00EE1850 8B 45 F8 mov eax,dword ptr [ebp-8]
00EE1853 50 push eax ;参数2
00EE1854 68 30 7B EE 00 push 0EE7B30h ;参数1
00EE1859 E8 E8 F7 FF FF call 00EE1046 ;printf
00EE185E 83 C4 08 add esp,8 ;平衡堆栈
00EE1861 EB DE jmp 00EE1841
; while 循环
int index = 0;
while (index < 10)
{
printf("%d", index);
index++;
}
00EE1863 C7 45 EC 00 00 00 00 mov dword ptr [ebp-14h],0 ;int index = 0
00EE186A 83 7D EC 0A cmp dword ptr [ebp-14h],0Ah ; if index >= 10 循环结束
00EE186E 7D 1C jge 00EE188C
00EE1870 8B 45 EC mov eax,dword ptr [ebp-14h]
07 定位关键代码
通过字符串搜索
通过API断点-》堆栈回溯
00EE1873 50 push eax ;参数2
00EE1874 68 30 7B EE 00 push 0EE7B30h ;参数1
00EE1879 E8 C8 F7 FF FF call 00EE1046 ;printf
00EE187E 83 C4 08 add esp,8
00EE1881 8B 45 EC mov eax,dword ptr [ebp-14h] ;index ++
00EE1884 83 C0 01 add eax,1
00EE1887 89 45 EC mov dword ptr [ebp-14h],eax
00EE188A EB DE jmp 00EE186A ;向上跳转
index = 0;
do{
printf("%d", index);
index++;
} while (index < 10);
00EE188C C7 45 EC 00 00 00 00 mov dword ptr [ebp-14h],0 ; index =0;
; 循环体
00EE1893 8B 45 EC mov eax,dword ptr [ebp-14h]
00EE1896 50 push eax ;参数2
00EE1897 68 30 7B EE 00 push 0EE7B30h ;参数1
00EE189C E8 A5 F7 FF FF call 00EE1046 ;printf
00EE18A1 83 C4 08 add esp,8
00EE18A4 8B 45 EC mov eax,dword ptr [ebp-14h] ;index ++
00EE18A7 83 C0 01 add eax,1
00EE18AA 89 45 EC mov dword ptr [ebp-14h],eax
00EE18AD 83 7D EC 0A cmp dword ptr [ebp-14h],0Ah ;如果小于10继续循环
00EE18B1 7C E0 jl 00EE1893
00EE18B3 33 C0 xor eax,eax
00EE18B5 5F pop edi
00EE18B6 5E pop esi
00EE18B7 5B pop ebx
00EE18B8 81 C4 D8 00 00 00 add esp,0D8h
00EE18BE 3B EC cmp ebp,esp
00EE18C0 E8 5C F9 FF FF call 00EE1221
00EE18C5 8B E5 mov esp,ebp
00EE18C7 5D pop ebp
00EE18C8 C3

汇编对应库

C语言 汇编 描述
#include <stdio> include msvcrt.inc includelib msvcrt.lib 输入输出库
printf crt_printf 格式化输出
scanf crt_scanf 格式化输入
getchar crt_getchar 获取一个字符
_getch crt__getch 无回显获取一个字符
fopen crt_fopen 打开文件
fwrite crt_fwrite 写入文件
fclose crt_fclose 关闭文件
C语言 汇编 描述
#include <stdlib.h> include msvcrt.inc include msvcrt.lib 输入输出库
system crt_system 系统函数
malloc crt_malloc 申请堆空间
free crt_free 释放堆空间
exit crt_exit 结束进程
srand crt_srand 设置随机数种子
rand crt_rand 生成随机数
Windows 汇编 描述
GDI对象相关API include gdi32.inc includelib gdi32.lib GDI对象相关,比如:画笔,画刷
CreateDC CreateDC 创建DC
CreatePen CreatePen 创建画笔
Windows 汇编 描述
网络相关 include wsock32.inc includelib wsock32.lib 内核对象相关,比如:进程线程
socket socket 创建套接字
bind bind 绑定套接字

汇编数据类型

名称 表示方式 缩写 长度(字节)
字节 byte db 1
word dw 2
双字 dword dd 4
三字 fword df 6
四字 qword dq 8
十字节BCD码 tbyte dt 10
有符号字节 sbyte 1
有符号字 sword 2
有符号双字节 sdword 4
单精度浮点数 real4 4
双精度浮点数 real8 8
10字节浮点数 real10 10

混合编程

x32混合编程

内联汇编

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>

int main()
{
int a;
// 内联汇编有两种方式
// 行内联汇编
_asm push 0
_asm pop eax

// 块内联汇编
_asm
{
mov eax,100
add eax,200
mov [a],eax
}

// 嵌入机器指令
_asm
{
_emit 0x6A;//push 0
_emit 0x00;
_emit 0x58; // pop eax
}

printf("%d", a);

}

x64混合编程

使用.asm进行外联汇编

替换vs的编译器

使用外联汇编

1.添加.asm文件

​ asm文件中写入需要的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
;参数1 rcx 参数2 rdx 参数3 r8 参数4 r9
;[esp]返回地址
;[esp+4]参数1
.code
AddFun proc
xor rax,rax
add rax,rcx
add rax,rdx
add rax,r8
add rax,r9
ret
AddFun endp
end

2,添加汇编编译器

​ 右击工程-生成依赖项-生成自定义-勾选masm(.targets,.props)项-确定

3.让asm文件加入连接选项

​ 右击-属性页-常规

​ -从生成中排除-否

​ -项类型-Microsoft Macro Assembler

​ 点击确定

4.C++使用汇编函数

1
2
3
4
5
6
7
8
#include <stdio.h>
extern "C" int AddFun(int a,int b,int c,int d);
int main()
{
int x = AddFun(1,2,3,4);
printf("%d\n",x);
return 0;
}

OPCODE

指令格式

前缀 代码 构造模式 SIB 位移 立即数
Prefixes Code ModR/M SIB Displacemen lmmediate

前缀指令

前缀(Prefixes)的大小为1Byte,用于描述指令的前缀情况,他们可以被划分为5个集合:

66 -切换操作数大小

67 -切换地址大小(切换寻址模式)

F2/F3 -重复操作前缀

2E/36/3E/26/64/65 -修改默认段 FO 锁定前缀

注意:

“切换”的意思是将其在两种状态间来回转换,而并非特指某种状态将默认段修改为其他段的操作被称之为“修改默认段”

一个OpCode可能会有几个前缀,并每个类型只能同时出现一个,最多能有4个前缀。

如果有多个前缀,那么它们的顺序可以打乱

如果前缀不能对随它之后的 Op Code起作用,那么它就会被忽略

CODE域

Code域是指令的必须存在的部分,他携带者一下信息

​ 1.指令的助记符名称

​ 2.指令的操作数个数

​ 3.指令的操作数类型

​ 4.指令的操作数大小

​ 5.指令是否存在ModR/M域

MODR/M域

ModR/M域保存着指令的比较复杂的寻址方式。它携带者以下信息

​ 1.操作数名称(寄存器,内存地址)

​ 2.是否存在SIB域

​ 3.是否存在相对偏移

ModR/M域占一个字节,可以将这一个字节拆成一下格式

​ 89 D8 mov eax,ebx

​ D8=11011000

​ Mod Reg R/M

​ 11 011 000

解析13个字节的OPCODE

F026C7 84 91 AA000000 11000000

LOCK mov dword ptr ES: [edx*4 + ecx + 0xAA], 0x11

同操作码,不同指令

•83C0 01 ADD EAX,1

•83C8 01 OR EAX,1

•83D0 01 ADC EAX,1

•83D8 01 SBB EAX,1