Skip to content

Instantly share code, notes, and snippets.

@jimmy947788
Created July 30, 2025 08:41
Show Gist options
  • Save jimmy947788/495ebbd6a6660ab219a7ccc525272f37 to your computer and use it in GitHub Desktop.
Save jimmy947788/495ebbd6a6660ab219a7ccc525272f37 to your computer and use it in GitHub Desktop.
IDA 腳本讀取 blutter產生的 pp.json 並將常數字串,註解在對應的IDA反編譯指令碼旁
import idaapi
import idautils
import idc
import ida_search
import ida_kernwin
import os
import ida_bytes
import ida_segment
import json
from collections import defaultdict
# --------------------------------------------------
# 日誌功能(可改用 loguru,如需請自行加回)
def info(msg):
print(f"[INFO] {msg}")
def error(msg):
print(f"[ERROR] {msg}")
def debug(msg):
pass # print(f"[DEBUG] {msg}")
# --------------------------------------------------
# 載入 pp.json,支援 offset 標準化
def load_pp_json(pp_path):
if not os.path.exists(pp_path):
error("pp.json file not exists.")
raise Exception("pp.json file not exists.")
with open(pp_path, 'r') as f:
raw = json.load(f)
pp_data = dict()
# 標準化所有 key 格式:去除大寫、補零、多種 [pp+xxx] 型式
for k, v in raw.items():
if k.startswith("[pp+") and k.endswith("]"):
off_hex = k[4:-1].lower().lstrip("0x").zfill(1)
pp_data[f"[pp+0x{off_hex}]"] = v
pp_data[f"[pp+{int(off_hex,16)}]"] = v
pp_data[f"[pp+{off_hex}]"] = v
return pp_data
def get_pp_value(pp_data, offset):
# 標準化 key,嘗試多種形式
keys = [
f"[pp+0x{offset:x}]",
f"[pp+{offset}]",
f"[pp+{hex(offset)}]",
f"[pp+{offset:x}]"
]
for k in keys:
if k in pp_data:
return pp_data[k]
return None
# --------------------------------------------------
# 匹配 pattern helper
def match_add_pool(insn):
# 支援 ADD/ADR/ADRP MOV
# 必須是 ADD X?, X27, #imm,LSL#12
return (insn.itype in [idaapi.ARM_add, idaapi.ARM_adrp, idaapi.ARM_adr, idaapi.ARM_mov]) and \
(insn.ops[1].type == idaapi.o_reg and insn.ops[1].reg in [156,155]) and \
(insn.ops[2].type in [idaapi.o_imm, idaapi.o_displ])
def match_ldr_pool(insn):
# LDR/STR Wn/Xn, [Xn, #offset]
return (insn.itype == idaapi.ARM_ldr) and \
(insn.ops[1].type == idaapi.o_displ)
# --------------------------------------------------
# 搜尋核心
def search_and_annotate(pp_data):
pool_xrefs = defaultdict(list)
unmapped_offsets = set()
total_segments = sum(1 for _ in idautils.Segments())
processed_segments = 0
for seg_start in idautils.Segments():
seg = idaapi.getseg(seg_start)
seg_name = ida_segment.get_segm_name(seg)
seg_type = seg.type
if seg_type != idaapi.SEG_CODE:
continue
info(f"搜尋段: {seg_name}")
start_ea = seg_start
end_ea = seg.end_ea
ea = start_ea
seg_size = end_ea - start_ea
last_progress = -1
while ea < end_ea:
# 進度顯示
current_progress = int(100 * (ea - start_ea) / seg_size)
if current_progress % 10 == 0 and current_progress != last_progress:
info(f" 進度 {current_progress}% @ 0x{ea:x}")
last_progress = current_progress
# 嘗試解出 ADD 指令
insn = idaapi.insn_t()
if not idaapi.decode_insn(insn, ea):
ea += 4
continue
if not match_add_pool(insn):
ea += 4
continue
add_reg = insn.ops[0].reg
base_reg = insn.ops[1].reg
imm_val = insn.ops[2].value
shift = getattr(insn.ops[2], "shft", 12)
offset = imm_val << shift if shift else imm_val
# 在 1~3 條指令內找對應 LDR
for lookahead in range(1, 4):
ldr_ea = ea + lookahead * 4
ldr_insn = idaapi.insn_t()
if not idaapi.decode_insn(ldr_insn, ldr_ea):
continue
if match_ldr_pool(ldr_insn) and ldr_insn.ops[0].reg == add_reg:
ldr_offset = ldr_insn.ops[1].addr
total_offset = offset + ldr_offset
# 清除舊註解
idc.set_cmt(ldr_ea, "", 0)
# 查 pp
value = get_pp_value(pp_data, total_offset)
if value is None:
unmapped_offsets.add(total_offset)
comment = f"[pp+0x{total_offset:x}] (未找到pp常數)"
else:
comment = f"[pp+0x{total_offset:x}] {value}"
idc.set_cmt(ldr_ea, comment, 0)
pool_xrefs[total_offset].append(ldr_ea)
info(f" 註解 {hex(ldr_ea)} ← {comment}")
break # 若找到即跳出
ea += 4
processed_segments += 1
info(f" 完成 {seg_name} ({processed_segments}/{total_segments})")
return pool_xrefs, unmapped_offsets
# --------------------------------------------------
# 執行
def main():
pp_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "pp.json")
info(f"pp_path: {pp_path}")
info("載入 pp.json ...")
pp_data = load_pp_json(pp_path)
info("載入 pp.json 成功!")
pool_xrefs, unmapped_offsets = search_and_annotate(pp_data)
# 最後報表
print("\n===== 所有 pool offset XREF 報表 =====")
for offset, addrs in sorted(pool_xrefs.items()):
print(f"[pp+0x{offset:x}] used at:", ", ".join([hex(a) for a in addrs]))
if unmapped_offsets:
print("\n===== 未對應到 pool 字串 offset(請補齊 pp.json)=====")
for off in sorted(unmapped_offsets):
print(f" [pp+0x{off:x}]")
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment