diff options
Diffstat (limited to 'idatui')
| -rw-r--r-- | idatui/app.py | 11 | ||||
| -rw-r--r-- | idatui/domain.py | 24 | ||||
| -rw-r--r-- | idatui/formats.py | 15 |
3 files changed, 40 insertions, 10 deletions
diff --git a/idatui/app.py b/idatui/app.py index ff67eef..ec55911 100644 --- a/idatui/app.py +++ b/idatui/app.py @@ -5191,13 +5191,22 @@ class IdaTui(App): verb = f"{mode} @ {ea:#x}" if r.get("forced_32bit"): verb += " (segment set to 32-bit; Thumb needs ARM32)" + if r.get("db_64bit"): + # Disassembly will look right and F5 will never work. + verb += (" \u26a0 this database is 64-bit, so Hex-Rays " + "won't decompile it \u2014 Ctrl+L and pick " + "arm:ARMv7-A") verb += (f" \u2014 {n} instruction{'s' if n != 1 else ''}" if n else " \u2014 still doesn't decode") # falls through to the shared reload: same cache bump, same # anchor restore, same flash. That is the whole point of having # one path. elif kind == "func": - self.program.define_func(ea) + r = self.program.define_func(ea) + if r.get("start") and r.get("end"): + verb = (f"created function {r['start']}\u2013{r['end']}" + + (" (end worked out from the code)" + if r.get("how") == "explicit-end" else "")) elif kind == "string": s = self.program.make_string(ea) verb = f"made string ({s[:24]!r})" if s else verb diff --git a/idatui/domain.py b/idatui/domain.py index 8787431..9047d82 100644 --- a/idatui/domain.py +++ b/idatui/domain.py @@ -1397,12 +1397,24 @@ class Program: f"@ {ea:#x}: {(r or {}).get('error', 'failed')}") return r - def define_func(self, ea: int) -> None: - """Create a function starting at ``ea`` (IDA's 'p').""" - res = self._first_result( - self.client.call("define_func", items=[{"addr": hex(ea)}])) - if res.get("error"): - raise IDAToolError("define_func", f"@ {ea:#x}: {res['error']}") + def define_func(self, ea: int) -> dict: + """Create a function starting at ``ea`` (IDA's 'p'). + + Prefers the injected tool, which works out the end when IDA can't; + falls back to the plain one for an older worker. + """ + try: + r = self.client.call("define_func_run", addr=hex(ea)) + except IDAToolError: + res = self._first_result( + self.client.call("define_func", items=[{"addr": hex(ea)}])) + if res.get("error"): + raise IDAToolError("define_func", f"@ {ea:#x}: {res['error']}") + return {"ok": True, "how": "legacy"} + if not isinstance(r, dict) or not r.get("ok"): + raise IDAToolError("define_func", + f"@ {ea:#x}: {(r or {}).get('error', 'failed')}") + return r def undefine(self, ea: int, size: int | None = None) -> None: """Undefine the item at ``ea`` back to raw bytes (IDA's 'u').""" diff --git a/idatui/formats.py b/idatui/formats.py index b2f784d..f09bb99 100644 --- a/idatui/formats.py +++ b/idatui/formats.py @@ -92,9 +92,18 @@ def needs_load_options(path: str) -> bool: #: reach for first — arm64, aarch64, mips, m68k — are all invalid. Re-run the #: script before adding to this list. PROCESSORS: tuple[tuple[str, str], ...] = ( - # 'arm' covers AArch64 too; arm64/aarch64 are NOT valid -p names, so they - # live in the label where the filter can still find them. - ("arm", "ARM / AArch64 / arm64 — little-endian"), + # Bare 'arm' gives a 64-BIT database in IDA 9 (AArch64). That matters far + # more than it looks: Hex-Rays refuses a 32-bit function in a 64-bit database + # ("only 64-bit functions can be decompiled in the current database"), and + # Thumb doesn't exist in AArch64 at all — so a 32-bit ARM image loaded as + # plain 'arm' disassembles wrongly and can never be decompiled. The database + # bitness is fixed at load; it cannot be corrected afterwards (setting it + # post-hoc makes the decompiler INTERR). Pick the right one here. + ("arm", "ARM64 / AArch64 / arm64 — 64-bit, little-endian"), + ("arm:ARMv7-A", "ARM 32-bit (ARMv7-A) — Thumb capable, most firmware"), + ("arm:ARMv7-M", "ARM 32-bit (ARMv7-M) — Cortex-M, Thumb only"), + ("arm:ARMv6-M", "ARM 32-bit (ARMv6-M) — Cortex-M0/M0+"), + ("arm:ARMv5TE", "ARM 32-bit (ARMv5TE) — older SoCs"), ("armb", "ARM — big-endian"), ("metapc", "x86 / x86-64"), ("mipsl", "MIPS — little-endian"), |
