Skip to content

โ† 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