【CTF Reverse】XCTF GFSJ1101 Mine- Writeup(反编译+动态调试+Base58编码)
Mine-
运气怎么这么差?
原理
Base58
Base58是用于比特币(Bitcoin)中使用的一种独特的编码方式,主要用于产生Bitcoin的钱包地址。
相比Base64,Base58不使用数字"0",字母大写"O",字母大写"I",和字母小写"l",以及"+“和”/"符号。
比特币的Base58字母表:
123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
工具
- Base58编码/解码:http://www.atoolbox.net/Tool.php?Id=932
- Base58在线编码、Base58解码、在线Base58check编码、Base58check解码:http://web.chacuo.net/charsetbase58
解法
一个扫雷游戏,输入坐标翻开,坐标用空格隔开。
没走两步就爆了。菜!
拉进 DIE 分析。无壳。
导入 IDA。
按 F5 反编译。
int __fastcall main(int argc, const char **argv, const char **envp)
{unsigned int v3; // eaxstd::ostream *v4; // raxstd::ostream *v5; // rax_BYTE *v6; // raxstd::istream *v7; // raxstd::ostream *v8; // raxstd::ostream *v9; // raxstd::ostream *v10; // raxstd::ostream *v11; // raxint v13; // [rsp+28h] [rbp-58h]int v14; // [rsp+2Ch] [rbp-54h]int v15; // [rsp+30h] [rbp-50h]unsigned int v16; // [rsp+34h] [rbp-4Ch]unsigned int v17; // [rsp+38h] [rbp-48h]int v18; // [rsp+3Ch] [rbp-44h]int v19; // [rsp+40h] [rbp-40h]int i3; // [rsp+44h] [rbp-3Ch]int i2; // [rsp+48h] [rbp-38h]int i1; // [rsp+4Ch] [rbp-34h]int nn; // [rsp+50h] [rbp-30h]int i4; // [rsp+54h] [rbp-2Ch]int mm; // [rsp+58h] [rbp-28h]int kk; // [rsp+5Ch] [rbp-24h]int jj; // [rsp+60h] [rbp-20h]int ii; // [rsp+64h] [rbp-1Ch]int n; // [rsp+68h] [rbp-18h]int m; // [rsp+6Ch] [rbp-14h]int k; // [rsp+70h] [rbp-10h]int v32; // [rsp+74h] [rbp-Ch]int j; // [rsp+78h] [rbp-8h]int i; // [rsp+7Ch] [rbp-4h]_main();memset(grid, 0, 0x190ui64);memset(randMark, 0, sizeof(randMark));memset(vis, 0, sizeof(vis));for ( i = 0; i <= 9; ++i ){for ( j = 0; j <= 9; ++j )showUs[100 * i + j] = 42;}v3 = time(0i64);srand(v3);v32 = 0;do{v19 = rand() % 10;v18 = rand() % 10;if ( randMark[100 * v19 + v18] != 1 ){randMark[100 * v19 + v18] = 1;++v32;}}while ( v32 != mine_sum );res = 0;for ( k = 0; k <= 9; ++k ){for ( m = 0; m <= 9; ++m ){if ( randMark[100 * k + m] )grid[10 * k + m] = -1;}}for ( n = 0; n <= 9; ++n ){for ( ii = 0; ii <= 9; ++ii ){if ( grid[10 * n + ii] != -1 ){for ( jj = 0; jj <= 7; ++jj ){v17 = *((_DWORD *)&dir + 2 * jj) + n;v16 = dword_475044[2 * jj] + ii;if ( v17 <= 9 && v16 <= 9 && grid[10 * v17 + v16] == -1 )++grid[10 * n + ii];}}}}for ( kk = 0; kk <= 9; ++kk ){for ( mm = 0; mm <= 9; ++mm ){v4 = (std::ostream *)std::operator<<<std::char_traits<char>>(refptr__ZSt4cout,(unsigned int)showUs[100 * kk + mm]);std::operator<<<std::char_traits<char>>(v4, " ");}refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(refptr__ZSt4cout);}
LABEL_40:v5 = (std::ostream *)std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, asc_48B002);refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(v5);while ( 100 - mine_sum != res ){v7 = (std::istream *)std::istream::operator>>(refptr__ZSt3cin);std::istream::operator>>(v7);if ( grid[10 * v14 + v13] == -1 ){v8 = (std::ostream *)std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, asc_48B01D);refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(v8);goto LABEL_67;}if ( !vis[100 * v14 + v13] && grid[10 * v14 + v13] > 0 ){++res;vis[100 * v14 + v13] = 1;showUs[100 * v14 + v13] = LOBYTE(grid[10 * v14 + v13]) + 48;system("cls");for ( nn = 0; nn <= 9; ++nn ){for ( i1 = 0; i1 <= 9; ++i1 ){v9 = (std::ostream *)std::operator<<<std::char_traits<char>>(refptr__ZSt4cout,(unsigned int)showUs[100 * nn + i1]);std::operator<<<std::char_traits<char>>(v9, " ");}refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(refptr__ZSt4cout);}goto LABEL_40;}if ( !vis[100 * v14 + v13] && !grid[10 * v14 + v13] ){bfs(v14, v13);system("cls");for ( i2 = 0; i2 <= 9; ++i2 ){for ( i3 = 0; i3 <= 9; ++i3 ){v10 = (std::ostream *)std::operator<<<std::char_traits<char>>(refptr__ZSt4cout,(unsigned int)showUs[100 * i2 + i3]);std::operator<<<std::char_traits<char>>(v10, " ");}refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(refptr__ZSt4cout);}v11 = (std::ostream *)std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, asc_48B002);refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(v11);}}v15 = std::string::length((std::string *)&ans);for ( i4 = 0; i4 < v15; ++i4 ){v6 = (_BYTE *)std::string::operator[](&ans, i4);std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, (unsigned int)(char)((v15 - i4) ^ *v6));}refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(refptr__ZSt4cout);
LABEL_67:system("pause");return 0;
}
没看到有字符串常量。
按 Shift + F12 打开 Strings 窗口,发现中文字符串。
追踪其位置。发现这个字符数组的标识符为 asc_48B002。
回到 main 函数,搜索 asc_48B002 找到这行。推测这段是 C++ 的 cout。
v11 = (std::ostream *)std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, asc_48B002);
这段找到变量 ans,可能是 flag。
v15 = std::string::length((std::string *)&ans);for ( i4 = 0; i4 < v15; ++i4 ){v6 = (_BYTE *)std::string::operator[](&ans, i4);std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, (unsigned int)(char)((v15 - i4) ^ *v6));}refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(refptr__ZSt4cout);
但是 ans 里面没有内容。
分析反编译伪代码可知,ans 在 while 循环结束后输出。找到 while 循环位置为 401ED2。
用 x64dbg 打开,Ctrl + G 跳转到 0000000000401ED2。
用 NOP 填充,跳出 while 循环。
运行程序,直接输出一段编码,应该是 ans。
7ii3VecVgof3r6ssiP2g7E3HqwqhM
提交 flag,错误。
可能是 base64 编码,用 base64 解码器解码,解码失败。
用 base58 解码器解码,解码失败。
看了大佬的 Writeup。因为有些解码器 base58 字母表的大写字母在小写字母前面:
123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
另一些解码器 base58 字母表的大写字母在小写字母后面:
123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ
ans 采用的是大写字母在前面的字母表。换了另一个解码器,解码成功,得到 flag。
Flag
flag{h4pp4-M1n3-G4m3}
PS
我的理解是,标题可能一语双关。根据有道词典的解释:
minepron.
我的;<英,非正式>我的家
n.
矿,矿井;地雷,水雷;宝库,源泉;<史>炸药坑道
v.
采(煤等矿物);布雷,用雷炸毁(车辆);寻找(某事物中)蕴含的价值;在(地下)挖洞(或坑道);挖掘(数据);挖矿(获取加密货币的勘探方式)
mine 有地雷的意思,指扫雷游戏,又有挖矿的意思,暗示了比特币钱包地址所使用的 base58 编码。
参考资料
- https://dict.youdao.com/result?word=mine&lang=en
声明
本博客上发布的所有关于网络攻防技术的文章,仅用于教育和研究目的。所有涉及到的实验操作都在虚拟机或者专门设计的靶机上进行,并且严格遵守了相关法律法规。
博主坚决反对任何形式的非法黑客行为,包括但不限于未经授权的访问、攻击或破坏他人的计算机系统。博主强烈建议每位读者在学习网络攻防技术时,必须遵守法律法规,不得用于任何非法目的。对于因使用这些技术而导致的任何后果,博主不承担任何责任。