aboutsummaryrefslogtreecommitdiffstats
path: root/server/patch_server.py
diff options
context:
space:
mode:
Diffstat (limited to 'server/patch_server.py')
-rw-r--r--server/patch_server.py62
1 files changed, 62 insertions, 0 deletions
diff --git a/server/patch_server.py b/server/patch_server.py
index c9d2705..73147d0 100644
--- a/server/patch_server.py
+++ b/server/patch_server.py
@@ -1967,6 +1967,68 @@ def pc_num_format(
return out
+@tool
+@idasync
+def flowchart(
+ addr: Annotated[str, "Address or name inside the function to chart"],
+) -> dict:
+ """Basic-block control-flow graph of the function containing ``addr``.
+
+ Returns the blocks and the edges between them -- NOT their text: the block
+ body is just an address range, which the client already knows how to render
+ with ``heads``. Keeping text out means the graph view reuses the exact same
+ listing rows (colours, operand marks and all) instead of growing a second
+ disassembly renderer.
+
+ Edge ``kind`` is what the graph view colours by:
+ * ``fall`` -- control falls through to the next address (IDA draws red)
+ * ``jump`` -- a taken conditional branch (green)
+ * ``uncond`` -- the block's only successor (blue)
+ * ``switch`` -- one of an n-way dispatch
+ """
+ import ida_funcs
+ import ida_gdl
+
+ try:
+ ea = parse_address(addr)
+ except Exception as e:
+ return {"addr": str(addr), "error": str(e), "blocks": []}
+ fn = ida_funcs.get_func(ea)
+ if fn is None:
+ return {"addr": str(addr), "error": "no function at that address",
+ "blocks": []}
+
+ fc = ida_gdl.FlowChart(fn, flags=ida_gdl.FC_PREDS)
+ index = {}
+ order = []
+ for bb in fc:
+ index[bb.start_ea] = len(order)
+ order.append(bb)
+ blocks = []
+ for bb in order:
+ sl = [s for s in bb.succs() if s.start_ea in index]
+ succs = []
+ for s in sl:
+ if len(sl) > 2:
+ kind = "switch"
+ elif s.start_ea == bb.end_ea:
+ kind = "fall"
+ else:
+ kind = "jump"
+ succs.append([index[s.start_ea], kind])
+ blocks.append({
+ "id": index[bb.start_ea],
+ "start": hex(bb.start_ea),
+ "end": hex(bb.end_ea),
+ "succs": succs,
+ })
+ return {
+ "addr": hex(ea),
+ "func": {"addr": hex(fn.start_ea), "end": hex(fn.end_ea),
+ "name": ida_funcs.get_func_name(fn.start_ea)},
+ "entry": index.get(fn.start_ea, 0),
+ "blocks": blocks,
+ }
'''
SNIPPET = f"{BEGIN}\n{BODY.strip()}\n{END}\n"