吾爱破解 - LCG - LSG |安卓破解|病毒分析|破解软件|www.40881110.com

 找回密码
 注册[Register]

QQ登录

只需一步£¬快速开始

查看: 11957|回复: 157
上一主题 下一主题

[调试逆向] CE教程£º进阶篇 CE Tutorial Games gtutorial

    [复制链接]
跳转到指定楼层
楼主
Ganlv 发表于 2019-3-29 23:38 回帖奖励
本帖最后由 Ganlv 于 2019-4-8 23:53 编辑

CE 教程£º进阶篇 CE Tutorial Games

本文大约 1.2 万字£¬阅读可能需要很长时间¡£本文包含大量图片£¬全文加载可能需要 10MB 以上的流量¡£

相关链接£º之前由 vnetuser 发布的 CE6.8 gTutorial STEP 1,2,3全集 简单破解教程

注意事项

本文使用 署名-?#24039;?#19994;性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 许可证发布£¨可以参考 此协议的中文翻译£©¡£您可以随意分享本文章的链接¡£全文转载的具体注意事项请阅读协议内容£¬转载的话最好在下面回帖告诉我一声¡£

请注意£º本文仍有可能更新£¬全文转载的话可能需要考虑更新的问题¡£

我用的是本文起草时£¨2019 年 3 月 26 日£©的最新版 Cheat Engine 6.8.3£¨在 2019 年 2 月 9 日发布的£©

请尽量在看懂 CE 教程£º基础篇 CE Tutorial 之后再来看本篇文章¡£

绪言

2018 年 6 月 8 日£¬Cheat Engine 6.8 发布£¬软件中新增了一个 Cheat Engine Tutorial Games¡£这个新的小游戏有 3 关£¬并不是特别难修改£¬但是却很有意思¡£因为它不再是之前的教学程序那种技能教学£¬而是一种有目的实战教学¡£

每一关目标只有一个£¬但办法是多种多样的¡£这篇文章尽可能利用不同方法来解决问题¡£重点不是修改这个小游?#32602;?#37325;点是理解其中的思路¡£

打开 CE Tutorial Games

菜单栏 ¡ú Help ¡ú Cheat Engine Tutorial Games

第 1 关

第 1 关?#22909;?5 次射击你必须重新装填£¬在这个过程?#24515;?#26631;会回复血量£¬尝试找到一种方法消灭目标¡£

[Tips]

游?#20998;?#20351;用空格键射击¡£

第 1 关尝试 1

有数字的时候肯定先尝试搜索数字£¬毕竟这个是最方便快捷¡¢最直观准确的方式¡£

右下角有个数字 5£¬新扫描£¬搜索 4 字节的精确数值 5¡£

射一发之后再搜索 4¡£

不知道你们搜索到没有£¬反正我是没有¡£

第 1 关尝试 2

?#19968;?#30097;上一种方法有?#27605;Ë|?#21487;能子弹没撞到目标和撞到目标时£¬游戏的数据是两种状态¡£

我勾选了 Pause the game while scanning¡£

在子弹射出过程中搜索 4£¬结果发?#21482;?#26159;不行¡£

第 1 关尝试 3

有时候游?#20998;?#26174;示的数字并不是内存中?#23548;?#23384;储的数据£¬你看到的只是计算结果¡£

[Info]

如果你学习过简单的编程知识£¬你应该了解堆内存和栈内存的区别

堆内存£¨这里的堆 Heap 与数据结构的堆 Heap 完全无关£¬这只是一种名称£©通常是使用 malloc 函数分配的£¬一旦分配完仅用?#21019;?#20648;某个确定结构¡¢数组¡¢对象£¬通常直到使用 free 释放之前都代表同一游戏数据¡£堆内存在传递的时候只会传递指针¡£

栈内存£¬栈内存是不断复用的£¬栈内存通常用作函数的局部变量¡¢?#38382;ý¡?#36820;回值£¬函数调用时一层一层嵌套的£¬调用一个函数£¬栈就会增长一块£¬一个函数调用完返回了£¬栈就会缩短£¬下一个函数再调用£¬这块内存就会被重新使用¡£所以栈内存是会快速变化的£¬搜索到栈内存通常都没有什么意义¡£你应该听过 ESP 和 EBP£¬SP 就是 Stack Pointer£¬栈指针£¬描述?#27426;?#22312;什么内存位置的寄存器¡£

这里有两篇扩展阅读£¨说实话我?#32422;?#37117;没看£©£º基于栈的内存分配 - 维基百科¡¢内存管理 动态内存分配 - 维基百科

我猜测他显示 5 的时候其?#30340;?#23384;中是已发射 0 颗子弹¡£5 只是一个局部变量的计算结果£¬他在栈中只存在很短的一段时间£¬搜索是搜索不到的¡£

所以显示 5 的时候搜索 0£¬显示 4 的时候搜索 1¡£

[Comment]

这个地址是在运行之后分配的£¬你的搜索结果可能和我不一样

把搜索结果添加到下方地址列表中£¬点击左侧的小方块锁定£¬这样就可以了¡£

[Tips]

这个游?#20998;?#26174;示的是 5 而存储的是 0¡£类似的£¬某些游戏的货币可能都是 10 的倍数£¬?#28909;?500 金币£¬内存中存储的可能就是 50£¬而不是显示的 500¡£例如£º?#21442;?#22823;战僵尸¡£

第 1 关尝试 4

你以为这样就结束了吗£¿

这个游戏真的很有意思¡£你的最终目的是要打败敌人£¬那如果我直?#24433;训?#20154;就设置为 1 滴血£¬会怎么样呢£¿

血条没有具体数值£¬我们使用未知初始值£¨Unknown initial value£©来搜索¡£

类型怎么选呢£¿血量这种东西一般我会先试试 Float£¨单精度浮点型£©£¬然后再试试 4 字节整数£¬如果不行的话再试试 8 字节和双精度浮点型£¬再不行的话就方案吧¡£

这里搜索了好?#22797;?#36824;剩几个结果£¬凭感觉应该是第一个£¬因为敌人回血回到满的时候£¬第一个数?#30331;?#22909;是 100¡£

直?#24433;训ÐÈ搜?#37327;改为 1£¬然后发射子弹¡£

一发入魂¡£

第 1 关尝试 5

找到刚才那两个内存地?#20998;?#21518;£¬我们还可以尝试代码注入£¬但是由于第 1 关是在太简单了£¬没有必要这样大费周章请?#21019;?#30721;注入这种复杂的东西£¬内存修改搞定就行¡£代码注入的应用在下一关会提到¡£

第 2 关

第 2 关£º这两个敌人和你相比拥有更多的血量¡¢造成更多的伤害¡£消灭他们¡£提示/警告£º敌人和玩家是相关联的¡£

[Tips]

游?#20998;?#20351;用左右方向键控制旋转£¬使用上方向键控制前进£¬使用空格键射击¡£

第 2 关尝试 1

敌人两个人一起打我们£¬每次要掉 4 滴血£¬我们总共才 100 滴血£¬而我们打敌人£¬每次大概就掉 1/100£¬而且对面还有两个人¡£

这谁顶得住啊£¡

我们直接搜?#32422;?#30340;血量£¬把血量改成上千£¬然后激情对射¡£

来呀£¬互相伤害啊£¡

然后...

第 2 关 Plus

第 2 关加强£º你将会为之付出代价£¡启动究极炸弹¡£3¡¢2¡¢1¡£

啊£¡我死了¡£

9199 血的我被炸到 -1 滴血¡£

第 2 关尝试 2

怎么办呢£¿

我们发现£¬我们的子弹飞行速度比较快£¬我可以先把两个人都打到只剩 1 滴血£¬然后杀掉其中一个£¬另一个会启动究极炸弹£¬这时我只需要一发小子弹就能?#35759;?#38754;打死¡£

然而¡£

我太天真了¡£

对面另外一个虽然也残血£¬但是启动究极炸弹的时候能回血¡£

第 2 关尝试 3

肯定有人觉得麻烦了£¬你直接搜索敌?#25628;?#37327;改成 1 不就得了£¬对面就算回血£¬就再改成 1¡£

果然£¬他又回到了 21 滴血£¬?#20197;?#25913;成 1 滴血£¬然后开火¡£

谁让他的炸弹飞的慢呢~

第 2 关尝试 4

这?#20174;?#24471;好像比较不保险£¬万一游戏作者?#35757;?#20154;导弹的速度调的比我们子弹快£¬那不就完蛋了¡£

我们?#21019;?#26681;本上解决问题¡£

找出修改我们?#32422;?#34880;量的指令£¬Find out what writes to this address¡£

然后把这个语句替换成 NOP £¨No operation£©£¬原来修改血量的代码就会变成什么也不做¡£

再与敌人打几个回合£¬发现£¬我们不掉血了£¬但敌人也不掉血了¡£

Tip/Warning: Enemy and player are related

提示/警告£º敌人和玩家是相关联的¡£

这就是¡°共用代码?#20445;¨Shared Code£©£¬敌人和我们减血的代码是共用的£¬不能简单地修改为 NOP£¬我们需要做一些判断¡£

第 2 关尝试 5

我们先把指令还原¡£

你可以分别对这三个地址使用 Find out what writes to this address£¬看看什么指令写入了这个地?#32602;?#20320;应该会有所发现¡£

你可以看到£¬分别向这三个地址写的指令是同一条指令£¨指令地址相同£¬就是图中的 10003F6A3£©¡£

?#28909;?#36825;几个血量的修改是通过相同的代码£¬那么就表示玩家的数据存储方式和敌人的数据存储方式是相同的£¬至少血量都是在 +60 的位置存储¡£

我们的想法是£º从储存玩家和敌人信息的结构体中找出一些差别£¬然后靠代码注入构造一个判断£¬如果是玩家?#32422;?#30340;话则跳过£¬不扣血¡£

这里使用 Dissect data/structures¡£

里面默认已经有一个地址了£¬我们再额外添加两个地址¡£

然后填入三个 血量地址 - 60£¬要注意这里需要减掉 60£¬因为 +60 之后的是血量地?#32602;?#25226;这个 60 减掉才是结构体的开头¡£

因为两个同类的结构肯定不能重叠£¬所以这里我可以算一下两个结构体的距离£¬一个结构体最大只有 160 字节£¬再大就会重叠了¡£

[Tips]

通常情况下£¬两个结构体会相距比?#26174;¶£?#20320;可以?#23454;?#35774;置这个数值£¬?#28909;?#35774;置一个 1024 甚至 4096 字节之类的£¬反正你觉得应该足够就行¡£

我们的逻辑就是

if (*(p + 70) == 0) { // 0 表示是玩家?#32422;?    // 什么也不干
} else {
    // 正常扣血
}

Find out what writes to this address ¡ú Show disassembler ¡ú Tools ¡ú Auto Assemble ¡ú Template ¡ú Code injection

这是自动生成的代码

alloc(newmem,2048,"gtutorial-x86_64.exe"+3F6A3)
label(returnhere)
label(originalcode)
label(exit)

newmem: //this is allocated memory, you have read,write,execute access
//place your code here

originalcode:
sub [rax+60],edx
ret
add [rax],al

exit:
jmp returnhere

"gtutorial-x86_64.exe"+3F6A03:
jmp newmem
nop
returnhere:

代码注入的原理就是?#35328;?#26469;那个位置的指令换成 jmp£¬跳转到我?#20999;?#30003;请的一块内存中£¬程序正常运行到这里就会跳转到我?#20999;?#30003;请的那块内存中£¬然后执行我们的指令£¬我们?#32422;?#20889;的指令的最后一条指令是跳转回原来的位置£¬这样程序中间就会多执行一段我们的指令了¡£

不过这里有一点问题£¬

originalcode:
sub [rax+60],edx
ret
add [rax],al

ret 语句之后是另外一个函数了£¬我们这样修改的话£¬如果有人调用那个函数就会出错£¬我们把注入点往前挪一下¡£

gtutorial-x86_64.exe+3F6A0 - 48 89 C8              - mov rax,rcx
gtutorial-x86_64.exe+3F6A3 - 29 50 60              - sub [rax+60],edx
gtutorial-x86_64.exe+3F6A6 - C3                    - ret
gtutorial-x86_64.exe+3F6A7 - 00 00                 - add [rax],al

重新生成一个 Code injection£¬注入点设置为上一条语句 gtutorial-x86_64.exe+3F6A0¡£

alloc(newmem,2048,gtutorial-x86_64.exe+3F6A0) 
label(returnhere)
label(originalcode)
label(exit)

newmem: //this is allocated memory, you have read,write,execute access
//place your code here

originalcode:
mov rax,rcx
sub [rax+60],edx

exit:
jmp returnhere

gtutorial-x86_64.exe+3F6A0:
jmp newmem
nop
returnhere:

其他部分不用动£¬我们直接在 originalcode 上修改

originalcode:
mov rax,rcx
cmp [rax+70],0
je exit // 如果等于 0£¬则表示玩家£¬跳到 exit£¬不执行下一条 sub 语句
sub [rax+60],edx

exit:

[Tips]

双斜线后面是注释£¬删除掉也可以¡£

然后点击 Execute

我们可?#32422;?#21333;修改一下£¬然后 File ¡ú Assign to current cheat table

[ENABLE]
alloc(newmem,2048,gtutorial-x86_64.exe+3F6A0)
label(returnhere)
label(originalcode)
label(exit)

newmem:

originalcode:
mov rax,rcx
cmp [rax+70],0
je exit
sub [rax+60],edx

exit:
jmp returnhere

gtutorial-x86_64.exe+3F6A0:
jmp newmem
nop
returnhere:

[DISABLE]
gtutorial-x86_64.exe+3F6A0:
mov rax,rcx
sub [rax+60],edx

第 2 关尝试 6

刚才的代码改的还不够好£¬我们可以像敌人的究极炸弹打我们一样£¬将敌人?#25442;?#33268;命¡£

originalcode 部分修改成

originalcode:
mov rax,rcx
cmp [rax+70],0
je exit
mov edx,[rax+60]
sub [rax+60],edx

直接令 edx 等于敌?#25628;?#37327;£¬然后敌?#25628;?#37327;会被扣掉 edx£¬这样敌人直接就被秒了¡£

第 2 关尝试 7

不£¬第二关还没有结束£¬我们的还可?#32422;?#32493;深入研究下去¡£

¡°扣血函数¡±是同一个的函数£¬但是调用¡°扣血函数¡±的地方肯定是不一样的¡£

我们可以找到调用他的位置¡£

我们在 sub [rax+60],edx 处下断点

gtutorial-x86_64.exe+3F6A0 - 48 89 C8              - mov rax,rcx
gtutorial-x86_64.exe+3F6A3 - 29 50 60              - sub [rax+60],edx
gtutorial-x86_64.exe+3F6A6 - C3                    - ret

发射一发子弹£¬等待他命中敌人或命中?#32422;º£?#26029;点会触发¡£

这个断点应该会触发 3 次£¬每?#25991;?#38656;要观察一下右侧寄存器窗口中的 rax 的数值来判断这个代表扣谁的血¡£

然后跟着 ret 返回到他调用的位置£¬上一条语句一定是 call¡£

玩家扣血前后的代码

gtutorial-x86_64.exe+3DFB8 - E8 E3160000           - call gtutorial-x86_64.exe+3F6A0
gtutorial-x86_64.exe+3DFBD - 48 8B 4B 28           - mov rcx,[rbx+28]

单步执行返回之后指针停留在 gtutorial-x86_64.exe+3DFBD£¬前一条一定是一个 call 指令£¬就是 call gtutorial-x86_64.exe+3F6A0¡£

gtutorial-x86_64.exe+3F6A0 这个地址就是之前那个扣血函数¡£

gtutorial-x86_64.exe+3F6A0 - 48 89 C8              - mov rax,rcx
gtutorial-x86_64.exe+3F6A3 - 29 50 60              - sub [rax+60],edx
gtutorial-x86_64.exe+3F6A6 - C3                    - ret

同理可以知道敌人被打中时扣血的代码

左侧敌人扣血代码

gtutorial-x86_64.exe+3E0ED - E8 AE150000           - call gtutorial-x86_64.exe+3F6A0

右侧敌人扣血代码

gtutorial-x86_64.exe+3E1D6 - E8 C5140000           - call gtutorial-x86_64.exe+3F6A0

这个游戏很有意思£¬命中敌人和命中玩家使用的是不同的代码£¬仅仅扣血使用的是相同的代码¡£

[Comment]

这就是传说中的面向复制?#31243;?#22411;编程¡£

?#28909;?#20182;们使用的是不同的代码£¬这个 fastcall 由没有影响栈平衡£¬那么我可以直?#24433;?gtutorial-x86_64.exe+3DFB8 这一行用 NOP 替换掉¡£

[Tips]

ret 语句的作用是返回调用处£¬call 的时候会往?#27426;?#21387;一个返回之后应该执行的地址¡£

简单一个 ret 语句就是跳回?#27426;?#37027;个地址的位置£¬然后再把?#27426;?#37027;个地?#36820;?#20986;

也有 ret 8 这样的语句£¬就是先从栈中弹出 8 个字节£¨相当于 add esp,8£©£¬然后再执行返回¡£之所以这样所是因为调用这个函数之前£¬往栈中压入了 8 个字节的?#38382;ý£¨±热?#20004;个 4 字节整数£©£¬函数返回之前必须恢复栈平衡¡£

gtutorial-x86_64.exe+3F6A6 这个 ret 语句£¬后面没有?#38382;ý£?#24212;该不会影响栈平衡¡£

现在也可以让敌人打我们不掉血£¬我们打敌人正常掉血了¡£

第 2 关尝试 8

现在来分析一下 gtutorial-x86_64.exe+3F6A0 这个扣血函数£¬这个函数总共就 3 条指令£¬函数有 2 个?#38382;ý£?#20998;别是 rcxedx£¬rcx 为结构体的指针£¬edx 为扣血的数量£¬没有返回值¡£

[Info]

这种用寄存器传递?#38382;?#26469;调用函数方法是典型的 fastcall¡£

我们需要分析一下 call 之前是什么确定了 edx 的值¡£

以玩家扣血为例£¬我们需要看 call 之前的几行代码¡£

gtutorial-x86_64.exe+3DF98 - FF 90 28010000        - call qword ptr [rax+00000128]
gtutorial-x86_64.exe+3DF9E - 84 C0                 - test al,al
gtutorial-x86_64.exe+3DFA0 - 0F84 D0000000         - je gtutorial-x86_64.exe+3E076
gtutorial-x86_64.exe+3DFA6 - 48 8B 53 40           - mov rdx,[rbx+40]
gtutorial-x86_64.exe+3DFAA - 49 63 C4              - movsxd  rax,r12d
gtutorial-x86_64.exe+3DFAD - 48 8B 04 C2           - mov rax,[rdx+rax*8]
gtutorial-x86_64.exe+3DFB1 - 8B 50 70              - mov edx,[rax+70]
gtutorial-x86_64.exe+3DFB4 - 48 8B 4B 28           - mov rcx,[rbx+28]
gtutorial-x86_64.exe+3DFB8 - E8 E3160000           - call gtutorial-x86_64.exe+3F6A0

call qword ptr [rax+00000128] 处下断点£¬然后回到游?#20998;?#21457;射子弹¡£会发现£¬?#25214;?#21457;射子弹立刻就断下来了¡£我猜测这里应该是碰?#24067;?#27979;£¬然后下面的 testje 来做判断£¬如果碰撞上了£¬则执行扣血函数£¬没撞上则直接跳过这部分代码£¬不扣血¡£

怎么验证一下呢£¿把 je 改成 jne£¬看看是不是子弹没撞上的时候就直接扣血了¡£

修改以后的确是这样的£¬而且之前是一次掉 4 滴血£¬现在连?#32422;?#30340;子弹都会把?#32422;?#25171;掉血£¬一次会掉 5 滴血¡£

把这里 je 改成 jmp 即可¡£CE 会提示原来指令是 6 字节£¬新指令是 5 字节£¬是否用 NOP 填充多余的£¬选是就行了¡£

现在子弹会从我们上方飞过£¬而不与我们产生碰撞£¬而敌人却依然会中弹¡£

第 2 关尝试 9

尝试 5 中的分块数据中可以看到一个 +34 是敌人角度£¬我们可以让敌人不对准我们¡£

手动添加地址 [[["gtutorial-x86_64.exe"+37DC50]+760]+30]+34£¨左侧敌人角度£©£¬Float 类型£¨单精度浮点型£©¡£

我们尝试把它修改成其他数值£¬但是他还会实时被游戏修改回指向玩家¡£

搜索写入这些数值的指令£¬然后 NOP 掉£¬这样游戏就不会修改他们的数值了£¬我们从外部的修改就成功了¡£

但是£¬最后的究极炸弹似乎是跟踪导弹啊¡£

我需要想个办法¡£

第 2 关尝试 10

尝试 5 中的扣血函数£¬我们下断点之后知道 edx = 2£¬这里的 edx 是扣血量¡£

gtutorial-x86_64.exe+3F6A3 - 29 50 60              - sub [rax+60],edx

我们再看函数调用处

gtutorial-x86_64.exe+3DFB1 - 8B 50 70              - mov edx,[rax+70]

这个 edx 是来自于 [rax+70] 的£¬我们下个断点之后我们从寄存器中知道了子弹强度的地?#32602;?#28982;后使用通用的找基址偏移的方法找到地址¡£

从寄存器中得?#38454;?#24377;地址

他的前一行¡£

gtutorial-x86_64.exe+3DFAD - 48 8B 04 C2           - mov rax,[rdx+rax*8]

遇到 [rdx+rax*8] 通常就是个动态数组£¬rdx 就是数组头部地?#32602;?code>rax 就是元素下标£¬64 位寻址内存对齐下£¬两个变量地址相差 8¡£

子弹数组强度的地址为 [[[["gtutorial-x86_64.exe"+37DC50]+760]+40]+rax*8]+70¡£

然后我可以?#35757;?#20154;那发究极炸弹的威力改成 1¡£

雷声大雨点小¡£

跟?#21451;?#30162;一样¡£

注意动画中£¬我使用 Adavanced Options ¡ú 暂停£¬把游戏暂停住了¡£这样避免了我操作时间不够£¬导致炸弹直?#24433;?#25105;打死了¡£

当然我也可以把我的子弹改成超级大的威力£¬让我一下把它打死¡£

第 2 关尝试 11

?#19968;?#21487;以直接锁住子弹的坐标£¬然后不让他接近我¡£

使用 Dissect data/structures¡£

我?#24039;?#20986;一发子弹£¬然后暂停游戏¡£然后根据屏幕中的 3 颗子弹£¬大致分析一下这些地址的?#38382;ý¡?/p>

注意子弹强度是 +70£¬所以我们填入 Structure dissect 中的地址应该是子弹强度地址 -70¡£

我们找到了子弹的 X 坐标和 Y 坐标£¬将他们锁定就可以让子弹无法靠近我¡£

第 3 关

第 3 关£º把每个?#25945;?#26631;记为绿色可以解锁?#24039;?#38376;¡£注意£º敌人会将你?#25442;?#33268;命£¨然后就失败了£©玩的愉快£¡提示£º有很多解决方?#28014;£±热¼‚?#25214;到与敌人的碰?#24067;?#27979;£¬或者 Teleport£¨传?#20572;©£?#25110;者飞行£¬或者...

第 3 关尝试 1

看样子£¬不开挂也能过啊¡£

第 3 关 Plus

看来?#19968;?#26159; too young, too naïve.

第 3 关加强£º门虽然解锁了£¬但是敌人把门堵住了¡£

第 3 关尝试 2

最简单的就是搜索人物坐标了¡£把人物直接改到门那里£¬不用¡°通过¡±敌人£¬而是直接瞬移过去¡£

计算机中£¬2D 游戏一般是左负¡¢右正£¬上下的正负不一定¡£3D 游戏一般高度方向上正¡¢下负£¬东西南北的正负不一定¡£

[Info]

2D 游?#32602;?#22914;果使用计算机绘图的坐标系则是下正¡¢上负£¬如果使用数学中的坐标则是上正¡¢下负¡£

图片来源页

图片来源页

3D 游戏也有两种坐标系¡£一种是向上为 y 轴£¨这是沿袭 2D 坐标的惯例£©£¬然后一般是向右为 x£¬向屏幕外为 z£¨也有向屏幕内为 z 的£©¡£另一种则是向上为 z£¬水平面中向北为 y£¬向东为 x¡£

搜索 Float£¨单精度浮点型£©未知初始值£¬然后向右移动人物£¬搜索增大了的数值£¬然后向左移动人物£¬搜索减小了的数值£¬反复?#22797;Σ?#20320;应该能看到剩下一个唯一的数值¡£过程中还可以不移动人物£¬搜索未改变的数值¡£

[Tips]

你可以在设置中给常用搜索功能添加快捷键¡£这样不用切出游戏就可以进行下一次扫描了¡£

添加到地址列表中£¬然后改名为¡°X 坐标¡±¡£然后复制?#31243;ù£?#20462;改地?#32602;¬°训?#22336; +4 即为 Y 坐标¡£

[Info]

这里 +4 还是 -4 主要看内存中的排列方式¡£一般 X 排在 Y 前面£¬所以要 +4¡£对于 3D 游?#32602;?#20320;搜索高度可能?#35757;?#30340;是 Y 也可能是 Z£¬你可以使用右键 ¡ú Browse this memory region£¬然后右键 ¡ú Display Type ¡ú Float 来看看前后的内存数据£¬然后在游?#20998;?#31227;动一下£¬凭感觉决定 X¡¢Y¡¢Z¡£

移动一下人物£¬大概估计一下坐标的?#27573;§£?#25972;个游戏区域对应的 X 和 Y 是 -11 直接的值¡£估计一下门的 X 坐标£¬把 X 坐标改成 0.97¡£

Well Done

你战胜了全部三个游?#32602;?#24178;得漂亮£¡

第 3 关尝试 3

上面的方法很简单也很实用£¬不过我们还可?#32422;?#32493;¡°玩¡±这个游戏¡£

我们可不可以直?#24433;?#25152;有的?#25945;?#37117;改成绿色呢£¿

因为每个?#25945;?#21482;有两种状态£¬而且只能从红变成绿£¬这样很不利于搜索£¬而且我也不知道他是怎?#21019;?#20648;的£¬不知道红和绿两个状态的值都是多少¡£

这个我尝试了很多种办法£¬例如£º

  1. 红的时候搜 0£¬绿的时候搜 1£¬然后撞敌人?#33756;¢?#20877;搜 0¡£

  2. 红的时候搜未知初始值£¬绿的时候搜改变了的数值£¬然后撞敌人?#33756;¢?#20877;搜改变了的数值¡£

  3. 把类型改为 Byte£¨单字节类型£©£¬因为 bool 类型都是?#21152;?1 字节的¡£

  4. 其实?#19968;?#24576;疑是不是每次?#33756;?#37117;会重新申请内存£¬这样就更麻烦了¡£

最后£¬我使用¡°红的时候搜 Byte 类型未知初始值£¬绿的时候搜改变了的数值£¬然后撞敌人?#33756;¢?#20877;搜改变了的数值¡±的方法找到了一点线索¡£虽然没有找到具体的数据存储地?#32602;?#20294;是我找到了绝对相关的一组数据¡£这组数据每次颜色转换都会相应的来回改变¡£

虽然没有找到具体与台阶有关的数值£¬但是注意图中 015F1AD8 这个值£¬他的含义似乎是已经点亮的?#25945;?#30340;数量

我直?#24433;?#36825;个数字改成 12 的话£¬虽然没有让所有的?#25945;?#37117;变绿£¬但是依然触发¡°门解锁¡¢敌人堵门¡±这一事件了¡£

我突然有个想法£¬就是我直接站在门上£¬然后把数值修改为 12£¬我已经在门上了£¬敌人就堵不到我了¡£

结果真的可以¡£

第 3 关尝试 4

4BFEEB60 ?#20999;?255204 看样子像是 RGB 值¡£如果我手动添加 4BFEEB60 类型设为 4 字节£¬显示十六进制值¡£结果就是 FF00FF00£¬4 个字节分别是 ARGB£¬就是不?#35813;?#30340;绿色¡£红色的?#25945;?#21017;是 FFCC0000£¬不?#35813;?#30340;暗红色¡£

但是这些数值改了也没什么用£¬应该就是每一像素的颜色¡£

上面那个 015ABE78£¬手动添加这个地?#32602;?#24182;设置成 Float 类型的话£¬就会发现£¬红色的时候是 0.8£¬绿色的时候是 0¡£同理 015ABE7C£¬红色的时候是 0£¬绿色的时候是 1¡£

把这两个数值改成其他的£¬你会发?#21046;教?#30340;颜色也变了¡£

我又发现一个有趣的现象£¬如果我把?#25945;?#30340;颜色锁定为红色£¬然后让人物站上去£¬这时¡°已变绿?#25945;?#25968;¡±那个计数器会快速增长¡£所?#38405;?#26377;什么想法£¿

这就是为什么我找不到一个 bool 型变量来描述?#25945;?#26159;否变绿£¬因为他的代码根本没有这样一个变量£¬他的逻辑应该大致是这样的¡£

if (collision) {
    R = 0;
    if (G != 1) {
        count++;
        G = 1;
    }
}

如果站到?#25945;?#19978;了£¬则令红色为 0£¬如果绿色不为 1£¬则计数器 +1£¬并令绿色为 1¡£

这里面没有出现 flag 这种东西来表示?#25945;?#26159;否变绿色£¬他直接用颜色来判断的¡£

第 3 关尝试 5

找到与敌人的碰?#24067;?#27979;£¬或者 Teleport£¨传?#20572;©£?#25110;者飞行£¬或者...

关卡说明中告诉里一部分思?#32602;¬TP 已经试过了£¬现在我们来试试飞行¡£

所谓的飞行其实就是把重力改小£¬或者是像玩 Flappy Bird 那样一跳一跳的£¬可以一直在天上飞着¡£

首先来找到重力大小¡£

重力会影响速度£¬速度影响坐标£¬我们现在只知道坐标的地?#32602;?#25105;们可以通过查找写入£¬然后分析附近代码来找到速度£¬然后进而找到重力加速度¡£

y = y0 + vy * t

计算位置需要先读取 Y 坐标 y0£¬然后加上速度差£¬在赋值给 y¡£

这里有个小?#35760;É£?#23601;是对同一个地址同时使用查找写入和查找访问£¬这样我们很容易地找到了写入的地?#32602;?#28982;后在查找访问窗口中£¬写入地址以前的几个读取都很可疑¡£

第二条写入指令£¬在跳起来悬空的时候£¬计数器不会增加£¬应该是当人物接触到地面的时候£¬防止人物穿过地面用的¡£我们只看第一条¡£

Show disassembler£¬我把 gtutorial-x86_64.exe+40491gtutorial-x86_64.exe+40506 截取出来¡£

gtutorial-x86_64.exe+4048D - 48 8B 43 28           - mov rax,[rbx+28]
gtutorial-x86_64.exe+40491 - F3 44 0F10 40 28      - movss xmm8,[rax+28] { 读取 Y 坐标 }
gtutorial-x86_64.exe+40497 - 48 8B 43 28           - mov rax,[rbx+28]
gtutorial-x86_64.exe+4049B - F3 0F5A 48 28         - cvtss2sd xmm1,[rax+28] { 再次读取 Y 坐标 }
gtutorial-x86_64.exe+404A0 - F3 0F5A 53 78         - cvtss2sd xmm2,[rbx+78] { 读取 Y 速度 }
gtutorial-x86_64.exe+404A5 - F2 0F2A C6            - cvtsi2sd xmm0,esi { esi 是毫秒数 }
gtutorial-x86_64.exe+404A9 - F2 0F5E 05 AF382400   - divsd xmm0,[gtutorial-x86_64.exe+283D60] { 除以 1000 }
gtutorial-x86_64.exe+404B1 - F2 0F59 C2            - mulsd xmm0,xmm2 { 速度乘时间 }
gtutorial-x86_64.exe+404B5 - F2 0F5C C8            - subsd xmm1,xmm0 { Y 坐标减去位移 }
gtutorial-x86_64.exe+404B9 - F2 44 0F5A C9         - cvtsd2ss xmm9,xmm1 { double 转 float }

......

gtutorial-x86_64.exe+40506 - F3 44 0F11 48 28      - movss [rax+28],xmm9 { 赋值给 [rax+28] }

[Comment]

注释是我?#32422;?#21152;的¡£这个是根据逻辑和感觉猜出来的£¬也有可能猜错¡£不过这个简单的速度位移公式£¬一般来说分析应该是正确的¡£

注意这几条

gtutorial-x86_64.exe+40497 - 48 8B 43 28           - mov rax,[rbx+28]
gtutorial-x86_64.exe+4049B - F3 0F5A 48 28         - cvtss2sd xmm1,[rax+28] { 再次读取 Y 坐标 }
gtutorial-x86_64.exe+404A0 - F3 0F5A 53 78         - cvtss2sd xmm2,[rbx+78] { 读取 Y 速度 }

Y 坐标的内存地址是 [[["gtutorial-x86_64.exe"+37DC50]+760]+28]+24£¬rbx 应该是一级指针的值 [["gtutorial-x86_64.exe"+37DC50]+760]£¬那么 Y 速度的地址应该就是 [["gtutorial-x86_64.exe"+37DC50]+760]+78¡£

手动添加 Y 速度的地?#32602;?#28982;后把速度改成 3£¬你会发现人物跳了起来¡£

刚才设成 3 跳的有点高£¬添加一个上箭头的热键£¬设置?#28404;?1¡£如果长按的话就会匀速向上飞¡£

第 3 关尝试 6

刚才改速度已经成功了£¬现在我们来改重力加速度¡£

还是查找写入¡£

  • 第 1 个在脱离地面之后不计数£¬应该是地面支撑
  • 第 2 个随时都会触发£¬应该是重力加速度导致的
  • 第 3 个是起跳时触发
  • 第 4 个则是长按跳跃连跳时触发

我突然有个想法£¬起跳时触发的那条语句£¬一定有什么限制他£¬让他只能在地面上起跳£¬而不能在空中起跳¡£

Show disassembler.

gtutorial-x86_64.exe+3FE9A - C6 43 74 01           - mov byte ptr [rbx+74],01 { 1 }
gtutorial-x86_64.exe+3FE9E - 80 7B 7C 00           - cmp byte ptr [rbx+7C],00 { 0 }
gtutorial-x86_64.exe+3FEA2 - 0F85 93000000         - jne gtutorial-x86_64.exe+3FF3B
gtutorial-x86_64.exe+3FEA8 - 8B 05 823E2400        - mov eax,[gtutorial-x86_64.exe+283D30] { (1.45) }
gtutorial-x86_64.exe+3FEAE - 89 43 78              - mov [rbx+78],eax

经过分析和猜测£¬[rbx+74] 表示是否按下跳跃键£¬[rbx+7C] 表示是否悬空¡£

jne 表示如果悬空则不允许跳¡£

直?#24433;?jne 那条语句 NOP 掉£¬就可以实现无限连跳了¡£刚才设置的热键?#21152;?#19981;着了¡£

你也尝试可以修改 gtutorial-x86_64.exe+283D30 这个地址的数值£¬它表示跳跃初速度¡£

上面的方法修改之后£¬长按不会一直向上飞£¬必须像 Flappy Bird 一样一下一下的¡£如果你想长按就一直向上飞£¬那就?#35757;?4 条指令前面的 jne 也 NOP 掉¡£

gtutorial-x86_64.exe+406F8 - 80 7B 7C 00           - cmp byte ptr [rbx+7C],00 { 0 }
gtutorial-x86_64.exe+406FC - 75 0B                 - jne gtutorial-x86_64.exe+40709
gtutorial-x86_64.exe+406FE - 8B 05 2C362400        - mov eax,[gtutorial-x86_64.exe+283D30] { (1.45) }
gtutorial-x86_64.exe+40704 - 89 43 78              - mov [rbx+78],eax

第 3 关尝试 7

刚?#25490;?#39064;了£¬我们继续来找重力加速度

v = v0 + g * t

分析一下第 2 个指令附近

gtutorial-x86_64.exe+40709 - F3 0F5A 43 78         - cvtss2sd xmm0,[rbx+78] { 读取速度 }
gtutorial-x86_64.exe+4070E - F2 0F5C 05 52362400   - subsd xmm0,[gtutorial-x86_64.exe+283D68] { 减去 0.1 }
gtutorial-x86_64.exe+40716 - F2 0F5A C0            - cvtsd2ss xmm0,xmm0 { double 转 float }
gtutorial-x86_64.exe+4071A - F3 0F11 43 78         - movss [rbx+78],xmm0 { 写入速度 }

这个逻辑好简单啊£¬与时间都无关£¬就是每次计算把 Y 速度减 0.1¡£

手动添加地址 gtutorial-x86_64.exe+283D68£¬类型为 double£¬然后把重力加速度调小就行了¡£

第 3 关尝试 8

我们还有什么办法£¿我可不可以?#35757;?#20154;固定住£¬让他不要移动£¬或者移到屏幕外£¬总之让他别妨碍我们就行了¡£

用同样搜索?#32422;?#22352;标的方法搜索敌人的坐标¡£只不过?#32422;?#30340;坐标可以?#32422;?#25511;制£¬敌人的坐标只能随他们移动了¡£

找到 3 个 X 坐标之后 +4 就是 Y 坐标¡£

把这些坐标锁定£¬可行¡£把已变绿?#25945;?#25968;改成 12£¬这些敌人又不听话了£¬又开始堵门了£¬锁定似乎对他们不好使¡£

查找写入他们的指令

?#28909;?#20182;堵住门时会一直触发第 5 条£¬那么我?#22270;?#21333;?#30452;?#19968;点£¬直?#24433;训?5 条指令 NOP 掉£¬这样我就可以从外部修改这个数值了¡£

第 3 关尝试 9

我们还可以想办法直接开门¡£

查找访问¡°已变绿?#25945;?#25968;¡±的指令¡£

只有这一条

gtutorial-x86_64.exe+4098B - 48 63 93 88000000     - movsxd  rdx,dword ptr [rbx+00000088]

我们分析一下附近

gtutorial-x86_64.exe+4098B - 48 63 93 88000000     - movsxd  rdx,dword ptr [rbx+00000088] { 读取已变绿?#25945;?#25968; }
gtutorial-x86_64.exe+40992 - 48 8B 43 30           - mov rax,[rbx+30]
gtutorial-x86_64.exe+40996 - 48 85 C0              - test rax,rax
gtutorial-x86_64.exe+40999 - 74 08                 - je gtutorial-x86_64.exe+409A3
gtutorial-x86_64.exe+4099B - 48 8B 40 F8           - mov rax,[rax-08] { [rax-08] 为?#25945;?#25968;组最大下标 }
gtutorial-x86_64.exe+4099F - 48 83 C0 01           - add rax,01 { 最大下标 + 1 即为总?#25945;?#25968; }
gtutorial-x86_64.exe+409A3 - 48 39 C2              - cmp rdx,rax { 比较已变绿?#25945;?#25968;和总?#25945;?#25968; }
gtutorial-x86_64.exe+409A6 - 7C 17                 - jl gtutorial-x86_64.exe+409BF
gtutorial-x86_64.exe+409A8 - 48 8B 43 60           - mov rax,[rbx+60] { 二级指针 }
gtutorial-x86_64.exe+409AC - C6 40 18 00           - mov byte ptr [rax+18],00 { 开门 }
gtutorial-x86_64.exe+409B0 - C6 43 7D 01           - mov byte ptr [rbx+7D],01 { 堵门 }
gtutorial-x86_64.exe+409B4 - 48 8B 43 68           - mov rax,[rbx+68]
gtutorial-x86_64.exe+409B8 - 48 89 83 80000000     - mov [rbx+00000080],rax
gtutorial-x86_64.exe+409BF - 48 83 7B 28 00        - cmp qword ptr [rbx+28],00 { 0 }

[rbx+00000088] 为已变绿?#25945;?#25968;£¬而已变绿?#25945;?#25968;的地址为 [["gtutorial-x86_64.exe"+37DC50]+760]+88£¬所以 rbx = [["gtutorial-x86_64.exe"+37DC50]+760]£¬

所以可以求得开门地址为 [[["gtutorial-x86_64.exe"+37DC50]+760]+60]+18£¬堵门的地址为 [["gtutorial-x86_64.exe"+37DC50]+760]+7D¡£

我们直接执行 mov byte ptr [rax+18],00 这条开门语句的内容就行了¡£手动添加开门地?#32602;¬Byte 类型£¬然后修改为 0¡£这样我们躲过敌人就可以进门了£¬不用让?#25945;?#21464;绿£¬也不会?#27426;?#20303;¡£

请注意上面动画中£¬修改完数值之后右下角门的变化¡£

第 3 关尝试 10

终于要到碰?#24067;?#27979;了¡£第 2 关中£¬我们让子弹直接忽略玩家£¬继续向前飞¡£第 3 关我们也可以让敌人忽略玩家£¬即使碰到了也不会死亡¡£

碰?#24067;?#27979;肯定会读取二者的 X¡¢Y 坐标¡£查找访问敌人 Y 坐标的指令¡£

最开始只看到 1 条指令¡£

gtutorial-x86_64.exe+39DDE - F3 0F10 4B 28         - movss xmm1,[rbx+28]

但是查看附近代码的时候我看到 call qword ptr [gtutorial-x86_64.exe+3825E0] { ->opengl32.glTranslatef }

gtutorial-x86_64.exe+39DDE - F3 0F10 4B 28         - movss xmm1,[rbx+28]
gtutorial-x86_64.exe+39DE3 - F3 0F10 05 7D7F2400   - movss xmm0,[gtutorial-x86_64.exe+281D68] { (0.00) }
gtutorial-x86_64.exe+39DEB - 0F57 C8               - xorps xmm1,xmm0
gtutorial-x86_64.exe+39DEE - F3 0F10 43 24         - movss xmm0,[rbx+24]
gtutorial-x86_64.exe+39DF3 - F3 0F10 15 757F2400   - movss xmm2,[gtutorial-x86_64.exe+281D70] { (0.00) }
gtutorial-x86_64.exe+39DFB - FF 15 DF873400        - call qword ptr [gtutorial-x86_64.exe+3825E0] { ->opengl32.glTranslatef }

所以这个应该是在绘图指令前读取 Y 坐标£¬这个应该不是碰?#24067;?#27979;的代码¡£

?#19968;?#30097;是不是碰?#24067;?#27979;?#25512;?#20182;代码混在一起£¬所以只有一次读取¡£我沿着这个附近单步调试了很长时间¡£

终于£¬一次不经意间我发现问题了¡£请观察下面的动图¡£

原始的代码很可能是这样的¡£

float player_w_2 = player_w / 2.0f;
float enemy_w_2 = enemy_w / 2.0f;
if (enemy_x - enemy_w_2 < player_x + player_w_2 && player_x - player_w_2 < enemy_x + enemy_w_2) {
    float player_h_2 = player_h / 2.0f;
    float enemy_h_2 = enemy_h / 2.0f;
    if (enemy_y - enemy_h_2 < player_y + player_h_2 && player_y - player_h_2 < enemy_y + enemy_h_2) {
        // 碰撞
    }
}

逻辑短路¡£如果 X 坐标不在敌人宽度?#27573;?#20869;£¬那么直接就不?#38376;?#26029; Y 坐标了£¬就不会对 Y 坐标造成访问¡£

解决这个问题之后£¬我们又找到这条语句¡£

gtutorial-x86_64.exe+39B45 - F3 0F10 43 28         - movss xmm0,[rbx+28]

在周围分析一下¡£

gtutorial-x86_64.exe+39B26 - FF 90 E0000000        - call qword ptr [rax+000000E0] { movss xmm0,[100284490]
                                                                                     xmm0 = 0.1 }
gtutorial-x86_64.exe+39B2C - F3 0F10 4E 30         - movss xmm1,[rsi+30]
gtutorial-x86_64.exe+39B31 - F3 0F58 0D 1F822400   - addss xmm1,dword ptr [gtutorial-x86_64.exe+281D58] { (1.00) }
gtutorial-x86_64.exe+39B39 - F3 0F59 0D 1F822400   - mulss xmm1,[gtutorial-x86_64.exe+281D60] { (0.50) }
gtutorial-x86_64.exe+39B41 - F3 0F59 C8            - mulss xmm1,xmm0 { xmm1 = 0.5 * 0.1 }
gtutorial-x86_64.exe+39B45 - F3 0F10 43 28         - movss xmm0,[rbx+28] { 读取敌人 Y 坐标 }
gtutorial-x86_64.exe+39B4A - F3 0F5C C1            - subss xmm0,xmm1 { 减掉敌人高度的一半 }

......

gtutorial-x86_64.exe+39B56 - C3                    - ret

跟踪这个函数的返回£¬你会发现一片新天地¡£

gtutorial-x86_64.exe+3A72E - 48 89 CB              - mov rbx,rcx { rbx 为敌人指针 }
gtutorial-x86_64.exe+3A731 - 48 89 D6              - mov rsi,rdx
gtutorial-x86_64.exe+3A734 - 40 B7 00              - mov dil,00 { 0 }
gtutorial-x86_64.exe+3A737 - 83 7B 58 00           - cmp dword ptr [rbx+58],00 { 0 }
gtutorial-x86_64.exe+3A73B - 75 58                 - jne gtutorial-x86_64.exe+3A795 { 如果 [rbx+58] != 0£¬则使用普通碰?#33756;?#27861;£¬否则使用简化碰?#33756;?#27861; }
gtutorial-x86_64.exe+3A73D - 48 89 F1              - mov rcx,rsi { rsi 为玩家指针 }
gtutorial-x86_64.exe+3A740 - E8 6BFFFFFF           - call gtutorial-x86_64.exe+3A6B0 { xmm0 = [rcx+44] 玩家碰撞半径 }
gtutorial-x86_64.exe+3A745 - 0F28 F0               - movaps xmm6,xmm0 { xmm6 = [rcx+44] 玩家碰撞半径 }
gtutorial-x86_64.exe+3A748 - 48 89 D9              - mov rcx,rbx { rbx 为敌人指针 }
gtutorial-x86_64.exe+3A74B - E8 60FFFFFF           - call gtutorial-x86_64.exe+3A6B0 { xmm0 = [rcx+44] 敌人碰撞半径 }
gtutorial-x86_64.exe+3A750 - F3 0F10 4E 24         - movss xmm1,[rsi+24] { xmm1 = 玩家 X }
gtutorial-x86_64.exe+3A755 - F3 0F5C 4B 24         - subss xmm1,[rbx+24] { xmm1 = 玩家 X - 敌人 X }
gtutorial-x86_64.exe+3A75A - 0F54 0D 0F641E00      - andps xmm1,[gtutorial-x86_64.exe+220B70] { 取绝对值 }
gtutorial-x86_64.exe+3A761 - F3 0F59 C9            - mulss xmm1,xmm1 { 平方 }
gtutorial-x86_64.exe+3A765 - F3 0F10 56 28         - movss xmm2,[rsi+28] { xmm2 = 玩家 Y }
gtutorial-x86_64.exe+3A76A - F3 0F5C 53 28         - subss xmm2,[rbx+28] { xmm2 = 玩家 Y - 敌人 Y }
gtutorial-x86_64.exe+3A76F - 0F54 15 FA631E00      - andps xmm2,[gtutorial-x86_64.exe+220B70] { 取绝对值 }
gtutorial-x86_64.exe+3A776 - F3 0F59 D2            - mulss xmm2,xmm2 { 平方 }
gtutorial-x86_64.exe+3A77A - F3 0F58 D1            - addss xmm2,xmm1 { 相加 }
gtutorial-x86_64.exe+3A77E - F3 0F51 D2            - sqrtss xmm2,xmm2 { xmm2 = 敌人¡¢玩家?#34892;木?#31163; }
gtutorial-x86_64.exe+3A782 - 0F28 CE               - movaps xmm1,xmm6
gtutorial-x86_64.exe+3A785 - F3 0F58 C8            - addss xmm1,xmm0 { xmm1 = 敌人碰撞半径+玩家碰撞半径 }
gtutorial-x86_64.exe+3A789 - 0F2F CA               - comiss xmm1,xmm2
gtutorial-x86_64.exe+3A78C - 40 0F97 C7            - seta dil
gtutorial-x86_64.exe+3A790 - E9 B4000000           - jmp gtutorial-x86_64.exe+3A849
gtutorial-x86_64.exe+3A795 - 83 7B 58 01           - cmp dword ptr [rbx+58],01 { 1 }
gtutorial-x86_64.exe+3A799 - 0F85 AA000000         - jne gtutorial-x86_64.exe+3A849 { 如果 [rbx+58] != 1 则不判断碰撞£¬前面已将 dil 设为 0 }
gtutorial-x86_64.exe+3A79F - 48 89 D9              - mov rcx,rbx
gtutorial-x86_64.exe+3A7A2 - 48 89 D8              - mov rax,rbx
gtutorial-x86_64.exe+3A7A5 - 48 8B 00              - mov rax,[rax]
gtutorial-x86_64.exe+3A7A8 - FF 90 F8000000        - call qword ptr [rax+000000F8] { xmm0 = 敌人 left }
gtutorial-x86_64.exe+3A7AE - 0F28 F0               - movaps xmm6,xmm0
gtutorial-x86_64.exe+3A7B1 - 48 89 F1              - mov rcx,rsi
gtutorial-x86_64.exe+3A7B4 - 48 89 F0              - mov rax,rsi
gtutorial-x86_64.exe+3A7B7 - 48 8B 00              - mov rax,[rax]
gtutorial-x86_64.exe+3A7BA - FF 90 00010000        - call qword ptr [rax+00000100] { xmm0 = 玩家 right }
gtutorial-x86_64.exe+3A7C0 - 0F2F C6               - comiss xmm0,xmm6
gtutorial-x86_64.exe+3A7C3 - 0F8A 7D000000         - jp gtutorial-x86_64.exe+3A846
gtutorial-x86_64.exe+3A7C9 - 0F86 77000000         - jbe gtutorial-x86_64.exe+3A846
gtutorial-x86_64.exe+3A7CF - 48 89 F1              - mov rcx,rsi
gtutorial-x86_64.exe+3A7D2 - 48 89 F0              - mov rax,rsi
gtutorial-x86_64.exe+3A7D5 - 48 8B 00              - mov rax,[rax]
gtutorial-x86_64.exe+3A7D8 - FF 90 F8000000        - call qword ptr [rax+000000F8] { xmm0 = 玩家 left }
gtutorial-x86_64.exe+3A7DE - 0F28 F0               - movaps xmm6,xmm0
gtutorial-x86_64.exe+3A7E1 - 48 89 D9              - mov rcx,rbx
gtutorial-x86_64.exe+3A7E4 - 48 89 D8              - mov rax,rbx
gtutorial-x86_64.exe+3A7E7 - 48 8B 00              - mov rax,[rax]
gtutorial-x86_64.exe+3A7EA - FF 90 00010000        - call qword ptr [rax+00000100] { xmm0 = 敌人 right }gtutorial-x86_64.exe+3A7F0 - 0F2F C6               - comiss xmm0,xmm6
gtutorial-x86_64.exe+3A7F3 - 7A 51                 - jp gtutorial-x86_64.exe+3A846
gtutorial-x86_64.exe+3A7F5 - 76 4F                 - jna gtutorial-x86_64.exe+3A846
gtutorial-x86_64.exe+3A7F7 - 48 89 D9              - mov rcx,rbx
gtutorial-x86_64.exe+3A7FA - 48 89 D8              - mov rax,rbx
gtutorial-x86_64.exe+3A7FD - 48 8B 00              - mov rax,[rax]
gtutorial-x86_64.exe+3A800 - FF 90 08010000        - call qword ptr [rax+00000108] { xmm0 = 敌人 top }
gtutorial-x86_64.exe+3A806 - 0F28 F0               - movaps xmm6,xmm0
gtutorial-x86_64.exe+3A809 - 48 89 F1              - mov rcx,rsi
gtutorial-x86_64.exe+3A80C - 48 89 F0              - mov rax,rsi
gtutorial-x86_64.exe+3A80F - 48 8B 00              - mov rax,[rax]
gtutorial-x86_64.exe+3A812 - FF 90 10010000        - call qword ptr [rax+00000110] { xmm0 = 玩家 bottom }
gtutorial-x86_64.exe+3A818 - 0F2F C6               - comiss xmm0,xmm6
gtutorial-x86_64.exe+3A81B - 7A 29                 - jp gtutorial-x86_64.exe+3A846
gtutorial-x86_64.exe+3A81D - 76 27                 - jna gtutorial-x86_64.exe+3A846
gtutorial-x86_64.exe+3A81F - 48 89 F1              - mov rcx,rsi
gtutorial-x86_64.exe+3A822 - 48 8B 06              - mov rax,[rsi]
gtutorial-x86_64.exe+3A825 - FF 90 08010000        - call qword ptr [rax+00000108] { xmm0 = 玩家 top }
gtutorial-x86_64.exe+3A82B - 0F28 F0               - movaps xmm6,xmm0
gtutorial-x86_64.exe+3A82E - 48 89 D9              - mov rcx,rbx
gtutorial-x86_64.exe+3A831 - 48 8B 03              - mov rax,[rbx]
gtutorial-x86_64.exe+3A834 - FF 90 10010000        - call qword ptr [rax+00000110] { xmm0 = 敌人 bottom }
gtutorial-x86_64.exe+3A83A - 0F2F C6               - comiss xmm0,xmm6
gtutorial-x86_64.exe+3A83D - 7A 07                 - jp gtutorial-x86_64.exe+3A846
gtutorial-x86_64.exe+3A83F - 76 05                 - jna gtutorial-x86_64.exe+3A846
gtutorial-x86_64.exe+3A841 - 40 B7 01              - mov dil,01 { 碰撞设置 dil 为 1 }
gtutorial-x86_64.exe+3A844 - EB 03                 - jmp gtutorial-x86_64.exe+3A849
gtutorial-x86_64.exe+3A846 - 40 B7 00              - mov dil,00 { 0 }
gtutorial-x86_64.exe+3A849 - 40 0FB6 C7            - movzx eax,dil { 碰撞函数返回?#28404;?eax }
gtutorial-x86_64.exe+3A84D - 90                    - nop
gtutorial-x86_64.exe+3A84E - 66 0F6F 74 24 20      - movdqa xmm6,[rsp+20]
gtutorial-x86_64.exe+3A854 - 48 8D 64 24 30        - lea rsp,[rsp+30]
gtutorial-x86_64.exe+3A859 - 5E                    - pop rsi
gtutorial-x86_64.exe+3A85A - 5F                    - pop rdi
gtutorial-x86_64.exe+3A85B - 5B                    - pop rbx
gtutorial-x86_64.exe+3A85C - C3                    - ret

上面是完整的碰?#33756;?#27861;分析¡£

其实并没有这么麻?#24120;?#25105;们只需要知道返回值是 eax£¬如果 eax == 1 则表示碰撞£¬eax == 0 则表示未碰?#30149;?/p>

我们跟踪 ret 返回¡£

gtutorial-x86_64.exe+4093A - FF 90 28010000        - call qword ptr [rax+00000128] { eax = 是否碰撞 }
gtutorial-x86_64.exe+40940 - 84 C0                 - test al,al
gtutorial-x86_64.exe+40942 - 74 11                 - je gtutorial-x86_64.exe+40955 { eax == 0 则跳转 }
gtutorial-x86_64.exe+40944 - 48 8B 4B 28           - mov rcx,[rbx+28]
gtutorial-x86_64.exe+40948 - 48 8B 43 28           - mov rax,[rbx+28]
gtutorial-x86_64.exe+4094C - 48 8B 00              - mov rax,[rax]
gtutorial-x86_64.exe+4094F - FF 90 20010000        - call qword ptr [rax+00000120] { 碰撞之后执行的事件 }
gtutorial-x86_64.exe+40955 - 48 8B 53 38           - mov rdx,[rbx+38]

je 修改成 jmp 即可¡£

第 3 关尝试 11

如果 [rbx+58] != 0£¬则使用普通碰?#33756;?#27861;£¬否则使用简化碰?#33756;?#27861;£¬如果 [rbx+58] != 1 则不判断碰?#30149;?#25152;以我们可以令 [rbx+58] = 2 这样两个碰撞就都没了¡£

手动添加 [[[["gtutorial-x86_64.exe"+37DC50]+760]+38]+0]+58£¬4 字节£¬设置为 2¡£

隐藏问题

你有没有注意到你执行代码注入以后标题栏会由 Step 2 变成 Step 2 (Integrity check error)

完整?#32422;?#26597;错误

游?#20998;?#20869;置的检测工具发现你修改了他们的程序指令¡£怎么办呢£¿

他们是怎么检测的呢£¿

原理很简单£¬就是比较代码区域的内存¡£

避免被发现的方法并不是如何伪造内存让他们别发现¡£通常检测程序运行在另一个线程£¬直接关掉那个线程就行了¡£

首先在地址列表中手动添加我们刚才修改的地址 gtutorial-x86_64.exe+3F6A3£¬然后查找谁在访问这个地址¡£

[Tips]

一般正常的程序不会访问程序代码部分的内存的£¬他们运行所要的数据和都在常量区¡¢全?#30452;?#37327;区¡¢?#36873;?#26632;中£¬代码区是额外的一个区域£¬他们之间都是隔离开的¡£要访问程序?#32422;?#30340;代码区的程序都不是正常的程序¡£

我这里找到了 3 个£¬然后选择其中一个£¨我这里?#33073;?#31532;一个了£©

Show disassembler£¬然后下断点¡£

然后我们需要做的就是记住标题上的线程编号¡£然后在 Memory ViewerView ¡ú Threadlist ¡ú 右键点击刚才的线程编号 ¡ú Freeze thread¡£

你打败了 3 个¡°游戏?#20445;?#24182;且你打败了完整?#32422;?#26597;£¡

干的真的漂亮£¡

总结

本文通过 3 个小游戏的二十多种思?#32602;?#21521;你展示了很多破解思路¡£

您应该学?#23433;?#29702;解这种思?#32602;?#20027;要是如何通过内存地址找代码£¬如果通过代码找内存地址¡£

本文还?#24425;?#20102;一些小?#35760;É£?#22914;何搜索坐标这种未知数值的内存数据¡£

最后简单讲解了内存校验的原理与简易破解方法¡£

希望您不仅能从本文中学到 Cheat Engine 工具的使用方法£¬还能学到更广阔的破解思想¡£

最后如果你想继续研究£¬你可以对照 GTutorial 源代码 进行研究£¬看看原作者的注释£¬你能看到他给你预留了很多变量用于破解¡£

如果您有任?#25105;?#38382;欢迎在评论区留言¡£

2019 年 3 月 29 日 Ganlv

附件

包含全部内存地址¡¢代码¡¢自动汇编¡¢反汇编注释的 CE 文件¡£

gtutorial-x86_64.zip (6.69 KB, 下载?#38382;? 92)

相关链接

点评

不错不错支持£¡看的?#31227;?#31373;已经开了六窍¡£笑哭 哭笑 笑出眼泪 破涕为笑 笑死 笑尿 笑cry  发表于 2019-4-3 15:17
膜拜大佬 五体投地 /膜拜/膜拜/膜拜  发表于 2019-4-2 09:41

免费评分

参与人数 122吾爱币 +120 热心值 +118 收起 理由
古驰啊 + 1 + 1 谢谢@Thanks£¡
ahtyqq789 + 1 ?#34892;?#21457;布原创作?#32602;?#21566;爱破解论坛因你更精彩£¡
i172523759 + 1 用心讨论£¬?#19981;?#25552;升£¡
666666666666366 + 1 + 1 一个游戏能玩一天
evill + 1 + 1 谢谢@Thanks£¡
q921117827 + 1 谢谢@Thanks£¡
微微微微微笑 + 1 + 1 我很赞同£¡
没有烟 + 1 + 1 谢谢@Thanks£¡
syclone + 1 + 1 谢谢@Thanks£¡
猫大林 + 1 + 1 谢谢@Thanks£¡
zmyzzx + 1 + 1 谢谢@Thanks£¡
漠北孤狼 + 1 ?#34892;?#21457;布原创作?#32602;?#21566;爱破解论坛因你更精彩£¡
momo16 + 1 + 1 谢谢@Thanks£¡
phyzero + 1 + 1 欢迎分析讨论交流£¬吾爱破解论坛有你更精彩£¡
wu0687050 + 2 + 1 第三关终于有人讲透透彻彻的了¡£
zx12zxzx12 + 1 + 1 谢谢@Thanks£¡
Mouse + 1 + 1 谢谢@Thanks£¡
绝境守护者 + 1 + 1 我很赞同£¡
honghi1 + 1 + 1 我很赞同£¡
wangdeqilin + 1 + 1 用心讨论£¬?#19981;?#25552;升£¡
232613618 + 1 + 1 欢迎分析讨论交流£¬吾爱破解论坛有你更精彩£¡
兩儀織 + 1 + 1 ?#34892;?#21457;布原创作?#32602;?#21566;爱破解论坛因你更精彩£¡
朔子哥哥 + 1 + 1 用心讨论£¬?#19981;?#25552;升£¡
zhaooptimus + 1 + 1 谢谢@Thanks£¡
lxz_2018 + 1 用心讨论£¬?#19981;?#25552;升£¡
?#30424;?/a> + 1 + 1 谢谢@Thanks£¡
北方有鱼 + 1 + 1 谢谢@Thanks£¡
gtrgtr + 1 + 1 我很赞同£¡
wazq + 1 + 1 谢谢@Thanks£¡
hetiwz + 1 + 1 ?#34892;?#21457;布原创作?#32602;?#21566;爱破解论坛因你更精彩£¡
f4cku + 1 + 1 我很赞同£¡
huyun246893 + 1 + 1 谢谢@Thanks£¡
hbwazxf + 1 + 1 用心讨论£¬?#19981;?#25552;升£¡
jkevin + 1 谢谢@Thanks£¡
I_君莫笑_I + 1 + 1 用心讨论£¬?#19981;?#25552;升£¡
q739806133 + 1 + 1 热心回复£¡
Gold_Jin + 1 + 1 我很赞同£¡
?#21496;?#19968;个字 + 1 + 1 谢谢@Thanks£¡
chugerxiaoxiao + 1 + 1 用心讨论£¬?#19981;?#25552;升£¡
zxc1998gzp + 1 + 1 谢谢@Thanks£¡
不在乎的存在 + 1 + 1 谢谢@Thanks£¡
卖血上网 + 1 + 1 谢谢@Thanks£¡
poisonbcat + 1 + 1 谢谢@Thanks£¡
longge188 + 1 用心讨论£¬?#19981;?#25552;升£¡
kerrypontiff + 1 + 1 谢谢@Thanks£¡
dioderen + 1 + 1 谢谢@Thanks£¡
染指倾城23153 + 1 + 1 ?#34892;?#21457;布原创作?#32602;?#21566;爱破解论坛因你更精彩£¡
jnez112358 + 1 + 1 谢谢@Thanks£¡
仰望大?#24515;?#19968;膜 + 1 + 1 太秀了
Leidus + 1 + 1 谢谢@Thanks£¡
alien11 + 1 我很赞同£¡
ikea + 1 + 1 用心讨论£¬?#19981;?#25552;升£¡
gongyong728125 + 1 + 1 热心回复£¡
富春山居 + 1 + 1 ?#34892;?#21457;布原创作?#32602;?#21566;爱破解论坛因你更精彩£¡
nlrr521 + 1 + 1 用心讨论£¬?#19981;?#25552;升£¡
darkenvan + 1 膜拜大佬
stcdalyc + 1 + 1 不错,简单详细,里面的tips很好
1633455355 + 1 + 1 我很赞同£¡
zaleyao123 + 1 + 1 谢谢@Thanks£¡
LLingding + 1 + 1 谢谢@Thanks£¡
CrazyNut + 3 + 1 大佬太牛逼了 膜拜
linuxabc + 1 + 1 鼓励转贴优秀软件安全工具和文档£¡
lyb1713 + 1 + 1 谢谢@Thanks£¡
独行风云 + 1 + 1 ?#34892;?#21457;布原创作?#32602;?#21566;爱破解论坛因你更精彩£¡
wzdm98 + 1 + 1 用心讨论£¬?#19981;?#25552;升£¡
好好学习2019 + 1 + 1 ?#34892;?#21457;布原创作?#32602;?#21566;爱破解论坛因你更精彩£¡
ectxt + 1 + 1 热心回复£¡
dmxayjn + 1 + 1 谢谢@Thanks£¡
叼毛兽 + 1 + 1 谢谢@Thanks£¡
巨根?#35753;?/a> + 1 鼓励转贴优秀软件安全工具和文档£¡
bbn + 1 + 1 我很赞同£¡
一人孤看夜空 + 1 + 1 热心回复£¡
天空?#20301;?#20687; + 2 + 1 给力的£¬分析学习了
BoatJ + 1 + 1 谢谢大神的详细讲解
初亦泽 + 3 + 1 我很赞同£¡
不懂破解 + 1 + 1 不说了厉害啊
哇哇哇~~~ + 1 谢谢@Thanks£¡
chen4321 + 1 + 1 我很赞同£¡
dongtian123sss + 1 + 1 热心回复£¡
SxAni丶 + 2 + 1 ?#34892;?#21457;布原创作?#32602;?#21566;爱破解论坛因你更精彩£¡
jusun + 1 + 1 大佬
风雨辰 + 1 谢谢£¬楼主?#37327;?#20102;
suoyt + 1 + 1 我很赞同£¡
天空藍 + 1 + 1 &amp;lt;font style=&amp;quot;vertical-align: inherit;&amp;quot;&amp;gt;&amp;lt;font style=
是朔朔 + 1 + 1 我很赞同£¡
jarry2009 + 1 + 1 我很赞同£¡
764193441 + 1 + 1 鼓励转贴优秀软件安全工具和文档£¡
伞兵 + 1 + 1 鼓励转贴优秀软件安全工具和文档£¡
校门口的萌新 + 1 + 1 我很赞同£¡
风轻?#39057;?#21834; + 1 + 1 ?#34892;?#21457;布原创作?#32602;?#21566;爱破解论坛因你更精彩£¡
WD丶活着 + 1 用心讨论£¬?#19981;?#25552;升£¡
天蝎浪花 + 1 + 1 谢谢@Thanks£¡
不学¡ü无术 + 1 + 1 ?#34892;?#21457;布原创作?#32602;?#21566;爱破解论坛因你更精彩£¡
hxnm + 1 + 1 热心回复£¡
showwh + 1 + 1 ?#34892;?#21457;布原创作?#32602;?#21566;爱破解论坛因你更精彩£¡
pj10234 + 1 + 1 --------
cxp521 + 1 + 1 去£¬牛逼£¡编辑都要N久£¬先留脚印£¬晚上看
15276305588 + 1 + 1 谢谢@Thanks£¡
公孙秒秒 + 1 + 1 思路好棒
KaQqi + 1 谢谢@Thanks£¡

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用¡¾论坛搜索¡¿功能£¬那里可能会有你要找的答案或者已经有人发布过相同内容了£¬请勿重复发帖¡£

推荐
刘JJ 发表于 2019-4-9 22:41
请问楼主大大 :
根据第二关尝试10£¬我学习了每一发子弹都存在动态数组中£¬但是不清楚子弹的初始化伤害值是由哪个常量调用到数组中去的
(第二关尝试10从寄存器中得?#38454;?#24377;地址的图片挂了£©

谢谢楼主大大的耐心帮助
推荐
 楼主| Ganlv 发表于 2019-4-14 13:09 |楼主
KaQqi 发表于 2019-4-14 12:51
楼主第一关改血量的方法怎么过的呀¡£¡£为什么我改血量£¬物体在受到攻击后又回到正确的血量了

改错了呗£¬如果有多个相同的值£¬有的值可能是中间计算步骤的值£¬你必须得改最初是那个值才有用¡£
半夜了£¬还有干货£¬收到有空学习£¬楼主早点休息
5#
Coxxs 发表于 2019-3-30 00:01
看完受益匪?#24120;?#22826;强了
6#
yntcxlong 发表于 2019-3-30 00:08
?#34892;?#20998;享£¬过程很详?#31119;?#24456;值得学习~~~~
7#
zheng123 发表于 2019-3-30 00:18
6666666厉害我的哥£¡A£¡£¡
8#
52pc 发表于 2019-3-30 00:27
?#34892;?#20998;享
10#
天空?#20301;?#20687; 发表于 2019-3-30 01:35
太牛逼了£¬6.8.3都还没下载的
12#
1毛钱雪糕 发表于 2019-3-30 04:06
?#34892;?#20998;享£¡£¡
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告£º禁止回复与主题无关内容£¬违者重罚£¡

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2019-5-22 11:02

Powered by Discuz!

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表
Çò̽ÍøÀºÇòÖ¸Êý