diff --git a/.github/git/git-help.md b/.github/git/git-help.md index 4a418ae9..61ad99df 100644 --- a/.github/git/git-help.md +++ b/.github/git/git-help.md @@ -51,6 +51,25 @@ git reset --hard git clean -fd ``` +To modify the last pushed commit: + +```bash +# Undo the last commit but keep the changes in your working tree +git reset --soft HEAD~1 + +# Make your additional modifications +# edit files... + +# Stage everything +git add . + +# Create a new commit +git commit -m "Updated commit message" + +# Force-push to replace the remote commit +git push --force-with-lease +``` + --------------- ## Releasing instructions diff --git a/.github/workflows/vs2022.yml b/.github/workflows/vs2022.yml index 287cf15e..aa834a05 100644 --- a/.github/workflows/vs2022.yml +++ b/.github/workflows/vs2022.yml @@ -1,26 +1,31 @@ name: vs2022-ci on: - push: - branches: - - master - - dev - tags: - - 'v*' - paths-ignore: - - '.gitignore' - - '.gitattributes' - - '**.cmd' - - '**.bat' - - '**.md' - - pull_request: - paths-ignore: - - '.gitignore' - - '.gitattributes' - - '**.cmd' - - '**.bat' - - '**.md' + workflow_dispatch: + +# Disabled, uncomment the below line and remove the above lines to activate +# on: +# push: +# branches: +# - master +# - dev +# tags: +# - 'v*' +# paths-ignore: +# - '.gitignore' +# - '.gitattributes' +# - '**.cmd' +# - '**.bat' +# - '**.md' +# +# pull_request: +# paths-ignore: +# - '.gitignore' +# - '.gitattributes' +# - '**.cmd' +# - '**.bat' +# - '**.md' + env: # WDK for Windows 11, version 22H2 WDK_URL: https://go.microsoft.com/fwlink/?linkid=2196230 @@ -73,7 +78,7 @@ jobs: # working-directory: ${{env.GITHUB_WORKSPACE}} # run: msbuild /m /p:Configuration="Release MT" /p:Platform=${{matrix.PLATFORM}} /target:zydis /target:zycore /p:OutDir=${{ github.workspace }}/hyperdbg/libraries/zydis/ ${{ github.workspace }}/hyperdbg/dependencies/zydis/msvc/Zydis.sln - - name: Build Hyperdbg solution + - name: Build HyperDbg solution working-directory: ${{env.GITHUB_WORKSPACE}} run: msbuild /m /p:Configuration=${{matrix.BUILD_CONFIGURATION}} /p:Platform=${{matrix.PLATFORM}} ${{env.SOLUTION_FILE_PATH}} diff --git a/.github/workflows/vs2026.yml b/.github/workflows/vs2026.yml new file mode 100644 index 00000000..0e587b16 --- /dev/null +++ b/.github/workflows/vs2026.yml @@ -0,0 +1,99 @@ +name: vs2026-ci + +on: + push: + branches: + - '**' # matches all branch names, including ones with slashes + tags: + - 'v*' + paths-ignore: + - '.gitignore' + - '.gitattributes' + - '**.cmd' + - '**.bat' + - '**.md' + + pull_request: + paths-ignore: + - '.gitignore' + - '.gitattributes' + - '**.cmd' + - '**.bat' + - '**.md' + +env: + SOLUTION_FILE_PATH: ./hyperdbg/hyperdbg.sln + RELEASE_ZIP_FILE_NAME: hyperdbg + BUILD_BIN_DIR: ./hyperdbg/build/bin/ + +jobs: + win-amd64-build: + runs-on: windows-2025-vs2026 + strategy: + matrix: + BUILD_CONFIGURATION: [release, debug] + PLATFORM: [x64] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + + - name: Checkout submodules recursively + run: git submodule update --init --recursive --remote + + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@v3 + + - name: Setup NuGet + uses: NuGet/setup-nuget@v2 + + - name: Restore NuGet packages + run: nuget restore ${{ env.SOLUTION_FILE_PATH }} + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Update vcxproj files + run: python utils/replace-sdk-wdk.py + + - name: Build HyperDbg solution + working-directory: ${{env.GITHUB_WORKSPACE}} + run: msbuild /m /p:Configuration=${{matrix.BUILD_CONFIGURATION}} /p:Platform=${{matrix.PLATFORM}} ${{env.SOLUTION_FILE_PATH}} + + - name: Upload build directory + uses: actions/upload-artifact@v4.4.0 + with: + name: build_files_${{matrix.BUILD_CONFIGURATION}} + path: ${{ env.BUILD_BIN_DIR }} + + deploy-release: + name: Deploy release + needs: win-amd64-build + runs-on: windows-2025-vs2026 + if: startsWith(github.ref, 'refs/tags/') + steps: + - name: Download build files from "build" job + uses: actions/download-artifact@v4.1.7 + with: + name: build_files_release + path: ${{ env.BUILD_BIN_DIR }} + + - name: Archive release + uses: thedoctor0/zip-release@master + with: + path: ${{ env.BUILD_BIN_DIR }}release/ + type: 'zip' + filename: ${{env.RELEASE_ZIP_FILE_NAME}}-${{ github.ref_name }}.zip + + - name: Release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + with: + files: | + ${{env.RELEASE_ZIP_FILE_NAME}}-${{ github.ref_name }}.zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitmodules b/.gitmodules index 8cd3de74..bc4340e1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "hyperdbg/dependencies/pdbex"] - path = hyperdbg/dependencies/pdbex - url = https://github.com/HyperDbg/pdbex.git [submodule "hyperdbg/tests/script-engine-test"] path = hyperdbg/tests/script-engine-test url = https://github.com/HyperDbg/script-engine-test.git @@ -10,6 +7,9 @@ [submodule "hyperdbg/dependencies/ia32-doc"] path = hyperdbg/dependencies/ia32-doc url = https://github.com/HyperDbg/ia32-doc.git +[submodule "hyperdbg/dependencies/pdbex"] + path = hyperdbg/dependencies/pdbex + url = https://github.com/HyperDbg/pdbex.git [submodule "hyperdbg/dependencies/zydis"] path = hyperdbg/dependencies/zydis url = https://github.com/HyperDbg/zydis.git diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b5d5059..f4f7ddac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,47 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.21.0.0] - 2026-07-05 +New release of the HyperDbg Debugger. + +### Added +- Added structure for the hyperperf (Hardware Performance Counter) project ([link](https://github.com/HyperDbg/HyperDbg/commit/c17ebc09c4d199a642352e66feebb3159582196c)) +- The 'unload' command checks whether any module is loaded or not ([link](https://docs.hyperdbg.org/commands/debugging-commands/unload)) +- Exported SDK API for checking whether any module is loaded or not ([link](https://github.com/HyperDbg/HyperDbg/commit/19e47c804f50f5dbb698f314e66b7d685a87ac1f)) +- Added user mode and kernel mode PT parameter parser ([link](https://github.com/HyperDbg/HyperDbg/pull/631)) +- Added thread (TID) and process (PID) helper functions for Intel PT ([link](https://github.com/HyperDbg/HyperDbg/pull/633)) + +### Changed +- Separated SDK libraries for user mode and kernel mode modules ([link](https://github.com/HyperDbg/HyperDbg/commit/c17ebc09c4d199a642352e66feebb3159582196c)) +- Added hypertrace, hyperevade, and hyperperf DLL files to SDK ([link](https://github.com/HyperDbg/HyperDbg/commit/c17ebc09c4d199a642352e66feebb3159582196c)) +- Fix pool manager uninitialize list corruption ([link](https://github.com/HyperDbg/HyperDbg/pull/629)) +- Fix 'access denied' error on loading all modules using 'load all' ([link](https://docs.hyperdbg.org/commands/debugging-commands/load))([link](https://github.com/HyperDbg/HyperDbg/commit/2b818a5116d80d466aab7b343d4aa9d1d640f082)) +- Fix the concatenation error for the "hyperkd" string on the hypertrace project (https://github.com/HyperDbg/HyperDbg/commit/2b0cc18899ff532d9d590a71bf880fee5456c15f)) +- Organized header files for the libhyperdbg project ([link](https://github.com/HyperDbg/HyperDbg/pull/632)) + +## [0.20.0.0-beta] - 2026-06-21 +New release of the HyperDbg Debugger. + +### Added +- Added the libipt library to the project and the SDK +- Added Intel Processor Trace (PT) example app +- Added Linux distinction and pragmas together with new platform functions ([link](https://github.com/HyperDbg/HyperDbg/commit/9c3e0ed23b5f4bb64bd305e546dd0e86ee1910d9)) +- Added interfaces to Linux port for the serial devices ([link](https://github.com/HyperDbg/HyperDbg/commit/31b514f8df40d81d44ae07a2328c02360ce488c3)) +- Added platform IOCTL interface ([link](https://github.com/HyperDbg/HyperDbg/pull/619)) +- Added typecast for wchar, mismatch on Linux and Windows ([link](https://github.com/HyperDbg/HyperDbg/commit/10576b064a9824ab655e7bea0e35e9556ee68ca4)) +- Added missing IOCTLs for Intel PT ([link](https://github.com/HyperDbg/HyperDbg/commit/df12e9fd79851c8f378ec82a45066da510da507a)) +- Added NuGet packages for Windows SDK and WDK +- Added Signal handler platform API for Linux ([link](https://github.com/HyperDbg/HyperDbg/pull/627)) +- Added contribution guidelines for Linux ([link](https://github.com/HyperDbg/HyperDbg/tree/master/hyperdbg/linux#readme)) + +### Changed +- Moved to Visual Studio 2026 ([link](https://github.com/HyperDbg/HyperDbg/pull/626)) +- Zydis and pdbex submodules are updated to VS2026 +- Fix the new form of loading and unloading in the example app and PT app ([link](https://github.com/HyperDbg/HyperDbg/commit/9ad48d30dcf6b409ae86b2d08262584cd06f606e)) +- Check whether the top-level hypervisor is Hyper-V and disable/enable VPIDs based on that, thanks to [@Idov31](https://github.com/Idov31) ([link](https://github.com/HyperDbg/HyperDbg/pull/625)) +- Fix PT operation IOCTL buffer size ([link](https://github.com/HyperDbg/HyperDbg/commit/dabf132d31503843f90949f35f8dce4601d43126)) +- CI/CD updated with NuGet packages for Visual Studio 2026 ([link](https://github.com/HyperDbg/HyperDbg/commit/5a0fad490124268550f955f594d1211d5c74f03d)) + ## [0.19.0.0-beta] - 2026-06-10 New release of the HyperDbg Debugger. @@ -13,7 +54,7 @@ New release of the HyperDbg Debugger. - Added Legacy LBR support to the HyperTrace module - Added Architectural LBR support to the HyperTrace module - Added the '!lbr' command for performing different Last Branch Record (LBR) operations ([link](https://docs.hyperdbg.org/commands/extension-commands/lbr)) -- Added the '!lbrdump' command for dumping saved Last Branch Record (LBR) entries ([link](https://docs.hyperdbg.org/commands/extension-commands/lbrdmp)) +- Added the '!lbrdump' command for dumping saved Last Branch Record (LBR) entries ([link](https://docs.hyperdbg.org/commands/extension-commands/lbrdump)) - Added **lbr_save()** and **lbr_print()** functions in the script engine ([link](https://docs.hyperdbg.org/commands/scripting-language/functions/tracing/lbr/lbr_save))([link](https://docs.hyperdbg.org/commands/scripting-language/functions/tracing/lbr/lbr_print)) - Added mock application for compiling SDK for Linux - Added '!help' alias for the '.help' command diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bc90e82f..4b0d2c53 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,47 +8,13 @@ HyperDbg is a large-scale project that requires a lot of time and effort from th Please make sure to create a [discussion](https://github.com/orgs/HyperDbg/discussions) or an [issue](https://github.com/HyperDbg/HyperDbg/issues), or even better, join the HyperDbg groups ([Telegram](https://t.me/HyperDbg), [Discord](https://discord.gg/anSPsGUtzN), [Matrix](https://matrix.to/#/#hyperdbg-discussion:matrix.org)) on social media. Discuss the way you want to implement your changes and inform developers, because we often see people simultaneously working on the same issue. To avoid this collision, make sure to inform us before you start developing. +### Working on an Idea or a Task +- We have a [wide range of ideas and tasks](https://github.com/orgs/HyperDbg/projects/2) that you might be interested in working on. You can choose a task and start working on it, and if you have any questions or need clarification, feel free to ask. This list will be updated frequently. + +### Other Things to Work on - Writing blog posts and creating videos about use-cases of HyperDbg (make sure to add it to the [awesome](https://github.com/HyperDbg/awesome) repository). - Fixing unresolved GitHub [issues](https://github.com/HyperDbg/HyperDbg/issues). -- Troubleshooting problems with running on Hyper-V's nested virtualization. -- Troubleshooting problems with running on VirtualBox's nested virtualization. -- Supporting KDNET (sending data over the network). -- Enhancing HyperDbg's [Transparent Mode](https://docs.hyperdbg.org/using-hyperdbg/prerequisites/operation-modes#transparent-mode). These features should be added as an extension to the [HyperEvade](https://www.vusec.net/projects/hyperevade/) project (e.g., by bypassing [al-khaser](https://github.com/LordNoteworthy/al-khaser) and similar anti-debugging and anti-hypervisor projects). -- Adding HyperDbg to the system startup using UEFI. -- Creating a QT-based GUI. -- Creating a SoftICE-style GUI. -- Supporting nested-virtualization on HyperDbg itself. -- Protecting HyperDbg code and memory from modification using VT-x capabilities. -- Adding support for the Intel Processor Trace (PT) and event command for detecting coverage | (In progress). -- Creating a wrapper that automatically interprets the [HyperDbg SDK](https://github.com/HyperDbg/HyperDbg/tree/master/hyperdbg/include/SDK) to GO, RUST, C#, Python, etc. -- Creating syntax highlighting for dslang for different IDEs (VSCode, VIM, etc.). -- Building HyperDbg using LLVM clang. -- Helping us start supporting HyperDbg on Linux (discussion needed) | (In progress). -- Helping us start supporting HyperDbg on AMD processors (discussion needed). -- Adding digital (FPGA) modules to the hwdbg hardware debugger. -- Creating a [ret-sync](https://github.com/bootleg/ret-sync) module for HyperDbg. -- Adding fuzzing capabilities to HyperDbg (maybe integrating [AFL++](https://github.com/AFLplusplus/AFLplusplus) into HyperDbg). -- Working on live memory migration and adding support for kernel-mode time travel debugging. -- Integrating the [z3 project](https://github.com/Z3Prover/z3) into HyperDbg and adding commands based on the z3 solver. -- Adding the [Bochs emulator](https://github.com/bochs-emu/Bochs) to HyperDbg. -- Creating different examples of how to use the SDK (using different programming languages). -- Debugging and fixing bugs related to HyperDbg's physical serial communication. -- Reading symbol information from modules in memory (currently, HyperDbg opens a file which continues the debugger). -- Adding APIC virtualization. -- Reading the list of modules for the '[lm](https://docs.hyperdbg.org/commands/debugging-commands/lm)' command directly from kernel-mode. -- Detecting and fixing anti-hypervisor methods described [here](https://github.com/Ahora57/MAJESTY-technologies). -- Investigating why the symbols parser (DIA SDK) could not read symbols of the 'kernel32!*'. -- Creating the 'alias' command that converts or registers scripts as a command, for example: "alias !list .script list.dbg" (discussion needed). -- Adding support for [Hardware Performance Counters (HPC)](https://en.wikipedia.org/wiki/Hardware_performance_counter). -- Any other interesting tasks you might find! - -- ~~Enhancing and adding more features to the ['.pe'](https://docs.hyperdbg.org/commands/meta-commands/.pe) command.~~ Added: [link][link] -- ~~Adding routines to activate and use Last Branch Record (LBR) and Branch Trace Store (BTS).~~ Added: [link][link] -- ~~Creating commands to inspect and read details of PCIe devices.~~ Added: [link][link] -- ~~Mitigating the anti-hypervisor method described [here](https://howtohypervise.blogspot.com/2019/01/a-common-missight-in-most-hypervisors.html).~~ [[Fixed](https://github.com/HyperDbg/HyperDbg/pull/497)] -- ~~Fixing the problem with [XSETBV instruction freezing](https://github.com/HyperDbg/HyperDbg/issues/429).~~ [[Fixed](https://github.com/HyperDbg/HyperDbg/pull/491)] - -This list will be updated frequently. +- Any other interesting ideas you might find! (Let's have a discussion before working on them.) ## Fixing Bugs diff --git a/examples/kernel/hyperdbg_driver/code/driver/Ioctl.c b/examples/kernel/hyperdbg_driver/code/driver/Ioctl.c index 679a8aef..8d7b1c5c 100644 --- a/examples/kernel/hyperdbg_driver/code/driver/Ioctl.c +++ b/examples/kernel/hyperdbg_driver/code/driver/Ioctl.c @@ -34,7 +34,7 @@ DrvDispatchIoControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) // // DO NOT CHANGE CALLING OF THE FOLLOWING FUNCTION // - PoolManagerCheckAndPerformAllocationAndDeallocation(); + // PoolManagerCheckAndPerformAllocationAndDeallocation(); if (g_VmmInitialized) { diff --git a/examples/kernel/hyperdbg_driver/hyperdbg_driver.vcxproj b/examples/kernel/hyperdbg_driver/hyperdbg_driver.vcxproj index 51e06464..fd70d5dc 100644 --- a/examples/kernel/hyperdbg_driver/hyperdbg_driver.vcxproj +++ b/examples/kernel/hyperdbg_driver/hyperdbg_driver.vcxproj @@ -1,5 +1,8 @@  + + + debug @@ -110,7 +113,20 @@ + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/examples/kernel/hyperdbg_driver/hyperdbg_driver.vcxproj.filters b/examples/kernel/hyperdbg_driver/hyperdbg_driver.vcxproj.filters index 2a6aa09a..3345321d 100644 --- a/examples/kernel/hyperdbg_driver/hyperdbg_driver.vcxproj.filters +++ b/examples/kernel/hyperdbg_driver/hyperdbg_driver.vcxproj.filters @@ -56,4 +56,7 @@ code\driver + + + \ No newline at end of file diff --git a/examples/kernel/hyperdbg_driver/packages.config b/examples/kernel/hyperdbg_driver/packages.config new file mode 100644 index 00000000..eb276766 --- /dev/null +++ b/examples/kernel/hyperdbg_driver/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/examples/user/hyperdbg_app/code/hyperdbg-app.cpp b/examples/user/hyperdbg_app/code/hyperdbg-app.cpp index 153b64cb..095876bf 100644 --- a/examples/user/hyperdbg_app/code/hyperdbg-app.cpp +++ b/examples/user/hyperdbg_app/code/hyperdbg-app.cpp @@ -1,175 +1,58 @@ -/** - * @file hyperdbg-app.cpp - * @author Sina Karvandi (sina@hyperdbg.org) - * @brief Controller of the reversing machine's module - * @details - * - * @version 0.2 - * @date 2023-02-01 - * - * @copyright This project is released under the GNU Public License v3. - * - */ #include "pch.h" -PVOID g_SharedMessageBuffer = NULL; - -/** - * @brief Show messages - * - * @param Text - * @return int - */ -int -hyperdbg_show_messages(const char* Text) +static int +ShowMessages(const char * Text) { printf("%s", Text); return 0; } -/** - * @brief Show messages (shared buffer) - * - * @param Text - * @return int - */ -int -hyperdbg_show_messages_shared_buffer() +static int +LoadVmm() { - printf("%s", (char*)g_SharedMessageBuffer); - return 0; + hyperdbg_u_set_text_message_callback((PVOID)ShowMessages); + + if (!hyperdbg_u_detect_vmx_support()) + { + printf("[-] VT-x (VMX) is not supported / enabled on this processor\n"); + return 1; + } + + printf("[*] loading HyperDbg VMM...\n"); + if (hyperdbg_u_install_kd_driver() == 1 || hyperdbg_u_load_vmm() == 1) + { + printf("[-] cannot load the HyperDbg VMM\n"); + return 1; + } + + printf("[+] HyperDbg VMM is running\n"); + + return 0; } -/** - * @brief Load the driver - * - * @return int return zero if it was successful or non-zero if there - * was error - */ int -hyperdbg_load_debugger_mode() +main(int argc, char ** argv) { - char CpuId[13] = { 0 }; + return main2(argc, argv); - // - // Read the vendor string - // - hyperdbg_u_read_vendor_string(CpuId); + if (LoadVmm() != 0) + { + return 1; + } - printf("current processor vendor is : %s\n", CpuId); + hyperdbg_u_run_command((CHAR*)"lm"); - if (strcmp(CpuId, "GenuineIntel") == 0) - { - printf("virtualization technology is vt-x\n"); - } - else - { - printf("this program is not designed to run in a non-VT-x " - "environment !\n"); - return 1; - } + printf("[*] unloading HyperDbg VMM...\n"); - // - // Detect if the processor supports vmx operation - // - if (hyperdbg_u_detect_vmx_support()) - { - printf("vmx operation is supported by your processor\n"); - } - else - { -#ifdef HYPERDBG_ENV_WINDOWS - printf("vmx operation is not supported by your processor " - "(if you are using an Intel processor, it might be because VBS is not disabled!)\n"); -#endif - return 1; - } + // + // Unload the driver + // + hyperdbg_u_unload_vmm(); + hyperdbg_u_unload_kd(); + hyperdbg_u_stop_kd_driver(); + hyperdbg_u_uninstall_kd_driver(); - // - // Set callback function for showing messages - // - hyperdbg_u_set_text_message_callback(hyperdbg_show_messages); + printf("[+] done\n"); - // - // Test interpreter - // - hyperdbg_u_connect_remote_debugger_using_named_pipe("\\\\.\\pipe\\HyperDbgPipe", TRUE); - Sleep(10000); - hyperdbg_u_run_command((CHAR*)"r"); - hyperdbg_u_run_command((CHAR*)".start path c:\\Windows\\system32\\calc.exe"); - hyperdbg_u_continue_debuggee(); - hyperdbg_u_continue_debuggee(); - hyperdbg_u_continue_debuggee(); - hyperdbg_u_continue_debuggee(); - hyperdbg_u_continue_debuggee(); - hyperdbg_u_continue_debuggee(); - hyperdbg_u_continue_debuggee(); - - return 0; -} - - -/** - * @brief Load the driver - * - * @return int return zero if it was successful or non-zero if there - * was error - */ -int -hyperdbg_load_vmi_mode() -{ - - // - // Set callback function for showing messages - // - hyperdbg_u_set_text_message_callback(hyperdbg_show_messages); - - - // - // Test interpreter - // - if (hyperdbg_u_install_kd_driver() == 1 || hyperdbg_u_load_vmm() == 1) - { - printf("failed to install or load the driver\n"); - return 1; - } - - return 0; -} - -/** - * @brief main function - * - * @return int - */ -int -main() -{ - // - // Load the driver in the vmi mode - // - if (hyperdbg_load_vmi_mode() == 0) - { - - // - // *** HyperDbg driver loaded successfully *** - // - - // - // Run a test command - // - hyperdbg_u_run_command((CHAR*)"lm"); - - // - // Unload the driver - // - hyperdbg_u_unload_vmm(); - hyperdbg_u_unload_kd(); - hyperdbg_u_stop_kd_driver(); - hyperdbg_u_uninstall_kd_driver(); - } - else - { - printf("err, in loading HyperDbg\n"); - } + return 0; } diff --git a/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp b/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp new file mode 100644 index 00000000..aacd776f --- /dev/null +++ b/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp @@ -0,0 +1,583 @@ +#include "pch.h" + +#include +#include +#include +#include "../dependencies/libipt/intel-pt.h" +#include + +#pragma comment(lib, "dbghelp.lib") + +static UINT64 g_ImageBase = 0; +static UINT64 g_CodeBase = 0; +static UINT64 g_CodeSize = 0; +static UINT8 * g_Code = NULL; + +static int +ShowMessages(const char * Text) +{ + printf("%s", Text); + return 0; +} + +static int +ReadImage(uint8_t * Buffer, size_t Size, const struct pt_asid * Asid, uint64_t Ip, void * Context) +{ + (void)Asid; + (void)Context; + + if (g_Code == NULL || Ip < g_CodeBase || Ip >= g_CodeBase + g_CodeSize) + return -pte_nomap; + + uint64_t Available = g_CodeBase + g_CodeSize - Ip; + size_t Count = (Size < Available) ? Size : (size_t)Available; + + memcpy(Buffer, g_Code + (Ip - g_CodeBase), Count); + return (int)Count; +} + +typedef struct _PROC_BASIC_INFO +{ + LONG ExitStatus; + PVOID PebBaseAddress; + ULONG_PTR Reserved[4]; +} PROC_BASIC_INFO; + +typedef LONG(NTAPI * PFN_NT_QIP)(HANDLE, ULONG, PVOID, ULONG, PULONG); + +static BOOLEAN +CaptureImage(HANDLE Process, UINT64 * TextStart, UINT64 * TextEnd) +{ + HMODULE Ntdll = GetModuleHandleA("ntdll.dll"); + PFN_NT_QIP NtQip = Ntdll ? (PFN_NT_QIP)GetProcAddress(Ntdll, "NtQueryInformationProcess") : NULL; + PROC_BASIC_INFO Pbi = {0}; + ULONG Ret = 0; + SIZE_T Got = 0; + UINT64 Base = 0; + IMAGE_DOS_HEADER Dos; + IMAGE_NT_HEADERS64 Nt; + UINT64 SectionBase; + + if (NtQip == NULL || NtQip(Process, 0, &Pbi, sizeof(Pbi), &Ret) < 0 || Pbi.PebBaseAddress == NULL) + return FALSE; + + if (!ReadProcessMemory(Process, (PBYTE)Pbi.PebBaseAddress + 0x10, &Base, sizeof(Base), &Got) || Base == 0) + return FALSE; + + if (!ReadProcessMemory(Process, (PVOID)Base, &Dos, sizeof(Dos), &Got) || Dos.e_magic != IMAGE_DOS_SIGNATURE) + return FALSE; + + if (!ReadProcessMemory(Process, (PBYTE)Base + Dos.e_lfanew, &Nt, sizeof(Nt), &Got) || Nt.Signature != IMAGE_NT_SIGNATURE) + return FALSE; + + g_ImageBase = Base; + SectionBase = Base + Dos.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader) + Nt.FileHeader.SizeOfOptionalHeader; + + for (WORD i = 0; i < Nt.FileHeader.NumberOfSections; i++) + { + IMAGE_SECTION_HEADER Section; + + if (!ReadProcessMemory(Process, (PBYTE)SectionBase + (UINT64)i * sizeof(Section), &Section, sizeof(Section), &Got)) + return FALSE; + + if (memcmp(Section.Name, ".text", 6) != 0) + continue; + + UINT64 Start = Base + Section.VirtualAddress; + UINT64 Size = Section.Misc.VirtualSize ? Section.Misc.VirtualSize : Section.SizeOfRawData; + + if (Size == 0) + return FALSE; + + g_Code = (UINT8 *)malloc((size_t)Size); + if (g_Code == NULL) + return FALSE; + + if (!ReadProcessMemory(Process, (PVOID)Start, g_Code, (SIZE_T)Size, &Got) || Got != Size) + { + free(g_Code); + g_Code = NULL; + return FALSE; + } + + g_CodeBase = Start; + g_CodeSize = Size; + *TextStart = Start; + *TextEnd = Start + Size - 1; + return TRUE; + } + + return FALSE; +} + +static BOOLEAN +ResolveFunction(HANDLE Process, const char * Path, const char * Name, UINT64 * Start, UINT64 * End) +{ + union + { + SYMBOL_INFO Info; + BYTE Buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; + } Symbol = {0}; + BOOLEAN Ok = FALSE; + + SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); + if (!SymInitialize(Process, NULL, FALSE)) + return FALSE; + + if (SymLoadModuleEx(Process, NULL, Path, NULL, (DWORD64)g_ImageBase, 0, NULL, 0) != 0) + { + Symbol.Info.SizeOfStruct = sizeof(SYMBOL_INFO); + Symbol.Info.MaxNameLen = MAX_SYM_NAME; + + if (SymFromName(Process, Name, &Symbol.Info) && Symbol.Info.Address != 0) + { + *Start = Symbol.Info.Address; + *End = Symbol.Info.Address + (Symbol.Info.Size ? Symbol.Info.Size : 0x200) - 1; + Ok = TRUE; + } + } + + SymCleanup(Process); + return Ok; +} + +static BOOLEAN +PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE Type) +{ + HYPERTRACE_PT_OPERATION_PACKETS Op = {}; + Op.PtOperationType = Type; + return hyperdbg_u_pt_operation(&Op); +} + +static BOOLEAN +PtFilter(UINT32 ProcessId, UINT64 Start, UINT64 End) +{ + HYPERTRACE_PT_OPERATION_PACKETS Op = {}; + + Op.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER; + Op.FilterOptions.TraceUser = 1; + Op.EnableOptions.Pid = ProcessId; + + if (End > Start) + { + Op.FilterOptions.NumAddrRanges = 1; + Op.FilterOptions.AddrRanges[0].Start = Start; + Op.FilterOptions.AddrRanges[0].End = End; + } + + if (!hyperdbg_u_pt_operation(&Op)) + return FALSE; + + printf("[+] PT filter: cr3=0x%llx traceuser=%u ranges=%u buffer=0x%llx\n", + (unsigned long long)Op.EnableOptions.Cr3, + Op.FilterOptions.TraceUser, + Op.FilterOptions.NumAddrRanges, + (unsigned long long)Op.BufferSize); + return TRUE; +} + +static const char * +PacketName(enum pt_packet_type Type) +{ + switch (Type) + { + case ppt_psb: return "PSB"; case ppt_psbend: return "PSBEND"; case ppt_pad: return "PAD"; + case ppt_fup: return "FUP"; case ppt_tip: return "TIP"; case ppt_tip_pge: return "TIP.PGE"; + case ppt_tip_pgd: return "TIP.PGD"; case ppt_tnt_8: return "TNT8"; case ppt_tnt_64: return "TNT64"; + case ppt_mode: return "MODE"; case ppt_pip: return "PIP"; case ppt_vmcs: return "VMCS"; + case ppt_cbr: return "CBR"; case ppt_tsc: return "TSC"; case ppt_tma: return "TMA"; + case ppt_mtc: return "MTC"; case ppt_cyc: return "CYC"; case ppt_ovf: return "OVF"; + case ppt_stop: return "STOP"; case ppt_exstop: return "EXSTOP"; case ppt_mnt: return "MNT"; + case ppt_ptw: return "PTW"; default: return "?"; + } +} + +static uint64_t +ReconstructIp(const struct pt_packet_ip * Packet, uint64_t * LastIp) +{ + uint64_t Value = *LastIp; + + switch (Packet->ipc) + { + case pt_ipc_update_16: Value = (Value & ~0xffffull) | (Packet->ip & 0xffffull); break; + case pt_ipc_update_32: Value = (Value & ~0xffffffffull) | (Packet->ip & 0xffffffffull); break; + case pt_ipc_update_48: Value = (Value & ~0xffffffffffffull) | (Packet->ip & 0xffffffffffffull); break; + case pt_ipc_sext_48: + Value = Packet->ip & 0xffffffffffffull; + if (Value & 0x800000000000ull) + Value |= 0xffff000000000000ull; + break; + default: Value = Packet->ip; break; + } + + *LastIp = Value; + return Value; +} + +static UINT64 +DecodeCorePackets(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) +{ + struct pt_config Config; + struct pt_packet_decoder * Decoder; + UINT64 Count = 0; + uint64_t LastIp = 0; + int Status; + + pt_config_init(&Config); + Config.begin = (uint8_t *)Buffer; + Config.end = (uint8_t *)Buffer + Size; + + Decoder = pt_pkt_alloc_decoder(&Config); + if (Decoder == NULL) + { + printf("[-] core %u: cannot allocate packet decoder\n", Cpu); + return 0; + } + + for (;;) + { + Status = pt_pkt_sync_forward(Decoder); + if (Status < 0) + break; + + for (;;) + { + struct pt_packet Packet; + + Status = pt_pkt_next(Decoder, &Packet, sizeof(Packet)); + if (Status < 0) + break; + + Count++; + + switch (Packet.type) + { + case ppt_tnt_8: + case ppt_tnt_64: + printf(" %-8s %2u ", PacketName(Packet.type), Packet.payload.tnt.bit_size); + for (uint8_t Bit = 0; Bit < Packet.payload.tnt.bit_size && Bit < 64; Bit++) + putchar(((Packet.payload.tnt.payload >> (Packet.payload.tnt.bit_size - 1 - Bit)) & 1) ? 'T' : 'N'); + putchar('\n'); + break; + + case ppt_tip: + case ppt_fup: + case ppt_tip_pge: + case ppt_tip_pgd: + if (Packet.payload.ip.ipc == pt_ipc_suppressed) + printf(" %-8s (ip suppressed)\n", PacketName(Packet.type)); + else + { + uint64_t Ip = ReconstructIp(&Packet.payload.ip, &LastIp); + printf(" %-8s 0x%016llx exe+0x%llx\n", + PacketName(Packet.type), (unsigned long long)Ip, (unsigned long long)(Ip - g_ImageBase)); + } + break; + + case ppt_pip: + printf(" %-8s cr3=0x%llx\n", PacketName(Packet.type), (unsigned long long)Packet.payload.pip.cr3); + break; + + case ppt_cbr: + //printf(" %-8s ratio=%u\n", PacketName(Packet.type), Packet.payload.cbr.ratio); + break; + + case ppt_tsc: + printf(" %-8s tsc=0x%llx\n", PacketName(Packet.type), (unsigned long long)Packet.payload.tsc.tsc); + break; + + default: + //printf(" %-8s\n", PacketName(Packet.type)); + break; + } + } + } + + pt_pkt_free_decoder(Decoder); + return Count; +} + +static UINT64 +DecodeCore(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) +{ + struct pt_config Config; + struct pt_insn_decoder * Decoder; + struct pt_image * Image; + UINT64 Count = 0; + int Status; + + pt_config_init(&Config); + Config.begin = (uint8_t *)Buffer; + Config.end = (uint8_t *)Buffer + Size; + + Decoder = pt_insn_alloc_decoder(&Config); + if (Decoder == NULL) + { + printf("[-] core %u: cannot allocate instruction decoder\n", Cpu); + return 0; + } + + Image = pt_insn_get_image(Decoder); + pt_image_set_callback(Image, ReadImage, NULL); + + for (;;) + { + Status = pt_insn_sync_forward(Decoder); + if (Status < 0) + break; + + for (;;) + { + struct pt_insn Insn; + + while (Status & pts_event_pending) + { + struct pt_event Event; + Status = pt_insn_event(Decoder, &Event, sizeof(Event)); + if (Status < 0) + break; + } + + if (Status < 0 || (Status & pts_eos)) + break; + + Status = pt_insn_next(Decoder, &Insn, sizeof(Insn)); + if (Status < 0) + break; + + ZydisDisassembledInstruction Disasm; + ZydisMachineMode Mode = (Insn.mode == ptem_32bit) ? ZYDIS_MACHINE_MODE_LEGACY_32 : ZYDIS_MACHINE_MODE_LONG_64; + + if (ZYAN_SUCCESS(ZydisDisassembleIntel(Mode, Insn.ip, Insn.raw, Insn.size, &Disasm))) + printf(" 0x%016llx exe+0x%-6llx %s\n", + (unsigned long long)Insn.ip, + (unsigned long long)(Insn.ip - g_ImageBase), + Disasm.text); + else + printf(" 0x%016llx (undecodable)\n", (unsigned long long)Insn.ip); + + Count++; + } + + if (Status >= 0 && (Status & pts_eos)) + break; + } + + pt_insn_free_decoder(Decoder); + return Count; +} + +static void +RunAndTrace(const char * Path, const char * Function, BOOLEAN Packets, int PinCore) +{ + STARTUPINFOA Startup = {0}; + PROCESS_INFORMATION Process = {0}; + HYPERTRACE_PT_MMAP_PACKETS Mmap = {0}; + HYPERTRACE_PT_OPERATION_PACKETS Sizes = {0}; + UINT64 TextStart = 0; + UINT64 TextEnd = 0; + UINT64 FilterStart = 0; + UINT64 FilterEnd = 0; + UINT64 Total = 0; + + Startup.cb = sizeof(Startup); + + if (!CreateProcessA(Path, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &Startup, &Process)) + { + printf("[-] cannot launch '%s' (error 0x%x)\n", Path, GetLastError()); + return; + } + + printf("[+] launched '%s' (pid %u, suspended)\n", Path, Process.dwProcessId); + + if (PinCore >= 0) + { + DWORD_PTR Mask = (DWORD_PTR)1 << PinCore; + if (SetProcessAffinityMask(Process.hProcess, Mask)) + printf("[+] pinned target to core %d (all trace should land on this core)\n", PinCore); + else + printf("[!] could not pin to core %d (error 0x%x); running unpinned\n", PinCore, GetLastError()); + } + else + { + printf("[*] target unpinned (scheduler may migrate it across cores)\n"); + } + + if (!CaptureImage(Process.hProcess, &TextStart, &TextEnd)) + { + printf("[-] cannot read target image / .text section\n"); + TerminateProcess(Process.hProcess, 1); + goto Cleanup; + } + + printf("[+] image base 0x%llx, .text 0x%llx-0x%llx (%llu bytes)\n", + (unsigned long long)g_ImageBase, + (unsigned long long)TextStart, + (unsigned long long)TextEnd, + (unsigned long long)g_CodeSize); + + FilterStart = TextStart; + FilterEnd = TextEnd; + + if (Function != NULL && ResolveFunction(Process.hProcess, Path, Function, &FilterStart, &FilterEnd)) { + printf("[+] IP filter narrowed to '%s' 0x%llx-0x%llx (%llu bytes)\n", + Function, + (unsigned long long)FilterStart, + (unsigned long long)FilterEnd, + (unsigned long long)(FilterEnd - FilterStart + 1)); + } + else + { + printf("[!] IP filter: whole .text (symbol '%s' not found - build the target with a PDB)\n", + Function ? Function : "(none)"); + } + + if (!PtFilter(Process.dwProcessId, FilterStart, FilterEnd) || + !PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE)) + { + printf("[-] cannot enable Intel PT\n"); + TerminateProcess(Process.hProcess, 1); + goto Cleanup; + } + + if (!hyperdbg_u_pt_mmap(&Mmap)) + { + printf("[-] pt_mmap failed\n"); + PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE); + TerminateProcess(Process.hProcess, 1); + goto Cleanup; + } + + printf("[+] PT enabled, %u per-core buffers mapped\n", Mmap.NumCpus); + printf("[*] resuming target and waiting for it to exit...\n"); + + ResumeThread(Process.hThread); + WaitForSingleObject(Process.hProcess, INFINITE); + printf("[+] target exited, decoding trace\n"); + + PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PAUSE); + + Sizes.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_SIZE; + if (!hyperdbg_u_pt_operation(&Sizes)) + { + printf("[-] cannot query PT sizes\n"); + PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE); + goto Cleanup; + } + + for (UINT32 i = 0; i < Mmap.NumCpus; i++) + { + UINT32 Cpu = Mmap.Cpus[i].CpuId; + UINT64 Bytes = (Cpu < Sizes.NumCpus) ? Sizes.BytesPerCpu[Cpu] : 0; + + if (Bytes == 0) + continue; + + if (Bytes > Mmap.Cpus[i].Size) + Bytes = Mmap.Cpus[i].Size; + + printf("\n[*] core %u: %llu bytes of trace\n", Cpu, (unsigned long long)Bytes); + Total += Packets + ? DecodeCorePackets(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes) + : DecodeCore(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes); + } + + printf("\n[+] decoded %llu %s total\n", (unsigned long long)Total, Packets ? "packet(s)" : "instruction(s)"); + + PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE); + +Cleanup: + if (g_Code != NULL) + { + free(g_Code); + g_Code = NULL; + } + if (Process.hThread != NULL) + CloseHandle(Process.hThread); + if (Process.hProcess != NULL) + CloseHandle(Process.hProcess); +} + +static int +LoadVmmAndTrace() +{ + hyperdbg_u_set_text_message_callback((PVOID)ShowMessages); + + if (!hyperdbg_u_detect_vmx_support()) + { + printf("[-] VT-x (VMX) is not supported / enabled on this processor\n"); + return 1; + } + + printf("[*] loading HyperDbg VMM...\n"); + if (hyperdbg_u_install_kd_driver() == 1 || hyperdbg_u_load_vmm() == 1) + { + printf("[-] cannot load the HyperDbg VMM\n"); + return 1; + } + + printf("[+] HyperDbg VMM is running\n"); + + printf("[*] loading HyperTrace...\n"); + + if ( hyperdbg_u_load_hypertrace_module() == 1) + { + printf("[-] cannot load the HyperDbg HyperTrace\n"); + return 1; + } + + printf("[+] HyperDbg HyperTrace is running\n"); + + return 0; +} + +int +main2(int argc, char ** argv) +{ + const char * function = "main"; + BOOLEAN packets = FALSE; + int pinCore = 0; + + if (argc < 2) + { + printf("HyperDbg Intel PT tracer\n"); + printf("usage: %s [function] [-p] [-c core]\n", argv[0]); + printf(" [function] symbol to IP-filter (default 'main'; pass '*' for whole .text)\n"); + printf(" -p dump raw PT packets (TNT/TIP/FUP/PSB/...) instead of instructions\n"); + printf(" -c core pin the target to this logical core (default 0; -1 = unpinned)\n"); + return 1; + } + + for (int i = 2; i < argc; i++) + { + if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--packets") == 0) + packets = TRUE; + else if (strcmp(argv[i], "-c") == 0 && i + 1 < argc) + pinCore = atoi(argv[++i]); + else if (strcmp(argv[i], "*") == 0) + function = NULL; + else + function = argv[i]; + } + + if (LoadVmmAndTrace() != 0) + { + return 1; + } + + RunAndTrace(argv[1], function, packets, pinCore); + + printf("[*] unloading HyperDbg VMM...\n"); + + // + // Unload the driver + // + hyperdbg_u_unload_vmm(); + hyperdbg_u_unload_kd(); + hyperdbg_u_stop_kd_driver(); + hyperdbg_u_uninstall_kd_driver(); + + printf("[+] done\n"); + + return 0; +} diff --git a/examples/user/hyperdbg_app/header/example-ipt.h b/examples/user/hyperdbg_app/header/example-ipt.h new file mode 100644 index 00000000..90f00b87 --- /dev/null +++ b/examples/user/hyperdbg_app/header/example-ipt.h @@ -0,0 +1,16 @@ +/** + * @file example-ipt.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Headers for Intel PT example + * @details + * + * @version 0.20 + * @date 2026-06-13 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +int +main2(int argc, char** argv); \ No newline at end of file diff --git a/examples/user/hyperdbg_app/header/pch.h b/examples/user/hyperdbg_app/header/pch.h index f62bfc90..d4bbf789 100644 --- a/examples/user/hyperdbg_app/header/pch.h +++ b/examples/user/hyperdbg_app/header/pch.h @@ -38,3 +38,8 @@ // #include "SDK/HyperDbgSdk.h" #include "SDK/imports/user/HyperDbgLibImports.h" + +// +// Other internal headers +// +#include "example-ipt.h" diff --git a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj index eecf0a79..3072d9ac 100644 --- a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj +++ b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj @@ -11,9 +11,11 @@ + + @@ -28,13 +30,13 @@ Application true - v143 + v145 Unicode Application false - v143 + v145 true Unicode @@ -49,33 +51,49 @@ - + + + C:\Users\masra\Desktop\libipt + $(SolutionDir)build\bin\$(Configuration)\ $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + false $(SolutionDir)build\bin\$(Configuration)\ $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + true Level3 true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + _DEBUG;_CONSOLE;ZYCORE_STATIC_BUILD;ZYDIS_STATIC_BUILD;%(PreprocessorDefinitions) true MultiThreadedDebug Create pch.h - $(SolutionDir)include;$(ProjectDir)header;%(AdditionalIncludeDirectories) + $(LibIptDir)\include;$(SolutionDir)include;$(SolutionDir)dependencies\zydis\include;$(SolutionDir)dependencies\zydis\dependencies\zycore\include;$(ProjectDir)header;%(AdditionalIncludeDirectories) true Console true - $(SolutionDir)build\bin\$(Configuration)\libhyperdbg.lib - true + $(LibIptDir)\lib;%(AdditionalLibraryDirectories) + $(SolutionDir)build\bin\$(Configuration)\libhyperdbg.lib;$(SolutionDir)libraries\zydis\user\Zydis.lib;$(SolutionDir)libraries\zydis\user\Zycore.lib;$(SolutionDir)libraries\libipt\libipt.lib + false + + + + + + @@ -83,12 +101,12 @@ true true true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + NDEBUG;_CONSOLE;ZYCORE_STATIC_BUILD;ZYDIS_STATIC_BUILD;%(PreprocessorDefinitions) true MultiThreaded Create pch.h - $(SolutionDir)include;$(ProjectDir)header;%(AdditionalIncludeDirectories) + $(LibIptDir)\include;$(SolutionDir)include;$(SolutionDir)dependencies\zydis\include;$(SolutionDir)dependencies\zydis\dependencies\zycore\include;$(ProjectDir)header;%(AdditionalIncludeDirectories) true MaxSpeed @@ -97,9 +115,16 @@ true true true - $(SolutionDir)build\bin\$(Configuration)\libhyperdbg.lib + $(LibIptDir)\lib;%(AdditionalLibraryDirectories) + $(SolutionDir)build\bin\$(Configuration)\libhyperdbg.lib;$(SolutionDir)libraries\zydis\user\Zydis.lib;$(SolutionDir)libraries\zydis\user\Zycore.lib;$(SolutionDir)libraries\libipt\libipt.lib true + + + + + + diff --git a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj.filters b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj.filters index 8a2081a1..d7dd0c26 100644 --- a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj.filters +++ b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj.filters @@ -15,8 +15,14 @@ header + + header + + + code + code diff --git a/hyperdbg/CMakeLists.txt b/hyperdbg/CMakeLists.txt index dce26678..ba5b3855 100644 --- a/hyperdbg/CMakeLists.txt +++ b/hyperdbg/CMakeLists.txt @@ -7,7 +7,6 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ON) if(LINUX) - add_subdirectory(script-engine) message(STATUS "Building on Linux") elseif(WIN32) message(STATUS "Building on Windows") @@ -53,36 +52,30 @@ target_link_libraries(hyperhv Zycore Zydis) add_subdirectory(hyperkd) target_link_libraries(hyperkd hyperlog hyperhv kdserial) +endif() -add_subdirectory(dependencies/pdbex/Source) +#add_subdirectory(dependencies/pdbex/Source) -add_subdirectory(symbol-parser) -target_link_libraries(symbol-parser pdbex) +#add_subdirectory(symbol-parser) +#target_link_libraries(symbol-parser pdbex) +#target_link_libraries(script-engine symbol-parser) -target_link_libraries(script-engine symbol-parser) - -link_directories(libraries/zydis/user libraries/keystone/release-lib) -add_subdirectory(libhyperdbg) -target_link_libraries(libhyperdbg Zycore Zydis script-engine keystone) - - -add_subdirectory(hyperdbg-test) +#add_subdirectory(hyperdbg-test) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") -add_subdirectory(symbol-parser) -target_link_libraries(symbol-parser pdbex) +#add_subdirectory(symbol-parser) +#target_link_libraries(symbol-parser pdbex) +#add_subdirectory(script-engine) +#target_link_libraries(script-engine symbol-parser) add_subdirectory(script-engine) -target_link_libraries(script-engine symbol-parser) - link_directories(libraries/zydis/user libraries/keystone/release-lib) add_subdirectory(libhyperdbg) -target_link_libraries(libhyperdbg Zycore Zydis script-engine keystone) -endif() - +find_package(Threads REQUIRED) +target_link_libraries(libhyperdbg Zycore Zydis script-engine keystone Threads::Threads) add_subdirectory(hyperdbg-cli) target_link_libraries(hyperdbg-cli libhyperdbg) diff --git a/hyperdbg/dependencies/libipt/intel-pt.h b/hyperdbg/dependencies/libipt/intel-pt.h new file mode 100644 index 00000000..4b90482c --- /dev/null +++ b/hyperdbg/dependencies/libipt/intel-pt.h @@ -0,0 +1,3177 @@ +/* + * Copyright (C) 2013-2026 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef INTEL_PT_H +#define INTEL_PT_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Intel(R) Processor Trace (Intel PT) decoder library. + * + * This file is logically structured into the following sections: + * + * - Version + * - Errors + * - Configuration + * - Packet encoder / decoder + * - Event decoder + * - Query decoder + * - Traced image + * - Instruction flow decoder + * - Block decoder + */ + + + +struct pt_encoder; +struct pt_packet_decoder; +struct pt_event_decoder; +struct pt_query_decoder; +struct pt_insn_decoder; +struct pt_block_decoder; + + + +/* A macro to mark functions as exported. */ +#ifndef pt_export +# if defined(__GNUC__) +# define pt_export __attribute__((visibility("default"))) +# elif defined(_MSC_VER) +# define pt_export __declspec(dllimport) +# else +# error "unknown compiler" +# endif +#endif + + + +/* Version. */ + + +/** The header version. */ +#define LIBIPT_VERSION_MAJOR 2 +#define LIBIPT_VERSION_MINOR 3 +#define LIBIPT_VERSION_PATCH 0 + +#define LIBIPT_VERSION ((LIBIPT_VERSION_MAJOR << 8) + LIBIPT_VERSION_MINOR) + + +/** The library version. */ +struct pt_version { + /** Major version number. */ + uint8_t major; + + /** Minor version number. */ + uint8_t minor; + + /** Patch level. */ + uint16_t patch; + + /** Build number. */ + uint32_t build; + + /** Version extension. */ + const char *ext; +}; + + +/** Return the library version. */ +extern pt_export struct pt_version pt_library_version(void); + + + +/* Errors. */ + + + +/** Error codes. */ +enum pt_error_code { + /* No error. Everything is OK. */ + pte_ok, + + /* Internal decoder error. */ + pte_internal, + + /* Invalid argument. */ + pte_invalid, + + /* Decoder out of sync. */ + pte_nosync, + + /* Unknown opcode. */ + pte_bad_opc, + + /* Unknown payload. */ + pte_bad_packet, + + /* Unexpected packet context. */ + pte_bad_context, + + /* Decoder reached end of trace stream. */ + pte_eos, + + /* No packet matching the query to be found. */ + pte_bad_query, + + /* Decoder out of memory. */ + pte_nomem, + + /* Bad configuration. */ + pte_bad_config, + + /* There is no IP. */ + pte_noip, + + /* The IP has been suppressed. */ + pte_ip_suppressed, + + /* There is no memory mapped at the requested address. */ + pte_nomap, + + /* An instruction could not be decoded. */ + pte_bad_insn, + + /* No wall-clock time is available. */ + pte_no_time, + + /* No core:bus ratio available. */ + pte_no_cbr, + + /* Bad traced image. */ + pte_bad_image, + + /* A locking error. */ + pte_bad_lock, + + /* The requested feature is not supported. */ + pte_not_supported, + + /* The return address stack is empty. */ + pte_retstack_empty, + + /* A compressed return is not indicated correctly by a taken branch. */ + pte_bad_retcomp, + + /* The current decoder state does not match the state in the trace. */ + pte_bad_status_update, + + /* The trace did not contain an expected enabled event. */ + pte_no_enable, + + /* An event was ignored. */ + pte_event_ignored, + + /* Something overflowed. */ + pte_overflow, + + /* A file handling error. */ + pte_bad_file, + + /* Unknown cpu. */ + pte_bad_cpu +}; + + +/** Decode a function return value into an pt_error_code. */ +static inline enum pt_error_code pt_errcode(int status) +{ + return (status >= 0) ? pte_ok : (enum pt_error_code) -status; +} + +/** Return a human readable error string. */ +extern pt_export const char *pt_errstr(enum pt_error_code); + + + +/* Configuration. */ + + + +/** A cpu vendor. */ +enum pt_cpu_vendor { + pcv_unknown, + pcv_intel +}; + +/** A cpu identifier. */ +struct pt_cpu { + /** The cpu vendor. */ + enum pt_cpu_vendor vendor; + + /** The cpu family. */ + uint16_t family; + + /** The cpu model. */ + uint8_t model; + + /** The stepping. */ + uint8_t stepping; +}; + +/** A collection of Intel PT errata. */ +struct pt_errata { + /** BDM70: Intel(R) Processor Trace PSB+ Packets May Contain + * Unexpected Packets. + * + * Same as: SKD024, SKL021, KBL021. + * + * Some Intel Processor Trace packets should be issued only between + * TIP.PGE and TIP.PGD packets. Due to this erratum, when a TIP.PGE + * packet is generated it may be preceded by a PSB+ that incorrectly + * includes FUP and MODE.Exec packets. + */ + uint32_t bdm70:1; + + /** BDM64: An Incorrect LBR or Intel(R) Processor Trace Packet May Be + * Recorded Following a Transactional Abort. + * + * Use of Intel(R) Transactional Synchronization Extensions (Intel(R) + * TSX) may result in a transactional abort. If an abort occurs + * immediately following a branch instruction, an incorrect branch + * target may be logged in an LBR (Last Branch Record) or in an Intel(R) + * Processor Trace (Intel(R) PT) packet before the LBR or Intel PT + * packet produced by the abort. + */ + uint32_t bdm64:1; + + /** SKD007: Intel(R) PT Buffer Overflow May Result in Incorrect Packets. + * + * Same as: SKL049, KBL041. + * + * Under complex micro-architectural conditions, an Intel PT (Processor + * Trace) OVF (Overflow) packet may be issued after the first byte of a + * multi-byte CYC (Cycle Count) packet, instead of any remaining bytes + * of the CYC. + */ + uint32_t skd007:1; + + /** SKD022: VM Entry That Clears TraceEn May Generate a FUP. + * + * Same as: SKL024, KBL023. + * + * If VM entry clears Intel(R) PT (Intel Processor Trace) + * IA32_RTIT_CTL.TraceEn (MSR 570H, bit 0) while PacketEn is 1 then a + * FUP (Flow Update Packet) will precede the TIP.PGD (Target IP Packet, + * Packet Generation Disable). VM entry can clear TraceEn if the + * VM-entry MSR-load area includes an entry for the IA32_RTIT_CTL MSR. + */ + uint32_t skd022:1; + + /** SKD010: Intel(R) PT FUP May be Dropped After OVF. + * + * Same as: SKD014, SKL033, KBL030. + * + * Some Intel PT (Intel Processor Trace) OVF (Overflow) packets may not + * be followed by a FUP (Flow Update Packet) or TIP.PGE (Target IP + * Packet, Packet Generation Enable). + */ + uint32_t skd010:1; + + /** SKL014: Intel(R) PT TIP.PGD May Not Have Target IP Payload. + * + * Same as: KBL014. + * + * When Intel PT (Intel Processor Trace) is enabled and a direct + * unconditional branch clears IA32_RTIT_STATUS.FilterEn (MSR 571H, bit + * 0), due to this erratum, the resulting TIP.PGD (Target IP Packet, + * Packet Generation Disable) may not have an IP payload with the target + * IP. + */ + uint32_t skl014:1; + + /** APL12: Intel(R) PT OVF May Be Followed By An Unexpected FUP Packet. + * + * Certain Intel PT (Processor Trace) packets including FUPs (Flow + * Update Packets), should be issued only between TIP.PGE (Target IP + * Packet - Packet Generation Enable) and TIP.PGD (Target IP Packet - + * Packet Generation Disable) packets. When outside a TIP.PGE/TIP.PGD + * pair, as a result of IA32_RTIT_STATUS.FilterEn[0] (MSR 571H) being + * cleared, an OVF (Overflow) packet may be unexpectedly followed by a + * FUP. + */ + uint32_t apl12:1; + + /** APL11: Intel(R) PT OVF Packet May Be Followed by TIP.PGD Packet + * + * If Intel PT (Processor Trace) encounters an internal buffer overflow + * and generates an OVF (Overflow) packet just as IA32_RTIT_CTL (MSR + * 570H) bit 0 (TraceEn) is cleared, or during a far transfer that + * causes IA32_RTIT_STATUS.ContextEn[1] (MSR 571H) to be cleared, the + * OVF may be followed by a TIP.PGD (Target Instruction Pointer - Packet + * Generation Disable) packet. + */ + uint32_t apl11:1; + + /** SKL168: Intel(R) PT CYC Packets Can be Dropped When Immediately + * Preceding PSB + * + * Due to a rare microarchitectural condition, generation of an Intel + * PT (Processor Trace) PSB (Packet Stream Boundary) packet can cause a + * single CYC (Cycle Count) packet, possibly along with an associated + * MTC (Mini Time Counter) packet, to be dropped. + */ + uint32_t skl168:1; + + /** SKZ84: Use of VMX TSC Scaling or TSC Offsetting Will Result in + * Corrupted Intel PT Packets + * + * When Intel(R) PT (Processor Trace) is enabled within a VMX (Virtual + * Machine Extensions) guest, and TSC (Time Stamp Counter) offsetting + * or TSC scaling is enabled for that guest, by setting primary + * processor-based execution control bit 3 or secondary processor-based + * execution control bit 25, respectively, in the VMCS (Virtual Machine + * Control Structure) for that guest, any TMA (TSC(MTC Alignment) + * packet generated will have corrupted values in the CTC (Core Timer + * Copy) and FastCounter fields. Additionally, the corrupted TMA + * packet will be followed by a bogus data byte. + */ + uint32_t skz84:1; + + /* Reserve a few bytes for the future. */ + uint32_t reserved[15]; +}; + +/** A collection of decoder-specific configuration flags. */ +struct pt_conf_flags { + /** The decoder variant. */ + union { + /** Flags for the block decoder. */ + struct { + /** End a block after a call instruction. */ + uint32_t end_on_call:1; + + /** Enable tick events for timing updates. */ + uint32_t enable_tick_events:1; + + /** End a block after a jump instruction. */ + uint32_t end_on_jump:1; + +#if (LIBIPT_VERSION >= 0x201) + /** Preserve timing calibration on overflow. */ + uint32_t keep_tcal_on_ovf:1; + + /** Enable iflags events. + * + * Use this only when Event Tracing was enabled via + * IA32_RTIT_CTL[31]. + */ + uint32_t enable_iflags_events:1; +#endif + } block; + + /** Flags for the instruction flow decoder. */ + struct { + /** Enable tick events for timing updates. */ + uint32_t enable_tick_events:1; + +#if (LIBIPT_VERSION >= 0x201) + /** Preserve timing calibration on overflow. */ + uint32_t keep_tcal_on_ovf:1; + + /** Enable iflags events. + * + * Use this only when Event Tracing was enabled via + * IA32_RTIT_CTL[31]. + */ + uint32_t enable_iflags_events:1; +#endif + } insn; + +#if (LIBIPT_VERSION >= 0x201) + /** Flags for the query decoder. */ + struct { + /** Preserve timing calibration on overflow. */ + uint32_t keep_tcal_on_ovf:1; + + /** Enable iflags events. + * + * Use this only when Event Tracing was enabled via + * IA32_RTIT_CTL[31]. + */ + uint32_t enable_iflags_events:1; + } query; + + /** Flags for the event decoder. */ + struct { + /** Preserve timing calibration on overflow. */ + uint32_t keep_tcal_on_ovf:1; + + /** Enable iflags events. + * + * Use this only when Event Tracing was enabled via + * IA32_RTIT_CTL[31]. + */ + uint32_t enable_iflags_events:1; + } event; +#endif /* (LIBIPT_VERSION >= 0x201) */ + + /* Reserve a few bytes for future extensions. */ + uint32_t reserved[4]; + } variant; +}; + +/** The address filter configuration. */ +struct pt_conf_addr_filter { + /** The address filter configuration. + * + * This corresponds to the respective fields in IA32_RTIT_CTL MSR. + */ + union { + uint64_t addr_cfg; + + struct { + uint32_t addr0_cfg:4; + uint32_t addr1_cfg:4; + uint32_t addr2_cfg:4; + uint32_t addr3_cfg:4; + } ctl; + } config; + + /** The address ranges configuration. + * + * This corresponds to the IA32_RTIT_ADDRn_A/B MSRs. + */ + uint64_t addr0_a; + uint64_t addr0_b; + uint64_t addr1_a; + uint64_t addr1_b; + uint64_t addr2_a; + uint64_t addr2_b; + uint64_t addr3_a; + uint64_t addr3_b; + + /* Reserve some space. */ + uint64_t reserved[8]; +}; + +/** An unknown packet. */ +struct pt_packet_unknown; + +/** An Intel PT decoder configuration. + */ +struct pt_config { + /** The size of the config structure in bytes. */ + size_t size; + + /** The trace buffer begin address. */ + uint8_t *begin; + + /** The trace buffer end address. */ + uint8_t *end; + + /** An optional callback for handling unknown packets. + * + * If \@callback is not NULL, it is called for any unknown opcode. + */ + struct { + /** The callback function. + * + * It shall decode the packet at \@pos into \@unknown. + * It shall return the number of bytes read upon success. + * It shall return a negative pt_error_code otherwise. + * The below context is passed as \@context. + */ + int (*callback)(struct pt_packet_unknown *unknown, + const struct pt_config *config, + const uint8_t *pos, void *context); + + /** The user-defined context for this configuration. */ + void *context; + } decode; + + /** The cpu on which Intel PT has been recorded. */ + struct pt_cpu cpu; + + /** The errata to apply when encoding or decoding Intel PT. */ + struct pt_errata errata; + + /* The CTC frequency. + * + * This is only required if MTC packets have been enabled in + * IA32_RTIT_CTRL.MTCEn. + */ + uint32_t cpuid_0x15_eax, cpuid_0x15_ebx; + + /* The MTC frequency as defined in IA32_RTIT_CTL.MTCFreq. + * + * This is only required if MTC packets have been enabled in + * IA32_RTIT_CTRL.MTCEn. + */ + uint8_t mtc_freq; + + /* The nominal frequency as defined in MSR_PLATFORM_INFO[15:8]. + * + * This is only required if CYC packets have been enabled in + * IA32_RTIT_CTRL.CYCEn. + * + * If zero, timing calibration will only be able to use MTC and CYC + * packets. + * + * If not zero, timing calibration will also be able to use CBR + * packets. + */ + uint8_t nom_freq; + + /** A collection of decoder-specific flags. */ + struct pt_conf_flags flags; + + /** The address filter configuration. */ + struct pt_conf_addr_filter addr_filter; +}; + + +/** Zero-initialize an Intel PT configuration. */ +static inline void pt_config_init(struct pt_config *config) +{ + memset(config, 0, sizeof(*config)); + + config->size = sizeof(*config); +} + +/** Determine errata for a given cpu. + * + * Updates \@errata based on \@cpu. + * + * Returns 0 on success, a negative error code otherwise. + * Returns -pte_invalid if \@errata or \@cpu is NULL. + * Returns -pte_bad_cpu if \@cpu is not known. + */ +extern pt_export int pt_cpu_errata(struct pt_errata *errata, + const struct pt_cpu *cpu); + + + +/* Packet encoder / decoder. */ + + + +/** Intel PT packet types. */ +enum pt_packet_type { + /* An invalid packet. */ + ppt_invalid, + + /* A packet decodable by the optional decoder callback. */ + ppt_unknown, + + /* Actual packets supported by this library. */ + ppt_pad, + ppt_psb, + ppt_psbend, + ppt_fup, + ppt_tip, + ppt_tip_pge, + ppt_tip_pgd, + ppt_tnt_8, + ppt_tnt_64, + ppt_mode, + ppt_pip, + ppt_vmcs, + ppt_cbr, + ppt_tsc, + ppt_tma, + ppt_mtc, + ppt_cyc, + ppt_stop, + ppt_ovf, + ppt_mnt, + ppt_exstop, + ppt_mwait, + ppt_pwre, + ppt_pwrx, + ppt_ptw, +#if (LIBIPT_VERSION >= 0x201) + ppt_cfe, + ppt_evd, +#endif +#if (LIBIPT_VERSION >= 0x202) + ppt_trig, +#endif +}; + +/** The IP compression. */ +enum pt_ip_compression { + /* The bits encode the payload size and the encoding scheme. + * + * No payload. The IP has been suppressed. + */ + pt_ipc_suppressed = 0x0, + + /* Payload: 16 bits. Update last IP. */ + pt_ipc_update_16 = 0x01, + + /* Payload: 32 bits. Update last IP. */ + pt_ipc_update_32 = 0x02, + + /* Payload: 48 bits. Sign extend to full address. */ + pt_ipc_sext_48 = 0x03, + + /* Payload: 48 bits. Update last IP. */ + pt_ipc_update_48 = 0x04, + + /* Payload: 64 bits. Full address. */ + pt_ipc_full = 0x06 +}; + +/** An execution mode. */ +enum pt_exec_mode { + ptem_unknown, + ptem_16bit, + ptem_32bit, + ptem_64bit +}; + +/** Mode packet leaves. */ +enum pt_mode_leaf { + pt_mol_exec = 0x00, + pt_mol_tsx = 0x20 +}; + +/** A TNT-8 or TNT-64 packet. */ +struct pt_packet_tnt { + /** TNT payload bit size. */ + uint8_t bit_size; + + /** TNT payload excluding stop bit. */ + uint64_t payload; +}; + +/** A packet with IP payload. */ +struct pt_packet_ip { + /** IP compression. */ + enum pt_ip_compression ipc; + + /** Zero-extended payload ip. */ + uint64_t ip; +}; + +/** A mode.exec packet. */ +struct pt_packet_mode_exec { + /** The mode.exec csl bit. */ + uint32_t csl:1; + + /** The mode.exec csd bit. */ + uint32_t csd:1; + +#if (LIBIPT_VERSION >= 0x201) + /** The mode.exec if bit. + * + * Enable Event Tracing to get updates when IF changes. + */ + uint32_t iflag:1; +#endif +}; + +static inline enum pt_exec_mode +pt_get_exec_mode(const struct pt_packet_mode_exec *packet) +{ + if (packet->csl) + return packet->csd ? ptem_unknown : ptem_64bit; + else + return packet->csd ? ptem_32bit : ptem_16bit; +} + +static inline struct pt_packet_mode_exec +pt_set_exec_mode(enum pt_exec_mode mode) +{ + struct pt_packet_mode_exec packet; + + memset(&packet, 0, sizeof(packet)); + switch (mode) { + default: + packet.csl = 1; + packet.csd = 1; + break; + + case ptem_64bit: + packet.csl = 1; + break; + + case ptem_32bit: + packet.csd = 1; + break; + + case ptem_16bit: + break; + } + + return packet; +} + +/** A mode.tsx packet. */ +struct pt_packet_mode_tsx { + /** The mode.tsx intx bit. */ + uint32_t intx:1; + + /** The mode.tsx abrt bit. */ + uint32_t abrt:1; +}; + +/** A mode packet. */ +struct pt_packet_mode { + /** Mode leaf. */ + enum pt_mode_leaf leaf; + + /** Mode bits. */ + union { + /** Packet: mode.exec. */ + struct pt_packet_mode_exec exec; + + /** Packet: mode.tsx. */ + struct pt_packet_mode_tsx tsx; + } bits; +}; + +/** A PIP packet. */ +struct pt_packet_pip { + /** The CR3 value. */ + uint64_t cr3; + + /** The non-root bit. */ + uint32_t nr:1; +}; + +/** A TSC packet. */ +struct pt_packet_tsc { + /** The TSC value. */ + uint64_t tsc; +}; + +/** A CBR packet. */ +struct pt_packet_cbr { + /** The core/bus cycle ratio. */ + uint8_t ratio; +}; + +/** A TMA packet. */ +struct pt_packet_tma { + /** The crystal clock tick counter value. */ + uint16_t ctc; + + /** The fast counter value. */ + uint16_t fc; +}; + +/** A MTC packet. */ +struct pt_packet_mtc { + /** The crystal clock tick counter value. */ + uint8_t ctc; +}; + +/** A CYC packet. */ +struct pt_packet_cyc { + /** The cycle counter value. */ + uint64_t value; +}; + +/** A VMCS packet. */ +struct pt_packet_vmcs { + /* The VMCS Base Address (i.e. the shifted payload). */ + uint64_t base; +}; + +/** A MNT packet. */ +struct pt_packet_mnt { + /** The raw payload. */ + uint64_t payload; +}; + +/** A EXSTOP packet. */ +struct pt_packet_exstop { + /** A flag specifying the binding of the packet: + * + * set: binds to the next FUP. + * clear: standalone. + */ + uint32_t ip:1; +}; + +/** A MWAIT packet. */ +struct pt_packet_mwait { + /** The MWAIT hints (EAX). */ + uint32_t hints; + + /** The MWAIT extensions (ECX). */ + uint32_t ext; +}; + +/** A PWRE packet. */ +struct pt_packet_pwre { + /** The resolved thread C-state. */ + uint8_t state; + + /** The resolved thread sub C-state. */ + uint8_t sub_state; + + /** A flag indicating whether the C-state entry was initiated by h/w. */ + uint32_t hw:1; +}; + +/** A PWRX packet. */ +struct pt_packet_pwrx { + /** The core C-state at the time of the wake. */ + uint8_t last; + + /** The deepest core C-state achieved during sleep. */ + uint8_t deepest; + + /** The wake reason: + * + * - due to external interrupt received. + */ + uint32_t interrupt:1; + + /** - due to store to monitored address. */ + uint32_t store:1; + + /** - due to h/w autonomous condition such as HDC. */ + uint32_t autonomous:1; +}; + +/** A PTW packet. */ +struct pt_packet_ptw { + /** The raw payload. */ + uint64_t payload; + + /** The payload size as encoded in the packet. */ + uint8_t plc; + + /** A flag saying whether a FUP is following PTW that provides + * the IP of the corresponding PTWRITE instruction. + */ + uint32_t ip:1; +}; + +static inline int pt_ptw_size(uint8_t plc) +{ + switch (plc) { + case 0: + return 4; + + case 1: + return 8; + + case 2: + case 3: + return -pte_bad_packet; + } + + return -pte_internal; +} + +#if (LIBIPT_VERSION >= 0x201) +/* Control-flow event types. */ +enum pt_cfe_type { + pt_cfe_intr = 0x01, + pt_cfe_iret = 0x02, + pt_cfe_smi = 0x03, + pt_cfe_rsm = 0x04, + pt_cfe_sipi = 0x05, + pt_cfe_init = 0x06, + pt_cfe_vmentry = 0x07, + pt_cfe_vmexit = 0x08, + pt_cfe_vmexit_intr = 0x09, + pt_cfe_shutdown = 0x0a, + pt_cfe_uintr = 0x0c, + pt_cfe_uiret = 0x0d, +#if (LIBIPT_VERSION >= 0x202) + pt_cfe_swintr = 0x0e, + pt_cfe_syscall = 0x0f, +#endif +}; + +/* Interrupt vectors defined in Table 6-1 in §6.5 of Vol.1 of the SDM. */ +enum pt_cfe_intr { + pt_cfe_intr_de = 0, /* Divide Error. */ + pt_cfe_intr_db = 1, /* Debug. */ + pt_cfe_intr_nmi = 2, /* Non Maskable Interrupt. */ + pt_cfe_intr_bp = 3, /* Breakpoint. */ + pt_cfe_intr_of = 4, /* Overflow. */ + pt_cfe_intr_br = 5, /* Bound Range Exceeded. */ + pt_cfe_intr_ud = 6, /* Undefined Opcode. */ + pt_cfe_intr_nm = 7, /* Device Not Available. */ + pt_cfe_intr_df = 8, /* Double Fault. */ + pt_cfe_intr_ts = 10, /* Invalid TSS. */ + pt_cfe_intr_np = 11, /* Segment Not Present. */ + pt_cfe_intr_ss = 12, /* Stack Segment Fault. */ + pt_cfe_intr_gp = 13, /* General Protection Fault. */ + pt_cfe_intr_pf = 14, /* Page Fault. */ + pt_cfe_intr_mf = 16, /* Math Fault. */ + pt_cfe_intr_ac = 17, /* Alignment Check. */ + pt_cfe_intr_mc = 18, /* Machine Check. */ + pt_cfe_intr_xm = 19, /* SIMD Floating Point Exception. */ + pt_cfe_intr_ve = 20, /* Virtualization Exception. */ + pt_cfe_intr_cp = 21, /* Control Protection Exception. */ +}; + +/** A CFE packet. */ +struct pt_packet_cfe { + /** The type of control-flow event. */ + enum pt_cfe_type type; + + /** The vector depending on the type: + * + * pt_cfe_intr: the event vector. + * pt_cfe_vmexit_intr: the event vector. + * pt_cfe_sipi: the SIPI vector. + * pt_cfe_uintr: the user interrupt vector. + * pt_cfe_swintr: the event vector. + */ + uint8_t vector; + + /** A flag specifying the binding of the packet: + * + * set: binds to the next FUP. + * clear: binds to the next FUP + TIP, if tracing enabled. + * clear: standalone, if tracing disabled. + */ + uint32_t ip:1; +}; + +/* Event data types. */ +enum pt_evd_type { + pt_evd_cr2 = 0x00, /* Page fault linear address (cr2). */ + pt_evd_vmxq = 0x01, /* VMX exit qualification. */ + pt_evd_vmxr = 0x02, /* VMX exit reason. */ +}; + +/** A EVD packet. */ +struct pt_packet_evd { + /** The type of control-flow event. */ + enum pt_evd_type type; + + /** The payload depending on the type: + * + * 0x00: page fault linear address (cr2). + * 0x01: vmx exit qualification. + * 0x02: vmx exit reason. + */ + uint64_t payload; +}; +#endif /* (LIBIPT_VERSION >= 0x201) */ + +#if (LIBIPT_VERSION >= 0x202) +/** A TRIG packet. */ +struct pt_packet_trig { + /** A bit vector of triggers that are represented by this packet. */ + uint8_t trbv; + + /** An instruction count from the last IP packet (FUP, TIP*, or TNT) + * indicating the instruction to which this packet is attributed. + * + * This field is only valid if \@icntv is set. + */ + uint16_t icnt; + + /** A flag saying whether a FUP is following TRIG that provides a + * reference IP from which to start counting. + */ + uint32_t ip:1; + + /** A flag saying whether the \@icnt field is valid.*/ + uint32_t icntv:1; + + /** A flag saying whether there were other triggers firing that are not + * reported. + */ + uint32_t mult:1; +}; +#endif /* (LIBIPT_VERSION >= 0x202) */ + +/** An unknown packet decodable by the optional decoder callback. */ +struct pt_packet_unknown { + /** Pointer to the raw packet bytes. */ + const uint8_t *packet; + + /** Optional pointer to a user-defined structure. */ + void *priv; +}; + +/** An Intel PT packet. */ +struct pt_packet { + /** The type of the packet. + * + * This also determines the \@payload field. + */ + enum pt_packet_type type; + + /** The size of the packet including opcode and payload. */ + uint8_t size; + + /** Packet specific data. */ + union { + /** Packets: pad, ovf, psb, psbend, stop - no payload. */ + + /** Packet: tnt-8, tnt-64. */ + struct pt_packet_tnt tnt; + + /** Packet: tip, fup, tip.pge, tip.pgd. */ + struct pt_packet_ip ip; + + /** Packet: mode. */ + struct pt_packet_mode mode; + + /** Packet: pip. */ + struct pt_packet_pip pip; + + /** Packet: tsc. */ + struct pt_packet_tsc tsc; + + /** Packet: cbr. */ + struct pt_packet_cbr cbr; + + /** Packet: tma. */ + struct pt_packet_tma tma; + + /** Packet: mtc. */ + struct pt_packet_mtc mtc; + + /** Packet: cyc. */ + struct pt_packet_cyc cyc; + + /** Packet: vmcs. */ + struct pt_packet_vmcs vmcs; + + /** Packet: mnt. */ + struct pt_packet_mnt mnt; + + /** Packet: exstop. */ + struct pt_packet_exstop exstop; + + /** Packet: mwait. */ + struct pt_packet_mwait mwait; + + /** Packet: pwre. */ + struct pt_packet_pwre pwre; + + /** Packet: pwrx. */ + struct pt_packet_pwrx pwrx; + + /** Packet: ptw. */ + struct pt_packet_ptw ptw; + +#if (LIBIPT_VERSION >= 0x201) + /** Packet: cfe. */ + struct pt_packet_cfe cfe; + + /** Packet: evd. */ + struct pt_packet_evd evd; +#endif +#if (LIBIPT_VERSION >= 0x202) + /** Packet: trig. */ + struct pt_packet_trig trig; +#endif + /** Packet: unknown. */ + struct pt_packet_unknown unknown; + } payload; +}; + + + +/* Packet encoder. */ + + + +/** Allocate an Intel PT packet encoder. + * + * The encoder will work on the buffer defined in \@config, it shall contain + * raw trace data and remain valid for the lifetime of the encoder. + * + * The encoder starts at the beginning of the trace buffer. + */ +extern pt_export struct pt_encoder * +pt_alloc_encoder(const struct pt_config *config); + +/** Free an Intel PT packet encoder. + * + * The \@encoder must not be used after a successful return. + */ +extern pt_export void pt_free_encoder(struct pt_encoder *encoder); + +/** Hard set synchronization point of an Intel PT packet encoder. + * + * Synchronize \@encoder to \@offset within the trace buffer. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_eos if the given offset is behind the end of the trace buffer. + * Returns -pte_invalid if \@encoder is NULL. + */ +extern pt_export int pt_enc_sync_set(struct pt_encoder *encoder, + uint64_t offset); + +/** Get the current packet encoder position. + * + * Fills the current \@encoder position into \@offset. + * + * This is useful for reporting errors. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@encoder or \@offset is NULL. + */ +extern pt_export int pt_enc_get_offset(const struct pt_encoder *encoder, + uint64_t *offset); + +/* Return a pointer to \@encoder's configuration. + * + * Returns a non-null pointer on success, NULL if \@encoder is NULL. + */ +extern pt_export const struct pt_config * +pt_enc_get_config(const struct pt_encoder *encoder); + +/** Encode an Intel PT packet. + * + * Writes \@packet at \@encoder's current position in the Intel PT buffer and + * advances the \@encoder beyond the written packet. + * + * The \@packet.size field is ignored. + * + * In case of errors, the \@encoder is not advanced and nothing is written + * into the Intel PT buffer. + * + * Returns the number of bytes written on success, a negative error code + * otherwise. + * + * Returns -pte_bad_opc if \@packet.type is not known. + * Returns -pte_bad_packet if \@packet's payload is invalid. + * Returns -pte_eos if \@encoder reached the end of the Intel PT buffer. + * Returns -pte_invalid if \@encoder or \@packet is NULL. + */ +extern pt_export int pt_enc_next(struct pt_encoder *encoder, + const struct pt_packet *packet); + + + +/* Packet decoder. */ + + + +/** Allocate an Intel PT packet decoder. + * + * The decoder will work on the buffer defined in \@config, it shall contain + * raw trace data and remain valid for the lifetime of the decoder. + * + * The decoder needs to be synchronized before it can be used. + */ +extern pt_export struct pt_packet_decoder * +pt_pkt_alloc_decoder(const struct pt_config *config); + +/** Free an Intel PT packet decoder. + * + * The \@decoder must not be used after a successful return. + */ +extern pt_export void pt_pkt_free_decoder(struct pt_packet_decoder *decoder); + +/** Synchronize an Intel PT packet decoder. + * + * Search for the next synchronization point in forward or backward direction. + * + * If \@decoder has not been synchronized, yet, the search is started at the + * beginning of the trace buffer in case of forward synchronization and at the + * end of the trace buffer in case of backward synchronization. + * + * Returns zero or a positive value on success, a negative error code otherwise. + * + * Returns -pte_eos if no further synchronization point is found. + * Returns -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_pkt_sync_forward(struct pt_packet_decoder *decoder); +extern pt_export int pt_pkt_sync_backward(struct pt_packet_decoder *decoder); + +/** Hard set synchronization point of an Intel PT decoder. + * + * Synchronize \@decoder to \@offset within the trace buffer. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_eos if the given offset is behind the end of the trace buffer. + * Returns -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_pkt_sync_set(struct pt_packet_decoder *decoder, + uint64_t offset); + +/** Get the current decoder position. + * + * Fills the current \@decoder position into \@offset. + * + * This is useful for reporting errors. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_pkt_get_offset(const struct pt_packet_decoder *decoder, + uint64_t *offset); + +/** Get the position of the last synchronization point. + * + * Fills the last synchronization position into \@offset. + * + * This is useful when splitting a trace stream for parallel decoding. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int +pt_pkt_get_sync_offset(const struct pt_packet_decoder *decoder, + uint64_t *offset); + +/* Return a pointer to \@decoder's configuration. + * + * Returns a non-null pointer on success, NULL if \@decoder is NULL. + */ +extern pt_export const struct pt_config * +pt_pkt_get_config(const struct pt_packet_decoder *decoder); + +/** Decode the next packet and advance the decoder. + * + * Decodes the packet at \@decoder's current position into \@packet and + * adjusts the \@decoder's position by the number of bytes the packet had + * consumed. + * + * The \@size argument must be set to sizeof(struct pt_packet). + * + * Returns the number of bytes consumed on success, a negative error code + * otherwise. + * + * Returns -pte_bad_opc if the packet is unknown. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if \@decoder reached the end of the Intel PT buffer. + * Returns -pte_invalid if \@decoder or \@packet is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_pkt_next(struct pt_packet_decoder *decoder, + struct pt_packet *packet, size_t size); + + + +/* Event decoder. */ + + + +/** Event types. */ +enum pt_event_type { + /* Tracing has been enabled/disabled. */ + ptev_enabled, + ptev_disabled, + + /* Tracing has been disabled asynchronously. */ + ptev_async_disabled, + + /* An asynchronous branch, e.g. interrupt. */ + ptev_async_branch, + + /* A synchronous paging event. */ + ptev_paging, + + /* An asynchronous paging event. */ + ptev_async_paging, + + /* Trace overflow. */ + ptev_overflow, + + /* An execution mode change. */ + ptev_exec_mode, + + /* A transactional execution state change. */ + ptev_tsx, + + /* Trace Stop. */ + ptev_stop, + + /* A synchronous vmcs event. */ + ptev_vmcs, + + /* An asynchronous vmcs event. */ + ptev_async_vmcs, + + /* Execution has stopped. */ + ptev_exstop, + + /* An MWAIT operation completed. */ + ptev_mwait, + + /* A power state was entered. */ + ptev_pwre, + + /* A power state was exited. */ + ptev_pwrx, + + /* A PTWRITE event. */ + ptev_ptwrite, + + /* A timing event. */ + ptev_tick, + + /* A core:bus ratio event. */ + ptev_cbr, + + /* A maintenance event. */ + ptev_mnt, + +#if (LIBIPT_VERSION >= 0x201) + /* An indirect branch event. */ + ptev_tip, + + /* A conditional branch indicator event. */ + ptev_tnt, + + /* An interrupt status event. */ + ptev_iflags, + + /* An interrupt or exception event. + * + * This event extends the async_branch event generated for the + * control-flow change; it does not replace that event. + */ + ptev_interrupt, + + /* A return from interrupt event. */ + ptev_iret, + + /* A system management interrupt. */ + ptev_smi, + + /* A return from system management mode. */ + ptev_rsm, + + /* A SIPI message. */ + ptev_sipi, + + /* An INIT reset. */ + ptev_init, + + /* A Virtual Machine entry. */ + ptev_vmentry, + + /* A Virtual Machine exit. */ + ptev_vmexit, + + /* A shutdown event. */ + ptev_shutdown, + + /* A user interrupt. */ + ptev_uintr, + + /* A return from user interrupt. */ + ptev_uiret, +#endif +#if (LIBIPT_VERSION >= 0x202) + /* A trigger event. */ + ptev_trig, + + /* A software interrupt. */ + ptev_swintr, + + /* A system call. */ + ptev_syscall, +#endif +}; + +/** An event. */ +struct pt_event { + /** The type of the event. */ + enum pt_event_type type; + + /** A flag indicating that the event IP has been suppressed. */ + uint32_t ip_suppressed:1; + + /** A flag indicating that the event is for status update. */ + uint32_t status_update:1; + + /** A flag indicating that the event has timing information. */ + uint32_t has_tsc:1; + + /** The time stamp count of the event. + * + * This field is only valid if \@has_tsc is set. + */ + uint64_t tsc; + + /** The number of lost mtc and cyc packets. + * + * This gives an idea about the quality of the \@tsc. The more packets + * were dropped, the less precise timing is. + */ + uint32_t lost_mtc; + uint32_t lost_cyc; + + /* Reserved space for future extensions. */ + uint64_t reserved[2]; + + /** Event specific data. */ + union { + /** Event: enabled. */ + struct { + /** The address at which tracing resumes. */ + uint64_t ip; + + /** A flag indicating that tracing resumes from the IP + * at which tracing had been disabled before. + */ + uint32_t resumed:1; + } enabled; + + /** Event: disabled. */ + struct { + /** The destination of the first branch inside a + * filtered area. + * + * This field is not valid if \@ip_suppressed is set. + */ + uint64_t ip; + + /* The exact source ip needs to be determined using + * disassembly and the filter configuration. + */ + } disabled; + + /** Event: async disabled. */ + struct { + /** The source address of the asynchronous branch that + * disabled tracing. + */ + uint64_t at; + + /** The destination of the first branch inside a + * filtered area. + * + * This field is not valid if \@ip_suppressed is set. + */ + uint64_t ip; + } async_disabled; + + /** Event: async branch. */ + struct { + /** The branch source address. */ + uint64_t from; + + /** The branch destination address. + * + * This field is not valid if \@ip_suppressed is set. + */ + uint64_t to; + } async_branch; + + /** Event: paging. */ + struct { + /** The updated CR3 value. + * + * The lower 5 bit have been zeroed out. + * The upper bits have been zeroed out depending on the + * maximum possible address. + */ + uint64_t cr3; + + /** A flag indicating whether the cpu is operating in + * vmx non-root (guest) mode. + */ + uint32_t non_root:1; + + /* The address at which the event is effective is + * obvious from the disassembly. + */ + } paging; + + /** Event: async paging. */ + struct { + /** The updated CR3 value. + * + * The lower 5 bit have been zeroed out. + * The upper bits have been zeroed out depending on the + * maximum possible address. + */ + uint64_t cr3; + + /** A flag indicating whether the cpu is operating in + * vmx non-root (guest) mode. + */ + uint32_t non_root:1; + + /** The address at which the event is effective. */ + uint64_t ip; + } async_paging; + + /** Event: overflow. */ + struct { + /** The address at which tracing resumes after overflow. + * + * This field is not valid, if ip_suppressed is set. + * In this case, the overflow resolved while tracing + * was disabled. + */ + uint64_t ip; + } overflow; + + /** Event: exec mode. */ + struct { + /** The execution mode. */ + enum pt_exec_mode mode; + + /** The address at which the event is effective. */ + uint64_t ip; + } exec_mode; + + /** Event: tsx. */ + struct { + /** The address at which the event is effective. + * + * This field is not valid if \@ip_suppressed is set. + */ + uint64_t ip; + + /** A flag indicating speculative execution mode. */ + uint32_t speculative:1; + + /** A flag indicating speculative execution aborts. */ + uint32_t aborted:1; + } tsx; + + /** Event: vmcs. */ + struct { + /** The VMCS base address. + * + * The address is zero-extended with the lower 12 bits + * all zero. + */ + uint64_t base; + + /* The new VMCS base address should be stored and + * applied on subsequent VM entries. + */ + } vmcs; + + /** Event: async vmcs. */ + struct { + /** The VMCS base address. + * + * The address is zero-extended with the lower 12 bits + * all zero. + */ + uint64_t base; + + /** The address at which the event is effective. */ + uint64_t ip; + + /* An async paging event that binds to the same IP + * will always succeed this async vmcs event. + */ + } async_vmcs; + + /** Event: execution stopped. */ + struct { + /** The address at which execution has stopped. This is + * the last instruction that did not complete. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } exstop; + + /** Event: mwait. */ + struct { + /** The address of the instruction causing the mwait. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + + /** The mwait hints (eax). + * + * Reserved bits are undefined. + */ + uint32_t hints; + + /** The mwait extensions (ecx). + * + * Reserved bits are undefined. + */ + uint32_t ext; + } mwait; + + /** Event: power state entry. */ + struct { + /** The resolved thread C-state. */ + uint8_t state; + + /** The resolved thread sub C-state. */ + uint8_t sub_state; + + /** A flag indicating whether the C-state entry was + * initiated by h/w. + */ + uint32_t hw:1; + } pwre; + + /** Event: power state exit. */ + struct { + /** The core C-state at the time of the wake. */ + uint8_t last; + + /** The deepest core C-state achieved during sleep. */ + uint8_t deepest; + + /** The wake reason: + * + * - due to external interrupt received. + */ + uint32_t interrupt:1; + + /** - due to store to monitored address. */ + uint32_t store:1; + + /** - due to h/w autonomous condition such as HDC. */ + uint32_t autonomous:1; + } pwrx; + + /** Event: ptwrite. */ + struct { + /** The address of the ptwrite instruction. + * + * This field is not valid, if \@ip_suppressed is set. + * + * In this case, the address is obvious from the + * disassembly. + */ + uint64_t ip; + + /** The size of the below \@payload in bytes. */ + uint8_t size; + + /** The ptwrite payload. */ + uint64_t payload; + } ptwrite; + + /** Event: tick. */ + struct { + /** The instruction address near which the tick + * occurred. + * + * A timestamp can sometimes be attributed directly to + * an instruction (e.g. to an indirect branch that + * receives CYC + TIP) and sometimes not (e.g. MTC). + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } tick; + + /** Event: cbr. */ + struct { + /** The core:bus ratio. */ + uint16_t ratio; + } cbr; + + /** Event: mnt. */ + struct { + /** The raw payload. */ + uint64_t payload; + } mnt; + +#if (LIBIPT_VERSION >= 0x201) + /** Event: tip. */ + struct { + /** The target instruction address. */ + uint64_t ip; + } tip; + + /** Event: tnt. */ + struct { + /** A sequence of conditional branch indicator bits. + * + * Indicators are ordered from oldest (bits[size-1]) to + * youngest (bits[0]) branch: + * + * 0...branch not taken + * 1...branch taken + */ + uint64_t bits; + + /** The number of valid bits in @bits. */ + uint8_t size; + } tnt; + + /** Event: iflags. */ + struct { + /** The EFLAGS.IF status. */ + uint32_t iflag:1; + + /** The address of the instruction at which the new + * status applies. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } iflags; + + /** Event: interrupt/exception. */ + struct { + /** A flag saying whether the \@cr2 field is valid. */ + uint32_t has_cr2:1; + + /** The interrupt/exception vector. */ + uint8_t vector; + + /** The page fault linear address in cr2. + * + * This field is only valid if \@has_cr2 is set. + */ + uint64_t cr2; + + /** The address of the instruction at which the + * interrupt or exception occurred. + * + * For faults, this will be the faulting instruction. + * For traps, this will be the next instruction. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } interrupt; + + /** Event: iret. */ + struct { + /** The address of the instruction. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } iret; + + /** Event: smi. */ + struct { + /** The address of the instruction at/before which the + * system management interrupt occurred. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } smi; + + /** Event: rsm. */ + struct { + /** The address of the instruction. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } rsm; + + /** Event: sipi. */ + struct { + /** The SIPI vector. */ + uint8_t vector; + } sipi; + + /** Event: init. */ + struct { + /** The address of the instruction. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } init; + + /** Event: vmentry. */ + struct { + /** The address of the instruction. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } vmentry; + + /** Event: vmexit. */ + struct { + /** A flag saying whether the \@vector field is valid. + * + * When set, the vmexit occurred due to an interrupt or + * exception. + */ + uint32_t has_vector:1; + + /** A flag saying whether the \@vmxr field is valid. */ + uint32_t has_vmxr:1; + + /** A flag saying whether the \@vmxq field is valid. */ + uint32_t has_vmxq:1; + + /** The interrupt/exception vector. + * + * This field is only valid if \@has_vector is set. + */ + uint8_t vector; + + /** The vmexit reason. + * + * This field is only valid if \@has_vmxr is set. + */ + uint64_t vmxr; + + /** The vmexit qualification. + * + * This field is only valid if \@has_vmxq is set. + */ + uint64_t vmxq; + + /** The address of the instruction at which the + * interrupt or exception occurred. + * + * For faults, this will be the faulting instruction. + * For traps, this will be the next instruction. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } vmexit; + + /** Event: shutdown. */ + struct { + /** The address of the first instruction that did not + * complete due to the shutdown. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } shutdown; + + /** Event: user interrupt. */ + struct { + /** The user interrupt vector. */ + uint8_t vector; + + /** The address of the instruction before which the + * user interrupt was delivered. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } uintr; + + /** Event: uiret. */ + struct { + /** The address of the instruction. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } uiret; +#endif /* (LIBIPT_VERSION >= 0x201) */ + +#if (LIBIPT_VERSION >= 0x202) + /** Event: trigger. */ + struct { + /** A bit vector of triggers represented by this event. + * + * Triggers are configured via IA32_RTIT_TRIGGERx_CFG. + */ + uint8_t trbv; + + /** The number of instructions after the anchor. + * + * The insn and block decoders will attempty to reduce + * \@icount to zero and update \@ip. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint16_t icnt; + + /** The address of the anchor instruction. + * + * If this field is zero, use the address of the last + * trig, tip, tip.pge, or async event, or the address + * after applying the final bit in the last tnt event. + * + * The insn and block decoders will supply a non-zero + * address or set \@ip_suppressed. + * + * The event is reported when reaching the instruction + * address before the instruction itself to correctly + * handle cases where the instruction faults. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + + /** A flag saying whether there were other triggers + * firing that are not reported. + */ + uint32_t mult:1; + } trig; + + /** Event: software interrupt. */ + struct { + /** The interrupt vector. */ + uint8_t vector; + + /** The address of the instruction that generated the + * software interrupt. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } swintr; + + /** Event: system call. */ + struct { + /** The address of the instruction. + * + * This field is not valid, if \@ip_suppressed is set. + */ + uint64_t ip; + } syscall; +#endif /* (LIBIPT_VERSION >= 0x202) */ + } variant; +}; + + +#if (LIBIPT_VERSION >= 0x201) +/** Allocate an Intel PT event decoder. + * + * The decoder will work on the buffer defined in \@config, it shall contain + * raw trace data and remain valid for the lifetime of the decoder. + * + * The decoder needs to be synchronized before it can be used. + */ +extern pt_export struct pt_event_decoder * +pt_evt_alloc_decoder(const struct pt_config *config); + +/** Free an Intel PT event decoder. + * + * The \@decoder must not be used after a successful return. + */ +extern pt_export void pt_evt_free_decoder(struct pt_event_decoder *decoder); + +/** Synchronize an Intel PT event decoder. + * + * Search for the next synchronization point in forward or backward direction. + * + * If \@decoder has not been synchronized, yet, the search is started at the + * beginning of the trace buffer in case of forward synchronization and at the + * end of the trace buffer in case of backward synchronization. + * + * Returns zero or a positive value on success, a negative error code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if no further synchronization point is found. + * Returns -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_evt_sync_forward(struct pt_event_decoder *decoder); +extern pt_export int pt_evt_sync_backward(struct pt_event_decoder *decoder); + +/** Manually synchronize an Intel PT event decoder. + * + * Synchronize \@decoder on the syncpoint at \@offset. There must be a PSB + * packet at \@offset. + * + * Returns zero or a positive value on success, a negative error code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if \@offset lies outside of \@decoder's trace buffer. + * Returns -pte_eos if \@decoder reaches the end of its trace buffer. + * Returns -pte_invalid if \@decoder is NULL. + * Returns -pte_nosync if there is no syncpoint at \@offset. + */ +extern pt_export int pt_evt_sync_set(struct pt_event_decoder *decoder, + uint64_t offset); + +/** Get the current decoder position. + * + * Fills the current \@decoder position into \@offset. + * + * This is useful for reporting errors. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_evt_get_offset(const struct pt_event_decoder *decoder, + uint64_t *offset); + +/** Get the position of the last synchronization point. + * + * Fills the last synchronization position into \@offset. + * + * This is useful for splitting a trace stream for parallel decoding. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int +pt_evt_get_sync_offset(const struct pt_event_decoder *decoder, + uint64_t *offset); + +/* Return a pointer to \@decoder's configuration. + * + * Returns a non-null pointer on success, NULL if \@decoder is NULL. + */ +extern pt_export const struct pt_config * +pt_evt_get_config(const struct pt_event_decoder *decoder); + +/** Determine the next event. + * + * On success, provides the next event in \@event. + * + * The \@size argument must be set to sizeof(struct pt_event). + * + * Returns zero or a positive value on success, a negative error code + * otherwise. + * + * Returns -pte_bad_opc if the packet is unknown. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if \@decoder reached the end of the Intel PT buffer. + * Returns -pte_invalid if \@decoder or \@event is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_evt_next(struct pt_event_decoder *decoder, + struct pt_event *event, size_t size); +#endif /* (LIBIPT_VERSION >= 0x201) */ + + + +/* Query decoder. */ + + + +/** Decoder status flags. */ +enum pt_status_flag { + /** There is an event pending. */ + pts_event_pending = 1 << 0, + + /** The address has been suppressed. */ + pts_ip_suppressed = 1 << 1, + + /** There is no more trace data available. */ + pts_eos = 1 << 2 +}; + +/** Allocate an Intel PT query decoder. + * + * The decoder will work on the buffer defined in \@config, it shall contain + * raw trace data and remain valid for the lifetime of the decoder. + * + * The decoder needs to be synchronized before it can be used. + */ +extern pt_export struct pt_query_decoder * +pt_qry_alloc_decoder(const struct pt_config *config); + +/** Free an Intel PT query decoder. + * + * The \@decoder must not be used after a successful return. + */ +extern pt_export void pt_qry_free_decoder(struct pt_query_decoder *decoder); + +/** Synchronize an Intel PT query decoder. + * + * Search for the next synchronization point in forward or backward direction. + * + * If \@decoder has not been synchronized, yet, the search is started at the + * beginning of the trace buffer in case of forward synchronization and at the + * end of the trace buffer in case of backward synchronization. + * + * If \@ip is not NULL, set it to last ip. + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative error + * code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if no further synchronization point is found. + * Returns -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_qry_sync_forward(struct pt_query_decoder *decoder, + uint64_t *ip); +extern pt_export int pt_qry_sync_backward(struct pt_query_decoder *decoder, + uint64_t *ip); + +/** Manually synchronize an Intel PT query decoder. + * + * Synchronize \@decoder on the syncpoint at \@offset. There must be a PSB + * packet at \@offset. + * + * If \@ip is not NULL, set it to last ip. + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative error + * code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if \@offset lies outside of \@decoder's trace buffer. + * Returns -pte_eos if \@decoder reaches the end of its trace buffer. + * Returns -pte_invalid if \@decoder is NULL. + * Returns -pte_nosync if there is no syncpoint at \@offset. + */ +extern pt_export int pt_qry_sync_set(struct pt_query_decoder *decoder, + uint64_t *ip, uint64_t offset); + +/** Get the current decoder position. + * + * Fills the current \@decoder position into \@offset. + * + * This is useful for reporting errors. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_qry_get_offset(const struct pt_query_decoder *decoder, + uint64_t *offset); + +/** Get the position of the last synchronization point. + * + * Fills the last synchronization position into \@offset. + * + * This is useful for splitting a trace stream for parallel decoding. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int +pt_qry_get_sync_offset(const struct pt_query_decoder *decoder, + uint64_t *offset); + +/* Return a pointer to \@decoder's configuration. + * + * Returns a non-null pointer on success, NULL if \@decoder is NULL. + */ +extern pt_export const struct pt_config * +pt_qry_get_config(const struct pt_query_decoder *decoder); + +/** Query whether the next unconditional branch has been taken. + * + * On success, provides 1 (taken) or 0 (not taken) in \@taken for the next + * conditional branch and updates \@decoder. + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative error + * code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_bad_query if no conditional branch is found. + * Returns -pte_eos if decoding reached the end of the Intel PT buffer. + * Returns -pte_invalid if \@decoder or \@taken is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_qry_cond_branch(struct pt_query_decoder *decoder, + int *taken); + +/** Get the next indirect branch destination. + * + * On success, provides the linear destination address of the next indirect + * branch in \@ip and updates \@decoder. + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative error + * code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_bad_query if no indirect branch is found. + * Returns -pte_eos if decoding reached the end of the Intel PT buffer. + * Returns -pte_invalid if \@decoder or \@ip is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_qry_indirect_branch(struct pt_query_decoder *decoder, + uint64_t *ip); + +/** Query the next pending event. + * + * On success, provides the next event \@event and updates \@decoder. + * + * The \@size argument must be set to sizeof(struct pt_event). + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative error + * code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_bad_query if no event is found. + * Returns -pte_eos if decoding reached the end of the Intel PT buffer. + * Returns -pte_invalid if \@decoder or \@event is NULL. + * Returns -pte_invalid if \@size is too small. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_qry_event(struct pt_query_decoder *decoder, + struct pt_event *event, size_t size); + +/** Query the current time. + * + * On success, provides the time at the last query in \@time. + * + * The time is similar to what a rdtsc instruction would return. Depending + * on the configuration, the time may not be fully accurate. If TSC is not + * enabled, the time is relative to the last synchronization and can't be used + * to correlate with other TSC-based time sources. In this case, -pte_no_time + * is returned and the relative time is provided in \@time. + * + * Some timing-related packets may need to be dropped (mostly due to missing + * calibration or incomplete configuration). To get an idea about the quality + * of the estimated time, we record the number of dropped MTC and CYC packets. + * + * If \@lost_mtc is not NULL, set it to the number of lost MTC packets. + * If \@lost_cyc is not NULL, set it to the number of lost CYC packets. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@time is NULL. + * Returns -pte_no_time if there has not been a TSC packet. + */ +extern pt_export int pt_qry_time(struct pt_query_decoder *decoder, + uint64_t *time, uint32_t *lost_mtc, + uint32_t *lost_cyc); + +/** Return the current core bus ratio. + * + * On success, provides the current core:bus ratio in \@cbr. The ratio is + * defined as core cycles per bus clock cycle. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@cbr is NULL. + * Returns -pte_no_cbr if there has not been a CBR packet. + */ +extern pt_export int pt_qry_core_bus_ratio(struct pt_query_decoder *decoder, + uint32_t *cbr); + + + +/* Traced image. */ + + + +/** An Intel PT address space identifier. + * + * This identifies a particular address space when adding file sections or + * when reading memory. + */ +struct pt_asid { + /** The size of this object - set to sizeof(struct pt_asid). */ + size_t size; + + /** The CR3 value. */ + uint64_t cr3; + + /** The VMCS Base address. */ + uint64_t vmcs; +}; + +/** An unknown CR3 value to be used for pt_asid objects. */ +static const uint64_t pt_asid_no_cr3 = 0xffffffffffffffffull; + +/** An unknown VMCS Base value to be used for pt_asid objects. */ +static const uint64_t pt_asid_no_vmcs = 0xffffffffffffffffull; + +/** Initialize an address space identifier. */ +static inline void pt_asid_init(struct pt_asid *asid) +{ + asid->size = sizeof(*asid); + asid->cr3 = pt_asid_no_cr3; + asid->vmcs = pt_asid_no_vmcs; +} + + +/** A cache of traced image sections. */ +struct pt_image_section_cache; + +/** Allocate a traced memory image section cache. + * + * An optional \@name may be given to the cache. The name string is copied. + * + * Returns a new traced memory image section cache on success, NULL otherwise. + */ +extern pt_export struct pt_image_section_cache * +pt_iscache_alloc(const char *name); + +/** Free a traced memory image section cache. + * + * The \@iscache must have been allocated with pt_iscache_alloc(). + * The \@iscache must not be used after a successful return. + */ +extern pt_export void pt_iscache_free(struct pt_image_section_cache *iscache); + +/** Set the image section cache limit. + * + * Set the limit for a section cache in bytes. A non-zero limit will keep the + * least recently used sections mapped until the limit is reached. A limit of + * zero disables caching. + * + * Returns zero on success, a negative pt_error_code otherwise. + * Returns -pte_invalid if \@iscache is NULL. + */ +extern pt_export int +pt_iscache_set_limit(struct pt_image_section_cache *iscache, uint64_t limit); + +/** Get the image section cache name. + * + * Returns a pointer to \@iscache's name or NULL if there is no name. + */ +extern pt_export const char * +pt_iscache_name(const struct pt_image_section_cache *iscache); + +/** Add a new file section to the traced memory image section cache. + * + * Adds a new section consisting of \@size bytes starting at \@offset in + * \@filename loaded at the virtual address \@vaddr if \@iscache does not + * already contain such a section. + * + * Returns an image section identifier (isid) uniquely identifying that section + * in \@iscache. + * + * The section is silently truncated to match the size of \@filename. + * + * Returns a positive isid on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@iscache or \@filename is NULL. + * Returns -pte_invalid if \@offset is too big. + */ +extern pt_export int pt_iscache_add_file(struct pt_image_section_cache *iscache, + const char *filename, uint64_t offset, + uint64_t size, uint64_t vaddr); + +/** Read memory from a cached file section + * + * Reads \@size bytes of memory starting at virtual address \@vaddr in the + * section identified by \@isid in \@iscache into \@buffer. + * + * The caller is responsible for allocating a \@buffer of at least \@size bytes. + * + * The read request may be truncated if it crosses section boundaries or if + * \@size is getting too big. We support reading at least 4Kbyte in one chunk + * unless the read would cross a section boundary. + * + * Returns the number of bytes read on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@iscache or \@buffer is NULL. + * Returns -pte_invalid if \@size is zero. + * Returns -pte_nomap if \@vaddr is not contained in section \@isid. + * Returns -pte_bad_image if \@iscache does not contain \@isid. + */ +extern pt_export int pt_iscache_read(struct pt_image_section_cache *iscache, + uint8_t *buffer, uint64_t size, int isid, + uint64_t vaddr); + +/** The traced memory image. */ +struct pt_image; + + +/** Allocate a traced memory image. + * + * An optional \@name may be given to the image. The name string is copied. + * + * Returns a new traced memory image on success, NULL otherwise. + */ +extern pt_export struct pt_image *pt_image_alloc(const char *name); + +/** Free a traced memory image. + * + * The \@image must have been allocated with pt_image_alloc(). + * The \@image must not be used after a successful return. + */ +extern pt_export void pt_image_free(struct pt_image *image); + +/** Get the image name. + * + * Returns a pointer to \@image's name or NULL if there is no name. + */ +extern pt_export const char *pt_image_name(const struct pt_image *image); + +/** Add a new file section to the traced memory image. + * + * Adds \@size bytes starting at \@offset in \@filename. The section is + * loaded at the virtual address \@vaddr in the address space \@asid. + * + * The \@asid may be NULL or (partially) invalid. In that case only the valid + * fields are considered when comparing with other address-spaces. Use this + * when tracing a single process or when adding sections to all processes. + * + * The section is silently truncated to match the size of \@filename. + * + * Existing sections that would overlap with the new section will be shrunk + * or split. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@image or \@filename is NULL. + * Returns -pte_invalid if \@offset is too big. + */ +extern pt_export int pt_image_add_file(struct pt_image *image, + const char *filename, uint64_t offset, + uint64_t size, + const struct pt_asid *asid, + uint64_t vaddr); + +/** Add a section from an image section cache. + * + * Add the section from \@iscache identified by \@isid in address space \@asid. + * + * Existing sections that would overlap with the new section will be shrunk + * or split. + * + * Returns zero on success, a negative error code otherwise. + * Returns -pte_invalid if \@image or \@iscache is NULL. + * Returns -pte_bad_image if \@iscache does not contain \@isid. + */ +extern pt_export int pt_image_add_cached(struct pt_image *image, + struct pt_image_section_cache *iscache, + int isid, const struct pt_asid *asid); + +/** Copy an image. + * + * Adds all sections from \@src to \@image. Sections that could not be added + * will be ignored. + * + * Returns the number of ignored sections on success, a negative error code + * otherwise. + * + * Returns -pte_invalid if \@image or \@src is NULL. + */ +extern pt_export int pt_image_copy(struct pt_image *image, + const struct pt_image *src); + +/** Remove all sections loaded from a file. + * + * Removes all sections loaded from \@filename from the address space \@asid. + * Specify the same \@asid that was used for adding sections from \@filename. + * + * Returns the number of removed sections on success, a negative error code + * otherwise. + * + * Returns -pte_invalid if \@image or \@filename is NULL. + */ +extern pt_export int pt_image_remove_by_filename(struct pt_image *image, + const char *filename, + const struct pt_asid *asid); + +/** Remove all sections loaded into an address space. + * + * Removes all sections loaded into \@asid. Specify the same \@asid that was + * used for adding sections. + * + * Returns the number of removed sections on success, a negative error code + * otherwise. + * + * Returns -pte_invalid if \@image is NULL. + */ +extern pt_export int pt_image_remove_by_asid(struct pt_image *image, + const struct pt_asid *asid); + +/** A read memory callback function. + * + * It shall read \@size bytes of memory from address space \@asid starting + * at \@ip into \@buffer. + * + * It shall return the number of bytes read on success. + * It shall return a negative pt_error_code otherwise. + */ +typedef int (read_memory_callback_t)(uint8_t *buffer, size_t size, + const struct pt_asid *asid, + uint64_t ip, void *context); + +/** Set the memory callback for the traced memory image. + * + * Sets \@callback for reading memory. The callback is used for addresses + * that are not found in file sections. The \@context argument is passed + * to \@callback on each use. + * + * There can only be one callback at any time. A subsequent call will replace + * the previous callback. If \@callback is NULL, the callback is removed. + * + * Returns -pte_invalid if \@image is NULL. + */ +extern pt_export int pt_image_set_callback(struct pt_image *image, + read_memory_callback_t *callback, + void *context); + + + +/* Instruction flow decoder. */ + + + +/** The instruction class. + * + * We provide only a very coarse classification suitable for reconstructing + * the execution flow. + */ +enum pt_insn_class { +#if (LIBIPT_VERSION >= 0x201) + /* The instruction has not been classified. */ + ptic_unknown, + + /* For backwards compatibility. */ + ptic_error = ptic_unknown, +#else + /* The instruction has not been classified. */ + ptic_error, +#endif + + /* The instruction is something not listed below. */ + ptic_other, + + /* The instruction is a near (function) call. */ + ptic_call, + + /* The instruction is a near (function) return. */ + ptic_return, + + /* The instruction is a near unconditional jump. */ + ptic_jump, + + /* The instruction is a near conditional jump. */ + ptic_cond_jump, + + /* The instruction is a call-like far transfer. + * E.g. SYSCALL, SYSENTER, or FAR CALL. + */ + ptic_far_call, + + /* The instruction is a return-like far transfer. + * E.g. SYSRET, SYSEXIT, IRET, or FAR RET. + */ + ptic_far_return, + + /* The instruction is a jump-like far transfer. + * E.g. FAR JMP. + */ + ptic_far_jump, + + /* The instruction is a PTWRITE. */ + ptic_ptwrite, + +#if (LIBIPT_VERSION >= 0x201) + /* The instruction is an indirect jump or a far transfer. */ + ptic_indirect, +#endif +}; + +/** The maximal size of an instruction. */ +enum { + pt_max_insn_size = 15 +}; + +/** A single traced instruction. */ +struct pt_insn { + /** The virtual address in its process. */ + uint64_t ip; + + /** The image section identifier for the section containing this + * instruction. + * + * A value of zero means that the section did not have an identifier. + * The section was not added via an image section cache or the memory + * was read via the read memory callback. + */ + int isid; + + /** The execution mode. */ + enum pt_exec_mode mode; + + /** A coarse classification. */ + enum pt_insn_class iclass; + + /** The raw bytes. */ + uint8_t raw[pt_max_insn_size]; + + /** The size in bytes. */ + uint8_t size; + + /** A collection of flags giving additional information: + * + * - the instruction was executed speculatively. + */ + uint32_t speculative:1; + + /** - this instruction is truncated in its image section. + * + * It starts in the image section identified by \@isid and continues + * in one or more other sections. + */ + uint32_t truncated:1; +}; + + +/** Allocate an Intel PT instruction flow decoder. + * + * The decoder will work on the buffer defined in \@config, it shall contain + * raw trace data and remain valid for the lifetime of the decoder. + * + * The decoder needs to be synchronized before it can be used. + */ +extern pt_export struct pt_insn_decoder * +pt_insn_alloc_decoder(const struct pt_config *config); + +/** Free an Intel PT instruction flow decoder. + * + * This will destroy the decoder's default image. + * + * The \@decoder must not be used after a successful return. + */ +extern pt_export void pt_insn_free_decoder(struct pt_insn_decoder *decoder); + +/** Synchronize an Intel PT instruction flow decoder. + * + * Search for the next synchronization point in forward or backward direction. + * + * If \@decoder has not been synchronized, yet, the search is started at the + * beginning of the trace buffer in case of forward synchronization and at the + * end of the trace buffer in case of backward synchronization. + * + * Returns zero or a positive value on success, a negative error code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if no further synchronization point is found. + * Returns -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_insn_sync_forward(struct pt_insn_decoder *decoder); +extern pt_export int pt_insn_sync_backward(struct pt_insn_decoder *decoder); + +/** Manually synchronize an Intel PT instruction flow decoder. + * + * Synchronize \@decoder on the syncpoint at \@offset. There must be a PSB + * packet at \@offset. + * + * Returns zero or a positive value on success, a negative error code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if \@offset lies outside of \@decoder's trace buffer. + * Returns -pte_eos if \@decoder reaches the end of its trace buffer. + * Returns -pte_invalid if \@decoder is NULL. + * Returns -pte_nosync if there is no syncpoint at \@offset. + */ +extern pt_export int pt_insn_sync_set(struct pt_insn_decoder *decoder, + uint64_t offset); + +#if (LIBIPT_VERSION >= 0x202) +/** Re-synchronize an Intel PT instruction flow decoder. + * + * Scan ahead for the next IP providing packet. Apply state changing packets + * along the way, including timing packets, but ignore everything else. + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative + * error code otherwise. + * + * Returns pts_ip_suppressed if re-synchronization succeeded with tracing + * disabled. + * + * Returns -pte_event_ignored if an event cannot safely be skipped + * (e.g. ptwrite). Use pt_insn_event() to read the event, then continue with + * pt_insn_resync() until re-synchronization completes or fails with an error. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if no further synchronization point is found. + * Returns -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_insn_resync(struct pt_insn_decoder *decoder); +#endif + +/** Get the current decoder position. + * + * Fills the current \@decoder position into \@offset. + * + * This is useful for reporting errors. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_insn_get_offset(const struct pt_insn_decoder *decoder, + uint64_t *offset); + +/** Get the position of the last synchronization point. + * + * Fills the last synchronization position into \@offset. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int +pt_insn_get_sync_offset(const struct pt_insn_decoder *decoder, + uint64_t *offset); + +/** Get the traced image. + * + * The returned image may be modified as long as no decoder that uses this + * image is running. + * + * Returns a pointer to the traced image the decoder uses for reading memory. + * Returns NULL if \@decoder is NULL. + */ +extern pt_export struct pt_image * +pt_insn_get_image(struct pt_insn_decoder *decoder); + +/** Set the traced image. + * + * Sets the image that \@decoder uses for reading memory to \@image. If \@image + * is NULL, sets the image to \@decoder's default image. + * + * Only one image can be active at any time. + * + * Returns zero on success, a negative error code otherwise. + * Return -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_insn_set_image(struct pt_insn_decoder *decoder, + struct pt_image *image); + +/* Return a pointer to \@decoder's configuration. + * + * Returns a non-null pointer on success, NULL if \@decoder is NULL. + */ +extern pt_export const struct pt_config * +pt_insn_get_config(const struct pt_insn_decoder *decoder); + +/** Return the current time. + * + * On success, provides the time at the last preceding timing packet in \@time. + * + * The time is similar to what a rdtsc instruction would return. Depending + * on the configuration, the time may not be fully accurate. If TSC is not + * enabled, the time is relative to the last synchronization and can't be used + * to correlate with other TSC-based time sources. In this case, -pte_no_time + * is returned and the relative time is provided in \@time. + * + * Some timing-related packets may need to be dropped (mostly due to missing + * calibration or incomplete configuration). To get an idea about the quality + * of the estimated time, we record the number of dropped MTC and CYC packets. + * + * If \@lost_mtc is not NULL, set it to the number of lost MTC packets. + * If \@lost_cyc is not NULL, set it to the number of lost CYC packets. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@time is NULL. + * Returns -pte_no_time if there has not been a TSC packet. + */ +extern pt_export int pt_insn_time(struct pt_insn_decoder *decoder, + uint64_t *time, uint32_t *lost_mtc, + uint32_t *lost_cyc); + +/** Return the current core bus ratio. + * + * On success, provides the current core:bus ratio in \@cbr. The ratio is + * defined as core cycles per bus clock cycle. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@cbr is NULL. + * Returns -pte_no_cbr if there has not been a CBR packet. + */ +extern pt_export int pt_insn_core_bus_ratio(struct pt_insn_decoder *decoder, + uint32_t *cbr); + +/** Return the current address space identifier. + * + * On success, provides the current address space identifier in \@asid. + * + * The \@size argument must be set to sizeof(struct pt_asid). At most \@size + * bytes will be copied and \@asid->size will be set to the actual size of the + * provided address space identifier. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@asid is NULL. + */ +extern pt_export int pt_insn_asid(const struct pt_insn_decoder *decoder, + struct pt_asid *asid, size_t size); + +/** Determine the next instruction. + * + * On success, provides the next instruction in execution order in \@insn. + * + * The \@size argument must be set to sizeof(struct pt_insn). + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative error + * code otherwise. + * + * Returns pts_eos to indicate the end of the trace stream. Subsequent calls + * to pt_insn_next() will continue to return pts_eos until trace is required + * to determine the next instruction. + * + * Returns -pte_event_ignored to indicate that the user needs to drain events + * using pt_insn_event() before continuing. + * + * Returns -pte_bad_context if the decoder encountered an unexpected packet. + * Returns -pte_bad_opc if the decoder encountered unknown packets. + * Returns -pte_bad_packet if the decoder encountered unknown packet payloads. + * Returns -pte_bad_query if the decoder got out of sync. + * Returns -pte_eos if decoding reached the end of the Intel PT buffer. + * Returns -pte_invalid if \@decoder or \@insn is NULL. + * Returns -pte_nomap if the memory at the instruction address can't be read. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_insn_next(struct pt_insn_decoder *decoder, + struct pt_insn *insn, size_t size); + +/** Get the next pending event. + * + * On success, provides the next event in \@event and updates \@decoder. + * + * The \@size argument must be set to sizeof(struct pt_event). + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative error + * code otherwise. + * + * Returns -pte_bad_query if there is no event. + * Returns -pte_invalid if \@decoder or \@event is NULL. + * Returns -pte_invalid if \@size is too small. + */ +extern pt_export int pt_insn_event(struct pt_insn_decoder *decoder, + struct pt_event *event, size_t size); + + + +/* Block decoder. */ + + + +/** A block of instructions. + * + * Instructions in this block are executed sequentially but are not necessarily + * contiguous in memory. Users are expected to follow direct branches. + */ +struct pt_block { + /** The IP of the first instruction in this block. */ + uint64_t ip; + + /** The IP of the last instruction in this block. + * + * This can be used for error-detection. + */ + uint64_t end_ip; + + /** The image section that contains the instructions in this block. + * + * A value of zero means that the section did not have an identifier. + * The section was not added via an image section cache or the memory + * was read via the read memory callback. + */ + int isid; + + /** The execution mode for all instructions in this block. */ + enum pt_exec_mode mode; + + /** The instruction class for the last instruction in this block. + * + * This field may be set to ptic_unknown (ptic_error prior to v2.1) to + * indicate that the instruction class is not available. The block + * decoder may choose to not provide the instruction class in some + * cases for performance reasons. + */ + enum pt_insn_class iclass; + + /** The number of instructions in this block. */ + uint16_t ninsn; + + /** The raw bytes of the last instruction in this block in case the + * instruction does not fit entirely into this block's section. + * + * This field is only valid if \@truncated is set. + */ + uint8_t raw[pt_max_insn_size]; + + /** The size of the last instruction in this block in bytes. + * + * This field is only valid if \@truncated is set. + */ + uint8_t size; + + /** A collection of flags giving additional information about the + * instructions in this block. + * + * - all instructions in this block were executed speculatively. + */ + uint32_t speculative:1; + + /** - the last instruction in this block is truncated. + * + * It starts in this block's section but continues in one or more + * other sections depending on how fragmented the memory image is. + * + * The raw bytes for the last instruction are provided in \@raw and + * its size in \@size in this case. + */ + uint32_t truncated:1; +}; + +/** Allocate an Intel PT block decoder. + * + * The decoder will work on the buffer defined in \@config, it shall contain + * raw trace data and remain valid for the lifetime of the decoder. + * + * The decoder needs to be synchronized before it can be used. + */ +extern pt_export struct pt_block_decoder * +pt_blk_alloc_decoder(const struct pt_config *config); + +/** Free an Intel PT block decoder. + * + * This will destroy the decoder's default image. + * + * The \@decoder must not be used after a successful return. + */ +extern pt_export void pt_blk_free_decoder(struct pt_block_decoder *decoder); + +/** Synchronize an Intel PT block decoder. + * + * Search for the next synchronization point in forward or backward direction. + * + * If \@decoder has not been synchronized, yet, the search is started at the + * beginning of the trace buffer in case of forward synchronization and at the + * end of the trace buffer in case of backward synchronization. + * + * Returns zero or a positive value on success, a negative error code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if no further synchronization point is found. + * Returns -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_blk_sync_forward(struct pt_block_decoder *decoder); +extern pt_export int pt_blk_sync_backward(struct pt_block_decoder *decoder); + +/** Manually synchronize an Intel PT block decoder. + * + * Synchronize \@decoder on the syncpoint at \@offset. There must be a PSB + * packet at \@offset. + * + * Returns zero or a positive value on success, a negative error code otherwise. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if \@offset lies outside of \@decoder's trace buffer. + * Returns -pte_eos if \@decoder reaches the end of its trace buffer. + * Returns -pte_invalid if \@decoder is NULL. + * Returns -pte_nosync if there is no syncpoint at \@offset. + */ +extern pt_export int pt_blk_sync_set(struct pt_block_decoder *decoder, + uint64_t offset); + +#if (LIBIPT_VERSION >= 0x202) +/** Re-synchronize an Intel PT block decoder. + * + * Scan ahead for the next IP providing packet. Apply state changing packets + * along the way, including timing packets, but ignore everything else. + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative + * error code otherwise. + * + * Returns pts_ip_suppressed if re-synchronization succeeded with tracing + * disabled. + * + * Returns -pte_event_ignored if an event cannot safely be skipped + * (e.g. ptwrite). Use pt_blk_event() to read the event, then continue with + * pt_blk_resync() until re-synchronization completes or fails with an error. + * + * Returns -pte_bad_opc if an unknown packet is encountered. + * Returns -pte_bad_packet if an unknown packet payload is encountered. + * Returns -pte_eos if no further synchronization point is found. + * Returns -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_blk_resync(struct pt_block_decoder *decoder); +#endif + +/** Get the current decoder position. + * + * Fills the current \@decoder position into \@offset. + * + * This is useful for reporting errors. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_blk_get_offset(const struct pt_block_decoder *decoder, + uint64_t *offset); + +/** Get the position of the last synchronization point. + * + * Fills the last synchronization position into \@offset. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@offset is NULL. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int +pt_blk_get_sync_offset(const struct pt_block_decoder *decoder, + uint64_t *offset); + +/** Get the traced image. + * + * The returned image may be modified as long as \@decoder is not running. + * + * Returns a pointer to the traced image \@decoder uses for reading memory. + * Returns NULL if \@decoder is NULL. + */ +extern pt_export struct pt_image * +pt_blk_get_image(struct pt_block_decoder *decoder); + +/** Set the traced image. + * + * Sets the image that \@decoder uses for reading memory to \@image. If \@image + * is NULL, sets the image to \@decoder's default image. + * + * Only one image can be active at any time. + * + * Returns zero on success, a negative error code otherwise. + * Return -pte_invalid if \@decoder is NULL. + */ +extern pt_export int pt_blk_set_image(struct pt_block_decoder *decoder, + struct pt_image *image); + +/* Return a pointer to \@decoder's configuration. + * + * Returns a non-null pointer on success, NULL if \@decoder is NULL. + */ +extern pt_export const struct pt_config * +pt_blk_get_config(const struct pt_block_decoder *decoder); + +/** Return the current time. + * + * On success, provides the time at the last preceding timing packet in \@time. + * + * The time is similar to what a rdtsc instruction would return. Depending + * on the configuration, the time may not be fully accurate. If TSC is not + * enabled, the time is relative to the last synchronization and can't be used + * to correlate with other TSC-based time sources. In this case, -pte_no_time + * is returned and the relative time is provided in \@time. + * + * Some timing-related packets may need to be dropped (mostly due to missing + * calibration or incomplete configuration). To get an idea about the quality + * of the estimated time, we record the number of dropped MTC and CYC packets. + * + * If \@lost_mtc is not NULL, set it to the number of lost MTC packets. + * If \@lost_cyc is not NULL, set it to the number of lost CYC packets. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@time is NULL. + * Returns -pte_no_time if there has not been a TSC packet. + */ +extern pt_export int pt_blk_time(struct pt_block_decoder *decoder, + uint64_t *time, uint32_t *lost_mtc, + uint32_t *lost_cyc); + +/** Return the current core bus ratio. + * + * On success, provides the current core:bus ratio in \@cbr. The ratio is + * defined as core cycles per bus clock cycle. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@cbr is NULL. + * Returns -pte_no_cbr if there has not been a CBR packet. + */ +extern pt_export int pt_blk_core_bus_ratio(struct pt_block_decoder *decoder, + uint32_t *cbr); + +/** Return the current address space identifier. + * + * On success, provides the current address space identifier in \@asid. + * + * The \@size argument must be set to sizeof(struct pt_asid). At most \@size + * bytes will be copied and \@asid->size will be set to the actual size of the + * provided address space identifier. + * + * Returns zero on success, a negative error code otherwise. + * + * Returns -pte_invalid if \@decoder or \@asid is NULL. + */ +extern pt_export int pt_blk_asid(const struct pt_block_decoder *decoder, + struct pt_asid *asid, size_t size); + +/** Determine the next block of instructions. + * + * On success, provides the next block of instructions in execution order in + * \@block. + * + * The \@size argument must be set to sizeof(struct pt_block). + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative error + * code otherwise. + * + * Returns pts_eos to indicate the end of the trace stream. Subsequent calls + * to pt_block_next() will continue to return pts_eos until trace is required + * to determine the next instruction. + * + * Returns -pte_bad_context if the decoder encountered an unexpected packet. + * Returns -pte_bad_opc if the decoder encountered unknown packets. + * Returns -pte_bad_packet if the decoder encountered unknown packet payloads. + * Returns -pte_bad_query if the decoder got out of sync. + * Returns -pte_eos if decoding reached the end of the Intel PT buffer. + * Returns -pte_invalid if \@decoder or \@block is NULL. + * Returns -pte_nomap if the memory at the instruction address can't be read. + * Returns -pte_nosync if \@decoder is out of sync. + */ +extern pt_export int pt_blk_next(struct pt_block_decoder *decoder, + struct pt_block *block, size_t size); + +/** Get the next pending event. + * + * On success, provides the next event in \@event and updates \@decoder. + * + * The \@size argument must be set to sizeof(struct pt_event). + * + * Returns a non-negative pt_status_flag bit-vector on success, a negative error + * code otherwise. + * + * Returns -pte_bad_query if there is no event. + * Returns -pte_invalid if \@decoder or \@event is NULL. + * Returns -pte_invalid if \@size is too small. + */ +extern pt_export int pt_blk_event(struct pt_block_decoder *decoder, + struct pt_event *event, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* INTEL_PT_H */ diff --git a/hyperdbg/dependencies/pdbex b/hyperdbg/dependencies/pdbex index 42a9fbb8..0e114472 160000 --- a/hyperdbg/dependencies/pdbex +++ b/hyperdbg/dependencies/pdbex @@ -1 +1 @@ -Subproject commit 42a9fbb821c7d140c5e00da60f5a5887c2fd8444 +Subproject commit 0e1144729a5b2d737cce11d1c0e083033630fff8 diff --git a/hyperdbg/dependencies/zydis b/hyperdbg/dependencies/zydis index e61f5933..e910df2f 160000 --- a/hyperdbg/dependencies/zydis +++ b/hyperdbg/dependencies/zydis @@ -1 +1 @@ -Subproject commit e61f59332ce49f8853006573ca853e404fafdd08 +Subproject commit e910df2fa54273b1a489b39771d331036e8de2db diff --git a/hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj b/hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj index b40055ce..5b16f0d4 100644 --- a/hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj +++ b/hyperdbg/hyperdbg-cli/hyperdbg-cli.vcxproj @@ -22,14 +22,14 @@ Application true - v143 + v145 Unicode false Application false - v143 + v145 true Unicode false @@ -82,15 +82,21 @@ if exist "$(OutDir)SDK\" rd /q /s "$(OutDir)SDK\" xcopy /E /I /Y "$(SolutionDir)include\SDK" "$(OutDir)SDK" -xcopy /E /I /Y "$(SolutionDir)..\examples" "$(OutDir)SDK\Examples" -mkdir "$(OutDir)SDK\Libraries" -copy "$(OutDir)pdbex.lib" "$(OutDir)SDK\Libraries\pdbex.lib" -copy "$(OutDir)kdserial.lib" "$(OutDir)SDK\Libraries\kdserial.lib" -copy "$(OutDir)libhyperdbg.dll" "$(OutDir)SDK\Libraries\libhyperdbg.dll" -copy "$(OutDir)script-engine.dll" "$(OutDir)SDK\Libraries\script-engine.dll" -copy "$(OutDir)symbol-parser.dll" "$(OutDir)SDK\Libraries\symbol-parser.dll" -copy "$(OutDir)hyperlog.dll" "$(OutDir)SDK\Libraries\hyperlog.dll" -copy "$(OutDir)hyperhv.dll" "$(OutDir)SDK\Libraries\hyperhv.dll" +xcopy /E /I /Y "$(SolutionDir)..\examples" "$(OutDir)SDK\examples" +mkdir "$(OutDir)SDK\libraries" +mkdir "$(OutDir)SDK\libraries\kernel" +mkdir "$(OutDir)SDK\libraries\user" +copy "$(OutDir)pdbex.dll" "$(OutDir)SDK\libraries\user\pdbex.dll" +copy "$(OutDir)libipt.dll" "$(OutDir)SDK\libraries\user\libipt.dll" +copy "$(OutDir)script-engine.dll" "$(OutDir)SDK\libraries\user\script-engine.dll" +copy "$(OutDir)symbol-parser.dll" "$(OutDir)SDK\libraries\user\symbol-parser.dll" +copy "$(OutDir)libhyperdbg.dll" "$(OutDir)SDK\libraries\user\libhyperdbg.dll" +copy "$(OutDir)hyperlog.dll" "$(OutDir)SDK\libraries\kernel\hyperlog.dll" +copy "$(OutDir)hyperhv.dll" "$(OutDir)SDK\libraries\kernel\hyperhv.dll" +copy "$(OutDir)hypertrace.dll" "$(OutDir)SDK\libraries\kernel\hyperevade.dll" +copy "$(OutDir)hypertrace.dll" "$(OutDir)SDK\libraries\kernel\hypertrace.dll" +copy "$(OutDir)hyperperf.dll" "$(OutDir)SDK\libraries\kernel\hyperperf.dll" +copy "$(OutDir)kdserial.dll" "$(OutDir)SDK\libraries\kernel\kdserial.dll" if exist "$(OutDir)constants\" rd /q /s "$(OutDir)constants\" mkdir "$(OutDir)constants" copy "$(SolutionDir)miscellaneous\constants\pciid\pci.ids" "$(OutDir)constants\pci.ids" @@ -126,15 +132,21 @@ copy "$(SolutionDir)miscellaneous\constants\pciid\pci.ids" "$(OutDir)constants\p if exist "$(OutDir)SDK\" rd /q /s "$(OutDir)SDK\" xcopy /E /I /Y "$(SolutionDir)include\SDK" "$(OutDir)SDK" -xcopy /E /I /Y "$(SolutionDir)..\examples" "$(OutDir)SDK\Examples" -mkdir "$(OutDir)SDK\Libraries" -copy "$(OutDir)pdbex.lib" "$(OutDir)SDK\Libraries\pdbex.lib" -copy "$(OutDir)kdserial.lib" "$(OutDir)SDK\Libraries\kdserial.lib" -copy "$(OutDir)libhyperdbg.dll" "$(OutDir)SDK\Libraries\libhyperdbg.dll" -copy "$(OutDir)script-engine.dll" "$(OutDir)SDK\Libraries\script-engine.dll" -copy "$(OutDir)symbol-parser.dll" "$(OutDir)SDK\Libraries\symbol-parser.dll" -copy "$(OutDir)hyperlog.dll" "$(OutDir)SDK\Libraries\hyperlog.dll" -copy "$(OutDir)hyperhv.dll" "$(OutDir)SDK\Libraries\hyperhv.dll" +xcopy /E /I /Y "$(SolutionDir)..\examples" "$(OutDir)SDK\examples" +mkdir "$(OutDir)SDK\libraries" +mkdir "$(OutDir)SDK\libraries\kernel" +mkdir "$(OutDir)SDK\libraries\user" +copy "$(OutDir)pdbex.dll" "$(OutDir)SDK\libraries\user\pdbex.dll" +copy "$(OutDir)libipt.dll" "$(OutDir)SDK\libraries\user\libipt.dll" +copy "$(OutDir)script-engine.dll" "$(OutDir)SDK\libraries\user\script-engine.dll" +copy "$(OutDir)symbol-parser.dll" "$(OutDir)SDK\libraries\user\symbol-parser.dll" +copy "$(OutDir)libhyperdbg.dll" "$(OutDir)SDK\libraries\user\libhyperdbg.dll" +copy "$(OutDir)hyperlog.dll" "$(OutDir)SDK\libraries\kernel\hyperlog.dll" +copy "$(OutDir)hyperhv.dll" "$(OutDir)SDK\libraries\kernel\hyperhv.dll" +copy "$(OutDir)hypertrace.dll" "$(OutDir)SDK\libraries\kernel\hyperevade.dll" +copy "$(OutDir)hypertrace.dll" "$(OutDir)SDK\libraries\kernel\hypertrace.dll" +copy "$(OutDir)hyperperf.dll" "$(OutDir)SDK\libraries\kernel\hyperperf.dll" +copy "$(OutDir)kdserial.dll" "$(OutDir)SDK\libraries\kernel\kdserial.dll" if exist "$(OutDir)constants\" rd /q /s "$(OutDir)constants\" mkdir "$(OutDir)constants" copy "$(SolutionDir)miscellaneous\constants\pciid\pci.ids" "$(OutDir)constants\pci.ids" diff --git a/hyperdbg/hyperdbg-test/hyperdbg-test.vcxproj b/hyperdbg/hyperdbg-test/hyperdbg-test.vcxproj index 1ce467c7..b50f7554 100644 --- a/hyperdbg/hyperdbg-test/hyperdbg-test.vcxproj +++ b/hyperdbg/hyperdbg-test/hyperdbg-test.vcxproj @@ -21,14 +21,14 @@ Application true - v143 + v145 Unicode false Application false - v143 + v145 true Unicode false @@ -134,4 +134,4 @@ - + \ No newline at end of file diff --git a/hyperdbg/hyperdbg.sln b/hyperdbg/hyperdbg.sln index fff4dc56..90b129a9 100644 --- a/hyperdbg/hyperdbg.sln +++ b/hyperdbg/hyperdbg.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.2.32602.215 +# Visual Studio Version 18 +VisualStudioVersion = 18.7.11911.148 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hyperdbg-cli", "hyperdbg-cli\hyperdbg-cli.vcxproj", "{FBCBBBAD-4EAE-469E-827F-F59FE9E7375B}" ProjectSection(ProjectDependencies) = postProject @@ -90,6 +90,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "imports", "imports", "{B3D9 EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hyperkd", "hyperkd\hyperkd.vcxproj", "{AFDD7028-1ED9-442E-8A3D-01CFA3AA1CAA}" ProjectSection(ProjectDependencies) = postProject + {360E54B1-0B92-4BCA-8111-4BF384292621} = {360E54B1-0B92-4BCA-8111-4BF384292621} {9FA45E25-DAEB-4C2D-806C-7908A180195D} = {9FA45E25-DAEB-4C2D-806C-7908A180195D} {AFDE69E9-EE3D-470E-8407-C1F0D98F9E3D} = {AFDE69E9-EE3D-470E-8407-C1F0D98F9E3D} {BB17323A-2460-4AE1-8AFE-B367400B934F} = {BB17323A-2460-4AE1-8AFE-B367400B934F} @@ -101,6 +102,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "modules", "modules", "{13E4 ProjectSection(SolutionItems) = preProject include\SDK\modules\HyperEvade.h = include\SDK\modules\HyperEvade.h include\SDK\Modules\HyperLog.h = include\SDK\Modules\HyperLog.h + include\SDK\modules\HyperPerf.h = include\SDK\modules\HyperPerf.h include\SDK\modules\HyperTrace.h = include\SDK\modules\HyperTrace.h include\SDK\Modules\VMM.h = include\SDK\Modules\VMM.h EndProjectSection @@ -207,6 +209,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "kernel", "kernel", "{947577 include\SDK\imports\kernel\HyperDbgHyperEvade.h = include\SDK\imports\kernel\HyperDbgHyperEvade.h include\SDK\Imports\Kernel\HyperDbgHyperLogImports.h = include\SDK\Imports\Kernel\HyperDbgHyperLogImports.h include\SDK\Imports\Kernel\HyperDbgHyperLogIntrinsics.h = include\SDK\Imports\Kernel\HyperDbgHyperLogIntrinsics.h + include\SDK\imports\kernel\HyperDbgHyperPerf.h = include\SDK\imports\kernel\HyperDbgHyperPerf.h include\SDK\imports\kernel\HyperDbgHyperTrace.h = include\SDK\imports\kernel\HyperDbgHyperTrace.h include\SDK\Imports\Kernel\HyperDbgVmmImports.h = include\SDK\Imports\Kernel\HyperDbgVmmImports.h EndProjectSection @@ -327,6 +330,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "header", "header", "{CA2D9C include\components\pe\header\pe-image-reader.h = include\components\pe\header\pe-image-reader.h EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hyperperf", "hyperperf\hyperperf.vcxproj", "{360E54B1-0B92-4BCA-8111-4BF384292621}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution debug|x64 = debug|x64 @@ -384,6 +389,10 @@ Global {9FA45E25-DAEB-4C2D-806C-7908A180195D}.debug|x64.Build.0 = debug|x64 {9FA45E25-DAEB-4C2D-806C-7908A180195D}.release|x64.ActiveCfg = release|x64 {9FA45E25-DAEB-4C2D-806C-7908A180195D}.release|x64.Build.0 = release|x64 + {360E54B1-0B92-4BCA-8111-4BF384292621}.debug|x64.ActiveCfg = debug|x64 + {360E54B1-0B92-4BCA-8111-4BF384292621}.debug|x64.Build.0 = debug|x64 + {360E54B1-0B92-4BCA-8111-4BF384292621}.release|x64.ActiveCfg = release|x64 + {360E54B1-0B92-4BCA-8111-4BF384292621}.release|x64.Build.0 = release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/hyperdbg/hyperevade/header/SyscallFootprints.h b/hyperdbg/hyperevade/header/SyscallFootprints.h index 40ab41dc..02e3d6a3 100644 --- a/hyperdbg/hyperevade/header/SyscallFootprints.h +++ b/hyperdbg/hyperevade/header/SyscallFootprints.h @@ -362,7 +362,7 @@ static const PWCH HV_FILES[] = { // HyperDbg Files // L"hyperhv", - L"hyperkd" + L"hyperkd", L"hyperlog", L"libhyperdbg", diff --git a/hyperdbg/hyperevade/hyperevade.vcxproj b/hyperdbg/hyperevade/hyperevade.vcxproj index 5acb9c74..ea3a07dc 100644 --- a/hyperdbg/hyperevade/hyperevade.vcxproj +++ b/hyperdbg/hyperevade/hyperevade.vcxproj @@ -1,5 +1,8 @@  + + + debug @@ -27,7 +30,7 @@ WindowsKernelModeDriver10.0 DynamicLibrary KMDF - Universal + Desktop false @@ -36,7 +39,7 @@ WindowsKernelModeDriver10.0 DynamicLibrary KMDF - Universal + Desktop false @@ -122,7 +125,20 @@ + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperevade/hyperevade.vcxproj.filters b/hyperdbg/hyperevade/hyperevade.vcxproj.filters index d96f4ba2..f7ed48f3 100644 --- a/hyperdbg/hyperevade/hyperevade.vcxproj.filters +++ b/hyperdbg/hyperevade/hyperevade.vcxproj.filters @@ -99,4 +99,7 @@ header\components\callback + + + \ No newline at end of file diff --git a/hyperdbg/hyperevade/packages.config b/hyperdbg/hyperevade/packages.config new file mode 100644 index 00000000..eb276766 --- /dev/null +++ b/hyperdbg/hyperevade/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperhv/code/vmm/ept/Ept.c b/hyperdbg/hyperhv/code/vmm/ept/Ept.c index f237b710..4295aca0 100644 --- a/hyperdbg/hyperhv/code/vmm/ept/Ept.c +++ b/hyperdbg/hyperhv/code/vmm/ept/Ept.c @@ -23,7 +23,7 @@ EptCheckFeatures(VOID) { IA32_VMX_EPT_VPID_CAP_REGISTER VpidRegister; IA32_MTRR_DEF_TYPE_REGISTER MTRRDefType; - + g_IsVpidSupported = FALSE; VpidRegister.AsUInt = CpuReadMsr(IA32_VMX_EPT_VPID_CAP); MTRRDefType.AsUInt = CpuReadMsr(IA32_MTRR_DEF_TYPE); @@ -37,6 +37,17 @@ EptCheckFeatures(VOID) LogDebugInfo("The processor doesn't report advanced VM-exit information for EPT violations"); } + if (VpidRegister.Invvpid && + VpidRegister.InvvpidAllContexts && + VpidRegister.InvvpidIndividualAddress && + VpidRegister.InvvpidSingleContext && + VpidRegister.InvvpidSingleContextRetainGlobals && + !g_IsTopLevelHypervisorHyperV) + { + g_IsVpidSupported = TRUE; + LogDebugInfo("The processor supports VPID"); + } + if (!VpidRegister.ExecuteOnlyPages) { g_CompatibilityCheck.ExecuteOnlySupport = FALSE; diff --git a/hyperdbg/hyperhv/code/vmm/ept/Vpid.c b/hyperdbg/hyperhv/code/vmm/ept/Vpid.c index a4874c4d..cd587b57 100644 --- a/hyperdbg/hyperhv/code/vmm/ept/Vpid.c +++ b/hyperdbg/hyperhv/code/vmm/ept/Vpid.c @@ -25,6 +25,12 @@ VpidInvvpid(INVVPID_TYPE Type, INVVPID_DESCRIPTOR * Descriptor) INVVPID_DESCRIPTOR * TargetDescriptor = NULL; INVVPID_DESCRIPTOR ZeroDescriptor = {0}; + // No need to do anything if VPID is not supported. + if (!g_IsVpidSupported) + { + return; + } + if (!Descriptor) { TargetDescriptor = &ZeroDescriptor; diff --git a/hyperdbg/hyperhv/code/vmm/vmx/MsrHandlers.c b/hyperdbg/hyperhv/code/vmm/vmx/MsrHandlers.c index 8998e8e1..2600002a 100644 --- a/hyperdbg/hyperhv/code/vmm/vmx/MsrHandlers.c +++ b/hyperdbg/hyperhv/code/vmm/vmx/MsrHandlers.c @@ -96,9 +96,11 @@ MsrHandleRdmsrVmexit(VIRTUAL_MACHINE_STATE * VCpu) // LogInfo("MSR read (RDMSR) VM-exit, MSR: %x, from: %llx", // TargetMsr, // VCpu->LastVmexitRip); + // - // Checking whether it is a synthetic MSR for Hyper-V. - if (MsrHandleIsHypervSyntheticMsr(TargetMsr)) + // Checking whether it is a synthetic MSR for Hyper-V + // + if (g_IsTopLevelHypervisorHyperV && MsrHandleIsHypervSyntheticMsr(TargetMsr)) { Msr.Flags = CpuReadMsr(TargetMsr); @@ -257,9 +259,11 @@ MsrHandleWrmsrVmexit(VIRTUAL_MACHINE_STATE * VCpu) // GuestRegs->rax, // GuestRegs->rdx, // VCpu->LastVmexitRip); + // - // Checking whether it is a synthetic MSR for Hyper-V. - if (MsrHandleIsHypervSyntheticMsr(TargetMsr)) + // Checking whether it is a synthetic MSR for Hyper-V + // + if (g_IsTopLevelHypervisorHyperV && MsrHandleIsHypervSyntheticMsr(TargetMsr)) { CpuWriteMsr(TargetMsr, Msr.Flags); return; diff --git a/hyperdbg/hyperhv/code/vmm/vmx/Vmx.c b/hyperdbg/hyperhv/code/vmm/vmx/Vmx.c index ab4eab29..1de5c030 100644 --- a/hyperdbg/hyperhv/code/vmm/vmx/Vmx.c +++ b/hyperdbg/hyperhv/code/vmm/vmx/Vmx.c @@ -274,6 +274,23 @@ VmxPerformVirtualizationOnAllCores() return FALSE; } + // + // Check if the top level hypervisor is Hyper-V or not + // + if (VmxIsTopLevelHypervisorHyperV()) + { + // + // The top-level hypervisor is hyper-v + // + g_IsTopLevelHypervisorHyperV = TRUE; + + LogDebugInfo(" Hyper-V is detected as the top-level hypervisor"); + } + else + { + LogDebugInfo(" Hyper-V is not detected as the top-level hypervisor"); + } + // // Allocate global variable to hold Ept State // @@ -733,15 +750,17 @@ VmxSetupVmcs(VIRTUAL_MACHINE_STATE * VCpu, PVOID GuestStack) LogDebugInfo("CPU Based VM Exec Controls (Based on %s) : 0x%x", VmxBasicMsr.VmxControls ? "IA32_VMX_TRUE_PROCBASED_CTLS" : "IA32_VMX_PROCBASED_CTLS", CpuBasedVmExecControls); + UINT32 SecondaryProcBasedVmExecControlsFlags = IA32_VMX_PROCBASED_CTLS2_ENABLE_RDTSCP_FLAG | + IA32_VMX_PROCBASED_CTLS2_ENABLE_EPT_FLAG | + IA32_VMX_PROCBASED_CTLS2_ENABLE_INVPCID_FLAG | + IA32_VMX_PROCBASED_CTLS2_ENABLE_XSAVES_FLAG | + IA32_VMX_PROCBASED_CTLS2_ENABLE_USER_WAIT_PAUSE_FLAG; - SecondaryProcBasedVmExecControls = HvAdjustControls( - IA32_VMX_PROCBASED_CTLS2_ENABLE_RDTSCP_FLAG | - IA32_VMX_PROCBASED_CTLS2_ENABLE_EPT_FLAG | - IA32_VMX_PROCBASED_CTLS2_ENABLE_INVPCID_FLAG | - IA32_VMX_PROCBASED_CTLS2_ENABLE_XSAVES_FLAG | - IA32_VMX_PROCBASED_CTLS2_ENABLE_VPID_FLAG | - IA32_VMX_PROCBASED_CTLS2_ENABLE_USER_WAIT_PAUSE_FLAG, - IA32_VMX_PROCBASED_CTLS2); + if (g_IsVpidSupported) + { + SecondaryProcBasedVmExecControlsFlags |= IA32_VMX_PROCBASED_CTLS2_ENABLE_VPID_FLAG; + } + SecondaryProcBasedVmExecControls = HvAdjustControls(SecondaryProcBasedVmExecControlsFlags, IA32_VMX_PROCBASED_CTLS2); VmxVmwrite64(VMCS_CTRL_SECONDARY_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, SecondaryProcBasedVmExecControls); @@ -1755,3 +1774,28 @@ VmxCompatibleMemcmp(const CHAR * Address1, const CHAR * Address2, SIZE_T Count) CpuWriteCr3(OriginalCr3.Flags); return Result; } + +/** + * @brief checks if the current top level hypervisor is Hyper-V + * + * + * @return BOOLEAN TRUE indicates that the current top level hypervisor is Hyper-V, FALSE otherwise. + */ +BOOLEAN +VmxIsTopLevelHypervisorHyperV() +{ + INT32 ProcessorFeatures[4] = {0}; + CpuCpuIdEx(ProcessorFeatures, CPUID_PROCESSOR_AND_PROCESSOR_FEATURE_IDENTIFIERS, 0); + + if (!((ULONG)(ProcessorFeatures[2]) & HYPERV_HYPERVISOR_PRESENT_BIT)) + { + return FALSE; + } + + INT32 HypervisorVendor[4] = {0}; + CpuCpuIdEx(HypervisorVendor, HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS, 0); + + return (ULONG)(HypervisorVendor[1]) == HYPERV_CPUID_VENDOR_MICROSOFT_EBX && + (ULONG)(HypervisorVendor[2]) == HYPERV_CPUID_VENDOR_MICROSOFT_ECX && + (ULONG)(HypervisorVendor[3]) == HYPERV_CPUID_VENDOR_MICROSOFT_EDX; +} diff --git a/hyperdbg/hyperhv/header/globals/GlobalVariables.h b/hyperdbg/hyperhv/header/globals/GlobalVariables.h index 8a4681bb..b0b71f10 100644 --- a/hyperdbg/hyperhv/header/globals/GlobalVariables.h +++ b/hyperdbg/hyperhv/header/globals/GlobalVariables.h @@ -208,3 +208,13 @@ UINT64 g_PageFaultInjectionAddressTo; * */ UINT32 g_PageFaultInjectionErrorCode; + +/** + * @brief Whether VPID is supported or not + */ +BOOLEAN g_IsVpidSupported; + +/** + * @brief Whether the top level hypervisor is Hyper-V or not + */ +BOOLEAN g_IsTopLevelHypervisorHyperV; diff --git a/hyperdbg/hyperhv/header/vmm/vmx/Vmx.h b/hyperdbg/hyperhv/header/vmm/vmx/Vmx.h index 0ff36878..2cb7b05d 100644 --- a/hyperdbg/hyperhv/header/vmm/vmx/Vmx.h +++ b/hyperdbg/hyperhv/header/vmm/vmx/Vmx.h @@ -205,6 +205,12 @@ enum HYPERCALL_CODE HvCallFlushGuestPhysicalAddressList = 0x00B0 }; +/** @brief Hyper-V CPUID leaves +*/ +#define HYPERV_CPUID_VENDOR_MICROSOFT_EBX 0x7263694d // "Micr" +#define HYPERV_CPUID_VENDOR_MICROSOFT_ECX 0x666f736f // "osof" +#define HYPERV_CPUID_VENDOR_MICROSOFT_EDX 0x76482074 // "t Hv" + ////////////////////////////////////////////////// // Enums // ////////////////////////////////////////////////// @@ -229,6 +235,9 @@ typedef enum _MOV_TO_DEBUG_REG // Functions // ////////////////////////////////////////////////// +BOOLEAN +VmxIsTopLevelHypervisorHyperV(); + BOOLEAN VmxCheckVmxSupport(); diff --git a/hyperdbg/hyperhv/hyperhv.vcxproj b/hyperdbg/hyperhv/hyperhv.vcxproj index f2dc2413..53c55a14 100644 --- a/hyperdbg/hyperhv/hyperhv.vcxproj +++ b/hyperdbg/hyperhv/hyperhv.vcxproj @@ -1,5 +1,8 @@  + + + debug @@ -28,7 +31,7 @@ WindowsKernelModeDriver10.0 DynamicLibrary KMDF - Universal + Desktop false false @@ -38,7 +41,7 @@ WindowsKernelModeDriver10.0 DynamicLibrary KMDF - Universal + Desktop false false @@ -53,13 +56,16 @@ DbgengKernelDebugger - true + + $(SolutionDir)build\bin\$(Configuration)\ $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + false DbgengKernelDebugger - true + + $(SolutionDir)build\bin\$(Configuration)\ false $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ @@ -296,8 +302,21 @@ + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperhv/hyperhv.vcxproj.filters b/hyperdbg/hyperhv/hyperhv.vcxproj.filters index f8fcb900..fbbe5e33 100644 --- a/hyperdbg/hyperhv/hyperhv.vcxproj.filters +++ b/hyperdbg/hyperhv/hyperhv.vcxproj.filters @@ -649,4 +649,7 @@ code\assembly + + + \ No newline at end of file diff --git a/hyperdbg/hyperhv/packages.config b/hyperdbg/hyperhv/packages.config new file mode 100644 index 00000000..eb276766 --- /dev/null +++ b/hyperdbg/hyperhv/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperkd/code/debugger/memory/PoolManager.c b/hyperdbg/hyperkd/code/debugger/memory/PoolManager.c index f676e3e7..3c195e68 100644 --- a/hyperdbg/hyperkd/code/debugger/memory/PoolManager.c +++ b/hyperdbg/hyperkd/code/debugger/memory/PoolManager.c @@ -92,7 +92,7 @@ PoolManagerInitialize() VOID PoolManagerUninitialize() { - PLIST_ENTRY ListTemp = &g_ListOfAllocatedPoolsHead; + PLIST_ENTRY Link; // // Pool manager is not initialized anymore @@ -101,14 +101,16 @@ PoolManagerUninitialize() SpinlockLock(&LockForReadingPool); - while (&g_ListOfAllocatedPoolsHead != ListTemp->Flink) + Link = g_ListOfAllocatedPoolsHead.Flink; + + while (Link != &g_ListOfAllocatedPoolsHead) { - ListTemp = ListTemp->Flink; + PLIST_ENTRY Next = Link->Flink; // // Get the head of the record // - PPOOL_TABLE PoolTable = (PPOOL_TABLE)CONTAINING_RECORD(ListTemp, POOL_TABLE, PoolsList); + PPOOL_TABLE PoolTable = (PPOOL_TABLE)CONTAINING_RECORD(Link, POOL_TABLE, PoolsList); // // Free the alloocated buffer (if not already changed) @@ -121,14 +123,20 @@ PoolManagerUninitialize() // // Unlink the PoolTable // - RemoveEntryList(&PoolTable->PoolsList); + RemoveEntryList(Link); // // Free the record itself // PlatformMemFreePool(PoolTable); + + Link = Next; } + InitializeListHead(&g_ListOfAllocatedPoolsHead); + g_IsNewRequestForDeAllocation = FALSE; + g_IsNewRequestForAllocationReceived = FALSE; + SpinlockUnlock(&LockForReadingPool); PlmgrFreeRequestNewAllocation(); @@ -311,8 +319,7 @@ PoolManagerAllocateAndAddToPoolTable(SIZE_T Size, UINT32 Count, POOL_ALLOCATION_ BOOLEAN PoolManagerCheckAndPerformAllocationAndDeallocation() { - BOOLEAN Result = TRUE; - PLIST_ENTRY ListTemp = 0; + BOOLEAN Result = TRUE; // // Make sure we're on vmx non-root and also we have new allocation @@ -364,16 +371,16 @@ PoolManagerCheckAndPerformAllocationAndDeallocation() // if (g_IsNewRequestForDeAllocation) { - ListTemp = &g_ListOfAllocatedPoolsHead; + PLIST_ENTRY Link = g_ListOfAllocatedPoolsHead.Flink; - while (&g_ListOfAllocatedPoolsHead != ListTemp->Flink) + while (Link != &g_ListOfAllocatedPoolsHead) { - ListTemp = ListTemp->Flink; + PLIST_ENTRY Next = Link->Flink; // // Get the head of the record // - PPOOL_TABLE PoolTable = (PPOOL_TABLE)CONTAINING_RECORD(ListTemp, POOL_TABLE, PoolsList); + PPOOL_TABLE PoolTable = (PPOOL_TABLE)CONTAINING_RECORD(Link, POOL_TABLE, PoolsList); // // Check whether this pool should be freed or not and @@ -394,13 +401,15 @@ PoolManagerCheckAndPerformAllocationAndDeallocation() // // Now we should remove the entry from the g_ListOfAllocatedPoolsHead // - RemoveEntryList(&PoolTable->PoolsList); + RemoveEntryList(Link); // // Free the structure pool // PlatformMemFreePool(PoolTable); } + + Link = Next; } } diff --git a/hyperdbg/hyperkd/code/driver/Ioctl.c b/hyperdbg/hyperkd/code/driver/Ioctl.c index 7b838ce0..ddd88593 100644 --- a/hyperdbg/hyperkd/code/driver/Ioctl.c +++ b/hyperdbg/hyperkd/code/driver/Ioctl.c @@ -134,6 +134,74 @@ IoctlCheckIoctlAllowed(ULONG Ioctl) } } +/** + * @brief Resolve a process id to the CR3 the Intel PT engine should match. + * + * @details Intel PT's CR3 filter compares the live CR3, which differs between + * user and kernel mode when KVA shadowing (KPTI) is enabled: + * - kernel-mode runs under _KPROCESS.DirectoryTableBase + * - user-mode runs under _KPROCESS.UserDirectoryTableBase (shadow) + * So pick the kernel CR3 for kernel-only traces, otherwise the user + * (shadow) CR3 — falling back to the kernel CR3 when KVA shadowing is + * off (UserDirectoryTableBase has a zero page base in that case, i.e. + * user-mode also runs under the kernel CR3). + * + * @param ProcessId Target process id + * @param TraceUser Whether CPL>0 will be traced + * @param TraceKernel Whether CPL==0 will be traced + * + * @return UINT64 CR3 page base (low 12 bits cleared), or 0 if the pid is gone + */ +static UINT64 +DrvResolvePtTargetCr3(UINT32 ProcessId, BOOLEAN TraceUser, BOOLEAN TraceKernel) +{ + // + // _KPROCESS.UserDirectoryTableBase offset (x64 Windows 10 1803+ / 11). + // Only this one constant is build-specific; adjust it if the user CR3 + // ever reads wrong on a newer kernel. + // + const ULONG UserDirTableBaseOffset = 0x388; + + PEPROCESS TargetProcess; + UINT64 KernelCr3; + UINT64 UserCr3; + UINT64 Chosen; + + if (PsLookupProcessByProcessId((HANDLE)(ULONG_PTR)ProcessId, &TargetProcess) != STATUS_SUCCESS) + { + return 0; + } + + KernelCr3 = (UINT64)((NT_KPROCESS *)TargetProcess)->DirectoryTableBase; + UserCr3 = *(UINT64 *)((UCHAR *)TargetProcess + UserDirTableBaseOffset); + + ObDereferenceObject(TargetProcess); + + if (TraceKernel && !TraceUser) + { + // + // Kernel-only trace — match the kernel CR3 + // + Chosen = KernelCr3; + } + else if ((UserCr3 & ~0xFFFULL) != 0) + { + // + // KVA shadowing on — user mode runs under the shadow CR3 + // + Chosen = UserCr3; + } + else + { + // + // KVA shadowing off — user mode runs under the kernel CR3 + // + Chosen = KernelCr3; + } + + return Chosen & ~0xFFFULL; +} + /** * @brief IOCTL Dispatcher for Basic IOCTLs (initialization and event registration) * @@ -1500,6 +1568,7 @@ DrvDispatchHyperTraceIoControl(PIRP Irp, PIO_STACK_LOCATION IrpStack, BOOLEAN * PHYPERTRACE_LBR_OPERATION_PACKETS HyperTraceLbrOperationRequest; PHYPERTRACE_LBR_DUMP_PACKETS HyperTraceLbrdumpRequest; PHYPERTRACE_PT_OPERATION_PACKETS HyperTracePtOperationRequest; + PHYPERTRACE_PT_MMAP_PACKETS HyperTracePtMmapRequest; ULONG InBuffLength; ULONG OutBuffLength; NTSTATUS Status = STATUS_SUCCESS; @@ -1593,6 +1662,23 @@ DrvDispatchHyperTraceIoControl(PIRP Irp, PIO_STACK_LOCATION IrpStack, BOOLEAN * break; } + // + // If the caller asked to filter by a process id (and didn't + // already provide an explicit CR3), resolve the PID to the CR3 + // the PT engine should match here — hyperkd owns the NT_KPROCESS + // layout, whereas the hypertrace engine only consumes a CR3. The + // kernel/user CR3 is chosen based on the requested trace mode so + // it works whether or not KVA shadowing (KPTI) is enabled. + // + if (HyperTracePtOperationRequest->EnableOptions.Pid != 0 && + HyperTracePtOperationRequest->EnableOptions.Cr3 == 0) + { + HyperTracePtOperationRequest->EnableOptions.Cr3 = + DrvResolvePtTargetCr3(HyperTracePtOperationRequest->EnableOptions.Pid, + (BOOLEAN)(HyperTracePtOperationRequest->FilterOptions.TraceUser != 0), + (BOOLEAN)(HyperTracePtOperationRequest->FilterOptions.TraceKernel != 0)); + } + // // Perform the HyperTrace PT operation // @@ -1605,6 +1691,34 @@ DrvDispatchHyperTraceIoControl(PIRP Irp, PIO_STACK_LOCATION IrpStack, BOOLEAN * break; + case IOCTL_PERFORM_HYPERTRACE_PT_MMAP: + + // + // Validate and adjust the parameters, and set the target buffer to the system buffer of the IRP + // + if (!DrvValidateAndAdjustIoctlParameter(SIZEOF_HYPERTRACE_PT_MMAP_PACKETS, + (PVOID *)&HyperTracePtMmapRequest, + Irp, + IrpStack, + &InBuffLength, + &OutBuffLength)) + { + Status = STATUS_INVALID_PARAMETER; + break; + } + + // + // Map the per-CPU PT output buffers into the calling user process + // + HyperTracePtMmap(HyperTracePtMmapRequest); + + // + // Adjust the status and output size + // + DrvAdjustStatusAndSetOutputSize(SIZEOF_HYPERTRACE_PT_MMAP_PACKETS, DoNotChangeInformation, Irp, &Status); + + break; + default: LogError("Err, unknown IOCTL"); Status = STATUS_NOT_IMPLEMENTED; diff --git a/hyperdbg/hyperkd/hyperkd.vcxproj b/hyperdbg/hyperkd/hyperkd.vcxproj index c7a6d222..b165f75d 100644 --- a/hyperdbg/hyperkd/hyperkd.vcxproj +++ b/hyperdbg/hyperkd/hyperkd.vcxproj @@ -1,5 +1,8 @@  + + + debug @@ -27,7 +30,7 @@ WindowsKernelModeDriver10.0 Driver KMDF - Universal + Desktop false @@ -36,7 +39,7 @@ WindowsKernelModeDriver10.0 Driver KMDF - Universal + Desktop false @@ -51,13 +54,16 @@ DbgengKernelDebugger $(SolutionDir)build\bin\$(Configuration)\ $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - true + + + false DbgengKernelDebugger $(SolutionDir)build\bin\$(Configuration)\ $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - true + + false @@ -205,7 +211,20 @@ + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperkd/hyperkd.vcxproj.filters b/hyperdbg/hyperkd/hyperkd.vcxproj.filters index 55eeeb30..8a1a1c5b 100644 --- a/hyperdbg/hyperkd/hyperkd.vcxproj.filters +++ b/hyperdbg/hyperkd/hyperkd.vcxproj.filters @@ -434,4 +434,7 @@ code\assembly + + + \ No newline at end of file diff --git a/hyperdbg/hyperkd/packages.config b/hyperdbg/hyperkd/packages.config new file mode 100644 index 00000000..eb276766 --- /dev/null +++ b/hyperdbg/hyperkd/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperlog/hyperlog.inf b/hyperdbg/hyperlog/hyperlog.inf deleted file mode 100644 index 8601163e..00000000 --- a/hyperdbg/hyperlog/hyperlog.inf +++ /dev/null @@ -1,77 +0,0 @@ -; -; hyperlog.inf -; - -[Version] -Signature="$WINDOWS NT$" -Class=System ; TODO: specify appropriate Class -ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318} ; TODO: specify appropriate ClassGuid -Provider=%ManufacturerName% -CatalogFile=hyperlog.cat -DriverVer= ; TODO: set DriverVer in stampinf property pages -PnpLockdown=1 - -[DestinationDirs] -DefaultDestDir = 12 -hyperlog_Device_CoInstaller_CopyFiles = 11 - -[SourceDisksNames] -1 = %DiskName%,,,"" - -[SourceDisksFiles] -hyperlog.sys = 1,, -WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames - -;***************************************** -; Install Section -;***************************************** - -[Manufacturer] -%ManufacturerName%=Standard,NT$ARCH$ - -[Standard.NT$ARCH$] -%hyperlog.DeviceDesc%=hyperlog_Device, Root\hyperlog ; TODO: edit hw-id - -[hyperlog_Device.NT] -CopyFiles=Drivers_Dir - -[Drivers_Dir] -hyperlog.sys - -;-------------- Service installation -[hyperlog_Device.NT.Services] -AddService = hyperlog,%SPSVCINST_ASSOCSERVICE%, hyperlog_Service_Inst - -; -------------- hyperlog driver install sections -[hyperlog_Service_Inst] -DisplayName = %hyperlog.SVCDESC% -ServiceType = 1 ; SERVICE_KERNEL_DRIVER -StartType = 3 ; SERVICE_DEMAND_START -ErrorControl = 1 ; SERVICE_ERROR_NORMAL -ServiceBinary = %12%\hyperlog.sys - -; -;--- hyperlog_Device Coinstaller installation ------ -; - -[hyperlog_Device.NT.CoInstallers] -AddReg=hyperlog_Device_CoInstaller_AddReg -CopyFiles=hyperlog_Device_CoInstaller_CopyFiles - -[hyperlog_Device_CoInstaller_AddReg] -HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" - -[hyperlog_Device_CoInstaller_CopyFiles] -WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll - -[hyperlog_Device.NT.Wdf] -KmdfService = hyperlog, hyperlog_wdfsect -[hyperlog_wdfsect] -KmdfLibraryVersion = $KMDFVERSION$ - -[Strings] -SPSVCINST_ASSOCSERVICE= 0x00000002 -ManufacturerName="" ;TODO: Replace with your manufacturer name -DiskName = "hyperlog Installation Disk" -hyperlog.DeviceDesc = "hyperlog Device" -hyperlog.SVCDESC = "hyperlog Service" diff --git a/hyperdbg/hyperlog/hyperlog.vcxproj b/hyperdbg/hyperlog/hyperlog.vcxproj index 4147f9d2..5e0ba787 100644 --- a/hyperdbg/hyperlog/hyperlog.vcxproj +++ b/hyperdbg/hyperlog/hyperlog.vcxproj @@ -1,5 +1,8 @@  + + + debug @@ -27,7 +30,7 @@ WindowsKernelModeDriver10.0 DynamicLibrary KMDF - Universal + Desktop false @@ -36,7 +39,7 @@ WindowsKernelModeDriver10.0 DynamicLibrary KMDF - Universal + Desktop false @@ -96,9 +99,6 @@ hyperlog.def - - - @@ -130,7 +130,20 @@ + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperlog/hyperlog.vcxproj.filters b/hyperdbg/hyperlog/hyperlog.vcxproj.filters index f6867f82..b7765775 100644 --- a/hyperdbg/hyperlog/hyperlog.vcxproj.filters +++ b/hyperdbg/hyperlog/hyperlog.vcxproj.filters @@ -20,11 +20,6 @@ {21f0281e-fc2a-4e13-97ac-e4b35a05a31e} - - - Driver Files - - code @@ -101,4 +96,7 @@ header\platform + + + \ No newline at end of file diff --git a/hyperdbg/hyperlog/packages.config b/hyperdbg/hyperlog/packages.config new file mode 100644 index 00000000..eb276766 --- /dev/null +++ b/hyperdbg/hyperlog/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperperf/CMakeLists.txt b/hyperdbg/hyperperf/CMakeLists.txt new file mode 100644 index 00000000..d6b1310f --- /dev/null +++ b/hyperdbg/hyperperf/CMakeLists.txt @@ -0,0 +1,22 @@ +# Code generated by Visual Studio kit, DO NOT EDIT. +set(SourceFiles + "../include/components/spinlock/code/Spinlock.c" + "../include/platform/kernel/code/PlatformMem.c" + "code/Logging.c" + "code/UnloadDll.c" + "../include/components/spinlock/header/Spinlock.h" + "../include/platform/kernel/header/Environment.h" + "../include/platform/kernel/header/PlatformMem.h" + "header/Logging.h" + "header/pch.h" + "header/UnloadDll.h" + "hyperperf.def" +) +include_directories( + "../include" + "header" +) +wdk_add_library(hyperperf SHARED + KMDF 1.15 + ${SourceFiles} +) diff --git a/hyperdbg/hyperperf/code/api/PerfApi.c b/hyperdbg/hyperperf/code/api/PerfApi.c new file mode 100644 index 00000000..59cef4c4 --- /dev/null +++ b/hyperdbg/hyperperf/code/api/PerfApi.c @@ -0,0 +1,84 @@ +/** + * @file PerfApi.c + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief PMU routines for HyperPerf module + * @details + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + */ +#include "pch.h" + +/** + * @brief Initialize the hyperperf module callbacks + * @details This only for callback initialization, not for PMU, etc. initialization + * + * @param HyperPerfCallbacks Pointer to the HyperPerf callbacks structure to be registered + * @param RunningOnHypervisorEnvironment Whether the initialization is being done for hypervisor environment or not, + * it can be used to skip some of the initialization steps if it is not for hypervisor environment and behave differently based on that + * + * @return BOOLEAN + */ +BOOLEAN +HyperPerfInitCallback(HYPERPERF_CALLBACKS * HyperPerfCallbacks, + BOOLEAN RunningOnHypervisorEnvironment) +{ + // + // Check if any of the required callbacks are NULL + // + for (UINT32 i = 0; i < sizeof(HYPERPERF_CALLBACKS) / sizeof(UINT64); i++) + { + if (((PVOID *)HyperPerfCallbacks)[i] == NULL) + { + // + // The callback has null entry, so we cannot proceed + // + return FALSE; + } + } + + // + // Save the callbacks + // + PlatformWriteMemory(&g_Callbacks, HyperPerfCallbacks, sizeof(HYPERPERF_CALLBACKS)); + + // + // Set the flag to indicate whether the initialization is being done for hypervisor environment or not + // + g_RunningOnHypervisorEnvironment = RunningOnHypervisorEnvironment; + + // + // Enable callbacks and set the initialized flag + // + g_HyperPerfCallbacksInitialized = TRUE; + + return TRUE; +} + +/** + * @brief Uninitialize the hypertrace module + * + * @return VOID + */ +VOID +HyperPerfUninit() +{ + // + // Check if the callbacks are initialized, if not, we don't need to handle anymore + // + if (!g_HyperPerfCallbacksInitialized) + { + return; + } + + // + // Reset the environment flag to default value + // + g_RunningOnHypervisorEnvironment = FALSE; + + // + // Set callbacks to not initialized + // + g_HyperPerfCallbacksInitialized = FALSE; +} diff --git a/hyperdbg/hyperperf/code/broadcast/Broadcast.c b/hyperdbg/hyperperf/code/broadcast/Broadcast.c new file mode 100644 index 00000000..ad97bd34 --- /dev/null +++ b/hyperdbg/hyperperf/code/broadcast/Broadcast.c @@ -0,0 +1,26 @@ +/** + * @file Broadcast.c + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Broadcasting functions + * @details + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#include "pch.h" + +/** + * @brief Routines to enable LBR on all cores + * + * @return VOID + */ +VOID +BroadcastEnableLbrOnAllCores() +{ + // + // Broadcast to all cores + // + KeGenericCallDpc(DpcRoutineTestPmu, NULL); +} diff --git a/hyperdbg/hyperperf/code/broadcast/DpcRoutines.c b/hyperdbg/hyperperf/code/broadcast/DpcRoutines.c new file mode 100644 index 00000000..f5443618 --- /dev/null +++ b/hyperdbg/hyperperf/code/broadcast/DpcRoutines.c @@ -0,0 +1,35 @@ +/** + * @file DpcRoutines.c + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief DPC routines + * @details + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#include "pch.h" + +/** + * @brief Broadcast enabling LBR + * + * @param Dpc + * @param DeferredContext + * @param SystemArgument1 + * @param SystemArgument2 + * @return BOOLEAN + */ +BOOLEAN +DpcRoutineTestPmu(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2) +{ + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(DeferredContext); + + // ------------------------------------------------------------------------------ + // Synchronize the end of this routine with the caller + // + PlatformBroadcastSynchronizeEndOfRoutine(SystemArgument1, SystemArgument2); + + return TRUE; +} diff --git a/hyperdbg/hyperperf/code/common/UnloadDll.c b/hyperdbg/hyperperf/code/common/UnloadDll.c new file mode 100644 index 00000000..17321dbf --- /dev/null +++ b/hyperdbg/hyperperf/code/common/UnloadDll.c @@ -0,0 +1,45 @@ +/** + * @file UnloadDll.c + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Unloading DLL in the target Windows + * @details + * @version 0.4 + * @date 2023-07-06 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#include "pch.h" + +// +// We'll add these functions, so whenever HyperDbg's driver is unloaded +// DllUnload will be called to unload this dll from the memory. +// this way we can remove the HyperDbg after unloading as there is no +// other module remains loaded in the memory. +// + +/** + * @brief Routine called on DLL initialization + * + * @param RegistryPath The registry path of the driver + * @return NTSTATUS + */ +NTSTATUS +DllInitialize( + _In_ PUNICODE_STRING RegistryPath) +{ + UNREFERENCED_PARAMETER(RegistryPath); + + return STATUS_SUCCESS; +} + +/** + * @brief Routine called on DLL unload + * + * @return NTSTATUS + */ +NTSTATUS +DllUnload(VOID) +{ + return STATUS_SUCCESS; +} diff --git a/hyperdbg/hyperperf/header/api/PerfApi.h b/hyperdbg/hyperperf/header/api/PerfApi.h new file mode 100644 index 00000000..660f4d20 --- /dev/null +++ b/hyperdbg/hyperperf/header/api/PerfApi.h @@ -0,0 +1,19 @@ +/** + * @file PerfApi.h + * @author + * @brief Header for general PMU routines for HyperPerf module + * @details + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + */ +#pragma once + +////////////////////////////////////////////////// +// Functions // +////////////////////////////////////////////////// + +// +// Most of the functions are defined and exported +// diff --git a/hyperdbg/hyperperf/header/broadcast/Broadcast.h b/hyperdbg/hyperperf/header/broadcast/Broadcast.h new file mode 100644 index 00000000..8698872f --- /dev/null +++ b/hyperdbg/hyperperf/header/broadcast/Broadcast.h @@ -0,0 +1,20 @@ + +/** + * @file Broadcast.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Headers for broadcasting functions + * @details + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +////////////////////////////////////////////////// +// Functions // +////////////////////////////////////////////////// + +VOID +BroadcastTestPmuOnAllCores(); diff --git a/hyperdbg/hyperperf/header/broadcast/DpcRoutines.h b/hyperdbg/hyperperf/header/broadcast/DpcRoutines.h new file mode 100644 index 00000000..397ef8b1 --- /dev/null +++ b/hyperdbg/hyperperf/header/broadcast/DpcRoutines.h @@ -0,0 +1,20 @@ + +/** + * @file DpcRoutines.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Definition for DPC functions + * @details + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +////////////////////////////////////////////////// +// Functions // +////////////////////////////////////////////////// + +BOOLEAN +DpcRoutineTestPmu(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2); diff --git a/hyperdbg/hyperperf/header/common/UnloadDll.h b/hyperdbg/hyperperf/header/common/UnloadDll.h new file mode 100644 index 00000000..9bb252d2 --- /dev/null +++ b/hyperdbg/hyperperf/header/common/UnloadDll.h @@ -0,0 +1,22 @@ +/** + * @file UnloadDll.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Headers for unloading DLL in the target Windows + * + * @version 0.4 + * @date 2023-07-06 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +////////////////////////////////////////////////// +// Exported Functions // +////////////////////////////////////////////////// + +__declspec(dllexport) NTSTATUS +DllInitialize(_In_ PUNICODE_STRING RegistryPath); + +__declspec(dllexport) NTSTATUS + DllUnload(VOID); diff --git a/hyperdbg/hyperperf/header/globals/GlobalVariables.h b/hyperdbg/hyperperf/header/globals/GlobalVariables.h new file mode 100644 index 00000000..79c0f02e --- /dev/null +++ b/hyperdbg/hyperperf/header/globals/GlobalVariables.h @@ -0,0 +1,35 @@ + +/** + * @file GlobalVariables.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Definition for global variables + * @details + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +////////////////////////////////////////////////// +// Global Variables // +////////////////////////////////////////////////// + +/** + * @brief List of callbacks + * + */ +HYPERPERF_CALLBACKS g_Callbacks; + +/** + * @brief The flag indicating whether the hyperperf module callbacks is initialized or not + * + */ +BOOLEAN g_HyperPerfCallbacksInitialized; + +/** + * @brief The flag indicating whether the initialization is being done for hypervisor environment or not + * + */ +BOOLEAN g_RunningOnHypervisorEnvironment; diff --git a/hyperdbg/hyperperf/header/pch.h b/hyperdbg/hyperperf/header/pch.h new file mode 100644 index 00000000..35e14b66 --- /dev/null +++ b/hyperdbg/hyperperf/header/pch.h @@ -0,0 +1,111 @@ + +/** + * @file pch.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Headers of Message logging and tracing + * @details + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +#define _NO_CRT_STDIO_INLINE + +#pragma warning(disable : 4201) // Suppress nameless struct/union warning + +// +// Environment headers +// +#include "platform/general/header/Environment.h" + +#ifdef HYPERDBG_ENV_WINDOWS + +// +// Windows defined functions +// +# include +# include +# include + +#endif // HYPERDBG_ENV_WINDOWS + +// +// Scope definitions +// +#define HYPERDBG_KERNEL_MODE +#define HYPERDBG_HYPERPERF + +// +// Add ia32-doc +// +#include "ia32-doc/out/ia32.h" + +// +// SDK headers +// +#include "SDK/HyperDbgSdk.h" + +// +// Configuration +// +#include "config/Configuration.h" + +// +// Platform independent headers +// +#include "platform/kernel/header/PlatformMem.h" +#include "platform/kernel/header/PlatformIntrinsics.h" +#include "platform/kernel/header/PlatformBroadcast.h" +#include "platform/kernel/header/PlatformCpu.h" +#include "platform/kernel/header/PlatformSpinlock.h" +#include "platform/kernel/header/PlatformIrql.h" +#include "platform/kernel/header/PlatformDpc.h" +#include "platform/kernel/header/PlatformTime.h" +#include "platform/kernel/header/PlatformDbg.h" +#include "platform/kernel/header/PlatformIo.h" +#include "platform/kernel/header/PlatformEvent.h" + +// +// Unload function (to be called when the driver is unloaded) +// +#include "common/UnloadDll.h" + +// +// Hyperlog headers +// +#include "components/callback/header/HyperLogCallback.h" +#include "SDK/imports/kernel/HyperDbgHyperLogIntrinsics.h" + +// +// Spinlock headers +// +#include "components/spinlock/header/Spinlock.h" + +// +// HyperPerf Callbacks +// +#include "SDK/modules/HyperPerf.h" + +// +// Definition of general tracing types +// +#include "api/PerfApi.h" + +// +// DPC and broadcasting function headers +// +#include "broadcast/DpcRoutines.h" +#include "broadcast/Broadcast.h" + +// +// Export functions +// +#include "SDK/imports/kernel/HyperDbgHyperPerf.h" + +// +// Global variables +// +#include "globals/GlobalVariables.h" diff --git a/hyperdbg/hyperperf/header/pt/Pt.h b/hyperdbg/hyperperf/header/pt/Pt.h new file mode 100644 index 00000000..07a10506 --- /dev/null +++ b/hyperdbg/hyperperf/header/pt/Pt.h @@ -0,0 +1,167 @@ +/** + * @file Pt.h + * @author Masoud Rahimi Jafari (Masoodrahimy1379@gmail.com) + * @brief Header for Processor Trace (PT) tracing routines for HyperTrace module + * @details Engine that programs Intel PT MSRs from VMX root or kernel context. + * Buffer / ToPA management is kept here; user-visible PT structures + * live in the SDK header [PtDefinitions.h]. + * @version 0.19 + * @date 2026-04-29 + * + * @copyright This project is released under the GNU Public License v3. + */ +#pragma once + +////////////////////////////////////////////////// +// Constants // +////////////////////////////////////////////////// + +// +// Pool tag for PT contiguous allocations (ASCII "PtHd") +// +#define POOL_TAG_PT 'dHtP' + +////////////////////////////////////////////////// +// Structures // +////////////////////////////////////////////////// + +/** + * @brief Narrow input descriptor for PtFilter. + * + * These are the only fields a caller is allowed to set per-CPU + * when reconfiguring an active PT trace. Engine-internal options + * (BranchEn, TscEn, MtcEn, CycEn, RetCompression, *Freq, etc.) + * stay under the engine's control and are NOT exposed here. + * + * BufferSize == 0 means "keep whatever the per-CPU slot already + * has" — pure filter changes don't touch the ToPA / output / + * overflow buffers and can run from a DPC. + */ +typedef struct _PT_FILTER_OPTIONS +{ + BOOLEAN TraceUser; + BOOLEAN TraceKernel; + UINT64 TargetCr3; + UINT64 BufferSize; + UINT32 NumAddrRanges; + PT_ADDR_RANGE AddrRanges[PT_MAX_ADDR_RANGES]; + +} PT_FILTER_OPTIONS, *PPT_FILTER_OPTIONS; + +/** + * @brief Per-CPU bookkeeping for the user-mode mmap surface. + * + * One MDL + user VA per CPU describes the main output buffer + * immediately followed by the 4 KB overflow page as a single + * virtually contiguous region in the mapping process. Lives in + * g_PtUserMappings; lifetime tied to the PT enable cycle. + */ +typedef struct _PT_USER_MAPPING +{ + PMDL Mdl; + PVOID UserVa; + +} PT_USER_MAPPING, *PPT_USER_MAPPING; + +////////////////////////////////////////////////// +// Functions // +////////////////////////////////////////////////// + +// +// HyperDbg-style wrappers (mirroring Lbr*) +// + +BOOLEAN +PtCheck(); + +BOOLEAN +PtStart(); + +VOID +PtStop(); + +VOID +PtPause(); + +VOID +PtResume(); + +UINT64 +PtSize(); + +VOID +PtDump(); + +VOID +PtFlush(); + +// +// LBR-style filter wrapper, one CPU at a time. Mirrors LbrFilter in shape: +// caller passes a PT_FILTER_OPTIONS describing only the user-tunable bits +// (TraceUser, TraceKernel, TargetCr3, BufferSize, NumAddrRanges, AddrRanges), +// and PtFilter handles the stop / config-update / start sequence on the +// CURRENT CPU. Engine-internal config (BranchEn, TscEn, etc.) is left +// untouched in the per-CPU PT_TRACE_CONFIG. +// +VOID +PtFilter(const PT_FILTER_OPTIONS * FilterOptions); + +// +// PASSIVE_LEVEL helpers — call before / after the per-core DPC broadcasts. +// Required because MmAllocateContiguousMemorySpecifyCache and +// MmFreeContiguousMemory must run at IRQL == PASSIVE_LEVEL. +// + +BOOLEAN +PtAllocateAllCpuBuffers(); + +VOID +PtFreeAllCpuBuffers(); + +// +// User-mode mmap surface: map every per-CPU main output + overflow +// buffer into the calling user process. Idempotent within an enable +// cycle; torn down by PtFreeAllCpuBuffers (i.e. PT disable / flush). +// +INT32 +PtMmapAllCpuBuffersToUser(PT_USER_BUFFER_DESC * OutDescs, UINT32 MaxDescs, UINT32 * OutNumCpus); + +VOID +PtUnmapAllCpuBuffersFromUser(); + +// +// Engine routines (operate on a specific PT_PER_CPU instance) +// + +INT32 +PtEngineQueryCapabilities(PT_CAPABILITIES * OutCaps); + +VOID +PtEngineInitDefaultConfig(PT_TRACE_CONFIG * Config); + +INT32 +PtEngineAllocateBuffers(PT_PER_CPU * Cpu, const PT_TRACE_CONFIG * Config); + +VOID +PtEngineFreeBuffers(PT_PER_CPU * Cpu); + +INT32 +PtEngineStart(PT_PER_CPU * Cpu); + +UINT64 +PtEngineStop(PT_PER_CPU * Cpu, PT_OUTPUT_BUFFER * Out); + +INT32 +PtEnginePause(PT_PER_CPU * Cpu); + +INT32 +PtEngineResume(PT_PER_CPU * Cpu); + +UINT64 +PtEngineHandlePmi(PT_PER_CPU * Cpu, PT_OUTPUT_BUFFER * Out); + +BOOLEAN +PtEngineIsPtPmi(); + +INT32 +PtEngineSizeToTopaEncoding(UINT64 SizeInBytes); diff --git a/hyperdbg/hyperperf/hyperperf.def b/hyperdbg/hyperperf/hyperperf.def new file mode 100644 index 00000000..1415f880 --- /dev/null +++ b/hyperdbg/hyperperf/hyperperf.def @@ -0,0 +1,6 @@ +LIBRARY hyperperf + +EXPORTS + + DllInitialize PRIVATE + DllUnload PRIVATE \ No newline at end of file diff --git a/hyperdbg/hyperperf/hyperperf.vcxproj b/hyperdbg/hyperperf/hyperperf.vcxproj new file mode 100644 index 00000000..20624659 --- /dev/null +++ b/hyperdbg/hyperperf/hyperperf.vcxproj @@ -0,0 +1,145 @@ + + + + + + + + debug + x64 + + + release + x64 + + + + {360E54B1-0B92-4BCA-8111-4BF384292621} + {1bc93793-694f-48fe-9372-81e2b05556fd} + v4.5 + 12.0 + Debug + x64 + hyperperf + $(LatestTargetPlatformVersion) + + + + Windows10 + true + WindowsKernelModeDriver10.0 + DynamicLibrary + KMDF + Desktop + false + + + Windows10 + false + WindowsKernelModeDriver10.0 + DynamicLibrary + KMDF + Desktop + false + + + + + + + + + + + DbgengKernelDebugger + $(SolutionDir)build\bin\$(Configuration)\ + $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + false + + + DbgengKernelDebugger + $(SolutionDir)build\bin\$(Configuration)\ + $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + false + + + + sha256 + + + $(SolutionDir)\include;$(ProjectDir)header;$(SolutionDir)dependencies;%(AdditionalIncludeDirectories) + true + Create + pch.h + stdcpp20 + + + true + + true + hyperperf.def + + + + + sha256 + + + $(SolutionDir)\include;$(ProjectDir)header;$(SolutionDir)dependencies;%(AdditionalIncludeDirectories) + true + Create + pch.h + stdcpp20 + Full + + + true + + true + hyperperf.def + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperperf/hyperperf.vcxproj.filters b/hyperdbg/hyperperf/hyperperf.vcxproj.filters new file mode 100644 index 00000000..d4254192 --- /dev/null +++ b/hyperdbg/hyperperf/hyperperf.vcxproj.filters @@ -0,0 +1,119 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {1ab177b4-9e6c-460e-834a-8fced04b42a3} + + + {21f0281e-fc2a-4e13-97ac-e4b35a05a31e} + + + {09d5457a-bade-4a3f-a171-641110492d57} + + + {b8b32a09-61fb-433a-ad79-eb7cfc5f3437} + + + {32ea8333-847b-443d-8992-4140d54dc1d3} + + + {df6eb164-34b2-4f2a-9530-b88443662fb7} + + + {cb4e742a-6e43-4798-af4a-72bcb11089c9} + + + {ab21116d-5f5b-4ef8-82bb-6585ac3dec95} + + + {57d56081-eede-41a3-b9f0-e7019d32c986} + + + {4f62540b-d186-479e-83a0-1b550134f487} + + + {9fe877a7-e261-4579-9752-a3156b6e69d9} + + + {8bc2336a-b1c5-4e93-9793-23a5cfdc741d} + + + {fc73555b-2be3-4898-bcdb-df83fa3e1388} + + + + + code\platform + + + code\broadcast + + + code\broadcast + + + code\common + + + code\api + + + code\platform + + + code\broadcast + + + code\platform + + + code\components\callback + + + + + header\platform + + + header\broadcast + + + header\broadcast + + + header\common + + + header\globals + + + header\api + + + header\platform + + + header\platform + + + header\platform + + + header\components\callback + + + header + + + + + + \ No newline at end of file diff --git a/hyperdbg/hyperperf/packages.config b/hyperdbg/hyperperf/packages.config new file mode 100644 index 00000000..eb276766 --- /dev/null +++ b/hyperdbg/hyperperf/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/hyperdbg/hypertrace/code/api/PtApi.c b/hyperdbg/hypertrace/code/api/PtApi.c index 054b5d64..408848c3 100644 --- a/hyperdbg/hypertrace/code/api/PtApi.c +++ b/hyperdbg/hypertrace/code/api/PtApi.c @@ -1,6 +1,6 @@ /** * @file PtApi.c - * @author Sina Karvandi (sina@hyperdbg.org) + * @author Masoud Rahimi Jafari (Masoodrahimy1379@gmail.com) * @brief Tracing routines for HyperTrace module (Intel Processor Trace) * @details * @version 0.19 @@ -36,26 +36,26 @@ HyperTracePtBuildConfig(const HYPERTRACE_PT_OPERATION_PACKETS * Req, // If the request specifies neither user nor kernel, default to both // (matches LBR behaviour when filter is empty). // - if (Req->TraceUser || Req->TraceKernel) + if (Req->FilterOptions.TraceUser || Req->FilterOptions.TraceKernel) { - OutCfg->TraceUser = (Req->TraceUser != 0) ? TRUE : FALSE; - OutCfg->TraceKernel = (Req->TraceKernel != 0) ? TRUE : FALSE; + OutCfg->TraceUser = (Req->FilterOptions.TraceUser != 0) ? TRUE : FALSE; + OutCfg->TraceKernel = (Req->FilterOptions.TraceKernel != 0) ? TRUE : FALSE; } - OutCfg->TargetCr3 = Req->TargetCr3; + OutCfg->TargetCr3 = Req->EnableOptions.Cr3; if (Req->BufferSize != 0) { OutCfg->BufferSize = Req->BufferSize; } - OutCfg->NumAddrRanges = Req->NumAddrRanges; + OutCfg->NumAddrRanges = Req->FilterOptions.NumAddrRanges; if (OutCfg->NumAddrRanges > PT_MAX_ADDR_RANGES) OutCfg->NumAddrRanges = PT_MAX_ADDR_RANGES; for (Copy = 0; Copy < OutCfg->NumAddrRanges; Copy++) { - OutCfg->AddrRanges[Copy] = Req->AddrRanges[Copy]; + OutCfg->AddrRanges[Copy] = Req->FilterOptions.AddrRanges[Copy]; } } @@ -284,6 +284,7 @@ HyperTracePtSize(HYPERTRACE_PT_OPERATION_PACKETS * HyperTraceOperationRequest) return FALSE; ProcessorsCount = KeQueryActiveProcessorCount(0); + if (ProcessorsCount > PT_MAX_CPUS_FOR_MMAP) ProcessorsCount = PT_MAX_CPUS_FOR_MMAP; @@ -405,37 +406,37 @@ HyperTracePtFlush(HYPERTRACE_PT_OPERATION_PACKETS * HyperTraceOperationRequest) BOOLEAN HyperTracePtFilter(HYPERTRACE_PT_OPERATION_PACKETS * Req) { - PT_FILTER_OPTIONS FilterOptions = {0}; - BOOLEAN WasEnabled = g_ProcessorTraceEnabled; - BOOLEAN BufferChanged = FALSE; - UINT64 ExistingSize = 0; - UINT32 Copy; + PT_APPLY_CORE_FILTER_REQUEST ApplyCoreFilterReq = {0}; + BOOLEAN WasEnabled = g_ProcessorTraceEnabled; + BOOLEAN BufferChanged = FALSE; + UINT64 ExistingSize = 0; // - // Translate the user-mode packet into PT_FILTER_OPTIONS — the narrow + // Copy the user-mode request into a kernel-mode PT_APPLY_CORE_FILTER_REQUEST + // + memcpy(&ApplyCoreFilterReq.FilterOptions, &Req->FilterOptions, sizeof(PT_FILTER_OPTIONS)); + memcpy(&ApplyCoreFilterReq.EnableOptions, &Req->EnableOptions, sizeof(PT_ENABLE_OPTIONS)); + ApplyCoreFilterReq.BufferSize = Req->BufferSize; + + // + // Translate the user-mode packet into PT_FILTER_OPTIONS - the narrow // surface PtFilter operates on. Default to user+kernel when the // caller specified neither (matches LBR's empty-filter behaviour). // - if (Req->TraceUser || Req->TraceKernel) + if (Req->FilterOptions.TraceUser || Req->FilterOptions.TraceKernel) { - FilterOptions.TraceUser = (Req->TraceUser != 0) ? TRUE : FALSE; - FilterOptions.TraceKernel = (Req->TraceKernel != 0) ? TRUE : FALSE; + ApplyCoreFilterReq.FilterOptions.TraceUser = (Req->FilterOptions.TraceUser != 0) ? TRUE : FALSE; + ApplyCoreFilterReq.FilterOptions.TraceKernel = (Req->FilterOptions.TraceKernel != 0) ? TRUE : FALSE; } else { - FilterOptions.TraceUser = TRUE; - FilterOptions.TraceKernel = TRUE; - } - FilterOptions.TargetCr3 = Req->TargetCr3; - FilterOptions.BufferSize = Req->BufferSize; - FilterOptions.NumAddrRanges = Req->NumAddrRanges; - if (FilterOptions.NumAddrRanges > PT_MAX_ADDR_RANGES) - FilterOptions.NumAddrRanges = PT_MAX_ADDR_RANGES; - for (Copy = 0; Copy < FilterOptions.NumAddrRanges; Copy++) - { - FilterOptions.AddrRanges[Copy] = Req->AddrRanges[Copy]; + ApplyCoreFilterReq.FilterOptions.TraceUser = TRUE; + ApplyCoreFilterReq.FilterOptions.TraceKernel = TRUE; } + if (Req->FilterOptions.NumAddrRanges > PT_MAX_ADDR_RANGES) + ApplyCoreFilterReq.FilterOptions.NumAddrRanges = PT_MAX_ADDR_RANGES; + // // Decide between fast (filter-only) and slow (buffer-resize) paths. // @@ -443,7 +444,8 @@ HyperTracePtFilter(HYPERTRACE_PT_OPERATION_PACKETS * Req) { ExistingSize = g_PtStateList[0].Config.BufferSize; } - if (FilterOptions.BufferSize != 0 && FilterOptions.BufferSize != ExistingSize) + + if (ApplyCoreFilterReq.BufferSize != 0 && ApplyCoreFilterReq.BufferSize != ExistingSize) { BufferChanged = TRUE; } @@ -495,8 +497,8 @@ HyperTracePtFilter(HYPERTRACE_PT_OPERATION_PACKETS * Req) // unchanged. Force FilterOptions.BufferSize=0 so PtFilter on each core // keeps the buffer that's already allocated, then broadcast. // - FilterOptions.BufferSize = 0; - BroadcastFilterPtOnAllCores(&FilterOptions); + ApplyCoreFilterReq.BufferSize = 0; + BroadcastFilterPtOnAllCores(&ApplyCoreFilterReq); } if (Req != NULL) diff --git a/hyperdbg/hypertrace/code/api/TraceApi.c b/hyperdbg/hypertrace/code/api/TraceApi.c index 709f5c6f..94f3e961 100644 --- a/hyperdbg/hypertrace/code/api/TraceApi.c +++ b/hyperdbg/hypertrace/code/api/TraceApi.c @@ -11,7 +11,7 @@ #include "pch.h" /** - * @brief Initialize the hyper trace module callbacks + * @brief Initialize the hypertrace module callbacks * @details This only for callback initialization, not for LBR, PT, etc. initialization * * @param HyperTraceCallbacks Pointer to the HyperTrace callbacks structure to be registered diff --git a/hyperdbg/hypertrace/code/broadcast/Broadcast.c b/hyperdbg/hypertrace/code/broadcast/Broadcast.c index 7dc5fce7..0fd14565 100644 --- a/hyperdbg/hypertrace/code/broadcast/Broadcast.c +++ b/hyperdbg/hypertrace/code/broadcast/Broadcast.c @@ -153,10 +153,12 @@ BroadcastFlushPtOnAllCores() * pointer is passed to every per-core DPC; KeGenericCallDpc is * synchronous so the caller's storage is valid throughout. * + * @param FilterRequest Pointer to a PT_APPLY_CORE_FILTER_REQUEST structure containing the filter options to apply on all cores + * * @return VOID */ VOID -BroadcastFilterPtOnAllCores(PT_FILTER_OPTIONS * FilterOptions) +BroadcastFilterPtOnAllCores(PT_APPLY_CORE_FILTER_REQUEST * FilterRequest) { - KeGenericCallDpc(DpcRoutineFilterPt, (PVOID)FilterOptions); + KeGenericCallDpc(DpcRoutineFilterPt, (PVOID)FilterRequest); } diff --git a/hyperdbg/hypertrace/code/broadcast/DpcRoutines.c b/hyperdbg/hypertrace/code/broadcast/DpcRoutines.c index b9cc478e..b17accdd 100644 --- a/hyperdbg/hypertrace/code/broadcast/DpcRoutines.c +++ b/hyperdbg/hypertrace/code/broadcast/DpcRoutines.c @@ -198,8 +198,11 @@ DpcRoutineEnablePt(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVO // PtStart(); - KeSignalCallDpcSynchronize(SystemArgument2); - KeSignalCallDpcDone(SystemArgument1); + // ------------------------------------------------------------------------------ + // Synchronize the end of this routine with the caller + // + PlatformBroadcastSynchronizeEndOfRoutine(SystemArgument1, SystemArgument2); + return TRUE; } @@ -214,8 +217,11 @@ DpcRoutineDisablePt(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PV PtStop(); - KeSignalCallDpcSynchronize(SystemArgument2); - KeSignalCallDpcDone(SystemArgument1); + // ------------------------------------------------------------------------------ + // Synchronize the end of this routine with the caller + // + PlatformBroadcastSynchronizeEndOfRoutine(SystemArgument1, SystemArgument2); + return TRUE; } @@ -230,8 +236,11 @@ DpcRoutinePausePt(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOI PtPause(); - KeSignalCallDpcSynchronize(SystemArgument2); - KeSignalCallDpcDone(SystemArgument1); + // ------------------------------------------------------------------------------ + // Synchronize the end of this routine with the caller + // + PlatformBroadcastSynchronizeEndOfRoutine(SystemArgument1, SystemArgument2); + return TRUE; } @@ -246,8 +255,11 @@ DpcRoutineResumePt(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVO PtResume(); - KeSignalCallDpcSynchronize(SystemArgument2); - KeSignalCallDpcDone(SystemArgument1); + // ------------------------------------------------------------------------------ + // Synchronize the end of this routine with the caller + // + PlatformBroadcastSynchronizeEndOfRoutine(SystemArgument1, SystemArgument2); + return TRUE; } @@ -269,8 +281,11 @@ DpcRoutineSizePt(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID if (Sizes != NULL && Core < PT_MAX_CPUS_FOR_MMAP) Sizes[Core] = PtSize(); - KeSignalCallDpcSynchronize(SystemArgument2); - KeSignalCallDpcDone(SystemArgument1); + // ------------------------------------------------------------------------------ + // Synchronize the end of this routine with the caller + // + PlatformBroadcastSynchronizeEndOfRoutine(SystemArgument1, SystemArgument2); + return TRUE; } @@ -285,8 +300,11 @@ DpcRoutineDumpPt(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID PtDump(); - KeSignalCallDpcSynchronize(SystemArgument2); - KeSignalCallDpcDone(SystemArgument1); + // ------------------------------------------------------------------------------ + // Synchronize the end of this routine with the caller + // + PlatformBroadcastSynchronizeEndOfRoutine(SystemArgument1, SystemArgument2); + return TRUE; } @@ -301,15 +319,18 @@ DpcRoutineFlushPt(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOI PtFlush(); - KeSignalCallDpcSynchronize(SystemArgument2); - KeSignalCallDpcDone(SystemArgument1); + // ------------------------------------------------------------------------------ + // Synchronize the end of this routine with the caller + // + PlatformBroadcastSynchronizeEndOfRoutine(SystemArgument1, SystemArgument2); + return TRUE; } /** * @brief Broadcast applying a PT filter to all cores. * - * DeferredContext carries the PT_FILTER_OPTIONS * supplied by the + * DeferredContext carries the PT_APPLY_CORE_FILTER_REQUEST * supplied by the * broadcaster; PtFilter writes the user-tunable fields into the * current CPU's per-CPU PT_TRACE_CONFIG and reprograms PT MSRs. */ @@ -318,9 +339,12 @@ DpcRoutineFilterPt(KDPC * Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVO { UNREFERENCED_PARAMETER(Dpc); - PtFilter((const PT_FILTER_OPTIONS *)DeferredContext); + PtFilter((const PT_APPLY_CORE_FILTER_REQUEST *)DeferredContext); + + // ------------------------------------------------------------------------------ + // Synchronize the end of this routine with the caller + // + PlatformBroadcastSynchronizeEndOfRoutine(SystemArgument1, SystemArgument2); - KeSignalCallDpcSynchronize(SystemArgument2); - KeSignalCallDpcDone(SystemArgument1); return TRUE; } diff --git a/hyperdbg/hypertrace/code/pt/Pt.c b/hyperdbg/hypertrace/code/pt/Pt.c index d575dbab..2dfe7ce5 100644 --- a/hyperdbg/hypertrace/code/pt/Pt.c +++ b/hyperdbg/hypertrace/code/pt/Pt.c @@ -1,6 +1,6 @@ /** * @file Pt.c - * @author Sina Karvandi (sina@hyperdbg.org) + * @author Masoud Rahimi Jafari (Masoodrahimy1379@gmail.com) * @brief Processor Trace (PT) tracing implementation for HyperTrace module * @details Programs Intel PT MSRs and manages per-CPU ToPA / output buffers. * The engine half (PtEngine*) deals with a single PT_PER_CPU at a @@ -348,7 +348,7 @@ PtEngineInitDefaultConfig(PT_TRACE_CONFIG * Config) Config->TargetCr3 = 0; Config->NumAddrRanges = 0; Config->EnableBranch = TRUE; - Config->EnableTsc = TRUE; + Config->EnableTsc = FALSE; Config->EnableMtc = FALSE; Config->EnableCyc = FALSE; Config->EnableRetCompression = FALSE; @@ -639,9 +639,9 @@ PtEngineStart(PT_PER_CPU * Cpu) if (g_RunningOnHypervisorEnvironment) { IsOnVmxRootMode = g_Callbacks.VmFuncVmxGetCurrentExecutionMode(); - LogInfo("PT: PtEngineStart on core %u (vmx-root=%u)\n", - KeGetCurrentProcessorNumberEx(NULL), - (UINT32)IsOnVmxRootMode); + // LogInfo("PT: PtEngineStart on core %u (vmx-root=%u)\n", + // KeGetCurrentProcessorNumberEx(NULL), + // (UINT32)IsOnVmxRootMode); } // @@ -826,9 +826,9 @@ PtEngineStop(PT_PER_CPU * Cpu, PT_OUTPUT_BUFFER * Out) if (g_RunningOnHypervisorEnvironment) { IsOnVmxRootMode = g_Callbacks.VmFuncVmxGetCurrentExecutionMode(); - LogInfo("PT: PtEngineStop on core %u (vmx-root=%u)\n", - KeGetCurrentProcessorNumberEx(NULL), - (UINT32)IsOnVmxRootMode); + // LogInfo("PT: PtEngineStop on core %u (vmx-root=%u)\n", + // KeGetCurrentProcessorNumberEx(NULL), + // (UINT32)IsOnVmxRootMode); } // @@ -1027,9 +1027,9 @@ PtCheck() if (g_RunningOnHypervisorEnvironment && !Caps.VmxSupport) { - LogInfo("PT: IA32_VMX_MISC[14] is clear — Intel PT in VMX is not " - "advertised on this CPU. Direct MSR programming still works " - "from VMX non-root because PT MSRs are not trapped.\n"); + // LogInfo("PT: IA32_VMX_MISC[14] is clear - Intel PT in VMX is not " + // "advertised on this CPU. Direct MSR programming still works " + // "from VMX non-root because PT MSRs are not trapped.\n"); } return TRUE; @@ -1071,7 +1071,7 @@ PtAllocateAllCpuBuffers() if (PtEngineAllocateBuffers(Cpu, &Cfg) != 0) { - LogInfo("PT: buffer allocation failed on core %u\n", i); + // LogInfo("PT: buffer allocation failed on core %u\n", i); return FALSE; } } @@ -1233,13 +1233,13 @@ PtStart() // Allocating from a DPC is unsafe (MmAllocateContiguousMemory* is // a paged routine) so just bail out. // - LogInfo("PT: buffers not allocated for core %u\n", CurrentCore); + // LogInfo("PT: buffers not allocated for core %u\n", CurrentCore); return FALSE; } if (PtEngineStart(Cpu) != 0) { - LogInfo("PT: PtEngineStart failed on core %u (state=%d)\n", CurrentCore, Cpu->State); + // LogInfo("PT: PtEngineStart failed on core %u (state=%d)\n", CurrentCore, Cpu->State); return FALSE; } @@ -1263,7 +1263,7 @@ PtStop() CurrentCore = KeGetCurrentProcessorNumberEx(NULL); Cpu = &g_PtStateList[CurrentCore]; - LogInfo("PT: stopping trace on core %d\n", CurrentCore); + // LogInfo("PT: stopping trace on core %d\n", CurrentCore); PtEngineStop(Cpu, NULL); } @@ -1284,7 +1284,7 @@ PtPause() CurrentCore = KeGetCurrentProcessorNumberEx(NULL); Cpu = &g_PtStateList[CurrentCore]; - LogInfo("PT: pausing trace on core %u\n", CurrentCore); + // LogInfo("PT: pausing trace on core %u\n", CurrentCore); PtEnginePause(Cpu); } @@ -1304,7 +1304,7 @@ PtResume() CurrentCore = KeGetCurrentProcessorNumberEx(NULL); Cpu = &g_PtStateList[CurrentCore]; - LogInfo("PT: resuming trace on core %u\n", CurrentCore); + // LogInfo("PT: resuming trace on core %u\n", CurrentCore); PtEngineResume(Cpu); } @@ -1399,14 +1399,14 @@ PtDump() * handles that case before broadcasting. */ VOID -PtFilter(const PT_FILTER_OPTIONS * FilterOptions) +PtFilter(const PT_APPLY_CORE_FILTER_REQUEST * FilterRequest) { UINT32 CurrentCore; PT_PER_CPU * Cpu; UINT32 i; PT_CAPABILITIES Caps = {0}; - if (g_PtStateList == NULL || FilterOptions == NULL) + if (g_PtStateList == NULL || FilterRequest == NULL) return; if (PtEngineQueryCapabilities(&Caps) != 0) @@ -1415,7 +1415,7 @@ PtFilter(const PT_FILTER_OPTIONS * FilterOptions) CurrentCore = KeGetCurrentProcessorNumberEx(NULL); Cpu = &g_PtStateList[CurrentCore]; - LogInfo("PT: applying filter on core %u\n", CurrentCore); + // LogInfo("PT: applying filter on core %u\n", CurrentCore); // // Stop tracing on this CPU first so we can safely mutate Cpu->Config @@ -1429,42 +1429,42 @@ PtFilter(const PT_FILTER_OPTIONS * FilterOptions) // // Apply only the user-tunable fields to this CPU's per-CPU config. // - Cpu->Config.TraceUser = FilterOptions->TraceUser; - Cpu->Config.TraceKernel = FilterOptions->TraceKernel; + Cpu->Config.TraceUser = (BOOLEAN)FilterRequest->FilterOptions.TraceUser; + Cpu->Config.TraceKernel = (BOOLEAN)FilterRequest->FilterOptions.TraceKernel; - if (FilterOptions->TargetCr3 != 0 && !Caps.Cr3Filtering) + if (FilterRequest->EnableOptions.Cr3 != 0 && !Caps.Cr3Filtering) { - LogInfo("PT: CR3 filtering requested but not supported by CPU\n"); + // LogInfo("PT: CR3 filtering requested but not supported by CPU\n"); Cpu->Config.TargetCr3 = 0; } else { - Cpu->Config.TargetCr3 = FilterOptions->TargetCr3; + Cpu->Config.TargetCr3 = FilterRequest->EnableOptions.Cr3; } - if (FilterOptions->NumAddrRanges > Caps.NumAddrRanges) + if (FilterRequest->FilterOptions.NumAddrRanges > Caps.NumAddrRanges) { - LogInfo("PT: requested %u IP filter ranges, but CPU only supports %u\n", FilterOptions->NumAddrRanges, Caps.NumAddrRanges); + // LogInfo("PT: requested %u IP filter ranges, but CPU only supports %u\n", FilterRequest->FilterOptions.NumAddrRanges, Caps.NumAddrRanges); Cpu->Config.NumAddrRanges = Caps.NumAddrRanges; } - else if (FilterOptions->NumAddrRanges > 0 && !Caps.IpFiltering) + else if (FilterRequest->FilterOptions.NumAddrRanges > 0 && !Caps.IpFiltering) { - LogInfo("PT: IP filtering requested but not supported by CPU\n"); + // LogInfo("PT: IP filtering requested but not supported by CPU\n"); Cpu->Config.NumAddrRanges = 0; } else { - Cpu->Config.NumAddrRanges = FilterOptions->NumAddrRanges; + Cpu->Config.NumAddrRanges = FilterRequest->FilterOptions.NumAddrRanges; } - if (FilterOptions->BufferSize != 0) + if (FilterRequest->BufferSize != 0) { - Cpu->Config.BufferSize = FilterOptions->BufferSize; + Cpu->Config.BufferSize = FilterRequest->BufferSize; } for (i = 0; i < PT_MAX_ADDR_RANGES; i++) { - Cpu->Config.AddrRanges[i] = FilterOptions->AddrRanges[i]; + Cpu->Config.AddrRanges[i] = FilterRequest->FilterOptions.AddrRanges[i]; } // @@ -1499,7 +1499,7 @@ PtFlush() CurrentCore = KeGetCurrentProcessorNumberEx(NULL); Cpu = &g_PtStateList[CurrentCore]; - LogInfo("PT: flush on core %u\n", CurrentCore); + // LogInfo("PT: flush on core %u\n", CurrentCore); if (Cpu->State == PT_STATE_TRACING || Cpu->State == PT_STATE_PAUSED) { diff --git a/hyperdbg/hypertrace/header/api/PtApi.h b/hyperdbg/hypertrace/header/api/PtApi.h index 10e4cba1..d75ea2b0 100644 --- a/hyperdbg/hypertrace/header/api/PtApi.h +++ b/hyperdbg/hypertrace/header/api/PtApi.h @@ -1,6 +1,6 @@ /** * @file PtApi.h - * @author Sina Karvandi (sina@hyperdbg.org) + * @author Masoud Rahimi Jafari (Masoodrahimy1379@gmail.com) * @brief Header for PT tracing routines for HyperTrace module (Intel Processor Trace) * @details * @version 0.19 diff --git a/hyperdbg/hypertrace/header/broadcast/Broadcast.h b/hyperdbg/hypertrace/header/broadcast/Broadcast.h index 878cd3cd..eb857072 100644 --- a/hyperdbg/hypertrace/header/broadcast/Broadcast.h +++ b/hyperdbg/hypertrace/header/broadcast/Broadcast.h @@ -50,4 +50,4 @@ VOID BroadcastFlushPtOnAllCores(); VOID -BroadcastFilterPtOnAllCores(PT_FILTER_OPTIONS * FilterOptions); +BroadcastFilterPtOnAllCores(PT_APPLY_CORE_FILTER_REQUEST * FilterRequest); diff --git a/hyperdbg/hypertrace/header/pch.h b/hyperdbg/hypertrace/header/pch.h index 51424dde..4e591600 100644 --- a/hyperdbg/hypertrace/header/pch.h +++ b/hyperdbg/hypertrace/header/pch.h @@ -67,20 +67,6 @@ #include "platform/kernel/header/PlatformIo.h" #include "platform/kernel/header/PlatformEvent.h" -// -// Definition of tracing types and structures (Processor Trace). -// Pt.h must come before broadcast/Broadcast.h because Broadcast.h -// references PT_FILTER_OPTIONS in its function signatures. -// -#include "pt/Pt.h" -#include "api/PtApi.h" - -// -// DPC and broadcasting function headers -// -#include "broadcast/DpcRoutines.h" -#include "broadcast/Broadcast.h" - // // Unload function (to be called when the driver is unloaded) // @@ -113,6 +99,20 @@ #include "lbr/Lbr.h" #include "api/LbrApi.h" +// +// Definition of tracing types and structures (Processor Trace). +// Pt.h must come before broadcast/Broadcast.h because Broadcast.h +// references PT_FILTER_OPTIONS in its function signatures. +// +#include "pt/Pt.h" +#include "api/PtApi.h" + +// +// DPC and broadcasting function headers +// +#include "broadcast/DpcRoutines.h" +#include "broadcast/Broadcast.h" + // // Export functions // diff --git a/hyperdbg/hypertrace/header/pt/Pt.h b/hyperdbg/hypertrace/header/pt/Pt.h index ceb5c98d..f7801bbb 100644 --- a/hyperdbg/hypertrace/header/pt/Pt.h +++ b/hyperdbg/hypertrace/header/pt/Pt.h @@ -1,6 +1,6 @@ /** * @file Pt.h - * @author Sina Karvandi (sina@hyperdbg.org) + * @author Masoud Rahimi Jafari (Masoodrahimy1379@gmail.com) * @brief Header for Processor Trace (PT) tracing routines for HyperTrace module * @details Engine that programs Intel PT MSRs from VMX root or kernel context. * Buffer / ToPA management is kept here; user-visible PT structures @@ -12,42 +12,10 @@ */ #pragma once -////////////////////////////////////////////////// -// Constants // -////////////////////////////////////////////////// - -// -// Pool tag for PT contiguous allocations (ASCII "PtHd") -// -#define POOL_TAG_PT 'dHtP' - ////////////////////////////////////////////////// // Structures // ////////////////////////////////////////////////// -/** - * @brief Narrow input descriptor for PtFilter. - * - * These are the only fields a caller is allowed to set per-CPU - * when reconfiguring an active PT trace. Engine-internal options - * (BranchEn, TscEn, MtcEn, CycEn, RetCompression, *Freq, etc.) - * stay under the engine's control and are NOT exposed here. - * - * BufferSize == 0 means "keep whatever the per-CPU slot already - * has" — pure filter changes don't touch the ToPA / output / - * overflow buffers and can run from a DPC. - */ -typedef struct _PT_FILTER_OPTIONS -{ - BOOLEAN TraceUser; - BOOLEAN TraceKernel; - UINT64 TargetCr3; - UINT64 BufferSize; - UINT32 NumAddrRanges; - PT_ADDR_RANGE AddrRanges[PT_MAX_ADDR_RANGES]; - -} PT_FILTER_OPTIONS, *PPT_FILTER_OPTIONS; - /** * @brief Per-CPU bookkeeping for the user-mode mmap surface. * @@ -63,6 +31,17 @@ typedef struct _PT_USER_MAPPING } PT_USER_MAPPING, *PPT_USER_MAPPING; +/** + * @brief PT apply core filter requests. + */ +typedef struct _PT_APPLY_CORE_FILTER_REQUEST +{ + PT_FILTER_OPTIONS FilterOptions; + PT_ENABLE_OPTIONS EnableOptions; + UINT64 BufferSize; /* Output buffer size (0 = default / PT_DEFAULT_BUFFER_SIZE) */ + +} PT_APPLY_CORE_FILTER_REQUEST, *PPT_APPLY_CORE_FILTER_REQUEST; + ////////////////////////////////////////////////// // Functions // ////////////////////////////////////////////////// @@ -97,14 +76,14 @@ PtFlush(); // // LBR-style filter wrapper, one CPU at a time. Mirrors LbrFilter in shape: -// caller passes a PT_FILTER_OPTIONS describing only the user-tunable bits +// caller passes a PT_APPLY_CORE_FILTER_REQUEST describing only the user-tunable bits // (TraceUser, TraceKernel, TargetCr3, BufferSize, NumAddrRanges, AddrRanges), // and PtFilter handles the stop / config-update / start sequence on the // CURRENT CPU. Engine-internal config (BranchEn, TscEn, etc.) is left // untouched in the per-CPU PT_TRACE_CONFIG. // VOID -PtFilter(const PT_FILTER_OPTIONS * FilterOptions); +PtFilter(const PT_APPLY_CORE_FILTER_REQUEST * FilterRequest); // // PASSIVE_LEVEL helpers — call before / after the per-core DPC broadcasts. diff --git a/hyperdbg/hypertrace/hypertrace.vcxproj b/hyperdbg/hypertrace/hypertrace.vcxproj index 794cfc67..1b0d38f2 100644 --- a/hyperdbg/hypertrace/hypertrace.vcxproj +++ b/hyperdbg/hypertrace/hypertrace.vcxproj @@ -1,5 +1,8 @@  + + + debug @@ -27,7 +30,7 @@ WindowsKernelModeDriver10.0 DynamicLibrary KMDF - Universal + Desktop false @@ -36,7 +39,7 @@ WindowsKernelModeDriver10.0 DynamicLibrary KMDF - Universal + Desktop false @@ -131,7 +134,20 @@ + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/hyperdbg/hypertrace/hypertrace.vcxproj.filters b/hyperdbg/hypertrace/hypertrace.vcxproj.filters index 80bfb9ae..32ace76c 100644 --- a/hyperdbg/hypertrace/hypertrace.vcxproj.filters +++ b/hyperdbg/hypertrace/hypertrace.vcxproj.filters @@ -149,4 +149,7 @@ header\components\callback + + + \ No newline at end of file diff --git a/hyperdbg/hypertrace/packages.config b/hyperdbg/hypertrace/packages.config new file mode 100644 index 00000000..eb276766 --- /dev/null +++ b/hyperdbg/hypertrace/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/hyperdbg/include/SDK/headers/BasicTypes.h b/hyperdbg/include/SDK/headers/BasicTypes.h index 0b0d1963..17e5c84a 100644 --- a/hyperdbg/include/SDK/headers/BasicTypes.h +++ b/hyperdbg/include/SDK/headers/BasicTypes.h @@ -72,7 +72,8 @@ typedef wchar_t WCHAR; typedef void VOID; -typedef size_t SIZE_T; +typedef size_t SIZE_T; +typedef SIZE_T * PSIZE_T; typedef signed long long INT64, *PINT64; typedef unsigned long long UINT64, *PUINT64; @@ -83,6 +84,13 @@ typedef unsigned long long DWORD64, *PDWORD64; typedef unsigned long long ULONGLONG; typedef unsigned long long ULONG_PTR, *PULONG_PTR; +typedef INT64 LONGLONG; + +typedef union _LARGE_INTEGER { + struct { DWORD LowPart; LONG HighPart; }; + LONGLONG QuadPart; +} LARGE_INTEGER, *PLARGE_INTEGER; + // // To be fixed later, linux wchar_t is 4 bytes, but windows wchar_t is 2 bytes // @@ -93,6 +101,10 @@ typedef UINT16 WCHAR; typedef PVOID HANDLE; typedef HANDLE * PHANDLE; +// Windows pointer-style aliases (used by the cross-platform driver/IOCTL wrappers) +typedef PVOID LPVOID; +typedef DWORD * LPDWORD; + #endif #define NULL_ZERO 0 diff --git a/hyperdbg/include/SDK/headers/Constants.h b/hyperdbg/include/SDK/headers/Constants.h index 0c893b51..dae3aa47 100644 --- a/hyperdbg/include/SDK/headers/Constants.h +++ b/hyperdbg/include/SDK/headers/Constants.h @@ -18,10 +18,10 @@ ////////////////////////////////////////////////// #define VERSION_MAJOR 0 -#define VERSION_MINOR 19 +#define VERSION_MINOR 21 #define VERSION_PATCH 0 -#define BETA_VERSION 1 +#define BETA_VERSION FALSE // // Example of __DATE__ string: "Jul 27 2012" @@ -110,7 +110,7 @@ const UCHAR BuildDateTime[] = { // Complete version as a string -# if BETA_VERSION == 0 +# if BETA_VERSION == FALSE # define HYPERDBG_COMPLETE_VERSION "v" TOSTRING(VERSION_MAJOR) "." TOSTRING(VERSION_MINOR) "." TOSTRING(VERSION_PATCH) "\0" # else # define HYPERDBG_COMPLETE_VERSION "v" TOSTRING(VERSION_MAJOR) "." TOSTRING(VERSION_MINOR) "." TOSTRING(VERSION_PATCH) "-beta\0" diff --git a/hyperdbg/include/SDK/headers/Ioctls.h b/hyperdbg/include/SDK/headers/Ioctls.h index 783e0366..20a2057a 100644 --- a/hyperdbg/include/SDK/headers/Ioctls.h +++ b/hyperdbg/include/SDK/headers/Ioctls.h @@ -420,3 +420,11 @@ */ #define IOCTL_PERFORM_HYPERTRACE_PT_OPERATION \ CTL_CODE(FILE_DEVICE_UNKNOWN, IOCTL_HYPERTRACE_IOCTL + 0x04, METHOD_BUFFERED, FILE_ANY_ACCESS) + +/** + * @brief ioctl, to map per-CPU HyperTrace PT output buffers into the + * calling user-mode process. See HYPERTRACE_PT_MMAP_PACKETS. + * + */ +#define IOCTL_PERFORM_HYPERTRACE_PT_MMAP \ + CTL_CODE(FILE_DEVICE_UNKNOWN, IOCTL_HYPERTRACE_IOCTL + 0x05, METHOD_BUFFERED, FILE_ANY_ACCESS) diff --git a/hyperdbg/include/SDK/headers/PtDefinitions.h b/hyperdbg/include/SDK/headers/PtDefinitions.h index da93f642..cb827378 100644 --- a/hyperdbg/include/SDK/headers/PtDefinitions.h +++ b/hyperdbg/include/SDK/headers/PtDefinitions.h @@ -1,6 +1,6 @@ /** * @file PtDefinitions.h - * @author Sina Karvandi (sina@hyperdbg.org) + * @author Masoud Rahimi Jafari (Masoodrahimy1379@gmail.com) * @brief Intel Processor Trace (PT) related data structures and hardware * definitions shared between the kernel and user-mode components. * @details @@ -48,7 +48,6 @@ ////////////////////////////////////////////////// #define PT_PAGE_SIZE 0x1000ULL /* 4 KB */ -#define PT_DEFAULT_BUFFER_SIZE 0x200000ULL /* 2 MB */ #define PT_OVERFLOW_SIZE PT_PAGE_SIZE /* 4 KB overflow landing zone */ #define PT_MAX_ADDR_RANGES 4 diff --git a/hyperdbg/include/SDK/headers/RequestStructures.h b/hyperdbg/include/SDK/headers/RequestStructures.h index bfd1cad5..5e5b66b4 100644 --- a/hyperdbg/include/SDK/headers/RequestStructures.h +++ b/hyperdbg/include/SDK/headers/RequestStructures.h @@ -1356,27 +1356,78 @@ typedef enum _HYPERTRACE_PT_OPERATION_REQUEST_TYPE HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PAUSE, HYPERTRACE_PT_OPERATION_REQUEST_TYPE_RESUME, HYPERTRACE_PT_OPERATION_REQUEST_TYPE_SIZE, - HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DUMP, HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FLUSH, + HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DUMP, HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER, + HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PACKET, } HYPERTRACE_PT_OPERATION_REQUEST_TYPE; +/** + * @brief The maximum buffer size for PT + * + */ +#define PT_DEFAULT_BUFFER_SIZE 0x200000 // 2 MB + +/** + * @brief The default core for PT + * + */ +#define PT_DEFAULT_PINNING_CORE 0x0 // 0th core + +/** + * @brief PT enable options structure + * + */ +typedef struct _PT_ENABLE_OPTIONS +{ + UINT32 EnableByCr3 : 1; // Enable by CR3 + UINT32 EnableByPid : 1; // Enable by PID + UINT32 EnableByTid : 1; // Enable by TID + UINT32 EnableByPname : 1; // Enable by process name + + UINT32 Pid; + UINT32 Tid; + UINT64 Cr3; + CHAR ProcessName[256]; + +} PT_ENABLE_OPTIONS, *PPT_ENABLE_OPTIONS; + +/** + * @brief PT filter options structure + * + */ +typedef struct _PT_FILTER_OPTIONS +{ + UINT32 TraceUser : 1; // Trace user mode + UINT32 TraceKernel : 1; // Trace kernel mode + + UINT8 NumAddrRanges; /* Number of valid AddrRanges entries */ + PT_ADDR_RANGE AddrRanges[PT_MAX_ADDR_RANGES]; // Address ranges to filter by + +} PT_FILTER_OPTIONS, *PPT_FILTER_OPTIONS; + +/** + * @brief PT packet options structure + * + */ +typedef struct _PT_PACKET_OPTIONS +{ + UINT32 PSB : 1; // PSB packet + UINT32 PIP : 1; // PIP packet + UINT32 TSC : 1; // TSC packet + UINT32 MTC : 1; // MTC packet + UINT32 CYC : 1; // CYC packet + UINT32 TNT : 1; // TNT packet + UINT32 TIP : 1; // TIP packet + UINT32 FUP : 1; // FUP packet + UINT32 MODE : 1; // MODE packet + +} PT_PACKET_OPTIONS, *PPT_PACKET_OPTIONS; + /** * @brief The structure of HyperTrace PT result packet in HyperDbg * - * Configuration fields (TraceUser/TraceKernel/TargetCr3/BufferSize/ - * NumAddrRanges/AddrRanges) are populated by the caller for ENABLE - * and FILTER operations. For other operations they are ignored. - * - * BufferSize must be a power of two multiple of 4 KB (4KB ... 128MB). - * Pass 0 to keep the existing per-CPU value (default 2 MB on first - * enable). - * - * For SIZE operations the kernel fills NumCpus and BytesPerCpu[] - * with each CPU's current PT output position, i.e. how many bytes - * of valid trace data are currently sitting in that CPU's main + - * overflow buffer; the rest of the packet is unused on output. */ typedef struct _HYPERTRACE_PT_OPERATION_PACKETS { @@ -1384,21 +1435,26 @@ typedef struct _HYPERTRACE_PT_OPERATION_PACKETS UINT32 KernelStatus; // - // Filter / config (used by FILTER and ENABLE) + // Enable // - UINT32 TraceUser; /* Boolean: trace CPL > 0 */ - UINT32 TraceKernel; /* Boolean: trace CPL == 0 */ - UINT64 TargetCr3; /* CR3 to filter by (0 = no filter) */ - UINT64 BufferSize; /* Output buffer size (0 = keep current) */ - UINT32 NumAddrRanges; /* Number of valid AddrRanges entries */ - UINT32 Reserved; /* Padding to keep the array 8-aligned */ - PT_ADDR_RANGE AddrRanges[PT_MAX_ADDR_RANGES]; + UINT64 BufferSize; /* Output buffer size (0 = default / PT_DEFAULT_BUFFER_SIZE) */ + PT_ENABLE_OPTIONS EnableOptions; /* Options for enabling PT (CPL, CR3, PID, TID, Pname) */ + UINT32 CoreId; /* Core ID for running (pinning) process/thread */ // - // SIZE output: per-CPU bytes-written snapshot + // Filter // - UINT32 NumCpus; /* CPUs populated in BytesPerCpu */ - UINT32 Reserved2; /* Padding to 8-align the array */ + PT_FILTER_OPTIONS FilterOptions; /* Options for filtering PT (CPL, AddrRanges) */ + + // + // Packet + // + PT_PACKET_OPTIONS PacketOptions; /* Options for PT packets */ + + // + // Size + // + UINT32 NumCpus; /* CPUs populated in BytesPerCpu */ UINT64 BytesPerCpu[PT_MAX_CPUS_FOR_MMAP]; } HYPERTRACE_PT_OPERATION_PACKETS, *PHYPERTRACE_PT_OPERATION_PACKETS; diff --git a/hyperdbg/include/SDK/imports/kernel/HyperDbgHyperPerf.h b/hyperdbg/include/SDK/imports/kernel/HyperDbgHyperPerf.h new file mode 100644 index 00000000..f6997795 --- /dev/null +++ b/hyperdbg/include/SDK/imports/kernel/HyperDbgHyperPerf.h @@ -0,0 +1,33 @@ +/** + * @file HyperDbgHyperPerf.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Headers relating exported functions from hyperperf (pmu) module + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +#ifdef HYPERDBG_HYPERPERF +# define IMPORT_EXPORT_HYPERPERF __declspec(dllexport) +#else +# define IMPORT_EXPORT_HYPERPERF __declspec(dllimport) +#endif + +////////////////////////////////////////////////// +// HyperPerf Functions // +////////////////////////////////////////////////// + +// +// Initialize the hyperperf module with the provided callbacks +// +IMPORT_EXPORT_HYPERPERF BOOLEAN +HyperPerfInitCallback(HYPERPERF_CALLBACKS * HyperPerfCallbacks, BOOLEAN RunningOnHypervisorEnvironment); + +// +// Uninitialize the HyperPerf module +// +IMPORT_EXPORT_HYPERPERF VOID +HyperPerfUninit(); diff --git a/hyperdbg/include/SDK/imports/user/HyperDbgLibImports.h b/hyperdbg/include/SDK/imports/user/HyperDbgLibImports.h index 892e47be..2491fca1 100644 --- a/hyperdbg/include/SDK/imports/user/HyperDbgLibImports.h +++ b/hyperdbg/include/SDK/imports/user/HyperDbgLibImports.h @@ -49,6 +49,9 @@ hyperdbg_u_get_processor_vendor(); // // All Modules // +IMPORT_EXPORT_LIBHYPERDBG BOOLEAN +hyperdbg_u_is_any_module_loaded(); + IMPORT_EXPORT_LIBHYPERDBG INT hyperdbg_u_load_all_modules(); @@ -306,6 +309,16 @@ hyperdbg_u_perform_smi_operation(SMI_OPERATION_PACKETS * SmiOperation); IMPORT_EXPORT_LIBHYPERDBG BOOLEAN hyperdbg_u_lbr_dump(HYPERTRACE_LBR_DUMP_PACKETS * LbrdumpRequest); +// +// Intel PT related commands +// Exported functionality of the '!pt' command + the PT mmap surface +// +IMPORT_EXPORT_LIBHYPERDBG BOOLEAN +hyperdbg_u_pt_operation(HYPERTRACE_PT_OPERATION_PACKETS * PtRequest); + +IMPORT_EXPORT_LIBHYPERDBG BOOLEAN +hyperdbg_u_pt_mmap(HYPERTRACE_PT_MMAP_PACKETS * MmapRequest); + // // Transparent mode related command // Exported functionality of the '!hide', and '!unhide' commands diff --git a/hyperdbg/include/SDK/modules/HyperPerf.h b/hyperdbg/include/SDK/modules/HyperPerf.h new file mode 100644 index 00000000..fe76e097 --- /dev/null +++ b/hyperdbg/include/SDK/modules/HyperPerf.h @@ -0,0 +1,80 @@ +/** + * @file HyperPerf.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief HyperDbg's SDK for hyperperf project + * @details This file contains definitions of HyperPerf routines + * @version 0.21 + * @date 2026-06-22 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +////////////////////////////////////////////////// +// Callback Types // +////////////////////////////////////////////////// + +/** + * @brief A function from the message tracer that send the inputs to the + * queue of the messages + * + */ +typedef BOOLEAN (*LOG_CALLBACK_PREPARE_AND_SEND_MESSAGE_TO_QUEUE)(UINT32 OperationCode, + BOOLEAN IsImmediateMessage, + BOOLEAN ShowCurrentSystemTime, + BOOLEAN Priority, + const CHAR * Fmt, + va_list ArgList); + +/** + * @brief A function that sends the messages to message tracer buffers + * + */ +typedef BOOLEAN (*LOG_CALLBACK_SEND_MESSAGE_TO_QUEUE)(UINT32 OperationCode, BOOLEAN IsImmediateMessage, CHAR * LogMessage, UINT32 BufferLen, BOOLEAN Priority); + +/** + * @brief A function that sends the messages to message tracer buffers + * + */ +typedef BOOLEAN (*LOG_CALLBACK_SEND_BUFFER)(_In_ UINT32 OperationCode, + _In_reads_bytes_(BufferLength) PVOID Buffer, + _In_ UINT32 BufferLength, + _In_ BOOLEAN Priority); + +/** + * @brief A function that checks whether the priority or regular buffer is full or not + * + */ +typedef BOOLEAN (*LOG_CALLBACK_CHECK_IF_BUFFER_IS_FULL)(BOOLEAN Priority); + +/** + * @brief A function that checks whether the current execution mode is VMX-root mode or not + * + */ +typedef BOOLEAN (*VM_FUNC_VMX_GET_CURRENT_EXECUTION_MODE)(); + +////////////////////////////////////////////////// +// Callback Structure // +////////////////////////////////////////////////// + +/** + * @brief Prototype of each function needed by hyperperf module + * + */ +typedef struct _HYPERPERF_CALLBACKS +{ + // + // *** Log (Hyperlog) callbacks *** + // + LOG_CALLBACK_PREPARE_AND_SEND_MESSAGE_TO_QUEUE LogCallbackPrepareAndSendMessageToQueueWrapper; + LOG_CALLBACK_SEND_MESSAGE_TO_QUEUE LogCallbackSendMessageToQueue; + LOG_CALLBACK_SEND_BUFFER LogCallbackSendBuffer; + LOG_CALLBACK_CHECK_IF_BUFFER_IS_FULL LogCallbackCheckIfBufferIsFull; + + // + // *** Hypervisor (Hyperhv) callbacks *** + // + VM_FUNC_VMX_GET_CURRENT_EXECUTION_MODE VmFuncVmxGetCurrentExecutionMode; + +} HYPERPERF_CALLBACKS, *PHYPERPERF_CALLBACKS; diff --git a/hyperdbg/include/components/pe/header/pe-image-reader.h b/hyperdbg/include/components/pe/header/pe-image-reader.h index bc4e0852..61fd23a5 100644 --- a/hyperdbg/include/components/pe/header/pe-image-reader.h +++ b/hyperdbg/include/components/pe/header/pe-image-reader.h @@ -11,7 +11,9 @@ */ #pragma once -/* +#ifndef _WIN32 +// On Windows these are provided by winnt.h; define them here for other platforms. + typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // Magic number @@ -46,7 +48,7 @@ typedef struct _IMAGE_FILE_HEADER WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; -#define IMAGE_SIZEOF_SHORT_NAME 8 +# define IMAGE_SIZEOF_SHORT_NAME 8 typedef struct _IMAGE_SECTION_HEADER { @@ -66,7 +68,7 @@ typedef struct _IMAGE_SECTION_HEADER DWORD Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; -*/ +#endif // _WIN32 typedef struct _PE_IMAGE_READER { diff --git a/hyperdbg/include/platform/general/header/Environment.h b/hyperdbg/include/platform/general/header/Environment.h index 55c5f29a..4abf4d21 100644 --- a/hyperdbg/include/platform/general/header/Environment.h +++ b/hyperdbg/include/platform/general/header/Environment.h @@ -30,11 +30,67 @@ # error "This code cannot compile on non-Windows, non-Linux, and non-BSD platforms" #endif - - -// Windows source annotation language (SAL) not present in Linux, so defining them here for the compiler #ifdef HYPERDBG_ENV_LINUX -# define _In_ -# define _Out_ -# define _Inout_ -#endif + +// SAL annotations +# define _In_ +# define _Out_ +# define _Inout_ +# define _In_opt_ +# define _Out_opt_ +# define _In_z_ +# define _Outptr_ +# define _In_reads_bytes_(x) +# define _Out_writes_bytes_(x) +# define _Inout_updates_bytes_all_(x) + +// wchar_t is a C++ built-in but needs this header in C +# include + +// POSIX sleep primitives (usleep) backing the Win32 Sleep() shim below +# include + +// Windows string/char types +typedef char TCHAR; +typedef char * LPTSTR; +typedef const char * LPCTSTR; +typedef const char * LPCSTR; +typedef char * LPSTR; +typedef const char * PCSTR; +typedef char * PSTR; +typedef short * PWCHAR; + +// Windows socket type (Linux sockets are plain int) +typedef int SOCKET; +# define INVALID_SOCKET ((SOCKET)(-1)) +# define SOCKET_ERROR (-1) + +// Windows calling convention (no-op on Linux) +# define WINAPI + +// Windows module handle (equivalent to dlopen's void * on Linux) +typedef void * HMODULE; + +// Misc Windows macros +# define UNREFERENCED_PARAMETER(P) ((void)(P)) + +// Win32 wait/event constants (used by the cross-platform sync wrappers) +# define INFINITE 0xFFFFFFFF +# define WAIT_OBJECT_0 0x00000000 + +// Win32 invalid handle sentinel (returned by the cross-platform file/serial wrappers) +# define INVALID_HANDLE_VALUE ((HANDLE)(SIZE_T)-1) + +// Win32 console-control event codes (kept at their Windows values so the +// shared BreakController() switch compiles unchanged). On Linux these are +// produced by the platform-signal layer from POSIX signals. +# define CTRL_C_EVENT 0 +# define CTRL_BREAK_EVENT 1 +# define CTRL_CLOSE_EVENT 2 +# define CTRL_LOGOFF_EVENT 5 +# define CTRL_SHUTDOWN_EVENT 6 + +// Win32 Sleep(milliseconds) -> POSIX usleep(microseconds) +# define Sleep(Milliseconds) usleep((useconds_t)(Milliseconds) * 1000) + +#endif // HYPERDBG_ENV_LINUX diff --git a/hyperdbg/include/platform/general/header/nt-list.h b/hyperdbg/include/platform/general/header/nt-list.h new file mode 100644 index 00000000..0dc4dd2b --- /dev/null +++ b/hyperdbg/include/platform/general/header/nt-list.h @@ -0,0 +1,145 @@ +/** + * @file nt-list.h + * @author Max Raulea (max.raulea@gmail.com) + * @brief Cross-platform NT-style intrusive doubly-linked list helpers + CONTAINING_RECORD + * @details The shared debugger code uses the NT LIST_ENTRY API (InitializeListHead, + * InsertHeadList, RemoveEntryList, CONTAINING_RECORD, ...). On Windows these + * come from (or, under USE_NATIVE_SDK_HEADERS, from the in-tree + * platform/user/header/Windows.h). Platforms whose system headers don't ship + * them (Linux) get them here, so the shared code compiles unchanged. + * + * These operate on the exact LIST_ENTRY {Flink, Blink} layout defined in + * SDK/headers/DataTypes.h, which is part of the kernel<->user IOCTL ABI -- + * that's why we mirror the NT list rather than reaching for sys/queue.h + * (different layout + a colliding LIST_ENTRY name). + * + * The body is compiled only where the OS headers don't already provide these + * (i.e. not on Windows), so including this unconditionally is safe. + * + * @version 0.20 + * @date 2026-06-16 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +#ifndef _WIN32 + +# include // offsetof + +// +// Given the address of a LIST_ENTRY field embedded in a struct, recover the +// address of the containing struct. +// +# ifndef CONTAINING_RECORD +# define CONTAINING_RECORD(address, type, field) \ + ((type *)((char *)(address) - offsetof(type, field))) +# endif + +// +// Initialize a list head to the empty (points-to-self) state. +// +static inline VOID +InitializeListHead(PLIST_ENTRY ListHead) +{ + ListHead->Flink = ListHead->Blink = ListHead; +} + +// +// TRUE if the list has no entries. +// +static inline BOOLEAN +IsListEmpty(const LIST_ENTRY * ListHead) +{ + return (BOOLEAN)(ListHead->Flink == ListHead); +} + +// +// Unlink an entry. Returns TRUE if the list is now empty. +// +static inline BOOLEAN +RemoveEntryList(PLIST_ENTRY Entry) +{ + PLIST_ENTRY Flink = Entry->Flink; + PLIST_ENTRY Blink = Entry->Blink; + + Blink->Flink = Flink; + Flink->Blink = Blink; + + return (BOOLEAN)(Flink == Blink); +} + +// +// Unlink and return the first entry. +// +static inline PLIST_ENTRY +RemoveHeadList(PLIST_ENTRY ListHead) +{ + PLIST_ENTRY Entry = ListHead->Flink; + PLIST_ENTRY Flink = Entry->Flink; + + ListHead->Flink = Flink; + Flink->Blink = ListHead; + + return Entry; +} + +// +// Unlink and return the last entry. +// +static inline PLIST_ENTRY +RemoveTailList(PLIST_ENTRY ListHead) +{ + PLIST_ENTRY Entry = ListHead->Blink; + PLIST_ENTRY Blink = Entry->Blink; + + ListHead->Blink = Blink; + Blink->Flink = ListHead; + + return Entry; +} + +// +// Insert an entry at the tail of the list. +// +static inline VOID +InsertTailList(PLIST_ENTRY ListHead, PLIST_ENTRY Entry) +{ + PLIST_ENTRY Blink = ListHead->Blink; + + Entry->Flink = ListHead; + Entry->Blink = Blink; + Blink->Flink = Entry; + ListHead->Blink = Entry; +} + +// +// Insert an entry at the head of the list. +// +static inline VOID +InsertHeadList(PLIST_ENTRY ListHead, PLIST_ENTRY Entry) +{ + PLIST_ENTRY Flink = ListHead->Flink; + + Entry->Flink = Flink; + Entry->Blink = ListHead; + Flink->Blink = Entry; + ListHead->Flink = Entry; +} + +// +// Splice the entries of ListToAppend onto the tail of ListHead. +// +static inline VOID +AppendTailList(PLIST_ENTRY ListHead, PLIST_ENTRY ListToAppend) +{ + PLIST_ENTRY ListEnd = ListHead->Blink; + + ListHead->Blink->Flink = ListToAppend; + ListHead->Blink = ListToAppend->Blink; + ListToAppend->Blink->Flink = ListHead; + ListToAppend->Blink = ListEnd; +} + +#endif // !_WIN32 diff --git a/hyperdbg/include/platform/kernel/code/PlatformIntrinsics.c b/hyperdbg/include/platform/kernel/code/PlatformIntrinsics.c index caf93924..3233d67e 100644 --- a/hyperdbg/include/platform/kernel/code/PlatformIntrinsics.c +++ b/hyperdbg/include/platform/kernel/code/PlatformIntrinsics.c @@ -325,6 +325,87 @@ CpuReadTscp(UINT32 * Aux) #endif } +////////////////////////////////////////////////// +// Interlocked (Atomic) Operations // +////////////////////////////////////////////////// + +/** + * @brief Atomic 64-bit exchange + */ +inline INT64 +CpuInterlockedExchange64(INT64 volatile * Target, INT64 Value) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedExchange64(Target, Value); +#elif defined(__linux__) + return __atomic_exchange_n(Target, Value, __ATOMIC_SEQ_CST); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Atomic 64-bit exchange-add + */ +inline INT64 +CpuInterlockedExchangeAdd64(INT64 volatile * Addend, INT64 Value) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedExchangeAdd64(Addend, Value); +#elif defined(__linux__) + return __atomic_fetch_add(Addend, Value, __ATOMIC_SEQ_CST); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Atomic 64-bit increment + */ +inline INT64 +CpuInterlockedIncrement64(INT64 volatile * Addend) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedIncrement64(Addend); +#elif defined(__linux__) + return __atomic_add_fetch(Addend, 1LL, __ATOMIC_SEQ_CST); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Atomic 64-bit decrement + */ +inline INT64 +CpuInterlockedDecrement64(INT64 volatile * Addend) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedDecrement64(Addend); +#elif defined(__linux__) + return __atomic_sub_fetch(Addend, 1LL, __ATOMIC_SEQ_CST); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Atomic 64-bit compare-exchange + */ +inline INT64 +CpuInterlockedCompareExchange64(INT64 volatile * Destination, INT64 ExChange, INT64 Comparand) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedCompareExchange64(Destination, ExChange, Comparand); +#elif defined(__linux__) + INT64 Expected = Comparand; + __atomic_compare_exchange_n(Destination, &Expected, ExChange, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return Expected; +#else +# error "Unsupported platform" +#endif +} + ////////////////////////////////////////////////// // Descriptor Table Instructions // ////////////////////////////////////////////////// diff --git a/hyperdbg/include/platform/kernel/code/PlatformMem.c b/hyperdbg/include/platform/kernel/code/PlatformMem.c index f93164ae..27e8112d 100644 --- a/hyperdbg/include/platform/kernel/code/PlatformMem.c +++ b/hyperdbg/include/platform/kernel/code/PlatformMem.c @@ -17,6 +17,31 @@ # include "../header/PlatformMem.h" #endif // defined(__linux__) +/** + * @brief Platform independent wrapper for sprintf_s / snprintf + * + * @param Buffer output buffer + * @param BufferSize size of the output buffer + * @param Format format string + * @return INT number of characters written, or -1 on error + */ +INT +PlatformSprintf(char * Buffer, SIZE_T BufferSize, const char * Format, ...) +{ + va_list Args; + va_start(Args, Format); + INT Result; +#if defined(_WIN32) || defined(_WIN64) + Result = vsprintf_s(Buffer, BufferSize, Format, Args); +#elif defined(__linux__) + Result = vsnprintf(Buffer, BufferSize, Format, Args); +#else +# error "Unsupported platform" +#endif + va_end(Args); + return Result; +} + ///////////////////////////////////////////////// /// ... New Unified API ... ///////////////////////////////////////////////// diff --git a/hyperdbg/include/platform/kernel/header/PlatformIntrinsics.h b/hyperdbg/include/platform/kernel/header/PlatformIntrinsics.h index 318b878b..618fc71b 100644 --- a/hyperdbg/include/platform/kernel/header/PlatformIntrinsics.h +++ b/hyperdbg/include/platform/kernel/header/PlatformIntrinsics.h @@ -207,6 +207,25 @@ extern inline UINT64 extern inline UINT64 CpuReadTscp(UINT32 * Aux); +////////////////////////////////////////////////// +// Interlocked (Atomic) Operations // +////////////////////////////////////////////////// + +extern inline INT64 +CpuInterlockedExchange64(INT64 volatile * Target, INT64 Value); + +extern inline INT64 +CpuInterlockedExchangeAdd64(INT64 volatile * Addend, INT64 Value); + +extern inline INT64 +CpuInterlockedIncrement64(INT64 volatile * Addend); + +extern inline INT64 +CpuInterlockedDecrement64(INT64 volatile * Addend); + +extern inline INT64 +CpuInterlockedCompareExchange64(INT64 volatile * Destination, INT64 ExChange, INT64 Comparand); + ////////////////////////////////////////////////// // Descriptor Table Instructions // ////////////////////////////////////////////////// diff --git a/hyperdbg/include/platform/kernel/header/PlatformMem.h b/hyperdbg/include/platform/kernel/header/PlatformMem.h index c7398722..b031fee1 100644 --- a/hyperdbg/include/platform/kernel/header/PlatformMem.h +++ b/hyperdbg/include/platform/kernel/header/PlatformMem.h @@ -22,6 +22,9 @@ // Functions // ////////////////////////////////////////////////// +INT +PlatformSprintf(char * Buffer, SIZE_T BufferSize, const char * Format, ...); + VOID PlatformFreeMemory(PVOID Memory); diff --git a/hyperdbg/include/platform/user/code/platform-intrinsics.c b/hyperdbg/include/platform/user/code/platform-intrinsics.c index 7340662f..805c78e9 100644 --- a/hyperdbg/include/platform/user/code/platform-intrinsics.c +++ b/hyperdbg/include/platform/user/code/platform-intrinsics.c @@ -83,10 +83,32 @@ CpuReadTsc(VOID) // Misc Instructions // ////////////////////////////////////////////////// +/** + * @brief Read Time-Stamp Counter (serializing) + * + * @param Aux processor ID output (may be NULL) + * @return UINT64 + */ +UINT64 +CpuReadTscp(UINT32 * Aux) +{ +#if defined(_WIN32) || defined(_WIN64) + return __rdtscp(Aux); +#elif defined(__linux__) + UINT32 __lo, __hi, __aux; + __asm__ __volatile__("rdtscp" : "=a"(__lo), "=d"(__hi), "=c"(__aux)); + if (Aux) + *Aux = __aux; + return ((UINT64)__hi << 32) | __lo; +#else +# error "Unsupported platform" +#endif +} + /** * @brief Execute PAUSE (spin-wait hint) */ - VOID +VOID CpuPause(VOID) { #if defined(_WIN32) || defined(_WIN64) @@ -97,3 +119,83 @@ CpuPause(VOID) # error "Unsupported platform" #endif } + +////////////////////////////////////////////////// +// Interlocked (Atomic) Operations // +////////////////////////////////////////////////// + +INT64 +CpuInterlockedExchange64(INT64 volatile * Target, INT64 Value) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedExchange64(Target, Value); +#elif defined(__linux__) + return __atomic_exchange_n(Target, Value, __ATOMIC_SEQ_CST); +#else +# error "Unsupported platform" +#endif +} + +INT64 +CpuInterlockedExchangeAdd64(INT64 volatile * Addend, INT64 Value) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedExchangeAdd64(Addend, Value); +#elif defined(__linux__) + return __atomic_fetch_add(Addend, Value, __ATOMIC_SEQ_CST); +#else +# error "Unsupported platform" +#endif +} + +INT64 +CpuInterlockedIncrement64(INT64 volatile * Addend) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedIncrement64(Addend); +#elif defined(__linux__) + return __atomic_add_fetch(Addend, 1LL, __ATOMIC_SEQ_CST); +#else +# error "Unsupported platform" +#endif +} + +INT64 +CpuInterlockedDecrement64(INT64 volatile * Addend) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedDecrement64(Addend); +#elif defined(__linux__) + return __atomic_sub_fetch(Addend, 1LL, __ATOMIC_SEQ_CST); +#else +# error "Unsupported platform" +#endif +} + +INT64 +CpuInterlockedCompareExchange64(INT64 volatile * Destination, INT64 ExChange, INT64 Comparand) +{ +#if defined(_WIN32) || defined(_WIN64) + return InterlockedCompareExchange64(Destination, ExChange, Comparand); +#elif defined(__linux__) + INT64 Expected = Comparand; + __atomic_compare_exchange_n(Destination, &Expected, ExChange, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return Expected; +#else +# error "Unsupported platform" +#endif +} + +UCHAR +CpuInterlockedBitTestAndSet(volatile LONG * Base, LONG Bit) +{ +#if defined(_WIN32) || defined(_WIN64) + return _interlockedbittestandset(Base, Bit); +#elif defined(__linux__) + LONG Mask = (1L << Bit); + LONG Old = __atomic_fetch_or(Base, Mask, __ATOMIC_SEQ_CST); + return (UCHAR)((Old >> Bit) & 1); +#else +# error "Unsupported platform" +#endif +} diff --git a/hyperdbg/include/platform/user/code/platform-ioctl.c b/hyperdbg/include/platform/user/code/platform-ioctl.c new file mode 100644 index 00000000..edfc699d --- /dev/null +++ b/hyperdbg/include/platform/user/code/platform-ioctl.c @@ -0,0 +1,83 @@ +/** + * @file platform-ioctl.c + * @author Max Raulea (max.raulea@gmail.com) + * @brief User mode cross-platform implementation of the local kernel-driver IOCTL transport + * @details See platform-ioctl.h. The Windows branch forwards directly to Win32 + * DeviceIoControl. The Linux branch is currently stubbed (returns FALSE) and is + * the home where the ioctl()-based implementation against the /dev/HyperDbg + * character device will live once the kernel module exists. + * + * @version 0.20 + * @date 2026-06-09 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#include "pch.h" + +#if defined(__linux__) +# include "../header/platform-ioctl.h" +#endif // defined(__linux__) + +#if defined(_WIN32) + +BOOL +PlatformDeviceIoControl(HANDLE Device, + DWORD IoControlCode, + LPVOID InBuffer, + DWORD InBufferSize, + LPVOID OutBuffer, + DWORD OutBufferSize, + LPDWORD BytesReturned, + LPVOID Overlapped) +{ + return DeviceIoControl(Device, + IoControlCode, + InBuffer, + InBufferSize, + OutBuffer, + OutBufferSize, + BytesReturned, + (LPOVERLAPPED)Overlapped); +} + +#elif defined(__linux__) + +// +// TODO: implement the local driver transport on Linux using ioctl() against a +// /dev/HyperDbg character device exposed by the kernel module: +// - open the device once (in the library init path) -> file descriptor stored +// in g_DeviceHandle +// - PlatformDeviceIoControl -> ioctl(fd, IoControlCode, ...) with the in/out +// buffer marshalling the driver expects (likely a single in-out buffer) +// - close on teardown +// The kernel module does not exist yet, so this returns failure for now: callers +// that have already asserted g_DeviceHandle will simply report the IOCTL failed +// rather than crashing. +// + +BOOL +PlatformDeviceIoControl(HANDLE Device, + DWORD IoControlCode, + LPVOID InBuffer, + DWORD InBufferSize, + LPVOID OutBuffer, + DWORD OutBufferSize, + LPDWORD BytesReturned, + LPVOID Overlapped) +{ + (void)Device; + (void)IoControlCode; + (void)InBuffer; + (void)InBufferSize; + (void)OutBuffer; + (void)OutBufferSize; + (void)Overlapped; + if (BytesReturned) + *BytesReturned = 0; + return FALSE; +} + +#else +# error "Unsupported platform" +#endif diff --git a/hyperdbg/include/platform/user/code/platform-lib-calls.c b/hyperdbg/include/platform/user/code/platform-lib-calls.c index 803a253a..19768c55 100644 --- a/hyperdbg/include/platform/user/code/platform-lib-calls.c +++ b/hyperdbg/include/platform/user/code/platform-lib-calls.c @@ -13,6 +13,11 @@ #if defined(__linux__) # include "../header/platform-lib-calls.h" +# include +# include +# include +# include +# include #endif // defined(__linux__) /** @@ -71,3 +76,538 @@ PlatformZeroMemory(PVOID Buffer, SIZE_T Size) # error "Unsupported platform" #endif } + +/** + * @brief Platform independent wrapper for QueryPerformanceFrequency + * + * @param Frequency output — ticks per second + * @return BOOLEAN TRUE on success + */ +BOOLEAN +PlatformQueryPerformanceFrequency(LARGE_INTEGER * Frequency) +{ +#if defined(_WIN32) + return (BOOLEAN)QueryPerformanceFrequency((LARGE_INTEGER *)Frequency); +#elif defined(__linux__) + Frequency->QuadPart = 1000000000LL; // clock_gettime gives nanosecond resolution + return TRUE; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for QueryPerformanceCounter + * + * @param Count output — current tick count + * @return BOOLEAN TRUE on success + */ +BOOLEAN +PlatformQueryPerformanceCounter(LARGE_INTEGER * Count) +{ +#if defined(_WIN32) + return (BOOLEAN)QueryPerformanceCounter((LARGE_INTEGER *)Count); +#elif defined(__linux__) + struct timespec Ts; + clock_gettime(CLOCK_MONOTONIC, &Ts); + Count->QuadPart = (INT64)Ts.tv_sec * 1000000000LL + Ts.tv_nsec; + return TRUE; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for sprintf_s / snprintf + * + * @param Buffer output buffer + * @param BufferSize size of the output buffer + * @param Format format string + * @return INT number of characters written, or -1 on error + */ +INT +PlatformSprintf(char * Buffer, SIZE_T BufferSize, const char * Format, ...) +{ + va_list Args; + va_start(Args, Format); + INT Result; +#if defined(_WIN32) + Result = vsprintf_s(Buffer, BufferSize, Format, Args); +#elif defined(__linux__) + Result = vsnprintf(Buffer, BufferSize, Format, Args); +#else +# error "Unsupported platform" +#endif + va_end(Args); + return Result; +} + +/** + * @brief Platform independent wrapper for GetCurrentThreadId / gettid + * + * @return UINT32 thread ID of the calling thread + */ +UINT32 +PlatformGetCurrentThreadId(VOID) +{ +#if defined(_WIN32) + return (UINT32)GetCurrentThreadId(); +#elif defined(__linux__) + return (UINT32)syscall(SYS_gettid); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for GetCurrentProcessorNumber / sched_getcpu + * + * @return UINT32 logical processor index the calling thread is running on + */ +UINT32 +PlatformGetCurrentProcessorNumber(VOID) +{ +#if defined(_WIN32) + return (UINT32)GetCurrentProcessorNumber(); +#elif defined(__linux__) + return (UINT32)sched_getcpu(); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for GetCurrentProcessId / getpid + * + * @return UINT32 PID of the calling process + */ +UINT32 +PlatformGetCurrentProcessId(VOID) +{ +#if defined(_WIN32) + return (UINT32)GetCurrentProcessId(); +#elif defined(__linux__) + return (UINT32)getpid(); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper to get the current process name + * + * @return CHAR* pointer to a static buffer holding the process name, or NULL on failure + */ +CHAR * +PlatformGetCurrentProcessName(VOID) +{ + static CHAR ProcessNameBuf[MAX_PATH] = {0}; + +#if defined(_WIN32) + // + // Use base kernel32 only (no psapi/shlwapi) so this compiles in every + // project that builds platform-lib-calls.c (e.g. script-engine, which has + // a minimal include set). GetModuleFileNameA(NULL, ...) returns the full + // path of the current process image. + // + if (GetModuleFileNameA(NULL, ProcessNameBuf, MAX_PATH) == 0) + { + return NULL; + } + + // + // Return the basename (strip the directory part) + // + char * LastSeparator = strrchr(ProcessNameBuf, '\\'); + if (LastSeparator) + { + return LastSeparator + 1; + } + + return ProcessNameBuf; + +#elif defined(__linux__) + FILE * f = fopen("/proc/self/comm", "r"); + if (f) + { + if (fgets(ProcessNameBuf, sizeof(ProcessNameBuf), f)) + { + size_t Len = strlen(ProcessNameBuf); + if (Len > 0 && ProcessNameBuf[Len - 1] == '\n') + ProcessNameBuf[Len - 1] = '\0'; + } + fclose(f); + return ProcessNameBuf; + } + return NULL; + +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for CreateEvent + * + * @param ManualReset TRUE for a manual-reset event, FALSE for auto-reset + * @param InitialState TRUE if the event starts signaled + * @return HANDLE to the event, or NULL on failure + */ +HANDLE +PlatformCreateEvent(BOOLEAN ManualReset, BOOLEAN InitialState) +{ +#if defined(_WIN32) + return CreateEvent(NULL, ManualReset, InitialState, NULL); +#elif defined(__linux__) + // + // TODO: back this with a pthread mutex+cond (or eventfd) when the Linux + // kernel-debugger transport is implemented. For now return a dummy + // non-NULL handle so existing NULL-checks treat creation as success. + // + (void)ManualReset; + (void)InitialState; + return (HANDLE)(uintptr_t)1; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for SetEvent + */ +BOOLEAN +PlatformSetEvent(HANDLE EventHandle) +{ +#if defined(_WIN32) + return (BOOLEAN)SetEvent(EventHandle); +#elif defined(__linux__) + (void)EventHandle; // TODO: signal the underlying cond/eventfd + return TRUE; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for ResetEvent + */ +BOOLEAN +PlatformResetEvent(HANDLE EventHandle) +{ +#if defined(_WIN32) + return (BOOLEAN)ResetEvent(EventHandle); +#elif defined(__linux__) + (void)EventHandle; // TODO: clear the underlying cond/eventfd + return TRUE; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for WaitForSingleObject + * + * @return 0 (WAIT_OBJECT_0) on success + */ +DWORD +PlatformWaitForSingleObject(HANDLE Handle, DWORD TimeoutMilliseconds) +{ +#if defined(_WIN32) + return WaitForSingleObject(Handle, TimeoutMilliseconds); +#elif defined(__linux__) + // + // TODO: wait on the underlying cond/eventfd. For now return immediately as + // success — no real transport exists yet to wait on. + // + (void)Handle; + (void)TimeoutMilliseconds; + return 0; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for CloseHandle + */ +BOOLEAN +PlatformCloseHandle(HANDLE Handle) +{ +#if defined(_WIN32) + return (BOOLEAN)CloseHandle(Handle); +#elif defined(__linux__) + (void)Handle; // TODO: free the underlying cond/eventfd or close the fd + return TRUE; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for GetLastError + */ +DWORD +PlatformGetLastError(VOID) +{ +#if defined(_WIN32) + return GetLastError(); +#elif defined(__linux__) + return (DWORD)errno; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper to write raw bytes to the console + * + * @details Used to emit pre-encoded UTF-8 byte sequences (e.g. box-drawing + * characters) directly to standard output. On Windows this goes + * through WriteConsoleA so the console code page is bypassed; on + * Linux the terminal is UTF-8 native so the bytes are written as-is. + * + * @param Buffer pointer to the bytes to write + * @param NumberOfBytes number of bytes to write + * @return BOOLEAN TRUE on success + */ +BOOLEAN +PlatformWriteConsole(const VOID * Buffer, DWORD NumberOfBytes) +{ +#if defined(_WIN32) + return (BOOLEAN)WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), Buffer, NumberOfBytes, NULL, NULL); +#elif defined(__linux__) + return (BOOLEAN)(fwrite(Buffer, 1, NumberOfBytes, stdout) == NumberOfBytes); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper to create/open a file for writing + * + * @param Path wide path of the file to create (truncated if it exists) + * @return HANDLE to the opened file, or INVALID_HANDLE_VALUE on failure + */ +HANDLE +PlatformOpenFileForWriting(const WCHAR * Path) +{ +#if defined(_WIN32) + return CreateFileW(Path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); +#elif defined(__linux__) + // + // TODO: handle this later. The path arrives as a std::wstring (4-byte + // wchar_t on Linux) and must be narrowed to a UTF-8 char* before it + // can be handed to fopen. Until that conversion is wired up, fail the + // open so callers (e.g. dump.cpp) bail out cleanly instead of writing + // to a bogus handle. + // + (void)Path; + return INVALID_HANDLE_VALUE; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper to write a buffer to an open file + * + * @param FileHandle handle returned by PlatformOpenFileForWriting + * @param Buffer pointer to the bytes to write + * @param NumberOfBytes number of bytes to write + * @return BOOLEAN TRUE on success + */ +BOOLEAN +PlatformWriteFile(HANDLE FileHandle, const VOID * Buffer, DWORD NumberOfBytes) +{ +#if defined(_WIN32) + DWORD BytesWritten; + return (BOOLEAN)WriteFile(FileHandle, Buffer, NumberOfBytes, &BytesWritten, NULL); +#elif defined(__linux__) + return (BOOLEAN)(fwrite(Buffer, 1, NumberOfBytes, (FILE *)FileHandle) == NumberOfBytes); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper to close a file opened by + * PlatformOpenFileForWriting + * + * @param FileHandle handle to close + * @return BOOLEAN TRUE on success + */ +BOOLEAN +PlatformCloseFile(HANDLE FileHandle) +{ +#if defined(_WIN32) + return (BOOLEAN)CloseHandle(FileHandle); +#elif defined(__linux__) + return (BOOLEAN)(fclose((FILE *)FileHandle) == 0); +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper to map an entire file read-only into memory + * + * @details The returned pointer stays valid until released with PlatformUnmapFile; + * the underlying file/descriptor is closed before returning (the mapping + * outlives it on both platforms). + * + * @param Path wide path of the file to map + * @param OutFileSize output — size of the file in bytes (0 on failure) + * @return VOID* base address of the mapped file, or NULL on failure + */ +VOID * +PlatformMapFileReadOnly(const WCHAR * Path, PSIZE_T OutFileSize, PHANDLE OutFileHandle) +{ +#if defined(_WIN32) + HANDLE FileHandle; + HANDLE MapObjectHandle; + VOID * BaseAddr; + LARGE_INTEGER FileSize; + + *OutFileSize = 0; + *OutFileHandle = INVALID_HANDLE_VALUE; + + FileHandle = CreateFileW(Path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (FileHandle == INVALID_HANDLE_VALUE) + { + return NULL; + } + + if (!GetFileSizeEx(FileHandle, &FileSize)) + { + CloseHandle(FileHandle); + return NULL; + } + + MapObjectHandle = CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL); + if (MapObjectHandle == NULL) + { + CloseHandle(FileHandle); + return NULL; + } + + BaseAddr = MapViewOfFile(MapObjectHandle, FILE_MAP_READ, 0, 0, 0); + + // + // The view stays valid after the mapping object handle is closed. The file + // handle is kept open and handed back so the caller can still issue raw + // reads (PlatformReadFileAtOffset); it is closed by PlatformUnmapFile. + // + CloseHandle(MapObjectHandle); + + if (BaseAddr == NULL) + { + CloseHandle(FileHandle); + return NULL; + } + + *OutFileSize = (SIZE_T)FileSize.QuadPart; + *OutFileHandle = FileHandle; + return BaseAddr; +#elif defined(__linux__) + // + // TODO (linux): implement the real mapping. Expected contract: + // 1. Narrow the 4-byte wchar_t 'Path' to a UTF-8 char* (the project still + // lacks a wchar_t->UTF-8 helper; the same one is needed by + // PlatformOpenFileForWriting for the dump.cpp write path). + // 2. fd = open(narrowed_path, O_RDONLY); // fail -> NULL + // 3. fstat(fd, &st) to get the file size. + // 4. base = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + // 5. *OutFileHandle = (HANDLE)(intptr_t)fd; // keep fd open for raw reads + // 6. *OutFileSize = st.st_size; return base; (return NULL on any failure) + // PlatformUnmapFile must then munmap(base, size) and close the fd — which is + // why both the size and the handle are passed back in on unmap. The raw-read + // path (PlatformReadFileAtOffset) would pread() from that same fd. + // + // Until implemented, fail the map so PE-parser callers print "could not open + // the file" and bail out cleanly instead of dereferencing a bogus pointer. + // + (void)Path; + *OutFileSize = 0; + *OutFileHandle = INVALID_HANDLE_VALUE; + return NULL; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper for a positioned (seek + read) file read + * + * @param FileHandle handle handed back by PlatformMapFileReadOnly + * @param Offset byte offset to read from (absolute, from start of file) + * @param Buffer destination buffer + * @param NumberOfBytes number of bytes to read + * @param BytesRead output — number of bytes actually read + * @return BOOLEAN TRUE on success + */ +BOOLEAN +PlatformReadFileAtOffset(HANDLE FileHandle, UINT64 Offset, VOID * Buffer, DWORD NumberOfBytes, LPDWORD BytesRead) +{ +#if defined(_WIN32) + LARGE_INTEGER Distance; + Distance.QuadPart = (LONGLONG)Offset; + + if (!SetFilePointerEx(FileHandle, Distance, NULL, FILE_BEGIN)) + { + return FALSE; + } + + return (BOOLEAN)ReadFile(FileHandle, Buffer, NumberOfBytes, BytesRead, NULL); +#elif defined(__linux__) + // + // TODO (linux): pread((int)(intptr_t)FileHandle, Buffer, NumberOfBytes, Offset) + // once PlatformMapFileReadOnly wraps a real fd. Unreached today + // because the map returns NULL on Linux, so callers bail first. + // + (void)FileHandle; + (void)Offset; + (void)Buffer; + (void)NumberOfBytes; + if (BytesRead != NULL) + { + *BytesRead = 0; + } + return FALSE; +#else +# error "Unsupported platform" +#endif +} + +/** + * @brief Platform independent wrapper to release a mapping from PlatformMapFileReadOnly + * + * @param BaseAddress base address returned by PlatformMapFileReadOnly + * @param FileSize size that was reported by PlatformMapFileReadOnly (needed by munmap) + * @param FileHandle file handle handed back by PlatformMapFileReadOnly + */ +VOID +PlatformUnmapFile(VOID * BaseAddress, SIZE_T FileSize, HANDLE FileHandle) +{ +#if defined(_WIN32) + (void)FileSize; // not needed by UnmapViewOfFile + if (BaseAddress != NULL) + { + UnmapViewOfFile(BaseAddress); + } + if (FileHandle != INVALID_HANDLE_VALUE) + { + CloseHandle(FileHandle); + } +#elif defined(__linux__) + // + // TODO (linux): munmap(BaseAddress, FileSize) and close the fd behind + // FileHandle once PlatformMapFileReadOnly is implemented. + // No-op for now since the map always returns NULL. + // + (void)BaseAddress; + (void)FileSize; + (void)FileHandle; +#else +# error "Unsupported platform" +#endif +} diff --git a/hyperdbg/include/platform/user/code/platform-serial.c b/hyperdbg/include/platform/user/code/platform-serial.c new file mode 100644 index 00000000..6266c0a8 --- /dev/null +++ b/hyperdbg/include/platform/user/code/platform-serial.c @@ -0,0 +1,270 @@ +/** + * @file platform-serial.c + * @author Max Raulea (max.raulea@gmail.com) + * @brief User mode cross-platform implementation of the kernel-debugger serial transport + * @details See platform-serial.h. The Windows branch wraps the Win32 serial primitives + * (CreateFile / Comm* / overlapped ReadFile/WriteFile) and owns the per-direction + * OVERLAPPED state internally so the protocol layer never sees it. The Linux + * branch is currently stubbed (returns FALSE/NULL) and is the home where the + * termios-based implementation over /dev/tty* will live. + * + * @version 0.20 + * @date 2026-06-08 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#include "pch.h" + +#if defined(__linux__) +# include "../header/platform-serial.h" +#endif // defined(__linux__) + +#if defined(_WIN32) + +// +// Per-direction overlapped I/O state, owned by the transport layer. +// +static OVERLAPPED g_PlatformOverlappedReadDebugger = {0}; +static OVERLAPPED g_PlatformOverlappedReadDebuggee = {0}; +static OVERLAPPED g_PlatformOverlappedWriteDebugger = {0}; + +HANDLE +PlatformSerialOpen(const char * PortName, PLATFORM_SERIAL_IO_ROLE Role) +{ + HANDLE Comm; + char PortNo[24] = {0}; + + // + // Append name to make a Windows-understandable format + // + sprintf_s(PortNo, sizeof(PortNo), "\\\\.\\%s", PortName); + + if (Role == PLATFORM_SERIAL_IO_DEBUGGEE) + { + // + // Debuggee uses non-overlapped (blocking) I/O + // + Comm = CreateFile(PortNo, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + + g_PlatformOverlappedReadDebuggee.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + } + else + { + // + // Debugger uses overlapped (async) I/O + // + Comm = CreateFile(PortNo, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL); + + g_PlatformOverlappedReadDebugger.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + g_PlatformOverlappedWriteDebugger.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + } + + if (Comm == INVALID_HANDLE_VALUE) + { + return NULL; + } + + // + // Purge the serial port + // + PurgeComm(Comm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); + + return Comm; +} + +BOOLEAN +PlatformSerialConfigure(HANDLE Handle, DWORD BaudRate) +{ + DCB SerialParams = {0}; + SerialParams.DCBlength = sizeof(SerialParams); + + if (GetCommState(Handle, &SerialParams) == FALSE) + { + return FALSE; + } + + SerialParams.BaudRate = BaudRate; + SerialParams.ByteSize = 8; + SerialParams.StopBits = ONESTOPBIT; + SerialParams.Parity = NOPARITY; + + if (SetCommState(Handle, &SerialParams) == FALSE) + { + return FALSE; + } + + return TRUE; +} + +BOOLEAN +PlatformSerialReadByte(HANDLE Handle, + CHAR * OutByte, + DWORD * BytesRead, + PLATFORM_SERIAL_IO_ROLE Role) +{ + OVERLAPPED * Ovl = (Role == PLATFORM_SERIAL_IO_DEBUGGEE) + ? &g_PlatformOverlappedReadDebuggee + : &g_PlatformOverlappedReadDebugger; + + if (Role == PLATFORM_SERIAL_IO_DEBUGGEE) + { + // + // Apply a read timeout for the debuggee side + // + COMMTIMEOUTS Timeouts; + GetCommTimeouts(Handle, &Timeouts); + Timeouts.ReadIntervalTimeout = MAXDWORD; + Timeouts.ReadTotalTimeoutConstant = 5000; + Timeouts.ReadTotalTimeoutMultiplier = 0; + Timeouts.WriteTotalTimeoutConstant = 0; + Timeouts.WriteTotalTimeoutMultiplier = 0; + SetCommTimeouts(Handle, &Timeouts); + } + + if (!ReadFile(Handle, OutByte, sizeof(CHAR), NULL, Ovl)) + { + if (GetLastError() != ERROR_IO_PENDING) + { + return FALSE; + } + } + + WaitForSingleObject(Ovl->hEvent, INFINITE); + GetOverlappedResult(Handle, Ovl, BytesRead, FALSE); + ResetEvent(Ovl->hEvent); + + return TRUE; +} + +BOOLEAN +PlatformSerialWrite(HANDLE Handle, const void * Buffer, UINT32 Length, BOOLEAN Synchronous) +{ + if (Synchronous) + { + DWORD BytesWritten = 0; + if (WriteFile(Handle, Buffer, Length, &BytesWritten, NULL) == FALSE) + { + return FALSE; + } + return (BytesWritten == Length); + } + else + { + if (WriteFile(Handle, Buffer, Length, NULL, &g_PlatformOverlappedWriteDebugger)) + { + return TRUE; + } + + if (GetLastError() != ERROR_IO_PENDING) + { + return FALSE; + } + + if (WaitForSingleObject(g_PlatformOverlappedWriteDebugger.hEvent, INFINITE) != WAIT_OBJECT_0) + { + return FALSE; + } + + ResetEvent(g_PlatformOverlappedWriteDebugger.hEvent); + return TRUE; + } +} + +BOOLEAN +PlatformSerialClose(HANDLE Handle) +{ + if (g_PlatformOverlappedReadDebugger.hEvent) + CloseHandle(g_PlatformOverlappedReadDebugger.hEvent); + if (g_PlatformOverlappedReadDebuggee.hEvent) + CloseHandle(g_PlatformOverlappedReadDebuggee.hEvent); + if (g_PlatformOverlappedWriteDebugger.hEvent) + CloseHandle(g_PlatformOverlappedWriteDebugger.hEvent); + + g_PlatformOverlappedReadDebugger.hEvent = NULL; + g_PlatformOverlappedReadDebuggee.hEvent = NULL; + g_PlatformOverlappedWriteDebugger.hEvent = NULL; + + if (Handle) + return (BOOLEAN)CloseHandle(Handle); + + return TRUE; +} + +#elif defined(__linux__) + +// +// TODO: implement the serial transport on Linux using termios over /dev/tty*: +// - PlatformSerialOpen -> open(PortName, O_RDWR | O_NOCTTY) +// - PlatformSerialConfigure -> tcgetattr/cfsetspeed/tcsetattr (raw, 8-N-1) +// - PlatformSerialReadByte -> read() (with VTIME/VMIN or poll() for the timeout role) +// - PlatformSerialWrite -> write() +// - PlatformSerialClose -> close() +// Named-pipe transport would map onto a UNIX domain socket / FIFO. +// +// Until then these return failure so the kernel-debugger connection simply +// reports "not supported on Linux yet" rather than crashing. +// + +HANDLE +PlatformSerialOpen(const char * PortName, PLATFORM_SERIAL_IO_ROLE Role) +{ + (void)PortName; + (void)Role; + return NULL; +} + +BOOLEAN +PlatformSerialConfigure(HANDLE Handle, DWORD BaudRate) +{ + (void)Handle; + (void)BaudRate; + return FALSE; +} + +BOOLEAN +PlatformSerialReadByte(HANDLE Handle, + CHAR * OutByte, + DWORD * BytesRead, + PLATFORM_SERIAL_IO_ROLE Role) +{ + (void)Handle; + (void)OutByte; + (void)Role; + if (BytesRead) + *BytesRead = 0; + return FALSE; +} + +BOOLEAN +PlatformSerialWrite(HANDLE Handle, const void * Buffer, UINT32 Length, BOOLEAN Synchronous) +{ + (void)Handle; + (void)Buffer; + (void)Length; + (void)Synchronous; + return FALSE; +} + +BOOLEAN +PlatformSerialClose(HANDLE Handle) +{ + (void)Handle; + return TRUE; +} + +#else +# error "Unsupported platform" +#endif diff --git a/hyperdbg/include/platform/user/code/platform-signal.c b/hyperdbg/include/platform/user/code/platform-signal.c new file mode 100644 index 00000000..b0f83071 --- /dev/null +++ b/hyperdbg/include/platform/user/code/platform-signal.c @@ -0,0 +1,137 @@ +/** + * @file platform-signal.c + * @author Max Raulea (max.raulea@gmail.com) + * @brief User mode cross-platform implementation of the console-control handler + * @details See platform-signal.h. The Windows branch forwards to SetConsoleCtrlHandler. + * The Linux branch blocks the handled signals and dispatches them from a + * dedicated sigwait() thread. sigwait() (rather than a sigaction() handler) + * is used deliberately: BreakController calls ShowMessages (printf), socket + * I/O and Sleep, none of which are async-signal-safe. Running the handler on + * a normal thread woken by sigwait() sidesteps that entirely and mirrors the + * Windows model where the console-control handler also runs on its own thread. + * + * @version 0.20 + * @date 2026-06-16 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#include "pch.h" + +#if defined(__linux__) +# include "../header/platform-signal.h" +#endif // defined(__linux__) + +#if defined(_WIN32) + +BOOLEAN +PlatformInstallCtrlHandler(PLATFORM_CTRL_HANDLER Handler) +{ + return (BOOLEAN)SetConsoleCtrlHandler((PHANDLER_ROUTINE)Handler, TRUE); +} + +#elif defined(__linux__) + +# include +# include + +// +// The installed handler, read by the dispatch thread. +// +static PLATFORM_CTRL_HANDLER g_PlatformCtrlHandler = NULL; + +// +// Map a POSIX signal onto the Win32 console-control event the shared handler +// understands. Only the two interactive break signals are mapped: +// SIGINT (CTRL+C) -> CTRL_C_EVENT +// SIGQUIT (CTRL+\) -> CTRL_BREAK_EVENT +// SIGTERM/SIGHUP are intentionally left at their default disposition so the +// process stays killable/terminable the usual way; the CLOSE/LOGOFF/SHUTDOWN +// console events have no clean Linux analog. +// +static DWORD +PlatformMapSignalToCtrlType(int Signal) +{ + switch (Signal) + { + case SIGINT: + return CTRL_C_EVENT; + case SIGQUIT: + return CTRL_BREAK_EVENT; + default: + return (DWORD)-1; + } +} + +// +// Dedicated thread: waits for the handled signals and dispatches them to the +// installed handler in ordinary (async-signal-safe) thread context. +// +static void * +PlatformSignalThread(void * Arg) +{ + sigset_t WaitSet; + int Signal; + + (void)Arg; + + sigemptyset(&WaitSet); + sigaddset(&WaitSet, SIGINT); + sigaddset(&WaitSet, SIGQUIT); + + while (1) + { + if (sigwait(&WaitSet, &Signal) != 0) + { + continue; + } + + DWORD CtrlType = PlatformMapSignalToCtrlType(Signal); + + if (g_PlatformCtrlHandler != NULL && CtrlType != (DWORD)-1) + { + g_PlatformCtrlHandler(CtrlType); + } + } + + return NULL; +} + +BOOLEAN +PlatformInstallCtrlHandler(PLATFORM_CTRL_HANDLER Handler) +{ + sigset_t BlockSet; + pthread_t Thread; + + g_PlatformCtrlHandler = Handler; + + // + // Block the handled signals in this (main) thread. Done before any other + // thread is created, the mask is inherited by every thread, guaranteeing + // the signals are delivered to our sigwait() thread and nowhere else. + // + sigemptyset(&BlockSet); + sigaddset(&BlockSet, SIGINT); + sigaddset(&BlockSet, SIGQUIT); + + if (pthread_sigmask(SIG_BLOCK, &BlockSet, NULL) != 0) + { + return FALSE; + } + + if (pthread_create(&Thread, NULL, PlatformSignalThread, NULL) != 0) + { + return FALSE; + } + + // + // Fire-and-forget: the thread lives for the life of the process. + // + pthread_detach(Thread); + + return TRUE; +} + +#else +# error "Unsupported platform" +#endif diff --git a/hyperdbg/include/platform/user/header/platform-intrinsics.h b/hyperdbg/include/platform/user/header/platform-intrinsics.h index c2334a1d..f90b7c90 100644 --- a/hyperdbg/include/platform/user/header/platform-intrinsics.h +++ b/hyperdbg/include/platform/user/header/platform-intrinsics.h @@ -41,6 +41,12 @@ CpuCpuIdEx(INT32 * CpuInfo, INT32 FunctionId, INT32 SubFunctionId); UINT64 CpuReadTsc(VOID); +// +// RDTSCP +// +UINT64 +CpuReadTscp(UINT32 * Aux); + ////////////////////////////////////////////////// // Misc Instructions // ////////////////////////////////////////////////// @@ -49,4 +55,26 @@ CpuReadTsc(VOID); // PAUSE // VOID - CpuPause(VOID); +CpuPause(VOID); + +////////////////////////////////////////////////// +// Interlocked (Atomic) Operations // +////////////////////////////////////////////////// + +INT64 +CpuInterlockedExchange64(INT64 volatile * Target, INT64 Value); + +INT64 +CpuInterlockedExchangeAdd64(INT64 volatile * Addend, INT64 Value); + +INT64 +CpuInterlockedIncrement64(INT64 volatile * Addend); + +INT64 +CpuInterlockedDecrement64(INT64 volatile * Addend); + +INT64 +CpuInterlockedCompareExchange64(INT64 volatile * Destination, INT64 ExChange, INT64 Comparand); + +UCHAR +CpuInterlockedBitTestAndSet(volatile LONG * Base, LONG Bit); diff --git a/hyperdbg/include/platform/user/header/platform-ioctl.h b/hyperdbg/include/platform/user/header/platform-ioctl.h new file mode 100644 index 00000000..c2c7ce14 --- /dev/null +++ b/hyperdbg/include/platform/user/header/platform-ioctl.h @@ -0,0 +1,40 @@ +/** + * @file platform-ioctl.h + * @author Max Raulea (max.raulea@gmail.com) + * @brief User mode cross-platform interface for the local kernel-driver IOCTL transport + * @details Distinct from the serial transport (platform-serial), which talks to a remote + * debuggee. This interface is the LOCAL control channel: the userspace library + * drives the HyperDbg kernel driver through device I/O control codes. The command + * and packet layers are platform independent; only the device-control primitive + * underneath them is OS specific. Windows maps onto Win32 DeviceIoControl over a + * \\.\HyperDbgDebuggerDevice handle; Linux will map onto ioctl() against a + * /dev/HyperDbg character device exposed by the (future) kernel module (TODO). + * + * @version 0.20 + * @date 2026-06-09 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +#if defined(__linux__) +# include "../../../../include/SDK/HyperDbgSdk.h" +#endif // defined(__linux__) + +// +// SEND an I/O control code to the local kernel driver. Mirrors the Win32 +// DeviceIoControl signature so the call sites stay verbatim apart from the name. +// The Overlapped parameter is kept as a void * so the shared signature does not +// leak OVERLAPPED into the protocol code; synchronous callers pass NULL. +// Returns TRUE on success; on failure use PlatformGetLastError for the reason. +// +BOOL +PlatformDeviceIoControl(HANDLE Device, + DWORD IoControlCode, + LPVOID InBuffer, + DWORD InBufferSize, + LPVOID OutBuffer, + DWORD OutBufferSize, + LPDWORD BytesReturned, + LPVOID Overlapped); diff --git a/hyperdbg/include/platform/user/header/platform-lib-calls.h b/hyperdbg/include/platform/user/header/platform-lib-calls.h index a449758c..021d4a5b 100644 --- a/hyperdbg/include/platform/user/header/platform-lib-calls.h +++ b/hyperdbg/include/platform/user/header/platform-lib-calls.h @@ -35,3 +35,103 @@ PlatformStrDup(const char * Str); // VOID PlatformZeroMemory(PVOID Buffer, SIZE_T Size); + +// +// SPRINTF +// +INT +PlatformSprintf(char * Buffer, SIZE_T BufferSize, const char * Format, ...); + +// +// HIGH-RESOLUTION PERFORMANCE COUNTER +// +BOOLEAN +PlatformQueryPerformanceFrequency(LARGE_INTEGER * Frequency); + +BOOLEAN +PlatformQueryPerformanceCounter(LARGE_INTEGER * Count); + +// +// EVENT / SYNCHRONIZATION OBJECTS +// +HANDLE +PlatformCreateEvent(BOOLEAN ManualReset, BOOLEAN InitialState); + +BOOLEAN +PlatformSetEvent(HANDLE EventHandle); + +BOOLEAN +PlatformResetEvent(HANDLE EventHandle); + +DWORD +PlatformWaitForSingleObject(HANDLE Handle, DWORD TimeoutMilliseconds); + +BOOLEAN +PlatformCloseHandle(HANDLE Handle); + +// +// LAST OS ERROR +// +// TODO (linux, correctness): the wrappers below only unify the success/failure +// *boolean* convention. The actual error semantics still differ across platforms +// and must be reconciled later: +// - PlatformGetLastError returns raw Linux errno (EACCES=13, ...) which does NOT +// match the Win32 ERROR_* code space (ERROR_ACCESS_DENIED=5, ...). Code that +// merely logs/checks-nonzero is fine; code that compares against ERROR_* needs +// an errno -> ERROR_* mapping here. +// - Failure sentinels are not uniform across the Win32 calls we wrap: file opens +// fail with INVALID_HANDLE_VALUE (not NULL), most other handle calls fail with +// NULL. Callers must test the right one. +// For now: getting the project to compile is step 1; correctness comes next. +// +DWORD +PlatformGetLastError(VOID); + +// +// CONSOLE OUTPUT (raw bytes to stdout) +// +BOOLEAN +PlatformWriteConsole(const VOID * Buffer, DWORD NumberOfBytes); + +// +// FILE I/O +// +HANDLE +PlatformOpenFileForWriting(const WCHAR * Path); + +BOOLEAN +PlatformWriteFile(HANDLE FileHandle, const VOID * Buffer, DWORD NumberOfBytes); + +BOOLEAN +PlatformCloseFile(HANDLE FileHandle); + +// +// READ-ONLY FILE MAPPING +// +// PlatformMapFileReadOnly also hands back the still-open file handle in +// *OutFileHandle, so callers can issue supplementary raw reads via +// PlatformReadFileAtOffset; release everything with PlatformUnmapFile. +// +VOID * +PlatformMapFileReadOnly(const WCHAR * Path, PSIZE_T OutFileSize, PHANDLE OutFileHandle); + +BOOLEAN +PlatformReadFileAtOffset(HANDLE FileHandle, UINT64 Offset, VOID * Buffer, DWORD NumberOfBytes, LPDWORD BytesRead); + +VOID +PlatformUnmapFile(VOID * BaseAddress, SIZE_T FileSize, HANDLE FileHandle); + +// +// PROCESS / THREAD IDENTITY +// +UINT32 +PlatformGetCurrentThreadId(VOID); + +UINT32 +PlatformGetCurrentProcessorNumber(VOID); + +UINT32 +PlatformGetCurrentProcessId(VOID); + +CHAR * + PlatformGetCurrentProcessName(VOID); diff --git a/hyperdbg/include/platform/user/header/platform-serial.h b/hyperdbg/include/platform/user/header/platform-serial.h new file mode 100644 index 00000000..f33b48ec --- /dev/null +++ b/hyperdbg/include/platform/user/header/platform-serial.h @@ -0,0 +1,70 @@ +/** + * @file platform-serial.h + * @author Max Raulea (max.raulea@gmail.com) + * @brief User mode cross-platform interface for the kernel-debugger serial transport + * @details The kernel-debugging *protocol* in kd.cpp is platform independent; only + * the byte transport underneath it (serial COM port / named pipe) is OS + * specific. This interface isolates those primitives so the protocol layer + * can stay shared. Windows maps onto Win32 (CreateFile / Comm* / overlapped + * ReadFile/WriteFile); Linux will map onto termios over /dev/tty* (TODO). + * + * @version 0.20 + * @date 2026-06-08 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +#if defined(__linux__) +# include "../../../../include/SDK/HyperDbgSdk.h" +#endif // defined(__linux__) + +// +// Distinguishes the two I/O modes the transport uses. On Windows the debugger +// side opens the port for overlapped (async) I/O while the debuggee side uses +// blocking I/O; this enum lets the platform layer pick the right mechanism +// without leaking OVERLAPPED into the protocol code. +// +typedef enum _PLATFORM_SERIAL_IO_ROLE +{ + PLATFORM_SERIAL_IO_DEBUGGER, // overlapped / async reads + PLATFORM_SERIAL_IO_DEBUGGEE, // blocking reads (with comm timeout) + +} PLATFORM_SERIAL_IO_ROLE; + +// +// OPEN a serial COM port by name for the given role. +// Returns a transport handle, or NULL on failure. +// +HANDLE +PlatformSerialOpen(const char * PortName, PLATFORM_SERIAL_IO_ROLE Role); + +// +// CONFIGURE baud rate and the fixed 8-N-1 framing the protocol expects. +// +BOOLEAN +PlatformSerialConfigure(HANDLE Handle, DWORD BaudRate); + +// +// READ a single byte. *BytesRead is set to the number of bytes actually read. +// The caller (protocol layer) owns the packet-assembly loop. +// +BOOLEAN +PlatformSerialReadByte(HANDLE Handle, + CHAR * OutByte, + DWORD * BytesRead, + PLATFORM_SERIAL_IO_ROLE Role); + +// +// WRITE a buffer. Synchronous selects blocking write (debuggee/handshaking) +// versus overlapped write (debugger). +// +BOOLEAN +PlatformSerialWrite(HANDLE Handle, const void * Buffer, UINT32 Length, BOOLEAN Synchronous); + +// +// CLOSE the transport handle and release any associated OS resources. +// +BOOLEAN +PlatformSerialClose(HANDLE Handle); diff --git a/hyperdbg/include/platform/user/header/platform-signal.h b/hyperdbg/include/platform/user/header/platform-signal.h new file mode 100644 index 00000000..9a3f73db --- /dev/null +++ b/hyperdbg/include/platform/user/header/platform-signal.h @@ -0,0 +1,43 @@ +/** + * @file platform-signal.h + * @author Max Raulea (max.raulea@gmail.com) + * @brief User mode cross-platform interface for the console-control (CTRL+C / CTRL+BREAK) handler + * @details HyperDbg installs a single handler (BreakController) that pauses the + * debuggee when the user hits CTRL+C / CTRL+BREAK. The handler body is + * platform independent; only the OS mechanism that delivers the event + * and the thread context it runs in are OS specific. This interface + * isolates that registration. Windows maps onto SetConsoleCtrlHandler; + * Linux maps onto POSIX signals serviced by a dedicated sigwait() thread + * so the handler runs in ordinary thread context (matching Windows, which + * also dispatches the handler on its own thread). + * + * @version 0.20 + * @date 2026-06-16 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +#if defined(__linux__) +# include "../../../../include/SDK/HyperDbgSdk.h" +#endif // defined(__linux__) + +// +// Console-control handler signature. Matches the Win32 PHANDLER_ROUTINE shape +// (and BreakController) so the same function pointer is usable on both platforms. +// CtrlType is one of the CTRL_*_EVENT codes. +// +typedef BOOL (*PLATFORM_CTRL_HANDLER)(DWORD CtrlType); + +// +// INSTALL the process console-control handler. +// Windows: registers Handler via SetConsoleCtrlHandler. +// Linux: blocks the relevant signals process-wide and starts a dedicated +// sigwait() thread that translates them into CTRL_*_EVENT codes and +// invokes Handler. Must be called once, before other threads spawn, +// so the blocked-signal mask is inherited by every thread. +// Returns TRUE on success, FALSE on failure. +// +BOOLEAN +PlatformInstallCtrlHandler(PLATFORM_CTRL_HANDLER Handler); diff --git a/hyperdbg/kdserial/kdserial.vcxproj b/hyperdbg/kdserial/kdserial.vcxproj index 5d67a92b..22ed068a 100644 --- a/hyperdbg/kdserial/kdserial.vcxproj +++ b/hyperdbg/kdserial/kdserial.vcxproj @@ -1,5 +1,8 @@  + + + debug @@ -31,6 +34,7 @@ + @@ -45,7 +49,7 @@ v4.5 14.0 kdserial - Universal + Desktop 10.0.10135.0 $(LatestTargetPlatformVersion) @@ -77,6 +81,7 @@ false $(SolutionDir)build\bin\$(Configuration)\ $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + false kdserial @@ -84,6 +89,7 @@ false $(SolutionDir)build\bin\$(Configuration)\ $(SolutionDir)build\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + false false @@ -138,5 +144,15 @@ + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/hyperdbg/kdserial/packages.config b/hyperdbg/kdserial/packages.config new file mode 100644 index 00000000..eb276766 --- /dev/null +++ b/hyperdbg/kdserial/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/hyperdbg/libhyperdbg/CMakeLists.txt b/hyperdbg/libhyperdbg/CMakeLists.txt index 91d5ce95..fac7603f 100644 --- a/hyperdbg/libhyperdbg/CMakeLists.txt +++ b/hyperdbg/libhyperdbg/CMakeLists.txt @@ -1,6 +1,6 @@ # Code generated by Visual Studio kit, DO NOT EDIT. set(SourceFiles - "../include/platform/user/header/Environment.h" + "../include/platform/general/header/Environment.h" "../include/platform/user/header/Windows.h" "header/assembler.h" "header/commands.h" @@ -27,6 +27,11 @@ set(SourceFiles "header/transparency.h" "header/ud.h" "pch.h" + "../include/platform/user/code/platform-intrinsics.c" + "../include/platform/user/code/platform-lib-calls.c" + "../include/platform/user/code/platform-serial.c" + "../include/platform/user/code/platform-ioctl.c" + "../include/platform/user/code/platform-signal.c" "../script-eval/code/Functions.c" "../script-eval/code/Keywords.c" "../script-eval/code/PseudoRegisters.c" @@ -166,4 +171,23 @@ include_directories( "../script-eval" "../dependencies/keystone/include" ) +set_source_files_properties( + "../include/platform/user/code/platform-intrinsics.c" + "../include/platform/user/code/platform-lib-calls.c" + "../include/platform/user/code/platform-serial.c" + "../include/platform/user/code/platform-ioctl.c" + "../include/platform/user/code/platform-signal.c" + "../script-eval/code/Functions.c" + "../script-eval/code/Keywords.c" + "../script-eval/code/PseudoRegisters.c" + "../script-eval/code/Regs.c" + "../script-eval/code/ScriptEngineEval.c" + PROPERTIES LANGUAGE CXX +) + +if(UNIX) + list(REMOVE_ITEM SourceFiles "code/debugger/script-engine/symbol.cpp") + list(APPEND SourceFiles "code/debugger/script-engine/symbol-linux.cpp") +endif() + add_library(libhyperdbg SHARED ${SourceFiles}) diff --git a/hyperdbg/libhyperdbg/code/app/libhyperdbg.cpp b/hyperdbg/libhyperdbg/code/app/libhyperdbg.cpp index a76945ce..5a2c812a 100644 --- a/hyperdbg/libhyperdbg/code/app/libhyperdbg.cpp +++ b/hyperdbg/libhyperdbg/code/app/libhyperdbg.cpp @@ -595,6 +595,29 @@ HyperDbgUnloadKd() return 0; } +/** + * @brief check if any module is loaded (KD, VMM, HyperTrace, etc.) + * + * @return BOOLEAN return TRUE if any module is loaded, otherwise return FALSE + */ +BOOLEAN +HyperDbgIsAnyModuleLoaded() +{ + INT RetVal = 0; + + // + // Check if any module is loaded (KD, VMM, HyperTrace, etc.) + // + if (g_IsKdModuleLoaded || g_IsVmmModuleLoaded || g_IsHyperTraceModuleLoaded) + { + return TRUE; + } + else + { + return FALSE; + } +} + /** * @brief unload all modules (KD, VMM, HyperTrace, etc.) * @@ -654,6 +677,18 @@ HyperDbgLoadKdModule() return 0; } + // + // Enable Debug privilege to the current token + // + if (!WindowsSetDebugPrivilege()) + { + ShowMessages("err, couldn't set debug privilege\n"); + return 1; + } + + // + // Create handle from the KD module + // if (HyperDbgCreateHandleFromKdModule() == 1) { // @@ -740,15 +775,6 @@ HyperDbgLoadVmmModule() return 1; } - // - // Enable Debug privilege to the current token - // - if (!WindowsSetDebugPrivilege()) - { - ShowMessages("err, couldn't set debug privilege\n"); - return 1; - } - // // Check if the processor is a genuine Intel processor (required for VT-x) // diff --git a/hyperdbg/libhyperdbg/code/app/messaging.cpp b/hyperdbg/libhyperdbg/code/app/messaging.cpp index c8d903f3..ca0f7264 100644 --- a/hyperdbg/libhyperdbg/code/app/messaging.cpp +++ b/hyperdbg/libhyperdbg/code/app/messaging.cpp @@ -103,7 +103,7 @@ ShowMessages(const CHAR * Fmt, ...) va_start(ArgList, Fmt); - INT SprintfResult = vsprintf_s(TempMessage, Fmt, ArgList); + INT SprintfResult = PlatformVsnprintf(TempMessage, sizeof(TempMessage), Fmt, ArgList); va_end(ArgList); diff --git a/hyperdbg/libhyperdbg/code/common/list.cpp b/hyperdbg/libhyperdbg/code/common/list.cpp deleted file mode 100644 index 58c2fc44..00000000 --- a/hyperdbg/libhyperdbg/code/common/list.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @file list.cpp - * @author Sina Karvandi (sina@hyperdbg.org) - * @brief The list working functions headers - * @details - * @version 0.1 - * @date 2020-04-11 - * - * @copyright This project is released under the GNU Public License v3. - * - */ -#include "pch.h" -// -///** -// * @brief List initializer -// * -// * @param ListHead -// */ -// void -// InitializeListHead(PLIST_ENTRY ListHead) -//{ -// ListHead->Flink = ListHead->Blink = ListHead; -//} -// -///** -// * @brief insert entry to the top of the list -// * -// * @param ListHead -// * @param Entry -// */ -// void -// InsertHeadList(PLIST_ENTRY ListHead, PLIST_ENTRY Entry) -//{ -// PLIST_ENTRY Flink; -// -// Flink = ListHead->Flink; -// Entry->Flink = Flink; -// Entry->Blink = ListHead; -// Flink->Blink = Entry; -// ListHead->Flink = Entry; -//} -// -///** -// * @brief remove the entry from the list -// * -// * @param Entry -// * @return BOOLEAN -// */ -// BOOLEAN -// RemoveEntryList(PLIST_ENTRY Entry) -//{ -// PLIST_ENTRY PrevEntry; -// PLIST_ENTRY NextEntry; -// -// NextEntry = Entry->Flink; -// PrevEntry = Entry->Blink; -// if ((NextEntry->Blink != Entry) || (PrevEntry->Flink != Entry)) -// { -// // -// // Error -// // -// _CrtDbgBreak(); -// } -// -// PrevEntry->Flink = NextEntry; -// NextEntry->Blink = PrevEntry; -// return (BOOLEAN)(PrevEntry == NextEntry); -//} diff --git a/hyperdbg/libhyperdbg/code/common/spinlock.cpp b/hyperdbg/libhyperdbg/code/common/spinlock.cpp index 39ebb82c..96cc5e0c 100644 --- a/hyperdbg/libhyperdbg/code/common/spinlock.cpp +++ b/hyperdbg/libhyperdbg/code/common/spinlock.cpp @@ -40,7 +40,7 @@ static UINT32 MaxWait = 65536; BOOLEAN SpinlockTryLock(volatile LONG * Lock) { - return (!(*Lock) && !_interlockedbittestandset(Lock, 0)); + return (!(*Lock) && !CpuInterlockedBitTestAndSet(Lock, 0)); } /** diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/a.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/a.cpp index 05f2c882..c7aca424 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/a.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/a.cpp @@ -275,7 +275,7 @@ CommandAssemble(vector CommandTokens, string Command) if (ProcId == 0) { - ProcId = GetCurrentProcessId(); + ProcId = PlatformGetCurrentProcessId(); } if (Address) // was the user only trying to get the bytes? diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/dt-struct.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/dt-struct.cpp index 6233e00a..c3c3db68 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/dt-struct.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/dt-struct.cpp @@ -353,7 +353,7 @@ CommandDtShowDataBasedOnSymbolTypes( // // Use the current process for the pid // - TargetPid = GetCurrentProcessId(); + TargetPid = PlatformGetCurrentProcessId(); } } diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/preactivate.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/preactivate.cpp index a64f908d..ee88936d 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/preactivate.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/preactivate.cpp @@ -50,7 +50,7 @@ CommandPreactivate(vector CommandTokens, string Command) { BOOL Status; ULONG ReturnedLength; - DEBUGGER_PREACTIVATE_COMMAND PreactivateRequest = {0}; + DEBUGGER_PREACTIVATE_COMMAND PreactivateRequest = {}; if (CommandTokens.size() != 2) { @@ -82,7 +82,7 @@ CommandPreactivate(vector CommandTokens, string Command) // // Send IOCTL // - Status = DeviceIoControl( + Status = PlatformDeviceIoControl( g_DeviceHandle, // Handle to device IOCTL_PREACTIVATE_FUNCTIONALITY, // IO Control Code (IOCTL) &PreactivateRequest, // Input Buffer to driver. @@ -96,7 +96,7 @@ CommandPreactivate(vector CommandTokens, string Command) if (!Status) { - ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); + ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError()); return; } diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/prealloc.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/prealloc.cpp index 9015c420..0bf9545b 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/prealloc.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/prealloc.cpp @@ -62,7 +62,7 @@ CommandPrealloc(vector CommandTokens, string Command) BOOL Status; ULONG ReturnedLength; UINT64 Count; - DEBUGGER_PREALLOC_COMMAND PreallocRequest = {0}; + DEBUGGER_PREALLOC_COMMAND PreallocRequest = {}; string SecondParam; if (CommandTokens.size() != 3) @@ -143,7 +143,7 @@ CommandPrealloc(vector CommandTokens, string Command) // // Send IOCTL // - Status = DeviceIoControl( + Status = PlatformDeviceIoControl( g_DeviceHandle, // Handle to device IOCTL_RESERVE_PRE_ALLOCATED_POOLS, // IO Control Code (IOCTL) &PreallocRequest, // Input Buffer to driver. @@ -157,7 +157,7 @@ CommandPrealloc(vector CommandTokens, string Command) if (!Status) { - ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); + ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError()); return; } diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/unload.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/unload.cpp index 45726689..0738fc1b 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/unload.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/debugging-commands/unload.cpp @@ -167,6 +167,15 @@ CommandUnload(vector CommandTokens, string Command) return; } + // + // Check if any module is loaded + // + if (!HyperDbgIsAnyModuleLoaded()) + { + ShowMessages("no module is loaded\n"); + return; + } + ShowMessages("unloading all modules\n"); // diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp index b670dd24..09a96caf 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/pt.cpp @@ -1,5 +1,6 @@ /** * @file pt.cpp + * @author Masoud Rahimi Jafari (Masoodrahimy1379@gmail.com) * @author Sina Karvandi (sina@hyperdbg.org) * @brief !pt command * @details @@ -25,39 +26,59 @@ extern BOOLEAN g_IsSerialConnectedToRemoteDebuggee; VOID CommandPtHelp() { - ShowMessages("!pt : enables, disables and configures Intel Processor Trace (PT).\n"); + ShowMessages("!pt : enables, disables and configures Intel Processor Trace (PT).\n\n"); - ShowMessages("syntax : \t!pt [Function (string)]\n"); - ShowMessages("syntax : \t!pt filter [user] [kernel] [cr3 ] [buffer ]\n"); - ShowMessages("\t [range ] [stoprange ]\n"); + ShowMessages("syntax : \t!pt enable\n"); + ShowMessages("syntax : \t!pt enable [size BufferSize (hex)]\n"); + ShowMessages("syntax : \t!pt enable [pid ProcessId (hex)] [size BufferSize (hex)] [core CoreId (hex)]\n"); + ShowMessages("syntax : \t!pt enable [tid ThreadId (hex)] [size BufferSize (hex)] [core CoreId (hex)]\n"); + ShowMessages("syntax : \t!pt enable [pname ProcessName (string)] [size BufferSize (hex)] [core CoreId (hex)]\n"); + ShowMessages("syntax : \t!pt enable [path Path (string)] [size BufferSize (hex)] [core CoreId (hex)]\n"); + ShowMessages("syntax : \t!pt enable [cr3 Cr3Value (hex)] [size BufferSize (hex)]\n"); + ShowMessages("syntax : \t!pt disable\n"); + ShowMessages("syntax : \t!pt pause\n"); + ShowMessages("syntax : \t!pt resume\n"); + ShowMessages("syntax : \t!pt flush\n"); + ShowMessages("syntax : \t!pt dump print [type TypeOfDump (string)]\n"); + ShowMessages("syntax : \t!pt dump path [type TypeOfDump (string)]\n"); + ShowMessages("syntax : \t!pt filter [Mode (string)]\n"); + ShowMessages("syntax : \t!pt filter [range1 FromAddress (hex) ToAddress (hex)] [range2 FromAddress (hex) ToAddress (hex)] [range3 FromAddress (hex) ToAddress (hex)] [range4 FromAddress (hex) ToAddress (hex)]\n"); + ShowMessages("syntax : \t!pt filter [range1 module ModuleName (string)] [range2 module ModuleName (string)] [range3 module ModuleName (string)] [range4 module ModuleName (string)]\n"); + ShowMessages("syntax : \t!pt filter [stoprange1 FromAddress (hex) ToAddress (hex)] [stoprange2 FromAddress (hex) ToAddress (hex)] [stoprange3 FromAddress (hex) ToAddress (hex)] [stoprange4 FromAddress (hex) ToAddress (hex)]\n"); + ShowMessages("syntax : \t!pt filter [stoprange1 module ModuleName (string)] [stoprange2 module ModuleName (string)] [stoprange3 module ModuleName (string)] [stoprange4 module ModuleName (string)]\n"); + ShowMessages("syntax : \t!pt packet [PacketType (string)]\n"); ShowMessages("\n"); ShowMessages("\t\te.g : !pt enable\n"); + ShowMessages("\t\te.g : !pt enable size 0x200000\n"); + ShowMessages("\t\te.g : !pt enable pid 0x4a8\n"); + ShowMessages("\t\te.g : !pt enable pname notepad.exe\n"); + ShowMessages("\t\te.g : !pt enable tid 0x1234 core 3\n"); + ShowMessages("\t\te.g : !pt enable cr3 0x1aabb000\n"); + ShowMessages("\t\te.g : !pt enable pid 0x4a8 size 0x200000\n"); + ShowMessages("\t\te.g : !pt enable path \"c:\\programs\\my exe file.exe\" size 0x200000 core 3\n"); ShowMessages("\t\te.g : !pt disable\n"); ShowMessages("\t\te.g : !pt pause\n"); ShowMessages("\t\te.g : !pt resume\n"); - ShowMessages("\t\te.g : !pt size\n"); - ShowMessages("\t\te.g : !pt dump\n"); ShowMessages("\t\te.g : !pt flush\n"); + ShowMessages("\t\te.g : !pt dump print type instruction\n"); + ShowMessages("\t\te.g : !pt dump print type packet\n"); + ShowMessages("\t\te.g : !pt dump path C:\\trace.txt type instruction\n"); + ShowMessages("\t\te.g : !pt filter user\n"); + ShowMessages("\t\te.g : !pt filter user kernel\n"); + ShowMessages("\t\te.g : !pt filter range1 0x140001000 0x140002000\n"); + ShowMessages("\t\te.g : !pt filter range1 module main\n"); + ShowMessages("\t\te.g : !pt filter range1 module ntdll range2 module nt\n"); + ShowMessages("\t\te.g : !pt filter stoprange1 0x140003000 0x140004000\n"); + ShowMessages("\t\te.g : !pt packet psb pip tsc\n"); ShowMessages("\n"); - ShowMessages("\t\te.g : !pt filter user\n"); - ShowMessages("\t\te.g : !pt filter kernel\n"); - ShowMessages("\t\te.g : !pt filter user kernel\n"); - ShowMessages("\t\te.g : !pt filter user cr3 0x1aabb000\n"); - ShowMessages("\t\te.g : !pt filter user buffer 0x100000\n"); - ShowMessages("\t\te.g : !pt filter user range 0x140001000 0x140002000\n"); - ShowMessages("\t\te.g : !pt filter user stoprange 0x140003000 0x140004000\n"); - - ShowMessages("\nlist of filter options: \n"); - ShowMessages("\t user : trace CPL > 0\n"); - ShowMessages("\t kernel : trace CPL == 0\n"); - ShowMessages("\t cr3 : only trace when CR3 matches (0 = no filter)\n"); - ShowMessages("\t buffer : per-CPU output buffer size, must be 4KB * 2^N\n"); - ShowMessages("\t (4KB, 8KB, ... up to 128MB; default 2MB)\n"); - ShowMessages("\t range : keep trace inside [start..end] (up to 4 ranges)\n"); - ShowMessages("\t stoprange : stop tracing when execution enters [s..e]\n"); - ShowMessages("\t (no option) : trace user + kernel, no CR3 / IP filter (default)\n"); + ShowMessages("Where:\n"); + ShowMessages("\t[Mode (string)] could be 'kernel' or/and 'user'\n"); + ShowMessages("\t[TypeOfDump (string)] could be 'instruction' or 'packet'\n"); + ShowMessages("\t[ModuleName (string)] could be 'main' (the main module of the process), 'nt', 'win32k', or any other module name\n"); + ShowMessages("\t[PacketType (string)] could be either or a combination of 'psb', 'pip', 'tsc', 'mtc', 'cyc', 'tnt', 'tip', 'fup', or 'mode'\n"); + ShowMessages("\n"); } /** @@ -137,127 +158,1334 @@ HyperDbgPerformPtOperation(HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) } /** - * @brief Parse a `!pt filter ...` clause into a HYPERTRACE_PT_OPERATION_PACKETS. + * @brief Send pt enable command * - * Returns TRUE on success, FALSE if the syntax was bad (caller - * should print help and bail). + * @return BOOLEAN */ static BOOLEAN -CommandPtParseFilterOptions(vector & CommandTokens, - HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) +CommandPtSendEnable() { - BOOLEAN AnyMode = FALSE; + HYPERTRACE_PT_OPERATION_PACKETS PtRequest = {}; - PtRequest->TraceUser = 0; - PtRequest->TraceKernel = 0; - PtRequest->TargetCr3 = 0; - PtRequest->BufferSize = 0; - PtRequest->NumAddrRanges = 0; + // + // Set the PtRequest structure for the operation + // + PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE; - for (size_t i = 2; i < CommandTokens.size(); i++) + // + // Send the request to perform the operation + // + if (!HyperDbgPerformPtOperation(&PtRequest)) { - if (CompareLowerCaseStrings(CommandTokens.at(i), "user")) + return FALSE; + } + else + { + return TRUE; + } +} + +/** + * @brief Send pt disable command + * + * @return BOOLEAN + */ +static BOOLEAN +CommandPtSendDisable() +{ + HYPERTRACE_PT_OPERATION_PACKETS PtRequest = {}; + + // + // Set the PtRequest structure for the operation + // + PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE; + + // + // Send the request to perform the operation + // + if (!HyperDbgPerformPtOperation(&PtRequest)) + { + return FALSE; + } + else + { + return TRUE; + } +} + +/** + * @brief Send pt pause command + * + * @return BOOLEAN + */ +static BOOLEAN +CommandPtSendPause() +{ + HYPERTRACE_PT_OPERATION_PACKETS PtRequest = {}; + + // + // Set the PtRequest structure for the operation + // + PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PAUSE; + + // + // Send the request to perform the operation + // + if (!HyperDbgPerformPtOperation(&PtRequest)) + { + return FALSE; + } + else + { + return TRUE; + } +} + +/** + * @brief Send pt resume command + * + * @return BOOLEAN + */ +static BOOLEAN +CommandPtSendResume() +{ + HYPERTRACE_PT_OPERATION_PACKETS PtRequest = {}; + + // + // Set the PtRequest structure for the operation + // + PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_RESUME; + + // + // Send the request to perform the operation + // + if (!HyperDbgPerformPtOperation(&PtRequest)) + { + return FALSE; + } + else + { + return TRUE; + } +} + +/** + * @brief Send pt flush command + * + * @return BOOLEAN + */ +static BOOLEAN +CommandPtSendFlush() +{ + HYPERTRACE_PT_OPERATION_PACKETS PtRequest = {}; + + // + // Set the PtRequest structure for the operation + // + PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FLUSH; + + // + // Send the request to perform the operation + // + if (!HyperDbgPerformPtOperation(&PtRequest)) + { + return FALSE; + } + else + { + return TRUE; + } +} + +/** + * @brief Send pt filter command + * + * @return BOOLEAN + */ +static BOOLEAN +CommandPtSendFilterByPid(UINT32 ProcessId, + BOOLEAN IsUserMode, + BOOLEAN IsKernelMode, + UINT64 StartRange1, + UINT64 EndRange1, + UINT64 StartRange2, + UINT64 EndRange2, + UINT64 StartRange3, + UINT64 EndRange3, + UINT64 StartRange4, + UINT64 EndRange4) +{ + HYPERTRACE_PT_OPERATION_PACKETS Op = {}; + UINT8 NumberOfRanges = 0; + + // + // Set the Op structure for the operation + // + Op.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER; + + // + // Set execution modes + // + Op.FilterOptions.TraceUser = IsUserMode ? 1 : 0; + Op.FilterOptions.TraceKernel = IsKernelMode ? 1 : 0; + + // + // Set the process ID + // + Op.EnableOptions.Pid = ProcessId; + + // + // Set the first range if provided + // + if (StartRange1 != NULL && EndRange1 != NULL) + { + Op.FilterOptions.AddrRanges[NumberOfRanges].Start = StartRange1; + Op.FilterOptions.AddrRanges[NumberOfRanges].End = EndRange1; + NumberOfRanges++; + } + + // + // Set the second range if provided + // + if (StartRange2 != NULL && EndRange2 != NULL) + { + Op.FilterOptions.AddrRanges[NumberOfRanges].Start = StartRange2; + Op.FilterOptions.AddrRanges[NumberOfRanges].End = EndRange2; + NumberOfRanges++; + } + + // + // Set the third range if provided + // + if (StartRange3 != NULL && EndRange3 != NULL) + { + Op.FilterOptions.AddrRanges[NumberOfRanges].Start = StartRange3; + Op.FilterOptions.AddrRanges[NumberOfRanges].End = EndRange3; + NumberOfRanges++; + } + + // + // Set the fourth range if provided + // + if (StartRange4 != NULL && EndRange4 != NULL) + { + Op.FilterOptions.AddrRanges[NumberOfRanges].Start = StartRange4; + Op.FilterOptions.AddrRanges[NumberOfRanges].End = EndRange4; + NumberOfRanges++; + } + + // + // Set number of ranges + // + Op.FilterOptions.NumAddrRanges = NumberOfRanges; + + // + // Send the request to perform the operation + // + if (!HyperDbgPerformPtOperation(&Op)) + { + return FALSE; + } + else + { + ShowMessages("[+] PT Filter: cr3=0x%llx, pid=0x%x, user=%x, kernel=%x, ranges=%x\n", + Op.EnableOptions.Cr3, + Op.EnableOptions.Pid, + Op.FilterOptions.TraceUser, + Op.FilterOptions.TraceKernel, + Op.FilterOptions.NumAddrRanges); + + return TRUE; + } + + return TRUE; +} + +/** + * @brief Shared core: enable PT, wait for the target, decode and disable + * + * @details Called by the three specialised helpers after they have filled + * Process with a valid hProcess (and optionally hThread). + * Owns and closes all handles in Process before returning. + * + * @param Process Pointer to a PROCESS_INFORMATION whose hProcess (and + * optionally hThread) have already been opened/created + * @param Path Executable path used for symbol resolution, or NULL + * @param Function Symbol name to narrow the IP filter, or NULL + * @param Packets TRUE → decode raw packets; FALSE → decode instructions + * @param PinCore ≥ 0 → pin the target to that logical core; < 0 → free + * @param LaunchedNew TRUE → process was launched suspended here (will be + * resumed, and terminated on error); FALSE → the + * process was already running (never terminated) + * + * @return VOID + */ +static VOID +CommandPtRunAndTraceCore(PROCESS_INFORMATION * Process, + const CHAR * Path, + const CHAR * Function, + BOOLEAN Packets, + int PinCore, + BOOLEAN LaunchedNew) +{ + DWORD_PTR Mask; + HYPERTRACE_PT_MMAP_PACKETS Mmap = {}; + HYPERTRACE_PT_OPERATION_PACKETS Sizes = {}; + IMAGE_SYMBOL_CONTEXT Ctx = {}; + UINT64 TextStart = 0; + UINT64 TextEnd = 0; + UINT64 FilterStart = 0; + UINT64 FilterEnd = 0; + UINT64 Total = 0; + + if (PinCore < 0) + { + PinCore = PT_DEFAULT_PINNING_CORE; + } + + Mask = (DWORD_PTR)1 << PinCore; + + // + // Set process affinity to the specified core so that all PT trace will land on that core + // + if (SetProcessAffinityMask(Process->hProcess, Mask)) + { + ShowMessages("[+] pinned target to core %d (all trace should land on this core)\n", PinCore); + } + else + { + ShowMessages("[!] could not pin to core %d (error 0x%x); running unpinned\n", PinCore, GetLastError()); + } + + // + // Capture the .text section of the target process image for symbol resolution + // + if (!PtHelperCaptureImage(Process->hProcess, &TextStart, &TextEnd, &Ctx)) + { + ShowMessages("[-] cannot read target image / .text section\n"); + + if (LaunchedNew) + TerminateProcess(Process->hProcess, 1); + + goto Cleanup; + } + + ShowMessages("[+] image base 0x%llx, .text 0x%llx-0x%llx (%llu bytes)\n", + (UINT64)Ctx.ImageBase, + (UINT64)TextStart, + (UINT64)TextEnd, + (UINT64)Ctx.CodeSize); + + FilterStart = TextStart; + FilterEnd = TextEnd; + + // + // Narrow the IP filter to the specified function if possible + // + if (Function != NULL && Path != NULL && + PtHelperResolveFunction(Process->hProcess, Path, Function, Ctx.ImageBase, &FilterStart, &FilterEnd)) + { + ShowMessages("[+] IP filter narrowed to '%s' 0x%llx-0x%llx (%llu bytes)\n", + Function, + (UINT64)FilterStart, + (UINT64)FilterEnd, + (UINT64)(FilterEnd - FilterStart + 1)); + } + else + { + ShowMessages("[!] IP filter: whole .text (symbol '%s' not found or not applicable)\n", + Function ? Function : "(none)"); + } + + // + // Enable PT with the specified filter and wait for the target process to exit + // + if (!CommandPtSendFilterByPid(Process->dwProcessId, TRUE, FALSE, FilterStart, FilterEnd, NULL, NULL, NULL, NULL, NULL, NULL) || + !CommandPtSendEnable()) + { + ShowMessages("[-] cannot enable Intel PT\n"); + + if (LaunchedNew) { - PtRequest->TraceUser = 1; - AnyMode = TRUE; + TerminateProcess(Process->hProcess, 1); } - else if (CompareLowerCaseStrings(CommandTokens.at(i), "kernel")) + + goto Cleanup; + } + + // + // Request the PT buffers to be mapped into user space so that we can decode them after the target exits + // + if (!HyperDbgPtMmapSendRequest(&Mmap)) + { + ShowMessages("[-] MMAP failed\n"); + CommandPtSendDisable(); + if (LaunchedNew) + TerminateProcess(Process->hProcess, 1); + goto Cleanup; + } + + ShowMessages("[+] PT enabled, %u per-core buffers mapped\n", Mmap.NumCpus); + + if (LaunchedNew) + { + ShowMessages("[*] resuming target and waiting for it to exit...\n"); + ResumeThread(Process->hThread); + } + else + { + ShowMessages("[*] waiting for target process to exit...\n"); + } + + // + // Wait for the target process to exit before decoding the trace + // + WaitForSingleObject(Process->hProcess, INFINITE); + + ShowMessages("[+] target exited, decoding trace\n"); + + // + // Pause PT so that the buffers are not being written to while we decode them + // + CommandPtSendPause(); + + Sizes.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_SIZE; + + // + // Request the sizes of the PT buffers for each core so that we know how much to decode + // + if (!CommandPtSendRequest(&Sizes)) + { + ShowMessages("[-] cannot query PT sizes\n"); + CommandPtSendDisable(); + goto Cleanup; + } + + // + // Decode the trace for each core that has a non-zero buffer size + // + for (UINT32 i = 0; i < Mmap.NumCpus; i++) + { + UINT32 Cpu = Mmap.Cpus[i].CpuId; + UINT64 Bytes = (Cpu < Sizes.NumCpus) ? Sizes.BytesPerCpu[Cpu] : 0; + + if (Bytes == 0) + continue; + + if (Bytes > Mmap.Cpus[i].Size) + Bytes = Mmap.Cpus[i].Size; + + ShowMessages("\n[*] core %u: %llu bytes of trace\n", Cpu, (UINT64)Bytes); + Total += Packets + ? PtHelperDecodeCorePackets(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes, Ctx.ImageBase) + : PtHelperDecodeCore(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes, &Ctx); + } + + ShowMessages("\n[+] decoded %llu %s total\n", (UINT64)Total, Packets ? "packet(s)" : "instruction(s)"); + + // + // Disable PT now that we are done decoding the trace + // + CommandPtSendDisable(); + +Cleanup: + + if (Ctx.Code != NULL) + { + free(Ctx.Code); + Ctx.Code = NULL; + } + + if (Process->hThread != NULL) + CloseHandle(Process->hThread); + if (Process->hProcess != NULL) + CloseHandle(Process->hProcess); +} + +/** + * @brief Launch an executable at Path as a suspended process, enable PT, and + * decode the trace after the process exits + * + * @param Path Full path to the executable to launch + * @param Function Symbol name to narrow the IP filter, or NULL for whole .text + * @param Packets TRUE → decode raw packets; FALSE → decode instructions + * @param PinCore ≥ 0 → pin to that logical core; < 0 → unpinned + * + * @return VOID + */ +static VOID +CommandPtRunAndTraceByPath(const CHAR * Path, const CHAR * Function, BOOLEAN Packets, int PinCore) +{ + STARTUPINFOA Startup = {}; + PROCESS_INFORMATION Process = {}; + + Startup.cb = sizeof(Startup); + + if (!CreateProcessA(Path, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &Startup, &Process)) + { + ShowMessages("[-] cannot launch '%s' (error 0x%x)\n", Path, GetLastError()); + return; + } + + ShowMessages("[+] launched '%s' (pid %u, suspended)\n", Path, Process.dwProcessId); + + CommandPtRunAndTraceCore(&Process, Path, Function, Packets, PinCore, TRUE); +} + +/** + * @brief Open an existing process by PID, enable PT, and decode the trace + * after the process exits + * + * @param ProcessId PID of the target process + * @param Function Symbol name to narrow the IP filter, or NULL for whole .text + * @param Packets TRUE → decode raw packets; FALSE → decode instructions + * @param PinCore ≥ 0 → pin to that logical core; < 0 → unpinned + * + * @return VOID + */ +static VOID +CommandPtRunAndTraceByPid(UINT32 ProcessId, const CHAR * Function, BOOLEAN Packets, int PinCore) +{ + PROCESS_INFORMATION Process = {}; + + Process.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)ProcessId); + + if (Process.hProcess == NULL) + { + ShowMessages("[-] cannot open process 0x%x (error 0x%x)\n", ProcessId, GetLastError()); + return; + } + + Process.dwProcessId = ProcessId; + + ShowMessages("[+] attached to pid 0x%x\n", ProcessId); + + CommandPtRunAndTraceCore(&Process, NULL, Function, Packets, PinCore, FALSE); +} + +/** + * @brief Open an existing thread by TID, derive its owning process, enable PT, + * and decode the trace after the process exits + * + * @param ThreadId TID of the target thread + * @param Function Symbol name to narrow the IP filter, or NULL for whole .text + * @param Packets TRUE → decode raw packets; FALSE → decode instructions + * @param PinCore ≥ 0 → pin to that logical core; < 0 → unpinned + * + * @return VOID + */ +static VOID +CommandPtRunAndTraceByTid(UINT32 ThreadId, const CHAR * Function, BOOLEAN Packets, int PinCore) +{ + PROCESS_INFORMATION Process = {}; + HANDLE ThreadHandle = NULL; + DWORD OwningPid = 0; + THREAD_BASIC_INFO_EX Tbi = {0}; + ULONG RetTid = 0; + HMODULE NtdllTid = GetModuleHandleA("ntdll.dll"); + PFN_NT_QIT NtQit = NtdllTid ? (PFN_NT_QIT)GetProcAddress(NtdllTid, "NtQueryInformationThread") : NULL; + + ThreadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD)ThreadId); + + if (ThreadHandle == NULL) + { + ShowMessages("[-] cannot open thread 0x%x (error 0x%x)\n", ThreadId, GetLastError()); + return; + } + + if (NtQit == NULL || NtQit(ThreadHandle, 0, &Tbi, sizeof(Tbi), &RetTid) < 0 || Tbi.ClientId.UniqueProcess == NULL) + { + ShowMessages("[-] cannot get owning PID for thread 0x%x\n", ThreadId); + CloseHandle(ThreadHandle); + return; + } + + OwningPid = (DWORD)(ULONG_PTR)Tbi.ClientId.UniqueProcess; + + if (OwningPid == 0) + { + ShowMessages("[-] cannot get owning PID for thread 0x%x\n", ThreadId); + CloseHandle(ThreadHandle); + return; + } + + Process.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, OwningPid); + + if (Process.hProcess == NULL) + { + ShowMessages("[-] cannot open process %u (error 0x%x)\n", OwningPid, GetLastError()); + CloseHandle(ThreadHandle); + return; + } + + Process.hThread = ThreadHandle; + Process.dwProcessId = OwningPid; + Process.dwThreadId = ThreadId; + + ShowMessages("[+] attached to tid 0x%x (owning pid %u)\n", ThreadId, OwningPid); + + CommandPtRunAndTraceCore(&Process, NULL, Function, Packets, PinCore, FALSE); +} + +/** + * @brief Wrapper: dispatch to the appropriate trace helper based on whichever + * selector (Path, ProcessId, ThreadId) is provided + * + * @param Path Executable path, or NULL + * @param Function Symbol name for IP filter, or NULL + * @param Packets TRUE → raw packets; FALSE → instructions + * @param PinCore Logical core to pin the target to, or < 0 for unpinned + * @param ProcessId PID of an existing process, or 0 + * @param ThreadId TID of an existing thread, or 0 + * + * @return VOID + */ +static VOID +CommandPtRunAndTrace(const CHAR * Path, const CHAR * Function, BOOLEAN Packets, int PinCore, UINT32 ProcessId, UINT32 ThreadId) +{ + if (Path != NULL) + CommandPtRunAndTraceByPath(Path, Function, Packets, PinCore); + else if (ThreadId != 0) + CommandPtRunAndTraceByTid(ThreadId, Function, Packets, PinCore); + else if (ProcessId != 0) + CommandPtRunAndTraceByPid(ProcessId, Function, Packets, PinCore); + else + ShowMessages("[-] no path, PID, or TID specified\n"); +} + +/** + * @brief Map the per-CPU PT output buffers into the current process + * + * @details On success MmapRequest->Cpus[0..NumCpus) hold one { UserVa, Size } + * per CPU, valid in this process until PT is disabled / flushed. + * Only meaningful in local (VMI) mode. + * + * @param MmapRequest + * + * @return BOOLEAN + */ +BOOLEAN +HyperDbgPtMmapSendRequest(HYPERTRACE_PT_MMAP_PACKETS * MmapRequest) +{ + BOOL Status; + ULONG ReturnedLength; + + if (g_IsSerialConnectedToRemoteDebuggee) + { + // + // The mmap surface maps into the caller's address space, which only + // makes sense in local mode (no remote-debuggee transport for it). + // + ShowMessages("err, PT mmap is only available in local (VMI) mode\n"); + return FALSE; + } + + AssertShowMessageReturnStmt(g_IsHyperTraceModuleLoaded, g_DeviceHandle, ASSERT_MESSAGE_HYPERTRACE_NOT_LOADED, ASSERT_MESSAGE_DRIVER_NOT_LOADED, AssertReturnFalse); + + Status = DeviceIoControl( + g_DeviceHandle, // Handle to device + IOCTL_PERFORM_HYPERTRACE_PT_MMAP, // IO Control Code (IOCTL) + MmapRequest, // Input Buffer to driver. + SIZEOF_HYPERTRACE_PT_MMAP_PACKETS, // Input buffer length + MmapRequest, // Output Buffer from driver. + SIZEOF_HYPERTRACE_PT_MMAP_PACKETS, // Length of output buffer in bytes. + &ReturnedLength, // Bytes placed in buffer. + NULL // synchronous call + ); + + if (!Status) + { + ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); + return FALSE; + } + + return MmapRequest->KernelStatus == DEBUGGER_OPERATION_WAS_SUCCESSFUL; +} + +/** + * @brief Parse and display enable options for !pt enable command + * + * @param CommandTokens The command tokens to parse + * @param PtRequest The PT request structure to fill with parsed options + * + * @return VOID + */ +static VOID +CommandPtParseEnable(vector & CommandTokens, HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) +{ + BOOLEAN HasPid = FALSE; + BOOLEAN HasPname = FALSE; + BOOLEAN HasPath = FALSE; + BOOLEAN HasTid = FALSE; + BOOLEAN HasCr3 = FALSE; + BOOLEAN HasSize = FALSE; + BOOLEAN HasCore = FALSE; + UINT64 Pid = 0; + UINT64 Tid = 0; + UINT64 Cr3 = 0; + UINT64 Size = 0; + UINT32 Core = 0; + string Pname; + string Path; + + for (SIZE_T i = 2; i < CommandTokens.size(); i++) + { + if (CompareLowerCaseStrings(CommandTokens.at(i), "pid")) { - PtRequest->TraceKernel = 1; - AnyMode = TRUE; + if (i + 1 >= CommandTokens.size()) + { + ShowMessages("err, 'pid' expects a hex process ID\n\n"); + CommandPtHelp(); + return; + } + i++; + if (!ConvertTokenToUInt64(CommandTokens.at(i), &Pid)) + { + ShowMessages("err, '%s' is not a valid hex process ID\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); + CommandPtHelp(); + return; + } + HasPid = TRUE; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "pname")) + { + if (i + 1 >= CommandTokens.size()) + { + ShowMessages("err, 'pname' expects a process name\n\n"); + CommandPtHelp(); + return; + } + i++; + Pname = GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)); + HasPname = TRUE; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "path")) + { + if (i + 1 >= CommandTokens.size()) + { + ShowMessages("err, 'path' expects a process executable path\n\n"); + CommandPtHelp(); + return; + } + i++; + Path = GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)); + HasPath = TRUE; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "tid")) + { + if (i + 1 >= CommandTokens.size()) + { + ShowMessages("err, 'tid' expects a hex thread ID\n\n"); + CommandPtHelp(); + return; + } + i++; + if (!ConvertTokenToUInt64(CommandTokens.at(i), &Tid)) + { + ShowMessages("err, '%s' is not a valid hex thread ID\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); + CommandPtHelp(); + return; + } + HasTid = TRUE; } else if (CompareLowerCaseStrings(CommandTokens.at(i), "cr3")) { if (i + 1 >= CommandTokens.size()) { - ShowMessages("err, '%s' expects a value\n", - GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); - return FALSE; + ShowMessages("err, 'cr3' expects a hex CR3 value\n\n"); + CommandPtHelp(); + return; } i++; - if (!ConvertTokenToUInt64(CommandTokens.at(i), &PtRequest->TargetCr3)) + if (!ConvertTokenToUInt64(CommandTokens.at(i), &Cr3)) { - ShowMessages("err, '%s' is not a valid number\n", + ShowMessages("err, '%s' is not a valid hex CR3 value\n\n", GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); - return FALSE; + CommandPtHelp(); + return; } + HasCr3 = TRUE; } - else if (CompareLowerCaseStrings(CommandTokens.at(i), "buffer")) + else if (CompareLowerCaseStrings(CommandTokens.at(i), "size")) { if (i + 1 >= CommandTokens.size()) { - ShowMessages("err, 'buffer' expects a size in bytes\n"); - return FALSE; + ShowMessages("err, 'size' expects a hex buffer size\n\n"); + CommandPtHelp(); + return; } i++; - if (!ConvertTokenToUInt64(CommandTokens.at(i), &PtRequest->BufferSize)) + if (!ConvertTokenToUInt64(CommandTokens.at(i), &Size)) { - ShowMessages("err, '%s' is not a valid number\n", + ShowMessages("err, '%s' is not a valid hex size\n\n", GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); - return FALSE; + CommandPtHelp(); + return; } + HasSize = TRUE; } - else if (CompareLowerCaseStrings(CommandTokens.at(i), "range") || - CompareLowerCaseStrings(CommandTokens.at(i), "stoprange")) + else if (CompareLowerCaseStrings(CommandTokens.at(i), "core")) { - BOOLEAN IsStop = CompareLowerCaseStrings(CommandTokens.at(i), "stoprange"); - - if (i + 2 >= CommandTokens.size()) + if (i + 1 >= CommandTokens.size()) { - ShowMessages("err, '%s' expects \n", - GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); - return FALSE; - } - - if (PtRequest->NumAddrRanges >= PT_MAX_ADDR_RANGES) - { - ShowMessages("err, no more than %u address ranges supported\n", - (UINT32)PT_MAX_ADDR_RANGES); - return FALSE; - } - - UINT64 Start = 0, End = 0; - - i++; - if (!ConvertTokenToUInt64(CommandTokens.at(i), &Start)) - { - ShowMessages("err, '%s' is not a valid address\n", - GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); - return FALSE; + ShowMessages("err, 'core' expects a hex value as the core number\n\n"); + CommandPtHelp(); + return; } i++; - if (!ConvertTokenToUInt64(CommandTokens.at(i), &End)) + if (!ConvertTokenToUInt32(CommandTokens.at(i), &Core)) { - ShowMessages("err, '%s' is not a valid address\n", + ShowMessages("err, '%s' is not a valid hex size\n\n", GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); - return FALSE; + CommandPtHelp(); + return; } - - UINT32 Idx = PtRequest->NumAddrRanges; - PtRequest->AddrRanges[Idx].Start = Start; - PtRequest->AddrRanges[Idx].End = End; - PtRequest->AddrRanges[Idx].IsStopRange = IsStop; - PtRequest->NumAddrRanges = Idx + 1; + HasCore = TRUE; } else { - ShowMessages("unknown filter option '%s'\n\n", + ShowMessages("err, unknown 'enable' option '%s'\n\n", GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); - return FALSE; + CommandPtHelp(); + return; } } // - // Default to both modes if neither was specified — matches LBR's - // empty-filter behaviour. + // pid, pname, tid, cr3 are mutually exclusive target selectors // - if (!AnyMode) + INT32 SelectorCount = (HasPid ? 1 : 0) + (HasPname ? 1 : 0) + (HasPath ? 1 : 0) + (HasTid ? 1 : 0) + (HasCr3 ? 1 : 0); + if (SelectorCount > 1) { - PtRequest->TraceUser = 1; - PtRequest->TraceKernel = 1; + ShowMessages("err, only one of 'pid', 'pname', 'path', 'tid', 'cr3' may be specified at a time\n\n"); + CommandPtHelp(); + return; } - return TRUE; + // + // Show parsed enable options + // + ShowMessages("PT enable:\n"); + + if (HasPid) + { + ShowMessages(" target pid : 0x%llx\n", Pid); + + PtRequest->EnableOptions.EnableByPid = 1; + PtRequest->EnableOptions.Pid = (UINT32)Pid; + } + else if (HasPname) + { + ShowMessages(" target pname : %s\n", Pname.c_str()); + + PtRequest->EnableOptions.EnableByCr3 = 1; + strcpy_s(PtRequest->EnableOptions.ProcessName, sizeof(PtRequest->EnableOptions.ProcessName), Pname.c_str()); + } + else if (HasPath) + { + ShowMessages(" target path : %s\n", Path.c_str()); + + PtRequest->EnableOptions.EnableByPid = 1; // Path is resolved to a PID + } + else if (HasTid) + { + ShowMessages(" target tid : 0x%llx\n", Tid); + + PtRequest->EnableOptions.EnableByTid = 1; + PtRequest->EnableOptions.Tid = (UINT32)Tid; + } + else if (HasCr3) + { + ShowMessages(" target cr3 : 0x%llx\n", Cr3); + + PtRequest->EnableOptions.EnableByCr3 = 1; + PtRequest->EnableOptions.Cr3 = Cr3; + } + else + { + ShowMessages(" target : all (no process/thread filter)\n"); + } + + // + // Check for size options + // + if (HasSize) + { + ShowMessages(" buffer size : 0x%llx bytes\n", Size); + + PtRequest->BufferSize = Size; + } + else + { + ShowMessages(" buffer size : default (%llx bytes)\n", PT_DEFAULT_BUFFER_SIZE); + + PtRequest->BufferSize = PT_DEFAULT_BUFFER_SIZE; + } + + // + // Check for core options + // + if (HasCore) + { + ShowMessages(" core : 0x%x\n", Core); + PtRequest->CoreId = Core; + } + else + { + ShowMessages(" core : default (0x%x)\n", PT_DEFAULT_PINNING_CORE); + PtRequest->CoreId = PT_DEFAULT_PINNING_CORE; + } + + // + // Fill the PtRequest structure with parsed options + // + PtRequest->PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE; + + // + // Temporary workaround for testing: + // If a path, PID, or TID is specified, invoke CommandPtRunAndTrace directly + // to launch/attach and decode the trace in one step. + // TODO: Should be removed once the kernel-side enable/filter path is complete. + // + if (HasPath) + { + ShowMessages(" Running '%s' on core: %llx\n", Path.c_str(), PtRequest->CoreId); + CommandPtRunAndTrace(Path.c_str(), NULL, FALSE, PtRequest->CoreId, 0, 0); + } + else if (HasPid) + { + ShowMessages(" Tracing pid 0x%llx on core: %llx\n", Pid, PtRequest->CoreId); + CommandPtRunAndTrace(NULL, NULL, FALSE, PtRequest->CoreId, (UINT32)Pid, 0); + } + else if (HasTid) + { + ShowMessages(" Tracing tid 0x%llx on core: %llx\n", Tid, PtRequest->CoreId); + CommandPtRunAndTrace(NULL, NULL, FALSE, PtRequest->CoreId, 0, (UINT32)Tid); + } +} + +/** + * @brief Parse and display !pt dump parameters + * + * @param CommandTokens The command tokens to parse + * @param PtRequest The PT request structure to fill with parsed options + * + * @return VOID + */ +static VOID +CommandPtParseDump(vector & CommandTokens, HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) +{ + if (CommandTokens.size() < 3) + { + ShowMessages("err, 'dump' requires additional options\n\n"); + CommandPtHelp(); + return; + } + + if (CompareLowerCaseStrings(CommandTokens.at(2), "print")) + { + // + // !pt dump print type + // + if (CommandTokens.size() != 5 || !CompareLowerCaseStrings(CommandTokens.at(3), "type")) + { + ShowMessages("err, syntax: !pt dump print type \n\n"); + CommandPtHelp(); + return; + } + + if (!CompareLowerCaseStrings(CommandTokens.at(4), "instruction") && + !CompareLowerCaseStrings(CommandTokens.at(4), "packet")) + { + ShowMessages("err, dump type must be 'instruction' or 'packet', got '%s'\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(4)).c_str()); + CommandPtHelp(); + return; + } + + ShowMessages("PT dump to console:\n"); + ShowMessages(" output : console (print)\n"); + ShowMessages(" format : %s\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(4)).c_str()); + } + else if (CompareLowerCaseStrings(CommandTokens.at(2), "path")) + { + // + // !pt dump path type + // + if (CommandTokens.size() != 6 || !CompareLowerCaseStrings(CommandTokens.at(4), "type")) + { + ShowMessages("err, syntax: !pt dump path type \n\n"); + CommandPtHelp(); + return; + } + + if (!CompareLowerCaseStrings(CommandTokens.at(5), "instruction") && + !CompareLowerCaseStrings(CommandTokens.at(5), "packet")) + { + ShowMessages("err, dump type must be 'instruction' or 'packet', got '%s'\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(5)).c_str()); + CommandPtHelp(); + return; + } + + ShowMessages("PT dump to file:\n"); + ShowMessages(" output : file\n"); + ShowMessages(" path : %s\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(3)).c_str()); + ShowMessages(" format : %s\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(5)).c_str()); + } + else + { + ShowMessages("err, unknown 'dump' sub-option '%s'\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(2)).c_str()); + CommandPtHelp(); + } +} + +/** + * @brief Parse and display !pt filter parameters + * + * @param CommandTokens The command tokens to parse + * @param PtRequest The PT request structure to fill with parsed options + * + * @return VOID + */ +static VOID +CommandPtParseFilter(vector & CommandTokens, HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) +{ + struct PtRangeEntry + { + BOOLEAN Active; + BOOLEAN IsModule; + UINT64 Start; + UINT64 End; + string ModuleName; + }; + + PtRangeEntry Ranges[4] = {}; + PtRangeEntry StopRanges[4] = {}; + BOOLEAN TraceUser = FALSE; + BOOLEAN TraceKernel = FALSE; + + for (SIZE_T i = 2; i < CommandTokens.size(); i++) + { + if (CompareLowerCaseStrings(CommandTokens.at(i), "user")) + { + TraceUser = TRUE; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "kernel")) + { + TraceKernel = TRUE; + } + else + { + // + // Resolve range1..range4 / stoprange1..stoprange4 + // + BOOLEAN IsStop = FALSE; + INT32 RangeIdx = -1; + + if (CompareLowerCaseStrings(CommandTokens.at(i), "range1")) + { + IsStop = FALSE; + RangeIdx = 0; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "range2")) + { + IsStop = FALSE; + RangeIdx = 1; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "range3")) + { + IsStop = FALSE; + RangeIdx = 2; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "range4")) + { + IsStop = FALSE; + RangeIdx = 3; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "stoprange1")) + { + IsStop = TRUE; + RangeIdx = 0; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "stoprange2")) + { + IsStop = TRUE; + RangeIdx = 1; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "stoprange3")) + { + IsStop = TRUE; + RangeIdx = 2; + } + else if (CompareLowerCaseStrings(CommandTokens.at(i), "stoprange4")) + { + IsStop = TRUE; + RangeIdx = 3; + } + else + { + ShowMessages("err, unknown filter option '%s'\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); + CommandPtHelp(); + return; + } + + PtRangeEntry * Entry = IsStop ? &StopRanges[RangeIdx] : &Ranges[RangeIdx]; + + if (i + 1 >= CommandTokens.size()) + { + ShowMessages("err, '%s' expects or 'module '\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); + CommandPtHelp(); + return; + } + + if (CompareLowerCaseStrings(CommandTokens.at(i + 1), "module")) + { + // + // range module + // + if (i + 2 >= CommandTokens.size()) + { + ShowMessages("err, 'module' expects a module name\n\n"); + CommandPtHelp(); + return; + } + i += 2; + Entry->IsModule = TRUE; + Entry->ModuleName = GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)); + Entry->Active = TRUE; + } + else + { + // + // range + // + if (i + 2 >= CommandTokens.size()) + { + ShowMessages("err, '%s' expects \n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); + CommandPtHelp(); + return; + } + i++; + if (!ConvertTokenToUInt64(CommandTokens.at(i), &Entry->Start)) + { + ShowMessages("err, '%s' is not a valid address\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); + CommandPtHelp(); + return; + } + i++; + if (!ConvertTokenToUInt64(CommandTokens.at(i), &Entry->End)) + { + ShowMessages("err, '%s' is not a valid address\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); + CommandPtHelp(); + return; + } + Entry->IsModule = FALSE; + Entry->Active = TRUE; + } + } + } + + // + // Show parsed filter options + // + ShowMessages("PT filter:\n"); + + if (TraceUser) + { + ShowMessages(" privilege : user (CPL > 0)\n"); + + PtRequest->FilterOptions.TraceUser = 1; + } + if (TraceKernel) + { + ShowMessages(" privilege : kernel (CPL == 0)\n"); + + PtRequest->FilterOptions.TraceKernel = 1; + } + if (!TraceUser && !TraceKernel) + { + ShowMessages(" privilege : (default - user + kernel)\n"); + + PtRequest->FilterOptions.TraceUser = 1; + PtRequest->FilterOptions.TraceKernel = 1; + } + + for (INT32 r = 0; r < 4; r++) + { + if (Ranges[r].Active) + { + if (Ranges[r].IsModule) + ShowMessages(" range%d : module '%s'\n", r + 1, Ranges[r].ModuleName.c_str()); + else + ShowMessages(" range%d : 0x%llx - 0x%llx\n", r + 1, Ranges[r].Start, Ranges[r].End); + } + + // + // Set the PtRequest structure for filter operation + // + PtRequest->FilterOptions.AddrRanges[r].IsStopRange = FALSE; + + PtRequest->FilterOptions.AddrRanges[r].Start = Ranges[r].Start; + PtRequest->FilterOptions.AddrRanges[r].End = Ranges[r].End; + } + + for (INT32 r = 0; r < 4; r++) + { + if (StopRanges[r].Active) + { + if (StopRanges[r].IsModule) + ShowMessages(" stoprange%d : module '%s'\n", r + 1, StopRanges[r].ModuleName.c_str()); + else + ShowMessages(" stoprange%d : 0x%llx - 0x%llx\n", r + 1, StopRanges[r].Start, StopRanges[r].End); + } + + // + // Set the PtRequest structure for filter operation + // + PtRequest->FilterOptions.AddrRanges[r].IsStopRange = TRUE; + + PtRequest->FilterOptions.AddrRanges[r].Start = Ranges[r].Start; + PtRequest->FilterOptions.AddrRanges[r].End = Ranges[r].End; + } + + // + // Set the PtRequest structure for filter operation + // + PtRequest->PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER; +} + +/** + * @brief Parse and display !pt packet parameters + * @param CommandTokens The command tokens to parse + * @param PtRequest The PT request structure to fill with parsed options + * + * @return VOID + */ +static VOID +CommandPtParsePacket(vector & CommandTokens, HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) +{ + if (CommandTokens.size() < 3) + { + ShowMessages("err, 'packet' requires at least one option\n\n"); + CommandPtHelp(); + return; + } + + BOOLEAN PktPsb = FALSE; + BOOLEAN PktPip = FALSE; + BOOLEAN PktTsc = FALSE; + BOOLEAN PktMtc = FALSE; + BOOLEAN PktCyc = FALSE; + BOOLEAN PktTnt = FALSE; + BOOLEAN PktTip = FALSE; + BOOLEAN PktFup = FALSE; + BOOLEAN PktMode = FALSE; + + for (SIZE_T i = 2; i < CommandTokens.size(); i++) + { + if (CompareLowerCaseStrings(CommandTokens.at(i), "psb")) + PktPsb = TRUE; + else if (CompareLowerCaseStrings(CommandTokens.at(i), "pip")) + PktPip = TRUE; + else if (CompareLowerCaseStrings(CommandTokens.at(i), "tsc")) + PktTsc = TRUE; + else if (CompareLowerCaseStrings(CommandTokens.at(i), "mtc")) + PktMtc = TRUE; + else if (CompareLowerCaseStrings(CommandTokens.at(i), "cyc")) + PktCyc = TRUE; + else if (CompareLowerCaseStrings(CommandTokens.at(i), "tnt")) + PktTnt = TRUE; + else if (CompareLowerCaseStrings(CommandTokens.at(i), "tip")) + PktTip = TRUE; + else if (CompareLowerCaseStrings(CommandTokens.at(i), "fup")) + PktFup = TRUE; + else if (CompareLowerCaseStrings(CommandTokens.at(i), "mode")) + PktMode = TRUE; + else + { + ShowMessages("err, unknown 'packet' option '%s'\n\n", + GetCaseSensitiveStringFromCommandToken(CommandTokens.at(i)).c_str()); + CommandPtHelp(); + return; + } + } + + // + // Show parsed packet options + // + ShowMessages("PT packet filter:\n"); + + if (PktPsb) + { + ShowMessages(" packet type: PSB (Packet Stream Boundary)\n"); + + PtRequest->PacketOptions.PSB = 1; + } + if (PktPip) + { + ShowMessages(" packet type: PIP (Paging Information Packet)\n"); + + PtRequest->PacketOptions.PIP = 1; + } + if (PktTsc) + { + ShowMessages(" packet type: TSC (Timestamp Counter)\n"); + + PtRequest->PacketOptions.TSC = 1; + } + if (PktMtc) + { + ShowMessages(" packet type: MTC (Mini Timestamp Counter)\n"); + + PtRequest->PacketOptions.MTC = 1; + } + if (PktCyc) + { + ShowMessages(" packet type: CYC (Cycle Counter)\n"); + + PtRequest->PacketOptions.CYC = 1; + } + if (PktTnt) + { + ShowMessages(" packet type: TNT (Taken/Not-Taken)\n"); + + PtRequest->PacketOptions.TNT = 1; + } + if (PktTip) + { + ShowMessages(" packet type: TIP (Target IP)\n"); + + PtRequest->PacketOptions.TNT = 1; + } + if (PktFup) + { + ShowMessages(" packet type: FUP (Flow Update Packet)\n"); + + PtRequest->PacketOptions.FUP = 1; + } + if (PktMode) + { + ShowMessages(" packet type: MODE (Mode packet)\n"); + + PtRequest->PacketOptions.MODE = 1; + } + + // + // Set the PtRequest structure for packet operation + // + PtRequest->PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PACKET; } /** @@ -271,106 +1499,112 @@ CommandPtParseFilterOptions(vector & CommandTokens, VOID CommandPt(vector CommandTokens, string Command) { - HYPERTRACE_PT_OPERATION_PACKETS PtRequest = {0}; + HYPERTRACE_PT_OPERATION_PACKETS PtRequest = {}; if (CommandTokens.size() == 1) { ShowMessages("incorrect use of the '%s'\n\n", GetCaseSensitiveStringFromCommandToken(CommandTokens.at(0)).c_str()); - CommandPtHelp(); return; } - if (CompareLowerCaseStrings(CommandTokens.at(1), "enable") && CommandTokens.size() == 2) + // + // Parse subcommands + // + if (CompareLowerCaseStrings(CommandTokens.at(1), "enable")) { - PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE; + // + // Parse and display enable options for !pt enable command + // + CommandPtParseEnable(CommandTokens, &PtRequest); } - else if (CompareLowerCaseStrings(CommandTokens.at(1), "disable") && CommandTokens.size() == 2) + else if (CompareLowerCaseStrings(CommandTokens.at(1), "disable")) { - PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE; - } - else if (CompareLowerCaseStrings(CommandTokens.at(1), "pause") && CommandTokens.size() == 2) - { - PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PAUSE; - } - else if (CompareLowerCaseStrings(CommandTokens.at(1), "resume") && CommandTokens.size() == 2) - { - PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_RESUME; - } - else if (CompareLowerCaseStrings(CommandTokens.at(1), "size") && CommandTokens.size() == 2) - { - PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_SIZE; - } - else if (CompareLowerCaseStrings(CommandTokens.at(1), "dump") && CommandTokens.size() == 2) - { - PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DUMP; - } - else if (CompareLowerCaseStrings(CommandTokens.at(1), "flush") && CommandTokens.size() == 2) - { - PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FLUSH; - } - else if (CompareLowerCaseStrings(CommandTokens.at(1), "filter")) - { - PtRequest.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER; - - if (!CommandPtParseFilterOptions(CommandTokens, &PtRequest)) + if (CommandTokens.size() != 2) { + ShowMessages("err, 'disable' takes no arguments\n\n"); CommandPtHelp(); return; } + + // + // Parse and display disable options for !pt disable command + // + CommandPtSendDisable(); + } + else if (CompareLowerCaseStrings(CommandTokens.at(1), "pause")) + { + if (CommandTokens.size() != 2) + { + ShowMessages("err, 'pause' takes no arguments\n\n"); + CommandPtHelp(); + return; + } + + // + // Parse and display pause options for !pt pause command + // + CommandPtSendPause(); + } + else if (CompareLowerCaseStrings(CommandTokens.at(1), "resume")) + { + if (CommandTokens.size() != 2) + { + ShowMessages("err, 'resume' takes no arguments\n\n"); + CommandPtHelp(); + return; + } + + // + // Parse and display resume options for !pt resume command + // + CommandPtSendResume(); + } + else if (CompareLowerCaseStrings(CommandTokens.at(1), "flush")) + { + if (CommandTokens.size() != 2) + { + ShowMessages("err, 'flush' takes no arguments\n\n"); + CommandPtHelp(); + return; + } + + // + // Parse and display flush options for !pt flush command + // + CommandPtSendFlush(); + } + else if (CompareLowerCaseStrings(CommandTokens.at(1), "dump")) + { + // + // Parse and display dump options for !pt dump command + // + CommandPtParseDump(CommandTokens, &PtRequest); + } + else if (CompareLowerCaseStrings(CommandTokens.at(1), "filter")) + { + // + // Parse and display filter options for !pt filter command + // + CommandPtParseFilter(CommandTokens, &PtRequest); + } + else if (CompareLowerCaseStrings(CommandTokens.at(1), "packet")) + { + // + // Parse and display packet options for !pt packet command + // + CommandPtParsePacket(CommandTokens, &PtRequest); } else { ShowMessages("incorrect use of the '%s'\n\n", GetCaseSensitiveStringFromCommandToken(CommandTokens.at(0)).c_str()); CommandPtHelp(); - return; } // - // Send the PT operation request + // Send the PT request to the debugger // - if (CommandPtSendRequest(&PtRequest)) - { - switch (PtRequest.PtOperationType) - { - case HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE: - ShowMessages("PT enabled successfully\n"); - break; - case HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE: - ShowMessages("PT disabled successfully\n"); - break; - case HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PAUSE: - ShowMessages("PT trace paused\n"); - break; - case HYPERTRACE_PT_OPERATION_REQUEST_TYPE_RESUME: - ShowMessages("PT trace resumed\n"); - break; - case HYPERTRACE_PT_OPERATION_REQUEST_TYPE_SIZE: - ShowMessages("PT buffer bytes-written per CPU:\n"); - for (UINT32 i = 0; i < PtRequest.NumCpus; i++) - { - ShowMessages(" core %u : 0x%llx\n", i, PtRequest.BytesPerCpu[i]); - } - break; - case HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DUMP: - ShowMessages("PT trace state is shown\n"); - break; - case HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FLUSH: - ShowMessages("PT trace state is flushed\n"); - break; - case HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER: - ShowMessages("PT filter / config updated successfully\n"); - break; - default: - ShowMessages("unknown PT operation type\n"); - break; - } - } - else - { - ShowErrorMessage(PtRequest.KernelStatus); - return; - } + CommandPtSendRequest(&PtRequest); } diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/track.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/track.cpp index 23c91fab..fee0841c 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/track.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/extension-commands/track.cpp @@ -244,7 +244,7 @@ CommandTrackHandleReceivedCallInstructions(const CHAR * NameOfFunctionFromSymbol for (SIZE_T i = 0; i < NumberOfCallsIdentation; i++) { - WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), Utf8String1, sizeof(Utf8String1) - 1, NULL, NULL); + PlatformWriteConsole(Utf8String1, sizeof(Utf8String1) - 1); } // @@ -252,7 +252,7 @@ CommandTrackHandleReceivedCallInstructions(const CHAR * NameOfFunctionFromSymbol // // CHAR Utf8String[] = "\xE2\x94\x9C\xE2\x94\x80\xE2\x94\x80"; CHAR Utf8String[] = "\xE2\x94\x8C\xE2\x94\x80\xE2\x94\x80"; - WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), Utf8String, sizeof(Utf8String) - 1, NULL, NULL); + PlatformWriteConsole(Utf8String, sizeof(Utf8String) - 1); if (NameOfFunctionFromSymbols != NULL) { @@ -296,14 +296,14 @@ CommandTrackHandleReceivedRetInstructions(UINT64 CurrentRip) for (SIZE_T i = 0; i < NumberOfCallsIdentation; i++) { - WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), Utf8String1, sizeof(Utf8String1) - 1, NULL, NULL); + PlatformWriteConsole(Utf8String1, sizeof(Utf8String1) - 1); } // // Write the UTF-8 encoded character sequence to the console // CHAR Utf8String[] = "\xE2\x94\x94\xE2\x94\x80\xE2\x94\x80\x20"; - WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), Utf8String, sizeof(Utf8String) - 1, NULL, NULL); + PlatformWriteConsole(Utf8String, sizeof(Utf8String) - 1); // // Apply addressconversion of settings here diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/dump.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/dump.cpp index f5de3129..2ac5bc92 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/dump.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/dump.cpp @@ -212,7 +212,7 @@ CommandDump(vector CommandTokens, string Command) // // Default process we read from current process // - Pid = GetCurrentProcessId(); + Pid = PlatformGetCurrentProcessId(); } // @@ -226,14 +226,19 @@ CommandDump(vector CommandTokens, string Command) // // Create or open the file for writing the dump file // - DumpFileHandle = CreateFileW( - Filepath.c_str(), - GENERIC_WRITE, - 0, - NULL, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); + // TEMPORARY LINUX SHIM (same as in pe.cpp): std::wstring stores native + // wchar_t (4 bytes on Linux), but the HyperDbg WCHAR type is 2 bytes + // (UINT16) and PlatformOpenFileForWriting takes a const WCHAR *. On Linux + // the cast is a bogus 2-byte reinterpretation, acceptable only because + // Linux file I/O is still stubbed (the path is never opened). On Windows + // WCHAR == wchar_t, so it is a plain correct pointer. TODO(Linux): remove + // once real file I/O lands and convert wchar_t -> 2-byte WCHAR properly. + // +#ifdef __linux__ + DumpFileHandle = PlatformOpenFileForWriting((const WCHAR *)Filepath.c_str()); +#else + DumpFileHandle = PlatformOpenFileForWriting(Filepath.c_str()); +#endif if (DumpFileHandle == INVALID_HANDLE_VALUE) { @@ -284,7 +289,7 @@ CommandDump(vector CommandTokens, string Command) // if (DumpFileHandle != NULL) { - CloseHandle(DumpFileHandle); + PlatformCloseFile(DumpFileHandle); DumpFileHandle = NULL; } @@ -302,8 +307,6 @@ CommandDump(vector CommandTokens, string Command) VOID CommandDumpSaveIntoFile(PVOID Buffer, UINT32 Length) { - DWORD BytesWritten; - // // Check if handle is valid // @@ -316,11 +319,11 @@ CommandDumpSaveIntoFile(PVOID Buffer, UINT32 Length) // // Write the buffer into the dump file // - if (!WriteFile(DumpFileHandle, Buffer, Length, &BytesWritten, NULL)) + if (!PlatformWriteFile(DumpFileHandle, Buffer, Length)) { ShowMessages("err, unable to write buffer into the dump\n"); - CloseHandle(DumpFileHandle); + PlatformCloseFile(DumpFileHandle); DumpFileHandle = NULL; return; diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/pagein.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/pagein.cpp index 5d83424d..a01aec56 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/pagein.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/pagein.cpp @@ -216,14 +216,14 @@ CommandPageinRequest(UINT64 TargetVirtualAddrFrom, if (Pid == 0) { - Pid = GetCurrentProcessId(); + Pid = PlatformGetCurrentProcessId(); PageFaultRequest.ProcessId = Pid; } // // Send IOCTL // - Status = DeviceIoControl( + Status = PlatformDeviceIoControl( g_DeviceHandle, // Handle to device IOCTL_DEBUGGER_BRING_PAGES_IN, // IO Control Code (IOCTL) &PageFaultRequest, // Input Buffer to driver. @@ -237,7 +237,7 @@ CommandPageinRequest(UINT64 TargetVirtualAddrFrom, if (!Status) { - ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); + ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError()); return; } diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/pe.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/pe.cpp index 7fb5a8f4..1d9fe58e 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/pe.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/pe.cpp @@ -106,10 +106,32 @@ CommandPe(vector CommandTokens, string Command) // StringToWString(Filepath, TempFilePath); + // + // TEMPORARY LINUX SHIM: + // std::wstring stores native wchar_t, which is 4 bytes on Linux, but the + // HyperDbg WCHAR type is 2 bytes (UINT16) and the PE-parser path APIs take + // a const WCHAR *. The cast below exists ONLY so the project compiles on + // Linux. On Linux it produces a bogus 2-byte reinterpretation of the 4-byte + // buffer; that is acceptable for now only because Linux file I/O is still + // stubbed (the path is never actually opened). On Windows WCHAR == wchar_t, + // so it is a plain, correct pointer with no reinterpretation. + // + // TODO (Linux): remove this cast once real Linux file I/O lands. The proper + // fix is to convert the 4-byte wchar_t path into a 2-byte WCHAR/UTF-16 + // buffer (e.g. a std::wstring -> WCHAR helper) and pass that, so the PE + // parser receives a valid path. The same conversion is needed by + // PlatformMapFileReadOnly / PlatformOpenFileForWriting. + // +#ifdef __linux__ + const WCHAR * FilepathW = (const WCHAR *)Filepath.c_str(); +#else + const WCHAR * FilepathW = Filepath.c_str(); +#endif + // // Detect whether PE is 32-bit or 64-bit // - if (!PeIsPE32BitOr64Bit(Filepath.c_str(), &Is32Bit)) + if (!PeIsPE32BitOr64Bit(FilepathW, &Is32Bit)) { // // File was invalid, the error message is shown in the above function @@ -122,10 +144,10 @@ CommandPe(vector CommandTokens, string Command) // if (!ShowDumpOfSection) { - PeShowSectionInformationAndDump(Filepath.c_str(), NULL, Is32Bit); + PeShowSectionInformationAndDump(FilepathW, NULL, Is32Bit); } else { - PeShowSectionInformationAndDump(Filepath.c_str(), GetCaseSensitiveStringFromCommandToken(CommandTokens.at(2)).c_str(), Is32Bit); + PeShowSectionInformationAndDump(FilepathW, GetCaseSensitiveStringFromCommandToken(CommandTokens.at(2)).c_str(), Is32Bit); } } diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/restart.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/restart.cpp index c0cc2d96..626856b7 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/restart.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/restart.cpp @@ -89,15 +89,33 @@ CommandRestart(vector CommandTokens, string Command) // if (g_StartCommandPathAndArguments.empty()) { + // + // TEMPORARY LINUX SHIM (same as in pe.cpp/dump.cpp): the path is a + // std::wstring of native wchar_t (4 bytes on Linux), but UdAttachToProcess + // takes a 2-byte HyperDbg WCHAR * (UINT16). The cast is a bogus 2-byte + // reinterpretation on Linux, acceptable only because the user-debugger + // path is still stubbed there. On Windows WCHAR == wchar_t, so it is a + // plain correct pointer. TODO(Linux): convert wchar_t -> 2-byte WCHAR + // properly once the Linux user-debugger path is real. + // UdAttachToProcess(NULL, - g_StartCommandPath.c_str(), + (WCHAR *)g_StartCommandPath.c_str(), NULL, FALSE); } else { + // + // TEMPORARY LINUX SHIM (same as in pe.cpp/dump.cpp): the path/arguments + // are std::wstring of native wchar_t (4 bytes on Linux), but + // UdAttachToProcess takes 2-byte HyperDbg WCHAR * (UINT16). The casts are + // a bogus 2-byte reinterpretation on Linux, acceptable only because the + // user-debugger path is still stubbed there. On Windows WCHAR == wchar_t, + // so they are plain correct pointers. TODO(Linux): convert wchar_t -> + // 2-byte WCHAR properly once the Linux user-debugger path is real. + // UdAttachToProcess(NULL, - g_StartCommandPath.c_str(), + (WCHAR *)g_StartCommandPath.c_str(), (WCHAR *)g_StartCommandPathAndArguments.c_str(), FALSE); } diff --git a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/start.cpp b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/start.cpp index 6141f4a0..c628cdb2 100644 --- a/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/start.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/commands/meta-commands/start.cpp @@ -147,8 +147,17 @@ CommandStart(vector CommandTokens, string Command) // if (Arguments.empty()) { + // + // TEMPORARY LINUX SHIM (same as in pe.cpp/dump.cpp): the path is a + // std::wstring of native wchar_t (4 bytes on Linux), but UdAttachToProcess + // takes a 2-byte HyperDbg WCHAR * (UINT16). The cast is a bogus 2-byte + // reinterpretation on Linux, acceptable only because the user-debugger + // path is still stubbed there. On Windows WCHAR == wchar_t, so it is a + // plain correct pointer. TODO(Linux): convert wchar_t -> 2-byte WCHAR + // properly once the Linux user-debugger path is real. + // UdAttachToProcess(NULL, - g_StartCommandPath.c_str(), + (WCHAR *)g_StartCommandPath.c_str(), NULL, FALSE); } @@ -164,8 +173,17 @@ CommandStart(vector CommandTokens, string Command) // StringToWString(g_StartCommandPathAndArguments, Arguments); + // + // TEMPORARY LINUX SHIM (same as in pe.cpp/dump.cpp): the path/arguments + // are std::wstring of native wchar_t (4 bytes on Linux), but + // UdAttachToProcess takes 2-byte HyperDbg WCHAR * (UINT16). The casts are + // a bogus 2-byte reinterpretation on Linux, acceptable only because the + // user-debugger path is still stubbed there. On Windows WCHAR == wchar_t, + // so they are plain correct pointers. TODO(Linux): convert wchar_t -> + // 2-byte WCHAR properly once the Linux user-debugger path is real. + // UdAttachToProcess(NULL, - g_StartCommandPath.c_str(), + (WCHAR *)g_StartCommandPath.c_str(), (WCHAR *)g_StartCommandPathAndArguments.c_str(), FALSE); } diff --git a/hyperdbg/libhyperdbg/code/debugger/core/debugger.cpp b/hyperdbg/libhyperdbg/code/debugger/core/debugger.cpp index 65cf542f..de827e8c 100644 --- a/hyperdbg/libhyperdbg/code/debugger/core/debugger.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/core/debugger.cpp @@ -656,6 +656,7 @@ ShowErrorMessage(UINT32 Error) UINT64 DebuggerGetNtoskrnlBase() { +#ifdef _WIN32 UINT64 NtoskrnlBase = NULL; PRTL_PROCESS_MODULES Modules = NULL; @@ -678,6 +679,14 @@ DebuggerGetNtoskrnlBase() free(Modules); return NtoskrnlBase; +#else + // + // TODO(Linux): NT-style system module enumeration (PRTL_PROCESS_MODULES via + // NtQuerySystemInformation) has no Linux analog. The kernel-module base lookup + // will need a Linux-specific mechanism once the kernel side exists. + // + return NULL64_ZERO; +#endif } /** @@ -719,7 +728,7 @@ DebuggerPauseDebuggee() // // Send a pause IOCTL // - StatusIoctl = DeviceIoControl(g_DeviceHandle, // Handle to device + StatusIoctl = PlatformDeviceIoControl(g_DeviceHandle, // Handle to device IOCTL_PAUSE_PACKET_RECEIVED, // IO Control Code (IOCTL) &PauseRequest, // Input Buffer to driver. SIZEOF_DEBUGGER_PAUSE_PACKET_RECEIVED, // Input buffer @@ -733,7 +742,7 @@ DebuggerPauseDebuggee() if (!StatusIoctl) { - ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); + ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError()); return FALSE; } @@ -1391,7 +1400,7 @@ SendEventToKernel(PDEBUGGER_GENERAL_EVENT_DETAIL Event, // Send IOCTL // - Status = DeviceIoControl(g_DeviceHandle, // Handle to device + Status = PlatformDeviceIoControl(g_DeviceHandle, // Handle to device IOCTL_DEBUGGER_REGISTER_EVENT, // IO Control Code (IOCTL) Event, // Input Buffer to driver. EventBufferLength, // Input buffer length @@ -1408,7 +1417,7 @@ SendEventToKernel(PDEBUGGER_GENERAL_EVENT_DETAIL Event, if (!Status) { - ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); + ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError()); return FALSE; } } @@ -1560,7 +1569,7 @@ RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event, // if (ActionBreakToDebugger != NULL) { - Status = DeviceIoControl( + Status = PlatformDeviceIoControl( g_DeviceHandle, // Handle to device IOCTL_DEBUGGER_ADD_ACTION_TO_EVENT, // IO Control Code (IOCTL) ActionBreakToDebugger, // Input Buffer to driver. @@ -1578,7 +1587,7 @@ RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event, if (!Status) { - ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); + ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError()); return FALSE; } } @@ -1588,7 +1597,7 @@ RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event, // if (ActionCustomCode != NULL) { - Status = DeviceIoControl( + Status = PlatformDeviceIoControl( g_DeviceHandle, // Handle to device IOCTL_DEBUGGER_ADD_ACTION_TO_EVENT, // IO Control Code (IOCTL) ActionCustomCode, // Input Buffer to driver. @@ -1606,7 +1615,7 @@ RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event, if (!Status) { - ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); + ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError()); return FALSE; } } @@ -1616,7 +1625,7 @@ RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event, // if (ActionScript != NULL) { - Status = DeviceIoControl( + Status = PlatformDeviceIoControl( g_DeviceHandle, // Handle to device IOCTL_DEBUGGER_ADD_ACTION_TO_EVENT, // IO Control Code (IOCTL) ActionScript, // Input Buffer to driver. @@ -1634,7 +1643,7 @@ RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event, if (!Status) { - ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); + ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError()); return FALSE; } } @@ -1800,7 +1809,7 @@ InterpretGeneralEventAndActionsFields( // PVOID BufferOfCommandString = malloc(BufferOfCommandStringLength); - RtlZeroMemory(BufferOfCommandString, BufferOfCommandStringLength); + PlatformZeroMemory(BufferOfCommandString, BufferOfCommandStringLength); // // Copy the string to the buffer @@ -2105,7 +2114,7 @@ InterpretGeneralEventAndActionsFields( LengthOfEventBuffer = sizeof(DEBUGGER_GENERAL_EVENT_DETAIL) + ConditionBufferLength; TempEvent = (PDEBUGGER_GENERAL_EVENT_DETAIL)malloc(LengthOfEventBuffer); - RtlZeroMemory(TempEvent, LengthOfEventBuffer); + PlatformZeroMemory(TempEvent, LengthOfEventBuffer); // // Check if buffer is available @@ -2187,7 +2196,7 @@ InterpretGeneralEventAndActionsFields( TempActionCustomCode = (PDEBUGGER_GENERAL_ACTION)malloc(LengthOfCustomCodeActionBuffer); - RtlZeroMemory(TempActionCustomCode, LengthOfCustomCodeActionBuffer); + PlatformZeroMemory(TempActionCustomCode, LengthOfCustomCodeActionBuffer); memcpy( (PVOID)((UINT64)TempActionCustomCode + sizeof(DEBUGGER_GENERAL_ACTION)), @@ -2226,7 +2235,7 @@ InterpretGeneralEventAndActionsFields( LengthOfScriptActionBuffer = sizeof(DEBUGGER_GENERAL_ACTION) + ScriptBufferLength; TempActionScript = (PDEBUGGER_GENERAL_ACTION)malloc(LengthOfScriptActionBuffer); - RtlZeroMemory(TempActionScript, LengthOfScriptActionBuffer); + PlatformZeroMemory(TempActionScript, LengthOfScriptActionBuffer); memcpy((PVOID)((UINT64)TempActionScript + sizeof(DEBUGGER_GENERAL_ACTION)), (PVOID)ScriptBufferAddress, @@ -2272,7 +2281,7 @@ InterpretGeneralEventAndActionsFields( TempActionBreak = (PDEBUGGER_GENERAL_ACTION)malloc(LengthOfBreakActionBuffer); - RtlZeroMemory(TempActionBreak, LengthOfBreakActionBuffer); + PlatformZeroMemory(TempActionBreak, LengthOfBreakActionBuffer); // // Set the action Tag diff --git a/hyperdbg/libhyperdbg/code/debugger/core/interpreter.cpp b/hyperdbg/libhyperdbg/code/debugger/core/interpreter.cpp index 5d8055e2..edc0ab52 100644 --- a/hyperdbg/libhyperdbg/code/debugger/core/interpreter.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/core/interpreter.cpp @@ -1295,12 +1295,17 @@ InitializeDebugger() // // Set the callback for symbol message handler // - ScriptEngineSetTextMessageCallbackWrapper(ShowMessages); + // + // ShowMessages is passed as PVOID (the callback API stores it in a PVOID and + // casts to the concrete fn-ptr type at the invocation site); g++ requires the + // explicit function-pointer -> void* cast that MSVC performs implicitly. + // + ScriptEngineSetTextMessageCallbackWrapper((PVOID)ShowMessages); // // Register the CTRL+C and CTRL+BREAK Signals handler // - if (!SetConsoleCtrlHandler(BreakController, TRUE)) + if (!PlatformInstallCtrlHandler(BreakController)) { ShowMessages("err, when registering CTRL+C and CTRL+BREAK Signals " "handler\n"); diff --git a/hyperdbg/libhyperdbg/code/debugger/kernel-level/kd.cpp b/hyperdbg/libhyperdbg/code/debugger/kernel-level/kd.cpp index f229ff5d..4020c620 100644 --- a/hyperdbg/libhyperdbg/code/debugger/kernel-level/kd.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/kernel-level/kd.cpp @@ -21,9 +21,11 @@ extern DEBUGGER_SYNCRONIZATION_EVENTS_STATE g_KernelSyncronizationObjectsHandleTable[DEBUGGER_MAXIMUM_SYNCRONIZATION_KERNEL_DEBUGGER_OBJECTS]; extern BYTE g_CurrentRunningInstruction[MAXIMUM_INSTR_SIZE]; extern BOOLEAN g_IsConnectedToHyperDbgLocally; +#ifdef _WIN32 extern OVERLAPPED g_OverlappedIoStructureForReadDebugger; extern OVERLAPPED g_OverlappedIoStructureForWriteDebugger; extern OVERLAPPED g_OverlappedIoStructureForReadDebuggee; +#endif // _WIN32 extern DEBUGGER_EVENT_AND_ACTION_RESULT g_DebuggeeResultOfRegisteringEvent; extern DEBUGGER_EVENT_AND_ACTION_RESULT g_DebuggeeResultOfAddingActionsToEvent; @@ -1565,6 +1567,7 @@ KdReceivePacketFromDebuggee(CHAR * BufferToSave, // do { +#ifdef _WIN32 // // It's in the debugger // @@ -1600,6 +1603,18 @@ KdReceivePacketFromDebuggee(CHAR * BufferToSave, // Reset event for next try // ResetEvent(g_OverlappedIoStructureForReadDebugger.hEvent); +#else + // + // Linux: read one byte through the cross-platform serial transport + // + if (!PlatformSerialReadByte(g_SerialRemoteComPortHandle, + &ReadData, + &NoBytesRead, + PLATFORM_SERIAL_IO_DEBUGGER)) + { + return FALSE; + } +#endif // // We already now that the maximum packet size is MaxSerialPacketSize @@ -1650,6 +1665,7 @@ KdReceivePacketFromDebugger(CHAR * BufferToSave, DWORD NoBytesRead = 0; /* Bytes read by ReadFile() */ UINT32 Loop = 0; +#ifdef _WIN32 // // Set the timeout in milliseconds (e.g., 5000 ms = 5 seconds) // @@ -1666,12 +1682,14 @@ KdReceivePacketFromDebugger(CHAR * BufferToSave, Timeouts.WriteTotalTimeoutConstant = 0; Timeouts.WriteTotalTimeoutMultiplier = 0; SetCommTimeouts(g_SerialRemoteComPortHandle, &Timeouts); +#endif // // Read data and store in a buffer // do { +#ifdef _WIN32 // // It's in the debuggee // @@ -1707,6 +1725,19 @@ KdReceivePacketFromDebugger(CHAR * BufferToSave, // Reset event for next try // ResetEvent(g_OverlappedIoStructureForReadDebuggee.hEvent); +#else + // + // Linux: read one byte through the cross-platform serial transport + // (the 5s read timeout is applied inside the platform layer) + // + if (!PlatformSerialReadByte(g_SerialRemoteComPortHandle, + &ReadData, + &NoBytesRead, + PLATFORM_SERIAL_IO_DEBUGGEE)) + { + return FALSE; + } +#endif // // We already now that the maximum packet size is MaxSerialPacketSize @@ -1781,6 +1812,7 @@ KdSendPacketToDebuggee(const CHAR * Buffer, UINT32 Length, BOOLEAN SendEndOfBuff return FALSE; } +#ifdef _WIN32 if (g_IsSerialConnectedToRemoteDebugger || g_IsDebuggeeInHandshakingPhase) { // @@ -1846,6 +1878,20 @@ KdSendPacketToDebuggee(const CHAR * Buffer, UINT32 Length, BOOLEAN SendEndOfBuff // ResetEvent(g_OverlappedIoStructureForWriteDebugger.hEvent); } +#else + // + // Linux: write through the cross-platform serial transport. The debuggee / + // handshaking path is synchronous; the debugger path is overlapped (handled + // inside the platform layer). + // + { + BOOLEAN Synchronous = (g_IsSerialConnectedToRemoteDebugger || g_IsDebuggeeInHandshakingPhase); + if (!PlatformSerialWrite(g_SerialRemoteComPortHandle, Buffer, Length, Synchronous)) + { + return FALSE; + } + } +#endif Out: if (SendEndOfBuffer) diff --git a/hyperdbg/libhyperdbg/code/debugger/misc/pt-helper.cpp b/hyperdbg/libhyperdbg/code/debugger/misc/pt-helper.cpp new file mode 100644 index 00000000..6f80b752 --- /dev/null +++ b/hyperdbg/libhyperdbg/code/debugger/misc/pt-helper.cpp @@ -0,0 +1,432 @@ +/** + * @file pt-help.cpp + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief PT helper functions + * @details + * @version 0.21 + * @date 2026-07-03 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#include "pch.h" + +/** + * @brief Read the process image for PT decoding + * + * @param Buffer + * @param Size + * @param Asid + * @param Ip + * @param Context + * + * @return int + */ +int +PtHelperReadImage(UINT8 * Buffer, SIZE_T Size, const struct pt_asid * Asid, UINT64 Ip, VOID * Context) +{ + (VOID) Asid; + + IMAGE_SYMBOL_CONTEXT * Ctx = (IMAGE_SYMBOL_CONTEXT *)Context; + + if (Ctx == NULL || Ctx->Code == NULL || Ip < Ctx->CodeBase || Ip >= Ctx->CodeBase + Ctx->CodeSize) + return -pte_nomap; + + UINT64 Available = Ctx->CodeBase + Ctx->CodeSize - Ip; + SIZE_T Count = (Size < Available) ? Size : (SIZE_T)Available; + + memcpy(Buffer, Ctx->Code + (Ip - Ctx->CodeBase), Count); + return (int)Count; +} + +/** + * @brief Capture the .text section of a process image + * + * @param Process + * @param TextStart + * @param TextEnd + * @param Ctx + * + * @return BOOLEAN + */ +BOOLEAN +PtHelperCaptureImage(HANDLE Process, UINT64 * TextStart, UINT64 * TextEnd, IMAGE_SYMBOL_CONTEXT * Ctx) +{ + HMODULE Ntdll = GetModuleHandleA("ntdll.dll"); + PFN_NT_QIP NtQip = Ntdll ? (PFN_NT_QIP)GetProcAddress(Ntdll, "NtQueryInformationProcess") : NULL; + PROC_BASIC_INFO Pbi = {0}; + ULONG Ret = 0; + SIZE_T Got = 0; + UINT64 Base = 0; + IMAGE_DOS_HEADER Dos; + IMAGE_NT_HEADERS64 Nt; + UINT64 SectionBase; + + if (NtQip == NULL || NtQip(Process, 0, &Pbi, sizeof(Pbi), &Ret) < 0 || Pbi.PebBaseAddress == NULL) + return FALSE; + + if (!ReadProcessMemory(Process, (PBYTE)Pbi.PebBaseAddress + 0x10, &Base, sizeof(Base), &Got) || Base == 0) + return FALSE; + + if (!ReadProcessMemory(Process, (PVOID)Base, &Dos, sizeof(Dos), &Got) || Dos.e_magic != IMAGE_DOS_SIGNATURE) + return FALSE; + + if (!ReadProcessMemory(Process, (PBYTE)Base + Dos.e_lfanew, &Nt, sizeof(Nt), &Got) || Nt.Signature != IMAGE_NT_SIGNATURE) + return FALSE; + + Ctx->ImageBase = Base; + SectionBase = Base + Dos.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader) + Nt.FileHeader.SizeOfOptionalHeader; + + for (WORD i = 0; i < Nt.FileHeader.NumberOfSections; i++) + { + IMAGE_SECTION_HEADER Section; + + if (!ReadProcessMemory(Process, (PBYTE)SectionBase + (UINT64)i * sizeof(Section), &Section, sizeof(Section), &Got)) + return FALSE; + + if (memcmp(Section.Name, ".text", 6) != 0) + continue; + + UINT64 Start = Base + Section.VirtualAddress; + UINT64 Size = Section.Misc.VirtualSize ? Section.Misc.VirtualSize : Section.SizeOfRawData; + + if (Size == 0) + return FALSE; + + Ctx->Code = (UINT8 *)malloc((SIZE_T)Size); + if (Ctx->Code == NULL) + return FALSE; + + if (!ReadProcessMemory(Process, (PVOID)Start, Ctx->Code, (SIZE_T)Size, &Got) || Got != Size) + { + free(Ctx->Code); + Ctx->Code = NULL; + return FALSE; + } + + Ctx->CodeBase = Start; + Ctx->CodeSize = Size; + *TextStart = Start; + *TextEnd = Start + Size - 1; + return TRUE; + } + + return FALSE; +} + +/** + * @brief Resolve function address using symbol information + * + * @param Process + * @param Path + * @param Name + * @param ImageBase + * @param Start + * @param End + * + * @return BOOLEAN + */ +BOOLEAN +PtHelperResolveFunction(HANDLE Process, const CHAR * Path, const CHAR * Name, UINT64 ImageBase, UINT64 * Start, UINT64 * End) +{ + union + { + SYMBOL_INFO Info; + BYTE Buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; + } Symbol = {0}; + BOOLEAN Ok = FALSE; + + SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); + if (!SymInitialize(Process, NULL, FALSE)) + return FALSE; + + if (SymLoadModuleEx(Process, NULL, Path, NULL, (DWORD64)ImageBase, 0, NULL, 0) != 0) + { + Symbol.Info.SizeOfStruct = sizeof(SYMBOL_INFO); + Symbol.Info.MaxNameLen = MAX_SYM_NAME; + + if (SymFromName(Process, Name, &Symbol.Info) && Symbol.Info.Address != 0) + { + *Start = Symbol.Info.Address; + *End = Symbol.Info.Address + (Symbol.Info.Size ? Symbol.Info.Size : 0x200) - 1; + Ok = TRUE; + } + } + + SymCleanup(Process); + return Ok; +} + +/** + * @brief Get PT packet name + * + * @param Type + * + * @return const CHAR * + */ +const CHAR * +PtHelperPacketName(enum pt_packet_type Type) +{ + switch (Type) + { + case ppt_psb: + return "PSB"; + case ppt_psbend: + return "PSBEND"; + case ppt_pad: + return "PAD"; + case ppt_fup: + return "FUP"; + case ppt_tip: + return "TIP"; + case ppt_tip_pge: + return "TIP.PGE"; + case ppt_tip_pgd: + return "TIP.PGD"; + case ppt_tnt_8: + return "TNT8"; + case ppt_tnt_64: + return "TNT64"; + case ppt_mode: + return "MODE"; + case ppt_pip: + return "PIP"; + case ppt_vmcs: + return "VMCS"; + case ppt_cbr: + return "CBR"; + case ppt_tsc: + return "TSC"; + case ppt_tma: + return "TMA"; + case ppt_mtc: + return "MTC"; + case ppt_cyc: + return "CYC"; + case ppt_ovf: + return "OVF"; + case ppt_stop: + return "STOP"; + case ppt_exstop: + return "EXSTOP"; + case ppt_mnt: + return "MNT"; + case ppt_ptw: + return "PTW"; + default: + return "?"; + } +} + +/** + * @brief Reconstruct IP from PT packet + * + * @param Packet + * @param LastIp + * + * @return UINT64 + */ +UINT64 +PtHelperReconstructIp(const struct pt_packet_ip * Packet, UINT64 * LastIp) +{ + UINT64 Value = *LastIp; + + switch (Packet->ipc) + { + case pt_ipc_update_16: + Value = (Value & ~0xffffull) | (Packet->ip & 0xffffull); + break; + case pt_ipc_update_32: + Value = (Value & ~0xffffffffull) | (Packet->ip & 0xffffffffull); + break; + case pt_ipc_update_48: + Value = (Value & ~0xffffffffffffull) | (Packet->ip & 0xffffffffffffull); + break; + case pt_ipc_sext_48: + Value = Packet->ip & 0xffffffffffffull; + if (Value & 0x800000000000ull) + Value |= 0xffff000000000000ull; + break; + default: + Value = Packet->ip; + break; + } + + *LastIp = Value; + return Value; +} + +/** + * @brief Decode PT packets + * + * @param Cpu + * @param Buffer + * @param Size + * @param ImageBase + * + * @return UINT64 + */ +UINT64 +PtHelperDecodeCorePackets(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size, UINT64 ImageBase) +{ + struct pt_config Config; + struct pt_packet_decoder * Decoder; + UINT64 Count = 0; + UINT64 LastIp = 0; + int Status; + + pt_config_init(&Config); + Config.begin = (UINT8 *)Buffer; + Config.end = (UINT8 *)Buffer + Size; + + Decoder = pt_pkt_alloc_decoder(&Config); + if (Decoder == NULL) + { + ShowMessages("[-] core %u: cannot allocate packet decoder\n", Cpu); + return 0; + } + + for (;;) + { + Status = pt_pkt_sync_forward(Decoder); + if (Status < 0) + break; + + for (;;) + { + struct pt_packet Packet; + + Status = pt_pkt_next(Decoder, &Packet, sizeof(Packet)); + if (Status < 0) + break; + + Count++; + + switch (Packet.type) + { + case ppt_tnt_8: + case ppt_tnt_64: + ShowMessages(" %-8s %2u ", PtHelperPacketName(Packet.type), Packet.payload.tnt.bit_size); + for (UINT8 Bit = 0; Bit < Packet.payload.tnt.bit_size && Bit < 64; Bit++) + putchar(((Packet.payload.tnt.payload >> (Packet.payload.tnt.bit_size - 1 - Bit)) & 1) ? 'T' : 'N'); + putchar('\n'); + break; + + case ppt_tip: + case ppt_fup: + case ppt_tip_pge: + case ppt_tip_pgd: + if (Packet.payload.ip.ipc == pt_ipc_suppressed) + ShowMessages(" %-8s (ip suppressed)\n", PtHelperPacketName(Packet.type)); + else + { + UINT64 Ip = PtHelperReconstructIp(&Packet.payload.ip, &LastIp); + ShowMessages(" %-8s 0x%016llx exe+0x%llx\n", + PtHelperPacketName(Packet.type), + (UINT64)Ip, + (UINT64)(Ip - ImageBase)); + } + break; + + case ppt_pip: + ShowMessages(" %-8s cr3=0x%llx\n", PtHelperPacketName(Packet.type), (UINT64)Packet.payload.pip.cr3); + break; + + case ppt_cbr: + // ShowMessages(" %-8s ratio=%u\n", PtHelperPacketName(Packet.type), Packet.payload.cbr.ratio); + break; + + case ppt_tsc: + ShowMessages(" %-8s tsc=0x%llx\n", PtHelperPacketName(Packet.type), (UINT64)Packet.payload.tsc.tsc); + break; + + default: + // ShowMessages(" %-8s\n", PtHelperPacketName(Packet.type)); + break; + } + } + } + + pt_pkt_free_decoder(Decoder); + return Count; +} + +/** + * @brief Decode PT instructions + * + * @param Cpu + * @param Buffer + * @param Size + * @param Ctx + * + * @return UINT64 + */ +UINT64 +PtHelperDecodeCore(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size, IMAGE_SYMBOL_CONTEXT * Ctx) +{ + struct pt_config Config; + struct pt_insn_decoder * Decoder; + struct pt_image * Image; + UINT64 Count = 0; + int Status; + + pt_config_init(&Config); + Config.begin = (UINT8 *)Buffer; + Config.end = (UINT8 *)Buffer + Size; + + Decoder = pt_insn_alloc_decoder(&Config); + if (Decoder == NULL) + { + ShowMessages("[-] core %u: cannot allocate instruction decoder\n", Cpu); + return 0; + } + + Image = pt_insn_get_image(Decoder); + pt_image_set_callback(Image, PtHelperReadImage, Ctx); + + for (;;) + { + Status = pt_insn_sync_forward(Decoder); + if (Status < 0) + break; + + for (;;) + { + struct pt_insn Insn; + + while (Status & pts_event_pending) + { + struct pt_event Event; + Status = pt_insn_event(Decoder, &Event, sizeof(Event)); + if (Status < 0) + break; + } + + if (Status < 0 || (Status & pts_eos)) + break; + + Status = pt_insn_next(Decoder, &Insn, sizeof(Insn)); + if (Status < 0) + break; + + ZydisDisassembledInstruction Disasm; + ZydisMachineMode Mode = (Insn.mode == ptem_32bit) ? ZYDIS_MACHINE_MODE_LEGACY_32 : ZYDIS_MACHINE_MODE_LONG_64; + + if (ZYAN_SUCCESS(ZydisDisassembleIntel(Mode, Insn.ip, Insn.raw, Insn.size, &Disasm))) + ShowMessages(" 0x%016llx exe+0x%-6llx %s\n", + (UINT64)Insn.ip, + (UINT64)(Insn.ip - Ctx->ImageBase), + Disasm.text); + else + ShowMessages(" 0x%016llx (undecodable)\n", (UINT64)Insn.ip); + + Count++; + } + + if (Status >= 0 && (Status & pts_eos)) + break; + } + + pt_insn_free_decoder(Decoder); + return Count; +} diff --git a/hyperdbg/libhyperdbg/code/debugger/script-engine/symbol-linux.cpp b/hyperdbg/libhyperdbg/code/debugger/script-engine/symbol-linux.cpp new file mode 100644 index 00000000..2f7f65a4 --- /dev/null +++ b/hyperdbg/libhyperdbg/code/debugger/script-engine/symbol-linux.cpp @@ -0,0 +1,107 @@ +/** + * @file symbol-linux.cpp + * @author Max Raulea (max.raulea@gmail.com) + * @brief Linux stub implementations of the symbol subsystem + * @details The Windows implementation uses DbgHelp + PDB files (symbol-parser/). + * Linux uses ELF/DWARF which requires a separate implementation. + * These stubs allow the library to compile and link on Linux while + * keeping all call sites intact. + * + * TODO: implement a real ELF/DWARF symbol parser for Linux + * (libdw / libelf / libbfd) and replace these stubs. + * + * @version 0.1 + * @date 2026-06-08 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#include "pch.h" + +#ifdef __linux__ + +VOID +SymbolBuildAndShowSymbolTable() +{ + ShowMessages("err, symbol table is not supported on Linux yet\n"); +} + +BOOLEAN +SymbolShowFunctionNameBasedOnAddress(UINT64 Address, PUINT64 UsedBaseAddress) +{ + return FALSE; +} + +BOOLEAN +SymbolLoadOrDownloadSymbols(BOOLEAN IsDownload, BOOLEAN SilentLoad) +{ + if (!SilentLoad) + ShowMessages("err, symbol loading is not supported on Linux yet\n"); + return FALSE; +} + +/** + * @brief Attempt to resolve a name or expression to an address. + * + * On Linux, full symbol resolution (ELF/DWARF) is not yet implemented. + * This stub handles the common case of a plain hex/decimal literal so that + * numeric addresses still work everywhere in the debugger. + */ +BOOLEAN +SymbolConvertNameOrExprToAddress(const string & TextToConvert, PUINT64 Result) +{ + try + { + *Result = std::stoull(TextToConvert, nullptr, 0); + return TRUE; + } + catch (...) + { + return FALSE; + } +} + +BOOLEAN +SymbolDeleteSymTable() +{ + return TRUE; +} + +BOOLEAN +SymbolBuildSymbolTable(PMODULE_SYMBOL_DETAIL * BufferToStoreDetails, + PUINT32 StoredLength, + UINT32 UserProcessId, + BOOLEAN SendOverSerial) +{ + return FALSE; +} + +BOOLEAN +SymbolBuildAndUpdateSymbolTable(PMODULE_SYMBOL_DETAIL SymbolDetail) +{ + return FALSE; +} + +VOID +SymbolInitialReload() +{ +} + +BOOLEAN +SymbolLocalReload(UINT32 UserProcessId) +{ + return FALSE; +} + +VOID +SymbolPrepareDebuggerWithSymbolInfo(UINT32 UserProcessId) +{ +} + +BOOLEAN +SymbolReloadSymbolTableInDebuggerMode(UINT32 ProcessId) +{ + return FALSE; +} + +#endif // __linux__ diff --git a/hyperdbg/libhyperdbg/code/debugger/user-level/pe-parser.cpp b/hyperdbg/libhyperdbg/code/debugger/user-level/pe-parser.cpp index 3ce28f1e..ce0dfc4a 100644 --- a/hyperdbg/libhyperdbg/code/debugger/user-level/pe-parser.cpp +++ b/hyperdbg/libhyperdbg/code/debugger/user-level/pe-parser.cpp @@ -3738,7 +3738,8 @@ PeShowSectionInformationAndDump(const WCHAR * AddressOfFile, const ULONGLONG MaxTotalSectionScanBytes = 64 * 1024 * 1024; const SIZE_T MaxSectionDumpBytes = 1024 * 1024; const ULONGLONG MaxTotalSectionDumpBytes = 4 * 1024 * 1024; - HANDLE MapObjectHandle = NULL, FileHandle = INVALID_HANDLE_VALUE; // File Mapping Object + HANDLE FileHandle = INVALID_HANDLE_VALUE; // Open file handle (kept for the raw Rich-header read) + SIZE_T MappedSize = 0; // Size of the mapped file in bytes UINT32 NumberOfSections; // Number of sections LPVOID BaseAddr = NULL; // Pointer to the base memory of mapped file PIMAGE_DOS_HEADER DosHeader; // Pointer to DOS Header @@ -3760,39 +3761,22 @@ PeShowSectionInformationAndDump(const WCHAR * AddressOfFile, time_t TimeDateStamp; // - // Open the EXE File + // Open and map the EXE file read-only. The wrapper also hands back the open + // file handle so the raw Rich-header read below can still seek/read it. // - FileHandle = CreateFileW(AddressOfFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + BaseAddr = PlatformMapFileReadOnly(AddressOfFile, &MappedSize, &FileHandle); - if (FileHandle == INVALID_HANDLE_VALUE) + if (BaseAddr == NULL) { ShowMessages("err, could not open the file specified\n"); return FALSE; } - if (!GetFileSizeEx(FileHandle, &FileSize) || FileSize.QuadPart < (LONGLONG)sizeof(IMAGE_DOS_HEADER)) + FileSize.QuadPart = (LONGLONG)MappedSize; + + if (FileSize.QuadPart < (LONGLONG)sizeof(IMAGE_DOS_HEADER)) { - CloseHandle(FileHandle); - return FALSE; - } - - // - // Mapping Given EXE file to Memory - // - MapObjectHandle = CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL); - - if (MapObjectHandle == NULL) - { - CloseHandle(FileHandle); - return FALSE; - } - - BaseAddr = MapViewOfFile(MapObjectHandle, FILE_MAP_READ, 0, 0, 0); - - if (BaseAddr == NULL) - { - CloseHandle(MapObjectHandle); - CloseHandle(FileHandle); + PlatformUnmapFile(BaseAddr, MappedSize, FileHandle); return FALSE; } @@ -3843,15 +3827,7 @@ PeShowSectionInformationAndDump(const WCHAR * AddressOfFile, goto SkipRichHeader; } - if (!SetFilePointerEx(FileHandle, FileStart, NULL, FILE_BEGIN)) - { - RichHeaderStatus = "read failed"; - delete[] DataPtr; - goto SkipRichHeader; - } - - BOOL Result = ReadFile(FileHandle, DataPtr, RichReadSize, &BytesRead, NULL); - if (!Result || BytesRead != RichReadSize) + if (!PlatformReadFileAtOffset(FileHandle, 0, DataPtr, RichReadSize, &BytesRead) || BytesRead != RichReadSize) { RichHeaderStatus = "read failed"; delete[] DataPtr; @@ -4491,20 +4467,7 @@ Finished: delete[] PeFileRichHeader.Entries; } - if (BaseAddr != NULL) - { - UnmapViewOfFile(BaseAddr); - } - - if (MapObjectHandle != NULL) - { - CloseHandle(MapObjectHandle); - } - - if (FileHandle != INVALID_HANDLE_VALUE) - { - CloseHandle(FileHandle); - } + PlatformUnmapFile(BaseAddr, MappedSize, FileHandle); return Result; } @@ -4519,52 +4482,31 @@ Finished: BOOLEAN PeIsPE32BitOr64Bit(const WCHAR * AddressOfFile, PBOOLEAN Is32Bit) { - BOOLEAN Result = FALSE; - HANDLE MapObjectHandle, FileHandle; // File Mapping Object - LPVOID BaseAddr; // Pointer to the base memory of mapped file + BOOLEAN Result = FALSE; + HANDLE FileHandle = INVALID_HANDLE_VALUE; // Open file handle + SIZE_T MappedSize = 0; // Size of the mapped file in bytes + LPVOID BaseAddr; // Pointer to the base memory of mapped file PIMAGE_DOS_HEADER DosHeader; // Pointer to DOS Header PIMAGE_NT_HEADERS32 NtHeader32 = NULL; // Pointer to NT Header 32 bit PE_IMAGE_READER Reader; LARGE_INTEGER FileSize; // - // Open the EXE File + // Open and map the EXE file read-only // - FileHandle = CreateFileW(AddressOfFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (FileHandle == INVALID_HANDLE_VALUE) - { - ShowMessages("err, unable to read the file (%x)\n", GetLastError()); - return FALSE; - }; - - if (!GetFileSizeEx(FileHandle, &FileSize) || FileSize.QuadPart < (LONGLONG)sizeof(IMAGE_DOS_HEADER)) - { - CloseHandle(FileHandle); - return FALSE; - } - - // - // Mapping Given EXE file to Memory - // - MapObjectHandle = - CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL); - - if (MapObjectHandle == NULL) - { - CloseHandle(FileHandle); - - ShowMessages("err, unable to create file mappings (%x)\n", GetLastError()); - return FALSE; - } - - BaseAddr = MapViewOfFile(MapObjectHandle, FILE_MAP_READ, 0, 0, 0); + BaseAddr = PlatformMapFileReadOnly(AddressOfFile, &MappedSize, &FileHandle); if (BaseAddr == NULL) { - CloseHandle(MapObjectHandle); - CloseHandle(FileHandle); + ShowMessages("err, unable to read or map the file (%x)\n", PlatformGetLastError()); + return FALSE; + } - ShowMessages("err, unable to create map view of file (%x)\n", GetLastError()); + FileSize.QuadPart = (LONGLONG)MappedSize; + + if (FileSize.QuadPart < (LONGLONG)sizeof(IMAGE_DOS_HEADER)) + { + PlatformUnmapFile(BaseAddr, MappedSize, FileHandle); return FALSE; } @@ -4649,9 +4591,7 @@ Finished: // // Unmap and close the handles // - UnmapViewOfFile(BaseAddr); - CloseHandle(MapObjectHandle); - CloseHandle(FileHandle); + PlatformUnmapFile(BaseAddr, MappedSize, FileHandle); return Result; } diff --git a/hyperdbg/libhyperdbg/code/export/export.cpp b/hyperdbg/libhyperdbg/code/export/export.cpp index b62b6b36..f78f73a5 100644 --- a/hyperdbg/libhyperdbg/code/export/export.cpp +++ b/hyperdbg/libhyperdbg/code/export/export.cpp @@ -53,6 +53,17 @@ hyperdbg_u_load_vmm() return HyperDbgLoadVmmModule(); } +/** + * @brief Check if any module is loaded + * + * @return BOOLEAN Returns true if any module is loaded and false if no module is loaded + */ +BOOLEAN +hyperdbg_u_is_any_module_loaded() +{ + return HyperDbgIsAnyModuleLoaded(); +} + /** * @brief Unload all modules * @@ -945,3 +956,31 @@ hyperdbg_u_lbr_dump(HYPERTRACE_LBR_DUMP_PACKETS * LbrdumpRequest) { return HyperDbgLbrdumpSendRequest(LbrdumpRequest); } + +/** + * @brief Perform an Intel PT operation (enable / disable / pause / resume / + * size / dump / flush / filter) + * + * @param PtRequest The PT operation request packet + * + * @return BOOLEAN TRUE if the operation was successful, otherwise FALSE + */ +BOOLEAN +hyperdbg_u_pt_operation(HYPERTRACE_PT_OPERATION_PACKETS * PtRequest) +{ + return HyperDbgPerformPtOperation(PtRequest); +} + +/** + * @brief Map the per-CPU Intel PT output buffers into the calling process + * + * @param MmapRequest The PT mmap request packet; filled with per-CPU + * { UserVa, Size } on success + * + * @return BOOLEAN TRUE if the operation was successful, otherwise FALSE + */ +BOOLEAN +hyperdbg_u_pt_mmap(HYPERTRACE_PT_MMAP_PACKETS * MmapRequest) +{ + return HyperDbgPtMmapSendRequest(MmapRequest); +} diff --git a/hyperdbg/libhyperdbg/header/libhyperdbg.h b/hyperdbg/libhyperdbg/header/app/libhyperdbg.h similarity index 97% rename from hyperdbg/libhyperdbg/header/libhyperdbg.h rename to hyperdbg/libhyperdbg/header/app/libhyperdbg.h index b43fce05..ca3dce77 100644 --- a/hyperdbg/libhyperdbg/header/libhyperdbg.h +++ b/hyperdbg/libhyperdbg/header/app/libhyperdbg.h @@ -15,6 +15,9 @@ // Functions // ////////////////////////////////////////////////// +BOOLEAN +HyperDbgIsAnyModuleLoaded(); + INT HyperDbgUnloadAllModules(); diff --git a/hyperdbg/libhyperdbg/header/messaging.h b/hyperdbg/libhyperdbg/header/app/messaging.h similarity index 100% rename from hyperdbg/libhyperdbg/header/messaging.h rename to hyperdbg/libhyperdbg/header/app/messaging.h diff --git a/hyperdbg/libhyperdbg/header/packets.h b/hyperdbg/libhyperdbg/header/app/packets.h similarity index 100% rename from hyperdbg/libhyperdbg/header/packets.h rename to hyperdbg/libhyperdbg/header/app/packets.h diff --git a/hyperdbg/libhyperdbg/header/common.h b/hyperdbg/libhyperdbg/header/common/common.h similarity index 98% rename from hyperdbg/libhyperdbg/header/common.h rename to hyperdbg/libhyperdbg/header/common/common.h index 70281a36..bdf2a82b 100644 --- a/hyperdbg/libhyperdbg/header/common.h +++ b/hyperdbg/libhyperdbg/header/common/common.h @@ -102,7 +102,7 @@ &g_KernelSyncronizationObjectsHandleTable[KernelSyncObjectId]; \ \ SyncronizationObject->IsOnWaitingState = TRUE; \ - WaitForSingleObject(SyncronizationObject->EventHandle, INFINITE); \ + PlatformWaitForSingleObject(SyncronizationObject->EventHandle, INFINITE); \ \ } while (FALSE); @@ -113,7 +113,7 @@ &g_UserSyncronizationObjectsHandleTable[UserSyncObjectId]; \ \ SyncronizationObject->IsOnWaitingState = TRUE; \ - WaitForSingleObject(SyncronizationObject->EventHandle, INFINITE); \ + PlatformWaitForSingleObject(SyncronizationObject->EventHandle, INFINITE); \ \ } while (FALSE); diff --git a/hyperdbg/libhyperdbg/header/list.h b/hyperdbg/libhyperdbg/header/common/list.h similarity index 100% rename from hyperdbg/libhyperdbg/header/list.h rename to hyperdbg/libhyperdbg/header/common/list.h diff --git a/hyperdbg/libhyperdbg/header/commands.h b/hyperdbg/libhyperdbg/header/debugger/commands/commands.h similarity index 100% rename from hyperdbg/libhyperdbg/header/commands.h rename to hyperdbg/libhyperdbg/header/debugger/commands/commands.h diff --git a/hyperdbg/libhyperdbg/header/help.h b/hyperdbg/libhyperdbg/header/debugger/commands/help.h similarity index 100% rename from hyperdbg/libhyperdbg/header/help.h rename to hyperdbg/libhyperdbg/header/debugger/commands/help.h diff --git a/hyperdbg/libhyperdbg/header/communication.h b/hyperdbg/libhyperdbg/header/debugger/communication/communication.h similarity index 100% rename from hyperdbg/libhyperdbg/header/communication.h rename to hyperdbg/libhyperdbg/header/debugger/communication/communication.h diff --git a/hyperdbg/libhyperdbg/header/forwarding.h b/hyperdbg/libhyperdbg/header/debugger/communication/forwarding.h similarity index 100% rename from hyperdbg/libhyperdbg/header/forwarding.h rename to hyperdbg/libhyperdbg/header/debugger/communication/forwarding.h diff --git a/hyperdbg/libhyperdbg/header/namedpipe.h b/hyperdbg/libhyperdbg/header/debugger/communication/namedpipe.h similarity index 100% rename from hyperdbg/libhyperdbg/header/namedpipe.h rename to hyperdbg/libhyperdbg/header/debugger/communication/namedpipe.h diff --git a/hyperdbg/libhyperdbg/header/debugger.h b/hyperdbg/libhyperdbg/header/debugger/core/debugger.h similarity index 98% rename from hyperdbg/libhyperdbg/header/debugger.h rename to hyperdbg/libhyperdbg/header/debugger/core/debugger.h index e5687685..9ac1012c 100644 --- a/hyperdbg/libhyperdbg/header/debugger.h +++ b/hyperdbg/libhyperdbg/header/debugger/core/debugger.h @@ -319,3 +319,9 @@ HyperDbgDisableTransparentMode(); BOOLEAN HyperDbgLbrdumpSendRequest(HYPERTRACE_LBR_DUMP_PACKETS * LbrdumpRequest); + +BOOLEAN +HyperDbgPerformPtOperation(HYPERTRACE_PT_OPERATION_PACKETS * PtRequest); + +BOOLEAN +HyperDbgPtMmapSendRequest(HYPERTRACE_PT_MMAP_PACKETS * MmapRequest); diff --git a/hyperdbg/libhyperdbg/header/steppings.h b/hyperdbg/libhyperdbg/header/debugger/core/steppings.h similarity index 100% rename from hyperdbg/libhyperdbg/header/steppings.h rename to hyperdbg/libhyperdbg/header/debugger/core/steppings.h diff --git a/hyperdbg/libhyperdbg/header/install.h b/hyperdbg/libhyperdbg/header/debugger/driver-loader/install.h similarity index 100% rename from hyperdbg/libhyperdbg/header/install.h rename to hyperdbg/libhyperdbg/header/debugger/driver-loader/install.h diff --git a/hyperdbg/libhyperdbg/header/kd.h b/hyperdbg/libhyperdbg/header/debugger/kernel-level/kd.h similarity index 98% rename from hyperdbg/libhyperdbg/header/kd.h rename to hyperdbg/libhyperdbg/header/debugger/kernel-level/kd.h index da50ff2b..9a7e2798 100644 --- a/hyperdbg/libhyperdbg/header/kd.h +++ b/hyperdbg/libhyperdbg/header/debugger/kernel-level/kd.h @@ -15,6 +15,8 @@ // Display Windows Details // ////////////////////////////////////////////////// +#ifdef _WIN32 +// HKEY / RegCloseKey are Windows registry APIs; this RAII helper is Windows-only struct HKeyHolder { private: @@ -37,6 +39,7 @@ public: HKEY * operator&() { return &m_Key; } }; +#endif // _WIN32 ////////////////////////////////////////////////// // Functions // diff --git a/hyperdbg/libhyperdbg/header/assembler.h b/hyperdbg/libhyperdbg/header/debugger/misc/assembler.h similarity index 100% rename from hyperdbg/libhyperdbg/header/assembler.h rename to hyperdbg/libhyperdbg/header/debugger/misc/assembler.h diff --git a/hyperdbg/libhyperdbg/header/inipp.h b/hyperdbg/libhyperdbg/header/debugger/misc/inipp.h similarity index 100% rename from hyperdbg/libhyperdbg/header/inipp.h rename to hyperdbg/libhyperdbg/header/debugger/misc/inipp.h diff --git a/hyperdbg/libhyperdbg/header/pci-id.h b/hyperdbg/libhyperdbg/header/debugger/misc/pci-id.h similarity index 99% rename from hyperdbg/libhyperdbg/header/pci-id.h rename to hyperdbg/libhyperdbg/header/debugger/misc/pci-id.h index 2ea4dabb..b2196274 100644 --- a/hyperdbg/libhyperdbg/header/pci-id.h +++ b/hyperdbg/libhyperdbg/header/debugger/misc/pci-id.h @@ -46,6 +46,7 @@ typedef struct Vendor ////////////////////////////////////////////////// // Functions // ////////////////////////////////////////////////// + Vendor * GetVendorById(UINT16 VendorId); void diff --git a/hyperdbg/libhyperdbg/header/debugger/misc/pt-helper.h b/hyperdbg/libhyperdbg/header/debugger/misc/pt-helper.h new file mode 100644 index 00000000..b3f595f4 --- /dev/null +++ b/hyperdbg/libhyperdbg/header/debugger/misc/pt-helper.h @@ -0,0 +1,28 @@ +/** + * @file pt-helper.h + * @author Sina Karvandi (sina@hyperdbg.org) + * @brief Headers for PT helper functions + * @details + * @version 0.21 + * @date 2026-07-03 + * + * @copyright This project is released under the GNU Public License v3. + * + */ +#pragma once + +////////////////////////////////////////////////// +// Functions // +////////////////////////////////////////////////// + +UINT64 +PtHelperDecodeCorePackets(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size, UINT64 ImageBase); + +UINT64 +PtHelperDecodeCore(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size, IMAGE_SYMBOL_CONTEXT * Ctx); + +BOOLEAN +PtHelperCaptureImage(HANDLE Process, UINT64 * TextStart, UINT64 * TextEnd, IMAGE_SYMBOL_CONTEXT * Ctx); + +BOOLEAN +PtHelperResolveFunction(HANDLE Process, const CHAR * Path, const CHAR * Name, UINT64 ImageBase, UINT64 * Start, UINT64 * End); diff --git a/hyperdbg/libhyperdbg/header/script-engine.h b/hyperdbg/libhyperdbg/header/debugger/script-engine/script-engine.h similarity index 100% rename from hyperdbg/libhyperdbg/header/script-engine.h rename to hyperdbg/libhyperdbg/header/debugger/script-engine/script-engine.h diff --git a/hyperdbg/libhyperdbg/header/symbol.h b/hyperdbg/libhyperdbg/header/debugger/script-engine/symbol.h similarity index 63% rename from hyperdbg/libhyperdbg/header/symbol.h rename to hyperdbg/libhyperdbg/header/debugger/script-engine/symbol.h index 6fab7ff4..b80cdc45 100644 --- a/hyperdbg/libhyperdbg/header/symbol.h +++ b/hyperdbg/libhyperdbg/header/debugger/script-engine/symbol.h @@ -26,6 +26,53 @@ typedef struct _LOCAL_FUNCTION_DESCRIPTION } LOCAL_FUNCTION_DESCRIPTION, *PLOCAL_FUNCTION_DESCRIPTION; +/** + * @brief Save the local module symbols' description + * + */ +typedef struct _IMAGE_SYMBOL_CONTEXT +{ + UINT64 ImageBase; + UINT64 CodeBase; + UINT64 CodeSize; + UINT8 * Code; +} IMAGE_SYMBOL_CONTEXT; + +/* + * @brief Process basic information structure + */ +typedef struct _PROC_BASIC_INFO +{ + LONG ExitStatus; + PVOID PebBaseAddress; + ULONG_PTR Reserved[4]; +} PROC_BASIC_INFO; + +/** + * @brief Thread basic information + * + */ +typedef struct _THREAD_BASIC_INFO_EX +{ + LONG ExitStatus; + PVOID TebBaseAddress; + struct + { + HANDLE UniqueProcess; + HANDLE UniqueThread; + } ClientId; + ULONG_PTR AffinityMask; + LONG Priority; + LONG BasePriority; +} THREAD_BASIC_INFO_EX; + +////////////////////////////////////////////////// +// Function Defs // +////////////////////////////////////////////////// + +typedef LONG(NTAPI * PFN_NT_QIP)(HANDLE, ULONG, PVOID, ULONG, PULONG); +typedef LONG(NTAPI * PFN_NT_QIT)(HANDLE, ULONG, PVOID, ULONG, PULONG); + ////////////////////////////////////////////////// // Pdbex // ////////////////////////////////////////////////// @@ -72,5 +119,8 @@ SymbolPrepareDebuggerWithSymbolInfo(UINT32 UserProcessId); BOOLEAN SymbolReloadSymbolTableInDebuggerMode(UINT32 ProcessId); +#ifdef _WIN32 +// PRTL_PROCESS_MODULES is from winternl.h — Windows-only BOOLEAN SymbolCheckAndAllocateModuleInformation(PRTL_PROCESS_MODULES * Modules); +#endif // _WIN32 diff --git a/hyperdbg/libhyperdbg/header/tests.h b/hyperdbg/libhyperdbg/header/debugger/tests/tests.h similarity index 100% rename from hyperdbg/libhyperdbg/header/tests.h rename to hyperdbg/libhyperdbg/header/debugger/tests/tests.h diff --git a/hyperdbg/libhyperdbg/header/transparency.h b/hyperdbg/libhyperdbg/header/debugger/transparency/transparency.h similarity index 100% rename from hyperdbg/libhyperdbg/header/transparency.h rename to hyperdbg/libhyperdbg/header/debugger/transparency/transparency.h diff --git a/hyperdbg/libhyperdbg/header/pe-parser.h b/hyperdbg/libhyperdbg/header/debugger/user-level/pe-parser.h similarity index 100% rename from hyperdbg/libhyperdbg/header/pe-parser.h rename to hyperdbg/libhyperdbg/header/debugger/user-level/pe-parser.h diff --git a/hyperdbg/libhyperdbg/header/ud.h b/hyperdbg/libhyperdbg/header/debugger/user-level/ud.h similarity index 100% rename from hyperdbg/libhyperdbg/header/ud.h rename to hyperdbg/libhyperdbg/header/debugger/user-level/ud.h diff --git a/hyperdbg/libhyperdbg/header/export.h b/hyperdbg/libhyperdbg/header/export/export.h similarity index 100% rename from hyperdbg/libhyperdbg/header/export.h rename to hyperdbg/libhyperdbg/header/export/export.h diff --git a/hyperdbg/libhyperdbg/header/globals.h b/hyperdbg/libhyperdbg/header/globals/globals.h similarity index 99% rename from hyperdbg/libhyperdbg/header/globals.h rename to hyperdbg/libhyperdbg/header/globals/globals.h index 7838e15b..bf8dfd11 100644 --- a/hyperdbg/libhyperdbg/header/globals.h +++ b/hyperdbg/libhyperdbg/header/globals/globals.h @@ -317,10 +317,12 @@ DEBUGGER_EVENT_AND_ACTION_RESULT g_DebuggeeResultOfAddingActionsToEvent = { * to write simultaneously but it's needed for write) * */ +#ifdef _WIN32 OVERLAPPED g_OverlappedIoStructureForReadDebugger = {0}; OVERLAPPED g_OverlappedIoStructureForWriteDebugger = {0}; OVERLAPPED g_OverlappedIoStructureForReadDebuggee = {0}; +#endif // _WIN32 /** * @brief Shows whether the queried event is enabled or disabled diff --git a/hyperdbg/libhyperdbg/header/hwdbg-interpreter.h b/hyperdbg/libhyperdbg/header/hwdbg/hwdbg-interpreter.h similarity index 100% rename from hyperdbg/libhyperdbg/header/hwdbg-interpreter.h rename to hyperdbg/libhyperdbg/header/hwdbg/hwdbg-interpreter.h diff --git a/hyperdbg/libhyperdbg/header/hwdbg-scripts.h b/hyperdbg/libhyperdbg/header/hwdbg/hwdbg-scripts.h similarity index 100% rename from hyperdbg/libhyperdbg/header/hwdbg-scripts.h rename to hyperdbg/libhyperdbg/header/hwdbg/hwdbg-scripts.h diff --git a/hyperdbg/libhyperdbg/header/objects.h b/hyperdbg/libhyperdbg/header/objects/objects.h similarity index 100% rename from hyperdbg/libhyperdbg/header/objects.h rename to hyperdbg/libhyperdbg/header/objects/objects.h diff --git a/hyperdbg/libhyperdbg/header/rev-ctrl.h b/hyperdbg/libhyperdbg/header/rev/rev-ctrl.h similarity index 100% rename from hyperdbg/libhyperdbg/header/rev-ctrl.h rename to hyperdbg/libhyperdbg/header/rev/rev-ctrl.h diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj index ec6a41f3..cd1d0655 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj @@ -21,14 +21,14 @@ DynamicLibrary true - v143 + v145 NotSet false DynamicLibrary false - v143 + v145 true NotSet false @@ -78,7 +78,7 @@ Windows true false - $(SolutionDir)libraries\zydis\user\Zycore.lib;$(SolutionDir)libraries\zydis\user\Zydis.lib;$(SolutionDir)build\bin\$(Configuration)\script-engine.lib;$(SolutionDir)libraries\keystone\$(Configuration)-lib\keystone.lib;%(AdditionalDependencies) + $(SolutionDir)libraries\zydis\user\Zycore.lib;$(SolutionDir)libraries\zydis\user\Zydis.lib;$(SolutionDir)build\bin\$(Configuration)\script-engine.lib;$(SolutionDir)libraries\keystone\$(Configuration)-lib\keystone.lib;$(SolutionDir)libraries\libipt\libipt.lib;%(AdditionalDependencies) %(AdditionalLibraryDirectories) false @@ -87,7 +87,9 @@ - msbuild "$(SolutionDir)dependencies\zydis\msvc\Zydis.sln" /m /p:Configuration="Release MT" /p:Platform=$(Platform) /target:zydis /target:zycore /p:OutDir="$(SolutionDir)libraries\zydis\user" + copy "$(SolutionDir)libraries\libipt\libipt.lib" "$(OutDir)" +copy "$(SolutionDir)libraries\libipt\libipt.dll" "$(OutDir)" +msbuild "$(SolutionDir)dependencies\zydis\msvc\Zydis.sln" /m /p:Configuration="Release MT" /p:Platform=$(Platform) /target:zydis /target:zycore /p:OutDir="$(SolutionDir)libraries\zydis\user" @@ -114,7 +116,7 @@ true true false - $(SolutionDir)libraries\zydis\user\Zycore.lib;$(SolutionDir)libraries\zydis\user\Zydis.lib;$(SolutionDir)build\bin\$(Configuration)\script-engine.lib;$(SolutionDir)libraries\keystone\$(Configuration)-lib\keystone.lib;%(AdditionalDependencies) + $(SolutionDir)libraries\zydis\user\Zycore.lib;$(SolutionDir)libraries\zydis\user\Zydis.lib;$(SolutionDir)build\bin\$(Configuration)\script-engine.lib;$(SolutionDir)libraries\keystone\$(Configuration)-lib\keystone.lib;$(SolutionDir)libraries\libipt\libipt.lib;%(AdditionalDependencies) %(AdditionalLibraryDirectories) true @@ -123,48 +125,60 @@ - msbuild "$(SolutionDir)dependencies\zydis\msvc\Zydis.sln" /m /p:Configuration="Release MT" /p:Platform=$(Platform) /target:zydis /target:zycore /p:OutDir="$(SolutionDir)libraries\zydis\user" + copy "$(SolutionDir)libraries\libipt\libipt.lib" "$(OutDir)" +copy "$(SolutionDir)libraries\libipt\libipt.dll" "$(OutDir)" +msbuild "$(SolutionDir)dependencies\zydis\msvc\Zydis.sln" /m /p:Configuration="Release MT" /p:Platform=$(Platform) /target:zydis /target:zycore /p:OutDir="$(SolutionDir)libraries\zydis\user" + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -218,6 +232,7 @@ + @@ -237,7 +252,6 @@ - diff --git a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters index 99d0aa4a..492c7f91 100644 --- a/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters +++ b/hyperdbg/libhyperdbg/libhyperdbg.vcxproj.filters @@ -102,103 +102,85 @@ {da7e68cc-540c-4efc-b4de-c23b4b13e2ec} + + {d6010267-4888-46f5-9bdb-2339565710d2} + + + {e81c07d3-5e98-4bdb-a00b-2b99e023553c} + + + {477d3059-08d7-433b-83c3-bbf57658deaf} + + + {559b68f3-8bf0-447a-a714-0e355c22516f} + + + {88de3dbf-90ab-47ef-94fe-8c21ce73c9f1} + + + {c1f438ff-fd3b-4fb4-989b-f145290e3837} + + + {c5b26259-abcc-459f-9b7e-fd90c34ad646} + + + {99425a01-168c-4b6b-a53f-d62fbb7c8cab} + + + {c7f85132-1802-4462-990f-3fffb0365dbc} + + + {4f4eca9f-c1f6-4342-91ef-2b533292e238} + + + {bde7408e-fad9-4329-98ac-e604674f7bbf} + + + {06e9888f-57f0-4d59-8f7e-1fdd336be9cc} + + + {0d88d581-8ff4-40f2-8585-3fdb97d4d211} + + + {439dcb49-1825-4076-affe-17b505e4033a} + + + {16331b3c-c8fd-4b12-abb4-7617d21db9d0} + + + {2d2c16a6-3fdc-4b63-a282-f2762a1f340d} + + + {9dcff6b3-9ebd-46f3-a0c5-c69d06357949} + + + {3e4dda9f-6050-409f-85e2-4cf7c8a95743} + header - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - - - header - header\platform - - header - - - header - - - header - - - header - - - header - - - header - header\platform - - header + + header\platform - - header + + header\platform + + + header\platform + + + header\platform + + + header\platform header\platform\windows-only @@ -206,6 +188,96 @@ header\components\pe + + header\app + + + header\app + + + header\app + + + header\common + + + header\common + + + header\export + + + header\globals + + + header\hwdbg + + + header\hwdbg + + + header\rev + + + header\objects + + + header\debugger\commands + + + header\debugger\commands + + + header\debugger\communication + + + header\debugger\communication + + + header\debugger\communication + + + header\debugger\core + + + header\debugger\core + + + header\debugger\driver-loader + + + header\debugger\kernel-level + + + header\debugger\misc + + + header\debugger\misc + + + header\debugger\misc + + + header\debugger\user-level + + + header\debugger\user-level + + + header\debugger\transparency + + + header\debugger\tests + + + header\debugger\script-engine + + + header\debugger\script-engine + + + header\debugger\misc + @@ -220,9 +292,6 @@ code\common - - code\common - code\debugger\commands\debugging-commands @@ -640,6 +709,18 @@ code\platform + + code\platform + + + code\platform + + + code\platform + + + code\platform + code\app @@ -652,6 +733,9 @@ code\components\pe + + code\debugger\misc + diff --git a/hyperdbg/libhyperdbg/pch.cpp b/hyperdbg/libhyperdbg/pch.cpp index 92958c22..fa15fce4 100644 --- a/hyperdbg/libhyperdbg/pch.cpp +++ b/hyperdbg/libhyperdbg/pch.cpp @@ -14,7 +14,7 @@ // // Global variables // -#include "header/globals.h" +#include "header/globals/globals.h" // // When you are using pre-compiled headers, this source file is necessary for diff --git a/hyperdbg/libhyperdbg/pch.h b/hyperdbg/libhyperdbg/pch.h index dffab754..6a6eaee8 100644 --- a/hyperdbg/libhyperdbg/pch.h +++ b/hyperdbg/libhyperdbg/pch.h @@ -41,41 +41,46 @@ typedef RFLAGS * PRFLAGS; #define USE_NATIVE_SDK_HEADERS #define _AMD64_ -#if defined(USE__NATIVE_PHNT_HEADERS) +#ifdef _WIN32 +# if defined(USE__NATIVE_PHNT_HEADERS) // // Dirty fix: the "PCWCHAR" in undefined in "ntrtl.h" so I deifined it here. // typedef const wchar_t *LPCWCHAR, *PCWCHAR; -# define PHNT_MODE PHNT_MODE_USER -# define PHNT_VERSION PHNT_WIN11 // Windows 11 -# define PHNT_PATCH_FOR_HYPERDBG TRUE +# define PHNT_MODE PHNT_MODE_USER +# define PHNT_VERSION PHNT_WIN11 // Windows 11 +# define PHNT_PATCH_FOR_HYPERDBG TRUE -# include -# include +# include +# include -#elif defined(USE_NATIVE_SDK_HEADERS) +# elif defined(USE_NATIVE_SDK_HEADERS) -# include -# include -# include -# include +# include +# include +# include +# include +# endif + +#endif //_WIN32 + +#ifdef _WIN32 +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include #endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include #include #include #include @@ -102,6 +107,7 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; #include #include #include +#include // // Scope definitions @@ -117,11 +123,6 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; # define NDEBUG #endif // !NDEBUG -// -// Keystone -// -#include "keystone/keystone.h" - // // HyperDbg defined headers // @@ -129,6 +130,11 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; #include "config/Definition.h" #include "SDK/HyperDbgSdk.h" +// +// Keystone +// +#include "keystone/keystone.h" + // // Script-engine // @@ -141,55 +147,101 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; #include "SDK/imports/user/HyperDbgLibImports.h" // -// Platform-specific intrinsics +// Platform lib calls (cross-platform wrappers) +// +#include "platform/user/header/platform-lib-calls.h" + +// +// Platform intrinsics (cross-platform CPU instructions and atomic ops) // #include "platform/user/header/platform-intrinsics.h" -#include "platform/user/header/windows-only/windows-privilege.h" + +// +// Platform serial transport (cross-platform kernel-debugger serial I/O) +// +#include "platform/user/header/platform-serial.h" + +// +// Platform IOCTL transport (cross-platform local kernel-driver device I/O) +// +#include "platform/user/header/platform-ioctl.h" + +// +// Platform signal (cross-platform console-control / CTRL+C handler registration) +// +#include "platform/user/header/platform-signal.h" + +// +// NT-style intrusive linked-list helpers + CONTAINING_RECORD (self-guards to +// non-Windows; Windows gets these from / the native-SDK shim) +// +#include "platform/general/header/nt-list.h" + +// +// Platform-specific intrinsics +// +#ifdef _WIN32 +# include "platform/user/header/windows-only/windows-privilege.h" +#endif // // PCI IDs // -#include "header/pci-id.h" +#include "header/debugger/misc/pci-id.h" + +// +// Intel PT +// +#include "../dependencies/libipt/intel-pt.h" // // General // -#include "header/libhyperdbg.h" -#include "header/export.h" -#include "header/inipp.h" -#include "header/commands.h" -#include "header/common.h" -#include "header/symbol.h" -#include "header/debugger.h" -#include "header/script-engine.h" -#include "header/help.h" -#include "header/install.h" -#include "header/list.h" -#include "header/tests.h" -#include "header/messaging.h" -#include "header/packets.h" -#include "header/transparency.h" -#include "header/communication.h" -#include "header/namedpipe.h" -#include "header/forwarding.h" -#include "header/kd.h" -#include "header/pe-parser.h" -#include "header/ud.h" -#include "header/objects.h" -#include "header/steppings.h" -#include "header/rev-ctrl.h" -#include "header/assembler.h" +#include "header/app/libhyperdbg.h" +#include "header/export/export.h" +#include "header/debugger/misc/inipp.h" +#include "header/debugger/commands/commands.h" +#include "header/common/common.h" +#include "header/debugger/script-engine/symbol.h" +#include "header/debugger/misc/pt-helper.h" +#include "header/debugger/core/debugger.h" +#include "header/debugger/script-engine/script-engine.h" +#include "header/debugger/commands/help.h" +#ifdef _WIN32 +# include "header/debugger/driver-loader/install.h" +#endif +#include "header/common/list.h" +#include "header/debugger/tests/tests.h" +#include "header/app/messaging.h" +#include "header/app/packets.h" +#include "header/debugger/transparency/transparency.h" +#include "header/debugger/communication/communication.h" +#include "header/debugger/communication/namedpipe.h" +#include "header/debugger/communication/forwarding.h" +#include "header/debugger/kernel-level/kd.h" // // Components // #include "../include/components/pe/header/pe-image-reader.h" +#include "header/debugger/user-level/pe-parser.h" +#include "header/debugger/user-level/ud.h" +#include "header/objects/objects.h" +#include "header/debugger/core/steppings.h" +#include "header/rev/rev-ctrl.h" +#include "header/debugger/misc/assembler.h" + // // hwdbg // -#include "header/hwdbg-interpreter.h" -#include "header/hwdbg-scripts.h" +#include "header/hwdbg/hwdbg-interpreter.h" +#include "header/hwdbg/hwdbg-scripts.h" + +// +// Zydis headers +// +#include // // Libraries @@ -220,4 +272,9 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR; # pragma comment(lib, "Psapi.lib") # pragma comment(lib, "Kernel32.lib") +// +// For resolving symbols on Intel PT +// +# pragma comment(lib, "dbghelp.lib") + #endif // HYPERDBG_ENV_WINDOWS diff --git a/hyperdbg/libraries/libipt/libipt.dll b/hyperdbg/libraries/libipt/libipt.dll new file mode 100644 index 00000000..7c873442 Binary files /dev/null and b/hyperdbg/libraries/libipt/libipt.dll differ diff --git a/hyperdbg/libraries/libipt/libipt.lib b/hyperdbg/libraries/libipt/libipt.lib new file mode 100644 index 00000000..8f04d94f Binary files /dev/null and b/hyperdbg/libraries/libipt/libipt.lib differ diff --git a/hyperdbg/linux/README.md b/hyperdbg/linux/README.md index 6ee5f6a6..7071f314 100644 --- a/hyperdbg/linux/README.md +++ b/hyperdbg/linux/README.md @@ -6,4 +6,46 @@ Linux is currently **not supported**. -We are in the process of porting HyperDbg to Linux. This effort is ongoing and may take some time to complete. \ No newline at end of file +We are in the process of porting HyperDbg to Linux. This effort is ongoing and may take some time to complete. + +# Contributing to the Linux Port + +HyperDbg is being ported from Windows to Linux. The work is incremental: most +of the codebase compiles file-by-file as the Win32-specific calls get replaced +with a platform-independent interface. + +## Building + +```bash +cmake . # generate the Makefiles +make # build +``` + +Run these from the repo root (or the relevant subdirectory). `cmake .` only +needs to be re-run when the CMake files change; otherwise `make` is enough. + +## How to contribute + +The port progresses one source file at a time. To pick up work: + +1. Build and find the next file that fails to compile. +2. Go through it and make it Linux-compatible. +3. Where the code calls Windows-only APIs (serial, registry, signals, + threads, etc.), don't `#ifdef` inline — route the call through the + **platform-independent interface** so both Windows and Linux share the same + call site with separate backing implementations. +4. Rebuild until the file is clean, then move to the next one. + +## The platform interface + +User-mode abstractions live in `include/platform/user/` (`header/` for the +interface, `code/` for the implementations). See existing examples such as +`platform-serial`, `platform-signal`, and `platform-ioctl` for the pattern to +follow. Kernel-mode equivalents are under `include/platform/kernel/`. + +## Guidelines + +- Keep platform-specific code behind the platform abstraction, not scattered + through the logic. +- Match the surrounding code's style and conventions. +- Build before submitting — every changed file should compile. \ No newline at end of file diff --git a/hyperdbg/script-engine/script-engine.vcxproj b/hyperdbg/script-engine/script-engine.vcxproj index dfe4b2d0..e6abe194 100644 --- a/hyperdbg/script-engine/script-engine.vcxproj +++ b/hyperdbg/script-engine/script-engine.vcxproj @@ -21,14 +21,14 @@ DynamicLibrary true - v143 + v145 Unicode false DynamicLibrary false - v143 + v145 true Unicode diff --git a/hyperdbg/script-eval/code/Functions.c b/hyperdbg/script-eval/code/Functions.c index 46562663..17b0fc0e 100644 --- a/hyperdbg/script-eval/code/Functions.c +++ b/hyperdbg/script-eval/code/Functions.c @@ -797,16 +797,16 @@ VOID UserModeMicroSleep(UINT64 Us) { LARGE_INTEGER Start, End, Frequency; - QueryPerformanceFrequency(&Frequency); + PlatformQueryPerformanceFrequency(&Frequency); LONGLONG TickPerUs = Frequency.QuadPart / 1000000; LONGLONG Ticks = TickPerUs * Us; - QueryPerformanceCounter(&Start); + PlatformQueryPerformanceCounter(&Start); while (TRUE) { - QueryPerformanceCounter(&End); + PlatformQueryPerformanceCounter(&End); if (End.QuadPart - Start.QuadPart > Ticks) { @@ -840,7 +840,7 @@ ScriptEngineFunctionMicroSleep(UINT64 Us) UINT64 ScriptEngineFunctionRdtsc() { - return __rdtsc(); + return CpuReadTsc(); } /** @@ -850,8 +850,8 @@ ScriptEngineFunctionRdtsc() UINT64 ScriptEngineFunctionRdtscp() { - unsigned int Aux; - return __rdtscp(&Aux); + UINT32 Aux; + return CpuReadTscp(&Aux); } /** @@ -879,7 +879,7 @@ ScriptEngineFunctionInterlockedExchange(long long volatile * Target, #endif // SCRIPT_ENGINE_KERNEL_MODE - Result = InterlockedExchange64(Target, Value); + Result = CpuInterlockedExchange64(Target, Value); return Result; } @@ -909,7 +909,7 @@ ScriptEngineFunctionInterlockedExchangeAdd(long long volatile * Addend, #endif // SCRIPT_ENGINE_KERNEL_MODE - Result = InterlockedExchangeAdd64(Addend, Value); + Result = CpuInterlockedExchangeAdd64(Addend, Value); return Result; } @@ -937,7 +937,7 @@ ScriptEngineFunctionInterlockedIncrement(long long volatile * Addend, #endif // SCRIPT_ENGINE_KERNEL_MODE - Result = InterlockedIncrement64(Addend); + Result = CpuInterlockedIncrement64(Addend); return Result; } @@ -965,7 +965,7 @@ ScriptEngineFunctionInterlockedDecrement(long long volatile * Addend, #endif // SCRIPT_ENGINE_KERNEL_MODE - Result = InterlockedDecrement64(Addend); + Result = CpuInterlockedDecrement64(Addend); return Result; } @@ -998,7 +998,7 @@ ScriptEngineFunctionInterlockedCompareExchange( #endif // SCRIPT_ENGINE_KERNEL_MODE - Result = InterlockedCompareExchange64(Destination, ExChange, Comperand); + Result = CpuInterlockedCompareExchange64(Destination, ExChange, Comperand); return Result; } @@ -1338,7 +1338,7 @@ ApplyFormatSpecifier(const CHAR * CurrentSpecifier, CHAR * FinalBuffer, PUINT32 *CurrentProcessedPositionFromStartOfFormat = *CurrentProcessedPositionFromStartOfFormat + (UINT32)strlen(CurrentSpecifier); - sprintf_s(TempBuffer, sizeof(TempBuffer), CurrentSpecifier, Val); + PlatformSprintf(TempBuffer, sizeof(TempBuffer), CurrentSpecifier, Val); TempBufferLen = (UINT32)strlen(TempBuffer); // @@ -1473,8 +1473,8 @@ ApplyStringFormatSpecifier(const CHAR * CurrentSpecifier, CHAR * FinalBuffer, PU // // Zero the buffers // - RtlZeroMemory(WstrBuffer, sizeof(WstrBuffer)); - RtlZeroMemory(AsciiBuffer, sizeof(AsciiBuffer)); + PlatformZeroMemory(WstrBuffer, sizeof(WstrBuffer)); + PlatformZeroMemory(AsciiBuffer, sizeof(AsciiBuffer)); // // Check for the last block diff --git a/hyperdbg/script-eval/code/PseudoRegisters.c b/hyperdbg/script-eval/code/PseudoRegisters.c index a3a46178..493c8a6f 100644 --- a/hyperdbg/script-eval/code/PseudoRegisters.c +++ b/hyperdbg/script-eval/code/PseudoRegisters.c @@ -25,7 +25,7 @@ UINT64 ScriptEnginePseudoRegGetTid() { #ifdef SCRIPT_ENGINE_USER_MODE - return (UINT64)GetCurrentThreadId(); + return (UINT64)PlatformGetCurrentThreadId(); #endif // SCRIPT_ENGINE_USER_MODE #ifdef SCRIPT_ENGINE_KERNEL_MODE @@ -42,7 +42,7 @@ UINT64 ScriptEnginePseudoRegGetCore() { #ifdef SCRIPT_ENGINE_USER_MODE - return (UINT64)GetCurrentProcessorNumber(); + return (UINT64)PlatformGetCurrentProcessorNumber(); #endif // SCRIPT_ENGINE_USER_MODE #ifdef SCRIPT_ENGINE_KERNEL_MODE @@ -59,7 +59,7 @@ UINT64 ScriptEnginePseudoRegGetPid() { #ifdef SCRIPT_ENGINE_USER_MODE - return (UINT64)GetCurrentProcessId(); + return (UINT64)PlatformGetCurrentProcessId(); #endif // SCRIPT_ENGINE_USER_MODE #ifdef SCRIPT_ENGINE_KERNEL_MODE @@ -76,39 +76,7 @@ CHAR * ScriptEnginePseudoRegGetPname() { #ifdef SCRIPT_ENGINE_USER_MODE - - HANDLE Handle = OpenProcess( - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, - FALSE, - GetCurrentProcessId() /* Current process */ - ); - - if (Handle) - { - CHAR CurrentModulePath[MAX_PATH] = {0}; - if (GetModuleFileNameEx(Handle, 0, CurrentModulePath, MAX_PATH)) - { - // - // At this point, buffer contains the full path to the executable - // - CloseHandle(Handle); - return PathFindFileNameA(CurrentModulePath); - } - else - { - // - // error might be shown by GetLastError() - // - CloseHandle(Handle); - return NULL; - } - } - - // - // unable to get handle - // - return NULL; - + return PlatformGetCurrentProcessName(); #endif // SCRIPT_ENGINE_USER_MODE #ifdef SCRIPT_ENGINE_KERNEL_MODE @@ -159,6 +127,7 @@ UINT64 ScriptEnginePseudoRegGetPeb() { #ifdef SCRIPT_ENGINE_USER_MODE +# ifdef _WIN32 // // Hand-rolled structs ( may cause conflict depending on your dev env ) // @@ -269,6 +238,22 @@ ScriptEnginePseudoRegGetPeb() return (UINT64)PebPtr; +# else // !_WIN32 + + // + // The PEB (Process Environment Block) is a Windows NT-specific structure + // maintained by the kernel for every user-mode process. It holds loader + // data, heap pointers, the image path, and other process-wide state that + // has no direct equivalent in the Linux process model. + // + // TODO: investigate whether a meaningful substitute exists on Linux + // (e.g. /proc/self/maps, dl_iterate_phdr, or a custom auxv walk) + // and implement it here if HyperDbg user-mode on Linux ever needs $peb. + // + return 0; + +# endif // _WIN32 + #endif // SCRIPT_ENGINE_USER_MODE #ifdef SCRIPT_ENGINE_KERNEL_MODE diff --git a/hyperdbg/symbol-parser/symbol-parser.vcxproj b/hyperdbg/symbol-parser/symbol-parser.vcxproj index b087b735..62dc6ef9 100644 --- a/hyperdbg/symbol-parser/symbol-parser.vcxproj +++ b/hyperdbg/symbol-parser/symbol-parser.vcxproj @@ -21,13 +21,13 @@ DynamicLibrary true - v143 + v145 Unicode DynamicLibrary false - v143 + v145 true Unicode @@ -119,4 +119,4 @@ - + \ No newline at end of file diff --git a/utils/replace-sdk-wdk.py b/utils/replace-sdk-wdk.py new file mode 100644 index 00000000..ffecbc45 --- /dev/null +++ b/utils/replace-sdk-wdk.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +""" +Walks the parent directory of this script (and all of its subdirectories) +looking for .vcxproj files. +""" + +import os + +TARGET_SDK_WDK_VERSION = "10.0.28000.0" # Update this version in case of updating NuGet packages + +# Only these .vcxproj files will be modified (matched by filename). +TARGET_PROJECTS = [ + "hyperhv.vcxproj", + "hyperkd.vcxproj", + "hypertrace.vcxproj", + "hyperlog.vcxproj", + "hyperevade.vcxproj", + "hyperperf.vcxproj", + "kdserial.vcxproj", + "hyperdbg_driver.vcxproj", +] + +REPLACEMENTS = { + "10.0": "" + TARGET_SDK_WDK_VERSION + "", + "$(LatestTargetPlatformVersion)": "" + TARGET_SDK_WDK_VERSION + "", +} + +# Directory *names* to skip wherever they appear in the tree. +EXCLUDED_DIR_NAMES = {".git"} + +def replace_in_file(filepath: str) -> bool: + with open(filepath, "r", encoding="utf-8", errors="ignore") as f: + content = f.read() + + original = content + for old, new in REPLACEMENTS.items(): + content = content.replace(old, new) + + if content != original: + with open(filepath, "w", encoding="utf-8") as f: + f.write(content) + return True + return False + + +def main() -> None: + script_path = os.path.abspath(__file__) + script_dir = os.path.dirname(script_path) + target_root = os.path.dirname(script_dir) # parent of this folder + + wanted = set(TARGET_PROJECTS) + found = set() + + print(f"Scanning under: {target_root}") + print(f"Looking for: {', '.join(sorted(wanted))}") + + changed_count = 0 + for root, dirs, files in os.walk(target_root): + # Prune in place so os.walk never descends into these directories. + dirs[:] = [d for d in dirs if d not in EXCLUDED_DIR_NAMES] + + for file in files: + if file in wanted: + filepath = os.path.join(root, file) + found.add(file) + + if replace_in_file(filepath): + print(f"Updated: {filepath}") + changed_count += 1 + else: + print(f"No matching text found, left unchanged: {filepath}") + + missing = wanted - found + for name in sorted(missing): + print(f"Warning: not found anywhere under {target_root}: {name}") + + print(f"Done. {changed_count} file(s) updated.") + + +if __name__ == "__main__": + main()