汇编

1. 指令
  1. mov
  • 两个操作数必须是同样的大小。
  • 两个操作数不能同时为内存操作数。
  • 指令指针寄存器(IP、EIP 或 RIP)不能作为目标操作数。
    • 64位模式 可以将8位 16位 32位常数送入 高位清零 32位寄存器送入会使高位清零
  1. movzx 全零扩展 无符号整数

  2. movsx 全符号扩展 有符号整数 不能为常数

  3. lahf 将标志寄存器低八位加载到ah

  4. sahf 将ah保存到标志寄存器

  5. xchg 交换操作数 不能同时为内存操作数 不能为立即操作数,不影响标志位

  6. inc dec 加一 减一 不会影响进位标志位

  7. add 目的加源值放在目的 操作数与mov相同

  8. sub 目的减源值放在目的 操作数与mov相同

  9. NEG 取反+1 得到补码

  • INC 和 DEC 指令不会影响进位标志位。在非零操作数上应用 NEG 指令总是会将进位标志位置 1。
  1. JMP 指令无条件跳转到目标地址,该地址用代码标号来标识,并被汇编器转换为偏移 量

  2. LOOP 循环目标必须距离当前地址计数器 -128 到 +127 字节范围内。LOOP 指令的执行有两个步骤:

  • 第一步,ECX 减 1。
  • 第二步,将 ECX 与 0 比较。
  • LOOPZ(为零跳转)LOOPE(相等跳转)指令的工作和 LOOP 指令相同,只是有一个附加条件:为零控制转向目的标号,零标志位必须置 1,
  • LOOPNZ(非零跳转)LOOPNE(不相等跳转)指令与 LOOPZ 相对应。当 ECX 中无符号数值大于零(减 1 操作之后)且零标志位等于零时,继续循环
  1. PUSH指令 首先减少 ESP 的值,再将源操作数复制到堆栈。内存数需要扩展,立即数会自动对齐 ,操作数是 16 位的,则 ESP 减 2,操作数是 32 位的,则 ESP 减 4

  2. POP 指令首先把 ESP 指向的堆栈元素内容复制到一个 16 位或 32 位目的操作数中,再增加 ESP 的值。如果操作数是 16 位的,ESP 加 2,如果操作数是 32 位的,ESP 加 4:

  3. PUSHFD 和 POPFD 指令

    PUSHFD 指令把 32 位 EFLAGS 寄存器内容压入堆栈,而 POPFD 指令则把栈顶单元内容弹出到 EFLAGS 寄存器

  4. PUSHAD,PUSHA,POPAD 和 POPA 指令按照 EAX、ECX、EDX、EBX、ESP(执行 PUSHAD 之前的值)、EBP、ESI 和 EDI 的顺序,将所有 32 位通用寄存器压入堆栈 PUSHA POPA操作16位寄存器

  5. CALL RET指令 从物理上来说,CALL 指令将其返回地址压入堆栈,再把被调用过程的地址复制到指令指针寄存器。当过程准备返回时,它的 RET 指令从堆栈把返回地址弹回到指令指针寄存器。32 位模式下,CPU 执行的指令由 EIP(指令指针寄存器)在内存中指岀。16 位模式下,由 IP 指出指令

  6. SHL(左移)指令使目的操作数逻辑左移一位,最低位用 0 填充。最高位移入进位标志位,而进位标志位中原来的数值被丢弃 左移一位相当乘2的几次方

  7. SHR(右移)指令使目的操作数逻辑右移一位,最高位用 0 填充。最低位复制到进位标志位,而进位标志位中原来的数值被丢弃

  8. 与SHL一样,每次移动时,SAL 都将目的操作数中的每一位移动到下一个最高位上。最低位用 0 填充;最高位移入进位标志位,该标志位原来的值被丢弃

  9. ROL(循环左移)指令把所有位都向左移。最高位复制到进位标志位和最低位。该指令格式与 SHL 指令相同

  10. ROR(循环右移)指令把所有位都向右移,最低位复制到进位标志位和最高位

  11. RCL(带进位循环左移)指令把每一位都向左移,进位标志位复制到 LSB,而 MSB 复制到进位标志位

  12. RCR(带进位循环右移)指令把每一位都向右移,进位标志位复制到 MSB,而 LSB 复制到进位标志位

  13. SHLD(双精度左移)指令将目的操作数向左移动指定位数。移动形成的空位由源操作数的高位填充。源操作数不变,但是符号标志位、零标志位、辅助进位标志位、奇偶标志位和进位标志位会受影响

  14. SHRD(双精度右移)指令将目的操作数向右移动指定位数。移动形成的空位由源操作数的低位填充

  15. MUL MUL指令乘数和被乘数的大小必须保持一致,乘积的大小则是它们的一倍。这三种类型都可以使用寄存器和内存操作数,但不能使用立即数

    被乘数乘数乘积
    ALreg/mem8AX
    AXreg/mem16DX:AX
    EAXreg/mem32EDX:EAX

    如果乘积的高半部分不为零,则 MUL 会把进位标志位和溢出标志位置 1

  16. IMUL(有符号数乘法)指令执行有符号整数乘法。与 MUL 指令不同,IMUL 会保留乘 积的符号,实现的方法是,将乘积低半部分的最高位符号扩展到高半部分。

    单操作数 双操作数 三操作数

  17. DIV 32 位模式下,DIV(无符号除法)指令执行 8 位、16 位和 32 位无符号数除法。其中,单寄存器或内存操作数是除数

    被除数除数余数
    AXreg/mem8ALAH
    DX:AXreg/mem16AXDX
    EDX:EAXreg/mem32EAXEDX
  18. IDICV指令 有符号除法几乎与无符号除法相同,只有一个重要的区别:在执行除法之前,必须对被除数进行符号扩展。

  19. 符号扩展指令

    • CBW(字节转字)指令将 AL 的符号位扩展到 AH,保留了数据的符号
    • CWD(字转双字)指令将 AX 的符号位扩展到 DX
    • CDQ(双字转四字)指令将 EAX 的符号位扩展到 EDX
  20. IDIV(有符号除法)指令执行有符号整数除法,其操作数与 DIV 指令相同。执行 8 位除法之前,被除数(AX)必须完成符号扩展。余数的符号总是与被除数相同。

  21. ADC指令 ADC(带进位加法)指令将源操作数和进位标志位的值都与目的操作数相加。该指令格式与 ADD 指令一样,且操作数大小必须相同

  22. SBB(带借位减法)指令从目的操作数中减去源操作数和进位标志位的值。

  23. AAA(执行加法后进行 ASCII 调整)AAM(执行乘法后进行 ASCII 调整)
    AAS(执行减法后进行 ASCII 调整)AAD(执行除法前进行 ASCII 调整)
  24. BCD码指令

  25. DAA DAS 指令

  26. LEA 指令返回间接操作数的地址。由于间接操作数中包含一个或多个寄存器,因此会在运行时计算这些操作数的偏移量

  27. ENTER指令 ENTER 指令为被调用过程自动创建堆栈帧。它为局部变量保留堆栈空间,把 EBP 入栈

    enter 8,0 push ebp;mov ebp,esp;sub esp,8

    LEVEL指令 mov esp,ebp;pop ebp

    LOCAL伪指令 紧跟在PROC伪指令后面,变量名:变量类型 等价于enter+level

  28. SETE AL //取标志寄存器中ZF的值, 放到AL中. SETNE取得ZF值后, 取反, 再放到AL中

1.1 布尔指令
  • 64 位模式中的 64 位指令与 32 位模式中的操作是一样的。比如,如果源操作数是常数,长度小于 32 位,而目的操作数是一个 64 位寄存器或内存操作数,那么,目的操作数中所有的位都会受到影响,如果源操作数是 32 位常数或寄存器,那么目的操作数中,只有低 32 位会受到影响
  1. AND指令 AND 指令在两个操作数的对应位之间进行(按位)逻辑与(AND)操作,并将结果存放在目标操作数中
    • AND 指令总是清除溢出和进位标志位
  2. OR指令 OR 指令在两个操作数的对应位之间进行(按位)逻辑或(OR)操作,并将结果存放在目标操作数中
    • OR 指令总是清除进位和溢出标志位,并根据目标操作数的值来修改符号标志位、零标志位和奇偶标志位
  3. NOT AND OR 补集 交集 并集 概念
  4. XOR 指令在两个操作数的对应位之间进行(按位)逻辑异或(XOR)操作,并将结果存放在目标操作数中,相同为0 不同为1,与 0 异或值保持不变,与 1 异或则被触发(求补)
    • XOR 指令总是清除溢岀和进位标志位
  5. NOT 指令触发(翻转)操作数中的所有位。其结果被称为反码
    • NOT指令不影响标志位
  6. TEST 指令在两个操作数的对应位之间进行 AND 操作,并根据运算结果设置符号标志位、零标志位和奇偶标志位。不改变操作数
  7. CMP CMP(比较)指令执行从目的操作数中减去源操作数的隐含减法操作,并且不修改任何操作数
1.2 Jcond指令
  • 基于特定标志位的值跳转
  • 基于两数是否相等,或是否等于(E)CX 的值跳转
  • 基于无符号操作数的比较跳转
  • 基于有符号操作数的比较跳转
  1. 助记符说明标志位/寄存器助记符说明标志位/寄存器
    JZ为零跳转ZF=1JNO无溢出跳转OF=0
    JNZ非零跳转ZF=0JS有符号跳转SF=1
    JC进位跳转CF=1JNS无符号跳转SF=0
    JNC无进位跳转CF=0JP偶校验跳转PF=1
    JO溢出跳转OF=1JNP奇校验跳转PF=0
  2. 助记符说明
    JE相等跳转 (leftOp=rightOp)
    JNE不相等跳转 (leftOp M rightOp)
    JCXZCX=0 跳转
    JECXZECX=0 跳转
    JRCXZRCX=0 跳转(64 位模式)
  3. 助记符说明助记符说明
    JA大于跳转(若 leftOp > rightOp)JB小于跳转(若 leftOp < rightOp)
    JNBE不小于或等于跳转(与 JA 相同)JNAE不大于或等于跳转(与 JB 相同)
    JAE大于或等于跳转(若 leftOp ≥ rightOp)JBE小于或等于跳转(若 leftOp ≤ rightOp)
    JNB不小于跳转(与 JAE 相同)JNA不大于跳转(与 JBE 相同)
  4. 助记符说明助记符说明
    JG大于跳转(若 leftOp > rightOp)JL小于跳转(若 leftOp < rightOp)
    JNLE不小于或等于跳转(与 JG 相同)JNGE不大于或等于跳转(与 JL 相同)
    JGE大于或等于跳转(若 leftOp ≥ rightOp)JLE小于或等于跳转(若 leftOp ≤ rightOp)
    JNL不小于跳转(与 JGE 相同)JNG不大于跳转(与 JLE 相同)
1.3 串操作指令
指令说明
MOVSB、MOVSW、MOVSD传送字符串数据:将 ESI 寻址的内存数据复制到 EDI 寻址的内存位置
CMPSB、CMPSW、CMPSD比较字符串:比较分别由 ESI 和 EDI 寻址的内存数据
SCASB、SCASW、SCASD扫描字符串:比较累加器 (AL、AX 或 EAX) 与 EDI 寻址的内存数据
STOSB、STOSW、STOSD保存字符串数据:将累加器内容保存到 EDI 寻址的内存位置
LODSB、LODSW、LODSD从字符串加载到累加器:将 ESI 寻址的内存数据加载到累加器
REPECX > 0 时重复
REPZ、REPE零标志位置 1 且 ECX > 0 时重复
REPNZ、REPNE零标志位清零且 ECX > 0 时重复

SCASB:如果发现该字符,则 EDI 指向匹配字符后面的一个位置。如果未发现匹配字符,则 JNZ 执行退出:

字符串操作

2. 伪指令
  1. = 伪指令 替换

  2. EQU 伪指令 表达式 符号替换 <文本> 不能重定义

  3. TEXTEQU 伪指令 <文本> 已有文本宏 %(表达式)

  4. ALIGN bound 对齐伪指令

  5. LABEL 伪指令可以插入一个标号,并定义它的大小属性,但是不为这个标号分配存储空间,常见的用法是,为数据段中定义的下一个变量提供不同的名称和大小属性

  6. EXTERN伪指令 调用当前模块之外的过程时使用EXTERN伪指令,它确定过程名和堆栈帧大小。

    包含头文件 EXTERN 函数名@堆栈帧大小:PROC

  7. PROC ENDP伪指令

    过程可以非正式地定义为:由返回语句结束的已命名的语句块。过程用 PROC 和 ENDP 伪指令来定义,并且必须为其分配一个名字(有效标识符)。标号只在定义的过程中有效

  8. LOCAL伪指令 紧跟在PROC伪指令后面,变量名:变量类型 等价于enter+level

    由于 32 位模式中,堆栈偏移量默认为 32 位,因此,var1 可能被认为会存放于 EBP-4 的位置。实际上,如下图所示,MASM 将 EBP 减去 4,但是却把 var1 存放在 EBP-1

  9. PROC 伪指令 过程定义

    当 PROC 有一个或多个参数时,STDCALL 是默认调用规范,自动生成push ebp;mov ebp,esp;leave;ret (n*4)

    属性说明
    distanceNEAR 或 FAR。指定汇编器生成的 RET 指令(RET 或 RETF)类型
    langtype指定调用规范(参数传递规范),如 C、PASCAL 或 STDCALL。能覆盖由 .MODEL 伪指令指定的语言
    visibility指明本过程对其他模块的可见性。选项包括 PRIVATE、PUBLIC (默认项)和 EXPORT。若可见性为 EXPORT,则链接器把过程名放入分段可执行文件的导出表。EXPORT 也使之具有了 PUBLIC 可见性
    prologuearg指定会影响开始和结尾代码生成的参数
  10. PROTO伪指令

    MySub PROTO ;过程原型.INVOKE MySub ;过程调用.MySub PROC ;过程实现

    可以覆盖调用规范,参数类型大于声明会报错,小于自动扩展

  11. INVOKE伪指令 等价于INVOKE DumpArray, OFFSET array, LENGTHOF array, TYPE array,从右往左依次执行push,call

    类型例子类型例子
    立即数10, 3000h, Offset mylist, TYPE array寄存器eax, bl, edi
    整数表达式(10*20), COUNTADDR nameADDR myList
    变量myLIst, array, my Word, myDwordOFFSET nameOFFSET myList
    地址表达式[myList+2], [ebx+esi]
    • 覆盖 EAX 和 EDX

      如果向过程传递的参数小于 32 位,那么在将参数入栈之前,INVOKE 为了扩展参数常常会使得汇编器覆盖 EAX 和 EDX 的内容。有两种方法可以避免这种情况:

      • 其一,传递给 INVOKE 的参数总是 32 位的;
      • 其二,在过程调用之前保存 EAX 和 EDX,在过程调用之后再恢复它们的值
    1. 定义结构使用的是 STRUCT 和 ENDS 伪指令。在结构内,定义字段的语法与一般的变量定义是相同的。结构对其包含字段的数量几乎没有任何限制:

      name STRUCT
      field-declarations
      name ENDS

      字段初始值若结构字段有初始值,那么在创建结构变量时就要进行赋值。字段初始值可以使用各种类型:

      • 无定义:运算符?使字段初始值为无定义。
      • 字符串文本:用引号括起的字符串。
      • 整数:整数常数和整数表达式。
      • 数组:DUP 运算符可以初始化数组元素。
  • 伪指令说明
    .BREAK生成代码终止 .WHILE 或 .REPEAT 块
    .CONTINUE生成代码跳转到 .WHILE 或 .REPEAT 块的顶端
    .ELSE当 .IF 条件不满足时,开始执行的语句块
    .ELSEIF condition生成代码测试 condition,并执行其后的语句,直到碰到一个 .ENDIF 或另一个 .ELSEIF 伪指令
    .ENDIF终止 .IF、.ELSE 或 .ELSEIF 伪指令后面的语句块
    .ENDW终止 .WHILE 伪指令后面的语句块
    .IF condition如果 condition 为真,则生成代码执行语句块
    .REPEAT生成代码重复执行语句块,直到条件为真
    .UNTIL condition生成代码重复执行 .REPEAT 和 .UNTIL 伪指令之间的语句块,直到 condition 为真
    .UNTILCXZ生成代码重复执行 .REPEAT 和 .UNTILCXZ 伪指令之间的语句块,直到 CX 为零
    .WHILE condition当 condition 为真时,生成代码执行 .WHILE 和 .ENDW 伪指令之间的语句块
  • .IF、.ELSE、.ELSEIF 和 .ENDIF 伪指令使得程序员易于对多分支逻辑进行编码。它们让汇编器在后台生成 CMP 和条件跳转指令,这些指令显示在输出列表文件中

  • 运算符说明
    expr1 == expr2若 expr1 等于 expr2,则返回“真”
    expr1 != expr2若 expr1 不等于 expr2,则返回“真”
    expr1 > expr2若 expr1 大于 expr2,则返回"真”
    expr1 ≥ expr2若 expr1 大于等于 expr2,则返回“真”
    expr1 < expr2若 expr1 小于 expr2,则返回“真”
    expr1 ≤ expr2若 expr1 小于等于 expr2,则返回“真”
    !expr1若 expr 为假,则返回“真”
    expr1expr2对 expr1 和 expr2 执行逻辑 AND 运算
    expr1 || expr2对 1xprl 和 expr2 执行逻辑 OR 运算
    expr1 & expr2对 expr1 和 expr2 执行按位 AND 运算
    CARR1?若进位标志位置 11则返回“真”
    OVERFLOW ?若溢出标志位置 1,则返回“真”
    PARITY ?若奇偶标志位置 1,则返回“真”
    SIGN ?若符号标志位置 1,则返回“真”
    ZERO ?若零标志位置 1,则返回“真”
  • .REPEAT 和 .WHILE 伪指令还提供了另一种方法来编写循环

    .REPEAT
    statements
    .UNTIL condition

    .WHILE 伪指令在执行循环体之前测试条件:

    .WHILE condition
    statements
    .ENDW

3. 运算符
  1. OFFSET 返回数字标号的偏移量,只能用于编译时已知的地址

  2. PTR 运算符可以用来重写一个已经被声明过的操作数的大小类型

  3. TYPE 运算符返回变量单个元素的大小,这个大小是以字节为单位计算的

  4. LENGTHOF 运算符计算数组中元素的个数,元素个数是由数组标号同一行出现的数值来定义的,加逗号计算下一行

  5. SIZEOF 运算符则为 LENGTHOF 与 TYPE 的乘积。

  6. TYPEDEF运算符 TYPEDEF 运算符可以创建用户定义类型,这些类型包含了定义变量时内置类型的所有状态,PBYTE TYPEDEF PTR BYTE

  7. USES 运算符与 PROC 伪指令一起使用,让程序员列出在该过程中修改的所有寄存器名。USES 告诉汇编器做两件事情:第一,在过程开始时生成 PUSH 指令,将寄存器保存到堆栈;第二,在过程结束时生成 POP 指令,从堆栈恢复寄存器的值。

    USES 运算符紧跟在 PROC 之后,其后是位于同一行上的寄存器列表,表项之间用空格符或制表符(不是逗号)分隔。

    如果使用偏移量参数,加上寄存器压入的大小

  8. $ 当前偏移量

  9. ADDR 运算符 传递参数指针 参数必须是汇编时常数

4. 操作数
  1. 直接偏移量操作数
  • 变量名加偏移量
  1. 间接操作数 任何一个 32 位通用寄存器(EAX、EBX、ECX、EDX、ESI、EDI、EBP 和 ESP)加上括号就能构成一个间接操作数。

  2. 变址操作数 变址操作数是指,在寄存器上加上常数产生一个有效地址

  3. 指针 如果一个变量包含另一个变量的地址,则该变量称为指针

5. 标志位
  1. CF 无符号整数运算结果超出范围 最高有效位产生进位或借位时 设置该标志位

  2. OF 有符号整数运算结构超出范围 设置该标志位

  • 两个正数相加,结果为负数

  • 两个负数相加,结果为正数

  1. ZF 运算结果为零时设置该标志位

  2. AF 辅助标志位 最低字节三位有进位时设置该标志位

  3. PF 最低字节为1的位数为偶数时设置该标志位

  4. SF 符号标志位 运算结果为有符号数是设置该标志位

  5. DF 方向标志位 设置为1时地址的变化方向为减少 为0时增加 用于串操作指令 控制地址的变化方向 CLD DF为0 STD DF为1

  6. IF 终端允许标记位 为1允许终端 CLI STI

  7. TF 陷阱标志位 为1 处理器单步执行指令

6. 堆栈
  • 汇编里把一段内存空间定义为一个栈,栈总是先进后出,栈的最大空间为 64K。由于 “栈” 是由高到低使用的,所以新压入的数据的位置更低,ESP 中的指针将一直指向这个新位置,所以 ESP 中的地址数据是动态的。
  • 堆栈证明了它很适合于保存信息,包括过程调用嵌套。一般说来,堆栈结构用于程序需要按照特定顺序返回的情况。
  1. 后进先出 LIFO

  2. ESP 扩展堆栈指针 EBP 扩展帧指针

  3. 入栈:32 位入栈操作把栈顶指针减 4,再将数值复制到栈顶指针指向的堆栈位置。

  4. 出栈:出栈操作从堆栈删除数据。数值弹岀堆栈后,栈顶指针增加(按堆栈元素大小),指向堆栈中下一个最高位置。

  5. 堆栈的作用

    • 当寄存器用于多个目的时,堆栈可以作为寄存器的一个方便的临时保存区。在寄存器被修改后,还可以恢复其初始值。
    • 执行 CALL 指令时,CPU 在堆栈中保存当前过程的返回地址。
    • 调用过程时,输入数值也被称为参数,通过将其压入堆栈实现参数传递。
    • 堆栈也为过程局部变量提供了临时存储区域。
  • 在 32 位模式下,堆栈参数总是由 Windows API 函数使用。然而在 64 位模式下,Windows 函数可以同时接收寄存器参数和堆栈参数。

    堆栈帧 (stack frame)( 或活动记录 (activation Tecord)) 是一块堆栈保留区域,用于存放被传递的实际参数、子程序的返回值、局部变量以及被保存的寄存器。

    堆栈帧的创建步骤如下所示:

    1. 被传递的实际参数。如果有,则压入堆栈。

    2. 当子程序被调用时,使该子程序的返回值压入堆栈。

    3. 子程序开始执行时,EEP 被压入堆栈。

    4. 设置 EBP 等于 ESP。从这时开始,EBP 就变成了该子程序所有参数的引用基址。

    5. 如果有局部变量,修改 ESP 以便在堆栈中为这些变量预留空间。

    6. 如果需要保存寄存器,就将它们压入堆栈。

    程序内存模式和对参数传递规则的选择直接影响到堆栈帧的结构。

    学习用堆栈传递参数有个好理由:几乎所有的高级语言都会用到它们。比如,如果想要在 32 位 Windows 应用程序接口 (API) 中调用函数,就必须用堆栈传递参数。而 64 位程序可以使用另一种不同的参数传递规则。

    32 位模式中,通过堆栈向子程序传递 64 位参数时,先将参数的高位双字入栈,再将其低位双字入栈。这样就使得整数在堆栈中是按照小端顺序(低字节在低地址)存放的

    传入8位16变量出错,立即数自动扩展

7. x64 调用规范
  1. CALL 指令将 RSP(堆栈指针)寄存器减 8,因为地址是 64 位的。

  2. 前四个参数依序存入 RCX、RDX、R8 和 R9 寄存器,并传递给过程。如果只有一个参数,则将其放入 RCX。如果还有第二个参数,则将其放入 RDX,以此类推。其他参数,按照从左到右的顺序压入堆栈。

  3. 调用者的责任还包括在运行时堆栈分配至少 32 字节的影子空间(shadow space),这样,被调用的过程就可以选择将寄存器参数保存在这个区域中。

  4. 在调用子程序时,堆栈指针(RSP)必须进行 16 字节边界对齐(16 的倍数)。CALL 指令把 8 字节的返回值压入堆栈,因此,除了已经减去的影子空间的 32 之外,调用程序还必须从堆栈指针中减去 8。后面的示例将显示如何实现这些操作。

  • Microsoft 遵循固定模式实现 64 位编程中的参数传递和子程序调用,该模式被称为 Microsoft x64 调用规范(Microsoft x64 calling convention)。它既用于 C 和 C++ 编译器,也用于 Windows API库。

    只有在要么调用 Windows 函数,要么调用 C 和 C++ 函数时,才需要使用这个调用规范。它的特点和要求如下所示:

    1. 由于地址长为 64 位,因此 CALL 指令把 RSP(堆栈指针)寄存器的值减去8。

    2. 第一批传递给子程序的四个参数依次存放于寄存器 RCX、RDX、R8 和 R9。因此,如果只传递一个参数,它就会被放入 RCX。如果还有第二个参数,它就会被放入 RDX,以此类推。其他参数按照从左到右的顺序入栈。

    3. 长度不足 64 位的参数不进行零扩展,因此,其高位的值是不确定的。

    4. 如果返回值的长度小于或等于 64 位,那么它必须放在 RAX 寄存器中。

    5. 主调者要负责在堆栈中分配至少 32 字节的影子空间,以便被调用的子程序可以选择将寄存器保存在这个区域中。

    6. 调用子程序时,堆栈指针(RSP)必须对齐 16 字节边界。CALL 指令将 8 字节的返回地址压入堆栈,因此,主调程序除了把堆栈指针减去 32 以便存放寄存器参数之外,还要减去8。

    7. 被调用子程序执行结束后,主调程序需负责从运行时堆栈中移除所有的参数和影子空间。

    8. 大于 64 位的返回值存放于运行时堆栈,由 RCX 指出其位置。

    9. 寄存器 RAX、RCX、RDX、R8、R9、R10 和 R11 常常被子程序修改,因此,如果主调程序想要保存它们的值,就应在调用子程序之前将它们入栈,之后再从堆栈弹出。

    10. 寄存器 RBX、RBP、RDI、RSI、R12、R13、R14 和 R15 的值必须由子程序保存。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U9DBV7OJ-1616406761400)(E:/%E8%BD%AF%E4%BB%B6%E4%B8%8B%E8%BD%BD/%E5%8A%9E%E5%85%AC/%E7%AC%94%E8%AE%B0/%E5%9B%BE%E7%89%87/image-20201016113242008.png)]

8. 调用规范
  1. _stdcall 入栈规则 从右到左 平衡堆栈方式 被调用者平衡

  2. _cdecl 入栈规则 从右到左 平衡堆栈方式 调用者平衡

    C 调用规范则允许子程序声明不同数量的参数,主调程序可以决定传递多少个参数

  3. _fastcall 入站规则 左边开始的两个不大于四字节的参数保存到ECX,EDX,其余参数从右到左入栈

    平衡堆栈方式 被调用者平衡

  4. _thiscall 入栈规则 通过ECX传递this指针,从右到左入栈 平衡堆栈方式 被调用者平衡

9. 参数类型
  1. 输入 提供变量的值
  2. 输出 提供缓冲区
  3. 输入输出型 提供变量的地址
10. 汇编调试器
  1. 16位:Debug,Trubo DBG

  2. 32位:OLLYDBG,windbg ,x32dbg,GDB,VSdbg,

  3. 64位:x64dbg

  4. 16位汇编调试器:Debug,TD

32位汇编调试器:x32dbg,windbg,OllyDbg

学员答案:

ollyDBG winDBG GDB vs Debugger

11. ollydbg操作
  1. L : log 保存日志信息
    E :程序的所有模块的信息(加载基址,大小,OEP,路径)
    M :程序的内存映射视图
    T :线程信息
    W :窗口信息
    H :句柄表
    C :CPU窗口(反汇编窗口)
    / :补丁信息
    K :调用堆栈
    B :软件断点列表
    R :显示参考(数据引用等)
    … : RUN跟踪
    S : 源码显示窗口

  2. F2:下断点

    F4:运行到光标选中处

    F7:单步步入

    F8:单步步入

    F9:运行

    F12暂停

    ctrl+F2:重启调试

    ctrl+F9:执行直到返回,到最近的ret

    Alt+F9:执行直到用户代码段

    shift+F9:忽略异常运行

    (Ctrl+A) 分析当前模块的代码段
    (Ctrl+B) 开始二进制搜索
    (Ctrl+C) 复制所选内容到剪贴板
    (Ctrl+E) 以二进制(十六进制)格式编辑所选内容
    (Ctrl+F) 开始命令搜索
    (Ctrl+G) 输入十六进制地址,快速定位到该地址处。该命令将弹出输入地址或表达式的窗口。该命令不会修改 EIP
    (Ctrl+J) 列出所有的涉及到该位置的调用和跳转,在您用这个功能之前,您必须使用分析代码功能
    (Ctrl+K) 查看与当前函数相关的调用树[Call tree]。在您用这个功能之前,您必须使用分析代码功能
    (Ctrl+L) 搜索下一个,重复上一次的搜索内容
    (Ctrl+N) 打开当前模块的名称(标签)列表
    (Ctrl+O) 扫描object文件
    (Ctrl+R) 搜索所选命令的参考
    (Ctrl+S) 多条命令搜索,显示命令查找对话框供您输入汇编命令,并从当前命令开始搜索

    (Ctrl+D) 中文引擎搜索

    Ctrl+X:复制地址

    shift+x:复制二进制数据

    enter:进入函数

    ‘-’:返回

  3. 窗口

    CPU窗口 寄存器窗口 堆栈窗口 自动分析小窗口 内存数据窗口 断点列表窗口

  4. ctrl+a 分析 右键分析 - 预设参数 - 断点 - WinProc断点 - 选择消息类型(WM_COMMAND在栈中查看)

  5. 右键查找 - 命令 - 所有命令 选择特征码 - 在每个命令上设置断点 找到关键调用

  6. 查找用户的所有注释

  7. 关键窗口API断点回溯找回调函数

  8. W窗口查看

  9. 点击直接修改寄存器,内存信息

  10. 在内存窗口crtl+b所有模块中搜索

  11. 右键断点,内存,硬件断点,条件断点,调式中可查看断点,四个硬件断点,一个内存断点

    • 检测软件断点,一般来说靠内存校验(crc、关键点监视)判断有没有哪行指令被修改成了断点。

      int 3 0xCC,需要可读写的位置ram

      软件断点不能设置在ROM/FLASH里面。因为软件断点的实现需要替换要设置断点的位置的内容,这点在ROM/FLASH做不到。

    • 检测硬件断点,通常来说是通过相关api或主动触发异常来获得进程的一些运行信息(上下文),其中有包括硬件断点相关的内容。

    • 检测内存断点,一般监视内存的属性是否被修改了。

12. 常用指令

一、数据传输指令

MOV:数据传递

PUSH: 入栈指令

POP: 出栈指令

XCHG: 内容交换

LEA: 有效地址传送

PUSHF: 标志寄存器入栈

POPF: 标志寄存器出栈

PUSHA: 所有寄存器入栈

POPA: 所有寄存器出栈

二、算术与逻辑运算指令

ADD: 加法指令

ADC: 带进位加法指令

INC: 加1指令

SUB: 减法指令

SBB: 带借位的减法指令

DEC: 减1指令

MUL: 无符号乘法指令

IMUL: 有符号乘法指令

DIV: 无符号除法指令

IDIV: 有符号除法指令

CMP: 比较指令(做减法)

AND: 逻辑与指令(按位与)

OR: 逻辑或指令(按位或)

XOR: 逻辑异或指令(按位异或)

NOT: 逻辑非指令(按位取反)

TEST: 按位与指令(按位与)

三、串操作指令

MOVS: 串传送指令

STOS: 存入串指令

LODS: 串取出指令

CMPS: 串比较指令

REP: 重复操作前缀

四、控制转移指令

JMP: 无条件转移指令

JXX: 条件转移指令

LOOP : 循环指令

CALL: 子程序指令

RET: 子程序返回指令

13.寻址方式

立即数寻址 mov eax,1
寄存器寻址 mov eax,ebx
存储器寻址
- 直接寻址 mov eax,[2000]
- 寄存器间接寻址 mov eax,[esi]
- 寄存器相对寻址 mov eax,[esi+06]
- 基址变址寻址 mov eax,[ebx+esi]
- 相对基址变址寻址 mov ax,[ebx+esi+06]

14. 汇编与反汇编引擎
  1. 反汇编 BeaEngine,capstone
  2. 汇编 keystone,XEDParse
15. OPcode

OPCode就是机器码,是CPU微指令对外的接口。
OpCode是Operation Code,意即操作码的意思。

OPCode是汇编代码编译以后的二进制代码。
就像现实生活中一个名称往往并不唯一对应一个事物一样,OpCode和助记符的关系如下:不同的OpCode可能有同样的助记符;一个OpCode可能有几个助记符对应.

不同的CPU对应不同的OPCODE

90 : nop
E9 : jmp
E8 : call
FF15: call [xxxxx]

0xCC int 3

0x40 inc eax

0x50 push eax

0x58 pop eax

66 切换操作数大小 eax … /dword ptr[]… -> ax / word ptr[]… 67 切换地址大小 dword ptr[eax]… dword ptr[ax] F2/F3 重复操作数 rep repne 2E/36/3E/26/64/65 修改默认段,段超越 F0 锁定前缀,变成原子操作 lock

在这里插入图片描述
在这里插入图片描述

00417000 - E9 CDA1FEFF JMP 000_jmpO.004011D2
00417005 90 NOP
00417006 90 NOP
00417007 > 60 PUSHAD
00417008 BB 00104000 MOV EBX,000_jmpO.00401000 ; 入口地址
0041700D B9 FF9F0000 MOV ECX,0x9FFF
00417012 803419 15 XOR BYTE PTR DS:[ECX+EBX],0x15
00417016 ^ E2 FA LOOPD SHORT 000_jmpO.00417012
00417018 E8 00000000 CALL 000_jmpO.0041701D
0041701D 58 POP EAX
0041701E 2D 1D700100 SUB EAX,0x1701D
00417023 05 D2110000 ADD EAX,0x11D2
00417028 FFD0 CALL EAX

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值