├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── VMPDump.sln ├── VMPDump ├── CMakeLists.txt ├── VMPDump.vcxproj ├── VMPDump.vcxproj.filters ├── disassembler.cpp ├── disassembler.hpp ├── imports.hpp ├── instruction.cpp ├── instruction.hpp ├── instruction_stream.cpp ├── instruction_stream.hpp ├── instruction_utilities.hpp ├── main.cpp ├── module_view.cpp ├── module_view.hpp ├── pe_constructor.cpp ├── pe_constructor.hpp ├── pe_image.hpp ├── tables.hpp ├── types.hpp ├── vmpdump.cpp ├── vmpdump.hpp └── winpe │ ├── common.hpp │ ├── debug.hpp │ ├── dir_debug.hpp │ ├── dir_exceptions.hpp │ ├── dir_export.hpp │ ├── dir_iat.hpp │ ├── dir_import.hpp │ ├── dir_load_config.hpp │ ├── dir_relocs.hpp │ ├── dir_resource.hpp │ ├── dir_tls.hpp │ ├── image.hpp │ └── nt_headers.hpp ├── VMPDump_Tester ├── CMakeLists.txt ├── VMPDump_Tester.vcxproj ├── VMPDump_Tester.vcxproj.filters └── main.cpp ├── after.png ├── before.png └── screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | 352 | build*/ 353 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | project(VMPDump) 4 | 5 | include(FetchContent) 6 | 7 | FetchContent_Declare( 8 | VTIL-NativeLifters 9 | GIT_REPOSITORY https://github.com/vtil-project/VTIL-NativeLifters 10 | GIT_TAG 39e9da9966790d9e117de6af927ef9abb7e7519e 11 | GIT_SHALLOW false 12 | ) 13 | FetchContent_MakeAvailable(VTIL-NativeLifters) 14 | 15 | # Hotfix for VTIL-Core on Windows 16 | target_compile_definitions(VTIL-Common PUBLIC NOMINMAX) 17 | 18 | add_subdirectory(VMPDump) 19 | add_subdirectory(VMPDump_Tester) 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VMPDump 2 | ![](https://raw.githubusercontent.com/0xnobody/vmpdump/master/screenshot.png) 3 | 4 | A dynamic VMP dumper and import fixer, powered by VTIL. Works for VMProtect 3.X x64. 5 | 6 | ## Before vs After 7 | ![](https://raw.githubusercontent.com/0xnobody/vmpdump/master/before.png) 8 | ![](https://raw.githubusercontent.com/0xnobody/vmpdump/master/after.png) 9 | 10 | ## Usage 11 | VMPDump.exe `` `""` `[-ep=]` `[-disable-reloc]` 12 | 13 | Arguments: 14 | * ``: The ID of the target process, in decimal or hex form. 15 | * ``: The name of the module which should be dumped and fixed. This can be an empty string ("") if the process image module is desired. 16 | * `[-ep=]`: An optionally-provided entry-point RVA, in hex form. VMPDump simply overwrites the Entry Point in the optional header with this value. 17 | * `[-disable-reloc]`: An optional setting to instruct VMPDump to mark that relocs have been stripped in the ouput image, forcing the image to load at the dumped ImageBase. This is useful if runnable dumps are desired. 18 | 19 | VMProtect initialization and unpacking must be complete in the target process before running VMPDump. This means it must be at or past the OEP (Original Entry Point). 20 | The dumped and fixed image will appear in the process image module directory, under the name `.VMPDump.`. 21 | 22 | ## How It Works 23 | VMProtect injects stubs for every import call or jmp. These stubs resolve the 'obfuscated' thunk in the `.vmpX` section, and add a fixed constant to 'deobfuscate' it. The calls or jumps themselves are then dispatched with a ret instruction. 24 | 25 | VMPDump scans all executable sections for these stubs, and lifts them to VTIL using the VTIL x64 lifter. Analysis is then performed on these stubs, in order to determine what kind of call must be replaced and what bytes must be overwritten. 26 | 27 | Once all calls have been retrieved, VMPDump then creates a new import table and appends thunks to the existing IAT. The calls to the VMP import stubs are replaced with direct calls to these thunks. 28 | 29 | Note that in mutated routines, there are situations when there are not enough bytes to replace the VMP import stub call with a direct thunk call, as the latter is 1 byte larger. In these cases, the section is extended and a stub that jumps to the import thunk is injected. The VMP import stub call is then replaced with a 5-byte relative call or jmp to said injected stub. 30 | 31 | ## Building (CMake) 32 | 33 | ``` 34 | mkdir build && cd build 35 | cmake -G "Visual Studio 16 2019" .. 36 | cmake --build . --config Release 37 | ``` 38 | 39 | ## Building (Visual Studio) 40 | 41 | Building in VS is as simple as replacing the include/library directories to VTIL-NativeLifers/VTIL-Core/Keystone/Capstone in the vcxproj. 42 | 43 | The project requires C++20. 44 | 45 | ## Issues and Limitations 46 | Due to the fact that code sections are linearly scanned, particularily in heavily mutated and obfuscated code, some import stub calls can be skipped and therefore not resolved. However, VMPDump includes workarounds for the majority of VMProtect mutation inconsistencies, so it should produce decent results even in heavily mutated code. 47 | 48 | If you encounter this, please make an issue with the relevant information and I'll take a look at it. 49 | 50 | ## Licence 51 | Licensed under the GPL-3.0 License. No warranty is provided of any kind. -------------------------------------------------------------------------------- /VMPDump.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30413.136 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VMPDump", "VMPDump\VMPDump.vcxproj", "{E68C36FF-75EE-4B01-9661-B3AB67480EE4}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VMPDump_Tester", "VMPDump_Tester\VMPDump_Tester.vcxproj", "{4DD0546B-E387-4304-9088-B5CEA383C4D4}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {E68C36FF-75EE-4B01-9661-B3AB67480EE4}.Debug|x64.ActiveCfg = Debug|x64 19 | {E68C36FF-75EE-4B01-9661-B3AB67480EE4}.Debug|x64.Build.0 = Debug|x64 20 | {E68C36FF-75EE-4B01-9661-B3AB67480EE4}.Debug|x86.ActiveCfg = Debug|Win32 21 | {E68C36FF-75EE-4B01-9661-B3AB67480EE4}.Debug|x86.Build.0 = Debug|Win32 22 | {E68C36FF-75EE-4B01-9661-B3AB67480EE4}.Release|x64.ActiveCfg = Release|x64 23 | {E68C36FF-75EE-4B01-9661-B3AB67480EE4}.Release|x64.Build.0 = Release|x64 24 | {E68C36FF-75EE-4B01-9661-B3AB67480EE4}.Release|x86.ActiveCfg = Release|Win32 25 | {E68C36FF-75EE-4B01-9661-B3AB67480EE4}.Release|x86.Build.0 = Release|Win32 26 | {4DD0546B-E387-4304-9088-B5CEA383C4D4}.Debug|x64.ActiveCfg = Debug|x64 27 | {4DD0546B-E387-4304-9088-B5CEA383C4D4}.Debug|x64.Build.0 = Debug|x64 28 | {4DD0546B-E387-4304-9088-B5CEA383C4D4}.Debug|x86.ActiveCfg = Debug|Win32 29 | {4DD0546B-E387-4304-9088-B5CEA383C4D4}.Debug|x86.Build.0 = Debug|Win32 30 | {4DD0546B-E387-4304-9088-B5CEA383C4D4}.Release|x64.ActiveCfg = Release|x64 31 | {4DD0546B-E387-4304-9088-B5CEA383C4D4}.Release|x64.Build.0 = Release|x64 32 | {4DD0546B-E387-4304-9088-B5CEA383C4D4}.Release|x86.ActiveCfg = Release|Win32 33 | {4DD0546B-E387-4304-9088-B5CEA383C4D4}.Release|x86.Build.0 = Release|Win32 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | GlobalSection(ExtensibilityGlobals) = postSolution 39 | SolutionGuid = {19DF9281-B3E2-46A7-B41B-89C984C69DCE} 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /VMPDump/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(VMPDump) 2 | 3 | file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS *.cpp *.hpp) 4 | 5 | add_executable(${PROJECT_NAME} 6 | ${SOURCES} 7 | ) 8 | 9 | target_link_libraries(${PROJECT_NAME} PRIVATE NativeLifters-Core Shlwapi) -------------------------------------------------------------------------------- /VMPDump/VMPDump.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {e68c36ff-75ee-4b01-9661-b3ab67480ee4} 25 | VMPDump 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | $(VC_IncludePath);$(WindowsSDK_IncludePath);C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\NativeLifters-Core\includes;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\out\build\x64-Debug\_deps\capstone-src\include;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\out\build\x64-Debug\_deps\keystone-src\include;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\Dependencies\VTIL-Core\VTIL-Architecture\includes;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\Dependencies\VTIL-Core\VTIL-Common\includes;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\Dependencies\VTIL-Core\VTIL-Compiler\includes;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\Dependencies\VTIL-Core\VTIL-SymEx\includes 82 | C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\out\build\x64-Debug\NativeLifters-Core;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\out\build\x64-Debug\_deps\capstone-build;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\out\build\x64-Debug\_deps\keystone-build\llvm\lib;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\out\build\x64-Debug\Dependencies\VTIL-Core\VTIL-Architecture;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\out\build\x64-Debug\Dependencies\VTIL-Core\VTIL-Common;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\out\build\x64-Debug\Dependencies\VTIL-Core\VTIL-Compiler;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\out\build\x64-Debug\Dependencies\VTIL-Core\VTIL-SymEx;$(LibraryPath) 83 | 84 | 85 | false 86 | $(VC_IncludePath);$(WindowsSDK_IncludePath);C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\NativeLifters-Core\includes;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\out\build\x64-Debug\_deps\capstone-src\include;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\out\build\x64-Debug\_deps\keystone-src\include;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\Dependencies\VTIL-Core\VTIL-Architecture\includes;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\Dependencies\VTIL-Core\VTIL-Common\includes;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\Dependencies\VTIL-Core\VTIL-Compiler\includes;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\Dependencies\VTIL-Core\VTIL-SymEx\includes 87 | C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\out\build\x64-Release\NativeLifters-Core;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\out\build\x64-Release\_deps\capstone-build;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\out\build\x64-Release\_deps\keystone-build\llvm\lib;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\out\build\x64-Release\Dependencies\VTIL-Core\VTIL-Architecture;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\out\build\x64-Release\Dependencies\VTIL-Core\VTIL-Common;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\out\build\x64-Release\Dependencies\VTIL-Core\VTIL-Compiler;C:\Users\adamn\Documents\GitHub\VTIL-NativeLifters\out\build\x64-Release\Dependencies\VTIL-Core\VTIL-SymEx;$(LibraryPath) 88 | 89 | 90 | 91 | Level3 92 | true 93 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 94 | true 95 | 96 | 97 | Console 98 | true 99 | 100 | 101 | 102 | 103 | Level3 104 | true 105 | true 106 | true 107 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 108 | true 109 | 110 | 111 | Console 112 | true 113 | true 114 | true 115 | 116 | 117 | 118 | 119 | Level3 120 | true 121 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions);NOMINMAX 122 | true 123 | stdcpplatest 124 | 125 | 126 | Console 127 | true 128 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);NativeLifters-Core.lib;capstone.lib;VTIL-Architecture.lib;VTIL-Common.lib;VTIL-Compiler.lib;VTIL-SymEx.lib;keystone.lib;Shlwapi.lib 129 | /STACK:34359738368 %(AdditionalOptions) 130 | 131 | 132 | 133 | 134 | Level3 135 | true 136 | true 137 | true 138 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions);NOMINMAX 139 | true 140 | stdcpplatest 141 | 142 | 143 | Console 144 | true 145 | true 146 | true 147 | %(AdditionalDependencies);NativeLifters-Core.lib;capstone.lib;VTIL-Architecture.lib;VTIL-Common.lib;VTIL-Compiler.lib;VTIL-SymEx.lib;keystone.lib;Shlwapi.lib 148 | /STACK:34359738368 %(AdditionalOptions) 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /VMPDump/VMPDump.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {0ced714f-5396-43d5-812c-4e8ca92e5ab2} 6 | 7 | 8 | {6bab3e38-9146-4d92-8f34-4d8c972f02f5} 9 | 10 | 11 | {2ca057a0-09dc-4812-bbfe-a399771bcb3a} 12 | 13 | 14 | {4d499fea-89e4-434d-a85f-20fdf13e4944} 15 | 16 | 17 | {808081d2-bc36-49e7-b647-fff02845de40} 18 | 19 | 20 | 21 | 22 | Instruction Parser 23 | 24 | 25 | Instruction Parser 26 | 27 | 28 | Instruction Parser 29 | 30 | 31 | Instruction Parser 32 | 33 | 34 | Imports 35 | 36 | 37 | Imports 38 | 39 | 40 | Reconstruction 41 | 42 | 43 | Third Party\winpe 44 | 45 | 46 | Third Party\winpe 47 | 48 | 49 | Third Party\winpe 50 | 51 | 52 | Third Party\winpe 53 | 54 | 55 | Third Party\winpe 56 | 57 | 58 | Third Party\winpe 59 | 60 | 61 | Third Party\winpe 62 | 63 | 64 | Third Party\winpe 65 | 66 | 67 | Third Party\winpe 68 | 69 | 70 | Third Party\winpe 71 | 72 | 73 | Third Party\winpe 74 | 75 | 76 | Third Party\winpe 77 | 78 | 79 | Third Party\winpe 80 | 81 | 82 | Reconstruction 83 | 84 | 85 | Reconstruction 86 | 87 | 88 | Reconstruction 89 | 90 | 91 | 92 | 93 | 94 | Instruction Parser 95 | 96 | 97 | Instruction Parser 98 | 99 | 100 | Instruction Parser 101 | 102 | 103 | Imports 104 | 105 | 106 | Reconstruction 107 | 108 | 109 | Reconstruction 110 | 111 | 112 | -------------------------------------------------------------------------------- /VMPDump/disassembler.cpp: -------------------------------------------------------------------------------- 1 | #include "disassembler.hpp" 2 | 3 | namespace vmpdump 4 | { 5 | // Disassembles at the offset from the base, negotating jumps according to the flags. 6 | // NOTE: The offset is used for the disassembled instructions' addresses. 7 | // If the number of instructions disassembled exceeds the provided max amount, en empty instruction stream is returned. 8 | // 9 | instruction_stream disassembler::disassemble( uint64_t base, uint64_t offset, disassembler_flags flags, uint64_t max_instructions ) 10 | { 11 | // ea = base + offset 12 | // 13 | uint64_t ea = base + offset; 14 | 15 | std::vector> instructions; 16 | 17 | size_t size = 0xFFFFFFFFFFFFFFFFull; 18 | 19 | uint64_t i = 0; 20 | 21 | // Helper lambda to exception-wrap the disassebly. 22 | // This is useful as we may be dealing with invalid instructions which may cause an access violation. 23 | // 24 | auto disasm = [&]() -> bool 25 | { 26 | __try 27 | { 28 | return cs_disasm_iter( handle, ( const uint8_t** )&ea, &size, &offset, insn ); 29 | } 30 | __except ( 1 ) {} 31 | return false; 32 | }; 33 | 34 | // While iterative disassembly is successful. 35 | // 36 | while ( disasm() ) 37 | { 38 | // Check max bounds. 39 | // 40 | if ( i >= max_instructions ) 41 | return instruction_stream {}; 42 | i++; 43 | 44 | // Construct a self-containing instruction. 45 | // 46 | auto ins = std::make_shared( insn ); 47 | 48 | // Is the instruction a branch? 49 | // 50 | if ( ins->is_branch() ) 51 | { 52 | // If it's unconditional, and we know the destination, and we are specified 53 | // to follow these types of jumps, do so. 54 | // 55 | if ( flags & disassembler_take_unconditional_imm 56 | && ins->is_uncond_jmp() && ins->operand( 0 ).type == X86_OP_IMM ) 57 | { 58 | // We must set the offset, otherwise the disassembly will be incorrect. 59 | // 60 | offset = ins->operand( 0 ).imm; 61 | 62 | // Update actual disassembly pointer. 63 | // 64 | ea = offset + base; 65 | 66 | // Don't append the jump to the stream. 67 | // 68 | continue; 69 | } 70 | 71 | // Branch not resolved - simply end disassembly. 72 | // 73 | break; 74 | } 75 | 76 | // Is the instruction a call? 77 | // 78 | if ( ins->ins.id == X86_INS_CALL ) 79 | { 80 | // If the pass calls flag is not set, add it and end disassembly. 81 | // 82 | if ( !( flags & disassembler_pass_calls ) ) 83 | { 84 | instructions.push_back( ins ); 85 | break; 86 | } 87 | } 88 | 89 | // Is the instruction a return? 90 | // 91 | if ( ins->ins.id == X86_INS_RET ) 92 | { 93 | // Add the instruction and end disassembly. 94 | // 95 | instructions.push_back( ins ); 96 | break; 97 | } 98 | 99 | // Add instruction to list. 100 | // 101 | instructions.push_back( ins ); 102 | } 103 | 104 | // Return an instruction stream of said instructions. 105 | // 106 | return { instructions }; 107 | } 108 | 109 | 110 | // Disassembles at the offset from the base, simply disassembling every instruction in order. 111 | // 112 | std::vector> disassembler::disassembly_simple( uint64_t base, uint64_t offset, uint64_t end_rva ) 113 | { 114 | // ea = base + offset 115 | // 116 | uint64_t ea = base + offset; 117 | 118 | std::vector> instructions; 119 | 120 | size_t size = end_rva - offset; 121 | 122 | // While iterative disassembly is successful. 123 | // 124 | while ( true ) 125 | { 126 | // Check if we're within bounds. 127 | // 128 | if ( offset >= size ) 129 | break; 130 | 131 | // In case disassembly failed (due to invalid instructions), try to continue by incrementing offset. 132 | // 133 | if ( !cs_disasm_iter( handle, ( const uint8_t** )&ea, &size, &offset, insn ) ) 134 | { 135 | offset++; 136 | ea++; 137 | 138 | continue; 139 | } 140 | 141 | instructions.push_back( std::make_unique( insn ) ); 142 | } 143 | 144 | return std::move( instructions ); 145 | } 146 | } -------------------------------------------------------------------------------- /VMPDump/disassembler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "instruction_stream.hpp" 5 | 6 | namespace vmpdump 7 | { 8 | // Defaults. 9 | // 10 | const cs_arch cs_default_arch = CS_ARCH_X86; 11 | const cs_mode cs_default_mode = CS_MODE_64; 12 | 13 | // Specifies the desired behaviour of the auto disassembler when a jump condition is 14 | // encountered 15 | // 16 | enum disassembler_flags : uint32_t 17 | { 18 | // When met with a branch, stop dissassembly. 19 | // 20 | disassembler_none = 0, 21 | 22 | // Take all unconditional immediate jumps, ignoring the jump instructions. 23 | // 24 | disassembler_take_unconditional_imm = 1 << 0, 25 | 26 | // Take all conditional jumps. 27 | // 28 | disassembler_take_conditional = 1 << 1, 29 | 30 | // Skip all conditional jumps. 31 | // 32 | disassembler_skip_conditional = 1 << 2, 33 | 34 | // Pass on calls. 35 | // 36 | disassembler_pass_calls = 1 << 3, 37 | }; 38 | 39 | // This class provides a very lightweight thread-safe wrapper over capstone. 40 | // 41 | class disassembler 42 | { 43 | private: 44 | // The internal handle. 45 | // 46 | csh handle; 47 | 48 | // The internal instruction allocation memory. 49 | // 50 | cs_insn* insn; 51 | 52 | 53 | public: 54 | // Cannot be copied or moved. 55 | // Only one disassembler can exist per thread. 56 | // 57 | disassembler( const disassembler& ) = delete; 58 | disassembler( disassembler&& ) = delete; 59 | disassembler& operator=( const disassembler&& ) = delete; 60 | disassembler& operator=( disassembler&& ) = delete; 61 | 62 | disassembler( cs_arch arch, cs_mode mode ) 63 | { 64 | fassert( cs_open( arch, mode, &handle ) == CS_ERR_OK ); 65 | cs_option( handle, CS_OPT_DETAIL, CS_OPT_ON ); 66 | insn = cs_malloc( handle ); 67 | } 68 | 69 | ~disassembler() 70 | { 71 | cs_close( &handle ); 72 | } 73 | 74 | // Getter to the handle. 75 | // 76 | csh get_handle() const { return handle; } 77 | 78 | cs_insn* get_insn() { return insn; } 79 | 80 | // Singleton to provide a unique disassembler instance for each thread. 81 | // 82 | inline static disassembler& get( cs_arch arch = cs_default_arch, cs_mode mode = cs_default_mode) 83 | { 84 | thread_local static disassembler instance( arch, mode ); 85 | 86 | return instance; 87 | } 88 | 89 | // Disassembles at the offset from the base, negotating jumps according to the flags. 90 | // NOTE: The offset is used for the disassembled instructions' addresses. 91 | // If the number of instructions disassembled exceeds the provided max amount, en empty instruction stream is returned. 92 | // 93 | instruction_stream disassemble( uint64_t base, uint64_t offset, disassembler_flags flags = disassembler_take_unconditional_imm, uint64_t max_instructions = -1 ); 94 | 95 | // Disassembles at the offset from the base, simply disassembling every instruction in order. 96 | // 97 | std::vector> disassembly_simple( uint64_t base, uint64_t offset, uint64_t end_rva ); 98 | }; 99 | } -------------------------------------------------------------------------------- /VMPDump/imports.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "instruction.hpp" 5 | #include "module_view.hpp" 6 | 7 | namespace vmpdump 8 | { 9 | // Struct that information regarding an obfuscated VMProtect import. 10 | // 11 | struct resolved_import 12 | { 13 | // The relative virtual address of the import thunk. 14 | // ie. The address that holds the obfuscated import pointer. 15 | // 16 | uint64_t thunk_rva; 17 | 18 | // The effective address of the import target. 19 | // 20 | remote_ea_t target_ea; 21 | 22 | // Constructor. 23 | // 24 | resolved_import( uint64_t thunk_rva, uintptr_t target_ea ) 25 | : thunk_rva( thunk_rva ), target_ea( target_ea ) 26 | {} 27 | }; 28 | 29 | // Struct that holds import calls and their referenced import. 30 | // 31 | struct import_call 32 | { 33 | // The relative virtual address of the actual call instruction. 34 | // 35 | uint64_t call_rva; 36 | 37 | // The import that the call referenced. 38 | // 39 | const resolved_import* import; 40 | 41 | // The number of bytes the stack is adjusted by at the VMP import stub. 42 | // 43 | int32_t stack_adjustment; 44 | 45 | // Whether the import is padded with a junk byte immediately following the call. 46 | // 47 | bool padded; 48 | 49 | // Whether the import call is actually a JMP. 50 | // 51 | bool is_jmp; 52 | 53 | // The instruction that came exactly before the call instruction. 54 | // 55 | std::optional prev_instruction; 56 | 57 | // Constructor. 58 | // 59 | import_call( uint64_t call_rva, const resolved_import* import, int32_t stack_adjustment, bool padded, bool is_jmp, std::optional prev_instruction = {} ) 60 | : call_rva( call_rva ), import( import ), stack_adjustment( stack_adjustment ), padded( padded ), is_jmp( is_jmp ), prev_instruction( prev_instruction ) 61 | {} 62 | }; 63 | } -------------------------------------------------------------------------------- /VMPDump/instruction.cpp: -------------------------------------------------------------------------------- 1 | #include "instruction.hpp" 2 | #include "disassembler.hpp" 3 | 4 | namespace vmpdump 5 | { 6 | // Determines whether this instruction is any type of jump. 7 | // 8 | bool instruction::is_jmp() const 9 | { 10 | // Enumerate instruction groups. 11 | // 12 | for ( int i = 0; i < ins.detail->groups_count; i++ ) 13 | { 14 | auto grp = ins.detail->groups[ i ]; 15 | 16 | // If group is JMP, return true. 17 | // 18 | if ( grp == X86_GRP_JUMP ) 19 | return true; 20 | } 21 | 22 | return false; 23 | } 24 | 25 | // Is the instruction a conditional jump? 26 | // 27 | bool instruction::is_cond_jump() const 28 | { 29 | // Return false if unconditional. 30 | // 31 | if ( ins.id == X86_INS_JMP ) 32 | return false; 33 | 34 | // Loop through groups. 35 | // 36 | for ( int i = 0; i < ins.detail->groups_count; i++ ) 37 | { 38 | if ( ins.detail->groups[ i ] == X86_GRP_JUMP ) 39 | return true; 40 | } 41 | 42 | return false; 43 | } 44 | 45 | // Returns a vector of registers this instruction writes to and reads from. 46 | // Read is returned in the first part of the pair, Written in the second. 47 | // 48 | std::pair, std::vector> instruction::get_regs_accessed() const 49 | { 50 | // Declare C-arrays of the data. 51 | // 52 | cs_regs read, write; 53 | uint8_t readc, writec; 54 | 55 | // Use capstone to get lists of registers read from / written to. 56 | // 57 | if ( cs_regs_access( disassembler::get().get_handle(), &ins, read, &readc, write, &writec ) != CS_ERR_OK ) 58 | return {}; 59 | 60 | std::vector read_vec, write_vec; 61 | 62 | // Convert raw C style arrays to pretty C++ vectors. 63 | // 64 | for ( int i = 0; i < readc; i++ ) 65 | read_vec.push_back( ( x86_reg )read[ i ] ); 66 | for ( int i = 0; i < writec; i++ ) 67 | write_vec.push_back( ( x86_reg )write[ i ] ); 68 | 69 | return { read_vec, write_vec }; 70 | } 71 | } -------------------------------------------------------------------------------- /VMPDump/instruction.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace vmpdump 7 | { 8 | // This class provides a simple wrapper over the cs_insn and cs_detail 9 | // structs to make it self-containing, and to provide some simple utilities. 10 | // 11 | class instruction 12 | { 13 | private: 14 | // This is an internal backing structure that the cs_insn->detail 15 | // points to. 16 | // 17 | cs_detail detail; 18 | 19 | public: 20 | // The wrapped instruction. 21 | // 22 | cs_insn ins; 23 | 24 | // Copy constructor. 25 | // 26 | instruction( const cs_insn* ins ) 27 | : ins( *ins ), detail( *ins->detail ) 28 | { 29 | // Point ins->detail to copy. 30 | // 31 | this->ins.detail = &detail; 32 | } 33 | 34 | // Determines whether this instruction is any type of jump. 35 | // 36 | bool is_jmp() const; 37 | 38 | // Useful utilities. 39 | // 40 | inline int operand_count() const { return detail.x86.op_count; } 41 | inline const cs_x86_op& operand( int i ) const { return detail.x86.operands[ i ]; } 42 | inline x86_op_type operand_type( int i ) const { return detail.x86.operands[ i ].type; } 43 | 44 | inline bool is_uncond_jmp() const { return ins.id == X86_INS_JMP; }; 45 | 46 | inline bool is_branch() const { return is_jmp(); } 47 | 48 | inline x86_prefix prefix( int i ) const { return ( x86_prefix )detail.x86.prefix[ i ]; } 49 | 50 | // Returns a vector of registers this instruction writes to and reads from. 51 | // Read is returned in the first part of the pair, Written in the second. 52 | // 53 | std::pair, std::vector> get_regs_accessed() const; 54 | 55 | // Is the instruction a conditional jump? 56 | // 57 | bool is_cond_jump() const; 58 | }; 59 | } -------------------------------------------------------------------------------- /VMPDump/instruction_stream.cpp: -------------------------------------------------------------------------------- 1 | #include "instruction_stream.hpp" 2 | #include 3 | #include 4 | 5 | namespace vmpdump 6 | { 7 | // Advances the stream, incrementing index and returning the 8 | // instruction ptr. 9 | // 10 | const instruction* instruction_stream::next() 11 | { 12 | // Check if within bounds. 13 | // 14 | if ( begin + index > end ) 15 | return nullptr; 16 | 17 | // Fetch instruction. 18 | // 19 | auto& ins = instructions[ begin + index ]; 20 | 21 | // Increment index. 22 | // 23 | index++; 24 | 25 | // Return a non-owning pointer to the instruction. 26 | // 27 | return ins.get(); 28 | } 29 | 30 | // Returns a byte vector of all the instructions' bytes. 31 | // 32 | std::vector instruction_stream::bytes() const 33 | { 34 | std::vector result; 35 | 36 | // Enumerate through each instruction. 37 | // 38 | for ( int i = begin; i <= end; i++ ) 39 | { 40 | auto& ins = instructions[ i ]; 41 | 42 | result.insert( result.end(), &ins->ins.bytes[ 0 ], &ins->ins.bytes[ 0 ] + ins->ins.size ); 43 | } 44 | 45 | return result; 46 | } 47 | 48 | // Lifts the instruction stream to VTIL. 49 | // 50 | vtil::basic_block* instruction_stream::lift() const 51 | { 52 | using namespace vtil; 53 | 54 | // Create a new basic block. 55 | // 56 | basic_block* block = basic_block::begin( 0 ); 57 | 58 | // We are lifting a raw instruction stream; we don't need to preserve anything. 59 | // 60 | block->owner->routine_convention = {}; 61 | block->owner->routine_convention.purge_stack = true; 62 | 63 | // Instansiate the lifter. 64 | // 65 | lifter::amd64::lifter_t lifter; 66 | 67 | // Enumerate through each instruction. 68 | // 69 | for ( int i = begin; i <= end; i++ ) 70 | { 71 | auto& ins = instructions[ i ]; 72 | 73 | // Lift the single instruction. 74 | // 75 | lifter.process( block, ins->ins.address, ins->ins.bytes ); 76 | 77 | // If block branches, end lifting. 78 | // 79 | if ( block->is_complete() ) 80 | break; 81 | } 82 | 83 | // Return the created basic block. 84 | // 85 | return block; 86 | } 87 | } -------------------------------------------------------------------------------- /VMPDump/instruction_stream.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "instruction.hpp" 4 | #include 5 | 6 | namespace vmpdump 7 | { 8 | // This class spans over an ordered vector of instructions. 9 | // It contains an index to determine the current position in the 10 | // stream. 11 | // 12 | class instruction_stream 13 | { 14 | public: 15 | // The backing instruction vector. 16 | // This is a shared_ptr vector as instruction_streams are copyable 17 | // and thus instruction objects can have multiple owners. 18 | // 19 | std::vector> instructions; 20 | 21 | private: 22 | // Begin index of span. 23 | // 24 | uint32_t begin; 25 | 26 | // End index of span. 27 | // 28 | uint32_t end; 29 | 30 | // Current Index. 31 | // 32 | uint32_t index; 33 | 34 | public: 35 | // Default constructor / move / copy. 36 | // 37 | instruction_stream( instruction_stream&& ) = default; 38 | instruction_stream( const instruction_stream& ) = default; 39 | instruction_stream& operator= ( instruction_stream&& ) = default; 40 | instruction_stream& operator= ( const instruction_stream& ) = default; 41 | 42 | // Construct as empty. 43 | // 44 | instruction_stream() 45 | : instructions{}, begin( 0 ), end( 0 ), index( 0 ) 46 | {} 47 | 48 | // Construct via copying existing instruction vector 49 | // 50 | instruction_stream( const std::vector>& instructions ) 51 | : instructions( instructions ), begin( 0 ), end( instructions.size() - 1 ), index( 0 ) 52 | {} 53 | 54 | // Get the stream base 55 | // 56 | inline uint64_t base() const 57 | { 58 | return instructions[ begin ]->ins.address; 59 | } 60 | 61 | // Disassembler bases instructions via RVA, thus base == rva. 62 | // 63 | inline uint64_t rva() const 64 | { 65 | return base(); 66 | } 67 | 68 | // Resets index to 0 69 | // 70 | inline void reset() 71 | { 72 | index = 0; 73 | } 74 | 75 | // Advances the stream, incrementing index and returning the 76 | // instruction ptr. 77 | // Non-owning. 78 | // 79 | const instruction* next(); 80 | 81 | // Returns a byte vector of all the instructions' bytes. 82 | // 83 | std::vector bytes() const; 84 | 85 | // Lifts the instruction stream to VTIL. 86 | // 87 | vtil::basic_block* lift() const; 88 | }; 89 | } -------------------------------------------------------------------------------- /VMPDump/instruction_utilities.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace vmpdump 6 | { 7 | // Determines whether or not the register's bases are equal. 8 | // e.g. RAX == AH, as base( RAX ) == AL, and base( AH ) == AL. 9 | // 10 | inline bool register_base_equal( x86_reg first, x86_reg second ) 11 | { 12 | return vtil::amd64::registers.resolve_mapping( first ).base_register == vtil::amd64::registers.resolve_mapping( second ).base_register; 13 | } 14 | 15 | // Gets the register's largest architecture equivalent. 16 | // 17 | inline x86_reg get_largest_for_arch( x86_reg reg ) 18 | { 19 | return vtil::amd64::registers.extend( reg ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /VMPDump/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "vmpdump.hpp" 3 | #include "tables.hpp" 4 | #include 5 | #include 6 | #include "pe_constructor.hpp" 7 | #include 8 | #include "winpe/image.hpp" 9 | #include 10 | #include 11 | 12 | #ifdef _MSC_VER 13 | #pragma comment(linker, "/STACK:34359738368") 14 | #endif 15 | 16 | using namespace vtil::logger; 17 | 18 | namespace vmpdump 19 | { 20 | // User-provided settings. 21 | // 22 | struct vmpdump_settings 23 | { 24 | uint32_t target_pid; 25 | std::string module_name; 26 | std::optional ep_rva; 27 | bool disable_relocation; 28 | }; 29 | 30 | // Attempts to parse the given argument list into vmpdump settings. 31 | // 32 | std::optional parse_settings( const std::vector& arguments ) 33 | { 34 | // Ensure required argument count. 35 | // 36 | if ( arguments.size() < 3 ) 37 | return {}; 38 | 39 | // Fetch target PID. 40 | // 41 | uint32_t pid = 0; 42 | ( std::stringstream( arguments[ 1 ] ) ) >> pid; 43 | 44 | // Try to parse hex. 45 | if ( pid == 0 ) 46 | ( std::stringstream( arguments[ 1 ] ) ) >> std::hex >> pid; 47 | 48 | // Ensure PID validity. 49 | // 50 | if ( pid == 0 ) 51 | return {}; 52 | 53 | // Fetch target module name. 54 | // 55 | std::string target_module_name = arguments[ 2 ]; 56 | 57 | std::optional ep_rva = {}; 58 | bool disable_relocation = false; 59 | 60 | // Fetch any other arguments. 61 | // 62 | for ( const std::string& arg : arguments ) 63 | { 64 | // Should we overwrite the entry point with the user-provided EP? 65 | // 66 | if ( arg.find( "-ep=" ) == 0 ) 67 | { 68 | uint32_t ep; 69 | ( std::stringstream( arg.substr( 4 ) ) ) >> std::hex >> ep; 70 | 71 | ep_rva = ep; 72 | continue; 73 | } 74 | 75 | // Should we mark in the dumped module that relocs have been stripped? 76 | // 77 | if ( arg.find( "-disable-reloc" ) ) 78 | { 79 | disable_relocation = true; 80 | continue; 81 | } 82 | } 83 | 84 | return vmpdump_settings { pid, target_module_name, ep_rva, disable_relocation }; 85 | } 86 | 87 | extern "C" int main( int argc, char* argv[] ) 88 | { 89 | std::optional settings = {}; 90 | 91 | #ifndef _DEBUG 92 | // Convert C-Style array to C++ vector. 93 | // 94 | std::vector arguments; 95 | for ( int i = 0; i < argc; i++ ) 96 | arguments.push_back( { argv[ i ] } ); 97 | 98 | // Try to parse arguments. 99 | // 100 | settings = parse_settings( arguments ); 101 | #else 102 | settings = { 0x1244, "", { 0x1D420 }, true }; 103 | #endif 104 | 105 | if ( !settings ) 106 | { 107 | log( "** Failed to parse provided arguments\r\n" ); 108 | return 0; 109 | } 110 | 111 | std::unique_ptr instance = vmpdump::from_pid( settings->target_pid, settings->module_name ); 112 | 113 | if ( !instance ) 114 | { 115 | log( "** Failed to open process 0x%lx\r\n", settings->target_pid ); 116 | return 0; 117 | } 118 | 119 | log( "** Successfully opened process %s, PID 0x%lx\r\n", instance->target_module_view->module_name, instance->process_id ); 120 | log( "** Selected module: %s\r\n", instance->module_full_path ); 121 | 122 | std::map resolved_imports = {}; 123 | std::vector import_calls = {}; 124 | 125 | instance->scan_for_imports( resolved_imports, import_calls ); 126 | 127 | log( "** Found %i calls to %i imports\r\n", import_calls.size(), resolved_imports.size() ); 128 | 129 | // Define helper structures to organize retrieved data. 130 | // 131 | struct export_info 132 | { 133 | export_id_t id; 134 | uint32_t rva; 135 | }; 136 | struct module_info 137 | { 138 | module_view view; 139 | std::vector exports; 140 | }; 141 | 142 | // Resolve exports for all found imports. 143 | // 144 | std::map module_views; 145 | for ( auto& [thunk_rva, import] : resolved_imports ) 146 | { 147 | // Resolve imported module base. 148 | // 149 | std::optional import_module_base = instance->base_from_ea( import.target_ea ); 150 | if ( !import_module_base ) 151 | { 152 | log( "\t** Failed to resolve import module of function 0x%p\r\n", import.target_ea ); 153 | continue; 154 | } 155 | 156 | // If module view already exists, fetch it. 157 | // 158 | auto it = module_views.find( *import_module_base ); 159 | if ( it == module_views.end() ) 160 | { 161 | // Otherwise create the module view. 162 | // 163 | std::optional import_module_view = instance->view_from_base( *import_module_base ); 164 | if ( !import_module_view ) 165 | { 166 | log( "\t** Failed to construct module view from base 0x%p\r\n", *import_module_base ); 167 | continue; 168 | } 169 | 170 | // And insert it into the map. 171 | // 172 | it = module_views.insert( { *import_module_base, { *import_module_view, {} } } ).first; 173 | } 174 | 175 | // Convert the import target remote ea to an export identifier for the target module. 176 | // 177 | std::optional export_id = it->second.view.get_export( import.target_ea ); 178 | if ( !export_id ) 179 | { 180 | log( "\t** Failed to resolve export for export 0x%p in module %s\r\n", import.target_ea, it->second.view.module_name ); 181 | continue; 182 | } 183 | 184 | // Add the resolved export to the module's vector of exports. 185 | // 186 | it->second.exports.push_back( { *export_id, ( uint32_t )( import.target_ea - it->second.view.module_base ) } ); 187 | 188 | // Notify the user that the export was resolved. 189 | // 190 | if ( !export_id->first.empty() ) 191 | { 192 | log( "\t** Successfully resolved export ", export_id->first, it->second.view.module_name ); 193 | log( "%s ", export_id->first ); 194 | log( "in module " ); 195 | log( "%s\r\n", it->second.view.module_name ); 196 | } 197 | else 198 | { 199 | log( "\t** Successfully resolved export ", export_id->first, it->second.view.module_name ); 200 | log( "0x%lx ", export_id->second ); 201 | log( "in module " ); 202 | log( "%s\r\n", it->second.view.module_name ); 203 | } 204 | } 205 | 206 | // Build named imports. 207 | // These must be built seperately so that they are in the correct order. 208 | // 209 | std::vector named_imports; 210 | for ( auto& [module_base, module_info] : module_views ) 211 | for ( auto& [export_info, export_rva] : module_info.exports ) 212 | if ( !export_info.first.empty() ) 213 | named_imports.push_back( { ( uint16_t )export_info.second, export_info.first } ); 214 | 215 | win::image_t* target_image = instance->target_module_view->local_module.get_image(); 216 | win::nt_headers_x64_t* nt = target_image->get_nt_headers(); 217 | 218 | // Serialize import names. 219 | // 220 | uint64_t import_section_begin_rva = pe_constructor::get_sections_end( instance->target_module_view->local_module ); 221 | auto [named_imports_serialized, named_imports_rvas, named_imports_end] = pe_constructor::serialize_table( named_imports, import_section_begin_rva ); 222 | 223 | // Build import thunks and import module names. 224 | // 225 | std::map module_first_thunk_indices; 226 | std::vector module_names; 227 | std::vector import_thunks; 228 | int name_index = 0; 229 | for ( auto& [module_base, module_info] : module_views ) 230 | { 231 | module_first_thunk_indices.insert( { module_base, import_thunks.size() } ); 232 | module_names.push_back( { module_info.view.module_name } ); 233 | 234 | for ( auto& [export_info, export_rva] : module_info.exports ) 235 | { 236 | std::string& export_name = export_info.first; 237 | uint32_t export_ordinal = export_info.second; 238 | 239 | // If not named import, import by ordinal. 240 | // 241 | if ( export_name.empty() ) 242 | { 243 | // Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 244 | // 245 | image_thunk_data_x64 thunk = {}; 246 | thunk.is_ordinal = true; 247 | thunk.ordinal = export_ordinal; 248 | 249 | import_thunks.push_back( thunk ); 250 | } 251 | // Otherwise, import by name RVA. 252 | // 253 | else 254 | { 255 | uint32_t named_import_rva = named_imports_rvas[ name_index ]; 256 | name_index++; 257 | 258 | import_thunks.push_back( image_thunk_data_x64{ .address = named_import_rva } ); 259 | } 260 | } 261 | 262 | // Add an empty thunk to indicate module end. 263 | // 264 | import_thunks.push_back( {} ); 265 | } 266 | 267 | // Serialize module names and import thunks. 268 | // 269 | auto [module_names_serialized, module_names_rvas, module_names_end] = pe_constructor::serialize_table( module_names, named_imports_end ); 270 | 271 | // Unlike the import table, we aren't gonna create a new IAT; we are going to append the existing one instead. 272 | // This is because we want to make sure that the existing, non-obfuscated imports are still valid, and it's easier 273 | // to just append to the existing IAT rather than scanning for all existing imports and relocating them. 274 | // 275 | // TODO: Check if the IAT actually exists before using it. 276 | // 277 | uint32_t appended_import_thunks_rva = nt->optional_header.data_directories.iat_directory.rva + nt->optional_header.data_directories.iat_directory.size; 278 | auto [import_thunks_serialized, import_thunks_rvas, import_thunks_end] = pe_constructor::serialize_table( import_thunks, appended_import_thunks_rva ); 279 | 280 | // Create map of {export remote ea, thunk rva} for easy future thunk lookup. 281 | // 282 | std::map export_thunk_rvas; 283 | int thunk_index = 0; 284 | for ( auto& [module_base, module_info] : module_views ) 285 | { 286 | for ( auto& [export_info, export_rva] : module_info.exports ) 287 | { 288 | export_thunk_rvas.insert( { module_base + export_rva, import_thunks_rvas[ thunk_index ] } ); 289 | thunk_index++; 290 | } 291 | thunk_index++; 292 | } 293 | 294 | // Now that we have built and serialized the new import thunks, we can fix the calls to said thunks. 295 | // 296 | log( "** Converting %i calls\r\n", import_calls.size(), resolved_imports.size() ); 297 | for ( auto& import_call : import_calls ) 298 | { 299 | if ( instance->convert_local_call( import_call, instance->target_module_view->module_base + export_thunk_rvas[ import_call.import->target_ea ] ) ) 300 | log( "\t** Successfully converted call @ RVA 0x%lx to thunk @ RVA 0x%lx\r\n", import_call.call_rva, export_thunk_rvas[ import_call.import->target_ea ] ); 301 | else 302 | log( "\t** Failed to convert call @ RVA 0x%lx\r\n", import_call.call_rva ); 303 | } 304 | 305 | // Parse & transfer existing import directories. 306 | // As we are creating a new import table, we must preserve the current one by copying it. 307 | // 308 | std::vector import_directories; 309 | uint8_t* existing_imports_base = instance->target_module_view->local_module.raw_bytes.data() + nt->optional_header.data_directories.import_directory.rva; 310 | size_t import_table_offset = 0; 311 | while ( true ) 312 | { 313 | // Verify we have enough space left for another iteration. 314 | // 315 | if ( import_table_offset + sizeof( win::import_directory_t ) >= nt->optional_header.data_directories.import_directory.size ) 316 | break; 317 | 318 | win::import_directory_t* import_dir = ( win::import_directory_t* )( existing_imports_base + import_table_offset ); 319 | 320 | import_directories.push_back( 321 | { 322 | .rva_original_first_thunk = import_dir->rva_original_first_thunk, 323 | .timedate_stamp = import_dir->timedate_stamp, 324 | .forwarder_chain = import_dir->forwarder_chain, 325 | .rva_name = import_dir->rva_name, 326 | .rva_first_thunk = import_dir->rva_first_thunk 327 | } ); 328 | 329 | // Increment the import table offset by the table size. 330 | // 331 | import_table_offset += sizeof( win::import_directory_t ); 332 | } 333 | 334 | // Build import directories. 335 | // 336 | int i = 0; 337 | for ( auto [module_base, first_thunk_index] : module_first_thunk_indices ) 338 | { 339 | import_directories.push_back( 340 | { 341 | .rva_original_first_thunk = import_thunks_rvas[ first_thunk_index ], 342 | .timedate_stamp = 0, 343 | .forwarder_chain = 0, 344 | .rva_name = module_names_rvas[ i ], 345 | .rva_first_thunk = import_thunks_rvas[ first_thunk_index ] 346 | } ); 347 | i++; 348 | } 349 | 350 | // Serialize import directories. 351 | // 352 | auto [import_directories_serialized, import_directories_rvas, import_directories_end] = pe_constructor::serialize_table( import_directories, module_names_end ); 353 | 354 | // Concat each serialized buffer to build the new import table section. 355 | // 356 | std::vector import_section; 357 | import_section.insert( import_section.end(), named_imports_serialized.begin(), named_imports_serialized.end() ); 358 | import_section.insert( import_section.end(), module_names_serialized.begin(), module_names_serialized.end() ); 359 | import_section.insert( import_section.end(), import_directories_serialized.begin(), import_directories_serialized.end() ); 360 | 361 | // Convert the virtual pe image to a raw pe image. 362 | // 363 | pe_image raw_module = pe_constructor::virtual_to_raw_image( instance->target_module_view->local_module ); 364 | 365 | // Add the new section to the raw module. 366 | // 367 | pe_constructor::add_section( raw_module, import_section, import_section_begin_rva, ".vmpdmp", { 0x40000040 } ); 368 | 369 | // Set new import data directory. 370 | // 371 | auto raw_nt = raw_module.get_image()->get_nt_headers(); 372 | raw_nt->optional_header.data_directories.import_directory.rva = module_names_end; 373 | raw_nt->optional_header.data_directories.import_directory.size = import_directories_end - module_names_end; 374 | 375 | // Add our new import thunks to the pre-existing IAT. 376 | // TODO: verify we have enough space left in the section! 377 | // 378 | memcpy( raw_module.get_image()->rva_to_ptr( appended_import_thunks_rva ), import_thunks_serialized.data(), import_thunks_serialized.size() ); 379 | raw_nt->optional_header.data_directories.iat_directory.size += import_thunks_serialized.size(); 380 | 381 | // Update EP if provided. 382 | // 383 | if ( settings->ep_rva ) 384 | raw_nt->optional_header.entry_point = *settings->ep_rva; 385 | 386 | // Disable relocation if requested. 387 | // 388 | if ( settings->disable_relocation ) 389 | raw_nt->file_header.characteristics.relocs_stripped = true; 390 | 391 | // Remove any integrity flags. 392 | // 393 | raw_nt->optional_header.characteristics.force_integrity = false; 394 | 395 | log( "** New ImageBase: 0x%llx, SizeOfImage: 0x%lx\r\n", raw_nt->optional_header.image_base, raw_nt->optional_header.size_image ); 396 | 397 | // Save module. 398 | // 399 | std::filesystem::path module_path = { instance->module_full_path }; 400 | module_path.remove_filename(); 401 | module_path /= instance->target_module_view->module_name; 402 | 403 | module_path.replace_extension( "VMPDump" + module_path.extension().string() ); 404 | std::ofstream outfile( module_path.string(), std::ios::out | std::ios::binary ); 405 | outfile.write( ( const char* )raw_module.raw_bytes.data(), raw_module.raw_bytes.size() ); 406 | 407 | log( "** File written to: %s\r\n", module_path.string() ); 408 | 409 | return 0; 410 | } 411 | } -------------------------------------------------------------------------------- /VMPDump/module_view.cpp: -------------------------------------------------------------------------------- 1 | #include "module_view.hpp" 2 | #include 3 | 4 | namespace vmpdump 5 | { 6 | // Commits any local module changes back to the target process. 7 | // 8 | bool module_view::commit() const 9 | { 10 | bool result = false; 11 | 12 | // Try to open the process. 13 | // 14 | HANDLE process_handle = OpenProcess( PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, process_id ); 15 | if ( !process_handle ) 16 | return false; 17 | 18 | // Get RWX permissions. 19 | // 20 | DWORD new_protect = PAGE_EXECUTE_READWRITE; 21 | DWORD old_protect; 22 | if ( !VirtualProtectEx( process_handle, ( LPVOID )module_base, module_size, new_protect, &old_protect ) ) 23 | goto cleanup; 24 | 25 | // Write the memory. 26 | // 27 | SIZE_T num_written; 28 | if ( WriteProcessMemory( process_handle, ( LPVOID )module_base, local_module.cdata(), local_module.size(), &num_written ) && num_written == module_size ) 29 | result = true; 30 | 31 | // Restore old memory permissions. 32 | // 33 | if ( !VirtualProtectEx( process_handle, ( LPVOID )module_base, module_size, old_protect, &new_protect ) ) 34 | result = false; 35 | 36 | // On function exit, close the handle. 37 | // 38 | cleanup: 39 | CloseHandle( process_handle ); 40 | return result; 41 | } 42 | 43 | // Fetches any remote module changes back to the local module buffer. 44 | // 45 | bool module_view::fetch() 46 | { 47 | bool result = false; 48 | 49 | // Try to open the process. 50 | // 51 | HANDLE process_handle = OpenProcess( PROCESS_ALL_ACCESS, FALSE, process_id ); 52 | if ( !process_handle ) 53 | return false; 54 | 55 | // Resize the local module in case it's not allocated yet. 56 | // 57 | local_module.raw_bytes.resize( module_size ); 58 | 59 | // Read the memory. 60 | // 61 | SIZE_T num_written; 62 | if ( ReadProcessMemory( process_handle, ( LPVOID )module_base, local_module.data(), local_module.size(), &num_written ) && num_written == local_module.size() ) 63 | result = true; 64 | 65 | // On function exit, close the handle. 66 | // 67 | cleanup: 68 | CloseHandle( process_handle ); 69 | return result; 70 | } 71 | 72 | // Returns the export name (if available) and ordinal. 73 | // 74 | std::optional module_view::get_export( remote_ea_t ea ) 75 | { 76 | using namespace win; 77 | 78 | uint64_t rva = ea - module_base; 79 | 80 | // Check if ea is in module bounds. 81 | // 82 | if ( !within_bounds( ea ) ) 83 | return {}; 84 | 85 | auto image = local_module.get_image(); 86 | 87 | auto export_dir_header = image->get_directory( directory_id::directory_entry_export ); 88 | if ( !export_dir_header->present() ) 89 | return {}; 90 | 91 | auto export_dir = ( export_directory_t* )( local_module.data() + export_dir_header->rva ); 92 | 93 | // Resolve effective addresses of each export table. 94 | // 95 | uint32_t* eat = ( uint32_t* )( local_module.data() + export_dir->rva_functions ); 96 | uint32_t* names = ( uint32_t* )( local_module.data() + export_dir->rva_names ); 97 | uint16_t* name_ordinals = ( uint16_t* )( local_module.data() + export_dir->rva_name_ordinals ); 98 | 99 | uint32_t function_ordinal = -1; 100 | 101 | // Resolve function ordinal. 102 | // 103 | for ( uint32_t i = 0; i < export_dir->num_functions; i++ ) 104 | if ( eat[ i ] == rva ) 105 | function_ordinal = i; 106 | 107 | // Verify function was found. 108 | // 109 | if ( function_ordinal == -1 ) 110 | return {}; 111 | 112 | uint32_t name_ordinal = -1; 113 | 114 | // Resolve name ordinal. 115 | // 116 | for ( uint32_t i = 0; i < export_dir->num_names; i++ ) 117 | if ( name_ordinals[ i ] == function_ordinal ) 118 | name_ordinal = i; 119 | 120 | uint32_t ordinal = export_dir->base + function_ordinal; 121 | 122 | // If no name ordinal found, return function ordinal. 123 | // 124 | if ( name_ordinal == -1 ) 125 | return { { { "" }, ordinal } }; 126 | 127 | // Return function name. 128 | // 129 | return { { std::string( ( const char* )( local_module.data() + names[ name_ordinal ] ) ), ordinal } }; 130 | } 131 | } -------------------------------------------------------------------------------- /VMPDump/module_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "pe_image.hpp" 9 | 10 | namespace vmpdump 11 | { 12 | // A remote process effective address. 13 | // 14 | using remote_ea_t = uintptr_t; 15 | 16 | // Identifies an export within a module. 17 | // 18 | using export_id_t = std::pair; 19 | 20 | // This class allows provides easy remote module read + write capabilities. 21 | // 22 | struct module_view 23 | { 24 | // The target process id. 25 | // 26 | const uint32_t process_id; 27 | 28 | // The name of the target module, or empty if not available. 29 | // 30 | const std::string module_name; 31 | 32 | // The base of the remote module in the target process. 33 | // 34 | const remote_ea_t module_base; 35 | 36 | // The virtual size of the module. 37 | // 38 | const size_t module_size; 39 | 40 | // The locally copied module. 41 | // 42 | pe_image local_module; 43 | 44 | // Determined whether the provided remote ea is within module bounds. 45 | // 46 | inline bool within_bounds( remote_ea_t ea ) const 47 | { 48 | return ea >= module_base && ea < module_base + module_size; 49 | } 50 | 51 | // Commits any local module changes back to the target process. 52 | // 53 | bool commit() const; 54 | 55 | // Fetches any remote module changes back to the local module buffer. 56 | // 57 | bool fetch(); 58 | 59 | // Returns the export name (if available) and ordinal. 60 | // 61 | std::optional get_export( remote_ea_t ea ); 62 | 63 | // Constructor, automatically fetching the remote module's bytes. 64 | // 65 | module_view( uint32_t process_id, const std::string& module_name, remote_ea_t module_base, size_t module_size ) 66 | : process_id( process_id ), module_name( module_name ), module_base( module_base ), module_size( module_size ) 67 | { 68 | fetch(); 69 | } 70 | 71 | // Constructor. 72 | // 73 | module_view( uint32_t process_id, const std::string& module_name, remote_ea_t module_base, size_t module_size, const pe_image& local_module ) 74 | : process_id( process_id ), module_name( module_name ), module_base( module_base ), module_size( module_size ), local_module( local_module ) 75 | {} 76 | }; 77 | } -------------------------------------------------------------------------------- /VMPDump/pe_constructor.cpp: -------------------------------------------------------------------------------- 1 | #include "pe_constructor.hpp" 2 | #include 3 | 4 | namespace vmpdump 5 | { 6 | namespace pe_constructor 7 | { 8 | // Converts the given virtual image to a raw-byte image. 9 | // 10 | pe_image virtual_to_raw_image( pe_image& virtual_image ) 11 | { 12 | using namespace win; 13 | 14 | std::vector& virtual_raw_bytes = virtual_image.raw_bytes; 15 | image_x64_t* img = virtual_image.get_image(); 16 | nt_headers_x64_t* nt = img->get_nt_headers(); 17 | 18 | std::vector raw_bytes; 19 | 20 | // Copy headers. 21 | // 22 | raw_bytes.insert( raw_bytes.end(), virtual_raw_bytes.begin(), virtual_raw_bytes.begin() + nt->optional_header.size_headers ); 23 | 24 | uint32_t section_alignment = nt->optional_header.section_alignment; 25 | 26 | // Copy each section. 27 | // We are using virtual addressing here on purpose, as in packed VMP files the raw data is NULL. 28 | // 29 | for ( int i = 0; i < nt->file_header.num_sections; i++ ) 30 | { 31 | section_header_t* section = nt->get_section( i ); 32 | 33 | // Determine aligned section end. 34 | // 35 | uint32_t section_end = section->virtual_address + section->virtual_size; 36 | uint32_t required_alignment = section_alignment - ( section_end % section_alignment ); 37 | 38 | // Resize vector if required. 39 | // 40 | if ( raw_bytes.size() < section_end + required_alignment ) 41 | raw_bytes.resize( section_end + required_alignment ); 42 | 43 | // Copy section bytes. 44 | // 45 | std::copy( virtual_raw_bytes.begin() + section->virtual_address, virtual_raw_bytes.begin() + section->virtual_address + section->virtual_size, raw_bytes.begin() + section->virtual_address ); 46 | } 47 | 48 | // Construct the raw image. 49 | // 50 | pe_image raw_image = { raw_bytes }; 51 | image_x64_t* raw_img = raw_image.get_image(); 52 | nt_headers_x64_t* raw_nt = raw_img->get_nt_headers(); 53 | 54 | // Copy virtual addresses to raw addresses. 55 | // 56 | for ( int i = 0; i < raw_nt->file_header.num_sections; i++ ) 57 | { 58 | section_header_t* section = raw_nt->get_section( i ); 59 | 60 | uint32_t required_alignment = section_alignment - ( section->virtual_size % section_alignment ); 61 | 62 | section->ptr_raw_data = section->virtual_address; 63 | section->size_raw_data = section->virtual_size + required_alignment; 64 | } 65 | 66 | return raw_image; 67 | } 68 | 69 | // Determines the RVA at which the last section of the virtual image provided ends. 70 | // 71 | uint32_t get_sections_end( pe_image& virtual_image ) 72 | { 73 | using namespace win; 74 | 75 | image_x64_t* img = virtual_image.get_image(); 76 | nt_headers_x64_t* nt = img->get_nt_headers(); 77 | 78 | uint32_t highest_section_end = 0; 79 | 80 | // Enumerate each section. 81 | // 82 | for ( int i = 0; i < nt->file_header.num_sections; i++ ) 83 | { 84 | section_header_t* section = nt->get_section( i ); 85 | 86 | uint32_t required_alignment = nt->optional_header.section_alignment - ( section->virtual_size % nt->optional_header.section_alignment ); 87 | 88 | uint32_t section_end = section->virtual_address + section->virtual_size + required_alignment; 89 | 90 | if ( section_end > highest_section_end ) 91 | highest_section_end = section_end; 92 | } 93 | 94 | return highest_section_end; 95 | } 96 | 97 | // Adds the given section, denoted by the byte vector, to the raw pe_image. 98 | // 99 | pe_image& add_section( pe_image& raw_image, const std::vector& section, uint32_t va, const std::string& name, win::section_characteristics_t characteristics ) 100 | { 101 | using namespace win; 102 | 103 | uint32_t file_alignment = raw_image.get_image()->get_nt_headers()->optional_header.file_alignment; 104 | uint32_t section_alignment = raw_image.get_image()->get_nt_headers()->optional_header.section_alignment; 105 | uint32_t required_raw_alignment = file_alignment - ( section.size() % file_alignment ); 106 | uint32_t required_section_alignment = section_alignment - ( section.size() % section_alignment ); 107 | 108 | // Create new section header. 109 | // 110 | section_header_t new_section = {}; 111 | memcpy( &new_section.name, name.data(), name.size() >= LEN_SECTION_NAME ? LEN_SECTION_NAME - 1 : name.size() ); 112 | new_section.virtual_size = section.size(); 113 | new_section.virtual_address = va; 114 | new_section.size_raw_data = section.size() + required_raw_alignment; 115 | new_section.ptr_raw_data = raw_image.size(); 116 | new_section.characteristics = characteristics; 117 | 118 | // Add new section to raw buffer. 119 | // 120 | raw_image.raw_bytes.insert( raw_image.raw_bytes.end(), section.begin(), section.end() ); 121 | raw_image.raw_bytes.resize( raw_image.raw_bytes.size() + required_raw_alignment ); 122 | 123 | image_x64_t* img = raw_image.get_image(); 124 | nt_headers_x64_t* nt = img->get_nt_headers(); 125 | 126 | // Copy new section. 127 | // 128 | // TODO: verify that we can fit in another section in the headers, and if we can't, increase 129 | // header size and relocate the rest of the image! 130 | // 131 | *nt->get_section( nt->file_header.num_sections ) = new_section; 132 | 133 | // Increment number of sections. 134 | // 135 | nt->file_header.num_sections++; 136 | 137 | // Write new SizeOfImage if required. 138 | // 139 | if ( nt->optional_header.size_image < new_section.virtual_address + new_section.virtual_size ) 140 | nt->optional_header.size_image = new_section.virtual_address + new_section.virtual_size + required_section_alignment; 141 | 142 | return raw_image; 143 | } 144 | } 145 | } -------------------------------------------------------------------------------- /VMPDump/pe_constructor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pe_image.hpp" 7 | 8 | namespace vmpdump 9 | { 10 | namespace pe_constructor 11 | { 12 | // Serializes the given table, returning a tuple of { serialized table bytes, entry offsets, end rva } 13 | // 14 | template 15 | std::tuple, std::vector, uint32_t> serialize_table( const std::vector& table_entries, uint64_t offset_base = 0 ) 16 | { 17 | // Result vectors. 18 | // 19 | std::vector result_bytes; 20 | std::vector result_offsets; 21 | 22 | // Enumerate each table entry. 23 | // 24 | for ( const T& entry : table_entries ) 25 | { 26 | // Push back current offset. 27 | // 28 | result_offsets.push_back( result_bytes.size() + offset_base ); 29 | 30 | // Fetch a byte vector of the entry's data. 31 | // 32 | std::vector entry_bytes = entry.get_bytes(); 33 | 34 | // Add entry bytes to byte vector. 35 | // 36 | result_bytes.insert( result_bytes.end(), entry_bytes.begin(), entry_bytes.end() ); 37 | } 38 | 39 | // Return the values. 40 | // 41 | return { result_bytes, result_offsets, result_bytes.size() + offset_base }; 42 | } 43 | 44 | // Converts the given virtual image to a raw-byte image. 45 | // 46 | pe_image virtual_to_raw_image( pe_image& virtual_image ); 47 | 48 | // Determines the RVA at which the last section of the virtual image provided ends. 49 | // 50 | uint32_t get_sections_end( pe_image& virtual_image ); 51 | 52 | // Adds the given section, denoted by the byte vector, to the raw pe_image. 53 | // 54 | pe_image& add_section( pe_image& raw_image, const std::vector& section, uint32_t va, const std::string& name, win::section_characteristics_t characteristics ); 55 | } 56 | } -------------------------------------------------------------------------------- /VMPDump/pe_image.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "winpe/image.hpp" 4 | 5 | namespace vmpdump 6 | { 7 | // Describes a 64/32 bit Microsoft Portable Executable Image. 8 | // 9 | struct pe_image 10 | { 11 | // Construct by raw byte array. 12 | // 13 | std::vector raw_bytes; 14 | pe_image( const std::vector& raw_bytes = {} ) : raw_bytes( raw_bytes ) {} 15 | 16 | // Default move/copy. 17 | // 18 | pe_image( pe_image&& ) = default; 19 | pe_image( const pe_image& ) = default; 20 | pe_image& operator=( pe_image&& ) = default; 21 | pe_image& operator=( const pe_image& ) = default; 22 | 23 | inline uint8_t* data() { return raw_bytes.data(); } 24 | inline const uint8_t* cdata() const { return raw_bytes.data(); } 25 | inline size_t size() const { return raw_bytes.size(); } 26 | 27 | inline win::image_t* get_image() { return ( win::image_t* )data(); } 28 | }; 29 | } -------------------------------------------------------------------------------- /VMPDump/tables.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace vmpdump 7 | { 8 | struct import_directory 9 | { 10 | union 11 | { 12 | uint32_t characteristics; 13 | uint32_t rva_original_first_thunk; 14 | }; 15 | uint32_t timedate_stamp; 16 | uint32_t forwarder_chain; 17 | uint32_t rva_name; 18 | uint32_t rva_first_thunk; 19 | 20 | inline std::vector get_bytes() const 21 | { 22 | // Allocate result vector. 23 | // 24 | std::vector result; 25 | result.resize( sizeof( *this ) ); 26 | 27 | // Copy data. 28 | // 29 | memcpy( result.data(), this, sizeof( *this ) ); 30 | 31 | return result; 32 | } 33 | }; 34 | 35 | struct image_thunk_data_x64 36 | { 37 | union 38 | { 39 | uint64_t forwarder_string; 40 | uint64_t function; 41 | uint64_t address; 42 | struct 43 | { 44 | uint64_t ordinal : 16; 45 | uint64_t _reserved0 : 47; 46 | uint64_t is_ordinal : 1; 47 | }; 48 | }; 49 | 50 | inline std::vector get_bytes() const 51 | { 52 | // Allocate result vector. 53 | // 54 | std::vector result; 55 | result.resize( sizeof( *this ) ); 56 | 57 | // Copy data. 58 | // 59 | memcpy( result.data(), this, sizeof( *this ) ); 60 | 61 | return result; 62 | } 63 | }; 64 | 65 | struct import_named_import 66 | { 67 | uint16_t hint; 68 | std::string name; 69 | 70 | inline std::vector get_bytes() const 71 | { 72 | // Allocate result vector. 73 | // 74 | std::vector result; 75 | result.resize( sizeof( hint ) + name.size() + 1 ); 76 | 77 | // Copy data. 78 | // 79 | memcpy( result.data(), &hint, sizeof( hint ) ); 80 | memcpy( result.data() + sizeof( hint ), name.data(), name.size() ); 81 | 82 | return result; 83 | } 84 | }; 85 | 86 | struct embedded_string 87 | { 88 | std::string string; 89 | 90 | inline std::vector get_bytes() const 91 | { 92 | std::vector result = { string.begin(), string.end() }; 93 | result.push_back( '\0' ); 94 | 95 | return result; 96 | } 97 | }; 98 | } -------------------------------------------------------------------------------- /VMPDump/types.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | -------------------------------------------------------------------------------- /VMPDump/vmpdump.cpp: -------------------------------------------------------------------------------- 1 | #include "vmpdump.hpp" 2 | #include 3 | #include 4 | #include "disassembler.hpp" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace vmpdump 14 | { 15 | // Structure used to return raw import stub analysis information. 16 | // 17 | struct import_stub_analysis 18 | { 19 | uintptr_t thunk_rva; 20 | uintptr_t dest_offset; 21 | int32_t stack_adjustment; 22 | bool padding; 23 | bool is_jmp; 24 | }; 25 | 26 | // Attempts to generate structures from the provided call EA and instruction_stream of a VMP import stub. 27 | // Returns empty {} if the import stub failed analysis (and therefore is an invalid stub). 28 | // 29 | std::optional analyze_import_stub( const instruction_stream& stream ) 30 | { 31 | using namespace vtil; 32 | 33 | // Lift the given instruction stream to VTIL. 34 | // 35 | basic_block* lifted_block = stream.lift(); 36 | 37 | // Ensure lifted block is valid. 38 | // 39 | if ( !lifted_block->is_complete() ) 40 | return {}; 41 | 42 | // Get the iterator just before the VMEXIT at the end. 43 | // This is the baseline we'll be using to see how certain registers / stack variables changed during the stub. 44 | // 45 | vtil::basic_block::const_iterator iterator = std::prev( lifted_block->end() ); 46 | 47 | // Verify that the last instruction is a JMP to a register. 48 | // 49 | if ( iterator->base->name != "jmp" || !iterator->operands[ 0 ].is_register() ) 50 | return {}; 51 | 52 | // Trace each variable that we'll be using to analyze the stub. 53 | // 54 | cached_tracer tracer; 55 | symbolic::expression::reference dest_expression = tracer.trace( { iterator, iterator->operands[ 0 ].reg() } ); 56 | symbolic::expression::reference sp_expression = tracer.trace( { iterator, REG_SP } ); 57 | symbolic::expression::reference retaddr_expression = tracer.trace( { iterator, { sp_expression, 64 } } ); 58 | 59 | #ifdef _DEBUG 60 | logger::log( "** Import stub analysis: dest_expression: %s sp_expression: %s retaddr_expression: %s\r\n", dest_expression, sp_expression, retaddr_expression ); 61 | #endif 62 | 63 | // Check if the retaddr expression matches the [CONST] + CONST expression. 64 | // 65 | uint64_t thunk_rva = 0; 66 | uint64_t dest_offset = 0; 67 | { 68 | using namespace symbolic::directive; 69 | 70 | int64_t sign; 71 | stack_vector results; 72 | if ( ( sign = +1, fast_match( &results, V + U, dest_expression ) ) || 73 | ( sign = -1, fast_match( &results, V - U, dest_expression ) ) || 74 | ( sign = +0, fast_match( &results, V, dest_expression ) ) ) 75 | { 76 | auto& var = results.front().translate( V )->uid.get(); 77 | if ( !var.is_memory() || !var.mem().decay()->is_constant() ) 78 | return {}; 79 | 80 | thunk_rva = *var.mem().decay()->get(); 81 | if ( sign != 0 ) 82 | dest_offset = sign * *results.front().translate( U )->get(); 83 | } 84 | else 85 | { 86 | return {}; 87 | } 88 | } 89 | 90 | symbolic::expression::reference retaddr_sp_exp; 91 | 92 | // Check if return address is padded. 93 | // TODO: rewrite this in a nicer way. 94 | // 95 | bool pad = false; 96 | { 97 | symbolic::expression::reference lhs = retaddr_expression->lhs; 98 | symbolic::expression::reference rhs = retaddr_expression->rhs; 99 | 100 | if ( lhs && rhs && lhs->is_variable() && rhs->is_constant() ) 101 | { 102 | uint32_t constant = *rhs->get(); 103 | 104 | if ( constant != 1 ) 105 | logger::log( "** Warning: Unexpected value for padding: 0x%lx\r\n", constant ); 106 | 107 | pad = true; 108 | 109 | // Set retaddr sp exp to [lhs]. 110 | // 111 | retaddr_sp_exp = lhs->uid.get().mem().base.base; 112 | } 113 | else 114 | retaddr_sp_exp = retaddr_expression->uid.get().mem().base.base; 115 | } 116 | 117 | #ifdef _DEBUG 118 | logger::log( "** Import stub analysis: retaddr_sp_exp: %s\r\n", retaddr_sp_exp ); 119 | #endif 120 | 121 | // Subtract initial SP from final SP to get the SP adjustment. 122 | // 123 | symbolic::expression stack_adjustment_expr = ( sp_expression - symbolic::CTX( lifted_block->begin() )[ REG_SP ] ).simplify( true ); 124 | #ifdef _DEBUG 125 | logger::log( "** Import stub analysis: stack_adjustment_expr: %s\r\n", stack_adjustment_expr ); 126 | #endif 127 | 128 | // Check if is jmp. 129 | // 130 | bool is_jmp = retaddr_sp_exp->equals( *sp_expression ) && *stack_adjustment_expr.get() >= 8; 131 | #ifdef _DEBUG 132 | logger::log( "** Import stub analysis: is_jmp: %d\r\n", is_jmp ); 133 | #endif 134 | 135 | if ( !stack_adjustment_expr.is_constant() ) 136 | return {}; 137 | 138 | // If is jump, expect stack adjustment of -0x8 to account for the initial call stub. 139 | // 140 | int32_t sp_adjustment = *stack_adjustment_expr.get() - ( is_jmp ? 8 : 0 ); 141 | 142 | // Construct the analysis result object. 143 | // 144 | return import_stub_analysis { thunk_rva, dest_offset, sp_adjustment, pad, is_jmp }; 145 | } 146 | 147 | // Scans the specified code range for any import calls and imports. 148 | // resolved_imports is a map of { import thunk rva, import structure }. 149 | // 150 | bool vmpdump::scan_for_imports( uint64_t rva, size_t code_size, std::map& resolved_imports, std::vector& import_calls, uint32_t flags ) 151 | { 152 | uint8_t* local_module_bytes = ( uint8_t* )target_module_view->local_module.data(); 153 | 154 | size_t size = code_size; 155 | 156 | uint8_t* code_start = local_module_bytes + rva; 157 | 158 | uint64_t start_offset = rva; 159 | uint64_t offset = start_offset; 160 | 161 | // Retain the previously disassembled instruction for future use. 162 | // 163 | std::optional previous_instruction = {}; 164 | 165 | // While iterative disassembly is successful. 166 | // 167 | while ( true ) 168 | { 169 | // Check if we're within bounds. 170 | // 171 | if ( offset >= start_offset + code_size ) 172 | break; 173 | 174 | // In case disassembly failed (due to invalid instructions), try to continue by incrementing offset. 175 | // 176 | if ( !cs_disasm_iter( disassembler::get().get_handle(), ( const uint8_t** )&code_start, &size, &offset, disassembler::get().get_insn() ) ) 177 | { 178 | offset++; 179 | code_start++; 180 | 181 | continue; 182 | } 183 | 184 | instruction ins = { disassembler::get().get_insn() }; 185 | 186 | // In order to scan mutated code without failing, we are following 1 and 2 byte absolute jumps. 187 | // 188 | if ( ins.ins.id == X86_INS_JMP 189 | && ins.operand_type( 0 ) == X86_OP_IMM ) 190 | { 191 | uint32_t jump_offset = ins.operand( 0 ).imm - ( ins.ins.address + ins.ins.size ); 192 | 193 | if ( jump_offset == 1 || jump_offset == 2 ) 194 | { 195 | offset += jump_offset; 196 | code_start += jump_offset; 197 | 198 | previous_instruction = ins; 199 | 200 | continue; 201 | } 202 | } 203 | 204 | // If the instruction is a relative ( E8 ) call. 205 | // 206 | if ( ins.ins.id == X86_INS_CALL && ins.operand_type( 0 ) == X86_OP_IMM && ins.ins.bytes[ 0 ] == 0xE8 ) 207 | { 208 | uint64_t call_target_offset = ins.operand( 0 ).imm; 209 | uint8_t* call_target = local_module_bytes + call_target_offset; 210 | 211 | // Ensure that the call destination is valid memory in the first place. 212 | // 213 | if ( !IsBadReadPtr( call_target, 1 ) ) 214 | { 215 | // Disassemble at the call target. 216 | // Max 25 instructions, in order to filter out invalid calls. 217 | // 218 | instruction_stream stream = disassembler::get().disassemble( ( uint64_t )local_module_bytes, call_target_offset, disassembler_take_unconditional_imm, 25 ); 219 | 220 | // Perform more preliminary filtering, so we only pass the most valid calls to the costly VTIL analysis. 221 | // 222 | if ( !stream.instructions.empty() && stream.instructions[ stream.instructions.size() - 1 ]->ins.id == X86_INS_RET ) 223 | { 224 | // Analyze the disassembled stream as a VMP import stub. 225 | // 226 | if ( std::optional stub_analysis = analyze_import_stub( stream ) ) 227 | { 228 | // vtil::logger::log( "** Resolved import stub @ 0x%p\r\n", ins.ins.address ); 229 | 230 | // Compute the ea of the function, in the target process. 231 | // 232 | uintptr_t target_ea = *( uintptr_t* )( local_module_bytes + stub_analysis->thunk_rva ) + stub_analysis->dest_offset; 233 | 234 | // If it doesn't already exist within the map, insert the import. 235 | // 236 | const resolved_import* referenced_import = &resolved_imports.insert( { stub_analysis->thunk_rva, { stub_analysis->thunk_rva, target_ea } } ).first->second; 237 | 238 | // Record the call to the import. 239 | // 240 | import_calls.push_back( { ins.ins.address, referenced_import, stub_analysis->stack_adjustment, stub_analysis->padding, stub_analysis->is_jmp, previous_instruction } ); 241 | 242 | // If the call is a jump, and has no backwards (push) padding, it must be padded after the stub. 243 | // Because jumps don't return, this information won't be provided to us by the analysis, so we have 244 | // to skip the next byte to prevent potentially invalid disassembly. 245 | // 246 | if ( stub_analysis->is_jmp && stub_analysis->stack_adjustment == 0 ) 247 | { 248 | offset++; 249 | code_start++; 250 | } 251 | } 252 | // else 253 | // vtil::logger::log( "** Potentially skipped import call @ RVA 0x%p\r\n", ins.ins.address ); 254 | } 255 | } 256 | } 257 | 258 | previous_instruction = ins; 259 | } 260 | 261 | return true; 262 | } 263 | 264 | // Scans all executable sections of the image for any import calls and imports. 265 | // 266 | bool vmpdump::scan_for_imports( std::map& resolved_imports, std::vector& import_calls, uint32_t flags ) 267 | { 268 | using namespace win; 269 | 270 | bool failed = false; 271 | 272 | nt_headers_t* nt = target_module_view->local_module.get_image()->get_nt_headers(); 273 | 274 | // Enumerate image sections. 275 | // 276 | for ( int i = 0; i < nt->file_header.num_sections; i++ ) 277 | { 278 | section_header_t* section = nt->get_section( i ); 279 | 280 | if ( section->characteristics.mem_read && section->characteristics.mem_execute && section->characteristics.cnt_code ) 281 | failed |= !scan_for_imports( section->virtual_address, section->virtual_size, resolved_imports, import_calls, flags ); 282 | } 283 | 284 | return !failed; 285 | } 286 | 287 | // Attempts to generate a stub in a code cave which jmps to the given thunk. 288 | // Returns the stub rva. 289 | // 290 | std::optional vmpdump::generate_stub( uint32_t rva, remote_ea_t thunk ) 291 | { 292 | // Save all stubs so we don't re-create them on each call. 293 | // 294 | static std::map stubs; 295 | 296 | // If the stub was already created, just return its rva. 297 | // 298 | auto it = stubs.find( thunk ); 299 | if ( it != stubs.end() ) 300 | return it->second; 301 | 302 | // We need 6 bytes for a thunk call. 303 | // 304 | const uint32_t req_len = 6; 305 | 306 | // Increase the section size. 307 | // 308 | auto section = target_module_view->local_module.get_image()->rva_to_section( rva ); 309 | uint32_t stub_rva = section->virtual_address + section->virtual_size; 310 | section->virtual_size += req_len; 311 | 312 | // TODO: Handle if there is no more padding left in the section to overwrite..... 313 | // 314 | // ... 315 | 316 | // If no code-cave found, return empty {}. 317 | // 318 | if ( !stub_rva ) 319 | return {}; 320 | 321 | // Assemble a jump. 322 | // 323 | std::vector jump = vtil::amd64::assemble( vtil::format::str( "jmp [0x%p]", thunk ), target_module_view->module_base + stub_rva ); 324 | 325 | // Sanity-check the size. 326 | // 327 | if ( jump.size() > 6 ) 328 | return {}; 329 | 330 | // Copy the assembled jump to the code-cave. 331 | // 332 | memcpy( target_module_view->local_module.data() + stub_rva, jump.data(), jump.size() ); 333 | 334 | // Add the generated stub to the list for future use. 335 | // 336 | stubs.insert( { thunk, stub_rva } ); 337 | 338 | return stub_rva; 339 | } 340 | 341 | // Attempts to convert the provided call to the VMP import stub to a direct import thunk call to the specified remote thunk ea. 342 | // 343 | bool vmpdump::convert_local_call( const import_call& call, remote_ea_t thunk ) 344 | { 345 | uint8_t* local_module_bytes = ( uint8_t* )target_module_view->local_module.data(); 346 | 347 | uint64_t fill_rva = 0; 348 | size_t fill_size = 0; 349 | 350 | // If the import stub call inline adjusts the stack, we must verify that the instruction 351 | // before the stub call is indeed a PUSH. 352 | // 353 | // In VMP3, the stack is only ever adjusted by a single 64-bit PUSH. 354 | // 355 | if ( call.stack_adjustment == 8 ) 356 | { 357 | if ( call.prev_instruction && call.prev_instruction->ins.id == X86_INS_PUSH && call.prev_instruction->operand_type( 0 ) == X86_OP_REG ) 358 | { 359 | // It is indeed a valid VMP-injected push. 360 | // We can NOP it later, and mark it as the starting point for our fill address. 361 | // 362 | fill_rva = call.prev_instruction->ins.address; 363 | fill_size += call.prev_instruction->ins.size; 364 | } 365 | else 366 | { 367 | vtil::logger::log( "!! Stack adjustment failed for call @ RVA 0x%llx for thunk @ 0x%llx\r\n", call.call_rva, thunk ); 368 | return false; 369 | } 370 | } 371 | 372 | uint8_t* call_ea = local_module_bytes + call.call_rva; 373 | 374 | // Disassemble instruction at the call rva. 375 | // 376 | auto instructions = vtil::amd64::disasm( call_ea, call.call_rva ); 377 | 378 | // Ensure disassembly succeeded. 379 | // 380 | if ( instructions.empty() ) 381 | { 382 | vtil::logger::log( "!! Disassembly failed for call @ RVA 0x%llx for thunk @ 0x%llx\r\n", call.call_rva, thunk ); 383 | return false; 384 | } 385 | 386 | // If it's a jump, we can increase fill size by 1, if we haven't already filled using a PUSH. 387 | // This is because thunk jumps must be 5 bytes, so VMP can insert a junk pad byte after its 4 byte stub. 388 | // 389 | if ( fill_size == 0 && call.is_jmp ) 390 | fill_size++; 391 | 392 | // If there's no fill rva selected, set it as the beginning of the disassembled instructions. 393 | // 394 | if ( fill_rva == 0 ) 395 | fill_rva = instructions[ 0 ].address; 396 | 397 | // Account for these instructions for the fill size. 398 | // 399 | for ( auto instruction : instructions ) 400 | fill_size += instruction.bytes.size(); 401 | 402 | // If padded, increase fill size by 1. 403 | // 404 | fill_size += call.padded ? 1 : 0; 405 | 406 | // Now we must inject a call to the newly-fixed thunk. 407 | // 408 | // We assemble this call as if we're in the target process address-space. 409 | // This is because we want to give the assembler the freedom to potentially make a non-relative call if it desires. 410 | // 411 | std::vector converted_call = vtil::amd64::assemble( vtil::format::str( "%s [0x%p]", call.is_jmp ? "jmp" : "call", thunk ), target_module_view->module_base + fill_rva ); 412 | 413 | // Ensure assembly succeeded. 414 | // 415 | if ( converted_call.empty() ) 416 | { 417 | vtil::logger::log( "!! Assembly failed for call @ RVA 0x%llx for thunk @ 0x%llx\r\n", call.call_rva, thunk ); 418 | return false; 419 | } 420 | 421 | // Ensure we have enough bytes to fill. 422 | // 423 | if ( converted_call.size() > fill_size ) 424 | { 425 | // If we don't have enough bytes, we can try to dispatch the call via a stub. 426 | // Try to generate this stub in a codecave. 427 | // 428 | if ( std::optional stub_rva = generate_stub( fill_rva, thunk ) ) 429 | { 430 | // Successful, we found a suitable code-cave and generated a stub. 431 | // Now replace the call with a dispatched call (or jmp) to the stub. 432 | // 433 | converted_call = vtil::amd64::assemble( vtil::format::str( "%s 0x%p", call.is_jmp ? "jmp" : "call", target_module_view->module_base + *stub_rva ), target_module_view->module_base + fill_rva ); 434 | } 435 | } 436 | 437 | // Ensure again we have enough bytes to fill. 438 | // 439 | if ( converted_call.size() > fill_size ) 440 | { 441 | vtil::logger::log( "!! Insufficient bytes [have %d, need %d] for call @ RVA 0x%llx for thunk @ 0x%llx\r\n", fill_size, converted_call.size(), call.call_rva, thunk ); 442 | return false; 443 | } 444 | 445 | // NOP the fill bytes and copy the converted call in. 446 | // 447 | memset( local_module_bytes + fill_rva, 0x90, fill_size ); 448 | memcpy( local_module_bytes + fill_rva, converted_call.data(), converted_call.size() ); 449 | 450 | return true; 451 | } 452 | 453 | // Searches for a module where the provided remote ea is within the module's address space, then returns a module_view of that module. 454 | // 455 | std::optional vmpdump::view_from_base( remote_ea_t base ) const 456 | { 457 | // Find the module by base. 458 | // 459 | auto it = process_modules.find( base ); 460 | 461 | // Return empty {} if not found. 462 | // 463 | if ( it == process_modules.end() ) 464 | return {}; 465 | 466 | // Construct module_view. 467 | // 468 | return { { process_id, it->second.first, base, it->second.second } }; 469 | } 470 | 471 | // Retrieves the module base from the given remote ea. 472 | // 473 | std::optional vmpdump::base_from_ea( remote_ea_t ea ) const 474 | { 475 | // Enumerate process modules. 476 | // 477 | for ( auto& [base, info] : process_modules ) 478 | { 479 | // If within bounds, return module base. 480 | // 481 | if ( ea >= base && ea < base + info.second ) 482 | return base; 483 | } 484 | 485 | // If none found, return empty {}. 486 | // 487 | return {}; 488 | } 489 | 490 | // Creates a vmpdump class from the given process id and target module name. 491 | // If module_name is empty "", the process module is used. 492 | // If the process cannot be opened for some reason or the module cannot be found, returns empty {}. 493 | // 494 | std::unique_ptr vmpdump::from_pid( uint32_t process_id, const std::string& module_name ) 495 | { 496 | std::unique_ptr result = {}; 497 | 498 | HMODULE process_modules[ 1024 ] = {}; 499 | 500 | // TODO: replace PROCESS_ALL_ACCESS with something more specific. 501 | // 502 | HANDLE process_handle = OpenProcess( PROCESS_ALL_ACCESS, FALSE, process_id ); 503 | if ( process_handle == NULL ) 504 | return {}; 505 | 506 | // Retrieves the module base address and size, in that order, in a pair. 507 | // 508 | auto get_module_info = [&]( HMODULE target_module ) -> std::pair 509 | { 510 | MODULEINFO info = {}; 511 | if ( !GetModuleInformation( process_handle, target_module, &info, sizeof( info ) ) ) 512 | return { 0, 0 }; 513 | 514 | return { ( uintptr_t )info.lpBaseOfDll, info.SizeOfImage }; 515 | }; 516 | 517 | // Do ... While( 0 ) "loop" for easy error wrapping. 518 | // 519 | do 520 | { 521 | // Try to get the process image file name. 522 | // 523 | char process_image_path[ MAX_PATH ] = {}; 524 | DWORD process_image_path_size = sizeof( process_image_path ); 525 | if ( !QueryFullProcessImageNameA( process_handle, 0, process_image_path, &process_image_path_size ) ) 526 | break; 527 | 528 | const char* process_image_name = PathFindFileNameA( process_image_path ); 529 | 530 | // Map of process modules, for later class construction. 531 | // 532 | std::map> process_modules_map; 533 | 534 | // Info of the target module. 535 | // 536 | std::string target_module_name; 537 | std::pair target_module_info = {}; 538 | bool target_module_found = false; 539 | 540 | // Enumerate through the process modules list. 541 | // 542 | DWORD process_modules_size; 543 | if ( EnumProcessModules( process_handle, process_modules, sizeof( process_modules ), &process_modules_size ) ) 544 | { 545 | // Loop through each module. 546 | // 547 | for ( int i = 0; i < ( process_modules_size / sizeof( HMODULE ) ); i++ ) 548 | { 549 | HMODULE curr_module = process_modules[ i ]; 550 | 551 | // Get the module base address and size. 552 | // 553 | std::pair curr_module_info = get_module_info( curr_module ); 554 | 555 | // Get the module name. 556 | // 557 | char module_base_name[ 64 ] = {}; 558 | if ( GetModuleBaseNameA( process_handle, curr_module, module_base_name, sizeof( module_base_name ) ) ) 559 | { 560 | // Add the module to the map. 561 | // 562 | process_modules_map.insert( { curr_module_info.first, { module_base_name, curr_module_info.second } } ); 563 | 564 | // If we're looking for the process module, compare module name to image base name. 565 | // Otherwise, compare the module name to the provided target module name in the argument. 566 | // 567 | if ( !target_module_found 568 | && ( module_name.empty() && _stricmp( module_base_name, process_image_name ) == 0 ) 569 | || ( std::string( module_base_name ) == module_name ) ) 570 | { 571 | target_module_info = curr_module_info; 572 | target_module_name = module_base_name; 573 | target_module_found = true; 574 | } 575 | } 576 | } 577 | } 578 | 579 | // Verify that we actually found the module. 580 | // 581 | if ( !target_module_found ) 582 | break; 583 | 584 | // Construct the object. 585 | // 586 | result = std::make_unique( process_id, process_modules_map, std::make_unique( process_id, target_module_name, target_module_info.first, target_module_info.second ), std::string { process_image_path } ); 587 | 588 | } while ( 0 ); 589 | 590 | // Close handle and return the constructed object. 591 | // 592 | CloseHandle( process_handle ); 593 | return result; 594 | } 595 | } 596 | -------------------------------------------------------------------------------- /VMPDump/vmpdump.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "imports.hpp" 10 | #include "module_view.hpp" 11 | 12 | namespace vmpdump 13 | { 14 | // The master class allowing for easy access to all dumper and import reconstruction functionality. 15 | // 16 | class vmpdump 17 | { 18 | public: 19 | // The target process' id. 20 | // 21 | const uint32_t process_id; 22 | 23 | // A map of { module base, { module name, module size> }. 24 | // 25 | const std::map> process_modules; 26 | 27 | // A view to the target module for dumping. 28 | // 29 | std::unique_ptr const target_module_view; 30 | 31 | // The full path to the target module. 32 | // 33 | const std::string module_full_path; 34 | 35 | // Disallow construction + copy. 36 | // 37 | vmpdump() = delete; 38 | vmpdump( const vmpdump& ) = delete; 39 | vmpdump& operator=( const vmpdump& ) = delete; 40 | 41 | // Allow move. 42 | // 43 | vmpdump( vmpdump&& ) = default; 44 | vmpdump& operator=( vmpdump&& ) = default; 45 | 46 | // Scans the specified code range for any import calls and imports. 47 | // resolved_imports is a map of { import thunk rva, import structure }. 48 | // 49 | bool scan_for_imports( uint64_t rva, size_t code_size, std::map& resolved_imports, std::vector& import_calls, uint32_t flags = 0 ); 50 | 51 | // Scans all executable sections of the image for any import calls and imports. 52 | // 53 | bool scan_for_imports( std::map& resolved_imports, std::vector& import_calls, uint32_t flags = 0 ); 54 | 55 | // Attempts to generate a stub in a code cave in the section of the call rva which jmps to the given thunk. 56 | // Returns the stub rva. 57 | // 58 | std::optional generate_stub( uint32_t rva, remote_ea_t thunk ); 59 | 60 | // Attempts to convert the provided call to the VMP import stub to a direct import thunk call to the specified remote thunk ea. 61 | // 62 | bool convert_local_call( const import_call& call, remote_ea_t thunk ); 63 | 64 | // Constructs a module_view from the given remote module base. 65 | // 66 | std::optional view_from_base( remote_ea_t base ) const; 67 | 68 | // Retrieves the module base from the given remote ea. 69 | // 70 | std::optional base_from_ea( remote_ea_t ea ) const; 71 | 72 | // Creates a vmpdump class from the given process id and target module name. 73 | // If module_name is empty "", the process module is used. 74 | // If the process cannot be opened for some reason or the module cannot be found, returns empty {}. 75 | // 76 | static std::unique_ptr from_pid( uint32_t process_id, const std::string& module_name = "" ); 77 | 78 | // Constructor. 79 | // 80 | vmpdump( uint32_t process_id, const std::map>& process_modules, std::unique_ptr target_module_view, const std::string& module_full_path ) 81 | : process_id( process_id ), process_modules( process_modules ), target_module_view( std::move( target_module_view ) ), module_full_path( module_full_path ) 82 | {} 83 | }; 84 | } -------------------------------------------------------------------------------- /VMPDump/winpe/common.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #define WIN_STRUCT_PACKING 4 -------------------------------------------------------------------------------- /VMPDump/winpe/debug.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "nt_headers.hpp" 3 | 4 | namespace win 5 | { 6 | namespace debug 7 | { 8 | // Enum -> String 9 | // 10 | static inline const char* resolve_enum( machine_id id ) 11 | { 12 | switch ( id ) 13 | { 14 | case machine_id::unknown: return "unknown"; 15 | case machine_id::target_host: return "target_host"; 16 | case machine_id::i386: return "i386"; 17 | case machine_id::r3000: return "r3000"; 18 | case machine_id::r4000: return "r4000"; 19 | case machine_id::r10000: return "r10000"; 20 | case machine_id::wcemipsv2: return "wcemipsv2"; 21 | case machine_id::alpha: return "alpha"; 22 | case machine_id::sh3: return "sh3"; 23 | case machine_id::sh3dsp: return "sh3dsp"; 24 | case machine_id::sh3e: return "sh3e"; 25 | case machine_id::sh4: return "sh4"; 26 | case machine_id::sh5: return "sh5"; 27 | case machine_id::arm: return "arm"; 28 | case machine_id::thumb: return "thumb"; 29 | case machine_id::armnt: return "armnt"; 30 | case machine_id::am33: return "am33"; 31 | case machine_id::powerpc: return "powerpc"; 32 | case machine_id::powerpcfp: return "powerpcfp"; 33 | case machine_id::ia64: return "ia64"; 34 | case machine_id::mips16: return "mips16"; 35 | case machine_id::alpha64: return "alpha64"; 36 | case machine_id::mipsfpu: return "mipsfpu"; 37 | case machine_id::mipsfpu16: return "mipsfpu16"; 38 | case machine_id::tricore: return "tricore"; 39 | case machine_id::cef: return "cef"; 40 | case machine_id::ebc: return "ebc"; 41 | case machine_id::amd64: return "amd64"; 42 | case machine_id::m32r: return "m32r"; 43 | case machine_id::arm64: return "arm64"; 44 | case machine_id::cee: return "cee"; 45 | default: return "?"; 46 | } 47 | } 48 | static inline const char* resolve_enum( subsystem_id id ) 49 | { 50 | switch ( id ) 51 | { 52 | case subsystem_id::unknown: return "unknown"; 53 | case subsystem_id::native: return "native"; 54 | case subsystem_id::windows_gui: return "windows_gui"; 55 | case subsystem_id::windows_cui: return "windows_cui"; 56 | case subsystem_id::os2_cui: return "os2_cui"; 57 | case subsystem_id::posix_cui: return "posix_cui"; 58 | case subsystem_id::native_windows: return "native_windows"; 59 | case subsystem_id::windows_ce_gui: return "windows_ce_gui"; 60 | case subsystem_id::efi_application: return "efi_application"; 61 | case subsystem_id::efi_boot_service_driver: return "efi_boot_service_driver"; 62 | case subsystem_id::efi_runtime_driver: return "efi_runtime_driver"; 63 | case subsystem_id::efi_rom: return "efi_rom"; 64 | case subsystem_id::xbox: return "xbox"; 65 | case subsystem_id::windows_boot_application: return "windows_boot_application"; 66 | case subsystem_id::xbox_code_catalog: return "xbox_code_catalog"; 67 | default: return "?"; 68 | } 69 | } 70 | static inline const char* resolve_enum( directory_id id ) 71 | { 72 | switch ( id ) 73 | { 74 | case directory_entry_export: return "export"; 75 | case directory_entry_import: return "import"; 76 | case directory_entry_resource: return "resource"; 77 | case directory_entry_exception: return "exception"; 78 | case directory_entry_security: return "security"; 79 | case directory_entry_basereloc: return "basereloc"; 80 | case directory_entry_debug: return "debug"; 81 | case directory_entry_architecture: return "architecture/copyright"; 82 | case directory_entry_globalptr: return "globalptr"; 83 | case directory_entry_tls: return "tls"; 84 | case directory_entry_load_config: return "load_config"; 85 | case directory_entry_bound_import: return "bound_import"; 86 | case directory_entry_iat: return "iat"; 87 | case directory_entry_delay_import: return "delay_import"; 88 | case directory_entry_com_descriptor: return "com_descriptor"; 89 | default: return "reserved"; 90 | } 91 | } 92 | }; 93 | }; -------------------------------------------------------------------------------- /VMPDump/winpe/dir_debug.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "nt_headers.hpp" 3 | 4 | #pragma pack(push, WIN_STRUCT_PACKING) 5 | namespace win 6 | { 7 | enum class debug_directory_type_id : uint32_t 8 | { 9 | unknown = 0x00000000, 10 | coff = 0x00000001, 11 | codeview = 0x00000002, 12 | fpo = 0x00000003, 13 | misc = 0x00000004, 14 | exception = 0x00000005, 15 | fixup = 0x00000006, 16 | omap_to_src = 0x00000007, 17 | omap_from_src = 0x00000008, 18 | borland = 0x00000009, 19 | reserved10 = 0x0000000A, 20 | clsid = 0x0000000B, 21 | vc_feature = 0x0000000C, 22 | pogo = 0x0000000D, 23 | iltcg = 0x0000000E, 24 | mpx = 0x0000000F, 25 | repro = 0x00000010, 26 | }; 27 | 28 | struct debug_directory_entry_t 29 | { 30 | uint32_t characteristics; 31 | uint32_t timedate_stamp; 32 | ex_version_t version; 33 | debug_directory_type_id type; 34 | uint32_t size_raw_data; 35 | uint32_t rva_raw_data; 36 | uint32_t ptr_raw_data; 37 | }; 38 | 39 | struct debug_directory_t 40 | { 41 | debug_directory_entry_t entries[ 1 ]; // Variable length array 42 | }; 43 | }; 44 | #pragma pack(pop) -------------------------------------------------------------------------------- /VMPDump/winpe/dir_exceptions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "nt_headers.hpp" 3 | 4 | #pragma pack(push, WIN_STRUCT_PACKING) 5 | namespace win 6 | { 7 | struct runtime_function_t 8 | { 9 | uint32_t rva_begin; 10 | uint32_t rva_end; 11 | uint32_t rva_unwind_data; 12 | }; 13 | 14 | struct exception_directory_t 15 | { 16 | // Length of this array is determined by the size of the directory 17 | // 18 | runtime_function_t functions[ 1 ]; 19 | }; 20 | }; 21 | #pragma pack(pop) -------------------------------------------------------------------------------- /VMPDump/winpe/dir_export.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "nt_headers.hpp" 3 | 4 | #pragma pack(push, WIN_STRUCT_PACKING) 5 | namespace win 6 | { 7 | struct export_directory_t 8 | { 9 | uint32_t characteristics; 10 | uint32_t timedate_stamp; 11 | ex_version_t version; 12 | uint32_t name; 13 | uint32_t base; 14 | uint32_t num_functions; 15 | uint32_t num_names; 16 | uint32_t rva_functions; 17 | uint32_t rva_names; 18 | uint32_t rva_name_ordinals; 19 | }; 20 | }; 21 | #pragma pack(pop) -------------------------------------------------------------------------------- /VMPDump/winpe/dir_iat.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "nt_headers.hpp" 3 | 4 | #pragma pack(push, WIN_STRUCT_PACKING) 5 | namespace win 6 | { 7 | struct image_named_import_t 8 | { 9 | uint16_t hint; 10 | char name[ 1 ]; 11 | }; 12 | 13 | #pragma pack(push, 8) 14 | struct image_thunk_data_x64_t 15 | { 16 | union 17 | { 18 | uint64_t forwarder_string; 19 | uint64_t function; 20 | uint64_t address; // -> image_named_import_t 21 | struct 22 | { 23 | uint64_t ordinal : 16; 24 | uint64_t _reserved0 : 47; 25 | uint64_t is_ordinal : 1; 26 | }; 27 | }; 28 | }; 29 | #pragma pack(pop) 30 | 31 | struct image_thunk_data_x86_t 32 | { 33 | union 34 | { 35 | uint32_t forwarder_string; 36 | uint32_t function; 37 | uint32_t address; // -> image_named_import_t 38 | struct 39 | { 40 | uint32_t ordinal : 16; 41 | uint32_t _reserved0 : 15; 42 | uint32_t is_ordinal : 1; 43 | }; 44 | }; 45 | }; 46 | 47 | template::type> 49 | struct image_thunk_data_t : base_type {}; 50 | static_assert( sizeof( image_thunk_data_t ) == sizeof( image_thunk_data_x86_t ) && 51 | sizeof( image_thunk_data_t ) == sizeof( image_thunk_data_x64_t ), 52 | "Empty structure influenced structure size." ); 53 | }; 54 | #pragma pack(pop) -------------------------------------------------------------------------------- /VMPDump/winpe/dir_import.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "nt_headers.hpp" 3 | 4 | #pragma pack(push, WIN_STRUCT_PACKING) 5 | namespace win 6 | { 7 | struct import_directory_t 8 | { 9 | union 10 | { 11 | uint32_t characteristics; // 0 for terminating null import descriptor 12 | uint32_t rva_original_first_thunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) 13 | }; 14 | uint32_t timedate_stamp; // 0 if not bound, 15 | // -1 if bound, and real date\time stamp 16 | // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) 17 | // O.W. date/time stamp of DLL bound to (Old BIND) 18 | 19 | uint32_t forwarder_chain; // -1 if no forwarders 20 | uint32_t rva_name; 21 | uint32_t rva_first_thunk; // RVA to IAT (if bound this IAT has actual addresses) 22 | }; 23 | }; 24 | #pragma pack(pop) -------------------------------------------------------------------------------- /VMPDump/winpe/dir_load_config.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "nt_headers.hpp" 3 | #include "dir_relocs.hpp" 4 | 5 | #pragma pack(push, WIN_STRUCT_PACKING) 6 | namespace win 7 | { 8 | // TODO: 9 | // - Implement enclave configuration 10 | 11 | // Dynamic relocations 12 | // 13 | enum class dynamic_reloc_entry_id 14 | { 15 | guard_rf_prologue = 1, 16 | guard_rf_epilogue = 2, 17 | guard_import_control_transfer = 3, 18 | guard_indir_control_transfer = 4, 19 | guard_switch_table_branch = 5, 20 | }; 21 | 22 | struct dynamic_reloc_guard_rf_prologue_t 23 | { 24 | uint8_t prologue_size; 25 | uint8_t prologue_bytes[ 1 ]; // Variable length array 26 | }; 27 | 28 | struct dynamic_reloc_guard_rf_epilogue_t 29 | { 30 | uint32_t epilogue_count; 31 | uint8_t epilogue_size; 32 | uint8_t branch_descriptor_element_size; 33 | uint16_t branch_descriptor_count; 34 | uint8_t branch_descriptors[ 1 ]; // Variable length array 35 | 36 | inline uint8_t* get_branch_descriptor_bit_map() { return branch_descriptors + branch_descriptor_count * branch_descriptor_element_size; } 37 | }; 38 | 39 | struct dynamic_reloc_import_control_transfer_t 40 | { 41 | uint32_t page_relative_offset : 12; 42 | uint32_t indirect_call : 1; 43 | uint32_t iat_index : 19; 44 | }; 45 | 46 | struct dynamic_reloc_indir_control_transfer_t 47 | { 48 | uint16_t page_relative_offset : 12; 49 | uint16_t indirect_call : 1; 50 | uint16_t rex_w_prefix : 1; 51 | uint16_t cfg_check : 1; 52 | uint16_t _pad0 : 1; 53 | }; 54 | 55 | struct dynamic_reloc_guard_switch_table_branch_t 56 | { 57 | uint16_t page_relative_offset : 12; 58 | uint16_t register_number : 4; 59 | }; 60 | 61 | struct dynamic_reloc_x86_t 62 | { 63 | uint32_t symbol; 64 | uint32_t size; 65 | reloc_block_t blocks[ 1 ]; // Variable length array 66 | }; 67 | 68 | struct dynamic_reloc_x64_t 69 | { 70 | uint64_t symbol; 71 | uint32_t size; 72 | reloc_block_t blocks[ 1 ]; // Variable length array 73 | }; 74 | 75 | struct dynamic_reloc_v2_x86_t 76 | { 77 | uint32_t header_size; 78 | uint32_t fixup_info_size; 79 | uint32_t symbol; 80 | uint32_t symbol_group; 81 | uint32_t flags; 82 | uint8_t fixup_info[ 1 ]; // Variable length array 83 | }; 84 | 85 | struct dynamic_reloc_v2_x64_t 86 | { 87 | uint32_t header_size; 88 | uint32_t fixup_info_size; 89 | uint64_t symbol; 90 | uint32_t symbol_group; 91 | uint32_t flags; 92 | uint8_t fixup_info[ 1 ]; // Variable length array 93 | }; 94 | 95 | struct dynamic_reloc_table_t 96 | { 97 | uint32_t version; 98 | uint32_t size; 99 | template inline T* get_relocs() { return ( T* ) ( this + 1 ); } 100 | }; 101 | 102 | // Hot patch information 103 | // 104 | struct hotpatch_base_t 105 | { 106 | uint32_t sequence_number; 107 | uint32_t flags; 108 | uint32_t orginal_timedate_stamp; 109 | uint32_t orginal_checksum; 110 | uint32_t code_integrity_info; 111 | uint32_t code_integrity_size; 112 | uint32_t path_table; 113 | uint32_t buffer_offset; 114 | }; 115 | 116 | struct hotpatch_info_t 117 | { 118 | uint32_t version; 119 | uint32_t size; 120 | uint32_t sequence_number; 121 | uint32_t base_image_list; 122 | uint32_t base_image_count; 123 | uint32_t buffer_offset; 124 | uint32_t extra_patch_size; 125 | }; 126 | 127 | struct hotpatch_hashes_t 128 | { 129 | uint8_t sha256[ 32 ]; 130 | uint8_t sha1[ 20 ]; 131 | }; 132 | 133 | // Code integrity information 134 | // 135 | struct load_config_ci_t 136 | { 137 | uint16_t flags; // Flags to indicate if CI information is available, etc. 138 | uint16_t catalog; // 0xFFFF means not available 139 | uint32_t rva_catalog; 140 | uint32_t _pad0; // Additional bitmask to be defined later 141 | }; 142 | 143 | struct load_config_directory_x64_t 144 | { 145 | uint32_t size; 146 | uint32_t timedate_stamp; 147 | ex_version_t version; 148 | uint32_t global_flags_clear; 149 | uint32_t global_flags_set; 150 | uint32_t critical_section_default_timeout; 151 | uint64_t decommit_free_block_threshold; 152 | uint64_t decommit_total_free_threshold; 153 | uint64_t lock_prefix_table; 154 | uint64_t maximum_allocation_size; 155 | uint64_t virtual_memory_threshold; 156 | uint64_t process_affinity_mask; 157 | uint32_t process_heap_flags; 158 | uint16_t csd_version; 159 | uint16_t dependent_load_flags; 160 | uint64_t edit_list; 161 | uint64_t security_cookie; 162 | uint64_t se_handler_table; 163 | uint64_t se_handler_count; 164 | uint64_t guard_cf_check_function_ptr; 165 | uint64_t guard_cf_dispatch_function_ptr; 166 | uint64_t guard_cf_function_table; 167 | uint64_t guard_cf_function_count; 168 | uint32_t guard_flags; 169 | load_config_ci_t code_integrity; 170 | uint64_t guard_address_taken_iat_entry_table; 171 | uint64_t guard_address_taken_iat_entry_count; 172 | uint64_t guard_long_jump_target_table; 173 | uint64_t guard_long_jump_target_count; 174 | uint64_t dynamic_value_reloc_table; 175 | uint64_t chpe_metadata_ptr; 176 | uint64_t guard_rf_failure_routine; 177 | uint64_t guard_rf_failure_routine_function_ptr; 178 | uint32_t dynamic_value_reloc_table_offset; 179 | uint16_t dynamic_value_reloc_table_section; 180 | uint16_t _pad0; 181 | uint64_t guard_rf_verify_stack_ptr_function_ptr; 182 | uint32_t hotpatch_table_offset; 183 | uint32_t _pad1; 184 | uint64_t enclave_configuration_ptr; 185 | uint64_t volatile_metadata_ptr; 186 | }; 187 | 188 | struct load_config_directory_x86_t 189 | { 190 | uint32_t size; 191 | uint32_t timedate_stamp; 192 | ex_version_t version; 193 | uint32_t global_flags_clear; 194 | uint32_t global_flags_set; 195 | uint32_t critical_section_default_timeout; 196 | uint32_t decommit_free_block_threshold; 197 | uint32_t decommit_total_free_threshold; 198 | uint32_t lock_prefix_table; 199 | uint32_t maximum_allocation_size; 200 | uint32_t virtual_memory_threshold; 201 | uint32_t process_heap_flags; 202 | uint32_t process_affinity_mask; 203 | uint16_t csd_version; 204 | uint16_t _pad0; 205 | uint32_t edit_list; 206 | uint32_t security_cookie; 207 | uint32_t se_handler_table; 208 | uint32_t se_handler_count; 209 | }; 210 | 211 | template::type> 213 | struct load_config_directory_t : base_type {}; 214 | static_assert( sizeof( load_config_directory_t ) == sizeof( load_config_directory_x86_t ) && 215 | sizeof( load_config_directory_t ) == sizeof( load_config_directory_x64_t ), 216 | "Empty structure influenced structure size." ); 217 | }; 218 | #pragma pack(pop) -------------------------------------------------------------------------------- /VMPDump/winpe/dir_relocs.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "nt_headers.hpp" 3 | 4 | #pragma pack(push, WIN_STRUCT_PACKING) 5 | namespace win 6 | { 7 | // Not "enum class" to ease the casting to uint16_t:4 8 | enum reloc_type_id 9 | { 10 | rel_based_absolute = 0, 11 | rel_based_high = 1, 12 | rel_based_low = 2, 13 | rel_based_high_low = 3, 14 | rel_based_high_adj = 4, 15 | rel_based_ia64_imm64 = 9, 16 | rel_based_dir64 = 10, 17 | }; 18 | 19 | struct reloc_entry_t 20 | { 21 | uint16_t offset : 12; 22 | uint16_t type : 4; 23 | }; 24 | 25 | struct reloc_block_t 26 | { 27 | uint32_t base_rva; 28 | uint32_t size_block; 29 | reloc_entry_t entries[ 1 ]; // Variable length array 30 | 31 | 32 | inline reloc_block_t* get_next() { return ( reloc_block_t* ) ( ( char* ) this + this->size_block ); } 33 | inline uint32_t num_entries() { return ( reloc_entry_t* ) get_next() - &entries[ 0 ]; } 34 | }; 35 | 36 | struct reloc_directory_t 37 | { 38 | reloc_block_t first_block; 39 | }; 40 | }; 41 | #pragma pack(pop) -------------------------------------------------------------------------------- /VMPDump/winpe/dir_resource.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "nt_headers.hpp" 3 | 4 | #pragma pack(push, WIN_STRUCT_PACKING) 5 | namespace win 6 | { 7 | enum resource_id : uint16_t 8 | { 9 | cursor = 1, 10 | bitmap = 2, 11 | icon = 3, 12 | menu = 4, 13 | dialog = 5, 14 | string = 6, 15 | font_dir = 7, 16 | font = 8, 17 | accelerator = 9, 18 | rcdata = 10, 19 | message_table = 11, 20 | group_cursor = 12, 21 | group_icon = 14, 22 | version = 16, 23 | dlg_include = 17, 24 | plug_play = 19, 25 | vxd = 20, 26 | ani_cursor = 21, 27 | ani_icon = 22, 28 | html = 23, 29 | manifest = 24, 30 | }; 31 | 32 | template 33 | struct resource_directory_string_t 34 | { 35 | using char_t = typename std::conditional::type; 36 | 37 | uint16_t length; 38 | char_t name[ 1 ]; // Variable length array 39 | }; 40 | 41 | struct resource_directory_data_t 42 | { 43 | uint32_t offset_to_data; 44 | uint32_t size; 45 | uint32_t code_page; 46 | uint32_t _pad0; 47 | }; 48 | 49 | struct resource_directory_entry_t 50 | { 51 | union 52 | { 53 | struct 54 | { 55 | uint32_t offset_name : 31; 56 | uint32_t is_named : 1; 57 | }; 58 | uint16_t identifier; 59 | }; 60 | uint32_t offset : 31; 61 | uint32_t is_directory : 1; 62 | }; 63 | 64 | struct resource_directory_desc_t 65 | { 66 | uint32_t characteristics; 67 | uint32_t timedate_stamp; 68 | ex_version_t version; 69 | uint16_t num_named_entries; 70 | uint16_t num_id_entries; 71 | resource_directory_entry_t entries[ 1 ]; // Variable length array 72 | 73 | inline uint32_t num_entries() { return num_named_entries + num_id_entries; } 74 | }; 75 | 76 | // Contains { Type -> Name -> Lang } directory, nested 77 | struct resource_directory_t 78 | { 79 | resource_directory_desc_t type_directory; 80 | 81 | template inline T* resolve_offset( uint32_t offset ) { return ( T* ) ( ( char* ) this + offset ); } 82 | inline resource_directory_desc_t* resolve_directory( const resource_directory_entry_t& entry ) { return entry.is_directory ? resolve_offset( entry.offset ) : nullptr; } 83 | inline resource_directory_data_t* resolve_data( const resource_directory_entry_t& entry ) { return !entry.is_directory ? resolve_offset( entry.offset ) : nullptr; } 84 | inline resource_directory_string_t* resolve_ustring( const resource_directory_entry_t& entry ) { return entry.is_named ? resolve_offset>( entry.offset_name ) : nullptr; } 85 | inline resource_directory_string_t* resolve_string( const resource_directory_entry_t& entry ) { return entry.is_named ? resolve_offset>( entry.offset_name ) : nullptr; } 86 | }; 87 | }; 88 | #pragma pack(pop) -------------------------------------------------------------------------------- /VMPDump/winpe/dir_tls.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "nt_headers.hpp" 3 | 4 | #pragma pack(push, WIN_STRUCT_PACKING) 5 | namespace win 6 | { 7 | union tls_characteristics_t 8 | { 9 | uint32_t flags; 10 | struct 11 | { 12 | uint32_t _reserved0 : 20; 13 | uint32_t alignment : 4; 14 | uint32_t _reserved1 : 8; 15 | }; 16 | }; 17 | 18 | struct tls_directory_x64_t 19 | { 20 | uint64_t address_raw_data_start; 21 | uint64_t address_raw_data_end; 22 | uint64_t address_index; 23 | uint64_t address_callbacks; 24 | uint32_t size_zero_fill; 25 | tls_characteristics_t characteristics; 26 | }; 27 | 28 | struct tls_directory_x86_t 29 | { 30 | uint32_t address_raw_data_start; 31 | uint32_t address_raw_data_end; 32 | uint32_t address_index; 33 | uint32_t address_callbacks; 34 | uint32_t size_zero_fill; 35 | tls_characteristics_t characteristics; 36 | }; 37 | 38 | template::type> 40 | struct tls_directory_t : base_type {}; 41 | static_assert( sizeof( tls_directory_t ) == sizeof( tls_directory_x86_t ) && 42 | sizeof( tls_directory_t ) == sizeof( tls_directory_x64_t ), 43 | "Empty structure influenced structure size." ); 44 | }; 45 | #pragma pack(pop) -------------------------------------------------------------------------------- /VMPDump/winpe/image.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "nt_headers.hpp" 3 | 4 | #include "dir_debug.hpp" 5 | #include "dir_exceptions.hpp" 6 | #include "dir_export.hpp" 7 | #include "dir_iat.hpp" 8 | #include "dir_import.hpp" 9 | #include "dir_relocs.hpp" 10 | #include "dir_tls.hpp" 11 | #include "dir_load_config.hpp" 12 | #include "dir_resource.hpp" 13 | 14 | // TODO: 15 | // - Implement security directory 16 | // - Implement parsing helpers 17 | namespace win 18 | { 19 | // Image wrapper 20 | // 21 | template 22 | struct image_t 23 | { 24 | dos_header_t dos_header; 25 | 26 | inline operator dos_header_t&() { return dos_header; } 27 | inline dos_header_t& get_dos_headers() { return dos_header; } 28 | 29 | inline nt_headers_t* get_nt_headers() { return dos_header.get_nt_headers(); } 30 | 31 | inline uint32_t compete_checksum( uint32_t file_len ) 32 | { 33 | // Calculate partial sum 34 | uint32_t psum_tmp = 0; 35 | uint16_t* raw_data = ( uint16_t* ) &dos_header; 36 | for ( uint32_t off = 0; off < ( file_len + 1 ) >> 1; off++ ) 37 | { 38 | // Add uint16_t 39 | psum_tmp += raw_data[ off ]; 40 | // If it overflows, increment by one 41 | psum_tmp = ( psum_tmp >> 16 ) + ( psum_tmp & 0xFFFF ); 42 | } 43 | uint16_t partial_sum = psum_tmp; 44 | 45 | // Adjust for the previous .checkum field (=0) 46 | uint16_t* adjust_sum = ( uint16_t* ) &get_nt_headers()->optional_header.checksum; 47 | for ( int i = 0; i < 2; i++ ) 48 | { 49 | // If it underflows, decrement by one 50 | partial_sum -= partial_sum < adjust_sum[ i ]; 51 | // Substract uint16_t 52 | partial_sum -= adjust_sum[ i ]; 53 | } 54 | 55 | // Return result 56 | return ( uint32_t ) partial_sum + file_len; 57 | } 58 | 59 | inline data_directory_t* get_directory( directory_id id ) 60 | { 61 | auto nt_hdrs = get_nt_headers(); 62 | if ( nt_hdrs->optional_header.num_data_directories <= id ) return nullptr; 63 | data_directory_t* dir = &nt_hdrs->optional_header.data_directories.entries[ id ]; 64 | return dir->present() ? dir : nullptr; 65 | } 66 | 67 | template 68 | inline T* rva_to_ptr( uint32_t rva ) 69 | { 70 | auto nt_hdrs = get_nt_headers(); 71 | if ( !rva || nt_hdrs->optional_header.size_image <= rva ) return nullptr; 72 | 73 | uint8_t* output = rva + ( uint8_t* ) &dos_header; 74 | for ( int i = 0; i < nt_hdrs->file_header.num_sections; i++ ) 75 | { 76 | auto section = nt_hdrs->get_section( i ); 77 | if ( section->virtual_address <= rva && rva < ( section->virtual_address + section->virtual_size ) ) 78 | { 79 | output = output - section->virtual_address + section->ptr_raw_data; 80 | break; 81 | } 82 | } 83 | 84 | return ( T* ) output; 85 | } 86 | 87 | inline section_header_t* rva_to_section( uint32_t rva ) 88 | { 89 | auto nt_hdrs = get_nt_headers(); 90 | if ( !rva || nt_hdrs->optional_header.size_image <= rva ) return nullptr; 91 | 92 | uint8_t* output = rva + ( uint8_t* ) &dos_header; 93 | for ( int i = 0; i < nt_hdrs->file_header.num_sections; i++ ) 94 | { 95 | auto section = nt_hdrs->get_section( i ); 96 | if ( section->virtual_address <= rva && rva < ( section->virtual_address + section->virtual_size ) ) 97 | { 98 | return section; 99 | } 100 | } 101 | 102 | return nullptr; 103 | } 104 | }; 105 | using image_x64_t = image_t; 106 | using image_x86_t = image_t; 107 | }; -------------------------------------------------------------------------------- /VMPDump/winpe/nt_headers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.hpp" 3 | 4 | #pragma pack(push, WIN_STRUCT_PACKING) 5 | namespace win 6 | { 7 | // Magic constants 8 | // 9 | static constexpr uint16_t DOS_HDR_MAGIC = 0x5A4D; // "MZ" 10 | static constexpr uint32_t NT_HDR_MAGIC = 0x00004550; // "PE\x0\x0" 11 | static constexpr uint16_t OPT_HDR32_MAGIC = 0x010B; 12 | static constexpr uint16_t OPT_HDR64_MAGIC = 0x020B; 13 | static constexpr bool IS_DEF_AMD64 = sizeof( void* ) == 8; 14 | 15 | static constexpr uint32_t NUM_DATA_DIRECTORIES = 16; 16 | static constexpr uint32_t LEN_SECTION_NAME = 8; 17 | 18 | // File target machine 19 | // 20 | enum class machine_id : uint16_t 21 | { 22 | unknown = 0x0000, 23 | target_host = 0x0001, // Useful for indicating we want to interact with the host and not a WoW guest. 24 | i386 = 0x014C, // Intel 386. 25 | r3000 = 0x0162, // MIPS little-endian, 0x160 big-endian 26 | r4000 = 0x0166, // MIPS little-endian 27 | r10000 = 0x0168, // MIPS little-endian 28 | wcemipsv2 = 0x0169, // MIPS little-endian WCE v2 29 | alpha = 0x0184, // Alpha_AXP 30 | sh3 = 0x01A2, // SH3 little-endian 31 | sh3dsp = 0x01A3, 32 | sh3e = 0x01A4, // SH3E little-endian 33 | sh4 = 0x01A6, // SH4 little-endian 34 | sh5 = 0x01A8, // SH5 35 | arm = 0x01C0, // ARM Little-Endian 36 | thumb = 0x01C2, // ARM Thumb/Thumb-2 Little-Endian 37 | armnt = 0x01C4, // ARM Thumb-2 Little-Endian 38 | am33 = 0x01D3, 39 | powerpc = 0x01F0, // IBM PowerPC Little-Endian 40 | powerpcfp = 0x01F1, 41 | ia64 = 0x0200, // Intel 64 42 | mips16 = 0x0266, // MIPS 43 | alpha64 = 0x0284, // ALPHA64 44 | mipsfpu = 0x0366, // MIPS 45 | mipsfpu16 = 0x0466, // MIPS 46 | axp64 = 0x0284, 47 | tricore = 0x0520, // Infineon 48 | cef = 0x0CEF, 49 | ebc = 0x0EBC, // EFI Byte Code 50 | amd64 = 0x8664, // AMD64 (K8) 51 | m32r = 0x9041, // M32R little-endian 52 | arm64 = 0xAA64, // ARM64 Little-Endian 53 | cee = 0xC0EE, 54 | }; 55 | 56 | // Subsystems 57 | // 58 | enum class subsystem_id : uint16_t 59 | { 60 | unknown = 0x0000, // Unknown subsystem. 61 | native = 0x0001, // Image doesn't require a subsystem. 62 | windows_gui = 0x0002, // Image runs in the Windows GUI subsystem. 63 | windows_cui = 0x0003, // Image runs in the Windows character subsystem 64 | os2_cui = 0x0005, // image runs in the OS/2 character subsystem. 65 | posix_cui = 0x0007, // image runs in the Posix character subsystem. 66 | native_windows = 0x0008, // image is a native Win9x driver. 67 | windows_ce_gui = 0x0009, // Image runs in the Windows CE subsystem. 68 | efi_application = 0x000A, // 69 | efi_boot_service_driver = 0x000B, // 70 | efi_runtime_driver = 0x000C, // 71 | efi_rom = 0x000D, 72 | xbox = 0x000E, 73 | windows_boot_application = 0x0010, 74 | xbox_code_catalog = 0x0011, 75 | }; 76 | 77 | // Directory indices 78 | // 79 | enum directory_id 80 | { 81 | directory_entry_export = 0, // Export Directory 82 | directory_entry_import = 1, // Import Directory 83 | directory_entry_resource = 2, // Resource Directory 84 | directory_entry_exception = 3, // Exception Directory 85 | directory_entry_security = 4, // Security Directory 86 | directory_entry_basereloc = 5, // Base Relocation Table 87 | directory_entry_debug = 6, // Debug Directory 88 | directory_entry_copyright = 7, // (X86 usage) 89 | directory_entry_architecture = 7, // Architecture Specific Data 90 | directory_entry_globalptr = 8, // RVA of GP 91 | directory_entry_tls = 9, // TLS Directory 92 | directory_entry_load_config = 10, // Load Configuration Directory 93 | directory_entry_bound_import = 11, // Bound Import Directory in headers 94 | directory_entry_iat = 12, // Import Address Table 95 | directory_entry_delay_import = 13, // Delay Load Import Descriptors 96 | directory_entry_com_descriptor = 14, // COM Runtime descriptor 97 | directory_reserved0 = 15, // - 98 | }; 99 | 100 | // File characteristics 101 | // 102 | union file_characteristics_t 103 | { 104 | uint16_t flags; 105 | struct 106 | { 107 | uint16_t relocs_stripped : 1; // Relocation info stripped from file. 108 | uint16_t executable : 1; // File is executable (i.e. no unresolved external references). 109 | uint16_t lines_stripped : 1; // Line nunbers stripped from file. 110 | uint16_t local_symbols_stripped : 1; // Local symbols stripped from file. 111 | uint16_t aggressive_ws_trim : 1; // Aggressively trim working set 112 | uint16_t large_address_aware : 1; // App can handle >2gb addresses 113 | uint16_t _pad0 : 1; 114 | uint16_t bytes_reversed_lo : 1; // Bytes of machine word are reversed. 115 | uint16_t machine_32 : 1; // 32 bit word machine. 116 | uint16_t debug_stripped : 1; // Debugging info stripped from file in .DBG file 117 | uint16_t runnable_from_swap : 1; // If Image is on removable media, copy and run from the swap file. 118 | uint16_t net_run_from_swap : 1; // If Image is on Net, copy and run from the swap file. 119 | uint16_t system_file : 1; // System File. 120 | uint16_t dll_file : 1; // File is a DLL. 121 | uint16_t up_system_only : 1; // File should only be run on a UP machine 122 | uint16_t bytes_reversed_hi : 1; // Bytes of machine word are reversed. 123 | }; 124 | }; 125 | 126 | // DLL characteristics 127 | // 128 | union dll_characteristics_t 129 | { 130 | uint16_t flags; 131 | struct 132 | { 133 | uint16_t _pad0 : 5; 134 | uint16_t high_entropy_va : 1; // Image can handle a high entropy 64-bit virtual address space. 135 | uint16_t dynamic_base : 1; // DLL can move. 136 | uint16_t force_integrity : 1; // Code Integrity Image 137 | uint16_t nx_compat : 1; // Image is NX compatible 138 | uint16_t no_isolation : 1; // Image understands isolation and doesn't want it 139 | uint16_t no_seh : 1; // Image does not use SEH. No SE handler may reside in this image 140 | uint16_t no_bind : 1; // Do not bind this image. 141 | uint16_t appcontainer : 1; // Image should execute in an AppContainer 142 | uint16_t wdm_driver : 1; // Driver uses WDM model 143 | uint16_t guard_cf : 1; // Image supports Control Flow Guard. 144 | uint16_t terminal_server_aware : 1; 145 | }; 146 | }; 147 | 148 | // Section characteristics 149 | // 150 | union section_characteristics_t 151 | { 152 | uint32_t flags; 153 | struct 154 | { 155 | uint32_t _pad0 : 5; 156 | uint32_t cnt_code : 1; // Section contains code. 157 | uint32_t cnt_init_data : 1; // Section contains initialized data. 158 | uint32_t cnt_uninit_data : 1; // Section contains uninitialized data. 159 | uint32_t _pad1 : 1; 160 | uint32_t lnk_info : 1; // Section contains comments or some other type of information. 161 | uint32_t _pad2 : 1; 162 | uint32_t lnk_remove : 1; // Section contents will not become part of image. 163 | uint32_t lnk_comdat : 1; // Section contents comdat. 164 | uint32_t _pad3 : 1; 165 | uint32_t no_defer_spec_exc : 1; // Reset speculative exceptions handling bits in the TLB entries for this section. 166 | uint32_t mem_far : 1; 167 | uint32_t _pad4 : 1; 168 | uint32_t mem_purgeable : 1; 169 | uint32_t mem_locked : 1; 170 | uint32_t mem_preload : 1; 171 | uint32_t alignment : 4; // Alignment calculated as: n ? 1 << ( n - 1 ) : 16 172 | uint32_t lnk_nreloc_ovfl : 1; // Section contains extended relocations. 173 | uint32_t mem_discardable : 1; // Section can be discarded. 174 | uint32_t mem_not_cached : 1; // Section is not cachable. 175 | uint32_t mem_not_paged : 1; // Section is not pageable. 176 | uint32_t mem_shared : 1; // Section is shareable. 177 | uint32_t mem_execute : 1; // Section is executable. 178 | uint32_t mem_read : 1; // Section is readable. 179 | uint32_t mem_write : 1; // Section is writeable. 180 | }; 181 | 182 | inline uint32_t get_alignment() { return alignment ? 1 << ( alignment - 1 ) : 0x10; } 183 | inline void set_alignment( uint32_t a ) { alignment = a == 0x10 ? 0x0 : __builtin_ctz( a ) + 1; } 184 | }; 185 | 186 | // NT versioning 187 | // 188 | union version_t 189 | { 190 | uint16_t identifier; 191 | struct 192 | { 193 | uint8_t major; 194 | uint8_t minor; 195 | }; 196 | }; 197 | 198 | union ex_version_t 199 | { 200 | uint32_t identifier; 201 | struct 202 | { 203 | uint16_t major; 204 | uint16_t minor; 205 | }; 206 | }; 207 | 208 | // File header 209 | // 210 | struct file_header_t 211 | { 212 | machine_id machine; 213 | uint16_t num_sections; 214 | uint32_t timedate_stamp; 215 | uint32_t ptr_symbols; 216 | uint32_t num_symbols; 217 | uint16_t size_optional_header; 218 | file_characteristics_t characteristics; 219 | }; 220 | 221 | // Data directories 222 | // 223 | struct data_directory_t 224 | { 225 | uint32_t rva; 226 | uint32_t size; 227 | 228 | inline bool present() { return size; } 229 | }; 230 | 231 | struct data_directories_x86_t 232 | { 233 | union 234 | { 235 | struct 236 | { 237 | data_directory_t export_directory; 238 | data_directory_t import_directory; 239 | data_directory_t resource_directory; 240 | data_directory_t exception_directory; 241 | data_directory_t security_directory; 242 | data_directory_t basereloc_directory; 243 | data_directory_t debug_directory; 244 | data_directory_t copyright_directory; 245 | data_directory_t globalptr_directory; 246 | data_directory_t tls_directory; 247 | data_directory_t load_config_directory; 248 | data_directory_t bound_import_directory; 249 | data_directory_t iat_directory; 250 | data_directory_t delay_import_directory; 251 | data_directory_t com_descriptor_directory; 252 | data_directory_t _reserved0; 253 | }; 254 | data_directory_t entries[ NUM_DATA_DIRECTORIES ]; 255 | }; 256 | }; 257 | 258 | struct data_directories_x64_t 259 | { 260 | union 261 | { 262 | struct 263 | { 264 | data_directory_t export_directory; 265 | data_directory_t import_directory; 266 | data_directory_t resource_directory; 267 | data_directory_t exception_directory; 268 | data_directory_t security_directory; 269 | data_directory_t basereloc_directory; 270 | data_directory_t debug_directory; 271 | data_directory_t architecture_directory; 272 | data_directory_t globalptr_directory; 273 | data_directory_t tls_directory; 274 | data_directory_t load_config_directory; 275 | data_directory_t bound_import_directory; 276 | data_directory_t iat_directory; 277 | data_directory_t delay_import_directory; 278 | data_directory_t com_descriptor_directory; 279 | data_directory_t _reserved0; 280 | }; 281 | data_directory_t entries[ NUM_DATA_DIRECTORIES ]; 282 | }; 283 | }; 284 | 285 | template::type> 287 | struct data_directories_t : base_type {}; 288 | static_assert( sizeof( data_directories_t ) == sizeof( data_directories_x86_t ) && 289 | sizeof( data_directories_t ) == sizeof( data_directories_x64_t ), 290 | "Empty structure influenced structure size." ); 291 | 292 | // Optional header 293 | // 294 | struct optional_header_x64_t 295 | { 296 | // Standard fields. 297 | uint16_t magic; 298 | version_t linker_version; 299 | 300 | uint32_t size_code; 301 | uint32_t size_init_data; 302 | uint32_t size_uninit_data; 303 | 304 | uint32_t entry_point; 305 | uint32_t base_of_code; 306 | 307 | // NT additional fields. 308 | uint64_t image_base; 309 | uint32_t section_alignment; 310 | uint32_t file_alignment; 311 | 312 | ex_version_t os_version; 313 | ex_version_t img_version; 314 | ex_version_t subsystem_version; 315 | uint32_t win32_version_value; 316 | 317 | uint32_t size_image; 318 | uint32_t size_headers; 319 | 320 | uint32_t checksum; 321 | subsystem_id subsystem; 322 | dll_characteristics_t characteristics; 323 | 324 | uint64_t size_stack_reserve; 325 | uint64_t size_stack_commit; 326 | uint64_t size_heap_reserve; 327 | uint64_t size_heap_commit; 328 | 329 | uint32_t ldr_flags; 330 | 331 | uint32_t num_data_directories; 332 | data_directories_x64_t data_directories; 333 | }; 334 | 335 | struct optional_header_x86_t 336 | { 337 | // Standard fields. 338 | uint16_t magic; 339 | version_t linker_version; 340 | 341 | uint32_t size_code; 342 | uint32_t size_init_data; 343 | uint32_t size_uninit_data; 344 | 345 | uint32_t entry_point; 346 | uint32_t base_of_code; 347 | uint32_t base_of_data; 348 | 349 | // NT additional fields. 350 | uint32_t image_base; 351 | uint32_t section_alignment; 352 | uint32_t file_alignment; 353 | 354 | ex_version_t os_version; 355 | ex_version_t img_version; 356 | ex_version_t subsystem_version; 357 | uint32_t win32_version_value; 358 | 359 | uint32_t size_image; 360 | uint32_t size_headers; 361 | 362 | uint32_t checksum; 363 | subsystem_id subsystem; 364 | dll_characteristics_t characteristics; 365 | 366 | uint32_t size_stack_reserve; 367 | uint32_t size_stack_commit; 368 | uint32_t size_heap_reserve; 369 | uint32_t size_heap_commit; 370 | 371 | uint32_t ldr_flags; 372 | 373 | uint32_t num_data_directories; 374 | data_directories_x86_t data_directories; 375 | 376 | inline bool has_directory( data_directory_t* dir ) { return &data_directories.entries[ num_data_directories ] < dir && dir->present(); } 377 | inline bool has_directory( directory_id id ) { return has_directory( &data_directories.entries[ id ] ); } 378 | }; 379 | 380 | template::type> 382 | struct optional_header_t : base_type {}; 383 | static_assert( sizeof( optional_header_t ) == sizeof( optional_header_x86_t ) && 384 | sizeof( optional_header_t ) == sizeof( optional_header_x64_t ), 385 | "Empty structure influenced structure size." ); 386 | 387 | // Section header 388 | // 389 | struct section_header_t 390 | { 391 | char name[ LEN_SECTION_NAME ]; 392 | 393 | union 394 | { 395 | uint32_t physical_address; 396 | uint32_t virtual_size; 397 | }; 398 | uint32_t virtual_address; 399 | 400 | uint32_t size_raw_data; 401 | uint32_t ptr_raw_data; 402 | 403 | uint32_t ptr_relocs; 404 | uint32_t ptr_line_numbers; 405 | uint16_t num_relocs; 406 | uint16_t num_line_numbers; 407 | 408 | section_characteristics_t characteristics; 409 | }; 410 | 411 | // NT headers 412 | // 413 | template 414 | struct nt_headers_t 415 | { 416 | uint32_t signature; 417 | file_header_t file_header; 418 | optional_header_t optional_header; 419 | 420 | inline section_header_t* get_sections() { return ( section_header_t* ) ( ( uint8_t* ) &optional_header + file_header.size_optional_header ); } 421 | inline section_header_t* get_section( int n ) { return get_sections() + n; } 422 | }; 423 | using nt_headers_x64_t = nt_headers_t; 424 | using nt_headers_x86_t = nt_headers_t; 425 | 426 | // DOS header 427 | // 428 | struct dos_header_t 429 | { 430 | uint16_t e_magic; 431 | uint16_t e_cblp; 432 | uint16_t e_cp; 433 | uint16_t e_crlc; 434 | uint16_t e_cparhdr; 435 | uint16_t e_minalloc; 436 | uint16_t e_maxalloc; 437 | uint16_t e_ss; 438 | uint16_t e_sp; 439 | uint16_t e_csum; 440 | uint16_t e_ip; 441 | uint16_t e_cs; 442 | uint16_t e_lfarlc; 443 | uint16_t e_ovno; 444 | uint16_t e_res[ 4 ]; 445 | uint16_t e_oemid; 446 | uint16_t e_oeminfo; 447 | uint16_t e_res2[ 10 ]; 448 | uint32_t e_lfanew; 449 | 450 | template inline auto get_nt_headers() { return ( nt_headers_t* ) ( ( uint8_t* ) this + e_lfanew ); } 451 | }; 452 | }; 453 | #pragma pack(pop) -------------------------------------------------------------------------------- /VMPDump_Tester/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(VMPDump_Tester) 2 | 3 | file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS *.cpp *.hpp) 4 | 5 | add_executable(${PROJECT_NAME} 6 | ${SOURCES} 7 | ) 8 | -------------------------------------------------------------------------------- /VMPDump_Tester/VMPDump_Tester.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {4dd0546b-e387-4304-9088-b5cea383c4d4} 25 | VMPDumpTester 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Console 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | 120 | 121 | Console 122 | true 123 | 124 | 125 | 126 | 127 | Level3 128 | true 129 | true 130 | true 131 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 132 | true 133 | 134 | 135 | Console 136 | true 137 | true 138 | true 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /VMPDump_Tester/VMPDump_Tester.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /VMPDump_Tester/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | int main() 5 | { 6 | HMODULE ntdll = GetModuleHandleA( "ntdll.dll" ); 7 | ntdll = GetModuleHandleA( "ntdll.dll" ); 8 | ntdll = GetModuleHandleA( "ntdll.dll" ); 9 | ntdll = GetModuleHandleA( "ntdll.dll" ); 10 | ntdll = GetModuleHandleA( "ntdll.dll" ); 11 | ntdll = GetModuleHandleA( "ntdll.dll" ); 12 | ntdll = GetModuleHandleA( "ntdll.dll" ); 13 | ntdll = GetModuleHandleA( "ntdll.dll" ); 14 | ntdll = GetModuleHandleA( "ntdll.dll" ); 15 | ntdll = GetModuleHandleA( "ntdll.dll" ); 16 | ntdll = GetModuleHandleA( "ntdll.dll" ); 17 | ntdll = GetModuleHandleA( "ntdll.dll" ); 18 | 19 | return 0; 20 | } -------------------------------------------------------------------------------- /after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xnobody/vmpdump/386801754de5e65a6b31d5a6549b9d8851d7c983/after.png -------------------------------------------------------------------------------- /before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xnobody/vmpdump/386801754de5e65a6b31d5a6549b9d8851d7c983/before.png -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xnobody/vmpdump/386801754de5e65a6b31d5a6549b9d8851d7c983/screenshot.png --------------------------------------------------------------------------------