backend/engines/ida66/extract.py

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()