Start
0x01 简要
查壳:
该程序中的用到的dll是MSVBVM50.dll,所以可以大概判断是VB5.0
一打开可以看到会先跳出这个界面:
然后才加载出主界面:
随便输入点东西提示:
这里大概比CrackMe02多了个弹窗,并且错误提示也不一样了,失败一次之后直接退出程序,这里的任务就是先把弹窗去掉,然后再写出注册机
0x02 去除弹窗
关于VB去除弹窗的有两种方法,一个是关于时间的,这里打开之后的Neg持续时间很长,那么就可以修改时间为0达到效果,另一个就是用4C法
参考:https://www.cnblogs.com/zpchcbd/p/12080968.html
对于VB而言,会通过FormGUI表中Flag1的值决定窗体执行的先后
载入OD后的OEP,压入的值就是指向VBHeader的位置,直接跟随数据窗口:
此时0x4067D4指向的就是VBHeader:
VBHeader的结构定义:
Signature BYTE[4] //00H签名,必须为VB5!
Flag1 WORD //04H.未知标志
LanguageDll BYTE[14] //06H语言链接库名,通常为”*"或者vb6chsdll
BackLanguageDll BYTE[14] //14H.后备语言链接库名
RuntimeDLLVersion WORD //22H运行库版本
Languageld DWORD //24H语言标识
BackupLanguageID DWORD //28H,未知标志
aSubMain DWORD //2CH,不为00000000则指向Sub MainO指针
aProjectlnfo DWORD //30H.指针,指向tProjeclnfo结构
Plag2 WORD //34H未知标志
Flag3 WORD //36H.未知标志
Flag4 DWORD //38H未知标志
ThreadSpace DWORD //3CH.线程空间
Flag5 DWORD //14H未知标志
ResCount WORD //44H.数量,表示form与cls文件个数
ImportCount WORD //46H.数量,引用的ocx、dll文件个数
Flag6 BYTE //48H未知,可能代表运行时程序所占内存大小
Flag7 BYTE //49H未知,可能与程序启动时花费时间有关
Flag8 WORD //4AH.未知
AGUTTable DWORD //4CH.指针,指向Form GuI描述表
AExdCompTrble DWORD //50H指针,指向“引入的ocx、dll文件描述表
Aproicclpescrinion DWORD //54H,指针;指向tProjectDescriptionTable的指针
OProjectExename DWORD //58H.偏移,Offset ProjectExename
OProjectTitle DWORD //5CH,偏移,Offset Projectitle
OHelpFile DWORD //60H.偏移,Ofset Helpile
OProjectName DWORD //64H.偏移,Offset ProjectName
关于4C法,所谓4C法就是VBHeader中4C位置的一个指针,该指针指向的石Form GUI的描述表AGUTTable:
Signature DWORD //00H.必须是50000000
FomID TGUID //04,
Index BYTE //24H 窗体的序号
Flag1 BYTE //28H 第一个窗体的启动标志,
AGUIDescriptionTable DWORD //48H指针指向FormGUI表
Flag3 Dword //4CH.意义不明
直接跟随到此处:
该位置存放的是指向Form GUI描述表的指针:
继续跟随来到真正的AGUTTable的位置,左边的00和01表示窗体的序号Index,左边的是窗体的启动标志Flag1,这里将Index或Flag1调换位置即可去掉Neg。
0x03 寻找算法流程
Kill The Neg之后进入Serial破解流程,整体流程与CrackMe02类似,简单跟随一下,可以发现:
在计算Serial的时候用到了大量浮点计算相关的(FPU)
关于FPU的详情可以看:https://www.cnblogs.com/LyShark/p/13611975.html
整理了一下流程:
res = len(Name) * 0x15B38 + ord(Name[0])
res = float(str(res))
res四舍五入为10的整数倍
res = res * 3
res = res - 2
res = res + 15
test -> 1067023
如果最终结果不受其他方面的影响,那么输入abcd
最终得到:
验证:
很明显得到的算法是有问题的,这里重新跟踪,这次输入a222:
rtcAnsicValBStr:获取ASCII码
_vbaStrI4:转成字符串
len(name) * 0x15B38 + name[0]
往下将得到的数值通过_vbaStrI4转成字符串,并移动:
往下调用了_vbaR8Str
:
FLD
指令格式:FLD STReg/MemReal
指令功能:将浮点数据压入处理器的堆栈中
此时为 fld st(0), dword ptr ds:[401000] -> 将401000的数据压入st(0)中
STReg为处理堆栈的寄存器st(0) - st(7)
FST
指令格式:FST STReg/MemReal
指令功能:类似于pop,不弹出堆栈
FSTP
指令格式:FSTP STReg/MemReal
指令功能:类似于pop,传送完数据后弹出堆栈
执行完fld之后:
之后来到:
关于fpu的加减乘除可以看这个:https://blog.csdn.net/weixin_30819085/article/details/95002884
fnstsw,将状态字给到ax寄存器中。
这里的跳转是默认不实现,说明不是重点,直接跳过:
这两步把加完的浮点数弹出FPU的堆栈,再压入ESP中,并转成字符串:
继续转成浮点数:
关键操作:
fmul st(0), qword ptr ds:[401010](3)
fsub st(0), qword ptr ds:[401018](-15)
第三次调用vbaStrR8
,并调用fsub st(0), qword ptr ds:[401020]
:
最终得到的在这个位置进行比较:
结果不为0,所以未实现跳转:
爆破的话只需要针对这个位置的跳转进行修改即可,或者将最终判断的位置nop也可以:
整个算法基本也就梳理完了:
(len(name) * 0x15b38 + ord(name[0]) + 10/5) * 3 - 2 + 15
0x04 编写注册机
由于整体流程并未发生变化,所以这里沿用CrackMe02的框架,只需要在生成Serial的地方稍作修改即可:
框架源码就不再贴出了,直接贴main.py:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'main.py'
#
# Created by: PyQt5 UI code generator 5.15.2
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
import sys
import random
import string
from PyQt5.QtWidgets import QApplication, QMainWindow
from crackMe03 import Ui_MainWindow
class MyMainWindow(QMainWindow, Ui_MainWindow):
name = ''
serial = ''
def __init__(self, parent=None):
super(MyMainWindow, self).__init__(parent)
self.setupUi(self)
self.pushButton.clicked.connect(self.generate_name_serial)
def generate_name(self):
return ''.join(random.sample(string.ascii_letters + string.digits, random.randint(4,10)))
def generate_serial(self, name):
return str((ord(name[0]) + len(name) * 0x15b38 + 10 // 5) * 3 - 2 + 15)
def generate_name_serial(self):
self.name = self.generate_name()
self.serial = self.generate_serial(self.name)
self.textEdit.setText(self.name)
self.textEdit_2.setText(self.serial)
if __name__ == '__main__':
app = QApplication(sys.argv)
myWin = MyMainWindow()
myWin.show()
sys.exit(app.exec_())
生成exe文件:
pyinstaller.exe -F main.py -W
0x05 关于VB程序
VB专用引擎:VB文件使用名为MSVBVM60.dll的VB专用引擎,也称为The Thunder Runtime Engine。
VB5.0则使用MSVBVM50.dll作为引擎。
VB6.0代码中调用MsgBox函数时,会调用MSVBVM60.dll中的rtcMsgBox函数,该DLL其实也只是封装的接口,在WINAPI的一个上层接口。rtcMsgBox会去调用WINAPI中的MessageBoxW函数,MessagBoxW会再往下调用NTDLL中关于MsgBox的函数,随后ntdll.dll再通过中断或者其他方式进入内核态,这些已经进入到内核的知识了,不是这里的重点。关于破解,重点应该还是放在应用层。
由于VB中的各种消息都是未公开的,微软并未开源出来,所以关于VB的文件,调试起来有时候会相对复杂一些,但是从网上也能零零散散找到一些关于VB逆向出来的结构体。
关于VB程序进入真正的ThunRTMain函数
可以观察到,call是0x40116A处,通过FF25jmp跳转到真正的ThunRTMain函数里,这里就是典型的间接调用了:
Comments NOTHING