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