【CTF Reverse】XCTF GFSJ1101 Mine- Writeup(反编译+动态调试+Base58编码)

Mine-

运气怎么这么差?


原理

Base58

Base58是用于比特币(Bitcoin)中使用的一种独特的编码方式,主要用于产生Bitcoin的钱包地址。

相比Base64,Base58不使用数字"0",字母大写"O",字母大写"I",和字母小写"l",以及"+“和”/"符号。

比特币的Base58字母表:

123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz

工具

解法

一个扫雷游戏,输入坐标翻开,坐标用空格隔开。

没走两步就爆了。

拉进 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 编码。

参考资料

声明

本博客上发布的所有关于网络攻防技术的文章,仅用于教育和研究目的。所有涉及到的实验操作都在虚拟机或者专门设计的靶机上进行,并且严格遵守了相关法律法规

博主坚决反对任何形式的非法黑客行为,包括但不限于未经授权的访问、攻击或破坏他人的计算机系统。博主强烈建议每位读者在学习网络攻防技术时,必须遵守法律法规不得用于任何非法目的。对于因使用这些技术而导致的任何后果,博主不承担任何责任

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值