[爱琴海]2017 最新 CTF Demo 试玩版题目 ------WriteUp
爱琴海 Demo 地址:
http://bbs.pediy.com/thread-219212.htm
下载题目,直接IDA打开查看字符串, 发现有很多py开头的函数,并且还有GetProcessAddress失败的提示信息,看来确实是Demo试玩版,提示信息很足,所以推测应该是内置了python脚本运行的。
由于之前没搞过python的,直接百度下相关内容了解到以下两个信息
如果只是单独的python脚本行,可以使用
PyRun_SimpleString
如果是脚本源文件,需要先编译再调用,也可以直接加载编译后的pyc使用
PyEval_EvalCode
调用了解以上两个信息后,打开OD调试,在IDA中通过
交叉引用
找到GetProcessAddress后的函数地址的call调用处对其下断点,运行发现断不下来!!!
OD中ctrl+F2重新运行
,发现了个奇怪的现象,之前的程序窗口没有被关闭,就又开了一个新进程。于是,关掉所有crackme, 打开任务管理器,重新运行cm,看到出现了两个进程
在IDA导入表中查找
CrateProcess
, 可以看到程序使用的是CreateProcessW
, OD中使用bp CreateProcessW
对其下断运行
这里发现没有额外的命令行参数,并且在IDA中可以看到,CreateProcess之后,进程1就在WaitForSingleObject
了,所以python相关的代码应该全部在进程2中执行,那么怎么调试进程2呢?
由于进程1没有使用调试模式来创建进程2,所以在进程2被创建成功后,使用od附加是可以成功附加的,但是这个时候附加已经过了python脚本的加载时机了,需要更早一些才行,所以我这里采用修改CC来触发异常给调试器接管。
考虑到GetProcessAddress之用后这些函数才正式开始使用,所以我找的是执行了GetProcessAddress函数之后的sub_403E10
函数,这个函数里可以看到有Py_SetProgramName
,Py_Initialize
等初始化设置的函数,所以我选择在sub_403E10
处修改0x56为0xCC
修改完成后直接运行CM,触发CC直接断在进程2的0x403E10处要设置OD为默认的实时调试器
单步跟看到这样一个路径0022CE90 00416140 UNICODE "C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\_MEI33682\base_"
找过去看了下,是一些python的运行环境,并没有关键的东西,继续跟代码。
这里添加了__main__
,看来下面就快要到调用__main__
函数的地方了, 继续跟代码, 就在下方不远处有一个小循环
这个一直在查找[ebx+0x11]
处等于0x73
的结果,查看EBX
对应的地址发现有这样一个表
简单推测可知道开头4个字节
为每一项的长度,0x11
偏移处为字符串起始,0x73是s
, 以s开头的有spyiboot01_bootstrap
和 sKERNEL32.dll
找到一个后就去打开自身,从自身0x1F000 + offset
处读取数据,解压数据,执行。最终分析的这个表的结构是|长度|偏移|大小|解压后大小|名字|
解压后的数据就是pyc,只不过被抹去了前12字节,抹去的前12字节主要的是前4字节Magic Number
打开自身
读取padding信息(数据为压缩过的)
解压使用的是zlib
库,版本1.2.8,可根据代码中的错误提示字符串搜索得到。
C代码的执行的逻辑到这就结束了,因为程序运行起来看到的界面都是在python中执行的,后面就说下怎么获取并反编译pyc
前面说到了一个表,这个表在原始的cm文件的末尾,可以直接拷贝出来单独保存为dataInfo
,然后用下面的脚本解出所有的pyc
解压出来的pyc中主要的是sKERNEL32.dll.pyc
下载 Easy Python Decompiler v1.3.2
参考这篇http://o1o1o1o1o.blogspot.hk/2016/11/python-pyinstaller-reverse-engineer.html 中的说法
下面這包是用 python 3.4,一樣可以查表
或是利用 archive_viewer.py 顯示前幾包的內容,開頭的 4 bytes 就是 magic number
'EE 0C 0D 0A'
在sKERNEL32.dll.pyc最前面添加 EE 0C 0D 0A XX XX XX XX XX XX XX XX 十二个字节,注意是添加不是修改
我一开始是修改了开头的12字节,结果导致Easy Python Decompiler无法还原,后来在这个帖子里
https://stackoverflow.com/questions/21067313/how-convert-and-save-python-module-from-pyobject-as-binary-data-to-use-it-lat 看到一段代码
这里减8 是因为是python2.7 只有前8字节是magic value
, 而3.4是前12字节。
补上12字节的magic value后直接拖到Easy Python Decompiler就能看到python代码了,如下
代码看起来有些问题,先注释掉while后面的用python运行下看看,注意运行环境是python3.x
看到了这个界面,看来这就是验证的地方,仔细阅读下while循环中的代码,得知z4是输入且长度为10,z1用来验证输入的有效性,
可知key的长度为10且由0-9,a-f组成
,str(int(z4, 16))用来把输入的16进制串转为10进制串。
关键的判断点是if hashlib.md5(z3.encode('utf-8')).hexdigest() == b(m5, 239)
所以算法就是判断输入的值转10进制字符串后的md5是否与预设值相等,将b(m5, 239)放在前面直接print得到md5为524b38df7fe44db9f9b6621f14550e55
但是这个md5值与实际执行时的并不一样。
代码中其实还有smc, 最终运行的python脚本与文件里保存的有一点区别,修改代码的函数是sub_407CC0
这里有个有趣的事情,下面这段代码如果单步运行,那么最后不会修改python代码,直接走free,如果在这段代码之后下断点直接run过去,就会多走修改python代码的处理,经过几次单步感觉是pop ss
影响了push fd
的执行,nop
掉push ss
和pop ss
就能单步跟到smc处
smc代码
所以最终修改后的代码应该在0x1c5处比原始的小 1 ,用比较软件打开内存dump的代码和文件中解压出来的代码可以看到确实只有此处是不同的
cmd5查到这个md5 424b38df7fe44db9f9b6621f14550e55
对应的数为597480335089
转为十六进制小写就是最终的key:8b1c9a66f1
感谢 风间仁 大神指出错误!
本文由 看雪论坛 NearJMP 原创,转载请注明来自看雪社区
如果你喜欢的话,不要忘记点个赞哦!
热门阅读文章:
更多优秀文章,长按下方二维码,“关注看雪学院公众号”查看!
商务合作:wsc@kanxue.com