229 lines
6.7 KiB
Python
229 lines
6.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
# IDAPython extraction script for IDA Pro 6.6
|
|
# Equivalent to extract.idc — same JSON output structure
|
|
import sys
|
|
import traceback
|
|
import os
|
|
import json
|
|
|
|
import idc
|
|
import idaapi
|
|
import idautils
|
|
|
|
OUTPUT = "output.json"
|
|
|
|
# Cross-reference type constants (IDA SDK values)
|
|
FL_CF = 16 # Call Far
|
|
FL_CN = 17 # Call Near
|
|
FL_JF = 18 # Jump Far
|
|
FL_JN = 19 # Jump Near
|
|
|
|
|
|
def msg(text):
|
|
idc.Message(text)
|
|
sys.stderr.write(text)
|
|
sys.stderr.flush()
|
|
|
|
|
|
def _json_string(s):
|
|
"""Escape a string for JSON — returns the quoted representation.
|
|
Handles raw bytes in disassembly that aren't valid UTF-8."""
|
|
if isinstance(s, unicode):
|
|
return json.dumps(s)
|
|
try:
|
|
return json.dumps(s)
|
|
except UnicodeDecodeError:
|
|
return json.dumps(s.decode('latin-1'))
|
|
|
|
|
|
def _json_hex(addr):
|
|
"""Format an address as 0x-prefixed hex string (uppercase, 8 digits)."""
|
|
return "0x%08X" % addr
|
|
|
|
|
|
def extract_func(f, ea):
|
|
"""Extract a single function. Returns True on success, False on failure."""
|
|
func_name = idc.GetFunctionName(ea)
|
|
func_end = idc.FindFuncEnd(ea)
|
|
func_flags = idc.GetFunctionFlags(ea)
|
|
|
|
f.write(" {\n")
|
|
f.write(" \"function\": %s,\n" % _json_string(func_name))
|
|
f.write(" \"address\": \"%s\",\n" % _json_hex(ea))
|
|
f.write(" \"size\": %d,\n" % (func_end - ea))
|
|
|
|
# ---- flags ----
|
|
flags_parts = []
|
|
if func_flags & idc.FUNC_LIB:
|
|
flags_parts.append("LIBRARY")
|
|
if func_flags & idc.FUNC_THUNK:
|
|
flags_parts.append("THUNK")
|
|
if func_flags & idc.FUNC_FAR:
|
|
flags_parts.append("FAR")
|
|
if func_flags & idc.FUNC_FRAME:
|
|
flags_parts.append("FRAME")
|
|
f.write(" \"flags\": %s,\n" % _json_string("|".join(flags_parts)))
|
|
|
|
# ---- xrefs (who calls/jumps to this function) ----
|
|
xref_parts = []
|
|
for xref in idautils.XrefsTo(ea):
|
|
if xref.type in (FL_CF, FL_CN, FL_JF, FL_JN):
|
|
caller_name = idc.GetFunctionName(xref.frm)
|
|
if caller_name != "":
|
|
if xref.type == FL_CN or xref.type == FL_CF:
|
|
type_str = "CALL"
|
|
elif xref.type == FL_JN or xref.type == FL_JF:
|
|
type_str = "JUMP"
|
|
else:
|
|
type_str = "UNKNOWN"
|
|
xref_parts.append("%s:%s" % (caller_name, type_str))
|
|
f.write(" \"xrefs\": %s,\n" % _json_string(" ".join(xref_parts)))
|
|
f.flush()
|
|
|
|
# ---- assembly ----
|
|
asm_lines = []
|
|
curr_inst = ea
|
|
last_inst = 0
|
|
while curr_inst < func_end:
|
|
if curr_inst == last_inst:
|
|
break
|
|
last_inst = curr_inst
|
|
disasm = idc.GetDisasm(curr_inst)
|
|
if disasm:
|
|
asm_lines.append("%s %s" % (_json_hex(curr_inst), disasm))
|
|
curr_inst = curr_inst + idc.ItemSize(curr_inst)
|
|
f.write(" \"assembly\": %s,\n" % _json_string("\n".join(asm_lines)))
|
|
f.flush()
|
|
|
|
# ---- labels (named data/code references from this function) ----
|
|
label_parts = []
|
|
curr_inst = ea
|
|
last_inst = 0
|
|
while curr_inst < func_end:
|
|
if curr_inst == last_inst:
|
|
break
|
|
last_inst = curr_inst
|
|
for xref in idautils.XrefsFrom(curr_inst):
|
|
if xref.type in (FL_CF, FL_CN, FL_JF, FL_JN):
|
|
label_name = idc.Name(xref.to)
|
|
if label_name != "":
|
|
if xref.type == FL_CN or xref.type == FL_CF:
|
|
type_str = "CALL"
|
|
elif xref.type == FL_JN or xref.type == FL_JF:
|
|
type_str = "JUMP"
|
|
else:
|
|
type_str = "UNKNOWN"
|
|
label_parts.append(
|
|
"%s:%s:%s" % (label_name, _json_hex(xref.to), type_str)
|
|
)
|
|
curr_inst = curr_inst + idc.ItemSize(curr_inst)
|
|
f.write(" \"labels\": %s\n" % _json_string(" ".join(label_parts)))
|
|
f.write(" }")
|
|
f.flush()
|
|
return True
|
|
|
|
|
|
def extract(f):
|
|
entry_ea = idc.MinEA()
|
|
entry_name = idc.Name(entry_ea)
|
|
if entry_name == "":
|
|
entry_name = "entry_point"
|
|
f.write(
|
|
"{\n \"entry_point\": \"%s:%s\",\n" % (_json_hex(entry_ea), entry_name)
|
|
)
|
|
|
|
# ---- segments ----
|
|
f.write(" \"segments\": [\n")
|
|
segs = []
|
|
seg = idc.FirstSeg()
|
|
while seg != idc.BADADDR:
|
|
segs.append(
|
|
" {\"name\": %s, \"start\": \"%s\", \"end\": \"%s\"}"
|
|
% (
|
|
_json_string(idc.SegName(seg)),
|
|
_json_hex(idc.SegStart(seg)),
|
|
_json_hex(idc.SegEnd(seg)),
|
|
)
|
|
)
|
|
seg = idc.NextSeg(seg)
|
|
f.write(",\n".join(segs))
|
|
f.write("\n ],\n")
|
|
f.flush()
|
|
|
|
# ---- functions ----
|
|
f.write(" \"functions\": [\n")
|
|
f.flush()
|
|
|
|
ea = idc.NextFunction(0)
|
|
func_count = 0
|
|
error_count = 0
|
|
|
|
while ea != idc.BADADDR:
|
|
if func_count > 0:
|
|
f.write(",\n")
|
|
f.flush()
|
|
|
|
try:
|
|
if extract_func(f, ea):
|
|
func_count += 1
|
|
else:
|
|
error_count += 1
|
|
msg("[extract.py] Aviso: falha ao extrair funcao em %s\n"
|
|
% _json_hex(ea))
|
|
except Exception as e:
|
|
error_count += 1
|
|
tb_str = traceback.format_exc().strip().replace("\n", "\\n")
|
|
msg("[extract.py] Erro na funcao %s: %s\n"
|
|
% (_json_hex(ea), str(e)))
|
|
# Write a placeholder with the error info so JSON remains valid
|
|
f.write(
|
|
" {\"function\": \"__ERROR__\","
|
|
" \"address\": \"%s\","
|
|
" \"error\": %s}"
|
|
% (_json_hex(ea), _json_string(tb_str))
|
|
)
|
|
f.flush()
|
|
|
|
ea = idc.NextFunction(ea)
|
|
|
|
msg("[extract.py] Extraidas %d funcoes, %d erros.\n"
|
|
% (func_count, error_count))
|
|
|
|
f.write("\n ]\n}\n")
|
|
f.flush()
|
|
|
|
|
|
def main():
|
|
msg("[extract.py] Script iniciado. CWD=%s\n" % os.getcwd())
|
|
|
|
idaapi.autoWait()
|
|
msg("[extract.py] Analise concluida. Extraindo...\n")
|
|
|
|
try:
|
|
# Line-buffered — flushes after every newline, prevents data loss
|
|
f = open(OUTPUT, "w", 1)
|
|
except Exception as e:
|
|
msg("[extract.py] ERRO ao criar %s: %s\n" % (OUTPUT, str(e)))
|
|
idc.Exit(1)
|
|
return
|
|
|
|
try:
|
|
extract(f)
|
|
except Exception as e:
|
|
msg("[extract.py] ERRO na extracao: %s\n" % str(e))
|
|
msg(traceback.format_exc() + "\n")
|
|
finally:
|
|
f.close()
|
|
|
|
if os.path.exists(OUTPUT):
|
|
msg("[extract.py] %s gerado com sucesso (%d bytes)!\n" %
|
|
(OUTPUT, os.path.getsize(OUTPUT)))
|
|
else:
|
|
msg("[extract.py] FALHA: %s nao foi criado.\n" % OUTPUT)
|
|
|
|
idc.Exit(0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|