[2019红帽杯]childRE
新手一枚,如有错误(不足)请指正,谢谢!!
题目链接:BUUCTF-re-[2019红帽杯]childRE
题目下载:点击下载

参考资料:

  1. UnDecorateSymbolName
  2. c/c++函数名修饰规则

IDA64位载入
F5伪代码

倒着分析

可知挨个取outputString字符串的字符,然后对23取余和除数分别在str3中找到对应位置,,,必须与str1和str2对应位置的值相等

然后提取出其中字符串,写出C脚本

#include <stdio.h>
#include <string.h>
int main()
{
	char str1[63] = {
    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
    };
    char str2[63] = {
    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
    };
    char str3[91] = {
    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
    };
    char outputstring[63] = { 0 };
    int v10=0,yu,shang;
    do
    {
        yu = strchr(str3, str1[v10]) - str3;
        shang = strchr(str3, str2[v10]) - str3;
        outputstring[v10] = shang * 23 + yu;
        v10++;
    } while (v10 < 62);
    printf("%s", outputstring);
    return 0;
}

然后可以知道outputString字符串里存放的为
outputString = private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
然后查看UnDecorateSymbolName函数

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

参考资料2

c++函数名的修饰更为复杂,提供的信息也更为丰富。
无论 __cdecl,__fastcall还是__stdcall调用方式,函数修饰都是以一个“?”开始,后面紧跟函数的名字。再后面是参数表的开始标识和依照参数类型代号拼出的参数表。

v2 = ?My_Aut0_PWN

对于C++的类成员函数(其调用方式是thiscall),函数的名字修饰与非成员的C++函数稍有不同,首先就是在函数名字和参数表之间插入以“@”字 符引导的类名。

v2 = ?My_Aut0_PWN@R0Pxx

其次是参数表的开始标识不同,公有(public)成员函数的标识是“@@QAE”,保护(protected)成员函数的标识是 “@@IAE”,私有(private)成员函数的标识是“@@AAE”,假设函数声明使用了constkeyword,则对应的标识应分别为“@@QBE”,“@@IBE”和“@@ABE”。

因为函数为private,私有成员
所以v2 =?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
所以v2 = ?My_Aut0_PWN@R0Pxx@@AAEPAD
然后是参数的类型unsigned char *,也就是PAE
所以v2 = ?My_Aut0_PWN@R0Pxx@@AAEPADPAE

参数表后以“@Z”标识整个名字的结束。假设该函数无参数,则以“Z”标识结束。

所以最终v2 = ?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z
然后就是上面if里的内容了,这里采用动态调试。
F2下断点,进行调试

然后输入31位字符,我这里输入的是ASCII码从65~95
然后F7单步调试

这里的name也就是v2我们已经算出来了,然后发现是一个result给他赋的值
汇编代码为

然后这里的al值就是result的值,记录一下al的值
(可以在那一行汇编代码上F2下断点,然后不停f9运行记录)
可以得到

然后到这里

v2也就是name,此时v4等于1E也就是30,然后v5对应的值是A,也就是65

开始写脚本

#include <stdio.h>
#include <string.h>
int main()
{
	char name[32] = "?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z";
	int biao[] = { 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 };
	char input[32] = { 0 };
	int i;
	for (i = 0; i < strlen(name); i++)
		input[biao[i] - 65] = name[i];
	puts(input);
	return 0;
}

最后得到输入的是Z0@tRAEyuP@xAAA?M_A0_WNPx@@EPDP


然后flag为输入的md5


也就是 63b148e750fed3a33419168ac58083f5

最终的flag为flag{63b148e750fed3a33419168ac58083f5}