├── .clang-format ├── .cmake-format.json ├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── CMakeLists.txt ├── CODEOWNERS ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── VERSION ├── examples ├── Makefile ├── example.py ├── example_punpckhdq.py ├── example_rep.py ├── example_tsc.py ├── example_x64.py ├── simple.asm └── simple.c ├── godefroid ├── flag_map.py ├── godefroid.py ├── policies.py ├── policy_map.py └── requirements.txt ├── microx ├── CMakeLists.txt ├── Executor.cpp ├── Python.cpp ├── XED.h ├── __init__.py └── include │ └── microx │ └── Executor.h ├── scripts ├── bootstrap.sh └── release └── setup.py /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google -------------------------------------------------------------------------------- /.cmake-format.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": { 3 | "line_width": 100, 4 | "tab_size": 2, 5 | "separate_ctrl_name_with_space": true, 6 | "separate_fn_name_with_space": false, 7 | "dangle_parens": true, 8 | "dangle_align": "prefix", 9 | "line_ending": "unix" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: github-actions 5 | directory: / 6 | schedule: 7 | interval: daily 8 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | schedule: 9 | # run CI every day even if no PRs/merges occur 10 | - cron: '0 12 * * *' 11 | 12 | jobs: 13 | format: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: deps 18 | run: | 19 | sudo pip install cmake-format black 20 | - name: format 21 | run: make format 22 | 23 | build: 24 | strategy: 25 | matrix: 26 | platform: ["ubuntu-latest", "macos-latest"] 27 | runs-on: ${{ matrix.platform }} 28 | steps: 29 | - uses: actions/checkout@v4 30 | - name: build-xed 31 | run: ./scripts/bootstrap.sh 32 | - name: build 33 | run: | 34 | mkdir build && cd build 35 | XED_DIR=../third_party/ cmake .. 36 | cmake --build . 37 | 38 | build-py: 39 | strategy: 40 | matrix: 41 | platform: ["ubuntu-latest", "macos-latest"] 42 | python: 43 | - "3.7" 44 | - "3.8" 45 | - "3.9" 46 | - "3.10" 47 | runs-on: ${{ matrix.platform }} 48 | steps: 49 | - uses: actions/checkout@v4 50 | 51 | - uses: actions/setup-python@v5 52 | with: 53 | python-version: ${{ matrix.python }} 54 | 55 | - name: build-xed 56 | run: ./scripts/bootstrap.sh 57 | 58 | - name: deps 59 | run: python -m pip install wheel 60 | 61 | - name: build 62 | run: pip wheel . -w wheel/ 63 | 64 | manylinux: 65 | runs-on: ubuntu-latest 66 | steps: 67 | - uses: actions/checkout@v4 68 | 69 | - name: manylinux 70 | run: make manylinux 71 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - 'v*' 5 | 6 | name: release 7 | 8 | jobs: 9 | create-release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: create release 13 | id: create_release 14 | uses: actions/create-release@v1 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | with: 18 | tag_name: ${{ github.ref }} 19 | release_name: Release ${{ github.ref }} 20 | draft: false 21 | prerelease: ${{ contains(github.ref, 'pre') || contains(github.ref, 'rc') }} 22 | 23 | linux-wheels: 24 | name: build linux wheels for PyPI 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - uses: actions/setup-python@v5 30 | with: 31 | python-version: "3.8" 32 | 33 | - name: deps 34 | run: python -m pip install auditwheel 35 | 36 | - name: build linux wheels 37 | run: make manylinux 38 | 39 | - name: fixup wheels 40 | run: | 41 | # NOTE(ww): --plat here MUST be kept up to date with the Makefile. 42 | for whl in dist/*.whl; do 43 | auditwheel repair "${whl}" --plat manylinux_2_28_x86_64 -w wheel/ 44 | done 45 | 46 | - uses: actions/upload-artifact@v4 47 | with: 48 | name: wheels 49 | path: wheel/*.whl 50 | 51 | macos-wheels: 52 | strategy: 53 | matrix: 54 | python: 55 | - "3.7" 56 | - "3.8" 57 | - "3.9" 58 | - "3.10" 59 | name: build macOS wheels for PyPI 60 | runs-on: macos-latest 61 | steps: 62 | - uses: actions/checkout@v4 63 | 64 | - uses: actions/setup-python@v5 65 | with: 66 | python-version: ${{ matrix.python }} 67 | 68 | - name: build xed 69 | run: ./scripts/bootstrap.sh 70 | 71 | - name: deps 72 | run: pip install wheel 73 | 74 | - name: build wheel 75 | run: pip wheel . -w wheel/ 76 | 77 | - uses: actions/upload-artifact@v4 78 | with: 79 | name: wheels 80 | path: wheel/*.whl 81 | 82 | publish-wheels: 83 | name: upload built wheels 84 | runs-on: ubuntu-latest 85 | needs: [linux-wheels, macos-wheels] 86 | steps: 87 | - uses: actions/checkout@v4 88 | 89 | - uses: actions/download-artifact@v4 90 | with: 91 | name: wheels 92 | path: wheel/ 93 | 94 | - name: publish 95 | uses: pypa/gh-action-pypi-publish@v1 96 | with: 97 | user: __token__ 98 | password: ${{ secrets.PYPI_TOKEN }} 99 | packages_dir: wheel/ 100 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /dist/ 3 | /examples/*.elf 4 | /third_party/ 5 | 6 | # The compiled extension is placed here when 7 | # microx is installed with `pip install -e .` 8 | /microx_core.* 9 | 10 | __pycache__/ 11 | *.py[cod] 12 | *.egg-info/ 13 | 14 | .vscode/ipch/* 15 | .vscode/settings.json 16 | 17 | # IntelliJ family of IDEs (PyCharm or any other IntelliJ IDE with python plugin) 18 | .idea/ 19 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10 FATAL_ERROR) 2 | 3 | project(microx) 4 | 5 | set(CMAKE_CXX_STANDARD 11) 6 | 7 | if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 8 | set(CMAKE_INSTALL_PREFIX 9 | "${CMAKE_BINARY_DIR}" 10 | CACHE PATH "Default install directory" FORCE 11 | ) 12 | endif () 13 | 14 | if (MSVC) 15 | set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) 16 | endif () 17 | 18 | add_subdirectory(microx) 19 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @pgoodman 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016 Trail of Bits, Inc. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include microx *.cpp *.h 2 | include LICENSE 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # NOTE(ww): Make sure to update the platform tag in .github/workflows/release.yml 2 | # if you update this image. The two MUST remain in sync to ensure correct wheel builds. 3 | MANYLINUX_IMAGE := quay.io/pypa/manylinux_2_28_x86_64 4 | CLANG_FORMAT := clang-format 5 | ALL_PY_SRCS := $(shell \ 6 | find . \ 7 | -type f \ 8 | \( -path */third_party/* \) -prune \ 9 | -o -name '*.py' \ 10 | -print \ 11 | ) 12 | ALL_CXX_SRCS := $(shell \ 13 | find . \ 14 | -type f \ 15 | \( -path */third_party/* \) -prune \ 16 | -o \( -name '*.cpp' -o -name '*.h' \) \ 17 | -print \ 18 | ) 19 | ALL_LISTFILES := $(shell \ 20 | find . \ 21 | -type f \ 22 | \( -path */third_party/* \) -prune \ 23 | -o \( -name 'CMakeLists.txt' -o -name '*.cmake' \) \ 24 | -print \ 25 | ) 26 | 27 | .PHONY: all 28 | all: 29 | @echo "This Makefile does not build anything." 30 | 31 | .PHONY: format 32 | format: blacken clang-format cmake-format 33 | 34 | .PHONY: blacken 35 | blacken: 36 | black $(ALL_PY_SRCS) 37 | git diff --exit-code 38 | 39 | .PHONY: clang-format 40 | clang-format: 41 | $(CLANG_FORMAT) -i -style=file $(ALL_CXX_SRCS) 42 | git diff --exit-code 43 | 44 | .PHONY: cmake-format 45 | cmake-format: 46 | cmake-format -i $(ALL_LISTFILES) 47 | git diff --exit-code 48 | 49 | .PHONY: manylinux 50 | manylinux: 51 | docker pull $(MANYLINUX_IMAGE) 52 | # NOTE(ww): We pass PYTHON through the environment here for the XED 53 | # build, which is Python based. The version doesn't matter, as long 54 | # as mbuild itself is okay with it. It is **not** related to the wheel 55 | # builds, which happen subsequently. 56 | docker run -e PYTHON=/opt/python/cp38-cp38/bin/python \ 57 | --rm -v $(shell pwd):/io $(MANYLINUX_IMAGE) \ 58 | /io/scripts/bootstrap.sh 59 | docker run \ 60 | --rm -v $(shell pwd):/io $(MANYLINUX_IMAGE) \ 61 | /opt/python/cp37-cp37m/bin/pip wheel /io -w /io/dist 62 | docker run \ 63 | --rm -v $(shell pwd):/io $(MANYLINUX_IMAGE) \ 64 | /opt/python/cp38-cp38/bin/pip wheel /io -w /io/dist 65 | docker run \ 66 | --rm -v $(shell pwd):/io $(MANYLINUX_IMAGE) \ 67 | /opt/python/cp39-cp39/bin/pip wheel /io -w /io/dist 68 | docker run \ 69 | --rm -v $(shell pwd):/io $(MANYLINUX_IMAGE) \ 70 | /opt/python/cp310-cp310/bin/pip wheel /io -w /io/dist 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # microx - a micro execution framework 2 | 3 | ![CI](https://github.com/lifting-bits/microx/workflows/CI/badge.svg) 4 | 5 | Microx is a single-instruction "micro execution" framework. Microx enables a program to safely execute an arbitrary x86 or x86-64 instruction. Microx does not take over or require a process context in order to execute an instruction. It is easily embedded within other programs, as exampled by the Python bindings. 6 | 7 | The microx approach to safe instruction execution of arbitrary instructions is to require the user of microx to manage machine state. Microx is packaged as a C++ `Executor` class that must be extended. The Python bindings also present a class, `microx.Executor`, that must be extended. A program extending this class must implement methods such as `read_register` and `read_memory`. When supplied with instruction bytes, microx will invoke the class methods in order to pull in the minimal requisite machine state to execute the instruction. After executing the instruction, microx will "report back" the state changes induced by the instruction's execution, again via methods like `write_register` and `write_memory`. 8 | 9 | The following lists some use-cases of microx: 10 | 11 | * Speculative execution of code within a debugger-like system. In this scenario, microx can be used to execute instructions from the process being debugged, in such a way that the memory and state of the original program will be preserved. 12 | * Binary symbolic execution. In this scenario, which was the original use-case of microx, a binary symbolic executor can use microx to safely execute an instruction that is not supported or modelled by the symbolic execution system. The use of microx will minimize the amount of symbolic state that may need to be concretized in order to execute the instruction. Microx was used in this fashion in a Python-based binary symbolic executor. Microx comes with Python bindings for this reason. 13 | * Headless taint tracking. Taint tracking can be implemented with microx, much as it would be with Intel's PIN, but without a process context. Microx can be integrated into a disassembler such as IDA or Binary Ninja and used to execute instruction, performing taint tracking along the way. 14 | 15 | Microx uses a combination of JIT-based dynamic binary translation and instruction emulation in order to safely execute x86 instructions. It is a 64-bit library, but it can execute 32-bit instructions that are not supported on 64-bit platforms. It can be easily embedded, as it performs no dynamic memory allocations, and is re-entrant. 16 | 17 | Microx depends on [Intel's XED](https://intelxed.github.io/) instruction encoder and decoder. 18 | 19 | ## Installing 20 | 21 | Microx has Python bindings; you can install them via pip on macOS and Linux: 22 | 23 | ```bash 24 | $ python -m pip install microx 25 | ``` 26 | 27 | ## Building (Python) 28 | 29 | If we don't supply a Python wheel for your platform, you can build microx yourself. You'll 30 | need at least Python 3.7. 31 | 32 | First, build XED: 33 | 34 | ```bash 35 | $ ./scripts/bootstrap.sh 36 | ``` 37 | 38 | Then, use `setup.py build`: 39 | 40 | ```bash 41 | $ setup.py build 42 | ``` 43 | 44 | ## Building (C++) 45 | 46 | Microx's C++ library can be built with CMake. 47 | 48 | The CMake build uses `XED_DIR` to locate the XED library and headers. 49 | 50 | To use the `third_party` XED build: 51 | 52 | ```bash 53 | $ ./scripts/bootstrap.sh 54 | $ export XED_DIR=$(pwd)/third_party 55 | ``` 56 | 57 | Then, run a normal CMake build: 58 | 59 | ```bash 60 | mkdir build && cd build 61 | cmake .. 62 | cmake --build . 63 | ``` 64 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.4.1 2 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | CC=clang 2 | 3 | TARGETS=simplec.elf simpleasm.elf 4 | 5 | .PHONY: all clean 6 | 7 | all: $(TARGETS) 8 | 9 | 10 | simplec.elf: simple.c 11 | $(CC) -m32 -O0 -o $@ $< 12 | 13 | simpleasm.elf: simple.asm 14 | $(CC) -m32 -o $@ $< 15 | 16 | clean: 17 | rm -f $(TARGETS) -------------------------------------------------------------------------------- /examples/example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2019 Trail of Bits, Inc., all rights reserved. 3 | 4 | import microx 5 | import traceback 6 | 7 | if __name__ == "__main__": 8 | 9 | # 13 Disassembly: 10 | # 14 0: 55 push ebp 11 | # 15 1: 89 e5 mov ebp,esp 12 | # 16 3: 51 push ecx 13 | # 17 4: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] 14 | # 18 7: 8a 08 mov cl,BYTE PTR [eax] 15 | # 19 9: 88 4d ff mov BYTE PTR [ebp-0x1],cl 16 | # 20 c: 89 ec mov esp,ebp 17 | # 21 e: 5d pop ebp 18 | # 22 f: c2 00 00 ret 0x0 19 | 20 | o = microx.Operations() 21 | 22 | code = microx.ArrayMemoryMap(o, 0x1000, 0x2000, can_write=False, can_execute=True) 23 | stack = microx.ArrayMemoryMap(o, 0x80000, 0x82000) 24 | 25 | code.store_bytes( 26 | 0x1000, 27 | b"\x55\x89\xE5\x51\x8B\x45\x08\x8A\x08\x88\x4D\xFF\x89\xEC\x5D\xC2\x00\x00", 28 | ) 29 | 30 | m = microx.Memory(o, 32) 31 | m.add_map(code) 32 | m.add_map(stack) 33 | 34 | t = microx.EmptyThread(o) 35 | t.write_register("EIP", 0x1000) 36 | t.write_register("ESP", 0x81000) 37 | 38 | p = microx.Process(o, m) 39 | 40 | try: 41 | while True: 42 | pc = t.read_register("EIP", t.REG_HINT_PROGRAM_COUNTER) 43 | print("Emulating instruction at {:08x}".format(pc)) 44 | p.execute(t, 1) 45 | except Exception as e: 46 | print(e) 47 | print(traceback.format_exc()) 48 | -------------------------------------------------------------------------------- /examples/example_punpckhdq.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | import microx 3 | 4 | 5 | def main(): 6 | o = microx.Operations() 7 | 8 | code = microx.ArrayMemoryMap(o, 0x1000, 0x2000, can_write=False, can_execute=True) 9 | stack = microx.ArrayMemoryMap(o, 0x80000, 0x82000) 10 | heap = microx.ArrayMemoryMap(o, 0x10000, 0x12000) 11 | 12 | code.store_bytes( 13 | 0x1050, 14 | # punpckhdq mm0, [eax] 15 | b"\x0F\x6A\x00\xeb\x0e", 16 | ) 17 | heap.store_bytes(0x10900, b"\xab\x00\x12\x00\xab\xab\xab\xab") 18 | 19 | m = microx.Memory(o, 32) 20 | m.add_map(code) 21 | m.add_map(stack) 22 | m.add_map(heap) 23 | 24 | t = microx.EmptyThread(o) 25 | t.write_register("EIP", 0x1050) 26 | t.write_register("ESP", 0x81000) 27 | 28 | t.write_register("MM0", 0xDEADBEEF12121212) 29 | t.write_register("EAX", 0x10900) 30 | 31 | p = microx.Process(o, m) 32 | 33 | try: 34 | pc = t.read_register("EIP", t.REG_HINT_PROGRAM_COUNTER) 35 | eax = t.read_register("EAX", t.REG_HINT_MEMORY_BASE_ADDRESS) 36 | mm0 = t.read_register("MM0", t.REG_HINT_GENERAL) 37 | print(f"Emulating instruction at {pc:08x} (EAX={eax:08x}, MM0={mm0:08x}") 38 | p.execute(t, 1) 39 | pc = t.read_register("EIP", t.REG_HINT_PROGRAM_COUNTER) 40 | eax = t.read_register("EAX", t.REG_HINT_MEMORY_BASE_ADDRESS) 41 | mm0 = t.read_register("MM0", t.REG_HINT_GENERAL) 42 | print(f"Finished emulating instruction: (EAX={eax:08x}, MM0={mm0:08x}") 43 | except Exception as e: 44 | print(e) 45 | print(traceback.format_exc()) 46 | 47 | 48 | if __name__ == "__main__": 49 | main() 50 | -------------------------------------------------------------------------------- /examples/example_rep.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2019 Trail of Bits, Inc., all rights reserved. 3 | 4 | import microx 5 | import traceback 6 | 7 | if __name__ == "__main__": 8 | 9 | # Disassembly: 10 | # lea edi, [esp - 32] 11 | # mov eax, 0x41 12 | # mov ecx, 32 13 | # rep stosb 14 | # 15 | # lea esi, [esp - 32] 16 | # lea edi, [esp - 64] 17 | # mov ecx, 32 18 | # rep movsb 19 | # 20 | # mov byte ptr [esp - 32], 0 21 | # lea edi, [esp - 64] 22 | # xor eax, eax 23 | # mov ecx, -1 24 | # repne scasb 25 | # not ecx 26 | # dec ecx 27 | 28 | o = microx.Operations() 29 | 30 | code = microx.ArrayMemoryMap(o, 0x1000, 0x2000, can_write=False, can_execute=True) 31 | stack = microx.ArrayMemoryMap(o, 0x80000, 0x82000) 32 | 33 | code.store_bytes( 34 | 0x1000, 35 | b"\x8d\x7c\x24\xe0\xb8\x41\x00\x00\x00\xb9\x20\x00\x00\x00\xf3\xaa\x8d\x74\x24\xe0\x8d\x7c\x24\xc0\xb9\x20\x00\x00\x00\xf3\xa4\xc6\x44\x24\xe0\x00\x8d\x7c\x24\xc0\x31\xc0\xb9\xff\xff\xff\xff\xf2\xae\xf7\xd1\x49", 36 | ) 37 | 38 | m = microx.Memory(o, 32) 39 | m.add_map(code) 40 | m.add_map(stack) 41 | 42 | t = microx.EmptyThread(o) 43 | t.write_register("EIP", 0x1000) 44 | t.write_register("ESP", 0x81000) 45 | 46 | p = microx.Process(o, m) 47 | 48 | try: 49 | while True: 50 | pc = t.read_register("EIP", t.REG_HINT_PROGRAM_COUNTER) 51 | eax = t.read_register("EAX", t.REG_HINT_GENERAL) 52 | esi = t.read_register("ESI", t.REG_HINT_GENERAL) 53 | edi = t.read_register("EDI", t.REG_HINT_GENERAL) 54 | ecx = t.read_register("ECX", t.REG_HINT_GENERAL) 55 | print( 56 | "Emulating instruction at {:08x} (EAX={:08x}, ESI={:08x}, EDI={:08x}, ECX={:08x})".format( 57 | pc, eax, esi, edi, ecx 58 | ) 59 | ) 60 | p.execute(t, 1) 61 | except Exception as e: 62 | print(e) 63 | print(traceback.format_exc()) 64 | -------------------------------------------------------------------------------- /examples/example_tsc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2019 Trail of Bits, Inc., all rights reserved. 3 | 4 | import microx 5 | import traceback 6 | 7 | if __name__ == "__main__": 8 | 9 | # Disassembly: 10 | # mov eax, 0x55555555 11 | # mov edx, eax 12 | # rdtsc 13 | # mov eax, 0x55555555 14 | # mov edx, eax 15 | # rdtscp 16 | 17 | o = microx.Operations() 18 | 19 | code = microx.ArrayMemoryMap(o, 0x1000, 0x2000, can_write=False, can_execute=True) 20 | stack = microx.ArrayMemoryMap(o, 0x80000, 0x82000) 21 | 22 | code.store_bytes( 23 | 0x1000, 24 | b"\xb8\x55\x55\x55\x55\x89\xc2\x0f\x31\xb8\x55\x55\x55\x55\x89\xc2\x0f\x01\xf9", 25 | ) 26 | 27 | m = microx.Memory(o, 32) 28 | m.add_map(code) 29 | m.add_map(stack) 30 | 31 | t = microx.EmptyThread(o) 32 | t.write_register("EIP", 0x1000) 33 | t.write_register("ESP", 0x81000) 34 | 35 | p = microx.Process(o, m) 36 | 37 | try: 38 | while True: 39 | pc = t.read_register("EIP", t.REG_HINT_PROGRAM_COUNTER) 40 | eax = t.read_register("EAX", t.REG_HINT_GENERAL) 41 | edx = t.read_register("EDX", t.REG_HINT_GENERAL) 42 | tsc = t.read_register("TSC", t.REG_HINT_NONE) 43 | print( 44 | "Emulating instruction at {:08x} (EAX={:08x}, EDX={:08x}, TSC={:016x})".format( 45 | pc, eax, edx, tsc 46 | ) 47 | ) 48 | p.execute(t, 1) 49 | except Exception as e: 50 | print(e) 51 | print(traceback.format_exc()) 52 | -------------------------------------------------------------------------------- /examples/example_x64.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2019 Trail of Bits, Inc., all rights reserved. 3 | 4 | import microx 5 | import traceback 6 | 7 | if __name__ == "__main__": 8 | 9 | # Disassembly: 10 | # push rbp 11 | # mov rbp,rsp 12 | # push rcx 13 | # mov rax,QWORD PTR [rbp+0x8] 14 | # mov cl,BYTE PTR [rax] 15 | # mov BYTE PTR [rbp-0x1],cl 16 | # mov rsp,rbp 17 | # pop rbp 18 | # ret 0x0 19 | o = microx.Operations() 20 | 21 | code = microx.ArrayMemoryMap( 22 | o, 0x100001000, 0x100002000, can_write=False, can_execute=True 23 | ) 24 | stack = microx.ArrayMemoryMap(o, 0x200080000, 0x200082000) 25 | 26 | code.store_bytes( 27 | 0x100001000, 28 | b"\x55\x48\x89\xe5\x51\x48\x8b\x45\x08\x8a\x08\x88\x4d\xff\x48\x89\xec\x5d\xc2\x00\x00", 29 | ) 30 | 31 | m = microx.Memory(o, 64) 32 | m.add_map(code) 33 | m.add_map(stack) 34 | 35 | t = microx.EmptyThread(o) 36 | t.write_register("RIP", 0x100001000) 37 | t.write_register("RSP", 0x200081000) 38 | 39 | p = microx.Process(o, m) 40 | 41 | try: 42 | while True: 43 | pc = t.read_register("RIP", t.REG_HINT_PROGRAM_COUNTER) 44 | print("Emulating instruction at {:016x}".format(pc)) 45 | p.execute(t, 1) 46 | except Exception as e: 47 | print(e) 48 | print(traceback.format_exc()) 49 | -------------------------------------------------------------------------------- /examples/simple.asm: -------------------------------------------------------------------------------- 1 | .text 2 | .globl main 3 | .intel_syntax noprefix 4 | 5 | main: 6 | push ebp 7 | mov ebp,esp 8 | push ecx 9 | mov eax, dword ptr [ebp+0x8] 10 | mov cl, byte ptr [eax] 11 | mov byte ptr [ebp-0x1], cl 12 | mov esp,ebp 13 | pop ebp 14 | ret 0x0 15 | 16 | -------------------------------------------------------------------------------- /examples/simple.c: -------------------------------------------------------------------------------- 1 | // build via: 2 | // clang -m32 -O0 -o simplec.elf simple.c 3 | void mystery(int *p) { 4 | *p = (int)p & 0xFFFFFFF; 5 | } 6 | 7 | int main (int argc, const char *argv[]) { 8 | 9 | int test_out = 0; 10 | 11 | mystery(&test_out); 12 | 13 | return test_out; 14 | } 15 | -------------------------------------------------------------------------------- /godefroid/flag_map.py: -------------------------------------------------------------------------------- 1 | import microx 2 | from flags import Flags 3 | 4 | 5 | class MemoryFlags(Flags): 6 | Read = () # can be read 7 | Write = () # can be written 8 | Execute = () # can be executed 9 | 10 | 11 | class PolicyFlags(Flags): 12 | Read = () 13 | Written = () 14 | Executed = () 15 | Present = () # TODO(artem): Used for future allocation/free tracking 16 | 17 | 18 | class FlaggedMemoryMap(microx.MemoryMap): 19 | def __init__(self, ops, base, limit, access_flags, mapname=None): 20 | assert base < limit 21 | self._ops = ops 22 | self._base = base 23 | self._limit = limit 24 | self._access_flags = access_flags 25 | super(FlaggedMemoryMap, self).__init__(mapname) 26 | 27 | def _can_do_op(self, op, addr): 28 | return op in self._access_flags and self._base <= addr < self._limit 29 | 30 | def can_read(self, byte_addr): 31 | return self._can_do_op(MemoryFlags.Read, byte_addr) 32 | 33 | def can_write(self, byte_addr): 34 | return self._can_do_op(MemoryFlags.Write, byte_addr) 35 | 36 | def can_execute(self, byte_addr): 37 | return self._can_do_op(MemoryFlags.Execute, byte_addr) 38 | 39 | def base(self): 40 | return self._base 41 | 42 | def limit(self): 43 | return self._limit 44 | -------------------------------------------------------------------------------- /godefroid/godefroid.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2019 Trail of Bits, Inc., all rights reserved. 3 | import microx 4 | from microx_core import InstructionFetchError # pylint: disable=no-name-in-module 5 | import traceback 6 | import logging 7 | import sys 8 | from flag_map import MemoryFlags 9 | from policies import DefaultMemoryPolicy, InputMemoryPolicy, InputType 10 | from policy_map import PolicyMemoryMap 11 | import argparse 12 | import os 13 | from enum import Enum, auto 14 | import cle 15 | import copy 16 | 17 | 18 | class Icount(Enum): 19 | INFINITE = auto() 20 | COUNTED = auto() 21 | 22 | 23 | class GodefroidRunner(object): 24 | def __init__( 25 | self, 26 | memory, 27 | initial_pc, 28 | initial_sp, 29 | max_insts, 30 | run_length=Icount.COUNTED, 31 | magic_return=0xFEEDF00D, 32 | ): 33 | 34 | assert run_length == Icount.INFINITE or max_insts > 0 35 | 36 | self._memory = memory 37 | self.i_pc = initial_pc 38 | self.i_sp = initial_sp 39 | self.max_insts = max_insts 40 | self.run_length = run_length 41 | self.magic_return = magic_return 42 | 43 | def make_new_process(self, mem): 44 | p = GodefroidProcess(mem._ops, mem, self.i_sp) 45 | # write our "magic return address" to the stack 46 | if self.magic_return is not None: 47 | stacks = list(p._memory.find_maps_by_name("[stack]")) 48 | assert len(stacks) == 1 49 | stack = stacks[0] 50 | # Write our magic return on the stack without triggering a policy 51 | stack.store_bytes_raw( 52 | p._initial_sp, 53 | self.magic_return.to_bytes( 54 | p._memory.address_size_bits() // 8, byteorder="little" 55 | ), 56 | ) 57 | sys.stdout.write( 58 | f"[+] Using fake return address of {self.magic_return:08x}\n" 59 | ) 60 | 61 | return p 62 | 63 | def run(self, iterations=1): 64 | 65 | return_dict = {} 66 | for itercount in range(iterations): 67 | 68 | sys.stdout.write(f"[+] Attempting iteration {itercount}/{iterations}\n") 69 | 70 | # TODO(artem): This is really slow. Should be implemented as some kind of CoW semantics 71 | # or at least provide a way to 'reset' to a clean state 72 | proc = self.make_new_process(copy.deepcopy(self._memory)) 73 | 74 | t = microx.EmptyThread(proc) 75 | t.write_register("EIP", self.i_pc) 76 | t.write_register("ESP", proc._initial_sp) 77 | 78 | sys.stdout.write(f"[+] Initial EIP is: {self.i_pc:08x}\n") 79 | sys.stdout.write(f"[+] Initial ESP is: {proc._initial_sp:08x}\n") 80 | 81 | instruction_count = 0 82 | try: 83 | while ( 84 | self.run_length == Icount.INFINITE 85 | or instruction_count < self.max_insts 86 | ): 87 | pc_bytes = t.read_register("EIP", t.REG_HINT_PROGRAM_COUNTER) 88 | pc = proc._ops.convert_to_integer(pc_bytes) 89 | if self.magic_return is not None and self.magic_return == pc: 90 | sys.stdout.write("[+] Reached return address. Stopping\n") 91 | break 92 | else: 93 | sys.stdout.write(f"[+] Emulating instruction at: {pc:08x}\n") 94 | proc.execute(t, 1) 95 | instruction_count += 1 96 | except InstructionFetchError as efe: 97 | sys.stdout.write( 98 | f"[!] Could not fetch instruction at: {pc:08x}. Error msg: {repr(efe)}.\n" 99 | ) 100 | except Exception as e: 101 | print(e) 102 | print(traceback.format_exc()) 103 | pass 104 | 105 | return_dict[itercount] = (instruction_count, proc) 106 | return return_dict 107 | 108 | 109 | class GodefroidProcess(microx.Process): 110 | INPUT_SPACE_SIZE = 0x1080000 111 | 112 | def __init__(self, ops, memory, sp_value=None): 113 | super(GodefroidProcess, self).__init__(ops, memory) 114 | 115 | # NOTE(artem): Allows for user-specified input heaps 116 | if not memory.find_maps_by_name("[input_space]"): 117 | input_size = GodefroidProcess.INPUT_SPACE_SIZE 118 | input_base = memory.find_hole(input_size) 119 | 120 | sys.stdout.write( 121 | f"[+] Mapping input space to: {input_base:08x} - {input_base+input_size:08x}\n" 122 | ) 123 | input_space = PolicyMemoryMap( 124 | self._ops, 125 | input_base, 126 | input_base + input_size, 127 | MemoryFlags.Read | MemoryFlags.Write, 128 | DefaultMemoryPolicy(), # DefaultPolicy is only temporary, see below 129 | mapname="[input_space]", 130 | ) 131 | 132 | memory.add_map(input_space) 133 | 134 | stacks = list(memory.find_maps_by_name("[stack]")) 135 | assert len(stacks) == 1 136 | stack = stacks[0] 137 | if sp_value is None: 138 | # generate an aligned stack of some kind 139 | sp_value = stack.base() + ((stack.limit() - stack.base()) // 2) & (~0xFF) 140 | 141 | self._initial_sp = sp_value 142 | 143 | # TODO(artem): Support multiple input areas 144 | inputs = list(memory.find_maps_by_name("[input_space]")) 145 | assert len(inputs) == 1 146 | 147 | # adjust function stack by the pushed return address 148 | function_stack_start = self._initial_sp + (memory.address_size_bits() // 8) 149 | assert stack.base() <= function_stack_start < stack.limit() 150 | 151 | # assumes stacks grow down 152 | # TODO(artem): Support multiple input spaces in InputMemoryPolicy 153 | input_policy = InputMemoryPolicy( 154 | memory.address_size_bits(), 155 | (function_stack_start, stack.limit()), # argument space 156 | (inputs[0].base(), inputs[0].limit()), # pointer space 157 | ) 158 | 159 | stacks[0].attach_policy(input_policy) 160 | 161 | # attach input policy instead of default policy on all input spaces 162 | for h in inputs: 163 | h.attach_policy(input_policy) 164 | 165 | self._policy = input_policy 166 | 167 | def compute_address(self, seg_name, base_addr, index, scale, disp, size, hint): 168 | result = super(GodefroidProcess, self).compute_address( 169 | seg_name, base_addr, index, scale, disp, size, hint 170 | ) 171 | assert isinstance(self._policy, InputMemoryPolicy) 172 | addr = self._policy.handle_compute( 173 | result, base_addr, index, scale, disp, size, hint 174 | ) 175 | return addr 176 | 177 | def get_inputs(self): 178 | assert isinstance(self._policy, InputMemoryPolicy) 179 | return self._policy.get_inputs() 180 | 181 | def get_outputs(self): 182 | assert isinstance(self._policy, InputMemoryPolicy) 183 | return self._policy.get_outputs() 184 | 185 | @classmethod 186 | def create_memory_from_sections(cls, sections): 187 | o = microx.Operations() 188 | 189 | m = microx.Memory(o, 32) 190 | 191 | mem_map = None 192 | for section in sections: 193 | start = section["start"] 194 | size = section["size"] 195 | flags = section["flags"] 196 | name = section["name"] 197 | content = section["content"] 198 | # sys.stdout.write(f"[+] Processing section {name}\n") 199 | 200 | page_start = start & ~(0xFFF) 201 | page_end = (start + size + 0xFFF) & ~0xFFF 202 | if "[stack]" == name: 203 | sys.stdout.write( 204 | f"[+] Creating stack region from 0x{page_start:x} to 0x{page_end:x}. Flags: {flags}\n" 205 | ) 206 | mem_map = PolicyMemoryMap( 207 | o, page_start, page_end, flags, DefaultMemoryPolicy(), mapname=name 208 | ) 209 | m.add_map(mem_map) 210 | else: 211 | for page in range(page_start, page_end, 0x1000): 212 | if not m.can_read(page): 213 | sys.stdout.write( 214 | f"[+] Mapping page from 0x{page:x} to 0x{page+0x1000:x}. Flags: {flags}\n" 215 | ) 216 | mem_map = PolicyMemoryMap( 217 | o, 218 | page, 219 | page + 0x1000, 220 | flags, 221 | DefaultMemoryPolicy(), 222 | mapname=name, 223 | ) 224 | 225 | m.add_map(mem_map) 226 | 227 | if content: 228 | assert mem_map is not None 229 | # sys.stdout.write(f"[+] Writing 0x{len(content):x} bytes at 0x{start:x}\n") 230 | mem_map.store_bytes_raw(start, content) 231 | 232 | return m 233 | 234 | 235 | def default_example(): 236 | 237 | # 13 Disassembly: 238 | # 14 0: 55 push ebp 239 | # 15 1: 89 e5 mov ebp,esp 240 | # 16 3: 51 push ecx 241 | # 17 4: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] 242 | # 18 7: 8a 08 mov cl,BYTE PTR [eax] 243 | # 19 9: 88 4d ff mov BYTE PTR [ebp-0x1],cl 244 | # 20 c: 89 ec mov esp,ebp 245 | # 21 e: 5d pop ebp 246 | # 22 f: c2 00 00 ret 0x0 247 | 248 | sections = [ 249 | { 250 | "name": ".text", 251 | "start": 0x1000, 252 | "size": 0x1000, 253 | "flags": MemoryFlags.Read | MemoryFlags.Execute, 254 | "content": b"\x55\x89\xE5\x51\x8B\x45\x08\x8A\x08\x88\x4D\xFF\x89\xEC\x5D\xC2\x00\x00", 255 | }, 256 | { 257 | "name": "[stack]", 258 | "start": 0x80000, 259 | "size": 0x82000, 260 | "flags": MemoryFlags.Read | MemoryFlags.Write, 261 | "content": None, 262 | }, 263 | ] 264 | 265 | m = GodefroidProcess.create_memory_from_sections(sections) 266 | r = GodefroidRunner(m, initial_pc=0x1000, initial_sp=0x81000, max_insts=15) 267 | rv = r.run(iterations=1) 268 | return rv[0] 269 | 270 | 271 | def make_stack_section(loaded): 272 | 273 | STACK_SIZE = 8 * 1024 * 1024 # 8MB Stack 274 | new_section = {} 275 | if loaded.min_addr - 0x40000 > STACK_SIZE: 276 | new_section["start"] = 0x40000 277 | elif 0xFFFFFFFF - (loaded.max_addr + 0x40000) > STACK_SIZE: 278 | # Align loaded.max_addr to page alignment (on x86) 279 | new_section["start"] = ((loaded.max_addr + 0xFFF) & ~0xFFF) + 0x40000 280 | else: 281 | sys.stdout.write( 282 | f"[!] Could not find a {STACK_SIZE:x} hole for stack. Aborting.\n" 283 | ) 284 | return None 285 | 286 | new_section["name"] = "[stack]" 287 | new_section["size"] = STACK_SIZE 288 | new_section["flags"] = MemoryFlags.Read | MemoryFlags.Write 289 | new_section["content"] = None 290 | 291 | return new_section 292 | 293 | 294 | def load_sections_from_binary(loader, cle_binary): 295 | 296 | sections = [] 297 | 298 | for section in cle_binary.sections: 299 | 300 | # Only care about in-memory sections 301 | if not section.occupies_memory: 302 | continue 303 | 304 | new_section = {} 305 | new_section["name"] = section.name 306 | new_section["start"] = section.min_addr 307 | new_section["size"] = section.memsize 308 | # sys.stdout.write(f"[+] CLE is loading section {section.name} from 0x{section.min_addr:x} to 0x{section.min_addr + section.memsize:x}\n") 309 | 310 | elfseg = cle_binary.find_segment_containing(section.min_addr) 311 | 312 | # no segment... use section permissions and hope for the best 313 | if elfseg is None: 314 | sys.stdout.write( 315 | "[!] WARNING: no ELF segments found.. using section permissions and hoping for the best\n" 316 | ) 317 | elfseg = section 318 | 319 | new_section["flags"] = MemoryFlags.no_flags 320 | if elfseg.is_readable: 321 | new_section["flags"] |= MemoryFlags.Read 322 | if elfseg.is_writable: 323 | new_section["flags"] |= MemoryFlags.Write 324 | if elfseg.is_executable: 325 | new_section["flags"] |= MemoryFlags.Execute 326 | 327 | if section.only_contains_uninitialized_data: 328 | new_section["content"] = b"\00" * new_section["size"] 329 | else: 330 | new_section["content"] = loader.memory.load( 331 | new_section["start"], new_section["size"] 332 | ) 333 | 334 | sections.append(new_section) 335 | 336 | return sections 337 | 338 | 339 | def run_on_binary(binary, entry, icount_type, maxinst): 340 | """ 341 | binary: path to binary file to load 342 | entry: name or hex (0x prefixed) of the entrypoint) 343 | icount_type: infinite (run forever) or counted (max instructions) 344 | maxinst: how many instructions to run (if in counted mode) 345 | """ 346 | 347 | loaded = None 348 | try: 349 | loaded = cle.Loader(binary) 350 | except Exception as e: 351 | sys.stdout.write(f"[!] Could not load binary [{binary}]. Reason: {str(e)}\n") 352 | 353 | if loaded is None: 354 | return None 355 | else: 356 | sys.stdout.write(f"[+] Loaded binary: {binary}\n") 357 | 358 | # loop over binary sections 359 | main_binary = loaded.main_object 360 | if not main_binary: 361 | sys.stdout.write(f"[!] Could not find sections in {binary}\n") 362 | return None 363 | 364 | ep = entry 365 | if entry.startswith("0x"): 366 | ep = int(entry, base=16) 367 | # zero is technically a valid address, but warn about it 368 | if int == type(ep): 369 | if ep == 0: 370 | sys.stdout.write( 371 | "[-] WARNING: Entrypoint is zero! This could be intentional but maybe something is wrong\n" 372 | ) 373 | sys.stdout.write("[+] Entry Point: 0x{ep:x}\n") 374 | else: 375 | sym = loaded.find_symbol(ep) 376 | if not sym: 377 | sys.stdout.write(f"[!] Could not find symbol {ep} in {binary}\n") 378 | return None 379 | else: 380 | ep_addr = sym.rebased_addr 381 | sys.stdout.write(f"[+] Found [{ep}] at 0x{ep_addr:x}\n") 382 | ep = ep_addr 383 | 384 | if ep < main_binary.min_addr or ep > main_binary.max_addr: 385 | sys.stdout.write( 386 | f"[!] Entry point addres f{ep:x} " 387 | f"is outside the range of the main " 388 | f"program binary [f{main_binary.min_addr:x} - f{main_binary.max_addr:x}]\n" 389 | ) 390 | return None 391 | 392 | sections = load_sections_from_binary(loaded, main_binary) 393 | if len(sections) == 0: 394 | sys.stdout.write(f"[!] Could not load sections from f{binary}\n") 395 | return None 396 | else: 397 | sys.stdout.write(f"[+] Loaded {len(sections)} sections from {binary}\n") 398 | 399 | stack_s = make_stack_section(loaded) 400 | if not stack_s: 401 | return None 402 | else: 403 | sys.stdout.write(f"[+] Loaded stack at: {stack_s['start']:x}\n") 404 | sections.append(stack_s) 405 | 406 | m = GodefroidProcess.create_memory_from_sections(sections) 407 | r = GodefroidRunner( 408 | m, initial_pc=ep, initial_sp=None, max_insts=maxinst, run_length=icount_type 409 | ) 410 | rv = r.run(iterations=1) 411 | return rv[0] 412 | 413 | 414 | if __name__ == "__main__": 415 | 416 | parser = argparse.ArgumentParser() 417 | group_a = parser.add_mutually_exclusive_group() 418 | group_a.add_argument( 419 | "--default", action="store_true", help="Run the default example" 420 | ) 421 | group_b = group_a.add_argument_group() 422 | group_b.add_argument("--binary", help="Which binary file to load") 423 | group_b.add_argument( 424 | "--entry", 425 | help="Address (in hex, 0x prefixed) or symbol at which to start execution", 426 | ) 427 | group_c = group_a.add_mutually_exclusive_group() 428 | group_c.add_argument( 429 | "--maxinst", type=int, default=1024, help="How many instrutions to execute" 430 | ) 431 | group_c.add_argument( 432 | "--infinite", 433 | action="store_true", 434 | help="Execute instructions until time limit is reached", 435 | ) 436 | 437 | args = parser.parse_args() 438 | 439 | if args.default: 440 | sys.stdout.write("[+] Executing the default Godefroid paper example\n") 441 | instruction_count, p = default_example() 442 | 443 | else: 444 | if not args.binary: 445 | sys.stdout.write("[!] Please specify a binary file to load\n") 446 | sys.exit(-1) 447 | 448 | if not os.path.exists(args.binary): 449 | sys.stdout.write(f"[!] Could not find file: {args.binary}\n") 450 | sys.exit(-1) 451 | 452 | if not args.entry: 453 | sys.stdout.write("[!] Please specify an entry point\n") 454 | sys.exit(-1) 455 | 456 | binary = args.binary 457 | entrypoint = args.entry 458 | 459 | icount_type = Icount.COUNTED 460 | max_inst = 0 461 | 462 | if args.infinite: 463 | sys.stdout.write( 464 | "[+] Running for an INFINITE amount of instructions (or until function return)\n" 465 | ) 466 | icount_type = Icount.INFINITE 467 | elif args.maxinst < 0: 468 | sys.stdout.write( 469 | f"[!] Max instruction count must be zero or more. Got {args.maxinst}\n" 470 | ) 471 | sys.exit(1) 472 | else: 473 | icount_type = Icount.COUNTED 474 | max_inst = args.maxinst 475 | sys.stdout.write( 476 | f"[+] Running for {max_inst} instructions (or function return)\n" 477 | ) 478 | 479 | result = run_on_binary( 480 | binary=binary, entry=entrypoint, icount_type=icount_type, maxinst=max_inst 481 | ) 482 | 483 | if result is None: 484 | sys.stdout.write(f"[!] Could not run on {binary} @ {entrypoint}\n") 485 | sys.exit(-1) 486 | 487 | instruction_count, p = result 488 | # Stats 489 | sys.stdout.write(f"[+] Executed {instruction_count} instructions\n") 490 | # Dump known inputs 491 | inputs = p.get_inputs() 492 | 493 | if len(inputs) > 0: 494 | sys.stdout.write("[+] Found the following inputs:\n") 495 | for (k, v) in inputs.items(): 496 | input_size, input_type, input_data = v 497 | sys.stdout.write(f"\t{k:08x} - {k+input_size:08x} [size: {input_size}]") 498 | if InputType.POINTER == input_type: 499 | sys.stdout.write(f" [POINTER TO: {input_data:08x}]") 500 | elif InputType.DATA == input_type: 501 | sys.stdout.write(" [DATA]") 502 | elif InputType.COMPUTED == input_type: 503 | sys.stdout.write(" [COMPUTED]") 504 | else: 505 | assert "Unknown input type" 506 | sys.stdout.write("\n") 507 | else: 508 | sys.stdout.write("[-] No inputs found\n") 509 | # Dump known outputs 510 | outputs = p.get_outputs() 511 | 512 | if len(outputs) > 0: 513 | sys.stdout.write("[+] Found the following outputs:\n") 514 | for (k, v) in outputs.items(): 515 | sys.stdout.write(f"\t{k:08x} - {k+v:08x}\n") 516 | else: 517 | sys.stdout.write("[-] No outputs found\n") 518 | -------------------------------------------------------------------------------- /godefroid/policies.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from enum import Enum 3 | import secrets 4 | import collections 5 | 6 | 7 | class DefaultMemoryPolicy: 8 | def __init__(self): 9 | pass 10 | 11 | def handle_store(self, addr): 12 | # Does this policy care about stores? 13 | return True 14 | 15 | def handle_load(self, addr): 16 | # Does this policy care about loads? 17 | return False 18 | 19 | def read_before_write(self, addr, size, data): 20 | # sys.stdout.write("Read before write of {:x} - {:x}\n".format(addr, addr + size)) 21 | return data 22 | 23 | def write_before_read(self, addr, size, data): 24 | # sys.stdout.write("Write before read of {:x} - {:x}\n".format(addr, addr + size)) 25 | return data 26 | 27 | 28 | class InputType(Enum): 29 | DATA = 0 30 | POINTER = 1 31 | COMPUTED = 2 32 | 33 | 34 | class InputMemoryPolicy: 35 | 36 | # TODO(artem): Make this a configurable value or based on address size 37 | POINTER_INCREMENT = int(0x1000 / 4) 38 | 39 | def __deepcopy__(self, memo): 40 | # Just create a new one. We do not care about 41 | # copying access ranges for what we're doing 42 | cp = InputMemoryPolicy( 43 | self._address_size * 8, 44 | (self._start, self._end), 45 | (self._pointers_start, self._pointers_end), 46 | ) 47 | return cp 48 | 49 | def __init__(self, address_size, argument_vas, pointer_vas): 50 | 51 | self._address_size = int(address_size / 8) 52 | self._known_inputs = {} 53 | self._known_outputs = {} 54 | 55 | # Where initial arguments will be found (i.e., the stack) 56 | self._start = argument_vas[0] 57 | self._end = argument_vas[1] 58 | assert self._start < self._end 59 | 60 | # Things that look like pointers all point *to* this range 61 | self._pointers_start = pointer_vas[0] 62 | self._pointers_end = pointer_vas[1] 63 | assert self._pointers_start < self._pointers_end 64 | 65 | self._pointer_watermark = self._pointers_start 66 | 67 | # maps address (such as stack) -> where it points to (in "heap") 68 | self._pointer_map = {} 69 | 70 | def add_output(self, addr, size): 71 | sys.stdout.write( 72 | f"!!! Manually adding output at {addr:08x} - {addr+size:08x}\n" 73 | ) 74 | self._known_outputs[addr] = size 75 | 76 | def pointer_to_bytes(self, ptr): 77 | return int(ptr).to_bytes(self._address_size, byteorder="little") 78 | 79 | def generate_pointer(self): 80 | 81 | # start at the current "high water mark" for input pointers 82 | new_ptr = self._pointer_watermark 83 | assert (new_ptr + self._address_size) < self._pointers_end 84 | # move watermark to next area, further down from here 85 | self._pointer_watermark += InputMemoryPolicy.POINTER_INCREMENT 86 | # TODO(artem): Handle the case where we run out of pointer space :) 87 | assert self._pointer_watermark < self._pointers_end 88 | sys.stdout.write( 89 | "Generating a pointer going to {:08x} in pointer space\n".format(new_ptr) 90 | ) 91 | 92 | return new_ptr 93 | 94 | def generate_random(self, size): 95 | # NOTE(artem): Consider a seeded random for reproducability 96 | return secrets.token_bytes(size) 97 | 98 | def handle_store(self, addr): 99 | if (self._start <= addr <= self._end) or ( 100 | self._pointers_start <= addr <= self._pointers_end 101 | ): 102 | # Does this policy care about stores? 103 | return True 104 | else: 105 | # This address is outside policy bounds 106 | return False 107 | 108 | def handle_load(self, addr): 109 | if (self._start <= addr <= self._end) or ( 110 | self._pointers_start <= addr <= self._pointers_end 111 | ): 112 | # Does this policy care about loads? 113 | return True 114 | else: 115 | # This address is outside policy bounds 116 | return False 117 | 118 | def read_before_write(self, addr, size, data): 119 | sys.stdout.write(f"Read-before-write of {size} bytes\n") 120 | sys.stdout.write(f" at {addr:08x} [{addr:08x} - {addr+size:08x}]\n") 121 | new_data = data 122 | # TODO(artem): Check if this address+size has been previously read 123 | if self._address_size == size: 124 | # when reading a pointer size, at first, always assume the value is a pointer 125 | # and generate a pointer into pointer space aka ("heap") 126 | ptr = self.generate_pointer() 127 | self._pointer_map[addr] = ptr 128 | new_data = self.pointer_to_bytes(ptr) 129 | else: 130 | # When reading a non-pointer size, return a random value 131 | new_data = self.generate_random(size) 132 | 133 | # Mark the memory cell containing this value as used 134 | self._known_inputs[addr] = size 135 | 136 | assert len(data) == len(new_data) 137 | 138 | return new_data 139 | 140 | def write_before_read(self, addr, size, data): 141 | sys.stdout.write(f"Write-before-read of {size} bytes") 142 | sys.stdout.write(f" at {addr:08x} [{addr:08x} - {addr+size:08x}]\n") 143 | 144 | self._known_outputs[addr] = size 145 | 146 | return data 147 | 148 | def _make_itype(self, addr, size): 149 | ptr = self.get_pointer(addr) 150 | if ptr is not None: 151 | return (size, InputType.POINTER, ptr) 152 | elif size != 0: 153 | # TODO(artem): Keep track of initial values returned 154 | return (size, InputType.DATA, 0) 155 | elif size == 0: 156 | return (size, InputType.COMPUTED, 0) 157 | 158 | def get_outputs(self): 159 | # a copy of get_inputs that doesn't care about 160 | # the kind of output, at least for now 161 | output_addrs = sorted(self._known_outputs.keys()) 162 | 163 | merged_addrs = collections.OrderedDict() 164 | 165 | # no outputs = blank dict 166 | if 0 == len(output_addrs): 167 | return merged_addrs 168 | 169 | # process the base case of the first input 170 | entry = output_addrs[0] 171 | merged_addrs[entry] = self._known_outputs[entry] 172 | 173 | watermark = entry + self._known_outputs[entry] 174 | 175 | # start merging overlapping input areas 176 | for addr in output_addrs[1:]: 177 | write_size = self._known_outputs[addr] 178 | 179 | if addr >= watermark: 180 | # Next output address is greater than addr+size of previous 181 | # This means a new output "area" was found 182 | merged_addrs[addr] = write_size 183 | watermark = addr + write_size 184 | entry = addr 185 | else: 186 | # This output address at least partially overlaps 187 | # the previous output address. Merge them 188 | if (addr + write_size) > watermark: 189 | new_watermark = addr + write_size 190 | merged_addrs[entry] = new_watermark - entry 191 | watermark = new_watermark 192 | # entry not updated since we extended the area 193 | else: 194 | # This entry is entirely subsumed by the previous output area 195 | pass 196 | 197 | return merged_addrs 198 | 199 | def get_inputs(self): 200 | # loop through inputs. Get ranges/bytes 201 | input_addrs = sorted(self._known_inputs.keys()) 202 | 203 | # return an ordered dict of 204 | # address : size of input area 205 | merged_addrs = collections.OrderedDict() 206 | 207 | # no inputs = blank dict 208 | if 0 == len(input_addrs): 209 | return merged_addrs 210 | 211 | # process the base case of the first input 212 | entry = input_addrs[0] 213 | merged_addrs[entry] = self._make_itype(entry, self._known_inputs[entry]) 214 | watermark = entry + self._known_inputs[entry] 215 | 216 | # start merging overlapping input areas 217 | for addr in input_addrs[1:]: 218 | read_size = self._known_inputs[addr] 219 | 220 | if addr >= watermark: 221 | # Next input address is greater than addr+size of previous 222 | # This means a new input "area" was found 223 | merged_addrs[addr] = self._make_itype(addr, read_size) 224 | watermark = addr + read_size 225 | entry = addr 226 | else: 227 | # This input address at least partially overlaps 228 | # the previous input address. Merge them 229 | if (addr + read_size) > watermark: 230 | new_watermark = addr + read_size 231 | merged_addrs[entry] = self._make_itype(addr, new_watermark - entry) 232 | watermark = new_watermark 233 | # entry not updated since we extended the area 234 | else: 235 | # This entry is entirely subsumed by the previous input area 236 | pass 237 | 238 | return merged_addrs 239 | 240 | def get_pointer(self, addr): 241 | # Return address it points to, or None if not a pointer 242 | return self._pointer_map.get(addr, None) 243 | 244 | def in_input_range(self, addr): 245 | 246 | # is it a known input? 247 | if addr in self._known_inputs: 248 | return True 249 | 250 | # is it a pointer to the input heap? 251 | if self._pointers_start <= addr < self._pointers_end: 252 | 253 | if addr > self._pointer_watermark: 254 | self._pointer_watermark += InputMemoryPolicy.POINTER_INCREMENT 255 | assert self._pointer_watermark < self._pointers_end 256 | 257 | return True 258 | 259 | # is it on the stack? 260 | if self._start <= addr < self._end: 261 | 262 | return True 263 | 264 | # Its probably not an input 265 | return False 266 | 267 | def handle_compute(self, result, base, scale, index, disp, size, hint): 268 | 269 | parts = (base, scale, index, disp) 270 | 271 | if self.in_input_range(result): 272 | # Computed address is an input range, mark it as input 273 | self._known_inputs[result] = size 274 | else: 275 | # TODO(artem): This code may not be necessary 276 | # Is the address computed from an input address? 277 | for p in parts: 278 | # NOTE(artem): the check for input address zero is here purely for sanity checking 279 | if p in self._known_inputs and p != 0: 280 | sys.stdout.write( 281 | f"Input Address: {p:08x} used to compute {result:08x}\n" 282 | ) 283 | sys.stdout.write(f"\tAdding {result:08x} to inputs") 284 | 285 | # Add a new 'computed' input address 286 | self._known_inputs[result] = size 287 | sys.stdout.write( 288 | "!!! Failed in_input_range but computed from known input\n" 289 | ) 290 | break 291 | 292 | return result 293 | -------------------------------------------------------------------------------- /godefroid/policy_map.py: -------------------------------------------------------------------------------- 1 | from flag_map import FlaggedMemoryMap, PolicyFlags 2 | import sys 3 | import copy 4 | 5 | 6 | class PolicyMemoryMap(FlaggedMemoryMap): 7 | def __init__(self, ops, base, limit, access_flags, policy, mapname=None): 8 | super(PolicyMemoryMap, self).__init__(ops, base, limit, access_flags, mapname) 9 | self._data = [0] * (limit - base) 10 | self._policy = policy 11 | self._access_map = {} 12 | 13 | def __deepcopy__(self, memo): 14 | # We do not want to copy access maps 15 | # and want to do a shallow copy of _data 16 | cp = PolicyMemoryMap( 17 | self._ops, 18 | self._base, 19 | self._limit, 20 | self._access_flags, 21 | copy.deepcopy(self._policy, memo), 22 | self.get_name(), 23 | ) 24 | cp._data = self._data[:] 25 | 26 | return cp 27 | 28 | def attach_policy(self, policy): 29 | # TODO(artem): Determine if we need to have multiple policies per map 30 | self._policy = policy 31 | 32 | def _load_policy(self, addr, size): 33 | 34 | # this policy doesn't care about memory reads 35 | if not self._policy.handle_load(addr): 36 | return 37 | 38 | flag_list = [ 39 | self._access_map.get(i, PolicyFlags.no_flags) 40 | for i in range(addr, addr + size) 41 | ] 42 | # read before write 43 | r_before_w = map(lambda x: PolicyFlags.Written not in x, flag_list) 44 | if any(r_before_w): 45 | # callback to read_before_write in Policy 46 | start = addr - self._base 47 | end = start + size 48 | 49 | # TODO(artem): pass individual bytes that meet policy? right now its 50 | # called if any byte hits it 51 | new_data = self._policy.read_before_write(addr, size, self._data[start:end]) 52 | 53 | assert len(new_data) == size 54 | self._data[start:end] = new_data 55 | 56 | # Mark all data as read 57 | for i in range(size): 58 | flag_list[i] |= PolicyFlags.Read 59 | self._access_map[addr + i] = flag_list[i] 60 | 61 | def _store_policy(self, addr, size): 62 | 63 | # this policy doesn't care about memory writes 64 | if not self._policy.handle_store(addr): 65 | # sys.stdout.write(f"!!! Addr not in policy: {addr:08x}\n") 66 | return 67 | 68 | flag_list = [ 69 | self._access_map.get(i, PolicyFlags.no_flags) 70 | for i in range(addr, addr + size) 71 | ] 72 | # write before read 73 | w_before_r = map(lambda x: PolicyFlags.Read not in x, flag_list) 74 | if any(w_before_r): 75 | # callback to write_before_read in Policy 76 | start = addr - self._base 77 | end = start + size 78 | 79 | # TODO(artem): pass individual bytes that meet policy? right now its 80 | # called if any byte hits it 81 | new_data = self._policy.write_before_read(addr, size, self._data[start:end]) 82 | 83 | assert len(new_data) == size 84 | self._data[start:end] = new_data 85 | else: 86 | # For now, assume we care a about *all* writes except those that 87 | # this mapping isn't set up to handle 88 | self._policy.add_output(addr, size) 89 | 90 | # Mark all data as written 91 | for i in range(size): 92 | flag_list[i] |= PolicyFlags.Written 93 | self._access_map[addr + i] = flag_list[i] 94 | 95 | def _load_unit(self, addr, size): 96 | # sys.stdout.write("Load size({:d}) at {:x}\n".format(size, addr)) 97 | self._load_policy(addr, size) 98 | offset = addr - self._base 99 | return self._ops.convert_to_byte_string(self._data[offset : (offset + size)]) 100 | 101 | def load_byte(self, addr): 102 | return self._load_unit(addr, 1) 103 | 104 | def load_word(self, addr): 105 | return self._load_unit(addr, 2) 106 | 107 | def load_dword(self, addr): 108 | return self._load_unit(addr, 4) 109 | 110 | def load_qword(self, addr): 111 | return self._load_unit(addr, 8) 112 | 113 | def load_bytes(self, addr, num_bytes): 114 | self._load_policy(addr, num_bytes) 115 | offset = addr - self._base 116 | return self._data[offset : (offset + num_bytes)] 117 | 118 | def _store_unit(self, addr, size, data): 119 | self._store_policy(addr, size) 120 | data = self._ops.convert_to_byte_string(data) 121 | self._store_bytes(addr, data[:size]) 122 | 123 | def store_byte(self, addr, data): 124 | return self._store_unit(addr, 1, data) 125 | 126 | def store_word(self, addr, data): 127 | return self._store_unit(addr, 2, data) 128 | 129 | def store_dword(self, addr, data): 130 | return self._store_unit(addr, 4, data) 131 | 132 | def store_qword(self, addr, data): 133 | return self._store_unit(addr, 8, data) 134 | 135 | def _store_bytes(self, addr, data): 136 | offset = addr - self._base 137 | for b in data: 138 | if isinstance(b, str): 139 | b = ord(b) 140 | self._data[offset] = b 141 | offset += 1 142 | 143 | def store_bytes_raw(self, addr, data): 144 | """Does a store bytes without policy checks""" 145 | return self._store_bytes(addr, data) 146 | 147 | def store_bytes(self, addr, data): 148 | self._store_policy(addr, len(data)) 149 | return self._store_bytes(addr, data) 150 | -------------------------------------------------------------------------------- /godefroid/requirements.txt: -------------------------------------------------------------------------------- 1 | py-flags==1.1.4 2 | cle==8.20.1.7 3 | -------------------------------------------------------------------------------- /microx/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10 FATAL_ERROR) 2 | 3 | project(microx) 4 | 5 | find_library( 6 | LIBXED 7 | NAMES xed 8 | PATHS ENV "XED_DIR" 9 | PATH_SUFFIXES "lib" REQUIRED 10 | ) 11 | 12 | add_library("${PROJECT_NAME}" Executor.cpp) 13 | target_include_directories( 14 | "${PROJECT_NAME}" PUBLIC $ 15 | $ 16 | ) 17 | set_target_properties("${PROJECT_NAME}" PROPERTIES PUBLIC_HEADER "include/microx/Executor.h") 18 | 19 | target_link_libraries("${PROJECT_NAME}" PRIVATE "${LIBXED}") 20 | target_include_directories("${PROJECT_NAME}" SYSTEM PRIVATE "$ENV{XED_DIR}/include") 21 | 22 | install( 23 | TARGETS "${PROJECT_NAME}" 24 | EXPORT microx-config 25 | RUNTIME DESTINATION "bin" 26 | LIBRARY DESTINATION "lib" 27 | ARCHIVE DESTINATION "lib" 28 | PUBLIC_HEADER DESTINATION "include/microx" 29 | ) 30 | export( 31 | TARGETS "${PROJECT_NAME}" 32 | NAMESPACE microx:: 33 | FILE "${CMAKE_CURRENT_BINARY_DIR}/microx-config.cmake" 34 | ) 35 | install( 36 | EXPORT microx-config 37 | DESTINATION "lib/cmake/microx" 38 | NAMESPACE microx:: 39 | EXPORT_LINK_INTERFACE_LIBRARIES 40 | ) 41 | -------------------------------------------------------------------------------- /microx/Executor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Trail of Bits, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #define _XOPEN_SOURCE 1 18 | 19 | #include 20 | #include 21 | 22 | #ifdef _WIN32 23 | #include 24 | #else 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #endif //_WIN32 31 | 32 | #include "XED.h" 33 | #include "microx/Executor.h" 34 | 35 | // TODO(ww): These headers really shouldn't be included here at all; 36 | // the hack in Executor::Executor with PyErr_Occurred should be fixed 37 | // at some point. 38 | #ifdef PYTHON_BINDINGS 39 | #pragma clang diagnostic push 40 | #pragma clang diagnostic ignored "-Wdeprecated-register" 41 | #define PY_SSIZE_T_CLEAN 42 | #include 43 | #include 44 | #pragma clang diagnostic pop 45 | #endif 46 | 47 | namespace microx { 48 | namespace { 49 | 50 | enum : size_t { kPageSize = 4096 }; 51 | 52 | // A memory value that is read by the executor. 53 | struct Memory final { 54 | bool present; 55 | bool write_back; 56 | xed_operand_enum_t op_name; 57 | xed_reg_enum_t segment_reg; 58 | xed_reg_enum_t base_reg; 59 | xed_reg_enum_t index_reg; 60 | 61 | uintptr_t base; 62 | uintptr_t index; 63 | uintptr_t scale; 64 | uintptr_t displacement; 65 | 66 | uintptr_t address; 67 | size_t size; // In bits. 68 | xed_memop_t *mem_op; 69 | Data data; 70 | }; 71 | 72 | union alignas(8) Flags final { 73 | uint64_t flat; 74 | struct { 75 | uint32_t cf : 1; // bit 0. 76 | uint32_t must_be_1 : 1; 77 | uint32_t pf : 1; 78 | uint32_t must_be_0a : 1; 79 | 80 | uint32_t af : 1; // bit 4. 81 | uint32_t must_be_0b : 1; 82 | uint32_t zf : 1; 83 | uint32_t sf : 1; 84 | 85 | uint32_t tf : 1; // bit 8. 86 | uint32_t _if : 1; // underscore to avoid token clash. 87 | uint32_t df : 1; 88 | uint32_t of : 1; 89 | 90 | uint32_t iopl : 2; // A 2-bit field, bits 12-13. 91 | uint32_t nt : 1; 92 | uint32_t must_be_0c : 1; 93 | 94 | uint32_t rf : 1; // bit 16. 95 | uint32_t vm : 1; 96 | uint32_t ac : 1; // Alignment check. 97 | uint32_t vif : 1; 98 | 99 | uint32_t vip : 1; // bit 20. 100 | uint32_t id : 1; // bit 21. 101 | uint32_t reserved_eflags : 10; // bits 22-31. 102 | uint32_t reserved_rflags; // bits 32-63. 103 | } __attribute__((packed)); 104 | } __attribute__((packed)); 105 | 106 | static_assert(8 == sizeof(Flags), "Invalid structure packing of `Flags`."); 107 | 108 | // Scoped access to a mutex. 109 | #ifdef _WIN32 110 | class LockGuard { 111 | public: 112 | LockGuard(CRITICAL_SECTION &lock_) : lock(&lock_) { 113 | EnterCriticalSection(lock); 114 | } 115 | ~LockGuard(void) { LeaveCriticalSection(lock); } 116 | LockGuard(const LockGuard &) = delete; 117 | LockGuard &operator=(const LockGuard &) = delete; 118 | 119 | private: 120 | CRITICAL_SECTION *lock; 121 | }; 122 | #else 123 | class LockGuard { 124 | public: 125 | LockGuard(pthread_mutex_t &lock_) : lock(&lock_) { pthread_mutex_lock(lock); } 126 | ~LockGuard(void) { pthread_mutex_unlock(lock); } 127 | LockGuard(const LockGuard &) = delete; 128 | LockGuard &operator=(const LockGuard &) = delete; 129 | 130 | private: 131 | pthread_mutex_t *lock; 132 | }; 133 | #endif //_WIN32 134 | 135 | // 32-bit decoded state. 136 | static const xed_state_t kXEDState32 = {XED_MACHINE_MODE_LONG_COMPAT_32, 137 | XED_ADDRESS_WIDTH_32b}; 138 | 139 | // 64-bit decoded state. 140 | static const xed_state_t kXEDState64 = {XED_MACHINE_MODE_LONG_64, 141 | XED_ADDRESS_WIDTH_64b}; 142 | 143 | // Decoded instructions. 144 | static xed_decoded_inst_t gXedd_; 145 | static xed_decoded_inst_t *const gXedd = &gXedd_; 146 | 147 | // High-level encoder info for the decoded instruction to be re-emitted. 148 | static xed_encoder_instruction_t gEmu; 149 | static unsigned gEmuSize = 0; 150 | 151 | // Region of executable code. 152 | static uint8_t gExecArea_[kPageSize * 2] = {0xCC}; 153 | static uint8_t *gExecArea = nullptr; 154 | 155 | // Storage for register data. 156 | static std::bitset gUsedRegs; 157 | static std::bitset gModifiedRegs; 158 | static std::bitset gStoreRegs; 159 | static Data gRegs[XED_REG_LAST] = {{0}}; 160 | static xed_reg_enum_t gStackPtrAlias = XED_REG_INVALID; 161 | 162 | static bool gUsesFPU = false; 163 | static bool gUsesMMX = false; 164 | FPU gFPU, gNativeFPU; 165 | 166 | #ifdef _WIN32 167 | static DWORD gExceptionCode = 0; 168 | #else 169 | static int gSignal = 0; 170 | static struct sigaction gSignalHandler; 171 | static struct sigaction gSIGILL; 172 | static struct sigaction gSIGSEGV; 173 | static struct sigaction gSIGBUS; 174 | static struct sigaction gSIGFPE; 175 | static sigjmp_buf gRecoveryTarget; 176 | #endif //_WIN32 177 | 178 | // Flags that must be written back. 179 | static Flags gWriteBackFlags; 180 | 181 | // Memory read from the executor. 182 | static Memory gMemory[2]; 183 | 184 | // Guards accesses to globals. Using pthreads for portability, so that 185 | // libc++ / libstdc++ doesn't need to be linked in (otherwise `std::mutex` 186 | // would be nicer). 187 | #ifdef _WIN32 188 | static CRITICAL_SECTION gExecutorLock; 189 | static bool gExecutorLockInitialized = [] { 190 | InitializeCriticalSection(&gExecutorLock); 191 | return true; 192 | }(); 193 | #else 194 | static pthread_mutex_t gExecutorLock = PTHREAD_MUTEX_INITIALIZER; 195 | #endif //_WIN32 196 | 197 | // Returns true if the executor is initialized. 198 | static bool gIsInitialized = false; 199 | 200 | // Decode an instruction. 201 | static bool DecodeInstruction(const uint8_t *bytes, size_t num_bytes, 202 | size_t addr_size) { 203 | auto dstate = 32 == addr_size ? &kXEDState32 : &kXEDState64; 204 | xed_decoded_inst_zero_set_mode(gXedd, dstate); 205 | xed_decoded_inst_set_input_chip(gXedd, XED_CHIP_ALL); 206 | if (XED_ERROR_NONE == xed_decode(gXedd, bytes, num_bytes)) { 207 | memset(&gEmu, 0, sizeof(gEmu)); 208 | 209 | // Good defaults, will fixup special cases later. 210 | gEmuSize = 0; 211 | gEmu.iclass = xed_decoded_inst_get_iclass(gXedd); 212 | gEmu.effective_address_width = 64; 213 | gEmu.effective_operand_width = xed_decoded_inst_get_operand_width(gXedd); 214 | gEmu.mode = kXEDState64; 215 | 216 | return true; 217 | } else { 218 | return false; 219 | } 220 | } 221 | 222 | // Return the widest version of the register that respects the address size 223 | // and presence of AVX(512). 224 | static xed_reg_enum_t WidestRegister(const Executor *executor, 225 | xed_reg_enum_t reg) { 226 | xed_reg_enum_t wreg; 227 | wreg = (64 == executor->addr_size) 228 | ? xed_get_largest_enclosing_register(reg) 229 | : xed_get_largest_enclosing_register32(reg); 230 | if (wreg == XED_REG_INVALID) { 231 | return reg; 232 | } 233 | 234 | // If not using AVX512, then downgrade a ZMM register to a YMM register. 235 | if (XED_REG_ZMM_FIRST <= wreg && wreg <= XED_REG_ZMM_LAST) { 236 | if (!executor->has_avx512) { 237 | wreg = static_cast((wreg - XED_REG_ZMM_FIRST) + 238 | XED_REG_YMM_FIRST); 239 | } 240 | } 241 | 242 | // If not using AVX, then downgrade a YMM register to an XMM register. 243 | if (XED_REG_YMM_FIRST <= wreg && wreg <= XED_REG_YMM_LAST) { 244 | if (!executor->has_avx) { 245 | wreg = static_cast((wreg - XED_REG_YMM_FIRST) + 246 | XED_REG_XMM_FIRST); 247 | } 248 | } 249 | 250 | return wreg; 251 | } 252 | 253 | // Read in a register from the executor. The data of the register is stored 254 | // into the largest enclosing register of any arch, but we identify the 255 | // register with the widest enclosing register that respects the features 256 | // (avx, avx512) and the address size (32, 64). 257 | static bool ReadRegister(const Executor *executor, xed_reg_enum_t reg, 258 | RegRequestHint hint) { 259 | if (XED_REG_INVALID == reg) { 260 | return true; 261 | } 262 | 263 | const auto reg_class = xed_reg_class(reg); 264 | if (XED_REG_CLASS_X87 == reg_class || XED_REG_CLASS_PSEUDOX87 == reg_class) { 265 | gUsesFPU = true; 266 | return true; 267 | 268 | // Mark the FPU as being used; we'll merge/split the MMX state manually. 269 | } else if (XED_REG_CLASS_MMX == reg_class) { 270 | gUsesFPU = true; 271 | gUsesMMX = true; 272 | } 273 | 274 | // Stack operations. 275 | if (XED_REG_STACKPUSH == reg || XED_REG_STACKPOP == reg) { 276 | reg = XED_REG_ESP; 277 | hint = RegRequestHint::kWriteBack; 278 | } 279 | 280 | // If this register will be modified then mark it as write-back so that 281 | // later we can only overwrite the necessary registers. 282 | auto widest_reg = WidestRegister(executor, reg); 283 | if (RegRequestHint::kWriteBack == hint) { 284 | gModifiedRegs.set(widest_reg); 285 | } 286 | 287 | // Don't request this register if we already got it. 288 | if (gUsedRegs.test(widest_reg)) { 289 | return true; 290 | } 291 | 292 | auto name = xed_reg_enum_t2str(widest_reg); 293 | auto size = xed_get_register_width_bits64(widest_reg); 294 | auto store_reg = xed_get_largest_enclosing_register(reg); 295 | auto &data = gRegs[store_reg]; 296 | memset(data.bytes, 0, size / 8); 297 | auto read = executor->ReadReg(name, size, hint, data); 298 | gUsedRegs.set(widest_reg); 299 | gStoreRegs.set(store_reg); 300 | 301 | return read; 302 | } 303 | 304 | // Read in register values associated with memory operands. 305 | static bool ReadRegistersMemOp(const Executor *executor, unsigned op_num) { 306 | auto base_reg = xed_decoded_inst_get_base_reg(gXedd, op_num); 307 | auto index_reg = xed_decoded_inst_get_index_reg(gXedd, op_num); 308 | return ReadRegister(executor, base_reg, RegRequestHint::kMemoryBaseAddress) && 309 | ReadRegister(executor, index_reg, RegRequestHint::kMemoryIndexAddress); 310 | } 311 | 312 | // Read in registers from the executor. This opportunistically read in written- 313 | // only instructions for completeness/simplicity, even though that introduces 314 | // a false dependency. 315 | static bool ReadRegisters(const Executor *executor) { 316 | auto num_operands = xed_decoded_inst_noperands(gXedd); 317 | auto xedi = xed_decoded_inst_inst(gXedd); 318 | 319 | // Start by going and getting registers involved in memory access. 320 | for (auto i = 0U, mem_index = 0U; i < num_operands; ++i) { 321 | auto xedo = xed_inst_operand(xedi, i); 322 | switch (auto op_name = xed_operand_name(xedo)) { 323 | case XED_OPERAND_AGEN: 324 | case XED_OPERAND_MEM0: 325 | case XED_OPERAND_MEM1: 326 | if (!ReadRegistersMemOp(executor, mem_index++)) { 327 | return false; 328 | } 329 | break; 330 | default: 331 | break; 332 | } 333 | } 334 | 335 | // Then get the registers associated with reads. 336 | for (auto i = 0U; i < num_operands; ++i) { 337 | auto xedo = xed_inst_operand(xedi, i); 338 | if (xed_operand_written(xedo)) { 339 | continue; 340 | } 341 | switch (auto op_name = xed_operand_name(xedo)) { 342 | case XED_OPERAND_REG: 343 | case XED_OPERAND_REG0: 344 | case XED_OPERAND_REG1: 345 | case XED_OPERAND_REG2: 346 | case XED_OPERAND_REG3: 347 | case XED_OPERAND_REG4: 348 | case XED_OPERAND_REG5: 349 | case XED_OPERAND_REG6: 350 | case XED_OPERAND_REG7: 351 | case XED_OPERAND_REG8: 352 | if (auto reg = xed_decoded_inst_get_reg(gXedd, op_name)) { 353 | if (!ReadRegister(executor, reg, RegRequestHint::kGeneral)) { 354 | return false; 355 | } 356 | } 357 | break; 358 | default: 359 | break; 360 | } 361 | } 362 | 363 | // Finally get the written registers. 364 | for (auto i = 0U; i < num_operands; ++i) { 365 | auto xedo = xed_inst_operand(xedi, i); 366 | if (!xed_operand_written(xedo)) { 367 | continue; 368 | } 369 | switch (auto op_name = xed_operand_name(xedo)) { 370 | case XED_OPERAND_REG: 371 | case XED_OPERAND_REG0: 372 | case XED_OPERAND_REG1: 373 | case XED_OPERAND_REG2: 374 | case XED_OPERAND_REG3: 375 | case XED_OPERAND_REG4: 376 | case XED_OPERAND_REG5: 377 | case XED_OPERAND_REG6: 378 | case XED_OPERAND_REG7: 379 | case XED_OPERAND_REG8: 380 | if (auto reg = xed_decoded_inst_get_reg(gXedd, op_name)) { 381 | if (!ReadRegister(executor, reg, RegRequestHint::kWriteBack)) { 382 | return false; 383 | } 384 | } 385 | break; 386 | default: 387 | break; 388 | } 389 | } 390 | 391 | return true; 392 | } 393 | 394 | // Cast the value of a specific register to a type. 395 | template 396 | static T ReadValue(xed_reg_enum_t reg) { 397 | auto reg_store = xed_get_largest_enclosing_register(reg); 398 | return *reinterpret_cast(gRegs[reg_store].bytes); 399 | } 400 | 401 | // Read the value of a general-purpose register. This handles things like 402 | // reading from AH, BH, CH, or DH. 403 | uintptr_t ReadGPR(xed_reg_enum_t reg) { 404 | auto size = xed_get_register_width_bits64(reg); 405 | uintptr_t shift = 0; 406 | if (XED_REG_GPR8h_FIRST <= reg && reg <= XED_REG_GPR8h_LAST) { 407 | shift = 8; 408 | size = 16; 409 | } 410 | switch (size) { 411 | case 64: 412 | return ReadValue(reg) >> shift; 413 | case 32: 414 | return ReadValue(reg) >> shift; 415 | case 16: 416 | return ReadValue(reg) >> shift; 417 | case 8: 418 | return ReadValue(reg) >> shift; 419 | default: 420 | return 0; 421 | } 422 | } 423 | 424 | // Cast the value of a specific register to a type. 425 | template 426 | static void WriteValue(xed_reg_enum_t reg, uintptr_t val) { 427 | auto reg_store = xed_get_largest_enclosing_register(reg); 428 | *reinterpret_cast(gRegs[reg_store].bytes) = static_cast(val); 429 | } 430 | 431 | // Write a value to a general-purpose register. This handles things like 432 | // reading from AH, BH, CH, or DH. 433 | void WriteGPR(xed_reg_enum_t reg, uintptr_t val) { 434 | switch (xed_get_register_width_bits64(reg)) { 435 | case 64: 436 | WriteValue(reg, val); 437 | break; 438 | case 32: 439 | WriteValue(reg, static_cast(val)); // Zero-extends. 440 | break; 441 | case 16: 442 | WriteValue(reg, val); 443 | break; 444 | case 8: 445 | if (XED_REG_GPR8h_FIRST <= reg && reg <= XED_REG_GPR8h_LAST) { 446 | auto whole_val = ReadValue(reg); 447 | whole_val &= ~0xFF00; 448 | whole_val |= ((val & 0xFFU) << 8); 449 | WriteValue(reg, whole_val); 450 | } else { 451 | WriteValue(reg, val); 452 | } 453 | break; 454 | default: 455 | return; 456 | } 457 | } 458 | 459 | // Write the registers back to the executor. We only write back ones that 460 | // may have been modified (W, *RW, *CW). 461 | static bool WriteRegisters(const Executor *executor) { 462 | if (XED_REG_INVALID != gStackPtrAlias) { 463 | WriteGPR(XED_REG_RSP, ReadGPR(gStackPtrAlias)); 464 | gStackPtrAlias = XED_REG_INVALID; 465 | } 466 | 467 | xed_reg_enum_t pc_reg = XED_REG_INVALID; 468 | for (auto i = 0UL; i < gUsedRegs.size(); ++i) { 469 | const auto reg = static_cast(i); 470 | if (!gUsedRegs[i]) { 471 | continue; 472 | } 473 | if (i == XED_REG_EIP || i == XED_REG_RIP) { 474 | pc_reg = reg; 475 | } else if (gModifiedRegs.test(i)) { 476 | const auto name = xed_reg_enum_t2str(reg); 477 | const auto size = xed_get_register_width_bits64(reg); 478 | const auto store_reg = xed_get_largest_enclosing_register(reg); 479 | if (!executor->WriteReg(name, size, gRegs[store_reg])) { 480 | return false; 481 | } 482 | } 483 | } 484 | 485 | // Make sure the last written register is the program counter. 486 | if (XED_REG_INVALID != pc_reg) { 487 | const auto name = xed_reg_enum_t2str(pc_reg); 488 | const auto size = xed_get_register_width_bits64(pc_reg); 489 | const auto store_reg = xed_get_largest_enclosing_register(pc_reg); 490 | if (!executor->WriteReg(name, size, gRegs[store_reg])) { 491 | return false; 492 | } 493 | } 494 | 495 | return true; 496 | } 497 | 498 | // Get the bit offset for a `BT*` instruction. 499 | // 500 | // Note: This function is destructive insofar as it goes and modifies the 501 | // reg/immediate source operands to "relative" them to the bit to the 502 | // memory cell that should be read. 503 | static uintptr_t GetBitOpByteOffset(void) { 504 | const auto iform = xed_decoded_inst_get_iform_enum(gXedd); 505 | const auto bit_width = gEmu.effective_operand_width; 506 | switch (iform) { 507 | case XED_IFORM_BT_MEMv_IMMb: 508 | case XED_IFORM_BTS_MEMv_IMMb: 509 | case XED_IFORM_BTS_LOCK_MEMv_IMMb: 510 | case XED_IFORM_BTC_LOCK_MEMv_IMMb: 511 | case XED_IFORM_BTR_LOCK_MEMv_IMMb: { 512 | auto bit_offset = xed_decoded_inst_get_unsigned_immediate(gXedd); 513 | xed_decoded_inst_set_immediate_unsigned( 514 | gXedd, bit_offset % bit_width, 515 | xed_decoded_inst_get_immediate_width(gXedd)); 516 | return 8 * (bit_offset / bit_width); 517 | } 518 | 519 | case XED_IFORM_BT_MEMv_GPRv: 520 | case XED_IFORM_BTS_MEMv_GPRv: 521 | case XED_IFORM_BTS_LOCK_MEMv_GPRv: 522 | case XED_IFORM_BTC_MEMv_IMMb: 523 | case XED_IFORM_BTC_MEMv_GPRv: 524 | case XED_IFORM_BTC_LOCK_MEMv_GPRv: 525 | case XED_IFORM_BTR_MEMv_IMMb: 526 | case XED_IFORM_BTR_MEMv_GPRv: 527 | case XED_IFORM_BTR_LOCK_MEMv_GPRv: { 528 | auto reg0 = xed_decoded_inst_get_reg(gXedd, XED_OPERAND_REG0); 529 | auto bit_offset = ReadGPR(reg0); 530 | WriteGPR(reg0, bit_offset % bit_width); 531 | return 8 * (bit_offset / bit_width); 532 | } 533 | 534 | default: 535 | return 0; 536 | } 537 | } 538 | 539 | // Compute a memory address and then ask the executor to read the memory at 540 | // that address. 541 | static bool ReadMemory(const Executor *executor, unsigned op_num, 542 | unsigned mem_index) { 543 | const auto iform = xed_decoded_inst_get_iform_enum(gXedd); 544 | const auto xedi = xed_decoded_inst_inst(gXedd); 545 | const auto xedo = xed_inst_operand(xedi, op_num); 546 | auto &mem = gMemory[mem_index]; 547 | 548 | mem.present = true; 549 | mem.write_back = xed_operand_written(xedo); 550 | mem.op_name = xed_operand_name(xedo); 551 | mem.segment_reg = xed_decoded_inst_get_seg_reg(gXedd, mem_index); 552 | mem.base_reg = xed_decoded_inst_get_base_reg(gXedd, mem_index); 553 | mem.index_reg = xed_decoded_inst_get_index_reg(gXedd, mem_index); 554 | mem.base = ReadGPR(mem.base_reg); 555 | 556 | // Deduce the implicit segment register. 557 | if (XED_REG_INVALID == mem.segment_reg) { 558 | mem.segment_reg = XED_REG_DS; 559 | if (XED_REG_RSP == xed_get_largest_enclosing_register(mem.base_reg) || 560 | XED_REG_RBP == xed_get_largest_enclosing_register(mem.base_reg)) { 561 | mem.segment_reg = XED_REG_SS; 562 | } 563 | } 564 | 565 | // PC-relative memory accesses are relative to the next PC. 566 | if (XED_REG_EIP == mem.base_reg || XED_REG_RIP == mem.base_reg) { 567 | mem.base += xed_decoded_inst_get_length(gXedd); 568 | } 569 | 570 | mem.index = ReadGPR(mem.index_reg); 571 | mem.scale = xed_decoded_inst_get_scale(gXedd, mem_index); 572 | mem.displacement = static_cast( 573 | xed_decoded_inst_get_memory_displacement(gXedd, mem_index)); 574 | 575 | // Adjust the displacement for this memory operand so that we're always 576 | // dealing with the correct memory address. 577 | const auto op_size_bytes = gEmu.effective_operand_width / 8; 578 | if (0 == mem_index) { 579 | switch (iform) { 580 | // For these, the memop is `[RSP]`, not `[RSP-N]` (which is what is 581 | // actually modified), so adjust accordingly. 582 | case XED_IFORM_CALL_NEAR_RELBRz: 583 | case XED_IFORM_CALL_NEAR_RELBRd: 584 | case XED_IFORM_CALL_NEAR_GPRv: 585 | case XED_IFORM_PUSH_GPRv_FFr6: 586 | case XED_IFORM_PUSH_GPRv_50: 587 | case XED_IFORM_PUSH_IMMz: 588 | case XED_IFORM_PUSH_IMMb: 589 | case XED_IFORM_PUSHF: 590 | case XED_IFORM_PUSHFD: 591 | case XED_IFORM_PUSHFQ: 592 | case XED_IFORM_PUSH_ES: 593 | case XED_IFORM_PUSH_CS: 594 | case XED_IFORM_PUSH_SS: 595 | case XED_IFORM_PUSH_DS: 596 | case XED_IFORM_PUSH_FS: 597 | case XED_IFORM_PUSH_GS: 598 | mem.displacement -= op_size_bytes; 599 | break; 600 | 601 | // Special case where, if the memory address being read by the POP uses 602 | // the stack pointer as the base register, then the stack pointer used 603 | // will be that *after* the POP happens. 604 | case XED_IFORM_POP_MEMv: 605 | if (XED_REG_RSP == xed_get_largest_enclosing_register(mem.base_reg)) { 606 | mem.displacement += op_size_bytes; 607 | } 608 | break; 609 | 610 | // In the case of `BT*` instructions, the memory operand is really a base 611 | // and the memory access itself can be very far away from the base. Figure 612 | // out what memory address is actually accessed by taking into account 613 | // what is being set/tested. 614 | case XED_IFORM_BT_MEMv_IMMb: 615 | case XED_IFORM_BT_MEMv_GPRv: 616 | case XED_IFORM_BTS_MEMv_IMMb: 617 | case XED_IFORM_BTS_MEMv_GPRv: 618 | case XED_IFORM_BTS_LOCK_MEMv_IMMb: 619 | case XED_IFORM_BTS_LOCK_MEMv_GPRv: 620 | case XED_IFORM_BTC_MEMv_IMMb: 621 | case XED_IFORM_BTC_MEMv_GPRv: 622 | case XED_IFORM_BTC_LOCK_MEMv_IMMb: 623 | case XED_IFORM_BTC_LOCK_MEMv_GPRv: 624 | case XED_IFORM_BTR_MEMv_IMMb: 625 | case XED_IFORM_BTR_MEMv_GPRv: 626 | case XED_IFORM_BTR_LOCK_MEMv_IMMb: 627 | case XED_IFORM_BTR_LOCK_MEMv_GPRv: 628 | mem.displacement += GetBitOpByteOffset(); 629 | break; 630 | 631 | default: 632 | break; 633 | } 634 | } else { 635 | switch (iform) { 636 | // Same stack adjustment as above, just on a different memop. 637 | case XED_IFORM_CALL_NEAR_MEMv: 638 | case XED_IFORM_PUSH_MEMv: 639 | mem.displacement -= (gEmu.effective_operand_width / 8); 640 | break; 641 | 642 | default: 643 | break; 644 | } 645 | } 646 | 647 | // Create a hint for the request, so that they can pre-emptively check things 648 | // like writability of the address before the instruction is emulated 649 | // or executed. 650 | auto hint = MemRequestHint::kReadOnly; 651 | if ((xed_operand_read(xedo) || xed_operand_conditional_write(xedo)) && 652 | xed_operand_written(xedo)) { 653 | hint = MemRequestHint::kReadWrite; 654 | } else if (xed_operand_written(xedo)) { 655 | hint = MemRequestHint::kWriteOnly; 656 | } 657 | 658 | if (XED_OPERAND_AGEN == mem.op_name) { 659 | hint = MemRequestHint::kAddressGeneration; 660 | } 661 | 662 | mem.address = executor->ComputeAddress(xed_reg_enum_t2str(mem.segment_reg), 663 | mem.base, mem.index, mem.scale, 664 | mem.displacement, mem.size, hint); 665 | 666 | // Mask the address down to its proper width. The individual values might 667 | // all have the correct width; however, when added together, some 32-bit 668 | // values might overflow into a 64-bit value. 669 | if (32 == xed_decoded_inst_get_memop_address_width(gXedd, mem_index)) { 670 | mem.address = static_cast(mem.address); 671 | } else if (16 == xed_decoded_inst_get_memop_address_width(gXedd, mem_index)) { 672 | mem.address = static_cast(mem.address); 673 | } 674 | 675 | mem.size = 8 * xed_decoded_inst_get_memory_operand_length(gXedd, mem_index); 676 | 677 | // Read in the data. 678 | memset(mem.data.bytes, 0, sizeof(mem.data)); 679 | return (XED_OPERAND_AGEN == mem.op_name) || 680 | executor->ReadMem(mem.address, mem.size, hint, mem.data); 681 | } 682 | 683 | // Read in memory from the executor. 684 | static bool ReadMemory(const Executor *executor) { 685 | gMemory[0].present = false; 686 | gMemory[1].present = false; 687 | 688 | auto num_operands = xed_decoded_inst_noperands(gXedd); 689 | auto xedi = xed_decoded_inst_inst(gXedd); 690 | for (auto i = 0U; i < num_operands; ++i) { 691 | auto xedo = xed_inst_operand(xedi, i); 692 | switch (xed_operand_name(xedo)) { 693 | case XED_OPERAND_AGEN: 694 | case XED_OPERAND_MEM0: 695 | if (!ReadMemory(executor, i, 0)) { 696 | return false; 697 | } 698 | break; 699 | case XED_OPERAND_MEM1: 700 | if (!ReadMemory(executor, i, 1)) { 701 | return false; 702 | } 703 | break; 704 | default: 705 | break; 706 | } 707 | } 708 | return true; 709 | } 710 | 711 | // Write the memory back to the executor, if the operand action specifies it, 712 | // that is. 713 | static bool WriteMemory(const Executor *executor) { 714 | for (auto &mem : gMemory) { 715 | if (!mem.present || !mem.write_back) { 716 | continue; 717 | } 718 | if (!executor->WriteMem(mem.address, mem.size, mem.data)) { 719 | return false; 720 | } 721 | } 722 | return true; 723 | } 724 | 725 | // Return the value of the program counter. 726 | static bool ReadPC(const Executor *executor) { 727 | auto reg = WidestRegister(executor, XED_REG_EIP); 728 | gModifiedRegs.set(reg); 729 | if (ReadRegister(executor, reg, RegRequestHint::kProgramCounter)) { 730 | return true; 731 | } else { 732 | return false; 733 | } 734 | } 735 | 736 | // Returns the target of a branch instruction assuming the branch is taken. 737 | static uintptr_t BranchTarget(uintptr_t next_pc) { 738 | auto disp = xed_decoded_inst_get_branch_displacement(gXedd); 739 | return next_pc + static_cast(static_cast(disp)); 740 | } 741 | 742 | // The current program counter. 743 | static uintptr_t GetPC(const Executor *executor) { 744 | auto reg = WidestRegister(executor, XED_REG_EIP); 745 | auto pc = ReadGPR(reg); 746 | if (32 == executor->addr_size) { 747 | return static_cast(pc); 748 | } else { 749 | return pc; 750 | } 751 | } 752 | 753 | // The next program counter. 754 | static uintptr_t GetNextPC(const Executor *executor) { 755 | auto reg = WidestRegister(executor, XED_REG_EIP); 756 | auto pc = ReadGPR(reg); 757 | auto next_pc = pc + xed_decoded_inst_get_length(gXedd); 758 | if (32 == executor->addr_size) { 759 | return static_cast(next_pc); 760 | } else { 761 | return next_pc; 762 | } 763 | } 764 | 765 | // Get the first immediate operand as if it's a signed value. 766 | static uint64_t GetSignedImmediate(void) { 767 | int64_t simm0 = xed_decoded_inst_get_signed_immediate(gXedd); 768 | switch (gEmu.effective_operand_width) { 769 | case 64: 770 | return static_cast(simm0); 771 | case 32: 772 | return static_cast(simm0); 773 | case 16: 774 | return static_cast(simm0); 775 | case 8: 776 | return static_cast(simm0); 777 | default: 778 | return 0; 779 | } 780 | } 781 | 782 | // Read the flags structure. 783 | static Flags &ReadFlags(void) { 784 | return *reinterpret_cast(&(gRegs[XED_REG_RFLAGS].bytes[0])); 785 | } 786 | 787 | // Compute the parity flag for a value. This is only computed on the low 8 788 | // bits of some value. 789 | static bool ParityFlag(uint8_t r0) { 790 | auto r1 = r0 >> 1; 791 | auto r2 = r1 >> 1; 792 | auto r3 = r2 >> 1; 793 | auto r4 = r3 >> 1; 794 | auto r5 = r4 >> 1; 795 | auto r6 = r5 >> 1; 796 | auto r7 = r6 >> 1; 797 | return !(1 & (r0 ^ r1 ^ r2 ^ r3 ^ r4 ^ r5 ^ r6 ^ r7)); 798 | } 799 | 800 | // Compute the flags produced from a subtraction. 801 | static void UpdateFlagsSub(Flags &flags, uintptr_t lhs, uintptr_t rhs, 802 | uintptr_t res, size_t size) { 803 | const auto sign_shift = 1ULL << (size - 1); 804 | const auto sign_lhs = (lhs >> sign_shift) & 1ULL; 805 | const auto sign_rhs = (rhs >> sign_shift) & 1ULL; 806 | const auto sign_res = (res >> sign_shift) & 1ULL; 807 | flags.sf = sign_res; 808 | flags.cf = lhs < rhs; 809 | flags.of = (2 == (sign_lhs ^ sign_rhs) + (sign_lhs ^ sign_res)); 810 | flags.zf = (0 == res); 811 | flags.af = (0 != ((res ^ lhs ^ rhs) & 0x10ULL)); 812 | flags.pf = ParityFlag(static_cast(res)); 813 | } 814 | 815 | #define STOS \ 816 | do { \ 817 | mem0 = ReadGPR(reg0); \ 818 | WriteGPR(dest_reg, \ 819 | static_cast(static_cast(ReadGPR(dest_reg)) + \ 820 | stringop_inc)); \ 821 | gModifiedRegs.set(dest_reg); \ 822 | } while (false) 823 | 824 | #define SCAS \ 825 | do { \ 826 | const auto reg0_val = ReadGPR(reg0); \ 827 | const auto temp = reg0_val - mem0; \ 828 | const auto op_size = gEmu.effective_operand_width; \ 829 | UpdateFlagsSub(aflag, reg0_val, mem0, temp, op_size); \ 830 | WriteGPR(dest_reg, \ 831 | static_cast(static_cast(ReadGPR(dest_reg)) + \ 832 | stringop_inc)); \ 833 | gModifiedRegs.set(dest_reg); \ 834 | } while (false) 835 | 836 | #define LODS \ 837 | do { \ 838 | WriteGPR(reg0, mem0); \ 839 | WriteGPR(src_reg, \ 840 | static_cast(static_cast(ReadGPR(src_reg)) + \ 841 | stringop_inc)); \ 842 | gModifiedRegs.set(src_reg); \ 843 | } while (false) 844 | 845 | #define MOVS \ 846 | do { \ 847 | mem0 = mem1; \ 848 | WriteGPR(src_reg, \ 849 | static_cast(static_cast(ReadGPR(src_reg)) + \ 850 | stringop_inc)); \ 851 | WriteGPR(dest_reg, \ 852 | static_cast(static_cast(ReadGPR(dest_reg)) + \ 853 | stringop_inc)); \ 854 | gModifiedRegs.set(src_reg); \ 855 | gModifiedRegs.set(dest_reg); \ 856 | } while (false) 857 | 858 | #define CMPS \ 859 | do { \ 860 | const auto temp = mem0 - mem1; \ 861 | const auto op_size = gEmu.effective_operand_width; \ 862 | UpdateFlagsSub(aflag, mem0, mem1, temp, op_size); \ 863 | WriteGPR(src_reg, \ 864 | static_cast(static_cast(ReadGPR(src_reg)) + \ 865 | stringop_inc)); \ 866 | WriteGPR(dest_reg, \ 867 | static_cast(static_cast(ReadGPR(dest_reg)) + \ 868 | stringop_inc)); \ 869 | gModifiedRegs.set(src_reg); \ 870 | gModifiedRegs.set(dest_reg); \ 871 | } while (false) 872 | 873 | #define REPNE(...) \ 874 | do { \ 875 | if (count) { \ 876 | __VA_ARGS__; \ 877 | count = count - 1; \ 878 | WriteGPR(count_reg, count); \ 879 | if (count && !aflag.zf) { \ 880 | next_pc = curr_pc; \ 881 | } \ 882 | } \ 883 | } while (false) 884 | 885 | #define REPE(...) \ 886 | do { \ 887 | if (count) { \ 888 | __VA_ARGS__; \ 889 | count = count - 1; \ 890 | WriteGPR(count_reg, count); \ 891 | if (count && aflag.zf) { \ 892 | next_pc = curr_pc; \ 893 | } \ 894 | } \ 895 | } while (false) 896 | 897 | #define REP(...) \ 898 | do { \ 899 | if (count) { \ 900 | __VA_ARGS__; \ 901 | count = count - 1; \ 902 | WriteGPR(count_reg, count); \ 903 | if (count) { \ 904 | next_pc = curr_pc; \ 905 | } \ 906 | } \ 907 | } while (false) 908 | 909 | // Figure out what the next program counter should be. 910 | static bool Emulate(const Executor *executor, uintptr_t &next_pc, 911 | ExecutorStatus &status) { 912 | status = ExecutorStatus::kGood; 913 | auto branch_target_pc = BranchTarget(next_pc); 914 | const auto curr_pc = ReadGPR(XED_REG_RIP); 915 | 916 | // Mask PCs to correct size. 917 | if (32 == executor->addr_size) { 918 | next_pc = static_cast(next_pc); 919 | branch_target_pc = static_cast(branch_target_pc); 920 | } 921 | 922 | // Get the flags (if this is a conditional branch). 923 | auto &aflag = ReadFlags(); 924 | 925 | // Use for REP* and LOOP*. 926 | const auto count_reg = WidestRegister(executor, XED_REG_ECX); 927 | auto count = ReadGPR(count_reg); 928 | const auto src_reg = WidestRegister(executor, XED_REG_ESI); 929 | const auto dest_reg = WidestRegister(executor, XED_REG_EDI); 930 | const auto stack_reg = WidestRegister(executor, XED_REG_ESP); 931 | const auto reg0 = xed_decoded_inst_get_reg(gXedd, XED_OPERAND_REG0); 932 | const auto reg1 = xed_decoded_inst_get_reg(gXedd, XED_OPERAND_REG1); 933 | const auto reg2 = xed_decoded_inst_get_reg(gXedd, XED_OPERAND_REG2); 934 | auto &mem0 = *reinterpret_cast(gMemory[0].data.bytes); 935 | auto &mem1 = *reinterpret_cast(gMemory[1].data.bytes); 936 | const auto addr_size_bytes = executor->addr_size / 8; 937 | const auto op_size_bytes = (gEmu.effective_operand_width / 8); 938 | const auto simm0 = GetSignedImmediate(); 939 | 940 | auto stringop_inc = static_cast(op_size_bytes); 941 | if (aflag.df) { 942 | stringop_inc = -stringop_inc; 943 | } 944 | 945 | switch (xed_decoded_inst_get_iform_enum(gXedd)) { 946 | case XED_IFORM_LEA_GPRv_AGEN: 947 | WriteGPR(reg0, gMemory[0].address); 948 | return true; 949 | 950 | // Conditional branches. 951 | case XED_IFORM_JNLE_RELBRb: 952 | case XED_IFORM_JNLE_RELBRz: 953 | case XED_IFORM_JNLE_RELBRd: 954 | if (!aflag.zf && aflag.sf == aflag.of) { 955 | next_pc = branch_target_pc; 956 | } 957 | return true; 958 | 959 | case XED_IFORM_JNS_RELBRb: 960 | case XED_IFORM_JNS_RELBRz: 961 | case XED_IFORM_JNS_RELBRd: 962 | if (!aflag.sf) { 963 | next_pc = branch_target_pc; 964 | } 965 | return true; 966 | 967 | case XED_IFORM_JL_RELBRb: 968 | case XED_IFORM_JL_RELBRz: 969 | case XED_IFORM_JL_RELBRd: 970 | if (aflag.sf != aflag.of) { 971 | next_pc = branch_target_pc; 972 | } 973 | return true; 974 | 975 | case XED_IFORM_JNP_RELBRb: 976 | case XED_IFORM_JNP_RELBRz: 977 | case XED_IFORM_JNP_RELBRd: 978 | if (!aflag.pf) { 979 | next_pc = branch_target_pc; 980 | } 981 | return true; 982 | 983 | case XED_IFORM_JNZ_RELBRb: 984 | case XED_IFORM_JNZ_RELBRz: 985 | case XED_IFORM_JNZ_RELBRd: 986 | if (!aflag.zf) { 987 | next_pc = branch_target_pc; 988 | } 989 | return true; 990 | 991 | case XED_IFORM_JNB_RELBRb: 992 | case XED_IFORM_JNB_RELBRz: 993 | case XED_IFORM_JNB_RELBRd: 994 | if (!aflag.cf) { 995 | next_pc = branch_target_pc; 996 | } 997 | return true; 998 | 999 | case XED_IFORM_JNO_RELBRb: 1000 | case XED_IFORM_JNO_RELBRz: 1001 | case XED_IFORM_JNO_RELBRd: 1002 | if (!aflag.of) { 1003 | next_pc = branch_target_pc; 1004 | } 1005 | return true; 1006 | 1007 | case XED_IFORM_JNL_RELBRb: 1008 | case XED_IFORM_JNL_RELBRz: 1009 | case XED_IFORM_JNL_RELBRd: 1010 | if (aflag.sf == aflag.of) { 1011 | next_pc = branch_target_pc; 1012 | } 1013 | return true; 1014 | 1015 | case XED_IFORM_JNBE_RELBRb: 1016 | case XED_IFORM_JNBE_RELBRz: 1017 | case XED_IFORM_JNBE_RELBRd: 1018 | if (!aflag.cf & !aflag.zf) { 1019 | next_pc = branch_target_pc; 1020 | } 1021 | return true; 1022 | 1023 | case XED_IFORM_JBE_RELBRb: 1024 | case XED_IFORM_JBE_RELBRz: 1025 | case XED_IFORM_JBE_RELBRd: 1026 | if (aflag.cf || aflag.zf) { 1027 | next_pc = branch_target_pc; 1028 | } 1029 | return true; 1030 | 1031 | case XED_IFORM_JZ_RELBRb: 1032 | case XED_IFORM_JZ_RELBRz: 1033 | case XED_IFORM_JZ_RELBRd: 1034 | if (aflag.zf) { 1035 | next_pc = branch_target_pc; 1036 | } 1037 | return true; 1038 | 1039 | case XED_IFORM_JP_RELBRb: 1040 | case XED_IFORM_JP_RELBRz: 1041 | case XED_IFORM_JP_RELBRd: 1042 | if (aflag.pf) { 1043 | next_pc = branch_target_pc; 1044 | } 1045 | return true; 1046 | 1047 | case XED_IFORM_JS_RELBRb: 1048 | case XED_IFORM_JS_RELBRz: 1049 | case XED_IFORM_JS_RELBRd: 1050 | if (aflag.sf) { 1051 | next_pc = branch_target_pc; 1052 | } 1053 | return true; 1054 | 1055 | case XED_IFORM_JO_RELBRb: 1056 | case XED_IFORM_JO_RELBRd: 1057 | case XED_IFORM_JO_RELBRz: 1058 | if (aflag.of) { 1059 | next_pc = branch_target_pc; 1060 | } 1061 | return true; 1062 | 1063 | case XED_IFORM_JB_RELBRb: 1064 | case XED_IFORM_JB_RELBRz: 1065 | case XED_IFORM_JB_RELBRd: 1066 | if (aflag.cf) { 1067 | next_pc = branch_target_pc; 1068 | } 1069 | return true; 1070 | 1071 | case XED_IFORM_JLE_RELBRb: 1072 | case XED_IFORM_JLE_RELBRz: 1073 | case XED_IFORM_JLE_RELBRd: 1074 | if (aflag.zf || aflag.sf != aflag.of) { 1075 | next_pc = branch_target_pc; 1076 | } 1077 | return true; 1078 | 1079 | case XED_IFORM_LOOPNE_RELBRb: 1080 | WriteGPR(count_reg, count - 1); 1081 | if (count && !aflag.zf) { 1082 | next_pc = branch_target_pc; 1083 | } 1084 | return true; 1085 | 1086 | case XED_IFORM_LOOPE_RELBRb: 1087 | WriteGPR(count_reg, count - 1); 1088 | if (count && aflag.zf) { 1089 | next_pc = branch_target_pc; 1090 | } 1091 | return true; 1092 | 1093 | case XED_IFORM_LOOP_RELBRb: 1094 | WriteGPR(count_reg, count - 1); 1095 | if (count) { 1096 | next_pc = branch_target_pc; 1097 | } 1098 | return true; 1099 | 1100 | case XED_IFORM_JCXZ_RELBRb: 1101 | if (!ReadGPR(XED_REG_CX)) { 1102 | next_pc = branch_target_pc; 1103 | } 1104 | return true; 1105 | 1106 | case XED_IFORM_JECXZ_RELBRb: 1107 | if (!ReadGPR(XED_REG_ECX)) { 1108 | next_pc = branch_target_pc; 1109 | } 1110 | return true; 1111 | 1112 | case XED_IFORM_JRCXZ_RELBRb: 1113 | if (!ReadGPR(XED_REG_RCX)) { 1114 | next_pc = branch_target_pc; 1115 | } 1116 | return true; 1117 | 1118 | // Pretend to handle XBEGIN by always failing the transaction and 1119 | // setting the error code to there being an internal buffer overflow 1120 | // (capacity failure in the L1 cache). 1121 | case XED_IFORM_XBEGIN_RELBRz: 1122 | next_pc = branch_target_pc; 1123 | *reinterpret_cast(gRegs[XED_REG_RAX].bytes) = 1 << 3; 1124 | *reinterpret_cast(gRegs[XED_REG_EAX].bytes) = 1 << 3; 1125 | return true; 1126 | 1127 | // Don't allow XABORT/XEND. 1128 | case XED_IFORM_XABORT_IMMb: 1129 | case XED_IFORM_XEND: 1130 | status = ExecutorStatus::kErrorUnsupportedCFI; 1131 | return false; 1132 | 1133 | case XED_IFORM_JMP_MEMv: 1134 | next_pc = mem0; 1135 | return true; 1136 | 1137 | case XED_IFORM_JMP_GPRv: 1138 | next_pc = ReadGPR(reg0); 1139 | return true; 1140 | 1141 | case XED_IFORM_JMP_RELBRz: 1142 | case XED_IFORM_JMP_RELBRd: 1143 | case XED_IFORM_JMP_RELBRb: 1144 | next_pc = branch_target_pc; 1145 | return true; 1146 | 1147 | case XED_IFORM_CALL_NEAR_RELBRz: 1148 | case XED_IFORM_CALL_NEAR_RELBRd: 1149 | mem0 = next_pc; 1150 | next_pc = branch_target_pc; 1151 | WriteGPR(stack_reg, ReadGPR(stack_reg) - addr_size_bytes); 1152 | return true; 1153 | 1154 | case XED_IFORM_CALL_NEAR_MEMv: 1155 | mem1 = next_pc; 1156 | next_pc = mem0; 1157 | WriteGPR(stack_reg, ReadGPR(stack_reg) - addr_size_bytes); 1158 | return true; 1159 | 1160 | case XED_IFORM_CALL_NEAR_GPRv: 1161 | mem0 = next_pc; 1162 | next_pc = ReadGPR(reg0); 1163 | WriteGPR(stack_reg, ReadGPR(stack_reg) - addr_size_bytes); 1164 | return true; 1165 | 1166 | case XED_IFORM_RET_NEAR: 1167 | next_pc = mem0; 1168 | WriteGPR(stack_reg, ReadGPR(stack_reg) + addr_size_bytes); 1169 | return true; 1170 | 1171 | case XED_IFORM_RET_NEAR_IMMw: 1172 | next_pc = mem0; 1173 | WriteGPR(stack_reg, ReadGPR(stack_reg) + addr_size_bytes + 1174 | static_cast(simm0)); 1175 | return true; 1176 | 1177 | // Far CALL/RET/JMP are not supported. 1178 | case XED_IFORM_CALL_FAR_MEMp2: 1179 | case XED_IFORM_CALL_FAR_PTRp_IMMw: 1180 | case XED_IFORM_JMP_FAR_MEMp2: 1181 | case XED_IFORM_JMP_FAR_PTRp_IMMw: 1182 | case XED_IFORM_RET_FAR_IMMw: 1183 | case XED_IFORM_RET_FAR: 1184 | status = ExecutorStatus::kErrorUnsupportedCFI; 1185 | return false; 1186 | 1187 | // We want to treat REP instructions as executing just a single loop of 1188 | // the (internal) repetition loop. 1189 | case XED_IFORM_REPE_SCASW: 1190 | case XED_IFORM_REPE_SCASQ: 1191 | case XED_IFORM_REPE_SCASD: 1192 | case XED_IFORM_REPE_SCASB: 1193 | REPE(SCAS); 1194 | return true; 1195 | 1196 | case XED_IFORM_REP_LODSQ: 1197 | case XED_IFORM_REP_LODSW: 1198 | case XED_IFORM_REP_LODSB: 1199 | case XED_IFORM_REP_LODSD: 1200 | REP(LODS); 1201 | return true; 1202 | 1203 | case XED_IFORM_REPNE_CMPSW: 1204 | case XED_IFORM_REPNE_CMPSQ: 1205 | case XED_IFORM_REPNE_CMPSB: 1206 | case XED_IFORM_REPNE_CMPSD: 1207 | REPNE(CMPS); 1208 | return true; 1209 | 1210 | case XED_IFORM_REP_STOSD: 1211 | case XED_IFORM_REP_STOSB: 1212 | case XED_IFORM_REP_STOSW: 1213 | case XED_IFORM_REP_STOSQ: 1214 | REP(STOS); 1215 | return true; 1216 | 1217 | case XED_IFORM_REPNE_SCASB: 1218 | case XED_IFORM_REPNE_SCASD: 1219 | case XED_IFORM_REPNE_SCASQ: 1220 | case XED_IFORM_REPNE_SCASW: 1221 | REPNE(SCAS); 1222 | return true; 1223 | 1224 | case XED_IFORM_REP_MOVSQ: 1225 | case XED_IFORM_REP_MOVSD: 1226 | case XED_IFORM_REP_MOVSB: 1227 | case XED_IFORM_REP_MOVSW: 1228 | REP(MOVS); 1229 | return true; 1230 | 1231 | case XED_IFORM_REPE_CMPSQ: 1232 | case XED_IFORM_REPE_CMPSD: 1233 | case XED_IFORM_REPE_CMPSB: 1234 | case XED_IFORM_REPE_CMPSW: 1235 | REPE(CMPS); 1236 | return true; 1237 | 1238 | case XED_IFORM_STOSD: 1239 | case XED_IFORM_STOSB: 1240 | case XED_IFORM_STOSW: 1241 | case XED_IFORM_STOSQ: 1242 | STOS; 1243 | return true; 1244 | 1245 | case XED_IFORM_SCASW: 1246 | case XED_IFORM_SCASQ: 1247 | case XED_IFORM_SCASD: 1248 | case XED_IFORM_SCASB: 1249 | SCAS; 1250 | return true; 1251 | 1252 | case XED_IFORM_LODSB: 1253 | case XED_IFORM_LODSQ: 1254 | case XED_IFORM_LODSW: 1255 | case XED_IFORM_LODSD: 1256 | LODS; 1257 | return true; 1258 | 1259 | case XED_IFORM_MOVSD: 1260 | case XED_IFORM_MOVSB: 1261 | case XED_IFORM_MOVSW: 1262 | case XED_IFORM_MOVSQ: 1263 | MOVS; 1264 | return true; 1265 | 1266 | case XED_IFORM_CMPSQ: 1267 | case XED_IFORM_CMPSW: 1268 | case XED_IFORM_CMPSB: 1269 | case XED_IFORM_CMPSD: 1270 | CMPS; 1271 | return true; 1272 | 1273 | case XED_IFORM_PUSH_MEMv: 1274 | mem1 = mem0; 1275 | WriteGPR(stack_reg, ReadGPR(stack_reg) - op_size_bytes); 1276 | return true; 1277 | 1278 | case XED_IFORM_PUSH_GPRv_FFr6: 1279 | mem0 = ReadGPR(reg0); 1280 | WriteGPR(stack_reg, ReadGPR(stack_reg) - op_size_bytes); 1281 | return true; 1282 | 1283 | case XED_IFORM_PUSH_GPRv_50: 1284 | mem0 = ReadGPR(reg0); 1285 | WriteGPR(stack_reg, ReadGPR(stack_reg) - op_size_bytes); 1286 | return true; 1287 | 1288 | case XED_IFORM_PUSH_IMMz: 1289 | case XED_IFORM_PUSH_IMMb: 1290 | mem0 = static_cast(simm0); 1291 | WriteGPR(stack_reg, ReadGPR(stack_reg) - op_size_bytes); 1292 | return true; 1293 | 1294 | case XED_IFORM_POP_MEMv: 1295 | mem0 = mem1; 1296 | WriteGPR(stack_reg, ReadGPR(stack_reg) + op_size_bytes); 1297 | return true; 1298 | 1299 | case XED_IFORM_POP_GPRv_8F: 1300 | case XED_IFORM_POP_GPRv_58: 1301 | WriteGPR(reg0, mem0); 1302 | WriteGPR(stack_reg, ReadGPR(stack_reg) + op_size_bytes); 1303 | return true; 1304 | 1305 | case XED_IFORM_LEAVE: 1306 | WriteGPR(reg1, ReadGPR(reg0)); 1307 | WriteGPR(reg0, mem0); 1308 | return true; 1309 | 1310 | case XED_IFORM_PUSH_ES: 1311 | case XED_IFORM_PUSH_CS: 1312 | case XED_IFORM_PUSH_SS: 1313 | case XED_IFORM_PUSH_DS: 1314 | case XED_IFORM_PUSH_FS: 1315 | case XED_IFORM_PUSH_GS: 1316 | mem0 = ReadGPR(reg0); 1317 | WriteGPR(stack_reg, ReadGPR(stack_reg) - addr_size_bytes); 1318 | return true; 1319 | 1320 | case XED_IFORM_POP_ES: 1321 | case XED_IFORM_POP_SS: 1322 | case XED_IFORM_POP_DS: 1323 | case XED_IFORM_POP_FS: 1324 | case XED_IFORM_POP_GS: 1325 | WriteGPR(reg0, mem0); 1326 | WriteGPR(stack_reg, ReadGPR(stack_reg) + addr_size_bytes); 1327 | return true; 1328 | 1329 | case XED_IFORM_MOV_MEMw_SEG: 1330 | mem0 = ReadGPR(reg0); 1331 | return true; 1332 | 1333 | case XED_IFORM_MOV_GPRv_SEG: 1334 | WriteGPR(reg0, ReadGPR(reg1)); 1335 | return true; 1336 | 1337 | case XED_IFORM_MOV_SEG_MEMw: 1338 | WriteGPR(reg0, mem0); 1339 | return true; 1340 | 1341 | case XED_IFORM_MOV_SEG_GPR16: 1342 | WriteGPR(reg0, ReadGPR(reg1)); 1343 | return true; 1344 | 1345 | // TODO(pag): ID flag (for checking CPUID)? 1346 | case XED_IFORM_PUSHF: 1347 | case XED_IFORM_PUSHFD: 1348 | case XED_IFORM_PUSHFQ: 1349 | mem0 = aflag.flat; 1350 | WriteGPR(stack_reg, ReadGPR(stack_reg) - addr_size_bytes); 1351 | return true; 1352 | 1353 | // TODO(pag): ID flag (for checking CPUID)? 1354 | case XED_IFORM_POPF: 1355 | case XED_IFORM_POPFD: 1356 | case XED_IFORM_POPFQ: 1357 | aflag.flat = mem0; 1358 | WriteGPR(stack_reg, ReadGPR(stack_reg) + addr_size_bytes); 1359 | return true; 1360 | 1361 | // Don't even try to handle these; too much memory traffic. 1362 | case XED_IFORM_PUSHA: 1363 | case XED_IFORM_PUSHAD: 1364 | case XED_IFORM_POPA: 1365 | case XED_IFORM_POPAD: 1366 | case XED_IFORM_ENTER_IMMw_IMMb: 1367 | status = ExecutorStatus::kErrorUnsupportedStack; 1368 | return false; 1369 | 1370 | case XED_IFORM_XLAT: 1371 | WriteGPR(reg0, mem0); 1372 | return true; 1373 | 1374 | case XED_IFORM_RDTSCP: 1375 | WriteGPR(reg2, ReadValue(XED_REG_TSCAUX)); 1376 | // fall-through 1377 | case XED_IFORM_RDTSC: { 1378 | uint64_t tsc = ReadValue(XED_REG_TSC); 1379 | WriteGPR(reg0, static_cast(tsc)); 1380 | WriteGPR(reg1, tsc >> 32); 1381 | } 1382 | return true; 1383 | 1384 | default: 1385 | return false; 1386 | } 1387 | } 1388 | 1389 | // Update the program counter register. 1390 | static void SetNextPC(const Executor *executor, uintptr_t next_pc) { 1391 | *reinterpret_cast(gRegs[XED_REG_RIP].bytes) = next_pc; 1392 | } 1393 | 1394 | // Detect instructions that we can't emulate or execute based on their 1395 | // attributes. 1396 | static bool UsesUnsupportedAttributes(void) { 1397 | return xed_decoded_inst_get_attribute(gXedd, XED_ATTRIBUTE_RING0) || 1398 | xed_decoded_inst_get_attribute(gXedd, XED_ATTRIBUTE_PROTECTED_MODE) || 1399 | xed_decoded_inst_get_attribute(gXedd, XED_ATTRIBUTE_FAR_XFER) || 1400 | xed_decoded_inst_get_attribute(gXedd, XED_ATTRIBUTE_EXCEPTION_BR) || 1401 | xed_decoded_inst_get_attribute(gXedd, XED_ATTRIBUTE_X87_MMX_STATE_R) || 1402 | xed_decoded_inst_get_attribute(gXedd, XED_ATTRIBUTE_X87_MMX_STATE_W) || 1403 | xed_decoded_inst_get_attribute(gXedd, 1404 | XED_ATTRIBUTE_X87_MMX_STATE_CW) || 1405 | xed_decoded_inst_get_attribute(gXedd, XED_ATTRIBUTE_XMM_STATE_R) || 1406 | xed_decoded_inst_get_attribute(gXedd, XED_ATTRIBUTE_XMM_STATE_W) || 1407 | xed_decoded_inst_get_attribute(gXedd, XED_ATTRIBUTE_XMM_STATE_CW) || 1408 | xed_decoded_inst_get_attribute(gXedd, XED_ATTRIBUTE_EXCEPTION_BR) || 1409 | xed_decoded_inst_get_attribute(gXedd, XED_ATTRIBUTE_KMASK) || 1410 | xed_decoded_inst_get_attribute(gXedd, XED_ATTRIBUTE_MASKOP) || 1411 | xed_decoded_inst_get_attribute(gXedd, XED_ATTRIBUTE_MASKOP_EVEX) || 1412 | xed_decoded_inst_get_attribute(gXedd, XED_ATTRIBUTE_MASK_AS_CONTROL) || 1413 | xed_decoded_inst_get_attribute(gXedd, 1414 | XED_ATTRIBUTE_MASK_VARIABLE_MEMOP); 1415 | } 1416 | 1417 | static bool UsesUnsupportedFeatures(const Executor *executor) { 1418 | switch (xed_decoded_inst_get_category(gXedd)) { 1419 | case XED_CATEGORY_SYSTEM: 1420 | switch (xed_decoded_inst_get_iform_enum(gXedd)) { 1421 | case XED_IFORM_RDTSC: 1422 | case XED_IFORM_RDTSCP: 1423 | return false; 1424 | default: 1425 | return true; 1426 | } 1427 | case XED_CATEGORY_3DNOW: 1428 | case XED_CATEGORY_MPX: 1429 | case XED_CATEGORY_AES: 1430 | case XED_CATEGORY_RDRAND: 1431 | case XED_CATEGORY_RDSEED: 1432 | case XED_CATEGORY_SEGOP: 1433 | case XED_CATEGORY_SYSCALL: 1434 | case XED_CATEGORY_INTERRUPT: 1435 | case XED_CATEGORY_SYSRET: 1436 | case XED_CATEGORY_XSAVE: 1437 | case XED_CATEGORY_XSAVEOPT: 1438 | case XED_CATEGORY_IOSTRINGOP: 1439 | case XED_CATEGORY_RDWRFSGS: 1440 | case XED_CATEGORY_VTX: 1441 | return true; 1442 | case XED_CATEGORY_AVX: 1443 | case XED_CATEGORY_AVX2: 1444 | case XED_CATEGORY_AVX2GATHER: 1445 | return !executor->has_avx || UsesUnsupportedAttributes(); 1446 | case XED_CATEGORY_AVX512: 1447 | case XED_CATEGORY_AVX512_4FMAPS: 1448 | case XED_CATEGORY_AVX512_4VNNIW: 1449 | case XED_CATEGORY_AVX512_BITALG: 1450 | case XED_CATEGORY_AVX512_VBMI: 1451 | return !executor->has_avx512 || UsesUnsupportedAttributes(); 1452 | default: 1453 | return UsesUnsupportedAttributes(); 1454 | } 1455 | } 1456 | 1457 | #define READ_FLAG(field, name) \ 1458 | if (read_flags.field) { \ 1459 | if (!executor->ReadReg(name, 1, RegRequestHint::kConditionCode, \ 1460 | flag_val)) { \ 1461 | return false; \ 1462 | } \ 1463 | aflag.field = !!(flag_val.bytes[0] & 1U); \ 1464 | flag_val.bytes[0] = 0; \ 1465 | } 1466 | 1467 | // Read in the flags, as if the individual flags themselves were registers. 1468 | static bool ReadFlags(const Executor *executor) { 1469 | gWriteBackFlags.flat = 0; 1470 | 1471 | const auto rflags = xed_decoded_inst_get_rflags_info(gXedd); 1472 | if (!rflags) { 1473 | return true; 1474 | } 1475 | 1476 | // Conditional writes to flags are implicit read dependencies. 1477 | Flags read_flags; 1478 | read_flags.flat = static_cast(rflags->read.flat); 1479 | if (rflags->may_write) { 1480 | read_flags.flat |= rflags->written.flat; 1481 | } 1482 | 1483 | const auto flags_reg = WidestRegister(executor, XED_REG_EFLAGS); 1484 | auto &data = gRegs[XED_REG_RFLAGS]; 1485 | 1486 | // Only write back written/undefined flags. 1487 | gWriteBackFlags.flat = static_cast(rflags->written.flat); 1488 | 1489 | gUsedRegs.set(flags_reg); 1490 | if (gWriteBackFlags.flat) { 1491 | gModifiedRegs.set(flags_reg); 1492 | } 1493 | 1494 | Flags aflag; 1495 | aflag.flat = 0; 1496 | 1497 | asm("pushfq;" 1498 | "pop %0;" 1499 | : "=m"(aflag.flat)); 1500 | 1501 | Data flag_val = {{0}}; 1502 | READ_FLAG(cf, "CF") 1503 | READ_FLAG(pf, "PF") 1504 | READ_FLAG(af, "AF") 1505 | READ_FLAG(zf, "ZF") 1506 | READ_FLAG(sf, "SF") 1507 | READ_FLAG(df, "DF") 1508 | READ_FLAG(of, "OF") 1509 | 1510 | memcpy(&(data.bytes[0]), &aflag.flat, sizeof(aflag)); 1511 | return true; 1512 | } 1513 | 1514 | #undef READ_FLAG 1515 | #define WRITE_FLAG(field, name) \ 1516 | if (gWriteBackFlags.field) { \ 1517 | flag_val.bytes[0] = aflag.field; \ 1518 | if (!executor->WriteReg(name, 1, flag_val)) { \ 1519 | return false; \ 1520 | } \ 1521 | flag_val.bytes[0] = 0; \ 1522 | } 1523 | 1524 | // Write back the flags, as if the individual flags themselves were registers. 1525 | static bool WriteFlags(const Executor *executor) { 1526 | const auto flags_reg = WidestRegister(executor, XED_REG_EFLAGS); 1527 | const auto &data = gRegs[XED_REG_RFLAGS]; 1528 | if (!gModifiedRegs.test(flags_reg)) { 1529 | return true; 1530 | } 1531 | 1532 | gUsedRegs.reset(flags_reg); 1533 | gModifiedRegs.reset(flags_reg); 1534 | 1535 | Flags aflag; 1536 | aflag.flat = 0; 1537 | memcpy(&aflag.flat, &(data.bytes[0]), sizeof(aflag)); 1538 | 1539 | Data flag_val = {{0}}; 1540 | WRITE_FLAG(cf, "CF") 1541 | WRITE_FLAG(pf, "PF") 1542 | WRITE_FLAG(af, "AF") 1543 | WRITE_FLAG(zf, "ZF") 1544 | WRITE_FLAG(sf, "SF") 1545 | WRITE_FLAG(df, "DF") 1546 | WRITE_FLAG(of, "OF") 1547 | 1548 | gWriteBackFlags.flat = 0; 1549 | return true; 1550 | } 1551 | 1552 | #undef WRITE_FLAG 1553 | 1554 | // Decode a `MEM0` operand into an absolute memory access. 1555 | static void DecodeMem0(unsigned i) { 1556 | auto &op = gEmu.operands[i]; 1557 | auto &mem = gMemory[0]; 1558 | op.type = XED_ENCODER_OPERAND_TYPE_MEM; 1559 | op.u.mem.disp.displacement = 0xFFFFFFFF; // Placeholder. 1560 | op.u.mem.disp.displacement_bits = 32; 1561 | op.u.mem.base = XED_REG_RIP; 1562 | op.width_bits = mem.size; 1563 | 1564 | mem.mem_op = &(op.u.mem); 1565 | } 1566 | 1567 | // Create and return an alias for the stack pointer register for use by the 1568 | // instruction. 1569 | static xed_reg_enum_t GetStackPointerAlias(xed_reg_enum_t reg) { 1570 | // Need to create an alias: these four registers are the most generally 1571 | // usable, and we expect at least one of them to be free. 1572 | if (XED_REG_INVALID == gStackPtrAlias) { 1573 | if (!gStoreRegs.test(XED_REG_RAX)) { 1574 | gStackPtrAlias = XED_REG_RAX; 1575 | } else if (!gStoreRegs.test(XED_REG_RCX)) { 1576 | gStackPtrAlias = XED_REG_RCX; 1577 | } else if (!gStoreRegs.test(XED_REG_RDX)) { 1578 | gStackPtrAlias = XED_REG_RDX; 1579 | } else if (!gStoreRegs.test(XED_REG_RBX)) { 1580 | gStackPtrAlias = XED_REG_RBX; 1581 | } else { 1582 | gStackPtrAlias = XED_REG_INVALID; // Uh oh. 1583 | } 1584 | 1585 | WriteGPR(gStackPtrAlias, ReadGPR(XED_REG_RSP)); 1586 | } 1587 | 1588 | // Scale the alias down to the desired size. 1589 | auto offset = (gStackPtrAlias - XED_REG_GPR64_FIRST); 1590 | switch (reg) { 1591 | case XED_REG_SPL: 1592 | return static_cast(offset + XED_REG_GPR8_FIRST); 1593 | case XED_REG_SP: 1594 | return static_cast(offset + XED_REG_GPR16_FIRST); 1595 | case XED_REG_ESP: 1596 | return static_cast(offset + XED_REG_GPR32_FIRST); 1597 | case XED_REG_RSP: 1598 | return gStackPtrAlias; 1599 | default: 1600 | return XED_REG_INVALID; 1601 | } 1602 | } 1603 | 1604 | // Decode the register into the high-level encoder interface. 1605 | static void DecodeRegN(unsigned i, xed_reg_enum_t reg) { 1606 | // If the stack pointer is used in the instruction, then reschedule it to 1607 | // a free register that can take its place. 1608 | if (XED_REG_RSP == xed_get_largest_enclosing_register(reg)) { 1609 | reg = GetStackPointerAlias(reg); 1610 | } 1611 | 1612 | auto &op = gEmu.operands[i]; 1613 | op.type = XED_ENCODER_OPERAND_TYPE_REG; 1614 | op.u.reg = reg; 1615 | op.width_bits = xed_get_register_width_bits64(reg); 1616 | } 1617 | 1618 | // Decode and `IMM0` operand into the high-level encoder interface. 1619 | static void DecodeImm0(unsigned i, xed_operand_enum_t op_name) { 1620 | auto &op = gEmu.operands[i]; 1621 | if (XED_OPERAND_IMM0SIGNED == op_name || 1622 | xed_operand_values_get_immediate_is_signed(gXedd)) { 1623 | op.type = XED_ENCODER_OPERAND_TYPE_SIMM0; 1624 | op.u.imm0 = static_cast( 1625 | static_cast(xed_decoded_inst_get_signed_immediate(gXedd))); 1626 | } else { 1627 | op.type = XED_ENCODER_OPERAND_TYPE_IMM0; 1628 | op.u.imm0 = xed_decoded_inst_get_unsigned_immediate(gXedd); 1629 | } 1630 | op.width_bits = xed_decoded_inst_get_immediate_width_bits(gXedd); 1631 | } 1632 | 1633 | // Decode an `IMM1` operand into the high-level encoder interface. 1634 | static void DecodeImm1(unsigned i) { 1635 | auto &op = gEmu.operands[i]; 1636 | op.type = XED_ENCODER_OPERAND_TYPE_IMM1; 1637 | op.u.imm1 = xed_decoded_inst_get_second_immediate(gXedd); 1638 | op.width_bits = xed_decoded_inst_get_immediate_width_bits(gXedd); 1639 | } 1640 | 1641 | // Convert the decoded instruction into XED's high-level encoder interface, so 1642 | // that we can re-encode the instruction and JIT it. 1643 | static void CreateEncodableInstruction(const Executor *executor) { 1644 | auto num_operands = xed_decoded_inst_noperands(gXedd); 1645 | auto xedi = xed_decoded_inst_inst(gXedd); 1646 | for (auto i = 0U; i < num_operands; ++i) { 1647 | auto xedo = xed_inst_operand(xedi, i); 1648 | auto vis = xed_operand_operand_visibility(xedo); 1649 | if (XED_OPVIS_EXPLICIT != vis && XED_OPVIS_IMPLICIT != vis) { 1650 | continue; 1651 | } 1652 | auto op_index = gEmu.noperands++; 1653 | switch (auto op_name = xed_operand_name(xedo)) { 1654 | case XED_OPERAND_MEM0: 1655 | DecodeMem0(op_index); 1656 | break; 1657 | 1658 | case XED_OPERAND_REG0: 1659 | case XED_OPERAND_REG1: 1660 | case XED_OPERAND_REG2: 1661 | case XED_OPERAND_REG3: 1662 | if (auto reg = xed_decoded_inst_get_reg(gXedd, op_name)) { 1663 | DecodeRegN(op_index, reg); 1664 | } 1665 | break; 1666 | 1667 | case XED_OPERAND_IMM0SIGNED: 1668 | case XED_OPERAND_IMM0: 1669 | DecodeImm0(op_index, op_name); 1670 | break; 1671 | 1672 | case XED_OPERAND_IMM1_BYTES: 1673 | case XED_OPERAND_IMM1: 1674 | DecodeImm1(op_index); 1675 | break; 1676 | 1677 | default: 1678 | break; 1679 | } 1680 | } 1681 | } 1682 | 1683 | // Encode an instruction. 1684 | static bool EncodeInstruction(void) { 1685 | xed_encoder_request_t xede; 1686 | xed_encoder_request_zero_set_mode(&xede, &(gEmu.mode)); 1687 | return xed_convert_to_encoder_request(&xede, &gEmu) && 1688 | XED_ERROR_NONE == xed_encode(&xede, gExecArea, 15, &gEmuSize); 1689 | } 1690 | 1691 | // Fill in the high-level encoder data structure with enough information to 1692 | // JIT this instruction. 1693 | static bool EncodeInstruction(const Executor *executor) { 1694 | CreateEncodableInstruction(executor); 1695 | 1696 | // Make sure that we can return from our function :-D 1697 | memset(gExecArea, 0xC3, 32); 1698 | 1699 | if (!EncodeInstruction()) { 1700 | return false; 1701 | } 1702 | 1703 | // If there's an explicit memory operand then we need to relativize its 1704 | // operand (based on the instruction length) to then point to the memory 1705 | // area. 1706 | if (gMemory[0].present) { 1707 | auto op = gMemory[0].mem_op; 1708 | auto rip = reinterpret_cast(gExecArea) + gEmuSize; 1709 | auto data = reinterpret_cast(&(gMemory[0].data.bytes[0])); 1710 | op->disp.displacement = data - rip; 1711 | return EncodeInstruction(); 1712 | 1713 | } else { 1714 | return true; 1715 | } 1716 | } 1717 | 1718 | #define COPY_FROM_MMX_32(i) \ 1719 | do { \ 1720 | if (gUsedRegs.test(XED_REG_MMX##i)) { \ 1721 | memcpy(&(gFPU.fxsave32.st[i].mmx), gRegs[XED_REG_MMX##i].bytes, 8); \ 1722 | gFPU.fxsave32.st[i].infinity = static_cast(~0U); \ 1723 | } \ 1724 | } while (0) 1725 | 1726 | #define COPY_FROM_MMX_64(i) \ 1727 | do { \ 1728 | if (gUsedRegs.test(XED_REG_MMX##i)) { \ 1729 | memcpy(&(gFPU.fxsave64.st[i].mmx), gRegs[XED_REG_MMX##i].bytes, 8); \ 1730 | gFPU.fxsave64.st[i].infinity = static_cast(~0U); \ 1731 | } \ 1732 | } while (0) 1733 | 1734 | #define COPY_TO_MMX_32(i) \ 1735 | do { \ 1736 | if (gUsedRegs.test(XED_REG_MMX##i)) { \ 1737 | memcpy(gRegs[XED_REG_MMX##i].bytes, &(gFPU.fxsave32.st[i].mmx), 8); \ 1738 | } \ 1739 | } while (0) 1740 | 1741 | #define COPY_TO_MMX_64(i) \ 1742 | do { \ 1743 | if (gUsedRegs.test(XED_REG_MMX##i)) { \ 1744 | memcpy(gRegs[XED_REG_MMX##i].bytes, &(gFPU.fxsave64.st[i].mmx), 8); \ 1745 | } \ 1746 | } while (0) 1747 | 1748 | static void CopyMMXStateToFPU(const Executor *executor) { 1749 | if (32 == executor->addr_size) { 1750 | COPY_FROM_MMX_32(0); 1751 | COPY_FROM_MMX_32(1); 1752 | COPY_FROM_MMX_32(2); 1753 | COPY_FROM_MMX_32(3); 1754 | COPY_FROM_MMX_32(4); 1755 | COPY_FROM_MMX_32(5); 1756 | COPY_FROM_MMX_32(6); 1757 | COPY_FROM_MMX_32(7); 1758 | } else { 1759 | COPY_FROM_MMX_64(0); 1760 | COPY_FROM_MMX_64(1); 1761 | COPY_FROM_MMX_64(2); 1762 | COPY_FROM_MMX_64(3); 1763 | COPY_FROM_MMX_64(4); 1764 | COPY_FROM_MMX_64(5); 1765 | COPY_FROM_MMX_64(6); 1766 | COPY_FROM_MMX_64(7); 1767 | } 1768 | } 1769 | 1770 | static void CopyMMXStateFromFPU(const Executor *executor) { 1771 | if (32 == executor->addr_size) { 1772 | COPY_TO_MMX_32(0); 1773 | COPY_TO_MMX_32(1); 1774 | COPY_TO_MMX_32(2); 1775 | COPY_TO_MMX_32(3); 1776 | COPY_TO_MMX_32(4); 1777 | COPY_TO_MMX_32(5); 1778 | COPY_TO_MMX_32(6); 1779 | COPY_TO_MMX_32(7); 1780 | } else { 1781 | COPY_TO_MMX_64(0); 1782 | COPY_TO_MMX_64(1); 1783 | COPY_TO_MMX_64(2); 1784 | COPY_TO_MMX_64(3); 1785 | COPY_TO_MMX_64(4); 1786 | COPY_TO_MMX_64(5); 1787 | COPY_TO_MMX_64(6); 1788 | COPY_TO_MMX_64(7); 1789 | } 1790 | } 1791 | // Load in the FPU that will be emulated. This will save the native FPU just 1792 | // in case it's got any special rounding modes or other settings going on. 1793 | static void LoadFPU(const Executor *executor) { 1794 | if (!gUsesFPU) return; 1795 | if (32 == executor->addr_size) { 1796 | asm(".byte 0x48; fxsave %0;" 1797 | "fxrstor %1;" 1798 | : 1799 | : "m"(gNativeFPU), "m"(gFPU)); 1800 | } else { 1801 | asm(".byte 0x48; fxsave %0;" 1802 | ".byte 0x48; fxrstor %1;" 1803 | : 1804 | : "m"(gNativeFPU), "m"(gFPU)); 1805 | } 1806 | } 1807 | 1808 | // Save the resulting FPU state to send it back to the user, and then restore 1809 | // the previous native FPU state. 1810 | static void StoreFPU(const Executor *executor) { 1811 | if (!gUsesFPU) return; 1812 | if (32 == executor->addr_size) { 1813 | asm("fxsave %0;" 1814 | ".byte 0x48; fxrstor %1;" 1815 | : 1816 | : "m"(gFPU), "m"(gNativeFPU)); 1817 | } else { 1818 | asm(".byte 0x48; fxsave %0;" 1819 | ".byte 0x48; fxrstor %1;" 1820 | : 1821 | : "m"(gFPU), "m"(gNativeFPU)); 1822 | } 1823 | } 1824 | 1825 | // Save and restore the native state, and execute the JITed instruction by 1826 | // calling into the `gExecArea`. 1827 | static void ExecuteNative(void) { 1828 | // Need locals because GCC doesn't like having things with function calls 1829 | // in the `asm` constraint list. 1830 | // 1831 | // Note: XED does *not* return ZMM registers as the widest enclosing 1832 | // version of XMM or YMM registers. 1833 | auto &XMM0 = gRegs[xed_get_largest_enclosing_register(XED_REG_XMM0)]; 1834 | auto &XMM1 = gRegs[xed_get_largest_enclosing_register(XED_REG_XMM1)]; 1835 | auto &XMM2 = gRegs[xed_get_largest_enclosing_register(XED_REG_XMM2)]; 1836 | auto &XMM3 = gRegs[xed_get_largest_enclosing_register(XED_REG_XMM3)]; 1837 | auto &XMM4 = gRegs[xed_get_largest_enclosing_register(XED_REG_XMM4)]; 1838 | auto &XMM5 = gRegs[xed_get_largest_enclosing_register(XED_REG_XMM5)]; 1839 | auto &XMM6 = gRegs[xed_get_largest_enclosing_register(XED_REG_XMM6)]; 1840 | auto &XMM7 = gRegs[xed_get_largest_enclosing_register(XED_REG_XMM7)]; 1841 | 1842 | asm("push %24;" 1843 | 1844 | "movdqu %0, %%xmm0;" 1845 | "movdqu %1, %%xmm1;" 1846 | "movdqu %2, %%xmm2;" 1847 | "movdqu %3, %%xmm3;" 1848 | "movdqu %4, %%xmm4;" 1849 | "movdqu %5, %%xmm5;" 1850 | "movdqu %6, %%xmm6;" 1851 | "movdqu %7, %%xmm7;" 1852 | 1853 | "xchg %8, %%rax;" 1854 | "xchg %9, %%rbx;" 1855 | "xchg %10, %%rcx;" 1856 | "xchg %11, %%rdx;" 1857 | "xchg %12, %%rbp;" 1858 | "xchg %13, %%rsi;" 1859 | "xchg %14, %%rdi;" 1860 | "xchg %15, %%r8;" 1861 | "xchg %16, %%r9;" 1862 | "xchg %17, %%r10;" 1863 | "xchg %18, %%r11;" 1864 | "xchg %19, %%r12;" 1865 | "xchg %20, %%r13;" 1866 | "xchg %21, %%r14;" 1867 | "xchg %22, %%r15;" 1868 | 1869 | "pushq %23;" 1870 | "popfq;" 1871 | 1872 | "fnclex;" 1873 | ".byte 0xff, 0x14, 0x24;" // `CALL QWORD PTR [RSP]`. 1874 | "fwait;" 1875 | "fnclex;" 1876 | 1877 | "pushfq;" 1878 | "popq %23;" 1879 | 1880 | "xchg %8, %%rax;" 1881 | "xchg %9, %%rbx;" 1882 | "xchg %10, %%rcx;" 1883 | "xchg %11, %%rdx;" 1884 | "xchg %12, %%rbp;" 1885 | "xchg %13, %%rsi;" 1886 | "xchg %14, %%rdi;" 1887 | "xchg %15, %%r8;" 1888 | "xchg %16, %%r9;" 1889 | "xchg %17, %%r10;" 1890 | "xchg %18, %%r11;" 1891 | "xchg %19, %%r12;" 1892 | "xchg %20, %%r13;" 1893 | "xchg %21, %%r14;" 1894 | "xchg %22, %%r15;" 1895 | 1896 | "movdqu %%xmm0, %0;" 1897 | "movdqu %%xmm1, %1;" 1898 | "movdqu %%xmm2, %2;" 1899 | "movdqu %%xmm3, %3;" 1900 | "movdqu %%xmm4, %4;" 1901 | "movdqu %%xmm5, %5;" 1902 | "movdqu %%xmm6, %6;" 1903 | "movdqu %%xmm7, %7;" 1904 | 1905 | "add $8, %%rsp;" 1906 | : 1907 | : "m"(XMM0), "m"(XMM1), "m"(XMM2), "m"(XMM3), "m"(XMM4), "m"(XMM5), 1908 | "m"(XMM6), "m"(XMM7), "m"(gRegs[XED_REG_RAX]), "m"(gRegs[XED_REG_RBX]), 1909 | "m"(gRegs[XED_REG_RCX]), "m"(gRegs[XED_REG_RDX]), 1910 | "m"(gRegs[XED_REG_RBP]), "m"(gRegs[XED_REG_RSI]), 1911 | "m"(gRegs[XED_REG_RDI]), "m"(gRegs[XED_REG_R8]), "m"(gRegs[XED_REG_R9]), 1912 | "m"(gRegs[XED_REG_R10]), "m"(gRegs[XED_REG_R11]), 1913 | "m"(gRegs[XED_REG_R12]), "m"(gRegs[XED_REG_R13]), 1914 | "m"(gRegs[XED_REG_R14]), "m"(gRegs[XED_REG_R15]), 1915 | "m"(gRegs[XED_REG_RFLAGS]), 1916 | "g"(reinterpret_cast(gExecArea))); 1917 | } 1918 | 1919 | static void ExecuteNativeAVX(void) { 1920 | #ifdef _WIN32 1921 | gExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; // TODO(pag): Implement this! 1922 | #else 1923 | gSignal = SIGILL; // TODO(pag): Implement this! 1924 | #endif //_WIN32 1925 | } 1926 | 1927 | static void ExecuteNativeAVX512(void) { 1928 | #ifdef _WIN32 1929 | gExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; // TODO(pag): Implement this! 1930 | #else 1931 | gSignal = SIGILL; // TODO(pag): Implement this! 1932 | #endif //_WIN32 1933 | } 1934 | 1935 | #ifdef _WIN32 1936 | LONG WINAPI VectoredHandler(struct _EXCEPTION_POINTERS *ExceptionInfo) { 1937 | #ifdef _WIN64 1938 | #define Cip ExceptionInfo->ContextRecord->Rip 1939 | #define Csp ExceptionInfo->ContextRecord->Rsp 1940 | #else 1941 | #define Cip ExceptionInfo->ContextRecord->Eip 1942 | #define Csp ExceptionInfo->ContextRecord->Esp 1943 | #endif //_WIN64 1944 | auto execArea = (uintptr_t)gExecArea; 1945 | if (Cip >= execArea && Cip < execArea + kPageSize) { 1946 | gExceptionCode = ExceptionInfo->ExceptionRecord->ExceptionCode; 1947 | // Emulate the RET 1948 | Cip = *(uintptr_t *)Csp; 1949 | Csp += sizeof(uintptr_t); 1950 | return EXCEPTION_CONTINUE_EXECUTION; 1951 | } else { 1952 | return EXCEPTION_CONTINUE_SEARCH; 1953 | } 1954 | #undef Csp 1955 | #undef Cip 1956 | } 1957 | #else 1958 | // Recover from a signal that was raised by executing the JITed instruction. 1959 | [[noreturn]] static void RecoverFromError(int sig) { 1960 | gSignal = sig; 1961 | siglongjmp(gRecoveryTarget, true); 1962 | } 1963 | #endif //_WIN32 1964 | 1965 | } // namespace 1966 | 1967 | Executor::Executor(size_t addr_size_, bool has_avx_, bool has_avx512_) 1968 | : addr_size(addr_size_), has_avx(has_avx_), has_avx512(has_avx512_) {} 1969 | 1970 | Executor::~Executor(void) {} 1971 | 1972 | bool Executor::Init(void) { 1973 | LockGuard locker(gExecutorLock); 1974 | if (gIsInitialized) { 1975 | return true; 1976 | } 1977 | 1978 | // Initialize the XED decode/encode tables. 1979 | xed_tables_init(); 1980 | 1981 | // Make `gExecArea` into a page-aligned address pointing to somewhere in 1982 | // `gExecArea_`. 1983 | auto ea_addr = reinterpret_cast(&(gExecArea_[0])); 1984 | auto ea_addr_rounded = (ea_addr + kPageSize - 1ULL) & ~(kPageSize - 1ULL); 1985 | gExecArea = &(gExecArea_[ea_addr_rounded - ea_addr]); 1986 | 1987 | // Map some portion of the `gExecArea_` memory to be RWX. The idea is that 1988 | // we want our executable area to be near our other data variables (e.g. 1989 | // register storage) so that we can access them via RIP-relative addressing. 1990 | #ifdef _WIN32 1991 | DWORD dwOldProtect = 0; 1992 | auto ret = VirtualProtect(gExecArea, kPageSize, PAGE_EXECUTE_READWRITE, 1993 | &dwOldProtect); 1994 | if (!ret) { 1995 | #else 1996 | auto ret = mmap(gExecArea, kPageSize, PROT_READ | PROT_WRITE | PROT_EXEC, 1997 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); 1998 | if (MAP_FAILED == gExecArea || gExecArea != ret) { 1999 | #endif //_WIN32 2000 | gExecArea = nullptr; 2001 | return false; 2002 | } 2003 | 2004 | gWriteBackFlags.flat = 0; 2005 | 2006 | memset(&gRegs, 0, sizeof(gRegs)); 2007 | 2008 | #ifdef _WIN32 2009 | #else 2010 | gSignalHandler.sa_handler = RecoverFromError; 2011 | gSignalHandler.sa_flags = SA_ONSTACK; 2012 | sigfillset(&(gSignalHandler.sa_mask)); 2013 | #endif //_WIN32 2014 | 2015 | gIsInitialized = true; 2016 | 2017 | return true; 2018 | } 2019 | 2020 | uintptr_t Executor::ComputeAddress(const char *, uintptr_t base, 2021 | uintptr_t index, uintptr_t scale, 2022 | uintptr_t displacement, size_t size, 2023 | MemRequestHint) const { 2024 | return base + (index * scale) + displacement; 2025 | } 2026 | 2027 | // Execute an instruction. 2028 | ExecutorStatus Executor::Execute(size_t max_num_executions) { 2029 | if (!max_num_executions) { 2030 | return ExecutorStatus::kGood; 2031 | } 2032 | 2033 | Data idata; 2034 | auto &bytes = idata.bytes; 2035 | 2036 | LockGuard locker(gExecutorLock); 2037 | 2038 | if (!gIsInitialized) { 2039 | return ExecutorStatus::kErrorNotInitialized; 2040 | } 2041 | 2042 | for (size_t num_executed = 0; num_executed < max_num_executions; 2043 | ++num_executed) { 2044 | gUsedRegs.reset(); 2045 | gModifiedRegs.reset(); 2046 | gStoreRegs.reset(); 2047 | gStackPtrAlias = XED_REG_INVALID; 2048 | gUsesFPU = false; 2049 | gUsesMMX = false; 2050 | 2051 | if (!ReadPC(this)) { 2052 | return ExecutorStatus::kErrorReadReg; 2053 | } 2054 | 2055 | const auto pc = ComputeAddress("CS", GetPC(this), 0, 0, 0, 8, 2056 | MemRequestHint::kReadExecutable); 2057 | 2058 | // the maximum possible instruction length given our memory model 2059 | size_t inst_length = 15; 2060 | for (; inst_length; --inst_length) { 2061 | if (ReadMem(pc, inst_length * 8, MemRequestHint::kReadExecutable, 2062 | idata)) { 2063 | // A read succeeded and we have computed the maximum fetch length 2064 | break; 2065 | } else { 2066 | #ifdef PYTHON_BINDINGS 2067 | // Ignore any exceptions generated by ReadMem 2068 | // If they are not ignored here, they will stack for every iteration 2069 | // of this loop and Python will get angry at us 2070 | if (PyErr_Occurred()) { 2071 | // TODO(artem): Debug print any 'unexpected' exceptions to warn the 2072 | // user they are ignored 2073 | PyErr_Clear(); 2074 | } 2075 | #endif 2076 | } 2077 | } 2078 | 2079 | if (!inst_length) { 2080 | return ExecutorStatus::kErrorReadInstMem; 2081 | } 2082 | 2083 | if (!DecodeInstruction(bytes, inst_length, addr_size)) { 2084 | return ExecutorStatus::kErrorDecode; 2085 | } 2086 | 2087 | // Reject some easy-to-reject stuff. 2088 | if (UsesUnsupportedFeatures(this)) { 2089 | return ExecutorStatus::kErrorUnsupportedFeatures; 2090 | } 2091 | 2092 | // Get only the flags we need. This treats the individual flags as if they 2093 | // are registers. 2094 | if (!ReadFlags(this)) { 2095 | return ExecutorStatus::kErrorReadFlags; 2096 | } 2097 | 2098 | if (!ReadRegisters(this)) { 2099 | return ExecutorStatus::kErrorReadReg; 2100 | } 2101 | 2102 | auto emulation_status = ExecutorStatus::kGood; 2103 | auto next_pc = GetNextPC(this); 2104 | 2105 | if (XED_CATEGORY_NOP != xed_decoded_inst_get_category(gXedd) && 2106 | XED_CATEGORY_WIDENOP != xed_decoded_inst_get_category(gXedd)) { 2107 | // Read in the FPU. We actually ignore the the embedded XMM registers 2108 | // entirely. 2109 | if (gUsesFPU && !this->ReadFPU(gFPU)) { 2110 | return ExecutorStatus::kErrorReadFPU; 2111 | } 2112 | 2113 | if (gUsesMMX) { 2114 | CopyMMXStateToFPU(this); 2115 | } 2116 | 2117 | // Read memory *after* reading in values of registers, so that we can 2118 | // figure out all the memory addresses to be read. 2119 | if (!ReadMemory(this)) { 2120 | return ExecutorStatus::kErrorReadMem; 2121 | } 2122 | 2123 | // Try to figure out what the target PC of the instruction is. If this 2124 | // is a control-flow instruction then the target PC is the target of 2125 | // the control-flow, otherwise it's just `next_pc`. 2126 | // 2127 | // Note: This might determine that we shouldn't execute the instruction. 2128 | // This will happen if figuring out the next/target program counter 2129 | // requires us to emulate the instruction. 2130 | if (!Emulate(this, next_pc, emulation_status)) { 2131 | if (ExecutorStatus::kGood != emulation_status) { 2132 | return emulation_status; 2133 | } else if (!EncodeInstruction(this)) { 2134 | return ExecutorStatus::kErrorExecute; 2135 | } else { 2136 | #ifdef _WIN32 2137 | gExceptionCode = 0; 2138 | auto hExceptionHandler = 2139 | AddVectoredExceptionHandler(1, VectoredHandler); 2140 | 2141 | LoadFPU(this); 2142 | if (has_avx512) { 2143 | ExecuteNativeAVX512(); 2144 | } else if (has_avx) { 2145 | ExecuteNativeAVX(); 2146 | } else { 2147 | ExecuteNative(); 2148 | } 2149 | StoreFPU(this); 2150 | 2151 | RemoveVectoredExceptionHandler(hExceptionHandler); 2152 | #else 2153 | gSignal = 0; 2154 | sigaction(SIGILL, &gSignalHandler, &gSIGILL); 2155 | sigaction(SIGBUS, &gSignalHandler, &gSIGBUS); 2156 | sigaction(SIGSEGV, &gSignalHandler, &gSIGSEGV); 2157 | sigaction(SIGFPE, &gSignalHandler, &gSIGFPE); 2158 | 2159 | LoadFPU(this); 2160 | if (!sigsetjmp(gRecoveryTarget, true)) { 2161 | if (has_avx512) { 2162 | ExecuteNativeAVX512(); 2163 | } else if (has_avx) { 2164 | ExecuteNativeAVX(); 2165 | } else { 2166 | ExecuteNative(); 2167 | } 2168 | } 2169 | StoreFPU(this); 2170 | 2171 | sigaction(SIGILL, &gSIGILL, nullptr); 2172 | sigaction(SIGBUS, &gSIGBUS, nullptr); 2173 | sigaction(SIGSEGV, &gSIGSEGV, nullptr); 2174 | sigaction(SIGFPE, &gSIGFPE, nullptr); 2175 | #endif //_WIN32 2176 | } 2177 | #ifdef _WIN32 2178 | switch (gExceptionCode) { 2179 | case 0: 2180 | break; // All good :-D 2181 | 2182 | case EXCEPTION_ACCESS_VIOLATION: 2183 | return ExecutorStatus::kErrorFault; 2184 | 2185 | case EXCEPTION_FLT_DENORMAL_OPERAND: 2186 | case EXCEPTION_FLT_DIVIDE_BY_ZERO: 2187 | case EXCEPTION_FLT_INEXACT_RESULT: 2188 | case EXCEPTION_FLT_INVALID_OPERATION: 2189 | case EXCEPTION_FLT_OVERFLOW: 2190 | case EXCEPTION_FLT_STACK_CHECK: 2191 | case EXCEPTION_FLT_UNDERFLOW: 2192 | return ExecutorStatus::kErrorFloatingPointException; 2193 | 2194 | case EXCEPTION_ILLEGAL_INSTRUCTION: 2195 | default: 2196 | return ExecutorStatus::kErrorExecute; 2197 | } 2198 | #else 2199 | switch (gSignal) { 2200 | case 0: 2201 | break; // All good :-D 2202 | 2203 | case SIGSEGV: 2204 | case SIGBUS: 2205 | return ExecutorStatus::kErrorFault; 2206 | 2207 | case SIGFPE: 2208 | return ExecutorStatus::kErrorFloatingPointException; 2209 | 2210 | case SIGILL: 2211 | default: 2212 | return ExecutorStatus::kErrorExecute; 2213 | } 2214 | #endif //_WIN32 2215 | } 2216 | 2217 | // Done before writing back the registers so that a failure of the 2218 | // instruction leaves no side-effects (on memory). This generally assumes 2219 | // that writing registers can't fail. 2220 | if (!WriteMemory(this)) { 2221 | return ExecutorStatus::kErrorWriteMem; 2222 | } 2223 | 2224 | if (gUsesFPU && !this->WriteFPU(gFPU)) { 2225 | return ExecutorStatus::kErrorWriteFPU; 2226 | } 2227 | 2228 | if (gUsesMMX) { 2229 | CopyMMXStateFromFPU(this); 2230 | } 2231 | } 2232 | 2233 | if (!WriteFlags(this)) { 2234 | return ExecutorStatus::kErrorWriteFlags; 2235 | } 2236 | 2237 | // Write back any registers that were read or written. 2238 | SetNextPC(this, next_pc); 2239 | 2240 | if (!WriteRegisters(this)) { 2241 | return ExecutorStatus::kErrorWriteReg; 2242 | } 2243 | } 2244 | 2245 | return ExecutorStatus::kGood; 2246 | } 2247 | 2248 | } // namespace microx 2249 | -------------------------------------------------------------------------------- /microx/Python.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Trail of Bits, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #pragma clang diagnostic push 24 | #pragma clang diagnostic ignored "-Wdeprecated-register" 25 | #define PY_SSIZE_T_CLEAN 26 | #include 27 | #include 28 | #pragma clang diagnostic pop 29 | 30 | #include "microx/Executor.h" 31 | 32 | #if PY_MAJOR_VERSION == 2 33 | #error "Python 2 builds are no longer supported" 34 | #elif PY_MAJOR_VERSION > 3 35 | #error "Building for an unsupported Python version" 36 | #endif 37 | 38 | namespace microx { 39 | namespace { 40 | 41 | struct PythonExecutorObject; 42 | 43 | // Extends the executor to invoke Python methods to satisfy requests for 44 | // data from the environment and respond with new values to place into the 45 | // environment. 46 | struct PythonExecutor : public Executor { 47 | PythonExecutor(PyObject *self_, unsigned addr_size); 48 | 49 | virtual ~PythonExecutor(void); 50 | 51 | bool ReadValue(PyObject *res, size_t num_bits, Data &val, 52 | const char *usage) const; 53 | 54 | uintptr_t ComputeAddress(const char *seg_name, uintptr_t base, 55 | uintptr_t index, uintptr_t scale, 56 | uintptr_t displacement, size_t size, 57 | MemRequestHint hint) const override; 58 | 59 | bool ReadReg(const char *name, size_t size, RegRequestHint hint, 60 | Data &val) const override; 61 | 62 | bool WriteReg(const char *name, size_t size, const Data &val) const override; 63 | 64 | bool ReadMem(uintptr_t addr, size_t size, MemRequestHint hint, 65 | Data &val) const override; 66 | 67 | bool WriteMem(uintptr_t addr, size_t size, const Data &val) const override; 68 | 69 | bool ReadFPU(FPU &val) const override; 70 | 71 | bool WriteFPU(const FPU &val) const override; 72 | 73 | PyObject *const self; 74 | mutable bool has_error{false}; 75 | mutable PyObject *error{nullptr}; 76 | mutable char error_message[512]; 77 | }; 78 | 79 | // Python representation for an instance of an executor. 80 | struct PythonExecutorObject { 81 | PyObject_HEAD PythonExecutor *executor; 82 | std::aligned_storage::type 83 | impl; 84 | }; 85 | 86 | static int Executor_init(PyObject *self_, PyObject *args, PyObject *) { 87 | unsigned addr_size = 0; 88 | if (!PyArg_ParseTuple(args, "I", &addr_size)) { 89 | return -1; 90 | } 91 | 92 | if (64 != addr_size && 32 != addr_size) { 93 | PyErr_Format( 94 | PyExc_ValueError, 95 | "Invalid address size %u. Expected 32 or 64 as the address size.", 96 | addr_size); 97 | return -1; 98 | } 99 | 100 | auto self = reinterpret_cast(self_); 101 | self->executor = new (&(self->impl)) PythonExecutor(self_, addr_size); 102 | return 0; 103 | } 104 | 105 | // A reference to the MicroxError 106 | static PyObject *MicroxError{nullptr}; 107 | 108 | // A reference to the InstructionDecodeError 109 | static PyObject *InstructionDecodeError{nullptr}; 110 | 111 | // A reference to the InstructionFetchError 112 | static PyObject *InstructionFetchError{nullptr}; 113 | 114 | // A reference to the AddressFaultError 115 | static PyObject *AddressFaultError{nullptr}; 116 | 117 | // A reference to the UnsupportedError 118 | static PyObject *UnsupportedError{nullptr}; 119 | 120 | // Initialize the exception references. 121 | static bool CreateExceptions(PyObject *microx) { 122 | MicroxError = PyErr_NewException("microx_core.MicroxError", nullptr, nullptr); 123 | if (nullptr == MicroxError) { 124 | return false; 125 | } 126 | PyModule_AddObject(microx, "MicroxError", MicroxError); 127 | 128 | InstructionDecodeError = PyErr_NewException( 129 | "microx_core.InstructionDecodeError", MicroxError, nullptr); 130 | if (nullptr == InstructionDecodeError) { 131 | return false; 132 | } 133 | PyModule_AddObject(microx, "InstructionDecodeError", InstructionDecodeError); 134 | 135 | InstructionFetchError = PyErr_NewException( 136 | "microx_core.InstructionFetchError", MicroxError, nullptr); 137 | if (nullptr == InstructionFetchError) { 138 | return false; 139 | } 140 | PyModule_AddObject(microx, "InstructionFetchError", InstructionFetchError); 141 | 142 | AddressFaultError = 143 | PyErr_NewException("microx_core.AddressFaultError", MicroxError, nullptr); 144 | if (nullptr == AddressFaultError) { 145 | return false; 146 | } 147 | PyModule_AddObject(microx, "AddressFaultError", AddressFaultError); 148 | 149 | UnsupportedError = 150 | PyErr_NewException("microx_core.UnsupportedError", MicroxError, nullptr); 151 | if (nullptr == UnsupportedError) { 152 | return false; 153 | } 154 | PyModule_AddObject(microx, "UnsupportedError", UnsupportedError); 155 | 156 | return true; 157 | } 158 | 159 | // Emulate an instruction. 160 | static PyObject *Executor_Execute(PyObject *self_, PyObject *args) { 161 | size_t num_execs = 0; 162 | 163 | if (!PyArg_ParseTuple(args, "K", &num_execs)) { 164 | PyErr_SetString(PyExc_TypeError, 165 | "Invalid value passed to 'execute' method."); 166 | return nullptr; 167 | } 168 | 169 | auto self = reinterpret_cast(self_); 170 | 171 | self->executor->has_error = false; 172 | self->executor->error = nullptr; 173 | switch (auto error_code = self->executor->Execute(num_execs)) { 174 | case ExecutorStatus::kGood: 175 | break; 176 | 177 | case ExecutorStatus::kErrorNotInitialized: 178 | PyErr_SetString(PyExc_ValueError, 179 | "Micro-execution environment is not initialized."); 180 | return nullptr; 181 | 182 | case ExecutorStatus::kErrorDecode: 183 | PyErr_SetString(InstructionDecodeError, "Unable to decode instruction."); 184 | return nullptr; 185 | case ExecutorStatus::kErrorUnsupportedFeatures: 186 | case ExecutorStatus::kErrorUnsupportedCFI: 187 | case ExecutorStatus::kErrorUnsupportedStack: 188 | PyErr_SetString(UnsupportedError, 189 | "Instruction is not supported by microx."); 190 | return nullptr; 191 | case ExecutorStatus::kErrorExecute: 192 | PyErr_SetString(MicroxError, "Unable to micro-execute instruction."); 193 | return nullptr; 194 | 195 | case ExecutorStatus::kErrorFault: 196 | PyErr_SetString(AddressFaultError, 197 | "Instruction faulted during micro-execution."); 198 | return nullptr; 199 | 200 | case ExecutorStatus::kErrorFloatingPointException: 201 | PyErr_SetString(PyExc_FloatingPointError, 202 | "Instruction faulted during micro-execution."); 203 | return nullptr; 204 | 205 | case ExecutorStatus::kErrorReadInstMem: 206 | if (!PyErr_Occurred() && !self->executor->error) { 207 | PyErr_SetString(InstructionFetchError, 208 | "Could not read instruction bytes."); 209 | } 210 | [[clang::fallthrough]]; 211 | 212 | default: 213 | if (PyErr_Occurred()) { 214 | // Do nothing, we've got an error already. 215 | 216 | } else if (self->executor->error) { 217 | PyErr_SetString(self->executor->error, self->executor->error_message); 218 | self->executor->error = nullptr; 219 | 220 | } else { 221 | PyErr_Format(PyExc_RuntimeError, 222 | "Unable to micro-execute instruction with status %u.", 223 | static_cast(error_code)); 224 | } 225 | return nullptr; 226 | } 227 | 228 | Py_RETURN_TRUE; 229 | } 230 | 231 | // Python representation for the type of an executor. 232 | static PyTypeObject gExecutorType; 233 | 234 | static PyMethodDef gModuleMethods[] = { 235 | {nullptr} /* Sentinel */ 236 | }; 237 | 238 | static PyMethodDef gExecutorMethods[] = { 239 | {"execute", Executor_Execute, METH_VARARGS, 240 | "Interpret a string of bytes as a machine instruction and perform a " 241 | "micro-execution of the instruction."}, 242 | {nullptr} /* Sentinel */ 243 | }; 244 | 245 | PythonExecutor::PythonExecutor(PyObject *self_, unsigned addr_size) 246 | : Executor(addr_size), self(self_), error(nullptr) {} 247 | 248 | PythonExecutor::~PythonExecutor(void) {} 249 | 250 | template 251 | static void WriteData(Data &data, T val) { 252 | *reinterpret_cast(&(data.bytes[0])) = val; 253 | } 254 | 255 | // Convert a Python value into a `Data` object. 256 | bool PythonExecutor::ReadValue(PyObject *res, size_t num_bits, Data &val, 257 | const char *usage) const { 258 | if (has_error) { 259 | return false; 260 | } 261 | 262 | const auto num_bytes = std::min(sizeof(val), (num_bits + 7) / 8); 263 | if (PyBytes_Check(res)) { 264 | auto res_size = static_cast(PyBytes_Size(res)); 265 | if (num_bytes != res_size) { 266 | has_error = true; 267 | error = PyExc_ValueError; 268 | snprintf(error_message, sizeof(error_message), 269 | "Incorrect number of bytes returned for value from '%s'; " 270 | "wanted %zu bytes but got %zu bytes.", 271 | usage, num_bytes, res_size); 272 | return false; 273 | } else { 274 | memcpy(&(val.bytes[0]), PyBytes_AsString(res), num_bytes); 275 | } 276 | 277 | } else if (PyLong_Check(res)) { 278 | auto long_res = reinterpret_cast(res); 279 | if (!_PyLong_AsByteArray(long_res, val.bytes, sizeof(val), true, false)) { 280 | return true; 281 | } 282 | if (PyErr_Occurred()) { 283 | has_error = true; 284 | } 285 | return false; 286 | } else if (PyLong_CheckExact(res)) { 287 | WriteData(val, PyLong_AsLong(res)); 288 | } else if (PyFloat_Check(res)) { 289 | if (32 == num_bits) { 290 | WriteData(val, static_cast(PyFloat_AsDouble(res))); 291 | } else { 292 | WriteData(val, PyFloat_AsDouble(res)); 293 | } 294 | } else { 295 | error = PyExc_TypeError; 296 | snprintf(error_message, sizeof(error_message), 297 | "Cannot convert type '%s' into a byte sequence from '%s'.", 298 | res->ob_type->tp_name, usage); 299 | return false; 300 | } 301 | memset(&(val.bytes[num_bytes]), 0, sizeof(val) - num_bytes); 302 | return true; 303 | } 304 | 305 | // Perform address computation. The segment register name is passed in so 306 | // that the extender can perform segmented address calculation. 307 | uintptr_t PythonExecutor::ComputeAddress(const char *seg_name, uintptr_t base, 308 | uintptr_t index, uintptr_t scale, 309 | uintptr_t displacement, size_t size, 310 | MemRequestHint hint) const { 311 | if (has_error) { 312 | return false; 313 | } 314 | 315 | char usage[256]; 316 | auto res = 317 | PyObject_CallMethod(self, "compute_address", "(s,K,K,K,K,I,i)", seg_name, 318 | base, index, scale, displacement, size / 8, hint); 319 | 320 | auto ret_addr = this->Executor::ComputeAddress(seg_name, base, index, scale, 321 | displacement, size, hint); 322 | 323 | if (res) { 324 | sprintf(usage, 325 | "compute_address(\"%s\", 0x%08" PRIx64 ", 0x%08" PRIx64 326 | ", 0x%08" PRIx64 ", 0x%08" PRIx64 ", %lu, %d)", 327 | seg_name, static_cast(base), static_cast(index), 328 | static_cast(scale), static_cast(displacement), 329 | size / 8, hint); 330 | Data val; 331 | auto ret = ReadValue(res, addr_size, val, usage); 332 | Py_DECREF(res); 333 | 334 | if (ret) { 335 | ret_addr = *reinterpret_cast(val.bytes); 336 | } 337 | 338 | } else if (PyErr_Occurred()) { 339 | has_error = true; 340 | } 341 | 342 | return ret_addr; 343 | } 344 | 345 | // Read a register from the environment. The name of the register should make 346 | // the size explicit. 347 | bool PythonExecutor::ReadReg(const char *name, size_t size, RegRequestHint hint, 348 | Data &val) const { 349 | if (has_error) { 350 | return false; 351 | } 352 | 353 | char usage[256]; 354 | auto res = PyObject_CallMethod(self, "read_register", "(s,i)", name, hint); 355 | if (res) { 356 | sprintf(usage, "read_register(\"%s\")", name); 357 | auto ret = ReadValue(res, size, val, usage); 358 | Py_DECREF(res); 359 | return ret; 360 | } else { 361 | return false; 362 | } 363 | } 364 | 365 | bool PythonExecutor::WriteReg(const char *name, size_t size, 366 | const Data &val) const { 367 | if (has_error) { 368 | return false; 369 | } 370 | 371 | auto ret = PyObject_CallMethod(self, "write_register", "(s,y#)", name, 372 | val.bytes, (size + 7) / 8); 373 | Py_XDECREF(ret); 374 | return nullptr != ret; 375 | } 376 | 377 | bool PythonExecutor::ReadMem(uintptr_t addr, size_t size, MemRequestHint hint, 378 | Data &val) const { 379 | if (has_error) { 380 | return false; 381 | } 382 | 383 | char usage[256]; 384 | auto res = 385 | PyObject_CallMethod(self, "read_memory", "(K,I,i)", addr, size / 8, hint); 386 | if (res) { 387 | sprintf(usage, "read_memory(0x%08" PRIx64 ", %lu, %d)", 388 | static_cast(addr), (size / 8), hint); 389 | auto ret = ReadValue(res, size, val, usage); 390 | Py_DECREF(res); 391 | return ret; 392 | } else { 393 | return false; 394 | } 395 | } 396 | 397 | bool PythonExecutor::WriteMem(uintptr_t addr, size_t size, 398 | const Data &val) const { 399 | if (has_error) { 400 | return false; 401 | } 402 | 403 | auto ret = PyObject_CallMethod(self, "write_memory", "(K,y#)", addr, 404 | val.bytes, size / 8); 405 | Py_XDECREF(ret); 406 | return nullptr != ret; 407 | } 408 | 409 | bool PythonExecutor::ReadFPU(FPU &val) const { 410 | if (has_error) { 411 | return false; 412 | } 413 | 414 | auto res = PyObject_CallMethod(self, "read_fpu", "()"); 415 | if (res) { 416 | if (!PyBytes_Check(res)) { 417 | Py_DECREF(res); 418 | has_error = true; 419 | error = PyExc_ValueError; 420 | snprintf(error_message, sizeof(error_message), 421 | "Expected 'read_fpu' to return string."); 422 | return false; 423 | } 424 | auto res_size = static_cast(PyBytes_Size(res)); 425 | if (sizeof(FPU) != res_size) { 426 | if (!error) { 427 | error = PyExc_ValueError; 428 | snprintf( 429 | error_message, sizeof(error_message), 430 | "Incorrect number of bytes returned for value from 'read_fpu'; " 431 | "wanted %zu bytes but got %zu bytes.", 432 | sizeof(FPU), res_size); 433 | } 434 | return false; 435 | } else { 436 | memcpy(&(val.bytes[0]), PyBytes_AsString(res), sizeof(FPU)); 437 | } 438 | } 439 | Py_XDECREF(res); 440 | return nullptr != res; 441 | } 442 | 443 | bool PythonExecutor::WriteFPU(const FPU &val) const { 444 | auto ret = 445 | PyObject_CallMethod(self, "write_fpu", "(y#)", val.bytes, sizeof(val)); 446 | Py_XDECREF(ret); 447 | return nullptr != ret; 448 | } 449 | 450 | struct module_state { 451 | PyObject *error; 452 | }; 453 | 454 | static struct PyModuleDef gMicroxModuleDef = { 455 | PyModuleDef_HEAD_INIT, 456 | "microx_core", 457 | "x86 and x86-64 micro-execution support.", 458 | sizeof(struct module_state), 459 | gModuleMethods, 460 | nullptr, 461 | nullptr, 462 | nullptr, 463 | nullptr}; 464 | 465 | PyMODINIT_FUNC PyInit_microx_core(void) { 466 | if (!Executor::Init()) { 467 | return nullptr; 468 | } 469 | 470 | auto microx = PyModule_Create(&gMicroxModuleDef); 471 | if (!microx) { 472 | return PyErr_NoMemory(); 473 | } 474 | 475 | if (!CreateExceptions(microx)) { 476 | return PyErr_NoMemory(); 477 | } 478 | 479 | // Initialize the `Executor` type. Easier to manually initialize the various 480 | // fields as opposed to trying to make sure the structure field initialization 481 | // is just right. 482 | memset(&gExecutorType, 0, sizeof(gExecutorType)); 483 | gExecutorType.tp_name = "microx_core.Executor"; 484 | gExecutorType.tp_basicsize = sizeof(PythonExecutorObject); 485 | gExecutorType.tp_alloc = PyType_GenericAlloc; 486 | gExecutorType.tp_new = PyType_GenericNew; 487 | gExecutorType.tp_init = Executor_init; 488 | gExecutorType.tp_flags = 489 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_TYPE_SUBCLASS; 490 | gExecutorType.tp_doc = "Instruction micro-executor."; 491 | gExecutorType.tp_methods = gExecutorMethods; 492 | gExecutorType.tp_base = &PyBaseObject_Type; 493 | if (0 != PyType_Ready(&gExecutorType)) { 494 | return nullptr; 495 | } 496 | 497 | Py_INCREF(&gExecutorType); 498 | PyModule_AddObject(microx, "Executor", 499 | reinterpret_cast(&gExecutorType)); 500 | 501 | return microx; 502 | } // namespace 503 | 504 | } // namespace 505 | } // namespace microx 506 | -------------------------------------------------------------------------------- /microx/XED.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Trail of Bits, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef MICROX_XED_H_ 18 | #define MICROX_XED_H_ 19 | 20 | #if defined(__clang__) 21 | #pragma clang diagnostic push 22 | #pragma clang diagnostic ignored "-Wsign-conversion" 23 | #pragma clang diagnostic ignored "-Wconversion" 24 | #pragma clang diagnostic ignored "-Wold-style-cast" 25 | #pragma clang diagnostic ignored "-Wswitch-enum" 26 | 27 | #elif defined(__GNUG__) 28 | #pragma GCC diagnostic push 29 | #pragma GCC diagnostic ignored "-Wsign-conversion" 30 | #pragma GCC diagnostic ignored "-Wconversion" 31 | #pragma GCC diagnostic ignored "-Wold-style-cast" 32 | #pragma GCC diagnostic ignored "-Wswitch-enum" 33 | 34 | #else 35 | #error "Unknown compiler." 36 | #endif 37 | 38 | extern "C" { 39 | #define XED_DLL 40 | #include 41 | } // extern C 42 | 43 | #if defined(__clang__) 44 | #pragma clang diagnostic pop 45 | #elif defined(__GNUG__) 46 | #pragma GCC diagnostic pop 47 | #endif 48 | 49 | #endif // MICROX_XED_H_ 50 | -------------------------------------------------------------------------------- /microx/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2019 Trail of Bits, Inc., all rights reserved. 3 | 4 | import collections 5 | 6 | from microx_core import Executor 7 | from microx_core import MicroxError 8 | from microx_core import ( # noqa: F401 9 | InstructionDecodeError, 10 | InstructionFetchError, 11 | AddressFaultError, 12 | UnsupportedError, 13 | ) 14 | 15 | LIST_LIKE = (str, bytes, bytearray, tuple, list) 16 | 17 | 18 | class Operations(object): 19 | def convert_to_byte_string(self, data, for_exe=False): 20 | if isinstance(data, int): 21 | data = data.to_bytes(8, byte_order="little") 22 | 23 | if for_exe: 24 | return bytes(data) 25 | else: 26 | return tuple(data) 27 | 28 | def convert_to_integer(self, val, for_exe=False): 29 | if isinstance(val, (str, bytes, bytearray)): 30 | val = int.from_bytes(val, byteorder="little") 31 | assert isinstance(val, int) 32 | return val 33 | 34 | def convert_to_byte(self, byte, for_exe=False): 35 | if isinstance(byte, LIST_LIKE): 36 | if isinstance(byte, str): 37 | byte = ord(byte[0]) 38 | elif isinstance(byte, (bytes, bytearray)): 39 | byte = byte[0] 40 | else: 41 | return self.convert_to_byte(byte[0], for_exe) 42 | return byte & 0xFF 43 | 44 | 45 | class ProxyOperations(Operations): 46 | def __init__(self, next): 47 | self._next = next 48 | 49 | def convert_to_byte(self, byte, for_exe=False): 50 | return self._next.convert_to_byte(byte, for_exe) 51 | 52 | def convert_to_byte_string(self, data, for_exe=False): 53 | return self._next.convert_to_byte_string(data, for_exe) 54 | 55 | def convert_to_integer(self, val, for_exe=False): 56 | return self._next.convert_to_integer(val, for_exe) 57 | 58 | 59 | class MemoryAccessException(MicroxError): 60 | pass 61 | 62 | 63 | class MemoryMap(object): 64 | def __init__(self, mapname=None): 65 | if mapname is None: 66 | # Default to a sane-ish map name 67 | self.__name = "map_{:08x}-{:08x}".format(self.base(), self.limit()) 68 | else: 69 | # Assume they picked a sane name 70 | self.__name = mapname 71 | 72 | def get_name(self): 73 | return self.__name 74 | 75 | def set_name(self, value): 76 | self.__name = value 77 | 78 | def del_name(self): 79 | del self.__name 80 | 81 | name = property(get_name, set_name, del_name, "This mapping's human readable name") 82 | 83 | def addresses(self): 84 | i = 0 85 | base = self.base() 86 | limit = self.limit() 87 | while (base + i) < limit: 88 | yield base + i 89 | i += 1 90 | 91 | def can_read(self, byte_addr): 92 | return False 93 | 94 | def can_write(self, byte_addr): 95 | return False 96 | 97 | def can_execute(self, byte_addr): 98 | return False 99 | 100 | def base(self): 101 | return 0 102 | 103 | def limit(self): 104 | return 0 105 | 106 | def load_byte(self, addr): 107 | raise MemoryAccessException("Can't load byte from address {:08x}".format(addr)) 108 | 109 | def load_word(self, addr): 110 | raise MemoryAccessException("Can't load word from address {:08x}".format(addr)) 111 | 112 | def load_dword(self, addr): 113 | raise MemoryAccessException("Can't load dword from address {:08x}".format(addr)) 114 | 115 | def load_qword(self, addr): 116 | raise MemoryAccessException("Can't load qword from address {:08x}".format(addr)) 117 | 118 | def load_bytes(self, addr, num_bytes): 119 | raise MemoryAccessException( 120 | "Can't load {} bytes from address {:08x}".format(num_bytes, addr) 121 | ) 122 | 123 | def store_byte(self, addr, val): 124 | raise MemoryAccessException("Can't store byte to address {:08x}".format(addr)) 125 | 126 | def store_word(self, addr, val): 127 | raise MemoryAccessException("Can't store word to address {:08x}".format(addr)) 128 | 129 | def store_dword(self, addr, val): 130 | raise MemoryAccessException("Can't store dword to address {:08x}".format(addr)) 131 | 132 | def store_qword(self, addr, val): 133 | raise MemoryAccessException("Can't store qword to address {:08x}".format(addr)) 134 | 135 | def store_bytes(self, addr, data): 136 | raise MemoryAccessException( 137 | "Can't store {} bytes to address {:08x}".format(len(data), addr) 138 | ) 139 | 140 | 141 | class ProxyMemoryMap(MemoryMap): 142 | def __init__(self, next): 143 | self._next = next 144 | 145 | def can_read(self, byte_addr): 146 | return self._next.can_read(byte_addr) 147 | 148 | def can_write(self, byte_addr): 149 | return self._next.can_write(byte_addr) 150 | 151 | def can_execute(self, byte_addr): 152 | return self._next.can_execute(byte_addr) 153 | 154 | def base(self): 155 | return self._next.base() 156 | 157 | def limit(self): 158 | return self._next.limit() 159 | 160 | def load_byte(self, addr): 161 | return self._next.load_byte(addr) 162 | 163 | def load_word(self, addr): 164 | return self._next.load_word(addr) 165 | 166 | def load_dword(self, addr): 167 | return self._next.load_dword(addr) 168 | 169 | def load_qword(self, addr): 170 | return self._next.load_qword(addr) 171 | 172 | def load_bytes(self, addr, num_bytes): 173 | return self._next.load_bytes(addr, num_bytes) 174 | 175 | def store_byte(self, addr, val): 176 | return self._next.store_byte(addr, val) 177 | 178 | def store_word(self, addr, val): 179 | return self._next.store_word(addr, val) 180 | 181 | def store_dword(self, addr, val): 182 | return self._next.store_dword(addr, val) 183 | 184 | def store_qword(self, addr, val): 185 | return self._next.store_qword(addr, val) 186 | 187 | def store_bytes(self, addr, data): 188 | return self._next.store_bytes(addr, data) 189 | 190 | 191 | class PermissionedMemoryMap(MemoryMap): 192 | def __init__( 193 | self, 194 | ops, 195 | base, 196 | limit, 197 | can_read=True, 198 | can_write=True, 199 | can_execute=False, 200 | mapname=None, 201 | ): 202 | assert base < limit 203 | self._ops = ops 204 | self._base = base 205 | self._limit = limit 206 | self._can_read = can_read 207 | self._can_write = can_write 208 | self._can_execute = can_execute 209 | super(PermissionedMemoryMap, self).__init__(mapname) 210 | 211 | def can_read(self, byte_addr): 212 | return self._can_read and self._base <= byte_addr < self._limit 213 | 214 | def can_write(self, byte_addr): 215 | return self._can_write and self._base <= byte_addr < self._limit 216 | 217 | def can_execute(self, byte_addr): 218 | return self._can_execute and self._base <= byte_addr < self._limit 219 | 220 | def base(self): 221 | return self._base 222 | 223 | def limit(self): 224 | return self._limit 225 | 226 | 227 | class ArrayMemoryMap(PermissionedMemoryMap): 228 | def __init__( 229 | self, ops, base, limit, can_read=True, can_write=True, can_execute=False 230 | ): 231 | super(ArrayMemoryMap, self).__init__( 232 | ops, base, limit, can_read, can_write, can_execute 233 | ) 234 | self._data = [0] * (limit - base) 235 | 236 | def load_byte(self, addr): 237 | offset = addr - self._base 238 | return self._data[offset : (offset + 1)] 239 | 240 | def load_word(self, addr): 241 | offset = addr - self._base 242 | return self._data[offset : (offset + 2)] 243 | 244 | def load_dword(self, addr): 245 | offset = addr - self._base 246 | return self._data[offset : (offset + 4)] 247 | 248 | def load_qword(self, addr): 249 | offset = addr - self._base 250 | return self._data[offset : (offset + 8)] 251 | 252 | def load_bytes(self, addr, num_bytes): 253 | offset = addr - self._base 254 | return self._data[offset : (offset + num_bytes)] 255 | 256 | def store_byte(self, addr, data): 257 | if isinstance(data, LIST_LIKE): 258 | self.store_bytes(addr, data[:1]) 259 | else: 260 | self.store_bytes(addr, data) 261 | 262 | def store_word(self, addr, data): 263 | if isinstance(data, LIST_LIKE): 264 | self.store_bytes(addr, data[:2]) 265 | else: 266 | self.store_bytes(addr, data) 267 | 268 | def store_dword(self, addr, data): 269 | if isinstance(data, LIST_LIKE): 270 | self.store_bytes(addr, data[:4]) 271 | else: 272 | self.store_bytes(addr, data) 273 | 274 | def store_qword(self, addr, data): 275 | if isinstance(data, LIST_LIKE): 276 | self.store_bytes(addr, data[:8]) 277 | else: 278 | self.store_bytes(addr, data) 279 | 280 | def store_bytes(self, addr, data): 281 | offset = addr - self._base 282 | if not isinstance(data, LIST_LIKE): 283 | data = self._ops.convert_to_byte_string(data) 284 | for b in data: 285 | if isinstance(b, str): 286 | b = ord(b) 287 | self._data[offset] = b 288 | offset += 1 289 | 290 | 291 | class Thread(object): 292 | REG_HINT_NONE = 0 293 | REG_HINT_GENERAL = 1 294 | REG_HINT_PROGRAM_COUNTER = 2 295 | REG_HINT_CONDITION_CODE = 3 296 | REG_HINT_WRITE_BACK = 4 297 | REG_HINT_MEMORY_BASE_ADDRESS = 5 298 | REG_HINT_MEMORY_INDEX_ADDRESS = 6 299 | REG_HINT_MEMORY_SEGMENT_ADDRESS = 7 300 | 301 | def __init__(self, ops): 302 | self._ops = ops 303 | 304 | def read_register(self, reg_name, hint): 305 | raise NotImplementedError("Abstract") 306 | 307 | def write_register(self, reg_name, value): 308 | raise NotImplementedError("Abstract") 309 | 310 | def read_fpu(self): 311 | raise NotImplementedError("Abstract") 312 | 313 | def write_fpu(self, new_fpu_data): 314 | raise NotImplementedError("Abstract") 315 | 316 | 317 | class EmptyThread(Thread): 318 | def __init__(self, ops): 319 | super(EmptyThread, self).__init__(ops) 320 | self._regs = collections.defaultdict(int) 321 | self._fpu_data = b"\0" * 512 322 | 323 | def read_register(self, reg_name, hint): 324 | return self._regs[reg_name] 325 | 326 | def write_register(self, reg_name, value): 327 | self._regs[reg_name] = value 328 | 329 | def read_fpu(self): 330 | return self._fpu_data 331 | 332 | def write_fpu(self, new_fpu_data): 333 | self._fpu_data = new_fpu_data 334 | 335 | 336 | class Memory(object): 337 | 338 | MEM_HINT_READ_ONLY = 0 339 | MEM_HINT_READ_EXECUTABLE = 1 340 | MEM_HINT_WRITE_ONLY = 2 341 | MEM_HINT_READ_WRITE = 3 342 | MEM_HINT_ADDRESS_GEN = 4 343 | 344 | def __init__(self, ops, address_size, page_shift=12): 345 | assert address_size in (32, 64) 346 | assert 0 < page_shift < 32 347 | self._ops = ops 348 | self._address_size = address_size 349 | self._address_mask = (1 << self._address_size) - 1 350 | self._page_shift = page_shift 351 | self._memory_maps = collections.defaultdict(MemoryMap) 352 | 353 | def find_hole(self, hole_size): 354 | """Finds a hole of size `hole_size` between current mappings""" 355 | 356 | paged_hole = hole_size >> self._page_shift 357 | if 0 == paged_hole: 358 | paged_hole = 1 359 | 360 | MIN_ADDR = 0x10000 >> self._page_shift 361 | MAX_ADDR = 0xFFFF0000 >> self._page_shift 362 | 363 | mm = sorted(self._memory_maps.keys()) 364 | 365 | lowest = MIN_ADDR 366 | for addr in mm: 367 | if addr > lowest and lowest - addr > paged_hole: 368 | return lowest << self._page_shift 369 | lowest = addr 370 | 371 | if addr < MAX_ADDR and MAX_ADDR - addr > paged_hole: 372 | return addr << self._page_shift 373 | 374 | return None 375 | 376 | def find_maps_by_name(self, map_name): 377 | 378 | found_maps = set() 379 | # TODO(artem): This iterates over every page in the 380 | # memory map we have. This is wasteful since there 381 | # are much fewer unique maps. We can try keeping track 382 | # of unique maps as we add them, and disallow overlapping maps 383 | for (k, v) in self._memory_maps.items(): 384 | if map_name == v.name: 385 | found_maps.add(v) 386 | 387 | return found_maps 388 | 389 | def _find_map(self, byte_addr): 390 | return self._memory_maps[(byte_addr & self._address_mask) >> self._page_shift] 391 | 392 | def _crosses_pages(self, addr, num_bytes): 393 | mmap = self._find_map(addr) 394 | i = 1 395 | while i < num_bytes: 396 | if self._find_map(addr + i) != mmap: 397 | return True 398 | i += 1 399 | return False 400 | 401 | def address_size_bits(self): 402 | return self._address_size 403 | 404 | def add_map(self, mmap): 405 | base, limit = mmap.base(), mmap.limit() 406 | assert ((base >> self._page_shift) << self._page_shift) == base 407 | while base < limit: 408 | self._memory_maps[(base & self._address_mask) >> self._page_shift] = mmap 409 | base += 1 << self._page_shift 410 | 411 | def can_read(self, byte_addr): 412 | return self._find_map(byte_addr).can_read(byte_addr) 413 | 414 | def can_write(self, byte_addr): 415 | return self._find_map(byte_addr).can_write(byte_addr) 416 | 417 | def can_execute(self, byte_addr): 418 | return self._find_map(byte_addr).can_execute(byte_addr) 419 | 420 | def load(self, addr, num_bytes): 421 | if num_bytes in (1, 2, 4, 8) and not self._crosses_pages(addr, num_bytes): 422 | if 1 == num_bytes: 423 | return self._find_map(addr).load_byte(addr) 424 | elif 2 == num_bytes: 425 | return self._find_map(addr).load_word(addr) 426 | elif 4 == num_bytes: 427 | return self._find_map(addr).load_dword(addr) 428 | else: 429 | return self._find_map(addr).load_qword(addr) 430 | else: 431 | reads = [] 432 | i = 0 433 | while i < num_bytes: 434 | byte_addr = addr + i 435 | i += 1 436 | reads.append(self._find_map(byte_addr).load_bytes(byte_addr, 1)[0]) 437 | return self._ops.convert_to_byte_string(reads) 438 | 439 | def store(self, addr, data): 440 | num_bytes = len(data) 441 | if num_bytes in (1, 2, 4, 8) and not self._crosses_pages(addr, num_bytes): 442 | if 1 == num_bytes: 443 | self._find_map(addr).store_byte(addr, data) 444 | elif 2 == num_bytes: 445 | self._find_map(addr).store_word(addr, data) 446 | elif 4 == num_bytes: 447 | self._find_map(addr).store_dword(addr, data) 448 | else: 449 | self._find_map(addr).store_qword(addr, data) 450 | else: 451 | i = 0 452 | while i < num_bytes: 453 | byte_addr = addr + i 454 | self._find_map(byte_addr).store_bytes(byte_addr, data[i : i + 1]) 455 | i += 1 456 | 457 | 458 | class ProxyThread(Thread): 459 | def __init__(self, next): 460 | super(ProxyThread, self).__init__(next._ops) 461 | assert isinstance(next, Thread) 462 | self._next = next 463 | 464 | def read_register(self, reg_name, hint): 465 | return self._next.read_register(reg_name, hint) 466 | 467 | def write_register(self, reg_name, value): 468 | return self._next.write_register(reg_name, value) 469 | 470 | def read_fpu(self): 471 | return self._next.read_fpu() 472 | 473 | def write_fpu(self, new_fpu_data): 474 | return self._next.write_fpu(new_fpu_data) 475 | 476 | 477 | class Process(Executor): 478 | MEM_READ_HINTS = ( 479 | Memory.MEM_HINT_READ_ONLY, 480 | Memory.MEM_HINT_READ_EXECUTABLE, 481 | Memory.MEM_HINT_READ_WRITE, 482 | ) 483 | 484 | MEM_WRITE_HINTS = (Memory.MEM_HINT_WRITE_ONLY, Memory.MEM_HINT_READ_WRITE) 485 | 486 | MEM_EXEC_HINTS = (Memory.MEM_HINT_READ_EXECUTABLE,) 487 | 488 | def __init__(self, ops, memory): 489 | assert isinstance(memory, Memory) 490 | super(Process, self).__init__(memory.address_size_bits()) 491 | self._memory = memory 492 | self._thread = None 493 | self._ops = ops 494 | 495 | def execute(self, thread, max_num_instructions=1): 496 | assert 0 < max_num_instructions 497 | assert isinstance(thread, Thread) 498 | 499 | self._thread = thread 500 | try: 501 | super(Process, self).execute(max_num_instructions) 502 | 503 | # Approximate TSC as 1 cycle / instruction. 504 | tsc = self.read_register("TSC", thread.REG_HINT_NONE) 505 | self.write_register("TSC", tsc + max_num_instructions) 506 | finally: 507 | self._thread = None 508 | 509 | def read_register(self, reg_name, hint): 510 | return self._ops.convert_to_integer( 511 | self._thread.read_register(reg_name, hint), for_exe=True 512 | ) 513 | 514 | def write_register(self, reg_name, val): 515 | self._thread.write_register(reg_name, self._ops.convert_to_integer(val)) 516 | 517 | def compute_address(self, seg_name, base_addr, index, scale, disp, size, hint): 518 | seg_base = 0 519 | if hint != Memory.MEM_HINT_ADDRESS_GEN: 520 | seg_base = self.read_register( 521 | "{}_BASE".format(seg_name), Thread.REG_HINT_MEMORY_SEGMENT_ADDRESS 522 | ) 523 | seg_base = seg_base & self._memory._address_mask 524 | return seg_base + base_addr + (index * scale) + disp 525 | 526 | def read_memory(self, addr, num_bytes, hint): 527 | check_read = hint in self.MEM_READ_HINTS 528 | check_write = hint in self.MEM_WRITE_HINTS 529 | check_exec = hint in self.MEM_EXEC_HINTS 530 | 531 | # Check permissions 532 | i = 0 533 | while i < num_bytes: 534 | byte_addr = addr + i 535 | i += 1 536 | 537 | if check_read: 538 | if not self._memory.can_read(byte_addr): 539 | raise MemoryAccessException( 540 | "Address {:08x} is not readable".format(byte_addr) 541 | ) 542 | 543 | if check_write: 544 | if not self._memory.can_write(byte_addr): 545 | raise MemoryAccessException( 546 | "Address {:08x} is not writable".format(byte_addr) 547 | ) 548 | 549 | if check_exec: 550 | if not self._memory.can_execute(byte_addr): 551 | raise MemoryAccessException( 552 | "Address {:08x} is not executable".format(byte_addr) 553 | ) 554 | 555 | return self._ops.convert_to_byte_string( 556 | self._memory.load(addr, num_bytes), for_exe=True 557 | ) 558 | 559 | def write_memory(self, addr, data): 560 | data = self._ops.convert_to_byte_string(data) 561 | self._memory.store(addr, data) 562 | 563 | # The FPU is treated as an opaque blob of memory. 564 | def read_fpu(self): 565 | return self._ops.convert_to_byte_string(self._thread.read_fpu(), for_exe=True) 566 | 567 | def write_fpu(self, fpu): 568 | self._thread.write_fpu(self._ops.convert_to_byte_string(fpu, for_exe=True)) 569 | -------------------------------------------------------------------------------- /microx/include/microx/Executor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Trail of Bits, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef MICROX_EXECUTOR_H_H_ 18 | #define MICROX_EXECUTOR_H_H_ 19 | 20 | #include 21 | #include 22 | 23 | namespace microx { 24 | 25 | struct alignas(16) Data { 26 | uint8_t bytes[512 / 8]; // Largest register is 512 bits. 27 | }; 28 | 29 | enum class ExecutorStatus { 30 | kGood, 31 | kErrorNotInitialized, 32 | kErrorDecode, 33 | kErrorUnsupportedFeatures, 34 | kErrorUnsupportedCFI, 35 | kErrorUnsupportedStack, 36 | kErrorExecute, 37 | kErrorFault, 38 | kErrorFloatingPointException, 39 | kErrorReadFlags, 40 | kErrorWriteFlags, 41 | kErrorReadReg, 42 | kErrorWriteReg, 43 | kErrorReadMem, 44 | kErrorReadInstMem, 45 | kErrorWriteMem, 46 | kErrorReadFPU, 47 | kErrorWriteFPU 48 | }; 49 | 50 | enum class RegRequestHint { 51 | kNone, 52 | kGeneral, 53 | kProgramCounter, 54 | kConditionCode, 55 | kWriteBack, 56 | kMemoryBaseAddress, 57 | kMemoryIndexAddress, 58 | kMemorySegmentAddress 59 | }; 60 | 61 | enum class MemRequestHint { 62 | kReadOnly, 63 | kReadExecutable, 64 | kWriteOnly, 65 | kReadWrite, 66 | kAddressGeneration 67 | }; 68 | 69 | // TODO(pag): Assumes little endian. 70 | struct float80_t final { 71 | uint8_t data[10]; 72 | } __attribute__((packed)); 73 | 74 | struct vec128_t final { 75 | uint8_t bytes[16]; 76 | } __attribute__((packed)); 77 | 78 | union FPUStatusWord final { 79 | uint16_t flat; 80 | struct { 81 | uint16_t ie : 1; // Invalid operation. 82 | uint16_t de : 1; // Denormal operand. 83 | uint16_t ze : 1; // Zero divide. 84 | uint16_t oe : 1; // Overflow. 85 | uint16_t ue : 1; // Underflow. 86 | uint16_t pe : 1; // Precision. 87 | uint16_t sf : 1; // Stack fault. 88 | uint16_t es : 1; // Error summary status. 89 | uint16_t c0 : 1; // Part of condition code. 90 | uint16_t c1 : 1; // Used for a whole lot of stuff. 91 | uint16_t c2 : 1; // Part of condition code. 92 | uint16_t top : 3; // Stack pointer. 93 | uint16_t c3 : 1; // Part of condition code. 94 | uint16_t b : 1; // Busy. 95 | } __attribute__((packed)); 96 | } __attribute__((packed)); 97 | 98 | static_assert(2 == sizeof(FPUStatusWord), 99 | "Invalid structure packing of `FPUFlags`."); 100 | 101 | #ifdef __clang__ 102 | 103 | enum FPUPrecisionControl : uint16_t { 104 | kPrecisionSingle, 105 | kPrecisionReserved, 106 | kPrecisionDouble, 107 | kPrecisionExtended 108 | }; 109 | 110 | enum FPURoundingControl : uint16_t { 111 | kFPURoundToNearestEven, 112 | kFPURoundDownNegInf, 113 | kFPURoundUpInf, 114 | kFPURoundToZero 115 | }; 116 | 117 | enum FPUInfinityControl : uint16_t { kInfinityProjective, kInfinityAffine }; 118 | 119 | #else 120 | using FPUPrecisionControl = uint16_t; 121 | using FPURoundingControl = uint16_t; 122 | using FPUInfinityControl = uint16_t; 123 | #endif 124 | 125 | union FPUControlWord final { 126 | uint16_t flat; 127 | struct { 128 | uint16_t im : 1; // Invalid Operation. 129 | uint16_t dm : 1; // Denormalized Operand. 130 | uint16_t zm : 1; // Zero Divide. 131 | uint16_t om : 1; // Overflow. 132 | uint16_t um : 1; // Underflow. 133 | uint16_t pm : 1; // Precision. 134 | uint16_t _rsvd0 : 2; 135 | FPUPrecisionControl pc : 2; // bit 8 136 | FPURoundingControl rc : 2; 137 | FPUInfinityControl x : 1; 138 | uint16_t _rsvd1 : 3; 139 | } __attribute__((packed)); 140 | } __attribute__((packed)); 141 | 142 | static_assert(2 == sizeof(FPUControlWord), 143 | "Invalid structure packing of `FPUControl`."); 144 | 145 | struct FPUStackElem final { 146 | union { 147 | float80_t st; 148 | struct { 149 | uint64_t mmx; 150 | uint16_t infinity; // When an MMX register is used, this is all 1s. 151 | } __attribute__((packed)); 152 | } __attribute__((packed)); 153 | uint8_t _rsvd[6]; 154 | } __attribute__((packed)); 155 | 156 | static_assert(0 == __builtin_offsetof(FPUStackElem, st), 157 | "Invalid structure packing of `FPUStackElem::st`."); 158 | 159 | static_assert(0 == __builtin_offsetof(FPUStackElem, mmx), 160 | "Invalid structure packing of `FPUStackElem::mmx`."); 161 | 162 | static_assert(8 == __builtin_offsetof(FPUStackElem, infinity), 163 | "Invalid structure packing of `FPUStackElem::st`."); 164 | 165 | static_assert(10 == __builtin_offsetof(FPUStackElem, _rsvd[0]), 166 | "Invalid structure packing of `FPUStackElem::st`."); 167 | 168 | static_assert(16 == sizeof(FPUStackElem), 169 | "Invalid structure packing of `FPUStackElem`."); 170 | 171 | union FPUControlStatus { 172 | uint32_t flat; 173 | struct { 174 | uint32_t ie : 1; // Invalid operation. 175 | uint32_t de : 1; // Denormal flag. 176 | uint32_t ze : 1; // Divide by zero. 177 | uint32_t oe : 1; // Overflow. 178 | uint32_t ue : 1; // Underflow. 179 | uint32_t pe : 1; // Precision. 180 | uint32_t daz : 1; // Denormals are zero. 181 | uint32_t im : 1; // Invalid operation. 182 | uint32_t dm : 1; // Denormal mask. 183 | uint32_t zm : 1; // Divide by zero mask. 184 | uint32_t om : 1; // Overflow mask. 185 | uint32_t um : 1; // Underflow mask. 186 | uint32_t pm : 1; // Precision mask. 187 | uint32_t rn : 1; // Round negative. 188 | uint32_t rp : 1; // Round positive. 189 | uint32_t fz : 1; // Flush to zero. 190 | uint32_t _rsvd : 16; 191 | } __attribute__((packed)); 192 | } __attribute__((packed)); 193 | 194 | static_assert(4 == sizeof(FPUControlStatus), 195 | "Invalid structure packing of `SSEControlStatus`."); 196 | 197 | #ifdef __clang__ 198 | enum FPUTag : uint16_t { 199 | kFPUTagNonZero, 200 | kFPUTagZero, 201 | kFPUTagSpecial, // Invalid (NaN, unsupported), infinity, denormal. 202 | kFPUTagEmpty 203 | }; 204 | 205 | enum FPUAbridgedTag : uint8_t { kFPUAbridgedTagEmpty, kFPUAbridgedTagValid }; 206 | #else 207 | using FPUTag = uint16_t; 208 | using FPUAbridgedTag = uint8_t; 209 | #endif 210 | 211 | // Note: Stored in top-of-stack order. 212 | union FPUTagWord final { 213 | uint16_t flat; 214 | struct { 215 | FPUTag tag0 : 2; 216 | FPUTag tag1 : 2; 217 | FPUTag tag2 : 2; 218 | FPUTag tag3 : 2; 219 | FPUTag tag4 : 2; 220 | FPUTag tag5 : 2; 221 | FPUTag tag6 : 2; 222 | FPUTag tag7 : 2; 223 | } __attribute__((packed)); 224 | } __attribute__((packed)); 225 | 226 | static_assert(sizeof(FPUTagWord) == 2, 227 | "Invalid structure packing of `TagWord`."); 228 | 229 | // Note: Stored in physical order. 230 | union FPUAbridgedTagWord final { 231 | uint8_t flat; 232 | struct { 233 | FPUAbridgedTag r0 : 1; 234 | FPUAbridgedTag r1 : 1; 235 | FPUAbridgedTag r2 : 1; 236 | FPUAbridgedTag r3 : 1; 237 | FPUAbridgedTag r4 : 1; 238 | FPUAbridgedTag r5 : 1; 239 | FPUAbridgedTag r6 : 1; 240 | FPUAbridgedTag r7 : 1; 241 | } __attribute__((packed)); 242 | } __attribute__((packed)); 243 | 244 | static_assert(sizeof(FPUAbridgedTagWord) == 1, 245 | "Invalid structure packing of `FPUAbridgedTagWord`."); 246 | 247 | #ifdef __clang__ 248 | enum RequestPrivilegeLevel : uint16_t { 249 | kRPLRingZero = 0, 250 | kRPLRingOne = 1, 251 | kRPLRingTwo = 2, 252 | kRPLRingThree = 3 253 | }; 254 | 255 | enum TableIndicator : uint16_t { 256 | kGlobalDescriptorTable = 0, 257 | kLocalDescriptorTable = 1 258 | }; 259 | #else 260 | using RequestPrivilegeLevel = uint16_t; 261 | using TableIndicator = uint16_t; 262 | #endif 263 | 264 | union SegmentSelector final { 265 | uint16_t flat; 266 | struct { 267 | RequestPrivilegeLevel rpi : 2; 268 | TableIndicator ti : 1; 269 | uint16_t index : 13; 270 | } __attribute__((packed)); 271 | } __attribute__((packed)); 272 | 273 | static_assert(sizeof(SegmentSelector) == 2, 274 | "Invalid packing of `union SegmentSelector`."); 275 | 276 | // FPU register state that conforms with `FSAVE` and `FRSTOR`. 277 | struct FpuFSAVE { 278 | FPUControlWord cwd; 279 | uint16_t _rsvd0; 280 | FPUStatusWord swd; 281 | uint16_t _rsvd1; 282 | FPUTagWord ftw; 283 | uint16_t fop; // Last instruction opcode. 284 | uint32_t ip; // Offset in segment of last non-control FPU instruction. 285 | SegmentSelector cs; // Code segment associated with `ip`. 286 | uint16_t _rsvd2; 287 | uint32_t dp; // Operand address. 288 | SegmentSelector ds; // Data segment associated with `dp`. 289 | uint16_t _rsvd3; 290 | FPUStackElem st[8]; 291 | } __attribute__((packed)); 292 | 293 | // FPU register state that conforms with `FXSAVE` and `FXRSTOR`. 294 | struct FpuFXSAVE { 295 | FPUControlWord cwd; 296 | FPUStatusWord swd; 297 | FPUAbridgedTagWord ftw; 298 | uint8_t _rsvd0; 299 | uint16_t fop; // Last instruction opcode. 300 | uint32_t ip; // Offset in segment of last non-control FPU instruction. 301 | SegmentSelector cs; // Code segment associated with `ip`. 302 | uint16_t _rsvd1; 303 | uint32_t dp; // Operand address. 304 | SegmentSelector ds; // Data segment associated with `dp`. 305 | uint16_t _rsvd2; 306 | FPUControlStatus mxcsr; 307 | FPUControlStatus mxcsr_mask; 308 | FPUStackElem st[8]; 309 | vec128_t xmm[16]; 310 | } __attribute__((packed)); 311 | 312 | // FPU register state that conforms with `FXSAVE64` and `FXRSTOR64`. 313 | struct FpuFXSAVE64 { 314 | FPUControlWord cwd; 315 | FPUStatusWord swd; 316 | FPUAbridgedTagWord ftw; 317 | uint8_t _rsvd0; 318 | uint16_t fop; // Last instruction opcode. 319 | uint64_t ip; // Offset in segment of last non-control FPU instruction. 320 | uint64_t dp; // Operand address. 321 | FPUControlStatus mxcsr; 322 | FPUControlStatus mxcsr_mask; 323 | FPUStackElem st[8]; 324 | vec128_t xmm[16]; 325 | } __attribute__((packed)); 326 | 327 | // FP register state that conforms with `FXSAVE` and `FXSAVE64`. 328 | union alignas(16) FPU final { 329 | uint8_t bytes[512]; // X87 FPU needs 512 bytes of state. 330 | 331 | struct : public FpuFSAVE { 332 | uint8_t _padding0[512 - sizeof(FpuFSAVE)]; 333 | } __attribute__((packed)) fsave; 334 | 335 | struct : public FpuFXSAVE { 336 | uint8_t _padding0[512 - sizeof(FpuFXSAVE)]; 337 | } __attribute__((packed)) fxsave32; 338 | 339 | struct : public FpuFXSAVE64 { 340 | uint8_t _padding0[512 - sizeof(FpuFXSAVE64)]; 341 | } __attribute__((packed)) fxsave64; 342 | } __attribute__((packed)); 343 | 344 | static_assert(512 == sizeof(FPU), "Invalid structure packing of `FPU`."); 345 | 346 | class Executor { 347 | public: 348 | Executor(size_t addr_size_, bool has_avx_ = false, bool has_avx512_ = false); 349 | 350 | virtual ~Executor(void); 351 | 352 | static bool Init(void); 353 | 354 | ExecutorStatus Execute(size_t max_num_executions = 1); 355 | 356 | virtual uintptr_t ComputeAddress(const char *seg_name, uintptr_t base, 357 | uintptr_t index, uintptr_t scale, 358 | uintptr_t displacement, size_t size, 359 | MemRequestHint hint) const; 360 | 361 | virtual bool ReadReg(const char *name, size_t size, RegRequestHint hint, 362 | Data &val) const = 0; 363 | 364 | virtual bool WriteReg(const char *name, size_t size, 365 | const Data &val) const = 0; 366 | 367 | virtual bool ReadMem(uintptr_t addr, size_t size, MemRequestHint hint, 368 | Data &val) const = 0; 369 | 370 | virtual bool WriteMem(uintptr_t addr, size_t size, const Data &val) const = 0; 371 | 372 | virtual bool ReadFPU(FPU &val) const = 0; 373 | 374 | virtual bool WriteFPU(const FPU &val) const = 0; 375 | 376 | const size_t addr_size; 377 | const bool has_avx; 378 | const bool has_avx512; 379 | 380 | private: 381 | Executor(void) = delete; 382 | Executor(const Executor &) = delete; 383 | Executor(const Executor &&) = delete; 384 | Executor &operator=(const Executor &) = delete; 385 | Executor &operator=(const Executor &&) = delete; 386 | }; 387 | 388 | } // namespace microx 389 | 390 | #endif // MICROX_EXECUTOR_H_H_ 391 | -------------------------------------------------------------------------------- /scripts/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2016 Peter Goodman (peter@trailofbits.com), all rights reserved. 3 | set -euo pipefail 4 | 5 | # microx git repo root directory 6 | DIR="$( dirname "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" )" 7 | PYTHON="${PYTHON:-python3}" 8 | XED_MFILE_FLAGS="${XED_MFILE_FLAGS-"--static"}" 9 | export CC="${CC:-$(command -v cc)}" 10 | export CXX="${CXX:-$(command -v c++)}" 11 | export AR="${AR:-$(command -v ar)}" 12 | 13 | function download_and_install_xed() 14 | { 15 | pushd "$DIR"/third_party/src 16 | 17 | if [[ ! -e xed ]] ; then 18 | git clone --depth 1 --single-branch --branch main https://github.com/intelxed/xed.git 19 | else 20 | pushd xed 21 | git pull origin main 22 | popd 23 | fi; 24 | 25 | if [[ ! -e mbuild ]] ; then 26 | git clone --depth 1 --single-branch --branch main https://github.com/intelxed/mbuild.git 27 | else 28 | pushd mbuild 29 | git pull origin main 30 | popd 31 | fi; 32 | 33 | pushd xed 34 | 35 | #rm -rf ./obj 36 | rm -rf ./microx-kit 37 | 38 | "${PYTHON}" ./mfile.py install \ 39 | --install-dir ./microx-kit \ 40 | --extra-flags="-fPIC" \ 41 | --ar="${AR}" \ 42 | --cc="${CC}" \ 43 | --cxx="${CXX}" \ 44 | ${XED_MFILE_FLAGS} 45 | 46 | rm -rf "$DIR"/third_party/include/xed 47 | cp -r ./microx-kit/include/xed "$DIR"/third_party/include/ 48 | rm -f "$DIR"/third_party/lib/libxed* 49 | cp -r ./microx-kit/lib/* "$DIR"/third_party/lib/ 50 | 51 | popd 52 | popd 53 | 54 | if [[ "$OSTYPE" == "darwin"* ]]; then 55 | find "$DIR"/third_party/lib -name 'libxed*.dylib' -exec install_name_tool -id {} {} \; 56 | fi 57 | } 58 | 59 | mkdir -p "$DIR"/third_party/src 60 | mkdir -p "$DIR"/third_party/lib 61 | mkdir -p "$DIR"/third_party/include 62 | 63 | download_and_install_xed 64 | -------------------------------------------------------------------------------- /scripts/release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # release: perform the chore work required for a microx release 4 | 5 | set -eo pipefail 6 | 7 | function installed { 8 | cmd=$(command -v "${1}") 9 | 10 | [[ -n "${cmd}" ]] && [[ -f "${cmd}" ]] 11 | return ${?} 12 | } 13 | 14 | function die { 15 | >&2 echo "Barf: ${*}" 16 | exit 1 17 | } 18 | 19 | # Fail early if we don't have the expected tools. 20 | installed git || die "Missing dependency: git" 21 | 22 | # Fail early if `git status` reports any untracked changes. 23 | [[ -n $(git status -s) ]] && die "Untracked changes in repo" 24 | 25 | # Next, check the VERSION in version and make sure it doesn't already have a git tag. 26 | [[ -f ./VERSION ]] || die "Missing VERSION file; wrong directory?" 27 | version=v$(<./VERSION) 28 | [[ -n $(git tag -l "${version}") ]] && die "git tag for ${version} already exists!" 29 | 30 | # Next, craft a tag for the current HEAD. Push both the current commit and the tag. 31 | git tag "${version}" 32 | git push 33 | git push origin "${version}" 34 | 35 | echo OK 36 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright 2019 Trail of Bits, all rights reserved. 3 | 4 | import os 5 | 6 | import setuptools 7 | 8 | here = os.path.dirname(__file__) 9 | 10 | with open(os.path.join(here, "README.md")) as f: 11 | README = f.read() 12 | 13 | with open(os.path.join(here, "VERSION")) as f: 14 | VERSION = f.read().strip() 15 | 16 | microx_core = setuptools.Extension( 17 | "microx_core", 18 | include_dirs=["microx/include", "third_party/include"], 19 | sources=["microx/Executor.cpp", "microx/Python.cpp"], 20 | extra_compile_args=["-DPYTHON_BINDINGS=1", "-std=gnu++11", "-g3", "-O0"], 21 | libraries=["xed"], 22 | library_dirs=["third_party/lib"], 23 | runtime_library_dirs=["third_party/lib"], 24 | ) 25 | 26 | setuptools.setup( 27 | name="microx", 28 | version=VERSION, 29 | description="x86 and x86_64 micro-executor.", 30 | long_description=README, 31 | long_description_content_type="text/markdown", 32 | author="Peter Goodman", 33 | author_email="peter@trailofbits.com", 34 | url="https://github.com/trailofbits/microx", 35 | license="Apache-2.0", 36 | py_modules=["microx.__init__"], 37 | ext_modules=[microx_core], 38 | python_requires=">=3.7", 39 | ) 40 | --------------------------------------------------------------------------------