汇编学习
对于一名安全工程师,不会不懂汇编,只能说是一个半吊子的安全工程师。汇编的学习不是为了写汇编,而是为了读懂汇编进而读懂逻辑,进而攻击或者防护。
此贴为原创博客,如转发请注明出处,多谢!!!!
进制
- 计算机使用的是二进制,只有0和1,理解什么是进制,进制间的换算。
- 二进制转十六进制是必须要掌握的。
- 什么是大端格式(数据高位在低位),什么是小端格式(相反)。
进制之间的加减乘除原理都是相同的,只不过生活中我们习惯了十进制,所以思维惯性会不适应二进制或者十六进制的加减乘除。熟练的对十六进制的加减运算是一个很必要的功底。
数据宽度
- 位 bit
- 字节 byte
- 字 word (2 bytes)
- 双字 double word (4 bytes)
计算机中只有后三种宽度。64位的汇编指令会不会有8 bytes,我就不知道了。
有符号数,无符号数
这两种需要掌握。
- 对于有符号数最高位为1就是负数,为0就是正数。
- 无符号数当然没有负数了,所以最高位也是需要换算成数值的。
- 有符号数的负数如何表示,需要掌握。
有符号数的编码规则:
原码:最高位为符号位,其余位的绝对值
反码:正数反码和原码相同,符号位1时,其余位对原码取反
补码:正数与原码相同,符号位1时,其余位对反码加1
举例: -1 (byte)
原码: 1000 0001
反码: 1111 1110
补码: 1111 1111
位运算
与运算(and &)
或运算(or |)
异或运算(xor ^)
非运算(not ~)
左移运算(shl <<)
右移运算(shr >>) shr:高位补0 sar:高位补符号位
计算机只会位运算,加减乘除都是通过位运算进行操作的。
举例加法:
- 亦或操作得到无进位的结果
- 与操作的结果判断是否有进位
- 如果有进位,与操作结果左移1位,然后与亦或操作结果做加法,以此类推
汇编学习工具
DTDBUG,自行搜索下载即可。
通用寄存器
CPU中存取数据的地方。
32位:EAX、EBX、ECX、EDX、ESP、EBP、ESI、EDI
16位:AX BX CX DX SP BP SI DI (低16位)
8位: AL BL CL DL (低8位)
AH BH CH DH (低16位中高8位)
16位和8位仅仅是32位寄存器的低位
内存
每一个进程都有4GB的内存(2的32位),逻辑内存。
内存地址是一个32的地址编号。0x00000000 ~ 0xffffffff
5种寻址的表现形式:
- 立即数 [0x11111111]
- 寄存器 [EAX]
- 寄存器+立即数 [EAX+4]
- 寄存器+寄存器*{1,2,4,8} [EAX+ECX*2]
- 寄存器+寄存器*{1,2,4,8} +立即数 [EAX+ECX*2+100]
堆栈
就是一块内存,用来做堆栈,先进后出。
堆栈从高地址往低地址使用,如果用完堆栈,会造成堆栈溢出。
汇编指令
- MOV指令
立即数到寄存器
寄存器到寄存器(宽度必须一致)
MOV EAX, EBX
立即数到内存(宽度必须一致)
MOVE BYTE(WOED/DWORD) PTR DS:[0x********], 1
寄存器到内存(宽度必须一致)
内存到寄存器(宽度必须一致)
内存到内存(不允许) - ADD指令 加法指令
ADD EAX, ECX
内存到内存(不允许) - SUB指令 减法指令
内存到内存(不允许) - AND指令
内存到内存(不允许) - OR指令
内存到内存(不允许) - XOR指令
内存到内存(不允许) - NOT指令
- MOVS指令
移动数据,内存到内存
MOVS BYTE PTR ES:[EDI], BYTE PTR DS:[ESI] 简写 MOVSB(/W/D)
… WORD/DWORD …, WORD/DWORD …
复制一次内存,ESI,EDI将会自动增加字长
ELF标志寄存器,当DF标志位为0时,ESI,EDI增长;当DF为1时,ESI,EDI递减 - STOS指令
把EAX/AX/AL的值存储到EDI指定的内存中 简写 STOSB(/W/D)
复制一次内存,EDI将会自动增加字长
ELF标志寄存器,当DF标志位为0时,EDI增长;当DF为1时,EDI递减 - REP指令
可重复执行 MOVS 或者 STOS指令,重复的次数为ECX的值
MOV ECX,10
REP MOVSD BYTE PTR ES:[EDI], BYTE PTR DS:[ESI]
一次性拷贝16个字节 - PUSH 指令
PUSH 立即数/寄存器/内存
将数据压入堆栈,ESP寄存器将会指向栈顶 - POP指令
POP EAX
与PUSH相反 - JMP指令
无条件跳转到相应地址,并且修改EIP寄存器(指向下一条要执行的指令)
JMP 立即数/寄存器/内存
JMP的本质其实就是修改EIP - CALL指令
CALL指令的作用
1、将当前指令下一行指令压堆栈栈
2、修改EIP为下一条要执行的地址
- RETN指令
栈顶的值出栈并修改EIP为出栈的值。这就是它实质性的作用。
堆栈平衡
调用函数之后,堆栈应该恢复到调用之前的状态。
- 函数退出后恢复平衡,外平栈
PUSH 1
CALL 0x11111111
ADD ESP, 4 //为了平衡堆栈 - 函数退出前恢复平衡,内平栈
PUSH 1
CALL 0x11111111
…
0X11111111: …
…
0X11111118: RETN 4 //恢复堆栈平衡
ESP寻址
ESP寻址有弊端,当调用的函数(CALL指令)嵌套太复杂,很难维护。因为我们知道需要维护函数的栈平衡,所以当更深层的函数调用造成栈的复杂度就会越来越高。
此图仅仅是演示每当进入一个CALL指令,如果PUSH了很多数据到堆栈,那在堆栈中使用就很困难,而且每当出函数需要保持堆栈平衡。
EBP寻址
EBP是基址寄存器
这里引用他人的一篇帖子,写的很好。(这篇帖子的上半段我感觉说的不太对,从这篇帖子的个人理解开始还是写的很好的)
https://blog.csdn.net/u013152417/article/details/44308289
所以基址选址就方便多了,而且恢复简单,一目了然。
JCC指令
主要是更改EIP指令寄存器。JCC指令的目的就是了解和掌握标记寄存器EFLAGS
运算相关的指令都会影响标志寄存器。
- CF:无符号数的计算结果有没有溢出(发生进位或者借位)
- PF:奇偶校验位,如果最低有效字节统计1的个数为偶数时为1,否则为0。通常用作传输过程中的奇偶校验。
- ZF:如果运算的结果为0,则为1
- SF:符号位,判断运算结果是正(0)还是负数(1)
- OF:有符号数,如果溢出则置位1
- DF:方向标识位,上面有所描述,MOVS, STOS指令等
借鉴一下图:用到时查查就可以
https://blog.csdn.net/fengshh2301/article/details/53327195
JCC跳转只和Flag相关
判断两个数是否相等:只影响标准寄存器
CMP EAX,EBX (CMP和SUB的操作类似,只不过没有赋值到EAX)
TEST EAX, EAX (TEST 和 AND类似,也是不赋值到EAX中)
发表的第一篇blog,希望多多支持,后续把自己的一些心德也慢慢更新下。