IDA打开程序,查看start函数C伪代码。
首先来看第一个for循环,它将输入的16个字符分成4部分。v12占20字节,用来存放大小为16字节的str,sub_402573是MD5计算函数,将4个部分的MD5值存放在64字节大小的v6中。具体对应关系如下图(每一格表示一个字节)。
如何看出sub_402573函数是MD5算法,进入该函数,看到两个函数,进入第一个,有一些初始赋值的操作,猜测为MD5。再返回来看v6,v6占64个字节,分成四部分就是16字节,每个字节包含两个十六进制,刚好每部分可以存一个MD5值,进一步猜测为MD5算法。于是动态调试进行验证。
将程序丢入od,在call sub_402573的地方下断点,运行程序,输入“1234567890123456”,继续运行,跟踪v6所在的内存,如下图,发现与“1234”、“5678”、“9012”、“3456”的MD5值相同,验证猜测。
接着到了strtol函数,这是一个C语言库函数。这里的意思是将v12的值转化为十六进制,中间的参数‘0’表示不返回非法字符串,意味着输入的每个字符应该是十六进制字符,否则每一部分可能就少于4个字符。由图2可知v12[3]+3、v12[2]+2、v12[1]+1、v12刚好对应每一部分字符串。
sub_4025b1函数,v12[3]+3 > v12[2]+2 > v12[1]+1 > v12 > 999,保证输入顺序。
sub_402513函数,是这道题最复杂的地方。首先来看传参,a1为输入值的一部分,a2为该部分的MD5值。关于小头位序存储方式,例如1234存为0x 34 12。因此,a2>>4即为MD5值的第一个字符,a2&0xF为第二字符,(a2+1)>>4为第三个字符,(a2+1)&0xF为第四个字符,这里只取MD5前四个字符作比较。
数字+48和字母+87是将十六进制转为assic码(ord(‘0’-0)=48,ord(‘a’-a)=87)。
v3是int型,占四个字节,分别为LOBYTE、BYTE1,、BYTE2、BYTE3。因此是将MD5值的3241与原字符串1234作比较,相等则为真。
最后和固定字符串进行异或得到flag,编写脚本如下:
1 | import hashlib |
输入字符串为”31795a469327c6e6”,flag为:flag{W0w_Y0u_Crack_1t}