Interactive Lab Skyrim Reverse Engineering: The Complete Guide
๐Ÿ‘ถ โšก
Active Mode: Layman View โ€” Technical assembly, raw memory byte arrays, and C++ template headers are translated into everyday visual analogies (Hotels, Rooms, Street Addresses & Note Chasing).
Active Mode: Engineer View โ€” Full x86_64 disassembly, Address Library RelocationIDs, trampoline write_call<5> hooks, raw Little-Endian arithmetic, and Ghidra RTTI vtable layouts are unlocked.
Zero Prior Knowledge Required

How Computers "Remember" Things: The 5 Mental Models

If you have never seen a line of code or a hex byte in your life, start here. Reverse engineering is not magicโ€”it is simply exploring an unmarked hotel with a flashlight.

๐Ÿจ 1. The Hotel Analogy: What is Memory?

Imagine a gigantic hotel with millions of rooms. Each room has:

  • Room Number = The Memory Address (e.g. #101 or 0x7FF74E210000).
  • What's Inside the Room = The Value / Data (e.g. Health: 100, Gold: 500).
  • A Pointer = A room that contains no luggage, just a note saying "Go to Room #304".
Interactive Hotel: Click a room to inspect what's inside!
Room #101 (Player)
๐Ÿ“œ Note to #205
Pointer to Inventory
Pointer
Room #102 (Health)
โค๏ธ 100 HP
Direct Number
Value
Room #205 (Inventory)
๐Ÿ—ก๏ธ Iron Sword
Object in Memory
Target
Room #300 (AI)
โš”๏ธ Aggressive
Combat State Flag
Flag
๐Ÿšช Inspecting Room #101
You found a note pointing to Room #205. Walking from #101 to #205 to grab the Iron Sword is called Dereferencing!

๐ŸšŒ 2. The Hotel on Wheels: What is ASLR?

For security, Windows places the entire hotel on wheels. Every time you launch Skyrim, the hotel is parked on a random street in the city (the Base Address).

๐Ÿ”‘ The Golden Rule of Offsets
Even if the hotel moves to a new street address today, the distance from the Lobby to Room #105 is always exactly 5 doors down (+5 offset)!

Formula: Today's Street Address (Base) + Distance Down Hall (Offset) = The Room (Runtime VA).

๐Ÿ“– 3. The Phone Directory: Address Library

When Bethesda updates Skyrim (from SE 1.5.97 to AE 1.6.1170), they rebuild the hotel and shuffle room numbers. Address Library IDs are permanent resident IDs that let our mods look up the new room number in a table automatically!

๐Ÿ•ต๏ธ 4. Landmarks & Proximity: How to Find Unnamed Functions

Skyrim's developers removed all name tags from their code before releasing the game. You want to find the secret unexported function that calculates stealth, but there is no door sign.

How do we find it? We use Landmark & Cross-Reference (XREF) Analysis:

  1. Look for the Neon Pizza Sign: We search for a known debug string like "Stealth Detected" in memory.
  2. Follow Footsteps (XREFs): We check which unexported function calls or reads that string.
  3. Measure Distance: It is sitting in the text segment right next to known ID 52224!
๐Ÿ“ธ 5. The Crime Scene Photo: Live Diffing

Don't know which memory number holds the "Sneaking" status?

  1. Take a photo of all memory while the player is standing.
  2. Press the crouch button in-game.
  3. Take a second photo and compare them.
  4. The single number that flipped from 0 to 1 is your target!
Core Foundation

Raw Bytes, Endianness & Dereferencing

In memory, there are no variable namesโ€”only numbered byte slots. Reverse engineering is learning how to read raw byte layouts and chase pointers through structures.

Pointer Dereference Chain

From Local Pointer to Struct Member

Note-Chasing Game: Follow the sticky notes from the Player to find who they are attacking in combat. Click step-by-step!

x86_64 Pointer Chain: mov rcx, [player]mov rcx, [rcx + 0xB8]mov rcx, [rcx + 0x10]mov rax, [rcx + 0x08]

๐Ÿ’ก Layman Rule: Backwards Numbers (Little-Endian)
Computers write numbers backward in memory! Address 0x1408e6140 is saved as:
40 61 8e 40 01 00 00 00
โšก x86_64 Little-Endian Architecture
Least Significant Byte (LSB) stored at the lowest memory offset. A 64-bit pointer loaded into RAX swaps byte order back automatically via hardware registers.

Live Memory Hex Inspector (Player Actor Region)

Click any byte row or field to inspect its decoded value and type

Base VA: 0x7FF74E210000
Step 0: Player Object
0x7FF74E210000
PlayerCharacter* (Singleton)
Step 1: +0x0B8 Offset
0x0000021A4B90F200
AIProcess* (Actor Process)
Step 2: +0x010 Offset
0x0000021A4B90FA40
MiddleHighProcess* Data
Step 3: Target Actor
0x0000021A88A12300
Target: Bandit Thug [0x0002E310]
Offset (RVA / Field) Raw Bytes (Hex Little-Endian) Interpretation
Selected Field PlayerCharacter::vtable
Virtual Address 0x7FF74E210000
Raw Hex (8 bytes) 80 42 6E 4E F7 7F 00 00
Decoded Pointer / Int 0x7FF74E6E4280 (Vtable)
Resolution Architecture

The Skyrim Address Chain & Dual-Build Engine

Skyrim exports no symbols. Every hook requires mapping an Address Library ID → RVA → Runtime Virtual Address (with ASLR).

๐Ÿ›ก๏ธ What is ASLR? (Address Space Layout Randomization)

Core OS Security

๐Ÿจ The Plain-English Explanation

โšก How Windows ASLR Works

ASLR stands for Address Space Layout Randomization. It is a security feature built into Windows. Every time you launch SkyrimSE.exe, Windows rolls a random dice and loads the entire game into a completely different memory address.

  • Why Windows does this: To prevent malware and viruses from predicting where functions live in RAM and exploiting buffer overflows.
  • What it means for SKSE mods: You can never hardcode a memory address (e.g. 0x7FF74EAF6140) because next launch, that address will point to junk or crash the game.
๐Ÿ”‘ The Solution: Base + Offset (RVA)

While the Base Address moves randomly every launch, the Relative Virtual Address (RVA) โ€” the distance of a function from the start โ€” never changes within a game version!

Runtime Address = (Random ASLR Base) + (Fixed RVA Offset)

Interactive Address Resolver

STREET ADDRESS
MODULE BASE
0x7FF74E210000
+
DISTANCE DOWN HALL
RVA OFFSET
0x008E6140
=
ROOM LOCATION TODAY
RUNTIME VA (HOOK TARGET)
0x7FF74EAF6140
๐Ÿ“– Layman Phone Book Lookup
โšก Generated SKSE Dual-Build Code
Your mod asks the Address Library phone book: "Give me Resident #52224 on SE and #53106 on AE". It calculates today's room location instantly without you needing to do manual math!
// SE ID: 52224, AE ID: 53106 inline static REL::Relocation<Actor*(*)(Actor*)> GetCombatTarget{ REL::RelocationID(52224, 53106) };

Address Library Dual Mapping Table

Why raw RVAs fail across versions: Steam updates shift text segment offsets, but Address Library IDs remain the canonical cross-version anchor.

Function / Anchor SE 1.5.97 AE 1.6.1170 RelocationID Pair
PlayerCharacter::GetSingleton ID 37609
RVA 0x620130
ID 38556
RVA 0x654300
REL::RelocationID(37609, 38556)
Actor::GetCombatTarget ID 52224
RVA 0x8E6140
ID 53106
RVA 0x932F20
REL::RelocationID(52224, 53106)
TESForm::GetFormID ID 19365
RVA 0x2213F0
ID 19792
RVA 0x2352B0
REL::RelocationID(19365, 19792)
Target Discovery Methodology

Locating Unexported Code & Struct Members

When a function or struct field has no name in Ghidra, we never guess. We anchor to a known entity and navigate proximity using 4 standard reverse engineering techniques.

Interactive Binary Text Segment (Click an Anchor to see how Target Discovery works) Target: Unexported Stealth Detection Function
Anchor 1 String: "Stealth Detected"
Anchor 2 Known ID: 52224
TARGET ? +0x140 from ID 52224
Anchor 3 Vtable Slot 0x0A8
Anchor 4 Struct Field Diff
Technique 1
Cross-Reference (XREF) Tracing
Search .rdata for debug strings, UI messages, or log prints. Trace XREFs up to find the enclosing unexported caller function.
Technique 2
Nearest-Neighbor ID Grep
When a function at RVA X has no symbol, grep offsets-se.txt for the closest lower RVA to establish a known anchor and measure byte distance.
Technique 3
Vtable Virtual Slot Indexing
Find the class RTTI locator (e.g. for PlayerCharacter). Count 8-byte pointer slots up or down from known virtual methods.
Technique 4
Dynamic Memory Diffing
Dump an Actor with Live Introspection Bridge before and after an action (e.g. sneaking, jumping) to watch which byte offset at Actor+0x... flips value.

Technique 1: Cross-Reference (XREF) Tracing Walkthrough

Skyrim contains thousands of unexported functions, but Bethesda left strings like error prints, console logs, and animation tag names in the .rdata section.

// Step 1: In Ghidra, search string: "DEFAULT: Target is not in combat" // Step 2: Look at XREFs to that string -> Lead directly to unexported function at RVA 0x8E6280 // Step 3: Check offsets-se.txt: closest ID is 52224 (Actor::GetCombatTarget at 0x8E6140) // Target is exactly +0x140 bytes down from ID 52224!
Dynamic Analysis

Live Bridge RE & Memory Diffing Simulator

Static decompilation tells you what code could do. Live memory inspection confirms what it actually does at runtime. Stimulate the game and isolate the changing offset.

Live Introspection Bridge: Actor Memory Diff Lab

Test stimulus triggers to discover the hidden field offset

Baseline Snapshot (Idle Stand) โ€” FormID: 0x00000014
Active Stimulus Snapshot (After Player Action)
๐ŸŽฏ RE Finding Analysis
Select a stimulus above to watch Live Introspection Bridge capture and isolate the exact byte offset in the Actor struct!
Hands-On Practice

Real-World RE Mission: Hooking Unexported Combat State

Step into the shoes of a Skyrim reverse engineer. Follow the exact workflow used to find, verify, and write an SKSE hook for an unexported engine function.

Step 1 of 4: The Inquiry Static Recon

Objective: Find where Skyrim calculates if an Actor is in Combat

CommonLib NG provides Actor::IsInCombat(), but we need to intercept the internal scoring function that decides combat targets before the state flips. Where do you begin?
Step 2 of 4: Nearest-Neighbor Analysis Anchor Lookup

Grep finds ID 52224 (Actor::GetCombatTarget) at SE RVA 0x8E6140

In Ghidra, we look at ID 52224 and notice it calls an unexported helper function at RVA 0x8E63B0. How do we map this unexported function to Skyrim AE (1.6.1170)?
Step 3 of 4: Dynamic Verification Live Introspection

Verify Live with Introspection Bridge

Static analysis suggests the function takes (Actor* a_this, Actor* a_target) and returns a boolean in the AL register. Before writing hook code, how do we verify?
Step 4 of 4: Production SKSE Synthesis C++ Hooking

Construct the Production SKSE Hook

Both SE ID (52230) and AE ID (53112) are confirmed. Here is the verified dual-build hook snippet ready to be wired into your plugin:
struct CombatScoreHook { static bool Hook_EvaluateTarget(RE::Actor* a_this, RE::Actor* a_target) { // Custom mod logic here bool result = _EvaluateTarget(a_this, a_target); return result; } static void Install() { REL::Relocation<uintptr_t> target{ REL::RelocationID(52230, 53112) }; auto& trampoline = SKSE::GetTrampoline(); _EvaluateTarget = trampoline.write_call<5>(target.address() + 0x24, Hook_EvaluateTarget); } inline static REL::Relocation<decltype(Hook_EvaluateTarget)> _EvaluateTarget; };
๐ŸŽ‰ Mission Accomplished! You have mastered the entire Skyrim RE chain: Static Recon → Anchor Proximity → Dual-ID Mapping → Live Introspection → Production SKSE Hook.