From 02d02417800184fb76cd0245cdaa94c437aa4081 Mon Sep 17 00:00:00 2001 From: blasty Date: Fri, 21 Aug 2026 12:14:46 +0200 Subject: reformat: ruff format + import sort, mechanically (see ruff.toml) No behavior. Listed in .git-blame-ignore-revs (next commit). --- experiments/opfmt_tools.py | 241 ++++++++++++++++++++++++++++++++------------- 1 file changed, 175 insertions(+), 66 deletions(-) (limited to 'experiments/opfmt_tools.py') diff --git a/experiments/opfmt_tools.py b/experiments/opfmt_tools.py index 535688c..7654677 100644 --- a/experiments/opfmt_tools.py +++ b/experiments/opfmt_tools.py @@ -8,6 +8,7 @@ does to the database, what Hex-Rays will and won't render. python3 experiments/opfmt_tools.py # 29 checks, ~40s """ + import importlib.util import os import shutil @@ -30,15 +31,21 @@ if os.path.exists(seed): shutil.copy(seed, tmp + ".i64") import idapro # noqa: E402 + idapro.enable_console_messages(False) assert idapro.open_database(tmp, run_auto_analysis=True) == 0 import ida_auto # noqa: E402 + ida_auto.auto_wait() -import ida_typeinf, idaapi, ida_bytes, ida_lines # noqa: E402,F401 +import ida_bytes +import ida_lines +import ida_typeinf # noqa: E402,F401 +import idaapi spec = importlib.util.spec_from_file_location( - "_patch", os.path.join(REPO, "server", "patch_server.py")) + "_patch", os.path.join(REPO, "server", "patch_server.py") +) patch = importlib.util.module_from_spec(spec) spec.loader.exec_module(patch) @@ -101,14 +108,25 @@ while e < ea + 0x400: if target: break e = ida_bytes.next_head(e, ea + 0x800) -print("target:", hex(target[0]), "n=", target[1], "value", hex(target[2]), - "|", line(target[0])) +print( + "target:", + hex(target[0]), + "n=", + target[1], + "value", + hex(target[2]), + "|", + line(target[0]), +) tea, tn, tv = target r = op_format(addr=hex(tea), mode="show") print(" show:", r) -check("show reports the operand and its choices", - r.get("n") == tn and "hex" in r.get("choices", []), str(r)) +check( + "show reports the operand and its choices", + r.get("n") == tn and "hex" in r.get("choices", []), + str(r), +) check("show doesn't change anything", r.get("applied") is False, str(r)) seen = [] @@ -116,15 +134,25 @@ for i in range(8): r = op_format(addr=hex(tea), mode="cycle") seen.append((r.get("format"), r.get("text"))) print(f" cycle -> {r.get('prev')} -> {r.get('format')}: {r.get('text')}") -check("cycling returns to where it started", - seen[0][0] == seen[len(r.get("choices", []))][0] - if len(seen) > len(r.get("choices", [])) else True, str(seen)) -check("decimal renders differently from hex", - any(s[1] != seen[0][1] for s in seen), str(seen)) +check( + "cycling returns to where it started", + seen[0][0] == seen[len(r.get("choices", []))][0] + if len(seen) > len(r.get("choices", [])) + else True, + str(seen), +) +check( + "decimal renders differently from hex", + any(s[1] != seen[0][1] for s in seen), + str(seen), +) r = op_format(addr=hex(tea), mode="dec") -check("explicit dec sticks", r.get("format") == "dec" and str(tv) in r.get("text", ""), - str(r)) +check( + "explicit dec sticks", + r.get("format") == "dec" and str(tv) in r.get("text", ""), + str(r), +) r = op_format(addr=hex(tea), mode="back") print(" back ->", r.get("format"), r.get("text")) check("back steps the ring the other way", r.get("format") == "hex", str(r)) @@ -142,29 +170,55 @@ while e < ea + 0x800: if off_target: break e = ida_bytes.next_head(e, ea + 0x800) -print("target:", off_target and hex(off_target[0]), "|", - off_target and line(off_target[0])) +print( + "target:", + off_target and hex(off_target[0]), + "|", + off_target and line(off_target[0]), +) if off_target: import ida_name # noqa: E402 + oe, on = off_target v, _w = NS["_idatui_op_value"](oe, on) named = bool(ida_name.get_ea_name(v)) r = op_format(addr=hex(oe), n=on, mode="show") - print(" ", hex(oe), "n=", on, "|", r.get("text"), r.get("choices"), - "target named:", named) - check("an unnamed target is not a cycle stop (it would invent a name)", - ("offset" in r.get("choices", [])) == named, str(r)) + print( + " ", + hex(oe), + "n=", + on, + "|", + r.get("text"), + r.get("choices"), + "target named:", + named, + ) + check( + "an unnamed target is not a cycle stop (it would invent a name)", + ("offset" in r.get("choices", [])) == named, + str(r), + ) r = op_format(addr=hex(oe), n=on, mode="offset") print(" offset ->", r.get("text")) - check("but asking explicitly makes the reference", - r.get("format") == "offset" and "offset" in r.get("text", ""), str(r)) + check( + "but asking explicitly makes the reference", + r.get("format") == "offset" and "offset" in r.get("text", ""), + str(r), + ) r = op_format(addr=hex(oe), n=on, mode="show") - check("and from then on the ring includes it", - "offset" in r.get("choices", []), str(r)) + check( + "and from then on the ring includes it", + "offset" in r.get("choices", []), + str(r), + ) r = op_format(addr=hex(oe), n=on, mode="hex") print(" hex ->", r.get("text")) - check("and back to a number", r.get("format") == "hex" - and "offset" not in r.get("text", ""), str(r)) + check( + "and back to a number", + r.get("format") == "hex" and "offset" not in r.get("text", ""), + str(r), + ) op_format(addr=hex(oe), n=on, mode="default") else: print(" (no literal-that-is-an-address in this function)") @@ -175,22 +229,32 @@ spans = NS["_idatui_op_spans"](tea, txt) print(" text:", repr(txt), "spans:", spans) if len(spans) >= 2: r = op_format(addr=hex(tea), col=spans[0][0], mode="show") - check("a column inside operand 0 picks operand 0 (or the first literal)", - r.get("n") in (spans[0][2], NS["_idatui_op_candidates"](tea)[0]), str(r)) + check( + "a column inside operand 0 picks operand 0 (or the first literal)", + r.get("n") in (spans[0][2], NS["_idatui_op_candidates"](tea)[0]), + str(r), + ) r = op_format(addr=hex(tea), col=spans[-1][0], mode="show") - check("a column inside the last operand picks it", r.get("n") == spans[-1][2], - str(r)) + check( + "a column inside the last operand picks it", r.get("n") == spans[-1][2], str(r) + ) print("\n=== listing: an unmapped value refuses to become an offset ===") r = op_format(addr=hex(tea), n=tn, mode="offset") print(" ", r.get("error") or r) -check("offset on a non-address is refused, not invented", - bool(r.get("error")) or ida_bytes.is_mapped(tv), str(r)) +check( + "offset on a non-address is refused, not invented", + bool(r.get("error")) or ida_bytes.is_mapped(tv), + str(r), +) print("\n=== listing: char is only offered when it renders as one ===") r = op_format(addr=hex(tea), n=tn, mode="show") -check("0x%x isn't offered as a char" % tv, - ("char" in r["choices"]) == NS["_idatui_printable"](tv), str(r)) +check( + "0x%x isn't offered as a char" % tv, + ("char" in r["choices"]) == NS["_idatui_printable"](tv), + str(r), +) print("\n=== listing: a stack variable can be cycled AND put back ===") stk = None @@ -208,23 +272,32 @@ if stk: orig = line(se) r = op_format(addr=hex(se), n=sn, mode="cycle") print(" cycle ->", r.get("format"), r.get("text"), "|", r.get("warn")) - check("leaving a stack variable says so, and how to undo it", - "stack" in (r.get("warn") or ""), str(r)) - ring = [op_format(addr=hex(se), n=sn, mode="cycle") - for _ in range(len(r["choices"]))] + check( + "leaving a stack variable says so, and how to undo it", + "stack" in (r.get("warn") or ""), + str(r), + ) + ring = [ + op_format(addr=hex(se), n=sn, mode="cycle") for _ in range(len(r["choices"])) + ] print(" ring:", [(x["format"], x["text"]) for x in ring]) - check("the ring is the same at every step (a lap comes home)", - [x["format"] for x in ring] == r["choices"][1:] + r["choices"][:1], - f"{[x['format'] for x in ring]} vs {r['choices']}") + check( + "the ring is the same at every step (a lap comes home)", + [x["format"] for x in ring] == r["choices"][1:] + r["choices"][:1], + f"{[x['format'] for x in ring]} vs {r['choices']}", + ) r = op_format(addr=hex(se), n=sn, mode="stack") - check("'stack' puts the frame variable back", - r.get("format") == "stack" and r.get("text") == orig, - f"{r.get('text')!r} want {orig!r}") + check( + "'stack' puts the frame variable back", + r.get("format") == "stack" and r.get("text") == orig, + f"{r.get('text')!r} want {orig!r}", + ) else: print(" (no stack-variable operand found)") print("\n=== data item ===") import ida_segment # noqa: E402 + seg = ida_segment.get_segm_by_name(".data") if seg: de = seg.start_ea @@ -257,8 +330,11 @@ while e < seg.end_ea and ue is None: if ue is not None: r = op_format(addr=hex(ue), mode="cycle") print(" ", hex(ue), "->", r.get("error")) - check("undefined bytes are refused with the fix, not a silent no-op", - "define" in (r.get("error") or ""), str(r)) + check( + "undefined bytes are refused with the fix, not a silent no-op", + "define" in (r.get("error") or ""), + str(r), + ) else: print(" (no undefined bytes)") @@ -268,6 +344,7 @@ print(" show without a line:", r.get("error")) check("no line is an error, not a guess", bool(r.get("error")), str(r)) import ida_hexrays # noqa: E402 + cf = ida_hexrays.decompile(parse_address("main")) sv = cf.get_pseudocode() pcline = None @@ -276,8 +353,13 @@ for i in range(len(sv)): if nums and nums[0]["value"] > 9: pcline = (i, nums[0]) break -print(" line", pcline[0], repr(ida_lines.tag_remove(sv[pcline[0]].line).strip()), - "num:", pcline[1]) +print( + " line", + pcline[0], + repr(ida_lines.tag_remove(sv[pcline[0]].line).strip()), + "num:", + pcline[1], +) i, num = pcline r = pc_num_format(addr="main", line=i, mode="show") check("show finds the literal", r.get("ea") == hex(num["ea"]), str(r)) @@ -293,8 +375,11 @@ print(" bin ->", r.get("error")) check("binary is refused with a reason", bool(r.get("error")), str(r)) r = pc_num_format(addr="main", line=i, mode="default") print(" default ->", r.get("text")) -check("default restores Hex-Rays' own choice", - r.get("text") == ida_lines.tag_remove(sv[i].line).strip(), str(r)) +check( + "default restores Hex-Rays' own choice", + r.get("text") == ida_lines.tag_remove(sv[i].line).strip(), + str(r), +) r = pc_num_format(addr="main", line=i, mode="show") check("and the format reads back as default", r.get("format") == "default", str(r)) print("\n=== pseudocode: the ring visits every stop ===") @@ -303,10 +388,16 @@ for _ in range(len(r.get("choices", [])) * 2): rr = pc_num_format(addr="main", line=i, mode="cycle") ring.append((rr.get("format"), rr.get("text"))) print(" ", [x[0] for x in ring]) -check("every stop in the ring is reached", - set(x[0] for x in ring) == set(r["choices"]), f"{ring} vs {r['choices']}") -check("the ring's renderings are distinct", - len({x[1] for x in ring}) >= len(r["choices"]) - 1, str(ring)) +check( + "every stop in the ring is reached", + set(x[0] for x in ring) == set(r["choices"]), + f"{ring} vs {r['choices']}", +) +check( + "the ring's renderings are distinct", + len({x[1] for x in ring}) >= len(r["choices"]) - 1, + str(ring), +) pc_num_format(addr="main", line=i, mode="default") print("\n=== pseudocode: col picks the literal ===") @@ -327,9 +418,12 @@ if multi: col = len(NS["_idatui_compact"](plain[:x]).rstrip()) if x else 0 r = pc_num_format(addr="main", line=k, col=col, mode="show") print(" col", col, "->", r.get("ea"), r.get("value")) - check("a column selects the number under it", - r.get("value") == hex(nums[1]["value"]) - or r.get("value") == hex(nums[0]["value"]), str(r)) + check( + "a column selects the number under it", + r.get("value") == hex(nums[1]["value"]) + or r.get("value") == hex(nums[0]["value"]), + str(r), + ) else: print(" (no line with two literals)") @@ -342,8 +436,11 @@ if row and row.get("ops"): t = row["text"] for lo, hi, n in row["ops"]: print(f" op{n}: {t[lo:hi]!r}") - check("the extents index the row's own text", - all(t[lo:hi].strip() for lo, hi, n in row["ops"]), str(row["ops"])) + check( + "the extents index the row's own text", + all(t[lo:hi].strip() for lo, hi, n in row["ops"]), + str(row["ops"]), + ) # and they agree with what op_format picks for a column inside them ok = True for lo, hi, n in row["ops"]: @@ -361,8 +458,11 @@ if regop: lo, hi, n = regop r = op_format(addr=hex(tea), col=(lo + hi) // 2, mode="cycle") print(" ", repr(row["text"][lo:hi]), "->", r.get("error")) - check("it names the operand and the one that CAN change", - bool(r.get("error")) and "operand" in r["error"], str(r)) + check( + "it names the operand and the one that CAN change", + bool(r.get("error")) and "operand" in r["error"], + str(r), + ) else: print(" (this instruction has no register-only operand)") @@ -376,15 +476,24 @@ for rec in pn["nums"]: two = next((v for v in multi2.values() if len(v) >= 2), None) if two: import ida_hexrays as _hx + cf2 = _hx.decompile(parse_address("main")) - disp = NS["_idatui_compact"](ida_lines.tag_remove(cf2.get_pseudocode()[two[0]["line"]].line)) + disp = NS["_idatui_compact"]( + ida_lines.tag_remove(cf2.get_pseudocode()[two[0]["line"]].line) + ) print(" line:", repr(disp.strip())) for rec in two: - print(f" x{rec['x0']}..{rec['x1']} = {disp[rec['x0']:rec['x1']]!r} value {rec['value']}") - check("spans land on the literals in the DISPLAYED text", - all(disp[r0["x0"]:r0["x1"]].strip() for r0 in two), str(two)) - check("distinct literals get distinct spans", - two[0]["x0"] != two[1]["x0"], str(two)) + print( + f" x{rec['x0']}..{rec['x1']} = {disp[rec['x0'] : rec['x1']]!r} value {rec['value']}" + ) + check( + "spans land on the literals in the DISPLAYED text", + all(disp[r0["x0"] : r0["x1"]].strip() for r0 in two), + str(two), + ) + check( + "distinct literals get distinct spans", two[0]["x0"] != two[1]["x0"], str(two) + ) print(f"\n{OK} passed, {FAIL} failed") idapro.close_database(save=False) -- cgit v1.3.1-sl0p