main函数如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| int __cdecl main(int argc, const char **argv, const char **envp) { __int64 v3; // rax __int64 v4; // rax const CHAR *v5; // r11 __int64 v6; // r10 int v7; // er9 const CHAR *v8; // r10 __int64 v9; // rcx __int64 *v10; // rax unsigned int v12; // ecx __int64 v13; // r9 __int64 v14; // r8 __int64 v15; // rdx __int128 v16[2]; // [rsp+20h] [rbp-38h] BYREF
memset(v16, 0, sizeof(v16)); sub_7FF6BF3D1080("%s", (const char *)v16); // 输入字符串存入v14 v3 = -1i64; // v3初始值为-1 do ++v3; while ( *((_BYTE *)v16 + v3) ); // 0123456789qwertyuiopasdfghjklzx if ( v3 != 31 ) // 限制输入字符串长度为31 { while ( 1 ) Sleep(0x3E8u); } v4 = sub_7FF6BF3D1280(v16); v5 = name; if ( v4 ) { sub_7FF6BF3D15C0(*(_QWORD *)(v4 + 8)); sub_7FF6BF3D15C0(*(_QWORD *)(v6 + 16)); v7 = dword_7FF6BF3D57E0; v5[dword_7FF6BF3D57E0] = *v8; dword_7FF6BF3D57E0 = v7 + 1; } UnDecorateSymbolName(v5, outputString, 0x100u, 0); v9 = -1i64; do ++v9; while ( outputString[v9] ); if ( v9 == 62 ) { v12 = 0; v13 = 0i64; do { v14 = outputString[v13] % 23; if ( a1234567890Qwer[v14] != a46200860044218[v13] ) _exit(v12); v15 = outputString[v13] / 23; if ( a1234567890Qwer[v15] != a55565653255552[v13] ) _exit(v12 * v12); ++v12; ++v13; } while ( v12 < 62 ); sub_7FF6BF3D1020("flag{MD5(your input)}\n", v15, v14, v13); return 0; } else { v10 = sub_7FF6BF3D18A0(std::cout); std::ostream::operator<<(v10, sub_7FF6BF3D1A60); return -1; } }
|
首先求出outputString
1 2 3 4 5 6 7 8 9
| str1 = '1234567890-=!@#$%^&*()_+qwertyuiop[]QWERTYUIOP{}asdfghjkl;'+ chr(0x27) + 'A' + 'SDFGHJKL:"ZXCVBNM<>?zxcvbnm,./' str2 = '(_@4620!08!6_0*0442!@186%%0@3=66!!974*3234=&0^3&1@=&0908!6_0*&' str3 = '55565653255552225565565555243466334653663544426565555525555222' input = '' for i in range(62): m = str1.index(str2[i]) n = str1.index(str3[i]) input += chr((n*23+m)%128) print(input)
|
运行结果为一个函数名private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
关键函数为UnDecorateSymbolName,功能为让函数更容易识别
方法一
参考链接:
C++ 编译器的函数名修饰规则 - yxysuanfa - 博客园 (cnblogs.com)
- 参考资料1
第二个参数为未修饰的名字,第三个参数为长度,第四个参数为0表示完全修饰,第一个参数为输出地址
- 参考材料2
c++函数名的修饰更为复杂,提供的信息也更为丰富。
无论 __cdecl,__fastcall还是__stdcall调用方式,函数修饰都是以一个“?”开始,后面紧跟函数的名字。再后面是参数表的开始标识和依照参数类型代号拼出的参数表。
v5 = ?My_Aut0_PWN
- 对于C++的类成员函数(其调用方式是thiscall),函数的名字修饰与非成员的C++函数稍有不同,首先就是在函数名字和参数表之间插入以“@”字 符引导的类名
v5 = ?My_Aut0_PWN@R0Pxx
- 其次是参数表的开始标识不同,公有(public)成员函数的标识是“@@QAE”,保护(protected)成员函数的标识是 “@@IAE”,私有(private)成员函数的标识是“@@AAE”,假设函数声明使用了constkeyword,则对应的标识应分别为“@@QBE”,“@@IBE”和“@@ABE”。
因为函数为 private,私有成员
所以 v5 = ?My_Aut0_PWN@R0Pxx@@AAE
后面就是添加参数了,先加入函数返回值参数,函数的返回值类型为char *
- 参数表的拼写代号如下:
X–void
D–char
E–unsigned char
F–short
H–int
I–unsigned int
J–long
K–unsigned long(DWORD)
M–float
N–double
_N–bool
U–struct
…
指针的方式有些特别。用PA表示指针,用PB表示const类型的指针。
char *也就是PAD
所以 v5 = ?My_Aut0_PWN@R0Pxx@@AAEPAD
然后是参数的类型 unsigned char *,也就是PAE
所以 v5 = ?My_Aut0_PWN@R0Pxx@@AAEPADPAE
- 参数表后以“@Z”标识整个名字的结束。假设该函数无参数,则以“Z”标识结束。
所以最终v5 = ?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z
方法二
另外一种方法参考 Hk_Mayfly 师傅的博客:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #include <iostream>
class R0Pxx { public: R0Pxx() { My_Aut0_PWN((unsigned char*)"hello"); } private: char* __thiscall My_Aut0_PWN(unsigned char*); };
char* __thiscall R0Pxx::My_Aut0_PWN(unsigned char*) { std::cout << __FUNCDNAME__ << std::endl;
return 0; }
int main() { R0Pxx A;
system("PAUSE"); return 0; }
|
接下来看上面的置换操作
可以采用动态调试打表的方法
输入串0123456789qwertyuiopasdfghjklzx
查看输出
即可求得flag
1 2 3 4 5 6 7 8
| import hashlib str1 = '0123456789qwertyuiopasdfghjklzx' decoded_str1 = 'yu7io83pa9sdq41fgwhje5klrzxt620' enc = '' str2 = '?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z' for i in range(len(str1)): enc += str2[decoded_str1.index(str1[i])] print('flag{' + hashlib.md5(enc.encode("utf-8")).hexdigest() + '}')
|