1.下载文件发现是.gba格式的
在mGBA中打开得到

txt文件中提示要输入正确的作弊码
2.对文件反编译
我这里用的是Ghidra10.1版本https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_10.1_build/ghidra_10.1_PUBLIC_20211210.zip
加GhidraGBA插件https://github.com/SiD3W4y/GhidraGBA/releases/download/10.1/ghidra_10.1_PUBLIC_20211213_GhidraGBA.zip
将插件压缩包直接放到Ghidra的Extensions\Ghidra\目录下

然后打开Ghidra在File->Install Extensions勾选GhidraGBA
重新打开Ghidra并导入.gba文件
3.在左侧Symbol Tree->Functions寻找关键函数
在FUN_080015a8找到
void FUN_080015a8(void)
{
ushort uVar1;
undefined4 uVar2;
undefined4 uVar3;
ushort uVar4;
int iVar5;
ushort *puVar6;
undefined *local_2c;
DISPCNT = 0x1140;
FUN_08000a74();
FUN_08000ce4(1);
DISPCNT = 0x404;
FUN_08000dd0(&DAT_02009584,0x6000000,&DAT_030000dc);
FUN_08000354(&DAT_030000dc,0x3c);
uVar4 = DAT_030004d8;
do {
DAT_030004da = uVar4;
DAT_030004d8 = KEYINPUT | 0xfc00;
puVar6 = &DAT_0200b03c;
uVar4 = DAT_030004d8;
do {
uVar2 = DAT_030004dc;
uVar1 = *puVar6;
if ((uVar1 & DAT_030004da & ~uVar4) != 0) {
if (uVar1 == 4) {
DAT_030000d4 = 0;
uVar3 = FUN_08001c24(DAT_030004dc);
FUN_08001868(uVar2,0,uVar3);
DAT_05000000 = 0x1483;
FUN_08001844(&DAT_0200ba18);
FUN_08001844(&DAT_0200ba20,&DAT_0200ba40);
DAT_030000d8 = 0;
uVar4 = DAT_030004d8;
}
else if (uVar1 == 8) {
if (DAT_030000d8 == 0xf3) {
DISPCNT = 0x404;
FUN_08000dd0(&DAT_02008aac,0x6000000,&DAT_030000dc);
FUN_08000354(&DAT_030000dc,0x3c);
uVar4 = DAT_030004d8;
}
}
else if (DAT_030000d4 < 8) {
DAT_030000d4 = DAT_030000d4 + 1;
FUN_08000864();
if (uVar1 == 0x10) {
DAT_030000d8 = DAT_030000d8 + 0x3a;
LAB_08001742:
local_2c = &DAT_0200ba0c;
}
else if (uVar1 < 0x11) {
if (uVar1 == 1) {
DAT_030000d8 = DAT_030000d8 + 3;
LAB_08001766:
local_2c = &DAT_0200b9f8;
}
else {
iVar5 = 0xe;
if (uVar1 != 2) {
LAB_0800168a:
iVar5 = 0;
}
DAT_030000d8 = iVar5 + DAT_030000d8;
if (uVar1 == 0x20) {
LAB_080016ea:
local_2c = &DAT_0200ba08;
}
else if (uVar1 < 0x21) {
if (uVar1 == 2) {
local_2c = &DAT_0200b9fc;
}
else {
if (uVar1 == 0x10) goto LAB_08001742;
if (uVar1 == 1) goto LAB_08001766;
}
}
else {
if (uVar1 == 0x80) goto LAB_08001754;
if (uVar1 < 0x81) {
if (uVar1 == 0x40) goto LAB_08001778;
}
else if (uVar1 == 0x100) {
local_2c = &DAT_0200ba04;
}
else if (uVar1 == 0x200) {
local_2c = &DAT_0200ba00;
}
}
}
}
else if (uVar1 == 0x40) {
DAT_030000d8 = DAT_030000d8 + 0x28;
LAB_08001778:
local_2c = &DAT_0200ba10;
}
else {
if (uVar1 != 0x80) {
if (uVar1 != 0x20) goto LAB_0800168a;
DAT_030000d8 = DAT_030000d8 + 0x6e;
goto LAB_080016ea;
}
DAT_030000d8 = DAT_030000d8 + 0xc;
LAB_08001754:
local_2c = &DAT_0200ba14;
}
uVar2 = FUN_08001bc4(DAT_030004dc,local_2c);
DAT_05000000 = 0x1483;
DAT_030004dc = uVar2;
FUN_08001844(&DAT_0200ba18);
FUN_08001844(&DAT_0200ba20,uVar2);
uVar4 = DAT_030004d8;
}
}
puVar6 = puVar6 + 1;
} while (puVar6 != (ushort *)&UNK_0200b050);
} while( true );
}
4.分析一下代码
do {
DAT_030004da = uVar4;
DAT_030004d8 = KEYINPUT | 0xfc00;这是一个无限循环
DAT_030004da = uVar4;:保存上一帧的按键状态。
DAT_030004d8 = KEYINPUT | 0xfc00;:
KEYINPUT是 GBA 按键寄存器。
| 0xfc00:屏蔽高 6 位,只保留低 10 位按键(A, B, L, R, Start, Select, 上下左右)。
结果存入DAT_030004d8表示当前按键状态。
puVar6 = &DAT_0200b03c;
uVar4 = DAT_030004d8;
do {
uVar2 = DAT_030004dc;
uVar1 = *puVar6;puVar6指向按键掩码数组:
每个元素对应一个按键的 bit 掩码。
| 按键 | bit 位 | 掩码(十六进制) | 掩码(十进制) |
|---|---|---|---|
| A | 0 | 0x0001 | 1 |
| B | 1 | 0x0002 | 2 |
| Select | 2 | 0x0004 | 4 |
| Start | 3 | 0x0008 | 8 |
| Right | 4 | 0x0010 | 16 |
| Left | 5 | 0x0020 | 32 |
| Up | 6 | 0x0040 | 64 |
| Down | 7 | 0x0080 | 128 |
| R | 8 | 0x0100 | 256 |
| L | 9 | 0x0200 | 512 |
uVar2 = DAT_030004dc:保存当前游戏状态,供函数调用使用。
uVar1 = *puVar6:当前遍历的按键掩码。
if ((uVar1 & DAT_030004da & ~uVar4) != 0)
这里是防止按键持续按下多次触发
if (uVar1 == 4) {
DAT_030000d4 = 0;
uVar3 = FUN_08001c24(DAT_030004dc);
FUN_08001868(uVar2,0,uVar3);
DAT_05000000 = 0x1483;
FUN_08001844(&DAT_0200ba18);
FUN_08001844(&DAT_0200ba20,&DAT_0200ba40);
DAT_030000d8 = 0;
uVar4 = DAT_030004d8;
}select键重置
else if (uVar1 == 8) {
if (DAT_030000d8 == 0xf3) {
DISPCNT = 0x404;
FUN_08000dd0(&DAT_02008aac,0x6000000,&DAT_030000dc);
FUN_08000354(&DAT_030000dc,0x3c);
uVar4 = DAT_030004d8;
}
}start键
当DAT_030000d8 == 0xf3时会刷新显示模式,可能是通关条件
else if (DAT_030000d4 < 8) {
DAT_030000d4 = DAT_030000d4 + 1;
FUN_08000864();这里限制了按键的次数小于8
if (uVar1 == 0x10) {
DAT_030000d8 = DAT_030000d8 + 0x3a;
LAB_08001742:
local_2c = &DAT_0200ba0c;
}→按键会使DAT_030000d8增加0x3a
后面省略一些于是我们可以得到
| 按键掩码 | 增加值 |
|---|---|
| 0x10 | +0x3a |
| 1 | +3 |
| 2 | +0xe |
| 0x20 | +0x6e |
| 0x40 | +0x28 |
| 0x80 | +0xc |
| 0x100 | +0 |
| 0x200 | +0 |
5.最后我们可以得到这样一张表
| 按键掩码 | 按键 | DAT_030000d8 增加值 |
|---|---|---|
| 1 | A | +3 |
| 2 | B | +0x0e (14) |
| 4 | Select | 0(无增加值,重置) |
| 8 | Start | 条件触发(0xf3时触发其他动作,其他情况无增加值) |
| 0x10 (16) | Right | +0x3a (58) |
| 0x20 (32) | Left | +0x6e (110) |
| 0x40 (64) | Up | +0x28 (40) |
| 0x80 (128) | Down | +0x0c (12) |
| 0x100 (256) | R | +0(未修改) |
| 0x200 (512) | L | +0(未修改) |
结合上面DAT_030000d8== 0xf3(243)的条件
我们得到以下组合
| 组合序号 | 按键及次数 |
|---|---|
| 1 | A ×1, Up ×6 |
| 2 | A ×1, B ×1, Right ×2, Left ×1 |
| 3 | A ×1, B ×1, Down ×1, Up ×1, Right ×3 |
| 4 | A ×1, B ×1, Down ×3, Up ×2, Left ×1 |
| 5 | A ×3, B ×1, Left ×2 |
| 6 | A ×3, B ×1, Down ×1, Up ×1, Right ×1, Left ×1 |
6.在mGBA->工具->设置->键盘可以看到对应按键

7.敲下其中一种组合

按下回车得到flag

已在FreeBuf发表 0 篇文章
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf
客服小蜜蜂(微信:freebee1024)



