
Cheat Engine 常用汇编
仅包含 Cheat Engine 常用的寄存器和指令,比如 FPU、MMX 之类的罕见或完全用不到的不在此列出。想了解更多可参考 MSDN 或 asm-dude/wiki。Cheat Engine 汇编器并不完整,部分指令和寄存器无法通过 AAS 汇编
§通用寄存器
使用前记得 push 备份
| 寄存器 | 低 8 位 | 高 8 位 | 16 位 | 32 位 | 64 位 | 说明 |
|---|---|---|---|---|---|---|
| a | a/al | ah | ax | eax | rax | 临时变量、返回值 |
| b | b/bl | bh | bx | ebx | rbx | - |
| c | c/cl | ch | cx | ecx | rcx | 计数器、this 指针、传递参数 |
| d | d/dl | dh | dx | edx | rdx | 传递参数 |
| si | sil | sih | si | esi | rsi | 索引、传递参数 |
| di | dil | dih | di | edi | rdi | 索引、传递参数 |
| r8 | r8b/r8l | r8h | r8w | r8d | r8 | 传递参数 |
| r9 | r9b/r9l | r9h | r9w | r9d | r9 | 传递参数 |
| r10 | r10b/r10l | r10h | r10w | r10d | r10 | 被调用的代码段持有 |
| r11 | r11b/r11l | r11h | r11w | r11d | r11 | 被调用的代码段持有 |
| r12 | r12b/r12l | r12h | r12w | r12d | r12 | 调用的代码段持有 |
| r13 | r13b/r13l | r13h | r13w | r13d | r13 | 调用的代码段持有 |
| r14 | r14b/r14l | r14h | r14w | r14d | r14 | 调用的代码段持有 |
| r15 | r15b/r15l | r15h | r15w | r15d | r15 | 调用的代码段持有 |
§控制寄存器
除非知道自己在干什么,否则不要去动它
| 寄存器 | 说明 | 低 8 位 | 高 8 位 | 16 位 | 32 位 | 64 位 |
|---|---|---|---|---|---|---|
| bp | 基址寄存器 | bpl | bph | bp | ebp | rbp |
| sp | 堆栈寄存器 | spl | sph | sp | esp | rsp |
| ip | 地址寄存器 | ipl | iph | ip | eip | rip |
§SIMD 寄存器
128 位寄存器,可存储 4 个 32位数据或 2 个 64 位数据,一般用于存储标量浮点数据,但也可以作为通用寄存器使用,需要打包数据再操作以提高性能
| 寄存器 | 寄存器 | 寄存器 | 寄存器 | 寄存器 | 寄存器 | 寄存器 | 寄存器 |
|---|---|---|---|---|---|---|---|
| xmm0 | xmm1 | xmm2 | xmm3 | xmm4 | xmm5 | xmm6 | xmm7 |
| xmm8 | xmm9 | xmm10 | xmm11 | xmm12 | xmm13 | xmm14 | xmm15 |
§堆栈指令
一般而言,在需要进行比较的时候需要通过堆栈备份数据,还有一个常见场景就是借一个寄存器用于中转数据,但也可以观察上下文找到后续必定被改写的寄存器当作临时寄存器;堆栈处理是重中之重,处理不好很容易崩掉应用程序
| 指令 | 说明 |
|---|---|
| push eax | 将 eax 压入栈顶,即 [esp] = eax |
| pop eax | 将栈顶的值弹出到 eax,即 eax = [esp] |
| pushfd | 将 32 位标识寄存器压入栈顶 |
| popfd | 将栈顶的值弹出到 32 位标识寄存器 |
| pushfq | 将 64 位标识寄存器压入栈顶 |
| popfq | 将栈顶的值弹出到 64 位标识寄存器 |
| pushad | 将 32 位寄存器压入栈顶 |
| popad | 将栈顶的值弹出到 32 位寄存器 |
| pushaq | 将 64 位寄存器压入栈顶 |
| popaq | 将栈顶的值弹出到 64 位寄存器 |
| call | 设置 IP 为方法地址并将当前 IP 压栈,方法中用 [ebp+04]、[rbp+08] 获得 call 的地址 |
| leave | 将 ebp 写入 esp 并从堆栈弹出到 ebp |
| ret | 将栈恢复并从栈中弹出 IP 寄存器 |
| add esp,04 | 将栈顶拉高 4 个字节 |
| sub esp,04 | 将栈顶降低 4 个字节 |
§移动指令
| 修饰 | 说明 |
|---|---|
| BYTE PTR | 8 位 |
| WORD PTR | 16 位 |
| DWORD PTR | 32 位 |
| QWORD PTR | 64 位 |
| 未指定 | 与平台同步 |
| 指令 | 说明 |
|---|---|
| mov eax,ebx | 移动寄存器数据,eax = ebx |
| mov eax,A0B0 | 移动立即数到寄存器,eax = 0xA0B0 |
| mov eax,[eax+A0] | 移动内存数据到寄存器,eax = [eax+A0] |
| mov [eax+A0],eax | 移动寄存器数据到内存,[eax+A0] = eax |
| mov [eax+A0],A0B0 | 移动立即数到内存,[eax+A0] = 0xA0B0 |
§运算指令
| 指令 | 说明 |
|---|---|
| lea eax,[eax+A0B0] | 地址运算,eax = eax + 0xA0B0 |
| lea eax,[eax-A0B0] | 地址运算,eax = eax - 0xA0B0 |
| inc eax | 数值运算,eax = eax + 1 |
| dec eax | 数值运算,eax = eax - 1 |
| add eax,A0B0 | 数值运算,eax = eax + 0xA0B0 |
| sub eax,A0B0 | 数值运算,eax = eax - 0xA0B0 |
| - | - |
| sar eax,04 | 算术右移,eax = eax >> 0x04,保留符号,若 eax 为 0x70 则 eax = 112/2/2/2/2 |
| sar [eax+04],04 | 同上 |
| shr eax,04 | 逻辑右移,eax = eax >> 0x04,补 0 |
| shr [eax+04],04 | 同上 |
| sal eax,04 | 算术左移,eax = eax << 0x04,保留符号,若 eax 为 0x07 则 eax = 7*2*2*2*2 |
| sal [eax+04],04 | 同上 |
| shl eax,04 | 逻辑左移,eax = eax << 0x04,补 0 |
| shl [eax+04],04 | 同上 |
| shld eax,ebx,04 | 逻辑左移,从 ebx(00110011) 左移 4 位到 eax(00000000) => 00000011 |
| shld [eax+04],ebx,04 | 同上 |
| shrd eax,ebx,04 | 逻辑右移,从 ebx(00110011) 右移 4 位到 eax(00000000) => 00110000 |
| shrd [eax+04],ebx,04 | 同上 |
| - | - |
| and eax,ebx | 逻辑与 |
| and eax,[ebx+04] | 同上 |
| and [eax+04],ebx | 同上 |
| test eax,ebx | 类似 cmp,只设置标志位,不修改寄存器,常用于判断是否为 0 |
| test eax,[ebx+04] | 同上 |
| test [eax+04],ebx | 同上 |
| not eax | 逻辑非,亦称为反转,按位取反 |
| or eax,ebx | 逻辑或 |
| or eax,[ebx+04] | 同上 |
| or [eax+04],ebx | 同上 |
| xor eax,ebx | 逻辑异或 |
| xor eax,[ebx+04] | 异或 |
| xor [eax+04],ebx | 异或 |
| xor eax,eax | 异或特殊用法,可用最低代价清空指定寄存器 |
| xorps xmm0,xmm1 | 逻辑异或 |
| xorps xmm0,[ebx+04] | 逻辑异或 |
| xorps xmm0,xmm0 | 异或特殊用法,可用最低代价清空指定寄存器 |
| - | - |
| mul ebx | 无符号乘,edx:eax = eax * ebx,用 edx 和 eax 联合出一个 64 位寄存器存放结果,其它位数操作与此一致 |
| mul DWORD PTR [ebx+04] | 无符号乘,edx:eax = eax * [ebx+04],用 edx 和 eax 联合出一个 64 位寄存器存放结果,其它位数操作与此一致 |
| div ebx | 无符号除,eax = edx:eax / ebx,edx = edx:eax % ebx,其它位数操作与此一致 |
| div DWORD PTR [ebx+04] | 无符号除,eax = edx:eax / [ebx+04],edx = edx:eax % [ebx+04],其它位数操作与此一致 |
| imul ebx | 有符号乘,edx:eax = eax * ebx,用 edx 和 eax 联合出一个 64 位寄存器存放结果,其它位数操作与此一致 |
| imul DWORD PTR [ebx+04] | 有符号乘,edx:eax = eax * [ebx+04],用 edx 和 eax 联合出一个 64 位寄存器存放结果,其它位数操作与此一致 |
| imul eax,ebx | 有符号乘,eax = eax * ebx,溢出丢弃并设置标志位,其它位数操作与此一致 |
| imul eax,DWORD PTR [ebx+04] | 有符号乘,eax = eax * [ebx+04],溢出丢弃并设置标志位,其它位数操作与此一致 |
| imul eax,A0B0 | 有符号乘,eax = eax * 0xA0B0,溢出丢弃并设置标志位,其它位数操作与此一致 |
| imul eax,ebx,A0B0 | 有符号乘,eax = ebx * 0xA0B0,溢出丢弃并设置标志位,其它位数操作与此一致 |
| imul eax,DWORD PTR [ebx+04],A0B0 | 有符号乘,eax = [ebx+04] * 0xA0B0,溢出丢弃并设置标志位,其它位数操作与此一致 |
| idiv ebx | 有符号除,eax = edx:eax / ebx,edx = edx:eax % ebx,溢出异常,其它位数操作与此一致 |
| idiv DWORD PTR [ebx+04] | 有符号除,eax = edx:eax / [ebx+04],edx = edx:eax % [ebx+04],溢出异常,其它位数操作与此一致 |
§比较指令
关于浮点数比较部分可以参考x86架构中UCOMISS浮点比较指令所产生的条件码
| 指令 | 说明 |
|---|---|
| cmp eax,ebx | 无符号,用减法的方式比较两个寄存器并设置标志位 |
| cmp eax,[eax+04] | 无符号,用减法的方式比较寄存器和内存数据并设置标志位 |
| cmp [eax+04],eax | 无符号,用减法的方式比较寄存器和内存数据并设置标志位 |
| - | - |
| ucomiss xmm0,xmm1 | 无符号单精度,用减法的方式比较两个寄存器并设置标志位 |
| ucomiss xmm0,[eax+04] | 无符号单精度,用减法的方式比较寄存器和内存数据并设置标志位 |
| comiss xmm0,xmm1 | 有符号单精度,用减法的方式比较两个寄存器并设置标志位 |
| comiss xmm0,[eax+04] | 有符号单精度,用减法的方式比较寄存器和内存数据并设置标志位 |
| ucomisd xmm0,xmm1 | 无符号双精度,用减法的方式比较两个寄存器并设置标志位 |
| ucomisd xmm0,[eax+08] | 无符号双精度,用减法的方式比较寄存器和内存数据并设置标志位 |
| comisd xmm0,xmm1 | 有符号双精度,用减法的方式比较两个寄存器并设置标志位 |
| comisd xmm0,[eax+08] | 有符号双精度,用减法的方式比较寄存器和内存数据并设置标志位 |
| - | - |
| cmpss xmm0,xmm1,0 | 无符号单精度,xmm0 = xmm0 == xmm1 ? +0.0 : -QNaN |
| cmpss xmm0,xmm1,1 | 无符号单精度,xmm0 = xmm0 < xmm1 ? +0.0 : -QNaN |
| cmpss xmm0,xmm1,2 | 无符号单精度,xmm0 = xmm0 <= xmm1 ? +0.0 : -QNaN |
| cmpss xmm0,xmm1,3 | 无符号单精度,无序比较,我也没弄清楚 |
| cmpss xmm0,xmm1,4 | 无符号单精度,xmm0 = xmm0 != xmm1 ? +0.0 : -QNaN |
| cmpss xmm0,xmm1,5 | 无符号单精度,xmm0 = xmm0 >= xmm1 ? +0.0 : -QNaN |
| cmpss xmm0,xmm1,6 | 无符号单精度,xmm0 = xmm0 > xmm1 ? +0.0 : -QNaN |
| cmpss xmm0,xmm1,7 | 无符号单精度,有序比较,我也没弄清楚 |
| cmpsd xmm0,xmm1,0 | 无符号双精度,xmm0 = xmm0 == xmm1 ? +0.0 : -QNaN |
| cmpsd xmm0,xmm1,1 | 无符号双精度,xmm0 = xmm0 < xmm1 ? +0.0 : -QNaN |
| cmpsd xmm0,xmm1,2 | 无符号双精度,xmm0 = xmm0 <= xmm1 ? +0.0 : -QNaN |
| cmpsd xmm0,xmm1,3 | 无符号双精度,无序比较,我也没弄清楚 |
| cmpsd xmm0,xmm1,4 | 无符号双精度,xmm0 = xmm0 != xmm1 ? +0.0 : -QNaN |
| cmpsd xmm0,xmm1,5 | 无符号双精度,xmm0 = xmm0 >= xmm1 ? +0.0 : -QNaN |
| cmpsd xmm0,xmm1,6 | 无符号双精度,xmm0 = xmm0 > xmm1 ? +0.0 : -QNaN |
| cmpsd xmm0,xmm1,7 | 无符号双精度,有序比较,我也没弄清楚 |
§控制指令
| 修饰 | 说明 |
|---|---|
| SHORT | 8 位范围内相对跳转(-128 ~ +127) |
| NEAR | 16 位范围内相对跳转(-32768 ~ +32767) |
| FAR | 32 位范围内绝对跳转,会生成 5 字节 E9 指令 |
| FAR | 64 位范围内绝对跳转,会生成 14 字节 FF 25 指令,一般不会这样用而是借用寄存器跳转 |
| 未指定 | 平台相关,例如 64 位平台将生成 14 字节指令 |
jg、jl 使用 OF、AF、SF 标志位,而 ja、jb 使用 ZF、CF、PF 标志位
| 指令 | 说明 |
|---|---|
| mov eip,eax | 设置 IP 为 eax |
| pop eip | 从栈顶弹出到 IP |
| call | 设置 IP 为方法地址并将当前 IP 压栈,方法中用 [ebp+04]、[rbp+08] 获得 call 的地址 |
| ret | 将栈恢复并从栈中弹出 IP 寄存器 |
| jmp eax | 设置 IP 为 eax |
| jmp [eax+04] | 设置 IP 为 [eax+04] |
| je eax | 等于条件,设置 IP 为 eax |
| je [eax+04] | 等于条件,设置 IP 为 [eax+04] |
| jne eax | 不等于条件,设置 IP 为 eax |
| jne [eax+04] | 不等于条件,设置 IP 为 [eax+04] |
| jg eax | 大于条件,设置 IP 为 eax |
| jg [eax+04] | 大于条件,设置 IP 为 [eax+04] |
| jge eax | 大于等于条件,设置 IP 为 eax |
| jge [eax+04] | 大于等于条件,设置 IP 为 [eax+04] |
| jl eax | 小于条件,设置 IP 为 eax |
| jl [eax+04] | 小于条件,设置 IP 为 [eax+04] |
| jle eax | 小于等于条件,设置 IP 为 eax |
| jle [eax+04] | 小于等于条件,设置 IP 为 [eax+04] |
| jng | 同 jle |
| jnl | 同 jge |
| ja eax | 大于条件,设置 IP 为 eax |
| ja [eax+04] | 大于条件,设置 IP 为 [eax+04] |
| jae eax | 大于等于条件,设置 IP 为 eax |
| jae [eax+04] | 大于等于条件,设置 IP 为 [eax+04] |
| jb eax | 小于条件,设置 IP 为 eax |
| jb [eax+04] | 小于条件,设置 IP 为 [eax+04] |
| jbe eax | 小于等于条件,设置 IP 为 eax |
| jbe [eax+04] | 小于等于条件,设置 IP 为 [eax+04] |
| jna | 同 jbe |
| jnb | 同 jae |
§转换指令
| 指令 | 说明 |
|---|---|
| cvtsi2ss xmm0,eax | xmm0 = (Single)eax |
| cvtsi2ss xmm0,[eax+04] | xmm0 = (Single)[eax+04] |
| cvtsi2sd xmm0,eax | xmm0 = (Double)eax |
| cvtsi2sd xmm0,[eax+04] | xmm0 = (Double)[eax+04] |
| cvtss2si eax,xmm0 | eax = (Int32)xmm0 |
| cvtss2si eax,[eax+04] | eax = (Int32)[eax+04] |
| cvtss2sd xmm0,xmm1 | xmm0 = (Double)xmm0 |
| cvtss2sd xmm0,[eax+04] | xmm0 = (Double)[eax+04] |
| cvtsd2si eax,xmm0 | eax = (Int32)xmm0,溢出截断 |
| cvtsd2si eax,[eax+04] | eax = (Int32)[eax+04],溢出截断 |
| cvtsd2ss xmm0,xmm1 | xmm0 = (Single)xmm0,溢出截断 |
| cvtsd2ss xmm0,[eax+04] | xmm0 = (Single)[eax+04],溢出截断 |
§杂项
| 指令 | 说明 |
|---|---|
| DB | 写入字节,DB 67 45 23 01 就是 dd 0x1234567 |
| $opreate $count DUP($byte) | 重复,DB 16 DUP(90) 就是连续写入 16 个 nop,这里的 16 是十进制 |
| align $widthSize | 地址对齐,align 4 即是让接下来的一行对齐到 4 字节,配合 movaps 之类的指令使用 |