lyl610abc 发表于 2021-1-29 12:58

【原创】CE反汇编硬追基址之pvz僵尸数组头部

本帖最后由 lyl610abc 于 2021-1-29 13:28 编辑

# 前言:

看了很多有关植物大战僵尸的教程,比较少看到用汇编来找基址的,大多是用"找出是什么访问了这个地址"来找的

但是这种方法存在弊端,一是可能存在多个指针,二是不一定搜的到

今天给大家带来CE反汇编硬追pvz的僵尸数组头部教程

适合学习人群:比萌新稍微强一丝丝足矣

# 事前准备

本人使用的工具:CE7.2汉化版(不用在意版本问题) PS:OD什么的不需要啦

本人使用的游戏:植物大战僵尸原版的汉化版(非年度版)      就是阳光基址是006A9EC0+768+5560的那个

# 教程内容

## 找到僵尸血量

首先瞄准一只普通僵尸,搜索得到它的血量

PS:普通僵尸的初始血量是270 豌豆射手的攻击是20 (模糊搜索可以找到这些数据)
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210128193537060.png)

所以我们先搜270
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210128193756710.png)

然后命中之后再搜250,过滤得到

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129000559707.png)

很轻松地得到这一只僵尸的血量,先把僵尸的血量改成9999(接下来要让它被狠狠地揍,我们好断点追踪),然后右键"找出是什么改写了这个地址"
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210128194254141.png)


接下来点击"显示反汇编程序"即可开启我们的反汇编之旅

## 开始反汇编追踪之旅
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210128194429846.png)


反汇编窗口如上所示,贴上代码:

```assembly
PlantsVsZombies.exe+131319 - 89 BD C8000000      - mov ,edi   
```

从这句我们可以得到ebp+c8这个地址里存的值就是这只僵尸的血量

于是问题转化为ebp从何而来?

我们在这里F5下个断点 顺便给这里加上备注
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210128195008278.png)


然后回到游戏,等待僵尸被攻击后,断下

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129000720679.png)

这里我们可以看到EBP=14EE1914,暂且记下

大家都知道代码是从上往下执行的,于是我们以这行代码为起点,向上查找是什么给EBP赋值的

再贴代码:

```assembly
PlantsVsZombies.exe+1312D0 - 83 EC 08            - sub esp,08 { 8 }
PlantsVsZombies.exe+1312D3 - 8B 44 24 14         - mov eax,
PlantsVsZombies.exe+1312D7 - 53                  - push ebx                        {这里是函数头部}
PlantsVsZombies.exe+1312D8 - 55                  - push ebp
PlantsVsZombies.exe+1312D9 - 8B 6C 24 14         - mov ebp,      {ebp在这里被赋值}
PlantsVsZombies.exe+1312DD - 56                  - push esi
PlantsVsZombies.exe+1312DE - 8B F0               - mov esi,eax
PlantsVsZombies.exe+1312E0 - 83 E6 08            - and esi,08 { 8 }
PlantsVsZombies.exe+1312E3 - 57                  - push edi
PlantsVsZombies.exe+1312E4 - 89 74 24 10         - mov ,esi
PlantsVsZombies.exe+1312E8 - 75 07               - jne PlantsVsZombies.exe+1312F1
PlantsVsZombies.exe+1312EA - C7 45 54 19000000   - mov ,00000019 { 25 }
PlantsVsZombies.exe+1312F1 - A8 04               - test al,04 { 4 }
PlantsVsZombies.exe+1312F3 - 74 09               - je PlantsVsZombies.exe+1312FE
PlantsVsZombies.exe+1312F5 - 6A 00               - push 00 { 0 }
PlantsVsZombies.exe+1312F7 - 8B C5               - mov eax,ebp
PlantsVsZombies.exe+1312F9 - E8 52F6FFFF         - call PlantsVsZombies.exe+130950
PlantsVsZombies.exe+1312FE - 8B BD C8000000      - mov edi,
PlantsVsZombies.exe+131304 - 8B C5               - mov eax,ebp
PlantsVsZombies.exe+131306 - 89 7C 24 14         - mov ,edi
PlantsVsZombies.exe+13130A - E8 01C4FFFF         - call PlantsVsZombies.exe+12D710
PlantsVsZombies.exe+13130F - 2B 7C 24 20         - sub edi,
PlantsVsZombies.exe+131313 - 89 44 24 1C         - mov ,eax
PlantsVsZombies.exe+131317 - 8B C5               - mov eax,ebp
PlantsVsZombies.exe+131319 - 89 BD C8000000      - mov ,edi { 血量被赋值}

```

我们找到了mov ebp, 并认为ebp就是在这里被赋值的,为了验证这一点,我们在这个位置下断点,并断下
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129000936360.png)

我们可以看到此时的ebp尚且为14,F8单步步过以后果然发现EBP变成了14EE1914   

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129001041030.png)

这时的EBP则转化为了,这时照理来说我们应该重复之前的操作去找esp,但实则不然

因为:

(1)ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。
(2)EBP:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。

萌新看到这里可能一脸懵逼:这是啥子呀,这里我也不说什么堆栈平衡的话了,通俗来说,ebp和esp都是临时的指针,只有像eax、ebx、edx、esi、edi这类寄存器我们才要向上去找是什么去赋值的,而遇到这种mov ebp,这类的,我们**要去找之前push xxx的xxx,或者在前面直接就有mov ,xxx的 **

为什么呢:**一个push会让esp减少4**,我们会发现,这里的ebp是得来的,但我们又能看到上面有一个sub esp,08

也就相当于mov ebp即mov ebp,这里主义mov []里面的值是十六进制的,14=16*1+4=20 20-8=12

不知道进制转换的直接拿计算器算即可
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210128201024895.png)


12=4*3 即3个push xxx,是不是感觉有点意义不明☆,**暂且记下**

接着向上找,发现已经到达了函数的头部,于是我们**得到上一层去寻找**了,如何判断到达头部? 看到 ret 还有一串int3就是了

也可以右键选择当前函数,然后找到选中的第一个,就可以得到函数的头部
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210128201630955.png)
回到刚才我们的那个mov ,edi,下一个断点,下完断点后回到游戏,等游戏再被断下以后,可以用shift+f8执行到返回,也可以一直手动按f8直到遇到一个ret

这里我用了后面那种方式,得到了:
!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129001324786.png)

此时这个pop edi上面的那个call 就是我们刚才那个函数,如果在那个call那里下断,然后F7单步步入就可以进入我们刚才的地方

然后我们在call这里做个断点,并给它一个标记call1(书签也可以设置一下,避免找不到)

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129001446346.png)

**此时我们发现call之前有3个push,而这里的esi正好是第三个push,而esi的值也"正好"等于之前的14EE1914**   

所以我们可以知道,之前的mov ebp,,其实就是将esi的值赋给了它,这边主要是说明一下值的由来,其实大家都不难发现之前就是ESI=EBP=14EE1914了

于是**我们的问题便又变成了**->ebp->esi->

我们接着向上找ESI是由谁给它赋值的,我们发现上面没有给esi赋值的语句,那会不会是上面的call给esi赋值的呢,有这个可能,但是我们可以在call之前设置断点,发现call之前esi就已经被赋值了,一路往上直到函数头部发现ESI的值都已经是被赋值了,得出结论**是从上一层给我们ESI赋值**的。

于是我们故技重施,直接在call这里下断点,然后执行到返回,得到:

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129001830915.png)

贴上代码

```assembly
PlantsVsZombies.exe+6E004 - 55                  - push ebp
PlantsVsZombies.exe+6E005 - 56                  - push esi
PlantsVsZombies.exe+6E006 - 57                  - push edi
PlantsVsZombies.exe+6E007 - 8B F0               - mov esi,eax                              {esi是由eax赋值的}
PlantsVsZombies.exe+6E009 - 8B F9               - mov edi,ecx
PlantsVsZombies.exe+6E00B - 56                  - push esi
PlantsVsZombies.exe+6E00C - 8B C7               - mov eax,edi
PlantsVsZombies.exe+6E00E - E8 1DFDFFFF         - call PlantsVsZombies.exe+6DD30
PlantsVsZombies.exe+6E013 - 8B 5F 5C            - mov ebx,
PlantsVsZombies.exe+6E016 - 83 FB 06            - cmp ebx,06 { 6 }
PlantsVsZombies.exe+6E019 - 75 21               - jne PlantsVsZombies.exe+6E03C
PlantsVsZombies.exe+6E01B - 85 F6               - test esi,esi
PlantsVsZombies.exe+6E01D - 74 1D               - je PlantsVsZombies.exe+6E03C
PlantsVsZombies.exe+6E01F - 8B 46 24            - mov eax,
PlantsVsZombies.exe+6E022 - 83 F8 16            - cmp eax,16 { 22 }
PlantsVsZombies.exe+6E025 - 74 3E               - je PlantsVsZombies.exe+6E065
PlantsVsZombies.exe+6E027 - 83 F8 0C            - cmp eax,0C { 12 }
PlantsVsZombies.exe+6E02A - 74 39               - je PlantsVsZombies.exe+6E065
PlantsVsZombies.exe+6E02C - 8B 86 D8000000      - mov eax,
PlantsVsZombies.exe+6E032 - 83 F8 01            - cmp eax,01 { 1 }
PlantsVsZombies.exe+6E035 - 74 2E               - je PlantsVsZombies.exe+6E065
PlantsVsZombies.exe+6E037 - 83 F8 03            - cmp eax,03 { 3 }
PlantsVsZombies.exe+6E03A - 74 29               - je PlantsVsZombies.exe+6E065
PlantsVsZombies.exe+6E03C - 83 FB 03            - cmp ebx,03 { 3 }
PlantsVsZombies.exe+6E03F - 74 0A               - je PlantsVsZombies.exe+6E04B
PlantsVsZombies.exe+6E041 - 83 FB 05            - cmp ebx,05 { 5 }
PlantsVsZombies.exe+6E044 - 74 05               - je PlantsVsZombies.exe+6E04B
PlantsVsZombies.exe+6E046 - 83 FB 06            - cmp ebx,06 { 6 }
PlantsVsZombies.exe+6E049 - 75 1A               - jne PlantsVsZombies.exe+6E065
PlantsVsZombies.exe+6E04B - 83 FB 06            - cmp ebx,06 { 6 }
PlantsVsZombies.exe+6E04E - 75 0B               - jne PlantsVsZombies.exe+6E05B
PlantsVsZombies.exe+6E050 - 85 F6               - test esi,esi
PlantsVsZombies.exe+6E052 - 74 07               - je PlantsVsZombies.exe+6E05B
PlantsVsZombies.exe+6E054 - 8B C6               - mov eax,esi
PlantsVsZombies.exe+6E056 - E8 E54A0C00         - call PlantsVsZombies.exe+132B40
PlantsVsZombies.exe+6E05B - 56                  - push esi
PlantsVsZombies.exe+6E05C - 8B C7               - mov eax,edi
PlantsVsZombies.exe+6E05E - E8 2DF3FFFF         - call PlantsVsZombies.exe+6D390
PlantsVsZombies.exe+6E063 - EB 1B               - jmp PlantsVsZombies.exe+6E080
PlantsVsZombies.exe+6E065 - 85 F6               - test esi,esi
PlantsVsZombies.exe+6E067 - 74 17               - je PlantsVsZombies.exe+6E080
PlantsVsZombies.exe+6E069 - 8B C6               - mov eax,esi
PlantsVsZombies.exe+6E06B - E8 C0F1FFFF         - call PlantsVsZombies.exe+6D230
PlantsVsZombies.exe+6E070 - 8D 0C 5B            - lea ecx,
PlantsVsZombies.exe+6E073 - 8B 14 8D C8F16900   - mov edx,
PlantsVsZombies.exe+6E07A - 52                  - push edx
PlantsVsZombies.exe+6E07B - E8 40370C00         - call PlantsVsZombies.exe+1317C0 { call2 }
```

我们向上找可以发现 mov esi,eax,说明esi是由eax得来的

问题再次转化为->ebp->esi->->

这时我们发现又到了函数头部,再次故技重施,下断并执行到返回,再来到上一层

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129001954312.png)

贴上代码:

```assembly
PlantsVsZombies.exe+6D04E - 5F                  - pop edi
PlantsVsZombies.exe+6D04F - 5E                  - pop esi
PlantsVsZombies.exe+6D050 - 5D                  - pop ebp
PlantsVsZombies.exe+6D051 - 5B                  - pop ebx
PlantsVsZombies.exe+6D052 - 83 C4 20            - add esp,20 { 32 }
PlantsVsZombies.exe+6D055 - C2 0400               - ret 0004 { 4 }
PlantsVsZombies.exe+6D058 - E8 E3FCFFFF         - call PlantsVsZombies.exe+6CD40 { EAX在这里面}
PlantsVsZombies.exe+6D05D - 8B D0               - mov edx,eax                                                {edx被eax赋值}
PlantsVsZombies.exe+6D05F - 85 D2               - test edx,edx
PlantsVsZombies.exe+6D061 - 74 EB               - je PlantsVsZombies.exe+6D04E
PlantsVsZombies.exe+6D063 - 80 BA BE000000 00   - cmp byte ptr ,00 { 0 }
PlantsVsZombies.exe+6D06A - 74 0B               - je PlantsVsZombies.exe+6D077
PlantsVsZombies.exe+6D06C - 8B CD               - mov ecx,ebp
PlantsVsZombies.exe+6D06E - E8 1D000000         - call PlantsVsZombies.exe+6D090
PlantsVsZombies.exe+6D073 - 84 C0               - test al,al
PlantsVsZombies.exe+6D075 - 75 D7               - jne PlantsVsZombies.exe+6D04E
PlantsVsZombies.exe+6D077 - 8B C2               - mov eax,edx                                                {eax被edx赋值}
PlantsVsZombies.exe+6D079 - 8B CD               - mov ecx,ebp
PlantsVsZombies.exe+6D07B - E8 800F0000         - call PlantsVsZombies.exe+6E000 { call3 }
```

不难发现,我们这里的eax是由edx赋值的;再向上找到mov edx,eax,edx又变成了eax所以我们还是要找eax

这里其实还遇到了一个小问题,就是在mov edx,eax 这里要下断点证明是不是eax将值赋给了edx,但是设置了断点以后,一进游戏就断下来了,并且eax的值并不是14EE1914,这个时候可能就有人觉得这里不是赋值处了,但其实这里就是赋值的地方,它是一个**共用段**,于是我们要通过**条件断点**来过滤,条件断点设置如下

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129002638911.png)

通过条件断点后,果然断下了

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129003109596.png)

再往上看只有一个CALL 了,要么EAX就是在CALL里被赋值的,要么就是在上一层被赋值的

怎么确认呢?我们可以在CALL 这里下个条件断点,如果是上一层赋值的,则会断下,否则不会断下

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129003330136.png)

设置条件断点以后发现并没有断下,得出结论,EAX是在CALL里面被赋值的

于是我们先设置一个普通的断点,然后F7单步步入CALL中,然后找到CALL的尾部(跳出CALL之前肯定EAX已经被赋值完毕了),于是我们右键选择当前函数,然后找到被选中的最后一行,如图:

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129003745902.png)

代码如下:

```assembly
PlantsVsZombies.exe+6CE5F - E8 8CFAFAFF         - call PlantsVsZombies.exe+1C8F0
PlantsVsZombies.exe+6CE64 - 84 C0               - test al,al
PlantsVsZombies.exe+6CE66 - 0F85 25FFFFFF         - jne PlantsVsZombies.exe+6CD91
PlantsVsZombies.exe+6CE6C - 8B 44 24 18         - mov eax, { eax被赋值 }
PlantsVsZombies.exe+6CE70 - 5F                  - pop edi
PlantsVsZombies.exe+6CE71 - 5E                  - pop esi
PlantsVsZombies.exe+6CE72 - 5D                  - pop ebp
PlantsVsZombies.exe+6CE73 - 5B                  - pop ebx
PlantsVsZombies.exe+6CE74 - 83 C4 30            - add esp,30 { 48 }
PlantsVsZombies.exe+6CE77 - C2 0400               - ret 0004 { 4 }
```

从下往上找,不难找到mov eax, 这一句,验证一下,在这一句下方设置条件断点

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129004014334.png)

断下

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129004050900.png)

根据前面的经验,我们知道是之前入栈的某个地址,而这里ebx正好等于eax,所以我们就可以将问题转为找ebx

从下往上接着寻找可以找到

```assembly
PlantsVsZombies.exe+6CDF9 - 8B DE               - mov ebx,esi { ebx被赋值 }
```

于是ebx又变成了esi,接着往上找可以找到

```assembly
PlantsVsZombies.exe+6CD70 - 8D 74 24 14         - lea esi,      {这里的esp+14正好和下面赋值对应}
PlantsVsZombies.exe+6CD74 - 89 44 24 18         - mov ,eax
PlantsVsZombies.exe+6CD78 - 89 44 24 1C         - mov ,eax
PlantsVsZombies.exe+6CD7C - 89 44 24 14         - mov ,eax
PlantsVsZombies.exe+6CD80 - E8 6BFBFAFF         - call PlantsVsZombies.exe+1C8F0                         { call }
PlantsVsZombies.exe+6CD85 - 84 C0               - test al,al
PlantsVsZombies.exe+6CD87 - 0F84 DF000000         - je PlantsVsZombies.exe+6CE6C
PlantsVsZombies.exe+6CD8D - 8B 6C 24 30         - mov ebp,
PlantsVsZombies.exe+6CD91 - 8B 74 24 14         - mov esi,               { esi被赋值 }
PlantsVsZombies.exe+6CD95 - 8B 46 1C            - mov eax,
PlantsVsZombies.exe+6CD98 - 2B 47 1C            - sub eax,
PlantsVsZombies.exe+6CD9B - BB 19000000         - mov ebx,00000019                        
```

这里我们可以找到mov esi,,在这里的下一句下条件断点,验证是否是esi被赋值

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129004650120.png)

果然断下了,但这里我们发现没有寄存器的值和ESI相同,说明寄存器在**入栈**之后,又被改变了

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129004810875.png)

于是我们只能接着向上找push xxx,或者mov ,xx ,这里找的是mov ,xx 因为上面的lea esi,是传址指令,相当于mov ,xx = mov ,xx

我们很快看到上面有个mov ,eax 在那里的下一句设置条件断点EAX==0x14EE1914,很可惜并没有断下,说明CALL里面有压入的参数,于是我们直接进入CALL中查看

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129005559296.png)

直接Ctrl+G,然后输入CALL后面的地址0041C8F0 即可跳转到CALL里面进行查看

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129005916187.png)

CALL里面的代码如下:

```assembly
PlantsVsZombies.exe+1C8F0 - 57                  - push edi
PlantsVsZombies.exe+1C8F1 - BF 0000FFFF         - mov edi,FFFF0000 { -65536 }
PlantsVsZombies.exe+1C8F6 - 8B 06               - mov eax,
PlantsVsZombies.exe+1C8F8 - 85 C0               - test eax,eax
PlantsVsZombies.exe+1C8FA - 75 08               - jne PlantsVsZombies.exe+1C904
PlantsVsZombies.exe+1C8FC - 8B 82 90000000      - mov eax,
PlantsVsZombies.exe+1C902 - EB 05               - jmp PlantsVsZombies.exe+1C909
PlantsVsZombies.exe+1C904 - 05 5C010000         - add eax,0000015C { 348 }
PlantsVsZombies.exe+1C909 - 8B 8A 94000000      - mov ecx,
PlantsVsZombies.exe+1C90F - 69 C9 5C010000      - imul ecx,ecx,0000015C { 348 }
PlantsVsZombies.exe+1C915 - 03 8A 90000000      - add ecx,
PlantsVsZombies.exe+1C91B - 3B C1               - cmp eax,ecx
PlantsVsZombies.exe+1C91D - 73 12               - jae PlantsVsZombies.exe+1C931
PlantsVsZombies.exe+1C91F - 90                  - nop
PlantsVsZombies.exe+1C920 - 85 B8 58010000      - test ,edi
PlantsVsZombies.exe+1C926 - 75 13               - jne PlantsVsZombies.exe+1C93B
PlantsVsZombies.exe+1C928 - 05 5C010000         - add eax,0000015C { 348 }
PlantsVsZombies.exe+1C92D - 3B C1               - cmp eax,ecx
PlantsVsZombies.exe+1C92F - 72 EF               - jb PlantsVsZombies.exe+1C920
PlantsVsZombies.exe+1C931 - C7 06 FFFFFFFF      - mov ,FFFFFFFF { -1 }
PlantsVsZombies.exe+1C937 - 32 C0               - xor al,al
PlantsVsZombies.exe+1C939 - 5F                  - pop edi
PlantsVsZombies.exe+1C93A - C3                  - ret
PlantsVsZombies.exe+1C93B - 89 06               - mov ,eax { 赋值}
PlantsVsZombies.exe+1C93D - 80 B8 EC000000 00   - cmp byte ptr ,00 { 0 }
PlantsVsZombies.exe+1C944 - 75 B0               - jne PlantsVsZombies.exe+1C8F6
PlantsVsZombies.exe+1C946 - B0 01               - mov al,01 { 1 }
PlantsVsZombies.exe+1C948 - 5F                  - pop edi
PlantsVsZombies.exe+1C949 - C3                  - ret

```

这个CALL比较简短,我们直接从下面找起 ,一下就找到了mov ,eax 可以下个条件断点验证一下

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129011029891.png)

果然断下

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129011127073.png)

于是问题又变成了找eax(这个14EE1914我们转了多少手寄存器了???)

接着向上找EAX,这里EAX上方是RET,则肯定是从上方的语句跳转过来的,

不难找到跳转过来的语句:

```assembly
PlantsVsZombies.exe+1C926 - 75 13               - jne PlantsVsZombies.exe+1C93B
```

于是从跳转过来的语句接着向上找EAX的赋值

```assembly
PlantsVsZombies.exe+1C8F6 - 8B 06               - mov eax,                        {eax赋值为内部的值}
PlantsVsZombies.exe+1C8F8 - 85 C0               - test eax,eax                        {判断eax是否为0}
PlantsVsZombies.exe+1C8FA - 75 08               - jne PlantsVsZombies.exe+1C904      {为0则不跳转}
PlantsVsZombies.exe+1C8FC - 8B 82 90000000      - mov eax,                        {eax赋值为}
PlantsVsZombies.exe+1C902 - EB 05               - jmp PlantsVsZombies.exe+1C909      
PlantsVsZombies.exe+1C904 - 05 5C010000         - add eax,0000015C { 348 }
PlantsVsZombies.exe+1C909 - 8B 8A 94000000      - mov ecx,
PlantsVsZombies.exe+1C90F - 69 C9 5C010000      - imul ecx,ecx,0000015C { 348 }
PlantsVsZombies.exe+1C915 - 03 8A 90000000      - add ecx,
PlantsVsZombies.exe+1C91B - 3B C1               - cmp eax,ecx
PlantsVsZombies.exe+1C91D - 73 12               - jae PlantsVsZombies.exe+1C931
PlantsVsZombies.exe+1C91F - 90                  - nop
PlantsVsZombies.exe+1C920 - 85 B8 58010000      - test ,edi
PlantsVsZombies.exe+1C926 - 75 13               - jne PlantsVsZombies.exe+1C93B
```

我们通过汇编语句不难得出 EAX刚开始是由赋值的,但刚开始为0的话则为赋值,也可以从外面对CALL下断,分析EAX的赋值过程得到:头部由来是,每个僵尸的大小是15C,这里其实我们已经得到了僵尸数组的动态头部地址,我们接着追溯EDX,这里可以看一下动态地址的头部14EE0CD8,和我们的14EE1914相减得到C3C=3132      15C=348      3132/348=9,说明我们这个僵尸是由头部地址偏移了9个僵尸~~ 然后记录一下

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129013745019.png)

接着就是找EDX,此时EDX的值为14EDB520

到这里找EDX就直接在CE里搜索了(也可以汇编里接着向上找,但一是出于篇幅问题,二是留给大家练手当作业吧)

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129122247763.png)

CE里直接搜索14EDB520,也就是EDX的值,然后去掉第一个动态的地址,第二个就是了,这个大家应该都很熟练了

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129122404999.png)

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129122425097.png)

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129122509689.png)

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129122547256.png)

基址找到了,手动添加验证一下:

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129122652158.png)


正确得到当前的僵尸血量~~~我们的反汇编追踪之旅算是告一段落了

# 作业

实践是检验真理的唯一标准。这不来两份热乎的作业温习温习?

第一份作业就是上面说的,继续通过反汇编的方式,从下往上跟踪EDX,然后找到基址

第二份作业则利用到了我们找到的这个僵尸数组头部的赋值语句,我们都知道所有僵尸的产生都会在这里的代码通过,那么能否在这块代码HOOK,让僵尸直接GO DIE呢?这种全屏秒杀才算是真正的全屏秒杀,直接从源头将你弄死~~~现在大多数的全屏秒杀都是通过在僵尸移动的那一块代码直接修改僵尸的状态来实现的,但是别忘了最后的僵尸BOSS,它貌似没有移动吧?因此僵尸王无法被秒杀,秒杀不够完美。第二份作业要求:**HOOK僵尸头部的这块代码,实现可以秒杀包括僵尸BOSS在内的所有僵尸**

# 总结(注意事项)

反汇编找基址其实就是**从下往上**,寻找目标寄存器的来源,最终找到目标

寄存器中比较特殊的**EBP和ESP**,要通过寻找之前入栈的寄存器来获得赋值来源

入栈操作不仅仅是push xxx 指令 还有 mov ,xxx 或者先lea xxx,,再mov ,xxx等等

有的时候向上追代码的时候,会遇到明明还没攻击到僵尸却引发了中断,并且此时寄存器里的内容都不对,有可能是**共用代码段**,需要用**条件断点**来进行判断

找到赋值语句以后可以在那里下个断点,看是否执行以后寄存器内容被修改,**验证语句来源的正确性**

有时候会遇到很多跳转,但要记住,如果我们的语句执行了,那么跳过我们语句的跳转肯定是没有跳的,不然我们怎么执行?

遇事不决的时候找不到来源的时候**多下断点**,看看寄存器的变化,实在找不到就去函数头部下断,然后一步步单步步过,看在哪里寄存器的值发生了改变

# 个人感言(无关技术,可跳过)

白嫖了网络教程资源这么多年,都没写过技术教程,这次算是我的首次技术教程吧,能力不足,水平有限,欢迎大家指出我的不当之处,也请大家多多包涵一下,希望能够一起变强。

最后碎碎念一下:这个教程肝了我一晚上,**太肝了**。出这个教程的初衷是看到现在的找基址教程十个有九个是PVZ(夸张),但一个汇编追基址的都没有(可能有?),于是出一期看看反响如何,之后可能会接着出**汇编硬追代码**的系列教程,视大家的**热情程度**而定吧

fanvalen 发表于 2021-1-29 15:11


define(address,"PlantsVsZombies.exe"+166D10)
define(bytes,89 B5 C8 00 00 00)


assert(address,bytes)
alloc(newmem,$1000)

label(code)
label(return)

newmem:
//将计算怪的剩余血置零它就死亡了(只写了一句汇编mov esi,0)
mov esi,0
code:
mov ,esi
jmp return

address:
jmp newmem
nop
return:


address:
db bytes
// mov ,esi

dealloc(newmem)


根据你提供的普通僵尸血量我找了
做了一个秒杀普通僵尸的ct脚本
耗时不到1分钟,基本下次也有效
当然研究汇编是一种读懂代码的方式
我只最求效率不求过程那种方式方便快捷就用那种
define(address,"PlantsVsZombies.exe"+166896)
define(bytes,89 8F D0 00 00 00)


assert(address,bytes)
alloc(newmem,$1000)

label(code)
label(return)

newmem:
mov ecx,0
code:
mov ,ecx
jmp return

address:
jmp newmem
nop
return:


address:
db bytes
// mov ,ecx

dealloc(newmem)
这还有个秒高防的


alloc(newmem,2048)
label(returnhere)
label(originalcode)
label(exit)

newmem:

mov ,0
originalcode:
sub ,esi

exit:
jmp returnhere

"PlantsVsZombies.exe"+1663FE:
jmp newmem
nop
returnhere:





dealloc(newmem)
"PlantsVsZombies.exe"+1663FE:
sub ,esi
//Alt: db 29 B7 DC 00 00 00

最后来个秒手持防御的

lyl610abc 发表于 2021-1-29 12:59

后续补充

本帖最后由 lyl610abc 于 2021-2-5 19:15 编辑

# 补充

首先感谢大家的评论和建议。这里先总结一下大家提出的问题:

- 反汇编追代码太过繁琐:如果只是为了实现秒杀僵尸根本无需如此大费周章,现在的代码注入HOOK之类的实现功能更加快速、高效。
- 看不懂

先说说看不懂如何解决:本教程面向的对象需要**比萌新稍微强一丝丝**,如果只是萌新可能还是有点吃力的,当然不排除个别能力很强的萌新,在学习之前可以先看看其它人的教程:**如何找到阳光基址的以及指针和汇编的相关知识**

## 不汇编定位僵尸头部数组

本教程主要还是带大家学习如何通过反汇编来分析和追踪基址,如果只追求实现的话确实大可不必

接下来给大家带来如何不通过反汇编追代码来得到僵尸数组的头部地址:

### 僵尸数组结构分析

我们已经知道所有僵尸都是存放在同一个数组即僵尸数组中的,而每个僵尸都是用相同的结构体,暂时将其为zombie结构体:

```c
struct zombie{
    float visiblePositionX;      //x坐标(图像位置)
    float visiblePositionY;      //y坐标(图像位置)
    int visible;                        //是否可见
    int row;                              //所在行数
    .......................................
    inthp;                              //当前血量
    int hpMAX;                              //血量上限
    .......................................
};
```

所有僵尸的结构体的属性(成员)都是相同的,则每个僵尸结构体的长度(存储空间)应该也是相同的

所以我们可以分析出

僵尸的地址=僵尸数组头部地址+n*僵尸结构体长度      (n为第几个僵尸)

### 实际操作

已经知道了僵尸的结构,前面我们可以通过血量减去偏移C8得出僵尸地址 那么我们可以定位两只僵尸,并获得它们的地址,这两个地址相减得到的数值一定就是僵尸结构体长度的整数倍,并且一般来说,前后产出的两只僵尸应该就是临近的,所以得到的差值应该就是僵尸结构体的长度。

首先我们要找到两只僵尸的血量,这里因为我们前面已经得到了僵尸扣血的那个反汇编代码:

```assembly
PlantsVsZombies.exe+131319 - 89 BD C8000000      - mov ,edi      {血量被赋值}
```

于是我们可以在这里右键找出指令访问的地址来找到僵尸的血量

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129171456500.png)

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129171733700.png)

轻松定位到了,然后我们将这两个地址加入列表

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129171833349.png)

接着再将两个地址相减(因为它们都加上了C8的血量偏移,所以直接相减不影响结构体长度的计算结果)得到:

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129172005546.png)

我们得出了和我们之前辛辛苦苦找到的每个僵尸占用空间大小一样的数值15C

得到这个关键数值后,我们就可以从当前僵尸地址开始,减去n*15C,n从1开始不断加1,直到明显出现有异于僵尸血量的数值,则最后一个符合僵尸血量的数值就是僵尸数组头部地址+C8

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129172518931.png)

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129172530112.png)

由此我们可以得出结论当前僵尸地址-8*15c-c8就可以得到僵尸数组的头部地址

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129172703633.png)

可以根据我们之前找到的指针来验证是否就是僵尸数组头部地址

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210129172746905.png)

于是我们就不费吹灰之力地收割了僵尸头部数组

# 最后
更新新教程了:https://www.52pojie.cn/thread-1366757-1-1.html
感谢大家的评论和支持,欢迎讨论,希望我们能一起学习,一起进步~~~

fanvalen 发表于 2021-1-29 13:41

我就比较懒从不找基址,直接在作用代码段改汇编,然后找个特征码定位这个汇编地址,因为代码生成程序后基本不变,改变的只有在内存中的位置

skymilong 发表于 2021-1-29 13:23

{:301_1000:} 主要是之前的教程都在教如何实现什么效果,楼主辛苦

lyl610abc 发表于 2021-1-29 13:30

skymilong 发表于 2021-1-29 13:23
主要是之前的教程都在教如何实现什么效果,楼主辛苦

论坛的markdown格式对图床的图片有点问题,刚刚进去手动修改了一下{:1_909:}
图片已经可以了吧:'(weeqw

世俗红尘 发表于 2021-1-29 14:16

虽然看不懂{:301_979:}

geleisisisi 发表于 2021-1-29 14:24

非常棒的教程,反追代码的思路非常值得学习
但是现实操作起来,如果只是为了基址性价比太低了

列明 发表于 2021-1-29 14:28

腦子:
我沒看會!

手:
這次你終於說了句實話!

zhouxinyi 发表于 2021-1-29 15:09

死追基址太费力费神费眼睛,所以有时候偷懒,能得到大致相同结果就好了:lol
页: [1] 2 3 4 5
查看完整版本: 【原创】CE反汇编硬追基址之pvz僵尸数组头部