半年前写的文章了,原本是发表在兔友论坛上的,还是在自己的blog上也保留一份吧。
图片有些不能显示,点我去看原帖
(不需要注册)
---------------------------------------------------------------------------------------------------------------------------------------------------------
用
CT2打开
rom后很容易发现文本是标准
JIS编码,粗看了下文本在根目录下的
msg/jpn下。字库也很容易找到,在
/font/jpn下,但是用
CT2无法看到。这里需要
ASM 了。
在存档处可以看到这里的文本显示是以
OAM
的形式存在的,
OAM
可以理解是用
sprite
的方式,所以在第一个
OAM
那里
0x0660000
那里设断点:
[06600000.. 06600004]!
,关于如何在
No$gba
里设置断点,请看
No$gba help
文档里的
Debugging
部分。
这里停下来:
可以看到最后停在
strr4,[r3,8h]。而
r3=0x040000D4。这里用到了
DMA4,关于
NDS的
DMA请看
gbatek:
http://nocash.emubase.de/gbatek.htm
。
0x040000D4,存的是源地址,是
0x2273440
0x040000D8,是目标地址,可以看到上图右下角这个值下就是我们刚才设置的地址。
0x040000DC,是复制的字节数
可以去
0x2273440看到里面确实是可以直接显示(这里主要是为了区分前面无法直接显示)的字模了。这时候你可以
dump memory然后用
CT2看一下。这里说一下
No&gba里提供的
dump memory(
Utility->Hexdump to File),但是这个
dump出的是
ASCII模式,所以需要自己写程序转换成二进制(这个很简单啦)。
TILE是
4bpp,然后直接用
8*8显示如下
然后改成
32*16,并且用
ObjH-1234:
好啦,我们继续。接下来就要考虑
0x2273440
开始的内容是从哪里来的了。下断
[2273440.. 2273444]!
。
可以看到最后在
strbr7,[r4,r0]停下了,对照刚才
dump下来的内存,看来到这里就已经开始解密字库了
继续
F7
的话可以看到一个循环
- 0208CBD8 E59D0018 ldr r0,[r13,18h] ;2 48
- 0208CBDC E3580000 cmp r8,0h ;1 49
- 0208CBE0 E4D01001 ldrb r1,[r0],1h – 读取rom中的数据 ;3 52
- 0208CBE4 E58D0018 str r0,[r13,18h] ;1 53
- 0208CBE8 E59D3174 ldr r3,[r13,174h] ;2 55
- 0208CBEC E2017033 and r7,r1,33h ;1 56
- 0208CBF0 11A00107 movne r0,r7,lsl 2h ;1 57
- 0208CBF4 E58D1014 str r1,[r13,14h] ;1 58
- 0208CBF8 120070FF andne r7,r0,0FFh ;1 59
- 0208CBFC E08A0006 add r0,r10,r6 ;1 60
- 0208CC00 E0891005 add r1,r9,r5 ;1 61
- 0208CC04 E1A0200B mov r2,r11 ;1 62
- 0208CC08 EBFFF747 bl 208A92Ch ;3 65
- 0208CC0C E7C47000 strb r7,[r4,r0] ;1 66
- 0208CC10 E59D0034 ldr r0,[r13,34h] ;2 68
- 0208CC14 E5901014 ldr r1,[r0,14h] ;3 71
- 0208CC18 E5900010 ldr r0,[r0,10h] ;3 74
- 0208CC1C E0851001 add r1,r5,r1 ;1 75
- 0208CC20 E1510000 cmp r1,r0 ;1 76
- 0208CC24 AA00000B bge 208CC58h ;3 79
- 0208CC28 E59D0014 ldr r0,[r13,14h] ;2 81
- 0208CC2C E3580000 cmp r8,0h ;1 82
- 0208CC30 E20000CC and r0,r0,0CCh ;1 83
- 0208CC34 E20070FF and r7,r0,0FFh ;1 84
- 0208CC38 01A00147 moveq r0,r7,asr 2h ;1 85
- 0208CC3C 020070FF andeq r7,r0,0FFh ;1 86
- 0208CC40 E59D3174 ldr r3,[r13,174h] ;2 88
- 0208CC44 E0891001 add r1,r9,r1 ;1 89
- 0208CC48 E08A0006 add r0,r10,r6 ;1 90
- 0208CC4C E1A0200B mov r2,r11 ;1 91
- 0208CC50 EBFFF735 bl 208A92Ch ;3 94
- 0208CC54 E7C47000 strb r7,[r4,r0] ;1 95
- 0208CC58 E59D0020 ldr r0,[r13,20h] ;2 97
- 0208CC5C E2866002 add r6,r6,2h ;1 98
- 0208CC60 E5D00002 ldrb r0,[r0,2h] ;3 101
- 0208CC64 E1560000 cmp r6,r0 ;1 102
- 0208CC68 BAFFFFDA blt 208CBD8h ;3 105
复制代码
我把重要的语句用红色标记了。
首先看
0208CBE0 E4D01001 ldrb r1,[r0],1h
;读取
rom中的数据
这一句就是从字库里去数据了,不信的话可以找到
r0所在的内存,去
rom里搜索,找到的就是
system16*16.fnt。
然后再看这两句:
0208CC0C E7C47000strb r7,[r4,r0]
;1 66
0208CC54 E7C47000 strb r7,[r4,r0]
;1 95
r4就是刚才设置的断点的首地址,所以
r0就是偏移了,这两句的上面一句
0208CC50 EBFFF735 bl 208A92Ch ;3 94
很明显就是调用子函数计
算
r0,里面的内容大家有兴趣可以去看噢
J
好,接下来要讲的就是这段程序最重要的部分了(怎么才讲到最重要的地方
~):
0208CBEC E2017033 and r7,r1,33h
;1 56
0208CC30 E20000CC and r0,r0,0CCh
;1 83
0208CC34 E20070FF and r7,r0,0FFh
;1 84
0208CC38 01A00147 moveq r0,r7,asr 2h
;1 85
0208CC3C 020070FF andeq r7,r0,0FFh
;1 86
其实从这两句已经能看到这个加密的主要部分,字库的上下部分重叠起来存了
。
上部用
r1&0x33
下部用
(r1&0xCC)>>2
这里似乎很难直接看出端倪(我很少用这种很书面的词语
…
),请看下面的图
用
CT2
进入
TILE
模式,可以看到字体大致是
12*14
。下面模拟解密的过程,还是用图(偶没美工天赋,大家将就着看吧)
这是原先的内存,每个
tile占
32个字节,并且因为用了
4bpp,
2个像素用一个字节存,所以这里宽只画了
4行。并且这里要注意,我们的例子中,只有
tile1,
2,
5,
6有数据。
Process:
读取第一个字节,解密成
t1
和
t2
,放入内存,
t1
为第
1
行的前两个像素,
t2
为第
8
行的前两个像素。
前
4
个字节都在第一个
TILE
里,但是到了第
5
个字节时,也就是(
1
,
5
)和(
8
,
5
)【这里理解为一个
12*14
字模的
1
行
5
列和
8
行
5
列】。解密成
b1
和
b2
,如下图
然后再是第
1
行和第
8
行解密完后,就是第
2
行和第
9
行了,假设第一个字节如下解密成
c1
和
c2
,如下:
好了,相信到这里大家都已经明白解密的过程了,解密时关键是要计算出这个字节对应的
t哪个
tile的哪个地方。下面就是大家写程序解密字库了,然后修改字库后再加密回来,一样的思路。
字库如下:
这个只是一部分。(里面的
”大
”字是我测试时用过的
~)
字库的解密过程是写完了,但是这里还没提到字库文件的格式,这里我相信大家去分析一下的话都能分析出来,只是这里要提的一点是游戏里的
16*16
字库存放是变宽的,也就是英文,日文,中文汉字的宽度可能不一样。