寄存器和汇编指令基础

0X1寄存器

通用寄存器

  1. AX,BX,CX,DX

    AX是累积寄存器,相对于其他寄存器,在运算方面比较常用,是很多加法乘法指令的缺省寄存器。

    BX是基地暂存器,作为内存偏移指针使用,在内存寻址时存放基地址。

    CX是计数暂存器,是重复(REP)前缀指令和LOOP指令的内定寄存器。

    DX是资料暂存器,用来放整数除法产生的余数。

  2. IP,SP,BP,

    IP是存储CPU下次所执行的指令地址(存放指令偏移地址)。

    SP是指针的寄存器,用于堆栈操作。被形象地称为栈顶指针。

    BP是基址指针,一般指向当前栈帧的栈底地址。

    他经常被称为高级语言函数调用的“框架指针”,在逆向的时候,经常可以看见一个标准的函数起始代码:

    1
    2
    3
    push ebp ;保存当前ebp
    mov ebp,esp;EBP设为当前栈帧的栈底指针
    sub esp , n ;预留n个字节给函数临时变量
  3. SI,DI

    SI和DI是变址寄存器。

    SI是源变址寄存器,在内存操作指令中作为“源地址指针”使用。

    DI是目的变址寄存器,在内存操作指令中作为“目的地址”使用。

  4. 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关于栈区的演示

首先,高级语言的栈是由高地址向低地址增长的,代码在低地址,栈区在高地址。这样会向中间增长。

  1. sp永远指向栈顶元素。不是指向没有数据的空地址。

    push时,先减四后赋值

    ebp永远指向栈底元素。他一般存储了上一个函数的ebp。

  2. 进入函数中,先push ebp

    再mov ebp,esp 这时俩寄存器都指向保存的上个函数的ebp的值。

    下面esp减去一大块,为临时变量流出空间。

  3. 离开函数时执行leave时

    他的作用与上面的相反,相当于

    mov esp,ebp 让esp指向当前的栈底

    pop ebp 将栈顶(esp指向的内存)的值给ebp这样,就恢复了上个函数调用此函数至少得值,esp因为弹栈而加四,然后指向的是返回地址。

  4. 随后执行ret,ret指令相当于

    pop eip 即将栈顶的值(esp指向的内容)复制给eip指令寄存器。esp+4

系统栈栈底和栈帧栈底

1
2
3
4
5
6
1.首先应该明白,栈是从高地址向低地址延伸的。
2.每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息
3.寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(低地址)
4.注意:EBP指向当前位于系统栈最上边一个栈帧的底部,而不是系统栈的底部。
5.严格说来,“栈帧底部”和“栈底”是不同的概念;
6.ESP所指的栈帧顶部和系统栈的顶部是同一个位置。

像下面的图是栈帧和系统栈的关系。

image-20220304212511289
百度百科中也提到栈帧是对应着未完成的函数,比如我运行main函数里面调用了其他函数,那么就是栈帧1(也就是main)跳到栈帧2,如果这个函数里再嵌套进其它函数,那么就继续往下开栈帧。

而并列的函数比如main里有两个函数的话栈ebp是不会变的,只是把esp放出去再“收”回来罢了。