mirror of
https://github.com/idapython/src
synced 2026-06-08 14:47:00 +00:00
IDAPython for IDA 7.0: Initial commit, plus bugfixes up to 2017 Oct, 13th.
This commit is contained in:
@@ -0,0 +1,428 @@
|
||||
#---------------------------------------------------------------------
|
||||
# IDAPython - Python plugin for Interactive Disassembler
|
||||
#
|
||||
# (c) The IDAPython Team <idapython@googlegroups.com>
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
# For detailed copyright information see the file COPYING in
|
||||
# the root of the distribution archive.
|
||||
#---------------------------------------------------------------------
|
||||
#
|
||||
# dex.py - module to access DEX-file related information
|
||||
#
|
||||
#---------------------------------------------------------------------
|
||||
# pylint: disable=C0103, C0111, C0301, C0326, W0511, R0903
|
||||
import ctypes
|
||||
import idaapi
|
||||
import ida_idaapi
|
||||
import ida_bytes
|
||||
|
||||
uint8 = ctypes.c_ubyte
|
||||
char = ctypes.c_char
|
||||
uint32 = ctypes.c_uint
|
||||
uint64 = ctypes.c_uint64
|
||||
uint16 = ctypes.c_ushort
|
||||
ushort = uint16
|
||||
# __EA64__ is set if IDA is running in 64-bit mode
|
||||
__EA64__ = ida_idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL
|
||||
ea_t = uint64 if __EA64__ else uint32
|
||||
|
||||
# parse a ctypes struct from byte data in str_ at 'off'
|
||||
def get_struct(str_, off, struct):
|
||||
s = struct()
|
||||
slen = ctypes.sizeof(s)
|
||||
bytebuf = str_[off:off+slen]
|
||||
fit = min(len(bytebuf), slen)
|
||||
if fit < slen:
|
||||
raise Exception("can't read struct: %d bytes available but %d required" % (fit, slen))
|
||||
ctypes.memmove(ctypes.addressof(s), bytebuf, fit)
|
||||
return s
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# This structure is used both for imported methods and locally defined ones
|
||||
#
|
||||
class dex_method(ctypes.LittleEndianStructure):
|
||||
# flags
|
||||
IS_LOCAL = 1
|
||||
HAS_CODE = 2
|
||||
_fields_ = [
|
||||
("flags", uint32), # Class type where this method is defined
|
||||
("defaddr", ea_t), # Address in file where the "definiton" (DexMethodId) is stored
|
||||
("cname", uint32), # Class type where this method is defined
|
||||
("id", uint32), # Id of method; key to look up name
|
||||
("proto_ret", uint32), # Name of return type
|
||||
("proto_shorty", uint32), # 'shorty' parameter descirptor name
|
||||
("nparams", ushort), # No of parameters to method. May be >32
|
||||
("proto_params",uint32*32), # Name of types for the first 32 parameters
|
||||
("access_flags", uint32), # Access flags
|
||||
("startAddr", ea_t), # Function start and end address
|
||||
("endAddr", ea_t), #
|
||||
("reg_total", ushort), # Registers total, parameters and out
|
||||
("reg_params", ushort), #
|
||||
("reg_out", ushort), #
|
||||
("catchHData", ea_t), # offset to methods catch handler data
|
||||
]
|
||||
def is_local(self):
|
||||
return (self.flags & dex_method.IS_LOCAL) != 0
|
||||
|
||||
|
||||
"""
|
||||
struct dex_field
|
||||
{
|
||||
uint32 ctype, name, type;
|
||||
ea_t maddr; // Address used for xrefs.
|
||||
};
|
||||
|
||||
"""
|
||||
class dex_field(ctypes.LittleEndianStructure):
|
||||
# flags
|
||||
_fields_ = [
|
||||
("ctype", uint32), #
|
||||
("name", uint32), #
|
||||
("type", uint32), #
|
||||
("maddr", ea_t), # Address used for xrefs.
|
||||
]
|
||||
|
||||
"""
|
||||
struct longname_director_t
|
||||
{
|
||||
char zero;
|
||||
netnode node;
|
||||
};
|
||||
"""
|
||||
class longname_director_t(ctypes.LittleEndianStructure):
|
||||
# flags
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
("zero", uint8), #
|
||||
("node", ea_t), # netnode index with the actual string blob
|
||||
]
|
||||
|
||||
|
||||
class Dex(object):
|
||||
|
||||
# meta-data
|
||||
HASHVAL_MAGIC = "version" # Interface version
|
||||
HASHVAL_OPTIMIZED = "optimized" # 1 for optimized dex files, 0 - for others
|
||||
HASHVAL_DEXVERSION = "dex_version" # DEX File version
|
||||
|
||||
# The dex string table; lookup from string id# to values
|
||||
STRTAB_TAB = 1 # Lookup string id => address
|
||||
STRTAB_RTAB = 2 # Lookup address => id
|
||||
|
||||
# fields
|
||||
FIELDTAB_DESCR = 1 # Field id => struct dex_field
|
||||
FIELDTAB_NAMEDATA = 2 # Field id => char data, field name
|
||||
|
||||
# The dex method table; lookup method meta-data based on index
|
||||
METHTAB_BEGIN = 1 # Method id => start address
|
||||
METHTAB_RBEGIN = 2 # Start address => method id
|
||||
METHTAB_DESCR = 3 # Method id => struct dex_method
|
||||
METHTAB_NAMEDATA = 4 # Method id => char data, method name
|
||||
METHTAB_NAMEORGDATA = 5 # Method id => char data, method name from dex file
|
||||
METHTAB_NTAB = 6 # Method id => String id of method name
|
||||
|
||||
# debug info representation
|
||||
DEBINFO_LINEINFO = 1 # Line start EA => dex_lineinfo_t
|
||||
|
||||
# Try/Catches
|
||||
TRYTAB_TRYLIST = 3 # key=methodIdx, value= tryItem data
|
||||
TRYTAB_HANDLERLIST = 4 # key=ea (handler start), value=list of typeIdx, handled types
|
||||
TRYTAB_HANDLERTRYLIST = 5 # key=ea (handler start), value=list of tryItemIdx
|
||||
|
||||
# Types
|
||||
TYPETAB_TAB = 1 # Type ID => String ID
|
||||
TYPETAB_STRDATA = 2 # Type ID => String data (possible user redefined)
|
||||
TYPETAB_STRORGDATA = 3 # Type ID => Original String data
|
||||
TYPETAB_EA = 4 # Type ID => ea
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
def __init__(self):
|
||||
self.nn_meta = idaapi.netnode("$ dex_meta")
|
||||
self.nn_strtab = idaapi.netnode("$ dex_strtab")
|
||||
self.nn_fieldtab = idaapi.netnode("$ dex_fields")
|
||||
self.nn_methtab = idaapi.netnode("$ dex_methtab")
|
||||
self.nn_debinfo = idaapi.netnode("$ dex_debinfo")
|
||||
self.nn_trytab = idaapi.netnode("$ dex_tries")
|
||||
self.nn_typetab = idaapi.netnode("$ dex_types")
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
ACCESS_FLAGS = {
|
||||
"public" : 0x00000001,
|
||||
"private" : 0x00000002,
|
||||
"protected" : 0x00000004,
|
||||
"static" : 0x00000008,
|
||||
"final" : 0x00000010,
|
||||
"synchronized" : 0x00000020,
|
||||
"volatile" : 0x00000040,
|
||||
"bridge" : 0x00000040,
|
||||
"transient" : 0x00000080,
|
||||
"varargs" : 0x00000080,
|
||||
"native" : 0x00000100,
|
||||
"interface" : 0x00000200,
|
||||
"abstract" : 0x00000400,
|
||||
"strictfp" : 0x00000800,
|
||||
"synthetic" : 0x00001000,
|
||||
"annotation" : 0x00002000,
|
||||
"enum" : 0x00004000,
|
||||
"constructor" : 0x00010000,
|
||||
"dsynchronized" : 0x00020000, }
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
@staticmethod
|
||||
def access_string(flags):
|
||||
res = ""
|
||||
for access_bit in ("synchronized", "synthetic", "public",
|
||||
"private", "protected", "interface",
|
||||
"abstract", "strictfp", "final",
|
||||
"native", "static"):
|
||||
if flags & Dex.ACCESS_FLAGS[access_bit] != 0:
|
||||
res += " " + access_bit
|
||||
return res[1:] if res else ""
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
def get_string(self, string_idx):
|
||||
addr = self.nn_strtab.altval(string_idx, Dex.STRTAB_TAB)
|
||||
if addr is 0:
|
||||
return None
|
||||
length = ida_bytes.get_max_strlit_length(addr, STRTYPE_C, ida_bytes.ALOPT_IGNHEADS|ida_bytes.ALOPT_IGNPRINT)
|
||||
return ida_bytes.get_strlit_contents(addr, length, STRTYPE_C)
|
||||
|
||||
def get_method_idx(self, ea):
|
||||
return self.nn_methtab.altval(ea, Dex.METHTAB_RBEGIN)
|
||||
|
||||
def get_method(self, method_idx):
|
||||
val = self.nn_methtab.supval(method_idx, Dex.METHTAB_DESCR)
|
||||
if len(val) != ctypes.sizeof(dex_method):
|
||||
print "bad data in METHTAB_DESCR for index 0x%X" % method_idx
|
||||
return None
|
||||
method = get_struct(val,0, dex_method)
|
||||
return method
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
@staticmethod
|
||||
def get_string_by_index(node, idx, tag):
|
||||
if idx is None:
|
||||
return None
|
||||
val = node.supval(idx, tag)
|
||||
# check for long line
|
||||
if len(val) == ctypes.sizeof(longname_director_t):
|
||||
longname_director = get_struct(val, 0, longname_director_t)
|
||||
if longname_director.zero == 0:
|
||||
nn = idaapi.netnode(longname_director.node)
|
||||
return nn.getblob(0, tag)[:-1]
|
||||
if len(val) > 0:
|
||||
return val[:-1]
|
||||
return ""
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Converts a single-char primitive type into its human-readable equivalent
|
||||
PRIMITVE_TYPES = {
|
||||
'B': "byte",
|
||||
'C': "char",
|
||||
'D': "double",
|
||||
'F': "float",
|
||||
'I': "int",
|
||||
'J': "long",
|
||||
'S': "short",
|
||||
'V': "void",
|
||||
'Z': "boolean",
|
||||
'L': "ref" }
|
||||
@staticmethod
|
||||
def _primitive_type_label(typechar):
|
||||
if typechar in Dex.PRIMITVE_TYPES:
|
||||
return Dex.PRIMITVE_TYPES[typechar]
|
||||
return "UNKNOWN"
|
||||
|
||||
@staticmethod
|
||||
def is_wide_type(typechar):
|
||||
return typechar[0] == 'J' or typechar[0] == 'D'
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Converts a type descriptor to human-readable "dotted" form. For
|
||||
# example, "Ljava/lang/String;" becomes "java.lang.String", and
|
||||
# "[I" becomes "int[]". Also converts '$' to '.', which means this
|
||||
# form can't be converted back to a descriptor.
|
||||
@staticmethod
|
||||
def decorate_java_typename(desc):
|
||||
target_len = len(desc)
|
||||
offset = 0
|
||||
# strip leading [s; will be added to end
|
||||
while target_len > 1 and desc[offset] == '[':
|
||||
offset += 1
|
||||
target_len -= 1
|
||||
array_depth = offset
|
||||
if target_len == 1:
|
||||
# primitive type
|
||||
desc = Dex._primitive_type_label(desc[offset])
|
||||
offset = 0
|
||||
target_len = len(desc)
|
||||
else:
|
||||
# account for leading 'L' and trailing ';'
|
||||
if target_len >= 2 and desc[offset] == 'L' and desc[offset + target_len - 1] == ';':
|
||||
target_len -= 2
|
||||
offset += 1
|
||||
# copy class name over
|
||||
res = ""
|
||||
for _i in range(0, target_len):
|
||||
ch = desc[offset + _i]
|
||||
res += '.' if ch == '/' else ch
|
||||
# add the appropriate number of brackets for arrays
|
||||
res += "[]"*array_depth
|
||||
return res
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
def get_type_string(self, type_idx):
|
||||
return Dex.get_string_by_index(self.nn_typetab, type_idx, Dex.TYPETAB_STRDATA)
|
||||
|
||||
def get_method_name(self, method_idx):
|
||||
return Dex.get_string_by_index(self.nn_methtab, method_idx, Dex.METHTAB_NAMEDATA)
|
||||
|
||||
def get_field_name(self, field_idx):
|
||||
return Dex.get_string_by_index(self.nn_fieldtab, field_idx, Dex.FIELDTAB_NAMEDATA)
|
||||
|
||||
def get_parameter_name(self, idx):
|
||||
return self.get_string(idx)
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
@staticmethod
|
||||
def get_short_type_name(longname):
|
||||
if not longname:
|
||||
return "unknown"
|
||||
deco = Dex.decorate_java_typename(longname)
|
||||
if not deco:
|
||||
return "unknown"
|
||||
start = deco.rfind('.')
|
||||
if start == -1:
|
||||
start = 0
|
||||
else:
|
||||
start += 1
|
||||
return deco[start:].replace('<', '_').replace('>', '_')
|
||||
|
||||
@staticmethod
|
||||
def get_full_type_name(longname):
|
||||
if not longname:
|
||||
return "unknown"
|
||||
return Dex.decorate_java_typename(longname)
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
def get_short_method_name(self, method):
|
||||
res = Dex.get_short_type_name(self.get_type_string(method.cname))
|
||||
res += '.'
|
||||
res += self.get_method_name(method.id)
|
||||
res += '@'
|
||||
res += self.get_string(method.proto_shorty)
|
||||
return res
|
||||
|
||||
def get_full_method_name(self, method):
|
||||
res = Dex.get_full_type_name(self.get_type_string(method.proto_ret))
|
||||
res += ' '
|
||||
res += self.get_full_type_name(self.get_type_string(method.cname))
|
||||
res += '.'
|
||||
res += self.get_method_name(method.id)
|
||||
|
||||
def get_call_method_name(self, method):
|
||||
shorty = self.get_string(method.proto_shorty)
|
||||
res = Dex._primitive_type_label(shorty[0])
|
||||
res += ' '
|
||||
res += Dex.get_short_type_name(self.get_type_string(method.cname))
|
||||
res += '.'
|
||||
res += self.get_method_name(method.id)
|
||||
res += '('
|
||||
last_idx = len(shorty) - 1
|
||||
for s in range(1, last_idx + 1):
|
||||
res += Dex._primitive_type_label(shorty[s])
|
||||
if s != last_idx:
|
||||
res += ", "
|
||||
res += ')'
|
||||
return res
|
||||
|
||||
def get_field(self, method_idx):
|
||||
val = self.nn_fieldtab.supval(method_idx, Dex.FIELDTAB_DESCR)
|
||||
if len(val) != ctypes.sizeof(dex_field):
|
||||
print "bad data in FIELDTAB_DESCR for index 0x%X" % method_idx
|
||||
return None
|
||||
field = get_struct(val,0, dex_field)
|
||||
return field
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
def get_full_field_name(self, field_idx, field, field_name):
|
||||
res = Dex.get_full_type_name(self.get_type_string(field.type))
|
||||
res += ' '
|
||||
res += Dex.get_full_type_name(self.get_type_string(field_idx))
|
||||
res += '.'
|
||||
res += field_name if field_name else self.get_field_name(field_idx)
|
||||
return res
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
def get_short_field_name(self, field_idx, field, field_name):
|
||||
res = Dex.get_short_type_name(self.get_type_string(field.ctype))
|
||||
res += '_'
|
||||
res += field_name if field_name else self.get_field_name(field_idx)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
dex = Dex()
|
||||
# reproduce IDA function header
|
||||
f = idaapi.get_func(here())
|
||||
if not f:
|
||||
print "ERROR: must be in a function!"
|
||||
exit(1)
|
||||
|
||||
func_start_ea = f.start_ea
|
||||
methno = dex.get_method_idx(func_start_ea)
|
||||
func_method = dex.get_method(methno)
|
||||
if func_method is None:
|
||||
print "ERROR: Missing method info"
|
||||
exit(1)
|
||||
out = ""
|
||||
# Return type
|
||||
out += Dex.access_string(func_method.access_flags) + " "
|
||||
method_proto = dex.get_type_string(func_method.proto_ret)
|
||||
if method_proto:
|
||||
out += Dex.get_full_type_name(method_proto)
|
||||
else:
|
||||
out += "%x" % func_method.proto_ret
|
||||
out += ' '
|
||||
# Class name
|
||||
method_classnm = dex.get_type_string(func_method.cname)
|
||||
if method_classnm:
|
||||
out += Dex.get_full_type_name(method_classnm)
|
||||
else:
|
||||
out += "%x" % func_method.cname
|
||||
out += '.'
|
||||
# Method name
|
||||
method_name = dex.get_method_name(methno)
|
||||
if method_name:
|
||||
out += method_name
|
||||
else:
|
||||
out += "%x" % methno
|
||||
# Method parameters
|
||||
if func_method.nparams == 0:
|
||||
print out + "()"
|
||||
else:
|
||||
print out + "("
|
||||
out = ""
|
||||
maxp = min(func_method.nparams, 32)
|
||||
start_reg = func_method.reg_total - func_method.reg_params
|
||||
if func_method.access_flags & Dex.ACCESS_FLAGS["static"] == 0:
|
||||
start_reg += 1
|
||||
for i in range(0, maxp):
|
||||
ptype = dex.get_type_string(func_method.proto_params[i])
|
||||
|
||||
out = " %s " % dex.get_full_type_name(ptype)
|
||||
regbuf = "v%u" % start_reg
|
||||
start_reg += 1
|
||||
r = idaapi.find_regvar(f, f.start_ea, regbuf)
|
||||
if r is None:
|
||||
out += regbuf
|
||||
if Dex.is_wide_type(ptype):
|
||||
out += ':'
|
||||
regbuf = "v%u" % start_reg
|
||||
start_reg += 1
|
||||
else:
|
||||
out += r.user
|
||||
out += ')' if i + 1 == maxp else ','
|
||||
print out
|
||||
+77
-129
@@ -56,7 +56,7 @@ def CodeRefsTo(ea, flow):
|
||||
|
||||
Example::
|
||||
|
||||
for ref in CodeRefsTo(ScreenEA(), 1):
|
||||
for ref in CodeRefsTo(get_screen_ea(), 1):
|
||||
print ref
|
||||
"""
|
||||
if flow == 1:
|
||||
@@ -77,7 +77,7 @@ def CodeRefsFrom(ea, flow):
|
||||
|
||||
Example::
|
||||
|
||||
for ref in CodeRefsFrom(ScreenEA(), 1):
|
||||
for ref in CodeRefsFrom(get_screen_ea(), 1):
|
||||
print ref
|
||||
"""
|
||||
if flow == 1:
|
||||
@@ -96,7 +96,7 @@ def DataRefsTo(ea):
|
||||
|
||||
Example::
|
||||
|
||||
for ref in DataRefsTo(ScreenEA()):
|
||||
for ref in DataRefsTo(get_screen_ea()):
|
||||
print ref
|
||||
"""
|
||||
return refs(ea, ida_xref.get_first_dref_to, ida_xref.get_next_dref_to)
|
||||
@@ -112,7 +112,7 @@ def DataRefsFrom(ea):
|
||||
|
||||
Example::
|
||||
|
||||
for ref in DataRefsFrom(ScreenEA()):
|
||||
for ref in DataRefsFrom(get_screen_ea()):
|
||||
print ref
|
||||
"""
|
||||
return refs(ea, ida_xref.get_first_dref_from, ida_xref.get_next_dref_from)
|
||||
@@ -193,24 +193,24 @@ def XrefsTo(ea, flags=0):
|
||||
|
||||
def Threads():
|
||||
"""Returns all thread IDs"""
|
||||
for i in xrange(0, idc.GetThreadQty()):
|
||||
yield idc.GetThreadId(i)
|
||||
for i in xrange(0, idc.get_thread_qty()):
|
||||
yield idc.getn_thread(i)
|
||||
|
||||
|
||||
def Heads(start=None, end=None):
|
||||
"""
|
||||
Get a list of heads (instructions or data)
|
||||
|
||||
@param start: start address (default: inf.minEA)
|
||||
@param end: end address (default: inf.maxEA)
|
||||
@param start: start address (default: inf.min_ea)
|
||||
@param end: end address (default: inf.max_ea)
|
||||
|
||||
@return: list of heads between start and end
|
||||
"""
|
||||
if not start: start = ida_ida.cvar.inf.minEA
|
||||
if not end: end = ida_ida.cvar.inf.maxEA
|
||||
if not start: start = ida_ida.cvar.inf.min_ea
|
||||
if not end: end = ida_ida.cvar.inf.max_ea
|
||||
|
||||
ea = start
|
||||
if not idc.isHead(idc.GetFlags(ea)):
|
||||
if not idc.is_head(ida_bytes.get_flags(ea)):
|
||||
ea = ida_bytes.next_head(ea, end)
|
||||
while ea != ida_idaapi.BADADDR:
|
||||
yield ea
|
||||
@@ -221,8 +221,8 @@ def Functions(start=None, end=None):
|
||||
"""
|
||||
Get a list of functions
|
||||
|
||||
@param start: start address (default: inf.minEA)
|
||||
@param end: end address (default: inf.maxEA)
|
||||
@param start: start address (default: inf.min_ea)
|
||||
@param end: end address (default: inf.max_ea)
|
||||
|
||||
@return: list of heads between start and end
|
||||
|
||||
@@ -231,19 +231,19 @@ def Functions(start=None, end=None):
|
||||
in multiple segments will be reported multiple times, once in each segment
|
||||
as they are listed.
|
||||
"""
|
||||
if not start: start = ida_ida.cvar.inf.minEA
|
||||
if not end: end = ida_ida.cvar.inf.maxEA
|
||||
if not start: start = ida_ida.cvar.inf.min_ea
|
||||
if not end: end = ida_ida.cvar.inf.max_ea
|
||||
|
||||
# find first function head chunk in the range
|
||||
chunk = ida_funcs.get_fchunk(start)
|
||||
if not chunk:
|
||||
chunk = ida_funcs.get_next_fchunk(start)
|
||||
while chunk and chunk.startEA < end and (chunk.flags & ida_funcs.FUNC_TAIL) != 0:
|
||||
chunk = ida_funcs.get_next_fchunk(chunk.startEA)
|
||||
while chunk and chunk.start_ea < end and (chunk.flags & ida_funcs.FUNC_TAIL) != 0:
|
||||
chunk = ida_funcs.get_next_fchunk(chunk.start_ea)
|
||||
func = chunk
|
||||
|
||||
while func and func.startEA < end:
|
||||
startea = func.startEA
|
||||
while func and func.start_ea < end:
|
||||
startea = func.start_ea
|
||||
yield startea
|
||||
func = ida_funcs.get_next_func(startea)
|
||||
|
||||
@@ -261,7 +261,7 @@ def Chunks(start):
|
||||
status = func_iter.main()
|
||||
while status:
|
||||
chunk = func_iter.chunk()
|
||||
yield (chunk.startEA, chunk.endEA)
|
||||
yield (chunk.start_ea, chunk.end_ea)
|
||||
status = func_iter.next()
|
||||
|
||||
|
||||
@@ -297,7 +297,7 @@ def Segments():
|
||||
for n in xrange(ida_segment.get_segm_qty()):
|
||||
seg = ida_segment.getnseg(n)
|
||||
if seg:
|
||||
yield seg.startEA
|
||||
yield seg.start_ea
|
||||
|
||||
|
||||
def Entries():
|
||||
@@ -338,11 +338,11 @@ def Structs():
|
||||
|
||||
@return: List of tuples (idx, sid, name)
|
||||
"""
|
||||
idx = idc.GetFirstStrucIdx()
|
||||
idx = idc.get_first_struc_idx()
|
||||
while idx != ida_idaapi.BADADDR:
|
||||
sid = idc.GetStrucId(idx)
|
||||
yield (idx, sid, idc.GetStrucName(sid))
|
||||
idx = idc.GetNextStrucIdx(idx)
|
||||
sid = idc.get_struc_by_idx(idx)
|
||||
yield (idx, sid, idc.get_struc_name(sid))
|
||||
idx = idc.get_next_struc_idx(idx)
|
||||
|
||||
|
||||
def StructMembers(sid):
|
||||
@@ -358,14 +358,14 @@ def StructMembers(sid):
|
||||
@note: This will not return 'holes' in structures/stack frames;
|
||||
it only returns defined structure members.
|
||||
"""
|
||||
m = idc.GetFirstMember(sid)
|
||||
m = idc.get_first_member(sid)
|
||||
if m == -1:
|
||||
raise Exception("No structure with ID: 0x%x" % sid)
|
||||
while (m != ida_idaapi.BADADDR):
|
||||
name = idc.GetMemberName(sid, m)
|
||||
name = idc.get_member_name(sid, m)
|
||||
if name:
|
||||
yield (m, name, idc.GetMemberSize(sid, m))
|
||||
m = idc.GetStrucNextOff(sid, m)
|
||||
yield (m, name, idc.get_member_size(sid, m))
|
||||
m = idc.get_next_offset(sid, m)
|
||||
|
||||
|
||||
def DecodePrecedingInstruction(ea):
|
||||
@@ -376,12 +376,9 @@ def DecodePrecedingInstruction(ea):
|
||||
@return: (None or the decode instruction, farref)
|
||||
farref will contain 'true' if followed an xref, false otherwise
|
||||
"""
|
||||
prev_addr, farref = ida_ua.decode_preceding_insn(ea)
|
||||
if prev_addr == ida_idaapi.BADADDR:
|
||||
return (None, False)
|
||||
else:
|
||||
return (ida_ua.cmd.copy(), farref)
|
||||
|
||||
insn = ida_ua.insn_t()
|
||||
prev_addr, farref = ida_ua.decode_preceding_insn(insn, ea)
|
||||
return (insn, farref) if prev_addr != ida_idaapi.BADADDR else (None, False)
|
||||
|
||||
|
||||
def DecodePreviousInstruction(ea):
|
||||
@@ -391,11 +388,9 @@ def DecodePreviousInstruction(ea):
|
||||
@param ea: address to decode
|
||||
@return: None or a new insn_t instance
|
||||
"""
|
||||
prev_addr = ida_ua.decode_prev_insn(ea)
|
||||
if prev_addr == ida_idaapi.BADADDR:
|
||||
return None
|
||||
|
||||
return ida_ua.cmd.copy()
|
||||
insn = ida_ua.insn_t()
|
||||
prev_addr = ida_ua.decode_prev_insn(insn, ea)
|
||||
return insn if prev_addr != ida_idaapi.BADADDR else None
|
||||
|
||||
|
||||
def DecodeInstruction(ea):
|
||||
@@ -405,11 +400,9 @@ def DecodeInstruction(ea):
|
||||
@param ea: address to decode
|
||||
@return: None or a new insn_t instance
|
||||
"""
|
||||
inslen = ida_ua.decode_insn(ea)
|
||||
if inslen == 0:
|
||||
return None
|
||||
|
||||
return ida_ua.cmd.copy()
|
||||
insn = ida_ua.insn_t()
|
||||
inslen = ida_ua.decode_insn(insn, ea)
|
||||
return insn if inslen > 0 else None
|
||||
|
||||
|
||||
def GetDataList(ea, count, itemsize=1):
|
||||
@@ -421,7 +414,7 @@ def GetDataList(ea, count, itemsize=1):
|
||||
elif itemsize == 2:
|
||||
getdata = ida_bytes.get_word
|
||||
elif itemsize == 4:
|
||||
getdata = ida_bytes.get_long
|
||||
getdata = ida_bytes.get_dword
|
||||
elif itemsize == 8:
|
||||
getdata = ida_bytes.get_qword
|
||||
else:
|
||||
@@ -445,7 +438,7 @@ def PutDataList(ea, datalist, itemsize=1):
|
||||
if itemsize == 2:
|
||||
putdata = ida_bytes.patch_word
|
||||
if itemsize == 4:
|
||||
putdata = ida_bytes.patch_long
|
||||
putdata = ida_bytes.patch_dword
|
||||
|
||||
assert putdata, "Invalid data size! Must be 1, 2 or 4"
|
||||
|
||||
@@ -474,19 +467,21 @@ def GetInputFileMD5():
|
||||
|
||||
@return: MD5 string or None on error
|
||||
"""
|
||||
return idc.GetInputMD5()
|
||||
return idc.retrieve_input_file_md5()
|
||||
|
||||
|
||||
class Strings(object):
|
||||
"""
|
||||
Allows iterating over the string list. The set of strings will not be modified.
|
||||
, unless asked explicitly at setup()-time..
|
||||
Allows iterating over the string list. The set of strings will not be
|
||||
modified, unless asked explicitly at setup()-time. This string list also
|
||||
is used by the "String window" so it may be changed when this window is
|
||||
updated.
|
||||
|
||||
Example:
|
||||
s = Strings()
|
||||
|
||||
for i in s:
|
||||
print "%x: len=%d type=%d -> '%s'" % (i.ea, i.length, i.type, str(i))
|
||||
print "%x: len=%d type=%d -> '%s'" % (i.ea, i.length, i.strtype, str(i))
|
||||
|
||||
"""
|
||||
class StringItem(object):
|
||||
@@ -494,34 +489,19 @@ class Strings(object):
|
||||
Class representing each string item.
|
||||
"""
|
||||
def __init__(self, si):
|
||||
self.ea = si.ea
|
||||
self.ea = si.ea
|
||||
"""String ea"""
|
||||
self.type = si.type
|
||||
"""string type (ASCSTR_xxxxx)"""
|
||||
self.strtype = si.type
|
||||
"""string type (STRTYPE_xxxxx)"""
|
||||
self.length = si.length
|
||||
"""string length"""
|
||||
|
||||
def is_1_byte_encoding(self):
|
||||
return not self.is_2_bytes_encoding() and not self.is_4_bytes_encoding()
|
||||
|
||||
def is_2_bytes_encoding(self):
|
||||
return (self.type & 7) in [ida_nalt.ASCSTR_UTF16, ida_nalt.ASCSTR_ULEN2, ida_nalt.ASCSTR_ULEN4]
|
||||
|
||||
def is_4_bytes_encoding(self):
|
||||
return (self.type & 7) == ida_nalt.ASCSTR_UTF32
|
||||
return ida_nalt.get_strtype_bpu(self.strtype) == 1
|
||||
|
||||
def _toseq(self, as_unicode):
|
||||
if self.is_2_bytes_encoding():
|
||||
conv = ida_bytes.ACFOPT_UTF16
|
||||
pyenc = "utf-16"
|
||||
elif self.is_4_bytes_encoding():
|
||||
conv = ida_bytes.ACFOPT_UTF8
|
||||
pyenc = "utf-8"
|
||||
else:
|
||||
conv = ida_bytes.ACFOPT_ASCII
|
||||
pyenc = 'ascii'
|
||||
strbytes = ida_bytes.get_ascii_contents2(self.ea, self.length, self.type, conv)
|
||||
return unicode(strbytes, pyenc, 'replace') if as_unicode else strbytes
|
||||
strbytes = ida_bytes.get_strlit_contents(self.ea, self.length, self.strtype)
|
||||
return unicode(strbytes, "UTF-8", 'replace') if as_unicode else strbytes
|
||||
|
||||
def __str__(self):
|
||||
return self._toseq(False)
|
||||
@@ -529,25 +509,9 @@ class Strings(object):
|
||||
def __unicode__(self):
|
||||
return self._toseq(True)
|
||||
|
||||
|
||||
STR_C = 0x0001
|
||||
"""C-style ASCII string"""
|
||||
STR_PASCAL = 0x0002
|
||||
"""Pascal-style ASCII string (length byte)"""
|
||||
STR_LEN2 = 0x0004
|
||||
"""Pascal-style, length is 2 bytes"""
|
||||
STR_UNICODE = 0x0008
|
||||
"""Unicode string"""
|
||||
STR_LEN4 = 0x0010
|
||||
"""Pascal-style, length is 4 bytes"""
|
||||
STR_ULEN2 = 0x0020
|
||||
"""Pascal-style Unicode, length is 2 bytes"""
|
||||
STR_ULEN4 = 0x0040
|
||||
"""Pascal-style Unicode, length is 4 bytes"""
|
||||
|
||||
def clear_cache(self):
|
||||
"""Clears the strings list cache"""
|
||||
self.refresh(0, 0) # when ea1=ea2 the kernel will clear the cache
|
||||
ida_strlist.clear_strlist()
|
||||
|
||||
def __init__(self, default_setup = False):
|
||||
"""
|
||||
@@ -559,54 +523,38 @@ class Strings(object):
|
||||
if default_setup:
|
||||
self.setup()
|
||||
else:
|
||||
self.refresh()
|
||||
# restore saved options
|
||||
ida_strlist.get_strlist_options()
|
||||
self.refresh()
|
||||
|
||||
self._si = ida_strlist.string_info_t()
|
||||
self._si = ida_strlist.string_info_t()
|
||||
|
||||
def refresh(self, ea1=None, ea2=None):
|
||||
|
||||
def refresh(self):
|
||||
"""Refreshes the strings list"""
|
||||
if ea1 is None:
|
||||
ea1 = ida_ida.cvar.inf.minEA
|
||||
if ea2 is None:
|
||||
ea2 = ida_ida.cvar.inf.maxEA
|
||||
|
||||
ida_strlist.refresh_strlist(ea1, ea2)
|
||||
ida_strlist.build_strlist()
|
||||
self.size = ida_strlist.get_strlist_qty()
|
||||
|
||||
|
||||
def setup(self,
|
||||
strtypes = STR_C,
|
||||
strtypes = [ida_nalt.STRTYPE_C],
|
||||
minlen = 5,
|
||||
only_7bit = True,
|
||||
ignore_instructions = False,
|
||||
ea1 = None,
|
||||
ea2 = None,
|
||||
display_only_existing_strings = False):
|
||||
|
||||
if ea1 is None:
|
||||
ea1 = ida_ida.cvar.inf.minEA
|
||||
|
||||
if ea2 is None:
|
||||
ea2 = ida_ida.cvar.inf.maxEA
|
||||
|
||||
t = ida_strlist.strwinsetup_t()
|
||||
t = ida_strlist.get_strlist_options()
|
||||
t.strtypes = strtypes
|
||||
t.minlen = minlen
|
||||
t.only_7bit = only_7bit
|
||||
t.ea1 = ea1
|
||||
t.ea2 = ea2
|
||||
t.display_only_existing_strings = display_only_existing_strings
|
||||
ida_strlist.set_strlist_options(t)
|
||||
|
||||
# Automatically refreshes
|
||||
self.refresh()
|
||||
|
||||
|
||||
def _get_item(self, index):
|
||||
if not ida_strlist.get_strlist_item(index, self._si):
|
||||
if not ida_strlist.get_strlist_item(self._si, index):
|
||||
return None
|
||||
else:
|
||||
return Strings.StringItem(self._si)
|
||||
return Strings.StringItem(self._si)
|
||||
|
||||
|
||||
def __iter__(self):
|
||||
@@ -627,7 +575,7 @@ def GetIdbDir():
|
||||
|
||||
This function returns directory path of the current IDB database
|
||||
"""
|
||||
return os.path.dirname(ida_loader.cvar.database_idb) + os.sep
|
||||
return os.path.dirname(ida_loader.get_path(ida_loader.PATH_TYPE_IDB)) + os.sep
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
def GetRegisterList():
|
||||
@@ -653,7 +601,7 @@ def _Assemble(ea, line):
|
||||
seg = ida_segment.getseg(ea)
|
||||
if not seg:
|
||||
return (False, "No segment at ea")
|
||||
ip = ea - (ida_segment.ask_selector(seg.sel) << 4)
|
||||
ip = ea - (ida_segment.sel2para(seg.sel) << 4)
|
||||
buf = ida_idp.AssembleLine(ea, seg.sel, ip, seg.bitness, line)
|
||||
if not buf:
|
||||
return (False, "Assembler failed: " + line)
|
||||
@@ -674,9 +622,9 @@ def Assemble(ea, line):
|
||||
@param ea: start address
|
||||
@return: (False, "Error message") or (True, asm_buf) or (True, [asm_buf1, asm_buf2, asm_buf3])
|
||||
"""
|
||||
old_batch = idc.Batch(1)
|
||||
old_batch = idc.batch(1)
|
||||
ret = _Assemble(ea, line)
|
||||
idc.Batch(old_batch)
|
||||
idc.batch(old_batch)
|
||||
return ret
|
||||
|
||||
def _copy_obj(src, dest, skip_list = None):
|
||||
@@ -711,21 +659,21 @@ class _reg_dtyp_t(object):
|
||||
This class describes a register's number and dtyp.
|
||||
The equal operator is overloaded so that two instances can be tested for equality
|
||||
"""
|
||||
def __init__(self, reg, dtyp):
|
||||
self.reg = reg
|
||||
self.dtyp = dtyp
|
||||
def __init__(self, reg, dtype):
|
||||
self.reg = reg
|
||||
self.dtype = dtype
|
||||
|
||||
def __eq__(self, other):
|
||||
return (self.reg == other.reg) and (self.dtyp == other.dtyp)
|
||||
return (self.reg == other.reg) and (self.dtype == other.dtype)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
class _procregs(object):
|
||||
"""Utility class allowing the users to identify registers in a decoded instruction"""
|
||||
def __getattr__(self, attr):
|
||||
ri = ida_idp.reg_info_t()
|
||||
if not ida_idp.parse_reg_name(attr, ri):
|
||||
if not ida_idp.parse_reg_name(ri, attr):
|
||||
raise AttributeError()
|
||||
r = _reg_dtyp_t(ri.reg, ord(ida_ua.get_dtyp_by_size(ri.size)))
|
||||
r = _reg_dtyp_t(ri.reg, ida_ua.get_dtype_by_size(ri.size))
|
||||
self.__dict__[attr] = r
|
||||
return r
|
||||
|
||||
@@ -735,14 +683,14 @@ class _procregs(object):
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
class _cpu(object):
|
||||
"Simple wrapper around GetRegValue/SetRegValue"
|
||||
"Simple wrapper around get_reg_value/set_reg_value"
|
||||
def __getattr__(self, name):
|
||||
#print "cpu.get(%s)" % name
|
||||
return idc.GetRegValue(name)
|
||||
return idc.get_reg_value(name)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
#print "cpu.set(%s)" % name
|
||||
return idc.SetRegValue(value, name)
|
||||
return idc.set_reg_value(value, name)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
+1455
-1685
File diff suppressed because it is too large
Load Diff
+25
-4
@@ -17,7 +17,26 @@ import time
|
||||
import warnings
|
||||
|
||||
# Prepare sys.path so loading of the shared objects works
|
||||
sys.path.append(os.path.join(sys.executable, IDAPYTHON_DYNLOAD_BASE, "python", "lib", "python2.7", "lib-dynload", IDAPYTHON_DYNLOAD_RELPATH))
|
||||
lib_dynload = os.path.join(
|
||||
sys.executable,
|
||||
IDAPYTHON_DYNLOAD_BASE,
|
||||
"python", "lib", "python2.7", "lib-dynload")
|
||||
|
||||
is_x64 = sys.maxint >= 0x100000000L
|
||||
if is_x64:
|
||||
# x64 python requires our lib_dynload to be added; sys.path seems
|
||||
# to be composed differently than x86 builds.
|
||||
# In addition, we always want our own lib-dynload to come first:
|
||||
# the PyQt (& sip) modules that might have to be loaded, should
|
||||
# be the ones shipped with IDA and not those possibly available
|
||||
# on the system.
|
||||
sys.path.insert(0, os.path.join(lib_dynload, IDAPYTHON_DYNLOAD_RELPATH))
|
||||
sys.path.insert(0, lib_dynload)
|
||||
else:
|
||||
# for non-x64 platforms, make sure everything works as it used to,
|
||||
# by appending our own lib-dynload to sys.argv..
|
||||
sys.path.append(os.path.join(lib_dynload, IDAPYTHON_DYNLOAD_RELPATH))
|
||||
|
||||
try:
|
||||
import ida_idaapi
|
||||
import ida_kernwin
|
||||
@@ -40,7 +59,7 @@ class IDAPythonStdOut:
|
||||
"""
|
||||
def write(self, text):
|
||||
# NB: in case 'text' is Unicode, msg() will decode it
|
||||
# and call umsg() to print it
|
||||
# and call msg() to print it
|
||||
ida_kernwin.msg(text)
|
||||
|
||||
def flush(self):
|
||||
@@ -88,7 +107,7 @@ sys.stdout = sys.stderr = IDAPythonStdOut()
|
||||
import pydoc
|
||||
class IDAPythonHelpPrompter:
|
||||
def readline(self):
|
||||
return ida_kernwin.askstr(0, '', 'Help topic?')
|
||||
return ida_kernwin.ask_str('', 0, 'Help topic?')
|
||||
help = pydoc.Helper(input = IDAPythonHelpPrompter(), output = sys.stdout)
|
||||
|
||||
# Assign a default sys.argv
|
||||
@@ -110,7 +129,9 @@ if not IDAPYTHON_REMOVE_CWD_SYS_PATH:
|
||||
|
||||
if IDAPYTHON_COMPAT_AUTOIMPORT_MODULES:
|
||||
# Import all the required modules
|
||||
from idaapi import Choose, get_user_idadir, cvar, Choose2, Appcall, Form
|
||||
from idaapi import get_user_idadir, cvar, Appcall, Form
|
||||
if IDAPYTHON_COMPAT_695_API:
|
||||
from idaapi import Choose2
|
||||
from idc import *
|
||||
from idautils import *
|
||||
import idaapi
|
||||
|
||||
Reference in New Issue
Block a user