攻防世界 Mine- IDA静调或x64dbg动调 两种方式
刷攻防世界的最后一道二星题,记录一下,大佬请飘过
题目
分析过程
我很勇,我先双击看看这是什么
是一个扫雷游戏,第一下运气好没踩雷,发现无法继续输入了;如果运气不好,会输出“您踩雷啦!”
丢到PE里面
无壳,64位
丢到IDA里面,shif+F12查看字符串,追踪可疑字符串,发现main函数
1 int __cdecl main(int argc, const char **argv, const char **envp) 2 { 3 unsigned int v3; // eax 4 std::ostream *v4; // rax 5 __int64 v5; // rax 6 _BYTE *v6; // rax 7 std::istream *v7; // rax 8 __int64 v8; // rax 9 std::ostream *v9; // rax 10 std::ostream *v10; // rax 11 __int64 v11; // rax 12 int v13; // [rsp+28h] [rbp-58h] 13 int v14; // [rsp+2Ch] [rbp-54h] 14 int v15; // [rsp+30h] [rbp-50h] 15 int v16; // [rsp+34h] [rbp-4Ch] 16 int v17; // [rsp+38h] [rbp-48h] 17 int v18; // [rsp+3Ch] [rbp-44h] 18 int v19; // [rsp+40h] [rbp-40h] 19 int i1; // [rsp+44h] [rbp-3Ch] 20 int nn; // [rsp+48h] [rbp-38h] 21 int mm; // [rsp+4Ch] [rbp-34h] 22 int ll; // [rsp+50h] [rbp-30h] 23 int i2; // [rsp+54h] [rbp-2Ch] 24 int kk; // [rsp+58h] [rbp-28h] 25 int jj; // [rsp+5Ch] [rbp-24h] 26 int ii; // [rsp+60h] [rbp-20h] 27 int n; // [rsp+64h] [rbp-1Ch] 28 int m; // [rsp+68h] [rbp-18h] 29 int l; // [rsp+6Ch] [rbp-14h] 30 int k; // [rsp+70h] [rbp-10h] 31 int v32; // [rsp+74h] [rbp-Ch] 32 int j; // [rsp+78h] [rbp-8h] 33 int i; // [rsp+7Ch] [rbp-4h] 34 35 _main(); 36 memset(grid, 0, 0x190ui64); 37 memset(randMark, 0, 0x9C40ui64); 38 memset(vis, 0, sizeof(vis)); 39 for ( i = 0; i <= 9; ++i ) // 第一个循环------------------------------------------------------------------------------- 40 { 41 for ( j = 0; j <= 9; ++j ) 42 showUs[100 * i + j] = 42; 43 } 44 v3 = time(0i64); 45 srand(v3); 46 v32 = 0; 47 do 48 { 49 v19 = rand() % 10; // 随机一个0~9 50 v18 = rand() % 10; 51 if ( randMark[100 * v19 + v18] != 1 ) 52 { 53 randMark[100 * v19 + v18] = 1; // v32不到mine_sum,就一直循环 54 // 我怀疑这里是布雷 55 // mine_sum是雷的数量 56 ++v32; 57 } 58 } 59 while ( v32 != mine_sum ); 60 res = 0; 61 for ( k = 0; k <= 9; ++k ) // 第二个循环------------------------------------------------------------------------------------------- 62 { 63 for ( l = 0; l <= 9; ++l ) 64 { 65 if ( randMark[100 * k + l] ) 66 grid[10 * k + l] = -1; 67 } 68 } 69 for ( m = 0; m <= 9; ++m ) // 第三个循环----------------------------------------------------------------------------------------------------- 70 { 71 for ( n = 0; n <= 9; ++n ) 72 { 73 if ( grid[10 * m + n] != -1 ) 74 { 75 for ( ii = 0; ii <= 7; ++ii ) 76 { 77 v17 = *((_DWORD *)&dir + 2 * ii) + m; 78 v16 = dword_475044[2 * ii] + n; 79 if ( v17 >= 0 && v17 <= 9 && v16 >= 0 && v16 <= 9 && grid[10 * v17 + v16] == -1 ) 80 ++grid[10 * m + n]; 81 } 82 } 83 } 84 } 85 for ( jj = 0; jj <= 9; ++jj ) // 第四个循环-------------------------------------------------------------------------------------------------------- 86 { 87 for ( kk = 0; kk <= 9; ++kk ) 88 { 89 v4 = (std::ostream *)std::operator<<<std::char_traits<char>>( 90 refptr__ZSt4cout, 91 (unsigned int)showUs[100 * jj + kk]); 92 std::operator<<<std::char_traits<char>>(v4, " "); 93 } 94 std::ostream::operator<<(refptr__ZSt4cout, refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_); 95 } 96 LABEL_42: 97 v5 = std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, asc_48B002);// 请输入要翻开的位置的坐标 98 std::ostream::operator<<(v5, refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_); 99 while ( 100 - mine_sum != res ) // res是没有雷的地方 100 { 101 v7 = (std::istream *)std::istream::operator>>(refptr__ZSt3cin); 102 std::istream::operator>>(v7); 103 if ( grid[10 * v14 + v13] == -1 ) // 第一个if---检查是否踩雷 104 { 105 v8 = std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, asc_48B01D);// 您中雷啦 106 std::ostream::operator<<(v8, refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_); 107 goto LABEL_69; // pause 108 } 109 if ( !vis[100 * v14 + v13] && grid[10 * v14 + v13] > 0 )// 第二个if--- 110 { 111 ++res; 112 vis[100 * v14 + v13] = 1; 113 showUs[100 * v14 + v13] = LOBYTE(grid[10 * v14 + v13]) + 48; 114 system("cls"); 115 for ( ll = 0; ll <= 9; ++ll ) 116 { 117 for ( mm = 0; mm <= 9; ++mm ) 118 { 119 v9 = (std::ostream *)std::operator<<<std::char_traits<char>>( 120 refptr__ZSt4cout, 121 (unsigned int)showUs[100 * ll + mm]); 122 std::operator<<<std::char_traits<char>>(v9, " "); 123 } 124 std::ostream::operator<<(refptr__ZSt4cout, refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_); 125 } 126 goto LABEL_42; 127 } 128 if ( !vis[100 * v14 + v13] && !grid[10 * v14 + v13] )// 第三个if--- 129 { 130 bfs(v14, v13); 131 system("cls"); 132 for ( nn = 0; nn <= 9; ++nn ) 133 { 134 for ( i1 = 0; i1 <= 9; ++i1 ) 135 { 136 v10 = (std::ostream *)std::operator<<<std::char_traits<char>>( 137 refptr__ZSt4cout, 138 (unsigned int)showUs[100 * nn + i1]); 139 std::operator<<<std::char_traits<char>>(v10, " "); 140 } 141 std::ostream::operator<<(refptr__ZSt4cout, refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_); 142 } 143 v11 = std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, asc_48B002);// 请输入要翻开的位置的坐标 144 std::ostream::operator<<(v11, refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_); 145 } 146 } // while结束的地方 147 v15 = std::string::length((std::string *)&ans); 148 for ( i2 = 0; i2 < v15; ++i2 ) 149 { 150 v6 = (_BYTE *)std::string::operator[](&ans, i2); 151 std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, (unsigned int)(char)((v15 - i2) ^ *v6));// 异或操作!!!!注意!!!! 152 } 153 std::ostream::operator<<(refptr__ZSt4cout, refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_); 154 LABEL_69: 155 system("pause"); 156 return 0; 157 }
一堆循环与if,粗略看看,147行之前都没发现什么表明游戏通关的字符串或者跳转函数
一直到147行,突然出现一个ans,上来直接就是将它的长度赋给v15,做题有点经验的小伙伴应该会发现,一般能够单开一行赋长度的参数,很有可能是关键之一
特别是下面还有一个异或操作,简直就是重点怀疑对象
动态调试(X64dbg)
输出ans紧接着的是一个循环,我们看看地址与汇编
x64dbg的基址与IDA是一样的,不用改了——需要改地址的可以看看我之前的博客攻防世界 gametime 使用IDA pro+OD动调 - demo41 - 博客园 (cnblogs.com)
既然不用改地址,我们直接记下jmz的地址 0000000000401ED2 ,在x64dbg中跳转到
将jnz改成jz,运行直到出现字符串
输出结果: 7ii3VecVgof3r6ssiP2g7E3HqwqhM
直接拿去提交了,出错了,那应该是加密了
我试了很多,一直试不出来,还以为自己错了
后面看了wp,我解题没错,这是一个标准base58
大部分base58解密网站的字符序都是BTC-base58序列,顺序为123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
标准base58的解密序列是123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ
这里提供一个解密网站:Base58编码/解码 - 一个工具箱 - 好用的在线工具都在这里! (atoolbox.net)
静态调试(IDA+py脚本)
我之前查看字符串的时候,还有追踪asc_48B002和asc_48B01D的时候,都发现一个特别可疑的字符串
追进去看看
1 int __fastcall __static_initialization_and_destruction_0(int a1, int a2) 2 { 3 int result; // eax 4 char v3[17]; // [rsp+2Fh] [rbp-51h] BYREF 5 6 if ( a1 == 1 && a2 == 0xFFFF ) 7 { 8 std::ios_base::Init::Init((std::ios_base::Init *)&std::__ioinit); 9 atexit(_tcf_0); 10 std::allocator<char>::allocator(v3); 11 std::string::string(&ans, "*ur)O}t@r{u!c&|}d\\9m>M4NtsrjL", v3);// 放进去吗? 12 std::allocator<char>::~allocator(v3); 13 result = atexit(_tcf_1); 14 } 15 return result; 16 }
竟然和ans联系在一起了,花指令我没查具体意思,七分逆向三分猜,我感觉就是把字符串放进ans里面,反正异或操作不难,脚本很容易写,试试再说
py脚本
1 ans="*ur)O}t@r{u!c&|}d\\9m>M4NtsrjL" 2 v15=len(ans) 3 str="" 4 5 for i in range(len(ans)): 6 str+=chr((v15-i)^ord(ans[i])) 7 8 print(str)
输出结果: 7ii3VecVgof3r6ssiP2g7E3HqwqhM
直接拿去提交了,出错了,那应该是加密了
我试了很多,一直试不出来,还以为自己错了
后面看了wp,我解题没错,这是一个标准base58
大部分base58解密网站的字符序都是BTC-base58序列,顺序为123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
标准base58的解密序列是123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ
这里提供一个解密网站:Base58编码/解码 - 一个工具箱 - 好用的在线工具都在这里! (atoolbox.net)
flag
flag{h4pp4-M1n3-G4m3}