├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitignore ├── CHANGELOG.md ├── CMakeLists.txt ├── README.md ├── TTD.sln ├── TTD ├── TTD.cpp ├── TTD.hpp ├── TTD.vcxproj ├── TTD.vcxproj.filters ├── csts.h ├── sha256.cpp ├── sha256.h ├── utils.cpp └── utils.h ├── assets ├── lsm_coverage.png ├── tenet.png └── timeline.png ├── cmake.toml ├── cmkr.cmake ├── example_api ├── example_api.py ├── example_api.vcxproj ├── example_api.vcxproj.filters └── main.cpp ├── example_calltree ├── example_calltree.vcxproj ├── example_calltree.vcxproj.filters └── main.cpp ├── example_cov ├── example_cov.vcxproj ├── example_cov.vcxproj.filters └── main.cpp ├── example_diff ├── example_diff.vcxproj ├── example_diff.vcxproj.filters └── main.cpp ├── example_tenet ├── example_tenet.vcxproj ├── example_tenet.vcxproj.filters └── main.cpp └── python-bindings ├── module.cpp ├── python-bindings.vcxproj └── python-bindings.vcxproj.filters /.gitattributes: -------------------------------------------------------------------------------- 1 | # cmkr 2 | /**/CMakeLists.txt linguist-generated 3 | /**/cmkr.cmake linguist-vendored 4 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build_CPP_wrapper: 11 | strategy: 12 | matrix: 13 | version: [Debug, Release] 14 | platform: [x64] 15 | runs-on: windows-latest 16 | steps: 17 | - uses: actions/checkout@v2 18 | - uses: microsoft/setup-msbuild@v1.0.2 19 | - name: Compile LIB 20 | run: msbuild TTD.sln /target:TTD /p:Platform=${{ matrix.platform }} /p:Configuration=${{ matrix.version }} 21 | 22 | build_examples: 23 | strategy: 24 | matrix: 25 | version: [Debug, Release] 26 | platform: [x64] 27 | project: [example_api, example_calltree, example_diff, example_cov, example_tenet] 28 | runs-on: windows-latest 29 | steps: 30 | - uses: actions/checkout@v2 31 | - uses: microsoft/setup-msbuild@v1.0.2 32 | - name: Compile ${{ matrix.project }} 33 | run: msbuild TTD.sln /target:${{ matrix.project }} /p:Platform=${{ matrix.platform }} /p:Configuration=${{ matrix.version }} 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | [Aa][Rr][Mm]/ 24 | [Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_h.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *_wpftmp.csproj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !?*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | 235 | # RIA/Silverlight projects 236 | Generated_Code/ 237 | 238 | # Backup & report files from converting an old project file 239 | # to a newer Visual Studio version. Backup files are not needed, 240 | # because we have git ;-) 241 | _UpgradeReport_Files/ 242 | Backup*/ 243 | UpgradeLog*.XML 244 | UpgradeLog*.htm 245 | ServiceFabricBackup/ 246 | *.rptproj.bak 247 | 248 | # SQL Server files 249 | *.mdf 250 | *.ldf 251 | *.ndf 252 | 253 | # Business Intelligence projects 254 | *.rdl.data 255 | *.bim.layout 256 | *.bim_*.settings 257 | *.rptproj.rsuser 258 | *- Backup*.rdl 259 | 260 | # Microsoft Fakes 261 | FakesAssemblies/ 262 | 263 | # GhostDoc plugin setting file 264 | *.GhostDoc.xml 265 | 266 | # Node.js Tools for Visual Studio 267 | .ntvs_analysis.dat 268 | node_modules/ 269 | 270 | # Visual Studio 6 build log 271 | *.plg 272 | 273 | # Visual Studio 6 workspace options file 274 | *.opt 275 | 276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 277 | *.vbw 278 | 279 | # Visual Studio LightSwitch build output 280 | **/*.HTMLClient/GeneratedArtifacts 281 | **/*.DesktopClient/GeneratedArtifacts 282 | **/*.DesktopClient/ModelManifest.xml 283 | **/*.Server/GeneratedArtifacts 284 | **/*.Server/ModelManifest.xml 285 | _Pvt_Extensions 286 | 287 | # Paket dependency manager 288 | .paket/paket.exe 289 | paket-files/ 290 | 291 | # FAKE - F# Make 292 | .fake/ 293 | 294 | # JetBrains Rider 295 | .idea/ 296 | *.sln.iml 297 | 298 | # CodeRush personal settings 299 | .cr/personal 300 | 301 | # Python Tools for Visual Studio (PTVS) 302 | __pycache__/ 303 | *.pyc 304 | 305 | # Cake - Uncomment if you are using it 306 | # tools/** 307 | # !tools/packages.config 308 | 309 | # Tabs Studio 310 | *.tss 311 | 312 | # Telerik's JustMock configuration file 313 | *.jmconfig 314 | 315 | # BizTalk build output 316 | *.btp.cs 317 | *.btm.cs 318 | *.odx.cs 319 | *.xsd.cs 320 | 321 | # OpenCover UI analysis results 322 | OpenCover/ 323 | 324 | # Azure Stream Analytics local run output 325 | ASALocalRun/ 326 | 327 | # MSBuild Binary and Structured Log 328 | *.binlog 329 | 330 | # NVidia Nsight GPU debugger configuration file 331 | *.nvuser 332 | 333 | # MFractors (Xamarin productivity tool) working folder 334 | .mfractor/ 335 | 336 | # Local History for Visual Studio 337 | .localhistory/ 338 | 339 | # BeatPulse healthcheck temp database 340 | healthchecksdb 341 | 342 | # cmkr 343 | build*/ 344 | cmake-build*/ 345 | .vscode/ 346 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.2] - 2022-07-17 9 | 10 | ### Thanks 11 | 12 | - [Sylvain Peyrefitte](https://github.com/citronneur) 13 | 14 | ### Added/Changed 15 | 16 | - Add support for: 17 | - threads 18 | - events (thread create and terminate, module load and unload) 19 | - x86_64 full context 20 | - exceptions 21 | - memory watchpoints 22 | - Add corresponding Python wrappers 23 | - CI with compilation checks 24 | - Position: 25 | - comparison 26 | - MAX constant 27 | - New examples: 28 | - `example_cov`: module coverage computation 29 | - `example_tenet`: Tenet compatible trace production 30 | 31 | ### Fixed 32 | 33 | - Remove `/MT` from compilation chain 34 | 35 | ## [0.1] - 2022-03-21 36 | 37 | ### Added/Changed 38 | 39 | - Initial publication 40 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file is automatically generated from cmake.toml - DO NOT EDIT 2 | # See https://github.com/build-cpp/cmkr for more information 3 | 4 | cmake_minimum_required(VERSION 3.15) 5 | 6 | if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) 7 | message(FATAL_ERROR "In-tree builds are not supported. Run CMake from a separate directory: cmake -B build") 8 | endif() 9 | 10 | set(CMKR_ROOT_PROJECT OFF) 11 | if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 12 | set(CMKR_ROOT_PROJECT ON) 13 | 14 | # Bootstrap cmkr and automatically regenerate CMakeLists.txt 15 | include(cmkr.cmake OPTIONAL RESULT_VARIABLE CMKR_INCLUDE_RESULT) 16 | if(CMKR_INCLUDE_RESULT) 17 | cmkr() 18 | endif() 19 | 20 | # Enable folder support 21 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 22 | 23 | # Create a configure-time dependency on cmake.toml to improve IDE support 24 | configure_file(cmake.toml cmake.toml COPYONLY) 25 | endif() 26 | 27 | # Options 28 | option(TTD_BUILD_EXAMPLES "" ${CMKR_ROOT_PROJECT}) 29 | option(TTD_PYTHON_BINDINGS "" ${CMKR_ROOT_PROJECT}) 30 | 31 | # Variables 32 | set(PYBIND11_FINDPYTHON ON) 33 | 34 | project(ttd-bindings) 35 | 36 | include(FetchContent) 37 | 38 | if(TTD_PYTHON_BINDINGS) # TTD_PYTHON_BINDINGS 39 | message(STATUS "Fetching pybind11 (v2.10.3)...") 40 | FetchContent_Declare(pybind11 41 | GIT_REPOSITORY 42 | "https://github.com/pybind/pybind11" 43 | GIT_TAG 44 | v2.10.3 45 | ) 46 | FetchContent_MakeAvailable(pybind11) 47 | 48 | endif() 49 | # Target: TTD 50 | set(TTD_SOURCES 51 | "TTD/TTD.cpp" 52 | "TTD/sha256.cpp" 53 | "TTD/utils.cpp" 54 | "TTD/TTD.hpp" 55 | "TTD/csts.h" 56 | "TTD/sha256.h" 57 | "TTD/utils.h" 58 | cmake.toml 59 | ) 60 | 61 | add_library(TTD STATIC) 62 | 63 | target_sources(TTD PRIVATE ${TTD_SOURCES}) 64 | source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${TTD_SOURCES}) 65 | 66 | add_library(TTD::TTD ALIAS TTD) 67 | target_include_directories(TTD PUBLIC 68 | . 69 | ) 70 | 71 | # Target: pyTTD 72 | if(TTD_PYTHON_BINDINGS) # TTD_PYTHON_BINDINGS 73 | set(pyTTD_SOURCES 74 | "python-bindings/module.cpp" 75 | cmake.toml 76 | ) 77 | 78 | pybind11_add_module(pyTTD ${pyTTD_SOURCES}) 79 | source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${pyTTD_SOURCES}) 80 | 81 | target_link_libraries(pyTTD PUBLIC 82 | TTD::TTD 83 | ) 84 | 85 | endif() 86 | # Target: example_api 87 | if(TTD_BUILD_EXAMPLES) # TTD_BUILD_EXAMPLES 88 | set(example_api_SOURCES 89 | "example_api/main.cpp" 90 | cmake.toml 91 | ) 92 | 93 | add_executable(example_api) 94 | 95 | target_sources(example_api PRIVATE ${example_api_SOURCES}) 96 | source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${example_api_SOURCES}) 97 | 98 | target_link_libraries(example_api PRIVATE 99 | TTD::TTD 100 | ) 101 | 102 | get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT) 103 | if(NOT CMKR_VS_STARTUP_PROJECT) 104 | set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT example_api) 105 | endif() 106 | 107 | endif() 108 | # Target: example_calltree 109 | if(TTD_BUILD_EXAMPLES) # TTD_BUILD_EXAMPLES 110 | set(example_calltree_SOURCES 111 | "example_calltree/main.cpp" 112 | cmake.toml 113 | ) 114 | 115 | add_executable(example_calltree) 116 | 117 | target_sources(example_calltree PRIVATE ${example_calltree_SOURCES}) 118 | source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${example_calltree_SOURCES}) 119 | 120 | target_link_libraries(example_calltree PRIVATE 121 | TTD::TTD 122 | ) 123 | 124 | get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT) 125 | if(NOT CMKR_VS_STARTUP_PROJECT) 126 | set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT example_calltree) 127 | endif() 128 | 129 | endif() 130 | # Target: example_cov 131 | if(TTD_BUILD_EXAMPLES) # TTD_BUILD_EXAMPLES 132 | set(example_cov_SOURCES 133 | "example_cov/main.cpp" 134 | cmake.toml 135 | ) 136 | 137 | add_executable(example_cov) 138 | 139 | target_sources(example_cov PRIVATE ${example_cov_SOURCES}) 140 | source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${example_cov_SOURCES}) 141 | 142 | target_link_libraries(example_cov PRIVATE 143 | TTD::TTD 144 | ) 145 | 146 | get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT) 147 | if(NOT CMKR_VS_STARTUP_PROJECT) 148 | set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT example_cov) 149 | endif() 150 | 151 | endif() 152 | # Target: example_diff 153 | if(TTD_BUILD_EXAMPLES) # TTD_BUILD_EXAMPLES 154 | set(example_diff_SOURCES 155 | "example_diff/main.cpp" 156 | cmake.toml 157 | ) 158 | 159 | add_executable(example_diff) 160 | 161 | target_sources(example_diff PRIVATE ${example_diff_SOURCES}) 162 | source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${example_diff_SOURCES}) 163 | 164 | target_link_libraries(example_diff PRIVATE 165 | TTD::TTD 166 | ) 167 | 168 | get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT) 169 | if(NOT CMKR_VS_STARTUP_PROJECT) 170 | set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT example_diff) 171 | endif() 172 | 173 | endif() 174 | # Target: example_tenet 175 | if(TTD_BUILD_EXAMPLES) # TTD_BUILD_EXAMPLES 176 | set(example_tenet_SOURCES 177 | "example_tenet/main.cpp" 178 | cmake.toml 179 | ) 180 | 181 | add_executable(example_tenet) 182 | 183 | target_sources(example_tenet PRIVATE ${example_tenet_SOURCES}) 184 | source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${example_tenet_SOURCES}) 185 | 186 | target_link_libraries(example_tenet PRIVATE 187 | TTD::TTD 188 | ) 189 | 190 | get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT) 191 | if(NOT CMKR_VS_STARTUP_PROJECT) 192 | set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT example_tenet) 193 | endif() 194 | 195 | endif() 196 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TTD-Bindings 2 | 3 | Bindings (PoC) for [Microsoft WinDBG Time Travel Debugging (TTD)](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/time-travel-debugging-overview). 4 | 5 | *This is for educational purposes only. Please consult and comply with the related Microsoft licensing* 6 | 7 | ## Usage 8 | 9 | * `TTD/` is the main wrapper. `TTDReplay.dll` and `TTDReplayCPU.dll` (from WinDBG Preview, at least v1.2111) must be present in the same directory than the executable 10 | * `example_api/` highlights some of the wrapping 11 | * `example_diff/` shows how to use the wrapping to perform naive trace diffing 12 | * `example_calltree/` produces a call tree of a trace excerpt 13 | * `example_cov/` produces a [Lighthouse](https://github.com/gaasedelen/lighthouse) compatible coverage of modules in a trace excerpt 14 | * `example_tenet/` produces a [Tenet](https://github.com/gaasedelen/tenet) compatible trace from a TTD trace 15 | * `python-bindings/` provides Python bindings over TTD 16 | 17 | After performing one or several traces using Windbg Preview, one can open the `.run` file: 18 | ```C++ 19 | // Openning the trace 20 | TTD::ReplayEngine ttdengine = TTD::ReplayEngine(); 21 | ttdengine.Initialize(L"D:\\traces\\demo.run"); 22 | 23 | // Retrieve information, such as the starting position 24 | TTD::Position* first = ttdengine.GetFirstPosition(); 25 | printf("%llx:%llx\n", first->Major, first->Minor); 26 | 27 | // Get a Cursor instance 28 | TTD::Cursor ttdcursor = ttdengine.NewCursor(); 29 | 30 | // Set it to the first position 31 | ttdcursor.SetPosition(first); 32 | 33 | // Print the value of RCX 34 | TTD::TTD_Replay_RegisterContext* ctxt = ttdcursor.GetCrossPlatformContext(); 35 | printf("RCX: %llx\n", ctxt->rcx); 36 | 37 | // Make 3 step forward 38 | TTD::TTD_Replay_ICursorView_ReplayResult replayresult; 39 | TTD::Position* last = ttdengine.GetLastPosition(); 40 | ttdcursor.ReplayForward(&replayresult, last, 3); 41 | ``` 42 | 43 | Currently, the wrapping system supports: 44 | 45 | * Openning a trace file 46 | * Getting and setting the position 47 | * Getting: 48 | * Peb address 49 | * Thread count 50 | * Module list 51 | * Program counter 52 | * Registers 53 | * Query memory 54 | * Replaying forward and backward 55 | * Adding a callback for "CallRet" 56 | * Memory breakpoints (R/W/X) 57 | * During a callback, getting: 58 | * Returned value 59 | * Current position 60 | * Current PC 61 | 62 | ## Exemple: diffing path in a trace 63 | 64 | Objective: find which instruction actually check for `PrimaryGroupID` possibilities when setting one in an Active Directory. For more information, please read [this article].(https://github.com/commial/experiments/tree/master/windows/random_things/primaryGroupID) 65 | 66 | First, a trace is made on the Domain controller's `lsass.exe` while attempting to: 67 | 68 | 1. Set an un-authorized `PrimaryGroupID` to the user `simple` 69 | 1. Set an authorized `PrimaryGroupID` on the user `simple` 70 | 71 | Then, the trace is loaded in Windbg Preview for analysis. We first find one of the entrypoint, for instance by looking at the callstack during some of the calls. 72 | As a result, we saw that `ntdsai!LDAP_CONN::ModifyRequest` seems to be called when a LDAP request is made. This method is called twice, as expected: 73 | 74 | ![](assets/timeline.png) 75 | 76 | The first time, it quickly fails; the second, it processes the request, taking more time. 77 | 78 | Now, here is the strategy: 79 | 80 | 1. Start from the first and the second call to `ntdsai!LDAP_CONN::ModifyRequest` 81 | 1. Step some instruction for both, and observe the program counter 82 | 83 | * If it differ, get back to the last known sync point, and step less instruction 84 | * If it is the same, continue from the new position 85 | 86 | Using this method, we quickly end in `ntdll!RtlpAllocateHeap`. Indeed, while browsing the heap chunks, they might be some differences (as new chunk have been allocated between the two calls). 87 | 88 | We know this is a false positive. To avoid it, we can: 89 | 90 | 1. Register a `CallReturnCallback` to track function calls and their return address 91 | 1. On detected diff, check if a function in the current call stack is in the allow-list and its corresponding return address is the same for both traces 92 | 1. If it is, forward both traces to the return address, and consider it a new sync point 93 | 94 | A stack of calls and return addresses is used, to handle cases such as: 95 | ```C 96 | f( 97 | subcall() 98 | diff_call( 99 | // Difference is here, we want to go out of `f` 100 | ) 101 | ) 102 | ``` 103 | 104 | This implementation is available in `example_diff`. 105 | In addition to allocation function, a few others functions are allow-listed, such as column reading APIs. 106 | 107 | As a result, the actual diff is quickly found: 108 | 109 | ``` 110 | Openning the trace 111 | Openning the trace2 112 | STEP 10000 113 | Trace 1: 177b:3f | Trace 2: 6871:3f 114 | Trace 1 ends on 7ffb0b9cdca0 | Trace 2 ends on 7ffb0b9f2e2b 115 | Rollback...OK 116 | STEP 5000 117 | Trace 1: 177b:3f | Trace 2: 6871:3f 118 | Trace 1 ends on 7ffb10ec1b5c | Trace 2 ends on 7ffb10ec1c4d 119 | Rollback...OK 120 | STEP 2500 121 | Trace 1: 177b:3f | Trace 2: 6871:3f 122 | Trace 1 ends on 7ffb10ec1d9b | Trace 2 ends on 7ffb10ec1d9b 123 | STEP 2500 124 | Trace 1: 1788:21a | Trace 2: 687e:21a 125 | Trace 1 ends on 7ffb10ec1b5c | Trace 2 ends on 7ffb10ec1c4d 126 | Rollback...OK 127 | STEP 1250 128 | Trace 1: 1788:21a | Trace 2: 687e:21a 129 | Trace 1 ends on 7ffb10ebfc88 | Trace 2 ends on 7ffb10ec1b06 130 | Rollback...OK 131 | STEP 625 132 | Trace 1: 1788:21a | Trace 2: 687e:21a 133 | Trace 1 ends on 7ffb0ba1f327 | Trace 2 ends on 7ffb0ba1eef4 134 | Rollback...OK 135 | STEP 312 136 | Trace 1: 1788:21a | Trace 2: 687e:21a 137 | Trace 1 ends on 7ffb10f568d0 | Trace 2 ends on 7ffb10ec3ed3 138 | Rollback...OK 139 | 140 | ... 141 | 142 | STEP 2 143 | Trace 1: 1788:229 | Trace 2: 687e:229 144 | Trace 1 ends on 7ffb10ec1de8 | Trace 2 ends on 7ffb10ec1ec9 145 | Rollback...OK 146 | STEP 1 147 | Trace 1: 1788:229 | Trace 2: 687e:229 148 | Trace 1 ends on 7ffb10ec1de5 | Trace 2 ends on 7ffb10ec1ec1 149 | Rollback...OK 150 | Last known sync reference: Trace 1: 1788:229 | Trace 2: 687e:229 151 | Cur func: 7ffb10ec1af0 -> 7ffb10ebfcad | 7ffb10ec1af0 -> 7ffb10ebfcad 152 | Common func/ret: 153 | 7ffb0ba1efa0 -> 7ffb0b9aa1bc 154 | 7ffb0bc0c8bc -> 7ffb0ba1f245 155 | 7ffb0b9b7a9c -> 7ffb0bc0c8ec 156 | 7ffb0b9d37a0 -> 7ffb0b9b7ad7 157 | 7ffb10ebf2a0 -> 7ffb0b9d38bc 158 | -> Resync traces after their RET on 7ffb0b9d38bc...TRACE1 7ffb0b9d38bc......TRACE2 7ffb0b9d38bc 159 | STEP 10000 160 | 161 | ... 162 | 163 | Rollback...OK 164 | Last known sync reference: Trace 1: 1ad2:43 | Trace 2: 6b7e:43 165 | Cur func: 7ffb0ca01f10 -> 7ffb0bac294d | 7ffb0ca01f10 -> 7ffb0bac294d 166 | Common func/ret: 167 | 7ffb0b9f2848 -> 7ffb0b9aa396 168 | 7ffb0b9f33b0 -> 7ffb0b9f29c6 169 | 7ffb0b9f35ac -> 7ffb0b9f34eb 170 | 7ffb0b9909f4 -> 7ffb0b9f389e 171 | 7ffb0b991bc8 -> 7ffb0b990af0 172 | 7ffb0b993334 -> 7ffb0b991f3e 173 | 7ffb0ca01f10 -> 7ffb0bac294d 174 | ``` 175 | 176 | Going to `1ad2:43` and `6b7e:43`, we indeed ends on the expected check in `samsrv!SampAssignPrimaryGroup` \o/! 177 | 178 | ``` 179 | ||0:0:001> !ttdext.tt 1ad2:43 180 | Setting position: 1AD2:43 181 | Time Travel Position: 1AD2:43 182 | samsrv!SampAssignPrimaryGroup+0xb4: 183 | 00007ffb`0ca01fc4 7409 je samsrv!SampAssignPrimaryGroup+0xbf (00007ffb`0ca01fcf) [br=0] 184 | ||0:0:001> k 185 | # Child-SP RetAddr Call Site 186 | 00 000000f0`006fd8b0 00007ffb`0bac294d samsrv!SampAssignPrimaryGroup+0xb4 187 | 01 000000f0`006fd970 00007ffb`0b991f3e ntdsai!SampDsSetInformationUser+0x12f619 188 | 02 000000f0`006fdb80 00007ffb`0b990af0 ntdsai!SampWriteSamAttributes+0x376 189 | 03 000000f0`006fdc90 00007ffb`0b9f389e ntdsai!SampModifyLoopback+0xfc 190 | 04 000000f0`006fdd00 00007ffb`0b9f34eb ntdsai!SampModifyLoopbackCheck+0x2f2 191 | 05 000000f0`006fddd0 00007ffb`0b9f29c6 ntdsai!LocalModify+0x13b 192 | 06 000000f0`006fe0f0 00007ffb`0b9aa396 ntdsai!DirModifyEntryNative+0x17e 193 | 07 000000f0`006fe450 00007ffb`0bd22ee9 ntdsai!LDAP_CONN::ModifyRequest+0x386 194 | 08 000000f0`006feac0 00007ffb`0b9c297a ntdsai!LDAP_CONN::ModifyRequest+0x41 195 | 09 000000f0`006feb20 00007ffb`0ba1c6a2 ntdsai!LDAP_CONN::ProcessRequestEx+0xb6e 196 | 0a 000000f0`006fedf0 00007ffb`0ba1c069 ntdsai!LDAP_CONN::IoCompletion+0x51a 197 | 0b 000000f0`006ff650 00007ffb`0b912dd6 ntdsai!LdapCompletionRoutine+0x139 198 | 0c 000000f0`006ff6f0 00007ffb`0b912ae4 NTDSATQ!AtqpProcessContext+0xd6 199 | 0d 000000f0`006ff740 00007ffb`0fff7974 NTDSATQ!AtqPoolThread+0x1a4 200 | 0e 000000f0`006ff7d0 00007ffb`10f0a2f1 KERNEL32!BaseThreadInitThunk+0x14 201 | 0f 000000f0`006ff800 00000000`00000000 ntdll!RtlUserThreadStart+0x21 202 | ``` 203 | 204 | ## Exemple: call tree 205 | 206 | In order to get a quick idea of what functions are called in a trace, one can write a tool which: 207 | 208 | * register a `CallRetCallback`: 209 | * tracking the call stack depth 210 | * printing the callee function 211 | * printing the returned address 212 | * run through a part of the trace 213 | 214 | To make a more usable trace, one can also: 215 | 216 | * print the position in the trace, to ease further investigation 217 | * print the returned value 218 | * use symbols (relative to the trace's modules, ie. even if not present on the system) to resolve addresses 219 | 220 | `example_calltree` implements theses features. 221 | Here is an example on the same `lsass` trace: 222 | 223 | ```bash 224 | # Run the tool 225 | example_calltree.exe -b 177B:3E -e 1B76:31 D:\traces\lsass01.run > out.txt 226 | # ~1.5 second, for ~25K entries 227 | 228 | # Whole trace would have needed ~7.5 seconds for ~140K entries 229 | ``` 230 | 231 | Outputs: 232 | 233 | ``` 234 | Openning the trace 235 | ModuleList: 236 | 256064a0000 3000 C:\Windows\system32\msprivs.DLL 237 | 7ff7f5710000 11000 C:\Windows\system32\lsass.exe 238 | 7ffae3170000 171000 C:\ProgramData\Microsoft\Windbg\0-0-0\TTD\TTDRecordCPU.dll 239 | 7ffaf3ea0000 22000 C:\Windows\SYSTEM32\certpoleng.dll 240 | 7ffaf3fe0000 1a000 C:\Windows\system32\keyiso.dll 241 | 242 | ... 243 | 244 | -> ntdsai!LDAP_CONN::ModifyRequest (7ffb0b9aa010) [177b:3e] 245 | | -> ntdsai!memset (7ffb0baad676) [177b:5e] 246 | | <- ntdsai!+2a0bb (7ffb0b9aa0bb) [177b:a0] RETURN f0006fe550 247 | | -> ntdsai!memset (7ffb0baad676) [177b:a4] 248 | | <- ntdsai!+2a0d0 (7ffb0b9aa0d0) [177b:118] RETURN f0006fe6b0 249 | | -> ntdsai!THREAD_STOPWATCH::Start (7ffb0b9ad920) [177b:126] 250 | | | -> ntdsai!THGetThreadCpuTime (7ffb0ba1c13c) [177b:12a] 251 | | | | -> KERNEL32!GetCurrentThread (7ffb0fff57f0) [177b:12c] 252 | | | | <- ntdsai!+9c146 (7ffb0ba1c146) [177b:12e] RETURN fffffffffffffffe 253 | | | | -> KERNEL32!GetThreadTimesStub (7ffb0fff9a80) [177b:135] 254 | | | | | -> ntdll!NtQueryInformationThread (7ffb10f4fe80) [177b:147] 255 | | | | | <- KERNELBASE!+69a76 (7ffb0d609a76) [177d:0] RETURN 0 256 | | | | | -> KERNELBASE!_security_check_cookie (7ffb0d628890) [177d:f] 257 | | | | | <- KERNELBASE!+69ab5 (7ffb0d609ab5) [177d:15] RETURN 1 258 | | | | <- ntdsai!+9c168 (7ffb0ba1c168) [177d:1b] RETURN 1 259 | | | <- ntdsai!+2d92e (7ffb0b9ad92e) [177d:21] RETURN 2625a0 260 | | | -> KERNELBASE!GetTickCount (7ffb0d5f8fd0) [177d:23] 261 | | | <- ntdsai!+2d938 (7ffb0b9ad938) [177d:29] RETURN daa1b 262 | | <- ntdsai!+2a11b (7ffb0b9aa11b) [177d:2e] RETURN daa1b 263 | | -> ntdsai!LDAP_CONN::IsBound (7ffb0b9aa7f0) [177d:32] 264 | | <- ntdsai!+2a12b (7ffb0b9aa12b) [177d:3b] RETURN 1 265 | | -> ntdsai!memset (7ffb0baad676) [177d:44] 266 | | <- ntdsai!+2a15b (7ffb0b9aa15b) [177d:b8] RETURN f0006fe6b0 267 | | -> ntdsai!InitCommarg (7ffb0b9f6fc0) [177d:bb] 268 | | | -> ntdsai!memset (7ffb0baad676) [177d:c1] 269 | | | <- ntdsai!+76fd6 (7ffb0b9f6fd6) [177d:f3] RETURN f0006fe6b0 270 | | <- ntdsai!+2a170 (7ffb0b9aa170) [177d:102] RETURN 400a 271 | | -> ntdsai!LDAP_LogAPIEntry (7ffb0ba1d6d4) [177e:3] 272 | | | -> KERNEL32!TlsGetValueStub (7ffb0fff5250) [177e:11] 273 | | | <- ntdsai!+9d714 (7ffb0ba1d714) [177e:19] RETURN 25606fb0cf0 274 | | | -> ntdsai!SetActiveThreadStateInfoStringA (7ffb0ba1eb8c) [177e:1c] 275 | | | | -> ntdsai!DsaSafeMult (7ffb0ba1eb30) [177e:33] 276 | | | | <- ntdsai!+9ebe0 (7ffb0ba1ebe0) [177e:4b] RETURN c8 277 | 278 | ... 279 | ``` 280 | 281 | ## Example: coverage 282 | 283 | To understand what a code is actually doing, one can extract the instruction coverage of a trace. 284 | To do so, `example_cov` is a tool which: 285 | 286 | * list available modules for filtering 287 | * for each target, add a breakpoint on execution, from the module's base address to its end 288 | * register a `MemoryWatchpointCallback`: 289 | * track in a set each breakpoint's address hitted 290 | * run through a part of the trace 291 | * export the resultings addresses in `module_name.trace.txt` files 292 | 293 | Here is an example with a trace of the *Local Session Manager* (`lsm.dll`): 294 | 295 | ```bash 296 | # Run the tool 297 | example_cov.exe -m lsm D:\traces\lsm.run 298 | # After a few seconds: 299 | Openning the trace 300 | Tracking lsm 301 | Number of entry: 0x1000, current position 13b6:26 302 | Number of entry: 0x1000, current position c03:9 303 | Number of entry: 0x2000, current position 134f:5b 304 | Number of entry: 0x3000, current position 1991:205 305 | Number of entry: 0x3000, current position 720c:1d 306 | Number of entry: 0x3000, current position 36af:28 307 | Number of entry: 0x3000, current position 7b01:10 308 | Number of entry: 0x4000, current position 100d2:396 309 | Number of entry: 0x4000, current position a7fd:18 310 | Number of entry: 0x4000, current position ff7f:17c 311 | Got 0x42f9 addresses 312 | Dump to file lsm.trace.txt 313 | ``` 314 | 315 | The `lsm.trace.txt` file looks like: 316 | 317 | ```bash 318 | lsm+1eb0 319 | lsm+1eb7 320 | lsm+1eba 321 | lsm+1ebe 322 | ... 323 | ``` 324 | 325 | As this format is compatible with [Lighthouse](https://github.com/gaasedelen/lighthouse), one can visualize it in IDA: 326 | 327 | ![](assets/lsm_coverage.png) 328 | 329 | By doing several traces, or one trace with several call splitted using `-b` (*begin*) and `-e` (*end*) arguments, one can obtain overlapping coverage map. 330 | Diffing them could be a great way to identify specific code snippets. 331 | 332 | ## Example: Trace exploration in IDA 333 | 334 | [Tenet](https://github.com/gaasedelen/tenet) is a "Trace Explorer" plug-in for IDA. 335 | Please refer to the official website for the full feature description. 336 | 337 | The `example_tenet` example produces a Tenet compatible trace for a given module, from a TTD trace. 338 | 339 | For instance: 340 | ```bash 341 | # Run the tool, for the module `lsm.dll` 342 | example_tenet.exe -m lsm D:\traces\lsm.run 343 | # The output is in `lsm.trace.tenet` 344 | Openning the trace 345 | Track c:\windows\system32\lsm.dll [7fffcb3e0000 to 7fffcb491fff] 346 | Dump to file lsm.trace.tenet 347 | 0x1000 instructions... 348 | 0x2000 instructions... 349 | 0x3000 instructions... 350 | 0x4000 instructions... 351 | 0x5000 instructions... 352 | 0x6000 instructions... 353 | 0x7000 instructions... 354 | ``` 355 | 356 | As a side note, if the TTD trace is going to other modules (such as `memcpy` implementation), the difference from the `call` instruction and the next one will provide a summary of all memory accesses done by `memcpy`. 357 | 358 | The resulting `lsm.trace.tenet` can then be loaded in IDA: 359 | 360 | ![](assets/tenet.png) 361 | 362 | ## Python 363 | 364 | ### Setup 365 | 366 | Either: 367 | 368 | * use the latest `pyTTD.pyd` [release](https://github.com/commial/ttd-bindings/releases/latest) 369 | * or compile the `python-bindings` project. 370 | 371 | ### Usage 372 | 373 | With `pyTTD.pyd`, `TTDReplay.dll` and `TTDReplayCPU.dll` in the directory, one can import `pyTTD`: 374 | 375 | ```python 376 | import pyTTD 377 | 378 | # Open the trace 379 | eng = pyTTD.ReplayEngine() 380 | eng.initialize("D:\\traces\\trace.run") 381 | 382 | # Get positions 383 | first = eng.get_first_position() 384 | last = eng.get_last_position() 385 | print(f"Trace from {first} to {last}") 386 | 387 | # Get a cursor 388 | cursor = eng.new_cursor() 389 | cursor.set_position(first) 390 | 391 | # Retrieve PC 392 | print(f"PC: {cursor.get_program_counter():x}") 393 | 394 | # Print RCX 395 | ctxt = cursor.get_context_x86_64() 396 | print("RCX: %x" % ctxt.rcx) 397 | 398 | # Read the memory at RCX on 16 bytes 399 | print("@128[RCX]: %s" % cursor.read_mem(ctxt.rcx, 16)) 400 | ``` 401 | 402 | More API example are available in `example_api/example_api.py`. 403 | 404 | ## References 405 | 406 | * [MSDN - Time Travel Debugging - Overview](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/time-travel-debugging-overview) 407 | * [TTD analysis](https://www.synacktiv.com/ressources/rumpinrennes-ttd.pdf) by @w4kfu at Rump'in Rennes 2019 408 | * [Initial iDNA paper](https://www.usenix.org/legacy/events/vee06/full_papers/p154-bhansali.pdf) : S. Bhansali, W.-K. Chen, S. de Jong, A. Edwards, R. Murray, M. Drinic, D. Mihocka, and J. Chau. Framework for "Instruction-level tracing and analysis of program executions" in VEE, 2006. 409 | -------------------------------------------------------------------------------- /TTD.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30413.136 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TTD", "TTD\TTD.vcxproj", "{095478E2-F772-42E7-A921-98B92CEFD363}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_api", "example_api\example_api.vcxproj", "{1B66E0DB-A111-476B-AB14-EAE15B7F9FD7}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_diff", "example_diff\example_diff.vcxproj", "{907B330C-3738-49B4-85B5-C6C38CBB648A}" 11 | EndProject 12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_calltree", "example_calltree\example_calltree.vcxproj", "{EE294520-BAE7-4BCE-9F06-6ACB10ACE0C2}" 13 | EndProject 14 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python-bindings", "python-bindings\python-bindings.vcxproj", "{6CD0EAF3-F683-4E30-B7FD-72FCBA758865}" 15 | EndProject 16 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_cov", "example_cov\example_cov.vcxproj", "{0BE1A525-D873-4630-8432-F9B7BC4EDCEA}" 17 | EndProject 18 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_tenet", "example_tenet\example_tenet.vcxproj", "{8923F142-E0F7-4595-8B83-1E509100C162}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|x64 = Debug|x64 23 | Debug|x86 = Debug|x86 24 | Release|x64 = Release|x64 25 | Release|x86 = Release|x86 26 | EndGlobalSection 27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 28 | {095478E2-F772-42E7-A921-98B92CEFD363}.Debug|x64.ActiveCfg = Debug|x64 29 | {095478E2-F772-42E7-A921-98B92CEFD363}.Debug|x64.Build.0 = Debug|x64 30 | {095478E2-F772-42E7-A921-98B92CEFD363}.Debug|x86.ActiveCfg = Debug|Win32 31 | {095478E2-F772-42E7-A921-98B92CEFD363}.Debug|x86.Build.0 = Debug|Win32 32 | {095478E2-F772-42E7-A921-98B92CEFD363}.Release|x64.ActiveCfg = Release|x64 33 | {095478E2-F772-42E7-A921-98B92CEFD363}.Release|x64.Build.0 = Release|x64 34 | {095478E2-F772-42E7-A921-98B92CEFD363}.Release|x86.ActiveCfg = Release|Win32 35 | {095478E2-F772-42E7-A921-98B92CEFD363}.Release|x86.Build.0 = Release|Win32 36 | {1B66E0DB-A111-476B-AB14-EAE15B7F9FD7}.Debug|x64.ActiveCfg = Debug|x64 37 | {1B66E0DB-A111-476B-AB14-EAE15B7F9FD7}.Debug|x64.Build.0 = Debug|x64 38 | {1B66E0DB-A111-476B-AB14-EAE15B7F9FD7}.Debug|x86.ActiveCfg = Debug|Win32 39 | {1B66E0DB-A111-476B-AB14-EAE15B7F9FD7}.Debug|x86.Build.0 = Debug|Win32 40 | {1B66E0DB-A111-476B-AB14-EAE15B7F9FD7}.Release|x64.ActiveCfg = Release|x64 41 | {1B66E0DB-A111-476B-AB14-EAE15B7F9FD7}.Release|x64.Build.0 = Release|x64 42 | {1B66E0DB-A111-476B-AB14-EAE15B7F9FD7}.Release|x86.ActiveCfg = Release|Win32 43 | {1B66E0DB-A111-476B-AB14-EAE15B7F9FD7}.Release|x86.Build.0 = Release|Win32 44 | {907B330C-3738-49B4-85B5-C6C38CBB648A}.Debug|x64.ActiveCfg = Debug|x64 45 | {907B330C-3738-49B4-85B5-C6C38CBB648A}.Debug|x64.Build.0 = Debug|x64 46 | {907B330C-3738-49B4-85B5-C6C38CBB648A}.Debug|x86.ActiveCfg = Debug|Win32 47 | {907B330C-3738-49B4-85B5-C6C38CBB648A}.Debug|x86.Build.0 = Debug|Win32 48 | {907B330C-3738-49B4-85B5-C6C38CBB648A}.Release|x64.ActiveCfg = Release|x64 49 | {907B330C-3738-49B4-85B5-C6C38CBB648A}.Release|x64.Build.0 = Release|x64 50 | {907B330C-3738-49B4-85B5-C6C38CBB648A}.Release|x86.ActiveCfg = Release|Win32 51 | {907B330C-3738-49B4-85B5-C6C38CBB648A}.Release|x86.Build.0 = Release|Win32 52 | {EE294520-BAE7-4BCE-9F06-6ACB10ACE0C2}.Debug|x64.ActiveCfg = Debug|x64 53 | {EE294520-BAE7-4BCE-9F06-6ACB10ACE0C2}.Debug|x64.Build.0 = Debug|x64 54 | {EE294520-BAE7-4BCE-9F06-6ACB10ACE0C2}.Debug|x86.ActiveCfg = Debug|Win32 55 | {EE294520-BAE7-4BCE-9F06-6ACB10ACE0C2}.Debug|x86.Build.0 = Debug|Win32 56 | {EE294520-BAE7-4BCE-9F06-6ACB10ACE0C2}.Release|x64.ActiveCfg = Release|x64 57 | {EE294520-BAE7-4BCE-9F06-6ACB10ACE0C2}.Release|x64.Build.0 = Release|x64 58 | {EE294520-BAE7-4BCE-9F06-6ACB10ACE0C2}.Release|x86.ActiveCfg = Release|Win32 59 | {EE294520-BAE7-4BCE-9F06-6ACB10ACE0C2}.Release|x86.Build.0 = Release|Win32 60 | {6CD0EAF3-F683-4E30-B7FD-72FCBA758865}.Debug|x64.ActiveCfg = Debug|x64 61 | {6CD0EAF3-F683-4E30-B7FD-72FCBA758865}.Debug|x64.Build.0 = Debug|x64 62 | {6CD0EAF3-F683-4E30-B7FD-72FCBA758865}.Debug|x86.ActiveCfg = Debug|Win32 63 | {6CD0EAF3-F683-4E30-B7FD-72FCBA758865}.Debug|x86.Build.0 = Debug|Win32 64 | {6CD0EAF3-F683-4E30-B7FD-72FCBA758865}.Release|x64.ActiveCfg = Release|x64 65 | {6CD0EAF3-F683-4E30-B7FD-72FCBA758865}.Release|x64.Build.0 = Release|x64 66 | {6CD0EAF3-F683-4E30-B7FD-72FCBA758865}.Release|x86.ActiveCfg = Release|Win32 67 | {6CD0EAF3-F683-4E30-B7FD-72FCBA758865}.Release|x86.Build.0 = Release|Win32 68 | {0BE1A525-D873-4630-8432-F9B7BC4EDCEA}.Debug|x64.ActiveCfg = Debug|x64 69 | {0BE1A525-D873-4630-8432-F9B7BC4EDCEA}.Debug|x64.Build.0 = Debug|x64 70 | {0BE1A525-D873-4630-8432-F9B7BC4EDCEA}.Debug|x86.ActiveCfg = Debug|Win32 71 | {0BE1A525-D873-4630-8432-F9B7BC4EDCEA}.Debug|x86.Build.0 = Debug|Win32 72 | {0BE1A525-D873-4630-8432-F9B7BC4EDCEA}.Release|x64.ActiveCfg = Release|x64 73 | {0BE1A525-D873-4630-8432-F9B7BC4EDCEA}.Release|x64.Build.0 = Release|x64 74 | {0BE1A525-D873-4630-8432-F9B7BC4EDCEA}.Release|x86.ActiveCfg = Release|Win32 75 | {0BE1A525-D873-4630-8432-F9B7BC4EDCEA}.Release|x86.Build.0 = Release|Win32 76 | {8923F142-E0F7-4595-8B83-1E509100C162}.Debug|x64.ActiveCfg = Debug|x64 77 | {8923F142-E0F7-4595-8B83-1E509100C162}.Debug|x64.Build.0 = Debug|x64 78 | {8923F142-E0F7-4595-8B83-1E509100C162}.Debug|x86.ActiveCfg = Debug|Win32 79 | {8923F142-E0F7-4595-8B83-1E509100C162}.Debug|x86.Build.0 = Debug|Win32 80 | {8923F142-E0F7-4595-8B83-1E509100C162}.Release|x64.ActiveCfg = Release|x64 81 | {8923F142-E0F7-4595-8B83-1E509100C162}.Release|x64.Build.0 = Release|x64 82 | {8923F142-E0F7-4595-8B83-1E509100C162}.Release|x86.ActiveCfg = Release|Win32 83 | {8923F142-E0F7-4595-8B83-1E509100C162}.Release|x86.Build.0 = Release|Win32 84 | EndGlobalSection 85 | GlobalSection(SolutionProperties) = preSolution 86 | HideSolutionNode = FALSE 87 | EndGlobalSection 88 | GlobalSection(ExtensibilityGlobals) = postSolution 89 | SolutionGuid = {1F2162E6-21E3-43A0-BCB5-735C10D99F55} 90 | EndGlobalSection 91 | EndGlobal 92 | -------------------------------------------------------------------------------- /TTD/TTD.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "sha256.h" 5 | #include "csts.h" 6 | #include "utils.h" 7 | #include "TTD.hpp" 8 | 9 | namespace TTD { 10 | 11 | /**** Wrapping around the vftable ****/ 12 | void Cursor::SetPosition(Position* position) { 13 | return this->cursor->ICursor->SetPosition(cursor, position); 14 | } 15 | void Cursor::SetPosition(unsigned __int64 Major, unsigned __int64 Minor) { 16 | Position pos; 17 | pos.Major = Major; 18 | pos.Minor = Minor; 19 | return this->SetPosition(&pos); 20 | } 21 | 22 | unsigned __int64 Cursor::GetThreadCount() { 23 | return this->cursor->ICursor->GetThreadCount(cursor); 24 | } 25 | 26 | GuestAddress Cursor::GetProgramCounter() { 27 | return this->GetProgramCounter(0); 28 | } 29 | GuestAddress Cursor::GetProgramCounter(unsigned int ThreadId) { 30 | return this->cursor->ICursor->GetProgramCounter(cursor, ThreadId); 31 | } 32 | 33 | struct TTD_Replay_ActiveThreadInfo* Cursor::GetThreadList() 34 | { 35 | return this->cursor->ICursor->GetThreadList(cursor); 36 | } 37 | 38 | struct TTD_Replay_ThreadInfo* Cursor::GetThreadInfo() { 39 | return this->GetThreadInfo(0); 40 | } 41 | struct TTD_Replay_ThreadInfo* Cursor::GetThreadInfo(unsigned int ThreadId) { 42 | return this->cursor->ICursor->GetThreadInfo(cursor, ThreadId); 43 | } 44 | 45 | void* Cursor::GetCrossPlatformContext() 46 | { 47 | return this->GetCrossPlatformContext(0); 48 | } 49 | 50 | //The caller should free ctxt after call GetCrossPlatformContext function 51 | void* Cursor::GetCrossPlatformContext(uint32_t threadId) { 52 | void* ctxt = malloc(0xA70); 53 | if (NULL == ctxt) 54 | return NULL; 55 | return this->cursor->ICursor->GetCrossPlatformContext(cursor, ctxt, threadId); 56 | } 57 | 58 | //The caller should free memorybuffer and memorybuffer->data (same value as buf->dst_buffer) after call QueryMemoryBuffer function 59 | struct MemoryBuffer* Cursor::QueryMemoryBuffer(GuestAddress address, unsigned __int64 size) { 60 | struct MemoryBuffer* memorybuffer = (struct MemoryBuffer*)malloc(sizeof(struct MemoryBuffer)); 61 | struct TBuffer* buf = (struct TBuffer*)malloc(sizeof(struct TBuffer)); 62 | if (NULL == buf || NULL == memorybuffer) 63 | return NULL; 64 | buf->size = size; 65 | buf->dst_buffer = (void*)malloc(size); 66 | if (NULL == buf->dst_buffer) 67 | return NULL; 68 | this->cursor->ICursor->QueryMemoryBuffer(cursor, memorybuffer, address, buf, 0); 69 | free(buf); 70 | return memorybuffer; 71 | } 72 | 73 | struct TTD_Replay_ICursorView_ReplayResult* Cursor::ReplayForward(struct TTD_Replay_ICursorView_ReplayResult* replay_result_out, struct Position* posMax, unsigned __int64 stepCount) { 74 | return this->cursor->ICursor->ReplayForward(cursor, replay_result_out, posMax, stepCount); 75 | } 76 | 77 | struct TTD_Replay_ICursorView_ReplayResult* Cursor::ReplayBackward(struct TTD_Replay_ICursorView_ReplayResult* replay_result_out, struct Position* posMin, unsigned __int64 stepCount) { 78 | return this->cursor->ICursor->ReplayBackward(cursor, replay_result_out, posMin, stepCount); 79 | } 80 | 81 | void Cursor::SetCallReturnCallback(PROC_CallCallback callCallback, unsigned __int64 callback_value) { 82 | return this->cursor->ICursor->SetCallReturnCallback(cursor, callCallback, callback_value); 83 | } 84 | 85 | void Cursor::SetMemoryWatchpointCallback(PROC_MemCallback memCallback, unsigned __int64 callback_value) { 86 | return this->cursor->ICursor->SetMemoryWatchpointCallback(cursor, memCallback, callback_value); 87 | } 88 | 89 | Position* Cursor::GetPosition(unsigned int ThreadId) { 90 | return this->cursor->ICursor->GetPosition(cursor, ThreadId); 91 | } 92 | 93 | Position* Cursor::GetPosition() { 94 | return this->GetPosition(0); 95 | } 96 | 97 | Position* Cursor::GetPreviousPosition(unsigned int ThreadId) { 98 | return this->cursor->ICursor->GetPreviousPosition(cursor, ThreadId); 99 | } 100 | 101 | Position* Cursor::GetPreviousPosition() { 102 | return this->GetPreviousPosition(0); 103 | } 104 | 105 | bool Cursor::AddMemoryWatchpoint(TTD_Replay_MemoryWatchpointData* data) { 106 | return this->cursor->ICursor->AddMemoryWatchpoint(cursor, data); 107 | } 108 | 109 | bool Cursor::RemoveMemoryWatchpoint(TTD_Replay_MemoryWatchpointData* data) { 110 | return this->cursor->ICursor->RemoveMemoryWatchpoint(cursor, data); 111 | } 112 | 113 | unsigned __int64 Cursor::GetModuleCount() 114 | { 115 | return this->cursor->ICursor->GetModuleCount(cursor); 116 | } 117 | struct TTD_Replay_ModuleInstance* Cursor::GetModuleList() 118 | { 119 | return this->cursor->ICursor->GetModuleList(cursor); 120 | } 121 | 122 | ReplayEngine::ReplayEngine(const wchar_t* ttdReplayPath, const wchar_t* ttdReplayCpuPath) { 123 | HINSTANCE hinstLib; 124 | PROC_Initiate InitiateReplayEngineHandshake; 125 | PROC_Create CreateReplayEngineWithHandshake; 126 | BYTE Source[48]; 127 | char Destination[336]; 128 | SHA256_CTX ctx; 129 | BYTE sha[32]; 130 | 131 | hinstLib = LoadLibraryW(ttdReplayCpuPath); 132 | if (hinstLib == NULL) { 133 | throw std::exception("Unable to find TTDReplayCPU.dll"); 134 | } 135 | 136 | hinstLib = LoadLibraryW(ttdReplayPath); 137 | if (hinstLib == NULL) { 138 | throw std::exception("Unable to find TTDReplay.dll"); 139 | } 140 | InitiateReplayEngineHandshake = (PROC_Initiate)GetProcAddress(hinstLib, "InitiateReplayEngineHandshake"); 141 | CreateReplayEngineWithHandshake = (PROC_Create)GetProcAddress(hinstLib, "CreateReplayEngineWithHandshake"); 142 | 143 | int result = InitiateReplayEngineHandshake("DbgEng", Source); 144 | 145 | strncpy_s(Destination, (char*)Source, 0x2F); 146 | for (int i = 0; i < 2; ++i) { 147 | strncat_s(Destination, &aScopeOfLicense[0x66 * ((Source[i] - 48) % 0x11ui64)], 0x65ui64); 148 | } 149 | strncat_s(Destination, &aVYHVAX4gukZ8Wv[79 * ((Source[2] - 48i64) % 0xBui64)], 0x4Eui64); 150 | 151 | sha256_init(&ctx); 152 | sha256_update(&ctx, (unsigned char*)Destination, strlen(Destination)); 153 | sha256_final(&ctx, sha); 154 | 155 | size_t sha_b64_size; 156 | char* sha_b64 = base64_encode(sha, 32, &sha_b64_size); 157 | char tmp[0x30]; 158 | memset(tmp, 0, 0x30); 159 | memcpy(tmp, sha_b64, sha_b64_size); 160 | 161 | void* instance; 162 | result = CreateReplayEngineWithHandshake(tmp, &instance, version_guid); 163 | this->engine = (TTD_Replay_ReplayEngine*)instance; 164 | } 165 | 166 | /**** Wrapping around the vftable ****/ 167 | bool ReplayEngine::Initialize(const wchar_t* trace_filename) { 168 | return this->engine->IReplayEngine->Initialize(engine, trace_filename); 169 | } 170 | 171 | struct TTD_SystemInfo* ReplayEngine::GetSystemInfo() { 172 | return this->engine->IReplayEngine->GetSystemInfo(engine); 173 | } 174 | 175 | unsigned __int64 ReplayEngine::GetThreadCount() { 176 | return this->engine->IReplayEngine->GetThreadCount(engine); 177 | } 178 | 179 | Position* ReplayEngine::GetFirstPosition() { 180 | return this->engine->IReplayEngine->GetFirstPosition(engine); 181 | } 182 | 183 | Position* ReplayEngine::GetLastPosition() { 184 | return this->engine->IReplayEngine->GetLastPosition(engine); 185 | } 186 | 187 | GuestAddress ReplayEngine::GetPebAddress() { 188 | return this->engine->IReplayEngine->GetPebAddress(engine); 189 | } 190 | 191 | Cursor ReplayEngine::NewCursor() { 192 | return Cursor(engine->IReplayEngine->NewCursor(engine, GUID_Cursor)); 193 | } 194 | 195 | unsigned __int64 ReplayEngine::GetModuleCount() { 196 | return this->engine->IReplayEngine->GetModuleCount(engine); 197 | } 198 | 199 | const TTD_Replay_Module* ReplayEngine::GetModuleList() { 200 | return this->engine->IReplayEngine->GetModuleList(engine); 201 | } 202 | 203 | unsigned __int64 ReplayEngine::GetModuleLoadedEventCount() 204 | { 205 | return this->engine->IReplayEngine->GetModuleLoadedEventCount(engine); 206 | } 207 | 208 | const TTD_Replay_ModuleLoadedEvent* ReplayEngine::GetModuleLoadedEventList() 209 | { 210 | return this->engine->IReplayEngine->GetModuleLoadedEventList(engine); 211 | } 212 | 213 | const std::vector ReplayEngine::GetModuleLoadedEvents() 214 | { 215 | return std::vector(this->GetModuleLoadedEventList(), this->GetModuleLoadedEventList() + this->GetModuleLoadedEventCount()); 216 | } 217 | 218 | unsigned __int64 ReplayEngine::GetModuleUnloadedEventCount() 219 | { 220 | return this->engine->IReplayEngine->GetModuleUnloadedEventCount(engine); 221 | } 222 | 223 | const TTD_Replay_ModuleUnloadedEvent* ReplayEngine::GetModuleUnloadedEventList() 224 | { 225 | return this->engine->IReplayEngine->GetModuleUnloadedEventList(engine); 226 | } 227 | 228 | const std::vector ReplayEngine::GetModuleUnloadedEvents() 229 | { 230 | return std::vector(this->GetModuleUnloadedEventList(), this->GetModuleUnloadedEventList() + this->GetModuleUnloadedEventCount()); 231 | } 232 | 233 | unsigned __int64 ReplayEngine::GetThreadCreatedEventCount() 234 | { 235 | return this->engine->IReplayEngine->GetThreadCreatedEventCount(engine); 236 | } 237 | 238 | const TTD_Replay_ThreadCreatedEvent* ReplayEngine::GetThreadCreatedEventList() 239 | { 240 | return this->engine->IReplayEngine->GetThreadCreatedEventList(engine); 241 | } 242 | 243 | const std::vector ReplayEngine::GetThreadCreatedEvents() 244 | { 245 | return std::vector(this->GetThreadCreatedEventList(), this->GetThreadCreatedEventList() + this->GetThreadCreatedEventCount()); 246 | } 247 | 248 | unsigned __int64 ReplayEngine::GetThreadTerminatedEventCount() 249 | { 250 | return this->engine->IReplayEngine->GetThreadTerminatedEventCount(engine); 251 | } 252 | 253 | const TTD_Replay_ThreadTerminatedEvent* ReplayEngine::GetThreadTerminatedEventList() 254 | { 255 | return this->engine->IReplayEngine->GetThreadTerminatedEventList(engine); 256 | } 257 | 258 | const std::vector ReplayEngine::GetThreadTerminatedEvents() 259 | { 260 | return std::vector(this->GetThreadTerminatedEventList(), this->GetThreadTerminatedEventList() + this->GetThreadTerminatedEventCount()); 261 | } 262 | 263 | unsigned __int64 ReplayEngine::GetExceptionEventCount() 264 | { 265 | return this->engine->IReplayEngine->GetExceptionEventCount(engine); 266 | } 267 | 268 | const TTD_Replay_ExceptionEvent* ReplayEngine::GetExceptionEventList() 269 | { 270 | return this->engine->IReplayEngine->GetExceptionEventList(engine); 271 | } 272 | 273 | const std::vector ReplayEngine::GetExceptionEvents() 274 | { 275 | return std::vector(this->GetExceptionEventList(), this->GetExceptionEventList() + this->GetExceptionEventCount()); 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /TTD/TTD.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 16.0 33 | Win32Proj 34 | {095478e2-f772-42e7-a921-98b92cefd363} 35 | TTD 36 | 10.0 37 | 38 | 39 | 40 | StaticLibrary 41 | true 42 | v142 43 | Unicode 44 | 45 | 46 | StaticLibrary 47 | false 48 | v142 49 | true 50 | Unicode 51 | 52 | 53 | StaticLibrary 54 | true 55 | v142 56 | Unicode 57 | 58 | 59 | StaticLibrary 60 | false 61 | v142 62 | true 63 | Unicode 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | true 85 | $(SolutionDir) 86 | 87 | 88 | false 89 | $(SolutionDir) 90 | 91 | 92 | true 93 | $(SolutionDir) 94 | 95 | 96 | false 97 | $(SolutionDir) 98 | 99 | 100 | 101 | Level3 102 | true 103 | WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | 108 | 109 | 110 | 111 | true 112 | 113 | 114 | 115 | 116 | Level3 117 | true 118 | true 119 | true 120 | WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) 121 | true 122 | 123 | 124 | 125 | 126 | 127 | 128 | true 129 | true 130 | true 131 | 132 | 133 | 134 | 135 | Level3 136 | true 137 | _DEBUG;_LIB;%(PreprocessorDefinitions) 138 | true 139 | 140 | 141 | 142 | 143 | 144 | 145 | true 146 | 147 | 148 | 149 | 150 | Level3 151 | true 152 | true 153 | true 154 | NDEBUG;_LIB;%(PreprocessorDefinitions) 155 | true 156 | 157 | 158 | MultiThreaded 159 | 160 | 161 | 162 | 163 | true 164 | true 165 | true 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /TTD/TTD.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | Header Files 37 | 38 | 39 | -------------------------------------------------------------------------------- /TTD/csts.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | const char aScopeOfLicense[] = 3 | { 4 | 0x53, 0x43, 0x4F, 0x50, 0x45, 0x20, 0x4F, 0x46, 0x20, 0x4C, 5 | 0x49, 0x43, 0x45, 0x4E, 0x53, 0x45, 0x2E, 0x20, 0x54, 0x68, 6 | 0x65, 0x20, 0x73, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 7 | 0x20, 0x69, 0x73, 0x20, 0x6C, 0x69, 0x63, 0x65, 0x6E, 0x73, 8 | 0x65, 0x64, 0x2C, 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x73, 0x6F, 9 | 0x6C, 0x64, 0x2E, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x61, 10 | 0x67, 0x72, 0x65, 0x65, 0x6D, 0x65, 0x6E, 0x74, 0x20, 0x6F, 11 | 0x6E, 0x6C, 0x79, 0x20, 0x67, 0x69, 0x76, 0x65, 0x73, 0x20, 12 | 0x79, 0x6F, 0x75, 0x20, 0x73, 0x6F, 0x6D, 0x65, 0x20, 0x72, 13 | 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x74, 0x6F, 0x20, 0x00, 14 | 0x00, 0x00, 0x75, 0x73, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 15 | 0x73, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x2E, 0x20, 16 | 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 17 | 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x73, 0x20, 0x61, 18 | 0x6C, 0x6C, 0x20, 0x6F, 0x74, 0x68, 0x65, 0x72, 0x20, 0x72, 19 | 0x69, 0x67, 0x68, 0x74, 0x73, 0x2E, 0x20, 0x55, 0x6E, 0x6C, 20 | 0x65, 0x73, 0x73, 0x20, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 21 | 0x61, 0x62, 0x6C, 0x65, 0x20, 0x6C, 0x61, 0x77, 0x20, 0x67, 22 | 0x69, 0x76, 0x65, 0x73, 0x20, 0x79, 0x6F, 0x75, 0x20, 0x6D, 23 | 0x6F, 0x72, 0x65, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 24 | 0x20, 0x00, 0x00, 0x00, 0x64, 0x65, 0x73, 0x70, 0x69, 0x74, 25 | 0x65, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6C, 0x69, 0x6D, 26 | 0x69, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2C, 0x20, 0x79, 27 | 0x6F, 0x75, 0x20, 0x6D, 0x61, 0x79, 0x20, 0x75, 0x73, 0x65, 28 | 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x6F, 0x66, 0x74, 0x77, 29 | 0x61, 0x72, 0x65, 0x20, 0x6F, 0x6E, 0x6C, 0x79, 0x20, 0x61, 30 | 0x73, 0x20, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x6C, 31 | 0x79, 0x20, 0x70, 0x65, 0x72, 0x6D, 0x69, 0x74, 0x74, 0x65, 32 | 0x64, 0x20, 0x69, 0x6E, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 33 | 0x61, 0x67, 0x72, 0x65, 0x65, 0x6D, 0x65, 0x6E, 0x74, 0x2E, 34 | 0x20, 0x49, 0x6E, 0x20, 0x00, 0x00, 0x64, 0x6F, 0x69, 0x6E, 35 | 0x67, 0x20, 0x73, 0x6F, 0x2C, 0x20, 0x79, 0x6F, 0x75, 0x20, 36 | 0x6D, 0x75, 0x73, 0x74, 0x20, 0x63, 0x6F, 0x6D, 0x70, 0x6C, 37 | 0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x6E, 0x79, 38 | 0x20, 0x74, 0x65, 0x63, 0x68, 0x6E, 0x69, 0x63, 0x61, 0x6C, 39 | 0x20, 0x6C, 0x69, 0x6D, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6F, 40 | 0x6E, 0x73, 0x20, 0x69, 0x6E, 0x20, 0x74, 0x68, 0x65, 0x20, 41 | 0x73, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x74, 42 | 0x68, 0x61, 0x74, 0x20, 0x6F, 0x6E, 0x6C, 0x79, 0x20, 0x61, 43 | 0x6C, 0x6C, 0x6F, 0x77, 0x20, 0x79, 0x6F, 0x75, 0x20, 0x74, 44 | 0x6F, 0x20, 0x75, 0x73, 0x65, 0x20, 0x00, 0x00, 0x69, 0x74, 45 | 0x20, 0x69, 0x6E, 0x20, 0x63, 0x65, 0x72, 0x74, 0x61, 0x69, 46 | 0x6E, 0x20, 0x77, 0x61, 0x79, 0x73, 0x2E, 0x20, 0x46, 0x6F, 47 | 0x72, 0x20, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x2C, 48 | 0x20, 0x69, 0x66, 0x20, 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 49 | 0x6F, 0x66, 0x74, 0x20, 0x74, 0x65, 0x63, 0x68, 0x6E, 0x69, 50 | 0x63, 0x61, 0x6C, 0x6C, 0x79, 0x20, 0x6C, 0x69, 0x6D, 0x69, 51 | 0x74, 0x73, 0x20, 0x6F, 0x72, 0x20, 0x64, 0x69, 0x73, 0x61, 52 | 0x62, 0x6C, 0x65, 0x73, 0x20, 0x65, 0x78, 0x74, 0x65, 0x6E, 53 | 0x73, 0x69, 0x62, 0x69, 0x6C, 0x69, 0x74, 0x79, 0x20, 0x66, 54 | 0x6F, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x00, 0x00, 0x00, 55 | 0x73, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x2C, 0x20, 56 | 0x79, 0x6F, 0x75, 0x20, 0x6D, 0x61, 0x79, 0x20, 0x6E, 0x6F, 57 | 0x74, 0x20, 0x65, 0x78, 0x74, 0x65, 0x6E, 0x64, 0x20, 0x74, 58 | 0x68, 0x65, 0x20, 0x73, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 59 | 0x65, 0x20, 0x62, 0x79, 0x2C, 0x20, 0x61, 0x6D, 0x6F, 0x6E, 60 | 0x67, 0x20, 0x6F, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, 0x68, 61 | 0x69, 0x6E, 0x67, 0x73, 0x2C, 0x20, 0x6C, 0x6F, 0x61, 0x64, 62 | 0x69, 0x6E, 0x67, 0x20, 0x6F, 0x72, 0x20, 0x69, 0x6E, 0x6A, 63 | 0x65, 0x63, 0x74, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x6E, 0x74, 64 | 0x6F, 0x20, 0x74, 0x68, 0x65, 0x20, 0x00, 0x00, 0x00, 0x00, 65 | 0x00, 0x00, 0x73, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 66 | 0x20, 0x61, 0x6E, 0x79, 0x20, 0x6E, 0x6F, 0x6E, 0x2D, 0x4D, 67 | 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x61, 68 | 0x64, 0x64, 0x2D, 0x69, 0x6E, 0x73, 0x2C, 0x20, 0x6D, 0x61, 69 | 0x63, 0x72, 0x6F, 0x73, 0x2C, 0x20, 0x6F, 0x72, 0x20, 0x70, 70 | 0x61, 0x63, 0x6B, 0x61, 0x67, 0x65, 0x73, 0x3B, 0x20, 0x6D, 71 | 0x6F, 0x64, 0x69, 0x66, 0x79, 0x69, 0x6E, 0x67, 0x20, 0x74, 72 | 0x68, 0x65, 0x20, 0x73, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 73 | 0x65, 0x20, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 74 | 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6E, 0x67, 0x73, 0x3B, 75 | 0x20, 0x00, 0x00, 0x00, 0x6F, 0x72, 0x20, 0x61, 0x64, 0x64, 76 | 0x69, 0x6E, 0x67, 0x20, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 77 | 0x65, 0x73, 0x20, 0x6F, 0x72, 0x20, 0x66, 0x75, 0x6E, 0x63, 78 | 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x69, 0x74, 0x79, 0x20, 79 | 0x65, 0x71, 0x75, 0x69, 0x76, 0x61, 0x6C, 0x65, 0x6E, 0x74, 80 | 0x20, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x66, 81 | 0x6F, 0x75, 0x6E, 0x64, 0x20, 0x69, 0x6E, 0x20, 0x56, 0x69, 82 | 0x73, 0x75, 0x61, 0x6C, 0x20, 0x53, 0x74, 0x75, 0x64, 0x69, 83 | 0x6F, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x56, 0x69, 0x73, 0x75, 84 | 0x61, 0x6C, 0x20, 0x53, 0x74, 0x75, 0x64, 0x69, 0x6F, 0x20, 85 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x64, 0x65, 86 | 0x20, 0x70, 0x72, 0x6F, 0x64, 0x75, 0x63, 0x74, 0x73, 0x2E, 87 | 0x20, 0x59, 0x6F, 0x75, 0x20, 0x6D, 0x61, 0x79, 0x20, 0x6E, 88 | 0x6F, 0x74, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 89 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 90 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 91 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 92 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 93 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 94 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 95 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 96 | 0x77, 0x6F, 0x72, 0x6B, 0x20, 0x61, 0x72, 0x6F, 0x75, 0x6E, 97 | 0x64, 0x20, 0x61, 0x6E, 0x79, 0x20, 0x74, 0x65, 0x63, 0x68, 98 | 0x6E, 0x69, 0x63, 0x61, 0x6C, 0x20, 0x6C, 0x69, 0x6D, 0x69, 99 | 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x20, 0x69, 0x6E, 100 | 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x6F, 0x66, 0x74, 0x77, 101 | 0x61, 0x72, 0x65, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 102 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 103 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 104 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 105 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 106 | 0x2A, 0x20, 0x20, 0x20, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 107 | 0x65, 0x20, 0x65, 0x6E, 0x67, 0x69, 0x6E, 0x65, 0x65, 0x72, 108 | 0x2C, 0x20, 0x64, 0x65, 0x63, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 109 | 0x65, 0x20, 0x6F, 0x72, 0x20, 0x64, 0x69, 0x73, 0x61, 0x73, 110 | 0x73, 0x65, 0x6D, 0x62, 0x6C, 0x65, 0x20, 0x74, 0x68, 0x65, 111 | 0x20, 0x73, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x2C, 112 | 0x20, 0x6F, 0x72, 0x20, 0x6F, 0x74, 0x68, 0x65, 0x72, 0x77, 113 | 0x69, 0x73, 0x65, 0x20, 0x61, 0x74, 0x74, 0x65, 0x6D, 0x70, 114 | 0x74, 0x20, 0x74, 0x6F, 0x20, 0x64, 0x65, 0x72, 0x69, 0x76, 115 | 0x65, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 116 | 0x00, 0x00, 0x74, 0x68, 0x65, 0x20, 0x73, 0x6F, 0x75, 0x72, 117 | 0x63, 0x65, 0x20, 0x63, 0x6F, 0x64, 0x65, 0x20, 0x66, 0x6F, 118 | 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x6F, 0x66, 0x74, 119 | 0x77, 0x61, 0x72, 0x65, 0x2C, 0x20, 0x65, 0x78, 0x63, 0x65, 120 | 0x70, 0x74, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x74, 0x6F, 0x20, 121 | 0x74, 0x68, 0x65, 0x20, 0x65, 0x78, 0x74, 0x65, 0x6E, 0x74, 122 | 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 123 | 0x62, 0x79, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64, 0x20, 0x70, 124 | 0x61, 0x72, 0x74, 0x79, 0x20, 0x6C, 0x69, 0x63, 0x65, 0x6E, 125 | 0x73, 0x69, 0x6E, 0x67, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 126 | 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x72, 0x6D, 0x73, 0x20, 127 | 0x67, 0x6F, 0x76, 0x65, 0x72, 0x6E, 0x69, 0x6E, 0x67, 0x20, 128 | 0x75, 0x73, 0x65, 0x20, 0x6F, 0x66, 0x20, 0x63, 0x65, 0x72, 129 | 0x74, 0x61, 0x69, 0x6E, 0x20, 0x6F, 0x70, 0x65, 0x6E, 0x2D, 130 | 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x63, 0x6F, 0x6D, 131 | 0x70, 0x6F, 0x6E, 0x65, 0x6E, 0x74, 0x73, 0x20, 0x74, 0x68, 132 | 0x61, 0x74, 0x20, 0x6D, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 133 | 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x65, 0x64, 0x20, 0x77, 134 | 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x6F, 135 | 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x3B, 0x00, 0x00, 0x00, 136 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x20, 0x20, 0x20, 137 | 0x72, 0x65, 0x6D, 0x6F, 0x76, 0x65, 0x2C, 0x20, 0x6D, 0x69, 138 | 0x6E, 0x69, 0x6D, 0x69, 0x7A, 0x65, 0x2C, 0x20, 0x62, 0x6C, 139 | 0x6F, 0x63, 0x6B, 0x20, 0x6F, 0x72, 0x20, 0x6D, 0x6F, 0x64, 140 | 0x69, 0x66, 0x79, 0x20, 0x61, 0x6E, 0x79, 0x20, 0x6E, 0x6F, 141 | 0x74, 0x69, 0x63, 0x65, 0x73, 0x20, 0x6F, 0x66, 0x20, 0x4D, 142 | 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x6F, 143 | 0x72, 0x20, 0x69, 0x74, 0x73, 0x20, 0x73, 0x75, 0x70, 0x70, 144 | 0x6C, 0x69, 0x65, 0x72, 0x73, 0x20, 0x69, 0x6E, 0x20, 0x74, 145 | 0x68, 0x65, 0x20, 0x73, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 146 | 0x65, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x20, 147 | 0x20, 0x20, 0x75, 0x73, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 148 | 0x73, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x69, 149 | 0x6E, 0x20, 0x61, 0x6E, 0x79, 0x20, 0x77, 0x61, 0x79, 0x20, 150 | 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x67, 151 | 0x61, 0x69, 0x6E, 0x73, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 152 | 0x6C, 0x61, 0x77, 0x3B, 0x20, 0x6F, 0x72, 0x00, 0x00, 0x00, 153 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 154 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 155 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 156 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 157 | 0x2A, 0x20, 0x20, 0x20, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2C, 158 | 0x20, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x73, 0x68, 0x2C, 0x20, 159 | 0x72, 0x65, 0x6E, 0x74, 0x20, 0x6F, 0x72, 0x20, 0x6C, 0x65, 160 | 0x61, 0x73, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x6F, 161 | 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x2C, 0x20, 0x6F, 0x72, 162 | 0x20, 0x70, 0x72, 0x6F, 0x76, 0x69, 0x64, 0x65, 0x20, 0x74, 163 | 0x68, 0x65, 0x20, 0x73, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 164 | 0x65, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x73, 0x74, 0x61, 165 | 0x6E, 0x64, 0x2D, 0x61, 0x6C, 0x6F, 0x6E, 0x65, 0x20, 0x6F, 166 | 0x66, 0x66, 0x65, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x00, 0x00, 167 | 0x00, 0x00, 0x66, 0x6F, 0x72, 0x20, 0x6F, 0x74, 0x68, 0x65, 168 | 0x72, 0x73, 0x20, 0x74, 0x6F, 0x20, 0x75, 0x73, 0x65, 0x2C, 169 | 0x20, 0x6F, 0x72, 0x20, 0x74, 0x72, 0x61, 0x6E, 0x73, 0x66, 170 | 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x6F, 0x66, 171 | 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x6F, 0x72, 0x20, 0x74, 172 | 0x68, 0x69, 0x73, 0x20, 0x61, 0x67, 0x72, 0x65, 0x65, 0x6D, 173 | 0x65, 0x6E, 0x74, 0x20, 0x74, 0x6F, 0x20, 0x61, 0x6E, 0x79, 174 | 0x20, 0x74, 0x68, 0x69, 0x72, 0x64, 0x20, 0x70, 0x61, 0x72, 175 | 0x74, 0x79, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 176 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 177 | 0x00, 0x00, 0x00, 0x00 178 | }; 179 | 180 | const char aVYHVAX4gukZ8Wv[] = 181 | { 182 | 0x56, 0x2B, 0x79, 0x2C, 0x28, 0x48, 0x60, 0x76, 0x26, 0x41, 183 | 0x5C, 0x40, 0x78, 0x2B, 0x3B, 0x34, 0x47, 0x75, 0x4B, 0x3C, 184 | 0x24, 0x7A, 0x5D, 0x2E, 0x2E, 0x3F, 0x38, 0x23, 0x77, 0x56, 185 | 0x5A, 0x6E, 0x27, 0x2A, 0x2B, 0x7D, 0x6A, 0x31, 0x45, 0x5C, 186 | 0x24, 0x6B, 0x30, 0x24, 0x2F, 0x6C, 0x76, 0x6B, 0x70, 0x62, 187 | 0x38, 0x34, 0x36, 0x4B, 0x3A, 0x6B, 0x66, 0x22, 0x43, 0x49, 188 | 0x5C, 0x59, 0x6C, 0x2A, 0x64, 0x34, 0x20, 0x2F, 0x20, 0x2E, 189 | 0x52, 0x2C, 0x7B, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 190 | 0x25, 0x26, 0x42, 0x48, 0x3C, 0x2F, 0x27, 0x65, 0x7B, 0x55, 191 | 0x60, 0x3E, 0x46, 0x3E, 0x6B, 0x73, 0x33, 0x6C, 0x6B, 0x67, 192 | 0x53, 0x58, 0x3E, 0x45, 0x54, 0x71, 0x7B, 0x56, 0x73, 0x75, 193 | 0x2D, 0x69, 0x3C, 0x6B, 0x56, 0x63, 0x7D, 0x29, 0x50, 0x28, 194 | 0x48, 0x60, 0x77, 0x4B, 0x6C, 0x54, 0x76, 0x75, 0x50, 0x45, 195 | 0x44, 0x3E, 0x42, 0x4C, 0x41, 0x58, 0x29, 0x43, 0x30, 0x58, 196 | 0x31, 0x73, 0x4E, 0x5C, 0x5B, 0x75, 0x34, 0x2C, 0x48, 0x00, 197 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6B, 198 | 0x40, 0x77, 0x4A, 0x72, 0x26, 0x37, 0x68, 0x2B, 0x4E, 0x5C, 199 | 0x60, 0x4A, 0x66, 0x6B, 0x34, 0x44, 0x24, 0x6E, 0x62, 0x63, 200 | 0x64, 0x4B, 0x65, 0x6E, 0x5E, 0x56, 0x6B, 0x4F, 0x48, 0x3C, 201 | 0x27, 0x4B, 0x4E, 0x3A, 0x25, 0x75, 0x56, 0x4F, 0x27, 0x47, 202 | 0x33, 0x65, 0x76, 0x23, 0x29, 0x2E, 0x24, 0x67, 0x4D, 0x24, 203 | 0x72, 0x2F, 0x3D, 0x3D, 0x71, 0x74, 0x59, 0x5D, 0x50, 0x4A, 204 | 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 205 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x6F, 0x77, 206 | 0x43, 0x6F, 0x5C, 0x31, 0x5C, 0x42, 0x3A, 0x39, 0x36, 0x34, 207 | 0x27, 0x34, 0x32, 0x29, 0x7B, 0x63, 0x30, 0x3C, 0x71, 0x2C, 208 | 0x3E, 0x5C, 0x5C, 0x31, 0x22, 0x20, 0x2D, 0x20, 0x21, 0x40, 209 | 0x31, 0x77, 0x7D, 0x58, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 210 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 211 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 212 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 213 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x27, 0x2B, 0x7C, 214 | 0x69, 0x35, 0x3F, 0x7D, 0x5D, 0x57, 0x50, 0x4C, 0x53, 0x7A, 215 | 0x65, 0x31, 0x57, 0x45, 0x36, 0x3B, 0x27, 0x7D, 0x54, 0x67, 216 | 0x3B, 0x38, 0x33, 0x76, 0x38, 0x56, 0x32, 0x7A, 0x7D, 0x6F, 217 | 0x76, 0x26, 0x78, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 218 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 219 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 220 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 221 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x58, 0x6A, 0x6D, 0x23, 222 | 0x58, 0x3A, 0x76, 0x63, 0x4B, 0x64, 0x59, 0x6E, 0x30, 0x56, 223 | 0x6E, 0x6B, 0x72, 0x4C, 0x51, 0x44, 0x4F, 0x77, 0x79, 0x22, 224 | 0x3B, 0x20, 0x2E, 0x33, 0x52, 0x42, 0x53, 0x57, 0x25, 0x52, 225 | 0x49, 0x64, 0x4C, 0x4F, 0x5A, 0x37, 0x28, 0x47, 0x4B, 0x52, 226 | 0x20, 0x2F, 0x20, 0x31, 0x3E, 0x28, 0x77, 0x63, 0x69, 0x6D, 227 | 0x65, 0x3A, 0x35, 0x71, 0x44, 0x73, 0x20, 0x3F, 0x20, 0x5B, 228 | 0x6C, 0x35, 0x62, 0x35, 0x44, 0x4B, 0x52, 0x20, 0x3D, 0x20, 229 | 0x39, 0x00, 0x00, 0x00, 0x31, 0x55, 0x48, 0x4A, 0x5E, 0x37, 230 | 0x51, 0x74, 0x2E, 0x53, 0x50, 0x74, 0x63, 0x3C, 0x51, 0x3E, 231 | 0x36, 0x3E, 0x23, 0x24, 0x6A, 0x45, 0x2B, 0x3E, 0x3E, 0x65, 232 | 0x22, 0x50, 0x2A, 0x4D, 0x62, 0x20, 0x7C, 0x20, 0x62, 0x4C, 233 | 0x20, 0x2F, 0x20, 0x60, 0x48, 0x35, 0x27, 0x68, 0x37, 0x6E, 234 | 0x50, 0x3A, 0x6F, 0x77, 0x20, 0x2B, 0x20, 0x7D, 0x7A, 0x61, 235 | 0x71, 0x44, 0x42, 0x32, 0x2C, 0x22, 0x5C, 0x28, 0x38, 0x36, 236 | 0x58, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 237 | 0x00, 0x00, 0x00, 0x3B, 0x4A, 0x4E, 0x43, 0x7B, 0x36, 0x62, 238 | 0x42, 0x2C, 0x3E, 0x52, 0x32, 0x47, 0x4D, 0x79, 0x3E, 0x74, 239 | 0x21, 0x26, 0x4A, 0x5E, 0x52, 0x53, 0x79, 0x7D, 0x32, 0x32, 240 | 0x40, 0x79, 0x50, 0x38, 0x7C, 0x51, 0x3B, 0x70, 0x68, 0x3A, 241 | 0x5C, 0x6D, 0x6A, 0x56, 0x3D, 0x78, 0x4C, 0x23, 0x79, 0x27, 242 | 0x29, 0x79, 0x2B, 0x4E, 0x7C, 0x63, 0x00, 0x00, 0x00, 0x00, 243 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 244 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 245 | 0x00, 0x00, 0x2F, 0x76, 0x4A, 0x7B, 0x23, 0x4F, 0x63, 0x24, 246 | 0x78, 0x67, 0x64, 0x75, 0x4F, 0x24, 0x5C, 0x36, 0x55, 0x52, 247 | 0x3E, 0x43, 0x5F, 0x35, 0x73, 0x3F, 0x4D, 0x32, 0x58, 0x5B, 248 | 0x65, 0x58, 0x6D, 0x61, 0x3A, 0x29, 0x55, 0x79, 0x72, 0x78, 249 | 0x62, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 250 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 251 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 252 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 253 | 0x00, 0x3F, 0x4D, 0x59, 0x22, 0x2C, 0x34, 0x5F, 0x62, 0x39, 254 | 0x4C, 0x70, 0x22, 0x79, 0x71, 0x47, 0x75, 0x33, 0x37, 0x68, 255 | 0x34, 0x2E, 0x7D, 0x5D, 0x77, 0x23, 0x28, 0x76, 0x62, 0x4E, 256 | 0x30, 0x63, 0x4B, 0x5E, 0x3F, 0x52, 0x5D, 0x22, 0x76, 0x3C, 257 | 0x58, 0x46, 0x67, 0x24, 0x5C, 0x4E, 0x5A, 0x60, 0x5B, 0x4B, 258 | 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 259 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 260 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 261 | 0x63, 0x5F, 0x23, 0x4C, 0x65, 0x66, 0x24, 0x56, 0x50, 0x5A, 262 | 0x4B, 0x52, 0x29, 0x52, 0x25, 0x66, 0x23, 0x59, 0x45, 0x53, 263 | 0x64, 0x54, 0x4F, 0x64, 0x50, 0x3B, 0x71, 0x24, 0x62, 0x6B, 264 | 0x2B, 0x38, 0x60, 0x3A, 0x2C, 0x75, 0x29, 0x48, 0x22, 0x52, 265 | 0x78, 0x66, 0x5F, 0x5C, 0x20, 0x2F, 0x20, 0x4E, 0x44, 0x4F, 266 | 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 267 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 268 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 269 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 270 | }; 271 | 272 | unsigned char version_guid[] = 273 | { 274 | 0xA5, 0x20, 0x34, 0x4D, 0xEF, 0x37, 0x14, 0x41, 0xAE, 0x91, 275 | 0x63, 0xD0, 0x37, 0x8C, 0x84, 0xA9, 0x00, 0x00, 0x00, 0x00, 276 | 0x00, 0x00, 0x00, 0x00 277 | }; 278 | 279 | unsigned char GUID_Cursor[] = 280 | { 281 | 0xAB, 0xE6, 0xD2, 0xB1, 0x52, 0x90, 0x72, 0x4B, 0x99, 0x9E, 282 | 0xA8, 0x8B, 0xA8, 0x68, 0xF8, 0x99 283 | }; -------------------------------------------------------------------------------- /TTD/sha256.cpp: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Filename: sha256.c 3 | * Author: Brad Conte (brad AT bradconte.com) 4 | * Copyright: 5 | * Disclaimer: This code is presented "as is" without any guarantees. 6 | * Details: Implementation of the SHA-256 hashing algorithm. 7 | SHA-256 is one of the three algorithms in the SHA2 8 | specification. The others, SHA-384 and SHA-512, are not 9 | offered in this implementation. 10 | Algorithm specification can be found here: 11 | * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf 12 | This implementation uses little endian byte order. 13 | *********************************************************************/ 14 | 15 | /*************************** HEADER FILES ***************************/ 16 | #include 17 | #include 18 | #include "sha256.h" 19 | 20 | /****************************** MACROS ******************************/ 21 | #define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) 22 | #define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) 23 | 24 | #define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) 25 | #define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) 26 | #define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) 27 | #define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) 28 | #define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) 29 | #define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) 30 | 31 | /**************************** VARIABLES *****************************/ 32 | static const WORDX k[64] = { 33 | 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, 34 | 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, 35 | 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, 36 | 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, 37 | 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, 38 | 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, 39 | 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, 40 | 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 41 | }; 42 | 43 | /*********************** FUNCTION DEFINITIONS ***********************/ 44 | void sha256_transform(SHA256_CTX* ctx, const BYTE data[]) 45 | { 46 | WORDX a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; 47 | 48 | for (i = 0, j = 0; i < 16; ++i, j += 4) 49 | m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); 50 | for (; i < 64; ++i) 51 | m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; 52 | 53 | a = ctx->state[0]; 54 | b = ctx->state[1]; 55 | c = ctx->state[2]; 56 | d = ctx->state[3]; 57 | e = ctx->state[4]; 58 | f = ctx->state[5]; 59 | g = ctx->state[6]; 60 | h = ctx->state[7]; 61 | 62 | for (i = 0; i < 64; ++i) { 63 | t1 = h + EP1(e) + CH(e, f, g) + k[i] + m[i]; 64 | t2 = EP0(a) + MAJ(a, b, c); 65 | h = g; 66 | g = f; 67 | f = e; 68 | e = d + t1; 69 | d = c; 70 | c = b; 71 | b = a; 72 | a = t1 + t2; 73 | } 74 | 75 | ctx->state[0] += a; 76 | ctx->state[1] += b; 77 | ctx->state[2] += c; 78 | ctx->state[3] += d; 79 | ctx->state[4] += e; 80 | ctx->state[5] += f; 81 | ctx->state[6] += g; 82 | ctx->state[7] += h; 83 | } 84 | 85 | void sha256_init(SHA256_CTX* ctx) 86 | { 87 | ctx->datalen = 0; 88 | ctx->bitlen = 0; 89 | ctx->state[0] = 0x6a09e667; 90 | ctx->state[1] = 0xbb67ae85; 91 | ctx->state[2] = 0x3c6ef372; 92 | ctx->state[3] = 0xa54ff53a; 93 | ctx->state[4] = 0x510e527f; 94 | ctx->state[5] = 0x9b05688c; 95 | ctx->state[6] = 0x1f83d9ab; 96 | ctx->state[7] = 0x5be0cd19; 97 | } 98 | 99 | void sha256_update(SHA256_CTX* ctx, const BYTE data[], size_t len) 100 | { 101 | WORDX i; 102 | 103 | for (i = 0; i < len; ++i) { 104 | ctx->data[ctx->datalen] = data[i]; 105 | ctx->datalen++; 106 | if (ctx->datalen == 64) { 107 | sha256_transform(ctx, ctx->data); 108 | ctx->bitlen += 512; 109 | ctx->datalen = 0; 110 | } 111 | } 112 | } 113 | 114 | void sha256_final(SHA256_CTX* ctx, BYTE hash[]) 115 | { 116 | WORDX i; 117 | 118 | i = ctx->datalen; 119 | 120 | // Pad whatever data is left in the buffer. 121 | if (ctx->datalen < 56) { 122 | ctx->data[i++] = 0x80; 123 | while (i < 56) 124 | ctx->data[i++] = 0x00; 125 | } 126 | else { 127 | ctx->data[i++] = 0x80; 128 | while (i < 64) 129 | ctx->data[i++] = 0x00; 130 | sha256_transform(ctx, ctx->data); 131 | memset(ctx->data, 0, 56); 132 | } 133 | 134 | // Append to the padding the total message's length in bits and transform. 135 | ctx->bitlen += ctx->datalen * 8; 136 | ctx->data[63] = (BYTE)ctx->bitlen; 137 | ctx->data[62] = (BYTE)(ctx->bitlen >> 8); 138 | ctx->data[61] = (BYTE)(ctx->bitlen >> 16); 139 | ctx->data[60] = (BYTE)(ctx->bitlen >> 24); 140 | ctx->data[59] = (BYTE)(ctx->bitlen >> 32); 141 | ctx->data[58] = (BYTE)(ctx->bitlen >> 40); 142 | ctx->data[57] = (BYTE)(ctx->bitlen >> 48); 143 | ctx->data[56] = (BYTE)(ctx->bitlen >> 56); 144 | sha256_transform(ctx, ctx->data); 145 | 146 | // Since this implementation uses little endian byte ordering and SHA uses big endian, 147 | // reverse all the bytes when copying the final state to the output hash. 148 | for (i = 0; i < 4; ++i) { 149 | hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; 150 | hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; 151 | hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; 152 | hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; 153 | hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; 154 | hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; 155 | hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; 156 | hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; 157 | } 158 | } -------------------------------------------------------------------------------- /TTD/sha256.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Filename: sha256.h 3 | * Author: Brad Conte (brad AT bradconte.com) 4 | * Copyright: 5 | * Disclaimer: This code is presented "as is" without any guarantees. 6 | * Details: Defines the API for the corresponding SHA1 implementation. 7 | *********************************************************************/ 8 | 9 | #ifndef SHA256_H 10 | #define SHA256_H 11 | 12 | /*************************** HEADER FILES ***************************/ 13 | #include 14 | 15 | /****************************** MACROS ******************************/ 16 | #define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest 17 | 18 | /**************************** DATA TYPES ****************************/ 19 | typedef unsigned char BYTE; // 8-bit byte 20 | typedef unsigned int WORDX; // 32-bit word, change to "long" for 16-bit machines 21 | 22 | typedef struct { 23 | BYTE data[64]; 24 | WORDX datalen; 25 | unsigned long long bitlen; 26 | WORDX state[8]; 27 | } SHA256_CTX; 28 | 29 | /*********************** FUNCTION DECLARATIONS **********************/ 30 | void sha256_init(SHA256_CTX* ctx); 31 | void sha256_update(SHA256_CTX* ctx, const BYTE data[], size_t len); 32 | void sha256_final(SHA256_CTX* ctx, BYTE hash[]); 33 | 34 | #endif // SHA256_H 35 | -------------------------------------------------------------------------------- /TTD/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | // From https://gist.github.com/ccbrown/9722406 4 | void DumpHex(const void* data, size_t size) { 5 | char ascii[17]; 6 | size_t i, j; 7 | ascii[16] = '\0'; 8 | for (i = 0; i < size; ++i) { 9 | printf("%02X ", ((unsigned char*)data)[i]); 10 | if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') { 11 | ascii[i % 16] = ((unsigned char*)data)[i]; 12 | } 13 | else { 14 | ascii[i % 16] = '.'; 15 | } 16 | if ((i + 1) % 8 == 0 || i + 1 == size) { 17 | printf(" "); 18 | if ((i + 1) % 16 == 0) { 19 | printf("| %s \n", ascii); 20 | } 21 | else if (i + 1 == size) { 22 | ascii[(i + 1) % 16] = '\0'; 23 | if ((i + 1) % 16 <= 8) { 24 | printf(" "); 25 | } 26 | for (j = (i + 1) % 16; j < 16; ++j) { 27 | printf(" "); 28 | } 29 | printf("| %s \n", ascii); 30 | } 31 | } 32 | } 33 | } 34 | 35 | static char encoding_table[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 36 | 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 37 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 38 | 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 39 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 40 | 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 41 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', 42 | '4', '5', '6', '7', '8', '9', '+', '/' }; 43 | static char* decoding_table = NULL; 44 | static int mod_table[] = { 0, 2, 1 }; 45 | 46 | // Based on https://stackoverflow.com/a/6782480 47 | char* base64_encode(const unsigned char* data, 48 | size_t input_length, 49 | size_t* output_length) { 50 | 51 | *output_length = 4 * ((input_length + 2) / 3); 52 | 53 | char* encoded_data = (char*)malloc(*output_length); 54 | if (encoded_data == NULL) return NULL; 55 | 56 | for (int i = 0, j = 0; i < input_length;) { 57 | 58 | uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0; 59 | uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0; 60 | uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0; 61 | 62 | uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; 63 | 64 | encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F]; 65 | encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F]; 66 | encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F]; 67 | encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F]; 68 | } 69 | 70 | // no padding 71 | for (int i = 0; i < mod_table[input_length % 3]; i++) 72 | encoded_data[*output_length - 1 - i] = '\x00'; 73 | 74 | return encoded_data; 75 | } 76 | -------------------------------------------------------------------------------- /TTD/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | char* base64_encode(const unsigned char* data, 7 | size_t input_length, 8 | size_t* output_length); 9 | 10 | void DumpHex(const void* data, size_t size); -------------------------------------------------------------------------------- /assets/lsm_coverage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/commial/ttd-bindings/6a5f08cbc3b31a08d73b179d277cf5ce51a5ae89/assets/lsm_coverage.png -------------------------------------------------------------------------------- /assets/tenet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/commial/ttd-bindings/6a5f08cbc3b31a08d73b179d277cf5ce51a5ae89/assets/tenet.png -------------------------------------------------------------------------------- /assets/timeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/commial/ttd-bindings/6a5f08cbc3b31a08d73b179d277cf5ce51a5ae89/assets/timeline.png -------------------------------------------------------------------------------- /cmake.toml: -------------------------------------------------------------------------------- 1 | # Reference: https://build-cpp.github.io/cmkr/cmake-toml 2 | [project] 3 | name = "ttd-bindings" 4 | 5 | [options] 6 | TTD_BUILD_EXAMPLES = "root" 7 | TTD_PYTHON_BINDINGS = "root" 8 | 9 | [variables] 10 | PYBIND11_FINDPYTHON = true 11 | 12 | [fetch-content.pybind11] 13 | condition = "TTD_PYTHON_BINDINGS" 14 | git = "https://github.com/pybind/pybind11" 15 | tag = "v2.10.3" 16 | 17 | [template.pyd] 18 | condition = "TTD_PYTHON_BINDINGS" 19 | type = "shared" 20 | add-function = "pybind11_add_module" 21 | pass-sources = true 22 | 23 | [template.example] 24 | condition = "TTD_BUILD_EXAMPLES" 25 | type = "executable" 26 | link-libraries = ["TTD::TTD"] 27 | 28 | [target.TTD] 29 | type = "static" 30 | alias = "TTD::TTD" 31 | sources = [ 32 | "TTD/*.cpp", 33 | "TTD/*.hpp", 34 | "TTD/*.h", 35 | ] 36 | include-directories = ["."] 37 | 38 | [target.pyTTD] 39 | type = "pyd" 40 | sources = ["python-bindings/module.cpp"] 41 | link-libraries = ["TTD::TTD"] 42 | 43 | [target.example_api] 44 | type = "example" 45 | sources = ["example_api/main.cpp"] 46 | 47 | [target.example_calltree] 48 | type = "example" 49 | sources = ["example_calltree/main.cpp"] 50 | 51 | [target.example_cov] 52 | type = "example" 53 | sources = ["example_cov/main.cpp"] 54 | 55 | [target.example_diff] 56 | type = "example" 57 | sources = ["example_diff/main.cpp"] 58 | 59 | [target.example_tenet] 60 | type = "example" 61 | sources = ["example_tenet/main.cpp"] -------------------------------------------------------------------------------- /cmkr.cmake: -------------------------------------------------------------------------------- 1 | include_guard() 2 | 3 | # Change these defaults to point to your infrastructure if desired 4 | set(CMKR_REPO "https://github.com/build-cpp/cmkr" CACHE STRING "cmkr git repository" FORCE) 5 | set(CMKR_TAG "v0.2.21" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE) 6 | set(CMKR_COMMIT_HASH "" CACHE STRING "cmkr git commit hash (optional)" FORCE) 7 | 8 | # To bootstrap/generate a cmkr project: cmake -P cmkr.cmake 9 | if(CMAKE_SCRIPT_MODE_FILE) 10 | set(CMAKE_BINARY_DIR "${CMAKE_BINARY_DIR}/build") 11 | set(CMAKE_CURRENT_BINARY_DIR "${CMAKE_BINARY_DIR}") 12 | file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}") 13 | endif() 14 | 15 | # Set these from the command line to customize for development/debugging purposes 16 | set(CMKR_EXECUTABLE "" CACHE FILEPATH "cmkr executable") 17 | set(CMKR_SKIP_GENERATION OFF CACHE BOOL "skip automatic cmkr generation") 18 | set(CMKR_BUILD_TYPE "Debug" CACHE STRING "cmkr build configuration") 19 | mark_as_advanced(CMKR_REPO CMKR_TAG CMKR_COMMIT_HASH CMKR_EXECUTABLE CMKR_SKIP_GENERATION CMKR_BUILD_TYPE) 20 | 21 | # Disable cmkr if generation is disabled 22 | if(DEFINED ENV{CI} OR CMKR_SKIP_GENERATION OR CMKR_BUILD_SKIP_GENERATION) 23 | message(STATUS "[cmkr] Skipping automatic cmkr generation") 24 | unset(CMKR_BUILD_SKIP_GENERATION CACHE) 25 | macro(cmkr) 26 | endmacro() 27 | return() 28 | endif() 29 | 30 | # Disable cmkr if no cmake.toml file is found 31 | if(NOT CMAKE_SCRIPT_MODE_FILE AND NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake.toml") 32 | message(AUTHOR_WARNING "[cmkr] Not found: ${CMAKE_CURRENT_SOURCE_DIR}/cmake.toml") 33 | macro(cmkr) 34 | endmacro() 35 | return() 36 | endif() 37 | 38 | # Convert a Windows native path to CMake path 39 | if(CMKR_EXECUTABLE MATCHES "\\\\") 40 | string(REPLACE "\\" "/" CMKR_EXECUTABLE_CMAKE "${CMKR_EXECUTABLE}") 41 | set(CMKR_EXECUTABLE "${CMKR_EXECUTABLE_CMAKE}" CACHE FILEPATH "" FORCE) 42 | unset(CMKR_EXECUTABLE_CMAKE) 43 | endif() 44 | 45 | # Helper macro to execute a process (COMMAND_ERROR_IS_FATAL ANY is 3.19 and higher) 46 | function(cmkr_exec) 47 | execute_process(COMMAND ${ARGV} RESULT_VARIABLE CMKR_EXEC_RESULT) 48 | if(NOT CMKR_EXEC_RESULT EQUAL 0) 49 | message(FATAL_ERROR "cmkr_exec(${ARGV}) failed (exit code ${CMKR_EXEC_RESULT})") 50 | endif() 51 | endfunction() 52 | 53 | # Windows-specific hack (CMAKE_EXECUTABLE_PREFIX is not set at the moment) 54 | if(WIN32) 55 | set(CMKR_EXECUTABLE_NAME "cmkr.exe") 56 | else() 57 | set(CMKR_EXECUTABLE_NAME "cmkr") 58 | endif() 59 | 60 | # Use cached cmkr if found 61 | if(DEFINED ENV{CMKR_CACHE}) 62 | set(CMKR_DIRECTORY_PREFIX "$ENV{CMKR_CACHE}") 63 | string(REPLACE "\\" "/" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}") 64 | if(NOT CMKR_DIRECTORY_PREFIX MATCHES "\\/$") 65 | set(CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}/") 66 | endif() 67 | # Build in release mode for the cache 68 | set(CMKR_BUILD_TYPE "Release") 69 | else() 70 | set(CMKR_DIRECTORY_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/_cmkr_") 71 | endif() 72 | set(CMKR_DIRECTORY "${CMKR_DIRECTORY_PREFIX}${CMKR_TAG}") 73 | set(CMKR_CACHED_EXECUTABLE "${CMKR_DIRECTORY}/bin/${CMKR_EXECUTABLE_NAME}") 74 | 75 | # Helper function to check if a string starts with a prefix 76 | # Cannot use MATCHES, see: https://github.com/build-cpp/cmkr/issues/61 77 | function(cmkr_startswith str prefix result) 78 | string(LENGTH "${prefix}" prefix_length) 79 | string(LENGTH "${str}" str_length) 80 | if(prefix_length LESS_EQUAL str_length) 81 | string(SUBSTRING "${str}" 0 ${prefix_length} str_prefix) 82 | if(prefix STREQUAL str_prefix) 83 | set("${result}" ON PARENT_SCOPE) 84 | return() 85 | endif() 86 | endif() 87 | set("${result}" OFF PARENT_SCOPE) 88 | endfunction() 89 | 90 | # Handle upgrading logic 91 | if(CMKR_EXECUTABLE AND NOT CMKR_CACHED_EXECUTABLE STREQUAL CMKR_EXECUTABLE) 92 | cmkr_startswith("${CMKR_EXECUTABLE}" "${CMAKE_CURRENT_BINARY_DIR}/_cmkr" CMKR_STARTSWITH_BUILD) 93 | cmkr_startswith("${CMKR_EXECUTABLE}" "${CMKR_DIRECTORY_PREFIX}" CMKR_STARTSWITH_CACHE) 94 | if(CMKR_STARTSWITH_BUILD) 95 | if(DEFINED ENV{CMKR_CACHE}) 96 | message(AUTHOR_WARNING "[cmkr] Switching to cached cmkr: '${CMKR_CACHED_EXECUTABLE}'") 97 | if(EXISTS "${CMKR_CACHED_EXECUTABLE}") 98 | set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE) 99 | else() 100 | unset(CMKR_EXECUTABLE CACHE) 101 | endif() 102 | else() 103 | message(AUTHOR_WARNING "[cmkr] Upgrading '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'") 104 | unset(CMKR_EXECUTABLE CACHE) 105 | endif() 106 | elseif(DEFINED ENV{CMKR_CACHE} AND CMKR_STARTSWITH_CACHE) 107 | message(AUTHOR_WARNING "[cmkr] Upgrading cached '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'") 108 | unset(CMKR_EXECUTABLE CACHE) 109 | endif() 110 | endif() 111 | 112 | if(CMKR_EXECUTABLE AND EXISTS "${CMKR_EXECUTABLE}") 113 | message(VERBOSE "[cmkr] Found cmkr: '${CMKR_EXECUTABLE}'") 114 | elseif(CMKR_EXECUTABLE AND NOT CMKR_EXECUTABLE STREQUAL CMKR_CACHED_EXECUTABLE) 115 | message(FATAL_ERROR "[cmkr] '${CMKR_EXECUTABLE}' not found") 116 | elseif(NOT CMKR_EXECUTABLE AND EXISTS "${CMKR_CACHED_EXECUTABLE}") 117 | set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE) 118 | message(STATUS "[cmkr] Found cached cmkr: '${CMKR_EXECUTABLE}'") 119 | else() 120 | set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE) 121 | message(VERBOSE "[cmkr] Bootstrapping '${CMKR_EXECUTABLE}'") 122 | 123 | message(STATUS "[cmkr] Fetching cmkr...") 124 | if(EXISTS "${CMKR_DIRECTORY}") 125 | cmkr_exec("${CMAKE_COMMAND}" -E rm -rf "${CMKR_DIRECTORY}") 126 | endif() 127 | find_package(Git QUIET REQUIRED) 128 | cmkr_exec("${GIT_EXECUTABLE}" 129 | clone 130 | --config advice.detachedHead=false 131 | --branch ${CMKR_TAG} 132 | --depth 1 133 | ${CMKR_REPO} 134 | "${CMKR_DIRECTORY}" 135 | ) 136 | if(CMKR_COMMIT_HASH) 137 | execute_process( 138 | COMMAND "${GIT_EXECUTABLE}" checkout -q "${CMKR_COMMIT_HASH}" 139 | RESULT_VARIABLE CMKR_EXEC_RESULT 140 | WORKING_DIRECTORY "${CMKR_DIRECTORY}" 141 | ) 142 | if(NOT CMKR_EXEC_RESULT EQUAL 0) 143 | message(FATAL_ERROR "Tag '${CMKR_TAG}' hash is not '${CMKR_COMMIT_HASH}'") 144 | endif() 145 | endif() 146 | message(STATUS "[cmkr] Building cmkr (using system compiler)...") 147 | cmkr_exec("${CMAKE_COMMAND}" 148 | --no-warn-unused-cli 149 | "${CMKR_DIRECTORY}" 150 | "-B${CMKR_DIRECTORY}/build" 151 | "-DCMAKE_BUILD_TYPE=${CMKR_BUILD_TYPE}" 152 | "-DCMAKE_UNITY_BUILD=ON" 153 | "-DCMAKE_INSTALL_PREFIX=${CMKR_DIRECTORY}" 154 | "-DCMKR_GENERATE_DOCUMENTATION=OFF" 155 | ) 156 | cmkr_exec("${CMAKE_COMMAND}" 157 | --build "${CMKR_DIRECTORY}/build" 158 | --config "${CMKR_BUILD_TYPE}" 159 | --parallel 160 | ) 161 | cmkr_exec("${CMAKE_COMMAND}" 162 | --install "${CMKR_DIRECTORY}/build" 163 | --config "${CMKR_BUILD_TYPE}" 164 | --prefix "${CMKR_DIRECTORY}" 165 | --component cmkr 166 | ) 167 | if(NOT EXISTS ${CMKR_EXECUTABLE}) 168 | message(FATAL_ERROR "[cmkr] Failed to bootstrap '${CMKR_EXECUTABLE}'") 169 | endif() 170 | cmkr_exec("${CMKR_EXECUTABLE}" version) 171 | message(STATUS "[cmkr] Bootstrapped ${CMKR_EXECUTABLE}") 172 | endif() 173 | execute_process(COMMAND "${CMKR_EXECUTABLE}" version 174 | RESULT_VARIABLE CMKR_EXEC_RESULT 175 | ) 176 | if(NOT CMKR_EXEC_RESULT EQUAL 0) 177 | message(FATAL_ERROR "[cmkr] Failed to get version, try clearing the cache and rebuilding") 178 | endif() 179 | 180 | # Use cmkr.cmake as a script 181 | if(CMAKE_SCRIPT_MODE_FILE) 182 | if(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake.toml") 183 | execute_process(COMMAND "${CMKR_EXECUTABLE}" init 184 | RESULT_VARIABLE CMKR_EXEC_RESULT 185 | ) 186 | if(NOT CMKR_EXEC_RESULT EQUAL 0) 187 | message(FATAL_ERROR "[cmkr] Failed to bootstrap cmkr project. Please report an issue: https://github.com/build-cpp/cmkr/issues/new") 188 | else() 189 | message(STATUS "[cmkr] Modify cmake.toml and then configure using: cmake -B build") 190 | endif() 191 | else() 192 | execute_process(COMMAND "${CMKR_EXECUTABLE}" gen 193 | RESULT_VARIABLE CMKR_EXEC_RESULT 194 | ) 195 | if(NOT CMKR_EXEC_RESULT EQUAL 0) 196 | message(FATAL_ERROR "[cmkr] Failed to generate project.") 197 | else() 198 | message(STATUS "[cmkr] Configure using: cmake -B build") 199 | endif() 200 | endif() 201 | endif() 202 | 203 | # This is the macro that contains black magic 204 | macro(cmkr) 205 | # When this macro is called from the generated file, fake some internal CMake variables 206 | get_source_file_property(CMKR_CURRENT_LIST_FILE "${CMAKE_CURRENT_LIST_FILE}" CMKR_CURRENT_LIST_FILE) 207 | if(CMKR_CURRENT_LIST_FILE) 208 | set(CMAKE_CURRENT_LIST_FILE "${CMKR_CURRENT_LIST_FILE}") 209 | get_filename_component(CMAKE_CURRENT_LIST_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY) 210 | endif() 211 | 212 | # File-based include guard (include_guard is not documented to work) 213 | get_source_file_property(CMKR_INCLUDE_GUARD "${CMAKE_CURRENT_LIST_FILE}" CMKR_INCLUDE_GUARD) 214 | if(NOT CMKR_INCLUDE_GUARD) 215 | set_source_files_properties("${CMAKE_CURRENT_LIST_FILE}" PROPERTIES CMKR_INCLUDE_GUARD TRUE) 216 | 217 | file(SHA256 "${CMAKE_CURRENT_LIST_FILE}" CMKR_LIST_FILE_SHA256_PRE) 218 | 219 | # Generate CMakeLists.txt 220 | cmkr_exec("${CMKR_EXECUTABLE}" gen 221 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 222 | ) 223 | 224 | file(SHA256 "${CMAKE_CURRENT_LIST_FILE}" CMKR_LIST_FILE_SHA256_POST) 225 | 226 | # Delete the temporary file if it was left for some reason 227 | set(CMKR_TEMP_FILE "${CMAKE_CURRENT_SOURCE_DIR}/CMakerLists.txt") 228 | if(EXISTS "${CMKR_TEMP_FILE}") 229 | file(REMOVE "${CMKR_TEMP_FILE}") 230 | endif() 231 | 232 | if(NOT CMKR_LIST_FILE_SHA256_PRE STREQUAL CMKR_LIST_FILE_SHA256_POST) 233 | # Copy the now-generated CMakeLists.txt to CMakerLists.txt 234 | # This is done because you cannot include() a file you are currently in 235 | configure_file(CMakeLists.txt "${CMKR_TEMP_FILE}" COPYONLY) 236 | 237 | # Add the macro required for the hack at the start of the cmkr macro 238 | set_source_files_properties("${CMKR_TEMP_FILE}" PROPERTIES 239 | CMKR_CURRENT_LIST_FILE "${CMAKE_CURRENT_LIST_FILE}" 240 | ) 241 | 242 | # 'Execute' the newly-generated CMakeLists.txt 243 | include("${CMKR_TEMP_FILE}") 244 | 245 | # Delete the generated file 246 | file(REMOVE "${CMKR_TEMP_FILE}") 247 | 248 | # Do not execute the rest of the original CMakeLists.txt 249 | return() 250 | endif() 251 | # Resume executing the unmodified CMakeLists.txt 252 | endif() 253 | endmacro() 254 | -------------------------------------------------------------------------------- /example_api/example_api.py: -------------------------------------------------------------------------------- 1 | import pyTTD 2 | 3 | # Create a ReplayEngine 4 | eng = pyTTD.ReplayEngine() 5 | # Open the trace 6 | eng.initialize("D:\\traces\\trace.run") 7 | 8 | # Retrieve positions 9 | first = eng.get_first_position() 10 | last = eng.get_last_position() 11 | print(f"Trace from {first} to {last}") 12 | 13 | # Positions can be compared 14 | assert all([first < last, last > first, first == eng.get_first_position()]) 15 | 16 | # Print a few information on the trace 17 | peb = eng.get_peb_address() 18 | print(f"Peb is at {peb:x}") 19 | print(f"Thread count {eng.get_thread_count()}") 20 | 21 | # Create a cursor, and set it at the beginning of the trace 22 | cursor = eng.new_cursor() 23 | cursor.set_position(first) 24 | 25 | # Print a few information 26 | print(f"PC: {cursor.get_program_counter():x}") 27 | thrdinfo = cursor.get_thread_info() 28 | print(f"Thread ID: {thrdinfo.threadid}") 29 | ctxt = cursor.get_context_x86_64() 30 | print("RCX: %x" % ctxt.rcx) 31 | 32 | # Print loaded modules 33 | print(f"Modules ({eng.get_module_count()}):") 34 | for mod in sorted(eng.get_module_list(), key=lambda x: x.base_addr): 35 | print(f"\t0x{mod.base_addr:x} - 0x{mod.base_addr + mod.image_size:x}\t{mod.path}") 36 | 37 | # Print an event based timeline 38 | events = sorted( 39 | list((x, "modload") for x in eng.get_module_loaded_event_list()) 40 | + list((x, "modunload") for x in eng.get_module_unloaded_event_list()) 41 | + list((x, "threadcreated") for x in eng.get_thread_created_event_list()) 42 | + list((x, "threadterm") for x in eng.get_thread_terminated_event_list()), 43 | key=lambda event:event[0].position 44 | ) 45 | for event, evtype in events: 46 | print(f"[{event.position.major:x}:{event.position.minor:x}]", end=" ") 47 | if evtype == "modload": 48 | print(f"Module {event.info.path} loaded") 49 | elif evtype == "modunload": 50 | print(f"Module {event.info.path} unloaded") 51 | elif evtype == "threadcreated": 52 | print(f"Thread {event.info.threadid:x} created") 53 | elif evtype == "threadterm": 54 | print(f"Thread {event.info.threadid:x} terminated") 55 | 56 | # Print threads' future and past active zone 57 | print(f"Threads ({cursor.get_thread_count()}):") 58 | for thread in cursor.get_thread_list(): 59 | print(f"\t[0x{thread.threadid:x}] last active: {thread.last_major:x}:{thread.last_minor:x}, next: {thread.next_major:x}:{thread.next_minor:x}") 60 | 61 | # Read the memory 62 | print("@128[RCX]: %s" % cursor.read_mem(ctxt.rcx, 16)) 63 | print("@128[RCX+4]: %s" % cursor.read_mem(ctxt.rcx + 4, 16)) 64 | 65 | # Step forward and backward 66 | print("Step forward: %d" % cursor.replay_forward(2, last)) 67 | print(f"PC: {cursor.get_program_counter():x}") 68 | print("Step backward: %d" % cursor.replay_backward(2, first)) 69 | print(f"PC: {cursor.get_program_counter():x}") 70 | 71 | # Set breakpoints 72 | addr = 0x1c26ac21e40 73 | print(f"Breakpoint r4 at 0x{addr:x}") 74 | bp = pyTTD.MemoryWatchpointData(addr=addr, size=4, flags=pyTTD.BP_FLAGS.READ) 75 | cursor.add_memory_watchpoint(bp) 76 | cursor.replay_forward(pyTTD.MAX_STEP, last) 77 | cursor.remove_memory_watchpoint(bp) 78 | print(f"PC: {cursor.get_program_counter():x}") 79 | 80 | addr = 0x7fffcf045418 81 | print(f"Breakpoint X at 0x{addr:x}") 82 | bp = pyTTD.MemoryWatchpointData(addr=addr, size=1, flags=pyTTD.BP_FLAGS.EXEC) 83 | cursor.add_memory_watchpoint(bp) 84 | cursor.replay_forward(pyTTD.MAX_STEP, last) 85 | cursor.remove_memory_watchpoint(bp) 86 | print(f"PC: {cursor.get_program_counter():x}") 87 | -------------------------------------------------------------------------------- /example_api/example_api.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {1b66e0db-a111-476b-ab14-eae15b7f9fd7} 25 | exampleapi 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Console 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | 120 | 121 | Console 122 | true 123 | 124 | 125 | 126 | 127 | Level3 128 | true 129 | true 130 | true 131 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 132 | true 133 | MultiThreaded 134 | 135 | 136 | Console 137 | true 138 | true 139 | true 140 | 141 | 142 | 143 | 144 | {095478e2-f772-42e7-a921-98b92cefd363} 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /example_api/example_api.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /example_api/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "TTD/utils.h" 5 | #include "TTD/TTD.hpp" 6 | 7 | /* 8 | @callback_value: value passed at callback registering 9 | 2 cases: 10 | @addr_func: called function address 11 | @addr_ret: return address after the call 12 | OR 13 | @addr_ret is 0 14 | @addr_func is the current returned address 15 | */ 16 | void callCallback(unsigned __int64 callback_value, TTD::GuestAddress addr_func, TTD::GuestAddress addr_ret, struct TTD::TTD_Replay_IThreadView* thread_info) { 17 | printf("[CALL CALLBACK] "); 18 | printf("arg1: %llx, arg2: %llx, arg3: %llx, arg4: %p\n", callback_value, addr_func, addr_ret, thread_info); 19 | TTD::Position* current = thread_info->IThreadView->GetPosition(thread_info); 20 | printf("Program counter: %llx | Position: %llx:%llx\n", thread_info->IThreadView->GetProgramCounter(thread_info), current->Major, current->Minor); 21 | if (addr_ret == 0) { 22 | printf("Returned value: %llx\n", thread_info->IThreadView->GetBasicReturnValue(thread_info)); 23 | } 24 | return; 25 | } 26 | 27 | /* 28 | * @callback_value: value passed at callback registering 29 | * Returns TRUE to stop execution on break 30 | */ 31 | bool __stdcall memCallback(unsigned __int64 callback_value, const TTD::TTD_Replay_MemoryWatchpointResult* mem, struct TTD::TTD_Replay_IThreadView* thread_info) { 32 | printf("[MEM CALLBACK] "); 33 | printf("callback_value: %llx, guest_addr: %llx, size: %llx, flags: %llx\n", callback_value, mem->addr, mem->size, mem->flags); 34 | TTD::Position* current = thread_info->IThreadView->GetPosition(thread_info); 35 | printf("Program counter: %llx | Position: %llx:%llx\n", thread_info->IThreadView->GetProgramCounter(thread_info), current->Major, current->Minor); 36 | return TRUE; 37 | } 38 | 39 | int main() 40 | { 41 | TTD::ReplayEngine ttdengine = TTD::ReplayEngine(); 42 | int result; 43 | 44 | std::cout << "Openning the trace\n"; 45 | result = ttdengine.Initialize(L"D:\\traces\\lsm_rpc.run"); 46 | if (result == 0) { 47 | std::cout << "Fail to open the trace"; 48 | exit(-1); 49 | } 50 | 51 | std::cout << "System Info:\n"; 52 | TTD::TTD_SystemInfo* info = ttdengine.GetSystemInfo(); 53 | DumpHex(info, 0x100); // size is not correct 54 | 55 | std::cout << "Thread count:\n"; 56 | std::cout << ttdengine.GetThreadCount() << "\n"; 57 | 58 | std::cout << "First Position:\n"; 59 | TTD::Position* first = ttdengine.GetFirstPosition(); 60 | printf("%llx:%llx\n", first->Major, first->Minor); 61 | 62 | std::cout << "Last Position:\n"; 63 | TTD::Position* last = ttdengine.GetLastPosition(); 64 | printf("%llx:%llx\n", last->Major, last->Minor); 65 | 66 | std::cout << "Peb:\n"; 67 | printf("%llx\n", ttdengine.GetPebAddress()); 68 | 69 | std::cout << "New Cursor:\n"; 70 | TTD::Cursor ttdcursor = ttdengine.NewCursor(); 71 | 72 | std::cout << "\nSetPosition:\n"; 73 | ttdcursor.SetPosition(first); 74 | 75 | std::cout << "\nThread count cursor:\n"; 76 | std::cout << ttdcursor.GetThreadCount(); 77 | 78 | std::cout << "\nProgram counter\n"; 79 | printf("%llx\n", ttdcursor.GetProgramCounter()); 80 | 81 | std::cout << "\nThread ID:\n"; 82 | printf("%x\n", ttdcursor.GetThreadInfo()->threadid); 83 | DumpHex(ttdcursor.GetThreadInfo(), 0x20); 84 | 85 | std::cout << "\nSet position to F3:0\n"; 86 | ttdcursor.SetPosition(0xF3, 0); 87 | std::cout << "\nProgram counter\n"; 88 | printf("%llx\n", ttdcursor.GetProgramCounter()); 89 | 90 | std::cout << "\nThread ID:\n"; 91 | printf("%x\n", ttdcursor.GetThreadInfo()->threadid); 92 | DumpHex(ttdcursor.GetThreadInfo(), 0x20); 93 | 94 | std::cout << "\nContext:\n"; 95 | // Use ttdcursor.GetContextx86() for x86 context 96 | auto ctxt = ttdcursor.GetContextx86_64(); 97 | DumpHex(ctxt, 0xA70); 98 | printf("RCX: %llx\n", ctxt->Rcx); 99 | std::cout << "Query memory @rcx:\n"; 100 | struct TTD::MemoryBuffer* memorybuffer = ttdcursor.QueryMemoryBuffer(ctxt->Rcx, 0x30); 101 | if (memorybuffer->data == NULL) { 102 | printf("Query Memory fail: memory do not exists in trace"); 103 | } 104 | printf("%llx: ", memorybuffer->addr); 105 | DumpHex(memorybuffer->data, memorybuffer->size); 106 | free(memorybuffer->data); 107 | free(memorybuffer); 108 | 109 | TTD::TTD_Replay_ICursorView_ReplayResult replayrez; 110 | for (int i = 0; i < 3; i++) { 111 | std::cout << "Step forward\n"; 112 | ttdcursor.ReplayForward(&replayrez, last, 1); 113 | DumpHex(&replayrez, sizeof(TTD::TTD_Replay_ICursorView_ReplayResult)); 114 | std::cout << "\nProgram counter\n"; 115 | printf("%llx\n", ttdcursor.GetProgramCounter()); 116 | } 117 | std::cout << "Step forward step=2\n"; 118 | ttdcursor.ReplayForward(&replayrez, last, 2); 119 | DumpHex(&replayrez, sizeof(TTD::TTD_Replay_ICursorView_ReplayResult)); 120 | std::cout << "\nProgram counter\n"; 121 | printf("%llx\n", ttdcursor.GetProgramCounter()); 122 | 123 | std::cout << "Step forward MAX\n"; 124 | ttdcursor.ReplayForward(&replayrez, last, -1); 125 | DumpHex(&replayrez, sizeof(TTD::TTD_Replay_ICursorView_ReplayResult)); 126 | std::cout << "\nProgram counter\n"; 127 | printf("%llx\n", ttdcursor.GetProgramCounter()); 128 | 129 | std::cout << "Step backward 1\n"; 130 | ttdcursor.ReplayBackward(&replayrez, first, 1); 131 | DumpHex(&replayrez, sizeof(TTD::TTD_Replay_ICursorView_ReplayResult)); 132 | std::cout << "\nProgram counter\n"; 133 | printf("%llx\n", ttdcursor.GetProgramCounter()); 134 | 135 | std::cout << "\nSet position to first instr\n"; 136 | ttdcursor.SetPosition(first); 137 | 138 | std::cout << "Add call callback"; 139 | ttdcursor.SetCallReturnCallback((TTD::PROC_CallCallback) callCallback, 0); 140 | 141 | std::cout << "Step 100 instr\n"; 142 | ttdcursor.ReplayForward(&replayrez, last, 100); 143 | DumpHex(&replayrez, sizeof(TTD::TTD_Replay_ICursorView_ReplayResult)); 144 | std::cout << "\nProgram counter\n"; 145 | printf("%llx\n", ttdcursor.GetProgramCounter()); 146 | 147 | std::cout << "Module:\n"; 148 | std::cout << ttdengine.GetModuleCount() << "\n"; 149 | 150 | std::cout << "ModuleList:\n"; 151 | const TTD::TTD_Replay_Module* mod_list = ttdengine.GetModuleList(); 152 | for (int i = 0; i < ttdengine.GetModuleCount(); i++) { 153 | printf("%llx\t%llx\t%ls\n", mod_list[i].base_addr, mod_list[i].imageSize, mod_list[i].path); 154 | } 155 | 156 | std::cout << "ExceptionList:" << std::endl; 157 | for (const auto& exceptionEvent : ttdengine.GetExceptionEvents()) 158 | { 159 | std::cout << "Exception raised at : 0x" << std::hex << exceptionEvent.info.ExceptionAddress << std::endl; 160 | } 161 | 162 | std::cout << "Remove callback\n"; 163 | ttdcursor.SetCallReturnCallback(0, 0); 164 | std::cout << "Breakpoint r4 at 0x1c26ac21e40 from beginning\n"; 165 | TTD::TTD_Replay_MemoryWatchpointData data; 166 | data.addr = 0x1c26ac21e40; 167 | data.size = 4; 168 | data.flags = TTD::BP_FLAGS::READ; 169 | ttdcursor.AddMemoryWatchpoint(&data); 170 | ttdcursor.SetMemoryWatchpointCallback(memCallback, 1234); 171 | ttdcursor.ReplayForward(&replayrez, last, -1); 172 | printf("%llx\n", ttdcursor.GetProgramCounter()); 173 | ttdcursor.RemoveMemoryWatchpoint(&data); 174 | 175 | std::cout << "Breakpoint X at 0x7fffcf045418 from beginning\n"; 176 | data.addr = 0x7fffcf045418; 177 | data.size = 1; 178 | data.flags = TTD::BP_FLAGS::EXEC; 179 | ttdcursor.AddMemoryWatchpoint(&data); 180 | ttdcursor.ReplayForward(&replayrez, last, -1); 181 | printf("%llx\n", ttdcursor.GetProgramCounter()); 182 | 183 | // Exemple of timeline 184 | std::cout << "Timeline" << std::endl; 185 | auto moduleLoaded = ttdengine.GetModuleLoadedEvents(); 186 | auto moduleUnloaded = ttdengine.GetModuleUnloadedEvents(); 187 | auto threadCreate = ttdengine.GetThreadCreatedEvents(); 188 | auto threadTerminate = ttdengine.GetThreadTerminatedEvents(); 189 | 190 | auto itModuleLoaded = moduleLoaded.begin(); 191 | auto itModuleUnloaded = moduleUnloaded.begin(); 192 | auto itThreadCreate = threadCreate.begin(); 193 | auto itThreadTerminate = threadTerminate.begin(); 194 | 195 | while (itModuleLoaded != moduleLoaded.end() || itModuleUnloaded != moduleUnloaded.end() || itThreadCreate != threadCreate.end() || itThreadTerminate != threadTerminate.end()) 196 | { 197 | TTD::Position moduleLoadedPosition = (itModuleLoaded == moduleLoaded.end()) ? TTD::POSITION_MAX : itModuleLoaded->pos; 198 | TTD::Position moduleUnloadedPosition = (itModuleUnloaded == moduleUnloaded.end()) ? TTD::POSITION_MAX : itModuleUnloaded->pos; 199 | TTD::Position threadCreatePosition = (itThreadCreate == threadCreate.end()) ? TTD::POSITION_MAX : itThreadCreate->pos; 200 | TTD::Position threadTerminatePosition = (itThreadTerminate == threadTerminate.end()) ? TTD::POSITION_MAX : itThreadTerminate->pos; 201 | 202 | if (moduleLoadedPosition < moduleUnloadedPosition && moduleLoadedPosition < threadCreatePosition && moduleLoadedPosition < threadTerminatePosition) 203 | { 204 | std::wcout << "Module Loaded at " << std::hex << itModuleLoaded->pos.Major << ":" << std::hex << itModuleLoaded->pos.Minor << " : " << std::wstring(itModuleLoaded->info->path) << std::endl; 205 | itModuleLoaded++; 206 | } 207 | 208 | if (moduleUnloadedPosition < moduleLoadedPosition && moduleUnloadedPosition < threadCreatePosition && moduleUnloadedPosition < threadTerminatePosition) 209 | { 210 | std::wcout << "Module Unloaded at " << std::hex << itModuleUnloaded->pos.Major << ":" << std::hex << itModuleUnloaded->pos.Minor << " : " << std::wstring(itModuleUnloaded->info->path) << std::endl; 211 | itModuleUnloaded++; 212 | } 213 | 214 | if (threadCreatePosition < moduleLoadedPosition && threadCreatePosition < moduleLoadedPosition && threadCreatePosition < threadTerminatePosition) 215 | { 216 | std::wcout << "Thread Created at " << std::hex << itThreadCreate->pos.Major << ":" << std::hex << itThreadCreate->pos.Minor << " id : " << std::hex << itThreadCreate->info->threadid << std::endl; 217 | itThreadCreate++; 218 | } 219 | 220 | if (threadTerminatePosition < moduleLoadedPosition && threadTerminatePosition < moduleLoadedPosition && threadTerminatePosition < threadCreatePosition) 221 | { 222 | std::wcout << "Thread Terminated at " << std::hex << itThreadTerminate->pos.Major << ":" << std::hex << itThreadTerminate->pos.Minor << " id : " << std::hex << itThreadTerminate->info->threadid << std::endl; 223 | itThreadTerminate++; 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /example_calltree/example_calltree.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {ee294520-bae7-4bce-9f06-6acb10ace0c2} 25 | examplecalltree 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Console 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | 120 | 121 | Console 122 | true 123 | 124 | 125 | 126 | 127 | Level3 128 | true 129 | true 130 | true 131 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 132 | true 133 | MultiThreaded 134 | 135 | 136 | Console 137 | true 138 | true 139 | true 140 | 141 | 142 | 143 | 144 | {095478e2-f772-42e7-a921-98b92cefd363} 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /example_calltree/example_calltree.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /example_calltree/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "TTD/utils.h" 7 | #include "TTD/TTD.hpp" 8 | 9 | 10 | /**** PDB ****/ 11 | #pragma comment(lib, "dbghelp.lib") 12 | 13 | // Inspired from https://github.com/silverf0x/RpcView RpcView/Pdb.c 14 | 15 | #define RSDS_SIGNATURE 'SDSR' 16 | #define PDB_MAX_SYMBOL_SIZE 1000 17 | #define STEP_COUNT 100000 18 | 19 | //Only for PDB7.0 format! 20 | typedef struct _CV_INFO_PDB70 { 21 | DWORD CvSignature; 22 | GUID Signature; 23 | DWORD Age; 24 | BYTE PdbFileName[MAX_PATH]; 25 | } CV_INFO_PDB70; 26 | 27 | BOOL readMemory(void* dest, TTD::GuestAddress addr, unsigned __int64 size, TTD::Cursor* cursor) { 28 | BOOL result = TRUE; 29 | struct TTD::MemoryBuffer* memorybuffer = cursor->QueryMemoryBuffer(addr, size); 30 | if (memorybuffer->data == NULL || memorybuffer->size != size) { 31 | result = FALSE; 32 | } 33 | else { 34 | memcpy(dest, memorybuffer->data, size); 35 | free(memorybuffer->data); 36 | } 37 | free(memorybuffer); 38 | return TRUE; 39 | } 40 | 41 | BOOL WINAPI GetModulePdbInfo(TTD::Cursor* cursor, TTD::GuestAddress pModuleBase, CV_INFO_PDB70* pPdb70Info) 42 | { 43 | IMAGE_DOS_HEADER ImageDosHeader; 44 | IMAGE_NT_HEADERS ImageNtHeaders; 45 | IMAGE_DEBUG_DIRECTORY ImageDebugDirectory; 46 | BOOL bResult = FALSE; 47 | 48 | // Assume 64 bits 49 | 50 | if (!readMemory(&ImageDosHeader, pModuleBase, sizeof(ImageDosHeader), cursor)) goto End; 51 | if (!readMemory(&ImageNtHeaders, pModuleBase + ImageDosHeader.e_lfanew, sizeof(ImageNtHeaders), cursor)) goto End; 52 | if (!readMemory(&ImageDebugDirectory, pModuleBase + ImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress, sizeof(ImageDebugDirectory), cursor)) goto End; 53 | if (!readMemory(pPdb70Info, pModuleBase + ImageDebugDirectory.AddressOfRawData, sizeof(*pPdb70Info), cursor)) goto End; 54 | if (pPdb70Info->CvSignature != RSDS_SIGNATURE) 55 | { 56 | printf("Invalid CvSignature"); 57 | goto End; 58 | } 59 | bResult = TRUE; 60 | End: 61 | return (bResult); 62 | } 63 | 64 | BOOL WINAPI GetPdbFilePath(TTD::Cursor* cursor, TTD::GuestAddress pModuleBase, char* PdbPath, size_t PdbPathSize) 65 | { 66 | CV_INFO_PDB70 Pdb70Info; 67 | CHAR SymbolPath[MAX_PATH] = { 0 }; 68 | CHAR NtSymbolPath[MAX_PATH] = { 0 }; 69 | BOOL bResult = FALSE; 70 | 71 | if (!GetModulePdbInfo(cursor, pModuleBase, &Pdb70Info)) goto End; 72 | if (strchr((char*)Pdb70Info.PdbFileName, '\\') != NULL) // Local Path 73 | { 74 | StringCbPrintfA((STRSAFE_LPSTR)PdbPath, PdbPathSize, "%hs", Pdb70Info.PdbFileName); 75 | } 76 | else 77 | { 78 | int iResult; 79 | char* pStar = NULL; 80 | if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", NtSymbolPath, sizeof(NtSymbolPath)) == 0) goto End; 81 | 82 | iResult = sscanf_s(NtSymbolPath, "srv*%259s", SymbolPath, (unsigned int)sizeof(SymbolPath)); 83 | if (iResult == 0) { 84 | iResult = sscanf_s(NtSymbolPath, "SRV*%259s", SymbolPath, (unsigned int)sizeof(SymbolPath)); 85 | if (iResult == 0) goto End; 86 | } 87 | pStar = strchr(SymbolPath + 4, '*'); 88 | if (pStar != NULL) *pStar = 0; 89 | 90 | 91 | StringCbPrintfA((STRSAFE_LPSTR)PdbPath, PdbPathSize, "%s\\%s\\%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%X\\%s", 92 | SymbolPath, 93 | Pdb70Info.PdbFileName, 94 | Pdb70Info.Signature.Data1, 95 | Pdb70Info.Signature.Data2, 96 | Pdb70Info.Signature.Data3, 97 | Pdb70Info.Signature.Data4[0], 98 | Pdb70Info.Signature.Data4[1], 99 | Pdb70Info.Signature.Data4[2], 100 | Pdb70Info.Signature.Data4[3], 101 | Pdb70Info.Signature.Data4[4], 102 | Pdb70Info.Signature.Data4[5], 103 | Pdb70Info.Signature.Data4[6], 104 | Pdb70Info.Signature.Data4[7], 105 | Pdb70Info.Age, 106 | Pdb70Info.PdbFileName 107 | ); 108 | } 109 | bResult = TRUE; 110 | End: 111 | return (bResult); 112 | } 113 | /**** END PDB ****/ 114 | 115 | // Indent level. Can be negative if we start inside a function 116 | __int64 g_cur_stack = 0; 117 | // Current process, used for Symbol loading 118 | HANDLE g_hProcess; 119 | 120 | // Loaded module information 121 | struct ModuleInfo { 122 | TTD::GuestAddress start_addr; 123 | TTD::GuestAddress end_addr; 124 | const wchar_t* name; 125 | struct ModuleInfo* next; 126 | }; 127 | struct ModuleInfo* g_moduleinfo = NULL; 128 | 129 | void printResolvedSymbol(HANDLE hProcess, struct ModuleInfo* p_module, TTD::GuestAddress addr_func, BOOL isRet) { 130 | // Try to resolve symbol 131 | // https://docs.microsoft.com/en-us/windows/win32/debug/retrieving-symbol-information-by-address 132 | 133 | char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; 134 | PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; 135 | DWORD64 dwDisplacement = 0; 136 | pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); 137 | pSymbol->MaxNameLen = MAX_SYM_NAME; 138 | TTD::GuestAddress addr_rva = addr_func - p_module->start_addr; 139 | 140 | const char* pszArrow; 141 | if (isRet) { 142 | pszArrow = "<-"; 143 | } 144 | else { 145 | pszArrow = "->"; 146 | } 147 | 148 | if (SymFromAddr(g_hProcess, addr_func, &dwDisplacement, pSymbol)) 149 | { 150 | if (dwDisplacement != 0) { 151 | // Symbol resolved with offset 152 | printf("%s %ls!%s+%llx", pszArrow, p_module->name, pSymbol->Name, dwDisplacement); 153 | } 154 | else { 155 | printf("%s %ls!%s", pszArrow, p_module->name, pSymbol->Name); 156 | } 157 | } 158 | else 159 | { 160 | // Unresolved symbol 161 | printf("%s %ls!+%llx", pszArrow, p_module->name, addr_rva); 162 | } 163 | } 164 | 165 | 166 | void callCallback_tree(unsigned __int64 callback_value, TTD::GuestAddress addr_func, TTD::GuestAddress addr_ret, struct TTD::TTD_Replay_IThreadView* thread_view) { 167 | // Indentation level printing 168 | if (addr_ret == 0) { 169 | g_cur_stack -= 1; 170 | } 171 | if (g_cur_stack < 0) { 172 | printf("\n**** PRE-START PARENT FUNCTION****\n"); 173 | g_cur_stack = 0; 174 | } 175 | for (int i = 0; i < g_cur_stack; i++) { 176 | printf("| "); 177 | } 178 | if (addr_ret != 0) { 179 | g_cur_stack += 1; 180 | } 181 | 182 | // Find the current module 183 | struct ModuleInfo* ptr = g_moduleinfo; 184 | while (ptr != NULL) { 185 | if (addr_func >= ptr->start_addr && addr_func < ptr->end_addr) 186 | break; 187 | ptr = ptr->next; 188 | } 189 | if (ptr == NULL) { 190 | fprintf(stderr, "ERROR: %llx unknown module\n", addr_func); 191 | return; 192 | } 193 | 194 | TTD::Position* position = thread_view->IThreadView->GetPosition(thread_view); 195 | if (addr_ret == 0) { 196 | // Call's end, addr_func is the Call's next instruction 197 | printResolvedSymbol(g_hProcess, ptr, addr_func, TRUE); 198 | // Additionnal info 199 | printf(" (%llx) [%llx:%llx] RETURN %llx\n", addr_func, position->Major, position->Minor, thread_view->IThreadView->GetBasicReturnValue(thread_view)); 200 | } 201 | else { 202 | // Actual Call 203 | printResolvedSymbol(g_hProcess, ptr, addr_func, FALSE); 204 | // Additionnal info 205 | printf(" (%llx) [%llx:%llx]\n", addr_func, position->Major, position->Minor); 206 | } 207 | return; 208 | } 209 | 210 | /**** ARGUMENT PARSING ****/ 211 | 212 | // From https://stackoverflow.com/a/868894 213 | char* getCmdOption(char** begin, char** end, const std::string& option) 214 | { 215 | char** itr = std::find(begin, end, option); 216 | if (itr != end && ++itr != end) 217 | { 218 | return *itr; 219 | } 220 | return 0; 221 | } 222 | 223 | bool cmdOptionExists(char** begin, char** end, const std::string& option) 224 | { 225 | return std::find(begin, end, option) != end; 226 | } 227 | /**** END ARGUMENT PARSING ****/ 228 | 229 | int main(int argc, char* argv[]) { 230 | TTD::ReplayEngine ttdengine = TTD::ReplayEngine(); 231 | TTD::TTD_Replay_ICursorView_ReplayResult replayrez; 232 | int result; 233 | char pdb_path[PDB_MAX_SYMBOL_SIZE] = { 0 }; 234 | 235 | if (argc <= 1) { 236 | printf("Usage:\n"); 237 | printf("%s [options] trace\n", argv[0]); 238 | printf("Options:\n"); 239 | printf("-b\tStarting position, as 1234:56. If not set, use the first position\n"); 240 | printf("-e\tEnding position, as 1234:56. If not set, use the last position\n"); 241 | printf("-t\tThread ID to trace, as 1df4. Use '-t list' to list them. If not set, use the one from position\n"); 242 | return 0; 243 | } 244 | 245 | g_hProcess = GetCurrentProcess(); 246 | if (!SymInitialize(g_hProcess, nullptr, false)) 247 | { 248 | std::wcerr << "SymInitialize() failed\n"; 249 | return -1; 250 | } 251 | 252 | std::cout << "Openning the trace\n"; 253 | wchar_t trace_path[MAX_PATH] = { 0 }; 254 | StringCbPrintfW(trace_path, MAX_PATH, L"%S", argv[argc - 1]); 255 | result = ttdengine.Initialize(trace_path); 256 | 257 | if (result == 0) { 258 | std::wcerr << "Fail to open the trace"; 259 | exit(-1); 260 | } 261 | 262 | char* tid_arg = getCmdOption(argv, argv + argc, "-t"); 263 | TTD::Position tid_pos; 264 | unsigned __int64 tid; 265 | if (tid_arg) { 266 | auto threadCreate = ttdengine.GetThreadCreatedEvents(); 267 | auto itThreadCreate = threadCreate.begin(); 268 | if (!strncmp(tid_arg, "list", 4)) { 269 | // Print the list of Thread ID and exit 270 | while (itThreadCreate != threadCreate.end()) { 271 | std::wcout << "Thread ID " << std::hex << itThreadCreate->info->threadid << " starting at " << std::hex << itThreadCreate->pos.Major << ":" << std::hex << itThreadCreate->pos.Minor << std::endl; 272 | itThreadCreate++; 273 | } 274 | exit(0); 275 | } 276 | 277 | bool found = FALSE; 278 | sscanf_s(tid_arg, "%llx", &tid); 279 | // Get the first position 280 | while (itThreadCreate != threadCreate.end()) { 281 | if (itThreadCreate->info->threadid == tid) { 282 | found = TRUE; 283 | tid_pos.Major = itThreadCreate->pos.Major; 284 | tid_pos.Minor = itThreadCreate->pos.Minor; 285 | break; 286 | } 287 | itThreadCreate++; 288 | } 289 | if (!found) { 290 | std::wcerr << "Thread ID " << std::hex << tid << " not found. Use -t list to list them." << std::endl; 291 | exit(-1); 292 | } 293 | } 294 | 295 | // Cursor needed before symbol resolution, to fetch PDB information from trace memory 296 | TTD::Cursor ttdcursor = ttdengine.NewCursor(); 297 | TTD::Position* first = ttdengine.GetFirstPosition(); 298 | TTD::Position* last = ttdengine.GetLastPosition(); 299 | char* begin_arg = getCmdOption(argv, argv + argc, "-b"); 300 | if (begin_arg) { 301 | if (tid_arg) { 302 | std::wcerr << "-t and -b cannot be used together" << std::endl; 303 | exit(-1); 304 | } 305 | unsigned __int64 Major, Minor; 306 | sscanf_s(begin_arg, "%llx:%llx", &Major, &Minor); 307 | ttdcursor.SetPosition(Major, Minor); 308 | } 309 | else if (tid_arg) { 310 | first = &tid_pos; 311 | ttdcursor.SetPosition(first); 312 | } 313 | else { 314 | ttdcursor.SetPosition(first); 315 | } 316 | 317 | TTD::Position end; 318 | char* end_arg = getCmdOption(argv, argv + argc, "-e"); 319 | if (end_arg) { 320 | sscanf_s(end_arg, "%llx:%llx", &end.Major, &end.Minor); 321 | } 322 | else { 323 | end.Major = last->Major; 324 | end.Minor = last->Minor; 325 | } 326 | 327 | std::cout << "ModuleList:\n"; 328 | const TTD::TTD_Replay_Module* mod_list = ttdengine.GetModuleList(); 329 | for (int i = 0; i < ttdengine.GetModuleCount(); i++) { 330 | printf("%llx\t%llx\t%ls\n", mod_list[i].base_addr, mod_list[i].imageSize, mod_list[i].path); 331 | 332 | // Extract filename 333 | std::wstring fname = mod_list[i].path; 334 | fname = fname.substr(fname.find_last_of(L"\\")); 335 | fname = fname.substr(1, fname.find_last_of(L".") - 1); 336 | 337 | // Add to g_moduleinfo list 338 | struct ModuleInfo* modin = (struct ModuleInfo*)malloc(sizeof(struct ModuleInfo)); 339 | modin->next = g_moduleinfo; 340 | modin->name = _wcsdup(fname.c_str()); 341 | modin->start_addr = mod_list[i].base_addr; 342 | modin->end_addr = mod_list[i].base_addr + mod_list[i].imageSize; 343 | g_moduleinfo = modin; 344 | } 345 | 346 | std::cout << "Load symbols:\n"; 347 | for (int i = 0; i < ttdengine.GetModuleCount(); i++) { 348 | printf("%ls", mod_list[i].path); 349 | 350 | if (!GetPdbFilePath(&ttdcursor, mod_list[i].base_addr, pdb_path, PDB_MAX_SYMBOL_SIZE)) { 351 | /* 352 | * If PDB is not available, fallback on the DLL (if available) 353 | * /!\ might not be the same than the one in the trace! 354 | */ 355 | printf(" ... fallback to DLL ... "); 356 | StringCbPrintfA((STRSAFE_LPSTR)pdb_path, PDB_MAX_SYMBOL_SIZE, "%S", mod_list[i].path); 357 | } 358 | if (SymLoadModuleEx(g_hProcess, NULL, pdb_path, NULL, mod_list[i].base_addr, (DWORD)mod_list[i].imageSize, NULL, 0)) 359 | printf(" OK (%s)\n", pdb_path); 360 | else 361 | printf(" SymLoadModuleEx(%s) returned error : %d\n", pdb_path, GetLastError()); 362 | 363 | } 364 | 365 | // Set the callback 366 | ttdcursor.SetCallReturnCallback((TTD::PROC_CallCallback)callCallback_tree, 0); 367 | 368 | TTD::Position LastPosition; 369 | unsigned long long stepCount; 370 | unsigned long long totalStepCount = 0; 371 | 372 | for (;;) { 373 | ttdcursor.ReplayForward(&replayrez, &end, STEP_COUNT); 374 | stepCount = replayrez.stepCount; 375 | totalStepCount += stepCount; 376 | 377 | if (replayrez.stepCount < STEP_COUNT) { 378 | ttdcursor.SetPosition(&LastPosition); 379 | ttdcursor.ReplayForward(&replayrez, &end, stepCount - 1); 380 | totalStepCount += stepCount - 1; 381 | break; 382 | } 383 | memcpy(&LastPosition, ttdcursor.GetPosition(), sizeof(LastPosition)); 384 | } 385 | 386 | TTD::Position* lastPosition = ttdcursor.GetPosition(); 387 | printf("\nLast cursor position: %llx:%llx\n", lastPosition->Major, lastPosition->Minor); 388 | printf("\nTotal instruction executed: 0x%llx\n", totalStepCount); 389 | return 0; 390 | } 391 | -------------------------------------------------------------------------------- /example_cov/example_cov.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {0be1a525-d873-4630-8432-f9b7bc4edcea} 25 | examplecov 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Console 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | 120 | 121 | Console 122 | true 123 | 124 | 125 | 126 | 127 | Level3 128 | true 129 | true 130 | true 131 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 132 | true 133 | MultiThreaded 134 | 135 | 136 | Console 137 | true 138 | true 139 | true 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | {095478e2-f772-42e7-a921-98b92cefd363} 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /example_cov/example_cov.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /example_cov/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "TTD/TTD.hpp" 10 | 11 | std::set reached_addrs; 12 | std::mutex set_access; 13 | 14 | bool __stdcall memCallback(unsigned __int64 callback_value, const TTD::TTD_Replay_MemoryWatchpointResult* mem, struct TTD::TTD_Replay_IThreadView* thread_info) { 15 | set_access.lock(); 16 | reached_addrs.insert(mem->addr); 17 | set_access.unlock(); 18 | 19 | auto size = reached_addrs.size(); 20 | if (size % 0x1000 == 0) { 21 | TTD::Position* current = thread_info->IThreadView->GetPosition(thread_info); 22 | printf("Number of entry: 0x%llx, current position %llx:%llx\n", size, current->Major, current->Minor); 23 | } 24 | return FALSE; 25 | } 26 | 27 | /**** ARGUMENT PARSING ****/ 28 | 29 | // From https://stackoverflow.com/a/868894 30 | char* getCmdOption(char** begin, char** end, const std::string& option) 31 | { 32 | char** itr = std::find(begin, end, option); 33 | if (itr != end && ++itr != end) 34 | { 35 | return *itr; 36 | } 37 | return 0; 38 | } 39 | 40 | bool cmdOptionExists(char** begin, char** end, const std::string& option) 41 | { 42 | return std::find(begin, end, option) != end; 43 | } 44 | 45 | std::vector extractModules(char* mod_arg) { 46 | std::vector mod_names; 47 | std::wstring_convert> converter; 48 | 49 | std::string mod_arg_str(mod_arg); 50 | size_t start = 0; 51 | auto end = mod_arg_str.find(","); 52 | while (end != std::string::npos) 53 | { 54 | mod_names.push_back(converter.from_bytes(mod_arg_str.substr(start, end - start))); 55 | start = end + 1; 56 | end = mod_arg_str.find(",", start); 57 | } 58 | mod_names.push_back(converter.from_bytes(mod_arg_str.substr(start, end))); 59 | return mod_names; 60 | } 61 | 62 | std::wstring pathToName(wchar_t* path_in) { 63 | std::wstring path(path_in); 64 | auto name = path.substr(path.find_last_of(L"/\\") + 1); 65 | return name.substr(0, name.find_last_of(L".")); 66 | } 67 | 68 | /**** END ARGUMENT PARSING ****/ 69 | 70 | int main(int argc, char** argv) 71 | { 72 | if (argc <= 1) { 73 | printf("Usage:\n"); 74 | printf("%s [options] trace\n", argv[0]); 75 | printf("Options:\n"); 76 | printf("-b\tStarting position, as 1234:56. If not set, use the first position\n"); 77 | printf("-e\tEnding position, as 1234:56. If not set, use the last position\n"); 78 | printf("-m\tModules to cover, as 'ntdll,cmd'. If not set, list availables modules and exit\n"); 79 | return 0; 80 | } 81 | 82 | TTD::ReplayEngine ttdengine = TTD::ReplayEngine(); 83 | int result; 84 | 85 | std::cout << "Openning the trace\n"; 86 | wchar_t trace_path[MAX_PATH] = { 0 }; 87 | StringCbPrintfW(trace_path, MAX_PATH, L"%S", argv[argc - 1]); 88 | result = ttdengine.Initialize(trace_path); 89 | 90 | if (result == 0) { 91 | std::wcerr << "Fail to open the trace"; 92 | exit(-1); 93 | } 94 | 95 | TTD::Position* first = ttdengine.GetFirstPosition(); 96 | TTD::Position* last = ttdengine.GetLastPosition(); 97 | TTD::Cursor ttdcursor = ttdengine.NewCursor(); 98 | 99 | char* begin_arg = getCmdOption(argv, argv + argc, "-b"); 100 | if (begin_arg) { 101 | unsigned __int64 Major, Minor; 102 | sscanf_s(begin_arg, "%llx:%llx", &Major, &Minor); 103 | ttdcursor.SetPosition(Major, Minor); 104 | } 105 | else { 106 | ttdcursor.SetPosition(first); 107 | } 108 | 109 | TTD::Position end; 110 | char* end_arg = getCmdOption(argv, argv + argc, "-e"); 111 | if (end_arg) { 112 | sscanf_s(end_arg, "%llx:%llx", &end.Major, &end.Minor); 113 | } 114 | else { 115 | end.Major = last->Major; 116 | end.Minor = last->Minor; 117 | } 118 | 119 | char* mod_arg = getCmdOption(argv, argv + argc, "-m"); 120 | if (!mod_arg) { 121 | std::cout << "Module:\n"; 122 | std::cout << ttdengine.GetModuleCount() << "\n"; 123 | 124 | std::cout << "ModuleList:\n"; 125 | const TTD::TTD_Replay_Module* mod_list = ttdengine.GetModuleList(); 126 | for (int i = 0; i < ttdengine.GetModuleCount(); i++) { 127 | printf("%llx\t%llx\t%ls\n", mod_list[i].base_addr, mod_list[i].imageSize, mod_list[i].path); 128 | } 129 | exit(0); 130 | } 131 | auto asked = extractModules(mod_arg); 132 | const TTD::TTD_Replay_Module* mod_list = ttdengine.GetModuleList(); 133 | for (int i = 0; i < ttdengine.GetModuleCount(); i++) { 134 | auto name_no_dll = pathToName(mod_list[i].path); 135 | if (std::find(asked.begin(), asked.end(), name_no_dll) != asked.end()) { 136 | printf("Tracking %ls\n", name_no_dll.c_str()); 137 | 138 | // Add execution BP for [BaseAddr; BaseAddr + Size[ 139 | TTD::TTD_Replay_MemoryWatchpointData data; 140 | data.addr = mod_list[i].base_addr; 141 | data.size = mod_list[i].imageSize; 142 | data.flags = TTD::BP_FLAGS::EXEC; 143 | ttdcursor.AddMemoryWatchpoint(&data); 144 | } 145 | } 146 | 147 | // Enable memory tracking 148 | ttdcursor.SetMemoryWatchpointCallback(memCallback, 0); 149 | 150 | // Launch the replay 151 | TTD::TTD_Replay_ICursorView_ReplayResult replayrez; 152 | ttdcursor.ReplayForward(&replayrez, &end, -1); 153 | 154 | // Create the corresponding traces 155 | printf("Got 0x%llx addresses\n", reached_addrs.size()); 156 | 157 | // This could be optimized to browse only once the vector... but comparing to I/O, it's fast enough 158 | for (int i = 0; i < ttdengine.GetModuleCount(); i++) { 159 | auto name_no_dll = pathToName(mod_list[i].path); 160 | if (std::find(asked.begin(), asked.end(), name_no_dll) != asked.end()) { 161 | wchar_t file_path[MAX_PATH] = { 0 }; 162 | StringCbPrintfW(file_path, MAX_PATH, L"%s.trace.txt", name_no_dll.c_str()); 163 | printf("Dump to file %ls\n", file_path); 164 | std::wofstream fdesc; 165 | fdesc.open(file_path, std::ios::out); 166 | auto itaddr = reached_addrs.begin(); 167 | while (itaddr != reached_addrs.end()) { 168 | if (*itaddr >= mod_list[i].base_addr && *itaddr < (mod_list[i].base_addr + mod_list[i].imageSize)) { 169 | fdesc << name_no_dll << "+" << std::hex << (*itaddr - mod_list[i].base_addr) << std::endl; 170 | } 171 | itaddr++; 172 | } 173 | fdesc.close(); 174 | } 175 | } 176 | return 0; 177 | } 178 | -------------------------------------------------------------------------------- /example_diff/example_diff.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {907b330c-3738-49b4-85b5-c6c38cbb648a} 25 | examplediff 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Console 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | 120 | 121 | Console 122 | true 123 | 124 | 125 | 126 | 127 | Level3 128 | true 129 | true 130 | true 131 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 132 | true 133 | MultiThreaded 134 | 135 | 136 | Console 137 | true 138 | true 139 | true 140 | 141 | 142 | 143 | 144 | {095478e2-f772-42e7-a921-98b92cefd363} 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /example_diff/example_diff.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /example_diff/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "TTD/TTD.hpp" 7 | 8 | /* 9 | @callback_value: value passed at callback registering 10 | @addr_func: called function address 11 | @addr_ret: return address after the call 12 | */ 13 | std::vector g_func_stack; 14 | std::vector g_ret_stack; 15 | std::vector g_func_stack2; 16 | std::vector g_ret_stack2; 17 | 18 | void callCallback_diff(unsigned __int64 callback_value, TTD::GuestAddress addr_func, TTD::GuestAddress addr_ret, struct TTD::TTD_Replay_IThreadView* arg4) { 19 | TTD::GuestAddress last_ret; 20 | if (addr_ret == 0) { 21 | // End of a call (RET's next instruction) 22 | // `addr_func` is the instruction address 23 | if (callback_value == 1) { 24 | if (!g_func_stack.empty() && !g_ret_stack.empty()) { 25 | g_func_stack.pop_back(); 26 | last_ret = g_ret_stack.back(); 27 | g_ret_stack.pop_back(); 28 | assert(addr_func == last_ret); 29 | } 30 | } 31 | else if (callback_value == 2) { 32 | if (!g_func_stack2.empty() && !g_ret_stack2.empty()) { 33 | g_func_stack2.pop_back(); 34 | last_ret = g_ret_stack2.back(); 35 | g_ret_stack2.pop_back(); 36 | assert(addr_func == last_ret); 37 | } 38 | } 39 | return; 40 | } 41 | 42 | if (callback_value == 1) { 43 | g_func_stack.push_back(addr_func); 44 | g_ret_stack.push_back(addr_ret); 45 | } 46 | else if (callback_value == 2) { 47 | g_func_stack2.push_back(addr_func); 48 | g_ret_stack2.push_back(addr_ret); 49 | } 50 | } 51 | 52 | 53 | struct Snapshot { 54 | 55 | std::vector func_stack; 56 | std::vector ret_stack; 57 | std::vector func_stack2; 58 | std::vector ret_stack2; 59 | TTD::Position cur; 60 | TTD::Position cur2; 61 | }; 62 | struct Snapshot g_snapshot; 63 | 64 | void takeSnapshot(TTD::Cursor* cursor, TTD::Cursor* cursor2) { 65 | g_snapshot.func_stack = g_func_stack; // This is a copy 66 | g_snapshot.ret_stack = g_ret_stack; 67 | g_snapshot.func_stack2 = g_func_stack2; 68 | g_snapshot.ret_stack2 = g_ret_stack2; 69 | memcpy(&g_snapshot.cur, cursor->GetPosition(), sizeof(TTD::Position)); 70 | memcpy(&g_snapshot.cur2, cursor2->GetPosition(), sizeof(TTD::Position)); 71 | } 72 | 73 | void restoreSnapshot(TTD::Cursor* cursor, TTD::Cursor* cursor2) { 74 | cursor->SetPosition(&g_snapshot.cur); 75 | cursor2->SetPosition(&g_snapshot.cur2); 76 | g_func_stack = g_snapshot.func_stack; 77 | g_ret_stack = g_snapshot.ret_stack; 78 | g_func_stack2 = g_snapshot.func_stack2; 79 | g_ret_stack2 = g_snapshot.ret_stack2; 80 | } 81 | 82 | const unsigned __int64 DEFAULT_STEP_SIZE = 10000; 83 | const std::vector KNOWN_FUNC = { 84 | 0x7ffb10ec1af0, // ntdll!RtlHeapAlloc 85 | 0x7ffb10ebf320, // ntdll!RtlpAllocateHeapInternal 86 | 0x7ffb10ebf2a0, // ntdll!RtlAllocateHeap 87 | 0x7ffb10ebc320, // ntdll!RtlReAllocateHeap 88 | 0x7ffb0b560eb0, // ESENT!JetSeek 89 | 0x7ffb0b5621d0, // ESENT!JetRetrieveColumn 90 | 0x7ffb0b9d2750, // ntdsai!dbReadDataColumns 91 | 0x7ffb0b98be60, // ntdsai!SampGetMemberships 92 | }; 93 | int main() 94 | { 95 | TTD::ReplayEngine ttdengine = TTD::ReplayEngine(); 96 | TTD::ReplayEngine ttdengine2 = TTD::ReplayEngine(); 97 | int result; 98 | unsigned __int64 step_size = DEFAULT_STEP_SIZE; 99 | 100 | std::cout << "Openning the trace\n"; 101 | result = ttdengine.Initialize(L"D:\\traces\\lsass01.run"); 102 | if (result == 0) { 103 | std::cout << "Fail to open the trace"; 104 | exit(-1); 105 | } 106 | std::cout << "Openning the trace2\n"; 107 | result = ttdengine2.Initialize(L"D:\\traces\\lsass01.run"); 108 | if (result == 0) { 109 | std::cout << "Fail to open the trace"; 110 | exit(-1); 111 | } 112 | 113 | TTD::Cursor ttdcursor = ttdengine.NewCursor(); 114 | TTD::Position* last = ttdengine.GetLastPosition(); 115 | TTD::Cursor ttdcursor2 = ttdengine2.NewCursor(); 116 | TTD::Position* last2 = ttdengine2.GetLastPosition(); 117 | 118 | ttdcursor.SetPosition(0x177B, 0x3F); 119 | ttdcursor2.SetPosition(0x6871, 0x3F); 120 | 121 | TTD::GuestAddress pc; 122 | TTD::GuestAddress pc2; 123 | TTD::TTD_Replay_ICursorView_ReplayResult replayrez; 124 | deb: 125 | ttdcursor.SetCallReturnCallback((TTD::PROC_CallCallback) callCallback_diff, 1); 126 | ttdcursor2.SetCallReturnCallback((TTD::PROC_CallCallback) callCallback_diff, 2); 127 | takeSnapshot(&ttdcursor, &ttdcursor2); 128 | TTD::Position* cur = ttdcursor.GetPosition(); 129 | TTD::Position* cur2 = ttdcursor2.GetPosition(); 130 | 131 | while (step_size > 0) { 132 | printf("STEP %llu\n", step_size); 133 | cur = ttdcursor.GetPosition(); 134 | cur2 = ttdcursor2.GetPosition(); 135 | printf("Trace 1: %llx:%llx\t| Trace 2: %llx:%llx\n", cur->Major, cur->Minor, cur2->Major, cur2->Minor); 136 | ttdcursor.ReplayForward(&replayrez, last, step_size); 137 | ttdcursor2.ReplayForward(&replayrez, last2, step_size); 138 | pc = ttdcursor.GetProgramCounter(); 139 | pc2 = ttdcursor2.GetProgramCounter(); 140 | printf("Trace 1 ends on %llx\t| Trace 2 ends on %llx\n", pc, pc2); 141 | if (pc != pc2) { 142 | printf("Rollback..."); 143 | restoreSnapshot(&ttdcursor, &ttdcursor2); 144 | printf("OK\n"); 145 | step_size /= 2; 146 | } 147 | else { 148 | // Set new reference 149 | takeSnapshot(&ttdcursor, &ttdcursor2); 150 | } 151 | } 152 | 153 | printf("Last known sync reference: Trace 1: %llx:%llx\t| Trace 2: %llx:%llx\n", cur->Major, cur->Minor, cur2->Major, cur2->Minor); 154 | printf("Cur func: %llx -> %llx | %llx -> %llx\n", g_func_stack.back(), g_ret_stack.back(), g_func_stack2.back(), g_ret_stack2.back()); 155 | 156 | // Look the call stacks for allow-listed functions 157 | TTD::GuestAddress ret_sync_addr = NULL; 158 | int stack_high; 159 | printf("Common func/ret:\n"); 160 | for (stack_high = 0; stack_high < g_func_stack.size(); stack_high++) { 161 | // Check if the function is the same in both traces 162 | if (g_func_stack[stack_high] != g_func_stack2[stack_high]) { 163 | continue; 164 | } 165 | // Check the return address is the same 166 | if (g_ret_stack[stack_high] != g_ret_stack2[stack_high]) { 167 | continue; 168 | } 169 | for (int j = 0; j < stack_high; j++) { 170 | printf("\t"); 171 | } 172 | printf("%llx -> %llx\n", g_func_stack[stack_high], g_ret_stack[stack_high]); 173 | if (std::find(KNOWN_FUNC.begin(), KNOWN_FUNC.end(), g_func_stack[stack_high]) != KNOWN_FUNC.end()) { // Function known for path diff, such as allocators 174 | ret_sync_addr = g_ret_stack[stack_high]; 175 | break; 176 | } 177 | } 178 | 179 | if (ret_sync_addr != NULL) { 180 | printf("-> Resync traces after their RET on %llx", ret_sync_addr); 181 | 182 | // Create a BP on RET address 183 | TTD::TTD_Replay_MemoryWatchpointData data; 184 | data.addr = ret_sync_addr; 185 | data.flags = TTD::BP_FLAGS::EXEC; 186 | data.size = 1; 187 | ttdcursor.AddMemoryWatchpoint(&data); 188 | ttdcursor2.AddMemoryWatchpoint(&data); 189 | 190 | // Clean-up callbacks, we won't use them during the fast-forward 191 | ttdcursor.SetCallReturnCallback((TTD::PROC_CallCallback) NULL, 1); 192 | ttdcursor2.SetCallReturnCallback((TTD::PROC_CallCallback) NULL, 2); 193 | 194 | // Forward Trace 1 195 | ttdcursor.ReplayForward(&replayrez, last, -1); 196 | ttdcursor.RemoveMemoryWatchpoint(&data); 197 | printf("...TRACE1 %llx...", ttdcursor.GetProgramCounter()); 198 | assert(ttdcursor.GetProgramCounter() == ret_sync_addr); 199 | 200 | // Forward Trace 2 201 | ttdcursor2.ReplayForward(&replayrez, last2, -1); 202 | ttdcursor2.RemoveMemoryWatchpoint(&data); 203 | printf("...TRACE2 %llx\n", ttdcursor.GetProgramCounter()); 204 | assert(ttdcursor2.GetProgramCounter() == ret_sync_addr); 205 | 206 | // Restore the step size 207 | step_size = DEFAULT_STEP_SIZE; 208 | 209 | // Remove no-more-used stacked elements 210 | for (auto j = g_func_stack.size(); j > stack_high; j--) { 211 | g_func_stack.pop_back(); 212 | g_func_stack2.pop_back(); 213 | g_ret_stack.pop_back(); 214 | g_ret_stack2.pop_back(); 215 | } 216 | assert(g_func_stack.size() == stack_high); 217 | 218 | // Let's do it all over again 219 | goto deb; 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /example_tenet/example_tenet.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {8923f142-e0f7-4595-8b83-1e509100c162} 25 | exampletenet 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Console 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | 120 | 121 | Console 122 | true 123 | 124 | 125 | 126 | 127 | Level3 128 | true 129 | true 130 | true 131 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 132 | true 133 | MultiThreaded 134 | 135 | 136 | Console 137 | true 138 | true 139 | true 140 | 141 | 142 | 143 | 144 | {095478e2-f772-42e7-a921-98b92cefd363} 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /example_tenet/example_tenet.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /example_tenet/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "TTD/TTD.hpp" 11 | 12 | // Track memory accesses between two trace lines 13 | std::vector memsinfo; 14 | 15 | /* Clean-up memsinfo */ 16 | void cleanMemsinfo() { 17 | for (auto itMems = memsinfo.begin(); itMems != memsinfo.end(); itMems++) { 18 | free(*itMems); 19 | } 20 | memsinfo.clear(); 21 | } 22 | 23 | /* Lock for memsinfo writing in memory callback */ 24 | std::mutex set_accessmems; 25 | 26 | bool __stdcall memCallback(unsigned __int64 callback_value, const TTD::TTD_Replay_MemoryWatchpointResult* mem, struct TTD::TTD_Replay_IThreadView* thread_info) { 27 | TTD::TTD_Replay_MemoryWatchpointResult* temp = (TTD::TTD_Replay_MemoryWatchpointResult*) malloc(sizeof(TTD::TTD_Replay_MemoryWatchpointResult)); 28 | memcpy(temp, mem, sizeof(TTD::TTD_Replay_MemoryWatchpointResult)); 29 | set_accessmems.lock(); 30 | memsinfo.push_back(temp); 31 | set_accessmems.unlock(); 32 | return FALSE; 33 | } 34 | 35 | #define DUMP_REGISTER(REG) if (old == NULL || old->REG != newc->REG) { \ 36 | if (print_coma) { fdesc << ","; } \ 37 | fdesc << #REG "=0x" << std::hex << (newc->REG); \ 38 | print_coma = TRUE; \ 39 | } 40 | 41 | void dumpContext(std::ofstream& fdesc, CONTEXT* old, CONTEXT* newc, TTD::Cursor ttdcursor) { 42 | /* 43 | DWORD64 Rax; 44 | DWORD64 Rcx; 45 | DWORD64 Rdx; 46 | DWORD64 Rbx; 47 | DWORD64 Rsp; 48 | DWORD64 Rbp; 49 | DWORD64 Rsi; 50 | DWORD64 Rdi; 51 | DWORD64 R8; 52 | DWORD64 R9; 53 | DWORD64 R10; 54 | DWORD64 R11; 55 | DWORD64 R12; 56 | DWORD64 R13; 57 | DWORD64 R14; 58 | DWORD64 R15; 59 | DWORD64 Rip; 60 | */ 61 | BOOL print_coma = FALSE; 62 | DUMP_REGISTER(Rax); 63 | DUMP_REGISTER(Rbx); 64 | DUMP_REGISTER(Rcx); 65 | DUMP_REGISTER(Rdx); 66 | DUMP_REGISTER(Rdi); 67 | DUMP_REGISTER(Rsi); 68 | DUMP_REGISTER(Rbp); 69 | DUMP_REGISTER(Rsp); 70 | DUMP_REGISTER(R8); 71 | DUMP_REGISTER(R9); 72 | DUMP_REGISTER(R10); 73 | DUMP_REGISTER(R11); 74 | DUMP_REGISTER(R12); 75 | DUMP_REGISTER(R13); 76 | DUMP_REGISTER(R14); 77 | DUMP_REGISTER(R15); 78 | DUMP_REGISTER(Rip); 79 | 80 | for (auto itMems = memsinfo.begin(); itMems != memsinfo.end(); itMems++) { 81 | //printf("\nMEMS:%x:%x:%x\n", (*itMems)->addr, (*itMems)->size, (*itMems)->flags); 82 | struct TTD::MemoryBuffer* memorybuffer = ttdcursor.QueryMemoryBuffer((*itMems)->addr, (*itMems)->size); 83 | if (memorybuffer->data == NULL) { 84 | std::wcerr << "Query Memory FAIL: memory do not exists in trace"; 85 | continue; 86 | } 87 | if ((*itMems)->flags == 1) { // WRITE 88 | fdesc << ",mw=0x" << std::hex << memorybuffer->addr << ":"; 89 | } 90 | else if ((*itMems)->flags == 0) { // READ 91 | fdesc << ",mr=0x" << std::hex << memorybuffer->addr << ":"; 92 | } 93 | else { 94 | std::wcerr << "Unexpected: flags " << std::hex << (*itMems)->flags << "\n"; 95 | } 96 | for (int i = 0; i < (*itMems)->size; i++) { 97 | fdesc << std::setw(2) << std::setfill('0') << std::hex << std::uppercase << (unsigned int)(((unsigned char*)memorybuffer->data)[i]); 98 | } 99 | free(memorybuffer->data); 100 | free(memorybuffer); 101 | } 102 | fdesc << "\n"; 103 | } 104 | 105 | /**** ARGUMENT PARSING ****/ 106 | 107 | // From https://stackoverflow.com/a/868894 108 | char* getCmdOption(char** begin, char** end, const std::string& option) 109 | { 110 | char** itr = std::find(begin, end, option); 111 | if (itr != end && ++itr != end) 112 | { 113 | return *itr; 114 | } 115 | return 0; 116 | } 117 | 118 | bool cmdOptionExists(char** begin, char** end, const std::string& option) 119 | { 120 | return std::find(begin, end, option) != end; 121 | } 122 | 123 | std::wstring pathToName(wchar_t* path_in) { 124 | std::wstring path(path_in); 125 | auto name = path.substr(path.find_last_of(L"/\\") + 1); 126 | return name.substr(0, name.find_last_of(L".")); 127 | } 128 | 129 | /**** END ARGUMENT PARSING ****/ 130 | 131 | 132 | int main(int argc, char** argv) 133 | { 134 | if (argc <= 1) { 135 | printf("Usage:\n"); 136 | printf("%s [options] trace\n", argv[0]); 137 | printf("Options:\n"); 138 | printf("-b\tStarting position, as 1234:56. If not set, use the first position\n"); 139 | printf("-e\tEnding position, as 1234:56. If not set, use the last position\n"); 140 | printf("-m\tModule to cover, as 'ntdll'. If not set, list availables modules and exit\n"); 141 | return 0; 142 | } 143 | 144 | TTD::ReplayEngine ttdengine = TTD::ReplayEngine(); 145 | int result; 146 | 147 | std::cout << "Openning the trace\n"; 148 | wchar_t trace_path[MAX_PATH] = { 0 }; 149 | StringCbPrintfW(trace_path, MAX_PATH, L"%S", argv[argc - 1]); 150 | result = ttdengine.Initialize(trace_path); 151 | 152 | if (result == 0) { 153 | std::wcerr << "Fail to open the trace"; 154 | exit(-1); 155 | } 156 | 157 | TTD::Position* first = ttdengine.GetFirstPosition(); 158 | TTD::Position* last = ttdengine.GetLastPosition(); 159 | TTD::Cursor ttdcursor = ttdengine.NewCursor(); 160 | 161 | char* begin_arg = getCmdOption(argv, argv + argc, "-b"); 162 | if (begin_arg) { 163 | unsigned __int64 Major, Minor; 164 | sscanf_s(begin_arg, "%llx:%llx", &Major, &Minor); 165 | ttdcursor.SetPosition(Major, Minor); 166 | } 167 | else { 168 | ttdcursor.SetPosition(first); 169 | } 170 | 171 | TTD::Position end; 172 | char* end_arg = getCmdOption(argv, argv + argc, "-e"); 173 | if (end_arg) { 174 | sscanf_s(end_arg, "%llx:%llx", &end.Major, &end.Minor); 175 | } 176 | else { 177 | end.Major = last->Major; 178 | end.Minor = last->Minor; 179 | } 180 | 181 | char* mod_arg = getCmdOption(argv, argv + argc, "-m"); 182 | if (!mod_arg) { 183 | std::cout << "Module:\n"; 184 | std::cout << ttdengine.GetModuleCount() << "\n"; 185 | 186 | std::cout << "ModuleList:\n"; 187 | const TTD::TTD_Replay_Module* mod_list = ttdengine.GetModuleList(); 188 | for (int i = 0; i < ttdengine.GetModuleCount(); i++) { 189 | printf("%llx\t%llx\t%ls\n", mod_list[i].base_addr, mod_list[i].imageSize, mod_list[i].path); 190 | } 191 | exit(0); 192 | } 193 | 194 | TTD::GuestAddress addr_min = NULL; 195 | TTD::GuestAddress addr_max = NULL; 196 | const TTD::TTD_Replay_Module* mod_list = ttdengine.GetModuleList(); 197 | std::wstring_convert> converter; 198 | auto mod_arg_wide = converter.from_bytes(mod_arg); 199 | for (int i = 0; i < ttdengine.GetModuleCount(); i++) { 200 | auto name_no_dll = pathToName(mod_list[i].path); 201 | if (name_no_dll == mod_arg_wide) { 202 | printf("Track %ls [%llx to %llx]\n", mod_list[i].path, mod_list[i].base_addr, mod_list[i].base_addr + mod_list[i].imageSize - 1); 203 | addr_min = mod_list[i].base_addr; 204 | addr_max = mod_list[i].base_addr + mod_list[i].imageSize; 205 | break; 206 | } 207 | } 208 | if (addr_min == NULL) { 209 | printf("Unable to find %s", mod_arg); 210 | exit(0); 211 | } 212 | 213 | wchar_t file_path[MAX_PATH] = { 0 }; 214 | StringCbPrintfW(file_path, MAX_PATH, L"%s.trace.tenet", mod_arg_wide.c_str()); 215 | printf("Dump to file %ls\n", file_path); 216 | std::ofstream fdesc; 217 | fdesc.open(file_path, std::ofstream::binary); 218 | 219 | int i = 0; 220 | TTD::TTD_Replay_ICursorView_ReplayResult temp_result; 221 | CONTEXT* old_context = ttdcursor.GetContextx86_64(); 222 | CONTEXT* new_context = NULL; 223 | // Dump initial state 224 | dumpContext(fdesc, NULL, old_context, ttdcursor); 225 | 226 | TTD::TTD_Replay_MemoryWatchpointData data; 227 | data.addr = 0; 228 | data.size = 0xFFFFFFFFFFFFFFFF; 229 | data.flags = TTD::BP_FLAGS::WRITE | TTD::BP_FLAGS::READ; 230 | ttdcursor.AddMemoryWatchpoint(&data); 231 | ttdcursor.SetMemoryWatchpointCallback(memCallback, 0); 232 | 233 | for (;;) { 234 | ttdcursor.ReplayForward(&temp_result, &end, 1); 235 | if (temp_result.stepCount < 1) { 236 | break; 237 | } 238 | 239 | new_context = ttdcursor.GetContextx86_64(); 240 | if (new_context->Rip < addr_min || new_context->Rip >= addr_max) 241 | continue; 242 | dumpContext(fdesc, old_context, new_context, ttdcursor); 243 | cleanMemsinfo(); 244 | old_context = new_context; 245 | 246 | i++; 247 | if (i % 0x1000 == 0) { 248 | printf("0x%x instructions...\n", i); 249 | } 250 | } 251 | fdesc.close(); 252 | } 253 | -------------------------------------------------------------------------------- /python-bindings/module.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "TTD/TTD.hpp" 7 | 8 | 9 | namespace py = pybind11; 10 | 11 | PYBIND11_MODULE(pyTTD, m) { 12 | m.doc() = "Time Travel Debugging (TTD) wrapping"; 13 | m.attr("MAX_STEP") = 0xFFFFFFFFFFFFFFFE; 14 | 15 | py::enum_(m, "BP_FLAGS") 16 | .value("READ", TTD::BP_FLAGS::READ) 17 | .value("WRITE", TTD::BP_FLAGS::WRITE) 18 | .value("EXEC", TTD::BP_FLAGS::EXEC) 19 | .export_values(); 20 | 21 | py::class_>(m, "MemoryWatchpointData") 22 | .def(py::init(), py::arg("addr"), py::arg("size"), py::arg("flags")); 23 | 24 | py::class_>(m, "Module") 25 | .def_readonly("base_addr", &TTD::TTD_Replay_Module::base_addr) 26 | .def_readonly("checksum", &TTD::TTD_Replay_Module::checkSum) 27 | .def_readonly("image_size", &TTD::TTD_Replay_Module::imageSize) 28 | .def_readonly("path", &TTD::TTD_Replay_Module::path); 29 | 30 | py::class_>(m, "ThreadInfo") 31 | .def_readonly("threadid", &TTD::TTD_Replay_ThreadInfo::threadid); 32 | 33 | py::class_>(m, "ExceptionInfo") 34 | .def_readonly("exception_code", &TTD::TTD_Replay_Exception::ExceptionCode) 35 | .def_readonly("exception_flags", &TTD::TTD_Replay_Exception::ExceptionFlags) 36 | .def_readonly("exception_record", &TTD::TTD_Replay_Exception::ExceptionRecord) 37 | .def_readonly("exception_address", &TTD::TTD_Replay_Exception::ExceptionAddress); 38 | 39 | py::class_, std::unique_ptr, py::nodelete>>(m, "ModuleEvent") 40 | .def_readonly("position", &TTD::TTD_Replay_Event::pos) 41 | .def_readonly("info", &TTD::TTD_Replay_Event::info); 42 | 43 | py::class_, std::unique_ptr, py::nodelete>>(m, "ThreadEvent") 44 | .def_readonly("position", &TTD::TTD_Replay_Event::pos) 45 | .def_readonly("info", &TTD::TTD_Replay_Event::info); 46 | 47 | py::class_, std::unique_ptr, py::nodelete>>(m, "ExceptionEvent") 48 | .def_readonly("position", &TTD::TTD_Replay_Event::pos) 49 | .def_readonly("info", &TTD::TTD_Replay_Event::info); 50 | 51 | py::class_>(m, "Position") 52 | .def_readwrite("major", &TTD::Position::Major) 53 | .def_readwrite("minor", &TTD::Position::Minor) 54 | .def("__repr__", 55 | [](const TTD::Position* pos) { 56 | char out[256] = { 0 }; 57 | sprintf_s(out, "", pos->Major, pos->Minor); 58 | return std::string(out); 59 | } 60 | ) 61 | .def("__lt__", [](TTD::Position &self, const TTD::Position &b) { 62 | return self < b; 63 | }, py::is_operator()) 64 | .def("__gt__", [](TTD::Position& self, const TTD::Position& b) { 65 | return self > b; 66 | }, py::is_operator()) 67 | .def("__eq__", [](TTD::Position& self, const TTD::Position& b) { 68 | return (self.Major == b.Major) && (self.Minor == b.Minor); 69 | }, py::is_operator()); 70 | 71 | py::class_>(m, "ActiveThreadInfo") 72 | .def_property("threadid", [](TTD::TTD_Replay_ActiveThreadInfo& self) { 73 | return self.info->threadid; 74 | }, nullptr) 75 | .def_property("next_major", [](TTD::TTD_Replay_ActiveThreadInfo& self) { 76 | return self.next.Major; 77 | }, nullptr) 78 | .def_property("next_minor", [](TTD::TTD_Replay_ActiveThreadInfo& self) { 79 | return self.next.Minor; 80 | }, nullptr) 81 | .def_property("last_major", [](TTD::TTD_Replay_ActiveThreadInfo& self) { 82 | return self.last.Major; 83 | }, nullptr) 84 | .def_property("last_minor", [](TTD::TTD_Replay_ActiveThreadInfo& self) { 85 | return self.last.Minor; 86 | }, nullptr); 87 | 88 | // x86_64 context 89 | py::class_>(m, "Context") 90 | .def_readonly("seg_cs", &CONTEXT::SegCs) 91 | .def_readonly("seg_ds", &CONTEXT::SegDs) 92 | .def_readonly("seg_es", &CONTEXT::SegEs) 93 | .def_readonly("seg_fs", &CONTEXT::SegFs) 94 | .def_readonly("seg_gs", &CONTEXT::SegGs) 95 | .def_readonly("seg_ss", &CONTEXT::SegSs) 96 | .def_readonly("eflags", &CONTEXT::EFlags) 97 | .def_readonly("rax", &CONTEXT::Rax) 98 | .def_readonly("rbx", &CONTEXT::Rbx) 99 | .def_readonly("rcx", &CONTEXT::Rcx) 100 | .def_readonly("rdx", &CONTEXT::Rdx) 101 | .def_readonly("rsi", &CONTEXT::Rsi) 102 | .def_readonly("rdi", &CONTEXT::Rdi) 103 | .def_readonly("rsp", &CONTEXT::Rsp) 104 | .def_readonly("rbp", &CONTEXT::Rbp) 105 | .def_readonly("r8", &CONTEXT::R8) 106 | .def_readonly("r9", &CONTEXT::R9) 107 | .def_readonly("r10", &CONTEXT::R10) 108 | .def_readonly("r11", &CONTEXT::R11) 109 | .def_readonly("r12", &CONTEXT::R12) 110 | .def_readonly("r13", &CONTEXT::R13) 111 | .def_readonly("r14", &CONTEXT::R14) 112 | .def_readonly("r15", &CONTEXT::R15) 113 | .def_readonly("rip", &CONTEXT::Rip) 114 | .def_readonly("fltsave", &CONTEXT::FltSave); 115 | 116 | // x86 context 117 | py::class_>(m, "WOW64_Context") 118 | .def_readonly("seg_cs", &WOW64_CONTEXT::SegCs) 119 | .def_readonly("seg_ds", &WOW64_CONTEXT::SegDs) 120 | .def_readonly("seg_es", &WOW64_CONTEXT::SegEs) 121 | .def_readonly("seg_fs", &WOW64_CONTEXT::SegFs) 122 | .def_readonly("seg_gs", &WOW64_CONTEXT::SegGs) 123 | .def_readonly("seg_ss", &WOW64_CONTEXT::SegSs) 124 | .def_readonly("eflags", &WOW64_CONTEXT::EFlags) 125 | .def_readonly("eax", &WOW64_CONTEXT::Eax) 126 | .def_readonly("ebx", &WOW64_CONTEXT::Ebx) 127 | .def_readonly("ecx", &WOW64_CONTEXT::Ecx) 128 | .def_readonly("edx", &WOW64_CONTEXT::Edx) 129 | .def_readonly("esi", &WOW64_CONTEXT::Esi) 130 | .def_readonly("edi", &WOW64_CONTEXT::Edi) 131 | .def_readonly("esp", &WOW64_CONTEXT::Esp) 132 | .def_readonly("ebp", &WOW64_CONTEXT::Ebp) 133 | .def_readonly("eip", &WOW64_CONTEXT::Eip); 134 | 135 | 136 | py::class_(m, "Cursor") 137 | .def("set_position", py::overload_cast(&TTD::Cursor::SetPosition), py::arg("position")) 138 | .def("set_position", py::overload_cast(&TTD::Cursor::SetPosition), py::arg("Major"), py::arg("Minor")) 139 | .def("get_position", py::overload_cast<>(&TTD::Cursor::GetPosition)) 140 | .def("get_position", py::overload_cast(&TTD::Cursor::GetPosition), py::arg("threadid")) 141 | .def("get_thread_count", &TTD::Cursor::GetThreadCount) 142 | .def("get_program_counter", py::overload_cast<>(&TTD::Cursor::GetProgramCounter)) 143 | .def("get_thread_info", py::overload_cast<>(&TTD::Cursor::GetThreadInfo)) 144 | .def("get_crossplatform_context", py::overload_cast<>(&TTD::Cursor::GetCrossPlatformContext)) 145 | .def("get_crossplatform_context", py::overload_cast(&TTD::Cursor::GetCrossPlatformContext), py::arg("threadid")) 146 | .def("get_context_x86_64", py::overload_cast<>(&TTD::Cursor::GetContextx86_64)) 147 | .def("get_context_x86_64", py::overload_cast(&TTD::Cursor::GetContextx86_64), py::arg("threadid")) 148 | .def("get_context_x86", py::overload_cast<>(&TTD::Cursor::GetContextx86)) 149 | .def("get_context_x86", py::overload_cast(&TTD::Cursor::GetContextx86), py::arg("threadid")) 150 | .def("read_mem", [](TTD::Cursor &self, TTD::GuestAddress addr, unsigned __int64 size) { 151 | TTD::MemoryBuffer *membuf = self.QueryMemoryBuffer(addr, size); 152 | std::string x((char*) membuf->data, membuf->size); 153 | free(membuf->data); 154 | free(membuf); 155 | return py::bytes(x); 156 | }) 157 | .def("replay_forward", [](TTD::Cursor& self, unsigned __int64 stepCount, TTD::Position* last) { 158 | TTD::TTD_Replay_ICursorView_ReplayResult replayrez; 159 | self.ReplayForward(&replayrez, last, stepCount); 160 | return replayrez.stepCount; 161 | }) 162 | .def("replay_backward", [](TTD::Cursor& self, unsigned __int64 stepCount, TTD::Position* first) { 163 | TTD::TTD_Replay_ICursorView_ReplayResult replayrez; 164 | self.ReplayBackward(&replayrez, first, stepCount); 165 | return replayrez.stepCount; 166 | }) 167 | .def("add_memory_watchpoint", &TTD::Cursor::AddMemoryWatchpoint) 168 | .def("remove_memory_watchpoint", &TTD::Cursor::RemoveMemoryWatchpoint) 169 | .def("get_module_count", &TTD::Cursor::GetModuleCount) 170 | .def("get_module_list", [](TTD::Cursor& self) { 171 | std::vector mods; 172 | TTD::TTD_Replay_ModuleInstance* mod_list = self.GetModuleList(); 173 | for (int i = 0; i < self.GetModuleCount(); i++) { 174 | mods.push_back(*mod_list[i].module); 175 | } 176 | return mods; 177 | }) 178 | .def("get_thread_list", [](TTD::Cursor& self) { 179 | std::vector threads; 180 | TTD::TTD_Replay_ActiveThreadInfo* thread_list = self.GetThreadList(); 181 | for (int i = 0; i < self.GetThreadCount(); i++) { 182 | threads.push_back(thread_list[i]); 183 | } 184 | return threads; 185 | }); 186 | 187 | py::class_(m, "ReplayEngine") 188 | .def(py::init<>()) 189 | .def("initialize", &TTD::ReplayEngine::Initialize, py::arg("trace_filename")) 190 | .def("get_thread_count", &TTD::ReplayEngine::GetThreadCount) 191 | .def("get_first_position", &TTD::ReplayEngine::GetFirstPosition) 192 | .def("get_last_position", &TTD::ReplayEngine::GetLastPosition) 193 | .def("get_peb_address", &TTD::ReplayEngine::GetPebAddress) 194 | .def("new_cursor", &TTD::ReplayEngine::NewCursor) 195 | .def("get_module_loaded_event_count", &TTD::ReplayEngine::GetModuleLoadedEventCount) 196 | .def("get_module_loaded_event_list", [](TTD::ReplayEngine& self) { 197 | std::vector mods; 198 | const TTD::TTD_Replay_ModuleLoadedEvent* mod_list = self.GetModuleLoadedEventList(); 199 | for (int i = 0; i < self.GetModuleLoadedEventCount(); i++) { 200 | mods.push_back(mod_list[i]); 201 | } 202 | return mods; 203 | }) 204 | .def("get_module_unloaded_event_count", &TTD::ReplayEngine::GetModuleUnloadedEventCount) 205 | .def("get_module_unloaded_event_list", [](TTD::ReplayEngine& self) { 206 | std::vector mods; 207 | const TTD::TTD_Replay_ModuleUnloadedEvent* mod_list = self.GetModuleUnloadedEventList(); 208 | for (int i = 0; i < self.GetModuleUnloadedEventCount(); i++) { 209 | mods.push_back(mod_list[i]); 210 | } 211 | return mods; 212 | }) 213 | .def("get_thread_created_event_count", &TTD::ReplayEngine::GetThreadCreatedEventCount) 214 | .def("get_thread_created_event_list", [](TTD::ReplayEngine& self) { 215 | std::vector thrds; 216 | const TTD::TTD_Replay_ThreadCreatedEvent* thrd_list = self.GetThreadCreatedEventList(); 217 | for (int i = 0; i < self.GetThreadCreatedEventCount(); i++) { 218 | thrds.push_back(thrd_list[i]); 219 | } 220 | return thrds; 221 | }) 222 | .def("get_thread_terminated_event_count", &TTD::ReplayEngine::GetThreadTerminatedEventCount) 223 | .def("get_thread_terminated_event_list", [](TTD::ReplayEngine& self) { 224 | std::vector thrds; 225 | const TTD::TTD_Replay_ThreadTerminatedEvent* thrd_list = self.GetThreadTerminatedEventList(); 226 | for (int i = 0; i < self.GetThreadTerminatedEventCount(); i++) { 227 | thrds.push_back(thrd_list[i]); 228 | } 229 | return thrds; 230 | }) 231 | .def("get_exception_event_count", &TTD::ReplayEngine::GetExceptionEventCount) 232 | .def("get_exception_event_list", [](TTD::ReplayEngine& self) { 233 | std::vector excps; 234 | const TTD::TTD_Replay_ExceptionEvent* excp_list = self.GetExceptionEventList(); 235 | for (int i = 0; i < self.GetExceptionEventCount(); i++) { 236 | excps.push_back(excp_list[i]); 237 | } 238 | return excps; 239 | }) 240 | .def("get_module_count", &TTD::ReplayEngine::GetModuleCount) 241 | .def("get_module_list", [](TTD::ReplayEngine& self) { 242 | std::vector mods; 243 | const TTD::TTD_Replay_Module* mod_list = self.GetModuleList(); 244 | for (int i = 0; i < self.GetModuleCount(); i++) { 245 | mods.push_back(mod_list[i]); 246 | } 247 | return mods; 248 | }); 249 | 250 | 251 | #ifdef VERSION_INFO 252 | m.attr("__version__") = VERSION_INFO; 253 | #else 254 | m.attr("__version__") = "dev"; 255 | #endif 256 | } 257 | -------------------------------------------------------------------------------- /python-bindings/python-bindings.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {6cd0eaf3-f683-4e30-b7fd-72fcba758865} 25 | pythonbindings 26 | 10.0 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | DynamicLibrary 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | DynamicLibrary 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | .pyd 76 | 77 | 78 | false 79 | .pyd 80 | 81 | 82 | true 83 | .pyd 84 | pyTTD 85 | 86 | 87 | false 88 | .pyd 89 | pyTTD 90 | 91 | 92 | 93 | Level3 94 | true 95 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 96 | true 97 | C:\Python310\Include;C:\Python310\lib\site-packages\pybind11\include 98 | 99 | 100 | Console 101 | true 102 | 103 | 104 | 105 | 106 | Level3 107 | true 108 | true 109 | true 110 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 111 | true 112 | C:\Python310\Include;C:\Python310\lib\site-packages\pybind11\include 113 | 114 | 115 | Console 116 | true 117 | true 118 | true 119 | 120 | 121 | 122 | 123 | Level3 124 | true 125 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 126 | true 127 | C:\Python310\Include;C:\Python310\lib\site-packages\pybind11\include 128 | 129 | 130 | Console 131 | true 132 | C:\Program Files\Python38\libs;%(AdditionalLibraryDirectories) 133 | %(AdditionalDependencies) 134 | 135 | 136 | 137 | 138 | Level3 139 | true 140 | true 141 | true 142 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 143 | true 144 | C:\Python310\Include;C:\Python310\lib\site-packages\pybind11\include 145 | MultiThreaded 146 | 147 | 148 | Console 149 | true 150 | true 151 | true 152 | C:\Python310\libs; 153 | %(AdditionalDependencies) 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | {095478e2-f772-42e7-a921-98b92cefd363} 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /python-bindings/python-bindings.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | --------------------------------------------------------------------------------