红帽杯——childRE
发布日期:2021-05-07 12:08:51 浏览次数:20 分类:技术文章

本文共 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/23v11 % 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

函数功能: 函数反修饰指定已修饰的 C++ 符号名

UnDecorateSymbolName(v2, outputString, 0x100u, 0);

参考资料:

接下来需要把private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)通过UnDecorateSymbolName转换为v2.

可以知道UnDecorateSymbolName第二个参数为未修饰的名字,第三个参数为长度,第四个参数为0表示完全修饰,第一个参数为输出地址

  1. 无论 __cdecl,__fastcall还是__stdcall调用方式,函数修饰都是以一个“?”开始,后面紧跟函数的名字。再后面是参数表的开始标识和依照参数类型代号拼出的参数表。
?My_Aut0_PWN
  1. 对于C++的类成员函数(其调用方式是thiscall),函数的名字修饰与非成员的C++函数稍有不同,首先就是在函数名字和参数表之间插入以“@”字 符引导的类名。
?My_Aut0_PWN@R0Pxx
  1. 其次是参数表的开始标识不同,公有(public)成员函数的标识是“@@QAE”,保护(protected)成员函数的标识是 “@@IAE”,私有(private)成员函数的标识是“@@AAE”,假设函数声明使用了constkeyword,则对应的标识应分别为“@@QBE”,“@@IBE”和“@@ABE”。
?My_Aut0_PWN@R0Pxx@@AAE
  1. 接下来就是添加参数了,先加入函数返回值参数,函数的返回值类型为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
  1. 然后是参数的类型unsigned char *,也就是PAE
?My_Aut0_PWN@R0Pxx@@AAEPADPAE
  1. 参数表后以“@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}
上一篇:BUUCTF 特殊的BASE64
下一篇:虎符杯——虚拟机逆向

发表评论

最新留言

能坚持,总会有不一样的收获!
[***.219.124.196]2025年04月07日 11时29分17秒