Commit d448c27
authored
feat: structural validator expansion — exports, delay-load, VS_VERSIONINFO, resource hierarchy (v0.7.5)
Substantial expansion of IOCX's structural validator suite. Four new
parser/validator pairs (exports, delay-load imports, VS_VERSIONINFO,
resource hierarchy), 24 new reason codes, security-relevant metadata
enrichment on Optional Header and per-resource fields. 1370 tests at
100% coverage.
## Structural validators
* pe_exports / exports: 40-byte IMAGE_EXPORT_DIRECTORY, EAT, ENPT, EOT
decoded from bytes via struct.unpack_from. Forwarder detection follows
the PE-spec rule (address RVA within export directory range).
* parser_delay_imports / validator_delay_imports: 32-byte
IMAGE_DELAY_IMPORT_DESCRIPTOR, INT, IAT decoded from bytes. PE32+/PE32
thunk sizing determined from OPTIONAL_HEADER.Magic; v1/v0 attribute
mode captured explicitly; bound state detected by bound_iat_rva != 0.
* pe_version_info / validator_version_info: envelope, VS_FIXEDFILEINFO,
StringFileInfo, VarFileInfo decoded from bytes. Deterministic leaf
selection by (name_id, language_id) across multiple RT_VERSION entries.
* Resource hierarchy enforcement: resource validator now tracks tree
depth and enforces Type → Name → Language layering.
All new parsers never raise; sub-structure failures emit tombstone tags
in truncations[] and per-entry errors[]. Validators use priority-resolved
sub-reasons via details["reason"], scoped by details["table"] where
applicable. Absence of any of these directories is not treated as a
structural defect.
## Reason codes (24 new)
Resource hierarchy: RESOURCE_DIRECTORY_LANGUAGE_NOT_ID,
RESOURCE_DATA_AT_INVALID_DEPTH.
VS_VERSIONINFO: RESOURCE_VERSIONINFO_INVALID_{HEADER, FIXEDINFO,
STRINGFILEINFO, VARFILEINFO}.
Exports: EXPORT_DIRECTORY_INVALID_HEADER, EXPORT_DIRECTORY_OUT_OF_BOUNDS,
EXPORT_TABLE_TRUNCATED, EXPORT_NAME_RVA_INVALID, EXPORT_NAME_NOT_ASCII,
EXPORT_NAME_POINTER_TABLE_UNSORTED, EXPORT_NAME_ORDINAL_INDEX_INVALID,
EXPORT_ORDINAL_OUT_OF_RANGE, EXPORT_FUNCTION_RVA_INVALID,
EXPORT_FORWARDER_MALFORMED.
Delay-load: DELAY_IMPORT_DIRECTORY_INVALID_HEADER,
DELAY_IMPORT_DIRECTORY_OUT_OF_BOUNDS, DELAY_IMPORT_TABLE_TRUNCATED,
DELAY_IMPORT_DESCRIPTOR_INVALID, DELAY_IMPORT_DLL_NAME_INVALID,
DELAY_IMPORT_INT_IAT_MISMATCH, DELAY_IMPORT_ATTRIBUTES_LEGACY_VA_MODE,
DELAY_IMPORT_ENTRY_INVALID.
## Public metadata enrichment
Optional Header gains dll_characteristics (raw + decoded flags + unknown
bits), win32_version_value, loader_flags, stack_reserve_size,
stack_commit_size, heap_reserve_size, heap_commit_size.
Header block gains subsystem_name (from IMAGE_SUBSYSTEM_*) and
machine_name (from IMAGE_FILE_MACHINE_*; table covers all 29 documented
machine types).
Resources now expose a structured ResourceEntry per resource with type,
name, language, language_name, codepage, size, entropy, rva, raw_offset,
and per-entry errors. Entropy rounded to 4 dp for snapshot stability;
output sorted by (type, language, rva). Resources with unreadable data
now emitted with error tombstones (size_invalid, rva_invalid,
data_out_of_bounds, raw_offset_invalid) rather than silently dropped.
## Schema typing
New TypedDicts: ExportStruct, DelayImportStruct, VersionInfoStruct with
sub-types. InternalMetadata gains export_struct, delay_import_struct,
version_info_struct as Optional[...]. New constants module
iocx.parsers.pe_constants with SUBSYSTEM_NAMES, MACHINE_NAMES,
DLL_CHARACTERISTICS_FLAGS, DLL_CHARACTERISTICS_KNOWN_MASK.
## Dispatcher order
validate_resources → validate_version_info → validate_exports →
validate_delay_imports → validate_entropy.
## Fixes and refactors
* Resource parser now guards pe.get_offset_from_rva against
pefile.PEFormatError and AttributeError; a corrupt RVA produces a -1
sentinel that flows through the validator's existing data_raw < 0
arm. No new reason code.
* Resource entropy now sliced from get_memory_mapped_image() using RVA
(correct) rather than raw file offset. Caught before snapshot
stamping.
* Resource validator no longer silently returns when a directory's own
RVA falls outside .rsrc.
* _decode_langid returns None for undecodable LANGIDs (previously the
magic string "unknown"). Removed the < 0x0400 early-return guard that
was rejecting valid neutral-sublang LANGIDs.
* analyse_extended refactored: removed duplicated _SUBSYSTEM_MAP and
_MACHINE_MAP tables (parser layer is now single source of truth),
removed legacy subsystem_human and machine_human fields, and made
resource entropy aggregates tolerate entries with entropy: None.
## Breaking changes
* Consumers checking language_name == "unknown" must update to
language_name is None.
* subsystem_human and machine_human removed from extended metadata;
use subsystem_name and machine_name (parser layer).
* Optional Header default convention: new fields use None for missing
(distinct from 0). Existing Optional Header fields retain default-to-
zero behaviour for backward compatibility.
* Snapshot refresh required for fixtures with resources, optional
header, or extended metadata. Mechanical via existing regeneration
tooling.
## Coverage and verification
* 100% line and branch coverage on all new modules.
* Defensive-path coverage via monkeypatched struct.error injection.
One # pragma: no cover applied to a defensive return in the exports
validator's _first_unsorted_index helper (documented unreachable).
* Delay-load parser cross-checked against dumpbin /imports on
mspaint.exe: 107 imports from gdiplus.dll with byte-exact agreement
on DLL name, hint values, IAT addresses, ordering, and bound state.
* ~650 new tests bringing suite total to 1370 tests.
## Architectural notes
Producer-facing structural metadata (export_struct, delay_import_struct,
Load Config) is internal by design, feeding validators and heuristics
rather than the public IOC schema. Consumers needing structural
information rely on validator reason codes and the existing consumer-
facing metadata fields (exports, delayed_imports, etc.).
Public schema promotion of version_info_struct (containing consumer-
facing metadata like CompanyName, ProductVersion, OriginalFilename) is
deferred to a coordinated future release with corresponding fixture
corpus refresh.
## Deferred to next release
* TLS Directory parser and validator (originally deferred from this
release).
* ~25 single-anomaly fixtures targeting the new reason codes, including
negative-control fixtures.
* Cross-tool divergence study using the new fixtures, particularly for
delay-load's three spec-interpretation questions (v0/v1 mode, INT/IAT
mirror vs bound assumption, declared-size vs walk-to-terminator).
* SUBLANG table refinement for multilingual LCID edge cases.
* Heuristic work items filed: CFG declaration consistency,
implausibility framework, intra-field DllCharacteristics flag
dependencies.1 parent 22f6057 commit d448c27
121 files changed
Lines changed: 12289 additions & 906 deletions
File tree
- docs/specs
- iocx
- analysis
- parsers
- schemas
- validators
- tests
- contract/snapshots
- layer1_core
- layer2_edge
- layer3_adversarial
- unit
- analysis
- parsers
- validators
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 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 | + | |
1 | 123 | | |
| 124 | + | |
2 | 125 | | |
3 | 126 | | |
4 | 127 | | |
| |||
19 | 142 | | |
20 | 143 | | |
21 | 144 | | |
| 145 | + | |
22 | 146 | | |
23 | 147 | | |
24 | 148 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
43 | 50 | | |
44 | 51 | | |
45 | 52 | | |
46 | 53 | | |
47 | 54 | | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
| 55 | + | |
58 | 56 | | |
59 | 57 | | |
60 | 58 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
| 12 | + | |
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| |||
202 | 202 | | |
203 | 203 | | |
204 | 204 | | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
205 | 214 | | |
206 | 215 | | |
207 | 216 | | |
| |||
0 commit comments