├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── README.md ├── Working Architecture.pdf ├── dependencies └── build_dependencies.sh ├── honey_analyzer ├── capture │ ├── ha_capture_session.c │ └── ha_capture_session.h ├── ha_debug_switch.h ├── honey_analyzer.h ├── processor_trace │ ├── ha_pt_decoder.c │ ├── ha_pt_decoder.h │ └── ha_pt_decoder_constants.h └── trace_analysis │ ├── ha_session.c │ ├── ha_session.h │ └── ha_session_internal.h ├── honey_coverage ├── hc_tree_set.c ├── hc_tree_set.h └── main.c ├── honey_driver ├── Makefile ├── honey_driver.c └── honey_driver_constants.h ├── honey_hive_generator ├── disassembly │ ├── elf.h │ ├── hh_disassembly.c │ └── hh_disassembly.h ├── hive_generation │ ├── hh_hive_generator.c │ └── hh_hive_generator.h └── main.c ├── honey_tester ├── main.c └── unit_testing │ ├── ha_session_audit.c │ └── ha_session_audit.h ├── honeybee_shared ├── hb_driver_packets.h ├── hb_hive.c └── hb_hive.h ├── paper └── process_traces.py └── unittest.py /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/macos,linux,clion+all,xcode,cmake 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=macos,linux,clion+all,xcode,cmake 4 | 5 | ### CLion+all ### 6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 8 | 9 | # User-specific stuff 10 | .idea/**/workspace.xml 11 | .idea/**/tasks.xml 12 | .idea/**/usage.statistics.xml 13 | .idea/**/dictionaries 14 | .idea/**/shelf 15 | 16 | # Generated files 17 | .idea/**/contentModel.xml 18 | 19 | # Sensitive or high-churn files 20 | .idea/**/dataSources/ 21 | .idea/**/dataSources.ids 22 | .idea/**/dataSources.local.xml 23 | .idea/**/sqlDataSources.xml 24 | .idea/**/dynamic.xml 25 | .idea/**/uiDesigner.xml 26 | .idea/**/dbnavigator.xml 27 | 28 | # Gradle 29 | .idea/**/gradle.xml 30 | .idea/**/libraries 31 | 32 | # Gradle and Maven with auto-import 33 | # When using Gradle or Maven with auto-import, you should exclude module files, 34 | # since they will be recreated, and may cause churn. Uncomment if using 35 | # auto-import. 36 | # .idea/artifacts 37 | # .idea/compiler.xml 38 | # .idea/jarRepositories.xml 39 | # .idea/modules.xml 40 | # .idea/*.iml 41 | # .idea/modules 42 | # *.iml 43 | # *.ipr 44 | 45 | # CMake 46 | cmake-build-*/ 47 | 48 | # Mongo Explorer plugin 49 | .idea/**/mongoSettings.xml 50 | 51 | # File-based project format 52 | *.iws 53 | 54 | # IntelliJ 55 | out/ 56 | 57 | # mpeltonen/sbt-idea plugin 58 | .idea_modules/ 59 | 60 | # JIRA plugin 61 | atlassian-ide-plugin.xml 62 | 63 | # Cursive Clojure plugin 64 | .idea/replstate.xml 65 | 66 | # Crashlytics plugin (for Android Studio and IntelliJ) 67 | com_crashlytics_export_strings.xml 68 | crashlytics.properties 69 | crashlytics-build.properties 70 | fabric.properties 71 | 72 | # Editor-based Rest Client 73 | .idea/httpRequests 74 | 75 | # Android studio 3.1+ serialized cache file 76 | .idea/caches/build_file_checksums.ser 77 | 78 | ### CLion+all Patch ### 79 | # Ignores the whole .idea folder and all .iml files 80 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 81 | 82 | .idea/ 83 | 84 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 85 | 86 | *.iml 87 | modules.xml 88 | .idea/misc.xml 89 | *.ipr 90 | 91 | # Sonarlint plugin 92 | .idea/sonarlint 93 | 94 | ### CMake ### 95 | CMakeLists.txt.user 96 | CMakeCache.txt 97 | CMakeFiles 98 | CMakeScripts 99 | Testing 100 | Makefile 101 | cmake_install.cmake 102 | install_manifest.txt 103 | compile_commands.json 104 | CTestTestfile.cmake 105 | _deps 106 | CMakeUserPresets.json 107 | 108 | ### CMake Patch ### 109 | # External projects 110 | *-prefix/ 111 | 112 | ### Linux ### 113 | *~ 114 | 115 | # temporary files which can be created if a process still has a handle open of a deleted file 116 | .fuse_hidden* 117 | 118 | # KDE directory preferences 119 | .directory 120 | 121 | # Linux trash folder which might appear on any partition or disk 122 | .Trash-* 123 | 124 | # .nfs files are created when an open file is removed but is still being accessed 125 | .nfs* 126 | 127 | ### macOS ### 128 | # General 129 | .DS_Store 130 | .AppleDouble 131 | .LSOverride 132 | 133 | # Icon must end with two \r 134 | Icon 135 | 136 | 137 | # Thumbnails 138 | ._* 139 | 140 | # Files that might appear in the root of a volume 141 | .DocumentRevisions-V100 142 | .fseventsd 143 | .Spotlight-V100 144 | .TemporaryItems 145 | .Trashes 146 | .VolumeIcon.icns 147 | .com.apple.timemachine.donotpresent 148 | 149 | # Directories potentially created on remote AFP share 150 | .AppleDB 151 | .AppleDesktop 152 | Network Trash Folder 153 | Temporary Items 154 | .apdisk 155 | 156 | ### Xcode ### 157 | # Xcode 158 | # 159 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 160 | 161 | ## User settings 162 | xcuserdata/ 163 | 164 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 165 | *.xcscmblueprint 166 | *.xccheckout 167 | 168 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 169 | build/ 170 | DerivedData/ 171 | *.moved-aside 172 | *.pbxuser 173 | !default.pbxuser 174 | *.mode1v3 175 | !default.mode1v3 176 | *.mode2v3 177 | !default.mode2v3 178 | *.perspectivev3 179 | !default.perspectivev3 180 | 181 | ## Gcc Patch 182 | /*.gcno 183 | 184 | ### Xcode Patch ### 185 | *.xcodeproj/* 186 | !*.xcodeproj/project.pbxproj 187 | !*.xcodeproj/xcshareddata/ 188 | !*.xcworkspace/contents.xcworkspacedata 189 | **/xcshareddata/WorkspaceSettings.xcsettings 190 | 191 | # End of https://www.toptal.com/developers/gitignore/api/macos,linux,clion+all,xcode,cmake 192 | 193 | !honey_driver/Makefile 194 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dependencies/xed"] 2 | path = dependencies/xed 3 | url = https://github.com/intelxed/xed.git 4 | [submodule "dependencies/mbuild"] 5 | path = dependencies/mbuild 6 | url = https://github.com/intelxed/mbuild.git 7 | [submodule "dependencies/libipt"] 8 | path = dependencies/libipt 9 | url = https://github.com/intel/libipt.git 10 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | set(CMAKE_C_STANDARD 99) 3 | 4 | project(honey_hive_generator C) 5 | add_executable(honey_hive_generator 6 | honey_hive_generator/main.c 7 | honey_hive_generator/disassembly/elf.h 8 | honey_hive_generator/disassembly/hh_disassembly.c 9 | honey_hive_generator/disassembly/hh_disassembly.h 10 | honey_hive_generator/hive_generation/hh_hive_generator.c 11 | honey_hive_generator/hive_generation/hh_hive_generator.h 12 | honeybee_shared/hb_hive.h) 13 | target_include_directories(honey_hive_generator PRIVATE ${CMAKE_SOURCE_DIR}/dependencies/xed/obj/wkit/include/xed) 14 | target_link_libraries(honey_hive_generator ${CMAKE_SOURCE_DIR}/dependencies/xed/obj/libxed.a) 15 | 16 | project(honey_analyzer C) 17 | add_library(honey_analyzer STATIC 18 | honey_analyzer/trace_analysis/ha_session.c 19 | honey_analyzer/trace_analysis/ha_session.h 20 | honey_analyzer/processor_trace/ha_pt_decoder.c 21 | honey_analyzer/processor_trace/ha_pt_decoder.h 22 | honey_analyzer/ha_debug_switch.h 23 | honey_analyzer/trace_analysis/ha_session_internal.h 24 | honey_analyzer/capture/ha_capture_session.c 25 | honey_analyzer/capture/ha_capture_session.h 26 | honeybee_shared/hb_hive.c 27 | honeybee_shared/hb_hive.h honey_analyzer/processor_trace/ha_pt_decoder_constants.h honey_analyzer/honey_analyzer.h) 28 | target_compile_options(honey_analyzer PRIVATE -Ofast) 29 | 30 | #For ease of debugging, we don't actually link against honey_analyzer in honey_tester since CMake does not recursively 31 | #detect changes. 32 | project(honey_tester C) 33 | add_executable(honey_tester 34 | honey_tester/main.c 35 | honey_tester/unit_testing/ha_session_audit.c 36 | honey_tester/unit_testing/ha_session_audit.h 37 | honey_analyzer/trace_analysis/ha_session.c 38 | honey_analyzer/trace_analysis/ha_session.h 39 | honey_analyzer/processor_trace/ha_pt_decoder.c 40 | honey_analyzer/processor_trace/ha_pt_decoder.h 41 | honey_analyzer/ha_debug_switch.h 42 | honey_analyzer/capture/ha_capture_session.c 43 | honey_analyzer/capture/ha_capture_session.h 44 | honey_analyzer/trace_analysis/ha_session_internal.h 45 | honeybee_shared/hb_hive.c 46 | honeybee_shared/hb_hive.h honey_analyzer/processor_trace/ha_pt_decoder_constants.h honey_analyzer/honey_analyzer.h) 47 | target_include_directories(honey_tester PRIVATE ${CMAKE_SOURCE_DIR}/dependencies/libipt/libipt/include) 48 | target_link_libraries(honey_tester ${CMAKE_SOURCE_DIR}/dependencies/libipt/lib/libipt.a) 49 | target_compile_options(honey_tester PRIVATE -Ofast) 50 | #target_compile_options(honey_analyzer PRIVATE -fno-omit-frame-pointer -fsanitize=address) 51 | #target_link_options(honey_analyzer PRIVATE -fno-omit-frame-pointer -fsanitize=address) 52 | 53 | project(honey_coverage C) 54 | add_executable(honey_coverage honey_coverage/main.c honey_coverage/hc_tree_set.c honey_coverage/hc_tree_set.h) 55 | target_link_libraries(honey_coverage honey_analyzer) 56 | target_compile_options(honey_coverage PRIVATE -Ofast) 57 | 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Honeybee 2 | 3 | Honeybee is a very fast Intel Processor Trace capture and decoding suite which enables previously unattainable performance in source and blackbox fuzzing alike by taking advantage of a custom ahead-of-time analysis cache to accelerate decoding multiple traces. 4 | 5 | ### Sub-projects 6 | 7 | This repo contains a handful of sub-projects. A brief explanation of each is given below: 8 | 9 | #### `honey_hive_generator` 10 | 11 | This project is an executable which processes an ELF binary into the custom ahead-of-time cache (a "hive" file, typically ending with `.hive`). It is expected to be invoked once per binary being traced while the hive file it produces should be re-used for optimal performance. 12 | 13 | #### `honey_driver` 14 | 15 | This project is a Linux kernel module which implements a minimal, fuzzing optimized interface for configuring Intel Processor Trace. It creates a devfs mount at `/dev/honey_driver` and clients may communicate with the driver using `ioctl` and `mmap`. See `honeybee_shared/hb_driver_packets.h` for information about the supported `ioctl`s. Notable optimizations involve allowing each CPU's tracing to be managed independently and in kernel trace preparation. This is important in fuzzing workflows where many instances are parralized by pinning fuzzing processes to specific CPU cores. 16 | 17 | You do not need to communicate with the kernel module directly if you use the `honey_analyzer` library since it implements a full client using the `ha_capture_session_*` functions. 18 | 19 | #### `honey_analyzer` 20 | 21 | This project is a library (static by default or shared depending on build configuration) which implements everything an analysis tool needs to process traces given a hive file. It includes functions for interacting with the kernel module (`ha_capture_session_*`) and analyzing traces (`ha_analysis_session_*`). 22 | 23 | #### `honey_coverage` 24 | 25 | This is a demo project which uses `honey_analyzer` to gather both unique edge and basic block coverage using Intel PT for a given program. It outputs the following in a plain text format after the given program exits: 26 | 27 | ``` 28 | block set count 29 | edge set count 30 | [[block set elements (1 per line)]] 31 | [[edge set elements (1 per line)]] 32 | ``` 33 | 34 | #### `honey_tester` 35 | 36 | This is project is a unit testing shim for `honey_analyzer`. It is used by `/unittest.py`. To run unit tests, download the [unit test data](https://github.com/trailofbits/Honeybee/releases/tag/0) and decompress it at the same level as this repository (i.e. adjacent) and then execute `python3 unittest.py`. 37 | 38 | 39 | ### Fuzzing implementations 40 | 41 | As part of the initial Honeybee development cycle, we implemented support into [a fork of Honggfuzz](https://github.com/ezhes/honggfuzz/tree/honeybee). To build, follow build directions below. To fuzz a binary `${TARGET}`, use the following procedure: 42 | 43 | 1. Execute `${TARGET}$` under a debugger (ASLR should not be enabled) and determine the address range for the code you wish to trace. This can be done with lldb by launch the process and then executing `image dump sections`. In most cases where you are tracing the main binary of a program, this range is `0x1` to `0x6fffffffffff`. Call the starting and stop address `${START_ADDRESS}` and `${STOP_ADDRESS}` respectively. 44 | 2. Generate a hive for the target binary `${TARGET}` and store it at `${HIVE_PATH}`: `./honey_hive_generator "$TARGET" "${HIVE_PATH}"` 45 | 3. Begin fuzzing. You may use whichever other arguments you wish, but Honeybee requires `--linux_honeybee_ipt_edge --honeybee_hive ${HIVE_PATH} --honeybee_start_address ${START_ADDRESS} --honeybee_stop_address ${STOP_ADDRESS}` 46 | 47 | **Miscellaneous notes on fuzzing** 48 | 49 | If you wish to persistently fuzz a program for maximal performance, you have two options. Firstly, if you have source, you can simply link in the honggfuzz persistent functions without adding software instrumentation by compiling with the flags `/honggfuzz/libhfuzz/libhfuzz.a /honggfuzz/libhfcommon/libhfcommon.a -ldl -lpthread -lrt`. If you do not have source but the target binary is relocatable, you can take advantage of the fact that relocatable ELF binaries are actually just shared libraries which you can load at runtime. You may use something like LIEF to expose a target function and then write a shim which loads the target binary at runtime and calls the target function through a persistent honggfuzz hook. Note that your `${START_ADDRESS}` and `${STOP_ADDRESS}` must be the address range at which the target binary (not the shim) is loaded to gather correct coverage information. 50 | 51 | ### Compiling 52 | 53 | #### Dependencies 54 | 55 | This project uses `cmake` for its primary build system. Honeybee also depends upon `libxed` for performing binary analysis in `honey_hive_generator` and `libipt` in `honey_test` for ensuring that Honeybee's decoder behaves identically to Intel's reference decoder. You can checkout and build all dependencies by executing `cd dependencies; ./build_dependencies.sh` 56 | 57 | ### Honeybee user-space components 58 | 59 | To build all of Honeybees user-space components, do the following to build all targets to the `cmake-build-debug` folder: 60 | 61 | ``` 62 | #Build libipt and libxed 63 | cd dependencies 64 | ./build_dependencies.sh 65 | cd ../ 66 | 67 | #Build user-space 68 | mkdir cmake_build_debug 69 | cd cmake_build_debug 70 | cmake .. 71 | cmake --build . --target all 72 | ``` 73 | 74 | ### Honeybee kernel components 75 | 76 | Building kernel components requires current kernel sources. Please refer to your distribution's manual for how to do this. Once sources are ready, the module may be compiled and loaded via `cd honey_driver; sudo make build_install` 77 | 78 | ### Hongfuzz fork 79 | 80 | 1. Checkout the `honeybee` branch of [our fork](https://github.com/ezhes/honggfuzz/tree/honeybee) at the same level as the Honeybee repo. 81 | 2. Compile Honeybee's user-space components, ensuring that `libhoney_analyzer` builds to a static library. 82 | 3. Compile honggfuzz using `make` 83 | 84 | 85 | ### Contributors 86 | 87 | * **Allison Husain** (University of California, Berkeley): Developed Honeybee's analysis algorithim, wrote Honeybee and of its tools 88 | * **Artem Dinaburg** (Trail of Bits): Project mentor 89 | * **Peter Goodman** (Trail of Bits): Project mentor 90 | -------------------------------------------------------------------------------- /Working Architecture.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trailofbits/Honeybee/edc56f31578d15a23b544a9b23d9d1a371dad875/Working Architecture.pdf -------------------------------------------------------------------------------- /dependencies/build_dependencies.sh: -------------------------------------------------------------------------------- 1 | #Fetch submodules 2 | git submodule init 3 | git submodule update --remote 4 | 5 | cd xed 6 | ./mfile.py && 7 | echo "Built xed" 8 | 9 | cd ../ 10 | cd libipt 11 | cmake -DBUILD_SHARED_LIBS=0 . && 12 | make && 13 | echo "Built libipt" 14 | -------------------------------------------------------------------------------- /honey_analyzer/capture/ha_capture_session.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 1/12/21. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "ha_capture_session.h" 15 | #include "../../honeybee_shared/hb_driver_packets.h" 16 | #include "../processor_trace/ha_pt_decoder_constants.h" 17 | 18 | struct ha_capture_session_internal { 19 | /** 20 | * The CPU we're configure to trace on 21 | */ 22 | uint16_t cpu_id; 23 | 24 | /** 25 | * The device driver handle 26 | */ 27 | int fd; 28 | 29 | /** 30 | * The handle for the trace buffer. Note, we vend this to external users, though they do not own it. 31 | */ 32 | uint8_t *mmap_handle; 33 | 34 | /** 35 | * The size of our mmap'd region 36 | */ 37 | uint64_t mmap_size; 38 | }; 39 | 40 | int ha_capture_session_alloc(ha_capture_session_t *session_out, uint16_t cpu_id) { 41 | ha_capture_session_t session = calloc(1, sizeof(struct ha_capture_session_internal)); 42 | if (!session) { 43 | return -ENOMEM; 44 | } 45 | 46 | session->cpu_id = cpu_id; 47 | session->fd = open("/dev/honey_driver", O_CLOEXEC | O_RDWR); 48 | if (session->fd < 0) { 49 | ha_capture_session_free(session); 50 | return -errno; 51 | } 52 | 53 | *session_out = session; 54 | 55 | return 0; 56 | } 57 | 58 | void ha_capture_session_free(ha_capture_session_t session) { 59 | if (!session) { 60 | return; 61 | } 62 | 63 | if (session->fd >= 0) { 64 | close(session->fd); 65 | } 66 | 67 | if (session->mmap_handle && session->mmap_handle != MAP_FAILED) { 68 | munmap(session->mmap_handle, session->mmap_size); 69 | } 70 | 71 | free(session); 72 | } 73 | 74 | int ha_capture_session_set_global_buffer_size(ha_capture_session_t session, uint32_t buffer_count, uint8_t page_power) { 75 | //If we're changing the buffer size, we need to invalidate our mapping 76 | if (session->mmap_handle && session->mmap_handle != MAP_FAILED) { 77 | munmap(session->mmap_handle, session->mmap_size); 78 | session->mmap_handle = NULL; 79 | session->mmap_size = 0; 80 | } 81 | 82 | hb_driver_packet_configure_buffers configure_buffers; 83 | // bzero(&configure_buffers, sizeof configure_buffers); 84 | 85 | configure_buffers.count = buffer_count; 86 | configure_buffers.page_count_power = page_power; 87 | 88 | return ioctl(session->fd, HB_DRIVER_PACKET_IOC_CONFIGURE_BUFFERS, &configure_buffers); 89 | } 90 | 91 | 92 | int ha_capture_session_set_trace_enable(ha_capture_session_t session, uint8_t enabled, uint8_t reset_output) { 93 | hb_driver_packet_set_enabled set_enabled; 94 | // bzero(&set_enabled, sizeof set_enabled); 95 | 96 | set_enabled.cpu_id = session->cpu_id; 97 | set_enabled.enabled = enabled ? 1 : 0; 98 | set_enabled.reset_output = reset_output; 99 | 100 | return ioctl(session->fd, HB_DRIVER_PACKET_IOC_SET_ENABLED, &set_enabled); 101 | } 102 | 103 | int ha_capture_session_configure_tracing(ha_capture_session_t session, uint32_t pid, 104 | ha_capture_session_range_filter filters[4]) { 105 | hb_driver_packet_configure_trace configure_trace; 106 | // bzero(&configure_trace, sizeof configure_trace); 107 | 108 | configure_trace.cpu_id = session->cpu_id; 109 | configure_trace.pid = pid; 110 | 111 | for (int i = 0; i < 4; i++) { 112 | hb_driver_packet_range_filter *dst_filter = &configure_trace.filters[i]; 113 | ha_capture_session_range_filter *src_filter = &filters[i]; 114 | dst_filter->enabled = src_filter->enabled; 115 | dst_filter->start_address = src_filter->start; 116 | dst_filter->stop_address = src_filter->stop; 117 | } 118 | 119 | return ioctl(session->fd, HB_DRIVER_PACKET_IOC_CONFIGURE_TRACE, &configure_trace); 120 | } 121 | 122 | static int get_trace_buffer_lengths(ha_capture_session_t session, uint64_t *packet_byte_count, uint64_t *buffer_size) { 123 | hb_driver_packet_get_trace_lengths get_trace_lengths; 124 | // bzero(&get_trace_lengths, sizeof get_trace_lengths); 125 | 126 | get_trace_lengths.cpu_id = session->cpu_id; 127 | get_trace_lengths.trace_packet_byte_count_out = packet_byte_count; 128 | get_trace_lengths.trace_buffer_length_out = buffer_size; 129 | 130 | return ioctl(session->fd, HB_DRIVER_PACKET_IOC_GET_TRACE_LENGTHS, &get_trace_lengths); 131 | } 132 | 133 | int ha_capture_get_trace(ha_capture_session_t session, uint8_t **trace_buffer, uint64_t *trace_length) { 134 | int result; 135 | 136 | uint64_t packet_byte_count; 137 | uint64_t buffer_length; 138 | if ((result = get_trace_buffer_lengths(session, &packet_byte_count, &buffer_length)) < 0) { 139 | return result; 140 | } 141 | 142 | //We can abuse the fact that we re-use buffers by reusing the buffer 143 | if (!session->mmap_handle || session->mmap_handle == MAP_FAILED) { 144 | session->mmap_handle = mmap(NULL, buffer_length, PROT_READ | PROT_WRITE, MAP_SHARED, session->fd, 145 | /* offsets are passed in PAGE_SIZE multiples as non-aligned offsets are invalid */ 146 | getpagesize() * session->cpu_id); 147 | 148 | if (session->mmap_handle == NULL || session->mmap_handle == MAP_FAILED) { 149 | return errno; 150 | } 151 | session->mmap_size = buffer_length; 152 | } 153 | 154 | /* Terminate the buffer using our stop codon */ 155 | if (packet_byte_count >= buffer_length) { 156 | //We need to truncate the trace buffer to insert the stop codon 157 | //This isn't great, but this should really just not happen 158 | packet_byte_count = buffer_length - 1; 159 | } 160 | 161 | session->mmap_handle[packet_byte_count] = PT_TRACE_END; 162 | 163 | *trace_buffer = session->mmap_handle; 164 | *trace_length = packet_byte_count; 165 | 166 | return result; 167 | } -------------------------------------------------------------------------------- /honey_analyzer/capture/ha_capture_session.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 1/12/21. 3 | // 4 | 5 | #ifndef HA_CAPTURE_SESSION_H 6 | #define HA_CAPTURE_SESSION_H 7 | 8 | #include 9 | 10 | typedef struct ha_capture_session_internal *ha_capture_session_t; 11 | 12 | /** 13 | * Represents a single range filter 14 | */ 15 | typedef struct { 16 | /** 17 | * If this filter should be enabled. If this value is zero, start and stop are ignored 18 | */ 19 | unsigned char enabled; 20 | 21 | /** 22 | * The start (inclusive) virtual address for the filter. 23 | * Code between [start, stop) will be sent to the trace buffer. Everything else (that is not matching any other 24 | * filter) will be IGNORED. 25 | */ 26 | uint64_t start; 27 | 28 | /** 29 | * The end (exclusive) virtual address for the filter 30 | */ 31 | uint64_t stop; 32 | } ha_capture_session_range_filter; 33 | 34 | /** 35 | * Create a new capture session for the Honeybee driver 36 | * @param session_out The location to place the session if successful 37 | * @param cpu_id The CPU ID (zero indexed) that this session should manage and capture PT data on. Nothing is stopping 38 | * you from launching multiple sessions on the same CPU, it's just a really bad idea. 39 | * @return A status code. Negative on error. 40 | */ 41 | int ha_capture_session_alloc(ha_capture_session_t *session_out, uint16_t cpu_id); 42 | 43 | /** 44 | * Frees a capture session and tears down any trace buffers. 45 | */ 46 | void ha_capture_session_free(ha_capture_session_t session); 47 | 48 | /** 49 | * Set the global buffer size to be used for all CPUs. This is only allowed when no CPUs are tracing. This triggers 50 | * an immediate release of any existing buffers. 51 | * @param buffer_count The number of buffers to allocate per CPU 52 | * @param page_power This indirectly controls the buffer size by the formula (PAGE_SIZE << page_power) 53 | * @return A status code. Negative on error. 54 | */ 55 | int ha_capture_session_set_global_buffer_size(ha_capture_session_t session, uint32_t buffer_count, uint8_t page_power); 56 | 57 | /** 58 | * Starts or stops tracing on this session's CPU 59 | * @param enabled Non-zero if tracing should be enabled 60 | * @param If the trace buffer output should be reset to the start. If false and the trace has not been reconfigured 61 | * since being disabled, tracing will resume without damaging data. 62 | * @return A status code. Negative on error. 63 | */ 64 | int ha_capture_session_set_trace_enable(ha_capture_session_t session, uint8_t enabled, uint8_t reset_output); 65 | 66 | /** 67 | * Configures tracing on the CPU. This is only valid when the CPU is not tracing. 68 | * @param pid The PID to trace. This process should be bound to this session's CPU (sched_setaffinity) otherwise the 69 | * trace data will not accurately reflect what the process did. 70 | * @param filters The filters to apply. Note, not all of these filters will be applied. The kernel applies the first 71 | * n filters, where n is the number of filters this hardware supports. Put the filters you want to apply most first :) 72 | * @return A status code. Negative on error. 73 | */ 74 | int ha_capture_session_configure_tracing(ha_capture_session_t session, uint32_t pid, 75 | ha_capture_session_range_filter filters[4]); 76 | 77 | /** 78 | * Gets the trace buffer. This trace buffer has a stop codon (PT_TRACE_END) at the end of it. 79 | * Note: you do not own this buffer and it will be destroyed when this session is freed or a new trace is launched on 80 | * this core. 81 | * @param trace_buffer The location to place a pointer to the trace buffer. Nothing is written on error. 82 | * @param trace_length The location to place the length of the trace and the stop codon. Nothing is written on error. 83 | * @return A status code. Negative on error. 84 | */ 85 | int ha_capture_get_trace(ha_capture_session_t session, uint8_t **trace_buffer, uint64_t *trace_length); 86 | 87 | #endif //HA_CAPTURE_SESSION_H 88 | -------------------------------------------------------------------------------- /honey_analyzer/ha_debug_switch.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 12/30/20. 3 | // 4 | 5 | #ifndef HONEY_MIRROR_HA_DEBUG_SWITCH_H 6 | #define HONEY_MIRROR_HA_DEBUG_SWITCH_H 7 | 8 | /* 9 | * This file contains global debug logging switches 10 | */ 11 | /** Controls debug logging in ha_pt_decoder */ 12 | #define HA_ENABLE_DECODER_LOGS 0 13 | /** Controls debug logging in ha_pt_session */ 14 | #define HA_ENABLE_ANALYSIS_LOGS 0 15 | /** Controls block logging for both print blocks and unit tests. Disable this for performance tests. */ 16 | #define HA_ENABLE_BLOCK_LEVEL_LOGS 0 17 | #define HA_BLOCK_REPORTS_ARE_EDGE_TRANSITIONS 0 18 | #endif //HONEY_MIRROR_HA_DEBUG_SWITCH_H 19 | -------------------------------------------------------------------------------- /honey_analyzer/honey_analyzer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 1/13/21. 3 | // 4 | 5 | #ifndef HONEY_ANALYZER_H 6 | #define HONEY_ANALYZER_H 7 | 8 | #include "trace_analysis/ha_session.h" 9 | #include "capture/ha_capture_session.h" 10 | #include "processor_trace/ha_pt_decoder.h" 11 | #include "../honeybee_shared/hb_hive.h" 12 | 13 | #endif //HONEY_ANALYZER_H 14 | -------------------------------------------------------------------------------- /honey_analyzer/processor_trace/ha_pt_decoder.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 12/29/20. 3 | // 4 | 5 | /* 6 | NOTE: This decoder was heavily inspired by libxdc. Parts of libxdc were borrowed directly while other while others 7 | were rewritten using ideas learned from libxdc. libxdc is available under the MIT license and is available in full at 8 | https://github.com/nyx-fuzz/libxdc/blob/master/LICENSE 9 | 10 | *** 11 | 12 | Copyright (c) 2020 Sergej Schumilo, Cornelius Aschermann 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy 15 | of this software and associated documentation files (the "Software"), to deal 16 | in the Software without restriction, including without limitation the rights 17 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | copies of the Software, and to permit persons to whom the Software is 19 | furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in all 22 | copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | SOFTWARE. 31 | */ 32 | 33 | #include "ha_pt_decoder.h" 34 | #include 35 | #include 36 | #include 37 | #include "../ha_debug_switch.h" 38 | #include "ha_pt_decoder_constants.h" 39 | #if HA_ENABLE_DECODER_LOGS 40 | #define LOGGER(format, ...) (printf("[" __FILE__ "] " format, ##__VA_ARGS__)) 41 | #else 42 | #define LOGGER(format, ...) (void)0 43 | #endif 44 | #define likely(x) __builtin_expect((x),1) 45 | #define unlikely(x) __builtin_expect((x),0) 46 | 47 | 48 | 49 | static uint8_t psb[16] = { 50 | 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 51 | 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82 52 | }; 53 | 54 | #define TAG "[" __FILE__"] " 55 | 56 | ha_pt_decoder_t ha_pt_decoder_alloc(void) { 57 | return calloc(1, sizeof(ha_pt_decoder)); 58 | } 59 | 60 | void ha_pt_decoder_free(ha_pt_decoder_t decoder) { 61 | if (!decoder) { 62 | return; 63 | } 64 | 65 | free(decoder); 66 | } 67 | 68 | void ha_pt_decoder_reconfigure_with_trace(ha_pt_decoder_t decoder, uint8_t *trace_buffer, uint64_t trace_length) { 69 | /* clear all state (including our embedded cache) */ 70 | bzero(decoder, sizeof(ha_pt_decoder)); 71 | 72 | decoder->pt_buffer = trace_buffer; 73 | decoder->i_pt_buffer = trace_buffer; 74 | decoder->pt_buffer_length = trace_length; 75 | } 76 | 77 | 78 | int ha_pt_decoder_sync_forward(ha_pt_decoder_t decoder) { 79 | uint8_t *pt_end_ptr = decoder->pt_buffer + decoder->pt_buffer_length - 1; 80 | for (uint8_t *ptr_i = decoder->i_pt_buffer; ptr_i < pt_end_ptr - PT_PKT_PSB_LEN; ptr_i++) { 81 | if (memcmp(ptr_i, psb, PT_PKT_PSB_LEN) == 0) { 82 | decoder->i_pt_buffer = ptr_i; 83 | return -HA_PT_DECODER_NO_ERROR; 84 | } 85 | } 86 | 87 | return -HA_PT_DECODER_COULD_NOT_SYNC; 88 | } 89 | 90 | void ha_pt_decoder_internal_get_trace_buffer(ha_pt_decoder_t decoder, uint8_t **trace, uint64_t *trace_length) { 91 | *trace = decoder->pt_buffer; 92 | *trace_length = decoder->pt_buffer_length; 93 | } 94 | 95 | /* *** Intel PT decode *** */ 96 | 97 | /** Returns true if the TNT cache can accept more TNT packets safely. */ 98 | __attribute__((always_inline)) 99 | static inline bool is_tnt_cache_near_full(ha_pt_decoder_t decoder) { 100 | //If we have fewer than 47 slots (aka the largest LTNT) we consider ourselves full so that we don't drop anything 101 | return HA_PT_DECODER_CACHE_TNT_COUNT - ha_pt_decoder_cache_tnt_count(&decoder->cache) < 47; 102 | } 103 | 104 | __attribute__((always_inline)) 105 | static inline uint64_t get_ip_val(uint8_t **pp, uint64_t *last_ip){ 106 | register uint8_t len = (*(*pp)++ >> PT_PKT_TIP_SHIFT); 107 | if(unlikely(!len)) 108 | return 0; 109 | uint64_t aligned_pp; 110 | memcpy(&aligned_pp, *pp, sizeof(uint64_t)); 111 | 112 | *last_ip = ((int64_t)((uint64_t)( 113 | ((aligned_pp & (0xFFFFFFFFFFFFFFFF >> ((4-len)*16))) | (*last_ip & (0xFFFFFFFFFFFFFFFF << ((len)*16))) ) 114 | )<< (64 - 48))) >> (64 - 48); 115 | 116 | *pp += (len*2); 117 | 118 | return *last_ip; 119 | } 120 | 121 | __attribute__((always_inline)) 122 | static inline bool tip_handler(ha_pt_decoder_t decoder) { 123 | decoder->cache.next_indirect_branch_target = get_ip_val(&decoder->i_pt_buffer, &decoder->last_tip); 124 | LOGGER("TIP \t%p (TNT: %llu)\n", (void *)decoder->last_tip, ha_pt_decoder_cache_tnt_count(&decoder->cache)); 125 | return false; 126 | } 127 | 128 | __attribute__((always_inline)) 129 | static inline bool tip_pge_handler(ha_pt_decoder_t decoder) { 130 | uint64_t last = decoder->last_tip; 131 | uint64_t result = get_ip_val(&decoder->i_pt_buffer, &decoder->last_tip); 132 | LOGGER("PGE \t%p (TNT: %llu)\n", (void *)decoder->last_tip, ha_pt_decoder_cache_tnt_count(&decoder->cache)); 133 | //We clear OVF state on PGE because it means that we have a new starting address and so will not take the FUP. 134 | decoder->is_in_ovf_state = 0; 135 | 136 | if (likely(last != result)) { 137 | decoder->cache.override_target = result; 138 | return false; 139 | } 140 | 141 | return true; 142 | } 143 | 144 | __attribute__((always_inline)) 145 | static inline bool tip_pgd_handler(ha_pt_decoder_t decoder) { 146 | get_ip_val(&decoder->i_pt_buffer, &decoder->last_tip); 147 | LOGGER("PGD \t%p (TNT: %llu)\n", (void *)decoder->last_tip, ha_pt_decoder_cache_tnt_count(&decoder->cache)); 148 | return true; 149 | } 150 | 151 | __attribute__((always_inline)) 152 | static inline bool tip_fup_handler(ha_pt_decoder_t decoder) { 153 | // uint64_t last = decoder->last_tip; 154 | uint64_t res = get_ip_val(&decoder->i_pt_buffer, &decoder->last_tip); 155 | 156 | //FIXME: ...do FUPs not matter? Enabling them actually CAUSES issues 157 | //We need to take an FUP when we have an overflow 158 | if (unlikely(decoder->is_in_ovf_state)) { 159 | LOGGER("FUP_OVF\t%p (TNT: %llu)\n", (void *)decoder->last_tip, ha_pt_decoder_cache_tnt_count 160 | (&decoder->cache)); 161 | decoder->cache.override_target = res; 162 | return false; 163 | } 164 | 165 | LOGGER("FUP \t%p (TNT: %llu)\n", (void *)decoder->last_tip, ha_pt_decoder_cache_tnt_count(&decoder->cache)); 166 | return true; 167 | } 168 | 169 | __attribute__((always_inline)) 170 | static inline bool ovf_handler(ha_pt_decoder_t decoder) { 171 | LOGGER("OVF \t@%p\n", (void *)(decoder->i_pt_buffer - decoder->pt_buffer)); 172 | decoder->is_in_ovf_state = 1; 173 | return true; 174 | } 175 | 176 | static inline uint8_t asm_bsr(uint64_t x) { 177 | #if __APPLE__ 178 | return __builtin_clz(x) ^ 31; 179 | #else 180 | asm ("bsrq %0, %0" : "=r" (x) : "0" (x)); 181 | return x; 182 | #endif 183 | } 184 | 185 | static inline bool append_tnt_cache(ha_pt_decoder_t decoder, uint8_t data) { 186 | uint8_t bits = asm_bsr(data)-SHORT_TNT_OFFSET; 187 | for (int16_t i = bits + SHORT_TNT_OFFSET - 1; i >= SHORT_TNT_OFFSET; i--) { 188 | uint8_t b = (data >> i) & 0b1; 189 | ha_pt_decoder_cache_tnt_push_back(&decoder->cache, b); 190 | } 191 | 192 | return !is_tnt_cache_near_full(decoder); 193 | } 194 | 195 | __attribute__((always_inline)) 196 | static inline bool append_tnt_cache_ltnt(ha_pt_decoder_t decoder, uint64_t data) { 197 | uint8_t bits = asm_bsr(data)-LONG_TNT_MAX_BITS; 198 | for (int16_t i = bits + LONG_TNT_MAX_BITS - 1; i >= LONG_TNT_MAX_BITS; i--) { 199 | uint8_t b = (data >> i) & 0b1; 200 | ha_pt_decoder_cache_tnt_push_back(&decoder->cache, b); 201 | } 202 | 203 | return !is_tnt_cache_near_full(decoder); 204 | } 205 | 206 | 207 | __attribute__((hot)) 208 | int ha_pt_decoder_decode_until_caches_filled(ha_pt_decoder_t decoder) { 209 | static void* dispatch_table_level_1[] = { 210 | __extension__ &&handle_pt_pad, // 00000000 211 | __extension__ &&handle_pt_tip_pgd, // 00000001 212 | __extension__ &&handle_pt_level_2, // 00000010 213 | __extension__ &&handle_pt_cyc, // 00000011 214 | __extension__ &&handle_pt_tnt8, // 00000100 215 | __extension__ &&handle_pt_error, // 00000101 216 | __extension__ &&handle_pt_tnt8, // 00000110 217 | __extension__ &&handle_pt_cyc, // 00000111 218 | __extension__ &&handle_pt_tnt8, // 00001000 219 | __extension__ &&handle_pt_error, // 00001001 220 | __extension__ &&handle_pt_tnt8, // 00001010 221 | __extension__ &&handle_pt_cyc, // 00001011 222 | __extension__ &&handle_pt_tnt8, // 00001100 223 | __extension__ &&handle_pt_tip, // 00001101 224 | __extension__ &&handle_pt_tnt8, // 00001110 225 | __extension__ &&handle_pt_cyc, // 00001111 226 | __extension__ &&handle_pt_tnt8, // 00010000 227 | __extension__ &&handle_pt_tip_pge, // 00010001 228 | __extension__ &&handle_pt_tnt8, // 00010010 229 | __extension__ &&handle_pt_cyc, // 00010011 230 | __extension__ &&handle_pt_tnt8, // 00010100 231 | __extension__ &&handle_pt_error, // 00010101 232 | __extension__ &&handle_pt_tnt8, // 00010110 233 | __extension__ &&handle_pt_cyc, // 00010111 234 | __extension__ &&handle_pt_tnt8, // 00011000 235 | __extension__ &&handle_pt_tsc, // 00011001 236 | __extension__ &&handle_pt_tnt8, // 00011010 237 | __extension__ &&handle_pt_cyc, // 00011011 238 | __extension__ &&handle_pt_tnt8, // 00011100 239 | __extension__ &&handle_pt_tip_fup, // 00011101 240 | __extension__ &&handle_pt_tnt8, // 00011110 241 | __extension__ &&handle_pt_cyc, // 00011111 242 | __extension__ &&handle_pt_tnt8, // 00100000 243 | __extension__ &&handle_pt_tip_pgd, // 00100001 244 | __extension__ &&handle_pt_tnt8, // 00100010 245 | __extension__ &&handle_pt_cyc, // 00100011 246 | __extension__ &&handle_pt_tnt8, // 00100100 247 | __extension__ &&handle_pt_error, // 00100101 248 | __extension__ &&handle_pt_tnt8, // 00100110 249 | __extension__ &&handle_pt_cyc, // 00100111 250 | __extension__ &&handle_pt_tnt8, // 00101000 251 | __extension__ &&handle_pt_error, // 00101001 252 | __extension__ &&handle_pt_tnt8, // 00101010 253 | __extension__ &&handle_pt_cyc, // 00101011 254 | __extension__ &&handle_pt_tnt8, // 00101100 255 | __extension__ &&handle_pt_tip, // 00101101 256 | __extension__ &&handle_pt_tnt8, // 00101110 257 | __extension__ &&handle_pt_cyc, // 00101111 258 | __extension__ &&handle_pt_tnt8, // 00110000 259 | __extension__ &&handle_pt_tip_pge, // 00110001 260 | __extension__ &&handle_pt_tnt8, // 00110010 261 | __extension__ &&handle_pt_cyc, // 00110011 262 | __extension__ &&handle_pt_tnt8, // 00110100 263 | __extension__ &&handle_pt_error, // 00110101 264 | __extension__ &&handle_pt_tnt8, // 00110110 265 | __extension__ &&handle_pt_cyc, // 00110111 266 | __extension__ &&handle_pt_tnt8, // 00111000 267 | __extension__ &&handle_pt_error, // 00111001 268 | __extension__ &&handle_pt_tnt8, // 00111010 269 | __extension__ &&handle_pt_cyc, // 00111011 270 | __extension__ &&handle_pt_tnt8, // 00111100 271 | __extension__ &&handle_pt_tip_fup, // 00111101 272 | __extension__ &&handle_pt_tnt8, // 00111110 273 | __extension__ &&handle_pt_cyc, // 00111111 274 | __extension__ &&handle_pt_tnt8, // 01000000 275 | __extension__ &&handle_pt_tip_pgd, // 01000001 276 | __extension__ &&handle_pt_tnt8, // 01000010 277 | __extension__ &&handle_pt_cyc, // 01000011 278 | __extension__ &&handle_pt_tnt8, // 01000100 279 | __extension__ &&handle_pt_error, // 01000101 280 | __extension__ &&handle_pt_tnt8, // 01000110 281 | __extension__ &&handle_pt_cyc, // 01000111 282 | __extension__ &&handle_pt_tnt8, // 01001000 283 | __extension__ &&handle_pt_error, // 01001001 284 | __extension__ &&handle_pt_tnt8, // 01001010 285 | __extension__ &&handle_pt_cyc, // 01001011 286 | __extension__ &&handle_pt_tnt8, // 01001100 287 | __extension__ &&handle_pt_tip, // 01001101 288 | __extension__ &&handle_pt_tnt8, // 01001110 289 | __extension__ &&handle_pt_cyc, // 01001111 290 | __extension__ &&handle_pt_tnt8, // 01010000 291 | __extension__ &&handle_pt_tip_pge, // 01010001 292 | __extension__ &&handle_pt_tnt8, // 01010010 293 | __extension__ &&handle_pt_cyc, // 01010011 294 | __extension__ &&handle_pt_tnt8, // 01010100 295 | __extension__ &&handle_pt_exit, // 01010101 296 | __extension__ &&handle_pt_tnt8, // 01010110 297 | __extension__ &&handle_pt_cyc, // 01010111 298 | __extension__ &&handle_pt_tnt8, // 01011000 299 | __extension__ &&handle_pt_mtc, // 01011001 300 | __extension__ &&handle_pt_tnt8, // 01011010 301 | __extension__ &&handle_pt_cyc, // 01011011 302 | __extension__ &&handle_pt_tnt8, // 01011100 303 | __extension__ &&handle_pt_tip_fup, // 01011101 304 | __extension__ &&handle_pt_tnt8, // 01011110 305 | __extension__ &&handle_pt_cyc, // 01011111 306 | __extension__ &&handle_pt_tnt8, // 01100000 307 | __extension__ &&handle_pt_tip_pgd, // 01100001 308 | __extension__ &&handle_pt_tnt8, // 01100010 309 | __extension__ &&handle_pt_cyc, // 01100011 310 | __extension__ &&handle_pt_tnt8, // 01100100 311 | __extension__ &&handle_pt_error, // 01100101 312 | __extension__ &&handle_pt_tnt8, // 01100110 313 | __extension__ &&handle_pt_cyc, // 01100111 314 | __extension__ &&handle_pt_tnt8, // 01101000 315 | __extension__ &&handle_pt_error, // 01101001 316 | __extension__ &&handle_pt_tnt8, // 01101010 317 | __extension__ &&handle_pt_cyc, // 01101011 318 | __extension__ &&handle_pt_tnt8, // 01101100 319 | __extension__ &&handle_pt_tip, // 01101101 320 | __extension__ &&handle_pt_tnt8, // 01101110 321 | __extension__ &&handle_pt_cyc, // 01101111 322 | __extension__ &&handle_pt_tnt8, // 01110000 323 | __extension__ &&handle_pt_tip_pge, // 01110001 324 | __extension__ &&handle_pt_tnt8, // 01110010 325 | __extension__ &&handle_pt_cyc, // 01110011 326 | __extension__ &&handle_pt_tnt8, // 01110100 327 | __extension__ &&handle_pt_error, // 01110101 328 | __extension__ &&handle_pt_tnt8, // 01110110 329 | __extension__ &&handle_pt_cyc, // 01110111 330 | __extension__ &&handle_pt_tnt8, // 01111000 331 | __extension__ &&handle_pt_error, // 01111001 332 | __extension__ &&handle_pt_tnt8, // 01111010 333 | __extension__ &&handle_pt_cyc, // 01111011 334 | __extension__ &&handle_pt_tnt8, // 01111100 335 | __extension__ &&handle_pt_tip_fup, // 01111101 336 | __extension__ &&handle_pt_tnt8, // 01111110 337 | __extension__ &&handle_pt_cyc, // 01111111 338 | __extension__ &&handle_pt_tnt8, // 10000000 339 | __extension__ &&handle_pt_tip_pgd, // 10000001 340 | __extension__ &&handle_pt_tnt8, // 10000010 341 | __extension__ &&handle_pt_cyc, // 10000011 342 | __extension__ &&handle_pt_tnt8, // 10000100 343 | __extension__ &&handle_pt_error, // 10000101 344 | __extension__ &&handle_pt_tnt8, // 10000110 345 | __extension__ &&handle_pt_cyc, // 10000111 346 | __extension__ &&handle_pt_tnt8, // 10001000 347 | __extension__ &&handle_pt_error, // 10001001 348 | __extension__ &&handle_pt_tnt8, // 10001010 349 | __extension__ &&handle_pt_cyc, // 10001011 350 | __extension__ &&handle_pt_tnt8, // 10001100 351 | __extension__ &&handle_pt_tip, // 10001101 352 | __extension__ &&handle_pt_tnt8, // 10001110 353 | __extension__ &&handle_pt_cyc, // 10001111 354 | __extension__ &&handle_pt_tnt8, // 10010000 355 | __extension__ &&handle_pt_tip_pge, // 10010001 356 | __extension__ &&handle_pt_tnt8, // 10010010 357 | __extension__ &&handle_pt_cyc, // 10010011 358 | __extension__ &&handle_pt_tnt8, // 10010100 359 | __extension__ &&handle_pt_error, // 10010101 360 | __extension__ &&handle_pt_tnt8, // 10010110 361 | __extension__ &&handle_pt_cyc, // 10010111 362 | __extension__ &&handle_pt_tnt8, // 10011000 363 | __extension__ &&handle_pt_mode, // 10011001 364 | __extension__ &&handle_pt_tnt8, // 10011010 365 | __extension__ &&handle_pt_cyc, // 10011011 366 | __extension__ &&handle_pt_tnt8, // 10011100 367 | __extension__ &&handle_pt_tip_fup, // 10011101 368 | __extension__ &&handle_pt_tnt8, // 10011110 369 | __extension__ &&handle_pt_cyc, // 10011111 370 | __extension__ &&handle_pt_tnt8, // 10100000 371 | __extension__ &&handle_pt_tip_pgd, // 10100001 372 | __extension__ &&handle_pt_tnt8, // 10100010 373 | __extension__ &&handle_pt_cyc, // 10100011 374 | __extension__ &&handle_pt_tnt8, // 10100100 375 | __extension__ &&handle_pt_error, // 10100101 376 | __extension__ &&handle_pt_tnt8, // 10100110 377 | __extension__ &&handle_pt_cyc, // 10100111 378 | __extension__ &&handle_pt_tnt8, // 10101000 379 | __extension__ &&handle_pt_error, // 10101001 380 | __extension__ &&handle_pt_tnt8, // 10101010 381 | __extension__ &&handle_pt_cyc, // 10101011 382 | __extension__ &&handle_pt_tnt8, // 10101100 383 | __extension__ &&handle_pt_tip, // 10101101 384 | __extension__ &&handle_pt_tnt8, // 10101110 385 | __extension__ &&handle_pt_cyc, // 10101111 386 | __extension__ &&handle_pt_tnt8, // 10110000 387 | __extension__ &&handle_pt_tip_pge, // 10110001 388 | __extension__ &&handle_pt_tnt8, // 10110010 389 | __extension__ &&handle_pt_cyc, // 10110011 390 | __extension__ &&handle_pt_tnt8, // 10110100 391 | __extension__ &&handle_pt_error, // 10110101 392 | __extension__ &&handle_pt_tnt8, // 10110110 393 | __extension__ &&handle_pt_cyc, // 10110111 394 | __extension__ &&handle_pt_tnt8, // 10111000 395 | __extension__ &&handle_pt_error, // 10111001 396 | __extension__ &&handle_pt_tnt8, // 10111010 397 | __extension__ &&handle_pt_cyc, // 10111011 398 | __extension__ &&handle_pt_tnt8, // 10111100 399 | __extension__ &&handle_pt_tip_fup, // 10111101 400 | __extension__ &&handle_pt_tnt8, // 10111110 401 | __extension__ &&handle_pt_cyc, // 10111111 402 | __extension__ &&handle_pt_tnt8, // 11000000 403 | __extension__ &&handle_pt_tip_pgd, // 11000001 404 | __extension__ &&handle_pt_tnt8, // 11000010 405 | __extension__ &&handle_pt_cyc, // 11000011 406 | __extension__ &&handle_pt_tnt8, // 11000100 407 | __extension__ &&handle_pt_error, // 11000101 408 | __extension__ &&handle_pt_tnt8, // 11000110 409 | __extension__ &&handle_pt_cyc, // 11000111 410 | __extension__ &&handle_pt_tnt8, // 11001000 411 | __extension__ &&handle_pt_error, // 11001001 412 | __extension__ &&handle_pt_tnt8, // 11001010 413 | __extension__ &&handle_pt_cyc, // 11001011 414 | __extension__ &&handle_pt_tnt8, // 11001100 415 | __extension__ &&handle_pt_tip, // 11001101 416 | __extension__ &&handle_pt_tnt8, // 11001110 417 | __extension__ &&handle_pt_cyc, // 11001111 418 | __extension__ &&handle_pt_tnt8, // 11010000 419 | __extension__ &&handle_pt_tip_pge, // 11010001 420 | __extension__ &&handle_pt_tnt8, // 11010010 421 | __extension__ &&handle_pt_cyc, // 11010011 422 | __extension__ &&handle_pt_tnt8, // 11010100 423 | __extension__ &&handle_pt_error, // 11010101 424 | __extension__ &&handle_pt_tnt8, // 11010110 425 | __extension__ &&handle_pt_cyc, // 11010111 426 | __extension__ &&handle_pt_tnt8, // 11011000 427 | __extension__ &&handle_pt_error, // 11011001 428 | __extension__ &&handle_pt_tnt8, // 11011010 429 | __extension__ &&handle_pt_cyc, // 11011011 430 | __extension__ &&handle_pt_tnt8, // 11011100 431 | __extension__ &&handle_pt_tip_fup, // 11011101 432 | __extension__ &&handle_pt_tnt8, // 11011110 433 | __extension__ &&handle_pt_cyc, // 11011111 434 | __extension__ &&handle_pt_tnt8, // 11100000 435 | __extension__ &&handle_pt_tip_pgd, // 11100001 436 | __extension__ &&handle_pt_tnt8, // 11100010 437 | __extension__ &&handle_pt_cyc, // 11100011 438 | __extension__ &&handle_pt_tnt8, // 11100100 439 | __extension__ &&handle_pt_error, // 11100101 440 | __extension__ &&handle_pt_tnt8, // 11100110 441 | __extension__ &&handle_pt_cyc, // 11100111 442 | __extension__ &&handle_pt_tnt8, // 11101000 443 | __extension__ &&handle_pt_error, // 11101001 444 | __extension__ &&handle_pt_tnt8, // 11101010 445 | __extension__ &&handle_pt_cyc, // 11101011 446 | __extension__ &&handle_pt_tnt8, // 11101100 447 | __extension__ &&handle_pt_tip, // 11101101 448 | __extension__ &&handle_pt_tnt8, // 11101110 449 | __extension__ &&handle_pt_cyc, // 11101111 450 | __extension__ &&handle_pt_tnt8, // 11110000 451 | __extension__ &&handle_pt_tip_pge, // 11110001 452 | __extension__ &&handle_pt_tnt8, // 11110010 453 | __extension__ &&handle_pt_cyc, // 11110011 454 | __extension__ &&handle_pt_tnt8, // 11110100 455 | __extension__ &&handle_pt_error, // 11110101 456 | __extension__ &&handle_pt_tnt8, // 11110110 457 | __extension__ &&handle_pt_cyc, // 11110111 458 | __extension__ &&handle_pt_tnt8, // 11111000 459 | __extension__ &&handle_pt_error, // 11111001 460 | __extension__ &&handle_pt_tnt8, // 11111010 461 | __extension__ &&handle_pt_cyc, // 11111011 462 | __extension__ &&handle_pt_tnt8, // 11111100 463 | __extension__ &&handle_pt_tip_fup, // 11111101 464 | __extension__ &&handle_pt_tnt8, // 11111110 465 | __extension__ &&handle_pt_error, // 11111111 466 | }; 467 | 468 | #define DISPATCH_L1 goto *dispatch_table_level_1[decoder->i_pt_buffer[0]]; 469 | DISPATCH_L1; 470 | handle_pt_mode: 471 | decoder->i_pt_buffer += PT_PKT_MODE_LEN; 472 | LOGGER("MODE\n"); 473 | DISPATCH_L1; 474 | handle_pt_tip: 475 | if (unlikely(!tip_handler(decoder))) { 476 | return HA_PT_DECODER_NO_ERROR; 477 | } 478 | DISPATCH_L1; 479 | handle_pt_tip_pge: 480 | if (unlikely(!tip_pge_handler(decoder))) { 481 | return HA_PT_DECODER_NO_ERROR; 482 | } 483 | DISPATCH_L1; 484 | handle_pt_tip_pgd: 485 | if (unlikely(!tip_pgd_handler(decoder))) { 486 | return HA_PT_DECODER_NO_ERROR; 487 | } 488 | DISPATCH_L1; 489 | handle_pt_tip_fup: 490 | if (unlikely(!tip_fup_handler(decoder))) { 491 | return HA_PT_DECODER_NO_ERROR; 492 | } 493 | DISPATCH_L1; 494 | handle_pt_pad: 495 | while(unlikely(!(*(++decoder->i_pt_buffer)))){} 496 | DISPATCH_L1; 497 | handle_pt_tnt8: 498 | LOGGER("TNT 0x%x\n", *decoder->i_pt_buffer); 499 | bool cont = append_tnt_cache(decoder, (uint64_t)(*(decoder->i_pt_buffer))); 500 | decoder->i_pt_buffer++; 501 | if (unlikely(!cont)) { 502 | return HA_PT_DECODER_NO_ERROR; 503 | } 504 | DISPATCH_L1; 505 | handle_pt_level_2: 506 | switch(decoder->i_pt_buffer[1]){ 507 | case __extension__ 0b00000011: /* CBR */ 508 | decoder->i_pt_buffer += PT_PKT_CBR_LEN; 509 | DISPATCH_L1; 510 | 511 | case __extension__ 0b00100011: /* PSBEND */ 512 | decoder->i_pt_buffer += PT_PKT_PSBEND_LEN; 513 | LOGGER("PSBEND\n"); 514 | DISPATCH_L1; 515 | 516 | case __extension__ 0b10000010: /* PSB */ 517 | decoder->i_pt_buffer += PT_PKT_PSB_LEN; 518 | LOGGER("PSB\n"); 519 | DISPATCH_L1; 520 | 521 | case __extension__ 0b10100011: /* LTNT */ 522 | LOGGER("LTNT\n"); 523 | bool cont = append_tnt_cache_ltnt(decoder, (uint64_t)*decoder->i_pt_buffer); 524 | decoder->i_pt_buffer += PT_PKT_LTNT_LEN; 525 | if (unlikely(!cont)) { 526 | return HA_PT_DECODER_NO_ERROR; 527 | } 528 | DISPATCH_L1; 529 | 530 | case __extension__ 0b11110011: /* OVF */ 531 | ovf_handler(decoder); 532 | decoder->i_pt_buffer += PT_PKT_OVF_LEN; 533 | DISPATCH_L1; 534 | 535 | case __extension__ 0b01000011: /* PIP -- ignoring because we don't care about kernel */ 536 | case __extension__ 0b10000011: /* TS -- ignoring because I have no idea what this is */ 537 | case __extension__ 0b11001000: /* VMCS -- ignoring because VM*/ 538 | case __extension__ 0b11000011: /* MNT -- ignoring because I also don't know what this is */ 539 | case __extension__ 0b01110011: /* TMA -- ignoring because we don't support time */ 540 | default: 541 | return -HA_PT_UNSUPPORTED_TRACE_PACKET; 542 | } 543 | 544 | handle_pt_mtc: /* ignoring because we don't support time */ 545 | handle_pt_tsc: /* ignoring because we don't support time */ 546 | handle_pt_cyc: /* ignoring because we don't support time */ 547 | handle_pt_error: /* just an error */ 548 | return -HA_PT_UNSUPPORTED_TRACE_PACKET; 549 | 550 | handle_pt_exit: 551 | //We hit the stop codon 552 | return -HA_PT_DECODER_END_OF_STREAM; 553 | 554 | #undef DISPATCH_L1 555 | } -------------------------------------------------------------------------------- /honey_analyzer/processor_trace/ha_pt_decoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 12/29/20. 3 | // 4 | 5 | #ifndef HONEY_ANALYZER_HA_PT_DECODER_H 6 | #define HONEY_ANALYZER_HA_PT_DECODER_H 7 | #include 8 | #include 9 | 10 | typedef struct internal_ha_pt_decoder * ha_pt_decoder_t; 11 | 12 | typedef enum { 13 | /** No error */ 14 | HA_PT_DECODER_NO_ERROR = 0, 15 | /** The trace ended. This is not an error but rather an indication to stop */ 16 | HA_PT_DECODER_END_OF_STREAM = 1, 17 | /** There was an internal decoder error. Probably not your fault. */ 18 | HA_PT_DECODER_INTERNAL = 2, 19 | /** A sync operation failed because the target PSB could not be found. */ 20 | HA_PT_DECODER_COULD_NOT_SYNC = 3, 21 | /** 22 | * An operation was requested which could not be completed given the trace. 23 | * This can mean one of three things: 24 | * 1. The decoder is buggy 25 | * 2. The analysis is buggy 26 | * 3. The mapping between the binary and the decoder is incorrect (leading to bad analysis) 27 | */ 28 | HA_PT_DECODER_TRACE_DESYNC = 4, 29 | /** An unsupported packet was found in the PT stream. */ 30 | HA_PT_UNSUPPORTED_TRACE_PACKET = 5, 31 | /** The target address was not found in the binary map. */ 32 | HA_PT_DECODER_NO_MAP = 6, 33 | } ha_pt_decoder_status; 34 | 35 | /** The number of elements our cache struct holds. This is a power of two so we can mask instead of modulo */ 36 | #define HA_PT_DECODER_CACHE_TNT_COUNT (1LLU<<16U) 37 | #define HA_PT_DECODER_CACHE_TNT_COUNT_MASK (HA_PT_DECODER_CACHE_TNT_COUNT - 1) 38 | typedef struct { 39 | /** If an indirect branch target is available, this field is non-zero.*/ 40 | uint64_t next_indirect_branch_target; 41 | /** If an event provided a new target, it should be taken before the indirect branch as an override. */ 42 | uint64_t override_target; 43 | 44 | /* 45 | * The TNT cache uses a technique where we allow the read and write indices to overflow. Since they are signed, 46 | * this is defined. Since we define our cache size as a power of two, we can easily mask these values to get our 47 | * fitting value. We do all of this since it greatly simplifies all operations without complicated pointer 48 | * index arithmetic. 49 | */ 50 | 51 | /** The next index for the TNT */ 52 | uint64_t tnt_cache_read; 53 | /** The index to place the next TNT (i.e. there is no valid TNT here) */ 54 | uint64_t tnt_cache_write; 55 | 56 | /** 57 | * The actual TNT cache. TNT items are in a FIFO ringbuffer. 58 | */ 59 | int8_t tnt_cache[HA_PT_DECODER_CACHE_TNT_COUNT]; 60 | } ha_pt_decoder_cache; 61 | 62 | typedef struct internal_ha_pt_decoder { 63 | /** 64 | * The PT buffer. This needs to be mmaped into a larger map in which the stop codon is placed just after the last 65 | * byte of this buffer 66 | */ 67 | uint8_t *pt_buffer; 68 | 69 | /** This size of the PT buffer. This does not include the stop codon. */ 70 | uint64_t pt_buffer_length; 71 | 72 | /** The iterator pointer. This is used to "walk" the trace without destroying our handle. */ 73 | uint8_t *i_pt_buffer; 74 | 75 | /** The last TIP. This is used for understanding future TIPs since they are masks on this value. */ 76 | uint64_t last_tip; 77 | 78 | /** Do we have an unresolved OVF packet? */ 79 | uint64_t is_in_ovf_state; 80 | 81 | /* KEEP THIS LAST FOR THE SAKE OF THE CACHE */ 82 | /** The cache struct. This is exposed directly to clients. */ 83 | ha_pt_decoder_cache cache; 84 | 85 | } ha_pt_decoder; 86 | 87 | /** 88 | * Creates a new decoder from a raw Intel Processor Trace dump 89 | * @param trace_path The path to the trace file 90 | * @return The tracer session or NULL 91 | */ 92 | 93 | /** 94 | * Creates a new trace session from a R/W buffer which contains the trace data. The trace buffer must have AT LEAST 95 | * one extra byte of space after the end of the trace. This is a performance hack borrowed from libxdc. 96 | * @param session_out The location to place a pointer to the created session. On error, left unchanged. 97 | * @param hive_path The path to the Honeybee hive to use for decoding this trace. 98 | * at least one byte more than this value. 99 | * @param trace_slide The ASLR shifted base address of the binary for this trace 100 | * @return Error code. On success, zero is returned 101 | */ 102 | 103 | /** 104 | * Allocates a new decoder. The decoder does not initially have a trace installed. 105 | */ 106 | ha_pt_decoder_t ha_pt_decoder_alloc(void); 107 | 108 | /** Frees a decoder and all other owned resources. */ 109 | void ha_pt_decoder_free(ha_pt_decoder_t decoder); 110 | 111 | /** 112 | * (Re)configures the decoder with a new trace buffer. This operation clears all internal state. 113 | * @param trace_buffer The pointer to the start of the buffer containing the trace data. NOTE: This buffer is NOT 114 | * owned by the decoder--you are still responsible for freeing it (and any buffer replaced by this operation). 115 | * @param trace_length The length of the trace 116 | */ 117 | void ha_pt_decoder_reconfigure_with_trace(ha_pt_decoder_t decoder, uint8_t *trace_buffer, uint64_t trace_length); 118 | 119 | /** Sync the decoder forwards towards the first PSB. Returns -ha_pt_decoder_status on error. */ 120 | int ha_pt_decoder_sync_forward(ha_pt_decoder_t decoder); 121 | 122 | /** Runs the decode process until one of the two caches fills */ 123 | int ha_pt_decoder_decode_until_caches_filled(ha_pt_decoder_t decoder); 124 | 125 | //MARK: -- Inline exports 126 | 127 | #define unlikely(x) __builtin_expect((x),0) 128 | 129 | /* ha_pt_decoder_cache */ 130 | 131 | /** Is the TNT cache empty? */ 132 | __attribute__((always_inline)) 133 | static inline int ha_pt_decoder_cache_tnt_is_empty(ha_pt_decoder_cache *cache) { 134 | return cache->tnt_cache_read == cache->tnt_cache_write; 135 | } 136 | 137 | /** Pushes a new TNT item to the end of the ringbuffer. Does not check for capacity. */ 138 | __attribute__((always_inline)) 139 | static inline void ha_pt_decoder_cache_tnt_push_back(ha_pt_decoder_cache *cache, uint8_t tnt) { 140 | cache->tnt_cache[(cache->tnt_cache_write++) & HA_PT_DECODER_CACHE_TNT_COUNT_MASK] = tnt; 141 | } 142 | 143 | /** Pops the first TNT item from front of the ringbuffer. Does not check for availability. */ 144 | __attribute__((always_inline)) 145 | static inline uint8_t ha_pt_decoder_cache_tnt_pop(ha_pt_decoder_cache *cache) { 146 | return cache->tnt_cache[(cache->tnt_cache_read++) & HA_PT_DECODER_CACHE_TNT_COUNT_MASK]; 147 | } 148 | 149 | /** Returns the number of valid items in the ringbuffer. */ 150 | __attribute__((always_inline)) 151 | static inline uint64_t ha_pt_decoder_cache_tnt_count(ha_pt_decoder_cache *cache) { 152 | //This may trigger an overflow, but in a defined and correct way 153 | return cache->tnt_cache_write - cache->tnt_cache_read; 154 | } 155 | 156 | /* ha_pt_decoder functions -- these are defined here for inline-ability */ 157 | 158 | /** 159 | * Query the decoder for the next TNT. This function will trigger additional analysis if necessary. 160 | * @param override If there was an FUP which must be taken instead of the expected TNT, fup_override will hold 161 | * the virtual address to continue decoding at. 162 | * @return 0 if the branch was not taken. 1 if the branch was taken. 2 if there was an FUP. A return value less than 163 | * zero indicates an error. 164 | */ 165 | __attribute__((always_inline)) 166 | static inline int ha_pt_decoder_cache_query_tnt(ha_pt_decoder_t decoder, uint64_t *override) { 167 | ha_pt_decoder_cache *cache = &decoder->cache; 168 | if (unlikely(ha_pt_decoder_cache_tnt_is_empty(cache))) { 169 | int refill_result = ha_pt_decoder_decode_until_caches_filled(decoder); 170 | if (unlikely(refill_result < 0 && refill_result != -HA_PT_DECODER_END_OF_STREAM)) { 171 | return refill_result; 172 | } 173 | 174 | //We tried to refill the cache but no TNTs were returned. 175 | //This indicates that the consumer consumed data from us in the wrong order. 176 | if (unlikely(ha_pt_decoder_cache_tnt_is_empty(cache))) { 177 | if (cache->override_target) { 178 | *override = cache->override_target; 179 | cache->override_target = 0; 180 | return 2; 181 | } else { 182 | return -HA_PT_DECODER_TRACE_DESYNC; 183 | } 184 | } 185 | } 186 | 187 | return ha_pt_decoder_cache_tnt_pop(cache); 188 | } 189 | 190 | /** 191 | * Query the decoder for where to go for an indirect jump. 192 | * @param ip A pointer to where the new IP should be placed. 193 | * @return Negative on error. 0 if an indirect branch was placed. 1 if there was an override. 194 | */ 195 | __attribute__((always_inline)) 196 | static inline int ha_pt_decoder_cache_query_indirect(ha_pt_decoder_t decoder, uint64_t *ip) { 197 | ha_pt_decoder_cache *cache = &decoder->cache; 198 | REROUTE: 199 | if (cache->override_target) { 200 | *ip = cache->override_target; 201 | cache->override_target = 0; 202 | return 1; 203 | } else if (cache->next_indirect_branch_target) { 204 | *ip = cache->next_indirect_branch_target; 205 | cache->next_indirect_branch_target = 0; 206 | return 0; 207 | } else { 208 | //No answer, we need to hit the decoder 209 | int result = ha_pt_decoder_decode_until_caches_filled(decoder); 210 | if (unlikely(result < 0)) { 211 | return result; 212 | } 213 | 214 | if (unlikely(!cache->next_indirect_branch_target && !cache->override_target)) { 215 | return -HA_PT_DECODER_TRACE_DESYNC; 216 | } 217 | 218 | goto REROUTE; 219 | } 220 | } 221 | 222 | #undef unlikely 223 | #undef HA_PT_DECODER_CACHE_TNT_COUNT_MASK 224 | #endif //HONEY_ANALYZER_HA_PT_DECODER_H 225 | -------------------------------------------------------------------------------- /honey_analyzer/processor_trace/ha_pt_decoder_constants.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 1/12/21. 3 | // 4 | 5 | #ifndef HA_PT_DECODER_CONSTANTS_H 6 | #define HA_PT_DECODER_CONSTANTS_H 7 | 8 | 9 | /* This PSB begin pattern repeats 8 times, forming a 16 byte sequence */ 10 | #define PSB_BEGIN_PATTERN_0 0b01000000 11 | #define PSB_BEGIN_PATTERN_1 0b01000001 12 | 13 | /* This is a two byte sequence */ 14 | #define PSB_END_PATTERN_0 0b01000000 15 | #define PSB_END_PATTERN_1 0b11000100 16 | 17 | #define PT_TRACE_END __extension__ 0b01010101 18 | 19 | #define PT_PKT_GENERIC_LEN 2 20 | #define PT_PKT_GENERIC_BYTE0 __extension__ 0b00000010 21 | 22 | #define PT_PKT_LTNT_LEN 8 23 | #define PT_PKT_LTNT_BYTE0 PT_PKT_GENERIC_BYTE0 24 | #define PT_PKT_LTNT_BYTE1 __extension__ 0b10100011 25 | 26 | #define PT_PKT_PIP_LEN 8 27 | #define PT_PKT_PIP_BYTE0 PT_PKT_GENERIC_BYTE0 28 | #define PT_PKT_PIP_BYTE1 __extension__ 0b01000011 29 | 30 | #define PT_PKT_CBR_LEN 4 31 | #define PT_PKT_CBR_BYTE0 PT_PKT_GENERIC_BYTE0 32 | #define PT_PKT_CBR_BYTE1 __extension__ 0b00000011 33 | 34 | #define PT_PKT_OVF_LEN 2 35 | #define PT_PKT_OVF_BYTE0 PT_PKT_GENERIC_BYTE0 36 | #define PT_PKT_OVF_BYTE1 __extension__ 0b11110011 37 | 38 | #define PT_PKT_PSB_LEN 16 39 | #define PT_PKT_PSB_BYTE0 PT_PKT_GENERIC_BYTE0 40 | #define PT_PKT_PSB_BYTE1 __extension__ 0b10000010 41 | 42 | #define PT_PKT_PSBEND_LEN 2 43 | #define PT_PKT_PSBEND_BYTE0 PT_PKT_GENERIC_BYTE0 44 | #define PT_PKT_PSBEND_BYTE1 __extension__ 0b00100011 45 | 46 | #define PT_PKT_MNT_LEN 11 47 | #define PT_PKT_MNT_BYTE0 PT_PKT_GENERIC_BYTE0 48 | #define PT_PKT_MNT_BYTE1 __extension__ 0b11000011 49 | #define PT_PKT_MNT_BYTE2 __extension__ 0b10001000 50 | 51 | #define PT_PKT_TMA_LEN 7 52 | #define PT_PKT_TMA_BYTE0 PT_PKT_GENERIC_BYTE0 53 | #define PT_PKT_TMA_BYTE1 __extension__ 0b01110011 54 | 55 | #define PT_PKT_VMCS_LEN 7 56 | #define PT_PKT_VMCS_BYTE0 PT_PKT_GENERIC_BYTE0 57 | #define PT_PKT_VMCS_BYTE1 __extension__ 0b11001000 58 | 59 | #define PT_PKT_TS_LEN 2 60 | #define PT_PKT_TS_BYTE0 PT_PKT_GENERIC_BYTE0 61 | #define PT_PKT_TS_BYTE1 __extension__ 0b10000011 62 | 63 | #define PT_PKT_MODE_LEN 2 64 | #define PT_PKT_MODE_BYTE0 __extension__ 0b10011001 65 | 66 | #define PT_PKT_TIP_LEN 8 67 | #define PT_PKT_TIP_SHIFT 5 68 | #define PT_PKT_TIP_MASK __extension__ 0b00011111 69 | #define PT_PKT_TIP_BYTE0 __extension__ 0b00001101 70 | #define PT_PKT_TIP_PGE_BYTE0 __extension__ 0b00010001 71 | #define PT_PKT_TIP_PGD_BYTE0 __extension__ 0b00000001 72 | #define PT_PKT_TIP_FUP_BYTE0 __extension__ 0b00011101 73 | 74 | 75 | #define TIP_VALUE_0 (0x0<<5) 76 | #define TIP_VALUE_1 (0x1<<5) 77 | #define TIP_VALUE_2 (0x2<<5) 78 | #define TIP_VALUE_3 (0x3<<5) 79 | #define TIP_VALUE_4 (0x4<<5) 80 | #define TIP_VALUE_5 (0x5<<5) 81 | #define TIP_VALUE_6 (0x6<<5) 82 | #define TIP_VALUE_7 (0x7<<5) 83 | 84 | #define SHORT_TNT_OFFSET 1 85 | #define SHORT_TNT_MAX_BITS 8-1-SHORT_TNT_OFFSET 86 | 87 | #define LONG_TNT_OFFSET 16 88 | #define LONG_TNT_MAX_BITS 64-1-LONG_TNT_OFFSET 89 | #define BIT(x) (1ULL << (x)) 90 | 91 | #endif //HA_PT_DECODER_CONSTANTS_H 92 | -------------------------------------------------------------------------------- /honey_analyzer/trace_analysis/ha_session.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 12/23/20. 3 | // 4 | #include 5 | #include 6 | #include 7 | 8 | #include "ha_session.h" 9 | #include "ha_session_internal.h" 10 | #include "../ha_debug_switch.h" 11 | 12 | #define TAG "[" __FILE__ "] " 13 | 14 | #if HA_ENABLE_ANALYSIS_LOGS 15 | #define ANALYSIS_LOGGER(format, ...) (printf("[" __FILE__ "] " format, ##__VA_ARGS__)) 16 | #define BLOCK_LOGGER(format, ...) (printf("[" __FILE__ "] " format, ##__VA_ARGS__)) 17 | #else 18 | #define ANALYSIS_LOGGER(format, ...) (void)0 19 | #define BLOCK_LOGGER(format, ...) (void)0 20 | #endif 21 | 22 | /** 23 | * Get the lower 32 bytes 24 | */ 25 | #define LO32(x) ((uint32_t)(x)) 26 | 27 | int ha_session_alloc(ha_session_t *session_out, hb_hive *hive) { 28 | int result = 0; 29 | 30 | if (!(session_out)) { 31 | //Invalid argument 32 | result = -1; 33 | goto CLEANUP; 34 | } 35 | 36 | ha_session *session = calloc(1, sizeof(ha_session)); 37 | if (!session) { 38 | result = -2; 39 | goto CLEANUP; 40 | } 41 | 42 | session->hive = hive; 43 | 44 | session->decoder = ha_pt_decoder_alloc(); 45 | if (!session->decoder) { 46 | result = -3; 47 | goto CLEANUP; 48 | } 49 | 50 | //init complete -- clear any status code since we're okay 51 | result = 0; 52 | 53 | CLEANUP: 54 | if (result) { 55 | //We hit some error, tear everything down 56 | //_dealloc is safe and works on half generated structures 57 | ha_session_free(session); 58 | } else { 59 | //Pass back the created structure 60 | *session_out = session; 61 | } 62 | 63 | return result; 64 | } 65 | 66 | int ha_session_reconfigure_with_terminated_trace_buffer(ha_session_t session, uint8_t *trace_buffer, 67 | uint64_t trace_length, uint64_t trace_slide) { 68 | if (!(session && trace_buffer)) { 69 | return -1; 70 | } 71 | 72 | session->trace_slide = trace_slide; 73 | ha_pt_decoder_reconfigure_with_trace(session->decoder, trace_buffer, trace_length); 74 | 75 | return ha_pt_decoder_sync_forward(session->decoder); 76 | } 77 | 78 | 79 | void ha_session_free(ha_session_t session) { 80 | if (!session) { 81 | return; 82 | } 83 | 84 | if (session->decoder) { 85 | ha_pt_decoder_free(session->decoder); 86 | session->decoder = NULL; 87 | } 88 | 89 | free(session); 90 | } 91 | 92 | /** 93 | * Initiates a block level trace decode using the session's on_block_function and extra_context 94 | * @param session 95 | * @return A negative code on error. An end-of-stream error is the expected exit code. 96 | */ 97 | __attribute__ ((hot)) 98 | int64_t block_decode(ha_session_t session) { 99 | uint64_t index; 100 | uint64_t vip; 101 | uint64_t *blocks = session->hive->blocks; 102 | int64_t status; 103 | #if HA_BLOCK_REPORTS_ARE_EDGE_TRANSITIONS 104 | uint64_t last_report = 0; 105 | #endif 106 | 107 | //We need to take an indirect jump since we currently don't have a starting state 108 | goto TRACE_INIT; 109 | while (status >= 0) { 110 | #if HA_BLOCK_REPORTS_ARE_EDGE_TRANSITIONS 111 | //This is the AFL edge transition function 112 | uint64_t report = (last_report << 1) ^LO32(vip); 113 | session->on_block_function(session, session->extra_context, report); 114 | last_report = LO32(vip); 115 | #else 116 | session->on_block_function(session, session->extra_context, LO32(vip) + session->hive->uvip_slide); 117 | #endif 118 | 119 | if (LO32(index) >= session->hive->block_count) { 120 | ANALYSIS_LOGGER("\tNo map error, index = %"PRIu32", block count = %"PRIu64"\n", LO32(index), 121 | session->hive->block_count); 122 | return -HA_PT_DECODER_NO_MAP; 123 | } 124 | 125 | /* if we inline both take_conditional and take_indirect and have them both pre-fetched, we can do a branchless increment on the value we consume */ 126 | vip = blocks[2 * LO32(index) + 1]; 127 | index = blocks[2 * LO32(index)]; 128 | 129 | if (index & HB_HIVE_FLAG_IS_CONDITIONAL) { 130 | uint64_t result = ha_pt_decoder_cache_query_tnt(session->decoder, &vip); 131 | if (result == 2 /* override */) { 132 | ANALYSIS_LOGGER("\tTNT result = 2: override destination to %p\n", (void *) vip); 133 | vip -= session->trace_slide; 134 | index = hb_hive_virtual_address_to_block_index(session->hive, vip); 135 | } else if (result == 1 /* taken */) { 136 | index >>= 1; 137 | ANALYSIS_LOGGER("\tTNT result = 1: vip = %p\n", 138 | (void *) (uint64_t) (LO32(vip)) + session->hive->uvip_slide); 139 | } else if (result == 0 /* not taken */) { 140 | index >>= 33; 141 | vip >>= 32; 142 | ANALYSIS_LOGGER("\tTNT result = 0: vip = %p\n", 143 | (void *) (uint64_t) (LO32(vip)) + session->hive->uvip_slide); 144 | } 145 | } else { 146 | /* taken or direct -- cuts off the conditional flag or the zero bit if NT */ 147 | index >>= 1; 148 | } 149 | 150 | if (LO32(index) == HB_HIVE_FLAG_INDIRECT_JUMP_INDEX_VALUE) { 151 | TRACE_INIT: 152 | status = ha_pt_decoder_cache_query_indirect(session->decoder, &vip); 153 | vip -= session->trace_slide; 154 | index = hb_hive_virtual_address_to_block_index(session->hive, vip); 155 | vip -= session->hive->uvip_slide; 156 | ANALYSIS_LOGGER("\tIndirect: vip = %p\n", (void *) (uint64_t) (LO32(vip))); 157 | } 158 | } 159 | 160 | return status; 161 | } 162 | 163 | int ha_session_decode(ha_session_t session, ha_hive_on_block_function *on_block_function, void *context) { 164 | session->on_block_function = on_block_function; 165 | session->extra_context = context; 166 | 167 | return block_decode(session); 168 | } 169 | 170 | //int c = 0; 171 | static void print_trace(ha_session_t session, void *context, uint64_t unslid_ip) { 172 | BLOCK_LOGGER("%p\n", (void *) unslid_ip); 173 | // if (c == 5) { 174 | // printf("STOP!\n"); 175 | // } 176 | // c++; 177 | } 178 | 179 | int ha_session_print_trace(ha_session_t session) { 180 | BLOCK_LOGGER(TAG "--BEGIN TRACE DECODE--\n"); 181 | return ha_session_decode(session, print_trace, NULL); 182 | } 183 | -------------------------------------------------------------------------------- /honey_analyzer/trace_analysis/ha_session.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 12/23/20. 3 | // 4 | 5 | #ifndef HONEY_ANALYSIS_HA_SESSION_H 6 | #define HONEY_ANALYSIS_HA_SESSION_H 7 | 8 | #include 9 | #include 10 | #include "../../honeybee_shared/hb_hive.h" 11 | 12 | typedef struct internal_ha_session *ha_session_t; 13 | 14 | /** 15 | * A function which the block decoder will call whenever it encounters a block 16 | */ 17 | typedef void (ha_hive_on_block_function)(ha_session_t session, void *context, uint64_t unslid_ip); 18 | 19 | /** 20 | * Create a new trace session from a trace file 21 | * @param session_out The location to place a pointer to the created session. On error, left unchanged. 22 | * @param hive The Honeybee hive to use for decoding this trace. 23 | * @return Error code. On success, zero is returned 24 | */ 25 | int ha_session_alloc(ha_session_t *session_out, hb_hive *hive); 26 | 27 | 28 | /** 29 | * (Re)configures the session with a new trace. This is a zero allocation operation. 30 | * @param trace_buffer The pointer to the start of the buffer containing the trace data. NOTE: This pointer is NOT 31 | * owned by the session, you are responsible for destroying it. 32 | * @param trace_length The length of the trace (read: NOT THE BUFFER ITSELF). The actual length of the buffer must be 33 | * @param trace_slide The base address of the binary for this trace in memory 34 | * @return Error code. On success, zero is returned 35 | */ 36 | int ha_session_reconfigure_with_terminated_trace_buffer(ha_session_t session, uint8_t *trace_buffer, 37 | uint64_t trace_length, uint64_t trace_slide); 38 | 39 | /** 40 | * Frees a session and all of its owned components 41 | * @param session 42 | */ 43 | void ha_session_free(ha_session_t session); 44 | 45 | /** 46 | * Decodes a trace and calls a function on each block. 47 | * @param on_block_function A callback function which will be invoked for each block 48 | * @param context An arbitrary pointer which will be passed to the on_block_function 49 | * @return A negative code on error. An end-of-stream error is the expected exit code. 50 | */ 51 | int ha_session_decode(ha_session_t, ha_hive_on_block_function *on_block_function, void *context); 52 | 53 | /** 54 | * Debug function. Walks the trace and dumps to the console. 55 | * @return A negative code on error. An end-of-stream error is the expected exit code. 56 | */ 57 | int ha_session_print_trace(ha_session_t session); 58 | 59 | 60 | #endif //HONEY_ANALYSIS_HA_SESSION_H 61 | -------------------------------------------------------------------------------- /honey_analyzer/trace_analysis/ha_session_internal.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 12/30/20. 3 | // 4 | 5 | #ifndef HONEY_ANALYZER_HA_SESSION_INTERNAL_H 6 | #define HONEY_ANALYZER_HA_SESSION_INTERNAL_H 7 | 8 | #include "../processor_trace/ha_pt_decoder.h" 9 | #include "../../honeybee_shared/hb_hive.h" 10 | 11 | /** 12 | * This is the internal representation of an ha_session. This is exposed in a separate header for custom loggers. If 13 | * you are consuming an ha_session_t you should not use this. 14 | */ 15 | typedef struct internal_ha_session { 16 | /** 17 | * The session's decoder 18 | */ 19 | ha_pt_decoder_t decoder; 20 | 21 | /** 22 | * The hive to use for decoding traces 23 | */ 24 | hb_hive *hive; 25 | 26 | /** 27 | * The (for now) global slide. This is the base address of the traced binary during the current trace 28 | */ 29 | uint64_t trace_slide; 30 | 31 | /** 32 | * The function called by this session when a block is decoded 33 | */ 34 | ha_hive_on_block_function *on_block_function; 35 | 36 | /** 37 | * An addition field which custom decoders can use 38 | */ 39 | void *extra_context; 40 | } ha_session; 41 | 42 | #endif //HONEY_ANALYZER_HA_SESSION_INTERNAL_H 43 | -------------------------------------------------------------------------------- /honey_coverage/hc_tree_set.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 2/28/21. 3 | // 4 | 5 | #include 6 | #include "hc_tree_set.h" 7 | 8 | typedef struct node { 9 | hc_tree_set_hash_type hash; 10 | void *element; 11 | struct node *left; 12 | struct node *right; 13 | } node; 14 | 15 | typedef struct internal_hc_tree_set { 16 | hc_tree_set_hash_fcn *hash_fcn; 17 | hc_tree_set_is_equal *equals_fcn; 18 | 19 | unsigned long long count; 20 | node *root; 21 | } hc_tree_set; 22 | 23 | typedef int (internal_iterate_nodes_fcn)(node *n, void *context); 24 | 25 | hc_tree_set_t hc_tree_set_alloc(hc_tree_set_hash_fcn *hash_fcn, hc_tree_set_is_equal *equals_fcn) { 26 | hc_tree_set_t tree_set = calloc(1, sizeof(hc_tree_set)); 27 | if (!tree_set) { 28 | return NULL; 29 | } 30 | 31 | tree_set->equals_fcn = equals_fcn; 32 | tree_set->hash_fcn = hash_fcn; 33 | 34 | return tree_set; 35 | } 36 | 37 | static int internal_iterate_all_nodes(hc_tree_set_t tree_set, internal_iterate_nodes_fcn iterator, void *context) { 38 | /* since the tree has an unbounded size, we use a pseudo stack which we re-alloc as we go */ 39 | int result = -1; 40 | unsigned long long tree_stack_element_count = 1 << 12; 41 | node **tree_stack = calloc(sizeof(node *), tree_stack_element_count); 42 | if (!tree_stack) { 43 | goto CLEANUP; 44 | } 45 | 46 | unsigned long long tree_stack_count = 1; 47 | tree_stack[0] = tree_set->root; 48 | while (tree_stack_count > 0) { 49 | //Since we use this function for destroying, we need to grab all references we need before calling the fcn 50 | node *n = tree_stack[--tree_stack_count]; 51 | 52 | //Make sure we have space to push the two children 53 | if (tree_stack_count + 2 >= tree_stack_element_count) { 54 | node **new_tree_stack = realloc(tree_stack, sizeof(node *) * tree_stack_element_count * 2); 55 | if (!new_tree_stack) { 56 | goto CLEANUP; 57 | } 58 | tree_stack = new_tree_stack; 59 | tree_stack_element_count *= 2; 60 | } 61 | 62 | if (n->left) { 63 | tree_stack[tree_stack_count++] = n->left; 64 | } 65 | 66 | if (n->right) { 67 | tree_stack[tree_stack_count++] = n->right; 68 | } 69 | 70 | /* we've stashed all information from the node, we can now safely hand it to the iterator */ 71 | if (iterator(n, context)) { 72 | //Iterator requested stop 73 | goto CLEANUP; 74 | } 75 | } 76 | 77 | result = 0; 78 | 79 | CLEANUP: 80 | free(tree_stack); 81 | return result; 82 | } 83 | 84 | static int node_free(node *n, void *context) { 85 | free(n); 86 | return 0; 87 | } 88 | 89 | void hc_tree_set_free(hc_tree_set_t tree_set) { 90 | internal_iterate_all_nodes(tree_set, node_free, NULL); 91 | } 92 | 93 | unsigned long long hc_tree_set_count(hc_tree_set_t tree_set) { 94 | return tree_set->count; 95 | } 96 | 97 | int hc_tree_set_insert(hc_tree_set_t tree_set, void *element) { 98 | hc_tree_set_hash_type element_hash = tree_set->hash_fcn(element); 99 | node **candidate_ptr = &tree_set->root; 100 | 101 | while (*candidate_ptr != NULL) { 102 | node *candidate = *candidate_ptr; 103 | if (candidate->hash == element_hash 104 | && tree_set->equals_fcn(element, candidate->element)) { 105 | //Element exists -> nothing to insert 106 | return 0; 107 | } else if (element_hash < candidate->hash) { 108 | candidate_ptr = &candidate->left; 109 | } else { 110 | //Greater AND collision elements go to the right!! 111 | candidate_ptr = &candidate->right; 112 | } 113 | } 114 | 115 | //If we got here, we've reached a case where we should insert 116 | node *element_node = calloc(1, sizeof(node)); 117 | if (!element_node) { 118 | return -1; 119 | } 120 | 121 | element_node->element = element; 122 | element_node->hash = element_hash; 123 | 124 | *candidate_ptr = element_node; 125 | 126 | tree_set->count++; 127 | 128 | return 1; 129 | } 130 | 131 | int hc_tree_set_contains(hc_tree_set_t tree_set, void *element) { 132 | node *candidate = tree_set->root; 133 | hc_tree_set_hash_type element_hash = tree_set->hash_fcn(element); 134 | while (candidate) { 135 | if (element_hash == candidate->hash && tree_set->equals_fcn(element, candidate->element)) { 136 | return 1; 137 | } else if (element_hash < candidate->hash) { 138 | candidate = candidate->left; 139 | } else { 140 | //Greater OR hash collision 141 | candidate = candidate->right; 142 | } 143 | } 144 | 145 | return 0; 146 | } 147 | 148 | struct internal_node_extract_value_context { 149 | hc_tree_iterator_fcn *iterator_fcn; 150 | void *user_context; 151 | }; 152 | 153 | static int internal_node_extract_value_iterator(node *n, void *context) { 154 | struct internal_node_extract_value_context *ctx_struct = context; 155 | return ctx_struct->iterator_fcn(n->element, ctx_struct->user_context);; 156 | } 157 | 158 | int hc_tree_set_iterate_all(hc_tree_set_t tree_set, hc_tree_iterator_fcn iterator_fcn, void *context) { 159 | struct internal_node_extract_value_context ctx_struct = { 160 | .iterator_fcn = iterator_fcn, 161 | .user_context = context, 162 | }; 163 | 164 | return internal_iterate_all_nodes(tree_set, internal_node_extract_value_iterator, &ctx_struct); 165 | } -------------------------------------------------------------------------------- /honey_coverage/hc_tree_set.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 2/28/21. 3 | // 4 | 5 | #ifndef HONEY_COVERAGE_HC_TREE_SET_H 6 | #define HONEY_COVERAGE_HC_TREE_SET_H 7 | 8 | typedef unsigned int hc_tree_set_hash_type; 9 | 10 | /** 11 | * A tree hash function takes in one node and returns that values' hash 12 | */ 13 | typedef hc_tree_set_hash_type (hc_tree_set_hash_fcn)(void *value); 14 | 15 | /** 16 | * A tree iterator function gets passed elements one-by-one until the iterator function returns non-zero or 17 | * there are no more elements in the set 18 | */ 19 | typedef int (hc_tree_iterator_fcn)(void *value, void *context); 20 | 21 | /** 22 | * A tree equality function returns 0 if the two objects are equal for the purpose of lookups 23 | */ 24 | typedef int (hc_tree_set_is_equal)(void *a, void *b); 25 | 26 | typedef struct internal_hc_tree_set *hc_tree_set_t; 27 | 28 | /** 29 | * Creates a new tree set 30 | * @param hash_fcn The function which generates hashes (which are used as tree values). Note that these values should 31 | * be as close to random as possible for performance 32 | * @param equals_fcn A function which returns 0 iff the two values are logically identical 33 | * @return A tree set, if it could be created, else NULL. 34 | */ 35 | hc_tree_set_t hc_tree_set_alloc(hc_tree_set_hash_fcn *hash_fcn, hc_tree_set_is_equal *equals_fcn); 36 | 37 | /** 38 | * Frees a tree set 39 | */ 40 | void hc_tree_set_free(hc_tree_set_t tree_set); 41 | 42 | /** 43 | * Returns the number of elements in the set, O(1) 44 | */ 45 | unsigned long long hc_tree_set_count(hc_tree_set_t tree_set); 46 | 47 | /** 48 | * Inserts an element into the set 49 | * Average O(log n) 50 | * @param tree_set The set to insert into 51 | * @param element THe element to insert 52 | * @return 1 if inserted, 0 if already exists, negative on insert error 53 | */ 54 | int hc_tree_set_insert(hc_tree_set_t tree_set, void *element); 55 | 56 | /** 57 | * Checks if the set contains an elements 58 | * Average O(log n) 59 | * @return Non-zero if contained, zero if not contained 60 | */ 61 | int hc_tree_set_contains(hc_tree_set_t tree_set, void *element); 62 | 63 | /** 64 | * Iterates all elements in the tree or until the iterator function returns non-zero (whichever happens first) 65 | * @return Non-zero on iteration error 66 | */ 67 | int hc_tree_set_iterate_all(hc_tree_set_t tree_set, hc_tree_iterator_fcn iterator_fcn, void *context); 68 | 69 | #endif //HONEY_COVERAGE_HC_TREE_SET_H 70 | -------------------------------------------------------------------------------- /honey_coverage/main.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 2/27/21. 3 | // 4 | 5 | #define _GNU_SOURCE 6 | #define _POSIX_SOURCE 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "../honey_analyzer/honey_analyzer.h" 24 | #include "hc_tree_set.h" 25 | 26 | #define TAG "[main] " 27 | #define TAGE "[!] " TAG 28 | #define TAGI "[*] " TAG 29 | 30 | typedef struct { 31 | uint64_t hive_slide; 32 | uint64_t last_block; 33 | hc_tree_set_t edge_set; 34 | hc_tree_set_t block_set; 35 | } hc_coverage_info; 36 | 37 | static void coverage_block_reported(ha_session_t session, void *context, uint64_t unslid_ip); 38 | static hc_tree_set_hash_type hc_uint64_t_hash(void *untyped_block); 39 | 40 | static int hc_uint64_t_equals(void *untyped_block_a, void *untyped_block_b); 41 | 42 | static int hc_tree_iterate_print(void *element, void *context); 43 | 44 | long current_clock() { 45 | struct timespec tv; 46 | clock_gettime(CLOCK_MONOTONIC_RAW, &tv); 47 | 48 | return tv.tv_sec * 1e9 + tv.tv_nsec; 49 | } 50 | 51 | pid_t spawn_suspended(const char *path, char *const *argv) { 52 | pid_t pid = fork(); 53 | // https://knight.sc/malware/2019/01/06/creating-suspended-processes.html 54 | if (pid == 0) { 55 | /* child */ 56 | ptrace(PTRACE_TRACEME, 0, 0, 0); 57 | //disable ASLR for child 58 | int old, rc; 59 | old = personality(0xffffffff); /* Fetch old personality. */ 60 | rc = personality(old | ADDR_NO_RANDOMIZE); 61 | if (-1 == rc) { 62 | perror("personality"); 63 | } 64 | 65 | //Since we do not use PTRACE_O_TRACEEXEC, this will trigger a SIGTRAP on success 66 | execv(path, argv); 67 | } else { 68 | /* parent */ 69 | int status; 70 | waitpid(pid, &status, 0); 71 | } 72 | 73 | return pid; 74 | } 75 | 76 | void suspend_process(pid_t pid) { 77 | ptrace(PTRACE_INTERRUPT, pid, (caddr_t) 1, 0); 78 | } 79 | 80 | void unsuspended_process(pid_t pid) { 81 | ptrace(PTRACE_CONT, pid, (caddr_t) 1, 0); 82 | } 83 | 84 | void pin_process_to_cpu(pid_t pid, int cpu) { 85 | cpu_set_t mask; 86 | CPU_ZERO(&mask); 87 | CPU_SET(cpu, &mask); 88 | if (sched_setaffinity(pid, sizeof mask, &mask)) { 89 | perror("Couldn't pin to CPU"); 90 | } 91 | } 92 | 93 | int main(int argc, const char * argv[]) { 94 | //XXX: Replace this with command line args 95 | if (argc < 7 96 | || strncmp(argv[4], "--", 2) != 0) { 97 | printf("Usage: %s -- [target args]\n" 98 | "This program outputs a set of all basic blocks and edge hashes visited in the following format:\n" 99 | "\n" 100 | "\n" 101 | "......\n" 102 | "......\n", 103 | argv[0]); 104 | return 1; 105 | } 106 | 107 | const char *hive_path = argv[1]; 108 | uint64_t filter_start_address = strtoull(argv[2], NULL, 0); 109 | uint64_t filter_stop_address = strtoull(argv[3], NULL, 0); 110 | const char *target_binary = argv[5]; 111 | const char **target_args = argv + 5; /* we need to include the target binary in target argv */ 112 | 113 | int result = 1; 114 | hc_coverage_info *coverage_info = NULL; 115 | ha_session_t session = NULL; 116 | hb_hive *hive = NULL; 117 | ha_capture_session_t capture_session = NULL; 118 | 119 | if (!(coverage_info = calloc(1, sizeof(hc_coverage_info))) 120 | || !(coverage_info->block_set = hc_tree_set_alloc(hc_uint64_t_hash, hc_uint64_t_equals)) 121 | || !(coverage_info->edge_set = hc_tree_set_alloc(hc_uint64_t_hash, hc_uint64_t_equals))) { 122 | goto CLEANUP; 123 | } 124 | 125 | if (!(hive = hb_hive_alloc(hive_path))) { 126 | printf(TAGE "Could not open hive at path %s\n", hive_path); 127 | goto CLEANUP; 128 | } 129 | 130 | coverage_info->hive_slide = hive->uvip_slide; 131 | 132 | if ((result = ha_session_alloc(&session, hive)) < 0) { 133 | printf(TAGE "Could not allocate analysis session, error = %d\n", result); 134 | goto CLEANUP; 135 | } 136 | 137 | if ((result = ha_capture_session_alloc(&capture_session, 0)) < 0) { 138 | printf(TAGE "Failed to start capture session on CPU 0, error = %d\n", result); 139 | goto CLEANUP; 140 | } 141 | 142 | if ((result = ha_capture_session_set_global_buffer_size(capture_session, 400, 5)) < 0) { 143 | printf(TAGE "Failed to configure trace buffers on CPU 0, error = %d\n", result); 144 | goto CLEANUP; 145 | } 146 | 147 | pid_t pid = spawn_suspended(target_binary, (char *const *) target_args); 148 | pin_process_to_cpu(pid, 0); 149 | // printf(TAGI "Spawned process %d\n", pid); 150 | 151 | ha_capture_session_range_filter filters[4]; 152 | bzero(&filters, sizeof(ha_capture_session_range_filter) * 4); 153 | filters[0].enabled = 1; 154 | filters[0].start = filter_start_address; 155 | filters[0].stop = filter_stop_address; 156 | if ((result = ha_capture_session_configure_tracing(capture_session, pid, filters)) < 0) { 157 | printf(TAGE "Failed to configure tracing on CPU 0, error = %d\n", result); 158 | goto CLEANUP; 159 | } 160 | 161 | if ((result = ha_capture_session_set_trace_enable(capture_session, 0x1, 0x1)) < 0) { 162 | printf(TAGE "Failed to start tracing CPU 0, error = %d\n", result); 163 | goto CLEANUP; 164 | } 165 | 166 | unsuspended_process(pid); 167 | 168 | if ((result = waitpid(pid, &result, 0) < 0)) { 169 | printf(TAGE "Failed to wait for process, error = %d\n", result); 170 | goto CLEANUP; 171 | } 172 | 173 | if ((result = ha_capture_session_set_trace_enable(capture_session, 0x0, 0x0)) < 0) { 174 | printf(TAGE "Failed to stop tracing CPU 0, error = %d\n", result); 175 | goto CLEANUP; 176 | } 177 | 178 | uint8_t *terminated_buffer = NULL; 179 | uint64_t buffer_length = 0; 180 | if ((result = ha_capture_get_trace(capture_session, &terminated_buffer, &buffer_length)) < 0) { 181 | printf(TAGE "Failed to get trace buffer, error = %d\n", result); 182 | goto CLEANUP; 183 | } 184 | 185 | if ((result = ha_session_reconfigure_with_terminated_trace_buffer(session, 186 | terminated_buffer, 187 | buffer_length, 188 | filter_start_address)) < 0) { 189 | printf(TAGE "Failed to reconfigure session, error = %d\n", result); 190 | goto CLEANUP; 191 | } 192 | 193 | 194 | if ((result = ha_session_decode(session, coverage_block_reported, coverage_info)) < 0 195 | && result != -HA_PT_DECODER_END_OF_STREAM) { 196 | printf(TAGE "Decoder error encountered, error = %d\n", result); 197 | goto CLEANUP; 198 | } 199 | 200 | /* 201 | * output format: 202 | * block set count 203 | * edge set count 204 | * [[block set elements]] 205 | * [[edge set elements ]] 206 | */ 207 | 208 | printf("%llu\n%llu\n", 209 | hc_tree_set_count(coverage_info->block_set), 210 | hc_tree_set_count(coverage_info->edge_set)); 211 | 212 | if ((result = hc_tree_set_iterate_all(coverage_info->block_set, hc_tree_iterate_print, 213 | (void *) hive->uvip_slide)) < 0) { 214 | printf(TAGE "Couldn't iterate block set"); 215 | goto CLEANUP; 216 | } 217 | 218 | if ((result = hc_tree_set_iterate_all(coverage_info->edge_set, hc_tree_iterate_print, 0)) < 0) { 219 | printf(TAGE "Couldn't iterate edge set"); 220 | goto CLEANUP; 221 | } 222 | 223 | result = 0; 224 | 225 | CLEANUP: 226 | if (coverage_info) { 227 | if (coverage_info->block_set) { 228 | hc_tree_set_free(coverage_info->block_set); 229 | } 230 | 231 | if (coverage_info->edge_set) { 232 | hc_tree_set_free(coverage_info->edge_set); 233 | } 234 | 235 | free(coverage_info); 236 | } 237 | 238 | if (session) { 239 | ha_session_free(session); 240 | } 241 | 242 | if (capture_session) { 243 | ha_capture_session_free(capture_session); 244 | } 245 | 246 | return result; 247 | } 248 | 249 | void coverage_block_reported(ha_session_t session, void *context, uint64_t unslid_ip) { 250 | hc_coverage_info *coverage_info = context; 251 | int inserted; 252 | uint64_t slid_ip = unslid_ip - coverage_info->hive_slide; 253 | //This is kinda sketchy, but since a current limitation is that we can't work over more than 4GB binaries 254 | //due to hive maps, this is technically safe since we are unsliding them 255 | uint64_t edge = coverage_info->last_block << 32 | slid_ip; 256 | coverage_info->last_block = slid_ip; 257 | 258 | inserted = hc_tree_set_insert(coverage_info->edge_set, (void *)edge); 259 | if (inserted == 0) { 260 | //Since the edge already exists, it was not inserted. Additionally, if the edge already exists, we know 261 | //the block already exists and so we don't need to insert it 262 | return; 263 | } else if (inserted < 0) { 264 | printf(TAGE "Unable to insert edge!"); 265 | abort(); 266 | } 267 | 268 | inserted = hc_tree_set_insert(coverage_info->block_set, (void *)slid_ip); 269 | if (inserted < 0) { 270 | printf(TAGE "Unable to insert block!"); 271 | abort(); 272 | } 273 | } 274 | 275 | static inline unsigned int mix_hash(unsigned int x) { 276 | x = ((x >> 16) ^ x) * 0x45d9f3b; 277 | x = ((x >> 16) ^ x) * 0x45d9f3b; 278 | x = (x >> 16) ^ x; 279 | return x; 280 | } 281 | 282 | static hc_tree_set_hash_type hc_uint64_t_hash(void *untyped_block) { 283 | uint64_t block = (uint64_t)untyped_block; 284 | return mix_hash(block); 285 | } 286 | 287 | static int hc_uint64_t_equals(void *untyped_block_a, void *untyped_block_b) { 288 | uint64_t a = (uint64_t)untyped_block_a; 289 | uint64_t b = (uint64_t)untyped_block_b; 290 | 291 | return a == b; 292 | } 293 | 294 | static int hc_tree_iterate_print(void *element, void *context) { 295 | uint64_t slide = (uint64_t) context; 296 | printf("%"PRIu64"\n", ((uint64_t) element) + slide); 297 | return 0; 298 | } -------------------------------------------------------------------------------- /honey_driver/Makefile: -------------------------------------------------------------------------------- 1 | KDIR = /lib/modules/`uname -r`/build 2 | obj-m += honey_driver.o 3 | M := make -C ${KDIR} M=`pwd` 4 | 5 | all: 6 | ${M} modules 7 | clean: 8 | ${M} clean 9 | build_install: 10 | `rmmod honey_driver` || echo "Unload failed -- not loaded?" 11 | ${M} modules 12 | ${M} modules_install 13 | `insmod honey_driver.ko` 14 | ${M} clean 15 | unload: 16 | `rmmod honey_driver` || echo "Unload failed -- not loaded?" -------------------------------------------------------------------------------- /honey_driver/honey_driver.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 1/9/21. 3 | // 4 | 5 | /* 6 | * This driver was built using small bits of code and ideas borrowed from simple-pt. It does, however, have 7 | * significant domain specific changes in order to support fuzzing under Honeybee as well as an entirely different 8 | * control mechanism (we use pure ioctls instead of simple-pt's array of pseudo-files). It's important to note, 9 | * however, that this driver is NOT intended for high-risk, security critical deployments. It should ONLY be used on 10 | * otherwise secure machines for fuzzing. While an effort has been made to put guard rails on parts of this code, 11 | * this was done primarily for system stability rather than to protect against malicious software. You should 12 | * probably unload this module after use. 13 | * 14 | * This file, and all of its modifications, is licensed under the same terms as simple-pt. The original source from 15 | * which this driver is partially derived from is online at: 16 | * https://github.com/andikleen/simple-pt/blob/master/simple-pt.c 17 | * 18 | * The original license for simple-pt is reproduced below: 19 | * 20 | * Copyright (c) 2015, Intel Corporation 21 | * Author: Andi Kleen 22 | * All rights reserved. 23 | * 24 | * Redistribution and use in source and binary forms, with or without 25 | * modification, are permitted provided that the following conditions are met: 26 | * 27 | * 1. Redistributions of source code must retain the above copyright notice, 28 | * this list of conditions and the following disclaimer. 29 | * 30 | * 2. Redistributions in binary form must reproduce the above copyright 31 | * notice, this list of conditions and the following disclaimer in the 32 | * documentation and/or other materials provided with the distribution. 33 | * 34 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 35 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 36 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 37 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 38 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 39 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 40 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 41 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 42 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 43 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 44 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 45 | * OF THE POSSIBILITY OF SUCH DAMAGE. 46 | * 47 | * Alternatively you can use this file under the GPLv2. 48 | */ 49 | 50 | 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include 73 | 74 | #include "../honeybee_shared/hb_driver_packets.h" 75 | #include "honey_driver_constants.h" 76 | 77 | #define ENABLE_LOGGING 0 78 | 79 | #if ENABLE_LOGGING 80 | #define LOGGER(format, ...) (printf("[" __FILE__ "] " format, ##__VA_ARGS__)) 81 | #else 82 | #define LOGGER(format, ...) (void)0 83 | #endif 84 | 85 | #define TAG "[honey_driver] " 86 | 87 | #define TAGI KERN_INFO TAG "[*] " 88 | #define TAGE KERN_ERR TAG "[!] " 89 | 90 | /* 91 | * Globals 92 | */ 93 | 94 | /** 95 | * Mutex to protect global configuration. This is taken at the start of all ioctls and mmap operations to protect 96 | * our state. 97 | */ 98 | static DEFINE_MUTEX(configuration_mutex); 99 | 100 | /** 101 | * The number of ToPA entries to allocate per CPU 102 | */ 103 | static uint32_t topa_buffer_count; 104 | 105 | /** 106 | * The number of pages, as a power of 2, to allocate 107 | */ 108 | static uint8_t topa_page_order; 109 | 110 | /** 111 | * The number of supported address ranges. This is configure by a call to intel_pt_hardware_support_preflight 112 | */ 113 | static uint8_t address_range_filter_count; 114 | 115 | /** 116 | * Virtual address of the ToPA table for this CPU 117 | */ 118 | static DEFINE_PER_CPU(uint64_t 119 | *, topa_cpu); 120 | 121 | /** 122 | * The trace status of a given core 123 | */ 124 | static DEFINE_PER_CPU(enum HB_DRIVER_TRACE_STATUS, trace_state); 125 | 126 | /* 127 | * Utilities 128 | */ 129 | 130 | // https://carteryagemann.com/pid-to-cr3.html 131 | static uint64_t pid_to_cr3(int const pid) { 132 | unsigned long cr3_phys = 0; 133 | rcu_read_lock(); 134 | { 135 | struct pid *pidp = find_vpid(pid); 136 | struct task_struct *task; 137 | struct mm_struct *mm; 138 | 139 | if (!pidp) 140 | goto out; 141 | task = pid_task(pidp, PIDTYPE_PID); 142 | if (task == NULL) 143 | goto out; // pid has no task_struct 144 | mm = task->mm; 145 | 146 | // mm can be NULL in some rare cases (e.g. kthreads) 147 | // when this happens, we should check active_mm 148 | if (mm == NULL) { 149 | mm = task->active_mm; 150 | if (mm == NULL) 151 | goto out; // this shouldn't happen, but just in case 152 | } 153 | 154 | cr3_phys = virt_to_phys((void *) mm->pgd); 155 | } 156 | out: 157 | rcu_read_unlock(); 158 | return cr3_phys; 159 | } 160 | 161 | /** 162 | * Checks a given CPU's state and returns a suitable return code for that state 163 | * @return Negative if the CPU is in an error state. 164 | */ 165 | static inline int cpu_state_to_result(int cpu) { 166 | enum HB_DRIVER_TRACE_STATUS code = per_cpu(trace_state, cpu); 167 | if (code > 0x2 /* is an error code */) { 168 | return -(1LLU << 11 | code); 169 | } 170 | 171 | return 0; 172 | } 173 | 174 | /** 175 | * Checks if a CPU's trace buffer can be accessed safely 176 | * @return Negative if not allowed 177 | */ 178 | static inline int preflight_trace_buffer_userspace_access(int cpu) { 179 | if (per_cpu(trace_state, cpu) != HB_DRIVER_TRACE_STATUS_IDLE) { 180 | //You can't get the buffer if tracing is in progress or we haven't traced 181 | return -EBUSY; 182 | } 183 | 184 | //You can't get the buffer if it hasn't been allocated 185 | if (per_cpu(topa_cpu, cpu) == 0x0) { 186 | return -ENOSPC; 187 | } 188 | 189 | return 0; 190 | } 191 | 192 | /* 193 | * Implementation 194 | */ 195 | 196 | /** 197 | * Runs a function on a given 198 | * @param cpu The CPU ID to run on 199 | * @param func The function to return 200 | * @param arg The value to pass to the function 201 | * @return A status code, negative if error 202 | */ 203 | static int smp_do_on_cpu(int cpu, smp_call_func_t func, void *arg) { 204 | cpumask_var_t mask; 205 | 206 | if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) { 207 | return -ENOMEM; 208 | } 209 | 210 | cpumask_set_cpu(cpu, (struct cpumask *) mask); 211 | 212 | on_each_cpu_mask((struct cpumask *) mask, func, arg, true); 213 | 214 | free_cpumask_var(mask); 215 | 216 | return 0; 217 | } 218 | 219 | /** 220 | * Installs the CPU's ToPA values into its MSRs. This must be called through SMP 221 | */ 222 | static void configure_topa_msrs_on_this_cpu(void *unused) { 223 | int status; 224 | uint64_t output_base; 225 | uint64_t ctl; 226 | 227 | status = HB_DRIVER_TRACE_STATUS_CONFIGURATION_WRITE_ERROR; 228 | 229 | /* Stop PT if it's running */ 230 | if (rdmsrl_safe(MSR_IA32_RTIT_CTL, &ctl)) { 231 | goto ABORT; 232 | } 233 | 234 | if (ctl & TRACE_EN 235 | && wrmsrl_safe(MSR_IA32_RTIT_CTL, ctl & ~TRACE_EN)) { 236 | goto ABORT; 237 | } 238 | 239 | /* Install the new ToPA base */ 240 | output_base = (uint64_t) __this_cpu_read(topa_cpu); 241 | if (output_base) { 242 | output_base = __pa(output_base); 243 | } 244 | 245 | if (wrmsrl_safe(MSR_IA32_RTIT_OUTPUT_BASE, output_base) 246 | || wrmsrl_safe(MSR_IA32_RTIT_OUTPUT_MASK_PTRS, 0)) { 247 | goto ABORT; 248 | } 249 | 250 | status = HB_DRIVER_TRACE_STATUS_IDLE; 251 | 252 | ABORT: 253 | __this_cpu_write(trace_state, status); 254 | } 255 | 256 | /** Allocates a ToPA buffer for a CPU and installs it. Uses the global topa_page_order and topa_buffer_count. */ 257 | static int allocate_pt_buffer_on_cpu(int cpu) { 258 | uint64_t * topa; 259 | 260 | /* allocate topa */ 261 | topa = per_cpu(topa_cpu, cpu); 262 | if (!topa) { 263 | int n; 264 | 265 | topa = (uint64_t *) __get_free_page(GFP_KERNEL | __GFP_ZERO); 266 | if (!topa) { 267 | LOGGER(TAGE "cpu %d, Cannot allocate topa page\n", cpu); 268 | return -ENOMEM; 269 | } 270 | 271 | per_cpu(topa_cpu, cpu) = topa; 272 | 273 | /* create circular topa table */ 274 | n = 0; 275 | for (; n < topa_buffer_count; n++) { 276 | uint8_t *buf = (void *) __get_free_pages( 277 | GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO, 278 | topa_page_order); 279 | 280 | if (!buf) { 281 | LOGGER(TAGE "Cannot allocate %d'th PT buffer for CPU %d, truncating this ToPA buffer\n", n, cpu); 282 | break; 283 | } 284 | 285 | topa[n] = __pa(buf) | (topa_page_order << TOPA_SIZE_SHIFT); 286 | } 287 | 288 | //This is important as we use it for both our count method and our free 289 | topa[n] = (uint64_t) __pa(topa) | TOPA_END; /* circular buffer */ 290 | } 291 | 292 | //Install it 293 | return smp_do_on_cpu(cpu, configure_topa_msrs_on_this_cpu, 0x00); 294 | } 295 | 296 | /** 297 | * Frees the ToPA structures and buffers for a given core 298 | */ 299 | static void free_pt_buffer_on_cpu(int cpu) { 300 | uint64_t * topa; 301 | 302 | topa = per_cpu(topa_cpu, cpu); 303 | if (topa) { 304 | int j; 305 | 306 | for (j = 1; j < topa_buffer_count; j++) { 307 | if (topa[j] & TOPA_END) { 308 | break; 309 | } 310 | 311 | free_pages((unsigned long) __va(topa[j] & PAGE_MASK), topa_page_order); 312 | } 313 | 314 | free_page((uint64_t) topa); 315 | per_cpu(topa_cpu, cpu) = 0x0; 316 | } 317 | 318 | //Purge MSRs 319 | smp_do_on_cpu(cpu, configure_topa_msrs_on_this_cpu, 0x00); 320 | } 321 | 322 | static unsigned int get_topa_entry_count(int cpu) { 323 | uint64_t * topa = per_cpu(topa_cpu, cpu); 324 | int count; 325 | 326 | if (!topa) { 327 | return 0; 328 | } 329 | 330 | //Kinda a hack but we assume the table is well-formed and terminated 331 | for (count = 0; !(topa[count] & TOPA_END); count++); 332 | return count; 333 | } 334 | 335 | /** 336 | * Internal cross-CPU structure meant to handle configuring PT on a CPU core 337 | */ 338 | struct confiugre_pt { 339 | /** 340 | * The CPU this configuration should be applied to 341 | */ 342 | uint16_t cpu_id; 343 | 344 | /** 345 | * The filters to apply. Note, only the first n filters will be applied (where n is the number of supported filters) 346 | */ 347 | hb_driver_packet_range_filter filters[HB_DRIVER_PACKET_CONFIGURE_TRACE_FILTER_COUNT]; 348 | 349 | /** 350 | * The CR3 value (i.e. memory space) to hinge tracing on 351 | */ 352 | uint64_t cr3; 353 | }; 354 | 355 | /** 356 | * Configures PT on the current CPU using a configuration struct. Use SMP to call this. 357 | * Sets a flag in __this_cpu(trace_state) >0x1 if there was an error configuring PT 358 | * 359 | * Assumes that PT is NOT running on this CPU. If it is, it is disabled and not re-enabled. 360 | */ 361 | static void configure_pt_on_this_cpu(void *arg) { 362 | uint64_t ctl; 363 | struct confiugre_pt *configure_pt; 364 | int filter_apply_count; 365 | int i; 366 | int status; 367 | 368 | status = HB_DRIVER_TRACE_STATUS_CONFIGURATION_WRITE_ERROR; 369 | configure_pt = arg; 370 | 371 | if (rdmsrl_safe(MSR_IA32_RTIT_CTL, &ctl)) { 372 | goto EXIT; 373 | } 374 | 375 | if (ctl & TRACE_EN) { 376 | //Disable tracing while configuring 377 | if (wrmsrl_safe(MSR_IA32_RTIT_CTL, ctl & ~TRACE_EN)) { 378 | goto EXIT; 379 | } 380 | } 381 | 382 | ctl &= ~(TSC_EN | CTL_OS | CTL_USER | CR3_FILTER | DIS_RETC | TO_PA | 383 | CYC_EN | TRACE_EN | BRANCH_EN | CYC_EN | MTC_EN | 384 | MTC_EN | MTC_MASK | CYC_MASK | PSB_MASK | PT_ERROR); 385 | 386 | for (i = 0; i < HB_DRIVER_PACKET_CONFIGURE_TRACE_FILTER_COUNT; i++) { 387 | ctl &= ~ADDRn_SHIFT(i); 388 | } 389 | 390 | //Baseline configuration 391 | ctl |= CTL_USER | DIS_RETC | TO_PA | BRANCH_EN; 392 | 393 | //configure_pt_on_cpu stuffed this with the cr3. If it is zero, however, that indicates that cr3 filtering is 394 | // disabled 395 | if (configure_pt->cr3) { 396 | ctl |= CR3_FILTER; 397 | 398 | if (IS_ENABLED(CONFIG_PAGE_TABLE_ISOLATION) && static_cpu_has(X86_FEATURE_PTI)) { 399 | configure_pt->cr3 |= 1 << PAGE_SHIFT; 400 | } 401 | } 402 | if (wrmsrl_safe(MSR_IA32_CR3_MATCH, configure_pt->cr3)) { 403 | goto EXIT; 404 | } 405 | 406 | if (address_range_filter_count >= HB_DRIVER_PACKET_CONFIGURE_TRACE_FILTER_COUNT) { 407 | filter_apply_count = HB_DRIVER_PACKET_CONFIGURE_TRACE_FILTER_COUNT; 408 | } else { 409 | filter_apply_count = address_range_filter_count; 410 | } 411 | 412 | //We only apply the first n supported filters 413 | for (i = 0; i < filter_apply_count; i++) { 414 | hb_driver_packet_range_filter *filter = configure_pt->filters + i; 415 | if (filter->enabled) { 416 | ctl |= 1LLU /* filter */ << ADDRn_SHIFT(i); 417 | if (wrmsrl_safe(MSR_IA32_ADDRn_START(i), filter->start_address) 418 | || wrmsrl_safe(MSR_IA32_ADDRn_END(i), filter->stop_address)) { 419 | goto EXIT; 420 | } 421 | } 422 | } 423 | 424 | if (wrmsrl_safe(MSR_IA32_RTIT_STATUS, 0ULL) //clear errors 425 | || wrmsrl_safe(MSR_IA32_RTIT_OUTPUT_MASK_PTRS, 0ULL) //reset our output pointers to the start 426 | || wrmsrl_safe(MSR_IA32_RTIT_CTL, ctl)) { //write out the config :) 427 | goto EXIT; 428 | } 429 | 430 | status = HB_DRIVER_TRACE_STATUS_IDLE; 431 | 432 | EXIT: 433 | __this_cpu_write(trace_state, status); 434 | } 435 | 436 | /** 437 | * Configures PT on a given CPU 438 | * @param cpu The CPU to trace on. Assumed to be a valid CPU index 439 | * @param configure_trace A pointer to the trace config 440 | * @return Negative on error 441 | */ 442 | static int configure_pt_on_cpu(int cpu, hb_driver_packet_configure_trace *configure_trace) { 443 | int result = 0; 444 | uint64_t cr3 = 0; 445 | struct confiugre_pt configure_pt; 446 | 447 | if (configure_trace->pid) { 448 | if (!(cr3 = pid_to_cr3((int) configure_trace->pid))) { 449 | return -ESRCH; 450 | } 451 | } 452 | 453 | configure_pt.cpu_id = configure_trace->cpu_id; 454 | memcpy(&configure_pt.filters, configure_trace->filters, sizeof(configure_pt.filters)); 455 | //If no PID was provided, cr3 is 0. This indicates no CR3/no memory filtering 456 | configure_pt.cr3 = cr3; 457 | 458 | if ((result = smp_do_on_cpu(cpu, configure_pt_on_this_cpu, &configure_pt))) { 459 | return result; 460 | } 461 | 462 | return cpu_state_to_result(cpu); 463 | } 464 | 465 | /** 466 | * Sets tracing status on this CPU 467 | * @param arg hb_driver_packet_set_enabled * 468 | */ 469 | static void set_pt_enable_on_this_cpu(void *arg) { 470 | hb_driver_packet_set_enabled *set_enabled = arg; 471 | int status; 472 | uint64_t enable; 473 | uint64_t ctl; 474 | 475 | if (set_enabled->reset_output 476 | && wrmsrl_safe(MSR_IA32_RTIT_OUTPUT_MASK_PTRS, 0ULL) /* reset our output pointers to the start */) { 477 | status = HB_DRIVER_TRACE_STATUS_CONFIGURATION_WRITE_ERROR; 478 | } 479 | 480 | enable = (!(unsigned long long) set_enabled->enabled) - 481 | 1; /* This is all ones if enabled and all zeros if disabled */ 482 | 483 | if (rdmsrl_safe(MSR_IA32_RTIT_CTL, &ctl) 484 | || wrmsrl_safe(MSR_IA32_RTIT_CTL, (ctl & ~TRACE_EN) | (enable & TRACE_EN))) { 485 | status = HB_DRIVER_TRACE_STATUS_CONFIGURATION_WRITE_ERROR; 486 | } else { 487 | status = HB_DRIVER_TRACE_STATUS_IDLE; 488 | } 489 | 490 | __this_cpu_write(trace_state, status); 491 | } 492 | 493 | /** 494 | * Internal cross-CPU structure for reading an MSR 495 | */ 496 | struct read_msr { 497 | /** 498 | * The MSR to read 499 | */ 500 | uint32_t msr; 501 | 502 | /** 503 | * The error code of reading the MSR 504 | */ 505 | int msr_read_return_code; 506 | 507 | /** 508 | * The value read from the MSR, if msr_read_return_code is zero 509 | */ 510 | uint64_t result; 511 | }; 512 | 513 | /** 514 | * Reads a given MSR safely into the provided struct read_msr * 515 | */ 516 | static void read_msr_on_this_cpu(void *arg) { 517 | struct read_msr *read_msr = arg; 518 | read_msr->msr_read_return_code = rdmsrl_safe(read_msr->msr, &read_msr->result); 519 | } 520 | 521 | /** 522 | * Reads an MSR from a specific CPU and returns the result 523 | */ 524 | static int read_msr_on_cpu(int cpu, uint32_t msr, uint64_t *result) { 525 | //Create the read request structure 526 | struct read_msr read_msr = { 527 | .msr = msr, 528 | .msr_read_return_code = 0, 529 | .result = 0, 530 | }; 531 | 532 | smp_do_on_cpu(cpu, read_msr_on_this_cpu, &read_msr); 533 | 534 | *result = read_msr.result; 535 | return read_msr.msr_read_return_code; 536 | } 537 | 538 | /** 539 | * Checks that all of our rigid PT requirements are satisfied. This fills any hardware state required globals. 540 | * Note, these requirements could be loosened given more time, but this driver was kinda a last minute bodge :( 541 | * @return Negative if PT is not fully supported on this hardware. 542 | */ 543 | static int intel_pt_hardware_support_preflight(void) { 544 | unsigned a, b, c, d; 545 | 546 | cpuid(0, &a, &b, &c, &d); 547 | if (a < 0x14) { 548 | LOGGER(TAGE "Not enough CPUID support for PT\n"); 549 | return -EIO; 550 | } 551 | cpuid_count(0x07, 0x0, &a, &b, &c, &d); 552 | if ((b & BIT(25)) == 0) { 553 | LOGGER(TAGE "No PT support\n"); 554 | return -EIO; 555 | } 556 | 557 | cpuid_count(0x14, 0x0, &a, &b, &c, &d); 558 | 559 | if (!(b & BIT(0))) { 560 | LOGGER(TAGE "No CR3 filter support\n"); 561 | return -EIO; 562 | } 563 | 564 | if (!(b & BIT(2))) { 565 | LOGGER(TAGE "IP filtering is not supported\n"); 566 | return -EIO; 567 | } 568 | 569 | if (!(c & BIT(0))) { 570 | LOGGER(TAGE "No ToPA support\n"); 571 | return -EIO; 572 | } 573 | 574 | if (!(c & BIT(1))) { 575 | LOGGER(TAGE "ToPA does not support multiple entries\n"); 576 | return -EIO; 577 | } 578 | 579 | cpuid_count(0x14, 0x1, &a, &b, &c, &d); 580 | address_range_filter_count = a & 0b111; 581 | 582 | return 0; 583 | } 584 | 585 | /** 586 | * Handles ioctls from userspace. Packets are defined in honeybee_shared/hb_driver_packets 587 | */ 588 | static long honey_driver_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { 589 | long result; 590 | 591 | //Take the configuration lock. All operations touch state and so we really need those to be serial. 592 | //A reader-writer lock could be used but that's honestly a bit more complicated than really necessary since we 593 | // don't really expect to have THAT many concurrent operations 594 | mutex_lock(&configuration_mutex); 595 | 596 | switch (cmd) { 597 | case HB_DRIVER_PACKET_IOC_CONFIGURE_BUFFERS: { 598 | hb_driver_packet_configure_buffers configure_buffers; 599 | int i; 600 | int cpu_count; 601 | 602 | LOGGER(TAGI "ioctl -- configure_buffers\n"); 603 | 604 | //XXX this doesn't actually work if you offline random cores in the middle (i.e. offline core 4 of 8) but 605 | // I'm choosing to ignore that because who offlines a CPU core while fuzzing?? 606 | cpu_count = num_online_cpus(); 607 | 608 | //You can't adjust allocations while any CPU is tracing 609 | for (i = 0; i < cpu_count; i++) { 610 | if (per_cpu(trace_state, i) == HB_DRIVER_TRACE_STATUS_TRACING) { 611 | result = -EBUSY; 612 | goto OUT; 613 | } 614 | } 615 | 616 | if (copy_from_user(&configure_buffers, (void *) arg, sizeof configure_buffers) != 0 617 | || configure_buffers.count > PAGE_SIZE - 1 618 | || configure_buffers.page_count_power == 0) { 619 | result = -EINVAL; 620 | goto OUT; 621 | } 622 | 623 | /* we don't actually allocate the buffers, but we will destroy existing ones before we change our config */ 624 | //Allocate the buffer for each online CPU 625 | for (i = 0; i < cpu_count; i++) { 626 | //Free any existing allocations. This is a NOP if no buffers have been allocated 627 | free_pt_buffer_on_cpu(i); 628 | } 629 | 630 | /* 631 | * buffers are actually allocated on trace configure. This is because we only want to allocate buffers 632 | * for CPUs we're tracing on. Since our fuzzing load has us leave ~1/2 of the CPUs unused, it's 633 | * nonsensical to give each CPU a buffer. 634 | */ 635 | topa_buffer_count = configure_buffers.count; 636 | topa_page_order = configure_buffers.page_count_power; 637 | 638 | result = 0; 639 | break; 640 | } 641 | case HB_DRIVER_PACKET_IOC_SET_ENABLED: { 642 | hb_driver_packet_set_enabled set_enabled; 643 | 644 | LOGGER(TAGI "ioctl -- set_enabled\n"); 645 | 646 | if (copy_from_user(&set_enabled, (void *) arg, sizeof set_enabled) != 0 647 | || !cpu_online(set_enabled.cpu_id)) { 648 | result = -EINVAL; 649 | goto OUT; 650 | } 651 | 652 | //First verify that we CAN launch this CPU 653 | //Are we in a valid state to enable PT? 654 | if (!(per_cpu(trace_state, set_enabled.cpu_id) == HB_DRIVER_TRACE_STATUS_IDLE 655 | || per_cpu(trace_state, set_enabled.cpu_id) == HB_DRIVER_TRACE_STATUS_TRACING)) { 656 | result = -EPERM; 657 | goto OUT; 658 | } 659 | 660 | //Do we have the required buffer configured (note: we assume that if the buffers are allocated the MSRs 661 | // were correctly set). 662 | if (!per_cpu(topa_cpu, set_enabled.cpu_id)) { 663 | result = -ENOSPC; 664 | goto OUT; 665 | } 666 | 667 | /* we are safe to enable PT */ 668 | smp_do_on_cpu(set_enabled.cpu_id, set_pt_enable_on_this_cpu, (void *) &set_enabled); 669 | 670 | result = cpu_state_to_result(set_enabled.cpu_id); 671 | break; 672 | } 673 | case HB_DRIVER_PACKET_IOC_CONFIGURE_TRACE: { 674 | hb_driver_packet_configure_trace configure_trace; 675 | 676 | LOGGER(TAGI "ioctl -- configure_trace\n"); 677 | 678 | if (!(topa_page_order && topa_buffer_count)) { 679 | //Buffers must be configured before tracing can be configured 680 | result = -ENOSPC; 681 | goto OUT; 682 | } 683 | 684 | if (copy_from_user(&configure_trace, (void *) arg, sizeof configure_trace) != 0 685 | || !cpu_online(configure_trace.cpu_id) 686 | || configure_trace.pid == 0) { 687 | result = -EINVAL; 688 | goto OUT; 689 | } 690 | 691 | if (per_cpu(trace_state, configure_trace.cpu_id) == HB_DRIVER_TRACE_STATUS_TRACING) { 692 | //You can't reconfigure a CPU while it's tracing 693 | result = -EBUSY; 694 | goto OUT; 695 | } 696 | 697 | if ((result = configure_pt_on_cpu(configure_trace.cpu_id, &configure_trace))) { 698 | goto OUT; 699 | } 700 | 701 | //Check if we've allocated a ToPA for this CPU yet 702 | if (!per_cpu(topa_cpu, configure_trace.cpu_id) 703 | && (result = allocate_pt_buffer_on_cpu(configure_trace.cpu_id)) < 0) { 704 | goto OUT; 705 | } 706 | 707 | break; 708 | } 709 | 710 | case HB_DRIVER_PACKET_IOC_GET_TRACE_LENGTHS: { 711 | hb_driver_packet_get_trace_lengths get_lengths; 712 | uint64_t mask_msr; 713 | uint64_t buffer_index; 714 | uint64_t buffer_offset; 715 | uint64_t packet_byte_count; 716 | uint64_t buffer_size; 717 | 718 | LOGGER(TAGI "ioctl -- get_trace_lengths\n"); 719 | 720 | if (copy_from_user(&get_lengths, (void *) arg, sizeof get_lengths) != 0 721 | || !cpu_online(get_lengths.cpu_id) 722 | || !get_lengths.trace_buffer_length_out 723 | || !get_lengths.trace_packet_byte_count_out) { 724 | result = -EINVAL; 725 | goto OUT; 726 | } 727 | 728 | if ((result = preflight_trace_buffer_userspace_access(get_lengths.cpu_id)) < 0) { 729 | goto OUT; 730 | } 731 | 732 | if ((result = read_msr_on_cpu(get_lengths.cpu_id, MSR_IA32_RTIT_OUTPUT_MASK_PTRS, &mask_msr)) < 0) { 733 | goto OUT; 734 | } 735 | 736 | /* 737 | * This is from the Intel docs. ToPA provides very specific information about the last write location in 738 | * the output mask MSR. It's gross bit hacking but buffer_index tells us the index in our table (zero 739 | * index) and buffer_offset tells us the byte inside that buffer. 740 | */ 741 | buffer_index = (mask_msr >> 7) & ((1LLU << 25) - 1); 742 | buffer_offset = (mask_msr >> 32) & ((1LLU << 32) - 1); 743 | packet_byte_count = buffer_index * (PAGE_SIZE << topa_page_order) + buffer_offset; 744 | 745 | buffer_size = get_topa_entry_count(get_lengths.cpu_id) * (PAGE_SIZE << topa_page_order); 746 | 747 | if (copy_to_user(get_lengths.trace_packet_byte_count_out, &packet_byte_count, sizeof packet_byte_count) 748 | || copy_to_user(get_lengths.trace_buffer_length_out, &buffer_size, sizeof buffer_size)) { 749 | result = -EIO; 750 | goto OUT; 751 | } 752 | 753 | result = 0; 754 | break; 755 | } 756 | default: 757 | result = -EINVAL; 758 | break; 759 | } 760 | 761 | OUT: 762 | //Unlock before we leave 763 | mutex_unlock(&configuration_mutex); 764 | 765 | LOGGER(TAGI "\tioctl result -> %ld\n", result); 766 | 767 | return result; 768 | } 769 | 770 | /** 771 | * Map the trace buffer for a given CPU to user space. This is only valid if the target CPU is not tracing. 772 | * Send the CPU index via the mmap offset field (you must multiply the ID by PAGE_SIZE lol) 773 | */ 774 | static int honey_driver_mmap(struct file *file, struct vm_area_struct *vma) { 775 | int result; 776 | unsigned int topa_entry_count; 777 | int i; 778 | uint64_t * topa; 779 | unsigned long len = vma->vm_end - vma->vm_start; 780 | int cpu = vma->vm_pgoff; 781 | unsigned long buffer_size = PAGE_SIZE << topa_page_order; 782 | 783 | //Take the configuration lock. All operations touch state and so we really need those to be serial. 784 | //A reader-writer lock could be used but that's honestly a bit more complicated than really necessary since we 785 | // don't really expect to have THAT many concurrent operations 786 | mutex_lock(&configuration_mutex); 787 | 788 | LOGGER(TAGI "mmap call for CPU %d, size = %lu\n", cpu, len); 789 | 790 | if (!cpu_online(cpu)) { 791 | return -EINVAL; 792 | } 793 | 794 | if ((result = preflight_trace_buffer_userspace_access(cpu)) < 0) { 795 | goto EXIT; 796 | } 797 | 798 | topa_entry_count = get_topa_entry_count(cpu); 799 | 800 | if (len != topa_entry_count * buffer_size) { 801 | return -EINVAL; 802 | } 803 | 804 | topa = per_cpu(topa_cpu, cpu); 805 | for (i = 0; i < topa_entry_count; i++) { 806 | result = remap_pfn_range(vma, 807 | vma->vm_start + i * buffer_size, 808 | topa[i] >> PAGE_SHIFT, 809 | buffer_size, 810 | vma->vm_page_prot); 811 | if (result) { 812 | break; 813 | } 814 | } 815 | 816 | 817 | result = 0; 818 | EXIT: 819 | //Unlock before we leave 820 | mutex_unlock(&configuration_mutex); 821 | 822 | return result; 823 | } 824 | 825 | static const struct file_operations honey_driver_fops = { 826 | .owner = THIS_MODULE, 827 | .unlocked_ioctl = honey_driver_ioctl, 828 | .mmap = honey_driver_mmap, 829 | .llseek = no_llseek, 830 | }; 831 | 832 | static struct miscdevice honey_driver_misc_dev = { 833 | .minor = MISC_DYNAMIC_MINOR, 834 | .name = "honey_driver", 835 | .fops = &honey_driver_fops, 836 | .mode = S_IRWXUGO, 837 | }; 838 | 839 | static int honey_driver_init(void) { 840 | int result = 0; 841 | int cpu_count; 842 | int i; 843 | 844 | LOGGER(TAGI "Starting...\n"); 845 | 846 | //Preflight check to make sure we support PT (plus to store various required information) 847 | result = intel_pt_hardware_support_preflight(); 848 | if (result < 0) { 849 | LOGGER(TAGE "CPU does not support all required features, aborting..."); 850 | goto ABORT; 851 | } 852 | 853 | //Register our devfs node 854 | result = misc_register(&honey_driver_misc_dev); 855 | if (result < 0) { 856 | LOGGER(TAGE "Cannot register device\n"); 857 | goto ABORT; 858 | } 859 | 860 | //XXX this doesn't actually work if you offline random cores in the middle (i.e. offline core 4 of 8) but 861 | // I'm choosing to ignore that because who offlines a CPU core while fuzzing?? 862 | cpu_count = num_online_cpus(); 863 | for (i = 0; i < cpu_count; i++) { 864 | per_cpu(trace_state, i) = HB_DRIVER_TRACE_STATUS_CORE_NOT_CONFIGURED; 865 | } 866 | 867 | LOGGER(TAGI "Started!\n"); 868 | return 0; 869 | 870 | ABORT: 871 | return result; 872 | } 873 | 874 | static void honey_driver_exit(void) { 875 | int i; 876 | int cpu_count; 877 | 878 | LOGGER(TAGI "Unloading...\n"); 879 | 880 | //XXX this doesn't actually work if you offline random cores in the middle (i.e. offline core 4 of 8) but 881 | // I'm choosing to ignore that because who offlines a CPU core while fuzzing?? 882 | cpu_count = num_online_cpus(); 883 | //Teardown all cpus 884 | for (i = 0; i < cpu_count; i++) { 885 | //This is safe since this is a NOP if the CPU has no buffers on it 886 | //If the CPU is tracing, the MSR clear will disable PT 887 | free_pt_buffer_on_cpu(i); 888 | } 889 | 890 | misc_deregister(&honey_driver_misc_dev); 891 | 892 | LOGGER(TAGI "Unloaded!\n"); 893 | } 894 | 895 | module_init(honey_driver_init); 896 | module_exit(honey_driver_exit); 897 | MODULE_LICENSE("Dual BSD/GPL"); 898 | MODULE_AUTHOR("Allison Husain"); 899 | -------------------------------------------------------------------------------- /honey_driver/honey_driver_constants.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 1/10/21. 3 | // 4 | 5 | #ifndef HONEY_DRIVER_HONEY_DRIVER_CONSTANTS_H 6 | #define HONEY_DRIVER_HONEY_DRIVER_CONSTANTS_H 7 | 8 | #define MSR_IA32_RTIT_OUTPUT_BASE 0x00000560 9 | #define MSR_IA32_RTIT_OUTPUT_MASK_PTRS 0x00000561 10 | #define MSR_IA32_RTIT_CTL 0x00000570 11 | #define TRACE_EN BIT_ULL(0) 12 | #define CYC_EN BIT_ULL(1) 13 | #define CTL_OS BIT_ULL(2) 14 | #define CTL_USER BIT_ULL(3) 15 | #define PT_ERROR BIT_ULL(4) 16 | #define CR3_FILTER BIT_ULL(7) 17 | #define PWR_EVT_EN BIT_ULL(4) 18 | #define FUP_ON_PTW_EN BIT_ULL(5) 19 | #define TO_PA BIT_ULL(8) 20 | #define MTC_EN BIT_ULL(9) 21 | #define TSC_EN BIT_ULL(10) 22 | #define DIS_RETC BIT_ULL(11) 23 | #define PTW_EN BIT_ULL(12) 24 | #define BRANCH_EN BIT_ULL(13) 25 | #define MTC_MASK (0xf << 14) 26 | #define CYC_MASK (0xf << 19) 27 | #define PSB_MASK (0xf << 24) 28 | #define ADDRn_SHIFT(n) (32 + (4*(n))) 29 | #define ADDRn_MASK (0xfULL << ADDRn_SHIFT(n)) 30 | #define MSR_IA32_RTIT_STATUS 0x00000571 31 | #define MSR_IA32_CR3_MATCH 0x00000572 32 | #define TOPA_STOP BIT_ULL(4) 33 | #define TOPA_INT BIT_ULL(2) 34 | #define TOPA_END BIT_ULL(0) 35 | #define TOPA_SIZE_SHIFT 6 36 | #define MSR_IA32_ADDRn_START(n) (0x00000580 + 2*(n)) 37 | #define MSR_IA32_ADDRn_END(n) (0x00000581 + 2*(n)) 38 | 39 | enum HB_DRIVER_TRACE_STATUS { 40 | HB_DRIVER_TRACE_STATUS_IDLE = 0x0, 41 | HB_DRIVER_TRACE_STATUS_TRACING = 0x1, 42 | HB_DRIVER_TRACE_STATUS_CORE_NOT_CONFIGURED = 0x2, 43 | /* error states */ 44 | HB_DRIVER_TRACE_STATUS_CONFIGURATION_WRITE_ERROR = 0x3, 45 | 46 | }; 47 | 48 | 49 | #endif //HONEY_DRIVER_HONEY_DRIVER_CONSTANTS_H 50 | -------------------------------------------------------------------------------- /honey_hive_generator/disassembly/hh_disassembly.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 12/21/20. 3 | // 4 | 5 | /* 6 | * This file provides tools for analyzing ELF binaries using Intel XED 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "xed-interface.h" 18 | 19 | #include "hh_disassembly.h" 20 | #include "elf.h" 21 | 22 | #define TAG "[" __FILE__ "] " 23 | 24 | /** 25 | * Init Intel XED 26 | */ 27 | __attribute__((constructor)) 28 | static void intel_xed_init() { 29 | xed_tables_init(); 30 | } 31 | 32 | 33 | /** 34 | * Is this instruction a Processor Trace qualifying change-of-flow-instruction? 35 | * @param category The category of the instruction 36 | * @return True if this is a COFI instruction 37 | */ 38 | __attribute((always_inline)) 39 | bool is_qualifying_cofi(xed_category_enum_t category) { 40 | switch (category) { 41 | case XED_CATEGORY_COND_BR: 42 | case XED_CATEGORY_UNCOND_BR: 43 | case XED_CATEGORY_CALL: 44 | case XED_CATEGORY_RET: 45 | case XED_CATEGORY_INTERRUPT: 46 | case XED_CATEGORY_SYSCALL: 47 | case XED_CATEGORY_SYSRET: 48 | case XED_CATEGORY_SYSTEM: 49 | return true; 50 | default: 51 | return false; 52 | } 53 | } 54 | 55 | 56 | bool hh_disassembly_get_blocks_from_elf(const char *path, hh_disassembly_block **blocks, int64_t *blocks_count) { 57 | int fd = 0; 58 | void *map_handle = NULL; 59 | hh_disassembly_block *_blocks = NULL; 60 | bool success = false; 61 | 62 | fd = open(path, O_RDONLY); 63 | if (fd < 0) { 64 | printf(TAG "Could not open file '%s'!\n", path); 65 | goto CLEANUP; 66 | } 67 | struct stat sb; 68 | int stat_result = fstat(fd, &sb); 69 | if (stat_result < 0) { 70 | printf(TAG "Could not stat file '%s'!\n", path); 71 | goto CLEANUP; 72 | } 73 | 74 | map_handle = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); 75 | if (!map_handle) { 76 | printf(TAG "Could not mmap file '%s'!\n", path); 77 | goto CLEANUP; 78 | } 79 | 80 | Elf64_Ehdr *header = map_handle; 81 | if (sb.st_size < sizeof(Elf64_Ehdr)) { 82 | printf(TAG "Too small!\n"); 83 | goto CLEANUP; 84 | } 85 | 86 | if (memcmp(header->e_ident, ELFMAG, SELFMAG) != 0) { 87 | printf(TAG "Bad magic!\n"); 88 | goto CLEANUP; 89 | } 90 | 91 | if (header->e_ident[EI_CLASS] != ELFCLASS64) { 92 | printf(TAG "Not 64-bit!\n"); 93 | goto CLEANUP; 94 | } 95 | 96 | if (header->e_ident[EI_DATA] != ELFDATA2LSB) { 97 | printf(TAG "x86_64 must be LSB"); 98 | goto CLEANUP; 99 | } 100 | 101 | if (header->e_ident[EI_VERSION] != EV_CURRENT) { 102 | printf(TAG "Unsupported ELF type?\n"); 103 | goto CLEANUP; 104 | } 105 | 106 | if (header->e_shoff > sb.st_size || header->e_shoff + sizeof(Elf64_Shdr) * header->e_shnum > sb.st_size) { 107 | printf(TAG "Bad section header!\n"); 108 | goto CLEANUP; 109 | } 110 | 111 | 112 | xed_decoded_inst_t xedd; 113 | xed_state_t dstate; 114 | dstate.mmode = XED_MACHINE_MODE_LONG_64; 115 | 116 | size_t blocks_capacity = 16; 117 | int64_t blocks_write_index = 0; 118 | _blocks = malloc(sizeof(hh_disassembly_block) * blocks_capacity); 119 | if (!_blocks) { 120 | printf(TAG "Out of memory!\n"); 121 | success = false; 122 | goto CLEANUP; 123 | } 124 | 125 | //Walk each section 126 | for (int i = 0; i < header->e_shnum; i++) { 127 | Elf64_Shdr *sh_header = (map_handle + header->e_shoff + sizeof(Elf64_Shdr) * i); 128 | if (sh_header->sh_offset > sb.st_size || sh_header->sh_offset + sh_header->sh_size > sb.st_size) { 129 | printf(TAG "Bad region defined by section header. Skipping...\n"); 130 | continue; 131 | } 132 | 133 | //Skip non-executable sections (poor man's __TEXT filter) 134 | if (!(sh_header->sh_flags & SHF_EXECINSTR)) { 135 | continue; 136 | } 137 | 138 | //Relative pointer into this text segment 139 | uint8_t *text_segment = map_handle + sh_header->sh_offset; 140 | off_t text_segment_offset = 0; 141 | //A running count of where our last block started. Updated after committing each block 142 | uint64_t block_start = sh_header->sh_offset + text_segment_offset; 143 | 144 | while (text_segment_offset < sh_header->sh_size) { 145 | uint64_t insn_va = sh_header->sh_offset + text_segment_offset; 146 | 147 | //Decode the instruction 148 | xed_error_enum_t result; 149 | xed_decoded_inst_zero_set_mode(&xedd, &dstate); 150 | result = xed_decode(&xedd, text_segment + text_segment_offset, sb.st_size - text_segment_offset); 151 | if (result != XED_ERROR_NONE) { 152 | //Try and just length decode it. Complicated weird instructions can't be decoded by xed but ild can 153 | // handle them. 154 | xed_decoded_inst_zero_set_mode(&xedd, &dstate); 155 | result = xed_ild_decode(&xedd,text_segment + text_segment_offset, sb.st_size - text_segment_offset); 156 | if (result != XED_ERROR_NONE) { 157 | printf(TAG "XED total decode error! %s -> %p\n", xed_error_enum_t2str(result), (void *) insn_va); 158 | break; 159 | } 160 | text_segment_offset += xed_decoded_inst_get_length(&xedd); 161 | continue; 162 | } 163 | 164 | 165 | uint32_t insn_length = xed_decoded_inst_get_length(&xedd); 166 | xed_category_enum_t category = xed_decoded_inst_get_category(&xedd); 167 | if (is_qualifying_cofi(category)) { 168 | int32_t branch_displacement = xed_decoded_inst_get_branch_displacement(&xedd); 169 | uint64_t cofi_destination = UINT64_MAX; 170 | if (xed_operand_values_has_branch_displacement(xed_decoded_inst_operands_const(&xedd))) { 171 | cofi_destination = insn_va + insn_length + branch_displacement; 172 | } 173 | 174 | if (blocks_write_index >= blocks_capacity) { 175 | blocks_capacity *= 2; 176 | hh_disassembly_block *new_blocks = realloc(_blocks, sizeof(hh_disassembly_block) * blocks_capacity); 177 | if (!new_blocks) { 178 | printf(TAG "Out of memory!\n"); 179 | success = false; 180 | goto CLEANUP; 181 | } 182 | 183 | _blocks = new_blocks; 184 | } 185 | 186 | hh_disassembly_block *block = &_blocks[blocks_write_index++]; 187 | block->instruction_category = category; 188 | block->start_offset = block_start; 189 | block->length = (uint32_t) (insn_va - block_start); 190 | block->last_instruction_size = insn_length; 191 | block->cofi_destination = cofi_destination; 192 | 193 | block_start = insn_va + insn_length; 194 | } 195 | 196 | text_segment_offset += insn_length; 197 | } 198 | } 199 | 200 | success = true; 201 | *blocks = _blocks; 202 | *blocks_count = blocks_write_index; //the next write index IS the count! 203 | 204 | CLEANUP: 205 | if (map_handle) { 206 | munmap(map_handle, sb.st_size); 207 | } 208 | 209 | if (fd) { 210 | close(fd); 211 | } 212 | 213 | if (!success) { 214 | if (_blocks) { 215 | free(_blocks); 216 | } 217 | } 218 | 219 | return success; 220 | } -------------------------------------------------------------------------------- /honey_hive_generator/disassembly/hh_disassembly.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 12/21/20. 3 | // 4 | 5 | #ifndef HONEY_MIRROR_HH_DISASSEMBLY_H 6 | #define HONEY_MIRROR_HH_DISASSEMBLY_H 7 | 8 | #include 9 | #include 10 | 11 | /** 12 | * Represents a single basic block in a binary 13 | */ 14 | typedef struct { 15 | uint64_t start_offset; 16 | uint64_t cofi_destination; 17 | uint32_t length; 18 | uint16_t last_instruction_size; 19 | uint16_t instruction_category; 20 | } hh_disassembly_block; 21 | 22 | /** 23 | * Iterates the basic blocks inside of an ELF binary 24 | * @param path The path to the ELF binary 25 | * @param blocks The location to place a pointer to a buffer of blocks. You are responsible for freeing this buffer. 26 | * @param blocks_count The location to place the number of blocks in the blocks buffer 27 | * @return true on success 28 | */ 29 | bool hh_disassembly_get_blocks_from_elf(const char *path, hh_disassembly_block **blocks, int64_t *blocks_count); 30 | 31 | 32 | #endif //HONEY_MIRROR_HH_DISASSEMBLY_H 33 | -------------------------------------------------------------------------------- /honey_hive_generator/hive_generation/hh_hive_generator.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 12/21/20. 3 | // 4 | #include 5 | #include 6 | #include 7 | 8 | #include "xed-interface.h" 9 | 10 | #include "hh_hive_generator.h" 11 | #include "../../honeybee_shared/hb_hive.h" 12 | 13 | static int64_t lookup_block_sorted(const hh_disassembly_block *sorted_blocks, int64_t block_count, uint64_t 14 | target_offset) { 15 | int64_t left = 0, right = block_count - 1; 16 | while (left <= right && right >= 0) { 17 | int64_t search = (left + right) / 2; 18 | const hh_disassembly_block *block = &sorted_blocks[search]; 19 | uint64_t block_start = block->start_offset; 20 | uint64_t block_end = block_start + block->length; 21 | 22 | if (block_start <= target_offset && target_offset <= block_end) { 23 | return search; 24 | } else if (target_offset < block_start) { 25 | right = search - 1; 26 | } else { 27 | left = search + 1; 28 | } 29 | } 30 | 31 | return -1; 32 | } 33 | 34 | #define LO31(x) ((uint64_t)((x) & ((1LLU<<31) - 1))) 35 | /** 36 | * Calculate the packed index value according to the format described in hb_hive.h 37 | */ 38 | static inline uint64_t packed_indices(uint64_t not_taken, uint64_t taken, uint8_t is_conditional) { 39 | return (LO31(not_taken) << 33) | LO31(taken) << 1 | (is_conditional & 0b1); 40 | } 41 | 42 | /** 43 | * Calculate the packed virtual address pointers according to the format described in hb_hive.h 44 | */ 45 | static inline uint64_t packed_uvip(uint64_t not_taken, uint64_t taken) { 46 | return (not_taken << 32) | LO31(taken); 47 | } 48 | 49 | /** 50 | * Write a uint32_t a certain number of times to a file. 51 | */ 52 | static inline void write_uint32t_times(FILE *fp, uint32_t value, uint64_t times) { 53 | for (uint64_t i = 0; i < times; i++) { 54 | fwrite(&value, sizeof(value), 1, fp); 55 | } 56 | } 57 | 58 | int hh_hive_generator_generate(const hh_disassembly_block *sorted_blocks, int64_t block_count, const char 59 | *hive_destination_path) { 60 | int result = 0; 61 | FILE *fp = NULL; 62 | size_t cofi_destination_size_bytes = 0; 63 | int64_t *cofi_destination_block_indexes = NULL; 64 | 65 | fp = fopen(hive_destination_path, "w"); 66 | if (!fp) { 67 | result = -1; 68 | goto CLEANUP; 69 | } 70 | 71 | //Write out our header 72 | hb_hive_file_header header = { 73 | .magic = HB_HIVE_FILE_HEADER_MAGIC, 74 | .block_count = block_count, 75 | .uvip_slide = sorted_blocks[0].start_offset, 76 | .direct_map_count = sorted_blocks[block_count - 1].start_offset + sorted_blocks[block_count - 1].length - sorted_blocks[0].start_offset, 77 | }; 78 | fwrite(&header, sizeof(hb_hive_file_header), 1, fp); 79 | 80 | /* Generate our cofi destination table */ 81 | 82 | if (__builtin_mul_overflow(block_count, sizeof(int64_t), &cofi_destination_size_bytes) 83 | || !(cofi_destination_block_indexes = malloc(cofi_destination_size_bytes))) { 84 | result = -2; 85 | goto CLEANUP; 86 | } 87 | 88 | for (int64_t i = 0; i < block_count; i++) { 89 | const hh_disassembly_block *block = sorted_blocks + i; 90 | 91 | int64_t next_block_i = -1; 92 | if (block->cofi_destination == UINT64_MAX 93 | || (next_block_i = lookup_block_sorted(sorted_blocks, block_count, block->cofi_destination)) == -1) { 94 | //We don't have a known next-IP 95 | cofi_destination_block_indexes[i] = -1; 96 | } else { 97 | cofi_destination_block_indexes[i] = next_block_i; 98 | } 99 | } 100 | 101 | hm_block out_block; 102 | bzero(&out_block, sizeof(hm_block)); 103 | 104 | //Write out all blocks 105 | for (int64_t i = 0; i < block_count; i++) { 106 | const hh_disassembly_block *block = sorted_blocks + i; 107 | int64_t next_block_i = cofi_destination_block_indexes[i]; 108 | uint64_t next_block_start_offset; 109 | if (next_block_i != -1) { 110 | next_block_start_offset = sorted_blocks[next_block_i].start_offset; 111 | } else { 112 | next_block_start_offset = header.uvip_slide - 1; 113 | } 114 | 115 | if (block->instruction_category == XED_CATEGORY_COND_BR) { 116 | out_block.packed_indices = packed_indices(/* NT */ i + 1, /* T */ next_block_i, /* COND? */ 1); 117 | out_block.packed_uvips = packed_uvip(/* NT */ block->start_offset + block->length + block->last_instruction_size - header.uvip_slide, /* T */ next_block_start_offset - header.uvip_slide); 118 | } else { 119 | //We have an unconditional branch. This means we KNOW our target 120 | out_block.packed_indices = packed_indices(/* NT */ 0, /* T */ next_block_i, /* COND? */ 0); 121 | out_block.packed_uvips = packed_uvip(/* NT */ 0, /* T */ next_block_start_offset - header.uvip_slide); 122 | } 123 | fwrite(&out_block, sizeof(hm_block), 1, fp); 124 | } 125 | 126 | //Write out our direct map 127 | uint64_t last_block_ip = sorted_blocks[0].start_offset; 128 | for (int64_t i = 0; i < block_count; i++) { 129 | const hh_disassembly_block *block = sorted_blocks + i; 130 | 131 | uint64_t invalid_count = block->start_offset - last_block_ip; 132 | uint64_t this_block_count = block->length + block->last_instruction_size; 133 | //invalid 134 | write_uint32t_times(fp, 0, invalid_count); 135 | //valid 136 | write_uint32t_times(fp, (uint32_t)i, this_block_count); 137 | 138 | last_block_ip = block->start_offset + this_block_count; 139 | } 140 | 141 | CLEANUP: 142 | if (fp) { 143 | fclose(fp); 144 | } 145 | 146 | if (cofi_destination_block_indexes) { 147 | free(cofi_destination_block_indexes); 148 | } 149 | 150 | return result; 151 | 152 | } 153 | -------------------------------------------------------------------------------- /honey_hive_generator/hive_generation/hh_hive_generator.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 12/21/20. 3 | // 4 | 5 | #ifndef HONEY_MIRROR_HH_HIVE_GENERATOR_H 6 | #define HONEY_MIRROR_HH_HIVE_GENERATOR_H 7 | #include 8 | #include "../disassembly/hh_disassembly.h" 9 | 10 | int hh_hive_generator_generate(const hh_disassembly_block *sorted_blocks, int64_t block_count, const char 11 | *hive_destination_path); 12 | 13 | #endif //HONEY_MIRROR_HH_HIVE_GENERATOR_H 14 | -------------------------------------------------------------------------------- /honey_hive_generator/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "disassembly/hh_disassembly.h" 8 | #include "hive_generation/hh_hive_generator.h" 9 | 10 | int main(int argc, const char * argv[]) { 11 | if (argc != 3) { 12 | printf( 13 | " .' '. __\n" 14 | " . . . (__\\_\n" 15 | " . . . -{{_(|8)\n" 16 | "jgs ' . . ' ' . . ' (__/\n\n" 17 | "honey_hive_generator converts an ELF binary to a 'hive' which may be used by Honeybee to accelerate " 18 | "Intel Processor Trace decoding inside another program.\n\n" 19 | "Usage:\n" 20 | "honey_hive_generator \n" 21 | ); 22 | return 1; 23 | } 24 | 25 | int result; 26 | hh_disassembly_block *blocks = NULL; 27 | 28 | const char *input_path = argv[1]; 29 | const char *output_path = argv[2]; 30 | 31 | //Stash our starting directory so that we can get back 32 | char starting_directory[PATH_MAX]; 33 | getcwd(starting_directory, sizeof(starting_directory)); 34 | 35 | //Generate our hive file 36 | int64_t block_count = 0; 37 | if (!hh_disassembly_get_blocks_from_elf(input_path, &blocks, &block_count)) { 38 | result = 2; 39 | printf("Failed to get blocks!\n"); 40 | goto CLEANUP; 41 | } 42 | 43 | if ((result = hh_hive_generator_generate(blocks, block_count, output_path))) { 44 | result = 3; 45 | printf("Failed to write hive file\n"); 46 | goto CLEANUP; 47 | } 48 | 49 | result = 0; 50 | CLEANUP: 51 | 52 | free(blocks); 53 | 54 | return result; 55 | } 56 | -------------------------------------------------------------------------------- /honey_tester/main.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 12/22/20. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "../honey_analyzer/processor_trace/ha_pt_decoder.h" 17 | #include "../honey_analyzer/processor_trace/ha_pt_decoder_constants.h" 18 | #include "../honey_analyzer/trace_analysis/ha_session.h" 19 | #include "unit_testing/ha_session_audit.h" 20 | 21 | #define TAG "[" __FILE__"] " 22 | 23 | enum execution_task { 24 | EXECUTION_TASK_UNKNOWN, EXECUTION_TASK_AUDIT, EXECUTION_TASK_PERFORMANCE, EXECUTION_TASK_RACE 25 | }; 26 | 27 | static long current_clock() { 28 | struct timespec tv; 29 | clock_gettime(CLOCK_MONOTONIC_RAW, &tv); 30 | 31 | return tv.tv_sec * 1e9 + tv.tv_nsec; 32 | } 33 | 34 | int main(int argc, const char * argv[]) { 35 | char *end_ptr = NULL; 36 | enum execution_task task = EXECUTION_TASK_UNKNOWN; 37 | char *trace_path = NULL; 38 | char *binary_path = NULL; 39 | char *hive_path = NULL; 40 | uint64_t slid_load_sideband_address = -1; 41 | uint64_t binary_offset_sideband = -1; 42 | 43 | int opt = 0; 44 | while ((opt = getopt(argc, (char *const *) argv, "aprh:s:o:t:b:")) != -1) { 45 | switch (opt) { 46 | case 'a': 47 | task = EXECUTION_TASK_AUDIT; 48 | break; 49 | case 'p': 50 | task = EXECUTION_TASK_PERFORMANCE; 51 | break; 52 | case 'r': 53 | task = EXECUTION_TASK_RACE; 54 | break; 55 | case 'h': 56 | hive_path = optarg; 57 | case 's': 58 | slid_load_sideband_address = strtoull(optarg, &end_ptr, 16); 59 | break; 60 | case 'o': 61 | binary_offset_sideband = strtoull(optarg, &end_ptr, 16); 62 | break; 63 | case 't': 64 | trace_path = optarg; 65 | break; 66 | case 'b': 67 | binary_path = optarg; 68 | break; 69 | default: 70 | SHOW_USAGE: 71 | printf( 72 | " .' '. __\n" 73 | " . . . (__\\_\n" 74 | " . . . -{{_(|8)\n" 75 | "jgs ' . . ' ' . . ' (__/\n\n" 76 | "honey_analyzer is a testing shim which allows for unit testing and various other debugging " 77 | "and development activities.\n" 78 | "Usage:\n" 79 | "-a Run a correctness audit using libipt\n" 80 | "-p Run a performance test\n" 81 | "-r Run a drag race between libipt and Honeybee\n" 82 | "-h The path to the Honeybee Hive to use to decode the trace\n" 83 | "-s The slid binary address according to sideband\n" 84 | "-o The executable segment offset according to sideband\n" 85 | "-t The Processor Trace file to decode\n" 86 | "-b The binary to decode with. This is only used in libipt based tests!\n" 87 | ); 88 | return 1; 89 | } 90 | } 91 | 92 | //Validate 93 | if (!trace_path || slid_load_sideband_address == -1 || binary_offset_sideband == -1 94 | || task == EXECUTION_TASK_UNKNOWN || !hive_path) { 95 | printf(TAG "Required argument missing\n"); 96 | goto SHOW_USAGE; 97 | } 98 | 99 | if ((task == EXECUTION_TASK_AUDIT || task == EXECUTION_TASK_RACE) && !binary_path) { 100 | printf(TAG "Binary path is required for test run mode\n"); 101 | goto SHOW_USAGE; 102 | } 103 | 104 | /* arguments are valid */ 105 | 106 | int result = HA_PT_DECODER_NO_ERROR; 107 | hb_hive *hive = NULL; 108 | ha_session_t session = NULL; 109 | int fd = 0; 110 | void *trace_map_handle = NULL; 111 | uint64_t trace_file_size; 112 | uint8_t *trace_buffer = NULL; 113 | 114 | /* Copy in the trace file */ 115 | 116 | fd = open(trace_path, O_RDONLY); 117 | if (fd < 0) { 118 | printf(TAG "Could not open file '%s'!\n", trace_path); 119 | goto CLEANUP; 120 | } 121 | 122 | struct stat sb; 123 | int stat_result = fstat(fd, &sb); 124 | if (stat_result < 0) { 125 | printf(TAG "Could not stat file '%s'!\n", trace_path); 126 | goto CLEANUP; 127 | } 128 | trace_file_size = sb.st_size; 129 | 130 | trace_map_handle = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); 131 | if (trace_map_handle == MAP_FAILED) { 132 | printf(TAG "Could not mmap file '%s'!\n", trace_path); 133 | goto CLEANUP; 134 | } 135 | 136 | if (!(trace_buffer = malloc(trace_file_size + 1 /* Required for the stop codon */))) { 137 | printf(TAG "Out of memory\n"); 138 | goto CLEANUP; 139 | } 140 | memcpy(trace_buffer, trace_map_handle, trace_file_size); 141 | trace_buffer[trace_file_size] = PT_TRACE_END; //terminate the buffer since we're pulling from a file 142 | 143 | /* Setup the session */ 144 | 145 | if (!(hive = hb_hive_alloc(hive_path))) { 146 | printf(TAG "Could not load hive at path %s\n", hive_path); 147 | goto CLEANUP; 148 | } 149 | 150 | uint64_t trace_base_address = slid_load_sideband_address - binary_offset_sideband; 151 | if ((result = ha_session_alloc(&session, hive)) < 0 152 | || (result = ha_session_reconfigure_with_terminated_trace_buffer(session, trace_buffer, trace_file_size, 153 | trace_base_address))) { 154 | printf(TAG "Failed to start session, error=%d\n", result); 155 | goto CLEANUP; 156 | } 157 | 158 | /* Execute our operation */ 159 | 160 | uint64_t start = current_clock(); 161 | uint64_t stop; 162 | if (task == EXECUTION_TASK_AUDIT) { 163 | result = ha_session_audit_perform_libipt_audit(session, binary_path, trace_buffer, trace_file_size); 164 | stop = current_clock(); 165 | 166 | if (result < 0) { 167 | printf(TAG "Test failure = %d\n", result); 168 | } else { 169 | printf(TAG "Test pass!\n"); 170 | } 171 | } else if (task == EXECUTION_TASK_PERFORMANCE) { 172 | result = ha_session_print_trace(session); 173 | stop = current_clock(); 174 | 175 | if (result < 0 && result != -HA_PT_DECODER_END_OF_STREAM) { 176 | printf(TAG "decode error = %d\n", result); 177 | } else { 178 | printf(TAG "Decode success!\n"); 179 | result = 0; 180 | } 181 | } else { 182 | result = ha_session_audit_libipt_drag_race(session, 25, binary_path, trace_buffer, trace_file_size); 183 | stop = current_clock(); 184 | 185 | if (result < 0) { 186 | printf(TAG "Drag race failed = %d\n", result); 187 | } 188 | 189 | result = 0; 190 | } 191 | 192 | printf(TAG "Execute time = %"PRIu64" ns\n", stop - start); 193 | 194 | /* Completed OK, clear the result if there is any */ 195 | CLEANUP: 196 | if (hive) { 197 | hb_hive_free(hive); 198 | } 199 | 200 | if (session) { 201 | ha_session_free(session); 202 | } 203 | 204 | if (fd > 0) { 205 | close(fd); 206 | } 207 | 208 | if (trace_map_handle && trace_map_handle != MAP_FAILED) { 209 | munmap(trace_map_handle, trace_file_size); 210 | } 211 | 212 | if (trace_buffer) { 213 | free(trace_buffer); 214 | } 215 | 216 | return -result; 217 | } -------------------------------------------------------------------------------- /honey_tester/unit_testing/ha_session_audit.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 12/30/20. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "intel-pt.h" 15 | 16 | #include "ha_session_audit.h" 17 | #include "../../honey_analyzer/trace_analysis/ha_session_internal.h" 18 | #include "../../honey_analyzer/ha_debug_switch.h" 19 | 20 | #define TAG "[" __FILE__ "] " 21 | 22 | typedef struct { 23 | struct pt_block_decoder *block_decoder; 24 | uint64_t last_libipt_ip; 25 | enum ha_session_audit_status status; 26 | uint64_t honey_blocks_passed; 27 | } audit_extra; 28 | 29 | static void libipt_audit_on_block(ha_session_t session, void *context, uint64_t hive_unslid_ip) { 30 | audit_extra *extra = context; 31 | if (extra->status) { 32 | //We don't actually have a way to abort a trace in progress, however we will refuse to continue the test so 33 | // as to not destroy the original error 34 | return; 35 | } 36 | 37 | struct pt_block_decoder *block_decoder = extra->block_decoder; 38 | 39 | int result = 0; 40 | struct pt_block block; 41 | bzero(&block, sizeof(struct pt_block)); 42 | 43 | while (block.ninsn == 0) { 44 | FUP_TRY_AGAIN: 45 | result = pt_blk_next(block_decoder, &block, sizeof(block)); 46 | while (result & pts_event_pending) { 47 | if (result < 0) { 48 | printf(TAG "Testing failed, libipt event decode error: %d\n", result); 49 | extra->status = -HA_SESSION_AUDIT_TEST_LIBIPT_ERROR; 50 | return; 51 | } 52 | struct pt_event event; 53 | result = pt_blk_event(block_decoder, &event, sizeof(event)); 54 | } 55 | 56 | if (result < 0) { 57 | printf(TAG "Testing failed, libipt block decode error: %d\n", result); 58 | extra->status = -HA_SESSION_AUDIT_TEST_LIBIPT_ERROR; 59 | return; 60 | } 61 | } 62 | 63 | uint64_t libipt_unslid = block.ip - session->trace_slide; 64 | uint32_t libipt_i = hb_hive_virtual_address_to_block_index(session->hive, libipt_unslid); 65 | uint32_t hive_i = hb_hive_virtual_address_to_block_index(session->hive, hive_unslid_ip); 66 | 67 | if (hive_i == libipt_i && hive_i != -1) { 68 | //We store the last block IP to account for libipt's special handling of FUPs and its choice to split blocks 69 | extra->last_libipt_ip = libipt_unslid; 70 | extra->honey_blocks_passed++; 71 | #if HA_ENABLE_BLOCK_LEVEL_LOGS 72 | printf(TAG "PASS! hive = %p, libipt = %p.\n", (void *)hive_unslid_ip, (void *)libipt_unslid); 73 | #endif 74 | } else if (extra->last_libipt_ip && libipt_i 75 | == hb_hive_virtual_address_to_block_index(session->hive, extra->last_libipt_ip)) { 76 | //We have a split block 77 | #if HA_ENABLE_BLOCK_LEVEL_LOGS 78 | printf(TAG "\tDetected libipt splitting last block %p to %p due to an FUP. Trying again!\n", 79 | (void *)extra->last_libipt_ip, (void *)libipt_unslid); 80 | #endif 81 | extra->last_libipt_ip = 0; //clear so we don't loop 82 | goto FUP_TRY_AGAIN; 83 | } else { 84 | printf(TAG "*** AUDIT FAILED ***\n"); 85 | printf(TAG "hive = %p, libipt = %p [honey_blocks = %"PRIu64"]\n", 86 | (void *) hive_unslid_ip, (void *) libipt_unslid, extra->honey_blocks_passed); 87 | extra->status = -HA_SESSION_AUDIT_TEST_INCORRECT_RESULT; 88 | } 89 | } 90 | 91 | int ha_session_audit_perform_libipt_audit(ha_session_t session, const char *binary_path, 92 | uint8_t *trace_buffer, uint64_t trace_length) { 93 | int result = 0; 94 | int fd = 0; 95 | struct stat sb; 96 | struct pt_block_decoder *block_decoder = NULL; 97 | const char *map_handle = NULL; 98 | audit_extra *extra = NULL; 99 | 100 | //Map in the binary for libipt 101 | fd = open(binary_path, O_RDONLY); 102 | if (fd < 0) { 103 | printf(TAG "Failed to open binary!\n"); 104 | result = -HA_SESSION_AUDIT_TEST_INIT_FAILED; 105 | goto CLEANUP; 106 | } 107 | 108 | if ((result = fstat(fd, &sb)) < 0) { 109 | printf(TAG "Failed to fstat!\n"); 110 | result = -HA_SESSION_AUDIT_TEST_INIT_FAILED; 111 | goto CLEANUP; 112 | } 113 | 114 | map_handle = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); 115 | if (map_handle == MAP_FAILED) { 116 | printf(TAG "mmap failed!\n"); 117 | result = -HA_SESSION_AUDIT_TEST_INIT_FAILED; 118 | goto CLEANUP; 119 | } 120 | 121 | extra = calloc(1, sizeof(audit_extra)); 122 | if (!extra) { 123 | result = -11; 124 | goto CLEANUP; 125 | } 126 | 127 | /* libipt configuration */ 128 | struct pt_config config; 129 | config.size = sizeof(struct pt_config); 130 | config.begin = trace_buffer; 131 | config.end = trace_buffer + trace_length; 132 | //Stop on all control flow in order to duplicate the behavior of mirrors 133 | config.flags.variant.block.end_on_jump = 1; 134 | config.flags.variant.block.end_on_call = 1; 135 | 136 | block_decoder = pt_blk_alloc_decoder(&config); 137 | 138 | if (!block_decoder) { 139 | result = -HA_SESSION_AUDIT_TEST_INIT_FAILED; 140 | goto CLEANUP; 141 | } 142 | 143 | struct pt_image *image_unowned = pt_blk_get_image(block_decoder); 144 | if ((result = pt_image_add_file(image_unowned, binary_path, 0x00, sb.st_size, NULL, session->trace_slide)) < 0 145 | || (result = pt_blk_sync_forward(block_decoder)) < 0) { 146 | result = -HA_SESSION_AUDIT_TEST_INIT_FAILED; 147 | goto CLEANUP; 148 | } 149 | 150 | //Stash the block_decoder pointer so the logger function can access it 151 | extra->block_decoder = block_decoder; 152 | 153 | //Kickoff mirror decoding from our side 154 | result = ha_session_decode(session, libipt_audit_on_block, extra); 155 | 156 | //Figure out which result we want to end with. We want the EARLIEST error. 157 | if (extra->status != HA_SESSION_AUDIT_TEST_PASS) { 158 | result = extra->status; 159 | goto CLEANUP; 160 | } else if (result < 0 && result != -HA_PT_DECODER_END_OF_STREAM) { 161 | result = -HA_SESSION_AUDIT_TEST_HONEYBEE_ERROR; 162 | goto CLEANUP; 163 | } 164 | 165 | result = HA_SESSION_AUDIT_TEST_PASS; 166 | 167 | CLEANUP: 168 | if (block_decoder) { 169 | pt_blk_free_decoder(block_decoder); 170 | } 171 | 172 | if (fd >= 0) { 173 | close(fd); 174 | } 175 | 176 | if (extra) { 177 | free(extra); 178 | } 179 | 180 | return result; 181 | } 182 | 183 | static long current_clock() { 184 | struct timespec tv; 185 | clock_gettime(CLOCK_MONOTONIC_RAW, &tv); 186 | 187 | return tv.tv_sec * 1e9 + tv.tv_nsec; 188 | } 189 | 190 | __attribute__((optnone)) 191 | static void nop_decode_block(ha_session_t session, void *context, uint64_t block) { 192 | 193 | } 194 | 195 | int ha_session_audit_libipt_drag_race(ha_session_t session, unsigned int iterations, const char *binary_path, 196 | uint8_t *trace_buffer, uint64_t trace_length) { 197 | int result = 0; 198 | int fd = 0; 199 | struct stat sb; 200 | struct pt_block_decoder *block_decoder = NULL; 201 | const char *map_handle = NULL; 202 | audit_extra *extra = NULL; 203 | 204 | //Map in the binary for libipt 205 | fd = open(binary_path, O_RDONLY); 206 | if (fd < 0) { 207 | printf(TAG "Failed to open binary!\n"); 208 | result = -HA_SESSION_AUDIT_TEST_INIT_FAILED; 209 | goto CLEANUP; 210 | } 211 | 212 | if ((result = fstat(fd, &sb)) < 0) { 213 | printf(TAG "Failed to fstat!\n"); 214 | result = -HA_SESSION_AUDIT_TEST_INIT_FAILED; 215 | goto CLEANUP; 216 | } 217 | 218 | map_handle = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); 219 | if (map_handle == MAP_FAILED) { 220 | printf(TAG "mmap failed!\n"); 221 | result = -HA_SESSION_AUDIT_TEST_INIT_FAILED; 222 | goto CLEANUP; 223 | } 224 | 225 | extra = calloc(1, sizeof(audit_extra)); 226 | if (!extra) { 227 | result = -11; 228 | goto CLEANUP; 229 | } 230 | 231 | /* libipt configuration */ 232 | struct pt_config config; 233 | config.size = sizeof(struct pt_config); 234 | config.begin = trace_buffer; 235 | config.end = trace_buffer + trace_length; 236 | //Stop on all control flow in order to duplicate the behavior of mirrors 237 | config.flags.variant.block.end_on_jump = 1; 238 | config.flags.variant.block.end_on_call = 1; 239 | 240 | block_decoder = pt_blk_alloc_decoder(&config); 241 | 242 | if (!block_decoder) { 243 | result = -HA_SESSION_AUDIT_TEST_INIT_FAILED; 244 | goto CLEANUP; 245 | } 246 | 247 | struct pt_image *image_unowned = pt_blk_get_image(block_decoder); 248 | if ((result = pt_image_add_file(image_unowned, binary_path, 0x00, sb.st_size, NULL, session->trace_slide)) < 0 249 | || (result = pt_blk_sync_forward(block_decoder)) < 0) { 250 | result = -HA_SESSION_AUDIT_TEST_INIT_FAILED; 251 | goto CLEANUP; 252 | } 253 | 254 | uint64_t total_libipt_time = 0; 255 | /* Test libipt */ 256 | for (unsigned int i = 0; i < iterations; i++) { 257 | uint64_t start = current_clock(); 258 | struct pt_block block; 259 | while (1) { 260 | result = pt_blk_next(block_decoder, &block, sizeof(block)); 261 | while (result & pts_event_pending) { 262 | if (result < 0 && result) { 263 | break; 264 | } 265 | struct pt_event event; 266 | result = pt_blk_event(block_decoder, &event, sizeof(event)); 267 | } 268 | 269 | if (block.ninsn) { 270 | //Force a reporting function call since we'd use one in a real test 271 | nop_decode_block(session, NULL, block.ip); 272 | } 273 | 274 | if (result < 0) { 275 | break; 276 | } 277 | } 278 | uint64_t stop = current_clock(); 279 | 280 | if (result < 0 && result != -pte_eos) { 281 | printf(TAG "Drag race failed, ipt failed to decode. error=%d (%s)\n", result, 282 | pt_errstr(pt_errcode(result))); 283 | 284 | goto CLEANUP; 285 | } 286 | 287 | //Reconfigure the decoder to prepare for the next round. libipt is a bit odd and so we just have to sync 288 | // backwards repeatedly to get to the start... 289 | // while ((result = pt_blk_sync_backward(block_decoder)) >= 0); 290 | pt_blk_sync_set(block_decoder, 0); 291 | result = 0; 292 | 293 | // printf(TAG "libipt round %u/%u: duration = %" PRIu64 "ns\n", i + 1, iterations, stop - start); 294 | printf("%"PRIu64"\n", stop - start); 295 | 296 | total_libipt_time += stop - start; 297 | } 298 | 299 | printf(TAG "libipt: rounds=%u, total time=%"PRIu64" ns, average=%f\n", iterations, total_libipt_time, 300 | ((double) total_libipt_time) / iterations); 301 | 302 | uint64_t total_honeybee_time = 0; 303 | /* Test Honeybee */ 304 | for (unsigned int i = 0; i < iterations; i++) { 305 | uint64_t start = current_clock(); 306 | result = ha_session_decode(session, nop_decode_block, NULL); 307 | uint64_t stop = current_clock(); 308 | 309 | if (result < 0 && result != -HA_PT_DECODER_END_OF_STREAM) { 310 | printf(TAG "Drag race failed, Honeybee failed to decode. error=%d\n", result); 311 | goto CLEANUP; 312 | } 313 | 314 | //Reconfigure the decoder in to prepare for the next round 315 | if ((result = ha_session_reconfigure_with_terminated_trace_buffer(session, trace_buffer, 316 | trace_length, session->trace_slide)) < 0) { 317 | printf(TAG "Drag race failed, Honeybee failed to reset. error=%d\n", result); 318 | goto CLEANUP; 319 | } 320 | 321 | // printf(TAG "Honeybee round %u/%u: duration = %" PRIu64 "ns\n", i + 1, iterations, stop - start); 322 | printf("%"PRIu64"\n", stop - start); 323 | total_honeybee_time += stop - start; 324 | } 325 | 326 | printf(TAG "Honeybee: rounds=%u, total time=%"PRIu64" ns, average=%f\n", iterations, total_honeybee_time, 327 | ((double) total_honeybee_time) / iterations); 328 | 329 | printf(TAG "Honeybee speedup = %f\n", ((double) total_libipt_time) / total_honeybee_time); 330 | 331 | 332 | result = 0; 333 | CLEANUP: 334 | if (block_decoder) { 335 | pt_blk_free_decoder(block_decoder); 336 | } 337 | 338 | if (fd >= 0) { 339 | close(fd); 340 | } 341 | 342 | if (extra) { 343 | free(extra); 344 | } 345 | 346 | return result; 347 | } 348 | 349 | -------------------------------------------------------------------------------- /honey_tester/unit_testing/ha_session_audit.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 12/30/20. 3 | // 4 | 5 | #ifndef HONEY_ANALYZER_HA_SESSION_AUDIT_H 6 | #define HONEY_ANALYZER_HA_SESSION_AUDIT_H 7 | 8 | #include "../../honey_analyzer/trace_analysis/ha_session.h" 9 | 10 | enum ha_session_audit_status { 11 | /** The audit passed. libipt and Honeybee produced essentially identical outputs. */ 12 | HA_SESSION_AUDIT_TEST_PASS = 0, 13 | /** The test could not be started */ 14 | HA_SESSION_AUDIT_TEST_INIT_FAILED = 1, 15 | /**libipt gave an error while decoding the trace */ 16 | HA_SESSION_AUDIT_TEST_LIBIPT_ERROR = 2, 17 | /** Honeybee gave an error while decoding the trace */ 18 | HA_SESSION_AUDIT_TEST_HONEYBEE_ERROR = 3, 19 | /** Honeybee and libipt disagree on how a trace should be decoded */ 20 | HA_SESSION_AUDIT_TEST_INCORRECT_RESULT = 4, 21 | }; 22 | 23 | /** 24 | * Decode the trace using both the mirror and using libipt's block decoder and compare results. 25 | * This is useful for testing the correctness of mirrors. 26 | * @param binary_path The path to the binary used to create the mirror/this trace. This is used by libipt. 27 | * @return 0 on success, negative on error. Error codes come from enum ha_session_audit_status. 28 | */ 29 | int ha_session_audit_perform_libipt_audit(ha_session_t session, const char *binary_path, 30 | uint8_t *trace_buffer, uint64_t trace_length); 31 | 32 | int ha_session_audit_libipt_drag_race(ha_session_t session, unsigned int iterations, const char *binary_path, 33 | uint8_t *trace_buffer, uint64_t trace_length); 34 | 35 | #endif //HONEY_ANALYZER_HA_SESSION_AUDIT_H 36 | -------------------------------------------------------------------------------- /honeybee_shared/hb_driver_packets.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 1/9/21. 3 | // 4 | 5 | #ifndef HONEY_DRIVER_HB_DRIVER_PACKETS_H 6 | #define HONEY_DRIVER_HB_DRIVER_PACKETS_H 7 | 8 | #ifndef __KERNEL__ 9 | #include 10 | #endif 11 | 12 | #ifdef __APPLE__ 13 | #include 14 | #else 15 | #include 16 | #endif 17 | 18 | #define HB_DRIVER_PACKET_IOC_MAGIC 0xab 19 | 20 | /** 21 | * (Re)allocates ToPA buffers on the CPU with a given size 22 | */ 23 | typedef struct { 24 | /** 25 | * The number of ToPA entries to allocate per CPU 26 | */ 27 | uint32_t count; 28 | 29 | /** 30 | * The number of pages, as a power of 2, to allocate 31 | */ 32 | uint8_t page_count_power; 33 | } hb_driver_packet_configure_buffers; 34 | 35 | #define HB_DRIVER_PACKET_IOC_CONFIGURE_BUFFERS _IOR(HB_DRIVER_PACKET_IOC_MAGIC, 1, hb_driver_packet_configure_buffers) 36 | 37 | /** 38 | * A packet which configures the trace state of a single CPU 39 | */ 40 | typedef struct { 41 | /** 42 | * The ID of the CPU to control the state of 43 | */ 44 | uint16_t cpu_id; 45 | 46 | /** 47 | * If non-zero, tracing is started on this CPU. 48 | * If zero, tracing is stopped, this terminates the trace buffer by placing a stop codon at the end of the trace. 49 | */ 50 | uint8_t enabled; 51 | 52 | /** 53 | * If non-zero, the trace output will be reset. 54 | */ 55 | uint8_t reset_output; 56 | } hb_driver_packet_set_enabled; 57 | 58 | #define HB_DRIVER_PACKET_IOC_SET_ENABLED _IOR(HB_DRIVER_PACKET_IOC_MAGIC, 2, hb_driver_packet_set_enabled) 59 | 60 | /** 61 | * Describes a range trace filter 62 | */ 63 | typedef struct { 64 | /** 65 | * The starting address to include in the trace (inclusive) 66 | */ 67 | uint64_t start_address; 68 | 69 | /** 70 | * The end of the range (exclusive) 71 | */ 72 | uint64_t stop_address; 73 | 74 | /** 75 | * Zero if this filter is NOT used 76 | */ 77 | uint8_t enabled; 78 | } hb_driver_packet_range_filter; 79 | 80 | #define HB_DRIVER_PACKET_CONFIGURE_TRACE_FILTER_COUNT 4 81 | typedef struct { 82 | /** 83 | * The CPU this configuration should be applied to 84 | */ 85 | uint16_t cpu_id; 86 | 87 | /** 88 | * The filters to apply. Note, only the first n filters will be applied (where n is the number of supported filters) 89 | */ 90 | hb_driver_packet_range_filter filters[HB_DRIVER_PACKET_CONFIGURE_TRACE_FILTER_COUNT]; 91 | 92 | /** 93 | * The PID to trace. This PID must be running (though it may be suspended). 94 | * This process should have its own memory space (i.e. already exec'd) since this PID is exchanged for a CR3 95 | * value internally. 96 | */ 97 | uint64_t pid; 98 | } hb_driver_packet_configure_trace; 99 | 100 | #define HB_DRIVER_PACKET_IOC_CONFIGURE_TRACE _IOR(HB_DRIVER_PACKET_IOC_MAGIC, 3, hb_driver_packet_configure_trace) 101 | 102 | /** 103 | * Fetch the lengths of a trace buffer for a given CPU. This operation is only valid when the target is not tracing. 104 | */ 105 | typedef struct { 106 | /** 107 | * The CPU to fetch the trace of 108 | */ 109 | uint16_t cpu_id; 110 | 111 | /** 112 | * The number of valid bytes of trace data in the buffer, offset from the start of the buffer. This can be used for 113 | * processing the trace data. 114 | * The kernel will place values at this location on success. 115 | */ 116 | uint64_t *trace_packet_byte_count_out; 117 | 118 | /** 119 | * The true length of the allocated buffer. This can be used for mmap-ing the trace buffer. 120 | * The kernel will place values at this location on success. 121 | */ 122 | uint64_t *trace_buffer_length_out; 123 | } hb_driver_packet_get_trace_lengths; 124 | 125 | #define HB_DRIVER_PACKET_IOC_GET_TRACE_LENGTHS _IOR(HB_DRIVER_PACKET_IOC_MAGIC, 4, hb_driver_packet_get_trace_lengths) 126 | 127 | 128 | #endif //HONEY_DRIVER_HB_DRIVER_PACKETS_H 129 | -------------------------------------------------------------------------------- /honeybee_shared/hb_hive.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 1/4/21. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "hb_hive.h" 16 | #define TAG "[" __FILE__ "] " 17 | 18 | hb_hive *hb_hive_alloc(const char *hive_path) { 19 | int fd = 0; 20 | void *map_handle = NULL; 21 | hb_hive *hive = NULL; 22 | bool success = false; 23 | 24 | if (!(hive = calloc(1, sizeof(hb_hive)))) { 25 | printf(TAG "Out of memory\n"); 26 | goto CLEANUP; 27 | } 28 | 29 | fd = open(hive_path, O_RDONLY); 30 | if (fd < 0) { 31 | printf(TAG "Could not open file '%s'!\n", hive_path); 32 | goto CLEANUP; 33 | } 34 | 35 | struct stat sb; 36 | int stat_result = fstat(fd, &sb); 37 | if (stat_result < 0) { 38 | printf(TAG "Could not stat file '%s'!\n", hive_path); 39 | goto CLEANUP; 40 | } 41 | 42 | map_handle = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); 43 | if (map_handle == MAP_FAILED) { 44 | printf(TAG "Could not mmap file '%s'!\n", hive_path); 45 | goto CLEANUP; 46 | } 47 | 48 | if (sb.st_size < sizeof(hb_hive_file_header)) { 49 | printf(TAG "File too small (cannot contain header!)\n"); 50 | goto CLEANUP; 51 | } 52 | 53 | hb_hive_file_header *header = map_handle; 54 | if (header->magic != HB_HIVE_FILE_HEADER_MAGIC) { 55 | printf(TAG "Bad magic.\n"); 56 | goto CLEANUP; 57 | } 58 | 59 | hive->block_count = header->block_count; 60 | hive->direct_map_count = header->direct_map_count; 61 | hive->uvip_slide = header->uvip_slide; 62 | 63 | uint8_t *raw_file_data_start_ptr = header->buffer; 64 | uint8_t *raw_file_data_end_ptr = ((uint8_t *)map_handle) + sb.st_size; 65 | 66 | /* copy in blocks */ 67 | uint64_t blocks_size; 68 | if (__builtin_mul_overflow(hive->block_count, sizeof(uint64_t), &blocks_size) 69 | || __builtin_mul_overflow(blocks_size, 2 /* each block is a pair of two uint64_t */, &blocks_size)) { 70 | printf(TAG "Hazardous file -> block_count overflow.\n"); 71 | goto CLEANUP; 72 | } 73 | //Bounds check for the region 74 | if (raw_file_data_start_ptr >= raw_file_data_end_ptr || raw_file_data_start_ptr + blocks_size >= raw_file_data_end_ptr) { 75 | printf(TAG "Hazardous file -> blocks buffer overrun.\n"); 76 | goto CLEANUP; 77 | } 78 | 79 | if (!(hive->blocks = malloc(blocks_size))) { 80 | printf(TAG "Out of memory\n"); 81 | goto CLEANUP; 82 | } 83 | 84 | memcpy(hive->blocks, raw_file_data_start_ptr, blocks_size); 85 | 86 | uint8_t *blocks_end_ptr = raw_file_data_start_ptr + blocks_size; 87 | 88 | /* copy in the direct map */ 89 | uint64_t direct_map_size; 90 | if (__builtin_mul_overflow(hive->direct_map_count, sizeof(uint32_t), &direct_map_size)) { 91 | printf(TAG "Hazardous file -> direct_map_count overflow.\n"); 92 | goto CLEANUP; 93 | } 94 | 95 | //Bounds check for the region 96 | if (blocks_end_ptr + direct_map_size >= raw_file_data_end_ptr) { 97 | printf(TAG "Hazardous file -> direct map buffer overrun.\n"); 98 | goto CLEANUP; 99 | } 100 | 101 | if (!(hive->direct_map_buffer = malloc(direct_map_size))) { 102 | printf(TAG "Out of memory\n"); 103 | goto CLEANUP; 104 | } 105 | 106 | memcpy(hive->direct_map_buffer, blocks_end_ptr, direct_map_size); 107 | 108 | success = true; 109 | CLEANUP: 110 | if (fd > 0) { 111 | close(fd); 112 | } 113 | 114 | if (map_handle && map_handle != MAP_FAILED) { 115 | munmap(map_handle, sb.st_size); 116 | } 117 | 118 | if (success) { 119 | return hive; 120 | } else { 121 | hb_hive_free(hive); 122 | return NULL; 123 | } 124 | } 125 | 126 | void hb_hive_free(hb_hive *hive) { 127 | if (!hive) { 128 | return; 129 | } 130 | 131 | if (hive->blocks) { 132 | free(hive->blocks); 133 | hive->blocks = NULL; 134 | } 135 | 136 | if (hive->direct_map_buffer) { 137 | free(hive->direct_map_buffer); 138 | hive->direct_map_buffer = NULL; 139 | } 140 | } 141 | 142 | void hb_hive_describe_block(hb_hive *hive, uint64_t i) { 143 | uint64_t index = hive->blocks[2 * i]; 144 | uint64_t vip = hive->blocks[2 * i + 1]; 145 | printf("Block %" PRIu64 ":\n", i); 146 | printf("Not-taken index = %" PRIu64 ", Taken index = %" PRIu64 ", Conditional=%" PRIu64 "\n", 147 | (index >> 33), (uint64_t) ((index >> 1) & ((1LLU << 31) - 1)), index & 1); 148 | printf("Not-taken VIP = %p, Taken VIP = %p\n", (void *) (vip >> 32), (void *) (vip & ((1LLU << 32) - 1))); 149 | } 150 | -------------------------------------------------------------------------------- /honeybee_shared/hb_hive.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Allison Husain on 1/4/21. 3 | // 4 | 5 | #ifndef HB_HIVE_H 6 | #define HB_HIVE_H 7 | 8 | #include 9 | #include 10 | 11 | /** HONEYBEE (little endian) :) */ 12 | #define HB_HIVE_FILE_HEADER_MAGIC (0x45454259454E4F48) 13 | /** Is this index block a conditional jump? */ 14 | #define HB_HIVE_FLAG_IS_CONDITIONAL (1) 15 | /** Is this jump index target an indirect jump? */ 16 | #define HB_HIVE_FLAG_INDIRECT_JUMP_INDEX_VALUE ((1LLU<<31) - 1) //31 bits of ones 17 | 18 | /** 19 | * This is the header of the Honeybee Hive file 20 | */ 21 | typedef struct { 22 | /** 23 | * The file magic 24 | * 0x45454259454E4F48 which is HONEYBEE (little endian) 25 | */ 26 | uint64_t magic; 27 | 28 | /** 29 | * The number of 64-bit pairs in the blocks buffer 30 | */ 31 | uint64_t block_count; 32 | /** 33 | * The value by which virtual IPs in blocks and the direct map are slid by. This can be thought of as a bias 34 | * value for an unsigned integer. 35 | */ 36 | uint64_t uvip_slide; 37 | 38 | /** 39 | * The number of 32-bit elements in the direct map buffer 40 | */ 41 | uint64_t direct_map_count; 42 | 43 | /** 44 | * A zero length array used to provide a pointer after the header. This is not a real field, this is for convenience 45 | */ 46 | uint8_t buffer[0]; 47 | 48 | /* blocks -- uint64_t */ 49 | 50 | //Since we know the number of blocks, we can just assume that everything after blocks is just map 51 | 52 | /* slid virtual address to block index -- uint32_t */ 53 | } hb_hive_file_header; 54 | 55 | /** 56 | * Each block is a pair of 64-bit packet values 57 | */ 58 | typedef struct { 59 | /** 60 | * This is a packed field which holds the indices of the next block(s) as well as information about the block. 61 | * The conditional flag should be tested for using HB_HIVE_FLAG_IS_CONDITIONAL. 62 | * If this block contains an indirect jump, the index for a branch will be HB_HIVE_FLAG_INDIRECT_JUMP_INDEX_VALUE. 63 | * 64 | * [{31 bits of not-taken}, {zero}][{31 bits of taken}, {1 bit conditional flag}] 65 | */ 66 | uint64_t packed_indices; 67 | 68 | /** 69 | * This is a packed field which holds the slid virtual instruction pointers for the block(s) after this one. 70 | * Holding slid VIPs in 32-bits is technically okay, even on a 64-bit OS, since our decoding stategy breaks down 71 | * as a result of the direct map on binaries >=4GB (since the direct map would necessarily be 16GB). 72 | * 73 | * [{32 bits of not-taken uVIP}][{32 bits of taken uVIP}] 74 | */ 75 | uint64_t packed_uvips; 76 | } hm_block; 77 | 78 | 79 | typedef struct { 80 | /** 81 | * A pointer to a buffer of blocks 82 | * This can also be read as an hm_block or simply stride-d 83 | */ 84 | uint64_t *blocks; 85 | 86 | /** 87 | * The number of pairs (or just hm_blocks) in the blocks buffer 88 | */ 89 | uint64_t block_count; 90 | 91 | /** 92 | * This bias value/positive slide for uVIPs. This is also the amount that values must be slid negatively to get 93 | * the index into the direct map buffer 94 | */ 95 | uint64_t uvip_slide; 96 | 97 | /** 98 | * The map of slid virtual instruction pointers to block indices. 99 | * To calculate the index into the buffer, use the hb_hive_virtual_address_to_block_index function 100 | */ 101 | uint32_t *direct_map_buffer; 102 | 103 | /** 104 | * The number of uint32_t elements in the direct map. The map is indexed by each byte of the binary being 105 | * assigned an index, starting from zero. 106 | */ 107 | uint64_t direct_map_count; 108 | } hb_hive; 109 | 110 | 111 | /** 112 | * Load a hive from disk and parse it 113 | * @param hive_path The path to the honeybee hive file 114 | * @return NULL if parsing failed for any reason 115 | */ 116 | hb_hive *hb_hive_alloc(const char *hive_path); 117 | 118 | /** 119 | * Frees a hive and all of its contents 120 | */ 121 | void hb_hive_free(hb_hive *hive); 122 | 123 | /** 124 | * Print a description of a given block to the console 125 | */ 126 | void hb_hive_describe_block(hb_hive *hive, uint64_t i); 127 | 128 | /** 129 | * Get the block index for a given unslid virtual address 130 | * @param hive The hive corresponding to the binary being traced 131 | * @param virtual_address The virtual address as it came from the trace 132 | * @return The index or -1 if the virtual address is not mapped in this hive 133 | */ 134 | static inline int64_t hb_hive_virtual_address_to_block_index(hb_hive *hive, uint64_t virtual_address) { 135 | uint64_t map_index = virtual_address - hive->uvip_slide; 136 | if (map_index >= hive->direct_map_count) { 137 | return -1; 138 | } 139 | return hive->direct_map_buffer[map_index]; 140 | } 141 | 142 | #endif //HB_HIVE_H 143 | -------------------------------------------------------------------------------- /paper/process_traces.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | 5 | class Trace: 6 | __slots__ = ["blocks", "edges", "timestamp"] 7 | def __init__(self, blocks, edges, timestamp): 8 | self.blocks = blocks 9 | self.edges = edges 10 | self.timestamp = timestamp 11 | 12 | def __repr__(self): 13 | return self.__str__() 14 | 15 | def __str__(self): 16 | return f" 8 | Date: December 31st, 2020 9 | """ 10 | import subprocess 11 | 12 | TESTS_ROOT = "../honeybee_unittest_data/" 13 | HONEY_HIVE_GENERATOR_PATH = "cmake-build-debug/honey_hive_generator" 14 | HONEY_TESTER_PATH = "cmake-build-debug/honey_tester" 15 | HIVE_TEMP_PATH = "/tmp/test_hive.hive" 16 | 17 | class Test: 18 | __slots__ = ["display_name", "binary_path", "traces", "hive_exit_code"] 19 | def __init__(self, display_name, binary_path, traces): 20 | self.display_name = display_name 21 | self.binary_path = binary_path 22 | self.traces = traces 23 | 24 | self.hive_exit_code = -1 25 | 26 | def print_test_result_and_return_summary(self): 27 | """ 28 | Prints the results of this test and all of its traces 29 | """ 30 | overall_success = self.hive_exit_code == 0 31 | print(f"[[{self.display_name}]]\n* Hive generator exit code = {str(self.hive_exit_code)}") 32 | 33 | for trace in self.traces: 34 | description, success = trace.get_result_description_and_success() 35 | if not success: 36 | overall_success = False 37 | print(description) 38 | summary_label = "PASSED" if overall_success else "FAILED" 39 | print(f"* TEST {summary_label}\n") 40 | return overall_success 41 | 42 | class Trace: 43 | __slots__ = ["display_name", "trace_path", "sideband_load_address", "sideband_offset", "libipt_audit_exit_code"] 44 | def __init__(self, display_name, trace_path, sideband_load_address, sideband_offset): 45 | self.display_name = display_name 46 | self.trace_path = trace_path 47 | self.sideband_load_address = sideband_load_address 48 | self.sideband_offset = sideband_offset 49 | self.libipt_audit_exit_code = -1 50 | 51 | def get_result_description_and_success(self): 52 | """ 53 | Returns a string representation of the test report. Returns true iff all modules passed on this trace. 54 | """ 55 | success = self.libipt_audit_exit_code == 0 56 | summary_label = "PASSED" if success else "FAILED" 57 | description = f"\t[[{self.display_name}]]\n"\ 58 | f"\t* libipt audit exit code = {str(self.libipt_audit_exit_code)}\n"\ 59 | f"\t* TRACE {summary_label}" 60 | 61 | return description, success 62 | 63 | 64 | def generate_test_hive(test): 65 | """ 66 | Generates the hive for the test target. 67 | Returns true on success. 68 | """ 69 | print(f"[***] Running hive generator on {test.display_name}") 70 | task = subprocess.Popen([HONEY_HIVE_GENERATOR_PATH, test.binary_path, HIVE_TEMP_PATH]) 71 | task.communicate() #wait 72 | test.hive_exit_code = task.returncode 73 | if task.returncode != 0: 74 | print(f"Hive generator for {test.display_name} failed with code {str(task.returncode)}") 75 | return False 76 | return True 77 | 78 | def perform_libipt_audit(test, trace): 79 | """ 80 | Performs a libipt audit to verify that the decoder and hive are working correctly. 81 | Requires that the analyze target has been built. 82 | Returns true on success. 83 | """ 84 | print(f"[***] Running libipt audit on {test.display_name}.{trace.display_name}") 85 | task = subprocess.Popen([HONEY_TESTER_PATH, "-a", "-h", HIVE_TEMP_PATH, "-s", trace.sideband_load_address, "-o", trace.sideband_offset, "-t", trace.trace_path, "-b", test.binary_path]) 86 | task.communicate() #wait 87 | trace.libipt_audit_exit_code = task.returncode 88 | if task.returncode != 0: 89 | print(f"[!!!] {test.display_name}.{trace.display_name} failed libipt audit with code {str(task.returncode)}") 90 | return False 91 | return True 92 | 93 | 94 | # These are the actual tests being run 95 | 96 | tests = [ 97 | Test("contrived_small", TESTS_ROOT + "contrived_small/small", [ 98 | Trace("trace_1", TESTS_ROOT + "contrived_small/trace_1.pt", "0x401000", "0x1000"), 99 | Trace("trace_2_part_1", TESTS_ROOT + "contrived_small/trace_2_1.pt", "0x401000", "0x1000"), 100 | Trace("trace_2_part_2", TESTS_ROOT + "contrived_small/trace_2_2.pt", "0x401000", "0x1000"), 101 | Trace("trace_2_part_3", TESTS_ROOT + "contrived_small/trace_2_3.pt", "0x401000", "0x1000"), 102 | ]), 103 | Test("contrived_medium", TESTS_ROOT + "contrived_medium/medium", [ 104 | Trace("trace_1", TESTS_ROOT + "contrived_medium/trace_1.pt", "0x401000", "0x1000"), 105 | Trace("trace_2_part_1", TESTS_ROOT + "contrived_medium/trace_2_1.pt", "0x401000", "0x1000"), 106 | Trace("trace_2_part_2", TESTS_ROOT + "contrived_medium/trace_2_2.pt", "0x401000", "0x1000"), 107 | Trace("trace_2_part_3", TESTS_ROOT + "contrived_medium/trace_2_3.pt", "0x401000", "0x1000"), 108 | Trace("trace_2_part_4", TESTS_ROOT + "contrived_medium/trace_2_4.pt", "0x401000", "0x1000"), 109 | 110 | ]), 111 | Test("tar", TESTS_ROOT + "tar/tar", [ 112 | Trace("decompress_clion", TESTS_ROOT + "tar/decompress_clion.pt", "0x55555555d000", "0x9000"), 113 | Trace("help_page", TESTS_ROOT + "tar/help_page.pt", "0x55555555d000", "0x9000") 114 | ]), 115 | Test("html_fast_parse", TESTS_ROOT + "html_fast_parse/fuzz_target", [ 116 | Trace("6.txt", TESTS_ROOT + "html_fast_parse/6_txt.pt", "0x555555558000", "0x4000"), 117 | ]), 118 | 119 | # These tests not used due to slight differences in when an OVF packet is handled. This does not effect decoding in a serious way since Intel's Software Developer Manual is very vague about what should be done about OVFs since so much can be lost during an internal overflow. 120 | # Test("ssh", TESTS_ROOT + "ssh/ssh", [ 121 | # Trace("interactive_login_attempt", TESTS_ROOT + "ssh/interactive_login_attempt.pt", "0x55555555e000", "0xa000"), 122 | # ]), 123 | # Test("clang", TESTS_ROOT + "clang/clang", [ 124 | # Trace("compile_simple_c_part_1", TESTS_ROOT + "clang/compile_simple_c_1.pt", "0x400000", "0x0"), 125 | # Trace("compile_simple_c_part_2", TESTS_ROOT + "clang/compile_simple_c_2.pt", "0x400000", "0x0"), 126 | # ]), 127 | # Test("honey_mirror_1", TESTS_ROOT + "honey_mirror_1/honey_mirror", [ 128 | # Trace("clang_huge", TESTS_ROOT + "honey_mirror_1/clang_huge.pt", "0x401000", "0x1000"), 129 | # Trace("bash", TESTS_ROOT + "honey_mirror_1/bash.pt", "0x401000", "0x1000"), 130 | # ]), 131 | ] 132 | 133 | 134 | for test in tests: 135 | #Try and build the target 136 | if not generate_test_hive(test): 137 | print(f"[!!!] Skipping all traces for test {test.display_name} because it failed to generate a hive") 138 | continue 139 | 140 | for trace in test.traces: 141 | perform_libipt_audit(test, trace) 142 | 143 | print("-" * 60) 144 | success_count = 0 145 | print("TEST RESULTS:") 146 | for test in tests: 147 | if test.print_test_result_and_return_summary(): 148 | success_count += 1 149 | print("-" * 60) 150 | print(f"Summary: {str(success_count)}/{str(len(tests))} targets passed") 151 | --------------------------------------------------------------------------------