VBA Projects Storage
All VBA Projects are stored in OLE (Object Linking and Embedding) containers. Project can be stored inside a main document compound file (like old MS Office formats: .doc, .xls, ...) or as an independent OLE compound file (AutoCAD .dvb, CorelDRAW .gms, SolidWorks .swp, ...). New MS Office formats (like .xlsm or .docm) fall in between - these files are zip archives with "vbaProject.bin" file used for VBA Project storage.
MS Access is a special case. Old versions used to split OLE containers into small blocks to store in "MSysAccessObjects" table rows. New versions emulate OLE storage and all VBA streams are records in "MSysAccessStorage" table.
Sidenote: MS Excel files can be encrypted with known password to open: "VelvetSweatshop" (it is default behavior if Workbook passwords is set for example). In such case Excel (.xlsm, .xlam) files are OLE containers with encrypted zip archive as the attached payload.
Project Format
VBA Project container is a collection of streams:
There is one streams per every code module (like Sheet1, Sheet2 or Module1, etc.) with compressed source code.
"PROJECT" stream contains VBA IDE (Integrated Development Environment) and Project settings: locks, visibility, code preview windows dimensions, help links, list of modules to show in VBAProject Explorer, etc.
"_VBA_PROJECT", "dir" and "PROJECTwm" are used to store technical and execution settings, referenced objects and links between modules.
Settings and Locks
"PROJECT" stream is a text with elements like the following:
ID="{A5...63}" Document=ThisWorkbook/&H00000000 ... Module=Module1 Name="VBAProject"
CMG="82...9C" DPB="5D...D5" GC="38...03"
[Workspace] ThisWorkbook=50, 50, 652, 368, Z Sheet1=0, 0, 0, 0, C Sheet2=25, 25, 627, 343, ...
CMG, DPB and GC are encrypted based on a key generated from ProjectId (ID) GUID, password protected projects IDs are set as {00000000-0000-0000-0000-000000000000}. CMG is EncryptedState (user locked, host locked or VBE locked), DPB is ProjectPassword (password verification value) and GC is ProjectVisibilityState (on / off).
[Workspace] blocks controls source code windows placements and dimensions for the modules in the project.
"PROJECT" stream is well documented in Microsoft [MS-OVBA].pdf file and it is easy to see how a single replaced char in ProjectId can lead to failure to decrypt Project parameters and into "Project is Unviewable" state.
"PROJECT" streams corruptions like this were abused over the years as "anti-preview / anti-debug" methods. In reality source code is still available and corrupted "PROJECT" stream causes chain of exceptions inside VBA-enabled application that can lead to unpredictable or malicious behavior and can change unexpectedly from version to version.
Hidden Modules
VBA library loads modules for execution from all streams inside "VBA" OLE folder (based on "dir" catalog), but only the modules present in "PROJECT" stream are listed and shown in VBAProject Explorer! "PROJECT" stream modification can be used to hide any module and this creates easy to exploit security vulnerability:
1) Malicious code of any length and complexity can be hidden without any security warnings or hints from VBA IDE whatsoever.
2) A single call to a hidden module is all that required to execute malicious code. This call can be masked by number of tricks (name visually similar to a standard function for example: latin letter 'O' (oh) replaced with '0' (zero) or 'o' with Cyrillic or Greek 'o', function name misspelled, etc.)
Function StrCоmp(a, b) -or- Function STRC0MP(a, b) -or simply- Function StrCmp(a, b)
Or Sub auto_open() can be used for self-containing and a bit more dangerous malicious module if the host file is useful enough to be opened twice.
3) An absence of any notifications about hidden modules presence and an ability to see all other listed modules together create a false sense of security and provoke to enable the code that looks harmlessly.
Sub Function1()
Dim LResult As Integer
LResult = StrCоmp ("vba", "vba") -or- LResult = STRC0MP("vba", "vba") -or- LResult = StrCmp("vba", "vba")
End Sub
4) This approach can be used to "weaponize" popular VBA scripts in all VBA-enabled applications: AutoCAD, Excel, CorelDRAW, PowerPoint, Word, some PLC Automation systems.
DIY: Hidden Module
You can create MS Excel file with hidden module using the following basic procedure:
1) Create new VBA-enabled ".xslm" Spreadsheet and add 2 modules "Module1", "Module2".
(at least one visible module is required to keep VBA library from removing VBA Project on Save)
2) Change file extension from ".xslm" to .zip, open the file and extract vbaProject.bin from "xl" folder.
3) With any OLE editor (like "SSView") remove line "Module=Module1" from PROJECT stream.
Hex editor can be used as well to replace "Module=Module1" with several empty lines (0xD 0xA).
4) Put modified vbaProject.bin back into archive and change the extension back to ".xslm".
5) Open the file in MS Excel to verify "Module1" invisibility.
DIY: Hide Module with VBA Recovery Toolkit
You can download MS Excel example files (with hidden and visible code blocks):
1) Start VBA Recovery Toolkit.
2) Press fourth toolbar button "Adv.mode" to activate Advanced mode.
3) Press "Open..." and open "vba_base.xlsm" file from the archive above.
4) You will see VBA "PROJECT" stream loaded for editing.
5) You can remove line "Module=Globals" to hide module named "Globals" and press "Use Modified".
DIY: Unhide Module with VBA Recovery Toolkit
You can download MS Excel example files (with hidden and visible code blocks):
1) Start VBA Recovery Toolkit.
2) Press fourth toolbar button "Adv.mode" to activate Advanced mode.
3) Press "Open..." and open "vba_hidden.xlsm" file from the archive above.
4) You will see VBA "PROJECT" stream loaded for editing.
5) You can insert line "Module=Globals" to unhide module named "Globals" and press "Use Modified".
Hints of hidden modules presence
Examples above are very crude proof of concept and there are still hints of hidden modules presence in VBA IDE:
Technique above requires at least one function from hidden module to be "globally" visible so VBA Object Browser (F2 shortcut) can be used to "expose" it. Attempts to access hidden code lead to unhandled exceptions in VBA library, but fortunately DIY procedure above can be applied in reverse as well.
Modules can be removed from MS Access VBA Project Explorer but due to the nature of VBA storage mentioned above "hidden" modules are visible (with the proper settings, of course) in "All Access Objects" panel.
It is all just an easy to miss hints unfortunately.
And of course you can use our VBA Recovery Toolkit and Access Forensics tools to reveal hidden modules, PROJECT stream details and more...