amiga-bootcamp/05_reversing/methodology.md

7.2 KiB

← Home · Reverse Engineering

RE Methodology — AmigaOS HUNK Binaries

Phase 1: Initial Triage

1.1 Identify the File Type

xxd target.library | head -4
First 4 bytes Type
00 00 03 F3 Executable (HUNK_HEADER)
00 00 03 E7 Object file (HUNK_UNIT)
70 FF 60 FA Resident tag (RomTag)

1.2 Dump HUNK Structure

hunkinfo target.library

Expected output:

Hunk 0: CODE  size=$1234  offset=$00012000
Hunk 1: DATA  size=$0200  offset=$00013240
Hunk 2: BSS   size=$0400
Symbols: 45
Relocs:  127

1.3 Verify Library Header

For a .library file, hunk 0 should start with a RomTag (resident module tag):

$4AFC    ; RT_MATCHWORD (hex: MOVEQ #0, D0 in context)

Real RomTag starts at word $4AFC:

_romtag:
    DC.W    RTC_MATCHWORD       ; $4AFC
    DC.L    _romtag             ; RT_MATCHTAG (self-pointer)
    DC.L    _endskip            ; RT_ENDSKIP
    DC.B    RTF_AUTOINIT        ; RT_FLAGS
    DC.B    VERSION             ; RT_VERSION
    DC.B    NT_LIBRARY          ; RT_TYPE
    DC.B    PRIORITY            ; RT_PRI
    DC.L    _libname            ; RT_NAME
    DC.L    _idstring           ; RT_IDSTRING
    DC.L    _inittable          ; RT_INIT (or function)

Phase 2: Load in IDA Pro

2.1 Load the File

File → New → select the binary → select "Amiga HUNK" format (or m68k raw if plugin not available).

If using the HUNK plugin:

  • Hunks are mapped to segments: CODE0, DATA0, BSS0, etc.
  • HUNK_SYMBOL entries become IDA names automatically
  • HUNK_RELOC32 become IDA fixups

Note

Alternative: Native Disassemblers If you are working directly on an Amiga or via emulation (WinUAE), native tools are highly effective:

  • Interactive Disassemblers: ReSource allows for interactive tracing and is well-aware of AmigaOS structures.
  • Command-line Disassemblers: IRA (Interactive Reassembler) is excellent for generating re-assemblable source code from HUNK binaries.
  • Assembler Environments: AsmOne provides a fully integrated debugging, disassembling, and patching environment.

2.2 Set Processor

Options → General → Processor type = Motorola 680x0

For A1200/A4000 targets, enable 68020 or 68040 instruction sets.

2.3 Apply Library Type Information

Load the AmigaOS TIL (Type Information Library) if available:

  • File → Load file → FLIRT signature file — use Amiga-specific FLIRT sigs
  • Or manually: View → Open Subviews → Type Libraries → load amigaos.til

Phase 3: Locate the Library Base

For a .library file, the library base is created by MakeLibrary() at runtime. In the static binary, look for:

; InitTable for the library:
DC.L  _libsize         ; LIB_POSSIZE (struct size)
DC.L  _funcTable       ; function array pointer
DC.L  _dataTable       ; data init table (or NULL)
DC.L  _initCode        ; init function

The function table is an array of absolute function pointers terminated by -1:

_funcTable:
    DC.L  _Open
    DC.L  _Close
    DC.L  _Expunge
    DC.L  _Reserved
    DC.L  _MyFunc1
    DC.L  _MyFunc2
    ...
    DC.L  -1           ; terminator

This is the easiest way to find all library functions from the static binary.


Phase 4: Identify Calling Conventions

4.1 Find Library Calls

Search for the pattern JSR -N(A6) or JSR -N(A5) (some code uses A5):

In IDA, search for instruction text: jsr -

Each hit is a library call. The register A6 (or A5) holds the library base at that point.

4.2 Trace Library Base

Trace backwards from the JSR to find where A6 was loaded:

MOVEA.L _DOSBase, A6     ; most common: global variable
JSR     -48(A6)          ; Write

MOVEA.L D0, A6           ; from return value of OpenLibrary
JSR     -30(A6)          ; Open

MOVEA.L 4.W, A6          ; SysBase directly
JSR     -198(A6)         ; AllocMem

4.3 Resolve the LVO

# IDA Python: resolve a JSR -N(A6) to a function name
def resolve_lvo(ea):
    insn = idc.print_insn_mnem(ea)
    if insn not in ('jsr', 'JSR'):
        return
    op = idc.print_operand(ea, 0)
    # op looks like "-48(A6)" or "-552(a6)"
    import re
    m = re.match(r'(-?\d+)\(A6\)', op, re.IGNORECASE)
    if m:
        lvo = int(m.group(1))
        # Look up in your LVO table
        name = LVO_TABLE.get(lvo, f"unknown_lvo_{-lvo}")
        idc.set_cmt(ea, f"→ {name} LVO={lvo}", 0)

Phase 5: Annotate and Name

5.1 Apply Library Function Names

Using the LVO tables from lvo_table.md, annotate each JSR:

# IDA script: annotate all JSR -N(A6) calls in exec context
import idautils, idc, idaapi

EXEC_LVOS = {
    -198: "AllocMem",
    -210: "FreeMem",
    -282: "AddTask",
    -552: "OpenLibrary",
    # ... (full table from lvo_table.md)
}

for ea in idautils.CodeRefsTo(idc.get_name_ea_simple("_SysBase"), 0):
    # For each reference to SysBase load, find subsequent JSR
    pass  # implement full trace

5.2 Name Functions

After resolving all library calls in a function, it becomes clear what the function does. Apply a meaningful name:

IDA: Press N on a function → rename to descriptive identifier

5.3 Define Structures

Apply AmigaOS structure types:

  • View → Open Subviews → Local Types → import from AmigaOS headers
  • Or manually define: ExecBase, DosLibrary, MsgPort, etc.

Phase 6: Patch Analysis

Look for evidence of SetFunction patching:

  1. JMP table entries pointing outside the library code segment
  2. LIBF_CHANGED bit set in lib_Flags
  3. Functions that immediately JSR to a stored old-function address

Look for timer/protection mechanisms:

  1. Calls to dos.library CurrentTime() or timer.device
  2. CIA timer reads ($BFDE00 area)
  3. Comparison of tick counts with a threshold

Limitations: The Decompilation Problem

While decompilation (generating C/C++ source code from assembly) is a common modern RE workflow via Hex-Rays or Ghidra, the Amiga ecosystem presents severe challenges for decompilation:

  • Heavy reliance on hand-written assembly: Many Amiga games and demos eschewed C compilers entirely. Decompiling highly optimized 68000 assembly that uses custom chip registers directly into C yields poor, unreadable results.
  • Custom Calling Conventions: Unlike modern standard ABIs (e.g., cdecl, fastcall), Amiga software frequently used register-based arguments (e.g., D0-D1 for data, A0-A1 for pointers) tailored to specific routines.

Note

Historical Context Straightforward decompilation of Amiga games is largely a myth. Successful "decompilation" projects, such as Tom Morton's GLFrontier (a port of the Atari ST/Amiga game Frontier), rely heavily on custom-built decompilation solutions tailored precisely to the game's specific binary patterns, rather than generic tools.


References