首先感谢littleNA大佬的文章https://bbs.pediy.com/thread-230312.htm,看完这篇文章后事半功倍。
第一题 EasyJob
首先为了方便动静结合调试,我先修改了PE文件头部使之跳过重定位。使用工具DIE
这样修改之后,使用ida和其他调试工具的时候地址就能对得上了,告别aslr。
用ida打开程序后,看一下main
函数,确定sub_45F650
和sub_460050
分别为检查request code
和verification code
的函数,做一下重命名。
值得注意的是check_request_code
和check_verification_code
有同一个参数,我把它命名为req_code_stru
。在后面的分析过程中,我得出该变量类型的结构体
1 | struct req_code_stru |
其中vector
结构可以参考上面littleNA大佬的文章,在这个结构体中应该是vector<string>
,每个成员的作用会在后面讲到。
进入check_request_code
函数后,发现字符串
很明显是一个正则表达式,于是推测request code
需要符合该正则表达式。打开程序随便输入了一个符合该表达式的request code
,比如1111-2222-3333-4444
,输入后程序提示继续输入verification code
,表明推测是正确的。通过调试后还发现该函数会把request code
通过字符-
分成4个string
插入到req_code_stru->request_code_part
中。
接下来看一下check_vertification_code
。通过浏览后发现一个比较关键的函数sub_45F960
,通过req_code_stru->request_code_part
计算出4个num
,具体代码如下
1 | int __thiscall sub_45F960(req_code_stru *req_code_stru) |
F5后看起来很乱,需要结合汇编一起分析。
接下来在sub_45FE00
计算出num5
num5
等于request_code
每个数字相加,比如1111-2222-3333-4444
计算出来的num5
就是40
接下来回到check_request_code
中把得到的4个part
和5个num
拼接
这里看F5可能看不太懂,可以看汇编
这里是先把4个operator<<
的参数全部压栈再一个个call,应该是编译器优化的结果。得到的结果就是一个字符串s=part1+part2+part3+part4+num1+num2+num3+num4+num5
最后将得到的字符串再做一个以part1
为key
的hmac_sha256
计算
因为用ida看了字符串发现OpenSSL 1.0.2m 2 Nov 2017
的字样所以事先用Rizzo打上了OpenSSL
的符号。关于OpenSSL
编译可以看https://cloud.tencent.com/developer/article/1343632。不过遗憾的是打上了符号也没办法识别出这个hmac
使用的hash
算法是什么,后来我是通过动态调试获得一组输入和输出暴力跑出了hash
为sha256
,做完hmac_sha256
后得到的结果即为verification_code
。
附上python3
注册机代码
1 | import re |
第二题 Rotate
第二题和第一题相差不大,不同的地方是多了一个num6
以及hmac_sha256
的key
不同。看一下导入表,发现一些opencv
的函数
为了方便分析,去抠了一下cv::Mat
的结构体。
1 | struct cvMat |
其中MatStep
和MatSize
被我拆了,影响不是很大。
首先在check_verification_code
中使用cv::imread
读取了题目目录下的flag.jpg
这里应该是个cv::Mat
构造函数,被编译器优化成内联函数了。
接下来在sub_14007A2C0
中进行图像的旋转。
通过cv::getRotationMatrix2D
获得旋转的矩阵再用cv::warpAffine
进行仿射变换。
在sub_140079C50
进行图像的处理并计算num6
,首先是图像的处理
分别调用了cv::cvtColor
cv::resize
和cv::dct
然后根据图像的数据计算num6
1 | v9 = *Dst.step_p; // 一行元素的字节数 |
得到的长度为64的字符串即为num6
最后拼接part
和num
得到字符串s=part1+part2+part3+part4+num1+num2+num3+num4+num5+num6
再以num6
为key
做hmac_sha256
得到verification code
,相关代码与第一题一致。
附上python3
注册机
1 | import re |
第三题 Invisiable
该题目录下的文件
打开Invisiable.exe
,首先看到有个tlscallback
反调试
通过NtQueryInformationProcess
查了DebugPort
,如果程序被调试了这个DebugPort
就不为0。检测到没有被调试后程序修改了某个函数,把这个函数填充成nop
。当然这个反调试没什么影响,因为我用了SharpOD
插件,所以基本不用管。
看一下main
中的主要部分
验证输入的函数并不是直接调用的,ida看起来也不方便,所以我选择通过动态调试获取了3个step的验证输入的函数
first step
在sub_404180
中判断number-2019000
是否为水仙花数。
如果是的话就就执行接下来的操作
读取题目目录下的invisiable.mp3
文件数据,然后通过sub_401C20
解密得到真实的文件decode.py
解密函数sub_401C20
如下
将原文件数据的每个字节与字符串ReverseEngineerIsEasy
的每个字符循环异或,得到真实文件数据,然后写回硬盘上。
然后对目录下的文件flag
也做了同样的事情,解密后得到nice.png
文件。
最后做了一个谜之操作,修改了main.dll
看了一下main.dll
偏移0x46c
处正是它的导出函数的位置,这里修改成了ret C
。
到这里first step就结束了
second step
首先判断了一下输入
如果符合条件就执行接下来的操作
读取目录下1.txt
的数据
然后判断文本内容
也就是说1.txt
的文本内容格式需要为xxxx-xxxx
,part1
长度暂时未知。
然后对这两个part
进行了和first step解密文件很像的操作,buf1
和buf2
分别为part1
和part2
然后将得到的结果做base64encode
计算,得到的结果需要是VFxQXkljVFo=
将VFxQXkljVFo=
做base64decode
后得到T\P^IcTZ
,也就是说我们的part1
长度必须为8。
然后就是写脚本跑一个符合该要求的输入出来了
1 | def xor_asc(l): |
为了方便输入,所以只取了可视的ascii字符(不包括空格)。这个脚本不会停,所以请自行ctrl+c
强制结束。随便取了一个结果u}6uhB2q-!!f+
保存到1.txt
后,再输入一个符合要求的number
,second step就结束了。
third step
在sub_404220
中对输入的number
进行了验证
需要输入一个长度为6的数字,然后每两个为一组拆成3个2位数,这3个数需要符合一个方程,解的结果为744110
。
输入正确结果后,程序加载main.dll
如果仅仅是这样,那么恭喜,程序崩溃。
还记得在first step中修改了main.dll
的导出函数吗?这就是程序崩溃的原因。为了能够得到正确结果,在third step输入数字前需要把未修改的main.dll
替换回去。最后符合题目要求,程序输出了flag。
但是我感觉这里过于诡异,所以不知道算不算正解。又看了一遍题目,要求输出的flag形式为flag{xxxxxxx}
。。。也就是说1.txt
中part1
的前五位是可以确定的,但是我不想改了,就这样吧。
还有一个诡异的地方就是,first step中创建的decode.py
和nice.png
在后面没用到,贴一下decode.py
的代码
1 | # coding=utf-8 |
这就超出我的能力范围了(怎么还有快速傅里叶变换呢)。试着用了一下这个脚本
1 | py -2 decode.py --original nice.png --image out.png --result res.png |
得到图片
nice.png
和out.png
分别为
看了这几张图,总感觉第三题还有一点门路。然而小弟能力有限,只能到这了,希望大佬们能指导指导。