├── .github └── workflows │ ├── build.yml │ ├── deploy-create-releases.yml │ ├── deploy-develop.yml │ └── deploy-releases.yml ├── .gitignore ├── LICENSE ├── README.md ├── pd.sln └── pd ├── DynArray.h ├── ReadMe.txt ├── close_watcher.cpp ├── close_watcher.h ├── dirent.h ├── dump_process.cpp ├── dump_process.h ├── export_list.cpp ├── export_list.h ├── hash.cpp ├── hash.h ├── module_list.cpp ├── module_list.h ├── nmd_assembly.h ├── pd.cpp ├── pd.h ├── pd.rc ├── pd.vcproj ├── pd.vcxproj ├── pd.vcxproj.filters ├── pe_exports.cpp ├── pe_exports.h ├── pe_hash_database.cpp ├── pe_hash_database.h ├── pe_header.cpp ├── pe_header.h ├── pe_imports.cpp ├── pe_imports.h ├── resource.h ├── simple.cpp ├── simple.h ├── stdafx.cpp ├── stdafx.h ├── stream_wrapper.h ├── targetver.h ├── terminate_monitor_hook.cpp ├── terminate_monitor_hook.h ├── utils.h └── work_queue.h /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push] 4 | 5 | env: 6 | # Path to the solution file relative to the root of the project. 7 | SOLUTION_FILE_PATH: . 8 | 9 | # Configuration type to build. 10 | # You can convert this to a build matrix if you need coverage of multiple configuration types. 11 | # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 12 | BUILD_CONFIGURATION: Release 13 | 14 | jobs: 15 | build: 16 | runs-on: windows-latest 17 | 18 | strategy: 19 | matrix: 20 | targetPlatform: [Win32, x64] 21 | 22 | env: 23 | BUILD_FILE_PATH: .\${{ matrix.targetPlatform }}\Release\pd.exe 24 | 25 | steps: 26 | - uses: actions/checkout@v2 27 | 28 | - name: Add MSBuild to PATH 29 | uses: microsoft/setup-msbuild@v1.0.2 30 | 31 | - name: Restore NuGet packages 32 | working-directory: ${{env.GITHUB_WORKSPACE}} 33 | run: nuget restore ${{env.SOLUTION_FILE_PATH}} 34 | 35 | - name: Build 36 | working-directory: ${{env.GITHUB_WORKSPACE}} 37 | # Add additional options to the MSBuild command line here (like platform or verbosity level). 38 | # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference 39 | run: msbuild /m /p:Configuration=${{ env.BUILD_CONFIGURATION }} ${{ env.SOLUTION_FILE_PATH }} /p:Platform=${{ matrix.targetPlatform }} 40 | 41 | - name: Upload build artifacts 42 | uses: actions/upload-artifact@v2 43 | with: 44 | name: ${{ matrix.targetPlatform }} Executable 45 | path: ${{ env.BUILD_FILE_PATH }} 46 | -------------------------------------------------------------------------------- /.github/workflows/deploy-create-releases.yml: -------------------------------------------------------------------------------- 1 | name: BuildAndDeploy_CreateReleaseFromCommit 2 | 3 | on: 4 | push: 5 | workflow_dispatch: 6 | inputs: 7 | commit: 8 | type: string 9 | description: Sha1 commit to build and release. 10 | version: 11 | type: string 12 | description: Version to release it as. Eg v2.1. 13 | 14 | env: 15 | # Path to the solution file relative to the root of the project. 16 | SOLUTION_FILE_PATH: . 17 | 18 | # Configuration type to build. 19 | # You can convert this to a build matrix if you need coverage of multiple configuration types. 20 | # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 21 | BUILD_CONFIGURATION: Release 22 | 23 | jobs: 24 | build: 25 | runs-on: windows-latest 26 | 27 | strategy: 28 | matrix: 29 | targetPlatform: [Win32, x64] 30 | 31 | env: 32 | BUILD_FILE_PATH: .\${{ matrix.targetPlatform }}\Release\pd.exe 33 | 34 | steps: 35 | - uses: actions/checkout@v2 36 | with: 37 | ref: ${{ github.event.inputs.commit }} 38 | 39 | - name: Add MSBuild to PATH 40 | uses: microsoft/setup-msbuild@v1.0.2 41 | 42 | - name: Restore NuGet packages 43 | working-directory: ${{env.GITHUB_WORKSPACE}} 44 | run: nuget restore ${{env.SOLUTION_FILE_PATH}} 45 | 46 | - name: Build 47 | working-directory: ${{env.GITHUB_WORKSPACE}} 48 | # Add additional options to the MSBuild command line here (like platform or verbosity level). 49 | # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference 50 | run: msbuild /m /p:Configuration=${{ env.BUILD_CONFIGURATION }} ${{ env.SOLUTION_FILE_PATH }} /p:Platform=${{ matrix.targetplatform }} 51 | 52 | - name: Upload build artifacts 53 | uses: actions/upload-artifact@v2 54 | with: 55 | name: ${{ matrix.targetPlatform }} Executable 56 | path: ${{ env.BUILD_FILE_PATH }} 57 | 58 | - name: Upload build artifacts old x86 59 | uses: actions/upload-artifact@v2 60 | with: 61 | name: ${{ matrix.targetPlatform }} Executable 62 | path: .\Release\pd.exe 63 | 64 | - name: Upload build artifacts old x64 65 | uses: actions/upload-artifact@v2 66 | with: 67 | name: ${{ matrix.targetPlatform }} Executable 68 | path: .\x64\Release\pd.exe 69 | 70 | release: 71 | needs: build 72 | runs-on: windows-latest 73 | 74 | steps: 75 | - uses: actions/checkout@v2 76 | 77 | - uses: actions/download-artifact@v2 78 | id: executable_x86 79 | with: 80 | name: Win32 Executable 81 | 82 | - name: Rename 83 | run: | 84 | ren pd.exe pd32.exe 85 | 86 | - uses: actions/download-artifact@v2 87 | id: executable_x64 88 | with: 89 | name: x64 Executable 90 | 91 | - name: Rename 92 | run: | 93 | ren pd.exe pd64.exe 94 | 95 | - name: Display structure of downloaded files 96 | run: ls -R 97 | 98 | - name: Create Release 99 | id: create_release 100 | uses: actions/create-release@latest 101 | env: 102 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 103 | with: 104 | tag_name: ${{ github.event.inputs.version }} 105 | release_name: ${{ github.event.inputs.version }} 106 | body: | 107 | ${{ steps.Changelog.outputs.changelog }} 108 | draft: false 109 | prerelease: false 110 | 111 | # Upload release asset: https://github.com/actions/upload-release-asset 112 | - name: Update release asset x86 113 | uses: actions/upload-release-asset@v1 114 | env: 115 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 116 | with: 117 | upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 118 | asset_path: ${{ steps.executable_x86.outputs.download-path }}\pd32.exe 119 | asset_name: pd32.exe 120 | asset_content_type: application/zip 121 | 122 | - name: Update release asset x64 123 | uses: actions/upload-release-asset@v1 124 | env: 125 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 126 | with: 127 | upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 128 | asset_path: ${{ steps.executable_x64.outputs.download-path }}\pd64.exe 129 | asset_name: pd64.exe 130 | asset_content_type: application/zip 131 | -------------------------------------------------------------------------------- /.github/workflows/deploy-develop.yml: -------------------------------------------------------------------------------- 1 | name: BuildAndDeploy_Develop 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | env: 10 | # Path to the solution file relative to the root of the project. 11 | SOLUTION_FILE_PATH: . 12 | 13 | # Configuration type to build. 14 | # You can convert this to a build matrix if you need coverage of multiple configuration types. 15 | # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 16 | BUILD_CONFIGURATION: Release 17 | 18 | jobs: 19 | build: 20 | runs-on: windows-latest 21 | 22 | strategy: 23 | matrix: 24 | targetPlatform: [Win32, x64] 25 | 26 | env: 27 | BUILD_FILE_PATH: .\${{ matrix.targetPlatform }}\Release\pd.exe 28 | 29 | steps: 30 | - uses: actions/checkout@v2 31 | 32 | - name: Add MSBuild to PATH 33 | uses: microsoft/setup-msbuild@v1.0.2 34 | 35 | - name: Restore NuGet packages 36 | working-directory: ${{env.GITHUB_WORKSPACE}} 37 | run: nuget restore ${{env.SOLUTION_FILE_PATH}} 38 | 39 | - name: Build 40 | working-directory: ${{env.GITHUB_WORKSPACE}} 41 | # Add additional options to the MSBuild command line here (like platform or verbosity level). 42 | # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference 43 | run: msbuild /m /p:Configuration=${{ env.BUILD_CONFIGURATION }} ${{ env.SOLUTION_FILE_PATH }} /p:Platform=${{ matrix.targetplatform }} 44 | 45 | - name: Upload build artifacts 46 | uses: actions/upload-artifact@v2 47 | with: 48 | name: ${{ matrix.targetPlatform }} Executable 49 | path: ${{ env.BUILD_FILE_PATH }} 50 | 51 | release: 52 | needs: build 53 | runs-on: windows-latest 54 | 55 | steps: 56 | - uses: actions/checkout@v2 57 | 58 | - uses: actions/download-artifact@v2 59 | id: executable_x86 60 | with: 61 | name: Win32 Executable 62 | 63 | - name: Rename 64 | run: | 65 | ren pd.exe pd32.exe 66 | 67 | - uses: actions/download-artifact@v2 68 | id: executable_x64 69 | with: 70 | name: x64 Executable 71 | 72 | - name: Rename 73 | run: | 74 | ren pd.exe pd64.exe 75 | 76 | - name: Display structure of downloaded files 77 | run: ls -R 78 | 79 | # Replace Develop release binaries 80 | - name: Upload binaries to release 81 | uses: svenstaro/upload-release-action@v2 82 | with: 83 | repo_token: ${{ secrets.GITHUB_TOKEN }} 84 | file: pd32.exe 85 | tag: Develop 86 | overwrite: true 87 | file_glob: false 88 | 89 | - name: Upload binaries to release 90 | uses: svenstaro/upload-release-action@v2 91 | with: 92 | repo_token: ${{ secrets.GITHUB_TOKEN }} 93 | file: pd64.exe 94 | tag: Develop 95 | overwrite: true 96 | file_glob: false 97 | -------------------------------------------------------------------------------- /.github/workflows/deploy-releases.yml: -------------------------------------------------------------------------------- 1 | name: BuildAndDeploy_Release 2 | 3 | on: 4 | push: 5 | tags: 6 | # Has a release version tag 7 | - 'v[0-9]+.[0-9]+' 8 | branches: 9 | - main 10 | workflow_dispatch: 11 | 12 | env: 13 | # Path to the solution file relative to the root of the project. 14 | SOLUTION_FILE_PATH: . 15 | 16 | # Configuration type to build. 17 | # You can convert this to a build matrix if you need coverage of multiple configuration types. 18 | # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 19 | BUILD_CONFIGURATION: Release 20 | 21 | jobs: 22 | build: 23 | runs-on: windows-latest 24 | 25 | strategy: 26 | matrix: 27 | targetPlatform: [Win32, x64] 28 | 29 | env: 30 | BUILD_FILE_PATH: .\${{ matrix.targetPlatform }}\Release\pd.exe 31 | 32 | steps: 33 | - uses: actions/checkout@v2 34 | 35 | - name: Add MSBuild to PATH 36 | uses: microsoft/setup-msbuild@v1.0.2 37 | 38 | - name: Restore NuGet packages 39 | working-directory: ${{env.GITHUB_WORKSPACE}} 40 | run: nuget restore ${{env.SOLUTION_FILE_PATH}} 41 | 42 | - name: Build 43 | working-directory: ${{env.GITHUB_WORKSPACE}} 44 | # Add additional options to the MSBuild command line here (like platform or verbosity level). 45 | # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference 46 | run: msbuild /m /p:Configuration=${{ env.BUILD_CONFIGURATION }} ${{ env.SOLUTION_FILE_PATH }} /p:Platform=${{ matrix.targetplatform }} 47 | 48 | - name: Upload build artifacts 49 | uses: actions/upload-artifact@v2 50 | with: 51 | name: ${{ matrix.targetPlatform }} Executable 52 | path: ${{ env.BUILD_FILE_PATH }} 53 | 54 | release: 55 | needs: build 56 | runs-on: windows-latest 57 | 58 | steps: 59 | - uses: actions/checkout@v2 60 | 61 | - uses: actions/download-artifact@v2 62 | id: executable_x86 63 | with: 64 | name: Win32 Executable 65 | 66 | - name: Rename 67 | run: | 68 | ren pd.exe pd32.exe 69 | 70 | - uses: actions/download-artifact@v2 71 | id: executable_x64 72 | with: 73 | name: x64 Executable 74 | 75 | - name: Rename 76 | run: | 77 | ren pd.exe pd64.exe 78 | 79 | - name: Display structure of downloaded files 80 | run: ls -R 81 | 82 | - name: 'Get Previous tag' 83 | id: previoustag 84 | uses: "WyriHaximus/github-action-get-previous-tag@v1" 85 | with: 86 | fallback: Develop # Optional fallback tag to use when no tag can be found 87 | 88 | - name: Create Release 89 | id: create_release 90 | uses: actions/create-release@latest 91 | env: 92 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 93 | with: 94 | tag_name: ${{ steps.previoustag.outputs.tag }} 95 | release_name: ${{ steps.previoustag.outputs.tag }} 96 | body: | 97 | ${{ steps.Changelog.outputs.changelog }} 98 | draft: false 99 | prerelease: false 100 | 101 | # Upload release asset: https://github.com/actions/upload-release-asset 102 | - name: Update release asset x86 103 | uses: actions/upload-release-asset@v1 104 | env: 105 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 106 | with: 107 | upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 108 | asset_path: ${{ steps.executable_x86.outputs.download-path }}\pd32.exe 109 | asset_name: pd32.exe 110 | asset_content_type: application/zip 111 | 112 | - name: Update release asset x64 113 | uses: actions/upload-release-asset@v1 114 | env: 115 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 116 | with: 117 | upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 118 | asset_path: ${{ steps.executable_x64.outputs.download-path }}\pd64.exe 119 | asset_name: pd64.exe 120 | asset_content_type: application/zip 121 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | <<<<<<< HEAD 2 | ## Ignore Visual Studio temporary files, build results, and 3 | ## files generated by popular Visual Studio add-ons. 4 | 5 | # User-specific files 6 | *.suo 7 | *.user 8 | *.userosscache 9 | *.sln.docstates 10 | 11 | # User-specific files (MonoDevelop/Xamarin Studio) 12 | *.userprefs 13 | 14 | # Build results 15 | [Dd]ebug/ 16 | [Dd]ebugPublic/ 17 | [Rr]elease/ 18 | [Rr]eleases/ 19 | x64/ 20 | x86/ 21 | build/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | 26 | # Visual Studo 2015 cache/options directory 27 | .vs/ 28 | 29 | # MSTest test Results 30 | [Tt]est[Rr]esult*/ 31 | [Bb]uild[Ll]og.* 32 | 33 | # NUNIT 34 | *.VisualState.xml 35 | TestResult.xml 36 | 37 | # Build Results of an ATL Project 38 | [Dd]ebugPS/ 39 | [Rr]eleasePS/ 40 | dlldata.c 41 | 42 | *_i.c 43 | *_p.c 44 | *_i.h 45 | *.ilk 46 | *.meta 47 | *.obj 48 | *.pch 49 | *.pdb 50 | *.pgc 51 | *.pgd 52 | *.rsp 53 | *.sbr 54 | *.tlb 55 | *.tli 56 | *.tlh 57 | *.tmp 58 | *.tmp_proj 59 | *.log 60 | *.vspscc 61 | *.vssscc 62 | .builds 63 | *.pidb 64 | *.svclog 65 | *.scc 66 | 67 | # Chutzpah Test files 68 | _Chutzpah* 69 | 70 | # Visual C++ cache files 71 | ipch/ 72 | *.aps 73 | *.ncb 74 | *.opensdf 75 | *.sdf 76 | *.cachefile 77 | 78 | # Visual Studio profiler 79 | *.psess 80 | *.vsp 81 | *.vspx 82 | 83 | # TFS 2012 Local Workspace 84 | $tf/ 85 | 86 | # Guidance Automation Toolkit 87 | *.gpState 88 | 89 | # ReSharper is a .NET coding add-in 90 | _ReSharper*/ 91 | *.[Rr]e[Ss]harper 92 | *.DotSettings.user 93 | 94 | # JustCode is a .NET coding addin-in 95 | .JustCode 96 | 97 | # TeamCity is a build add-in 98 | _TeamCity* 99 | 100 | # DotCover is a Code Coverage Tool 101 | *.dotCover 102 | 103 | # NCrunch 104 | _NCrunch_* 105 | .*crunch*.local.xml 106 | 107 | # MightyMoose 108 | *.mm.* 109 | AutoTest.Net/ 110 | 111 | # Web workbench (sass) 112 | .sass-cache/ 113 | 114 | # Installshield output folder 115 | [Ee]xpress/ 116 | 117 | # DocProject is a documentation generator add-in 118 | DocProject/buildhelp/ 119 | DocProject/Help/*.HxT 120 | DocProject/Help/*.HxC 121 | DocProject/Help/*.hhc 122 | DocProject/Help/*.hhk 123 | DocProject/Help/*.hhp 124 | DocProject/Help/Html2 125 | DocProject/Help/html 126 | 127 | # Click-Once directory 128 | publish/ 129 | 130 | # Publish Web Output 131 | *.[Pp]ublish.xml 132 | *.azurePubxml 133 | # TODO: Comment the next line if you want to checkin your web deploy settings 134 | # but database connection strings (with potential passwords) will be unencrypted 135 | *.pubxml 136 | *.publishproj 137 | 138 | # NuGet Packages 139 | *.nupkg 140 | # The packages folder can be ignored because of Package Restore 141 | **/packages/* 142 | # except build/, which is used as an MSBuild target. 143 | !**/packages/build/ 144 | # Uncomment if necessary however generally it will be regenerated when needed 145 | #!**/packages/repositories.config 146 | 147 | # Windows Azure Build Output 148 | csx/ 149 | *.build.csdef 150 | 151 | # Windows Store app package directory 152 | AppPackages/ 153 | 154 | # Others 155 | *.[Cc]ache 156 | ClientBin/ 157 | [Ss]tyle[Cc]op.* 158 | ~$* 159 | *~ 160 | *.dbmdl 161 | *.dbproj.schemaview 162 | *.pfx 163 | *.publishsettings 164 | node_modules/ 165 | bower_components/ 166 | 167 | # RIA/Silverlight projects 168 | Generated_Code/ 169 | 170 | # Backup & report files from converting an old project file 171 | # to a newer Visual Studio version. Backup files are not needed, 172 | # because we have git ;-) 173 | _UpgradeReport_Files/ 174 | Backup*/ 175 | UpgradeLog*.XML 176 | UpgradeLog*.htm 177 | 178 | # SQL Server files 179 | *.mdf 180 | *.ldf 181 | 182 | # Business Intelligence projects 183 | *.rdl.data 184 | *.bim.layout 185 | *.bim_*.settings 186 | 187 | # Microsoft Fakes 188 | FakesAssemblies/ 189 | 190 | # Node.js Tools for Visual Studio 191 | .ntvs_analysis.dat 192 | 193 | # Visual Studio 6 build log 194 | *.plg 195 | 196 | # Visual Studio 6 workspace options file 197 | *.opt 198 | 199 | ======= 200 | ## Ignore Visual Studio temporary files, build results, and 201 | ## files generated by popular Visual Studio add-ons. 202 | 203 | # User-specific files 204 | *.suo 205 | *.user 206 | *.userosscache 207 | *.sln.docstates 208 | 209 | # User-specific files (MonoDevelop/Xamarin Studio) 210 | *.userprefs 211 | 212 | # Build results 213 | [Dd]ebug/ 214 | [Dd]ebugPublic/ 215 | [Rr]elease/ 216 | [Rr]eleases/ 217 | x64/ 218 | x86/ 219 | build/ 220 | bld/ 221 | [Bb]in/ 222 | [Oo]bj/ 223 | 224 | # Visual Studo 2015 cache/options directory 225 | .vs/ 226 | 227 | # MSTest test Results 228 | [Tt]est[Rr]esult*/ 229 | [Bb]uild[Ll]og.* 230 | 231 | # NUNIT 232 | *.VisualState.xml 233 | TestResult.xml 234 | 235 | # Build Results of an ATL Project 236 | [Dd]ebugPS/ 237 | [Rr]eleasePS/ 238 | dlldata.c 239 | 240 | *_i.c 241 | *_p.c 242 | *_i.h 243 | *.ilk 244 | *.meta 245 | *.obj 246 | *.pch 247 | *.pdb 248 | *.pgc 249 | *.pgd 250 | *.rsp 251 | *.sbr 252 | *.tlb 253 | *.tli 254 | *.tlh 255 | *.tmp 256 | *.tmp_proj 257 | *.log 258 | *.vspscc 259 | *.vssscc 260 | .builds 261 | *.pidb 262 | *.svclog 263 | *.scc 264 | 265 | # Chutzpah Test files 266 | _Chutzpah* 267 | 268 | # Visual C++ cache files 269 | ipch/ 270 | *.aps 271 | *.ncb 272 | *.opensdf 273 | *.sdf 274 | *.cachefile 275 | 276 | # Visual Studio profiler 277 | *.psess 278 | *.vsp 279 | *.vspx 280 | 281 | # TFS 2012 Local Workspace 282 | $tf/ 283 | 284 | # Guidance Automation Toolkit 285 | *.gpState 286 | 287 | # ReSharper is a .NET coding add-in 288 | _ReSharper*/ 289 | *.[Rr]e[Ss]harper 290 | *.DotSettings.user 291 | 292 | # JustCode is a .NET coding addin-in 293 | .JustCode 294 | 295 | # TeamCity is a build add-in 296 | _TeamCity* 297 | 298 | # DotCover is a Code Coverage Tool 299 | *.dotCover 300 | 301 | # NCrunch 302 | _NCrunch_* 303 | .*crunch*.local.xml 304 | 305 | # MightyMoose 306 | *.mm.* 307 | AutoTest.Net/ 308 | 309 | # Web workbench (sass) 310 | .sass-cache/ 311 | 312 | # Installshield output folder 313 | [Ee]xpress/ 314 | 315 | # DocProject is a documentation generator add-in 316 | DocProject/buildhelp/ 317 | DocProject/Help/*.HxT 318 | DocProject/Help/*.HxC 319 | DocProject/Help/*.hhc 320 | DocProject/Help/*.hhk 321 | DocProject/Help/*.hhp 322 | DocProject/Help/Html2 323 | DocProject/Help/html 324 | 325 | # Click-Once directory 326 | publish/ 327 | 328 | # Publish Web Output 329 | *.[Pp]ublish.xml 330 | *.azurePubxml 331 | # TODO: Comment the next line if you want to checkin your web deploy settings 332 | # but database connection strings (with potential passwords) will be unencrypted 333 | *.pubxml 334 | *.publishproj 335 | 336 | # NuGet Packages 337 | *.nupkg 338 | # The packages folder can be ignored because of Package Restore 339 | **/packages/* 340 | # except build/, which is used as an MSBuild target. 341 | !**/packages/build/ 342 | # Uncomment if necessary however generally it will be regenerated when needed 343 | #!**/packages/repositories.config 344 | 345 | # Windows Azure Build Output 346 | csx/ 347 | *.build.csdef 348 | 349 | # Windows Store app package directory 350 | AppPackages/ 351 | 352 | # Others 353 | *.[Cc]ache 354 | ClientBin/ 355 | [Ss]tyle[Cc]op.* 356 | ~$* 357 | *~ 358 | *.dbmdl 359 | *.dbproj.schemaview 360 | *.pfx 361 | *.publishsettings 362 | node_modules/ 363 | bower_components/ 364 | 365 | # RIA/Silverlight projects 366 | Generated_Code/ 367 | 368 | # Backup & report files from converting an old project file 369 | # to a newer Visual Studio version. Backup files are not needed, 370 | # because we have git ;-) 371 | _UpgradeReport_Files/ 372 | Backup*/ 373 | UpgradeLog*.XML 374 | UpgradeLog*.htm 375 | 376 | # SQL Server files 377 | *.mdf 378 | *.ldf 379 | 380 | # Business Intelligence projects 381 | *.rdl.data 382 | *.bim.layout 383 | *.bim_*.settings 384 | 385 | # Microsoft Fakes 386 | FakesAssemblies/ 387 | 388 | # Node.js Tools for Visual Studio 389 | .ntvs_analysis.dat 390 | 391 | # Visual Studio 6 build log 392 | *.plg 393 | 394 | # Visual Studio 6 workspace options file 395 | *.opt 396 | >>>>>>> d26ace875aaef086a6992374a3169ca5c6db031d 397 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Geoff McDonald 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Process Dump 2 | Process Dump is a Windows reverse-engineering command-line tool to dump malware memory components back to disk for analysis. Often malware files are packed and obfuscated before they are executed in order to avoid AV scanners, however when these files are executed they will often unpack or inject a clean version of the malware code in memory. A common task for malware researchers when analyzing malware is to dump this unpacked code back from memory to disk for scanning with AV products or for analysis with static analysis tools such as IDA. 3 | 4 | Process Dump works for Windows 32 and 64 bit operating systems and can dump memory components from specific processes or from all processes currently running. Process Dump supports creation and use of a clean-hash database, so that dumping of all the clean files such as kernel32.dll can be skipped. It's main features include: 5 | * Dumps code from a specific process or all processes. 6 | * Finds and dumps hidden modules that are not properly loaded in processes. 7 | * Finds and dumps loose code chunks even if they aren't associated with a PE file. It builds a PE header and import table for the chunks. 8 | * Reconstructs imports using an aggressive approach. 9 | * Can run in close dump monitor mode ('-closemon'), where processes will be paused and dumped just before they terminate. 10 | * Multi-threaded, so when you are dumping all running processes it will go pretty quickly. 11 | * Can generate a clean hash database. Generate this before a machine is infected with malware so Process Dump will only dump the new malicious malware components. 12 | 13 | I'm maintaining an official compiled release on my website here: 14 | https://split-code.com/processdump.html 15 | 16 | # Installation 17 | You can download the latest compiled release of Process Dump here: 18 | * https://github.com/glmcdona/Process-Dump/releases 19 | 20 | # Compiling source code 21 | This is designed for Visual Studio 2019 and works with the free Community edition. Just open the project file with VS2019 and compile, it should be that easy! 22 | 23 | # Command-line arguments 24 | Process dump can be used to dump all unknown code from memory ('-system' flag), dump specific processes, or run in a monitoring mode that dumps all processes just before they terminate. 25 | 26 | Before first usage of this tool, when on the clean workstation the clean excluding hash database can be generated by either: 27 | * pd -db genquick 28 | * pd -db gen 29 | 30 | Example Usage: 31 | * pd -system 32 | * pd -pid 419 33 | * pd -pid 0x1a3 34 | * pd -pid 0x1a3 -a 0x401000 -o c:\dump\ -c c:\dump\test\clean.db 35 | * pd -p chrome.exe 36 | * pd -p "(?i).\*chrome.\*" 37 | * pd -closemon 38 | 39 | The command-line arguments can be grouped as follows: 40 | 41 | **General Dumping Options** 42 | 43 | | Option | Description | 44 | |--------|-------------| 45 | | -system | Dumps all modules not matching the clean hash database from all accessible processes into the working directory. | 46 | | -pid \ | Dumps all modules not matching the clean hash database from the specified PID into the current working directory. Use a '0x' prefix to specify a hex PID. | 47 | | -closemon | Runs in monitor mode. When any processes are terminating, process dump will first dump the process. | 48 | | -p \ | Dumps all modules not matching the clean hash database from the process name found to match the filter into specified PID into the current working directory. | 49 | | -a \ | Dumps a module at the specified base address from the process. | 50 | | -o \ | Sets the default output root folder for dumped components. | 51 | 52 | **Clean Hash Database Options** 53 | 54 | | Option | Description | 55 | |--------|-------------| 56 | | -db gen | Automatically processes a few common folders as well as all the currently running processes and adds the found module hashes to the clean hash database. It will add all files recursively in: `%WINDIR%`, `%HOMEPATH%`, `C:\Program Files\`, `C:\Program Files (x86)\`, as well as all modules in all running processes. These clean hashes will be added to the file `clean.hashes` in the application directory. During future process dumping commands, these known modules will not be dumped. It is recommended to run this command one time on a clean system prior to using the tool that way not too many modules will be dumped from memory.| 57 | | -db genquick | Same as above, but only adds the hashes from all modules in all processes to the clean hash database. This is a much faster way to build the clean hash database, but it will be less complete. | 58 | | -db add \ | Adds all the files in the specified directory recursively to the clean hash database. | 59 | | -db rem \ | Removes all the files in the specified directory recursively from the clean hash database. | 60 | | -nr | Disable recursion on hash database directory add or remove commands. | 61 | | -db clean | Clears the clean hash database. | 62 | | -db ignore | Ignores the clean hash database when dumping a process this time. All modules will be dumped even if a match is found. | 63 | | -cdb \ | Full filepath to the clean hash database to use for this run if you'd like to override the default of `clean.hashes`. | 64 | | -edb \ | Full filepath to the entrypoint hash database to use for this run. | 65 | | -esdb \ | Full filepath to the entrypoint short hash database to use for this run. | 66 | 67 | **Output Options** 68 | 69 | | Option | Description | 70 | |--------|-------------| 71 | | -v | Verbose mode where more details will be printed for debugging. | 72 | | -nh | No header is printed in the output. | 73 | 74 | **Advanced Options** 75 | 76 | | Option | Description | 77 | |--------|-------------| 78 | | -g | Forces generation of PE headers from scratch, ignoring existing headers. | 79 | | -eprec | Force the entry point to be reconstructed, even if a valid one appears to exist. | 80 | | -ni | Disable import reconstruction. | 81 | | -nc | Disable dumping of loose code regions. | 82 | | -nt | Disable multithreading. | 83 | | -nep | Disable entry point hashing. | 84 | | -t \ | Sets the number of threads to use (default 16). | 85 | 86 | # Usage Examples 87 | 88 | | Command | Description | 89 | | ------- | ----------- | 90 | | `pd64.exe -db genquick` | Quickly build clean module database based on currently running processes. Process Dump in later tasks will only dump unrecognized modules. | 91 | | `pd64.exe -system` | Dump all modules and hidden chunks from all processes while ignoring clean modules. | 92 | | `pd64.exe -closemon` | Run in terminate monitor mode. This will dump all processes when they attempt to terminate. | 93 | | `pd64.exe -pid 0x18A` | Dump modules and hidden chunks from a specific process ID. | 94 | | `pd64.exe -p .\*chrome.\*` | Dump modules and hidden chunks by process name. | 95 | | `pd64.exe -db gen` | Build a clean-hash database of known modules. This is used to avoid dumping known good modules in later tasks. | 96 | | `pd64.exe -pid 0x1a3 -a 0xffb4000` | Dump code from a specific address in PID. This will generate two files for analysis, with reconstructed 32bit and 64bit PE headers: `notepad_exe_x64_hidden_FFB40000.exe` and `notepad_exe_x86_hidden_FFB40000.exe`. | 97 | 98 | Sure, here's a more streamlined version of the information: 99 | 100 | ## Sandbox Usage 101 | 102 | When using Process Dump in an automated sandbox or for manual anti-malware research, the following steps can be useful. Make sure to run all commands as an Administrator in a clean environment. 103 | 104 | - **Build the Clean Hash Database:** Run `pd64.exe -db gen` or for a faster less complete process, use `pd64.exe -db genquick`. Depending on your situation, you may want to snapshot your VM after creating this clean hash database that way it doesn't need to be repeated each time. 105 | 106 | - **Start the Process Dump Terminate Monitor:** Keep `pd64.exe -closemon` running in the background. It will dump all intermediate processes used by the malware. 107 | 108 | - **Execute the Malware File:** Monitor the malware installation. `pd64.exe` will automatically dump any process that tries to close. 109 | 110 | - **Dump the Running Malware from Memory:** When ready, use `pd64.exe -system` to dump all processes. 111 | 112 | The dumped components will be found in the working directory of `pd64.exe`. To change the output path, use the `-o` flag. 113 | 114 | # Notes on the naming convention of dumped modules: 115 | * 'hiddemodule' in the filename instead of the module name indicates the module was not properly registered in the process. 116 | * 'codechunk' in the filename means that it is a reconstructed dump from a loose executable region. This can be for example injected code that did not have a PE header. Codechunks will be dumped twice, once with a reconstructed x86 and again with a reconstructed x64 header. 117 | 118 | Example filenames of dumped files 119 | * notepad_exe_PID2990_hiddenmodule_16B8ABB0000_x86.dll 120 | * notepad_exe_PID3b5c_notepad.exe_7FF6E6630000_x64.exe 121 | * notepad_exe_PID2c54_codechunk_17BD0000_x86.dll 122 | * notepad_exe_PID2c54_codechunk_17BD0000_x64.dll 123 | 124 | 125 | # Version history 126 | 127 | ## Version 2.1 (February 12th, 2017) 128 | * Fixed a bug where the last section in some cases would instead be filled with zeros. Thanks to megastupidmonkey for reporting this issue. 129 | * Fixed a bug where 64-bit base addresses would be truncated to a 32-bit address. It now properly keeps the full 64-bit module base address. Thanks to megastupidmonkey for reporting this issue. 130 | * Addressed an issue where the processes dump close monitor would crash csrss.exe. 131 | * Stopped Process Dump from hooking it's own process in close monitor mode. 132 | 133 | ## Version 2.0 (September 18th, 2016) 134 | * Added new flag '-closemon' which runs Process Dump in a monitoring 135 | mode. It will pause and dump any process just as it closes. This is designed 136 | to work well with malware analysis sandboxes, to be sure to dump 137 | malware from memory before the malicious process closes. 138 | * Upgraded Process Dump to be multi-threaded. Commands that dump or get 139 | hashes from multiple processes will run separate threads per operation. 140 | Default number of threads is 16, which speeds up the general Process 141 | Dump dumping processing significantly. 142 | * Upgraded Process Dump to dump unattached code chunks found in memory. 143 | These are identified as executable regions in memory which are not 144 | attached to a module and do not have a PE header. It also requires that 145 | the codechunk refer to at least 2 imports to be considered valid in 146 | order to reduce noise. When dumped, a PE header is recreated along with 147 | an import table. Code chunks are fully supported by the clean hash database. 148 | * Added flags to control the filepath to the clean hash database as well 149 | as the output folder for dumped files. 150 | * Fix to generating clean hash database from user path that was causing a 151 | crash. 152 | * Fix to the flag '-g' that forces generation of PE headers. Before even 153 | if this flag was set, system dumps (-system), would ignore this flag 154 | when dumping a process. 155 | * Various performance improvements. 156 | * Upgraded project to VS2015. 157 | 158 | ## Version 1.5 (November 21st, 2015) 159 | * Fixed bug where very large memory regions would cause Process Dump to hang. 160 | * Fixed bug where some modules at high addresses would not be found under 64-bit Windows. 161 | * More debug information now outputted under Verbose mode. 162 | 163 | ## Version 1.4 (April 18th, 2015) 164 | * Added new aggressive import reconstruction approach. Now patches up all DWORDs and QWORDs in the module to the corresponding export match. 165 | * Added '-a (address to dump)' flag to dump a specific address. It will generate PE headers and build an import table for the address. 166 | * Added '-ni' flag to skip new import reconstruction algorithm. 167 | * Added '-g' flag to force generation of new PE header even if there exists one when dumping a module. This is good if the PE header is malformed for example. 168 | * Various bug fixes. 169 | 170 | ## Version 1.3 (October 10th, 2013) 171 | * Improved handling of PE headers with sections that specify invalid virtual sizes and addresses. 172 | * Better module dumping methodology for dumping virtual sections down to disk sections. 173 | 174 | ## Version 1.1 (April 8th, 2013) 175 | * Fixed a compatibility issue with Windows XP. 176 | * Corrected bug where process dump would print it is dumping a module but not actually dump it. 177 | * Implemented the '-pid ' dump flag. 178 | 179 | ## Version 1.0 (April 2nd, 2013) 180 | * Initial release. 181 | -------------------------------------------------------------------------------- /pd.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.32002.261 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pd", "pd\pd.vcxproj", "{49B0E81A-D38E-426E-AB76-CC8463EC0003}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Debug|x64 = Debug|x64 12 | Release|Win32 = Release|Win32 13 | Release|x64 = Release|x64 14 | x64|Win32 = x64|Win32 15 | x64|x64 = x64|x64 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {49B0E81A-D38E-426E-AB76-CC8463EC0003}.Debug|Win32.ActiveCfg = Debug|Win32 19 | {49B0E81A-D38E-426E-AB76-CC8463EC0003}.Debug|Win32.Build.0 = Debug|Win32 20 | {49B0E81A-D38E-426E-AB76-CC8463EC0003}.Debug|x64.ActiveCfg = Debug|x64 21 | {49B0E81A-D38E-426E-AB76-CC8463EC0003}.Debug|x64.Build.0 = Debug|x64 22 | {49B0E81A-D38E-426E-AB76-CC8463EC0003}.Release|Win32.ActiveCfg = Release|Win32 23 | {49B0E81A-D38E-426E-AB76-CC8463EC0003}.Release|Win32.Build.0 = Release|Win32 24 | {49B0E81A-D38E-426E-AB76-CC8463EC0003}.Release|x64.ActiveCfg = Release|x64 25 | {49B0E81A-D38E-426E-AB76-CC8463EC0003}.Release|x64.Build.0 = Release|x64 26 | {49B0E81A-D38E-426E-AB76-CC8463EC0003}.x64|Win32.ActiveCfg = Debug|Win32 27 | {49B0E81A-D38E-426E-AB76-CC8463EC0003}.x64|Win32.Build.0 = Debug|Win32 28 | {49B0E81A-D38E-426E-AB76-CC8463EC0003}.x64|x64.ActiveCfg = x64|x64 29 | {49B0E81A-D38E-426E-AB76-CC8463EC0003}.x64|x64.Build.0 = x64|x64 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {F0554F35-5E45-4F1C-9AA9-5C93793CC37D} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /pd/DynArray.h: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // DynArray.h: interface&implementation for the DynArray class 3 | ////////////////////////////////////////////////////////////////////// 4 | // From: http://www.anyexample.com/programming/cplusplus/cplusplus_dynamic_array_template_class.xml 5 | 6 | #ifndef _AE_DYNARRAY_H_INCLUDED_ 7 | #define _AE_DYNARRAY_H_INCLUDED_ 8 | 9 | #include 10 | 11 | template 12 | class DynArray 13 | { 14 | public: 15 | DynArray(); // constructor 16 | DynArray(const DynArray &a); // copy constructor 17 | ~DynArray(); // distructor 18 | DynArray& operator = (const DynArray &a); // assignment operator 19 | 20 | el& operator [] (unsigned int index); // get array item 21 | void Add(const el &item); // Add item to the end of array 22 | 23 | unsigned int GetSize(); // get size of array (elements) 24 | void SetSize(unsigned int newsize); // set size of array (elements) 25 | void Clear(); // clear array 26 | void Delete(unsigned int pos); // delete array item 27 | void* getptr(); // get void* pointer to array data 28 | 29 | enum exception { MEMFAIL }; // exception enum 30 | 31 | private: 32 | el *array; // pointer for array's memory 33 | unsigned int size; // size of array (elemets) 34 | unsigned int realsize; // actual size of allocated memory 35 | 36 | const static int dyn_array_step = 128; // initial size of array memory (elements) 37 | const static int dyn_array_mult = 2; // multiplier (enlarge array memory 38 | // dyn_array_mult times ) 39 | }; 40 | 41 | ////////////////////////////////////////////////////////////////////// 42 | 43 | template 44 | DynArray::DynArray() 45 | { 46 | realsize = dyn_array_step; // First, allocate step 47 | // for dyn_array_step items 48 | size = 0; 49 | array = (el *)malloc(realsize*sizeof(el)); 50 | 51 | if (array == NULL) 52 | throw MEMFAIL; 53 | } 54 | 55 | 56 | template 57 | DynArray::~DynArray() 58 | { 59 | if (array) 60 | { 61 | free(array); 62 | array = NULL; 63 | } 64 | } 65 | 66 | 67 | template 68 | DynArray::DynArray(const DynArray &a) 69 | { 70 | array = (el *)malloc(sizeof(el)*a.realsize); 71 | if (array == NULL) 72 | throw MEMFAIL; 73 | 74 | memcpy(array, a.array, sizeof(el)*a.realsize); 75 | realsize = a.realsize; 76 | size = a.size; 77 | } 78 | 79 | 80 | template 81 | DynArray& DynArray::operator = (const DynArray &a) 82 | { 83 | if (this == &a) // in case somebody tries assign array to itself 84 | return *this; 85 | 86 | if (a.size == 0) // is other array is empty -- clear this array 87 | Clear(); 88 | 89 | SetSize(a.size); // set size 90 | 91 | memcpy(array, a.array, sizeof(el)*a.size); 92 | 93 | return *this; 94 | } 95 | 96 | template 97 | unsigned int DynArray::GetSize() 98 | { 99 | return size; // simply return size 100 | } 101 | 102 | 103 | template 104 | void DynArray::SetSize(unsigned int newsize) 105 | { 106 | size = newsize; 107 | 108 | if (size != 0) 109 | { 110 | // change array memory size 111 | // if new size is larger than current 112 | // or new size is less then half of the current 113 | if ((size > realsize) || (size < realsize/2)) 114 | { 115 | realsize = size; 116 | array = (el *)realloc(array, sizeof(el)*size); 117 | 118 | if (array == NULL) 119 | throw MEMFAIL; 120 | } 121 | } 122 | else 123 | Clear(); 124 | } 125 | 126 | template 127 | void DynArray::Delete(unsigned int pos) 128 | { 129 | if (size == 1) // If array has only one element 130 | Clear(); // than we clear it, since it will be deleted 131 | else 132 | { 133 | // otherwise, shift array elements 134 | for(unsigned int i=pos; i 143 | void DynArray::Clear() // clear array memory 144 | { 145 | size = 0; 146 | array = (el *)realloc(array, sizeof(el)*dyn_array_step); 147 | // set initial memory size again 148 | realsize = dyn_array_step; 149 | } 150 | 151 | template 152 | void* DynArray::getptr() 153 | { 154 | return array; // return void* pointer 155 | } 156 | 157 | template 158 | el& DynArray::operator [] (unsigned int index) 159 | { 160 | return array[index]; // return array element 161 | } 162 | 163 | template 164 | void DynArray::Add(const el &item) 165 | { 166 | size++; 167 | 168 | if (size > realsize) 169 | { 170 | realsize *= dyn_array_mult; 171 | 172 | array = (el *)realloc(array, sizeof(el)*realsize); 173 | 174 | if (array == NULL) 175 | throw MEMFAIL; 176 | } 177 | 178 | array[size-1] = item; 179 | } 180 | 181 | #endif // ifndef _AE_DYNARRAY_H_INCLUDED_ -------------------------------------------------------------------------------- /pd/ReadMe.txt: -------------------------------------------------------------------------------- 1 | ======================================================================== 2 | CONSOLE APPLICATION : pd Project Overview 3 | ======================================================================== 4 | 5 | AppWizard has created this pd application for you. 6 | 7 | This file contains a summary of what you will find in each of the files that 8 | make up your pd application. 9 | 10 | 11 | pd.vcproj 12 | This is the main project file for VC++ projects generated using an Application Wizard. 13 | It contains information about the version of Visual C++ that generated the file, and 14 | information about the platforms, configurations, and project features selected with the 15 | Application Wizard. 16 | 17 | pd.cpp 18 | This is the main application source file. 19 | 20 | ///////////////////////////////////////////////////////////////////////////// 21 | Other standard files: 22 | 23 | StdAfx.h, StdAfx.cpp 24 | These files are used to build a precompiled header (PCH) file 25 | named pd.pch and a precompiled types file named StdAfx.obj. 26 | 27 | ///////////////////////////////////////////////////////////////////////////// 28 | Other notes: 29 | 30 | AppWizard uses "TODO:" comments to indicate parts of the source code you 31 | should add to or customize. 32 | 33 | ///////////////////////////////////////////////////////////////////////////// 34 | -------------------------------------------------------------------------------- /pd/close_watcher.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "close_watcher.h" 3 | 4 | 5 | close_watcher::close_watcher(pe_hash_database* clean_db, PD_OPTIONS* options) 6 | { 7 | _clean_db = clean_db; 8 | _options = options; 9 | _monitoring_thread = NULL; 10 | _monitor_request_stop = false; 11 | } 12 | 13 | bool close_watcher::start_monitor() 14 | { 15 | // Create the main monitoring thread 16 | if (_monitoring_thread == NULL) 17 | { 18 | _monitor_request_stop = false; 19 | _monitoring_thread = new thread(&close_watcher::_monitor_dump_on_close, this); 20 | 21 | printf("Started monitoring for process closes.\n"); 22 | } 23 | return true; 24 | } 25 | 26 | bool close_watcher::stop_monitor() 27 | { 28 | if (_monitoring_thread != NULL) 29 | { 30 | // Cleanly exit the main thread 31 | _monitor_request_stop = true; 32 | _monitoring_thread->join(); 33 | 34 | delete _monitoring_thread; 35 | _monitoring_thread = NULL; 36 | 37 | printf("Stopped monitoring for process closes.\n"); 38 | } 39 | 40 | return true; 41 | } 42 | 43 | 44 | void close_watcher::_monitor_dump_on_close() 45 | { 46 | // List of processes hooked 47 | unordered_set hooked_pids; 48 | unordered_map hooked_processes; 49 | 50 | // Create our threads that process the dumping of processes as they close 51 | thread** threads = new thread*[_options->NumberOfThreads]; 52 | 53 | for (int i = 0; i < _options->NumberOfThreads; i++) 54 | { 55 | threads[i] = new thread(&close_watcher::_dump_process_worker_and_close, this); 56 | } 57 | 58 | // Hook all processes terminates 59 | PROCESSENTRY32 entry; 60 | entry.dwSize = sizeof(PROCESSENTRY32); 61 | HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); 62 | DWORD myPid = GetCurrentProcessId(); 63 | 64 | while (!_monitor_request_stop) 65 | { 66 | // Keep hooking any new processes 67 | snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); 68 | if (snapshot != INVALID_HANDLE_VALUE) 69 | { 70 | if (Process32First(snapshot, &entry) == TRUE) 71 | { 72 | while (Process32Next(snapshot, &entry) == TRUE) 73 | { 74 | if ( myPid != entry.th32ProcessID && hooked_pids.count(entry.th32ProcessID) == 0) 75 | { 76 | if (_wcsicmp(entry.szExeFile, L"csrss.exe") != 0) // TEMPORARY FIX TO ISSUE #10 CRASHING CSRSS.EXE 77 | { 78 | // Test code to only hook notepad.exe 79 | //if (_wcsicmp(entry.szExeFile, L"notepad.exe") == 0) 80 | //{ 81 | // New process 82 | dump_process* dumper = new dump_process(entry.th32ProcessID, _clean_db, _options, true); 83 | if (dumper->monitor_close_start()) 84 | { 85 | printf("...hooked close of: pid 0x%x,%S\n", entry.th32ProcessID, entry.szExeFile); 86 | hooked_processes.insert(std::pair(dumper->get_pid(), dumper)); 87 | hooked_pids.insert(dumper->get_pid()); 88 | } 89 | else 90 | delete dumper; 91 | //} 92 | } 93 | } 94 | } 95 | } 96 | CloseHandle(snapshot); 97 | } 98 | 99 | // Check if any processes are waiting to close 100 | for (unordered_map::iterator it = hooked_processes.begin(); it != hooked_processes.end(); ) 101 | { 102 | if (it->second->monitor_close_is_waiting()) 103 | { 104 | 105 | // Dump this process by adding it to the multi-threaded dumping queue 106 | char name[0x200]; 107 | it->second->get_process_name(name, sizeof(name)); 108 | printf("Process %s requesting to close, we are dumping it...\n", name); 109 | _work_queue.push(it->second); // Will be freed when it gets processed from work queue 110 | 111 | // Remove this process 112 | it = hooked_processes.erase(it); 113 | } 114 | else 115 | { 116 | it++; 117 | } 118 | } 119 | 120 | Sleep(10); 121 | } 122 | 123 | // Wait for the work queue to finish processing 124 | while (!_work_queue.empty()) 125 | { 126 | printf("waiting for dump commands to be pulled from work queue...\n"); 127 | Sleep(200); 128 | } 129 | 130 | // Wait for all worker threads to complete 131 | for (int i = 0; i < _options->NumberOfThreads; i++) 132 | { 133 | threads[i]->join(); // blocks until each thread is finished 134 | delete threads[i]; 135 | threads[i] = NULL; 136 | } 137 | delete[]threads; 138 | 139 | // Clean up list of processes hooked 140 | for (unordered_map::iterator it = hooked_processes.begin(); it != hooked_processes.end(); ++it) 141 | { 142 | delete it->second; // cleans up the hook in the destructor 143 | } 144 | } 145 | 146 | 147 | void close_watcher::_dump_process_worker_and_close() 148 | { 149 | // Dump this process 150 | unordered_set new_hashes; 151 | while (!_monitor_request_stop || !_work_queue.empty()) 152 | { 153 | // Process the hashes for this process 154 | dump_process* entry; 155 | if (_work_queue.pop(entry)) 156 | { 157 | // Process this process 158 | 159 | // Dump this process 160 | entry->monitor_close_dump_and_resume(); 161 | 162 | // We're done with the process 163 | delete entry; 164 | } 165 | 166 | Sleep(10); 167 | } 168 | } 169 | 170 | 171 | close_watcher::~close_watcher() 172 | { 173 | stop_monitor(); 174 | } 175 | -------------------------------------------------------------------------------- /pd/close_watcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "work_queue.h" 3 | #include 4 | #include "pe_hash_database.h" 5 | #include "dump_process.h" 6 | #include "windows.h" 7 | 8 | class close_watcher 9 | { 10 | pe_hash_database* _clean_db; 11 | PD_OPTIONS* _options; 12 | 13 | Queue _work_queue; 14 | 15 | thread* _monitoring_thread; 16 | bool _monitor_request_stop; 17 | 18 | void _monitor_dump_on_close(); 19 | void _dump_process_worker_and_close(); 20 | 21 | public: 22 | close_watcher(pe_hash_database* clean_db, PD_OPTIONS* options); 23 | bool start_monitor(); 24 | bool stop_monitor(); 25 | ~close_watcher(); 26 | }; 27 | 28 | -------------------------------------------------------------------------------- /pd/dirent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Dirent interface for Microsoft Visual Studio 5 | * Version 1.21 6 | * 7 | * Copyright (C) 2006-2012 Toni Ronkko 8 | * This file is part of dirent. Dirent may be freely distributed 9 | * under the MIT license. For all details and documentation, see 10 | * https://github.com/tronkko/dirent 11 | */ 12 | #ifndef DIRENT_H 13 | #define DIRENT_H 14 | 15 | /* 16 | * Include windows.h without Windows Sockets 1.1 to prevent conflicts with 17 | * Windows Sockets 2.0. 18 | */ 19 | #ifndef WIN32_LEAN_AND_MEAN 20 | # define WIN32_LEAN_AND_MEAN 21 | #endif 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | /* Indicates that d_type field is available in dirent structure */ 35 | #define _DIRENT_HAVE_D_TYPE 36 | 37 | /* Indicates that d_namlen field is available in dirent structure */ 38 | #define _DIRENT_HAVE_D_NAMLEN 39 | 40 | /* Entries missing from MSVC 6.0 */ 41 | #if !defined(FILE_ATTRIBUTE_DEVICE) 42 | # define FILE_ATTRIBUTE_DEVICE 0x40 43 | #endif 44 | 45 | /* File type and permission flags for stat(), general mask */ 46 | #if !defined(S_IFMT) 47 | # define S_IFMT _S_IFMT 48 | #endif 49 | 50 | /* Directory bit */ 51 | #if !defined(S_IFDIR) 52 | # define S_IFDIR _S_IFDIR 53 | #endif 54 | 55 | /* Character device bit */ 56 | #if !defined(S_IFCHR) 57 | # define S_IFCHR _S_IFCHR 58 | #endif 59 | 60 | /* Pipe bit */ 61 | #if !defined(S_IFFIFO) 62 | # define S_IFFIFO _S_IFFIFO 63 | #endif 64 | 65 | /* Regular file bit */ 66 | #if !defined(S_IFREG) 67 | # define S_IFREG _S_IFREG 68 | #endif 69 | 70 | /* Read permission */ 71 | #if !defined(S_IREAD) 72 | # define S_IREAD _S_IREAD 73 | #endif 74 | 75 | /* Write permission */ 76 | #if !defined(S_IWRITE) 77 | # define S_IWRITE _S_IWRITE 78 | #endif 79 | 80 | /* Execute permission */ 81 | #if !defined(S_IEXEC) 82 | # define S_IEXEC _S_IEXEC 83 | #endif 84 | 85 | /* Pipe */ 86 | #if !defined(S_IFIFO) 87 | # define S_IFIFO _S_IFIFO 88 | #endif 89 | 90 | /* Block device */ 91 | #if !defined(S_IFBLK) 92 | # define S_IFBLK 0 93 | #endif 94 | 95 | /* Link */ 96 | #if !defined(S_IFLNK) 97 | # define S_IFLNK 0 98 | #endif 99 | 100 | /* Socket */ 101 | #if !defined(S_IFSOCK) 102 | # define S_IFSOCK 0 103 | #endif 104 | 105 | /* Read user permission */ 106 | #if !defined(S_IRUSR) 107 | # define S_IRUSR S_IREAD 108 | #endif 109 | 110 | /* Write user permission */ 111 | #if !defined(S_IWUSR) 112 | # define S_IWUSR S_IWRITE 113 | #endif 114 | 115 | /* Execute user permission */ 116 | #if !defined(S_IXUSR) 117 | # define S_IXUSR 0 118 | #endif 119 | 120 | /* Read group permission */ 121 | #if !defined(S_IRGRP) 122 | # define S_IRGRP 0 123 | #endif 124 | 125 | /* Write group permission */ 126 | #if !defined(S_IWGRP) 127 | # define S_IWGRP 0 128 | #endif 129 | 130 | /* Execute group permission */ 131 | #if !defined(S_IXGRP) 132 | # define S_IXGRP 0 133 | #endif 134 | 135 | /* Read others permission */ 136 | #if !defined(S_IROTH) 137 | # define S_IROTH 0 138 | #endif 139 | 140 | /* Write others permission */ 141 | #if !defined(S_IWOTH) 142 | # define S_IWOTH 0 143 | #endif 144 | 145 | /* Execute others permission */ 146 | #if !defined(S_IXOTH) 147 | # define S_IXOTH 0 148 | #endif 149 | 150 | /* Maximum length of file name */ 151 | #if !defined(PATH_MAX) 152 | # define PATH_MAX MAX_PATH 153 | #endif 154 | #if !defined(FILENAME_MAX) 155 | # define FILENAME_MAX MAX_PATH 156 | #endif 157 | #if !defined(NAME_MAX) 158 | # define NAME_MAX FILENAME_MAX 159 | #endif 160 | 161 | /* File type flags for d_type */ 162 | #define DT_UNKNOWN 0 163 | #define DT_REG S_IFREG 164 | #define DT_DIR S_IFDIR 165 | #define DT_FIFO S_IFIFO 166 | #define DT_SOCK S_IFSOCK 167 | #define DT_CHR S_IFCHR 168 | #define DT_BLK S_IFBLK 169 | #define DT_LNK S_IFLNK 170 | 171 | /* Macros for converting between st_mode and d_type */ 172 | #define IFTODT(mode) ((mode) & S_IFMT) 173 | #define DTTOIF(type) (type) 174 | 175 | /* 176 | * File type macros. Note that block devices, sockets and links cannot be 177 | * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are 178 | * only defined for compatibility. These macros should always return false 179 | * on Windows. 180 | */ 181 | #if !defined(S_ISFIFO) 182 | # define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) 183 | #endif 184 | #if !defined(S_ISDIR) 185 | # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) 186 | #endif 187 | #if !defined(S_ISREG) 188 | # define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) 189 | #endif 190 | #if !defined(S_ISLNK) 191 | # define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) 192 | #endif 193 | #if !defined(S_ISSOCK) 194 | # define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) 195 | #endif 196 | #if !defined(S_ISCHR) 197 | # define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) 198 | #endif 199 | #if !defined(S_ISBLK) 200 | # define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) 201 | #endif 202 | 203 | /* Return the exact length of d_namlen without zero terminator */ 204 | #define _D_EXACT_NAMLEN(p) ((p)->d_namlen) 205 | 206 | /* Return number of bytes needed to store d_namlen */ 207 | #define _D_ALLOC_NAMLEN(p) (PATH_MAX) 208 | 209 | 210 | #ifdef __cplusplus 211 | extern "C" { 212 | #endif 213 | 214 | 215 | /* Wide-character version */ 216 | struct _wdirent { 217 | /* Always zero */ 218 | long d_ino; 219 | 220 | /* Structure size */ 221 | unsigned short d_reclen; 222 | 223 | /* Length of name without \0 */ 224 | size_t d_namlen; 225 | 226 | /* File type */ 227 | int d_type; 228 | 229 | /* File name */ 230 | wchar_t d_name[PATH_MAX]; 231 | }; 232 | typedef struct _wdirent _wdirent; 233 | 234 | struct _WDIR { 235 | /* Current directory entry */ 236 | struct _wdirent ent; 237 | 238 | /* Private file data */ 239 | WIN32_FIND_DATAW data; 240 | 241 | /* True if data is valid */ 242 | int cached; 243 | 244 | /* Win32 search handle */ 245 | HANDLE handle; 246 | 247 | /* Initial directory name */ 248 | wchar_t *patt; 249 | }; 250 | typedef struct _WDIR _WDIR; 251 | 252 | static _WDIR *_wopendir(const wchar_t *dirname); 253 | static struct _wdirent *_wreaddir(_WDIR *dirp); 254 | static int _wclosedir(_WDIR *dirp); 255 | static void _wrewinddir(_WDIR* dirp); 256 | 257 | 258 | /* For compatibility with Symbian */ 259 | #define wdirent _wdirent 260 | #define WDIR _WDIR 261 | #define wopendir _wopendir 262 | #define wreaddir _wreaddir 263 | #define wclosedir _wclosedir 264 | #define wrewinddir _wrewinddir 265 | 266 | 267 | /* Multi-byte character versions */ 268 | struct dirent { 269 | /* Always zero */ 270 | long d_ino; 271 | 272 | /* Structure size */ 273 | unsigned short d_reclen; 274 | 275 | /* Length of name without \0 */ 276 | size_t d_namlen; 277 | 278 | /* File type */ 279 | int d_type; 280 | 281 | /* File name */ 282 | char d_name[PATH_MAX]; 283 | }; 284 | typedef struct dirent dirent; 285 | 286 | struct DIR { 287 | struct dirent ent; 288 | struct _WDIR *wdirp; 289 | }; 290 | typedef struct DIR DIR; 291 | 292 | static DIR *opendir(const char *dirname); 293 | static struct dirent *readdir(DIR *dirp); 294 | static int closedir(DIR *dirp); 295 | static void rewinddir(DIR* dirp); 296 | 297 | 298 | /* Internal utility functions */ 299 | static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp); 300 | static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp); 301 | 302 | static int dirent_mbstowcs_s( 303 | size_t *pReturnValue, 304 | wchar_t *wcstr, 305 | size_t sizeInWords, 306 | const char *mbstr, 307 | size_t count); 308 | 309 | static int dirent_wcstombs_s( 310 | size_t *pReturnValue, 311 | char *mbstr, 312 | size_t sizeInBytes, 313 | const wchar_t *wcstr, 314 | size_t count); 315 | 316 | static void dirent_set_errno(int error); 317 | 318 | /* 319 | * Open directory stream DIRNAME for read and return a pointer to the 320 | * internal working area that is used to retrieve individual directory 321 | * entries. 322 | */ 323 | static _WDIR* 324 | _wopendir( 325 | const wchar_t *dirname) 326 | { 327 | _WDIR *dirp = NULL; 328 | int error; 329 | 330 | /* Must have directory name */ 331 | if (dirname == NULL || dirname[0] == '\0') { 332 | dirent_set_errno(ENOENT); 333 | return NULL; 334 | } 335 | 336 | /* Allocate new _WDIR structure */ 337 | dirp = (_WDIR*)malloc(sizeof(struct _WDIR)); 338 | if (dirp != NULL) { 339 | DWORD n; 340 | 341 | /* Reset _WDIR structure */ 342 | dirp->handle = INVALID_HANDLE_VALUE; 343 | dirp->patt = NULL; 344 | dirp->cached = 0; 345 | 346 | /* Compute the length of full path plus zero terminator 347 | * 348 | * Note that on WinRT there's no way to convert relative paths 349 | * into absolute paths, so just assume its an absolute path. 350 | */ 351 | # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) 352 | n = wcslen(dirname); 353 | # else 354 | n = GetFullPathNameW(dirname, 0, NULL, NULL); 355 | # endif 356 | 357 | /* Allocate room for absolute directory name and search pattern */ 358 | dirp->patt = (wchar_t*)malloc(sizeof(wchar_t) * n + 16); 359 | if (dirp->patt) { 360 | 361 | /* 362 | * Convert relative directory name to an absolute one. This 363 | * allows rewinddir() to function correctly even when current 364 | * working directory is changed between opendir() and rewinddir(). 365 | * 366 | * Note that on WinRT there's no way to convert relative paths 367 | * into absolute paths, so just assume its an absolute path. 368 | */ 369 | # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) 370 | wcsncpy_s(dirp->patt, n + 1, dirname, n); 371 | # else 372 | n = GetFullPathNameW(dirname, n, dirp->patt, NULL); 373 | # endif 374 | if (n > 0) { 375 | wchar_t *p; 376 | 377 | /* Append search pattern \* to the directory name */ 378 | p = dirp->patt + n; 379 | if (dirp->patt < p) { 380 | switch (p[-1]) { 381 | case '\\': 382 | case '/': 383 | case ':': 384 | /* Directory ends in path separator, e.g. c:\temp\ */ 385 | /*NOP*/; 386 | break; 387 | 388 | default: 389 | /* Directory name doesn't end in path separator */ 390 | *p++ = '\\'; 391 | } 392 | } 393 | *p++ = '*'; 394 | *p = '\0'; 395 | 396 | /* Open directory stream and retrieve the first entry */ 397 | if (dirent_first(dirp)) { 398 | /* Directory stream opened successfully */ 399 | error = 0; 400 | } 401 | else { 402 | /* Cannot retrieve first entry */ 403 | error = 1; 404 | dirent_set_errno(ENOENT); 405 | } 406 | 407 | } 408 | else { 409 | /* Cannot retrieve full path name */ 410 | dirent_set_errno(ENOENT); 411 | error = 1; 412 | } 413 | 414 | } 415 | else { 416 | /* Cannot allocate memory for search pattern */ 417 | error = 1; 418 | } 419 | 420 | } 421 | else { 422 | /* Cannot allocate _WDIR structure */ 423 | error = 1; 424 | } 425 | 426 | /* Clean up in case of error */ 427 | if (error && dirp) { 428 | _wclosedir(dirp); 429 | dirp = NULL; 430 | } 431 | 432 | return dirp; 433 | } 434 | 435 | /* 436 | * Read next directory entry. The directory entry is returned in dirent 437 | * structure in the d_name field. Individual directory entries returned by 438 | * this function include regular files, sub-directories, pseudo-directories 439 | * "." and ".." as well as volume labels, hidden files and system files. 440 | */ 441 | static struct _wdirent* 442 | _wreaddir( 443 | _WDIR *dirp) 444 | { 445 | WIN32_FIND_DATAW *datap; 446 | struct _wdirent *entp; 447 | 448 | /* Read next directory entry */ 449 | datap = dirent_next(dirp); 450 | if (datap) { 451 | size_t n; 452 | DWORD attr; 453 | 454 | /* Pointer to directory entry to return */ 455 | entp = &dirp->ent; 456 | 457 | /* 458 | * Copy file name as wide-character string. If the file name is too 459 | * long to fit in to the destination buffer, then truncate file name 460 | * to PATH_MAX characters and zero-terminate the buffer. 461 | */ 462 | n = 0; 463 | while (n + 1 < PATH_MAX && datap->cFileName[n] != 0) { 464 | entp->d_name[n] = datap->cFileName[n]; 465 | n++; 466 | } 467 | dirp->ent.d_name[n] = 0; 468 | 469 | /* Length of file name excluding zero terminator */ 470 | entp->d_namlen = n; 471 | 472 | /* File type */ 473 | attr = datap->dwFileAttributes; 474 | if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { 475 | entp->d_type = DT_CHR; 476 | } 477 | else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { 478 | entp->d_type = DT_DIR; 479 | } 480 | else { 481 | entp->d_type = DT_REG; 482 | } 483 | 484 | /* Reset dummy fields */ 485 | entp->d_ino = 0; 486 | entp->d_reclen = sizeof(struct _wdirent); 487 | 488 | } 489 | else { 490 | 491 | /* Last directory entry read */ 492 | entp = NULL; 493 | 494 | } 495 | 496 | return entp; 497 | } 498 | 499 | /* 500 | * Close directory stream opened by opendir() function. This invalidates the 501 | * DIR structure as well as any directory entry read previously by 502 | * _wreaddir(). 503 | */ 504 | static int 505 | _wclosedir( 506 | _WDIR *dirp) 507 | { 508 | int ok; 509 | if (dirp) { 510 | 511 | /* Release search handle */ 512 | if (dirp->handle != INVALID_HANDLE_VALUE) { 513 | FindClose(dirp->handle); 514 | dirp->handle = INVALID_HANDLE_VALUE; 515 | } 516 | 517 | /* Release search pattern */ 518 | if (dirp->patt) { 519 | free(dirp->patt); 520 | dirp->patt = NULL; 521 | } 522 | 523 | /* Release directory structure */ 524 | free(dirp); 525 | ok = /*success*/0; 526 | 527 | } 528 | else { 529 | /* Invalid directory stream */ 530 | dirent_set_errno(EBADF); 531 | ok = /*failure*/-1; 532 | } 533 | return ok; 534 | } 535 | 536 | /* 537 | * Rewind directory stream such that _wreaddir() returns the very first 538 | * file name again. 539 | */ 540 | static void 541 | _wrewinddir( 542 | _WDIR* dirp) 543 | { 544 | if (dirp) { 545 | /* Release existing search handle */ 546 | if (dirp->handle != INVALID_HANDLE_VALUE) { 547 | FindClose(dirp->handle); 548 | } 549 | 550 | /* Open new search handle */ 551 | dirent_first(dirp); 552 | } 553 | } 554 | 555 | /* Get first directory entry (internal) */ 556 | static WIN32_FIND_DATAW* 557 | dirent_first( 558 | _WDIR *dirp) 559 | { 560 | WIN32_FIND_DATAW *datap; 561 | 562 | /* Open directory and retrieve the first entry */ 563 | dirp->handle = FindFirstFileExW( 564 | dirp->patt, FindExInfoStandard, &dirp->data, 565 | FindExSearchNameMatch, NULL, 0); 566 | if (dirp->handle != INVALID_HANDLE_VALUE) { 567 | 568 | /* a directory entry is now waiting in memory */ 569 | datap = &dirp->data; 570 | dirp->cached = 1; 571 | 572 | } 573 | else { 574 | 575 | /* Failed to re-open directory: no directory entry in memory */ 576 | dirp->cached = 0; 577 | datap = NULL; 578 | 579 | } 580 | return datap; 581 | } 582 | 583 | /* Get next directory entry (internal) */ 584 | static WIN32_FIND_DATAW* 585 | dirent_next( 586 | _WDIR *dirp) 587 | { 588 | WIN32_FIND_DATAW *p; 589 | 590 | /* Get next directory entry */ 591 | if (dirp->cached != 0) { 592 | 593 | /* A valid directory entry already in memory */ 594 | p = &dirp->data; 595 | dirp->cached = 0; 596 | 597 | } 598 | else if (dirp->handle != INVALID_HANDLE_VALUE) { 599 | 600 | /* Get the next directory entry from stream */ 601 | if (FindNextFileW(dirp->handle, &dirp->data) != FALSE) { 602 | /* Got a file */ 603 | p = &dirp->data; 604 | } 605 | else { 606 | /* The very last entry has been processed or an error occured */ 607 | FindClose(dirp->handle); 608 | dirp->handle = INVALID_HANDLE_VALUE; 609 | p = NULL; 610 | } 611 | 612 | } 613 | else { 614 | 615 | /* End of directory stream reached */ 616 | p = NULL; 617 | 618 | } 619 | 620 | return p; 621 | } 622 | 623 | /* 624 | * Open directory stream using plain old C-string. 625 | */ 626 | static DIR* 627 | opendir( 628 | const char *dirname) 629 | { 630 | struct DIR *dirp; 631 | int error; 632 | 633 | /* Must have directory name */ 634 | if (dirname == NULL || dirname[0] == '\0') { 635 | dirent_set_errno(ENOENT); 636 | return NULL; 637 | } 638 | 639 | /* Allocate memory for DIR structure */ 640 | dirp = (DIR*)malloc(sizeof(struct DIR)); 641 | if (dirp) { 642 | wchar_t wname[PATH_MAX]; 643 | size_t n; 644 | 645 | /* Convert directory name to wide-character string */ 646 | error = dirent_mbstowcs_s(&n, wname, PATH_MAX, dirname, PATH_MAX); 647 | if (!error) { 648 | 649 | /* Open directory stream using wide-character name */ 650 | dirp->wdirp = _wopendir(wname); 651 | if (dirp->wdirp) { 652 | /* Directory stream opened */ 653 | error = 0; 654 | } 655 | else { 656 | /* Failed to open directory stream */ 657 | error = 1; 658 | } 659 | 660 | } 661 | else { 662 | /* 663 | * Cannot convert file name to wide-character string. This 664 | * occurs if the string contains invalid multi-byte sequences or 665 | * the output buffer is too small to contain the resulting 666 | * string. 667 | */ 668 | error = 1; 669 | } 670 | 671 | } 672 | else { 673 | /* Cannot allocate DIR structure */ 674 | error = 1; 675 | } 676 | 677 | /* Clean up in case of error */ 678 | if (error && dirp) { 679 | free(dirp); 680 | dirp = NULL; 681 | } 682 | 683 | return dirp; 684 | } 685 | 686 | /* 687 | * Read next directory entry. 688 | * 689 | * When working with text consoles, please note that file names returned by 690 | * readdir() are represented in the default ANSI code page while any output to 691 | * console is typically formatted on another code page. Thus, non-ASCII 692 | * characters in file names will not usually display correctly on console. The 693 | * problem can be fixed in two ways: (1) change the character set of console 694 | * to 1252 using chcp utility and use Lucida Console font, or (2) use 695 | * _cprintf function when writing to console. The _cprinf() will re-encode 696 | * ANSI strings to the console code page so many non-ASCII characters will 697 | * display correcly. 698 | */ 699 | static struct dirent* 700 | readdir( 701 | DIR *dirp) 702 | { 703 | WIN32_FIND_DATAW *datap; 704 | struct dirent *entp; 705 | 706 | /* Read next directory entry */ 707 | datap = dirent_next(dirp->wdirp); 708 | if (datap) { 709 | size_t n; 710 | int error; 711 | 712 | /* Attempt to convert file name to multi-byte string */ 713 | error = dirent_wcstombs_s( 714 | &n, dirp->ent.d_name, PATH_MAX, datap->cFileName, PATH_MAX); 715 | 716 | /* 717 | * If the file name cannot be represented by a multi-byte string, 718 | * then attempt to use old 8+3 file name. This allows traditional 719 | * Unix-code to access some file names despite of unicode 720 | * characters, although file names may seem unfamiliar to the user. 721 | * 722 | * Be ware that the code below cannot come up with a short file 723 | * name unless the file system provides one. At least 724 | * VirtualBox shared folders fail to do this. 725 | */ 726 | if (error && datap->cAlternateFileName[0] != '\0') { 727 | error = dirent_wcstombs_s( 728 | &n, dirp->ent.d_name, PATH_MAX, 729 | datap->cAlternateFileName, PATH_MAX); 730 | } 731 | 732 | if (!error) { 733 | DWORD attr; 734 | 735 | /* Initialize directory entry for return */ 736 | entp = &dirp->ent; 737 | 738 | /* Length of file name excluding zero terminator */ 739 | entp->d_namlen = n - 1; 740 | 741 | /* File attributes */ 742 | attr = datap->dwFileAttributes; 743 | if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { 744 | entp->d_type = DT_CHR; 745 | } 746 | else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { 747 | entp->d_type = DT_DIR; 748 | } 749 | else { 750 | entp->d_type = DT_REG; 751 | } 752 | 753 | /* Reset dummy fields */ 754 | entp->d_ino = 0; 755 | entp->d_reclen = sizeof(struct dirent); 756 | 757 | } 758 | else { 759 | /* 760 | * Cannot convert file name to multi-byte string so construct 761 | * an errornous directory entry and return that. Note that 762 | * we cannot return NULL as that would stop the processing 763 | * of directory entries completely. 764 | */ 765 | entp = &dirp->ent; 766 | entp->d_name[0] = '?'; 767 | entp->d_name[1] = '\0'; 768 | entp->d_namlen = 1; 769 | entp->d_type = DT_UNKNOWN; 770 | entp->d_ino = 0; 771 | entp->d_reclen = 0; 772 | } 773 | 774 | } 775 | else { 776 | /* No more directory entries */ 777 | entp = NULL; 778 | } 779 | 780 | return entp; 781 | } 782 | 783 | /* 784 | * Close directory stream. 785 | */ 786 | static int 787 | closedir( 788 | DIR *dirp) 789 | { 790 | int ok; 791 | if (dirp) { 792 | 793 | /* Close wide-character directory stream */ 794 | ok = _wclosedir(dirp->wdirp); 795 | dirp->wdirp = NULL; 796 | 797 | /* Release multi-byte character version */ 798 | free(dirp); 799 | 800 | } 801 | else { 802 | 803 | /* Invalid directory stream */ 804 | dirent_set_errno(EBADF); 805 | ok = /*failure*/-1; 806 | 807 | } 808 | return ok; 809 | } 810 | 811 | /* 812 | * Rewind directory stream to beginning. 813 | */ 814 | static void 815 | rewinddir( 816 | DIR* dirp) 817 | { 818 | /* Rewind wide-character string directory stream */ 819 | _wrewinddir(dirp->wdirp); 820 | } 821 | 822 | /* Convert multi-byte string to wide character string */ 823 | static int 824 | dirent_mbstowcs_s( 825 | size_t *pReturnValue, 826 | wchar_t *wcstr, 827 | size_t sizeInWords, 828 | const char *mbstr, 829 | size_t count) 830 | { 831 | int error; 832 | 833 | #if defined(_MSC_VER) && _MSC_VER >= 1400 834 | 835 | /* Microsoft Visual Studio 2005 or later */ 836 | error = mbstowcs_s(pReturnValue, wcstr, sizeInWords, mbstr, count); 837 | 838 | #else 839 | 840 | /* Older Visual Studio or non-Microsoft compiler */ 841 | size_t n; 842 | 843 | /* Convert to wide-character string (or count characters) */ 844 | n = mbstowcs(wcstr, mbstr, sizeInWords); 845 | if (!wcstr || n < count) { 846 | 847 | /* Zero-terminate output buffer */ 848 | if (wcstr && sizeInWords) { 849 | if (n >= sizeInWords) { 850 | n = sizeInWords - 1; 851 | } 852 | wcstr[n] = 0; 853 | } 854 | 855 | /* Length of resuting multi-byte string WITH zero terminator */ 856 | if (pReturnValue) { 857 | *pReturnValue = n + 1; 858 | } 859 | 860 | /* Success */ 861 | error = 0; 862 | 863 | } 864 | else { 865 | 866 | /* Could not convert string */ 867 | error = 1; 868 | 869 | } 870 | 871 | #endif 872 | 873 | return error; 874 | } 875 | 876 | /* Convert wide-character string to multi-byte string */ 877 | static int 878 | dirent_wcstombs_s( 879 | size_t *pReturnValue, 880 | char *mbstr, 881 | size_t sizeInBytes, /* max size of mbstr */ 882 | const wchar_t *wcstr, 883 | size_t count) 884 | { 885 | int error; 886 | 887 | #if defined(_MSC_VER) && _MSC_VER >= 1400 888 | 889 | /* Microsoft Visual Studio 2005 or later */ 890 | error = wcstombs_s(pReturnValue, mbstr, sizeInBytes, wcstr, count); 891 | 892 | #else 893 | 894 | /* Older Visual Studio or non-Microsoft compiler */ 895 | size_t n; 896 | 897 | /* Convert to multi-byte string (or count the number of bytes needed) */ 898 | n = wcstombs(mbstr, wcstr, sizeInBytes); 899 | if (!mbstr || n < count) { 900 | 901 | /* Zero-terminate output buffer */ 902 | if (mbstr && sizeInBytes) { 903 | if (n >= sizeInBytes) { 904 | n = sizeInBytes - 1; 905 | } 906 | mbstr[n] = '\0'; 907 | } 908 | 909 | /* Length of resulting multi-bytes string WITH zero-terminator */ 910 | if (pReturnValue) { 911 | *pReturnValue = n + 1; 912 | } 913 | 914 | /* Success */ 915 | error = 0; 916 | 917 | } 918 | else { 919 | 920 | /* Cannot convert string */ 921 | error = 1; 922 | 923 | } 924 | 925 | #endif 926 | 927 | return error; 928 | } 929 | 930 | /* Set errno variable */ 931 | static void 932 | dirent_set_errno( 933 | int error) 934 | { 935 | #if defined(_MSC_VER) && _MSC_VER >= 1400 936 | 937 | /* Microsoft Visual Studio 2005 and later */ 938 | _set_errno(error); 939 | 940 | #else 941 | 942 | /* Non-Microsoft compiler or older Microsoft compiler */ 943 | errno = error; 944 | 945 | #endif 946 | } 947 | 948 | 949 | #ifdef __cplusplus 950 | } 951 | #endif 952 | #endif /*DIRENT_H*/ 953 | -------------------------------------------------------------------------------- /pd/dump_process.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // content 4 | #include "pe_header.h" 5 | #include "pe_hash_database.h" 6 | #include "windows.h" 7 | #include 8 | #include "simple.h" 9 | #include "module_list.h" 10 | #include "DynArray.h" 11 | #include "export_list.h" 12 | #include "hash.h" 13 | #include 14 | #include "terminate_monitor_hook.h" 15 | 16 | #define PAGE_SIZE 0x1000 17 | #define CODECHUNK_HEADER_HASH_SIZE 0x200 // First X bytes are CRC32'd of each loose code chunk. Only unique CRC32s are processed deeply. 18 | #define CODECHUNK_NEW_HASH_LIMIT 500 // At most X code chunks processed deeply per process 19 | 20 | using namespace std; 21 | using namespace std::tr1; 22 | 23 | 24 | struct MBI_BASIC_INFO 25 | { 26 | __int64 base; 27 | __int64 end; 28 | DWORD protect; 29 | bool valid; 30 | bool executable; 31 | }; 32 | 33 | class dump_process 34 | { 35 | pe_hash_database* _db_clean; 36 | bool _opened; 37 | HANDLE _ph; 38 | DWORD _pid; 39 | char* _process_name; 40 | export_list _export_list; 41 | bool _export_list_built; 42 | PD_OPTIONS* _options; 43 | terminate_monitor_hook* _term_hook; 44 | 45 | unsigned __int64 _address_main_module = NULL; 46 | 47 | bool _loaded_is64; 48 | bool _is64; 49 | bool _quieter; // Suppress some of the error and warning messages 50 | 51 | MBI_BASIC_INFO get_mbi_info(unsigned __int64 address); 52 | 53 | public: 54 | dump_process(DWORD pid, pe_hash_database* db, PD_OPTIONS* options, bool quieter); 55 | void dump_all(); 56 | void dump_region(__int64 base); 57 | void dump_header(pe_header* header, __int64 base, DWORD pid); 58 | DWORD get_pid() { return _pid; }; 59 | bool build_export_list(); 60 | bool build_export_list(export_list* result, char* library, module_list* modules); 61 | int get_all_hashes(unordered_set* output_hashes, unordered_set* output_hashes_eps, unordered_set* output_hashes_ep_shorts); 62 | unsigned __int64 hash_codechunk_header(__int64 base); 63 | bool is64(); 64 | bool get_process_name(char* process_name, SIZE_T byte_length); 65 | 66 | // Functions for dumping processes as they close 67 | bool monitor_close_start(); // start terminate hooks 68 | bool monitor_close_is_waiting(); // check if the process has closed and is waiting dumping 69 | bool monitor_close_dump_and_resume(); // dumps the process if it is waiting dumping 70 | bool monitor_close_stop(); // stop terminate hooks 71 | 72 | ~dump_process(void); 73 | }; -------------------------------------------------------------------------------- /pd/export_list.cpp: -------------------------------------------------------------------------------- 1 | #include "StdAfx.h" 2 | #include "export_list.h" 3 | 4 | 5 | export_entry::export_entry(char* library_name, char* name, WORD ord, unsigned __int64 rva, unsigned __int64 address, bool is64) 6 | { 7 | // Copy the strings locally, knowing the function might not have a name but has to have a library name 8 | if (library_name != NULL) 9 | { 10 | this->library_name = new char[strlen(library_name) + 1]; 11 | strcpy(this->library_name, library_name); 12 | } 13 | else 14 | { 15 | this->library_name = NULL; 16 | } 17 | 18 | if( name != NULL ) 19 | { 20 | this->name = new char[strlen(name) + 1]; 21 | strcpy( this->name, name ); 22 | } 23 | else 24 | { 25 | this->name = NULL; 26 | } 27 | 28 | this->is64 = is64; 29 | this->ord = ord; 30 | this->rva = rva; 31 | this->address = address; 32 | } 33 | 34 | export_entry::export_entry(export_entry* other) 35 | { 36 | // Copy the strings locally, knowing the function might not have a name but has to have a library name 37 | if (other->library_name != NULL) 38 | { 39 | this->library_name = new char[strlen(other->library_name) + 1]; 40 | strcpy(this->library_name, other->library_name); 41 | } 42 | else 43 | { 44 | this->library_name = NULL; 45 | } 46 | 47 | if( other->name != NULL ) 48 | { 49 | this->name = new char[strlen(other->name) + 1]; 50 | strcpy( this->name, other->name ); 51 | } 52 | else 53 | { 54 | this->name = NULL; 55 | } 56 | 57 | this->is64 = other->is64; 58 | this->ord = other->ord; 59 | this->rva = other->rva; 60 | this->address = other->address; 61 | } 62 | 63 | export_entry::~export_entry(void) 64 | { 65 | if( library_name != NULL ) 66 | delete [] library_name; 67 | if( name != NULL ) 68 | delete [] name; 69 | } 70 | 71 | export_list::export_list() 72 | { 73 | _min64 = _UI64_MAX; 74 | _max64 = UINT_MAX; 75 | _min32 = UINT_MAX; 76 | _max32 = 0; 77 | _bits32 = 0; 78 | _bits64 = 0; 79 | } 80 | 81 | bool export_list::contains(unsigned __int64 address) 82 | { 83 | // Look up a 64-bit value 84 | if ( address <= UINT_MAX ) 85 | return contains((unsigned __int32)address); 86 | 87 | if (address > _max64 || address < _min64 || (address & ~_bits64) > 0) 88 | { 89 | // We know there is no match by this quick filtering. This improves performance hugely. 90 | return false; 91 | } 92 | 93 | // Lookup the address 94 | unordered_set::const_iterator got = _addresses.find(address); 95 | if (got != _addresses.end()) 96 | { 97 | return true; 98 | } 99 | return false; 100 | } 101 | 102 | bool export_list::contains(unsigned __int32 address) 103 | { 104 | // Look up a 32-bit value 105 | if (address > _max32 || address < _min32 || (address & ~_bits32) > 0) 106 | { 107 | // We know there is no match by this quick filtering. This improves performance hugely. 108 | return false; 109 | } 110 | 111 | // Lookup the address 112 | unordered_set::const_iterator got = _addresses.find(address); 113 | if (got != _addresses.end()) 114 | { 115 | return true; 116 | } 117 | return false; 118 | } 119 | 120 | unsigned __int64 export_list::find_export(char* library, char* name, bool is64) 121 | { 122 | // Find the specified procedure in the corresponding library. Limit it to the specific 32-bit or 64-bit version of the library. 123 | for (unordered_map::iterator it = _address_to_exports.begin(); it != _address_to_exports.end(); ++it) 124 | { 125 | //if( strcmp(it->second->library_name,"kernel32.dll") == 0 ) 126 | // printf("%s::%s\n", it->second->library_name, it->second->name); 127 | if (it->second->is64 == is64 && (library == NULL || strcmpi(library, it->second->library_name) == 0 ) && strcmpi(name, it->second->name) == 0) 128 | { 129 | // Found a match 130 | return it->second->address; 131 | } 132 | } 133 | 134 | // No match 135 | return NULL; 136 | } 137 | 138 | export_entry export_list::find(unsigned __int64 address) 139 | { 140 | // Lookup the address 141 | unordered_map::const_iterator got = _address_to_exports.find(address); 142 | if (got != _address_to_exports.end()) 143 | { 144 | return got->second; 145 | } 146 | return NULL; 147 | } 148 | 149 | void export_list::add_export(unsigned __int64 address, export_entry* entry) 150 | { 151 | // Register this export address for quick lookups later 152 | if (_address_to_exports.count(address) == 0) 153 | { 154 | _address_to_exports.insert(std::pair(address, new export_entry(entry))); 155 | 156 | if (_addresses.count(address) == 0) 157 | { 158 | _addresses.insert(address); 159 | 160 | // Update our quick-lookup values 161 | if ( address > UINT_MAX ) 162 | { 163 | // 64bit value 164 | if (address > _max64) 165 | _max64 = address; 166 | if (address < _min64) 167 | _min64 = address; 168 | _bits64 = _bits64 | address; 169 | } 170 | else 171 | { 172 | // 32bit value 173 | if (address > _max32) 174 | _max32 = address; 175 | if (address < _min32) 176 | _min32 = address; 177 | _bits32 = _bits32 | address; 178 | } 179 | } 180 | } 181 | } 182 | 183 | bool export_list::add_exports(export_list* other) 184 | { 185 | // Merge the exports from the other list with the current export list 186 | for (unordered_map::iterator it = other->_address_to_exports.begin(); 187 | it != other->_address_to_exports.end(); ++it) 188 | { 189 | add_export(it->first, it->second); 190 | } 191 | return true; 192 | } 193 | 194 | 195 | bool export_list::add_exports(unsigned char* image, SIZE_T image_size, unsigned __int64 image_base, IMAGE_EXPORT_DIRECTORY* export_directory, bool is64) 196 | { 197 | if( export_directory->NumberOfFunctions > 0 && export_directory->AddressOfNameOrdinals != 0 ) 198 | { 199 | char* library_name = (char*) ((__int64) export_directory->Name + image); 200 | if( !test_read(image, image_size, (unsigned char*) library_name, 0x1ff) || strlen(library_name) == 0 ) 201 | { 202 | // Library name is invalid, no point in continuing to parse since we need this name to reconstruct any imports anyway 203 | //throw std::invalid_argument( printf("Invalid library export directory module name. Unable to add exports to table for import reconstruction. Library base 0x%llX.", (unsigned long long int) image_base ) ); 204 | fprintf( stderr, "WARNING: Invalid library export directory module name. Unable to add exports to table for import reconstruction. Library base 0x%llX.\n", 205 | (unsigned long long int) image_base ); 206 | return false; 207 | } 208 | 209 | // Parse the export directory 210 | for (int i = 0; i < export_directory->NumberOfNames; i++) 211 | { 212 | // Load the ordinal 213 | if( test_read(image, image_size, image + export_directory->AddressOfNameOrdinals + i*2, 2) ) 214 | { 215 | DWORD ordinal_relative = *(WORD*)(export_directory->AddressOfNameOrdinals + i*2 + image); 216 | DWORD ordinal = export_directory->Base + ordinal_relative; 217 | 218 | // Load the name, there doesn't have to be one 219 | char* name = NULL; 220 | if (i < export_directory->NumberOfNames) 221 | { 222 | if( test_read(image, image_size, image + export_directory->AddressOfNames + i*4, 4) ) 223 | { 224 | DWORD name_offset = *((DWORD*) (image + export_directory->AddressOfNames + i*4)); 225 | 226 | if( test_read(image, image_size, image + name_offset, 0x4f) ) 227 | { 228 | name = (char*) ( name_offset + image ); 229 | } 230 | } 231 | } 232 | 233 | 234 | // Load the rva 235 | if( test_read(image, image_size, image + export_directory->AddressOfFunctions + ordinal_relative*4, 4) ) 236 | { 237 | __int64 rva = *((DWORD*)(image + export_directory->AddressOfFunctions + ordinal_relative * 4)); 238 | 239 | // Don't consider rva's of multiple of 0x1000 to prevent making mistakes in dump repairs 240 | if( rva % 0x1000 != 0 ) 241 | { 242 | __int64 address = image_base + rva; 243 | 244 | // Add this export 245 | export_entry* new_entry = new export_entry( library_name, name, ordinal, rva, address, is64); 246 | add_export(address, new_entry); 247 | delete new_entry; 248 | } 249 | } 250 | 251 | } 252 | } 253 | } 254 | return true; 255 | 256 | 257 | } 258 | 259 | export_list::~export_list(void) 260 | { 261 | // Clean up the export list 262 | for (unordered_map::iterator it = _address_to_exports.begin(); 263 | it != _address_to_exports.end(); ++it) 264 | { 265 | delete it->second; 266 | } 267 | _address_to_exports.clear(); 268 | } 269 | -------------------------------------------------------------------------------- /pd/export_list.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DynArray.h" 4 | #include "windows.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "utils.h" 11 | #include "limits.h" 12 | 13 | using namespace std; 14 | using namespace std::tr1; 15 | 16 | class export_entry 17 | { 18 | public: 19 | char* library_name; 20 | char* name; 21 | WORD ord; 22 | bool is64; 23 | unsigned __int64 rva; 24 | unsigned __int64 address; 25 | 26 | export_entry(char* library_name, char* name, WORD ord, unsigned __int64 rva, unsigned __int64 address, bool is64); 27 | export_entry(export_entry* other); 28 | ~export_entry(void); 29 | }; 30 | 31 | 32 | class export_list 33 | { 34 | unsigned __int64 _min64; 35 | unsigned __int64 _max64; 36 | unsigned __int32 _min32; 37 | unsigned __int32 _max32; 38 | unsigned __int32 _bits32; 39 | unsigned __int64 _bits64; 40 | 41 | unordered_map _address_to_exports; // List of export addresses in this export list 42 | unordered_set _addresses; // List of export addresses 43 | public: 44 | 45 | 46 | export_list(); 47 | 48 | bool add_exports(unsigned char* image, SIZE_T image_size, unsigned __int64 image_base, IMAGE_EXPORT_DIRECTORY* header_export_directory, bool is64); 49 | bool add_exports(export_list* other); 50 | void add_export(unsigned __int64 address, export_entry* entry); 51 | 52 | // Find export addresses in a process 53 | unsigned __int64 find_export(char* library, char* name, bool is64); 54 | 55 | // Functions to get quick filter values before doing a lookup 56 | bool contains(unsigned __int64 address); 57 | bool contains(unsigned __int32 address); 58 | export_entry find(unsigned __int64 address); 59 | unsigned __int64 get_min64() { return _min64; }; 60 | unsigned __int64 get_max64() { return _max64; }; 61 | unsigned __int32 get_min32() { return _min32; }; 62 | unsigned __int32 get_max32() { return _max32; }; 63 | unsigned __int32 get_nobits32() { return ~_bits32; }; 64 | unsigned __int64 get_nobits64() { return ~_bits64; }; 65 | 66 | ~export_list(void); 67 | }; 68 | -------------------------------------------------------------------------------- /pd/hash.cpp: -------------------------------------------------------------------------------- 1 | #include "StdAfx.h" 2 | #include "hash.h" 3 | 4 | /* Crc - 32 BIT ANSI X3.66 CRC checksum files */ 5 | 6 | 7 | /**********************************************************************\ 8 | |* Demonstration program to compute the 32-bit CRC used as the frame *| 9 | |* check sequence in ADCCP (ANSI X3.66, also known as FIPS PUB 71 *| 10 | |* and FED-STD-1003, the U.S. versions of CCITT's X.25 link-level *| 11 | |* protocol). The 32-bit FCS was added via the Federal Register, *| 12 | |* 1 June 1982, p.23798. I presume but don't know for certain that *| 13 | |* this polynomial is or will be included in CCITT V.41, which *| 14 | |* defines the 16-bit CRC (often called CRC-CCITT) polynomial. FIPS *| 15 | |* PUB 78 says that the 32-bit FCS reduces otherwise undetected *| 16 | |* errors by a factor of 10^-5 over 16-bit FCS. *| 17 | \**********************************************************************/ 18 | 19 | /* Need an unsigned type capable of holding 32 bits; */ 20 | 21 | 22 | /* Copyright (C) 1986 Gary S. Brown. You may use this program, or 23 | code or tables extracted from it, as desired without restriction.*/ 24 | 25 | /* First, the polynomial itself and its table of feedback terms. The */ 26 | /* polynomial is */ 27 | /* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ 28 | /* Note that we take it "backwards" and put the highest-order term in */ 29 | /* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ 30 | /* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ 31 | /* the MSB being 1. */ 32 | 33 | /* Note that the usual hardware shift register implementation, which */ 34 | /* is what we're using (we're merely optimizing it by doing eight-bit */ 35 | /* chunks at a time) shifts bits into the lowest-order term. In our */ 36 | /* implementation, that means shifting towards the right. Why do we */ 37 | /* do it this way? Because the calculated CRC must be transmitted in */ 38 | /* order from highest-order term to lowest-order term. UARTs transmit */ 39 | /* characters in order from LSB to MSB. By storing the CRC this way, */ 40 | /* we hand it to the UART in the order low-byte to high-byte; the UART */ 41 | /* sends each low-bit to hight-bit; and the result is transmission bit */ 42 | /* by bit from highest- to lowest-order term without requiring any bit */ 43 | /* shuffling on our part. Reception works similarly. */ 44 | 45 | /* The feedback terms table consists of 256, 32-bit entries. Notes: */ 46 | /* */ 47 | /* 1. The table can be generated at runtime if desired; code to do so */ 48 | /* is shown later. It might not be obvious, but the feedback */ 49 | /* terms simply represent the results of eight shift/xor opera- */ 50 | /* tions for all combinations of data and CRC register values. */ 51 | /* */ 52 | /* 2. The CRC accumulation logic is the same for all CRC polynomials, */ 53 | /* be they sixteen or thirty-two bits wide. You simply choose the */ 54 | /* appropriate table. Alternatively, because the table can be */ 55 | /* generated at runtime, you can start by generating the table for */ 56 | /* the polynomial in question and use exactly the same "updcrc", */ 57 | /* if your application needn't simultaneously handle two CRC */ 58 | /* polynomials. (Note, however, that XMODEM is strange.) */ 59 | /* */ 60 | /* 3. For 16-bit CRCs, the table entries need be only 16 bits wide; */ 61 | /* of course, 32-bit entries work OK if the high 16 bits are zero. */ 62 | /* */ 63 | /* 4. The values must be right-shifted by eight bits by the "updcrc" */ 64 | /* logic; the shift must be unsigned (bring in zeroes). On some */ 65 | /* hardware you could probably optimize the shift in assembler by */ 66 | /* using byte-swap instructions. */ 67 | 68 | static DWORD crc_32_tab[] = { /* CRC polynomial 0xedb88320 */ 69 | 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 70 | 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 71 | 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 72 | 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 73 | 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 74 | 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 75 | 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 76 | 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 77 | 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 78 | 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 79 | 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 80 | 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 81 | 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 82 | 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 83 | 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 84 | 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 85 | 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 86 | 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 87 | 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 88 | 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 89 | 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 90 | 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 91 | 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 92 | 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 93 | 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 94 | 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 95 | 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 96 | 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 97 | 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 98 | 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 99 | 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 100 | 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 101 | 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 102 | 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 103 | 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 104 | 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 105 | 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 106 | 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 107 | 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 108 | 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 109 | 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 110 | 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 111 | 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d 112 | }; 113 | 114 | 115 | 116 | DWORD updateCRC32(unsigned char ch, DWORD crc) 117 | { 118 | return UPDC32(ch, crc); 119 | } 120 | 121 | DWORD crc32buf(char *buf, size_t len) 122 | { 123 | register DWORD oldcrc32; 124 | 125 | oldcrc32 = 0xFFFFFFFF; 126 | 127 | for ( ; len; --len, ++buf) 128 | { 129 | oldcrc32 = UPDC32(*buf, oldcrc32); 130 | } 131 | 132 | return ~oldcrc32; 133 | 134 | } -------------------------------------------------------------------------------- /pd/hash.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "StdAfx.h" 4 | #include "hash.h" 5 | #include 6 | #include "windows.h" 7 | 8 | /* Crc - 32 BIT ANSI X3.66 CRC checksum files */ 9 | 10 | #include 11 | 12 | 13 | /**********************************************************************\ 14 | |* Demonstration program to compute the 32-bit CRC used as the frame *| 15 | |* check sequence in ADCCP (ANSI X3.66, also known as FIPS PUB 71 *| 16 | |* and FED-STD-1003, the U.S. versions of CCITT's X.25 link-level *| 17 | |* protocol). The 32-bit FCS was added via the Federal Register, *| 18 | |* 1 June 1982, p.23798. I presume but don't know for certain that *| 19 | |* this polynomial is or will be included in CCITT V.41, which *| 20 | |* defines the 16-bit CRC (often called CRC-CCITT) polynomial. FIPS *| 21 | |* PUB 78 says that the 32-bit FCS reduces otherwise undetected *| 22 | |* errors by a factor of 10^-5 over 16-bit FCS. *| 23 | \**********************************************************************/ 24 | 25 | 26 | /* Copyright (C) 1986 Gary S. Brown. You may use this program, or 27 | code or tables extracted from it, as desired without restriction.*/ 28 | 29 | /* First, the polynomial itself and its table of feedback terms. The */ 30 | /* polynomial is */ 31 | /* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ 32 | /* Note that we take it "backwards" and put the highest-order term in */ 33 | /* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ 34 | /* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ 35 | /* the MSB being 1. */ 36 | 37 | /* Note that the usual hardware shift register implementation, which */ 38 | /* is what we're using (we're merely optimizing it by doing eight-bit */ 39 | /* chunks at a time) shifts bits into the lowest-order term. In our */ 40 | /* implementation, that means shifting towards the right. Why do we */ 41 | /* do it this way? Because the calculated CRC must be transmitted in */ 42 | /* order from highest-order term to lowest-order term. UARTs transmit */ 43 | /* characters in order from LSB to MSB. By storing the CRC this way, */ 44 | /* we hand it to the UART in the order low-byte to high-byte; the UART */ 45 | /* sends each low-bit to hight-bit; and the result is transmission bit */ 46 | /* by bit from highest- to lowest-order term without requiring any bit */ 47 | /* shuffling on our part. Reception works similarly. */ 48 | 49 | /* The feedback terms table consists of 256, 32-bit entries. Notes: */ 50 | /* */ 51 | /* 1. The table can be generated at runtime if desired; code to do so */ 52 | /* is shown later. It might not be obvious, but the feedback */ 53 | /* terms simply represent the results of eight shift/xor opera- */ 54 | /* tions for all combinations of data and CRC register values. */ 55 | /* */ 56 | /* 2. The CRC accumulation logic is the same for all CRC polynomials, */ 57 | /* be they sixteen or thirty-two bits wide. You simply choose the */ 58 | /* appropriate table. Alternatively, because the table can be */ 59 | /* generated at runtime, you can start by generating the table for */ 60 | /* the polynomial in question and use exactly the same "updcrc", */ 61 | /* if your application needn't simultaneously handle two CRC */ 62 | /* polynomials. (Note, however, that XMODEM is strange.) */ 63 | /* */ 64 | /* 3. For 16-bit CRCs, the table entries need be only 16 bits wide; */ 65 | /* of course, 32-bit entries work OK if the high 16 bits are zero. */ 66 | /* */ 67 | /* 4. The values must be right-shifted by eight bits by the "updcrc" */ 68 | /* logic; the shift must be unsigned (bring in zeroes). On some */ 69 | /* hardware you could probably optimize the shift in assembler by */ 70 | /* using byte-swap instructions. */ 71 | 72 | DWORD updateCRC32(unsigned char ch, DWORD crc); 73 | DWORD crc32buf(char *buf, size_t len); 74 | 75 | #define UPDC32(b, c) (crc_32_tab[((int)c ^ b) & 0xff] ^ ((c >> 8) & 0x00FFFFFF)) -------------------------------------------------------------------------------- /pd/module_list.cpp: -------------------------------------------------------------------------------- 1 | #include "StdAfx.h" 2 | #include "module_list.h" 3 | 4 | 5 | module_list::module_list() 6 | { 7 | // Empty list 8 | } 9 | 10 | module_list::module_list( DWORD pid ) 11 | { 12 | #if defined(_WIN64) 13 | // List modules on a 64 bit machine. A 64 bit machine is assumed to be Windows Vista+ 14 | HMODULE hMods[2048]; 15 | DWORD cbNeeded; 16 | unsigned int i; 17 | HANDLE ph = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 18 | FALSE, pid ); 19 | if( ph != NULL ) 20 | { 21 | if( EnumProcessModulesEx(ph, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_ALL)) 22 | { 23 | for ( i = 0; i < (cbNeeded / sizeof(HMODULE)); i++ ) 24 | { 25 | // Query the module basic information 26 | MODULEINFO info; 27 | if( GetModuleInformation( ph, hMods[i], &info, sizeof(MODULEINFO) ) ) 28 | { 29 | // Check if this base address is already occupied 30 | unordered_map::const_iterator item = _modules.find( (unsigned __int64) info.lpBaseOfDll ); 31 | 32 | if( item == _modules.end() ) 33 | { 34 | // Add this module 35 | _modules[(unsigned __int64) info.lpBaseOfDll ] = new module( ph, hMods[i], info ); 36 | } 37 | } 38 | else 39 | { 40 | PrintLastError(L"module_list GetModuleInformation"); 41 | } 42 | } 43 | } 44 | 45 | CloseHandle( ph ); 46 | } 47 | else 48 | { 49 | if( GetLastError() == 299 ) 50 | fprintf(stderr, "ERROR: Unable to open process PID 0x%x since it is a 64 bit process and this tool is running as a 32 bit process.\n", pid); 51 | else 52 | PrintLastError(L"module_list OpenProcess"); 53 | } 54 | #elif defined(_WIN32) 55 | 56 | HANDLE hSnapshot=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); 57 | if ( hSnapshot == INVALID_HANDLE_VALUE ) 58 | { 59 | if( global_flag_verbose ) 60 | printf ("WARNING: Could not gather process information for process pid 0x%X, error code (%d).\n", pid, GetLastError()); 61 | return; 62 | } 63 | 64 | MODULEENTRY32 tmpModule; 65 | tmpModule.dwSize = sizeof(MODULEENTRY32); 66 | if( Module32First(hSnapshot, &tmpModule) ) 67 | { 68 | // Add this first module to our array 69 | tmpModule.dwSize = sizeof(MODULEENTRY32); 70 | 71 | // Check if this base address is already occupied 72 | unordered_map::const_iterator item = _modules.find( (unsigned __int64) tmpModule.modBaseAddr ); 73 | if( item == _modules.end() ) 74 | { 75 | // Add this module 76 | _modules[(unsigned __int64) tmpModule.modBaseAddr ] = new module( tmpModule ); 77 | } 78 | 79 | while(Module32Next(hSnapshot,&tmpModule)) 80 | { 81 | // Add this module to our array 82 | unordered_map::const_iterator item = _modules.find( (unsigned __int64) tmpModule.modBaseAddr ); 83 | if( item == _modules.end() ) 84 | { 85 | // Add this module 86 | _modules[(unsigned __int64) tmpModule.modBaseAddr ] = new module( tmpModule ); 87 | } 88 | 89 | tmpModule.dwSize = sizeof(MODULEENTRY32); 90 | } 91 | } 92 | 93 | CloseHandle(hSnapshot); 94 | 95 | #endif 96 | } 97 | 98 | module_list::~module_list(void) 99 | { 100 | // Clean up the module list 101 | for ( unordered_map::const_iterator item = _modules.begin(); item != _modules.end(); ++item ) 102 | delete item->second; 103 | } 104 | -------------------------------------------------------------------------------- /pd/module_list.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "windows.h" 5 | #include "simple.h" 6 | #include 7 | #include 8 | #include "Psapi.h" 9 | 10 | using namespace std::tr1; 11 | 12 | 13 | extern bool global_flag_verbose; 14 | 15 | class module 16 | { 17 | public: 18 | unsigned __int64 start; 19 | unsigned __int64 size; 20 | char* full_name; 21 | char* short_name; 22 | 23 | module( HANDLE ph, HMODULE mh, MODULEINFO info ) 24 | { 25 | this->start = (unsigned __int64) info.lpBaseOfDll; 26 | this->size = (unsigned __int64) info.SizeOfImage; 27 | 28 | full_name = new char[260]; 29 | short_name = new char[256]; 30 | 31 | // Read in the short and long name 32 | GetModuleFileNameExA( ph, mh, full_name, 260 ); 33 | GetModuleBaseNameA( ph, mh, short_name, 256 ); 34 | } 35 | 36 | module( MODULEENTRY32 tmpModule ) 37 | { 38 | this->start = (unsigned __int64) tmpModule.modBaseAddr; 39 | this->size = (unsigned __int64) tmpModule.modBaseSize; 40 | 41 | full_name = new char[260]; 42 | short_name = new char[256]; 43 | 44 | _snprintf( full_name, 259, "%S", tmpModule.szExePath ); 45 | _snprintf( short_name, 255, "%S", tmpModule.szModule ); 46 | } 47 | 48 | ~module() 49 | { 50 | if( full_name != NULL ) 51 | delete[] full_name; 52 | if( short_name != NULL ) 53 | delete[] short_name; 54 | } 55 | }; 56 | 57 | class module_list 58 | { 59 | 60 | HANDLE _ph; 61 | 62 | public: 63 | unordered_map _modules; 64 | module_list(); 65 | module_list( DWORD pid ); 66 | ~module_list(void); 67 | }; 68 | -------------------------------------------------------------------------------- /pd/pd.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glmcdona/Process-Dump/1d91e0eb7b9b2ba21136bf3903eb107b5004120d/pd/pd.h -------------------------------------------------------------------------------- /pd/pd.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United States) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 20 | #pragma code_page(1252) 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Version 51 | // 52 | 53 | VS_VERSION_INFO VERSIONINFO 54 | FILEVERSION 2,2,0,0 55 | PRODUCTVERSION 2,2,0,0 56 | FILEFLAGSMASK 0x3fL 57 | #ifdef _DEBUG 58 | FILEFLAGS 0x1L 59 | #else 60 | FILEFLAGS 0x0L 61 | #endif 62 | FILEOS 0x40004L 63 | FILETYPE 0x1L 64 | FILESUBTYPE 0x0L 65 | BEGIN 66 | BLOCK "StringFileInfo" 67 | BEGIN 68 | BLOCK "100904b0" 69 | BEGIN 70 | VALUE "CompanyName", "Geoff McDonald" 71 | VALUE "FileDescription", "Process Dump. https://github.com/glmcdona/Process-Dump" 72 | VALUE "FileVersion", "2.2.0.0" 73 | VALUE "InternalName", "pd.exe" 74 | VALUE "LegalCopyright", "Copyright (C) 2022" 75 | VALUE "OriginalFilename", "pd.exe" 76 | VALUE "ProductName", "Process Dump" 77 | VALUE "ProductVersion", "2.2.0.0" 78 | END 79 | END 80 | BLOCK "VarFileInfo" 81 | BEGIN 82 | VALUE "Translation", 0x1009, 1200 83 | END 84 | END 85 | 86 | #endif // English (United States) resources 87 | ///////////////////////////////////////////////////////////////////////////// 88 | 89 | 90 | 91 | #ifndef APSTUDIO_INVOKED 92 | ///////////////////////////////////////////////////////////////////////////// 93 | // 94 | // Generated from the TEXTINCLUDE 3 resource. 95 | // 96 | 97 | 98 | ///////////////////////////////////////////////////////////////////////////// 99 | #endif // not APSTUDIO_INVOKED 100 | 101 | -------------------------------------------------------------------------------- /pd/pd.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 18 | 19 | 20 | 21 | 22 | 29 | 32 | 35 | 38 | 41 | 44 | 55 | 58 | 61 | 64 | 72 | 75 | 78 | 81 | 84 | 87 | 90 | 93 | 94 | 101 | 104 | 107 | 110 | 113 | 117 | 128 | 131 | 134 | 137 | 145 | 148 | 151 | 154 | 157 | 160 | 163 | 166 | 167 | 175 | 178 | 181 | 184 | 187 | 190 | 201 | 204 | 207 | 210 | 220 | 223 | 226 | 229 | 232 | 235 | 238 | 241 | 242 | 250 | 253 | 256 | 259 | 262 | 266 | 277 | 280 | 283 | 286 | 296 | 299 | 302 | 305 | 308 | 311 | 314 | 317 | 318 | 325 | 328 | 331 | 334 | 337 | 340 | 351 | 354 | 357 | 360 | 367 | 370 | 373 | 376 | 379 | 382 | 385 | 388 | 389 | 396 | 399 | 402 | 405 | 408 | 412 | 423 | 426 | 429 | 432 | 440 | 443 | 446 | 449 | 452 | 455 | 458 | 461 | 462 | 463 | 464 | 465 | 466 | 471 | 474 | 475 | 478 | 479 | 482 | 483 | 486 | 487 | 490 | 491 | 494 | 495 | 498 | 499 | 502 | 503 | 506 | 507 | 510 | 511 | 514 | 517 | 521 | 522 | 525 | 529 | 530 | 533 | 537 | 538 | 541 | 545 | 546 | 549 | 553 | 554 | 557 | 561 | 562 | 563 | 564 | 569 | 572 | 573 | 576 | 577 | 580 | 581 | 584 | 585 | 588 | 589 | 592 | 593 | 596 | 597 | 601 | 602 | 605 | 606 | 609 | 610 | 613 | 614 | 617 | 618 | 623 | 624 | 627 | 628 | 631 | 632 | 633 | 638 | 639 | 640 | 641 | 642 | 643 | -------------------------------------------------------------------------------- /pd/pd.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | x64 22 | Win32 23 | 24 | 25 | x64 26 | x64 27 | 28 | 29 | 30 | {49B0E81A-D38E-426E-AB76-CC8463EC0003} 31 | pd 32 | Win32Proj 33 | 10.0 34 | 35 | 36 | 37 | Application 38 | v142 39 | Unicode 40 | 41 | 42 | Application 43 | v142 44 | Unicode 45 | true 46 | 47 | 48 | Application 49 | v142 50 | Unicode 51 | 52 | 53 | Application 54 | v142 55 | Unicode 56 | 57 | 58 | Application 59 | v142 60 | Unicode 61 | true 62 | 63 | 64 | Application 65 | v142 66 | Unicode 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | <_ProjectFileVersion>14.0.24730.2 92 | 93 | 94 | $(SolutionDir)$(Configuration)\ 95 | $(Configuration)\ 96 | true 97 | 98 | 99 | $(SolutionDir)$(Platform)\$(Configuration)\ 100 | $(Platform)\$(Configuration)\ 101 | true 102 | 103 | 104 | $(SolutionDir)$(Platform)\$(Configuration)\ 105 | $(Platform)\$(Configuration)\ 106 | false 107 | 108 | 109 | $(SolutionDir)$(Platform)\$(Configuration)\ 110 | $(Platform)\$(Configuration)\ 111 | false 112 | 113 | 114 | $(SolutionDir)$(Configuration)\ 115 | $(Configuration)\ 116 | true 117 | 118 | 119 | $(SolutionDir)$(Platform)\$(Configuration)\ 120 | $(Platform)\$(Configuration)\ 121 | true 122 | 123 | 124 | 125 | Disabled 126 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 127 | true 128 | EnableFastChecks 129 | MultiThreadedDebugDLL 130 | Use 131 | Level3 132 | EditAndContinue 133 | 134 | 135 | Shlwapi.lib;Kernel32.lib;%(AdditionalDependencies) 136 | true 137 | Console 138 | MachineX86 139 | 140 | 141 | 142 | 143 | X64 144 | 145 | 146 | Disabled 147 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 148 | true 149 | EnableFastChecks 150 | MultiThreadedDebugDLL 151 | Use 152 | Level3 153 | ProgramDatabase 154 | 155 | 156 | Shlwapi.lib;Kernel32.lib;Psapi.lib;%(AdditionalDependencies) 157 | true 158 | Console 159 | MachineX64 160 | 161 | 162 | 163 | 164 | MaxSpeed 165 | true 166 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 167 | MultiThreaded 168 | true 169 | Use 170 | Level3 171 | ProgramDatabase 172 | 173 | 174 | Shlwapi.lib;Kernel32.lib;%(AdditionalDependencies) 175 | true 176 | Console 177 | true 178 | true 179 | MachineX86 180 | 181 | 182 | 183 | 184 | X64 185 | 186 | 187 | MaxSpeed 188 | true 189 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 190 | MultiThreaded 191 | true 192 | Use 193 | Level3 194 | ProgramDatabase 195 | 196 | 197 | Shlwapi.lib;Kernel32.lib;Psapi.lib;%(AdditionalDependencies) 198 | true 199 | Console 200 | true 201 | true 202 | MachineX64 203 | 204 | 205 | 206 | 207 | Disabled 208 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 209 | true 210 | EnableFastChecks 211 | MultiThreadedDebugDLL 212 | Use 213 | Level3 214 | EditAndContinue 215 | 216 | 217 | true 218 | Console 219 | MachineX86 220 | 221 | 222 | 223 | 224 | X64 225 | 226 | 227 | Disabled 228 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 229 | true 230 | EnableFastChecks 231 | MultiThreadedDebugDLL 232 | Use 233 | Level3 234 | ProgramDatabase 235 | 236 | 237 | Shlwapi.lib;Kernel32.lib;Psapi.lib;%(AdditionalDependencies) 238 | true 239 | Console 240 | MachineX64 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | Create 257 | Create 258 | Create 259 | Create 260 | Create 261 | Create 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | CppCode 278 | 279 | 280 | 281 | 282 | 283 | 284 | true 285 | CppClass 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | -------------------------------------------------------------------------------- /pd/pd.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;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 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | Source Files 50 | 51 | 52 | Source Files 53 | 54 | 55 | Source Files 56 | 57 | 58 | 59 | 60 | Header Files 61 | 62 | 63 | Header Files 64 | 65 | 66 | Header Files 67 | 68 | 69 | Header Files 70 | 71 | 72 | Header Files 73 | 74 | 75 | Header Files 76 | 77 | 78 | Header Files 79 | 80 | 81 | Header Files 82 | 83 | 84 | Header Files 85 | 86 | 87 | Header Files 88 | 89 | 90 | Header Files 91 | 92 | 93 | Header Files 94 | 95 | 96 | Header Files 97 | 98 | 99 | Header Files 100 | 101 | 102 | Header Files 103 | 104 | 105 | Header Files 106 | 107 | 108 | Header Files 109 | 110 | 111 | Header Files 112 | 113 | 114 | Header Files 115 | 116 | 117 | Header Files 118 | 119 | 120 | 121 | 122 | Resource Files 123 | 124 | 125 | -------------------------------------------------------------------------------- /pd/pe_exports.cpp: -------------------------------------------------------------------------------- 1 | #include "StdAfx.h" 2 | #include "pe_exports.h" 3 | 4 | pe_exports::pe_exports(void) 5 | { 6 | } 7 | 8 | pe_exports::~pe_exports(void) 9 | { 10 | } 11 | -------------------------------------------------------------------------------- /pd/pe_exports.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class pe_exports 4 | { 5 | public: 6 | pe_exports(void); 7 | ~pe_exports(void); 8 | }; 9 | -------------------------------------------------------------------------------- /pd/pe_hash_database.cpp: -------------------------------------------------------------------------------- 1 | #include "StdAfx.h" 2 | #include "pe_hash_database.h" 3 | 4 | bool pe_hash_database::_is_mz(FILE* stream) 5 | { 6 | char twochars[2]; 7 | if( (fread( &twochars, 1, 2, stream) == 2) && twochars[0] == 'M' && twochars[1] == 'Z' ) 8 | { 9 | fseek( stream, 0, SEEK_SET); 10 | return true; 11 | } 12 | fseek( stream, 0, SEEK_SET); 13 | return false; 14 | } 15 | 16 | pe_hash_database::pe_hash_database(char* clean_database_name, char* ep_database_name, char* epshort_database_name) 17 | { 18 | InitializeCriticalSectionAndSpinCount(&_lock, 0x00000400); 19 | EnterCriticalSection( &_lock ); 20 | 21 | // Build the full database names 22 | _clean_database_path = new char[ strlen(clean_database_name) + 1 ]; 23 | strcpy( _clean_database_path, clean_database_name ); 24 | 25 | _ep_database_path = new char[strlen(ep_database_name) + 1]; 26 | strcpy(_ep_database_path, ep_database_name); 27 | 28 | _epshort_database_path = new char[strlen(epshort_database_name) + 1]; 29 | strcpy(_epshort_database_path, epshort_database_name); 30 | 31 | _clean_hashes.rehash(0x10000); 32 | _ep_hashes.rehash(0x10000); 33 | _epshort_hashes.rehash(0x10000); 34 | 35 | // Open and read in the database of clean hashes if it exists. 36 | printf("Loading clean hash database from '%s'.\n", clean_database_name); 37 | FILE* fh = fopen( _clean_database_path, "rb" ); 38 | unsigned __int64 hash; 39 | if( fh ) 40 | { 41 | // Read in the database 42 | while( !feof( fh ) ) 43 | { 44 | if( fread( &hash, sizeof(unsigned __int64), 1, fh ) == 1 ) 45 | { 46 | _clean_hashes.insert( hash ); 47 | } 48 | } 49 | fclose(fh); 50 | 51 | printf("Loaded %i clean hashes from database.\n", _clean_hashes.size()); 52 | } 53 | else 54 | { 55 | if( PathFileExistsA( _clean_database_path ) ) 56 | { 57 | // Do not continue if the database exists, but we failed to open it. This is to protect 58 | // the contents of the database from being overwritten if we save the now empty database 59 | // successfully. 60 | PrintLastError(L"Failed to open existing hash database. Terminating."); 61 | exit(-1); 62 | } 63 | 64 | printf("Did not find an existing clean hash database, using an empty one.\n"); 65 | } 66 | 67 | // Open and read in the database of entry points 68 | printf("Loading entrypoint hash database from '%s'.\n", ep_database_name); 69 | fh = fopen(_ep_database_path, "rb"); 70 | if (fh) 71 | { 72 | // Read in the database 73 | while (!feof(fh)) 74 | { 75 | if (fread(&hash, sizeof(unsigned __int64), 1, fh) == 1) 76 | { 77 | _ep_hashes.insert(hash); 78 | } 79 | } 80 | fclose(fh); 81 | 82 | printf("Loaded %i entrypoint hashes from database.\n", _ep_hashes.size()); 83 | } 84 | else 85 | { 86 | if (PathFileExistsA(_ep_database_path)) 87 | { 88 | // Do not continue if the database exists, but we failed to open it. This is to protect 89 | // the contents of the database from being overwritten if we save the now empty database 90 | // successfully. 91 | PrintLastError(L"Failed to open existing entrypoint hash database. Terminating."); 92 | exit(-1); 93 | } 94 | 95 | printf("Did not find an existing entrypoint hash database, using an empty one.\n"); 96 | } 97 | 98 | 99 | // Open and read in the database of entry points 100 | printf("Loading entrypoint short hash database from '%s'.\n", epshort_database_name); 101 | fh = fopen(_epshort_database_path, "rb"); 102 | if (fh) 103 | { 104 | // Read in the database 105 | while (!feof(fh)) 106 | { 107 | if (fread(&hash, sizeof(unsigned __int64), 1, fh) == 1) 108 | { 109 | _epshort_hashes.insert(hash); 110 | } 111 | } 112 | fclose(fh); 113 | 114 | printf("Loaded %i entrypoint short hashes from database.\n", _epshort_hashes.size()); 115 | } 116 | else 117 | { 118 | if (PathFileExistsA(_epshort_database_path)) 119 | { 120 | // Do not continue if the database exists, but we failed to open it. This is to protect 121 | // the contents of the database from being overwritten if we save the now empty database 122 | // successfully. 123 | PrintLastError(L"Failed to open existing entrypoint short hash database. Terminating."); 124 | exit(-1); 125 | } 126 | 127 | printf("Did not find an existing entrypoint short hash database, using an empty one.\n"); 128 | } 129 | 130 | LeaveCriticalSection( &_lock ); 131 | } 132 | 133 | int pe_hash_database::count() 134 | { 135 | EnterCriticalSection( &_lock ); 136 | int result = _clean_hashes.size(); 137 | LeaveCriticalSection( &_lock ); 138 | return result; 139 | } 140 | 141 | int pe_hash_database::count_eps() 142 | { 143 | EnterCriticalSection(&_lock); 144 | int result = _ep_hashes.size(); 145 | LeaveCriticalSection(&_lock); 146 | return result; 147 | } 148 | 149 | int pe_hash_database::count_epshorts() 150 | { 151 | EnterCriticalSection(&_lock); 152 | int result = _epshort_hashes.size(); 153 | LeaveCriticalSection(&_lock); 154 | return result; 155 | } 156 | 157 | bool pe_hash_database::clear_database() 158 | { 159 | EnterCriticalSection( &_lock ); 160 | _clean_hashes.clear(); 161 | _ep_hashes.clear(); 162 | _epshort_hashes.clear(); 163 | LeaveCriticalSection( &_lock ); 164 | return true; 165 | } 166 | 167 | bool pe_hash_database::add_hashes(unordered_set hashes) 168 | { 169 | EnterCriticalSection( &_lock ); 170 | 171 | for (unordered_set::iterator it=hashes.begin(); it!=hashes.end(); it++) 172 | { 173 | if( *it != 0 && _clean_hashes.count( *it ) == 0 ) 174 | { 175 | _clean_hashes.insert( *it ); 176 | } 177 | } 178 | 179 | LeaveCriticalSection( &_lock ); 180 | 181 | return true; 182 | } 183 | 184 | 185 | bool pe_hash_database::add_hashes_eps(unordered_set hashes, unordered_set hashes_short) 186 | { 187 | EnterCriticalSection(&_lock); 188 | 189 | for (unordered_set::iterator it = hashes.begin(); it != hashes.end(); it++) 190 | { 191 | if (*it != 0 && _ep_hashes.count(*it) == 0) 192 | { 193 | _ep_hashes.insert(*it); 194 | } 195 | } 196 | 197 | for (unordered_set::iterator it = hashes_short.begin(); it != hashes_short.end(); it++) 198 | { 199 | if (*it != 0 && _epshort_hashes.count(*it) == 0) 200 | { 201 | _epshort_hashes.insert(*it); 202 | } 203 | } 204 | 205 | LeaveCriticalSection(&_lock); 206 | 207 | return true; 208 | } 209 | 210 | 211 | bool pe_hash_database::add_folder( char* dir_name, WCHAR* filter, bool recursively ) 212 | { 213 | // Expand the environment names in the directory 214 | char dir_name_expanded[PATH_MAX + 1]; 215 | if (ExpandEnvironmentStringsA(dir_name, dir_name_expanded, PATH_MAX) < PATH_MAX) 216 | { 217 | DWORD ftyp = GetFileAttributesA(dir_name_expanded); 218 | if (ftyp != INVALID_FILE_ATTRIBUTES && ftyp & FILE_ATTRIBUTE_DIRECTORY && !(ftyp & FILE_ATTRIBUTE_REPARSE_POINT)) 219 | { 220 | DIR *dir; 221 | struct dirent *ent; 222 | dir = opendir(dir_name_expanded); 223 | if (dir != NULL) 224 | { 225 | /* print all the files and directories within directory */ 226 | while ((ent = readdir(dir)) != NULL) { 227 | // Convert the path to wchar format 228 | wchar_t* result = new wchar_t[strlen(ent->d_name) + 1]; 229 | 230 | if (result != NULL) 231 | { 232 | for (int i = 0; i < strlen(ent->d_name); i++) 233 | result[i] = ent->d_name[i]; 234 | result[strlen(ent->d_name)] = 0; 235 | 236 | if ((ent->d_type & DT_DIR)) 237 | { 238 | // Process this subdirectory if recursive flag is on 239 | if (recursively && wcscmp(result, L".") != 0 && wcscmp(result, L"..") != 0) 240 | { 241 | // Build the directory path 242 | char* directory = new char[strlen(dir_name_expanded) + strlen(ent->d_name) + 2]; 243 | sprintf_s(directory, strlen(dir_name_expanded) + strlen(ent->d_name) + 2, "%s\\%s", dir_name_expanded, ent->d_name); 244 | 245 | add_folder(directory, filter, recursively); 246 | 247 | // Cleanup 248 | delete[] directory; 249 | } 250 | } 251 | else { 252 | // Check if this filename is a match to the specified pattern 253 | if (PathMatchSpec(result, filter)) 254 | { 255 | // Process this file 256 | int length = wcslen(result) + strlen(dir_name_expanded) + 1; 257 | char* filename = new char[length + 1]; 258 | filename[length] = 0; 259 | sprintf(filename, "%s\\%S", dir_name_expanded, result); 260 | 261 | // Processes the specified file 262 | FILE* fh = fopen(filename, "rb"); 263 | if (fh != NULL) 264 | { 265 | if (_is_mz(fh)) 266 | { 267 | fclose(fh); 268 | 269 | add_file(filename); 270 | } 271 | else 272 | fclose(fh); 273 | } 274 | else { 275 | // Error 276 | fprintf(stderr, "Error opening file %s: %s.\n", filename, strerror(errno)); 277 | } 278 | delete[] filename; 279 | } 280 | } 281 | delete[] result; 282 | } 283 | else 284 | { 285 | fprintf(stderr, "Failed to allocate memory block of size %i for filename: %s.\n", ent->d_namlen + 1, strerror(errno)); 286 | } 287 | } 288 | closedir(dir); 289 | return true; 290 | } 291 | else { 292 | fprintf(stderr, "Unable to open directory %s: %s.\n", dir_name_expanded, strerror(errno)); 293 | } 294 | } 295 | } 296 | return false; 297 | } 298 | 299 | 300 | bool pe_hash_database::remove_folder( char* dir_name, WCHAR* filter, bool recursively ) 301 | { 302 | // Expand the environment names in the directory 303 | char* dir_name_expanded = new char[1000]; 304 | ExpandEnvironmentStringsA( dir_name, dir_name_expanded, 1000 ); 305 | 306 | 307 | DIR *dir; 308 | struct dirent *ent; 309 | dir = opendir (dir_name_expanded); 310 | if (dir != NULL) 311 | { 312 | /* print all the files and directories within directory */ 313 | while ((ent = readdir (dir)) != NULL) { 314 | // Convert the path to wchar format 315 | wchar_t* result = new wchar_t[ent->d_namlen + 1]; 316 | 317 | if( result != NULL ) 318 | { 319 | for( int i = 0; i < ent->d_namlen; i++ ) 320 | result[i] = ent->d_name[i]; 321 | result[ent->d_namlen] = 0; 322 | 323 | if( (ent->d_type & DT_DIR) ) 324 | { 325 | // Process this subdirectory if recursive flag is on 326 | if( recursively && wcscmp(result, L".") != 0 && wcscmp(result, L"..") != 0 ) 327 | { 328 | // Build the directory path 329 | char* directory = new char[strlen(dir_name_expanded) + strlen(ent->d_name) + 2]; 330 | sprintf(directory, "%s/%s", dir_name_expanded, ent->d_name); 331 | 332 | remove_folder( directory, filter, recursively ); 333 | 334 | // Cleanup 335 | delete[] directory; 336 | } 337 | }else{ 338 | // Check if this filename is a match to the specified pattern 339 | if( PathMatchSpec( result, filter ) ) 340 | { 341 | // Process this file 342 | int length = wcslen(result) + strlen(dir_name_expanded) + 1; 343 | char* filename = new char[length + 1]; 344 | filename[length] = 0; 345 | sprintf( filename, "%s\\%S", dir_name_expanded, result ); 346 | 347 | // Processes the specified file 348 | FILE* fh = fopen( filename, "rb" ); 349 | if( fh != NULL ) 350 | { 351 | if( _is_mz( fh ) ) 352 | { 353 | fclose(fh); 354 | 355 | remove_file(filename); 356 | } 357 | else 358 | fclose(fh); 359 | }else{ 360 | // Error 361 | fprintf(stderr, "Error opening file %s: %s.\n", filename, strerror(errno)); 362 | } 363 | delete[] filename; 364 | } 365 | } 366 | delete[] result; 367 | } 368 | else 369 | { 370 | fprintf(stderr, "Failed to allocate memory block of size %i for filename: %s.\n", ent->d_namlen + 1, strerror(errno)); 371 | } 372 | } 373 | closedir (dir); 374 | return true; 375 | }else{ 376 | fprintf(stderr, "Unable to open directory %s: %s.\n", dir_name_expanded, strerror(errno)); 377 | } 378 | return false; 379 | } 380 | 381 | 382 | bool pe_hash_database::contains(unsigned __int64 hash) 383 | { 384 | return _clean_hashes.count( hash ) != 0; 385 | } 386 | 387 | bool pe_hash_database::contains_ep(unsigned __int64 hash) 388 | { 389 | return _ep_hashes.count(hash) != 0; 390 | } 391 | 392 | bool pe_hash_database::contains_epshort(unsigned __int64 hash) 393 | { 394 | return _epshort_hashes.count(hash) != 0; 395 | } 396 | 397 | bool pe_hash_database::add_file(char* file) 398 | { 399 | PD_OPTIONS options; 400 | options.ForceGenHeader = false; 401 | options.ImportRec = false; 402 | options.Verbose = false; 403 | pe_header* header = new pe_header(file, &options); 404 | unsigned __int64 hash = 0; 405 | unsigned __int64 hash_ep = 0; 406 | unsigned __int64 hash_ep_short = 0; 407 | header->process_pe_header(); 408 | header->process_sections(); 409 | 410 | if( header->somewhat_parsed() ) 411 | { 412 | hash = header->get_hash(); 413 | hash_ep = header->get_hash_ep(); 414 | hash_ep_short = header->get_hash_ep_short(); 415 | } 416 | else 417 | { 418 | printf("Failed to parse PE header for %s.\n", file); 419 | delete header; 420 | return false; 421 | } 422 | 423 | 424 | delete header; 425 | 426 | // Add the entrypoint hash 427 | if (hash_ep != 0) 428 | { 429 | EnterCriticalSection(&_lock); 430 | if (_ep_hashes.count(hash_ep) == 0) 431 | { 432 | _ep_hashes.insert(hash_ep); 433 | printf("...new entrypoint hash %s,0x%llX\n", file, hash_ep); 434 | } 435 | LeaveCriticalSection(&_lock); 436 | } 437 | 438 | // Add the entrypoint hash 439 | if (hash_ep_short != 0) 440 | { 441 | EnterCriticalSection(&_lock); 442 | if (_epshort_hashes.count(hash_ep_short) == 0) 443 | { 444 | _epshort_hashes.insert(hash_ep_short); 445 | printf("...new entrypoint short hash %s,0x%llX\n", file, hash_ep_short); 446 | } 447 | LeaveCriticalSection(&_lock); 448 | } 449 | 450 | // Add the module hash 451 | if (hash != 0) 452 | { 453 | EnterCriticalSection(&_lock); 454 | if (_clean_hashes.count(hash) == 0) 455 | { 456 | _clean_hashes.insert(hash); 457 | printf("...new hash %s,0x%llX\n", file, hash); 458 | } 459 | LeaveCriticalSection(&_lock); 460 | 461 | return true; 462 | } 463 | 464 | printf("Failed to calculate hash for file %s.\n", file); 465 | return false; 466 | } 467 | 468 | bool pe_hash_database::remove_file(char* file) 469 | { 470 | PD_OPTIONS options; 471 | options.ForceGenHeader = false; 472 | options.ImportRec = false; 473 | options.Verbose = false; 474 | pe_header* header = new pe_header(file, &options); 475 | header->process_pe_header(); 476 | header->process_sections(); 477 | 478 | unsigned __int64 hash = 0; 479 | if( header->somewhat_parsed() ) 480 | { 481 | hash = header->get_hash(); 482 | } 483 | delete header; 484 | 485 | if( hash != 0 ) 486 | { 487 | EnterCriticalSection( &_lock ); 488 | if( _clean_hashes.count( hash ) != 0 ) 489 | { 490 | _clean_hashes.erase( hash ); 491 | printf("...deleted hash %s,0x%llX\n", file, hash); 492 | } 493 | LeaveCriticalSection( &_lock ); 494 | 495 | return true; 496 | } 497 | 498 | printf("Failed to add hash for file %s.\n", file); 499 | return false; 500 | } 501 | 502 | bool pe_hash_database::save() 503 | { 504 | // Save the entrypoint database 505 | FILE* fh = fopen(_ep_database_path, "wb"); 506 | unsigned __int64 hash; 507 | if (fh) 508 | { 509 | // Write the database 510 | EnterCriticalSection(&_lock); 511 | for (unordered_set::const_iterator it = _ep_hashes.begin(); 512 | it != _ep_hashes.end(); ++it) 513 | { 514 | hash = *it; 515 | fwrite(&hash, sizeof(unsigned __int64), 1, fh); 516 | } 517 | fclose(fh); 518 | LeaveCriticalSection(&_lock); 519 | 520 | printf("Wrote to entrypoint hash database. It now has a total of %i entrypoint hashes.\n", _ep_hashes.size()); 521 | } 522 | else 523 | { 524 | PrintLastError(L"Failed to open existing entrypoint database.."); 525 | } 526 | 527 | // Save the short entrypoint database 528 | fh = fopen(_epshort_database_path, "wb"); 529 | if (fh) 530 | { 531 | // Write the database 532 | EnterCriticalSection(&_lock); 533 | for (unordered_set::const_iterator it = _epshort_hashes.begin(); 534 | it != _epshort_hashes.end(); ++it) 535 | { 536 | hash = *it; 537 | fwrite(&hash, sizeof(unsigned __int64), 1, fh); 538 | } 539 | fclose(fh); 540 | LeaveCriticalSection(&_lock); 541 | 542 | printf("Wrote to entrypoint short hash database. It now has a total of %i entrypoint short hashes.\n", _epshort_hashes.size()); 543 | } 544 | else 545 | { 546 | PrintLastError(L"Failed to open existing entrypoint short hash database.."); 547 | } 548 | 549 | // Open and read in the database of clean hashes if it exists. 550 | fh = fopen( _clean_database_path, "wb" ); 551 | if( fh ) 552 | { 553 | // Write the database 554 | EnterCriticalSection( &_lock ); 555 | for (unordered_set::const_iterator it = _clean_hashes.begin(); 556 | it != _clean_hashes.end(); ++it) 557 | { 558 | hash = *it; 559 | fwrite( &hash, sizeof(unsigned __int64), 1, fh ); 560 | } 561 | fclose(fh); 562 | LeaveCriticalSection( &_lock ); 563 | 564 | printf("Wrote to clean hash database. It now has a total of %i clean hashes.\n", _clean_hashes.size()); 565 | return true; 566 | } 567 | else 568 | { 569 | PrintLastError(L"Failed to open existing clean hash database.."); 570 | } 571 | 572 | 573 | return false; 574 | } 575 | 576 | pe_hash_database::~pe_hash_database(void) 577 | { 578 | delete[] _clean_database_path; 579 | DeleteCriticalSection(&_lock); 580 | } 581 | 582 | -------------------------------------------------------------------------------- /pd/pe_hash_database.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "dirent.h" 10 | #include "Shlwapi.h" 11 | #include "pe_header.h" 12 | #include "simple.h" 13 | #include // std::mutex 14 | 15 | 16 | 17 | 18 | 19 | 20 | using namespace std; 21 | using namespace std::tr1; 22 | 23 | class pe_hash_database 24 | { 25 | char* _clean_database_path; // Known clean modules 26 | char* _ep_database_path; // EntryPoint hashes 27 | char* _epshort_database_path; // EntryPoint short hashes 28 | 29 | unordered_set _clean_hashes; 30 | unordered_set _ep_hashes; 31 | unordered_set _epshort_hashes; 32 | 33 | bool _is_mz(FILE* stream); 34 | CRITICAL_SECTION _lock; 35 | 36 | public: 37 | pe_hash_database(char* clean_database_name, char* ep_database_name, char* epshort_database_name); 38 | 39 | int count(); 40 | int count_eps(); 41 | int count_epshorts(); 42 | 43 | bool clear_database(); 44 | 45 | bool add_hashes(unordered_set hashes); 46 | bool add_hashes_eps(unordered_set hashes, unordered_set hashes_short); 47 | 48 | bool add_folder( char* dir_name, WCHAR* filter, bool recursively ); 49 | bool remove_folder( char* dir_name, WCHAR* filter, bool recursively ); 50 | bool contains(unsigned __int64 hash); 51 | bool contains_epshort(unsigned __int64 hash); 52 | bool contains_ep(unsigned __int64 hash); 53 | bool add_file(char* file); 54 | bool remove_file(char* file); 55 | bool save(); 56 | ~pe_hash_database(void); 57 | }; -------------------------------------------------------------------------------- /pd/pe_header.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "nmd_assembly.h" 4 | #include "stdafx.h" 5 | #include 6 | #include "windows.h" 7 | #include "stream_wrapper.h" 8 | #include 9 | #include "module_list.h" 10 | #include "export_list.h" 11 | #include 12 | #include 13 | #include "string.h" 14 | #include "pe_imports.h" 15 | #include 16 | #include "pe_hash_database.h" 17 | 18 | using namespace std; 19 | using namespace std::tr1; 20 | 21 | 22 | #define FILEPATH_SIZE 265 23 | 24 | #define EP_HASH_OPCODES_MIN 30 25 | #define EP_HASH_OPCODES_MAX 100 26 | 27 | // 10MB 28 | #define MAX_SECTION_SIZE (1024 * 1000) * 60 29 | 30 | class pe_hash_database; 31 | 32 | static bool static_zero_init = false; 33 | static char static_zero [100]; 34 | 35 | enum ALIGNMENT 36 | { 37 | PHYSICAL, 38 | VIRTUAL 39 | }; 40 | 41 | struct IMPORT_SUMMARY 42 | { 43 | size_t COUNT_UNIQUE_IMPORT_ADDRESSES; 44 | size_t COUNT_UNIQUE_IMPORT_LIBRARIES; 45 | unsigned __int64 HASH_GENERIC; 46 | unsigned __int64 HASH_SPECIFIC; 47 | }; 48 | 49 | class pe_header 50 | { 51 | 52 | unsigned __int64 _unique_hash; 53 | unsigned __int64 _unique_hash_ep; 54 | unsigned __int64 _unique_hash_ep_short; 55 | 56 | PD_OPTIONS* _options; 57 | 58 | stream_wrapper* _stream; 59 | void* _original_base; 60 | 61 | 62 | SIZE_T _raw_header_size; 63 | unsigned char* _raw_header; 64 | SIZE_T _image_size; 65 | unsigned char* _image; 66 | SIZE_T _disk_image_size; 67 | unsigned char* _disk_image; 68 | 69 | 70 | // Extracted current and original filename information about this module 71 | SIZE_T _name_filepath_short_size; 72 | char* _name_filepath_short; 73 | SIZE_T _name_filepath_long_size; 74 | char* _name_filepath_long; 75 | SIZE_T _name_original_exports_size; 76 | char* _name_original_exports; 77 | SIZE_T _name_original_manifest_size; 78 | char* _name_original_manifest; 79 | SIZE_T _name_symbols_path_size; 80 | char* _name_symbols_path; 81 | 82 | 83 | bool _parsed_dos; 84 | IMAGE_DOS_HEADER* _header_dos; 85 | 86 | bool _parsed_pe_32; 87 | IMAGE_NT_HEADERS32* _header_pe32; 88 | bool _parsed_pe_64; 89 | IMAGE_NT_HEADERS64* _header_pe64; 90 | int _correction_offset; 91 | 92 | // Import table 93 | int _header_import_descriptors_count; 94 | IMAGE_IMPORT_DESCRIPTOR* _header_import_descriptors; 95 | 96 | // Export table 97 | IMAGE_EXPORT_DIRECTORY* _header_export_directory; 98 | export_list* _export_list; 99 | 100 | bool _parsed_sections; 101 | int _num_sections; 102 | IMAGE_SECTION_HEADER* _header_sections; 103 | DWORD* _header_section_sizes; 104 | 105 | bool _test_read( unsigned char* buffer, SIZE_T length, unsigned char* read_ptr, SIZE_T read_length ); 106 | 107 | unsigned __int64 _hash_asm(SIZE_T offset); 108 | unsigned __int64 _hash_short_asm(SIZE_T offset); 109 | 110 | 111 | DWORD _section_align( DWORD address, DWORD alignment); 112 | __int64 _section_align( __int64 address, DWORD alignment); 113 | 114 | public: 115 | pe_header( char* filename, PD_OPTIONS* options ); 116 | pe_header( DWORD pid, void* base, module_list* modules, PD_OPTIONS* options ); 117 | pe_header( DWORD pid, module_list* modules, PD_OPTIONS* options ); 118 | pe_header( HANDLE ph, void* base, module_list* modules, PD_OPTIONS* options ); 119 | 120 | bool build_pe_header( __int64 size, bool amd64 ); 121 | bool build_pe_header( __int64 size, bool amd64, int num_sections_limit ); 122 | 123 | bool process_pe_header( ); 124 | bool process_import_directory( ); 125 | bool process_export_directory( ); 126 | bool process_relocation_directory(); 127 | bool process_sections( ); 128 | bool process_disk_image(export_list* exports, pe_hash_database* hash_database); 129 | bool process_hash( ); 130 | bool process_hash_ep( ); 131 | 132 | bool somewhat_parsed(); 133 | 134 | unsigned __int64 get_hash(); 135 | unsigned __int64 get_hash_ep(); 136 | unsigned __int64 get_hash_ep_short(); 137 | 138 | IMPORT_SUMMARY get_imports_information( export_list* exports ); 139 | IMPORT_SUMMARY get_imports_information( export_list* exports, __int64 size_limit ); 140 | 141 | bool write_image( char* filename ); 142 | 143 | export_list* get_exports(); 144 | unsigned __int64 get_virtual_size(); 145 | 146 | bool is_64(); 147 | bool is_dll(); 148 | bool is_exe(); 149 | bool is_sys(); 150 | char* get_name(); 151 | void set_name(char* new_name); 152 | 153 | void print_report( FILE* stream ); 154 | 155 | __int64 get_export_addresses(); 156 | 157 | ~pe_header(void); 158 | }; 159 | -------------------------------------------------------------------------------- /pd/pe_imports.cpp: -------------------------------------------------------------------------------- 1 | #include "StdAfx.h" 2 | #include "pe_imports.h" 3 | 4 | void pe_imports::add_fixup(char* library_name, int ordinal, __int64 rva, bool win64) 5 | { 6 | this->_libraries.Add(new import_library(library_name, ordinal, rva, win64)); 7 | } 8 | 9 | void pe_imports::add_fixup(char* library_name, char* proc_name, __int64 rva, bool win64) 10 | { 11 | this->_libraries.Add(new import_library(library_name, proc_name, rva, win64)); 12 | } 13 | 14 | 15 | void pe_imports::get_table_size(__int64 &descriptor_size, __int64 &extra_size) 16 | { 17 | for( int i = 0; i < _libraries.GetSize(); i++ ) 18 | { 19 | _libraries[i]->get_table_size( descriptor_size, extra_size ); 20 | } 21 | descriptor_size += sizeof(IMAGE_IMPORT_DESCRIPTOR); 22 | } 23 | 24 | bool pe_imports::build_table(unsigned char* section, __int64 section_size, __int64 section_rva, __int64 descriptor_offset, __int64 extra_offset) 25 | { 26 | // Build the import table in the new section 27 | bool retval = true; 28 | for( int i = 0; i < _libraries.GetSize(); i++ ) 29 | { 30 | if( !_libraries[i]->build_table(section, section_size, section_rva, descriptor_offset, extra_offset) ) 31 | retval = false; 32 | } 33 | 34 | // Write the final descriptor 35 | IMAGE_IMPORT_DESCRIPTOR blank_descrptor; 36 | memset( &blank_descrptor, 0, sizeof(IMAGE_IMPORT_DESCRIPTOR) ); 37 | 38 | if( test_read( section, section_size, section + descriptor_offset , sizeof(IMAGE_IMPORT_DESCRIPTOR) ) ) 39 | memcpy( section + descriptor_offset, &blank_descrptor, sizeof(IMAGE_IMPORT_DESCRIPTOR) ); 40 | 41 | 42 | return retval; 43 | } 44 | 45 | pe_imports::pe_imports(unsigned char* image, __int64 image_size, IMAGE_IMPORT_DESCRIPTOR* imports, bool win64) 46 | { 47 | _win64 = win64; 48 | 49 | // Process the import descriptor directory 50 | bool more; 51 | int i = 0; 52 | do 53 | { 54 | more = false; 55 | if( test_read( image, image_size, ((unsigned char*)imports) + i * sizeof(IMAGE_IMPORT_DESCRIPTOR ), 56 | sizeof(IMAGE_IMPORT_DESCRIPTOR ) ) ) 57 | { 58 | IMAGE_IMPORT_DESCRIPTOR* current = &((IMAGE_IMPORT_DESCRIPTOR*) imports)[i]; 59 | if( current->Characteristics != 0 || current->FirstThunk != 0 || current->ForwarderChain != 0 || current->Name != 0 ) 60 | { 61 | this->add_descriptor(current); 62 | more = true; 63 | i++; 64 | } 65 | } 66 | }while(more); 67 | } 68 | 69 | void pe_imports::add_descriptor(IMAGE_IMPORT_DESCRIPTOR* descriptor) 70 | { 71 | this->_libraries.Add( new import_library(descriptor, _win64) ); 72 | } 73 | 74 | pe_imports::~pe_imports(void) 75 | { 76 | } 77 | 78 | import_library::~import_library(void) 79 | { 80 | delete _descriptor; 81 | 82 | if( _import_by_name != NULL ) 83 | delete[] _import_by_name; 84 | 85 | if( _library_name != NULL ) 86 | delete[] _library_name; 87 | 88 | if( _thunk_entry != NULL ) 89 | delete _thunk_entry; 90 | } 91 | 92 | import_library::import_library(IMAGE_IMPORT_DESCRIPTOR* descriptor, bool win64) 93 | { 94 | // Localize the descriptor, TODO: and the referenced data maybe 95 | _descriptor = new IMAGE_IMPORT_DESCRIPTOR(*descriptor); 96 | _import_by_name = NULL; 97 | _library_name = NULL; 98 | _thunk_entry = NULL; 99 | } 100 | 101 | import_library::import_library(char* library_name, int ordinal, __int64 rva, bool win64) 102 | { 103 | // Create an import library to fixup a specific rva to the ordinal 104 | _descriptor = new IMAGE_IMPORT_DESCRIPTOR(); 105 | _descriptor->OriginalFirstThunk = NULL; // Replace with rva to this->thunk_entry when outputting 106 | _descriptor->TimeDateStamp = -1; 107 | _descriptor->ForwarderChain = -1; 108 | _descriptor->Name = NULL; // Replace with rva to name upon writing 109 | _descriptor->FirstThunk = rva; // PE Loader with patchup address at rva to become the address of the import, awesome! 110 | _import_by_name = NULL; 111 | _thunk_entry = new IMAGE_THUNK_DATA64(); 112 | 113 | _library_name = new char[strlen(library_name)+1]; 114 | strcpy(_library_name, library_name); 115 | 116 | // Ordinal import 117 | if( win64 ) 118 | _thunk_entry->u1.Ordinal = IMAGE_ORDINAL_FLAG64 | (ordinal & 0xffff); 119 | else 120 | _thunk_entry->u1.Ordinal = IMAGE_ORDINAL_FLAG32 | (ordinal & 0xffff); 121 | } 122 | 123 | import_library::import_library(char* library_name, char* proc_name, __int64 rva, bool win64) 124 | { 125 | // Create an import library to fixup a specific rva to the ordinal 126 | _descriptor = new IMAGE_IMPORT_DESCRIPTOR(); 127 | _descriptor->OriginalFirstThunk = NULL; // Replace with rva to this->thunk_entry when outputting 128 | _descriptor->TimeDateStamp = -1; 129 | _descriptor->ForwarderChain = -1; 130 | _descriptor->Name = NULL; // Replace with rva to name upon writing 131 | _descriptor->FirstThunk = rva; // PE Loader with patchup address at rva to become the address of the import, awesome! 132 | _thunk_entry = new IMAGE_THUNK_DATA64(); 133 | 134 | _library_name = new char[strlen(library_name)+1]; 135 | strcpy(_library_name, library_name); 136 | 137 | // Name import 138 | _thunk_entry->u1.AddressOfData = NULL; // Replace with rva to import_by_name 139 | 140 | _import_by_name = (IMAGE_IMPORT_BY_NAME*) new char[strlen(proc_name)+1+sizeof(WORD)]; // {DWORD, procedure name} 141 | _import_by_name->Hint = 0; // Not necessary 142 | _import_by_name_len = strlen(proc_name)+1+sizeof(WORD); 143 | strcpy((char*)(&_import_by_name->Name), proc_name); 144 | } 145 | 146 | void import_library::get_table_size(__int64 &descriptor_size, __int64 &extra_size) 147 | { 148 | // Calculate the size required 149 | 150 | // Write the _import_by_name name if necessary 151 | if( _import_by_name != NULL ) 152 | { 153 | extra_size += _import_by_name_len; 154 | } 155 | 156 | // Write the _thunk_entry IMAGE_THUNK_DATA64 struct, and update it 157 | if( _thunk_entry != NULL ) 158 | { 159 | extra_size += 2 * sizeof(IMAGE_THUNK_DATA64); 160 | } 161 | 162 | // Patch up the library name 163 | if( _library_name != NULL ) 164 | { 165 | extra_size += strlen(_library_name) + 1; 166 | } 167 | 168 | // Copy over the descriptor 169 | descriptor_size += sizeof(IMAGE_IMPORT_DESCRIPTOR); 170 | } 171 | 172 | bool import_library::build_table(unsigned char* section, __int64 section_size, __int64 section_rva, __int64 &descriptor_offset, __int64 &extra_offset) 173 | { 174 | // Output this import table into the specified section blob 175 | 176 | // Write the _import_by_name name if necessary 177 | __int64 import_name_rva = 0; 178 | if( _import_by_name != NULL ) 179 | { 180 | if( !test_read(section, section_size, extra_offset + section, _import_by_name_len ) ) 181 | return false; // Not enough room 182 | 183 | memcpy(extra_offset + section, (char*)_import_by_name, _import_by_name_len); 184 | import_name_rva = extra_offset + section_rva; 185 | extra_offset += _import_by_name_len; 186 | } 187 | 188 | // Write the _thunk_entry IMAGE_THUNK_DATA64 struct, and update it 189 | __int64 thunk_entry_rva = 0; 190 | if( _thunk_entry != NULL ) 191 | { 192 | if( !test_read(section, section_size, extra_offset + section, sizeof(IMAGE_THUNK_DATA64)*2 ) ) 193 | return false; 194 | 195 | if( import_name_rva != NULL ) 196 | _thunk_entry->u1.AddressOfData = import_name_rva; 197 | 198 | memcpy(extra_offset + section, (char*) _thunk_entry, sizeof(IMAGE_THUNK_DATA64)); 199 | thunk_entry_rva = extra_offset + section_rva; 200 | 201 | // Copy a null _thunk_entry terminator. For a 32 bit module, we are wasting a DWORD for the 64 structure, but that's fine. 202 | memset(extra_offset + section + sizeof(IMAGE_THUNK_DATA64), 0, sizeof(IMAGE_THUNK_DATA64)); 203 | 204 | extra_offset += 2*sizeof(IMAGE_THUNK_DATA64); 205 | } 206 | 207 | // Update and write the IMAGE_IMPORT_DESCRIPTOR 208 | if( _descriptor == NULL ) 209 | return false; 210 | if( !test_read(section, section_size, descriptor_offset + section, sizeof(IMAGE_IMPORT_DESCRIPTOR)) ) 211 | return false; // Not enough room 212 | 213 | if( thunk_entry_rva != NULL ) 214 | _descriptor->OriginalFirstThunk = thunk_entry_rva; // Redirect this to our thunk rva 215 | 216 | // Patch up the library name 217 | if( _library_name != NULL ) 218 | { 219 | if( !test_read(section, section_size, extra_offset + section, strlen(_library_name) + 1) ) 220 | return false; // Not enough room 221 | 222 | strcpy((char*)(extra_offset + section), _library_name); 223 | _descriptor->Name = extra_offset + section_rva; 224 | 225 | extra_offset += strlen(_library_name) + 1; 226 | } 227 | 228 | // Copy over the descriptor 229 | memcpy(descriptor_offset + section, (unsigned char*)_descriptor, sizeof(IMAGE_IMPORT_DESCRIPTOR)); 230 | descriptor_offset += sizeof(IMAGE_IMPORT_DESCRIPTOR); 231 | 232 | return true; 233 | } -------------------------------------------------------------------------------- /pd/pe_imports.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "windows.h" 3 | #include "DynArray.h" 4 | #include "utils.h" 5 | 6 | #define IMAGE_ORDINAL_FLAG64 0x8000000000000000ULL 7 | #define IMAGE_ORDINAL_FLAG32 0x80000000 8 | 9 | class import_library 10 | { 11 | char* _library_name; 12 | 13 | IMAGE_IMPORT_DESCRIPTOR* _descriptor; 14 | IMAGE_THUNK_DATA64* _thunk_entry; // Use 64 bit definition for both 32 and 64 bit modules. 15 | IMAGE_IMPORT_BY_NAME* _import_by_name; // IMAGE_IMPORT_BY_NAME 16 | int _import_by_name_len; 17 | 18 | public: 19 | import_library(IMAGE_IMPORT_DESCRIPTOR* descriptor, bool win64); 20 | import_library(char* library_name, int ordinal, __int64 rva, bool win64); 21 | import_library(char* library_name, char* proc_name, __int64 rva, bool win64); 22 | 23 | bool build_table(unsigned char* section, __int64 section_size, __int64 section_rva, __int64 &descriptor_offset, __int64 &extra_offset); 24 | void get_table_size(__int64 &descriptor_size, __int64 &extra_size); 25 | char* GetName(); 26 | ~import_library(void); 27 | }; 28 | 29 | class pe_imports 30 | { 31 | bool _win64; 32 | DynArray _libraries; 33 | public: 34 | pe_imports(unsigned char* image, __int64 image_size, IMAGE_IMPORT_DESCRIPTOR* imports, bool win64); 35 | void add_descriptor(IMAGE_IMPORT_DESCRIPTOR* descriptor); 36 | bool build_table(unsigned char* section, __int64 section_size, __int64 section_rva, __int64 descriptor_offset, __int64 extra_offset); 37 | void get_table_size(__int64 &descriptor_size, __int64 &extra_size); 38 | 39 | void add_fixup(char* library_name, int ordinal, __int64 rva, bool win64); 40 | void add_fixup(char* library_name, char* proc_name, __int64 rva, bool win64); 41 | //char* build_table(); 42 | ~pe_imports(void); 43 | }; 44 | 45 | 46 | -------------------------------------------------------------------------------- /pd/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by pd.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /pd/simple.cpp: -------------------------------------------------------------------------------- 1 | #include "StdAfx.h" 2 | #include "simple.h" 3 | 4 | 5 | DWORD process_find(string match_regex, DynArray* result) 6 | { 7 | PROCESSENTRY32 entry; 8 | entry.dwSize = sizeof(PROCESSENTRY32); 9 | HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); 10 | 11 | if( snapshot != INVALID_HANDLE_VALUE ) 12 | { 13 | if (Process32First(snapshot, &entry) == TRUE) 14 | { 15 | while (Process32Next(snapshot, &entry) == TRUE) 16 | { 17 | char* process_name = new char[wcslen(entry.szExeFile)+1]; 18 | sprintf( process_name, "%S", entry.szExeFile ); 19 | 20 | string name (process_name); 21 | try 22 | { 23 | regex reg (match_regex); 24 | if( regex_match( name, reg ) ) 25 | { 26 | // Record this as a matching process 27 | result->Add( new process_description( process_name, entry.th32ProcessID ) ); 28 | } 29 | } 30 | catch( std::tr1::regex_error e ) 31 | { 32 | fprintf( stderr, "ERROR: Invalid regex expression for matching process names." ); 33 | return 0; 34 | } 35 | 36 | 37 | } 38 | } 39 | 40 | CloseHandle(snapshot); 41 | } 42 | return result->GetSize(); 43 | } 44 | 45 | string ExePath() { 46 | char buffer[MAX_PATH]; 47 | GetModuleFileNameA( NULL, buffer, MAX_PATH ); 48 | string::size_type pos = string( buffer ).find_last_of( "\\/" ); 49 | return string( buffer ).substr( 0, pos); 50 | } 51 | 52 | void PrintLastError(LPTSTR lpszFunction) 53 | { 54 | // Retrieve the system error message for the last-error code 55 | LPVOID lpMsgBuf; 56 | LPVOID lpDisplayBuf; 57 | DWORD dw = GetLastError(); 58 | 59 | FormatMessage( 60 | FORMAT_MESSAGE_ALLOCATE_BUFFER | 61 | FORMAT_MESSAGE_FROM_SYSTEM | 62 | FORMAT_MESSAGE_IGNORE_INSERTS, 63 | NULL, 64 | dw, 65 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 66 | (LPTSTR) &lpMsgBuf, 67 | 0, NULL ); 68 | 69 | // Display the error message and exit the process 70 | lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 71 | (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); 72 | StringCchPrintf((LPTSTR)lpDisplayBuf, 73 | LocalSize(lpDisplayBuf) / sizeof(TCHAR), 74 | TEXT("%s failed with error %d: %s"), 75 | lpszFunction, dw, lpMsgBuf); 76 | 77 | fwprintf(stderr,(LPCTSTR) lpDisplayBuf ); 78 | 79 | LocalFree(lpMsgBuf); 80 | LocalFree(lpDisplayBuf); 81 | } -------------------------------------------------------------------------------- /pd/simple.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "windows.h" 8 | #include 9 | #include 10 | #include 11 | #include "DynArray.h" 12 | 13 | using namespace std; 14 | using namespace std::tr1; 15 | 16 | class PD_OPTIONS 17 | { 18 | public: 19 | bool ImportRec; 20 | bool ForceGenHeader; 21 | bool Verbose; 22 | bool ReconstructHeaderAsDll; 23 | bool DumpChunks; // Dump loose code chunks 24 | bool EntryPointHash; 25 | bool ForceReconstructEntryPoint; 26 | int NumberOfThreads; 27 | 28 | __int64 EntryPointOverride; 29 | char* output_path; 30 | 31 | PD_OPTIONS() 32 | { 33 | output_path = new char[1]; 34 | strcpy(output_path,""); 35 | } 36 | 37 | void set_output_path( char* path ) 38 | { 39 | if( output_path != NULL ) 40 | delete[] output_path; 41 | 42 | output_path = new char[strlen(path) + 1]; 43 | strcpy_s( output_path, strlen(path) + 1, path); 44 | } 45 | 46 | ~PD_OPTIONS() 47 | { 48 | if( output_path != NULL ) 49 | { 50 | delete[] output_path; 51 | } 52 | } 53 | }; 54 | 55 | class process_description 56 | { 57 | public: 58 | char* process_name; 59 | DWORD pid; 60 | 61 | process_description(char* name, DWORD pid) 62 | { 63 | process_name = new char[strlen(name)+1]; 64 | strcpy( process_name, name ); 65 | this->pid = pid; 66 | } 67 | 68 | ~process_description() 69 | { 70 | delete[] process_name; 71 | } 72 | }; 73 | 74 | DWORD process_find(string match_regex, DynArray* result); 75 | string ExePath(); 76 | void PrintLastError(LPTSTR lpszFunction); -------------------------------------------------------------------------------- /pd/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // pd.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | 7 | // TODO: reference any additional headers you need in STDAFX.H 8 | // and not in this file 9 | -------------------------------------------------------------------------------- /pd/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #include 11 | #include 12 | 13 | 14 | 15 | // TODO: reference additional headers your program requires here 16 | -------------------------------------------------------------------------------- /pd/stream_wrapper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "windows.h" 5 | #include "simple.h" 6 | #include 7 | #include "module_list.h" 8 | 9 | 10 | // A stream class that wraps reading from either a file or process memory offset. 11 | class stream_wrapper 12 | { 13 | public: 14 | bool file_alignment; 15 | virtual SIZE_T block_size( long offset ) = 0; 16 | virtual bool read( long offset, SIZE_T size, unsigned char* output, SIZE_T* out_read ) = 0; 17 | virtual SIZE_T get_short_name( char* out_name, SIZE_T out_name_size ) = 0; 18 | virtual SIZE_T get_long_name( char* out_name, SIZE_T out_name_size ) = 0; 19 | virtual SIZE_T get_location( char* out_name, SIZE_T out_name_size ) = 0; 20 | virtual __int64 get_address() = 0; 21 | virtual __int64 estimate_section_size( long offset ) = 0; 22 | virtual DWORD get_region_characteristics( long offset ) = 0; 23 | virtual ~stream_wrapper() {} 24 | virtual void update_base( __int64 rva ) = 0; 25 | }; 26 | 27 | 28 | class file_stream : stream_wrapper 29 | { 30 | char* _filename; 31 | 32 | bool opened; 33 | FILE* fh; 34 | public: 35 | 36 | file_stream(char* filename) 37 | { 38 | // Localize the filename 39 | _filename = new char[ strlen(filename) + 1 ]; 40 | strcpy( _filename, filename ); 41 | 42 | // Set the input stream as a file 43 | fh = fopen(filename, "rb"); 44 | file_alignment = true; 45 | 46 | if( fh != NULL ) 47 | opened = true; 48 | else 49 | { 50 | PrintLastError(L"Failed to open file."); 51 | opened = false; 52 | } 53 | } 54 | 55 | virtual void update_base( __int64 rva ) 56 | { 57 | // nothing 58 | } 59 | 60 | virtual __int64 get_address( ) 61 | { 62 | return 0; 63 | } 64 | 65 | virtual __int64 estimate_section_size( long offset ) 66 | { 67 | return 0; 68 | } 69 | 70 | virtual SIZE_T get_location( char* out_name, SIZE_T out_name_size ) 71 | { 72 | return get_long_name( out_name, out_name_size ); 73 | } 74 | 75 | virtual SIZE_T get_long_name( char* out_name, SIZE_T out_name_size ) 76 | { 77 | // Return the path of this file on disk 78 | SIZE_T length = strlen(_filename) + 1; 79 | if( length > out_name_size ) 80 | length = out_name_size; 81 | memcpy( out_name, _filename, length ); 82 | out_name[length-1] = 0; 83 | 84 | return length - 1; 85 | } 86 | 87 | virtual SIZE_T get_short_name( char* out_name, SIZE_T out_name_size ) 88 | { 89 | char fname[_MAX_FNAME]; 90 | char ext[_MAX_EXT]; 91 | char short_name[_MAX_FNAME + _MAX_EXT + 1]; 92 | _splitpath( _filename, NULL, NULL, fname, ext ); 93 | sprintf( short_name, "%s.%s", fname, ext ); 94 | 95 | SIZE_T length = strlen(short_name) + 1; 96 | if( length > out_name_size ) 97 | length = out_name_size; 98 | memcpy( out_name, short_name, length ); 99 | out_name[length-1] = 0; 100 | 101 | return length - 1; 102 | } 103 | 104 | 105 | virtual SIZE_T block_size( long offset ) 106 | { 107 | if( opened ) 108 | { 109 | if( !fseek( fh, 0, SEEK_END) ) 110 | return ftell( fh ) - offset; 111 | else 112 | PrintLastError(L"Seek failed."); 113 | } 114 | return 0; 115 | } 116 | 117 | virtual DWORD get_region_characteristics( long offset ) 118 | { 119 | return IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE; 120 | } 121 | 122 | virtual bool read( long offset, SIZE_T size, unsigned char* output, SIZE_T* out_read ) 123 | { 124 | *out_read = 0; 125 | 126 | if( opened ) 127 | { 128 | if( !fseek( fh, offset, SEEK_SET) ) 129 | { 130 | *out_read = fread( output, 1, size, fh ); 131 | if( *out_read == size ) 132 | return true; 133 | } 134 | } 135 | return false; 136 | } 137 | 138 | ~file_stream(void) 139 | { 140 | if( opened ) 141 | fclose( fh ); 142 | if( _filename != NULL ) 143 | delete[] _filename; 144 | } 145 | }; 146 | 147 | 148 | 149 | class process_stream : stream_wrapper 150 | { 151 | bool opened; 152 | HANDLE ph; 153 | 154 | char* _long_name; 155 | char* _short_name; 156 | 157 | void init( HANDLE ph, void* base, module_list* modules ) 158 | { 159 | file_alignment = false; 160 | this->ph = ph; 161 | _long_name = NULL; 162 | _short_name = NULL; 163 | if( ph != NULL ) 164 | { 165 | opened = true; 166 | this->base = base; 167 | 168 | // Copy this long and short name 169 | unordered_map::const_iterator item = modules->_modules.find( (unsigned __int64) base ); 170 | if( item != modules->_modules.end() ) 171 | { 172 | _long_name = new char[260]; 173 | _short_name = new char[256]; 174 | strcpy( _long_name, ((module*)item->second)->full_name ); 175 | strcpy( _short_name, ((module*)item->second)->short_name ); 176 | } 177 | } 178 | else 179 | opened = false; 180 | } 181 | 182 | public: 183 | void* base; 184 | 185 | 186 | process_stream(HANDLE ph, void* base) 187 | { 188 | _long_name = NULL; 189 | _short_name = NULL; 190 | file_alignment = false; 191 | this->ph = ph; 192 | if( ph != NULL ) 193 | { 194 | opened = true; 195 | this->base = base; 196 | } 197 | else 198 | opened = false; 199 | } 200 | 201 | process_stream(HANDLE ph, void* base, module_list* modules ) 202 | { 203 | _long_name = NULL; 204 | _short_name = NULL; 205 | init( ph, base, modules ); 206 | } 207 | 208 | process_stream(DWORD pid, module_list* modules) 209 | { 210 | _long_name = NULL; 211 | _short_name = NULL; 212 | 213 | // Try to open the specified process pid and use the main module as the base 214 | file_alignment = false; 215 | ph = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); 216 | opened = false; 217 | 218 | if( ph != NULL ) 219 | { 220 | HANDLE hSnapshot=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); 221 | if( hSnapshot != INVALID_HANDLE_VALUE ) 222 | { 223 | MODULEENTRY32 tmpModule; 224 | tmpModule.dwSize = sizeof(MODULEENTRY32); 225 | if( Module32First(hSnapshot, &tmpModule) ) 226 | { 227 | opened = true; 228 | this->base = tmpModule.modBaseAddr; 229 | } 230 | CloseHandle( hSnapshot ); 231 | 232 | init( ph, base, modules ); 233 | } 234 | else 235 | { 236 | if( GetLastError() == 299 ) 237 | fprintf(stderr, "ERROR: Unable to open process PID 0x%x since it is a 64 bit process and this tool is running as a 32 bit process.\n", pid); 238 | else 239 | PrintLastError(L"create_process_stream CreateToolhelp32Snapshot"); 240 | } 241 | } 242 | else 243 | { 244 | fprintf(stderr, "Failed to open process with PID 0x%x:\n", pid ); 245 | PrintLastError(L"\tcreate_process_stream"); 246 | } 247 | } 248 | 249 | process_stream(DWORD pid, void* base, module_list* modules ) 250 | { 251 | // Try to open the specified process pid 252 | _long_name = NULL; 253 | _short_name = NULL; 254 | file_alignment = false; 255 | opened = false; 256 | ph = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); 257 | 258 | if( ph != NULL ) 259 | { 260 | init( ph, base, modules ); 261 | } 262 | else 263 | { 264 | fprintf(stderr, "Failed to open process with PID 0x%x:\n", pid ); 265 | PrintLastError(L"\tcreate_process_stream"); 266 | } 267 | } 268 | 269 | virtual SIZE_T get_location( char* out_name, SIZE_T out_name_size ) 270 | { 271 | char* hex = new char[16 + 2 + 1]; // Max space required 272 | int hexLength = sprintf( hex, "0x%llX", (__int64) this->base ); 273 | 274 | if( hexLength < out_name_size ) 275 | { 276 | memcpy( out_name, hex, hexLength ); 277 | out_name[hexLength] = 0; 278 | delete[] hex; 279 | return hexLength; 280 | } 281 | delete[] hex; 282 | return 0; 283 | } 284 | 285 | virtual SIZE_T get_short_name( char* out_name, SIZE_T out_name_size ) 286 | { 287 | if( _short_name != NULL ) 288 | { 289 | SIZE_T length = strlen( _short_name ) + 1; 290 | if( length > out_name_size ) 291 | length = out_name_size; 292 | memcpy( out_name, _short_name, length ); 293 | out_name[length-1] = 0; 294 | 295 | return length; 296 | } 297 | return 0; 298 | } 299 | 300 | virtual SIZE_T get_long_name( char* out_name, SIZE_T out_name_size ) 301 | { 302 | if( _long_name != NULL ) 303 | { 304 | SIZE_T length = strlen( _long_name ) + 1; 305 | if( length > out_name_size ) 306 | length = out_name_size; 307 | memcpy( out_name, _long_name, length ); 308 | out_name[length-1] = 0; 309 | 310 | return length; 311 | } 312 | 313 | return 0; 314 | } 315 | 316 | virtual SIZE_T block_size( long offset ) 317 | { 318 | if( opened ) 319 | { 320 | _MEMORY_BASIC_INFORMATION64 mbi; 321 | __int64 blockSize = VirtualQueryEx(ph, (LPCVOID) ((unsigned char*)base + (SIZE_T)offset), (PMEMORY_BASIC_INFORMATION) &mbi, sizeof(_MEMORY_BASIC_INFORMATION64)); 322 | 323 | if( blockSize == sizeof(_MEMORY_BASIC_INFORMATION64) ) 324 | { 325 | return mbi.RegionSize - offset; 326 | } 327 | else if( blockSize == sizeof(_MEMORY_BASIC_INFORMATION32) ) 328 | { 329 | _MEMORY_BASIC_INFORMATION32* mbi32 = (_MEMORY_BASIC_INFORMATION32*) &mbi; 330 | return mbi32->RegionSize - offset; 331 | } 332 | else if( blockSize == 0 ) 333 | { 334 | PrintLastError(L"VirtualQueryEx query block size"); 335 | } 336 | } 337 | return 0; 338 | } 339 | 340 | virtual DWORD get_region_characteristics( long offset ) 341 | { 342 | DWORD characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE; 343 | if( opened ) 344 | { 345 | _MEMORY_BASIC_INFORMATION64 mbi; 346 | __int64 blockSize = VirtualQueryEx(ph, (LPCVOID) ((unsigned char*)base + (SIZE_T)offset), (_MEMORY_BASIC_INFORMATION*) &mbi, sizeof(_MEMORY_BASIC_INFORMATION64)); 347 | 348 | if( blockSize == sizeof(_MEMORY_BASIC_INFORMATION64) ) 349 | { 350 | if( mbi.State == MEM_COMMIT && !(mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD)) ) 351 | { 352 | if( mbi.AllocationProtect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE ) ) 353 | characteristics |= IMAGE_SCN_MEM_EXECUTE; 354 | } 355 | } 356 | else if( blockSize == sizeof(_MEMORY_BASIC_INFORMATION32) ) 357 | { 358 | _MEMORY_BASIC_INFORMATION32* mbi32 = (_MEMORY_BASIC_INFORMATION32*) &mbi; 359 | if( mbi32->State == MEM_COMMIT && !(mbi32->Protect & (PAGE_NOACCESS | PAGE_GUARD)) ) 360 | { 361 | if( mbi32->AllocationProtect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE ) ) 362 | characteristics |= IMAGE_SCN_MEM_EXECUTE; 363 | } 364 | } 365 | } 366 | 367 | return characteristics; 368 | } 369 | 370 | virtual __int64 estimate_section_size( long offset ) 371 | { 372 | // Estimate the section size according to the heap size and privilege level 373 | if( opened ) 374 | { 375 | _MEMORY_BASIC_INFORMATION64 mbi; 376 | __int64 blockSize = VirtualQueryEx(ph, (LPCVOID) ((unsigned char*)base + (SIZE_T)offset), (_MEMORY_BASIC_INFORMATION*) &mbi, sizeof(_MEMORY_BASIC_INFORMATION64)); 377 | 378 | if( blockSize == sizeof(_MEMORY_BASIC_INFORMATION64) ) 379 | { 380 | if( mbi.State == MEM_COMMIT && !(mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD)) ) 381 | { 382 | // Good region 383 | return mbi.RegionSize; 384 | } 385 | } 386 | else if( blockSize == sizeof(_MEMORY_BASIC_INFORMATION32) ) 387 | { 388 | _MEMORY_BASIC_INFORMATION32* mbi32 = (_MEMORY_BASIC_INFORMATION32*) &mbi; 389 | if( mbi32->State == MEM_COMMIT && !(mbi32->Protect & (PAGE_NOACCESS | PAGE_GUARD)) ) 390 | { 391 | return mbi32->RegionSize; 392 | } 393 | } 394 | } 395 | 396 | return 0; // Not valid 397 | } 398 | 399 | virtual void update_base( __int64 rva ) 400 | { 401 | this->base = (void*) ((__int64)base + rva); 402 | } 403 | 404 | virtual __int64 get_address( ) 405 | { 406 | return (__int64) this->base; 407 | } 408 | 409 | virtual bool read( long offset, SIZE_T size, unsigned char* output, SIZE_T* out_read ) 410 | { 411 | // Reads in memory by region. Skips noaccess, guard, and failures leaving the corresponding 412 | // parts of the output buffer untouched. 413 | *out_read = 0; 414 | 415 | SIZE_T num_read = 0; 416 | 417 | __int64 already_read = 0; 418 | 419 | if( opened ) 420 | { 421 | while( already_read < size ) 422 | { 423 | _MEMORY_BASIC_INFORMATION64 mbi; 424 | __int64 blockSize = VirtualQueryEx(ph, (LPCVOID) ((unsigned char*)base + (SIZE_T)offset + (SIZE_T)already_read), (_MEMORY_BASIC_INFORMATION*) &mbi, sizeof(_MEMORY_BASIC_INFORMATION64)); 425 | __int64 start_address = already_read + (__int64)base + offset; 426 | 427 | if( blockSize == sizeof(_MEMORY_BASIC_INFORMATION64) ) 428 | { 429 | if( mbi.State == MEM_COMMIT && !(mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD)) ) 430 | { 431 | // Read in this whole or part of this region 432 | bool success; 433 | if( start_address + size - already_read >= mbi.BaseAddress + mbi.RegionSize ) 434 | { 435 | // Read in the whole region 436 | success = ReadProcessMemory( ph, 437 | (LPCVOID) (start_address), 438 | (void*)((__int64) output + already_read), 439 | mbi.RegionSize, 440 | &num_read); 441 | already_read += mbi.RegionSize; 442 | *out_read += num_read; 443 | } 444 | else 445 | { 446 | // Read in the partial region 447 | success = ReadProcessMemory( ph, 448 | (LPCVOID) (start_address), 449 | (void*)((__int64) output + already_read), 450 | size - already_read, 451 | &num_read); 452 | already_read += size - already_read; 453 | *out_read += num_read; 454 | } 455 | } 456 | else 457 | { 458 | // Guard or noaccess, skip this region 459 | already_read += mbi.RegionSize - (start_address - mbi.BaseAddress); 460 | } 461 | } 462 | else if( blockSize == sizeof(_MEMORY_BASIC_INFORMATION32) ) 463 | { 464 | _MEMORY_BASIC_INFORMATION32* mbi32 = (_MEMORY_BASIC_INFORMATION32*) &mbi; 465 | if( mbi32->State == MEM_COMMIT && !(mbi32->Protect & (PAGE_NOACCESS | PAGE_GUARD)) ) 466 | { 467 | // Read in this whole or part of this region 468 | bool success; 469 | 470 | if( start_address + size - already_read >= mbi32->BaseAddress + mbi32->RegionSize ) 471 | { 472 | // Read in the whole region 473 | success = ReadProcessMemory( ph, 474 | (LPCVOID) (start_address), 475 | (void*)((__int64) output + already_read), 476 | mbi32->RegionSize, 477 | &num_read); 478 | already_read += mbi32->RegionSize; 479 | *out_read += num_read; 480 | } 481 | else 482 | { 483 | // Read in the partial region 484 | success = ReadProcessMemory( ph, 485 | (LPCVOID) (start_address), 486 | (void*)((__int64) output + already_read), 487 | size - already_read, 488 | &num_read); 489 | already_read += size - already_read; 490 | *out_read += num_read; 491 | } 492 | } 493 | else 494 | { 495 | // Guard or noaccess, skip this region 496 | already_read += mbi32->RegionSize - (start_address - mbi32->BaseAddress); 497 | } 498 | } 499 | else 500 | { 501 | // Failed to query MBI information, best we can do is skip 1 page? 502 | already_read += 0x1000; 503 | } 504 | } 505 | } 506 | 507 | if( *out_read != already_read ) 508 | return false; 509 | return true; 510 | } 511 | 512 | ~process_stream(void) 513 | { 514 | if( opened ) 515 | { 516 | // Close the handle 517 | //CloseHandle( ph ); SHOULD NOT DO THIS. USED BY CREATOR. 518 | 519 | if( _long_name != NULL ) 520 | delete[] _long_name; 521 | if( _short_name != NULL ) 522 | delete[] _short_name; 523 | } 524 | 525 | } 526 | }; 527 | 528 | 529 | 530 | -------------------------------------------------------------------------------- /pd/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // The following macros define the minimum required platform. The minimum required platform 4 | // is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run 5 | // your application. The macros work by enabling all features available on platform versions up to and 6 | // including the version specified. 7 | 8 | // Modify the following defines if you have to target a platform prior to the ones specified below. 9 | // Refer to MSDN for the latest info on corresponding values for different platforms. 10 | #ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. 11 | #define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. 12 | #endif 13 | 14 | -------------------------------------------------------------------------------- /pd/terminate_monitor_hook.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "terminate_monitor_hook.h" 3 | 4 | 5 | bool terminate_monitor_hook::add_redirect(unsigned __int64 target_address) 6 | { 7 | SIZE_T num_written = 0; 8 | unsigned char inject[0x20]; 9 | int num_write = 0; 10 | 11 | if (!_is64) 12 | { 13 | // near jmp to injected code 14 | unsigned char data[] = { 15 | 0xE9, 0x00, 0x00, 0x00, 0x00 16 | }; 17 | 18 | *(unsigned __int32*)(data + 1) = (unsigned __int32)(target_address - _address_terminate - 5); 19 | memcpy(inject, data, sizeof(data)); 20 | num_write = sizeof(data); 21 | } 22 | else 23 | { 24 | // 64-bit code 25 | unsigned char data[] = { 26 | // call +0 27 | 0xE8, 0x00, 0x00, 0x00, 0x00, 28 | 29 | // pop rax 30 | 0x58, 31 | 32 | // add rax, 0x7 33 | 0x48, 0x83, 0xC0, 0x07, 34 | 35 | // jmp [rax] 36 | 0xFF, 0x20, 37 | 38 | // 39 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 40 | }; 41 | 42 | *(unsigned __int64*)(data + 12) = (unsigned __int64)(target_address); 43 | memcpy(inject, data, sizeof(data)); 44 | num_write = sizeof(data); 45 | } 46 | 47 | 48 | // Write the redirect 49 | bool success = WriteProcessMemory(_ph, (LPVOID) _address_terminate, inject, num_write, &num_written); 50 | if (success && num_write == num_written) 51 | { 52 | return true; 53 | } 54 | 55 | return false; 56 | } 57 | 58 | bool terminate_monitor_hook::unhock_terminate() 59 | { 60 | if (_address_terminate != NULL && _original_hook_bytes != NULL) 61 | { 62 | // Write the original code back 63 | SIZE_T num_written = 0; 64 | bool success = WriteProcessMemory(_ph, (LPVOID) _address_terminate, _original_hook_bytes, sizeof(_original_hook_bytes), &num_written); 65 | 66 | // Handle any stuck threads that are waiting to terminate 67 | if (is_terminate_waiting()) 68 | { 69 | resume_terminate(); 70 | 71 | // no need to cleanup hook allocation since process is terminating 72 | } 73 | else 74 | { 75 | // Remove our hook allocation 76 | if (!_process_is_terminating && _hook_address != NULL) 77 | VirtualFreeEx(_ph, (LPVOID)_hook_address, 0, MEM_RELEASE); 78 | } 79 | 80 | _hook_address = NULL; 81 | _address_is_waiting = NULL; 82 | _address_thread_id = NULL; 83 | } 84 | 85 | return true; 86 | } 87 | 88 | unsigned __int64 terminate_monitor_hook::get_address(char * library, char * procedure_name) 89 | { 90 | return 0; 91 | } 92 | 93 | bool terminate_monitor_hook::get_terminate_orig_code(unsigned char * out_buffer, int length) 94 | { 95 | return false; 96 | } 97 | 98 | bool terminate_monitor_hook::hook_terminate(export_list* exports) 99 | { 100 | // Allocate space in the remote process for the hook 101 | if (_hook_address == NULL) 102 | { 103 | _hook_address = (unsigned __int64)VirtualAllocEx(_ph, NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 104 | if (_hook_address > 0) 105 | { 106 | // Lookup the function addresses in the target process 107 | _address_terminate = exports->find_export("ntdll.dll", "NtTerminateProcess", _is64); 108 | unsigned __int64 address_getthread = exports->find_export("kernel32.dll", "GetCurrentThread", _is64); 109 | unsigned __int64 address_getthreadid = exports->find_export("kernel32.dll", "GetCurrentThreadId", _is64); 110 | unsigned __int64 address_suspendthread = exports->find_export("kernel32.dll", "SuspendThread", _is64); 111 | 112 | if (_address_terminate == NULL || address_getthread == NULL || address_suspendthread == NULL || address_getthreadid == NULL) 113 | { 114 | if (_options->Verbose) 115 | { 116 | fprintf(stderr, "WARNING: Failed to find library exports used in hooking ZwTerminateProcess. PID: 0x%x, 64bit mode: %i\n", _pid, (int)_is64); 117 | fprintf(stderr, "WARNING: ntdll.dll::NtTerminateProcess = 0x%llX\n", _address_terminate); 118 | fprintf(stderr, "WARNING: kernel32.dll::GetCurrentThreadId = 0x%llX\n", address_getthreadid); 119 | fprintf(stderr, "WARNING: kernel32.dll::GetCurrentThread = 0x%llX\n", address_getthread); 120 | fprintf(stderr, "WARNING: kernel32.dll::SuspendThread = 0x%llX\n", address_suspendthread); 121 | } 122 | } 123 | else 124 | { 125 | // Found the addresses for our hook code 126 | 127 | // Modify the NtTerminateProcess region to add WRITE privileges 128 | //VirtualQueryEx( _ph, _address_terminate, ) 129 | DWORD old_protection = 0; 130 | bool changed = VirtualProtectEx(_ph, (LPVOID) _address_terminate, 0x1000, PAGE_EXECUTE_READWRITE, &old_protection); 131 | 132 | if (changed) 133 | { 134 | // Read the original code 135 | SIZE_T num_read = 0; 136 | bool success = ReadProcessMemory(_ph, (LPCVOID)(_address_terminate), (void*)(_original_hook_bytes), sizeof(_original_hook_bytes), &num_read); 137 | 138 | if (num_read == sizeof(_original_hook_bytes) && success) 139 | { 140 | unsigned char* hook_code = NULL; 141 | int hook_code_length = 0; 142 | 143 | if (!_is64) 144 | { 145 | // Windows 10, wow64 version of ZwTerminateProcess 146 | // ntdll.dll -> ZwTerminateProcess() 147 | // B8 29 00 00 00 mov eax, 29h; NtTerminateProcess 148 | // 33 C9 xor ecx, ecx 149 | // 8D 54 24 04 lea edx, [esp + arg_0] 150 | // 64 FF 15 C0 00 00 00 call large dword ptr fs : 0C0h 151 | // 83 C4 04 add esp, 4 152 | // C2 08 00 retn 8 153 | 154 | // ------ Hook (x86/x64 independent code) ------- 155 | // is_waiting: (00) 156 | // dq 0 157 | // thread_id: (08) 158 | // dq 0 159 | // GetCurrentThread: (10) 160 | // dq 0 161 | // GetCurrentThreadId: (18) 162 | // dq 0 163 | // SuspendThread: (20) 164 | // dq 0 165 | // TerminateProcess: (28) 166 | // dq 0 167 | // _hook_original_code_length: (30) 168 | // dq 0 169 | // _hook_original_code: (38) 170 | // dq 0 171 | // dq 0 172 | // dq 0 173 | // dq 0 174 | // hook: 175 | // push ebx 176 | // push ecx 177 | // push esi 178 | // push edi 179 | // 180 | // call 0 181 | // pop ebx 182 | // sub ebx, 0x5d // 5 + 0x58 = 0x5d 183 | // 184 | // push eax 185 | // mov eax, [ebx+0x10] // GetCurrentThreadId 186 | // call eax 187 | // mov [ebx+0x08], eax // thread_id 188 | // mov [ebx], 1 // is_waiting 189 | // push eax 190 | // mov eax, [ebx+0x18] // SuspendThread 191 | // call eax 192 | // 193 | // mov ecx, [ebx+0x28] // _hook_original_code_length 194 | // mov esi, ebx 195 | // add esi, 0x30 // _hook_original_code 196 | // mov edi, [ebx+0x20] // TerminateProcess 197 | // rep movsb 198 | // 199 | // mov eax, [ebx+0x20] // TerminateProcess 200 | // pop edi 201 | // pop esi 202 | // pop ecx 203 | // pop ebx 204 | // 205 | // jmp eax 206 | 207 | unsigned char code[] = { 208 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 209 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 210 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 211 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 212 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 213 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 214 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, // <_hook_original_code_length> 215 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // <_hook_original_code> 216 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // <_hook_original_code> + 8 217 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // <_hook_original_code> + 16 218 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // <_hook_original_code> + 24 219 | 220 | 221 | // push ebx 222 | // push ecx 223 | // push esi 224 | // push edi 225 | 0x53, 0x51, 0x56, 0x57, 226 | 227 | // call 0 228 | // pop ebx 229 | // sub ebx, 0x61 // 5 + 4 + 0x58 = 0x61 230 | 0xE8, 0x00, 0x00, 0x00, 0x00, 231 | 0x5B, 232 | 0x83, 0xEB, 0x61, 233 | 234 | // mov eax, [ebx+0x10] // GetCurrentThreadId 235 | // call eax 236 | // mov [ebx+0x08], eax // thread_id 237 | 0x8B, 0x43, 0x10, 238 | 0xFF, 0xD0, 239 | 0x89, 0x43, 0x08, 240 | 241 | // mov eax, [ebx+0x18] // GetCurrentThread 242 | // call eax 243 | 0x8B, 0x43, 0x18, 244 | 0xFF, 0xD0, 245 | 246 | // mov [ebx], 1 // is_waiting 247 | // push eax 248 | // mov eax, [ebx+0x20] // SuspendThread 249 | // call eax 250 | 0xC7, 0x03, 0x01, 0x00, 0x00, 0x00, 251 | 0x50, 252 | 0x8B, 0x43, 0x20, 253 | 0xFF, 0xD0, 254 | 255 | // mov ecx, [ebx+0x30] // _hook_original_code_length 256 | // mov esi, ebx 257 | // add esi, 0x38 // _hook_original_code 258 | // mov edi, [ebx+0x28] // TerminateProcess 259 | // cld 260 | // rep movsb [edi], [esi] 261 | 0x8B, 0x4B, 0x30, 262 | 0x89, 0xDE, 263 | 0x83, 0xC6, 0x38, 264 | 0x8B, 0x7B, 0x28, 265 | 0xFC, 266 | 0xF3, 0xA4, 267 | 268 | // mov eax, [ebx+0x28] // TerminateProcess 269 | 0x8B, 0x43, 0x28, 270 | 271 | // pop edi 272 | // pop esi 273 | // pop ecx 274 | // pop ebx 275 | 0x5F, 0x5E, 0x59, 0x5B, 276 | 277 | //0xCC, 278 | 279 | // jmp eax 280 | 0xFF, 0xE0 281 | 282 | }; 283 | 284 | // Fill out the variables in the code 285 | // is_waiting: (00) 286 | // dq 0 287 | // thread_id: (08) 288 | // dq 0 289 | // GetCurrentThreadId: (10) 290 | // dq 0 291 | // SuspendThread: (18) 292 | // dq 0 293 | // TerminateProcess: (20) 294 | // dq 0 295 | // _hook_original_code_length: (28) 296 | // dq 0 297 | // _hook_original_code: (30) 298 | // dq 0 299 | // dq 0 300 | hook_code = new unsigned char[sizeof(code)]; 301 | hook_code_length = sizeof(code); 302 | *((unsigned __int64*)(code + 0x10)) = address_getthreadid; // GetCurrentThreadId 303 | *((unsigned __int64*)(code + 0x18)) = address_getthread; // GetCurrentThread 304 | *((unsigned __int64*)(code + 0x20)) = address_suspendthread; // SuspendThread 305 | *((unsigned __int64*)(code + 0x28)) = _address_terminate; // 306 | *((unsigned __int64*)(code + 0x30)) = 32; // _hook_original_code_length 307 | memcpy(code + 0x38, _original_hook_bytes, 32); // _hook_original_code 308 | 309 | // Copy to use this code as our hook code 310 | memcpy(hook_code, code, sizeof(code)); 311 | } 312 | else 313 | { 314 | // total size should be 0x47 (compiled code) + 0x58 (variables) = 0x9f 315 | unsigned char code64[] = { 316 | /* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 317 | /* 08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 318 | /* 10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 319 | /* 18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 320 | /* 20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 321 | /* 28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 322 | /* 30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, // <_hook_original_code_length> 323 | /* 38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // <_hook_original_code> 324 | /* 40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // <_hook_original_code> + 8 325 | /* 48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // <_hook_original_code> + 16 326 | /* 50 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // <_hook_original_code> + 24 327 | 328 | 329 | // push rbx 330 | // push rcx 331 | // push rsi 332 | // push rdi 333 | 0x53, 0x51, 0x56, 0x57, 334 | 335 | // call +0 336 | // pop rbx 337 | // sub rbx, 0x61 // 5 + 4 + 0x58 = 0x61 338 | 0xE8, 0x00, 0x00, 0x00, 0x00, 339 | 0x5B, 340 | 0x48, 0x83, 0xEB, 0x61, 341 | 342 | // mov rax, [rbx+0x10] // GetCurrentThreadId 343 | // sub rsp, 0x20 344 | // call rax 345 | // add rsp, 0x20 346 | // mov [rbx+0x08], rax // thread_id 347 | 0x48, 0x8B, 0x43, 0x10, 348 | 0x48, 0x83, 0xEC, 0x20, 349 | 0xFF, 0xD0, 350 | 0x48, 0x83, 0xC4, 0x20, 351 | 0x48, 0x89, 0x43, 0x08, 352 | 353 | // mov rax, [rbx+0x18] // GetCurrentThread 354 | // sub rsp, 0x20 355 | // call rax 356 | // add rsp, 0x20 357 | 0x48, 0x8B, 0x43, 0x18, 358 | 0x48, 0x83, 0xEC, 0x20, 359 | 0xFF, 0xD0, 360 | 0x48, 0x83, 0xC4, 0x20, 361 | 362 | // mov QWORD [rbx], 1 // is_waiting 363 | // 4889C1 mov rcx, rax ; threadid 364 | // mov rax, [rbx+0x20] // SuspendThread 365 | // sub rsp, 0x20 366 | // call rax 367 | // add rsp, 0x20 368 | 0x48, 0xC7, 0x03, 0x01, 0x00, 0x00, 0x00, 369 | 0x48, 0x89, 0xC1, 370 | 0x48, 0x8B, 0x43, 0x20, 371 | 0x48, 0x83, 0xEC, 0x20, 372 | 0xFF, 0xD0, 373 | 0x48, 0x83, 0xC4, 0x20, 374 | 375 | // mov rcx, [rbx+0x30] // _hook_original_code_length 376 | // mov rsi, rbx 377 | // add rsi, 0x38 // _hook_original_code 378 | // mov rdi, [rbx+0x28] // TerminateProcess 379 | // cld 380 | // rep movsb [rdi], [rsi] 381 | 0x48, 0x8B, 0x4B, 0x30, 382 | 0x48, 0x89, 0xDE, 383 | 0x48, 0x83, 0xC6, 0x38, 384 | 0x48, 0x8B, 0x7B, 0x28, 385 | 0xFC, 386 | 0xF3, 0xA4, 387 | 388 | // mov rax, [rbx+0x28] // TerminateProcess 389 | 0x48, 0x8B, 0x43, 0x28, 390 | 391 | // pop rdi 392 | // pop rsi 393 | // pop rcx 394 | // pop rbx 395 | 0x5F, 0x5E, 0x59, 0x5B, 396 | 397 | //0xCC, 398 | 399 | // jmp rax 400 | 0xFF, 0xE0 401 | 402 | }; 403 | 404 | // Fill out the variables in the code 405 | // is_waiting: (00) 406 | // dq 0 407 | // thread_id: (08) 408 | // dq 0 409 | // GetCurrentThreadId: (10) 410 | // dq 0 411 | // SuspendThread: (18) 412 | // dq 0 413 | // TerminateProcess: (20) 414 | // dq 0 415 | // _hook_original_code_length: (28) 416 | // dq 0 417 | // _hook_original_code: (30) 418 | // dq 0 419 | // dq 0 420 | hook_code = new unsigned char[sizeof(code64)]; 421 | hook_code_length = sizeof(code64); 422 | *((unsigned __int64*)(code64 + 0x10)) = address_getthreadid; // GetCurrentThreadId 423 | *((unsigned __int64*)(code64 + 0x18)) = address_getthread; // GetCurrentThread 424 | *((unsigned __int64*)(code64 + 0x20)) = address_suspendthread; // SuspendThread 425 | *((unsigned __int64*)(code64 + 0x28)) = _address_terminate; // 426 | *((unsigned __int64*)(code64 + 0x30)) = 32; // _hook_original_code_length 427 | memcpy(code64 + 0x38, _original_hook_bytes, 32); // _hook_original_code (_original_hook_bytes is not our hook!) 428 | 429 | // Copy to use this code as our hook code 430 | memcpy(hook_code, code64, sizeof(code64)); 431 | } 432 | 433 | // Inject the hook code 434 | SIZE_T num_written = 0; 435 | bool success = WriteProcessMemory(_ph, (LPVOID)_hook_address, hook_code, hook_code_length, &num_written); 436 | if (hook_code != NULL) 437 | delete[]hook_code; 438 | 439 | if (success && num_written == hook_code_length) 440 | { 441 | // Set the variable addresses in the remote process 442 | _address_is_waiting = _hook_address; 443 | _address_thread_id = _hook_address + 8; 444 | 445 | // Redirect ZwTerminateProcess 446 | return add_redirect(_hook_address + 0x58); 447 | } 448 | else 449 | { 450 | PrintLastError(L"Failed to write NtTerminateProcess hook code."); 451 | } 452 | } 453 | } 454 | } 455 | } 456 | else 457 | { 458 | if( _options->Verbose ) 459 | PrintLastError(L"Failed to allocate space for NtTerminateProcess hook."); 460 | } 461 | 462 | return false; // Failed to hook 463 | } 464 | return true; // Already hooked 465 | } 466 | 467 | bool terminate_monitor_hook::is_terminate_waiting() 468 | { 469 | if (_hook_address != NULL && _address_is_waiting != NULL ) 470 | { 471 | unsigned __int64 value = 0; 472 | SIZE_T num_read = 0; 473 | bool success = ReadProcessMemory(_ph, (LPCVOID) _address_is_waiting, &value, sizeof(value), &num_read); 474 | if (success && num_read == sizeof(value)) 475 | { 476 | return value == 1; 477 | } 478 | } 479 | return false; 480 | } 481 | 482 | void terminate_monitor_hook::resume_terminate() 483 | { 484 | if (is_terminate_waiting() && _address_thread_id != NULL) 485 | { 486 | DWORD tid = 0; 487 | if ( read_memory(_ph, _address_thread_id, &tid) && tid != 0 ) 488 | { 489 | // Set it's state to no longer waiting to terminate 490 | write_memory(_ph, _address_is_waiting, (unsigned __int64)0); 491 | 492 | // Resume this thread in the target process 493 | HANDLE th = OpenThread(THREAD_SUSPEND_RESUME, false, tid); 494 | DWORD result = ResumeThread(th); 495 | 496 | _process_is_terminating = true; 497 | } 498 | } 499 | } 500 | 501 | terminate_monitor_hook::terminate_monitor_hook(HANDLE ph, DWORD pid, bool is64, PD_OPTIONS* options) 502 | { 503 | _ph = ph; 504 | _pid = pid; 505 | _is64 = is64; 506 | _hook_address = NULL; 507 | _address_is_waiting = NULL; 508 | _address_thread_id = NULL; 509 | _address_terminate = NULL; 510 | _process_is_terminating = false; 511 | _options = options; 512 | } 513 | 514 | 515 | terminate_monitor_hook::~terminate_monitor_hook() 516 | { 517 | // Remove our redirect from this process if it is in place 518 | unhock_terminate(); 519 | } 520 | -------------------------------------------------------------------------------- /pd/terminate_monitor_hook.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "windows.h" 4 | #include "simple.h" 5 | #include "export_list.h" 6 | #include "utils.h" 7 | 8 | class terminate_monitor_hook 9 | { 10 | HANDLE _ph; 11 | DWORD _pid; 12 | bool _is64; 13 | unsigned __int64 _hook_address; 14 | unsigned __int64 _address_terminate; 15 | unsigned __int64 _address_is_waiting; 16 | unsigned __int64 _address_thread_id; 17 | unsigned char _original_hook_bytes[32]; 18 | bool _process_is_terminating; 19 | PD_OPTIONS* _options; 20 | 21 | bool add_redirect(unsigned __int64 target_address); 22 | 23 | unsigned __int64 get_address(char* library, char* procedure_name); 24 | bool get_terminate_orig_code(unsigned char* out_buffer, int length); 25 | 26 | public: 27 | bool hook_terminate(export_list* exports); 28 | bool unhock_terminate(); 29 | 30 | bool is_terminate_waiting(); 31 | void resume_terminate(); 32 | 33 | terminate_monitor_hook(HANDLE ph, DWORD pid, bool is64, PD_OPTIONS* _options); 34 | ~terminate_monitor_hook(); 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /pd/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | static bool test_read( unsigned char* buffer, SIZE_T length, unsigned char* read_ptr, SIZE_T read_length ) 4 | { 5 | return read_ptr >= buffer && read_ptr + read_length <= buffer + length; 6 | }; 7 | 8 | 9 | static bool write_memory(HANDLE ph, unsigned __int64 address, unsigned __int64 value) 10 | { 11 | SIZE_T num_written = 0; 12 | return WriteProcessMemory(ph, (LPVOID) address, &value, sizeof(value), &num_written) && num_written == sizeof(value); 13 | }; 14 | 15 | static bool write_memory(HANDLE ph, unsigned __int64 address, unsigned __int32 value) 16 | { 17 | SIZE_T num_written = 0; 18 | return WriteProcessMemory(ph, (LPVOID)address, &value, sizeof(value), &num_written) && num_written == sizeof(value); 19 | }; 20 | 21 | static bool read_memory(HANDLE ph, unsigned __int64 address, unsigned __int64* value) 22 | { 23 | SIZE_T num_read = 0; 24 | return ReadProcessMemory(ph, (LPVOID)address, value, sizeof(*value), &num_read) && num_read == sizeof(*value); 25 | }; 26 | 27 | static bool read_memory(HANDLE ph, unsigned __int64 address, unsigned __int32* value) 28 | { 29 | SIZE_T num_read = 0; 30 | return ReadProcessMemory(ph, (LPVOID)address, value, sizeof(*value), &num_read) && num_read == sizeof(*value); 31 | }; 32 | 33 | static bool read_memory(HANDLE ph, unsigned __int64 address, void** value) 34 | { 35 | SIZE_T num_read = 0; 36 | return ReadProcessMemory(ph, (LPVOID)address, value, sizeof(*value), &num_read) && num_read == sizeof(*value); 37 | }; 38 | 39 | static bool read_memory(HANDLE ph, unsigned __int64 address, DWORD* value) 40 | { 41 | SIZE_T num_read = 0; 42 | return ReadProcessMemory(ph, (LPVOID)address, value, sizeof(*value), &num_read) && num_read == sizeof(*value); 43 | }; -------------------------------------------------------------------------------- /pd/work_queue.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Juan Palacios juan.palacios.puyana@gmail.com 3 | // Subject to the BSD 2-Clause License 4 | // - see < http://opensource.org/licenses/BSD-2-Clause> 5 | 6 | /* 7 | Copyright (c) 2013, Juan Palacios 8 | All rights reserved. 9 | 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions are met: 12 | 13 | 1. Redistributions of source code must retain the above copyright notice, this 14 | list of conditions and the following disclaimer. 15 | 2. Redistributions in binary form must reproduce the above copyright notice, 16 | this list of conditions and the following disclaimer in the documentation 17 | and/or other materials provided with the distribution. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 23 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #pragma once 32 | 33 | #ifndef CONCURRENT_QUEUE_ 34 | #define CONCURRENT_QUEUE_ 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | template 42 | class Queue 43 | { 44 | public: 45 | 46 | T pop() 47 | { 48 | std::unique_lock mlock(mutex_); 49 | while (queue_.empty()) 50 | { 51 | cond_.wait(mlock); 52 | } 53 | auto val = queue_.front(); 54 | queue_.pop(); 55 | return val; 56 | } 57 | 58 | bool pop(T& item) 59 | { 60 | std::unique_lock mlock(mutex_); 61 | if (queue_.empty()) 62 | return false; 63 | 64 | item = queue_.front(); 65 | queue_.pop(); 66 | return true; 67 | } 68 | 69 | bool empty() 70 | { 71 | std::unique_lock mlock(mutex_); 72 | bool is_empty = queue_.empty(); 73 | mlock.unlock(); 74 | return is_empty; 75 | } 76 | 77 | int count() 78 | { 79 | std::unique_lock mlock(mutex_); 80 | int count = queue_.size(); 81 | mlock.unlock(); 82 | return count; 83 | } 84 | 85 | void push(const T& item) 86 | { 87 | std::unique_lock mlock(mutex_); 88 | queue_.push(item); 89 | mlock.unlock(); 90 | cond_.notify_one(); 91 | } 92 | Queue()=default; 93 | Queue(const Queue&) = delete; // disable copying 94 | Queue& operator=(const Queue&) = delete; // disable assignment 95 | 96 | private: 97 | std::queue queue_; 98 | std::mutex mutex_; 99 | std::condition_variable cond_; 100 | }; 101 | 102 | #endif --------------------------------------------------------------------------------