├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── NoVmp ├── CMakeLists.txt ├── NoVmp.licenseheader ├── NoVmp.rc ├── NoVmp.sln ├── NoVmp.vcxproj ├── NoVmp.vcxproj.filters ├── demo_compiler.hpp ├── emulator │ ├── emulator.cpp │ ├── emulator.hpp │ ├── rwx_allocator.cpp │ └── rwx_allocator.hpp ├── logo.ico ├── main.cpp ├── resource.h └── vmprotect │ ├── architecture.cpp │ ├── architecture.hpp │ ├── deobfuscator.hpp │ ├── il2vtil.cpp │ ├── il2vtil.hpp │ ├── image_desc.hpp │ ├── rkey.hpp │ ├── subroutines.cpp │ ├── subroutines.hpp │ ├── vm_state.hpp │ ├── vtil_lifter.cpp │ └── vtil_lifter.hpp ├── README.md ├── appveyor.yml └── assets ├── logo.png └── vmenter.png /.gitignore: -------------------------------------------------------------------------------- 1 | ## Visual Studio 2 | # User-specific files 3 | *.rsuser 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # User-specific files (MonoDevelop/Xamarin Studio) 10 | *.userprefs 11 | 12 | # Mono auto generated files 13 | mono_crash.* 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | [Ww][Ii][Nn]32/ 23 | [Aa][Rr][Mm]/ 24 | #[Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | [Ll]ogs/ 30 | 31 | # Visual Studio 2015/2017 cache/options directory 32 | .vs/ 33 | # Uncomment if you have tasks that create the project's static files in wwwroot 34 | #wwwroot/ 35 | 36 | # Visual Studio 2017 auto generated files 37 | Generated\ Files/ 38 | 39 | # MSTest test Results 40 | [Tt]est[Rr]esult*/ 41 | [Bb]uild[Ll]og.* 42 | 43 | # NUnit 44 | *.VisualState.xml 45 | TestResult.xml 46 | nunit-*.xml 47 | 48 | # Build Results of an ATL Project 49 | [Dd]ebugPS/ 50 | [Rr]eleasePS/ 51 | dlldata.c 52 | 53 | # Benchmark Results 54 | BenchmarkDotNet.Artifacts/ 55 | 56 | # .NET Core 57 | project.lock.json 58 | project.fragment.lock.json 59 | artifacts/ 60 | 61 | # ASP.NET Scaffolding 62 | ScaffoldingReadMe.txt 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Binaries 68 | *.lib 69 | *.exe 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.ipch 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*[.json, .xml, .info] 147 | 148 | # Visual Studio code coverage results 149 | *.coverage 150 | *.coveragexml 151 | 152 | # NCrunch 153 | _NCrunch_* 154 | .*crunch*.local.xml 155 | nCrunchTemp_* 156 | 157 | # MightyMoose 158 | *.mm.* 159 | AutoTest.Net/ 160 | 161 | # Web workbench (sass) 162 | .sass-cache/ 163 | 164 | # Installshield output folder 165 | [Ee]xpress/ 166 | 167 | # DocProject is a documentation generator add-in 168 | DocProject/buildhelp/ 169 | DocProject/Help/*.HxT 170 | DocProject/Help/*.HxC 171 | DocProject/Help/*.hhc 172 | DocProject/Help/*.hhk 173 | DocProject/Help/*.hhp 174 | DocProject/Help/Html2 175 | DocProject/Help/html 176 | 177 | # Click-Once directory 178 | publish/ 179 | 180 | # Publish Web Output 181 | *.[Pp]ublish.xml 182 | *.azurePubxml 183 | # Note: Comment the next line if you want to checkin your web deploy settings, 184 | # but database connection strings (with potential passwords) will be unencrypted 185 | *.pubxml 186 | *.publishproj 187 | 188 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 189 | # checkin your Azure Web App publish settings, but sensitive information contained 190 | # in these scripts will be unencrypted 191 | PublishScripts/ 192 | 193 | # NuGet Packages 194 | *.nupkg 195 | # NuGet Symbol Packages 196 | *.snupkg 197 | # The packages folder can be ignored because of Package Restore 198 | **/[Pp]ackages/* 199 | # except build/, which is used as an MSBuild target. 200 | !**/[Pp]ackages/build/ 201 | # Uncomment if necessary however generally it will be regenerated when needed 202 | #!**/[Pp]ackages/repositories.config 203 | # NuGet v3's project.json files produces more ignorable files 204 | *.nuget.props 205 | *.nuget.targets 206 | 207 | # Microsoft Azure Build Output 208 | csx/ 209 | *.build.csdef 210 | 211 | # Microsoft Azure Emulator 212 | ecf/ 213 | rcf/ 214 | 215 | # Windows Store app package directories and files 216 | AppPackages/ 217 | BundleArtifacts/ 218 | Package.StoreAssociation.xml 219 | _pkginfo.txt 220 | *.appx 221 | *.appxbundle 222 | *.appxupload 223 | 224 | # Visual Studio cache files 225 | # files ending in .cache can be ignored 226 | *.[Cc]ache 227 | # but keep track of directories ending in .cache 228 | !?*.[Cc]ache/ 229 | 230 | # Others 231 | ClientBin/ 232 | ~$* 233 | *~ 234 | *.dbmdl 235 | *.dbproj.schemaview 236 | *.jfm 237 | *.pfx 238 | *.publishsettings 239 | orleans.codegen.cs 240 | 241 | # Including strong name files can present a security risk 242 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 243 | #*.snk 244 | 245 | # Since there are multiple workflows, uncomment next line to ignore bower_components 246 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 247 | #bower_components/ 248 | 249 | # RIA/Silverlight projects 250 | Generated_Code/ 251 | 252 | # Backup & report files from converting an old project file 253 | # to a newer Visual Studio version. Backup files are not needed, 254 | # because we have git ;-) 255 | _UpgradeReport_Files/ 256 | Backup*/ 257 | UpgradeLog*.XML 258 | UpgradeLog*.htm 259 | ServiceFabricBackup/ 260 | *.rptproj.bak 261 | 262 | # SQL Server files 263 | *.mdf 264 | *.ldf 265 | *.ndf 266 | 267 | # Business Intelligence projects 268 | *.rdl.data 269 | *.bim.layout 270 | *.bim_*.settings 271 | *.rptproj.rsuser 272 | *- [Bb]ackup.rdl 273 | *- [Bb]ackup ([0-9]).rdl 274 | *- [Bb]ackup ([0-9][0-9]).rdl 275 | 276 | # Microsoft Fakes 277 | FakesAssemblies/ 278 | 279 | # GhostDoc plugin setting file 280 | *.GhostDoc.xml 281 | 282 | # Node.js Tools for Visual Studio 283 | .ntvs_analysis.dat 284 | node_modules/ 285 | 286 | # Visual Studio 6 build log 287 | *.plg 288 | 289 | # Visual Studio 6 workspace options file 290 | *.opt 291 | 292 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 293 | *.vbw 294 | 295 | # Visual Studio LightSwitch build output 296 | **/*.HTMLClient/GeneratedArtifacts 297 | **/*.DesktopClient/GeneratedArtifacts 298 | **/*.DesktopClient/ModelManifest.xml 299 | **/*.Server/GeneratedArtifacts 300 | **/*.Server/ModelManifest.xml 301 | _Pvt_Extensions 302 | 303 | # Paket dependency manager 304 | .paket/paket.exe 305 | paket-files/ 306 | 307 | # FAKE - F# Make 308 | .fake/ 309 | 310 | # CodeRush personal settings 311 | .cr/personal 312 | 313 | # Python Tools for Visual Studio (PTVS) 314 | __pycache__/ 315 | *.pyc 316 | 317 | # Cake - Uncomment if you are using it 318 | # tools/** 319 | # !tools/packages.config 320 | 321 | # Tabs Studio 322 | *.tss 323 | 324 | # Telerik's JustMock configuration file 325 | *.jmconfig 326 | 327 | # BizTalk build output 328 | *.btp.cs 329 | *.btm.cs 330 | *.odx.cs 331 | *.xsd.cs 332 | 333 | # OpenCover UI analysis results 334 | OpenCover/ 335 | 336 | # Azure Stream Analytics local run output 337 | ASALocalRun/ 338 | 339 | # MSBuild Binary and Structured Log 340 | *.binlog 341 | 342 | # NVidia Nsight GPU debugger configuration file 343 | *.nvuser 344 | 345 | # MFractors (Xamarin productivity tool) working folder 346 | .mfractor/ 347 | 348 | # Local History for Visual Studio 349 | .localhistory/ 350 | 351 | # BeatPulse healthcheck temp database 352 | healthchecksdb 353 | 354 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 355 | MigrationBackup/ 356 | 357 | # Ionide (cross platform F# VS Code tools) working folder 358 | .ionide/ 359 | 360 | # Fody - auto-generated XML schema 361 | FodyWeavers.xsd 362 | 363 | ## Visual Studio Code 364 | # Workspace 365 | .vscode/* 366 | !.vscode/settings.json 367 | !.vscode/tasks.json 368 | !.vscode/launch.json 369 | !.vscode/extensions.json 370 | *.code-workspace 371 | 372 | # Local History for Visual Studio Code 373 | .history/ 374 | 375 | ## VTIL Project related 376 | build/ 377 | Capstone/ 378 | Keystone/ 379 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "VTIL-Core"] 2 | path = VTIL-Core 3 | url = https://github.com/vtil-project/VTIL-Core 4 | [submodule "linux-pe"] 5 | path = linux-pe 6 | url = https://github.com/can1357/linux-pe 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(NoVmp) 3 | 4 | add_subdirectory(VTIL-Core) 5 | add_subdirectory(NoVmp) -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /NoVmp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS *.cpp *.hpp) 2 | 3 | add_executable(${PROJECT_NAME} ${SOURCES}) 4 | 5 | 6 | set(THREADS_PREFER_PTHREAD_FLAG ON) 7 | find_package(Threads REQUIRED) 8 | 9 | target_include_directories(${PROJECT_NAME} PRIVATE ../linux-pe/includes) 10 | target_link_libraries(${PROJECT_NAME} PRIVATE VTIL Threads::Threads) -------------------------------------------------------------------------------- /NoVmp/NoVmp.licenseheader: -------------------------------------------------------------------------------- 1 | extensions: .hpp .cpp .h .c 2 | // Copyright (C) 2020 Can Boluk 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | // -------------------------------------------------------------------------------- /NoVmp/NoVmp.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United States) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 20 | #pragma code_page(1252) 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | #endif // English (United States) resources 48 | ///////////////////////////////////////////////////////////////////////////// 49 | 50 | 51 | ///////////////////////////////////////////////////////////////////////////// 52 | // English (United Kingdom) resources 53 | 54 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) 55 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK 56 | #pragma code_page(1252) 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Icon 61 | // 62 | 63 | // Icon with lowest ID value placed first to ensure application icon 64 | // remains consistent on all systems. 65 | IDI_ICON1 ICON "logo.ico" 66 | 67 | #endif // English (United Kingdom) resources 68 | ///////////////////////////////////////////////////////////////////////////// 69 | 70 | 71 | 72 | #ifndef APSTUDIO_INVOKED 73 | ///////////////////////////////////////////////////////////////////////////// 74 | // 75 | // Generated from the TEXTINCLUDE 3 resource. 76 | // 77 | 78 | 79 | ///////////////////////////////////////////////////////////////////////////// 80 | #endif // not APSTUDIO_INVOKED 81 | 82 | -------------------------------------------------------------------------------- /NoVmp/NoVmp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29709.97 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NoVmp", "NoVmp.vcxproj", "{C93BF114-7A97-45E1-8DA3-638AFDEDE25A}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VTIL", "..\VTIL-Core\VTIL\VTIL.vcxproj", "{8163E74C-DDE4-4507-BD3D-064CD95FF33B}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VTIL-Architecture", "..\VTIL-Core\VTIL-Architecture\VTIL-Architecture.vcxproj", "{A79E2869-7626-4801-B09D-5C12F5163BA3}" 11 | EndProject 12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VTIL-Common", "..\VTIL-Core\VTIL-Common\VTIL-Common.vcxproj", "{EC6B8F7F-730C-4086-B143-4664CC16DF8F}" 13 | EndProject 14 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VTIL-Compiler", "..\VTIL-Core\VTIL-Compiler\VTIL-Compiler.vcxproj", "{F960486B-2DB4-44AF-91BB-0F19F228ABCF}" 15 | EndProject 16 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VTIL-SymEx", "..\VTIL-Core\VTIL-SymEx\VTIL-SymEx.vcxproj", "{FE3202CE-D05C-4E04-AE9B-D30305D8CE31}" 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|x64 = Debug|x64 21 | Release|x64 = Release|x64 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {C93BF114-7A97-45E1-8DA3-638AFDEDE25A}.Debug|x64.ActiveCfg = Debug|x64 25 | {C93BF114-7A97-45E1-8DA3-638AFDEDE25A}.Debug|x64.Build.0 = Debug|x64 26 | {C93BF114-7A97-45E1-8DA3-638AFDEDE25A}.Release|x64.ActiveCfg = Release|x64 27 | {C93BF114-7A97-45E1-8DA3-638AFDEDE25A}.Release|x64.Build.0 = Release|x64 28 | {8163E74C-DDE4-4507-BD3D-064CD95FF33B}.Debug|x64.ActiveCfg = Debug|x64 29 | {8163E74C-DDE4-4507-BD3D-064CD95FF33B}.Debug|x64.Build.0 = Debug|x64 30 | {8163E74C-DDE4-4507-BD3D-064CD95FF33B}.Release|x64.ActiveCfg = Release|x64 31 | {8163E74C-DDE4-4507-BD3D-064CD95FF33B}.Release|x64.Build.0 = Release|x64 32 | {A79E2869-7626-4801-B09D-5C12F5163BA3}.Debug|x64.ActiveCfg = Debug|x64 33 | {A79E2869-7626-4801-B09D-5C12F5163BA3}.Debug|x64.Build.0 = Debug|x64 34 | {A79E2869-7626-4801-B09D-5C12F5163BA3}.Release|x64.ActiveCfg = Release|x64 35 | {A79E2869-7626-4801-B09D-5C12F5163BA3}.Release|x64.Build.0 = Release|x64 36 | {EC6B8F7F-730C-4086-B143-4664CC16DF8F}.Debug|x64.ActiveCfg = Debug|x64 37 | {EC6B8F7F-730C-4086-B143-4664CC16DF8F}.Debug|x64.Build.0 = Debug|x64 38 | {EC6B8F7F-730C-4086-B143-4664CC16DF8F}.Release|x64.ActiveCfg = Release|x64 39 | {EC6B8F7F-730C-4086-B143-4664CC16DF8F}.Release|x64.Build.0 = Release|x64 40 | {F960486B-2DB4-44AF-91BB-0F19F228ABCF}.Debug|x64.ActiveCfg = Debug|x64 41 | {F960486B-2DB4-44AF-91BB-0F19F228ABCF}.Debug|x64.Build.0 = Debug|x64 42 | {F960486B-2DB4-44AF-91BB-0F19F228ABCF}.Release|x64.ActiveCfg = Release|x64 43 | {F960486B-2DB4-44AF-91BB-0F19F228ABCF}.Release|x64.Build.0 = Release|x64 44 | {FE3202CE-D05C-4E04-AE9B-D30305D8CE31}.Debug|x64.ActiveCfg = Debug|x64 45 | {FE3202CE-D05C-4E04-AE9B-D30305D8CE31}.Debug|x64.Build.0 = Debug|x64 46 | {FE3202CE-D05C-4E04-AE9B-D30305D8CE31}.Release|x64.ActiveCfg = Release|x64 47 | {FE3202CE-D05C-4E04-AE9B-D30305D8CE31}.Release|x64.Build.0 = Release|x64 48 | EndGlobalSection 49 | GlobalSection(SolutionProperties) = preSolution 50 | HideSolutionNode = FALSE 51 | EndGlobalSection 52 | GlobalSection(ExtensibilityGlobals) = postSolution 53 | SolutionGuid = {96E5AB4E-69EC-4A76-A493-3DAB4B906E03} 54 | EndGlobalSection 55 | EndGlobal 56 | -------------------------------------------------------------------------------- /NoVmp/NoVmp.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | 16.0 15 | {C93BF114-7A97-45E1-8DA3-638AFDEDE25A} 16 | Win32Proj 17 | NoVmp 18 | 10.0 19 | 20 | 21 | 22 | Application 23 | true 24 | v142 25 | Unicode 26 | 27 | 28 | Application 29 | false 30 | v142 31 | false 32 | Unicode 33 | true 34 | false 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | true 51 | $(SolutionDir)..\VTIL-Core\VTIL-Compiler\includes;$(SolutionDir)..\VTIL-Core\VTIL-Architecture\includes;$(SolutionDir)..\VTIL-Core\VTIL-SymEx\includes;$(SolutionDir)..\VTIL-Core\VTIL\includes;$(SolutionDir)..\VTIL-Core\VTIL-Common\includes;$(SolutionDir)..\VTIL-Core\Keystone\include;$(SolutionDir)..\VTIL-Core\Capstone\include;$(SolutionDir)..\linux-pe\includes;$(IncludePath) 52 | 53 | 54 | false 55 | $(SolutionDir)..\VTIL-Core\VTIL-Compiler\includes;$(SolutionDir)..\VTIL-Core\VTIL-Architecture\includes;$(SolutionDir)..\VTIL-Core\VTIL-SymEx\includes;$(SolutionDir)..\VTIL-Core\VTIL\includes;$(SolutionDir)..\VTIL-Core\VTIL-Common\includes;$(SolutionDir)..\VTIL-Core\Keystone\include;$(SolutionDir)..\VTIL-Core\Capstone\include;$(SolutionDir)..\linux-pe\includes;$(IncludePath) 56 | 57 | 58 | 59 | 60 | 61 | Level3 62 | true 63 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 64 | true 65 | stdcpplatest 66 | false 67 | true 68 | 69 | 70 | MultiThreadedDebugDLL 71 | /bigobj %(AdditionalOptions) 72 | 73 | 74 | Console 75 | true 76 | %(AdditionalDependencies) 77 | 78 | 79 | 80 | 81 | 82 | 83 | Level1 84 | true 85 | true 86 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 87 | true 88 | stdcpplatest 89 | true 90 | true 91 | MultiFile 92 | false 93 | false 94 | keep 95 | Full 96 | Level2 97 | false 98 | Speed 99 | NotSet 100 | false 101 | true 102 | AnySuitable 103 | true 104 | true 105 | Fast 106 | false 107 | true 108 | /bigobj /d2AssertRecursiveInline %(AdditionalOptions) 109 | 110 | 111 | Console 112 | true 113 | true 114 | DebugFastLink 115 | %(AdditionalDependencies) 116 | /CGTHREADS:8 %(AdditionalOptions) 117 | RequireAdministrator 118 | 119 | 120 | false 121 | 122 | 123 | 124 | 125 | false 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | {8163e74c-dde4-4507-bd3d-064cd95ff33b} 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /NoVmp/NoVmp.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {782c02f3-3325-4fa1-a1e1-0e4eb7a60d0a} 6 | 7 | 8 | {50a33830-2ebf-4ddb-aac5-7458941a307f} 9 | 10 | 11 | 12 | 13 | Emulator 14 | 15 | 16 | Emulator 17 | 18 | 19 | 20 | VMProtect 21 | 22 | 23 | VMProtect 24 | 25 | 26 | VMProtect 27 | 28 | 29 | VMProtect 30 | 31 | 32 | 33 | 34 | Emulator 35 | 36 | 37 | Emulator 38 | 39 | 40 | 41 | VMProtect 42 | 43 | 44 | VMProtect 45 | 46 | 47 | VMProtect 48 | 49 | 50 | VMProtect 51 | 52 | 53 | VMProtect 54 | 55 | 56 | VMProtect 57 | 58 | 59 | VMProtect 60 | 61 | 62 | VMProtect 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /NoVmp/emulator/emulator.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of VTIL Project nor the names of its contributors 13 | // may be used to endorse or promote products derived from this software 14 | // without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #include "emulator.hpp" 29 | #include 30 | #include "rwx_allocator.hpp" 31 | 32 | #ifndef _WIN32 33 | #define __stdcall __attribute__ ( ( ms_abi ) ) 34 | #endif 35 | 36 | /* 37 | mov rax, rsp 38 | mov rsp, rcx 39 | add rsp, 20h 40 | mov [rsp+188h], rax 41 | 42 | lea rax, qword ptr [rsp+178h] 43 | 44 | push qword ptr [rax] 45 | pushfq 46 | pop qword ptr [rax] 47 | popfq 48 | 49 | xchg rax, [rsp+100h] 50 | xchg rbx, [rsp+108h] 51 | xchg rcx, [rsp+110h] 52 | xchg rdx, [rsp+118h] 53 | xchg rsi, [rsp+120h] 54 | xchg rdi, [rsp+128h] 55 | xchg rbp, [rsp+130h] 56 | xchg r8, [rsp+138h] 57 | xchg r9, [rsp+140h] 58 | xchg r10, [rsp+148h] 59 | xchg r11, [rsp+150h] 60 | xchg r12, [rsp+158h] 61 | xchg r13, [rsp+160h] 62 | xchg r14, [rsp+168h] 63 | xchg r15, [rsp+170h] 64 | 65 | call qword ptr [rsp+180h] 66 | 67 | xchg rax, [rsp+100h] 68 | xchg rbx, [rsp+108h] 69 | xchg rcx, [rsp+110h] 70 | xchg rdx, [rsp+118h] 71 | xchg rsi, [rsp+120h] 72 | xchg rdi, [rsp+128h] 73 | xchg rbp, [rsp+130h] 74 | xchg r8, [rsp+138h] 75 | xchg r9, [rsp+140h] 76 | xchg r10, [rsp+148h] 77 | xchg r11, [rsp+150h] 78 | xchg r12, [rsp+158h] 79 | xchg r13, [rsp+160h] 80 | xchg r14, [rsp+168h] 81 | xchg r15, [rsp+170h] 82 | 83 | lea rax, qword ptr [rsp+178h] 84 | 85 | push qword ptr [rax] 86 | pushfq 87 | pop qword ptr [rax] 88 | popfq 89 | 90 | 91 | mov rsp, [rsp+188h] 92 | ret 93 | */ 94 | static const std::vector> emulator_shellcode = { 95 | 0x48, 0x89, 0xE0, 0x48, 0x89, 0xCC, 0x48, 0x83, 0xC4, 0x20, 0x48, 0x89, 0x84, 0x24, 96 | 0x88, 0x01, 0x00, 0x00, 0x48, 0x8D, 0x84, 0x24, 0x78, 0x01, 0x00, 0x00, 0xFF, 0x30, 97 | 0x9C, 0x8F, 0x00, 0x9D, 0x48, 0x87, 0x84, 0x24, 0x00, 0x01, 0x00, 0x00, 0x48, 0x87, 98 | 0x9C, 0x24, 0x08, 0x01, 0x00, 0x00, 0x48, 0x87, 0x8C, 0x24, 0x10, 0x01, 0x00, 0x00, 99 | 0x48, 0x87, 0x94, 0x24, 0x18, 0x01, 0x00, 0x00, 0x48, 0x87, 0xB4, 0x24, 0x20, 0x01, 100 | 0x00, 0x00, 0x48, 0x87, 0xBC, 0x24, 0x28, 0x01, 0x00, 0x00, 0x48, 0x87, 0xAC, 0x24, 101 | 0x30, 0x01, 0x00, 0x00, 0x4C, 0x87, 0x84, 0x24, 0x38, 0x01, 0x00, 0x00, 0x4C, 0x87, 102 | 0x8C, 0x24, 0x40, 0x01, 0x00, 0x00, 0x4C, 0x87, 0x94, 0x24, 0x48, 0x01, 0x00, 0x00, 103 | 0x4C, 0x87, 0x9C, 0x24, 0x50, 0x01, 0x00, 0x00, 0x4C, 0x87, 0xA4, 0x24, 0x58, 0x01, 104 | 0x00, 0x00, 0x4C, 0x87, 0xAC, 0x24, 0x60, 0x01, 0x00, 0x00, 0x4C, 0x87, 0xB4, 0x24, 105 | 0x68, 0x01, 0x00, 0x00, 0x4C, 0x87, 0xBC, 0x24, 0x70, 0x01, 0x00, 0x00, 0xFF, 0x94, 106 | 0x24, 0x80, 0x01, 0x00, 0x00, 0x48, 0x87, 0x84, 0x24, 0x00, 0x01, 0x00, 0x00, 0x48, 107 | 0x87, 0x9C, 0x24, 0x08, 0x01, 0x00, 0x00, 0x48, 0x87, 0x8C, 0x24, 0x10, 0x01, 0x00, 108 | 0x00, 0x48, 0x87, 0x94, 0x24, 0x18, 0x01, 0x00, 0x00, 0x48, 0x87, 0xB4, 0x24, 0x20, 109 | 0x01, 0x00, 0x00, 0x48, 0x87, 0xBC, 0x24, 0x28, 0x01, 0x00, 0x00, 0x48, 0x87, 0xAC, 110 | 0x24, 0x30, 0x01, 0x00, 0x00, 0x4C, 0x87, 0x84, 0x24, 0x38, 0x01, 0x00, 0x00, 0x4C, 111 | 0x87, 0x8C, 0x24, 0x40, 0x01, 0x00, 0x00, 0x4C, 0x87, 0x94, 0x24, 0x48, 0x01, 0x00, 112 | 0x00, 0x4C, 0x87, 0x9C, 0x24, 0x50, 0x01, 0x00, 0x00, 0x4C, 0x87, 0xA4, 0x24, 0x58, 113 | 0x01, 0x00, 0x00, 0x4C, 0x87, 0xAC, 0x24, 0x60, 0x01, 0x00, 0x00, 0x4C, 0x87, 0xB4, 114 | 0x24, 0x68, 0x01, 0x00, 0x00, 0x4C, 0x87, 0xBC, 0x24, 0x70, 0x01, 0x00, 0x00, 0x48, 115 | 0x8D, 0x84, 0x24, 0x78, 0x01, 0x00, 0x00, 0xFF, 0x30, 0x9C, 0x8F, 0x00, 0x9D, 0x48, 116 | 0x8B, 0xA4, 0x24, 0x88, 0x01, 0x00, 0x00, 0xC3 117 | }; 118 | 119 | // Invokes routine at the pointer given with the current context and updates the context. 120 | // - Template argument is a small trick to make it work with ICC, declaring a constexpr within the scope does not work. 121 | // 122 | void emulator::invoke( const void* routine_pointer ) 123 | { 124 | // Set the runtime RIP. 125 | // 126 | __rip = routine_pointer; 127 | 128 | // Invoke shellcode. 129 | // 130 | ( ( void( __stdcall* )( emulator* ) )emulator_shellcode.data() )( this ); 131 | } 132 | 133 | // Resolves the offset<0> where the value is saved at for the given register 134 | // and the number of bytes<1> it takes. 135 | // 136 | std::pair emulator::resolve( x86_reg reg ) const 137 | { 138 | auto [base_reg, offset, size] = vtil::amd64::registers.resolve_mapping( reg ); 139 | 140 | const void* base; 141 | switch ( base_reg ) 142 | { 143 | case X86_REG_RAX: base = &v_rax; break; 144 | case X86_REG_RBP: base = &v_rbp; break; 145 | case X86_REG_RBX: base = &v_rbx; break; 146 | case X86_REG_RCX: base = &v_rcx; break; 147 | case X86_REG_RDI: base = &v_rdi; break; 148 | case X86_REG_RDX: base = &v_rdx; break; 149 | case X86_REG_RSI: base = &v_rsi; break; 150 | case X86_REG_R8: base = &v_r8; break; 151 | case X86_REG_R9: base = &v_r9; break; 152 | case X86_REG_R10: base = &v_r10; break; 153 | case X86_REG_R11: base = &v_r11; break; 154 | case X86_REG_R12: base = &v_r12; break; 155 | case X86_REG_R13: base = &v_r13; break; 156 | case X86_REG_R14: base = &v_r14; break; 157 | case X86_REG_R15: base = &v_r15; break; 158 | default: unreachable(); 159 | } 160 | 161 | return { ( ( uint8_t* ) base - ( uint8_t* ) this ) + offset, size }; 162 | } 163 | 164 | // Sets the value of a register. 165 | // 166 | emulator& emulator::set( x86_reg reg, uint64_t value ) 167 | { 168 | auto [off, sz] = resolve( reg ); 169 | memcpy( ( uint8_t* ) this + off, &value, sz ); 170 | return *this; 171 | } 172 | 173 | // Gets the value of a register. 174 | // 175 | uint64_t emulator::get( x86_reg reg ) const 176 | { 177 | uint64_t value = 0; 178 | auto [off, sz] = resolve( reg ); 179 | memcpy( &value, ( uint8_t* ) this + off, sz ); 180 | return value; 181 | } 182 | -------------------------------------------------------------------------------- /NoVmp/emulator/emulator.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of VTIL Project nor the names of its contributors 13 | // may be used to endorse or promote products derived from this software 14 | // without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | #include 32 | 33 | #pragma pack(push, 1) 34 | struct emulator 35 | { 36 | static constexpr uint64_t default_register_value = 0xCCCCCCCCCCCCCCCC; 37 | static constexpr uint64_t default_rflags_value = 0x202; 38 | static constexpr size_t user_stack_size = 0x100; 39 | static constexpr size_t reserved_stack_size = 0x20; 40 | 41 | // Virtual stack, must not be moved from the beginning of this structure 42 | // since this pointer is used as a stack pointer. 43 | // 44 | uint64_t v_reserved_stack[ reserved_stack_size / 8 ] = { default_register_value }; 45 | uint64_t v_stack[ user_stack_size / 8 ] = { default_register_value }; 46 | 47 | // Each individual register. 48 | // 49 | uint64_t v_rax = default_register_value; 50 | uint64_t v_rbx = default_register_value; 51 | uint64_t v_rcx = default_register_value; 52 | uint64_t v_rdx = default_register_value; 53 | uint64_t v_rsi = default_register_value; 54 | uint64_t v_rdi = default_register_value; 55 | uint64_t v_rbp = default_register_value; 56 | uint64_t v_r8 = default_register_value; 57 | uint64_t v_r9 = default_register_value; 58 | uint64_t v_r10 = default_register_value; 59 | uint64_t v_r11 = default_register_value; 60 | uint64_t v_r12 = default_register_value; 61 | uint64_t v_r13 = default_register_value; 62 | uint64_t v_r14 = default_register_value; 63 | uint64_t v_r15 = default_register_value; 64 | uint64_t v_rflags = default_rflags_value; 65 | 66 | // Internal values that must be stored, used by ::transform(). 67 | // 68 | const void* __rip = nullptr; 69 | const void* __rsp = 0; 70 | 71 | // Invokes routine at the pointer given with the current context and updates the context. 72 | // - Template argument is a small trick to make it work with ICC, declaring a constexpr within the scope does not work. 73 | // 74 | void invoke( const void* routine_pointer ); 75 | 76 | // Resolves the offset<0> where the value is saved at for the given register 77 | // and the number of bytes<1> it takes. 78 | // 79 | std::pair resolve( x86_reg reg ) const; 80 | 81 | // Sets the value of a register. 82 | // 83 | emulator& set( x86_reg reg, uint64_t value ); 84 | 85 | // Gets the value of a register. 86 | // 87 | uint64_t get( x86_reg reg ) const; 88 | }; 89 | #pragma pack(pop) -------------------------------------------------------------------------------- /NoVmp/emulator/rwx_allocator.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of VTIL Project nor the names of its contributors 13 | // may be used to endorse or promote products derived from this software 14 | // without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #include "rwx_allocator.hpp" 29 | #if _WIN64 30 | #define WIN32_LEAN_AND_MEAN 31 | #define NOMINMAX 32 | #include 33 | #else 34 | #include 35 | #endif 36 | #include 37 | 38 | namespace mem 39 | { 40 | // If on Windows platform, create a RWX heap. 41 | // 42 | #if _WIN64 43 | auto rwx_heap = [ ] () 44 | { 45 | static HANDLE h = HeapCreate( HEAP_CREATE_ENABLE_EXECUTE, 0, 0 ); 46 | return h; 47 | }; 48 | #endif 49 | 50 | // A RWX memory descriptor prefixes any allocations made by us, 51 | // most to support freeing without an explicit size argument 52 | // on non-Windows platforms. 53 | // 54 | static constexpr size_t rwx_mem_magic = 0x1337DEAD; 55 | struct rwx_mem_desc 56 | { 57 | size_t magic; 58 | size_t allocation_size; 59 | }; 60 | 61 | // Allocates bytes of read/write/execute memory. 62 | // 63 | void* allocate_rwx( size_t size ) 64 | { 65 | size += sizeof( rwx_mem_desc ); 66 | 67 | #if _WIN64 68 | // Allocate a block of RWX memory from the heap we've created. 69 | // 70 | void* p = HeapAlloc( rwx_heap(), HEAP_ZERO_MEMORY, size ); 71 | #else 72 | // Allocate new RWX page(s). 73 | // 74 | void* p = mmap( 0, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 ); 75 | #endif 76 | // If the API returned NULL, throw exception. 77 | // 78 | if ( !p ) throw std::bad_alloc(); 79 | 80 | // Cast the type to rwx_mem_desc, write the size and magic. 81 | // 82 | rwx_mem_desc* desc = ( rwx_mem_desc* ) p; 83 | desc->allocation_size = size; 84 | desc->magic = rwx_mem_magic; 85 | 86 | // Return the data pointer, which is right after the descriptor. 87 | // 88 | return desc + 1; 89 | } 90 | 91 | // Frees the read/write/execute memory at . 92 | // 93 | void free_rwx( void* pointer ) noexcept 94 | { 95 | // Resolve the descriptor which is right before the data, assert magic is valid. 96 | // 97 | rwx_mem_desc* desc = ( rwx_mem_desc* ) pointer - 1; 98 | fassert( desc->magic == rwx_mem_magic ); 99 | 100 | #if _WIN64 101 | // Free the heap memory we've allocated. 102 | // 103 | HeapFree( rwx_heap(), 0, desc ); 104 | #else 105 | // Free the page(s) we've allocated. 106 | // 107 | munmap( desc, desc->allocation_size ); 108 | #endif 109 | } 110 | }; -------------------------------------------------------------------------------- /NoVmp/emulator/rwx_allocator.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Can Boluk and contributors of the VTIL Project 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // 1. Redistributions of source code must retain the above copyright notice, 8 | // this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 3. Neither the name of VTIL Project nor the names of its contributors 13 | // may be used to endorse or promote products derived from this software 14 | // without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | #pragma once 29 | #include 30 | #include 31 | 32 | namespace mem 33 | { 34 | // Allocates bytes of read/write/execute memory. 35 | // 36 | void* allocate_rwx( size_t size ); 37 | 38 | // Frees byets of read/write/execute memory at . 39 | // 40 | void free_rwx( void* pointer ) noexcept; 41 | 42 | // A standard C++ allocator wrapping our helpers. 43 | // 44 | template 45 | struct rwx_allocator 46 | { 47 | using value_type = T; 48 | 49 | rwx_allocator() = default; 50 | 51 | template 52 | constexpr rwx_allocator( const rwx_allocator& ) noexcept {} 53 | 54 | inline T* allocate( size_t count ) { return ( T* ) allocate_rwx( count * sizeof( T ) ); } 55 | inline void deallocate( T* pointer, size_t ) noexcept { free_rwx( pointer ); } 56 | 57 | template constexpr bool operator==( const rwx_allocator& ) { return true; } 58 | template constexpr bool operator!=( const rwx_allocator& ) { return false; } 59 | }; 60 | }; -------------------------------------------------------------------------------- /NoVmp/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tai7sy/NoVmp/6c23c9a335f70e8d5ed6299668fd802f2314c896/NoVmp/logo.ico -------------------------------------------------------------------------------- /NoVmp/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Can Boluk 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | #define _CRT_SECURE_NO_WARNINGS 17 | #ifdef _WIN32 18 | #include 19 | #else 20 | #include 21 | #endif 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "vmprotect/image_desc.hpp" 31 | #include "vmprotect/vtil_lifter.hpp" 32 | #include "vmprotect/vm_state.hpp" 33 | #include "demo_compiler.hpp" 34 | 35 | #ifdef _MSC_VER 36 | #pragma comment(linker, "/STACK:34359738368") 37 | #endif 38 | 39 | using namespace vtil::logger; 40 | 41 | static std::vector read_raw( const std::string& file_path ) 42 | { 43 | // Try to open file as binary 44 | std::ifstream file( file_path, std::ios::binary ); 45 | if ( !file.good() ) error( "Input file cannot be opened." ); 46 | 47 | // Read the whole file 48 | std::vector bytes = std::vector( std::istreambuf_iterator( file ), {} ); 49 | if ( bytes.size() == 0 ) error( "Input file is empty." ); 50 | return bytes; 51 | } 52 | 53 | static void write_raw( void* data, size_t size, const std::string& file_path ) 54 | { 55 | std::ofstream file( file_path, std::ios::binary ); 56 | if ( !file.good() ) error( "File cannot be opened for write." ); 57 | file.write( ( char* ) data, size ); 58 | } 59 | 60 | static const std::string gpl3_license_header = "" 61 | "##############################################################################\n" 62 | "# NoVmp Copyright (C) 2020 Can Boluk #\n" 63 | "# This program comes with absolutely no warranty, and it is free software. #\n" 64 | "# You are welcome to redistribute it under certain conditions--for which you #\n" 65 | "# can refer to the GNU General Public License v3. #\n" 66 | "##############################################################################\n\n"; 67 | 68 | int main( int argc, const char** argv ) 69 | { 70 | vtil::logger::error_hook = [ ] ( const std::string& message ) 71 | { 72 | log( "[*] Unexpected error: %s\n", message ); 73 | throw std::runtime_error( message ); 74 | }; 75 | 76 | // Feelin' fancy. 77 | // 78 | for ( char c : gpl3_license_header ) 79 | log( c == '#' ? CON_RED : CON_YLW, "%c", c ); 80 | 81 | // Parse command line. 82 | // 83 | if ( argc < 2 ) error( "No input file provided." ); 84 | std::filesystem::path image_path( argv[ 1 ] ); 85 | std::filesystem::path working_directory = vtil::make_copy( 86 | image_path 87 | ).remove_filename() / "vms"; 88 | std::filesystem::create_directory( working_directory ); 89 | 90 | // Create the basic descriptor for the image 91 | // 92 | vmp::image_desc* desc = new vmp::image_desc; 93 | desc->raw = read_raw( image_path.string() ); 94 | desc->override_image_base = 0; 95 | desc->has_relocs = desc->get_nt_headers()->optional_header.data_directories.basereloc_directory.present(); 96 | desc->strip_constant_obfuscation = false; 97 | 98 | // Warn if relocs are stripped. 99 | // 100 | if ( !desc->has_relocs ) 101 | warning( "This image has relocations stripped, NoVmp is not 100%% compatible with this switch yet." ); 102 | 103 | // Parse options: 104 | // 105 | bool compile = false; 106 | bool optimize = true; 107 | std::vector target_vms; 108 | for ( int i = 2; i < argc; ) 109 | { 110 | if ( !strcmp( argv[ i ], "-vms" ) ) 111 | { 112 | while ( ++i < argc && argv[ i ][ 0 ] != '-' ) 113 | target_vms.emplace_back( strtoul( argv[ i ], nullptr, 16 ) ); 114 | } 115 | else if ( !strcmp( argv[ i ], "-sections" ) ) 116 | { 117 | while ( ++i < argc && argv[ i ][ 0 ] != '-' ) 118 | vmp::section_prefixes.emplace_back( argv[ i ] ); 119 | } 120 | else if ( !strcmp( argv[ i ], "-base" ) ) 121 | { 122 | fassert( ++i < argc ); 123 | desc->override_image_base = strtoull( argv[ i ], nullptr, 16 ); 124 | desc->has_relocs = true; 125 | i++; 126 | } 127 | else if ( !strcmp( argv[ i ], "-noopt" ) ) 128 | { 129 | optimize = false; 130 | i++; 131 | } 132 | else if ( !strcmp( argv[ i ], "-opt:constant" ) ) 133 | { 134 | desc->strip_constant_obfuscation = true; 135 | i++; 136 | } 137 | else if ( !strcmp( argv[ i ], "-experimental:recompile" ) ) 138 | { 139 | i++; 140 | compile = true; 141 | } 142 | else 143 | { 144 | error( "Unknown parameter: %s", argv[ i ] ); 145 | } 146 | } 147 | 148 | // Iterate each section: 149 | // 150 | uint32_t rva_high = 0; 151 | uint32_t raw_low = 0; 152 | for ( int i = 0; i < desc->get_nt_headers()->file_header.num_sections; i++ ) 153 | { 154 | // Reference section and re-calculate some stats 155 | // 156 | win::section_header_t* scn = desc->get_nt_headers()->get_section( i ); 157 | rva_high = std::max( scn->virtual_address + std::max( scn->virtual_size, scn->size_raw_data ), rva_high ); 158 | raw_low = std::max( scn->ptr_raw_data, raw_low ); 159 | 160 | // Skip if it cannot be executed 161 | // 162 | if ( !scn->characteristics.mem_execute ) continue; 163 | 164 | // Iterate each byte 165 | // 166 | uint8_t* scn_begin = desc->raw.data() + scn->ptr_raw_data; 167 | uint8_t* scn_end = scn_begin + std::min( scn->size_raw_data, scn->virtual_size ); 168 | 169 | for ( uint8_t* it = scn_begin; it < ( scn_end - 10 ); it++ ) 170 | { 171 | // Skip if not [JMP rel32] OR [CALL rel32 NOP] 172 | // 173 | bool mid_func = false; 174 | if ( it[ 0 ] == 0xE9 ) 175 | mid_func = true; 176 | else if ( it[ 0 ] == 0xE8 ) 177 | mid_func = false; 178 | else 179 | continue; 180 | uint32_t jmp_rva = scn->virtual_address + ( it - scn_begin ) + 5 + *( int32_t* ) &it[ 1 ]; 181 | 182 | // Skip if JMP target is in the same section / in a non-executable section 183 | // 184 | win::section_header_t* scn_jmp = desc->rva_to_section( jmp_rva ); 185 | if ( !scn_jmp || scn_jmp == scn || !scn_jmp->characteristics.mem_execute ) continue; 186 | 187 | // Skip if it's not VMENTER 188 | // 189 | uint8_t* jmp_target_bytes = desc->raw.data() + jmp_rva + scn_jmp->ptr_raw_data - scn_jmp->virtual_address; 190 | if ( jmp_target_bytes > &desc->raw.back() || 191 | jmp_target_bytes[ 0 ] != 0x68 || 192 | jmp_target_bytes[ 5 ] != 0xE8 ) continue; 193 | 194 | // Add to image descriptor 195 | // 196 | uint64_t ptr = ( scn->virtual_address + ( it - scn_begin ) ); 197 | 198 | desc->virt_routines.push_back( vmp::virtual_routine{ 199 | .jmp_rva = jmp_rva, 200 | .mid_routine = mid_func 201 | } ); 202 | 203 | log( "Discovered vmenter at %p\n", desc->get_real_image_base() + ptr ); 204 | } 205 | } 206 | 207 | // If VM list is given, replace discovery. 208 | // 209 | if ( !target_vms.empty() ) desc->virt_routines.clear(); 210 | for ( uint32_t rva : target_vms ) 211 | { 212 | desc->virt_routines.push_back( vmp::virtual_routine{ 213 | .jmp_rva = rva, 214 | } ); 215 | } 216 | 217 | // Declare the worker. 218 | // 219 | const auto vm_lifter = [ & ] ( int vm_index ) -> vtil::routine* 220 | { 221 | // Lift the virtual machine. 222 | // 223 | vmp::virtual_routine* vr = &desc->virt_routines[ vm_index ]; 224 | log( "Lifting virtual-machine at %p...\n", vr->jmp_rva ); 225 | vmp::vm_state state = { desc, vr->jmp_rva }; 226 | vtil::routine* rtn = lift_il( &state ); 227 | if ( !rtn ) return nullptr; 228 | 229 | // Save unoptimized routine. 230 | // 231 | vtil::save_routine( 232 | rtn, 233 | ( working_directory / vtil::format::str( "%p.premature.vtil", vr->jmp_rva ) ).string() 234 | ); 235 | 236 | // If noopt set, return. 237 | // 238 | if ( !optimize ) return rtn; 239 | 240 | // Apply optimizations. 241 | // 242 | int64_t ins = rtn->num_instructions(); 243 | int64_t blks = rtn->num_blocks(); 244 | vtil::optimizer::apply_all_profiled( rtn ); 245 | int64_t oins = rtn->num_instructions(); 246 | int64_t oblks = rtn->num_blocks(); 247 | 248 | // Write routine and optimization information. 249 | // 250 | { 251 | std::lock_guard _g{ logger_state }; 252 | log( "\nLifted & optimized virtual-machine at %p\n", vr->jmp_rva ); 253 | 254 | log( "Optimizer stats:\n" ); 255 | log( " - Block count: %-5d => %-5d (%.2f%%).\n", blks, oblks, 100.0f * float( float( oblks - blks ) / blks ) ); 256 | log( " - Instruction count: %-5d => %-5d (%.2f%%).\n", ins, oins, 100.0f * float( float( oins - ins ) / ins ) ); 257 | 258 | std::vector bytes; 259 | for ( auto& [_, block] : rtn->explored_blocks ) 260 | { 261 | for ( auto& ins : *block ) 262 | { 263 | if ( ins.base->name == "vemit" ) 264 | { 265 | uint8_t* bs = ( uint8_t* ) &ins.operands[ 0 ].imm().u64; 266 | bytes.insert( bytes.end(), bs, bs + ins.operands[ 0 ].size() ); 267 | } 268 | } 269 | } 270 | 271 | if ( bytes.size() ) 272 | { 273 | log( "Special instructions:\n" ); 274 | 275 | size_t n = 0; 276 | auto dasm = vtil::amd64::disasm( bytes.data(), 0, bytes.size() ); 277 | for ( auto& ins : dasm ) 278 | { 279 | n++; 280 | log( " - %s\n", ins ); 281 | if ( n > 10 ) 282 | { 283 | log( " - ...\n" ); 284 | break; 285 | } 286 | } 287 | } 288 | } 289 | 290 | // Save optimized routine. 291 | // 292 | vtil::save_routine( 293 | rtn, 294 | ( working_directory / vtil::format::str( "%p.optimized.vtil", vr->jmp_rva ) ).string() 295 | ); 296 | return rtn; 297 | }; 298 | 299 | // Lift every routine and wait for completion. 300 | // 301 | std::vector>> worker_pool; 302 | for ( int i = 0; i < desc->virt_routines.size(); i++ ) 303 | worker_pool.emplace_back( i, std::async( /*std::launch::async*/ std::launch::deferred, vm_lifter, i ) ); 304 | 305 | for ( auto& [idx, rtn] : worker_pool ) 306 | { 307 | try 308 | { 309 | desc->virt_routines[ idx ].routine = rtn.get(); 310 | } 311 | catch ( const std::exception& ex ) 312 | { 313 | log( "Error: %s\n", ex.what() ); 314 | } 315 | } 316 | 317 | // Return if recompilation is not requested. 318 | // 319 | if ( !compile ) 320 | { 321 | system( "pause" ); 322 | return 0; 323 | } 324 | 325 | uint32_t rva_sec = ( rva_high + 0xFFF ) & ~0xFFF; 326 | std::vector byte_stream; 327 | for ( auto& vr : desc->virt_routines ) 328 | { 329 | // Page align rva high and calculate where we place the next section 330 | // 331 | uint32_t rva_routine = rva_sec + byte_stream.size(); 332 | std::vector substream = vtil::compile( vr.routine, rva_routine ); 333 | 334 | // Write jump to new routine. 335 | // 336 | if ( vr.jmp_rva ) 337 | { 338 | uint8_t* jmp_rel32 = desc->rva_to_ptr( vr.jmp_rva ); 339 | *jmp_rel32 = 0xE9; 340 | *( int32_t* ) ( jmp_rel32 + 1 ) = rva_routine - ( vr.jmp_rva + 5 ); 341 | } 342 | 343 | // Append to stream. 344 | // 345 | byte_stream.insert( byte_stream.end(), substream.begin(), substream.end() ); 346 | } 347 | 348 | // Page align the section 349 | // 350 | size_t scn_original_size = byte_stream.size(); 351 | byte_stream.resize( ( scn_original_size + 0xFFF ) & ~0xFFF ); 352 | memset( byte_stream.data() + scn_original_size, 0xCC, byte_stream.size() - scn_original_size ); 353 | 354 | // Create a new section in the image 355 | // 356 | fassert( raw_low > ( sizeof( win::section_header_t ) + desc->get_nt_headers()->optional_header.size_headers ) ); 357 | size_t img_original_size = desc->raw.size(); 358 | desc->raw.resize( img_original_size + byte_stream.size() ); 359 | win::image_x64_t* img = ( win::image_x64_t* ) desc->raw.data(); 360 | 361 | win::nt_headers_x64_t* nt_hdrs = img->get_nt_headers(); 362 | nt_hdrs->optional_header.size_code += byte_stream.size(); 363 | nt_hdrs->optional_header.size_image += byte_stream.size(); 364 | nt_hdrs->optional_header.size_headers += sizeof( win::section_header_t ); 365 | 366 | win::section_header_t* scn = nt_hdrs->get_section( nt_hdrs->file_header.num_sections++ ); 367 | memset( scn, 0, sizeof( win::section_header_t ) ); 368 | strcpy( &scn->name.short_name[ 0 ], ".novmp" ); 369 | scn->characteristics.cnt_code = 1; 370 | scn->characteristics.mem_execute = 1; 371 | scn->characteristics.mem_read = 1; 372 | scn->virtual_address = rva_sec; 373 | scn->ptr_raw_data = img_original_size; 374 | scn->size_raw_data = byte_stream.size(); 375 | scn->virtual_size = byte_stream.size(); 376 | 377 | memcpy( desc->raw.data() + img_original_size, 378 | byte_stream.data(), 379 | byte_stream.size() ); 380 | 381 | // Write the recompiled image. 382 | // 383 | image_path.replace_extension( "devirt" + image_path.extension().string() ); 384 | write_raw( 385 | desc->raw.data(), 386 | desc->raw.size(), 387 | image_path.string() 388 | ); 389 | system( "pause" ); 390 | return 0; 391 | } 392 | -------------------------------------------------------------------------------- /NoVmp/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by NoVmp.rc 4 | // 5 | #define IDI_ICON1 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /NoVmp/vmprotect/architecture.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Can Boluk 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | #pragma once 17 | #include 18 | #include 19 | #include "vm_state.hpp" 20 | #include "deobfuscator.hpp" 21 | 22 | namespace vmp::arch 23 | { 24 | using namespace vtil::logger; 25 | 26 | using opcode_id = std::string; 27 | static constexpr char PANY = 0xAA; 28 | 29 | // In order of common-ness 30 | static const std::vector possible_variants = { 8, 4, 2, 1 }; 31 | static char abbrv_param_size( char size ) 32 | { 33 | switch ( size ) 34 | { 35 | case 8: return 'Q'; 36 | case 4: return 'D'; 37 | case 2: return 'W'; 38 | case 1: return 'B'; 39 | } 40 | unreachable(); 41 | } 42 | 43 | static char resolve_abbrv_param_size( char abbrv ) 44 | { 45 | switch ( abbrv ) 46 | { 47 | case 'Q': return 8; 48 | case 'D': return 4; 49 | case 'W': return 2; 50 | case 'B': return 1; 51 | } 52 | unreachable(); 53 | } 54 | 55 | // Instruction description 56 | // 57 | constexpr int32_t unknown_delta = 0x10000000; 58 | struct instruction 59 | { 60 | // Describe the operation this instruction does 61 | // 62 | opcode_id op; 63 | instruction_stream stream = {}; 64 | 65 | // Describe its parameters 66 | // 67 | std::vector parameters = {}; 68 | std::vector parameter_sizes = {}; 69 | 70 | // Summarize stack operations 71 | // 72 | int32_t stack_delta = 0; 73 | std::set stack_reads = {}; 74 | std::set stack_writes = {}; 75 | 76 | // Summarize context operations 77 | // 78 | std::set context_reads = {}; 79 | std::set context_writes = {}; 80 | }; 81 | 82 | instruction classify( vm_state* vstate, const instruction_stream& is ); 83 | }; -------------------------------------------------------------------------------- /NoVmp/vmprotect/deobfuscator.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Can Boluk 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | #pragma once 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "image_desc.hpp" 23 | 24 | namespace vmp 25 | { 26 | using namespace vtil::logger; 27 | 28 | using fn_instruction_filter = std::function; 29 | 30 | struct instruction_stream 31 | { 32 | std::vector> stream = {}; 33 | instruction_stream() {}; 34 | 35 | // std::vector wrappers, stripping the index 36 | // 37 | auto& operator[]( size_t n ) const { return stream[ n ].second; } 38 | size_t size() const { return stream.size(); } 39 | 40 | // Dumps all of the instructions in the stream to a byte array 41 | // 42 | std::vector to_raw() 43 | { 44 | // Normalize the stream 45 | // 46 | normalize(); 47 | 48 | // Linearly decode into a byte array and return 49 | // 50 | std::vector raw_stream; 51 | for ( auto& p : stream ) 52 | raw_stream.insert( raw_stream.end(), p.second.bytes.begin(), p.second.bytes.end() ); 53 | return raw_stream; 54 | } 55 | std::vector to_raw() const 56 | { 57 | return instruction_stream( *this ).to_raw(); 58 | } 59 | 60 | // Merges two instruction streams together 61 | // 62 | instruction_stream operator+( const instruction_stream& i2 ) const 63 | { 64 | // Merge both instruction streams, remove duplicates. 65 | // 66 | instruction_stream out; 67 | std::map pushed; 68 | for ( auto& is : { stream, i2.stream } ) 69 | { 70 | for ( auto& pair : is ) 71 | { 72 | if ( !pushed[ pair.first ] ) 73 | { 74 | pushed[ pair.first ] = true; 75 | out.stream.push_back( pair ); 76 | } 77 | } 78 | } 79 | 80 | // Return after sorting by instruction index into the stream 81 | // 82 | return out.normalize(); 83 | } 84 | 85 | // Normalizes the instruction stream 86 | // 87 | instruction_stream& normalize() 88 | { 89 | // Sort according to the original order 90 | std::sort( stream.begin(), stream.end(), [ ] ( auto& p1, auto& p2 ) 91 | { 92 | return p1.first <= p2.first; 93 | } ); 94 | return *this; 95 | } 96 | 97 | // Traces the register's usage history throughout the instruction stream 98 | // 99 | template 100 | std::pair> trace( x86_reg ireg, int end, int begin = 0 ) const 101 | { 102 | std::map dependencies; 103 | instruction_stream substream; 104 | 105 | // Trace the instruction upto the beginning of the control flow 106 | // 107 | for ( int i = end; i >= begin; i-- ) 108 | { 109 | auto& ins = stream[ i ].second; 110 | 111 | // Check whether the register is read / written to by this instruction 112 | // 113 | uint64_t read = false; 114 | uint64_t write = false; 115 | std::vector access_list; 116 | 117 | for ( int j = 0; j < ins.operands.size(); j++ ) 118 | { 119 | if ( ins.operands[ j ].type == X86_OP_REG ) 120 | { 121 | auto reg = ins.operands[ j ].reg; 122 | if ( vtil::amd64::registers.extend( reg ) != vtil::amd64::registers.extend( ireg ) ) 123 | { 124 | access_list.push_back( reg ); 125 | continue; 126 | } 127 | read |= ins.operands[ j ].access & CS_AC_READ; 128 | write |= ins.operands[ j ].access & CS_AC_WRITE; 129 | } 130 | else if ( ins.operands[ j ].type == X86_OP_MEM ) 131 | { 132 | for ( auto reg : { 133 | ins.operands[ j ].mem.base, 134 | ins.operands[ j ].mem.index } ) 135 | { 136 | if ( vtil::amd64::registers.extend( reg ) != vtil::amd64::registers.extend( ireg ) ) 137 | { 138 | access_list.push_back( reg ); 139 | continue; 140 | } 141 | read |= ins.operands[ j ].access & CS_AC_READ; 142 | // Can't read as its writing to memory if WRITE is marked 143 | } 144 | } 145 | } 146 | 147 | // If this instruction writes to this register, log it 148 | // 149 | if ( write ) 150 | { 151 | for ( auto reg : access_list ) dependencies[ reg ] = true; 152 | substream.stream.push_back( stream[ i ] ); 153 | 154 | if constexpr ( dump ) 155 | { 156 | log( " %c%c [[ %p: %s\t%s\n", 157 | read ? 'R' : ' ', 158 | write ? 'W' : ' ', 159 | ins.address, ins.mnemonic.data(), ins.operand_string.data() ); 160 | } 161 | } 162 | 163 | // If the instruction writes to this register WITHOUT reading it 164 | // no need to trace any further, return. 165 | if ( write && !read ) break; 166 | } 167 | 168 | // Convert to std::vector<> 169 | std::vector dependencies_r; 170 | for ( auto& p : dependencies ) 171 | if ( p.first != X86_REG_INVALID ) 172 | dependencies_r.push_back( p.first ); 173 | 174 | // Print dependencies 175 | if constexpr ( dump ) 176 | { 177 | log( " Depends on: [ " ); 178 | for ( auto reg : dependencies_r ) 179 | log( "%s ", vtil::amd64::name( reg ) ); 180 | log( "]\n" ); 181 | } 182 | 183 | return { substream.normalize(), dependencies_r }; 184 | } 185 | 186 | // Finds the next/prev matching instruction 187 | // 188 | int next( uint32_t instruction_id, 189 | const std::vector& operands, 190 | int from = 0 ) const 191 | { 192 | for ( int i = from; i < stream.size(); i++ ) 193 | if ( stream[ i ].second.is( instruction_id, operands ) ) return i; 194 | return -1; 195 | } 196 | 197 | int next( const fn_instruction_filter& filter, 198 | int from = 0 ) const 199 | { 200 | for ( int i = from; i < stream.size(); i++ ) 201 | if ( filter( stream[ i ].second ) ) return i; 202 | return -1; 203 | } 204 | 205 | int next( uint32_t instruction_id, 206 | const std::vector& operands, 207 | const fn_instruction_filter& filter, 208 | int from = 0 ) const 209 | { 210 | for ( int i = from; i < stream.size(); i++ ) 211 | if ( stream[ i ].second.is( instruction_id, operands ) && filter( stream[ i ].second ) ) return i; 212 | return -1; 213 | } 214 | 215 | int prev( uint32_t instruction_id, const std::vector& operands, int from = -1 ) const 216 | { 217 | if ( from == -1 ) from = stream.size() - 1; 218 | 219 | for ( int i = from; i >= 0; i-- ) 220 | if ( stream[ i ].second.is( instruction_id, operands ) ) return i; 221 | return -1; 222 | } 223 | 224 | int prev( const fn_instruction_filter& filter, 225 | int from = -1 ) const 226 | { 227 | if ( from == -1 ) from = stream.size() - 1; 228 | 229 | for ( int i = from; i >= 0; i-- ) 230 | if ( filter( stream[ i ].second ) ) return i; 231 | return -1; 232 | } 233 | 234 | int prev( uint32_t instruction_id, 235 | const std::vector& operands, 236 | const fn_instruction_filter& filter, 237 | int from = -1 ) const 238 | { 239 | if ( from == -1 ) from = stream.size() - 1; 240 | 241 | for ( int i = from; i >= 0; i-- ) 242 | if ( stream[ i ].second.is( instruction_id, operands ) && filter( stream[ i ].second ) ) return i; 243 | return -1; 244 | } 245 | 246 | // Deletes first N instructions 247 | // 248 | void erase( int n ) 249 | { 250 | while ( n-- ) stream.erase( stream.begin() ); 251 | } 252 | 253 | // Dumps the given instruction sequence 254 | // 255 | std::string to_string() const 256 | { 257 | std::string out; 258 | for ( auto& p : stream ) 259 | out += p.second.to_string() + "\n"; 260 | return out; 261 | } 262 | }; 263 | 264 | // Unrolls the entire instruction stream as far as possiblibly predictable statically 265 | // 266 | static instruction_stream deobfuscate( image_desc* img, uint32_t rva_rip ) 267 | { 268 | static std::mutex cache_mutex; 269 | static std::map cache; 270 | 271 | std::lock_guard g( cache_mutex ); 272 | auto& output = cache[ rva_rip ]; 273 | if ( output.stream.size() ) return output; 274 | 275 | // For each instruction at the given VA: 276 | // 277 | int instruction_idx = 0; 278 | while ( true ) 279 | { 280 | 281 | // Disassemble the instruction 282 | // 283 | std::vector i1 = vtil::amd64::disasm( img->rva_to_ptr( rva_rip ), rva_rip ); 284 | fassert( !i1.empty() ); 285 | vtil::amd64::instruction& instruction = i1[ 0 ]; 286 | output.stream.push_back( { ++instruction_idx, instruction } ); 287 | 288 | // Check if control flow deviates 289 | // 290 | if ( instruction.is( X86_INS_CALL, { X86_OP_IMM } ) ) 291 | rva_rip = instruction.operands[ 0 ].imm; 292 | else if ( instruction.is( X86_INS_JMP, { X86_OP_IMM } ) ) 293 | rva_rip = instruction.operands[ 0 ].imm, output.stream.pop_back(); 294 | else if ( instruction.id == X86_INS_JMP || instruction.id == X86_INS_RET ) 295 | break; 296 | else 297 | rva_rip += instruction.bytes.size(); 298 | } 299 | if ( output.stream.empty() ) vtil::logger::error( "Failed to unroll control-flow." ); 300 | return output; 301 | } 302 | }; -------------------------------------------------------------------------------- /NoVmp/vmprotect/il2vtil.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Can Boluk 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | #include "il2vtil.hpp" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "architecture.hpp" 22 | 23 | namespace vmp 24 | { 25 | struct converter 26 | { 27 | arch::opcode_id base_op; 28 | void( *converter_fn )( vtil::basic_block*, const arch::instruction&, uint8_t ); 29 | 30 | bool convert( vtil::basic_block* fl, const arch::instruction& ins, bool write_src = true ) 31 | { 32 | // Check if opcode matches the base opcode id 33 | // and extract the variants from the name 34 | // 35 | if ( ins.op.size() != base_op.size() ) return false; 36 | 37 | std::vector variants = {}; 38 | for ( int i = 0; i < ins.op.size(); i++ ) 39 | { 40 | if ( ins.op[ i ] == base_op[ i ] ) continue; 41 | if ( base_op[ i ] != '*' ) return {}; 42 | variants.push_back( arch::resolve_abbrv_param_size( ins.op[ i ] ) ); 43 | } 44 | fassert( variants.size() <= 1 ); 45 | variants.resize( 1 ); 46 | 47 | // Redirect to the converter 48 | // 49 | size_t p = fl->size(); 50 | converter_fn( fl, ins, variants[ 0 ] ); 51 | return true; 52 | } 53 | }; 54 | 55 | // Add per/bit flag addressing and vtil::UNDEFINED register. 56 | // - Ugly yeah but don't want to tailor the repo with arch-dependent code. 57 | // 58 | static constexpr vtil::register_desc FLAG_CF = vtil::REG_FLAGS.select( 1, 0 ); 59 | static constexpr vtil::register_desc FLAG_PF = vtil::REG_FLAGS.select( 1, 2 ); 60 | static constexpr vtil::register_desc FLAG_AF = vtil::REG_FLAGS.select( 1, 4 ); 61 | static constexpr vtil::register_desc FLAG_ZF = vtil::REG_FLAGS.select( 1, 6 ); 62 | static constexpr vtil::register_desc FLAG_SF = vtil::REG_FLAGS.select( 1, 7 ); 63 | static constexpr vtil::register_desc FLAG_DF = vtil::REG_FLAGS.select( 1, 10 ); 64 | static constexpr vtil::register_desc FLAG_OF = vtil::REG_FLAGS.select( 1, 11 ); 65 | 66 | static constexpr vtil::register_desc make_virtual_register( uint8_t context_offset, uint8_t size ) 67 | { 68 | fassert( ( ( context_offset & 7 ) + size ) <= 8 && size ); 69 | 70 | return { 71 | vtil::register_virtual, 72 | ( size_t ) context_offset / 8, 73 | size * 8, 74 | ( context_offset % 8 ) * 8 75 | }; 76 | } 77 | 78 | static std::vector converters = 79 | { 80 | { 81 | "VPOPV*", 82 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 83 | { 84 | auto& p = ins.parameters; 85 | fl 86 | // pop vrN 87 | ->pop( make_virtual_register( p[ 0 ], v ) ); 88 | } 89 | }, 90 | 91 | { 92 | "VPOPD*", 93 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 94 | { 95 | auto& p = ins.parameters; 96 | fl 97 | // add rsp, * 98 | ->shift_sp( v ); 99 | } 100 | }, 101 | 102 | { 103 | "VPUSHC*", 104 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 105 | { 106 | auto& p = ins.parameters; 107 | fl 108 | // push Imm 109 | ->push( vtil::operand( p[ 0 ], v * 8 ) ); 110 | } 111 | }, 112 | 113 | { 114 | "VPUSHV*", 115 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 116 | { 117 | auto& p = ins.parameters; 118 | fl 119 | // push vrN 120 | ->push( make_virtual_register( p[ 0 ], v ) ); 121 | 122 | } 123 | }, 124 | { 125 | "VPUSHR*", 126 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 127 | { 128 | auto& p = ins.parameters; 129 | 130 | if ( v == 8 ) 131 | { 132 | fl->push( vtil::REG_SP ); 133 | } 134 | else 135 | { 136 | auto t0 = fl->tmp( v * 8 ); 137 | fl->mov( t0, vtil::REG_SP ); 138 | fl->push( t0 ); 139 | } 140 | } 141 | }, 142 | 143 | { 144 | "VADDU*", 145 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 146 | { 147 | auto& p = ins.parameters; 148 | auto [t0, t1, t2] = fl->tmp( v * 8, v * 8, v * 8 ); 149 | auto [b0, b1, b2, b3] = fl->tmp( 1, 1, 1, 1 ); 150 | 151 | fl 152 | // t0 := [rsp] 153 | // t1 := [rsp+*] 154 | ->pop( t0 ) 155 | ->pop( t1 ) 156 | 157 | // t1 += t0 158 | ->mov( t2, t1 ) 159 | ->add( t1, t0 ) 160 | 161 | // Update flags. 162 | // SF = r < 0 163 | ->tl( FLAG_SF, t1, 0 ) 164 | // ZF = r == 0 165 | ->te( FLAG_ZF, t1, 0 ) 166 | // CF = r < a 167 | ->tul( FLAG_CF, t1, t2 ) 168 | // b0 = (a < 0) == (b < 0) 169 | ->tl( b2, t2, 0 ) 170 | ->tl( b3, t0, 0 ) 171 | ->te( b0, b2, b3 ) 172 | // b1 = (a < 0) != (r < 0) 173 | ->tl( b2, t2, 0 ) 174 | ->tl( b3, t1, 0 ) 175 | ->tne( b1, b2, b3 ) 176 | // OF = B0 & B1 177 | ->mov( FLAG_OF, b0 ) 178 | ->band( FLAG_OF, b1 ) 179 | 180 | // [rsp] := flags 181 | // [rsp+8] := t1 182 | ->push( t1 ) 183 | ->pushf(); 184 | } 185 | }, 186 | { 187 | "VDIVU*", 188 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 189 | { 190 | auto& p = ins.parameters; 191 | auto [a0, a1, d, c] = fl->tmp( v * 8, v * 8, v * 8, v * 8 ); 192 | 193 | fl 194 | // t0 := [rsp] 195 | // t1 := [rsp+*] 196 | // t2 := [rsp+2*] 197 | ->pop( d ) // d 198 | ->pop( a0 ) // a 199 | ->pop( c ) // c 200 | ->mov( a1, a0 ) 201 | 202 | // div 203 | ->div( a0, d, c ) 204 | ->rem( a1, d, c ) 205 | 206 | // [rsp] := flags 207 | // [rsp+8] := t0 208 | // [rsp+8+*] := t1 209 | ->push( a0 ) 210 | ->push( a1 ) 211 | ->pushf(); 212 | } 213 | }, 214 | { 215 | "VMULU*", 216 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 217 | { 218 | auto& p = ins.parameters; 219 | auto [a0, a1, d] = fl->tmp( v * 8, v * 8, v * 8 ); 220 | 221 | fl 222 | // t0 := [rsp] 223 | // t1 := [rsp+*] 224 | ->pop( d ) // d 225 | ->pop( a0 ) // a 226 | ->mov( a1, a0 ) 227 | 228 | // mul 229 | ->mul( a0, d ) 230 | ->mulhi( a1, d ) 231 | //->upflg( vtil::REG_FLAGS ) TODO 232 | 233 | // [rsp] := flags 234 | // [rsp+8] := t0 235 | // [rsp+8+*] := t1 236 | ->push( a0 ) 237 | ->push( a1 ) 238 | ->pushf(); 239 | } 240 | }, 241 | { 242 | "VIDIVU*", 243 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 244 | { 245 | auto& p = ins.parameters; 246 | auto [a0, a1, d, c] = fl->tmp( v * 8, v * 8, v * 8, v * 8 ); 247 | 248 | fl 249 | // t0 := [rsp] 250 | // t1 := [rsp+*] 251 | // t2 := [rsp+2*] 252 | ->pop( d ) // d 253 | ->pop( a0 ) // a 254 | ->pop( c ) // c 255 | ->mov( a1, a0 ) 256 | 257 | // idiv 258 | ->idiv( a0, d, c ) 259 | ->irem( a1, d, c ) 260 | 261 | // [rsp] := flags 262 | // [rsp+8] := t0 263 | // [rsp+8+*] := t1 264 | ->push( a0 ) 265 | ->push( a1 ) 266 | ->pushf(); 267 | } 268 | }, 269 | { 270 | "VIMULU*", 271 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 272 | { 273 | auto& p = ins.parameters; 274 | auto [a0, a1, d] = fl->tmp( v * 8, v * 8, v * 8 ); 275 | 276 | fl 277 | // t0 := [rsp] 278 | // t1 := [rsp+*] 279 | ->pop( d ) // d 280 | ->pop( a0 ) // a 281 | ->mov( a1, a0 ) 282 | 283 | // imul 284 | ->imul( a0, d ) 285 | ->imulhi( a1, d ) 286 | //->upflg( vtil::REG_FLAGS ) TODO 287 | ->mov( FLAG_SF, vtil::UNDEFINED ) 288 | ->mov( FLAG_ZF, vtil::UNDEFINED ) 289 | ->mov( FLAG_OF, vtil::UNDEFINED ) 290 | ->mov( FLAG_CF, vtil::UNDEFINED ) 291 | 292 | // [rsp] := flags 293 | // [rsp+8] := t0 294 | // [rsp+8+*] := t1 295 | ->push( a0 ) 296 | ->push( a1 ) 297 | ->pushf(); 298 | } 299 | }, 300 | { 301 | "VNANDU*", 302 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 303 | { 304 | auto& p = ins.parameters; 305 | auto [t0, t1] = fl->tmp( v * 8, v * 8 ); 306 | 307 | fl 308 | // t0 := [rsp] 309 | // t1 := [rsp+*] 310 | ->pop( t0 ) 311 | ->pop( t1 ) 312 | 313 | // t0 = nand(t0, t1) 314 | ->bnot( t0 ) 315 | ->bnot( t1 ) 316 | ->bor( t0, t1 ) 317 | //->upflg( vtil::REG_FLAGS ) TODO 318 | ->tl( FLAG_SF, t0, 0 ) 319 | ->te( FLAG_ZF, t0, 0 ) 320 | ->mov( FLAG_OF, 0 ) 321 | ->mov( FLAG_CF, 0 ) 322 | 323 | // [rsp] := flags 324 | // [rsp+8] := t0 325 | ->push( t0 ) 326 | ->pushf(); 327 | } 328 | }, 329 | 330 | { 331 | "VNORU*", 332 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 333 | { 334 | auto& p = ins.parameters; 335 | auto [t0, t1] = fl->tmp( v * 8, v * 8 ); 336 | 337 | fl 338 | // t0 := [rsp] 339 | // t1 := [rsp+*] 340 | ->pop( t0 ) 341 | ->pop( t1 ) 342 | 343 | // t0 = nor(t0, t1) 344 | ->bnot( t0 ) 345 | ->bnot( t1 ) 346 | ->band( t0, t1 ) 347 | //->upflg( vtil::REG_FLAGS ) TODO 348 | ->tl( FLAG_SF, t0, 0 ) 349 | ->te( FLAG_ZF, t0, 0 ) 350 | ->mov( FLAG_OF, 0 ) 351 | ->mov( FLAG_CF, 0 ) 352 | 353 | // [rsp] := flags 354 | // [rsp+8] := t0 355 | ->push( t0 ) 356 | ->pushf(); 357 | } 358 | }, 359 | 360 | { 361 | "VSHRU*", 362 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 363 | { 364 | auto& p = ins.parameters; 365 | auto [t0, t1, t2] = fl->tmp( v * 8, v * 8, 8 ); 366 | 367 | auto cf = t1; 368 | cf.bit_offset = cf.bit_count - 1; 369 | cf.bit_count = 1; 370 | 371 | auto ofx = t0; 372 | ofx.bit_offset = ofx.bit_count - 1; 373 | ofx.bit_count = 1; 374 | 375 | fl 376 | // t0 := [rsp] 377 | // t2 := [rsp+*] 378 | ->pop( t0 ) 379 | ->pop( t2 ) 380 | 381 | // t0 = t0 >> t2 382 | ->mov( t1, t0 ) 383 | ->bshr( t0, t2 ) 384 | //->upflg( vtil::REG_FLAGS ) TODO 385 | ->tl( FLAG_SF, t0, 0 ) 386 | ->te( FLAG_ZF, t0, 0 ) 387 | ->mov( FLAG_OF, ofx ) 388 | ->mov( FLAG_CF, cf ) 389 | ->bxor( FLAG_OF, cf ) 390 | 391 | // [rsp] := flags 392 | // [rsp+8] := t0 393 | ->push( t0 ) 394 | ->pushf(); 395 | } 396 | }, 397 | 398 | { 399 | "VSHLU*", 400 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 401 | { 402 | auto& p = ins.parameters; 403 | auto [t0, t1, t2] = fl->tmp( v * 8, v * 8, 8 ); 404 | 405 | auto cf = t1; 406 | cf.bit_offset = cf.bit_count - 1; 407 | cf.bit_count = 1; 408 | 409 | auto ofx = t0; 410 | ofx.bit_offset = ofx.bit_count - 1; 411 | ofx.bit_count = 1; 412 | 413 | fl 414 | // t0 := [rsp] 415 | // t1 := [rsp+*] 416 | ->pop( t0 ) 417 | ->pop( t2 ) 418 | 419 | // t0 = t0 << t1 420 | ->mov( t1, t0 ) 421 | ->bshl( t0, t2 ) 422 | //->upflg( vtil::REG_FLAGS ) TODO 423 | ->tl( FLAG_SF, t0, 0 ) 424 | ->te( FLAG_ZF, t0, 0 ) 425 | ->mov( FLAG_OF, ofx ) 426 | ->mov( FLAG_CF, cf ) 427 | ->bxor( FLAG_OF, cf ) 428 | 429 | // [rsp] := flags 430 | // [rsp+8] := t0 431 | ->push( t0 ) 432 | ->pushf(); 433 | } 434 | }, 435 | 436 | { 437 | "VREADU*", 438 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 439 | { 440 | auto& p = ins.parameters; 441 | auto [t0, t1] = fl->tmp( 64, v * 8 ); 442 | 443 | fl 444 | // t0 := [rsp] 445 | // t1 := [t0] 446 | // [rsp] := t1 447 | ->pop( t0 ); 448 | 449 | if ( ins.stream[ 1 ].prefix[ 1 ] == X86_PREFIX_GS ) 450 | { 451 | fl ->vemits( "mov rax, gs:0x30" ) 452 | ->vpinw( X86_REG_RAX ) 453 | ->add( t0, X86_REG_RAX ); 454 | } 455 | else if ( ins.stream[ 1 ].prefix[ 1 ] == X86_PREFIX_FS ) 456 | { 457 | unreachable(); 458 | } 459 | 460 | fl ->ldd( t1, t0, vtil::make_imm( 0ull ) ) 461 | ->push( t1 ); 462 | } 463 | }, 464 | { 465 | "VWRITEU*", 466 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 467 | { 468 | auto& p = ins.parameters; 469 | auto [t0, t1] = fl->tmp( 64, v * 8 ); 470 | 471 | fl 472 | // t0 := [rsp] 473 | // t1 := [rsp+8] 474 | // [t0] := t1 475 | ->pop( t0 ) 476 | ->pop( t1 ); 477 | 478 | if ( ins.stream[ 3 ].prefix[ 1 ] == X86_PREFIX_GS ) 479 | { 480 | fl ->vemits( "mov rax, gs:0x30" ) 481 | ->vpinw( X86_REG_RAX ) 482 | ->add( t0, X86_REG_RAX ); 483 | } 484 | else if ( ins.stream[ 3 ].prefix[ 1 ] == X86_PREFIX_FS ) 485 | { 486 | unreachable(); 487 | } 488 | 489 | fl 490 | ->str( t0, vtil::make_imm( 0ull ), t1 ); 491 | } 492 | }, 493 | { 494 | "VSETVSP", 495 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 496 | { 497 | auto& p = ins.parameters; 498 | fl 499 | // rsp := [rsp], 500 | ->pop( vtil::REG_SP ); 501 | } 502 | }, 503 | 504 | { 505 | "VNOP", 506 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 507 | { 508 | auto& p = ins.parameters; 509 | fl 510 | // nop 511 | ->nop(); 512 | } 513 | }, 514 | { 515 | "VJMP", 516 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 517 | { 518 | auto& p = ins.parameters; 519 | auto t0 = fl->tmp( 64 ); 520 | 521 | fl 522 | // t0 := [rsp], 523 | ->pop( t0 ) 524 | 525 | // jmp t0 526 | ->jmp( t0 ); 527 | } 528 | }, 529 | { 530 | "VEMIT", 531 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 532 | { 533 | auto& p = ins.parameters; 534 | 535 | for ( auto& instr : ins.stream.stream ) 536 | { 537 | // Pin read registers 538 | // 539 | for ( uint16_t reg : instr.second.regs_read ) 540 | if ( reg != X86_REG_RSP && reg != X86_REG_RIP && reg != X86_REG_EFLAGS ) 541 | fl->vpinr( vtil::operand( x86_reg( reg ) ) ); 542 | 543 | // Emit all bytes 544 | // 545 | for ( uint8_t byte : instr.second.bytes ) 546 | fl->vemit( vtil::make_imm( byte ) ); 547 | 548 | // Pin written registers 549 | // 550 | for ( uint16_t reg : instr.second.regs_write ) 551 | if ( reg != X86_REG_RSP && reg != X86_REG_RIP && reg != X86_REG_EFLAGS ) 552 | fl->vpinw( vtil::operand( x86_reg( reg ) ) ); 553 | } 554 | } 555 | }, 556 | { 557 | "VRDTSC", 558 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 559 | { 560 | auto& p = ins.parameters; 561 | fl 562 | // RDTSC 563 | ->vemits( "rdtsc" ) 564 | ->vpinw( X86_REG_RDX ) 565 | ->vpinw( X86_REG_RAX ) 566 | 567 | // [rsp + 4] := edx 568 | // [rsp] := eax 569 | ->push( X86_REG_EAX ) 570 | ->push( X86_REG_EDX ); 571 | } 572 | }, 573 | { 574 | "VCPUID", 575 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 576 | { 577 | auto& p = ins.parameters; 578 | fl 579 | // eax := [rsp] 580 | ->pop( X86_REG_EAX ) 581 | 582 | // CPUID 583 | ->vpinr( X86_REG_RCX ) 584 | ->vpinr( X86_REG_RAX ) 585 | ->vemits( "cpuid" ) 586 | ->vpinw( X86_REG_RDX ) 587 | ->vpinw( X86_REG_RCX ) 588 | ->vpinw( X86_REG_RBX ) 589 | ->vpinw( X86_REG_RAX ) 590 | 591 | // [rsp] := edx 592 | // [rsp+4] := ecx 593 | // [rsp+8] := ebx 594 | // [rsp+C] := eax 595 | ->push( X86_REG_EAX ) 596 | ->push( X86_REG_EBX ) 597 | ->push( X86_REG_ECX ) 598 | ->push( X86_REG_EDX ); 599 | } 600 | }, 601 | { 602 | "VCPUIDX", 603 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 604 | { 605 | auto& p = ins.parameters; 606 | fl 607 | // eax := [rsp] 608 | ->pop( X86_REG_EAX ) 609 | 610 | // CPUID 611 | ->vpinr( X86_REG_RCX ) 612 | ->vpinr( X86_REG_RAX ) 613 | ->vemits( "cpuid" ) 614 | ->vpinw( X86_REG_RDX ) 615 | ->vpinw( X86_REG_RCX ) 616 | ->vpinw( X86_REG_RBX ) 617 | ->vpinw( X86_REG_RAX ) 618 | 619 | // [rsp] := edx 620 | // [rsp+4] := ecx 621 | // [rsp+8] := ebx 622 | // [rsp+C] := eax 623 | ->push( X86_REG_EAX ) 624 | ->push( X86_REG_EBX ) 625 | ->push( X86_REG_ECX ) 626 | ->push( X86_REG_EDX ); 627 | } 628 | }, 629 | { 630 | "VLOCKXCHGU*", 631 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 632 | { 633 | const char* reader = 634 | v == 8 ? "lock xchg qword ptr [rdx], rax" : 635 | v == 4 ? "lock xchg dword ptr [rdx], eax" : 636 | v == 2 ? "lock xchg word ptr [rdx], ax" : 637 | v == 1 ? "lock xchg byte ptr [rdx], al" : ""; 638 | 639 | auto vr = vtil::amd64::registers.remap( X86_REG_RAX, 0, v ); 640 | auto& p = ins.parameters; 641 | fl 642 | // rdx := [rsp] 643 | ->pop( X86_REG_RDX ) 644 | 645 | // reg := [rsp + 8] 646 | ->pop( vr ) 647 | 648 | // LOCK XCHG [RDX], reg 649 | ->vpinr( X86_REG_RDX ) 650 | ->vpinr( X86_REG_RAX ) 651 | ->vemits( reader ) 652 | ->vpinw( X86_REG_RAX ) 653 | 654 | // [rsp] := reg 655 | ->push( vr ); 656 | } 657 | }, 658 | { 659 | "VSHRDU*", 660 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 661 | { 662 | auto& p = ins.parameters; 663 | auto [t0, t1, t2, t3] = fl->tmp( v * 8, v * 8, 8, 8 ); 664 | 665 | auto [f0, f1] = fl->tmp( v * 8, v * 8 ); 666 | 667 | fl 668 | // t0 := [rsp] 669 | // t1 := [rsp+*] 670 | // t2 := [rsp+2*] 671 | ->pop( t0 ) 672 | ->pop( t1 ) 673 | ->pop( t2 ) 674 | 675 | // t0 := t0 >> t2 676 | ->bshr( t0, t2 ) 677 | 678 | // t3 := v[0]*8 679 | // t3 -= t2 680 | ->mov( t3, vtil::make_imm( v * 8 ) ) 681 | ->sub( t3, t2 ) 682 | 683 | // t1 := t1 << t3 684 | ->bshl( t1, t3 ) 685 | 686 | // t0 |= t1 687 | ->bor( t0, t1 ) 688 | //->upflg( vtil::REG_FLAGS ) TODO 689 | ->tl( FLAG_SF, t0, 0 ) 690 | ->te( FLAG_ZF, t0, 0 ) 691 | ->mov( FLAG_OF, vtil::UNDEFINED ) 692 | ->mov( FLAG_CF, vtil::UNDEFINED ) 693 | 694 | // [rsp+8] := t0 695 | ->push( t0 ) 696 | ->pushf(); 697 | } 698 | }, 699 | { 700 | "VSHLDU*", 701 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 702 | { 703 | auto& p = ins.parameters; 704 | auto [t0, t1, t2, t3] = fl->tmp( v * 8, v * 8, 8, 8 ); 705 | 706 | fl 707 | // t0 := [rsp] 708 | // t1 := [rsp+*] 709 | // t2 := [rsp+2*] 710 | ->pop( t0 ) 711 | ->pop( t1 ) 712 | ->pop( t2 ) 713 | 714 | // t0 := t0 >> t2 715 | ->bshl( t0, t2 ) 716 | 717 | // t3 := v[0]*8 718 | // t3 -= t2 719 | ->mov( t3, vtil::make_imm( v * 8 ) ) 720 | ->sub( t3, t2 ) 721 | 722 | // t1 := t1 << t3 723 | ->bshr( t1, t3 ) 724 | 725 | // t0 |= t1 726 | ->bor( t0, t1 ) 727 | //->upflg( vtil::REG_FLAGS ) TODO 728 | ->tl( FLAG_SF, t0, 0 ) 729 | ->te( FLAG_ZF, t0, 0 ) 730 | ->mov( FLAG_OF, vtil::UNDEFINED ) 731 | ->mov( FLAG_CF, vtil::UNDEFINED ) 732 | 733 | // [rsp+8] := t0 734 | ->push( t0 ) 735 | ->pushf(); 736 | } 737 | }, 738 | { 739 | "VPUSHCR0", 740 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 741 | { 742 | auto& p = ins.parameters; 743 | fl 744 | ->vemits( "mov rax, cr0" ) 745 | ->vpinw( X86_REG_RAX ) 746 | ->push( X86_REG_RAX ); 747 | } 748 | }, 749 | { 750 | "VPUSHCR3", 751 | [ ] ( vtil::basic_block* fl, const arch::instruction& ins, uint8_t v ) 752 | { 753 | auto& p = ins.parameters; 754 | fl 755 | ->vemits( "mov rax, cr3" ) 756 | ->vpinw( X86_REG_RAX ) 757 | ->push( X86_REG_RAX ); 758 | } 759 | }, 760 | }; 761 | 762 | void translate( vtil::basic_block* fl, const arch::instruction& ins ) 763 | { 764 | for ( auto& converter : converters ) 765 | { 766 | if ( converter.convert( fl, ins ) ) 767 | return; 768 | } 769 | vtil::logger::error( "Failed converting:\n", ins.op.data() ); 770 | } 771 | }; 772 | -------------------------------------------------------------------------------- /NoVmp/vmprotect/il2vtil.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Can Boluk 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | #pragma once 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "architecture.hpp" 23 | 24 | namespace vmp 25 | { 26 | void translate( vtil::basic_block* fl, const arch::instruction& ins ); 27 | }; -------------------------------------------------------------------------------- /NoVmp/vmprotect/image_desc.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Can Boluk 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | #pragma once 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | namespace vmp 23 | { 24 | struct virtual_routine 25 | { 26 | uint32_t jmp_rva; 27 | bool mid_routine; 28 | vtil::routine* routine = nullptr; 29 | }; 30 | 31 | struct image_desc 32 | { 33 | // Basic PE image & details 34 | // 35 | std::vector raw; 36 | uint64_t override_image_base = 0; 37 | 38 | win::image_x64_t* get_pe_header() { return ( win::image_x64_t* ) raw.data(); } 39 | win::nt_headers_x64_t* get_nt_headers() { return get_pe_header()->get_nt_headers(); } 40 | uint64_t get_mapped_image_base() { return get_nt_headers()->optional_header.image_base; } 41 | uint64_t get_real_image_base() { return override_image_base ? override_image_base : get_mapped_image_base(); } 42 | 43 | template 44 | T* rva_to_ptr( uint32_t rva ) { return get_pe_header()->rva_to_ptr( rva ); } 45 | win::section_header_t* rva_to_section( uint32_t rva ) { return get_pe_header()->rva_to_section( rva ); } 46 | 47 | // List of virtualized routines 48 | // 49 | std::vector virt_routines; 50 | 51 | // VMProtect specific options. 52 | // 53 | bool has_relocs = false; 54 | bool strip_constant_obfuscation = false; 55 | }; 56 | }; -------------------------------------------------------------------------------- /NoVmp/vmprotect/rkey.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Can Boluk 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | #pragma once 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "../emulator/emulator.hpp" 24 | #include "../emulator/rwx_allocator.hpp" 25 | #include "vm_state.hpp" 26 | #include "deobfuscator.hpp" 27 | 28 | namespace vmp 29 | { 30 | // Extracts the next rolling key decryption block within the given instruction stream 31 | // - On failure returns -1 as the iterator index 32 | static std::pair extract_next_rkey_block( vm_state* state, 33 | const instruction_stream& is, 34 | int index = 0 ) 35 | { 36 | rkey_block out; 37 | 38 | // Extend the register we received originally (EAX->RAX and so on) 39 | // 40 | out.rolling_key_register = vtil::amd64::registers.extend( state->reg_vrk ); 41 | 42 | // Define our filters 43 | // 44 | auto prologue_filter = [ & ] ( const vtil::amd64::instruction& ins ) 45 | { 46 | // Type #1 47 | // [ xor rbp, r8 ] 48 | // 49 | return ins.is( X86_INS_XOR, { X86_OP_REG, X86_OP_REG } ) && 50 | vtil::amd64::registers.extend( ins.operands[ 1 ].reg ) == out.rolling_key_register; 51 | }; 52 | auto epilogue_filter = [ & ] ( const vtil::amd64::instruction& ins ) 53 | { 54 | // Type #1 55 | // [ xor r8, rbp ] 56 | // 57 | if ( ins.is( X86_INS_XOR, { X86_OP_REG, X86_OP_REG } ) ) 58 | { 59 | return 60 | vtil::amd64::registers.extend( ins.operands[ 0 ].reg ) == out.rolling_key_register && 61 | ins.operands[ 1 ].reg == out.output_register; 62 | } 63 | // Type #2 64 | // [ push r8 ] 65 | // [ xor dword ptr [rsp], edi ] 66 | // [ pop r8 ] 67 | // 68 | else if ( ins.is( X86_INS_XOR, { X86_OP_MEM, X86_OP_REG } ) ) 69 | { 70 | return 71 | ins.operands[ 0 ].mem.base == X86_REG_RSP && 72 | ins.operands[ 0 ].mem.disp == 0 && 73 | ins.operands[ 0 ].mem.index == X86_REG_INVALID && 74 | ins.operands[ 0 ].mem.scale == 1 && 75 | ins.operands[ 1 ].reg == out.output_register; 76 | } 77 | return false; 78 | }; 79 | 80 | // Find the next prologue, report failure if we fail to do so 81 | // 82 | int prologue_index = is.next( prologue_filter, index ); 83 | if ( prologue_index == -1 ) return { -1, {} }; 84 | 85 | // Fill out the block details 86 | // 87 | out.block_start = { prologue_index, is[ prologue_index ].address }; 88 | out.output_size = is[ prologue_index ].operands[ 0 ].size; 89 | out.output_register = is[ prologue_index ].operands[ 0 ].reg; 90 | 91 | // Find the next epilogue, increment the iterator and try the next prologue if we fail to do so 92 | // 93 | int epilogue_index = is.next( epilogue_filter, prologue_index + 1 ); 94 | if ( epilogue_index == -1 ) return extract_next_rkey_block( state, is, prologue_index + 1 ); 95 | out.block_end = { epilogue_index, is[ epilogue_index ].address }; 96 | 97 | // Trace register usage accross the block 98 | // 99 | auto [block_stream, block_dependencies] = is.trace( 100 | out.output_register, 101 | epilogue_index - 1, 102 | prologue_index + 1 103 | ); 104 | 105 | // If the decryption block we discovered has dependencies, then it is not valid, try the next prologue 106 | // 107 | if ( !block_dependencies.empty() ) return extract_next_rkey_block( state, is, prologue_index + 1 ); 108 | 109 | // Write the emulation information and return the block 110 | // 111 | out.decrypt = [ =, block_stream = block_stream ] ( void* src, rkey_t key ) -> std::pair 112 | { 113 | rkey_value value; 114 | value.u64 = 0; 115 | value.size = out.output_size; 116 | memcpy( &value.u64, src, value.size ); 117 | 118 | // Emulate prologue manually 119 | // 120 | switch ( value.size ) 121 | { 122 | case 1: value.u8 ^= key; break; 123 | case 2: value.u16 ^= key; break; 124 | case 4: value.u32 ^= key; break; 125 | case 8: value.u64 ^= key; break; 126 | } 127 | 128 | // Emulate the block entirely 129 | // 130 | std::vector raw_stream = block_stream.to_raw(); 131 | std::vector> exec_stream = { raw_stream.begin(), raw_stream.end() }; 132 | exec_stream.push_back( 0xC3 ); 133 | 134 | emulator emu = {}; 135 | emu.set( state->reg_vrk, key ); 136 | emu.set( out.output_register, value.u64 ); 137 | emu.invoke( exec_stream.data() ); 138 | value.u64 = emu.get( out.output_register ); 139 | 140 | // Emulate epilogue manually 141 | // 142 | switch ( value.size ) 143 | { 144 | case 1: key ^= value.u8; break; 145 | case 2: key ^= value.u16; break; 146 | case 4: key ^= value.u32; break; 147 | case 8: key ^= value.u64; break; 148 | } 149 | return { value, key }; 150 | }; 151 | return { epilogue_index + 1, out }; 152 | } 153 | 154 | // Extracts all of the rolling key decryption blocks within the given instruction stream 155 | // 156 | static std::vector extract_rkey_blocks( vm_state* vstate, 157 | const instruction_stream& is ) 158 | { 159 | // Iterate entire instruction stream: 160 | // 161 | std::vector out; 162 | for ( int iterator = 0; iterator != -1 && iterator < is.stream.size(); ) 163 | { 164 | // Try to extract the next block 165 | // 166 | auto [it_next, block] = extract_next_rkey_block( vstate, is, iterator ); 167 | 168 | // Break the loop if failed to find the block 169 | // 170 | if ( it_next == -1 ) break; 171 | 172 | // Else push on the output list and continue iteration 173 | // 174 | out.push_back( block ); 175 | iterator = it_next; 176 | } 177 | return out; 178 | } 179 | }; -------------------------------------------------------------------------------- /NoVmp/vmprotect/subroutines.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Can Boluk 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | #include "subroutines.hpp" 17 | 18 | namespace vmp 19 | { 20 | // Reduces the given virtualized instruction handler to the base 21 | // (AKA Deobfuscation + I/O based Register tracing) 22 | // 23 | void reduce_chunk( vm_state* vstate, instruction_stream& is, const std::vector>& parameters, bool has_next ) 24 | { 25 | for ( auto& parameter_pair : parameters ) 26 | { 27 | rkey_block* block = parameter_pair.first; 28 | 29 | int i_0 = 0; 30 | for ( auto& ins : is.stream ) 31 | { 32 | if ( ins.second.address == block->block_start.second ) 33 | { 34 | i_0 = ins.first; 35 | break; 36 | } 37 | } 38 | 39 | int i_1 = 0; 40 | for ( auto& ins : is.stream ) 41 | { 42 | if ( ins.second.address == block->block_end.second ) 43 | { 44 | i_1 = ins.first; 45 | break; 46 | } 47 | } 48 | 49 | // Strip all of the decryption block 50 | is.stream.erase( std::remove_if( is.stream.begin(), is.stream.end(), [ & ] ( auto& p ) 51 | { 52 | return i_0 <= p.first && p.first <= i_1; 53 | } ), is.stream.end() ); 54 | 55 | // Append fake instruction 56 | vtil::amd64::instruction ins; 57 | ins.address = -1; 58 | ins.id = X86_INS_INVALID; 59 | ins.mnemonic = "loadc"; 60 | ins.operand_string = vtil::amd64::name( block->output_register ); 61 | ins.operand_string += ", " + std::to_string( parameter_pair.second.u64 ); 62 | ins.regs_write.insert( block->output_register ); 63 | 64 | ins.operands.resize( 2 ); 65 | ins.eflags = 0; 66 | ins.operands[ 0 ].type = X86_OP_REG; 67 | ins.operands[ 0 ].reg = block->output_register; 68 | ins.operands[ 0 ].access = CS_AC_WRITE; 69 | ins.operands[ 0 ].size = block->output_size; 70 | 71 | ins.operands[ 1 ].type = X86_OP_IMM; 72 | ins.operands[ 1 ].imm = parameter_pair.second.u64; 73 | ins.operands[ 1 ].size = block->output_size; 74 | is.stream.push_back( { i_1, ins } ); 75 | 76 | // Normalize the stream 77 | is.normalize(); 78 | }; 79 | 80 | // Trace all changes to RSP and VSP 81 | // 82 | std::map traced = {}; 83 | traced[ X86_REG_RSP ] = true; 84 | traced[ vstate->reg_vsp ] = true; 85 | 86 | // If JA is present, always take the branch 87 | // 88 | int ja_i = is.next( X86_INS_JA, { X86_OP_IMM } ); 89 | if ( ja_i != -1 ) 90 | is.stream.resize( ja_i ); 91 | 92 | // Trace the instruction from the end of the control flow 93 | // 94 | instruction_stream is_reduced = {}; 95 | for ( int i = is.stream.size() - 1; i >= 0; i-- ) 96 | { 97 | auto& ins = is[ i ]; 98 | 99 | // Skip if we hit the xor block preceding the decryption rkb 100 | // 101 | if ( has_next && 102 | ins.id > parameters.back().first->block_start.first&& 103 | ins.is( X86_INS_XOR, { X86_OP_MEM, X86_OP_REG } ) && 104 | ins.operands[ 0 ].mem.base == X86_REG_RSP && 105 | vtil::amd64::registers.extend( ins.operands[ 1 ].reg ) == vtil::amd64::registers.extend( parameters.back().first->output_register ) ) 106 | { 107 | continue; 108 | } 109 | // Blacklist certain instructions as they 110 | // mess up with our vtil::amd64::registers.extend logic or FLAGS 111 | // 112 | if ( ins.id == X86_INS_CQO || 113 | ins.id == X86_INS_CWD || 114 | ins.id == X86_INS_CBW || 115 | ins.id == X86_INS_CWDE || 116 | ins.id == X86_INS_CDQ || 117 | ins.id == X86_INS_CDQE || 118 | ins.id == X86_INS_LAHF || 119 | ins.id == X86_INS_TEST || 120 | ins.id == X86_INS_CMP ) 121 | { 122 | continue; 123 | } 124 | // Self references are always logged 125 | // 126 | if ( ins.is( X86_INS_LEA, { X86_OP_REG, X86_OP_MEM } ) && 127 | ins.operands[ 1 ].mem.disp == -7 && 128 | ins.operands[ 1 ].mem.scale == 1 && 129 | ins.operands[ 1 ].mem.base == X86_REG_RIP && 130 | ins.operands[ 1 ].mem.index == X86_REG_INVALID ) 131 | { 132 | // Do not trace till the ADD of the jump destination calculation though 133 | // 134 | is_reduced.stream.push_back( is.stream[ i ] ); 135 | continue; 136 | } 137 | // PUSHFQ is always logged 138 | // 139 | if ( ins.is( X86_INS_PUSHFQ, {} ) ) 140 | { 141 | // Nothing to trace 142 | // 143 | is_reduced.stream.push_back( is.stream[ i ] ); 144 | continue; 145 | } 146 | 147 | // Check whether the register is read / written to by this instruction 148 | // (Non-implicit operand-invoked R/W only) 149 | // 150 | std::map reads; 151 | std::map writes; 152 | uint64_t mem_read = false; 153 | uint64_t mem_write = false; 154 | uint32_t eflags_write = ins.eflags; 155 | 156 | for ( auto& op : ins.operands ) 157 | { 158 | if ( op.type == X86_OP_REG ) 159 | { 160 | if ( op.access & CS_AC_READ ) 161 | reads[ vtil::amd64::registers.extend( op.reg ) ] = true; 162 | if ( op.access & CS_AC_WRITE ) 163 | writes[ vtil::amd64::registers.extend( op.reg ) ] = true; 164 | } 165 | else if ( op.type == X86_OP_MEM ) 166 | { 167 | for ( auto reg : { 168 | op.mem.base, 169 | op.mem.index } ) 170 | { 171 | if ( reg != X86_REG_INVALID ) 172 | reads[ vtil::amd64::registers.extend( reg ) ] = true; 173 | } 174 | 175 | mem_read |= op.access & CS_AC_READ; 176 | mem_write |= op.access & CS_AC_WRITE; 177 | } 178 | } 179 | 180 | // Consider the side effects of the register execution 181 | // (With the exception of RSP and RFLAGS) 182 | // 183 | for ( uint16_t _r : ins.regs_read ) 184 | { 185 | x86_reg r = vtil::amd64::registers.extend( _r ); 186 | if ( r == X86_REG_EFLAGS || r == X86_REG_RSP ) continue; 187 | // CPUID:RCX exception 188 | if ( ins.id == X86_INS_CPUID && r == X86_REG_RCX ) continue; 189 | reads[ r ] = true; 190 | } 191 | 192 | for ( uint16_t _r : ins.regs_write ) 193 | { 194 | x86_reg r = vtil::amd64::registers.extend( _r ); 195 | if ( r == X86_REG_EFLAGS || r == X86_REG_RSP ) continue; 196 | writes[ r ] = true; 197 | } 198 | 199 | // If we write to memory OR a traced register, 200 | // all of the registers we read should be traced 201 | // 202 | bool should_be_tracked = mem_write; 203 | for ( auto& p : traced ) 204 | { 205 | if ( writes[ p.first ] ) 206 | should_be_tracked |= p.second; 207 | } 208 | 209 | // If instruction is tracked: 210 | // 211 | if ( should_be_tracked ) 212 | { 213 | // Stop tracing the registers we wrote to 214 | // 215 | for ( auto& p : writes ) 216 | traced[ p.first ] &= !p.second; 217 | 218 | // Start tracing the registers we read from 219 | // 220 | for ( auto& p : reads ) 221 | traced[ p.first ] |= p.second; 222 | 223 | // Log the current instruction 224 | // 225 | is_reduced.stream.push_back( is.stream[ i ] ); 226 | } 227 | } 228 | 229 | // Replace input stream with the reduced stream 230 | // 231 | is.stream = is_reduced.stream; 232 | is.normalize(); 233 | } 234 | 235 | // Deduces the virtual register key from the given instruction stream 236 | // 237 | void update_vrk( vm_state* state, const instruction_stream& is ) 238 | { 239 | // Find new rolling key register 240 | // 241 | int i_enc_end = is.next( X86_INS_XOR, { X86_OP_MEM, X86_OP_REG }, [ & ] ( const vtil::amd64::instruction& ins ) 242 | { 243 | return 244 | ins.operands[ 0 ].mem.base == X86_REG_RSP && 245 | ins.operands[ 0 ].mem.disp == 0 && 246 | ins.operands[ 0 ].mem.index == X86_REG_INVALID && 247 | ins.operands[ 0 ].mem.scale == 1; 248 | } ); 249 | fassert( i_enc_end != -1 ); 250 | int i_pop = is.next( X86_INS_POP, { X86_OP_REG }, i_enc_end ); 251 | fassert( i_pop != -1 ); 252 | state->reg_vrk = is[ i_pop ].operands[ 0 ].reg; 253 | } 254 | 255 | // Deduces the virtual instruction stream direction from the given instruction stream 256 | // 257 | void update_vip_direction( vm_state* state, const instruction_stream& is ) 258 | { 259 | // Define the filters based on the way VIP stream is read 260 | // 261 | auto fwd_filter = [ & ] ( const vtil::amd64::instruction& ins ) 262 | { 263 | // Type #1: 264 | // [ add rbp, 4 ] 265 | // 266 | if ( ins.is( X86_INS_ADD, { X86_OP_REG, X86_OP_IMM } ) ) 267 | { 268 | return ins.operands[ 0 ].reg == state->reg_vip && 269 | ins.operands[ 1 ].imm == 4;; 270 | } 271 | // Type #2: 272 | // [ lea rbp, [rbp+4] ] 273 | // 274 | else if ( ins.is( X86_INS_LEA, { X86_OP_REG, X86_OP_MEM } ) ) 275 | { 276 | return 277 | ins.operands[ 0 ].reg == state->reg_vip && 278 | ins.operands[ 1 ].mem.disp == 4 && 279 | ins.operands[ 1 ].mem.scale == 1 && 280 | ins.operands[ 1 ].mem.base == state->reg_vip && 281 | ins.operands[ 1 ].mem.index == X86_REG_INVALID; 282 | } 283 | return false; 284 | }; 285 | auto bwd_filter = [ & ] ( const vtil::amd64::instruction& ins ) 286 | { 287 | // Type #1: 288 | // [ sub rbp, 4 ] 289 | // 290 | if ( ins.is( X86_INS_SUB, { X86_OP_REG, X86_OP_IMM } ) ) 291 | { 292 | return ins.operands[ 0 ].reg == state->reg_vip && 293 | ins.operands[ 1 ].imm == 4; 294 | } 295 | // Type #2: 296 | // [ lea rbp, [rbp-4] ] 297 | // 298 | else if ( ins.is( X86_INS_LEA, { X86_OP_REG, X86_OP_MEM } ) ) 299 | { 300 | return 301 | ins.operands[ 0 ].reg == state->reg_vip && 302 | ins.operands[ 1 ].mem.disp == -4 && 303 | ins.operands[ 1 ].mem.scale == 1 && 304 | ins.operands[ 1 ].mem.base == state->reg_vip && 305 | ins.operands[ 1 ].mem.index == X86_REG_INVALID; 306 | } 307 | return false; 308 | }; 309 | 310 | // Find the first instances for both where possible 311 | // 312 | auto i_fwd = is.next( fwd_filter ); 313 | auto i_bwd = is.next( bwd_filter ); 314 | 315 | // Deduct the way instruction stream is iterated 316 | // 317 | if ( i_fwd == -1 && i_bwd != -1 ) state->dir_vip = -1; 318 | else if ( i_fwd != -1 && i_bwd == -1 ) state->dir_vip = +1; 319 | else if ( i_fwd != -1 && i_bwd != -1 ) state->dir_vip = i_fwd > i_bwd ? -1 : +1; 320 | else unreachable(); 321 | } 322 | 323 | // Finds the self-reference point from the given instruction stream if relevant 324 | // 325 | std::optional find_self_ref( vm_state* state, const instruction_stream& is, int index ) 326 | { 327 | // Find the first LEA r64, [$] 328 | // 329 | int i_ref_self = is.next( X86_INS_LEA, { X86_OP_REG, X86_OP_MEM }, [ & ] ( const vtil::amd64::instruction& ins ) 330 | { 331 | return 332 | ins.operands[ 1 ].mem.disp == -7 && 333 | ins.operands[ 1 ].mem.scale == 1 && 334 | ins.operands[ 1 ].mem.base == X86_REG_RIP && 335 | ins.operands[ 1 ].mem.index == X86_REG_INVALID; 336 | }, index ); 337 | if ( i_ref_self == -1 ) return {}; 338 | else return { is[ i_ref_self ].address }; 339 | } 340 | 341 | // Parses VMENTER subroutine and extracts the vm information, entry point of the 342 | // virtualized routine, rolling key 0 value, and describes the push order of registers. 343 | // - Pushing reloc at last is left to the caller. 344 | // 345 | std::pair, vtil::vip_t> parse_vmenter( vm_state* vstate, uint32_t rva_ep ) 346 | { 347 | // Unroll the stream 348 | // 349 | auto is = deobfuscate( vstate->img, rva_ep ); 350 | 351 | // Instruction stream should start with a 32 bit constant being pushed which is the 352 | // encrypted offset to the beginning of the virtual instruction stream 353 | // 354 | fassert( is[ 0 ].is( X86_INS_PUSH, { X86_OP_IMM } ) ); 355 | uint32_t vip_offset_encrypted = is[ 0 ].operands[ 0 ].imm; 356 | 357 | // Resolve the stack composition 358 | // 359 | x86_reg reg_reloc_delta; 360 | std::vector stack = 361 | { 362 | { vip_offset_encrypted, 64 }, 363 | { vstate->img->get_real_image_base() + is[ 0 ].address + is[ 0 ].bytes.size() + 5, 64 } 364 | }; 365 | 366 | for ( int i = 0;; i++ ) 367 | { 368 | // If PUSH R64 369 | if ( is[ i ].is( X86_INS_PUSH, { X86_OP_REG } ) ) 370 | stack.push_back( is[ i ].operands[ 0 ].reg ); 371 | // If PUSHFQ 372 | if ( is[ i ].is( X86_INS_PUSHFQ, {} ) ) 373 | stack.push_back( vtil::REG_FLAGS ); 374 | 375 | // End of pushed registers, reset stream 376 | if ( is[ i ].is( X86_INS_MOVABS, { X86_OP_REG, X86_OP_IMM } ) ) 377 | { 378 | reg_reloc_delta = is[ i ].operands[ 0 ].reg; 379 | is.erase( i - 1 ); 380 | break; 381 | } 382 | } 383 | fassert( stack.size() == ( 16 + 2 ) ); 384 | 385 | // Resolve the stack composition 386 | // 387 | uint32_t ep_vip_offset = stack.size() * 8; 388 | 389 | // Resolve the register mapped to be VSP 390 | // 391 | int i_save_registers_id = 0; 392 | while ( true ) 393 | { 394 | // Find the first MOV r64, RSP 395 | // 396 | i_save_registers_id = is.next( X86_INS_MOV, { X86_OP_REG, X86_OP_REG }, [ & ] ( const vtil::amd64::instruction& ins ) 397 | { 398 | return ins.operands[ 1 ].reg == X86_REG_RSP; 399 | }, i_save_registers_id ); 400 | fassert( i_save_registers_id != -1 ); 401 | vstate->reg_vsp = is[ i_save_registers_id ].operands[ 0 ].reg; 402 | 403 | // Check for any false positives 404 | // 405 | auto [vsp_ss, vsp_dep] = is.trace( vstate->reg_vsp, is.stream.size() - 1 ); 406 | if ( vsp_ss.stream.size() != 1 || 407 | vsp_ss[ 0 ].address != is[ i_save_registers_id ].address ) 408 | { 409 | i_save_registers_id++; 410 | continue; 411 | } 412 | break; 413 | } 414 | 415 | // Find the first stack access 416 | // 417 | int i_load_vip_id = is.next( X86_INS_MOV, { X86_OP_REG, X86_OP_MEM }, [ & ] ( const vtil::amd64::instruction& ins ) 418 | { 419 | return 420 | ins.operands[ 1 ].mem.base == X86_REG_RSP && 421 | ins.operands[ 1 ].mem.disp == ep_vip_offset; 422 | } ); 423 | fassert( i_load_vip_id != -1 ); 424 | vstate->reg_vip = is[ i_load_vip_id ].operands[ 0 ].reg; 425 | 426 | // Find the first ADD r, x or LEA r, [r+x] 427 | // 428 | auto vip_d_epi_filter = [ & ] ( const vtil::amd64::instruction& ins ) 429 | { 430 | if ( ins.is( X86_INS_ADD, { X86_OP_REG, X86_OP_REG } ) ) 431 | { 432 | return ins.operands[ 0 ].reg == vstate->reg_vip && 433 | ins.operands[ 1 ].reg == reg_reloc_delta; 434 | } 435 | else if ( ins.is( X86_INS_LEA, { X86_OP_REG, X86_OP_MEM } ) ) 436 | { 437 | return ins.operands[ 0 ].reg == vstate->reg_vip && 438 | ins.operands[ 1 ].mem.disp == 0 && 439 | ins.operands[ 1 ].mem.scale == 1 && 440 | ( ( ins.operands[ 1 ].mem.base == reg_reloc_delta && 441 | ins.operands[ 1 ].mem.index == vstate->reg_vip ) || 442 | ( ins.operands[ 1 ].mem.index == reg_reloc_delta && 443 | ins.operands[ 1 ].mem.base == vstate->reg_vip ) ); 444 | } 445 | return false; 446 | }; 447 | int i_add_base_id = is.next( vip_d_epi_filter, i_load_vip_id ); 448 | fassert( i_add_base_id != -1 ); 449 | 450 | // Extract the VIP decryption code and wrap with a lambda 451 | // 452 | auto [vip_dec_ss, vip_dec_ss_dep] = is.trace( 453 | vstate->reg_vip, 454 | i_add_base_id - 1, 455 | i_load_vip_id + 1 456 | ); 457 | fassert( vip_dec_ss_dep.empty() ); 458 | 459 | // Cleanup the stream again 460 | // 461 | is.erase( i_add_base_id ); 462 | 463 | // Decrypt the VIP entry point 464 | // 465 | std::vector raw_stream = vip_dec_ss.to_raw(); 466 | std::vector> exec_stream = { raw_stream.begin(), raw_stream.end() }; 467 | exec_stream.push_back( 0xC3 ); 468 | emulator emu = {}; 469 | emu.set( vstate->reg_vip, vip_offset_encrypted ); 470 | emu.invoke( exec_stream.data() ); 471 | static constexpr uint64_t default_image_base = 0x100000000; 472 | uint32_t rva_vip0 = emu.get( vstate->reg_vip ) + default_image_base - vstate->img->get_real_image_base(); 473 | 474 | // Find our reference point 475 | // 476 | auto ref_point = find_self_ref( vstate, is ); 477 | fassert( ref_point.has_value() ); 478 | 479 | // Update VIP direction 480 | // 481 | update_vip_direction( vstate, is ); 482 | 483 | // Update rolling key register 484 | // 485 | update_vrk( vstate, is ); 486 | 487 | // Find the first decryption block 488 | // 489 | auto [i_rkeyb, rkeyb] = extract_next_rkey_block( vstate, is ); 490 | fassert( i_rkeyb != -1 ); 491 | 492 | // Skip to next handler 493 | // 494 | vstate->next( rkeyb, rva_vip0, ref_point.value() ); 495 | 496 | // Return the content pushed on stack in order 497 | // 498 | return { stack, rva_vip0 }; 499 | } 500 | 501 | // Parses the VMEXIT subroutine and extracts the order registers are pop'd from the stack. 502 | // 503 | std::vector parse_vmexit( vm_state* vstate, const instruction_stream& is ) 504 | { 505 | // Resolve popped registers 506 | // 507 | std::vector stack; 508 | for ( int i = 0;; i++ ) 509 | { 510 | // If POP R64 511 | if ( is[ i ].is( X86_INS_POP, { X86_OP_REG } ) ) 512 | stack.push_back( is[ i ].operands[ 0 ].reg ); 513 | // If POPFW 514 | if ( is[ i ].is( X86_INS_POPFQ, {} ) ) 515 | stack.push_back( vtil::REG_FLAGS ); 516 | // End of pushed registers, reset stream 517 | if ( is[ i ].is( X86_INS_RET, {} ) ) 518 | return stack; 519 | } 520 | unreachable(); 521 | } 522 | 523 | // Parses swapping vm of context/registers, returns newly extracted rkey blocks 524 | // 525 | std::vector parse_vmswap( vm_state* vstate, instruction_stream& is, instruction_stream& prefix_out ) 526 | { 527 | // ################################################# 528 | // All of the constant VM registers will be mutated 529 | // - [VIP => VSP => VRK] in order 530 | // ################################################# 531 | 532 | // Make sure this instruction stream abides by our current pattern 533 | // 534 | auto& ins_read_vsp = is[ 0 ]; 535 | if ( ins_read_vsp.is( X86_INS_MOV, { X86_OP_REG, X86_OP_MEM } ) ) 536 | { 537 | x86_reg vip_from = ins_read_vsp.operands[ 0 ].reg; 538 | 539 | // Find the mutation end point 540 | // 541 | int i_mut_end = is.next( X86_INS_MOVABS, { X86_OP_REG, X86_OP_IMM } ); 542 | if ( i_mut_end != -1 ) 543 | { 544 | // Map all registers and resolve their final value 545 | // 546 | std::map> register_mappings; 547 | for ( int i = 0; i < X86_REG_ENDING; i++ ) 548 | { 549 | x86_reg r = vtil::amd64::registers.extend( ( x86_reg ) i ); 550 | register_mappings[ r ] = { 0, r }; 551 | } 552 | 553 | for ( int i = 1; i < i_mut_end; i++ ) 554 | { 555 | auto& ins = is[ i ]; 556 | 557 | // Make sure it matches our instruction type and extract registers 558 | // 559 | if ( ins.operands.size() != 2 ) continue; 560 | if ( ins.operands[ 0 ].size != 8 ) continue; 561 | 562 | if ( ins.is( X86_INS_MOV, { X86_OP_REG, X86_OP_REG } ) ) 563 | { 564 | x86_reg r1 = ins.operands[ 0 ].reg; 565 | x86_reg r2 = ins.operands[ 1 ].reg; 566 | register_mappings[ r1 ] = { i, register_mappings[ r2 ].second }; 567 | } 568 | else if ( ins.is( X86_INS_XCHG, { X86_OP_REG, X86_OP_REG } ) ) 569 | { 570 | x86_reg r1 = ins.operands[ 0 ].reg; 571 | x86_reg r2 = ins.operands[ 1 ].reg; 572 | std::swap( register_mappings[ r1 ].second, register_mappings[ r2 ].second ); 573 | register_mappings[ r1 ].first = i; 574 | register_mappings[ r2 ].first = i; 575 | } 576 | } 577 | 578 | // Resolve all inheritance 579 | // 580 | auto inherits_from = [ & ] ( x86_reg reg ) 581 | { 582 | std::vector> inheritance; 583 | 584 | for ( auto& pair : register_mappings ) 585 | { 586 | if ( pair.second.first != 0 && 587 | pair.second.second == reg ) 588 | inheritance.push_back( { pair.second.first, pair.first } ); 589 | } 590 | 591 | std::sort( inheritance.begin(), inheritance.end() ); 592 | return inheritance; 593 | }; 594 | 595 | // Assert the pattern is abided by and map the new registers 596 | // 597 | auto vip_inh = inherits_from( vip_from ); 598 | auto vsp_inh = inherits_from( vstate->reg_vsp ); 599 | if ( vip_inh.size() == 1 ) vip_inh.insert( vip_inh.begin(), { 0, vip_from } ); 600 | fassert( vip_inh.size() >= 2 ); 601 | 602 | // Reduce the chunk before mutating the VM parameters 603 | // 604 | int pfx_end = vip_inh[ 0 ].first; 605 | if ( vsp_inh.size() ) pfx_end = std::max( pfx_end, vsp_inh.back().first ); 606 | if ( pfx_end == 0 && vip_inh.size() >= 2 ) pfx_end = vip_inh[ 1 ].first; 607 | 608 | prefix_out.stream = { is.stream.begin(), is.stream.begin() + pfx_end }; 609 | reduce_chunk( vstate, prefix_out, {}, false ); 610 | 611 | // Strip any assigning pre-mutation 612 | // 613 | for ( int i = prefix_out.size() - 1; i >= 0; i-- ) 614 | { 615 | if ( prefix_out[ i ].is( X86_INS_MOV, { X86_OP_REG, X86_OP_REG } ) || 616 | prefix_out[ i ].is( X86_INS_XCHG, { X86_OP_REG, X86_OP_REG } ) ) 617 | prefix_out.stream.erase( prefix_out.stream.begin() + i ); 618 | } 619 | 620 | // Assign the new registers 621 | // 622 | vstate->reg_vip = vip_inh[ 0 ].second; 623 | vstate->reg_vsp = vsp_inh.empty() ? vstate->reg_vsp : vsp_inh.back().second; 624 | vstate->reg_vrk = vip_inh[ 1 ].second; 625 | 626 | // Update instruction stream direction 627 | update_vip_direction( vstate, is ); 628 | 629 | // Erase all instruction prior to the mutation so the 630 | // IL conversion does not get confused 631 | // 632 | is.erase( i_mut_end ); 633 | } 634 | } 635 | 636 | return extract_rkey_blocks( vstate, is ); 637 | } 638 | }; 639 | -------------------------------------------------------------------------------- /NoVmp/vmprotect/subroutines.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Can Boluk 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | #pragma once 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "vm_state.hpp" 24 | #include "deobfuscator.hpp" 25 | #include "../emulator/emulator.hpp" 26 | #include "../emulator/rwx_allocator.hpp" 27 | #include "rkey.hpp" 28 | 29 | namespace vmp 30 | { 31 | // Reduces the given virtualized instruction handler to the base 32 | // (AKA Deobfuscation + I/O based Register tracing) 33 | // 34 | void reduce_chunk( vm_state* vstate, instruction_stream& is, const std::vector>& parameters, bool has_next = true ); 35 | 36 | // Deduces the virtual register key from the given instruction stream 37 | // 38 | void update_vrk( vm_state* state, const instruction_stream& is ); 39 | 40 | // Deduces the virtual instruction stream direction from the given instruction stream 41 | // 42 | void update_vip_direction( vm_state* state, const instruction_stream& is ); 43 | 44 | // Finds the self-reference point from the given instruction stream if relevant 45 | // 46 | std::optional find_self_ref( vm_state* state, const instruction_stream& is, int index = 0 ); 47 | 48 | // Parses VMENTER subroutine and extracts the vm information, entry point of the 49 | // virtualized routine, rolling key 0 value, and describes the push order of registers. 50 | // - Pushing reloc at last is left to the caller. 51 | // 52 | std::pair, vtil::vip_t> parse_vmenter( vm_state* vstate, uint32_t rva_ep ); 53 | 54 | // Parses the VMEXIT subroutine and extracts the order registers are pop'd from the stack. 55 | // 56 | std::vector parse_vmexit( vm_state* vstate, const instruction_stream& is ); 57 | 58 | // Parses swapping vm of context/registers, returns newly extracted rkey blocks 59 | // 60 | std::vector parse_vmswap( vm_state* vstate, instruction_stream& is, instruction_stream& prefix_out ); 61 | }; -------------------------------------------------------------------------------- /NoVmp/vmprotect/vm_state.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Can Boluk 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | #pragma once 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "deobfuscator.hpp" 24 | #include "image_desc.hpp" 25 | 26 | namespace vmp 27 | { 28 | using rkey_t = uint64_t; 29 | 30 | struct rkey_value 31 | { 32 | // Value of the constant. 33 | // 34 | union 35 | { 36 | uint64_t u64; 37 | uint32_t u32; 38 | uint16_t u16; 39 | uint8_t u8; 40 | 41 | int64_t i64; 42 | int32_t i32; 43 | int16_t i16; 44 | int8_t i8; 45 | }; 46 | 47 | // Size of this parameter. 48 | // 49 | uint32_t size; 50 | 51 | // Some helpers to extend from original size. 52 | // 53 | int64_t get_signed() 54 | { 55 | switch ( size ) 56 | { 57 | case 8: return i64; 58 | case 4: return i32; 59 | case 2: return i16; 60 | case 1: return i8; 61 | default: unreachable(); 62 | } 63 | } 64 | 65 | uint64_t get() 66 | { 67 | switch ( size ) 68 | { 69 | case 8: return u64; 70 | case 4: return u32; 71 | case 2: return u16; 72 | case 1: return u8; 73 | default: unreachable(); 74 | } 75 | } 76 | }; 77 | 78 | struct rkey_block 79 | { 80 | // After the parameter is decrypted it's written into this register 81 | // 82 | x86_reg output_register = X86_REG_INVALID; 83 | 84 | // Register of the associated rolling key 85 | // 86 | x86_reg rolling_key_register = X86_REG_INVALID; 87 | 88 | // Boundaries of the block within the input stream 89 | // 90 | std::pair block_start; 91 | std::pair block_end; 92 | 93 | // Size of the data 94 | // 95 | size_t output_size = ~0ull; 96 | 97 | // Auto-resolved decryption function and the simulation context associated with it 98 | // 99 | std::function( void* src, rkey_t k0 )> decrypt; 100 | }; 101 | 102 | struct vm_state 103 | { 104 | // The associated image 105 | // 106 | image_desc* img = nullptr; 107 | 108 | // RVA of the current handler 109 | // 110 | uint32_t current_handler_rva = 0; 111 | 112 | // RVA of the current point in virtual instruction stream 113 | // 114 | vtil::vip_t vip = 0; 115 | 116 | // Register that holds the virtual instruction pointer 117 | // 118 | x86_reg reg_vip = X86_REG_INVALID; 119 | 120 | // Register that holds the virtual stack pointer 121 | // 122 | x86_reg reg_vsp = X86_REG_INVALID; 123 | 124 | // Register that holds the virtual machine rolling key 125 | // 126 | x86_reg reg_vrk = X86_REG_INVALID; 127 | 128 | // Direction of the virtual machine instruction stream 129 | // 130 | int8_t dir_vip = 0; 131 | 132 | // Rolling key 133 | // 134 | rkey_t rolling_key = 0; 135 | 136 | // Unrolls all instructions for the current handler 137 | // 138 | instruction_stream unroll() 139 | { 140 | return deobfuscate( img, current_handler_rva ); 141 | } 142 | 143 | // Peeks at the virtual instruction stream without forwarding it 144 | // 145 | uint8_t* peek_vip( size_t num_bytes = 0 ) 146 | { 147 | // If inverse stream, we substract the number of bytes being read first 148 | if ( dir_vip == -1 ) 149 | return img->rva_to_ptr( vip - num_bytes ); 150 | 151 | // Otherwise we use the current RVA 152 | else if ( dir_vip == +1 ) 153 | return img->rva_to_ptr( vip ); 154 | 155 | // Cannot execute this operation when direction is unknown 156 | unreachable(); 157 | return nullptr; 158 | } 159 | 160 | // References the N bytes from the virtual instruction stream and skips them 161 | // 162 | uint8_t* read_vip( size_t num_bytes ) 163 | { 164 | // Peek at the stream 165 | uint8_t* ret = peek_vip( num_bytes ); 166 | 167 | // If invalid, throw 168 | if ( !ret ) throw std::runtime_error( "Invalid VIP." ); 169 | 170 | // Skip the bytes 171 | vip += num_bytes * dir_vip; 172 | 173 | // Return the output 174 | return ret; 175 | } 176 | 177 | // Reads encrypted value from the instruction stream 178 | // 179 | rkey_value decrypt_vip( rkey_block& block, size_t num_bytes = -1 ) 180 | { 181 | // Assert the sanity of the expected i/o size given the decryption block 182 | if ( num_bytes == -1 ) 183 | num_bytes = block.output_size; 184 | else 185 | fassert( num_bytes == block.output_size ); 186 | 187 | // Decrypt next 4 bytes in the instruction stream 188 | auto [value, rolling_key1] = block.decrypt( read_vip( num_bytes ), rolling_key ); 189 | 190 | // Update the rolling key 191 | rolling_key = rolling_key1; 192 | 193 | // Return the value 194 | return value; 195 | } 196 | 197 | // Skips to next instruction (when no self-reference point is given) [Serial instructions] 198 | // Last decrypted parameter should be passed 199 | // 200 | void next( rkey_value delta_rva_v ) 201 | { 202 | // Offset decryption blocks are always 4 bytes, int32_t 203 | fassert( delta_rva_v.size == 4 ); 204 | 205 | // Calculate the new handler RVA based on the decrypted offset and the self reference point 206 | current_handler_rva += delta_rva_v.i32; 207 | } 208 | 209 | // Skips to next instruction (when self-reference point is provided) [VMENTER, VMMUTATE, Branching instructions] 210 | // 211 | void next( rkey_block& off_dec_block, uint32_t new_vip, uint32_t self_ref_rva ) 212 | { 213 | // Offset decryption blocks are always 4 bytes, int32_t 214 | fassert( off_dec_block.output_size == 4 ); 215 | 216 | // Set VIP RVA as is 217 | vip = new_vip; 218 | 219 | // Calculate the new rolling key 220 | rolling_key = new_vip + img->get_real_image_base(); 221 | 222 | // Calculate the new handler RVA based on the decrypted offset and the self reference point 223 | current_handler_rva = self_ref_rva + decrypt_vip( off_dec_block, 4 ).i32; 224 | } 225 | }; 226 | }; -------------------------------------------------------------------------------- /NoVmp/vmprotect/vtil_lifter.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Can Boluk 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | #include "vtil_lifter.hpp" 17 | #include 18 | #include "rkey.hpp" 19 | #include "subroutines.hpp" 20 | #include "deobfuscator.hpp" 21 | #include "architecture.hpp" 22 | #include "il2vtil.hpp" 23 | 24 | #define DISCOVERY_VERBOSE_OUTPUT 0 25 | 26 | namespace vmp 27 | { 28 | vtil::basic_block* lift_il( vtil::basic_block* block, vm_state* vstate ) 29 | { 30 | const auto fix_constant_pool = [ & ] () 31 | { 32 | // Fix stack access to avoid trace cost. 33 | // 34 | vtil::optimizer::stack_pinning_pass{}( block ); 35 | vtil::optimizer::istack_ref_substitution_pass{}( block ); 36 | 37 | // Reloc base is always at the top of virtual stack upon block begin, 38 | // create the base-address offset evaluator using it. 39 | // 40 | vtil::symbolic::pointer reloc_base = 41 | vtil::symbolic::variable{ block->begin(), vtil::REG_SP }.to_expression(); 42 | auto eval = [ & ] ( const vtil::symbolic::unique_identifier& uid ) 43 | -> std::optional 44 | { 45 | auto& var = uid.get(); 46 | if ( var.is_register() && var.reg().is_image_base() ) 47 | return 0; 48 | if ( var.is_memory() && ( var.mem().base - reloc_base ) == 0 ) 49 | return ( uint64_t ) -( int64_t ) vstate->img->get_real_image_base(); 50 | return std::nullopt; 51 | }; 52 | 53 | // For each LDD: 54 | // 55 | vtil::cached_tracer tracer{}; 56 | for ( auto it = block->begin(); it != block->end(); it++ ) 57 | { 58 | if ( it->base != &vtil::ins::ldd ) 59 | continue; 60 | auto [base, off] = it->memory_location(); 61 | if ( base.is_stack_pointer() ) 62 | continue; 63 | 64 | // If it evaluates to constant delta from base: 65 | // 66 | if ( auto res = tracer( { it, base } )->evaluate( eval ); res.is_known() ) 67 | { 68 | uint64_t rva = *res.get() + off; 69 | if ( !vstate->img->has_relocs ) rva -= vstate->img->get_real_image_base(); 70 | 71 | // If in a read-only section: 72 | // 73 | if ( auto section = vstate->img->rva_to_section( rva ) ) 74 | { 75 | if ( !section->characteristics.mem_write ) 76 | { 77 | // Replace with MOV. 78 | // 79 | uint64_t value = 0; 80 | 81 | memcpy( 82 | &value, 83 | vstate->img->rva_to_ptr( rva ), 84 | it->access_size() / 8 85 | ); 86 | 87 | ( +it )->base = &vtil::ins::mov; 88 | ( +it )->operands = { it->operands[ 0 ], vtil::operand{ value, it->access_size() } }; 89 | } 90 | } 91 | } 92 | } 93 | }; 94 | 95 | 96 | // If virtual instruction pointer is not set: 97 | // 98 | if ( !vstate->vip ) 99 | { 100 | // Parse VMENTER: 101 | // 102 | auto [entry_stack, entry_vip] = parse_vmenter( vstate, vstate->current_handler_rva ); 103 | auto blk_caller = block; 104 | entry_vip += ( vstate->dir_vip < 0 ? -1 : 0 ); 105 | 106 | // Begin block if none passed. 107 | // 108 | if ( !block ) 109 | { 110 | block = vtil::basic_block::begin( entry_vip ); 111 | } 112 | // Otherwise, fork the block. 113 | // 114 | else 115 | { 116 | auto new_block = block->fork( entry_vip ); 117 | // If returned nullptr, it's already explored, skip. 118 | // 119 | if ( !new_block ) 120 | { 121 | std::lock_guard g( block->owner->mutex ); 122 | block = block->owner->explored_blocks[ entry_vip ]; 123 | fassert( block ); 124 | // TODO: Trace possible exits once more ?. 125 | // 126 | return block; 127 | } 128 | block = new_block; 129 | } 130 | 131 | // Insert push instructions. 132 | // 133 | for ( auto& op : entry_stack ) 134 | block->push( op ); 135 | 136 | // Push relocation offset. 137 | // 138 | auto treloc = block->tmp( 64 ); 139 | block->mov( treloc, vtil::REG_IMGBASE ) 140 | ->sub( treloc, vstate->img->get_real_image_base() ) 141 | ->push( treloc ); 142 | } 143 | else 144 | { 145 | // If passed block is nullptr, it's already explored, skip. 146 | // 147 | if ( !block ) return nullptr; 148 | } 149 | 150 | while ( 1 ) 151 | { 152 | if ( !vstate->img->rva_to_section( vstate->current_handler_rva ) ) 153 | { 154 | // TODO: Whoooops. 155 | // 156 | vtil::debug::dump( block->prev[ 0 ] ); 157 | throw std::runtime_error( "Whoooops invalid virtual jump." ); 158 | } 159 | 160 | // Unroll the stream 161 | // 162 | instruction_stream is = vstate->unroll(); 163 | 164 | // Resolve self-referencing point and decryption blocks 165 | // 166 | std::optional self_ref_point = find_self_ref( vstate, is ); 167 | std::vector rkblocks = extract_rkey_blocks( vstate, is ); 168 | 169 | // If handler has a self referencing point (X: LEA r64, [X]), handle VM swap 170 | // 171 | instruction_stream prefixss; 172 | if ( self_ref_point.has_value() ) 173 | { 174 | // Parse the new VM state 175 | // 176 | rkblocks = parse_vmswap( vstate, is, prefixss ); 177 | } 178 | 179 | // If instruction has no decryption blocks, handle VM exit / external call 180 | // 181 | if ( rkblocks.empty() ) 182 | { 183 | const auto is_rva_in_vmp_scn = [ & ] ( uint32_t rva ) 184 | { 185 | auto section = vstate->img->rva_to_section( rva ); 186 | if ( !section ) return false; 187 | 188 | if ( vstate->img->rva_to_section( rva ) == 189 | vstate->img->rva_to_section( vstate->current_handler_rva ) ) 190 | return true; 191 | 192 | for ( const auto& prefix : section_prefixes ) 193 | if ( !memcmp( prefix.data(), §ion->name.short_name[0], prefix.size() ) ) 194 | return true; 195 | 196 | return false; 197 | }; 198 | 199 | // Parse VMEXIT to resolve the order registers are popped 200 | // 201 | std::vector exit_stack = parse_vmexit( vstate, is ); 202 | 203 | // Simulate the VPOP for each register being popped in the routine 204 | // 205 | for ( auto& op : exit_stack ) 206 | block->pop( op ); 207 | 208 | // Pop target from stack. 209 | // 210 | vtil::operand jmp_dest = block->tmp( 64 ); 211 | block->pop( jmp_dest ); 212 | 213 | // Insert vexit to the location. 214 | // 215 | block->vexit( jmp_dest ); 216 | 217 | // Remove constant obfuscation. 218 | // 219 | if ( vstate->img->strip_constant_obfuscation ) 220 | fix_constant_pool(); 221 | 222 | // Pass the current block through optimization. 223 | // 224 | //block->owner->local_opt_count += vtil::optimizer::apply_all( block ); // OPTIMIZER 225 | jmp_dest = block->back().operands[ 0 ]; 226 | 227 | // Determine current stack offset. 228 | // 229 | vtil::tracer tracer; 230 | auto stack_0 = vtil::symbolic::variable{ block->owner->entry_point->begin(), vtil::REG_SP }.to_expression(); 231 | auto stack_1 = tracer.rtrace_p( { std::prev( block->end() ), vtil::REG_SP } ) + block->sp_offset; 232 | auto offset = stack_1 - stack_0; 233 | #if DISCOVERY_VERBOSE_OUTPUT 234 | log( "sp offset => %s\n", offset.to_string() ); 235 | #endif 236 | 237 | // If stack offset is non-const or [Offset < 0]: 238 | // 239 | if ( !offset.is_constant() || *offset.get() < 0 ) 240 | { 241 | // Try to read from the top of the stack. 242 | // 243 | auto continue_from = ( tracer.rtrace_p( { std::prev( block->end() ), 244 | { tracer( { std::prev( block->end() ), vtil::REG_SP } ) + block->sp_offset, 64 } } ) - 245 | (vstate->img->has_relocs ? vtil::symbolic::variable{ {}, vtil::REG_IMGBASE }.to_expression() : vtil::symbolic::expression{ vstate->img->get_real_image_base() })).simplify( true ); 246 | #if DISCOVERY_VERBOSE_OUTPUT 247 | log( "continue => %s\n", continue_from.to_string() ); 248 | #endif 249 | // If constant and is in VMP section: 250 | // 251 | if ( continue_from.is_constant() && is_rva_in_vmp_scn( *continue_from.get() ) ) 252 | { 253 | // If return address points to a PUSH IMM32, aka VMENTER. 254 | // 255 | auto disasm = deobfuscate( vstate->img, *continue_from.get() ); 256 | if ( disasm.size() && disasm[ 0 ].is( X86_INS_PUSH, { X86_OP_IMM } ) ) 257 | { 258 | // Convert into vxcall and indicate that push is implicit by 259 | // shifting the stack pointer. 260 | // 261 | block->wback().base = &vtil::ins::vxcall; 262 | block->wback().vip = vstate->vip; 263 | block->shift_sp( 8, false, block->end() ); 264 | 265 | /* TODO: 266 | // Check if it's alloca_probe inserted by the compiler, in which case we can ignore 267 | // and should be preserving RAX 268 | // 269 | if ( ret_rva ) 270 | { 271 | // Try to identify ALLOCA probe 272 | // 273 | instruction_stream is = unroll_stream( vstate->img, ret_rva ); 274 | if ( is.size() && 275 | is[ 0 ].is( X86_INS_SUB, { X86_OP_REG, X86_OP_IMM } ) && 276 | is[ 0 ].operands[ 1 ].imm == 0x10 ) 277 | { 278 | // Try to evaluate EAX into a constant 279 | // 280 | mlil::symbol_map sym_rax; 281 | expression_old::instance exp_rax = mlf->trace( mlil::operand{ X86_REG_RAX }, &sym_rax ).value; 282 | std::optional val_rax; 283 | if ( exp_rax ) 284 | { 285 | if ( auto res = expression_old::simplify( exp_rax )->evaluate() ) 286 | val_rax = res.value().u64; 287 | } 288 | 289 | // If rax does indeed evaluate to a constant value 290 | // 291 | if ( val_rax.has_value() ) 292 | { 293 | // Skip the VXCALL that would be appended to the stream and go straight to the parsing of VMENTER 294 | // 295 | for ( x86_reg reg : parse_vmenter( vstate, xret_rva ) ) 296 | { 297 | // Declare RAX preserved 298 | // 299 | if ( reg == X86_REG_RAX ) 300 | { 301 | mlf->mov( mlil::operand{ X86_REG_RAX }, mlil::make_const( val_rax.value() ) ); 302 | mlf->push( mlil::make_const( val_rax.value() ) ); 303 | } 304 | else 305 | { 306 | mlf->push( mlil::operand( reg ) ); 307 | } 308 | } 309 | 310 | // Continue parsing as usual 311 | // 312 | io::log( "Processed vmexit-vmenter chain [_alloca_probe()]...\n" ); 313 | continue; 314 | } 315 | } 316 | } 317 | */ 318 | 319 | // Continue lifting from the linked virtual machine. 320 | // 321 | vm_state state = { vstate->img, *continue_from.get() }; 322 | lift_il( block, &state ); 323 | break; 324 | } 325 | } 326 | } 327 | 328 | // Reached real exit, determine destination. 329 | // 330 | auto exit_destination = jmp_dest.is_immediate() 331 | ? vtil::symbolic::expression{ jmp_dest.imm().u64, jmp_dest.bit_count() } 332 | : (tracer.rtrace_p({ std::prev(block->end()), jmp_dest.reg() }) - (vstate->img->has_relocs ? vtil::symbolic::variable{ {}, vtil::REG_IMGBASE }.to_expression() : vtil::symbolic::expression{ vstate->img->get_real_image_base() })).simplify(true); 333 | #if DISCOVERY_VERBOSE_OUTPUT 334 | log( "exit => %s\n", exit_destination.to_string() ); 335 | #endif 336 | 337 | // If constant and is in VMP section: 338 | // 339 | if ( exit_destination.is_constant() && is_rva_in_vmp_scn( *exit_destination.get() ) ) 340 | { 341 | // Pop the VMEXIT. 342 | // 343 | block->pop_back(); 344 | 345 | // Extract the non-virtualized chunk before VMENTER. 346 | // 347 | auto is = deobfuscate( vstate->img, *exit_destination.get() ); 348 | 349 | instruction_stream non_virt_chunk = {}; 350 | while ( true ) 351 | { 352 | if ( is.size() <= 1 ) 353 | { 354 | block->vexit( *exit_destination.get() ); 355 | return block; 356 | } 357 | 358 | if ( is[ 0 ].is( X86_INS_PUSH, { X86_OP_IMM } ) && 359 | is[ 1 ].is( X86_INS_CALL, { X86_OP_IMM } ) ) 360 | break; 361 | else 362 | non_virt_chunk.stream.push_back( is.stream[ 0 ] ); 363 | is.erase( 1 ); 364 | } 365 | 366 | // Insert VPINR->VEMIT->VPINW stream. 367 | // 368 | for ( auto& [id, ins] : non_virt_chunk.stream ) 369 | { 370 | if ( ins.is( X86_INS_PUSHFQ, {} ) ) 371 | { 372 | block->pushf(); 373 | continue; 374 | } 375 | else if ( ins.is( X86_INS_POPFQ, {} ) ) 376 | { 377 | block->popf(); 378 | continue; 379 | } 380 | 381 | for ( auto reg_read : ins.regs_read ) 382 | { 383 | vtil::operand op = x86_reg( reg_read ); 384 | if ( reg_read == X86_REG_RSP ) op = { vtil::REG_SP }; 385 | if ( reg_read == X86_REG_EFLAGS ) op = { vtil::REG_FLAGS }; 386 | block->vpinr( op ); 387 | } 388 | 389 | for ( uint8_t byte : ins.bytes ) 390 | block->vemit( byte ); 391 | 392 | for ( auto reg_write : ins.regs_write ) 393 | { 394 | vtil::operand op = x86_reg( reg_write ); 395 | fassert( reg_write != X86_REG_RSP ); 396 | if ( reg_write == X86_REG_EFLAGS ) op = { vtil::REG_FLAGS }; 397 | block->vpinw( op ); 398 | } 399 | } 400 | 401 | // Insert a dummy JMP. 402 | // 403 | block->jmp( vtil::invalid_vip ); 404 | 405 | // Continue lifting from the linked virtual machine. 406 | // 407 | vm_state state = { vstate->img, is[ 0 ].address }; 408 | auto block_next = lift_il( block, &state ); 409 | 410 | // Replace the destination of the jump. 411 | // 412 | block->wback().operands[ 0 ] = { block_next->entry_vip, 64 }; 413 | break; 414 | } 415 | 416 | // Break out of the lifter loop. 417 | // 418 | break; 419 | } 420 | 421 | // Assert we have valid decryption blocks 422 | // 423 | fassert( !rkblocks.empty() ); 424 | fassert( rkblocks.back().output_size == 4 ); 425 | 426 | // Decrypt all parameters for the instruction 427 | // 428 | vtil::vip_t vip_params = vstate->vip; 429 | std::vector> parameters; 430 | for ( rkey_block& rkblock : rkblocks ) 431 | parameters.push_back( { &rkblock, vstate->decrypt_vip( rkblock ) } ); 432 | 433 | // Reduce the instruction handler chunk, classify the IL instruction associated 434 | // 435 | reduce_chunk( vstate, is, parameters ); 436 | is.stream.insert( is.stream.begin(), prefixss.stream.begin(), prefixss.stream.end() ); 437 | arch::instruction il_instruction = arch::classify( vstate, is ); 438 | 439 | // If there was a self-reference point in the handler 440 | // 441 | if ( self_ref_point.has_value() ) 442 | { 443 | // Handle nop: 444 | // 445 | if( il_instruction.op == "VNOP" ) 446 | { 447 | // Insert jump to VIP+$. 448 | // 449 | vtil::vip_t dst = vip_params + ( vstate->dir_vip < 0 ? -1 : 0 ); 450 | block->jmp( dst ); 451 | 452 | // Remove constant obfuscation. 453 | // 454 | if ( vstate->img->strip_constant_obfuscation ) 455 | fix_constant_pool(); 456 | 457 | // Pass the current block through optimization. 458 | // 459 | //block->owner->local_opt_count += vtil::optimizer::apply_all( block ); // OPTIMIZER 460 | 461 | // Fork the current flow and parse as a seperate block. 462 | // 463 | vstate->next( rkblocks.back(), vip_params, self_ref_point.value() ); 464 | lift_il( block->fork( dst ), vstate ); 465 | return block; 466 | } 467 | else if ( il_instruction.op == "VJMP" ) 468 | { 469 | // Pop target from stack. 470 | // 471 | auto jmp_dest = block->tmp( 64 ); 472 | block->pop( jmp_dest ); 473 | 474 | if ( vstate->dir_vip < 0 ) 475 | block->sub( jmp_dest, 1 ); 476 | 477 | // If relocs stripped, substract image base, uses absolute address. 478 | // 479 | if( !vstate->img->has_relocs ) 480 | block->sub( jmp_dest, vstate->img->get_real_image_base() ); 481 | 482 | // Insert jump to the location. 483 | // 484 | block->jmp( jmp_dest ); 485 | 486 | // Remove constant obfuscation. 487 | // 488 | if ( vstate->img->strip_constant_obfuscation ) 489 | fix_constant_pool(); 490 | 491 | // Pass the current block through optimization. 492 | // 493 | block->owner->local_opt_count += vtil::optimizer::apply_all( block ); // OPTIMIZER 494 | 495 | // Allocate an array of resolved destinations. 496 | // 497 | vtil::/*cached_*/tracer tracer = {}; 498 | std::vector destination_list; 499 | uint64_t image_base = vstate->img->has_relocs ? 0 : vstate->img->get_real_image_base(); 500 | auto branch_info = vtil::optimizer::aux::analyze_branch( block, &tracer, { .pack = true } ); 501 | #if DISCOVERY_VERBOSE_OUTPUT 502 | log( "CC: %s\n", branch_info.cc ); 503 | log( "VJMP => %s\n", branch_info.destinations ); 504 | #endif 505 | for ( auto& branch : branch_info.destinations ) 506 | { 507 | // If not constant: 508 | // 509 | if ( !branch->is_constant() ) 510 | { 511 | // Recursively trace the expression and remove any matches of REG_IMGBASE. 512 | // 513 | branch = tracer.rtrace_pexp( *branch ); 514 | branch.transform( [image_base] ( vtil::symbolic::expression::delegate& ex ) 515 | { 516 | if ( ex->is_variable() ) 517 | { 518 | auto& var = ex->uid.get(); 519 | if ( var.is_register() && var.reg() == vtil::REG_IMGBASE ) 520 | *+ex = { image_base, ex->size() }; 521 | } 522 | } ) 523 | .simplify( true ); 524 | } 525 | 526 | // If not constant: 527 | // 528 | if ( !branch->is_constant() ) 529 | { 530 | // TODO: Handle switch table patterns. 531 | // 532 | log( "VJMP =>\n" ); 533 | for ( auto [branch, idx] : vtil::zip( branch_info.destinations, vtil::iindices ) ) 534 | { 535 | log( "-- %d) %s\n", idx, branch ); 536 | log( ">> %s\n", tracer.rtrace_exp( *branch ) ); 537 | } 538 | log( "CC: %s\n", branch_info.cc ); 539 | //vtil::optimizer::aux::analyze_branch( block, &tracer, false ); 540 | throw std::runtime_error( "Whoooops hit switch case..." ); 541 | } 542 | 543 | destination_list.push_back( *branch->get() ); 544 | } 545 | 546 | // Declare branch helper. 547 | // 548 | const auto explorer = [ & ] ( vtil::vip_t dst ) 549 | { 550 | #if DISCOVERY_VERBOSE_OUTPUT 551 | log( "Exploring branch => %p\n", dst ); 552 | #endif 553 | vm_state vstate_dup = *vstate; 554 | vstate_dup.vip = dst + ( vstate->dir_vip < 0 ? +1 : 0 ); 555 | vstate_dup.next( rkblocks.back(), vstate_dup.vip, self_ref_point.value() ); 556 | lift_il( block->fork( dst ), &vstate_dup ); 557 | }; 558 | 559 | // 560 | // TODO: Fix me. 561 | // There are still some concurrency issues in this code base so 562 | // not using multi-threading for now, optimization is not done here 563 | // so we don't lose much speed anyways. 564 | // 565 | for ( auto& l : destination_list ) 566 | explorer( l ); 567 | 568 | /*// Allocate a thread for each additional destination. 569 | // 570 | std::vector thread_pool; 571 | fassert( destination_list.size() >= 1 ); 572 | for ( auto dst : vtil::make_range( destination_list.begin() + 1, destination_list.end() ) ) 573 | thread_pool.emplace_back( explorer, dst ); 574 | 575 | // Invoke current helper and wait for each thread, then break out of the loop. 576 | // 577 | explorer( destination_list.front() ); 578 | for ( auto& thread : thread_pool ) 579 | thread.join();*/ 580 | break; 581 | } 582 | unreachable(); 583 | } 584 | 585 | // Translate from VMP Arch to VTIL and continue processing 586 | // 587 | block->label_begin( vip_params ); 588 | translate( block, il_instruction ); 589 | block->label_end(); 590 | 591 | // Skip to next instruction and continue parsing the flow linearly 592 | // 593 | vstate->next( parameters.back().second ); 594 | } 595 | return block; 596 | } 597 | }; -------------------------------------------------------------------------------- /NoVmp/vmprotect/vtil_lifter.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Can Boluk 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see . 15 | // 16 | #pragma once 17 | #include 18 | #include 19 | #include "vm_state.hpp" 20 | 21 | namespace vmp 22 | { 23 | // List of vmp section names, used to chain VMs, detecting re-entry. 24 | // 25 | inline std::vector section_prefixes = { ".vmp" }; 26 | 27 | vtil::basic_block* lift_il( vtil::basic_block* block, vm_state* vstate ); 28 | static vtil::routine* lift_il( vm_state* vstate ) 29 | { 30 | if ( vtil::routine* rtn = lift_il( nullptr, vstate )->owner ) 31 | return rtn; 32 | return nullptr; 33 | } 34 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 | 6 |

NoVmp

7 | 8 |

9 | 10 | appveyor-ci 11 | 12 | 13 | license 14 | 15 |

16 | 17 |

18 | A static devirtualizer for VMProtect x64 3.x powered by VTIL. 19 |

20 |

21 | 22 | ### VMProtect? Nope. 23 | NoVmp is a project devirtualizing VMProtect x64 3.0 - 3.5 (latest) into optimized VTIL and optionally recompiling back to x64 using the [Virtual-machine Translation Intermediate Language](https://github.com/vtil-project/VTIL-Core) library. It is rather experimental and is mostly a PoC I wanted to release. Most things can be improved especially with the new NativeLifters repo, but it did not exist back in the time this was written. 24 | 25 | # Usage 26 | NoVmp accepts **unpacked binaries**, so if your binary is packed you'll have to dump it first, additionally if you did dump it using a tool like Scylla, you'll have to provide the original image base using the `-base` parameter like so: 27 | 28 | `-base 0x14000000` 29 | 30 | By default NoVmp will parse every single jump into a VM, if you are only interested in a number of **specific** virtualized routines you can use the `-vms` parameter like so with relative virtual addresses: 31 | 32 | `-vms 0x729B81 0x72521` 33 | 34 | These addresses should be pointing at the VMEnter, as shown below: 35 | 36 | ![VMEnter](https://raw.githubusercontent.com/can1357/NoVmp/master/assets/vmenter.png) 37 | 38 | By default section discovery is automatic, but in case your calls are not being chained you should try adding the VMProtect section name into the section list using `-sections` as shown below: 39 | 40 | `-sections .xxx0 .yyy0` 41 | 42 | Note that the `.1` section is the merged VMProtect DLL which should not be inputted. 43 | 44 | Additionally you can use any of the following switches: 45 | - `-noopt`: Disables optimization. 46 | - `-opt:constant`: Optimizes the VMProtect Ultra constant obfuscation out. 47 | - `-experimental:recompile`: Enables the experimental x64 compiler. 48 | 49 | # Known bugs 50 | - Known issues from VTIL-Core, mainly the lack of jump table support and propagation passes taking too long/not being great which are being worked on. 51 | - Binaries compiled with relocations stripped are not fully supported yet. 52 | - Experimental compiler is a borderline broken demo, issues related to it should not be submitted as it'll be reworked and will be in VTIL-Core. 53 | 54 | # License 55 | NoVmp is licensed under the GNU General Public License v3. 56 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | image: Visual Studio 2019 3 | build_script: 4 | - cmd: git submodule update --init --recursive 5 | - cmd: mkdir build && cd build 6 | - cmd: cmake .. 7 | - cmd: cmake --build . --config Release 8 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tai7sy/NoVmp/6c23c9a335f70e8d5ed6299668fd802f2314c896/assets/logo.png -------------------------------------------------------------------------------- /assets/vmenter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tai7sy/NoVmp/6c23c9a335f70e8d5ed6299668fd802f2314c896/assets/vmenter.png --------------------------------------------------------------------------------