vb6decompiler 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 the vb6decompiler authors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,279 @@
1
+ Metadata-Version: 2.4
2
+ Name: vb6decompiler
3
+ Version: 0.2.0
4
+ Summary: Evidence-based recovery of native-compiled VB6 programs: procedures, literals, SQL, form blobs, DataReports
5
+ Author: vb6decompiler authors
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/47Hunter47/vb6decompiler
8
+ Keywords: vb6,reverse-engineering,decompiler,pe,sql-server
9
+ Requires-Python: >=3.11
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Provides-Extra: pe
13
+ Requires-Dist: pefile>=2023.2.7; extra == "pe"
14
+ Provides-Extra: asm
15
+ Requires-Dist: capstone>=5; extra == "asm"
16
+ Provides-Extra: dev
17
+ Requires-Dist: pytest>=8; extra == "dev"
18
+ Dynamic: license-file
19
+
20
+ # vb6decompiler
21
+
22
+ Recovering what a native-compiled VB6 program actually does, with evidence.
23
+
24
+ Commercial VB6 decompilers stop at one of two walls: they give you forms without
25
+ code, or code without the semantics that make it portable. This toolkit was built
26
+ while recovering a large native-compiled VB6 application (111 MB, ~34,000
27
+ procedures) whose source was lost. Every claim below is a measurement, and every
28
+ measurement has a command that reproduces it.
29
+
30
+ The point is not another disassembler. VB6 is not plain x86 at the level that
31
+ matters: strings are length-prefixed `BSTR`s stored inside `.text`, procedures are
32
+ entered through a fixed runtime prologue, most calls go through `MSVBVM60` vtable
33
+ dispatch, forms are serialised into the executable as binary property blobs, and
34
+ reports live in separate VB6 DLLs as `DataReport` blobs. Off-the-shelf tools read
35
+ none of that. These modules do.
36
+
37
+ Nothing here points at the program it was developed against: no target name, no
38
+ host, no database name, no account, no credential, no absolute install path.
39
+ **You supply every path.**
40
+
41
+ ## What it does
42
+
43
+ | Stage | Command | Evidence it produces |
44
+ |---|---|---|
45
+ | PE surface of an install | `vb6d pe <dir>` | imports, exports, VB6 runtime dependency, ActiveX/COM classification |
46
+ | Procedure inventory | `vb6d procs <exe>` | object table: objects and method counts, entry points verified by prologue |
47
+ | String / SQL harvest | `vb6d literals <exe or listing dir>` | `BSTR` (UTF-16LE) literals the ASCII scanners miss, with code addresses |
48
+ | SQL reconstruction | `vb6d stitch <dir or listing>` | fragments joined into whole statements, with explicit "not provable statically" buckets |
49
+ | Form design | `vb6d frm <dir>` | controls, geometry, `Visible`, control arrays, `SSTab` pages from `.frm` text |
50
+ | Form geometry from binary | `vb6d blob <exe> --validate` | geometry for forms that have no `.frm`, calibrated against `.frm` ground truth |
51
+ | DataReport layout | `vb6d datareport <dll or dir>` | report sections, controls, captions recovered from the runtime blobs |
52
+ | Form icons from `.frx` | `scripts/frx_icon.py <frx dir> <out> [--validate <frm dir>]` | standalone `.ico` per form, extracted from the leading ICON record without the executable; coverage cross-checked against `.frm` Icon references |
53
+ | Credential gate | `vb6d mask <dir>` | files rewritten in place: connection strings redacted, then an audit counting what is still live |
54
+
55
+ ## You fill these in
56
+
57
+ Every entry point takes a path; nothing is baked in. Real-target tests read five
58
+ variables and skip when they are unset.
59
+
60
+ | Variable | What you put in it |
61
+ |---|---|
62
+ | `VB6D_TARGET` | your VB6 executable, e.g. `D:/App/App.exe` or `/opt/app/App.exe` |
63
+ | `VB6D_LISTINGS` | a directory of decompiler output: listing text carrying `'VA` address comments (the shape `docs/formats.md` §4 reads), plus `.frm` and `.frx` files |
64
+ | `VB6D_FRM_DIR` | a directory holding `.frm` design text — ground truth for `--validate` |
65
+ | `VB6D_REPORT_DIR` | the install directory holding the report DLLs |
66
+ | `VB6D_SCRUB_DENYLIST` | optional: a private file, one name per line — names you refuse to publish. The hygiene test fails if any of them appears in this repository. |
67
+
68
+ Set them for one command, or export them for a session:
69
+
70
+ ```bash
71
+ # bash / zsh
72
+ export VB6D_TARGET="/opt/app/App.exe"
73
+ export VB6D_LISTINGS="$HOME/work/decompile"
74
+ export VB6D_FRM_DIR="$HOME/work/decompile"
75
+ export VB6D_REPORT_DIR="/opt/app"
76
+ ```
77
+
78
+ ```powershell
79
+ # PowerShell
80
+ $env:VB6D_TARGET = "D:/App/App.exe"
81
+ $env:VB6D_LISTINGS = "D:/work/decompile"
82
+ $env:VB6D_FRM_DIR = "D:/work/decompile"
83
+ $env:VB6D_REPORT_DIR = "D:/App"
84
+ ```
85
+
86
+ ## Why not an existing tool
87
+
88
+ Measured on the same target (`docs/tool-matrix.md` has the full table, including
89
+ how each number was obtained):
90
+
91
+ * **Semi-VB-Decompiler** — the only free tool that recovers procedure *bodies*. It
92
+ is single-threaded, overflows its `Integer` procedure counter above 32,767
93
+ procedures (`Error 6`), needs a PE `LAA` bit patch to use more than 2 GB, and
94
+ writes one event name for every body in a form. Bodies are pseudo-source
95
+ (`var_64`, `arg_C`), not compilable code.
96
+ * **VBReFormer** — forms and structure, expression bodies withheld without a
97
+ licence; cannot even accept the 111 MB executable (transport limit).
98
+ * **Ghidra / Rizin / IDA** — no VB6 knowledge: no `BSTR` literals, no procedure
99
+ table, no form blobs, no `DataReport` blobs.
100
+ * **Generic RE engines** — correct for C/C++/Rust/Go, blind to VB6 layout. One such
101
+ engine found 6,875 functions of 34,159 (20 %, stopped at its own instruction cap)
102
+ and 59 strings with zero SQL, because its string scanner does not read UTF-16LE
103
+ literals.
104
+
105
+ None of these is wrong; they are aimed at C/C++/Rust/Go binaries. The gap this
106
+ repository fills is VB6-specific structure, and it fills it with reproducible
107
+ counts rather than vendor percentages.
108
+
109
+ ## Install
110
+
111
+ ```
112
+ git clone <this repo> && cd vb6decompiler
113
+ python -m pip install -e .
114
+ ```
115
+
116
+ Python 3.11+. **Nothing outside the standard library is required** — every parser
117
+ here is hand-written, which is the point (a library wrapper would hide the layout).
118
+ `pefile` and `capstone` are optional and used only as a second opinion while
119
+ developing:
120
+
121
+ ```
122
+ python -m pip install -e ".[pe,asm]"
123
+ ```
124
+
125
+ ## Coding agents
126
+
127
+ The repository ships as a plugin for coding agents as well as a pip package, so
128
+ an agent can drive `vb6d` without a tutorial:
129
+
130
+ ```
131
+ claude plugin marketplace add 47Hunter47/vb6decompiler
132
+ claude plugin install vb6decompiler@vb6decompiler
133
+ ```
134
+
135
+ One skill lives at `.agents/skills/vb6decompiler/SKILL.md` — the location both
136
+ Claude Code and Codex read — and says when the tool applies, how to parse its
137
+ output and which traps to avoid; the plugin manifests point at that single copy.
138
+ Agents editing the repository itself follow `AGENTS.md`.
139
+
140
+ ## Quick start
141
+
142
+ ```
143
+ vb6d pe <install dir> # what is this install made of
144
+ vb6d procs <app.exe> # object table: objects, method counts
145
+ vb6d procs <app.exe> --validate # are the entry points real prologues
146
+ vb6d procs <app.exe> --names <listing dir> # attach decompiler names/addresses
147
+ vb6d literals <app.exe> # BSTR/UTF-16 literals with addresses
148
+ vb6d literals <listing dir> --sql-only # literals a decompiler listing produced
149
+ vb6d stitch <listing dir> # join fragments, classify what stayed open
150
+ vb6d frm <listing dir> # .frm controls, geometry, arrays, SSTab
151
+ vb6d blob <app.exe> --validate --frm-dir <frm dir>
152
+ vb6d datareport <install dir> # report layouts from the report DLLs
153
+ vb6d mask . # mask credentials in your artefacts
154
+ vb6d mask . --dry-run # report only; exit 1 if anything is live
155
+ ```
156
+
157
+ Exit codes: 0 = completed, whatever it found; 1 = mask audit not clean; 2 =
158
+ unusable input (one line on stderr, no traceback — set `VB6D_DEBUG=1` for the
159
+ traceback when reporting a bug).
160
+
161
+ What the reference build (111 MB, 802 forms) produced while this toolkit was
162
+ developed, so you can tell at a glance whether your own numbers look sane:
163
+
164
+ | Stage | Result |
165
+ |---|---|
166
+ | object table | 834 objects, 34,415 methods, 0.4 s |
167
+ | literals from the image | 167,564 in 4.6 s (6,171 with a SQL verb) |
168
+ | literals from a listing | 441,607 in 12,480 procedures |
169
+ | statements | 10,691 starts → 8,745 closed in 4,109 procedures, 494 tables |
170
+ | `.frm` design | 369 forms, 30,000 controls, 126 forms with control arrays |
171
+ | form blobs | 801 blobs, 24,967/24,967 controls (100 %), 367/369 forms |
172
+ | DataReports | 437 layouts (413 in report DLLs + 24 embedded in the executable) |
173
+
174
+ A different program legitimately gives different numbers. The tests assert
175
+ invariants and lower bounds, not these totals.
176
+
177
+ ## Using it well
178
+
179
+ Run the stages in pipeline order; each one tells you whether the next is worth
180
+ running. `docs/pipeline.md` states what every stage may claim and what it must
181
+ never be used to claim.
182
+
183
+ 1. `vb6d pe <install dir>` first. It costs seconds and says what you have: which
184
+ binaries carry VB6 code, which are redistributables, where the report DLLs are.
185
+ 2. `vb6d procs`, then `--validate` before trusting anything downstream. Method
186
+ counts are cheap; entry points proven by their runtime prologue are evidence.
187
+ Addresses and names only exist once a decompiler listing is merged in with
188
+ `--names`.
189
+ 3. `vb6d literals` on the executable gives UTF-16 text with addresses; on a
190
+ listing it gives procedure ownership, and that ownership is what makes
191
+ `vb6d stitch` possible at all.
192
+ 4. `vb6d stitch` reports what stayed open as open. The gap buckets are not noise:
193
+ `OPEN_QUOTE` means a run-time value is missing, and no smarter regex closes
194
+ it.
195
+ 5. `vb6d frm` reads the design text; `vb6d blob --validate` calibrates the binary
196
+ reader against it. Trust blob geometry for forms that have no `.frm` exactly
197
+ as far as that calibration holds.
198
+ 6. Count embedded reports when you run `vb6d datareport`. A scan limited to
199
+ `rpt*.dll` misses 24 of the 437 layouts the reference build carries inside its
200
+ own executable.
201
+ 7. `vb6d mask . --dry-run` before sharing anything you produced. A non-zero exit
202
+ means the artefacts are not publishable, not that you should retry quietly.
203
+
204
+ Things that cost days to learn:
205
+
206
+ * Procedure names do not exist in a native build. A decompiler's names are
207
+ guesses and `.frm` event names repeat; treat every name as a hint and every
208
+ prologue-verified address as the better fact.
209
+ * If a string scanner reports zero SQL on a VB6 binary, ask whether it reads
210
+ UTF-16LE at all. One engine reported 59 strings and zero SQL on a build that
211
+ holds 167,564 `BSTR` literals, 6,171 of them with a SQL verb.
212
+ * Compare shapes, not totals: literals per procedure, closed versus open
213
+ statements, controls per form. Totals scale with the program; the tests assert
214
+ invariants for the same reason.
215
+ * When a free decompiler refuses a large executable, run `scripts/semi_laa.py`
216
+ on a copy. The Large Address Aware bit is a property of the file, not of the
217
+ tool's competence.
218
+ * Three-state flags such as `Visible` and `Locked` have defaults that look like
219
+ explicit values, and `docs/pitfalls.md` lists that class of trap with the
220
+ measurement that proved each one.
221
+
222
+ ## Honest limits
223
+
224
+ * Procedure **names** are not recoverable from a native build; only addresses and
225
+ the names a decompiler guessed.
226
+ * Bodies recovered by a third-party decompiler are pseudo-source. Expression-level
227
+ fidelity does not exist in any free tool measured here.
228
+ * SQL that is built at run time (concatenated from variables) cannot be closed
229
+ statically. `vb6d stitch` reports those gaps as gaps instead of guessing.
230
+ * Geometry recovered from the binary is calibrated, not exact for forms that
231
+ changed after the `.frm` in hand was written; `--validate` says so per form.
232
+ * The toolkit is calibrated on native-compiled builds; a P-Code build is outside
233
+ that calibration and was not tested here. If `vb6d procs --validate` reports no
234
+ real prologues in your build, suspect a P-Code compile before suspecting the
235
+ toolkit.
236
+ * The parsers know the grammar of the reference build. Another program ships
237
+ different controls, property sets and report sections, so expect more `unknown`
238
+ lines than the reference numbers show. An un-calibrated construct is reported as
239
+ unknown, never guessed (`docs/formats.md` §7), and no stage drops a file
240
+ silently: failures are counted and named.
241
+
242
+ ## Layout
243
+
244
+ ```
245
+ vb6decompiler/ the library (one module per stage, stdlib only except pefile/capstone)
246
+ scripts/ standalone helpers (semi_laa.py: set the LAA bit on a copy;
247
+ frx_icon.py: extract the leading ICON record of a .frx;
248
+ win32_gui_probe.py: drive vendor GUI tools headlessly when the
249
+ measurement only exists in a window)
250
+ tests/ unit tests + target tests that skip unless VB6D_TARGET is set
251
+ docs/ formats.md (structures), pitfalls.md (traps), tool-matrix.md,
252
+ pipeline.md (stages and what each may claim), RULES.md
253
+ .agents/skills/ the vb6decompiler skill — what a coding agent installs
254
+ .claude-plugin/ Claude Code plugin + marketplace manifests (skills point at it)
255
+ .codex-plugin/ Codex plugin manifest (same single skill copy)
256
+ ```
257
+
258
+ Run the tests:
259
+
260
+ ```
261
+ pip install -e ".[dev]"
262
+ pytest -q # unit tests, no target needed
263
+ VB6D_TARGET="C:/path/App.exe" pytest -q # + real-target invariants
264
+ VB6D_LISTINGS="C:/path/decompile" pytest -q # + listing/stitching tests
265
+ VB6D_FRM_DIR="C:/path/decompile" pytest -q # + blob calibration
266
+ VB6D_REPORT_DIR="C:/path/install" pytest -q # + DataReport parsing
267
+ ```
268
+
269
+ `docs/formats.md` is the structure reference: every offset, every grammar, and the
270
+ fields that look documented but are not what they claim (`lpMethods` in a native
271
+ build, `lpGuiTable`). `docs/pitfalls.md` is the second most useful file: it lists
272
+ the traps that cost days (double-`Integer` overflow, `OriginalFirstThunk` vs
273
+ `FirstThunk`, CP1254 vs UTF-16, `Visible` defaults, blob property sorting) with the
274
+ measurement that
275
+ proved each one.
276
+
277
+ ## Licence
278
+
279
+ MIT. See `LICENSE`.
@@ -0,0 +1,260 @@
1
+ # vb6decompiler
2
+
3
+ Recovering what a native-compiled VB6 program actually does, with evidence.
4
+
5
+ Commercial VB6 decompilers stop at one of two walls: they give you forms without
6
+ code, or code without the semantics that make it portable. This toolkit was built
7
+ while recovering a large native-compiled VB6 application (111 MB, ~34,000
8
+ procedures) whose source was lost. Every claim below is a measurement, and every
9
+ measurement has a command that reproduces it.
10
+
11
+ The point is not another disassembler. VB6 is not plain x86 at the level that
12
+ matters: strings are length-prefixed `BSTR`s stored inside `.text`, procedures are
13
+ entered through a fixed runtime prologue, most calls go through `MSVBVM60` vtable
14
+ dispatch, forms are serialised into the executable as binary property blobs, and
15
+ reports live in separate VB6 DLLs as `DataReport` blobs. Off-the-shelf tools read
16
+ none of that. These modules do.
17
+
18
+ Nothing here points at the program it was developed against: no target name, no
19
+ host, no database name, no account, no credential, no absolute install path.
20
+ **You supply every path.**
21
+
22
+ ## What it does
23
+
24
+ | Stage | Command | Evidence it produces |
25
+ |---|---|---|
26
+ | PE surface of an install | `vb6d pe <dir>` | imports, exports, VB6 runtime dependency, ActiveX/COM classification |
27
+ | Procedure inventory | `vb6d procs <exe>` | object table: objects and method counts, entry points verified by prologue |
28
+ | String / SQL harvest | `vb6d literals <exe or listing dir>` | `BSTR` (UTF-16LE) literals the ASCII scanners miss, with code addresses |
29
+ | SQL reconstruction | `vb6d stitch <dir or listing>` | fragments joined into whole statements, with explicit "not provable statically" buckets |
30
+ | Form design | `vb6d frm <dir>` | controls, geometry, `Visible`, control arrays, `SSTab` pages from `.frm` text |
31
+ | Form geometry from binary | `vb6d blob <exe> --validate` | geometry for forms that have no `.frm`, calibrated against `.frm` ground truth |
32
+ | DataReport layout | `vb6d datareport <dll or dir>` | report sections, controls, captions recovered from the runtime blobs |
33
+ | Form icons from `.frx` | `scripts/frx_icon.py <frx dir> <out> [--validate <frm dir>]` | standalone `.ico` per form, extracted from the leading ICON record without the executable; coverage cross-checked against `.frm` Icon references |
34
+ | Credential gate | `vb6d mask <dir>` | files rewritten in place: connection strings redacted, then an audit counting what is still live |
35
+
36
+ ## You fill these in
37
+
38
+ Every entry point takes a path; nothing is baked in. Real-target tests read five
39
+ variables and skip when they are unset.
40
+
41
+ | Variable | What you put in it |
42
+ |---|---|
43
+ | `VB6D_TARGET` | your VB6 executable, e.g. `D:/App/App.exe` or `/opt/app/App.exe` |
44
+ | `VB6D_LISTINGS` | a directory of decompiler output: listing text carrying `'VA` address comments (the shape `docs/formats.md` §4 reads), plus `.frm` and `.frx` files |
45
+ | `VB6D_FRM_DIR` | a directory holding `.frm` design text — ground truth for `--validate` |
46
+ | `VB6D_REPORT_DIR` | the install directory holding the report DLLs |
47
+ | `VB6D_SCRUB_DENYLIST` | optional: a private file, one name per line — names you refuse to publish. The hygiene test fails if any of them appears in this repository. |
48
+
49
+ Set them for one command, or export them for a session:
50
+
51
+ ```bash
52
+ # bash / zsh
53
+ export VB6D_TARGET="/opt/app/App.exe"
54
+ export VB6D_LISTINGS="$HOME/work/decompile"
55
+ export VB6D_FRM_DIR="$HOME/work/decompile"
56
+ export VB6D_REPORT_DIR="/opt/app"
57
+ ```
58
+
59
+ ```powershell
60
+ # PowerShell
61
+ $env:VB6D_TARGET = "D:/App/App.exe"
62
+ $env:VB6D_LISTINGS = "D:/work/decompile"
63
+ $env:VB6D_FRM_DIR = "D:/work/decompile"
64
+ $env:VB6D_REPORT_DIR = "D:/App"
65
+ ```
66
+
67
+ ## Why not an existing tool
68
+
69
+ Measured on the same target (`docs/tool-matrix.md` has the full table, including
70
+ how each number was obtained):
71
+
72
+ * **Semi-VB-Decompiler** — the only free tool that recovers procedure *bodies*. It
73
+ is single-threaded, overflows its `Integer` procedure counter above 32,767
74
+ procedures (`Error 6`), needs a PE `LAA` bit patch to use more than 2 GB, and
75
+ writes one event name for every body in a form. Bodies are pseudo-source
76
+ (`var_64`, `arg_C`), not compilable code.
77
+ * **VBReFormer** — forms and structure, expression bodies withheld without a
78
+ licence; cannot even accept the 111 MB executable (transport limit).
79
+ * **Ghidra / Rizin / IDA** — no VB6 knowledge: no `BSTR` literals, no procedure
80
+ table, no form blobs, no `DataReport` blobs.
81
+ * **Generic RE engines** — correct for C/C++/Rust/Go, blind to VB6 layout. One such
82
+ engine found 6,875 functions of 34,159 (20 %, stopped at its own instruction cap)
83
+ and 59 strings with zero SQL, because its string scanner does not read UTF-16LE
84
+ literals.
85
+
86
+ None of these is wrong; they are aimed at C/C++/Rust/Go binaries. The gap this
87
+ repository fills is VB6-specific structure, and it fills it with reproducible
88
+ counts rather than vendor percentages.
89
+
90
+ ## Install
91
+
92
+ ```
93
+ git clone <this repo> && cd vb6decompiler
94
+ python -m pip install -e .
95
+ ```
96
+
97
+ Python 3.11+. **Nothing outside the standard library is required** — every parser
98
+ here is hand-written, which is the point (a library wrapper would hide the layout).
99
+ `pefile` and `capstone` are optional and used only as a second opinion while
100
+ developing:
101
+
102
+ ```
103
+ python -m pip install -e ".[pe,asm]"
104
+ ```
105
+
106
+ ## Coding agents
107
+
108
+ The repository ships as a plugin for coding agents as well as a pip package, so
109
+ an agent can drive `vb6d` without a tutorial:
110
+
111
+ ```
112
+ claude plugin marketplace add 47Hunter47/vb6decompiler
113
+ claude plugin install vb6decompiler@vb6decompiler
114
+ ```
115
+
116
+ One skill lives at `.agents/skills/vb6decompiler/SKILL.md` — the location both
117
+ Claude Code and Codex read — and says when the tool applies, how to parse its
118
+ output and which traps to avoid; the plugin manifests point at that single copy.
119
+ Agents editing the repository itself follow `AGENTS.md`.
120
+
121
+ ## Quick start
122
+
123
+ ```
124
+ vb6d pe <install dir> # what is this install made of
125
+ vb6d procs <app.exe> # object table: objects, method counts
126
+ vb6d procs <app.exe> --validate # are the entry points real prologues
127
+ vb6d procs <app.exe> --names <listing dir> # attach decompiler names/addresses
128
+ vb6d literals <app.exe> # BSTR/UTF-16 literals with addresses
129
+ vb6d literals <listing dir> --sql-only # literals a decompiler listing produced
130
+ vb6d stitch <listing dir> # join fragments, classify what stayed open
131
+ vb6d frm <listing dir> # .frm controls, geometry, arrays, SSTab
132
+ vb6d blob <app.exe> --validate --frm-dir <frm dir>
133
+ vb6d datareport <install dir> # report layouts from the report DLLs
134
+ vb6d mask . # mask credentials in your artefacts
135
+ vb6d mask . --dry-run # report only; exit 1 if anything is live
136
+ ```
137
+
138
+ Exit codes: 0 = completed, whatever it found; 1 = mask audit not clean; 2 =
139
+ unusable input (one line on stderr, no traceback — set `VB6D_DEBUG=1` for the
140
+ traceback when reporting a bug).
141
+
142
+ What the reference build (111 MB, 802 forms) produced while this toolkit was
143
+ developed, so you can tell at a glance whether your own numbers look sane:
144
+
145
+ | Stage | Result |
146
+ |---|---|
147
+ | object table | 834 objects, 34,415 methods, 0.4 s |
148
+ | literals from the image | 167,564 in 4.6 s (6,171 with a SQL verb) |
149
+ | literals from a listing | 441,607 in 12,480 procedures |
150
+ | statements | 10,691 starts → 8,745 closed in 4,109 procedures, 494 tables |
151
+ | `.frm` design | 369 forms, 30,000 controls, 126 forms with control arrays |
152
+ | form blobs | 801 blobs, 24,967/24,967 controls (100 %), 367/369 forms |
153
+ | DataReports | 437 layouts (413 in report DLLs + 24 embedded in the executable) |
154
+
155
+ A different program legitimately gives different numbers. The tests assert
156
+ invariants and lower bounds, not these totals.
157
+
158
+ ## Using it well
159
+
160
+ Run the stages in pipeline order; each one tells you whether the next is worth
161
+ running. `docs/pipeline.md` states what every stage may claim and what it must
162
+ never be used to claim.
163
+
164
+ 1. `vb6d pe <install dir>` first. It costs seconds and says what you have: which
165
+ binaries carry VB6 code, which are redistributables, where the report DLLs are.
166
+ 2. `vb6d procs`, then `--validate` before trusting anything downstream. Method
167
+ counts are cheap; entry points proven by their runtime prologue are evidence.
168
+ Addresses and names only exist once a decompiler listing is merged in with
169
+ `--names`.
170
+ 3. `vb6d literals` on the executable gives UTF-16 text with addresses; on a
171
+ listing it gives procedure ownership, and that ownership is what makes
172
+ `vb6d stitch` possible at all.
173
+ 4. `vb6d stitch` reports what stayed open as open. The gap buckets are not noise:
174
+ `OPEN_QUOTE` means a run-time value is missing, and no smarter regex closes
175
+ it.
176
+ 5. `vb6d frm` reads the design text; `vb6d blob --validate` calibrates the binary
177
+ reader against it. Trust blob geometry for forms that have no `.frm` exactly
178
+ as far as that calibration holds.
179
+ 6. Count embedded reports when you run `vb6d datareport`. A scan limited to
180
+ `rpt*.dll` misses 24 of the 437 layouts the reference build carries inside its
181
+ own executable.
182
+ 7. `vb6d mask . --dry-run` before sharing anything you produced. A non-zero exit
183
+ means the artefacts are not publishable, not that you should retry quietly.
184
+
185
+ Things that cost days to learn:
186
+
187
+ * Procedure names do not exist in a native build. A decompiler's names are
188
+ guesses and `.frm` event names repeat; treat every name as a hint and every
189
+ prologue-verified address as the better fact.
190
+ * If a string scanner reports zero SQL on a VB6 binary, ask whether it reads
191
+ UTF-16LE at all. One engine reported 59 strings and zero SQL on a build that
192
+ holds 167,564 `BSTR` literals, 6,171 of them with a SQL verb.
193
+ * Compare shapes, not totals: literals per procedure, closed versus open
194
+ statements, controls per form. Totals scale with the program; the tests assert
195
+ invariants for the same reason.
196
+ * When a free decompiler refuses a large executable, run `scripts/semi_laa.py`
197
+ on a copy. The Large Address Aware bit is a property of the file, not of the
198
+ tool's competence.
199
+ * Three-state flags such as `Visible` and `Locked` have defaults that look like
200
+ explicit values, and `docs/pitfalls.md` lists that class of trap with the
201
+ measurement that proved each one.
202
+
203
+ ## Honest limits
204
+
205
+ * Procedure **names** are not recoverable from a native build; only addresses and
206
+ the names a decompiler guessed.
207
+ * Bodies recovered by a third-party decompiler are pseudo-source. Expression-level
208
+ fidelity does not exist in any free tool measured here.
209
+ * SQL that is built at run time (concatenated from variables) cannot be closed
210
+ statically. `vb6d stitch` reports those gaps as gaps instead of guessing.
211
+ * Geometry recovered from the binary is calibrated, not exact for forms that
212
+ changed after the `.frm` in hand was written; `--validate` says so per form.
213
+ * The toolkit is calibrated on native-compiled builds; a P-Code build is outside
214
+ that calibration and was not tested here. If `vb6d procs --validate` reports no
215
+ real prologues in your build, suspect a P-Code compile before suspecting the
216
+ toolkit.
217
+ * The parsers know the grammar of the reference build. Another program ships
218
+ different controls, property sets and report sections, so expect more `unknown`
219
+ lines than the reference numbers show. An un-calibrated construct is reported as
220
+ unknown, never guessed (`docs/formats.md` §7), and no stage drops a file
221
+ silently: failures are counted and named.
222
+
223
+ ## Layout
224
+
225
+ ```
226
+ vb6decompiler/ the library (one module per stage, stdlib only except pefile/capstone)
227
+ scripts/ standalone helpers (semi_laa.py: set the LAA bit on a copy;
228
+ frx_icon.py: extract the leading ICON record of a .frx;
229
+ win32_gui_probe.py: drive vendor GUI tools headlessly when the
230
+ measurement only exists in a window)
231
+ tests/ unit tests + target tests that skip unless VB6D_TARGET is set
232
+ docs/ formats.md (structures), pitfalls.md (traps), tool-matrix.md,
233
+ pipeline.md (stages and what each may claim), RULES.md
234
+ .agents/skills/ the vb6decompiler skill — what a coding agent installs
235
+ .claude-plugin/ Claude Code plugin + marketplace manifests (skills point at it)
236
+ .codex-plugin/ Codex plugin manifest (same single skill copy)
237
+ ```
238
+
239
+ Run the tests:
240
+
241
+ ```
242
+ pip install -e ".[dev]"
243
+ pytest -q # unit tests, no target needed
244
+ VB6D_TARGET="C:/path/App.exe" pytest -q # + real-target invariants
245
+ VB6D_LISTINGS="C:/path/decompile" pytest -q # + listing/stitching tests
246
+ VB6D_FRM_DIR="C:/path/decompile" pytest -q # + blob calibration
247
+ VB6D_REPORT_DIR="C:/path/install" pytest -q # + DataReport parsing
248
+ ```
249
+
250
+ `docs/formats.md` is the structure reference: every offset, every grammar, and the
251
+ fields that look documented but are not what they claim (`lpMethods` in a native
252
+ build, `lpGuiTable`). `docs/pitfalls.md` is the second most useful file: it lists
253
+ the traps that cost days (double-`Integer` overflow, `OriginalFirstThunk` vs
254
+ `FirstThunk`, CP1254 vs UTF-16, `Visible` defaults, blob property sorting) with the
255
+ measurement that
256
+ proved each one.
257
+
258
+ ## Licence
259
+
260
+ MIT. See `LICENSE`.
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "vb6decompiler"
7
+ version = "0.2.0"
8
+ description = "Evidence-based recovery of native-compiled VB6 programs: procedures, literals, SQL, form blobs, DataReports"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "vb6decompiler authors" }]
13
+ keywords = ["vb6", "reverse-engineering", "decompiler", "pe", "sql-server"]
14
+
15
+ [project.urls]
16
+ Homepage = "https://github.com/47Hunter47/vb6decompiler"
17
+
18
+ [project.optional-dependencies]
19
+ pe = ["pefile>=2023.2.7"]
20
+ asm = ["capstone>=5"]
21
+ dev = ["pytest>=8"]
22
+
23
+ [project.scripts]
24
+ vb6d = "vb6decompiler.cli:main"
25
+
26
+ [tool.setuptools.packages.find]
27
+ include = ["vb6decompiler*"]
28
+
29
+ [tool.pytest.ini_options]
30
+ testpaths = ["tests"]
31
+ addopts = "-q"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+