GBA反汇编(烈火之剑中文版查找字库)
其实,反汇编很简单,但最好还是看一下每个功能
第一步,下载 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反汇编(烈火之剑中文版查找字库)相关推荐
- 终于打通了《火焰之纹章——烈火之剑》
前段时间在PSP上装了GBA模拟器,装上了<火焰之纹章>GBA三部曲:封印之剑.烈火之剑.圣魔之光石.今天终于打通了<烈火之剑>,总共611回合,其中一关有竞技场的打了132回 ...
- 《火焰纹章·烈火之剑》游戏评测
前言:据说1990年到2000年这段时间战棋类游戏十分的火,导致所有游戏公司都扎堆去搞战旗.而随着玩家游戏节奏的加快,2000之后战棋类慢慢不火了,现在市场上的战棋类游戏就变得比较少了.<火焰纹 ...
- 关于对玩过的游戏的想法汇总
2014.6.28 最近几天在玩GBA 火焰纹章 烈火之剑,真是一款很不错的经典的策略游戏, 玩着玩着就像他是怎么做的,发现确实很有水平,所以决定把玩过游戏的的好的想法和AI之类的都写在这上面 先来一 ...
- stm32怎么加载字库_收藏 | STM32单片机超详细学习汇总资料(二)
点击"蓝字"关注我们 3110月 收藏 | STM32单片机超详细学习汇总资料(一) ◆41.DMA仲裁器分为软件和硬件两种.软件部分分为4个等级,分别是很高优先级.高优先级.中等 ...
- 读字库遇到坑爹的问题
转载请注明出处:http://blog.csdn.net/qq_26093511/article/details/53099262 最近在做一个led显示屏的项目, 我想显示 "常" ...
- 有关The Last Promise的汉化①国外高手的FE7 hack版
这里就不多说什么了,博客了有一篇详解找字库的文章,这里我就大致的分析The Last Promise的程序 ①下断点,找到此断程序, 注:此段程序与烈火之剑的中文版有很大的不同,仔细分析一下.不过显示 ...
- 汉字转拼音(不带音调)
[JS 版本] 实现原理:直接弄一字库,汉字后面紧接着对应的拼音,把要转换的字符串逐字跟字库匹配,如果不是汉字直接返回,如果是汉字,查找字库返回相应的拼音. <script> var py ...
- IGN评史上最佳100 RPG
IGN评选完整榜单: 001.<超时空之轮>(Square.SFC.1995年3月11日发售) 002.<最终幻想6>(Square.SFC.1994年4月2日发售) 003. ...
- stm32显示flash下载失败_STM32大神笔记,超详细单片机学习汇总资料(干货分享4)...
1.NEC协议在发送的时候,会有560us的38KHz的载波信号,而在接收的时候这部分载波信号被认定为低电平,而剩余的(2.25ms-650us)的逻辑"1"和(1.12ms-65 ...
最新文章
- C++数值极限numeric_limits
- Part1_4 python函数、文件操作、异常处理
- 【Beta阶段】发布说明
- 视觉基础与开发思路-第九节形态学操作
- fpm制作mysql rpm包_FPM简介(定制rpm包)
- sqlalchemy mysql配置中怎么设置utf8_在SqlAlchemy中,我想要一个列是UTF8?
- maven 学习---Maven本地资源库
- 已知贝塞尔曲线上的点求控制点
- 为什么nodejs是单进程的_Nodejs探秘:深入理解单线程实现高并发原理
- php 数据处理--合并,拆分,追加,去重
- 系统调用功能模块的初始化
- 从程序员到项目经理(2)
- 项目管理软件: 禅道、JIRA
- 将.pem转换为.crt和.key
- imageAI基本使用
- MongoDB——explain执行计划详解
- ims系统 呈现服务器,ims系统
- 20220326-代码日记-Unity画符
- 微信小程序顶部导航栏颜色修改
- iOS Xcode中UIButton文字换行
热门文章
- 一套仿阿里完整版Spring Boot电商项目,前后端分离+权限管理系统
- 联通签到php,联通缓存服务器测速脚本(php)
- 将多个文件进行压缩处理,然后传输到服务器
- 鸿蒙网络请求(原生+zzr+OkHttp+Retrofit)
- 洞道干燥及计算机控制实验报告,化工原理洞道干燥实验报告模版
- 一个或多个音频服务未运行怎么修复?
- Protocol Buffer在MCU上的实现--C语言
- 巴旦木树苗移栽方法丨巴旦木苗木种植技巧
- 【渝粤教育】国家开放大学2018年春季 0700-22T中级会计实务(一) 参考试题
- Avery Dennison推新款RFID嵌体,适用于牛仔服饰