寄存器和汇编指令基础和对于栈区的思考
寄存器和汇编指令基础
0X1寄存器
通用寄存器
AX,BX,CX,DX
AX是累积寄存器,相对于其他寄存器,在运算方面比较常用,是很多加法乘法指令的缺省寄存器。
BX是基地暂存器,作为内存偏移指针使用,在内存寻址时存放基地址。
CX是计数暂存器,是重复(REP)前缀指令和LOOP指令的内定寄存器。
DX是资料暂存器,用来放整数除法产生的余数。
IP,SP,BP,
IP是存储CPU下次所执行的指令地址(存放指令偏移地址)。
SP是指针的寄存器,用于堆栈操作。被形象地称为栈顶指针。
BP是基址指针,一般指向当前栈帧的栈底地址。
他经常被称为高级语言函数调用的“框架指针”,在逆向的时候,经常可以看见一个标准的函数起始代码:
1
2
3push ebp ;保存当前ebp
mov ebp,esp;EBP设为当前栈帧的栈底指针
sub esp , n ;预留n个字节给函数临时变量SI,DI
SI和DI是变址寄存器。
SI是源变址寄存器,在内存操作指令中作为“源地址指针”使用。
DI是目的变址寄存器,在内存操作指令中作为“目的地址”使用。
R8~R15
内容不详,暂时还用不到这些知识,才疏学浅,要是有大佬愿意补充的话,感激不尽。
标志寄存器
PFLAGES寄存器包含一系列的状态为位(或者标志)处理器使用这些位(或者标志)来表示算术,逻辑或者比较操作的结果。他还包含一些主要由操作系统使用的控制位。这些标志位会影响条件跳转指令是否跳转。
以下是RFLAGS寄存器中位的组织结构。
位的位置 | 名称 | 符号 |
---|---|---|
0 | 进位标志(Carry Flag) | CF |
1 | 保留 | |
2 | 奇偶校验标志(Rarity Flag) | PF |
3 | 保留 | |
4 | 辅助进位标志(Auxiliary Carry Flag ) | AF |
5 | 保留 | |
6 | 零标志(Zero Flag) | ZF |
7 | 符号标志(Sign Flag) | SF |
8 | 陷阱标志(Trap Flag) | TF |
9 | 中断启用标志(Interrupt Enable Flag) | IF |
10 | 方向标志(Direction Flag) | DF |
11 | 溢出标志(Overflow Flag) | OF |
21 | ID标志 | ID |
标志寄存器对于电脑的用处特别大,很多指令的执行都是依靠标志寄存器
11位到21位中除了15位保留了,其他的都有含义,我才疏学浅并不能理解,这里就不放上去了。
PS:对于一些寄存器前面的E或者R都是表示额外附加,EAX表示32位的寄存器,RAX表示64位的寄存器,其他的寄存器同理
0x2汇编基础指令
通用数据传送指令
指令名称 | 功能 | 备注 |
---|---|---|
mov | 传送赋值 | mov a,b将b的值赋值给a |
push | 压栈 | 当前栈帧的栈顶减4,然后将数据压入当前栈帧。 |
pop | 出栈 | 将当前栈顶(SP) |
xchg | 交换 | 交换两个寄存器或者寄存器和地址的值,但不能是两内存。 |
这里面的指令用得比较多,后面我会对push和pop对栈区的作用进行演示
标志寄存器传送指令
指令名称 | 功能 | 备注 |
---|---|---|
pushf | 标志进栈 | push the flag |
popf | 标志出栈 | pop the flag |
条件跳转(一般配合CMP使用)
指令名称 | 指令英文全拼 | 功能 | 备注 |
---|---|---|---|
jmp | jump | 无条件跳转 | 强制跳转 |
jz/je | jump if zero,or equal | 结果为0(相等)跳转 | 检测Z位 |
jnz/jne | jump if not zero,or not equal | 结果不为0(不相等)跳转 | 检测Z位 |
js | jump if sign | 结果为负跳转 | 检测S位 |
jns | jump if not sign | 结果为正跳转 | 检测S位 |
jb | jump if below | 比较小于跳转 | 检测C位 |
jnb | jump if not below | 比较大于或等于跳转 | 检测C位 |
子程序
指令名称 | 指令英文全拼 | 功能 |
---|---|---|
call | CALL | 调用子程序或者函数,CALL 指令将其返回地址压入堆栈,再把被调用过程的地址复制到指令指针寄存器 |
ret | return | 相当于POP eip 将当前栈帧的栈顶的值赋给eip寄存器,然后esp+4 |
leave | leave | mov esp,ebp 然后再 pop ebp。相当与恢复调用函数前的栈帧 |
循环指令
LOOP loop
算数指令
指令名称 | 功能 |
---|---|
add | 加法 |
sub subr | 减法 |
mul | 乘法 |
div divr | 除法 |
比较指令
指令名称 | 功能 |
---|---|
cmp | cmp a,b 比较a和b是否相等 |
空指令
指令名称 | 功能 |
---|---|
nop | 这个指令在汇编中的作用是空指令,意味着什么都不做的意思,一般用来控制CPU的时间周期,达到时钟延时的效果 |
0x3关于栈区的演示
首先,高级语言的栈是由高地址向低地址增长的,代码在低地址,栈区在高地址。这样会向中间增长。
sp永远指向栈顶元素。不是指向没有数据的空地址。
push时,先减四后赋值
ebp永远指向栈底元素。他一般存储了上一个函数的ebp。
进入函数中,先push ebp
再mov ebp,esp 这时俩寄存器都指向保存的上个函数的ebp的值。
下面esp减去一大块,为临时变量流出空间。
离开函数时执行leave时
他的作用与上面的相反,相当于
mov esp,ebp 让esp指向当前的栈底
pop ebp 将栈顶(esp指向的内存)的值给ebp这样,就恢复了上个函数调用此函数至少得值,esp因为弹栈而加四,然后指向的是返回地址。
随后执行ret,ret指令相当于
pop eip 即将栈顶的值(esp指向的内容)复制给eip指令寄存器。esp+4
系统栈栈底和栈帧栈底
1 | 1.首先应该明白,栈是从高地址向低地址延伸的。 |
像下面的图是栈帧和系统栈的关系。
百度百科中也提到栈帧是对应着未完成的函数,比如我运行main函数里面调用了其他函数,那么就是栈帧1(也就是main)跳到栈帧2,如果这个函数里再嵌套进其它函数,那么就继续往下开栈帧。
而并列的函数比如main里有两个函数的话栈ebp是不会变的,只是把esp放出去再“收”回来罢了。