CrackMe-003

发布于 2023-04-13  73 次阅读


Start

0x01 简要

查壳:

image20210202203026809.png

该程序中的用到的dll是MSVBVM50.dll,所以可以大概判断是VB5.0

一打开可以看到会先跳出这个界面:

image20210202203110046.png

然后才加载出主界面:

image20210202203134581.png

随便输入点东西提示:

image20210202203200635.png

这里大概比CrackMe02多了个弹窗,并且错误提示也不一样了,失败一次之后直接退出程序,这里的任务就是先把弹窗去掉,然后再写出注册机

0x02 去除弹窗

关于VB去除弹窗的有两种方法,一个是关于时间的,这里打开之后的Neg持续时间很长,那么就可以修改时间为0达到效果,另一个就是用4C法

参考:https://www.cnblogs.com/zpchcbd/p/12080968.html

对于VB而言,会通过FormGUI表中Flag1的值决定窗体执行的先后

载入OD后的OEP,压入的值就是指向VBHeader的位置,直接跟随数据窗口:

image20210204225917876.png

此时0x4067D4指向的就是VBHeader:

image20210204230251494.png

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.意义不明

直接跟随到此处:

image20210204230805424.png

该位置存放的是指向Form GUI描述表的指针:

image20210204230945664.png

继续跟随来到真正的AGUTTable的位置,左边的00和01表示窗体的序号Index,左边的是窗体的启动标志Flag1,这里将Index或Flag1调换位置即可去掉Neg。

image20210206152541157.png

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

image20210206181414182.png

如果最终结果不受其他方面的影响,那么输入abcd最终得到:

image20210206181832143.png

验证:

image20210206181925229.png

很明显得到的算法是有问题的,这里重新跟踪,这次输入a222:

rtcAnsicValBStr:获取ASCII码

_vbaStrI4:转成字符串

len(name) * 0x15B38 + name[0]
image20210206182927117.png

往下将得到的数值通过_vbaStrI4转成字符串,并移动:

image20210207010254102.png

往下调用了_vbaR8Str:

image20210206214435548.png
image20210207011338046.png
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之后:

image20210207012128211.png

之后来到:

image20210207012415952.png

关于fpu的加减乘除可以看这个:https://blog.csdn.net/weixin_30819085/article/details/95002884

image20210207012610003.png

fnstsw,将状态字给到ax寄存器中。

image20210207013231877.png
image20210207013259032.png

这里的跳转是默认不实现,说明不是重点,直接跳过:

image20210207013458346.png
image20210207013515624.png

这两步把加完的浮点数弹出FPU的堆栈,再压入ESP中,并转成字符串:

image20210207013813285.png

继续转成浮点数:

image20210207014236466.png

关键操作:

fmul st(0), qword ptr ds:[401010](3)
fsub st(0), qword ptr ds:[401018](-15)
image20210207014403850.png
image20210207014433172.png

第三次调用vbaStrR8,并调用fsub st(0), qword ptr ds:[401020]:

image20210207014638418.png

最终得到的在这个位置进行比较:

image20210207015056540.png

结果不为0,所以未实现跳转:

image20210207015156713.png

爆破的话只需要针对这个位置的跳转进行修改即可,或者将最终判断的位置nop也可以:

image20210207020522474.png

整个算法基本也就梳理完了:

(len(name) * 0x15b38 + ord(name[0]) + 10/5) * 3 - 2 + 15

0x04 编写注册机

由于整体流程并未发生变化,所以这里沿用CrackMe02的框架,只需要在生成Serial的地方稍作修改即可:

image20210207203346892.png
image20210207203339842.png

框架源码就不再贴出了,直接贴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函数

image20210216231022621.png

可以观察到,call是0x40116A处,通过FF25jmp跳转到真正的ThunRTMain函数里,这里就是典型的间接调用了:

image20210216231215866.png

End