|
本帖最后由 jiangzhengwenjz 于 2015-3-21 20:56 编辑
原始贴作者FBI agent,发在这里主要是作个记录,当然有人要用的话征得作者同意应该也是可以。怎么用?最后的脚本会作解释(很容易,这个作者都懒得写了
然后再次重申下这贴不是教程,楼主又是个新人,难免有错漏,要转请转原帖。
工具:VBA-SDL-H, hex编辑器, thumb编译器, A-trainer(方便修改来搜查), XSE
注:此贴需要最基本的改版知识,否则难以看懂
这个原帖主要举了一个例子来说明如何利用ASM解决实际问题:读取刚对战的训练师名称。(看上去很简单,其实通过debug深入探究就会发现并不是那么容易)
这里的例子是JANICE,一个训练师
第一部分:目的简介
目的:通过ASM读取上次对战的训练师名称,并将之应用于文本
基本想法:从内存中读取文本数据,然后用脚本转换为文本
第二部分:初探
用VBA-SDL-H打开火红,到准备考察的训练师那里,在有训练师图片的地方停住(此时还未派出精灵),然后进入debug模式,键入命令搜查某训练师名称的十六进制数据。(利用fh)
(如何转换文本为hex? 文本大师或者有对应码表就可以)
可以看到,搜查的结果有一个在RAM中,如果之后游戏不会乱动它,最简单的想法是直接读取。
显然,我们先该测试看看这个地址是否动态。
最简单的方法是重做一遍刚才的步骤,可以发现这个地址是固定的。
但是,仅凭这样显然不能得出结论,因为有可能很快这个地址的数据就被修改了
为了证实猜测,不妨用bpw来看看是不是会被改掉。
继续游戏(进入对战了),很快游戏被断到(注意用bpw的时候要给名字占用的字节+1,别忘了FF,虽说不加应该也无所谓)
(根据我自己的尝试,似乎不同训练师的这个内存地址不同,但既然这个方法无效所以无所谓了)
为了更直观地查看,不妨我们用mb命令看看这个内存地址变成了什么数据,查看后发现果然变了。(所以之前被断到)
也就是说,很快游戏就把这个内存地址写了别的数据,所以直接从这个地址读数据的想法已经废了。
那么接下来不妨用另一个方法:
反向思考,可以从ROM中读取训练师名称的数据,但这样做的话我们必须找到读取训练师的ID的方法。
第三部分:搜查数据表格
这是最容易的一个部分了(好吧除了脚本),也是我们搜寻数据的一个基本方法,那就是修改后搜寻/比对。打开训练师编辑器,改掉第一、二个训练师的名字,然后hex搜索这两段文本对应的数据。很容易发现第一个名字在0x23EAF4。然后多修改几次能够发现,训练师名称之间都相差0x28个字节,也就是说既然我们知道第一个名字的地址,那么就可以得出读出任一训练师的名字地址(本质上是等差数列)。既然如此,问题依然归结到如何获得ID。
第四部分:再次debug
问题现在简化,即探究运行时是如何读取ID的。那么自然,debug。
对JANICE而言其ID是0x74,其名字的ROM地址也很清楚。(0x823FCEC)
那么VBA-SDL-H中必然是输入触发对战,游戏会在0x8011376被我们断到。
其对应的thumb指令是:那么,是时候反汇编了,可以用VBA直接反汇编,也可在VBA-SDL-H中利用dt
。
不妨从0x08011366开始反汇编。(如何查看此时寄存器中的值?再设置断点即可)
- ROM:08011366 loc_8011366:
- ROM:08011366 MOVS R6, #0
- ROM:08011368 LDR R0, =0x823EAC8
- ROM:0801136A LDR R2, [SP,#0x20]
- ROM:0801136C LDR R3, [SP,#0x14]
- ROM:0801136E ADDS R1, R2, R3
- ROM:08011370 LSLS R1, R1, #3
- ROM:08011372 ADDS R3, R0, #4
- ROM:08011374 ADDS R1, R1, R3
- ROM:08011376 LDRB R1, [R1]
- ROM:08011378 MOVS R4, R0
- ROM:0801137A LDR R0, [SP,#0x18]
- ROM:0801137C ADDS R0, #1
- ROM:0801137E STR R0, [SP,#0x1C]
- ROM:08011380 CMP R1, #0xFF
- ROM:08011382 BEQ loc_801139E
- ROM:08011384
- ROM:08011384 loc_8011384:
- ROM:08011384 LDR R0, [SP,#0x14]
- ROM:08011386 ADDS R1, R2, R0
- ROM:08011388 LSLS R1, R1, #3
- ROM:0801138A ADDS R0, R6, R1
- ROM:0801138C ADDS R0, R0, R3
- ROM:0801138E LDRB R0, [R0]
- ROM:08011390 ADD R9, R0
- ROM:08011392 ADDS R6, #1
- ROM:08011394 ADDS R1, R6, R1
- ROM:08011396 ADDS R1, R1, R3
- ROM:08011398 LDRB R0, [R1]
- ROM:0801139A CMP R0, #0xFF
- ROM:0801139C BNE loc_8011384
复制代码
关于代码的解释很容易我也不想罗嗦了,大概说下吧。
(顺便说句最好对训练师数据结构了解一下)
第一段是检测名字的第一个是否为FF,值得注意的是[SP, #0x14]是训练师ID。
第二段是先和第一段类似再次检测。
也就是说无法从这段代码获得有用的信息,因为此时SP+0x14这个地址中已经存入了ID。
那么自然,读取ID并存入SP+0x14过程是在执行这段指令之前,有必要进行探究。
往上翻,注意到附近所有的跳转都会到0x08011366,无甚值得考察。直到0x0801133C。
这个命令不是thumb命令。但注意到0x0801133c和0x0801133e这里的数据居然正好是表格的指针。
作者似乎认为这很有可能表征着这段无关代码的结束,那么我们在接下来的地址下断点0x0811340。
再次对战,果然又断到了。而且这次终于有了些发现。
- 08011684 E65C b $08011340
- > 08011340 9b05 ldr r3, [sp. #0x14]
- 08011342 18c8 add r0, r1, r3
复制代码
可以看到这段代码由0x08011684跳转而来。但是可惜依然说明不了任何问题。而无休止下断的方法显然是个无底洞。
因此我们需要及时转变思路了。
注意到0x08011324的代码出现了[SP, #0x14],然后在0x08011330跳转到了0x801167E。很好,这样就和之前的研究连起来了。因为:
- 0801167E ldrb r0, [r0, #0x0]
- 08011680 cmp r4, r0
- 08011682 bge #0x8011686
- 08011684 b #0x8011340
复制代码
既然如此,接下来又可以反向追踪指令了!继续往上翻,发现在0x11318有bl指令。注意到0x1130C再次用到了[SP, 0x14]。
由于bl命令的特性,无疑我们只需确定向[SP, 0X14]中写数据的命令是否在其跳转的地址的函数中。换一种方法思考,也可以看0x1130C时,[SP, 0x14]是否已被写入了ID。(更容易)
那么就很容易了,在此处下断,观察寄存器的变化即可。
事实上,此时这个值已经是ID,所以bl到的地址到底发生了什么无所谓了。
那么,自然是继续往前翻了。注意到0x112E0 push了寄存器,往往这表征着一个程序的开始。那么自然稍稍往下看:
是的,在0x112f2的地方,终于找到了我们需要的命令:str r1, [SP, 0x14]
那么,接下来有两种办法:第一种是强设跳转,第二种继续追踪。
作者采用了继续追踪。
那么,自然在0x080112E0下断。断到后,注意到前一条指令(0x0800FF92)看不懂,这其实是由bl命令的特性造成的(占用4字节)。那就只能再反汇编了,证明这的确是bl命令。
果然:
更为重要的是,前面的命令告诉了我们r1是如何取得ID值的!(从0x20386AE读值)
也就是说,从这个地方读值无疑是个不错的想法。接下来看看它会不会被其他数据覆盖。bpw也好,比对内存也好,发现这个数据直到对战结束都不会变化!
第五部分:编写程序
首先:
名字对应的数据存在哪里比较好呢?作者给出的地址:0x2021D18
那么接下来就很容易了,这个代码我也不想解释了
- .text
- .align 2
- .thumb
- .thumb_func
- main:
- push {lr}
- calc_name_location:
- ldr r0, =(0x20386AE)
- ldrh r0, [r0]
- mov r1, #0x28
- mul r0, r0, r1
- ldr r1, =(0x823EACC)
- add r0, r0, r1
- ldr r1, =(0x2021D18)
- loop:
- ldrb r2, [r0]
- strb r2, [r1]
- cmp r2, #0xFF
- beq end
- add r0, r0, #0x1
- add r1, r1, #0x1
- b loop
- end:
- pop {pc}
复制代码
注意为什么不用push一些寄存器呢?因为我们用的是callasm指令,会自动push和pop r0-r3。
第六部分:脚本
老实说这个实在太容易,但可能有人不知道bufferstring这个命令,所以我提一下。
简单说,callasm然后bufferstring,即可,具体的就不写了。
结语:极其简单的程序背后的debug工程量其实也很巨大,要真正做到熟练地debug恐怕还是要多多实践。由于楼主还是个新手难免出现各种各样的错误,还请各位指正! |
评分
-
查看全部评分
|