一觉睡到2点发现一血已经没了...QAQ
结合这个信息和第二题的位置我假设这是一道非常有良心的题目。(^_^) (不像去年某些改DES、高精度库、LuaJIT的题)
然后下载好题目并打开,发现是输入一个字符串,然后打印一个是否正确的信息。
于是拖入IDA进行分析
首先注意到的是许多常量字符串都加了密,这没有关系,我们设断点把解密结果记下来即可,其中,解密算法如下:
这是个很常规的XOR加密算法,其中密钥由于当前位置相关的一个多项式给出。为了方便,下文给出的代码中这类加密字符串所存储的位置都被命名为对所存储内容的描述。
然后我们来看代码:
我们可以看到,输出信息被传递到checkWithoutRangeCheck
里由该函数进行输出,外部主要进行两个判断:
这里比较有意思的地方是大小写字母判断:
粗看以为只可输入小写字母,很巧妙,反正我是没见过。
然后我们看到checkWithoutRangeCheck
这个newTrieTree
函数里有这样一句话(大概是IDA通过残留的RTTI信息推测出来的)
结合这是个良心题目的假设和“数据结构”这个题目,我就先当他是一颗Trie树了。
字符串打乱的过程不想看代码,于是拿起OD,输入abcdefghijklmnopqrstuv
,直接单步获得c1
-c8
分别是:
c2.ab c5.cd c4.efg c6.hi c3.jklm c1.nop c7.qrs c8.tuv
结合这是棵Trie的假设粗略的读了这些函数的代码,发现这段程序的功能是先将一些字符串插入到这个Trie,最后将这个Trie和一颗staticTireTree
比较是否相同。
双击staticTireTree
发现是个变量,一路下来没看到初始化这个变量啊,估计是在哪个构造函数里初始化了吧。
用OD的内存断点或者直接用xref可以跟踪到如下初始化代码:
首先是一堆加密串,手工动态解密之。
然后查看linkNode
代码是在一个常量大小的边表里插入信息,不难猜出是在构建一个图,setCount
是在设置一个属性值,由于所有这些属性的和为8,我们不难猜出这个值代表到这个节点为止的字符串数量。
PS: 这里面的kx是一个由两个字符组成的串,这里所谓的Trie其实应该是Radix Tree(基数树)。不过这与这里的分析其实没多大关系。
我们根据linkNode
可以在草稿纸上画出图:
这样,我们得到以下内容
长度为2的串:
kx
c7
ct
长度为3的串:
c7M
c7M
ctf
ct9
长度为4的串:
c7Mk
这下要在c1
-c8
中对号入座还有3!*4!/2!
种可能,我们来看两棵Radix Tree比对通过后进行的最后验证:
可以看到,这里检查了c1
-c8
中的几个字符串中的一些内容异或值是否正确,我们可以根据这些内容来对号入座。
我们先写个程序来查询异或值:
查询得(控制台信息)
kx
19
ct
23
c7
84
7M
122
tf
18
t9
77
这样我们确定了c1
、c2
、c6
、c7
的值。
由于长度为4的空只有一个,余下的长度为2的空也只有一个,而两个余下的长度为3的字符串相同(交换次序后flag不变),我们对号入座得到:
c2.c7 c5.ct c4.c7M c6.kx c3.c7Mk c1.ctf c7.ct9 c8.c7M
也就是说flag为c7ctc7Mkxc7Mkctfct9c7M
。
经检验,这一flag正确。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2018-6-18 16:17
被qwertyaa编辑
,原因: