建立 MAG160C 逆向工程交接仓库
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Dict, Iterable, List, Optional, Tuple
|
||||
|
||||
from capstone import Cs, CS_ARCH_ARM64, CS_MODE_ARM
|
||||
from capstone.arm64 import ARM64_OP_IMM
|
||||
from elftools.elf.elffile import ELFFile
|
||||
from elftools.elf.relocation import RelocationSection
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Symbol:
|
||||
name: str
|
||||
addr: int
|
||||
size: int
|
||||
bind: str
|
||||
typ: str
|
||||
|
||||
|
||||
class ElfAnalyzer:
|
||||
def __init__(self, path: str | Path):
|
||||
self.path = Path(path)
|
||||
self._fp = self.path.open("rb")
|
||||
self.elf = ELFFile(self._fp)
|
||||
self._symbols = self._collect_symbols()
|
||||
self._symbols_by_name = {s.name: s for s in self._symbols}
|
||||
self._symbols_by_addr = {s.addr: s for s in self._symbols if s.addr}
|
||||
self._plt_symbols = self._collect_plt_symbols()
|
||||
self._cs = Cs(CS_ARCH_ARM64, CS_MODE_ARM)
|
||||
self._cs.detail = True
|
||||
|
||||
def close(self) -> None:
|
||||
self._fp.close()
|
||||
|
||||
def _collect_symbols(self) -> List[Symbol]:
|
||||
out: List[Symbol] = []
|
||||
for section in self.elf.iter_sections():
|
||||
if section["sh_type"] not in ("SHT_SYMTAB", "SHT_DYNSYM"):
|
||||
continue
|
||||
for sym in section.iter_symbols():
|
||||
name = sym.name
|
||||
if not name:
|
||||
continue
|
||||
info = sym["st_info"]
|
||||
out.append(
|
||||
Symbol(
|
||||
name=name,
|
||||
addr=int(sym["st_value"]),
|
||||
size=int(sym["st_size"]),
|
||||
bind=str(info["bind"]),
|
||||
typ=str(info["type"]),
|
||||
)
|
||||
)
|
||||
out.sort(key=lambda s: (s.addr, s.name))
|
||||
return out
|
||||
|
||||
def _collect_plt_symbols(self) -> Dict[int, str]:
|
||||
# AArch64 ELF PLT layout: the resolver entry is 32 bytes, then one
|
||||
# 16-byte entry per relocation in .rela.plt order.
|
||||
plt = self.elf.get_section_by_name(".plt")
|
||||
if plt is None:
|
||||
return {}
|
||||
out: Dict[int, str] = {}
|
||||
plt_addr = int(plt["sh_addr"])
|
||||
entry_addr = plt_addr + 32
|
||||
for section in self.elf.iter_sections():
|
||||
if not isinstance(section, RelocationSection):
|
||||
continue
|
||||
if section.name not in (".rela.plt", ".rela.plt.sec", ".rel.plt"):
|
||||
continue
|
||||
symtab = self.elf.get_section(section["sh_link"])
|
||||
for index, rel in enumerate(section.iter_relocations()):
|
||||
sym = symtab.get_symbol(rel["r_info_sym"])
|
||||
if sym.name:
|
||||
out[entry_addr + index * 16] = sym.name + "@plt"
|
||||
return out
|
||||
|
||||
def symbols(self, pattern: Optional[str] = None) -> List[Symbol]:
|
||||
if pattern is None:
|
||||
return list(self._symbols)
|
||||
rx = re.compile(pattern)
|
||||
return [s for s in self._symbols if rx.search(s.name)]
|
||||
|
||||
def find_symbol(self, name: str) -> Optional[Symbol]:
|
||||
return self._symbols_by_name.get(name)
|
||||
|
||||
def describe_addr(self, addr: int) -> str:
|
||||
if addr in self._plt_symbols:
|
||||
return self._plt_symbols[addr]
|
||||
if addr in self._symbols_by_addr:
|
||||
return self._symbols_by_addr[addr].name
|
||||
best: Optional[Symbol] = None
|
||||
for sym in self._symbols:
|
||||
if sym.addr and sym.size and sym.addr <= addr < sym.addr + sym.size:
|
||||
if best is None or sym.addr > best.addr:
|
||||
best = sym
|
||||
if best is not None:
|
||||
return f"{best.name}+0x{addr - best.addr:x}"
|
||||
return f"0x{addr:x}"
|
||||
|
||||
def vaddr_to_offset(self, addr: int) -> int:
|
||||
for seg in self.elf.iter_segments():
|
||||
if seg["p_type"] != "PT_LOAD":
|
||||
continue
|
||||
start = int(seg["p_vaddr"])
|
||||
size = int(seg["p_memsz"])
|
||||
if start <= addr < start + size:
|
||||
return int(seg["p_offset"]) + (addr - start)
|
||||
raise ValueError(f"virtual address 0x{addr:x} is not in a PT_LOAD segment")
|
||||
|
||||
def read_vaddr(self, addr: int, size: int) -> bytes:
|
||||
off = self.vaddr_to_offset(addr)
|
||||
self._fp.seek(off)
|
||||
return self._fp.read(size)
|
||||
|
||||
def disassemble_symbol(self, name: str, max_bytes: Optional[int] = None) -> List[str]:
|
||||
sym = self.find_symbol(name)
|
||||
if sym is None:
|
||||
raise KeyError(name)
|
||||
if sym.size <= 0:
|
||||
raise ValueError(f"symbol {name} has no size")
|
||||
size = min(sym.size, max_bytes) if max_bytes else sym.size
|
||||
code = self.read_vaddr(sym.addr, size)
|
||||
lines = [f"{name} @ 0x{sym.addr:x} size=0x{sym.size:x}"]
|
||||
for insn in self._cs.disasm(code, sym.addr):
|
||||
comment = ""
|
||||
if insn.mnemonic in {"b", "bl", "cbz", "cbnz", "tbz", "tbnz"}:
|
||||
imm = self._first_imm(insn)
|
||||
if imm is not None:
|
||||
comment = f" ; {self.describe_addr(imm)}"
|
||||
lines.append(f"0x{insn.address:08x}:\t{insn.mnemonic}\t{insn.op_str}{comment}")
|
||||
return lines
|
||||
|
||||
@staticmethod
|
||||
def _first_imm(insn) -> Optional[int]:
|
||||
for op in insn.operands:
|
||||
if op.type == ARM64_OP_IMM:
|
||||
return int(op.imm)
|
||||
return None
|
||||
|
||||
|
||||
def _default_so() -> Path:
|
||||
return (
|
||||
Path.cwd()
|
||||
/ "IR_Camera_SDK-1.0.1"
|
||||
/ "android"
|
||||
/ "android"
|
||||
/ "lib"
|
||||
/ "arm64-v8a"
|
||||
/ "libcoresdk.so"
|
||||
)
|
||||
|
||||
|
||||
def main(argv: Optional[List[str]] = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Small ARM64 ELF disassembler for libcoresdk.so")
|
||||
parser.add_argument("--elf", default=str(_default_so()))
|
||||
parser.add_argument("--symbol", action="append", default=[])
|
||||
parser.add_argument("--grep-symbols", help="Regex for listing matching symbols")
|
||||
parser.add_argument("--max-bytes", type=lambda s: int(s, 0), default=None)
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
analyzer = ElfAnalyzer(args.elf)
|
||||
try:
|
||||
if args.grep_symbols:
|
||||
for sym in analyzer.symbols(args.grep_symbols):
|
||||
print(f"0x{sym.addr:08x}\t0x{sym.size:x}\t{sym.typ}\t{sym.name}")
|
||||
for name in args.symbol:
|
||||
for line in analyzer.disassemble_symbol(name, args.max_bytes):
|
||||
print(line)
|
||||
print()
|
||||
finally:
|
||||
analyzer.close()
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user