- 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 -")()