新建了一个新的文集《逆向工程》,开启新的篇章。
春节假期在家看了《逆向工程 核心原理》,里面第8章是关于abex' crackme的破解。书中主要针对这个例子讲了如何破解达到目的,但是针对碰到vb编写的exe逆向时有哪些技巧没有做过多说明,所以就有了这篇文章。这篇文章的内容部分来自网络搜集,部分来自我自己研究。
abex' crackme #2 下载地址: 链接:https://pan.baidu.com/s/1SlcCOlPnr8GsaFijxI5x6w 提取码:lht4
0x00 vb入口点特征
VB文件使用名为MSVBVM60.dll(Microsoft Visual Basic Virtual Machine6.0)的VB专用引擎(也称为The Thunder Runtime Engine),举个使用VB引擎的例子,显示消息框时,VB代码中要调用MsgBox函数。其实VB编辑器真正调用的时MSVBVM60.dll里的rtcMsgBox函数。在该函数内部通过调用user32.dll里的MessageBoxW函数来工作(也可以直接在VB代码中直接调用user32.dll中的MessageBoxW)。
VB主要用于编写GUI程序,其采用Windows操作系统的事件驱动方式工作,所以在main()或WinMain()中并不存在用户代码,用户代码存在于各个事件处理程序(event handler)中。
运行OllyDbg,加载abex' crackme#2,停在入口点,如下图:
VB开局是固定的调用ThunRTMain函数初始化各种变量,其需要一个RTMain结构体地址,所以汇编层面就表现为一个push指令将结构体压栈,一个call指令调用MSVBVM60.dll中的ThunRTMain。
RTMain完整结构体(VBHeader)参考:https://www.hex-rays.com/products/ida/support/freefiles/vb.idc
struct VBHeader
{
char szVbMagic[4]; // 固定的VB5! 字符串
__int16 wRuntimeBuild;
char szLangDll[14];
char szSecLangDll[14];
__int16 wRuntimeRevision;
int dwLCID;
int dwSecLCID;
int lpSubMain;
int lpProjectData; /* pointer to ProjectInfo */
int fMdlIntCtls;
int fMdlIntCtls2;
int dwThreadFlags;
int dwThreadCount;
__int16 wFormCount;
__int16 wExternalCount;
int dwThunkCount;
int lpGuiTable;
int lpExternalTable;
int lpComRegisterData;
int bSZProjectDescription;
int bSZProjectExeName;
int bSZProjectHelpFile;
int bSZProjectName;
};
所以总结下来vb应用程序的入口点特征:
- EP点是push+call指令
- push的结构体前4个字节是
VB5!
0x01 vb中Variant数据类型
在调试abex' crackme #2过程中发现,__vbaLenVar
或者__vbaVarTstLt
之类函数被调用时基础类型的内存表示与C/C++不同,差别在于其所有参数传递都像是被一个struct结构体包裹了一层。
比如下面跟踪__vbaLenVar
参数传递:
我很长时间无法理解这里的字符串传递为什么是一个毫无规律的十六进制,直到我看到下面这个帖子:
https://www.unknowncheats.me/forum/general-programming-and-reversing/185136-msvbvm60-dll-reversed-functions.html
函数名
__vbaLenVar
中Var意味着VARIANT
,在32位上sizeof值为16,在64位上sizeof值为24。
其数据结构为如下,完整代码见:https://docs.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-variant
typedef struct tagVARIANT {
union {
struct {
VARTYPE vt;
WORD wReserved1;
WORD wReserved2;
WORD wReserved3;
union {
LONGLONG llVal;
LONG lVal;
BYTE bVal;
SHORT iVal;
FLOAT fltVal;
DOUBLE dblVal;
VARIANT_BOOL boolVal;
VARIANT_BOOL __OBSOLETE__VARIANT_BOOL;
SCODE scode;
CY cyVal;
DATE date;
BSTR bstrVal;
IUnknown *punkVal;
IDispatch *pdispVal;
SAFEARRAY *parray;
BSTR *pbstrVal;
// 此处省略诸多代码
} VARIANT;
需要特殊关照一下 VARIANT_BOOL
这个数据类型(全0是false,全1是true):
typedef short VARIANT_BOOL;
#define VARIANT_TRUE ((VARIANT_BOOL)-1)
#define VARIANT_FALSE ((VARIANT_BOOL)0)
VARIANT结构体中前2个字节是变量类型vt,vt是枚举值,部分值如下:
完整见 https://docs.microsoft.com/en-us/windows/win32/api/wtypes/ne-wtypes-varenum
typedef enum VARENUM {
VT_EMPTY,
VT_NULL,
VT_I2,
VT_I4,
VT_R4,
VT_R8,
VT_CY,
VT_DATE,
VT_BSTR,
// 此处省略诸多代码
}
重新回过头看上面的汇编代码,解释__vbaLenVar
函数的行为:
00402FF9 . 50 push eax
00402FFA . 51 push ecx
00402FFB . C785 2CFFFFFF>mov dword ptr ss:[ebp-0xD4],0x4
00403005 . C785 24FFFFFF>mov dword ptr ss:[ebp-0xDC],0x8002
0040300F . FF15 28104000 call dword ptr ds:[<&MSVBVM60.__vbaLenVa>; msvbvm60.__vbaLenVar
00403015 . 8D95 24FFFFFF lea edx,dword ptr ss:[ebp-0xDC]
0040301B . 50 push eax
0040301C . 52 push edx
0040301D . FF15 44104000 call dword ptr ds:[<&MSVBVM60.__vbaVarTs>; msvbvm60.__vbaVarTstLt
eax寄存器指向一个类型为08 00
的VARIANT结构体,08 00
表示这是一个字符串。ecx指向一个类型为00 00
的VARIANT结构体,用于存放字符串长度。其函数原型为:VARIANT* __vbaLenVar(VARIANT* strLen,VARIANT* str)
,返回值也是strLen。00402FFB
和00403005
两行汇编构造了一个类型为02(VT_I4)值为4的VARIANT变量,压栈后调用__vbaVarTstLt
函数进行2个值大小比较。
0x02: vb其他技巧汇总
网友针对vb常用函数进行了汇总。
常用函数的功能解释:https://blog.csdn.net/mmmsss987/article/details/100516352
常用函数的汇编解释:https://www.cnblogs.com/findeasy/archive/2012/10/11/4053150.html
针对vb窗口属性特征值的技巧:https://bbs.pediy.com/thread-12133.htm
0x03: vb逆向神器(VB Decompiler)
下载地址:https://down.52pojie.cn/Tools/Disassemblers/VB.Decompiler.Pro.v10.1.Cracked.by.yoza[RET].rar
可以帮助快速找到函数入口点,已经了解大概逻辑,但是详细的可能不准确。