mirror of
https://github.com/idapython/src
synced 2026-06-08 14:47:00 +00:00
119 lines
3.6 KiB
Python
119 lines
3.6 KiB
Python
"""
|
|
summary: react to trace notifications
|
|
|
|
description:
|
|
This script demonstrates using the low-level tracing hook
|
|
(ida_dbg.DBG_Hooks.dbg_trace). It can be run like so:
|
|
|
|
ida.exe -B -Sdbg_trace.py -Ltrace.log file.exe
|
|
|
|
level: advanced
|
|
"""
|
|
|
|
import time
|
|
|
|
import ida_dbg
|
|
import ida_ida
|
|
import ida_pro
|
|
import ida_ua
|
|
from ida_allins import NN_callni, NN_call, NN_callfi
|
|
from ida_lines import generate_disasm_line, GENDSM_FORCE_CODE, GENDSM_REMOVE_TAGS
|
|
|
|
# Note: this try/except block below is just there to
|
|
# let us (at Hex-Rays) test this script in various
|
|
# situations.
|
|
try:
|
|
import idc
|
|
print(idc.ARGV[1])
|
|
under_test = bool(idc.ARGV[1])
|
|
except:
|
|
under_test = False
|
|
|
|
class TraceHook(ida_dbg.DBG_Hooks):
|
|
def __init__(self):
|
|
ida_dbg.DBG_Hooks.__init__(self)
|
|
self.traces = 0
|
|
self.epReached = False
|
|
|
|
def _log(self, msg):
|
|
print(">>> %s" % msg)
|
|
|
|
def dbg_trace(self, tid, ea):
|
|
# Log all traced addresses
|
|
if ea < ida_ida.inf_get_min_ea() or ea > ida_ida.inf_get_max_ea():
|
|
raise Exception(
|
|
"Received a trace callback for an address outside this database!"
|
|
)
|
|
|
|
self._log("trace %08X" % ea)
|
|
self.traces += 1
|
|
insn = ida_ua.insn_t()
|
|
insnlen = ida_ua.decode_insn(insn, ea)
|
|
# log disassembly and ESP for call instructions
|
|
if insnlen > 0 and insn.itype in [NN_callni, NN_call, NN_callfi]:
|
|
self._log(
|
|
"call insn: %s"
|
|
% generate_disasm_line(ea, GENDSM_FORCE_CODE | GENDSM_REMOVE_TAGS)
|
|
)
|
|
self._log("ESP=%08X" % ida_dbg.get_reg_val("ESP"))
|
|
|
|
return 1
|
|
|
|
def dbg_run_to(self, pid, tid=0, ea=0):
|
|
# this hook is called once execution reaches temporary breakpoint set by run_to(ep) below
|
|
if not self.epReached:
|
|
ida_dbg.refresh_debugger_memory()
|
|
self._log("reached entry point at 0x%X" % ida_dbg.get_reg_val("EIP"))
|
|
self._log("current step trace options: %x" % ida_dbg.get_step_trace_options())
|
|
self.epReached = True
|
|
|
|
# enable step tracing (single-step the program and generate dbg_trace events)
|
|
ida_dbg.request_enable_step_trace(1)
|
|
# change options to only "over debugger segments" (i.e. library functions will be traced)
|
|
ida_dbg.request_set_step_trace_options(ida_dbg.ST_OVER_DEBUG_SEG)
|
|
ida_dbg.request_continue_process()
|
|
ida_dbg.run_requests()
|
|
|
|
def dbg_process_exit(self, pid, tid, ea, code):
|
|
self._log("process exited with %d" % code)
|
|
self._log("traced %d instructions" % self.traces)
|
|
return 0
|
|
|
|
|
|
def do_trace(then_quit_ida=True):
|
|
debugHook = TraceHook()
|
|
debugHook.hook()
|
|
|
|
# Start tracing when entry point is hit
|
|
ep = ida_ida.inf_get_start_ip()
|
|
ida_dbg.enable_step_trace(1)
|
|
ida_dbg.set_step_trace_options(ida_dbg.ST_OVER_DEBUG_SEG | ida_dbg.ST_OVER_LIB_FUNC)
|
|
print("Running to %x" % ep)
|
|
ida_dbg.run_to(ep)
|
|
|
|
while ida_dbg.get_process_state() != 0:
|
|
ida_dbg.wait_for_next_event(1, 0)
|
|
|
|
if not debugHook.epReached:
|
|
raise Exception("Entry point wasn't reached!")
|
|
|
|
if not debugHook.unhook():
|
|
raise Exception("Error uninstalling hooks!")
|
|
|
|
del debugHook
|
|
|
|
if then_quit_ida:
|
|
# we're done; exit IDA
|
|
ida_pro.qexit(0)
|
|
|
|
|
|
# load the debugger module depending on the file type
|
|
if ida_ida.inf_get_filetype() == ida_ida.f_PE:
|
|
ida_dbg.load_debugger("win32", 0)
|
|
elif ida_ida.inf_get_filetype() == ida_ida.f_ELF:
|
|
ida_dbg.load_debugger("linux", 0)
|
|
elif ida_ida.inf_get_filetype() == ida_ida.f_MACHO:
|
|
ida_dbg.load_debugger("mac", 0)
|
|
if not under_test:
|
|
do_trace()
|