ctypes style structs

59 views
Skip to first unread message

James

unread,
Jan 5, 2011, 3:02:15 AM1/5/11
to idapython
I'm relatively new to python. However one thing that i liked was
ctypes style structs. Unfortunately you can't use ctypes structs to
interact with the IDA database, in IDAPython (for obvious reasons).
You can always copy the data to a buffer and use that. But then you
can't use pointers.

Anyway i decided to create this script. I think this style of
structure access would be a good addition to IDAPython. This is proof
of concept code.

import idc

def CreateArrayType(typ, length):
"""Creates a new Array type"""
name = "%.200s_Array_%i" % (typ.__name__, length)
return ArrayType_Type(name, (Array_Type,), {"_length_": length,
"_type_": typ})

def POINTER(typ):
"""Creates a new pointer type """
name = "LP_%s" % typ.__name__
return type(name, (Pointer_Type,), {"_type_": typ})

class ArrayType_Type(type):
"""metatype for the Array Objects"""
def __init__(self, name, bases, attrs):
if self.isArrayType(attrs):
setattr(self, "_size_", self._type_._size_* self._length_)

def isArrayType(self, attrs):
length = attrs.get('_length_')
if length:
return True
return False

def __mul__(self, length):
return CreateArrayType(self, length)

#similar to CData object in ctypes
class IDAData(object):
_address_ = 0
_size_ = 0 #size of block in bytes

#Just return self if we don't have a _type_ to grab
#and update the address for the type
def getfunc(self, address):
self._address_ = address
return self

class Array_Type(IDAData):
def __init__(self):
self.typ = self._type_()

def __getitem__(self, index):
if index < 0 or index >= self._length_:
raise IndexError()

offset = index * self._type_._size_
return self.typ.getfunc(self._address_ + offset)

def __len__(self):
return self._length_



class IDASimpleType(IDAData):
__metaclass__ = ArrayType_Type

class IDAField(object):
"""A data descriptor that sets and returns values
normally and prints a message logging their access.
"""
def __init__(self, name ,offset, typ):
self.name = name
self.offset = offset
self.typ = typ()

def __get__(self, obj, objtype):
if obj:
return self.typ.getfunc(obj._address_ + self.offset)
return self

def __set__(self, obj, val):
raise NotImplementedError()

def __repr__(self):
return "<Field type=%s, ofs=%d>" % (type(self.typ).__name__,
self.offset)


class StructType_Type(type):
def __init__(self, name, bases, attrs):
fields = attrs.get('_fields_')
if fields:
offset = 0
for fieldName, typ in fields:
setattr(IDAStructure, fieldName, IDAField(fieldName,
offset, typ))
offset += typ._size_
#the last offset is also the size
setattr(self, "_size_", offset)

def __mul__(self, length):
return CreateArrayType(self, length)

class IDAStructure(IDAData):
__metaclass__ = StructType_Type
def __init__(self):
pass

def setAddress(self, address):
self._address_ = address

class ida_int(IDASimpleType):
_type_ = "i"
_size_ = 4
def getfunc(self, address):
return idc.Dword(address)

class ida_b(IDASimpleType):
_type_ = "b"
_size_ = 1
def getfunc(self, address):
return idc.Byte(address)

class ida_c(IDASimpleType):
_type_ = "c"
_size_ = 1
def getfunc(self, address):
return chr(idc.Byte(address))

class ida_char_p(IDASimpleType):
_type_ = "z"
_size_ = 4
def getfunc(self, address):
return idc.GetString(idc.Dword(address), -1, idc.ASCSTR_C)


class Pointer_Type(IDASimpleType):
_size_ = 4
def __init__(self):
self.typ = self._type_()

def getfunc(self,address):
addr = idc.Dword(address)
print hex(addr)
return self.typ.getfunc(addr)

class Entry(IDAStructure):
_fields_ = [("name", ida_char_p),
("index", ida_int),
("size", ida_int),
("unk", ida_int*2)]

class Fields(IDAStructure):
_address_ = 0x00C144D0
_fields_ = [("entrys", Entry*6)]

def main():
fields = Fields()
for entry in fields.entrys:
print entry.name, "=", entry.index

main()

elias

unread,
Jan 5, 2011, 6:17:13 AM1/5/11
to idapython
Hello James,

I like your solution.

Nonetheless, we already have something similar to this that allows you
read/write access.

Here is a code snippet from our test suite:

#
-----------------------------------------------------------------------
WRITE_AREA = 0x501000
a = idaapi.Appcall

#
-----------------------------------------------------------------------
# Packs/Unpacks a structure to the database using appcall facilities
def test_pck_idb():
print "%x: ..." % WRITE_AREA
tp = a.typedobj("struct { int a, b; char x[5];};")
o = a.obj(a=16, b=17,x="zzzhi")
if tp.store(o, WRITE_AREA) != 0:
return -1

# Retrieve
ok, r = tp.retrieve(WRITE_AREA)
if not ok:
return -2
if r.a != o.a and r.b != o.b and r.x != o.x:
return -3

return 1

#
-----------------------------------------------------------------------
# Packs/Unpacks a structure to/from a string
def test_pck_bv():
tp = a.typedobj("struct { int a, b; char x[5];};")
o = a.obj(a=16, b=17,x="zzzhi")
ok, packed = tp.store(o)
if not ok:
return -1
print "packed->", repr(packed)
ok, r = tp.retrieve(packed)
if not ok:
return -2

if r.a != o.a and r.b != o.b and r.x != o.x:
return -3

return 1

The idea is to use the typedobj() to store/retrieve typed objects. You
can declare the structure using a C declaration as shown in the
example.

Please let me know if that suits your needs.

James

unread,
Jan 5, 2011, 11:15:21 PM1/5/11
to idapython
Interesting, i must have missed that. I haven't done much with IDA's
appcall functionality. I'll have to more experimenting, but it appears
to do everything that i want :)
Reply all
Reply to author
Forward
0 new messages