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; // eax
std::ostream *v4; // rax
std::ostream *v5; // rax
_BYTE *v6; // rax
std::istream *v7; // rax
std::ostream *v8; // rax
std::ostream *v9; // rax
std::ostream *v10; // rax
std::ostream *v11; // rax
int 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
我的理解是,标题可能一语双关。根据有道词典的解释:
mine
pron.
我的;<英,非正式>我的家
n.
矿,矿井;地雷,水雷;宝库,源泉;<史>炸药坑道
v.
采(煤等矿物);布雷,用雷炸毁(车辆);寻找(某事物中)蕴含的价值;在(地下)挖洞(或坑道);挖掘(数据);挖矿(获取加密货币的勘探方式)
mine 有地雷的意思,指扫雷游戏,又有挖矿的意思,暗示了比特币钱包地址所使用的 base58 编码。
参考资料
- https://dict.youdao.com/result?word=mine&lang=en
声明
本博客上发布的所有关于网络攻防技术的文章,仅用于教育和研究目的。所有涉及到的实验操作都在虚拟机或者专门设计的靶机上进行,并且严格遵守了相关法律法规。
博主坚决反对任何形式的非法黑客行为,包括但不限于未经授权的访问、攻击或破坏他人的计算机系统。博主强烈建议每位读者在学习网络攻防技术时,必须遵守法律法规,不得用于任何非法目的。对于因使用这些技术而导致的任何后果,博主不承担任何责任。