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 ================================================




# About **ROPium** (ex-ROPGenerator) is a library/tool that makes ROP-exploits easy. It automatically extracts and analyses gadgets from binaries and lets you find ROP-chains with semantic queries. ROPium supports *X86* and *X64* architectures, soon to be extended with *ARM*. Key features: - **Effortless**: ROPium works out-of-the-box with a smooth Command Line Interface - **Python API**: It is easy to integrate ROPium in script thanks to its python API - **Automatic chaining**: ROPium automatically combines gadgets to create complex ROP-chains - **Advanced features**: ROPium supports function calls for various ABIs, syscalls, ... - **Semantic queries**: ROPium queries are quick and convenient to write : ``rax=rbx+8``, ``[rdi+0x20]=rax``, ``rsi=[rbx+16]``, ``0x08040212(1, 2, rax)``, ``[0xdeadbeaf] = "/bin/sh\x00"``, ``sys_execve(0xdeadbeef, 0, 0)``, ``sys_0x1(0)``, ``...`` # Content - [About](#about) - [Installation](#installation) - [Getting started](#getting-started) - [CLI tool](#cli-tool) - [Python API](#python-api) - [Docker](#docker) - [Contact](#contact) - [Licence](#licence) - [Special thanks](#special-thanks) # Installation First install the [Capstone](https://github.com/aquynh/capstone) disassembly framework: sudo apt-get install libcapstone-dev You also need the latest [ROPgadget](https://github.com/JonathanSalwan/ROPgadget) release: git clone https://github.com/JonathanSalwan/ROPgadget && cd ROPgadget python setup.py install --user To use the CLI tool, install [prompt_toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit): pip3 install prompt_toolkit Finally install **ROPium**: git clone https://github.com/Boyan-MILANOV/ropium && cd ropium make make test sudo make install # Getting started ### CLI tool Thanks to a Command-Line-Interface wrapper, you can use ROPium interactively to quickly build ropchains:

### Python API Do you need to integrate ropchains directly in your scripts ? Good news, ROPium has a python API ! Loading a binary and finding ropchains: ```Python from ropium import * rop = ROPium(ARCH.X64) rop.load('/lib/x86_64-linux-gnu/libc-2.27.so') chain = rop.compile('rbx = [rax + 0x20]') ``` Dumping a ropchain in various formats: ```Python >>> print( chain.dump() ) 0x000000000009a851 (sub rax, 0x10; ret) 0x0000000000130018 (mov rax, qword ptr [rax + 0x30]; ret) 0x0000000000052240 (push rax; pop rbx; ret) >>> print(chain.dump('python')) from struct import pack off = 0x0 p = '' p += pack('>> 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 #include "exception.hpp" using std::string; using std::vector; using std::shared_ptr; using std::ostream; using std::map; /* Type aliasing */ typedef uint16_t exprsize_t ; typedef uint32_t hash_t; typedef int64_t cst_t; typedef uint64_t ucst_t; typedef ucst_t addr_t; /* Types of expressions ==================== Different expression types are supported: - CST: constant value - VAR: symbolic variable, identified by its name - MEM: a memory content, identified by an address and the number of bits that are read - UNOP/BINOP: unary and binary operations on expressions - EXTRACT: extraction of a bit-interval of another expression, the interval is specified with the values of the higher and lower bits to extract - CONCAT: binary concatenation of two expressions - BISZ: zero testing. Depending on its mode, it can be equal to 1 IFF the argument is zero, or to 0 IFF the argument is zero - UNKNOWN: represents a value which is unknown or can't be computed */ enum class ExprType { VAR, MEM, EXTRACT, CONCAT, UNOP, BINOP, BISZ, CST, UNKNOWN }; bool operator<(ExprType t1, ExprType t2); /* Types of operations =================== Different operations on expressions are supported. Their effects are pretty straightforward. Note that unary and binary operations are a member of the same enum. Note that there is no binary SUB operation, only a unary SUB. */ enum class Op { ADD=0, MUL, MULH, SMULL, SMULH, DIV, SDIV, NEG, AND, OR, XOR, SHL, SHR, MOD, SMOD, NOT, NONE // No operation }; string op_to_str(Op op); bool operator<(Op op1, Op op2); bool op_is_symetric(Op op); bool op_is_associative(Op op); bool op_is_left_associative(Op op); bool op_is_distributive_over(Op op1, Op op2); bool op_is_multiplication(Op op); /* Forward declarations */ class ExprObject; class VarContext; typedef shared_ptr Expr; /* Generic base class */ class ExprObject{ friend class ExprSimplifier; protected: // Hash bool _hashed; hash_t _hash; // Concrete cst_t _concrete; int _concrete_ctx_id; // Simplification Expr _simplified_expr; bool _is_simplified; public: // General const ExprType type; exprsize_t size; vector args; ExprObject(ExprType type, exprsize_t size, bool _is_simp=false); virtual void get_associative_args(Op op, vector& vec){}; virtual void get_left_associative_args(Op op, vector& vec, Expr& leftmost){}; /* Virtual accessors of specialized child classes members */ virtual hash_t hash(){throw runtime_exception("Called virtual function in ExprObject base class!");}; virtual cst_t cst(){throw runtime_exception("Called virtual function in ExprObject base class!");}; virtual const string& name(){throw runtime_exception("Called virtual function in ExprObject base class!");}; virtual void replace_name(string& new_name){throw runtime_exception("Called virtual function in ExprObject base class!");}; virtual Op op(){throw runtime_exception("Called virtual function in ExprObject base class!");}; virtual cst_t mode(){throw runtime_exception("Called virtual function in ExprObject base class!");}; virtual void print(ostream& out){out << "???";}; virtual int reg(){throw runtime_exception("Called virtual function in ExprObject base class!");}; /* Type */ bool is_cst(); bool is_var(); virtual bool is_reg(int reg){return false;} bool is_mem(); virtual bool is_unop(Op op=Op::NONE); virtual bool is_binop(Op op=Op::NONE); bool is_extract(); bool is_concat(); bool is_bisz(); bool is_unknown(); /* Concretize */ virtual cst_t concretize(VarContext* ctx=nullptr); /* Equality between expressions */ bool eq(Expr other); bool neq(Expr other); /* Priority between expressions */ bool inf(Expr other); /* Replace constants (used for dependencies) */ void replace_var_name(string& curr_name, string& new_name); /* Copy */ virtual Expr copy(){throw runtime_exception("Called virtual function in ExprObject base class!");}; }; /* Child specialized classes */ class ExprCst: public ExprObject{ cst_t _cst; public: ExprCst(exprsize_t size, cst_t cst); hash_t hash(); cst_t cst(); virtual cst_t concretize(VarContext* ctx=nullptr); virtual Expr copy(); void print(ostream& out); }; class ExprVar: public ExprObject{ const string _name; int _num; public: ExprVar(exprsize_t size, string name, int num=0); hash_t hash(); bool is_reg(int reg); int reg(); virtual cst_t concretize(VarContext* ctx=nullptr); const string& name(); void replace_name(string& new_name); virtual Expr copy(); void print(ostream& out); }; class ExprMem: public ExprObject{ public: ExprMem(exprsize_t size, Expr addr); hash_t hash(); virtual cst_t concretize(VarContext* ctx=nullptr); virtual Expr copy(); void print(ostream& out); }; class ExprUnop: public ExprObject{ Op _op; public: ExprUnop(Op op, Expr arg); hash_t hash(); Op op(); virtual cst_t concretize(VarContext* ctx=nullptr); void print(ostream& out); virtual Expr copy(); bool is_unop(Op op); }; class ExprBinop: public ExprObject{ Op _op; public: ExprBinop(Op op, Expr left, Expr right); hash_t hash(); Op op(); void get_associative_args(Op op, vector& vec); void get_left_associative_args(Op op, vector& vec, Expr& leftmost); virtual cst_t concretize(VarContext* ctx=nullptr); void print(ostream& out); virtual Expr copy(); bool is_binop(Op op); }; class ExprExtract: public ExprObject{ public: ExprExtract(Expr arg, Expr higher, Expr lower); hash_t hash(); virtual cst_t concretize(VarContext* ctx=nullptr); virtual Expr copy(); void print(ostream& out); }; class ExprConcat: public ExprObject{ public: ExprConcat(Expr upper, Expr lower); hash_t hash(); virtual cst_t concretize(VarContext* ctx=nullptr); virtual Expr copy(); void print(ostream& out); }; class ExprBisz: public ExprObject{ cst_t _mode; public: ExprBisz(exprsize_t size, Expr cond, cst_t mode); hash_t hash(); cst_t mode(); virtual cst_t concretize(VarContext* ctx=nullptr); virtual Expr copy(); void print(ostream& out); }; class ExprUnknown: public ExprObject{ public: ExprUnknown(exprsize_t size); hash_t hash(); virtual cst_t concretize(VarContext* ctx=nullptr); void print(ostream& out); }; /* Helper functions to create new expressions */ // Create from scratch Expr exprcst(exprsize_t size, cst_t cst); Expr exprvar(exprsize_t size, string name, int num=-1); Expr exprmem(exprsize_t size, Expr addr); Expr exprunop(Op op, Expr arg); Expr exprbinop(Op op, Expr left, Expr right); Expr extract(Expr arg, unsigned long higher, unsigned long lower); Expr extract(Expr arg, Expr higher, Expr lower); Expr concat(Expr upper, Expr lower); Expr bisz(exprsize_t size, Expr arg, cst_t mode); Expr exprunknown(exprsize_t size); // Binary operations Expr operator+(Expr left, Expr right); Expr operator+(Expr left, cst_t right); Expr operator+(cst_t left, Expr right); Expr operator-(Expr left, Expr right); Expr operator-(Expr left, cst_t right); Expr operator-(cst_t left, Expr right); Expr operator*(Expr left, Expr right); Expr operator*(Expr left, cst_t right); Expr operator*(cst_t left, Expr right); Expr operator/(Expr left, Expr right); Expr operator/(Expr left, cst_t right); Expr operator/(cst_t left, Expr right); Expr operator&(Expr left, Expr right); Expr operator&(Expr left, cst_t right); Expr operator&(cst_t left, Expr right); Expr operator|(Expr left, Expr right); Expr operator|(Expr left, cst_t right); Expr operator|(cst_t left, Expr right); Expr operator^(Expr left, Expr right); Expr operator^(Expr left, cst_t right); Expr operator^(cst_t left, Expr right); Expr operator%(Expr left, Expr right); Expr operator%(Expr left, cst_t right); Expr operator%(cst_t left, Expr right); Expr operator<<(Expr left, Expr right); Expr operator<<(Expr left, cst_t right); Expr operator<<(cst_t left, Expr right); Expr operator>>(Expr left, Expr right); Expr operator>>(Expr left, cst_t right); Expr operator>>(cst_t left, Expr right); Expr shl(Expr arg, Expr shift); Expr shl(Expr arg, cst_t shift); Expr shl(cst_t arg, Expr shift); Expr shr(Expr arg, Expr shift); Expr shr(Expr arg, cst_t shift); Expr shr(cst_t arg, Expr shift); Expr sdiv(Expr left, Expr right); Expr sdiv(Expr left, cst_t right); Expr sdiv(cst_t left, Expr right); Expr smod(Expr left, Expr right); Expr smod(Expr left, cst_t right); Expr smod(cst_t left, Expr right); Expr mulh(Expr left, Expr right); Expr mulh(Expr left, cst_t right); Expr mulh(cst_t left, Expr right); Expr smull(Expr left, Expr right); Expr smull(Expr left, cst_t right); Expr smull(cst_t left, Expr right); Expr smulh(Expr left, Expr right); Expr smulh(Expr left, cst_t right); Expr smulh(cst_t left, Expr right); // Unary operations Expr operator~(Expr arg); Expr operator-(Expr arg); /* Printing expressions */ ostream& operator<< (ostream& os, Expr e); /* Canonizing expressions */ Expr expr_canonize(Expr e); cst_t cst_sign_trunc(exprsize_t size, cst_t val); cst_t cst_mask(exprsize_t size); cst_t cst_sign_extend(exprsize_t size, cst_t val); // Macros to statically cast expressions to access fields if needed #define _exprobject_(e) (*(static_cast(e.get()))) #define _cst_(e) (*(static_cast(e.get()))) #define _var_(e) (*(static_cast(e.get()))) #define _mem_(e) (*(static_cast(e.get()))) #define _unop_(e) (*(static_cast(e.get()))) #define _binop_(e) (*(static_cast(e.get()))) #define _extract_(e) (*(static_cast(e.get()))) #define _concat_(e) (*(static_cast(e.get()))) #define _bisz_(e) (*(static_cast(e.get()))) #define _unknown_(e) (*(static_cast(e.get()))) /* VarContext ========== A VarContext associates a list of concrete values to a list of variables. It used with the variables names as keys for lookup. */ class VarContext{ map varmap; public: int id; VarContext(int id=0); void set(const string& name, cst_t value); cst_t get(const string& name); void remove(const string& name); void print(ostream& os); }; ostream& operator<<(ostream& os, VarContext& c); #endif ================================================ FILE: libropium/include/il.hpp ================================================ #ifndef IL_HPP #define IL_HPP #include "arch.hpp" /* IL - Intermediate Language ========================== The IL is used to write ROP programs that the ROPCompiler will then try to satisfy using the available gadgets. It is very close to the different kinds of gadgets supported ( MOV_REG, MOV_CST, LOAD, STORE, etc) with a few extra types available for convenience (like CST_STORE, LOAD_CST, etc). */ enum class ILInstructionType{ // 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) LOAD_CST, // reg <- mem(offset) ALOAD_CST, // reg OP<- mem(offset) // Store to memory STORE, // mem(reg + offset) <- reg ASTORE, // mem(reg + offset) OP<- reg CST_STORE, // mem(offset) <- reg CST_ASTORE, // mem(offset) OP<- reg STORE_CST, // mem(reg + offset) <- cst ASTORE_CST, // mem(reg + offset) OP<- cst CST_STORE_CST, // mem(offset) <- cst CST_ASTORE_CST, // mem(offset) OP<- cst CST_STORE_STRING, // mem(offset) <- string // jump JMP, // PC <- reg // Call functions FUNCTION, SYSCALL, SINGLE_SYSCALL }; /* IL - Instruction ================ IL instructions are represented by a simple class which holds the instruction type and the list of arguments of the instruction. The argument are ordered as defined in the strategy.hpp file (it holds #define enums for gadget arguments and IL arguments since those are very similar in most cases). For example to access the destination register of a MOV_CST instruction we do: instr.args[PARAM_MOVCST_DST_REG]. Instructions are build directly from a string, examples are: "eax += ebx" "ecx = 678" "esi = ebx ^ 0xdead" "[edx+8] *= 2" "edx = [ecx]" */ #define IL_FUNC_ARG_REG 0 #define IL_FUNC_ARG_CST 1 class ILInstruction{ public: ILInstructionType type; string syscall_name; // Used for SYSCALL int syscall_num; // Use for SYSCALL string str; // Use for STORE_STRING vector args; vector args_type; // Used for FUNCTION ILInstruction(ILInstructionType type, vector* args=nullptr, vector* args_type = nullptr, string syscall_name="", int syscall_num = -1, string str=""); ILInstruction(Arch& arch, string instr_str); // raises il_exception if instr_str is invalid }; #endif ================================================ FILE: libropium/include/ir.hpp ================================================ #ifndef IR_H #define IR_H #include #include #include "expression.hpp" #include "simplification.hpp" using std::unordered_map; /* Type aliasing */ typedef unsigned int IRVar; typedef unsigned int IRBasicBlockId; /* IR Operations =============== IR supports basic arithmetic and logical operations, store/load operations, and register 'mov' It also has two branchment instructions: - BCC : conditionnal jump to an IRBasicBlock - JCC : conditionnal jump to an IRBlock And two special instructions: - INT - SYSCALL */ enum class IROperation{ /* Arithmetic and logical operations */ ADD, SUB, MUL, MULH, SMULL, SMULH, DIV, SDIV, NEG, AND, OR, XOR, NOT, SHL, SHR, MOD, SMOD, /* Memory read and write */ LDM, STM, /* Set register with a value */ MOV, /* Conditionnal jumps */ BCC, // Internal, to same IRBlock. Used for conditionnal instructions JCC, // External, to other IRBlock Used for branch instructions /* Boolean flag set if zero */ BISZ, /* Concatenate two variables */ CONCAT, /* System calls and interrupt */ INT, SYSCALL }; bool iroperation_is_assignment(IROperation& op); bool iroperation_is_memory(IROperation& op); ostream& operator<<(ostream& os, IROperation& op); /* Values for syscalls */ #define SYSCALL_X86_INT80 1 #define SYSCALL_X86_SYSENTER 2 #define SYSCALL_X64_SYSCALL 3 /* IR Operations =============== IR operands can be of 3 main types. - CST: a constant operand - VAR: a operand representing a register of the disassembled arch - TMP: temporary registers used to model complex operations, they don't correspond to actual processor registers - NONE: represents the fact that there is no argument used */ enum class IROperandType{ CST, VAR, TMP, NONE }; class IROperand{ cst_t _val; public: IROperandType type; exprsize_t high, low, size; IROperand(); IROperand(IROperandType t, cst_t val, exprsize_t high, exprsize_t low); bool is_cst(); bool is_var(); bool is_tmp(); bool is_none(); cst_t cst(); IRVar var(); IRVar tmp(); }; ostream& operator<<(ostream& os, IROperand& op); /* Helpers to create operands */ IROperand ir_cst(cst_t val, exprsize_t high, exprsize_t low); IROperand ir_var(cst_t num, exprsize_t high, exprsize_t low); IROperand ir_tmp(cst_t num, exprsize_t high, exprsize_t low); IROperand ir_none(); /* IR Instructions =============== IR Instructions are composed of an IROperation, and 3 IROperands: one destination, and two (optional) sources */ class IRInstruction{ public: addr_t addr; IROperation op; IROperand dst; IROperand src1; IROperand src2; IRInstruction(IROperation op, IROperand dst, IROperand src1, addr_t addr = 0); IRInstruction(IROperation op, IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); bool reads_var(IRVar var); bool writes_var(IRVar var); bool uses_var(IRVar var); bool reads_tmp(IRVar tmp); bool writes_tmp(IRVar tmp); vector used_vars_read(); vector used_vars_write(); vector used_tmps_read(); vector used_tmps_write(); }; ostream& operator<<(ostream& os, IRInstruction& ins); /* Helpers to create instructions */ IRInstruction ir_add(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_sub(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_mul(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_mulh(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_smull(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_smulh(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_div(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_sdiv(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_and(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_or(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_xor(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_shl(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_shr(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_mod(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_smod(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_neg(IROperand dst, IROperand src1, addr_t addr = 0); IRInstruction ir_not(IROperand dst, IROperand src1, addr_t addr = 0); IRInstruction ir_ldm(IROperand dst, IROperand src1, addr_t addr = 0); IRInstruction ir_stm(IROperand dst, IROperand src1, addr_t addr = 0); IRInstruction ir_mov(IROperand dst, IROperand src1, addr_t addr = 0); IRInstruction ir_bcc(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_jcc(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_bisz(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_concat(IROperand dst, IROperand src1, IROperand src2, addr_t addr = 0); IRInstruction ir_int(IROperand num, IROperand ret, addr_t addr = 0); IRInstruction ir_syscall(IROperand type, IROperand ret, addr_t addr = 0); /* IRContext ========= Holds current expressions for every register */ class IRContext{ friend class BreakpointManager; Expr* _var; int _nb_var; public: IRContext(); IRContext(IRVar nb_var); ~IRContext(); int nb_vars(); /* Get and set IR variables */ void set(IRVar num, Expr e); Expr get(IRVar num); }; ostream& operator<<(ostream& os, IRContext& ctx); /* MemEngine ========== */ class MemContext{ public: unordered_map writes; ExprSimplifier* simp; MemContext(); void write(Expr addr, Expr expr); Expr read(Expr addr, int octets); ~MemContext(); }; ostream& operator<<(ostream& os, MemContext& ctx); /* Type aliasing */ typedef vector IRBasicBlock; /* IRBlock ======= An IRBlock represents a basic block in assembly. By basic block we mean a sequence of contiguous instructions that are executed sequentially (so no branchement instruction in the middle of a basic block, only at the end). An IRBlock is **uniquely** identifier by its start address ! There is a 'name' field but it's just here for convenience. An IRBlock is made of several IRBasicBlocks (which are just lists of IRInstructions). It also holds several "meta" informations like the number of tmp ir vars it holds, it's size in IR, in raw assembly, the branchment type it finishes with, etc. */ class IRBlock{ public: vector _bblocks; int _nb_tmp_vars; // Number of tmp variables used in the block int _nb_instr, _nb_instr_ir; addr_t start_addr, end_addr; string name; unsigned int ir_size; unsigned int raw_size; cst_t max_sp_inc; bool known_max_sp_inc; bool dereferenced_regs[128]; bool ends_with_syscall; bool ends_with_int80; addr_t branch_target[2]; // [0]: target when condition expression is 0 // [1]: target when condition expression is != 0 IRBlock(string name, addr_t start=0, addr_t end=0); void add_instr(IRBasicBlockId bblock, IRInstruction instr); /* Manage IR Basic Blocks */ IRBasicBlockId new_bblock(); IRBasicBlock& get_bblock(IRBasicBlockId id); int nb_bblocks(); vector& bblocks(); }; ostream& operator<<(ostream& os, IRBlock& blk); #endif ================================================ FILE: libropium/include/ropchain.hpp ================================================ #ifndef ROPCHAIN_H #define ROPCHAIN_H #include "symbolic.hpp" #include "arch.hpp" #include "utils.hpp" #include using std::string; /* ======== Gadgets ========== */ enum class BranchType{ RET, JMP, ANY, // Any of RET or JMP, not SYSCALL or INT80 SYSCALL, INT80, NONE, }; class Gadget{ public: int id; // To be set by the db when gadget is added int bin_num; // Identifies the binary/library it comes from string asm_str, _hex_str; Semantics* semantics; vector addresses; /* Number of instructions in the gadget */ int nb_instr, nb_instr_ir; // Info about gadget semantics cst_t sp_inc; cst_t max_sp_inc; BranchType branch_type; reg_t jmp_reg; bool modified_regs[NB_REGS_MAX]; bool dereferenced_regs[NB_REGS_MAX]; // Constructor Gadget(); ~Gadget(); // Other void add_address(addr_t addr); void print(ostream& os); bool lthan(Gadget& other); }; ostream& operator<<(ostream& os, Gadget& g); /* ======== ROPChain ========== */ enum class ROPItemType{ GADGET, PADDING, GADGET_ADDRESS }; class ROPItem{ public: ROPItemType type; Gadget* gadget; // If gadget addr_t addr; cst_t value; // If cst or padding string msg; ROPItem(addr_t a, Gadget* g, string m=""):type(ROPItemType::GADGET), addr(a), value(-1), gadget(g), msg(m){}; ROPItem(ROPItemType t, cst_t v, string m=""):type(t), value(v), msg(m), addr(0), gadget(nullptr){}; }; class ROPChain{ public: Arch *arch; // Not owned vector items; ROPChain(Arch* arch); void add_gadget(addr_t addr, Gadget* gadget); void add_padding(cst_t val, string m=""); void add_gadget_address(cst_t addr, string m = ""); void add_chain(ROPChain& other); int len(); void print_pretty(ostream& os, string tab=""); void print_python(ostream& os, string tab=""); void dump_raw(vector& bytes); }; ostream& operator<<(ostream& os, ROPChain& ropchain); #endif ================================================ FILE: libropium/include/ropium.hpp ================================================ #ifndef ROPIUM_H #define ROPIUM_H #include "expression.hpp" #include "simplification.hpp" #include "arch.hpp" #include "assertion.hpp" #include "memory.hpp" #include "ir.hpp" #include "il.hpp" #include "constraint.hpp" #include "disassembler.hpp" #include "ropchain.hpp" #include "exception.hpp" #include "database.hpp" #include "symbolic.hpp" #include "strategy.hpp" #include "utils.hpp" #endif ================================================ FILE: libropium/include/simplification.hpp ================================================ #ifndef SIMPLIFICATION_H #define SIMPLIFICATION_H #include "expression.hpp" #include using std::vector; /* Forward declaration */ class ExprSimplifier; /* Type aliasing */ typedef Expr (*ExprSimplifierFunc)(Expr); typedef Expr (*RecExprSimplifierFunc)(Expr, ExprSimplifier&); /* Expression simplifier */ class ExprSimplifier{ protected: vector simplifiers; vector rec_simplifiers; vector restruct_simplifiers; Expr run_simplifiers(Expr e); public: ExprSimplifier(); Expr simplify(Expr e); void add(ExprSimplifierFunc func); void add(RecExprSimplifierFunc func); void add_restruct(RecExprSimplifierFunc func); }; ExprSimplifier* NewDefaultExprSimplifier(); /* Simplification functions */ Expr es_constant_folding(Expr e); Expr es_neutral_elements(Expr e); Expr es_absorbing_elements(Expr e); Expr es_arithmetic_properties(Expr e); Expr es_involution(Expr e); Expr es_extract_patterns(Expr e); Expr es_basic_transform(Expr e); Expr es_logical_properties(Expr e); Expr es_concat_patterns(Expr e); Expr es_arithmetic_factorize(Expr e); Expr es_generic_factorize(Expr e); Expr es_generic_distribute(Expr e); Expr es_deep_associative(Expr e, ExprSimplifier& simp); #endif ================================================ FILE: libropium/include/strategy.hpp ================================================ #ifndef STRATEGY_H #define STRATEGY_H #include "database.hpp" #include "expression.hpp" #include "constraint.hpp" #include "assertion.hpp" #include #include #include using std::stringstream; using std::vector; using std::array; /* Forward declaration */ typedef int node_t; typedef int param_t; enum class ParamType{ CST, REG, OP, NONE }; struct ParamDep{ node_t node; param_t param_type; }; class Param{ public: ParamType type; string name; // Name for the param (used for 'free' constants only) // Value cst_t value; // Used to put constant OR regnum // Dependencies vector deps; Expr expr; // For constants only bool is_fixed; bool is_data_link; Param():type(ParamType::NONE), name(""), value(-1), expr(nullptr), is_fixed(true), is_data_link(false){}; void add_dep(node_t n, param_t p){ // Check if already present for( ParamDep& dep : deps ) if( dep.node == n && dep.param_type == p ) return; // If not present, add the dependency deps.push_back(ParamDep{n, p}); }; bool depends_on(node_t n){ for( ParamDep& dep : deps ){ if( dep.node == n ) return true; } return false; }; // Fixed or free register void make_reg(int reg, bool fixed=true){ type = ParamType::REG; value = reg; is_fixed = fixed; deps.clear(); expr = nullptr; is_data_link = false; }; // Dependent register void make_reg(node_t dn, int dpt){ type = ParamType::REG; value = -1; is_fixed = false; deps.clear(); add_dep(dn, dpt); expr = nullptr; is_data_link = false; }; // Fixed or free constant void make_cst(cst_t val, string n, bool fixed=true){ type = ParamType::CST; name = n; value = val; is_fixed = fixed; deps.clear(); expr = nullptr; is_data_link = false; }; // Dependent constant void make_cst(node_t dn, int dpt, Expr e, string n){ type = ParamType::CST; name = n; value = 0; is_fixed = false; deps.clear(); add_dep(dn, dpt); expr = e; is_data_link = false; }; // Operator void make_op(Op op){ type = ParamType::OP; value = (int)op; is_fixed = true; deps.clear(); expr = nullptr; is_data_link = false; }; bool is_dependent(){return !is_fixed && !deps.empty();}; bool is_free(){return !is_dependent() && !is_fixed;}; bool is_cst(){return type == ParamType::CST;}; bool is_reg(){return type == ParamType::REG;}; }; ostream& operator<<(ostream& os, Param& param); struct EdgeSet{ vector in; vector out; }; class UniqueNameGenerator{ private: int n; public: UniqueNameGenerator():n(0){}; string new_name(string& name){ stringstream ss; ss << name << "_" << std::dec << n; n++; return ss.str(); }; }; /* Different kinds parameters for nodes/IL instructions ==================================================== WARNING: their values have to match the place they have in the tuple when the gadgets are addded in the database ! */ #define MAX_PARAMS 9 #define PARAM_MOVREG_DST_REG 0 #define PARAM_MOVREG_SRC_REG 1 #define PARAM_MOVREG_GADGET_ADDR 2 #define PARAM_MOVREG_GADGET_SP_INC 3 #define PARAM_MOVREG_GADGET_JMP_REG 4 #define PARAM_MOVREG_GADGET_SP_DELTA 5 #define PARAM_MOVREG_DATA_LINK 6 #define NB_PARAM_MOVREG 7 #define PARAM_MOVCST_DST_REG 0 #define PARAM_MOVCST_SRC_CST 1 #define PARAM_MOVCST_GADGET_ADDR 2 #define PARAM_MOVCST_GADGET_SP_INC 3 #define PARAM_MOVCST_GADGET_JMP_REG 4 #define PARAM_MOVCST_GADGET_SP_DELTA 5 #define PARAM_MOVCST_DATA_LINK 6 #define NB_PARAM_MOVCST 7 #define PARAM_AMOVCST_DST_REG 0 #define PARAM_AMOVCST_SRC_REG 1 #define PARAM_AMOVCST_SRC_OP 2 #define PARAM_AMOVCST_SRC_CST 3 #define PARAM_AMOVCST_GADGET_ADDR 4 #define PARAM_AMOVCST_GADGET_SP_INC 5 #define PARAM_AMOVCST_GADGET_JMP_REG 6 #define PARAM_AMOVCST_GADGET_SP_DELTA 7 #define PARAM_AMOVCST_DATA_LINK 8 #define NB_PARAM_AMOVCST 9 #define PARAM_AMOVREG_DST_REG 0 #define PARAM_AMOVREG_SRC_REG1 1 #define PARAM_AMOVREG_SRC_OP 2 #define PARAM_AMOVREG_SRC_REG2 3 #define PARAM_AMOVREG_GADGET_ADDR 4 #define PARAM_AMOVREG_GADGET_SP_INC 5 #define PARAM_AMOVREG_GADGET_JMP_REG 6 #define PARAM_AMOVREG_GADGET_SP_DELTA 7 #define PARAM_AMOVREG_DATA_LINK 8 #define NB_PARAM_AMOVREG 9 #define PARAM_LOAD_DST_REG 0 #define PARAM_LOAD_SRC_ADDR_REG 1 #define PARAM_LOAD_SRC_ADDR_OFFSET 2 #define PARAM_LOAD_GADGET_ADDR 3 #define PARAM_LOAD_GADGET_SP_INC 4 #define PARAM_LOAD_GADGET_JMP_REG 5 #define PARAM_LOAD_GADGET_SP_DELTA 6 #define PARAM_LOAD_DATA_LINK 7 #define NB_PARAM_LOAD 8 #define PARAM_ALOAD_DST_REG 0 #define PARAM_ALOAD_OP 1 #define PARAM_ALOAD_SRC_ADDR_REG 2 #define PARAM_ALOAD_SRC_ADDR_OFFSET 3 #define PARAM_ALOAD_GADGET_ADDR 4 #define PARAM_ALOAD_GADGET_SP_INC 5 #define PARAM_ALOAD_GADGET_JMP_REG 6 #define PARAM_ALOAD_GADGET_SP_DELTA 7 #define PARAM_ALOAD_DATA_LINK 8 #define NB_PARAM_ALOAD 9 #define PARAM_LOADCST_DST_REG 0 #define PARAM_LOADCST_SRC_ADDR_OFFSET 1 #define PARAM_LOADCST_GADGET_ADDR 2 #define PARAM_LOADCST_GADGET_SP_INC 3 #define PARAM_LOADCST_GADGET_JMP_REG 4 #define PARAM_LOADCST_GADGET_SP_DELTA 5 #define NB_PARAM_LOADCST 6 #define PARAM_ALOADCST_DST_REG 0 #define PARAM_ALOADCST_OP 1 #define PARAM_ALOADCST_SRC_ADDR_OFFSET 2 #define PARAM_ALOADCST_GADGET_ADDR 3 #define PARAM_ALOADCST_GADGET_SP_INC 4 #define PARAM_ALOADCST_GADGET_JMP_REG 5 #define PARAM_ALOADCST_GADGET_SP_DELTA 6 #define NB_PARAM_ALOADCST 7 #define PARAM_STORE_DST_ADDR_REG 0 #define PARAM_STORE_DST_ADDR_OFFSET 1 #define PARAM_STORE_SRC_REG 2 #define PARAM_STORE_GADGET_ADDR 3 #define PARAM_STORE_GADGET_SP_INC 4 #define PARAM_STORE_GADGET_JMP_REG 5 #define PARAM_STORE_GADGET_SP_DELTA 6 #define PARAM_STORE_DATA_LINK 7 #define NB_PARAM_STORE 8 #define PARAM_CSTSTORE_DST_ADDR_OFFSET 0 #define PARAM_CSTSTORE_SRC_REG 1 #define PARAM_CSTSTORE_GADGET_ADDR 2 #define PARAM_CSTSTORE_GADGET_SP_INC 3 #define PARAM_CSTSTORE_GADGET_JMP_REG 4 #define PARAM_CSTSTORE_GADGET_SP_DELTA 5 #define NB_PARAM_CSTSTORE 6 #define PARAM_ASTORE_DST_ADDR_REG 0 #define PARAM_ASTORE_DST_ADDR_OFFSET 1 #define PARAM_ASTORE_OP 2 #define PARAM_ASTORE_SRC_REG 3 #define PARAM_ASTORE_GADGET_ADDR 4 #define PARAM_ASTORE_GADGET_SP_INC 5 #define PARAM_ASTORE_GADGET_JMP_REG 6 #define PARAM_ASTORE_GADGET_SP_DELTA 7 #define PARAM_ASTORE_DATA_LINK 8 #define NB_PARAM_ASTORE 9 #define PARAM_CSTASTORE_DST_ADDR_OFFSET 0 #define PARAM_CSTASTORE_OP 1 #define PARAM_CSTASTORE_SRC_REG 2 #define PARAM_CSTASTORE_GADGET_ADDR 3 #define PARAM_CSTASTORE_GADGET_SP_INC 4 #define PARAM_CSTASTORE_GADGET_JMP_REG 5 #define PARAM_CSTASTORE_GADGET_SP_DELTA 6 #define NB_PARAM_CSTASTORE 7 #define PARAM_STORECST_DST_ADDR_REG 0 #define PARAM_STORECST_DST_ADDR_OFFSET 1 #define PARAM_STORECST_SRC_CST 2 #define PARAM_STORECST_GADGET_ADDR 3 #define PARAM_STORECST_GADGET_SP_INC 4 #define PARAM_STORECST_GADGET_JMP_REG 5 #define PARAM_STORECST_GADGET_SP_DELTA 6 #define NB_PARAM_STORECST 7 #define PARAM_CSTSTORECST_DST_ADDR_OFFSET 0 #define PARAM_CSTSTORECST_SRC_CST 1 #define PARAM_CSTSTORECST_GADGET_ADDR 2 #define PARAM_CSTSTORECST_GADGET_SP_INC 3 #define PARAM_CSTSTORECST_GADGET_JMP_REG 4 #define PARAM_CSTSTORECST_GADGET_SP_DELTA 5 #define NB_PARAM_CSTSTORECST 6 #define PARAM_ASTORECST_DST_ADDR_REG 0 #define PARAM_ASTORECST_DST_ADDR_OFFSET 1 #define PARAM_ASTORECST_OP 2 #define PARAM_ASTORECST_SRC_CST 3 #define PARAM_ASTORECST_GADGET_ADDR 4 #define PARAM_ASTORECST_GADGET_SP_INC 5 #define PARAM_ASTORECST_GADGET_JMP_REG 6 #define PARAM_ASTORECST_GADGET_SP_DELTA 7 #define NB_PARAM_ASTORECST 8 #define PARAM_CSTASTORECST_DST_ADDR_OFFSET 0 #define PARAM_CSTASTORECST_OP 1 #define PARAM_CSTASTORECST_SRC_CST 2 #define PARAM_CSTASTORECST_GADGET_ADDR 3 #define PARAM_CSTASTORECST_GADGET_SP_INC 4 #define PARAM_CSTASTORECST_GADGET_JMP_REG 5 #define PARAM_CSTASTORECST_GADGET_SP_DELTA 6 #define NB_PARAM_CSTASTORECST 7 #define PARAM_FUNCTION_ADDR 0 #define PARAM_FUNCTION_ARGS 1 #define PARAM_SYSCALL_ARGS 0 // For IL #define PARAM_SYSCALL_GADGET_ADDR 0 // For gadget #define PARAM_SYSCALL_GADGET_SP_INC 1 #define PARAM_SYSCALL_GADGET_JMP_REG 2 #define PARAM_SYSCALL_GADGET_SP_DELTA 3 #define PARAM_SYSCALL_DATA_LINK 4 #define NB_PARAM_SYSCALL 5 #define PARAM_INT80_ARGS 0 // For IL #define PARAM_INT80_GADGET_ADDR 0 // For gadget #define PARAM_INT80_GADGET_SP_INC 1 #define PARAM_INT80_GADGET_JMP_REG 2 #define PARAM_INT80_GADGET_SP_DELTA 3 #define PARAM_INT80_DATA_LINK 4 #define NB_PARAM_INT80 5 #define PARAM_CSTSTORE_STRING_ADDR_OFFSET 0 typedef struct { Param offset; Param value; } ROPPadding; class Node; class NodeValidPointers{ vector _params; public: void add_valid_pointer(param_t param); void to_assertion(Node& node, Assertion* assertion); void clear(); }; class NodeAssertion{ public: NodeValidPointers valid_pointers; void to_assertion(Node& node, Assertion* a); void clear(); }; // Callback for custom constraints called to filter gadgets on each node class Node; class StrategyGraph; typedef bool (*constraint_callback_t)(Node* node, StrategyGraph* graph, Arch* arch); // Commonly used node constraints bool constraint_branch_type(Node* node, StrategyGraph* graph, Arch* arch); class Node{ public: int id; bool is_indirect; bool is_disabled; GadgetType type; // Edges EdgeSet strategy_edges; EdgeSet param_edges; EdgeSet interference_edges; // Parameters Param params[MAX_PARAMS]; // Affected gadget Gadget* affected_gadget; // Constraint vector strategy_constraints; vector assigned_gadget_constraints; // Branch type (RET, JMP, ANY, ...) BranchType branch_type; // Gadget paddings vector special_paddings; // Assertions NodeAssertion node_assertion; Assertion assertion; // Mandatory Following node node_t mandatory_following_node; Node(int i, GadgetType t); int nb_params(); bool has_free_param(); bool has_mandatory_following_node(); // Manage edges void add_incoming_strategy_edge(node_t src_node); void add_incoming_param_edge(node_t src_node); void add_outgoing_strategy_edge(node_t dst_node); void add_outgoing_param_edge(node_t dst_node); void remove_incoming_strategy_edge(node_t src_node); void remove_incoming_param_edge(node_t src_node); void remove_outgoing_strategy_edge(node_t dst_node); void remove_outgoing_param_edge(node_t dst_node); bool is_initial_param(param_t param); bool is_final_param(param_t param); bool is_src_param(param_t param); bool is_generic_param(param_t param); int get_param_num_gadget_sp_inc(); int get_param_num_gadget_addr(); int get_param_num_gadget_jmp_reg(); int get_param_num_gadget_sp_delta(); int get_param_num_src_reg(); int get_param_num_dst_reg(); int get_param_num_src_addr_offset(); int get_param_num_src_addr_reg(); int get_param_num_dst_addr_offset(); int get_param_num_dst_addr_reg(); int get_param_num_data_link(); bool has_dst_reg_param(); bool has_dst_addr_reg_param(); bool assign_gadget(Gadget* gadget, Arch* arch=nullptr, Constraint* constraint=nullptr); void apply_assertion(); bool modifies_reg(int reg_num); }; ostream& operator<<(ostream& os, Node& node); class InterferencePoint { public: node_t interfering_node; node_t start_node; node_t end_node; InterferencePoint(node_t i, node_t s, node_t e):interfering_node(i), start_node(s), end_node(e){}; }; /* Strategy graph */ class StrategyGraph{ private: UniqueNameGenerator name_generator; void _dfs_strategy_explore(vector& marked, node_t n); void _dfs_params_explore(vector& marked, node_t n); bool _dfs_scheduling_explore(vector& marked, node_t n); void _resolve_param(Param& param); void _resolve_all_params(node_t n); const vector& _get_matching_gadgets(GadgetDB& db, node_t node); PossibleGadgets* _get_possible_gadgets(GadgetDB& db, node_t n); bool _check_strategy_constraints(Node& node, Arch* arch); bool _check_assigned_gadget_constraints(Node& node, Arch* arch); bool _check_special_padding_constraints(Node& node, Arch* arch, Constraint* constraint=nullptr); bool _do_scheduling(int interference_idx=0); bool has_gadget_selection; VarContext params_ctx; int _depth; vector interference_points; public: string _history; int size; vector nodes; vector dfs_strategy; vector dfs_params; vector dfs_scheduling; StrategyGraph(); // Create new nodes/edges node_t new_node(GadgetType t); string new_name(string base); void disable_node(node_t node); void redirect_param_edges(node_t curr_node, param_t curr_param_type, node_t new_node, param_t new_param_type); void redirect_incoming_strategy_edges(node_t curr_node, node_t new_node); void redirect_outgoing_strategy_edges(node_t curr_node, node_t new_node); void redirect_generic_param_edges(node_t curr_node, node_t new_node); void add_strategy_edge(node_t from, node_t to); void add_param_edge(node_t from, node_t to); void add_interference_edge(node_t from, node_t to); void update_param_edges(); void update_size(); void clear_interference_edges(node_t n); bool modifies_reg(node_t n, int reg_num, bool check_following_node=false); // Strategy rules bool rule_mov_cst_pop(node_t n, Arch* arch); bool rule_generic_transitivity(node_t n); bool rule_generic_src_transitivity(node_t n); bool rule_generic_adjust_jmp(node_t n, Arch* arch); bool rule_adjust_load(node_t n, Arch* arch); bool rule_adjust_store(node_t n, Arch* arch); // Ordering void compute_dfs_strategy(); void compute_dfs_params(); bool compute_dfs_scheduling(); // Gadget selection bool select_gadgets(GadgetDB& db, Constraint* constraint=nullptr, Arch* arch=nullptr, node_t dfs_idx=-1); ROPChain* get_ropchain(Arch* arch, Constraint* constraint=nullptr); // Scheduling void compute_interference_points(); bool schedule_gadgets(); bool has_dependent_param(node_t node, param_t param); // Copy StrategyGraph* copy(); }; ostream& operator<<(ostream& os, StrategyGraph& graph); #endif ================================================ FILE: libropium/include/symbolic.hpp ================================================ #ifndef SYMBOLIC_H #define SYMBOLIC_H #include "ir.hpp" #include "expression.hpp" #include "simplification.hpp" #include "arch.hpp" using std::tuple; using std::string; /* Semantics ========= */ class Semantics{ public: IRContext* regs; // Takes ownership MemContext* mem; // Takes ownership Semantics(IRContext* regs, MemContext* mem); void simplify(); ~Semantics(); }; ostream& operator<<(ostream&, Semantics& s); /* SymbolicEngine ============== */ class SymbolicEngine{ public: Arch* arch; SymbolicEngine(ArchType arch); ~SymbolicEngine(); Semantics* execute_block(IRBlock* block); }; #endif ================================================ FILE: libropium/include/systems.hpp ================================================ #ifndef SYSTEMS_H #define SYSTEMS_H #include #include "arch.hpp" using std::string; enum class System{ LINUX, WINDOWS, NONE }; // Specification of a syscall class SyscallDef{ public: string name; int nb_args; cst_t num; SyscallDef(string n, cst_t sysn, int nb):name(n), nb_args(nb), num(sysn){}; }; // Get syscall definition by name SyscallDef* get_syscall_def(ArchType arch, System sys, string name); #endif ================================================ FILE: libropium/include/utils.hpp ================================================ #ifndef UTILS_H #define UTILS_H #include #include #include #include "expression.hpp" using std::string; using std::vector; /* ======== Raw gadgets interface ======== */ class RawGadget{ public: RawGadget(){}; RawGadget(string r, uint64_t a):raw(r), addr(a){} string raw; uint64_t addr; }; // Read gadgets from file vector* raw_gadgets_from_file(string filename); // Write gadgets to file from ROPgadget output bool ropgadget_to_file(string filename, string ropgadget_tmp_file, string bin); /* ========= Support for hashing tuples ========== */ #include // function has to live in the std namespace // so that it is picked up by argument-dependent name lookup (ADL). namespace std{ namespace { // Code from boost // Reciprocal of the golden ratio helps spread entropy // and handles duplicates. // See Mike Seymour in magic-numbers-in-boosthash-combine: // https://stackoverflow.com/questions/4948780 template inline void hash_combine(std::size_t& seed, T const& v) { seed ^= hash()(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); } // Recursive template code derived from Matthieu M. template ::value - 1> struct HashValueImpl { static void apply(size_t& seed, Tuple const& tuple) { HashValueImpl::apply(seed, tuple); hash_combine(seed, get(tuple)); } }; template struct HashValueImpl { static void apply(size_t& seed, Tuple const& tuple) { hash_combine(seed, get<0>(tuple)); } }; } template struct hash> { size_t operator()(std::tuple const& tt) const { size_t seed = 0; HashValueImpl >::apply(seed, tt); return seed; } }; } /* ========= Convert tuples to array/vector ================= */ template struct indices { using next = indices; }; template struct build_indices { using type = typename build_indices::type::next; }; template<> struct build_indices<0> { using type = indices<>; }; template using Bare = typename std::remove_cv::type>::type; template constexpr typename build_indices>::value>::type make_indices() { return {}; } template std::array< cst_t, std::tuple_size>::value > to_array(Tuple&& tuple, indices) { using std::get; return {{ get(std::forward(tuple))... }}; } template auto tuple_to_array(Tuple&& tuple) -> decltype( to_array(std::declval(), make_indices()) ) { return to_array(std::forward(tuple), make_indices()); } template vector tuple_to_vector(Tuple&& tuple) { auto array = tuple_to_array(tuple); vector res; for( auto& a : array ){ res.push_back(a); } return res; } /* =============== Printing stuff =============== */ #define DEFAULT_ERROR_COLOR_ANSI "\033[91m" #define DEFAULT_BOLD_COLOR_ANSI "\033[1m" #define DEFAULT_SPECIAL_COLOR_ANSI "\033[93m" #define DEFAULT_PAYLOAD_COLOR_ANSI "\033[96m" #define DEFAULT_EXPLOIT_DESCRIPTION_ANSI "\033[95m" #define DEFAULT_END_COLOR_ANSI "\033[0m" string str_bold(string s); string str_special(string s); string value_to_hex_str(int octets, addr_t addr); void disable_colors(); void enable_colors(); /* ========= Catching ctrl+C ============= */ void set_sigint_handler(); void unset_signint_handler(); bool is_pending_sigint(); void notify_sigint_handled(); #endif ================================================ FILE: libropium/ir/ir.cpp ================================================ #include "ir.hpp" #include "exception.hpp" #include #include #include #include /* ===================================== */ bool iroperation_is_assignment(IROperation& op){ return op == IROperation::ADD || op == IROperation::SUB || op == IROperation::MUL || op == IROperation::MULH || op == IROperation::SMULL || op == IROperation::SMULH || op == IROperation::DIV || op == IROperation::SDIV || op == IROperation::SHL || op == IROperation::SHR || op == IROperation::NEG || op == IROperation::AND || op == IROperation::OR || op == IROperation::XOR || op == IROperation::NOT || op == IROperation::MOD || op == IROperation::SMOD || op == IROperation::MOV || op == IROperation::CONCAT; } bool iroperation_is_memory(IROperation& op){ return op == IROperation::STM || op == IROperation::LDM ; } ostream& operator<<(ostream& os, IROperation& op){ switch(op){ case IROperation::ADD: os << "ADD"; break; case IROperation::SUB: os << "SUB"; break; case IROperation::MUL: os << "MUL"; break; case IROperation::MULH: os << "MUL(h)"; break; case IROperation::SMULL: os << "SMUL(l)"; break; case IROperation::SMULH: os << "SMUL(h)"; break; case IROperation::DIV: os << "DIV"; break; case IROperation::SDIV: os << "SDIV"; break; case IROperation::SHL: os << "SHL"; break; case IROperation::SHR: os << "SHR"; break; case IROperation::NEG: os << "NEG"; break; case IROperation::AND: os << "AND"; break; case IROperation::OR: os << "OR"; break; case IROperation::XOR: os << "XOR"; break; case IROperation::NOT: os << "NOT"; break; case IROperation::MOV: os << "MOV"; break; case IROperation::MOD: os << "MOD"; break; case IROperation::SMOD: os << "MOD"; break; case IROperation::STM: os << "STM"; break; case IROperation::LDM: os << "LDM"; break; case IROperation::BCC: os << "BCC"; break; case IROperation::JCC: os << "JCC"; break; case IROperation::BISZ: os << "BISZ"; break; case IROperation::CONCAT: os << "CONCAT"; break; case IROperation::INT: os << "INT"; break; case IROperation::SYSCALL: os << "SYSCALL"; break; default: os << "???"; break; } return os; } /* ===================================== */ IROperand::IROperand(): type(IROperandType::NONE), _val(0), high(0), low(0), size(0){} IROperand::IROperand(IROperandType t, cst_t cst, exprsize_t h, exprsize_t l): type(t), _val(cst_sign_extend(sizeof(cst_t)*8, cst)), high(h), low(l), size(h-l+1){} bool IROperand::is_cst(){ return type == IROperandType::CST; } bool IROperand::is_var(){ return type == IROperandType::VAR; } bool IROperand::is_tmp(){ return type == IROperandType::TMP; } bool IROperand::is_none(){ return type == IROperandType::NONE; } cst_t IROperand::cst(){ return _val; } IRVar IROperand::var(){ return (IRVar)_val;} IRVar IROperand::tmp(){return (IRVar)_val;} ostream& operator<<(ostream& os, IROperand& op){ switch(op.type){ case IROperandType::CST: os << op.cst(); break; case IROperandType::TMP: os << "TMP_" << op.tmp(); break; case IROperandType::VAR: os << "VAR_" << op.var(); break; case IROperandType::NONE: os << "_" ; break; } os << "[" << op.high << ":" << op.low << "]"; return os; } /* Helpers to create operands */ IROperand ir_cst(cst_t val, exprsize_t high, exprsize_t low){ return IROperand(IROperandType::CST, val, high, low); } IROperand ir_var(cst_t num, exprsize_t high, exprsize_t low){ return IROperand(IROperandType::VAR, num, high, low); } IROperand ir_tmp(cst_t num, exprsize_t high, exprsize_t low){ return IROperand(IROperandType::TMP, num, high, low); } IROperand ir_none(){ return IROperand(); } /* ===================================== */ IRInstruction::IRInstruction(IROperation _op, IROperand _dst, IROperand _src1, addr_t a){ op = _op; dst = _dst; src1 = _src1; src2 = IROperand(); addr = a; } IRInstruction::IRInstruction(IROperation _op, IROperand _dst, IROperand _src1, IROperand _src2, addr_t a){ op = _op; dst = _dst; src1 = _src1; src2 = _src2; addr = a; } bool IRInstruction::reads_var(IRVar var){ if( iroperation_is_assignment(op)){ return (src1.is_var() && src1.var() == var) || (src2.is_var() && src2.var() == var); }else if( iroperation_is_memory(op)){ return (dst.is_var() && dst.var() == var) || (src1.is_var() && src1.var() == var) || (src2.is_var() && src2.var() == var); }else if( op == IROperation::BCC || op == IROperation::JCC){ return (dst.is_var() && dst.var() == var) || (src1.is_var() && src1.var() == var) || (src2.is_var() && src2.var() == var); }else if( op == IROperation::BISZ ){ return src1.is_var() && src1.var() == var; }else{ throw runtime_exception("IRInstruction::reads_var() got unknown IROperation"); } } bool IRInstruction::writes_var(IRVar var){ if( iroperation_is_assignment(op)){ return (dst.is_var() && dst.var() == var); }else if( iroperation_is_memory(op)){ return false; }else if( op == IROperation::BCC || op == IROperation::JCC){ return false; }else if( op == IROperation::BISZ){ return (dst.is_var() && dst.var() == var); }else{ throw runtime_exception("IRInstruction::writes_var() got unknown IROperation"); } } bool IRInstruction::uses_var(IRVar var){ return reads_var(var) || writes_var(var); } bool IRInstruction::reads_tmp(IRVar tmp){ if( iroperation_is_assignment(op)){ return (src1.is_tmp() && src1.tmp() == tmp) || (src2.is_tmp() && src2.tmp() == tmp); }else if( iroperation_is_memory(op)){ return (dst.is_tmp() && dst.tmp() == tmp) || (src1.is_tmp() && src1.tmp() == tmp) || (src2.is_tmp() && src2.tmp() == tmp); }else if( op == IROperation::BCC || op == IROperation::JCC){ return (dst.is_tmp() && dst.tmp() == tmp) || (src1.is_tmp() && src1.tmp() == tmp) || (src2.is_tmp() && src2.tmp() == tmp); }else if( op == IROperation::BISZ ){ return src1.is_tmp() && src1.tmp() == tmp; }else{ throw runtime_exception("IRInstruction::reads_tmp() got unknown IROperation"); } } bool IRInstruction::writes_tmp(IRVar tmp){ if( iroperation_is_assignment(op)){ return (dst.is_tmp() && dst.tmp() == tmp); }else if( iroperation_is_memory(op)){ return false; }else if( op == IROperation::BCC || op == IROperation::JCC){ return false; }else if( op == IROperation::BISZ){ return (dst.is_tmp() && dst.tmp() == tmp); }else{ throw runtime_exception("IRInstruction::writes_tmp() got unknown IROperation"); } } vector IRInstruction::used_vars_read(){ vector res; if( iroperation_is_assignment(op)){ if(src1.is_var()) res.push_back(src1); if( src2.is_var()) res.push_back(src2); }else if( iroperation_is_memory(op) || op == IROperation::BCC || op == IROperation::JCC ){ if(src1.is_var()) res.push_back(src1); if( src2.is_var()) res.push_back(src2); if( dst.is_var() ) res.push_back(dst); }else if( op == IROperation::BISZ ){ if(src1.is_var()) res.push_back(src1); }else if( op == IROperation::INT || op == IROperation::SYSCALL){ // Ignore }else{ throw runtime_exception("IRInstruction::used_vars_read() got unknown IROperation"); } return res; } vector IRInstruction::used_vars_write(){ vector res; if( iroperation_is_assignment(op) || op == IROperation::LDM || op == IROperation::BISZ){ if(dst.is_var()) res.push_back(dst); }else if( op == IROperation::STM || op == IROperation::BCC || op == IROperation::JCC || op == IROperation::INT || op == IROperation::SYSCALL ){ // Ignore those even if they rewrite pc }else{ throw runtime_exception("IRInstruction::used_vars_write() got unknown IROperation"); } return res; } vector IRInstruction::used_tmps_read(){ vector res; if( iroperation_is_assignment(op)){ if(src1.is_tmp()) res.push_back(src1); if( src2.is_tmp()) res.push_back(src2); }else if( iroperation_is_memory(op) || op == IROperation::BCC || op == IROperation::JCC || op == IROperation::INT ){ if(src1.is_tmp()) res.push_back(src1); if( src2.is_tmp()) res.push_back(src2); if( dst.is_tmp() ) res.push_back(dst); }else if( op == IROperation::BISZ ){ if(src1.is_tmp()) res.push_back(src1); }else if( op == IROperation::SYSCALL ){ if( src1.is_tmp() ) res.push_back(src1); }else{ throw runtime_exception("IRInstruction::used_tmps_read() got unknown IROperation"); } return res; } vector IRInstruction::used_tmps_write(){ vector res; if( iroperation_is_assignment(op)){ if(dst.is_tmp()) res.push_back(dst); }else if( iroperation_is_memory(op) || op == IROperation::BCC || op == IROperation::JCC || op == IROperation::INT || op == IROperation::SYSCALL){ // Ignore }else if( op == IROperation::BISZ ){ if(dst.is_tmp()) res.push_back(dst); }else{ throw runtime_exception("IRInstruction::used_tmps_write() got unknown IROperation"); } return res; } ostream& operator<<(ostream& os, IRInstruction& ins){ os << "(0x" << std::hex << ins.addr << ")"; os << "\t" << ins.op << "\t"; if( ins.op == IROperation::BCC ){ os << ins.dst << ",\tbblk_" << ins.src1.cst(); if( !ins.src2.is_none()){ os << ",\t\tbblk_" << ins.src2.cst(); } }else{ os << ins.dst << ",\t" << ins.src1; if( !ins.src2.is_none()){ os << ",\t" << ins.src2; } } os << std::endl; return os; } /* Helpers to create instructions */ IRInstruction ir_add(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::ADD, dst, src1, src2, addr); } IRInstruction ir_sub(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::SUB, dst, src1, src2, addr); } IRInstruction ir_mul(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::MUL, dst, src1, src2, addr); } IRInstruction ir_mulh(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::MULH, dst, src1, src2, addr); } IRInstruction ir_smull(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::SMULL, dst, src1, src2, addr); } IRInstruction ir_smulh(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::SMULH, dst, src1, src2, addr); } IRInstruction ir_div(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::DIV, dst, src1, src2, addr); } IRInstruction ir_sdiv(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::SDIV, dst, src1, src2, addr); } IRInstruction ir_and(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::AND, dst, src1, src2, addr); } IRInstruction ir_or(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::OR, dst, src1, src2, addr); } IRInstruction ir_xor(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::XOR, dst, src1, src2, addr); } IRInstruction ir_shl(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::SHL, dst, src1, src2, addr); } IRInstruction ir_shr(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::SHR, dst, src1, src2, addr); } IRInstruction ir_mod(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::MOD, dst, src1, src2, addr); } IRInstruction ir_smod(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::SMOD, dst, src1, src2, addr); } IRInstruction ir_neg(IROperand dst, IROperand src1, addr_t addr){ return IRInstruction(IROperation::NEG, dst, src1, addr); } IRInstruction ir_not(IROperand dst, IROperand src1, addr_t addr){ return IRInstruction(IROperation::NOT, dst, src1, addr); } IRInstruction ir_ldm(IROperand dst, IROperand src1, addr_t addr){ return IRInstruction(IROperation::LDM, dst, src1, addr); } IRInstruction ir_stm(IROperand dst, IROperand src1, addr_t addr){ return IRInstruction(IROperation::STM, dst, src1, addr); } IRInstruction ir_mov(IROperand dst, IROperand src1, addr_t addr){ return IRInstruction(IROperation::MOV, dst, src1, addr); } IRInstruction ir_bcc(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::BCC, dst, src1, src2, addr); } IRInstruction ir_jcc(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::JCC, dst, src1, src2, addr); } IRInstruction ir_bisz(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::BISZ, dst, src1, src2, addr); } IRInstruction ir_concat(IROperand dst, IROperand src1, IROperand src2, addr_t addr){ return IRInstruction(IROperation::CONCAT, dst, src1, src2, addr); } IRInstruction ir_int(IROperand num, IROperand ret, addr_t addr){ return IRInstruction(IROperation::INT, num, ret, addr); } IRInstruction ir_syscall(IROperand type, IROperand ret, addr_t addr){ return IRInstruction(IROperation::SYSCALL, type, ret, addr); } /* ====================================== */ IRContext::IRContext():_var(nullptr), _nb_var(0){} IRContext::IRContext(IRVar nb_var){ _var = new Expr[nb_var]{nullptr}; _nb_var = nb_var; } IRContext::~IRContext(){ delete [] _var; _var = nullptr; } int IRContext::nb_vars(){ return _nb_var; } void IRContext::set(IRVar num, Expr e){ if( num >= _nb_var ){ throw ir_exception("IRContext::set(): Invalid register argument"); } _var[num] = e; } Expr IRContext::get(IRVar num){ if( num >= _nb_var ){ throw ir_exception("IRContext::get(): Invalid register argument"); } return _var[num]; } ostream& operator<<(ostream& os, IRContext& ctx){ for( int i = 0; i < ctx.nb_vars(); i++){ os << "Var_" << i << " : " << ctx.get(i) << std::endl; } return os; } /* ====================================== */ MemContext::MemContext(){ simp = NewDefaultExprSimplifier(); } void MemContext::write(Expr addr, Expr expr){ unordered_map::iterator it; addr = simp->simplify(addr); // Simplify address to check for collisions if( (it = writes.find(addr)) != writes.end()){ if( it->second->size <= expr->size ){ writes[addr] = expr; }else{ writes[addr] = concat(extract(it->second, it->second->size-1, expr->size), expr); } }else{ writes[addr] = expr; } } Expr MemContext::read(Expr addr, int octets){ unordered_map::iterator it; addr = simp->simplify(addr); if( (it = writes.find(addr)) != writes.end()){ if( it->second->size/8 == octets ){ return it->second; }else if( it->second->size/8 > octets ){ return extract(it->second, (octets*8)-1, 0); }else{ return concat(exprmem(octets*8 - it->second->size, addr + it->second->size), it->second); } }else{ return exprmem(octets*8, addr); } } MemContext::~MemContext(){ delete simp; simp = nullptr; } ostream& operator<<(ostream& os, MemContext& ctx){ for( auto w : ctx.writes ){ os << "MEM[" << w.first << "] : " << w.second << std::endl; } return os; } /* ====================================== */ using std::max; using std::min; IRBlock::IRBlock(string n, addr_t start, addr_t end): name(n), ir_size(0), raw_size(0), start_addr(start), end_addr(end), _nb_tmp_vars(0), _nb_instr(0), _nb_instr_ir(0), known_max_sp_inc(false), ends_with_int80(false), ends_with_syscall(false){ branch_target[0] = 0; branch_target[1] = 0; memset(dereferenced_regs, false, sizeof(dereferenced_regs)); } void IRBlock::add_instr(IRBasicBlockId bblock, IRInstruction instr){ assert(_bblocks.size() > bblock && "Adding instruction to basic block that doesn't exist" ); _bblocks[bblock].push_back(instr); ir_size++; } IRBasicBlockId IRBlock::new_bblock(){ _bblocks.push_back(IRBasicBlock()); return (IRBasicBlockId)(_bblocks.size()-1); } IRBasicBlock& IRBlock::get_bblock(IRBasicBlockId id){ return _bblocks[id]; } int IRBlock::nb_bblocks(){ return _bblocks.size(); } vector& IRBlock::bblocks(){ return _bblocks; }; ostream& operator<<(ostream& os, IRBlock& blk){ IRBasicBlock::iterator it; os << std::endl << blk.name; for( int i = 0; i < blk.bblocks().size(); i++){ os << "\n\tbblk_" << i << ":" << std::endl; for( it = blk.bblocks()[i].begin(); it != blk.bblocks()[i].end(); it++){ os << "\t" << *it; } } return os; } ================================================ FILE: libropium/ropchain/assertion.cpp ================================================ #include "assertion.hpp" #include void ValidPointers::add_valid_pointer(int reg){ _regs.push_back(reg); } bool ValidPointers::is_valid_pointer(int reg){ return std::find(_regs.begin(), _regs.end(), reg) != _regs.end(); } void ValidPointers::clear(){ _regs.clear(); } void Assertion::clear(){ valid_pointers.clear(); } ================================================ FILE: libropium/ropchain/constraint.cpp ================================================ #include "constraint.hpp" #include #include /* =============== Bad Bytes ================= */ void BadBytes::add_bad_byte(unsigned char byte){ _bad_bytes.push_back(byte); } void BadBytes::clear(){ _bad_bytes.clear(); } bool BadBytes::is_valid_byte(unsigned char byte){ return (std::find(_bad_bytes.begin(), _bad_bytes.end(), byte) == _bad_bytes.end()); } unsigned char BadBytes::get_valid_byte(){ for( unsigned char byte = 0xff; byte >= 0; byte--){ if( is_valid_byte(byte) ){ return byte; } } throw runtime_exception("BadBytes::get_valid_byte(): all bytes are invalid!"); } addr_t BadBytes::get_valid_padding(int nb_bytes){ unsigned char byte = get_valid_byte(); addr_t res = 0; for(; nb_bytes > 0; nb_bytes--){ res = (res<<8) + byte; } return res; } bool BadBytes::is_valid_address(addr_t addr, int arch_bytes){ for( int i = 0; i < arch_bytes; i++){ if( ! is_valid_byte(addr & 0xff)){ return false; } addr >>= 8; } return true; } addr_t BadBytes::get_valid_address(Gadget* gadget, int arch_bytes){ for( addr_t addr : gadget->addresses ){ if( is_valid_address(addr, arch_bytes) ){ return addr; } } throw runtime_exception("BadBytes::get_valid_address(): all addresses are invalid!"); } bool BadBytes::check(Gadget* gadget, int arch_bytes){ for( addr_t addr : gadget->addresses ){ if( is_valid_address(addr, arch_bytes) ){ return true; } } return false; } /* ================ Keep Regs ================= */ void KeepRegs::add_keep_reg(int reg_num){ _keep.push_back(reg_num); } void KeepRegs::clear(){ _keep.clear(); } vector& KeepRegs::regs_to_keep(){ return _keep; } bool KeepRegs::is_kept(int reg_num){ return (std::find(_keep.begin(), _keep.end(), reg_num) != _keep.end()); } bool KeepRegs::check(Gadget* gadget){ for( int reg : _keep ){ if( gadget->modified_regs[reg]) return false; } return true; } /* ================ Memory Safety ================= */ MemSafety::MemSafety(){ _force_safe = true; // Enforce pointer safety by default memset(_safe_reg_pointers, false, sizeof(_safe_reg_pointers)); } void MemSafety::force_safe(){ _force_safe = true; } void MemSafety::enable_unsafe(){ _force_safe = false; } void MemSafety::add_safe_reg(int reg_num){ _safe_reg_pointers[reg_num] = true; } bool MemSafety::is_enforced(){ return _force_safe; } void MemSafety::clear(){ _force_safe = true; memset(_safe_reg_pointers, false, sizeof(_safe_reg_pointers)); } bool MemSafety::check(Gadget* gadget, int arch_nb_regs, Assertion* assertion){ if( !_force_safe) return true; for( int i = 0; i < arch_nb_regs; i++){ if( gadget->dereferenced_regs[i] ){ if( !_safe_reg_pointers[i] && (assertion == nullptr || !assertion->valid_pointers.is_valid_pointer(i))) return false; } } return true; } /* =============== Full Constraint =================== */ void Constraint::clear(){ bad_bytes.clear(); keep_regs.clear(); mem_safety.clear(); } bool Constraint::check(Gadget* gadget, Arch* arch, Assertion* assertion){ return bad_bytes.check(gadget, arch->octets) && keep_regs.check(gadget) && mem_safety.check(gadget, arch->nb_regs, assertion); } ================================================ FILE: libropium/ropchain/gadget.cpp ================================================ #include "ropchain.hpp" #include #include #include using std::unordered_map; Gadget::Gadget():semantics(nullptr), bin_num(-1), branch_type(BranchType::NONE){ memset(modified_regs, 0, sizeof(modified_regs)); } Gadget::~Gadget(){ delete semantics; semantics = nullptr; } void Gadget::add_address(addr_t addr){ addresses.push_back(addr); } void Gadget::print(ostream& os){ os << "Gadget: " << asm_str << std::endl; for( int i = 0; i < semantics->regs->nb_vars(); i++){ if( modified_regs[i] ) os << "Reg_" << i << " : " << semantics->regs->get(i) << std::endl; } os << *(semantics->mem) << std::endl; } ostream& operator<<(ostream& os, Gadget& g){ g.print(os); return os; } bool Gadget::lthan(Gadget& other){ if( nb_instr != other.nb_instr ){ return nb_instr < other.nb_instr; }else{ return nb_instr_ir < other.nb_instr_ir; } } ================================================ FILE: libropium/ropchain/ropchain.cpp ================================================ #include "ropchain.hpp" #include "utils.hpp" #include #include ROPChain::ROPChain(Arch* a):arch(a){} void ROPChain::add_gadget(addr_t addr, Gadget* gadget){ items.push_back(ROPItem(addr, gadget)); } void ROPChain::add_padding(cst_t value, string msg){ items.push_back(ROPItem(ROPItemType::PADDING, value, msg)); } void ROPChain::add_gadget_address(cst_t value, string msg){ items.push_back(ROPItem(ROPItemType::GADGET_ADDRESS, value, msg)); } void ROPChain::add_chain(ROPChain& other){ for( ROPItem& item : other.items ){ items.push_back(item); } } int ROPChain::len(){ return items.size(); } void ROPChain::print_pretty(ostream& os, string tab){ for(ROPItem& item : items){ if( item.type == ROPItemType::GADGET ){ os << tab << str_special(value_to_hex_str(arch->octets, item.addr)) << " (" << str_bold(item.gadget->asm_str) << ")" << std::endl; }else if( item.type == ROPItemType::PADDING ){ os << tab << str_special(value_to_hex_str(arch->octets, item.value)); if( !item.msg.empty() ) os << " (" << str_bold(item.msg) << ")"; os << std::endl; }else if( item.type == ROPItemType::GADGET_ADDRESS ){ os << tab << str_special(value_to_hex_str(arch->octets, item.value)); if( !item.msg.empty() ) os << " (" << str_bold(item.msg) << ")"; os << std::endl; }else{ os << tab << "Unsupported " << std::endl; } } } void ROPChain::print_python(ostream& os, string tab){ string pack, endian, p; addr_t gadgets_offset = 0; // Set packing strings, endianness, etc p = "p"; if( arch->octets == 4 ) endian = "'octets == 8 ) endian = "'octets, item.addr)) << " + off) # " << str_bold(item.gadget->asm_str) << std::endl; }else if( item.type == ROPItemType::PADDING ){ os << tab << pack << str_special(value_to_hex_str(arch->octets, item.value)) << ")"; if( !item.msg.empty()) os << " # " << str_bold(item.msg); os << std::endl; }else if( item.type == ROPItemType::GADGET_ADDRESS ){ os << tab << pack << str_special(value_to_hex_str(arch->octets, item.value)) << " + off)"; if( !item.msg.empty()) os << " # " << str_bold(item.msg); os << std::endl; }else{ os << tab << "[Unsupported item]" << std::endl; } } } void append_value_to_bytes(vector& bytes, addr_t val, int nb_octets){ // Assume little endian for( int i = 0; i < nb_octets; i++){ bytes.push_back((uint8_t)(val & 0xff)); val >>= 8; } } void ROPChain::dump_raw(vector& bytes){ for(ROPItem& item : items){ if( item.type == ROPItemType::GADGET ){ append_value_to_bytes(bytes, item.addr, arch->octets); }else if( item.type == ROPItemType::PADDING ){ append_value_to_bytes(bytes, item.value, arch->octets); }else if( item.type == ROPItemType::GADGET_ADDRESS ){ append_value_to_bytes(bytes, item.value, arch->octets); }else{ throw runtime_exception("ROPChain::print_raw() got unsupported item type"); } } } ostream& operator<<(ostream& os, ROPChain& ropchain){ ropchain.print_pretty(os); return os; } ================================================ FILE: libropium/symbolic/expression.cpp ================================================ #include "expression.hpp" #include "exception.hpp" #include #include #include "murmur3.h" #include #include #include using std::make_shared; using std::stringstream; /* Expression hashes In order to enabe quick equality checks between expressions, each expression has a 32-bit hash that 'uniquely' identifies it (colisions are estimated unlikely enough to be ignored). The hash is not computed at expression creation. Some benchmarks seemed to indicate that it was increasing the creation time by about 80%. For this reason, hashes are computed dynamically when needed. The current implementation uses the murmur3 hash function C implementation available on https://github.com/PeterScott/murmur3. Hash computation: Several util functions named "prepare_hash_with_" enable to add data to the input buffer, and the exprhash() function computes the hash of the buffer contents. */ #define MAXLEN_HASH_IN 1024 /* Set of functions to add a value to be hashed in the hash input buffer * 'hash_in' and returns the number of bytes added */ inline int prepare_hash_with_i64(uint8_t* hash_in, int64_t val, int index=0){ *(int64_t*)(hash_in+index) = val; return index + 8; } inline int prepare_hash_with_str(uint8_t* hash_in, const string& str, int index=0){ strncpy((char*)hash_in+index, str.data(), str.length()); return index + str.length(); } inline int prepare_hash_with_i32(uint8_t* hash_in, int32_t val, int index=0){ *(int32_t*)(hash_in+index) = val; return (index + 4); } inline int prepare_hash_with_op(uint8_t* hash_in, Op op, int index=0){ *((uint8_t*)((char*)hash_in+index)) = static_cast(op); return index + 1; } /* Hash the currently prepared buffer */ hash_t exprhash(void* hash_in, int len, uint32_t seed){ unsigned char hash_out[4]; MurmurHash3_x86_32(hash_in, len, seed, hash_out); return *((hash_t*)hash_out); } /* Implementation of expression classes */ // ================================== ExprObject::ExprObject(ExprType t, exprsize_t s, bool _is_simp): type(t), size(s), _hashed(false), _hash(0), _simplified_expr(nullptr), _is_simplified(_is_simp), _concrete_ctx_id(-1){} bool ExprObject::is_cst(){return type == ExprType::CST;} bool ExprObject::is_var(){return type == ExprType::VAR;} bool ExprObject::is_mem(){return type == ExprType::MEM;} bool ExprObject::is_unop(Op op){return false;} bool ExprObject::is_binop(Op op){return false;} bool ExprObject::is_extract(){return type == ExprType::EXTRACT;} bool ExprObject::is_concat(){return type == ExprType::CONCAT;} bool ExprObject::is_bisz(){return type == ExprType::BISZ;} bool ExprObject::is_unknown(){return type == ExprType::UNKNOWN;} cst_t ExprObject::concretize(VarContext* ctx ){throw runtime_exception("Can not concretize base class ExprObject");} bool ExprObject::eq(Expr other){return hash() == other->hash();} bool ExprObject::neq(Expr other){return hash() != other->hash();} bool ExprObject::inf(Expr e2){ if( type != e2->type ){ return type < e2->type; }else{ switch(type){ case ExprType::CST: return cst() < e2->cst(); case ExprType::VAR: return name().compare(e2->name()) > 0; case ExprType::MEM: return args[0] < e2->args[0]; case ExprType::UNOP: return( op() < e2->op() || args[0]->inf(e2->args[0])); case ExprType::BINOP: if( op() == e2->op() ){ if( args[0]->eq(e2->args[0]) ) return args[1]->inf(e2->args[1]); else return args[0]->inf(e2->args[0]); }else return op() < e2->op(); case ExprType::EXTRACT: case ExprType::CONCAT: for( int i = 0; i < (this->is_extract()?3:2); i++){ if( args[i]->eq(e2->args[i]) ) continue; return args[i]->inf(e2->args[i]); } return false; case ExprType::BISZ: return args[0]->inf(e2->args[0]); case ExprType::UNKNOWN: return false; default: throw runtime_exception("ExprObject::inf() got unsupported ExprType"); } } } void ExprObject::replace_var_name(string& curr_name, string& new_name){ switch(type){ case ExprType::CST: return; case ExprType::VAR: if (name() == curr_name) replace_name(new_name); return; case ExprType::MEM: case ExprType::UNOP: case ExprType::BISZ: return args[0]->replace_var_name(curr_name, new_name); case ExprType::BINOP: case ExprType::EXTRACT: case ExprType::CONCAT: args[0]->replace_var_name(curr_name, new_name); args[1]->replace_var_name(curr_name, new_name); return; case ExprType::UNKNOWN: return; default: throw runtime_exception("ExprObject::replace_var_name() got unsupported ExprType"); } } // ================================== ExprCst::ExprCst(exprsize_t s, cst_t c): ExprObject(ExprType::CST, s, true){ _cst = cst_sign_extend(s, c); if( s > 64 ){ throw expression_exception(QuickFmt() << "Cannot create constant expression of size > 64 (got " << std::dec << s << ")" >> QuickFmt::to_str); } } hash_t ExprCst::hash(){ unsigned char hash_in[MAXLEN_HASH_IN]; if( !_hashed ){ _hash = exprhash(hash_in, prepare_hash_with_i64(hash_in, _cst), size); _hashed = true; } return _hash; } cst_t ExprCst::cst(){ return _cst; } cst_t ExprCst::concretize(VarContext* ctx){return _cst;} void ExprCst::print(ostream& os){os << std::showbase << cst_sign_trunc(size, _cst) << std::noshowbase;} Expr ExprCst::copy(){ return exprcst(size, _cst); } // ================================== ExprVar::ExprVar(exprsize_t s, string n, int num): ExprObject(ExprType::VAR, s, true), _name(n), _num(num){ if( s > 64 ){ throw expression_exception(QuickFmt() << "Cannot create symbolic variables of size > 64 (got " << std::dec << s << ")" >> QuickFmt::to_str); } assert( n.size() <= MAXLEN_HASH_IN ); } hash_t ExprVar::hash(){ unsigned char hash_in[MAXLEN_HASH_IN]; if( !_hashed ){ _hash = exprhash(hash_in, prepare_hash_with_str(hash_in, _name),size); _hashed = true; } return _hash; } bool ExprVar::is_reg(int reg){ return _num == reg; } int ExprVar::reg(){ return _num;} cst_t ExprVar::concretize(VarContext* ctx){ if( ctx == nullptr){ throw expression_exception("Cannot concretize symbolic variable without supplying a context"); } if( _concrete_ctx_id == ctx->id ) return _concrete; else{ _concrete = ctx->get(_name); _concrete_ctx_id = ctx->id; } return _concrete; } const string& ExprVar::name(){ return _name; } void ExprVar::replace_name(string& new_name){ _name = new_name; } void ExprVar::print(ostream& os){os << _name;} Expr ExprVar::copy(){ return exprvar(size, _name); } // ================================== ExprMem::ExprMem(exprsize_t s, Expr addr): ExprObject(ExprType::MEM, s, false) { args.push_back(addr); } hash_t ExprMem::hash(){ unsigned char hash_in[MAXLEN_HASH_IN]; if( !_hashed ){ _hash = exprhash(hash_in, prepare_hash_with_i32(hash_in, args[0]->hash()), size); _hashed = true; } return _hash; } cst_t ExprMem::concretize(VarContext* ctx){ throw runtime_exception("concretize() not imlemented for memory expressions!"); } void ExprMem::print(ostream& os){ os << "@" << std::dec << size << "[" << std::hex << args.at(0) << "]"; } Expr ExprMem::copy(){ return exprmem(size, args[0]->copy()); } // ================================== ExprUnop::ExprUnop(Op o, Expr arg): ExprObject(ExprType::UNOP, arg->size), _op(o){ args.push_back(arg); } hash_t ExprUnop::hash(){ unsigned char hash_in[MAXLEN_HASH_IN]; if( !_hashed ){ _hash = exprhash(hash_in, prepare_hash_with_i32(hash_in, args[0]->hash(), prepare_hash_with_op(hash_in, _op)), size); _hashed = true; } return _hash; } Op ExprUnop::op(){ return _op;} void ExprUnop::print(ostream& os){ os << op_to_str(_op) << std::hex; args.at(0)->print(os); } bool ExprUnop::is_unop(Op op){ if( op == Op::NONE ) return true; else return op == _op; } cst_t ExprUnop::concretize(VarContext* ctx){ if( ctx != nullptr && _concrete_ctx_id == ctx->id ) return _concrete; else{ switch(_op){ case Op::NEG: _concrete =cst_sign_extend(size, -(args[0]->concretize(ctx))); break; case Op::NOT: _concrete =cst_sign_extend(size, ~(args[0]->concretize(ctx))); break; default: throw runtime_exception("Missing case in ExprUnop::concretize()"); } if( ctx != nullptr ){ _concrete_ctx_id = ctx->id; } _concrete = cst_sign_extend(size, _concrete); } return _concrete; } Expr ExprUnop::copy(){ return exprunop(_op, args[0]->copy()); } // ================================== ExprBinop::ExprBinop(Op o, Expr left, Expr right): ExprObject(ExprType::BINOP, left->size), _op(o){ if( left->size != right->size ){ throw expression_exception(QuickFmt() << "Cannot use binary operator on expressions of different sizes (got " << left->size << " and " << right->size << ")" >> QuickFmt::to_str); } args.push_back(left); args.push_back(right); } hash_t ExprBinop::hash(){ unsigned char hash_in[MAXLEN_HASH_IN]; if( !_hashed ){ _hash = exprhash(hash_in, prepare_hash_with_i32(hash_in, args[1]->hash(), prepare_hash_with_op(hash_in, _op, prepare_hash_with_i32(hash_in, args[0]->hash()))), size); _hashed = true; } return _hash; } Op ExprBinop::op(){ return _op;} void ExprBinop::get_associative_args(Op o, vector& vec){ if( _op == o ){ if( args[0]->is_binop() && args[0]->op() == o ) args[0]->get_associative_args(o, vec); else vec.push_back(args[0]); if( args[1]->is_binop(o) ) args[1]->get_associative_args(o, vec); else vec.push_back(args[1]); } /* No else statement * This function should never be called recursively when the operand * is not equal to the argument 'o'. The reason is that leaf expressions * (i.e that are not from the requested operator) cannot return shared_ptr * to themselves without loosing the type information. So all checks are done * by the enclosing binary operations */ } void ExprBinop::get_left_associative_args(Op o, vector& vec, Expr& leftmost){ if( _op == o ){ vec.push_back(args[1]); if( args[0]->is_binop(o)) args[0]->get_left_associative_args(o, vec, leftmost ); else leftmost = args[0]; }else{ leftmost = make_shared(*this); } } void ExprBinop::print(ostream& os){ os << "(" << std::hex; args.at(0)->print(os); os << op_to_str(_op) << std::hex; args.at(1)->print(os); os << ")"; } bool ExprBinop::is_binop(Op op){ if( op == Op::NONE ) return true; else return op == _op; } cst_t ExprBinop::concretize(VarContext* ctx){ if( ctx != nullptr && _concrete_ctx_id == ctx->id ) return _concrete; else{ switch(_op){ case Op::ADD: _concrete = (args[0]->concretize(ctx) + args[1]->concretize(ctx)); break; case Op::MUL: _concrete = ((ucst_t)args[0]->concretize(ctx) * (ucst_t)args[1]->concretize(ctx)); break; case Op::MULH: _concrete = (cst_t)(((__uint128_t)cst_sign_trunc(args[0]->size, args[0]->concretize(ctx)) * cst_sign_trunc(args[1]->size, (__uint128_t)args[1]->concretize(ctx))) >> size ); break; case Op::DIV: _concrete = ((ucst_t)cst_sign_trunc(args[0]->size, args[0]->concretize(ctx)) / (ucst_t)cst_sign_trunc(args[1]->size, args[1]->concretize(ctx))); break; case Op::SDIV: _concrete = (args[0]->concretize(ctx) / args[1]->concretize(ctx)); break; case Op::AND: _concrete = (args[0]->concretize(ctx) & args[1]->concretize(ctx)); break; case Op::OR: _concrete = (args[0]->concretize(ctx) | args[1]->concretize(ctx)); break; case Op::XOR: _concrete = (args[0]->concretize(ctx) ^ args[1]->concretize(ctx)); break; case Op::MOD: _concrete = ((ucst_t)args[0]->concretize(ctx) % (ucst_t)args[1]->concretize(ctx)); break; case Op::SMOD: _concrete = (args[0]->concretize(ctx) % args[1]->concretize(ctx)); break; case Op::SMULL: _concrete = (cst_t)((__int128_t)args[0]->concretize(ctx) * args[1]->concretize(ctx)); break; case Op::SMULH: _concrete = (cst_t)(((__int128_t)args[0]->concretize(ctx) * args[1]->concretize(ctx)) >> size); break; case Op::SHL: if( cst_sign_trunc(args[1]->size, args[1]->concretize(ctx)) >= args[0]->size ){ _concrete = 0; }else{ _concrete = ((ucst_t)cst_sign_trunc(args[0]->size, args[0]->concretize(ctx))) << ((ucst_t)args[1]->concretize(ctx)); } break; case Op::SHR: if( cst_sign_trunc(args[1]->size, args[1]->concretize(ctx)) >= args[0]->size ){ _concrete = 0; }else{ _concrete = ((ucst_t)cst_sign_trunc(args[0]->size, args[0]->concretize(ctx))) >> ((ucst_t)args[1]->concretize(ctx)); } break; default: throw runtime_exception("Missing case in ExprBinop::concretize()"); } if( ctx != nullptr ){ _concrete_ctx_id = ctx->id; } _concrete =cst_sign_extend(size, _concrete); } return _concrete; } Expr ExprBinop::copy(){ return exprbinop(_op, args[0]->copy(), args[1]->copy()); } // ================================== ExprExtract::ExprExtract(Expr arg, Expr higher, Expr lower): ExprObject(ExprType::EXTRACT, 0){ assert(higher->is_cst() && lower->is_cst() && "Cannot create extract with bit parameters that are not constant expressions"); if( (ucst_t)higher->cst() < (ucst_t)lower->cst() ){ throw expression_exception("Can not use Extract() with higher bit smaller than lower bit"); } if( (ucst_t)higher->cst() >= arg->size ){ throw expression_exception(QuickFmt() << "Can not extract bit " << higher->cst() << " from expression of size " << arg->size >> QuickFmt::to_str ); } args.push_back(arg); args.push_back(higher); args.push_back(lower); size = (ucst_t)higher->cst() - (ucst_t)lower->cst() + 1; } hash_t ExprExtract::hash(){ unsigned char hash_in[MAXLEN_HASH_IN]; if( !_hashed ){ _hash = exprhash(hash_in, prepare_hash_with_i32(hash_in, args[2]->hash(), prepare_hash_with_i32(hash_in, args[1]->hash(), prepare_hash_with_i32(hash_in, args[0]->hash()))), size); _hashed = true; } return _hash; } void ExprExtract::print(ostream& os){ os << std::hex; args.at(0)->print(os); os << "[" << std::dec; args.at(1)->print(os); os << ":" << std::dec; args.at(2)->print(os); os << "]"; } cst_t ExprExtract::concretize(VarContext* ctx){ cst_t high, low; ucst_t mask; if( ctx != nullptr && _concrete_ctx_id == ctx->id ) return _concrete; else{ high = args[1]->concretize(ctx); low = args[2]->concretize(ctx); if( high == 63 ){ mask = 0xffffffffffffffff; }else{ mask = (((cst_t)1 << (high+1))-1); } _concrete = ((ucst_t)args[0]->concretize(ctx) & mask) >> (ucst_t)low; if( ctx != nullptr ) _concrete_ctx_id = ctx->id; _concrete = cst_sign_extend(size, _concrete); } return _concrete; } Expr ExprExtract::copy(){ return extract(args[0]->copy(), args[1]->copy(), args[2]->copy()); } // ================================== ExprConcat::ExprConcat(Expr upper, Expr lower): ExprObject(ExprType::CONCAT, upper->size+lower->size){ args.push_back(upper); args.push_back(lower); } hash_t ExprConcat::hash(){ unsigned char hash_in[MAXLEN_HASH_IN]; if( !_hashed ){ _hash = exprhash(hash_in, prepare_hash_with_i32(hash_in, args[1]->hash(), prepare_hash_with_i32(hash_in, args[0]->hash())), size); _hashed = true; } return _hash; } void ExprConcat::print(ostream& os){ os << "{" << std::hex; args.at(0)->print(os); os << "," << std::hex; args.at(1)->print(os); os << "}"; } cst_t ExprConcat::concretize(VarContext* ctx){ cst_t upper, lower; if( ctx != nullptr && _concrete_ctx_id == ctx->id ) return _concrete; else{ upper = args[0]->concretize(ctx); lower = args[1]->concretize(ctx); _concrete = cst_sign_extend(size, (((ucst_t)upper)<<(ucst_t)args[1]->size) | (ucst_t)cst_sign_trunc(args[1]->size, lower)); if( ctx != nullptr ) _concrete_ctx_id = ctx->id; _concrete =cst_sign_extend(size, _concrete); } return _concrete; } Expr ExprConcat::copy(){ return concat(args[0]->copy(), args[1]->copy()); } /* ===================================== */ ExprBisz::ExprBisz(exprsize_t _size, Expr cond, cst_t mode): ExprObject(ExprType::BISZ, _size){ if( mode != 0 && mode != 1){ throw expression_exception(QuickFmt() << "Can only use Bisz() with mode 0 or 1 (got " << mode << ")" >> QuickFmt::to_str ); } args.push_back(cond); _mode = mode; } hash_t ExprBisz::hash(){ unsigned char hash_in[MAXLEN_HASH_IN]; if( !_hashed ){ _hash = exprhash(hash_in, prepare_hash_with_i32(hash_in, args[0]->hash(), prepare_hash_with_str(hash_in, (_mode)?"BISZ1":"BISZ0")), size); _hashed = true; } return _hash; } cst_t ExprBisz::mode(){return _mode;} void ExprBisz::print(ostream& out){ if( _mode ){ out << "bisz<1>(" << std::hex; args[0]->print(out); out << ")"; }else{ out << "bisz<0>(" << std::hex; args[0]->print(out); out << ")"; } } cst_t ExprBisz::concretize(VarContext* ctx){ if( ctx != nullptr && _concrete_ctx_id == ctx->id ) return _concrete; else{ _concrete = (args[0]->concretize(ctx) == 0)? _mode : _mode^1; if( ctx != nullptr ) _concrete_ctx_id = ctx->id; _concrete =cst_sign_extend(size, _concrete); } return _concrete; } Expr ExprBisz::copy(){ return bisz(size, args[0]->copy(), _mode); } // ================================== ExprUnknown::ExprUnknown(exprsize_t s): ExprObject(ExprType::UNKNOWN, s){} hash_t ExprUnknown::hash(){ unsigned char hash_in[MAXLEN_HASH_IN]; if( !_hashed ){ _hash = exprhash(hash_in, prepare_hash_with_i32(hash_in, 0x77777777), size); _hashed = true; } return _hash; } void ExprUnknown::print(ostream& os){ os << "???"; } cst_t ExprUnknown::concretize(VarContext* ctx){ throw runtime_exception("Can not concretize ExprUnknown instance"); } // ================================== /* Helper functions to create new expressions */ // Create from scratch Expr exprcst(exprsize_t size, cst_t cst){ return make_shared(size, cst); } Expr exprvar(exprsize_t size, string name, int num){ return make_shared(size, name, num); } Expr exprmem(exprsize_t size, Expr addr){ return make_shared(size, addr); } Expr exprbinop(Op op, Expr left, Expr right){ return expr_canonize(make_shared(op, left, right)); } Expr exprunop(Op op, Expr arg){ return expr_canonize(make_shared(op, arg)); } Expr extract(Expr arg, unsigned long higher, unsigned long lower){ return make_shared(arg, exprcst(sizeof(cst_t)*8, higher), exprcst(sizeof(cst_t)*8, lower)); } Expr extract(Expr arg, Expr higher, Expr lower){ return make_shared(arg, higher, lower); } Expr concat(Expr upper, Expr lower){ return expr_canonize(make_shared(upper, lower)); } Expr exprunknown(exprsize_t size){ return make_shared(size); } // Binary operations Expr operator+(Expr left, Expr right){ return exprbinop(Op::ADD, left, right); } Expr operator+(Expr left, cst_t right ){ return exprbinop(Op::ADD, left, exprcst(left->size, right)); } Expr operator+(cst_t left, Expr right){ return exprbinop(Op::ADD, exprcst(right->size, left), right); } Expr operator-(Expr left, Expr right){ return exprbinop(Op::ADD, left, make_shared(Op::NEG,right)); } Expr operator-(Expr left, cst_t right ){ return left - exprcst(left->size, right); } Expr operator-(cst_t left, Expr right){ return exprcst(right->size, left) - right; } Expr operator*(Expr left, Expr right){ return exprbinop(Op::MUL, left, right); } Expr operator*(Expr left, cst_t right ){ return exprbinop(Op::MUL, left, exprcst(left->size, right)); } Expr operator*(cst_t left, Expr right){ return exprbinop(Op::MUL, exprcst(right->size, left), right); } Expr operator/(Expr left, Expr right){ return exprbinop(Op::DIV, left, right); } Expr operator/(Expr left, cst_t right ){ return exprbinop(Op::DIV, left, exprcst(left->size, right)); } Expr operator/(cst_t left, Expr right){ return exprbinop(Op::DIV, exprcst(right->size, left), right); } Expr operator&(Expr left, Expr right){ return exprbinop(Op::AND, left, right); } Expr operator&(Expr left, cst_t right ){ return exprbinop(Op::AND, left, exprcst(left->size, right)); } Expr operator&(cst_t left, Expr right){ return exprbinop(Op::AND, exprcst(right->size, left), right); } Expr operator|(Expr left, Expr right){ return exprbinop(Op::OR, left, right); } Expr operator|(Expr left, cst_t right ){ return exprbinop(Op::OR, left, exprcst(left->size, right)); } Expr operator|(cst_t left, Expr right){ return exprbinop(Op::OR, exprcst(right->size, left), right); } Expr operator^(Expr left, Expr right){ return exprbinop(Op::XOR, left, right); } Expr operator^(Expr left, cst_t right ){ return exprbinop(Op::XOR, left, exprcst(left->size, right)); } Expr operator^(cst_t left, Expr right){ return exprbinop(Op::XOR, exprcst(right->size, left), right); } Expr operator%(Expr left, Expr right){ return exprbinop(Op::MOD, left, right); } Expr operator%(Expr left, cst_t right ){ return exprbinop(Op::MOD, left, exprcst(left->size, right)); } Expr operator%(cst_t left, Expr right){ return exprbinop(Op::MOD, exprcst(right->size, left), right); } Expr operator<<(Expr left, Expr right){ return exprbinop(Op::SHL, left, right); } Expr operator<<(Expr left, cst_t right ){ return exprbinop(Op::SHL, left, exprcst(left->size, right)); } Expr operator<<(cst_t left, Expr right){ return exprbinop(Op::SHL, exprcst(right->size, left), right); } Expr operator>>(Expr left, Expr right){ return exprbinop(Op::SHR, left, right); } Expr operator>>(Expr left, cst_t right ){ return exprbinop(Op::SHR, left, exprcst(left->size, right)); } Expr operator>>(cst_t left, Expr right){ return exprbinop(Op::SHR, exprcst(right->size, left), right); } Expr shl(Expr arg, Expr shift){ return exprbinop(Op::SHL, arg, shift); } Expr shl(Expr arg, cst_t shift){ return exprbinop(Op::SHL, arg, exprcst(arg->size,shift)); } Expr shl(cst_t arg, Expr shift){ return exprbinop(Op::SHL, exprcst(shift->size,arg), shift); } Expr shr(Expr arg, Expr shift){ return exprbinop(Op::SHR, arg, shift); } Expr shr(Expr arg, cst_t shift){ return exprbinop(Op::SHR, arg, exprcst(arg->size,shift)); } Expr shr(cst_t arg, Expr shift){ return exprbinop(Op::SHR, exprcst(shift->size,arg), shift); } Expr sdiv(Expr left, Expr right){ return exprbinop(Op::SDIV, left, right); } Expr sdiv(Expr left, cst_t right){ return exprbinop(Op::SDIV, left, exprcst(left->size, right)); } Expr sdiv(cst_t left, Expr right){ return exprbinop(Op::SDIV, exprcst(right->size, left), right); } Expr smod(Expr left, Expr right){ return exprbinop(Op::SMOD, left, right); } Expr smod(Expr left, cst_t right){ return exprbinop(Op::SMOD, left, exprcst(left->size, right)); } Expr smod(cst_t left, Expr right){ return exprbinop(Op::SMOD, exprcst(right->size, left), right); } Expr mulh(Expr left, Expr right){ return exprbinop(Op::MULH, left, right); } Expr mulh(Expr left, cst_t right){ return exprbinop(Op::MULH, left, exprcst(left->size, right)); } Expr mulh(cst_t left, Expr right){ return exprbinop(Op::MULH, exprcst(right->size, left), right); } Expr smull(Expr left, Expr right){ return exprbinop(Op::SMULL, left, right); } Expr smull(Expr left, cst_t right){ return exprbinop(Op::SMULL, left, exprcst(left->size, right)); } Expr smull(cst_t left, Expr right){ return exprbinop(Op::SMULL, exprcst(right->size, left), right); } Expr smulh(Expr left, Expr right){ return exprbinop(Op::SMULH, left, right); } Expr smulh(Expr left, cst_t right){ return exprbinop(Op::SMULH, left, exprcst(left->size, right)); } Expr smulh(cst_t left, Expr right){ return exprbinop(Op::SMULH, exprcst(right->size, left), right); } // Unary operations Expr operator~(Expr arg){ return make_shared(Op::NOT, arg); } Expr operator-(Expr arg){ return make_shared(Op::NEG, arg); } Expr bisz(exprsize_t size, Expr cond, cst_t mode){ return make_shared(size, cond, mode); } /* Printing operators */ ostream& operator<<(ostream& os, Expr e){ os << std::hex; // Default, print constants in hex e->print(os); return os; } string op_to_str(Op op){ switch(op){ case Op::ADD: return "+"; case Op::MUL: return "*"; case Op::MULH: return "*h "; case Op::SMULL: return "*lS "; case Op::SMULH: return "*hS "; case Op::DIV: return "/"; case Op::SDIV: return "/S "; case Op::NEG: return "-"; case Op::AND: return "&"; case Op::OR: return "|"; case Op::XOR: return "^"; case Op::SHL: return "<<"; case Op::SHR: return ">>"; case Op::NOT: return "~"; case Op::MOD: return "%"; case Op::SMOD: return "%S "; default: throw expression_exception("op_to_str(): got unknown operation!"); } } /* ======= Canonize an expression ========== */ /* This function can be used to build an associative binary operation from * an expression and a list of arguments. * * This function is used when canonizing associative binary expressions where * arguments should be reordered and grouped by higher priority first. * * The function takes several arguments: * - e : an expression that must be combined with the expressions in 'new_args' * to build the new associative expression. It will be handled differently * if it is a binop corresponding to 'op' or if it's a normal expression * - op : the associative operation we build * - new_args : a list of args that must be combined to 'e' with operation 'op'. * the arguments are expected to be sorted from higher priority to * lower priority * * The function combines the arguments in the canonic way ! * */ Expr build_associative_from_args(Expr e, Op op, vector& new_args){ Expr new_arg = nullptr, next_arg = nullptr; Expr res = nullptr; if( new_args.empty() ){ return e; } if( !e->is_binop(op)){ // e is not a binop of type 'op', we stop here and combine all args by priority bool added_leaf = false; for( vector::iterator it = new_args.begin(); it != new_args.end(); it++ ){ if( !added_leaf && (*it)->inf(e)){ // Time to add args[0] next_arg = e; added_leaf = true; it = it-1; // Dont forget to stay on the same new_arg then }else{ // Get next arg next_arg = *it; } if( res == nullptr){ res = next_arg; }else{ res = make_shared(op, res, next_arg); } } if( !added_leaf){ res = make_shared(op, res, e); } return res; }else if( new_args.back()->inf(e->args[1]) ){ // e is a binop of type 'op' and the smaller new argument is smaller than // the right side of 'e'. So we insert the rest of the new arguments and // add the smaller one in the end new_arg = new_args.back(); new_args.pop_back(); res = build_associative_from_args(e, op, new_args); return make_shared(op, res, new_arg); }else{ // e is a binop of type 'op' and the smaller new argument is bigger than // the right side of 'e'. So we need to insert all new args to the left side // and finally add the right one in the end (because smallest priority) res = build_associative_from_args(e->args[0], op, new_args); return make_shared(op, res, e->args[1]); } } Expr build_left_associative_from_args(Expr e, Op op, vector& new_args){ Expr new_arg = nullptr, next_arg = nullptr; Expr res = nullptr; if( new_args.empty() ){ return e; } if( !e->is_binop(op)){ // e is not a binop of type 'op', we stop here and combine all args by priority res = e; for( vector::iterator it = new_args.begin(); it != new_args.end(); it++ ){ res = make_shared(op, res, *it); } return res; }else if( new_args.back()->inf(e->args[1]) ){ // e is a binop of type 'op' and the smaller new argument is smaller than // the right side of 'e'. So we insert the rest of the new arguments and // add the smaller one in the end new_arg = new_args.back(); new_args.pop_back(); res = build_left_associative_from_args(e, op, new_args); return make_shared(op, res, new_arg); }else{ // e is a binop of type 'op' and the smaller new argument is bigger than // the right side of 'e'. So we need to insert all new args to the left side // and finally add the right one in the end (because smallest priority) res = build_left_associative_from_args(e->args[0], op, new_args); return make_shared(op, res, e->args[1]); } } Expr expr_canonize(Expr e){ vector new_args; Expr e1, e2, leftmost; Expr res; /* Binop */ if( e->is_binop() ){ if( op_is_associative(e->op()) && op_is_symetric(e->op())){ // Associative and symetric -> re-order arguments // First get arguments list as long as the operator is used for // right side argument if( e->args[1]->is_binop(e->op())) e->args[1]->get_associative_args(e->op(), new_args); else new_args.push_back(e->args[1]); // Sort the arguments to call build_associative_from_args std::reverse(new_args.begin(), new_args.end()); // Invert vector to have the bigger ones first res = build_associative_from_args(e->args[0], e->op(), new_args); return res; }else if( op_is_left_associative(e->op()) && e->args[0]->is_binop(e->op())){ // Left associative -> (a/b)/c -> (a/c)/b new_args.push_back(e->args[1]); res = build_left_associative_from_args(e->args[0], e->op(), new_args); return res; } // Canonize and return if( new_args.size() > 0 ){ // Group higher args together first while( new_args.size() > 1 ){ e1 = new_args.back(); new_args.pop_back(); e2 = new_args.back(); new_args.pop_back(); new_args.push_back(make_shared(e->op(), e1, e2)); } return new_args.back(); }else{ // Nothing to do, return the same expression return e; } /* Concat */ }else if( e->is_concat() ){ if( e->args[0]->is_concat() ) return concat(e->args[0]->args[0], concat(e->args[0]->args[1], e->args[1])); else return e; }else return e; } /* ====================================== */ /* Misc operations and functions on enums */ bool operator<(Op op1, Op op2){ return static_cast(op1) < static_cast(op2); } bool op_is_symetric(Op op){ return (op == Op::ADD || op == Op::AND || op == Op::MUL || op == Op::MULH || op == Op::OR || op == Op::XOR || op == Op::SMULL || op == Op::SMULH ); } bool op_is_associative(Op op){ return (op == Op::ADD || op == Op::AND || op == Op::MUL || op == Op::MULH || op == Op::OR || op == Op::XOR || op == Op::SMULL || op == Op::SMULH ); } bool op_is_left_associative(Op op){ return (op == Op::DIV); } bool op_is_multiplication(Op op){ return (op == Op::MUL || op == Op::SMULL || op == Op::SMULH || op == Op::MULH); } bool op_is_distributive_over(Op op1, Op op2){ switch(op1){ case Op::MUL: case Op::MULH: case Op::SMULL: case Op::SMULH: return (op2 == Op::ADD); case Op::AND: return ( op2 == Op::AND || op2 == Op::OR ); case Op::OR: return ( op2 == Op::OR || op2 == Op::AND ); default: return false; } } bool operator<(ExprType t1, ExprType t2){ return static_cast(t1) < static_cast(t2); } /* Constant manipulation */ cst_t cst_sign_trunc(exprsize_t size, cst_t val){ if( size == sizeof(cst_t)*8 ) return val; else return val & (((ucst_t)1<<(ucst_t)size)-1); } cst_t cst_mask(exprsize_t size){ if( size == sizeof(cst_t)*8 ) return -1; else return ((ucst_t)1<::iterator it; if( ( it = varmap.find(name)) == varmap.end()) throw expression_exception(QuickFmt() << "Trying to access variable '" << name << "' which is unknown in context" >> QuickFmt::to_str); return it->second; } void VarContext::remove(const string& name){ varmap.erase(name); id++; } void VarContext::print(ostream& os ){ os << std::endl; for( auto var : varmap ){ os << var.first << " : " << std::hex << "0x" << var.second << std::dec << std::endl; } } ostream& operator<<(ostream& os, VarContext& c){ c.print(os); return os; } ================================================ FILE: libropium/symbolic/simplification.cpp ================================================ #include "simplification.hpp" #include "expression.hpp" #include "exception.hpp" #include #include #include using std::make_shared; /* ExprSimplifier implementation */ ExprSimplifier::ExprSimplifier(){} void ExprSimplifier::add(ExprSimplifierFunc func){ simplifiers.push_back(func); } void ExprSimplifier::add(RecExprSimplifierFunc func){ rec_simplifiers.push_back(func); } void ExprSimplifier::add_restruct(RecExprSimplifierFunc func){ restruct_simplifiers.push_back(func); } Expr ExprSimplifier::run_simplifiers(Expr e){ Expr tmp_expr = e; vector::iterator func; vector::iterator rec_func; /* Normal functions */ for (func = simplifiers.begin(); func != simplifiers.end(); func++){ tmp_expr = (**func)(tmp_expr); } /* Recursive functions */ for (rec_func = rec_simplifiers.begin(); rec_func != rec_simplifiers.end(); rec_func++) tmp_expr = (**rec_func)(tmp_expr, *this); return tmp_expr; } Expr ExprSimplifier::simplify(Expr e){ Expr tmp_expr = e; Expr prev_expr; Expr prev_arg; // Check if already simplified or if simple constant if( e->_is_simplified || e->is_cst()){ return e; }else if( e->_simplified_expr != nullptr ){ return e->_simplified_expr; } // Simplify util fix point is found do{ prev_expr = tmp_expr; tmp_expr = run_simplifiers(tmp_expr); /* If no high level change, simplify arguments and try again */ /* !!! Don't enter the block if args.size() == 0 because it would * cause basic expressions (cst, var) to loose their taint ! */ if( prev_expr->eq(tmp_expr) && tmp_expr->args.size() > 0){ // Simplify args in place :) for( int i = 0; i < tmp_expr->args.size(); i++ ){ tmp_expr->args[i] = simplify(tmp_expr->args[i]); } // ! If binop we recanonize it because arguments changed ! tmp_expr = expr_canonize(tmp_expr); // ! We remove the hash and taint of tmp_expr because we modify its // arguments directly in the AST tmp_expr->_hashed = false; // Re-apply simplifications prev_expr = tmp_expr; tmp_expr = run_simplifiers(tmp_expr); } }while( prev_expr->neq(tmp_expr) ); if( tmp_expr->neq(e) ){ // If the expression was simplified then save the pointer to the // simplified expression. If not the don't save anything because // the object shouldn't hold a shared pointer to itself :/ e->_simplified_expr = tmp_expr; } tmp_expr->_is_simplified = true; return tmp_expr; } ExprSimplifier* NewDefaultExprSimplifier(){ ExprSimplifier* simp = new ExprSimplifier(); simp->add(es_constant_folding); simp->add(es_neutral_elements); simp->add(es_absorbing_elements); simp->add(es_arithmetic_properties); simp->add(es_involution); simp->add(es_extract_patterns); simp->add(es_basic_transform); simp->add(es_logical_properties); simp->add(es_concat_patterns); simp->add(es_arithmetic_factorize); //simp->add(es_generic_distribute); simp->add(es_generic_factorize); //simp->add(es_deep_associative); return simp; } /* ================================================== Light simplifications ================================================= */ /* Constant folding */ Expr es_constant_folding(Expr e){ Expr res = nullptr; cst_t _concrete, high, low; ucst_t mask; if( e->is_binop() && e->args[0]->is_cst() && e->args[1]->is_cst()){ /* Binary operators */ switch(e->op()){ case Op::ADD: _concrete = (e->args[0]->cst() + e->args[1]->cst()); break; case Op::MUL: _concrete = ((ucst_t)e->args[0]->cst() * (ucst_t)e->args[1]->cst()); break; case Op::MULH: _concrete = (cst_t)(((__uint128_t)cst_sign_trunc(e->args[0]->size, e->args[0]->cst()) * cst_sign_trunc(e->args[1]->size, (__uint128_t)e->args[1]->cst())) >> e->size ); break; case Op::DIV: _concrete = ((ucst_t)cst_sign_trunc(e->args[0]->size, e->args[0]->cst()) / (ucst_t)cst_sign_trunc(e->args[1]->size, e->args[1]->cst())); break; case Op::SDIV: _concrete = (e->args[0]->cst() / e->args[1]->cst()); break; case Op::AND: _concrete = (e->args[0]->cst() & e->args[1]->cst()); break; case Op::OR: _concrete = (e->args[0]->cst() | e->args[1]->cst()); break; case Op::XOR: _concrete = (e->args[0]->cst() ^ e->args[1]->cst()); break; case Op::MOD: _concrete = ((ucst_t)e->args[0]->cst() % (ucst_t)e->args[1]->cst()); break; case Op::SMOD: _concrete = (e->args[0]->cst() % e->args[1]->cst()); break; case Op::SMULL: _concrete = (cst_t)((__int128_t)e->args[0]->cst() * e->args[1]->cst()); break; case Op::SMULH: _concrete = (cst_t)(((__int128_t)e->args[0]->cst() * e->args[1]->cst()) >> e->size); break; case Op::SHL: if( e->args[1]->cst() >= e->args[0]->size ){ _concrete = 0; }else{ _concrete = ((ucst_t)cst_sign_trunc(e->args[0]->size, e->args[0]->cst())) << ((ucst_t)e->args[1]->cst()); } break; case Op::SHR: if( cst_sign_trunc(e->args[1]->size, e->args[1]->cst()) >= e->args[0]->size ){ _concrete = 0; }else{ _concrete = ((ucst_t)cst_sign_trunc(e->args[0]->size, e->args[0]->cst())) >> ((ucst_t)e->args[1]->cst()); } break; default: throw runtime_exception("Missing case in constant folding simplification"); } res = exprcst(e->size, cst_sign_extend(e->size, _concrete)); }else if( e->is_unop() && e->args[0]->is_cst()){ /* Unary operators */ switch(e->op()){ case Op::NEG: _concrete =cst_sign_extend(e->size, -(e->args[0]->cst())); break; case Op::NOT: _concrete =cst_sign_extend(e->size, ~(e->args[0]->cst())); break; default: throw runtime_exception("Missing case in constant folding simplification"); } res = exprcst(e->size, cst_sign_extend(e->size, _concrete)); }else if( e->is_bisz() && e->args[0]->is_cst()){ /* BISZ */ _concrete = (e->args[0]->cst() == 0)? e->mode() : e->mode()^1; res = exprcst(e->size, cst_sign_extend(e->size, _concrete)); }else if( e->is_extract() && e->args[0]->is_cst() && e->args[1]->is_cst() && e->args[2]->is_cst()){ /* Extract */ high = e->args[1]->cst(); low = e->args[2]->cst(); if( high == 63 ){ mask = 0xffffffffffffffff; }else{ mask = (((cst_t)1 << (high+1))-1); } _concrete = ((ucst_t)e->args[0]->cst() & mask) >> (ucst_t)low; res = exprcst(e->size, cst_sign_extend(e->size, _concrete)); }else if( e->is_concat() && e->args[0]->is_cst() && e->args[1]->is_cst() ){ /* Concat */ high = e->args[0]->cst(); low = e->args[1]->cst(); _concrete = cst_sign_extend(e->size, (((ucst_t)high)<<(ucst_t)e->args[1]->size) | (ucst_t)cst_sign_trunc(e->args[1]->size, low)); res = exprcst(e->size, cst_sign_extend(e->size, _concrete)); } /* Return result */ if( res != nullptr ){ return res; }else return e; } /* Neutral elements */ Expr es_neutral_elements(Expr e){ if( e->is_binop() && e->args[0]->is_cst()){ // 0 + X if( e->op() == Op::ADD && e->args[0]->cst() == 0) return e->args[1]; // 1 * X else if( (op_is_multiplication(e->op())) && e->args[0]->cst() == 1) return e->args[1]; // 0xfffff.... & X else if( e->op() == Op::AND && cst_sign_trunc(e->size, e->args[0]->cst()) == cst_mask(e->size)) return e->args[1]; // 0 |^ X else if( (e->op() == Op::OR || e->op() == Op::XOR) && e->args[0]->cst() == 0) return e->args[1]; }else if( e->is_binop() && e->args[1]->is_cst()){ // X / 1 if( (e->op()==Op::DIV || e->op() == Op::SDIV) && e->args[1]->cst() == 1 ) return e->args[0]; // X << 0 or X >> 0 else if( (e->op() == Op::SHL || e->op() == Op::SHR) && e->args[1]->cst() == 0 ) return e->args[0]; }else if(e->is_extract() && e->args[1]->is_cst() && e->args[2]->is_cst()){ // Extract(X, sizeof(X)-1, 0) if( e->args[1]->cst() == e->args[0]->size-1 && e->args[2]->cst() == 0 ) return e->args[0]; } return e; } /* Absorbing elements */ Expr es_absorbing_elements(Expr e){ if( !e->is_binop() ) return e; if( e->args[0]->is_cst()){ // 0 &*//S X if( (e->op() == Op::AND || op_is_multiplication(e->op()) || e->op() == Op::DIV || e->op() == Op::SDIV) && e->args[0]->cst() == 0) return e->args[0]; // 0xffff..... | X else if( (e->op() == Op::OR) && cst_sign_trunc(e->size, e->args[0]->cst()) == cst_mask(e->size)) return e->args[0]; // X << sizeof(X) or X >> sizeof(X) }else if( (e->op() == Op::SHL || e->op() == Op::SHR) && e->args[1]->is_cst() && (e->args[1]->cst() >= (cst_t)e->size)){ return exprcst(e->size, 0); } return e; } /* ADD specific simplifications */ Expr es_arithmetic_properties(Expr e){ if( !e->is_binop(Op::ADD)){ return e; } // X-X --> 0 if( e->args[1]->is_unop(Op::NEG) && e->args[0]->eq(e->args[1]->args[0])){ return exprcst(e->size, 0); // X+(-1*X) --> 0 if( e->args[1]->is_binop() && op_is_multiplication(e->args[1]->op()) && e->args[1]->args[0]->is_cst() && e->args[1]->args[0]->cst() == -1 && e->args[1]->args[1]->eq(e->args[0])){ return exprcst(e->size, 0); // -X+X --> 0 }else if( e->args[0]->is_unop(Op::NEG) && e->args[1]->eq(e->args[0]->args[0])){ return exprcst(e->size, 0); // (-1*X)+X --> 0 }else if(e->args[1]->is_binop() && op_is_multiplication(e->args[1]->op()) && e->args[0]->args[0]->is_cst() && e->args[0]->args[0]->cst() == -1 && e->args[0]->args[1]->eq(e->args[1])) return exprcst(e->size, 0); } return e; } /* NEG and NOT involution simplifications */ Expr es_involution(Expr e){ if( e->is_unop(Op::NEG) || e->is_unop(Op::NOT)){ if( e->args[0]->is_unop(e->op())) return e->args[0]->args[0]; } return e; } /* Extract specific simplifications */ Expr es_extract_patterns(Expr e){ if( e->is_extract() ){ if( e->args[0]->is_concat() && e->args[1]->is_cst() && e->args[2]->is_cst()){ // extract(concat(X,Y), a, b) --> extract(X, a', b') if( e->args[2]->cst() >= e->args[0]->args[1]->size ){ return extract( e->args[0]->args[0], e->args[1]->cst()-e->args[0]->args[1]->size, e->args[2]->cst()-e->args[0]->args[1]->size); } // extract(concat(X,Y), a, b) --> extract(Y, a', b') else if( e->args[1]->cst() < e->args[0]->args[1]->size ){ return extract( e->args[0]->args[1], e->args[1]->cst(), e->args[2]->cst()); } // extract(extract(X,a,b),c,d) --> extract(X, a',b') }else if( e->args[0]->is_extract() && (e->args[0]->args[2]->size == e->args[1]->size) && (e->args[0]->args[2]->size == e->args[2]->size)){ return extract(e->args[0]->args[0], e->args[0]->args[2]->cst()+e->args[1]->cst(), e->args[0]->args[2]->cst()+e->args[2]->cst()); } } return e; } /* Basic transformations to canonize expressions a bit more */ Expr es_basic_transform(Expr e){ if( e->is_binop(Op::SHL) && e->args[1]->is_cst()){ // X << Y --> X * (2**Y) return e->args[0]*exprcst(e->size, ((ucst_t)1<<(ucst_t)e->args[1]->cst())); }else if( e->is_binop(Op::SHR) && e->args[1]->is_cst() ){ // X >> Y --> X / (2**Y) return e->args[0]/exprcst(e->size, ((ucst_t)1<<(ucst_t)(e->args[1]->cst()))); // -X --> -1*X }else if(e->is_unop(Op::NEG)){ return exprcst(e->size, -1)*e->args[0]; // -Y*X --> -(Y*X) /* }else if( e->is_binop(Op::MUL) && e->args[0]->is_unop(Op::NEG)){ return -(e->args[0]->args[0]*e->args[1]); */ // X*-Y --> -(X*Y) /* }else if( e->is_binop(Op::MUL) && e->args[1]->is_unop(Op::NEG)){ return -(e->args[1]->args[0]*e->args[0]); */ // 1+~X --> -X }else if( e->is_binop(Op::ADD) && e->args[0]->is_cst() && e->args[0]->cst()==1 && e->args[1]->is_unop(Op::NOT)){ return -e->args[1]->args[0]; // -1^X --> ~X }else if( e->is_binop(Op::XOR) && e->args[0]->is_cst() && e->args[0]->cst() == -1){ return ~e->args[1]; // CST*-Y --> -CST*Y }else if( ((e->is_binop() && op_is_multiplication(e->op())) || e->is_binop(Op::SDIV)) && (e->args[1]->is_unop(Op::NEG)) && (e->args[0]->is_cst()) ) { return exprbinop(e->op(), -e->args[0], e->args[1]->args[0]); // (-Y)*CST --> Y*(-CST) }else if( ((e->is_binop() && op_is_multiplication(e->op())) || e->is_binop(Op::SDIV)) && (e->args[0]->is_unop(Op::NEG)) && (e->args[1]->is_cst()) ) { return exprbinop(e->op(), e->args[0]->args[0], -e->args[1]); } return e; } /* logical properties */ Expr es_logical_properties(Expr e){ // X &| X --> X if( (e->is_binop(Op::AND) || e->is_binop(Op::OR)) && ( e->args[0]->eq(e->args[1]))){ return e->args[0]; // X & ~X --> 0 }else if( e->is_binop(Op::AND) && e->args[1]->is_unop(Op::NOT) && e->args[0]->eq(e->args[1]->args[0])){ return exprcst(e->size, 0); // ~X & X --> 0 }else if( e->is_binop(Op::AND) && e->args[0]->is_unop(Op::NOT) && e->args[1]->eq(e->args[0]->args[0])){ return exprcst(e->size, 0); // ~X |^ X --> 0xfffff.... }else if( (e->is_binop(Op::OR) || e->is_binop(Op::XOR)) && e->args[0]->is_unop(Op::NOT) && e->args[1]->eq(e->args[0]->args[0])){ return exprcst(e->size, (cst_t)-1); // X |^ ~X --> 0xfffff.... }else if( (e->is_binop(Op::OR) || e->is_binop(Op::XOR)) && e->args[1]->is_unop(Op::NOT) && e->args[0]->eq(e->args[1]->args[0])){ return exprcst(e->size, (cst_t)-1); // X ^ X --> 0 }else if( e->is_binop(Op::XOR) && e->args[0]->eq(e->args[1])){ return exprcst(e->size, 0); } return e; } /* Concat simplification patterns */ Expr es_concat_patterns(Expr e){ // concat(X[a:b], X[b-1:c])) if( e->is_concat() && e->args[0]->is_extract() && e->args[1]->is_extract() && e->args[0]->args[0]->eq(e->args[1]->args[0]) && e->args[0]->args[2]->cst() == e->args[1]->args[1]->cst()+1){ return extract(e->args[0]->args[0], e->args[0]->args[1], e->args[1]->args[2]); } if( e->is_binop(Op::AND) && e->args[0]->is_cst() && e->args[1]->is_concat()){ if( cst_sign_trunc(e->args[0]->size, e->args[0]->cst()) == (((ucst_t)1<args[1]->args[1]->size)-1)){ if( e->args[1]->args[1]->is_cst() && e->args[1]->args[1]->is_cst() == 0 ){ // concat(X,0) & 0x000...11111 = 0 return exprcst(e->size, 0); }else{ // concat(X,Y) & 0x000...11111 = concat(0, Y) return concat(exprcst(e->args[1]->args[0]->size, 0), e->args[1]->args[1]); } } if( e->args[0]->cst() == (((cst_t)-1)<args[1]->args[1]->size)){ if( e->args[1]->args[0]->is_cst() && e->args[1]->args[0]->is_cst() == 0 ){ // concat(0,Y) & 0x111...000 = 0 return exprcst(e->size, 0); }else{ // concat(X,Y) & 0x111...000 = concat(X, 0) return concat(e->args[1]->args[0], exprcst(e->args[1]->args[1]->size, 0)); } } } return e; } /* Basic factorization patterns */ Expr es_arithmetic_factorize(Expr e){ if( !e->is_binop(Op::ADD)) return e; if( e->args[0]->is_binop() && op_is_multiplication(e->args[0]->op())){ // (X*Y)+Y --> (X+1)*Y if (e->args[0]->args[1]->eq(e->args[1])){ return (e->args[0]->args[0]+exprcst(e->size,1))*(e->args[1]); // (Y*X)+Y --> (X+1)*Y }else if(e->args[0]->args[0]->eq(e->args[1])){ return (e->args[0]->args[1]+exprcst(e->size,1))*(e->args[1]); // (X*Y)-Y --> (X-1)*Y }else if(e->args[1]->is_unop(Op::NEG) && e->args[0]->args[1]->eq(e->args[1]->args[0])){ return (e->args[0]->args[0]-exprcst(e->size,1))*e->args[0]->args[1]; }else if( e->args[1]->is_binop() && op_is_multiplication(e->args[1]->op())){ // (Y*X)-Y --> (X-1)*Y if(e->args[1]->is_unop(Op::NEG) && e->args[0]->args[0]->eq(e->args[1]->args[0])){ return (e->args[0]->args[1]-exprcst(e->size,1))*e->args[0]->args[0]; // (A*Y)+(B*Y) --> (A+B)*Y }else if( e->args[0]->args[1]->eq(e->args[1]->args[1])){ return (e->args[0]->args[0]+e->args[1]->args[0])*e->args[0]->args[1]; // (A*Y)+(Y*B) --> (A+B)*Y }else if( e->args[0]->args[1]->eq(e->args[1]->args[0])){ return (e->args[0]->args[0]+e->args[1]->args[1])*e->args[0]->args[1]; // (Y*A)+(B*Y) --> (A+B)*Y }else if( e->args[0]->args[0]->eq(e->args[1]->args[1])){ return (e->args[0]->args[1]+e->args[1]->args[0])*e->args[0]->args[0]; // (Y*A)+(Y*B) --> (A+B)*Y }else if( e->args[0]->args[0]->eq(e->args[1]->args[0])){ return (e->args[0]->args[1]+e->args[1]->args[1])*e->args[0]->args[0]; } } }else if(e->args[1]->is_binop() && op_is_multiplication(e->args[1]->op())) { // Y+(X*Y) --> (X+1)*Y if(e->args[0]->eq(e->args[1]->args[1])){ return (e->args[1]->args[0]+exprcst(e->size, 1))*(e->args[0]); // Y+(Y*X) --> (X+1)*Y }else if( e->args[0]->eq(e->args[1]->args[0]) ){ return (e->args[1]->args[1]+exprcst(e->size,1))*(e->args[0]); // -Y+(Y*X) --> (X-1)*Y }else if( e->args[0]->is_unop(Op::NEG) && e->args[0]->args[0]->eq(e->args[1]->args[0])){ return (e->args[1]->args[1]-exprcst(e->size, 1))*e->args[0]->args[0]; // -Y+(X*Y) --> (X-1)*Y }else if( e->args[0]->is_unop(Op::NEG) && e->args[0]->args[0]->eq(e->args[1]->args[1])){ return (e->args[1]->args[0]-exprcst(e->size, 1))*e->args[0]->args[0]; } // X+X --> 2*X }else if( e->args[0]->eq(e->args[1])){ return exprcst(e->size, 2)*e->args[0]; } return e; } /* Generic factorization on distributive operators */ Expr es_generic_factorize(Expr e){ if( e->is_binop() && e->args[0]->is_binop() && e->args[1]->is_binop() && e->args[0]->op() == e->args[1]->op() && op_is_distributive_over(e->args[0]->op(), e->op()) && op_is_symetric(e->args[0]->op())){ // (AxB)o(AxC) --> Ax(BoC) if( e->args[0]->args[0]->eq(e->args[1]->args[0])){ return exprbinop(e->args[0]->op(), e->args[0]->args[0], exprbinop(e->op(), e->args[0]->args[1], e->args[1]->args[1])); // (AxB)o(CxA) }else if( e->args[0]->args[0]->eq(e->args[1]->args[1])){ return exprbinop(e->args[0]->op(), e->args[0]->args[0], exprbinop(e->op(), e->args[0]->args[1], e->args[1]->args[0])); // (BxA)o(CxA) }else if( e->args[0]->args[1]->eq(e->args[1]->args[1])){ return exprbinop(e->args[0]->op(), e->args[0]->args[1], exprbinop(e->op(), e->args[0]->args[0], e->args[1]->args[0])); // (BxA)o(AxC) }else if( e->args[0]->args[1]->eq(e->args[1]->args[0])){ return exprbinop(e->args[0]->op(), e->args[0]->args[1], exprbinop(e->op(), e->args[0]->args[0], e->args[1]->args[1])); } } return e; } /* Propagate distributive operators */ Expr es_generic_distribute(Expr e){ // (AxB)oC --> AoC x BoC if( e->is_binop() && e->args[0]->is_binop() && op_is_distributive_over(e->op(), e->args[0]->op())){ return exprbinop(e->args[0]->op(), exprbinop(e->op(), e->args[0]->args[0], e->args[1]), exprbinop(e->op(), e->args[0]->args[1], e->args[1])); // Co(AxB) --> CoA x CoB }else if( e->is_binop() && e->args[1]->is_binop() && op_is_distributive_over(e->op(), e->args[1]->op())){ return exprbinop(e->args[1]->op(), exprbinop(e->op(), e->args[0], e->args[1]->args[0]), exprbinop(e->op(), e->args[0], e->args[1]->args[1])); } return e; } /* ========================================= * Heavy Simplifications * ========================================= */ /* Associativity deep in the AST * Try to unfold nested associative operators and see if * pairs can be simplified for all possible associations */ Expr es_deep_associative(Expr e, ExprSimplifier& s){ vector vec; Expr expr, simp, tmp1, tmp2; bool restart; int i=0, j=0; if( e->is_binop() && op_is_associative(e->op()) && op_is_symetric(e->op())){ /* Get all args */ e->get_associative_args(e->op(), vec); /* If only two args, no need to simplify in depth */ if( vec.size() <= 2 ){ return e; } /* Else enter simplify loop */ while( i < vec.size()-1 ){ j = i+1; tmp1 = vec[i]; restart = false; while( j < vec.size() ){ tmp2 = vec[j]; /* Normal op */ expr = exprbinop(e->op(), tmp1, tmp2); /* Simplified one */ simp = s.simplify(expr); /* If changed, push the new one and continue, * else continue with other args */ if( expr->neq(simp) ){ vec.erase(std::next(vec.begin(),j)); vec.erase(vec.begin()+i); i = 0; /* Insert in sorted ! */ vec.push_back(simp); restart = true; break; }else{ j++; } } if( !restart){ i++; } } /* Recombine all expressions */ std::sort(vec.begin(), vec.end()); while( vec.size() > 1 ){ tmp1 = vec.back(); vec.pop_back(); tmp2 = vec.back(); vec.pop_back(); vec.push_back(exprbinop(e->op(), tmp1, tmp2)); } return vec.back(); } return e; } ================================================ FILE: libropium/symbolic/symbolic.cpp ================================================ #include "symbolic.hpp" #include "exception.hpp" #include "simplification.hpp" #include #include #include #include #include using std::get; using std::vector; using std::stringstream; using std::make_shared; Semantics::Semantics(IRContext* r, MemContext* m): regs(r), mem(m){} void Semantics::simplify(){ ExprSimplifier* simp = NewDefaultExprSimplifier(); for( int reg = 0; reg < regs->nb_vars(); reg++ ){ regs->set(reg, simp->simplify(regs->get(reg))); } for( unordered_map::iterator write = mem->writes.begin(); write != mem->writes.end(); write++ ){ mem->writes[write->first] = simp->simplify(write->second); } delete simp; } Semantics::~Semantics(){ delete regs; regs = nullptr; delete mem; mem = nullptr; } ostream& operator<<(ostream& os, Semantics& s){ os << *(s.regs) << std::endl << *(s.mem); return os; } /* ======================================= */ SymbolicEngine::SymbolicEngine(ArchType a){ if(a == ArchType::X86){ arch = new ArchX86(); }else if( a == ArchType::X64 ){ arch = new ArchX64(); }else if (a == ArchType::NONE){ arch = new ArchNone(); }else{ throw symbolic_exception("SymbolicEngine::SymbolicEngine() unsupported ArchType"); } } SymbolicEngine::~SymbolicEngine(){ delete arch; arch = nullptr; } /* Some util functions to manipulate values during symbolic execution */ Expr _reduce_rvalue(Expr e, exprsize_t high, exprsize_t low ){ if( high-low+1 == e->size ) return e; else return extract(e, high, low); } Expr _expand_lvalue(Expr current, Expr e, exprsize_t high, exprsize_t low){ if( high-low+1 >= current->size ) return e; else if(low == 0){ return concat(extract(current, current->size-1, high+1), e); }else if(high == current->size-1){ return concat(e, extract(current, low-1, 0)); }else{ return concat(extract(current, current->size-1, high+1), concat(e, extract(current, low-1, 0))); } } inline void _set_tmp_var(int num, Expr e, int high, int low, vector& tmp_vars){ unsigned int tmp_vars_size = tmp_vars.size(); if( tmp_vars_size <= num ){ /* Fill missing tmp variables if needed *//* for( int i = 0; i < (num - tmp_vars_size); i++){ tmp_vars.push_back(nullptr); }*/ std::fill_n(std::back_inserter(tmp_vars), (num - tmp_vars_size+1), nullptr); } if( tmp_vars[num] == nullptr ){ if( low == 0 ){ tmp_vars[num] = e; }else{ /* If new tmp and low is != 0, then we pad the lower bits * with zero. That's a ugly hack but used to avoid some bugs * for some instructions when their IR gets optimized */ tmp_vars[num] = _expand_lvalue(exprcst(high+1, 0), e, high, low); } }else{ tmp_vars[num] = _expand_lvalue(tmp_vars[num], e, high, low); } } Expr _get_operand(IROperand& arg, IRContext* irctx, vector& tmp_vars){ if( arg.is_cst() ){ if( arg.high-arg.low+1 == sizeof(cst_t)*8 ) return exprcst(arg.high-arg.low+1, arg.cst()); else return exprcst(arg.high-arg.low+1, ((ucst_t)arg.cst() & (((ucst_t)1 << (arg.high+1))-1)) >> (ucst_t)arg.low); }else if( arg.is_var() ){ return _reduce_rvalue(irctx->get(arg.var()), arg.high, arg.low); }else if( arg.is_tmp() ){ return _reduce_rvalue(tmp_vars[arg.tmp()], arg.high, arg.low); }else{ return nullptr; } } void _update_dereferenced_regs(bool* deref, Expr e){ switch( e->type ){ case ExprType::CST: case ExprType::MEM: case ExprType::UNKNOWN: return; case ExprType::VAR: deref[e->reg()] = true; break; case ExprType::UNOP: _update_dereferenced_regs(deref, e->args[0]); break; case ExprType::BINOP: case ExprType::CONCAT: case ExprType::EXTRACT: _update_dereferenced_regs(deref, e->args[0]); _update_dereferenced_regs(deref, e->args[1]); break; default: break; } } #define DELETE_ALL_OBJECTS() delete regs; delete mem; delete simp; Semantics* SymbolicEngine::execute_block(IRBlock* block){ Expr rvalue, dst, src1, src2; IRBasicBlock::iterator instr; bool stop = false; IRContext* regs = new IRContext(arch->nb_regs); MemContext* mem = new MemContext(); vector tmp_vars; ExprSimplifier *simp = NewDefaultExprSimplifier(); IRBasicBlockId bblkid = 0; /* Init context */ for( reg_t reg = 0; reg < arch->nb_regs; reg++){ regs->set(reg, exprvar(arch->bits, arch->reg_name(reg), reg)); } block->known_max_sp_inc = true; block->max_sp_inc = 0; // FOR DEBUG // std::cout << "DEBUG EXECUTING " << block->name << std::endl; while( !stop ){ /* ====================== Execute an IR basic block ======================== */ /* Execute the basic block as long as there is no reason to stop */ for( instr = block->get_bblock(bblkid).begin(); instr != block->get_bblock(bblkid).end(); instr++){ // FOR DEBUG // std::cout << "DEBUG, executing " << *instr << std::endl; /* Get operands expressions */ src1 = _get_operand(instr->src1, regs, tmp_vars); src2 = _get_operand(instr->src2, regs, tmp_vars); /* Arithmetic and logic operations */ if( iroperation_is_assignment(instr->op)){ /* Build rvalue */ switch( instr->op ){ case IROperation::ADD: rvalue = src1 + src2; break; case IROperation::SUB: rvalue = src1 - src2; break; case IROperation::MUL: rvalue = src1 * src2; break; case IROperation::MULH: rvalue = mulh(src1, src2); break; case IROperation::SMULL: rvalue = smull(src1,src2); break; case IROperation::SMULH: rvalue = smulh(src1,src2); break; case IROperation::DIV: rvalue = src1 / src2; break; case IROperation::SDIV: rvalue = sdiv(src1, src2); break; case IROperation::SHL: rvalue = shl(src1, src2); break; case IROperation::SHR: rvalue = shr(src1, src2); break; case IROperation::AND: rvalue = src1 & src2; break; case IROperation::OR: rvalue = src1 | src2; break; case IROperation::XOR: rvalue = src1 ^ src2; break; case IROperation::MOD: rvalue = src1 % src2; break; case IROperation::SMOD: rvalue = smod(src1,src2); break; case IROperation::NEG: rvalue = -src1; break; case IROperation::NOT: rvalue = ~src1; break; case IROperation::MOV: rvalue = src1; break; case IROperation::CONCAT: rvalue = concat(src1, src2); break; default: DELETE_ALL_OBJECTS() throw runtime_exception("Unsupported assignment IROperation in SymbolicEngine::execute_block()"); } /* Affect lvalue */ if( instr->dst.is_tmp()){ _set_tmp_var(instr->dst.tmp(), rvalue, instr->dst.high, instr->dst.low, tmp_vars); }else if( instr->dst.is_var()){ regs->set(instr->dst.var(), _expand_lvalue(regs->get(instr->dst.var()), rvalue, instr->dst.high, instr->dst.low)); }else{ DELETE_ALL_OBJECTS() throw runtime_exception("SymbolicEngine::execute_block() got invalid dst operand type"); } }else if(instr->op == IROperation::STM){ /* Store memory */ dst = _get_operand(instr->dst, regs, tmp_vars); /* THEN execute the store */ mem->write(dst, src1); /* Record regs that have been dereferenced (in the address)*/ _update_dereferenced_regs(block->dereferenced_regs, dst); }else if( instr->op == IROperation::LDM){ /* Load memory */ /* Record regs that have been dereferenced (in the address)*/ _update_dereferenced_regs(block->dereferenced_regs, src1); // Affect lvalue rvalue = mem->read(src1, (instr->dst.high-instr->dst.low+1)/8); if( instr->dst.is_tmp()){ _set_tmp_var(instr->dst.tmp(), rvalue, instr->dst.high, instr->dst.low, tmp_vars); }else if( instr->dst.is_var()){ regs->set(instr->dst.var(), _expand_lvalue(regs->get(instr->dst.var()), rvalue, instr->dst.high, instr->dst.low)); }else{ DELETE_ALL_OBJECTS() throw runtime_exception("SymbolicEngine::execute_block() got invalid dst operand type"); } }else if( instr->op == IROperation::BCC){ dst = _get_operand(instr->dst, regs, tmp_vars); /* Check condition and update basic block to execute */ if( !dst->is_cst() || !src1->is_cst() || (src2 != nullptr && !src2->is_cst())){ DELETE_ALL_OBJECTS() throw symbolic_exception("BCC with non constant operand(s) not supported"); } if( cst_sign_trunc(dst->size, dst->cst()) != 0){ bblkid = src1->cst(); }else{ bblkid = src2->cst(); } break; }else if( instr->op == IROperation::JCC ){ dst = _get_operand(instr->dst, regs, tmp_vars); /* Set new PC */ if( dst->is_cst() && cst_sign_trunc(dst->size, dst->cst()) != 0){ regs->set(arch->pc(), _expand_lvalue(regs->get(arch->pc()), src1, instr->dst.high, instr->dst.low)); }else{ DELETE_ALL_OBJECTS() throw symbolic_exception("JCC with non constant or null condition not supported"); } /* Quit this block */ stop = true; // Go out of this block break; // Stop executing instructions in the basic block }else if(instr->op == IROperation::BISZ){ if( !src2->is_cst() ){ DELETE_ALL_OBJECTS() throw symbolic_exception("BISZ with not constant mode not supported"); } rvalue = bisz((instr->dst.high-instr->dst.low)+1 , src1, cst_sign_trunc(src2->size, src2->cst())); /* Affect lvalue */ if( instr->dst.is_tmp()){ _set_tmp_var(instr->dst.tmp(), rvalue, instr->dst.high, instr->dst.low, tmp_vars); }else if( instr->dst.is_var()){ regs->set(instr->dst.var(), _expand_lvalue(regs->get(instr->dst.var()), rvalue, instr->dst.high, instr->dst.low)); }else{ DELETE_ALL_OBJECTS() throw runtime_exception("SymbolicEngine::execute_block() got invalid dst operand type"); } }else if(instr->op == IROperation::INT){ cst_t num = cst_sign_trunc(instr->dst.size, _get_operand(instr->dst, regs, tmp_vars)->concretize()); if( num != 0x80 ){ DELETE_ALL_OBJECTS() throw symbolic_exception("SymbolicEngine::execute_block() interruption: got unsupported INT number"); } block->ends_with_int80 = true; /* Quit this block */ stop = true; // Go out of this block break; // Stop executing instructions in the basic block }else if(instr->op == IROperation::SYSCALL){ block->ends_with_syscall = true; /* Quit this block */ stop = true; // Go out of this block break; // Stop executing instructions in the basic block }else{ DELETE_ALL_OBJECTS() throw runtime_exception("SymbolicEngine::execute_block(): unknown IR instruction type"); } /* Check for sp increment */ Expr sp = regs->get(arch->sp()); sp = simp->simplify(sp); cst_t sp_inc = 0xffffffff; if( sp->is_binop(Op::ADD) && sp->args[0]->is_cst() && sp->args[0]->cst() % arch->octets == 0 && sp->args[1]->is_reg(arch->sp())){ // sp = sp0 + cst sp_inc = sp->args[0]->cst(); }else if( sp->is_binop(Op::ADD) && sp->args[0]->is_unop(Op::NEG) && sp->args[0]->args[0]->is_cst() && sp->args[0]->args[0]->cst() % arch->octets == 0 && sp->args[1]->is_reg(arch->sp())){ // sp = sp0 - cst sp_inc = -1*sp->args[0]->args[0]->cst(); }else if( sp->is_var() && arch->reg_num(sp->name()) == arch->sp()){ // sp = sp0 sp_inc = 0; }else{ // sp is unknown block->known_max_sp_inc = false; } // Assign max sp inc if( sp_inc != 0xffffffff && block->known_max_sp_inc){ block->max_sp_inc = block->max_sp_inc > sp_inc ? block->max_sp_inc : sp_inc; } } } delete simp; return new Semantics(regs, mem); } ================================================ FILE: libropium/utils/utils.cpp ================================================ #include "utils.hpp" #include "exception.hpp" #include #include #include #include #include #include #include #include #include #include using std::ifstream; using std::ofstream; using std::ios; using std::stringstream; using std::vector; /* ======== Raw gadgets interface ======== */ // Read gadgets from file vector* raw_gadgets_from_file(string filename){ vector* res = new vector(); RawGadget raw; bool got_addr; ifstream file; string line; string addr_str; string byte; file.open(filename, ios::in | ios::binary ); while( getline(file, line)){ raw = RawGadget(); got_addr = false; addr_str = ""; byte = ""; for( char& c : line ){ // First the gadget address if( c == '$' ){ try{ raw.addr = std::stoi(addr_str, 0, 16); if( raw.addr == 0 ) throw std::invalid_argument(""); got_addr = true; }catch(std::invalid_argument& e){ throw runtime_exception(QuickFmt() << "raw_gadgets_from_file: error, bad address string: " << line >> QuickFmt::to_str); } }else if( !got_addr){ addr_str += c; }else{ byte += c; if( byte.size() == 2 ){ try{ raw.raw += (char)(std::stoi(byte, 0, 16)); byte = ""; }catch(std::invalid_argument& e){ throw runtime_exception(QuickFmt() << "raw_gadgets_from_file: error, bad byte in: " << line >> QuickFmt::to_str); } } } } res->push_back(raw); } file.close(); return res; } // Write gadgets to file from ROPgadget output void split(const std::string& str, vector& cont, char delim = ' ') { std::size_t current, previous = 0; current = str.find(delim); while (current != std::string::npos) { cont.push_back(str.substr(previous, current - previous)); previous = current + 1; current = str.find(delim, previous); } cont.push_back(str.substr(previous, current - previous)); } bool ropgadget_to_file(string out, string ropgadget_out, string bin){ stringstream cmd; ofstream out_file; ifstream ropgadget_file; string line; out_file.open(out, ios::out); cmd << "ROPgadget --binary " << bin << " --dump --all --depth 15 > " << ropgadget_out << std::endl; try{ FILE* pipe = popen(cmd.str().c_str(), "w"); string addr_str, raw_str; stringstream ss; vector splited; if (!pipe) { throw std::runtime_error("popen() failed!"); } pclose(pipe); ropgadget_file.open(ropgadget_out, ios::in); while( std::getline(ropgadget_file, line)){ splited.clear(); split(line, splited); // Get address string if( splited.size() > 3 ){ addr_str = splited[0]; }else{ continue; } if( addr_str.substr(0, 2) != "0x" ){ continue; } // Get raw string raw_str = splited.back(); if( raw_str.back() != '\n' ) raw_str += '\n'; // Write them to file out_file << addr_str << "$" << raw_str; } }catch(std::runtime_error& e){ return false; } out_file.close(); ropgadget_file.close(); return true; } /* ========== Printing stuff ============== */ // Colors string g_ERROR_COLOR_ANSI = DEFAULT_ERROR_COLOR_ANSI; string g_BOLD_COLOR_ANSI = DEFAULT_BOLD_COLOR_ANSI; string g_SPECIAL_COLOR_ANSI = DEFAULT_SPECIAL_COLOR_ANSI; string g_PAYLOAD_COLOR_ANSI = DEFAULT_PAYLOAD_COLOR_ANSI; string g_EXPLOIT_DESCRIPTION_ANSI = DEFAULT_EXPLOIT_DESCRIPTION_ANSI; string g_END_COLOR_ANSI = DEFAULT_END_COLOR_ANSI ; // String coloration string str_bold(string s){ return g_BOLD_COLOR_ANSI + s + g_END_COLOR_ANSI; } string str_special(string s){ return g_SPECIAL_COLOR_ANSI + s + g_END_COLOR_ANSI; } string value_to_hex_str(int octets, addr_t addr){ char res[32], format[32]; // Get format (32 or 64 bits) snprintf(format, sizeof(format), "%%0%02dllx", octets*2); // Write hex bytes snprintf(res, sizeof(res), format, addr); return "0x"+string(res); } void disable_colors(){ g_ERROR_COLOR_ANSI = ""; g_BOLD_COLOR_ANSI = ""; g_SPECIAL_COLOR_ANSI = ""; g_PAYLOAD_COLOR_ANSI = ""; g_EXPLOIT_DESCRIPTION_ANSI = ""; g_END_COLOR_ANSI = ""; } void enable_colors(){ g_ERROR_COLOR_ANSI = DEFAULT_ERROR_COLOR_ANSI; g_BOLD_COLOR_ANSI = DEFAULT_BOLD_COLOR_ANSI; g_SPECIAL_COLOR_ANSI = DEFAULT_SPECIAL_COLOR_ANSI; g_PAYLOAD_COLOR_ANSI = DEFAULT_PAYLOAD_COLOR_ANSI; g_EXPLOIT_DESCRIPTION_ANSI = DEFAULT_EXPLOIT_DESCRIPTION_ANSI; g_END_COLOR_ANSI = DEFAULT_END_COLOR_ANSI ; } /* ========= Catching ctrl+C ============= */ struct sigaction g_ropium_sigint_handler; struct sigaction g_ropium_prev_sigint_handler; bool g_ropium_sigint_flag = false; void ropium_sigint_handler(int s){ g_ropium_sigint_flag = true; } void set_sigint_handler(){ g_ropium_sigint_handler.sa_handler = ropium_sigint_handler; sigemptyset(&g_ropium_sigint_handler.sa_mask); g_ropium_sigint_handler.sa_flags = 0; sigaction(SIGINT, &g_ropium_sigint_handler, &g_ropium_prev_sigint_handler); } void unset_signint_handler(){ sigaction(SIGINT, &g_ropium_prev_sigint_handler, nullptr); } bool is_pending_sigint(){ return g_ropium_sigint_flag; } void notify_sigint_handled(){ g_ropium_sigint_flag = false; } ================================================ FILE: tests/ressources/gadgets.txt ================================================ 0xaaaaaaa$505BFFE3 0x12345678$89D8C3 ================================================ FILE: tests/test_all.cpp ================================================ #include "exception.hpp" #include #include #include #include using std::cout; using std::endl; using std::string; void test_expression(); void test_simplification(); void test_ir(); void test_database(); void test_gadgets(); void test_strategy(); void test_il(); void test_compiler(); int main(int argc, char ** argv){ string bold = "\033[1m"; string def = "\033[0m"; string red = "\033[1;31m"; string green = "\033[1;32m"; cout << bold << "\nRunnning ROPium unit-tests" << def << endl << "==========================" << endl << endl; for(int i = 0; i < 1; i++){ try{ if( argc == 1 ){ /* If no args specified, test all */ test_expression(); test_simplification(); test_ir(); test_gadgets(); test_database(); test_strategy(); test_il(); test_compiler(); }else{ /* Iterate through all options */ for( int i = 1; i < argc; i++){ if( !strcmp(argv[i], "expr")) test_expression(); else if (!strcmp(argv[i], "simp")) test_simplification(); else if (!strcmp(argv[i], "ir")) test_ir(); else if( !strcmp(argv[i], "db")) test_database(); else if( !strcmp(argv[i], "gadgets")) test_gadgets(); else if( !strcmp(argv[i], "strategy")) test_strategy(); else if( !strcmp(argv[i], "il")) test_il(); else if( !strcmp(argv[i], "compiler")) test_compiler(); else std::cout << "[" << red << "!" << def << "] Skipping unknown test: " << argv[i] << std::endl; } } }catch(test_exception& e){ cout << red << "Fatal: Unit test failed" << def << endl << endl; return 1; } } cout << endl; return 0; } ================================================ FILE: tests/test_compiler.cpp ================================================ #include "compiler.hpp" #include "arch.hpp" #include "exception.hpp" #include #include #include #include #include using std::cout; using std::endl; using std::string; namespace test{ namespace compiler{ unsigned int _assert_ropchain(ROPChain* ropchain, const string& msg){ if( ropchain == nullptr){ cout << "\nFail: " << msg << endl << std::flush; throw test_exception(); } delete ropchain; return 1; } unsigned int _assert_no_ropchain(ROPChain* ropchain, const string& msg){ if( ropchain != nullptr){ cout << "\nFail: " << msg << endl << std::flush; throw test_exception(); } return 1; } unsigned int direct_match(){ unsigned int nb = 0; ArchX86 arch; GadgetDB db; ROPCompiler comp = ROPCompiler(&arch, &db); ROPChain* ropchain; // Available gadgets vector raw; raw.push_back(RawGadget(string("\x89\xf9\xbb\x01\x00\x00\x00\xc3", 8), 1)); // mov ecx, edi; mov ebx, 1; ret raw.push_back(RawGadget(string("\x89\xC8\xC3", 3), 2)); // mov eax, ecx; ret raw.push_back(RawGadget(string("\x89\xC3\xC3", 3), 3)); // mov ebx, eax; ret raw.push_back(RawGadget(string("\x01\xf0\x89\xc3\xc3", 5), 4)); // add eax, esp; mov ebx, eax; ret raw.push_back(RawGadget(string("\xbb\x04\x00\x00\x00\xc3", 6), 5)); // mov ebx, 4; ret raw.push_back(RawGadget(string("\x83\xc0\x04\x89\xc3\xc3", 6), 6)); // add eax, 4; mov ebx, eax; ret raw.push_back(RawGadget(string("\x8b\x59\xf7\x89\xd8\xc3", 6), 7)); // mov ebx, [ecx-9]; mov eax, ebx; ret raw.push_back(RawGadget(string("\x03\x39\xc3", 3), 8)); // add edi, [ecx]; ret raw.push_back(RawGadget(string("\xb9\x0a\x00\x00\x00\xc3", 6), 9)); // mov ecx, 10; ret raw.push_back(RawGadget(string("\x89\x0f\x89\x5e\xfd\xc3", 6), 10)); // mov [edi], ecx; mov [esi-3], ebx; ret raw.push_back(RawGadget(string("\xbe\x16\x00\x00\x00\xc3", 6), 11)); // mov esi, 22; ret raw.push_back(RawGadget(string("\xbf\x78\x56\x34\x12\xc3", 6), 12)); // mov edi, 0x12345678; ret raw.push_back(RawGadget(string("\x01\x21\xc3", 3), 13)); // add [ecx], esp; ret raw.push_back(RawGadget(string("\x33\x79\xf6\xc3", 4), 14)); // xor edi, [ecx-10]; ret raw.push_back(RawGadget(string("\x83\xc9\xff\xc3", 4), 15)); // or ecx, 0xffffffff; ret raw.push_back(RawGadget(string("\x21\x49\xf7\xc3", 4), 16)); // and [ecx-9], ecx; ret raw.push_back(RawGadget(string("\x01\x1E\xC3", 3), 17)); // add [esi], ebx; ret db.analyse_raw_gadgets(raw, &arch); // Test basic queries ropchain = comp.compile("eax = ecx"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile(" ebx = 4"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile(" ebx = eax + 4"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile(" ebx = eax + esi "); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile(" ebx = [ ecx - 0x9] "); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile(" ebx = [ 1] "); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile(" [edi] = ecx"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile(" [ esi- 0x3] = ebx"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile(" [19] = ebx"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile(" [0x12345678] = ecx"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile(" edi += [ecx]"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile(" edi ^= [0]"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile(" [ecx+0x000 ] += esp \t\t\n\t "); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile(" ecx = -1"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile(" [22] += 4"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); return nb; } unsigned int indirect_match(){ unsigned int nb = 0; ArchX86 arch; GadgetDB db; ROPCompiler comp = ROPCompiler(&arch, &db); ROPChain* ropchain; Constraint constr; constr.bad_bytes.add_bad_byte(0xff); // Available gadgets vector raw; raw.push_back(RawGadget(string("\x89\xf9\xbb\x01\x00\x00\x00\xc3", 8), 1)); // mov ecx, edi; mov ebx, 1; ret raw.push_back(RawGadget(string("\x89\xC8\xC3", 3), 2)); // mov eax, ecx; ret raw.push_back(RawGadget(string("\x89\xC3\xC3", 3), 3)); // mov ebx, eax; ret raw.push_back(RawGadget(string("\xb9\xad\xde\x00\x00\xc3", 6), 4)); // mov ecx, 0xdead; ret raw.push_back(RawGadget(string("\x5f\x5e\x59\xc3", 4), 5)); // pop edi; pop esi; pop ecx; ret raw.push_back(RawGadget(string("\x89\xE8\xFF\xE6", 4), 6)); // mov eax, ebp; jmp esi raw.push_back(RawGadget(string("\x89\xF1\xFF\xE0", 4), 7)); // mov ecx, esi; jmp eax raw.push_back(RawGadget(string("\x5A\x59\xC3", 3), 8)); // pop edx; pop ecx; ret raw.push_back(RawGadget(string("\x8B\x40\x08\xC3", 4), 9)); // mov eax, [eax + 8]; ret raw.push_back(RawGadget(string("\x8D\x4B\x08\xC3", 4), 10)); // lea ecx, [ebx + 8]; ret raw.push_back(RawGadget(string("\x8D\x40\x20\xFF\xE1", 5), 11)); // lea eax, [eax + 32]; jmp ecx; raw.push_back(RawGadget(string("\x89\x43\x08\xC3", 4), 12)); // mov [ebx + 8], eax; ret db.analyse_raw_gadgets(raw, &arch); // Test mov_reg_transitivity ropchain = comp.compile("eax = edi"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile("ebx = edi"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); // Test mov_cst_transitivity ropchain = comp.compile("eax = 0xdead"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile("ebx = 0xdead"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); // Test mov_cst pop ropchain = comp.compile(" edi = -2"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile(" eax = 0x12345678 "); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); // Test generic adjust jmp ropchain = comp.compile(" ebx = ebp"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile(" eax = esi"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); // Test adjust load ropchain = comp.compile(" eax = [ebx+16]"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile(" eax = [eax+40]"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); // Test src transitivity ropchain = comp.compile(" [ebx+8] = ebp"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); // Test adjust store ropchain = comp.compile(" [eax+8] = ecx"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); return nb; } unsigned int store_string(){ unsigned int nb = 0; ArchX86 arch; GadgetDB db; ROPCompiler comp = ROPCompiler(&arch, &db); ROPChain* ropchain; Constraint constr; constr.bad_bytes.add_bad_byte(0xff); // Available gadgets vector raw; raw.push_back(RawGadget(string("\x89\xf9\xbb\x01\x00\x00\x00\xc3", 8), 1)); // mov ecx, edi; mov ebx, 1; ret raw.push_back(RawGadget(string("\x89\xC8\xC3", 3), 2)); // mov eax, ecx; ret raw.push_back(RawGadget(string("\x89\xC3\xC3", 3), 3)); // mov ebx, eax; ret raw.push_back(RawGadget(string("\xb9\xad\xde\x00\x00\xc3", 6), 4)); // mov ecx, 0xdead; ret raw.push_back(RawGadget(string("\x5f\x5e\x59\xc3", 4), 5)); // pop edi; pop esi; pop ecx; ret raw.push_back(RawGadget(string("\x89\xE8\xFF\xE6", 4), 6)); // mov eax, ebp; jmp esi raw.push_back(RawGadget(string("\x89\xF1\xFF\xE0", 4), 7)); // mov ecx, esi; jmp eax raw.push_back(RawGadget(string("\x5A\x59\xC3", 3), 8)); // pop edx; pop ecx; ret raw.push_back(RawGadget(string("\x8B\x40\x08\xC3", 4), 9)); // mov eax, [eax + 8]; ret raw.push_back(RawGadget(string("\x8D\x4B\x08\xC3", 4), 10)); // lea ecx, [ebx + 8]; ret raw.push_back(RawGadget(string("\x8D\x40\x20\xFF\xE1", 5), 11)); // lea eax, [eax + 32]; jmp ecx; raw.push_back(RawGadget(string("\x89\x43\x08\xC3", 4), 12)); // mov [ebx + 8], eax; ret db.analyse_raw_gadgets(raw, &arch); // Test adjust store ropchain = comp.compile(" [0x1234] = 'lala'"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); ropchain = comp.compile(" [0x1234] = 'lalatotoo\\x00'"); nb += _assert_ropchain(ropchain, "Failed to find ropchain"); return nb; } unsigned int incorrect_match(){ unsigned int nb = 0; ArchX86 arch; GadgetDB db; ROPCompiler comp = ROPCompiler(&arch, &db); ROPChain* ropchain; Constraint constr; constr.bad_bytes.add_bad_byte(0xff); // Test when adjust gadget clobbers reg that must be set // Here gadget 2 and 3 both modify ecx vector raw; raw.push_back(RawGadget(string("\x89\xF1\xFF\xE0", 4), 1)); // mov ecx, esi; jmp eax raw.push_back(RawGadget(string("\x59\xC3", 2), 2)); // pop ecx; ret raw.push_back(RawGadget(string("\x58\x59\xC3", 3), 3)); // pop eax; pop ecx; ret db.analyse_raw_gadgets(raw, &arch); ropchain = comp.compile(" ecx = esi"); nb += _assert_no_ropchain(ropchain, "Found ropchain but no ropchain should exist"); // Test when adjust gadget clobbers input register // Here gadget 2 can set eax but modifies esi db.clear(); raw.clear(); raw.push_back(RawGadget(string("\x89\xF1\xFF\xE0", 4), 1)); // mov ecx, esi; jmp eax raw.push_back(RawGadget(string("\x5E\x58\xC3", 3), 2)); // pop esi; pop eax; ret raw.push_back(RawGadget(string("\xC3", 1), 3)); // ret db.analyse_raw_gadgets(raw, &arch); ropchain = comp.compile(" ecx = esi"); nb += _assert_no_ropchain(ropchain, "Found ropchain but no ropchain should exist"); return nb; } unsigned int function_call_x86(){ unsigned int nb = 0; ArchX86 arch; GadgetDB db; ROPCompiler comp = ROPCompiler(&arch, &db); ROPChain* ropchain; Constraint constr; constr.bad_bytes.add_bad_byte(0xff); // Test when adjust gadget clobbers reg that must be set // Here gadget 2 and 3 both modify ecx vector raw; raw.push_back(RawGadget(string("\x58\xFF\xE0", 3), 1)); // pop eax; jmp eax raw.push_back(RawGadget(string("\xC3", 1), 2)); // ret raw.push_back(RawGadget(string("\x59\xC3", 2), 3)); // pop ecx; ret raw.push_back(RawGadget(string("\x83\xC4\x0C\xC3", 4), 4)); // add esp, 12; ret db.analyse_raw_gadgets(raw, &arch); // X86 CDECL ABI ropchain = comp.compile(" 0x1234(42)", nullptr, ABI::X86_CDECL); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to call function"); ropchain = comp.compile(" 0x12345678(42, -1, 43)", nullptr, ABI::X86_CDECL); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to call function"); ropchain = comp.compile(" 0()", nullptr, ABI::X86_CDECL); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to call function"); // X86 STDCALL ABI ropchain = comp.compile(" 0x1234(42)", nullptr, ABI::X86_STDCALL); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to call function"); ropchain = comp.compile(" 0x12345678(42, -1, 43)", nullptr, ABI::X86_STDCALL); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to call function"); ropchain = comp.compile(" 0()", nullptr, ABI::X86_STDCALL); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to call function"); return nb; } unsigned int function_call_x64(){ unsigned int nb = 0; ArchX64 arch; GadgetDB db; ROPCompiler comp = ROPCompiler(&arch, &db); ROPChain* ropchain; Constraint constr; constr.bad_bytes.add_bad_byte(0xff); // Test when adjust gadget clobbers reg that must be set // Here gadget 2 and 3 both modify ecx vector raw; raw.push_back(RawGadget(string("\x58\xFF\xE0", 3), 1)); // pop rax; jmp rax raw.push_back(RawGadget(string("\xC3", 1), 2)); // ret raw.push_back(RawGadget(string("\x5F\xC3", 2), 3)); // pop rdi; ret raw.push_back(RawGadget(string("\x48\x83\xC4\x08\xC3", 5), 4)); // add rsp, 8; ret raw.push_back(RawGadget(string("\x5E\xC3", 2), 5)); // pop rsi; ret raw.push_back(RawGadget(string("\x5A\xC3", 2), 6)); // pop rdx; ret raw.push_back(RawGadget(string("\x59\xC3", 2), 7)); // pop rcx; ret raw.push_back(RawGadget(string("\x41\x58\xc3", 3), 8)); // pop r8; ret raw.push_back(RawGadget(string("\x41\x59\x59\xc3", 4), 9)); // pop r9; pop rcx; ret raw.push_back(RawGadget(string("\x48\x89\xef\xc3",4), 10)); // mov rdi, rbp; ret raw.push_back(RawGadget(string("\x48\x83\xC4\x18\xC3", 5), 11)); // add rsp, 24; ret raw.push_back(RawGadget(string("\x49\x89\xd1\xc3",4), 12)); // mov r9, rdx; ret raw.push_back(RawGadget(string("\x48\x83\xC4\x10\xC3", 5), 13)); // add rsp, 16; ret db.analyse_raw_gadgets(raw, &arch); // X64 SYSTEM V ropchain = comp.compile(" 0x1234(42)", nullptr, ABI::X64_SYSTEM_V); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to call function"); ropchain = comp.compile(" 0x1234(1, 2, 3, 4)", nullptr, ABI::X64_SYSTEM_V); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to call function"); ropchain = comp.compile(" 0x1234(rbp, 2, 3, 4, 5, 6)", nullptr, ABI::X64_SYSTEM_V); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to call function"); ropchain = comp.compile(" 0x1234(rbp, 2, 3, 4, 5, 6, 7, 8)", nullptr, ABI::X64_SYSTEM_V); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to call function"); ropchain = comp.compile(" 0x1234(rbp, 2, 3, 4, 5, 6, 7, 8, 9)", nullptr, ABI::X64_SYSTEM_V); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to call function"); // X64 Microsoft ropchain = comp.compile(" 0x1234(42)", nullptr, ABI::X64_MS); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to call function"); ropchain = comp.compile(" 0x1234(1, 2, 3, rdx)", nullptr, ABI::X64_MS); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to call function"); ropchain = comp.compile(" 0x1234(1, 2, 3, 4, 5, 6)", nullptr, ABI::X64_MS); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to call function"); ropchain = comp.compile(" 0x1234(1, 2, 3, rdx, 5, 6, 7)", nullptr, ABI::X64_MS); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to call function"); return nb; } unsigned int syscall_x86(){ unsigned int nb = 0; ArchX86 arch; GadgetDB db; ROPCompiler comp = ROPCompiler(&arch, &db); ROPChain* ropchain; Constraint constr; constr.bad_bytes.add_bad_byte(0xff); vector raw; raw.push_back(RawGadget(string("\x58\xC3", 2), 1)); // pop eax; ret raw.push_back(RawGadget(string("\x5B\xC3", 2), 2)); // pop ebx; ret raw.push_back(RawGadget(string("\x83\xC5\x20\x0F\x34", 5), 3)); // add ebp, 32; sysenter raw.push_back(RawGadget(string("\x59\xC3", 2), 4)); // pop ecx; ret raw.push_back(RawGadget(string("\x59\x5A\xC3", 3), 5)); // pop ecx; pop edx; ret raw.push_back(RawGadget(string("\x58\x89\xC6\xC3", 4), 6)); // pop eax; mov esi, eax; ret raw.push_back(RawGadget(string("\x89\xDA\xC3", 3), 7)); // mov edx, ebx; ret db.analyse_raw_gadgets(raw, &arch); // X86 Linux ropchain = comp.compile(" sys_exit(1)", nullptr, ABI::NONE, System::LINUX); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to make syscall"); ropchain = comp.compile(" sys_execve( 3, 2, 1)", nullptr, ABI::NONE, System::LINUX); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to make syscall"); ropchain = comp.compile(" sys_execve( 3, 2, ebx)", nullptr, ABI::NONE, System::LINUX); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to make syscall"); ropchain = comp.compile(" sys_ptrace( 3, 2, 1, 2)", nullptr, ABI::NONE, System::LINUX); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to make syscall"); ropchain = comp.compile(" sys_123(1, 2, 3) ", nullptr, ABI::NONE, System::LINUX); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to make syscall"); return nb; } unsigned int syscall_x64(){ unsigned int nb = 0; ArchX64 arch; GadgetDB db; ROPCompiler comp = ROPCompiler(&arch, &db); ROPChain* ropchain; Constraint constr; constr.bad_bytes.add_bad_byte(0xff); vector raw; raw.push_back(RawGadget(string("\x58\xC3", 2), 1)); // pop rax; ret raw.push_back(RawGadget(string("\x5F\xC3", 2), 2)); // pop rdi; ret raw.push_back(RawGadget(string("\x83\xC5\x20\x0F\x05", 5), 3)); // add ebp, 32; syscall raw.push_back(RawGadget(string("\x5E\xC3", 2), 4)); // pop rsi; ret raw.push_back(RawGadget(string("\x59\x5A\xC3", 3), 5)); // pop rcx; pop rdx; ret raw.push_back(RawGadget(string("\x58\x48\x89\xC6\xC3", 5), 6)); // pop rax; mov rsi, rax; ret raw.push_back(RawGadget(string("\x48\x89\xF2\xC3", 4), 7)); // mov rdx, rsi; ret db.analyse_raw_gadgets(raw, &arch); // X64 Linux ropchain = comp.compile(" sys_exit(1)", nullptr, ABI::NONE, System::LINUX); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to make syscall"); ropchain = comp.compile(" sys_execve(1, 2, 3)", nullptr, ABI::NONE, System::LINUX); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to make syscall"); ropchain = comp.compile(" sys_execve(1, 2, rsi)", nullptr, ABI::NONE, System::LINUX); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to make syscall"); ropchain = comp.compile(" sys_0x42(1, 2, rsi)", nullptr, ABI::NONE, System::LINUX); nb += _assert_ropchain(ropchain, "Couldn't build ropchain to make syscall"); return nb; } } } using namespace test::compiler; // All unit tests void test_compiler(){ unsigned int total = 0; string green = "\033[1;32m"; string def = "\033[0m"; string bold = "\033[1m"; // Start testing cout << bold << "[" << green << "+" << def << bold << "]" << def << std::left << std::setw(34) << " Testing ROP compiler... " << std::flush; total += direct_match(); total += indirect_match(); total += function_call_x86(); total += function_call_x64(); total += syscall_x86(); total += syscall_x64(); total += store_string(); total += incorrect_match(); // Return res cout << "\t" << total << "/" << total << green << "\t\tOK" << def << endl; } ================================================ FILE: tests/test_database.cpp ================================================ #include "database.hpp" #include "exception.hpp" #include #include #include #include #include #include using std::cout; using std::endl; using std::string; using std::make_tuple; using std::tuple; namespace test{ namespace database{ unsigned int _assert(bool val, const string& msg){ if( !val){ cout << "\nFail: " << msg << endl << std::flush; throw test_exception(); } return 1; } unsigned int base_db(){ BaseDB> db; int nb = 0; Gadget *g1 = new Gadget(), *g2 = new Gadget(); vector all; g1->id = 0; g2->id = 1; all.push_back(g1); all.push_back(g2); db.add(make_tuple(1,2), g1); nb += _assert(db.get(make_tuple(1,2))[0] == g1, "BaseDB, failed to add then get gadget"); db.add(make_tuple(1,4456), g2); nb += _assert(db.get(make_tuple(1,4456))[0] == g2, "BaseDB, failed to add then get gadget"); delete g1; delete g2; return nb; } unsigned int _assert_db(addr_t addr, const vector& list){ for( Gadget* g : list ){ if( std::find(g->addresses.begin(), g->addresses.end(), addr) != g->addresses.end() ) return 1; } cout << "\nFail: " << "GadgetDB: failed to classify/return gadget correctly" << endl << std::flush; throw test_exception(); } unsigned int classification(){ unsigned int nb = 0; Arch* arch = new ArchX86(); GadgetDB db; vector raw; raw.push_back(RawGadget(string("\xb8\x03\x00\x00\x00\xc3", 6), 0)); // mov eax, 3; ret raw.push_back(RawGadget(string("\x89\xf9\xbb\x01\x00\x00\x00\xc3", 8), 1)); // mov ecx, edi; mov ebx, 1; ret raw.push_back(RawGadget(string("\xb8\x03\x00\x00\x00\xc3", 6), 2)); // mov eax, 3; ret raw.push_back(RawGadget(string("\x83\xc0\x02\x89\xc6\xc3", 6), 3)); // add eax, 2; mov esi, eax; ret raw.push_back(RawGadget(string("\x81\xea\x34\x12\x00\x00\xc3", 7), 4)); // sub edx, 0x1234; ret raw.push_back(RawGadget(string("\x01\xe5\xc3", 3), 5)); // add ebp, esp; ret raw.push_back(RawGadget(string("\x58\x5e\xc3", 3), 6)); // pop eax; pop esi; ret raw.push_back(RawGadget(string("\x8b\x59\xf7\x89\xd8\xc3", 6), 7)); // mov ebx, [ecx-9]; mov eax, ebx; ret raw.push_back(RawGadget(string("\x03\x39\xc3", 3), 8)); // add edi, [ecx]; ret raw.push_back(RawGadget(string("\x33\x79\xf6\xc3", 4), 9)); // xor edi, [ecx-10]; ret raw.push_back(RawGadget(string("\xff\xe0", 2), 10)); // jmp eax; raw.push_back(RawGadget(string("\x89\x0f\x89\x5e\xfd\xc3", 6), 11)); // mov [edi], ecx; mov [esi-3], ebx; ret raw.push_back(RawGadget(string("\x01\x21\xc3", 3), 12)); // add [ecx], esp; ret raw.push_back(RawGadget(string("\x21\x49\xf7\xc3", 4), 13)); // and [ecx-9], ecx; ret raw.push_back(RawGadget(string("\x83\xC0\x03\xCD\x80", 5), 14)); // add eax, 3; int 0x80 raw.push_back(RawGadget(string("\x83\xC5\x20\x0F\x34", 5), 15)); // add ebp, 32; sysenter db.analyse_raw_gadgets(raw, arch); // Test gadget classification nb += _assert_db(0, db.get_mov_cst(X86_EAX, 3)); nb += _assert_db(1, db.get_mov_cst(X86_EBX, 1)); nb += _assert_db(1, db.get_mov_reg(X86_ECX, X86_EDI)); nb += _assert_db(2, db.get_mov_cst(X86_EAX, 3)); nb += _assert_db(3, db.get_amov_cst(X86_EAX, X86_EAX, Op::ADD, 2)); nb += _assert_db(3, db.get_amov_cst(X86_ESI, X86_EAX, Op::ADD, 2)); nb += _assert_db(4, db.get_amov_cst(X86_EDX, X86_EDX, Op::ADD, -0x1234)); nb += _assert_db(5, db.get_amov_reg(X86_EBP, X86_ESP, Op::ADD, X86_EBP)); nb += _assert_db(6, db.get_load(X86_EAX, X86_ESP, 0)); nb += _assert_db(6, db.get_load(X86_ESI, X86_ESP, 4)); nb += _assert_db(7, db.get_load(X86_EBX, X86_ECX, -9)); nb += _assert_db(7, db.get_load(X86_EAX, X86_ECX, -9)); nb += _assert_db(8, db.get_aload(X86_EDI, Op::ADD, X86_ECX, 0)); nb += _assert_db(9, db.get_aload(X86_EDI, Op::XOR, X86_ECX, -10)); nb += _assert_db(10, db.get_jmp(X86_EAX)); nb += _assert_db(10, db.get_mov_reg(X86_EIP, X86_EAX)); nb += _assert_db(11, db.get_store(X86_EDI, 0, X86_ECX)); nb += _assert_db(11, db.get_store(X86_ESI, -3, X86_EBX)); nb += _assert_db(12, db.get_astore(X86_ECX, 0, Op::ADD, X86_ESP)); nb += _assert_db(13, db.get_astore(X86_ECX, -9, Op::AND, X86_ECX)); nb += _assert_db(14, db.get_int80()); nb += _assert_db(15, db.get_syscall()); delete arch; return nb; } unsigned int classification_x64(){ unsigned int nb = 0; Arch* arch = new ArchX64(); GadgetDB db; vector raw; raw.push_back(RawGadget(string("\x83\xC5\x20\x0F\x05", 5), 1)); // add ebp, 32; syscall db.analyse_raw_gadgets(raw, arch); // Test gadget classification nb += _assert_db(1, db.get_syscall()); delete arch; return nb; } } } using namespace test::database; // All unit tests void test_database(){ unsigned int total = 0; string green = "\033[1;32m"; string def = "\033[0m"; string bold = "\033[1m"; // Start testing cout << bold << "[" << green << "+" << def << bold << "]" << def << std::left << std::setw(34) << " Testing gadget database... " << std::flush; total += base_db(); total += classification(); total += classification_x64(); // Return res cout << "\t" << total << "/" << total << green << "\t\tOK" << def << endl; } ================================================ FILE: tests/test_expression.cpp ================================================ #include "expression.hpp" #include "simplification.hpp" #include "exception.hpp" #include #include #include #include #include using std::cout; using std::endl; using std::string; namespace test{ namespace expression{ // Individual unit tests unsigned int basic(){ Expr e1, e2, e3, e4, e5, e6, e7, e8; for( int i = 0; i < 10; i++){ e1 = exprcst(32, -1); e2 = exprcst(32, 1048567); e3 = exprmem(32, e2); e4 = -e1; e5 = e2 - e1; e6 = extract(e1, 31, 23); e7 = e6; } return 0; } /* Expression hashing */ unsigned int _assert_hash_eq(Expr e1, Expr e2){ if( e1->hash() != e2->hash() ){ cout << endl << "Fail: _assert_hash_eq: " << e1 << " == " << e2 << endl; throw test_exception(); } return 1; } unsigned int _assert_hash_neq(Expr e1, Expr e2){ if( e1->hash() == e2->hash() ){ cout << endl << "Fail: _assert_hash_eq: " << e1 << " == " << e2 << endl; throw test_exception(); } return 1; } unsigned hashing(){ Expr e1 = exprcst(32,1), e2 = exprvar(32, "var1"), e3 = exprmem(32, e2), e4 = -e1, e5 = e2 & e3, e6 = exprmem(32, e5), e7 = exprmem(32, e6), e8 = bisz(32, e5, 1), e9 = e3 % e5;; unsigned int nb = 0; // Hash equality nb += _assert_hash_eq(e1, exprcst(32,1)); nb += _assert_hash_eq(e2, exprvar(32, "var1")); nb += _assert_hash_eq(e3, exprmem(32, e2)); nb += _assert_hash_eq(e4, (-e1)); nb += _assert_hash_eq(e5, (e2 & e3)); nb += _assert_hash_eq(e6, exprmem(32, e5)); nb += _assert_hash_eq(e7, exprmem(32,e6)); nb += _assert_hash_eq(e8, bisz(32, e5, 1)); nb += _assert_hash_eq(e9, e3%e5); // Hash inequality nb += _assert_hash_neq(e1, e2); nb += _assert_hash_neq(e2,e3); nb += _assert_hash_neq(e3,e4); nb += _assert_hash_neq(e4,e5); nb += _assert_hash_neq(e5,e6); nb += _assert_hash_neq(e6,e7); nb += _assert_hash_neq(e8, bisz(32, e5, 0)); nb += _assert_hash_neq(e9, e5%e3); return nb; } /* Expression Canonization */ unsigned int _assert_canonize_eq(Expr e1, Expr e2 ){ Expr tmp1 = expr_canonize(e1), tmp2 = expr_canonize(e2); if(!(tmp1->eq(tmp2))){ cout << endl << "Fail: _assert_canonize_eq: " << e1 << " <==> " << e2 << endl << "Note: canonized as : " << tmp1 << " <==> " << tmp2 << endl << std::flush; throw test_exception(); } return 1; } unsigned int _assert_canonize_neq(Expr e1, Expr e2 ){ Expr tmp1 = expr_canonize(e1), tmp2 = expr_canonize(e2); if(!tmp1->neq(tmp2)){ cout << endl << "Fail: _assert_canonize_neq: " << e1 << " <=/=> " << e2 << endl << "Note: canonized as : " << tmp1 << " <==> " << tmp2 << endl << std::flush; throw test_exception(); } return 1; } unsigned int canonize(){ Expr cst1 = exprcst(32, 1), cst2 = exprcst(32, 567), var1 = exprvar(32, "var1"), var2 = exprvar(32, "var2"), var3 = exprvar(32, "var3"), un1 = -var2, bin1 = var1+var2, bin2 = var3/var2, bin3 = sdiv(var3,var2); unsigned int nb = 0; // a+b == b+a nb += _assert_canonize_eq((cst1+cst2), (cst2+cst1)); nb += _assert_canonize_eq((cst1+var1), (var1+cst1)); nb += _assert_canonize_eq((bin3+var1), (var1+bin3)); nb += _assert_canonize_eq((bin1+bin2), (bin2+bin1)); nb += _assert_canonize_eq((bin1+bin1), (bin1+bin1)); // a*b == b*a nb += _assert_canonize_eq((cst1*cst2), (cst2*cst1)); nb += _assert_canonize_eq((cst1*var1), (var1*cst1)); // (a^b)^c == (c^b)^a nb += _assert_canonize_eq( cst1^var1^bin3, cst1^bin3^var1); // a/b/c == a/c/b nb += _assert_canonize_eq( var2/var3/un1, var2/un1/var3); // a/b != b/a nb += _assert_canonize_neq(var3/cst1, cst1/var3); // a<concretize(&ctx) == 10, "Concretization gave wrong result"); nb += _assert( v2->concretize(&ctx) == -2, "Concretization gave wrong result"); nb += _assert( v3->concretize(&ctx) == 0xffff000000000000, "Concretization gave wrong result"); nb += _assert( v4->concretize(&ctx) == 0x0000ffffffffffff, "Concretization gave wrong result"); nb += _assert( (v1+v2)->concretize(&ctx) == exprcst(32, 8)->concretize(&ctx), "Concretization gave wrong result"); nb += _assert( (v1*v2)->concretize(&ctx) == exprcst(32, -20)->concretize(&ctx), "Concretization gave wrong result"); nb += _assert( (v1/v2)->concretize(&ctx) == exprcst(32, 0)->concretize(&ctx), "Concretization gave wrong result"); nb += _assert( sdiv(v1,v2)->concretize(&ctx) == exprcst(32, -5)->concretize(&ctx), "Concretization gave wrong result"); nb += _assert( (v1^v2)->concretize(&ctx) == exprcst(32, 10^-2)->concretize(&ctx), "Concretization gave wrong result"); nb += _assert( (v1|v2)->concretize(&ctx) == exprcst(32, 10|-2)->concretize(&ctx), "Concretization gave wrong result"); nb += _assert( extract(v2,31,24)->concretize(&ctx) == exprcst(8, 0xff)->concretize(&ctx), "Concretization gave wrong result"); nb += _assert( shr(v1,exprcst(32, 2))->concretize(&ctx) == 2, "Concretization gave wrong result"); nb += _assert( shl(exprcst(32, 0x800000001),exprcst(32, 2))->concretize(&ctx) == 4, "Concretization gave wrong result"); nb += _assert( concat(v1,v2)->concretize(&ctx) == 0x0000000afffffffe, "Concretization gave wrong result"); nb += _assert( bisz(16, v1, 1)->concretize(&ctx) == 0, "Concretization gave wrong result"); nb += _assert( bisz(16, v1, 0)->concretize(&ctx) == 1, "Concretization gave wrong result"); nb += _assert( bisz(4, exprcst(26, 0), 1)->concretize(&ctx) == 1, "Concretization gave wrong result"); nb += _assert( bisz(4, exprcst(26, 0), 0)->concretize(&ctx) == 0, "Concretization gave wrong result"); nb += _assert( smod(exprcst(32, -6), exprcst(32, 5))->concretize(&ctx) == -1, "Concretization gave wrong result"); nb += _assert( smod(exprcst(32, -10), exprcst(32,3))->concretize(&ctx) == -1, "Concretization gave wrong result"); nb += _assert( smod(exprcst(32, 10), exprcst(32,-3))->concretize(&ctx) == 1, "Concretization gave wrong result"); // multiplications nb += _assert( mulh(exprcst(64, 0xbbf543), exprcst(64, 0xfffffabc7865))->concretize(&ctx) == 0xbb, "Concretization gave wrong result"); nb += _assert( mulh(exprcst(32, 0xbbf543), exprcst(32, 0xc7865))->concretize(&ctx) == 0x927, "Concretization gave wrong result"); nb += _assert( smull(exprcst(8, 48), exprcst(8, 4))->concretize(&ctx) == 0xffffffffffffffc0, "Concretization gave wrong result"); nb += _assert( smulh(exprcst(8, 48), exprcst(8, 4))->concretize(&ctx) == 0, "Concretization gave wrong result"); nb += _assert( smull(exprcst(8, -4), exprcst(8, 4))->concretize(&ctx) == 0xfffffffffffffff0, "Concretization gave wrong result"); nb += _assert( smulh(exprcst(8, -4), exprcst(8, 4))->concretize(&ctx) == 0xffffffffffffffff, "Concretization gave wrong result"); nb += _assert( smull(exprcst(16, 48), exprcst(16, 4))->concretize(&ctx) == 0xc0, "Concretization gave wrong result"); nb += _assert( smulh(exprcst(16, 48), exprcst(16, 4))->concretize(&ctx) == 0, "Concretization gave wrong result"); nb += _assert( smull(exprcst(32, 4823424), exprcst(32, -423))->concretize(&ctx) == 0xffffffff86635D80, "Concretization gave wrong result"); nb += _assert( smulh(exprcst(32, 4823424), exprcst(32, -423))->concretize(&ctx) == 0xffffffffffffffff, "Concretization gave wrong result"); nb += _assert( smull(exprcst(32, -1), exprcst(32, -1))->concretize(&ctx) == 1, "Concretization gave wrong result"); nb += _assert( smulh(exprcst(32, -1), exprcst(32, -1))->concretize(&ctx) == 0, "Concretization gave wrong result"); nb += _assert( (-v3)->concretize(&ctx) == 0x0001000000000000, "Concretization gave wrong result"); nb += _assert( (~v4)->concretize(&ctx) == 0xffff000000000000, "Concretization gave wrong result"); nb += _assert( (v3^v4)->concretize(&ctx) == -1, "Concretization gave wrong result"); nb += _assert( (v4&v3)->concretize(&ctx) == 0, "Concretization gave wrong result"); nb += _assert( (v3|v4)->concretize(&ctx) == -1, "Concretization gave wrong result"); nb += _assert( (v3*v4)->concretize(&ctx) == 0xffff000000000000*0x0000ffffffffffff, "Concretization gave wrong result"); nb += _assert(( exprcst(32, 23)%exprcst(32, 2))->concretize(&ctx) == 1, "Concretization gave wrong result"); nb += _assert(( exprcst(32, 20)%exprcst(32, 27))->concretize(&ctx) == 20, "Concretization gave wrong result"); nb += _assert(( exprcst(32, 0xffffffff)%exprcst(32, 4))->concretize(&ctx) == ( exprcst(32, -1)%exprcst(32, 4))->concretize(&ctx), "Concretization gave wrong result"); nb += _assert( v1->concretize(&ctx) != v1->concretize(&ctx2), "Concretization with different contexts gave same result"); nb += _assert( v2->concretize(&ctx) != v2->concretize(&ctx2), "Concretization with different contexts gave same result"); nb += _assert( v3->concretize(&ctx) != v3->concretize(&ctx2), "Concretization with different contexts gave same result"); nb += _assert( v4->concretize(&ctx) != v4->concretize(&ctx2), "Concretization with different contexts gave same result"); nb += _assert( (v1|v2)->concretize(&ctx) != (v1|v2)->concretize(&ctx2), "Concretization with different contexts gave same result"); nb += _assert( e1->concretize(&ctx) != e1->concretize(&ctx2), "Concretization with different contexts gave same result"); nb += _assert( e2->concretize(&ctx) != e2->concretize(&ctx2), "Concretization with different contexts gave same result"); nb += _assert( e3->concretize(&ctx) != e3->concretize(&ctx2), "Concretization with different contexts gave same result"); nb += _assert( e4->concretize(&ctx) != e4->concretize(&ctx2), "Concretization with different contexts gave same result"); return nb; } unsigned int change_varctx(){ unsigned int nb = 0; VarContext ctx = VarContext(0); Expr v1 = exprvar(32, "var1" ), v2 = exprvar(32, "var2"), v3 = exprvar(64, "var3"), v4 = exprvar(64, "var4"), e1 = v1+v2, e2 = v3|v4; ctx.set("var1", 100); ctx.set("var2", -2); nb += _assert( v1->concretize(&ctx) == 100, "Concretization gave wrong result"); nb += _assert( v2->concretize(&ctx) == -2, "Concretization gave wrong result"); ctx.set("var1", 10); ctx.set("var3", 0xffff000000000000); ctx.set("var4", 0x0000ffffffffffff); nb += _assert( v1->concretize(&ctx) == 10, "Concretization gave wrong result"); nb += _assert( v2->concretize(&ctx) == -2, "Concretization gave wrong result"); nb += _assert( v3->concretize(&ctx) == 0xffff000000000000, "Concretization gave wrong result"); nb += _assert( v4->concretize(&ctx) == 0x0000ffffffffffff, "Concretization gave wrong result"); nb += _assert( e1->concretize(&ctx) == exprcst(32, 8)->concretize(&ctx), "Concretization gave wrong result"); nb += _assert( e2->concretize(&ctx) == exprcst(64, -1)->concretize(&ctx), "Concretization gave wrong result"); ctx.set("var2", -3); ctx.set("var4", 0xffff000000000000); nb += _assert( e1->concretize(&ctx) == exprcst(32, 7)->concretize(&ctx), "Concretization gave wrong result"); nb += _assert( e2->concretize(&ctx) == exprcst(64, 0xffff000000000000)->concretize(&ctx), "Concretization gave wrong result"); return nb; } } } using namespace test::expression; // All unit tests void test_expression(){ unsigned int total = 0; string green = "\033[1;32m"; string def = "\033[0m"; string bold = "\033[1m"; // Start testing cout << bold << "[" << green << "+" << def << bold << "]" << def << std::left << std::setw(34) << " Testing expression module... " << std::flush; total += basic(); total += canonize(); total += hashing(); total += concretization(); total += change_varctx(); // Return res cout << "\t" << total << "/" << total << green << "\t\tOK" << def << endl; } ================================================ FILE: tests/test_gadgets.cpp ================================================ #include "database.hpp" #include "exception.hpp" #include #include #include #include #include using std::cout; using std::endl; using std::string; namespace test{ namespace gadgets{ unsigned int _assert(bool val, const string& msg){ if( !val){ cout << "\nFail: " << msg << endl << std::flush; throw test_exception(); } return 1; } unsigned int basic(){ unsigned int nb = 0; Arch* arch = new ArchX86(); ropgadget_to_file("/tmp/gadgets.ropium", "/tmp/ropgagdet_tmp.ropium", "/usr/bin/nmap"); vector* raw = raw_gadgets_from_file("/tmp/gadgets.ropg"); delete raw; delete arch; return nb; } } } using namespace test::gadgets; // All unit tests void test_gadgets(){ unsigned int total = 0; string green = "\033[1;32m"; string def = "\033[0m"; string bold = "\033[1m"; // Start testing cout << bold << "[" << green << "+" << def << bold << "]" << def << std::left << std::setw(34) << " Testing gadget analysis... " << std::flush; total += basic(); // Return res cout << "\t" << total << "/" << total << green << "\t\tOK" << def << endl; } ================================================ FILE: tests/test_il.cpp ================================================ #include "il.hpp" #include "arch.hpp" #include "exception.hpp" #include #include #include #include #include #include "strategy.hpp" using std::cout; using std::endl; using std::string; namespace test{ namespace il{ unsigned int _assert(bool val, const string& msg){ if( !val){ cout << "\nFail: " << msg << endl << std::flush; throw test_exception(); } return 1; } unsigned int il_parser(){ unsigned int nb = 0; ArchX86 arch; // mov reg string str = " eax = ebx"; ILInstruction instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::MOV_REG, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_MOVREG_DST_REG] == X86_EAX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_MOVREG_SRC_REG] == X86_EBX, "Failed to parse IL Instruction"); str = "ecx= esi "; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::MOV_REG, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_MOVREG_DST_REG] == X86_ECX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_MOVREG_SRC_REG] == X86_ESI, "Failed to parse IL Instruction"); // mov cst str = "eip= 1234 "; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::MOV_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_MOVCST_DST_REG] == X86_EIP, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_MOVCST_SRC_CST] == 1234, "Failed to parse IL Instruction"); str = " edx =12345"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::MOV_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_MOVCST_DST_REG] == X86_EDX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_MOVCST_SRC_CST] == 12345, "Failed to parse IL Instruction"); str = "eip = 0x1234 "; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::MOV_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_MOVCST_DST_REG] == X86_EIP, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_MOVCST_SRC_CST] == 0x1234, "Failed to parse IL Instruction"); str = "eip = -0x1234 "; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::MOV_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_MOVCST_DST_REG] == X86_EIP, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_MOVCST_SRC_CST] == -0x1234, "Failed to parse IL Instruction"); str = "eip = - 0x1234 "; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::MOV_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_MOVCST_DST_REG] == X86_EIP, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_MOVCST_SRC_CST] == -0x1234, "Failed to parse IL Instruction"); // amov cst str = " esp = eax + 42 \n"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::AMOV_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_AMOVCST_DST_REG] == X86_ESP, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_AMOVCST_SRC_REG] == X86_EAX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_AMOVCST_SRC_OP] == (int)Op::ADD, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_AMOVCST_SRC_CST] == 42, "Failed to parse IL Instruction"); str = " esp = esi >>0x3 \n"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::AMOV_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_AMOVCST_DST_REG] == X86_ESP, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_AMOVCST_SRC_REG] == X86_ESI, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_AMOVCST_SRC_OP] == (int)Op::SHR, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_AMOVCST_SRC_CST] == 3, "Failed to parse IL Instruction"); // amov reg str = " esp = eax * ebp \n\n"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::AMOV_REG, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_AMOVREG_DST_REG] == X86_ESP, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_AMOVREG_SRC_REG1] == X86_EAX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_AMOVREG_SRC_OP] == (int)Op::MUL, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_AMOVREG_SRC_REG2] == X86_EBP, "Failed to parse IL Instruction"); str = " esp = esi << esi"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::AMOV_REG, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_AMOVREG_DST_REG] == X86_ESP, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_AMOVREG_SRC_REG1] == X86_ESI, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_AMOVREG_SRC_OP] == (int)Op::SHL, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_AMOVREG_SRC_REG2] == X86_ESI, "Failed to parse IL Instruction"); // load str = " eax = [ esp + 32 ] \t\n"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::LOAD, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_LOAD_DST_REG] == X86_EAX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_LOAD_SRC_ADDR_REG] == X86_ESP, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_LOAD_SRC_ADDR_OFFSET] == 32, "Failed to parse IL Instruction"); str = " eax =[esi-0xabcd ]"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::LOAD, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_LOAD_DST_REG] == X86_EAX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_LOAD_SRC_ADDR_REG] == X86_ESI, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_LOAD_SRC_ADDR_OFFSET] == -0xabcd, "Failed to parse IL Instruction"); // aload str = " eax *= [ esp + 32 ] \t\n"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::ALOAD, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ALOAD_DST_REG] == X86_EAX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ALOAD_OP] == (int)Op::MUL, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ALOAD_SRC_ADDR_REG] == X86_ESP, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ALOAD_SRC_ADDR_OFFSET] == 32, "Failed to parse IL Instruction"); str = " eax <<=[esi]"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::ALOAD, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ALOAD_DST_REG] == X86_EAX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ALOAD_OP] == (int)Op::SHL, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ALOAD_SRC_ADDR_REG] == X86_ESI, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ALOAD_SRC_ADDR_OFFSET] == 0, "Failed to parse IL Instruction"); // load_cst str = " eax =[-1]"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::LOAD_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_LOADCST_DST_REG] == X86_EAX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_LOADCST_SRC_ADDR_OFFSET] == -1, "Failed to parse IL Instruction"); str = "eax= [0xffffffff]"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::LOAD_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_LOADCST_DST_REG] == X86_EAX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_LOADCST_SRC_ADDR_OFFSET] == 0xffffffff, "Failed to parse IL Instruction"); // aload_cst str = " edi ^= [0]"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::ALOAD_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ALOADCST_DST_REG] == X86_EDI, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ALOADCST_OP] == (int)Op::XOR, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ALOADCST_SRC_ADDR_OFFSET] == 0, "Failed to parse IL Instruction"); // store str = " [eax - 2] = edx"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::STORE, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_STORE_DST_ADDR_REG] == X86_EAX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_STORE_DST_ADDR_OFFSET] == -2, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_STORE_SRC_REG] == X86_EDX, "Failed to parse IL Instruction"); str = " [ eax] = edx"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::STORE, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_STORE_DST_ADDR_REG] == X86_EAX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_STORE_DST_ADDR_OFFSET] == 0, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_STORE_SRC_REG] == X86_EDX, "Failed to parse IL Instruction"); // cst_store str = " [6789] = edx"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::CST_STORE, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTSTORE_DST_ADDR_OFFSET] == 6789, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTSTORE_SRC_REG] == X86_EDX, "Failed to parse IL Instruction"); str = " [-2]=eip"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::CST_STORE, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTSTORE_DST_ADDR_OFFSET] == -2, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTSTORE_SRC_REG] == X86_EIP, "Failed to parse IL Instruction"); // astore str = " [esp] |= edx"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::ASTORE, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ASTORE_DST_ADDR_REG] == X86_ESP, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ASTORE_DST_ADDR_OFFSET] == 0, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ASTORE_OP] == (int)Op::OR, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ASTORE_SRC_REG] == X86_EDX, "Failed to parse IL Instruction"); // cst_astore str = " [0x1800] %= ebx"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::CST_ASTORE, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTASTORE_DST_ADDR_OFFSET] == 0x1800, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTASTORE_SRC_REG] == X86_EBX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTASTORE_OP] == (int)Op::MOD, "Failed to parse IL Instruction"); // store_cst str = " [eax - 2] = 42"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::STORE_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_STORECST_DST_ADDR_REG] == X86_EAX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_STORECST_DST_ADDR_OFFSET] == -2, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_STORECST_SRC_CST] == 42, "Failed to parse IL Instruction"); str = " [ eax] = 1234"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::STORE_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_STORECST_DST_ADDR_REG] == X86_EAX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_STORECST_DST_ADDR_OFFSET] == 0, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_STORECST_SRC_CST] == 1234, "Failed to parse IL Instruction"); // cst_store_cst str = " [6789] = 0x42"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::CST_STORE_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTSTORECST_DST_ADDR_OFFSET] == 6789, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTSTORECST_SRC_CST] == 0x42, "Failed to parse IL Instruction"); str = " [-20]=12"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::CST_STORE_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTSTORECST_DST_ADDR_OFFSET] == -20, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTSTORECST_SRC_CST] == 12, "Failed to parse IL Instruction"); // astore_cst str = " [esp] |= 34"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::ASTORE_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ASTORECST_DST_ADDR_REG] == X86_ESP, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ASTORECST_DST_ADDR_OFFSET] == 0, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ASTORECST_OP] == (int)Op::OR, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_ASTORECST_SRC_CST] == 34, "Failed to parse IL Instruction"); // cst_astore_cst str = " [0x1800] %= 00"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::CST_ASTORE_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTASTORECST_DST_ADDR_OFFSET] == 0x1800, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTASTORECST_SRC_CST] == 0, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTASTORECST_OP] == (int)Op::MOD, "Failed to parse IL Instruction"); // function str = " 0x1000()"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::FUNCTION, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_FUNCTION_ADDR] == 0x1000, "Failed to parse IL Instruction"); str = " 1000( 22 )"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::FUNCTION, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_FUNCTION_ADDR] == 1000, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_FUNCTION_ARGS+0] == 22, "Failed to parse IL Instruction"); nb += _assert(instr.args_type[PARAM_FUNCTION_ARGS+0] == IL_FUNC_ARG_CST, "Failed to parse IL Instruction"); str = " 1000( eax )"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::FUNCTION, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_FUNCTION_ADDR] == 1000, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_FUNCTION_ARGS+0] == X86_EAX, "Failed to parse IL Instruction"); nb += _assert(instr.args_type[PARAM_FUNCTION_ARGS+0] == IL_FUNC_ARG_REG, "Failed to parse IL Instruction"); str = " 1000( 22, ebx )"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::FUNCTION, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_FUNCTION_ADDR] == 1000, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_FUNCTION_ARGS+0] == 22, "Failed to parse IL Instruction"); nb += _assert(instr.args_type[PARAM_FUNCTION_ARGS+0] == IL_FUNC_ARG_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_FUNCTION_ARGS+1] == X86_EBX, "Failed to parse IL Instruction"); nb += _assert(instr.args_type[PARAM_FUNCTION_ARGS+1] == IL_FUNC_ARG_REG, "Failed to parse IL Instruction"); str = " 1000( eax , 34, ebx )"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::FUNCTION, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_FUNCTION_ADDR] == 1000, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_FUNCTION_ARGS+0] == X86_EAX, "Failed to parse IL Instruction"); nb += _assert(instr.args_type[PARAM_FUNCTION_ARGS+0] == IL_FUNC_ARG_REG, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_FUNCTION_ARGS+1] == 34, "Failed to parse IL Instruction"); nb += _assert(instr.args_type[PARAM_FUNCTION_ARGS+1] == IL_FUNC_ARG_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_FUNCTION_ARGS+2] == X86_EBX, "Failed to parse IL Instruction"); nb += _assert(instr.args_type[PARAM_FUNCTION_ARGS+2] == IL_FUNC_ARG_REG, "Failed to parse IL Instruction"); // syscall str = " sys_read()"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::SYSCALL, "Failed to parse IL Instruction"); nb += _assert(instr.syscall_name == "read", "Failed to parse IL Instruction"); str = " sys_truc(42)"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::SYSCALL, "Failed to parse IL Instruction"); nb += _assert(instr.syscall_name == "truc", "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_SYSCALL_ARGS+0] == 42, "Failed to parse IL Instruction"); nb += _assert(instr.args_type[PARAM_SYSCALL_ARGS+0] == IL_FUNC_ARG_CST, "Failed to parse IL Instruction"); str = " sys_truc(42, esp, 1)"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::SYSCALL, "Failed to parse IL Instruction"); nb += _assert(instr.syscall_name == "truc", "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_SYSCALL_ARGS+0] == 42, "Failed to parse IL Instruction"); nb += _assert(instr.args_type[PARAM_SYSCALL_ARGS+0] == IL_FUNC_ARG_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_SYSCALL_ARGS+1] == X86_ESP, "Failed to parse IL Instruction"); nb += _assert(instr.args_type[PARAM_SYSCALL_ARGS+1] == IL_FUNC_ARG_REG, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_SYSCALL_ARGS+2] == 1, "Failed to parse IL Instruction"); nb += _assert(instr.args_type[PARAM_SYSCALL_ARGS+2] == IL_FUNC_ARG_CST, "Failed to parse IL Instruction"); str = " sys_11()"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::SYSCALL, "Failed to parse IL Instruction"); nb += _assert(instr.syscall_num == 11, "Failed to parse IL Instruction"); str = " sys_0x42(eax)"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::SYSCALL, "Failed to parse IL Instruction"); nb += _assert(instr.syscall_num == 0x42, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_SYSCALL_ARGS+0] == X86_EAX, "Failed to parse IL Instruction"); nb += _assert(instr.args_type[PARAM_SYSCALL_ARGS+0] == IL_FUNC_ARG_REG, "Failed to parse IL Instruction"); // cst_store_string str = " [6789] = 'lala'"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::CST_STORE_STRING, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTSTORE_STRING_ADDR_OFFSET] == 6789, "Failed to parse IL Instruction"); nb += _assert(instr.str == "lala", "Failed to parse IL Instruction"); str = " [6789] = 'lal\\\\a'"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::CST_STORE_STRING, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTSTORE_STRING_ADDR_OFFSET] == 6789, "Failed to parse IL Instruction"); nb += _assert(instr.str == "lal\\a", "Failed to parse IL Instruction"); str = " [6789] = 'lal\\'a'"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::CST_STORE_STRING, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTSTORE_STRING_ADDR_OFFSET] == 6789, "Failed to parse IL Instruction"); nb += _assert(instr.str == "lal'a", "Failed to parse IL Instruction"); str = " [6789] = 'lal\\x41\\x42a'"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::CST_STORE_STRING, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTSTORE_STRING_ADDR_OFFSET] == 6789, "Failed to parse IL Instruction"); nb += _assert(instr.str == "lalABa", "Failed to parse IL Instruction"); str = " [0x1234] = 'lalatotokikoo\\x00'"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::CST_STORE_STRING, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_CSTSTORE_STRING_ADDR_OFFSET] == 0x1234, "Failed to parse IL Instruction"); nb += _assert(instr.str == string("lalatotokikoo\x00", 14), "Failed to parse IL Instruction"); str = " syscall "; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::SINGLE_SYSCALL, "Failed to parse IL Instruction"); return nb; } unsigned int il_parser64(){ unsigned int nb = 0; ArchX64 arch; // parse_il_cst should work with both unsigned and signed constants string str = " 1000( 0xffffffffffffffff, -1 )"; ILInstruction instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::FUNCTION, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_FUNCTION_ADDR] == 1000, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_FUNCTION_ARGS+0] == (cst_t)0xffffffffffffffff, "Failed to parse IL Instruction"); nb += _assert(instr.args_type[PARAM_FUNCTION_ARGS+0] == IL_FUNC_ARG_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_FUNCTION_ARGS+1] == -1, "Failed to parse IL Instruction"); nb += _assert(instr.args_type[PARAM_FUNCTION_ARGS+1] == IL_FUNC_ARG_CST, "Failed to parse IL Instruction"); str = "rax= [0x7000000000000000]"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::LOAD_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_LOADCST_DST_REG] == X64_RAX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_LOADCST_SRC_ADDR_OFFSET] == (cst_t)0x7000000000000000, "Failed to parse IL Instruction"); // 0xffffffffffffffff == -1 str = "rdx= 0xffffffffffffffff"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::MOV_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_MOVCST_DST_REG] == X64_RDX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_MOVCST_SRC_CST] == -1, "Failed to parse IL Instruction"); // Edge case: -0xffffffffffffffff == -(0xffffffffffffffff) == -(-1) == 1 str = "rcx= -0xffffffffffffffff"; instr = ILInstruction(arch, str); nb += _assert(instr.type == ILInstructionType::MOV_CST, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_MOVCST_DST_REG] == X64_RCX, "Failed to parse IL Instruction"); nb += _assert(instr.args[PARAM_MOVCST_SRC_CST] == 1, "Failed to parse IL Instruction"); return nb; } } } using namespace test::il; // All unit tests void test_il(){ unsigned int total = 0; string green = "\033[1;32m"; string def = "\033[0m"; string bold = "\033[1m"; // Start testing cout << bold << "[" << green << "+" << def << bold << "]" << def << std::left << std::setw(34) << " Testing il module... " << std::flush; total += il_parser(); total += il_parser64(); // Return res cout << "\t" << total << "/" << total << green << "\t\tOK" << def << endl; } ================================================ FILE: tests/test_ir.cpp ================================================ #include "ir.hpp" #include "exception.hpp" #include #include #include #include #include using std::cout; using std::endl; using std::string; namespace test{ namespace ir{ unsigned int _assert(bool val, const string& msg){ if( !val){ cout << "\nFail: " << msg << endl << std::flush; throw test_exception(); } return 1; } unsigned int ir_context(){ IRContext ctx = IRContext(4); Expr e1 = exprcst(32, 56), e2 = exprvar(64, "var1"), e3 = exprvar(16, "var2"); unsigned int nb = 0; ctx.set(0, e1); ctx.set(1, e1); ctx.set(2, e2); ctx.set(3, e3); nb += _assert(ctx.get(0)->eq(e1), "IRContext failed to update then get variable"); nb += _assert(ctx.get(1)->eq(e1), "IRContext failed to update then get variable"); nb += _assert(ctx.get(2)->eq(e2), "IRContext failed to update then get variable"); nb += _assert(ctx.get(3)->eq(e3), "IRContext failed to update then get variable"); return nb; } } } using namespace test::ir; // All unit tests void test_ir(){ unsigned int total = 0; string green = "\033[1;32m"; string def = "\033[0m"; string bold = "\033[1m"; // Start testing cout << bold << "[" << green << "+" << def << bold << "]" << def << std::left << std::setw(34) << " Testing ir module... " << std::flush; total += ir_context(); // Return res cout << "\t" << total << "/" << total << green << "\t\tOK" << def << endl; } ================================================ FILE: tests/test_simplification.cpp ================================================ #include "expression.hpp" #include "simplification.hpp" #include "exception.hpp" #include #include #include #include #include using std::cout; using std::endl; using std::string; namespace test{ namespace simplification{ unsigned int _assert_simplify(Expr e1, Expr e2, ExprSimplifier& simp){ Expr tmp1 = simp.simplify(e1); Expr tmp2 = simp.simplify(e2); if( tmp1->neq(tmp2) ){ cout << "\nFail: _assert_simplify: " << e1 << " => " << e2 << endl << "Note: instead simplified into " << tmp1 << " => " << tmp2 << endl << std::flush; throw test_exception(); } return 1; } unsigned int _assert(bool val, const string& msg){ if( !val){ cout << "\nFail: " << msg << endl << std::flush; throw test_exception(); } return 1; } unsigned int basic(ExprSimplifier& s){ Expr e1 = exprvar(32,"varA"); return 0; } unsigned int const_folding(ExprSimplifier& s){ unsigned int nb = 0; Expr e1 = exprcst(32,-1), e2 =exprcst(32, 1048567); nb += _assert_simplify(exprcst(16,2)+exprcst(16,4), exprcst(16,6), s); nb += _assert_simplify(exprcst(4,3)*exprcst(4,7), exprcst(4, 5), s); nb += _assert_simplify(exprcst(8,0xc3)/exprcst(8,0x40), exprcst(8, 3), s); nb += _assert_simplify(exprcst(16, 321)/exprcst(16, 40), exprcst(16, 321U/40U), s); nb += _assert_simplify(sdiv(exprcst(16, 567),exprcst(16, 56)), exprcst(16, 567/56), s); nb += _assert_simplify(exprcst(16, 0x2)&exprcst(16, 0x1234), exprcst(16, 0x2&0x1234), s); nb += _assert_simplify(exprcst(16, 0x2)|exprcst(16, 0x1234), exprcst(16, 0x2|0x1234), s); nb += _assert_simplify(exprcst(16, 0x2)^exprcst(16, 0x1234), exprcst(16, 0x2^0x1234), s); nb += _assert_simplify(shl(exprcst(16, 1),exprcst(16, 4)), exprcst(16, 16), s); nb += _assert_simplify(shr(exprcst(16, 16),exprcst(16, 4)), exprcst(16, 1), s); nb += _assert_simplify(shl(exprcst(16, 1), exprcst(16, 16)), exprcst(16,0), s); nb += _assert_simplify(extract(exprcst(8, 20), 4, 2), exprcst(3, 5), s); nb += _assert_simplify(concat(exprcst(8, 1), exprcst(4, -1)), exprcst(12, 0x1f), s); nb += _assert_simplify(-exprcst(7,3), exprcst(7, -3), s); nb += _assert_simplify(~exprcst(7,3), exprcst(7, ~3), s); nb += _assert_simplify(e2+e1-e1, e2, s); nb += _assert_simplify(bisz(32, e1, 1), exprcst(32, 0), s); nb += _assert_simplify(bisz(23, exprcst(32,0), 1), exprcst(23, 1), s); return nb; } unsigned int neutral_elems(ExprSimplifier& s){ unsigned int nb = 0; nb += _assert_simplify(exprvar(32,"var1")+exprcst(32, 0), exprvar(32, "var1"), s); nb += _assert_simplify(exprvar(32,"var1")*exprcst(32, 1), exprvar(32, "var1"), s); nb += _assert_simplify(exprvar(32,"var1")/exprcst(32, 1), exprvar(32, "var1"), s); nb += _assert_simplify(sdiv(exprvar(32,"var1"),exprcst(32, 1)), exprvar(32, "var1"), s); nb += _assert_simplify(exprvar(7,"var1")&exprcst(7, 0b1111111), exprvar(7, "var1"), s); nb += _assert_simplify(exprvar(6,"var1")|exprcst(6, 0), exprvar(6, "var1"), s); nb += _assert_simplify(exprvar(32,"var1")^exprcst(32, 0), exprvar(32, "var1"), s); nb += _assert_simplify(extract(exprvar(32,"var1"), 31, 0), exprvar(32, "var1"), s); return nb; } unsigned int absorbing_elems(ExprSimplifier& s){ unsigned int nb = 0; nb += _assert_simplify(exprvar(33,"var1")*exprcst(33,0), exprcst(33,0), s); nb += _assert_simplify(exprvar(6, "var1")|exprcst(6,0b111111), exprcst(6,0b111111), s); nb += _assert_simplify(exprvar(5,"var1")&exprcst(5,0), exprcst(5,0), s); nb += _assert_simplify(shl(exprvar(32,"var1"),exprcst(32, 50)), exprcst(32,0), s); nb += _assert_simplify(shr(exprvar(32,"var1"),exprcst(32, 32)), exprcst(32,0), s); return nb; } unsigned int arithmetic_properties(ExprSimplifier& s){ unsigned int nb = 0; Expr e1 = exprvar(64, "var1"), e2 = exprvar(64, "var2"), e3 = exprvar(64, "var3"), e4 = e1/e2, c1 = exprcst(64, 1); nb += _assert_simplify( e1+(e1*e2), (e2+c1)*e1, s); nb += _assert_simplify( (e2*e1)+e1, (e2+c1)*e1, s); nb += _assert_simplify( (e1*e2)-e1, (e2-c1)*e1, s); nb += _assert_simplify( (e2*e1)-e1, (e2-c1)*e1, s); nb += _assert_simplify( (e1*e3)+(e2*e3), (e1+e2)*e3 , s); nb += _assert_simplify( (e3*e1)+(e2*e3), (e1+e2)*e3 , s); nb += _assert_simplify( (e1*e3)+(e3*e2), (e1+e2)*e3 , s); nb += _assert_simplify( (e3*e1)+(e3*e2), (e1+e2)*e3 , s); nb += _assert_simplify( (e4+(e4*e3)), (e3+c1)*e4, s); nb += _assert_simplify( (e4+(e3*e4)), (e3+c1)*e4, s); nb += _assert_simplify( (-e4+(e4*e3)), (e3-c1)*e4, s); nb += _assert_simplify( (-e4+(e3*e4)), (e3-c1)*e4, s); nb += _assert_simplify( (e4+e4) , e4*exprcst(64, 2), s); nb += _assert_simplify( e4-e4, exprcst(64,0), s); nb += _assert_simplify( -e3+e3, exprcst(64,0), s); return nb; } unsigned int involution(ExprSimplifier& s){ unsigned int nb = 0; nb += _assert_simplify( -(-exprvar(64, "var1")), exprvar(64, "var1"), s); nb += _assert_simplify( ~~exprvar(64, "var1"), exprvar(64, "var1"), s); return nb; } unsigned int extract_patterns(ExprSimplifier& s){ unsigned int nb = 0; Expr e1 = exprvar(32,"var1"), e2 = exprvar(14, "var2"); Expr e = concat(e1, e2); nb += _assert_simplify(extract(e, 45, 40), extract(e1, 31, 26), s); nb += _assert_simplify(extract(e, 8, 1), extract(e2, 8, 1), s); nb += _assert_simplify(extract(extract(e1, 28,10),8,1), extract(e1, 18,11), s); nb += _assert_simplify(extract(extract(exprcst(64,0xffffff), 31,0),10,10), extract(exprcst(64,0xffffff), 10,10), s); return nb; } unsigned int basic_transform(ExprSimplifier& s){ unsigned int nb = 0; Expr e1 = exprvar(56, "var1"); Expr e2 = exprmem(56, e1); nb += _assert_simplify(shl(e1, exprcst(56, 3)), e1*exprcst(56, 8), s); nb += _assert_simplify(shr(e1, exprcst(56, 4)), e1/exprcst(56, 16), s); nb += _assert_simplify(exprcst(56, -1)*e1, -e1, s); nb += _assert_simplify((~e1)+exprcst(56,1), -e1, s); nb += _assert_simplify((~(-e1))+exprcst(56,1), e1, s); nb += _assert_simplify(e1*(-e2), -(e2*e1), s); nb += _assert_simplify((-e1)*e2, -(e2*e1), s); return nb; } unsigned int logical_properties(ExprSimplifier& s){ unsigned int nb = 0; Expr e = exprvar(64, "var1"); nb += _assert_simplify(e&e, e, s); nb += _assert_simplify(e|e, e, s); nb += _assert_simplify(e&(~e), exprcst(64,0), s); nb += _assert_simplify((~e)&e, exprcst(64,0), s); nb += _assert_simplify((~e)^e, exprcst(64,-1), s); nb += _assert_simplify(e^(~e), exprcst(64,-1), s); nb += _assert_simplify((~e)|e, exprcst(64,-1), s); nb += _assert_simplify(e|(~e), exprcst(64,-1), s); nb += _assert_simplify(e^e, exprcst(64,0), s); return nb; } unsigned int concat_patterns(ExprSimplifier& s){ unsigned int nb = 0; Expr e = exprvar(64, "var1"); Expr v1 = exprvar(8, "a"), c1 = exprcst(24, 0x100c3); Expr e1 = concat(v1, c1); nb += _assert_simplify(concat(extract(e, 63,10), extract(e,9,0)), e, s); nb += _assert_simplify(extract( concat(extract(e1, 31, 8), extract(e1, 7, 0)>>6), 7, 0) - 3, (extract(e1, 7, 0)>>6)-3, s); e1 = exprcst(32, 0x00ffffff) & concat(v1, c1); nb += _assert_simplify(e1, concat(exprcst(8, 0), c1), s); e1 = exprcst(32, 0xff000000) & concat(v1, c1); nb += _assert_simplify(e1, concat(v1, exprcst(24, 0)), s); e1 = exprcst(64, 0xffffffff) & concat(exprvar(32, "blabla"), exprcst(32, 0)); nb += _assert_simplify(e1, exprcst(64, 0), s); e1 = exprcst(64, 0xffffffff00000000) & concat(exprcst(32, 0), exprvar(32, "blu")); nb += _assert_simplify(e1, exprcst(64, 0), s); return nb; } unsigned int advanced(ExprSimplifier& s){ unsigned int nb = 0; Expr e1 = exprvar(32,"varA"), e2 = exprvar(32,"varB"), e3 = exprcst(32, -1), e4 = exprcst(32, 0xffff7), e5 = e3+e4, e6 = e4/e1, e7 = shr(e5,exprcst(32, 1)), e8 = exprmem(32, e3), e9 = concat(extract(e1, 31, 16), extract(e4, 15, 0)); nb += _assert_simplify(((e1-e2)*e6)^e8, e8^((e1-e2+e2-e2)*(e6&e6)), s); nb += _assert_simplify(e1+e2+e3-e1+e4-e2-e3, e4, s); nb += _assert_simplify(e3*e4, exprcst(32, 0xfffffffffff00009), s); nb += _assert_simplify(e4*e4, exprcst(32, 0xfffee00051), s); nb += _assert_simplify(exprcst(32, 0xfffee00051)*e3, exprcst(32, 0xffffff00011fffaf), s); nb += _assert_simplify(e4*(e3-e3+e4)*e3, e4*e3*e4, s); nb += _assert_simplify(e3*e4*(e1+e2+e3-e1+e4-e2-e3), e4*e4*e3, s); nb += _assert_simplify(e2/(e1+e1-e1), e2/e1, s); nb += _assert_simplify(e8, e8+e5-e5, s); nb += _assert_simplify((e6/e1/e8), (e6/(e8+e5-e5)/e1), s); nb += _assert_simplify((e6/e7/e8), (e6/(e8+e5-e5)/e7), s); nb += _assert_simplify(e9|e9, e9, s); nb += _assert_simplify((e2&(~e1))&e1, exprcst(32,0), s); //nb += _assert_simplify(extract(e8^(e9^~e8), 31, 0), e8&(-e6+(e9|e9)+e6) , s); /*nb += _assert_simplify(, , s); nb += _assert_simplify(, , s); nb += _assert_simplify(, , s); nb += _assert_simplify(, , s);*/ //nb += _assert_simplify(, , s); return nb; } } } using namespace test::simplification; // All unit tests void test_simplification(){ ExprSimplifier simp = ExprSimplifier(); simp.add(es_constant_folding); simp.add(es_neutral_elements); simp.add(es_absorbing_elements); simp.add(es_arithmetic_properties); simp.add(es_involution); simp.add(es_extract_patterns); simp.add(es_basic_transform); simp.add(es_logical_properties); simp.add(es_concat_patterns); simp.add(es_arithmetic_factorize); //simp.add(es_generic_distribute); simp.add(es_generic_factorize); simp.add(es_deep_associative); unsigned int total = 0; string green = "\033[1;32m"; string def = "\033[0m"; string bold = "\033[1m"; // Start testing cout << bold << "[" << green << "+" << def << bold << "]" << def << " Testing simplification module... " << std::flush; for( int i = 0; i < 1; i++){ total += basic(simp); total += const_folding(simp); total += neutral_elems(simp); total += absorbing_elems(simp); total += arithmetic_properties(simp); total += involution(simp); total += extract_patterns(simp); total += basic_transform(simp); total += logical_properties(simp); total += concat_patterns(simp); total += advanced(simp); } // Return res cout << "\t" << total << "/" << total << green << "\t\tOK" << def << endl; } ================================================ FILE: tests/test_strategy.cpp ================================================ #include "strategy.hpp" #include "compiler.hpp" #include "il.hpp" #include "exception.hpp" #include "utils.hpp" #include #include #include #include #include using std::cout; using std::endl; using std::string; namespace test{ namespace strategy{ unsigned int _assert(bool val, const string& msg){ if( !val){ cout << "\nFail: " << msg << endl << std::flush; throw test_exception(); } return 1; } unsigned int _assert_ropchain(ROPChain* ropchain, const string& msg){ if( ropchain == nullptr){ cout << "\nFail: " << msg << endl << std::flush; throw test_exception(); } delete ropchain; return 1; } unsigned int basic(){ unsigned int nb = 0; StrategyGraph sgraph; node_t n1 = sgraph.new_node(GadgetType::MOV_REG); node_t n2 = sgraph.new_node(GadgetType::MOV_REG); Node& node1 = sgraph.nodes[n1]; Node& node2 = sgraph.nodes[n2]; node1.params[PARAM_MOVREG_SRC_REG].make_reg(X86_EAX); node1.params[PARAM_MOVREG_DST_REG].make_reg(n2, PARAM_MOVREG_SRC_REG); node2.params[PARAM_MOVREG_SRC_REG].make_reg(-1, false); node2.params[PARAM_MOVREG_DST_REG].make_reg(X86_ECX); sgraph.add_strategy_edge(n1, n2); sgraph.add_param_edge(n1, n2); //std::cout << sgraph; sgraph.rule_generic_transitivity(n1); sgraph.compute_dfs_params(); sgraph.compute_dfs_strategy(); //std::cout << sgraph; return nb; } unsigned int rules(){ unsigned int nb = 0; Arch* arch = new ArchX86(); GadgetDB db; ROPChain* ropchain; vector raw; raw.push_back(RawGadget(string("\x89\xf9\xbb\x01\x00\x00\x00\xc3", 8), 1)); // mov ecx, edi; mov ebx, 1; ret raw.push_back(RawGadget(string("\x89\xC8\xC3", 3), 2)); // mov eax, ecx; ret raw.push_back(RawGadget(string("\x89\xC3\xC3", 3), 3)); // mov ebx, eax; ret raw.push_back(RawGadget(string("\x89\xCB\xC3", 3), 4)); // mov ebx, ecx; ret raw.push_back(RawGadget(string("\xbb\x04\x00\x00\x00\xc3", 6), 5)); // mov ebx, 4; ret raw.push_back(RawGadget(string("\xb8\x05\x00\x00\x00\xc3", 6), 6)); // mov eax, 5; ret raw.push_back(RawGadget(string("\x89\xc2\xc3", 3), 7)); // mov edx, eax; ret raw.push_back(RawGadget(string("\x5f\x5e\x59\xc3", 4), 8)); // pop edi; pop esi; pop ecx; ret raw.push_back(RawGadget(string("\x89\xF5\xFF\xE0", 4), 9)); // mov ebp, esi; jmp eax raw.push_back(RawGadget(string("\xB8\x09\x00\x00\x00\xC3", 6), 10)); // mov eax, 9; ret raw.push_back(RawGadget(string("\x8B\x4F\x04\xFF\xE0", 5), 11)); // mov ecx, [edi+4]; jmp eax db.analyse_raw_gadgets(raw, arch); // Test register transitivity StrategyGraph sgraph; node_t n1 = sgraph.new_node(GadgetType::MOV_REG); node_t n2 = sgraph.new_node(GadgetType::MOV_REG); Node& node1 = sgraph.nodes[n1]; Node& node2 = sgraph.nodes[n2]; node1.params[PARAM_MOVREG_SRC_REG].make_reg(X86_EDI); node1.params[PARAM_MOVREG_DST_REG].make_reg(n2, PARAM_MOVREG_SRC_REG); node2.params[PARAM_MOVREG_SRC_REG].make_reg(0, false); node2.params[PARAM_MOVREG_DST_REG].make_reg(X86_EBX); sgraph.add_strategy_edge(n1, n2); sgraph.add_param_edge(n1, n2); // Apply strat sgraph.rule_generic_transitivity(n2); sgraph.select_gadgets(db); ropchain = sgraph.get_ropchain(arch); nb += _assert_ropchain(ropchain, "Basic application of strategy rule failed"); // Test constant param resolving StrategyGraph graph2; n1 = graph2.new_node(GadgetType::MOV_CST); n2 = graph2.new_node(GadgetType::MOV_CST); Node& node1a = graph2.nodes[n1]; Node& node2a = graph2.nodes[n2]; node1a.params[PARAM_MOVCST_SRC_CST].make_cst(n2, PARAM_MOVCST_SRC_CST, exprvar(32, "cst1")+1, "cst2"); node1a.params[PARAM_MOVCST_DST_REG].make_reg(X86_EAX); node2a.params[PARAM_MOVCST_SRC_CST].make_cst(0, "cst1", false); // free node2a.params[PARAM_MOVCST_DST_REG].make_reg(-1, false); // free graph2.add_strategy_edge(n1, n2); graph2.add_param_edge(n1, n2); graph2.select_gadgets(db); ropchain = graph2.get_ropchain(arch); nb += _assert_ropchain(ropchain, "Basic application of strategy rule failed"); // Test MovCst transitivity StrategyGraph graph3; n1 = graph3.new_node(GadgetType::MOV_CST); Node& node1b = graph3.nodes[n1]; node1b.params[PARAM_MOVCST_SRC_CST].make_cst(5, "cst_1"); node1b.params[PARAM_MOVCST_DST_REG].make_reg(X86_EDX); // Apply strat graph3.rule_generic_transitivity(n1); graph3.select_gadgets(db); ropchain = graph3.get_ropchain(arch); nb += _assert_ropchain(ropchain, "Basic application of strategy rule failed"); // Test MovCst pop StrategyGraph graph4; n1 = graph4.new_node(GadgetType::MOV_CST); Node& node1c = graph4.nodes[n1]; node1c.params[PARAM_MOVCST_SRC_CST].make_cst(0x1234, "cst_2"); node1c.params[PARAM_MOVCST_DST_REG].make_reg(X86_ESI); // Apply strat graph4.rule_mov_cst_pop(n1, arch); graph4.select_gadgets(db, nullptr, arch); ropchain = graph4.get_ropchain(arch); nb += _assert_ropchain(ropchain, "Basic application of strategy rule failed"); // Test generic adjust jmp StrategyGraph graph5; n1 = graph5.new_node(GadgetType::MOV_REG); Node& node1d = graph5.nodes[n1]; node1d.params[PARAM_MOVREG_SRC_REG].make_reg(X86_ESI); node1d.params[PARAM_MOVREG_DST_REG].make_reg(X86_EBP); node1d.branch_type = BranchType::RET; // So the rule applies // Apply strat graph5.rule_generic_adjust_jmp(n1, arch); graph5.select_gadgets(db, nullptr, arch); ropchain = graph5.get_ropchain(arch); nb += _assert_ropchain(ropchain, "Basic application of strategy rule failed"); StrategyGraph graph6; n1 = graph6.new_node(GadgetType::LOAD); Node& node1e = graph6.nodes[n1]; node1e.params[PARAM_LOAD_SRC_ADDR_REG].make_reg(X86_EDI); node1e.params[PARAM_LOAD_SRC_ADDR_OFFSET].make_cst(4, "cstlalal"); node1e.params[PARAM_LOAD_DST_REG].make_reg(X86_ECX); node1e.branch_type = BranchType::RET; // So the rule applies // Apply strat graph6.rule_generic_adjust_jmp(n1, arch); graph6.select_gadgets(db, nullptr, arch); ropchain = graph6.get_ropchain(arch); nb += _assert_ropchain(ropchain, "Basic application of strategy rule failed"); delete arch; return nb; } unsigned int test_generic_adjust_jmp(){ unsigned int nb = 0; Arch* arch = new ArchX86(); GadgetDB db; ROPChain* ropchain; vector raw; raw.push_back(RawGadget(string("\x89\xC8\xC3", 3), 1)); // mov eax, ecx; ret raw.push_back(RawGadget(string("\xc3", 1), 2)); // ret raw.push_back(RawGadget(string("\x89\xF1\xFF\xE0", 4), 3)); // mov ecx, esi; jmp eax raw.push_back(RawGadget(string("\x5A\x59\xC3", 3), 4)); // pop edx; pop ecx; ret db.analyse_raw_gadgets(raw, arch); // Test on more advanced example (eax = esi) StrategyGraph sgraph; node_t n1 = sgraph.new_node(GadgetType::MOV_REG); Node& node1 = sgraph.nodes[n1]; node1.params[PARAM_MOVREG_DST_REG].make_reg(X86_EAX); node1.params[PARAM_MOVREG_SRC_REG].make_reg(X86_ESI); // Apply strat sgraph.rule_generic_transitivity(n1); sgraph.rule_generic_adjust_jmp(1, arch); sgraph.rule_generic_transitivity(3); sgraph.rule_mov_cst_pop(5, arch); sgraph.select_gadgets(db, nullptr, arch); ropchain = sgraph.get_ropchain(arch); nb += _assert_ropchain(ropchain, "Basic application of strategy rule failed"); delete arch; return nb; } unsigned int test_adjust_load(){ unsigned int nb = 0; Arch* arch = new ArchX86(); GadgetDB db; ROPChain* ropchain; vector raw; raw.push_back(RawGadget(string("\x8B\x41\x08\xC3", 4), 1)); // mov eax, [ecx + 8]; ret raw.push_back(RawGadget(string("\x8D\x4B\x08\xC3", 4), 2)); // lea ecx, [ebx + 8]; ret raw.push_back(RawGadget(string("\x23\x56\xF8\xC3", 4), 3)); // and edx, [esi - 8]; ret raw.push_back(RawGadget(string("\x8D\x77\x10\xC3", 4), 4)); // lea esi, [edi + 16]; ret db.analyse_raw_gadgets(raw, arch); // Test adjust load on LOAD type StrategyGraph sgraph; node_t n1 = sgraph.new_node(GadgetType::LOAD); Node& node1 = sgraph.nodes[n1]; node1.params[PARAM_LOAD_DST_REG].make_reg(X86_EAX); node1.params[PARAM_LOAD_SRC_ADDR_REG].make_reg(X86_EBX); node1.params[PARAM_LOAD_SRC_ADDR_OFFSET].make_cst(0x10, "cst_0"); // Apply strat sgraph.rule_adjust_load(n1, arch); sgraph.select_gadgets(db, nullptr, arch); ropchain = sgraph.get_ropchain(arch); nb += _assert_ropchain(ropchain, "Basic application of strategy rule failed"); // Test adjust load on ALOAD type StrategyGraph sgraph2; node_t n2 = sgraph2.new_node(GadgetType::ALOAD); Node& node2 = sgraph2.nodes[n2]; node2.params[PARAM_ALOAD_DST_REG].make_reg(X86_EDX); node2.params[PARAM_ALOAD_OP].make_op(Op::AND); node2.params[PARAM_ALOAD_SRC_ADDR_REG].make_reg(X86_EDI); node2.params[PARAM_ALOAD_SRC_ADDR_OFFSET].make_cst(8, "cst_0"); // Apply strat sgraph2.rule_adjust_load(n2, arch); sgraph2.select_gadgets(db, nullptr, arch); ropchain = sgraph2.get_ropchain(arch); nb += _assert_ropchain(ropchain, "Basic application of strategy rule failed"); delete arch; return nb; } unsigned int test_generic_src_transitivity(){ unsigned int nb = 0; Arch* arch = new ArchX86(); GadgetDB db; ROPChain* ropchain; vector raw; raw.push_back(RawGadget(string("\x89\x51\xEC\xC3", 4), 1)); // mov [ecx - 20], edx; ret raw.push_back(RawGadget(string("\x89\xC2\xC3", 3), 2)); // mov edx, eax; ret raw.push_back(RawGadget(string("\x31\x51\xEC\xC3", 4), 3)); // xor [ecx - 20], edx; ret db.analyse_raw_gadgets(raw, arch); // Test src transitivity on STORE StrategyGraph sgraph; node_t n1 = sgraph.new_node(GadgetType::STORE); Node& node1 = sgraph.nodes[n1]; node1.params[PARAM_STORE_DST_ADDR_REG].make_reg(X86_ECX); node1.params[PARAM_STORE_DST_ADDR_OFFSET].make_cst(-20, "cst_0"); node1.params[PARAM_STORE_SRC_REG].make_reg(X86_EAX); // Apply strat sgraph.rule_generic_src_transitivity(n1); sgraph.select_gadgets(db, nullptr, arch); ropchain = sgraph.get_ropchain(arch); nb += _assert_ropchain(ropchain, "Basic application of strategy rule failed"); // Test src transitivity on ASTORE StrategyGraph sgraph2; node_t n2 = sgraph2.new_node(GadgetType::ASTORE); Node& node2 = sgraph2.nodes[n2]; node2.params[PARAM_ASTORE_DST_ADDR_REG].make_reg(X86_ECX); node2.params[PARAM_ASTORE_DST_ADDR_OFFSET].make_cst(-20, "cst_0"); node2.params[PARAM_ASTORE_SRC_REG].make_reg(X86_EAX); node2.params[PARAM_ASTORE_OP].make_op(Op::XOR); // Apply strat sgraph2.rule_generic_src_transitivity(n2); sgraph2.select_gadgets(db, nullptr, arch); ropchain = sgraph2.get_ropchain(arch); nb += _assert_ropchain(ropchain, "Basic application of strategy rule failed"); return nb; } unsigned int test_adjust_store(){ unsigned int nb = 0; Arch* arch = new ArchX86(); GadgetDB db; ROPChain* ropchain; vector raw; raw.push_back(RawGadget(string("\x89\x41\x08\xC3", 4), 1)); // mov [ecx + 8], eax; ret raw.push_back(RawGadget(string("\x8D\x4B\x08\xC3", 4), 2)); // lea ecx, [ebx + 8]; ret raw.push_back(RawGadget(string("\x21\x56\xF8\xC3", 4), 3)); // and [esi - 8], edx; ret raw.push_back(RawGadget(string("\x8D\x77\x10\xC3", 4), 4)); // lea esi, [edi + 16]; ret raw.push_back(RawGadget(string("\x89\xC8\xC3", 3), 5)); // mov eax, ecx; ret raw.push_back(RawGadget(string("\x89\xC3\xC3", 3), 6)); // mov ebx, eax; ret raw.push_back(RawGadget(string("\x89\x43\x08\xC3", 4), 7)); // mov [ebx + 8], eax; ret db.analyse_raw_gadgets(raw, arch); // Test adjust store on STORE type StrategyGraph sgraph; node_t n1 = sgraph.new_node(GadgetType::STORE); Node& node1 = sgraph.nodes[n1]; node1.params[PARAM_STORE_DST_ADDR_REG].make_reg(X86_EBX); node1.params[PARAM_STORE_DST_ADDR_OFFSET].make_cst(16, "cst_0"); node1.params[PARAM_STORE_SRC_REG].make_reg(X86_EAX); // Apply strat sgraph.rule_adjust_store(n1, arch); sgraph.select_gadgets(db, nullptr, arch); ropchain = sgraph.get_ropchain(arch); nb += _assert_ropchain(ropchain, "Basic application of strategy rule failed"); // Test adjust store on ASTORE type StrategyGraph sgraph2; node_t n2 = sgraph2.new_node(GadgetType::ASTORE); Node& node2 = sgraph2.nodes[n2]; node2.params[PARAM_ASTORE_DST_ADDR_REG].make_reg(X86_EDI); node2.params[PARAM_ASTORE_DST_ADDR_OFFSET].make_cst(8, "cst_0"); node2.params[PARAM_ASTORE_SRC_REG].make_reg(X86_EDX); node2.params[PARAM_ASTORE_OP].make_op(Op::AND); // Apply strat sgraph2.rule_adjust_store(n2, arch); sgraph2.select_gadgets(db, nullptr, arch); ropchain = sgraph2.get_ropchain(arch); nb += _assert_ropchain(ropchain, "Basic application of strategy rule failed"); // ANother adjust store on STORE mixed with src_transitivity StrategyGraph sgraph3; node_t n3 = sgraph3.new_node(GadgetType::STORE); Node& node3 = sgraph3.nodes[n3]; node3.params[PARAM_STORE_DST_ADDR_REG].make_reg(X86_EAX); node3.params[PARAM_STORE_DST_ADDR_OFFSET].make_cst(8, "cst_0"); node3.params[PARAM_STORE_SRC_REG].make_reg(X86_ECX); // Apply strat sgraph3.rule_adjust_store(n3, arch); sgraph3.rule_generic_src_transitivity(2); sgraph3.select_gadgets(db, nullptr, arch); ropchain = sgraph3.get_ropchain(arch); nb += _assert_ropchain(ropchain, "Basic application of strategy rules failed"); delete arch; return nb; } unsigned int test_cst_pop(){ unsigned int nb = 0; /* Arch* arch = new ArchX86(); GadgetDB db; ROPChain* ropchain; vector raw; raw.push_back(RawGadget(string("\x59\x58\x5B\xFF\xE0", 5), 1)); // pop ecx; pop eax; pop ebx; jmp eax raw.push_back(RawGadget(string("\xC3", 1), 2)); // ret db.analyse_raw_gadgets(raw, arch); // Test cst_pop on a function call strategy graph vector graphs; ROPCompiler* comp = new ROPCompiler(arch, &db); vector instrs = comp->parse("0x1234()"); comp->il_to_strategy(graphs, instrs[0], NULL, ABI::X86_CDECL); // Apply strat graphs[0]->rule_mov_cst_pop(1, arch); graphs[0]->select_gadgets(db); ropchain = graphs[0]->get_ropchain(arch); // nb += _assert_ropchain(ropchain, "Basic application of strategy rule failed"); delete arch; delete comp; */ return nb; } // Buggy X64 syscall... unsigned int test_x64_syscall(){ unsigned int nb = 0; /* DOESN'T WORK ANYMORE WITH NEW COMPILER MECANISM Arch* arch = new ArchX64(); GadgetDB db; ROPChain* ropchain = nullptr; vector raw; raw.push_back(RawGadget(string("\x58\xC3", 2), 1)); // pop rax; ret raw.push_back(RawGadget(string("\x5F\xC3", 2), 2)); // pop rdi; ret raw.push_back(RawGadget(string("\x83\xC5\x20\x0F\x05", 5), 3)); // add ebp, 32; syscall raw.push_back(RawGadget(string("\x5E\xC3", 2), 4)); // pop rsi; ret raw.push_back(RawGadget(string("\x59xC3", 2), 5)); // pop rcx; ret raw.push_back(RawGadget(string("\x41\x5F\xC3", 3), 6)); // pop r15; ret raw.push_back(RawGadget(string("\x48\x89\xC2\x41\xFF\xD7", 6), 7)); // mov rdx, rax; call r15 db.analyse_raw_gadgets(raw, arch); // Test cst_pop on a function call strategy graph vector graphs; ROPCompiler* comp = new ROPCompiler(arch, &db); string query = "sys_11(1,2,3)"; vector instrs = comp->parse(query); comp->il_to_strategy(graphs, instrs[0], nullptr, ABI::NONE, System::LINUX); // Apply strat graphs[0]->rule_mov_cst_pop(1, arch); graphs[0]->rule_mov_cst_pop(2, arch); // Adjust rdx graphs[0]->rule_generic_adjust_jmp(3, arch); graphs[0]->rule_mov_cst_pop(7, arch); graphs[0]->rule_generic_transitivity(3); graphs[0]->rule_mov_cst_pop(10, arch); // Adjust eax graphs[0]->rule_mov_cst_pop(4, arch); graphs[0]->select_gadgets(db, nullptr, arch); ropchain = graphs[0]->get_ropchain(arch); nb += _assert_ropchain(ropchain, "Applications of rules to get syscall ropchain failed"); for( auto g : graphs ){ delete g; } delete arch; delete comp; */ return nb; } } } using namespace test::strategy; // All unit tests void test_strategy(){ unsigned int total = 0; string green = "\033[1;32m"; string def = "\033[0m"; string bold = "\033[1m"; // Start testing cout << bold << "[" << green << "+" << def << bold << "]" << def << " Testing strategy graphs... " << std::flush; for( int i = 0; i < 1; i++){ total += basic(); total += rules(); total += test_generic_adjust_jmp(); total += test_adjust_load(); total += test_generic_src_transitivity(); total += test_adjust_store(); total += test_cst_pop(); total += test_x64_syscall(); } // Return res cout << "\t\t" << total << "/" << total << green << "\t\tOK" << def << endl; }