1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//! Parse and inspect Visual Basic 6 compiled binaries.
//!
//! This crate provides typed access to all internal structures within a
//! VB6 compiled executable, from the PE entry point down to individual
//! P-Code bytecode instructions.
//!
//! # Quick Start
//!
//! ```no_run
//! use visualbasic::VbProject;
//!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let file_bytes = std::fs::read("sample.exe")?;
//! let project = VbProject::from_bytes(&file_bytes)?;
//!
//! println!("Objects: {}", project.object_count()?);
//! for obj in project.objects()? {
//! let obj = obj?;
//! println!(" Object: {:?}", obj.name()?);
//! }
//! # Ok(())
//! # }
//! ```
//!
//! # Architecture
//!
//! The crate is organized in layers:
//!
//! - **Address translation** ([`addressmap::AddressMap`]): Converts VAs/RVAs to file offsets
//! using section tables from [`goblin`].
//! - **VB structures** ([`vb`]): View types for each structure in the VB6
//! internal format (VBHeader, ProjectData, ObjectTable, etc.).
//! - **P-Code decoding** ([`pcode`]): Opcode tables, operand types, and a streaming
//! instruction iterator.
//! - **High-level API** ([`VbProject`]): Ties everything together into a convenient
//! exploration interface.
//!
//! # Design
//!
//! All structure types borrow from the original file byte slice (`&'a [u8]`).
//! Accessor methods read directly from the underlying buffer using
//! little-endian byte decoding.
//!
//! # Robustness contract
//!
//! VB6 binaries from the wild include malware samples that may be truncated,
//! have inconsistent structure-size fields, or carry intentionally adversarial
//! VAs. Every public API in this crate falls into one of three behavioural
//! categories — pick the one that matches your call site:
//!
//! ## 1. Fail-loud at the byte boundary (primitive accessors)
//!
//! Every fixed-offset accessor like
//! [`VbHeader::project_data_va`](crate::vb::header::VbHeader::project_data_va),
//! [`ProcDscInfo::frame_size`](crate::vb::procedure::ProcDscInfo::frame_size), or
//! [`VbObject::name_bytes`](crate::project::VbObject::name_bytes) returns
//! `Result<T, Error>`. The crate's underlying byte readers are
//! panic-free (no `unwrap`, no panicking indexing, no unchecked arithmetic);
//! out-of-buffer reads surface as
//! [`Error::Truncated`] and offset overflows as [`Error::ArithmeticOverflow`].
//! Use `?` to propagate.
//!
//! ## 2. Skip-and-continue (per-entry iterators)
//!
//! Iterators like
//! [`InstructionIterator`](crate::pcode::decoder::InstructionIterator),
//! [`PCodeMethodIterator`](crate::project::PCodeMethodIterator), and
//! [`ConstPoolIter`](crate::vb::constantpool::ConstPoolIter) yield
//! `Item = Result<T, Error>` per entry — they emit one `Err` per malformed
//! row and *keep going*, so a single bad entry does not poison the whole
//! sweep. Match on each `Item` to pull successes, log failures, or stop
//! early on first error per your policy. Iteration ends only when the
//! underlying byte stream is exhausted (or a structural-truncation
//! `Err` is yielded — most iterators continue past per-entry errors).
//!
//! ## 3. Silent fail-soft (high-level joins)
//!
//! High-level convenience methods like
//! [`VbObject::events`](crate::project::VbObject::events),
//! [`VbProject::code_entrypoints`](crate::project::VbProject::code_entrypoints),
//! and the [`pcode::calltarget`] resolvers eagerly
//! collect from one or more underlying iterators and **silently drop**
//! malformed rows (`filter_map(|r| r.ok())`) so the returned `Vec` is
//! "everything that parsed". This matches malware-analysis triage: one
//! corrupt control should not block enumeration of the other 50.
//! Enable the optional `tracing` feature to capture the dropped rows
//! as `target = "visualbasic::dropped"` `warn` events.
//!
//! ## 4. Recognition-time errors at [`VbProject::from_bytes`]
//!
//! Parse failures during the recognition phase are classified by
//! [`Error::recognition_failure`] into one of:
//!
//! - [`RecognitionFailure::NotRecognized`] — valid PE but no VB6 marker.
//! - [`RecognitionFailure::TruncatedContainer`] — looks VB6 but truncated.
//! - [`RecognitionFailure::UnrecognizedFormat`] — not a PE container at all.
//!
//! Consumers tagging files as "VB6 or not" should match on this
//! classification to silently skip non-VB6 files and only log the
//! `TruncatedContainer` cases.
//!
//! # Adversarial input invariants
//!
//! The crate is built under the lint set
//! `clippy::{unwrap_used, expect_used, panic, arithmetic_side_effects,
//! indexing_slicing}` — every byte read goes through a checked helper, every
//! offset computation uses `checked_add` / `wrapping_add` / `saturating_add`
//! depending on semantics, and every slice access uses `.get(...)` rather
//! than `[]`. **No input byte sequence can panic this parser.** Tests are
//! exempt from these lints.
// The `missing_docs`, `unsafe_code`, `clippy::unwrap_used`,
// `clippy::expect_used`, `clippy::panic`,
// `clippy::arithmetic_side_effects`, and `clippy::indexing_slicing` lints
// are declared in `Cargo.toml` under `[lints]` so they enforce on every
// build regardless of the consuming workspace. visualbasic is used in
// malware-analysis pipelines where every input byte is adversarial and the
// parser must not panic.
/// Internal re-export of [`tracing`] for use by [`crate::trace::warn_drop`].
///
/// Hidden from rustdoc; consumers should depend on `tracing` directly.
/// The re-export lets the macro emit `$crate::__tracing::warn!(...)`
/// instead of an absolute `::tracing::warn!(...)` path, satisfying the
/// project's no-absolute-paths guideline while preserving macro hygiene
/// (the macro can be expanded from any module without requiring
/// `tracing` to be in scope at the call site).
pub use tracing as __tracing;
// Re-export primary types at crate root.
pub use AddressMap;
pub use ;
pub use ;
// Thread-safety guarantee: VbProject and PCodeMethod borrow from a `&[u8]`
// file buffer, hold no interior mutability, and contain no raw pointers or
// `Cell`/`RefCell`. They are therefore both `Send` and `Sync` whenever the
// borrowed buffer is — i.e., always, for any `&'a [u8]` input. This static
// assertion makes that guarantee a compile-time invariant: a future change
// that adds a non-Send/non-Sync field will break the build here, not silently
// at a downstream `.await` point.
const _: fn = ;