Repository: Boyan-MILANOV/ropgenerator Branch: master Commit: a9cbff211c17 Files: 58 Total size: 783.6 KB Directory structure: gitextract_uvwwhzll/ ├── Dockerfile ├── Makefile ├── README.md ├── bin/ │ └── .gitignore ├── bindings/ │ ├── py_arch.cpp │ ├── py_module.cpp │ ├── py_ropchain.cpp │ ├── py_ropium.cpp │ ├── python_bindings.hpp │ └── utils.cpp ├── cli-tool/ │ └── ropium ├── libropium/ │ ├── arch/ │ │ ├── arch.cpp │ │ ├── archX86.cpp │ │ └── disassembler.cpp │ ├── compiler/ │ │ ├── compiler.cpp │ │ ├── il.cpp │ │ ├── strategy_graph.cpp │ │ ├── strategy_rules.cpp │ │ └── systems.cpp │ ├── database/ │ │ └── database.cpp │ ├── dependencies/ │ │ └── murmur3/ │ │ ├── murmur3.c │ │ └── murmur3.h │ ├── include/ │ │ ├── arch.hpp │ │ ├── assertion.hpp │ │ ├── compiler.hpp │ │ ├── constraint.hpp │ │ ├── database.hpp │ │ ├── disassembler.hpp │ │ ├── exception.hpp │ │ ├── expression.hpp │ │ ├── il.hpp │ │ ├── ir.hpp │ │ ├── ropchain.hpp │ │ ├── ropium.hpp │ │ ├── simplification.hpp │ │ ├── strategy.hpp │ │ ├── symbolic.hpp │ │ ├── systems.hpp │ │ └── utils.hpp │ ├── ir/ │ │ └── ir.cpp │ ├── ropchain/ │ │ ├── assertion.cpp │ │ ├── constraint.cpp │ │ ├── gadget.cpp │ │ └── ropchain.cpp │ ├── symbolic/ │ │ ├── expression.cpp │ │ ├── simplification.cpp │ │ └── symbolic.cpp │ └── utils/ │ └── utils.cpp └── tests/ ├── ressources/ │ └── gadgets.txt ├── test_all.cpp ├── test_compiler.cpp ├── test_database.cpp ├── test_expression.cpp ├── test_gadgets.cpp ├── test_il.cpp ├── test_ir.cpp ├── test_simplification.cpp └── test_strategy.cpp ================================================ FILE CONTENTS ================================================ ================================================ FILE: Dockerfile ================================================ FROM python:3.7.3-slim-stretch RUN pip3 install prompt_toolkit==2.0.9 \ # https://github.com/lief-project/packages/raw/lief-master-latest/pylief-0.9.0.dev.zip \ capstone \ https://github.com/JonathanSalwan/ROPgadget/archive/v5.9.zip \ && apt-get update && apt-get install -y --no-install-recommends \ # g++ \ libmagic1 \ make \ libcapstone-dev \ && rm -rf /var/lib/apt/lists/* /root/.cache COPY . ropium/ # At the expense of a larger image size, recompilation of ropium can be # performed without reinstalling g++ (and thus without an active internet # connection) by uncommenting the previous g++ installation and removing the # apt-get commands of the following RUN RUN apt-get update && apt-get install -y --no-install-recommends g++ \ && cd ropium && make && make test && make install \ && cd .. && rm -rf ropium \ && apt-get -y remove g++ \ && apt-get purge -y --autoremove \ && rm -rf /var/lib/apt/lists/* ENTRYPOINT ["ropium"] ================================================ FILE: Makefile ================================================ CC = gcc CXX = g++ OUTDIR = ./bin LIB_FILE = libropium.so LIB_HEADER_FILE = ropium.hpp BINDINGS_FILE = ropium.so ## Basic default flags CFLAGS ?= CXXFLAGS ?= CXXFLAGS ?= LDFLAGS ?= LDLIBS ?= LDLIBS += -lcapstone ## Flags for debug mode DEBUG ?= 0 ifeq ($(DEBUG), 1) CFLAGS += -g -O0 CXXFLAGS += -g -O0 LDFLAGS += -g else CFLAGS += -O2 CXXFLAGS += -O2 -Wno-narrowing endif ## Bindings BINDINGS ?= 1 ifeq ($(BINDINGS), 1) CXXFLAGS += `python3-config --cflags` -DPYTHON_BINDINGS -Ibindings/python BINDINGS_DIR = ./bindings BINDINGS_SRCS = $(wildcard $(BINDINGS_DIR)/*.cpp) BINDINGS_OBJS = $(BINDINGS_SRCS:.cpp=.o) BINDINGS_RULE = bindings LDLIBS += `python3-config --libs` else BINDINGS_RULE = endif SRCDIR=./libropium ## Final C++ flags CXXFLAGS += -std=c++11 -fpermissive -fPIC -I $(SRCDIR)/include -I $(SRCDIR)/dependencies/murmur3 -Wno-write-strings -Wno-sign-compare -Wno-reorder # Source files SRCS=$(wildcard $(SRCDIR)/symbolic/*.cpp) SRCS+=$(wildcard $(SRCDIR)/ir/*.cpp) SRCS+=$(wildcard $(SRCDIR)/arch/*.cpp) SRCS+=$(wildcard $(SRCDIR)/ropchain/*.cpp) SRCS+=$(wildcard $(SRCDIR)/utils/*.cpp) SRCS+=$(wildcard $(SRCDIR)/database/*.cpp) SRCS+=$(wildcard $(SRCDIR)/compiler/*.cpp) OBJS=$(SRCS:.cpp=.o) TESTDIR = ./tests TESTSRCS = $(wildcard $(TESTDIR)/*.cpp) TESTOBJS = $(TESTSRCS:.cpp=.o) DEPDIR = $(SRCDIR)/dependencies DEPSRCS = $(DEPDIR)/murmur3/murmur3.c DEPOBJS = $(DEPSRCS:.c=.o) INCLUDEDIR = $(SRCDIR)/include # Compile lib and tests all: lib tests $(BINDINGS_RULE) # librop lib: $(OBJS) $(DEPOBJS) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(OUTDIR)/$(LIB_FILE) -shared $(OBJS) $(DEPOBJS) $(LDLIBS) # unit tests tests: $(TESTOBJS) $(OBJS) $(DEPOBJS) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(OUTDIR)/tests $(TESTOBJS) $(OBJS) $(DEPOBJS) $(LDLIBS) # bindings bindings: $(BINDINGS_OBJS) $(OBJS) $(DEPOBJS) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(OUTDIR)/$(BINDINGS_FILE) -shared $(BINDINGS_OBJS) $(OBJS) $(DEPOBJS) $(LDLIBS) # generic %.o : %.cpp $(CXX) $(CXXFLAGS) $(LDFLAGS) -c $< -o $@ $(LDLIBS) %.o : %.c $(CC) $(CFLAGS) $(LDFLAGS) -c $< -o $@ $(LDLIBS) # Installation (assuming Linux system) # If prefix not set, set default ifeq ($(PREFIX),) PREFIX = /usr endif INSTALL_MESSAGE_RULE= # Check if lib and binding files exist ifneq (,$(wildcard ./bin/$(LIB_FILE))) INSTALL_LIB_RULE=install_lib INSTALL_MESSAGE_RULE=print_install_message else INSTALL_LIB_RULE= endif ifneq (,$(wildcard ./bin/$(BINDINGS_FILE))) INSTALL_BINDINGS_RULE=install_bindings PYTHONDIR=$(shell python3 -m site --user-site)/ INSTALL_MESSAGE_RULE=print_install_message else INSTALL_BINDINGS_RULE= endif # make install command install: $(INSTALL_LIB_RULE) $(INSTALL_BINDINGS_RULE) install_cli_tool $(INSTALL_MESSAGE_RULE) install_lib: install -d $(DESTDIR)$(PREFIX)/lib/ install -D $(OUTDIR)/$(LIB_FILE) $(DESTDIR)$(PREFIX)/lib/ install -d $(DESTDIR)$(PREFIX)/include/ install -D $(INCLUDEDIR)/$(LIB_HEADER_FILE) $(DESTDIR)$(PREFIX)/include/ install_bindings: install -d $(PYTHONDIR) install -D $(OUTDIR)/$(BINDINGS_FILE) $(PYTHONDIR) install_cli_tool: install -d $(DESTDIR)$(PREFIX)/bin/ install -D cli-tool/ropium $(DESTDIR)$(PREFIX)/bin/ print_install_message: @echo "\nROPium was successfully installed." # make test command test: $(OUTDIR)/tests # cleaning cleanall: clean clean: rm -f $(OBJS) rm -f $(DEPOBJS) rm -f $(TESTOBJS) rm -f $(BINDINGS_OBJS) rm -f `find . -type f -name "*.gch"` rm -f $(OUTDIR)/* ================================================ FILE: README.md ================================================

>> print(chain.dump('raw')) b'Q\xa8\t\x00\x00\x00\x00\x00\x18\x00\x13\x00\x00\x00\x00\x00@"\x05\x00\x00\x00\x00\x00' ``` Set constraints on ropchains: ```Python # Bytes that should not appear in the ropchain rop.bad_bytes = [0x00, 0x0a, 0x0b] # Register that should not be clobbered by the ropchain rop.keep_regs = ['rsi', 'rdx'] # Enable/Forbid ropchain to dereference registers that might hold invalid addresses # Safe mode is 'True' by default rop.safe_mem = False # Specify which ABI you want to use when calling functions rop.abi = ABI.X86_CDECL # Specify which system to target when doing syscalls rop.os = OS.LINUX ``` # Docker If needed you can run ROPium in a docker container. The container can be generated from the *Dockerfile* as follows: ```bash # Create your docker image (this will take time!) docker build . --tag ropium # Run the image in interactive mode, bind mounting the file to analyze docker run --rm -it -v /FULL/HOST/PATH/FILE:/tmp/FILE:ro ropium (ropium)> load -a X86 /tmp/FILE ``` The actual image is around 200 MB based on a Debian Stretch with a Python 3.7.3 installed. # Contact **Boyan MILANOV** - boyan.milanov (at) hotmail (dot) fr # Licence ROPium is provided under the MIT licence. # Special thanks Contributors: - Docker container support: [migounette](https://github.com/migounette), [clslgrnc](https://github.com/clslgrnc) ROPium uses the following awesome projects: - [capstone](https://github.com/aquynh/capstone) : Disassembly Framework - [ROPgadget](https://github.com/JonathanSalwan/ROPgadget) : Gadget extractor - [prompt-toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit) : Python CLI interface library ================================================ FILE: bin/.gitignore ================================================ # Ignore everything in this directory * # Except this file !.gitignore ================================================ FILE: bindings/py_arch.cpp ================================================ #include "python_bindings.hpp" #include "arch.hpp" #include "compiler.hpp" void init_arch(PyObject* module){ /* ARCH enum */ PyObject* arch_enum = PyDict_New(); PyDict_SetItemString(arch_enum, "X86", PyLong_FromLong((int)ArchType::X86)); PyDict_SetItemString(arch_enum, "X64", PyLong_FromLong((int)ArchType::X64)); PyDict_SetItemString(arch_enum, "ARM32", PyLong_FromLong((int)ArchType::ARM32)); PyDict_SetItemString(arch_enum, "ARM64", PyLong_FromLong((int)ArchType::ARM64)); PyObject* arch_class = create_class(PyUnicode_FromString("ARCH"), PyTuple_New(0), arch_enum); PyModule_AddObject(module, "ARCH", arch_class); /* OS enum */ PyObject* os_enum = PyDict_New(); PyDict_SetItemString(os_enum, "LINUX", PyLong_FromLong((int)System::LINUX)); PyDict_SetItemString(os_enum, "WINDOWS", PyLong_FromLong((int)System::WINDOWS)); PyDict_SetItemString(os_enum, "NONE", PyLong_FromLong((int)System::NONE)); PyObject* os_class = create_class(PyUnicode_FromString("OS"), PyTuple_New(0), os_enum); PyModule_AddObject(module, "OS", os_class); /* ABI enum */ PyObject* abi_enum = PyDict_New(); PyDict_SetItemString(abi_enum, "X86_CDECL", PyLong_FromLong((int)ABI::X86_CDECL)); PyDict_SetItemString(abi_enum, "X86_STDCALL", PyLong_FromLong((int)ABI::X86_STDCALL)); PyDict_SetItemString(abi_enum, "X64_SYSTEM_V", PyLong_FromLong((int)ABI::X64_SYSTEM_V)); PyDict_SetItemString(abi_enum, "X64_MS", PyLong_FromLong((int)ABI::X64_MS)); PyDict_SetItemString(abi_enum, "NONE", PyLong_FromLong((int)ABI::NONE)); PyObject* abi_class = create_class(PyUnicode_FromString("ABI"), PyTuple_New(0), abi_enum); PyModule_AddObject(module, "ABI", abi_class); /* X86 registers enum */ PyObject* x86_enum = PyDict_New(); PyDict_SetItemString(x86_enum, "EAX", PyLong_FromLong(X86_EAX)); PyDict_SetItemString(x86_enum, "EBX", PyLong_FromLong(X86_EBX)); PyDict_SetItemString(x86_enum, "ECX", PyLong_FromLong(X86_ECX)); PyDict_SetItemString(x86_enum, "EDX", PyLong_FromLong(X86_EDX)); PyDict_SetItemString(x86_enum, "EDI", PyLong_FromLong(X86_EDI)); PyDict_SetItemString(x86_enum, "ESI", PyLong_FromLong(X86_ESI)); PyDict_SetItemString(x86_enum, "EBP", PyLong_FromLong(X86_EBP)); PyDict_SetItemString(x86_enum, "ESP", PyLong_FromLong(X86_ESP)); PyDict_SetItemString(x86_enum, "EIP", PyLong_FromLong(X86_EIP)); PyDict_SetItemString(x86_enum, "CS", PyLong_FromLong(X86_CS)); PyDict_SetItemString(x86_enum, "DS", PyLong_FromLong(X86_DS)); PyDict_SetItemString(x86_enum, "ES", PyLong_FromLong(X86_ES)); PyDict_SetItemString(x86_enum, "FS", PyLong_FromLong(X86_FS)); PyDict_SetItemString(x86_enum, "GS", PyLong_FromLong(X86_GS)); PyDict_SetItemString(x86_enum, "SS", PyLong_FromLong(X86_SS)); PyDict_SetItemString(x86_enum, "CF", PyLong_FromLong(X86_CF)); PyDict_SetItemString(x86_enum, "PF", PyLong_FromLong(X86_PF)); PyDict_SetItemString(x86_enum, "AF", PyLong_FromLong(X86_AF)); PyDict_SetItemString(x86_enum, "ZF", PyLong_FromLong(X86_ZF)); PyDict_SetItemString(x86_enum, "SF", PyLong_FromLong(X86_SF)); PyDict_SetItemString(x86_enum, "TF", PyLong_FromLong(X86_TF)); PyDict_SetItemString(x86_enum, "IF", PyLong_FromLong(X86_IF)); PyDict_SetItemString(x86_enum, "DF", PyLong_FromLong(X86_DF)); PyDict_SetItemString(x86_enum, "OF", PyLong_FromLong(X86_OF)); PyDict_SetItemString(x86_enum, "IOPL", PyLong_FromLong(X86_IOPL)); PyDict_SetItemString(x86_enum, "NT", PyLong_FromLong(X86_NT)); PyDict_SetItemString(x86_enum, "RF", PyLong_FromLong(X86_RF)); PyDict_SetItemString(x86_enum, "VM", PyLong_FromLong(X86_VM)); PyDict_SetItemString(x86_enum, "AC", PyLong_FromLong(X86_AC)); PyDict_SetItemString(x86_enum, "VIF", PyLong_FromLong(X86_VIF)); PyDict_SetItemString(x86_enum, "VIP", PyLong_FromLong(X86_VIP)); PyDict_SetItemString(x86_enum, "ID", PyLong_FromLong(X86_ID)); PyDict_SetItemString(x86_enum, "TSC", PyLong_FromLong(X86_TSC)); PyDict_SetItemString(x86_enum, "NB_REGS", PyLong_FromLong(X86_NB_REGS)); PyObject* x86_class = create_class(PyUnicode_FromString("X86"), PyTuple_New(0), arch_enum); PyModule_AddObject(module, "X86", x86_class); /* X64 registers enum */ PyObject* x64_enum = PyDict_New(); PyDict_SetItemString(x64_enum, "RAX", PyLong_FromLong(X64_RAX)); PyDict_SetItemString(x64_enum, "RBX", PyLong_FromLong(X64_RBX)); PyDict_SetItemString(x64_enum, "RCX", PyLong_FromLong(X64_RCX)); PyDict_SetItemString(x64_enum, "RDX", PyLong_FromLong(X64_RDX)); PyDict_SetItemString(x64_enum, "RDI", PyLong_FromLong(X64_RDI)); PyDict_SetItemString(x64_enum, "RSI", PyLong_FromLong(X64_RSI)); PyDict_SetItemString(x64_enum, "RBP", PyLong_FromLong(X64_RBP)); PyDict_SetItemString(x64_enum, "RSP", PyLong_FromLong(X64_RSP)); PyDict_SetItemString(x64_enum, "RIP", PyLong_FromLong(X64_RIP)); PyDict_SetItemString(x64_enum, "R8", PyLong_FromLong(X64_R8)); PyDict_SetItemString(x64_enum, "R9", PyLong_FromLong(X64_R9)); PyDict_SetItemString(x64_enum, "R10", PyLong_FromLong(X64_R10)); PyDict_SetItemString(x64_enum, "R11", PyLong_FromLong(X64_R11)); PyDict_SetItemString(x64_enum, "R12", PyLong_FromLong(X64_R12)); PyDict_SetItemString(x64_enum, "R13", PyLong_FromLong(X64_R13)); PyDict_SetItemString(x64_enum, "R14", PyLong_FromLong(X64_R14)); PyDict_SetItemString(x64_enum, "R15", PyLong_FromLong(X64_R15)); PyDict_SetItemString(x64_enum, "CS", PyLong_FromLong(X64_CS)); PyDict_SetItemString(x64_enum, "DS", PyLong_FromLong(X64_DS)); PyDict_SetItemString(x64_enum, "ES", PyLong_FromLong(X64_ES)); PyDict_SetItemString(x64_enum, "FS", PyLong_FromLong(X64_FS)); PyDict_SetItemString(x64_enum, "GS", PyLong_FromLong(X64_GS)); PyDict_SetItemString(x64_enum, "SS", PyLong_FromLong(X64_SS)); PyDict_SetItemString(x64_enum, "CF", PyLong_FromLong(X64_CF)); PyDict_SetItemString(x64_enum, "PF", PyLong_FromLong(X64_PF)); PyDict_SetItemString(x64_enum, "AF", PyLong_FromLong(X64_AF)); PyDict_SetItemString(x64_enum, "ZF", PyLong_FromLong(X64_ZF)); PyDict_SetItemString(x64_enum, "SF", PyLong_FromLong(X64_SF)); PyDict_SetItemString(x64_enum, "TF", PyLong_FromLong(X64_TF)); PyDict_SetItemString(x64_enum, "IF", PyLong_FromLong(X64_IF)); PyDict_SetItemString(x64_enum, "DF", PyLong_FromLong(X64_DF)); PyDict_SetItemString(x64_enum, "OF", PyLong_FromLong(X64_OF)); PyDict_SetItemString(x64_enum, "IOPL", PyLong_FromLong(X64_IOPL)); PyDict_SetItemString(x64_enum, "NT", PyLong_FromLong(X64_NT)); PyDict_SetItemString(x64_enum, "RF", PyLong_FromLong(X64_RF)); PyDict_SetItemString(x64_enum, "VM", PyLong_FromLong(X64_VM)); PyDict_SetItemString(x64_enum, "AC", PyLong_FromLong(X64_AC)); PyDict_SetItemString(x64_enum, "VIF", PyLong_FromLong(X64_VIF)); PyDict_SetItemString(x64_enum, "VIP", PyLong_FromLong(X64_VIP)); PyDict_SetItemString(x64_enum, "ID", PyLong_FromLong(X64_ID)); PyDict_SetItemString(x64_enum, "TSC", PyLong_FromLong(X64_TSC)); PyDict_SetItemString(x64_enum, "NB_REGS", PyLong_FromLong(X64_NB_REGS)); PyObject* x64_class = create_class(PyUnicode_FromString("X64"), PyTuple_New(0), arch_enum); PyModule_AddObject(module, "X64", x64_class); }; ================================================ FILE: bindings/py_module.cpp ================================================ #include "Python.h" #include "python_bindings.hpp" /* Module methods */ PyMethodDef module_methods[] = { {"ROPium", (PyCFunction)ropium_ROPium, METH_VARARGS, "Create a new ROPium instance"}, {NULL} }; /* Module information */ PyModuleDef ropium_module_def = { PyModuleDef_HEAD_INIT, "ropium", nullptr, -1, // m_size module_methods, // m_methods nullptr, // m_slots nullptr, // m_traverse nullptr, // m_clear nullptr // m_free }; PyMODINIT_FUNC PyInit_ropium(){ Py_Initialize(); PyObject* module = PyModule_Create(&ropium_module_def); init_arch(module); init_ropchain(module); return module; } ================================================ FILE: bindings/py_ropchain.cpp ================================================ #include "python_bindings.hpp" #include/* ------------------------------------- * ROPChain object * ------------------------------------ */ static void ropchain_dealloc(PyObject* self){ delete ((ropchain_Object*)self)->ropchain; ((ropchain_Object*)self)->ropchain = nullptr; Py_TYPE(self)->tp_free((PyObject *)self); }; static PyObject* ropchain_str(PyObject* self){ stringstream ss; ss << *(as_ropchain_object(self).ropchain); return PyUnicode_FromString(ss.str().c_str()); }; static int ropchain_print(PyObject* self){ std::cout << *(as_ropchain_object(self).ropchain); return 0; }; static PyObject* ropchain_repr(PyObject* self){ return ropchain_str(self); }; static PyObject* ropchain_dump(PyObject* self, PyObject* args, PyObject* keywords){ char* arg = "pretty"; // Default string fmt; stringstream ss; int color = 1; vector raw; PyObject* res; char* tab = ""; char* keywd[] = {"", "tab", "color", NULL}; if( ! PyArg_ParseTupleAndKeywords(args, keywords, "|ssp", keywd, &arg, &tab, &color)){ return NULL; } fmt = string(arg); if(!color){ disable_colors(); } if( fmt == "pretty" ){ as_ropchain_object(self).ropchain->print_pretty(ss, string(tab)); res = PyUnicode_FromString(ss.str().c_str()); }else if( fmt == "python" ){ as_ropchain_object(self).ropchain->print_python(ss, string(tab)); res = PyUnicode_FromString(ss.str().c_str()); }else if( fmt == "raw" ){ as_ropchain_object(self).ropchain->dump_raw(raw); // Translate vector into python bytes res = PyBytes_FromStringAndSize((char*)raw.data(), raw.size()); }else return PyErr_Format(PyExc_ValueError, "Unknown dump format: %s", arg); enable_colors(); if( res == NULL ){ return PyErr_Format(PyExc_RuntimeError, "%s", "Failed to dump ropchain"); } return res; } static PyMethodDef ropchain_methods[] = { {"dump", (PyCFunction)ropchain_dump, METH_VARARGS | METH_KEYWORDS, "dump(fmt='pretty', tab='', color=True) \nDump the ropchain in various formats. Available formats: 'pretty', 'python', 'raw'"}, {NULL, NULL, 0, NULL} }; static PyMemberDef ropchain_members[] = { {NULL} }; static PyNumberMethods ropchain_operators; // Empty PyNumberMethods, will be filled in the init function /* Type description for python Expr objects */ PyTypeObject ropchain_Type = { PyVarObject_HEAD_INIT(NULL, 0) "ropchain", /* tp_name */ sizeof(ropchain_Object), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)ropchain_dealloc, /* tp_dealloc */ (printfunc)ropchain_print, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ ropchain_repr, /* tp_repr */ &ropchain_operators, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ ropchain_str, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ "ROPChain object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ ropchain_methods, /* tp_methods */ ropchain_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyObject* get_ropchain_Type(){ return (PyObject*)&ropchain_Type; }; /* Constructor */ PyObject* Pyropchain_FromROPChain(ROPChain* chain){ ropchain_Object* object; // Create object PyType_Ready(&ropchain_Type); object = PyObject_New(ropchain_Object, &ropchain_Type); if( object != nullptr ){ object->ropchain = chain; } return (PyObject*)object; } // Adding two ropchains /* Number methods & Various Constructors */ static PyObject* ropchain_nb_add(PyObject* self, PyObject *other){ if( ! PyObject_IsInstance(other, (PyObject*)&(ropchain_Type))){ return PyErr_Format(PyExc_TypeError, "Mismatching types for operator '+'"); } ROPChain * rop = new ROPChain(as_ropchain_object(self).ropchain->arch); rop->add_chain(*(as_ropchain_object(self).ropchain)); rop->add_chain(*(as_ropchain_object(other).ropchain)); return Pyropchain_FromROPChain(rop); } /* ------------------------------------- * Init function * ------------------------------------ */ void init_ropchain(PyObject* module){ /* Add number operators to ropchain */ ropchain_operators.nb_add = ropchain_nb_add; } ================================================ FILE: bindings/py_ropium.cpp ================================================ #include "python_bindings.hpp" #include #include /* ------------------------------------- * ROPium object * ------------------------------------ */ static void ROPium_dealloc(PyObject* self){ delete ((ROPium_Object*)self)->compiler; ((ROPium_Object*)self)->compiler = nullptr; delete ((ROPium_Object*)self)->arch; ((ROPium_Object*)self)->arch = nullptr; delete ((ROPium_Object*)self)->gadget_db; ((ROPium_Object*)self)->gadget_db = nullptr; delete ((ROPium_Object*)self)->constraint; ((ROPium_Object*)self)->constraint = nullptr; Py_TYPE(self)->tp_free((PyObject *)self); }; static PyObject* ROPium_load(PyObject* self, PyObject* args){ const char* filename; int filename_len; int filenum = 0; stringstream ss; string gadget_file; string ropgadget_tmp_file; int max_filenum = 0x7fffffff; vector * raw = nullptr; if( ! PyArg_ParseTuple(args, "s#", &filename, &filename_len) ){ return NULL; } // Get available file to dump gadgets for( filenum = 0; filenum < max_filenum; filenum++){ ss.str(""); ss << ".ropium_raw_gadgets." << filenum; gadget_file = ss.str(); // Test if file exists std::ifstream fin(gadget_file); if( !fin ){ break; // File doesn't exist }else{ fin.close(); // Try next filenum } } if( filenum == max_filenum ){ return PyErr_Format(PyExc_RuntimeError, "Couldn't create new file where to dump gadgets"); } ss.str(""); ss << ".ropgadget_output." << filenum, ropgadget_tmp_file = ss.str(); try{ // Try to load binary and get gadgets using ROPgadget for now if( ! ropgadget_to_file(gadget_file, ropgadget_tmp_file, filename)){ return PyErr_Format(PyExc_RuntimeError, "Couldn't analyse binary with ROPgadget"); } raw = raw_gadgets_from_file(gadget_file); as_ropium_object(self).gadget_db->analyse_raw_gadgets(*raw, as_ropium_object(self).arch); delete raw; raw = nullptr; remove(gadget_file.c_str()); remove(ropgadget_tmp_file.c_str()); }catch(runtime_exception& e){ return PyErr_Format(PyExc_RuntimeError, "%s", e.what()); } Py_RETURN_NONE; }; static PyObject* ROPium_compile(PyObject* self, PyObject* args){ const char* query; int query_len; ROPChain* ropchain; if( ! PyArg_ParseTuple(args, "s#", &query, &query_len) ){ return NULL; } try{ ropchain = as_ropium_object(self).compiler->compile( string(query, query_len), as_ropium_object(self).constraint, as_ropium_object(self).abi, as_ropium_object(self).system); if( ropchain ){ return Pyropchain_FromROPChain(ropchain); } }catch(il_exception& e){ return PyErr_Format(PyExc_ValueError, "%s", e.what()); }catch(runtime_exception& e){ return PyErr_Format(PyExc_RuntimeError, "%s", e.what()); }catch(compiler_exception& e){ return PyErr_Format(PyExc_RuntimeError, "%s", e.what()); } Py_RETURN_NONE; }; static PyMethodDef ROPium_methods[] = { {"load", (PyCFunction)ROPium_load, METH_VARARGS, "load( ) \nLoad and analyse gadgets from a binary"}, {"compile", (PyCFunction)ROPium_compile, METH_VARARGS, "compile( ) \nCompile a semantic query into a ropchain"}, {NULL, NULL, 0, NULL} }; // Get/Set Attributes static PyObject* ROPium_get_bad_bytes(PyObject* self, void* closure){ PyObject* list; list = PyList_New(0); if( list == NULL ){ return PyErr_Format(PyExc_RuntimeError, "%s", "Failed to create new python list"); } // Add bad bytes to list for (int i = 0; i < 0x100; i++){ if( !as_ropium_object(self).constraint->bad_bytes.is_valid_byte(i) ){ if( PyList_Append(list, PyLong_FromLong(i)) == -1){ return PyErr_Format(PyExc_RuntimeError, "%s", "Failed to add bad byte to python list"); } } } return list; } static int ROPium_set_bad_bytes(PyObject* self, PyObject* list, void* closure){ PyObject *item; Py_ssize_t size; if( ! PyList_Check(list)){ PyErr_SetString(PyExc_RuntimeError, "Expected a list of integers"); return -1; } size = PyList_Size(list); // Clear previous bad bytes as_ropium_object(self).constraint->bad_bytes.clear(); // Add new bad bytes for( int i = 0; i < size; i++){ item = PyList_GetItem(list, i); if( item == NULL ){ PyErr_SetString(PyExc_RuntimeError, "Error getting item in supplied list"); return -1; } if( ! PyLong_Check(item) || PyLong_AsUnsignedLong(item) > 0xff ){ PyErr_SetString(PyExc_ValueError, "Bad bytes list has incorrect element(s)"); return -1; } // Add bad byte as_ropium_object(self).constraint->bad_bytes.add_bad_byte(PyLong_AsUnsignedLong(item)); } return 0; } static PyObject* ROPium_get_safe_mem(PyObject* self, void* closure){ if( as_ropium_object(self).constraint->mem_safety.is_enforced()) Py_RETURN_TRUE; else Py_RETURN_FALSE; } static int ROPium_set_safe_mem(PyObject* self, PyObject* val, void* closure){ if( ! PyBool_Check(val)){ PyErr_SetString(PyExc_RuntimeError, "Excepted a boolean value"); return -1; } if( val == Py_True ){ as_ropium_object(self).constraint->mem_safety.force_safe(); }else{ as_ropium_object(self).constraint->mem_safety.enable_unsafe(); } return 0; } static PyObject* ROPium_get_keep_regs(PyObject* self, void* closure){ PyObject* list; list = PyList_New(0); if( list == NULL ){ return PyErr_Format(PyExc_RuntimeError, "%s", "Failed to create new python list"); } // Add bad bytes to list for (int i = 0; i < as_ropium_object(self).arch->nb_regs; i++){ if( as_ropium_object(self).constraint->keep_regs.is_kept(i) ){ if( PyList_Append(list, PyUnicode_FromString( as_ropium_object(self).arch->reg_name(i).c_str())) == -1){ return PyErr_Format(PyExc_RuntimeError, "%s", "Failed to add register name to python list"); } } } return list; } static int ROPium_set_keep_regs(PyObject* self, PyObject* list, void* closure){ PyObject *item; Py_ssize_t size; string name; int reg_num; if( ! PyList_Check(list)){ PyErr_SetString(PyExc_RuntimeError, "Expected a list of str"); return -1; } size = PyList_Size(list); // Clear previous regs as_ropium_object(self).constraint->keep_regs.clear(); // Add new regs for( int i = 0; i < size; i++){ item = PyList_GetItem(list, i); if( item == NULL ){ PyErr_SetString(PyExc_RuntimeError, "Error getting item in supplied list"); return -1; } if( ! PyUnicode_Check(item) ){ PyErr_SetString(PyExc_ValueError, "Registers must be specified as strings: 'eax', 'ebx', ..."); return -1; } name = string((char*)PyUnicode_DATA(item)); try{ reg_num = as_ropium_object(self).arch->reg_num(name); }catch(runtime_exception& e){ PyErr_Format(PyExc_ValueError, "Invalid register: %s", name.c_str()); return -1; } // Add keep reg as_ropium_object(self).constraint->keep_regs.add_keep_reg(reg_num); } return 0; } static PyObject* ROPium_get_arch(PyObject* self, void* closure){ return PyLong_FromLong((int)(as_ropium_object(self).arch->type)); } static PyObject* ROPium_get_abi(PyObject* self, void* closure){ return PyLong_FromLong((int)(as_ropium_object(self).abi)); } static int ROPium_set_abi(PyObject* self, PyObject* val, void* closure){ int abi; if( ! PyLong_Check(val)){ PyErr_SetString(PyExc_RuntimeError, "Argument should be a ABI.* enum value"); return -1; } abi = PyLong_AsLong(val); as_ropium_object(self).abi = (ABI)abi; return 0; } static PyObject* ROPium_get_os(PyObject* self, void* closure){ return PyLong_FromLong((int)(as_ropium_object(self).system)); } static int ROPium_set_os(PyObject* self, PyObject* val, void* closure){ int system; if( ! PyLong_Check(val)){ PyErr_SetString(PyExc_RuntimeError, "Argument should be a OS.* enum value"); return -1; } system = PyLong_AsLong(val); as_ropium_object(self).system = (System)system; return 0; } static PyGetSetDef ROPium_getset[] = { {"bad_bytes", ROPium_get_bad_bytes, ROPium_set_bad_bytes, "Bad bytes that must not occur in the ropchains", NULL}, {"keep_regs", ROPium_get_keep_regs, ROPium_set_keep_regs, "Registers that should not be clobbered by the ropchains", NULL}, {"safe_mem", ROPium_get_safe_mem, ROPium_set_safe_mem, "Indicates whether ropchains can contain gadgets that perform potentially unsafe register dereferencing", NULL}, {"arch", ROPium_get_arch, NULL, "Architecture type", NULL}, {"abi", ROPium_get_abi, ROPium_set_abi, "ABI to use when calling functions", NULL}, {"os", ROPium_get_os, ROPium_set_os, "OS to target when doing syscalls", NULL}, {NULL} }; static PyMemberDef ROPium_members[] = { {NULL} }; /* Type description for python Expr objects */ PyTypeObject ROPium_Type = { PyVarObject_HEAD_INIT(NULL, 0) "ROPium", /* tp_name */ sizeof(ROPium_Object), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)ROPium_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ "ROPium: automatic ropchain finder", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ ROPium_methods, /* tp_methods */ ROPium_members, /* tp_members */ ROPium_getset, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyObject* get_ROPium_Type(){ return (PyObject*)&ROPium_Type; }; /* Constructor */ PyObject* ropium_ROPium(PyObject* self, PyObject* args){ ROPium_Object* object; int arch; // Parse arguments if( ! PyArg_ParseTuple(args, "i", &arch) ){ return NULL; } // Create object try{ PyType_Ready(&ROPium_Type); object = PyObject_New(ROPium_Object, &ROPium_Type); if( object != nullptr ){ // Set constraint object->constraint = new Constraint(); // Set architecture switch ( (ArchType)arch){ case ArchType::X86: as_ropium_object(object).arch = new ArchX86(); break; case ArchType::X64: as_ropium_object(object).arch = new ArchX64(); break; default: return PyErr_Format(PyExc_ValueError, "This architecture isn't supported yet"); } // Set gadget db as_ropium_object(object).gadget_db = new GadgetDB(); // Set compiler as_ropium_object(object).compiler = new ROPCompiler(object->arch, (object->gadget_db)); as_ropium_object(object).abi = ABI::NONE; as_ropium_object(object).system = System::NONE; } }catch(runtime_exception& e){ return PyErr_Format(PyExc_RuntimeError, "%s", e.what()); } return (PyObject*)object; } ================================================ FILE: bindings/python_bindings.hpp ================================================ #ifndef PYTHON_BINDINGS_INCLUDE_H #define PYTHON_BINDINGS_INCLUDE_H #include "Python.h" #include "structmember.h" #include "exception.hpp" #include "arch.hpp" #include "database.hpp" #include "compiler.hpp" #include "systems.hpp" /* ------------------------------------------------- * Utils * ------------------------------------------------- */ PyObject* create_class(PyObject* name, PyObject* bases, PyObject* dict); /* -------------------------------------------------- * Arch * -------------------------------------------------- */ void init_arch(PyObject* module); /* -------------------------------------------------- * Ropium * -------------------------------------------------- */ typedef struct{ PyObject_HEAD Arch* arch; GadgetDB* gadget_db; ROPCompiler* compiler; Constraint* constraint; ABI abi; System system; } ROPium_Object; PyObject* get_ROPium_Type(); PyObject* ropium_ROPium(PyObject* self, PyObject* args); #define as_ropium_object(x) (*((ROPium_Object*)x)) /* -------------------------------------------------- * ROPChain * -------------------------------------------------- */ void init_ropchain(PyObject* module); typedef struct{ PyObject_HEAD ROPChain* ropchain; } ropchain_Object; PyObject* get_ropchain_Type(); PyObject* Pyropchain_FromROPChain(ROPChain* chain); #define as_ropchain_object(x) (*((ropchain_Object*)x)) #endif ================================================ FILE: bindings/utils.cpp ================================================ #include "python_bindings.hpp" PyObject* create_class(PyObject* name, PyObject* bases, PyObject* dict){ PyObject* res = PyObject_CallFunctionObjArgs((PyObject*)&PyType_Type, name, bases, dict, NULL); Py_CLEAR(name); Py_CLEAR(bases); Py_CLEAR(dict); return res; } ================================================ FILE: cli-tool/ropium ================================================ #!/usr/bin/env python3 from ropium import * from prompt_toolkit import PromptSession, ANSI import os # Colors and util functions MAIN_COLOR_ANSI = '\033[92m' # Default color ERROR_COLOR_ANSI = '\033[91m' BOLD_COLOR_ANSI = '\033[1m' WARNING_COLOR_ANSI = '\033[93m' SPECIAL_COLOR_ANSI = '\033[93m' END_COLOR_ANSI = '\033[0m' def str_bold(msg): return BOLD_COLOR_ANSI + msg + END_COLOR_ANSI def str_error(msg): return ERROR_COLOR_ANSI + msg + END_COLOR_ANSI def str_main(msg): return MAIN_COLOR_ANSI + msg + END_COLOR_ANSI def str_warning(msg): return WARNING_COLOR_ANSI + msg + END_COLOR_ANSI def str_special(msg): return SPECIAL_COLOR_ANSI + msg + END_COLOR_ANSI def error(msg, skip=False): if skip: print('') print("\t[" + str_bold(str_error("!")) + "] " + msg) def info(msg): print("\t[" + str_main("+") + "] " + msg) def warning(msg, skip=False): if skip: print('') print("\t[" + str_bold(str_warning("!")) + "] " + msg) compiler = None bad_bytes = [] keep_regs = [] safe_mem = True abi = ABI.NONE system = OS.NONE start_msg = "\n" + str_bold("ROPium") + " - v3.2\n" # Commands CMD_HELP = "help" CMD_LOAD = "load" CMD_FIND = "find" CMD_BADBYTES = "badbytes" CMD_KEEPREGS = "keepregs" CMD_ABI = "abi" CMD_OS = "os" CMD_SAFEMEM = "safemem" CMD_EXIT = "exit" # Arch correspondance str_to_arch = {"X86":ARCH.X86, "X64":ARCH.X64} # Main function def main(): print(start_msg) finish = False promptSession = PromptSession(ANSI(u"("+ str_main(u"ropium") +u")> ")) while( not finish ): try: user_input = promptSession.prompt() args = user_input.split() argslen = len(args) if( argslen > 0 ): command = args[0] else: command = None continue if( command == CMD_LOAD ): try: load(args[1:]) except LoadException as e: error(str(e), skip=True) elif( command == CMD_EXIT ): finish = True elif( command == CMD_HELP ): if len(args) > 1: if args[1] == "load": print(load_help) elif args[1] == "find": print(find_help) elif args[1] == "badbytes": print(badbytes_help) elif args[1] == "keepregs": print(keepregs_help) elif args[1] == "safemem": print(safemem_help) elif args[1] == "abi": print(abi_help) else: print(main_help) else: print(main_help) elif( command == CMD_FIND ): try: find(args[1:]) except FindException as e: error(str(e), skip=True) elif( command == CMD_BADBYTES ): try: badbytes(args[1:]) except ContextException as e: error(str(e), skip=True) elif( command == CMD_KEEPREGS ): try: keepregs(args[1:]) except ContextException as e: error(str(e), skip=True) elif( command == CMD_SAFEMEM ): try: safemem(args[1:]) except ContextException as e: error(str(e), skip=True) elif( command == CMD_ABI ): try: cmd_abi(args[1:]) except ContextException as e: error(str(e), skip=True) elif( command == CMD_OS ): try: cmd_os(args[1:]) except ContextException as e: error(str(e), skip=True) else: error(f"Unknown command '{command}' (type 'help' for help)", skip=True) print('') except KeyboardInterrupt: pass except EOFError: finish = True print('Thanks for using ROPium !') return # Load command class LoadException(Exception): pass def load(args): global compiler OPTIONS_ARCH = ['-a', '--arch'] OPTIONS_HELP = ['-h', '--help'] seen_arch = False seen_filename = False arch = None filenames = [] compiler_was_none = False # Parse arguments if not args: print(load_help) return i = 0 while i < len(args): if args[i] in OPTIONS_ARCH: if seen_arch: raise LoadException(f"Option '{args[i]}' can be used only one time") seen_arch = True if( i+1 == len(args)): raise LoadException(f"Missing argument after {args[i]}") else: arch = args[i+1] i += 2 elif args[i] in OPTIONS_HELP: print(load_help) return else: filenames.append(args[i]) i += 1 # Check arguments if not filenames: raise LoadException("Missing filename") if not arch and not compiler: raise LoadException("Missing architecture") if arch and (arch not in str_to_arch): raise LoadException(f"Unsupported architecture: {arch}") # Instanciate compiler if not already if compiler is None: compiler = ROPium(str_to_arch[arch]) compiler_was_none = True elif compiler and arch and (str_to_arch[arch] != compiler.arch): raise LoadException(f"Already working on a different architecture than '{arch}'") loaded_at_least_one = False print('') # So it's moar pretty for f in filenames: # Test if the file exists if not os.path.isfile(f): warning(f"Skipped: {f} (file doesn't exist)") else: compiler.load(f) info(f"Loaded: {f}") loaded_at_least_one = True if compiler_was_none and not loaded_at_least_one: compiler = None # Find command class FindException(Exception): pass def find(args): global compiler global bad_bytes global keep_regs global safemem global abi global system if not compiler: raise FindException("You must load a binary before finding ropchains") query = "".join(args) compiler.bad_bytes = bad_bytes compiler.keep_regs = keep_regs compiler.safe_mem = safe_mem compiler.abi = abi compiler.os = system try: ropchain = compiler.compile(query) except ValueError as e: raise FindException(str(e)) except RuntimeError as e: raise FindException(str(e)) if ropchain: print('') print(ropchain.dump(tab="\t")) else: print("\n\tNo ROPChain found.") # Badbytes command class ContextException(Exception): pass def badbytes(args): global compiler if not args: print(badbytes_help) return subcommand = args[0] if subcommand == "set": set_badbytes(args[1:]) elif subcommand == "reset": reset_badbytes(args[1:]) else: raise ContextException(f"Unsupported action '{subcommand}'") def str_to_byte(s): try: return int(s, 10) except: try: return int(s, 16) except: return None def set_badbytes(args): global bad_bytes new_bad = [] for arg in args: bad = str_to_byte(arg) if bad is None or bad > 0xff: raise ContextException(f"'{arg}' is not a valid byte") new_bad.append(bad) bad_bytes = new_bad def reset_badbytes(args): global bad_bytes bad_bytes = [] # Keppregs command def keepregs(args): global compiler if not args: print(keepregs_help) return subcommand = args[0] if subcommand == "set": set_keepregs(args[1:]) elif subcommand == "reset": reset_keepregs(args[1:]) else: raise ContextException(f"Unsupported action '{subcommand}'") reg_map = { "eax":X86.EAX, "ebx":X86.EBX, "ecx":X86.ECX, "edx":X86.EDX, "esi":X86.ESI, "edi":X86.EDI, "esp":X86.ESP, "ebp":X86.EBP, "eip":X86.EIP, "rax":X64.RAX, "rbx":X64.RBX, "rcx":X64.RBX, "rdx":X64.RDX, "rdi":X64.RDI, "rsi":X64.RSI, "rsp":X64.RSP, "rbp":X64.RBP, "rip":X64.RIP, "r8":X64.R8, "r9":X64.R9, "r10":X64.R10, "r11":X64.R11, "r12":X64.R12, "r13":X64.R13, "r14":X64.R14, "r15":X64.R15 } def str_to_reg(s): if s in reg_map: return reg_map[s] else: return None def set_keepregs(args): global keep_regs new_keep = [] for arg in args: reg = str_to_reg(arg) if reg is None: raise ContextException(f"Register '{arg}' is not supported for 'keepregs'") new_keep.append(arg) keep_regs = new_keep def reset_keepregs(args): global keep_regs keep_regs = [] # safemem command def safemem(args): global compiler global safe_mem if not args: print(safemem_help) return subcommand = args[0] if subcommand == "set": safe_mem = True elif subcommand == "unset": safe_mem = False else: raise ContextException(f"Unsupported action '{subcommand}'") if len(args) > 1: args_str = ' '.join(args[1:]) warning(f"Extra arguments ignored: '{args_str}'", skip=True) # ABI Command str_to_abi = { "X86_CDECL":ABI.X86_CDECL, "X86_STDCALL":ABI.X86_STDCALL, "X64_SYSTEM_V":ABI.X64_SYSTEM_V, "X64_MS":ABI.X64_MS } def cmd_abi(args): global compiler if not args: print(abi_help) return subcommand = args[0] if subcommand == "set": set_abi(args[1:]) else: raise ContextException(f"Unsupported action '{subcommand}'") def set_abi(args): global abi if not args: raise ContextException(f"Missing ABI argument") if args[0] not in str_to_abi: raise ContextException(f"Unsupported ABI: '{args[0]}'") else: abi = str_to_abi[args[0]] if len(args) > 1: extra_args = ' '.join(args[1:]) warning(f"Extra arguments ignored: '{extra_args}'", skip=True) # OS Command str_to_os = { "LINUX":OS.LINUX, "WINDOWS":OS.WINDOWS } def cmd_os(args): global compiler if not args: print(os_help) return subcommand = args[0] if subcommand == "set": set_os(args[1:]) else: raise ContextException(f"Unsupported action '{subcommand}'") def set_os(args): global system if not args: raise ContextException(f"Missing OS argument") if args[0] not in str_to_os: raise ContextException(f"Unsupported Operating System: '{args[0]}'") else: system = str_to_os[args[0]] if len(args) > 1: extra_args = ' '.join(args[1:]) warning(f"Extra arguments ignored: '{extra_args}'", skip=True) # Help strings main_help = str_main(str_bold('\n\tMain Commands')) main_help += str_special("\n\t(For more info about a command type 'help ')") main_help += '\n\n\t' + str_bold(CMD_LOAD) + ': \t\tload gadgets from a binary file' main_help += '\n\t' + str_bold(CMD_FIND) + ': \t\tFind ropchains using semantic queries' main_help += '\n\n\t' + str_bold(CMD_BADBYTES) + ': \tSet bad bytes to be avoided in ropchains' main_help += '\n\t' + str_bold(CMD_KEEPREGS) + ': \tSet registers that must not be clobbered' main_help += '\n\t' + str_bold(CMD_SAFEMEM) + ': \tEnable/Disable the use of unsafe gadgets' main_help += '\n\t' + str_bold(CMD_ABI) + ': \t\tSpecify the ABI to use when calling functions' main_help += '\n\t' + str_bold(CMD_OS) + ': \t\tSpecify the OS to target when doing syscalls' main_help += '\n\n\t' + str_bold(CMD_HELP) + ': \t\tshow this help' main_help += '\n\t' + str_bold(CMD_EXIT) + ': \t\texit ROPium' load_help = str_main(str_bold("\n\t'load' Command")) load_help += str_special("\n\t(Load gadgets from a binary file)") load_help += "\n\n\t"+str_bold("Usage")+":\tload [OPTIONS] [ ...]" load_help += "\n\n\t"+str_bold("Options")+":" load_help += str_special("\n\t\t-a,--arch ")+" architecture to use for gadget" +"\n\t\t\t\t disassembly/analysis" load_help += "\n\n\t"+str_bold("Supported achitectures")+": "+', '.join([str_special(s) for s in str_to_arch]) load_help += "\n\n\t"+str_bold("Examples")+":\n\t\tload -a X86 /bin/bash \n\t\tload -a X64 ../my_binary1 ../my_binary2 " find_help = str_main(str_bold("\n\t'find' Command")) find_help += str_special("\n\t(Automatically find ropchains)") find_help += "\n\n\t"+str_bold("Usage")+":\tfind " find_help += "\n\n\t"+str_bold("Query examples")+":\n" find_help += "\n\t eax = 0x42" find_help += "\n\t eax = ebx" find_help += "\n\t eax = ebx ^ 3" find_help += "\n\t eax = ebx & ecx" find_help += "\n\t eax = [ebx + 16] " find_help += "\n\t eax = [0x12345678] " find_help += "\n\t eax += [ebx + 16]" find_help += "\n\t eax *= [0x12345678]" find_help += "\n\t [eax - 8] = ebx" find_help += "\n\t [eax - 8] = 0x42" find_help += "\n\t [eax - 8] &= ebx" find_help += "\n\t [eax - 8] &= 0x42" find_help += "\n\t [0x12345678] = ebx" find_help += "\n\t [0x12345678] = 0x42" find_help += "\n\t [0x12345678] &= ebx" find_help += "\n\t [0x12345678] &= 0x42" find_help += "\n\t [0x12345678] = '/bin/sh\x00'" find_help += "\n\t 0x08040120()" find_help += "\n\t 0x08040120(1, 2, 3, 4)" find_help += "\n\t sys_execve(0x1234, 0, 0) [syscall by name]" find_help += "\n\t sys_0xb(0x1234, 0, 0) [syscall by num]" badbytes_help = str_main(str_bold("\n\t'badbytes' Command")) badbytes_help += str_special("\n\t(Set bad bytes to avoid in ropchains)") badbytes_help += "\n\n\t"+str_bold("Usage")+":\tbadbytes set BYTE [BYTE ...]"+"\n\t\tbadbytes reset" badbytes_help += "\n\n\t"+str_bold("Example")+": badbytes set 0 0xa 0xb 255" keepregs_help = str_main(str_bold("\n\t'keepregs' Command")) keepregs_help += str_special("\n\t(Set registers that must not be clobbered)") keepregs_help += "\n\n\t"+str_bold("Usage")+":\tkeepregs set REG [REG ...]"+"\n\t\tkeepregs reset" keepregs_help += "\n\n\t"+str_bold("Example")+": keepregs set rsi rbp" safemem_help = str_main(str_bold("\n\t'safemem' Command")) safemem_help += str_special("\n\t(Enable/Disable the use of gadgets that dereference" + "\n\tregisters holding unknown values and thus might"+ "\n\tcause a crash)") safemem_help += "\n\n\t"+str_bold("Usage")+":\tsafemem set (disable unsafe gadgets)"+"\n\t\tsafemem unset (enable unsafe gadgets)" abi_help = str_main(str_bold("\n\t'abi' Command")) abi_help += str_special("\n\t(Set ABI to use when calling functions)") abi_help += "\n\n\t"+str_bold("Usage")+":\tabi set " abi_help += "\n\n\t"+str_bold("Supported ABIs")+": "+', '.join([str_special(s) for s in str_to_abi]) os_help = str_main(str_bold("\n\t'os' Command")) os_help += str_special("\n\t(Set OS to target when doing syscalls)") os_help += "\n\n\t"+str_bold("Usage")+":\tos set " os_help += "\n\n\t"+str_bold("Supported systems")+": "+', '.join([str_special(s) for s in str_to_os]) if __name__ == "__main__": main() ================================================ FILE: libropium/arch/arch.cpp ================================================ #include "arch.hpp" #include Arch::Arch(ArchType _type, int _bits, int _octets, int _nb, CPUMode _mode, Disassembler* _disasm): type(_type), bits(_bits), octets(_octets), nb_regs(_nb), mode(_mode), disasm(_disasm){} Arch::~Arch(){ delete disasm; disasm = nullptr; } ================================================ FILE: libropium/arch/archX86.cpp ================================================ #include "expression.hpp" #include "arch.hpp" #include "disassembler.hpp" #include "exception.hpp" #include "ir.hpp" #include #include #include #include #include using std::stringstream; /* =================================== * ArchX86 * ================================== */ ArchX86::ArchX86(): Arch(ArchType::X86, 32, 4, X86_NB_REGS, CPUMode::X86, new DisassemblerX86(CPUMode::X86)){ } string ArchX86::reg_name(reg_t num){ switch(num){ case X86_EAX: return "eax"; case X86_EBX: return "ebx"; case X86_ECX: return "ecx"; case X86_EDX: return "edx"; case X86_EDI: return "edi"; case X86_ESI: return "esi"; case X86_EBP: return "ebp"; case X86_ESP: return "esp"; case X86_EIP: return "eip"; case X86_CS: return "cs"; case X86_DS: return "ds"; case X86_ES: return "es"; case X86_FS: return "fs"; case X86_GS: return "gs"; case X86_SS: return "ss"; case X86_CF: return "cf"; case X86_PF: return "pf"; case X86_AF: return "af"; case X86_ZF: return "zf"; case X86_SF: return "sf"; case X86_TF: return "tf"; case X86_IF: return "if"; case X86_DF: return "df"; case X86_OF: return "of"; case X86_IOPL: return "iopl"; case X86_VM: return "vm"; case X86_NT: return "nt"; case X86_RF: return "rf"; case X86_AC: return "ac"; case X86_VIP: return "vip"; case X86_VIF: return "vif"; case X86_ID: return "id"; case X86_TSC: return "tsc"; default: throw runtime_exception("ArchX86::reg_name() got unknown reg num"); } } reg_t ArchX86::reg_num(string name){ if( !name.compare("eax")) return X86_EAX; else if( !name.compare("ebx")) return X86_EBX; else if( !name.compare("ecx")) return X86_ECX; else if( !name.compare("edx")) return X86_EDX; else if( !name.compare("edi")) return X86_EDI; else if( !name.compare("esi")) return X86_ESI; else if( !name.compare("ebp")) return X86_EBP; else if( !name.compare("esp")) return X86_ESP; else if( !name.compare("eip")) return X86_EIP; else if( !name.compare("cs")) return X86_CS; else if( !name.compare("ds")) return X86_DS; else if( !name.compare("es")) return X86_ES; else if( !name.compare("fs")) return X86_FS; else if( !name.compare("gs")) return X86_GS; else if( !name.compare("ss")) return X86_SS; else if( !name.compare("cf")) return X86_CF; else if( !name.compare("pf")) return X86_PF; else if( !name.compare("af")) return X86_AF; else if( !name.compare("zf")) return X86_ZF; else if( !name.compare("sf")) return X86_SF; else if( !name.compare("tf")) return X86_TF; else if( !name.compare("if")) return X86_IF; else if( !name.compare("df")) return X86_DF; else if( !name.compare("of")) return X86_OF; else if( !name.compare("iopl")) return X86_IOPL; else if( !name.compare("vm")) return X86_VM; else if( !name.compare("nt")) return X86_NT; else if( !name.compare("rf")) return X86_RF; else if( !name.compare("ac")) return X86_AC; else if( !name.compare("vip")) return X86_VIP; else if( !name.compare("vif")) return X86_VIF; else if( !name.compare("id")) return X86_ID; else if( !name.compare("tsc")) return X86_TSC; else throw runtime_exception(QuickFmt () << "ArchX86::reg_num() got unknown reg name: " << name >> QuickFmt::to_str); } bool ArchX86::is_valid_reg(string& name){ return ( !name.compare("eax")) || (!name.compare("ebx")) || (!name.compare("ecx")) || (!name.compare("edx")) || (!name.compare("edi")) || (!name.compare("esi")) || (!name.compare("ebp")) || (!name.compare("esp")) || (!name.compare("eip")) || (!name.compare("cs")) || (!name.compare("ds")) || (!name.compare("es")) || (!name.compare("fs")) || (!name.compare("gs")) || (!name.compare("ss")) || (!name.compare("cf")) || (!name.compare("pf")) || (!name.compare("af")) || (!name.compare("zf")) || (!name.compare("sf")) || (!name.compare("tf")) || (!name.compare("if")) || (!name.compare("df")) || (!name.compare("of")) || (!name.compare("iopl")) || (!name.compare("vm")) || (!name.compare("nt")) || (!name.compare("rf")) || (!name.compare("ac")) || (!name.compare("vip")) || (!name.compare("vif")) || (!name.compare("id")) || (!name.compare("tsc")); } reg_t ArchX86::sp(){ return X86_ESP; } reg_t ArchX86::pc(){ return X86_EIP; } reg_t ArchX86::tsc(){ return X86_TSC; } /* =================================== * ArchX64 * ================================== */ ArchX64::ArchX64(): Arch(ArchType::X64, 64, 8, X64_NB_REGS, CPUMode::X64, new DisassemblerX86(CPUMode::X64)){ } string ArchX64::reg_name(reg_t num){ switch(num){ case X64_RAX: return "rax"; case X64_RBX: return "rbx"; case X64_RCX: return "rcx"; case X64_RDX: return "rdx"; case X64_RDI: return "rdi"; case X64_RSI: return "rsi"; case X64_RBP: return "rbp"; case X64_RSP: return "rsp"; case X64_RIP: return "rip"; case X64_R8: return "r8"; case X64_R9: return "r9"; case X64_R10: return "r10"; case X64_R11: return "r11"; case X64_R12: return "r12"; case X64_R13: return "r13"; case X64_R14: return "r14"; case X64_R15: return "r15"; case X64_CS: return "cs"; case X64_DS: return "ds"; case X64_ES: return "es"; case X64_FS: return "fs"; case X64_GS: return "gs"; case X64_SS: return "ss"; case X64_CF: return "cf"; case X64_PF: return "pf"; case X64_AF: return "af"; case X64_ZF: return "zf"; case X64_SF: return "sf"; case X64_TF: return "tf"; case X64_IF: return "if"; case X64_DF: return "df"; case X64_OF: return "of"; case X64_IOPL: return "iopl"; case X64_VM: return "vm"; case X64_NT: return "nt"; case X64_RF: return "rf"; case X64_AC: return "ac"; case X64_VIP: return "vip"; case X64_VIF: return "vif"; case X64_ID: return "id"; case X64_TSC: return "tsc"; default: throw runtime_exception("ArchX64::reg_name() got unknown reg num"); } } reg_t ArchX64::reg_num(string name){ if( !name.compare("rax")) return X64_RAX; else if( !name.compare("rbx")) return X64_RBX; else if( !name.compare("rcx")) return X64_RCX; else if( !name.compare("rdx")) return X64_RDX; else if( !name.compare("rdi")) return X64_RDI; else if( !name.compare("rsi")) return X64_RSI; else if( !name.compare("rbp")) return X64_RBP; else if( !name.compare("rsp")) return X64_RSP; else if( !name.compare("rip")) return X64_RIP; else if( !name.compare("r8")) return X64_R8; else if( !name.compare("r9")) return X64_R9; else if( !name.compare("r10")) return X64_R10; else if( !name.compare("r11")) return X64_R11; else if( !name.compare("r12")) return X64_R12; else if( !name.compare("r13")) return X64_R13; else if( !name.compare("r14")) return X64_R14; else if( !name.compare("r15")) return X64_R15; else if( !name.compare("cs")) return X64_CS; else if( !name.compare("ds")) return X64_DS; else if( !name.compare("es")) return X64_ES; else if( !name.compare("fs")) return X64_FS; else if( !name.compare("gs")) return X64_GS; else if( !name.compare("ss")) return X64_SS; else if( !name.compare("cf")) return X64_CF; else if( !name.compare("pf")) return X64_PF; else if( !name.compare("af")) return X64_AF; else if( !name.compare("zf")) return X64_ZF; else if( !name.compare("sf")) return X64_SF; else if( !name.compare("tf")) return X64_TF; else if( !name.compare("if")) return X64_IF; else if( !name.compare("df")) return X64_DF; else if( !name.compare("of")) return X64_OF; else if( !name.compare("iopl")) return X64_IOPL; else if( !name.compare("vm")) return X64_VM; else if( !name.compare("nt")) return X64_NT; else if( !name.compare("rf")) return X64_RF; else if( !name.compare("ac")) return X64_AC; else if( !name.compare("vip")) return X64_VIP; else if( !name.compare("vif")) return X64_VIF; else if( !name.compare("id")) return X64_ID; else if( !name.compare("tsc")) return X64_TSC; else throw runtime_exception(QuickFmt () << "ArchX86::reg_num() got unknown reg name: " << name >> QuickFmt::to_str); } bool ArchX64::is_valid_reg(string& name){ return (!name.compare("rax")) || (!name.compare("rbx")) || (!name.compare("rcx")) || (!name.compare("rdx")) || (!name.compare("rdi")) || (!name.compare("rsi")) || (!name.compare("rbp")) || (!name.compare("rsp")) || (!name.compare("rip")) || (!name.compare("r8")) || (!name.compare("r9")) || (!name.compare("r10")) || (!name.compare("r11")) || (!name.compare("r12")) || (!name.compare("r13")) || (!name.compare("r14")) || (!name.compare("r15")) || (!name.compare("cs")) || (!name.compare("ds")) || (!name.compare("es")) || (!name.compare("fs")) || (!name.compare("gs")) || (!name.compare("ss")) || (!name.compare("cf")) || (!name.compare("pf")) || (!name.compare("af")) || (!name.compare("zf")) || (!name.compare("sf")) || (!name.compare("tf")) || (!name.compare("if")) || (!name.compare("df")) || (!name.compare("of")) || (!name.compare("iopl")) || (!name.compare("vm")) || (!name.compare("nt")) || (!name.compare("rf")) || (!name.compare("ac")) || (!name.compare("vip")) || (!name.compare("vif")) || (!name.compare("id")) || (!name.compare("tsc")); } reg_t ArchX64::sp(){ return X64_RSP; } reg_t ArchX64::pc(){ return X64_RIP; } reg_t ArchX64::tsc(){ return X64_TSC; } /* =================================== * X86 & X64 Disassembler * ================================== */ DisassemblerX86::DisassemblerX86(CPUMode mode){ _mode = mode; if( mode == CPUMode::X86 ){ cs_open(CS_ARCH_X86, CS_MODE_32, &_handle); }else if( mode == CPUMode::X64 ){ cs_open(CS_ARCH_X86, CS_MODE_64, &_handle); }else{ throw runtime_exception("DisassemblerX86: got unsupported mode"); } // Ask for detailed instructions cs_option(_handle, CS_OPT_DETAIL, CS_OPT_ON); // allocate memory cache for 1 instruction, to be used by cs_disasm_iter later. // (will be freed in destructor) _insn = cs_malloc(_handle); } inline IROperand x86_32_reg_translate(x86_reg reg){ switch(reg){ case X86_REG_AL: return IROperand(IROperandType::VAR, X86_EAX, 7, 0); case X86_REG_AH: return IROperand(IROperandType::VAR, X86_EAX, 15, 8); case X86_REG_AX: return IROperand(IROperandType::VAR, X86_EAX, 15, 0); case X86_REG_EAX: return IROperand(IROperandType::VAR, X86_EAX, 31, 0); case X86_REG_BL: return IROperand(IROperandType::VAR, X86_EBX, 7, 0); case X86_REG_BH: return IROperand(IROperandType::VAR, X86_EBX, 15, 8); case X86_REG_BX: return IROperand(IROperandType::VAR, X86_EBX, 15, 0); case X86_REG_EBX: return IROperand(IROperandType::VAR, X86_EBX , 31, 0); case X86_REG_CL: return IROperand(IROperandType::VAR, X86_ECX, 7, 0); case X86_REG_CH: return IROperand(IROperandType::VAR, X86_ECX, 15, 8); case X86_REG_CX: return IROperand(IROperandType::VAR, X86_ECX, 15, 0); case X86_REG_ECX: return IROperand(IROperandType::VAR, X86_ECX, 31, 0); case X86_REG_DL: return IROperand(IROperandType::VAR, X86_EDX, 7, 0); case X86_REG_DH: return IROperand(IROperandType::VAR, X86_EDX, 15, 8); case X86_REG_DX: return IROperand(IROperandType::VAR, X86_EDX, 15, 0); case X86_REG_EDX: return IROperand(IROperandType::VAR, X86_EDX, 31, 0); case X86_REG_DI: return IROperand(IROperandType::VAR, X86_EDI, 15, 0); case X86_REG_EDI: return IROperand(IROperandType::VAR, X86_EDI, 31, 0); case X86_REG_SI: return IROperand(IROperandType::VAR, X86_ESI, 15, 0); case X86_REG_ESI: return IROperand(IROperandType::VAR, X86_ESI, 31, 0); case X86_REG_BP: return IROperand(IROperandType::VAR, X86_EBP, 15, 0); case X86_REG_EBP: return IROperand(IROperandType::VAR, X86_EBP, 31, 0); case X86_REG_SP: return IROperand(IROperandType::VAR, X86_ESP, 15, 0); case X86_REG_ESP: return IROperand(IROperandType::VAR, X86_ESP, 31, 0); case X86_REG_IP: return IROperand(IROperandType::VAR, X86_EIP, 15, 0); case X86_REG_EIP: return IROperand(IROperandType::VAR, X86_EIP, 31, 0); case X86_REG_CS: return IROperand(IROperandType::VAR, X86_CS, 31, 0); case X86_REG_DS: return IROperand(IROperandType::VAR, X86_DS, 31, 0); case X86_REG_ES: return IROperand(IROperandType::VAR, X86_ES, 31, 0); case X86_REG_GS: return IROperand(IROperandType::VAR, X86_GS, 31, 0); case X86_REG_FS: return IROperand(IROperandType::VAR, X86_FS, 31, 0); case X86_REG_SS: return IROperand(IROperandType::VAR, X86_SS, 31, 0); default: throw runtime_exception( QuickFmt() << "Disassembler X86: unknown capstone register " << reg >> QuickFmt::to_str); } } inline IROperand x86_64_reg_translate(x86_reg reg){ switch(reg){ case X86_REG_AL: return IROperand(IROperandType::VAR, X64_RAX, 7, 0); case X86_REG_AH: return IROperand(IROperandType::VAR, X64_RAX, 15, 8); case X86_REG_AX: return IROperand(IROperandType::VAR, X64_RAX, 15, 0); case X86_REG_EAX: return IROperand(IROperandType::VAR, X64_RAX, 31, 0); case X86_REG_RAX: return IROperand(IROperandType::VAR, X64_RAX, 63, 0); case X86_REG_BL: return IROperand(IROperandType::VAR, X64_RBX, 7, 0); case X86_REG_BH: return IROperand(IROperandType::VAR, X64_RBX, 15, 8); case X86_REG_BX: return IROperand(IROperandType::VAR, X64_RBX, 15, 0); case X86_REG_EBX: return IROperand(IROperandType::VAR, X64_RBX , 31, 0); case X86_REG_RBX: return IROperand(IROperandType::VAR, X64_RBX , 63, 0); case X86_REG_CL: return IROperand(IROperandType::VAR, X64_RCX, 7, 0); case X86_REG_CH: return IROperand(IROperandType::VAR, X64_RCX, 15, 8); case X86_REG_CX: return IROperand(IROperandType::VAR, X64_RCX, 15, 0); case X86_REG_ECX: return IROperand(IROperandType::VAR, X64_RCX, 31, 0); case X86_REG_RCX: return IROperand(IROperandType::VAR, X64_RCX, 63, 0); case X86_REG_DL: return IROperand(IROperandType::VAR, X64_RDX, 7, 0); case X86_REG_DH: return IROperand(IROperandType::VAR, X64_RDX, 15, 8); case X86_REG_DX: return IROperand(IROperandType::VAR, X64_RDX, 15, 0); case X86_REG_EDX: return IROperand(IROperandType::VAR, X64_RDX, 31, 0); case X86_REG_RDX: return IROperand(IROperandType::VAR, X64_RDX, 63, 0); case X86_REG_DI: return IROperand(IROperandType::VAR, X64_RDI, 15, 0); case X86_REG_EDI: return IROperand(IROperandType::VAR, X64_RDI, 31, 0); case X86_REG_RDI: return IROperand(IROperandType::VAR, X64_RDI, 63, 0); case X86_REG_SI: return IROperand(IROperandType::VAR, X64_RSI, 15, 0); case X86_REG_ESI: return IROperand(IROperandType::VAR, X64_RSI, 31, 0); case X86_REG_RSI: return IROperand(IROperandType::VAR, X64_RSI, 63, 0); case X86_REG_BP: return IROperand(IROperandType::VAR, X64_RBP, 15, 0); case X86_REG_EBP: return IROperand(IROperandType::VAR, X64_RBP, 31, 0); case X86_REG_RBP: return IROperand(IROperandType::VAR, X64_RBP, 63, 0); case X86_REG_SP: return IROperand(IROperandType::VAR, X64_RSP, 15, 0); case X86_REG_ESP: return IROperand(IROperandType::VAR, X64_RSP, 31, 0); case X86_REG_RSP: return IROperand(IROperandType::VAR, X64_RSP, 63, 0); case X86_REG_IP: return IROperand(IROperandType::VAR, X64_RIP, 15, 0); case X86_REG_EIP: return IROperand(IROperandType::VAR, X64_RIP, 31, 0); case X86_REG_RIP: return IROperand(IROperandType::VAR, X64_RIP, 63, 0); case X86_REG_R8: return IROperand(IROperandType::VAR, X64_R8, 63, 0); case X86_REG_R8B: return IROperand(IROperandType::VAR, X64_R8, 7, 0); case X86_REG_R8D: return IROperand(IROperandType::VAR, X64_R8, 31, 0); case X86_REG_R8W: return IROperand(IROperandType::VAR, X64_R8, 15, 0); case X86_REG_R9: return IROperand(IROperandType::VAR, X64_R9, 63, 0); case X86_REG_R9B: return IROperand(IROperandType::VAR, X64_R9, 7, 0); case X86_REG_R9D: return IROperand(IROperandType::VAR, X64_R9, 31, 0); case X86_REG_R9W: return IROperand(IROperandType::VAR, X64_R9, 15, 0); case X86_REG_R10: return IROperand(IROperandType::VAR, X64_R10, 63, 0); case X86_REG_R10B: return IROperand(IROperandType::VAR, X64_R10, 7, 0); case X86_REG_R10D: return IROperand(IROperandType::VAR, X64_R10, 31, 0); case X86_REG_R10W: return IROperand(IROperandType::VAR, X64_R10, 15, 0); case X86_REG_R11: return IROperand(IROperandType::VAR, X64_R11, 63, 0); case X86_REG_R11B: return IROperand(IROperandType::VAR, X64_R11, 7, 0); case X86_REG_R11D: return IROperand(IROperandType::VAR, X64_R11, 31, 0); case X86_REG_R11W: return IROperand(IROperandType::VAR, X64_R11, 15, 0); case X86_REG_R12: return IROperand(IROperandType::VAR, X64_R12, 63, 0); case X86_REG_R12B: return IROperand(IROperandType::VAR, X64_R12, 7, 0); case X86_REG_R12D: return IROperand(IROperandType::VAR, X64_R12, 31, 0); case X86_REG_R12W: return IROperand(IROperandType::VAR, X64_R12, 15, 0); case X86_REG_R13: return IROperand(IROperandType::VAR, X64_R13, 63, 0); case X86_REG_R13B: return IROperand(IROperandType::VAR, X64_R13, 7, 0); case X86_REG_R13D: return IROperand(IROperandType::VAR, X64_R13, 31, 0); case X86_REG_R13W: return IROperand(IROperandType::VAR, X64_R13, 15, 0); case X86_REG_R14: return IROperand(IROperandType::VAR, X64_R14, 63, 0); case X86_REG_R14B: return IROperand(IROperandType::VAR, X64_R14, 7, 0); case X86_REG_R14D: return IROperand(IROperandType::VAR, X64_R14, 31, 0); case X86_REG_R14W: return IROperand(IROperandType::VAR, X64_R14, 15, 0); case X86_REG_R15: return IROperand(IROperandType::VAR, X64_R15, 63, 0); case X86_REG_R15B: return IROperand(IROperandType::VAR, X64_R15, 7, 0); case X86_REG_R15D: return IROperand(IROperandType::VAR, X64_R15, 31, 0); case X86_REG_R15W: return IROperand(IROperandType::VAR, X64_R15, 15, 0); case X86_REG_CS: return IROperand(IROperandType::VAR, X64_CS, 63, 0); case X86_REG_DS: return IROperand(IROperandType::VAR, X64_DS, 63, 0); case X86_REG_ES: return IROperand(IROperandType::VAR, X64_ES, 63, 0); case X86_REG_GS: return IROperand(IROperandType::VAR, X64_GS, 63, 0); case X86_REG_FS: return IROperand(IROperandType::VAR, X64_FS, 63, 0); case X86_REG_SS: return IROperand(IROperandType::VAR, X64_SS, 63, 0); default: throw runtime_exception( QuickFmt() << "Disassembler X86: unknown capstone register " << reg >> QuickFmt::to_str); } } inline IROperand x86_reg_translate(CPUMode mode, x86_reg reg){ if( mode == CPUMode::X86 ){ return x86_32_reg_translate(reg); }else{ return x86_64_reg_translate(reg); } } inline IROperand x86_arg_extract(IROperand& arg, exprsize_t high, exprsize_t low){ switch(arg.type){ case IROperandType::CST: return IROperand(IROperandType::CST, arg.cst(), high, low); case IROperandType::VAR: return IROperand(IROperandType::VAR, arg.var(), high, low); case IROperandType::TMP: return IROperand(IROperandType::TMP, arg.tmp(), high, low); case IROperandType::NONE: return IROperand(); default: throw runtime_exception("x86_arg_extract(): got unknown IROperandType!"); } } /* Translate capstone argument to IR argument * Arguments: * mode - the current CPU mode for registers translation * addr - the address of the instruction being translated * arg - the capstone operand * block/bblkid - block and basicblockid where to add instructions if needed * tmp_var_count - the counter of the tmp variables used in the current IRBlock * load_mem - if TRUE then load memory operands (dereference), else only return the operand (pointer) */ inline IROperand x86_arg_translate(CPUMode mode, addr_t addr, cs_x86_op* arg, IRBlock* block, IRBasicBlockId bblkid, int& tmp_vars_count, bool load_mem=false){ IROperand base, index, res, disp, segment; exprsize_t size = arg->size*8, addr_size = 0, reg_size = (mode==CPUMode::X86)? 32:64; try{ switch(arg->type){ /* Register */ case X86_OP_REG: return x86_reg_translate(mode, arg->reg); /* Immediate */ case X86_OP_IMM: return IROperand(IROperandType::CST, arg->imm, size-1, 0); /* Memory */ case X86_OP_MEM: // Arg = segment + base + (index*scale) + disp // Get index*scale if( arg->mem.index != X86_OP_INVALID ){ index = x86_reg_translate(mode, (x86_reg)arg->mem.index); if( arg->mem.scale != 1 ){ block->add_instr(bblkid, IRInstruction(IROperation::MUL, ir_tmp(tmp_vars_count++, index.size-1, 0), ir_cst(arg->mem.scale, index.size-1, 0), index, addr)); index = ir_tmp(tmp_vars_count-1, index.size-1, 0); } addr_size = index.size; } // Get base if( arg->mem.base != X86_OP_INVALID ){ base = x86_reg_translate(mode, (x86_reg)arg->mem.base); // If too small adjust if( base.size < index.size ){ block->add_instr(bblkid, ir_mov(ir_tmp(tmp_vars_count++, index.size-1, 0), ir_cst(0, index.size-1, 0), addr)); block->add_instr(bblkid, ir_mov(ir_tmp(tmp_vars_count-1, base.size-1, 0), base, addr)); base = ir_tmp(tmp_vars_count-1, base.size-1, 0); } addr_size = base.size; }else{ //base = ir_cst(0, index.size-1, 0); base = ir_none(); //throw runtime_exception("Disassembler X86: didn't expect X86_OP_INVALID base for mem operand in capstone"); } // Get displacement if( addr_size == 0 ) addr_size = reg_size; if( arg->mem.disp != 0 ){ disp = IROperand(IROperandType::CST, arg->mem.disp, addr_size-1, 0); }else{ disp = ir_none(); } // Get segment selector (here we consider that the segment selector symbolic register holds the address // of the segment, not the index in the GDT if( arg->mem.segment != X86_OP_INVALID ){ segment = x86_reg_translate(mode, (x86_reg)arg->mem.segment); // If too big, adjust if( segment.size > addr_size ){ block->add_instr(bblkid, ir_mov(ir_tmp(tmp_vars_count++, addr_size-1, 0), x86_arg_extract(segment, addr_size-1, 0), addr)); segment = ir_tmp(tmp_vars_count-1, addr_size-1, 0); } }else{ segment = ir_none(); } // === Build the operand now === // Add base and index if any if( !index.is_none() ){ if( !base.is_none() ){ block->add_instr(bblkid, ir_add(ir_tmp(tmp_vars_count++, index.size-1, 0), base, index, addr)); res = IROperand(IROperandType::TMP, tmp_vars_count-1, index.size-1, 0); }else{ res = index; } }else if(!base.is_none()){ res = base; }else{ res = ir_none(); } // Add displacement if any if( !disp.is_none() ){ if( !res.is_none()){ block->add_instr(bblkid, ir_add( ir_tmp(tmp_vars_count++, res.size-1, 0), disp, res, addr)); res = IROperand(IROperandType::TMP, tmp_vars_count-1, res.size-1, 0); }else{ res = disp; } } // Add segment if any if( !segment.is_none() ){ if( !res.is_none() ){ block->add_instr(bblkid, ir_add( ir_tmp(tmp_vars_count++, res.size-1, 0), segment, res, addr)); res = IROperand(IROperandType::TMP, tmp_vars_count-1, res.size-1, 0); }else{ res = segment; } } // Check res if( res.is_none() ){ throw symbolic_exception("Got IR_NONE memory operand"); } // Do load memory if requested if( load_mem ){ block->add_instr(bblkid, IRInstruction(IROperation::LDM, IROperand(IROperandType::TMP, tmp_vars_count++, size-1 , 0), res, addr)); res = IROperand(IROperandType::TMP, tmp_vars_count-1, size-1, 0); } return res; default: throw runtime_exception(QuickFmt() << "Disassembler X86: at addr: 0x" << std::hex << addr << " :got unknown capstone operand type" >> QuickFmt::to_str); } }catch(runtime_exception& e){ throw runtime_exception(QuickFmt() << "Disassembler X86: error at addr: 0x" << std::hex << addr << " :" + string(e.what()) >> QuickFmt::to_str); } throw runtime_exception(QuickFmt() << "Disassembler X86: at addr: 0x" << std::hex << addr << " :couldn't translate operand" >> QuickFmt::to_str); } /* Translate a 32bits assignments to a 64bits assignment where the upper * 32 bits are cleared. For the bits to be cleared, the following conditions * must be fulfilled: * - mode is CPUMOde::X64 * - destination is a register (IROperandType::VAR) * - destination is 32 bits */ inline void x86_adjust_reg_assign(CPUMode mode, addr_t addr, IRBlock* block, IRBasicBlockId bblkid, int& tmp_vars_count, IROperand dest, IROperand val){ IROperand res; if( mode != CPUMode::X64 || dest.size != 32 || dest.type != IROperandType::VAR){ // No need to adjust, to assign result to destination block->add_instr(bblkid, ir_mov(dest, val, addr)); return; } // Need to clear upper bits block->add_instr(bblkid, ir_concat(ir_var(dest.var(), 63, 0), ir_cst(0, 63-val.size, 0), val, addr)); } /* ========================================= */ inline IROperand x86_get_pc(CPUMode mode ){ if( mode == CPUMode::X86 ) return ir_var(X86_EIP, 31, 0 ); else if( mode == CPUMode::X64 ) return ir_var(X64_RIP, 63, 0 ); else throw runtime_exception("x86_get_pc(): got unknown CPUMode!"); } inline IROperand x86_get_tsc(CPUMode mode ){ if( mode == CPUMode::X86 ) return ir_var(X86_TSC, 63, 0 ); else if( mode == CPUMode::X64 ) return ir_var(X64_TSC, 63, 0 ); else throw runtime_exception("x86_get_pc(): got unknown CPUMode!"); } inline void x86_set_zf(CPUMode mode, IROperand& arg, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid){ if( mode == CPUMode::X86 ) block->add_instr(bblkid, ir_bisz(ir_var(X86_ZF, 31, 0), arg, ir_cst(1, 31, 0), addr)); else block->add_instr(bblkid, ir_bisz(ir_var(X64_ZF, 63, 0), arg , ir_cst(1, 63, 0), addr)); } inline void x86_add_set_cf(CPUMode mode, IROperand op0, IROperand op1, IROperand res, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid, int& tmp_var_count){ IROperand msb0 = x86_arg_extract(op0, op0.high, op0.high), msb1 = x86_arg_extract(op1, op1.high, op1.high), msb2 = x86_arg_extract(res, res.high, res.high), tmp0 = ir_tmp(tmp_var_count++, 0, 0 ), tmp1 = ir_tmp(tmp_var_count++, 0, 0 ), tmp2 = ir_tmp(tmp_var_count++, 0, 0 ); // cf -> higher bits of both operands are already 1 block->add_instr(bblkid, ir_and(tmp0, msb0, msb1, addr)); // or they are 1 and 0 and result has MSB 0 block->add_instr(bblkid, ir_xor(tmp1, msb0, msb1, addr)); block->add_instr(bblkid, ir_not(tmp2, msb2, addr)); block->add_instr(bblkid, ir_and(tmp2, tmp1, tmp2, addr)); block->add_instr(bblkid, ir_or(tmp2, tmp0, tmp2, addr)); if( mode == CPUMode::X86 ) block->add_instr(bblkid, ir_bisz( ir_var(X86_CF, 31, 0),tmp2, ir_cst(0, 31, 0), addr)); else if( mode == CPUMode::X64 ) block->add_instr(bblkid, ir_bisz( ir_var(X64_CF, 63, 0),tmp2, ir_cst(0, 63, 0), addr)); } inline void x86_add_set_of(CPUMode mode, IROperand op0, IROperand op1, IROperand res, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid, int& tmp_var_count){ IROperand msb0 = x86_arg_extract(op0, op0.high, op0.high), msb1 = x86_arg_extract(op1, op1.high, op1.high), msb2 = x86_arg_extract(res, res.high, res.high), tmp0 = ir_tmp(tmp_var_count++, 0, 0 ), tmp1 = ir_tmp(tmp_var_count++, 0, 0 ); // of -> msb of both operands have the same MSB but result // has different block->add_instr(bblkid, ir_xor(tmp0, msb0, msb1, addr)); block->add_instr(bblkid, ir_not(tmp0, tmp0, addr)); block->add_instr(bblkid, ir_xor(tmp1, msb0, msb2, addr)); block->add_instr(bblkid, ir_and(tmp1, tmp0, tmp1, addr)); if( mode == CPUMode::X86 ) block->add_instr(bblkid, ir_bisz(ir_var(X86_OF, 31, 0), tmp1, ir_cst(0, 31, 0), addr)); else if( mode == CPUMode::X64 ) block->add_instr(bblkid, ir_bisz(ir_var(X64_OF, 63, 0), tmp1, ir_cst(0, 63, 0), addr)); } inline void x86_sub_set_cf(CPUMode mode, IROperand op0, IROperand op1, IROperand res, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid, int& tmp_var_count){ IROperand msb0 = x86_arg_extract(op0, op0.high, op0.high), msb1 = x86_arg_extract(op1, op1.high, op1.high), msb2 = x86_arg_extract(res, res.high, res.high), tmp0 = ir_tmp(tmp_var_count++, 0, 0 ), tmp1 = ir_tmp(tmp_var_count++, 0, 0 ); // cf <- (~msb0&msb1) | (msb1&msb2) | (~msb0&msb2) block->add_instr(bblkid, ir_not(tmp0, msb0, addr)); block->add_instr(bblkid, ir_and(tmp0, tmp0, msb1, addr)); block->add_instr(bblkid, ir_and(tmp1, msb1, msb2, addr)); block->add_instr(bblkid, ir_or(tmp1, tmp0, tmp1, addr)); block->add_instr(bblkid, ir_not(tmp0, msb0, addr)); block->add_instr(bblkid, ir_and(tmp0, tmp0, msb2, addr)); block->add_instr(bblkid, ir_or(tmp1, tmp1, tmp0, addr)); if( mode == CPUMode::X86 ) block->add_instr(bblkid, ir_bisz( ir_var(X86_CF, 31, 0),tmp1, ir_cst(0, 31, 0), addr)); else if( mode == CPUMode::X64 ) block->add_instr(bblkid, ir_bisz( ir_var(X64_CF, 63, 0),tmp1, ir_cst(0, 63, 0), addr)); } inline void x86_sub_set_af(CPUMode mode, IROperand op0, IROperand op1, IROperand res, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid, int& tmp_var_count){ /* Like cf but for bit 3 */ IROperand msb0 = x86_arg_extract(op0, 3, 3), msb1 = x86_arg_extract(op1, 3, 3), msb2 = x86_arg_extract(res, 3, 3), tmp0 = ir_tmp(tmp_var_count++, 0, 0 ), tmp1 = ir_tmp(tmp_var_count++, 0, 0 ); // cf <- (~msb0&msb1) | (msb1&msb2) | (~msb0&msb2) block->add_instr(bblkid, ir_not(tmp0, msb0, addr)); block->add_instr(bblkid, ir_and(tmp0, tmp0, msb1, addr)); block->add_instr(bblkid, ir_and(tmp1, msb1, msb2, addr)); block->add_instr(bblkid, ir_or(tmp1, tmp0, tmp1, addr)); block->add_instr(bblkid, ir_not(tmp0, msb0, addr)); block->add_instr(bblkid, ir_and(tmp0, tmp0, msb2, addr)); block->add_instr(bblkid, ir_or(tmp1, tmp1, tmp0, addr)); if( mode == CPUMode::X86 ) block->add_instr(bblkid, ir_bisz( ir_var(X86_AF, 31, 0),tmp1, ir_cst(0, 31, 0), addr)); else if( mode == CPUMode::X64 ) block->add_instr(bblkid, ir_bisz( ir_var(X64_AF, 63, 0),tmp1, ir_cst(0, 63, 0), addr)); } inline void x86_sub_set_of(CPUMode mode, IROperand op0, IROperand op1, IROperand res, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid, int& tmp_var_count){ IROperand msb0 = x86_arg_extract(op0, op0.high, op0.high), msb1 = x86_arg_extract(op1, op1.high, op1.high), msb2 = x86_arg_extract(res, res.high, res.high), tmp0 = ir_tmp(tmp_var_count++, 0, 0 ), tmp1 = ir_tmp(tmp_var_count++, 0, 0 ); // of -> msb of both operands have different MSB and result // has the same as second operand block->add_instr(bblkid, ir_xor(tmp0, msb0, msb1, addr)); block->add_instr(bblkid, ir_xor(tmp1, msb1, msb2, addr)); block->add_instr(bblkid, ir_not(tmp1, tmp1, addr)); block->add_instr(bblkid, ir_and(tmp1, tmp0, tmp1, addr)); if( mode == CPUMode::X86 ) block->add_instr(bblkid, ir_bisz(ir_var(X86_OF, 31, 0), tmp1, ir_cst(0, 31, 0), addr)); else if( mode == CPUMode::X64 ) block->add_instr(bblkid, ir_bisz(ir_var(X64_OF, 63, 0), tmp1, ir_cst(0, 63, 0), addr)); } inline void x86_set_sf(CPUMode mode, IROperand& arg, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid){ IROperand sf = mode == CPUMode::X86 ? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); block->add_instr(bblkid, ir_bisz(sf, x86_arg_extract(arg, arg.high, arg.high), ir_cst(0, sf.high, 0), addr)); } inline void x86_add_set_af(CPUMode mode, IROperand op0, IROperand op1, IROperand res, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid, int& tmp_var_count){ // Basically like cf but for bits 3 IROperand msb0 = x86_arg_extract(op0, 3, 3), msb1 = x86_arg_extract(op1, 3, 3), msb2 = x86_arg_extract(res, 3, 3), tmp0 = ir_tmp(tmp_var_count++, 0, 0 ), tmp1 = ir_tmp(tmp_var_count++, 0, 0 ), tmp2 = ir_tmp(tmp_var_count++, 0, 0 ), tmp3 = ir_tmp(tmp_var_count++, 0, 0 ), tmp4 = ir_tmp(tmp_var_count++, 0, 0 ); // cf -> higher bits of both operands are already 1 block->add_instr(bblkid, ir_and(tmp0, msb0, msb1, addr)); // or they are 1 and 0 and result has MSB 0 block->add_instr(bblkid, ir_xor(tmp1, msb0, msb1, addr)); block->add_instr(bblkid, ir_not(tmp2, msb2, addr)); block->add_instr(bblkid, ir_and(tmp3, tmp1, tmp2, addr)); block->add_instr(bblkid, ir_or(tmp4, tmp0, tmp3, addr)); if( mode == CPUMode::X86 ) block->add_instr(bblkid, ir_bisz( ir_var(X86_AF, 31, 0),tmp4, ir_cst(0, 31, 0), addr)); else if( mode == CPUMode::X64 ) block->add_instr(bblkid, ir_bisz( ir_var(X64_AF, 63, 0),tmp4, ir_cst(0, 63, 0), addr)); } inline void x86_set_pf(CPUMode mode, IROperand arg, addr_t addr, IRBlock* block, IRBasicBlockId bblkid, int& tmp_var_count){ // pf number of bits that are equal to zero in the least significant byte // of the result of an operation -> xor all and set flag if zero IROperand tmp = ir_tmp(tmp_var_count++, 0, 0 ); block->add_instr(bblkid, ir_mov(tmp, x86_arg_extract(arg, 0, 0), addr)); for( int i = 1; i < 8; i++){ block->add_instr(bblkid, ir_xor(tmp, tmp, x86_arg_extract(arg, i, i), addr)); } if( mode == CPUMode::X86 ){ block->add_instr(bblkid, ir_bisz(ir_var(X86_PF, 31, 0), tmp, ir_cst(1, 31, 0), addr)); }else if( mode == CPUMode::X64 ){ block->add_instr(bblkid, ir_bisz(ir_var(X64_PF, 63, 0), tmp, ir_cst(1, 31, 0), addr)); } } /* ===================== * Instruction prefixes * ===================== */ IRBasicBlockId _x86_init_prefix(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid){ IRBasicBlockId start; if( instr->detail->x86.prefix[0] != X86_PREFIX_REP && instr->detail->x86.prefix[0] != X86_PREFIX_REPNE ){ return -1; } start = block->new_bblock(); block->add_instr(bblkid, ir_bcc(ir_cst(1, 31, 0), ir_cst(start, 31, 0), ir_none(), addr)); bblkid = block->new_bblock(); return start; } bool inline _accepts_repe_prefix(cs_insn* instr){ return instr->id == X86_INS_CMPSB || instr->id == X86_INS_CMPSW || instr->id == X86_INS_CMPSD || instr->id == X86_INS_CMPSQ || instr->id == X86_INS_SCASB || instr->id == X86_INS_SCASW || instr->id == X86_INS_SCASD || instr->id == X86_INS_SCASQ; } bool inline _accepts_rep_prefix(cs_insn* instr){ return instr->id == X86_INS_INSB || instr->id == X86_INS_INSW || instr->id == X86_INS_INSD || instr->id == X86_INS_MOVSB || instr->id == X86_INS_MOVSW || instr->id == X86_INS_MOVSD || instr->id == X86_INS_MOVSQ || instr->id == X86_INS_OUTSB || instr->id == X86_INS_OUTSW || instr->id == X86_INS_OUTSD || instr->id == X86_INS_LODSB || instr->id == X86_INS_LODSW || instr->id == X86_INS_LODSD || instr->id == X86_INS_LODSQ || instr->id == X86_INS_STOSB || instr->id == X86_INS_STOSW || instr->id == X86_INS_STOSD || instr->id == X86_INS_STOSQ; } /* Wraps an instruction block with a REP prefix * Parameters: * start - the basic block where to test the terminating condition. The instruction semantics start at start+1 * last - the current last bblock of the instruction * */ inline void _x86_end_prefix(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId start, IRBasicBlockId& last, int& tmp_var_count){ IROperand cx = (mode == CPUMode::X86)? ir_var(X86_ECX, 31, 0): ir_var(X64_RCX, 63, 0); IROperand zf = (mode == CPUMode::X86)? ir_var(X86_ZF, 31, 0): ir_var(X64_ZF, 63, 0); IROperand tmp; IRBasicBlockId end; if( instr->detail->x86.prefix[0] != X86_PREFIX_REP && instr->detail->x86.prefix[0] != X86_PREFIX_REPNE ){ return; } /* Add loop and cx decrement at the end of the instruction */ block->add_instr(last, ir_sub(cx, cx, ir_cst(1, cx.size-1, 0), addr)); block->add_instr(last, ir_bcc(ir_cst(1, 31, 0), ir_cst(start, 31, 0), ir_none(), addr)); /* Add REP test in the beginning */ end = block->new_bblock(); if( instr->detail->x86.prefix[0] == X86_PREFIX_REP && _accepts_rep_prefix(instr) ){ block->add_instr(start, ir_bcc(cx, ir_cst(start+1, 31, 0), ir_cst(end, 31, 0), addr)); }else if( instr->detail->x86.prefix[0] == X86_PREFIX_REP && _accepts_repe_prefix(instr) ){ tmp = ir_tmp(tmp_var_count++, 0, 0); block->add_instr(start, ir_bisz(tmp, cx, ir_cst(0, 0, 0), addr)); block->add_instr(start, ir_and(tmp, tmp, x86_arg_extract(zf, 0, 0), addr)); block->add_instr(start, ir_bcc(tmp, ir_cst(start+1, 31, 0), ir_cst(end, 31, 0), addr)); }else if( instr->detail->x86.prefix[0] == X86_PREFIX_REPNE ){ tmp = ir_tmp(tmp_var_count++, 0, 0); block->add_instr(start, ir_bisz(tmp, cx, ir_cst(1, 0, 0), addr)); block->add_instr(start, ir_or(tmp, tmp, x86_arg_extract(zf, 0, 0), addr)); block->add_instr(start, ir_bcc(tmp, ir_cst(end, 31, 0), ir_cst(start+1, 31, 0), addr)); } last = end; // Update last basic block } /* ========================================= */ /* Instructions translation */ inline void x86_aaa_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand af, eax, cf, tmp0, tmp1, pc; if( mode == CPUMode::X86 ){ eax = ir_var(X86_EAX, 31, 0); af = ir_var(X86_AF, 31, 0); cf = ir_var(X86_CF, 31, 0); }else if( mode == CPUMode::X64 ){ throw illegal_instruction_exception("X86 AAA instruction is valid only in 32-bit mode"); } tmp0 = ir_tmp(tmp_var_count++, af.size-1, 0), // Get the size from any register tmp1 = ir_tmp(tmp_var_count++, af.size-1, 0); /* If 4 LSB are > 9 or if AF is set then adjust the unpacked BCD values */ // (4 LSB) > 9 block->add_instr(bblkid, ir_bisz(tmp0, x86_arg_extract(eax, 3, 3), ir_cst(0, eax.size,0), addr)); block->add_instr(bblkid, ir_bisz(tmp1, x86_arg_extract(eax, 2, 1), ir_cst(0, eax.size,0), addr)); block->add_instr(bblkid, ir_and(tmp1, tmp1, tmp0, addr)); // AF block->add_instr(bblkid, ir_or(tmp1, af, tmp1, addr)); // Branch depending on condition block->add_instr(bblkid, ir_bcc(tmp1, ir_cst(bblkid+1, 31,0), ir_cst(bblkid+2, 31, 0), addr)); // 1°) Branch 1 - Do the adjust bblkid = block->new_bblock(); // AL <- AL + 6 block->add_instr(bblkid, ir_add(x86_arg_extract(eax, 7, 0), x86_arg_extract(eax, 7, 0), ir_cst(6, 7, 0), addr)); // AH ++ block->add_instr(bblkid, ir_add(x86_arg_extract(eax, 15, 8), x86_arg_extract(eax, 15, 8), ir_cst(1, 7, 0), addr)); // CF <- 1 , AF <- 1 block->add_instr(bblkid, ir_mov(af, ir_cst(1, af.size-1, 0), addr)); block->add_instr(bblkid, ir_mov(cf, ir_cst(1, af.size-1, 0), addr)); // Jump to common end block->add_instr(bblkid, ir_bcc(ir_cst(1, 0, 0), ir_cst(bblkid+2, 31, 0), ir_none(), addr)); // 2°) Branch 2 - Just reset flags bblkid = block->new_bblock(); block->add_instr(bblkid, ir_mov(af, ir_cst(0, af.size-1, 0), addr)); block->add_instr(bblkid, ir_mov(cf, ir_cst(0, af.size-1, 0), addr)); // Jump to common end block->add_instr(bblkid, ir_bcc(ir_cst(1, 0, 0), ir_cst(bblkid+1, 31, 0), ir_none(), addr)); // 3°) Common end - Keep only 4 LSB of AL bblkid = block->new_bblock(); block->add_instr(bblkid, ir_and(x86_arg_extract(eax, 7, 0), x86_arg_extract(eax, 7, 0), ir_cst(0xf, 7, 0), addr)); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_aad_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand tmp0, imm, al, pc; if( mode != CPUMode::X86 ){ throw illegal_instruction_exception("X86 AAD instruction is valid only in 32-bit mode"); } tmp0 = ir_tmp(tmp_var_count++, 7, 0), // Get the size from any register imm = ir_cst(0xa, 7, 0); // 2 byte of the encoded instruction always 0xA for AAD al = ir_var(X86_EAX, 7,0); // AL <- (AL + (AH ∗ imm8)) & 0xFF; // AH <- 0 block->add_instr(bblkid, ir_mul(tmp0, ir_var(X86_EAX, 15, 8), imm, addr)); block->add_instr(bblkid, ir_add(al, al, tmp0, addr)); block->add_instr(bblkid, ir_mov(ir_var(X86_EAX, 15, 8), ir_cst(0, 7, 0), addr)); // Set flags : SF, ZF, PF x86_set_sf(mode, al, addr, block, bblkid); x86_set_zf(mode, al, addr, block, bblkid); x86_set_pf(mode, al, addr, block, bblkid, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_aam_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand tmp0, imm, al, pc; if( mode != CPUMode::X86 ){ throw illegal_instruction_exception("X86 AAM instruction is valid only in 32-bit mode"); } tmp0 = ir_tmp(tmp_var_count++, 7, 0), // Get the size from any register imm = ir_cst(0xa, 7, 0); // 2 byte of the encoded instruction always 0xA for AAM al = ir_var(X86_EAX, 7,0); // AH <- AL / 10 // AL <- AL % 10 block->add_instr(bblkid, ir_mov(tmp0, al, addr)); block->add_instr(bblkid, ir_div(ir_var(X86_EAX, 15, 8), tmp0, imm, addr)); block->add_instr(bblkid, ir_mod(al, tmp0, imm, addr)); // Set flags : SF, ZF, PF x86_set_sf(mode, al, addr, block, bblkid); x86_set_zf(mode, al, addr, block, bblkid); x86_set_pf(mode, al, addr, block, bblkid, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_aas_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand af, eax, cf, tmp0, tmp1, pc; if( mode == CPUMode::X86 ){ eax = ir_var(X86_EAX, 31, 0); af = ir_var(X86_AF, 31, 0); cf = ir_var(X86_CF, 31, 0); }else if( mode == CPUMode::X64 ){ throw illegal_instruction_exception("X86 AAS instruction is valid only in 32-bit mode"); } tmp0 = ir_tmp(tmp_var_count++, af.size-1, 0), // Get the size from any register tmp1 = ir_tmp(tmp_var_count++, af.size-1, 0); /* If 4 LSB are > 9 or if AF is set then adjust the unpacked BCD values */ // (4 LSB) > 9 block->add_instr(bblkid, ir_bisz(tmp0, x86_arg_extract(eax, 3, 3), ir_cst(0, eax.size,0), addr)); block->add_instr(bblkid, ir_bisz(tmp1, x86_arg_extract(eax, 2, 1), ir_cst(0, eax.size,0), addr)); block->add_instr(bblkid, ir_and(tmp1, tmp1, tmp0, addr)); // AF block->add_instr(bblkid, ir_or(tmp1, af, tmp1, addr)); // Branch depending on condition block->add_instr(bblkid, ir_bcc(tmp1, ir_cst(bblkid+1, 31,0), ir_cst(bblkid+2, 31, 0), addr)); // 1°) Branch 1 - Do the adjust bblkid = block->new_bblock(); // AL <- AL - 6 block->add_instr(bblkid, ir_sub(x86_arg_extract(eax, 7, 0), x86_arg_extract(eax, 7, 0), ir_cst(6, 7, 0), addr)); // AH block->add_instr(bblkid, ir_sub(x86_arg_extract(eax, 15, 8), x86_arg_extract(eax, 15, 8), ir_cst(1, 7, 0), addr)); // CF <- 1 , AF <- 1 block->add_instr(bblkid, ir_mov(af, ir_cst(1, af.size-1, 0), addr)); block->add_instr(bblkid, ir_mov(cf, ir_cst(1, af.size-1, 0), addr)); // Jump to common end block->add_instr(bblkid, ir_bcc(ir_cst(1, 0, 0), ir_cst(bblkid+2, 31, 0), ir_none(), addr)); // 2°) Branch 2 - Just reset flags bblkid = block->new_bblock(); block->add_instr(bblkid, ir_mov(af, ir_cst(0, af.size-1, 0), addr)); block->add_instr(bblkid, ir_mov(cf, ir_cst(0, af.size-1, 0), addr)); // Jump to common end block->add_instr(bblkid, ir_bcc(ir_cst(1, 0, 0), ir_cst(bblkid+1, 31, 0), ir_none(), addr)); // 3°) Common end - Keep only 4 LSB of AL bblkid = block->new_bblock(); block->add_instr(bblkid, ir_and(x86_arg_extract(eax, 7, 0), x86_arg_extract(eax, 7, 0), ir_cst(0xf, 7, 0), addr)); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_adc_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, res, prev_cf, pc; /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); }else{ op0 = dest; } op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); res = ir_tmp(tmp_var_count++, (instr->detail->x86.operands[0].size*8)-1, 0); if( mode == CPUMode::X86 ) prev_cf = ir_var(X86_CF, res.size-1, 0); else if( mode == CPUMode::X64 ) prev_cf = ir_var(X64_CF, res.size-1, 0); /* Do the add */ block->add_instr(bblkid, ir_add(res, op0, op1, addr)); block->add_instr(bblkid, ir_add(res, res, prev_cf, addr)); /* Update flags */ x86_set_zf(mode, res, addr, block, bblkid); x86_add_set_cf(mode, op0, op1, res, addr, block, bblkid, tmp_var_count); x86_add_set_af(mode, op0, op1, res, addr, block, bblkid, tmp_var_count); x86_add_set_of(mode, op0, op1, res, addr, block, bblkid, tmp_var_count); x86_set_sf(mode, res, addr, block, bblkid); x86_set_pf(mode, res, addr, block, bblkid, tmp_var_count); /* Finally assign the result to the destination */ /* If the add is written in memory */ if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(bblkid, ir_stm(dest, res, addr)); /* Else direct register assign */ }else{ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, res); } // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_adcx_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, res, prev_cf, pc; /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); }else{ op0 = dest; } op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); res = ir_tmp(tmp_var_count++, (instr->detail->x86.operands[0].size*8)-1, 0); if( mode == CPUMode::X86 ) prev_cf = ir_var(X86_CF, res.size-1, 0); else if( mode == CPUMode::X64 ) prev_cf = ir_var(X64_CF, res.size-1, 0); /* Do the add */ block->add_instr(bblkid, ir_add(res, op0, op1, addr)); block->add_instr(bblkid, ir_add(res, res, prev_cf, addr)); /* Update flags */ x86_add_set_cf(mode, op0, op1, res, addr, block, bblkid, tmp_var_count); /* Finally assign the result to the destination */ /* ADCX destination is always a general purpose reg */ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, res); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_add_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, res, pc; /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); }else{ op0 = dest; } op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); /* Do the add */ res = ir_tmp(tmp_var_count++, (instr->detail->x86.operands[0].size*8)-1, 0); block->add_instr(bblkid, ir_add(res, op0, op1, addr)); /* Update flags */ x86_set_zf(mode, res, addr, block, bblkid); x86_add_set_cf(mode, op0, op1, res, addr, block, bblkid, tmp_var_count); x86_add_set_af(mode, op0, op1, res, addr, block, bblkid, tmp_var_count); x86_add_set_of(mode, op0, op1, res, addr, block, bblkid, tmp_var_count); x86_set_sf(mode, res, addr, block, bblkid); x86_set_pf(mode, res, addr, block, bblkid, tmp_var_count); /* Finally assign the result to the destination */ /* If the add is written in memory */ if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(bblkid, ir_stm(dest, res, addr)); /* Else direct register assign */ }else{ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, res); } // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_and_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, res, of, cf, pc; of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); }else{ op0 = dest; } op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); /* Do the and */ res = ir_tmp(tmp_var_count++, (instr->detail->x86.operands[0].size*8)-1, 0); block->add_instr(bblkid, ir_and(res, op0, op1, addr)); /* Update flags: SF, ZF, PF */ x86_set_zf(mode, res, addr, block, bblkid); x86_set_sf(mode, res, addr, block, bblkid); x86_set_pf(mode, res, addr, block, bblkid, tmp_var_count); /* OF and CF cleared */ block->add_instr(bblkid, ir_mov(of, ir_cst(0, of.high, of.low), addr)); block->add_instr(bblkid, ir_mov(cf, ir_cst(0, cf.high, cf.low), addr)); /* Finally assign the result to the destination */ /* If the add is written in memory */ if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(bblkid, ir_stm(dest, res, addr)); /* Else direct register assign */ }else{ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, res); } // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_andn_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, res, of, cf, pc; of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[2]), block, bblkid, tmp_var_count, true); /* Do the not then the and */ res = ir_tmp(tmp_var_count++, (instr->detail->x86.operands[0].size*8)-1, 0); block->add_instr(bblkid, ir_not(res, op0, addr)); block->add_instr(bblkid, ir_and(res, res, op1, addr)); /* Update flags: SF, ZF */ x86_set_zf(mode, res, addr, block, bblkid); x86_set_sf(mode, res, addr, block, bblkid); /* OF and CF cleared */ block->add_instr(bblkid, ir_mov(of, ir_cst(0, of.high, of.low), addr)); block->add_instr(bblkid, ir_mov(cf, ir_cst(0, cf.high, cf.low), addr)); /* Finally assign the result to the destination */ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, res); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_blsi_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, res, of, cf, pc; of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); /* Do the not then the and */ res = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_neg(res, op0, addr)); block->add_instr(bblkid, ir_and(res, res, op0, addr)); /* Update flags: SF, ZF */ x86_set_zf(mode, res, addr, block, bblkid); x86_set_sf(mode, res, addr, block, bblkid); /* CF set if op0 is source is not zero */ block->add_instr(bblkid, ir_bisz(cf, op0, ir_cst(0, cf.size-1, 0), addr)); /* OF cleared */ block->add_instr(bblkid, ir_mov(of, ir_cst(0, of.high, of.low), addr)); /* Finally assign the result to the destination */ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, res); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_blsmsk_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, res, of, cf, zf, pc; of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); zf = (mode == CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); /* res <- (op0-1) XOR op0 */ res = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_sub(res, op0, ir_cst(1, op0.size-1, 0), addr)); block->add_instr(bblkid, ir_xor(res, res, op0, addr)); /* Update flags: SF */ x86_set_sf(mode, res, addr, block, bblkid); /* CF set if op0 is source is zero */ block->add_instr(bblkid, ir_bisz(cf, op0, ir_cst(1, cf.size-1, 0), addr)); /* OF and ZF cleared */ block->add_instr(bblkid, ir_mov(of, ir_cst(0, of.high, of.low), addr)); block->add_instr(bblkid, ir_mov(zf, ir_cst(0, of.high, of.low), addr)); /* Finally assign the result to the destination */ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, res); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_blsr_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, res, of, cf, zf, pc; of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); zf = (mode == CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); /* res <- (op0-1) AND op0 */ res = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_sub(res, op0, ir_cst(1, op0.size-1, 0), addr)); block->add_instr(bblkid, ir_and(res, res, op0, addr)); /* Update flags: SF, ZF */ x86_set_sf(mode, res, addr, block, bblkid); x86_set_zf(mode, res, addr, block, bblkid); /* CF set if op0 is source is zero */ block->add_instr(bblkid, ir_bisz(cf, op0, ir_cst(1, cf.size-1, 0), addr)); /* OF cleared */ block->add_instr(bblkid, ir_mov(of, ir_cst(0, of.high, of.low), addr)); /* Finally assign the result to the destination */ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, res); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_bsf_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand dest, op0, counter, tmp0, zf, pc; IRBasicBlockId loop_test, loop_body, loop_exit, op_is_zero, op_not_zero, end; zf = (mode == CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); op_not_zero = block->new_bblock(); loop_test = block->new_bblock(); loop_body = block->new_bblock(); loop_exit = block->new_bblock(); op_is_zero = block->new_bblock(); end = block->new_bblock(); // Update PC first because then we don't know what branch we take pc = x86_get_pc(mode); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // op0 == 0 ?? block->add_instr(bblkid, ir_bcc(op0, ir_cst(op_not_zero, 31, 0), ir_cst(op_is_zero, 31, 0), addr)); // 1°) Branch1 : op_not_zero counter = ir_tmp(tmp_var_count++, dest.size-1, 0); tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(op_not_zero, ir_mov(counter, ir_cst(0, counter.size-1, 0), addr)); // counter <- 0 block->add_instr(op_not_zero, ir_bcc(ir_cst(1, 31, 0) , ir_cst(loop_test, 31, 0), ir_none(), addr)); // loop test: while ( op0[i] == 0 ) block->add_instr(loop_test, ir_shr(tmp0, op0, counter, addr)); block->add_instr(loop_test, ir_bcc(x86_arg_extract(tmp0,0,0) , ir_cst(loop_exit, 31, 0), ir_cst(loop_body, 31, 0), addr)); // loop body: counter = counter + 1 block->add_instr(loop_body, ir_add(counter, counter, ir_cst(1, counter.size-1, 0), addr)); block->add_instr(loop_body, ir_bcc(ir_cst(1, 31, 0) , ir_cst(loop_test, 31, 0), ir_none(), addr)); // loop exit: dest <- counter and ZF <- 0 x86_adjust_reg_assign(mode, addr, block, loop_exit, tmp_var_count, dest, counter); x86_set_zf(mode, op0, addr, block, loop_exit ); block->add_instr(loop_exit, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // 2°) Branch2: op_is_zero // ZF <- 1 block->add_instr(op_is_zero, ir_mov(zf, ir_cst(1, zf.size-1, 0), addr)); block->add_instr(op_is_zero, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_bsr_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand dest, op0, counter, tmp0, zf, pc; IRBasicBlockId loop_test, loop_body, loop_exit, op_is_zero, op_not_zero, end; zf = (mode == CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); op_not_zero = block->new_bblock(); loop_test = block->new_bblock(); loop_body = block->new_bblock(); loop_exit = block->new_bblock(); op_is_zero = block->new_bblock(); end = block->new_bblock(); // Update PC first because then we don't know what branch we take pc = x86_get_pc(mode); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // op0 == 0 ?? block->add_instr(bblkid, ir_bcc(op0, ir_cst(op_not_zero, 31, 0), ir_cst(op_is_zero, 31, 0), addr)); // 1°) Branch1 : op_not_zero counter = ir_tmp(tmp_var_count++, dest.size-1, 0); tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(op_not_zero, ir_mov(counter, ir_cst((dest.size-1), counter.size-1, 0), addr)); // counter <- sizeof(op0)-1 block->add_instr(op_not_zero, ir_bcc(ir_cst(1, 31, 0) , ir_cst(loop_test, 31, 0), ir_none(), addr)); // loop test: while ( op0[i] == 0 ) block->add_instr(loop_test, ir_shr(tmp0, op0, counter, addr)); block->add_instr(loop_test, ir_bcc(x86_arg_extract(tmp0,0,0) , ir_cst(loop_exit, 31, 0), ir_cst(loop_body, 31, 0), addr)); // loop body: counter = counter - 1 block->add_instr(loop_body, ir_sub(counter, counter, ir_cst(1, counter.size-1, 0), addr)); block->add_instr(loop_body, ir_bcc(ir_cst(1, 31, 0) , ir_cst(loop_test, 31, 0), ir_none(), addr)); // loop exit: dest <- counter and ZF <- 0 x86_adjust_reg_assign(mode, addr, block, loop_exit, tmp_var_count, dest, counter); x86_set_zf(mode, op0, addr, block, loop_exit ); block->add_instr(loop_exit, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // 2°) Branch2: op0 == 0 // ZF <- 1 block->add_instr(op_is_zero, ir_mov(zf, ir_cst(1, zf.size-1, 0), addr)); block->add_instr(op_is_zero, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_bswap_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand dest, tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, pc, res; /* Get operand */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); if( dest.size == 64 ){ res = ir_tmp(tmp_var_count++, 63, 0); tmp0 = ir_tmp(tmp_var_count++, 7, 0); tmp1 = ir_tmp(tmp_var_count++, 7, 0); tmp2 = ir_tmp(tmp_var_count++, 7, 0); tmp3 = ir_tmp(tmp_var_count++, 7, 0); tmp4 = ir_tmp(tmp_var_count++, 7, 0); tmp5 = ir_tmp(tmp_var_count++, 7, 0); tmp6 = ir_tmp(tmp_var_count++, 7, 0); tmp7 = ir_tmp(tmp_var_count++, 7, 0); block->add_instr(bblkid, ir_mov(tmp0, x86_arg_extract(dest, 7, 0), addr)); block->add_instr(bblkid, ir_mov(tmp1, x86_arg_extract(dest, 15, 8), addr)); block->add_instr(bblkid, ir_mov(tmp2, x86_arg_extract(dest, 23, 16), addr)); block->add_instr(bblkid, ir_mov(tmp3, x86_arg_extract(dest, 31, 24), addr)); block->add_instr(bblkid, ir_mov(tmp4, x86_arg_extract(dest, 39,32), addr)); block->add_instr(bblkid, ir_mov(tmp5, x86_arg_extract(dest, 47, 40), addr)); block->add_instr(bblkid, ir_mov(tmp6, x86_arg_extract(dest, 55, 48), addr)); block->add_instr(bblkid, ir_mov(tmp7, x86_arg_extract(dest, 63, 56), addr)); block->add_instr(bblkid, ir_mov(x86_arg_extract(res, 63, 56), tmp0, addr)); block->add_instr(bblkid, ir_mov(x86_arg_extract(res, 55, 48), tmp1, addr)); block->add_instr(bblkid, ir_mov(x86_arg_extract(res, 47, 40), tmp2, addr)); block->add_instr(bblkid, ir_mov(x86_arg_extract(res, 39, 32), tmp3, addr)); block->add_instr(bblkid, ir_mov(x86_arg_extract(res, 31, 24), tmp4, addr)); block->add_instr(bblkid, ir_mov(x86_arg_extract(res, 23, 16), tmp5, addr)); block->add_instr(bblkid, ir_mov(x86_arg_extract(res, 15, 8), tmp6, addr)); block->add_instr(bblkid, ir_mov(x86_arg_extract(res, 7, 0), tmp7, addr)); x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, res); }else if( dest.size == 32 ){ res = ir_tmp(tmp_var_count++, 31, 0); tmp0 = ir_tmp(tmp_var_count++, 7, 0); tmp1 = ir_tmp(tmp_var_count++, 7, 0); tmp2 = ir_tmp(tmp_var_count++, 7, 0); tmp3 = ir_tmp(tmp_var_count++, 7, 0); block->add_instr(bblkid, ir_mov(tmp0, x86_arg_extract(dest, 7, 0), addr)); block->add_instr(bblkid, ir_mov(tmp1, x86_arg_extract(dest, 15, 8), addr)); block->add_instr(bblkid, ir_mov(tmp2, x86_arg_extract(dest, 23, 16), addr)); block->add_instr(bblkid, ir_mov(tmp3, x86_arg_extract(dest, 31, 24), addr)); block->add_instr(bblkid, ir_mov(x86_arg_extract(res, 31, 24), tmp0, addr)); block->add_instr(bblkid, ir_mov(x86_arg_extract(res, 23, 16), tmp1, addr)); block->add_instr(bblkid, ir_mov(x86_arg_extract(res, 15, 8), tmp2, addr)); block->add_instr(bblkid, ir_mov(x86_arg_extract(res, 7, 0), tmp3, addr)); x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, res); }else{ throw runtime_exception("X86 BSWAP translation: accepts operands of size 32 or 64 only"); } // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); } inline void x86_bt_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand base, off, cf, pc, tmp0; cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); /* Get operands */ base = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); off = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); tmp0 = ir_tmp(tmp_var_count++, base.size-1, 0); /* cf <- bit(base, off % {16/32/64}) */ block->add_instr(bblkid, ir_mod(tmp0, off, ir_cst(base.size, off.size-1, 0), addr)); block->add_instr(bblkid, ir_shr(tmp0, base, tmp0, addr)); block->add_instr(bblkid, ir_bisz(cf, x86_arg_extract(tmp0,0,0), ir_cst(0, cf.size-1, 0), addr)); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_btc_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand dest, base, off, cf, pc, tmp0, tmp1; cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); base = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); off = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); tmp0 = ir_tmp(tmp_var_count++, base.size-1, 0); tmp1 = ir_tmp(tmp_var_count++, base.size-1, 0); /* cf <- bit(base, off % {16/32/64}) */ block->add_instr(bblkid, ir_mod(tmp0, off, ir_cst(base.size, off.size-1, 0), addr)); block->add_instr(bblkid, ir_shr(tmp1, base, tmp0, addr)); block->add_instr(bblkid, ir_bisz(cf, x86_arg_extract(tmp1,0,0), ir_cst(0, cf.size-1, 0), addr)); /* invert bit(base, off % ... )*/ block->add_instr(bblkid, ir_shl(tmp1, ir_cst(1, tmp0.size-1, 0), tmp0, addr)); block->add_instr(bblkid, ir_xor(tmp1, base, tmp1, addr)); /* Set the bit in the destination */ /* If the add is written in memory */ if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(bblkid, ir_stm(dest, tmp1, addr)); /* Else direct register assign */ }else{ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, tmp1); } // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_btr_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand dest, base, off, cf, pc, tmp0, tmp1; cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); base = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); off = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); tmp0 = ir_tmp(tmp_var_count++, base.size-1, 0); tmp1 = ir_tmp(tmp_var_count++, base.size-1, 0); /* cf <- bit(base, off % {16/32/64}) */ block->add_instr(bblkid, ir_mod(tmp0, off, ir_cst(base.size, off.size-1, 0), addr)); block->add_instr(bblkid, ir_shr(tmp1, base, tmp0, addr)); block->add_instr(bblkid, ir_bisz(cf, x86_arg_extract(tmp1,0,0), ir_cst(0, cf.size-1, 0), addr)); /* bit(base, off % ... ) <- 0 */ block->add_instr(bblkid, ir_shl(tmp1, ir_cst(1, tmp0.size-1, 0), tmp0, addr)); block->add_instr(bblkid, ir_not(tmp1, tmp1, addr)); block->add_instr(bblkid, ir_and(tmp1, base, tmp1, addr)); /* Set the bit in the destination */ /* If the add is written in memory */ if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(bblkid, ir_stm(dest, tmp1, addr)); /* Else direct register assign */ }else{ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, tmp1); } // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_bts_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand dest, base, off, cf, pc, tmp0, tmp1; cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); base = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); off = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); tmp0 = ir_tmp(tmp_var_count++, base.size-1, 0); tmp1 = ir_tmp(tmp_var_count++, base.size-1, 0); /* cf <- bit(base, off % {16/32/64}) */ block->add_instr(bblkid, ir_mod(tmp0, off, ir_cst(base.size, off.size-1, 0), addr)); block->add_instr(bblkid, ir_shr(tmp1, base, tmp0, addr)); block->add_instr(bblkid, ir_bisz(cf, x86_arg_extract(tmp1,0,0), ir_cst(0, cf.size-1, 0), addr)); /* bit(base, off % ... ) <- 1 */ block->add_instr(bblkid, ir_shl(tmp1, ir_cst(1, tmp0.size-1, 0), tmp0, addr)); block->add_instr(bblkid, ir_or(tmp1, base, tmp1, addr)); /* Set the bit in the destination */ /* If the add is written in memory */ if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(bblkid, ir_stm(dest, tmp1, addr)); /* Else direct register assign */ }else{ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, tmp1); } // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_bzhi_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand dest, op0, op1, cf, of, pc, index, tmp0, tmp1, opsize, res; IRBasicBlockId index_too_big = block->new_bblock(), index_ok = block->new_bblock(), end = block->new_bblock(); /* Get operands */ cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[2]), block, bblkid, tmp_var_count, true); index = ir_tmp(tmp_var_count++, dest.size-1, 0); tmp0 = ir_tmp(tmp_var_count++, dest.size-1, 0); res = ir_tmp(tmp_var_count++, dest.size-1, 0); /* index <- op1[7:0] * dest <- op0 * dest[size(dest)-1:index] <- 0 * cf = 1 iff index > size(dest)-1 */ // Get index block->add_instr(bblkid, ir_mov(index, op1, addr)); block->add_instr(bblkid, ir_and(index, index, ir_cst(0xff, index.size-1, 0), addr)); // Compare index and size operands opsize = ir_cst(dest.size, dest.size-1, 0); block->add_instr(bblkid, ir_sub(tmp0, opsize, ir_cst(1, opsize.size-1, 0), addr)); block->add_instr(bblkid, ir_sub(tmp0, tmp0, index, addr)); block->add_instr(bblkid, ir_bcc(x86_arg_extract(tmp0, tmp0.size-1, tmp0.size-1), ir_cst(index_too_big, 31, 0), ir_cst(index_ok, 31, 0), addr)); // 1°) Index > size operands -1 block->add_instr(index_too_big, ir_mov(cf, ir_cst(1, cf.size-1, 0), addr)); block->add_instr(index_too_big, ir_mov(res, op0, addr)); x86_adjust_reg_assign(mode, addr, block, index_too_big, tmp_var_count, dest, res); block->add_instr(index_too_big, ir_bcc(ir_cst(1, 31, 0), ir_cst(end, 31, 0), ir_none(), addr)); // 2°) Index < size operands tmp1 = ir_tmp(tmp_var_count++, dest.size-1, 0); block->add_instr(index_ok, ir_mov(cf, ir_cst(0, cf.size-1, 0), addr )); // Get mask size(dest)-1 .. index block->add_instr(index_ok, ir_shl(tmp0, ir_cst(1, index.size-1, 0), index, addr)); block->add_instr(index_ok, ir_neg(tmp1, tmp0, addr)); block->add_instr(index_ok, ir_or(tmp1, tmp1, tmp0, addr)); block->add_instr(index_ok, ir_not(tmp1, tmp1, addr)); // Mask res block->add_instr(index_ok, ir_mov(res, dest, addr)); block->add_instr(index_ok, ir_and(res, op0, tmp1, addr)); x86_adjust_reg_assign(mode, addr, block, index_ok, tmp_var_count, dest, res); block->add_instr(index_ok, ir_bcc(ir_cst(1, 31, 0), ir_cst(end, 31, 0), ir_none(), addr)); // 3° ) Common end: set flags and pc // OF cleared block->add_instr(end, ir_mov(of, ir_cst(0, of.size-1, 0), addr )); // Set zf, cf x86_set_sf(mode, res, addr, block, end); x86_set_zf(mode, res, addr, block, end); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_call_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, sp, pc; /* Increment program counter first because * the call is maybe relative to EIP/RIP */ pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.high, 0), addr)); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); sp = (mode == CPUMode::X86)? ir_var(X86_ESP, 31, 0): ir_var(X64_RSP, 63, 0); /* Get and push next instruction address */ block->add_instr(bblkid, ir_sub(sp, sp, ir_cst(pc.size/8, pc.size-1, 0), addr)); block->add_instr(bblkid, ir_stm(sp, pc, addr)); /* Jump to called address */ block->add_instr(bblkid, ir_jcc(ir_cst(1, pc.size-1, 0), op0, ir_none(), addr)); return; } inline void x86_cbw_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand tmp0, reg, pc; IRBasicBlockId ext0 = block->new_bblock(), ext1 = block->new_bblock(), end = block->new_bblock(); reg = (mode==CPUMode::X86)? ir_var(X86_EAX, 31, 0) : ir_var(X64_RAX, 63, 0); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); /* ax <- sign_extend(al) */ block->add_instr(bblkid, ir_bcc(x86_arg_extract(reg, 7, 7), ir_cst(ext1, 31, 0), ir_cst(ext0, 31, 0), addr)); // extend 1 block->add_instr(ext1, ir_mov(x86_arg_extract(reg, 15, 8), ir_cst(0xff, 7, 0), addr)); block->add_instr(ext1, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // extend 0 block->add_instr(ext0, ir_mov(x86_arg_extract(reg, 15, 8), ir_cst(0x0, 7, 0), addr)); block->add_instr(ext0, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cdq_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand tmp0, reg_a, reg_d, pc, cst0, cst1; IRBasicBlockId ext0 = block->new_bblock(), ext1 = block->new_bblock(), end = block->new_bblock(); reg_a = (mode==CPUMode::X86)? ir_var(X86_EAX, 31, 0) : ir_var(X64_RAX, 63, 0); reg_d = (mode==CPUMode::X86)? ir_var(X86_EDX, 31, 0) : ir_var(X64_RDX, 63, 0); reg_d = x86_arg_extract(reg_d, 31, 0); cst1 = ir_cst(0xffffffff, 31, 0); cst0 = ir_cst(0, 31, 0); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); /* edx <- replicate(eax[31]) */ block->add_instr(bblkid, ir_bcc(x86_arg_extract(reg_a, 31, 31), ir_cst(ext1, 31, 0), ir_cst(ext0, 31, 0), addr)); // extend 1 x86_adjust_reg_assign(mode, addr, block, ext1, tmp_var_count, reg_d, cst1); block->add_instr(ext1, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // extend 0 x86_adjust_reg_assign(mode, addr, block, ext0, tmp_var_count, reg_d, cst0); block->add_instr(ext0, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cdqe_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand tmp0, reg, pc; if( mode == CPUMode::X86 ){ throw runtime_exception("CDQE: invalid instruction in X86 mode"); } IRBasicBlockId ext0 = block->new_bblock(), ext1 = block->new_bblock(), end = block->new_bblock(); reg = ir_var(X64_RAX, 63, 0); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); /* rax <- sign_extend(eax) */ block->add_instr(bblkid, ir_bcc(x86_arg_extract(reg, 31, 31), ir_cst(ext1, 31, 0), ir_cst(ext0, 31, 0), addr)); // extend 1 block->add_instr(ext1, ir_mov(x86_arg_extract(reg, 63, 32), ir_cst(0xffffffff, 31, 0), addr)); block->add_instr(ext1, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // extend 0 block->add_instr(ext0, ir_mov(x86_arg_extract(reg, 63, 32), ir_cst(0x0, 31, 0), addr)); block->add_instr(ext0, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_clc_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand cf, pc; cf = (mode==CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // cf <- 0 block->add_instr(bblkid, ir_mov(cf, ir_cst(0x0, cf.size-1, 0), addr)); return; } inline void x86_cld_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand df, pc; df = (mode==CPUMode::X86)? ir_var(X86_DF, 31, 0) : ir_var(X64_DF, 63, 0); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // df <- 0 block->add_instr(bblkid, ir_mov(df, ir_cst(0x0, df.size-1, 0), addr)); return; } inline void x86_cli_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand if_flag, pc; if_flag = (mode==CPUMode::X86)? ir_var(X86_IF, 31, 0) : ir_var(X64_IF, 63, 0); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // if_flag <- 0 block->add_instr(bblkid, ir_mov(if_flag, ir_cst(0x0, if_flag.size-1, 0), addr)); return; } inline void x86_cmc_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand cf, pc; cf = (mode==CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // complement cf block->add_instr(bblkid, ir_xor(cf, cf, ir_cst(0x1, cf.size-1, 0), addr)); return; } inline void x86_cmova_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand cf, zf, pc, tmp0, tmp1, op0, op1; IRBasicBlockId do_mov = block->new_bblock(), dont_mov = block->new_bblock(), end = block->new_bblock(); cf = (mode==CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); zf = (mode==CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); tmp0 = ir_tmp(tmp_var_count++, 0, 0); tmp1 = ir_tmp(tmp_var_count++, 0, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if CF = 0 and ZF = 0 block->add_instr(bblkid, ir_not(tmp0, cf, addr)); block->add_instr(bblkid, ir_not(tmp1, zf, addr)); block->add_instr(bblkid, ir_and(tmp1, tmp1, tmp0, addr)); block->add_instr(bblkid, ir_bcc(tmp1, ir_cst(do_mov,31, 0), ir_cst(dont_mov, 31, 0), addr)); // do mov x86_adjust_reg_assign(mode, addr, block, do_mov, tmp_var_count, op0, op1); block->add_instr(do_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont mov - do nothing /* but still assign op0 to itself if 64 bits (because in 64 bits, the instruction * clears the upper bits when operands are 32 bits, even when the condition * is false */ if( mode == CPUMode::X64 && op0.size == 32 ){ x86_adjust_reg_assign(mode, addr, block, dont_mov, tmp_var_count, op0, op0); } block->add_instr(dont_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cmovae_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand cf, pc, op0, op1; IRBasicBlockId do_mov = block->new_bblock(), dont_mov = block->new_bblock(), end = block->new_bblock(); cf = (mode==CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if CF = 0 block->add_instr(bblkid, ir_bcc(cf, ir_cst(dont_mov,31, 0), ir_cst(do_mov, 31, 0), addr)); // do mov x86_adjust_reg_assign(mode, addr, block, do_mov, tmp_var_count, op0, op1); block->add_instr(do_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont mov - do nothing /* but still assign op0 to itself if 64 bits (because in 64 bits, the instruction * clears the upper bits when operands are 32 bits, even when the condition * is false */ if( mode == CPUMode::X64 && op0.size == 32 ){ x86_adjust_reg_assign(mode, addr, block, dont_mov, tmp_var_count, op0, op0); } block->add_instr(dont_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cmovb_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand cf, pc, op0, op1; IRBasicBlockId do_mov = block->new_bblock(), dont_mov = block->new_bblock(), end = block->new_bblock(); cf = (mode==CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if CF = 1 block->add_instr(bblkid, ir_bcc(cf, ir_cst(do_mov,31, 0), ir_cst(dont_mov, 31, 0), addr)); // do mov x86_adjust_reg_assign(mode, addr, block, do_mov, tmp_var_count, op0, op1); block->add_instr(do_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont mov - do nothing /* but still assign op0 to itself if 64 bits (because in 64 bits, the instruction * clears the upper bits when operands are 32 bits, even when the condition * is false */ if( mode == CPUMode::X64 && op0.size == 32 ){ x86_adjust_reg_assign(mode, addr, block, dont_mov, tmp_var_count, op0, op0); } block->add_instr(dont_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cmovbe_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand cf, zf, pc, tmp0, op0, op1; IRBasicBlockId do_mov = block->new_bblock(), dont_mov = block->new_bblock(), end = block->new_bblock(); cf = (mode==CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); zf = (mode==CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); tmp0 = ir_tmp(tmp_var_count++, 0, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if CF = 1 or ZF = 1 block->add_instr(bblkid, ir_or(tmp0, cf, zf, addr)); block->add_instr(bblkid, ir_bcc(tmp0, ir_cst(do_mov,31, 0), ir_cst(dont_mov, 31, 0), addr)); // do mov x86_adjust_reg_assign(mode, addr, block, do_mov, tmp_var_count, op0, op1); block->add_instr(do_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont mov - do nothing /* but still assign op0 to itself if 64 bits (because in 64 bits, the instruction * clears the upper bits when operands are 32 bits, even when the condition * is false */ if( mode == CPUMode::X64 && op0.size == 32 ){ x86_adjust_reg_assign(mode, addr, block, dont_mov, tmp_var_count, op0, op0); } block->add_instr(dont_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cmove_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand zf, pc, op0, op1; IRBasicBlockId do_mov = block->new_bblock(), dont_mov = block->new_bblock(), end = block->new_bblock(); zf = (mode==CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if zf = 1 block->add_instr(bblkid, ir_bcc(zf, ir_cst(do_mov,31, 0), ir_cst(dont_mov, 31, 0), addr)); // do mov x86_adjust_reg_assign(mode, addr, block, do_mov, tmp_var_count, op0, op1); block->add_instr(do_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont mov - do nothing /* but still assign op0 to itself if 64 bits (because in 64 bits, the instruction * clears the upper bits when operands are 32 bits, even when the condition * is false */ if( mode == CPUMode::X64 && op0.size == 32 ){ x86_adjust_reg_assign(mode, addr, block, dont_mov, tmp_var_count, op0, op0); } block->add_instr(dont_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cmovg_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand sf, of, zf, pc, tmp0, tmp1, op0, op1; IRBasicBlockId do_mov = block->new_bblock(), dont_mov = block->new_bblock(), end = block->new_bblock(); sf = (mode==CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); zf = (mode==CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); of = (mode==CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); tmp0 = ir_tmp(tmp_var_count++, 0, 0); tmp1 = ir_tmp(tmp_var_count++, 0, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if ZF = 0 and OF = SF block->add_instr(bblkid, ir_xor(tmp0, sf, of, addr)); block->add_instr(bblkid, ir_not(tmp0, tmp0, addr)); block->add_instr(bblkid, ir_not(tmp1, zf, addr)); block->add_instr(bblkid, ir_and(tmp1, tmp1, tmp0, addr)); block->add_instr(bblkid, ir_bcc(tmp1, ir_cst(do_mov,31, 0), ir_cst(dont_mov, 31, 0), addr)); // do mov x86_adjust_reg_assign(mode, addr, block, do_mov, tmp_var_count, op0, op1); block->add_instr(do_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont mov - do nothing /* but still assign op0 to itself if 64 bits (because in 64 bits, the instruction * clears the upper bits when operands are 32 bits, even when the condition * is false */ if( mode == CPUMode::X64 && op0.size == 32 ){ x86_adjust_reg_assign(mode, addr, block, dont_mov, tmp_var_count, op0, op0); } block->add_instr(dont_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cmovge_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand sf, of, zf, pc, tmp0, op0, op1; IRBasicBlockId do_mov = block->new_bblock(), dont_mov = block->new_bblock(), end = block->new_bblock(); sf = (mode==CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); of = (mode==CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); tmp0 = ir_tmp(tmp_var_count++, 0, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if OF = SF block->add_instr(bblkid, ir_xor(tmp0, sf, of, addr)); block->add_instr(bblkid, ir_not(tmp0, tmp0, addr)); block->add_instr(bblkid, ir_bcc(tmp0, ir_cst(do_mov,31, 0), ir_cst(dont_mov, 31, 0), addr)); // do mov x86_adjust_reg_assign(mode, addr, block, do_mov, tmp_var_count, op0, op1); block->add_instr(do_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont mov - do nothing /* but still assign op0 to itself if 64 bits (because in 64 bits, the instruction * clears the upper bits when operands are 32 bits, even when the condition * is false */ if( mode == CPUMode::X64 && op0.size == 32 ){ x86_adjust_reg_assign(mode, addr, block, dont_mov, tmp_var_count, op0, op0); } block->add_instr(dont_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cmovl_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand sf, of, zf, pc, tmp0, op0, op1; IRBasicBlockId do_mov = block->new_bblock(), dont_mov = block->new_bblock(), end = block->new_bblock(); sf = (mode==CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); of = (mode==CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); tmp0 = ir_tmp(tmp_var_count++, 0, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if OF != SF block->add_instr(bblkid, ir_xor(tmp0, sf, of, addr)); block->add_instr(bblkid, ir_bcc(tmp0, ir_cst(do_mov,31, 0), ir_cst(dont_mov, 31, 0), addr)); // do mov x86_adjust_reg_assign(mode, addr, block, do_mov, tmp_var_count, op0, op1); block->add_instr(do_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont mov - do nothing /* but still assign op0 to itself if 64 bits (because in 64 bits, the instruction * clears the upper bits when operands are 32 bits, even when the condition * is false */ if( mode == CPUMode::X64 && op0.size == 32 ){ x86_adjust_reg_assign(mode, addr, block, dont_mov, tmp_var_count, op0, op0); } block->add_instr(dont_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cmovle_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand sf, of, zf, pc, tmp0, op0, op1; IRBasicBlockId do_mov = block->new_bblock(), dont_mov = block->new_bblock(), end = block->new_bblock(); sf = (mode==CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); zf = (mode==CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); of = (mode==CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); tmp0 = ir_tmp(tmp_var_count++, 0, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if ZF = 1 or OF != SF block->add_instr(bblkid, ir_xor(tmp0, sf, of, addr)); block->add_instr(bblkid, ir_or(tmp0, x86_arg_extract(zf, 0, 0), tmp0, addr)); block->add_instr(bblkid, ir_bcc(tmp0, ir_cst(do_mov,31, 0), ir_cst(dont_mov, 31, 0), addr)); // do mov x86_adjust_reg_assign(mode, addr, block, do_mov, tmp_var_count, op0, op1); block->add_instr(do_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont mov - do nothing /* but still assign op0 to itself if 64 bits (because in 64 bits, the instruction * clears the upper bits when operands are 32 bits, even when the condition * is false */ if( mode == CPUMode::X64 && op0.size == 32 ){ x86_adjust_reg_assign(mode, addr, block, dont_mov, tmp_var_count, op0, op0); } block->add_instr(dont_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cmovne_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand zf, pc, op0, op1; IRBasicBlockId do_mov = block->new_bblock(), dont_mov = block->new_bblock(), end = block->new_bblock(); zf = (mode==CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if ZF = 0 block->add_instr(bblkid, ir_bcc(zf, ir_cst(dont_mov,31, 0), ir_cst(do_mov, 31, 0), addr)); // do mov x86_adjust_reg_assign(mode, addr, block, do_mov, tmp_var_count, op0, op1); block->add_instr(do_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont mov - do nothing /* but still assign op0 to itself if 64 bits (because in 64 bits, the instruction * clears the upper bits when operands are 32 bits, even when the condition * is false */ if( mode == CPUMode::X64 && op0.size == 32 ){ x86_adjust_reg_assign(mode, addr, block, dont_mov, tmp_var_count, op0, op0); } block->add_instr(dont_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cmovno_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand of, pc, op0, op1; IRBasicBlockId do_mov = block->new_bblock(), dont_mov = block->new_bblock(), end = block->new_bblock(); of = (mode==CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if OF = 0 block->add_instr(bblkid, ir_bcc(of, ir_cst(dont_mov,31, 0), ir_cst(do_mov, 31, 0), addr)); // do mov x86_adjust_reg_assign(mode, addr, block, do_mov, tmp_var_count, op0, op1); block->add_instr(do_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont mov - do nothing /* but still assign op0 to itself if 64 bits (because in 64 bits, the instruction * clears the upper bits when operands are 32 bits, even when the condition * is false */ if( mode == CPUMode::X64 && op0.size == 32 ){ x86_adjust_reg_assign(mode, addr, block, dont_mov, tmp_var_count, op0, op0); } block->add_instr(dont_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cmovnp_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pf, pc, op0, op1; IRBasicBlockId do_mov = block->new_bblock(), dont_mov = block->new_bblock(), end = block->new_bblock(); pf = (mode==CPUMode::X86)? ir_var(X86_PF, 31, 0) : ir_var(X64_PF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if PF = 0 block->add_instr(bblkid, ir_bcc(pf, ir_cst(dont_mov,31, 0), ir_cst(do_mov, 31, 0), addr)); // do mov x86_adjust_reg_assign(mode, addr, block, do_mov, tmp_var_count, op0, op1); block->add_instr(do_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont mov - do nothing /* but still assign op0 to itself if 64 bits (because in 64 bits, the instruction * clears the upper bits when operands are 32 bits, even when the condition * is false */ if( mode == CPUMode::X64 && op0.size == 32 ){ x86_adjust_reg_assign(mode, addr, block, dont_mov, tmp_var_count, op0, op0); } block->add_instr(dont_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cmovns_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand sf, pc, op0, op1; IRBasicBlockId do_mov = block->new_bblock(), dont_mov = block->new_bblock(), end = block->new_bblock(); sf = (mode==CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if SF = 0 block->add_instr(bblkid, ir_bcc(sf, ir_cst(dont_mov,31, 0), ir_cst(do_mov, 31, 0), addr)); // do mov x86_adjust_reg_assign(mode, addr, block, do_mov, tmp_var_count, op0, op1); block->add_instr(do_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont mov - do nothing /* but still assign op0 to itself if 64 bits (because in 64 bits, the instruction * clears the upper bits when operands are 32 bits, even when the condition * is false */ if( mode == CPUMode::X64 && op0.size == 32 ){ x86_adjust_reg_assign(mode, addr, block, dont_mov, tmp_var_count, op0, op0); } block->add_instr(dont_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cmovo_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand of, pc, op0, op1; IRBasicBlockId do_mov = block->new_bblock(), dont_mov = block->new_bblock(), end = block->new_bblock(); of = (mode==CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if OF = 1 block->add_instr(bblkid, ir_bcc(of, ir_cst(do_mov,31, 0), ir_cst(dont_mov, 31, 0), addr)); // do mov x86_adjust_reg_assign(mode, addr, block, do_mov, tmp_var_count, op0, op1); block->add_instr(do_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont mov - do nothing /* but still assign op0 to itself if 64 bits (because in 64 bits, the instruction * clears the upper bits when operands are 32 bits, even when the condition * is false */ if( mode == CPUMode::X64 && op0.size == 32 ){ x86_adjust_reg_assign(mode, addr, block, dont_mov, tmp_var_count, op0, op0); } block->add_instr(dont_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cmovp_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pf, pc, op0, op1; IRBasicBlockId do_mov = block->new_bblock(), dont_mov = block->new_bblock(), end = block->new_bblock(); pf = (mode==CPUMode::X86)? ir_var(X86_PF, 31, 0) : ir_var(X64_PF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if OF = 1 block->add_instr(bblkid, ir_bcc(pf, ir_cst(do_mov,31, 0), ir_cst(dont_mov, 31, 0), addr)); // do mov x86_adjust_reg_assign(mode, addr, block, do_mov, tmp_var_count, op0, op1); block->add_instr(do_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont mov - do nothing /* but still assign op0 to itself if 64 bits (because in 64 bits, the instruction * clears the upper bits when operands are 32 bits, even when the condition * is false */ if( mode == CPUMode::X64 && op0.size == 32 ){ x86_adjust_reg_assign(mode, addr, block, dont_mov, tmp_var_count, op0, op0); } block->add_instr(dont_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cmovs_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand sf, pc, op0, op1; IRBasicBlockId do_mov = block->new_bblock(), dont_mov = block->new_bblock(), end = block->new_bblock(); sf = (mode==CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if SF = 1 block->add_instr(bblkid, ir_bcc(sf, ir_cst(do_mov,31, 0), ir_cst(dont_mov, 31, 0), addr)); // do mov x86_adjust_reg_assign(mode, addr, block, do_mov, tmp_var_count, op0, op1); block->add_instr(do_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont mov - do nothing /* but still assign op0 to itself if 64 bits (because in 64 bits, the instruction * clears the upper bits when operands are 32 bits, even when the condition * is false */ if( mode == CPUMode::X64 && op0.size == 32 ){ x86_adjust_reg_assign(mode, addr, block, dont_mov, tmp_var_count, op0, op0); } block->add_instr(dont_mov, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cmp_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, op0, op1, tmp; /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); // Check if op1 is a imm and needs sign extend if( op1.size < op0.size && op1.is_cst()){ op1 = ir_cst(cst_sign_extend(op1.size, op1.cst()), op0.size-1, 0); } // tmp <- op0 - op1 tmp = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_sub(tmp, op0, op1, addr)); // Update flags x86_set_pf( mode, tmp, addr, block, bblkid, tmp_var_count ); x86_set_zf( mode, tmp, addr, block, bblkid ); x86_set_sf( mode, tmp, addr, block, bblkid ); x86_sub_set_of( mode, op0, op1, tmp, addr, block, bblkid, tmp_var_count ); x86_sub_set_cf( mode, op0, op1, tmp, addr, block, bblkid, tmp_var_count ); x86_sub_set_af( mode, op0, op1, tmp, addr, block, bblkid, tmp_var_count ); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_cmpsb_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, tmp0, tmp1, tmp2, si, di, df; IRBasicBlockId inc, dec, end, prefix_start; /* Get operands */ si = (mode == CPUMode::X86) ? ir_var(X86_ESI, 31, 0) : ir_var(X64_RSI, 63, 0); di = (mode == CPUMode::X86) ? ir_var(X86_EDI, 31, 0) : ir_var(X64_RDI, 63, 0); df = (mode == CPUMode::X86) ? ir_var(X86_DF, 31, 0) : ir_var(X64_DF, 63, 0); prefix_start = _x86_init_prefix(mode, instr, addr, block, bblkid); inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); /* Read bytes from memory and compare them */ tmp0 = ir_tmp(tmp_var_count++, 7, 0); tmp1 = ir_tmp(tmp_var_count++, 7, 0); tmp2 = ir_tmp(tmp_var_count++, 7, 0); block->add_instr(bblkid, ir_ldm(tmp0, si, addr)); block->add_instr(bblkid, ir_ldm(tmp1, di, addr)); block->add_instr(bblkid, ir_sub(tmp2, tmp0, tmp1, addr)); // Update flags x86_set_pf( mode, tmp2, addr, block, bblkid, tmp_var_count ); x86_set_zf( mode, tmp2, addr, block, bblkid ); x86_set_sf( mode, tmp2, addr, block, bblkid ); x86_sub_set_of( mode, tmp0, tmp1, tmp2, addr, block, bblkid, tmp_var_count ); x86_sub_set_cf( mode, tmp0, tmp1, tmp2, addr, block, bblkid, tmp_var_count ); x86_sub_set_af( mode, tmp0, tmp1, tmp2, addr, block, bblkid, tmp_var_count ); // Increment or decrement ESI/EDI according to DF block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, 31, 0), ir_cst(inc, 31, 0), addr)); block->add_instr(inc, ir_add(si, si, ir_cst(1, si.size-1, 0), addr)); block->add_instr(inc, ir_add(di, di, ir_cst(1, di.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); block->add_instr(dec, ir_sub(si, si, ir_cst(1, si.size-1, 0), addr)); block->add_instr(dec, ir_sub(di, di, ir_cst(1, di.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Add prefix if any */ _x86_end_prefix(mode, instr, addr, block, prefix_start, end, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_cmpsd_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, tmp0, tmp1, tmp2, si, di, df; IRBasicBlockId inc, dec, end, prefix_start; /* Get operands */ si = (mode == CPUMode::X86) ? ir_var(X86_ESI, 31, 0) : ir_var(X64_RSI, 63, 0); di = (mode == CPUMode::X86) ? ir_var(X86_EDI, 31, 0) : ir_var(X64_RDI, 63, 0); df = (mode == CPUMode::X86) ? ir_var(X86_DF, 31, 0) : ir_var(X64_DF, 63, 0); prefix_start = _x86_init_prefix(mode, instr, addr, block, bblkid); inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); /* Read dwords from memory and compare them */ tmp0 = ir_tmp(tmp_var_count++, 31, 0); tmp1 = ir_tmp(tmp_var_count++, 31, 0); tmp2 = ir_tmp(tmp_var_count++, 31, 0); block->add_instr(bblkid, ir_ldm(tmp0, si, addr)); block->add_instr(bblkid, ir_ldm(tmp1, di, addr)); block->add_instr(bblkid, ir_sub(tmp2, tmp0, tmp1, addr)); // Update flags x86_set_pf( mode, tmp2, addr, block, bblkid, tmp_var_count ); x86_set_zf( mode, tmp2, addr, block, bblkid ); x86_set_sf( mode, tmp2, addr, block, bblkid ); x86_sub_set_of( mode, tmp0, tmp1, tmp2, addr, block, bblkid, tmp_var_count ); x86_sub_set_cf( mode, tmp0, tmp1, tmp2, addr, block, bblkid, tmp_var_count ); x86_sub_set_af( mode, tmp0, tmp1, tmp2, addr, block, bblkid, tmp_var_count ); // Increment or decrement ESI/EDI according to DF block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, 31, 0), ir_cst(inc, 31, 0), addr)); block->add_instr(inc, ir_add(si, si, ir_cst(4, si.size-1, 0), addr)); block->add_instr(inc, ir_add(di, di, ir_cst(4, di.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); block->add_instr(dec, ir_sub(si, si, ir_cst(4, si.size-1, 0), addr)); block->add_instr(dec, ir_sub(di, di, ir_cst(4, di.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Add prefix if any */ _x86_end_prefix(mode, instr, addr, block, prefix_start, end, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_cmpsq_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, tmp0, tmp1, tmp2, si, di, df; IRBasicBlockId inc, dec, end, prefix_start; if( mode == CPUMode::X86 ){ throw runtime_exception("CMPSQ: instruction is invalid in X86 mode"); } /* Get operands */ si = ir_var(X64_RSI, 63, 0); di = ir_var(X64_RDI, 63, 0); df = ir_var(X64_DF, 63, 0); prefix_start = _x86_init_prefix(mode, instr, addr, block, bblkid); inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); /* Read words from memory and compare them */ tmp0 = ir_tmp(tmp_var_count++, 63, 0); tmp1 = ir_tmp(tmp_var_count++, 63, 0); tmp2 = ir_tmp(tmp_var_count++, 63, 0); block->add_instr(bblkid, ir_ldm(tmp0, si, addr)); block->add_instr(bblkid, ir_ldm(tmp1, di, addr)); block->add_instr(bblkid, ir_sub(tmp2, tmp0, tmp1, addr)); // Update flags x86_set_pf( mode, tmp2, addr, block, bblkid, tmp_var_count ); x86_set_zf( mode, tmp2, addr, block, bblkid ); x86_set_sf( mode, tmp2, addr, block, bblkid ); x86_sub_set_of( mode, tmp0, tmp1, tmp2, addr, block, bblkid, tmp_var_count ); x86_sub_set_cf( mode, tmp0, tmp1, tmp2, addr, block, bblkid, tmp_var_count ); x86_sub_set_af( mode, tmp0, tmp1, tmp2, addr, block, bblkid, tmp_var_count ); // Increment or decrement ESI/EDI according to DF block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, 31, 0), ir_cst(inc, 31, 0), addr)); block->add_instr(inc, ir_add(si, si, ir_cst(8, si.size-1, 0), addr)); block->add_instr(inc, ir_add(di, di, ir_cst(8, di.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); block->add_instr(dec, ir_sub(si, si, ir_cst(8, si.size-1, 0), addr)); block->add_instr(dec, ir_sub(di, di, ir_cst(8, di.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Add prefix if any */ _x86_end_prefix(mode, instr, addr, block, prefix_start, end, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_cmpsw_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, tmp0, tmp1, tmp2, si, di, df; IRBasicBlockId inc, dec, end, prefix_start; /* Get operands */ si = (mode == CPUMode::X86) ? ir_var(X86_ESI, 31, 0) : ir_var(X64_RSI, 63, 0); di = (mode == CPUMode::X86) ? ir_var(X86_EDI, 31, 0) : ir_var(X64_RDI, 63, 0); df = (mode == CPUMode::X86) ? ir_var(X86_DF, 31, 0) : ir_var(X64_DF, 63, 0); prefix_start = _x86_init_prefix(mode, instr, addr, block, bblkid); inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); /* Read words from memory and compare them */ tmp0 = ir_tmp(tmp_var_count++, 15, 0); tmp1 = ir_tmp(tmp_var_count++, 15, 0); tmp2 = ir_tmp(tmp_var_count++, 15, 0); block->add_instr(bblkid, ir_ldm(tmp0, si, addr)); block->add_instr(bblkid, ir_ldm(tmp1, di, addr)); block->add_instr(bblkid, ir_sub(tmp2, tmp0, tmp1, addr)); // Update flags x86_set_pf( mode, tmp2, addr, block, bblkid, tmp_var_count ); x86_set_zf( mode, tmp2, addr, block, bblkid ); x86_set_sf( mode, tmp2, addr, block, bblkid ); x86_sub_set_of( mode, tmp0, tmp1, tmp2, addr, block, bblkid, tmp_var_count ); x86_sub_set_cf( mode, tmp0, tmp1, tmp2, addr, block, bblkid, tmp_var_count ); x86_sub_set_af( mode, tmp0, tmp1, tmp2, addr, block, bblkid, tmp_var_count ); // Increment or decrement ESI/EDI according to DF block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, 31, 0), ir_cst(inc, 31, 0), addr)); block->add_instr(inc, ir_add(si, si, ir_cst(2, si.size-1, 0), addr)); block->add_instr(inc, ir_add(di, di, ir_cst(2, di.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); block->add_instr(dec, ir_sub(si, si, ir_cst(2, si.size-1, 0), addr)); block->add_instr(dec, ir_sub(di, di, ir_cst(2, di.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Add prefix if any */ _x86_end_prefix(mode, instr, addr, block, prefix_start, end, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_cmpxchg_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, dest, op0, op1, ax, zf, tmp; IRBasicBlockId eq, neq, end; eq = block->new_bblock(); neq = block->new_bblock(); end = block->new_bblock(); /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); ax = (mode == CPUMode::X86) ? ir_var(X86_EAX, op0.size-1, 0) : ir_var(X64_RAX, op0.size-1, 0); zf = (mode == CPUMode::X86) ? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); /* Compare op0 and op1 */ tmp = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_sub(tmp, ax, op0, addr )); /* Set flags */ x86_set_pf(mode, tmp, addr, block, bblkid, tmp_var_count); x86_set_sf(mode, tmp, addr, block, bblkid ); x86_set_zf(mode, tmp, addr, block, bblkid ); x86_sub_set_af(mode, ax, op0, tmp, addr, block, bblkid, tmp_var_count); x86_sub_set_cf(mode, ax, op0, tmp, addr, block, bblkid, tmp_var_count); x86_sub_set_of(mode, ax, op0, tmp, addr, block, bblkid, tmp_var_count); /* Exchange values depending on zf */ block->add_instr(bblkid, ir_bcc(zf, ir_cst(eq, 31, 0), ir_cst(neq, 31, 0), addr)); if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(eq, ir_stm(op0, op1, addr)); }else{ x86_adjust_reg_assign(mode, addr, block, eq, tmp_var_count, op0, op1); } block->add_instr(eq, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); x86_adjust_reg_assign(mode, addr, block, neq, tmp_var_count, ax, op0); block->add_instr(neq, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cpuid_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid, int& tmp_var_count){ IRBasicBlockId leaf_0 = block->new_bblock(), end = block->new_bblock(); IROperand eax = (mode == CPUMode::X86)? ir_var(X86_EAX, 31, 0) : ir_var(X64_RAX, 63, 0); IROperand ebx = (mode == CPUMode::X86)? ir_var(X86_EBX, 31, 0) : ir_var(X64_RBX, 63, 0); IROperand ecx = (mode == CPUMode::X86)? ir_var(X86_ECX, 31, 0) : ir_var(X64_RCX, 63, 0); IROperand edx = (mode == CPUMode::X86)? ir_var(X86_EDX, 31, 0) : ir_var(X64_RDX, 63, 0); /* Test eax to know what cpuid leaf is requested */ block->add_instr(bblkid, ir_bcc(eax, ir_cst(end, 31, 0), ir_cst(leaf_0, 31, 0), addr)); /* Leaf 0 * Return the CPU's manufacturer ID string in ebx, edx and ecx * Set EAX to the higher supported leaf */ // Set registers to "GenuineIntel" x86_adjust_reg_assign(mode, addr, block, leaf_0, tmp_var_count, ebx, ir_cst(0x756e6547, 31, 0)); x86_adjust_reg_assign(mode, addr, block, leaf_0, tmp_var_count, edx, ir_cst(0x49656e69, 31, 0)); x86_adjust_reg_assign(mode, addr, block, leaf_0, tmp_var_count, ecx, ir_cst(0x6c65746e, 31, 0)); // Set eax to 0 because other leafs are not supported yet x86_adjust_reg_assign(mode, addr, block, leaf_0, tmp_var_count, eax, ir_cst(0, 31, 0)); block->add_instr(leaf_0, ir_bcc(ir_cst(1, 31, 0), ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cqo_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand tmp0, reg_a, reg_d, pc, cst0, cst1; if( mode == CPUMode::X86 ){ throw runtime_exception("CQO: invalid instruction in X86 mode"); } IRBasicBlockId ext0 = block->new_bblock(), ext1 = block->new_bblock(), end = block->new_bblock(); reg_a = ir_var(X64_RAX, 63, 0); reg_d = ir_var(X64_RDX, 63, 0); cst1 = ir_cst(0xffffffffffffffff, 63, 0); cst0 = ir_cst(0, 63, 0); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); /* edx <- replicate(eax[63]) */ block->add_instr(bblkid, ir_bcc(x86_arg_extract(reg_a, 63, 63), ir_cst(ext1, 31, 0), ir_cst(ext0, 31, 0), addr)); // extend 1 x86_adjust_reg_assign(mode, addr, block, ext1, tmp_var_count, reg_d, cst1); block->add_instr(ext1, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // extend 0 x86_adjust_reg_assign(mode, addr, block, ext0, tmp_var_count, reg_d, cst0); block->add_instr(ext0, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cwd_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand tmp0, reg_a, reg_d, pc; IRBasicBlockId ext0 = block->new_bblock(), ext1 = block->new_bblock(), end = block->new_bblock(); reg_a = (mode==CPUMode::X86)? ir_var(X86_EAX, 31, 0) : ir_var(X64_RAX, 63, 0); reg_d = (mode==CPUMode::X86)? ir_var(X86_EDX, 31, 0) : ir_var(X64_RDX, 63, 0); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); /* dx <- replicate(ax[15]) */ block->add_instr(bblkid, ir_bcc(x86_arg_extract(reg_a, 15, 15), ir_cst(ext1, 31, 0), ir_cst(ext0, 31, 0), addr)); // extend 1 block->add_instr(ext1, ir_mov(x86_arg_extract(reg_d, 15, 0), ir_cst(0xffff, 15, 0), addr)); block->add_instr(ext1, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // extend 0 block->add_instr(ext0, ir_mov(x86_arg_extract(reg_d, 15, 0), ir_cst(0x0, 15, 0), addr)); block->add_instr(ext0, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_cwde_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand tmp0, reg, pc; IRBasicBlockId ext0 = block->new_bblock(), ext1 = block->new_bblock(), end = block->new_bblock(); reg = (mode==CPUMode::X86)? ir_var(X86_EAX, 31, 0) : ir_var(X64_RAX, 63, 0); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); /* eax <- sign_extend(ax) */ block->add_instr(bblkid, ir_bcc(x86_arg_extract(reg, 15, 15), ir_cst(ext1, 31, 0), ir_cst(ext0, 31, 0), addr)); // extend 1 if( mode == CPUMode::X64 ){ // zero higher bits block->add_instr(ext1, ir_mov(x86_arg_extract(reg, 63, 32), ir_cst(0, 31, 0), addr)); } block->add_instr(ext1, ir_mov(x86_arg_extract(reg, 31, 16), ir_cst(0xffff, 15, 0), addr)); block->add_instr(ext1, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // extend 0 if( mode == CPUMode::X64 ){ // zero higher bits block->add_instr(ext0, ir_mov(x86_arg_extract(reg, 63, 32), ir_cst(0, 31, 0), addr)); } block->add_instr(ext0, ir_mov(x86_arg_extract(reg, 31, 16), ir_cst(0x0, 15, 0), addr)); block->add_instr(ext0, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_dec_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, dest, op0, tmp; /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); /* Decrement op0 */ tmp = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_sub(tmp, op0, ir_cst(1, op0.size-1, 0), addr )); /* Set flags (except CF) */ x86_set_pf(mode, tmp, addr, block, bblkid, tmp_var_count); x86_set_sf(mode, tmp, addr, block, bblkid ); x86_set_zf(mode, tmp, addr, block, bblkid ); x86_sub_set_af(mode, op0, ir_cst(1, op0.size-1, 0), tmp, addr, block, bblkid, tmp_var_count); x86_sub_set_of(mode, op0, ir_cst(1, op0.size-1, 0), tmp, addr, block, bblkid, tmp_var_count); /* Store result */ if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(bblkid, ir_stm(dest, tmp, addr)); }else{ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, tmp); } // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_div_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, op0, dividend, remainder, tmp, ax, dx, tmp_dividend, tmp_remainder; /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); ax = (mode == CPUMode::X86)? ir_var(X86_EAX, 31, 0) : ir_var(X64_RAX, 63, 0); dx = (mode == CPUMode::X86)? ir_var(X86_EDX, 31, 0) : ir_var(X64_RDX, 63, 0); tmp = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp_dividend = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp_remainder = ir_tmp(tmp_var_count++, op0.size-1, 0); if( op0.size == 8 ){ dividend = x86_arg_extract(ax, 7, 0); remainder = x86_arg_extract(ax, 15, 8); }else{ dividend = x86_arg_extract(ax, op0.size-1, 0); remainder = x86_arg_extract(dx, op0.size-1, 0); } /* Do the div */ block->add_instr(bblkid, ir_mov(tmp, x86_arg_extract(ax, op0.size-1, 0), addr)); block->add_instr(bblkid, ir_div(tmp_dividend, tmp , op0, addr )); block->add_instr(bblkid, ir_mod(tmp_remainder, tmp , op0, addr )); /* Assign results to registers */ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dividend, tmp_dividend); x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, remainder, tmp_remainder); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_idiv_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, op0, ax, dx, tmp, dividend, remainder, tmp_dividend, tmp_remainder; /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); ax = (mode == CPUMode::X86)? ir_var(X86_EAX, 31, 0) : ir_var(X64_RAX, 63, 0); dx = (mode == CPUMode::X86)? ir_var(X86_EDX, 31, 0) : ir_var(X64_RDX, 63, 0); tmp = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp_dividend = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp_remainder = ir_tmp(tmp_var_count++, op0.size-1, 0); if( op0.size == 8 ){ dividend = x86_arg_extract(ax, 7, 0); remainder = x86_arg_extract(ax, 15, 8); }else{ dividend = x86_arg_extract(ax, op0.size-1, 0); remainder = x86_arg_extract(dx, op0.size-1, 0); } /* Quotient in *ax, remainder in *dx */ block->add_instr(bblkid, ir_mov(tmp, x86_arg_extract(ax, op0.size-1, 0), addr)); block->add_instr(bblkid, ir_sdiv(tmp_dividend, tmp , op0, addr )); block->add_instr(bblkid, ir_smod(tmp_remainder, tmp , op0, addr )); /* Assign results to registers */ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dividend, tmp_dividend); x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, remainder, tmp_remainder); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_imul_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, op0, op1, op2, lower, higher, tmp0, tmp1, ax, tmp2, tmp3, tmp4, cf, of; cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0): ir_var(X64_CF, 63, 0); of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0): ir_var(X64_OF, 63, 0); /* One-operand form */ if( instr->detail->x86.op_count == 1 ){ /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); ax = (mode == CPUMode::X86)? ir_var(X86_EAX, op0.size-1, 0): ir_var(X64_RAX, op0.size-1, 0); if( op0.size == 8 ){ lower = (mode == CPUMode::X86)? ir_var(X86_EAX, 7, 0): ir_var(X64_RAX, 7, 0); higher = (mode == CPUMode::X86)? ir_var(X86_EAX, 15, 8): ir_var(X64_RAX, 15, 8); }else{ lower = (mode == CPUMode::X86)? ir_var(X86_EAX, op0.size-1, 0): ir_var(X64_RAX, op0.size-1, 0); higher = (mode == CPUMode::X86)? ir_var(X86_EDX, op0.size-1, 0): ir_var(X64_RDX, op0.size-1, 0); } tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp1 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp2 = ir_tmp(tmp_var_count++, 0, 0); tmp3 = ir_tmp(tmp_var_count++, 0, 0); /* Do the multiplication */ block->add_instr(bblkid, ir_smull(tmp0, ax, op0, addr)); block->add_instr(bblkid, ir_smulh(tmp1, ax, op0, addr)); x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, lower, tmp0); x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, higher, tmp1); /* Set OF and CF iff the higher:lower != signextend(lower) * SO we do * higher==0 && lower[n-1] == 0 * OR higher==0xfff.... && lower[n-1] == 1 */ block->add_instr(bblkid, ir_bisz(tmp2, tmp1, ir_cst(1, 0, 0), addr)); block->add_instr(bblkid, ir_not(tmp3, x86_arg_extract(tmp0, tmp0.size-1, tmp0.size-1), addr)); block->add_instr(bblkid, ir_and(tmp2, tmp2, tmp3, addr)); block->add_instr(bblkid, ir_not(tmp1, tmp1, addr)); block->add_instr(bblkid, ir_bisz(tmp3, tmp1, ir_cst(1, 0, 0), addr)); block->add_instr(bblkid, ir_and(tmp3, tmp3, x86_arg_extract(tmp0, tmp0.size-1, tmp0.size-1), addr)); block->add_instr(bblkid, ir_or(tmp3, tmp3, tmp2, addr)); block->add_instr(bblkid, ir_bisz(cf, tmp3, ir_cst(1, 0, 0), addr)); block->add_instr(bblkid, ir_bisz(of, tmp3, ir_cst(1, 0, 0), addr)); /* Two-operands form */ }else if( instr->detail->x86.op_count == 2){ /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp1 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp2 = ir_tmp(tmp_var_count++, 0, 0); tmp3 = ir_tmp(tmp_var_count++, 0, 0); /* Do the multiplication */ block->add_instr(bblkid, ir_smull(tmp0, op0, op1, addr)); block->add_instr(bblkid, ir_smulh(tmp1, op0, op1, addr)); x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, op0, tmp0); /* Set OF and CF iff the higher:lower != signextend(lower) * SO we do * higher==0 && lower[n-1] == 0 * OR higher==0xfff.... && lower[n-1] == 1 */ block->add_instr(bblkid, ir_bisz(tmp2, tmp1, ir_cst(1, 0, 0), addr)); block->add_instr(bblkid, ir_not(tmp3, x86_arg_extract(tmp0, tmp0.size-1, tmp0.size-1), addr)); block->add_instr(bblkid, ir_and(tmp2, tmp2, tmp3, addr)); block->add_instr(bblkid, ir_not(tmp1, tmp1, addr)); block->add_instr(bblkid, ir_bisz(tmp3, tmp1, ir_cst(1, 0, 0), addr)); block->add_instr(bblkid, ir_and(tmp3, tmp3, x86_arg_extract(tmp0, tmp0.size-1, tmp0.size-1), addr)); block->add_instr(bblkid, ir_or(tmp3, tmp3, tmp2, addr)); block->add_instr(bblkid, ir_bisz(cf, tmp3, ir_cst(1, 0, 0), addr)); block->add_instr(bblkid, ir_bisz(of, tmp3, ir_cst(1, 0, 0), addr)); /* Three-operands form */ }else{ /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); op2 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[2]), block, bblkid, tmp_var_count, true); if( op2.size == 8 ) op2 = ir_cst(op2.cst(), op1.size-1, 0); // Already sign extended in IROperand() constructor tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp1 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp2 = ir_tmp(tmp_var_count++, 0, 0); tmp3 = ir_tmp(tmp_var_count++, 0, 0); /* Do the multiplication */ block->add_instr(bblkid, ir_smull(tmp0, op1, op2, addr)); block->add_instr(bblkid, ir_smulh(tmp1, op1, op2, addr)); x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, op0, tmp0); /* Set OF and CF iff the higher:lower != signextend(lower) * SO we do * higher==0 && lower[n-1] == 0 * OR higher==0xfff.... && lower[n-1] == 1 */ block->add_instr(bblkid, ir_bisz(tmp2, tmp1, ir_cst(1, 0, 0), addr)); block->add_instr(bblkid, ir_not(tmp3, x86_arg_extract(tmp0, tmp0.size-1, tmp0.size-1), addr)); block->add_instr(bblkid, ir_and(tmp2, tmp2, tmp3, addr)); block->add_instr(bblkid, ir_not(tmp1, tmp1, addr)); block->add_instr(bblkid, ir_bisz(tmp3, tmp1, ir_cst(1, 0, 0), addr)); block->add_instr(bblkid, ir_and(tmp3, tmp3, x86_arg_extract(tmp0, tmp0.size-1, tmp0.size-1), addr)); block->add_instr(bblkid, ir_or(tmp3, tmp3, tmp2, addr)); block->add_instr(bblkid, ir_bisz(cf, tmp3, ir_cst(1, 0, 0), addr)); block->add_instr(bblkid, ir_bisz(of, tmp3, ir_cst(1, 0, 0), addr)); } // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_inc_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, dest, op0, tmp; /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); /* Increment op0 */ tmp = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_add(tmp, op0, ir_cst(1, op0.size-1, 0), addr )); /* Set flags (except CF) */ x86_set_pf(mode, tmp, addr, block, bblkid, tmp_var_count); x86_set_sf(mode, tmp, addr, block, bblkid ); x86_set_zf(mode, tmp, addr, block, bblkid ); x86_sub_set_af(mode, op0, ir_cst(1, op0.size-1, 0), tmp, addr, block, bblkid, tmp_var_count); x86_sub_set_of(mode, op0, ir_cst(1, op0.size-1, 0), tmp, addr, block, bblkid, tmp_var_count); /* Store result */ if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(bblkid, ir_stm(dest, tmp, addr)); }else{ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, tmp); } // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_int_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, num, next_pc; /* Get operands */ pc = x86_get_pc(mode); next_pc = ir_tmp(tmp_var_count++, pc.size-1, 0); block->add_instr(bblkid, ir_add(next_pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); num = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); /* Create interrupt */ block->add_instr(bblkid, ir_int(num, next_pc, addr)); return; } inline void x86_int3_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, num, next_pc; /* Get operands */ pc = x86_get_pc(mode); next_pc = ir_tmp(tmp_var_count++, pc.size-1, 0); block->add_instr(bblkid, ir_add(next_pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); /* Create interrupt 3 */ block->add_instr(bblkid, ir_int(ir_cst(3, 7, 0), next_pc, addr)); return; } inline void x86_leave_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, bp, sp; sp = (mode == CPUMode::X86)? ir_var(X86_ESP, 31, 0) : ir_var(X64_RSP, 63, 0); bp = (mode == CPUMode::X86)? ir_var(X86_EBP, 31, 0) : ir_var(X64_RBP, 63, 0); /* esp <- ebp * ebp <- pop() */ block->add_instr(bblkid, ir_mov(sp, bp, addr )); block->add_instr(bblkid, ir_ldm(bp, sp, addr )); block->add_instr(bblkid, ir_add(sp, sp, ir_cst(bp.size/8, sp.size-1, 0), addr )); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_ja_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, tmp0, tmp2, zf, cf, op0; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); zf = (mode == CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); tmp0 = ir_tmp(tmp_var_count++, cf.size-1, 0); /* Condition CF = ZF = 0 */ block->add_instr(bblkid, ir_or(tmp0, cf, zf, addr )); /* Two possible values */ block->add_instr(bblkid, ir_jcc(tmp0, ir_cst(instr->size+addr, pc.size-1, 0), ir_cst(op0.cst(), pc.size-1, 0), addr)); return; } inline void x86_jae_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, tmp0, cf, op0; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); /* Condition CF = 0 */ block->add_instr(bblkid, ir_jcc(cf, ir_cst(instr->size+addr, pc.size-1, 0), ir_cst(op0.cst(), pc.size-1, 0), addr)); return; } inline void x86_jb_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, cf, op0; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); /* Condition CF = 1 */ block->add_instr(bblkid, ir_jcc(cf, ir_cst(op0.cst(), pc.size-1, 0), ir_cst(instr->size+addr, pc.size-1, 0), addr)); return; } inline void x86_jbe_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, tmp0, zf, cf, op0; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); zf = (mode == CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); tmp0 = ir_tmp(tmp_var_count++, cf.size-1, 0); /* Condition CF = 1 or ZF = 1 */ block->add_instr(bblkid, ir_or(tmp0, cf, zf, addr )); /* Two possible values */ block->add_instr(bblkid, ir_jcc(tmp0, ir_cst(op0.cst(), pc.size-1, 0), ir_cst(instr->size+addr, pc.size-1, 0), addr )); return; } inline void x86_jcxz_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, cx, op0; if( mode == CPUMode::X64 ){ throw runtime_exception("JCXZ: invalid in X64 mode"); } op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); cx = (mode == CPUMode::X86)? ir_var(X86_ECX, 15, 0) : ir_var(X64_RCX, 15, 0); /* Condition CX = 0 */ block->add_instr(bblkid, ir_jcc(cx, ir_cst(instr->size+addr, pc.size-1, 0), ir_cst(op0.cst(), pc.size-1, 0), addr )); return; } inline void x86_je_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, zf, op0; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); zf = (mode == CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); /* Condition ZF = 1 */ block->add_instr(bblkid, ir_jcc(zf, ir_cst(op0.cst(), pc.size-1, 0), ir_cst(instr->size+addr, pc.size-1, 0), addr)); return; } inline void x86_jecxz_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, ecx, op0; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); ecx = (mode == CPUMode::X86)? ir_var(X86_ECX, 31, 0) : ir_var(X64_RCX, 31, 0); /* Condition ECX = 0 */ block->add_instr(bblkid, ir_jcc(ecx, ir_cst(instr->size+addr, pc.size-1, 0), ir_cst(op0.cst(), pc.size-1, 0), addr )); return; } inline void x86_jg_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, tmp0, zf, sf, of, op0; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); sf = (mode == CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); zf = (mode == CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); tmp0 = ir_tmp(tmp_var_count++, sf.size-1, 0); /* Condition ZF = 0 and SF = OF */ block->add_instr(bblkid, ir_xor(tmp0, sf, of, addr )); block->add_instr(bblkid, ir_or(tmp0, tmp0, zf, addr )); /* Two possible values */ block->add_instr(bblkid, ir_jcc(tmp0, ir_cst(instr->size+addr, pc.size-1, 0), ir_cst(op0.cst(), pc.size-1, 0), addr )); return; } inline void x86_jge_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, tmp0, sf, of, op0; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); sf = (mode == CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); tmp0 = ir_tmp(tmp_var_count++, sf.size-1, 0); /* Condition SF = OF */ block->add_instr(bblkid, ir_xor(tmp0, sf, of, addr )); /* Two possible values */ block->add_instr(bblkid, ir_jcc(tmp0, ir_cst(instr->size+addr, pc.size-1, 0), ir_cst(op0.cst(), pc.size-1, 0), addr )); return; } inline void x86_jl_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, tmp0, sf, of, op0; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); sf = (mode == CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); tmp0 = ir_tmp(tmp_var_count++, sf.size-1, 0); /* Condition SF != OF */ block->add_instr(bblkid, ir_xor(tmp0, sf, of, addr )); /* Two possible values */ block->add_instr(bblkid, ir_jcc(tmp0, ir_cst(op0.cst(), pc.size-1, 0), ir_cst(instr->size+addr, pc.size-1, 0), addr )); return; } inline void x86_jle_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, tmp0, zf, sf, of, op0; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); sf = (mode == CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); zf = (mode == CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); tmp0 = ir_tmp(tmp_var_count++, sf.size-1, 0); /* Condition ZF = 1 or SF != OF */ block->add_instr(bblkid, ir_xor(tmp0, sf, of, addr )); block->add_instr(bblkid, ir_or(tmp0, tmp0, zf, addr )); /* Two possible values */ block->add_instr(bblkid, ir_jcc(tmp0, ir_cst(op0.cst(), pc.size-1, 0), ir_cst(instr->size+addr, pc.size-1, 0), addr )); return; } inline void x86_jmp_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, op0; // Update PC first in case jmp is PC relative pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); block->add_instr(bblkid, ir_jcc(ir_cst(1, pc.size-1, 0), op0, ir_none(), addr )); return; } inline void x86_jne_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, zf, op0; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); zf = (mode == CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); /* Condition ZF = 0 */ block->add_instr(bblkid, ir_jcc(zf, ir_cst(instr->size+addr, pc.size-1, 0), ir_cst(op0.cst(), pc.size-1, 0), addr )); return; } inline void x86_jno_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, of, op0; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); /* Condition OF = 0 */ block->add_instr(bblkid, ir_jcc(of, ir_cst(instr->size+addr, pc.size-1, 0), ir_cst(op0.cst(), pc.size-1, 0), addr )); return; } inline void x86_jnp_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, pf, op0; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); pf = (mode == CPUMode::X86)? ir_var(X86_PF, 31, 0) : ir_var(X64_PF, 63, 0); /* Condition PF = 0 */ block->add_instr(bblkid, ir_jcc(pf, ir_cst(instr->size+addr, pc.size-1, 0), ir_cst(op0.cst(), pc.size-1, 0), addr )); return; } inline void x86_jns_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, sf, op0; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); sf = (mode == CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); /* Condition SF = 0 */ block->add_instr(bblkid, ir_jcc(sf, ir_cst(instr->size+addr, pc.size-1, 0), ir_cst(op0.cst(), pc.size-1, 0), addr )); return; } inline void x86_jo_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, of, op0; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); /* Condition OF = 1 */ block->add_instr(bblkid, ir_jcc(of, ir_cst(op0.cst(), pc.size-1, 0), ir_cst(instr->size+addr, pc.size-1, 0), addr )); return; } inline void x86_jp_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, pf, op0; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); pf = (mode == CPUMode::X86)? ir_var(X86_PF, 31, 0) : ir_var(X64_PF, 63, 0); /* Condition PF = 1 */ block->add_instr(bblkid, ir_jcc(pf, ir_cst(op0.cst(), pc.size-1, 0), ir_cst(instr->size+addr, pc.size-1, 0), addr )); return; } inline void x86_jrcxz_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, rcx, op0; if( mode == CPUMode::X86 ){ throw runtime_exception("JRCXZ: invalid in X86 mode"); } op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = ir_var(X64_RIP, 63, 0); rcx = ir_var(X64_RCX, 63, 0); /* Condition RCX = 0 */ block->add_instr(bblkid, ir_jcc(rcx, ir_cst(instr->size+addr, pc.size-1, 0), ir_cst(op0.cst(), pc.size-1, 0), addr )); return; } inline void x86_js_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, sf, op0; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); sf = (mode == CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); /* Condition SF = 1 */ block->add_instr(bblkid, ir_jcc(sf, ir_cst(op0.cst(), pc.size-1, 0), ir_cst(instr->size+addr, pc.size-1, 0), addr )); return; } inline void x86_lahf_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, sf, zf, af, pf, cf, ax; pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); sf = (mode == CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); zf = (mode == CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); af = (mode == CPUMode::X86)? ir_var(X86_AF, 31, 0) : ir_var(X64_AF, 63, 0); pf = (mode == CPUMode::X86)? ir_var(X86_PF, 31, 0) : ir_var(X64_PF, 63, 0); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); ax = (mode == CPUMode::X86)? ir_var(X86_EAX, 31, 0) : ir_var(X64_RAX, 63, 0); /* AH <- EFLAGS(SF:ZF:0:AF:0:PF:1:CF) */ block->add_instr(bblkid, ir_mov(x86_arg_extract(ax, 15, 15), x86_arg_extract(sf, 0, 0), addr )); block->add_instr(bblkid, ir_mov(x86_arg_extract(ax, 14, 14), x86_arg_extract(zf, 0, 0), addr )); block->add_instr(bblkid, ir_mov(x86_arg_extract(ax, 13, 13), ir_cst(0, 0, 0), addr )); block->add_instr(bblkid, ir_mov(x86_arg_extract(ax, 12, 12), x86_arg_extract(af, 0, 0), addr )); block->add_instr(bblkid, ir_mov(x86_arg_extract(ax, 11, 11), ir_cst(0, 0, 0), addr )); block->add_instr(bblkid, ir_mov(x86_arg_extract(ax, 10, 10), x86_arg_extract(pf, 0, 0), addr )); block->add_instr(bblkid, ir_mov(x86_arg_extract(ax, 9, 9), ir_cst(1, 0, 0), addr )); block->add_instr(bblkid, ir_mov(x86_arg_extract(ax, 8, 8), x86_arg_extract(cf, 0, 0), addr )); // Update PC block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_lea_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, tmp0, op0, op1; pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); // Update PC first in case PC relative load! block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count); /* Check operand sizes */ if( op0.size > op1.size ){ /* Zero extend */ tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_mov(tmp0, ir_cst(0, op0.size-1, 0), addr )); block->add_instr(bblkid, ir_mov(x86_arg_extract(tmp0, op1.size-1, 0), op1, addr)); x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, op0, tmp0); }else{ /* Truncate if needed */ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, op0, x86_arg_extract(op1, op0.size-1, 0)); } } inline void x86_lodsb_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, al, si, df; IRBasicBlockId inc, dec, end, prefix_start; df = (mode == CPUMode::X86)? ir_var(X86_DF, 31, 0) : ir_var(X64_DF, 63, 0); al = (mode == CPUMode::X86)? ir_var(X86_EAX, 7, 0): ir_var(X64_RAX, 7, 0); si = (mode == CPUMode::X86)? ir_var(X86_ESI, 31, 0): ir_var(X64_RSI, 63, 0); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); prefix_start = _x86_init_prefix(mode, instr, addr, block, bblkid); /* Load byte */ block->add_instr(bblkid, ir_ldm(al, si, addr)); inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, 31, 0), ir_cst(inc, 31, 0), addr)); /* Increment */ block->add_instr(inc, ir_add(si, si, ir_cst(1, si.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Or decrement */ block->add_instr(dec, ir_sub(si, si, ir_cst(1, si.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Add prefix if any */ _x86_end_prefix(mode, instr, addr, block, prefix_start, end, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_lodsd_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, eax, si, df, tmp; IRBasicBlockId inc, dec, end, prefix_start; df = (mode == CPUMode::X86)? ir_var(X86_DF, 31, 0) : ir_var(X64_DF, 63, 0); eax = (mode == CPUMode::X86)? ir_var(X86_EAX, 31, 0): ir_var(X64_RAX, 31, 0); si = (mode == CPUMode::X86)? ir_var(X86_ESI, 31, 0): ir_var(X64_RSI, 63, 0); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); tmp = ir_tmp(tmp_var_count++, eax.size-1, 0); prefix_start = _x86_init_prefix(mode, instr, addr, block, bblkid); /* Load byte */ block->add_instr(bblkid, ir_ldm(tmp, si, addr)); x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, eax, tmp); inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, df.size-1, 0), ir_cst(inc, df.size-1, 0), addr)); /* Increment */ block->add_instr(inc, ir_add(si, si, ir_cst(4, si.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Or decrement */ block->add_instr(dec, ir_sub(si, si, ir_cst(4, si.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Add prefix if any */ _x86_end_prefix(mode, instr, addr, block, prefix_start, end, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_lodsq_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, rax, si, df; IRBasicBlockId inc, dec, end, prefix_start; df = ir_var(X64_DF, 63, 0); rax = ir_var(X64_RAX, 63, 0); si = ir_var(X64_RSI, 63, 0); pc = ir_var(X64_RIP, 63, 0); prefix_start = _x86_init_prefix(mode, instr, addr, block, bblkid); /* Load byte */ block->add_instr(bblkid, ir_ldm(rax, si, addr)); inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, df.size-1, 0), ir_cst(inc, df.size-1, 0), addr)); /* Increment */ block->add_instr(inc, ir_add(si, si, ir_cst(8, si.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Or decrement */ block->add_instr(dec, ir_sub(si, si, ir_cst(8, si.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Add prefix if any */ _x86_end_prefix(mode, instr, addr, block, prefix_start, end, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_lodsw_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, ax, si, df; IRBasicBlockId inc, dec, end, prefix_start; df = (mode == CPUMode::X86)? ir_var(X86_DF, 31, 0) : ir_var(X64_DF, 63, 0); ax = (mode == CPUMode::X86)? ir_var(X86_EAX, 15, 0): ir_var(X64_RAX, 15, 0); si = (mode == CPUMode::X86)? ir_var(X86_ESI, 31, 0): ir_var(X64_RSI, 63, 0); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); prefix_start = _x86_init_prefix(mode, instr, addr, block, bblkid); /* Load byte */ block->add_instr(bblkid, ir_ldm(ax, si, addr)); inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, df.size-1, 0), ir_cst(inc, df.size-1, 0), addr)); /* Increment */ block->add_instr(inc, ir_add(si, si, ir_cst(2, si.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Or decrement */ block->add_instr(dec, ir_sub(si, si, ir_cst(2, si.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Add prefix if any */ _x86_end_prefix(mode, instr, addr, block, prefix_start, end, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_mov_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, op0, op1; // Update PC (in case PC-relative addressing) pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); /* Do the mov */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(bblkid, ir_stm(op0, op1, addr)); }else{ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, op0, op1); } return; } inline void x86_movsb_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, di, si, df, tmp0; IRBasicBlockId inc, dec, end, prefix_start; df = (mode == CPUMode::X86)? ir_var(X86_DF, 31, 0) : ir_var(X64_DF, 63, 0); di = (mode == CPUMode::X86)? ir_var(X86_EDI, 31, 0): ir_var(X64_RDI, 63, 0); si = (mode == CPUMode::X86)? ir_var(X86_ESI, 31, 0): ir_var(X64_RSI, 63, 0); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); prefix_start = _x86_init_prefix(mode, instr, addr, block, bblkid); /* Load byte */ tmp0 = ir_tmp(tmp_var_count++, 7, 0); block->add_instr(bblkid, ir_ldm(tmp0, si, addr)); block->add_instr(bblkid, ir_stm(di, tmp0, addr)); inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, df.size-1, 0), ir_cst(inc, df.size-1, 0), addr)); /* Increment if DF = 0 */ block->add_instr(inc, ir_add(si, si, ir_cst(1, si.size-1, 0), addr)); block->add_instr(inc, ir_add(di, di, ir_cst(1, di.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Or decrement if DF = 1*/ block->add_instr(dec, ir_sub(si, si, ir_cst(1, si.size-1, 0), addr)); block->add_instr(dec, ir_sub(di, di, ir_cst(1, di.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Add prefix if any */ _x86_end_prefix(mode, instr, addr, block, prefix_start, end, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_movsd_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, di, si, df, tmp0; IRBasicBlockId inc, dec, end, prefix_start; df = (mode == CPUMode::X86)? ir_var(X86_DF, 31, 0) : ir_var(X64_DF, 63, 0); di = (mode == CPUMode::X86)? ir_var(X86_EDI, 31, 0): ir_var(X64_RDI, 63, 0); si = (mode == CPUMode::X86)? ir_var(X86_ESI, 31, 0): ir_var(X64_RSI, 63, 0); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); prefix_start = _x86_init_prefix(mode, instr, addr, block, bblkid); /* Load dword */ tmp0 = ir_tmp(tmp_var_count++, 31, 0); block->add_instr(bblkid, ir_ldm(tmp0, si, addr)); block->add_instr(bblkid, ir_stm(di, tmp0, addr)); inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, df.size-1, 0), ir_cst(inc, df.size-1, 0), addr)); /* Increment if DF = 0 */ block->add_instr(inc, ir_add(si, si, ir_cst(4, si.size-1, 0), addr)); block->add_instr(inc, ir_add(di, di, ir_cst(4, di.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Or decrement if DF = 1*/ block->add_instr(dec, ir_sub(si, si, ir_cst(4, si.size-1, 0), addr)); block->add_instr(dec, ir_sub(di, di, ir_cst(4, di.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Add prefix if any */ _x86_end_prefix(mode, instr, addr, block, prefix_start, end, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_movsq_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, di, si, df, tmp0; IRBasicBlockId inc, dec, end, prefix_start; df = (mode == CPUMode::X86)? ir_var(X86_DF, 31, 0) : ir_var(X64_DF, 63, 0); di = (mode == CPUMode::X86)? ir_var(X86_EDI, 31, 0): ir_var(X64_RDI, 63, 0); si = (mode == CPUMode::X86)? ir_var(X86_ESI, 31, 0): ir_var(X64_RSI, 63, 0); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); prefix_start = _x86_init_prefix(mode, instr, addr, block, bblkid); /* Load qword */ tmp0 = ir_tmp(tmp_var_count++, 63, 0); block->add_instr(bblkid, ir_ldm(tmp0, si, addr)); block->add_instr(bblkid, ir_stm(di, tmp0, addr)); inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, df.size-1, 0), ir_cst(inc, df.size-1, 0), addr)); /* Increment if DF = 0 */ block->add_instr(inc, ir_add(si, si, ir_cst(8, si.size-1, 0), addr)); block->add_instr(inc, ir_add(di, di, ir_cst(8, di.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Or decrement if DF = 1*/ block->add_instr(dec, ir_sub(si, si, ir_cst(8, si.size-1, 0), addr)); block->add_instr(dec, ir_sub(di, di, ir_cst(8, di.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Add prefix if any */ _x86_end_prefix(mode, instr, addr, block, prefix_start, end, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_movsw_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, di, si, df, tmp0; IRBasicBlockId inc, dec, end, prefix_start; df = (mode == CPUMode::X86)? ir_var(X86_DF, 31, 0) : ir_var(X64_DF, 63, 0); di = (mode == CPUMode::X86)? ir_var(X86_EDI, 31, 0): ir_var(X64_RDI, 63, 0); si = (mode == CPUMode::X86)? ir_var(X86_ESI, 31, 0): ir_var(X64_RSI, 63, 0); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); prefix_start = _x86_init_prefix(mode, instr, addr, block, bblkid); /* Load word */ tmp0 = ir_tmp(tmp_var_count++, 15, 0); block->add_instr(bblkid, ir_ldm(tmp0, si, addr)); block->add_instr(bblkid, ir_stm(di, tmp0, addr)); inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, df.size-1, 0), ir_cst(inc, df.size-1, 0), addr)); /* Increment if DF = 0 */ block->add_instr(inc, ir_add(si, si, ir_cst(2, si.size-1, 0), addr)); block->add_instr(inc, ir_add(di, di, ir_cst(2, di.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Or decrement if DF = 1*/ block->add_instr(dec, ir_sub(si, si, ir_cst(2, si.size-1, 0), addr)); block->add_instr(dec, ir_sub(di, di, ir_cst(2, di.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Add prefix if any */ _x86_end_prefix(mode, instr, addr, block, prefix_start, end, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_movsx_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, op0, op1, tmp0; IRBasicBlockId pos, neg, end; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); // Update PC block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); /* Test MSB */ pos = block->new_bblock(); neg = block->new_bblock(); end = block->new_bblock(); block->add_instr(bblkid, ir_bcc(x86_arg_extract(op1, op1.size-1, op1.size-1), ir_cst(neg, 31, 0), ir_cst(pos, 31, 0), addr)); /* Positive (0 extend) */ block->add_instr(pos, ir_mov(tmp0, ir_cst(0, tmp0.size-1, 0), addr)); block->add_instr(pos, ir_mov(x86_arg_extract(tmp0, op1.size-1, 0), op1, addr)); x86_adjust_reg_assign(mode, addr, block, pos, tmp_var_count, op0, tmp0); block->add_instr(pos, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Negative (1 extend) */ block->add_instr(neg, ir_mov(tmp0, ir_cst((ucst_t)0xffffffffffffffff< add_instr(neg, ir_mov(x86_arg_extract(tmp0, op1.size-1, 0), op1, addr)); x86_adjust_reg_assign(mode, addr, block, neg, tmp_var_count, op0, tmp0); block->add_instr(neg, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_movsxd_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, op0, op1, tmp0; IRBasicBlockId pos, neg, end; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); // Update PC block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); /* If already same size, just mov */ if( op0.size == op1.size ){ block->add_instr(bblkid, ir_mov(op0, op1, addr)); return; } /* Else extend : Test MSB */ pos = block->new_bblock(); neg = block->new_bblock(); end = block->new_bblock(); block->add_instr(bblkid, ir_bcc(x86_arg_extract(op1, op1.size-1, op1.size-1), ir_cst(neg, 31, 0), ir_cst(pos, 31, 0), addr)); /* Positive (0 extend) */ block->add_instr(pos, ir_mov(tmp0, ir_cst(0, tmp0.size-1, 0), addr)); block->add_instr(pos, ir_mov(x86_arg_extract(tmp0, op1.size-1, 0), op1, addr)); x86_adjust_reg_assign(mode, addr, block, pos, tmp_var_count, op0, tmp0); block->add_instr(pos, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Negative (1 extend) */ block->add_instr(neg, ir_mov(tmp0, ir_cst((ucst_t)0xffffffffffffffff< add_instr(neg, ir_mov(x86_arg_extract(tmp0, op1.size-1, 0), op1, addr)); x86_adjust_reg_assign(mode, addr, block, neg, tmp_var_count, op0, tmp0); block->add_instr(neg, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_movzx_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, op0, op1, tmp0; op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); /* Positive (0 extend) */ block->add_instr(bblkid, ir_mov(tmp0, ir_cst(0, tmp0.size-1, 0), addr)); block->add_instr(bblkid, ir_mov(x86_arg_extract(tmp0, op1.size-1, 0), op1, addr)); x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, op0, tmp0); // Update PC block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_mul_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, op0, lower, higher, tmp0, tmp1, ax, tmp2, tmp3, tmp4, cf, of; cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0): ir_var(X64_CF, 63, 0); of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0): ir_var(X64_OF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); ax = (mode == CPUMode::X86)? ir_var(X86_EAX, op0.size-1, 0): ir_var(X64_RAX, op0.size-1, 0); if( op0.size == 8 ){ lower = (mode == CPUMode::X86)? ir_var(X86_EAX, 7, 0): ir_var(X64_RAX, 15, 0); higher = (mode == CPUMode::X86)? ir_var(X86_EAX, 15, 8): ir_var(X64_RAX, 15, 8); }else{ lower = (mode == CPUMode::X86)? ir_var(X86_EAX, op0.size-1, 0): ir_var(X64_RAX, op0.size-1, 0); higher = (mode == CPUMode::X86)? ir_var(X86_EDX, op0.size-1, 0): ir_var(X64_RDX, op0.size-1, 0); } tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp1 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp2 = ir_tmp(tmp_var_count++, 0, 0); tmp3 = ir_tmp(tmp_var_count++, 0, 0); /* Do the multiplication */ block->add_instr(bblkid, ir_mul(tmp0, ax, op0, addr)); block->add_instr(bblkid, ir_mulh(tmp1, ax, op0, addr)); x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, lower, tmp0); x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, higher, tmp1); /* Set OF and CF to 1 if high order bits are not zero, else clear */ block->add_instr(bblkid, ir_bisz(cf, tmp1, ir_cst(0, cf.size-1, 0), addr)); block->add_instr(bblkid, ir_bisz(of, tmp1, ir_cst(0, of.size-1, 0), addr)); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_neg_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, op0, cf, tmp0, dest; cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0): ir_var(X64_CF, 63, 0); /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); /* CF = (op0 != 0) */ block->add_instr(bblkid, ir_bisz(cf, op0, ir_cst(0, cf.size-1, 0), addr)); /* Do the neg */ block->add_instr(bblkid, ir_neg(tmp0, op0, addr)); /* Set flags according to the result (same that for a sub from 0) */ x86_set_sf(mode, tmp0, addr, block, bblkid); x86_set_zf(mode, tmp0, addr, block, bblkid); x86_set_pf(mode, tmp0, addr, block, bblkid, tmp_var_count); x86_sub_set_af(mode, ir_cst(0, op0.size-1, 0), op0, tmp0, addr, block, bblkid, tmp_var_count); x86_sub_set_of(mode, ir_cst(0, op0.size-1, 0), op0, tmp0, addr, block, bblkid, tmp_var_count); /* Assign result */ if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(bblkid, ir_stm(dest, tmp0, addr)); }else{ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, tmp0); } // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_nop_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc; // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_not_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, op0, dest, tmp; /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); tmp = ir_tmp(tmp_var_count++, op0.size-1, 0); /* Do the not */ block->add_instr(bblkid, ir_not(tmp, op0, addr)); /* Assign result */ if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(bblkid, ir_stm(dest, tmp, addr)); }else{ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, tmp); } // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_or_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, res, of, cf, pc; of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); }else{ op0 = dest; } op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); /* Do the or */ res = ir_tmp(tmp_var_count++, (instr->detail->x86.operands[0].size*8)-1, 0); block->add_instr(bblkid, ir_or(res, op0, op1, addr)); /* Update flags: SF, ZF, PF */ x86_set_zf(mode, res, addr, block, bblkid); x86_set_sf(mode, res, addr, block, bblkid); x86_set_pf(mode, res, addr, block, bblkid, tmp_var_count); /* OF and CF cleared */ block->add_instr(bblkid, ir_mov(of, ir_cst(0, of.high, of.low), addr)); block->add_instr(bblkid, ir_mov(cf, ir_cst(0, cf.high, cf.low), addr)); /* Finally assign the result to the destination */ if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(bblkid, ir_stm(dest, res, addr)); }else{ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, res); } // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_pop_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, sp, pc, tmp0; /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); sp = (mode == CPUMode::X86)? ir_var(X86_ESP, 31, 0): ir_var(X64_RSP, 63, 0); /* Get the value on the stack */ tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_ldm(tmp0, sp, addr)); /* Increment stack pointer */ block->add_instr(bblkid, ir_add(sp, sp, ir_cst(instr->detail->x86.operands[0].size, sp.size-1, 0), addr)); /* Assign the value that was on the stack (AFTER incrementing ESP) */ if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(bblkid, ir_stm(op0, tmp0, addr)); }else{ block->add_instr(bblkid, ir_mov(op0, tmp0, addr)); } // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_popad_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand esp, pc, edi, esi, ebp, ebx, edx, ecx, eax; if( mode == CPUMode::X64 ){ throw runtime_exception("POPAD: invalid in X64 mode"); } /* Get operands */ esp = ir_var(X86_ESP, 31, 0); edi = ir_var(X86_EDI, 31, 0); esi = ir_var(X86_ESI, 31, 0); ebp = ir_var(X86_EBP, 31, 0); ebx = ir_var(X86_EBX, 31, 0); edx = ir_var(X86_EDX, 31, 0); ecx = ir_var(X86_ECX, 31, 0); eax = ir_var(X86_EAX, 31, 0); /* Get the registers on the stack: EDI ← Pop(); ESI ← Pop(); EBP ← Pop(); Increment ESP by 4; (* Skip next 4 bytes of stack *) EBX ← Pop(); EDX ← Pop(); ECX ← Pop(); EAX ← Pop(); */ block->add_instr(bblkid, ir_ldm(edi, esp, addr)); block->add_instr(bblkid, ir_add(esp, esp, ir_cst(esp.size/8, esp.size-1, 0), addr)); block->add_instr(bblkid, ir_ldm(esi, esp, addr)); block->add_instr(bblkid, ir_add(esp, esp, ir_cst(esp.size/8, esp.size-1, 0), addr)); block->add_instr(bblkid, ir_ldm(ebp, esp, addr)); block->add_instr(bblkid, ir_add(esp, esp, ir_cst(esp.size/4, esp.size-1, 0), addr)); block->add_instr(bblkid, ir_ldm(ebx, esp, addr)); block->add_instr(bblkid, ir_add(esp, esp, ir_cst(esp.size/8, esp.size-1, 0), addr)); block->add_instr(bblkid, ir_ldm(edx, esp, addr)); block->add_instr(bblkid, ir_add(esp, esp, ir_cst(esp.size/8, esp.size-1, 0), addr)); block->add_instr(bblkid, ir_ldm(ecx, esp, addr)); block->add_instr(bblkid, ir_add(esp, esp, ir_cst(esp.size/8, esp.size-1, 0), addr)); block->add_instr(bblkid, ir_ldm(eax, esp, addr)); block->add_instr(bblkid, ir_add(esp, esp, ir_cst(esp.size/8, esp.size-1, 0), addr)); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_push_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, sp, pc; /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); sp = (mode == CPUMode::X86)? ir_var(X86_ESP, 31, 0): ir_var(X64_RSP, 63, 0); /* Decrement stack pointer */ block->add_instr(bblkid, ir_sub(sp, sp, ir_cst(instr->detail->x86.operands[0].size, sp.size-1, 0), addr)); /* Get the value on the stack */ block->add_instr(bblkid, ir_stm(sp, op0, addr)); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_pushad_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand esp, pc, edi, esi, ebp, ebx, edx, ecx, eax, tmp0; /* Get operands */ esp = (mode == CPUMode::X86)? ir_var(X86_ESP, 31, 0): ir_var(X64_RSP, 63, 0); edi = (mode == CPUMode::X86)? ir_var(X86_EDI, 31, 0): ir_var(X64_RDI, 63, 0); esi = (mode == CPUMode::X86)? ir_var(X86_ESI, 31, 0): ir_var(X64_RSI, 63, 0); ebp = (mode == CPUMode::X86)? ir_var(X86_EBP, 31, 0): ir_var(X64_RBP, 63, 0); ebx = (mode == CPUMode::X86)? ir_var(X86_EBX, 31, 0): ir_var(X64_RBX, 63, 0); edx = (mode == CPUMode::X86)? ir_var(X86_EDX, 31, 0): ir_var(X64_RDX, 63, 0); ecx = (mode == CPUMode::X86)? ir_var(X86_ECX, 31, 0): ir_var(X64_RCX, 63, 0); eax = (mode == CPUMode::X86)? ir_var(X86_EAX, 31, 0): ir_var(X64_RAX, 63, 0); tmp0 = ir_tmp(tmp_var_count++, esp.size-1, 0); /* Get the registers on the stack: Temp ← (ESP); Push(EAX); Push(ECX); Push(EDX); Push(EBX); Push(Temp); Push(EBP); Push(ESI); Push(EDI); */ block->add_instr(bblkid, ir_mov(tmp0, esp, addr)); block->add_instr(bblkid, ir_sub(esp, esp, ir_cst(esp.size/8, esp.size-1, 0), addr)); block->add_instr(bblkid, ir_stm(esp, eax, addr)); block->add_instr(bblkid, ir_sub(esp, esp, ir_cst(esp.size/8, esp.size-1, 0), addr)); block->add_instr(bblkid, ir_stm(esp, ecx, addr)); block->add_instr(bblkid, ir_sub(esp, esp, ir_cst(esp.size/8, esp.size-1, 0), addr)); block->add_instr(bblkid, ir_stm(esp, edx, addr)); block->add_instr(bblkid, ir_sub(esp, esp, ir_cst(esp.size/8, esp.size-1, 0), addr)); block->add_instr(bblkid, ir_stm(esp, ebx, addr)); block->add_instr(bblkid, ir_sub(esp, esp, ir_cst(esp.size/8, esp.size-1, 0), addr)); block->add_instr(bblkid, ir_stm(esp, tmp0, addr)); block->add_instr(bblkid, ir_sub(esp, esp, ir_cst(esp.size/8, esp.size-1, 0), addr)); block->add_instr(bblkid, ir_stm(esp, ebp, addr)); block->add_instr(bblkid, ir_sub(esp, esp, ir_cst(esp.size/8, esp.size-1, 0), addr)); block->add_instr(bblkid, ir_stm(esp, esi, addr)); block->add_instr(bblkid, ir_sub(esp, esp, ir_cst(esp.size/8, esp.size-1, 0), addr)); block->add_instr(bblkid, ir_stm(esp, edi, addr)); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_rcl_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, pc, tmp0, cf, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, of, res; IRBasicBlockId set_of, cont, rotate; /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); /* Blocks */ rotate = block->new_bblock(); set_of = block->new_bblock(); cont = block->new_bblock(); // mask is 5 bits <= 32 bits operands, 6 bits for 64 bits operands unsigned int mask = (op0.size == 64)? 0b111111 : 0b11111; /* Mask the number of rotations N */ tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp1 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_and(tmp0, x86_arg_extract(op1, op0.size-1, 0), ir_cst(mask, op0.size-1, 0), addr)); /* If masked count is zero, go to end, else do rotate */ block->add_instr(bblkid, ir_bcc(tmp0, ir_cst(rotate, 31, 0), ir_cst(cont, 31, 0), addr)); // Rotate /* REG[up] = REG[size-1-N:N] = tmp1 */ block->add_instr(rotate, ir_shl(tmp1, op0, tmp0, addr)); // Just shift left /* REG[N-1] = CF = tmp2 */ tmp2 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp3 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(rotate, ir_sub(tmp3, tmp0, ir_cst(1, tmp0.size-1, 0), addr)); // tmp3 = N-1 block->add_instr(rotate, ir_shl(tmp2, x86_arg_extract(cf, op0.size-1, 0), tmp3, addr)); // Just shift left of N-1 /* REG[N-2:0] = REG[:size-N+1] = tmp5 */ tmp4 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp5 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(rotate, ir_sub(tmp4, ir_cst(op0.size+1, op0.size-1, 0), tmp0, addr)); // tmp4 = size-N+1 block->add_instr(rotate, ir_shr(tmp5, op0, tmp4, addr)); // Shift right of size-N+1 /* Res is the OR of everything */ res = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(rotate, ir_or(res, tmp1, tmp2, addr)); block->add_instr(rotate, ir_or(res, res, tmp5, addr)); /* Assign res to dest and CF */ /* CF = REG[size-N] (first CF because after we modify reg ! */ tmp6 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(rotate, ir_shl(tmp6, op0, tmp3, addr)); // Just shift left of N-1 block->add_instr(rotate, ir_mov(x86_arg_extract(cf, 0, 0), x86_arg_extract(tmp6, tmp6.size-1, tmp6.size-1), addr)); if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(rotate, ir_stm(dest, res, addr)); }else{ x86_adjust_reg_assign(mode, addr, block, rotate, tmp_var_count, dest, res); } /* Affect OF flag iff masked count == 1 (cf.res in tmp3)*/ tmp7 = ir_tmp(tmp_var_count++, tmp0.size-1, 0); block->add_instr(rotate, ir_xor(tmp7, tmp0, ir_cst(1, tmp0.size-1, 0), addr)); block->add_instr(rotate, ir_bcc(tmp7, ir_cst(cont, 31, 0), ir_cst(set_of, 31, 0), addr)); block->add_instr(set_of, ir_xor(x86_arg_extract(of, 0, 0), x86_arg_extract(res, res.size-1, res.size-1), x86_arg_extract(cf, 0, 0), addr)); block->add_instr(set_of, ir_bcc(ir_cst(1, 31, 0) , ir_cst(cont, 31, 0), ir_none(), addr)); // Update PC pc = x86_get_pc(mode); block->add_instr(cont, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = cont; return; } inline void x86_rcr_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, pc, tmp0, cf, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, of, res; IRBasicBlockId set_of, cont, rotate; /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); /* Blocks */ rotate = block->new_bblock(); set_of = block->new_bblock(); cont = block->new_bblock(); // mask is 5 bits <= 32 bits operands, 6 bits for 64 bits operands unsigned int mask = (op0.size == 64)? 0b111111 : 0b11111; /* Mask the number of rotations N */ tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp1 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_and(tmp0, x86_arg_extract(op1, op0.size-1, 0), ir_cst(mask, op0.size-1, 0), addr)); /* If masked count is zero, go to end, else do rotate */ block->add_instr(bblkid, ir_bcc(tmp0, ir_cst(rotate, 31, 0), ir_cst(cont, 31, 0), addr)); // Rotate /* REG[down] = REG[size-1:N] = tmp1 */ block->add_instr(rotate, ir_shr(tmp1, op0, tmp0, addr)); // Just shift right /* REG[size-N] = CF = tmp2 */ tmp2 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp3 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(rotate, ir_sub(tmp3, ir_cst(tmp0.size, tmp0.size-1, 0), tmp0, addr)); // tmp3 = size-N block->add_instr(rotate, ir_shl(tmp2, x86_arg_extract(cf, op0.size-1, 0), tmp3, addr)); // Just shift left of size-N /* REG[size:N] = REG[N-2:] = tmp5 */ tmp4 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp5 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(rotate, ir_add(tmp4, ir_cst(1, op0.size-1, 0), tmp3, addr)); // tmp4 = size-N+1 block->add_instr(rotate, ir_shl(tmp5, op0, tmp4, addr)); // Shift left of size-N+1 /* Res is the OR of everything */ res = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(rotate, ir_or(res, tmp1, tmp2, addr)); block->add_instr(rotate, ir_or(res, res, tmp5, addr)); /* Assign res to dest and CF */ /* CF = REG[size-N] (first CF because after we modify reg ! */ tmp6 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(rotate, ir_shl(tmp6, op0, tmp3, addr)); // Just shift left of N-1 block->add_instr(rotate, ir_mov(x86_arg_extract(cf, 0, 0), x86_arg_extract(tmp6, tmp6.size-1, tmp6.size-1), addr)); if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(rotate, ir_stm(dest, res, addr)); }else{ x86_adjust_reg_assign(mode, addr, block, rotate, tmp_var_count, dest, res); } /* Affect OF flag iff masked count == 1 (cf.res in tmp3)*/ tmp7 = ir_tmp(tmp_var_count++, tmp0.size-1, 0); block->add_instr(rotate, ir_xor(tmp7, tmp0, ir_cst(1, tmp0.size-1, 0), addr)); block->add_instr(rotate, ir_bcc(tmp7, ir_cst(cont, 31, 0), ir_cst(set_of, 31, 0), addr)); block->add_instr(set_of, ir_xor(x86_arg_extract(of, 0, 0), x86_arg_extract(res, res.size-1, res.size-1), x86_arg_extract(res, res.size-2, res.size-2), addr)); block->add_instr(set_of, ir_bcc(ir_cst(1, 31, 0) , ir_cst(cont, 31, 0), ir_none(), addr)); // Update PC pc = x86_get_pc(mode); block->add_instr(cont, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = cont; return; } inline void x86_rdtsc_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand eax = (mode == CPUMode::X86)? ir_var(X86_EAX, 31, 0): ir_var(X64_RAX, 63, 0), edx = (mode == CPUMode::X86)? ir_var(X86_EDX, 31, 0): ir_var(X64_RDX, 63, 0), tsc = x86_get_tsc(mode), // TSC is always 64 bits pc = x86_get_pc(mode); // Higher 32 bits in edx, lower 32 bits in eax x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, edx, x86_arg_extract(tsc, 63, 32)); x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, eax, x86_arg_extract(tsc, 31, 0)); // Update PC block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); } inline void x86_ret_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, sp, pc, tmp0; /* Get operands */ sp = (mode == CPUMode::X86)? ir_var(X86_ESP, 31, 0): ir_var(X64_RSP, 63, 0); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0): ir_var(X64_RIP, 63, 0); /* Pop program counter */ tmp0 = ir_tmp(tmp_var_count++, pc.size-1, 0); block->add_instr(bblkid, ir_ldm(tmp0, sp, addr)); block->add_instr(bblkid, ir_add(sp, sp, ir_cst(pc.size/8, sp.size-1, 0), addr)); /* If source operand adjust sp */ if( instr->detail->x86.op_count != 0 ){ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); // Set sp adjustment size according to sp size and add it to sp block->add_instr(bblkid, ir_add(sp, sp, x86_arg_extract(op0, sp.size-1, 0), addr)); } block->add_instr(bblkid, ir_jcc(ir_cst(1, pc.size-1, 0), tmp0, ir_none(), addr)); return; } inline void x86_rol_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, pc, tmp0, cf, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, of; IRBasicBlockId set_of, cont, set_cf, end, rotate; rotate = block->new_bblock(); set_cf = block->new_bblock(); cont = block->new_bblock(); set_of = block->new_bblock(); end = block->new_bblock(); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); // mask is 5 bits for <= 32 bits operands, 6 bits for 64 bits operands unsigned int mask = (op0.size == 64)? 0b111111 : 0b11111; /* Mask the number of rotations */ tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_and(tmp0, x86_arg_extract(op1, op0.size-1, 0), ir_cst(mask, op0.size-1, 0), addr)); /* Check if count is 0 */ block->add_instr(bblkid, ir_bcc(tmp0, ir_cst(rotate, 31, 0), ir_cst(end, 31, 0), addr)); // Do rotate /* Set rotations */ tmp4 = ir_tmp(tmp_var_count++, tmp0.size-1, 0); block->add_instr(rotate, ir_sub(tmp4, ir_cst(op0.size, tmp0.size-1, 0), tmp0, addr)); /* Rotate it (2 shifts) */ tmp2 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp3 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(rotate, ir_shl(tmp2, op0, tmp0, addr)); block->add_instr(rotate, ir_shr(tmp3, op0, tmp4, addr)); block->add_instr(rotate, ir_or(tmp3, tmp3, tmp2, addr)); // res in tmp3 /* Assign result to operand */ if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(rotate, ir_stm(dest, tmp3, addr)); }else{ x86_adjust_reg_assign(mode, addr, block, rotate, tmp_var_count, dest, tmp3); } /* Affect CF flag iff masked count != 0 */ block->add_instr(rotate, ir_bcc(tmp0, ir_cst(set_cf, 31, 0), ir_cst(end, 31, 0), addr)); block->add_instr(set_cf, ir_mov(x86_arg_extract(cf, 0, 0), x86_arg_extract(tmp3, 0, 0), addr)); block->add_instr(set_cf, ir_bcc(ir_cst(1, 31, 0), ir_cst(cont, 31, 0), ir_none(), addr)); /* Affect OF flag iff masked count == 1 (res in tmp3) */ tmp7 = ir_tmp(tmp_var_count++, tmp0.size-1, 0); block->add_instr(cont, ir_xor(tmp7, tmp0, ir_cst(1, tmp0.size-1, 0), addr)); block->add_instr(cont, ir_bcc(tmp7, ir_cst(end, 31, 0), ir_cst(set_of, 31, 0), addr)); block->add_instr(set_of, ir_xor(x86_arg_extract(of, 0, 0), x86_arg_extract(tmp3, tmp3.size-1, tmp3.size-1), x86_arg_extract(cf, 0, 0), addr)); block->add_instr(set_of, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_ror_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, pc, tmp0, cf, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, of; IRBasicBlockId set_of, cont, set_cf, end, rotate; rotate = block->new_bblock(); set_cf = block->new_bblock(); cont = block->new_bblock(); set_of = block->new_bblock(); end = block->new_bblock(); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); // mask is 5 bits <= 32 bits operands, 6 bits for 64 bits operands unsigned int mask = (op0.size == 64)? 0b111111 : 0b11111; /* Mask the number of rotations */ tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp1 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_and(tmp0, x86_arg_extract(op1, op0.size-1, 0), ir_cst(mask, op0.size-1, 0), addr)); /* Check if count is 0 */ block->add_instr(bblkid, ir_bcc(tmp0, ir_cst(rotate, 31, 0), ir_cst(end, 31, 0), addr)); // Do rotate block->add_instr(rotate, ir_mov(tmp1, tmp0, addr)); // copy of tmp0 /* Set rotations */ tmp4 = ir_tmp(tmp_var_count++, tmp0.size-1, 0); block->add_instr(rotate, ir_sub(tmp4, ir_cst(op0.size, tmp0.size-1, 0), tmp0, addr)); /* Rotate it (2 shifts) */ tmp2 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp3 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(rotate, ir_shr(tmp2, op0, tmp0, addr)); block->add_instr(rotate, ir_shl(tmp3, op0, tmp4, addr)); block->add_instr(rotate, ir_or(tmp3, tmp3, tmp2, addr)); // res in tmp3 /* Assign result to operand */ if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(rotate, ir_stm(dest, tmp3, addr)); }else{ x86_adjust_reg_assign(mode, addr, block, rotate, tmp_var_count, dest, tmp3); } /* Affect CF flag iff masked count != 0 */ block->add_instr(rotate, ir_bcc(tmp0, ir_cst(set_cf, 31, 0), ir_cst(end, 31, 0), addr)); block->add_instr(set_cf, ir_mov(x86_arg_extract(cf, 0, 0), x86_arg_extract(tmp3, tmp3.size-1, tmp3.size-1), addr)); block->add_instr(set_cf, ir_bcc(ir_cst(1, 31, 0), ir_cst(cont, 31, 0), ir_none(), addr)); /* Affect OF flag iff masked count == 1 (res in tmp3) */ tmp5 = ir_tmp(tmp_var_count++, tmp0.size-1, 0); block->add_instr(cont, ir_xor(tmp5, tmp1, ir_cst(1, tmp0.size-1, 0), addr)); block->add_instr(cont, ir_bcc(tmp5, ir_cst(end, 31, 0), ir_cst(set_of, 31, 0), addr)); block->add_instr(set_of, ir_xor(x86_arg_extract(of, 0, 0), x86_arg_extract(tmp3, tmp3.size-2, tmp3.size-2), x86_arg_extract(cf, 0, 0), addr)); block->add_instr(set_of, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_sal_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, pc, tmp0, cf, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, of; IRBasicBlockId set_of, cont, shift; shift = block->new_bblock(); set_of = block->new_bblock(); cont = block->new_bblock(); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); // mask is 5 bits for operands <= 32bits, 6 bits for 64 bits operands unsigned int mask = (op0.size == 64)? 0b111111 : 0b11111; /* Mask the number of rotations */ tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_and(tmp0, x86_arg_extract(op1, op0.size-1, 0), ir_cst(mask, op0.size-1, 0), addr)); /* Check if count is 0 */ block->add_instr(bblkid, ir_bcc(tmp0, ir_cst(shift, 31, 0), ir_cst(cont, 31, 0), addr)); // Do shift /* Affect CF (last bit shifted out) */ tmp1 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp4 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(shift, ir_sub(tmp1, ir_cst(op0.size, tmp0.size-1, 0), tmp0, addr)); // Num of the last bit that'll be shifted out //block->add_instr(shift, ir_neg(tmp1, tmp1, addr)); // Shift right to get the bit block->add_instr(shift, ir_shr(tmp4, op0, tmp1, addr)); block->add_instr(shift, ir_mov(x86_arg_extract(cf, 0, 0), x86_arg_extract(tmp4, 0, 0), addr)); /* Do the shift */ tmp2 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(shift, ir_shl(tmp2, op0, tmp0, addr)); if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(shift, ir_stm(dest, tmp2, addr)); }else{ x86_adjust_reg_assign(mode, addr, block, shift, tmp_var_count, dest, tmp2); } /* Affect OF flag iff masked count == 1 */ tmp3 = ir_tmp(tmp_var_count++, tmp0.size-1, 0); block->add_instr(shift, ir_xor(tmp3, tmp0, ir_cst(1, tmp0.size-1, 0), addr)); block->add_instr(shift, ir_bcc(tmp3, ir_cst(cont, 31, 0), ir_cst(set_of, 31, 0), addr)); block->add_instr(set_of, ir_xor(x86_arg_extract(of, 0, 0), x86_arg_extract(tmp2, tmp2.size-1, tmp2.size-1), x86_arg_extract(cf, 0, 0), addr)); block->add_instr(set_of, ir_bcc(ir_cst(1, 31, 0) , ir_cst(cont, 31, 0), ir_none(), addr)); // Update PC pc = x86_get_pc(mode); block->add_instr(cont, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = cont; return; } inline void x86_sar_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, pc, tmp0, cf, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, of; IRBasicBlockId set_of, cont, pos, neg, shift; shift = block->new_bblock(); pos = block->new_bblock(); neg = block->new_bblock(); set_of = block->new_bblock(); cont = block->new_bblock(); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); // mask is 5 bits for operands <= 32bits, 6 bits for 64 bits operands unsigned int mask = (op0.size == 64)? 0b111111 : 0b11111; /* Mask the number of rotations */ tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp3 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_and(tmp0, x86_arg_extract(op1, op0.size-1, 0), ir_cst(mask, op0.size-1, 0), addr)); block->add_instr(bblkid, ir_mov(tmp3, tmp0, addr)); // save in tmp3 /* Check if count is 0 */ block->add_instr(bblkid, ir_bcc(tmp0, ir_cst(shift, 31, 0), ir_cst(cont, 31, 0), addr)); // Do shift /* Affect CF (last bit shifted out) */ tmp1 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp4 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(shift, ir_sub(tmp1, tmp0, ir_cst(1, tmp0.size-1, 0), addr)); // Num of the last bit that'll be shifted out //block->add_instr(shift, ir_neg(tmp1, tmp1, addr)); // Shift right to get the bit block->add_instr(shift, ir_shr(tmp4, op0, tmp1, addr)); block->add_instr(shift, ir_mov(x86_arg_extract(cf, 0, 0), x86_arg_extract(tmp4, 0, 0), addr)); /* Get mask for sign propagation when shifting */ tmp5 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp6 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(shift, ir_bcc(x86_arg_extract(op0, op0.size-1, op0.size-1), ir_cst(neg, 31, 0), ir_cst(pos, 31, 0), addr)); shift = block->new_bblock(); block->add_instr(pos, ir_mov(tmp5, ir_cst(0, tmp5.size-1, 0), addr)); block->add_instr(pos, ir_bcc(ir_cst(1, 31, 0), ir_cst(shift, 31, 0), ir_none(), addr)); block->add_instr(neg, ir_mov(tmp5, ir_cst(-1, tmp5.size-1, 0), addr)); block->add_instr(neg, ir_sub(tmp6, ir_cst(op0.size, tmp0.size-1, 0), tmp0, addr)); block->add_instr(neg, ir_shl(tmp5, tmp5, tmp6, addr)); block->add_instr(neg, ir_bcc(ir_cst(1, 31, 0), ir_cst(shift, 31, 0), ir_none(), addr)); /* Do the shift */ tmp2 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(shift, ir_shr(tmp2, op0, tmp0, addr)); block->add_instr(shift, ir_or(tmp2, tmp2, tmp5, addr)); if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(shift, ir_stm(dest, tmp2, addr)); }else{ x86_adjust_reg_assign(mode, addr, block, shift, tmp_var_count, dest, tmp2); } /* Affect OF flag iff masked count == 1 */ block->add_instr(shift, ir_xor(tmp3, tmp3, ir_cst(1, tmp0.size-1, 0), addr)); block->add_instr(shift, ir_bcc(tmp3, ir_cst(cont, 31, 0), ir_cst(set_of, 31, 0), addr)); block->add_instr(set_of, ir_mov(of, ir_cst(0, of.size-1, 0), addr)); block->add_instr(set_of, ir_bcc(ir_cst(1, 31, 0) , ir_cst(cont, 31, 0), ir_none(), addr)); // Update PC pc = x86_get_pc(mode); block->add_instr(cont, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = cont; return; } inline void x86_scasb_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, al, di, df, tmp0, tmp1; IRBasicBlockId inc, dec, end, prefix_start; df = (mode == CPUMode::X86)? ir_var(X86_DF, 31, 0) : ir_var(X64_DF, 63, 0); al = (mode == CPUMode::X86)? ir_var(X86_EAX, 7, 0): ir_var(X64_RAX, 7, 0); di = (mode == CPUMode::X86)? ir_var(X86_EDI, 31, 0): ir_var(X64_RDI, 63, 0); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); prefix_start = _x86_init_prefix(mode, instr, addr, block, bblkid); /* Load byte */ tmp0 = ir_tmp(tmp_var_count++, al.size-1, 0); tmp1 = ir_tmp(tmp_var_count++, al.size-1, 0); block->add_instr(bblkid, ir_ldm(tmp0, di, addr)); block->add_instr(bblkid, ir_sub(tmp1, al, tmp0, addr)); /* Set flags */ x86_set_pf( mode, tmp1, addr, block, bblkid, tmp_var_count ); x86_set_zf( mode, tmp1, addr, block, bblkid ); x86_set_sf( mode, tmp1, addr, block, bblkid ); x86_sub_set_of( mode, al, tmp0, tmp1, addr, block, bblkid, tmp_var_count ); x86_sub_set_cf( mode, al, tmp0, tmp1, addr, block, bblkid, tmp_var_count ); x86_sub_set_af( mode, al, tmp0, tmp1, addr, block, bblkid, tmp_var_count ); /* Adjust DI */ inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, df.size-1, 0), ir_cst(inc, df.size-1, 0), addr)); /* Increment */ block->add_instr(inc, ir_add(di, di, ir_cst(1, di.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Or decrement */ block->add_instr(dec, ir_sub(di, di, ir_cst(1, di.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Add prefix if any */ _x86_end_prefix(mode, instr, addr, block, prefix_start, end, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_scasd_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, eax, di, df, tmp0, tmp1; IRBasicBlockId inc, dec, end, prefix_start; df = (mode == CPUMode::X86)? ir_var(X86_DF, 31, 0) : ir_var(X64_DF, 63, 0); eax = (mode == CPUMode::X86)? ir_var(X86_EAX, 31, 0): ir_var(X64_RAX, 31, 0); di = (mode == CPUMode::X86)? ir_var(X86_EDI, 31, 0): ir_var(X64_RDI, 31, 0); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); prefix_start = _x86_init_prefix(mode, instr, addr, block, bblkid); /* Load byte */ tmp0 = ir_tmp(tmp_var_count++, eax.size-1, 0); tmp1 = ir_tmp(tmp_var_count++, eax.size-1, 0); block->add_instr(bblkid, ir_ldm(tmp0, di, addr)); block->add_instr(bblkid, ir_sub(tmp1, eax, tmp0, addr)); /* Set flags */ x86_set_pf( mode, tmp1, addr, block, bblkid, tmp_var_count ); x86_set_zf( mode, tmp1, addr, block, bblkid ); x86_set_sf( mode, tmp1, addr, block, bblkid ); x86_sub_set_of( mode, eax, tmp0, tmp1, addr, block, bblkid, tmp_var_count ); x86_sub_set_cf( mode, eax, tmp0, tmp1, addr, block, bblkid, tmp_var_count ); x86_sub_set_af( mode, eax, tmp0, tmp1, addr, block, bblkid, tmp_var_count ); /* Adjust DI */ inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, df.size-1, 0), ir_cst(inc, df.size-1, 0), addr)); /* Increment */ block->add_instr(inc, ir_add(di, di, ir_cst(4, di.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Or decrement */ block->add_instr(dec, ir_sub(di, di, ir_cst(4, di.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Add prefix if any */ _x86_end_prefix(mode, instr, addr, block, prefix_start, end, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_scasq_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, rax, di, df, tmp0, tmp1; IRBasicBlockId inc, dec, end, prefix_start; df = ir_var(X64_DF, 63, 0); rax = ir_var(X64_RAX, 63, 0); di = ir_var(X64_RDI, 63, 0); pc = ir_var(X64_RIP, 63, 0); prefix_start = _x86_init_prefix(mode, instr, addr, block, bblkid); /* Load byte */ tmp0 = ir_tmp(tmp_var_count++, rax.size-1, 0); tmp1 = ir_tmp(tmp_var_count++, rax.size-1, 0); block->add_instr(bblkid, ir_ldm(tmp0, di, addr)); block->add_instr(bblkid, ir_sub(tmp1, rax, tmp0, addr)); /* Set flags */ x86_set_pf( mode, tmp1, addr, block, bblkid, tmp_var_count ); x86_set_zf( mode, tmp1, addr, block, bblkid ); x86_set_sf( mode, tmp1, addr, block, bblkid ); x86_sub_set_of( mode, rax, tmp0, tmp1, addr, block, bblkid, tmp_var_count ); x86_sub_set_cf( mode, rax, tmp0, tmp1, addr, block, bblkid, tmp_var_count ); x86_sub_set_af( mode, rax, tmp0, tmp1, addr, block, bblkid, tmp_var_count ); /* Adjust DI */ inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, df.size-1, 0), ir_cst(inc, df.size-1, 0), addr)); /* Increment */ block->add_instr(inc, ir_add(di, di, ir_cst(8, di.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Or decrement */ block->add_instr(dec, ir_sub(di, di, ir_cst(8, di.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Add prefix if any */ _x86_end_prefix(mode, instr, addr, block, prefix_start, end, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_scasw_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, ax, di, df, tmp0, tmp1; IRBasicBlockId inc, dec, end, prefix_start; df = (mode == CPUMode::X86)? ir_var(X86_DF, 31, 0) : ir_var(X64_DF, 63, 0); ax = (mode == CPUMode::X86)? ir_var(X86_EAX, 15, 0): ir_var(X64_RAX, 15, 0); di = (mode == CPUMode::X86)? ir_var(X86_EDI, 31, 0): ir_var(X64_RDI, 63, 0); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); prefix_start = _x86_init_prefix(mode, instr, addr, block, bblkid); /* Load byte */ tmp0 = ir_tmp(tmp_var_count++, ax.size-1, 0); tmp1 = ir_tmp(tmp_var_count++, ax.size-1, 0); block->add_instr(bblkid, ir_ldm(tmp0, di, addr)); block->add_instr(bblkid, ir_sub(tmp1, ax, tmp0, addr)); /* Set flags */ x86_set_pf( mode, tmp1, addr, block, bblkid, tmp_var_count ); x86_set_zf( mode, tmp1, addr, block, bblkid ); x86_set_sf( mode, tmp1, addr, block, bblkid ); x86_sub_set_of( mode, ax, tmp0, tmp1, addr, block, bblkid, tmp_var_count ); x86_sub_set_cf( mode, ax, tmp0, tmp1, addr, block, bblkid, tmp_var_count ); x86_sub_set_af( mode, ax, tmp0, tmp1, addr, block, bblkid, tmp_var_count ); /* Adjust DI */ inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, df.size-1, 0), ir_cst(inc, df.size-1, 0), addr)); /* Increment */ block->add_instr(inc, ir_add(di, di, ir_cst(2, di.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Or decrement */ block->add_instr(dec, ir_sub(di, di, ir_cst(2, di.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); /* Add prefix if any */ _x86_end_prefix(mode, instr, addr, block, prefix_start, end, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_seta_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand cf, zf, pc, tmp0, tmp1, op0; IRBasicBlockId set = block->new_bblock(), dont_set = block->new_bblock(), end = block->new_bblock(); cf = (mode==CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); zf = (mode==CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); tmp0 = ir_tmp(tmp_var_count++, 0, 0); tmp1 = ir_tmp(tmp_var_count++, 0, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if CF = 0 and ZF = 0 block->add_instr(bblkid, ir_not(tmp0, cf, addr)); block->add_instr(bblkid, ir_not(tmp1, zf, addr)); block->add_instr(bblkid, ir_and(tmp1, tmp1, tmp0, addr)); block->add_instr(bblkid, ir_bcc(tmp1, ir_cst(set,31, 0), ir_cst(dont_set, 31, 0), addr)); // do set if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(set, ir_stm(op0, ir_cst(1, op0.size-1, 0), addr)); }else{ block->add_instr(set, ir_mov(op0, ir_cst(1, op0.size-1, 0), addr)); } block->add_instr(set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont set - put zero if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(dont_set, ir_stm(op0, ir_cst(0, op0.size-1, 0), addr)); }else{ block->add_instr(dont_set, ir_mov(op0, ir_cst(0, op0.size-1, 0), addr)); } block->add_instr(dont_set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_setae_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand cf, pc, tmp0, tmp1, op0; IRBasicBlockId set = block->new_bblock(), dont_set = block->new_bblock(), end = block->new_bblock(); cf = (mode==CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if CF = 0 block->add_instr(bblkid, ir_bcc(cf, ir_cst(dont_set,31, 0), ir_cst(set, 31, 0), addr)); // do set if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(set, ir_stm(op0, ir_cst(1, op0.size-1, 0), addr)); }else{ block->add_instr(set, ir_mov(op0, ir_cst(1, op0.size-1, 0), addr)); } block->add_instr(set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont set - put zero if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(dont_set, ir_stm(op0, ir_cst(0, op0.size-1, 0), addr)); }else{ block->add_instr(dont_set, ir_mov(op0, ir_cst(0, op0.size-1, 0), addr)); } block->add_instr(dont_set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_setb_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand cf, pc, tmp0, tmp1, op0; IRBasicBlockId set = block->new_bblock(), dont_set = block->new_bblock(), end = block->new_bblock(); cf = (mode==CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if CF = 1 block->add_instr(bblkid, ir_bcc(cf, ir_cst(set,31, 0), ir_cst(dont_set, 31, 0), addr)); // do set if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(set, ir_stm(op0, ir_cst(1, op0.size-1, 0), addr)); }else{ block->add_instr(set, ir_mov(op0, ir_cst(1, op0.size-1, 0), addr)); } block->add_instr(set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont set - put zero if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(dont_set, ir_stm(op0, ir_cst(0, op0.size-1, 0), addr)); }else{ block->add_instr(dont_set, ir_mov(op0, ir_cst(0, op0.size-1, 0), addr)); } block->add_instr(dont_set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_setbe_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand cf, zf, pc, tmp0, op0; IRBasicBlockId set = block->new_bblock(), dont_set = block->new_bblock(), end = block->new_bblock(); cf = (mode==CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); zf = (mode==CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); tmp0 = ir_tmp(tmp_var_count++, cf.size-1, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if CF = 1 or ZF = 1 block->add_instr(bblkid, ir_or(tmp0, cf, zf, addr)); block->add_instr(bblkid, ir_bcc(tmp0, ir_cst(set,31, 0), ir_cst(dont_set, 31, 0), addr)); // do set if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(set, ir_stm(op0, ir_cst(1, op0.size-1, 0), addr)); }else{ block->add_instr(set, ir_mov(op0, ir_cst(1, op0.size-1, 0), addr)); } block->add_instr(set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont set - put zero if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(dont_set, ir_stm(op0, ir_cst(0, op0.size-1, 0), addr)); }else{ block->add_instr(dont_set, ir_mov(op0, ir_cst(0, op0.size-1, 0), addr)); } block->add_instr(dont_set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_sete_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand zf, pc, tmp0, tmp1, op0; IRBasicBlockId set = block->new_bblock(), dont_set = block->new_bblock(), end = block->new_bblock(); zf = (mode==CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if ZF = 1 block->add_instr(bblkid, ir_bcc(zf, ir_cst(set,31, 0), ir_cst(dont_set, 31, 0), addr)); // do set if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(set, ir_stm(op0, ir_cst(1, op0.size-1, 0), addr)); }else{ block->add_instr(set, ir_mov(op0, ir_cst(1, op0.size-1, 0), addr)); } block->add_instr(set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont set - put zero if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(dont_set, ir_stm(op0, ir_cst(0, op0.size-1, 0), addr)); }else{ block->add_instr(dont_set, ir_mov(op0, ir_cst(0, op0.size-1, 0), addr)); } block->add_instr(dont_set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_setg_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand sf, zf, of, pc, tmp0, tmp1, op0; IRBasicBlockId set = block->new_bblock(), dont_set = block->new_bblock(), end = block->new_bblock(); sf = (mode==CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); of = (mode==CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); zf = (mode==CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); tmp0 = ir_tmp(tmp_var_count++, 0, 0); tmp1 = ir_tmp(tmp_var_count++, 0, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if ZF = 0 and SF=OF block->add_instr(bblkid, ir_xor(tmp0, x86_arg_extract(sf, 0, 0), x86_arg_extract(of, 0, 0), addr)); block->add_instr(bblkid, ir_not(tmp0, tmp0, addr)); block->add_instr(bblkid, ir_not(tmp1, x86_arg_extract(zf, 0, 0), addr)); block->add_instr(bblkid, ir_and(tmp1, tmp1, tmp0, addr)); block->add_instr(bblkid, ir_bcc(tmp1, ir_cst(set,31, 0), ir_cst(dont_set, 31, 0), addr)); // do set if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(set, ir_stm(op0, ir_cst(1, op0.size-1, 0), addr)); }else{ block->add_instr(set, ir_mov(op0, ir_cst(1, op0.size-1, 0), addr)); } block->add_instr(set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont set - put zero if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(dont_set, ir_stm(op0, ir_cst(0, op0.size-1, 0), addr)); }else{ block->add_instr(dont_set, ir_mov(op0, ir_cst(0, op0.size-1, 0), addr)); } block->add_instr(dont_set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_setge_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand sf, of, pc, tmp0, tmp1, op0; IRBasicBlockId set = block->new_bblock(), dont_set = block->new_bblock(), end = block->new_bblock(); sf = (mode==CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); of = (mode==CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); tmp0 = ir_tmp(tmp_var_count++, 0, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if SF=OF block->add_instr(bblkid, ir_xor(tmp0, x86_arg_extract(sf, 0, 0), x86_arg_extract(of, 0, 0), addr)); block->add_instr(bblkid, ir_bcc(tmp0, ir_cst(dont_set,31, 0), ir_cst(set, 31, 0), addr)); // do set if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(set, ir_stm(op0, ir_cst(1, op0.size-1, 0), addr)); }else{ block->add_instr(set, ir_mov(op0, ir_cst(1, op0.size-1, 0), addr)); } block->add_instr(set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont set - put zero if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(dont_set, ir_stm(op0, ir_cst(0, op0.size-1, 0), addr)); }else{ block->add_instr(dont_set, ir_mov(op0, ir_cst(0, op0.size-1, 0), addr)); } block->add_instr(dont_set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_setl_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand sf, of, pc, tmp0, tmp1, op0; IRBasicBlockId set = block->new_bblock(), dont_set = block->new_bblock(), end = block->new_bblock(); sf = (mode==CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); of = (mode==CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); tmp0 = ir_tmp(tmp_var_count++, 0, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if SF != OF block->add_instr(bblkid, ir_xor(tmp0, x86_arg_extract(sf, 0, 0), x86_arg_extract(of, 0, 0), addr)); block->add_instr(bblkid, ir_bcc(tmp0, ir_cst(set,31, 0), ir_cst(dont_set, 31, 0), addr)); // do set if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(set, ir_stm(op0, ir_cst(1, op0.size-1, 0), addr)); }else{ block->add_instr(set, ir_mov(op0, ir_cst(1, op0.size-1, 0), addr)); } block->add_instr(set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont set - put zero if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(dont_set, ir_stm(op0, ir_cst(0, op0.size-1, 0), addr)); }else{ block->add_instr(dont_set, ir_mov(op0, ir_cst(0, op0.size-1, 0), addr)); } block->add_instr(dont_set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_setle_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand sf, zf, of, pc, tmp0, tmp1, op0; IRBasicBlockId set = block->new_bblock(), dont_set = block->new_bblock(), end = block->new_bblock(); sf = (mode==CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); of = (mode==CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); zf = (mode==CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); tmp0 = ir_tmp(tmp_var_count++, 0, 0); tmp1 = ir_tmp(tmp_var_count++, 0, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if ZF = 1 or SF != OF block->add_instr(bblkid, ir_xor(tmp0, x86_arg_extract(sf, 0, 0), x86_arg_extract(of, 0, 0), addr)); block->add_instr(bblkid, ir_or(tmp0, x86_arg_extract(zf, 0, 0), tmp0, addr)); block->add_instr(bblkid, ir_bcc(tmp0, ir_cst(set,31, 0), ir_cst(dont_set, 31, 0), addr)); // do set if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(set, ir_stm(op0, ir_cst(1, op0.size-1, 0), addr)); }else{ block->add_instr(set, ir_mov(op0, ir_cst(1, op0.size-1, 0), addr)); } block->add_instr(set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont set - put zero if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(dont_set, ir_stm(op0, ir_cst(0, op0.size-1, 0), addr)); }else{ block->add_instr(dont_set, ir_mov(op0, ir_cst(0, op0.size-1, 0), addr)); } block->add_instr(dont_set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_setne_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand zf, pc, tmp0, tmp1, op0; IRBasicBlockId set = block->new_bblock(), dont_set = block->new_bblock(), end = block->new_bblock(); zf = (mode==CPUMode::X86)? ir_var(X86_ZF, 31, 0) : ir_var(X64_ZF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if ZF = 0 block->add_instr(bblkid, ir_bcc(zf, ir_cst(dont_set,31, 0), ir_cst(set, 31, 0), addr)); // do set if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(set, ir_stm(op0, ir_cst(1, op0.size-1, 0), addr)); }else{ block->add_instr(set, ir_mov(op0, ir_cst(1, op0.size-1, 0), addr)); } block->add_instr(set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont set - put zero if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(dont_set, ir_stm(op0, ir_cst(0, op0.size-1, 0), addr)); }else{ block->add_instr(dont_set, ir_mov(op0, ir_cst(0, op0.size-1, 0), addr)); } block->add_instr(dont_set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_setno_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand of, pc, tmp0, tmp1, op0; IRBasicBlockId set = block->new_bblock(), dont_set = block->new_bblock(), end = block->new_bblock(); of = (mode==CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if OF = 0 block->add_instr(bblkid, ir_bcc(of, ir_cst(dont_set,31, 0), ir_cst(set, 31, 0), addr)); // do set if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(set, ir_stm(op0, ir_cst(1, op0.size-1, 0), addr)); }else{ block->add_instr(set, ir_mov(op0, ir_cst(1, op0.size-1, 0), addr)); } block->add_instr(set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont set - put zero if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(dont_set, ir_stm(op0, ir_cst(0, op0.size-1, 0), addr)); }else{ block->add_instr(dont_set, ir_mov(op0, ir_cst(0, op0.size-1, 0), addr)); } block->add_instr(dont_set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_setnp_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pf, pc, tmp0, tmp1, op0; IRBasicBlockId set = block->new_bblock(), dont_set = block->new_bblock(), end = block->new_bblock(); pf = (mode==CPUMode::X86)? ir_var(X86_PF, 31, 0) : ir_var(X64_PF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if PF = 0 block->add_instr(bblkid, ir_bcc(pf, ir_cst(dont_set,31, 0), ir_cst(set, 31, 0), addr)); // do set if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(set, ir_stm(op0, ir_cst(1, op0.size-1, 0), addr)); }else{ block->add_instr(set, ir_mov(op0, ir_cst(1, op0.size-1, 0), addr)); } block->add_instr(set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont set - put zero if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(dont_set, ir_stm(op0, ir_cst(0, op0.size-1, 0), addr)); }else{ block->add_instr(dont_set, ir_mov(op0, ir_cst(0, op0.size-1, 0), addr)); } block->add_instr(dont_set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_setns_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand sf, pc, tmp0, tmp1, op0; IRBasicBlockId set = block->new_bblock(), dont_set = block->new_bblock(), end = block->new_bblock(); sf = (mode==CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if SF = 0 block->add_instr(bblkid, ir_bcc(sf, ir_cst(dont_set,31, 0), ir_cst(set, 31, 0), addr)); // do set if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(set, ir_stm(op0, ir_cst(1, op0.size-1, 0), addr)); }else{ block->add_instr(set, ir_mov(op0, ir_cst(1, op0.size-1, 0), addr)); } block->add_instr(set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont set - put zero if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(dont_set, ir_stm(op0, ir_cst(0, op0.size-1, 0), addr)); }else{ block->add_instr(dont_set, ir_mov(op0, ir_cst(0, op0.size-1, 0), addr)); } block->add_instr(dont_set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_seto_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand of, pc, tmp0, tmp1, op0; IRBasicBlockId set = block->new_bblock(), dont_set = block->new_bblock(), end = block->new_bblock(); of = (mode==CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if OF = 1 block->add_instr(bblkid, ir_bcc(of, ir_cst(set,31, 0), ir_cst(dont_set, 31, 0), addr)); // do set if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(set, ir_stm(op0, ir_cst(1, op0.size-1, 0), addr)); }else{ block->add_instr(set, ir_mov(op0, ir_cst(1, op0.size-1, 0), addr)); } block->add_instr(set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont set - put zero if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(dont_set, ir_stm(op0, ir_cst(0, op0.size-1, 0), addr)); }else{ block->add_instr(dont_set, ir_mov(op0, ir_cst(0, op0.size-1, 0), addr)); } block->add_instr(dont_set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_setp_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pf, pc, tmp0, tmp1, op0; IRBasicBlockId set = block->new_bblock(), dont_set = block->new_bblock(), end = block->new_bblock(); pf = (mode==CPUMode::X86)? ir_var(X86_PF, 31, 0) : ir_var(X64_PF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if PF = 1 block->add_instr(bblkid, ir_bcc(pf, ir_cst(set,31, 0), ir_cst(dont_set, 31, 0), addr)); // do set if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(set, ir_stm(op0, ir_cst(1, op0.size-1, 0), addr)); }else{ block->add_instr(set, ir_mov(op0, ir_cst(1, op0.size-1, 0), addr)); } block->add_instr(set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont set - put zero if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(dont_set, ir_stm(op0, ir_cst(0, op0.size-1, 0), addr)); }else{ block->add_instr(dont_set, ir_mov(op0, ir_cst(0, op0.size-1, 0), addr)); } block->add_instr(dont_set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_sets_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand sf, pc, tmp0, tmp1, op0; IRBasicBlockId set = block->new_bblock(), dont_set = block->new_bblock(), end = block->new_bblock(); sf = (mode==CPUMode::X86)? ir_var(X86_SF, 31, 0) : ir_var(X64_SF, 63, 0); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); // test if SF = 1 block->add_instr(bblkid, ir_bcc(sf, ir_cst(set,31, 0), ir_cst(dont_set, 31, 0), addr)); // do set if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(set, ir_stm(op0, ir_cst(1, op0.size-1, 0), addr)); }else{ block->add_instr(set, ir_mov(op0, ir_cst(1, op0.size-1, 0), addr)); } block->add_instr(set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); // dont set - put zero if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(dont_set, ir_stm(op0, ir_cst(0, op0.size-1, 0), addr)); }else{ block->add_instr(dont_set, ir_mov(op0, ir_cst(0, op0.size-1, 0), addr)); } block->add_instr(dont_set, ir_bcc(ir_cst(1, 31, 0) , ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_shr_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, pc, tmp0, cf, tmp1, tmp2, tmp3, tmp4, tmp5, of; IRBasicBlockId set_of, cont, end; cont = block->new_bblock(); set_of = block->new_bblock(); end = block->new_bblock(); /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); // 5 bits for <= 32bits, 6 bits for 64bits unsigned int mask = (op0.size == 64 )? 0b111111 : 0b11111; /* Mask the number of rotations */ tmp0 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_and(tmp0, x86_arg_extract(op1, op0.size-1, 0), ir_cst(mask, op0.size-1, 0), addr)); /* Test if masked count is 0 */ block->add_instr(bblkid, ir_bcc(tmp0, ir_cst(cont, 31, 0), ir_cst(end, 31, 0), addr)); // Do shift /* Affect CF (last bit shifted out) */ tmp1 = ir_tmp(tmp_var_count++, op0.size-1, 0); tmp4 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(cont, ir_sub(tmp1, tmp0, ir_cst(1, tmp0.size-1, 0), addr)); // Num of the last bit that'll be shifted out block->add_instr(cont, ir_shr(tmp4, op0, tmp1, addr)); block->add_instr(cont, ir_mov(x86_arg_extract(cf, 0, 0), x86_arg_extract(tmp4, 0, 0), addr)); /* Shift */ tmp2 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(cont, ir_shr(tmp2, op0, tmp0, addr)); /* Save op0 */ tmp5 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(cont, ir_mov(tmp5, op0, addr)); // Assign res if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(cont, ir_stm(dest, tmp2, addr)); }else{ x86_adjust_reg_assign(mode, addr, block, cont, tmp_var_count, dest, tmp2); } /* Affect OF flag iff masked count == 1 */ tmp3 = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(cont, ir_xor(tmp3, tmp0, ir_cst(1, tmp0.size-1, 0), addr)); block->add_instr(cont, ir_bcc(tmp3, ir_cst(end, 31, 0), ir_cst(set_of, 31, 0), addr)); block->add_instr(set_of, ir_mov(x86_arg_extract(of,0,0), x86_arg_extract(op0, op0.size-1, op0.size-1), addr)); block->add_instr(set_of, ir_bcc(ir_cst(1, 31, 0), ir_cst(end, 31, 0), ir_none(), addr)); // Update PC pc = x86_get_pc(mode); block->add_instr(end, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); bblkid = end; return; } inline void x86_stc_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, cf; /* Get operand */ cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); /* Set flag */ block->add_instr(bblkid, ir_mov(cf, ir_cst(1, cf.size-1, 0), addr)); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_std_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, df; /* Get operand */ df = (mode == CPUMode::X86)? ir_var(X86_DF, 31, 0) : ir_var(X64_DF, 63, 0); /* Set flag */ block->add_instr(bblkid, ir_mov(df, ir_cst(1, df.size-1, 0), addr)); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_sti_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, iflag; /* Get operand */ iflag = (mode == CPUMode::X86)? ir_var(X86_IF, 31, 0) : ir_var(X64_IF, 63, 0); /* Set flag */ block->add_instr(bblkid, ir_mov(iflag, ir_cst(1, iflag.size-1, 0), addr)); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_stosb_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, al, di, df; IRBasicBlockId inc, dec, end; df = (mode == CPUMode::X86)? ir_var(X86_DF, 31, 0) : ir_var(X64_DF, 63, 0); al = (mode == CPUMode::X86)? ir_var(X86_EAX, 7, 0): ir_var(X64_RAX, 7, 0); di = (mode == CPUMode::X86)? ir_var(X86_EDI, 31, 0): ir_var(X64_RDI, 63, 0); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); // Update PC block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); /* Store byte */ block->add_instr(bblkid, ir_stm(di, al, addr)); /* Adjust DI */ inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, df.size-1, 0), ir_cst(inc, df.size-1, 0), addr)); /* Increment */ block->add_instr(inc, ir_add(di, di, ir_cst(1, di.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0), ir_cst(end, 31, 0), ir_none(), addr)); /* Or decrement */ block->add_instr(dec, ir_sub(di, di, ir_cst(1, di.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0), ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_stosd_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, eax, di, df; IRBasicBlockId inc, dec, end; df = (mode == CPUMode::X86)? ir_var(X86_DF, 31, 0) : ir_var(X64_DF, 63, 0); eax = (mode == CPUMode::X86)? ir_var(X86_EAX, 31, 0): ir_var(X64_RAX, 31, 0); di = (mode == CPUMode::X86)? ir_var(X86_EDI, 31, 0): ir_var(X64_RDI, 63, 0); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); // Update PC block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); /* Store byte */ block->add_instr(bblkid, ir_stm(di, eax, addr)); /* Adjust DI */ inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, df.size-1, 0), ir_cst(inc, df.size-1, 0), addr)); /* Increment */ block->add_instr(inc, ir_add(di, di, ir_cst(4, di.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0), ir_cst(end, 31, 0), ir_none(), addr)); /* Or decrement */ block->add_instr(dec, ir_sub(di, di, ir_cst(4, di.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0), ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_stosq_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, rax, di, df; IRBasicBlockId inc, dec, end; df = ir_var(X64_DF, 63, 0); rax = ir_var(X64_RAX, 63, 0); di = ir_var(X64_RDI, 63, 0); pc = ir_var(X64_RIP, 63, 0); // Update PC block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); /* Store byte */ block->add_instr(bblkid, ir_stm(di, rax, addr)); /* Adjust DI */ inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, df.size-1, 0), ir_cst(inc, df.size-1, 0), addr)); /* Increment */ block->add_instr(inc, ir_add(di, di, ir_cst(8, di.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0), ir_cst(end, 31, 0), ir_none(), addr)); /* Or decrement */ block->add_instr(dec, ir_sub(di, di, ir_cst(8, di.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0), ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_stosw_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, ax, di, df; IRBasicBlockId inc, dec, end; df = (mode == CPUMode::X86)? ir_var(X86_DF, 31, 0) : ir_var(X64_DF, 63, 0); ax = (mode == CPUMode::X86)? ir_var(X86_EAX, 15, 0): ir_var(X64_RAX, 15, 0); di = (mode == CPUMode::X86)? ir_var(X86_EDI, 31, 0): ir_var(X64_RDI, 63, 0); pc = (mode == CPUMode::X86)? ir_var(X86_EIP, 31, 0) : ir_var(X64_RIP, 63, 0); // Update PC block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); /* Store byte */ block->add_instr(bblkid, ir_stm(di, ax, addr)); /* Adjust DI */ inc = block->new_bblock(); dec = block->new_bblock(); end = block->new_bblock(); block->add_instr(bblkid, ir_bcc(df, ir_cst(dec, df.size-1, 0), ir_cst(inc, df.size-1, 0), addr)); /* Increment */ block->add_instr(inc, ir_add(di, di, ir_cst(2, di.size-1, 0), addr)); block->add_instr(inc, ir_bcc(ir_cst(1, 31, 0), ir_cst(end, 31, 0), ir_none(), addr)); /* Or decrement */ block->add_instr(dec, ir_sub(di, di, ir_cst(2, di.size-1, 0), addr)); block->add_instr(dec, ir_bcc(ir_cst(1, 31, 0), ir_cst(end, 31, 0), ir_none(), addr)); bblkid = end; return; } inline void x86_sub_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, op0, op1, tmp, dest; /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); // tmp <- op0 - op1 tmp = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_sub(tmp, op0, op1, addr)); // Update flags x86_set_pf( mode, tmp, addr, block, bblkid, tmp_var_count ); x86_set_zf( mode, tmp, addr, block, bblkid ); x86_set_sf( mode, tmp, addr, block, bblkid ); x86_sub_set_of( mode, op0, op1, tmp, addr, block, bblkid, tmp_var_count ); x86_sub_set_cf( mode, op0, op1, tmp, addr, block, bblkid, tmp_var_count ); x86_sub_set_af( mode, op0, op1, tmp, addr, block, bblkid, tmp_var_count ); /* Set dest operand */ if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(bblkid, ir_stm(dest, tmp, addr)); }else{ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, tmp); } // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_syscall_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, type, next_pc; if( mode == CPUMode::X86 ){ throw unsupported_instruction_exception("SYSCALL: not supported in X86 mode"); } /* Get operands */ pc = x86_get_pc(mode); next_pc = ir_tmp(tmp_var_count++, pc.size-1, 0); block->add_instr(bblkid, ir_add(next_pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); type = ir_cst(SYSCALL_X64_SYSCALL, 63, 0); /* Create interrupt */ block->add_instr(bblkid, ir_syscall(type, next_pc, addr)); return; } inline void x86_sysenter_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, type, next_pc; if( mode == CPUMode::X64 ){ throw unsupported_instruction_exception("SYSENTER: not supported in X64 mode"); } /* Get operands */ pc = x86_get_pc(mode); next_pc = ir_tmp(tmp_var_count++, pc.size-1, 0); block->add_instr(bblkid, ir_add(next_pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); type = ir_cst(SYSCALL_X86_SYSENTER, 31, 0); /* Create interrupt */ block->add_instr(bblkid, ir_syscall(type, next_pc, addr)); return; } inline void x86_test_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand pc, op0, op1, tmp, cf, of; /* Get operands */ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0): ir_var(X64_CF, 63, 0); of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0): ir_var(X64_OF, 63, 0); // tmp <- op0 & op1 tmp = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_and(tmp, op0, op1, addr)); // Update flags (except AF that is undefined) x86_set_pf( mode, tmp, addr, block, bblkid, tmp_var_count ); x86_set_zf( mode, tmp, addr, block, bblkid ); x86_set_sf( mode, tmp, addr, block, bblkid ); block->add_instr(bblkid, ir_mov(cf, ir_cst(0, cf.size-1, 0), addr)); block->add_instr(bblkid, ir_mov(of, ir_cst(0, of.size-1, 0), addr)); // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_xadd_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, res, pc, tmp; /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); /* Do the add */ res = ir_tmp(tmp_var_count++, op0.size-1, 0); block->add_instr(bblkid, ir_add(res, op0, op1, addr)); /* Update flags */ x86_set_zf(mode, res, addr, block, bblkid); x86_add_set_cf(mode, op0, op1, res, addr, block, bblkid, tmp_var_count); x86_add_set_af(mode, op0, op1, res, addr, block, bblkid, tmp_var_count); x86_add_set_of(mode, op0, op1, res, addr, block, bblkid, tmp_var_count); x86_set_sf(mode, res, addr, block, bblkid); x86_set_pf(mode, res, addr, block, bblkid, tmp_var_count); /* Exchange operands */ if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ tmp = ir_tmp(tmp_var_count++, dest.size-1, 0); block->add_instr(bblkid, ir_mov(tmp, dest, addr)); // In case dest is op1 block->add_instr(bblkid, ir_mov(op1, op0, addr)); block->add_instr(bblkid, ir_stm(tmp, res, addr)); }else{ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, op1, op0); x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, res); } // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_xchg_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, pc, tmp, tmp2; /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); tmp2 = ir_tmp(tmp_var_count++, op1.size-1, 0); block->add_instr(bblkid, ir_mov(tmp2, op1, addr)); /* Exchange operands */ if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ tmp = ir_tmp(tmp_var_count++, dest.size-1, 0); block->add_instr(bblkid, ir_mov(tmp, dest, addr)); // In case dest is op1 block->add_instr(bblkid, ir_mov(op1, op0, addr)); block->add_instr(bblkid, ir_stm(tmp, tmp2, addr)); }else{ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, op1, op0); x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, tmp2); } // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } inline void x86_xor_d(CPUMode mode, cs_insn* instr, addr_t addr, IRBlock* block, IRBasicBlockId& bblkid , int& tmp_var_count){ IROperand op0, op1, dest, res, of, cf, pc; of = (mode == CPUMode::X86)? ir_var(X86_OF, 31, 0) : ir_var(X64_OF, 63, 0); cf = (mode == CPUMode::X86)? ir_var(X86_CF, 31, 0) : ir_var(X64_CF, 63, 0); /* Get operands */ dest = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count); if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ op0 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[0]), block, bblkid, tmp_var_count, true); }else{ op0 = dest; } op1 = x86_arg_translate(mode, addr, &(instr->detail->x86.operands[1]), block, bblkid, tmp_var_count, true); /* Do the xor */ res = ir_tmp(tmp_var_count++, (instr->detail->x86.operands[0].size*8)-1, 0); block->add_instr(bblkid, ir_xor(res, op0, op1, addr)); /* Update flags: SF, ZF, PF */ x86_set_zf(mode, res, addr, block, bblkid); x86_set_sf(mode, res, addr, block, bblkid); x86_set_pf(mode, res, addr, block, bblkid, tmp_var_count); /* OF and CF cleared */ block->add_instr(bblkid, ir_mov(of, ir_cst(0, of.high, of.low), addr)); block->add_instr(bblkid, ir_mov(cf, ir_cst(0, cf.high, cf.low), addr)); /* Finally assign the result to the destination */ /* If the add is written in memory */ if( instr->detail->x86.operands[0].type == X86_OP_MEM ){ block->add_instr(bblkid, ir_stm(dest, res, addr)); /* Else direct register assign */ }else{ x86_adjust_reg_assign(mode, addr, block, bblkid, tmp_var_count, dest, res); } // Update PC pc = x86_get_pc(mode); block->add_instr(bblkid, ir_add(pc, pc, ir_cst(instr->size, pc.size-1, 0), addr)); return; } /* ==================================== */ /* Disassembly wapper * * If sym is not null, then is_symbolic and is_tainted should not be null. * If they are not null, then the disassembler should check for symbolic/tainted * code and update the booleans accordingly. Disassembly ends immediately if * symbolic code is detected. * */ IRBlock* DisassemblerX86::disasm_block(addr_t addr, code_t code, size_t code_size){ // Create new ir block IRBlock * block = new IRBlock("", addr); IRBasicBlockId bblkid = block->new_bblock(); int tmp_var_count = 0; addr_t curr_addr = addr; bool stop = false; stringstream asm_str; while( (!stop) && cs_disasm_iter(_handle, (const uint8_t**)&code, &code_size, &addr, _insn) ){ // DEBUG // std::cout << "DEBUG, dissassembled " << _insn->mnemonic << " " << _insn->op_str << std::endl; // Add instruction to IRBlock switch(_insn->id){ case X86_INS_AAA: x86_aaa_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_AAD: x86_aad_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_AAM: x86_aam_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_AAS: x86_aas_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_ADC: x86_adc_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_ADCX: x86_adcx_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_ADD: x86_add_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_AND: x86_and_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_ANDN: x86_andn_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_BLSI: x86_blsi_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_BLSMSK: x86_blsmsk_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_BLSR: x86_blsr_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_BSF: x86_bsf_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_BSR: x86_bsr_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_BSWAP: x86_bswap_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_BT: x86_bt_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_BTC: x86_btc_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_BTR: x86_btr_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_BTS: x86_bts_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_BZHI: x86_bzhi_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CALL: x86_call_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CBW: x86_cbw_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CDQ: x86_cdq_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CDQE: x86_cdqe_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CLC: x86_clc_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CLD: x86_cld_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CLI: x86_cli_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMC: x86_cmc_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMOVA: x86_cmova_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMOVAE: x86_cmovae_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMOVB: x86_cmovb_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMOVBE: x86_cmovbe_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMOVE: x86_cmove_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMOVG: x86_cmovg_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMOVGE: x86_cmovge_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMOVL: x86_cmovl_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMOVLE: x86_cmovle_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMOVNE: x86_cmovne_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMOVNO: x86_cmovno_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMOVNP: x86_cmovnp_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMOVNS: x86_cmovns_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMOVO: x86_cmovo_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMOVP: x86_cmovp_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMOVS: x86_cmovs_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMP: x86_cmp_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMPSB: x86_cmpsb_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMPSD: x86_cmpsd_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMPSQ: x86_cmpsq_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMPSW: x86_cmpsw_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CMPXCHG: x86_cmpxchg_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CPUID: x86_cpuid_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CQO: x86_cqo_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CWD: x86_cwd_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_CWDE: x86_cwde_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_DEC: x86_dec_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_DIV: x86_div_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_IDIV: x86_idiv_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_IMUL: x86_imul_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_INC: x86_inc_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_INT: x86_int_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_INT3: x86_int3_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JA: x86_ja_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JAE: x86_jae_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JB: x86_jb_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JBE: x86_jbe_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JCXZ: x86_jcxz_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JE: x86_je_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JECXZ: x86_jecxz_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JG: x86_jg_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JGE: x86_jge_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JL: x86_jl_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JLE: x86_jle_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JMP: x86_jmp_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JNE: x86_jne_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JNO: x86_jno_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JNP: x86_jnp_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JNS: x86_jns_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JO: x86_jo_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JP: x86_jp_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JRCXZ: x86_jrcxz_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_JS: x86_js_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_LAHF: x86_lahf_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_LEA: x86_lea_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_LEAVE: x86_leave_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_LODSB: x86_lodsb_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_LODSD: x86_lodsd_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_LODSQ: x86_lodsq_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_LODSW: x86_lodsw_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_MOV: x86_mov_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_MOVABS: x86_mov_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; // Just mov with 64b imm/mem case X86_INS_MOVSB: x86_movsb_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_MOVSD: x86_movsd_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_MOVSQ: x86_movsq_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_MOVSW: x86_movsw_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_MOVSX: x86_movsx_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_MOVSXD: x86_movsxd_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_MOVZX: x86_movzx_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_MUL: x86_mul_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_NEG: x86_neg_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_NOP: x86_nop_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_NOT: x86_not_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_OR: x86_or_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_POP: x86_pop_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_POPAL: x86_popad_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_PUSH: x86_push_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_PUSHAL: x86_pushad_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_RCL: x86_rcl_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_RCR: x86_rcr_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_RDTSC: x86_rdtsc_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_RET: x86_ret_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_ROL: x86_rol_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_ROR: x86_ror_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SAL: x86_sal_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SAR: x86_sar_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SCASB: x86_scasb_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SCASD: x86_scasd_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SCASQ: x86_scasq_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SCASW: x86_scasw_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SETA: x86_seta_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SETAE: x86_setae_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SETB: x86_setb_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SETBE: x86_setbe_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SETE: x86_sete_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SETG: x86_setg_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SETGE: x86_setge_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SETL: x86_setl_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SETLE: x86_setle_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SETNE: x86_setne_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SETNO: x86_setno_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SETNP: x86_setnp_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SETNS: x86_setns_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SETO: x86_seto_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SETP: x86_setp_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SETS: x86_sets_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SHL: x86_sal_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; // Same as SAL case X86_INS_SHR: x86_shr_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_STC: x86_stc_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_STD: x86_std_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_STI: x86_sti_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_STOSB: x86_stosb_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_STOSD: x86_stosd_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_STOSQ: x86_stosq_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_STOSW: x86_stosw_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SUB: x86_sub_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SYSENTER: x86_sysenter_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_SYSCALL: x86_syscall_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_TEST: x86_test_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_XADD: x86_xadd_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_XCHG: x86_xchg_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; case X86_INS_XOR: x86_xor_d(_mode, _insn, curr_addr, block, bblkid, tmp_var_count); break; default: string error_str = QuickFmt() << "unsupported instruction " << _insn->mnemonic >> QuickFmt::to_str; throw unsupported_instruction_exception(error_str); } // Update asm_str asm_str << " " << _insn->mnemonic << " " << _insn->op_str << ";"; // Increment instruction count block->_nb_instr++; // Stop if last operation is a branch operation for( int i = 0; i < _insn->detail->groups_count; i++){ if( _insn->detail->groups[i] == X86_GRP_JUMP || _insn->detail->groups[i] == X86_GRP_CALL || _insn->detail->groups[i] == X86_GRP_RET || _insn->detail->groups[i] == X86_GRP_INT || _insn->detail->groups[i] == X86_GRP_IRET /*|| _insn->detail->groups[i] == X86_GRP_PRIVILEGE || _insn->detail->groups[i] == X86_GRP_BRANCH_RELATIVE */ ){ stop = true; } } curr_addr = addr; } // Check if we stopped for a legit reason or because capstone failed if( !stop ){ throw runtime_exception(QuickFmt() << "DisassemblerX86:disasm_block(): capstone error at addr 0x" << std::hex << curr_addr >> QuickFmt::to_str ); } /* Set some infos about the block */ block->end_addr = addr; block->raw_size = block->start_addr - addr; /* Save number of tmp variables */ block->_nb_tmp_vars = tmp_var_count; // Get number of IR instructions block->_nb_instr_ir = 0; for( auto bblk : block->bblocks()){ block->_nb_instr_ir += bblk.size(); } // Set asm_str as name string s = asm_str.str(); s = s.substr(1, s.size()-1-1); // Remove last ';' if( s.back() == ' ' ) s.pop_back(); block->name = s; return block; } ================================================ FILE: libropium/arch/disassembler.cpp ================================================ #include "disassembler.hpp" #include Disassembler::~Disassembler(){ cs_free(_insn, 1); _insn = nullptr; cs_close(&_handle); } ================================================ FILE: libropium/compiler/compiler.cpp ================================================ #include "compiler.hpp" #include "exception.hpp" #include "il.hpp" #include /* ============= Compiler Task ============ */ CompilerTask::CompilerTask(Arch* a):arch(a){} void CompilerTask::add_strategy(StrategyGraph* graph, int max_tries){ vector ::iterator g; if( pending_strategies.size() >= max_tries && pending_strategies.front()->size <= graph->size ){ // If strategy list is already full with smaller strategies, ignore this one delete graph; return; } for( g = pending_strategies.begin(); g != pending_strategies.end() && (*g)->size >= graph->size; g++ ){} pending_strategies.insert(g, graph); } ROPChain* CompilerTask::compile(Arch* arch, GadgetDB* db, Constraint* constraint, int nb_tries){ StrategyGraph* graph; ROPChain* res = nullptr; nb_tries = 3000; // Set sigint handler to catch Ctrl+C set_sigint_handler(); while( nb_tries-- > 0 && !pending_strategies.empty() && !res){ // Check if user entered Ctrl+C if( is_pending_sigint() ){ notify_sigint_handled(); unset_signint_handler(); return nullptr; } graph = pending_strategies.back(); pending_strategies.pop_back(); if( graph->select_gadgets(*db, constraint, arch) ){ res = graph->get_ropchain(arch, constraint); }else{ // Apply strategy rules to the graph to get new candidate strategies apply_rules_to_graph(graph, nb_tries); } delete graph; graph = nullptr; } // Restore original sigint handler unset_signint_handler(); return res; } void CompilerTask::apply_rules_to_graph(StrategyGraph* graph, int max_tries){ StrategyGraph* new_graph; vector new_list; // Iterate through all nodes of the graph for( Node& node : graph->nodes ){ if( node.is_disabled || node.is_indirect ) continue; // Skip invalid/removed nodes // Apply strategy rules // Generic transitivity new_graph = graph->copy(); if( new_graph->rule_generic_transitivity(node.id)){ add_strategy(new_graph, max_tries); new_graph = graph->copy(); } // MovCst pop if( new_graph->rule_mov_cst_pop(node.id, arch)){ add_strategy(new_graph, max_tries); new_graph = graph->copy(); } // Generic adjust_jmp if( new_graph->rule_generic_adjust_jmp(node.id, arch)){ add_strategy(new_graph, max_tries); new_graph = graph->copy(); } // Adjust load if( new_graph->rule_adjust_load(node.id, arch)){ add_strategy(new_graph, max_tries); new_graph = graph->copy(); } // Generic src reg transitivity if( new_graph->rule_generic_src_transitivity(node.id)){ add_strategy(new_graph, max_tries); new_graph = graph->copy(); } // Adjust store if( new_graph->rule_adjust_store(node.id, arch)){ add_strategy(new_graph, max_tries); // Put new_graph = graph->copy() when adding more strategies }else{ delete new_graph; new_graph = nullptr; } } } void CompilerTask::clear(){ for( StrategyGraph* g : pending_strategies ){ delete g; g = nullptr; } pending_strategies.clear(); } CompilerTask::~CompilerTask(){ clear(); } /* ============= ROPCompiler ============= */ ROPCompiler::ROPCompiler(Arch* a, GadgetDB *d):arch(a), db(d){} bool ROPCompiler::is_complex_instr(ILInstruction& instr, ABI abi){ if( instr.type == ILInstructionType::SYSCALL ) return true; if( instr.type == ILInstructionType::FUNCTION ){ if( abi == ABI::X64_MS || abi == ABI::X64_SYSTEM_V ){ return true; } } return false; } ROPChain* ROPCompiler::compile(string program, Constraint* constraint, ABI abi, System system){ Constraint* tmp_constraint; ROPChain* res; vector final_instr; vector instr = parse(program); // This raises il_exception if malformed program // Add some general assertions if( !constraint ){ tmp_constraint = new Constraint(); tmp_constraint->mem_safety.enable_unsafe(); }else{ tmp_constraint = constraint; } // Add generic constraints tmp_constraint->mem_safety.add_safe_reg(arch->sp()); // Stack pointer is always safe for RW if( is_complex_instr(instr[0], abi)){ res = process_complex(instr, tmp_constraint, abi, system); }else{ res = process_simple(instr, tmp_constraint, abi, system); } if( !constraint ){ delete tmp_constraint; } return res; } ROPChain* ROPCompiler::process_simple(vector & ins, Constraint* constraint, ABI abi, System system){ CompilerTask task = CompilerTask(arch); ROPChain * res = nullptr, *tmp = nullptr; vector instructions; // Preprocess instructions preprocess(instructions, ins, constraint); // Compile for( ILInstruction& instr : instructions ){ task.clear(); il_to_strategy(task.pending_strategies, instr, constraint, abi, system); if( (tmp = task.compile(arch, db, constraint)) != nullptr){ if( !res ) res = tmp; else{ res->add_chain(*tmp); delete tmp; tmp = nullptr; } }else{ delete res; return nullptr; } } return res; } ROPChain* ROPCompiler::process_complex(vector & ins, Constraint* constraint, ABI abi, System system){ CompilerTask task = CompilerTask(arch); ILInstruction instr = ins[0]; if( instr.type == ILInstructionType::SYSCALL ){ // Syscalls if( system == System::NONE ){ throw compiler_exception("Target OS must be specified to compile syscalls"); } if( arch->type == ArchType::X86 ){ switch( system ){ case System::LINUX: return _compile_x86_linux_syscall(instr, constraint); default: throw compiler_exception("Syscalls are not supported for this system on X86"); } }else if( arch->type == ArchType::X64 ){ switch( system ){ case System::LINUX: return _compile_x64_linux_syscall(instr, constraint); default: throw compiler_exception("Syscalls are not supported for this system on X64"); } }else{ throw runtime_exception("Syscalls are not supported for this architecture"); } }else if( instr.type == ILInstructionType::FUNCTION ){ // Functions if( abi == ABI::NONE ){ throw compiler_exception("ABI must be specified to call functions"); } if( arch->type == ArchType::X86 ){ switch( abi ){ default: throw compiler_exception("This ABI is not supported for X86"); } }else if( arch->type == ArchType::X64 ){ switch( abi ){ case ABI::X64_SYSTEM_V: return _compile_x64_system_v_call(instr, constraint); case ABI::X64_MS: return _compile_x64_ms_call(instr, constraint); default: throw compiler_exception("This ABI is not supported for X64"); } }else{ throw runtime_exception("Function calls are not supported for this architecture"); } } return nullptr; } bool _is_empty_line(string& s){ for( char& c : s ){ if( !isspace(c)) return false; } return true; } vector ROPCompiler::parse(string& program){ size_t pos; string instr; vector res; pos = 0; while( !program.empty() && pos != string::npos){ pos = program.find('\n'); instr = program.substr(0, pos); if( !_is_empty_line(instr)){ try{ ILInstruction ins = ILInstruction(*arch, instr); res.push_back(ins); }catch(il_exception const& e) { throw il_exception(QuickFmt() << "Invalid query: " << instr >> QuickFmt::to_str); } } program.erase(0, pos + 1); } return res; } bool _permutation_contains(vector & perm1, vector & perm2){ if( perm1.back() != perm2.back() ) return false; // Last = failed one, so if not the same we don't remove the recorded fail // Check perm2 is a prefix of perm1 for( auto i = perm2.begin(); i != perm2.end()-1; i++ ){ if( std::find(perm1.begin(), perm1.end()-1, *i) == (perm1.end()-1)){ return false; } } return true; } void _record_failed_permutation(list >& failed_perms, vector & perm){ failed_perms.remove_if( [&perm](vector & failed_perm){ return _permutation_contains(failed_perm, perm); }); failed_perms.push_back(perm); } bool _is_failed_permutation(list >& failed_perms, vector & perm){ for( auto failed_perm : failed_perms ){ if( _permutation_contains(perm, failed_perm)){ return true; } } return false; } ROPChain* ROPCompiler::_set_registers_permutation( vector & instr, vector & permutation, Constraint* constraint, list >& failed_perms, bool& failed_on_first){ CompilerTask task(arch); Constraint constr, tmp_constr; ROPChain *res = nullptr, *chain=nullptr; vector tmp_perm; vector tmp_keep_regs; constr = *constraint; // Copy base constraint for( int i = 0; i < permutation.size(); i++ ){ int idx = permutation[i]; tmp_perm.push_back(idx); task.clear(); il_to_strategy(task.pending_strategies, instr[idx], &constr); // Add the register args to the constr (don't modify them) tmp_constr = constr; tmp_keep_regs.clear(); for( int j = i+1; j < permutation.size(); j++ ){ if( instr[j].type == ILInstructionType::MOV_REG ){ tmp_constr.keep_regs.add_keep_reg(instr[j].args[PARAM_MOVREG_SRC_REG]); tmp_keep_regs.push_back(instr[j].args[PARAM_MOVREG_SRC_REG]); } } chain = task.compile(arch, db, &tmp_constr); if( chain == nullptr ){ if( i == 0 && tmp_keep_regs.empty()){ // No chain to set he first register failed_on_first = true; return nullptr; } // Add the additional future keep reg to tmp_perm when recording the fail for( int keep : tmp_keep_regs ){ if( std::find(tmp_perm.begin(), tmp_perm.end(), keep) == tmp_perm.end()) tmp_perm.push_back(keep); } // Record fail and return _record_failed_permutation(failed_perms, tmp_perm); delete res; return nullptr; }else{ // Add it to res if( res == nullptr ) res = chain; else res->add_chain(*chain); // Do not modify this dest reg later on constr.keep_regs.add_keep_reg(instr[idx].args[0]); // Add the dst reg to keepregs } } return res; } ROPChain* ROPCompiler::_set_multiple_registers(vector & instr, Constraint* constraint){ ROPChain* res = nullptr; vector order; list > failed_permutations; bool failed_on_first = false; for( int i = 0; i < instr.size(); i++) order.push_back(i); do{ if( !_is_failed_permutation(failed_permutations, order)){ res = _set_registers_permutation(instr, order, constraint, failed_permutations, failed_on_first); if( res ){ break; // Found chain, stop searching }else if( failed_on_first ){ return nullptr; } } }while( std::next_permutation(order.begin(), order.end())); return res; } bool ROPCompiler::_x86_cdecl_to_strategy(StrategyGraph& graph, ILInstruction& instr){ // Arguments pushed on the stack right to left // Caller-cleanup = we have to set a proper gadget as return address to go to // the next gadget in the ropchain node_t n_ret = graph.new_node(GadgetType::LOAD); node_t n = graph.new_node(GadgetType::MOV_CST); Node& node_ret = graph.nodes[n_ret]; Node& node = graph.nodes[n]; // Add the 'ret' gadget that will node_ret.is_indirect = true; // Indirect node_ret.params[PARAM_LOAD_DST_REG].make_reg(X86_EIP); node_ret.params[PARAM_LOAD_SRC_ADDR_REG].make_reg(X86_ESP); // For return gadget, skip all the arguments and return // (nb args is args.size() -1 because 1rst arg is the function address) node_ret.params[PARAM_LOAD_SRC_ADDR_OFFSET].make_cst(arch->octets*(instr.args.size()-1), graph.new_name("stack_offset")); // Main node /* Arguments are on the stack, pushed right to left */ node.params[PARAM_MOVCST_DST_REG].make_reg(X86_EIP); node.params[PARAM_MOVCST_SRC_CST].make_cst(instr.args[PARAM_FUNCTION_ADDR], graph.new_name("function_address")); // Add parameters at the final sp_inc of the gadget for( int i = 1; i < instr.args.size(); i++){ node.special_paddings.push_back(ROPPadding()); // The offset is sp_inc + arch_size_bytes*(param_num+1) (+1 because return address comes before args) node.special_paddings.back().offset.make_cst( node.id, PARAM_MOVCST_GADGET_SP_INC, exprvar(arch->bits, node.params[PARAM_MOVCST_GADGET_SP_INC].name) + (arch->octets * ((i-1)+1)), graph.new_name("func_arg_offset") ); if( instr.args_type[i] == IL_FUNC_ARG_CST ){ node.special_paddings.back().value.make_cst(instr.args[i], graph.new_name("func_arg")); }else{ // Putting the registers on the stack then call a function isn't supported return false; } } // Add constraint to check that the sp-delta of the gadget is 0 node.assigned_gadget_constraints.push_back( // The gadget should have a sp_delta == 0 (otherwise the arguments won't be in the right place when // jumping to the function [](Node* n, StrategyGraph* g, Arch* arch)->bool{ return n->affected_gadget->max_sp_inc == n->affected_gadget->sp_inc; } ); // Add the 'ret' gadget address as first padding of the first gadget :) node.special_paddings.push_back(ROPPadding()); node.special_paddings.back().offset.make_cst( node.id, PARAM_MOVCST_GADGET_SP_INC, exprvar(arch->bits, node.params[PARAM_MOVCST_GADGET_SP_INC].name), graph.new_name("func_ret_addr_offset") ); node.special_paddings.back().value.make_cst(node_ret.id, PARAM_LOAD_GADGET_ADDR, exprvar(arch->bits, node_ret.params[PARAM_LOAD_GADGET_ADDR].name), graph.new_name("func_ret_addr")); // Add mandatory following node node.mandatory_following_node = node_ret.id; return true; } bool ROPCompiler::_x86_stdcall_to_strategy(StrategyGraph& graph, ILInstruction& instr){ // Similar to cdecl but easier since it's a callee-cleaup convention so we just need // a 'ret' as return gadget and don't need to adapt it to the number of arguments node_t n_ret = graph.new_node(GadgetType::LOAD); node_t n = graph.new_node(GadgetType::MOV_CST); Node& node_ret = graph.nodes[n_ret]; Node& node = graph.nodes[n]; // Add the 'ret' gadget node_ret.is_indirect = true; // Indirect node_ret.params[PARAM_LOAD_DST_REG].make_reg(X86_EIP); node_ret.params[PARAM_LOAD_SRC_ADDR_REG].make_reg(X86_ESP); node_ret.params[PARAM_LOAD_SRC_ADDR_OFFSET].make_cst(0, graph.new_name("stack_offset")); // Main node /* Arguments are on the stack, pushed right to left */ node.params[PARAM_MOVCST_DST_REG].make_reg(X86_EIP); node.params[PARAM_MOVCST_SRC_CST].make_cst(instr.args[PARAM_FUNCTION_ADDR], graph.new_name("func_address")); // Add parameters at the final sp_inc of the gadget for( int i = 1; i < instr.args.size(); i++){ node.special_paddings.push_back(ROPPadding()); // The offset is sp_inc + arch_size_bytes*(param_num+1) (+1 because return address comes before args) node.special_paddings.back().offset.make_cst( node.id, PARAM_MOVCST_GADGET_SP_INC, exprvar(arch->bits, node.params[PARAM_MOVCST_GADGET_SP_INC].name) + (arch->octets * ((i-1)+1)), graph.new_name("func_arg_offset") ); if( instr.args_type[i] == IL_FUNC_ARG_CST ){ node.special_paddings.back().value.make_cst(instr.args[i], graph.new_name("func_arg")); }else{ // Putting the registers on the stack then call a function isn't supported return false; } } // Add constraint to check that the sp-delta of the gadget is 0 node.assigned_gadget_constraints.push_back( // The gadget should have a sp_delta == 0 (otherwise the arguments won't be in the right place when // jumping to the function [](Node* n, StrategyGraph* g, Arch* arch)->bool{ return n->affected_gadget->max_sp_inc == n->affected_gadget->sp_inc; } ); // Add the 'ret' gadget address as first padding of the first gadget :) node.special_paddings.push_back(ROPPadding()); node.special_paddings.back().offset.make_cst( node.id, PARAM_MOVCST_GADGET_SP_INC, exprvar(arch->bits, node.params[PARAM_MOVCST_GADGET_SP_INC].name), graph.new_name("func_ret_addr_offset") ); node.special_paddings.back().value.make_cst(node_ret.id, PARAM_LOAD_GADGET_ADDR, exprvar(arch->bits, node_ret.params[PARAM_LOAD_GADGET_ADDR].name), graph.new_name("func_ret_addr")); // Add mandatory following node node.mandatory_following_node = node_ret.id; return true; } ROPChain* ROPCompiler::_compile_x64_system_v_call(ILInstruction& instr, Constraint* constraint){ // First 6 args in RDI,RSI,RDX,RCX,R8,R9 then on the stack pushed right to left node_t call_node, ret_node; int arg_regs[6] = {X64_RDI, X64_RSI, X64_RDX, X64_RCX, X64_R8, X64_R9}; int nb_args_on_stack; CompilerTask task(arch); ROPChain *res=nullptr, *tmp=nullptr; StrategyGraph *graph = new StrategyGraph(); vector set_regs_instr; Constraint call_constr; // Get base constraint call_constr = *constraint; // Constraint for the call node with the keepregs set // Create node for strategy graph to call the function (with padding etc) if( instr.args.size()-1 > 6 ) nb_args_on_stack = instr.args.size()-1 - 6; else nb_args_on_stack = 0; // Add the 'ret' gadget ret_node = graph->new_node(GadgetType::LOAD); graph->nodes[ret_node].is_indirect = true; // Indirect graph->nodes[ret_node].params[PARAM_LOAD_DST_REG].make_reg(X64_RIP); graph->nodes[ret_node].params[PARAM_LOAD_SRC_ADDR_REG].make_reg(X64_RSP); graph->nodes[ret_node].params[PARAM_LOAD_SRC_ADDR_OFFSET].make_cst(nb_args_on_stack*arch->octets, graph->new_name("stack_offset")); // Add the call node call_node = graph->new_node(GadgetType::MOV_CST); graph->nodes[call_node].params[PARAM_MOVCST_DST_REG].make_reg(X64_RIP); graph->nodes[call_node].params[PARAM_MOVCST_SRC_CST].make_cst(instr.args[PARAM_FUNCTION_ADDR], graph->new_name("func_address")); // Add constraint to check that the sp-delta of the gadget is 0 graph->nodes[call_node].assigned_gadget_constraints.push_back( // The gadget should have a sp_delta == 0 (otherwise the arguments won't be in the right place when // jumping to the function [](Node* n, StrategyGraph* g, Arch* arch)->bool{ return n->affected_gadget->max_sp_inc == n->affected_gadget->sp_inc; } ); // If needed add special paddings for extra args (after the 6th argument) for( int i = 0; i < nb_args_on_stack; i++){ graph->nodes[call_node].special_paddings.push_back(ROPPadding()); // The offset is sp_inc + arch_size_bytes*(param_num+1) (+1 because return address comes before args) graph->nodes[call_node].special_paddings.back().offset.make_cst( call_node, PARAM_MOVCST_GADGET_SP_INC, exprvar(arch->bits, graph->nodes[call_node].params[PARAM_MOVCST_GADGET_SP_INC].name) + (arch->octets * (i+1)), graph->new_name("func_arg_offset") ); if( instr.args_type[PARAM_FUNCTION_ARGS+6+i] == IL_FUNC_ARG_CST ){ graph->nodes[call_node].special_paddings.back().value.make_cst(instr.args[PARAM_FUNCTION_ARGS+6+i], graph->new_name("func_arg")); }else{ // Putting the registers on the stack then call a function isn't supported delete graph; return nullptr; } } // Add the 'ret' gadget address as first padding of the first gadget :) graph->nodes[call_node].special_paddings.push_back(ROPPadding()); graph->nodes[call_node].special_paddings.back().offset.make_cst( call_node, PARAM_MOVCST_GADGET_SP_INC, exprvar(arch->bits, graph->nodes[call_node].params[PARAM_MOVCST_GADGET_SP_INC].name), graph->new_name("func_ret_addr_offset") ); graph->nodes[call_node].special_paddings.back().value.make_cst(ret_node, PARAM_LOAD_GADGET_ADDR, exprvar(arch->bits, graph->nodes[ret_node].params[PARAM_LOAD_GADGET_ADDR].name), graph->new_name("func_ret_addr")); // Add 'ret' node as mandatory following node graph->nodes[call_node].mandatory_following_node = ret_node; // Create a vector of instructions to set the register arguments for( int i = 0; i < 6 && (i < instr.args.size()-PARAM_FUNCTION_ARGS); i++){ // Set register that must hold the argument if( instr.args_type[PARAM_FUNCTION_ARGS+i] == IL_FUNC_ARG_CST ){ set_regs_instr.push_back(ILInstruction(ILInstructionType::MOV_CST)); set_regs_instr.back().args = {arg_regs[i], instr.args[PARAM_FUNCTION_ARGS+i]}; }else{ set_regs_instr.push_back(ILInstruction(ILInstructionType::MOV_REG)); set_regs_instr.back().args = {arg_regs[i], instr.args[PARAM_FUNCTION_ARGS+i]}; } call_constr.keep_regs.add_keep_reg(arg_regs[i]); } // Get the chain that sets the registers res = _set_multiple_registers(set_regs_instr, constraint); if( !res ){ delete graph; return nullptr; } // Get the chain that calls the function task.clear(); task.pending_strategies.push_back(graph); tmp = task.compile(arch, db, &call_constr); if( !tmp ){ delete res; res = nullptr; return nullptr; } // Sucess, return chain! res->add_chain(*tmp); return res; } ROPChain* ROPCompiler::_compile_x64_ms_call(ILInstruction& instr, Constraint* constraint){ // Similar to system_v but only 4 args passed in RDX,RCX,R8,R9 then on the stack pushed right to left // (Code is almost identical to _x64_system_v_to_strategy, only number of stack regs changes, // it could be factorized in the future if needed) node_t call_node, ret_node; int arg_regs[4] = {X64_RDX, X64_RCX, X64_R8, X64_R9}; int nb_args_on_stack; CompilerTask task(arch); ROPChain *res=nullptr, *tmp=nullptr; StrategyGraph *graph = new StrategyGraph(); vector set_regs_instr; Constraint call_constr; // Get base constraint call_constr = *constraint; // Constraint for the call node with the keepregs set if( instr.args.size()-1 > 4 ) nb_args_on_stack = instr.args.size()-1 - 4; else nb_args_on_stack = 0; // Add the 'ret' gadget ret_node = graph->new_node(GadgetType::LOAD); graph->nodes[ret_node].is_indirect = true; // Indirect graph->nodes[ret_node].params[PARAM_LOAD_DST_REG].make_reg(X64_RIP); graph->nodes[ret_node].params[PARAM_LOAD_SRC_ADDR_REG].make_reg(X64_RSP); graph->nodes[ret_node].params[PARAM_LOAD_SRC_ADDR_OFFSET].make_cst(nb_args_on_stack*arch->octets, graph->new_name("stack_offset")); // Add the call node call_node = graph->new_node(GadgetType::MOV_CST); graph->nodes[call_node].params[PARAM_MOVCST_DST_REG].make_reg(X64_RIP); graph->nodes[call_node].params[PARAM_MOVCST_SRC_CST].make_cst(instr.args[PARAM_FUNCTION_ADDR], graph->new_name("func_address")); // Add constraint to check that the sp-delta of the gadget is 0 graph->nodes[call_node].assigned_gadget_constraints.push_back( // The gadget should have a sp_delta == 0 (otherwise the arguments won't be in the right place when // jumping to the function [](Node* n, StrategyGraph* g, Arch* arch)->bool{ return n->affected_gadget->max_sp_inc == n->affected_gadget->sp_inc; } ); // If needed add special paddings for extra args (after the 4th argument) for( int i = 0; i < nb_args_on_stack; i++){ graph->nodes[call_node].special_paddings.push_back(ROPPadding()); // The offset is sp_inc + arch_size_bytes*(param_num+1) (+1 because return address comes before args) graph->nodes[call_node].special_paddings.back().offset.make_cst( call_node, PARAM_MOVCST_GADGET_SP_INC, exprvar(arch->bits, graph->nodes[call_node].params[PARAM_MOVCST_GADGET_SP_INC].name) + (arch->octets * (i+1)), graph->new_name("func_arg_offset") ); if( instr.args_type[PARAM_FUNCTION_ARGS+4+i] == IL_FUNC_ARG_CST ){ graph->nodes[call_node].special_paddings.back().value.make_cst(instr.args[PARAM_FUNCTION_ARGS+4+i], graph->new_name("func_arg")); }else{ // Putting the registers on the stack then call a function isn't supported delete graph; return nullptr; } } // Add the 'ret' gadget address as first padding of the first gadget :) graph->nodes[call_node].special_paddings.push_back(ROPPadding()); graph->nodes[call_node].special_paddings.back().offset.make_cst( call_node, PARAM_MOVCST_GADGET_SP_INC, exprvar(arch->bits, graph->nodes[call_node].params[PARAM_MOVCST_GADGET_SP_INC].name), graph->new_name("func_ret_addr_offset") ); graph->nodes[call_node].special_paddings.back().value.make_cst(ret_node, PARAM_LOAD_GADGET_ADDR, exprvar(arch->bits, graph->nodes[ret_node].params[PARAM_LOAD_GADGET_ADDR].name), graph->new_name("func_ret_addr")); // Add 'ret' node as mandatory following node graph->nodes[call_node].mandatory_following_node = ret_node; // Create a vector of instructions to set the register arguments for( int i = 0; i < 4 && (i < instr.args.size()-PARAM_FUNCTION_ARGS); i++){ // Set register that must hold the argument if( instr.args_type[PARAM_FUNCTION_ARGS+i] == IL_FUNC_ARG_CST ){ set_regs_instr.push_back(ILInstruction(ILInstructionType::MOV_CST)); set_regs_instr.back().args = {arg_regs[i], instr.args[PARAM_FUNCTION_ARGS+i]}; }else{ set_regs_instr.push_back(ILInstruction(ILInstructionType::MOV_REG)); set_regs_instr.back().args = {arg_regs[i], instr.args[PARAM_FUNCTION_ARGS+i]}; } call_constr.keep_regs.add_keep_reg(arg_regs[i]); } // Get the chain that sets the registers res = _set_multiple_registers(set_regs_instr, constraint); if( !res ){ delete graph; return nullptr; } // Get the chain that calls the function task.clear(); task.pending_strategies.push_back(graph); tmp = task.compile(arch, db, &call_constr); if( !tmp ){ delete res; res = nullptr; return nullptr; } // Sucess, return chain! res->add_chain(*tmp); return res; } ROPChain* ROPCompiler::_compile_x86_linux_syscall(ILInstruction& instr, Constraint* constraint){ // Get syscall def for this syscall SyscallDef* def; bool def_by_name; int syscall_num; CompilerTask task(arch); vector set_regs_instr; ROPChain *res, *syscall_chain; Constraint syscall_constraint = *constraint; def_by_name = !instr.syscall_name.empty(); if( def_by_name ){ def = get_syscall_def(ArchType::X64, System::LINUX, instr.syscall_name); if( def == nullptr ){ throw compiler_exception(QuickFmt() << "Syscall '" << instr.syscall_name << "' is not supported"); } syscall_num = def->num; }else{ syscall_num = instr.syscall_num; } int arg_regs[6] = {X86_EBX, X86_ECX, X86_EDX, X86_ESI, X86_EDI, X86_EBP}; if( instr.args.size() > 6 ) throw compiler_exception("X86 syscalls can not take more than 6 arguments"); else if( def_by_name && instr.args.size() != def->nb_args ){ throw compiler_exception(QuickFmt() << "Syscall " << def->name << "() expects " << std::dec << def->nb_args << " arguments (got " << instr.args.size() << ")" >> QuickFmt::to_str ); } // Create vector of instructions to put the first 6 args in registers for( int i = 0; i < 6 && (i < instr.args.size()-PARAM_SYSCALL_ARGS); i++){ // Set register that must hold the argument if( instr.args_type[PARAM_SYSCALL_ARGS+i] == IL_FUNC_ARG_CST ){ set_regs_instr.push_back(ILInstruction(ILInstructionType::MOV_CST)); set_regs_instr.back().args = {arg_regs[i], instr.args[PARAM_SYSCALL_ARGS+i]}; // MOV_CST args }else{ set_regs_instr.push_back(ILInstruction(ILInstructionType::MOV_REG)); set_regs_instr.back().args = {arg_regs[i], instr.args[PARAM_SYSCALL_ARGS+i]}; // MOV_CST args } syscall_constraint.keep_regs.add_keep_reg(arg_regs[i]); } // Add instruction to put syscall number in eax set_regs_instr.push_back(ILInstruction(ILInstructionType::MOV_CST)); set_regs_instr.back().args = {X86_EAX, syscall_num}; // Syscall num in EAX // Get the chain to set all the registers res = _set_multiple_registers(set_regs_instr, constraint); if( !res ){ return nullptr; } // Get the chain to make the syscall syscall_chain = compile("syscall", &syscall_constraint); if( ! syscall_chain ){ delete res; return nullptr; }else{ res->add_chain(*syscall_chain); } return res; } ROPChain* ROPCompiler::_compile_x64_linux_syscall(ILInstruction& instr, Constraint* constraint){ // Get syscall def for this syscall SyscallDef* def; bool def_by_name; int syscall_num; CompilerTask task(arch); vector set_regs_instr; ROPChain *res, *syscall_chain; Constraint syscall_constraint = *constraint; def_by_name = !instr.syscall_name.empty(); if( def_by_name ){ def = get_syscall_def(ArchType::X64, System::LINUX, instr.syscall_name); if( def == nullptr ){ throw compiler_exception(QuickFmt() << "Syscall '" << instr.syscall_name << "' is not supported"); } syscall_num = def->num; }else{ syscall_num = instr.syscall_num; } int arg_regs[6] = {X64_RDI, X64_RSI, X64_RDX, X64_R10, X64_R8, X64_R9}; if( instr.args.size() > 6 ) throw compiler_exception("X64 syscalls can not take more than 6 arguments"); else if( def_by_name && instr.args.size() != def->nb_args ){ throw compiler_exception(QuickFmt() << "Syscall " << def->name << "() expects " << std::dec << def->nb_args << " arguments (got " << instr.args.size() << ")" >> QuickFmt::to_str ); } // Create vector of instructions to put the first 6 args in registers for( int i = 0; i < 6 && (i < instr.args.size()-PARAM_SYSCALL_ARGS); i++){ // Set register that must hold the argument if( instr.args_type[PARAM_SYSCALL_ARGS+i] == IL_FUNC_ARG_CST ){ set_regs_instr.push_back(ILInstruction(ILInstructionType::MOV_CST)); set_regs_instr.back().args = {arg_regs[i], instr.args[PARAM_SYSCALL_ARGS+i]}; // MOV_CST args }else{ set_regs_instr.push_back(ILInstruction(ILInstructionType::MOV_REG)); set_regs_instr.back().args = {arg_regs[i], instr.args[PARAM_SYSCALL_ARGS+i]}; // MOV_CST args } syscall_constraint.keep_regs.add_keep_reg(arg_regs[i]); } // Add instruction to put syscall number in eax set_regs_instr.push_back(ILInstruction(ILInstructionType::MOV_CST)); set_regs_instr.back().args = {X64_RAX, syscall_num}; // Syscall num in RAX // Get the chain to set all the registers res = _set_multiple_registers(set_regs_instr, constraint); if( !res ){ return nullptr; } // Get the chain to make the syscall syscall_chain = compile("syscall", &syscall_constraint); if( ! syscall_chain ){ delete res; return nullptr; }else{ res->add_chain(*syscall_chain); } return res; } bool _string_to_integers(vector & integers, string& str, int arch_octets, Constraint* constraint){ // Assuming little endian int i = 0, j; cst_t val; unsigned char padding_byte = 0xff; // Default if( constraint != nullptr ){ try{ padding_byte = constraint->bad_bytes.get_valid_byte(); }catch(runtime_exception& e){ return false; } } while( i < str.size() ){ val = 0; for( j = 0; j < arch_octets && i < str.size(); j++){ val += ((cst_t)str[i++]) << (j*8); } // Check if full value if( j != arch_octets ){ // Adjust value for( ; j < arch_octets; j++){ val += ((cst_t)padding_byte) << (j*8); } } integers.push_back(val); } return true; } bool _cst_store_cst_to_strategy(StrategyGraph& graph, ILInstruction& instr, Arch* arch){ node_t n1 = graph.new_node(GadgetType::STORE); node_t n2 = graph.new_node(GadgetType::MOV_CST); node_t n3 = graph.new_node(GadgetType::MOV_CST); Node& node1 = graph.nodes[n1]; Node& node2 = graph.nodes[n2]; Node& node3 = graph.nodes[n3]; node1.branch_type = BranchType::RET; node2.branch_type = BranchType::RET; node3.branch_type = BranchType::RET; // First node is mem(X + C) <- reg // Second is X <- src_cst - C node1.params[PARAM_STORE_SRC_REG].make_reg(-1, false); // Free reg node1.params[PARAM_STORE_DST_ADDR_REG].make_reg(-1, false); // Free node1.params[PARAM_STORE_DST_ADDR_OFFSET].make_cst(-1, graph.new_name("offset"), false); node1.strategy_constraints.push_back( // Can not adjust the addr_reg if it is the same as the reg that must be written // (i.e mov [ecx+8], ecx can't become mov [0x12345678], ecx [](Node* n, StrategyGraph* g, Arch* arch)->bool{ return n->params[PARAM_STORE_DST_ADDR_REG].value != n->params[PARAM_STORE_SRC_REG].value; } ); node1.node_assertion.valid_pointers.add_valid_pointer(PARAM_STORE_DST_ADDR_REG); node2.params[PARAM_MOVCST_DST_REG].make_reg(n1, PARAM_STORE_DST_ADDR_REG); // node2 X is same as addr reg in node1 node2.params[PARAM_MOVCST_DST_REG].is_data_link = true; node2.params[PARAM_MOVCST_SRC_CST].make_cst(n1, PARAM_STORE_DST_ADDR_OFFSET, instr.args[PARAM_CSTSTORECST_DST_ADDR_OFFSET] - exprvar(arch->bits, node1.params[PARAM_STORE_DST_ADDR_OFFSET].name) , graph.new_name("cst")); // node2 cst is the target const C minus the offset in the node1 load node3.params[PARAM_MOVCST_DST_REG].make_reg(n1, PARAM_STORE_SRC_REG); node3.params[PARAM_MOVCST_DST_REG].is_data_link = true; node3.params[PARAM_MOVCST_SRC_CST].make_cst(instr.args[PARAM_CSTSTORECST_SRC_CST], graph.new_name("cst")); graph.add_param_edge(n2, n1); graph.add_strategy_edge(n2, n1); graph.add_param_edge(n3, n1); graph.add_strategy_edge(n3, n1); return true; } bool _preprocess_cst_store_string(vector & dst, ILInstruction& instr, Arch* arch, Constraint* constraint){ vector integers; if( !_string_to_integers(integers, instr.str, arch->octets, constraint)){ return false; } // For each integer, add node to store it to the correct address :) for( int i = 0; i < integers.size(); i++ ){ cst_t addr_offset = instr.args[PARAM_CSTSTORE_STRING_ADDR_OFFSET] + (i*arch->octets); cst_t src_cst = integers[i]; vector store_cst_args = {addr_offset, src_cst}; ILInstruction il_instr = ILInstruction(ILInstructionType::CST_STORE_CST, &store_cst_args); dst.push_back(il_instr); } return true; } bool ROPCompiler::preprocess(vector & dst, vector & src, Constraint* constraint){ for( ILInstruction& instr : src ){ if( instr.type == ILInstructionType::CST_STORE_STRING ){ // Splite a store string into several smaller ones if( ! _preprocess_cst_store_string(dst, instr, arch, constraint)) return false; }else{ // Just copy it dst.push_back(instr); } } return true; } void ROPCompiler::il_to_strategy(vector & graphs, ILInstruction& instr, Constraint* constraint, ABI abi, System system){ StrategyGraph* graph; if( instr.type == ILInstructionType::MOV_CST ){ // MOV_CST graph = new StrategyGraph(); node_t n = graph->new_node(GadgetType::MOV_CST); Node& node = graph->nodes[n]; node.branch_type = BranchType::RET; node.params[PARAM_MOVCST_DST_REG].make_reg(instr.args[PARAM_MOVCST_DST_REG]); node.params[PARAM_MOVCST_DST_REG].is_data_link = true; node.params[PARAM_MOVCST_SRC_CST].make_cst(instr.args[PARAM_MOVCST_SRC_CST], graph->new_name("cst")); graph->update_size(); graphs.push_back(graph); }else if( instr.type == ILInstructionType::MOV_REG ){ // MOV_REG graph = new StrategyGraph(); node_t n = graph->new_node(GadgetType::MOV_REG); Node& node = graph->nodes[n]; node.branch_type = BranchType::RET; node.params[PARAM_MOVREG_DST_REG].make_reg(instr.args[PARAM_MOVREG_DST_REG]); node.params[PARAM_MOVREG_DST_REG].is_data_link = true; node.params[PARAM_MOVREG_SRC_REG].make_reg(instr.args[PARAM_MOVREG_SRC_REG]); node.params[PARAM_MOVREG_SRC_REG].is_data_link = true; graph->update_size(); graphs.push_back(graph); }else if( instr.type == ILInstructionType::AMOV_CST){ // AMOV_CST graph = new StrategyGraph(); node_t n = graph->new_node(GadgetType::AMOV_CST); Node& node = graph->nodes[n]; node.branch_type = BranchType::RET; node.params[PARAM_AMOVCST_DST_REG].make_reg(instr.args[PARAM_AMOVCST_DST_REG]); node.params[PARAM_AMOVCST_DST_REG].is_data_link = true; node.params[PARAM_AMOVCST_SRC_REG].make_reg(instr.args[PARAM_AMOVCST_SRC_REG]); node.params[PARAM_AMOVCST_SRC_OP].make_op((Op)instr.args[PARAM_AMOVCST_SRC_OP]); node.params[PARAM_AMOVCST_SRC_CST].make_cst(instr.args[PARAM_AMOVCST_SRC_CST], graph->new_name("cst")); graph->update_size(); graphs.push_back(graph); }else if( instr.type == ILInstructionType::AMOV_REG){ // AMOV_REG graph = new StrategyGraph(); node_t n = graph->new_node(GadgetType::AMOV_REG); Node& node = graph->nodes[n]; node.branch_type = BranchType::RET; node.params[PARAM_AMOVREG_DST_REG].make_reg(instr.args[PARAM_AMOVREG_DST_REG]); node.params[PARAM_AMOVREG_DST_REG].is_data_link = true; node.params[PARAM_AMOVREG_SRC_REG1].make_reg(instr.args[PARAM_AMOVREG_SRC_REG1]); node.params[PARAM_AMOVREG_SRC_REG1].is_data_link = true; node.params[PARAM_AMOVREG_SRC_OP].make_op((Op)instr.args[PARAM_AMOVREG_SRC_OP]); node.params[PARAM_AMOVREG_SRC_REG2].make_reg(instr.args[PARAM_AMOVREG_SRC_REG2]); node.params[PARAM_AMOVREG_SRC_REG2].is_data_link = true; graph->update_size(); graphs.push_back(graph); }else if( instr.type == ILInstructionType::LOAD ){ // LOAD graph = new StrategyGraph(); node_t n = graph->new_node(GadgetType::LOAD); Node& node = graph->nodes[n]; node.branch_type = BranchType::RET; node.params[PARAM_LOAD_DST_REG].make_reg(instr.args[PARAM_LOAD_DST_REG]); node.params[PARAM_LOAD_DST_REG].is_data_link = true; node.params[PARAM_LOAD_SRC_ADDR_REG].make_reg(instr.args[PARAM_LOAD_SRC_ADDR_REG]); node.params[PARAM_LOAD_SRC_ADDR_REG].is_data_link = true; node.params[PARAM_LOAD_SRC_ADDR_OFFSET].make_cst(instr.args[PARAM_LOAD_SRC_ADDR_OFFSET], graph->new_name("offset")); node.node_assertion.valid_pointers.add_valid_pointer(PARAM_LOAD_SRC_ADDR_REG); graph->update_size(); graphs.push_back(graph); }else if( instr.type == ILInstructionType::ALOAD ){ // ALOAD graph = new StrategyGraph(); node_t n = graph->new_node(GadgetType::ALOAD); Node& node = graph->nodes[n]; node.branch_type = BranchType::RET; node.params[PARAM_ALOAD_DST_REG].make_reg(instr.args[PARAM_LOAD_DST_REG]); node.params[PARAM_ALOAD_DST_REG].is_data_link = true; node.params[PARAM_ALOAD_OP].make_op((Op)instr.args[PARAM_ALOAD_OP]); node.params[PARAM_ALOAD_SRC_ADDR_REG].make_reg(instr.args[PARAM_ALOAD_SRC_ADDR_REG]); node.params[PARAM_ALOAD_SRC_ADDR_REG].is_data_link = true; node.params[PARAM_ALOAD_SRC_ADDR_OFFSET].make_cst(instr.args[PARAM_ALOAD_SRC_ADDR_OFFSET], graph->new_name("offset")); node.node_assertion.valid_pointers.add_valid_pointer(PARAM_ALOAD_SRC_ADDR_REG); graph->update_size(); graphs.push_back(graph); }else if( instr.type == ILInstructionType::LOAD_CST ){ // LOAD_CST graph = new StrategyGraph(); node_t n1 = graph->new_node(GadgetType::LOAD); node_t n2 = graph->new_node(GadgetType::MOV_CST); Node& node1 = graph->nodes[n1]; Node& node2 = graph->nodes[n2]; node1.branch_type = BranchType::RET; node2.branch_type = BranchType::RET; // First node is reg <- mem(X + C) // Second is X <- src_cst - C node1.params[PARAM_LOAD_DST_REG].make_reg(instr.args[PARAM_LOADCST_DST_REG]); node1.params[PARAM_LOAD_DST_REG].is_data_link = true; node1.params[PARAM_LOAD_SRC_ADDR_REG].make_reg(-1, false); // Free node1.params[PARAM_LOAD_SRC_ADDR_OFFSET].make_cst(-1, graph->new_name("offset"), false); node1.node_assertion.valid_pointers.add_valid_pointer(PARAM_LOAD_SRC_ADDR_REG); node2.params[PARAM_MOVCST_DST_REG].make_reg(n1, PARAM_LOAD_SRC_ADDR_REG); // node2 X is same as addr reg in node1 node2.params[PARAM_MOVCST_SRC_CST].make_cst(n1, PARAM_LOAD_SRC_ADDR_OFFSET, instr.args[PARAM_LOADCST_SRC_ADDR_OFFSET] - exprvar(arch->bits, node1.params[PARAM_LOAD_SRC_ADDR_OFFSET].name) , graph->new_name("cst")); // node2 cst is the target const C minus the offset in the node1 load graph->add_param_edge(n2, n1); graph->add_strategy_edge(n2, n1); graph->update_size(); graphs.push_back(graph); }else if( instr.type == ILInstructionType::ALOAD_CST ){ // ALOAD_CST graph = new StrategyGraph(); node_t n1 = graph->new_node(GadgetType::ALOAD); node_t n2 = graph->new_node(GadgetType::MOV_CST); Node& node1 = graph->nodes[n1]; Node& node2 = graph->nodes[n2]; node1.branch_type = BranchType::RET; node2.branch_type = BranchType::RET; // First node is reg Op<- mem(X + C) // Second is X <- src_cst - C node1.params[PARAM_ALOAD_DST_REG].make_reg(instr.args[PARAM_ALOADCST_DST_REG]); node1.params[PARAM_ALOAD_DST_REG].is_data_link = true; node1.params[PARAM_ALOAD_OP].make_op((Op)instr.args[PARAM_ALOADCST_OP]); node1.params[PARAM_ALOAD_SRC_ADDR_REG].make_reg(-1, false); // Free node1.params[PARAM_ALOAD_SRC_ADDR_OFFSET].make_cst(-1, graph->new_name("offset"), false); node1.node_assertion.valid_pointers.add_valid_pointer(PARAM_ALOAD_SRC_ADDR_REG); node2.params[PARAM_MOVCST_DST_REG].make_reg(n1, PARAM_ALOAD_SRC_ADDR_REG); // node2 X is same as addr reg in node1 node2.params[PARAM_MOVCST_SRC_CST].make_cst(n1, PARAM_ALOAD_SRC_ADDR_OFFSET, instr.args[PARAM_ALOADCST_SRC_ADDR_OFFSET] - exprvar(arch->bits, node1.params[PARAM_ALOAD_SRC_ADDR_OFFSET].name) , graph->new_name("cst")); // node2 cst is the target const C minus the offset in the node1 load graph->add_param_edge(n2, n1); graph->add_strategy_edge(n2, n1); graph->update_size(); graphs.push_back(graph); }else if( instr.type == ILInstructionType::STORE ){ // STORE graph = new StrategyGraph(); node_t n = graph->new_node(GadgetType::STORE); Node& node = graph->nodes[n]; node.branch_type = BranchType::RET; node.params[PARAM_STORE_DST_ADDR_REG].make_reg(instr.args[PARAM_STORE_DST_ADDR_REG]); node.params[PARAM_STORE_DST_ADDR_REG].is_data_link = true; node.params[PARAM_STORE_DST_ADDR_OFFSET].make_cst(instr.args[PARAM_STORE_DST_ADDR_OFFSET], graph->new_name("offset")); node.params[PARAM_STORE_SRC_REG].make_reg(instr.args[PARAM_STORE_SRC_REG]); node.params[PARAM_STORE_SRC_REG].is_data_link = true; node.node_assertion.valid_pointers.add_valid_pointer(PARAM_STORE_DST_ADDR_REG); graph->update_size(); graphs.push_back(graph); }else if( instr.type == ILInstructionType::CST_STORE ){ // CST_STORE graph = new StrategyGraph(); node_t n1 = graph->new_node(GadgetType::STORE); node_t n2 = graph->new_node(GadgetType::MOV_CST); Node& node1 = graph->nodes[n1]; Node& node2 = graph->nodes[n2]; node1.branch_type = BranchType::RET; node2.branch_type = BranchType::RET; // First node is mem(X + C) <- reg // Second is X <- src_cst - C node1.params[PARAM_STORE_SRC_REG].make_reg(instr.args[PARAM_CSTSTORE_SRC_REG]); node1.params[PARAM_STORE_SRC_REG].is_data_link = true; node1.params[PARAM_STORE_DST_ADDR_REG].make_reg(-1, false); // Free node1.params[PARAM_STORE_DST_ADDR_OFFSET].make_cst(-1, graph->new_name("offset"), false); node1.strategy_constraints.push_back( // Can not adjust the addr_reg if it is the same as the reg that must be written // (i.e mov [ecx+8], ecx can't become mov [0x12345678], ecx [](Node* n, StrategyGraph* g, Arch* arch)->bool{ return n->params[PARAM_STORE_DST_ADDR_REG].value != n->params[PARAM_STORE_SRC_REG].value; } ); node1.node_assertion.valid_pointers.add_valid_pointer(PARAM_STORE_DST_ADDR_REG); node2.params[PARAM_MOVCST_DST_REG].make_reg(n1, PARAM_STORE_DST_ADDR_REG); // node2 X is same as addr reg in node1 node2.params[PARAM_MOVCST_SRC_CST].make_cst(n1, PARAM_STORE_DST_ADDR_OFFSET, instr.args[PARAM_CSTSTORE_DST_ADDR_OFFSET] - exprvar(arch->bits, node1.params[PARAM_STORE_DST_ADDR_OFFSET].name) , graph->new_name("cst")); // node2 cst is the target const C minus the offset in the node1 load graph->add_param_edge(n2, n1); graph->add_strategy_edge(n2, n1); graph->update_size(); graphs.push_back(graph); }else if( instr.type == ILInstructionType::ASTORE ){ // ASTORE graph = new StrategyGraph(); node_t n = graph->new_node(GadgetType::ASTORE); Node& node = graph->nodes[n]; node.branch_type = BranchType::RET; node.params[PARAM_ASTORE_DST_ADDR_REG].make_reg(instr.args[PARAM_ASTORE_DST_ADDR_REG]); node.params[PARAM_ASTORE_DST_ADDR_REG].is_data_link = true; node.params[PARAM_ASTORE_DST_ADDR_OFFSET].make_cst(instr.args[PARAM_ASTORE_DST_ADDR_OFFSET], graph->new_name("offset")); node.params[PARAM_ASTORE_OP].make_op((Op)instr.args[PARAM_ASTORE_OP]); node.params[PARAM_ASTORE_SRC_REG].make_reg(instr.args[PARAM_ASTORE_SRC_REG]); node.params[PARAM_ASTORE_SRC_REG].is_data_link = true; node.node_assertion.valid_pointers.add_valid_pointer(PARAM_ASTORE_DST_ADDR_REG); graph->update_size(); graphs.push_back(graph); }else if( instr.type == ILInstructionType::CST_ASTORE ){ // CST_ASTORE graph = new StrategyGraph(); node_t n1 = graph->new_node(GadgetType::ASTORE); node_t n2 = graph->new_node(GadgetType::MOV_CST); Node& node1 = graph->nodes[n1]; Node& node2 = graph->nodes[n2]; node1.branch_type = BranchType::RET; node2.branch_type = BranchType::RET; // First node is mem(X + C) op<- reg // Second is X <- src_cst - C node1.params[PARAM_ASTORE_SRC_REG].make_reg(instr.args[PARAM_CSTASTORE_SRC_REG]); node1.params[PARAM_ASTORE_SRC_REG].is_data_link = true; node1.params[PARAM_ASTORE_OP].make_op((Op)instr.args[PARAM_CSTASTORE_OP]); node1.params[PARAM_ASTORE_DST_ADDR_REG].make_reg(-1, false); // Free node1.params[PARAM_ASTORE_DST_ADDR_OFFSET].make_cst(-1, graph->new_name("offset"), false); node1.strategy_constraints.push_back( // Can not adjust the addr_reg if it is the same as the reg that must be written // (i.e mov [ecx+8], ecx can't become mov [0x12345678], ecx [](Node* n, StrategyGraph* g, Arch * arch)->bool{ return n->params[PARAM_ASTORE_DST_ADDR_REG].value != n->params[PARAM_ASTORE_SRC_REG].value; } ); node1.node_assertion.valid_pointers.add_valid_pointer(PARAM_ASTORE_DST_ADDR_REG); node2.params[PARAM_MOVCST_DST_REG].make_reg(n1, PARAM_ASTORE_DST_ADDR_REG); // node2 X is same as addr reg in node1 node2.params[PARAM_MOVCST_SRC_CST].make_cst(n1, PARAM_ASTORE_DST_ADDR_OFFSET, instr.args[PARAM_CSTASTORE_DST_ADDR_OFFSET] - exprvar(arch->bits, node1.params[PARAM_ASTORE_DST_ADDR_OFFSET].name) , graph->new_name("cst")); // node2 cst is the target const C minus the offset in the node1 load graph->add_param_edge(n2, n1); graph->add_strategy_edge(n2, n1); graph->update_size(); graphs.push_back(graph); }else if( instr.type == ILInstructionType::STORE_CST ){ // STORE_CST graph = new StrategyGraph(); node_t n = graph->new_node(GadgetType::STORE); node_t n1 = graph->new_node(GadgetType::MOV_CST); Node& node = graph->nodes[n]; Node& node1 = graph->nodes[n1]; node.branch_type = BranchType::RET; node.params[PARAM_STORE_DST_ADDR_REG].make_reg(instr.args[PARAM_STORE_DST_ADDR_REG]); node.params[PARAM_STORE_DST_ADDR_OFFSET].make_cst(instr.args[PARAM_STORE_DST_ADDR_OFFSET], graph->new_name("offset")); node.params[PARAM_STORE_SRC_REG].make_reg(-1, false); // Free reg node.node_assertion.valid_pointers.add_valid_pointer(PARAM_STORE_DST_ADDR_REG); node1.branch_type = BranchType::RET; node1.params[PARAM_MOVCST_DST_REG].make_reg(node.id, PARAM_STORE_SRC_REG); node1.params[PARAM_MOVCST_DST_REG].is_data_link = true; node1.params[PARAM_MOVCST_SRC_CST].make_cst(instr.args[PARAM_STORECST_SRC_CST], graph->new_name("cst")); graph->add_strategy_edge(node1.id, node.id); graph->add_param_edge(node1.id, node.id); graph->update_size(); graphs.push_back(graph); }else if( instr.type == ILInstructionType::CST_STORE_CST ){ // CST_STORE_CST graph = new StrategyGraph(); if( _cst_store_cst_to_strategy(*graph, instr, arch)){ graph->update_size(); graphs.push_back(graph); } }else if( instr.type == ILInstructionType::ASTORE_CST ){ // ASTORE_CST graph = new StrategyGraph(); node_t n = graph->new_node(GadgetType::ASTORE); node_t n1 = graph->new_node(GadgetType::MOV_CST); Node& node = graph->nodes[n]; Node& node1 = graph->nodes[n1]; node.branch_type = BranchType::RET; node.params[PARAM_ASTORE_DST_ADDR_REG].make_reg(instr.args[PARAM_ASTORE_DST_ADDR_REG]); node.params[PARAM_ASTORE_DST_ADDR_REG].is_data_link = true; node.params[PARAM_ASTORE_DST_ADDR_OFFSET].make_cst(instr.args[PARAM_ASTORE_DST_ADDR_OFFSET], graph->new_name("offset")); node.params[PARAM_ASTORE_SRC_REG].make_reg(-1, false); // Free reg node.params[PARAM_ASTORE_OP].make_op((Op)instr.args[PARAM_ASTORECST_OP]); node.node_assertion.valid_pointers.add_valid_pointer(PARAM_ASTORE_DST_ADDR_REG); node1.branch_type = BranchType::RET; node1.params[PARAM_MOVCST_DST_REG].make_reg(node.id, PARAM_ASTORE_SRC_REG); node1.params[PARAM_MOVCST_DST_REG].is_data_link = true; node1.params[PARAM_MOVCST_SRC_CST].make_cst(instr.args[PARAM_ASTORECST_SRC_CST], graph->new_name("cst")); graph->add_strategy_edge(node1.id, node.id); graph->add_param_edge(node1.id, node.id); graph->update_size(); graphs.push_back(graph); }else if( instr.type == ILInstructionType::CST_ASTORE_CST ){ // CST_ASTORE_CST graph = new StrategyGraph(); node_t n1 = graph->new_node(GadgetType::ASTORE); node_t n2 = graph->new_node(GadgetType::MOV_CST); node_t n3 = graph->new_node(GadgetType::MOV_CST); Node& node1 = graph->nodes[n1]; Node& node2 = graph->nodes[n2]; Node& node3 = graph->nodes[n3]; node1.branch_type = BranchType::RET; node2.branch_type = BranchType::RET; node3.branch_type = BranchType::RET; // First node is mem(X + C) <- reg // Second is X <- src_cst - C node1.params[PARAM_ASTORE_OP].make_op((Op)instr.args[PARAM_CSTASTORECST_OP]); node1.params[PARAM_ASTORE_SRC_REG].make_reg(-1, false); // Free reg node1.params[PARAM_ASTORE_DST_ADDR_REG].make_reg(-1, false); // Free reg node1.params[PARAM_ASTORE_DST_ADDR_OFFSET].make_cst(-1, graph->new_name("offset"), false); // Free offset also node1.strategy_constraints.push_back( // Can not adjust the addr_reg if it is the same as the reg that must be written // (i.e mov [ecx+8], ecx can't become mov [0x12345678], ecx [](Node* n, StrategyGraph* g, Arch* arch)->bool{ return n->params[PARAM_ASTORE_DST_ADDR_REG].value != n->params[PARAM_ASTORE_SRC_REG].value; } ); node1.node_assertion.valid_pointers.add_valid_pointer(PARAM_ASTORE_DST_ADDR_REG); node2.params[PARAM_MOVCST_DST_REG].make_reg(n1, PARAM_ASTORE_DST_ADDR_REG); // node2 X is same as addr reg in node1 node2.params[PARAM_MOVCST_DST_REG].is_data_link = true; node2.params[PARAM_MOVCST_SRC_CST].make_cst(n1, PARAM_ASTORE_DST_ADDR_OFFSET, instr.args[PARAM_CSTASTORECST_DST_ADDR_OFFSET] - exprvar(arch->bits, node1.params[PARAM_ASTORE_DST_ADDR_OFFSET].name) , graph->new_name("cst")); // node2 cst is the target const C minus the offset in the node1 load node3.params[PARAM_MOVCST_DST_REG].make_reg(n1, PARAM_ASTORE_SRC_REG); node3.params[PARAM_MOVCST_DST_REG].is_data_link = true; node3.params[PARAM_MOVCST_SRC_CST].make_cst(instr.args[PARAM_CSTASTORECST_SRC_CST], graph->new_name("cst")); graph->add_param_edge(n2, n1); graph->add_strategy_edge(n2, n1); graph->add_param_edge(n3, n1); graph->add_strategy_edge(n3, n1); graph->update_size(); graphs.push_back(graph); }else if( instr.type == ILInstructionType::FUNCTION ){ graph = new StrategyGraph(); bool success = false; switch( abi ){ case ABI::X86_CDECL: success = _x86_cdecl_to_strategy(*graph, instr); break; case ABI::X86_STDCALL: success = _x86_stdcall_to_strategy(*graph, instr); break; case ABI::NONE: throw compiler_exception("You have to specify which ABI to use to call functions"); default: throw compiler_exception("il_instruction_to_strategy(): Unsupported ABI for function call"); } if( !success ){ throw compiler_exception("Couldn't translate function call into a chaining strategy"); } graph->update_size(); graphs.push_back(graph); }else if( instr.type == ILInstructionType::SINGLE_SYSCALL ){ graph = new StrategyGraph(); node_t n = graph->new_node(GadgetType::SYSCALL); graph->nodes[n].branch_type = BranchType::ANY; graph->update_size(); graphs.push_back(graph); }else{ throw runtime_exception("il_instruction_to_strategy(): unsupported ILInstructionType"); } } ================================================ FILE: libropium/compiler/il.cpp ================================================ #include "il.hpp" #include #include #include "exception.hpp" #include using std::string; /* ======= Parse IL Instructions ========== */ void _skip_whitespace(string& str, int& idx){ while( isspace(str[idx]) && str[idx] != '\n' && idx < str.size()){ idx++; } } bool _parse_end(string& str, int& idx){ while( idx < str.size() ){ if( !isspace(str[idx]) ) return false; idx++; } return true; } bool _parse_il_cst(Arch& arch, vector & args, string& str, int& idx){ string s; int i; int base = 10; ucst_t cst; cst_t mult; _skip_whitespace(str, idx); // Check if sign - in front of constant if( str[idx] == '-' ){ mult = -1; idx++; }else mult = 1; _skip_whitespace(str, idx); // Check if hexa if( str.substr(idx, 2) == "0x"){ idx += 2; s = "0x"; base = 16; } i = idx; if( base == 10 ){ while( i < str.size() && isdigit(str[i])){ s += str[i++]; } }else if( base == 16 ){ while( i < str.size() && isxdigit(str[i])){ s += str[i++]; } } try{ cst = std::stoull(s, 0, base); // Check if cst is not too big if( arch.octets < 8 && (cst >= (ucst_t)((ucst_t)1<<(arch.bits)))){ return false; } idx = i; args.push_back(cst * mult); return true; }catch(std::invalid_argument const& e){ return false; }catch(std::out_of_range const& e){ return false; } } bool _parse_il_reg(Arch& arch, vector & args, string& str, int& idx){ string s; string prev; int i, prev_i; bool found = false; _skip_whitespace(str, idx); i = idx; while( i < str.size() && !isspace(str[i])){ s += str[i++]; if( arch.is_valid_reg(s) ){ found = true; prev = s; prev_i = i; }else if( found ){ break; } } if( found ){ args.push_back(arch.reg_num(prev)); idx = prev_i; return true; }else return false; } bool _parse_il_string( string& res, string& str, int& idx){ int i = idx; string s = ""; char delimiter; _skip_whitespace(str, i); // Check if starts with strin delimiter " or ' if( str.size() - i < 2 ) return false; if( str[i] == '\'' ){ delimiter = '\''; }else if( str[i] == '"'){ delimiter = '"'; }else{ return false; } // Get string i++; while( i < str.size() && str[i] != delimiter){ if( str[i] == '\\' ){ // Escape sequence // Escapes '\' ? if( i+1 >= str.size() ){ return false; }else if( str[i+1] == '\\' ){ s += '\\'; i += 2; } // Escapes delimiter ? else if( str[i+1] == delimiter ){ s += delimiter; i += 2; } // Escapes a hex byte ? '\x...' else if( i+3 >= str.size() ){ return false; }else if( str[i+1] == 'x' && isxdigit(str[i+2]) && isxdigit(str[i+3])){ unsigned int byte = std::stoul(str.substr(i+2, 2), nullptr, 16); s += (char)byte; i += 4; }else{ return false; } }else{ // Normal char s += str[i++]; } } // Check and return if( i == str.size() ) return false; // No end delimiter found else if( s.empty() ){ return false; // Empty string not allowed (use "\x00" instead }else{ res = s; idx = ++i; // Increment i to make it point after the last delimiter return true; } } bool _parse_il_affect(string& str, int& idx){ _skip_whitespace(str, idx); if( idx >= str.size()) return false; if( str[idx] == '=' ){ idx++; return true; }else return false; } bool _parse_il_mem_start(string& str, int& idx){ _skip_whitespace(str, idx); if( idx > str.size()-1) return false; if( str.substr(idx, 1) == "[" ){ idx+=1; return true; }else return false; } bool _parse_il_mem_end(string& str, int& idx){ _skip_whitespace(str, idx); if( idx >= str.size()) return false; if( str[idx] == ']' ){ idx++; return true; }else return false; } bool _parse_il_function_args_list(Arch& arch, vector & args, vector & args_type, string& str, int& i){ _skip_whitespace(str, i); // Parse first argument if any if( _parse_il_cst(arch, args, str, i)){ args_type.push_back(IL_FUNC_ARG_CST); }else if( _parse_il_reg(arch, args, str, i)){ args_type.push_back(IL_FUNC_ARG_REG); } // Go to next char _skip_whitespace(str, i); // If coma, next argument is expected if( str[i] == ','){ return _parse_il_function_args_list(arch, args, args_type, str, ++i); // Else just return true }else{ return true; } } // (arg1, arg2, arg3, ... ) // or just () bool _parse_il_function_args(Arch& arch, vector & args, vector & args_type, string& str, int& idx){ int i = idx; _skip_whitespace(str, i); if( i >= str.size()) return false; if( str[i] != '(' ) return false; i++; // Parse args list if( _parse_il_function_args_list(arch, args, args_type, str, i)){ _skip_whitespace(str, i); if( str[i] == ')' ){ idx = i+1; return true; }else return false; }else{ return false; } } bool _parse_il_syscall_name( string& name, string& str, int& idx){ int i = idx; string s = ""; _skip_whitespace(str, i); // Check if starts with sys_ if( str.size() - i < 4 ) return false; if( str.substr(i, 4) != "sys_" ){ return false; }else{ i += 4; } // Get name while( i < str.size() && (isalpha(str[i]) || isdigit(str[i]))){ s += str[i++]; } // Check and return if( s.empty() ) return false; // No empty syscall name allowed else{ name = s; idx = i; return true; } } bool _parse_il_single_syscall(Arch& arch, ILInstruction* instr, string& str){ int i = 0; _skip_whitespace(str, i); // Check if starts with sys_ if( str.size() - i < 7 ) return false; if( str.substr(i, 7) != "syscall" ){ return false; }else{ i += 7; } if( _parse_end(str, i)){ instr->type = ILInstructionType::SINGLE_SYSCALL; return true; }else{ return false; } } bool _parse_il_syscall_num( Arch& arch, int& num, string& str, int& idx){ int i = idx; vector args; _skip_whitespace(str, i); // Check if starts with sys_ if( str.size() - i < 4 ) return false; if( str.substr(i, 4) != "sys_" ){ return false; }else{ i += 4; } // Get num if( ! _parse_il_cst(arch, args, str, i)){ return false; }else{ num = args[0]; idx = i; return true; } } bool _parse_il_reg_and_offset(Arch& arch, vector & args, string& str, int& idx){ cst_t mult; // Get reg _skip_whitespace(str, idx); if( !_parse_il_reg(arch, args, str, idx) ){ return false; } // Parse op (+ or - ) _skip_whitespace(str, idx); if( str[idx] == '+' ){ mult = 1; idx++; }else if( str[idx] == '-' ){ mult = -1; idx++; }else{ args.push_back(0); // Push null offset return true; } // Parse offset _skip_whitespace(str, idx); if( _parse_il_cst(arch, args, str, idx)){ args.back() = args.back() * mult; // Adjust const return true; } return false; } bool _parse_il_binop(vector & args, string& str, int& idx){ Op res = Op::NONE; _skip_whitespace(str, idx); if( idx == str.size()) return false; switch( str[idx] ){ case '+': idx++; res = Op::ADD; break; case '/': idx++; res = Op::DIV; break; case '*': idx++; res = Op::MUL; break; case '^': idx++; res = Op::XOR; break; case '&': idx++; res = Op::AND; break; case '|': idx++; res = Op::OR; break; case '%': idx++; res = Op::MOD; break; default: break; } if( res == Op::NONE ){ if( str.substr(idx,2) == "<<" ){ idx += 2; res = Op::SHL; }else if( str.substr(idx,2) == ">>" ){ idx += 2; res = Op::SHR; }else{ return false; } } args.push_back((int)res); return true; } bool _parse_il_unop(vector & args, string& str, int& idx){ Op res; _skip_whitespace(str, idx); if( idx == str.size()) return false; switch( str[idx] ){ case '-': idx++; res = Op::NEG; break; case '~': idx++; res = Op::NOT; break; default: return false; } args.push_back((int)res); return true; } bool _parse_il_mov_reg(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; if (_parse_il_reg(arch, args, str, idx) && _parse_il_affect(str, idx) && _parse_il_reg(arch, args, str, idx) && _parse_end(str, idx)) { instr->args = args; instr->type = ILInstructionType::MOV_REG; return true; } return false; } bool _parse_il_mov_cst(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; if (_parse_il_reg(arch, args, str, idx) && _parse_il_affect(str, idx) && _parse_il_cst(arch, args, str, idx) && _parse_end(str, idx)) { instr->args = args; instr->type = ILInstructionType::MOV_CST; return true; } return false; } bool _parse_il_amov_cst(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; if (_parse_il_reg(arch, args, str, idx) && _parse_il_affect(str, idx) && _parse_il_reg(arch, args, str, idx) && _parse_il_binop(args, str, idx) && _parse_il_cst(arch, args, str, idx) && _parse_end(str, idx)) { instr->args = args; instr->type = ILInstructionType::AMOV_CST; return true; } return false; } bool _parse_il_amov_reg(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; if (_parse_il_reg(arch, args, str, idx) && _parse_il_affect(str, idx) && _parse_il_reg(arch, args, str, idx) && _parse_il_binop(args, str, idx) && _parse_il_reg(arch, args, str, idx) && _parse_end(str, idx)) { instr->args = args; instr->type = ILInstructionType::AMOV_REG; return true; } return false; } bool _parse_il_load(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; if (_parse_il_reg(arch, args, str, idx) && _parse_il_affect(str, idx) && _parse_il_mem_start(str, idx) && _parse_il_reg_and_offset(arch, args, str, idx) && _parse_il_mem_end(str, idx) && _parse_end(str, idx)) { instr->args = args; instr->type = ILInstructionType::LOAD; return true; } return false; } bool _parse_il_aload(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; if (_parse_il_reg(arch, args, str, idx) && _parse_il_binop(args, str, idx) && _parse_il_affect(str, idx) && _parse_il_mem_start(str, idx) && _parse_il_reg_and_offset(arch, args, str, idx) && _parse_il_mem_end(str, idx) && _parse_end(str, idx)) { instr->args = args; instr->type = ILInstructionType::ALOAD; return true; } return false; } bool _parse_il_load_cst(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; if (_parse_il_reg(arch, args, str, idx) && _parse_il_affect(str, idx) && _parse_il_mem_start(str, idx) && _parse_il_cst(arch, args, str, idx) && _parse_il_mem_end(str, idx) && _parse_end(str, idx)) { instr->args = args; instr->type = ILInstructionType::LOAD_CST; return true; } return false; } bool _parse_il_aload_cst(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; if (_parse_il_reg(arch, args, str, idx) && _parse_il_binop(args, str, idx) && _parse_il_affect(str, idx) && _parse_il_mem_start(str, idx) && _parse_il_cst(arch, args, str, idx) && _parse_il_mem_end(str, idx) && _parse_end(str, idx)) { instr->args = args; instr->type = ILInstructionType::ALOAD_CST; return true; } return false; } bool _parse_il_store(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; if (_parse_il_mem_start(str, idx) && _parse_il_reg_and_offset(arch, args, str, idx) && _parse_il_mem_end(str, idx) && _parse_il_affect(str, idx) && _parse_il_reg(arch, args, str, idx) && _parse_end(str, idx)) { instr->args = args; instr->type = ILInstructionType::STORE; return true; } return false; } bool _parse_il_astore(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; if (_parse_il_mem_start(str, idx) && _parse_il_reg_and_offset(arch, args, str, idx) && _parse_il_mem_end(str, idx) && _parse_il_binop(args, str, idx) && _parse_il_affect(str, idx) && _parse_il_reg(arch, args, str, idx) && _parse_end(str, idx)) { instr->args = args; instr->type = ILInstructionType::ASTORE; return true; } return false; } bool _parse_il_cst_store(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; if (_parse_il_mem_start(str, idx) && _parse_il_cst(arch, args, str, idx) && _parse_il_mem_end(str, idx) && _parse_il_affect(str, idx) && _parse_il_reg(arch, args, str, idx) && _parse_end(str, idx)) { instr->args = args; instr->type = ILInstructionType::CST_STORE; return true; } return false; } bool _parse_il_cst_astore(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; if (_parse_il_mem_start(str, idx) && _parse_il_cst(arch, args, str, idx) && _parse_il_mem_end(str, idx) && _parse_il_binop(args, str, idx) && _parse_il_affect(str, idx) && _parse_il_reg(arch, args, str, idx) && _parse_end(str, idx)) { instr->args = args; instr->type = ILInstructionType::CST_ASTORE; return true; } return false; } bool _parse_il_store_cst(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; if (_parse_il_mem_start(str, idx) && _parse_il_reg_and_offset(arch, args, str, idx) && _parse_il_mem_end(str, idx) && _parse_il_affect(str, idx) && _parse_il_cst(arch, args, str, idx) && _parse_end(str, idx)) { instr->args = args; instr->type = ILInstructionType::STORE_CST; return true; } return false; } bool _parse_il_astore_cst(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; if (_parse_il_mem_start(str, idx) && _parse_il_reg_and_offset(arch, args, str, idx) && _parse_il_mem_end(str, idx) && _parse_il_binop(args, str, idx) && _parse_il_affect(str, idx) && _parse_il_cst(arch, args, str, idx) && _parse_end(str, idx)) { instr->args = args; instr->type = ILInstructionType::ASTORE_CST; return true; } return false; } bool _parse_il_cst_store_cst(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; if (_parse_il_mem_start(str, idx) && _parse_il_cst(arch, args, str, idx) && _parse_il_mem_end(str, idx) && _parse_il_affect(str, idx) && _parse_il_cst(arch, args, str, idx) && _parse_end(str, idx)) { instr->args = args; instr->type = ILInstructionType::CST_STORE_CST; return true; } return false; } bool _parse_il_cst_astore_cst(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; if (_parse_il_mem_start(str, idx) && _parse_il_cst(arch, args, str, idx) && _parse_il_mem_end(str, idx) && _parse_il_binop(args, str, idx) && _parse_il_affect(str, idx) && _parse_il_cst(arch, args, str, idx) && _parse_end(str, idx)) { instr->args = args; instr->type = ILInstructionType::CST_ASTORE_CST; return true; } return false; } bool _parse_il_cst_store_string(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; string s; if (_parse_il_mem_start(str, idx) && _parse_il_cst(arch, args, str, idx) && _parse_il_mem_end(str, idx) && _parse_il_affect(str, idx) && _parse_il_string(s, str, idx) && _parse_end(str, idx)) { instr->args = args; instr->str = s; instr->type = ILInstructionType::CST_STORE_STRING; return true; } return false; } bool _parse_il_function(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; vector args_type; if (_parse_il_cst(arch, args, str, idx) && _parse_il_function_args(arch, args, args_type, str, idx) && _parse_end(str, idx)) { instr->args = args; instr->args_type = args_type; instr->args_type.insert(instr->args_type.begin(), -1); // Because first arg is the function address :/ instr->type = ILInstructionType::FUNCTION; return true; } return false; } bool _parse_il_syscall_by_name(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; vector args_type; string name; if (_parse_il_syscall_name(name, str, idx) && _parse_il_function_args(arch, args, args_type, str, idx) && _parse_end(str, idx)) { instr->syscall_name = name; instr->args = args; instr->args_type = args_type; instr->type = ILInstructionType::SYSCALL; return true; } return false; } bool _parse_il_syscall_by_num(Arch& arch, ILInstruction* instr, string& str){ int idx = 0; vector args; vector args_type; int num; if (_parse_il_syscall_num(arch, num, str, idx) && _parse_il_function_args(arch, args, args_type, str, idx) && _parse_end(str, idx)) { instr->syscall_num = num; instr->args = args; instr->args_type = args_type; instr->type = ILInstructionType::SYSCALL; return true; } return false; } bool _parse_il_syscall(Arch& arch, ILInstruction* instr, string& str){ // NUM before NAME otherwise the num is just parsed as a name string :) return _parse_il_syscall_by_num(arch, instr, str) || _parse_il_syscall_by_name(arch, instr, str); } bool _parse_il_instruction(Arch& arch, ILInstruction* instr, string& str){ return _parse_il_mov_cst(arch, instr, str) || _parse_il_mov_reg(arch, instr, str) || _parse_il_amov_cst(arch, instr, str) || _parse_il_amov_reg(arch, instr, str) || _parse_il_load(arch, instr, str) || _parse_il_load_cst(arch, instr, str) || _parse_il_aload(arch, instr, str) || _parse_il_aload_cst(arch, instr, str) || _parse_il_store(arch, instr, str) || _parse_il_cst_store(arch, instr, str) || _parse_il_astore(arch, instr, str) || _parse_il_cst_astore(arch, instr, str) || _parse_il_store_cst(arch, instr, str) || _parse_il_cst_store_cst(arch, instr, str) || _parse_il_astore_cst(arch, instr, str) || _parse_il_cst_astore_cst(arch, instr, str) || _parse_il_cst_store_string(arch, instr, str) || _parse_il_function(arch, instr, str) || _parse_il_syscall(arch, instr, str) || _parse_il_single_syscall(arch, instr, str); } ILInstruction::ILInstruction(Arch& arch, string str){ if( !_parse_il_instruction(arch, this, str)){ throw il_exception("Invalid instruction string"); } } ILInstruction::ILInstruction(ILInstructionType t, vector * a, vector * at , string sname, int snum, string s){ type = t; syscall_name = sname; syscall_num = snum; str = s; if( a ) args = *a; if( at ) args_type = *at; } ================================================ FILE: libropium/compiler/strategy_graph.cpp ================================================ #include "strategy.hpp" #include "expression.hpp" #include "exception.hpp" #include /* =============== Node Assertions ============== */ void NodeValidPointers::add_valid_pointer(param_t p){ _params.push_back(p); } void NodeValidPointers::to_assertion(Node& node, Assertion* assertion){ for( auto p : _params ){ assertion->valid_pointers.add_valid_pointer(node.params[p].value); } } void NodeValidPointers::clear(){ _params.clear(); } void NodeAssertion::clear(){ valid_pointers.clear(); } void NodeAssertion::to_assertion(Node& node, Assertion * a){ valid_pointers.to_assertion(node, a); } /* =============== Nodes ============== */ bool constraint_branch_type(Node* node, StrategyGraph* graph, Arch* arch){ return (node->affected_gadget->branch_type == node->branch_type) || (node->branch_type == BranchType::ANY); } Node::Node(int i, GadgetType t):id(i), type(t), branch_type(BranchType::ANY), is_indirect(false), is_disabled(false), affected_gadget(nullptr){ mandatory_following_node = -1; // Add constraints that must always be verified assigned_gadget_constraints.push_back(constraint_branch_type); // Matching branch type }; bool Node::has_mandatory_following_node(){ return mandatory_following_node != -1; } int Node::nb_params(){ switch( type ){ case GadgetType::MOV_REG: return NB_PARAM_MOVREG; case GadgetType::MOV_CST: return NB_PARAM_MOVCST; case GadgetType::AMOV_CST: return NB_PARAM_AMOVCST; case GadgetType::AMOV_REG: return NB_PARAM_AMOVREG; case GadgetType::LOAD: return NB_PARAM_LOAD; case GadgetType::ALOAD: return NB_PARAM_ALOAD; case GadgetType::STORE: return NB_PARAM_STORE; case GadgetType::ASTORE: return NB_PARAM_ASTORE; case GadgetType::SYSCALL: return NB_PARAM_SYSCALL; case GadgetType::INT80: return NB_PARAM_INT80; default: throw runtime_exception("Unsupported gadget type in Node::nb_params()"); } } bool Node::has_free_param(){ for( int p = 0; p < nb_params(); p++){ if( params[p].is_free() ) return true; } return false; } bool Node::is_final_param(param_t param){ return strategy_edges.out.empty() && ( (has_dst_reg_param() && (param == get_param_num_dst_reg())) || (has_dst_addr_reg_param() && param == get_param_num_dst_addr_reg())); } bool Node::is_initial_param(param_t param){ return is_src_param(param); } bool Node::has_dst_reg_param(){ return type == GadgetType::MOV_CST || type == GadgetType::MOV_REG || type == GadgetType::AMOV_CST || type == GadgetType::AMOV_REG || type == GadgetType::LOAD || type == GadgetType::ALOAD; } bool Node::has_dst_addr_reg_param(){ return type == GadgetType::STORE || type == GadgetType::ASTORE; } bool Node::is_src_param(param_t param){ switch( type ){ case GadgetType::MOV_REG: return param == PARAM_MOVREG_SRC_REG; case GadgetType::MOV_CST: return false; case GadgetType::AMOV_CST: return param == PARAM_AMOVCST_SRC_REG; case GadgetType::AMOV_REG: return param == PARAM_AMOVREG_SRC_REG1 || param == PARAM_AMOVREG_SRC_REG2; case GadgetType::LOAD: return param == PARAM_LOAD_SRC_ADDR_REG; case GadgetType::ALOAD: return param == PARAM_ALOAD_SRC_ADDR_REG; case GadgetType::STORE: return param == PARAM_STORE_SRC_REG; case GadgetType::ASTORE: return param == PARAM_ASTORE_SRC_REG; case GadgetType::SYSCALL: case GadgetType::INT80: return false; default: throw runtime_exception(QuickFmt() << "Node::is_src_param(): got unsupported node type " << (int)type >> QuickFmt::to_str); } } bool Node::is_generic_param(param_t param){ return param == get_param_num_gadget_addr() || param == get_param_num_gadget_jmp_reg() || param == get_param_num_gadget_sp_delta() || param == get_param_num_gadget_sp_inc(); } void Node::add_incoming_strategy_edge(node_t src_node){ if( std::find(strategy_edges.in.begin(), strategy_edges.in.end(), src_node) == strategy_edges.in.end()){ strategy_edges.in.push_back(src_node); } } void Node::add_incoming_param_edge(node_t src_node){ if( std::find(param_edges.in.begin(), param_edges.in.end(), src_node) == param_edges.in.end()){ param_edges.in.push_back(src_node); } } void Node::add_outgoing_strategy_edge(node_t dst_node){ if( std::find(strategy_edges.out.begin(), strategy_edges.out.end(), dst_node) == strategy_edges.out.end()){ strategy_edges.out.push_back(dst_node); } } void Node::add_outgoing_param_edge(node_t dst_node){ if( std::find(param_edges.out.begin(), param_edges.out.end(), dst_node) == param_edges.out.end()){ param_edges.out.push_back(dst_node); } } void Node::remove_incoming_strategy_edge(node_t src_node){ strategy_edges.in.erase(std::remove(strategy_edges.in.begin(), strategy_edges.in.end(), src_node), strategy_edges.in.end()); } void Node::remove_incoming_param_edge(node_t src_node){ param_edges.in.erase(std::remove(param_edges.in.begin(), param_edges.in.end(), src_node), param_edges.in.end()); } void Node::remove_outgoing_strategy_edge(node_t dst_node){ strategy_edges.out.erase(std::remove(strategy_edges.out.begin(), strategy_edges.out.end(), dst_node), strategy_edges.out.end()); } void Node::remove_outgoing_param_edge(node_t dst_node){ param_edges.out.erase(std::remove(param_edges.out.begin(), param_edges.out.end(), dst_node), param_edges.out.end()); } int Node::get_param_num_data_link(){ switch( type ){ case GadgetType::MOV_REG: return PARAM_MOVREG_DATA_LINK; case GadgetType::AMOV_REG: return PARAM_AMOVREG_DATA_LINK; case GadgetType::MOV_CST: return PARAM_MOVCST_DATA_LINK; case GadgetType::AMOV_CST: return PARAM_AMOVCST_DATA_LINK; case GadgetType::LOAD: return PARAM_LOAD_DATA_LINK; case GadgetType::ALOAD: return PARAM_ALOAD_DATA_LINK; case GadgetType::STORE: return PARAM_STORE_DATA_LINK; case GadgetType::ASTORE: return PARAM_ASTORE_DATA_LINK; case GadgetType::SYSCALL: return PARAM_SYSCALL_DATA_LINK; case GadgetType::INT80: return PARAM_INT80_DATA_LINK; default: throw runtime_exception("Node::get_param_num_data_link(): got unsupported gadget type"); } } int Node::get_param_num_gadget_sp_inc(){ switch( type ){ case GadgetType::MOV_REG: return PARAM_MOVREG_GADGET_SP_INC; case GadgetType::AMOV_REG: return PARAM_AMOVREG_GADGET_SP_INC; case GadgetType::MOV_CST: return PARAM_MOVCST_GADGET_SP_INC; case GadgetType::AMOV_CST: return PARAM_AMOVCST_GADGET_SP_INC; case GadgetType::LOAD: return PARAM_LOAD_GADGET_SP_INC; case GadgetType::ALOAD: return PARAM_ALOAD_GADGET_SP_INC; case GadgetType::STORE: return PARAM_STORE_GADGET_SP_INC; case GadgetType::ASTORE: return PARAM_ASTORE_GADGET_SP_INC; case GadgetType::SYSCALL: return PARAM_SYSCALL_GADGET_SP_INC; case GadgetType::INT80: return PARAM_INT80_GADGET_SP_INC; default: throw runtime_exception("Node::get_param_num_gadget_sp_inc(): got unsupported gadget type"); } } int Node::get_param_num_gadget_sp_delta(){ switch( type ){ case GadgetType::MOV_REG: return PARAM_MOVREG_GADGET_SP_DELTA; case GadgetType::AMOV_REG: return PARAM_AMOVREG_GADGET_SP_DELTA; case GadgetType::MOV_CST: return PARAM_MOVCST_GADGET_SP_DELTA; case GadgetType::AMOV_CST: return PARAM_AMOVCST_GADGET_SP_DELTA; case GadgetType::LOAD: return PARAM_LOAD_GADGET_SP_DELTA; case GadgetType::ALOAD: return PARAM_ALOAD_GADGET_SP_DELTA; case GadgetType::STORE: return PARAM_STORE_GADGET_SP_DELTA; case GadgetType::ASTORE: return PARAM_ASTORE_GADGET_SP_DELTA; case GadgetType::SYSCALL: return PARAM_SYSCALL_GADGET_SP_DELTA; case GadgetType::INT80: return PARAM_INT80_GADGET_SP_DELTA; default: throw runtime_exception("Node::get_param_num_gadget_sp_inc(): got unsupported gadget type"); } } int Node::get_param_num_gadget_addr(){ switch( type ){ case GadgetType::MOV_REG: return PARAM_MOVREG_GADGET_ADDR; case GadgetType::AMOV_REG: return PARAM_AMOVREG_GADGET_ADDR; case GadgetType::MOV_CST: return PARAM_MOVCST_GADGET_ADDR; case GadgetType::AMOV_CST: return PARAM_AMOVCST_GADGET_ADDR; case GadgetType::LOAD: return PARAM_LOAD_GADGET_ADDR; case GadgetType::ALOAD: return PARAM_ALOAD_GADGET_ADDR; case GadgetType::STORE: return PARAM_STORE_GADGET_ADDR; case GadgetType::ASTORE: return PARAM_ASTORE_GADGET_ADDR; case GadgetType::SYSCALL: return PARAM_SYSCALL_GADGET_ADDR; case GadgetType::INT80: return PARAM_INT80_GADGET_ADDR; default: throw runtime_exception("Node::get_param_num_gadget_addr(): got unsupported gadget type"); } } int Node::get_param_num_gadget_jmp_reg(){ switch( type ){ case GadgetType::MOV_REG: return PARAM_MOVREG_GADGET_JMP_REG; case GadgetType::AMOV_REG: return PARAM_AMOVREG_GADGET_JMP_REG; case GadgetType::MOV_CST: return PARAM_MOVCST_GADGET_JMP_REG; case GadgetType::AMOV_CST: return PARAM_AMOVCST_GADGET_JMP_REG; case GadgetType::LOAD: return PARAM_LOAD_GADGET_JMP_REG; case GadgetType::ALOAD: return PARAM_ALOAD_GADGET_JMP_REG; case GadgetType::STORE: return PARAM_STORE_GADGET_JMP_REG; case GadgetType::ASTORE: return PARAM_ASTORE_GADGET_JMP_REG; case GadgetType::SYSCALL: return PARAM_SYSCALL_GADGET_JMP_REG; case GadgetType::INT80: return PARAM_INT80_GADGET_JMP_REG; default: throw runtime_exception("Node::get_param_num_gadget_jmp_reg(): got unsupported gadget type"); } } int Node::get_param_num_dst_reg(){ switch( type ){ case GadgetType::MOV_REG: return PARAM_MOVREG_DST_REG; case GadgetType::AMOV_REG: return PARAM_AMOVREG_DST_REG; case GadgetType::MOV_CST: return PARAM_MOVCST_DST_REG; case GadgetType::AMOV_CST: return PARAM_AMOVCST_DST_REG; case GadgetType::LOAD: return PARAM_LOAD_DST_REG; case GadgetType::ALOAD: return PARAM_ALOAD_DST_REG; default: throw runtime_exception("Node::get_param_num_dst_reg(): got unsupported gadget type"); } } int Node::get_param_num_src_reg(){ switch( type ){ case GadgetType::MOV_REG: return PARAM_MOVREG_SRC_REG; case GadgetType::MOV_CST: return PARAM_MOVCST_DST_REG; case GadgetType::AMOV_CST: return PARAM_AMOVCST_SRC_REG; case GadgetType::STORE: return PARAM_STORE_SRC_REG; case GadgetType::ASTORE: return PARAM_ASTORE_SRC_REG; default: throw runtime_exception("Node::get_param_num_src_reg(): got unsupported gadget type"); } } int Node::get_param_num_src_addr_offset(){ switch( type ){ case GadgetType::LOAD: return PARAM_LOAD_SRC_ADDR_OFFSET; case GadgetType::ALOAD: return PARAM_ALOAD_SRC_ADDR_OFFSET; default: throw runtime_exception("Node::get_param_num_src_addr_offset(): got unsupported gadget type"); } } int Node::get_param_num_src_addr_reg(){ switch( type ){ case GadgetType::LOAD: return PARAM_LOAD_SRC_ADDR_REG; case GadgetType::ALOAD: return PARAM_ALOAD_SRC_ADDR_REG; default: throw runtime_exception("Node::get_param_num_src_addr_offset(): got unsupported gadget type"); } } int Node::get_param_num_dst_addr_offset(){ switch( type ){ case GadgetType::STORE: return PARAM_STORE_DST_ADDR_OFFSET; case GadgetType::ASTORE: return PARAM_ASTORE_DST_ADDR_OFFSET; default: throw runtime_exception("Node::get_param_num_dst_addr_offset(): got unsupported gadget type"); } } int Node::get_param_num_dst_addr_reg(){ switch( type ){ case GadgetType::STORE: return PARAM_STORE_DST_ADDR_REG; case GadgetType::ASTORE: return PARAM_ASTORE_DST_ADDR_REG; default: throw runtime_exception("Node::get_param_num_dst_addr_offset(): got unsupported gadget type"); } } bool Node::modifies_reg(int reg_num){ return (affected_gadget->modified_regs[reg_num]); } addr_t _get_valid_gadget_address(Gadget* gadget, Arch* arch, Constraint* constraint){ for( addr_t addr : gadget->addresses){ if( !constraint || constraint->bad_bytes.is_valid_address(addr, arch->octets)) return addr; } throw strategy_exception("Fatal error: couldn't get valid gadget address. This should not happen ! "); } bool Node::assign_gadget(Gadget* gadget, Arch* arch, Constraint* constraint){ addr_t addr; try{ addr = _get_valid_gadget_address(gadget, arch, constraint); }catch(strategy_exception& e){ return false; } affected_gadget = gadget; // Set gadget parameters depending on type (but dont change the name!) params[get_param_num_gadget_addr()].value = addr; params[get_param_num_gadget_sp_inc()].value = gadget->sp_inc; params[get_param_num_gadget_jmp_reg()].value = gadget->jmp_reg; params[get_param_num_gadget_sp_delta()].value = gadget->max_sp_inc - gadget->sp_inc; return true; } void Node::apply_assertion(){ assertion.clear(); node_assertion.to_assertion(*this, &assertion); } /* =============== Strategy Graphs ============== */ /* =============================================== */ StrategyGraph::StrategyGraph(): has_gadget_selection(false), _depth(-1){}; void StrategyGraph::update_size(){ size = 0; for( Node& node: nodes ) if( ! node.is_disabled ) size++; } /* =========== Basic manips on edges/nodes =========== */ node_t StrategyGraph::new_node(GadgetType t){ nodes.push_back(Node(nodes.size(), t)); // Give names to generic parameters nodes.back().params[nodes.back().get_param_num_gadget_addr()].make_cst( -1, new_name("gadget_addr")); nodes.back().params[nodes.back().get_param_num_gadget_sp_inc()].make_cst( -1, new_name("gadget_sp_inc")); nodes.back().params[nodes.back().get_param_num_gadget_jmp_reg()].make_cst( -1, new_name("gadget_jmp_reg")); nodes.back().params[nodes.back().get_param_num_gadget_sp_delta()].make_cst( -1, new_name("gadget_sp_delta")); return nodes.size()-1; } void StrategyGraph::disable_node(node_t node){ nodes[node].is_disabled = true; nodes[node].special_paddings.clear(); } string StrategyGraph::new_name(string base){ return name_generator.new_name(base); } // Make the dep that point to the parameter 'curr_param_type' on 'curr_node' point to 'new_node' // and 'new_param_type' instead. bool _redirect_param_dep(ParamDep& dep, node_t curr_node, param_t curr_param_type, node_t new_node, param_t new_param_type){ if( dep.node == curr_node && dep.param_type == curr_param_type ){ dep.node = new_node; dep.param_type = new_param_type; return true; }else return false; } void _redirect_param_deps(Param& param, Node& curr_node, param_t curr_param_type, Node& new_node, param_t new_param_type){ for( ParamDep& dep : param.deps ){ if( dep.node == curr_node.id && dep.param_type == curr_param_type ){ // Change var name in expression if constant if( param.type == ParamType::CST && param.expr != nullptr){ Expr e = param.expr->copy(); e->replace_var_name(curr_node.params[curr_param_type].name, new_node.params[new_param_type].name); param.expr = e; } // Redirect dep.node = new_node.id; dep.param_type = new_param_type; } } } // Make the edges that point to the parameter 'curr_param_type' on 'curr_node' point to 'new_node' // and 'new_param_type'. void StrategyGraph::redirect_param_edges(node_t curr_node, param_t curr_param_type, node_t new_node, param_t new_param_type){ for( node_t p = 0; p < nodes.size(); p++ ){ Node& prev = nodes[p]; // Redirect parameters for( int p = 0; p < prev.nb_params(); p++ ){ _redirect_param_deps(prev.params[p], nodes[curr_node], curr_param_type, nodes[new_node], new_param_type); } // Redirect special paddings for( ROPPadding& padd : prev.special_paddings ){ _redirect_param_deps(padd.offset, nodes[curr_node], curr_param_type, nodes[new_node], new_param_type); _redirect_param_deps(padd.value, nodes[curr_node], curr_param_type, nodes[new_node], new_param_type); } } } void StrategyGraph::redirect_incoming_strategy_edges(node_t curr_node, node_t new_node){ Node& curr = nodes[curr_node]; Node& newn = nodes[new_node]; for( node_t p : curr.strategy_edges.in ){ Node& prev = nodes[p]; if( std::count(prev.strategy_edges.out.begin(), prev.strategy_edges.out.end(), curr_node) > 0 ){ // Erase previous outgoing edges to curr prev.strategy_edges.out.erase(std::remove(prev.strategy_edges.out.begin(), prev.strategy_edges.out.end(), curr_node), prev.strategy_edges.out.end()); if( new_node != prev.id ){ // If new node depends on curr_node, don't redirect it to itself // Add new outgoing to new_node prev.add_outgoing_strategy_edge(new_node); // Add new incoming in new_node newn.add_incoming_strategy_edge(prev.id); } } } } void StrategyGraph::redirect_outgoing_strategy_edges(node_t curr_node, node_t new_node){ Node& curr = nodes[curr_node]; Node& newn = nodes[new_node]; for( node_t n : curr.strategy_edges.out ){ Node& next = nodes[n]; if( std::count(next.strategy_edges.in.begin(), next.strategy_edges.in.end(), curr_node) > 0 ){ // Erase next incoming edges from curr next.strategy_edges.in.erase(std::remove(next.strategy_edges.in.begin(), next.strategy_edges.in.end(), curr_node), next.strategy_edges.in.end()); if( new_node != next.id ){ // If new node depends on curr_node, don't redirect it to itself // Add new incoming from new_node next.add_incoming_strategy_edge(new_node); // Add new outgoing in new_node newn.add_outgoing_strategy_edge(next.id); } } } } void StrategyGraph::redirect_generic_param_edges(node_t curr_node, node_t new_node){ Node& curr = nodes[curr_node]; Node& newn = nodes[new_node]; redirect_param_edges(curr_node, curr.get_param_num_gadget_addr(), new_node, newn.get_param_num_gadget_addr()); redirect_param_edges(curr_node, curr.get_param_num_gadget_jmp_reg(), new_node, newn.get_param_num_gadget_jmp_reg()); redirect_param_edges(curr_node, curr.get_param_num_gadget_sp_inc(), new_node, newn.get_param_num_gadget_sp_inc()); redirect_param_edges(curr_node, curr.get_param_num_gadget_sp_delta(), new_node, newn.get_param_num_gadget_sp_delta()); } void StrategyGraph::add_strategy_edge(node_t from, node_t to){ nodes[from].add_outgoing_strategy_edge(to); nodes[to].add_incoming_strategy_edge(from); } void StrategyGraph::add_param_edge(node_t from, node_t to){ nodes[from].add_outgoing_param_edge(to); nodes[to].add_incoming_param_edge(from); } void StrategyGraph::add_interference_edge(node_t from, node_t to){ nodes[from].interference_edges.out.push_back(to); // We don't add an incoming edge in 'to' for interference edges // because they are not used } // Update all parameter edges according to params and paddings void StrategyGraph::update_param_edges(){ // First, clear all param edges for( Node& node : nodes ){ node.param_edges.in.clear(); node.param_edges.out.clear(); } // Check every couple of nodes for( int n1 = 0; n1 < nodes.size(); n1++ ){ Node& node1 = nodes[n1]; // Check params for( int p = 0; p < node1.nb_params(); p++){ Param& param = node1.params[p]; for( ParamDep& dep : param.deps ){ add_param_edge(n1, dep.node); } } // Check paddings for( ROPPadding& padd : node1.special_paddings){ for( ParamDep& dep : padd.offset.deps ){ if( dep.node != n1 ) add_param_edge(n1, dep.node); } for( ParamDep& dep : padd.value.deps ){ if( dep.node != n1 ) add_param_edge(n1, dep.node); } } } } void StrategyGraph::clear_interference_edges(node_t n){ nodes[n].interference_edges.out.clear(); } bool StrategyGraph::modifies_reg(node_t n, int reg_num, bool check_following_node){ bool res = nodes[n].modifies_reg(reg_num); if( check_following_node && nodes[n].mandatory_following_node != -1) return res || modifies_reg(nodes[n].mandatory_following_node, reg_num, true); else return res; } bool StrategyGraph::has_dependent_param(node_t n, param_t param){ for( node_t prev : nodes[n].param_edges.in ){ for( int p = 0; p < nodes[prev].nb_params(); p++ ){ for( ParamDep& dep : nodes[prev].params[p].deps){ if( dep.node == n && dep.param_type == param) return true; } } } return false; } /* =============== Ordering ============== */ void StrategyGraph::_dfs_strategy_explore(vector & marked, node_t n){ if( nodes[n].is_disabled || nodes[n].is_indirect || std::count(dfs_strategy.begin(), dfs_strategy.end(), n)) return; // Ignore disabled or indirect nodes if( std::count(marked.begin(), marked.end(), n) != 0 ){ throw runtime_exception("StrategyGraph: strategy DFS: unexpected cycle detected!"); }else{ marked.push_back(n); } for( node_t n2 : nodes[n].strategy_edges.out ){ _dfs_strategy_explore(marked, n2); } dfs_strategy.push_back(n); } void StrategyGraph::compute_dfs_strategy(){ vector marked; dfs_strategy.clear(); for( Node& node : nodes ){ if( node.is_disabled || (std::count(marked.begin(), marked.end(), node.id) != 0)) continue; else _dfs_strategy_explore(marked, node.id); } } void StrategyGraph::_dfs_params_explore(vector & marked, node_t n){ if( std::count(dfs_params.begin(), dfs_params.end(), n)) return; // Ignore already visited nodes // Note: we don't ignore disabled nodes because they can hold constants parameters // from which other nodes depend if( std::count(marked.begin(), marked.end(), n) != 0 ){ throw runtime_exception("StrategyGraph: params DFS: unexpected cycle detected!"); }else{ marked.push_back(n); } for( node_t n2 : nodes[n].param_edges.out ){ _dfs_params_explore(marked, n2); } marked.pop_back(); // Unmark the node for the current exploration dfs_params.push_back(n); } void StrategyGraph::compute_dfs_params(){ vector marked; dfs_params.clear(); for( Node& node : nodes ){ if( node.is_disabled || (std::count(marked.begin(), marked.end(), node.id) != 0)) continue; else _dfs_params_explore(marked, node.id); } } // Returns false <=> the graph contains a cycle bool StrategyGraph::_dfs_scheduling_explore(vector & marked, node_t n){ if( nodes[n].is_disabled || std::count(dfs_scheduling.begin(), dfs_scheduling.end(), n)) return true; // Ignore disabled or indirect nodes or already visited ones if( std::count(marked.begin(), marked.end(), n) != 0 ){ // Cycle detected ! return false; }else{ marked.push_back(n); } for( node_t n2 : nodes[n].strategy_edges.out ){ if( n2 == nodes[n].mandatory_following_node ) continue; if( ! _dfs_scheduling_explore(marked, n2)) return false; } for( node_t n2 : nodes[n].interference_edges.out){ if( n2 == nodes[n].mandatory_following_node ) continue; if( ! _dfs_scheduling_explore(marked, n2)) return false; } // Do mandatory node in the end if any if( nodes[n].mandatory_following_node != -1 ){ if( ! _dfs_scheduling_explore(marked, nodes[n].mandatory_following_node)) return false; } dfs_scheduling.push_back(n); return true; } bool StrategyGraph::compute_dfs_scheduling(){ vector marked; dfs_scheduling.clear(); for( Node& node : nodes ){ if( node.is_disabled || node.is_indirect || (std::count(marked.begin(), marked.end(), node.id) != 0)){ continue; }else{ if( ! _dfs_scheduling_explore(marked, node.id) ){ return false; // Cycle detected } } } return true; } /* =============== Gadget Selection ============== */ // Get the concrete value for parameters depending on other // gadgets. This functions expects all the parameters in nodes that // are used by the 'param' argument to have been resolved already void StrategyGraph::_resolve_param(Param& param){ if( param.is_dependent()){ if( param.type == ParamType::REG ){ param.value = nodes[param.deps[0].node].params[param.deps[0].param_type].value; }else if( param.type == ParamType::CST){ if( param.expr == nullptr ){ // If not expr, just take the value of the other param param.value = nodes[param.deps[0].node].params[param.deps[0].param_type].value; }else{ param.value = param.expr->concretize(¶ms_ctx); } }else{ throw runtime_exception("_resolve_param(): got unsupported param type"); } } // If constant, update the context if( param.type == ParamType::CST){ params_ctx.set(param.name, param.value); } } void StrategyGraph::_resolve_all_params(node_t n){ Node& node = nodes[n]; // Resolve normal parameters for( int p = 0; p < node.nb_params(); p++){ _resolve_param(node.params[p]); } // Resolve special paddings for( ROPPadding& padd : node.special_paddings ){ _resolve_param(padd.offset); _resolve_param(padd.value); } } // Wrapper that queries the database to find the list of gadgets that match // a strategy node const vector & StrategyGraph::_get_matching_gadgets(GadgetDB& db, node_t n){ Node& node = nodes[n]; reg_t src_reg, src_reg2, dst_reg, dst_addr_reg, src_addr_reg; cst_t src_cst, src_addr_cst, dst_addr_cst; Op src_op, op; // resolve parameters for node 'n' _resolve_all_params(n); switch( node.type ){ // make query case GadgetType::MOV_REG: src_reg = node.params[PARAM_MOVREG_SRC_REG].value; dst_reg = node.params[PARAM_MOVREG_DST_REG].value; return db.get_mov_reg(dst_reg, src_reg); case GadgetType::MOV_CST: dst_reg = node.params[PARAM_MOVCST_DST_REG].value; src_cst = node.params[PARAM_MOVCST_SRC_CST].value; return db.get_mov_cst(dst_reg, src_cst); case GadgetType::AMOV_CST: dst_reg = node.params[PARAM_AMOVCST_DST_REG].value; src_reg = node.params[PARAM_AMOVCST_SRC_REG].value; src_op = (Op)node.params[PARAM_AMOVCST_SRC_OP].value; src_cst = node.params[PARAM_AMOVCST_SRC_CST].value; return db.get_amov_cst(dst_reg, src_reg, src_op, src_cst); case GadgetType::AMOV_REG: dst_reg = node.params[PARAM_AMOVREG_DST_REG].value; src_reg = node.params[PARAM_AMOVREG_SRC_REG1].value; src_op = (Op)node.params[PARAM_AMOVREG_SRC_OP].value; src_reg2 = node.params[PARAM_AMOVREG_SRC_REG2].value; return db.get_amov_reg(dst_reg, src_reg, src_op, src_reg2); case GadgetType::LOAD: dst_reg = node.params[PARAM_LOAD_DST_REG].value; src_addr_reg = node.params[PARAM_LOAD_SRC_ADDR_REG].value; src_addr_cst = node.params[PARAM_LOAD_SRC_ADDR_OFFSET].value; return db.get_load(dst_reg, src_addr_reg, src_addr_cst); case GadgetType::ALOAD: dst_reg = node.params[PARAM_ALOAD_DST_REG].value; op = (Op)node.params[PARAM_ALOAD_OP].value; src_addr_reg = node.params[PARAM_ALOAD_SRC_ADDR_REG].value; src_addr_cst = node.params[PARAM_ALOAD_SRC_ADDR_OFFSET].value; return db.get_aload(dst_reg, op, src_addr_reg, src_addr_cst); case GadgetType::STORE: dst_addr_reg = node.params[PARAM_STORE_DST_ADDR_REG].value; dst_addr_cst = node.params[PARAM_STORE_DST_ADDR_OFFSET].value; src_reg = node.params[PARAM_STORE_SRC_REG].value; return db.get_store(dst_addr_reg, dst_addr_cst, src_reg); case GadgetType::ASTORE: dst_addr_reg = node.params[PARAM_ASTORE_DST_ADDR_REG].value; dst_addr_cst = node.params[PARAM_ASTORE_DST_ADDR_OFFSET].value; op = (Op)node.params[PARAM_ASTORE_OP].value; src_reg = node.params[PARAM_ASTORE_SRC_REG].value; return db.get_astore(dst_addr_reg, dst_addr_cst, op, src_reg); case GadgetType::SYSCALL: return db.get_syscall(); case GadgetType::INT80: return db.get_int80(); default: throw runtime_exception(QuickFmt() << "_get_matching_gadgets(): got unsupported node type " << (int)node.type >> QuickFmt::to_str); } } // Wrapper to the database to get a list of gadgets that match a strategy node // that still has non-resolved (also called 'free') parameters. // // For example it can find all gadgets that match a node: X = ecx + Y // and return : // - mov edx, ecx // - add ecx, esi // ... PossibleGadgets* StrategyGraph::_get_possible_gadgets(GadgetDB& db, node_t n){ Node& node = nodes[n]; bool params_status[MAX_PARAMS]; int p; // resolve parameters for node 'n' _resolve_all_params(n); // Fill a table with parameters status (free or not) for( p = 0; p < node.nb_params(); p++){ params_status[p] = node.params[p].is_free(); } // Make the query to the db switch( node.type ){ case GadgetType::MOV_REG: return db.get_possible_mov_reg(node.params[PARAM_MOVREG_DST_REG].value, node.params[PARAM_MOVREG_SRC_REG].value, params_status); case GadgetType::AMOV_REG: return db.get_possible_amov_reg(node.params[PARAM_AMOVREG_DST_REG].value, node.params[PARAM_AMOVREG_SRC_REG1].value, (Op)node.params[PARAM_AMOVREG_SRC_OP].value, node.params[PARAM_AMOVREG_SRC_REG2].value, params_status); case GadgetType::MOV_CST: return db.get_possible_mov_cst(node.params[PARAM_MOVCST_DST_REG].value, node.params[PARAM_MOVCST_SRC_CST].value, params_status); case GadgetType::AMOV_CST: return db.get_possible_amov_cst(node.params[PARAM_AMOVCST_DST_REG].value, node.params[PARAM_AMOVCST_SRC_REG].value, (Op)node.params[PARAM_AMOVCST_SRC_OP].value, node.params[PARAM_AMOVCST_SRC_CST].value, params_status); case GadgetType::LOAD: return db.get_possible_load(node.params[PARAM_LOAD_DST_REG].value, node.params[PARAM_LOAD_SRC_ADDR_REG].value, node.params[PARAM_LOAD_SRC_ADDR_OFFSET].value, params_status); case GadgetType::ALOAD: return db.get_possible_aload(node.params[PARAM_ALOAD_DST_REG].value, (Op)node.params[PARAM_ALOAD_OP].value, node.params[PARAM_ALOAD_SRC_ADDR_REG].value, node.params[PARAM_ALOAD_SRC_ADDR_OFFSET].value, params_status); case GadgetType::STORE: return db.get_possible_store(node.params[PARAM_STORE_DST_ADDR_REG].value, node.params[PARAM_STORE_DST_ADDR_OFFSET].value, node.params[PARAM_STORE_SRC_REG].value, params_status); case GadgetType::ASTORE: return db.get_possible_astore(node.params[PARAM_ASTORE_DST_ADDR_REG].value, node.params[PARAM_ASTORE_DST_ADDR_OFFSET].value, (Op)node.params[PARAM_ASTORE_OP].value, node.params[PARAM_ASTORE_SRC_REG].value, params_status); default: throw runtime_exception("_get_possible_gadgets(): got unsupported gadget type!"); } } // Must be checked after parameter resolution bool StrategyGraph::_check_strategy_constraints(Node& node, Arch* arch){ for( constraint_callback_t constr : node.strategy_constraints ){ if( ! constr(&node, this, arch)) return false; } return true; } // Must be checked after parameter resolution (padding resolution more precisely) bool StrategyGraph::_check_special_padding_constraints(Node& node, Arch* arch, Constraint* constraint){ if( !constraint ) return true; for( ROPPadding& padd: node.special_paddings ){ if( !constraint->bad_bytes.is_valid_address(padd.value.value, arch->octets)) return false; } return true; } // Must be checked after gadget assignment bool StrategyGraph::_check_assigned_gadget_constraints(Node& node, Arch* arch){ for( constraint_callback_t constr : node.assigned_gadget_constraints ){ if( ! constr(&node, this, arch)) return false; } return true; } /* This function tries to find a gadget selection for a strategy graph. It iteratively (the order is the one of the DFS on parameter dependencies) resolves parameters and queries the database to find a matching gadget on each node of the strategy graph. */ bool StrategyGraph::select_gadgets(GadgetDB& db, Constraint* constraint, Arch* arch, int dfs_idx){ // Check if constraint is specified with an architecture if( constraint && !arch){ throw runtime_exception("StrategyGraph::select_gadget(): should NEVER be called with a non-NULL constraint and a NULL arch"); } // Check if SIGINT if( is_pending_sigint()){ return false; } // Otherwise do proper gadget selection : // If root call if( dfs_idx == -1 ){ compute_dfs_params(); compute_dfs_strategy(); params_ctx = VarContext(); // New context for params has_gadget_selection = select_gadgets(db, constraint, arch, 0); return has_gadget_selection; } if( dfs_idx >= dfs_params.size()){ return schedule_gadgets(); } node_t n = dfs_params[dfs_idx]; Node& node = nodes[n]; // If the node is a disabled node, juste resolve the parameters // and continue the selection if( node.is_disabled){ _resolve_all_params(n); // Continue to select from next node if( select_gadgets(db, constraint, arch, dfs_idx+1) ) return true; else return false; } // 1. Try all possibilities for parameters if( node.has_free_param() ){ // Get possible gadgets PossibleGadgets* possible = _get_possible_gadgets(db, node.id); // 2.a. Try all possible params for( auto pos: possible->gadgets ){ // Update free params for( int p = 0; p < node.nb_params(); p++){ if( node.params[p].is_free()) node.params[p].value = pos.first[p]; if( node.params[p].is_cst()) params_ctx.set(node.params[p].name, node.params[p].value); } // Resolve params again (useful for special paddings that depend // on regular parameters such as offsets, etc) _resolve_all_params(node.id); // Check strategy constraints if( !_check_strategy_constraints(node, arch) || !_check_special_padding_constraints(node, arch, constraint)){ continue; } // Prepare assertion for current parameter choice node.apply_assertion(); // 2.b Try all possible gadgets for( Gadget* gadget : *(pos.second) ){ if( ! node.assign_gadget(gadget, arch, constraint)) continue; // Resolve params once again (useful for special paddings that depend // on gadget specific parameters such as gadget_addr, gadget_sp_inc, etc) _resolve_all_params(node.id); // Check assigned gadget constraints and global constraint if( !_check_assigned_gadget_constraints(node, arch) || (constraint && !constraint->check(gadget, arch, &node.assertion))){ continue; } // 3. Recursive call on next node if( select_gadgets(db, constraint, arch, dfs_idx+1)){ delete possible; possible = nullptr; return true; } } } delete possible; possible = nullptr; }else{ // Check strategy constraints if( _check_strategy_constraints(node, arch)){ // Get matching gadgets const vector & gadgets = _get_matching_gadgets(db, node.id); // 2. Try all possible gadgets (or a subset) for( Gadget* gadget : gadgets ){ if( ! node.assign_gadget(gadget, arch, constraint)) continue; // Resolve params again (useful for special paddings that depend // on regular parameters such as offsets, etc) _resolve_all_params(node.id); // Check if paddings have valid values (no bad bytes) if( !_check_special_padding_constraints(node, arch, constraint)) continue; // Prepare assertion for current parameter choice node.apply_assertion(); // Check assigned gadget constraints and global constraint if( !_check_assigned_gadget_constraints(node, arch) || (constraint && !constraint->check(gadget, arch, &node.assertion))){ continue; } // 3. Recursive call on next node if( select_gadgets(db, constraint, arch, dfs_idx+1) ){ return true; } } } } return false; } /* ==================== Scheduling ======================= */ void StrategyGraph::compute_interference_points(){ // Clear previous points if any interference_points.clear(); // 1. Compute interfering points for regs for( Node& node : nodes ){ if( node.is_disabled ) // Allow indirect nodes though since they will be executed and interfere continue; for( int p = 0; p < node.nb_params(); p++ ){ Param& param = node.params[p]; if( ! param.is_data_link ) continue; // Check if this link is modified by another node (other) for( Node& other : nodes ){ // If disabled, indirect, or one part of the data link, ignore if( other.is_disabled || param.depends_on(other.id) || other.id == node.id) continue; if( modifies_reg(other.id, param.value, true)){ // True to check also mandatory_following_gadgets // Add interfering point if( node.is_initial_param(p) && !has_dependent_param(node.id, p)){ // If the param is initial (input in the chain), other has to be after (can never be before) interference_points.push_back(InterferencePoint(other.id, -1, node.id)); }else if( node.is_final_param(p)){ // If the param is final (an output of the chain), other must be before interference_points.push_back(InterferencePoint(other.id, node.id, -1)); }else{ // Add the first param dependency (since it is for regs we assume there's only one dependency) interference_points.push_back(InterferencePoint(other.id, node.id, param.deps[0].node)); } } } } } } bool StrategyGraph::_do_scheduling(int interference_idx){ bool success = false; if( interference_idx == interference_points.size() ){ // All choices where made for interference edges, try to schedule return compute_dfs_scheduling(); }else{ // Need to make a choice InterferencePoint& inter = interference_points[interference_idx]; // Choice 1, put it BEFORE if( inter.start_node != -1 ){ EdgeSet saved_edges = nodes[inter.interfering_node].interference_edges; // Save current edges state add_interference_edge(inter.interfering_node, inter.start_node); if( inter.end_node != -1 ){ add_interference_edge(inter.interfering_node, inter.end_node); } if( _do_scheduling(interference_idx+1) ){ success = true; } nodes[inter.interfering_node].interference_edges = saved_edges; // Restore edges state if( success ) return true; } if( inter.end_node != -1 ){ EdgeSet saved_start_edges; EdgeSet saved_end_edges = nodes[inter.end_node].interference_edges; // Save current edges state // Choice 2, put if AFTER if( inter.start_node != -1 ){ saved_start_edges = nodes[inter.start_node].interference_edges; // Save current edges state add_interference_edge(inter.start_node, inter.interfering_node); } add_interference_edge( inter.end_node, inter.interfering_node); if( _do_scheduling( interference_idx+1 ) ){ success = true; } if( inter.start_node != -1 ){ nodes[inter.start_node].interference_edges = saved_start_edges; // Restore interference edges } nodes[inter.end_node].interference_edges = saved_end_edges; // Restore interference edges } return success; } } bool StrategyGraph::schedule_gadgets(){ bool success = false; // Compute inteference points compute_interference_points(); // Go through all interference points and try both possibilities // (interfering gadget goes BEFORE or AFTER both linked nodes) success = _do_scheduling(); // Clean-up interference_points.clear(); // Return return success; } /* Function that builds a ROPChain from a valid gadget selection ==> If no valid selection has been computed for the graph, it returns a NULL pointer */ ROPChain* StrategyGraph::get_ropchain(Arch* arch, Constraint* constraint){ vector ::reverse_iterator rit; cst_t default_padding; ROPPadding padding; int padding_num = -1; // Check if there is a selection in the nodes if( !has_gadget_selection ){ return nullptr; } // Get default padding (validate against bad_bytes if constraint specified) default_padding = constraint ? constraint->bad_bytes.get_valid_padding(arch->octets) : cst_sign_trunc(arch->bits, -1); ROPChain* ropchain = new ROPChain(arch); for( rit = dfs_scheduling.rbegin(); rit != dfs_scheduling.rend(); rit++ ){ Node& node = nodes[*rit]; if( node.is_indirect ){ continue; // Skip indirect nodes } // Add gadget ropchain->add_gadget(node.params[node.get_param_num_gadget_addr()].value, node.affected_gadget); // Order paddings by offset std::sort(nodes[*rit].special_paddings.begin(), nodes[*rit].special_paddings.end(), [](const ROPPadding& padd1, const ROPPadding& padd2){ return padd1.offset.value < padd2.offset.value; }); // Init padding iterator if( !node.special_paddings.empty()){ padding = node.special_paddings[0]; padding_num = 0; } // Get number of paddings depending on sp_inc int nb_paddings = node.affected_gadget->sp_inc / arch->octets; if( node.affected_gadget->branch_type == BranchType::RET ){ nb_paddings--; } // Check number of paddings according to special paddings if( !node.special_paddings.empty() && (node.special_paddings.back().offset.value/arch->octets)+1 > nb_paddings){ nb_paddings = (node.special_paddings.back().offset.value/arch->octets)+1; } for( int offset = 0; offset < nb_paddings*arch->octets; offset += arch->octets){ // If special padding if( padding_num != -1 && padding.offset.value == offset ){ // If the padding is a gadget address (indirect gadget), add a info msg string msg = ""; if( padding.value.is_dependent() && padding.value.deps[0].param_type == nodes[padding.value.deps[0].node].get_param_num_gadget_addr()){ msg = nodes[padding.value.deps[0].node].affected_gadget->asm_str; ropchain->add_gadget_address(cst_sign_trunc(arch->bits, padding.value.value), msg); }else{ ropchain->add_padding(cst_sign_trunc(arch->bits, padding.value.value), msg); } // Step to next special padding (if any) if( padding_num == node.special_paddings.size()-1 ){ // No more special paddings padding_num = -1; }else{ // Next special padding padding = nodes[*rit].special_paddings[++padding_num]; } } // Else default padding else{ ropchain->add_padding(default_padding); } } } return ropchain; } StrategyGraph* StrategyGraph::copy(){ StrategyGraph* new_graph = new StrategyGraph(); // Copy nodes new_graph->nodes = nodes; // Copy name generator (to avoid create new names for 0 that colision with previous ones) new_graph->name_generator = name_generator; new_graph->_history = _history; return new_graph; } /* ================ Printing =================== */ ostream& operator<<(ostream& os, Param& param){ string tab = "\t"; os << tab << "Param:" << std::endl; os << tab << "\t Value: " << std::dec << param.value << std::endl; os << tab << "\t Fixed?: " << param.is_fixed << std::endl; os << tab << "\t Is data link?: " << param.is_data_link << std::endl; os << tab << "\t Depends on : " << std::endl; for( ParamDep& dep : param.deps){ os << tab << "\t\t Node: " << dep.node << " Param: " << dep.param_type << std::endl; } if( param.expr != nullptr ) os << tab << "\t Expr: " << param.expr << std::endl; if( !param.name.empty()) os << tab << "\t Name: " << param.name << std::endl; return os; } ostream& operator<<(ostream& os, Node& node){ os << "Node " << std::dec << node.id << ":"; if( node.is_disabled ) os << " ( disabled ) "; if( node.is_indirect ) os << " ( indirect ) "; if( node.affected_gadget != nullptr ) os << "\n\tAffected gadget: " << node.affected_gadget->asm_str; os << "\n\tGadget type: " << (int)node.type; os << "\n\tBranch type: " << (int)node.branch_type; os << "\n\tIncoming strategy edges: "; for( node_t n : node.strategy_edges.in ) os << n << " "; os << "\n\tOutgoing strategy edges: "; for( node_t n : node.strategy_edges.out ) os << n << " "; os << "\n\tIncoming param edges: "; for( node_t n : node.param_edges.in ) os << n << " "; os << "\n\tOutgoing param edges: "; for( node_t n : node.param_edges.out ) os << n << " "; os << "\n\tParams: \n"; for( int p = 0; p < node.nb_params(); p++){ os << node.params[p] << std::endl; } os << "\n\tSpecial paddings: \n"; for( ROPPadding& padding : node.special_paddings){ os << "offset: " << padding.offset << ", value: " << padding.value << std::endl; } os << std::endl; return os; } ostream& operator<<(ostream& os, StrategyGraph& graph){ os << "STRATEGY GRAPH\n=============="; os << "\n\t History: " << graph._history; os << "\n\tDFS strategy: "; for( node_t n : graph.dfs_strategy ){ os << n << " "; } os << "\n\tDFS params: "; for( node_t n : graph.dfs_params ){ os << n << " "; } os << std::endl; for( Node& n : graph.nodes ){ os << n; } return os; } ================================================ FILE: libropium/compiler/strategy_rules.cpp ================================================ #include "strategy.hpp" #include "expression.hpp" #include "exception.hpp" #include /* =============== Strategy Rules ============== */ /* MovXXX dst_reg, src_xxx * ======================= * (n1) MovReg R1, src_xxx * (n2) MovReg dst_reg, R1 * ======================= */ bool StrategyGraph::rule_generic_transitivity(node_t n){ int i = 0; if( nodes[n].type != GadgetType::MOV_CST && nodes[n].type != GadgetType::MOV_REG && nodes[n].type != GadgetType::AMOV_CST && nodes[n].type != GadgetType::AMOV_REG && nodes[n].type != GadgetType::LOAD && nodes[n].type != GadgetType::ALOAD ){ return false; } // Get/Create nodes node_t n1 = new_node(nodes[n].type); node_t n2 = new_node(GadgetType::MOV_REG); Node& node = nodes[n]; Node& node1 = nodes[n1]; Node& node2 = nodes[n2]; node1 = node; // Copy node to node1 node1.id = n1; // But keep id // Modify dst_reg node1.params[node1.get_param_num_dst_reg()].make_reg(node2.id, PARAM_MOVREG_SRC_REG); // Set node2 with the reg transitivity gadget node2.params[PARAM_MOVREG_SRC_REG].make_reg(-1, false); // free reg node2.params[PARAM_MOVREG_DST_REG] = node.params[node.get_param_num_dst_reg()]; // Same dst reg as initial query in node // Add data link between node 1 and 2 for the transitive reg node1.params[node1.get_param_num_dst_reg()].is_data_link = true; // Node1 must end with a ret node1.branch_type = BranchType::RET; // Node2 same as node node2.branch_type = node.branch_type; // Redirect dst reg (to node2) (and datalink of course) redirect_param_edges(node.id, node.get_param_num_dst_reg(), node2.id, node2.get_param_num_dst_reg()); redirect_param_edges(node.id, node.get_param_num_data_link(), node2.id, node2.get_param_num_data_link()); redirect_param_edges(node.id, node.get_param_num_gadget_jmp_reg(), node2.id, node2.get_param_num_gadget_jmp_reg()); // If JMP redirect_param_edges(node.id, node.get_param_num_gadget_sp_inc(), node2.id, node2.get_param_num_gadget_sp_inc()); // If JMP redirect_param_edges(node.id, node.get_param_num_gadget_sp_delta(), node2.id, node2.get_param_num_gadget_sp_delta()); // If JMP // Redirect other input params arcs from node to node1 for( i = 0; i < MAX_PARAMS; i++){ redirect_param_edges(node.id, i, node1.id, i); } // Update param edges update_param_edges(); // Redirect/add strategy edges add_strategy_edge(node1.id, node2.id); redirect_incoming_strategy_edges(node.id, node1.id); redirect_outgoing_strategy_edges(node.id, node2.id); // Disable previous node disable_node(node.id); // Update size in the end update_size(); // Update graph history stringstream ss; ss << _history << "generic_transitivity(" << std::dec << n << ")" << std::endl; _history = ss.str(); return true; } /* MovCst dst_reg, src_cst + * ======================= + * Load dst_reg, mem(SP + off) + * padding(off, src_cst) + * ======================= */ bool StrategyGraph::rule_mov_cst_pop(node_t n, Arch* arch){ if( nodes[n].type != GadgetType::MOV_CST ){ return false; } if( nodes[n].branch_type == BranchType::JMP ){ return false; } // Get/Create nodes node_t n1 = new_node(GadgetType::LOAD); Node& node = nodes[n]; Node& node1 = nodes[n1]; // Node1 must have same return type than node node1.branch_type = node.branch_type; // Node1 must have same special paddings than node node1.special_paddings = node.special_paddings; // Modify parameters node1.params[PARAM_LOAD_DST_REG] = node.params[PARAM_MOVCST_DST_REG]; node1.params[PARAM_LOAD_SRC_ADDR_REG].make_reg(arch->sp()); node1.params[PARAM_LOAD_SRC_ADDR_OFFSET].make_cst(-1, new_name("stack_offset"), false); // Free offset // Set special padding at SP offset to put the constant node1.special_paddings.push_back(ROPPadding()); // Offset is the offset at which we pop the constant node1.special_paddings.back().offset.make_cst(n1, PARAM_LOAD_SRC_ADDR_OFFSET, exprvar(arch->bits, node1.params[PARAM_LOAD_SRC_ADDR_OFFSET].name), new_name("padding_offset")); // Padding value is just the constant node1.special_paddings.back().value = node.params[PARAM_MOVCST_SRC_CST]; node1.special_paddings.back().value.name = new_name("padding_value"); // Get a new name for the value parameter // Add a constraint: the offset of the pop must not be too big (max 240) node1.strategy_constraints.push_back( [](Node* node, StrategyGraph* graph, Arch* arch){ return node->params[PARAM_LOAD_SRC_ADDR_OFFSET].value < 240 && node->params[PARAM_LOAD_SRC_ADDR_OFFSET].value >= 0; } ); // Add a constraint: the offset of the pop must not be bigger than the sp inc // and if the gadget is RET then the pop must not correspond to the return address ! node1.assigned_gadget_constraints.push_back( [](Node* node, StrategyGraph* graph, Arch* arch){ int adjust=0; // Padding can't overlap return address unless the register we want // to set is PC ;) if( node->affected_gadget->branch_type == BranchType::RET && node->params[PARAM_LOAD_DST_REG].value != arch->pc()){ adjust = arch->octets; // } return node->params[PARAM_LOAD_SRC_ADDR_OFFSET].value < node->affected_gadget->sp_inc - adjust; } ); // Redirect the different params and edges // Generic params redirect_generic_param_edges(node.id, node1.id); redirect_param_edges(node.id, node.get_param_num_data_link(), node1.id, node1.get_param_num_data_link()); // Redirect strategy edges redirect_incoming_strategy_edges(node.id, node1.id); redirect_outgoing_strategy_edges(node.id, node1.id); // Update param edges update_param_edges(); // Disable node disable_node(node.id); // Update size in the end update_size(); // Update graph history stringstream ss; ss << _history << "mov_cst_pop(" << std::dec << n << ")" << std::endl; _history = ss.str(); return true; } /* ; ret * ==================== * R1 <- @(next gadget) * ; jmp R1; */ bool StrategyGraph::rule_generic_adjust_jmp(node_t n, Arch* arch){ // Only apply on "RET" nodes (and "ANY" by extension) if( nodes[n].branch_type != BranchType::RET && nodes[n].branch_type != BranchType::ANY ){ return false; } // Get/Create nodes node_t n1 = new_node(GadgetType::MOV_CST); // Node to adjust the jmp reg node_t n_ret = new_node(GadgetType::LOAD); // Node of the 'adjust gadget' (ret N basically) Node& node = nodes[n]; Node& node1 = nodes[n1]; Node& node_ret = nodes[n_ret]; // Change return type to JMP in node node.branch_type = BranchType::JMP; // Node MUST be followed by the indirect gadget ;) node.mandatory_following_node = node_ret.id; // Node1 (set the jmp reg) MUST be a RET one node1.branch_type = BranchType::RET; // Set the 'adjust gadget' node. It must adjust the PC that the next value on the // stack after the 'jmp' gadget is executed node_ret.params[PARAM_LOAD_DST_REG].make_reg(arch->pc()); // Dest reg is PC node_ret.params[PARAM_LOAD_SRC_ADDR_REG].make_reg(arch->sp()); // addr reg is SP (pop from the stack) node_ret.params[PARAM_LOAD_SRC_ADDR_OFFSET].make_cst( n, node.get_param_num_gadget_sp_delta(), nullptr, new_name("adjust_jmp_offset")); node_ret.is_indirect = true; // This node is 'indirect' (gadget not added explicitely on the stack) // Set the 'pre-jmp' gadget. It sets the jmp reg to the address of the 'adjust gadget'. // Dest reg of node1 is the jmp reg of node node1.params[PARAM_MOVCST_DST_REG].make_reg(n, node.get_param_num_gadget_jmp_reg()); // Src cst of node1 is the address of the adjust gadget node1.params[PARAM_MOVCST_SRC_CST].make_cst(node_ret.id, node_ret.get_param_num_gadget_addr(), nullptr, new_name("adjust_jmp_addr")); // Add data link between node1 and node (the jmp reg must NOT be clobbered after it was set to // point to the adjust gadget node1.params[PARAM_MOVCST_DST_REG].is_data_link = true; // Redirect strategy edges redirect_incoming_strategy_edges(node.id, node1.id); add_strategy_edge(node1.id, node.id); // Add after so it's not redirected ;) // Add callback that checks that the jmp reg is not implied in the operation // for example if the gadget is mov eax,ebx; jmp ebx; and we adjust ebx then // the semantics are corrupted because ebx's value will be overwritten with // the address of the 'adjust-gadget' node.assigned_gadget_constraints.push_back( [](Node* node, StrategyGraph* graph, Arch* arch){ switch( node->type ){ case GadgetType::MOV_CST: return node->params[node->get_param_num_gadget_jmp_reg()].value != node->params[PARAM_MOVCST_DST_REG].value; case GadgetType::MOV_REG: return node->params[node->get_param_num_gadget_jmp_reg()].value != node->params[PARAM_MOVREG_DST_REG].value && node->params[node->get_param_num_gadget_jmp_reg()].value != node->params[PARAM_MOVREG_SRC_REG].value; case GadgetType::AMOV_CST: return node->params[node->get_param_num_gadget_jmp_reg()].value != node->params[PARAM_AMOVCST_DST_REG].value && node->params[node->get_param_num_gadget_jmp_reg()].value != node->params[PARAM_AMOVCST_SRC_REG].value; case GadgetType::AMOV_REG: return node->params[node->get_param_num_gadget_jmp_reg()].value != node->params[PARAM_AMOVREG_DST_REG].value && node->params[node->get_param_num_gadget_jmp_reg()].value != node->params[PARAM_AMOVREG_SRC_REG1].value && node->params[node->get_param_num_gadget_jmp_reg()].value != node->params[PARAM_AMOVREG_SRC_REG2].value; case GadgetType::LOAD: return node->params[node->get_param_num_gadget_jmp_reg()].value != node->params[PARAM_LOAD_DST_REG].value && node->params[node->get_param_num_gadget_jmp_reg()].value != node->params[PARAM_LOAD_SRC_ADDR_REG].value; case GadgetType::ALOAD: return node->params[node->get_param_num_gadget_jmp_reg()].value != node->params[PARAM_ALOAD_DST_REG].value && node->params[node->get_param_num_gadget_jmp_reg()].value != node->params[PARAM_ALOAD_SRC_ADDR_REG].value; case GadgetType::STORE: return node->params[node->get_param_num_gadget_jmp_reg()].value != node->params[PARAM_STORE_SRC_REG].value && node->params[node->get_param_num_gadget_jmp_reg()].value != node->params[PARAM_STORE_DST_ADDR_REG].value; case GadgetType::ASTORE: return node->params[node->get_param_num_gadget_jmp_reg()].value != node->params[PARAM_ASTORE_SRC_REG].value && node->params[node->get_param_num_gadget_jmp_reg()].value != node->params[PARAM_ASTORE_DST_ADDR_REG].value; default: throw runtime_exception("rule_generic_adjust_jmp(): constraint callback got unsupported GadgetType! "); } } ); // Update param edges update_param_edges(); // Update size in the end update_size(); // Update graph history stringstream ss; ss << _history << "generic_adjust_jmp(" << std::dec << n << ")" << std::endl; _history = ss.str(); return true; } /* dst_reg <-- mem(src_addr_reg + src_addr_offset) * ======================= * (n1) MovReg R1, src_addr_reg + (src_addr_offset - C1) * (n2) dst_reg <-- mem(R1 + C1) * ======================= */ bool StrategyGraph::rule_adjust_load(node_t n, Arch* arch){ if( nodes[n].type != GadgetType::LOAD && nodes[n].type != GadgetType::ALOAD ){ return false; } if( nodes[n].params[nodes[n].get_param_num_src_addr_reg()].value == arch->sp()){ // If we want to read from the stack pointer (typically to pop a value), don't // apply this strategy return false; } // Get/Create nodes node_t n1 = new_node(GadgetType::AMOV_CST); node_t n2 = new_node(nodes[n].type); Node& node = nodes[n]; Node& node1 = nodes[n1]; Node& node2 = nodes[n2]; node2 = node; // Copy node to node2 node2.id = n2; // But keep id // SET NODE 2 // Modify src_addr_reg to be any register node2.params[node2.get_param_num_src_addr_reg()].make_reg(-1, false); // free reg // Make the offset also free node2.params[node2.get_param_num_src_addr_offset()].make_cst(0, new_name("addr_offset"), false); // free cst // SET NODE 1 node1.params[PARAM_AMOVCST_SRC_OP].make_op(Op::ADD); // Set node1 with the right reg and cst node1.params[PARAM_AMOVCST_DST_REG].make_reg(node2.id,node2.get_param_num_src_addr_reg()); // depends on the load src addr reg node1.params[PARAM_AMOVCST_SRC_REG] = node.params[node.get_param_num_src_addr_reg()]; // Reg should be set with the same reg of the initial LOAD // Cst must be the original offset (of node) minus the new one (of node2) Param& node_offset = node.params[node.get_param_num_src_addr_offset()]; Param& node2_offset = node2.params[node2.get_param_num_src_addr_offset()]; Expr src_cst_expr = exprvar(arch->bits, node_offset.name) - exprvar(arch->bits, node2_offset.name); node1.params[PARAM_AMOVCST_SRC_CST].make_cst(node2.id, node2.get_param_num_src_addr_offset(), src_cst_expr, new_name("addr_offset")); node1.params[PARAM_AMOVCST_SRC_CST].add_dep(node.id, node.get_param_num_src_addr_offset()); // Add data link between node 1 and 2 for the address reg node1.params[PARAM_AMOVCST_DST_REG].is_data_link = true; // Node1 must end with a ret node1.branch_type = BranchType::RET; // Node2 same as node node2.branch_type = node.branch_type; // Redirect input params arcs from node to node1 redirect_param_edges(node.id, node.get_param_num_dst_reg(), node2.id, node2.get_param_num_dst_reg()); // Redirect data_link to node2 redirect_param_edges(node.id, node.get_param_num_data_link(), node2.id, node2.get_param_num_data_link()); // Redirect/add strategy edges add_strategy_edge(node1.id, node2.id); redirect_incoming_strategy_edges(node.id, node1.id); redirect_outgoing_strategy_edges(node.id, node2.id); // Update param edges update_param_edges(); // Disable previous node disable_node(node.id); // Update size in the end update_size(); // Update graph history stringstream ss; ss << _history << "adjust_load(" << std::dec << n << ")" << std::endl; _history = ss.str(); return true; } /* dst, src_reg * ======================= * (n2) MovReg R1, src_reg * (n1) dst, R1 * ======================= */ bool StrategyGraph::rule_generic_src_transitivity(node_t n){ if( nodes[n].type != GadgetType::STORE && nodes[n].type != GadgetType::ASTORE ){ return false; } // Don't apply this strategy if the src reg is already free no sense to add a // transitivy step if( nodes[n].params[nodes[n].get_param_num_src_reg()].is_free()){ return false; } // Get/Create nodes node_t n2 = new_node(GadgetType::MOV_REG); Node& node = nodes[n]; Node& node2 = nodes[n2]; // Redirect parameter to src_reg redirect_param_edges(node.id, node.get_param_num_src_reg(), node2.id, node2.get_param_num_src_reg()); // Set node2 with the reg transitivity gadget node2.params[PARAM_MOVREG_DST_REG].make_reg(node.id, node.get_param_num_src_reg()); // Depends on node src reg node2.params[PARAM_MOVREG_SRC_REG] = node.params[node.get_param_num_src_reg()]; // Same src reg as initial query in node // Add data link between node 1 and 2 for the transitive reg node2.params[PARAM_MOVREG_DST_REG].is_data_link = true; // Node2 must end in ret node2.branch_type = BranchType::RET; // Modify src_reg to make it free node.params[node.get_param_num_src_reg()].make_reg(-1, false); // Free // Update param edges update_param_edges(); // Redirect/add strategy edges add_strategy_edge(node2.id, node.id); // Update size in the end update_size(); // Update graph history stringstream ss; ss << _history << "generic_src_transitivity(" << std::dec << n << ")" << std::endl; _history = ss.str(); return true; } /* mem(dst_addr_reg, dst_addr_offset) <-- src_reg * ======================= * (n1) MovReg R1, dst_addr_reg + (dst_addr_offset - C1) * (n2) mem(R1 + C1) <-- src_reg * ======================= */ bool StrategyGraph::rule_adjust_store(node_t n, Arch* arch){ if( nodes[n].type != GadgetType::STORE && nodes[n].type != GadgetType::ASTORE ){ return false; } // If we want to store at the stack pointer, don't apply this strategy if( nodes[n].params[nodes[n].get_param_num_dst_addr_reg()].value == arch->sp()){ return false; } // If the parameters are free then it doesn't make sense to adjust it if( nodes[n].params[nodes[n].get_param_num_dst_addr_reg()].is_free() && nodes[n].params[nodes[n].get_param_num_dst_addr_offset()].is_free()){ return false; } // Get/Create nodes node_t n1 = new_node(GadgetType::AMOV_CST); node_t n2 = new_node(nodes[n].type); Node& node = nodes[n]; Node& node1 = nodes[n1]; Node& node2 = nodes[n2]; // SET NODE 2 node2 = node; // Copy node to node2 node2.id = n2; // But keep id // Modify dst_addr_reg to be any register node2.params[node2.get_param_num_dst_addr_reg()].make_reg(-1, false); // free reg // Make the offset also free node2.params[node2.get_param_num_dst_addr_offset()].make_cst(0, new_name("addr_offset"), false); // free cst // SET NODE 1 node1.params[PARAM_AMOVCST_SRC_OP].make_op(Op::ADD); // Set node1 with the right reg and cst node1.params[PARAM_AMOVCST_DST_REG].make_reg(node2.id,node2.get_param_num_dst_addr_reg()); // depends on the store dst addr reg node1.params[PARAM_AMOVCST_SRC_REG] = node.params[node.get_param_num_dst_addr_reg()]; // Reg should be set with the same reg of the initial STORE // Cst must be the original offset (of node) minus the new one (of node2) Param& node_offset = node.params[node.get_param_num_dst_addr_offset()]; Param& node2_offset = node2.params[node2.get_param_num_dst_addr_offset()]; Expr src_cst_expr = exprvar(arch->bits, node_offset.name) - exprvar(arch->bits, node2_offset.name); node1.params[PARAM_AMOVCST_SRC_CST].make_cst(node2.id, node2.get_param_num_dst_addr_offset(), src_cst_expr, new_name("addr_offset")); node1.params[PARAM_AMOVCST_SRC_CST].add_dep(node.id, node.get_param_num_dst_addr_offset()); // Add data link between node 1 and 2 for the address reg node1.params[PARAM_AMOVCST_DST_REG].is_data_link = true; // Node1 must end with a ret node1.branch_type = BranchType::RET; // Node2 same as node node2.branch_type = node.branch_type; // Redirect input params arcs from node to node1 redirect_param_edges(node.id, node.get_param_num_dst_addr_reg(), node2.id, node2.get_param_num_dst_addr_reg()); redirect_param_edges(node.id, node.get_param_num_src_reg(), node2.id, node2.get_param_num_src_reg()); // Redirect data_link redirect_param_edges(node.id, node.get_param_num_data_link(), node2.id, node2.get_param_num_data_link()); // Redirect/add strategy edges add_strategy_edge(node1.id, node2.id); redirect_incoming_strategy_edges(node.id, node1.id); redirect_outgoing_strategy_edges(node.id, node2.id); // Update param edges update_param_edges(); // Disable previous node disable_node(node.id); // Update size in the end update_size(); // Update graph history stringstream ss; ss << _history << "adjust_store(" << std::dec << n << ")" << std::endl; _history = ss.str(); return true; } ================================================ FILE: libropium/compiler/systems.cpp ================================================ #include "systems.hpp" #include using std::vector; // Supported syscalls vector linux_x86_syscalls = { SyscallDef("exit", 1, 1), SyscallDef("fork", 2, 1), SyscallDef("read", 3, 3), SyscallDef("write", 4, 3), SyscallDef("open", 5, 3), SyscallDef("close", 6, 2), SyscallDef("waitpid", 7, 3), SyscallDef("creat", 8, 2), SyscallDef("link", 9, 2), SyscallDef("unlink", 10, 1), SyscallDef("execve", 11, 3), SyscallDef("chdir", 12, 1), SyscallDef("time", 13, 1), SyscallDef("mknod", 14, 3), SyscallDef("chmod", 15, 2), SyscallDef("lchown", 16, 2), SyscallDef("stat", 18, 2), SyscallDef("lseek", 19, 3), SyscallDef("getpid", 20, 0), SyscallDef("mount", 21, 3), SyscallDef("umount", 22, 1), SyscallDef("setuid", 23, 1), SyscallDef("getuid", 24, 0), SyscallDef("stime", 25, 1), SyscallDef("ptrace", 26, 4), SyscallDef("alarm", 27, 1), SyscallDef("pause", 29, 0), SyscallDef("access", 33, 2), SyscallDef("sync", 36, 0), SyscallDef("kill", 37, 2), SyscallDef("rename", 38, 2), SyscallDef("mkdir", 39, 2), SyscallDef("rmdir", 40, 1), SyscallDef("dup", 41, 1), SyscallDef("umount", 52, 2), SyscallDef("setpgid", 57, 2), SyscallDef("chroot", 61, 1), SyscallDef("sigaction", 67, 3), SyscallDef("symlink", 83, 2), SyscallDef("reboot", 88, 4), SyscallDef("mmap", 90, 6), SyscallDef("munmap", 91, 2), SyscallDef("uname", 109, 1), SyscallDef("mprotect", 125, 3), SyscallDef("sysctl", 149, 1), SyscallDef("setreuid", 203, 2), SyscallDef("setregid", 204, 2), SyscallDef("setuid", 213, 1), SyscallDef("setgid", 214, 1) }; vector linux_x64_syscalls = { SyscallDef("read", 0, 3), SyscallDef("write", 1, 3), SyscallDef("open", 2, 3), SyscallDef("close", 3, 2), SyscallDef("mmap", 9, 6), SyscallDef("mprotect", 10, 3), SyscallDef("munmap", 11, 2), SyscallDef("rt_sigaction", 14, 4), SyscallDef("rt_sigreturn", 15, 1), SyscallDef("access", 21, 2), SyscallDef("mremap", 25, 5), SyscallDef("pause", 34, 0), SyscallDef("alarm", 37, 1), SyscallDef("getpid", 39, 0), SyscallDef("connect", 42, 3), SyscallDef("accept", 43, 3), SyscallDef("sendto", 44, 5), SyscallDef("rcvfrom", 45, 5), SyscallDef("shutdown", 48, 2), SyscallDef("bind", 49, 3), SyscallDef("listen", 50, 2), SyscallDef("execve", 59, 3), SyscallDef("exit", 60, 1), SyscallDef("kill", 62, 2), SyscallDef("uname", 63, 1), SyscallDef("mkdir", 83, 2), SyscallDef("rmdir", 84, 1), SyscallDef("creat", 85, 2), SyscallDef("link", 86, 2), SyscallDef("unlink", 87, 1), SyscallDef("chmod", 90, 2), SyscallDef("chown", 92, 3), SyscallDef("ptrace", 101, 4), SyscallDef("getuid", 102, 0), SyscallDef("getgid", 104, 0), SyscallDef("setuid", 105, 1), SyscallDef("setgid", 106, 1), SyscallDef("setreuid", 113, 2), SyscallDef("setregid", 114, 2), SyscallDef("chroot", 161, 1), SyscallDef("mount", 165, 5), SyscallDef("umount2", 166, 2), SyscallDef("reboot", 169, 4) }; SyscallDef* get_syscall_def(ArchType arch, System sys, string name){ vector * list = nullptr; if( arch == ArchType::X86 ){ switch( sys ){ case System::LINUX: list = &linux_x86_syscalls; break; default: throw runtime_exception("get_syscall_def(): got unsupported system for arch X86"); } }else if( arch == ArchType::X64 ){ switch( sys ){ case System::LINUX: list = &linux_x64_syscalls; break; default: throw runtime_exception("get_syscall_def(): got unsupported system for arch X64"); } }else{ throw runtime_exception("get_syscall_def(): got unknown arch"); } // Find syscall in list for( SyscallDef& def : *list ){ if( def.name == name ) return &def; // Found } return nullptr; // Not found } ================================================ FILE: libropium/database/database.cpp ================================================ #include "database.hpp" #include "exception.hpp" #include int find_insert_index(vector & gadget_list, Gadget* gadget){ int count= gadget_list.size(); int first = 0; int curr; while(count > 0){ curr = first; curr += count/2; if( gadget_list.at(curr)->lthan(*gadget)){ first = curr+1; count -= count/2 + 1; }else{ count = count/2; } } return first; } int find_insert_index_possible_gadgets(PossibleGadgets* possible, Gadget* gadget){ int count= possible->gadgets.size(); int first = 0; int curr; while(count > 0){ curr = first; curr += count/2; if( possible->gadgets.at(curr).second->at(0)->lthan(*gadget)){ first = curr+1; count -= count/2 + 1; }else{ count = count/2; } } return first; } gadget_t GadgetDB::add(Gadget* gadget, Arch* arch){ Expr e, addr; // Add to global list gadget->id = all.size(); all.push_back(gadget); // Check semantics and classify gadget // 0. First check is special branch gadget such as syscall/int80 if( gadget->branch_type == BranchType::SYSCALL ){ syscall.add(0, gadget); }else if( gadget->branch_type == BranchType::INT80 ){ int80.add(0, gadget); } // 1. Register semantics for( int reg = 0; reg < gadget->semantics->regs->nb_vars(); reg++){ e = gadget->semantics->regs->get(reg); // JMP if( reg == arch->pc() && e->is_var()){ jmp.add(e->reg(), gadget); } // MOV_CST if( e->is_cst() ){ mov_cst.add(make_tuple(reg, e->cst()), gadget); } // MOV_REG else if( e->is_var() && !e->is_reg(reg) ){ mov_reg.add(make_tuple(reg, e->reg()), gadget); amov_cst.add(make_tuple(reg, e->reg(), (op_t)Op::ADD, 0), gadget); } // AMOV_CST else if( e->is_binop() && e->args[0]->is_cst() && e->args[1]->is_var() && op_is_symetric(e->op())){ amov_cst.add(make_tuple(reg, e->args[1]->reg(), (op_t)e->op(), e->args[0]->cst()), gadget); }else if( e->is_binop() && e->args[1]->is_cst() && e->args[0]->is_var()){ amov_cst.add(make_tuple(reg, e->args[0]->reg(), (op_t)e->op(), e->args[1]->cst()), gadget); } // AMOV_REG else if( e->is_binop() && e->args[0]->is_var() && e->args[1]->is_var()){ amov_reg.add(make_tuple(reg, e->args[0]->reg(), (op_t)e->op(), e->args[1]->reg()), gadget); if( op_is_symetric(e->op())){ amov_reg.add(make_tuple(reg, e->args[1]->reg(), (op_t)e->op(), e->args[0]->reg()), gadget); } } // LOAD else if( e->is_mem() && e->args[0]->is_var()){ load.add(make_tuple(reg, e->args[0]->reg(), 0), gadget); }else if( e->is_mem() && e->args[0]->is_binop(Op::ADD) && e->args[0]->args[0]->is_cst() && e->args[0]->args[1]->is_var()){ load.add(make_tuple(reg, e->args[0]->args[1]->reg(), e->args[0]->args[0]->cst()), gadget); } // ALOAD else if( e->is_binop() && e->args[1]->is_reg(reg) && e->args[0]->is_mem() && e->args[0]->args[0]->is_var()){ aload.add(make_tuple(reg, (op_t)e->op(), e->args[0]->args[0]->reg(), 0), gadget); }else if( e->is_binop() && e->args[1]->is_reg(reg) && e->args[0]->is_mem() && e->args[0]->args[0]->is_binop(Op::ADD) && e->args[0]->args[0]->args[0]->is_cst() && e->args[0]->args[0]->args[1]->is_var()){ aload.add(make_tuple(reg, (op_t)e->op(), e->args[0]->args[0]->args[1]->reg(), e->args[0]->args[0]->args[0]->cst()), gadget); } } // 2. Memory semantics for( auto write : gadget->semantics->mem->writes ){ addr = write.first; e = write.second; // STORE if( addr->is_var() && e->is_var()){ store.add(make_tuple(addr->reg(), 0, e->reg()), gadget); }else if( addr->is_binop(Op::ADD) && addr->args[0]->is_cst() && addr->args[1]->is_var() && e->is_var()){ store.add(make_tuple(addr->args[1]->reg(), addr->args[0]->cst(), e->reg()), gadget); } // ASTORE else if( e->is_binop() && e->args[0]->is_mem() && e->args[1]->is_var() && (addr->eq(e->args[0]->args[0]))){ if( addr->is_var() ){ astore.add(make_tuple(addr->reg(), 0, (op_t)e->op(), e->args[1]->reg()), gadget); }else if( addr->is_binop(Op::ADD) && addr->args[0]->is_cst() && addr->args[1]->is_var() ){ astore.add(make_tuple(addr->args[1]->reg(), addr->args[0]->cst(), (op_t)e->op(), e->args[1]->reg()), gadget); } } } return gadget->id; } int GadgetDB::analyse_raw_gadgets(vector & raw_gadgets, Arch* arch){ unordered_map ::iterator git; Gadget* gadget; Semantics* semantics; IRBlock* irblock; SymbolicEngine sym = SymbolicEngine(arch->type); Expr e; int nb_success = 0; for( auto raw: raw_gadgets ){ if( (git = seen.find(raw.raw)) != seen.end()){ // Already seen, just add a new address git->second->add_address(raw.addr); nb_success++; }else{ // New gadget gadget = new Gadget(); // Lift instructions try{ if( (irblock = arch->disasm->disasm_block(raw.addr, (code_t)raw.raw.c_str(), raw.raw.size())) == nullptr){ throw runtime_exception("disassembler returned null block"); } }catch(std::exception& e){ // std::cout << "DEBUG COULDN'T LIFT GADGET: " << e.what() << std::endl; delete gadget; continue; } // Get semantics try{ if( (semantics = sym.execute_block(irblock)) == nullptr ){ throw symbolic_exception("symbolic engine returned null semantics"); } }catch(symbolic_exception& e){ //std::cout << "DEBUG SYMBOLIC ERROR WHILE EXECUTING GADGET: " << irblock->name << " --> " << e.what() << std::endl; delete gadget; continue; delete irblock; irblock = nullptr; }catch(expression_exception& e){ //std::cout << "DEBUG EXPRESSION ERROR WHILE EXECUTING GADGET: " << irblock->name << " --> " << e.what() << std::endl; delete gadget; continue; delete irblock; irblock = nullptr; } semantics->simplify(); gadget->semantics = semantics; // Set nb instructions gadget->nb_instr = irblock->_nb_instr; gadget->nb_instr_ir = irblock->_nb_instr_ir; // Set dereferenced regs for( int i = 0; i < NB_REGS_MAX; i++){ gadget->dereferenced_regs[i] = irblock->dereferenced_regs[i]; } // Get sp increment if( !irblock->known_max_sp_inc ){ // std::cout << "DEBUG ERROR UNKNOWN MAX SP INC " << irblock->name << std::endl; // Might clobber our ropchain delete gadget; continue; }else{ gadget->max_sp_inc = irblock->max_sp_inc; } e = semantics->regs->get(arch->sp()); if( e->is_binop(Op::ADD) && e->args[0]->is_cst() && e->args[1]->is_reg(arch->sp())){ if( e->args[0]->cst() % arch->octets != 0 ){ // std::cout << "DEBUG ERROR got SP INC Not multiple of arch size: " << irblock->name << std::endl; delete gadget; continue; } gadget->sp_inc = e->args[0]->cst(); }else if( e->is_binop(Op::ADD) && e->args[0]->is_unop(Op::NEG) && e->args[0]->args[0]->is_cst() && e->args[0]->args[0]->cst() % arch->octets == 0 && e->args[1]->is_reg(arch->sp())){ // sp = sp0 - cst gadget->sp_inc = -1*e->args[0]->args[0]->cst(); }else if( e->is_reg(arch->sp()) ){ gadget->sp_inc = 0; } // Get branch type if( irblock->ends_with_syscall ){ gadget->branch_type = BranchType::SYSCALL; }else if( irblock->ends_with_int80 ){ gadget->branch_type = BranchType::INT80; }else{ // Check last PC value e = semantics->regs->get(arch->pc()); if( e->is_var() ){ // Jmp gadget->branch_type = BranchType::JMP; gadget->jmp_reg = arch->reg_num(e->name()); }else if( e->is_mem()){ // Ret (sp) if( e->args[0]->is_var() && arch->reg_num(e->args[0]->name()) == arch->sp() && gadget->sp_inc == arch->octets ){ gadget->branch_type = BranchType::RET; }else if( e->args[0]->is_binop(Op::ADD) && e->args[0]->args[0]->is_cst() && e->args[0]->args[1]->is_var() && (e->args[0]->args[0]->cst() + arch->octets == gadget->sp_inc) && arch->reg_num(e->args[0]->args[1]->name()) == arch->sp()){ gadget->branch_type = BranchType::RET; }else{ // std::cout << "DEBUG ERROR, NO VALID BRANCH TYPE: " << irblock->name << std::endl; delete gadget; continue; } }else{ // std::cout << "DEBUG ERROR, NO VALID BRANCH TYPE: " << irblock->name << std::endl; delete gadget; continue; } } // Set name gadget->asm_str = irblock->name; // Set address gadget->addresses.push_back(raw.addr); // Set modified registers for( int r = 0; r < arch->nb_regs; r++){ if( !semantics->regs->get(r)->is_var() || semantics->regs->get(r)->name() != arch->reg_name(r)){ gadget->modified_regs[r] = true; } } // Delete irblock (we don't need it anymore, only semantics) delete irblock; irblock = nullptr; // Classify gadget in db seen[raw.raw] = gadget; add(gadget, arch); nb_success++; } } return nb_success; } Gadget* GadgetDB::get(gadget_t gadget_num){ if( gadget_num >= all.size()) throw runtime_exception("GadgetDB::get() got invalid gadget number"); return all[gadget_num]; } const vector & GadgetDB::get_mov_cst(reg_t reg, cst_t cst){ return mov_cst.get(make_tuple(reg, cst)); } const vector & GadgetDB::get_mov_reg(reg_t dst_reg, reg_t src_reg){ return mov_reg.get(make_tuple(dst_reg, src_reg)); } const vector & GadgetDB::get_amov_cst(reg_t dst_reg, reg_t src_reg, Op op, cst_t src_cst){ return amov_cst.get(make_tuple(dst_reg, src_reg, (op_t)op, src_cst)); } const vector & GadgetDB::get_amov_reg(reg_t dst_reg, reg_t src_reg1, Op op, reg_t src_reg2){ return amov_reg.get(make_tuple(dst_reg, src_reg1, (op_t)op, src_reg2)); } const vector & GadgetDB::get_load(reg_t dst_reg, reg_t addr_reg, cst_t offset){ return load.get(make_tuple(dst_reg, addr_reg, offset)); } const vector & GadgetDB::get_aload(reg_t dst_reg, Op op, reg_t addr_reg, cst_t offset){ return aload.get(make_tuple(dst_reg, (op_t)op, addr_reg, offset)); } const vector & GadgetDB::get_jmp(reg_t jmp_reg){ return jmp.get(jmp_reg); } const vector & GadgetDB::get_store(reg_t addr_reg, cst_t offset, reg_t src_reg){ return store.get(make_tuple(addr_reg, offset, src_reg)); } const vector & GadgetDB::get_astore(reg_t addr_reg, cst_t offset, Op op, reg_t src_reg){ return astore.get(make_tuple(addr_reg, offset, (op_t)op, src_reg)); } const vector & GadgetDB::get_int80(){ return int80.get(0); // Use dummy key 0 } const vector & GadgetDB::get_syscall(){ return syscall.get(0); // Use dummy key 0 } /* ============== Get possible gadgets ===================== */ PossibleGadgets* GadgetDB::get_possible_mov_reg(reg_t dst_reg, reg_t src_reg, bool* param_is_free){ return mov_reg.get_possible(make_tuple(dst_reg, src_reg), param_is_free, 2); } PossibleGadgets* GadgetDB::get_possible_amov_reg(reg_t dst_reg, reg_t src_reg1, Op src_op, reg_t src_reg2, bool* param_is_free){ return amov_reg.get_possible(make_tuple(dst_reg, src_reg1, (op_t)src_op, src_reg2), param_is_free, 4); } PossibleGadgets* GadgetDB::get_possible_mov_cst(reg_t dst_reg, cst_t src_cst, bool* param_is_free){ return mov_cst.get_possible(make_tuple(dst_reg, src_cst), param_is_free, 2); } PossibleGadgets* GadgetDB::get_possible_amov_cst(reg_t dst_reg, reg_t src_reg, Op src_op, cst_t src_cst, bool* param_is_free){ return amov_cst.get_possible(make_tuple(dst_reg, src_reg, (op_t)src_op, src_cst), param_is_free, 4); } PossibleGadgets* GadgetDB::get_possible_load(reg_t dst_reg, reg_t src_addr_reg, cst_t src_addr_offset, bool* param_is_free){ return load.get_possible(make_tuple(dst_reg, src_addr_reg, src_addr_offset), param_is_free, 3); } PossibleGadgets* GadgetDB::get_possible_aload(reg_t dst_reg, Op op, reg_t src_addr_reg, cst_t src_addr_offset, bool* param_is_free){ return aload.get_possible(make_tuple(dst_reg, (op_t)op, src_addr_reg, src_addr_offset), param_is_free, 4); } PossibleGadgets* GadgetDB::get_possible_store(reg_t dst_addr_reg, cst_t dst_addr_cst, reg_t src_reg, bool* param_is_free){ return store.get_possible(make_tuple(dst_addr_reg, dst_addr_cst, src_reg), param_is_free, 3); } PossibleGadgets* GadgetDB::get_possible_astore(reg_t dst_addr_reg, cst_t dst_addr_cst, Op op, reg_t src_reg, bool* param_is_free){ return astore.get_possible(make_tuple(dst_addr_reg, dst_addr_cst, (op_t)op, src_reg), param_is_free, 3); } void GadgetDB::clear(){ mov_cst.clear(); amov_cst.clear(); mov_reg.clear(); amov_reg.clear(); load.clear(); aload.clear(); store.clear(); astore.clear(); for( auto g : all ){ delete g; } all.clear(); seen.clear(); } GadgetDB::~GadgetDB(){ // Delete all gadgets clear(); } ================================================ FILE: libropium/dependencies/murmur3/murmur3.c ================================================ //----------------------------------------------------------------------------- // MurmurHash3 was written by Austin Appleby, and is placed in the public // domain. The author hereby disclaims copyright to this source code. // Note - The x86 and x64 versions do _not_ produce the same results, as the // algorithms are optimized for their respective platforms. You can still // compile and run any of them on any platform, but your performance with the // non-native version will be less than optimal. #include "murmur3.h" //----------------------------------------------------------------------------- // Platform-specific functions and macros #ifdef __GNUC__ #define FORCE_INLINE __attribute__((always_inline)) inline #else #define FORCE_INLINE inline #endif static FORCE_INLINE uint32_t rotl32 ( uint32_t x, int8_t r ) { return (x << r) | (x >> (32 - r)); } static FORCE_INLINE uint64_t rotl64 ( uint64_t x, int8_t r ) { return (x << r) | (x >> (64 - r)); } #define ROTL32(x,y) rotl32(x,y) #define ROTL64(x,y) rotl64(x,y) #define BIG_CONSTANT(x) (x##LLU) //----------------------------------------------------------------------------- // Block read - if your platform needs to do endian-swapping or can only // handle aligned reads, do the conversion here #define getblock(p, i) (p[i]) //----------------------------------------------------------------------------- // Finalization mix - force all bits of a hash block to avalanche static FORCE_INLINE uint32_t fmix32 ( uint32_t h ) { h ^= h >> 16; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; return h; } //---------- static FORCE_INLINE uint64_t fmix64 ( uint64_t k ) { k ^= k >> 33; k *= BIG_CONSTANT(0xff51afd7ed558ccd); k ^= k >> 33; k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53); k ^= k >> 33; return k; } //----------------------------------------------------------------------------- void MurmurHash3_x86_32 ( const void * key, int len, uint32_t seed, void * out ) { const uint8_t * data = (const uint8_t*)key; const int nblocks = len / 4; int i; uint32_t h1 = seed; uint32_t c1 = 0xcc9e2d51; uint32_t c2 = 0x1b873593; //---------- // body const uint32_t * blocks = (const uint32_t *)(data + nblocks*4); for(i = -nblocks; i; i++) { uint32_t k1 = getblock(blocks,i); k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; h1 = ROTL32(h1,13); h1 = h1*5+0xe6546b64; } //---------- // tail const uint8_t * tail = (const uint8_t*)(data + nblocks*4); uint32_t k1 = 0; switch(len & 3) { case 3: k1 ^= tail[2] << 16; case 2: k1 ^= tail[1] << 8; case 1: k1 ^= tail[0]; k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; }; //---------- // finalization h1 ^= len; h1 = fmix32(h1); *(uint32_t*)out = h1; } //----------------------------------------------------------------------------- void MurmurHash3_x86_128 ( const void * key, const int len, uint32_t seed, void * out ) { const uint8_t * data = (const uint8_t*)key; const int nblocks = len / 16; int i; uint32_t h1 = seed; uint32_t h2 = seed; uint32_t h3 = seed; uint32_t h4 = seed; uint32_t c1 = 0x239b961b; uint32_t c2 = 0xab0e9789; uint32_t c3 = 0x38b34ae5; uint32_t c4 = 0xa1e38b93; //---------- // body const uint32_t * blocks = (const uint32_t *)(data + nblocks*16); for(i = -nblocks; i; i++) { uint32_t k1 = getblock(blocks,i*4+0); uint32_t k2 = getblock(blocks,i*4+1); uint32_t k3 = getblock(blocks,i*4+2); uint32_t k4 = getblock(blocks,i*4+3); k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b; k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747; k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35; k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17; } //---------- // tail const uint8_t * tail = (const uint8_t*)(data + nblocks*16); uint32_t k1 = 0; uint32_t k2 = 0; uint32_t k3 = 0; uint32_t k4 = 0; switch(len & 15) { case 15: k4 ^= tail[14] << 16; case 14: k4 ^= tail[13] << 8; case 13: k4 ^= tail[12] << 0; k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; case 12: k3 ^= tail[11] << 24; case 11: k3 ^= tail[10] << 16; case 10: k3 ^= tail[ 9] << 8; case 9: k3 ^= tail[ 8] << 0; k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; case 8: k2 ^= tail[ 7] << 24; case 7: k2 ^= tail[ 6] << 16; case 6: k2 ^= tail[ 5] << 8; case 5: k2 ^= tail[ 4] << 0; k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; case 4: k1 ^= tail[ 3] << 24; case 3: k1 ^= tail[ 2] << 16; case 2: k1 ^= tail[ 1] << 8; case 1: k1 ^= tail[ 0] << 0; k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; }; //---------- // finalization h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len; h1 += h2; h1 += h3; h1 += h4; h2 += h1; h3 += h1; h4 += h1; h1 = fmix32(h1); h2 = fmix32(h2); h3 = fmix32(h3); h4 = fmix32(h4); h1 += h2; h1 += h3; h1 += h4; h2 += h1; h3 += h1; h4 += h1; ((uint32_t*)out)[0] = h1; ((uint32_t*)out)[1] = h2; ((uint32_t*)out)[2] = h3; ((uint32_t*)out)[3] = h4; } //----------------------------------------------------------------------------- void MurmurHash3_x64_128 ( const void * key, const int len, const uint32_t seed, void * out ) { const uint8_t * data = (const uint8_t*)key; const int nblocks = len / 16; int i; uint64_t h1 = seed; uint64_t h2 = seed; uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5); uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f); //---------- // body const uint64_t * blocks = (const uint64_t *)(data); for(i = 0; i < nblocks; i++) { uint64_t k1 = getblock(blocks,i*2+0); uint64_t k2 = getblock(blocks,i*2+1); k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729; k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5; } //---------- // tail const uint8_t * tail = (const uint8_t*)(data + nblocks*16); uint64_t k1 = 0; uint64_t k2 = 0; switch(len & 15) { case 15: k2 ^= (uint64_t)(tail[14]) << 48; case 14: k2 ^= (uint64_t)(tail[13]) << 40; case 13: k2 ^= (uint64_t)(tail[12]) << 32; case 12: k2 ^= (uint64_t)(tail[11]) << 24; case 11: k2 ^= (uint64_t)(tail[10]) << 16; case 10: k2 ^= (uint64_t)(tail[ 9]) << 8; case 9: k2 ^= (uint64_t)(tail[ 8]) << 0; k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; case 8: k1 ^= (uint64_t)(tail[ 7]) << 56; case 7: k1 ^= (uint64_t)(tail[ 6]) << 48; case 6: k1 ^= (uint64_t)(tail[ 5]) << 40; case 5: k1 ^= (uint64_t)(tail[ 4]) << 32; case 4: k1 ^= (uint64_t)(tail[ 3]) << 24; case 3: k1 ^= (uint64_t)(tail[ 2]) << 16; case 2: k1 ^= (uint64_t)(tail[ 1]) << 8; case 1: k1 ^= (uint64_t)(tail[ 0]) << 0; k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; }; //---------- // finalization h1 ^= len; h2 ^= len; h1 += h2; h2 += h1; h1 = fmix64(h1); h2 = fmix64(h2); h1 += h2; h2 += h1; ((uint64_t*)out)[0] = h1; ((uint64_t*)out)[1] = h2; } //----------------------------------------------------------------------------- ================================================ FILE: libropium/dependencies/murmur3/murmur3.h ================================================ //----------------------------------------------------------------------------- // MurmurHash3 was written by Austin Appleby, and is placed in the // public domain. The author hereby disclaims copyright to this source // code. #ifndef _MURMURHASH3_H_ #define _MURMURHASH3_H_ #include #ifdef __cplusplus extern "C" { #endif //----------------------------------------------------------------------------- void MurmurHash3_x86_32 (const void *key, int len, uint32_t seed, void *out); void MurmurHash3_x86_128(const void *key, int len, uint32_t seed, void *out); void MurmurHash3_x64_128(const void *key, int len, uint32_t seed, void *out); //----------------------------------------------------------------------------- #ifdef __cplusplus } #endif #endif // _MURMURHASH3_H_ ================================================ FILE: libropium/include/arch.hpp ================================================ #ifndef ARCH_H #define ARCH_H #include #include #include using std::string; using std::to_string; /* Forward declarations */ class Disassembler; /* Type aliasing */ typedef uint16_t reg_t; #define NB_REGS_MAX 64 /* CPU modes */ enum class CPUMode{ X86, X64, ARM32, ARM_THUMB, ARM64, NONE }; /* Different architectures supported */ enum class ArchType{ X86, X64, ARM32, ARM64, NONE }; #include "disassembler.hpp" /* Arch ==== The Arch object represents an architecture. It holds information such as the size of the registers, the number of registers, and the mode for architectures that can have several modes (X86, ARM, ...). It also holds a pointer to its corresponding Disassembler. Registers are represented as integers under the type reg_t. The Arch class is a Base class. Each architecture must the have its own child class, such as ArchX86, ArchX64, etc. */ class Arch{ public: ArchType type; Disassembler* disasm; int bits; int octets; int nb_regs; CPUMode mode; Arch(ArchType type, int bits, int octets, int nb_regs, CPUMode mode, Disassembler * disasm); virtual ~Arch(); virtual string reg_name(reg_t num) = 0; virtual reg_t reg_num(string name) = 0; virtual bool is_valid_reg(string& name) = 0; virtual reg_t sp() = 0; // Stack pointer virtual reg_t pc() = 0; // Program counter virtual reg_t tsc() = 0; // Timestamp counter }; class ArchNone: public Arch{ public: ArchNone(): Arch(ArchType::NONE, 32, 4, 20, CPUMode::NONE, (Disassembler*)nullptr){}; string reg_name(reg_t num){return "reg" + to_string(num);}; reg_t reg_num(string name){return 0;}; bool is_valid_reg(string& name){return false;}; reg_t sp(){return 19;}; reg_t pc(){return 18;}; reg_t tsc(){return 17;}; }; /* ================================================== * Arch X86 * ================================================= */ /* Registers */ #define X86_EAX 0 #define X86_EBX 1 #define X86_ECX 2 #define X86_EDX 3 #define X86_EDI 4 #define X86_ESI 5 #define X86_EBP 6 #define X86_ESP 7 #define X86_EIP 8 /* Segment Registers */ #define X86_CS 9 #define X86_DS 10 #define X86_ES 11 #define X86_FS 12 #define X86_GS 13 #define X86_SS 14 /* Flag Registers */ #define X86_CF 15 // Carry flag #define X86_PF 16 // Parity flag #define X86_AF 17 // Auxiliary carry flag #define X86_ZF 18 // Zero flag #define X86_SF 19 // Sign flag #define X86_TF 20 // Trap flag #define X86_IF 21 // Interrupt enable flag #define X86_DF 22 // Direction flag #define X86_OF 23 // Overflow flag #define X86_IOPL 24 // I/O Priviledge level #define X86_NT 25 // Nested task flag #define X86_RF 26 // Resume flag #define X86_VM 27 // Virtual 8086 mode flag #define X86_AC 28 // Alignment check flag (486+) #define X86_VIF 29 // Virutal interrupt flag #define X86_VIP 30 // Virtual interrupt pending flag #define X86_ID 31 // ID Flag #define X86_TSC 32 // TImeSTamp counter #define X86_NB_REGS 33 class ArchX86: public Arch{ public: ArchX86(); string reg_name(reg_t num); reg_t reg_num(string name); bool is_valid_reg(string& name); reg_t sp(); reg_t pc(); reg_t tsc(); }; /* ================================================== * Arch X64 * ================================================= */ /* Registers */ #define X64_RAX 0 #define X64_RBX 1 #define X64_RCX 2 #define X64_RDX 3 #define X64_RDI 4 #define X64_RSI 5 #define X64_RBP 6 #define X64_RSP 7 #define X64_RIP 8 #define X64_R8 9 #define X64_R9 10 #define X64_R10 11 #define X64_R11 12 #define X64_R12 13 #define X64_R13 14 #define X64_R14 15 #define X64_R15 16 /* Segment Registers */ #define X64_CS 17 #define X64_DS 18 #define X64_ES 19 #define X64_FS 20 #define X64_GS 21 #define X64_SS 22 /* Flag Registers */ #define X64_CF 23 // Carry flag #define X64_PF 24 // Parity flag #define X64_AF 25 // Auxiliary carry flag #define X64_ZF 26 // Zero flag #define X64_SF 27 // Sign flag #define X64_TF 28 // Trap flag #define X64_IF 29 // Interrupt enable flag #define X64_DF 30 // Direction flag #define X64_OF 31 // Overflow flag #define X64_IOPL 32 // I/O Priviledge level #define X64_NT 33 // Nested task flag #define X64_RF 34 // Resume flag #define X64_VM 35 // Virtual 8086 mode flag #define X64_AC 36 // Alignment check flag (486+) #define X64_VIF 37 // Virutal interrupt flag #define X64_VIP 38 // Virtual interrupt pending flag #define X64_ID 39 // ID Flag #define X64_TSC 40 // Timestamp counter #define X64_NB_REGS 41 class ArchX64: public Arch{ public: ArchX64(); string reg_name(reg_t num); reg_t reg_num(string name); bool is_valid_reg(string& name); reg_t sp(); reg_t pc(); reg_t tsc(); }; #endif ================================================ FILE: libropium/include/assertion.hpp ================================================ #ifndef ASSERTION_H #define ASSERTION_H #include "ropchain.hpp" #include "arch.hpp" #include class ValidPointers{ vector _regs; public: void add_valid_pointer(int reg); bool is_valid_pointer(int reg); void clear(); }; class Assertion{ public: ValidPointers valid_pointers; void clear(); }; #endif ================================================ FILE: libropium/include/compiler.hpp ================================================ #ifndef ROP_COMPILER_H #define ROP_COMPILER_H #include "strategy.hpp" #include "il.hpp" #include "database.hpp" #include "systems.hpp" #include using std::list; enum class ABI{ /* X86 */ X86_CDECL, X86_STDCALL, X86_FASTCALL, X86_THISCALL_GCC, X86_THISCALL_MS, X86_LINUX_SYSENTER, X86_LINUX_INT80, /* X64 */ X64_MS, X64_SYSTEM_V, /* No specific ABI */ NONE }; /* CompilerTask ============ A compiler task is basically a set of StrategyGraph. For each graph, it tries to find a valid gadget selection. - If it succeeds, the corresponding ROPChain is returned. - If it fails, it applies strategy rules to the graph to create new graphs, adds the new graphs to the queue of pending graphs, and then tries the next one */ class CompilerTask{ void apply_rules_to_graph(StrategyGraph* graph, int max_tries); Arch * arch; public: CompilerTask(Arch* arch); vector
pending_strategies; void add_strategy(StrategyGraph* graph, int max_tries); ROPChain* compile(Arch* arch, GadgetDB* db, Constraint* constraint=nullptr, int nb_tries=3000); void clear(); ~CompilerTask(); }; /* ROPCompiler ============ A ROP compiler is an abstraction over IL and StrategyGraph functionnalities. Basically it takes an IL program, parses it, translates it into strategy graphs, and then start a compiler task to try to satisfy the program and find a matching ROPChain. */ class ROPCompiler{ ROPChain* _set_registers_permutation( vector & instr, vector & permutation, Constraint* constraint, list >& failed_perms, bool& failed_on_first); bool is_complex_instr(ILInstruction& instr, ABI abi); public: Arch* arch; GadgetDB* db; // Translate function calls into strategy graphs bool _x86_cdecl_to_strategy(StrategyGraph& graph, ILInstruction& instr); bool _x86_stdcall_to_strategy(StrategyGraph& graph, ILInstruction& instr); // Compile wrappers for functions and syscalls that set multiple registers ROPChain* _set_multiple_registers(vector & instr, Constraint* constraint); ROPChain* _compile_x86_linux_syscall(ILInstruction& instr, Constraint* constraint); ROPChain* _compile_x64_linux_syscall(ILInstruction& instr, Constraint* constraint); ROPChain* _compile_x64_system_v_call(ILInstruction& instr, Constraint* constraint); ROPChain* _compile_x64_ms_call(ILInstruction& instr, Constraint* constraint); ROPCompiler( Arch* arch, GadgetDB* db); // Main API // Take a list of instructions and compile all of them sequentially into a ropchain ROPChain* process_simple(vector & instructions, Constraint* constraint=nullptr, ABI abi = ABI::NONE, System sys=System::NONE); ROPChain* process_complex(vector & instructions, Constraint* constraint=nullptr, ABI abi = ABI::NONE, System sys=System::NONE); // Transform complex instructions into simpler instructions that can be handled by "process()" bool preprocess(vector & dst, vector & src, Constraint* constraint=nullptr); // Parse a program into a vector of instructions vector parse(string& program); // Translate an IL instruction into one or several strategy graphs void il_to_strategy(vector & graphs, ILInstruction& instr, Constraint* constraint = nullptr, ABI abi = ABI::NONE, System sys=System::NONE); // Parse and process a program ROPChain* compile(string program, Constraint* constraint=nullptr, ABI abi=ABI::NONE, System sys=System::NONE); }; #endif ================================================ FILE: libropium/include/constraint.hpp ================================================ #ifndef CONSTRAINT_H #define CONSTRAINT_H #include "ropchain.hpp" #include "arch.hpp" #include "assertion.hpp" #include class BadBytes{ vector _bad_bytes; public: void add_bad_byte(unsigned char byte); void clear(); bool is_valid_byte(unsigned char byte); unsigned char get_valid_byte(); bool is_valid_address(addr_t addr, int arch_bytes); addr_t get_valid_padding(int arch_bytes); addr_t get_valid_address(Gadget* gadget, int nb_bytes); bool check(Gadget* gadget, int arch_bytes); }; class KeepRegs{ vector _keep; public: void add_keep_reg(int reg_num); vector & regs_to_keep(); bool is_kept(int reg_num); void clear(); bool check(Gadget* gadget); }; class MemSafety{ bool _force_safe; bool _safe_reg_pointers[NB_REGS_MAX]; // Registers that should be considered valid pointers public: MemSafety(); void force_safe(); void enable_unsafe(); bool is_enforced(); void add_safe_reg(int reg_num); void clear(); bool check(Gadget* gadget, int arch_nb_regs, Assertion* assertion=nullptr); }; class Constraint{ public: BadBytes bad_bytes; KeepRegs keep_regs; MemSafety mem_safety; void clear(); bool check(Gadget* gadget, Arch* arch, Assertion* assertion = nullptr); }; #endif ================================================ FILE: libropium/include/database.hpp ================================================ #ifndef DATABASE_H #define DATABASE_H #include #include #include #include "utils.hpp" #include "expression.hpp" #include "ropchain.hpp" #include "arch.hpp" using std::pair; using std::tuple; using std::make_tuple; using std::unordered_map; typedef int gadget_t; #define NO_GADGET -1 #define DB_MAX_REGS 64 // Types of gadgets supported in database enum class GadgetType{ NOP, // Register to register MOV_CST, // reg <- cst MOV_REG, // reg <- reg AMOV_CST, // reg <- reg OP cst AMOV_REG, // reg <- reg OP reg // Read from memory LOAD, // reg <- mem(reg + offset) ALOAD, // reg OP<- mem(reg + offset) // Store to memory STORE, // mem(reg + offset) <- reg ASTORE, // mem(reg + offset) OP<- reg // jump JMP, // PC <- reg // Syscalls SYSCALL, INT80 }; /* PossibleGadgets =============== This class holds query results to the database where some parameters are 'free' (ie eax = ebx + X where X is not fixed) */ class PossibleGadgets{ public: vector , vector *>> gadgets; // Pointers to vector are not owned! int size(){return gadgets.size();}; vector & get_gadgets(int i){return *(gadgets[i].second);}; cst_t get_param(int i, int p){return gadgets[i].first[p];}; PossibleGadgets(){}; PossibleGadgets(const PossibleGadgets& other){ gadgets = std::move(other.gadgets); }; }; // Generic database for different kinds of gadgets int find_insert_index(vector & gadget_list, Gadget* gadget); int find_insert_index_possible_gadgets(PossibleGadgets* possible, Gadget* gadget); template class BaseDB{ public: unordered_map > db; // Template methods void add(K key, Gadget* gadget){ vector ::iterator it; int index; if( db.count(key) > 0 ){ index = find_insert_index(db[key], gadget); db[key].insert(db[key].begin()+index, gadget); }else{ db[key] = vector {gadget}; } } const vector & get(K key){ typename unordered_map >::iterator it; if( (it = db.find(key)) == db.end()){ db[key] = vector {}; return db[key]; }else{ return it->second; } } bool _check_key_match(const K& key1, const K& key2, bool* param_is_free, int nb_params){ auto a1 = tuple_to_array(key1); auto a2 = tuple_to_array(key2); for( int p = 0; p < a1.size(); p++){ if( !param_is_free[p] && !(a1[p] == a2[p])) return false; } return true; } PossibleGadgets* get_possible(K key, bool* param_is_free, int nb_params){ PossibleGadgets* res = new PossibleGadgets(); int index; for( auto& it : db ){ // Check if key matches if( !it.second.empty() && _check_key_match(key, it.first, param_is_free, nb_params)){ vector vec = tuple_to_vector(it.first); index = find_insert_index_possible_gadgets(res, it.second[0]); res->gadgets.insert(res->gadgets.begin()+index, std::make_pair(vec, &(it.second))); } } return res; } void clear(){ db.clear(); } }; // Big gadget database typedef int op_t; class GadgetDB{ // Map of raw gadget strings that have already be analysed unordered_map seen; public: // Global gadgets lisst (gadgets are owned) vector all; // Databases for all different gadgets types BaseDB > mov_cst; BaseDB > mov_reg; BaseDB > amov_cst; BaseDB > amov_reg; BaseDB > load; BaseDB > aload; BaseDB > store; BaseDB > astore; BaseDB jmp; BaseDB syscall; // key is always 0 BaseDB int80; // key is always 0 // Add and classify a gadget in the database gadget_t add(Gadget* gadget, Arch* arch); // Analyse raw gadgets and fill the database accordingly // return the number of successfuly analysed gadgets int analyse_raw_gadgets(vector & raw_gadgets, Arch* arch); // Get a gadget by id Gadget* get(gadget_t gadget_num); // Get gadgets semantically const vector & get_mov_cst(reg_t reg, cst_t cst); const vector & get_mov_reg(reg_t dst_reg, reg_t src_reg); const vector & get_amov_cst(reg_t dst_reg, reg_t src_reg, Op op, cst_t src_cst); const vector & get_amov_reg(reg_t dst_reg, reg_t src_reg1, Op op, reg_t src_reg2); const vector & get_load(reg_t dst_reg, reg_t addr_reg, cst_t offset); const vector & get_aload(reg_t dst_reg, Op op, reg_t addr_reg, cst_t offset); const vector & get_jmp(reg_t jmp_reg); const vector & get_store(reg_t addr_reg, cst_t offset, reg_t src_reg); const vector & get_astore(reg_t addr_reg, cst_t offset, Op op, reg_t src_reg); const vector & get_syscall(); const vector & get_int80(); // Get gadgets with optional parameters PossibleGadgets* get_possible_mov_cst(reg_t reg, cst_t cst, bool* param_is_free); PossibleGadgets* get_possible_mov_reg(reg_t dst_reg, reg_t src_reg, bool* param_is_free); PossibleGadgets* get_possible_amov_cst(reg_t dst_reg, reg_t src_reg, Op op, cst_t src_cst, bool* param_is_free); PossibleGadgets* get_possible_amov_reg(reg_t dst_reg, reg_t src_reg1, Op op, reg_t src_reg2, bool* param_is_free); PossibleGadgets* get_possible_load(reg_t dst_reg, reg_t addr_reg, cst_t offset, bool* param_is_free); PossibleGadgets* get_possible_aload(reg_t dst_reg, Op op, reg_t addr_reg, cst_t offset, bool* param_is_free); PossibleGadgets* get_possible_jmp(reg_t jmp_reg, bool* param_is_free); PossibleGadgets* get_possible_store(reg_t addr_reg, cst_t offset, reg_t src_reg, bool* param_is_free); PossibleGadgets* get_possible_astore(reg_t addr_reg, cst_t offset, Op op, reg_t src_reg, bool* param_is_free); // Clear void clear(); // Destructor ~GadgetDB(); }; #endif ================================================ FILE: libropium/include/disassembler.hpp ================================================ #ifndef DISASSEMBLER_H #define DISASSEMBLER_H #include "arch.hpp" #include "ir.hpp" #include /* Types aliasing */ typedef uint8_t* code_t; /* Forward declaration */ enum class CPUMode; class IRBlock; /* Disassembler ============ A disassembler is aimed at translating bytecode into IR. It is basically a wrapper around capstone disassembly framework. Every disassembler initializes a capstone context with a handle and an cs_insn pointer in order to disassembly code iteratively instruction by instruction. */ class Disassembler{ public: CPUMode _mode; /* Capstone objects */ csh _handle; cs_insn * _insn; virtual ~Disassembler(); /* Disassemble instructions until next branch instruction and return an IRBlock* */ virtual IRBlock* disasm_block(addr_t addr, code_t code, size_t code_size=0xffffffff)=0; }; class DisassemblerX86: public Disassembler{ public: DisassemblerX86(CPUMode mode); IRBlock* disasm_block(addr_t addr, code_t code, size_t code_size=0xffffffff); }; #endif ================================================ FILE: libropium/include/exception.hpp ================================================ #ifndef EXCEPTION_H #define EXCEPTION_H #include #include #include using std::string; /* From stackoverflow */ class QuickFmt{ public: QuickFmt() {} ~QuickFmt() {} template QuickFmt & operator << (const Type & value) { stream_ << value; return *this; } std::string str() const { return stream_.str(); } operator std::string () const { return stream_.str(); } enum ConvertToString { to_str }; std::string operator >> (ConvertToString) { return stream_.str(); } private: std::stringstream stream_; QuickFmt(const QuickFmt &); QuickFmt & operator = (QuickFmt &); }; /* Generic exception * This exception is thrown when an unexpected error or inconsistency occurs * and execution should not continue */ class runtime_exception: public std::exception { string _msg; public: explicit runtime_exception(string msg): _msg(msg){}; virtual const char * what () const throw () { return _msg.c_str(); } }; /* Expression exception */ class expression_exception: public std::exception { string _msg; public: explicit expression_exception(string msg): _msg(msg){}; virtual const char * what () const throw () { return _msg.c_str(); } }; class ir_exception: public std::exception { string _msg; public: explicit ir_exception(string msg): _msg(msg){}; virtual const char * what () const throw () {return _msg.c_str();} }; class il_exception: public std::exception { string _msg; public: explicit il_exception(string msg): _msg(msg){}; virtual const char * what () const throw () {return _msg.c_str();} }; class compiler_exception: public std::exception { string _msg; public: explicit compiler_exception(string msg): _msg(msg){}; virtual const char * what () const throw () {return _msg.c_str();} }; class strategy_exception: public std::exception { string _msg; public: explicit strategy_exception(string msg): _msg(msg){}; virtual const char * what () const throw () {return _msg.c_str();} }; /* Symbolic Exception */ class symbolic_exception: public std::exception { string _msg; public: explicit symbolic_exception(string msg): _msg(msg){}; virtual const char * what () const throw () { return _msg.c_str(); } }; class unsupported_instruction_exception: public std::exception { string _msg; public: explicit unsupported_instruction_exception(string msg): _msg(msg){}; virtual const char * what () const throw () { return _msg.c_str(); } }; class illegal_instruction_exception: public std::exception { string _msg; public: explicit illegal_instruction_exception(string msg): _msg(msg){}; virtual const char * what () const throw () { return _msg.c_str(); } }; /* Test exception */ class test_exception : public std::exception { const char * what () const throw () { return "Unit test failure"; } }; #endif ================================================ FILE: libropium/include/expression.hpp ================================================ #ifndef EXPRESSION_H #define EXPRESSION_H #include #include #include #include #include #include