Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CrashContextCpp

A game-agnostic UE4.27 crash-context mod for UE4SS. It turns a bare crash line

Unhandled Exception: EXCEPTION_ACCESS_VIOLATION reading address 0x000000000000004c

into a report naming what the Blueprint VM was doing when it died:

=== CrashContext report (UE4.27) ===
Crash class: Hardware exception
Exception: EXCEPTION_ACCESS_VIOLATION (0xC0000005) at 0x7FF6A1B2C3D4
Access violation reading address 0x000000000000004C
Faulting module: YourGame-Win64-Shipping.exe + 0x1B2C3D4 (module base 0x7FF6A0000000)
RIP=0x7FF6A1B2C3D4 RSP=... RBP=...
Crashing thread: 12345  (game thread)

Blueprint script stack (3 frame(s), innermost first):
  [0] Function /Game/objects/foo.foo_C:UpdateTarget
        self: foo_C /Game/maps/level.level:PersistentLevel.foo_37
        UFunction*=0x...  UObject*=0x...
        bytecode offset: 0x1A4 of 0x820
        MostRecentProperty: ObjectProperty /Game/objects/foo.foo_C:Target
        CurrentNativeFunction: Function /Script/Engine.KismetSystemLibrary:K2_SetActorLocation
  [1] Function /Game/objects/foo.foo_C:Tick

It uses only UE 4.27 engine internals and UE4SS reflection, so it works on any UE4.27 UE4SS title. Shipped via the VotV Thunderstore, not VotV-specific.

How it works

The engine's own script stack (FBlueprintContextTracker::ScriptStack) is compiled out in Shipping (#if DO_BLUEPRINT_GUARD), so we can't read it. But the FFrame interpreter frames still live on the game thread's stack, and FFrame::MostRecentProperty is set regardless of build config.

  1. Own shadow stack (ShadowStack.hpp). Hooks ProcessLocalScriptFunction (and ProcessInternal) via UE4SS. In 4.27 every interpreter frame runs through ProcessLocalScriptFunction once, so a push there snapshots the live FFrame* plus the stable UFunction*/UObject*.

  2. First-priority VEH (CrashHandler.cpp). A vectored handler runs before any frame-based SEH filter (before UE's __except(ReportCrash) and UE4SS's minidump handler), reads the crashing thread's shadow stack, then returns EXCEPTION_CONTINUE_SEARCH so the normal minidump still runs. Additive, not intercepting.

Every field is read inside a nested __try/__except with a hex fallback, so a secondary fault can't crash the crash handler.

Where the report goes

  1. CrashContext_<timestamp>.log in UE4SS's working directory (full copy, written first).
  2. Appended to UE4SS.log in the same directory.
  3. A modal dialog ("CrashContext (UE4.27)") with the crash class, exception, and top Blueprint frame.

The engine's own crash message and minidump are left untouched.

Crash classes covered

  • Hardware exceptions (EXCEPTION_ACCESS_VIOLATION etc.). The VEH sees them directly.

  • Assertions / Fatal errors (check/checkf/verify, UE_LOG(..., Fatal, ...), LowLevelFatalError). These don't fault; they go through GError->Serialize -> ReportAssert, which calls RaiseException(0x4000, ...) with a FAssertInfo* in ExceptionInformation[0]. The VEH handles 0x4000 (assert) and 0x8000 (GPU crash), reads the message out of FAssertInfo, and dumps the same BP stack. No AOB, no symbols; the codes are fixed UE 4.27 constants.

    ReportAssert only raises when GIsGuarded, which holds during normal gameplay. A Fatal fired while unguarded (very early startup / late shutdown) exits without raising and is missed. ensure() (0xBDB70) is recoverable and ignored.

Resolving offset to node (offline)

Cooked builds strip the offset-to-node map. The report gives UFunction + bytecode offset + property; resolve it by opening the function in an editor copy that still has the map, or by disassembling the bytecode.

Native symbolication

Faulting module: <exe> + 0xRVA is all you get natively. Most shipped UE4 games are monolithic, so Epic's engine PDBs won't symbolize the exe. Map the RVA with your own AOB anchors. The Blueprint story above needs no symbols.

Build and deploy

Built in-tree with the other cppmods (registered in cppmods/CMakeLists.txt). The DLL is copied to MOD_OUTPUT_PATH_CRASHCONTEXTCPP; retarget that to your UE4SS Mods / shimloader mod directory. Reports land in UE4SS's working directory next to UE4SS.log.

Caveats

  • UE4SS settings (default on): [Hooks] HookProcessLocalScriptFunction = 1 (and/or HookProcessInternal = 1). If both are 0 the shadow stack stays empty and reports are native-only.
  • Name resolution calls GetFullName(), which allocates and reads the FName pool. Safe for a typical null-deref; if memory is corrupt it faults and falls back to raw pointers. It can theoretically deadlock if another thread holds an FName lock at crash time, rare for game-thread null derefs.
  • Stack overflow (0xC00000FD): little stack left, so the report may be truncated or skipped.
  • First-chance: only hard fatal codes (AV, illegal instruction, div-by-zero, asserts) are acted on; C++ EH (0xE06D7363), ensures, and debug events are ignored. Capped at 24 reports/session.
  • Shadow-stack callbacks are noexcept and allocation-free: a throwing script-hook callback tears down UE4SS's shared dispatcher and kills every mod's BP hooks.

Files

File Role
include/CrashContext/ShadowStack.hpp Per-thread shadow of the live BP VM stack; noexcept push/pop
include/CrashContext/CrashHandler.hpp Install / remove API
src/CrashHandler.cpp VEH + report writer (file + UE4SS.log + dialog)
src/dllmain.cpp Mod entry; registers VM hooks and arms the handler

About

A game-agnostic UE4.27 crash-context mod for UE4SS. Better crash context info for BP mods.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages