import ctypes, mmap, struct try: _VirtualAlloc = ctypes.windll.kernel32.VirtualAlloc def valloc(size): addr = _VirtualAlloc(0, size, 0x1000, 0x40) if not addr: raise RuntimeError("Cannot allocate RWX memory") return addr except: libc = ctypes.CDLL("libc.so.6") def valloc(size): addr = libc.valloc(size) if not addr or libc.mprotect(addr, size, 0x07): raise RuntimeError("Cannot allocate RWX memory") return addr class RPN_jit: def __init__(self): self.size = mmap.PAGESIZE self.exepage = valloc(self.size) # a single page for execution at the very minimum def emit(self, code): # we will use cdecl function declarations, assume small endianness buffer = "\x55" + "\x8b\xec" + "\x81\xec\xcc\0\0\0" + "\x53" + "\x56" + "\x57" + "\x8d\xbd\x34\xff\xff\xff" def _num(o): try: return int(o) except: return int(o, 16) sp = 0 # stack count, used to clean up stack space for o in code.split(): # valid tokens: integers, +, -, *, /, %; all operators are assumed binary try: o = _num(o) # eax, ecx serves as our registers for most current values, the stack comes later # every time we load in an integer, effectively, we push the content of ecx, bump eax's data into ecx, and then load into eax buffer += "\x51"+"\x91"+"\xb8"+struct.pack("i",o&(0xffffffff)) # don't want to overflow the mov instruction sp+=1 except (ValueError): # eax is first param, ecx is second param, eax is storage, and then we pop into ecx # at the end of the run, eax is the most recently "pushed" item, perfect for /xc3 if sp<2: raise RuntimeError("Stack will underflow.") buffer += "\x03\xc1" if o in ("+", "add", "plus") else "\x2b\xc1" if o in ("-", "sub", "minus") \ else "\x0f\xaf\xc1" if o in ("*", "mul", "mult") else "\x99\xf7\xf9" if o in ("/", "div") \ else "\x99\xf7\xf9\x92" if o in ("%", "mod", "rem") else "\x55" # mod is actually just idiv and xchg edx, eax buffer += "\x59" # pop ecx sp-=1 if not sp: raise RuntimeError("Nothing to compile.") for _ in range(sp): buffer += "\x59" # pop ecx to clear the stack buffer += "\x5f\x5e\x5b\x8b\xe5\x5d\xc3" # pops all register, rebases ebp&esp, and return eax, which contains the last push if not ctypes.memmove(self.exepage, buffer, min(len(buffer), self.size)): raise RuntimeError("Input cannot not fit into memory.") return ctypes.CFUNCTYPE(ctypes.c_int32)(self.exepage) a = RPN_jit() print a.emit("3 10 mod 10 + 0x100 * 100 * 50 -")()