[GH-ISSUE #2811] [Feature] Recover Delphi VMT class graph and published methods/fields statically (and emit Ghidra seeds) #1003

Open
opened 2026-10-15 18:30:16 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @matbrgz on GitHub (Oct 13, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/2811

Type: Feature Domain: Reverse Engineering Status: Proposed Ecosystem: REA

Before submitting

  • I searched the open and closed issues and pull requests (searched Delphi, VMT, RTTI, published, Borland). Related but different: #2373 (Delphi MAP files, which most shipped Delphi binaries do not include), #1667 (REA_GHIDRA_SEED_FILE / borlandcpp compiler spec, which this proposal would feed).
  • I understand the contribution scope must be reviewed before integration.
  • This request does not include credentials, private samples, or sensitive target data.

Area

Native analysis or decompilation

Current limitation

Shipped Delphi executables have no PDB and usually no MAP file, so a native provider sees thousands of FUN_xxxxxxxx functions. Yet every Delphi 2–2007 Win32 binary carries a self-describing class graph: each VMT has fixed negative offsets (vmtSelfPtr = -76, vmtFieldTable = -56, vmtMethodTable = -52, vmtClassName = -44, vmtParent = -36 in Delphi 7) that give class name, parent class, published method table (name → code address) and published field table (offset → component name/class). These are exactly the names an analyst needs (TFCadAPAC.bitsalvarClick, TDm.ConnBeforeConnect, [eax+0x3b0] → edtCNS).

REA 6.4.0 has no lane for this. rea providers --json / rea capabilities --json list no Delphi metadata operation, and the source tree has no VMT/RTTI parser. The only way to get those names into a Ghidra session today is to recover them with an external script and hand-write a REA_GHIDRA_SEED_FILE.

Environment: macOS 27.0.1 arm64, Node 24.13.0, rea-agents@6.4.0 (local install, no agent setup).

Proposed outcome

A provider-free, static inspect_delphi_classes (name open) for PE32 images:

  • Scan sections for VMT candidates by validating vmtSelfPtr (points back to the VMT) and a printable ShortString class name; walk vmtParent to build the class tree.
  • Decode the published method table (name + code address) and published field table (offset + name + class index), keeping file offsets and RVAs for every fact.
  • Make the VMT layout an explicit, recorded profile (Delphi 2–2007 Win32 first); report other versions/layouts as unsupported instead of guessing.
  • Emit an optional seed/label projection compatible with REA_GHIDRA_SEED_FILE (address<TAB>function<TAB>Class.Method), so the Ghidra provider imports the names with provenance.
  • Out of scope for this issue: Delphi 2009+ extended RTTI, Win64, runtime evaluation (#2375).

Use cases

Reverse engineering legacy Delphi line-of-business software (public-sector health systems in Brazil, ERP, banking). On the sample below, a reference implementation recovers 860 classes and 1,608 published methods, which turn an unnamed binary into one where every UI event handler has a name.

Minimal public repro

  • ftp://arpoador.datasus.gov.br/siasus/autorizador/AUTORIZADOR_SETUP.exe (sha256 226e68e4e5c2ef3d8749bdc788481f80eb79d5ac13dc51c89ecdc1cf96082aaa), innoextract -e, target app/AUTORIZADOR.exe (sha256 708b974938d3028f158d78162636ab0a25dcc3a06540fae2aaea02670ec22f4f, PE32, Delphi 7, ImageBase 0x400000).
  • Newer build: AUTORIZADOR_VER6630.exe (sha256 0c79af43ce52a9317591e081ebfc9f29f70d5575033b2eb9e177d0828a634c44), inner exe sha256 1398d55a…8fa363.
  • Expected anchors on Delphi 7 builds: TObject VMT at 0x401190, TInterfacedObject at 0x4012b4.

Suggested approach

Pure static parser over the PE snapshot (no provider needed), returning Evidence like inspect_pe_resources. Pair it with the TPF0 form decoder (#2810): form OnClick = bitsalvarClick + VMT method table → address gives handler-to-control wiring.

Originally created by @matbrgz on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2811 [![Type: Feature](https://img.shields.io/badge/type-feature-blue.svg?style=flat-square)](https://github.com/morluto/rea) [![Domain: Reverse Engineering](https://img.shields.io/badge/domain-reverse_engineering-blueviolet.svg?style=flat-square)](https://github.com/morluto/rea) [![Status: Proposed](https://img.shields.io/badge/status-proposed-blue.svg?style=flat-square)](https://github.com/morluto/rea) [![Ecosystem: REA](https://img.shields.io/badge/ecosystem-morluto%2Frea-8A2BE2.svg?style=flat-square)](https://github.com/morluto/rea) ### Before submitting - [x] I searched the open and closed issues and pull requests (searched `Delphi`, `VMT`, `RTTI`, `published`, `Borland`). Related but different: #2373 (Delphi MAP files, which most shipped Delphi binaries do not include), #1667 (`REA_GHIDRA_SEED_FILE` / `borlandcpp` compiler spec, which this proposal would feed). - [x] I understand the contribution scope must be reviewed before integration. - [x] This request does not include credentials, private samples, or sensitive target data. ### Area Native analysis or decompilation ### Current limitation Shipped Delphi executables have no PDB and usually no MAP file, so a native provider sees thousands of `FUN_xxxxxxxx` functions. Yet every Delphi 2–2007 Win32 binary carries a self-describing class graph: each VMT has fixed negative offsets (`vmtSelfPtr = -76`, `vmtFieldTable = -56`, `vmtMethodTable = -52`, `vmtClassName = -44`, `vmtParent = -36` in Delphi 7) that give class name, parent class, published method table (name → code address) and published field table (offset → component name/class). These are exactly the names an analyst needs (`TFCadAPAC.bitsalvarClick`, `TDm.ConnBeforeConnect`, `[eax+0x3b0]` → `edtCNS`). REA 6.4.0 has no lane for this. `rea providers --json` / `rea capabilities --json` list no Delphi metadata operation, and the source tree has no VMT/RTTI parser. The only way to get those names into a Ghidra session today is to recover them with an external script and hand-write a `REA_GHIDRA_SEED_FILE`. Environment: macOS 27.0.1 arm64, Node 24.13.0, `rea-agents@6.4.0` (local install, no agent setup). ### Proposed outcome A provider-free, static `inspect_delphi_classes` (name open) for PE32 images: - Scan sections for VMT candidates by validating `vmtSelfPtr` (points back to the VMT) and a printable `ShortString` class name; walk `vmtParent` to build the class tree. - Decode the published method table (name + code address) and published field table (offset + name + class index), keeping file offsets and RVAs for every fact. - Make the VMT layout an explicit, recorded profile (Delphi 2–2007 Win32 first); report other versions/layouts as unsupported instead of guessing. - Emit an optional seed/label projection compatible with `REA_GHIDRA_SEED_FILE` (`address<TAB>function<TAB>Class.Method`), so the Ghidra provider imports the names with provenance. - Out of scope for this issue: Delphi 2009+ extended RTTI, Win64, runtime evaluation (#2375). ### Use cases Reverse engineering legacy Delphi line-of-business software (public-sector health systems in Brazil, ERP, banking). On the sample below, a reference implementation recovers 860 classes and 1,608 published methods, which turn an unnamed binary into one where every UI event handler has a name. ### Minimal public repro - `ftp://arpoador.datasus.gov.br/siasus/autorizador/AUTORIZADOR_SETUP.exe` (sha256 `226e68e4e5c2ef3d8749bdc788481f80eb79d5ac13dc51c89ecdc1cf96082aaa`), `innoextract -e`, target `app/AUTORIZADOR.exe` (sha256 `708b974938d3028f158d78162636ab0a25dcc3a06540fae2aaea02670ec22f4f`, PE32, Delphi 7, ImageBase `0x400000`). - Newer build: `AUTORIZADOR_VER6630.exe` (sha256 `0c79af43ce52a9317591e081ebfc9f29f70d5575033b2eb9e177d0828a634c44`), inner exe sha256 `1398d55a…8fa363`. - Expected anchors on Delphi 7 builds: `TObject` VMT at `0x401190`, `TInterfacedObject` at `0x4012b4`. ### Suggested approach Pure static parser over the PE snapshot (no provider needed), returning Evidence like `inspect_pe_resources`. Pair it with the TPF0 form decoder (#2810): form `OnClick = bitsalvarClick` + VMT method table → address gives handler-to-control wiring.
Author
Owner

@matbrgz commented on GitHub (Oct 13, 2026):

New evidence with the Ghidra provider (REA 6.4.0, Ghidra 12.1.4, macOS arm64): a seed file generated from an external VMT parser (1,610 published Delphi methods, format address<TAB>function<TAB>Class::Method) was accepted by REA_GHIDRA_SEED_FILE on AUTORIZADOR.exe 6.6.3.0 (sha256 1398d55a…8fa363): 1610 functions created, 0 already present, 0 failed. Pseudocode and trace_feature results then carry names such as TFCadAPAC::edprocautExit and TFHelpProcAPAC::Pesquisa011Click instead of FUN_… (e.g. trace_feature "('053','065')" resolves its references to three named handlers). So the seed lane already works end to end; this issue is only about producing those seeds inside REA. REA_GHIDRA_COMPILER_SPEC_ID=borlanddelphi (Ghidra's __register default prototype) produced byte-identical pseudocode for the 5 functions I compared, so names are the main win.
Related string-inventory gap found in the same session: #2825.

<!-- gh-comment-id:6147255483 --> @matbrgz commented on GitHub (Oct 13, 2026): New evidence with the Ghidra provider (REA 6.4.0, Ghidra 12.1.4, macOS arm64): a seed file generated from an external VMT parser (1,610 published Delphi methods, format `address<TAB>function<TAB>Class::Method`) was accepted by `REA_GHIDRA_SEED_FILE` on `AUTORIZADOR.exe` 6.6.3.0 (sha256 `1398d55a…8fa363`): *1610 functions created, 0 already present, 0 failed*. Pseudocode and `trace_feature` results then carry names such as `TFCadAPAC::edprocautExit` and `TFHelpProcAPAC::Pesquisa011Click` instead of `FUN_…` (e.g. `trace_feature "('053','065')"` resolves its references to three named handlers). So the seed lane already works end to end; this issue is only about producing those seeds inside REA. `REA_GHIDRA_COMPILER_SPEC_ID=borlanddelphi` (Ghidra's `__register` default prototype) produced byte-identical pseudocode for the 5 functions I compared, so names are the main win. Related string-inventory gap found in the same session: #2825.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#1003
No description provided.