汇编我爱编程

CE学习笔记

2018-05-27  本文已影响623人  LazzMan

1.CE修改游戏特例说明

模拟器游戏不能直接修改游戏的程序代码(即不能直接使用代码注入的手段修改code段代码),因为游戏并非使用平台语言所写,只有模拟器是使用平台语言写的,即壳是汇编写的,壳用来翻译跨平台程序,因此不能直接修改跨平台程序。

2.精确搜索

精确搜索时需要根据具体的数值范围(数据类型)搜索数据:
例如:

3.模糊搜索

当模糊搜索的值再次搜索过滤多次还是很多时,可以先精确搜索到能搜索到的地址,然后根据精确地址找模糊地址(因为都是变量数据,都会存储在data中,所以他们的地址应该很近)
大部分游戏显示的数值都经过了加密(*2+1),因此需要模糊搜索来查找,以植物大战僵尸的金币为例:搜索值应为游戏中金币数/10(植物大战僵尸的金币最小单位为10),才可以搜到结果。

4.指针与多级指针

基址:

指针:

CE中基地址找到动态地址:

32位游戏一般多级指针搜索到4级即可,64位一般最多则为8级或12级,但是一般我们超过4级指针后可以通过人造指针来实现。

5.代码注入—AOB注入

AOB注入是最常用的注入脚本,选中需要注入的代码行,


AOB注入

弹出自动汇编窗口中选择AOB注入


AOB注入

生成代码如图:


AOB代码

AOB脚本支持ENABLEDISABLE,因此可以加入到CT列表中,一键开启或关闭脚本功能。

注入点
有时直接对当前代码行注入可能会造成程序报错,选择当前代码行的上一行注入可能会避免程序报错问题。

代码注入点

6.汇编知识

寄存器

可以理解为一个临时变量,每个寄存器有他们惯例的用途,不过这仅仅是惯例,你想用他们做其他事也可以(除了 esp 栈顶指针以外)。
通常我们使 eax, ebx,ecx, edx 做普通的操作,虽然这些寄存器也有一些特殊的用途,但是用途较少所以一般的操作用这些寄存器就可以。
esiedi:看他们的名字,叫做“源索引寄存器”和“目标索引寄存器” (source indexdestination index),因为在很多字符串操作指令用的,在其他的寄存器的时候不用这几个,当然如果你能掌控得好的话 esi 和 edi 其实也可以随便使用。
espesp 是不可以乱动的,esp 指向堆栈顶部,pushpop 指令会影响 esp,由于寄存器的数量太少了,我们编写程序时需要的变量有很多,所以使用内存来辅助我们,函数的局部变量就会保存在栈中,调用函数的参数也会保存在栈中。
比如说,现在我们的寄存器都用完了,我需要腾出一个寄存器来做其他事情。那么就把寄存器的变量放到内存(栈)中(push),然后就可以对这个寄存器为所欲为了,然后用完之后,再把栈中的值提取出来(pop),放回寄存器。

push eax
; 做一堆有关eax的事
; 比如:
; mov eax, [ebp+04]
; add eax, [edx]
; mov [ebp+04], eax
pop eax

常见指令

基础指令集 —— 8086指令集
8086汇编指令总结

sse指令集
movss 表示 Move Scalar Single,移动标量单精度浮点值。
xorps 表示 XOR Packed Single,压缩单精度浮点值逻辑位异或。
cvtps2pd 表示 Convert Packed Single to Packed Double,压缩单精度浮点值转换成压缩双精度浮点值。
addsd 表示 Add Scalar Double,标量单精度浮点值加法。
mulsd 表示 Multiply Scalar Double,标量单精度浮点值乘法。
这些可以直接进行浮点运算的指令、可以直接操作XMM寄存器的指令,都属于 SSE 指令集。
每条汇编指令的名字起得都是有意义的,好好学习英语可以帮助我们更好地理解他们。
看到一条 SSE 指令,要把他拆成两部分,“操作”和“数据类型”,
第一部分:操作。mov、xor、add、mul 这些指令,x86 最基础的指令集中也有。
第二部分:数据类型。ss、sd、ps、pd 这一部分又要拆分成两部分来看。第二位是 s 表示是 single 单精度浮点型,一个数据占 32 位,第二位是 d 表示 double 双精度浮点型,一个数据占 64 位。第一位是 s 表示只操作 XMM 寄存器的第一个数据(ss 就是 32 位,sd 就是 64 位),第一位是 p 表示同时操作全部 128 位数据(ps 就是 4 个 32 位,pd 就是 2 个 64 位)。
xor 后面的两个操作数相同的话就是用来清零的,比如 xor eax,eax 就是令 eax 为 0,这是最简单最快捷的寄存器清零方法。对于 XMM 寄存器同样也是清零。
cvt 指令就是浮点数精度的转换,主要看 s 和 d 的位置,s2d 就是 single to double 单精度浮点数到双精度浮点数,从只占 32 位变成占 64 位,反之 d2s就是双精度到单精度的转换。

常见的函数开头

push ebp ; 保存上一个 ebp
mov ebp, esp ; 把 esp 给 ebp
sub esp, 08 ; 分配8字节栈空间用于局部变量
; 函数主体内容...
; 在这里可以使用 ebp 来定位与函数有关的变量
; [ebp-08] 代表第 2 个局部变量
; [ebp-04] 代表第 1 个局部变量
; [ebp] 刚才 push 进来那个 ebp
; [ebp+04] 函数返回之后的要执行的指令所在位置
; [ebp+08] 代表函数的第 1 一个输入参数
; [ebp+0C] 代表函数的第 2 个输入参数
add esp, 08
pop ebp ; 复原 ebp
ret 08 ; 返回函数调用位置,并且把栈指针 +8,把调用的参数从栈中移除

范例

以植物大战僵尸为例:
阳光地址是一个二级地址。查看反汇编代码是这样子的:

mov [eax*2 + edi + 00005578],esi

其中,edi是指针,eax*2 + 00005578是偏移值,esi是阳光数量,这句代码的作用是把阳光的值放在阳光的内存地址中

7.共用代码段的利用与处理

公共代码段是代码复用的体现,好的代码复用性很高,也就是说一个函数很可能供多个调用者调用。

针对公共代码处理:
以火炬之光为例:
当汇编nop了掉血代码时,发现不仅人物血不掉,而且怪物血也不掉了(因为掉血函数是人物与怪物扣血的共用函数)。
使用代码注入的方式时,要利用cmpje jne jle等条件转移指令判断是否是己方还是敌方,达到己方不掉血。

以CE:STEP9为例:
STEP9中扣除血量的代码就是公用代码


CE:STEP9

先使用浮点搜索找到一个血量地址后,使用什么改写搜索到代码段地址,右键找出指令访问的地址

公共代码段

依次点击攻击,4个单位血量分别降低,可以看到4个单位的血量地址依次显示出来。


4个单位的血量地址

全选地址,右键打开选中地址的分析数据,弹窗默认确认即可,显示如图所示。

数据结构

代码注入(模版使用全部注入)如下:


代码注入
汇编代码

此时点击攻击,友军不再会掉血。

利用公共代码找相关地址:
当我们找到物品栏的某个格子物品数量时,可以通过扫描谁改写了值找到改写代码处,再右键选择“找出指令访问的地址”打开扫描窗口,然后游戏中移动物品到其他格子,就能找到这个代码访问的其他格子地址。原理即是利用公共代码操作多个地址。

8.特征代码的应用 (针对程序段地址会变动)

1. 被迫使用特征码:
以flash游戏国王的战争为例:
由于使用的flash启动的游戏,游戏的内存地址每次都会不同(类似于模拟器游戏),因此只能根据特征码(指令的字节数组)找到修改的地址,再做修改:

特征码—代码的字节数组

CEAA提供一个函数可以查询特征码的地址:

aobscan //CEAA函数 搜索特征码,并将地址赋给变量

例:

aobscan(Money,特征码)  把特征码的地址给了money
money+修改地址的相对偏移量 即要修改代码的地址

2. 主动使用特征码: 实现多版本兼容修改器
以植物大战僵尸为例:有两个版本(无尽版、年度版)进程名相同,代码实现相同(特征码一样),使用特征码便可以实现通用的CT脚本。

9.人造指针 (针对指针级别过多、找不到基址的情况)

以植物大战僵尸为例:
原理:阳光的地址每次重开游戏都会变化,有一个固定的程序段记录阳光的地址

10.线程注入的应用

以植物大战僵尸修改阳光为例:

脚本代码:

      [ENABLE]
      alloc(newmem,256)
      lable(exit)
      Createthread(newmem)
      newmem:
      mov eax,[755e0c]  //阳光基址
      mov eax,[eax+868]
      add [eax+5578],1F4
      cmp [eax+5578],2709 //阳光上限是9990
      jle exit
      mov [eax+5578],2706
      exit:
      ret
      [DISABLE]

线程注入效果:当启动一次脚本阳光数量就+500,相当于游戏修改器中点一次加一次属性的操作。

11.Windows函数应用

以火炬之光为例:
模拟自动回蓝:判断蓝的值小于15时,自动摁2键吃药

    [ENABLE]
    alloc(newmem.1024)
    lable(returnhere)
    lable(originalcode)
    label(exit)
    newmem:
    fld dword ptr [ecx+000003B8]
    cmp [ecx+000003B8],40A00000
    ja originalcode
    push 0
    push 0
    push 0
    push 32     //2键的键码
    call keybd_event
    push 0
    push 0
    push 0
    push 32     //2键的键码
    call keybd_event
    originalcode:
    exit:
    jmp returnhere
    Torchlight.exe+85FE0:
    jmp newmem
    nop
    ......

12.CE的断点选择与跟踪

以皇家守卫军怪物秒杀为例:
在找到怪物血量减少的地方后应该会有判断怪物是否该死亡的跳转,修改跳转完成秒杀功能

以植物大战僵尸自动收集阳光为例:
收集阳光的时候阳光会增加,下内存写入断点,找到阳光增加的语句,返回到上一层,在阳光增加call的上面会有点击阳光的call,修改跳转完成自动收集阳光的功能

【三种断点的原理】
(1)int 3断点,即cc断点,这是一种基于软中断机制断点,3为中断号。OD中,当你在代码区某行按F2即可实现,其机理是把所在代码的第一个字节保存到一张表上,然后将其修改为CC,当程序运行到此代码时,就会产生中断,从而转至中断服务程序。当你去除断点时,OD会从表里读取出当前断点原来的字节内容。
(2)内存断点,假如你用int 3断点对数据区下断,OD会提示你断点可能不会实现,其实也是必然,程序不可能执行数据区,然而我们却可以当数据被读取或写入时进行下断,这种原理主要基于内存属性,当下读写断点是,OD会修改断点处读写属性,如果程序对此数据读写的话,会产生读写异常,OD捕捉此异常并分析,其可以知道运行到何处,对代码段也可以下此断点,机理相似。
(3)硬件断点,这是由硬件实现(这里是CPU实现),其由CPU调试器实现,断点长度有限,其只用两位记录断点长度,所以只支持4个硬件断点,调试寄存器中有3位表示断点状态及属性,000 保留 001 执行断点 010 访问断点 011 写入断点 100 保留 101 临时断点 110 保留 111 保留

上一篇 下一篇

猜你喜欢

热点阅读