buu刷题 [2019红帽杯]childRE wp

此题可以分为三大块来解
考点:二叉树(动态调试)、C++函数名修饰

1.

首先f5反编译,首先我们要求出outputString(长度为62),逆分析可以知道取outputString字符串的字符,然后对23取余和除数分别在a1234567890Qwer中找到对应位置,其值必须与str1和str2对应位置的值相等

buu刷题 [2019红帽杯]childRE wp_第1张图片

上这一段脚本:

str1 = "(_@4620!08!6_0*0442!@186%%0@3=66!!974*3234=&0^3&1@=&0908!6_0*&"
str2 = "55565653255552225565565555243466334653663544426565555525555222"
str3 = '1234567890-=!@#$%^&*()_+qwertyuiop[]QWERTYUIOP{}asdfghjkl;,ASDFGHJKL:"ZXCVBNM<>?zxcvbnm,./'

name = ''

for i in range(62):
    name += chr(str3.index(str1[i]) + str3.index(str2[i])*23 )

print (name)

输出为:private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)

2.

然后查看UnDecorateSymbolName()函数


以下所有参考材料均来自该师傅博客——https://www.freesion.com/article/6515734088/

参考资料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

另一种方法——直接上脚本:(参考博客)

#include 

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;
}
//输出?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z

3.

最后就是再上面的if里面的内容,这里采用ida动态调试,
在if(v1)下断点:
(这里看其他师傅的博客考的应该是二叉树?)

buu刷题 [2019红帽杯]childRE wp_第2张图片

先输入31个字符:
buu刷题 [2019红帽杯]childRE wp_第3张图片

然后F7单步调试,发现:

buu刷题 [2019红帽杯]childRE wp_第4张图片

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

buu刷题 [2019红帽杯]childRE wp_第5张图片

然后这里的al值就是result的值,记录一下al的值(这里多运行几次,记录每一次的值就可以了)
可以得到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,

再F7可以得到

buu刷题 [2019红帽杯]childRE wp_第6张图片

v2也就是name,此时v4等于1E也就是30,点进汇编看可知此时v5为0x41,对应的值是A,也就是65

上最后的脚本:

#include 
#include 
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

然后再md5——get flag!

4.

flag{63b148e750fed3a33419168ac58083f5}

 

 

你可能感兴趣的:(#,RE)