mirror of
https://dev.lirent.ru/Vatrog/vm-introspection-engine.git
synced 2026-06-18 02:06:36 +03:00
35c5dc06ba
Wave 2 of the code-analysis layer:
- vmie_win32_imports resolves the import directory (INT/IAT) to {iat_rva, dll,
name, ordinal} - named APIs, walking the name and slot thunks in lockstep so
every import carries the IAT slot a call lands on.
- vmie_win32_inline_hooks decodes each .pdata function's entry and reports any
whose first instruction is a direct jmp/call leaving the module image - the
detour/trampoline shape.
- vmie_win32_func_imports records, in order, the IAT slots a function calls
through (call qword [rip+disp] onto an import slot): the function's API-call
sequence, named by correlating with vmie_win32_imports.
- func_hash (codeanalysis.h) hashes a function position-independently, zeroing
the displacement bytes the decoder locates - one primitive for fingerprinting
known code and for detecting a changed body across snapshots.
Devirtualization needs no new call and is documented as a composition: a
vtable's methods are gva_jumptable(vtable_va), its instances are
pmap_referrers(vtable_va), and func_hash names each method. Imports reuse the
shared data-directory accessor; the analyses reuse the function/section/decode
primitives - no second PE or instruction parser.
97 lines
5.5 KiB
C
97 lines
5.5 KiB
C
/* pe.h - PE/COFF image parsing (engine-private, Windows-specific).
|
|
*
|
|
* Locating a section by name inside a mapped PE image is a Windows-image
|
|
* concern, not a property of the source-agnostic matcher: it lives in the
|
|
* engine, alongside the rest of the Windows bring-up. Handlers never see this
|
|
* header - they consume only the generic memory model (memmodel.h) and the pure
|
|
* matcher (sigscan.h). The engine uses these to build mem_view_t windows out of
|
|
* a guest image and feed them to the matcher.
|
|
*/
|
|
#ifndef VMIE_PE_H
|
|
#define VMIE_PE_H
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include "memmodel.h" /* mem_view_t, vmie_mem */
|
|
|
|
/* One enumerated PE section header, decoded by pe_sections. Mirrors the public
|
|
* win32 section_desc, but stays engine-private (this header is engine-only).
|
|
* name - section name, NUL-terminated (PE names are <= 8 bytes; name[8] NUL)
|
|
* rva - section RVA (relative to module_base)
|
|
* vsize - virtual size in bytes
|
|
* prot - VR_R/VR_W/VR_X from the section Characteristics (VR_U never set) */
|
|
typedef struct { char name[9]; uint32_t rva; uint32_t vsize; uint32_t prot; } pe_secrec;
|
|
|
|
/* Enumerate the section headers of the PE image based at `module_base` inside a
|
|
* view holding at least the image headers (the first page is enough).
|
|
* out, max - caller array receiving up to `max` pe_secrec; out may be NULL to
|
|
* count only. Headers truncated by the view end are not reported.
|
|
* Returns the TOTAL section count (may exceed `max`), or -1 if `v` does not hold
|
|
* a parseable PE at `module_base`. Shares the section-table walk with
|
|
* pe_find_section (one header parser, no duplication). */
|
|
int pe_sections(mem_view_t v, uint64_t module_base, pe_secrec* out, int max);
|
|
|
|
/* Locate a PE section by name within a view that contains at least the image
|
|
* headers at `module_base` (the first page is enough).
|
|
* module_base - image base VA, must be >= v.base_va and inside `v`
|
|
* name - section name, e.g. ".text" (compared up to 8 bytes)
|
|
* rva_out - receives the section RVA (relative to module_base); may be NULL
|
|
* vsize_out - receives the section virtual size; may be NULL
|
|
* Returns true if found. Only the headers need to be present in `v`; the section
|
|
* body does not. */
|
|
bool pe_find_section(mem_view_t v, uint64_t module_base, const char* name,
|
|
uint64_t* rva_out, uint32_t* vsize_out);
|
|
|
|
/* Locate a PE section AND return a sub-view spanning it. Requires the whole
|
|
* section body to be present in `v` (true for an in-memory image dump). Prefer
|
|
* scanning ".text" over a whole image: faster, and avoids false hits in data.
|
|
* Returns true and fills *out on success. For guest memory, where the body is
|
|
* usually not co-resident with the headers, use vmie_pe_section. */
|
|
bool pe_section(mem_view_t v, uint64_t module_base, const char* name,
|
|
mem_view_t* out);
|
|
|
|
/* Read a PE section out of guest memory under `cr3` into `buf`.
|
|
* module_base - image base VA (headers read from the first page)
|
|
* name - section name, e.g. ".text"
|
|
* buf, bufcap - destination buffer and its capacity (section is truncated to fit)
|
|
* out - on success, a view spanning the bytes read into `buf`
|
|
* Returns 0 on success, -1 if the headers/section are unreadable or absent. The
|
|
* guest image body need not be co-resident with the headers (unlike pe_section).*/
|
|
int vmie_pe_section(vmie_mem* m, uintptr_t cr3, uint64_t module_base,
|
|
const char* name, uint8_t* buf, size_t bufcap, mem_view_t* out);
|
|
|
|
/* OptionalHeader DataDirectory indices used across the engine. */
|
|
#define PE_DIR_EXPORT 0u /* IMAGE_DIRECTORY_ENTRY_EXPORT */
|
|
#define PE_DIR_IMPORT 1u /* IMAGE_DIRECTORY_ENTRY_IMPORT */
|
|
#define PE_DIR_DEBUG 6u /* IMAGE_DIRECTORY_ENTRY_DEBUG */
|
|
#define PE_DIR_EXCEPTION 3u /* IMAGE_DIRECTORY_ENTRY_EXCEPTION (.pdata) */
|
|
|
|
/* Read one OptionalHeader DataDirectory entry of the PE32+ image based at `base`
|
|
* in the address space `cr3`. This is the SINGLE data-directory accessor used by
|
|
* every directory walk in the engine (.pdata / export / debug) - it walks the
|
|
* DOS+NT headers from `base` once and reads DataDirectory[idx].
|
|
* idx - directory index (PE_DIR_*).
|
|
* rva - receives DataDirectory[idx].VirtualAddress (0 if the directory is
|
|
* absent); never NULL.
|
|
* size - receives DataDirectory[idx].Size; may be NULL.
|
|
* Returns 0 on success (rva/size filled), -1 if the headers are unreadable. A
|
|
* present-but-absent directory reports rva==0 with return 0. */
|
|
int pe_data_dir(vmie_mem* m, uintptr_t cr3, uint64_t base, unsigned idx,
|
|
uint32_t* rva, uint32_t* size);
|
|
|
|
/* Extract a module's CodeView RSDS reference from its debug directory. This is
|
|
* the SINGLE debug-dir/RSDS parser in the engine, shared by the kernel bootstrap
|
|
* (host.c) and the public vmie_win32_pdb_ref - there is no second copy.
|
|
* base - image base VA in `cr3`.
|
|
* guid[16] - receives the PDB GUID (in-memory byte order); never NULL.
|
|
* age - receives the PDB age; never NULL.
|
|
* name - receives the NUL-terminated PDB file name; never NULL.
|
|
* namecap - capacity of `name` (>= 1). The name is truncated to namecap-1.
|
|
* Walks PE_DIR_DEBUG for an IMAGE_DEBUG_TYPE_CODEVIEW entry whose payload starts
|
|
* with 'RSDS', then reads {guid, age, name}. Returns 0 on success, -1 if there
|
|
* is no debug directory, no CodeView/RSDS entry, or the bytes are unreadable. */
|
|
int pe_pdb_ref(vmie_mem* m, uintptr_t cr3, uint64_t base,
|
|
uint8_t guid[16], uint32_t* age, char* name, size_t namecap);
|
|
|
|
#endif /* VMIE_PE_H */
|