
本文共 5601 字,大约阅读时间需要 18 分钟。
查壳
拖进ida
main函数
signed __int64 main(){ signed __int64 v0; // rax __int64 v1; // rax const CHAR *v2; // r11 __int64 v3; // r10 __int64 v4; // r9 const CHAR *v5; // r10 signed __int64 v6; // rcx __int64 v7; // rax signed __int64 result; // rax unsigned int v9; // ecx __int64 v10; // r9 int v11; // er10 __int64 v12; // r8 __int128 v13; // [rsp+20h] [rbp-38h] __int128 v14; // [rsp+30h] [rbp-28h] v13 = 0i64; v14 = 0i64; sub_140001080("%s", &v13); v0 = -1i64; do ++v0; while ( *((_BYTE *)&v13 + v0) ); if ( v0 != 31 ) { while ( 1 ) Sleep(0x3E8u); } v1 = sub_140001280(&v13); v2 = name; if ( v1 ) { sub_1400015C0(*(_QWORD *)(v1 + 8)); sub_1400015C0(*(_QWORD *)(v3 + 16)); v4 = dword_1400057E0; v2[v4] = *v5; dword_1400057E0 = v4 + 1; } UnDecorateSymbolName(v2, outputString, 0x100u, 0); v6 = -1i64; do ++v6; while ( outputString[v6] ); if ( v6 == 62 ) { v9 = 0; v10 = 0i64; do { v11 = outputString[v10]; v12 = v11 % 23; if ( a1234567890Qwer[v12] != *(_BYTE *)(v10 + 5368722552i64) ) _exit(v9); if ( a1234567890Qwer[v11 / 23] != *(_BYTE *)(v10 + 5368722488i64) ) _exit(v9 * v9); ++v9; ++v10; } while ( v9 < 0x3E ); sub_140001020("flag{MD5(your input)}\n", v11 / 23, v12, v10); result = 0i64; } else { v7 = sub_1400018A0(std::cout); std::basic_ostream>::operator<<(v7, sub_140001A60); result = 0xFFFFFFFFi64; } return result;}
分析
观察之后,这道题需要先看下面的,思路倒着回去:
sub_7FF6E48A1020("flag{MD5(your input)}\n", v11 / 23, v11 % 23, v10);
v11/23
和v11 % 23
根据这里可以算出,
v9 = 0; v10 = 0i64; do { v11 = outputString[v10]; if ( a1234567890__Qw[v11 % 23] != *(_BYTE *)(v10 + 140698372945016i64) ) _exit(v9); if ( a1234567890__Qw[v11 / 23] != *(_BYTE *)(v10 + 140698372944952i64) ) _exit(v9 * v9); ++v9; ++v10; }
字符串a1234567890__Qw
unsigned char a1234567890__Qw[] ={ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x5B, 0x5D, 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x7B, 0x7D, 0x61, 0x73, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B, 0x27, 0x41, 0x53, 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A, 0x22, 0x5A, 0x58, 0x43, 0x56, 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x7A, 0x78, 0x63, 0x76, 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00};
字符串v10 + 140698372945016i64
unsigned char ida_chars[] ={ 0x35, 0x35, 0x35, 0x36, 0x35, 0x36, 0x35, 0x33, 0x32, 0x35, 0x35, 0x35, 0x35, 0x32, 0x32, 0x32, 0x35, 0x35, 0x36, 0x35, 0x35, 0x36, 0x35, 0x35, 0x35, 0x35, 0x32, 0x34, 0x33, 0x34, 0x36, 0x36, 0x33, 0x33, 0x34, 0x36, 0x35, 0x33, 0x36, 0x36, 0x33, 0x35, 0x34, 0x34, 0x34, 0x32, 0x36, 0x35, 0x36, 0x35, 0x35, 0x35, 0x35, 0x35, 0x32, 0x35, 0x35, 0x35, 0x35, 0x32, 0x32, 0x32, 0x00};
字符串v10 + 140698372944952i64
unsigned char ida_chars[] ={ 0x28, 0x5F, 0x40, 0x34, 0x36, 0x32, 0x30, 0x21, 0x30, 0x38, 0x21, 0x36, 0x5F, 0x30, 0x2A, 0x30, 0x34, 0x34, 0x32, 0x21, 0x40, 0x31, 0x38, 0x36, 0x25, 0x25, 0x30, 0x40, 0x33, 0x3D, 0x36, 0x36, 0x21, 0x21, 0x39, 0x37, 0x34, 0x2A, 0x33, 0x32, 0x33, 0x34, 0x3D, 0x26, 0x30, 0x5E, 0x33, 0x26, 0x31, 0x40, 0x3D, 0x26, 0x30, 0x39, 0x30, 0x38, 0x21, 0x36, 0x5F, 0x30, 0x2A, 0x26, 0x00};
strchr()
函数包含于头文件:#include<stdio.h>
中;
函数原型为:char * strchr(char * str, char/int c);
函数功能为:在字符串str中寻找字符C第一次出现的位置,并返回其位置(地址指针),若失败则返回NULL;
根据代码反推出outputString
为:
char outputString[63] = { 0}; int v10 = 0, yu, shang; do { shang = strchr(a1234567890__Qw, ida_chars1[v10]) - a1234567890__Qw; yu = strchr(a1234567890__Qw, ida_chars2[v10])- a1234567890__Qw; outputString[v10] = shang * 23 + yu; v10++; } while (v10 < 62); printf("%s", outputString);
private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
然后遇到了 UnDecorateSymbolName
这个函数,然后才能求出v2
,
UnDecorateSymbolName(v2, outputString, 0x100u, 0);
参考资料:
接下来需要把private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
通过UnDecorateSymbolName
转换为v2
.
可以知道UnDecorateSymbolName
第二个参数为未修饰的名字,第三个参数为长度,第四个参数为0表示完全修饰,第一个参数为输出地址
- 无论 __cdecl,__fastcall还是__stdcall调用方式,函数修饰都是以一个“?”开始,后面紧跟函数的名字。再后面是参数表的开始标识和依照参数类型代号拼出的参数表。
?My_Aut0_PWN
- 对于C++的类成员函数(其调用方式是thiscall),函数的名字修饰与非成员的C++函数稍有不同,首先就是在函数名字和参数表之间插入以“@”字 符引导的类名。
?My_Aut0_PWN@R0Pxx
- 其次是参数表的开始标识不同,公有(public)成员函数的标识是“@@QAE”,保护(protected)成员函数的标识是 “@@IAE”,私有(private)成员函数的标识是“@@AAE”,假设函数声明使用了constkeyword,则对应的标识应分别为“@@QBE”,“@@IBE”和“@@ABE”。
?My_Aut0_PWN@R0Pxx@@AAE
- 接下来就是添加参数了,先加入函数返回值参数,函数的返回值类型为char *
参数表的拼写代号如下:X–voidD–charE–unsigned charF–shortH–intI–unsigned intJ–longK–unsigned long(DWORD)M–floatN–double_N–boolU–struct…指针的方式有些特别。用PA表示指针,用PB表示const类型的指针。
char *的话也就是PAD
?My_Aut0_PWN@R0Pxx@@AAEPAD
- 然后是参数的类型unsigned char *,也就是PAE
?My_Aut0_PWN@R0Pxx@@AAEPADPAE
- 参数表后以“@Z”标识整个名字的结束。假设该函数无参数,则以“Z”标识结束。
?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z
输入ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
ASCII码65 ~ 95
name也就是v2我们已经算出来了:
0x50, 0x51, 0x48, 0x52, 0x53, 0x49, 0x44, 0x54, 0x55, 0x4a, 0x56, 0x57, 0x4b, 0x45, 0x42, 0x58, 0x59, 0x4c, 0x5a, 0x5b, 0x4d, 0x46, 0x5c, 0x5d, 0x4e, 0x5e, 0x5f, 0x4f, 0x47, 0x43










v2也就是name,此时v4等于1E也就是30,然后v5对应的值是A,也就是65
0x50, 0x51, 0x48, 0x52, 0x53, 0x49, 0x44, 0x54, 0x55, 0x4a, 0x56, 0x57, 0x4b, 0x45, 0x42, 0x58, 0x59, 0x4c, 0x5a, 0x5b, 0x4d, 0x46, 0x5c, 0x5d, 0x4e, 0x5e, 0x5f, 0x4f, 0x47, 0x43,65
Z0@tRAEyuP@xAAA?M_A0_WNPx@@EPDP
flag{ 63b148e750fed3a33419168ac58083f5}
发表评论
最新留言
关于作者
