汇编重修

LinX Lv1

感觉自己的汇编很依托,当时入门太匆忙了,假期重新学了一点(可能依旧依托

前置知识

原码反码补码(重点是负数

1
2
3
4
5
6
7
原码:最高位为符号位    
反码:正数反码与原码相同
符号位为1,其余的部分取反
补码:正数补码与原码相同
符号位为1,其余的部分取反,后+1
储存方式:正数原码储存
负数补码储存

一些运算

  • 与运算 AND(&) 两个数都为1时,结果为1
  • 或运算 OR(|) 只要有一个为1,结果为1
  • 异或 XOR(^) 不一样的时候是1 xor eax,eax 清零寄存器
  • 非运算 NOT(~) 0就是1,1就是0单目运算
  • 左移 SHL/SAL(<<) 高位丢弃,低位补0 (左边高位,右边低位)
  • 右移 高位丢弃(>>)
    SHR 高位补0
    SAR 最高位补符号位,保持符号不变
  • 循环左移ROL 左移,最左边的到最右边 rol eax,1
  • 循环右移ROR 右移,最右边的到最左边 ror eax,1

寄存器

1
2
3
4
5
6
7
8
9
32位 16位 8位    
EAX AX AL(EAX低8位,后8位)AH(EAX的高8位,前8位) //
ECX CX CL CH
EDX DX DL DH
EBX BX BL BH
ESP SP
EBP BP
ESI SI
EDI DI

在进行数据传送或者运算时,要注意指令的两个操作对象的位数应该是一致的,例如:

1
2
3
4
5
6
mov ax, bx
mov bx, cx
mov ax, 18H
mov al, 18H
add ax, bx
add ax, 20000

以上指令都是正确的。
而:

1
2
3
4
mov ax, bl (在8位寄存器和16位寄存器之间传送数据)
mov bh, ax (在16位寄存器和8位寄存器之间传送数据)
mov al, 20000 (8位寄存器最大可存储值为255的数据)
mov al, 100H (将高于8位的数据加到8位寄存器中) (一个字节为两个16进制位表示)

以上都是错误的指令,错误原因是指令的两个操作对象位数不一致。
(这里是一些以前没注意的点)

1.在add的时候,有时候会遇到溢出的情况,需要把溢出舍掉,留低位,比如:

1
2
mov ax,9000H;
add ax,ax

add之后,ax中的值原本是12000H,但是真正储存的2000H,1被舍了。
还有

1
2
mov ax,9090H;
add al,al;

因为al中只能存储两位16进制的数据,所以哪怕现在,本应该al的值为120H,但是这里1被舍弃。
最终ax为9020H
在上面这个情况中,al作为一个独立的8位寄存器使用

2.8086cpu给出物理地址的方法

8086采用在内部用两个16位地址合成的方法来形成一个20位的物理地址。
流程如下
alt text
地址加法器的操作:物理地址=段地址*16(左移4位)+偏移地址
为什么要用这种方式加密,这里王爽老师给了一个很通俗的例子
alt text
这样做的好处在于,增加了cpu的寻址能力。如果只简单的发出,只能传16位的地址。但是通过这样的方式,可以送出20位的地址。

3.段的概念

并不是内存分段了,而是我们可以用分段的方式管理内存(因为在2中,我们知道物理地址是由 段地址*16+偏移地址形成的)
cpu会用不同的
段寄存器4个:CS DS SS ES
区别程序和数据的方式:如果段地址是ds,则存放数据,如果段地址是cs,则存放指令

数据:[[rax]][数字]
栈:SS:SP表示栈顶(32位仍沿用这一方式,64位则用rsp代替了这一概念)
32位:SS提供基址,ESP提供栈的偏移地址
64位:RSP表示栈顶

压栈过程:esp=esp-4,存入数据
fs段寄存器:指向线程环境块(TEB)
fs $+20 进程id 线程id
fs $+30 进程环境块(TEB)指针
PEB结构:
$+2 是否正在被调试
$+68 NtGlobalFlag (拖入调试的话会变成0x70)

数据传输指令

push 压栈
pop 出栈
xchg 交换两个操作数的值
lea 取地址(相当于c的&

算术运算

add 加
sub 减
mul 无符号乘法

默认eax为被乘数,操作数为乘数
mov ebx eax=eax*ebx 结果储存在ebx中

imul 有符号乘法

单操作数:默认被乘数为eax ,同mul
双操作数:imul ebx,ecx 结果储存在ebx
三操作数:imul data,srm,imm 计算srm*imm,结果储存在data

div 无符号除法

被除数默认是edx:eax,操作数为除数,结果:

  • 商储存在eax
  • 余数储存在edx(需要确保edx被清零,或正确设置)

mov edx,0
mov eax,15
mov ecx ,3
div ecx
结果:
eax=5
edx=0

idiv 有符号除法

和div基本相同,但是需要在开启前设置cdq指令
mov eax,-15
cdq ;拓展符号位到edx
mov ecx,3
idiv ecx
结果:
eax=-5
edx=0
inc 自增(increase+1) inc eax
dec 自减(decrease-1) dec eax

逻辑比较指令

cmp eax,ebx ;对操作数进行减法运算

如果eax==ebx,则ZF=1,可以通过je,jz跳转
alt text
cmp影响的标志位有CF,ZF,SF,OF,PF,AF
test eax,ebx ;按位与,只更新标志寄存器不储存结果(ZF)

条件跳转指令

常用:
je/jz 相等,ZF=1
jne/jnz 不相等,ZF=0
jg 大于跳转
jl 小于跳转
jge 大于等于跳转
jle 小于等于跳转
loop 循环跳转
int 调用中断
iret 从中断返回

alt text
call:把下一条指令压栈和jmp
ret:返回到call下一行的执行代码

栈操作指令

push
pop
pushad:eax,ecx,edx,ebx,esp,ebp,esi,edi
popad:反pushad弹出
pushfd:压栈标志寄存器EFLAGS
popfd:反pushad弹出
enter:设置栈帧,将ebp压栈,设置新的ebp为局部变量并分配空间
leave:恢复栈帧,将ebp的值恢复到esp然后弹出原ebp

mov esp,ebp
pop ebp

字符串操作指令

1
2
3
4
mov ecx,length
mov esi,source ;原字符串地址
mov edi,destination ;目标字符串地址
rep movsb

复制字节
常用于memcpy,strcpy
rep重复下面的操作直到ecx=0

1
2
3
4
5
6
mov esi,offset str_userinput ;用户输入的字符串
mov esi,offset str_password ;目标字符串
mov ecx,length ;设置比较的字节数
cld ;清除方向标志位
repo cmpsb ;比较两端内存的字符串
je xxxxx ;如果相等。。。

比较字符
常用于strcmp,memcmp

windows使用小端存储

  • Title: 汇编重修
  • Author: LinX
  • Created at : 2026-02-01 13:41:39
  • Updated at : 2026-02-01 13:49:26
  • Link: https://redefine.ohevan.com/2026/02/01/汇编/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments