-
-
[原创]2019_Redhat ChildRe PE逆向writeup
-
发表于: 2025-4-8 08:17 3403
-
拖进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}
赞赏
他的文章
谁下载
赞赏
雪币:
留言: