补码:等于源码取反再加1
基础知识点
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:中断标志
寻址方式有哪些?
立即数寻址: 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 |
|
对应汇编代码
1 | -------main函数------------ |
调用约定
| 约定 | 参数传递的方式 | 清理栈的方式 | 特点 |
|---|---|---|---|
| _cdecl | 由右向左 | 调用者清理 | |
| _stdcall | 由右向左 | 被调用者清理 | |
| _fastcall | 由右向左 | 被调用者清理 | 左边开始的两个不大于4字节的参数分别放在ecx和edx寄存器,其余参数自右向左压栈传送 |
| _thiscall | 由右向左 | 被调用清理 | 通过ecx传递指针 |
C代码演示
1 |
|
cdecl调用约定
1 | max1(10, 20); |
stdcall调用约定
1 | max2(20, 30); |
fastcall调用约定
1 | max3(40, 50,60); |
thiscall调用约定
1 | OBJECT obj; |
三大结构
顺序结构
请忽略…
选择结构
if else
C代码
1 |
|
汇编代码
1 | 00731810 55 push ebp |
switch case 1
C代码
1 | int a = 4; |
汇编代码
1 | 00801700 55 push ebp |
switch case 2
C代码
1 | int a = 4; |
汇编代码
1 | 00731700 55 push ebp |
循环结构
1 | 00EE1810 55 push ebp |
汇编对应库
| 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 |
|
x64混合编程
使用.asm进行外联汇编
替换vs的编译器
使用外联汇编
1.添加.asm文件
asm文件中写入需要的函数
1 | ;参数1 rcx 参数2 rdx 参数3 r8 参数4 r9 |
2,添加汇编编译器
右击工程-生成依赖项-生成自定义-勾选masm(.targets,.props)项-确定
3.让asm文件加入连接选项
右击-属性页-常规
-从生成中排除-否
-项类型-Microsoft Macro Assembler
点击确定
4.C++使用汇编函数
1 |
|
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

