diff options
| author | blasty <blasty@local> | 2026-07-10 14:53:20 +0200 |
|---|---|---|
| committer | blasty <blasty@local> | 2026-07-10 14:53:20 +0200 |
| commit | b8d0335682758ef51eab1c6dea6435b11d4e0bd2 (patch) | |
| tree | b37fdb91e74dbe136f2d0d757c2062937be82b1c /idatui/domain.py | |
| parent | hex view: raw image bytes (VA-addressed), synced to the code cursor (diff) | |
| download | ida-tui-b8d0335682758ef51eab1c6dea6435b11d4e0bd2.tar.gz ida-tui-b8d0335682758ef51eab1c6dea6435b11d4e0bd2.tar.xz ida-tui-b8d0335682758ef51eab1c6dea6435b11d4e0bd2.zip | |
hex view: file offsets + 'g' goto (dedicated input)
Add a file_regions server tool (get_fileregion_offset per segment) and
Program.file_regions/file_offset so a virtual address maps to its raw
on-disk offset without a format-specific header parser. The hex view now
shows a VA column and a file-offset column side by side, and the status
line reports file+off.
Goto moves to its own top-level #goto Input instead of overloading the
function-filter box (which is hidden with the names pane, so goto was
unreachable there). In the hex view 'g' jumps the cursor to an address
(bounds-checked against the image); elsewhere it navigates as before.
Diffstat (limited to 'idatui/domain.py')
| -rw-r--r-- | idatui/domain.py | 31 |
1 files changed, 31 insertions, 0 deletions
diff --git a/idatui/domain.py b/idatui/domain.py index 602d54d..826d0f7 100644 --- a/idatui/domain.py +++ b/idatui/domain.py @@ -418,6 +418,9 @@ class HexModel: def total_rows(self) -> int: return (self.size + 15) // 16 + def file_offset(self, va: int) -> int | None: + return self._prog.file_offset(va) + def _fetch_block(self, b: int) -> bytes: addr = self.start + b * self.BLOCK n = min(self.BLOCK, self.end - addr) @@ -492,6 +495,7 @@ class Program: self._decomp: dict[int, tuple[Decompilation, int]] = {} self._name_gen = 0 # bumped on rename; invalidates stale name caches self._sections: list[tuple[int, int, str]] | None = None + self._fileregions: list[tuple[int, int, int]] | None = None self._hexmodel: "HexModel | None" = None self._lock = threading.Lock() @@ -553,6 +557,33 @@ class Program: self._hexmodel = HexModel(self, rng[0], rng[1]) return self._hexmodel + def file_regions(self) -> list[tuple[int, int, int]]: + """Sorted [(start, end, file_off)] mapping loaded segments to raw file + offsets (file_off == -1 for non-file-backed, e.g. .bss). Cached; needs + the injected ``file_regions`` server tool.""" + if self._fileregions is not None: + return self._fileregions + regions: list[tuple[int, int, int]] = [] + try: + r = self.client.call("file_regions") + for d in (r.get("regions", []) if isinstance(r, dict) else []): + if isinstance(d, dict) and "start" in d: + regions.append((_as_int(d["start"]), _as_int(d["end"]), + int(d.get("file_off", -1)))) + except IDAToolError: + regions = [] + regions.sort() + with self._lock: + self._fileregions = regions + return regions + + def file_offset(self, va: int) -> int | None: + """Raw on-disk file offset for ``va``, or None if not file-backed.""" + for start, end, fo in self.file_regions(): + if start <= va < end: + return (fo + (va - start)) if fo >= 0 else None + return None + def read_bytes(self, ea: int, n: int) -> bytes: """Raw bytes [ea, ea+n) from IDA (gaps read as zero).""" if n <= 0: |
