这题本来不想详细的说,因为我解此题用的比较笨的方法,但这也不失为一种方法,赛后反思总结了一下,觉得在逆向调试和分析的技巧上可以有所提高,并且最后也会提及如何在自己的程序中加入llvm混淆。
通过file
指令可知是一个64位,linux动态编译的程序,载入IDA
可以看到程序混淆的十分严重。
不过和叹息之墙比起来,还差的远呢。
通过查看字符串,可以知道flag的长度为32,并且看到一串明文。
这段明文放在这里肯定会被使用到,交叉引用可以来到loc_412164
基本块中
strings
查看字符串。
可以知道程序使用Obfuscator-LLVM 4.0.1
编译的。
但是现在我们并不能看出什么东西,正常的逻辑是这样的,不过要是感觉敏锐一点,byte_613050
是最后用来校验的数据,ds:dword_6130D0
是经过加密变换后的数据,然后我们在分析时就可以抓住ds:dword_6130D0
关键线索进行查看,但这篇文章中我想说明并不是这个,而是一点提升动调效率的技巧,所以接下来我们还是从main
函数开始我们的分析。
IDA
是个神器,我们应该好好利用。IDA在分析完程序后,会显示CFG
控制流程图,每个基本块之间的关系我们可以清楚的看到。
总揽一下代码可以看到这样那样的花指令
以及使用了控制流平坦化的混淆方式,这也已经很常见了。
假设大家都不知道llvm
混淆的程序是怎样的,我们从loc_400655
基本块开始看起。我们需要时刻盯着我们的输入。
不断的f8
我们便可以来到loc_400979
基本块中,其实可以发现全程只有jmp
和jz
两条跳转指令,从上到下的中间过程我们是无法修改的,也就是说在这个过程中,并没有进行改变程序流程的校验。了解到这一点之后我们便可以在每个分支的最后一个基本块的下断,而后便可以F9
运行到断点处,从而节省一部分时间。继续回到这个块中。
可以看到它的功能是将输入的第一个字符取出,并存入到ds:dword_6130D0
中,同时ecx
作为下标,也就是ds:index
(已重命名,最后我会附上idb文件),可以想到后续必然是会逐个获取我的输入,并将其拷贝到ds:dword_6130D0
中,此时我们便可以 F9 运行到下一个loc_4009A9
基本块中,很明显是为了将ds:index
++。
同样的 F9 可以来到loc_40095C
,比较ds:index
是否大于0x20
其实这时已经比较明显了,但我们仍需要验证一下,再次F9再次来到loc_400979
中,之后便是一个循环,此时我们可以取消先前设置的三个断点,再次F9,注意数据部分的变换,最后运行到loc_4009C6
,由于代码块太大,IDA无法使用CFG图将其展示出来。
此时的ds:dword_6130D0
如下:
loc_4009C6
代码块非常的大,如果单步调试可能真的需要半天时间,因此我们需要找到重复的部分,我们紧盯着ds:dword_6130D0
这部分数据,这是切入点,前面一大段在进行复制,可以通过F4运行到光标处,进行快速的调试,代码重复的部分便可一快速的跳过。
接下来的一大片代码都在增加index
的值,我们可以快速的滑动滚轮略过,直到40F13D
,出现大片奇怪的字符。
单步跟一会可以发现,程序将这些字符复制到了ds:dword_6130D0
偏移index*4
的位置
同样略过重复代码,之后是大量的index--
,也就是
而后同样的又是将一块数据复制到ds:dword_6130D0
偏移0x1d0*4
处。最后来到0x411A09
有一个很明显的xor操作,此时通过查看eax 和 rcx
可知是将输入同刚刚初始化的数据进行xor,我们也可以进行一个反向验证,这很容易,因为xor是可逆操作。所以此部分代码可以略过,最后可以看到我们的输入变化如下:
只有前0x10字节发生了变化,因此我们便可以得到第一部分的解密脚本。
我想要讲的到这里已经差不多了,在带混淆调试时,我们不可能完全的单步调试,必须有选择的略过一些无用代码,这可以提高我们的效率,其实做逆向,看完题目后心中应该大致有一个方向,我们需要做的是去验证自己的想法是否正确,带着一定的目的进行调试,而不是走一步看一步,反而做了大量的无用功。
接下来的过程和第一部分差不多,至少方法上是一样的,因此我也不愿在这里做无用功,以上就是我的一点技巧,希望能给大家一些帮助,也希望同学们能动手调试一遍,感受其中的乐趣,感受逆向工程的美丽。在文末我会附上我的idb
,仅供参考!
最后的校验部分如图:
解密脚本如下:
dic = '012345abcdefghijklmnopqrstuvwxyz'
dic_list=list(dic)
xor1=[0x68,0x1C,0x7C,0x66,0x77,0x74,0x1A,0x57,0x06,0x53,0x52,0x53,0x02,0x5D,0x0C,0x5D]
xor2=[0x04,0x74,0x46,0x0E,0x49,0x06,0x3D,0x72,0x73,0x76,0x27,0x74,0x25,0x78,0x79,0x30]
xor3=[0x68,0x1C,0x7C,0x66,0x77,0x74,0x1A,0x57,0x06,0x53,0x52,0x53,0x02,0x5D,0x0C,0x5D]
print len(xor1)
flag1=""
for i in range(16):
flag1+=chr(xor1[i]^ord(dic[i]))
print flag1
flag2=""
for i in range(16):
j=i+16
flag2+=chr(xor2[i]^ord(dic[j])^xor3[i]^ord(dic[i]))
print flag2
print flag1+flag2
$ git clone -b llvm-4.0 https://github.com/obfuscator-llvm/obfuscator.git
$ mkdir build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Release ../obfuscator/
$ make -j7
ubuntu
下照着官方的流程来一遍就可以了。
使用如下方式进行编译:
$ path_to_the/build/bin/clang test.c -o test -mllvm -sub -mllvm -fla
这样我们便可以自己给自己出道llvm
的题目了,是不是很刺激呢。
其实解决这道题目的方法有很多,这里只是想分享一下自己的一些小思路,希望自己在以后遇到需要头铁分析的题目时能善于利用技巧,提高逆向效率。最后也欢迎大家一起交流。
附件链接:https://pan.baidu.com/s/1hsf1fkIdwDiMiXBY6J76KQ 密码:kvs8