一、SOFTICE 配置方法
尽管大多数 VB 程序依然会调用 Win32 API 函数,但若你希望在 VB DLL 运行库中设置断点,就必须先将对应的 VB DLL 运行库添加到 SOFTICE 的配置文件中。以 Windows 95/98 系统为例,你需要在 winice.dat 配置文件里添加相应的 VB DLL 运行库(以下是部分内容):
; 注意分号后是描述语言,不被执行。每次只装载需要的一种 VB dll,最好不要同时装载两个以上,把要装载那种 VB dll 那一行的分号去掉,重新启动即可。另外,判断软件是何种 VB dll 有几种方法,最直接一种用 WDasm89 查看,还有一种方法是用十六进制工具打开程序观察。或用 SOFTICE 跟进去(用 bpx hmemcpy),稍转一圈,你观察一下调试窗口领空处会出现何种 VB DLL 即可。
; EXP=c:\windows\system\msvbvm60.dll ; Visual Basic 6
; EXP=c:\windows\system\msvbvm50.dll ; Visual Basic 5
; EXP=c:\windows\system\vb40032.dll ; Visual Basic 4(32-bit)
; EXP=c:\windows\system\vb40016.dll ;Visual Basic 4(16-bit) 较少见
; EXP=c:\windows\system\vbrun300.dll ; Visual Basic 3
完成上述配置后,重新启动操作系统,即可在 VB 运行库的各类函数上自由设置断点,从而进行后续调试与分析。
二、VB DLL 常用函数详解
1、字符处理函数
常用字符处理函数包括:MultiByteToWideChar、rtcR8ValFromBstr、WideCharToMultiByte、__vbaStrCmp、__vbaStrComp、__vbaStrCopy、__vbaStrMove、__vbaVarTstNe。
注意:这些函数名称前的下划线 __ 是由两根短线 _ 组成的,请勿混淆。如果你正在破解 VB6 程序,需要在断点前加上 msvbvm60! 前缀,例如:bpx msvbvm60!__vbastrcomp。
以下是一些常用的字符串操作函数:
Val()将字符串转换为数字Rtrim$()去除字符串右侧的空格Str$()将数字转换为字符串Trim$()去除字符串两端的空格Left$()按指定长度从左侧提取字符,例如Left$("Theodolite", 4) = "Theo"Asc()将字符转换为对应的 ANSI 编码,例如Asc("A") = 65(65 为 ASCII 码的十进制值)Ltrim$()去除字符串左侧的空格,例如Ltrim$(" Hello ") = "Hello "Chr$()将 ANSI 编码转换为对应的字符,例如Chr$(65) = "A"Right$()按指定长度从右侧提取字符
几个常用函数的具体说明:
1、__vba vartstne(在 VB5 中调用 MSVBVM50!___vba vartstne)
进入该函数后,在地址 0F04E351 处你会看到以下代码:
push dword ptr [ebp 0c] ;Push address *真* serial 的地址
push dword ptr [ebp 10] ;Push address 你输入的serial地址
此时执行命令 d ebp 0c,数据窗口会显示 4 字节地址的倒序排列。再次使用 D 命令(地址已纠正顺序),即可查看到正确的序列号。
2、__vbaR8Str(在 VB5 中的使用情况如下)
Push ebp-20
Call MSVBUM50.__vbaR8Str ;转换 string 到 Integer/Real
fcomp qword ptr [00401028] ;数据比较
在执行 fcomp qword ptr [00401028] 这一行时,使用 DL 00401028 命令即可看到真实的序列号。需要特别留意的是,DL 用于显示 Long/real 型数据,而 SOFTICE 默认状态下为 DB 字节型。
3、__vbastrcmp
具体示例:
:005BDC32 CALL [MSVBVM50!__VbaFreeVar]
:005BDC38 MOV ESI,[EBP 08]
:005BDC3B PUSH ESI
:005BDC3E CALL [ECX 00000790] <-- 值返回到 EDX.
:005BDC44 MOV EDX,[ESI 4C] <-- 输入的号码,但倒转
:005BDC47 MOV EAX,[005E0078] <-- 正确号码
:005BDC4C PUSH EDX
:005BDC4D PUSH EAX <-- Push参数给 __vbaStrCmp.
:005BDC4E CALL [MSVBVM50!__vbaStrCmp] <-- Visual Basic 字符串比较
:005BDC54 TEST EAX,EAX <-- 测试比较结果
:005BDC56 JNZ 005BDC64 <-- 不相等跳走
2、警告窗口相关函数
常用的警告窗口函数包括 rtcBeep、rtcGetPresentDate(time API)等。
三、VB 字符串存储格式
在大多数 VB 程序中,我们可以使用 bpx Hmemcpy 设置断点,但很快会发现系统陷入 VBRUNxxx.DLL 运行库,难以直接定位到 EXE 文件中的真正比较核心。因此,通常需要借助字符串线索来跟踪程序流程。值得注意的是,VB4 及以上版本的程序在存储和比较字符串时,采用的是 wide character 格式(即每个字符之间填充 0x00)。
MultiByteToWideChar() 函数负责将普通字符串映射为宽字符(Unicode)字符串。其函数原型如下:
int MultiByteToWideChar(
UINT CodePage, // code page
DWORD dwFlags, // character-type options
LPCSTR lpMultiByteStr, // string to map
int cbMultiByte, // number of bytes in string
LPWSTR lpWideCharStr, // wide-character buffer
int cchWideChar // size of buffer
);
举例来说,原始字符串 "CRACKZ"(43h 52h 41h 43h 4Bh 5Ah),转换为 Wide 字符串后会变成:C R A C K Z(43h 00h 52h 00h 41h 00h 43h 00h 4Bh 00h 5Ah)。在 SOFTICE 中查看内存中的字符串时,某些情况下需要使用 DL(长实型)命令才能正确显示数字序号(SOFTICE 默认状态为 DB 字节型)。
多数情况下,在 VB 中设置准确的断点存在一定难度。断点设置完成后,尝试输入序列号并运行程序,通常会返回到 VBRUNxxx.DLL 中。此时请检查寄存器(EAX 和 EBX)中的值,其中存放着你输入字符串的长度信息。如果未发现有用信息,请按 Ctrl+D 再次返回 VB DLL 中的另一处位置,继续观察,重复此过程。一旦在寄存器中发现了字符串长度,就逐步仔细跟踪:如果运气不错,输入字符串就会出现在寄存器或其附近的内存区域中。此时,在 SOFTICE 的数据窗口中使用 Alt+光标键滚动查找,正确的序列号很可能就静静地位于其中。
四、oleaut32.dll 的作用解析
在破解 VB 程序时,大家通常比较关注 vbrun*.dll(VB4、VB3)和 msvbvm*.dll(VB5、VB6),但实际上 VB 程序的很多运算是在 oleaut32.dll 中完成的。这个 DLL 提供了大量针对 VB 中 Variant 类型变量进行操作的函数,主要包括一系列 VarXXX(),其中几个专门用于比较字符串和数值:
VarCmp(Addr:77A11AAE, Ord:176)VarCyCmp(Addr:77A0E5D1, Ord:311)VarCyCmpR8(Addr:77A0E5F8, Ord:312)VarBstrCmp(Addr:77A129CD, Ord:314)VarR4CmpR8(Addr:77A12958, Ord:316)VarDecCmp(Addr:77A13697, Ord:204)VarDecCmpR8(Addr:77A1298B, Ord:298)
这里有一个名为 RAMQuota(www.stepnet.com.au)的小程序,它使用 VB6 编写,并调用了上述函数中的 VarBstrCmp() 来比较注册码。该函数的关键指令如下:
:77A12A03 8B7D0C mov edi, dword ptr [ebp 0C]
:77A12A06 8B7508 mov esi, dword ptr [ebp 08]
:77A12A09 8B4D10 mov ecx, dword ptr [ebp 10]
:77A12A0C 33C0 xor eax, eax
:77A12A0E F3 repz
:77A12A0F 66A7 cmpsw
:77A12A11 7405 je 77A12A18
:77A12A13 1BC0 sbb eax, eax
:77A12A15 83D8FF sbb eax, FFFFFFFF
:77A12A18 85C0 test eax, eax
:77A12A1A 7F45 jg 77A12A61
:77A12A1C 7D16 jge 77A12A34
:77A12A1E 33C0 xor eax, eax
由此可见,深入研究 oleaut32.dll 的作用对于 VB 程序破解具有重要意义。
五、Visual Basic 程序中的常见比较方法
在 Visual Basic 中,存在 8 种以上的方法用于检测正确的序列号或密码。本文将重点介绍其中 8 种比较常用且易于上手的方式。首先是最普遍的 3 种方法。
1) 字符串(String)比较
在这种方法中,正确的密码串(例如 "Correct Password")与用户输入的密码串(例如 "Entered Password")直接进行对比。字符串由相邻字符按顺序排列组成,可包含字母、数字、空格以及标点符号。固定长度的字符串最多可存储 0 到 63K 字符,而动态字符串则最多可存储约 20 亿字符。
VB 中字符串比较的典型代码格式如下:
If "Correct Password" = "Entered Password" Then
GoTo Correct Message
Else
GoTo Wrong Message
End If
这是一种较为简单的保护方案,如果程序采用这种函数进行保护,使用 SOFTICE 可以很容易地拦截到。
可用的断点:
__vbastrcomp或__vbastrcmp(注意下划线由两根短线 _ 组成)——字符串比较函数。如果破解的是 VB6 程序,需要在断点前加上msvbvm60!前缀,例如:bpx msvbvm60!__vbastrcomp- 搜索特定的比较代码(具体参考下一节),例如:
56,57,8b,7c,24,10,8b,74,24,0c,8b,4c,24,14,33,c0,f3,66,a7
2) 变量(Variant)比较
在这种方法中,两个变量(Variant 数据类型)之间进行相互比较。Variant 是一种特殊的数据类型,可以包含数字、字符串、日期数据以及一些用户自定义的类型。存储数字时占用 16 字节,存储字符串时占用 22 字节(加上字符串长度)。
示例代码:
Dim correct As Variant, entered As Variant '定义 correct 和 entered 作为变量
correct = "Correct Password" '设置 correct 为正确密码
entered = Text1.Text '设置 entered 为输入密码
If correct = entered Then '用变量方法比较
GoTo Correct Message
Else
GoTo Wrong Message
End If
在这种方法中,方法1中的两个断点将不再起作用,因为程序不再调用 __vbastrcomp 等函数。
有用的断点:
__vba vartsteq——测试变量是否相等。如果破解的是 VB6 程序,需要添加前缀:bpx msvbvm60!__vba vartsteq
3) 长整型(Long)比较
这也是一种常见的比较方式:两个长整型变量之间直接进行比较。Long 型数据占用 4 字节(32 位),取值范围从 -2,147,483,648 到 2,147,483,647,因此只能用于数字比较,存在一定的局限性。
示例代码:
Dim correct As Long, entered As Long
correct = 12345
entered = Text1.Text
If entered = correct Then
GoTo Correct Message
Else
GoTo Wrong Message
End If
对于这种类型,没有专门的断点函数,因为数据比较直接在主程序内部完成,而非在 VB DLL 中。
4) 单精度实数(Single)比较
5) 双精度(Double)比较
6) 整型(Integer)比较
7) 字节(Byte)比较
8) Currency 型比较
上述 8 种比较方式并不意味着每次都能中断在单一类型的比较程序里。因为程序有可能同时采用两种方法进行组合比较,例如 Currency 和 String 混用,或 Variant 和 Long 混用等。
一些可能用到的实用断点:
1) 数据类型转换相关
- 字符串转字节或整型:
__vbai2str - 字符串转长型:
__vbai4str - 字符串转单精度型:
__vbar4str - 字符串转双精度型:
__vbar8str - 字符串转 Currency 型:
VarCyFromStr(适用于 VB6,需确保 WINICE.DAT 中包含 OLEAUT32.DLL) - 整型转字符串型:
VarBstrFromI2(适用于 VB6,同样需要 OLEAUT32.DLL)
2) 移动数据相关
- 字符串到内存:
__vbaStrCopy - 变量到内存:
__vbaVarCopy或__vbaVarMove
3) 运算符相关
- 加法:
__vba varadd——增加变量 - 减法:
__vba varsub——减去变量 - 乘法:
__vba varmul——乘以变量 - 除法:
__vba varidiv——变量相除,结果取整 - XOR:
__vba varxor
4) 其他类别
__vba varfornext——用于 For...Next 循环语句中__vbafreestr__vbafreeobj__vbastrvarval——从字符串中的特定位置取值multibytetowidecharrtcMsgBox__vba varcat——连接两个 Variant__vbafreevar__vbaobjset
