一种隐藏VBA代码模块和恢复可见的方法

        为了保护自己写的VBA程序代码不被他人抄袭,你可能想到给程序添加查看密码。然而这种密码是很容易被破解的,其中的一种破解方法详见我之前写的一篇文章:如何把Excel的VBA程序封装成DLL并分发给他人使用。其实还有很多种破解方法,包括下面即将介绍的这种隐藏代码模块的方法也可以改造成破解密码的方法。除了使用密码,其实还可以使用代码混淆和隐藏代码模块的办法。下面就介绍一种可以隐藏代码模块的方法。
          VBA程序的文件其实是一种“复合文件”,无论是在Excel里写的VBA程序,还是在Solidworks里写的程序,都属于这种“复合文件”。首先要了解这种“复合文件“的内部结构,下面用一个VBA方法(可以在Excel里写,也可以在solidworks里写这个方法)打印出一个Solidworks里编写的swp程序文件的内部结构,代码如下:
'打印出复合文件的内部目录结构
PrivateSub Test_PrintDirs()
Dim fAs CFile
Set f= New CFile()
'打开文件,以字节方式打开
f.OpenFile "D:\VBA\隐藏模块测试.swp"
Dim cf As CCompoundFile
Set cf=New CCompoundFile
Dim ret As String
ret= cf.Parse(f)
If VBA.Len(ret) Then
Debug.Print ret
EndIf
Dim fs() As String
fs= cf.DirsName()
Dim i As Long
For i=0 To UBound(fs)
Debug.Print i, fs(i)
Next
Set cf=Nothing
Set f=Nothing
End Sub
View Code

上面这段代码用于打印“D:\VBA\隐藏代码测试.swp”这个“复合文件”的内部结构,这个文件在solidworks里打开的结果如下图所示,代码主要由Main和Hide这两个模块组成,还有一个UserForm1窗体。

 运行上面的代码段后,得到下面这样的输出结果:

0            Root Entry
 1            Root Entry\apc
 2            Root Entry\apc\The VBA Project
 3            Root Entry\apc\The VBA Project\_VBA_Project
 4            Root Entry\apc\The VBA Project\Host Project Item Names
 5            Root Entry\apc\The VBA Project\VBA Project Data
 6            Root Entry\apc\The VBA Project\Host Project Items
 7            Root Entry\apc\The VBA Project\VBA Project Signature
 8            Root Entry\apc\The VBA Project\Host Project Item Names\ThisLibrary
 9            Root Entry\apc\The VBA Project\Host Project Item Names\Project_Data_CurVer
 10           Root Entry\apc\The VBA Project\Host Project Item Names\Host Project Item List Data
 11           Root Entry\apc\The VBA Project\_VBA_Project\VBA
 12           Root Entry\apc\The VBA Project\_VBA_Project\PROJECTwm
 13           Root Entry\apc\The VBA Project\_VBA_Project\PROJECT
 14           Root Entry\apc\The VBA Project\_VBA_Project\VBA\ThisLibrary
 15           Root Entry\apc\The VBA Project\_VBA_Project\VBA\Main
 16           Root Entry\apc\The VBA Project\_VBA_Project\VBA\__SRP_2
 17           Root Entry\apc\The VBA Project\Host Project Item Names\ThisLibrary\Project Item Data
 18           Root Entry\apc\The VBA Project\Host Project Item Names\ThisLibrary\Control List Data
 19           Root Entry\apc\The VBA Project\Host Project Item Names\ThisLibrary\ReqControl List Data
 20           Root Entry\apc\The VBA Project\_VBA_Project\VBA\__SRP_3
 21           Root Entry\apc\The VBA Project\_VBA_Project\VBA\UserForm1
 22           Root Entry\apc\The VBA Project\_VBA_Project\UserForm1\f
 23           Root Entry\apc\The VBA Project\_VBA_Project\UserForm1\o
 24           Root Entry\apc\The VBA Project\_VBA_Project\UserForm1
 25           Root Entry\apc\The VBA Project\_VBA_Project\UserForm1\[1]CompObj
 26           Root Entry\apc\The VBA Project\_VBA_Project\UserForm1\[3]VBFrame
 27           Root Entry\apc\The VBA Project\_VBA_Project\VBA\Hide
 28           Root Entry\apc\The VBA Project\_VBA_Project\VBA\__SRP_4
 29           Root Entry\apc\The VBA Project\_VBA_Project\VBA\__SRP_5
 30           Root Entry\apc\The VBA Project\_VBA_Project\VBA\_VBA_PROJECT
 31           Root Entry\apc\The VBA Project\_VBA_Project\VBA\dir
 32           Root Entry\apc\The VBA Project\_VBA_Project\VBA\__SRP_0
 33           Root Entry\apc\The VBA Project\_VBA_Project\VBA\__SRP_1
 34           Root Entry\apc\The VBA Project\Host Project Item Names\ThisLibrary\ReqControl List Data\ReqControl List Data
 35           Root Entry\apc\The VBA Project\Host Project Item Names\ThisLibrary\Control List Data\Control_Data_CurVer
 36           Root Entry\apc\The VBA Project\Host Project Item Names\ThisLibrary\Control List Data\Control List Data
View Code

在这个“复合文件”内部,有许多组成文件,而VBA模块的定义就放在“Root Entry\apc\The VBA Project\_VBA_Project\PROJECT”这个文件里,我们只需要改写这个文件,就可以达到隐藏其中某个模块的目的。为了改写这个文件,首先要了解它有哪些内容,可以用下面这段代码把它的内容显示出来:

Private Sub Test_GetStream()
Dim f As CFile
Set f = NewCFile()
f.OpenFile"D:\VBA\隐藏模块测试.swp"
Dim cf As CCompoundFile
Set cf=New CCompoundFile
Dim ret As String
ret= cf.Parse(f)
Dim b() As Byte
ret= cf.GetStream("Root Entry\apc\The VBA Project\_VBA_Project\PROJECT", b)
If VBA.Len(ret) Then
Debug.Print ret
End If
Debug.Print VBA.StrConv(b, vbUnicode)
Set cf=Nothing
Set f=Nothing
End Sub
View Code

运行上面这段代码后,得到下面的输出结果:

ID="{6366A273-ED0F-4463-BE89-B6DD8ECD755B}"
Document=ThisLibrary/&H00000000
Module=Main
Module=Hide
Package={AC9F2F90-E877-11CE-9F68-00AA00574A4F}
BaseClass=UserForm1
Name="Macro4"
HelpContextID="0"
VersionCompatible32="393222000"
CMG="888A859389938993899389"
DPB="C4C6C9EF392A3A2A3A2A"
GC="00020D0E0E0E0EF1"

[Host Extender Info]
&H00000001={3832D640-CF90-11CF-8E43-00A0C911005A};VBE;&H00000000

[Workspace]
ThisLibrary=0, 0, 0, 0, C
Main=534, 155, 1812, 802, Z
UserForm1=49, 97, 1327, 744, , 756, 68, 2034, 715, 
Hide=224, 224, 1187, 708,
View Code

从上面的输出结果可以看到,程序文件一共有2个模块:Main和Hide,如果要隐藏Hide模块,那么只需要把Module=Hide这行删除即可。下面再用一段VBA代码来改写上面的“复合文件”,使Hide模块隐藏。

'改写对应的文件
Private Sub Test_ReWriteStream()
Dim f As CFile
Set f= New CFile()
f.OpenFile "D:\VBA\隐藏模块测试.swp"
Dim cf As CCompoundFile
Set cf=New CCompoundFile
Dim ret As String
ret= cf.Parse(f)
If VBA.Len(ret)Then
Debug.Print ret
End If
Dim b()As Byte
ret= cf.GetStream("Root Entry\apc\The VBA Project\_VBA_Project\PROJECT", b)
If VBA.Len(ret)Then
Debug.Print ret
End If
Dim strSrc As String
strSrc= VBA.StrConv(b, vbUnicode)
'替换后模块将被隐藏
'strSrc = VBA.Replace(strSrc, "Module=MMain" & vbNewLine, "")
strSrc= VBA.Replace(strSrc,"Module=Hide"& vbNewLine,"")
Debug.Print"更改后的文本:"& Chr(13)& strSrc
b= VBA.StrConv(strSrc, vbFromUnicode)
ret= b
Debug.Print"转换后的文本"& Chr(13)& ret
ret= cf.ReWriteStream("Root Entry\apc\The VBA Project\_VBA_Project\PROJECT", b)
If VBA.Len(ret) Then
Debug.Print ret
EndIf
Set cf=Nothing
Set f=Nothing
EndSub
View Code

经过改写文件后,再用solidworks打开swp文件,发现Hide模块不见了,但是程序还能正常运行。学会了隐藏模块的方法,那么恢复模块可见就很简单了,只需要给文件重新添加上对应的模块名即可。其实上面这种方法不仅可以隐藏模块,也可以用于解除VBA程序设置的密码,下次我们再详细说说如何解除程序密码。

上面的代码段用到了一些未公开的方法和代码,如果你想要深入学习和了解,请关注微信公众号“全栈开发的码农”,欢迎私信或留言探讨。

posted @ 2024-12-22 19:12  wwwzgy  阅读(1012)  评论(0)    收藏  举报