其实,反汇编很简单,但最好还是看一下每个功能

第一步,下载 No$GBA debug版,注意是后缀是debug;

第二步,ROM,烈火之剑的ROM.

这两样东西,都能用搜索引擎搜索到

第三步

注意绿色方框的地址,写断点("调试"→"设置带条件的断点"或 ctrl + B)设置成  "[06013000..06013020]!"(注:不包括双引号,因为每个Tile 20字节大小,所以设置为区域断点)

②不要关了精灵对话框,时刻注意它的改变,切换回不显示任何文本的状态如下图

(注:其中会中断几步,那些都不是要显示文本的);

③,按R键,其中会中断很多次例如下图

看指令和寄存器状态,指令上一步是,

streqh r3,[r1],2h;此指令,把寄存器R3的值写入[r1]中.(注:以后[]里面都表示存储器地址);

虽然是写入该地址数据指令,但看r3的值为0,所以现在这段程序还不是显示文本的

<提示:你可以按F7单步跟踪,你会发现此段是把区域的Tile清空>

④点击游戏的屏幕或F9或"运行"→"运行",会发现又断在刚才那个地方,注意了,你要时刻注意这精灵对话框,看里面的变化

⑤因为运行过头了,所以变成现在这种情况

注意绿色的六个寄存器状态,和绿色的代码,

08005C8C E7D1     b       8005C32h   ;
08005C8E B001     add     sp,4h     ;
08005C90 BC03     pop     r0,r1     ;
08005C92 7882     ldrb    r2,[r0,2h]    ;
08005C94 1852     add     r2,r2,r1  ;
08005C96 7082     strb    r2,[r0,2h]    ;
08005C98 BCF8     pop     r3-r7     ;
08005C9A 4698     mov     r8,r3     ;
08005C9C 46A1     mov     r9,r4     ;
08005C9E 46AA     mov     r10,r5    ;
08005CA0 46B3     mov     r11,r6    ;
08005CA2 46BC     mov     r12,r7    ;
08005CA4 BCF0     pop     r4-r7     ;
08005CA6 BC01     pop     r0        ;
08005CA8 4700     bx      r0        ;
08005CAA 6838     ldr     r0,[r7]   ;
08005CAC 4310     orr     r0,r2     ;
08005CAE 6038     str     r0,[r7]   ;把R0的值写入[R7]中,刚刚好R7是显存地址,而R0又是有效的数据,切会看精灵,会发现写了两点
08005CB0>4770     bx      r14        ;

这个时候,你就要看所有寄存器状态,因为是写入有效显示值(要和精灵显示对话框对比),

所以,我们要找的就是这段程序,再看看所有寄存器状态,发现R7非常可疑,因为R7是ROM的地址,

⑥这是我们用记事本几下所有寄存器状态和此断点地址,(当然你记忆好,可以不用记事本)

后面,我们试着把该ROM的R7地址修改一下数据  (可以在下图红色框框中 右键→"Data window")

然后跳转(ctrl+g)到08BE578C

尝试修改Dota window里面光标的数据(1-10bytes),然后继续运行(完全运行,无中断为止)

你会发现此时游戏中的文本已经不一样了,所以r7为该字的字库地址

⑦虽然找到了该地址,但我们用CT2打开,并跳转该地址时,未发现存在任何字,难道我们错了吗?

此时,我们不应该着急,我们应该研究一下程序

返回以上我们的断点(中断地址为:08005CB0 <记事本有用了>)

先在那个断点单步调试或F7,一步步来(你可以大概看一下,或仔细看一下),

按了F7几十次后,会发现它在一个循环中执行,并且再切换到Tile视图,会发现一下下更新Tile,

循环起始到结束地址:5C36-5CB0

⑧我们这时,就要分析指令

分析结果如下

08005C36 2200     mov     r2,0h              ;初始化r2
{                                           ;循环
08005C38 2D00     cmp     r5,0h             ;比较r5,这是看r5的值,设置状态寄存器       *还是未知
08005C3A>D103     bne     8005C44h           ;标志不等于则跳转,(我没有深入学弄arm汇编,可以理解r5不等于0时)
{
08005C3C 4640     mov     r0,r8             ;
08005C3E C840     ldmia   [r0]!,r6          ;
08005C40 4680     mov     r8,r0             ;
08005C42 2510     mov     r5,10h            ;此程序段表示读取ROM数据
}
08005C44 3D01     sub     r5,1h             ;r5--                                   *
08005C46 2003     mov     r0,3h             ;r0=3                                                  *r0=bit:11
08005C48 4030     and     r0,r6             ;r0&=r6        //注,此时r6似乎是关键,<其实是ROM里面的数据>   *保存r0:bit11
08005C4A 08B6     lsr     r6,r6,2h          ;r6>>=2h //右移两位,就是 bit<转换成二进制后再右移>     *把r6删除掉两bit
08005C4C 4468     add     r0,r13            ;r0+=r13  //目测是关键数据,r0=r0+栈地址                   *.....
08005C4E 7800     ldrb    r0,[r0]           ;r0=[r0]   //读取栈地址+3,未知意义?                        *读取[栈+r0]数据,注②
08005C50 4098     lsl     r0,r3             ;r0<<=r3 //r0左移r3,未知意义?                          *循环中发现,r0每次进4位以防止r2^=r0重叠
08005C52 4302     orr     r2,r0             ;r2^=r0        //r2异或r0,bit运算,百度去                  *r2为重点,所以逆向分析
08005C54 3C01     sub     r4,1h             ;设置标志并r4--                          ***
08005C56 D007     beq     8005C68h          ;等于则跳转                              **跳转到该地址后,又跳转到子程序<不得不承认,汉化组的天才之处>
08005C58 3304     add     r3,4h             ;r3+=4                                    *表示已经位运算了4位
08005C5A 2B20     cmp     r3,20h            ;比较r3与20                                *当等于20时,表示已经运算了20h位 32bit,寄存器存不下超过32bit的值
08005C5C D3EC     bcc     8005C38h          ;小于则跳转                              *未达到要求,继续循环5C38-5C5C
}
08005C5E F000F824 bl      8005CAAh          ;跳到子函数,我们单步了很久,终于出来了        *
{
08005C62 3720     add     r7,20h            ;此程序段表示切换成Title2绘制
08005C64 2300     mov     r3,0h             ;
08005C66 E7E6     b       8005C36h          ;
}
08005C68 F000F81F bl      8005CAAh          ;       **子程序               ***
08005C6C 4660     mov     r0,r12            ;       **r0=r12           ***
08005C6E 3801     sub     r0,1h             ;       **设置标志位 r0--    ***
08005C70 D00D     beq     8005C8Eh          ;       **等于则跳转         ***
08005C72 4684     mov     r12,r0            ;       **r12=r0           ***
08005C74 464F     mov     r7,r9             ;       **r7=r9                ***切换回Title
08005C76 2900     cmp     r1,0h             ;       **r1和0比较            ***  r12代表绘制剩余行数
08005C78 D006     beq     8005C88h          ;       **等于则跳转         ***以上的程序段,判断是Tile1?Tile3 地址相差400
{
08005C7A 3901     sub     r1,1h             ;       **设置标志位,r1--
08005C7C 2900     cmp     r1,0h             ;       **比较r0与0
08005C7E DC03     bgt     8005C88h          ;       **有符号大于
08005C80 2080     mov     r0,80h            ;       **r0=80h
08005C82 00C0     lsl     r0,r0,3h          ;       **r0<<=3 ***
08005C84 3820     sub     r0,20h            ;       **r0-=20   ***字库地址-20
08005C86 183F     add     r7,r7,r0          ;       **r7+=r0  ***字库地址+400  Tile3地址,也就是第三个Title(看精灵视图)
}
08005C88 3704     add     r7,4h             ;       **r7+=4
08005C8A 46B9     mov     r9,r7             ;       **r9=r7     以上程序:更新显存地址,
08005C8C E7D1     b       8005C32h          ;       **跳转5C32,检查那两条指令,分别赋值两个,一个是r4,r3进行第三次注释(现在分析r4)
08005C8E B001     add     sp,4h             ;
08005C90 BC03     pop     r0,r1             ;
08005C92 7882     ldrb    r2,[r0,2h]        ;
08005C94 1852     add     r2,r2,r1          ;
08005C96 7082     strb    r2,[r0,2h]        ;
08005C98 BCF8     pop     r3-r7             ;
08005C9A 4698     mov     r8,r3             ;
08005C9C 46A1     mov     r9,r4             ;
08005C9E 46AA     mov     r10,r5            ;
08005CA0 46B3     mov     r11,r6            ;
08005CA2 46BC     mov     r12,r7            ;
08005CA4 BCF0     pop     r4-r7             ;
08005CA6 BC01     pop     r0                ;
08005CA8 4700     bx      r0                ;
08005CAA 6838     ldr     r0,[r7]           ;r0=[r7]   //读取显存地址的值
08005CAC 4310     orr     r0,r2             ;r0^=r2        //bit运算
08005CAE 6038     str     r0,[r7]           ;[r7]=r0   //写入显存,注①4770     bx      r14               ;返回子函数先前的地址注①:写入前,发现r0和r2位运算,那么说明r2是重点,那么r0的为是什么呢? r0 = 1000100010001000100010001000100 :
r0 = 1000100010001000100010001000100 :然后结合Tite视图发现是白底,你也可以改成00000000,会发现是透明;
那么说明r2为字库值,因为是16色所以,所以表示一个word存放8个点 (arm中 word=32bit)
我们再做第二遍注释,注释中"*"表示注解2注②:非常仔细分析,发现 r0 在0-3的区间,也就是取址范围在 [栈地址+0]-[栈地址+3] 0Fh 0Eh 0Dh 04H读取偏移来写入显存数据,此注释要结合程序慢慢理解,此2bit为一个点颜色
注尾:突然觉得标注,会有人很不理解,大家还是慢慢分析吧,我给出结构
r7 = 显存地址
r8 = 字库目前地址
r11 = 字的宽度

重点是要慢慢分析

结果得出r8和r11为重点.

⑨在5C2A(此程序头)下断点.,运行之,查看r14寄存器发现是上一条指令遗留下来的(详细查百度,bl指令),所以在5C26下断点,运行之

现在我们查看我们的r8寄存器,

r8 = 08BCDAE8

现在我们把所有断点都删除,然后添加断点

r8 = 08BCDAE8

运行,重新显示说明文本,发现中断在此处

分析一下,得出,r1为重点了,查看r14的值记事本记下来

跳转到r14地址,下断点,发现r1为rom地址,逆向分析一下,发现有可疑的地方,如下图

在56EC处下断点,运行之

发现寄存器如下图

r1,r4可疑,跳转至0202ADC4,发现此处问字符串地址,记事本记录

080056EC>686E     ldr     r6,[r5,4h]      ;
080056EE 7821     ldrb    r1,[r4]       ;r1 X 读取到的字符串1
080056F0 7862     ldrb    r2,[r4,1h]        ;r2 Y 读取到的字符串2
080056F2 3402     add     r4,2h         ;r4+=2
080056F4 2981     cmp     r1,81h        ;X与0x81比较
080056F6 D303     bcc     8005700h      ;小于则跳转,这里小于,则表示为ANSIC字符串
080056F8 2998     cmp     r1,098h       ;比较98,  这里表示当X大于0x98时,则跳出去,0x81<X<0x98
080056FA D801     bhi     8005700h      ;
080056FC 2A80     cmp     r2,80h        ;比较R2与80,大于等于
080056FE D201     bcs     8005704h      ;跳至
08005700 2183     mov     r1,83h        ;
08005702 229B     mov     r2,09Bh       ;
08005704 3981     sub     r1,81h        ;r1-=0x81
08005706 2380     mov     r3,80h        ;
08005708 4359     mul     r1,r3         ;r1*=0x80  //注,这里为大跳转
0800570A 3A80     sub     r2,80h        ;r2-=0x80
0800570C 1852     add     r2,r2,r1      ;r2+=r1       //注,这里为小挑转+大挑战
0800570E 2154     mov     r1,54h        ;
08005710 434A     mul     r2,r1         ;r2*=0x54  //注,这里表示每个字符多少个字节 54个字节
08005712 1991     add     r1,r2,r6      ;r1=r2+r6 //r6为字库基地址
08005714 68AA     ldr     r2,[r5,8h]        ;
08005716 F0BBF83F bl      80C0798h      ;

C/C#/JAVA获取字库地址,这里base为0xBC0698

int offset(byte X, byte Y,int Base)
{if ((X<0x81)&&(X>0x98)) return 0;if (Y < 0x80) return 0;X -= 0x81;Y -= 0x80;return Base + (((X * 0x80) + Y)*0x54);
}

C#字库生成

      /// <summary>/// bit转换成bitmap/// </summary>/// <param name="X">宽度,字地址+0得出X</param>/// <param name="Y">高度,字地址+1得出Y</param>/// <param name="Bdata">输入数据字节数组,一般为0x20字节</param>/// <param name="Cdata">输入颜色字节数组,一般为{ 0x9C, 0x6f, 0x7b, 0x6f, 0xF7, 0X5E, 0xA5, 0X14 }</param>/// <returns>返回bitmap</returns>public Bitmap Bit2Map(int X, int Y, byte[] Bdata, byte[] Cdata){Bitmap Bmap = new Bitmap(X, Y);Color[] color = new Color[4];byte[] nBy;UInt16 Cword;int Iint;int i;nBy = bit2Bytes(Bdata);for (i = 0; i < color.Length; i++){Iint = Cdata[i * 2] + (Cdata[i * 2 + 1] << 8);Cword = (UInt16)(Iint);color[i] = colorW(Cword);}i = 0;for (Y = 0; Y < Bmap.Height; Y++)for (X = 0; X < Bmap.Width; X++){if (i >= nBy.Length) break;Bmap.SetPixel(X, Y, color[nBy[i]]);i++;}return Bmap;}/// <summary>/// bit2 转换成byte/// </summary>/// <param name="Bdata">数据</param>/// <returns></returns>public byte[] bit2Bytes(byte[] Bdata){byte[] nBy = new byte[Bdata.Length * 4];int i;for (i = 0; i < Bdata.Length; i++){nBy[i * 4] = (byte)(Bdata[i] & 3);nBy[i * 4 + 1] = (byte)((Bdata[i] >> 2) & 3);nBy[i * 4 + 2] = (byte)((Bdata[i] >> 4) & 3);nBy[i * 4 + 3] = (byte)((Bdata[i] >> 6) & 3);}return nBy;}/// <summary>/// 转换成颜色/// </summary>/// <param name="word">16位无符号数据</param>/// <returns></returns>public Color colorW(UInt16 word){Color color;byte R;byte G;byte B;R = (byte)(word & 31);G = (byte)((word >> 5) & 31);B = (byte)((word >> 10) & 31);color = Color.FromArgb(0xFF, R * 8, G * 8, B * 8);return color;}

或许会有些出入,但记住就行了

字基地址为 :BC0698 
偏移地址为:((X-0x81)*80+(Y-0x80))*0x54
字的颜色数为: 2bit,四色,CT2中可以设置成如下图
宽度为 :字地址+0   高度为:字地址+1  Tile颜色格式为:VB 2bpp

GBA反汇编(烈火之剑中文版查找字库)相关推荐

  1. 终于打通了《火焰之纹章——烈火之剑》

    前段时间在PSP上装了GBA模拟器,装上了<火焰之纹章>GBA三部曲:封印之剑.烈火之剑.圣魔之光石.今天终于打通了<烈火之剑>,总共611回合,其中一关有竞技场的打了132回 ...

  2. 《火焰纹章·烈火之剑》游戏评测

    前言:据说1990年到2000年这段时间战棋类游戏十分的火,导致所有游戏公司都扎堆去搞战旗.而随着玩家游戏节奏的加快,2000之后战棋类慢慢不火了,现在市场上的战棋类游戏就变得比较少了.<火焰纹 ...

  3. 关于对玩过的游戏的想法汇总

    2014.6.28 最近几天在玩GBA 火焰纹章 烈火之剑,真是一款很不错的经典的策略游戏, 玩着玩着就像他是怎么做的,发现确实很有水平,所以决定把玩过游戏的的好的想法和AI之类的都写在这上面 先来一 ...

  4. stm32怎么加载字库_收藏 | STM32单片机超详细学习汇总资料(二)

    点击"蓝字"关注我们 3110月 收藏 | STM32单片机超详细学习汇总资料(一) ◆41.DMA仲裁器分为软件和硬件两种.软件部分分为4个等级,分别是很高优先级.高优先级.中等 ...

  5. 读字库遇到坑爹的问题

    转载请注明出处:http://blog.csdn.net/qq_26093511/article/details/53099262 最近在做一个led显示屏的项目, 我想显示 "常" ...

  6. 有关The Last Promise的汉化①国外高手的FE7 hack版

    这里就不多说什么了,博客了有一篇详解找字库的文章,这里我就大致的分析The Last Promise的程序 ①下断点,找到此断程序, 注:此段程序与烈火之剑的中文版有很大的不同,仔细分析一下.不过显示 ...

  7. 汉字转拼音(不带音调)

    [JS 版本] 实现原理:直接弄一字库,汉字后面紧接着对应的拼音,把要转换的字符串逐字跟字库匹配,如果不是汉字直接返回,如果是汉字,查找字库返回相应的拼音. <script> var py ...

  8. IGN评史上最佳100 RPG

    IGN评选完整榜单: 001.<超时空之轮>(Square.SFC.1995年3月11日发售) 002.<最终幻想6>(Square.SFC.1994年4月2日发售) 003. ...

  9. stm32显示flash下载失败_STM32大神笔记,超详细单片机学习汇总资料(干货分享4)...

    1.NEC协议在发送的时候,会有560us的38KHz的载波信号,而在接收的时候这部分载波信号被认定为低电平,而剩余的(2.25ms-650us)的逻辑"1"和(1.12ms-65 ...

最新文章

  1. C++数值极限numeric_limits
  2. Part1_4 python函数、文件操作、异常处理
  3. 【Beta阶段】发布说明
  4. 视觉基础与开发思路-第九节形态学操作
  5. fpm制作mysql rpm包_FPM简介(定制rpm包)
  6. sqlalchemy mysql配置中怎么设置utf8_在SqlAlchemy中,我想要一个列是UTF8?
  7. maven 学习---Maven本地资源库
  8. 已知贝塞尔曲线上的点求控制点
  9. 为什么nodejs是单进程的_Nodejs探秘:深入理解单线程实现高并发原理
  10. php 数据处理--合并,拆分,追加,去重
  11. 系统调用功能模块的初始化
  12. 从程序员到项目经理(2)
  13. 项目管理软件: 禅道、JIRA
  14. 将.pem转换为.crt和.key
  15. imageAI基本使用
  16. MongoDB——explain执行计划详解
  17. ims系统 呈现服务器,ims系统
  18. 20220326-代码日记-Unity画符
  19. 微信小程序顶部导航栏颜色修改
  20. iOS Xcode中UIButton文字换行

热门文章

  1. 一套仿阿里完整版Spring Boot电商项目,前后端分离+权限管理系统
  2. 联通签到php,联通缓存服务器测速脚本(php)
  3. 将多个文件进行压缩处理,然后传输到服务器
  4. 鸿蒙网络请求(原生+zzr+OkHttp+Retrofit)
  5. 洞道干燥及计算机控制实验报告,化工原理洞道干燥实验报告模版
  6. 一个或多个音频服务未运行怎么修复?
  7. Protocol Buffer在MCU上的实现--C语言
  8. 巴旦木树苗移栽方法丨巴旦木苗木种植技巧
  9. 【渝粤教育】国家开放大学2018年春季 0700-22T中级会计实务(一) 参考试题
  10. Avery Dennison推新款RFID嵌体,适用于牛仔服饰