汇编中常见指令解释
这篇博文很有趣,竟然将hexo的content设置给冲垮了,感觉里面写了不可思议的东西,十分值得我去深挖!!!
1. pop 和 push
常见的汇编指令源码
pop机器码 | 含义 | push机器码 | 含义 |
---|---|---|---|
5f | pop edi | 57 | push edi |
5e | pop esi | 56 | push esi |
5d | pop ebp | 55 | push ebp |
5c | pop esp | 54 | push esp |
5b | pop ebx | 53 | push ebx |
5a | pop edx | 52 | push edx |
59 | pop ecx | 51 | push ecx |
58 | pop eax | 50 | push eax |
2. call
call (short) eax
1.将当前指令的下一条指令的地址压入栈中;
2.JMP到EAX这个地址。
call (long) eax
1.将CS压入栈中;
2.将当前指令的下一条指令的地址压入栈中;
2.JMP到EAX这个地址。
3. retn 和 retf
retn
1.将当前的ESP中指向的地址出栈;
2.JMP到这个地址。
retn k
1.将当前的ESP中指向的地址出栈;
2.JMP到这个地址;
3.弹出栈顶的k个字节的数据
retf
1.将当前的ESP中指向的地址出栈给EIP;
2.将当前的ESP中指向的地址出栈给CS;
2.JMP到这个地址。
4. jmp
jmp短跳
\xeb\xf6: jmp short $-8(其中$指代当前EIP)
在Asm2MachineCode(x86)中如果要尝试jmp short $-8,是不可能实现的,但是我们可以:
1. ImageBase(hex)设置为0012ff60 (此处为当前的EIP地址)
2. jmp short 0012FF58
jmp长跳
\xE9\x2B\xFF\xFF\xFF\xFF: jmp 0x0012FF30
在Asm2MachineCode(x86)中如果要尝试jmp short $-8,是不可能实现的,但是我们可以:
1. ImageBase(hex)设置为0012ff58 (此处为当前的EIP地址)
2. jmp 0012FE88
5. cmp
cmp op1, op2
ZF=1 这个简单,则说明两个数相等
当无符号时:
CF=1 则说明了有进位或借位,cmp是进行的减操作,故可以看出为借位,所以,此时oprd1<oprd2
CF=0 则说明了无借位,但此时要注意ZF是否为0,若为0,则说明结果不为0,故此时oprd1>oprd2
当有符号时:
若SF=0,OF=0 则说明了此时的值为正数,没有溢出,可以直观的看出,oprd1>oprd2
若SF=1,OF=0 则说明了此时的值为负数,没有溢出,则为oprd1<oprd2
若SF=0,OF=1 则说明了此时的值为正数,有溢出,可以看出oprd1<oprd2
若SF=1,OF=1则说明了此时的值为负数,有溢出,可以看出oprd1>oprd2
最后两个可以作出这种判断的原因是,溢出的本质问题:
两数同为正,相加,值为负,则说明溢出
两数同为负,相加,值为正,则说明溢出
故有,正正得负则溢出,负负得正则溢出
6. leave
其作用是删除函数的栈帧
leave在32位汇编下相当于
1 |
|
7. les
LES( load ES)指令的功能是:把内存中指定位置的双字操作数的低位字装入指令中指定的寄存器、高位字装入ES寄存器。
1 |
|
DEST=WORD PTR[SRC]
ES=WORD PTR[SRC+2]
DEST赋值为SRC处双字的低位;
ES赋值为SRC处双字的高位;
8. 比较
A(above)大于、B(below)小于、E(equal)等于,用于比较无符号数
G(great)大于、L(less than)小于、E(equal)等于,用于比较带符号数
其实这些地方也是漏洞点,有时候比较没有对是否有符号进行确定,所以可能会出问题。
9. lods/stos
lodsb指令,将esi指向的地址处的数据取出来赋给AL寄存器, esi=esi+1;
lodsw指令则取得是一个字。
lodsd指令,取得是双字节,即mov eax,[esi],esi=esi+4;
stosb指令,将AL寄存器的值取出来赋给edi所指向的地址处。mov [edi],AL;edi=edi+1;
stosw指令去的是一个字。
stosd指令,取得是双字节,mov [edi],eax;edi=edi+4;
数学指令
add src, dest
将 src 添加到 dest 。
sub src, dest
从 dest 中减去 src 。
imul src, dest
将 dest 乘以 src 。
idiv divisor Divide rdx:rax
将 rdx:rax 除以 divisor 。将商存储在 rax 中,并将余数存储在 rdx 中。
shr reg
shl reg
将 reg 向左或向右移动 cl 中的值( rcx 的低 8 位)。
ror src, dest
将 dest 向左或向右循环 src 位。
cmp src, dest
设置与 dest 是否小于、等于或大于 src 相对应的标志
其他指令
endbr64
如果我们使用gdb查看程序的汇编代码,我们会发现第一句往往是endbr64
。这个语句实际上是一个安全语句。为了对付ROP、JOP、COP攻击技术。
参考
https://mp.weixin.qq.com/s/htJ73BQK5zRTReHRaAqwxg
0x1. ARM64
mov
传送指令:将立即数传送到寄存器/内存单元,或互传内存单元和寄存器两者中的数据。
例如:mov x0, #0x1 / mov $0x1, %rdi 将立即数0x1移动到寄存器x0/rdi中
mvn
该指令与mov唯一不同的是:需要对操作数进行按位取反。
str
store register. 将寄存器中的数据存到内存单元中。
例如:str x0, [sp, #0x28] 将寄存器x0中的数据存到地址sp+0x28处
stp
store pair of registers.
例如:stp x29, x30, [sp, #0x70] 先将x29存到地址sp+0x70处,再将x30存到sp+0x78处 (Tip1)。
{ %note info % }
Tip1: 64位下,寄存器大小为8bytes
{ %endnote% }
ldr
load register. 从内存中读取数据,存到寄存器中。
例如:ldr x1, [sp, #0x30] 将内存地址sp+0x30处的数据加载到x1中。
ldp
load pair of registers.
例如:ldp x29, x30, [sp, #0x70] 将内存地址sp+0x70处的数据加载到x29中,再将sp+0x78(Tip1)处的数据加载到x30中。
b
branch. 无条件跳转。
例如:b 0x1b6b79cf8 跳转到0x1b6b79cf8处继续执行。
bl
branch with link. 将下一条指令地址copy到lr中,然后跳转。由于保存了下一条指令地址(相对于pc),所以可实现子程序的返回,而b只能单纯的实现跳转,不能实现子程序返回。
例如:
1 |
|
当程序执行完bl指令时,lr中的内容应该是是bl下一条指令的地址,即0x102b7d8a8。通过控制台查看lr中的内容如下:
1 |
|
cbz
compare and branch on zero. 比较操作数是否为0,若为0则跳转。
例如:cbz w24, 0x1b6b798f4 若w24 (x24寄存器低32位) 中的数据为0,则跳转到0x1b6b798f4处执行 (Note1)。
Note1: 跳转地址需要在cbz/cbnz指令之后的4~130字节内。
cbnz
compare and branch on non-zero. 操作数不为0,则跳转。
例如:cbnz w27, 0x1b73c9e2c 若w27中的数据不为0,则跳转到0x1b73c9e2c处执行 (Note1)。
lsl
logical shift left. 左移位。
例如:str x0, [x1, x2, lsl #2] 将x2中的值左移两位,再加上x1中的值,得到的结果为一个内存地址,最后将x0中的数据存到该内存中。
lsr
logical shift right. 右移位。
cmp
compare. 比较两个数是否相等,首先将两个数相减,若差为0,则ZF (Tip2) 为1,即两者相等。
cmp w8, #0x3 通过判断w8中的值与0x3的差值是否为0,即ZF (Tip2) 是否为1,来比较两者是否相等。
Tip2: ZF是零标志位寄存器。它记录相关指令执行后,其结果是否为0。若结果为0,则ZF=1;若结果为1,则ZF=0。
adrp
form pc-relative address to 4KB page. 先将操作数左移12位,再将pc的低12位清零 (即3个十六进制位清零,得到一个页对齐地址),最后两者相加,赋值给寄存器。
例如:
1 |
|
执行adrp时,先将操作数270332 (十六进制为0x41ffc) 左移12位得到0x41ffc000,再将pc (0x1b703f074) 低12清零得到 0x1b703f000,最后两者相加得到0x1f903b000赋值给x20。通过控制台查看x20中的内容:
1 |
|
adr
load a program-relative or register-relative address into a register. 是一条伪指令,遇到该指令时汇编器会生成一条add或sub指令,来计算pc和操作数的和或者差,最后将得到的结果,放在寄存器中。
例如:
1 |
|
brk
breakpoint instruction. 用于生成断点指令异常。
tbz
test branch zero. 测试位为0,则跳转。
tbz w24, #0x6, 0x19307005c ; 即w24第6位,若为0,则跳转到0x19307005c执行
w24 二级制数为:0b00000111000000000000100000000110
tbnz
test branch no zero. 测试位不为0,则跳转。
tbnz w24, #0x6, 0x19307005c ; 即w24第6位,若不为0,则跳转到0x19307005c执行
w24 二级制数为:0b00000111000000000000100000000110
0x2. AT&T
test
将指令后的两个数进行按位与操作,其结果影响ZF。通常用来判断某个数是否为0/nil。
例如:
testq %rax, %rax ; 若%rax中的值为0,则ZF=1;反之,ZF=0,q表示四字长
sete
取ZF中的值,放入目标寄存器中。
例如:
1 |
|
setne
取ZF中的值,然后取反,再放入目标寄存器中。
例如:setne %rax 若ZF=1,则%rax设为0;反之设为1
je / jz
je 和 jz这两条指令虽然名字不同,但做的事情都是一样的。即ZF=1,则跳转。
例如:
1 |
|
jne / jnz
jne 和 jnz这两条指令做的事情也都是一样的。即ZF=0,则跳转。
js
jump if sign. 若结果为负数,即SF=1,则进行跳转。
SF: 符号标志位
它记录相关指令执行后,其结果是否为负。若结果为负,则SF=1;若为非负,则SF=0。
nop
no operation. 不执行任何操作。是一条单字节指令。 Opcode 90
ud2
undefined instruction. 该指令用于生成一个无效操作码。当CPU试图执行无效或未定义的操作码时,将发生无效的操作码异常。UD2指令除了引发无效的操作码异常外,与NOP指令相同。
这里的异常是指CPU在发生“错误”时生成的。大多数情况下,有些异常并不是真正的错误,而是中断的一种类型。比如: Page Fault。异常分类如下:
Faults (错误):这些错误可以被纠正,程序可以像什么都没有发生一样继续运行。
Traps (陷阱):陷阱指令执行后会立即被报告。
Aborts (终止):一些严重的不可恢复的错误。
ret
该指令用于子程序的返回。将栈顶数据(返回地址)弹出,赋值给 ip 寄存器,此时 sp 会增加一个内存单元大小,来释放栈空间。
{ %note info % }
栈底 [ … | … , parameters, return address | previous frame pointer, %rbp ] 栈顶
pop %rbp 👇
栈底 [ … | … , parameters, return address ] 栈顶
ret 👇
栈底 [ … | … , parameters ] 栈顶
caller release parameters 👇
栈底 [ … | … ] 栈顶
{ %endnote% }
xor
常常使用这个指令来判断两个地址中的数据是否一致:
1 |
|