*文章原创作者:mintancy,本文 未经许可禁止转载

背景

萌新第一次学习逆向,所以在这里也算总结和记录,如有错误之处还请指出。

分析文件是exe格式,附带一个dll链接文件。要求得到解密后的三个key,exe主程序负责key1的解密,dll文件负责key2和key3的解密。文件可以在链接处下载(链接: https://pan.baidu.com/s/1eS22yH0 密码:wtne)。

使用工具

ida(V6.8,吾爱破解网站下载)静态分析+ollydbg(V1.10,官网下载)动态分析。

运行环境

本人是mac系统,然后安装了虚拟机:vmware+windows xp sp3。

那么,我们进入正题吧。

一、ida静态分析

将程序crackme.exe在ida中打开,可以看到对应的汇编语言。

然后看到左侧列表有个main函数,直接双击,找到汇编程序的main入口处,f2加断点高亮显示:

图片 1.png

此时,可以直接在高亮处按F5反汇编得到对应的C代码:

图片 2.png

代码前面部分就是各种初始化,变量声明之类的,没什么特别。继续往下看,看到”&unk_40FA58″看起来是调用了数据段的某个地址的内容,于是双击看看是什么:

图片 3.png

哟,很明显的字符串,会不会是key1呢?于是输入key1,显示错误。然后继续看看程序中使用这串字符串的位置:

图片 4.png

可以看到,在程序找到字符串之后,接下来就是具体的解密部分,其实可以看到很明显的一句汇编语言:

.text:00401128                 dec     edi

寄存器edi中存储的是加密字符,执行的计算是减一,也可以理解是向前移位的计算。

到这一步,再打开ollydbg进行动态分析,验证自己的猜想。其实直接分析汇编语言也可以得到答案,只不过觉得执行程序得到结果更形象。

二、od动态分析

在之前还是简单地介绍一下od的界面和在之后用到的界面(其实也就是常用的几个界面):

图片 5.png

汇编代码窗口和寄存器窗口是两个常用窗口,一个确定程序运行到了哪一步,一个确定需要的内容在内存的存储位置。

对照在ida找到的main函数,直接右键选择“Go To”跳到main函数位置设置断点(在没有ida静态分析不知道主函数位置的网上有各种方法):

图片 6.png图片 7.png

然后反复调试程序设置合适的断点,可以确定汇编代码的功能并得到计算后的字符串:

图片 8.png

这一段代码很短,可以一步一步调试(F7),然后注意oa右边的寄存器窗口,截图中解释的地址就是根据相应寄存器存储内容确定的。

解密算法执行结束之后到内存地址窗口找到存储解密字符串的位置[00393F68],可以看到一串熟悉的字符串亲切地躺在这里:

图片 9.png

此时不太确定是不是key1,然后到程序上输入:

图片 10.png

YEAH!!第一个key1找到啦!萌新特别开心!

然后一鼓作气,继续解决key2和key3。这个时候有点陷入汇编代码的茫茫大海中,直接在od中使用f7动态调试,没有再对照ida中的代码。本意是想分析一下后面的执行顺序,看看内存的变化。

f7太慢,然后又按f8。结果执行又太快,还没注意到具体解密算法,就看到右下角栈区域出现了一个疑似key的字符串。找到对应的内存位置,上下滚动一下发现有两个字符串:

图片 11.png

然后拿到程序中试试:

图片 12.png

踏破铁鞋无觅处,得来全不费工夫啊!

但是,本着认真学习的态度,我决定再回过头看看解密这两个字符串使用了什么算法。

od使用 alt+f2 重新载入程序。

三、再次分析

记住key2在内存中的写入位置:003B3D78,然后重新载入程序,设置内存写入断点,看看是什么时候在经历了什么步骤之后写入的。结果发现并没有这个位置,有可能是载入动态链接库之后才赋予的地址:

图片 13.png

继续分析c代码,发现忽略了这两个地址引用:

图片 14.png

就按照上一个key的方法再次找到汇编语言中使用这两字符串的地方:

图片 15.png

同理在od中定位到[0040123D]然后继续在od中F8动态调试,找到合适的断点:

图片 16.png

进入key2解密函数,此时已载入动态链接库。再查找内存地址003B3D78,出现了该地址内容。可以看到该内存地址还没有被初始化,在此位置设置内存写入断点(关于设置内存写入断点的细节可以参见链接:http://bbs.pediy.com/thread-187118.htm):

图片 17.png

然后F9执行之后在一行代码停下,可以看到此时key已经开始写入:

图片 18.png

此时再使用f7可以单步执行溯源找到解密算法:

图片 19.png

可得:key2的解密是让字符串与77进行亦或(xor)运算。

同理得到key3的解密算法,key3的内存写入地址是003B3DC0:

图片 20.png

key3的解密是让字符串和0F做与运算然后左移4位。

四、一鼓作气

等等!你以为这样就结束了吗?!别走,快回来!

并没有,让我们再回过头来看看主程序的代码:

图片 21.png

看到这个sub_401000()木有?程序调用了动态库的decode函数对字符串解密得到了key2和key3,也就是v21和v23,v24、v26分别是用户输入的key2和key3。然后它们分别经过sub_401000()函数的洗礼(加密),来看看这个函数到底在干什么:

图片 22.png

没办法,萌新看到这一串C语言就觉得头疼,还是使用oa调试看看吧。这段代码可以当作是再次加密,同时将加密后字符串存入[003929A0]开头的地址中:

图片 23.png

这段代码中,需要理解的就是imul操作符及后面的代码对字符进行的操作。在这里使用了IMUL单操作数的使用方法:

imul AL;

意思就是:

AL=AL*AL;

而后面的IMUL CL意思是:

AL=AL*CL;

综上对单个字符的操作描述为:取字符的十六进制数(寄存器EAX低八位即AL),平方后取低八位结果(存入AL中),用这结果再加上循环次数(CL)的平方得到加密字符存入BL中,再写入内存。

用python语言表示为:

#在寄存器中字符用16进制表示,在这里假设字符a也用16进制表示;

#计数君ECX低八位CL用t表示:

a = int(‘a’, 16);         //计算的时候将a转化为10进制数

a = a * a;                      //自乘得到结果

a = hex(a – (a/255*255));       //取乘法结果的低八位再转化为16进制数

a = a +hex( ‘t*t’);             //然后再与循环次数的平方相加得到最后结果

所以最后的结论就是,将存储在程序中的key2和key3解密并加密得到key22和key33,然后用户输入usr_key2和usr_key3用同样的加密算法加密得到usr_key22和usr_key33。最后比较key22和usr_key22,key33和usr_key33是否相等。

总结

用户输入前程序做了什么:

1. key1向前移一位解密得key11。

程序输入key之后程序做了什么:

1. 输入usr_key1正确之后生成中间dll文件,程序载入两个dll文件;

2. key2与77做XOR运算再加密得key22;

3. key3与0F做AND运算然后左移4位再加密得key33;

4. 用户输入usr_key2,usr_key3;

5. usr_key2加密得usr_key22,usr_key3加密得usr_key33

6. if (key22 == usr_key22 && key33 == usr_key33),you win!

分析结束,撒花撒花~~

*文章原创作者:mintancy,本文 未经许可禁止转载

源链接

Hacking more

...