1.打开看看
下载下来发现是一个exe文件,那就先运行看看咯
显示要输入密码(1),随便输一个试试,结果程序直接退出了,既然有密码(1),推测还存在密码(2)
2.查壳
拖到exeinfope里边看看
显示是一个32位可执行文件,没有加壳,于是我们可以直接用ida进行下一步分析
3.静态分析
看到main函数,双击进去看看,接着又跳进了_main_0函数
看上去有我们想要的东西,于是tab键切换到伪代码界面看看,看c还是比看汇编要直观高效
一段一段的解读代码吧
两个memset操作,给pbData和String1初始化一段内存空间,初始化v4,把输进去的值给pbData。
判断输进去的密码(1)pbData的长度,不为0就退出程序,为0继续往下执行,atoi函数将pbData这个字符串转换为integer整型赋给v4,v4小于10000的话就退出程序,根据atoi函数的特性,如果想要v4>100000的话,那肯定pbData的前6的字符都为数字且这6位数字>100000,前面又要求pbData的长度为6,那么就可以判断pbData就是由6个数字组成的字符串,范围是100000到999999。
把“@DBApp”接在pbData后边,再计算pbData的长度,接着进入sub_40100A函数,进去看看
刚看到的时候是懵的,以为是啥底层函数,但是仔细看发现每个函数都有“Crypt”字样,应该和密码学相关。
简而言之,CryptoAcquireContextA函数用于得到一个句柄phProv,CryptCreateHash函数规定哈希加密的方式,0x8004表示sha1加密,CryptHashData函数中pbData是要加密的数据,dwDataLen是数据的长度,CryptGetHashParam函数将哈希值传入v6,长度传给pdwDataLen。
v6格式化打印给String2,再把String2的值接到lpString1也就是实参String1后边,由于String1是一个空字符串,所以这里相当于把String2赋给String1。
然后就是一些销毁指针的一些操作,就不做具体分析了。
回到main函数
比较String1和一个很长的字符串,那么只要String1和这个哈希值相等,就证明我们输入的密码1正确,就可以进入下一个密码(2)的验证流程了,这里可以用python爆破一下输入的密码1
得到密码为123321,跑一下程序验证一下
没毛病!!!接着往下
流程感觉和第一个密码的流程差不多欸,也是输入了6个字符,然后把之前的pbData拼接到String后边,于是String = “xxxxxx123321@DBApp”
接着进入sub_401019这个函数
还是一个哈希加密函数,但是这回他用的是0x8003也就是md5加密,好像貌似可以爆破?但问题是输入的这6个字符无法确定是啥,不可能把所有的可打印字符组合都尝试一遍吧,就算是字母加数字的组合,花上很长时间都不一定爆破得出。这里出题人应该就是不想让我们去考虑爆破这个方法,应该还有别的点可以突破。接着往下看
在哈希值验证相同了之后,程序又进入了一个sub_40100F函数,而参数String正是我们前面说的String = “xxxxxx123321@DBApp”
说不定这就是突破口?进去看看
FindResourceA函数,用于返回指定资源类型整数标识符和资源的整数标识符所对应的句柄,SizeofResource函数得到句柄对应的资源文件的大小,LoadResource函数得到指向资源文件第一个字节的句柄,LockResource函数返回指向对应资源文件第一个字节的指针。这里稍稍提一下资源文件,PE格式文件里边报考pe文件头,代码节,资源节等等,这些个函数就是为了找到并打开某个资源文件,指向它的第一个字节,以便对他进行操作,为了直观的看到这个资源文件,我们需要用到Resource hacker这个软件,打开后可以发现
AAA资源类型下的101文件也即是0x65文件,接着往下看
进入sub_401005函数看看
v5得到lpString也就是实参String = "xxxxxx123321@DBApp"的长度
a2[i] ^= lpString[i % v5]
算法很简单,把a2[i] 异或lpString[i % v5]的值传回给a2[i],而a2是lpBuffer也就是指向资源文件第一个字节的指针。接着往下看
CreateFileA函数在该程序的相对目录下创建一个dbapp.rtf文件,接着WriteFile函数把lpBuffer字符串的内容写入这个文件。也就是说把刚刚异或的结果写入这个文件,密码 ^ 资源文件 = rtf文件 ,如果我们能得到rtf文件的数据,再用rtf文件 ^ 资源文件 = 密码 ^ 资源文件 ^ 资源文件 = 密码。相同类型文件的内容是不同的,但是每个文件都有对应的文件头,文件头是不变的,那我们只要知道rtf文件头是啥,只要这个文件头是大于或等于6个字节,那我们至少就能得到密码的前6个字节了。上网搜搜rtf文件头
放到010里
只有5位…太抽象了,到自己的电脑里搜搜rtf后缀的文件吧
找到了一堆,我看了好几个,开头6位都是{\rtf1
知道了rtf文件头,就可以写脚本异或反推密码咯
得到密码是~!3a@0
运行一下程序试试
运行完后直接退出了,啥也没显示,回想起创建在相对目录下的dbapp.rtf文件,打开看看
里边放着flag{N0_M0re_Free_Bugs}
4.总结
这道题考察的知识点挺多挺杂的,对windows api的了解和掌握程度,对杂凑函数的熟悉程度,对文件打开关闭函数的掌握程度,当然,还有python写脚本的能力。是一道很经典的题