amiga-bootcamp/05_reversing/dynamic/live_memory_probing.md
Ilia Sharin fca930d0db docs(amiga): make cross-references clickable markdown links
Convert bare .md path references in 29 files to proper [name](relative/path)
markdown links for GitHub navigation.
2026-04-23 12:24:21 -04:00

3.3 KiB
Raw Blame History

← Home · Reverse Engineering

Live Memory Probing

Overview

Live memory probing on a running Amiga means directly reading exec structures — SysBase, LibList, TaskReady, MemList — to observe system state without a traditional debugger.


SysBase: The Root of Everything

SysBase is always at absolute address $4 (a pointer to the ExecBase structure):

struct ExecBase *SysBase = *((struct ExecBase **)4);
printf("exec version: %d.%d\n",
       SysBase->LibNode.lib_Version,
       SysBase->LibNode.lib_Revision);

In assembly:

MOVEA.L  4.W, A6              ; A6 = SysBase (exec.library base)
MOVE.W   ($16,A6), D0         ; lib_Version
MOVE.W   ($18,A6), D1         ; lib_Revision

Walking the Library List

struct Node *n = SysBase->LibList.lh_Head;
while (n->ln_Succ != NULL) {
    struct Library *lib = (struct Library *)n;
    printf("%-30s v%d.%d  opens=%d\n",
           lib->lib_Node.ln_Name,
           lib->lib_Version, lib->lib_Revision,
           lib->lib_OpenCnt);
    n = n->ln_Succ;
}

This enumerates all currently loaded libraries. Useful for:

  • Finding if a target library is loaded
  • Reading lib_OpenCnt to detect if your hook is installed
  • Checking lib_Flags & LIBF_DELEXP (expunge pending)

Reading lib_OpenCnt Live

/* Check if bsdsocket.library is loaded and its open count */
struct Library *base = FindName(&SysBase->LibList, "bsdsocket.library");
if (base) {
    printf("bsdsocket: OpenCnt=%d, Version=%d\n",
           base->lib_OpenCnt, base->lib_Version);
}

FindName scans ln_Name in a linked list — it is an exec function at LVO 276.


Memory Region Map

SysBase->MemList lists all memory regions:

struct MemHeader *mh = (struct MemHeader *)SysBase->MemList.lh_Head;
while (mh->mh_Node.ln_Succ) {
    printf("Region: %s  %08lx%08lx  free=%ld\n",
           mh->mh_Node.ln_Name,
           (ULONG)mh->mh_Lower,
           (ULONG)mh->mh_Upper,
           mh->mh_Free);
    mh = (struct MemHeader *)mh->mh_Node.ln_Succ;
}

Output example:

Region: chip memory   $000000$1FFFFF  free=524288
Region: fast memory   $200000$9FFFFF  free=6291456

Task List Inspection

/* Running tasks: */
Forbid();
struct Task *t = (struct Task *)SysBase->TaskReady.lh_Head;
while (t->tc_Node.ln_Succ) {
    printf("Task: %-20s pri=%d state=%d\n",
           t->tc_Node.ln_Name,
           t->tc_Node.ln_Pri,
           t->tc_State);
    t = (struct Task *)t->tc_Node.ln_Succ;
}
Permit();

Forbid() / Permit() are mandatory — the task list must not change while walking it.


Patching Memory Live (Surgical Writes)

For RE/patching: direct longword write to an OS structure:

/* Example: force a library's version to 99 */
Forbid();
target_lib->lib_Version = 99;
Permit();

Caution

Direct memory writes to OS structures bypass all synchronization. Always use Forbid() at minimum; use Disable() if modifying interrupt-visible data.


References