reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
Apache License 2.0 — see the repository root LICENSE file for the full text.
|
||||
|
||||
Copyright the AnalystAIPack contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
name: reverse-engineering-arm-binaries
|
||||
description: 'Reverse engineers ARM/AArch64 malware by identifying the architecture and instruction
|
||||
set state (ARM/Thumb), parsing ELF/Mach-O ARM headers, and orienting analysis around the ARM
|
||||
calling convention. Activates for requests to reverse ARM binaries, analyze AArch64 malware, or
|
||||
handle ARM/Thumb instruction-set decoding.'
|
||||
domain: cybersecurity
|
||||
subdomain: reverse-engineering
|
||||
tags:
|
||||
- reverse-engineering
|
||||
- arm
|
||||
- aarch64
|
||||
- thumb
|
||||
- disassembly
|
||||
version: 1.0.0
|
||||
author: analyst-ai-pack
|
||||
license: Apache-2.0
|
||||
mitre_attack:
|
||||
- T1027
|
||||
- T1059
|
||||
- T1587.001
|
||||
d3fend:
|
||||
- D3-DA
|
||||
- D3-SDA
|
||||
references:
|
||||
- 'Arm Architecture Reference Manual — https://developer.arm.com/documentation/'
|
||||
- 'ELF for the Arm Architecture (AAELF) — https://github.com/ARM-software/abi-aa'
|
||||
---
|
||||
|
||||
# Reverse Engineering ARM Binaries
|
||||
|
||||
## When to Use
|
||||
|
||||
- You have an ARM (32-bit) or AArch64 (64-bit) binary — IoT/mobile/Linux malware — and need to
|
||||
identify the architecture, instruction-set state, and entry point before disassembly.
|
||||
- You need to handle ARM/Thumb interworking correctly.
|
||||
|
||||
**Do not use** an x86 mindset for calling conventions/registers — ARM differs. This skill reads the
|
||||
binary statically and executes nothing.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- The ARM binary (ELF or Mach-O), read inertly. Capstone optional for instruction decoding.
|
||||
|
||||
## Safety & Handling
|
||||
|
||||
- Read bytes statically; analyze on an isolated host (and emulate via QEMU separately if needed).
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Identify architecture and format
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py identify sample.bin
|
||||
```
|
||||
|
||||
Parses ELF/Mach-O headers to report ARM vs AArch64, endianness, entry point, and (for ELF) whether
|
||||
the entry is Thumb (low bit set in `e_entry` or `$t` mapping symbols).
|
||||
|
||||
### Step 2: Set the correct disassembly mode
|
||||
|
||||
Disassemble AArch64 as A64; for 32-bit ARM, switch between ARM and Thumb per the entry/mapping
|
||||
symbols.
|
||||
|
||||
### Step 3: Orient around the calling convention
|
||||
|
||||
Track arguments in `r0-r3`/`x0-x7`, return in `r0`/`x0`, and syscalls via `svc` with the syscall
|
||||
number in `r7`/`x8`.
|
||||
|
||||
### Step 4: Proceed with analysis
|
||||
|
||||
Identify functions, strings, and syscalls; pair with emulation if dynamic insight is needed.
|
||||
|
||||
## Validation
|
||||
|
||||
- Architecture (ARM/AArch64) and endianness are read from the header.
|
||||
- The entry point and ARM/Thumb state are reported.
|
||||
- Disassembly mode matches the detected state.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- Missing Thumb state and decoding Thumb as ARM (garbage output).
|
||||
- Big-endian ARM (rare but real) mis-parsed as little-endian.
|
||||
- Statically linked musl/uClibc inflating the function set on IoT samples.
|
||||
|
||||
## References
|
||||
|
||||
- See [`references/api-reference.md`](references/api-reference.md) for the identifier.
|
||||
- Arm ARM and AAELF references (linked in frontmatter).
|
||||
@@ -0,0 +1,42 @@
|
||||
# API Reference: Reverse Engineering ARM Binaries
|
||||
|
||||
## Overview
|
||||
|
||||
`scripts/analyst.py` parses ELF and Mach-O headers to identify ARM vs AArch64, endianness, entry
|
||||
point, and (for 32-bit ARM ELF) Thumb entry state. It reads bytes statically and executes nothing.
|
||||
Capstone can be used downstream for instruction decoding.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| (stdlib) | — | struct, JSON, file IO |
|
||||
|
||||
## Core functions
|
||||
|
||||
### `identify(path)`
|
||||
Returns format, architecture, endianness, entry point, and Thumb-entry flag.
|
||||
|
||||
### `_parse_elf(data)` / `_parse_macho(data)`
|
||||
Header parsers reading `e_machine`/`e_entry` (ELF) and CPU type (Mach-O).
|
||||
|
||||
## Machine types
|
||||
|
||||
- ELF: `0x28` ARM, `0xB7` AArch64.
|
||||
- Mach-O: `0x0000000C` ARM, `0x0100000C` ARM64.
|
||||
|
||||
## Calling convention notes
|
||||
|
||||
Args in `r0-r3`/`x0-x7`, return `r0`/`x0`, syscall via `svc` with number in `r7`/`x8`.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py identify sample.bin
|
||||
```
|
||||
|
||||
## Sources
|
||||
|
||||
- Arm Architecture Reference Manual — https://developer.arm.com/documentation/
|
||||
- ELF for the Arm Architecture (AAELF), `$a`/`$t` mapping symbols — https://github.com/ARM-software/abi-aa
|
||||
- ELF `e_machine` 0x28/0xB7 and Mach-O CPU types from the respective format specifications
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Identify ARM/AArch64 binary architecture, endianness, entry point, and Thumb state.
|
||||
|
||||
Parses ELF and Mach-O headers to report the ARM machine type, bitness, endianness, and entry
|
||||
point, flagging Thumb entry for 32-bit ARM ELF (low bit of e_entry). Reads bytes statically;
|
||||
executes nothing. Capstone (optional) can be used downstream for actual decoding.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import struct
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ELF_MACHINE = {0x28: "ARM (32-bit)", 0xB7: "AArch64 (64-bit)"}
|
||||
MACHO_CPU = {0x0000000C: "ARM (32-bit)", 0x0100000C: "AArch64 (64-bit)"}
|
||||
|
||||
|
||||
def _parse_elf(data: bytes) -> dict:
|
||||
ei_class = data[4] # 1=32, 2=64
|
||||
ei_data = data[5] # 1=LE, 2=BE
|
||||
endian = "<" if ei_data == 1 else ">"
|
||||
machine = struct.unpack_from(endian + "H", data, 18)[0]
|
||||
if ei_class == 1:
|
||||
entry = struct.unpack_from(endian + "I", data, 24)[0]
|
||||
else:
|
||||
entry = struct.unpack_from(endian + "Q", data, 24)[0]
|
||||
thumb = bool(machine == 0x28 and (entry & 1))
|
||||
return {
|
||||
"format": "ELF",
|
||||
"architecture": ELF_MACHINE.get(machine, hex(machine)),
|
||||
"endianness": "little" if ei_data == 1 else "big",
|
||||
"entry_point": hex(entry & ~1 if thumb else entry),
|
||||
"thumb_entry": thumb,
|
||||
}
|
||||
|
||||
|
||||
def _parse_macho(data: bytes) -> dict:
|
||||
magic = struct.unpack_from("<I", data, 0)[0]
|
||||
big = magic in (0xFEEDFACF, 0xFEEDFACE) and data[0] == 0xFE
|
||||
endian = ">" if big else "<"
|
||||
cputype = struct.unpack_from(endian + "I", data, 4)[0]
|
||||
return {
|
||||
"format": "Mach-O",
|
||||
"architecture": MACHO_CPU.get(cputype, hex(cputype)),
|
||||
"endianness": "big" if big else "little",
|
||||
"entry_point": "parse LC_MAIN/LC_UNIXTHREAD for entry",
|
||||
"thumb_entry": False,
|
||||
}
|
||||
|
||||
|
||||
def identify(path: str) -> dict:
|
||||
data = Path(path).read_bytes()
|
||||
if data[:4] == b"\x7fELF":
|
||||
return _parse_elf(data)
|
||||
if struct.unpack_from("<I", data, 0)[0] in (0xFEEDFACE, 0xFEEDFACF, 0xCEFAEDFE, 0xCFFAEDFE):
|
||||
return _parse_macho(data)
|
||||
return {"error": "not an ELF or Mach-O binary"}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Identify ARM/AArch64 binaries")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("identify")
|
||||
p.add_argument("file")
|
||||
args = parser.parse_args(argv)
|
||||
print(json.dumps(identify(args.file), indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user