mirror of
https://github.com/idapython/src
synced 2026-06-08 14:47:00 +00:00
IDAPython for IDA 7.0 SP1
This commit is contained in:
+63
-33
@@ -3,11 +3,14 @@
|
||||
# The sample will allow you to open an assembly file and display it in color
|
||||
# (c) Hex-Rays
|
||||
#
|
||||
import idaapi
|
||||
import idautils
|
||||
import idc
|
||||
|
||||
import os
|
||||
|
||||
import ida_idaapi
|
||||
import ida_kernwin
|
||||
import ida_lines
|
||||
import idautils
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
class asm_colorizer_t(object):
|
||||
def is_id(self, ch):
|
||||
@@ -79,11 +82,38 @@ class asm_colorizer_t(object):
|
||||
x += 1
|
||||
self.add_line(s)
|
||||
|
||||
|
||||
class base_asmview_ah_t(ida_kernwin.action_handler_t):
|
||||
def __init__(self, obj):
|
||||
ida_kernwin.action_handler_t.__init__(self)
|
||||
self.obj = obj
|
||||
|
||||
def update(self, ctx):
|
||||
if self.obj.view and self.obj.view.GetWidget() == ctx.widget:
|
||||
return ida_kernwin.AST_ENABLE_FOR_WIDGET
|
||||
else:
|
||||
return ida_kernwin.AST_DISABLE_FOR_WIDGET
|
||||
|
||||
|
||||
class refresh_ah_t(base_asmview_ah_t):
|
||||
def activate(self, ctx):
|
||||
self.obj.view.reload_file()
|
||||
print("Reloaded")
|
||||
|
||||
|
||||
class close_ah_t(base_asmview_ah_t):
|
||||
def activate(self, ctx):
|
||||
self.obj.view.Close()
|
||||
print("Closed")
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
class asmview_t(idaapi.simplecustviewer_t, asm_colorizer_t):
|
||||
class asmview_t(ida_kernwin.simplecustviewer_t, asm_colorizer_t):
|
||||
def Create(self, fn):
|
||||
# Create the customview
|
||||
if not idaapi.simplecustviewer_t.Create(self, "Viewing file - %s" % os.path.basename(fn)):
|
||||
if not ida_kernwin.simplecustviewer_t.Create(
|
||||
self,
|
||||
"Viewing file - %s" % os.path.basename(fn)):
|
||||
return False
|
||||
|
||||
self.instruction_list = idautils.GetInstructionList()
|
||||
@@ -95,9 +125,6 @@ class asmview_t(idaapi.simplecustviewer_t, asm_colorizer_t):
|
||||
if not self.reload_file():
|
||||
return False
|
||||
|
||||
self.id_refresh = self.AddPopupMenu("Refresh")
|
||||
self.id_close = self.AddPopupMenu("Close")
|
||||
|
||||
return True
|
||||
|
||||
def reload_file(self):
|
||||
@@ -123,38 +150,25 @@ class asmview_t(idaapi.simplecustviewer_t, asm_colorizer_t):
|
||||
self.AddLine(s)
|
||||
|
||||
def as_comment(self, s):
|
||||
return idaapi.COLSTR(s, idaapi.SCOLOR_RPTCMT)
|
||||
return ida_lines.COLSTR(s, ida_lines.SCOLOR_RPTCMT)
|
||||
|
||||
def as_id(self, s):
|
||||
t = s.lower()
|
||||
if t in self.register_list:
|
||||
return idaapi.COLSTR(s, idaapi.SCOLOR_REG)
|
||||
return ida_lines.COLSTR(s, ida_lines.SCOLOR_REG)
|
||||
elif t in self.instruction_list:
|
||||
return idaapi.COLSTR(s, idaapi.SCOLOR_INSN)
|
||||
return ida_lines.COLSTR(s, ida_lines.SCOLOR_INSN)
|
||||
else:
|
||||
return s
|
||||
|
||||
def as_string(self, s):
|
||||
return idaapi.COLSTR(s, idaapi.SCOLOR_STRING)
|
||||
return ida_lines.COLSTR(s, ida_lines.SCOLOR_STRING)
|
||||
|
||||
def as_num(self, s):
|
||||
return idaapi.COLSTR(s, idaapi.SCOLOR_NUMBER)
|
||||
return ida_lines.COLSTR(s, ida_lines.SCOLOR_NUMBER)
|
||||
|
||||
def as_directive(self, s):
|
||||
return idaapi.COLSTR(s, idaapi.SCOLOR_KEYWORD)
|
||||
|
||||
def OnPopupMenu(self, menu_id):
|
||||
"""
|
||||
A context (or popup) menu item was executed.
|
||||
@param menu_id: ID previously registered with AddPopupMenu()
|
||||
@return: Boolean
|
||||
"""
|
||||
if self.id_refresh == menu_id:
|
||||
return self.reload_file()
|
||||
elif self.id_close == menu_id:
|
||||
self.Close()
|
||||
return True
|
||||
return False
|
||||
return ida_lines.COLSTR(s, ida_lines.SCOLOR_KEYWORD)
|
||||
|
||||
def OnKeydown(self, vkey, shift):
|
||||
"""
|
||||
@@ -170,8 +184,8 @@ class asmview_t(idaapi.simplecustviewer_t, asm_colorizer_t):
|
||||
lineno = self.GetLineNo()
|
||||
if lineno is not None:
|
||||
line, fg, bg = self.GetLine(lineno)
|
||||
if line and line[0] != idaapi.SCOLOR_INV:
|
||||
s = idaapi.SCOLOR_INV + line + idaapi.SCOLOR_INV
|
||||
if line and line[0] != ida_lines.SCOLOR_INV:
|
||||
s = ida_lines.SCOLOR_INV + line + ida_lines.SCOLOR_INV
|
||||
self.EditLine(lineno, s, fg, bg)
|
||||
self.Refresh()
|
||||
elif vkey == ord('C'):
|
||||
@@ -186,8 +200,11 @@ class asmview_t(idaapi.simplecustviewer_t, asm_colorizer_t):
|
||||
return True
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
class asmviewplg(idaapi.plugin_t):
|
||||
flags = idaapi.PLUGIN_KEEP
|
||||
ACTNAME_REFRESH = "asmview_t::refresh"
|
||||
ACTNAME_CLOSE = "asmview_t::close"
|
||||
|
||||
class asmviewplg(ida_idaapi.plugin_t):
|
||||
flags = ida_idaapi.PLUGIN_KEEP
|
||||
comment = "ASM viewer"
|
||||
help = "This is help"
|
||||
wanted_name = "ASM file viewer"
|
||||
@@ -196,17 +213,30 @@ class asmviewplg(idaapi.plugin_t):
|
||||
self.view = None
|
||||
|
||||
def init(self):
|
||||
return idaapi.PLUGIN_KEEP
|
||||
# Register actions
|
||||
ida_kernwin.register_action(
|
||||
ida_kernwin.action_desc_t(
|
||||
ACTNAME_REFRESH, "Refresh", refresh_ah_t(self)))
|
||||
ida_kernwin.register_action(
|
||||
ida_kernwin.action_desc_t(
|
||||
ACTNAME_CLOSE, "Close", close_ah_t(self)))
|
||||
return ida_idaapi.PLUGIN_KEEP
|
||||
|
||||
def run(self, arg):
|
||||
if self.view:
|
||||
self.Close()
|
||||
fn = idaapi.ask_file(0, "*.asm", "Select ASM file to view")
|
||||
fn = ida_kernwin.ask_file(0, "*.asm", "Select ASM file to view")
|
||||
if not fn:
|
||||
return
|
||||
self.view = asmview_t()
|
||||
if not self.view.Create(fn):
|
||||
return
|
||||
self.view.Show()
|
||||
widget = self.view.GetWidget()
|
||||
|
||||
# Attach actions to this widget's popup menu
|
||||
ida_kernwin.attach_action_to_popup(widget, None, ACTNAME_REFRESH)
|
||||
ida_kernwin.attach_action_to_popup(widget, None, ACTNAME_CLOSE)
|
||||
|
||||
def term(self):
|
||||
if self.view:
|
||||
|
||||
+53
-53
@@ -4,17 +4,19 @@ A script that tries to determine the call stack
|
||||
|
||||
Run the application with the debugger, suspend the debugger, select a thread and finally run the script.
|
||||
|
||||
Copyright (c) 1990-2009 Hex-Rays
|
||||
Copyright (c) 1990-2017 Hex-Rays
|
||||
ALL RIGHTS RESERVED.
|
||||
|
||||
|
||||
v1.0 - initial version
|
||||
v1.0.1 - added stack segment bitness detection, thus works with 64bit processes too
|
||||
"""
|
||||
import idaapi
|
||||
import idc
|
||||
import ida_ua
|
||||
import ida_bytes
|
||||
import ida_kernwin
|
||||
import ida_funcs
|
||||
import ida_name
|
||||
import ida_ida
|
||||
import ida_idp
|
||||
import ida_segment
|
||||
import ida_dbg
|
||||
import idautils
|
||||
from ida_kernwin import Choose
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# class to take a copy of a segment_t
|
||||
@@ -24,6 +26,7 @@ class Seg():
|
||||
self.end_ea = s.end_ea
|
||||
self.perm = s.perm
|
||||
self.bitness = s.bitness
|
||||
|
||||
def __cmp__(self, other):
|
||||
return cmp(self.start_ea, other.start_ea)
|
||||
|
||||
@@ -51,18 +54,18 @@ def IsPrevInsnCall(ea):
|
||||
is a CALL instruction
|
||||
"""
|
||||
global CallPattern
|
||||
if ea == idaapi.BADADDR or ea < 10:
|
||||
if ea == ida_idaapi.BADADDR or ea < 10:
|
||||
return None
|
||||
|
||||
for delta, opcodes in CallPattern:
|
||||
# assume caller's ea
|
||||
caller = ea + delta
|
||||
# get the bytes
|
||||
bytes = [x for x in GetDataList(caller, len(opcodes), 1)]
|
||||
bytes = [x for x in idautils.GetDataList(caller, len(opcodes), 1)]
|
||||
# do we have a match? is it a call instruction?
|
||||
if bytes == opcodes:
|
||||
tmp = idaapi.insn_t()
|
||||
if idaapi.decode_insn(tmp, caller) and idaapi.is_call_insn(tmp):
|
||||
insn = ida_ua.insn_t()
|
||||
if ida_ua.decode_insn(insn, caller) and ida_idp.is_call_insn(insn):
|
||||
return caller
|
||||
return None
|
||||
|
||||
@@ -79,32 +82,35 @@ def CallStackWalk(nn):
|
||||
def __init__(self, caller, sp):
|
||||
self.caller = caller
|
||||
self.sp = sp
|
||||
f = idaapi.get_func(caller)
|
||||
self.displ = ""
|
||||
f = ida_funcs.get_func(caller)
|
||||
self.displ = "%08x: " % caller
|
||||
if f:
|
||||
self.displ += idc.get_func_name(caller)
|
||||
self.displ += ida_funcs.get_func_name(caller)
|
||||
t = caller - f.start_ea
|
||||
if t > 0: self.displ += "+" + hex(t)
|
||||
else:
|
||||
self.displ += hex(caller)
|
||||
self.displ += " [" + hex(sp) + "]"
|
||||
|
||||
def __str__(self):
|
||||
return self.displ
|
||||
|
||||
# get stack pointer
|
||||
sp = cpu.Esp
|
||||
seg = idaapi.getseg(sp)
|
||||
sp = idautils.cpu.Esp
|
||||
seg = ida_segment.getseg(sp)
|
||||
if not seg:
|
||||
return (False, "Could not locate stack segment!")
|
||||
|
||||
stack_seg = Seg(seg)
|
||||
word_size = 2 ** (seg.bitness + 1)
|
||||
callers = []
|
||||
sp = cpu.Esp - word_size
|
||||
sp = idautils.cpu.Esp - word_size
|
||||
while sp < stack_seg.end_ea:
|
||||
sp += word_size
|
||||
ptr = idautils.GetDataList(sp, 1, word_size).next()
|
||||
seg = idaapi.getseg(ptr)
|
||||
seg = ida_segment.getseg(ptr)
|
||||
# only accept executable segments
|
||||
if (not seg) or ((seg.perm & idaapi.SEGPERM_EXEC) == 0):
|
||||
if (not seg) or ((seg.perm & ida_segment.SEGPERM_EXEC) == 0):
|
||||
continue
|
||||
# try to find caller
|
||||
caller = IsPrevInsnCall(ptr)
|
||||
@@ -118,16 +124,16 @@ def CallStackWalk(nn):
|
||||
if ret:
|
||||
ea = ret[0]
|
||||
# function exists?
|
||||
f = idaapi.get_func(ea)
|
||||
f = ida_funcs.get_func(ea)
|
||||
if not f:
|
||||
# create function
|
||||
idc.add_func(ea, idaapi.BADADDR)
|
||||
ida_funcs.add_func(ea)
|
||||
|
||||
# get the flags
|
||||
f = idc.get_flags(caller)
|
||||
f = ida_bytes.get_flags(caller)
|
||||
# no code there?
|
||||
if not is_code(f):
|
||||
create_insn(caller)
|
||||
if not ida_bytes.is_code(f):
|
||||
ida_ua.create_insn(caller)
|
||||
|
||||
callers.append(Result(caller, sp))
|
||||
#
|
||||
@@ -135,52 +141,46 @@ def CallStackWalk(nn):
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Chooser class
|
||||
class CallStackWalkChoose(Choose):
|
||||
class CallStackWalkChoose(ida_kernwin.Choose):
|
||||
def __init__(self, title, items):
|
||||
Choose.__init__(self, title, [ ["Caller", 16], ["Display", 250] ])
|
||||
ida_kernwin.Choose.__init__(
|
||||
self,
|
||||
title,
|
||||
[["Location", 30]])
|
||||
self.items = items
|
||||
|
||||
def OnGetLine(self, n):
|
||||
o = self.items[n]
|
||||
line = []
|
||||
line.append("%X" % o.caller)
|
||||
line.append("%s" % o.displ)
|
||||
return line
|
||||
self.modal = True
|
||||
|
||||
def OnGetSize(self):
|
||||
return len(self.items)
|
||||
|
||||
def OnGetLine(self, n):
|
||||
return [str(self.items[n])]
|
||||
|
||||
def OnSelectLine(self, n):
|
||||
o = self.items[n]
|
||||
jumpto(o.caller)
|
||||
return (Choose.NOTHING_CHANGED, )
|
||||
ida_kernwin.jumpto(self.items[n].caller)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
def main():
|
||||
if not idaapi.is_debugger_on():
|
||||
idc.warning("Please run the process first!")
|
||||
if not ida_dbg.is_debugger_on():
|
||||
ida_kernwin.warning("Please run the process first!")
|
||||
return
|
||||
if idaapi.get_process_state() != -1:
|
||||
idc.warning("Please suspend the debugger first!")
|
||||
if ida_dbg.get_process_state() != -1:
|
||||
ida_kernwin.warning("Please suspend the debugger first!")
|
||||
return
|
||||
|
||||
# only avail from IdaPython r232
|
||||
if hasattr(idaapi, "NearestName"):
|
||||
# get all debug names
|
||||
dn = idaapi.get_debug_names(idaapi.cvar.inf.min_ea, idaapi.cvar.inf.max_ea)
|
||||
# initiate a nearest name search (using debug names)
|
||||
nn = idaapi.NearestName(dn)
|
||||
else:
|
||||
nn = None
|
||||
# get all debug namesp
|
||||
dn = ida_name.get_debug_names(ida_ida.cvar.inf.min_ea, ida_ida.cvar.inf.max_ea)
|
||||
# initiate a nearest name search (using debug names)
|
||||
nn = ida_name.NearestName(dn)
|
||||
|
||||
ret, callstack = CallStackWalk(nn)
|
||||
if ret:
|
||||
title = "Call stack walker (thread %X)" % (get_current_thread())
|
||||
idaapi.close_chooser(title)
|
||||
title = "Call stack walker (thread %X)" % (ida_dbg.get_current_thread())
|
||||
ida_kernwin.close_chooser(title)
|
||||
c = CallStackWalkChoose(title, callstack)
|
||||
c.Show()
|
||||
c.Show(True)
|
||||
else:
|
||||
idc.warning("Failed to walk the stack:" + callstack)
|
||||
ida_kernwin.warning("Failed to walk the stack:" + callstack)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
main()
|
||||
|
||||
+70
-57
@@ -2,37 +2,70 @@
|
||||
# Debugger command prompt with CustomViewers
|
||||
# (c) Hex-Rays
|
||||
#
|
||||
import idaapi
|
||||
import idc
|
||||
from idaapi import simplecustviewer_t
|
||||
import ida_idaapi
|
||||
import ida_kernwin
|
||||
import ida_lines
|
||||
import ida_expr
|
||||
import ida_dbg
|
||||
|
||||
def SendDbgCommand(cmd):
|
||||
"""Sends a command to the debugger and returns the output string.
|
||||
An exception will be raised if the debugger is not running or the current debugger does not export
|
||||
the 'send_dbg_command' IDC command.
|
||||
"""
|
||||
s = idc.eval('send_dbg_command("%s");' % cmd)
|
||||
if s.startswith("IDC_FAILURE"):
|
||||
raise Exception, "Debugger command is available only when the debugger is active!"
|
||||
return s
|
||||
# The viewer instance
|
||||
dbgcmd = None
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
class dbgcmd_t(simplecustviewer_t):
|
||||
class base_dbgcmd_ah_t(ida_kernwin.action_handler_t):
|
||||
def __init__(self):
|
||||
ida_kernwin.action_handler_t.__init__(self)
|
||||
|
||||
def update(self, ctx):
|
||||
if dbgcmd and ctx.widget == dbgcmd.GetWidget():
|
||||
return ida_kernwin.AST_ENABLE_FOR_WIDGET
|
||||
else:
|
||||
return ida_kernwin.AST_DISABLE_FOR_WIDGET
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
class clear_dbgcmd_ah_t(base_dbgcmd_ah_t):
|
||||
def activate(self, ctx):
|
||||
dbgcmd.ResetOutput()
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
class newcmd_dbgcmd_ah_t(base_dbgcmd_ah_t):
|
||||
def activate(self, ctx):
|
||||
dbgcmd.IssueCommand()
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
class close_dbgcmd_ah_t(base_dbgcmd_ah_t):
|
||||
def activate(self, ctx):
|
||||
dbgcmd.Close()
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Register actions (if needed)
|
||||
ACTNAME_CLEAR = "dbgcmd:clear"
|
||||
ACTNAME_NEWCMD = "dbgcmd:newcmd"
|
||||
ACTNAME_CLOSE = "dbgcmd:close"
|
||||
ida_kernwin.register_action(
|
||||
ida_kernwin.action_desc_t(
|
||||
ACTNAME_CLEAR, "Clear", clear_dbgcmd_ah_t(), "x"))
|
||||
ida_kernwin.register_action(
|
||||
ida_kernwin.action_desc_t(
|
||||
ACTNAME_NEWCMD, "New command", newcmd_dbgcmd_ah_t(), "Insert"))
|
||||
ida_kernwin.register_action(
|
||||
ida_kernwin.action_desc_t(
|
||||
ACTNAME_CLOSE, "Close", close_dbgcmd_ah_t(), "Escape"))
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
class dbgcmd_t(ida_kernwin.simplecustviewer_t):
|
||||
def Create(self):
|
||||
# Form the title
|
||||
title = "Debugger command window"
|
||||
# Create the customview
|
||||
if not simplecustviewer_t.Create(self, title):
|
||||
if not ida_kernwin.simplecustviewer_t.Create(self, title):
|
||||
return False
|
||||
self.last_cmd = ""
|
||||
self.menu_clear = self.AddPopupMenu("Clear")
|
||||
self.menu_cmd = self.AddPopupMenu("New command")
|
||||
|
||||
self.ResetOutput()
|
||||
return True
|
||||
|
||||
def IssueCommand(self):
|
||||
s = idaapi.ask_str(self.last_cmd, 0, "Please enter a debugger command")
|
||||
s = ida_kernwin.ask_str(self.last_cmd, 0, "Please enter a debugger command")
|
||||
if not s:
|
||||
return
|
||||
|
||||
@@ -40,43 +73,24 @@ class dbgcmd_t(simplecustviewer_t):
|
||||
self.last_cmd = s
|
||||
|
||||
# Add it using a different color
|
||||
self.AddLine("debugger>" + idaapi.COLSTR(s, idaapi.SCOLOR_VOIDOP))
|
||||
self.AddLine("debugger>" + ida_lines.COLSTR(s, ida_lines.SCOLOR_VOIDOP))
|
||||
|
||||
try:
|
||||
r = SendDbgCommand(s).split("\n")
|
||||
for s in r:
|
||||
self.AddLine(idaapi.COLSTR(s, idaapi.SCOLOR_LIBNAME))
|
||||
except:
|
||||
self.AddLine(idaapi.COLSTR("Debugger is not active or does not export send_dbg_command()", idaapi.SCOLOR_ERROR))
|
||||
ok, out = ida_dbg.send_dbg_command(s)
|
||||
if ok:
|
||||
for line in out.split("\n"):
|
||||
self.AddLine(ida_lines.COLSTR(line, ida_lines.SCOLOR_LIBNAME))
|
||||
else:
|
||||
self.AddLine(
|
||||
ida_lines.COLSTR(
|
||||
"Debugger is not active or does not export ida_dbg.send_dbg_command() (%s)" % out,
|
||||
ida_lines.SCOLOR_ERROR))
|
||||
self.Refresh()
|
||||
|
||||
def ResetOutput(self):
|
||||
self.ClearLines()
|
||||
self.AddLine(idaapi.COLSTR("Please press INS to enter command; X to clear output", idaapi.SCOLOR_AUTOCMT))
|
||||
self.AddLine(ida_lines.COLSTR("Please press INS to enter command; X to clear output", ida_lines.SCOLOR_AUTOCMT))
|
||||
self.Refresh()
|
||||
|
||||
def OnKeydown(self, vkey, shift):
|
||||
# ESCAPE?
|
||||
if vkey == 27:
|
||||
self.Close()
|
||||
# VK_INSERT
|
||||
elif vkey == 45:
|
||||
self.IssueCommand()
|
||||
elif vkey == ord('X'):
|
||||
self.ResetOutput()
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
def OnPopupMenu(self, menu_id):
|
||||
if menu_id == self.menu_clear:
|
||||
self.ResetOutput()
|
||||
elif menu_id == self.menu_cmd:
|
||||
self.IssueCommand()
|
||||
else:
|
||||
# Unhandled
|
||||
return False
|
||||
return True
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
def show_win():
|
||||
@@ -85,17 +99,16 @@ def show_win():
|
||||
print "Failed to create debugger command line!"
|
||||
return None
|
||||
x.Show()
|
||||
|
||||
# Attach actions to this widget's popup menu
|
||||
widget = x.GetWidget()
|
||||
ida_kernwin.attach_action_to_popup(widget, None, ACTNAME_CLEAR)
|
||||
ida_kernwin.attach_action_to_popup(widget, None, ACTNAME_NEWCMD)
|
||||
ida_kernwin.attach_action_to_popup(widget, None, ACTNAME_CLOSE)
|
||||
return x
|
||||
|
||||
try:
|
||||
# created already?
|
||||
dbgcmd
|
||||
if dbgcmd is not None:
|
||||
dbgcmd.Close()
|
||||
del dbgcmd
|
||||
except:
|
||||
pass
|
||||
dbgcmd = None
|
||||
|
||||
dbgcmd = show_win()
|
||||
if not dbgcmd:
|
||||
del dbgcmd
|
||||
|
||||
|
||||
+64
-59
@@ -2,30 +2,34 @@
|
||||
|
||||
A script to demonstrate how to send commands to the debugger and then parse and use the output in IDA
|
||||
|
||||
Copyright (c) 1990-2009 Hex-Rays
|
||||
Copyright (c) 1990-2017 Hex-Rays
|
||||
ALL RIGHTS RESERVED.
|
||||
|
||||
"""
|
||||
|
||||
import re
|
||||
import idc
|
||||
from ida_kernwin import Choose
|
||||
|
||||
import ida_idaapi
|
||||
import ida_expr
|
||||
import ida_kernwin
|
||||
import ida_dbg
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
def WinDbg_command(cmd):
|
||||
ok, s = ida_dbg.send_dbg_command(cmd)
|
||||
return s if ok else False
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
def CmdDriverList():
|
||||
s = idc.eval('send_dbg_command("lm o");')
|
||||
if "IDC_FAILURE" in s: return False
|
||||
return s
|
||||
return WinDbg_command("lm o")
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
def CmdDrvObj(drvname, flag=2):
|
||||
return idc.eval('send_dbg_command("!drvobj %s %d");' % (drvname, flag))
|
||||
return WinDbg_command("!drvobj %s %d" % (drvname, flag))
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
def CmdReloadForce():
|
||||
s = idc.eval('send_dbg_command(".reload /f");')
|
||||
if "IDC_FAILURE" in s: return False
|
||||
return True
|
||||
return WinDbg_command(".reload /f")
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# class to hold dispatch entry information
|
||||
@@ -33,82 +37,83 @@ class DispatchEntry:
|
||||
def __init__(self, addr, name):
|
||||
self.addr = addr
|
||||
self.name = name
|
||||
def __repr__(self):
|
||||
return "%08X: %s" % (self.addr, self.name)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
def GetDriverDispatch():
|
||||
# return a list of arrays of the form: [addr, name]
|
||||
ret_list = []
|
||||
|
||||
# build the RE for parsing output from the "lm o" command
|
||||
re_drv = re.compile('^[a-f0-9]+\s+[a-f0-9]+\s+(\S+)', re.I)
|
||||
|
||||
# build the RE for parsing output from the "!drvobj DRV_NAME 2" command
|
||||
re_tbl = re.compile('^\[\d{2}\]\s+IRP_MJ_(\S+)\s+([0-9a-f]+)', re.I)
|
||||
# return a list of arrays of the form: [addr, name]
|
||||
ret_list = []
|
||||
|
||||
# force reloading of module symbols
|
||||
if not CmdReloadForce():
|
||||
print "Could not communicate with WinDbg, make sure the debugger is running!"
|
||||
return None
|
||||
# build the RE for parsing output from the "lm o" command
|
||||
re_drv = re.compile('^[a-f0-9]+\s+[a-f0-9]+\s+(\S+)', re.I)
|
||||
|
||||
# get driver list
|
||||
lm_out = CmdDriverList()
|
||||
if not lm_out:
|
||||
return "Failed to get driver list!"
|
||||
# build the RE for parsing output from the "!drvobj DRV_NAME 2" command
|
||||
re_tbl = re.compile('^\[\d{2}\]\s+IRP_MJ_(\S+)\s+([0-9a-f]+)', re.I)
|
||||
|
||||
# for each line
|
||||
for line in lm_out.split("\n"):
|
||||
# parse
|
||||
r = re_drv.match(line)
|
||||
if not r: continue
|
||||
# force reloading of module symbols
|
||||
if not CmdReloadForce():
|
||||
print "Could not communicate with WinDbg, make sure the debugger is running!"
|
||||
return None
|
||||
|
||||
# extract driver name
|
||||
drvname = r.group(1).strip()
|
||||
|
||||
# execute "drvobj" command
|
||||
tbl_out = CmdDrvObj(drvname)
|
||||
|
||||
if not tbl_out:
|
||||
print "Failed to get driver object for", drvname
|
||||
continue
|
||||
# get driver list
|
||||
lm_out = CmdDriverList()
|
||||
if not lm_out:
|
||||
return "Failed to get driver list!"
|
||||
|
||||
# for each line
|
||||
for line in tbl_out.split("\n"):
|
||||
for line in lm_out.split("\n"):
|
||||
# parse
|
||||
r = re_tbl.match(line)
|
||||
r = re_drv.match(line)
|
||||
if not r: continue
|
||||
disp_addr = int(r.group(2), 16) # convert hex string to number
|
||||
disp_name = "Dispatch" + r.group(1)
|
||||
ret_list.append(DispatchEntry(disp_addr, drvname + "_" + disp_name))
|
||||
|
||||
return ret_list
|
||||
# extract driver name
|
||||
drvname = r.group(1).strip()
|
||||
|
||||
# execute "drvobj" command
|
||||
tbl_out = CmdDrvObj(drvname)
|
||||
|
||||
if not tbl_out:
|
||||
print "Failed to get driver object for", drvname
|
||||
continue
|
||||
|
||||
# for each line
|
||||
for line in tbl_out.split("\n"):
|
||||
# parse
|
||||
r = re_tbl.match(line)
|
||||
if not r: continue
|
||||
disp_addr = int(r.group(2), 16) # convert hex string to number
|
||||
disp_name = "Dispatch" + r.group(1)
|
||||
ret_list.append(DispatchEntry(disp_addr, drvname + "_" + disp_name))
|
||||
|
||||
return ret_list
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Chooser class
|
||||
class DispatchChoose(Choose):
|
||||
class DispatchChoose(ida_kernwin.Choose):
|
||||
def __init__(self, title, items):
|
||||
Choose.__init__(self, title, [ ["Address", 16], ["Name", 250] ])
|
||||
ida_kernwin.Choose.__init__(
|
||||
self,
|
||||
title,
|
||||
[["Address", 30]],
|
||||
width=250)
|
||||
self.items = items
|
||||
|
||||
def OnGetLine(self, n):
|
||||
o = self.items[n]
|
||||
line = []
|
||||
line.append("%08X" % o.addr)
|
||||
line.append("%s" % o.name)
|
||||
return line
|
||||
|
||||
def OnGetSize(self):
|
||||
return len(self.items)
|
||||
|
||||
def OnGetLine(self, n):
|
||||
return [str(self.items[n])]
|
||||
|
||||
def OnSelectLine(self, n):
|
||||
o = self.items[n]
|
||||
Jump(o.addr)
|
||||
return (Choose.NOTHING_CHANGED, )
|
||||
ida_kernwin.jumpto(self.items[n].addr)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# main
|
||||
r = GetDriverDispatch()
|
||||
if r:
|
||||
c = DispatchChoose("Dispatch table browser", r)
|
||||
c.Show()
|
||||
c.Show(True)
|
||||
else:
|
||||
print "Failed to retrieve dispatchers list!"
|
||||
print "Failed to retrieve dispatchers list!"
|
||||
|
||||
+16
-24
@@ -7,56 +7,48 @@ ALL RIGHTS RESERVED.
|
||||
|
||||
"""
|
||||
|
||||
import idc
|
||||
import re
|
||||
|
||||
import ida_kernwin
|
||||
from ida_kernwin import Choose
|
||||
|
||||
# class to store parsed results
|
||||
class exchain:
|
||||
def __init__(self, m):
|
||||
self.name = m.group(1)
|
||||
self.addr = int(m.group(2), 16)
|
||||
self.name = m.group(1)
|
||||
self.addr = int(m.group(2), 16)
|
||||
|
||||
# Chooser class
|
||||
class MyChoose(Choose):
|
||||
class MyChoose(ida_kernwin.Choose):
|
||||
def __init__(self, title, items):
|
||||
Choose.__init__(self, title, [ ["Address", 16], ["Name", 250] ])
|
||||
ida_kernwin.Choose.__init__(self, title, [ ["Address", 16], ["Name", 250] ])
|
||||
self.items = items
|
||||
|
||||
def OnGetLine(self, n):
|
||||
o = self.items[n]
|
||||
line = []
|
||||
line.append("%08X" % o.addr)
|
||||
line.append("%s" % o.name)
|
||||
return line
|
||||
return ["%08X" % o.addr, o.name]
|
||||
|
||||
def OnGetSize(self):
|
||||
return len(self.items)
|
||||
|
||||
def OnSelectLine(self, n):
|
||||
o = self.items[n]
|
||||
Jump(o.addr)
|
||||
return (Choose.NOTHING_CHANGED, )
|
||||
ida_kernwin.jumpto(self.items[n].addr)
|
||||
return (ida_kernwin.Choose.NOTHING_CHANGED, )
|
||||
|
||||
# main
|
||||
def main():
|
||||
s = idc.eval('send_dbg_command("!exchain")')
|
||||
if "IDC_FAILURE" in s:
|
||||
return (False, "Cannot execute the command")
|
||||
ok, s = ida_dbg.send_dbg_command("!exchain")
|
||||
if not ok:
|
||||
return (False, "Cannot execute the command (%s)" % s)
|
||||
|
||||
matches = re.finditer(r'[^:]+: ([^\(]+) \(([^\)]+)\)\n', s)
|
||||
L = []
|
||||
for x in matches:
|
||||
L.append(exchain(x))
|
||||
if not L:
|
||||
entries = [exchain(x) for x in matches]
|
||||
if not entries:
|
||||
return (False, "Nothing to display: Could parse the result!")
|
||||
|
||||
# Get a Choose instance
|
||||
chooser = MyChoose("Exchain choose", L)
|
||||
# Run the chooser
|
||||
# Show a list of results, and let the user possibly jump to one of those
|
||||
chooser = MyChoose("Exchain choose", entries)
|
||||
chooser.Show()
|
||||
return (True, "Success!")
|
||||
|
||||
ok, r = main()
|
||||
if not ok:
|
||||
print r
|
||||
|
||||
+57
-46
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
FindInstructions.py: A script to help you find desired opcodes/instructions in a database
|
||||
A script to help you find desired opcodes/instructions in a database
|
||||
|
||||
The script accepts opcodes and assembly statements (which will be assembled) separated by semicolon
|
||||
|
||||
@@ -13,15 +13,22 @@ The general syntax is:
|
||||
* To specify in which context the instructions should be assembled, pass asm_where=ea:
|
||||
find("jmp dword ptr [esp]", asm_where=here())
|
||||
|
||||
Copyright (c) 1990-2009 Hex-Rays
|
||||
Copyright (c) 1990-2017 Hex-Rays
|
||||
ALL RIGHTS RESERVED.
|
||||
|
||||
v1.0 - initial version
|
||||
"""
|
||||
import idaapi
|
||||
import re
|
||||
|
||||
import ida_idaapi
|
||||
import ida_lines
|
||||
import ida_segment
|
||||
import ida_kernwin
|
||||
import ida_bytes
|
||||
import ida_ua
|
||||
import ida_ida
|
||||
import ida_search
|
||||
import ida_funcs
|
||||
|
||||
import idautils
|
||||
import idc
|
||||
from ida_kernwin import Choose
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
def FindInstructions(instr, asm_where=None):
|
||||
@@ -31,8 +38,9 @@ def FindInstructions(instr, asm_where=None):
|
||||
"""
|
||||
if not asm_where:
|
||||
# get first segment
|
||||
asm_where = get_first_seg()
|
||||
if asm_where == idaapi.BADADDR:
|
||||
seg = ida_segment.get_first_seg()
|
||||
asm_where = seg.start_ea if seg else ida_idaapi.BADADDR
|
||||
if asm_where == ida_idaapi.BADADDR:
|
||||
return (False, "No segments defined")
|
||||
|
||||
# regular expression to distinguish between opcodes and instructions
|
||||
@@ -49,7 +57,7 @@ def FindInstructions(instr, asm_where=None):
|
||||
buf = ''.join([chr(int(x, 16)) for x in line.split()])
|
||||
else:
|
||||
# assemble the instruction
|
||||
ret, buf = Assemble(asm_where, line)
|
||||
ret, buf = idautils.Assemble(asm_where, line)
|
||||
if not ret:
|
||||
return (False, "Failed to assemble:"+line)
|
||||
# add the assembled buffer
|
||||
@@ -66,63 +74,66 @@ def FindInstructions(instr, asm_where=None):
|
||||
|
||||
# find all binary strings
|
||||
print "Searching for: [%s]" % bin_str
|
||||
ea = get_inf_attr(INF_MIN_EA)
|
||||
ea = ida_ida.cvar.inf.min_ea
|
||||
ret = []
|
||||
while True:
|
||||
ea = find_binary(ea, SEARCH_DOWN, bin_str)
|
||||
if ea == idaapi.BADADDR:
|
||||
ea = ida_search.find_binary(ea, ida_idaapi.BADADDR, bin_str, 16, ida_search.SEARCH_DOWN)
|
||||
if ea == ida_idaapi.BADADDR:
|
||||
break
|
||||
ret.append(ea)
|
||||
msg(".")
|
||||
ida_kernwin.msg(".")
|
||||
ea += tlen
|
||||
if not ret:
|
||||
return (False, "Could not match [%s]" % bin_str)
|
||||
msg("\n")
|
||||
ida_kernwin.msg("\n")
|
||||
return (True, ret)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Chooser class
|
||||
class SearchResultChoose(Choose):
|
||||
class SearchResultChoose(ida_kernwin.Choose):
|
||||
def __init__(self, title, items):
|
||||
Choose.__init__(self, title, [ ["Address", 16], ["Results", 250] ])
|
||||
ida_kernwin.Choose.__init__(
|
||||
self,
|
||||
title,
|
||||
[["Address", 30], ["Function (or segment)", 25], ["Instruction", 20]],
|
||||
width=250)
|
||||
self.items = items
|
||||
|
||||
def OnGetLine(self, n):
|
||||
o = self.items[n]
|
||||
line = []
|
||||
line.append("%08X" % o.ea)
|
||||
line.append("%s" % o.display)
|
||||
return line
|
||||
|
||||
def OnGetSize(self):
|
||||
return len(self.items)
|
||||
|
||||
def OnGetLine(self, n):
|
||||
i = self.items[n]
|
||||
ea = i.ea
|
||||
return [
|
||||
hex(i.ea),
|
||||
i.funcname_or_segname,
|
||||
i.text
|
||||
]
|
||||
|
||||
def OnSelectLine(self, n):
|
||||
o = self.items[n]
|
||||
Jump(o.ea)
|
||||
return (Choose.NOTHING_CHANGED, )
|
||||
ida_kernwin.jumpto(self.items[n].ea)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# class to represent the results
|
||||
class SearchResult:
|
||||
def __init__(self, ea):
|
||||
self.ea = ea
|
||||
if not is_code(get_flags(ea)):
|
||||
create_insn(ea)
|
||||
t = idaapi.generate_disasm_line(ea)
|
||||
self.funcname_or_segname = ""
|
||||
self.text = ""
|
||||
if not ida_bytes.is_code(ida_bytes.get_flags(ea)):
|
||||
ida_ua.create_insn(ea)
|
||||
|
||||
# text
|
||||
t = ida_lines.generate_disasm_line(ea)
|
||||
if t:
|
||||
line = idaapi.tag_remove(t)
|
||||
else:
|
||||
line = ""
|
||||
func = get_func_name(ea)
|
||||
self.display = ""
|
||||
if func:
|
||||
self.display += func + ": "
|
||||
else:
|
||||
n = get_segm_name(ea)
|
||||
if n:
|
||||
self.display += n + ": "
|
||||
self.display += line
|
||||
self.text = ida_lines.tag_remove(t)
|
||||
|
||||
# funcname_or_segname
|
||||
n = ida_funcs.get_func_name(ea) \
|
||||
or ida_segment.get_segm_name(ida_segment.getseg(ea))
|
||||
if n:
|
||||
self.funcname_or_segname = n
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
def find(s=None, x=False, asm_where=None):
|
||||
@@ -132,16 +143,16 @@ def find(s=None, x=False, asm_where=None):
|
||||
if x:
|
||||
results = []
|
||||
for ea in ret:
|
||||
seg = idaapi.getseg(ea)
|
||||
if (not seg) or (seg.perm & idaapi.SEGPERM_EXEC) == 0:
|
||||
seg = ida_segment.getseg(ea)
|
||||
if (not seg) or (seg.perm & ida_segment.SEGPERM_EXEC) == 0:
|
||||
continue
|
||||
results.append(SearchResult(ea))
|
||||
else:
|
||||
results = [SearchResult(ea) for ea in ret]
|
||||
title = "Search result for: [%s]" % s
|
||||
idaapi.close_chooser(title)
|
||||
ida_kernwin.close_chooser(title)
|
||||
c = SearchResultChoose(title, results)
|
||||
c.Show()
|
||||
c.Show(True)
|
||||
else:
|
||||
print ret
|
||||
|
||||
|
||||
+13
-11
@@ -4,12 +4,14 @@
|
||||
#
|
||||
# (c) Hex-Rays
|
||||
#
|
||||
|
||||
import idaapi
|
||||
import idc
|
||||
import idautils
|
||||
import re
|
||||
|
||||
import ida_kernwin
|
||||
import ida_nalt
|
||||
import ida_funcs
|
||||
|
||||
import idautils
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
def find_imported_funcs(dllname):
|
||||
def imp_cb(ea, name, ord):
|
||||
@@ -19,12 +21,12 @@ def find_imported_funcs(dllname):
|
||||
return True
|
||||
|
||||
imports = []
|
||||
nimps = idaapi.get_import_module_qty()
|
||||
nimps = ida_nalt.get_import_module_qty()
|
||||
for i in xrange(0, nimps):
|
||||
name = idaapi.get_import_module_name(i)
|
||||
name = ida_nalt.get_import_module_name(i)
|
||||
if re.match(dllname, name, re.IGNORECASE) is None:
|
||||
continue
|
||||
idaapi.enum_import_names(i, imp_cb)
|
||||
ida_nalt.enum_import_names(i, imp_cb)
|
||||
|
||||
return imports
|
||||
|
||||
@@ -38,9 +40,9 @@ def find_import_ref(dllname):
|
||||
for xref in idautils.XrefsTo(ea):
|
||||
# check if referrer is a thunk
|
||||
ea = xref.frm
|
||||
f = idaapi.get_func(ea)
|
||||
if f and (f.flags & idaapi.FUNC_THUNK) != 0:
|
||||
imports.append([f.start_ea, idaapi.get_func_name(f.start_ea), 0])
|
||||
f = ida_funcs.get_func(ea)
|
||||
if f and (f.flags & ida_funcs.FUNC_THUNK) != 0:
|
||||
imports.append([f.start_ea, ida_funcs.get_func_name(f.start_ea), 0])
|
||||
#print "\t%x %s: from a thunk, parent added %x" % (ea, name, f.start_ea)
|
||||
continue
|
||||
|
||||
@@ -54,7 +56,7 @@ def find_import_ref(dllname):
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
def main():
|
||||
dllname = idaapi.ask_str('kernel32', 0, "Enter module name")
|
||||
dllname = ida_kernwin.ask_str('kernel32', 0, "Enter module name")
|
||||
if not dllname:
|
||||
print("Cancelled")
|
||||
return
|
||||
|
||||
+22
-20
@@ -1,8 +1,9 @@
|
||||
import idaapi
|
||||
import idc
|
||||
from ida_kernwin import Choose
|
||||
|
||||
def parse_pte(str):
|
||||
import ida_kernwin
|
||||
import ida_segment
|
||||
import ida_dbg
|
||||
|
||||
def parse_pte(s):
|
||||
try:
|
||||
parse_pte.re
|
||||
except:
|
||||
@@ -10,15 +11,17 @@ def parse_pte(str):
|
||||
parse_pte.items = ('pde', 'pte', 'pdec', 'ptec', 'pdepfn', 'pdepfns', 'ptepfn', 'ptepfns')
|
||||
|
||||
m = parse_pte.re.search(s)
|
||||
if not m:
|
||||
return None
|
||||
r = {}
|
||||
for i in range(0, len(parse_pte.items)):
|
||||
r[parse_pte.items[i]] = m.group(i+1)
|
||||
return r
|
||||
|
||||
class MyChoose(Choose):
|
||||
class MyChoose(ida_kernwin.Choose):
|
||||
|
||||
def __init__(self, title, ea1, ea2):
|
||||
Choose.__init__(self, title, [ ["VA", 10], ["PTE attr", 30] ])
|
||||
ida_kernwin.Choose.__init__(self, title, [ ["VA", 10], ["PTE attr", 30] ])
|
||||
self.ea1 = ea1
|
||||
self.ea2 = ea2
|
||||
self.icon = 5
|
||||
@@ -26,17 +29,14 @@ class MyChoose(Choose):
|
||||
self.Refresh()
|
||||
|
||||
def OnGetLine(self, n):
|
||||
print("getline %d" % n)
|
||||
return self.items[n]
|
||||
|
||||
def OnGetSize(self):
|
||||
n = len(self.items)
|
||||
self.Refresh()
|
||||
return n
|
||||
return len(self.items)
|
||||
|
||||
def OnRefresh(self, n):
|
||||
print("refresh %d" % n)
|
||||
return None # call standard refresh
|
||||
self.Refresh()
|
||||
return (ida_kernwin.Choose.ALL_CHANGED, )
|
||||
|
||||
def Refresh(self):
|
||||
items = []
|
||||
@@ -44,11 +44,12 @@ class MyChoose(Choose):
|
||||
ea1 = self.ea1
|
||||
npages = (self.ea2 - ea1) / PG
|
||||
for i in range(npages):
|
||||
r = idc.send_dbg_command("!pte %x" % ea1)
|
||||
if not r:
|
||||
ok, r = ida_dbg.send_dbg_command("!pte %x" % ea1)
|
||||
if not ok:
|
||||
return False
|
||||
r = parse_pte(r)
|
||||
items.append([hex(ea1), r['ptepfns']])
|
||||
if r:
|
||||
items.append([hex(ea1), r['ptepfns']])
|
||||
ea1 += PG
|
||||
|
||||
self.items = items
|
||||
@@ -66,16 +67,17 @@ def DumpPTE(ea1, ea2):
|
||||
PG = 0x1000
|
||||
npages = (ea2 - ea1) / PG
|
||||
for i in range(npages):
|
||||
r = idc.send_dbg_command("!pte %x" % ea1)
|
||||
if not r:
|
||||
ok, r = ida_dbg.send_dbg_command("!pte %x" % ea1)
|
||||
if not ok:
|
||||
return False
|
||||
print r
|
||||
r = parse_pte(r)
|
||||
print("VA: %08X PTE: %s PDE: %s" % (ea1, r['ptepfns'], r['pdepfns']))
|
||||
if r:
|
||||
print("VA: %08X PTE: %s PDE: %s" % (ea1, r['ptepfns'], r['pdepfns']))
|
||||
ea1 += PG
|
||||
|
||||
def DumpSegPTE(ea):
|
||||
DumpPTE(idc.get_segm_start(ea), idc.get_segm_end(ea))
|
||||
s = ida_segment.getseg(ea)
|
||||
DumpPTE(s.start_ea, s.end_ea)
|
||||
|
||||
DumpSegPTE(here())
|
||||
|
||||
|
||||
+23
-24
@@ -4,26 +4,24 @@ A script that graphs all the exception handlers in a given process
|
||||
|
||||
It will be easy to see what thread uses what handler and what handlers are commonly used between threads
|
||||
|
||||
Copyright (c) 1990-2009 Hex-Rays
|
||||
Copyright (c) 1990-2017 Hex-Rays
|
||||
ALL RIGHTS RESERVED.
|
||||
|
||||
|
||||
v1.0 - initial version
|
||||
|
||||
"""
|
||||
|
||||
import idaapi
|
||||
import idautils
|
||||
import idc
|
||||
import ida_kernwin
|
||||
import ida_graph
|
||||
import ida_idd
|
||||
import ida_dbg
|
||||
import ida_funcs
|
||||
|
||||
from idaapi import GraphViewer
|
||||
import idautils
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Since Windbg debug module does not support get_thread_sreg_base()
|
||||
# we will call the debugger engine "dg" command and parse its output
|
||||
def WindbgGetRegBase(tid):
|
||||
s = idc.eval('send_dbg_command("dg %x")' % cpu.fs)
|
||||
if "IDC_FAILURE" in s:
|
||||
ok, s = ida_dbg.send_dbg_command("dg %x" % idautils.cpu.fs)
|
||||
if not ok:
|
||||
return 0
|
||||
m = re.compile("[0-9a-f]{4} ([0-9a-f]{8})")
|
||||
t = m.match(s.split('\n')[-2])
|
||||
@@ -33,8 +31,8 @@ def WindbgGetRegBase(tid):
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
def GetFsBase(tid):
|
||||
idc.select_thread(tid)
|
||||
base = idaapi.dbg_get_thread_sreg_base(tid, cpu.fs)
|
||||
ida_dbg.select_thread(tid)
|
||||
base = ida_idd.dbg_get_thread_sreg_base(tid, idautils.cpu.fs)
|
||||
if base != 0:
|
||||
return base
|
||||
return WindbgGetRegBase(tid)
|
||||
@@ -43,7 +41,8 @@ def GetFsBase(tid):
|
||||
# Walks the SEH chain and returns a list of handlers
|
||||
def GetExceptionChain(tid):
|
||||
fs_base = GetFsBase(tid)
|
||||
exc_rr = get_wide_dword(fs_base)
|
||||
print("FS_BASE for %s: %s (cpu.fs=%s)" % (repr(tid), repr(fs_base), repr(idautils.cpu.fs)))
|
||||
exc_rr = ida_bytes.get_wide_dword(fs_base)
|
||||
result = []
|
||||
while exc_rr != 0xffffffff:
|
||||
prev = get_wide_dword(exc_rr)
|
||||
@@ -53,9 +52,9 @@ def GetExceptionChain(tid):
|
||||
return result
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
class SEHGraph(GraphViewer):
|
||||
class SEHGraph(ida_graph.GraphViewer):
|
||||
def __init__(self, title, result):
|
||||
GraphViewer.__init__(self, title)
|
||||
ida_graph.GraphViewer.__init__(self, title)
|
||||
self.result = result
|
||||
self.names = {} # ea -> name
|
||||
|
||||
@@ -74,13 +73,13 @@ class SEHGraph(GraphViewer):
|
||||
# Add each handler
|
||||
for handler in chain:
|
||||
# Check if a function is created at the handler's address
|
||||
f = idaapi.get_func(handler)
|
||||
f = ida_funcs.get_func(handler)
|
||||
if not f:
|
||||
# create function
|
||||
idc.add_func(handler, idaapi.BADADDR)
|
||||
ida_funcs.add_func(handler)
|
||||
|
||||
# Node label is function name or address
|
||||
s = get_func_name(handler)
|
||||
s = ida_funcs.get_func_name(handler)
|
||||
if not s:
|
||||
s = "%x" % handler
|
||||
|
||||
@@ -110,7 +109,7 @@ class SEHGraph(GraphViewer):
|
||||
def OnDblClick(self, node_id):
|
||||
is_thread, value, label = self[node_id]
|
||||
if is_thread:
|
||||
idc.select_thread(value)
|
||||
ida_dbg.select_thread(value)
|
||||
self.Show()
|
||||
s = "SEH chain for " + hex(value)
|
||||
t = "-" * len(s)
|
||||
@@ -121,18 +120,18 @@ class SEHGraph(GraphViewer):
|
||||
print "%x: %s" % (handler, self.names[handler])
|
||||
print t
|
||||
else:
|
||||
idc.jumpto(value)
|
||||
ida_kernwin.jumpto(value)
|
||||
return True
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
def main():
|
||||
if not idaapi.dbg_can_query():
|
||||
if not ida_idd.dbg_can_query():
|
||||
print "The debugger must be active and suspended before using this script!"
|
||||
return
|
||||
|
||||
# Save current thread id
|
||||
tid = get_current_thread()
|
||||
tid = ida_dbg.get_current_thread()
|
||||
|
||||
# Iterate through all function instructions and take only call instructions
|
||||
result = {}
|
||||
@@ -140,7 +139,7 @@ def main():
|
||||
result[tid] = GetExceptionChain(tid)
|
||||
|
||||
# Restore previously selected thread
|
||||
idc.select_thread(tid)
|
||||
ida_dbg.select_thread(tid)
|
||||
|
||||
# Build the graph
|
||||
g = SEHGraph("SEH graph", result)
|
||||
|
||||
+8
-9
@@ -7,11 +7,10 @@ ALL RIGHTS RESERVED.
|
||||
|
||||
"""
|
||||
|
||||
import idc
|
||||
from ida_kernwin import Choose
|
||||
|
||||
import re
|
||||
|
||||
import ida_kernwin
|
||||
|
||||
# class to store parsed results
|
||||
class memva:
|
||||
def __init__(self, m):
|
||||
@@ -29,7 +28,7 @@ class memva:
|
||||
self.typestr = ""
|
||||
|
||||
# Chooser class
|
||||
class MemChoose(Choose):
|
||||
class MemChoose(ida_kernwin.Choose):
|
||||
def __init__(self, title, items):
|
||||
headers = []
|
||||
headers.append(["Base", 10])
|
||||
@@ -37,7 +36,7 @@ class MemChoose(Choose):
|
||||
headers.append(["State", 20])
|
||||
headers.append(["Protect", 20])
|
||||
headers.append(["Type", 20])
|
||||
Choose.__init__(self, title, headers)
|
||||
ida_kernwin.Choose.__init__(self, title, headers)
|
||||
self.items = items
|
||||
|
||||
def OnGetLine(self, n):
|
||||
@@ -55,13 +54,13 @@ class MemChoose(Choose):
|
||||
|
||||
def OnSelectLine(self, n):
|
||||
o = self.items[n]
|
||||
idc.jumpto(o.base)
|
||||
return (NOTHING_CHANGED, )
|
||||
ida_kernwin.jumpto(o.base)
|
||||
return (ida_kernwin.Choose.NOTHING_CHANGED, )
|
||||
|
||||
# main
|
||||
def main():
|
||||
s = idc.eval('send_dbg_command("!vadump")')
|
||||
if "IDC_FAILURE" in s:
|
||||
ok, s = ida_dbg.send_dbg_command("!vadump")
|
||||
if not ok:
|
||||
return (False, "Cannot execute the command")
|
||||
|
||||
matches = re.finditer(r'BaseAddress:\s*?(\w+?)\n' \
|
||||
|
||||
+26
-26
@@ -5,30 +5,33 @@ This script fetches the API reference (from MSDN) of a given highlighted identif
|
||||
and returns the results in a new web browser page.
|
||||
|
||||
This script depends on the feedparser package: http://code.google.com/p/feedparser/
|
||||
|
||||
10/05/2010
|
||||
- initial version
|
||||
|
||||
|
||||
"""
|
||||
|
||||
import idaapi
|
||||
# -----------------------------------------------------------------------
|
||||
import ida_kernwin
|
||||
import ida_name
|
||||
import ida_idaapi
|
||||
|
||||
try:
|
||||
import feedparser
|
||||
except:
|
||||
ida_kernwin.warning('Feedparser package not installed')
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
class msdnapihelp_plugin_t(idaapi.plugin_t):
|
||||
flags = idaapi.PLUGIN_UNL
|
||||
class msdnapihelp_plugin_t(ida_idaapi.plugin_t):
|
||||
flags = ida_idaapi.PLUGIN_UNL
|
||||
comment = "Online MSDN API Help"
|
||||
help = "Help me"
|
||||
wanted_name = "MSDN API Help"
|
||||
wanted_hotkey = "F3"
|
||||
|
||||
def init(self):
|
||||
return idaapi.PLUGIN_OK
|
||||
return ida_idaapi.PLUGIN_OK
|
||||
|
||||
|
||||
@staticmethod
|
||||
def sanitize_name(name):
|
||||
t = idaapi.FUNC_IMPORT_PREFIX
|
||||
t = ida_name.FUNC_IMPORT_PREFIX
|
||||
if name.startswith(t):
|
||||
return name[len(t):]
|
||||
return name
|
||||
@@ -36,28 +39,25 @@ class msdnapihelp_plugin_t(idaapi.plugin_t):
|
||||
|
||||
def run(self, arg):
|
||||
# Get the highlighted identifier
|
||||
v = idaapi.get_current_viewer()
|
||||
id = ida_kernwin.get_highlight(v)[0]
|
||||
if not id:
|
||||
v = ida_kernwin.get_current_viewer()
|
||||
ident, ok = ida_kernwin.get_highlight(v)
|
||||
if not ok:
|
||||
print "No identifier was highlighted"
|
||||
return
|
||||
|
||||
import webbrowser
|
||||
|
||||
try:
|
||||
import feedparser
|
||||
except:
|
||||
idaapi.warning('Feedparser package not installed')
|
||||
return
|
||||
|
||||
id = self.sanitize_name(id)
|
||||
print "Looking up '%s' in MSDN online" % id
|
||||
d = feedparser.parse("http://social.msdn.microsoft.com/Search/Feed.aspx?locale=en-us&format=RSS&Query=%s" % id)
|
||||
ident = self.sanitize_name(ident)
|
||||
print "Looking up '%s' in MSDN online" % ident
|
||||
qurl = "https://social.msdn.microsoft.com/search/en-US/feed?query=%s&format=RSS&theme=feed%%2fen-us"
|
||||
d = feedparser.parse(qurl % ident)
|
||||
if len(d['entries']) > 0:
|
||||
url = d['entries'][0].link
|
||||
webbrowser.open_new_tab(url)
|
||||
if arg > 0:
|
||||
print("URL: %s" % url)
|
||||
else:
|
||||
import webbrowser
|
||||
webbrowser.open_new_tab(url)
|
||||
else:
|
||||
print "API documentation not found for: %s" % id
|
||||
print "API documentation not found for: %s" % ident
|
||||
|
||||
|
||||
def term(self):
|
||||
|
||||
Binary file not shown.
@@ -2611,6 +2611,7 @@ AF_FINAL = 0x80000000 # Final pass of analysis
|
||||
INF_AF2 = 44 # uint32; Analysis flags 2
|
||||
|
||||
AF2_DOEH = 0x00000001 # Handle EH information
|
||||
AF2_DORTTI = 0x00000002 # Handle RTTI information
|
||||
|
||||
INF_BASEADDR = 48 # uval_t; base paragraph of the program
|
||||
INF_START_SS = 52 # int32; value of SS at the start
|
||||
@@ -8338,6 +8339,13 @@ if sys.modules["__main__"].IDAPYTHON_COMPAT_695_API:
|
||||
def MakeStr(ea, endea):
|
||||
return create_strlit(ea, endea)
|
||||
|
||||
def GetProcessorName():
|
||||
return ida_ida.cvar.inf.procname
|
||||
|
||||
def SegStart(ea): return get_segm_start(ea)
|
||||
def SegEnd(ea): return get_segm_end(ea)
|
||||
def SetSegmentType(ea, type): return set_segm_type(ea, type)
|
||||
|
||||
# Convenience functions:
|
||||
def here(): return get_screen_ea()
|
||||
def is_mapped(ea): return (prev_addr(ea+1)==ea)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#<pycode(py_dbg)>
|
||||
import ida_idaapi
|
||||
import ida_idd
|
||||
import ida_expr
|
||||
|
||||
def get_tev_reg_val(tev, reg):
|
||||
rv = ida_idd.regval_t()
|
||||
@@ -27,6 +29,32 @@ def get_tev_reg_mem_ea(tev, idx):
|
||||
if get_insn_tev_reg_mem(tev, mis):
|
||||
if idx >= 0 and idx < mis.size():
|
||||
return mis[idx].ea
|
||||
|
||||
def send_dbg_command(command):
|
||||
"""
|
||||
Send a direct command to the debugger backend, and
|
||||
retrieve the result as a string.
|
||||
|
||||
Note: any double-quotes in 'command' must be backslash-escaped.
|
||||
Note: this only works with some debugger backends: Bochs, WinDbg, GDB.
|
||||
|
||||
Returns: (True, <result string>) on success, or (False, <Error message string>) on failure
|
||||
"""
|
||||
rv = ida_expr.idc_value_t()
|
||||
err = ida_expr.eval_idc_expr(rv, ida_idaapi.BADADDR, """send_dbg_command("%s");""" % command)
|
||||
if err:
|
||||
return False, "eval_idc_expr() failed: %s" % err
|
||||
vtype = ord(rv.vtype)
|
||||
if vtype == ida_expr.VT_STR:
|
||||
s = rv.c_str()
|
||||
if "IDC_FAILURE" in s:
|
||||
return False, "eval_idc_expr() reported an error: %s" % s
|
||||
return True, s
|
||||
elif vtype == ida_expr.VT_LONG:
|
||||
return True, str(rv.num)
|
||||
else:
|
||||
return False, "eval_idc_expr(): wrong return type: %d" % vtype
|
||||
|
||||
#</pycode(py_dbg)>
|
||||
|
||||
#<pycode_BC695(py_dbg)>
|
||||
|
||||
+7
-3
@@ -236,11 +236,15 @@ static PyObject *dbg_get_thread_sreg_base(PyObject *py_tid, PyObject *py_sreg_va
|
||||
{
|
||||
PYW_GIL_CHECK_LOCKED_SCOPE();
|
||||
|
||||
if ( !dbg_can_query() || !PyInt_Check(py_tid) || !PyInt_Check(py_sreg_value) )
|
||||
if ( !dbg_can_query()
|
||||
|| (!PyInt_Check(py_tid) && !PyLong_Check(py_tid))
|
||||
|| (!PyInt_Check(py_sreg_value) && !PyLong_Check(py_sreg_value)) )
|
||||
{
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
ea_t answer;
|
||||
thid_t tid = PyInt_AsLong(py_tid);
|
||||
int sreg_value = PyInt_AsLong(py_sreg_value);
|
||||
thid_t tid = PyLong_AsLong(py_tid);
|
||||
int sreg_value = PyLong_AsLong(py_sreg_value);
|
||||
if ( internal_get_sreg_base(&answer, tid, sreg_value) != 1 )
|
||||
Py_RETURN_NONE;
|
||||
|
||||
|
||||
+5
-2
@@ -224,11 +224,14 @@ class Appcall_callable__(object):
|
||||
class Appcall_consts__(object):
|
||||
"""Helper class used by Appcall.Consts attribute
|
||||
It is used to retrieve constants via attribute access"""
|
||||
def __init__(self, default=0):
|
||||
def __init__(self, default=None):
|
||||
self.__default = default
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return Appcall__.valueof(attr, self.__default)
|
||||
v = Appcall__.valueof(attr, self.__default)
|
||||
if v is None:
|
||||
raise ValueError, "No constant with name " + attr
|
||||
return v
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
class Appcall__(object):
|
||||
|
||||
@@ -91,7 +91,10 @@ chtype_segreg=chtype_srcp
|
||||
close_tform=close_widget
|
||||
find_tform=find_widget
|
||||
get_current_tform=get_current_widget
|
||||
get_highlighted_identifier=get_highlight
|
||||
def get_highlighted_identifier():
|
||||
thing = get_highlight(get_current_widget())
|
||||
if thing and thing[1]:
|
||||
return thing[0]
|
||||
get_tform_title=get_widget_title
|
||||
get_tform_type=get_widget_type
|
||||
is_chooser_tform=is_chooser_widget
|
||||
|
||||
@@ -67,6 +67,21 @@
|
||||
%ignore close_linput;
|
||||
%rename (close_linput) py_close_linput;
|
||||
|
||||
%apply qstrvec_t *out { qstrvec_t *dirs };
|
||||
|
||||
%cstring_output_buf_and_size_returning_charptr(
|
||||
1,
|
||||
char *buf,
|
||||
size_t bufsize,
|
||||
const char *filename,
|
||||
const char *subdir); // getsysfile
|
||||
%cstring_output_buf_and_size_returning_charptr(
|
||||
3,
|
||||
linput_t *li,
|
||||
int64 fpos,
|
||||
char *buf,
|
||||
size_t bufsize); // qlgetz
|
||||
|
||||
%include "diskio.hpp"
|
||||
|
||||
%{
|
||||
|
||||
@@ -63,6 +63,12 @@
|
||||
%ignore compile_idc_text;
|
||||
%rename (compile_idc_text) py_compile_idc_text;
|
||||
|
||||
%cstring_output_buf_and_size_returning_charptr(
|
||||
1,
|
||||
char *buf,
|
||||
size_t bufsize,
|
||||
const char *file); // get_idc_filename
|
||||
|
||||
%nonnul_argument_prototype(
|
||||
bool py_compile_idc_file(const char *nonnul_line, qstring *errbuf),
|
||||
const char *nonnul_line);
|
||||
|
||||
@@ -7,4 +7,15 @@
|
||||
%ignore register_custom_fixup;
|
||||
%ignore unregister_custom_fixup;
|
||||
|
||||
%cstring_output_qstring_returning_charptr(
|
||||
1,
|
||||
qstring *buf,
|
||||
ea_t source); // fixup_data_t::get_desc
|
||||
|
||||
%cstring_output_qstring_returning_charptr(
|
||||
1,
|
||||
qstring *buf,
|
||||
ea_t source,
|
||||
const fixup_data_t &fd); // get_fixup_desc
|
||||
|
||||
%include "fixup.hpp"
|
||||
|
||||
+14
@@ -80,6 +80,20 @@
|
||||
static PyObject *AssembleLine(ea_t ea, ea_t cs, ea_t ip, bool use32, const char *nonnul_line),
|
||||
const char *nonnul_line);
|
||||
|
||||
#ifndef SWIGIMPORTED // let's not modify the wrappers for modules %import'ing us (e.g., typeinf.i, hexrays.i)
|
||||
%cstring_output_buf_and_size_returning_charptr(
|
||||
1,
|
||||
char *buf,
|
||||
size_t bufsize); // get_idp_name
|
||||
#endif // SWIGIMPORTED
|
||||
|
||||
%cstring_output_qstring_returning_charptr(
|
||||
1,
|
||||
qstring *out,
|
||||
const char *name,
|
||||
uint32 disable_mask,
|
||||
int demreq); // ev_demangle_name
|
||||
|
||||
%include "idp.hpp"
|
||||
%include "config.hpp"
|
||||
|
||||
|
||||
@@ -131,6 +131,9 @@ extern plugin_t PLUGIN;
|
||||
%ignore place_t__serialize;
|
||||
%ignore place_t::deserialize;
|
||||
%ignore place_t__deserialize;
|
||||
%ignore place_t::generate;
|
||||
%ignore place_t__generate;
|
||||
%rename (generate) py_generate;
|
||||
|
||||
%ignore register_place_class;
|
||||
%ignore register_loc_converter;
|
||||
@@ -297,6 +300,21 @@ static void _py_unregister_compiled_form(PyObject *py_form, bool shutdown);
|
||||
static enumplace_t *as_enumplace_t(place_t *p) { return (enumplace_t *) p; }
|
||||
static structplace_t *as_structplace_t(place_t *p) { return (structplace_t *) p; }
|
||||
static simpleline_place_t *as_simpleline_place_t(place_t *p) { return (simpleline_place_t *) p; }
|
||||
|
||||
PyObject *py_generate(void *ud, int maxsize)
|
||||
{
|
||||
qstrvec_t lines;
|
||||
int deflnnum = 0;
|
||||
color_t pfx_color = 0;
|
||||
bgcolor_t bgcolor = DEFCOLOR;
|
||||
int generated = $self->generate(&lines, &deflnnum, &pfx_color, &bgcolor, ud, maxsize);
|
||||
PyObject *tuple = PyTuple_New(4);
|
||||
PyTuple_SetItem(tuple, 0, qstrvec2pylist(lines));
|
||||
PyTuple_SetItem(tuple, 1, PyLong_FromLong(deflnnum));
|
||||
PyTuple_SetItem(tuple, 2, PyLong_FromLong(uchar(pfx_color)));
|
||||
PyTuple_SetItem(tuple, 3, PyLong_FromLong(bgcolor));
|
||||
return tuple;
|
||||
}
|
||||
}
|
||||
|
||||
%extend twinpos_t {
|
||||
|
||||
@@ -255,6 +255,12 @@
|
||||
%ignore remove_tinfo_pointer;
|
||||
%rename (remove_tinfo_pointer) py_remove_tinfo_pointer;
|
||||
|
||||
%cstring_output_buf_and_size_returning_charptr(
|
||||
2,
|
||||
ea_t ea,
|
||||
char *buf,
|
||||
size_t bufsize); // idc_guess_type, idc_get_type
|
||||
|
||||
%include "typeinf.hpp"
|
||||
|
||||
// Custom wrappers
|
||||
|
||||
@@ -468,6 +468,28 @@ static PyObject *type##_get_clink_ptr(PyObject *self)
|
||||
}
|
||||
%enddef
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// and a helper for overriding the 'argout' of some of those functions,
|
||||
// that return the input char* as return code; those build on recent
|
||||
// versions of Xcode (>= 9.0), and need special care
|
||||
%define %cstring_output_buf_and_size_returning_charptr(BUFIDX, ...)
|
||||
%typemap(argout) (__VA_ARGS__) {
|
||||
// cstring_output_buf_and_size_returning_charptr's argout
|
||||
Py_XDECREF(resultobj);
|
||||
if (result != NULL)
|
||||
{
|
||||
resultobj = PyString_FromString($BUFIDX);
|
||||
}
|
||||
else
|
||||
{
|
||||
Py_INCREF(Py_None);
|
||||
resultobj = Py_None;
|
||||
}
|
||||
qfree($BUFIDX);
|
||||
}
|
||||
%enddef
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// IN/OUT qstring
|
||||
//---------------------------------------------------------------------
|
||||
@@ -491,8 +513,43 @@ static PyObject *type##_get_clink_ptr(PyObject *self)
|
||||
// Nothing. We certainly don't want 'temp' to be deleted.
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// md5/sha256 hash retrieval
|
||||
//-------------------------------------------------------------------------
|
||||
// see %cstring_output_buf_and_size_returning_charptr above
|
||||
%define %cstring_output_qstring_returning_charptr(BUFIDX, ...)
|
||||
%typemap(argout) (__VA_ARGS__) {
|
||||
// cstring_output_qstring_returning_charptr's argout
|
||||
Py_XDECREF(resultobj);
|
||||
if (result != NULL)
|
||||
{
|
||||
resultobj = PyString_FromStringAndSize($BUFIDX->begin(), $BUFIDX->length());
|
||||
}
|
||||
else
|
||||
{
|
||||
Py_INCREF(Py_None);
|
||||
resultobj = Py_None;
|
||||
}
|
||||
}
|
||||
%enddef
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// OUT qstrvec_t
|
||||
//-------------------------------------------------------------------------
|
||||
%typemap(in,numinputs=0) qstrvec_t *out (qstrvec_t temp) {
|
||||
$1 = &temp;
|
||||
}
|
||||
%typemap(argout) qstrvec_t *out
|
||||
{
|
||||
Py_XDECREF(resultobj);
|
||||
resultobj = qstrvec2pylist(*($1));
|
||||
}
|
||||
%typemap(freearg) qstrvec_t* out
|
||||
{
|
||||
// Nothing. We certainly don't want 'temp' to be deleted.
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// md5/sha256 hash retrieval
|
||||
//-------------------------------------------------------------------------
|
||||
%typemap(in, numinputs=0) uchar hash[ANY] (uchar temp[$1_dim0])
|
||||
{
|
||||
$1 = temp;
|
||||
@@ -771,7 +828,7 @@ struct wrapped_array_t {
|
||||
|
||||
|
||||
%{
|
||||
static PyObject *qstrvec2pylist(qstrvec_t &vec)
|
||||
static PyObject *qstrvec2pylist(const qstrvec_t &vec)
|
||||
{
|
||||
size_t n = vec.size();
|
||||
PyObject *py_list = PyList_New(n);
|
||||
|
||||
@@ -64,6 +64,10 @@ forbidden = [
|
||||
"SaveBase",
|
||||
"eval",
|
||||
"MakeStr",
|
||||
"GetProcessorName",
|
||||
"SegStart",
|
||||
"SegEnd",
|
||||
"SetSegmentType",
|
||||
]
|
||||
|
||||
symbols_modules = {
|
||||
|
||||
Reference in New Issue
Block a user