Skip to content

强化阶段笔记

1. x86 汇编

1.1. 汇编语言基础

  • 通用寄存器:EAX,EBX,ECX,EDX。可以存放任何数据。

    • A, B, C, D 为标号,E = Extended 表示 32 bit,X 表示未知。
    • 可以只用低 16 位,即 AX, BX, CX, DX。
    • 低 16 位又进一步分为低 8 位和高 8 位,分别用 AL, AH, BL, BH 等表示。
  • 变址寄存器:ESI,EDI。用于线性表(数组)和字符串处理。

    • I = IndexS = SourceD = Destination
  • 堆栈寄存器:EBP,ESP。实现函数调用。

    • EBP 为栈基(栈底)指针,ESP 为栈顶指针。
  • 寄存器常见用途

    image-20250812102055875

1.2. 数据传送指令

以下都用 Intel 格式为例(前面是目的数后面是源数)。若是 ATMT 格式则反过来。考试中会有说明。

  1. mov
    • 格式:mov 目的操作数, 源操作数
    • 将源数(寄存器、主存或立即数)送至目的数(寄存器或主存)。
    • 目的数不能为立即数,也不能直接指向内存地址,例如 mov [1234H], eax。必须用寄存器辅助
  2. push
    • 格式:push 操作数
    • 将操作数压入栈。常用于函数调用
    • RECALL 压栈过程:ESP 先减 4,再将操作数送 ESP 地址。出栈先读出 ESP,再 ESP 自增 4。
  3. pop
    • 格式:pop 目的操作数
    • 和 push 相反。将栈顶数出栈,送到目的操作数(寄存器或主存)。目的数不能为立即数。

1.3. 算术运算和逻辑指令

  1. add/sub
    • 格式:add/sub 目的操作数, 源操作数
    • 目的数加上/减去源操作数,结果送目的数。注意减法时目的数作为被减数。目的数不能为立即数。
  2. inc/dec
    • 格式:inc/dec 操作数
    • 将操作数(寄存器或主存)自增或自减 1.
  3. imul
    • 格式:(1) imul 目的操作数, 源操作数,(2) imul 寄存器, 操作数 1, 操作数 2
    • 带符号数相乘。
      • 格式 1,两个数相乘,结果送目的数(寄存器或主存)。
      • 格式 2,操作数 1 和 2 相乘,结果送寄存器。第一个数只能是寄存器
    • 溢出时,置 OF = 1,CPU 会调用溢出异常处理程序
  4. idiv
    • 格式:idiv 操作数
    • 带符号整数除法。只有一个操作数作为除数被除数默认为 eax
    • eax 视为 64 位,除法结果商送 eax,余数送 edx
  5. and/or/xor
    • 格式:and/or/xor 目的操作数, 源操作数
    • 两个操作数按位与/或/异或运算。结果送目的数(寄存器或主存)。
  6. not/neg
    • 格式:not/neg 操作数
    • 将操作数按位取反/取负值。单操作数指令,操作数不能为立即数。
  7. shl/shr
    • 格式:shl/shr 目的操作数, 源操作数
    • 将目的数左移/右移指定位数,用源数给出。源数一般为立即数,有时也用寄存器。
    • 目的数一般为寄存器,不能为立即数。

1.4. 控制流指令

x86 维护一个寄存器 IP,等效于 PC。IP 不能直接操作,但可以用控制指令来修改。

  1. jmp
    • 格式:jmp label
    • 无条件跳转指令。将 IP 直接修改到 label 所指示的指令地址。
  2. j+condition
    • 格式:je/jne/jz/jnz/... label
    • 有条件跳转指令。满足条件时等价于 jmp,否则不执行任何操作。
    • 本质上是对 PSW 中的标志位进行判断。所以通常需要在上一条指令用 cmp/test 指令修改 PSW。
  3. cmp/test
    • 格式:cmp/test 目的操作数, 源操作数
    • cmp:比较目的操作数和源操作数,根据比较结果修改 PSW。
    • test:两个操作数按位与运算根据结果是否为 0 修改 ZF
  4. call/ret
    • 格式:call [子程序]ret
    • 实现子程序的调用和返回。
    • call:先将当前 IP 入栈,然后无条件跳转到子程序入口地址。
    • ret:从栈顶弹出保存的 IP,然后无条件跳转到指向的地址。

1.5. 扩展:函数调用与栈帧

  • 栈帧:函数运行过程中为了存储形参和一些局部变量,需要占据的一段栈地址范围

  • enter 和 leave 指令:enter 用于为被调用的函数创建栈帧,修改 ebp 和 esp。leave 在函数返回时清理栈帧,恢复调用前的 ebp 和 esp。由基本的 mov 和 push 指令组成。

    • enter 指令
    asm
    ; enter
    ; num_bytes 为被调用函数需要的栈空间大小
    
    push ebp             ; 当前 ebp 压栈保存
    mov ebp, esp         ; 将当前 ebp 置为 esp
    sub esp, num_bytes   ; esp 减去 num_bytes, 指向新的栈顶. num_bytes 为新函数的栈帧大小
    
    ; 新的 ebp 指向被保存的原来 ebp 的栈单元地址
    • leave 指令
    asm
    ; leave
    
    mov esp, ebp         ; 将当前 esp 置为 ebp
    pop ebp              ; 被保存的 ebp 出栈, 赋给当前 ebp, 同时 esp 自增, 恢复原来的状态
  • 一般函数调用过程

    asm
    ; 被调用函数 sub
    
    sub:
    enter
    ...        ; 其他代码
    ...
    leave      ; 清理栈帧
    ret        ; 返回
    
    ; 调用方 main
    
    main:
    ...        ; 其他代码
    call sub   ; 调用 sub