山石网科
- 关注
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

not-so-advanced writeup
最终解答队伍数60 / 总得分队伍数470
这道题目放出的时候,看见后缀为gba文件。
先使用file 命令查看
之前没有接触过对应文件格式的内容,使用https://fileinfo.com/extension/gba
查看后得知
GBA文件是任天堂Game Boy Advance(GBA)视频游戏(如神奇宝贝、塞尔达或最终幻想)的ROM镜像。它包含一个GBA游 戏的文件,保存的格式可以使用game Boy模拟器玩。
一个任天堂gameboy游戏 ,因此下载对应的VisualBoy Advance模拟器,这里我使用的是http://www.mgba.io/downloads.html使用模拟器运行,界面很简单,只是输出了Read the code,should be easy
使用键盘上的上下按键能够更改字母,发现都是a-z小写字母, 回车键为确认,会输出信息
通过运行程序大致了解到程序会检查输入的值,与对应的值比对,来显示出不同的提示信息。
IDA 载入能够看到一些字符串
但代码段无法进行反编译,
尝试使用Ghidra进行逆向,找到了关于GBA的插件,https://github.com/SiD3W4y/GhidraGBA
这里踩了一个坑,本机上使用的Ghidra版本是最新版,插件版本为10.1.导致Ghidra无法加载下载的插件,因此重新下载了10.1版本的Ghidra ,花了一些时间。 下载对应版本的Ghidra就可以直接载入插件了,就能对gba文件分析了。
插件放入的具体位置
在Ghidra中新建项目 ,就能识别出来了
直接看entry
能看到字符串
"abcdefghijklmnopqrstuvwxyz_"
这个可能会作为最后解题所需的内容。
再根据界面上显示出来的值 进行关键信息定位
交叉引用
很容易就找到了程序对应的源码以及伪代码
很明显 FUN_08000568 就是main函数
FUN_08000e74就是printf函数,main函数代码如下
longlong main(void) { int iVar1; uint in_lr; undefined auStack268 [256]; undefined uStack12; FUN_080009cc(); printf("Read the code, should be easy\n"); uStack12 = 0; FUN_08000340(auStack268); printf("Checking it out\n"); iVar1 = FUN_08000298(auStack268); if(iVar1 == 0x12e1) { printf("That works"); } else{ printf(&DAT_08005ce4); } return(ulonglong)in_lr << 0x20; }
对应的汇编代码为
************************************************************** * * * FUNCTION * ************************************************************** undefined main() assume LRset = 0x0 assume TMode = 0x1 undefined r0:1 <RETURN> main 08000568 80 b5 push { r7, lr } 0800056a c2 b0 sub sp,#0x108 0800056c 00 af add r7,sp,#0x0 0800056e 00 f0 2d fa bl FUN_080009cc undefined FUN_080009cc() 08000572 16 4b ldr r3=>s_Read_the_code,_should_be_easy_08005ca4,[ = "Read the code, should be easy = 08005ca4 08000574 18 00 mov r0=>s_Read_the_code,_should_be_easy_08005ca4,r3 = "Read the code, should be easy 08000576 00 f0 7d fc bl printf undefined printf() 0800057a 15 4b ldr r3,[DAT_080005d0] = FFFFFEFCh 0800057c 84 22 mov r2,#0x84 0800057e 52 00 lsl r2,r2,#0x1 08000580 9b 18 add r3,r3,r2 08000582 da 19 add r2,r3,r7 08000584 80 23 mov r3,#0x80 08000586 5b 00 lsl r3,r3,#0x1 08000588 00 21 mov r1,#0x0 0800058a d1 54 strb r1,[r2,r3] 0800058c 3b 1d add r3,r7,#0x4 0800058e 18 00 mov r0,r3 08000590 ff f7 d6 fe bl FUN_08000340 undefined FUN_08000340() 08000594 0f 4b ldr r3=>s_Checking_it_out_08005cc4,[PTR_s_Checking = 08005cc4 = "Checking it out\n" 08000596 18 00 mov r0=>s_Checking_it_out_08005cc4,r3 = "Checking it out\n" 08000598 00 f0 6cfc bl printf undefined printf() 0800059c 3b 1d add r3,r7,#0x4 0800059e 18 00 mov r0,r3 080005a0 ff f7 7afe bl FUN_08000298 undefined FUN_08000298() 080005a4 03 00 mov r3,r0 080005a6 0c 4a ldr r2,[DAT_080005d8] = 000012E1h 080005a8 93 42 cmp r3,r2 080005aa 04 d1 bne LAB_080005b6 080005ac 0b 4b ldr r3=>s_That_works_08005cd8,[PTR_s_That_works_08 = 08005cd8 = "That works" 080005ae 18 00 mov r0=>s_That_works_08005cd8,r3 = "That works" 080005b0 00 f0 60 fc bl printf undefined printf() 080005b4 03 e0 b LAB_080005be LAB_080005b6 XREF[1]: 080005aa(j) 080005b6 0a 4b ldr r3=>DAT_08005ce4,[PTR_DAT_080005e0] = 08005ce4 = 4Eh N 080005b8 18 00 mov r0=>DAT_08005ce4,r3 = 4Eh N 080005ba 00 f0 5bfc bl printf undefined printf() LAB_080005be XREF[1]: 080005b4(j) 080005be 00 23 mov r3,#0x0 080005c0 18 00 mov r0,r3 080005c2 bd 46 mov sp,r7 080005c4 42 b0 add sp,#0x108 080005c6 80 bc pop { r7 } 080005c8 02 bc pop { r1 } 080005ca 08 47 bx r1 -- Flow Override: RETURN (TERMINATOR)
通过分析以上代码分析,main函数功能很少 ,只是获取输入并检查,
iVar1 = FUN_08000298(auStack268);
如果通过函数FUN_08000298的值处理后的值iVar1的值等于0x12E1,那么就可以通过程序,输出That works。
而不是我们随便输入的值 ,输出DAT_08005ce4中的值(Nope)
输入点只能小写字母,确认后进行验证,看代码还要满足值等于 0x12e1 ,否则会输入nope
现在就要着重分析函数FUN_08000298,其伪代码为
undefined8 FUN_08000298(int param_1) { int iVar1; ushort extraout_r1; ushort extraout_r1_00; uint uVar2; undefined4 in_lr; uint local_18; ushort local_14; ushort local_12; iVar1 = FUN_08001148(param_1); local_12 = 1; local_14 = 0; if(iVar1 == 9) { for(local_18 = 0; local_18 < 9; local_18 = local_18 + 1) { thunk_FUN_08000cc8((uint)local_12 + (uint)*(byte *)(param_1 + local_18),0xfff1); thunk_FUN_08000cc8((uint)local_14 + (uint)extraout_r1,0xfff1); local_14 = extraout_r1_00; local_12 = extraout_r1; } uVar2 = (uint)(local_14 ^ local_12); } else{ uVar2 = 0xffffffff; } returnCONCAT44(in_lr,uVar2); }
从以上代码可以从循环中分辨出输入长度必须为9,
进入函数thunk_FUN_08000cc8,
其调用了fun_08000ba0,继续看fun_08000ba0伪代码
uint FUN_08000ba0(uint param_1,uint param_2) { uint uVar1; uint uVar2; int iVar3; uint uVar4; uint uVar5; uint uVar6; bool in_NG; bool bVar7; uVar6 = param_1 ^ param_2; uVar4 = param_2; if(in_NG) { uVar4 = -param_2; } if(uVar4 - 1 == 0) { if((int)param_2 < 0) { param_1 = -param_1; } returnparam_1; } uVar5 = param_1; if((int)param_1 < 0) { uVar5 = -param_1; } if(uVar5 <= uVar4) { if(uVar5 < uVar4) { param_1 = 0; } if(uVar5 == uVar4) { param_1 = (int)uVar6 >> 0x1f | 1; } returnparam_1; } if((uVar4 & uVar4 - 1) == 0) { if(uVar4 < 0x10000) { iVar3 = 0; } else{ uVar4 = uVar4 >> 0x10; iVar3 = 0x10; } if(0xff < uVar4) { uVar4 = uVar4 >> 8; iVar3 = iVar3 + 8; } if(0xf < uVar4) { uVar4 = uVar4 >> 4; iVar3 = iVar3 + 4; } if(uVar4 < 5) { uVar4 = iVar3 + (uVar4 >> 1); } else{ uVar4 = iVar3 + 3; } uVar5 = uVar5 >> (uVar4 & 0xff); if((int)uVar6 < 0) { uVar5 = -uVar5; } returnuVar5; } if((uVar4 & 0xe0000000) == 0) { uVar4 = uVar4 << 3; uVar2 = 8; } else{ uVar2 = 1; } for(; uVar4 < 0x10000000 && uVar4 < uVar5; uVar4 = uVar4 << 4) { uVar2 = uVar2 << 4; } for(; uVar4 < 0x80000000 && uVar4 < uVar5; uVar4 = uVar4 << 1) { uVar2 = uVar2 << 1; } uVar1 = 0; while( true ) { if(uVar4 <= uVar5) { uVar5 = uVar5 - uVar4; uVar1 = uVar1 | uVar2; } if(uVar4 >> 1 <= uVar5) { uVar5 = uVar5 - (uVar4 >> 1); uVar1 = uVar1 | uVar2 >> 1; } if(uVar4 >> 2 <= uVar5) { uVar5 = uVar5 - (uVar4 >> 2); uVar1 = uVar1 | uVar2 >> 2; } if(uVar4 >> 3 <= uVar5) { uVar5 = uVar5 - (uVar4 >> 3); uVar1 = uVar1 | uVar2 >> 3; } bVar7 = uVar5 == 0; if(!bVar7) { uVar2 = uVar2 >> 4; bVar7 = uVar2 == 0; } if(bVar7) break; uVar4 = uVar4 >> 4; } if((int)uVar6 < 0) { uVar1 = -uVar1; } returnuVar1; }
很复杂,进行了大量的变量的处理,其实关注这两行代码就可以了
thunk_FUN_08000cc8((uint)local_12 + (uint)*(byte *)(param_1 + local_18),0xfff1); thunk_FUN_08000cc8((uint)local_14 + (uint)extraout_r1,0xfff1);
param_1这里是输入的值
通过分析以上代码,输入长度为9个字符。
那么最开始的 Abcdefghijklmnopqrstuvwxyz_ 要作为字符串输入。
且最终返回值为0x12E1 因此暴力破解写算法
成功。所以最后的flag为 aaaaaanzb
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)