一种基于虚拟机分支判断的加密狗破解方法
程序员文章站
2022-03-12 17:34:32
今天一朋友的公司拿到和他们同类产品的软件,想参考分析该软件,但该软件有加密狗保护,无狗无法运行,因此叫我分析一下该软件,看是否可以无狗情况下也能运行。折腾了大半天,发现该软件有反调试、虚拟机等...
今天一朋友的公司拿到和他们同类产品的软件,想参考分析该软件,但该软件有加密狗保护,无狗无法运行,因此叫我分析一下该软件,看是否可以无狗情况下也能运行。折腾了大半天,发现该软件有反调试、虚拟机等保护手段,通过模拟构造狗的数据难以成功,后来想出这种基于虚拟机分支判断的方法,破解成功。这种方法不需要加密狗,也不需要关心数据的加解密算法,也不需要分析虚拟机的handler。
技术思路是,根据记录虚拟机的执行流程,观察虚拟指令的跳转过程。通过观察错误条件下的虚拟指令执行流程,分析跳转处的2个分支功能,并且通过修正虚拟机数据,让其执行正确的分支。通过反复记录与修正,最终找到正确的路径。
现在简单描述一下这种方法,和大家讨论。
1. 初步分析与判断
直接运行该软件,出现提示:
找到打开狗的驱动和读取狗数据的位置比较简单,在此并不重要,在进行一系列的狗数据的加解密计算后,程序会返回到这里:
多次运行验证可以看出,函数00C06D20即是判断有无加密狗并验证数据合法性的函数,并且可以确认,假如有合法的加密狗,该函数应该返回0,但是如果直接将返回值修改为0的话,程序仍然会出现提示,这表示接下去的程序执行会使用到该函数的数据计算结果。
2. 虚拟机结构的基本分析
接下去的程序执行很明显又进入虚拟机循环执行。
虚拟机看上去是标准的循环执行结构,如图:
在此可以看出虚拟机的结构:00BD974C地址保存虚拟机指令IP地址,并且是指令长度固定的虚拟机,即每条指令长度是8字节。 www.2cto.com
3. 关键问题
由于虚拟机指令长度固定,因此根据记录虚拟机的执行流程,很容易观察到虚拟指令的跳转过程。通过观察无狗条件下的虚拟指令执行流程,能够分析出跳转处的2个分支功能,并且通过修正虚拟机数据,让其执行正确的分支。通过反复记录与修正,最终找到正确的路径。
首先,将00C06D20的返回值修改为0后进行一次虚拟指令的运行记录,如图:
指令记录的结果如下:
可以看出,在虚拟机指令IP==00BD9D58时进行了分支跳转,软件出现了提示,因此可以判断,00BD9D58是一条判断指令,合法的执行应该是继续执行00BD9D60。通过设置条件断点观察,也确实可以证明这一点:
即判断[[00BD9748]]是否为0,如果不为0就进行虚拟指令跳转。
可以用Mdebug脚本来完成这些自动化的执行和修正:
bp 00BE6C87, "ecx==00BD9D58"
g
bc*
[[00BD9748]] = 0
修正以后,继续重复上述步骤,寻找下一个分支判断点,直到软件最终正确运行。(用脚本功能可以很方便地将被调试程序停止在当前操作点)
脚本最后如下:
bc*
g 00BFDE17
eax=0
bp 00BE6C87, "ecx==00BD9D58"
g
bc*
[[00BD9748]] = 0
bp 00BE6C87, "ecx==00BD9E88"
g
bc*
[[00BD9748]] = 0
bp 00BE6C87, "ecx==00BD8360"
g
bc*
[[00BD9748]] = 0
bp 00BE6C87, "ecx==00BD8390"
g
bc*
[[00BD9748]] = 0
bp 00BE6C87, "ecx==00BD9988"
g
bc*
[[00BD9748]] = 0
bp 00BE6C87, "ecx==00BD9a88"
g
bc*
[[00BD9748]] = 0
bp 00BE6C87, "ecx==00BD9AB0"
g
bc*
[[00BD9748]] = 0
最后附上软件运行界面:
4. 总结
这种方法只是对于虚拟机或加密狗的分析的其中一种思路,不一定在所有情况下都适用。对于可变指令长度或更复杂的虚拟机,要配合虚拟机指令的反编译器,结合控制流图和调用流图的分析才能达到结果。
技术思路是,根据记录虚拟机的执行流程,观察虚拟指令的跳转过程。通过观察错误条件下的虚拟指令执行流程,分析跳转处的2个分支功能,并且通过修正虚拟机数据,让其执行正确的分支。通过反复记录与修正,最终找到正确的路径。
现在简单描述一下这种方法,和大家讨论。
1. 初步分析与判断
直接运行该软件,出现提示:
找到打开狗的驱动和读取狗数据的位置比较简单,在此并不重要,在进行一系列的狗数据的加解密计算后,程序会返回到这里:
多次运行验证可以看出,函数00C06D20即是判断有无加密狗并验证数据合法性的函数,并且可以确认,假如有合法的加密狗,该函数应该返回0,但是如果直接将返回值修改为0的话,程序仍然会出现提示,这表示接下去的程序执行会使用到该函数的数据计算结果。
2. 虚拟机结构的基本分析
接下去的程序执行很明显又进入虚拟机循环执行。
虚拟机看上去是标准的循环执行结构,如图:
在此可以看出虚拟机的结构:00BD974C地址保存虚拟机指令IP地址,并且是指令长度固定的虚拟机,即每条指令长度是8字节。 www.2cto.com
3. 关键问题
由于虚拟机指令长度固定,因此根据记录虚拟机的执行流程,很容易观察到虚拟指令的跳转过程。通过观察无狗条件下的虚拟指令执行流程,能够分析出跳转处的2个分支功能,并且通过修正虚拟机数据,让其执行正确的分支。通过反复记录与修正,最终找到正确的路径。
首先,将00C06D20的返回值修改为0后进行一次虚拟指令的运行记录,如图:
指令记录的结果如下:
可以看出,在虚拟机指令IP==00BD9D58时进行了分支跳转,软件出现了提示,因此可以判断,00BD9D58是一条判断指令,合法的执行应该是继续执行00BD9D60。通过设置条件断点观察,也确实可以证明这一点:
即判断[[00BD9748]]是否为0,如果不为0就进行虚拟指令跳转。
可以用Mdebug脚本来完成这些自动化的执行和修正:
bp 00BE6C87, "ecx==00BD9D58"
g
bc*
[[00BD9748]] = 0
修正以后,继续重复上述步骤,寻找下一个分支判断点,直到软件最终正确运行。(用脚本功能可以很方便地将被调试程序停止在当前操作点)
脚本最后如下:
bc*
g 00BFDE17
eax=0
bp 00BE6C87, "ecx==00BD9D58"
g
bc*
[[00BD9748]] = 0
bp 00BE6C87, "ecx==00BD9E88"
g
bc*
[[00BD9748]] = 0
bp 00BE6C87, "ecx==00BD8360"
g
bc*
[[00BD9748]] = 0
bp 00BE6C87, "ecx==00BD8390"
g
bc*
[[00BD9748]] = 0
bp 00BE6C87, "ecx==00BD9988"
g
bc*
[[00BD9748]] = 0
bp 00BE6C87, "ecx==00BD9a88"
g
bc*
[[00BD9748]] = 0
bp 00BE6C87, "ecx==00BD9AB0"
g
bc*
[[00BD9748]] = 0
最后附上软件运行界面:
4. 总结
这种方法只是对于虚拟机或加密狗的分析的其中一种思路,不一定在所有情况下都适用。对于可变指令长度或更复杂的虚拟机,要配合虚拟机指令的反编译器,结合控制流图和调用流图的分析才能达到结果。