初略分析
1. 查壳
显然无壳,而且为64位程序
2.放入ida分析
粗略分析一手逻辑为:
输入->判断长度->一个好像是加密的函数->一段奇怪的代码->一个系统函数->关键判断
刚看到题被误导了先分析的是中间那段奇怪的代码,导致有点饶进去了,而且比较疑惑的是那个name的变量,感觉就跟突然出现的一样,总之,先看看什么能搞,把能搞的弄出来。
细看
很明显这个判断部分就outputString无法确定,写个小脚本来看看outputString
a="(_@4620!08!6_0*0442!@186%%0@3=66!!974*3234=&0^3&1@=&0908!6_0*&"
b="55565653255552225565565555243466334653663544426565555525555222"
c=[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]
output=""
x=0
y=0
for i in range(62):
for j in range(90):
if(c[j]==ord(a[i])):
x=j
if(c[j]==ord(b[i])):
y=j
output+=(chr(x+y*23))
print(output,end="")
#private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
看到这个outputString还是有点疑惑,百度了一下输出它的系统函数UnDecorateSymbolName的功能:
反修饰指定已修饰的 C++ 符号名,就是将一个已经修饰的符号符号名变为没修饰前的符号名。而修饰又是什么呢?简单的说,就是当编译器在读取我们写的变量名/函数的时候,是不可能直接把我们的命名直接读入的,这样可能会导致与读入的代码指令相混淆,所以必须把这些变量名/函数经过一定格式的转化,而这个转化的过程就是修饰
了解了这个函数功能后在了解一下修饰的规则,就可以写出这个函数的输入是:?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z
修饰的规则可以看看link
接下来就是对这个程序的最后一步分析了
在外层的结构与sub_1400015C0内部的结构太像了,所以推测可以看出了是一个递归结构,由于本人的对递归的理解不够深,所以我采用动态调试的方式,但是这里调试的时候要注意不要尽量不要用相同字符的字符串(比如“aaaaaaaaaaa”)去调试,这样不容易找到规律。这里我使用“ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_”这个字符串调试。当然前面还有一个我们觉得是加密的函数,这里我们先把它当做黑盒处理,先不管他。
这里就是sub_1400015C0的第一次调用,这个rax里面存放的就是那个黑盒处理过后的第一个数据的地址,我们看看是什么
rax存放的地址的后面的第八个数据开始给rcx,作为参数传给那个递归函数,我们f7跟进
然后得出之前传入rcx的数据确实就是下一个数据的位置,这确实跟链表是差不多一个意思,我推测它的源码其实就是一个链表,然后通过对他这种递归方式的分析这可能是一个二叉树,通过深度优先搜索的方式在遍历整个二叉树,接着分析遍历直到到达第一个分支的末尾
通过对这两个位置的作用分析,上面一个是用来储存index的位置,也就是保存要输出字符串的角标,下面的那一个就是用来储存输出的字符串的位置。
接下来就是一直搜索模每个分支末尾,完成数据储存。所以我们找到了它储存数据的地方只需要跑完就可以了。
这是跑完后的情况,发现我们的输入其实并没有改变,只是顺序发生了变化,所以可以看出我们之前的黑盒函数只是构建了一个二叉树的模型,并没有对其进行变化。
由“ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_”变为
“PQHRSIDTUJVWKEBXYLZ[MF]N^_OGCA”,而"?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z"与“PQHRSIDTUJVWKEBXYLZ[MF]N^_OGCA”等价,所以我们写个脚本推出input的情况:
a="?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z"
index='PQHRSIDTUJVWKEBXYLZ[MF\]N^_OGCA'
b=[0]*31
for i in range(31):
b[ord(index[i])-ord('A')]=a[i]
for i in range(31):
print(b[i],end="")
#输出为Z0@tRAEyuP@xAAA?M_A0_WNPx@@EPDP
#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
#||
#PQHRSIDTUJVWKEBXYLZ[MF\]N^_OGCA
#?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z
最后不要忘了MD5我们的input得出flag为
63b148e750fed3a33419168ac58083f5
总结
这个题的二叉树没有直接看出来比较可惜,带来了许多不必要的麻烦,在遇到非系统函数的调用的比较长的情况下,一定不要硬着头皮的去尝试逆它,它有可能是一段加密或者一个后面可以退出的东西,系统函数如果不明白功能的化一定得去了解一下,从结论附近寻找去切入点是比较重要的。切入点!!!