拖进IDA查看,主要功能在main函数中,直接F5
int __fastcall main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
_QWORD *v4; // rax
const CHAR *v5; // r11
__int64 v6; // r10
int v7; // r9d
const CHAR *v8; // r10
__int64 v9; // rcx
__int64 v10; // rax
unsigned int v12; // ecx
__int64 v13; // r9
__int64 v14; // r8
__int64 v15; // rdx
_OWORD v16[2]; // [rsp+20h] [rbp-38h] BYREF
memset(v16, 0, sizeof(v16));
sub_140001080("%s", (const char *)v16); // //输入31个字符
v3 = -1LL;
do
++v3;
while ( *((_BYTE *)v16 + v3) );
if ( v3 != 31 ) // 如果输入不等于31个字符就一直循环
{
while ( 1 )
Sleep(0x3E8u);
}
v4 = XMMI2_FP_Emulation(v16); // 这个函数的作用仅仅是申请0x18个字节的内存
v5 = name; // CHAR name[32] 初始值是0
if ( v4 )
{
sub_1400015C0((unsigned __int8 *)v4[1]);
sub_1400015C0(*(unsigned __int8 **)(v6 + 16));
v7 = dword_1400057E0;
v5[dword_1400057E0] = *v8;
dword_1400057E0 = v7 + 1;
}
UnDecorateSymbolName(v5, outputString, 0x100u, 0);// CHAR outputString[256]
v9 = -1LL;
do
++v9;
while ( outputString[v9] );
if ( v9 == 62 )
{
v12 = 0;
v13 = 0LL;
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 < 0x3E );
sub_140001020("flag{MD5(your input)}\n", v15, v14, v13);
return 0;
}
else
{
v10 = sub_1400018A0(std::cout);
std::ostream::operator<<(v10, sub_140001A60);
return -1;
}
} int __fastcall main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
_QWORD *v4; // rax
const CHAR *v5; // r11
__int64 v6; // r10
int v7; // r9d
const CHAR *v8; // r10
__int64 v9; // rcx
__int64 v10; // rax
unsigned int v12; // ecx
__int64 v13; // r9
__int64 v14; // r8
__int64 v15; // rdx
_OWORD v16[2]; // [rsp+20h] [rbp-38h] BYREF
memset(v16, 0, sizeof(v16));
sub_140001080("%s", (const char *)v16); // //输入31个字符
v3 = -1LL;
do
++v3;
while ( *((_BYTE *)v16 + v3) );
if ( v3 != 31 ) // 如果输入不等于31个字符就一直循环
{
while ( 1 )
Sleep(0x3E8u);
}
v4 = XMMI2_FP_Emulation(v16); // 这个函数的作用仅仅是申请0x18个字节的内存
v5 = name; // CHAR name[32] 初始值是0
if ( v4 )
{
sub_1400015C0((unsigned __int8 *)v4[1]);
sub_1400015C0(*(unsigned __int8 **)(v6 + 16));
v7 = dword_1400057E0;
v5[dword_1400057E0] = *v8;
dword_1400057E0 = v7 + 1;
}
UnDecorateSymbolName(v5, outputString, 0x100u, 0);// CHAR outputString[256]
v9 = -1LL;
do
++v9;
while ( outputString[v9] );
if ( v9 == 62 )
{
v12 = 0;
v13 = 0LL;
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 < 0x3E );
sub_140001020("flag{MD5(your input)}\n", v15, v14, v13);
return 0;
}
else
{
v10 = sub_1400018A0(std::cout);
std::ostream::operator<<(v10, sub_140001A60);
return -1;
}
}
这里会验证输入的位数为31位,如果不是31 位则陷入死循环
do
++v3;
while ( *((_BYTE *)v16 + v3) );
这里会验证outputString,会将outputString的每一位除以23的整数和余数比对,如果不对程序退出
if ( v9 == 62 )
{
v12 = 0;
v13 = 0LL;
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 < 0x3E );
sub_140001020("flag{MD5(your input)}\n", v15, v14, v13);
return 0;
}
先算出outputString的值,代码如下:
int getout()
{
char os[63] = { 0 };
int v13 = 0;
int v12 = 0;
do
{
//计算v14 即余数部分
for (int v14 = 0; v14 < sizeof(qwer); v14++)
{
if (qwer[v14] == hex218[v13])
{
os[v13] = v14;
}
}
//计算整数部分
for (int v15 = 0; v15 < sizeof(qwer); v15++)
{
if (qwer[v15] == hex552[v13])
{
os[v13] += v15 * 23;
}
}
++v12;
++v13;
} while (v12 < 0x3E);
//
//char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
printf("outputString is %s\n", os);
return 0;
}
算出outputString值为//char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
再往前看,outputString由v5通过
UnDecorateSymbolName(v5, outputString, 0x100u, 0);// CHAR outputString[256]
获得,UnDecorateSymbolName去除函数修饰,v5即为去修饰之前的值
用以下代码获得v5的值
#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;
}
算出v5的值 为 ?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z
再往前看,发现一段略显混乱的代码,这段代码用动态调试发现,其实是对输入的字符串顺序进行打乱
v4 = XMMI2_FP_Emulation(v16); // 这个函数的作用仅仅是申请0x18个字节的内存
v5 = name; // CHAR name[32] 初始值是0
if ( v4 )
{
sub_1400015C0((unsigned __int8 *)v4[1]);
sub_1400015C0(*(unsigned __int8 **)(v6 + 16));
v7 = dword_1400057E0;
v5[dword_1400057E0] = *v8;
dword_1400057E0 = v7 + 1;
}
比如我们自己的输入
abcdefghijklmnopqrstuvwxyz12345
顺序打乱之后变成
pqhrsidtujvwkebxylz1mf23n45ogca
那么就可以根据这个求得输入,代码如下
void rev()
{
char a1[] = "abcdefghijklmnopqrstuvwxyz12345";
char a2[] = "pqhrsidtujvwkebxylz1mf23n45ogca";
char a3[] = "?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z";
char a4[32] = "";
for (int i = 0; i < 32; i++)
{
for (int j = 0; j < 32; j++)
{
if (a2[j] == a1[i])
{
a4[i] = a3[j];
}
}
}
printf("input is %s\n",a4);
//输出flag input is Z0@tRAEyuP@xAAA?M_A0_WNPx@@EPDP
}
最终的的输入为Z0@tRAEyuP@xAAA?M_A0_WNPx@@EPDP,
根据最后的提示,需要进行MD5运算,
求出最后的flag{63b148e750fed3a33419168ac58083f5}