Skip to content

Add/bof dll stomp#303

Open
MaorSabag wants to merge 3 commits intoAdaptix-Framework:dev-v1.3from
MaorSabag:add/bof-dll-stomp
Open

Add/bof dll stomp#303
MaorSabag wants to merge 3 commits intoAdaptix-Framework:dev-v1.3from
MaorSabag:add/bof-dll-stomp

Conversation

@MaorSabag
Copy link
Copy Markdown

BOF Module Stomping — Execute BOF Sections from Legitimate DLL .text Memory

Overview

Adds module stomping for BOF execution, allowing BOF sections to execute from within the .text region of a legitimate Windows DLL instead of anonymous VirtualAlloc memory.

Two techniques are supported and selected at compile time via BOF_STOMP_METHOD.


Techniques

Technique 0 — LoadLibraryEx (DONT_RESOLVE_DLL_REFERENCES)

Loads the sacrificial DLL without resolving imports. Simple and reliable. However, the DLL is still subject to CFG and ETW callbacks, and a PEB loader entry is automatically created.

Technique 1 — NtCreateSection + NtMapViewOfSection

Maps the DLL directly via NT APIs with:

  • No LoadLibrary ETW callbacks
  • No CFG enforcement
  • No automatic PEB loader entry

A synthetic LDR_DATA_TABLE_ENTRY is manually inserted into InLoadOrderModuleList and InMemoryOrderModuleList so stack walkers resolve the DLL name correctly.


Key Properties

Property Detail
Memory protection BOF code executes as PAGE_EXECUTE_READ — no RWX at execution time
.pdata handling Zeroed on init to force IFT fallthrough to RtlAddFunctionTable dynamic entries
Restore behavior Original .text bytes saved and restored after each BOF execution
Concurrency CRITICAL_SECTION guards the stomp region against concurrent BOF execution

UI Changes

The BOF Module Stomping group in the agent config now exposes a Method combo box (LoadLibraryEx / NtCreateSection + NtMapViewOfSection), disabled automatically when x86 arch is selected.

bof_stomp_technique

Call Stack Comparison

Without BOF Stomping

Anonymous 0x7ff... address at frame 2 — immediately flagged by stack-based EDR heuristics.

without_bof_stomp

With BOF Stomping (Technique 1 — NtCreateSection)

Frame 2 resolves to edgehtml.dll+0x1095 — indistinguishable from a legitimate DLL-backed call stack.

with_bof_stomp ---

Compile Flags — Unwind Table Fix for Clean Sleep Stack

Change

# Before
OPTIMIZATION_FLAGS := -fno-exceptions \
    -fno-unwind-tables \
    -fno-asynchronous-unwind-tables

# After
OPTIMIZATION_FLAGS := -fno-exceptions \
    -fasynchronous-unwind-tables \
    -mabi=ms

Why

-fno-unwind-tables + -fno-asynchronous-unwind-tables strips .pdata/.xdata from the agent binary entirely. Without RUNTIME_FUNCTION entries, the x64 stack unwinder cannot walk past agent frames — frames that lack unwind metadata show up as raw 0x25000000000, 0xd14cdff920, etc., polluting the call stack with ~20 unresolvable pointers that are a trivial EDR signal.

Switching to -fasynchronous-unwind-tables re-emits .pdata so the unwinder can correctly walk every agent frame. -mabi=ms enforces the Microsoft x64 calling convention, required for correct unwind info when targeting Windows from a cross-compiler.

Call Stack Comparison

Without fix — frames 10–31 are garbage addresses; unwinder falls off a cliff after agent.x64.exe+0x1666b:

without_optimize_flag_changes

With fix — clean 17-frame stack, all agent frames resolve, terminates properly at kernel32!BaseThreadInitThunkntdll!RtlUserThreadStart:

with_optimize_flag_changes

Replace -fno-asynchronous-unwind-tables with -fasynchronous-unwind-tables
and add -mabi=ms to OPTIMIZATION_FLAGS.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant