buu刷题 [2019红帽杯]childRE wp

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

1.

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

上这一段脚本:

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

3.

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

先输入31个字符:

然后F7单步调试,发现:

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

然后这里的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可以得到

v2也就是name,此时v4等于1E也就是30,点进汇编看可知此时v5为0x41,对应的值是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

然后再md5——get flag!

4.

flag{63b148e750fed3a33419168ac58083f5}

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值