├── .github └── workflows │ └── build.yaml ├── .gitignore ├── .gitmodules ├── Android └── README.md ├── CMakeLists.txt ├── CONTRIBUTING.md ├── LICENSE ├── Linux ├── README.md ├── debugger.cpp ├── debugger.h ├── elffile.cpp ├── elffile.h ├── procmaps.cpp ├── procmaps.h ├── syscallhook.cpp └── syscallhook.h ├── README.md ├── Windows ├── debugger.cpp ├── debugger.h ├── winunwind.cpp └── winunwind.h ├── arch ├── arm64 │ ├── arm64_assembler.cpp │ ├── arm64_assembler.h │ ├── arm64_helpers.cpp │ ├── arm64_helpers.h │ ├── arm64_litecov.cpp │ └── reg.h └── x86 │ ├── reg.h │ ├── x86_assembler.cpp │ ├── x86_assembler.h │ ├── x86_helpers.cpp │ ├── x86_helpers.h │ └── x86_litecov.cpp ├── assembler.h ├── common.cpp ├── common.h ├── coverage.cpp ├── coverage.h ├── hook.cpp ├── hook.h ├── hook.md ├── instruction.h ├── litecov.cpp ├── litecov.h ├── macOS ├── .gitignore ├── README.md ├── debugger.cpp ├── debugger.h ├── dyld_cache_map_parser.cpp ├── dyld_cache_map_parser.h ├── machtarget.cpp ├── machtarget.h ├── mig.defs ├── unwindmacos.cpp └── unwindmacos.h ├── sslhook-main.cpp ├── sslhook.cpp ├── sslhook.h ├── third_party ├── CMakeLists.txt ├── llvm │ ├── LEB128.h │ ├── LICENSE.TXT │ └── libunwind │ │ ├── CompactUnwinder.hpp │ │ └── dwarf2.h └── reil │ └── reil │ └── aarch64 │ ├── decoder.cpp │ ├── decoder.h │ └── printer.cpp ├── tinyinst-coverage.cpp ├── tinyinst.cpp ├── tinyinst.h └── unwind.h /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ${{ matrix.os }} 8 | 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | os: [windows-2019, macos-latest] 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | with: 17 | submodules: true 18 | 19 | - name: Create Build Environment 20 | # Some projects don't allow in-source building, so create a separate build directory 21 | # We'll use this as our working directory for all subsequent commands 22 | run: cmake -E make_directory ${{runner.workspace}}/TinyInst/build 23 | 24 | - name: Configure CMake 25 | working-directory: ${{runner.workspace}}/TinyInst/build 26 | run: cmake .. 27 | 28 | - name: Build 29 | working-directory: ${{runner.workspace}}/TinyInst/build 30 | run: cmake --build . --config Release 31 | 32 | - name: Test 33 | working-directory: ${{runner.workspace}}/TinyInst/build 34 | shell: bash 35 | # Execute tests defined by the CMake configuration. 36 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 37 | run: ctest -C Release 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .DS_Store 3 | 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/xed"] 2 | path = third_party/xed 3 | url = https://github.com/intelxed/xed 4 | [submodule "third_party/mbuild"] 5 | path = third_party/mbuild 6 | url = https://github.com/intelxed/mbuild 7 | -------------------------------------------------------------------------------- /Android/README.md: -------------------------------------------------------------------------------- 1 | # TinyInst on Android 2 | 3 | ``` 4 | Copyright 2023 Google LLC 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | https://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | ``` 18 | ## Cross-compiling TinyInst for Android 19 | 20 | ``` 21 | mkdir build-android 22 | cd build-android 23 | cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/android/ndk/build/cmake/android.toolchain.cmake -DANDROID_NDK=/path/to/android/ndk -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM= .. 24 | cmake --build . --config Release 25 | ``` 26 | 27 | ## Limitations on Android 28 | 29 | * Only ARM64 is supported at this time. 30 | * Android has no support for Linux-style shared memory, which TinyInst needs to share a coverage buffer between TinyInst and the target process. As a temporary replacement, shared memory in TinyInst on Android is currently implemented by memory-mapping a file in `/data/local/tmp/`. While it may seem like this will be slow, note that TinyInst only writes to coverage buffer when new coverage is encountered. Thus, this workaround should not have a large impact after the initial warmup. 31 | * `-generate_unwind` is not implemented on Android. For targets that throw C++ exceptions, `-patch_return_addresses` should be used instead. 32 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | cmake_minimum_required(VERSION "3.1") 16 | set (CMAKE_CXX_STANDARD 17) 17 | 18 | # Determine whether TinyInst should be build for arm64 or x86 19 | if (WIN32) 20 | # no support for arm64 on Windows 21 | elseif(${ANDROID_ABI} MATCHES "arm64") 22 | set(ARCHITECTURE ${ANDROID_ABI}) 23 | add_definitions(-DARM64) 24 | elseif(NOT DEFINED ${ARCHITECTURE}) 25 | EXECUTE_PROCESS(COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE ARCHITECTURE) 26 | if(${ARCHITECTURE} MATCHES arm64) 27 | add_definitions(-DARM64) 28 | endif() 29 | endif() 30 | 31 | add_subdirectory(third_party) 32 | 33 | project("tinyinst") 34 | 35 | include_directories(${CMAKE_CURRENT_BINARY_DIR}/third_party/obj/wkit/include) 36 | 37 | if(${ARCHITECTURE} MATCHES arm64) 38 | set (arch_specific_files 39 | arch/arm64/reg.h 40 | arch/arm64/arm64_helpers.h 41 | arch/arm64/arm64_helpers.cpp 42 | arch/arm64/arm64_assembler.h 43 | arch/arm64/arm64_assembler.cpp 44 | arch/arm64/arm64_litecov.cpp 45 | ) 46 | else() 47 | set (arch_specific_files 48 | arch/x86/reg.h 49 | arch/x86/x86_helpers.h 50 | arch/x86/x86_helpers.cpp 51 | arch/x86/x86_assembler.h 52 | arch/x86/x86_assembler.cpp 53 | arch/x86/x86_litecov.cpp 54 | ) 55 | endif() 56 | 57 | set (cross_platform_files 58 | common.h 59 | common.cpp 60 | assembler.h 61 | instruction.h 62 | tinyinst.h 63 | tinyinst.cpp 64 | coverage.h 65 | coverage.cpp 66 | litecov.h 67 | litecov.cpp 68 | unwind.h 69 | hook.cpp 70 | hook.h 71 | ) 72 | 73 | if (WIN32) 74 | set (platform_specific_files 75 | Windows/debugger.h 76 | Windows/debugger.cpp 77 | Windows/winunwind.cpp 78 | Windows/winunwind.h 79 | ) 80 | elseif (APPLE) 81 | if (${ARCHITECTURE} MATCHES arm64) 82 | set(CMAKE_REQUIRED_LINK_OPTIONS "-arch;arm64") 83 | endif() 84 | 85 | add_custom_command( 86 | OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/macOS/mig_client.c 87 | ${CMAKE_CURRENT_SOURCE_DIR}/macOS/mig_server.c 88 | ${CMAKE_CURRENT_SOURCE_DIR}/macOS/mig_client.h 89 | ${CMAKE_CURRENT_SOURCE_DIR}/macOS/mig_server.h 90 | COMMAND mig -user ${CMAKE_CURRENT_SOURCE_DIR}/macOS/mig_client.c 91 | -server ${CMAKE_CURRENT_SOURCE_DIR}/macOS/mig_server.c 92 | -header ${CMAKE_CURRENT_SOURCE_DIR}/macOS/mig_client.h 93 | -sheader ${CMAKE_CURRENT_SOURCE_DIR}/macOS/mig_server.h 94 | ${CMAKE_CURRENT_SOURCE_DIR}/macOS/mig.defs 95 | COMMENT "Generating Mig files" 96 | ) 97 | 98 | set (platform_specific_files 99 | macOS/debugger.h 100 | macOS/debugger.cpp 101 | macOS/machtarget.h 102 | macOS/machtarget.cpp 103 | macOS/mig_client.h 104 | macOS/mig_client.c 105 | macOS/mig_server.h 106 | macOS/mig_server.c 107 | macOS/unwindmacos.cpp 108 | macOS/unwindmacos.h 109 | macOS/dyld_cache_map_parser.cpp 110 | macOS/dyld_cache_map_parser.h 111 | ) 112 | elseif (UNIX) 113 | set (platform_specific_files 114 | Linux/debugger.h 115 | Linux/debugger.cpp 116 | Linux/elffile.h 117 | Linux/elffile.cpp 118 | Linux/procmaps.h 119 | Linux/procmaps.cpp 120 | Linux/syscallhook.h 121 | Linux/syscallhook.cpp 122 | ) 123 | endif() 124 | 125 | add_library(tinyinst STATIC 126 | ${arch_specific_files} 127 | ${arch_specific_files} 128 | ${platform_specific_files} 129 | ${cross_platform_files} 130 | ) 131 | 132 | target_include_directories(tinyinst PUBLIC 133 | ${CMAKE_CURRENT_SOURCE_DIR} 134 | ${CMAKE_CURRENT_BINARY_DIR}/third_party/obj/wkit/include 135 | ) 136 | 137 | if(${ARCHITECTURE} MATCHES arm64) 138 | add_dependencies(tinyinst reil) 139 | target_link_libraries(tinyinst reil) 140 | else() 141 | add_dependencies(tinyinst xed) 142 | if (WIN32) 143 | target_link_libraries(tinyinst 144 | ${CMAKE_CURRENT_BINARY_DIR}/third_party/obj/wkit/lib/xed.lib 145 | Dbghelp.lib 146 | ) 147 | else () 148 | target_link_libraries(tinyinst 149 | ${CMAKE_CURRENT_BINARY_DIR}/third_party/obj/wkit/lib/libxed.a 150 | ) 151 | endif() 152 | endif() 153 | 154 | project("sslhook") 155 | 156 | add_executable(sslhook 157 | sslhook-main.cpp 158 | sslhook.h 159 | sslhook.cpp 160 | ) 161 | 162 | target_link_libraries(sslhook tinyinst) 163 | 164 | project("litecov") 165 | 166 | add_executable(litecov 167 | tinyinst-coverage.cpp 168 | ) 169 | 170 | target_link_libraries(litecov tinyinst) 171 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows [Google's Open Source Community 28 | Guidelines](https://opensource.google/conduct/). 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Linux/README.md: -------------------------------------------------------------------------------- 1 | # TinyInst on Linux 2 | 3 | ``` 4 | Copyright 2023 Google LLC 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | https://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | ``` 18 | 19 | ## Notes and limitations on Linux 20 | 21 | * x86, x86-64 and ARM64 binaries are supported, running on a 64-bit OS (support for a 32-bit Linux OS is not planned at this time). 22 | * 32-bit x86 binaries running on a 64-bit Linux OS commonly read return addresses from the stack. This breaks TinyInst unless `-patch_return_addresses` is used. A warning to enable the flag will be printed if such a case is encountered. 23 | * `-generate_unwind` is not implemented on Linux. For targets that throw C++ exceptions, `-patch_return_addresses` should be used instead. 24 | 25 | -------------------------------------------------------------------------------- /Linux/debugger.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Google LLC 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 | https ://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 DEBUGGER_H 18 | #define DEBUGGER_H 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "common.h" 32 | #include "procmaps.h" 33 | 34 | #ifdef ARM64 35 | #include "arch/arm64/reg.h" 36 | #else 37 | #include "arch/x86/reg.h" 38 | #endif 39 | 40 | #ifdef __ANDROID__ 41 | typedef int __ptrace_request; 42 | #endif 43 | 44 | #ifdef ARM64 45 | #define SYSCALL_RETURN_REGISTER X0 46 | #define SYSCALL_NUMER_REGISTER X8 47 | #else 48 | #define SYSCALL_RETURN_REGISTER RAX 49 | #define SYSCALL_NUMER_REGISTER SYSCALL_RAX 50 | #endif 51 | 52 | class SyscallHook; 53 | 54 | enum DebuggerStatus { 55 | DEBUGGER_NONE, 56 | DEBUGGER_CONTINUE, 57 | DEBUGGER_PROCESS_EXIT, 58 | DEBUGGER_TARGET_START, 59 | DEBUGGER_TARGET_END, 60 | DEBUGGER_CRASHED, 61 | DEBUGGER_HANGED, 62 | DEBUGGER_ATTACHED, 63 | }; 64 | 65 | enum MemoryProtection { 66 | READONLY, 67 | READWRITE, 68 | READEXECUTE, 69 | READWRITEEXECUTE 70 | }; 71 | 72 | enum CallingConvention { 73 | CALLCONV_DEFAULT, 74 | }; 75 | 76 | struct LoadedModule { 77 | std::string path; 78 | uint64_t address; 79 | }; 80 | 81 | bool operator< (LoadedModule const& lhs, LoadedModule const& rhs); 82 | 83 | #ifdef ARM64 84 | typedef user_pt_regs arch_reg_struct; 85 | #else 86 | typedef user_regs_struct arch_reg_struct; 87 | #endif 88 | 89 | struct SavedRegisters { 90 | arch_reg_struct saved_context; 91 | }; 92 | 93 | class Debugger { 94 | 95 | public: 96 | virtual void Init(int argc, char **argv); 97 | 98 | DebuggerStatus Run(int argc, char **argv, uint32_t timeout); 99 | DebuggerStatus Run(char *cmd, uint32_t timeout); 100 | DebuggerStatus Continue(uint32_t timeout); 101 | DebuggerStatus Kill(); 102 | DebuggerStatus Attach(unsigned int pid, uint32_t timeout); 103 | 104 | bool IsTargetAlive() { return is_target_alive; } 105 | bool IsTargetFunctionDefined() { return target_function_defined; } 106 | 107 | uint64_t GetTargetReturnValue() { return target_return_value; } 108 | 109 | enum ExceptionType { 110 | BREAKPOINT, 111 | ACCESS_VIOLATION, 112 | ILLEGAL_INSTRUCTION, 113 | STACK_OVERFLOW, 114 | OTHER 115 | }; 116 | 117 | struct Exception { 118 | ExceptionType type; 119 | void *ip; 120 | bool maybe_write_violation; 121 | bool maybe_execute_violation; 122 | void *access_address; 123 | }; 124 | 125 | Exception GetLastException() { 126 | return last_exception; 127 | } 128 | 129 | protected: 130 | class SharedMemory { 131 | public: 132 | std::string name; 133 | uint64_t local_address; 134 | uint64_t remote_address; 135 | int local_fd; 136 | int remote_fd; 137 | uint64_t size; 138 | }; 139 | 140 | enum TargetEndDetection { 141 | RETADDR_STACK_OVERWRITE, 142 | RETADDR_BREAKPOINT 143 | }; 144 | 145 | virtual void OnProcessCreated(); 146 | virtual void OnEntrypoint(); 147 | virtual void OnModuleLoaded(void *module, char *module_name); 148 | virtual void OnModuleUnloaded(void *module); 149 | virtual void OnProcessExit() {}; 150 | virtual void OnTargetMethodReached() {} 151 | virtual void OnSyscall(int thread_id, uint64_t syscall_number) {} 152 | virtual void OnSyscallEnd(int thread_id) {} 153 | 154 | virtual bool OnException(Exception *exception_record) { return false; } 155 | virtual void OnCrashed(Exception *exception_record) { } 156 | 157 | void *GetModuleEntrypoint(void *base_address); 158 | 159 | void GetImageSize(void *base_address, size_t *min_address, size_t *max_address); 160 | 161 | void ExtractCodeRanges(void *module_base, 162 | size_t min_address, 163 | size_t max_address, 164 | std::list *executable_ranges, 165 | size_t *code_size, 166 | bool do_protect = true); 167 | 168 | void ProtectCodeRanges(std::list *executable_ranges); 169 | 170 | void PatchPointersRemote(void *base_address, std::unordered_map& search_replace); 171 | void PatchPointersRemote(size_t min_address, size_t max_address, std::unordered_map& search_replace); 172 | template 173 | void PatchPointersRemoteT(size_t min_address, size_t max_address, std::unordered_map& search_replace); 174 | 175 | 176 | virtual size_t GetTranslatedAddress(size_t address) { return address; } 177 | 178 | void RemoteRead(void *address, void *buffer, size_t size); 179 | void RemoteWrite(void *address, const void *buffer, size_t size); 180 | 181 | size_t GetRegister(Register r); 182 | void SetRegister(Register r, size_t value); 183 | 184 | void *GetTargetMethodAddress() { return target_address; } 185 | 186 | void SaveRegisters(SavedRegisters* registers); 187 | void RestoreRegisters(SavedRegisters* registers); 188 | 189 | void* RemoteAllocate(size_t size, MemoryProtection protection, bool use_shared_memory = false); 190 | void RemoteFree(void *address, size_t size); 191 | void RemoteProtect(void *address, size_t size, MemoryProtection protection); 192 | 193 | void *RemoteAllocateNear(uint64_t region_min, 194 | uint64_t region_max, 195 | size_t size, 196 | MemoryProtection protection, 197 | bool use_shared_memory = false); 198 | 199 | void *GetSymbolAddress(void *base_address, const char *symbol_name); 200 | 201 | size_t GetReturnAddress(); 202 | void SetReturnAddress(size_t value); 203 | 204 | void GetFunctionArguments(uint64_t *arguments, size_t num_arguments, uint64_t sp, CallingConvention callconv); 205 | void SetFunctionArguments(uint64_t *arguments, size_t num_arguments, uint64_t sp, CallingConvention callconv); 206 | 207 | void SetSyscallArguments(uint64_t *args, size_t num_args); 208 | void GetSyscallArguments(uint64_t *args, size_t num_args); 209 | 210 | int GetCurrentThreadID() { return current_pid; } 211 | 212 | void RegisterSyscallHook(SyscallHook *hook); 213 | 214 | int32_t child_ptr_size = sizeof(void *); 215 | 216 | bool child_entrypoint_reached; 217 | bool target_reached; 218 | bool target_function_defined; 219 | 220 | Exception last_exception; 221 | 222 | private: 223 | std::set loaded_modules; 224 | std::set threads; 225 | 226 | struct Breakpoint { 227 | void *address; 228 | int type; 229 | #ifdef ARM64 230 | uint32_t original_opcode; 231 | #else 232 | unsigned char original_opcode; 233 | #endif 234 | }; 235 | std::list breakpoints; 236 | 237 | DebuggerStatus DebugLoop(uint32_t timeout); 238 | DebuggerStatus HandleStopped(int status); 239 | 240 | void AddBreakpoint(void *address, int type); 241 | void DeleteBreakpoints(); 242 | int HandleDebuggerBreakpoint(); 243 | template 244 | int GetLoadedModulesT(std::set &modules, bool set_breakpoint); 245 | int GetLoadedModules(std::set &modules, bool set_breakpoint); 246 | void OnLoadedModulesChanged(bool set_breakpoint); 247 | void SetThreadOptions(pid_t pid); 248 | 249 | uint64_t* GetRegisterHelper(Register r, arch_reg_struct *regs); 250 | 251 | uint64_t GetSegment(uint64_t header, uint32_t type, uint64_t *segment_size); 252 | int ReadCString(uint64_t address, char *str, size_t size); 253 | void GetElfIdentAndOffset(uint64_t header, uint8_t *e_ident, uint64_t *pie_offset); 254 | void SetupModules(); 255 | void SetupSyscalls(); 256 | void OnNotifier(); 257 | 258 | void GetModuleFilename(void *base_address, std::string *name); 259 | 260 | void *RemoteMmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); 261 | int RemoteMunmap(void *addr, size_t len); 262 | int RemoteOpen(const char *pathname, int flags, mode_t mode); 263 | int RemoteMprotect(void *addr, size_t len, int prot); 264 | void RemoteSyscall(); 265 | 266 | int GetProt(MemoryProtection protection); 267 | 268 | void *GetTargetAddress(void *base_address); 269 | void HandleTargetReachedInternal(); 270 | void HandleTargetEnded(); 271 | 272 | char **GetEnvp(); 273 | 274 | Register ArgumentToRegister(int arg); 275 | 276 | void *RemoteAllocateBefore(uint64_t min_address, 277 | uint64_t max_address, 278 | size_t size, 279 | MemoryProtection protection, 280 | std::vector &map_entries, 281 | SharedMemory *shm); 282 | 283 | void *RemoteAllocateAfter(uint64_t min_address, 284 | uint64_t max_address, 285 | size_t size, 286 | MemoryProtection protection, 287 | std::vector &map_entries, 288 | SharedMemory *shm); 289 | 290 | void *RemoteAllocateAt(uint64_t address, uint64_t size, MemoryProtection protection, SharedMemory *shm); 291 | 292 | void ReadStack(void *stack_addr, uint64_t *buffer, size_t numitems); 293 | void WriteStack(void *stack_addr, uint64_t *buffer, size_t numitems); 294 | 295 | void GetArchRegs(arch_reg_struct *regs); 296 | void SetArchRegs(arch_reg_struct *regs); 297 | 298 | void GetThreads(int pid); 299 | 300 | void CleanupTarget(); 301 | 302 | void ResolveSymlinks(std::string *path); 303 | 304 | void Watchdog(); 305 | friend void *debugger_watchdog_thread(void *arg); 306 | 307 | SharedMemory *CreateSharedMemory(size_t size); 308 | void ClearSharedMemory(); 309 | std::list shared_memory; 310 | unsigned int curr_shm_index; 311 | 312 | volatile bool watchdog_enabled; 313 | volatile uint64_t watchdog_timeout_time; 314 | volatile bool killed_by_watchdog; 315 | std::mutex watchdog_mutex; 316 | 317 | std::unordered_map symlink_cache; 318 | 319 | // todo initialize 320 | int proc_mem_fd; 321 | std::list additional_env; 322 | pid_t main_pid, current_pid; 323 | uint64_t main_binary_header; 324 | uint64_t entrypoint_address; 325 | std::string main_binary_path; 326 | std::string main_binary_name; 327 | uint64_t rendezvous_address; 328 | uint64_t syscall_address; 329 | uint64_t debugger_allocated_memory; 330 | 331 | bool trace_debug_events; 332 | 333 | bool loop_mode; 334 | 335 | char target_module[PATH_MAX]; 336 | char target_method[PATH_MAX]; 337 | 338 | int target_num_args; 339 | uint64_t target_offset; 340 | 341 | void *target_address; 342 | void *saved_sp; 343 | void *saved_return_address; 344 | void **saved_args; 345 | TargetEndDetection target_end_detection; 346 | 347 | uint64_t target_return_value; 348 | 349 | DebuggerStatus handle_exception_status; 350 | DebuggerStatus dbg_last_status; 351 | 352 | bool killing_target; 353 | bool is_target_alive; 354 | bool attach_mode; 355 | 356 | bool linux32_warning; 357 | 358 | bool trace_syscalls; 359 | std::set pending_syscalls; 360 | __ptrace_request ptrace_continue_request; 361 | 362 | friend class SyscallHook; 363 | std::list syscall_hooks; 364 | }; 365 | 366 | #endif /* DEBUGGER_H */ 367 | -------------------------------------------------------------------------------- /Linux/elffile.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Google LLC 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 | https ://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 | 20 | #include "common.h" 21 | #include "elffile.h" 22 | 23 | size_t fread_check(void *ptr, size_t size, size_t nmemb, FILE *stream) { 24 | size_t ret = fread(ptr, size, nmemb, stream); 25 | if(ret != nmemb) FATAL("fread error"); 26 | return ret; 27 | } 28 | 29 | int fseek_check(FILE *stream, long offset, int whence) { 30 | int ret = fseek(stream, offset, whence); 31 | if(ret < 0) FATAL("fseek eroor"); 32 | return ret; 33 | } 34 | 35 | ElfFile::Section::~Section() { 36 | if(data) free(data); 37 | } 38 | 39 | void ElfFile::Section::EnsureLoaded(FILE *fp) { 40 | if(data) return; 41 | 42 | data = (char *)malloc(size); 43 | fseek_check(fp, offset, SEEK_SET); 44 | fread_check(data, 1, size, fp); 45 | } 46 | 47 | ElfFile::~ElfFile() { 48 | CloseFile(); 49 | } 50 | 51 | void ElfFile::OpenFile(const char *filename) { 52 | this->filename = filename; 53 | fp = fopen(filename, "rb"); 54 | if(!fp) FATAL("Error opening %s", filename); 55 | 56 | // check the file and load class 57 | 58 | uint32_t magic; 59 | fread_check(&magic, sizeof(magic), 1, fp); 60 | if(magic != 0x464c457f) { 61 | FATAL("Elf header expected"); 62 | } 63 | 64 | fread_check(&elf_class, sizeof(elf_class), 1, fp); 65 | 66 | fseek_check(fp, 0x10, SEEK_SET); 67 | fread_check(&elf_type, sizeof(elf_type), 1, fp); 68 | } 69 | 70 | void ElfFile::CloseFile() { 71 | if(fp) fclose(fp); 72 | fp = 0; 73 | } 74 | 75 | void ElfFile::EnsureSections() { 76 | if(sections_loaded) return; 77 | 78 | uint64_t e_shoff; 79 | uint16_t e_shentsize, e_shnum; 80 | 81 | if(elf_class == 1) { 82 | uint32_t e_shoff32; 83 | fseek_check(fp, 0x20, SEEK_SET); 84 | fread_check(&e_shoff32, sizeof(e_shoff32), 1, fp); 85 | e_shoff = e_shoff32; 86 | fseek_check(fp, 0x2E, SEEK_SET); 87 | fread_check(&e_shentsize, sizeof(e_shentsize), 1, fp); 88 | fread_check(&e_shnum, sizeof(e_shnum), 1, fp); 89 | } else { 90 | fseek_check(fp, 0x28, SEEK_SET); 91 | fread_check(&e_shoff, sizeof(e_shoff), 1, fp); 92 | fseek_check(fp, 0x3A, SEEK_SET); 93 | fread_check(&e_shentsize, sizeof(e_shentsize), 1, fp); 94 | fread_check(&e_shnum, sizeof(e_shnum), 1, fp); 95 | } 96 | 97 | char* sectionsbuf = (char *)malloc(e_shentsize * e_shnum); 98 | 99 | fseek_check(fp, e_shoff, SEEK_SET); 100 | fread_check(sectionsbuf, e_shentsize, e_shnum, fp); 101 | 102 | bool found = false; 103 | char *section = sectionsbuf; 104 | for(size_t i = 0; i < e_shnum; i++) { 105 | Section newsection; 106 | newsection.type = *(uint32_t *)(section + 0x04); 107 | if(elf_class == 1) { 108 | newsection.offset = *(uint32_t *)(section + 0x10); 109 | newsection.size = *(uint32_t *)(section + 0x14); 110 | newsection.link = *(uint32_t *)(section + 0x18); 111 | newsection.entsize = *(uint32_t *)(section + 0x24); 112 | } else { 113 | newsection.offset = *(uint64_t *)(section + 0x18); 114 | newsection.size = *(uint64_t *)(section + 0x20); 115 | newsection.link = *(uint32_t *)(section + 0x28); 116 | newsection.entsize = *(uint64_t *)(section + 0x38); 117 | } 118 | sections.push_back(newsection); 119 | section += e_shentsize; 120 | } 121 | 122 | free(sectionsbuf); 123 | 124 | sections_loaded = true; 125 | } 126 | 127 | ElfFile::Section *ElfFile::GetSectionByType(uint32_t type) { 128 | EnsureSections(); 129 | 130 | for(size_t i = 0; i < sections.size(); i++) { 131 | Section *section = §ions[i]; 132 | if(section->type == type) { 133 | section->EnsureLoaded(fp); 134 | return section; 135 | } 136 | } 137 | return NULL; 138 | } 139 | 140 | ElfFile::Section *ElfFile::GetSectionByIndex(size_t index) { 141 | EnsureSections(); 142 | if(index >= sections.size()) FATAL("Requested invalid section index"); 143 | Section *ret = §ions[index]; 144 | ret->EnsureLoaded(fp); 145 | return ret; 146 | } 147 | 148 | uint64_t ElfFile::GetSymbolAddress(uint32_t section_type, const char *symbol_name) { 149 | Section *symbol_section, *string_section; 150 | symbol_section = GetSectionByType(section_type); 151 | if(!symbol_section) return 0; 152 | string_section = GetSectionByIndex(symbol_section->link); 153 | 154 | size_t nsymbols = symbol_section->size / symbol_section->entsize; 155 | 156 | for(int i = 0; i < nsymbols; i++) { 157 | uint64_t name_index, value; 158 | if(elf_class == 1) { 159 | Elf32_Sym *sym = (Elf32_Sym *)(symbol_section->data + i * symbol_section->entsize); 160 | name_index = sym->st_name; 161 | value = sym->st_value; 162 | } else { 163 | Elf64_Sym *sym = (Elf64_Sym *)(symbol_section->data + i * symbol_section->entsize); 164 | name_index = sym->st_name; 165 | value = sym->st_value; 166 | } 167 | char *name = string_section->data + name_index; 168 | // printf("Symbol: %s\n", name); 169 | if(strcmp(name, symbol_name) == 0) { 170 | return value; 171 | } 172 | } 173 | return 0; 174 | } 175 | 176 | uint64_t ElfFile::GetSymbolAddress(const char *symbol_name) { 177 | uint64_t ret = GetSymbolAddress(SHT_SYMTAB, symbol_name); 178 | if(ret) return ret; 179 | return GetSymbolAddress(SHT_DYNSYM, symbol_name); 180 | } 181 | 182 | int ElfFile::IsPIE() { 183 | if(elf_type == ET_DYN) return 1; 184 | return 0; 185 | } 186 | -------------------------------------------------------------------------------- /Linux/elffile.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Google LLC 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 | https ://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 ELFFILE_H 18 | #define ELFFILE_H 19 | 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | class ElfFile { 27 | public: 28 | class Section { 29 | public: 30 | Section() { 31 | loaded = false; 32 | data = nullptr; 33 | } 34 | 35 | ~Section(); 36 | 37 | void EnsureLoaded(FILE *fp); 38 | 39 | bool loaded; 40 | uint32_t type; 41 | uint64_t offset; 42 | uint64_t size; 43 | uint32_t link; 44 | uint64_t entsize; 45 | char *data; 46 | }; 47 | 48 | ElfFile() { 49 | fp = 0; 50 | sections_loaded = false; 51 | } 52 | 53 | ~ElfFile(); 54 | 55 | void OpenFile(const char *filename); 56 | 57 | void EnsureSections(); 58 | 59 | Section *GetSectionByType(uint32_t type); 60 | Section *GetSectionByIndex(size_t index); 61 | 62 | uint64_t GetSymbolAddress(uint32_t section_type, const char *symbol_name); 63 | uint64_t GetSymbolAddress(const char *symbol_name); 64 | 65 | int IsPIE(); 66 | 67 | protected: 68 | void CloseFile(); 69 | 70 | uint8_t elf_class; 71 | uint16_t elf_type; 72 | std::vector
sections; 73 | bool sections_loaded; 74 | std::string filename; 75 | FILE *fp; 76 | }; 77 | 78 | #endif /* ELFFILE_H */ -------------------------------------------------------------------------------- /Linux/procmaps.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Google LLC 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 | https ://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 | 20 | #include "procmaps.h" 21 | 22 | void MapsParser::SplitLine(char *line, std::vector &parts) { 23 | char *p = line; 24 | int numparts = 0; 25 | while(1) { 26 | if((*p == 0) || (*p == 0x0A) || (*p == 0x0D)) break; 27 | while((*p == 0x20) || (*p == 0x09)) p++; 28 | if((*p == 0) || (*p == 0x0A) || (*p == 0x0D)) break; 29 | char *start = p; 30 | if(numparts == 5) { 31 | // there can be spaces in path, treat them as normal characters 32 | while((*p != 0) && (*p != 0x0A) && (*p != 0x0D)) p++; 33 | } else { 34 | while((*p != 0x20) && (*p != 0x09) && (*p != 0) && (*p != 0x0A) && (*p != 0x0D)) p++; 35 | } 36 | char *end = p; 37 | std::string part(start, (end-start)); 38 | parts.push_back(part); 39 | numparts++; 40 | } 41 | } 42 | 43 | void MapsParser::Parse(int pid, std::vector &entries) { 44 | char procfile[24]; 45 | 46 | sprintf(procfile, "/proc/%d/maps", pid); 47 | FILE *fp = fopen(procfile, "r"); 48 | if(!fp) return; 49 | 50 | char *line = NULL; 51 | size_t len = 0; 52 | while (getline(&line, &len, fp) != -1) { 53 | std::vector parts; 54 | MapsEntry newentry; 55 | 56 | // printf("Line: %s", line); 57 | SplitLine(line, parts); 58 | if(parts.size() < 5) continue; 59 | 60 | //address range 61 | size_t addrrange_len = parts[0].length(); 62 | char *addrrange = (char *)malloc(addrrange_len + 1); 63 | memcpy(addrrange, parts[0].data(), addrrange_len); 64 | addrrange[addrrange_len] = 0; 65 | char *dash = strchr(addrrange, '-'); 66 | if(!dash) { 67 | free(addrrange); 68 | continue; 69 | } 70 | *dash = 0; 71 | newentry.addr_from = strtoul(addrrange, 0, 16); 72 | newentry.addr_to = strtoul(dash+1, 0, 16); 73 | free(addrrange); 74 | 75 | //permissions 76 | uint32_t permissions = 0; 77 | if(parts[1].length() < 3) continue; 78 | if(parts[1][0] == 'r') permissions += PROT_READ; 79 | if(parts[1][1] == 'w') permissions += PROT_WRITE; 80 | if(parts[1][2] == 'x') permissions += PROT_EXEC; 81 | newentry.permissions = permissions; 82 | 83 | // path 84 | if(parts.size() == 6) { 85 | newentry.name = parts[5]; 86 | } 87 | 88 | entries.push_back(newentry); 89 | } 90 | 91 | if(line) free(line); 92 | fclose(fp); 93 | } 94 | -------------------------------------------------------------------------------- /Linux/procmaps.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Google LLC 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 | https ://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 PROCMAPS_H 18 | #define PROCMAPS_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | class MapsEntry { 26 | public: 27 | uint64_t addr_from; 28 | uint64_t addr_to; 29 | uint32_t permissions; 30 | std::string name; 31 | 32 | bool IsReadable() { return (permissions & PROT_READ); } 33 | bool IsWritable() { return (permissions & PROT_WRITE); } 34 | bool IsExecutable() { return (permissions & PROT_EXEC); } 35 | }; 36 | 37 | class MapsParser { 38 | public: 39 | void Parse(int pid, std::vector &entries); 40 | 41 | void SplitLine(char *line, std::vector &parts); 42 | }; 43 | 44 | #endif /* PROCMAPS_H */ -------------------------------------------------------------------------------- /Linux/syscallhook.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Google LLC 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 | https ://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 "common.h" 18 | #include "syscallhook.h" 19 | 20 | SyscallHookContext::SyscallHookContext(size_t num_arguments) { 21 | this->num_arguments = num_arguments; 22 | arguments = NULL; 23 | if(num_arguments) { 24 | arguments = (uint64_t *)malloc(num_arguments * sizeof(uint64_t)); 25 | } 26 | args_changed = false; 27 | return_value_changed = false; 28 | user_data = NULL; 29 | current_thread = 0; 30 | return_value = 0; 31 | } 32 | 33 | SyscallHookContext::~SyscallHookContext() { 34 | if(arguments) free(arguments); 35 | } 36 | 37 | uint64_t SyscallHookContext::GetArg(size_t index) { 38 | if(index >= num_arguments) { 39 | FATAL("Requested argument exceeds function number of arguments"); 40 | } 41 | return arguments[index]; 42 | } 43 | 44 | void SyscallHookContext::SetArg(size_t index, uint64_t value) { 45 | if(index >= num_arguments) { 46 | FATAL("Requested argument exceeds function number of arguments"); 47 | } 48 | arguments[index] = value; 49 | args_changed = true; 50 | } 51 | 52 | uint64_t SyscallHookContext::GetReturnValue() { 53 | return return_value; 54 | } 55 | 56 | void SyscallHookContext::SetReturnValue(uint64_t value) { 57 | return_value = value; 58 | return_value_changed = true; 59 | } 60 | 61 | uint64_t SyscallHook::GetArg(size_t index) { 62 | if(!current_context) { 63 | FATAL("Can't access arguments, did the function run yet?"); 64 | } 65 | 66 | return current_context->GetArg(index); 67 | } 68 | 69 | void SyscallHook::SetArg(size_t index, uint64_t value) { 70 | if(!current_context) { 71 | FATAL("Can't access arguments, did the function run yet?"); 72 | } 73 | 74 | current_context->SetArg(index, value); 75 | } 76 | 77 | uint64_t SyscallHook::GetRegister(Register r) { 78 | return debugger->GetRegister(r); 79 | } 80 | 81 | void SyscallHook::SetRegister(Register r, uint64_t value) { 82 | debugger->SetRegister(r, value); 83 | } 84 | 85 | void SyscallHook::SetReturnValue(uint64_t value) { 86 | return current_context->SetReturnValue(value); 87 | } 88 | 89 | uint64_t SyscallHook::GetReturnValue() { 90 | return current_context->GetReturnValue(); 91 | } 92 | 93 | void SyscallHook::RemoteWrite(void *address, const void *buffer, size_t size) { 94 | debugger->RemoteWrite(address, buffer, size); 95 | } 96 | 97 | void SyscallHook::RemoteRead(void *address, void *buffer, size_t size) { 98 | debugger->RemoteRead(address, buffer, size); 99 | } 100 | 101 | void *SyscallHook::RemoteAllocate(size_t size, MemoryProtection protection) { 102 | return debugger->RemoteAllocate(size, protection); 103 | } 104 | 105 | SyscallHookContext *SyscallHook::CreateContext() { 106 | SyscallHookContext *context = new SyscallHookContext(num_args); 107 | debugger->GetSyscallArguments(context->arguments, num_args); 108 | return context; 109 | } 110 | 111 | void SyscallHook::CommitContext() { 112 | if(!current_context->args_changed) return; 113 | debugger->SetSyscallArguments(current_context->arguments, 114 | current_context->num_arguments); 115 | } 116 | 117 | void SyscallHook::OnSyscallInternal(int thread_id, uint64_t syscall_number) { 118 | if(syscall_number != this->syscall_number) return; 119 | if(thread_filter && (thread_id != thread_filter)) return; 120 | 121 | current_context = CreateContext(); 122 | 123 | SyscallStatus ret = OnSyscall(); 124 | 125 | if(ret == SYSCALL_SKIP) SetRegister(SYSCALL_NUMER_REGISTER, (uint64_t)-1); 126 | 127 | CommitContext(); 128 | 129 | thread_contexts[thread_id] = current_context; 130 | current_context = NULL; 131 | } 132 | 133 | void SyscallHook::OnSyscallEndInternal(int thread_id) { 134 | if(thread_filter && (thread_id != thread_filter)) return; 135 | 136 | auto iter = thread_contexts.find(thread_id); 137 | if(iter == thread_contexts.end()) return; 138 | current_context = iter->second; 139 | thread_contexts.erase(iter); 140 | 141 | if(!current_context->return_value_changed) { 142 | current_context->return_value = GetRegister(SYSCALL_RETURN_REGISTER); 143 | } 144 | 145 | OnSyscallEnd(); 146 | 147 | if(current_context->return_value_changed) { 148 | SetRegister(SYSCALL_RETURN_REGISTER, current_context->return_value); 149 | } 150 | 151 | delete current_context; 152 | current_context = NULL; 153 | } 154 | 155 | void SyscallHook::OnProcessExit() { 156 | if(current_context) { 157 | delete current_context; 158 | current_context = NULL; 159 | } 160 | for(auto iter = thread_contexts.begin(); iter != thread_contexts.end(); iter++) { 161 | delete iter->second; 162 | } 163 | thread_contexts.clear(); 164 | } 165 | -------------------------------------------------------------------------------- /Linux/syscallhook.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Google LLC 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 | https ://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 SYSCALLHOOK_H 18 | #define SYSCALLHOOK_H 19 | 20 | #include "debugger.h" 21 | 22 | enum SyscallStatus { 23 | SYSCALL_CONTINUE, 24 | SYSCALL_SKIP, 25 | }; 26 | 27 | class SyscallHookContext { 28 | public: 29 | SyscallHookContext(size_t num_arguments); 30 | ~SyscallHookContext(); 31 | 32 | uint64_t GetArg(size_t index); 33 | void SetArg(size_t index, uint64_t value); 34 | uint64_t GetReturnValue(); 35 | void SetReturnValue(uint64_t value); 36 | 37 | size_t num_arguments; 38 | uint64_t *arguments; 39 | uint64_t return_value; 40 | int current_thread; 41 | bool args_changed; 42 | bool return_value_changed; 43 | 44 | // clients can store their per-call data here 45 | void *user_data; 46 | }; 47 | 48 | class SyscallHook { 49 | public: 50 | SyscallHook(uint64_t syscall_number, size_t num_args, int thread_filter = 0) 51 | : thread_filter(thread_filter), syscall_number(syscall_number), num_args(num_args), current_context(NULL) { } 52 | 53 | virtual void OnProcessExit(); 54 | 55 | virtual SyscallStatus OnSyscall() { return SYSCALL_CONTINUE; } 56 | virtual void OnSyscallEnd() { } 57 | 58 | void OnSyscallInternal(int thread_id, uint64_t syscall_number); 59 | void OnSyscallEndInternal(int thread_id); 60 | 61 | void SetDebugger(Debugger *debugger) { 62 | this->debugger = debugger; 63 | } 64 | 65 | protected: 66 | uint64_t GetArg(size_t index); 67 | void SetArg(size_t index, uint64_t value); 68 | uint64_t GetRegister(Register r); 69 | void SetRegister(Register r, uint64_t value); 70 | void SetReturnValue(uint64_t value); 71 | uint64_t GetReturnValue(); 72 | 73 | void RemoteWrite(void *address, const void *buffer, size_t size); 74 | void RemoteRead(void *address, void *buffer, size_t size); 75 | void *RemoteAllocate(size_t size, MemoryProtection protection); 76 | 77 | SyscallHookContext *CreateContext(); 78 | void CommitContext(); 79 | 80 | protected: 81 | SyscallHookContext *current_context; 82 | Debugger *debugger; 83 | size_t num_args; 84 | int thread_filter; 85 | uint64_t syscall_number; 86 | 87 | std::unordered_map thread_contexts; 88 | }; 89 | 90 | 91 | #endif /* SYSCALLHOOK_H */ 92 | -------------------------------------------------------------------------------- /Windows/debugger.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Google LLC 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 | https ://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 DEBUGGER_H 18 | #define DEBUGGER_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "common.h" 26 | #include "windows.h" 27 | #include "arch/x86/reg.h" 28 | 29 | enum CallingConvention { 30 | CALLCONV_MICROSOFT_X64, 31 | CALLCONV_THISCALL, 32 | CALLCONV_FASTCALL, 33 | CALLCONV_CDECL, 34 | CALLCONV_DEFAULT, 35 | }; 36 | 37 | enum MemoryProtection { 38 | READONLY, 39 | READWRITE, 40 | READEXECUTE, 41 | READWRITEEXECUTE 42 | }; 43 | 44 | enum DebuggerStatus { 45 | DEBUGGER_NONE, 46 | DEBUGGER_CONTINUE, 47 | DEBUGGER_PROCESS_EXIT, 48 | DEBUGGER_TARGET_START, 49 | DEBUGGER_TARGET_END, 50 | DEBUGGER_CRASHED, 51 | DEBUGGER_HANGED, 52 | DEBUGGER_ATTACHED 53 | }; 54 | 55 | struct SavedRegisters { 56 | CONTEXT saved_context; 57 | }; 58 | 59 | class Debugger { 60 | public: 61 | 62 | virtual void Init(int argc, char **argv); 63 | DebuggerStatus Run(char *cmd, uint32_t timeout); 64 | DebuggerStatus Run(int argc, char **argv, uint32_t timeout); 65 | DebuggerStatus Kill(); 66 | DebuggerStatus Continue(uint32_t timeout); 67 | DebuggerStatus Attach(unsigned int pid, uint32_t timeout); 68 | 69 | bool IsTargetAlive() { return (child_handle != NULL); }; 70 | bool IsTargetFunctionDefined() { return target_function_defined; } 71 | 72 | uint64_t GetTargetReturnValue() { return target_return_value; } 73 | 74 | enum ExceptionType { 75 | BREAKPOINT, 76 | ACCESS_VIOLATION, 77 | ILLEGAL_INSTRUCTION, 78 | STACK_OVERFLOW, 79 | OTHER 80 | }; 81 | 82 | struct Exception { 83 | ExceptionType type; 84 | void *ip; 85 | bool maybe_write_violation; 86 | bool maybe_execute_violation; 87 | void *access_address; 88 | }; 89 | 90 | Exception GetLastException() { 91 | return last_exception; 92 | } 93 | 94 | protected: 95 | 96 | virtual void OnModuleLoaded(void *module, char *module_name); 97 | virtual void OnModuleUnloaded(void *module); 98 | virtual void OnTargetMethodReached() {} 99 | virtual void OnProcessCreated(); 100 | virtual void OnProcessExit() {}; 101 | virtual void OnEntrypoint(); 102 | 103 | // should return true if the exception has been handled 104 | virtual bool OnException(Exception *exception_record) { 105 | return false; 106 | } 107 | 108 | virtual void OnCrashed(Exception *exception_record) { } 109 | 110 | void *GetModuleEntrypoint(void *base_address); 111 | void ReadStack(void *stack_addr, uint64_t *buffer, size_t numitems); 112 | void WriteStack(void *stack_addr, uint64_t *buffer, size_t numitems); 113 | void GetImageSize(void *base_address, size_t *min_address, size_t *max_address); 114 | 115 | // helper functions 116 | void* RemoteAllocate(size_t size, MemoryProtection protection); 117 | void *RemoteAllocateNear(uint64_t region_min, 118 | uint64_t region_max, 119 | size_t size, 120 | MemoryProtection protection, 121 | bool use_shared_memory = false); 122 | 123 | void ExtractCodeRanges(void *module_base, 124 | size_t min_address, 125 | size_t max_address, 126 | std::list *executable_ranges, 127 | size_t *code_size, 128 | bool do_protect = true); 129 | 130 | void ProtectCodeRanges(std::list *executable_ranges); 131 | 132 | // returns address in (potentially) instrumented code 133 | virtual size_t GetTranslatedAddress(size_t address) { return address; } 134 | 135 | void RemoteFree(void *address, size_t size); 136 | void RemoteWrite(void *address, const void *buffer, size_t size); 137 | void RemoteRead(void *address, void *buffer, size_t size); 138 | void RemoteProtect(void *address, size_t size, MemoryProtection protect); 139 | 140 | size_t GetRegister(Register r); 141 | void SetRegister(Register r, size_t value); 142 | 143 | void *GetTargetMethodAddress() { return target_address; } 144 | 145 | DWORD GetProcOffset(HMODULE module, const char* name); 146 | void* GetSymbolAddress(void* base_address, const char* symbol_name); 147 | 148 | void SaveRegisters(SavedRegisters* registers); 149 | void RestoreRegisters(SavedRegisters* registers); 150 | 151 | void SetReturnAddress(size_t value); 152 | size_t GetReturnAddress(); 153 | 154 | void GetFunctionArguments(uint64_t* arguments, size_t num_args, uint64_t sp, CallingConvention callconv); 155 | void SetFunctionArguments(uint64_t* arguments, size_t num_args, uint64_t sp, CallingConvention callconv); 156 | 157 | void GetExceptionHandlers(size_t module_haeder, std::unordered_set & handlers); 158 | 159 | void PatchPointersRemote(size_t min_address, size_t max_address, std::unordered_map& search_replace); 160 | template 161 | void PatchPointersRemoteT(size_t min_address, size_t max_address, std::unordered_map& search_replace); 162 | 163 | HANDLE GetChildProcessHandle() { return child_handle; } 164 | 165 | private: 166 | struct Breakpoint { 167 | void *address; 168 | int type; 169 | unsigned char original_opcode; 170 | }; 171 | std::list breakpoints; 172 | 173 | void StartProcess(char *cmd); 174 | void GetProcessPlatform(); 175 | DebuggerStatus DebugLoop(uint32_t timeout, bool killing=false); 176 | int HandleDebuggerBreakpoint(void *address); 177 | void HandleDllLoadInternal(LOAD_DLL_DEBUG_INFO *LoadDll); 178 | DebuggerStatus HandleExceptionInternal(EXCEPTION_RECORD *exception_record); 179 | void HandleTargetReachedInternal(); 180 | void HandleTargetEnded(); 181 | char *GetTargetAddress(HMODULE module); 182 | void AddBreakpoint(void *address, int type); 183 | DWORD GetLoadedModules(HMODULE **modules); 184 | void DeleteBreakpoints(); 185 | DWORD WindowsProtectionFlags(MemoryProtection protection); 186 | DWORD GetImageSize(void *base_address); 187 | void *RemoteAllocateBefore(uint64_t min_address, 188 | uint64_t max_address, 189 | size_t size, 190 | MemoryProtection protection); 191 | void *RemoteAllocateAfter(uint64_t min_address, 192 | uint64_t max_address, 193 | size_t size, 194 | MemoryProtection protection); 195 | 196 | protected: 197 | 198 | bool child_entrypoint_reached; 199 | bool target_reached; 200 | 201 | int32_t child_ptr_size = sizeof(void *); 202 | 203 | private: 204 | HANDLE child_handle, child_thread_handle; 205 | 206 | HANDLE devnul_handle = INVALID_HANDLE_VALUE; 207 | 208 | DEBUG_EVENT dbg_debug_event; 209 | DWORD dbg_continue_status; 210 | bool dbg_continue_needed; 211 | DebuggerStatus dbg_last_status; 212 | 213 | int wow64_target = 0; 214 | 215 | protected: 216 | bool target_function_defined; 217 | bool loop_mode; 218 | bool attach_mode; 219 | bool trace_debug_events; 220 | 221 | bool sinkhole_stds; 222 | uint64_t mem_limit; 223 | uint64_t cpu_aff; 224 | 225 | private: 226 | // persistence related 227 | int target_num_args; 228 | uint64_t target_offset; 229 | std::string target_module; 230 | std::string target_method; 231 | CallingConvention calling_convention; 232 | void *target_address; 233 | void *saved_sp; 234 | void *saved_return_address; 235 | uint64_t *saved_args; 236 | 237 | uint64_t target_return_value; 238 | 239 | void RetrieveThreadContext(); 240 | void CreateException(EXCEPTION_RECORD *win_exception_record, 241 | Exception *exception); 242 | 243 | Exception last_exception; 244 | // thread id of the last event 245 | DWORD thread_id; 246 | CONTEXT lcContext; 247 | bool have_thread_context; 248 | size_t allocation_granularity; 249 | 250 | bool force_dep; 251 | }; 252 | 253 | #endif // DEBUGGER_H 254 | -------------------------------------------------------------------------------- /Windows/winunwind.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Google LLC 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 | https ://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 WINUNWIND_H 18 | #define WINUNWIND_H 19 | 20 | #ifdef _WIN64 21 | 22 | #include "unwind.h" 23 | #include "tinyinst.h" 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | struct UnwindInfo { 30 | BYTE version_flags; 31 | BYTE prolog_size; 32 | BYTE frame_register; 33 | 34 | std::vector unwind_codes; 35 | 36 | size_t handler; 37 | size_t handler_data; 38 | 39 | DWORD translated_offset; 40 | }; 41 | 42 | struct FunctionInfo { 43 | size_t function_start; 44 | size_t function_end; 45 | 46 | UnwindInfo* unwind_info; 47 | 48 | // address of this function info in the original code 49 | size_t function_info_addr; 50 | }; 51 | 52 | struct FunctionTable { 53 | FunctionTable() : max_entries(0), addr(0) {} 54 | 55 | size_t addr; 56 | size_t offset; 57 | size_t num_entries; 58 | size_t max_entries; 59 | }; 60 | 61 | class WinUnwindData : public UnwindData { 62 | public: 63 | WinUnwindData() : table_registered(false), 64 | register_breakpoint(0), register_continue_IP(0), 65 | last_lookup_translate(NULL), last_lookup_other(NULL), 66 | last_translated_entry(NULL) {} 67 | 68 | ~WinUnwindData(); 69 | 70 | // maps from offset (in the original code) to UnwindInfo 71 | // presumably a single UnwindInfo can be referenced from 72 | // multtiple FunctionInfo structures 73 | std::unordered_map unwind_infos; 74 | std::vector original_function_infos; 75 | std::set handlers; 76 | 77 | bool table_registered; 78 | size_t register_breakpoint; 79 | size_t register_continue_IP; 80 | SavedRegisters register_saved_registers; 81 | 82 | struct ReturnAddressInfo { 83 | size_t original_return_address; 84 | FunctionInfo* original_function_info; 85 | }; 86 | 87 | // Maps the return addresses in the instrumented code (the keys) 88 | // to the return addresses in the original code (the values). 89 | std::unordered_map return_addresses; 90 | 91 | FunctionInfo* LookupFunctionInfoForTranslate(size_t IP); 92 | FunctionInfo* LookupFunctionInfoForOther(size_t IP); 93 | FunctionInfo* LookupFunctionInfoSlow(size_t IP); 94 | 95 | FunctionInfo* last_lookup_translate; 96 | FunctionInfo* last_lookup_other; 97 | FunctionInfo* last_translated_entry; 98 | 99 | // FunctionInfo structures for the instrumented code 100 | // that haven't been written to the remote process yet 101 | std::vector translated_infos; 102 | 103 | void DoTranslate(size_t original_address, size_t translated_address); 104 | 105 | // maps from original handler to translated handler 106 | std::unordered_map translated_handler_map; 107 | // maps from translated handler to original handler 108 | std::unordered_map handler_start_breakpoints; 109 | 110 | // info about the function table for the instrumented code 111 | // (in the remote process) 112 | FunctionTable function_table; 113 | 114 | void CommitLastTranslated(); 115 | }; 116 | 117 | class WinUnwindGenerator : public UnwindGenerator { 118 | public: 119 | WinUnwindGenerator(TinyInst& tinyinst) : UnwindGenerator(tinyinst), RtlAddFunctionTable_addr(0) { } 120 | 121 | void OnModuleInstrumented(ModuleInfo* module) override; 122 | void OnModuleUninstrumented(ModuleInfo* module) override; 123 | 124 | void OnModuleLoaded(void* module, char* module_name) override; 125 | 126 | size_t MaybeRedirectExecution(ModuleInfo* module, size_t IP) override; 127 | 128 | bool HandleBreakpoint(ModuleInfo* module, void* address) override; 129 | 130 | void OnReturnAddress(ModuleInfo* module, 131 | size_t original_address, 132 | size_t translated_address) override; 133 | 134 | void OnBasicBlockStart(ModuleInfo* module, 135 | size_t original_address, 136 | size_t translated_address) override; 137 | 138 | void OnInstruction(ModuleInfo* module, 139 | size_t original_address, 140 | size_t translated_address) override; 141 | 142 | void OnBasicBlockEnd(ModuleInfo* module, 143 | size_t original_address, 144 | size_t translated_address) override; 145 | 146 | bool Is64BitOnly() override { return true; } 147 | 148 | protected: 149 | UnwindInfo* ReadUnwindInfo(ModuleInfo* module, unsigned char* modulebuf, size_t image_size, uint32_t unwind_info_offset); 150 | 151 | int GetExceptionTableOffsetAndSize(char* data, DWORD* offset, DWORD* size); 152 | 153 | void WriteFunctionInfo(ModuleInfo* module, FunctionInfo *info, FunctionTable* functionTable); 154 | 155 | DWORD WriteUnwindInfo(ModuleInfo* module, UnwindInfo* info); 156 | 157 | void WriteHandler(ModuleInfo* module, size_t original_handler); 158 | 159 | size_t WriteFunctionTable(ModuleInfo* module, FunctionTable &functionTable, size_t max_entries); 160 | 161 | void FixUnwindCodes(UnwindInfo* info); 162 | 163 | size_t RtlAddFunctionTable_addr; 164 | }; 165 | 166 | #endif // _WIN64 167 | 168 | #endif // WINUNWIND_H 169 | -------------------------------------------------------------------------------- /arch/arm64/arm64_assembler.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Google LLC 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 | https ://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 ARCH_ARM64_ARM64_ASSEMBLER_H 18 | #define ARCH_ARM64_ARM64_ASSEMBLER_H 19 | 20 | #include "arch/arm64/arm64_helpers.h" 21 | #include "assembler.h" 22 | #include "tinyinst.h" 23 | 24 | class Arm64Assembler : public Assembler { 25 | public: 26 | using Assembler::Assembler; 27 | virtual ~Arm64Assembler() {} 28 | void Init() override; 29 | 30 | bool DecodeInstruction(Instruction &inst, const unsigned char *buffer, 31 | unsigned int buffer_size) override; 32 | void FixInstructionAndOutput(ModuleInfo *module, Instruction &inst, 33 | const unsigned char *input, 34 | const unsigned char *input_address_remote, 35 | bool convert_call_to_jmp = false) override; 36 | void HandleBasicBlockEnd( 37 | const char *address, ModuleInfo *module, std::set *queue, 38 | std::list> *offset_fixes, Instruction &inst, 39 | const char *code_ptr, size_t offset, size_t last_offset) override; 40 | 41 | void JmpAddress(ModuleInfo *module, size_t address) override; 42 | void Nop(ModuleInfo *module) override; 43 | void Ret(ModuleInfo *module) override; 44 | size_t Breakpoint(ModuleInfo *module) override; 45 | void Crash(ModuleInfo *module) override; 46 | 47 | void OffsetStack(ModuleInfo *module, int32_t offset) override; 48 | bool IsRipRelative(ModuleInfo *module, Instruction &inst, 49 | size_t instruction_address, size_t *mem_address) override; 50 | void TranslateJmp(ModuleInfo *module, ModuleInfo *target_module, 51 | size_t original_target, 52 | IndirectBreakpoinInfo &breakpoint_info, 53 | bool global_indirect, size_t previous_offset) override; 54 | void InstrumentLocalIndirect(ModuleInfo *module, Instruction &inst, 55 | size_t instruction_address, 56 | size_t bb_address) override; 57 | void InstrumentGlobalIndirect(ModuleInfo *module, Instruction &inst, 58 | size_t instruction_address) override; 59 | void FixOffset(ModuleInfo *module, uint32_t jmp_offset, 60 | uint32_t target_offset) override; 61 | 62 | protected: 63 | void ReadRegStack(ModuleInfo *module, Register dst, int32_t offset); 64 | void WriteRegStack(ModuleInfo *module, Register src, int32_t offset); 65 | void EmitLoadLit(ModuleInfo *module, Register dst_reg, size_t size, 66 | bool is_signed, uint64_t value); 67 | 68 | private: 69 | uint8_t GetIndirectTarget(Instruction &inst, uint8_t *is_pac); 70 | void MovIndirectTarget(ModuleInfo *module, uint8_t target_address_reg, uint8_t is_pac); 71 | 72 | void TranslateSimdLdrLiteral(ModuleInfo *module, 73 | Instruction &inst, 74 | const unsigned char *input, 75 | const unsigned char *input_address_remote); 76 | 77 | void ReadStack(ModuleInfo *module, int32_t offset); 78 | void WriteStack(ModuleInfo *module, int32_t offset); 79 | 80 | void SetReturnAddress(ModuleInfo *module, uint64_t return_address); 81 | 82 | void InstrumentRet(const char *address, ModuleInfo *module, 83 | std::set *queue, 84 | std::list> *offset_fixes, 85 | Instruction &inst, const char *code_ptr, size_t offset, 86 | size_t last_offset); 87 | void InstrumentCondJmp(const char *address, ModuleInfo *module, 88 | std::set *queue, 89 | std::list> *offset_fixes, 90 | Instruction &inst, const char *code_ptr, size_t offset, 91 | size_t last_offset); 92 | void InstrumentJmp(const char *address, ModuleInfo *module, 93 | std::set *queue, 94 | std::list> *offset_fixes, 95 | Instruction &inst, const char *code_ptr, size_t offset, 96 | size_t last_offset); 97 | void InstrumentCall(const char *address, ModuleInfo *module, 98 | std::set *queue, 99 | std::list> *offset_fixes, 100 | Instruction &inst, const char *code_ptr, size_t offset, 101 | size_t last_offset); 102 | 103 | friend class LiteCov; 104 | }; 105 | #endif // ARCH_ARM64_ARM64_ASSEMBLER_H 106 | -------------------------------------------------------------------------------- /arch/arm64/arm64_helpers.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Google LLC 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 | https ://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 ARCH_ARM64_ARM64_HELPERS_H 18 | #define ARCH_ARM64_ARM64_HELPERS_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "third_party/reil/reil/aarch64/decoder.h" 25 | #include "arch/arm64/reg.h" 26 | 27 | namespace arm64 = reil::aarch64::decoder; 28 | 29 | Register reg(arm64::Register r); 30 | 31 | uint32_t bits(uint32_t msb, uint32_t lsb, uint32_t val); 32 | uint32_t bit(uint32_t lsb); 33 | 34 | uint32_t EncodeSignedImmediate(const uint8_t msb, const uint8_t lsb, int32_t value); 35 | uint32_t EncodeUnsignedImmediate(const uint8_t msb, const uint8_t lsb, uint32_t value); 36 | 37 | uint32_t ldr(uint8_t size, Register data_reg, Register base_reg, int32_t offset); 38 | uint32_t str(uint8_t size, Register data_reg, Register base_reg, int32_t offset); 39 | uint32_t ldr_lit(Register dst_reg, int64_t rip_offset, size_t size, bool is_signed); 40 | uint32_t load_store(uint8_t op, uint8_t size, Register data_reg, Register base_reg, int32_t offset); 41 | 42 | uint32_t movzn(Register dst_reg, int32_t imm); 43 | uint32_t movz_imm(Register dst, int32_t imm); 44 | uint32_t mov(Register dst, Register src); 45 | 46 | uint32_t add_sub_reg_imm(uint8_t op, Register dst_reg, Register src_reg, uint32_t offset); 47 | uint32_t sub_reg_imm(Register dst_reg, Register src_reg, uint32_t offset); 48 | uint32_t add_reg_imm(Register dst_reg, Register src_reg, uint32_t offset); 49 | uint32_t orr_shifted_reg(Register dst, Register rn, Register src); 50 | uint32_t eor_shifted_reg(uint8_t sz, Register rd, Register rn, Register rm, arm64::Shift::Type shift_type, uint8_t shift_count); 51 | 52 | uint32_t cmp(Register src1, Register src2); 53 | 54 | uint32_t branch_imm(size_t instr_address, size_t address, bool do_link); 55 | uint32_t b(size_t instr_address, size_t address); 56 | uint32_t bl(size_t instr_address, size_t address); 57 | uint32_t br(Register dst_reg); 58 | uint32_t b_cond(const std::string &cond, int32_t off); 59 | 60 | uint32_t ldr_simd_x0_from_ldr_simd_literal(uint32_t orig_inst); 61 | 62 | #endif // ARCH_ARM64_ARM64_HELPERS_H 63 | -------------------------------------------------------------------------------- /arch/arm64/arm64_litecov.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Google LLC 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 | https ://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 "arch/arm64/arm64_assembler.h" 18 | #include "arch/arm64/arm64_helpers.h" 19 | #include "common.h" 20 | #include "instruction.h" 21 | #include "litecov.h" 22 | 23 | void LiteCov::NopCovInstructions(ModuleInfo *module, size_t code_offset) { 24 | uint32_t b_instr = b(0, skip_cov_instruction_br_off); 25 | WriteCodeAtOffset(module, code_offset, &b_instr, sizeof(b_instr)); 26 | // need to commit since this isn't a part of normal instrumentation process 27 | CommitCode(module, code_offset, sizeof(b_instr)); 28 | } 29 | 30 | void LiteCov::NopCmpCovInstructions(ModuleInfo *module, 31 | CmpCoverageRecord &cmp_record, 32 | int matched_width) { 33 | if (matched_width >= cmp_record.width - 8) { 34 | uint32_t b_instr = b(0, cmp_record.instrumentation_size); 35 | WriteCodeAtOffset(module, cmp_record.instrumentation_offset, &b_instr, 36 | sizeof(b_instr)); 37 | CommitCode(module, cmp_record.instrumentation_offset, sizeof(b_instr)); 38 | cmp_record.ignored = true; 39 | return; 40 | } 41 | 42 | if (matched_width >= cmp_record.match_width) { 43 | cmp_record.match_width = matched_width + 8; 44 | char new_offset_data = static_cast(cmp_record.match_width); 45 | WriteCodeAtOffset(module, cmp_record.match_width_offset, &new_offset_data, 46 | 1); 47 | CommitCode(module, cmp_record.instrumentation_offset, 1); 48 | } 49 | } 50 | 51 | void LiteCov::EmitCoverageInstrumentation(ModuleInfo *module, 52 | size_t bit_address, 53 | size_t mov_address) { 54 | ////////////////////////////////////////////////// 55 | // mov [coverage_buffer + coverage_buffer_next], 1 56 | ////////////////////////////////////////////////// 57 | 58 | Arm64Assembler *arm64asm = static_cast(assembler_); 59 | 60 | size_t cov_instruction_start_off = module->instrumented_code_allocated; 61 | if (sp_offset) { 62 | arm64asm->OffsetStack(module, -sp_offset); 63 | } 64 | // 3 instr 65 | arm64asm->OffsetStack(module, -16); 66 | arm64asm->WriteRegStack(module, X0, 0); 67 | arm64asm->WriteRegStack(module, X1, 8); 68 | 69 | // 4 + 2 instr 70 | arm64asm->EmitLoadLit(module, X0, 64, false, bit_address); 71 | uint32_t mov = movz_imm(X1, 1); 72 | WriteCode(module, &mov, sizeof(mov)); 73 | uint32_t strb = str(8, X1, X0, 0); 74 | WriteCode(module, &strb, sizeof(strb)); 75 | 76 | // 3 instr 77 | arm64asm->ReadRegStack(module, X1, 8); 78 | arm64asm->ReadRegStack(module, X0, 0); 79 | arm64asm->OffsetStack(module, 16); 80 | 81 | if (sp_offset) { 82 | arm64asm->OffsetStack(module, sp_offset); 83 | } 84 | 85 | skip_cov_instruction_br_off = 86 | module->instrumented_code_allocated - cov_instruction_start_off; 87 | } 88 | 89 | InstructionResult LiteCov::InstrumentInstruction(ModuleInfo *module, 90 | Instruction &inst, 91 | size_t bb_address, 92 | size_t instruction_address) { 93 | 94 | InstructionResult tinyinst_ret = TinyInst::InstrumentInstruction(module, inst, bb_address, instruction_address); 95 | if(tinyinst_ret != INST_NOTHANDLED) return tinyinst_ret; 96 | 97 | if (!compare_coverage) { 98 | return INST_NOTHANDLED; 99 | } 100 | 101 | if (inst.instr.opcode != arm64::Opcode::kSubImmediate && 102 | inst.instr.opcode != arm64::Opcode::kAddImmediate && 103 | inst.instr.opcode != arm64::Opcode::kSubShiftedRegister && 104 | inst.instr.opcode != arm64::Opcode::kAddShiftedRegister && 105 | inst.instr.opcode != arm64::Opcode::kSubExtendedRegister && 106 | inst.instr.opcode != arm64::Opcode::kAddExtendedRegister) { 107 | return INST_NOTHANDLED; 108 | } 109 | 110 | arm64::Register rd = std::get(inst.instr.operands[0]); 111 | int operand_width = rd.size; 112 | 113 | // Skip instructions that are unlikely to be used as compare: 114 | // most compare instructions have zero register as destination 115 | // switch statements use subs (setting flag) 116 | if (rd.name != arm64::Register::kXzr && 117 | !(inst.instr.set_flags && 118 | (inst.instr.opcode == arm64::Opcode::kSubImmediate || 119 | inst.instr.opcode == arm64::Opcode::kSubShiftedRegister || 120 | inst.instr.opcode == arm64::Opcode::kSubExtendedRegister))) { 121 | return INST_NOTHANDLED; 122 | } 123 | 124 | arm64::Register rn = std::get(inst.instr.operands[1]); 125 | if (rn.name == arm64::Register::kSp) { 126 | return INST_NOTHANDLED; 127 | } 128 | 129 | if (inst.instr.opcode == arm64::Opcode::kSubShiftedRegister || 130 | inst.instr.opcode == arm64::Opcode::kAddShiftedRegister || 131 | inst.instr.opcode == arm64::Opcode::kAddExtendedRegister || 132 | inst.instr.opcode == arm64::Opcode::kSubExtendedRegister) { 133 | arm64::Register rm = std::get(inst.instr.operands[2]); 134 | if (rm.name == arm64::Register::kSp) { 135 | return INST_NOTHANDLED; 136 | } 137 | } 138 | 139 | // kSubExtendedRegister / kAddExtendedRegister might have sizes smaller than 140 | // 32 bit. In case of 8 bit cmp, skip, in case of 16bit cmp add and-instr 141 | // after xor. 142 | bool is_ext_16_bit = false; 143 | 144 | if (inst.instr.opcode == arm64::Opcode::kAddExtendedRegister || 145 | inst.instr.opcode == arm64::Opcode::kSubExtendedRegister) { 146 | arm64::Extend extend = std::get(inst.instr.operands[3]); 147 | 148 | // skip byte cmp 149 | if (extend.type == arm64::Extend::Type::kUxtb || 150 | extend.type == arm64::Extend::Type::kSxtb) { 151 | return INST_NOTHANDLED; 152 | } 153 | } 154 | 155 | 156 | ModuleCovData *data = (ModuleCovData *)module->client_data; 157 | 158 | size_t bb_offset = bb_address - module->min_address; 159 | size_t cmp_offset = instruction_address - bb_address; 160 | if (cmp_offset >= 0x1000000) { 161 | // only allow one cmp instrumentation per bb 162 | WARN("Too large basic block for cmp coverage\n"); 163 | return INST_NOTHANDLED; 164 | } 165 | 166 | // check what we matched already 167 | int match_width = operand_width - 8; 168 | for (; match_width >= 8; match_width -= 8) { 169 | uint64_t already_matched_code = 170 | GetCmpCode(bb_offset, cmp_offset, match_width); 171 | if (data->ignore_coverage.find(already_matched_code) != 172 | data->ignore_coverage.end()) { 173 | break; 174 | } 175 | } 176 | match_width += 8; 177 | if (match_width == operand_width) { 178 | // we already have an (almost) full match 179 | return INST_NOTHANDLED; 180 | } 181 | 182 | size_t instrumentation_start_offset = module->instrumented_code_allocated; 183 | 184 | Arm64Assembler *arm64asm = static_cast(assembler_); 185 | 186 | if (sp_offset) { 187 | arm64asm->OffsetStack(module, -sp_offset); 188 | } 189 | arm64asm->OffsetStack(module, -16); 190 | arm64asm->WriteRegStack(module, X1, 8); 191 | arm64asm->WriteRegStack(module, X0, 0); 192 | 193 | uint32_t eor_instr = 0; 194 | if (inst.instr.opcode == arm64::Opcode::kSubShiftedRegister || 195 | inst.instr.opcode == arm64::Opcode::kAddShiftedRegister) { 196 | arm64::Register rn = std::get(inst.instr.operands[1]); 197 | arm64::Register rm = std::get(inst.instr.operands[2]); 198 | arm64::Shift shift = std::get(inst.instr.operands[3]); 199 | 200 | eor_instr = eor_shifted_reg(operand_width, X0, reg(rn), reg(rm), shift.type, 201 | shift.count); 202 | } else if (inst.instr.opcode == arm64::Opcode::kSubImmediate || 203 | inst.instr.opcode == arm64::Opcode::kAddImmediate) { 204 | // Immediate encoding for xor is complex (DecodeBitMasks), hence, move 205 | // the imm into x0 or x1 for eor 206 | Register imm_reg = X0; 207 | if (rn.name == arm64::Register::Name::kX0) { 208 | imm_reg = X1; 209 | } 210 | 211 | auto imm = std::get(inst.instr.operands[2]); 212 | uint32_t mov_imm_instr = movz_imm(imm_reg, imm.value); 213 | WriteCode(module, &mov_imm_instr, sizeof(mov_imm_instr)); 214 | 215 | eor_instr = eor_shifted_reg(operand_width, X0, reg(rn), imm_reg, 216 | arm64::Shift::Type::kNone, 0); 217 | } 218 | 219 | else if (inst.instr.opcode == arm64::Opcode::kAddExtendedRegister || 220 | inst.instr.opcode == arm64::Opcode::kSubExtendedRegister) { 221 | arm64::Register rn = std::get(inst.instr.operands[1]); 222 | arm64::Register rm = std::get(inst.instr.operands[2]); 223 | arm64::Extend extend = std::get(inst.instr.operands[3]); 224 | 225 | if (extend.type == arm64::Extend::Type::kUxth || 226 | extend.type == arm64::Extend::Type::kSxth) { 227 | is_ext_16_bit = true; 228 | } 229 | 230 | // ensure 32 bit op 231 | operand_width = 32; 232 | eor_instr = eor_shifted_reg(operand_width, X0, reg(rn), reg(rm), 233 | arm64::Shift::Type::kNone, 0); 234 | } 235 | 236 | WriteCode(module, &eor_instr, sizeof(eor_instr)); 237 | 238 | if (is_ext_16_bit) { 239 | // and x0, x0, 0xffff 240 | uint32_t and_instr = 0x92403c00; 241 | WriteCode(module, &and_instr, sizeof(and_instr)); 242 | } 243 | 244 | // count leading zeros depending on operand width 245 | if (operand_width == 64) { 246 | uint32_t clz_x0_instr = 0xdac01000; 247 | WriteCode(module, &clz_x0_instr, sizeof(clz_x0_instr)); 248 | } else { 249 | uint32_t clz_w0_instr = 0x5ac01000; 250 | WriteCode(module, &clz_w0_instr, sizeof(clz_w0_instr)); 251 | } 252 | 253 | arm64asm->EmitLoadLit(module, X1, operand_width, false, match_width); 254 | size_t match_width_offset = module->instrumented_code_allocated - 8; 255 | 256 | uint32_t cmp_x0_x1_instr = cmp(X0, X1); 257 | WriteCode(module, &cmp_x0_x1_instr, sizeof(cmp_x0_x1_instr)); 258 | 259 | size_t jmp_offset = module->instrumented_code_allocated; 260 | uint32_t b_lt = b_cond("lt", 0); 261 | WriteCode(module, &b_lt, sizeof(b_lt)); 262 | 263 | size_t bit_address = 264 | (size_t)data->coverage_buffer_remote + data->coverage_buffer_next; 265 | arm64asm->EmitLoadLit(module, X1, 64, false, bit_address); 266 | size_t mov_address = GetCurrentInstrumentedAddress(module) - 8; 267 | 268 | uint32_t str_match = str(64, X0, X1, 0); 269 | WriteCode(module, &str_match, sizeof(str_match)); 270 | 271 | arm64asm->FixOffset(module, jmp_offset, module->instrumented_code_allocated); 272 | 273 | arm64asm->ReadRegStack(module, X0, 0); 274 | arm64asm->ReadRegStack(module, X1, 8); 275 | arm64asm->OffsetStack(module, 16); 276 | if (sp_offset) { 277 | arm64asm->OffsetStack(module, sp_offset); 278 | } 279 | 280 | CmpCoverageRecord *cmp_record = new CmpCoverageRecord(); 281 | cmp_record->ignored = false; 282 | cmp_record->width = operand_width; 283 | cmp_record->match_width = match_width; 284 | cmp_record->match_width_offset = match_width_offset; 285 | cmp_record->instrumentation_offset = instrumentation_start_offset; 286 | cmp_record->bb_address = bb_address; 287 | cmp_record->bb_offset = bb_offset; 288 | cmp_record->cmp_offset = cmp_offset; 289 | cmp_record->instrumentation_size = 290 | module->instrumented_code_allocated - instrumentation_start_offset; 291 | data->coverage_to_cmp[GetCmpCode(bb_offset, cmp_offset, 0)] = cmp_record; 292 | data->buf_to_cmp[data->coverage_buffer_next] = cmp_record; 293 | data->coverage_buffer_next++; 294 | 295 | // return INST_NOTHANDLED which causes 296 | // the original instruction to be repeated 297 | return INST_NOTHANDLED; 298 | } 299 | -------------------------------------------------------------------------------- /arch/arm64/reg.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Google LLC 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 | https ://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 ARCH_ARM64_REG_H 18 | #define ARCH_ARM64_REG_H 19 | 20 | #define ARCH_SP SP 21 | #define ARCH_PC PC 22 | #define ARCH_RETURN_VALUE_REGISTER X0 23 | #define ORIG_ADDR_REG X0 24 | 25 | enum Register { 26 | X0 = 0, 27 | X1, 28 | X2, 29 | X3, 30 | X4, 31 | X5, 32 | X6, 33 | X7, 34 | X8, 35 | X9, 36 | X10, 37 | X11, 38 | X12, 39 | X13, 40 | X14, 41 | X15, 42 | X16, 43 | X17, 44 | X18, 45 | X19, 46 | X20, 47 | X21, 48 | X22, 49 | X23, 50 | X24, 51 | X25, 52 | X26, 53 | X27, 54 | X28, 55 | X29, // fp 56 | X30, // lr 57 | X31, // sp 58 | PC, 59 | CPSR, 60 | FP = X29, 61 | LR = X30, 62 | SP = X31, 63 | XZR = X31, // magic.. 64 | }; 65 | 66 | #endif // ARCH_ARM64_REG_H 67 | -------------------------------------------------------------------------------- /arch/x86/reg.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Google LLC 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 | https ://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 ARCH_X86_REG_H 18 | #define ARCH_X86_REG_H 19 | 20 | #define ARCH_SP RSP 21 | #define ARCH_PC RIP 22 | #define ARCH_RETURN_VALUE_REGISTER RAX 23 | #define ARCH_PC RIP 24 | #define ORIG_ADDR_REG RAX 25 | 26 | enum Register { 27 | RAX, 28 | RCX, 29 | RDX, 30 | RBX, 31 | RSP, 32 | RBP, 33 | RSI, 34 | RDI, 35 | R8, 36 | R9, 37 | R10, 38 | R11, 39 | R12, 40 | R13, 41 | R14, 42 | R15, 43 | RIP, 44 | SYSCALL_RAX 45 | }; 46 | 47 | #endif // ARCH_X86_REG_H 48 | -------------------------------------------------------------------------------- /arch/x86/x86_assembler.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Google LLC 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 | https ://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 ARCH_X86_X86_ASSEMBLER_H 18 | #define ARCH_X86_X86_ASSEMBLER_H 19 | 20 | extern "C" { 21 | #include "xed/xed-interface.h" 22 | } 23 | 24 | #include "assembler.h" 25 | #include "tinyinst.h" 26 | 27 | class X86Assembler : public Assembler { 28 | public: 29 | using Assembler::Assembler; 30 | virtual ~X86Assembler() {} 31 | void Init() override; 32 | 33 | bool DecodeInstruction(Instruction &inst, 34 | const unsigned char *buffer, 35 | unsigned int buffer_size) override; 36 | void FixInstructionAndOutput(ModuleInfo *module, 37 | Instruction &inst, 38 | const unsigned char *input, 39 | const unsigned char *input_address_remote, 40 | bool convert_call_to_jmp = false) override; 41 | void HandleBasicBlockEnd( 42 | const char *address, 43 | ModuleInfo *module, std::set *queue, 44 | std::list> *offset_fixes, 45 | Instruction &inst, 46 | const char *code_ptr, 47 | size_t offset, 48 | size_t last_offset) override; 49 | 50 | void JmpAddress(ModuleInfo *module, size_t address) override; 51 | void Nop(ModuleInfo *module) override; 52 | void Ret(ModuleInfo *module) override; 53 | size_t Breakpoint(ModuleInfo *module) override; 54 | void Crash(ModuleInfo *module) override; 55 | 56 | void OffsetStack(ModuleInfo *module, int32_t offset) override; 57 | bool IsRipRelative(ModuleInfo *module, 58 | Instruction &inst, 59 | size_t instruction_address, 60 | size_t *mem_address) override; 61 | void TranslateJmp(ModuleInfo *module, 62 | ModuleInfo *target_module, 63 | size_t original_target, 64 | IndirectBreakpoinInfo& breakpoint_info, 65 | bool global_indirect, 66 | size_t previous_offset) override; 67 | void InstrumentLocalIndirect(ModuleInfo *module, 68 | Instruction &inst, 69 | size_t instruction_address, 70 | size_t bb_address) override; 71 | void InstrumentGlobalIndirect(ModuleInfo *module, 72 | Instruction &inst, 73 | size_t instruction_address) override; 74 | void FixOffset(ModuleInfo *module, 75 | uint32_t jmp_offset, 76 | uint32_t target_offset) override; 77 | private: 78 | inline void FixDisp4(ModuleInfo *module, int32_t disp); 79 | void ReadStack(ModuleInfo *module, int32_t offset); 80 | void WriteStack(ModuleInfo *module, int32_t offset); 81 | void MovIndirectTarget(ModuleInfo *module, 82 | Instruction &inst, 83 | size_t original_address, 84 | int32_t stack_offset); 85 | 86 | void InstrumentRet(ModuleInfo *module, 87 | Instruction &inst, 88 | size_t instruction_address, 89 | TinyInst::IndirectInstrumentation mode, 90 | size_t bb_address); 91 | void PushReturnAddress(ModuleInfo *module, uint64_t return_address); 92 | 93 | int xed_mmode_; 94 | }; 95 | 96 | #endif // ARCH_X86_X86_ASSEMBLER_H 97 | -------------------------------------------------------------------------------- /arch/x86/x86_helpers.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Google LLC 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 | https ://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 ARCH_X86_X86_HELPERS_H 18 | #define ARCH_X86_X86_HELPERS_H 19 | 20 | extern "C" { 21 | #include "xed/xed-interface.h" 22 | } 23 | 24 | xed_reg_enum_t GetFullSizeRegister(xed_reg_enum_t r, int child_ptr_size); 25 | xed_reg_enum_t GetUnusedRegister(xed_reg_enum_t used_register, int operand_width); 26 | xed_reg_enum_t Get8BitRegister(xed_reg_enum_t r); 27 | 28 | uint32_t Push(xed_state_t *dstate, xed_reg_enum_t r, unsigned char *encoded, size_t encoded_size); 29 | uint32_t Pop(xed_state_t *dstate, xed_reg_enum_t r, unsigned char *encoded, size_t encoded_size); 30 | 31 | uint32_t Mov(xed_state_t *dstate, uint32_t operand_width, 32 | xed_reg_enum_t base_reg, int32_t displacement, 33 | xed_reg_enum_t r2, unsigned char *encoded, 34 | size_t encoded_size); 35 | 36 | uint32_t Lzcnt(xed_state_t *dstate, uint32_t operand_width, 37 | xed_reg_enum_t dest_reg, xed_reg_enum_t src_reg, 38 | unsigned char *encoded, size_t encoded_size); 39 | 40 | uint32_t CmpImm8(xed_state_t *dstate, uint32_t operand_width, 41 | xed_reg_enum_t dest_reg, uint64_t imm, 42 | unsigned char *encoded, size_t encoded_size); 43 | 44 | void CopyOperandFromInstruction(xed_decoded_inst_t *src, 45 | xed_encoder_request_t *dest, 46 | xed_operand_enum_t src_operand_name, 47 | xed_operand_enum_t dest_operand_name, 48 | int dest_operand_index, 49 | size_t stack_offset); 50 | 51 | uint32_t GetInstructionLength(xed_encoder_request_t *inst); 52 | 53 | void FixRipDisplacement(xed_encoder_request_t *inst, 54 | size_t mem_address, 55 | size_t fixed_instruction_address); 56 | 57 | bool IsRspRelative(xed_decoded_inst_t* xedd, size_t* displacement); 58 | 59 | #endif // ARCH_X86_X86_HELPERS_H 60 | -------------------------------------------------------------------------------- /assembler.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Google LLC 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 | https ://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 ASSEMBLER_H 18 | #define ASSEMBLER_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "instruction.h" 25 | 26 | struct IndirectBreakpoinInfo; 27 | class TinyInst; 28 | class ModuleInfo; 29 | 30 | class Assembler { 31 | public: 32 | Assembler(TinyInst &tinyinst) : tinyinst_(tinyinst) {} 33 | virtual ~Assembler() = default; 34 | 35 | virtual void Init() = 0; 36 | 37 | virtual bool DecodeInstruction(Instruction &inst, 38 | const unsigned char *address, 39 | unsigned int max_size) = 0; 40 | virtual void FixInstructionAndOutput( 41 | ModuleInfo *module, 42 | Instruction &inst, 43 | const unsigned char *input, 44 | const unsigned char *input_address_remote, 45 | bool convert_call_to_jmp = false) = 0; 46 | virtual void HandleBasicBlockEnd( 47 | const char *address, ModuleInfo *module, 48 | std::set *queue, 49 | std::list> *offset_fixes, 50 | Instruction &inst, 51 | const char *code_ptr, 52 | size_t offset, 53 | size_t last_offset) = 0; 54 | 55 | virtual void JmpAddress(ModuleInfo *module, size_t address) = 0; 56 | virtual void Nop(ModuleInfo *module) = 0; 57 | virtual void Ret(ModuleInfo *module) = 0; 58 | virtual size_t Breakpoint(ModuleInfo *module) = 0; 59 | virtual void Crash(ModuleInfo *module) = 0; 60 | 61 | virtual void OffsetStack(ModuleInfo *module, int32_t offset) = 0; 62 | virtual bool IsRipRelative(ModuleInfo *module, 63 | Instruction &inst, 64 | size_t instruction_address, 65 | size_t *mem_address) = 0; 66 | virtual void TranslateJmp(ModuleInfo *module, 67 | ModuleInfo *target_module, 68 | size_t original_target, 69 | IndirectBreakpoinInfo& breakpoint_info, 70 | bool global_indirect, 71 | size_t previous_offset) = 0; 72 | virtual void InstrumentLocalIndirect(ModuleInfo *module, 73 | Instruction &inst, 74 | size_t instruction_address, 75 | size_t bb_address) = 0; 76 | virtual void InstrumentGlobalIndirect(ModuleInfo *module, 77 | Instruction &inst, 78 | size_t instruction_address) = 0; 79 | virtual void FixOffset(ModuleInfo *module, 80 | uint32_t jmp_offset, 81 | uint32_t target_offset) = 0; 82 | 83 | protected: 84 | TinyInst &tinyinst_; 85 | }; 86 | 87 | #endif // ASSEMBLER_H 88 | -------------------------------------------------------------------------------- /common.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Google LLC 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 | https ://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 | 22 | #include "common.h" 23 | 24 | uint64_t GetCurTime(void) { 25 | auto duration = std::chrono::system_clock::now().time_since_epoch(); 26 | auto millis = std::chrono::duration_cast(duration).count(); 27 | return millis; 28 | } 29 | 30 | bool BoolFromOptionValue(char *value) { 31 | if (_stricmp(value, "off") == 0) return false; 32 | if (_stricmp(value, "false") == 0) return false; 33 | if (_stricmp(value, "0") == 0) return false; 34 | return true; 35 | } 36 | 37 | bool GetBinaryOption(const char *name, int argc, char** argv, bool default_value) { 38 | for (int i = 0; i < argc; i++) { 39 | if (strcmp(argv[i], "--") == 0) break; 40 | if (strcmp(argv[i], name) == 0) { 41 | if ((i + 1) < argc && strcmp(argv[i + 1], "--")) { 42 | return BoolFromOptionValue(argv[i + 1]); 43 | } 44 | return true; 45 | } 46 | if (strncmp(argv[i], name, strlen(name)) == 0) { 47 | if (argv[i][strlen(name)] == '=') { 48 | return BoolFromOptionValue(argv[i] + strlen(name) + 1); 49 | } 50 | } 51 | } 52 | return default_value; 53 | } 54 | 55 | char *GetOption(const char *name, int argc, char** argv) { 56 | for (int i = 0; i < argc; i++) { 57 | if(strcmp(argv[i], "--") == 0) return NULL; 58 | if(strcmp(argv[i], name) == 0) { 59 | if ((i + 1) < argc && strcmp(argv[i + 1], "--")) { 60 | return argv[i + 1]; 61 | } else { 62 | return NULL; 63 | } 64 | } 65 | if (strncmp(argv[i], name, strlen(name)) == 0) { 66 | if (argv[i][strlen(name)] == '=') { 67 | return argv[i] + strlen(name) + 1; 68 | } 69 | } 70 | } 71 | return NULL; 72 | } 73 | 74 | 75 | void GetOptionAll(const char *name, int argc, char** argv, std::list *results) { 76 | for (int i = 0; i < argc; i++) { 77 | if (strcmp(argv[i], "--") == 0) return; 78 | if (strcmp(argv[i], name) == 0) { 79 | if ((i + 1) < argc && strcmp(argv[i + 1], "--")) { 80 | results->push_back(argv[i + 1]); 81 | } else { 82 | return; 83 | } 84 | } 85 | if (strncmp(argv[i], name, strlen(name)) == 0) { 86 | if (argv[i][strlen(name)] == '=') { 87 | results->push_back(argv[i] + strlen(name) + 1); 88 | } 89 | } 90 | } 91 | } 92 | 93 | int GetIntOption(const char *name, int argc, char** argv, int default_value) { 94 | char *option = GetOption(name, argc, argv); 95 | if (!option) return default_value; 96 | return (int)strtol(option, NULL, 0); 97 | } 98 | 99 | 100 | //quoting on Windows is weird 101 | size_t ArgvEscapeWindows(char *in, char *out) { 102 | int needs_quoting = 0; 103 | size_t size = 0; 104 | char *p = in; 105 | size_t i; 106 | 107 | //check if quoting is necessary 108 | if (strchr(in, ' ')) needs_quoting = 1; 109 | if (strchr(in, '\"')) needs_quoting = 1; 110 | if (strchr(in, '\t')) needs_quoting = 1; 111 | if (strchr(in, '\n')) needs_quoting = 1; 112 | if (strchr(in, '\v')) needs_quoting = 1; 113 | if (!needs_quoting) { 114 | size = strlen(in); 115 | if (out) memcpy(out, in, size); 116 | return size; 117 | } 118 | 119 | if (out) out[size] = '\"'; 120 | size++; 121 | 122 | while (*p) { 123 | size_t num_backslashes = 0; 124 | while ((*p) && (*p == '\\')) { 125 | p++; 126 | num_backslashes++; 127 | } 128 | 129 | if (*p == 0) { 130 | for (i = 0; i < (num_backslashes * 2); i++) { 131 | if (out) out[size] = '\\'; 132 | size++; 133 | } 134 | break; 135 | } 136 | else if (*p == '\"') { 137 | for (i = 0; i < (num_backslashes * 2 + 1); i++) { 138 | if (out) out[size] = '\\'; 139 | size++; 140 | } 141 | if (out) out[size] = *p; 142 | size++; 143 | } 144 | else { 145 | for (i = 0; i < num_backslashes; i++) { 146 | if (out) out[size] = '\\'; 147 | size++; 148 | } 149 | if (out) out[size] = *p; 150 | size++; 151 | } 152 | 153 | p++; 154 | } 155 | 156 | if (out) out[size] = '\"'; 157 | size++; 158 | 159 | return size; 160 | } 161 | 162 | size_t ArgvEscapeMacOS(char *in, char *out) { 163 | size_t size = 0; 164 | char *p = in; 165 | while (*p) { 166 | if (strchr("|&:;()<>~*@?!$#[]{}\\ '\"`\t\n\v\r", *p)) { 167 | if (out) out[size] = '\\'; 168 | size++; 169 | } 170 | 171 | if (out) out[size] = *p; 172 | size++; 173 | 174 | p++; 175 | } 176 | 177 | return size; 178 | } 179 | 180 | 181 | char *ArgvToCmd(int argc, char** argv) { 182 | size_t len = 0; 183 | int i; 184 | char* buf, *ret; 185 | 186 | for (i = 0; i < argc; i++) 187 | len += ArgvEscape(argv[i], NULL) + 1; 188 | 189 | if (!len) FATAL("Error creating command line"); 190 | 191 | buf = ret = (char *)malloc(len); 192 | 193 | for (i = 0; i < argc; i++) { 194 | size_t l = ArgvEscape(argv[i], buf); 195 | buf += l; 196 | *(buf++) = ' '; 197 | } 198 | 199 | ret[len - 1] = 0; 200 | 201 | // printf("%s\n", ret); 202 | 203 | return ret; 204 | } 205 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Google LLC 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 | https ://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 COMMON_H 18 | #define COMMON_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) 27 | #include 28 | #define ArgvEscape ArgvEscapeWindows 29 | #else 30 | #include 31 | #ifndef MAX_PATH 32 | #define MAX_PATH PATH_MAX 33 | #endif 34 | 35 | #include 36 | #define _stricmp strcasecmp 37 | 38 | #define ArgvEscape ArgvEscapeMacOS 39 | #endif 40 | 41 | #define SAY(...) printf(__VA_ARGS__) 42 | 43 | #define WARN(...) do { \ 44 | SAY("[!] WARNING: " __VA_ARGS__); \ 45 | SAY("\n"); \ 46 | } while (0) 47 | 48 | #define FATAL(...) do { \ 49 | SAY("[-] PROGRAM ABORT : " __VA_ARGS__); \ 50 | SAY(" Location : %s(), %s:%u\n\n", \ 51 | __FUNCTION__, __FILE__, __LINE__); \ 52 | exit(1); \ 53 | } while (0) 54 | 55 | #define USAGE_CHECK(condition, message) if(!(condition)) FATAL("%s\n", message); 56 | 57 | struct AddressRange { 58 | size_t from; 59 | size_t to; 60 | char *data; 61 | }; 62 | 63 | // gets time in milliseconds 64 | uint64_t GetCurTime(void); 65 | 66 | char *GetOption(const char *name, int argc, char** argv); 67 | void GetOptionAll(const char *name, int argc, char** argv, std::list *results); 68 | bool GetBinaryOption(const char *name, int argc, char** argv, bool default_value); 69 | int GetIntOption(const char *name, int argc, char** argv, int default_value); 70 | 71 | char *ArgvToCmd(int argc, char** argv); 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /coverage.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Google LLC 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 | https ://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 _CRT_SECURE_NO_WARNINGS 18 | 19 | #include "common.h" 20 | #include "coverage.h" 21 | #include 22 | 23 | ModuleCoverage::ModuleCoverage() { 24 | module_name[0] = 0; 25 | } 26 | 27 | ModuleCoverage::ModuleCoverage(std::string& name, std::set offsets) { 28 | module_name = name; 29 | this->offsets = offsets; 30 | } 31 | 32 | ModuleCoverage *GetModuleCoverage(Coverage &coverage, std::string& name) { 33 | for (auto iter = coverage.begin(); iter != coverage.end(); iter++) { 34 | if (_stricmp(iter->module_name.c_str(), name.c_str()) == 0) { 35 | return &(*iter); 36 | } 37 | } 38 | return NULL; 39 | } 40 | 41 | void MergeCoverage(Coverage &coverage, Coverage &toAdd) { 42 | for (auto iter = toAdd.begin(); iter != toAdd.end(); iter++) { 43 | ModuleCoverage *module_coverage = GetModuleCoverage(coverage, iter->module_name); 44 | if (module_coverage) { 45 | module_coverage->offsets.insert( 46 | iter->offsets.begin(), 47 | iter->offsets.end()); 48 | } else { 49 | coverage.push_back({ iter->module_name, iter->offsets }); 50 | } 51 | } 52 | } 53 | 54 | void CoverageIntersection(Coverage &coverage1, 55 | Coverage &coverage2, 56 | Coverage &result) 57 | { 58 | for (auto iter = coverage1.begin(); iter != coverage1.end(); iter++) { 59 | ModuleCoverage *module1 = &(*iter); 60 | ModuleCoverage *module2 = GetModuleCoverage(coverage2, iter->module_name); 61 | if (!module2) continue; 62 | std::set offsets; 63 | for (auto offset1 = module1->offsets.begin(); 64 | offset1 != module1->offsets.end(); offset1++) 65 | { 66 | if (module2->offsets.find(*offset1) != module2->offsets.end()) { 67 | offsets.insert(*offset1); 68 | } 69 | } 70 | if (!offsets.empty()) { 71 | result.push_back({ iter->module_name, offsets }); 72 | } 73 | } 74 | } 75 | 76 | void CoverageSymmetricDifference(Coverage &coverage1, 77 | Coverage &coverage2, 78 | Coverage &result) 79 | { 80 | for (auto iter = coverage1.begin(); iter != coverage1.end(); iter++) { 81 | ModuleCoverage *module1 = &(*iter); 82 | ModuleCoverage *module2 = GetModuleCoverage(coverage2, iter->module_name); 83 | if (!module2) { 84 | result.push_back({ iter->module_name, iter->offsets }); 85 | } else { 86 | std::set offsets; 87 | for (auto offset1 = module1->offsets.begin(); 88 | offset1 != module1->offsets.end(); offset1++) 89 | { 90 | if (module2->offsets.find(*offset1) == module2->offsets.end()) { 91 | offsets.insert(*offset1); 92 | } 93 | } 94 | for (auto offset2 = module2->offsets.begin(); 95 | offset2 != module2->offsets.end(); offset2++) 96 | { 97 | if (module1->offsets.find(*offset2) == module1->offsets.end()) { 98 | offsets.insert(*offset2); 99 | } 100 | } 101 | if (!offsets.empty()) { 102 | result.push_back({ iter->module_name, offsets }); 103 | } 104 | } 105 | } 106 | // still need to add coverage for modules in coverage2 107 | // not present in coverage1 108 | for (auto iter = coverage2.begin(); iter != coverage2.end(); iter++) { 109 | ModuleCoverage *module2 = &(*iter); 110 | ModuleCoverage *module1 = GetModuleCoverage(coverage1, iter->module_name); 111 | if (!module1) { 112 | result.push_back({ iter->module_name, iter->offsets }); 113 | } 114 | } 115 | } 116 | 117 | // returns coverage2 not present in coverage1 118 | void CoverageDifference(Coverage &coverage1, 119 | Coverage &coverage2, 120 | Coverage &result) 121 | { 122 | for (auto iter = coverage2.begin(); iter != coverage2.end(); iter++) { 123 | ModuleCoverage *module1 = GetModuleCoverage(coverage1, iter->module_name); 124 | ModuleCoverage *module2 = &(*iter); 125 | if (!module1) { 126 | result.push_back({ iter->module_name, iter->offsets }); 127 | } else { 128 | std::set offsets; 129 | for (auto offset2 = module2->offsets.begin(); 130 | offset2 != module2->offsets.end(); offset2++) 131 | { 132 | if (module1->offsets.find(*offset2) == module1->offsets.end()) { 133 | offsets.insert(*offset2); 134 | } 135 | } 136 | if (!offsets.empty()) { 137 | result.push_back({ iter->module_name, offsets }); 138 | } 139 | } 140 | } 141 | } 142 | 143 | bool CoverageContains(Coverage &coverage1, Coverage &coverage2) { 144 | for (auto iter = coverage2.begin(); iter != coverage2.end(); iter++) { 145 | ModuleCoverage *module2 = &(*iter); 146 | ModuleCoverage *module1 = GetModuleCoverage(coverage1, iter->module_name); 147 | if (!module1) { 148 | return false; 149 | } 150 | for (auto offset_iter = module2->offsets.begin(); 151 | offset_iter != module2->offsets.end(); offset_iter++) 152 | { 153 | if (module1->offsets.find(*offset_iter) == module1->offsets.end()) { 154 | return false; 155 | } 156 | } 157 | } 158 | return true; 159 | } 160 | 161 | 162 | void WriteCoverage(Coverage& coverage, const char *filename) { 163 | FILE *fp = fopen(filename, "w"); 164 | if (!fp) { 165 | printf("Error opening %s\n", filename); 166 | return; 167 | } 168 | for (auto iter = coverage.begin(); iter != coverage.end(); iter++) { 169 | for (auto offsetiter = iter->offsets.begin(); 170 | offsetiter != iter->offsets.end(); offsetiter++) 171 | { 172 | // skip cmp and other special coverage types 173 | if(*offsetiter & 0x8000000000000000ULL) continue; 174 | 175 | fprintf(fp, "%s+%llx\n", iter->module_name.c_str(), *offsetiter); 176 | } 177 | } 178 | fclose(fp); 179 | } 180 | 181 | void WriteCoverageBinary(Coverage& coverage, FILE *fp) { 182 | uint64_t num_modules = coverage.size(); 183 | fwrite(&num_modules, sizeof(num_modules), 1, fp); 184 | for (auto iter = coverage.begin(); iter != coverage.end(); iter++) { 185 | uint64_t str_size = iter->module_name.size(); 186 | fwrite(&str_size, sizeof(str_size), 1, fp); 187 | fwrite(iter->module_name.data(), str_size, 1, fp); 188 | uint64_t num_offsets = iter->offsets.size(); 189 | fwrite(&num_offsets, sizeof(num_offsets), 1, fp); 190 | uint64_t *offsets = (uint64_t *)malloc((size_t)num_offsets * sizeof(uint64_t)); 191 | size_t i = 0; 192 | for (auto iter2 = iter->offsets.begin(); iter2 != iter->offsets.end(); iter2++) { 193 | offsets[i] = *iter2; 194 | i++; 195 | } 196 | fwrite(offsets, sizeof(uint64_t), (size_t)num_offsets, fp); 197 | free(offsets); 198 | } 199 | } 200 | 201 | void WriteCoverageBinary(Coverage& coverage, char *filename) { 202 | FILE *fp = fopen(filename, "wb"); 203 | if (!fp) { 204 | printf("Error opening %s\n", filename); 205 | return; 206 | } 207 | WriteCoverageBinary(coverage, fp); 208 | fclose(fp); 209 | } 210 | 211 | void ReadCoverageBinary(Coverage& coverage, FILE *fp) { 212 | uint64_t num_modules; 213 | fread(&num_modules, sizeof(num_modules), 1, fp); 214 | for (size_t m = 0; m < num_modules; m++) { 215 | ModuleCoverage module_coverage; 216 | uint64_t str_size; 217 | fread(&str_size, sizeof(str_size), 1, fp); 218 | char* str_data = (char*)malloc(str_size); 219 | fread(str_data, str_size, 1, fp); 220 | module_coverage.module_name = std::string(str_data, str_size); 221 | free(str_data); 222 | uint64_t num_offsets; 223 | fread(&num_offsets, sizeof(num_offsets), 1, fp); 224 | uint64_t *offsets = (uint64_t *)malloc((size_t)num_offsets * sizeof(uint64_t)); 225 | fread(offsets, sizeof(uint64_t), (size_t)num_offsets, fp); 226 | for (size_t i = 0; i < num_offsets; i++) { 227 | module_coverage.offsets.insert(offsets[i]); 228 | } 229 | free(offsets); 230 | coverage.push_back(module_coverage); 231 | } 232 | } 233 | 234 | void ReadCoverageBinary(Coverage& coverage, char *filename) { 235 | FILE *fp = fopen(filename, "rb"); 236 | if (!fp) { 237 | printf("Error opening %s\n", filename); 238 | return; 239 | } 240 | ReadCoverageBinary(coverage, fp); 241 | fclose(fp); 242 | } 243 | 244 | void PrintCoverage(Coverage& coverage) { 245 | for (auto iter = coverage.begin(); iter != coverage.end(); iter++) { 246 | printf("%s\n", iter->module_name.c_str()); 247 | for (auto offsetiter = iter->offsets.begin(); 248 | offsetiter != iter->offsets.end(); offsetiter++) 249 | { 250 | printf("0x%llx ", *offsetiter); 251 | } 252 | printf("\n"); 253 | } 254 | } 255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /coverage.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Google LLC 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 | https ://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 COVERAGE_H 18 | #define COVERAGE_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | class ModuleCoverage { 26 | public: 27 | ModuleCoverage(); 28 | ModuleCoverage(std::string& name, std::set offsets); 29 | 30 | std::string module_name; 31 | std::set offsets; 32 | }; 33 | 34 | typedef std::list Coverage; 35 | 36 | ModuleCoverage *GetModuleCoverage(Coverage &coverage, std::string &name); 37 | 38 | void PrintCoverage(Coverage& coverage); 39 | void WriteCoverage(Coverage& coverage, const char *filename); 40 | 41 | void MergeCoverage(Coverage &coverage, Coverage &toAdd); 42 | void CoverageIntersection(Coverage &coverage1, 43 | Coverage &coverage2, 44 | Coverage &result); 45 | // returns coverage2 not present in coverage1 46 | void CoverageDifference(Coverage &coverage1, 47 | Coverage &coverage2, 48 | Coverage &result); 49 | // returns coverage2 not present in coverage1 and vice versa 50 | void CoverageSymmetricDifference(Coverage &coverage1, 51 | Coverage &coverage2, 52 | Coverage &result); 53 | bool CoverageContains(Coverage &coverage1, Coverage &coverage2); 54 | 55 | void ReadCoverageBinary(Coverage& coverage, char *filename); 56 | void ReadCoverageBinary(Coverage& coverage, FILE *fp); 57 | void WriteCoverageBinary(Coverage& coverage, char *filename); 58 | void WriteCoverageBinary(Coverage& coverage, FILE *fp); 59 | 60 | #endif // COVERAGE_H 61 | 62 | -------------------------------------------------------------------------------- /hook.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Google LLC 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 | https ://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 "common.h" 18 | #include "hook.h" 19 | 20 | HookContext::HookContext(size_t num_arguments) { 21 | this->num_arguments = num_arguments; 22 | arguments = NULL; 23 | if(num_arguments) { 24 | arguments = (uint64_t *)malloc(num_arguments * sizeof(uint64_t)); 25 | } 26 | sp = 0; 27 | return_address = 0; 28 | args_changed = false; 29 | user_data = NULL; 30 | } 31 | 32 | HookContext::~HookContext() { 33 | if(arguments) free(arguments); 34 | } 35 | 36 | uint64_t HookContext::GetArg(size_t index) { 37 | if(index >= num_arguments) { 38 | FATAL("Requested argument exceeds function number of arguments"); 39 | } 40 | return arguments[index]; 41 | } 42 | 43 | void HookContext::SetArg(size_t index, uint64_t value) { 44 | if(index >= num_arguments) { 45 | FATAL("Requested argument exceeds function number of arguments"); 46 | } 47 | arguments[index] = value; 48 | args_changed = true; 49 | } 50 | 51 | uint64_t Hook::GetArg(size_t index) { 52 | if(!current_context) { 53 | FATAL("Can't access arguments, did the function run yet?"); 54 | } 55 | 56 | return current_context->GetArg(index); 57 | } 58 | 59 | void Hook::SetArg(size_t index, uint64_t value) { 60 | if(!current_context) { 61 | FATAL("Can't access arguments, did the function run yet?"); 62 | } 63 | 64 | current_context->SetArg(index, value); 65 | } 66 | 67 | uint64_t Hook::GetRegister(Register r) { 68 | return tinyinst->GetRegister(r); 69 | } 70 | 71 | void Hook::SetRegister(Register r, uint64_t value) { 72 | tinyinst->SetRegister(r, value); 73 | } 74 | 75 | void Hook::SetReturnValue(uint64_t value) { 76 | tinyinst->SetRegister(ARCH_RETURN_VALUE_REGISTER, value); 77 | } 78 | 79 | uint64_t Hook::GetReturnValue() { 80 | return tinyinst->GetRegister(ARCH_RETURN_VALUE_REGISTER); 81 | } 82 | 83 | uint64_t Hook::GetReturnAddress() { 84 | return tinyinst->GetReturnAddress(); 85 | } 86 | 87 | void Hook::SetReturnAddress(uint64_t address) { 88 | tinyinst->SetReturnAddress(address); 89 | } 90 | 91 | void Hook::RemoteWrite(void *address, const void *buffer, size_t size) { 92 | tinyinst->RemoteWrite(address, buffer, size); 93 | } 94 | 95 | void Hook::RemoteRead(void *address, void *buffer, size_t size) { 96 | tinyinst->RemoteRead(address, buffer, size); 97 | } 98 | 99 | void *Hook::RemoteAllocate(size_t size, MemoryProtection protection) { 100 | return tinyinst->RemoteAllocate(size, protection); 101 | } 102 | 103 | void Hook::WriteCode(ModuleInfo *module, void *data, size_t size) { 104 | tinyinst->WriteCode(module, data, size); 105 | } 106 | 107 | void Hook::CommitCode(ModuleInfo *module, size_t start_offset, size_t size) { 108 | tinyinst->CommitCode(module, start_offset, size); 109 | } 110 | 111 | size_t Hook::GetCurrentInstrumentedAddress(ModuleInfo *module) { 112 | return tinyinst->GetCurrentInstrumentedAddress(module); 113 | } 114 | 115 | HookContext *Hook::CreateContext() { 116 | HookContext *context = new HookContext(num_args); 117 | uint64_t sp = tinyinst->GetRegister(ARCH_SP); 118 | context->sp = sp; 119 | context->return_address = GetReturnAddress(); 120 | tinyinst->GetFunctionArguments(context->arguments, num_args, sp, callconv); 121 | return context; 122 | } 123 | 124 | void Hook::CommitContext() { 125 | if(!current_context->args_changed) return; 126 | tinyinst->SetFunctionArguments(current_context->arguments, 127 | current_context->num_arguments, 128 | current_context->sp, callconv); 129 | 130 | } 131 | 132 | InstructionResult HookReplace::InstrumentFunction(ModuleInfo* module, size_t function_address) { 133 | entry_breakpoints.insert(assembler->Breakpoint(module)); 134 | WriteCodeBefore(module); 135 | assembler->Ret(module); 136 | return INST_STOPBB; 137 | } 138 | 139 | bool HookReplace::HandleBreakpoint(ModuleInfo *module, void *address) { 140 | if(entry_breakpoints.find((size_t)address) != entry_breakpoints.end()) { 141 | current_context = CreateContext(); 142 | 143 | OnFunctionEntered(); 144 | 145 | CommitContext(); 146 | delete current_context; 147 | current_context = NULL; 148 | 149 | return true; 150 | } 151 | 152 | return false; 153 | } 154 | 155 | void HookReplace::OnProcessExit() { 156 | entry_breakpoints.clear(); 157 | if(current_context) delete current_context; 158 | } 159 | 160 | InstructionResult HookBegin::InstrumentFunction(ModuleInfo* module, size_t function_address) { 161 | entry_breakpoints.insert(assembler->Breakpoint(module)); 162 | WriteCodeBefore(module); 163 | return INST_NOTHANDLED; 164 | } 165 | 166 | bool HookBegin::HandleBreakpoint(ModuleInfo *module, void *address) { 167 | if(entry_breakpoints.find((size_t)address) != entry_breakpoints.end()) { 168 | current_context = CreateContext(); 169 | 170 | OnFunctionEntered(); 171 | 172 | CommitContext(); 173 | delete current_context; 174 | current_context = NULL; 175 | 176 | return true; 177 | } 178 | 179 | return false; 180 | } 181 | 182 | void HookBegin::OnProcessExit() { 183 | entry_breakpoints.clear(); 184 | if(current_context) delete current_context; 185 | } 186 | 187 | 188 | InstructionResult HookBeginEnd::InstrumentFunction(ModuleInfo* module, size_t function_address) { 189 | entry_breakpoints.insert(assembler->Breakpoint(module)); 190 | WriteCodeBefore(module); 191 | return INST_NOTHANDLED; 192 | } 193 | 194 | bool HookBeginEnd::HandleBreakpoint(ModuleInfo *module, void *address) { 195 | if(entry_breakpoints.find((size_t)address) != entry_breakpoints.end()) { 196 | current_context = CreateContext(); 197 | 198 | OnFunctionEntered(); 199 | 200 | CommitContext(); 201 | 202 | HookTrailer *trailer; 203 | if(!unused_trailers.empty()) { 204 | trailer = *unused_trailers.begin(); 205 | unused_trailers.pop_front(); 206 | } else { 207 | trailer = CreateTrailer(module); 208 | } 209 | 210 | // we can't rely on current_context being the same when we reach 211 | // breakpoint_after, because the hooked function could have been 212 | // entered from another thread or called recursively 213 | // that's why we allocate unique breakpoint per call and associate 214 | // context with it 215 | trailer->context = current_context; 216 | breakpoints_after[trailer->breakpoint] = trailer; 217 | 218 | SetReturnAddress(trailer->trailer_start); 219 | 220 | return true; 221 | } 222 | 223 | auto iter = breakpoints_after.find((uint64_t)address); 224 | if(iter != breakpoints_after.end()) { 225 | HookTrailer *trailer = iter->second; 226 | current_context = trailer->context; 227 | 228 | OnFunctionReturned(); 229 | 230 | SetRegister(ARCH_PC, current_context->return_address); 231 | 232 | unused_trailers.push_back(trailer); 233 | breakpoints_after.erase(iter); 234 | 235 | trailer->context = NULL; 236 | delete current_context; 237 | current_context = NULL; 238 | 239 | return true; 240 | } 241 | 242 | return false; 243 | } 244 | 245 | HookTrailer *HookBeginEnd::CreateTrailer(ModuleInfo *module) { 246 | HookTrailer *trailer = new HookTrailer(); 247 | trailer->trailer_start = GetCurrentInstrumentedAddress(module); 248 | 249 | size_t code_size_before = module->instrumented_code_allocated; 250 | 251 | WriteCodeAfter(module); 252 | // Breakpoint needs to be *after* user-inserted code 253 | // because when resolving the breakpoint, we jump 254 | // to the real return address 255 | trailer->breakpoint = assembler->Breakpoint(module); 256 | 257 | size_t code_size_after = module->instrumented_code_allocated; 258 | CommitCode(module, code_size_before, (code_size_after - code_size_before)); 259 | 260 | return trailer; 261 | } 262 | 263 | void HookBeginEnd::OnProcessExit() { 264 | entry_breakpoints.clear(); 265 | for(auto iter = breakpoints_after.begin(); iter != breakpoints_after.end(); iter++) { 266 | if(iter->second->context) delete iter->second->context; 267 | delete iter->second; 268 | } 269 | breakpoints_after.clear(); 270 | for(auto iter = unused_trailers.begin(); iter != unused_trailers.end(); iter++) { 271 | delete *iter; 272 | } 273 | unused_trailers.clear(); 274 | } 275 | -------------------------------------------------------------------------------- /hook.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Google LLC 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 | https ://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 HOOK_H 18 | #define HOOK_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "tinyinst.h" 28 | 29 | class TinyInst; 30 | class Assembler; 31 | 32 | class HookContext { 33 | public: 34 | HookContext(size_t num_arguments); 35 | ~HookContext(); 36 | 37 | uint64_t GetArg(size_t index); 38 | void SetArg(size_t index, uint64_t value); 39 | 40 | size_t num_arguments; 41 | uint64_t *arguments; 42 | uint64_t sp; 43 | uint64_t return_address; 44 | bool args_changed; 45 | 46 | // clients can store their per-call data here 47 | void *user_data; 48 | }; 49 | 50 | class HookTrailer { 51 | public: 52 | uint64_t trailer_start; 53 | uint64_t breakpoint; 54 | HookContext *context; 55 | }; 56 | 57 | class Hook { 58 | public: 59 | Hook(const char *module_name, const char *function_name, size_t num_args, CallingConvention call_convention = CALLCONV_DEFAULT) 60 | : module_name(module_name), function_name(function_name), callconv(call_convention), num_args(num_args), current_context(NULL), function_offset(0) { } 61 | 62 | Hook(const char *module_name, size_t offset, size_t num_args, CallingConvention call_convention = CALLCONV_DEFAULT) 63 | : module_name(module_name), function_offset(offset), callconv(call_convention), num_args(num_args), current_context(NULL) { } 64 | 65 | virtual void OnProcessExit() { } 66 | 67 | std::string &GetModuleName() { 68 | return module_name; 69 | } 70 | 71 | std::string &GetFunctionName() { 72 | return function_name; 73 | } 74 | 75 | uint64_t GetFunctionOffset() { 76 | return function_offset; 77 | } 78 | 79 | void SetTinyInst(TinyInst *tinyinst) { 80 | this->tinyinst = tinyinst; 81 | } 82 | 83 | void SetAssembler(Assembler *assembler) { 84 | this->assembler = assembler; 85 | } 86 | 87 | virtual InstructionResult InstrumentFunction(ModuleInfo* module, size_t function_address) = 0; 88 | 89 | virtual bool HandleBreakpoint(ModuleInfo *module, void *address) { return false; } 90 | 91 | protected: 92 | uint64_t GetArg(size_t index); 93 | void SetArg(size_t index, uint64_t value); 94 | uint64_t GetRegister(Register r); 95 | void SetRegister(Register r, uint64_t value); 96 | void SetReturnValue(uint64_t value); 97 | uint64_t GetReturnValue(); 98 | uint64_t GetReturnAddress(); 99 | void SetReturnAddress(uint64_t address); 100 | 101 | void RemoteWrite(void *address, const void *buffer, size_t size); 102 | void RemoteRead(void *address, void *buffer, size_t size); 103 | void *RemoteAllocate(size_t size, MemoryProtection protection); 104 | 105 | 106 | void SaveArgs(); 107 | void RestoreArgs(); 108 | 109 | void WriteCode(ModuleInfo *module, void *data, size_t size); 110 | void CommitCode(ModuleInfo *module, size_t start_offset, size_t size); 111 | 112 | size_t GetCurrentInstrumentedAddress(ModuleInfo *module); 113 | 114 | HookContext *CreateContext(); 115 | void CommitContext(); 116 | 117 | protected: 118 | HookContext *current_context; 119 | Assembler *assembler; 120 | TinyInst *tinyinst; 121 | std::string module_name; 122 | std::string function_name; 123 | uint64_t function_offset; 124 | CallingConvention callconv; 125 | size_t num_args; 126 | }; 127 | 128 | class HookReplace : public Hook { 129 | public: 130 | HookReplace(const char *module_name, const char *function_name, size_t num_args, CallingConvention call_convention = CALLCONV_DEFAULT) : Hook(module_name, function_name, num_args, call_convention) { } 131 | 132 | HookReplace(const char *module_name, size_t offset, size_t num_args, CallingConvention call_convention = CALLCONV_DEFAULT) : Hook(module_name, offset, num_args, call_convention) { } 133 | 134 | protected: 135 | virtual InstructionResult InstrumentFunction(ModuleInfo* module, size_t function_address); 136 | 137 | virtual bool HandleBreakpoint(ModuleInfo *module, void *address); 138 | 139 | virtual void WriteCodeBefore(ModuleInfo* module) { } 140 | 141 | virtual void OnFunctionEntered() { } 142 | 143 | virtual void OnProcessExit(); 144 | 145 | private: 146 | std::unordered_set entry_breakpoints; 147 | }; 148 | 149 | class HookBegin : public Hook { 150 | public: 151 | HookBegin(const char *module_name, const char *function_name, size_t num_args, CallingConvention call_convention = CALLCONV_DEFAULT) : Hook(module_name, function_name, num_args, call_convention) { } 152 | 153 | HookBegin(const char *module_name, size_t offset, size_t num_args, CallingConvention call_convention = CALLCONV_DEFAULT) : Hook(module_name, offset, num_args, call_convention) { } 154 | 155 | protected: 156 | virtual InstructionResult InstrumentFunction(ModuleInfo* module, size_t function_address); 157 | 158 | virtual bool HandleBreakpoint(ModuleInfo *module, void *address); 159 | 160 | virtual void WriteCodeBefore(ModuleInfo* module) { } 161 | 162 | virtual void OnFunctionEntered() { } 163 | 164 | virtual void OnProcessExit(); 165 | 166 | private: 167 | std::unordered_set entry_breakpoints; 168 | }; 169 | 170 | class HookBeginEnd : public Hook { 171 | public: 172 | HookBeginEnd(const char *module_name, const char *function_name, size_t num_args, CallingConvention call_convention = CALLCONV_DEFAULT) : Hook(module_name, function_name, num_args, call_convention) { } 173 | 174 | HookBeginEnd(const char *module_name, size_t offset, size_t num_args, CallingConvention call_convention = CALLCONV_DEFAULT) : Hook(module_name, offset, num_args, call_convention) { } 175 | 176 | protected: 177 | virtual InstructionResult InstrumentFunction(ModuleInfo* module, size_t function_address); 178 | 179 | virtual bool HandleBreakpoint(ModuleInfo *module, void *address); 180 | 181 | virtual void WriteCodeBefore(ModuleInfo* module) { } 182 | virtual void WriteCodeAfter(ModuleInfo* module) { } 183 | 184 | virtual void OnFunctionEntered() { } 185 | 186 | virtual void OnFunctionReturned() { } 187 | 188 | virtual void OnProcessExit(); 189 | 190 | private: 191 | HookTrailer *CreateTrailer(ModuleInfo *module); 192 | 193 | std::unordered_set entry_breakpoints; 194 | std::unordered_map breakpoints_after; 195 | std::list unused_trailers; 196 | }; 197 | 198 | 199 | 200 | #endif /* HOOK_H */ 201 | -------------------------------------------------------------------------------- /hook.md: -------------------------------------------------------------------------------- 1 | ## Hook API 2 | 3 | In addition to the general-purpose low-level API that allows inserting code on every instruction, basic block or edge, TinyInst also implements a hooking API that is better suited for inspecting and modifying behavior of individual functions. 4 | 5 | Users can write hooks by creating a class (for each function they wish to hook) that inherits from one of the Hook classes explained below. Inside the class constructor, the user describes the function they want to hook by specifying module name (`*` applies the hook to all instrumented modules), function name or offset, number of function arguments and the calling convention. 6 | 7 | Currently implemented Hook classes are 8 | 9 | - `HookReplace` - Useful for completely replacing the implementation of a function (the original function code never runs). The user gets a breakpoint when the function is supposed to run which they can then handle by implementing `OnFunctionEntered` method. The user also gets the ability to insert alternate function assembly by implementing `WriteCodeBefore` method. 10 | 11 | - `HookBegin` - Hooks just the start of the function. The original function code runs, but before it does, the user gets a breakpointwhich (`OnFunctionEntered`). The user also gets the ability to insert assembly code that runs before the original function implementation by implementing `WriteCodeBefore` method. 12 | 13 | - `HookBeginEnd` - Hooks both the start and the end of the function. The user gets a breakpoint both when the function enters (`OnFunctionEntered`) and immediately after the function returns (`OnFunctionReturned`). The user can also insert additional assembly code that runs before the function code (`WriteCodeBefore`) and after the function returns (`WriteCodeAfter`). 14 | 15 | In all cases the order of events when calling the hooked function is 16 | - Breakpoint handler `OnFunctionEntered` gets called. 17 | - Code inserted during `WriteCodeBefore` runs . 18 | - (Except in `HookReplace`) Original function code runs. 19 | - (In `HookBeginEnd`) Code inserted during `WriteCodeAfter` runs. 20 | - (In `HookBeginEnd`) Breakpoint handler `OnFunctionReturned` gets called. 21 | 22 | It is expected that most hooking operations can be performed just using the breakpoint handlers without the need to insert assembly code. Note however that breakpoint handlers run in a process (TinyInst process) different than the target process. This means that any memory read/write operations on the target process must be performed using `RemoteRead`/`RemoteWrite` instead of reading/writing memory directly. There are other useful function the breakpoint handlers can call such as for getting ans setting function arguments (`GetArg/SetArg`, please note arguments are zero-indexed), getting and setting registers (`GetRegister`/`SetRegister`), getting and setting function return value (`GetReturnValue`/`SetReturnValue`, mostly useful inside the `OnFunctionReturned` or inside `OnFunctionEntered` for `HookReplace`) and allocating memory in the target process (`RemoteAllocate`). 23 | 24 | If a hook needs to add additional assembly code, this can be done by implementing `WriteCodeBefore`/`WriteCodeAfter` methods of the hook class. Assembly code can be inserted by calling the `WriteCode` function with the buffer containing the assembly to be inserted. Note that both `WriteCodeBefore`/`WriteCodeAfter` get called during instrumentation time (before the function gets run) and, due to how `HookBeginEnd` is implemented, `WriteCodeAfter` can be called multiple times for a single hooked function. 25 | 26 | Once the hook classes have been implemented for each function the user wants to hook, the user can register them by calling `RegisterHook` method inside their client's constructor. 27 | 28 | ### Example 29 | 30 | A small example on how to use the Hook API is provided in [sslhook.h](https://github.com/googleprojectzero/TinyInst/blob/master/sslhook.h) and [sslhook.cpp](https://github.com/googleprojectzero/TinyInst/blob/master/sslhook.cpp). 31 | 32 | These files implement hooks for SSL_read and SSL_write functions, to be applied to all instrumented modules. These hooks inspects content sent/received during SSL_write/SSL_read and print it out to console (if it's printable). 33 | 34 | The main executable for the `SSLInst` client is implemented in [sslhook-main.cpp](https://github.com/googleprojectzero/TinyInst/blob/master/sslhook-main.cpp). 35 | 36 | ### SyscallHook API 37 | 38 | On Linux/Android, a special SyscallHook API is exposed that allows for easier syscall hooking. A syscall hook should inherit from the `SyscallHook` class, override `OnSyscall` and/or `OnSyscallEnd()` and be registered via `RegisterSyscallHook` function. SyscallHook API implements much of the same helper functions as the Hook API. 39 | -------------------------------------------------------------------------------- /instruction.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Google LLC 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 | https ://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 INSTRUCTION_H 18 | #define INSTRUCTION_H 19 | 20 | #ifdef ARM64 21 | #include "third_party/reil/reil/aarch64/decoder.h" 22 | #else 23 | extern "C" { 24 | #include "xed/xed-interface.h" 25 | } 26 | typedef struct xed_decoded_inst_s xed_decoded_inst_t; 27 | #endif 28 | 29 | enum InstructionClass { 30 | INVALID = 0, 31 | RET, 32 | IJUMP, 33 | ICALL, 34 | OTHER, 35 | }; 36 | 37 | struct Instruction { 38 | size_t address; 39 | size_t length; 40 | bool bbend; 41 | InstructionClass iclass; 42 | 43 | #ifdef ARM64 44 | reil::aarch64::decoder::Instruction instr; 45 | #else 46 | xed_decoded_inst_t xedd; 47 | #endif 48 | Instruction() 49 | : address(0), 50 | length(0), 51 | bbend(false), 52 | iclass(InstructionClass::INVALID), 53 | #ifdef ARM64 54 | instr({}) 55 | #else 56 | xedd({}) 57 | #endif 58 | {} 59 | }; 60 | 61 | #endif // INSTRUCTION_H 62 | -------------------------------------------------------------------------------- /litecov.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Google LLC 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 | https ://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 LITECOV_H 18 | #define LITECOV_H 19 | 20 | #include 21 | #include 22 | 23 | #include "coverage.h" 24 | #include "tinyinst.h" 25 | #include "instruction.h" 26 | 27 | #define COVERAGE_SIZE 0 28 | 29 | enum CovType { 30 | COVTYPE_BB, 31 | COVTYPE_EDGE 32 | }; 33 | 34 | struct CmpCoverageRecord { 35 | bool ignored; 36 | int width; 37 | int match_width; 38 | size_t bb_address; // for debugging 39 | size_t bb_offset; 40 | size_t cmp_offset; 41 | size_t instrumentation_offset; 42 | size_t instrumentation_size; 43 | size_t match_width_offset; 44 | }; 45 | 46 | class ModuleCovData { 47 | public: 48 | ModuleCovData(); 49 | void ClearInstrumentationData(); 50 | 51 | unsigned char *coverage_buffer_remote; 52 | 53 | size_t coverage_buffer_size; 54 | size_t coverage_buffer_next; 55 | 56 | std::set collected_coverage; 57 | std::set ignore_coverage; 58 | 59 | // maps offset in the coverage buffer to 60 | // offset of the basic block / edge code 61 | std::unordered_map buf_to_coverage; 62 | 63 | // maps coverage code (e.g. a bb offset) 64 | // to offset in the instrumented buffer 65 | // of the corresponding instrumentation 66 | std::unordered_map coverage_to_inst; 67 | 68 | bool has_remote_coverage; 69 | 70 | void ClearCmpCoverageData(); 71 | std::unordered_map buf_to_cmp; 72 | std::unordered_map coverage_to_cmp; 73 | }; 74 | 75 | class LiteCov : public TinyInst { 76 | public: 77 | virtual void Init(int argc, char **argv) override; 78 | 79 | void GetCoverage(Coverage &coverage, bool clear_coverage); 80 | void ClearCoverage(); 81 | 82 | // note: this does not affect already collected coverage 83 | void IgnoreCoverage(Coverage &coverage); 84 | 85 | bool HasNewCoverage(); 86 | 87 | protected: 88 | virtual void OnModuleInstrumented(ModuleInfo *module) override; 89 | virtual void OnModuleUninstrumented(ModuleInfo *module) override; 90 | 91 | virtual void OnProcessExit() override; 92 | 93 | virtual void OnModuleEntered(ModuleInfo *module, size_t entry_address) override; 94 | virtual bool OnException(Exception *exception_record) override; 95 | 96 | virtual void InstrumentBasicBlock(ModuleInfo *module, size_t bb_address) override; 97 | virtual void InstrumentEdge(ModuleInfo *previous_module, 98 | ModuleInfo *next_module, 99 | size_t previous_address, 100 | size_t next_address) override; 101 | virtual InstructionResult InstrumentInstruction(ModuleInfo *module, 102 | Instruction &inst, 103 | size_t bb_address, 104 | size_t instruction_address) override; 105 | 106 | void EmitCoverageInstrumentation(ModuleInfo *module, uint64_t coverage_code); 107 | void EmitCoverageInstrumentation(ModuleInfo *module, 108 | size_t bit_address, 109 | size_t mov_address); 110 | void ClearCoverageInstrumentation(ModuleInfo *module, uint64_t coverage_code); 111 | 112 | void NopCovInstructions(ModuleInfo *module, size_t code_offset); 113 | void NopCmpCovInstructions(ModuleInfo *module, 114 | CmpCoverageRecord &cmp_record, 115 | int matched_width); 116 | 117 | // compute a unique code for a basic block 118 | // this is just an offset into the module 119 | uint64_t GetBBCode(ModuleInfo *module, size_t bb_address); 120 | 121 | // compute a unique code for a basic block 122 | // this has address1 offset in lower 32 bits and 123 | // address2 offset in higher 32 bits 124 | uint64_t GetEdgeCode(ModuleInfo *module, size_t edge_address1, size_t edge_address2); 125 | 126 | ModuleCovData *GetDataByRemoteAddress(size_t address); 127 | void HandleBufferWriteException(ModuleCovData *data); 128 | 129 | void ClearCoverage(ModuleCovData *data); 130 | void ClearRemoteBuffer(ModuleCovData *data); 131 | 132 | void CollectCoverage(ModuleCovData *data); 133 | void CollectCoverage(); 134 | 135 | uint64_t GetCmpCode(size_t bb_offset, size_t cmp_offset, int bits_match); 136 | bool IsCmpCoverageCode(uint64_t code); 137 | void ClearCmpCoverageInstrumentation(ModuleInfo *module, uint64_t coverage_code); 138 | void CollectCmpCoverage(ModuleCovData *data, size_t buffer_offset, char buffer_value); 139 | bool ShouldInstrumentSub(ModuleInfo *module, 140 | Instruction& cmp_instr, 141 | size_t instruction_address); 142 | private: 143 | CovType coverage_type; 144 | bool compare_coverage; 145 | size_t skip_cov_instruction_br_off; 146 | }; 147 | 148 | #endif // LITECOV_H 149 | -------------------------------------------------------------------------------- /macOS/.gitignore: -------------------------------------------------------------------------------- 1 | mig_client.c 2 | mig_client.h 3 | mig_server.c 4 | mig_server.h 5 | -------------------------------------------------------------------------------- /macOS/README.md: -------------------------------------------------------------------------------- 1 | # TinyInst on macOS 2 | 3 | ``` 4 | Copyright 2020 Google LLC 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | https://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | ``` 18 | 19 | ## Limitations on macOS 20 | 21 | * TinyInst may not always detect the exact time of target process exit. As a consequence the `OnProcessExit()` callback might have a maximum delay of 100ms. In the future, additional APIs (e.g. kqueue) could be used to detect process exit accurately. 22 | * TinyInst on macOS is affected by the same custom exceptions-handling issues as the Windows version. For the description of the issue and workarounds, see [this section in the readme](https://github.com/googleprojectzero/TinyInst#return-address-patching) 23 | * TinyInst leverages read-only pages to redirect the instruction pointer to the 24 | instrumented code if the original, uninstrumented, code is invoked. On macOS running on ARM chips, the code 25 | section of the modules inside the Dyld cache is aligned to 4k pages, however, ARM uses 16k pages. This makes it more difficult to instrument 26 | individual modules inside the Dyld cache. TinyInst currently solves ths by instrumenting adjacent modules automatically until their code section 27 | aligns to 16k to ensure that not only parts of a module is instrumented. This 28 | behavior is controlled by the `-page_extend_modules` flag which is set to 29 | `true` by default on M1. In some cases it might be possible to turn off this flag resulting in better performance. 30 | 31 | ## TinyInst and Guard Malloc 32 | 33 | On macOS, [Guard Malloc](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/ManagingMemory/Articles/MallocDebug.html) is a special version of the malloc library that makes it easier to catch certain types of memory safety issues. To enable Guard Malloc for a target process running under TinyInst, use the following flag: 34 | 35 | ``` 36 | -target_env DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib 37 | ``` 38 | 39 | However, on some targets, additional workarounds might be needed. 40 | 41 | An issue was observed with some targets, where the combination of TinyInst and Guard Malloc put a process in a state where calls to (mach_)vm_allocate would fail, even though there was still sufficient free memory in the system and the target process address space (possibly due to an issue in macOS itself). This caused libgmalloc to be stuck in an infinite loop the first time it tried to allocate memory after a module was instrumented. The workaround for this is to have the modules loaded and instrumented before libgmalloc is loaded. This can be accomplished by the following flags: 42 | 43 | ``` 44 | -target_env DYLD_INSERT_LIBRARIES=/path/to/instrumented/module.dylib:/usr/lib/libgmalloc.dylib -instrument_modules_on_load 45 | ``` 46 | 47 | The first part ensures that the instrumented module will be loaded before libgmalloc (the order of libraries in `DYLD_INSERT_LIBRARIES` is important). The `-instrument_modules_on_load` flag ensures that modules will be instrumented as soon as they are loaded (and not when the process entypoint or the target method is reached, as is normally the case in TinyInst. 48 | 49 | Additionally, especially if you enconter errors related to stack unwinding, the `-patch_return_addresses` flag might be needed. 50 | -------------------------------------------------------------------------------- /macOS/dyld_cache_map_parser.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Google LLC 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 | https ://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 "macOS/dyld_cache_map_parser.h" 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "common.h" 24 | 25 | struct Module { 26 | Module(std::string n, uint64_t s, uint64_t e) 27 | : name(n), start(s), end(e) {} 28 | std::string name; 29 | uint64_t start; 30 | uint64_t end; 31 | }; 32 | 33 | static bool is_page_aligned(uint64_t addr, uint64_t page_size = 0x4000) { 34 | return !(addr & (page_size-1)); 35 | } 36 | 37 | std::string find_dyld_map() { 38 | std::string name = "/System/Library/dyld/dyld_shared_cache_arm64e.map"; 39 | 40 | // from dyld4::APIs::dyld_shared_cache_find_iterate_text 41 | std::string cryptexPrefixes[] = { 42 | "/System/Volumes/Preboot/Cryptexes/OS", 43 | "/private/preboot/Cryptexes/OS", 44 | "/System/Cryptexes/OS" 45 | }; 46 | 47 | auto check = [](const std::string &path) { 48 | std::ifstream stream(path); 49 | return !stream.fail(); 50 | }; 51 | 52 | if (check(name)) { 53 | return name; 54 | } 55 | 56 | for (auto i = 0; i < 3; i++) { 57 | std::string cryptex = cryptexPrefixes[i] + name; 58 | if (check(cryptex)) { 59 | return cryptex; 60 | } 61 | } 62 | 63 | FATAL("Unable to locate dyld_shared_cache"); 64 | } 65 | 66 | std::map> parse_dyld_map_file(const std::string &path) { 67 | std::ifstream cache_map(path); 68 | 69 | if(!cache_map.is_open()) { 70 | FATAL("Unable to open: %s", path.c_str()); 71 | } 72 | 73 | std::string line; 74 | 75 | std::map> result; 76 | std::map tmp_result; 77 | 78 | std::string regex = "__TEXT 0x([A-F0-9]+) -> 0x([A-F0-9]+)"; 79 | std::smatch m; 80 | std::regex r(regex); 81 | 82 | 83 | bool lib = false; 84 | int i = 0; 85 | std::string lib_name; 86 | while (std::getline(cache_map, line)) { 87 | if (line.length() > 0 && line[0] == '/') { 88 | lib = true; 89 | if (line.rfind("/") == std::string::npos) { 90 | std::cout << "error\n"; 91 | std::cout << line << "\n"; 92 | return {}; 93 | } 94 | lib_name = line.substr(line.rfind("/") + 1); 95 | } 96 | 97 | if (lib && line.length() == 0) { 98 | lib = false; 99 | } 100 | 101 | if (lib) { 102 | if(std::regex_search(line, m, r)) { 103 | if(m.size() != 3) continue; 104 | uint64_t start = std::stoul(m[1], nullptr, 16); 105 | uint64_t end = std::stoul(m[2], nullptr, 16); 106 | tmp_result.insert({start, Module(lib_name, start, end)}); 107 | } 108 | } 109 | } 110 | 111 | uint64_t prev_end_addr = tmp_result.begin()->second.start; 112 | std::vector mod_group; 113 | for(const auto &[start_address, mod]: tmp_result) { 114 | mod_group.push_back(mod.name); 115 | 116 | if(is_page_aligned(mod.end)) { 117 | for(const auto mod_name: mod_group) { 118 | result.insert({mod_name, mod_group}); 119 | } 120 | 121 | mod_group.clear(); 122 | prev_end_addr = mod.end; 123 | } 124 | else if(prev_end_addr != mod.start && is_page_aligned(mod.start)) { 125 | mod_group.pop_back(); 126 | 127 | for(const auto mod_name: mod_group) { 128 | result.insert({mod_name, mod_group}); 129 | } 130 | 131 | mod_group.clear(); 132 | mod_group.push_back(mod.name); 133 | } 134 | else { 135 | prev_end_addr = mod.end; 136 | } 137 | } 138 | 139 | cache_map.close(); 140 | return result; 141 | } 142 | -------------------------------------------------------------------------------- /macOS/dyld_cache_map_parser.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Google LLC 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 | https ://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 | 21 | std::string find_dyld_map(void); 22 | std::map> parse_dyld_map_file(const std::string &path); 23 | -------------------------------------------------------------------------------- /macOS/machtarget.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Google LLC 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 | https ://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 MACHTARGET_H 18 | #define MACHTARGET_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | class MachTarget { 25 | private: 26 | pid_t pid; 27 | task_t task; 28 | mach_port_t exception_port; 29 | vm_size_t m_page_size; 30 | 31 | exception_mask_t saved_masks[EXC_TYPES_COUNT]; 32 | mach_port_t saved_ports[EXC_TYPES_COUNT]; 33 | exception_behavior_t saved_behaviors[EXC_TYPES_COUNT]; 34 | thread_state_flavor_t saved_flavors[EXC_TYPES_COUNT]; 35 | mach_msg_type_number_t saved_exception_types_count; 36 | 37 | size_t MaxBytesLeftInPage(mach_vm_address_t address, mach_vm_size_t size); 38 | 39 | public: 40 | MachTarget(pid_t target_pid); 41 | 42 | pid_t Pid() { return pid; } 43 | task_t Task() { return task; } 44 | mach_port_t ExceptionPort() { return exception_port; } 45 | 46 | vm_size_t PageSize(); 47 | vm_size_t MemSize(); 48 | 49 | kern_return_t BasicInfo(mach_task_basic_info *info); 50 | void GetRegionSubmapInfo(mach_vm_address_t *region_address, 51 | mach_vm_size_t *region_size, 52 | vm_region_submap_info_data_64_t *info); 53 | 54 | bool IsExceptionPortValid(); 55 | bool IsTaskValid(); 56 | 57 | dyld_all_image_infos GetAllImageInfos(); 58 | void ReadCString(uint64_t address, size_t max_size, void *string); 59 | void ReadCString(uint64_t address, std::string &string); 60 | 61 | kern_return_t WaitForException(uint32_t timeout, mach_msg_header_t *req, uint32_t size); 62 | void ReplyToException(mach_msg_header_t *rpl); 63 | 64 | void FreeMemory(uint64_t address, size_t size); 65 | void ReadMemory(uint64_t address, size_t size, void *buf); 66 | void WriteMemory(uint64_t address, const void *buf, size_t size); 67 | void ProtectMemory(uint64_t address, uint64_t size, vm_prot_t protection); 68 | 69 | void CleanUp(); 70 | }; 71 | 72 | #endif /* MACHTARGET_H */ 73 | -------------------------------------------------------------------------------- /macOS/mig.defs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Google LLC 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 | https ://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 | //Resolve MIG generated files 18 | #import 19 | -------------------------------------------------------------------------------- /macOS/unwindmacos.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Google LLC 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 | https ://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 unwindmacos_h 18 | #define unwindmacos_h 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include "unwind.h" 26 | #include "tinyinst.h" 27 | #include 28 | 29 | #include 30 | 31 | class UnwindDataMacOS: public UnwindData { 32 | public: 33 | UnwindDataMacOS(); 34 | ~UnwindDataMacOS(); 35 | 36 | void *unwind_section_address; 37 | uint64_t unwind_section_size; 38 | void *unwind_section_buffer; 39 | unwind_info_section_header *unwind_section_header; 40 | 41 | std::vector personality_vector; 42 | 43 | bool registered_fde; 44 | 45 | bool LookupPersonality(size_t ip, size_t *personality); 46 | 47 | struct PersonalityLookup { 48 | PersonalityLookup() { 49 | found = false; 50 | personality = (size_t)(-1); 51 | min_address = 0; 52 | max_address = 0; 53 | } 54 | 55 | void Init(bool found, 56 | size_t personality, 57 | size_t min_address, 58 | size_t max_address) 59 | { 60 | this->found = found; 61 | this->personality = personality; 62 | this->min_address = min_address; 63 | this->max_address = max_address; 64 | } 65 | 66 | bool found; 67 | size_t personality; 68 | size_t min_address; 69 | size_t max_address; 70 | }; 71 | 72 | PersonalityLookup last_personality_lookup; 73 | 74 | std::map encoding_map; 75 | 76 | struct BreakpointData { 77 | SavedRegisters saved_registers; 78 | size_t continue_ip; 79 | }; 80 | 81 | size_t register_breakpoint; 82 | BreakpointData register_breakpoint_data; 83 | 84 | size_t personality_breakpoint; 85 | 86 | struct ReturnAddressInfo { 87 | size_t original_return_address; 88 | size_t personality; 89 | }; 90 | 91 | // Maps the return addresses in the instrumented code (the keys) 92 | // to the return addresses in the original code (the values). 93 | std::unordered_map return_addresses; 94 | 95 | class LookupTable { 96 | public: 97 | LookupTable() { 98 | header_local = NULL; 99 | header_remote = 0; 100 | buffer_cur = 0; 101 | buffer_end = 0; 102 | } 103 | 104 | ~LookupTable(); 105 | 106 | size_t *header_local; 107 | size_t header_remote; 108 | size_t buffer_cur; 109 | size_t buffer_end; 110 | }; 111 | 112 | LookupTable lookup_table; 113 | }; 114 | 115 | class ByteStream { 116 | private: 117 | std::vector byte_stream; 118 | 119 | public: 120 | size_t size() { 121 | return byte_stream.size(); 122 | } 123 | 124 | uint8_t *data() { 125 | return byte_stream.data(); 126 | } 127 | 128 | template 129 | void PutValue(T value) { 130 | for (int i = 0; i < sizeof(T); ++i) { 131 | byte_stream.push_back((value >> (i * 8)) & 0xff); 132 | } 133 | } 134 | 135 | template 136 | void PutValueFront(T value) { 137 | PutValue(value); 138 | std::rotate(byte_stream.begin(), byte_stream.end() - sizeof(T), byte_stream.end()); 139 | } 140 | 141 | void PutString(const char *s) { 142 | for (char *p = (char *)s; *p; p++) { 143 | byte_stream.push_back(*p); 144 | } 145 | byte_stream.push_back(0); 146 | } 147 | 148 | void PutULEB128Value(uint64_t value) { 149 | encodeULEB128(value, byte_stream); 150 | } 151 | 152 | void PutSLEB128Value(int64_t value) { 153 | encodeSLEB128(value, byte_stream); 154 | } 155 | }; 156 | 157 | class UnwindGeneratorMacOS : public UnwindGenerator { 158 | public: 159 | UnwindGeneratorMacOS(TinyInst& tinyinst) : UnwindGenerator(tinyinst), 160 | register_frame_addr(0), 161 | unwind_getip(0), 162 | #ifdef ARM64 163 | unwind_cursor_setReg(0), 164 | unwind_cursor_setInfoBasedOnIPRegister(0) {} 165 | #else 166 | unwind_setip(0) {} 167 | #endif 168 | ~UnwindGeneratorMacOS() = default; 169 | 170 | void Init(int argc, char **argv) override; 171 | 172 | void OnModuleInstrumented(ModuleInfo* module) override; 173 | void OnModuleUninstrumented(ModuleInfo* module) override; 174 | 175 | size_t MaybeRedirectExecution(ModuleInfo* module, size_t IP) override; 176 | 177 | void OnModuleLoaded(void *module, char *module_name) override; 178 | 179 | void OnReturnAddress(ModuleInfo *module, 180 | size_t original_address, 181 | size_t translated_address) override; 182 | 183 | bool HandleBreakpoint(ModuleInfo* module, void *address) override; 184 | 185 | private: 186 | void SanityCheckUnwindHeader(ModuleInfo *module); 187 | void CheckUnwindBufferBounds(ModuleInfo *module, const char *array_description, 188 | size_t start_address, size_t end_address); 189 | 190 | void ExtractFirstLevel(ModuleInfo *module); 191 | void ExtractEncodingsSecondLevel(ModuleInfo *module, 192 | unwind_info_section_header_index_entry *first_level_entry); 193 | void ExtractEncodingsCompressed(ModuleInfo *module, 194 | unwind_info_section_header_index_entry *first_level_entry, 195 | size_t second_level_page_addr); 196 | void ExtractEncodingsRegular(ModuleInfo *module, 197 | unwind_info_section_header_index_entry *first_level_entry, 198 | size_t second_level_page_addr); 199 | 200 | compact_unwind_encoding_t GetCompactEncoding(ModuleInfo *module, 201 | size_t second_level_page_addr, 202 | uint32_t curr_entry_encoding_index); 203 | 204 | void ExtractPersonalityArray(ModuleInfo *module); 205 | 206 | size_t WriteCIE(ModuleInfo *module, 207 | const char *augmentation, 208 | size_t personality_addr); 209 | size_t WriteFDE(ModuleInfo *module, 210 | size_t cie_address, 211 | size_t min_address, 212 | size_t max_address); 213 | 214 | size_t WriteCustomPersonality(ModuleInfo* module); 215 | void WritePersonalityLookup(ModuleInfo* module); 216 | 217 | size_t AllocateLookupTableChunk(); 218 | void WriteLookupTable(ModuleInfo* module); 219 | 220 | #ifdef ARM64 221 | void AlignCodeMemory(ModuleInfo *module); 222 | #endif 223 | 224 | size_t register_frame_addr; 225 | size_t unwind_getip; 226 | #ifdef ARM64 227 | size_t unwind_cursor_getReg; 228 | size_t unwind_cursor_setReg; 229 | size_t unwind_cursor_setInfoBasedOnIPRegister; 230 | #else 231 | size_t unwind_setip; 232 | #endif 233 | 234 | bool in_process_lookup; 235 | 236 | #ifdef ARM64 237 | static constexpr unsigned char register_assembly_arm64[] = { 238 | // save registers 239 | 0xff, 0xc3, 0x00, 0xd1, // sub sp, sp, #48 240 | 0xe0, 0x4f, 0x02, 0xa9, // stp x0, x19, [sp, #32] 241 | 0xf4, 0x57, 0x01, 0xa9, // stp x20, x21, [sp, #16] 242 | 0xfd, 0x7b, 0x00, 0xa9, // stp fp, lr, [sp, #0] 243 | 0xfd, 0x03, 0x00, 0x91, // mov fp, sp 244 | 0xa0, 0x0f, 0x40, 0x92, // and x0, fp, #0xf 245 | 0x40, 0x00, 0x00, 0xb4, // cbz x0, skip_alignment 246 | 0xff, 0x23, 0x00, 0xd1, // sub sp, sp, #8 247 | // skip_alignment: 248 | 0x93, 0x00, 0x00, 0x58, // ldr x19, #16; x19 becomes the current array pointer 249 | 0xb4, 0x00, 0x00, 0x58, // ldr x20, #20; x20 becomes the end array pointer 250 | 0xd5, 0x00, 0x00, 0x58, // ldr x21, #24; x21 becomes __register_frame address 251 | 0x09, 0x00, 0x00, 0x14, // b #36; loop 252 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // array start addr goes here 253 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // array end addr goes here 254 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // __register_frame addr goes here 255 | // loop: 256 | 0x7f, 0x02, 0x14, 0xeb, // cmp x19, x20 257 | 0xaa, 0x00, 0x00, 0x54, // b.ge loop_end 258 | 0x60, 0x02, 0x40, 0xf9, // ldr x0, [x19] 259 | 0xa0, 0x02, 0x3f, 0xd6, // blr x21 260 | 0x73, 0x22, 0x00, 0x91, // add x19, x19, #8 261 | 0xfb, 0xff, 0xff, 0x17, // b loop 262 | // loop_end: 263 | 0xbf, 0x03, 0x00, 0x91, // mov sp, fp 264 | 0xfd, 0x7b, 0x40, 0xa9, // ldp fp, lr, [sp, #0] 265 | 0xf4, 0x57, 0x41, 0xa9, // ldp x20, x21, [sp, #16] 266 | 0xe0, 0x4f, 0x42, 0xa9, // ldp x0, x19, [sp, #32] 267 | 0xff, 0xc3, 0x00, 0x91, // add sp, sp, #48 268 | }; 269 | static const size_t register_assembly_arm64_data_offset = 48; 270 | #else 271 | static constexpr unsigned char register_assembly_x86[] = { 272 | // save registers 273 | 0x53, // push rbx 274 | 0x41, 0x54, // push r12 275 | 0x41, 0x55, // push r13 276 | // save rbp, rsp 277 | 0x55, // push rbp 278 | 0x48, 0x89, 0xE5, // mov rbp, rsp 279 | // fix stack alignment 280 | 0x48, 0xF7, 0xC4, 0x0F, 0x00, 0x00, 0x00, // test rsp,0xf 281 | 0x0F, 0x84, 0x02, 0x00, 0x00, 0x00, // je skip_alignment 282 | //0x0F, 0x85, 0x02, 0x00, 0x00, 0x00, // jne skip_alignment 283 | 0x6A, 0x00, // push 0 284 | // load parameters 285 | 0x48, 0x8B, 0x1D, 0x13, 0x00, 0x00, 0x00, // mov rbx, [rip + offset]; rbx becomes the current array pointer 286 | 0x4C, 0x8B, 0x25, 0x14, 0x00, 0x00, 0x00, // mov r12, [rip + offset]; r12 becomes the end array pointer 287 | 0x4C, 0x8B, 0x2D, 0x15, 0x00, 0x00, 0x00, // mov r13, [rip + offset]; r13 becomes __register_frame address 288 | 0xE9, 0x18, 0x00, 0x00, 0x00, // jmp 0x18 289 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // array start addr goes here 290 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // array end addr goes here 291 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // __register_frame addr goes here 292 | // loop start 293 | 0x4C, 0x39, 0xE3, // cmp rbx, r12 294 | 0x0F, 0x83, 0x0F, 0x00, 0x00, 0x00, // JAE 0x0F (loop end) 295 | 0x48, 0x8B, 0x3B, // mov rdi, [rbx] 296 | 0x41, 0xFF, 0xD5, // call r13 297 | 0x48, 0x83, 0xC3, 0x08, // add rbx, 8 298 | 0xE9, 0xe8, 0xff, 0xff, 0xff, // jmp to loop start 299 | //restore rsp, rbp 300 | 0x48, 0x89, 0xEC, // mov rsp,rbp 301 | 0x5D, // pop rbp 302 | // restore registers 303 | 0x41, 0x5D, // pop r13 304 | 0x41, 0x5C, // pop r12 305 | 0x5B, // pop rbx 306 | }; 307 | 308 | static const size_t register_assembly_x86_data_offset = 50; 309 | #endif 310 | }; 311 | 312 | #endif /* unwindmacos_h */ 313 | -------------------------------------------------------------------------------- /sslhook-main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Google LLC 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 | https ://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 _CRT_SECURE_NO_WARNINGS 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "sslhook.h" 24 | 25 | 26 | int main(int argc, char **argv) 27 | { 28 | SSLInst *instrumentation = new SSLInst(); 29 | instrumentation->Init(argc, argv); 30 | 31 | int target_opt_ind = 0; 32 | for (int i = 1; i < argc; i++) { 33 | if (strcmp(argv[i], "--") == 0) { 34 | target_opt_ind = i + 1; 35 | break; 36 | } 37 | } 38 | 39 | int target_argc = (target_opt_ind) ? argc - target_opt_ind : 0; 40 | char **target_argv = (target_opt_ind) ? argv + target_opt_ind : NULL; 41 | 42 | unsigned int pid = GetIntOption("-pid", argc, argv, 0); 43 | 44 | if (!target_argc && !pid) { 45 | printf("Usage:\n"); 46 | printf("%s -- \n", argv[0]); 47 | printf("Or:\n"); 48 | printf("%s -pid \n", argv[0]); 49 | return 0; 50 | } 51 | 52 | DebuggerStatus status; 53 | if (target_argc) { 54 | status = instrumentation->Run(target_argc, target_argv, 0xFFFFFFFF); 55 | } else { 56 | status = instrumentation->Attach(pid, 0xFFFFFFFF); 57 | } 58 | 59 | switch (status) { 60 | case DEBUGGER_CRASHED: 61 | printf("Process crashed\n"); 62 | instrumentation->Kill(); 63 | break; 64 | case DEBUGGER_HANGED: 65 | printf("Process hanged\n"); 66 | instrumentation->Kill(); 67 | break; 68 | case DEBUGGER_PROCESS_EXIT: 69 | printf("Process exited normally\n"); 70 | break; 71 | default: 72 | FATAL("Unexpected status received from the debugger\n"); 73 | break; 74 | } 75 | 76 | return 0; 77 | } 78 | -------------------------------------------------------------------------------- /sslhook.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Google LLC 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 | https ://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 "sslhook.h" 18 | 19 | bool IsPrintable(char *buf, size_t size) { 20 | for(size_t i=0; i= 0x20) && (c <= 0x7E))) 24 | { 25 | //pass 26 | } else { 27 | return false; 28 | } 29 | } 30 | return true; 31 | } 32 | 33 | void SSLWriteHook::OnFunctionEntered() { 34 | char *buf_addr = (char *)GetArg(1); 35 | size_t size = GetArg(2); 36 | 37 | if(size == 0) { 38 | printf("SSL_write: \n"); 39 | return; 40 | } 41 | 42 | char *buf = (char *)malloc(size + 1); 43 | RemoteRead(buf_addr, buf, size); 44 | buf[size] = 0; 45 | 46 | if(IsPrintable(buf,size)) { 47 | printf("SSL_write: %s\n", buf); 48 | } else { 49 | printf("SSL_write: \n", size); 50 | } 51 | 52 | free(buf); 53 | } 54 | 55 | void SSLReadHook::OnFunctionReturned() { 56 | int retval = (int)GetReturnValue(); 57 | 58 | if(retval <= 0) { 59 | printf("SSL_read: \n"); 60 | return; 61 | } 62 | 63 | size_t size = retval; 64 | char *buf_addr = (char *)GetArg(1); 65 | 66 | char *buf = (char *)malloc(size + 1); 67 | RemoteRead(buf_addr, buf, size); 68 | buf[size] = 0; 69 | 70 | if(IsPrintable(buf,size)) { 71 | printf("SSL_read: %s\n", buf); 72 | } else { 73 | printf("SSL_read: \n", size); 74 | } 75 | 76 | free(buf); 77 | } 78 | 79 | SSLInst::SSLInst() { 80 | RegisterHook(new SSLReadHook()); 81 | RegisterHook(new SSLWriteHook()); 82 | } 83 | 84 | 85 | -------------------------------------------------------------------------------- /sslhook.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Google LLC 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 | https ://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 SSLINST_H 18 | #define SSLINST_H 19 | 20 | #include "hook.h" 21 | 22 | class SSLWriteHook : public HookBegin { 23 | public: 24 | SSLWriteHook() : HookBegin("*", "SSL_write", 3, CALLCONV_DEFAULT) {} 25 | protected: 26 | void OnFunctionEntered() override; 27 | }; 28 | 29 | class SSLReadHook : public HookBeginEnd { 30 | public: 31 | SSLReadHook() : HookBeginEnd("*", "SSL_read", 3, CALLCONV_DEFAULT) {} 32 | protected: 33 | void OnFunctionReturned() override; 34 | }; 35 | 36 | class SSLInst : public TinyInst { 37 | public: 38 | SSLInst(); 39 | }; 40 | 41 | #endif /* SSLINST_H */ 42 | -------------------------------------------------------------------------------- /third_party/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | cmake_minimum_required(VERSION "3.1") 16 | set (CMAKE_CXX_STANDARD 17) 17 | 18 | if (${ARCHITECTURE} MATCHES arm64) 19 | project("reil") 20 | add_library(reil STATIC 21 | reil/reil/aarch64/decoder.cpp 22 | reil/reil/aarch64/printer.cpp 23 | reil/reil/aarch64/decoder.h 24 | ) 25 | target_include_directories(reil PUBLIC 26 | reil/ 27 | ) 28 | 29 | else() 30 | project("xed") 31 | 32 | find_package (Python3 REQUIRED) 33 | 34 | set(XED_INTERFACE_H ${CMAKE_CURRENT_BINARY_DIR}/obj/wkit/include/xed/xed-interface.h) 35 | 36 | if (WIN32) 37 | set(XED_LIB ${CMAKE_CURRENT_BINARY_DIR}/obj/wkit/lib/xed.lib) 38 | else () 39 | set(XED_LIB ${CMAKE_CURRENT_BINARY_DIR}/obj/wkit/lib/libxed.a) 40 | endif() 41 | 42 | if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") 43 | set(XED_HOST_CPU "--host-cpu=ia32") 44 | else() 45 | set(XED_HOST_CPU "") 46 | endif() 47 | 48 | # Attempt to fix building XED on VS2022 and later 49 | # Because having a custom build system is just awesome :-/ 50 | if(MSVC_VERSION AND (MSVC_VERSION GREATER 1929)) 51 | get_filename_component(VS_BIN_PATH "${CMAKE_C_COMPILER}" DIRECTORY) 52 | set(VS_LIB_EXE "${VS_BIN_PATH}/lib.exe") 53 | set(XED_ADDITONAL_FLAGS --cc ${CMAKE_C_COMPILER} --cxx ${CMAKE_CXX_COMPILER} --linker ${CMAKE_LINKER} --ar ${VS_LIB_EXE}) 54 | else() 55 | set(XED_ADDITONAL_FLAGS "") 56 | endif() 57 | 58 | set (BUILD_XED_COMMAND 59 | ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/xed/mfile.py ${XED_HOST_CPU} ${XED_ADDITONAL_FLAGS} 60 | ) 61 | 62 | if (WIN32) 63 | add_custom_command( 64 | OUTPUT ${XED_LIB} ${XED_INTERFACE_H} 65 | COMMAND ${BUILD_XED_COMMAND} 66 | COMMENT "Building Xed" 67 | ) 68 | else() 69 | add_custom_command( 70 | OUTPUT ${XED_LIB} ${XED_INTERFACE_H} 71 | COMMAND ${BUILD_XED_COMMAND} 72 | COMMAND ranlib ${XED_LIB} 73 | COMMENT "Building Xed" 74 | ) 75 | endif() 76 | 77 | add_custom_target( 78 | xed 79 | DEPENDS ${XED_LIB} ${XED_INTERFACE_H} 80 | ) 81 | endif() 82 | -------------------------------------------------------------------------------- /third_party/llvm/LEB128.h: -------------------------------------------------------------------------------- 1 | //===- llvm/Support/LEB128.h - [SU]LEB128 utility functions -----*- C++ -*-===// 2 | // 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | // 9 | // This file declares some utility functions for encoding SLEB128 and 10 | // ULEB128 values. 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | #ifndef LLVM_SUPPORT_LEB128_H 15 | #define LLVM_SUPPORT_LEB128_H 16 | 17 | #include 18 | 19 | /// Utility function to encode a value into SLEB128 and store it in a byte stream. 20 | inline void encodeSLEB128(int64_t Value, std::vector &byte_stream, 21 | unsigned PadTo = 0) { 22 | unsigned Count = 0; 23 | bool More; 24 | do { 25 | uint8_t Byte = Value & 0x7f; 26 | // NOTE: this assumes that this signed shift is an arithmetic right shift. 27 | Value >>= 7; 28 | More = !((((Value == 0 ) && ((Byte & 0x40) == 0)) || 29 | ((Value == -1) && ((Byte & 0x40) != 0)))); 30 | Count++; 31 | if (More || Count < PadTo) 32 | Byte |= 0x80; // Mark this byte to show that more bytes will follow. 33 | byte_stream.push_back(Byte); 34 | } while (More); 35 | 36 | // Pad with 0x80 and emit a terminating byte at the end. 37 | if (Count < PadTo) { 38 | uint8_t PadValue = Value < 0 ? 0x7f : 0x00; 39 | for (; Count < PadTo - 1; ++Count) 40 | byte_stream.push_back(PadValue | 0x80); 41 | byte_stream.push_back(PadValue); 42 | } 43 | } 44 | 45 | /// Utility function to encode a value into ULEB128 and store it in a byte stream. 46 | inline void encodeULEB128(uint64_t Value, std::vector &byte_stream, 47 | unsigned PadTo = 0) { 48 | unsigned Count = 0; 49 | do { 50 | uint8_t Byte = Value & 0x7f; 51 | Value >>= 7; 52 | Count++; 53 | if (Value != 0 || Count < PadTo) 54 | Byte |= 0x80; // Mark this byte to show that more bytes will follow. 55 | byte_stream.push_back(Byte); 56 | } while (Value != 0); 57 | 58 | // Pad with 0x80 and emit a null byte at the end. 59 | if (Count < PadTo) { 60 | for (; Count < PadTo - 1; ++Count) 61 | byte_stream.push_back('\x80'); 62 | byte_stream.push_back('\x00'); 63 | } 64 | } 65 | 66 | #endif // LLVM_SUPPORT_LEB128_H 67 | -------------------------------------------------------------------------------- /third_party/llvm/libunwind/CompactUnwinder.hpp: -------------------------------------------------------------------------------- 1 | //===-------------------------- CompactUnwinder.hpp -----------------------===// 2 | // 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | // 8 | // Does runtime stack unwinding using compact unwind encodings. 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | #ifndef __COMPACT_UNWINDER_HPP__ 13 | #define __COMPACT_UNWINDER_HPP__ 14 | 15 | #define EXTRACT_BITS(value, mask) \ 16 | ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1)) 17 | 18 | #endif // __COMPACT_UNWINDER_HPP__ 19 | -------------------------------------------------------------------------------- /third_party/llvm/libunwind/dwarf2.h: -------------------------------------------------------------------------------- 1 | //===------------------------------- dwarf2.h -----------------------------===// 2 | // 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | 9 | 10 | /* 11 | These constants were taken from version 3 of the DWARF standard, 12 | which is Copyright (c) 2005 Free Standards Group, and 13 | Copyright (c) 1992, 1993 UNIX International, Inc. 14 | */ 15 | 16 | #ifndef __DWARF2__ 17 | #define __DWARF2__ 18 | 19 | // DWARF unwind instructions 20 | enum { 21 | DW_CFA_nop = 0x0, 22 | DW_CFA_set_loc = 0x1, 23 | DW_CFA_advance_loc1 = 0x2, 24 | DW_CFA_advance_loc2 = 0x3, 25 | DW_CFA_advance_loc4 = 0x4, 26 | DW_CFA_offset_extended = 0x5, 27 | DW_CFA_restore_extended = 0x6, 28 | DW_CFA_undefined = 0x7, 29 | DW_CFA_same_value = 0x8, 30 | DW_CFA_register = 0x9, 31 | DW_CFA_remember_state = 0xA, 32 | DW_CFA_restore_state = 0xB, 33 | DW_CFA_def_cfa = 0xC, 34 | DW_CFA_def_cfa_register = 0xD, 35 | DW_CFA_def_cfa_offset = 0xE, 36 | DW_CFA_def_cfa_expression = 0xF, 37 | DW_CFA_expression = 0x10, 38 | DW_CFA_offset_extended_sf = 0x11, 39 | DW_CFA_def_cfa_sf = 0x12, 40 | DW_CFA_def_cfa_offset_sf = 0x13, 41 | DW_CFA_val_offset = 0x14, 42 | DW_CFA_val_offset_sf = 0x15, 43 | DW_CFA_val_expression = 0x16, 44 | DW_CFA_advance_loc = 0x40, // high 2 bits are 0x1, lower 6 bits are delta 45 | DW_CFA_offset = 0x80, // high 2 bits are 0x2, lower 6 bits are register 46 | DW_CFA_restore = 0xC0, // high 2 bits are 0x3, lower 6 bits are register 47 | 48 | // GNU extensions 49 | DW_CFA_GNU_window_save = 0x2D, 50 | DW_CFA_GNU_args_size = 0x2E, 51 | DW_CFA_GNU_negative_offset_extended = 0x2F, 52 | 53 | // AARCH64 extensions 54 | DW_CFA_AARCH64_negate_ra_state = 0x2D 55 | }; 56 | 57 | 58 | // FSF exception handling Pointer-Encoding constants 59 | // Used in CFI augmentation by GCC 60 | enum { 61 | DW_EH_PE_ptr = 0x00, 62 | DW_EH_PE_uleb128 = 0x01, 63 | DW_EH_PE_udata2 = 0x02, 64 | DW_EH_PE_udata4 = 0x03, 65 | DW_EH_PE_udata8 = 0x04, 66 | DW_EH_PE_signed = 0x08, 67 | DW_EH_PE_sleb128 = 0x09, 68 | DW_EH_PE_sdata2 = 0x0A, 69 | DW_EH_PE_sdata4 = 0x0B, 70 | DW_EH_PE_sdata8 = 0x0C, 71 | DW_EH_PE_absptr = 0x00, 72 | DW_EH_PE_pcrel = 0x10, 73 | DW_EH_PE_textrel = 0x20, 74 | DW_EH_PE_datarel = 0x30, 75 | DW_EH_PE_funcrel = 0x40, 76 | DW_EH_PE_aligned = 0x50, 77 | DW_EH_PE_indirect = 0x80, 78 | DW_EH_PE_omit = 0xFF 79 | }; 80 | 81 | 82 | // DWARF expressions 83 | enum { 84 | DW_OP_addr = 0x03, // constant address (size target specific) 85 | DW_OP_deref = 0x06, 86 | DW_OP_const1u = 0x08, // 1-byte constant 87 | DW_OP_const1s = 0x09, // 1-byte constant 88 | DW_OP_const2u = 0x0A, // 2-byte constant 89 | DW_OP_const2s = 0x0B, // 2-byte constant 90 | DW_OP_const4u = 0x0C, // 4-byte constant 91 | DW_OP_const4s = 0x0D, // 4-byte constant 92 | DW_OP_const8u = 0x0E, // 8-byte constant 93 | DW_OP_const8s = 0x0F, // 8-byte constant 94 | DW_OP_constu = 0x10, // ULEB128 constant 95 | DW_OP_consts = 0x11, // SLEB128 constant 96 | DW_OP_dup = 0x12, 97 | DW_OP_drop = 0x13, 98 | DW_OP_over = 0x14, 99 | DW_OP_pick = 0x15, // 1-byte stack index 100 | DW_OP_swap = 0x16, 101 | DW_OP_rot = 0x17, 102 | DW_OP_xderef = 0x18, 103 | DW_OP_abs = 0x19, 104 | DW_OP_and = 0x1A, 105 | DW_OP_div = 0x1B, 106 | DW_OP_minus = 0x1C, 107 | DW_OP_mod = 0x1D, 108 | DW_OP_mul = 0x1E, 109 | DW_OP_neg = 0x1F, 110 | DW_OP_not = 0x20, 111 | DW_OP_or = 0x21, 112 | DW_OP_plus = 0x22, 113 | DW_OP_plus_uconst = 0x23, // ULEB128 addend 114 | DW_OP_shl = 0x24, 115 | DW_OP_shr = 0x25, 116 | DW_OP_shra = 0x26, 117 | DW_OP_xor = 0x27, 118 | DW_OP_skip = 0x2F, // signed 2-byte constant 119 | DW_OP_bra = 0x28, // signed 2-byte constant 120 | DW_OP_eq = 0x29, 121 | DW_OP_ge = 0x2A, 122 | DW_OP_gt = 0x2B, 123 | DW_OP_le = 0x2C, 124 | DW_OP_lt = 0x2D, 125 | DW_OP_ne = 0x2E, 126 | DW_OP_lit0 = 0x30, // Literal 0 127 | DW_OP_lit1 = 0x31, // Literal 1 128 | DW_OP_lit2 = 0x32, // Literal 2 129 | DW_OP_lit3 = 0x33, // Literal 3 130 | DW_OP_lit4 = 0x34, // Literal 4 131 | DW_OP_lit5 = 0x35, // Literal 5 132 | DW_OP_lit6 = 0x36, // Literal 6 133 | DW_OP_lit7 = 0x37, // Literal 7 134 | DW_OP_lit8 = 0x38, // Literal 8 135 | DW_OP_lit9 = 0x39, // Literal 9 136 | DW_OP_lit10 = 0x3A, // Literal 10 137 | DW_OP_lit11 = 0x3B, // Literal 11 138 | DW_OP_lit12 = 0x3C, // Literal 12 139 | DW_OP_lit13 = 0x3D, // Literal 13 140 | DW_OP_lit14 = 0x3E, // Literal 14 141 | DW_OP_lit15 = 0x3F, // Literal 15 142 | DW_OP_lit16 = 0x40, // Literal 16 143 | DW_OP_lit17 = 0x41, // Literal 17 144 | DW_OP_lit18 = 0x42, // Literal 18 145 | DW_OP_lit19 = 0x43, // Literal 19 146 | DW_OP_lit20 = 0x44, // Literal 20 147 | DW_OP_lit21 = 0x45, // Literal 21 148 | DW_OP_lit22 = 0x46, // Literal 22 149 | DW_OP_lit23 = 0x47, // Literal 23 150 | DW_OP_lit24 = 0x48, // Literal 24 151 | DW_OP_lit25 = 0x49, // Literal 25 152 | DW_OP_lit26 = 0x4A, // Literal 26 153 | DW_OP_lit27 = 0x4B, // Literal 27 154 | DW_OP_lit28 = 0x4C, // Literal 28 155 | DW_OP_lit29 = 0x4D, // Literal 29 156 | DW_OP_lit30 = 0x4E, // Literal 30 157 | DW_OP_lit31 = 0x4F, // Literal 31 158 | DW_OP_reg0 = 0x50, // Contents of reg0 159 | DW_OP_reg1 = 0x51, // Contents of reg1 160 | DW_OP_reg2 = 0x52, // Contents of reg2 161 | DW_OP_reg3 = 0x53, // Contents of reg3 162 | DW_OP_reg4 = 0x54, // Contents of reg4 163 | DW_OP_reg5 = 0x55, // Contents of reg5 164 | DW_OP_reg6 = 0x56, // Contents of reg6 165 | DW_OP_reg7 = 0x57, // Contents of reg7 166 | DW_OP_reg8 = 0x58, // Contents of reg8 167 | DW_OP_reg9 = 0x59, // Contents of reg9 168 | DW_OP_reg10 = 0x5A, // Contents of reg10 169 | DW_OP_reg11 = 0x5B, // Contents of reg11 170 | DW_OP_reg12 = 0x5C, // Contents of reg12 171 | DW_OP_reg13 = 0x5D, // Contents of reg13 172 | DW_OP_reg14 = 0x5E, // Contents of reg14 173 | DW_OP_reg15 = 0x5F, // Contents of reg15 174 | DW_OP_reg16 = 0x60, // Contents of reg16 175 | DW_OP_reg17 = 0x61, // Contents of reg17 176 | DW_OP_reg18 = 0x62, // Contents of reg18 177 | DW_OP_reg19 = 0x63, // Contents of reg19 178 | DW_OP_reg20 = 0x64, // Contents of reg20 179 | DW_OP_reg21 = 0x65, // Contents of reg21 180 | DW_OP_reg22 = 0x66, // Contents of reg22 181 | DW_OP_reg23 = 0x67, // Contents of reg23 182 | DW_OP_reg24 = 0x68, // Contents of reg24 183 | DW_OP_reg25 = 0x69, // Contents of reg25 184 | DW_OP_reg26 = 0x6A, // Contents of reg26 185 | DW_OP_reg27 = 0x6B, // Contents of reg27 186 | DW_OP_reg28 = 0x6C, // Contents of reg28 187 | DW_OP_reg29 = 0x6D, // Contents of reg29 188 | DW_OP_reg30 = 0x6E, // Contents of reg30 189 | DW_OP_reg31 = 0x6F, // Contents of reg31 190 | DW_OP_breg0 = 0x70, // base register 0 + SLEB128 offset 191 | DW_OP_breg1 = 0x71, // base register 1 + SLEB128 offset 192 | DW_OP_breg2 = 0x72, // base register 2 + SLEB128 offset 193 | DW_OP_breg3 = 0x73, // base register 3 + SLEB128 offset 194 | DW_OP_breg4 = 0x74, // base register 4 + SLEB128 offset 195 | DW_OP_breg5 = 0x75, // base register 5 + SLEB128 offset 196 | DW_OP_breg6 = 0x76, // base register 6 + SLEB128 offset 197 | DW_OP_breg7 = 0x77, // base register 7 + SLEB128 offset 198 | DW_OP_breg8 = 0x78, // base register 8 + SLEB128 offset 199 | DW_OP_breg9 = 0x79, // base register 9 + SLEB128 offset 200 | DW_OP_breg10 = 0x7A, // base register 10 + SLEB128 offset 201 | DW_OP_breg11 = 0x7B, // base register 11 + SLEB128 offset 202 | DW_OP_breg12 = 0x7C, // base register 12 + SLEB128 offset 203 | DW_OP_breg13 = 0x7D, // base register 13 + SLEB128 offset 204 | DW_OP_breg14 = 0x7E, // base register 14 + SLEB128 offset 205 | DW_OP_breg15 = 0x7F, // base register 15 + SLEB128 offset 206 | DW_OP_breg16 = 0x80, // base register 16 + SLEB128 offset 207 | DW_OP_breg17 = 0x81, // base register 17 + SLEB128 offset 208 | DW_OP_breg18 = 0x82, // base register 18 + SLEB128 offset 209 | DW_OP_breg19 = 0x83, // base register 19 + SLEB128 offset 210 | DW_OP_breg20 = 0x84, // base register 20 + SLEB128 offset 211 | DW_OP_breg21 = 0x85, // base register 21 + SLEB128 offset 212 | DW_OP_breg22 = 0x86, // base register 22 + SLEB128 offset 213 | DW_OP_breg23 = 0x87, // base register 23 + SLEB128 offset 214 | DW_OP_breg24 = 0x88, // base register 24 + SLEB128 offset 215 | DW_OP_breg25 = 0x89, // base register 25 + SLEB128 offset 216 | DW_OP_breg26 = 0x8A, // base register 26 + SLEB128 offset 217 | DW_OP_breg27 = 0x8B, // base register 27 + SLEB128 offset 218 | DW_OP_breg28 = 0x8C, // base register 28 + SLEB128 offset 219 | DW_OP_breg29 = 0x8D, // base register 29 + SLEB128 offset 220 | DW_OP_breg30 = 0x8E, // base register 30 + SLEB128 offset 221 | DW_OP_breg31 = 0x8F, // base register 31 + SLEB128 offset 222 | DW_OP_regx = 0x90, // ULEB128 register 223 | DW_OP_fbreg = 0x91, // SLEB128 offset 224 | DW_OP_bregx = 0x92, // ULEB128 register followed by SLEB128 offset 225 | DW_OP_piece = 0x93, // ULEB128 size of piece addressed 226 | DW_OP_deref_size = 0x94, // 1-byte size of data retrieved 227 | DW_OP_xderef_size = 0x95, // 1-byte size of data retrieved 228 | DW_OP_nop = 0x96, 229 | DW_OP_push_object_addres = 0x97, 230 | DW_OP_call2 = 0x98, // 2-byte offset of DIE 231 | DW_OP_call4 = 0x99, // 4-byte offset of DIE 232 | DW_OP_call_ref = 0x9A, // 4- or 8-byte offset of DIE 233 | DW_OP_lo_user = 0xE0, 234 | DW_OP_APPLE_uninit = 0xF0, 235 | DW_OP_hi_user = 0xFF 236 | }; 237 | 238 | 239 | #endif 240 | -------------------------------------------------------------------------------- /third_party/reil/reil/aarch64/decoder.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef REIL_AARCH64_DECODER_H_ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #if 0 24 | #include "absl/types/variant.h" 25 | #endif 26 | 27 | namespace reil { 28 | namespace aarch64 { 29 | namespace decoder { 30 | enum Opcode { 31 | // PC-relative addressing 32 | kAdr, 33 | kAdrp, 34 | 35 | // Add/subtract immediate 36 | kAddImmediate, 37 | kSubImmediate, 38 | 39 | // Logical immediate 40 | kAndImmediate, 41 | kOrrImmediate, 42 | kEorImmediate, 43 | 44 | // Move wide immediate 45 | kMovk, 46 | kMovn, 47 | kMovz, 48 | 49 | // Bitfield 50 | kBfm, 51 | kSbfm, 52 | kUbfm, 53 | 54 | // Extract 55 | kExtr, 56 | 57 | // Conditional branch 58 | kBCond, 59 | 60 | // Exception generation 61 | kBrk, 62 | kDcps1, 63 | kDcps2, 64 | kDcps3, 65 | kHlt, 66 | kHvc, 67 | kSmc, 68 | kSvc, 69 | 70 | // System 71 | kAutia1716, 72 | kAutiasp, 73 | kAutiaz, 74 | kAutib1716, 75 | kAutibsp, 76 | kAutibz, 77 | kClrex, 78 | kDmb, 79 | kDsb, 80 | kEsb, 81 | kHint, 82 | kIsb, 83 | kMrs, 84 | kMsr, 85 | kNop, 86 | kPacia1716, 87 | kPaciasp, 88 | kPaciaz, 89 | kPacib1716, 90 | kPacibsp, 91 | kPacibz, 92 | kPsbCsync, 93 | kSev, 94 | kSevl, 95 | kSys, 96 | kSysl, 97 | kWfe, 98 | kWfi, 99 | kXpaclri, 100 | kYield, 101 | 102 | // Unconditional branch register 103 | kBlr, 104 | kBlraa, 105 | kBlraaz, 106 | kBlrab, 107 | kBlrabz, 108 | kBr, 109 | kBraa, 110 | kBraaz, 111 | kBrab, 112 | kBrabz, 113 | kDrps, 114 | kEret, 115 | kEretaa, 116 | kEretaaz, 117 | kEretab, 118 | kEretabz, 119 | kRet, 120 | kRetaa, 121 | kRetaaz, 122 | kRetab, 123 | kRetabz, 124 | 125 | // Unconditional branch immediate 126 | kB, 127 | kBl, 128 | 129 | // Compare and branch 130 | kCbnz, 131 | kCbz, 132 | 133 | // Test and branch 134 | kTbnz, 135 | kTbz, 136 | 137 | // Load/store exclusive 138 | kCas, 139 | kCasa, 140 | kCasal, 141 | kCasl, 142 | kCasp, 143 | kCaspa, 144 | kCaspal, 145 | kCaspl, 146 | kLdar, 147 | kLdaxp, 148 | kLdaxr, 149 | kLdlar, 150 | kLdxp, 151 | kLdxr, 152 | kStllr, 153 | kStlr, 154 | kStlxp, 155 | kStlxr, 156 | kStxp, 157 | kStxr, 158 | 159 | // Load literal 160 | kSimdLdrLiteral, 161 | kLdrLiteral, 162 | kLdrsLiteral, 163 | kPrfmLiteral, 164 | 165 | // Load/store pair 166 | kSimdLdnp, 167 | kSimdLdp, 168 | kSimdStnp, 169 | kSimdStp, 170 | kLdnp, 171 | kLdp, 172 | kLdpsw, 173 | kStnp, 174 | kStp, 175 | 176 | // Load/store 177 | kSimdLdr, 178 | kSimdLdur, 179 | kSimdStr, 180 | kSimdStur, 181 | kLdr, 182 | kLdrs, 183 | kLdtr, 184 | kLdtrs, 185 | kLdur, 186 | kLdurs, 187 | kPrfm, 188 | kStr, 189 | kSttr, 190 | kStur, 191 | 192 | // Data-processing (2 source) 193 | kAsr, 194 | kCrc32b, 195 | kCrc32cb, 196 | kCrc32ch, 197 | kCrc32cw, 198 | kCrc32cx, 199 | kCrc32h, 200 | kCrc32w, 201 | kCrc32x, 202 | kLsl, 203 | kLsr, 204 | kPacga, 205 | kRor, 206 | kSdiv, 207 | kUdiv, 208 | 209 | // Data-processing (1 source) 210 | kAutda, 211 | kAutdb, 212 | kAutia, 213 | kAutib, 214 | kClz, 215 | kCls, 216 | kPacda, 217 | kPacdb, 218 | kPacia, 219 | kPacib, 220 | kRbit, 221 | kRev, 222 | kRev16, 223 | kRev32, 224 | kXpacd, 225 | kXpaci, 226 | 227 | // Logical (shifted register) 228 | kAndShiftedRegister, 229 | kBicShiftedRegister, 230 | kOrrShiftedRegister, 231 | kOrnShiftedRegister, 232 | kEorShiftedRegister, 233 | kEonShiftedRegister, 234 | 235 | // Add/subtract (shifted register) 236 | kAddShiftedRegister, 237 | kSubShiftedRegister, 238 | 239 | // Add/subtract (extended register) 240 | kAddExtendedRegister, 241 | kSubExtendedRegister, 242 | 243 | // Add/subtract with carry 244 | kAdc, 245 | kSbc, 246 | 247 | // Conditional compare 248 | kCcmn, 249 | kCcmp, 250 | 251 | // Conditional select 252 | kCsel, 253 | kCsinc, 254 | kCsinv, 255 | kCsneg, 256 | 257 | // Data processing (3 source) 258 | kMadd, 259 | kMsub, 260 | kSmaddl, 261 | kSmulh, 262 | kSmsubl, 263 | kUmaddl, 264 | kUmulh, 265 | kUmsubl, 266 | 267 | // Unallocated encodings 268 | kUnallocated, 269 | }; 270 | 271 | enum ConditionCode { 272 | kEq = 0b0000, 273 | kNe = 0b0001, 274 | kCs = 0b0010, 275 | kCc = 0b0011, 276 | kMi = 0b0100, 277 | kPl = 0b0101, 278 | kVs = 0b0110, 279 | kVc = 0b0111, 280 | kHi = 0b1000, 281 | kLs = 0b1001, 282 | kGe = 0b1010, 283 | kLt = 0b1011, 284 | kGt = 0b1100, 285 | kLe = 0b1101, 286 | kAl = 0b1110, 287 | }; 288 | 289 | enum OperandType { 290 | kImmediate, 291 | kRegister, 292 | kSystemRegister, 293 | kShift, 294 | kExtend, 295 | kImmediateOffset, 296 | kRegisterOffset 297 | }; 298 | 299 | struct Immediate { 300 | uint8_t size; 301 | uint64_t value; 302 | 303 | explicit Immediate(uint8_t size_, uint64_t value_) 304 | : size(size_), value(value_ & (0xffffffffffffffffull >> (64 - size_))) {} 305 | }; 306 | 307 | struct Register { 308 | uint8_t size; 309 | enum Name { 310 | kX0 = 0, 311 | kX1, 312 | kX2, 313 | kX3, 314 | kX4, 315 | kX5, 316 | kX6, 317 | kX7, 318 | kX8, 319 | kX9, 320 | kX10, 321 | kX11, 322 | kX12, 323 | kX13, 324 | kX14, 325 | kX15, 326 | kX16, 327 | kX17, 328 | kX18, 329 | kX19, 330 | kX20, 331 | kX21, 332 | kX22, 333 | kX23, 334 | kX24, 335 | kX25, 336 | kX26, 337 | kX27, 338 | kX28, 339 | kX29, 340 | kX30, 341 | kXzr, 342 | 343 | kSp, 344 | kPc, 345 | 346 | kV0, 347 | kV1, 348 | kV2, 349 | kV3, 350 | kV4, 351 | kV5, 352 | kV6, 353 | kV7, 354 | kV8, 355 | kV9, 356 | kV10, 357 | kV11, 358 | kV12, 359 | kV13, 360 | kV14, 361 | kV15, 362 | kV16, 363 | kV17, 364 | kV18, 365 | kV19, 366 | kV20, 367 | kV21, 368 | kV22, 369 | kV23, 370 | kV24, 371 | kV25, 372 | kV26, 373 | kV27, 374 | kV28, 375 | kV29, 376 | kV30, 377 | kV31, 378 | 379 | kN, 380 | kZ, 381 | kC, 382 | kV, 383 | } name; 384 | 385 | explicit Register(uint8_t size_, enum Name name_) 386 | : size(size_), name(name_) {} 387 | }; 388 | 389 | struct SystemRegister { 390 | enum Name { 391 | kUnknown = 0, 392 | kSPSel, 393 | kDAIFSet, 394 | kDAIFClr, 395 | kUAO, 396 | kPAN, 397 | } name; 398 | 399 | uint8_t op0; 400 | uint8_t op1; 401 | uint8_t op2; 402 | uint8_t crn; 403 | uint8_t crm; 404 | 405 | explicit SystemRegister(enum Name name_) : name(name_) {} 406 | 407 | explicit SystemRegister(uint8_t op0_, uint8_t op1_, uint8_t op2_, 408 | uint8_t crn_, uint8_t crm_) 409 | : name(kUnknown), op0(op0_), op1(op1_), op2(op2_), crn(crn_), crm(crm_) {} 410 | }; 411 | 412 | struct Shift { 413 | enum Type { 414 | kNone = 0, 415 | kLsl, 416 | kLsr, 417 | kAsr, 418 | kRol, 419 | kRor, 420 | } type; 421 | 422 | uint8_t count; 423 | 424 | explicit Shift(enum Type type_, uint8_t count_) 425 | : type(type_), count(count_) {} 426 | }; 427 | 428 | struct Extend { 429 | enum Type { 430 | kNone, 431 | kUxtb, 432 | kUxth, 433 | kUxtw, 434 | kUxtx, 435 | kLsl, 436 | kSxtb, 437 | kSxth, 438 | kSxtw, 439 | kSxtx, 440 | } type; 441 | 442 | uint8_t count; 443 | 444 | explicit Extend(enum Type type_, uint8_t count_) 445 | : type(type_), count(count_) {} 446 | }; 447 | 448 | struct ImmediateOffset { 449 | uint8_t size; 450 | Register base; 451 | Immediate offset; 452 | Shift shift; 453 | 454 | bool writeback; 455 | bool post_index; 456 | 457 | explicit ImmediateOffset(Register base_, Immediate offset_, Shift shift_, 458 | uint8_t size_, bool writeback_ = false, 459 | bool post_index_ = false) 460 | : size(size_), 461 | base(base_), 462 | offset(offset_), 463 | shift(shift_), 464 | writeback(writeback_), 465 | post_index(post_index_) {} 466 | }; 467 | 468 | struct RegisterOffset { 469 | uint8_t size; 470 | Register base; 471 | Register offset; 472 | Extend extend; 473 | 474 | bool writeback; 475 | bool post_index; 476 | 477 | explicit RegisterOffset(Register base_, Register offset_, Extend extend_, 478 | uint8_t size_, bool writeback_ = false, 479 | bool post_index_ = false) 480 | : size(size_), 481 | base(base_), 482 | offset(offset_), 483 | extend(extend_), 484 | writeback(writeback_), 485 | post_index(post_index_) {} 486 | }; 487 | 488 | typedef std::variant 490 | Operand; 491 | #if 0 492 | typedef absl::variant 494 | Operand; 495 | #endif 496 | 497 | struct Instruction { 498 | uint64_t address; 499 | enum Opcode opcode; 500 | 501 | std::vector operands; 502 | 503 | ConditionCode cc; 504 | bool set_flags; 505 | 506 | Instruction() : address(0), opcode(kUnallocated), set_flags(false) {} 507 | }; 508 | 509 | std::tuple DecodeBitMasks(uint8_t size, Immediate imms, 510 | Immediate immr); 511 | Instruction DecodeInstruction(uint64_t address, uint32_t opcode); 512 | 513 | std::ostream &operator<<(std::ostream &stream, const Operand &opnd); 514 | std::ostream &operator<<(std::ostream &stream, const Instruction &insn); 515 | } // namespace decoder 516 | } // namespace aarch64 517 | } // namespace reil 518 | 519 | #define REIL_AARCH64_DECODER_H_ 520 | #endif // REIL_AARCH64_DECODER_H_ 521 | -------------------------------------------------------------------------------- /tinyinst-coverage.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Google LLC 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 | https ://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 _CRT_SECURE_NO_WARNINGS 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "common.h" 24 | #include "litecov.h" 25 | 26 | uint8_t *trace_bits; 27 | 28 | LiteCov *instrumentation; 29 | bool persist; 30 | int num_iterations; 31 | int cur_iteration; 32 | 33 | // run a single iteration over the target process 34 | // whether it's the whole process or target method 35 | // and regardless if the target is persistent or not 36 | // (should know what to do in pretty much all cases) 37 | void RunTarget(int argc, char **argv, unsigned int pid, uint32_t timeout) { 38 | DebuggerStatus status; 39 | 40 | if (instrumentation->IsTargetFunctionDefined()) { 41 | if (cur_iteration == num_iterations) { 42 | instrumentation->Kill(); 43 | cur_iteration = 0; 44 | } 45 | } 46 | 47 | // else clear only when the target function is reached 48 | if (!instrumentation->IsTargetFunctionDefined()) { 49 | instrumentation->ClearCoverage(); 50 | } 51 | 52 | if (instrumentation->IsTargetAlive() && persist) { 53 | status = instrumentation->Continue(timeout); 54 | } else { 55 | instrumentation->Kill(); 56 | cur_iteration = 0; 57 | if (argc) { 58 | status = instrumentation->Run(argc, argv, timeout); 59 | } else { 60 | status = instrumentation->Attach(pid, timeout); 61 | } 62 | } 63 | 64 | // if target function is defined, 65 | // we should wait until it is hit 66 | if (instrumentation->IsTargetFunctionDefined()) { 67 | if ((status != DEBUGGER_TARGET_START) && argc) { 68 | // try again with a clean process 69 | WARN("Target function not reached, retrying with a clean process\n"); 70 | instrumentation->Kill(); 71 | cur_iteration = 0; 72 | status = instrumentation->Run(argc, argv, timeout); 73 | } 74 | 75 | if (status != DEBUGGER_TARGET_START) { 76 | switch (status) { 77 | case DEBUGGER_CRASHED: 78 | FATAL("Process crashed before reaching the target method\n"); 79 | break; 80 | case DEBUGGER_HANGED: 81 | FATAL("Process hanged before reaching the target method\n"); 82 | break; 83 | case DEBUGGER_PROCESS_EXIT: 84 | FATAL("Process exited before reaching the target method\n"); 85 | break; 86 | default: 87 | FATAL("An unknown problem occured before reaching the target method\n"); 88 | break; 89 | } 90 | } 91 | 92 | instrumentation->ClearCoverage(); 93 | 94 | status = instrumentation->Continue(timeout); 95 | } 96 | 97 | switch (status) { 98 | case DEBUGGER_CRASHED: 99 | printf("Process crashed\n"); 100 | instrumentation->Kill(); 101 | break; 102 | case DEBUGGER_HANGED: 103 | printf("Process hanged\n"); 104 | instrumentation->Kill(); 105 | break; 106 | case DEBUGGER_PROCESS_EXIT: 107 | if (instrumentation->IsTargetFunctionDefined()) { 108 | printf("Process exit during target function\n"); 109 | } else { 110 | printf("Process finished normally\n"); 111 | } 112 | break; 113 | case DEBUGGER_TARGET_END: 114 | if (instrumentation->IsTargetFunctionDefined()) { 115 | printf("Target function returned normally\n"); 116 | cur_iteration++; 117 | } else { 118 | FATAL("Unexpected status received from the debugger\n"); 119 | } 120 | break; 121 | default: 122 | FATAL("Unexpected status received from the debugger\n"); 123 | break; 124 | } 125 | } 126 | 127 | int main(int argc, char **argv) 128 | { 129 | instrumentation = new LiteCov(); 130 | instrumentation->Init(argc, argv); 131 | 132 | int target_opt_ind = 0; 133 | for (int i = 1; i < argc; i++) { 134 | if (strcmp(argv[i], "--") == 0) { 135 | target_opt_ind = i + 1; 136 | break; 137 | } 138 | } 139 | 140 | int target_argc = (target_opt_ind) ? argc - target_opt_ind : 0; 141 | char **target_argv = (target_opt_ind) ? argv + target_opt_ind : NULL; 142 | 143 | unsigned int pid = GetIntOption("-pid", argc, argv, 0); 144 | persist = GetBinaryOption("-persist", argc, argv, false); 145 | num_iterations = GetIntOption("-iterations", argc, argv, 1); 146 | char *outfile = GetOption("-coverage_file", argc, argv); 147 | 148 | if (!target_argc && !pid) { 149 | printf("Usage:\n"); 150 | printf("%s -- \n", argv[0]); 151 | printf("Or:\n"); 152 | printf("%s -pid \n", argv[0]); 153 | return 0; 154 | } 155 | 156 | Coverage coverage, newcoverage; 157 | 158 | for (int i = 0; i < num_iterations; i++) { 159 | RunTarget(target_argc, target_argv, pid, 0xFFFFFFFF); 160 | 161 | Coverage newcoverage; 162 | 163 | instrumentation->GetCoverage(newcoverage, true); 164 | 165 | for (auto iter = newcoverage.begin(); iter != newcoverage.end(); iter++) { 166 | printf("Found %zd new offsets in %s\n", iter->offsets.size(), iter->module_name.c_str()); 167 | } 168 | 169 | instrumentation->IgnoreCoverage(newcoverage); 170 | 171 | MergeCoverage(coverage, newcoverage); 172 | } 173 | 174 | if (outfile) WriteCoverage(coverage, outfile); 175 | 176 | instrumentation->Kill(); 177 | 178 | return 0; 179 | } 180 | -------------------------------------------------------------------------------- /tinyinst.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Google LLC 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 | https ://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 TINYINST_H 18 | #define TINYINST_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) 27 | #include "Windows/debugger.h" 28 | #elif __APPLE__ 29 | #include "macOS/debugger.h" 30 | #else 31 | #include "Linux/debugger.h" 32 | #endif 33 | 34 | #include "common.h" 35 | #include "assembler.h" 36 | #include "instruction.h" 37 | #include "unwind.h" 38 | 39 | class Hook; 40 | 41 | #if defined(_WIN64) 42 | 43 | #include "Windows/winunwind.h" 44 | 45 | #elif __APPLE__ 46 | 47 | #include "macOS/unwindmacos.h" 48 | class UnwindGeneratorMacOS; 49 | 50 | #endif 51 | 52 | // must be a power of two 53 | #define JUMPTABLE_SIZE 0x2000 54 | 55 | // we will allocate 56 | // original_code_size * CODE_SIZE_MULTIPLIER + 57 | // JUMPTABLE_SIZE * child_ptr_size 58 | // for instrumented code 59 | #ifdef ARM64 60 | #define CODE_SIZE_MULTIPLIER 8 61 | #else 62 | #define CODE_SIZE_MULTIPLIER 4 63 | #endif 64 | 65 | typedef struct xed_decoded_inst_s xed_decoded_inst_t; 66 | 67 | class ModuleInfo; 68 | 69 | 70 | enum InstructionResult { 71 | INST_HANDLED, 72 | INST_NOTHANDLED, 73 | INST_STOPBB 74 | }; 75 | 76 | class TinyInst : public Debugger { 77 | public: 78 | virtual void Init(int argc, char **argv) override; 79 | 80 | void EnableInstrumentation() { 81 | instrumentation_disabled = false; 82 | } 83 | 84 | void DisableInstrumentation() { 85 | instrumentation_disabled = true; 86 | } 87 | 88 | protected: 89 | 90 | enum IndirectInstrumentation { 91 | II_NONE, 92 | II_GLOBAL, 93 | II_LOCAL, 94 | II_AUTO 95 | }; 96 | 97 | enum PatchModuleEntriesValue { 98 | OFF = 0, 99 | DATA = 1, 100 | CODE = 2, 101 | ALL = DATA | CODE 102 | }; 103 | 104 | std::list instrumented_modules; 105 | 106 | struct CrossModuleLink { 107 | ModuleInfo *module1; 108 | ModuleInfo *module2; 109 | size_t offset1; 110 | size_t offset2; 111 | }; 112 | 113 | virtual void OnEntrypoint() override; 114 | virtual void OnProcessCreated() override; 115 | virtual void OnProcessExit() override; 116 | virtual void OnModuleLoaded(void *module, char *module_name) override; 117 | virtual void OnModuleUnloaded(void *module) override; 118 | virtual bool OnException(Exception *exception_record) override; 119 | virtual void OnTargetMethodReached() override; 120 | virtual void OnCrashed(Exception *exception_record) override; 121 | 122 | virtual size_t GetTranslatedAddress(size_t address) override; 123 | 124 | void WriteCode(ModuleInfo *module, void *data, size_t size); 125 | void WriteCodeAtOffset(ModuleInfo *module, size_t offset, void *data, size_t size); 126 | void WritePointer(ModuleInfo *module, size_t value); 127 | void WritePointerAtOffset(ModuleInfo *module, size_t value, size_t offset); 128 | size_t ReadPointer(ModuleInfo *module, size_t offset); 129 | 130 | inline void FixDisp4(ModuleInfo *module, int32_t disp); 131 | 132 | size_t GetCurrentInstrumentedAddress(ModuleInfo *module); 133 | void CommitCode(ModuleInfo *module, size_t start_offset, size_t size); 134 | 135 | ModuleInfo *GetModuleByName(const char *name); 136 | ModuleInfo *GetModule(size_t address); 137 | ModuleInfo *GetModuleFromInstrumented(size_t address); 138 | AddressRange *GetRegion(ModuleInfo *module, size_t address); 139 | 140 | // instrumentation API 141 | virtual void OnModuleEntered(ModuleInfo *module, size_t entry_address) {} 142 | virtual void InstrumentBasicBlock(ModuleInfo *module, size_t bb_address) {} 143 | virtual void InstrumentEdge(ModuleInfo *previous_module, 144 | ModuleInfo *next_module, 145 | size_t previous_address, 146 | size_t next_address) {} 147 | 148 | virtual InstructionResult InstrumentInstruction(ModuleInfo *module, 149 | Instruction& inst, 150 | size_t bb_address, 151 | size_t instruction_address); 152 | 153 | virtual void OnModuleInstrumented(ModuleInfo* module); 154 | virtual void OnModuleUninstrumented(ModuleInfo* module); 155 | virtual void OnBasicBlcokTranslated(ModuleInfo *module, size_t start_offset, size_t end_offset) { } 156 | 157 | int32_t sp_offset; 158 | Assembler* assembler_; 159 | 160 | UnwindGenerator* unwind_generator; 161 | virtual void OnReturnAddress(ModuleInfo *module, size_t original_address, size_t translated_address); 162 | 163 | void RegisterHook(Hook *hook); 164 | 165 | void InstrumentAddressRange(const char *name, size_t min_address, size_t max_address); 166 | 167 | private: 168 | void AddInstrumentedModule(char* name, bool do_protect); 169 | bool HandleBreakpoint(void *address); 170 | void OnInstrumentModuleLoaded(void *module, ModuleInfo *target_module); 171 | ModuleInfo *IsInstrumentModule(char *module_name); 172 | void InstrumentAllLoadedModules(); 173 | void InstrumentModule(ModuleInfo *module); 174 | void ClearInstrumentation(ModuleInfo *module); 175 | bool TryExecuteInstrumented(char * address); 176 | size_t GetTranslatedAddress(ModuleInfo *module, size_t address); 177 | void TranslateBasicBlock(char *address, 178 | ModuleInfo *module, 179 | std::set *queue, 180 | std::list> *offset_fixes); 181 | void TranslateBasicBlockRecursive(char *address, ModuleInfo *module); 182 | void FixOffsetOrEnqueue(ModuleInfo *module, 183 | uint32_t bb, 184 | uint32_t jmp_offset, 185 | std::set *queue, 186 | std::list> *offset_fixes); 187 | void InvalidInstruction(ModuleInfo* module); 188 | 189 | // relative jump outside of current module 190 | void OutsideJump(ModuleInfo* module, size_t address); 191 | 192 | // needed to support cross-module linking 193 | // on module unloads / reloads 194 | void InvalidateCrossModuleLink(CrossModuleLink *link); 195 | void FixCrossModuleLink(CrossModuleLink *link); 196 | void FixCrossModuleLinks(ModuleInfo *module); 197 | void InvalidateCrossModuleLinks(ModuleInfo *module); 198 | void InvalidateCrossModuleLinks(); 199 | void ClearCrossModuleLinks(ModuleInfo *module); 200 | void ClearCrossModuleLinks(); 201 | 202 | void PatchModuleEntries(ModuleInfo* module); 203 | 204 | // functions related to indirect jump/call instrumentation 205 | void InitGlobalJumptable(ModuleInfo *module); 206 | void InstrumentIndirect(ModuleInfo *module, 207 | Instruction& inst, 208 | size_t instruction_address, 209 | IndirectInstrumentation mode, 210 | size_t bb_address); 211 | 212 | // returns the indirect instrumentation mode that should be used for a particular call 213 | // can be overriden 214 | virtual IndirectInstrumentation ShouldInstrumentIndirect(ModuleInfo *module, 215 | Instruction& inst, 216 | size_t instruction_address); 217 | 218 | size_t AddTranslatedJump(ModuleInfo *module, 219 | ModuleInfo *target_module, 220 | size_t original_target, 221 | size_t actual_target, 222 | size_t list_head_offset, 223 | IndirectBreakpoinInfo& breakpoint_info, 224 | bool global_indirect); 225 | bool HandleIndirectJMPBreakpoint(void *address); 226 | 227 | void PatchPointersLocal(char* buf, size_t size, 228 | std::unordered_map& search_replace, 229 | bool commit_code, ModuleInfo* module); 230 | template 231 | void PatchPointersLocalT(char* buf, size_t size, 232 | std::unordered_map& search_replace, 233 | bool commit_code, ModuleInfo* module); 234 | 235 | IndirectInstrumentation indirect_instrumentation_mode; 236 | 237 | bool instrument_cross_module_calls; 238 | bool patch_return_addresses; 239 | bool persist_instrumentation_data; 240 | 241 | bool trace_basic_blocks; 242 | bool trace_module_entries; 243 | 244 | bool generate_unwind; 245 | 246 | bool page_extend_modules; 247 | 248 | // these could be indexed by module1 and module2 for performance 249 | // but the assumption for now is that there won't be too many of 250 | // them so a flat structure shoudl be ok for now 251 | std::list cross_module_links; 252 | 253 | bool instrumentation_disabled; 254 | bool instrument_modules_on_load; 255 | 256 | bool full_address_map; 257 | 258 | PatchModuleEntriesValue patch_module_entries; 259 | 260 | std::list hooks; 261 | std::unordered_map resolved_hooks; 262 | 263 | friend class Asssembler; 264 | friend class X86Assembler; 265 | friend class Arm64Assembler; 266 | friend class ModuleInfo; 267 | friend class UnwindGenerator; 268 | friend class Hook; 269 | #if defined(_WIN64) 270 | friend class WinUnwindGenerator; 271 | #elif __APPLE__ 272 | friend class UnwindGeneratorMacOS; 273 | #endif 274 | }; 275 | 276 | struct IndirectBreakpoinInfo { 277 | size_t list_head; 278 | size_t source_bb; 279 | #ifdef ARM64 280 | uint8_t branch_register; 281 | #endif 282 | }; 283 | 284 | class ModuleInfo { 285 | public: 286 | ModuleInfo(); 287 | void ClearInstrumentation(); 288 | 289 | std::string module_name; 290 | void *module_header; 291 | size_t min_address; 292 | size_t max_address; 293 | size_t code_size; 294 | bool loaded; 295 | bool instrumented; 296 | std::list executable_ranges; 297 | 298 | size_t instrumented_code_size; 299 | size_t instrumented_code_allocated; 300 | char *instrumented_code_local; 301 | char *instrumented_code_remote; 302 | char *instrumented_code_remote_previous; 303 | 304 | std::unordered_map basic_blocks; 305 | 306 | // instrumented address to original address 307 | std::map address_map; 308 | 309 | size_t br_indirect_newtarget_global; 310 | 311 | // per callsite jumplist breakpoint 312 | // from breakpoint address to list head offset 313 | std::unordered_map br_indirect_newtarget_list; 314 | 315 | size_t jumptable_offset; 316 | size_t jumptable_address_offset; 317 | 318 | std::unordered_set invalid_instructions; 319 | std::unordered_map outside_jumps; 320 | std::unordered_map tracepoints; 321 | 322 | std::unordered_set entry_offsets; 323 | 324 | UnwindData *unwind_data; 325 | 326 | bool do_protect; 327 | 328 | // clients can use this to store additional data 329 | // about the module 330 | void *client_data; 331 | }; 332 | 333 | #endif // TINYINST_H 334 | -------------------------------------------------------------------------------- /unwind.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Google LLC 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 | https ://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 UNWIND_H 18 | #define UNWIND_H 19 | 20 | class TinyInst; 21 | class ModuleInfo; 22 | 23 | class UnwindData { 24 | public: 25 | virtual ~UnwindData() = default; 26 | }; 27 | 28 | class UnwindGenerator { 29 | public: 30 | UnwindGenerator(TinyInst& tinyinst) : tinyinst_(tinyinst) {} 31 | virtual ~UnwindGenerator() = default; 32 | 33 | virtual void Init(int argc, char **argv) {} 34 | 35 | virtual void OnModuleInstrumented(ModuleInfo* module) { } 36 | virtual void OnModuleUninstrumented(ModuleInfo* module) { } 37 | 38 | virtual size_t MaybeRedirectExecution(ModuleInfo* module, size_t IP) { 39 | return IP; 40 | } 41 | 42 | virtual void OnBasicBlockStart(ModuleInfo* module, 43 | size_t original_address, 44 | size_t translated_address) 45 | { } 46 | 47 | virtual void OnInstruction(ModuleInfo* module, 48 | size_t original_address, 49 | size_t translated_address) 50 | { } 51 | 52 | virtual void OnBasicBlockEnd(ModuleInfo* module, 53 | size_t original_address, 54 | size_t translated_address) 55 | { } 56 | 57 | virtual void OnModuleLoaded(void *module, char *module_name) { } 58 | 59 | virtual void OnReturnAddress(ModuleInfo *module, size_t original_address, size_t translated_address) { } 60 | 61 | virtual bool HandleBreakpoint(ModuleInfo* module, void *address) { return false; } 62 | 63 | virtual bool Is64BitOnly() { return false; } 64 | 65 | protected: 66 | TinyInst& tinyinst_; 67 | }; 68 | 69 | #endif // UNWIND_H 70 | --------------------------------------------------------------------------------