题目下载:下载
参考:re学习笔记(24)BUUCTF-re-[2019红帽杯]childRE_Forgo7ten的博客-CSDN博客
这道题涉及到c++函数的修饰规则,按照规则来看应该是比较容易理解的。上面博客中有总结规则,可以学习一下。
载入IDA
可以知道用户输入长度是31,然后好像就看不出来啥。
看下部分IDA
可以知道最终flag为用户输入的MD5加密形式。然后上面有一个do...while()循环,里面又有一个操作,为了不触发exit()退出程序,所以上面比较的值肯定相等,而a1234567890Qwer[]数组和0x140003478i64,0x140003438i64地址处的数据已知所以可以求出outputString的值。
a123='1234567890-=!@#$%^&*()_+qwertyuiop[]QWERTYUIOP{}asdfghjkl;,ASDFGHJKL:"ZXCVBNM<>?zxcvbnm,./'
s78='(_@4620!08!6_0*0442!@186%%0@3=66!!974*3234=&0^3&1@=&0908!6_0*&'
s38='55565653255552225565565555243466334653663544426565555525555222'
v13=0
name=''
for i in range(62):
name+=chr(a123.index(s78[v13])+a123.index(s38[v13])*23)
v13=v13+1
print(name)
# private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
发现输出的是一个函数声明,把他MD5加密后当做flag发现不对。所以还存在别的操作,往上看刚才没有处理的函数
发现有一个UnDecorateSymbolName()函数,并且参数有outputString。
UnDecorateSymbolName:
UnDecorateSymbolName 函数反修饰指定已修饰的 C++ 符号名。简单说就是把c++中编译修饰的函数名变回去。
参数:DecoratedName [输入]
已修饰的 C++ 符号名。此名称能以始终为问号 (?) 的首字符鉴别。
UnDecoratedName [输出]
指向字符串缓冲区的指针,该缓冲区接收未修饰的名字。
UndecoratedLength [输入]
UnDecoratedName 缓冲区的大小,为字符数。
Flags [输入]
用于反修饰已修饰名称的方式的选项。此参数能为零或更多个下列值。
所以刚才求出的东西就是函数未修饰的形式,v5就是被修饰的形式,可以知道v5在上面有进行操作所以求出v5来,根据c++修饰规则知道v5为“?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z
”。在v5之前还有一个函数sub_1400015C0
跟进:
可以看出这应该是二叉树的后序遍历,可以尝试查看一下这个二叉树的规律(二叉树的输出不就相当于对数据进行打乱重新排序嘛),所以输入长度31的任意数据,如ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
下断点:
输入运行:
f8步过,得到二叉树后序后的输出顺序:
所以可以写代码了
#include <stdio.h>
#include <string.h>
int main(){
int i;
char s[32]="?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z";
char flag[32]={0};
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, 0x41};
for(i=0;i<32;i++){
flag[biao[i]-65]=s[i];
}
for(i=0;i<32;i++){
printf("%c",flag[i]);
}
}
//Z0@tRAEyuP@xAAA?M_A0_WNPx@@EPDP
在MD5加密