#pragma once #include #include namespace assembler { typedef unsigned char byte; struct Register; struct MemAddress; enum RegCode : byte { EAX = 0, ECX = 1, EDX = 2, EBX = 3, ESP = 4, SIB = 4, EBP = 5, ADDR = 5, ESI = 6, EDI = 7, R8 = 8, R9 = 9, R10 = 10, R12 = 12, R13 = 13, R14 = 14, R15 = 15, //R11 is supervolatile. Can change in between //virtual asm ops (used as a temporary), so be careful //with using it. R11 = 11, //XMM registers have unique numbers so we can recognize them XMM0 = 16, XMM1 = 17, XMM2 = 18, XMM3 = 19, XMM4 = 20, XMM5 = 21, XMM6 = 22, XMM7 = 23, XMM8 = 24, XMM9 = 25, XMM10 = 26, XMM11 = 27, XMM12 = 28, XMM13 = 29, XMM14 = 30, XMM15 = 31, NONE = 0, }; //Floating Point Register codes representing the stack registers on the FPU // The top of the stack is always FPU_0 enum FloatReg : byte { FPU_0 = 0, FPU_1 = 1, FPU_2 = 2, FPU_3 = 3, FPU_4 = 4, FPU_5 = 5, FPU_6 = 6, FPU_7 = 7, }; enum JumpType { Overflow, NotOverflow, Below, Carry = Below, NotBelow, NotCarry = NotBelow, Equal, Zero = Equal, NotEqual, NotZero = NotEqual, NotAbove, Above, Sign, NotSign, Parity, NotParity, Less, GreaterOrEqual, LessOrEqual, Greater, Jump, JumpTypeCount }; //Handles thread safety for the JIT struct CriticalSection { void* pLock; void enter(); void leave(); CriticalSection(); ~CriticalSection(); }; struct AddrPrefix { MemAddress& adr; bool defLong; unsigned char further; AddrPrefix(MemAddress& Adr, bool DefLong, unsigned char Further) : adr(Adr), defLong(DefLong), further(Further) { } }; struct RegPrefix { Register& reg; unsigned short other; bool defLong; RegPrefix(Register& Reg, unsigned short Other, bool DefLong) : reg(Reg), other(Other), defLong(DefLong) { } }; //Stores information about the code page // Generates an executable page in memory when created // Deletes the asssociated page when deleted //Implementation in virtual_asm_.cpp (e.g. virtual_asm_windows.cpp) struct CodePage { void* page; unsigned int size, used, references; bool final; CodePage(unsigned int Size, void* requestedStart = 0); ~CodePage(); void grab(); void drop(); //Call finalize when done writing to the code page to guarantee that it can be executed //No more writing may be done to the allocated pages void finalize(); //Returns the pointer to the first currently unused chunk of the page template T getFunctionPointer() { return reinterpret_cast((byte*)page+used); } byte* getActivePage() const { return (byte*)page+used; } //Marks bytes as used; //future calls to getFunctionPointer() will not reference the location that is being marked as used void markBytesUsed(unsigned int count) { used += count; } //Marks bytes up to
as used void markUsedAddress(void* address) { unsigned newUsed = (unsigned)((byte*)address - (byte*)page); if(newUsed > used && newUsed <= size) used = newUsed; } //Returns the number of bytes not yet allocated to a function unsigned int getFreeSize() const { return size-used; } //Returns the smallest page (in bytes) that can be allocated by a code page (Sizes other than multiples of this size allocate an extra page) static unsigned int getMinimumPageSize(); private: CodePage() {} }; //Stores the code pointer and provides access to various processor-level operations // To work with the processor, create a set of 'Register' instances, each taking the RegCode of the associated register (e.g. Register eax(cpu, EAX)) //Implementation in virtual_asm_.cpp (e.g. virtual_asm_x86.cpp) struct Processor { //Pointer to the location for the next opcode byte* op; byte* pageStart; //The current mode of operation, in bits // e.g. 32 bits for x86, indicating that operations should treat addresses as if they were unsigned integers unsigned bitMode, lastBitMode; //The number of bytes currently on the stack that we are responsible for unsigned stackDepth; //Reserved jump space unsigned jumpSpace; byte* jumpPtr; //Initializes the processor to point to the active page of the code page //Optionally takes a bitMode override (defaults to the same bitMode as the exe) Processor(CodePage& codePage, unsigned defaultBitMode = sizeof(void*)*8 ); //Creates a jump to the new code page, and marks the current address as used on the old code page //Updates output pointer to the new code page's active page void migrate(CodePage& prevPage, CodePage& newPage); //Changes the current bitMode, and stores the previous bitMode void setBitMode(unsigned bits) { lastBitMode = bitMode; bitMode = bits; } //Restores the previous bitMode void resetBitMode() { bitMode = lastBitMode; } //Returns the alignment of the stack (number of bytes a push increments esp) static unsigned pushSize(); //Pushes data to the opcode output template Processor& operator<<(T b) { *(T*)op = b; op += sizeof(T); return *this; } //Pushes bytes representing a memory address to the opcode output template Processor& operator<<(MemAddress addr); //Pushes bytes representing a prefix template Processor& operator<<(AddrPrefix pr); template Processor& operator<<(RegPrefix pr); //Calls the function, passing the arguments specified by 'args' //args is a string like "rrcmrm" which specifies arguments as sourced by a Register*, MemAddres*, or a constant //EBP is invalid during the call void call_cdecl(void* func, const char* args, va_list ap); void call_cdecl(void* func, const char* args, ...); //Use call() in between these to set up a call with an arbitrary function unsigned call_cdecl_args(const char* args, ...); unsigned call_cdecl_args(const char* args, va_list ap); unsigned call_thiscall_args(Register* obj, const char* args, ...); unsigned call_thiscall_args(Register* obj, const char* args, va_list ap); //Prepares for a call to manual call to a cdecl function (Do not use with call_cdecl) // Use before pushing arguments // Invalidates EBP until call_cdecl_end() void call_cdecl_prep(unsigned argBytes); //Ends a manual call to a cdecl function (Do not use with call_cdecl) // Use after returning from the function void call_cdecl_end(unsigned argBytes, bool returnPointer = false); //Note: stdcall is like cdecl, but does not use cdecl_end //Calls the function, passing the arguments specified by 'args' //args is a string like "rrcmrm" which specifies arguments as sourced by a Register*, MemAddres*, or a constant //EBP is invalid during the call void call_stdcall(void* func, const char* args, ...); //To call a thiscall: // cpu.call_thiscall_prep(total argument size) // cpu.push(arguments) // cpu.call_thiscall_this(source of 'this' pointer) // cpu.call(function) // cpu.call_thiscall_end(total argument size) void call_thiscall_prep(unsigned argBytes); void call_thiscall_this(MemAddress address); void call_thiscall_this(Register& reg); void call_thiscall_this_mem(MemAddress address, Register& memreg); void call_thiscall_this_mem(Register& reg, Register& memreg); void call_thiscall_end(unsigned argBytes, bool returnPointer = false); //Calls a function (push code pointer, jump to function) void call(Register& reg); void call(void* func); //Pushes a constant value onto the stack (Pushes are always pushSize() large, values beyond this size are an error) void push(size_t value); //Pops times (Pops are always pushSize() large) void pop(unsigned int count); //Pushes the value of onto the stack void push(Register& reg); //Pops the alue of from the stack void pop(Register& reg); //Get a register corresponding to an argument on 64-bit calling convention unsigned maxIntArgs64(); unsigned maxFloatArgs64(); bool isIntArg64Register(unsigned char number, unsigned char arg); bool isFloatArg64Register(unsigned char number, unsigned char arg); Register intArg64(unsigned char number, unsigned char arg); Register floatArg64(unsigned char number, unsigned char arg); Register intArg64(unsigned char number, unsigned char arg, Register defaultReg); Register floatArg64(unsigned char number, unsigned char arg, Register defaultReg); Register floatReturn64(); Register intReturn64(); //Pushes the memory at
onto the stack (Pushes are always pushSize() large, pushing larger values invokes multiple pushes) void push(MemAddress address); //Pops the value on the stack to the memory at
(Pops are always pushSize() large, popping larger values invokes multiple pops) void pop(MemAddress address); //Prepares a short jump (fewer than approx. 120 bytes in either direction) // Pass the return to a matching end_short_jump void* prep_short_jump(JumpType type); //Ends a short jump void end_short_jump(void* p); //Prepares a large jump (can jump to any location) // Pass the return to a matching end_long_jump void* prep_long_jump(JumpType type); //Ends a large jump void end_long_jump(void* p); //Jumps to void jump(JumpType type, volatile byte* dest); //Jumps to the address in void jump(Register& reg); //Decrements ecx and jumps if it becomes 0; Optionally conditionally jumps based on a Zero/NotZero test void loop(volatile byte* dest, JumpType type = Jump); //Copies from *esi to *edi, and adjusts them both by the data size according to the direction flag void string_copy(unsigned size); //Sets direction flag for string copy void setDirFlag(bool forward); //Returns from a function (pop code pointer, jump there) void ret(); //Triggers a debug break void debug_interrupt(); private: Processor() {} }; //Provides access to the floating point unit's state //Implementation in virtual_asm_.cpp (e.g. virtual_asm_x86.cpp) struct FloatingPointUnit { Processor& cpu; FloatingPointUnit(Processor& CPU); //Clears the FPU's state and registers void init(); //Negates FPU_0 void negate(); //Pushes void load_const_0(); void load_const_1(); //FPU_1 becomes FPU_0 (Pops the fpu stack) void pop(); //Exchanges contents of FPU_n and FPU_0 void exchange(FloatReg floatReg); //Compares FPU_0 to floatReg, setting the CPU's flags according to the values' relation // Optionally pops the fpu stack void compare_toCPU(FloatReg floatReg, bool pop = true); //Pushes the specified data type stored at
onto the FPU stack (becomes FPU_0) void load_float(MemAddress address); void load_dword(MemAddress address); void load_qword(MemAddress address); void load_double(MemAddress address); //Stores the value on FPU_0 to
according to the data type // Optionally pops the fpu stack void store_float(MemAddress address, bool pop = true); void store_dword(MemAddress address, bool pop = true); void store_double(MemAddress address, bool pop = true); //Control words void store_control_word(MemAddress address); void load_control_word(MemAddress address); //Effect: FPU_0 -= void operator-=(FloatReg reg); //Effect: FPU_0 += *(float*)address void add_float(MemAddress address); //Effect: FPU_0 -= *(float*)address void sub_float(MemAddress address); //Effect: FPU_0 *= *(float*)address void mult_float(MemAddress address); //Effect: FPU_0 /= *(float*)address void div_float(MemAddress address); //Effect: FPU_0 += *(double*)address void add_double(MemAddress address); void add_double(FloatReg reg, bool pop = true); //Effect: FPU_0 -= *(double*)address // If Reversed: FPU_0 = *(double*)address - FPU_0 void sub_double(MemAddress address, bool reversed = false); void sub_double(FloatReg reg, bool reversed = false, bool pop = true); //Effect: FPU_0 *= *(double*)address void mult_double(MemAddress address); void mult_double(FloatReg reg, bool pop = true); //Effect: FPU_0 /= *(double*)address // If Reversed: FPU_0 = *(double*)address / FPU_0 void div_double(MemAddress address, bool reversed = false); void div_double(FloatReg reg, bool reversed = false, bool pop = true); }; //Temporary struct that represents an addition to a memory address, with optional scaling struct ScaledIndex { RegCode reg; unsigned char scaleFactor; ScaledIndex(RegCode Reg, unsigned char Scale) : reg(Reg), scaleFactor(Scale) {} }; //Temporary struct that stores data necessary for memory access // Provides operations that can be performed on a memory address //Implementation in virtual_asm_.cpp (e.g. virtual_asm_x86.cpp) struct MemAddress { Processor& cpu; void* absolute_address; int offset; unsigned bitMode; RegCode code; RegCode scaleReg; unsigned char other; unsigned char scaleFactor; bool Float; bool Signed; MemAddress(Processor& CPU, void* address); MemAddress(Processor& CPU, RegCode Code); MemAddress(Processor& CPU, RegCode Code, int Offset); MemAddress operator+(ScaledIndex scale); MemAddress operator+(int Offset); MemAddress operator-(int Offset); void operator++(); void operator--(); void operator-(); void operator~(); void operator+=(unsigned int amount); void operator-=(unsigned int amount); void operator=(unsigned int value); void operator=(void* pointer); void operator=(Register fromReg); void operator&=(unsigned int value); void operator|=(unsigned int value); //Copies memory using an intermediate register void direct_copy(MemAddress address, Register& intermediate); AddrPrefix prefix(unsigned char further = 0, bool defLong = false); }; //Converts a MemAddress from the default unsigned to match the passed type template MemAddress as(MemAddress addr) { addr.bitMode = sizeof(T) * 8; addr.Signed = (T)-1 < (T)0; return addr; } template<> MemAddress as(MemAddress addr); template<> MemAddress as(MemAddress addr); //Structure that provides operations that can be performed on a register // Also provides the means to generate MemAddresses relative to a register via dereference (e.g. *eax+8) //Implementation in virtual_asm_.cpp (e.g. virtual_asm_x86.cpp) struct Register { Processor& cpu; RegCode code; unsigned bitMode; Register(Processor& CPU, RegCode Code); Register(Processor& CPU, RegCode Code, unsigned BitModeOverride); void set_regCode(Register& other) { code = other.code; bitMode = other.bitMode; } unsigned getBitMode() const; unsigned getBitMode(const MemAddress& addr) const; MemAddress operator*() const; ScaledIndex operator*(unsigned char scale) const; //Loads the address pointed to by
into this register void copy_address(MemAddress address); void swap(MemAddress address); void swap(Register& other); void operator<<=(Register& other); void operator>>=(Register& other); void rightshift_logical(Register& other); void operator+=(unsigned int amount); void operator+=(MemAddress address); void operator+=(Register& other); void operator-=(unsigned int amount); void operator-=(Register& other); void operator-=(MemAddress address); void operator*=(MemAddress address); void operator-(); void operator~(); void operator--(); void operator++(); void operator&=(unsigned long long mask); void operator&=(MemAddress address); void operator&=(Register other); void operator^=(MemAddress address); void operator^=(Register& other); void operator|=(MemAddress address); void operator|=(unsigned long long mask); //Copies a smaller data type, retaining the sign void copy_expanding(MemAddress address); //Copies an 8 bit register, leaving 0s in higher bytes void copy_zeroing(Register& other); void operator=(unsigned long long value); void operator=(void* pointer); void operator=(Register other); void operator=(MemAddress addr); void operator==(Register other); void operator==(MemAddress addr); void operator==(unsigned int test); void setIf(JumpType condition); void* setDeferred(unsigned long long def = 0); bool xmm(); bool extended(); RegCode index(); RegPrefix prefix(unsigned short other = 0, bool defaultLong = false); RegPrefix prefix(Register& other, bool defaultLong = false); unsigned char modrm(unsigned short other); unsigned char modrm(Register& other); //Multiplies *address with value, stores the result in this register void multiply_signed(MemAddress address, int value); //Divides {eax,edx} by this register; result in eax, remainder in edx void divide(); void divide_signed(); }; //Converts a MemAddress from the default unsigned to match the passed type template Register as(Register reg) { reg.bitMode = sizeof(T) * 8; return reg; } };