Think like a cracker
以前在E文的破文中,经常可以见到一句话:“Think like a cracker”。随着破解的东西多了,慢慢积累一些经验,觉得这话大有深意。
以我的理解,大概是指要善于揣摩保护者的想法。我也没法描述清楚,只是有点感觉,在破解的时候,要多从具体的代码中跳出来想想,把握大局,判断形势,决定正确的方向。能把这方面的东西表现在破文里,我想会更有意思。
以下是以前破解的一个软件,比较旧了(整理以前的笔记得来的,做不到总用刚破解的东西来写文章)。
是个股票分析类的软件,叫StockNT吧。背景如下:
用户可从Internet下载,功能有限制(好象是数据不能更新)。用户向作者付款后,可从其网站下载一个叫formal.ini的文件,拷入安装目录,然后再联机注册,成功后即为正版用户。不能从别的机器拷贝,只能用以上方式成为正式用户。只能在一台机器上注册,若要把软件安装到别的机器上,必须先“联机注销”,安装后重新注册。
1.先来研究一下formal.ini。断在CreateFileA,很快就可以找到访问该文件的代码。简单 的分析之后,可得到以下结论:
文件长度仅18个字符,只使用了前16个,为16进制数字。可以用UltraEdit自己做一
个,便于分析。假设文件内容为0123456789abcdef01,
将前16字符转换为数字,结果为:
76543210,fedcba98两个dword。
其使用方式为:
42A939 push 2
push eax
//指向包含上述2个dword的buffer
call f_Compute //我取的名字:-)
mov eax,[ebp-28h] //还是放在那个buffer中的第一个值(计算结果)
pop ecx
cmp eax,[ebp-24h]//与第2个值比较
pop ecx
jnz l_TrialVersion
即经过call的处理后得到的2个dword必须相等。
现在来推测一下:虽然这个文件是付款之后才可获得,但还未“联机注册”,不会含有与用户相关的真正信息。也许,只是一个简单的计数,表示“第XXX个用户”。做的保护,只为避免谁都可以做一个formal.ini去注册。这个函数内的计算比较复杂,难以迅速弄清其算法。
如何做出一个这样的文件?很容易想到暴力穷举。根据以上的推测,可以相信:我们只需给出一个dword的值,计算另一个dword即可。
在上述地址设断点,断下后在SoftIce中手工输入代码:初始化一个dword为0x76543210,另一个为1。在循环中递增第2个值,找到符合条件的值或到达0xFFFFFFFF后,调用int 3。按F5开始计算,等待一段时间后,SoftIce弹出,得到的结果为0xEA0D4CAD。所以,可做出一个正确的formal.ini文件,内容为:
01234567DAC4D0AE01//最后的"01"未用
2.首先,我不打算真的去“联机注册”(破解时,是在不能上网的机器上做的)。很快可以发现,有了formal.ini之后,运行时程序会访问安装目录下的Reg\Register.ini文件,这才是真正的keyfile。同样,从访问文件的代码可以看到,这个文件长度为36字符,格式与formal.ini相似,有2行。将文件内容读出后经过变换,与正确值对比。正确值是由用户机器的一些特征数据计算而来的,包括硬盘信息(与分区相关的数据),一些系统文件的时间戳,系统时间等等)。这就是所谓的只能安装在一台机器上的由来。
结合静态分析,对代码稍加跟踪,可得到全部用于比较的正确值。问题是,这里做的是密文比较。Register.ini的数据经过与formal.ini一样的处理(每次取一行的16字符转换为数字),经上述的f_Compute的计算后才用于对比。拿到正确的结果,如何得到被f_Compute处理以前的数据?此函数计算太复杂,层层调用子函数,难以看清其计算方式,更不要说写出个反函数。
利用前面formal.ini的数据做个测试,把结果作为参数放入buffer,f_Compute得到的结果并非是原来的参数,即此函数不是既可加密又可解密的双向函数,没有捷径可走。
现在该怎么办?再来猜一猜,Think like a cracker呵呵。既然用户在获取formal.ini后,可以通过联机注册成为正式用户,那么,注册成功后,程序自己必定会有创建Reg目录,生成Register.ini的动作,也就是说,f_Compute的反函数必定已存在于代码之中。我们只要找到这个函数,把用于比较的正确值作为参数输入,经该函数处理的结果就是Register.ini的正确内容。
如何找到该函数?有很多线索:与WinSock相关的函数调用,对CreateDirectoryA的调用,对"Register.ini"字符串的使用等等。总之,找到了,在这个过程中随时可用formal.ini的数据来测试。要注意的是,要手动修改WinSock相关函数调用的返回值,否则我们的目标函数执行不到(实际上,只要执行了该函数,破解也就成功了:-)。
最后得到Register,ini的正确数据为:
37BC43727C4B3BA801
E10226146C01741601//每行最后2个字符未用
多说两句,我不太喜欢列出大段的代码,我觉得对阅读者没什么用。尽量说明破解过程中的一些思路想法可能更有意思。各位大虾以为如何?