Skip to content

Instantly share code, notes, and snippets.

@junron
Last active February 7, 2025 05:14
Show Gist options
  • Save junron/9e203a745095e793f92922c4e208e9ff to your computer and use it in GitHub Desktop.
Save junron/9e203a745095e793f92922c4e208e9ff to your computer and use it in GitHub Desktop.
C Types for Python
import real_ctypes
arr = (char[16])(b'I love Python+C!')
print("A big integer:", (uint128)(arr))
arr2 = (long[:])(arr)
print("Chunked:", arr2)
arr2 += 1
print("Back to bytes, but offset:", bytes((char[:])(arr2)))
import __main__
from ctypes import py_object
import builtins
import typing
class context:
print_style = 'hex'
allow_excess = True
class IntWrapperMeta(type):
def __new__(cls, name, bases, namespace):
int_returning_methods = [
'__add__', '__radd__', '__sub__', '__rsub__',
'__mul__', '__rmul__', '__floordiv__', '__rfloordiv__',
'__mod__', '__rmod__', '__pow__', '__rpow__',
'__lshift__', '__rlshift__', '__rshift__', '__rrshift__',
'__and__', '__rand__', '__or__', '__ror__',
'__xor__', '__rxor__', '__neg__', '__pos__',
'__abs__'
]
for method_name in int_returning_methods:
if method_name not in namespace:
def make_wrapped_method(method_name):
def wrapped_method(self, *args, **kwargs):
original_method = getattr(int, method_name)
args = [arg.val if isinstance(arg, IntTypeVal) else arg for arg in args]
kwargs = {k:arg.val if isinstance(arg, IntTypeVal) else arg for k,arg in kwargs.items()}
result = original_method(self.val, *args, **kwargs)
return IntTypeVal(self.int_type, result)
return wrapped_method
namespace[method_name] = make_wrapped_method(method_name)
return super().__new__(cls, name, bases, namespace)
class IntArray:
def __init__(self, int_type: "IntType", length):
self.int_type = int_type
self.length = length
def __call__(self, arg) -> typing.Any:
if isinstance(arg, IntArrayVal):
arg = arg.to_bytes()
elif isinstance(arg, IntTypeVal):
arg = arg.to_bytes()
elif type(arg) == str:
arg = arg.encode()
assert type(arg) == bytes
sz = self.int_type.size
if self.length is None:
if not context.allow_excess:
assert len(arg) % sz == 0, f"Input length {len(arg)} not divisible by item size {sz}"
self.length = len(arg) // sz
if context.allow_excess:
arg = arg[:sz * self.length]
assert len(arg) == sz * self.length, f"Input length {len(arg)} != required length {sz * self.length}"
arr = []
for i in range(self.length):
data = arg[i*sz:(i+1)*sz]
arr.append(self.int_type.from_bytes(data))
return IntArrayVal(self, arr)
def __repr__(self) -> builtins.str:
return f"{self.int_type}[{self.length}]"
class IntType:
def __init__(self, signed, size, byte_order):
self.signed = signed
self.size = size
self.byte_order = byte_order
def __getitem__(self, index):
if isinstance(index, slice):
index = None
return IntArray(self, index)
def __call__(self, arg):
if type(arg) == int:
return IntTypeVal(self, arg)
if isinstance(arg, IntTypeVal):
arg = arg.to_bytes()
elif isinstance(arg, IntArrayVal):
arg = arg.to_bytes()
if type(arg) == bytes:
return self.from_bytes(arg)
raise TypeError(f"Cannot cast {arg} to {self}")
def from_bytes(self, b):
if context.allow_excess:
b = b[:self.size]
assert len(b) == self.size, f"Input size {len(b)} != integer size {self.size}"
return IntTypeVal(self, int.from_bytes(b, self.byte_order, signed=self.signed))
def __repr__(self):
return f"{'' if self.signed else 'u'}int{self.size*8}{'' if self.byte_order == 'little' else 'be'}"
class IntTypeVal(int, metaclass=IntWrapperMeta):
def __new__(cls, int_type: IntType, val: int):
self = super().__new__(cls, val)
self.int_type = int_type
self.val = self._cast(val)
return self
def __init__(self, int_type: IntType, val: int):
self.int_type = int_type
self.val = self._cast(val)
def _cast(self, val):
bit_count = 8*self.int_type.size
if not self.int_type.signed:
while val < 0:
val += pow(2, bit_count)
return val % pow(2, bit_count)
while val > pow(2, bit_count - 1):
val -= pow(2, bit_count)
while val < -pow(2, bit_count-1):
val += pow(2, bit_count)
return val
def to_bytes(self):
return self.val.to_bytes(self.int_type.size, self.int_type.byte_order, signed=self.int_type.signed)
def __repr__(self):
if context.print_style == 'dec':
return repr(self.val)
elif context.print_style == 'hex':
return hex(self.val)
raise ValueError(f"Invalid print style {context.print_style}")
class IntArrayVal(list):
def __new__(cls, t: IntArray, val: list["IntArrayVal"]):
self = super().__new__(cls, val)
self.t = t
self.val = val
return self
def __init__(self, a, b):
super().__init__(b)
def to_bytes(self):
out = b""
for v in self:
out += v.to_bytes()
return out
def __add__(self, value):
assert type(value) == int
assert value >= 0
assert value < len(self)
new_type = IntArray(self.t.int_type, len(self) - value)
return IntArrayVal(new_type, self[value:])
def __iadd__(self, value):
if type(value) == int:
return self + value
return super().__iadd__(value)
def __repr__(self) -> builtins.str:
return repr(self.t) + " {" + (", ".join(repr(x) for x in self)) + "}"
obase = py_object.from_address(id(__main__.__dict__) + 8)
class fglobals(dict):
__slots__ = ()
def __getitem__(self, key, dict=dict, obase=obase):
mapping = {
"char": "int8",
"short": "int16",
"int": "int32",
"long": "int64",
"byte": "uint8",
"word": "uint16",
"dword": "uint32",
"qword": "uint64",
}
try:
obase.value = dict
return self[key]
except:
try:
return getattr(builtins, key)
except:
k = key
signed = True
byte_order = 'little'
if k.startswith("u"):
k = k[1:]
signed = False
if k in mapping:
k = mapping[k]
if k.startswith("u"):
k = k[1:]
signed = False
if k[:3] == "int":
if k.endswith("be"):
byte_order = 'big'
k = k[:-2]
size = int(k[3:])
assert size % 8 == 0
size //= 8
assert size > 0
return IntType(signed, size, byte_order)
else:
raise NameError(f"Name '{key}' not found!")
finally:
obase.value = __class__
obase.value = fglobals
import real_ctypes
arr = (char[16])(b'I love Python+C!')
int1 = (uint128)(arr)
int2 = (uint128be)(int(int1))
rev = (char[:])(int2)
print(bytes(rev))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment