首页
论坛
专栏
课程

[旧帖] [原创]FC游戏 炸弹人续关码逆向 编码部分 0.00元

2011-4-3 11:25 6999

[旧帖] [原创]FC游戏 炸弹人续关码逆向 编码部分 0.00元

2011-4-3 11:25
6999
4年前做的,现在有空整理一下发出来混个邀请码….

    FC游戏-炸弹人1代,相信很多80后都玩过吧,我小时候挺喜欢玩的,游戏玩的不怎么样,但是对那个续关码很感兴趣,曾经记了20多页纸试图找出其中的规律,只知道最后一位是校验位 -_-!
    要想知道续关码的算法,首先要知道它的处理器,一搜才知道,原来NES用的是6502,这可是一款经典的CPU,Apple一代和二代都是用的它,还有国产的文曲星…说到文曲星,当年(2003年)表哥送我一台文曲星(具体什么型号忘了),我上网查到可以用数据线升级BASIC功能,就满泰安城找数据线去了,把跟我去的同学都逛烦了,最后也没有找到,不然也许能早入门个几年……:)

用到的资源:
炸弹人ROM 文件     Bomber Man (J).nes  http://www.romnation.net/srv/roms/28411/nes/Bomberman-J.html

6502 指令集                  http://www.laogu.com/laogubbs/sharedown.asp?id=270&srv=1
FCEU0.98.12.win       (带Debug功能的NES模拟器)http://mmm.onegreen.net/soft10/FCEU0.98.12.win.rar
NESpackage          (Ida 载入nes格式的插件)  http://bbs.pediy.com/showthread.php?t=125458

      首先,我们把 NESpackage\nesdbg\bin\nes_stub.plw copy到ida的 \loaders文件夹里面,这样Ida才能识别NES格式的文件,用Ida载入Bomber Man (J).nes
      我们可以看到ida已经识别出代码段和数据段了,下面要做的就是定位代码,
      要定位代码,就要知道数据的存储地址,懒得去自己调试,最快的方法还是站在巨人的肩膀上…从网上搜了个炸弹人的金手指

; 炸弹人1代金手指
#1 0068-01-09    生命无限
#1 0073-02-09C0    最强的雷
#1 0075-01-01    速度加快
#1 0076-04-01010101  免疫功能
#1 007D-02-0101    其他功能
#1 007A-01-01    无敌
#1 0058-01-4A    最终关


要注意的是续关码只保存了一部分数据(例如,不保存剩余生命数),我知道0x75(加速鞋子)是保存在续关码里面的
用FCEU载入游戏,玩到最后一条命的时候,打开调试窗口


-------------------------------------------------------------------------------------------------
当主角死了但是还没有出现续关码的屏幕前,切换到调试窗口,点击单步,先让游戏暂停下来,在0x75设内存读取断点,然后点击运行


-------------------------------------------------------------------------------------------------
可以看到,程序中断在 e31b
切换到ida ,看看e31b在干什么?


-------------------------------------------------------------------------------------------------
E31b处的指令是   LDA     ($34),Y
查询6502指令集 


-------------------------------------------------------------------------------------------------
 得知,这种寻址方式属于零页间接索引地址,内存34和35处放置着一个16位地址,把这个地址再加上Y寄存器的值作为有效地址,再把有效地址指向的数据送到A寄存器中
我们看前面那条指令
JSR     sub_E327
Jsr是6502的call指令,我第一眼以为是像jmp一样不回来了呢
让我们到 子函数sub_E327 里面看看吧


-------------------------------------------------------------------------------------------------
原来地址在这里面,双击E334,来到这里


-------------------------------------------------------------------------------------------------
不好…ida把这里认作了代码….
只好手动undefine ,一共20个16位地址


-------------------------------------------------------------------------------------------------
BYTE offset[20]={       0x67,0x77,0xDD,0x61,0x99,0x66,0xDC,0x64,0x79,0x9A,
                        0x74,0x63,0x75,0x62,0x9B,0x65,0x94,0xDE,0x76,0x95
                };

经过无数的模拟器调试,我们知道了各个内存对应的属性

typedef struct
{
        BYTE score_1;           //分数右1位
        BYTE Remote_bomber;         //遥控
        BYTE stage_low;         //关卡低4位
        BYTE score_7;           //分数右7位     
       BYTE byte_99;        //未知
        
         BYTE score_2;           //分数右2位
        BYTE power;             //威力 由mem_73右移4位获得
        BYTE score_4;           //分数右4位
        BYTE mianyi;            //炸弹免疫        
         BYTE byte_9a;        //未知

        BYTE bomber_num;          //炸弹数
        BYTE score_5;           //分数右5位
        BYTE speed;             //速度
        BYTE score_6;           //分数右6位        
     BYTE byte_9b;        //未知

        BYTE score_3;           //分数右3位
        BYTE show_obj;          //显示物品
        BYTE stage_high;        //关卡高4位
        BYTE chuanqiang;        //穿墙
        BYTE byte_95;          //未知
}Address;


让我们再回到sub_E310:


-------------------------------------------------------------------------------------------------
一看就知道,这是一个for循环,以 byte_20为循环变量,目的是计算连续4个地址的校验和,放到byte_24中,我们要到上层函数里面看看byte_24放哪里去了
来到函数sub_E291: 其实这个就是encode函数


-------------------------------------------------------------------------------------------------
我们可以看到 byte_24 被放到刚才的地址列表中去了,原来
0x99;
0x9A
0x9B
分别放的是 前面4位数据的校验和 

下面我们对encode函数从头到尾分析一遍


-------------------------------------------------------------------------------------------------
通过调试,我们知道,byte_73里面放到是威力大小,但是是低4位和高4位反着放的,所以,最多能有15格的威力
Encode函数把byte_73的数据左移4位,得到威力数据,放到byte_dc中
下一步是 把byte_58(关卡数)中的数据分成高4位和低4位两部分
低4位放到 byte_dd 高4位放到byte_de
再往下就是初始化循环变量了


-------------------------------------------------------------------------------------------------
前面我们分析过,这是个 do while 循环 一共3次,把前3组的校验和算出来,分别放到0x99  0x9A  0x9B
然后下面还有一次,是计算最后一组校验和的,不过最后一组的校验和没有直接放到 0x95,而是加上前面3个校验和的左移一位之后的和,

下面还是个 do while 循环


-------------------------------------------------------------------------------------------------
一共循环了 0x28/2 = 20 次

程序设置了一个临时变量 byte_54
把内存列表对应的数据取出来,然后取数据的低4位,再减去byte_54 ,减去7,再取低4位,把结果放到 182(因为sub_e327中x加了2次)开始的内存地址。
用c语言表示
do
        {
                cache= (p[x]&0x0F)-Temp;
                Temp = ( cache-7 )&0x0F;
                psw[x++] = Temp;
        }while(x<20);


这时的内存 ....



到了这里,只差最后一步了,


-------------------------------------------------------------------------------------------------
程序循环 (0x2a -2) /2次,把 0x182开始的数据查表后输出
转换表在 e35c


-------------------------------------------------------------------------------------------------
共16位 

BYTE convert[]="AOFKCPGELBHMJDNI";      //转换表
转换之后就大功告成了……

炸弹人续关码编码和解码工具:
Project1.rar

[公告][征集寄语] 看雪20周年年会(12.28上海) | 感恩有你,一路同行

上传的附件:
最新回复 (21)
zbzb 2011-4-3 11:51
2
0
太强大了啊。。IDA简直无所不能啊
aliuwr 2011-4-3 14:36
3
0
这个确实,呵呵
tihty 3 2011-4-3 19:45
4
0
6502,也就意味着这些都不是x86指令?
逆转录酶 2 2011-4-4 11:28
5
0
正解~~~~
算是嵌入式吧
b23526 2011-4-4 12:41
6
0
强大,给力,膜拜之
Maskeva 2011-4-4 12:53
7
0
能力强大到凶残了
ttgood 2011-4-4 13:48
8
0
楼主,好多连接打不开了
huyuanhk 2011-4-4 14:08
9
0
看雪人才可真多。。
hbwazxf 2011-4-4 19:37
10
0
楼主太牛了,我以前用步步高学习机的时候还花100大元买了一本6502汇编的程序和书呢,结果现在还躺在我的箱子里呢。。
XPoy 3 2011-4-4 22:24
11
0
帅帅帅!

打个标记。
逆转录酶 2 2011-4-5 11:39
12
0
汇编?有开发环境吗?
不如basic更现实些吧
今夕望月 2011-4-5 20:44
13
0
太强了吧!!!顶一个
梦想自由 2011-4-5 23:33
14
0
感谢lz,这个对我修改模拟器游戏可是有很大的启发作用
tihty 3 2011-4-5 23:36
15
0
十分佩服楼主啊
煮雪 2011-4-7 15:46
16
0
向楼主学习。致敬。。
babalove 2011-4-7 21:03
17
0
厉害厉害   ida 什么都能调试
cocular 2011-4-7 21:36
18
0
怀念啊。。
真详细,好好学习学习
逆转录酶 2 2011-4-7 22:15
19
0
ida只能对6502静态分析,调试还得靠模拟器
广海混沌 2011-4-9 23:15
20
0
呵呵 得感谢下楼主
大小人物 2011-4-11 20:29
21
0
有点高深,我们只好看看带过
Chele 2011-4-11 20:38
22
0
做个标记
游客
登录 | 注册 方可回帖
返回