Ctypes on Steroids

Advanced Dynamic Interaction With C

Dynamically Interact With Static

Using Ctypes to Interop Python-C

What is Ctypes for?


from ctypes import *
libc = cdll.LoadLibrary("libc.so.6")
libc.printf("Hello World!\n")
					

But what if some function needs a non trivial type of parameter?

Defining Structs


from ctypes import *
class Foo(Structure):
    _fields_ = [('x', c_uint32), ('y', c_char*20)]
					

foo = Foo()
foo.x = 5
foo.y = "Hello World!"
buffer(foo)[:]
'\x05\x00\x00\x00Hello World!\x00\x00\x00\x00\x00\x00\x00\x00'
					

immutable_buf = _
foo = Foo.from_buffer_copy(immutable_buf)
                    

buf = bytearray(immutable_buf)
foo = Foo.from_buffer(buf)
					

Tell me more about those structs...

Technical Difficulties

Enums


class StructBar(Structure):
    _fields_ = [('foo', c_int)]
                        

class EnumFoo(c_int):
    pass
class StructBar(Structure):
    _fields_ = [('foo', EnumFoo)]
                        

class BaseEnum(c_int):
    __repr__ and friends
    
class EnumFoo(BaseEnum):
    _values_ = {0: "A", 1: "B", 2: "C"}
                        

EnumFoo.A = EnumFoo(0)
EnumFoo.B = EnumFoo(1)
EnumFoo.C = EnumFoo(2)
bar.foo = EnumFoo.B
                        

class BaseEnumMixin(object):
    __repr__ and friends

class EnumFoo(BaseEnumMixin, c_int):
    _values_ = {0: "A", 1: "B", 2: "C"}
                        

EnumFoo.A = EnumFoo(0)
EnumFoo.B = EnumFoo(1)
EnumFoo.C = EnumFoo(2)
bar.foo = EnumFoo.B
                        

Technical Difficulties

Support Name Changes


ALIASES_DICT = {"Foo": 
                    {"x": ("y",)}
               }
@aliased_class
class Foo(Structure):
    _fields_ = [("y", c_uint64)]
					

Technical Difficulties

Mimic GCC packing


typedef struct __attribute__ ((__packed__)) Foo {
        uint32          x
        uint64          y
} Foo;
                        

class Foo(Structure):
    _pack_ = 1
    _fields_ = [("x", c_uint32), ("y", c_uint64)]
                        

typedef struct Foo {
        uint16          x:13;
        uint8           y:3;
} Foo;
                        

class Foo(Structure):
    _fields_ = [("x", c_uint16, 13), ("y", c_uint8, 3)]
                        

typedef struct __attribute__ ((__packed__)) Foo {
        uint64          x:13;
        uint8           y:3;
} Foo;
                        

class Foo(Structure):
    _pack_ = 1
    _fields_ = [(u'_bf_x_0', c_uint8, 8), (u'_bf_x_1', c_uint8, 5),
                (u'_bf_y_0', c_uint8, 3),]
    def _get_x(self):
        return self._bf_x_0 << 0 | self._bf_x_1 << 8
    def _set_x(self, value):
        self._bf_x_0 = (value >> 0) & 255
        self._bf_x_1 = (value >> 8) & 255
    x = property(_get_x, _set_x)
    ...
                        

Plugging in the Juice - Reflection

Plugging in the Juice - Reflection

GDB


(gdb) python print [(f.name, f.bitpos, f.bitsize, str(f.type)) \
                    for f in gdb.lookup_type("XMemUsePrimitive").fields()]
[('static_in_bytes', 0L, 0L, 'uint64'), 
 ('dynamic_in_bytes', 64L, 0L, 'uint64'), 
 ('mmap_in_bytes', 128L, 0L, 'uint64'), 
 ('mmap_hugepg_in_bytes', 192L, 0L, 'uint64'), 
 ('available_pages_in_global_fbpool', 256L, 0L, 'uint32'), 
 ('prealloc_pages_in_global_fbpool', 288L, 0L, 'uint32')]
					

Plugging in the Juice - Reflection

readelf


readelf -Wwi
<1><c2>: Abbrev Number: 9 (DW_TAG_structure_type)
   <c3>   DW_AT_name        : (indirect string, offset: 0x14): _IO_FILE
   <c7>   DW_AT_byte_size   : 216
   ...
<2><cf>: Abbrev Number: 10 (DW_TAG_member)
   <d0>   DW_AT_name        : (indirect string, offset: 0x4c18f): _flags
   <d7>   DW_AT_type        : <0x43>
   ...
<2>...
<2><25d>: Abbrev Number: 0
<1><43>: Abbrev Number: 4 (DW_TAG_base_type)
   <44>   DW_AT_byte_size   : 4
   <46>   DW_AT_name        : int
					

Why did we need all that for?

Well obviously, now we won

What For

Upgrade

Old Struct

typedef struct Foo {
    float x
    float y
    float z
    uint32_t blah
} Foo;
                    
New Struct

typedef struct Foo {
    double x
    double y
    uint64_t blah
} Foo;
                    

What For

Recovery & Exploration


class BaseMetaData(object):
    CTYPE = NotImplemented
    
    @classmethod
    def load(cls, offset):
        data = read(offset, sizeof(self.CTYPE))
        inst = cls.CTYPE.from_buffer_copy(data)
        return cls(inst, offset)
    
    def dump(self):
        write(self.offset, buffer(self.inst)[:])

class FooMetaData(BaseMetaData):
    CTYPE=Foo
                    

What For

Easy communication over socket


register_handler(0, foo) <=== REGISTER_HANDLER(foo, FooRequestStruct)
register_handler(1, bar) <=== REGISTER_HANDLER(bar, BarRequestStruct)
                    

pickle.dump({"foo": (0, reflected.FooRequestStruct),
             "bar": (1, reflected.BarRequestStruct),
             }, apifile)
                    

get_buffer("FooResponseStruct", sizeof(FooResponseStruct))
                    

What For

Basically anything

Questions?