├── .gitattributes ├── .gitignore ├── Demo ├── Demo.vcxproj ├── Demo.vcxproj.filters ├── main.cpp ├── pch.cpp └── pch.h ├── README.md ├── VMProtect.sln └── VMProtectTest ├── AbstractStream.cpp ├── AbstractStream.hpp ├── CFG.cpp ├── CFG.hpp ├── IR.cpp ├── IR.hpp ├── ProcessStream.cpp ├── ProcessStream.hpp ├── VMHandler.cpp ├── VMHandler.hpp ├── VMProtectAnalyzer.cpp ├── VMProtectAnalyzer.hpp ├── VMProtectTest.vcxproj ├── VMProtectTest.vcxproj.filters ├── VirtualMachine.cpp ├── VirtualMachine.hpp ├── main.cpp ├── pch.cpp ├── pch.h ├── x86_instruction.cpp ├── x86_instruction.hpp ├── x86_operand.cpp ├── x86_operand.hpp ├── x86_register.cpp └── x86_register.hpp /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /Demo/Demo.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {F9A9A341-7EFE-4607-8970-C40A4ACB72EA} 24 | Win32Proj 25 | Demo 26 | 10.0.17763.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v141 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v141 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v141 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | false 75 | 76 | 77 | true 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Use 88 | Level3 89 | MaxSpeed 90 | true 91 | true 92 | true 93 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 94 | true 95 | pch.h 96 | C:\Program Files\VMProtect Demo\Include\C;%(AdditionalIncludeDirectories) 97 | 98 | 99 | Console 100 | true 101 | true 102 | true 103 | C:\Program Files\VMProtect Demo\Lib\Windows;%(AdditionalLibraryDirectories) 104 | 105 | 106 | 107 | 108 | Use 109 | Level3 110 | Disabled 111 | true 112 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 113 | true 114 | pch.h 115 | C:\Program Files\VMProtect Demo\Include\C;%(AdditionalIncludeDirectories) 116 | 117 | 118 | Console 119 | true 120 | C:\Program Files\VMProtect Demo\Lib\Windows;%(AdditionalLibraryDirectories) 121 | 122 | 123 | 124 | 125 | Use 126 | Level3 127 | Disabled 128 | true 129 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 130 | true 131 | pch.h 132 | C:\Program Files\VMProtect Demo\Include\C;%(AdditionalIncludeDirectories) 133 | 134 | 135 | Console 136 | true 137 | C:\Program Files\VMProtect Demo\Lib\Windows;%(AdditionalLibraryDirectories) 138 | 139 | 140 | 141 | 142 | Use 143 | Level3 144 | MaxSpeed 145 | true 146 | true 147 | true 148 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 149 | true 150 | pch.h 151 | C:\Program Files\VMProtect Demo\Include\C;%(AdditionalIncludeDirectories) 152 | 153 | 154 | Console 155 | true 156 | true 157 | true 158 | C:\Program Files\VMProtect Demo\Lib\Windows;%(AdditionalLibraryDirectories) 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | Create 168 | Create 169 | Create 170 | Create 171 | 172 | 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /Demo/Demo.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | ヘッダー ファイル 20 | 21 | 22 | 23 | 24 | ソース ファイル 25 | 26 | 27 | ソース ファイル 28 | 29 | 30 | -------------------------------------------------------------------------------- /Demo/main.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include 4 | #include 5 | 6 | #ifdef __WIN32 7 | __declspec(naked) void sub_eax_1() 8 | { 9 | VMProtectBeginVirtualization(__FUNCTION__); 10 | __asm 11 | { 12 | sub eax, 1; 13 | } 14 | volatile unsigned long long rax = 1; // lol 15 | rax -= 1; 16 | VMProtectEnd(); 17 | } 18 | #else 19 | void sub_eax_1() 20 | { 21 | VMProtectBeginVirtualization(__FUNCTION__); 22 | volatile unsigned long long rax = 1; // lol 23 | rax -= 1; 24 | VMProtectEnd(); 25 | } 26 | #endif 27 | 28 | int main() 29 | { 30 | std::cout << std::hex << sub_eax_1 << std::endl; 31 | return 0; 32 | } -------------------------------------------------------------------------------- /Demo/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: プリコンパイル済みヘッダーに対応するソース ファイル。コンパイルが正常に実行されるために必要です 2 | 3 | #include "pch.h" 4 | 5 | // 一般に、このファイルは無視できますが、プリコンパイル済みヘッダーを使用している場合は保持します。 6 | -------------------------------------------------------------------------------- /Demo/pch.h: -------------------------------------------------------------------------------- 1 | // 作業を開始するためのヒント: 2 | // 1. ソリューション エクスプローラー ウィンドウを使用してファイルを追加/管理します 3 | // 2. チーム エクスプローラー ウィンドウを使用してソース管理に接続します 4 | // 3. 出力ウィンドウを使用して、ビルド出力とその他のメッセージを表示します 5 | // 4. エラー一覧ウィンドウを使用してエラーを表示します 6 | // 5. [プロジェクト] > [新しい項目の追加] と移動して新しいコード ファイルを作成するか、[プロジェクト] > [既存の項目の追加] と移動して既存のコード ファイルをプロジェクトに追加します 7 | // 6. 後ほどこのプロジェクトを再び開く場合、[ファイル] > [開く] > [プロジェクト] と移動して .sln ファイルを選択します 8 | 9 | #ifndef PCH_H 10 | #define PCH_H 11 | 12 | // TODO: ここでプリコンパイルするヘッダーを追加します 13 | 14 | #endif //PCH_H 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VMProtectTest 2 | 3 | **Note**: This is not my code. If I remember correctly this was mirrored from a random public git/svn server. 4 | -------------------------------------------------------------------------------- /VMProtect.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.572 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VMProtectTest", "VMProtectTest\VMProtectTest.vcxproj", "{E3F4A203-B840-4F3D-9580-C8E4378CB468}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Demo", "Demo\Demo.vcxproj", "{F9A9A341-7EFE-4607-8970-C40A4ACB72EA}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {E3F4A203-B840-4F3D-9580-C8E4378CB468}.Debug|x64.ActiveCfg = Debug|x64 19 | {E3F4A203-B840-4F3D-9580-C8E4378CB468}.Debug|x64.Build.0 = Debug|x64 20 | {E3F4A203-B840-4F3D-9580-C8E4378CB468}.Debug|x86.ActiveCfg = Debug|Win32 21 | {E3F4A203-B840-4F3D-9580-C8E4378CB468}.Debug|x86.Build.0 = Debug|Win32 22 | {E3F4A203-B840-4F3D-9580-C8E4378CB468}.Release|x64.ActiveCfg = Release|x64 23 | {E3F4A203-B840-4F3D-9580-C8E4378CB468}.Release|x64.Build.0 = Release|x64 24 | {E3F4A203-B840-4F3D-9580-C8E4378CB468}.Release|x86.ActiveCfg = Release|Win32 25 | {E3F4A203-B840-4F3D-9580-C8E4378CB468}.Release|x86.Build.0 = Release|Win32 26 | {F9A9A341-7EFE-4607-8970-C40A4ACB72EA}.Debug|x64.ActiveCfg = Debug|x64 27 | {F9A9A341-7EFE-4607-8970-C40A4ACB72EA}.Debug|x64.Build.0 = Debug|x64 28 | {F9A9A341-7EFE-4607-8970-C40A4ACB72EA}.Debug|x86.ActiveCfg = Debug|Win32 29 | {F9A9A341-7EFE-4607-8970-C40A4ACB72EA}.Debug|x86.Build.0 = Debug|Win32 30 | {F9A9A341-7EFE-4607-8970-C40A4ACB72EA}.Release|x64.ActiveCfg = Release|x64 31 | {F9A9A341-7EFE-4607-8970-C40A4ACB72EA}.Release|x64.Build.0 = Release|x64 32 | {F9A9A341-7EFE-4607-8970-C40A4ACB72EA}.Release|x86.ActiveCfg = Release|Win32 33 | {F9A9A341-7EFE-4607-8970-C40A4ACB72EA}.Release|x86.Build.0 = Release|Win32 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | GlobalSection(ExtensibilityGlobals) = postSolution 39 | SolutionGuid = {E2D8B03F-C602-400E-87BE-4B99B9199F6F} 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /VMProtectTest/AbstractStream.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "AbstractStream.hpp" 4 | 5 | AbstractStream::AbstractStream(bool x86_64) 6 | { 7 | this->m_x86_64 = x86_64; 8 | } 9 | AbstractStream::~AbstractStream() 10 | { 11 | } 12 | 13 | std::shared_ptr AbstractStream::readNext() 14 | { 15 | constexpr unsigned int bufSize = 16; 16 | xed_uint8_t buf[bufSize]; 17 | unsigned long long address = this->pos(); 18 | unsigned int readBytes = this->read(buf, bufSize); 19 | 20 | std::shared_ptr inst = std::make_shared(address); 21 | inst->decode(buf, readBytes, 22 | this->m_x86_64 ? XED_MACHINE_MODE_LONG_64 : XED_MACHINE_MODE_LEGACY_32, 23 | this->m_x86_64 ? XED_ADDRESS_WIDTH_64b : XED_ADDRESS_WIDTH_32b); 24 | 25 | this->seek(inst->get_addr() + inst->get_length()); 26 | return inst; 27 | } -------------------------------------------------------------------------------- /VMProtectTest/AbstractStream.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "x86_instruction.hpp" 4 | 5 | class AbstractStream 6 | { 7 | protected: 8 | bool m_x86_64; 9 | 10 | AbstractStream(bool x86_64 = false); 11 | virtual ~AbstractStream(); 12 | 13 | public: 14 | virtual bool isOpen() const = 0; 15 | virtual void close() = 0; 16 | virtual SIZE_T read(void* buf, SIZE_T size) = 0; 17 | virtual SIZE_T write(const void* buf, SIZE_T size) = 0; 18 | 19 | virtual unsigned long long pos() = 0; 20 | virtual void seek(unsigned long long pos) = 0; 21 | 22 | std::shared_ptr readNext(); 23 | }; -------------------------------------------------------------------------------- /VMProtectTest/CFG.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrexodia/VMProtectTest/aad08e36ce19910b07e779a20e4e3d1b480835f5/VMProtectTest/CFG.cpp -------------------------------------------------------------------------------- /VMProtectTest/CFG.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "AbstractStream.hpp" 4 | 5 | struct BasicBlock 6 | { 7 | // first instruction that starts basic block 8 | unsigned long long leader; 9 | 10 | std::list> instructions; 11 | 12 | // when last instruction can't follow 13 | bool terminator; 14 | 15 | // dead registers when it enters basic block 16 | std::map dead_registers; 17 | xed_uint32_t dead_flags; 18 | 19 | std::shared_ptr next_basic_block, target_basic_block; 20 | }; 21 | 22 | extern std::shared_ptr make_cfg(AbstractStream& stream, unsigned long long address); -------------------------------------------------------------------------------- /VMProtectTest/IR.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "IR.hpp" 4 | 5 | namespace IR 6 | { 7 | int Variable::s_index = 0; 8 | 9 | // 10 | std::string get_size_string(ir_size size) 11 | { 12 | switch (size) 13 | { 14 | case IR::ir_size_b: return "byte"; 15 | case IR::ir_size_w: return "word"; 16 | case IR::ir_size_d: return "dword"; 17 | case IR::ir_size_q: return "qword"; 18 | 19 | default: 20 | throw std::invalid_argument("invalid size"); 21 | } 22 | } 23 | std::string get_segment_string(ir_segment segment) 24 | { 25 | switch (segment) 26 | { 27 | case ir_segment_scratch: return "scratch"; 28 | case ir_segment_cs: return "cs"; 29 | case ir_segment_ds: return "ds"; 30 | case ir_segment_es: return "es"; 31 | case ir_segment_fs: return "fs"; 32 | case ir_segment_gs: return "gs"; 33 | case ir_segment_ss: return "ss"; 34 | case ir_segment_regular: return "regular"; 35 | default: 36 | throw std::invalid_argument("invalid segment"); 37 | } 38 | } 39 | 40 | // Register 41 | Register::Register(triton::uint64 offset) : Expression(expr_register) 42 | { 43 | if (offset > 0xFF) 44 | throw std::runtime_error("offset is bigger than 0xFF"); 45 | 46 | char name[64]; 47 | sprintf_s(name, 64, "VM_REG_%02llX", offset); 48 | this->m_name.assign(name); 49 | this->m_offset = offset; 50 | } 51 | Register::Register(const triton::arch::Register &triton_register) : Expression(expr_register) 52 | { 53 | this->m_name = triton_register.getName(); 54 | this->m_offset = 0; 55 | this->m_register = triton_register; 56 | } 57 | void Register::to_string(std::ostream& stream) const 58 | { 59 | stream << this->get_name(); 60 | } 61 | std::string Register::get_name() const 62 | { 63 | return this->m_name; 64 | } 65 | triton::uint64 Register::get_offset() const 66 | { 67 | return this->m_offset; 68 | } 69 | 70 | 71 | // Memory 72 | Memory::Memory(const std::shared_ptr &expr, ir_segment segment, ir_size size) : Expression(expr_memory) 73 | { 74 | this->m_expr = expr; 75 | this->m_segment = segment; 76 | this->m_size = size; 77 | } 78 | void Memory::to_string(std::ostream& stream) const 79 | { 80 | stream << get_size_string(m_size) << " ptr " << get_segment_string(this->m_segment) << ":[" << this->m_expr << "]"; 81 | } 82 | std::shared_ptr Memory::get_expression() const 83 | { 84 | return this->m_expr; 85 | } 86 | 87 | 88 | // Variable 89 | Variable::Variable(ir_size size) : Expression(expr_variable) 90 | { 91 | this->m_name = "Temp" + std::to_string(++s_index); 92 | this->m_size = size; 93 | } 94 | Variable::Variable(const std::string &name, ir_size size) : Expression(expr_variable) 95 | { 96 | this->m_name = name; 97 | this->m_size = size; 98 | } 99 | void Variable::to_string(std::ostream& stream) const 100 | { 101 | stream << this->get_name() << "(" << this->m_size << "bytes)"; 102 | } 103 | std::string Variable::get_name() const 104 | { 105 | return this->m_name; 106 | } 107 | std::shared_ptr Variable::create_variable(triton::uint32 size) 108 | { 109 | auto temp_variable = std::make_shared((IR::ir_size)size); 110 | return temp_variable; 111 | } 112 | 113 | 114 | // Immediate 115 | Immediate::Immediate(triton::uint64 immediate) : Expression(expr_immediate) 116 | { 117 | this->m_immediate = immediate; 118 | } 119 | void Immediate::to_string(std::ostream& stream) const 120 | { 121 | stream << "0x" << std::hex << this->m_immediate << std::dec; 122 | } 123 | 124 | 125 | // Dereference 126 | Dereference::Dereference(const std::shared_ptr &expr, ir_segment segment, ir_size size) : Expression(expr_deref) 127 | { 128 | this->m_expr = expr; 129 | this->m_segment = segment; 130 | this->m_size = size; 131 | } 132 | std::shared_ptr Dereference::get_expression() const 133 | { 134 | return this->m_expr; 135 | } 136 | void Dereference::set_expression(const std::shared_ptr &expr) 137 | { 138 | this->m_expr = expr; 139 | } 140 | void Dereference::to_string(std::ostream& stream) const 141 | { 142 | // Deref(segment, expr, size) 143 | stream << "Deref(" << get_segment_string(this->m_segment) << ", " 144 | << this->get_expression() << ", " 145 | << get_size_string(this->m_size) << ")"; 146 | } 147 | 148 | 149 | // Assign 150 | Assign::Assign(const std::shared_ptr &left, const std::shared_ptr &right) : Statement(ir_statement_assign) 151 | { 152 | this->m_left = left; 153 | this->m_right = right; 154 | } 155 | void Assign::to_string(std::ostream& stream) const 156 | { 157 | stream << this->get_left() << "=" << this->get_right(); 158 | } 159 | std::shared_ptr Assign::get_left() const 160 | { 161 | return this->m_left; 162 | } 163 | std::shared_ptr Assign::get_right() const 164 | { 165 | return this->m_right; 166 | } 167 | 168 | 169 | // 170 | std::ostream& operator<<(std::ostream& stream, const Expression& expr) 171 | { 172 | expr.to_string(stream); 173 | return stream; 174 | } 175 | std::ostream& operator<<(std::ostream& stream, const Expression* expr) 176 | { 177 | expr->to_string(stream); 178 | return stream; 179 | } 180 | 181 | std::ostream& operator<<(std::ostream& stream, const Statement& expr) 182 | { 183 | expr.to_string(stream); 184 | return stream; 185 | } 186 | std::ostream& operator<<(std::ostream& stream, const Statement* expr) 187 | { 188 | expr->to_string(stream); 189 | return stream; 190 | } 191 | 192 | std::shared_ptr simplify_expression(const std::shared_ptr &expression) 193 | { 194 | if (expression->get_type() == IR::expr_binary_operation) 195 | { 196 | std::shared_ptr binary_op = std::dynamic_pointer_cast(expression); 197 | if (binary_op->get_binary_type() == IR::binary_op_add 198 | || binary_op->get_binary_type() == IR::binary_op_sub 199 | || binary_op->get_binary_type() == IR::binary_op_xor) 200 | { 201 | // (add|sub|xor) X,0 -> X 202 | if (binary_op->get_expression0()->get_type() == IR::expr_immediate 203 | && std::dynamic_pointer_cast(binary_op->get_expression0())->get_value() == 0) 204 | { 205 | return binary_op->get_expression1(); 206 | } 207 | else if (binary_op->get_expression1()->get_type() == IR::expr_immediate 208 | && std::dynamic_pointer_cast(binary_op->get_expression1())->get_value() == 0) 209 | { 210 | return binary_op->get_expression0(); 211 | } 212 | } 213 | 214 | // return 215 | auto parse_binary_expression = [](const std::shared_ptr &binary_op) -> 216 | std::tuple, std::shared_ptr> 217 | { 218 | std::shared_ptr immediate_node; 219 | std::shared_ptr the_other_node; 220 | 221 | auto expr0 = binary_op->get_expression0(); 222 | auto expr1 = binary_op->get_expression1(); 223 | if (expr0->get_type() == IR::expr_immediate) 224 | { 225 | immediate_node = expr0; 226 | the_other_node = expr1; 227 | } 228 | else if (expr1->get_type() == IR::expr_immediate) 229 | { 230 | the_other_node = expr0; 231 | immediate_node = expr1; 232 | } 233 | return std::make_tuple(the_other_node, immediate_node); 234 | }; 235 | 236 | // add(add(x, imm0), imm1) -> add(x, imm0 + imm1) 237 | if (binary_op->get_binary_type() == IR::binary_op_add 238 | || binary_op->get_binary_type() == IR::binary_op_sub) 239 | { 240 | std::shared_ptr possible_binary_expr, imm_node_0; 241 | std::tie(possible_binary_expr, imm_node_0) = parse_binary_expression(binary_op); 242 | if (possible_binary_expr && imm_node_0 && possible_binary_expr->get_type() == IR::expr_binary_operation) 243 | { 244 | std::shared_ptr sub_binary_node = std::dynamic_pointer_cast(possible_binary_expr); 245 | if (sub_binary_node->get_binary_type() == IR::binary_op_add || sub_binary_node->get_binary_type() == IR::binary_op_sub) 246 | { 247 | std::shared_ptr rest_of_node, imm_node_1; 248 | std::tie(rest_of_node, imm_node_1) = parse_binary_expression(sub_binary_node); 249 | if (rest_of_node && imm_node_1) 250 | { 251 | triton::sint64 value0 = std::dynamic_pointer_cast(imm_node_0)->get_value(); 252 | if (binary_op->get_binary_type() == IR::binary_op_sub) 253 | value0 = -value0; 254 | 255 | triton::sint64 value1 = std::dynamic_pointer_cast(imm_node_1)->get_value(); 256 | if (sub_binary_node->get_binary_type() == IR::binary_op_sub) 257 | value1 = -value1; 258 | 259 | return std::make_shared(rest_of_node, std::make_shared(value0 + value1)); 260 | } 261 | } 262 | } 263 | } 264 | else if (binary_op->get_binary_type() == IR::binary_op_xor) 265 | { 266 | std::shared_ptr possible_binary_expr, imm_node_0; 267 | std::tie(possible_binary_expr, imm_node_0) = parse_binary_expression(binary_op); 268 | if (possible_binary_expr && imm_node_0 && possible_binary_expr->get_type() == IR::expr_binary_operation) 269 | { 270 | std::shared_ptr sub_binary_node = std::dynamic_pointer_cast(possible_binary_expr); 271 | if (sub_binary_node->get_binary_type() == IR::binary_op_xor) 272 | { 273 | std::shared_ptr rest_of_node, imm_node_1; 274 | std::tie(rest_of_node, imm_node_1) = parse_binary_expression(sub_binary_node); 275 | if (rest_of_node && imm_node_1) 276 | { 277 | triton::sint64 value0 = std::dynamic_pointer_cast(imm_node_0)->get_value(); 278 | triton::sint64 value1 = std::dynamic_pointer_cast(imm_node_1)->get_value(); 279 | return std::make_shared(rest_of_node, std::make_shared(value0 ^ value1)); 280 | } 281 | } 282 | } 283 | } 284 | 285 | // simplify 286 | std::shared_ptr simplified_expr0 = simplify_expression(binary_op->get_expression0()); 287 | std::shared_ptr simplified_expr1 = simplify_expression(binary_op->get_expression1()); 288 | binary_op->set_expression0(simplified_expr0); 289 | binary_op->set_expression1(simplified_expr1); 290 | } 291 | return expression; 292 | } 293 | } -------------------------------------------------------------------------------- /VMProtectTest/IR.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace IR 4 | { 5 | enum ir_size : triton::uint32 6 | { 7 | ir_size_b = 1, 8 | ir_size_w = 2, 9 | ir_size_d = 4, 10 | ir_size_q = 8 11 | }; 12 | 13 | enum ir_segment 14 | { 15 | ir_segment_regular = triton::arch::ID_REG_INVALID, // ds? 16 | ir_segment_scratch, 17 | ir_segment_cs = triton::arch::ID_REG_X86_CS, 18 | ir_segment_ds = triton::arch::ID_REG_X86_DS, 19 | ir_segment_es = triton::arch::ID_REG_X86_ES, 20 | ir_segment_fs = triton::arch::ID_REG_X86_FS, 21 | ir_segment_gs = triton::arch::ID_REG_X86_GS, 22 | ir_segment_ss = triton::arch::ID_REG_X86_SS, 23 | }; 24 | 25 | enum expression_type 26 | { 27 | expr_register, 28 | expr_memory, 29 | expr_variable, 30 | expr_immediate, 31 | expr_deref, 32 | expr_unary_operation, 33 | expr_binary_operation 34 | }; 35 | 36 | enum statement_type 37 | { 38 | ir_statement_assign, 39 | ir_statement_push, 40 | ir_statement_pop, 41 | ir_statement_ret, 42 | 43 | // special 44 | ir_statement_rdtsc, 45 | ir_statement_cpuid 46 | }; 47 | 48 | enum binary_operation_type 49 | { 50 | binary_op_invalid = -1, 51 | binary_op_add, 52 | binary_op_sub, 53 | binary_op_shl, 54 | binary_op_shr, 55 | binary_op_rcr, 56 | binary_op_rcl, 57 | binary_op_rol, 58 | binary_op_ror, 59 | binary_op_and, 60 | binary_op_or, 61 | binary_op_xor, 62 | binary_op_cmp, 63 | binary_op_test 64 | }; 65 | 66 | // Expression 67 | class Immediate; 68 | class Expression 69 | { 70 | expression_type m_type; 71 | 72 | protected: 73 | Expression(expression_type type) : m_type(type) {} 74 | virtual ~Expression() {} 75 | 76 | public: 77 | expression_type get_type() const 78 | { 79 | return this->m_type; 80 | } 81 | 82 | public: 83 | virtual void to_string(std::ostream& stream) const = 0; 84 | }; 85 | std::ostream& operator<<(std::ostream& stream, const Expression& expr); 86 | std::ostream& operator<<(std::ostream& stream, const Expression* expr); 87 | extern std::shared_ptr simplify_expression(const std::shared_ptr &expression); 88 | 89 | 90 | // Register 91 | class Register : public Expression 92 | { 93 | public: 94 | // vm 95 | Register(triton::uint64 offset); 96 | 97 | // x86register 98 | Register(const triton::arch::Register &triton_register); 99 | 100 | virtual void to_string(std::ostream& stream) const override; 101 | 102 | std::string get_name() const; 103 | triton::uint64 get_offset() const; 104 | 105 | private: 106 | std::string m_name; 107 | triton::uint64 m_offset; 108 | triton::arch::Register m_register; 109 | }; 110 | 111 | 112 | // Memory 113 | class Memory : public Expression 114 | { 115 | public: 116 | Memory(const std::shared_ptr &expr, ir_segment segment, ir_size size); 117 | 118 | virtual void to_string(std::ostream& stream) const override; 119 | 120 | std::shared_ptr get_expression() const; 121 | 122 | private: 123 | ir_size m_size; 124 | ir_segment m_segment; 125 | std::shared_ptr m_expr; 126 | }; 127 | 128 | 129 | // Variable 130 | class Variable : public Expression 131 | { 132 | public: 133 | Variable(ir_size size); 134 | Variable(const std::string &name, ir_size size); 135 | 136 | virtual void to_string(std::ostream& stream) const override; 137 | 138 | std::string get_name() const; 139 | 140 | static std::shared_ptr create_variable(triton::uint32 size); 141 | 142 | private: 143 | static int s_index; 144 | std::string m_name; 145 | ir_size m_size; 146 | }; 147 | 148 | 149 | // Immediate 150 | class Immediate : public Expression 151 | { 152 | public: 153 | Immediate(triton::uint64 immediate); 154 | 155 | virtual void to_string(std::ostream& stream) const override; 156 | 157 | triton::uint64 get_value() const { return this->m_immediate; } 158 | 159 | private: 160 | triton::uint64 m_immediate; 161 | }; 162 | 163 | 164 | // Dereference (y = Deref(x)) 165 | class Dereference : public Expression 166 | { 167 | public: 168 | Dereference(const std::shared_ptr &expr, ir_segment segment, ir_size size); 169 | 170 | virtual void to_string(std::ostream& stream) const override; 171 | 172 | std::shared_ptr get_expression() const; 173 | void set_expression(const std::shared_ptr &expr); 174 | 175 | private: 176 | ir_size m_size; 177 | ir_segment m_segment; 178 | std::shared_ptr m_expr; 179 | }; 180 | 181 | 182 | class Statement 183 | { 184 | protected: 185 | statement_type m_statement_type; 186 | 187 | Statement(statement_type t) : m_statement_type(t) {} 188 | virtual ~Statement() {}; 189 | 190 | public: 191 | statement_type get_type() const 192 | { 193 | return this->m_statement_type; 194 | } 195 | 196 | public: 197 | virtual void to_string(std::ostream& stream) const = 0; 198 | }; 199 | std::ostream& operator<<(std::ostream& stream, const Statement& expr); 200 | std::ostream& operator<<(std::ostream& stream, const Statement* expr); 201 | 202 | // Assign (x = y) 203 | class Assign : public Statement 204 | { 205 | public: 206 | Assign(const std::shared_ptr &left, const std::shared_ptr &right); 207 | 208 | virtual void to_string(std::ostream& stream) const override; 209 | 210 | std::shared_ptr get_left() const; 211 | std::shared_ptr get_right() const; 212 | void set_left(const std::shared_ptr &expr) 213 | { 214 | this->m_left = expr; 215 | } 216 | void set_right(const std::shared_ptr &expr) 217 | { 218 | this->m_right = expr; 219 | } 220 | 221 | private: 222 | std::shared_ptr m_left, m_right; 223 | }; 224 | 225 | // Push x 226 | class Push : public Statement 227 | { 228 | public: 229 | Push(const std::shared_ptr &expr) : Statement(ir_statement_push) 230 | { 231 | this->m_expr = expr; 232 | } 233 | 234 | virtual void to_string(std::ostream& stream) const override 235 | { 236 | stream << "Push (" << this->m_expr << ")"; 237 | } 238 | 239 | std::shared_ptr get_expression() const 240 | { 241 | return this->m_expr; 242 | } 243 | void set_expression(const std::shared_ptr &expr) 244 | { 245 | this->m_expr = expr; 246 | } 247 | 248 | private: 249 | std::shared_ptr m_expr; 250 | }; 251 | 252 | // Pop y 253 | class Pop : public Statement 254 | { 255 | public: 256 | Pop(const std::shared_ptr &expr) : Statement(ir_statement_pop) 257 | { 258 | this->m_expr = expr; 259 | } 260 | 261 | virtual void to_string(std::ostream& stream) const override 262 | { 263 | stream << "Pop (" << this->m_expr << ")"; 264 | } 265 | 266 | std::shared_ptr get_expression() const 267 | { 268 | return this->m_expr; 269 | } 270 | void set_expression(const std::shared_ptr &expr) 271 | { 272 | this->m_expr = expr; 273 | } 274 | 275 | private: 276 | std::shared_ptr m_expr; 277 | }; 278 | 279 | // UnaryOperation 280 | class UnaryOperation : public Expression 281 | { 282 | protected: 283 | UnaryOperation(const std::shared_ptr &op) : Expression(expr_unary_operation) 284 | { 285 | this->m_op = op; 286 | } 287 | 288 | public: 289 | std::shared_ptr get_expression() const 290 | { 291 | return this->m_op; 292 | } 293 | void set_expression(const std::shared_ptr &expr) 294 | { 295 | this->m_op = expr; 296 | } 297 | 298 | protected: 299 | std::shared_ptr m_op; 300 | }; 301 | class Inc : public UnaryOperation 302 | { 303 | public: 304 | Inc(const std::shared_ptr &op) : UnaryOperation(op) 305 | { 306 | 307 | } 308 | 309 | virtual void to_string(std::ostream& stream) const override 310 | { 311 | stream << "Inc(" << this->m_op << ")"; 312 | } 313 | }; 314 | class Dec : public UnaryOperation 315 | { 316 | public: 317 | Dec(const std::shared_ptr &op) : UnaryOperation(op) 318 | { 319 | 320 | } 321 | 322 | virtual void to_string(std::ostream& stream) const override 323 | { 324 | stream << "Dec(" << this->m_op << ")"; 325 | } 326 | }; 327 | class Not : public UnaryOperation 328 | { 329 | public: 330 | Not(const std::shared_ptr &op) : UnaryOperation(op) 331 | { 332 | 333 | } 334 | 335 | virtual void to_string(std::ostream& stream) const override 336 | { 337 | stream << "Not(" << this->m_op << ")"; 338 | } 339 | }; 340 | class Neg : public UnaryOperation 341 | { 342 | public: 343 | Neg(const std::shared_ptr &op) : UnaryOperation(op) 344 | { 345 | 346 | } 347 | 348 | virtual void to_string(std::ostream& stream) const override 349 | { 350 | stream << "Neg(" << this->m_op << ")"; 351 | } 352 | }; 353 | 354 | // BinaryOperation 355 | class BinaryOperation : public Expression 356 | { 357 | protected: 358 | BinaryOperation(const std::shared_ptr &op0, const std::shared_ptr &op1, binary_operation_type t = binary_op_invalid) : Expression(expr_binary_operation) 359 | { 360 | this->m_op0 = op0; 361 | this->m_op1 = op1; 362 | this->m_binary_type = t; 363 | } 364 | virtual ~BinaryOperation() {} 365 | 366 | std::shared_ptr m_op0, m_op1; 367 | binary_operation_type m_binary_type; 368 | 369 | public: 370 | binary_operation_type get_binary_type() const 371 | { 372 | return this->m_binary_type; 373 | } 374 | std::shared_ptr get_expression0() const 375 | { 376 | return this->m_op0; 377 | } 378 | std::shared_ptr get_expression1() const 379 | { 380 | return this->m_op1; 381 | } 382 | void set_expression0(const std::shared_ptr &expr) 383 | { 384 | this->m_op0 = expr; 385 | } 386 | void set_expression1(const std::shared_ptr &expr) 387 | { 388 | this->m_op1 = expr; 389 | } 390 | 391 | virtual void to_string(std::ostream& stream) const = 0; 392 | }; 393 | class Add : public BinaryOperation 394 | { 395 | public: 396 | Add(const std::shared_ptr &op0, 397 | const std::shared_ptr &op1) : BinaryOperation(op0, op1, binary_op_add) 398 | { 399 | 400 | } 401 | 402 | virtual void to_string(std::ostream& stream) const override 403 | { 404 | stream << "Add(" << this->m_op0 << ", " << this->m_op1 << ")"; 405 | } 406 | }; 407 | class Sub : public BinaryOperation 408 | { 409 | public: 410 | Sub(const std::shared_ptr &op0, 411 | const std::shared_ptr &op1) : BinaryOperation(op0, op1, binary_op_sub) 412 | { 413 | 414 | } 415 | 416 | virtual void to_string(std::ostream& stream) const override 417 | { 418 | stream << "Sub(" << this->m_op0 << ", " << this->m_op1 << ")"; 419 | } 420 | }; 421 | 422 | class Shl : public BinaryOperation 423 | { 424 | public: 425 | Shl(const std::shared_ptr &op0, 426 | const std::shared_ptr &op1) : BinaryOperation(op0, op1) 427 | { 428 | 429 | } 430 | 431 | virtual void to_string(std::ostream& stream) const override 432 | { 433 | stream << "Shl(" << this->m_op0 << ", " << this->m_op1 << ")"; 434 | } 435 | }; 436 | class Shr : public BinaryOperation 437 | { 438 | public: 439 | Shr(const std::shared_ptr &op0, 440 | const std::shared_ptr &op1) : BinaryOperation(op0, op1) 441 | { 442 | 443 | } 444 | 445 | virtual void to_string(std::ostream& stream) const override 446 | { 447 | stream << "Shr(" << this->m_op0 << ", " << this->m_op1 << ")"; 448 | } 449 | }; 450 | 451 | class Rcr : public BinaryOperation 452 | { 453 | public: 454 | Rcr(const std::shared_ptr &op0, 455 | const std::shared_ptr &op1) : BinaryOperation(op0, op1) 456 | { 457 | 458 | } 459 | 460 | virtual void to_string(std::ostream& stream) const override 461 | { 462 | stream << "Rcr(" << this->m_op0 << ", " << this->m_op1 << ")"; 463 | } 464 | }; 465 | class Rcl : public BinaryOperation 466 | { 467 | public: 468 | Rcl(const std::shared_ptr &op0, 469 | const std::shared_ptr &op1) : BinaryOperation(op0, op1) 470 | { 471 | 472 | } 473 | 474 | virtual void to_string(std::ostream& stream) const override 475 | { 476 | stream << "Rcl(" << this->m_op0 << ", " << this->m_op1 << ")"; 477 | } 478 | }; 479 | class Rol : public BinaryOperation 480 | { 481 | public: 482 | Rol(const std::shared_ptr &op0, 483 | const std::shared_ptr &op1) : BinaryOperation(op0, op1) 484 | { 485 | 486 | } 487 | 488 | virtual void to_string(std::ostream& stream) const override 489 | { 490 | stream << "Rol(" << this->m_op0 << ", " << this->m_op1 << ")"; 491 | } 492 | }; 493 | class Ror : public BinaryOperation 494 | { 495 | public: 496 | Ror(const std::shared_ptr &op0, 497 | const std::shared_ptr &op1) : BinaryOperation(op0, op1) 498 | { 499 | 500 | } 501 | 502 | virtual void to_string(std::ostream& stream) const override 503 | { 504 | stream << "Ror(" << this->m_op0 << ", " << this->m_op1 << ")"; 505 | } 506 | }; 507 | 508 | class And : public BinaryOperation 509 | { 510 | public: 511 | And(const std::shared_ptr &op0, 512 | const std::shared_ptr &op1) : BinaryOperation(op0, op1) 513 | { 514 | 515 | } 516 | 517 | virtual void to_string(std::ostream& stream) const override 518 | { 519 | stream << "And(" << this->m_op0 << ", " << this->m_op1 << ")"; 520 | } 521 | }; 522 | class Or : public BinaryOperation 523 | { 524 | public: 525 | Or(const std::shared_ptr &op0, 526 | const std::shared_ptr &op1) : BinaryOperation(op0, op1) 527 | { 528 | 529 | } 530 | 531 | virtual void to_string(std::ostream& stream) const override 532 | { 533 | stream << "Or(" << this->m_op0 << ", " << this->m_op1 << ")"; 534 | } 535 | }; 536 | class Xor : public BinaryOperation 537 | { 538 | public: 539 | Xor(const std::shared_ptr &op0, 540 | const std::shared_ptr &op1) : BinaryOperation(op0, op1, binary_op_xor) 541 | { 542 | 543 | } 544 | 545 | virtual void to_string(std::ostream& stream) const override 546 | { 547 | stream << "Xor(" << this->m_op0 << ", " << this->m_op1 << ")"; 548 | } 549 | }; 550 | 551 | class Cmp : public BinaryOperation 552 | { 553 | public: 554 | Cmp(const std::shared_ptr &op0, 555 | const std::shared_ptr &op1) : BinaryOperation(op0, op1) 556 | { 557 | 558 | } 559 | 560 | virtual void to_string(std::ostream& stream) const override 561 | { 562 | stream << "Cmp(" << this->m_op0 << ", " << this->m_op1 << ")"; 563 | } 564 | }; 565 | class Test : public BinaryOperation 566 | { 567 | public: 568 | Test(const std::shared_ptr &op0, 569 | const std::shared_ptr &op1) : BinaryOperation(op0, op1) 570 | { 571 | 572 | } 573 | 574 | virtual void to_string(std::ostream& stream) const override 575 | { 576 | stream << "Test(" << this->m_op0 << ", " << this->m_op1 << ")"; 577 | } 578 | }; 579 | 580 | // special 581 | class Cpuid : public Statement 582 | { 583 | public: 584 | Cpuid() : Statement(ir_statement_cpuid) 585 | { 586 | 587 | } 588 | 589 | virtual void to_string(std::ostream& stream) const override 590 | { 591 | stream << "Cpuid"; 592 | } 593 | }; 594 | class Rdtsc : public Statement 595 | { 596 | public: 597 | Rdtsc() : Statement(ir_statement_rdtsc) 598 | { 599 | 600 | } 601 | 602 | virtual void to_string(std::ostream& stream) const override 603 | { 604 | stream << "Rdtsc"; 605 | } 606 | }; 607 | } -------------------------------------------------------------------------------- /VMProtectTest/ProcessStream.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "ProcessStream.hpp" 4 | 5 | ProcessStream::ProcessStream(bool x86_64) : AbstractStream(x86_64) 6 | { 7 | this->m_processId = 0; 8 | this->m_processHandle = NULL; 9 | this->m_pos = 0; 10 | } 11 | ProcessStream::~ProcessStream() 12 | { 13 | this->close(); 14 | } 15 | 16 | bool ProcessStream::isOpen() const 17 | { 18 | return this->m_processHandle != NULL; 19 | } 20 | 21 | bool ProcessStream::open(unsigned long pid) 22 | { 23 | this->close(); 24 | this->m_processId = pid; 25 | this->m_processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); 26 | return this->isOpen(); 27 | } 28 | 29 | void ProcessStream::close() 30 | { 31 | if (this->m_processHandle != NULL) 32 | { 33 | CloseHandle(this->m_processHandle); 34 | this->m_processHandle = NULL; 35 | } 36 | } 37 | 38 | SIZE_T ProcessStream::read(void* buf, SIZE_T size) 39 | { 40 | if (!this->isOpen()) 41 | throw std::runtime_error("process is not open"); 42 | 43 | LPCVOID address = reinterpret_cast(this->m_pos); 44 | SIZE_T readBytes = 0; 45 | if (!ReadProcessMemory(this->m_processHandle, address, buf, size, &readBytes)) 46 | { 47 | DWORD lastError = GetLastError(); 48 | std::stringstream ss; 49 | ss << "ReadProcessMemory(" << address << ") failed with error code: " << lastError; 50 | throw std::runtime_error(ss.str()); 51 | } 52 | 53 | this->m_pos += readBytes; 54 | return readBytes; 55 | } 56 | 57 | SIZE_T ProcessStream::write(const void* buf, SIZE_T size) 58 | { 59 | if (!this->isOpen()) 60 | throw std::runtime_error("process is not open"); 61 | 62 | LPVOID address = reinterpret_cast(this->m_pos); 63 | SIZE_T writtenBytes = 0; 64 | if (!WriteProcessMemory(this->m_processHandle, address, buf, size, &writtenBytes)) 65 | { 66 | DWORD lastError = GetLastError(); 67 | std::stringstream ss; 68 | ss << "WriteProcessMemory failed with error code: " << lastError; 69 | throw std::runtime_error(ss.str()); 70 | } 71 | 72 | this->m_pos += writtenBytes; 73 | return writtenBytes; 74 | } 75 | 76 | unsigned long long ProcessStream::pos() 77 | { 78 | return this->m_pos; 79 | } 80 | 81 | void ProcessStream::seek(unsigned long long pos) 82 | { 83 | if (!this->isOpen()) 84 | throw std::runtime_error("process is not open"); 85 | 86 | this->m_pos = pos; 87 | } -------------------------------------------------------------------------------- /VMProtectTest/ProcessStream.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "AbstractStream.hpp" 4 | 5 | class ProcessStream : public AbstractStream 6 | { 7 | public: 8 | ProcessStream(bool x86_64 = false); 9 | ~ProcessStream(); 10 | 11 | bool isOpen() const override; 12 | bool open(unsigned long pid); 13 | void close() override; 14 | 15 | SIZE_T read(void* buf, SIZE_T size) override; 16 | SIZE_T write(const void* buf, SIZE_T size) override; 17 | 18 | unsigned long long pos() override; 19 | void seek(unsigned long long pos) override; 20 | 21 | private: 22 | unsigned long m_processId; 23 | HANDLE m_processHandle; 24 | unsigned long long m_pos; 25 | }; -------------------------------------------------------------------------------- /VMProtectTest/VMHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "VMHandler.hpp" 4 | #include "x86_instruction.hpp" 5 | 6 | VMHandler::VMHandler() 7 | { 8 | } 9 | VMHandler::~VMHandler() 10 | { 11 | } 12 | 13 | unsigned long long VMHandler::compute_next_handler_address(void *context) 14 | { 15 | // simulate instructions? 16 | } -------------------------------------------------------------------------------- /VMProtectTest/VMHandler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class x86_instruction; 4 | 5 | enum handler_type 6 | { 7 | vmprotect_handler_pushb, 8 | vmprotect_handler_pushw, 9 | vmprotect_handler_pushd, 10 | vmprotect_handler_pushq, 11 | 12 | vmprotect_handler_push_vmvar, 13 | vmprotect_handler_push_sp, 14 | 15 | vmprotect_handler_popd, 16 | vmprotect_handler_popq, 17 | 18 | vmprotect_handler_fetch, // ds? 19 | vmprotect_handler_fetchss, 20 | 21 | vmprotect_handler_write, 22 | 23 | // binary op 24 | vmprotect_handler_add, // add(op, op) 25 | vmprotect_handler_shr, // shr(op, op_w) 26 | vmprotect_handler_nor, // nor(op, op) 27 | 28 | vmprotect_handler_mul, // mul(op, op) 29 | vmprotect_handler_imul, // imul(op, op) 30 | }; 31 | 32 | enum 33 | { 34 | vmp_size_byte, 35 | vmp_size_word, 36 | vmp_size_dword, 37 | vmp_size_qword, 38 | }; 39 | 40 | enum vmp_segment 41 | { 42 | vmp_segment_es, 43 | vmp_segment_cs, 44 | vmp_segment_ss, 45 | vmp_segment_ds, 46 | vmp_segment_fs, 47 | vmp_segment_gs, 48 | vmp_segment_scratch 49 | }; 50 | 51 | class VMPMemory 52 | { 53 | vmp_segment m_segment; 54 | }; 55 | 56 | class VMPRegister 57 | { 58 | // 59 | }; 60 | 61 | class VMPExpression 62 | { 63 | // VMPMemory or VMPRegister 64 | }; 65 | 66 | class VMP_Push 67 | { 68 | // register or memory or immediate 69 | VMPExpression m_expr; 70 | }; 71 | class VMP_Pop 72 | { 73 | // register or memory 74 | VMPExpression m_expr; 75 | }; 76 | 77 | class VMHandler 78 | { 79 | public: 80 | VMHandler(); 81 | ~VMHandler(); 82 | 83 | unsigned long long compute_next_handler_address(void *context); 84 | 85 | private: 86 | std::shared_ptr m_basic_blocks; 87 | handler_type m_type; 88 | }; -------------------------------------------------------------------------------- /VMProtectTest/VMProtectAnalyzer.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "VMProtectAnalyzer.hpp" 4 | #include "x86_instruction.hpp" 5 | #include "AbstractStream.hpp" 6 | #include "CFG.hpp" 7 | #include "IR.hpp" 8 | 9 | // helper? 10 | void print_basic_blocks(const std::shared_ptr &first_basic_block) 11 | { 12 | std::set visit_for_print; 13 | std::shared_ptr basic_block = first_basic_block; 14 | for (auto it = basic_block->instructions.begin(); it != basic_block->instructions.end();) 15 | { 16 | const auto& instruction = *it; 17 | if (++it != basic_block->instructions.end()) 18 | { 19 | // loop until it reaches end 20 | instruction->print(); 21 | continue; 22 | } 23 | 24 | // dont print unconditional jmp, they are annoying 25 | if (instruction->get_category() != XED_CATEGORY_UNCOND_BR 26 | || instruction->get_branch_displacement_width() == 0) 27 | { 28 | instruction->print(); 29 | } 30 | 31 | visit_for_print.insert(basic_block->leader); 32 | if (basic_block->next_basic_block && visit_for_print.count(basic_block->next_basic_block->leader) <= 0) 33 | { 34 | // print next 35 | basic_block = basic_block->next_basic_block; 36 | } 37 | else if (basic_block->target_basic_block && visit_for_print.count(basic_block->target_basic_block->leader) <= 0) 38 | { 39 | // it ends with jmp? 40 | basic_block = basic_block->target_basic_block; 41 | } 42 | else 43 | { 44 | // perhaps finishes? 45 | break; 46 | } 47 | 48 | it = basic_block->instructions.begin(); 49 | } 50 | } 51 | 52 | // variablenode? 53 | triton::engines::symbolic::SharedSymbolicVariable get_symbolic_var(const triton::ast::SharedAbstractNode &node) 54 | { 55 | return node->getType() == triton::ast::VARIABLE_NODE ? 56 | std::dynamic_pointer_cast(node)->getSymbolicVariable() : nullptr; 57 | } 58 | std::set collect_symvars(const triton::ast::SharedAbstractNode &parent) 59 | { 60 | std::set result; 61 | if (!parent) 62 | return result; 63 | 64 | if (parent->getChildren().empty() && parent->isSymbolized()) 65 | { 66 | // this must be variable node right? 67 | assert(parent->getType() == triton::ast::VARIABLE_NODE); 68 | result.insert(parent); 69 | } 70 | 71 | for (const triton::ast::SharedAbstractNode &child : parent->getChildren()) 72 | { 73 | if (!child->getChildren().empty()) 74 | { 75 | // go deep if symbolized 76 | if (child->isSymbolized()) 77 | { 78 | auto _new = collect_symvars(child); 79 | result.insert(_new.begin(), _new.end()); 80 | } 81 | } 82 | else if (child->isSymbolized()) 83 | { 84 | // this must be variable node right? 85 | assert(child->getType() == triton::ast::VARIABLE_NODE); 86 | result.insert(child); 87 | } 88 | } 89 | return result; 90 | } 91 | bool is_unary_operation(const triton::arch::Instruction &triton_instruction) 92 | { 93 | switch (triton_instruction.getType()) 94 | { 95 | case triton::arch::x86::ID_INS_INC: 96 | case triton::arch::x86::ID_INS_DEC: 97 | case triton::arch::x86::ID_INS_NEG: 98 | case triton::arch::x86::ID_INS_NOT: 99 | return true; 100 | 101 | default: 102 | return false; 103 | } 104 | } 105 | bool is_binary_operation(const triton::arch::Instruction &triton_instruction) 106 | { 107 | switch (triton_instruction.getType()) 108 | { 109 | case triton::arch::x86::ID_INS_ADD: 110 | case triton::arch::x86::ID_INS_SUB: 111 | case triton::arch::x86::ID_INS_SHL: 112 | case triton::arch::x86::ID_INS_SHR: 113 | case triton::arch::x86::ID_INS_RCR: 114 | case triton::arch::x86::ID_INS_RCL: 115 | case triton::arch::x86::ID_INS_ROL: 116 | case triton::arch::x86::ID_INS_ROR: 117 | case triton::arch::x86::ID_INS_AND: 118 | case triton::arch::x86::ID_INS_OR: 119 | case triton::arch::x86::ID_INS_XOR: 120 | case triton::arch::x86::ID_INS_CMP: 121 | case triton::arch::x86::ID_INS_TEST: 122 | return true; 123 | 124 | default: 125 | return false; 126 | } 127 | } 128 | 129 | 130 | // VMProtectAnalyzer 131 | VMProtectAnalyzer::VMProtectAnalyzer(triton::arch::architecture_e arch) 132 | { 133 | triton_api = std::make_shared(); 134 | triton_api->setArchitecture(arch); 135 | triton_api->setMode(triton::modes::ALIGNED_MEMORY, true); 136 | //triton_api->setAstRepresentationMode(triton::ast::representations::PYTHON_REPRESENTATION); 137 | this->m_scratch_size = 0; 138 | } 139 | VMProtectAnalyzer::~VMProtectAnalyzer() 140 | { 141 | } 142 | 143 | bool VMProtectAnalyzer::is_x64() const 144 | { 145 | const triton::arch::architecture_e architecture = this->triton_api->getArchitecture(); 146 | switch (architecture) 147 | { 148 | case triton::arch::ARCH_X86: 149 | return false; 150 | 151 | case triton::arch::ARCH_X86_64: 152 | return true; 153 | 154 | default: 155 | throw std::runtime_error("invalid architecture"); 156 | } 157 | } 158 | 159 | triton::arch::Register VMProtectAnalyzer::get_bp_register() const 160 | { 161 | return this->is_x64() ? triton_api->registers.x86_rbp : triton_api->registers.x86_ebp; 162 | } 163 | triton::arch::Register VMProtectAnalyzer::get_sp_register() const 164 | { 165 | const triton::arch::CpuInterface *_cpu = triton_api->getCpuInstance(); 166 | if (!_cpu) 167 | throw std::runtime_error("CpuInterface is nullptr"); 168 | 169 | return _cpu->getStackPointer(); 170 | } 171 | triton::arch::Register VMProtectAnalyzer::get_ip_register() const 172 | { 173 | const triton::arch::CpuInterface *_cpu = triton_api->getCpuInstance(); 174 | if (!_cpu) 175 | throw std::runtime_error("CpuInterface is nullptr"); 176 | 177 | return _cpu->getProgramCounter(); 178 | } 179 | 180 | triton::uint64 VMProtectAnalyzer::get_bp() const 181 | { 182 | return triton_api->getConcreteRegisterValue(this->get_bp_register()).convert_to(); 183 | } 184 | triton::uint64 VMProtectAnalyzer::get_sp() const 185 | { 186 | return triton_api->getConcreteRegisterValue(this->get_sp_register()).convert_to(); 187 | } 188 | triton::uint64 VMProtectAnalyzer::get_ip() const 189 | { 190 | return triton_api->getConcreteRegisterValue(this->get_ip_register()).convert_to(); 191 | } 192 | 193 | void VMProtectAnalyzer::symbolize_registers() 194 | { 195 | // symbolize all registers; 196 | if (this->is_x64()) 197 | { 198 | triton::engines::symbolic::SharedSymbolicVariable symvar_eax = triton_api->symbolizeRegister(triton_api->registers.x86_rax); 199 | triton::engines::symbolic::SharedSymbolicVariable symvar_ebx = triton_api->symbolizeRegister(triton_api->registers.x86_rbx); 200 | triton::engines::symbolic::SharedSymbolicVariable symvar_ecx = triton_api->symbolizeRegister(triton_api->registers.x86_rcx); 201 | triton::engines::symbolic::SharedSymbolicVariable symvar_edx = triton_api->symbolizeRegister(triton_api->registers.x86_rdx); 202 | triton::engines::symbolic::SharedSymbolicVariable symvar_esi = triton_api->symbolizeRegister(triton_api->registers.x86_rsi); 203 | triton::engines::symbolic::SharedSymbolicVariable symvar_edi = triton_api->symbolizeRegister(triton_api->registers.x86_rdi); 204 | triton::engines::symbolic::SharedSymbolicVariable symvar_ebp = triton_api->symbolizeRegister(triton_api->registers.x86_rbp); 205 | //triton::engines::symbolic::SharedSymbolicVariable symvar_esp = triton_api->symbolizeRegister(triton_api->registers.x86_rsp); 206 | 207 | triton::engines::symbolic::SharedSymbolicVariable symvar_r8 = triton_api->symbolizeRegister(triton_api->registers.x86_r8); 208 | triton::engines::symbolic::SharedSymbolicVariable symvar_r9 = triton_api->symbolizeRegister(triton_api->registers.x86_r9); 209 | triton::engines::symbolic::SharedSymbolicVariable symvar_r10 = triton_api->symbolizeRegister(triton_api->registers.x86_r10); 210 | triton::engines::symbolic::SharedSymbolicVariable symvar_r11 = triton_api->symbolizeRegister(triton_api->registers.x86_r11); 211 | triton::engines::symbolic::SharedSymbolicVariable symvar_r12 = triton_api->symbolizeRegister(triton_api->registers.x86_r12); 212 | triton::engines::symbolic::SharedSymbolicVariable symvar_r13 = triton_api->symbolizeRegister(triton_api->registers.x86_r13); 213 | triton::engines::symbolic::SharedSymbolicVariable symvar_r14 = triton_api->symbolizeRegister(triton_api->registers.x86_r14); 214 | triton::engines::symbolic::SharedSymbolicVariable symvar_r15 = triton_api->symbolizeRegister(triton_api->registers.x86_r15); 215 | 216 | symvar_eax->setAlias("rax"); 217 | symvar_ebx->setAlias("rbx"); 218 | symvar_ecx->setAlias("rcx"); 219 | symvar_edx->setAlias("rdx"); 220 | symvar_esi->setAlias("rsi"); 221 | symvar_edi->setAlias("rdi"); 222 | symvar_ebp->setAlias("rbp"); 223 | //symvar_esp->setAlias("rsp"); 224 | symvar_r8->setAlias("r8"); 225 | symvar_r9->setAlias("r9"); 226 | symvar_r10->setAlias("r10"); 227 | symvar_r11->setAlias("r11"); 228 | symvar_r12->setAlias("r12"); 229 | symvar_r13->setAlias("r13"); 230 | symvar_r14->setAlias("r14"); 231 | symvar_r15->setAlias("r15"); 232 | } 233 | else 234 | { 235 | triton::engines::symbolic::SharedSymbolicVariable symvar_eax = triton_api->symbolizeRegister(triton_api->registers.x86_eax); 236 | triton::engines::symbolic::SharedSymbolicVariable symvar_ebx = triton_api->symbolizeRegister(triton_api->registers.x86_ebx); 237 | triton::engines::symbolic::SharedSymbolicVariable symvar_ecx = triton_api->symbolizeRegister(triton_api->registers.x86_ecx); 238 | triton::engines::symbolic::SharedSymbolicVariable symvar_edx = triton_api->symbolizeRegister(triton_api->registers.x86_edx); 239 | triton::engines::symbolic::SharedSymbolicVariable symvar_esi = triton_api->symbolizeRegister(triton_api->registers.x86_esi); 240 | triton::engines::symbolic::SharedSymbolicVariable symvar_edi = triton_api->symbolizeRegister(triton_api->registers.x86_edi); 241 | triton::engines::symbolic::SharedSymbolicVariable symvar_ebp = triton_api->symbolizeRegister(triton_api->registers.x86_ebp); 242 | //triton::engines::symbolic::SharedSymbolicVariable symvar_esp = triton_api->symbolizeRegister(triton_api->registers.x86_esp); 243 | symvar_eax->setAlias("eax"); 244 | symvar_ebx->setAlias("ebx"); 245 | symvar_ecx->setAlias("ecx"); 246 | symvar_edx->setAlias("edx"); 247 | symvar_esi->setAlias("esi"); 248 | symvar_edi->setAlias("edi"); 249 | symvar_ebp->setAlias("ebp"); 250 | //symvar_esp->setAlias("esp"); 251 | } 252 | } 253 | 254 | const triton::arch::Register& VMProtectAnalyzer::get_source_register(const triton::arch::Instruction &triton_instruction) const 255 | { 256 | if (triton_instruction.getType() == triton::arch::x86::ID_INS_POP) 257 | { 258 | // idk... 259 | return triton_api->registers.x86_eflags; 260 | } 261 | 262 | if (triton_instruction.getType() != triton::arch::x86::ID_INS_MOV) 263 | { 264 | std::stringstream ss; 265 | ss << "memory has written by undefined opcode\n" 266 | << "\t" << triton_instruction << "\"\n" 267 | << "\tFile: " << __FILE__ << ", L: " << __LINE__; 268 | throw std::runtime_error(ss.str()); 269 | } 270 | 271 | // mov MEM,REG 272 | const std::vector &operands = triton_instruction.operands; 273 | if (operands.size() != 2 274 | || operands[0].getType() != triton::arch::OP_MEM 275 | || operands[1].getType() != triton::arch::OP_REG) 276 | { 277 | std::stringstream ss; 278 | ss << "memory has written by unknown instruction\n" 279 | << "\t" << triton_instruction << "\"\n" 280 | << "\tFile: " << __FILE__ << ", L: " << __LINE__; 281 | throw std::runtime_error(ss.str()); 282 | } 283 | return operands[1].getConstRegister(); 284 | } 285 | const triton::arch::Register& VMProtectAnalyzer::get_dest_register(const triton::arch::Instruction &triton_instruction) const 286 | { 287 | const triton::uint32 instruction_type = triton_instruction.getType(); 288 | if (instruction_type != triton::arch::x86::ID_INS_MOV 289 | && instruction_type != triton::arch::x86::ID_INS_MOVZX) 290 | { 291 | std::stringstream ss; 292 | ss << "memory has read by undefined opcode\n" 293 | << "\t" << triton_instruction << "\"\n" 294 | << "\tFile: " << __FILE__ << ", L: " << __LINE__; 295 | throw std::runtime_error(ss.str()); 296 | } 297 | 298 | // [mov|movzx] REG,MEM 299 | const std::vector &operands = triton_instruction.operands; 300 | if (operands.size() != 2 301 | || operands[0].getType() != triton::arch::OP_REG 302 | || operands[1].getType() != triton::arch::OP_MEM) 303 | { 304 | std::stringstream ss; 305 | ss << "memory has read by unknown instruction\n" 306 | << "\t" << triton_instruction << "\"\n" 307 | << "\tFile: " << __FILE__ << ", L: " << __LINE__; 308 | throw std::runtime_error(ss.str()); 309 | } 310 | return operands[0].getConstRegister(); 311 | } 312 | 313 | bool VMProtectAnalyzer::is_bytecode_address(const triton::ast::SharedAbstractNode &lea_ast, VMPHandlerContext *context) 314 | { 315 | // return true if lea_ast is constructed by bytecode 316 | const std::set symvars = collect_symvars(lea_ast); 317 | if (symvars.empty()) 318 | return false; 319 | 320 | for (auto it = symvars.begin(); it != symvars.end(); ++it) 321 | { 322 | const triton::ast::SharedAbstractNode &node = *it; 323 | const triton::engines::symbolic::SharedSymbolicVariable &symvar = std::dynamic_pointer_cast(node)->getSymbolicVariable(); 324 | if (symvar->getId() != context->symvar_bytecode->getId()) 325 | return false; 326 | } 327 | return true; 328 | } 329 | bool VMProtectAnalyzer::is_stack_address(const triton::ast::SharedAbstractNode &lea_ast, VMPHandlerContext *context) 330 | { 331 | // return true if lea_ast is constructed by stack 332 | const std::set symvars = collect_symvars(lea_ast); 333 | if (symvars.empty()) 334 | return false; 335 | 336 | for (auto it = symvars.begin(); it != symvars.end(); ++it) 337 | { 338 | const triton::ast::SharedAbstractNode &node = *it; 339 | const triton::engines::symbolic::SharedSymbolicVariable &symvar = std::dynamic_pointer_cast(node)->getSymbolicVariable(); 340 | if (symvar != context->symvar_stack) 341 | return false; 342 | } 343 | return true; 344 | } 345 | bool VMProtectAnalyzer::is_scratch_area_address(const triton::ast::SharedAbstractNode &lea_ast, VMPHandlerContext *context) 346 | { 347 | // size is hardcoded for now (can see in any push handler perhaps) 348 | const triton::uint64 runtime_address = lea_ast->evaluate().convert_to(); 349 | return context->x86_sp <= runtime_address && runtime_address < (context->x86_sp + context->scratch_area_size); 350 | } 351 | bool VMProtectAnalyzer::is_fetch_arguments(const triton::ast::SharedAbstractNode &lea_ast, VMPHandlerContext *context) 352 | { 353 | if (lea_ast->getType() != triton::ast::VARIABLE_NODE) 354 | return false; 355 | 356 | const triton::engines::symbolic::SharedSymbolicVariable &symvar = 357 | std::dynamic_pointer_cast(lea_ast)->getSymbolicVariable(); 358 | return context->arguments.find(symvar->getId()) != context->arguments.end(); 359 | } 360 | 361 | void VMProtectAnalyzer::load(AbstractStream& stream, 362 | unsigned long long module_base, unsigned long long vmp0_address, unsigned long long vmp0_size) 363 | { 364 | // concretize vmp section memory 365 | unsigned long long vmp_section_address = (module_base + vmp0_address); 366 | unsigned long long vmp_section_size = vmp0_size; 367 | void *vmp0 = malloc(vmp_section_size); 368 | 369 | stream.seek(vmp_section_address); 370 | if (stream.read(vmp0, vmp_section_size) != vmp_section_size) 371 | throw std::runtime_error("stream.read failed"); 372 | 373 | triton_api->setConcreteMemoryAreaValue(vmp_section_address, (const triton::uint8 *)vmp0, vmp_section_size); 374 | free(vmp0); 375 | } 376 | 377 | // vm-enter 378 | void VMProtectAnalyzer::analyze_vm_enter(AbstractStream& stream, unsigned long long address) 379 | { 380 | // reset symbolic 381 | triton_api->concretizeAllMemory(); 382 | //triton_api->concretizeAllRegister(); 383 | this->symbolize_registers(); 384 | 385 | // set esp 386 | const triton::arch::Register sp_register = this->is_x64() ? triton_api->registers.x86_rsp : triton_api->registers.x86_esp; 387 | triton_api->setConcreteRegisterValue(sp_register, 0x1000); 388 | 389 | const triton::uint64 previous_sp = this->get_sp(); 390 | bool check_flags = true; 391 | 392 | std::shared_ptr basic_block = make_cfg(stream, address); 393 | for (auto it = basic_block->instructions.begin(); it != basic_block->instructions.end();) 394 | { 395 | const std::shared_ptr instruction = *it; 396 | const std::vector bytes = instruction->get_bytes(); 397 | 398 | // fix ip 399 | triton_api->setConcreteRegisterValue(this->get_ip_register(), instruction->get_addr()); 400 | 401 | // do stuff with triton 402 | triton::arch::Instruction triton_instruction; 403 | triton_instruction.setOpcode(&bytes[0], (triton::uint32)bytes.size()); 404 | triton_instruction.setAddress(instruction->get_addr()); 405 | triton_api->processing(triton_instruction); 406 | 407 | // check flags 408 | if (check_flags) 409 | { 410 | // symbolize eflags if pushfd 411 | if (triton_instruction.getType() == triton::arch::x86::ID_INS_PUSHFD) 412 | { 413 | const auto stores = triton_instruction.getStoreAccess(); 414 | if (stores.size() != 1) 415 | throw std::runtime_error("bluh"); 416 | 417 | triton_api->symbolizeMemory(stores.begin()->first)->setAlias("eflags"); 418 | } 419 | else if (triton_instruction.getType() == triton::arch::x86::ID_INS_PUSHFQ) 420 | { 421 | const auto stores = triton_instruction.getStoreAccess(); 422 | if (stores.size() != 1) 423 | throw std::runtime_error("bluh"); 424 | 425 | triton_api->symbolizeMemory(stores.begin()->first)->setAlias("rflags"); 426 | } 427 | 428 | // written_register 429 | for (const auto &pair : triton_instruction.getWrittenRegisters()) 430 | { 431 | const triton::arch::Register &written_register = pair.first; 432 | if (written_register.getId() == triton::arch::ID_REG_X86_EFLAGS) 433 | { 434 | check_flags = false; 435 | break; 436 | } 437 | } 438 | } 439 | 440 | if (++it != basic_block->instructions.end()) 441 | { 442 | // loop until it reaches end 443 | std::cout << triton_instruction << std::endl; 444 | continue; 445 | } 446 | 447 | if (instruction->get_category() != XED_CATEGORY_UNCOND_BR || instruction->get_branch_displacement_width() == 0) 448 | { 449 | std::cout << triton_instruction << std::endl; 450 | } 451 | 452 | if (basic_block->next_basic_block && basic_block->target_basic_block) 453 | { 454 | // it ends with conditional branch 455 | if (triton_instruction.isConditionTaken()) 456 | { 457 | basic_block = basic_block->target_basic_block; 458 | } 459 | else 460 | { 461 | basic_block = basic_block->next_basic_block; 462 | } 463 | } 464 | else if (basic_block->target_basic_block) 465 | { 466 | // it ends with jmp? 467 | basic_block = basic_block->target_basic_block; 468 | } 469 | else if (basic_block->next_basic_block) 470 | { 471 | // just follow :) 472 | basic_block = basic_block->next_basic_block; 473 | } 474 | else 475 | { 476 | // perhaps finishes? 477 | break; 478 | } 479 | 480 | it = basic_block->instructions.begin(); 481 | } 482 | 483 | const triton::uint64 bp = this->get_bp(); 484 | const triton::uint64 sp = this->get_sp(); 485 | const triton::uint64 scratch_size = bp - sp; 486 | const triton::uint64 scratch_length = scratch_size / triton_api->getGprSize(); 487 | const triton::uint64 var_length = (previous_sp - bp) / triton_api->getGprSize(); 488 | for (triton::uint64 i = 0; i < var_length; i++) 489 | { 490 | triton::ast::SharedAbstractNode mem_ast = triton_api->getMemoryAst( 491 | triton::arch::MemoryAccess(previous_sp - (i * triton_api->getGprSize()) - triton_api->getGprSize(), triton_api->getGprSize())); 492 | triton::ast::SharedAbstractNode simplified = triton_api->processSimplification(mem_ast, true); 493 | if (simplified->getType() == triton::ast::BV_NODE) 494 | { 495 | triton::uint64 val = simplified->evaluate().convert_to(); 496 | 497 | char buf[1024]; 498 | if (this->is_x64()) 499 | sprintf_s(buf, 1024, "push Qword(0x%llX)", val); 500 | else 501 | sprintf_s(buf, 1024, "push Dword(0x%llX)", val); 502 | output_strings.push_back(buf); 503 | } 504 | else if (simplified->getType() == triton::ast::VARIABLE_NODE) 505 | { 506 | char buf[1024]; 507 | sprintf_s(buf, 1024, "push %s", 508 | std::dynamic_pointer_cast(simplified)->getSymbolicVariable()->getAlias().c_str()); 509 | output_strings.push_back(buf); 510 | } 511 | else 512 | { 513 | throw std::runtime_error("vm enter error"); 514 | } 515 | } 516 | 517 | printf("scratch_size: 0x%016llX, scratch_length: %lld\n", scratch_size, scratch_length); 518 | this->m_scratch_size = scratch_size; 519 | } 520 | 521 | 522 | // vm-handler 523 | void VMProtectAnalyzer::symbolize_memory(const triton::arch::MemoryAccess& mem, VMPHandlerContext *context) 524 | { 525 | const triton::uint64 address = mem.getAddress(); 526 | triton::ast::SharedAbstractNode lea_ast = mem.getLeaAst(); 527 | if (!lea_ast) 528 | { 529 | // most likely can be ignored 530 | return; 531 | } 532 | 533 | lea_ast = triton_api->processSimplification(lea_ast, true); 534 | if (!lea_ast->isSymbolized()) 535 | { 536 | // most likely can be ignored 537 | return; 538 | } 539 | 540 | if (this->is_bytecode_address(lea_ast, context)) 541 | { 542 | // bytecode can be considered const value 543 | triton_api->taintMemory(mem); 544 | } 545 | 546 | // lea_ast = context + const 547 | else if (this->is_scratch_area_address(lea_ast, context)) 548 | { 549 | // [EBP+offset] 550 | const triton::uint64 scratch_offset = lea_ast->evaluate().convert_to() - context->x86_sp; 551 | 552 | triton::engines::symbolic::SharedSymbolicVariable symvar_vmreg = triton_api->symbolizeMemory(mem); 553 | context->scratch_variables.insert(std::make_pair(symvar_vmreg->getId(), symvar_vmreg)); 554 | std::cout << "Load Scratch:[0x" << std::hex << scratch_offset << "]" << std::endl; 555 | 556 | // TempVar = VM_REG 557 | auto temp_variable = IR::Variable::create_variable(mem.getSize()); 558 | 559 | auto ir_imm = std::make_shared(scratch_offset); 560 | std::shared_ptr right_expression = std::make_shared(ir_imm, IR::ir_segment_scratch, (IR::ir_size)mem.getSize()); 561 | 562 | auto assign = std::make_shared(temp_variable, right_expression); 563 | context->m_statements.push_back(assign); 564 | context->m_expression_map[symvar_vmreg->getId()] = temp_variable; 565 | symvar_vmreg->setAlias(temp_variable->get_name()); 566 | } 567 | else if (this->is_stack_address(lea_ast, context)) 568 | { 569 | const triton::uint64 offset = address - context->stack; 570 | 571 | triton::engines::symbolic::SharedSymbolicVariable symvar_arg = triton_api->symbolizeMemory(mem); 572 | context->arguments.insert(std::make_pair(symvar_arg->getId(), symvar_arg)); 573 | std::cout << "Load [EBP+0x" << std::hex << offset << "]" << std::endl; 574 | 575 | // test i guess 576 | char v[1024]; 577 | sprintf_s(v, 1024, "[SP+0x%llX]", offset); 578 | 579 | // TempVar = ARG (possibly pop) 580 | auto temp_variable = IR::Variable::create_variable(mem.getSize()); 581 | auto assign = std::make_shared(temp_variable, std::make_shared(v, (IR::ir_size)mem.getSize())); 582 | context->m_statements.push_back(assign); 583 | context->m_expression_map[symvar_arg->getId()] = temp_variable; 584 | symvar_arg->setAlias(temp_variable->get_name()); 585 | } 586 | else if (this->is_fetch_arguments(lea_ast, context)) 587 | { 588 | // lea_ast == VM_REG_X 589 | triton::arch::Register segment_register = mem.getConstSegmentRegister(); 590 | if (segment_register.getId() == triton::arch::ID_REG_INVALID) 591 | { 592 | // DS? 593 | //segment_register = triton_api->registers.x86_ds; 594 | } 595 | triton::engines::symbolic::SharedSymbolicVariable symvar_source = get_symbolic_var(lea_ast); 596 | 597 | const triton::engines::symbolic::SharedSymbolicVariable symvar = triton_api->symbolizeMemory(mem); 598 | std::cout << "Deref(" << lea_ast << "," << segment_register.getName() << ")" << std::endl; 599 | 600 | // IR 601 | auto it = context->m_expression_map.find(symvar_source->getId()); 602 | if (it == context->m_expression_map.end()) 603 | throw std::runtime_error("what do you mean"); 604 | 605 | // declare Temp 606 | auto temp_variable = IR::Variable::create_variable(mem.getSize()); 607 | 608 | // Temp = deref(expr) 609 | std::shared_ptr expr = it->second; 610 | std::shared_ptr deref = std::make_shared(expr, (IR::ir_segment)segment_register.getId(), (IR::ir_size)mem.getSize()); 611 | context->m_statements.push_back(std::make_shared(temp_variable, deref)); 612 | context->m_expression_map[symvar->getId()] = temp_variable; 613 | symvar->setAlias(temp_variable->get_name()); 614 | } 615 | else 616 | { 617 | std::cout << "unknown read addr: " << std::hex << address << " " << lea_ast << std::endl; 618 | } 619 | } 620 | std::vector> VMProtectAnalyzer::save_expressions(triton::arch::Instruction &triton_instruction, VMPHandlerContext *context) 621 | { 622 | std::vector> expressions; 623 | if (!is_unary_operation(triton_instruction) && !is_binary_operation(triton_instruction)) 624 | { 625 | return expressions; 626 | } 627 | 628 | bool do_it = false; 629 | for (const auto& operand : triton_instruction.operands) 630 | { 631 | if (operand.getType() == triton::arch::operand_e::OP_IMM) 632 | { 633 | expressions.push_back(std::make_shared( 634 | operand.getConstImmediate().getValue())); 635 | } 636 | else if (operand.getType() == triton::arch::operand_e::OP_MEM) 637 | { 638 | const triton::arch::MemoryAccess& _mem = operand.getConstMemory(); 639 | triton::engines::symbolic::SharedSymbolicVariable _symvar = get_symbolic_var(triton_api->processSimplification(triton_api->getMemoryAst(_mem), true)); 640 | if (_symvar) 641 | { 642 | // load symbolic 643 | auto _it = context->m_expression_map.find(_symvar->getId()); 644 | if (_it != context->m_expression_map.end()) 645 | { 646 | expressions.push_back(_it->second); 647 | do_it = true; 648 | continue; 649 | } 650 | } 651 | 652 | // otherwise immediate 653 | expressions.push_back(std::make_shared( 654 | triton_api->getConcreteMemoryValue(_mem).convert_to())); 655 | } 656 | else if (operand.getType() == triton::arch::operand_e::OP_REG) 657 | { 658 | const triton::arch::Register& _reg = operand.getConstRegister(); 659 | triton::engines::symbolic::SharedSymbolicVariable _symvar = get_symbolic_var(triton_api->processSimplification(triton_api->getRegisterAst(_reg), true)); 660 | if (_symvar) 661 | { 662 | if (_symvar->getId() == context->symvar_stack->getId()) 663 | { 664 | // nope... 665 | do_it = false; 666 | break; 667 | } 668 | 669 | // load symbolic 670 | auto _it = context->m_expression_map.find(_symvar->getId()); 671 | if (_it != context->m_expression_map.end()) 672 | { 673 | expressions.push_back(_it->second); 674 | do_it = true; 675 | continue; 676 | } 677 | } 678 | 679 | // otherwise immediate 680 | expressions.push_back(std::make_shared( 681 | triton_api->getConcreteRegisterValue(_reg).convert_to())); 682 | } 683 | else 684 | throw std::runtime_error("invalid operand type"); 685 | } 686 | if (!do_it) 687 | expressions.clear(); 688 | return expressions; 689 | } 690 | void VMProtectAnalyzer::check_arity_operation(triton::arch::Instruction &triton_instruction, const std::vector> &operands_expressions, VMPHandlerContext *context) 691 | { 692 | if (triton_instruction.getType() == triton::arch::x86::ID_INS_CPUID) 693 | { 694 | std::shared_ptr statement = std::make_shared(); 695 | context->m_statements.push_back(statement); 696 | 697 | auto symvar_eax = this->triton_api->symbolizeRegister(triton_api->registers.x86_eax); 698 | auto symvar_ebx = this->triton_api->symbolizeRegister(triton_api->registers.x86_ebx); 699 | auto symvar_ecx = this->triton_api->symbolizeRegister(triton_api->registers.x86_ecx); 700 | auto symvar_edx = this->triton_api->symbolizeRegister(triton_api->registers.x86_edx); 701 | context->m_expression_map[symvar_eax->getId()] = std::make_shared(triton_api->registers.x86_eax); 702 | context->m_expression_map[symvar_ebx->getId()] = std::make_shared(triton_api->registers.x86_ebx); 703 | context->m_expression_map[symvar_ecx->getId()] = std::make_shared(triton_api->registers.x86_ecx); 704 | context->m_expression_map[symvar_edx->getId()] = std::make_shared(triton_api->registers.x86_edx); 705 | symvar_eax->setAlias("cpuid_eax"); 706 | symvar_ebx->setAlias("cpuid_ebx"); 707 | symvar_ecx->setAlias("cpuid_ecx"); 708 | symvar_edx->setAlias("cpuid_edx"); 709 | return; 710 | } 711 | else if (triton_instruction.getType() == triton::arch::x86::ID_INS_RDTSC) 712 | { 713 | std::shared_ptr statement = std::make_shared(); 714 | context->m_statements.push_back(statement); 715 | 716 | auto symvar_eax = this->triton_api->symbolizeRegister(triton_api->registers.x86_eax); 717 | auto symvar_edx = this->triton_api->symbolizeRegister(triton_api->registers.x86_edx); 718 | context->m_expression_map[symvar_eax->getId()] = std::make_shared(triton_api->registers.x86_eax); 719 | context->m_expression_map[symvar_edx->getId()] = std::make_shared(triton_api->registers.x86_edx); 720 | symvar_eax->setAlias("rdtsc_eax"); 721 | symvar_edx->setAlias("rdtsc_edx"); 722 | return; 723 | } 724 | 725 | bool unary = is_unary_operation(triton_instruction) && operands_expressions.size() == 1; 726 | bool binary = is_binary_operation(triton_instruction) && operands_expressions.size() == 2; 727 | if (!unary && !binary) 728 | return; 729 | 730 | // symbolize left operand 731 | triton::engines::symbolic::SharedSymbolicVariable symvar; 732 | const auto &operand0 = triton_instruction.operands[0]; 733 | if (operand0.getType() == triton::arch::operand_e::OP_REG) 734 | { 735 | const triton::arch::Register& _reg = operand0.getConstRegister(); 736 | triton_api->concretizeRegister(_reg); 737 | symvar = triton_api->symbolizeRegister(_reg); 738 | } 739 | else if (operand0.getType() == triton::arch::operand_e::OP_MEM) 740 | { 741 | const triton::arch::MemoryAccess& _mem = operand0.getConstMemory(); 742 | triton_api->concretizeMemory(_mem); 743 | symvar = triton_api->symbolizeMemory(_mem); 744 | } 745 | else 746 | { 747 | throw std::runtime_error("invalid operand type"); 748 | } 749 | 750 | 751 | std::shared_ptr temp_variable = IR::Variable::create_variable(operand0.getSize()); 752 | std::shared_ptr expr; 753 | if (unary) 754 | { 755 | // unary 756 | auto op0_expression = operands_expressions[0]; 757 | switch (triton_instruction.getType()) 758 | { 759 | case triton::arch::x86::ID_INS_INC: 760 | { 761 | expr = std::make_shared(op0_expression); 762 | break; 763 | } 764 | case triton::arch::x86::ID_INS_DEC: 765 | { 766 | expr = std::make_shared(op0_expression); 767 | break; 768 | } 769 | case triton::arch::x86::ID_INS_NEG: 770 | { 771 | expr = std::make_shared(op0_expression); 772 | break; 773 | } 774 | case triton::arch::x86::ID_INS_NOT: 775 | { 776 | expr = std::make_shared(op0_expression); 777 | break; 778 | } 779 | default: 780 | { 781 | throw std::runtime_error("unknown unary operation"); 782 | } 783 | } 784 | } 785 | else 786 | { 787 | // binary 788 | auto op0_expression = operands_expressions[0]; 789 | auto op1_expression = operands_expressions[1]; 790 | switch (triton_instruction.getType()) 791 | { 792 | case triton::arch::x86::ID_INS_ADD: 793 | { 794 | expr = std::make_shared(op0_expression, op1_expression); 795 | break; 796 | } 797 | case triton::arch::x86::ID_INS_SUB: 798 | { 799 | expr = std::make_shared(op0_expression, op1_expression); 800 | break; 801 | } 802 | case triton::arch::x86::ID_INS_SHL: 803 | { 804 | expr = std::make_shared(op0_expression, op1_expression); 805 | break; 806 | } 807 | case triton::arch::x86::ID_INS_SHR: 808 | { 809 | expr = std::make_shared(op0_expression, op1_expression); 810 | break; 811 | } 812 | case triton::arch::x86::ID_INS_RCR: 813 | { 814 | expr = std::make_shared(op0_expression, op1_expression); 815 | break; 816 | } 817 | case triton::arch::x86::ID_INS_RCL: 818 | { 819 | expr = std::make_shared(op0_expression, op1_expression); 820 | break; 821 | } 822 | case triton::arch::x86::ID_INS_ROL: 823 | { 824 | expr = std::make_shared(op0_expression, op1_expression); 825 | break; 826 | } 827 | case triton::arch::x86::ID_INS_ROR: 828 | { 829 | expr = std::make_shared(op0_expression, op1_expression); 830 | break; 831 | } 832 | case triton::arch::x86::ID_INS_AND: 833 | { 834 | expr = std::make_shared(op0_expression, op1_expression); 835 | break; 836 | } 837 | case triton::arch::x86::ID_INS_OR: 838 | { 839 | expr = std::make_shared(op0_expression, op1_expression); 840 | break; 841 | } 842 | case triton::arch::x86::ID_INS_XOR: 843 | { 844 | expr = std::make_shared(op0_expression, op1_expression); 845 | break; 846 | } 847 | case triton::arch::x86::ID_INS_CMP: 848 | { 849 | expr = std::make_shared(op0_expression, op1_expression); 850 | break; 851 | } 852 | case triton::arch::x86::ID_INS_TEST: 853 | { 854 | expr = std::make_shared(op0_expression, op1_expression); 855 | break; 856 | } 857 | default: 858 | { 859 | throw std::runtime_error("unknown binary operation"); 860 | } 861 | } 862 | } 863 | context->m_statements.push_back(std::make_shared(temp_variable, expr)); 864 | context->m_expression_map[symvar->getId()] = temp_variable; 865 | symvar->setAlias(temp_variable->get_name()); 866 | } 867 | void VMProtectAnalyzer::check_store_access(triton::arch::Instruction &triton_instruction, VMPHandlerContext *context) 868 | { 869 | const auto& storeAccess = triton_instruction.getStoreAccess(); 870 | for (const std::pair& pair : storeAccess) 871 | { 872 | const triton::arch::MemoryAccess &mem = pair.first; 873 | //const triton::ast::SharedAbstractNode &mem_ast = pair.second; 874 | const triton::ast::SharedAbstractNode &mem_ast = triton_api->getMemoryAst(mem); 875 | const triton::uint64 address = mem.getAddress(); 876 | triton::ast::SharedAbstractNode lea_ast = mem.getLeaAst(); 877 | if (!lea_ast) 878 | { 879 | // most likely can be ignored 880 | continue; 881 | } 882 | 883 | lea_ast = triton_api->processSimplification(lea_ast, true); 884 | if (!lea_ast->isSymbolized()) 885 | { 886 | // most likely can be ignored 887 | continue; 888 | } 889 | 890 | if (this->is_scratch_area_address(lea_ast, context)) 891 | { 892 | const triton::uint64 scratch_offset = lea_ast->evaluate().convert_to() - context->x86_sp; 893 | std::cout << "modifies [x86_sp + 0x" << std::hex << scratch_offset << "]" << std::endl; 894 | 895 | // create IR (VM_REG = mem_ast) 896 | auto source_node = triton_api->processSimplification(mem_ast, true); 897 | triton::engines::symbolic::SharedSymbolicVariable symvar = get_symbolic_var(source_node); 898 | if (symvar) 899 | { 900 | auto ir_imm = std::make_shared(scratch_offset); 901 | std::shared_ptr v1 = std::make_shared(ir_imm, IR::ir_segment_scratch, (IR::ir_size)mem.getSize()); 902 | auto it = context->m_expression_map.find(symvar->getId()); 903 | if (it != context->m_expression_map.end()) 904 | { 905 | std::shared_ptr expr = it->second; 906 | context->m_statements.push_back(std::make_shared(v1, expr)); 907 | } 908 | else if (symvar->getId() == context->symvar_stack->getId()) 909 | { 910 | std::shared_ptr expr = std::make_shared(this->get_sp_register()); 911 | context->m_statements.push_back(std::make_shared(v1, expr)); 912 | } 913 | else if (symvar->getAlias().find("eflags") != std::string::npos) 914 | { 915 | std::shared_ptr expr = std::make_shared(triton_api->registers.x86_eflags); 916 | context->m_statements.push_back(std::make_shared(v1, expr)); 917 | } 918 | else 919 | { 920 | printf("%s\n", symvar->getAlias().c_str()); 921 | throw std::runtime_error("what do you mean 2"); 922 | } 923 | } 924 | else 925 | { 926 | std::cout << "source_node: " << source_node << std::endl; 927 | } 928 | } 929 | else if (this->is_stack_address(lea_ast, context)) 930 | { 931 | // stores to stack 932 | const triton::uint64 stack_offset = address - context->stack; 933 | 934 | std::shared_ptr expr; 935 | auto get_expr = [this, context](std::shared_ptr ctx, triton::ast::SharedAbstractNode mem_ast) 936 | { 937 | std::shared_ptr expr; 938 | auto simplified_source_node = ctx->processSimplification(mem_ast, true); 939 | if (!simplified_source_node->isSymbolized()) 940 | { 941 | // expression is immediate 942 | expr = std::make_shared(simplified_source_node->evaluate().convert_to()); 943 | } 944 | else 945 | { 946 | triton::engines::symbolic::SharedSymbolicVariable _symvar = get_symbolic_var(simplified_source_node); 947 | if (_symvar) 948 | { 949 | auto _it = context->m_expression_map.find(_symvar->getId()); 950 | if (_it == context->m_expression_map.end()) 951 | { 952 | throw std::runtime_error("what do you mean..."); 953 | } 954 | expr = _it->second; 955 | } 956 | } 957 | return expr; 958 | }; 959 | expr = get_expr(this->triton_api, mem_ast); 960 | if (!expr && mem.getSize() == 2) 961 | { 962 | const triton::arch::MemoryAccess _mem(mem.getAddress(), 1); 963 | expr = get_expr(this->triton_api, triton_api->getMemoryAst(_mem)); 964 | } 965 | 966 | // should be push 967 | if (expr) 968 | { 969 | auto ir_stack = context->m_expression_map[context->symvar_stack->getId()]; 970 | auto ir_stack_address = std::make_shared(ir_stack, std::make_shared(stack_offset)); 971 | 972 | std::shared_ptr v1 = std::make_shared( 973 | ir_stack_address, (IR::ir_segment)mem.getConstSegmentRegister().getId(), (IR::ir_size)mem.getSize()); 974 | context->m_statements.push_back(std::make_shared(v1, expr)); 975 | } 976 | else 977 | { 978 | std::cout << "unknown store addr: " << std::hex << address << ", lea_ast: " << lea_ast 979 | << ", simplified_source_node: " << triton_api->processSimplification(mem_ast, true) << std::endl; 980 | } 981 | } 982 | else 983 | { 984 | // create IR (VM_REG = mem_ast) 985 | // get right expression 986 | std::shared_ptr expr; 987 | auto simplified_source_node = triton_api->processSimplification(mem_ast, true); 988 | if (!simplified_source_node->isSymbolized()) 989 | { 990 | // expression is immediate 991 | expr = std::make_shared(simplified_source_node->evaluate().convert_to()); 992 | } 993 | else 994 | { 995 | triton::engines::symbolic::SharedSymbolicVariable symvar1 = get_symbolic_var(simplified_source_node); 996 | if (symvar1) 997 | { 998 | auto _it = context->m_expression_map.find(symvar1->getId()); 999 | if (_it == context->m_expression_map.end()) 1000 | { 1001 | throw std::runtime_error("what do you mean..."); 1002 | } 1003 | expr = _it->second; 1004 | } 1005 | } 1006 | 1007 | triton::engines::symbolic::SharedSymbolicVariable symvar0 = get_symbolic_var(lea_ast); 1008 | if (symvar0 && expr) 1009 | { 1010 | auto it0 = context->m_expression_map.find(symvar0->getId()); 1011 | if (it0 != context->m_expression_map.end()) 1012 | { 1013 | std::shared_ptr v1 = std::make_shared(it0->second, 1014 | (IR::ir_segment)mem.getConstSegmentRegister().getId(), (IR::ir_size)mem.getSize()); 1015 | context->m_statements.push_back(std::make_shared(v1, expr)); 1016 | } 1017 | else 1018 | { 1019 | throw std::runtime_error("what do you mean 2"); 1020 | } 1021 | } 1022 | else 1023 | { 1024 | std::cout << "unknown store addr: " << std::hex << address << ", lea_ast: " << lea_ast << ", simplified_source_node: " << simplified_source_node << std::endl; 1025 | } 1026 | } 1027 | } 1028 | } 1029 | 1030 | void VMProtectAnalyzer::analyze_vm_handler(AbstractStream& stream, unsigned long long handler_address) 1031 | { 1032 | this->m_scratch_size = 0xC0; // test 1033 | 1034 | // reset 1035 | triton_api->concretizeAllMemory(); 1036 | triton_api->concretizeAllRegister(); 1037 | 1038 | // allocate scratch area 1039 | const triton::arch::Register bp_register = this->get_bp_register(); 1040 | const triton::arch::Register sp_register = this->get_sp_register(); 1041 | const triton::arch::Register si_register = this->is_x64() ? triton_api->registers.x86_rsi : triton_api->registers.x86_esi; 1042 | const triton::arch::Register ip_register = this->get_ip_register(); 1043 | 1044 | constexpr unsigned long c_stack_base = 0x1000; 1045 | triton_api->setConcreteRegisterValue(bp_register, c_stack_base); 1046 | triton_api->setConcreteRegisterValue(sp_register, c_stack_base - this->m_scratch_size); 1047 | 1048 | unsigned int arg0 = c_stack_base; 1049 | triton_api->setConcreteMemoryAreaValue(c_stack_base, (const triton::uint8*)&arg0, 4); 1050 | 1051 | // ebp = VM's "stack" pointer 1052 | triton::engines::symbolic::SharedSymbolicVariable symvar_stack = triton_api->symbolizeRegister(bp_register); 1053 | 1054 | // esi = pointer to VM bytecode 1055 | triton::engines::symbolic::SharedSymbolicVariable symvar_bytecode = triton_api->symbolizeRegister(si_register); 1056 | 1057 | // x86 stack pointer 1058 | triton::engines::symbolic::SharedSymbolicVariable symvar_x86_sp = triton_api->symbolizeRegister(sp_register); 1059 | 1060 | symvar_stack->setAlias("stack"); 1061 | symvar_bytecode->setAlias("bytecode"); 1062 | symvar_x86_sp->setAlias("sp"); 1063 | 1064 | // yo... 1065 | VMPHandlerContext context; 1066 | context.scratch_area_size = this->is_x64() ? 0x140 : 0x60; 1067 | context.address = handler_address; 1068 | context.stack = triton_api->getConcreteRegisterValue(bp_register).convert_to(); 1069 | context.bytecode = triton_api->getConcreteRegisterValue(si_register).convert_to(); 1070 | context.x86_sp = triton_api->getConcreteRegisterValue(sp_register).convert_to(); 1071 | context.symvar_stack = symvar_stack; 1072 | context.symvar_bytecode = symvar_bytecode; 1073 | context.symvar_x86_sp = symvar_x86_sp; 1074 | 1075 | // expr 1076 | std::shared_ptr ir_stack = std::make_shared("STACK", (IR::ir_size)sp_register.getSize()); 1077 | context.m_expression_map.insert(std::make_pair(symvar_stack->getId(), ir_stack)); 1078 | // 1079 | 1080 | std::shared_ptr basic_block; 1081 | auto handler_it = this->m_handlers.find(handler_address); 1082 | if (handler_it == this->m_handlers.end()) 1083 | { 1084 | basic_block = make_cfg(stream, handler_address); 1085 | this->m_handlers.insert(std::make_pair(handler_address, basic_block)); 1086 | } 1087 | else 1088 | { 1089 | basic_block = handler_it->second; 1090 | } 1091 | 1092 | triton::uint64 expected_return_address = 0; 1093 | for (auto it = basic_block->instructions.begin(); it != basic_block->instructions.end();) 1094 | { 1095 | const std::shared_ptr xed_instruction = *it; 1096 | const std::vector bytes = xed_instruction->get_bytes(); 1097 | bool mem_read = false; 1098 | for (xed_uint_t j = 0, memops = xed_instruction->get_number_of_memory_operands(); j < memops; j++) 1099 | { 1100 | if (xed_instruction->is_mem_read(j)) 1101 | { 1102 | mem_read = true; 1103 | break; 1104 | } 1105 | } 1106 | 1107 | // do stuff with triton 1108 | triton::arch::Instruction triton_instruction; 1109 | triton_instruction.setOpcode(&bytes[0], (triton::uint32)bytes.size()); 1110 | triton_instruction.setAddress(xed_instruction->get_addr()); 1111 | 1112 | // fix ip 1113 | triton_api->setConcreteRegisterValue(ip_register, xed_instruction->get_addr()); 1114 | 1115 | // DIS 1116 | triton_api->disassembly(triton_instruction); 1117 | if (mem_read 1118 | && (triton_instruction.getType() != triton::arch::x86::ID_INS_POP 1119 | && triton_instruction.getType() != triton::arch::x86::ID_INS_POPFD)) // no need but makes life easier 1120 | { 1121 | for (auto& operand : triton_instruction.operands) 1122 | { 1123 | if (operand.getType() == triton::arch::OP_MEM) 1124 | { 1125 | triton_api->getSymbolicEngine()->initLeaAst(operand.getMemory()); 1126 | this->symbolize_memory(operand.getConstMemory(), &context); 1127 | } 1128 | } 1129 | } 1130 | std::vector> operands_expressions = this->save_expressions(triton_instruction, &context); 1131 | 1132 | triton_api->processing(triton_instruction); 1133 | 1134 | // lol 1135 | this->check_arity_operation(triton_instruction, operands_expressions, &context); 1136 | 1137 | // check store 1138 | this->check_store_access(triton_instruction, &context); 1139 | 1140 | if (xed_instruction->get_category() != XED_CATEGORY_UNCOND_BR 1141 | || xed_instruction->get_branch_displacement_width() == 0) 1142 | { 1143 | std::cout << "\t" << triton_instruction << std::endl; 1144 | } 1145 | 1146 | // symbolize eflags 1147 | static std::string ins_name; 1148 | for (const auto& pair : xed_instruction->get_written_registers()) 1149 | { 1150 | if (pair.is_flag()) 1151 | { 1152 | ins_name = xed_instruction->get_name(); 1153 | break; 1154 | } 1155 | } 1156 | if (triton_instruction.getType() == triton::arch::x86::ID_INS_PUSHFD) 1157 | { 1158 | triton::arch::MemoryAccess _mem(this->get_sp(), 4); 1159 | triton::engines::symbolic::SharedSymbolicVariable _symvar = triton_api->symbolizeMemory(_mem); 1160 | _symvar->setAlias(ins_name + "_eflags"); 1161 | 1162 | auto ir_eflags = std::make_shared(triton_api->registers.x86_eflags); 1163 | context.m_expression_map.insert(std::make_pair(_symvar->getId(), ir_eflags)); 1164 | } 1165 | else if (triton_instruction.getType() == triton::arch::x86::ID_INS_PUSHFQ) 1166 | { 1167 | triton::arch::MemoryAccess _mem(this->get_sp(), 8); 1168 | triton::engines::symbolic::SharedSymbolicVariable _symvar = triton_api->symbolizeMemory(_mem); 1169 | _symvar->setAlias(ins_name + "_eflags"); 1170 | 1171 | auto ir_eflags = std::make_shared(triton_api->registers.x86_eflags); 1172 | context.m_expression_map.insert(std::make_pair(_symvar->getId(), ir_eflags)); 1173 | } 1174 | 1175 | if (++it != basic_block->instructions.end()) 1176 | { 1177 | // loop until it reaches end 1178 | continue; 1179 | } 1180 | 1181 | if (triton_instruction.getType() == triton::arch::x86::ID_INS_CALL) 1182 | { 1183 | expected_return_address = xed_instruction->get_addr() + 5; 1184 | } 1185 | else if (triton_instruction.getType() == triton::arch::x86::ID_INS_RET) 1186 | { 1187 | if (expected_return_address != 0 && this->get_ip() == expected_return_address) 1188 | { 1189 | basic_block = make_cfg(stream, expected_return_address); 1190 | it = basic_block->instructions.begin(); 1191 | } 1192 | } 1193 | 1194 | while (it == basic_block->instructions.end()) 1195 | { 1196 | if (basic_block->next_basic_block && basic_block->target_basic_block) 1197 | { 1198 | // it ends with conditional branch 1199 | if (triton_instruction.isConditionTaken()) 1200 | { 1201 | basic_block = basic_block->target_basic_block; 1202 | } 1203 | else 1204 | { 1205 | basic_block = basic_block->next_basic_block; 1206 | } 1207 | } 1208 | else if (basic_block->target_basic_block) 1209 | { 1210 | // it ends with jmp? 1211 | basic_block = basic_block->target_basic_block; 1212 | } 1213 | else if (basic_block->next_basic_block) 1214 | { 1215 | // just follow :) 1216 | basic_block = basic_block->next_basic_block; 1217 | } 1218 | else 1219 | { 1220 | // perhaps finishes? 1221 | goto l_categorize_handler; 1222 | } 1223 | it = basic_block->instructions.begin(); 1224 | } 1225 | } 1226 | 1227 | l_categorize_handler: 1228 | this->categorize_handler(&context); 1229 | } 1230 | void VMProtectAnalyzer::analyze_vm_exit(unsigned long long handler_address) 1231 | { 1232 | // not the best impl but faspofkapwskefo 1233 | std::stack modified_registers; 1234 | const triton::arch::Register rb_register = this->is_x64() ? triton_api->registers.x86_rbp : triton_api->registers.x86_ebp; 1235 | const triton::uint64 previous_stack = triton_api->getConcreteRegisterValue(rb_register).convert_to(); 1236 | 1237 | std::shared_ptr basic_block = this->m_handlers[handler_address]; 1238 | for (auto it = basic_block->instructions.begin(); it != basic_block->instructions.end();) 1239 | { 1240 | const auto instruction = *it; 1241 | const std::vector bytes = instruction->get_bytes(); 1242 | 1243 | // do stuff with triton 1244 | triton::arch::Instruction triton_instruction; 1245 | triton_instruction.setOpcode(&bytes[0], (triton::uint32)bytes.size()); 1246 | triton_instruction.setAddress(instruction->get_addr()); 1247 | triton_api->processing(triton_instruction); 1248 | 1249 | std::vector written_registers = instruction->get_written_registers(); 1250 | for (const auto& reg : written_registers) 1251 | { 1252 | if (this->is_x64()) 1253 | { 1254 | if ((reg == XED_REG_RFLAGS || reg.get_gpr_class() == XED_REG_CLASS_GPR64) && reg != XED_REG_RSP) 1255 | { 1256 | modified_registers.push(reg); 1257 | } 1258 | } 1259 | else 1260 | { 1261 | if ((reg == XED_REG_EFLAGS || reg.get_gpr_class() == XED_REG_CLASS_GPR32) && reg != XED_REG_ESP) 1262 | { 1263 | modified_registers.push(reg); 1264 | } 1265 | } 1266 | } 1267 | 1268 | if (++it != basic_block->instructions.end()) 1269 | { 1270 | // loop until it reaches end 1271 | std::cout << triton_instruction << std::endl; 1272 | continue; 1273 | } 1274 | 1275 | if (!instruction->is_branch()) 1276 | { 1277 | std::cout << triton_instruction << std::endl; 1278 | } 1279 | 1280 | if (basic_block->next_basic_block && basic_block->target_basic_block) 1281 | { 1282 | // it ends with conditional branch 1283 | if (triton_instruction.isConditionTaken()) 1284 | { 1285 | basic_block = basic_block->target_basic_block; 1286 | } 1287 | else 1288 | { 1289 | basic_block = basic_block->next_basic_block; 1290 | } 1291 | } 1292 | else if (basic_block->target_basic_block) 1293 | { 1294 | // it ends with jmp? 1295 | basic_block = basic_block->target_basic_block; 1296 | } 1297 | else if (basic_block->next_basic_block) 1298 | { 1299 | // just follow :) 1300 | basic_block = basic_block->next_basic_block; 1301 | } 1302 | else 1303 | { 1304 | // perhaps finishes? 1305 | break; 1306 | } 1307 | 1308 | it = basic_block->instructions.begin(); 1309 | } 1310 | 1311 | std::set _set; 1312 | std::stack _final; 1313 | while (!modified_registers.empty()) 1314 | { 1315 | x86_register r = modified_registers.top(); 1316 | modified_registers.pop(); 1317 | 1318 | if (_set.count(r) == 0) 1319 | { 1320 | _set.insert(r); 1321 | _final.push(r); 1322 | } 1323 | } 1324 | 1325 | while (!_final.empty()) 1326 | { 1327 | x86_register r = _final.top(); 1328 | _final.pop(); 1329 | 1330 | std::string s = "pop " + std::string(r.get_name()); 1331 | this->output_strings.push_back(s); 1332 | } 1333 | this->output_strings.push_back("ret"); 1334 | } 1335 | void VMProtectAnalyzer::categorize_handler(VMPHandlerContext *context) 1336 | { 1337 | const triton::arch::Register rb_register = this->is_x64() ? triton_api->registers.x86_rbp : triton_api->registers.x86_ebp; 1338 | const triton::arch::Register sp_register = this->is_x64() ? triton_api->registers.x86_rsp : triton_api->registers.x86_esp; 1339 | const triton::arch::Register si_register = this->is_x64() ? triton_api->registers.x86_rsi : triton_api->registers.x86_esi; 1340 | const triton::uint64 bytecode = triton_api->getConcreteRegisterValue(si_register).convert_to(); 1341 | const triton::uint64 sp = this->get_sp(); 1342 | const triton::uint64 stack = this->get_bp(); 1343 | 1344 | std::cout << "handlers outputs:" << std::endl; 1345 | printf("\tbytecode: 0x%016llX -> 0x%016llX\n", context->bytecode, bytecode); 1346 | printf("\tsp: 0x%016llX -> 0x%016llX\n", context->x86_sp, sp); 1347 | printf("\tstack: 0x%016llX -> 0x%016llX\n", context->stack, stack); 1348 | 1349 | bool handler_detected = false; 1350 | 1351 | // check if push 1352 | triton::sint64 stack_offset = stack - context->stack; // needs to be signed 1353 | if (stack_offset) 1354 | { 1355 | // just for testing purpose 1356 | std::shared_ptr ir_stack = context->m_expression_map[context->symvar_stack->getId()]; 1357 | std::shared_ptr _add = std::make_shared(ir_stack, std::make_shared(stack_offset)); 1358 | context->m_statements.push_back(std::make_shared(ir_stack, _add)); 1359 | } 1360 | 1361 | if (0) 1362 | { 1363 | // convert to push/pop 1364 | 1365 | // constant propagation 1366 | std::map, std::shared_ptr> assigned; 1367 | for (auto it = context->m_statements.begin(); it != context->m_statements.end(); ++it) 1368 | { 1369 | const std::shared_ptr &expr = *it; 1370 | if (expr->get_type() == IR::ir_statement_assign) 1371 | { 1372 | std::shared_ptr _assign = std::dynamic_pointer_cast(expr); 1373 | const auto rvalue = _assign->get_right(); 1374 | if (rvalue->get_type() == IR::expr_unary_operation) 1375 | { 1376 | std::shared_ptr unary_expr = std::dynamic_pointer_cast(rvalue); 1377 | auto assigned_it = assigned.find(unary_expr->get_expression()); 1378 | if (assigned_it != assigned.end()) 1379 | { 1380 | unary_expr->set_expression(assigned_it->second); 1381 | } 1382 | } 1383 | else if (rvalue->get_type() == IR::expr_binary_operation) 1384 | { 1385 | std::shared_ptr binary_op = std::dynamic_pointer_cast(rvalue); 1386 | auto assigned_it = assigned.find(binary_op->get_expression0()); 1387 | if (assigned_it != assigned.end()) 1388 | { 1389 | binary_op->set_expression0(assigned_it->second); 1390 | } 1391 | 1392 | assigned_it = assigned.find(binary_op->get_expression1()); 1393 | if (assigned_it != assigned.end()) 1394 | { 1395 | binary_op->set_expression1(assigned_it->second); 1396 | } 1397 | } 1398 | else if (rvalue->get_type() == IR::expr_variable) 1399 | { 1400 | auto assigned_it = assigned.find(rvalue); 1401 | if (assigned_it != assigned.end() 1402 | && assigned.find(assigned_it->second) == assigned.end()) 1403 | { 1404 | _assign->set_right(assigned_it->second); 1405 | } 1406 | } 1407 | else if (rvalue->get_type() == IR::expr_deref) 1408 | { 1409 | std::shared_ptr deref_expr = std::dynamic_pointer_cast(rvalue); 1410 | auto assigned_it = assigned.find(deref_expr->get_expression()); 1411 | if (assigned_it != assigned.end()) 1412 | { 1413 | deref_expr->set_expression(assigned_it->second); 1414 | } 1415 | } 1416 | 1417 | assigned[_assign->get_left()] = _assign->get_right(); 1418 | } 1419 | } 1420 | 1421 | // simplify 1422 | for (int i = 0; i < 10; i++) 1423 | { 1424 | for (auto it = context->m_statements.begin(); it != context->m_statements.end(); ++it) 1425 | { 1426 | const std::shared_ptr statement = *it; 1427 | switch (statement->get_type()) 1428 | { 1429 | case IR::ir_statement_assign: 1430 | { 1431 | std::shared_ptr _assign = std::dynamic_pointer_cast(statement); 1432 | std::shared_ptr simplified_rvalue = simplify_expression(_assign->get_right()); 1433 | _assign->set_right(simplified_rvalue); 1434 | *it = _assign; 1435 | break; 1436 | } 1437 | case IR::ir_statement_push: 1438 | { 1439 | std::shared_ptr _statement = std::dynamic_pointer_cast(statement); 1440 | std::shared_ptr simplified_rvalue = simplify_expression(_statement->get_expression()); 1441 | _statement->set_expression(simplified_rvalue); 1442 | *it = _statement; 1443 | break; 1444 | } 1445 | case IR::ir_statement_pop: 1446 | { 1447 | std::shared_ptr _statement = std::dynamic_pointer_cast(statement); 1448 | std::shared_ptr simplified_rvalue = simplify_expression(_statement->get_expression()); 1449 | _statement->set_expression(simplified_rvalue); 1450 | *it = _statement; 1451 | break; 1452 | } 1453 | default: 1454 | break; 1455 | } 1456 | } 1457 | } 1458 | } 1459 | 1460 | for (const std::shared_ptr &expr : context->m_statements) 1461 | { 1462 | std::stringstream ss; 1463 | ss << "\t" << expr; 1464 | output_strings.push_back(ss.str()); 1465 | } 1466 | 1467 | if (!handler_detected) 1468 | { 1469 | //this->print_output(); 1470 | //output_strings.clear(); 1471 | //getchar(); 1472 | } 1473 | } -------------------------------------------------------------------------------- /VMProtectTest/VMProtectAnalyzer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "AbstractStream.hpp" 4 | 5 | struct BasicBlock; 6 | 7 | namespace IR 8 | { 9 | class Expression; 10 | class Statement; 11 | } 12 | 13 | struct VMPHandlerContext 14 | { 15 | // before start 16 | triton::uint64 scratch_area_size; 17 | triton::uint64 address; 18 | triton::uint64 stack, bytecode, x86_sp; 19 | triton::engines::symbolic::SharedSymbolicVariable symvar_stack, symvar_bytecode, symvar_x86_sp; 20 | 21 | // load 22 | std::map scratch_variables, arguments; 23 | 24 | // expressions 25 | std::list> m_statements; 26 | std::map> m_expression_map; // associate symbolic variable with IR::Expression 27 | }; 28 | 29 | class VMProtectAnalyzer 30 | { 31 | public: 32 | VMProtectAnalyzer(triton::arch::architecture_e arch = triton::arch::ARCH_X86); 33 | ~VMProtectAnalyzer(); 34 | 35 | // 36 | bool is_x64() const; 37 | 38 | triton::arch::Register get_bp_register() const; 39 | triton::arch::Register get_sp_register() const; 40 | triton::arch::Register get_ip_register() const; 41 | 42 | triton::uint64 get_bp() const; 43 | triton::uint64 get_sp() const; 44 | triton::uint64 get_ip() const; 45 | 46 | // helpers 47 | void symbolize_registers(); 48 | 49 | const triton::arch::Register& get_source_register(const triton::arch::Instruction &triton_instruction) const; 50 | const triton::arch::Register& get_dest_register(const triton::arch::Instruction &triton_instruction) const; 51 | 52 | // lea ast 53 | bool is_bytecode_address(const triton::ast::SharedAbstractNode &lea_ast, VMPHandlerContext *context); 54 | bool is_stack_address(const triton::ast::SharedAbstractNode &lea_ast, VMPHandlerContext *context); 55 | bool is_scratch_area_address(const triton::ast::SharedAbstractNode &lea_ast, VMPHandlerContext *context); 56 | bool is_fetch_arguments(const triton::ast::SharedAbstractNode &lea_ast, VMPHandlerContext *context); 57 | 58 | // work-sub 59 | void categorize_handler(VMPHandlerContext *context); 60 | 61 | // work 62 | void load(AbstractStream& stream, 63 | unsigned long long module_base, unsigned long long vmp0_address, unsigned long long vmp0_size); 64 | 65 | // vm-enter 66 | void analyze_vm_enter(AbstractStream& stream, unsigned long long address); 67 | 68 | // vm-handler 69 | void symbolize_memory(const triton::arch::MemoryAccess& mem, VMPHandlerContext *context); 70 | std::vector> save_expressions(triton::arch::Instruction &triton_instruction, VMPHandlerContext *context); 71 | void check_arity_operation(triton::arch::Instruction &triton_instruction, const std::vector> &operands_expressions, VMPHandlerContext *context); 72 | void check_store_access(triton::arch::Instruction &triton_instruction, VMPHandlerContext *context); 73 | 74 | void analyze_vm_handler(AbstractStream& stream, unsigned long long handler_address); 75 | void analyze_vm_exit(unsigned long long handler_address); 76 | 77 | void print_output() 78 | { 79 | for (const std::string &s : output_strings) 80 | { 81 | std::cout << s << std::endl; 82 | } 83 | } 84 | 85 | private: 86 | std::shared_ptr triton_api; 87 | std::list output_strings; 88 | 89 | // after vm_enter 90 | unsigned long long m_scratch_size; 91 | 92 | // runtimeshit 93 | std::map> m_handlers; 94 | }; -------------------------------------------------------------------------------- /VMProtectTest/VMProtectTest.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {E3F4A203-B840-4F3D-9580-C8E4378CB468} 24 | Win32Proj 25 | VMProtectTest 26 | 10.0.17763.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v141 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v141 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v141 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Use 88 | Level3 89 | Disabled 90 | true 91 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 92 | true 93 | pch.h 94 | 95 | 96 | Console 97 | true 98 | 99 | 100 | 101 | 102 | Use 103 | Level3 104 | Disabled 105 | true 106 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 107 | true 108 | pch.h 109 | 110 | 111 | Console 112 | true 113 | 114 | 115 | 116 | 117 | Use 118 | Level3 119 | MaxSpeed 120 | true 121 | true 122 | true 123 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | true 125 | pch.h 126 | C:\Library\xed\kits\win-ia32\include;%(AdditionalIncludeDirectories) 127 | 128 | 129 | Console 130 | true 131 | true 132 | true 133 | C:\Library\xed\kits\win-ia32\lib;%(AdditionalLibraryDirectories) 134 | 135 | 136 | 137 | 138 | Use 139 | Level3 140 | MaxSpeed 141 | true 142 | true 143 | true 144 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 145 | true 146 | pch.h 147 | E:\Library\boost_1_65_1;C:\Library\Triton\src\libtriton\includes;C:\Library\xed\kits\win-x86-64\include;%(AdditionalIncludeDirectories) 148 | stdcpp14 149 | 150 | 151 | Console 152 | true 153 | true 154 | true 155 | C:\Library\triton.x64.release;C:\Library\xed\kits\win-x86-64\lib;%(AdditionalLibraryDirectories) 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | Create 177 | Create 178 | Create 179 | Create 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /VMProtectTest/VMProtectTest.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {80af6e34-bd16-4b7a-80f8-c1e117331d10} 14 | 15 | 16 | {2b4073a1-8820-4b4e-a4c0-89ee9b82fb27} 17 | 18 | 19 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 20 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 21 | 22 | 23 | {791796c7-8e97-462b-b002-6c10777c8582} 24 | 25 | 26 | {e9d8ef90-a2ce-4ce1-a1c0-78275a7eeadc} 27 | 28 | 29 | 30 | 31 | ヘッダー ファイル 32 | 33 | 34 | xed 35 | 36 | 37 | xed 38 | 39 | 40 | xed 41 | 42 | 43 | stream 44 | 45 | 46 | stream 47 | 48 | 49 | vmp 50 | 51 | 52 | vmp 53 | 54 | 55 | vmp 56 | 57 | 58 | vmp\IR 59 | 60 | 61 | cfg 62 | 63 | 64 | 65 | 66 | ソース ファイル 67 | 68 | 69 | ソース ファイル 70 | 71 | 72 | xed 73 | 74 | 75 | xed 76 | 77 | 78 | xed 79 | 80 | 81 | stream 82 | 83 | 84 | stream 85 | 86 | 87 | vmp 88 | 89 | 90 | vmp 91 | 92 | 93 | vmp 94 | 95 | 96 | vmp\IR 97 | 98 | 99 | cfg 100 | 101 | 102 | -------------------------------------------------------------------------------- /VMProtectTest/VirtualMachine.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "VirtualMachine.hpp" 4 | #include "AbstractStream.hpp" 5 | 6 | // VirtualMachine 7 | VirtualMachine::VirtualMachine() 8 | { 9 | } 10 | VirtualMachine::~VirtualMachine() 11 | { 12 | } 13 | 14 | void VirtualMachine::start_virtual_machine(unsigned long long pos) 15 | { 16 | 17 | } 18 | void VirtualMachine::categorize_handler(unsigned long long pos) 19 | { 20 | m_bytecode; 21 | } -------------------------------------------------------------------------------- /VMProtectTest/VirtualMachine.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class VirtualMachine 4 | { 5 | struct Context 6 | { 7 | triton::uint64 address; 8 | triton::arch::Register _register; 9 | bool is_register; 10 | }; 11 | 12 | public: 13 | VirtualMachine(); 14 | ~VirtualMachine(); 15 | 16 | void start_virtual_machine(unsigned long long pos); 17 | void categorize_handler(unsigned long long pos); 18 | 19 | private: 20 | // themida: sp == stack 21 | Context m_bytecode, m_sp, m_stack; 22 | }; -------------------------------------------------------------------------------- /VMProtectTest/main.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #pragma comment(lib, "xed.lib") 4 | #pragma comment(lib, "triton.lib") 5 | 6 | #include 7 | #include 8 | #include "VMProtectAnalyzer.hpp" 9 | #include "ProcessStream.hpp" 10 | 11 | DWORD find_process(LPCTSTR processName) 12 | { 13 | DWORD processId = 0; 14 | HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 15 | if (hProcessSnap != INVALID_HANDLE_VALUE) 16 | { 17 | // Set the size of the structure before using it. 18 | PROCESSENTRY32 pe32; 19 | pe32.dwSize = sizeof(PROCESSENTRY32); 20 | if (Process32First(hProcessSnap, &pe32)) 21 | { 22 | do 23 | { 24 | static size_t c_nLength = _tcslen(processName); 25 | size_t nLength = _tcslen(pe32.szExeFile); 26 | if (nLength < c_nLength || 27 | _tcscmp(pe32.szExeFile + nLength - c_nLength, processName) != 0) 28 | { 29 | continue; 30 | } 31 | 32 | processId = pe32.th32ProcessID; 33 | break; 34 | } while (Process32Next(hProcessSnap, &pe32)); 35 | } 36 | CloseHandle(hProcessSnap); 37 | } 38 | return processId; 39 | } 40 | 41 | void test_x86_64() 42 | { 43 | ProcessStream stream(true); 44 | if (!stream.open(0x5b08)) 45 | throw std::runtime_error("stream.open failed."); 46 | 47 | unsigned long long module_base = 0x140000000; 48 | unsigned long long vmp0_address = 0x1C000; 49 | unsigned long long vmp0_size = 0xE1F74; 50 | 51 | VMProtectAnalyzer analyzer(triton::arch::ARCH_X86_64); 52 | analyzer.load(stream, module_base, vmp0_address, vmp0_size); // vmp0 53 | analyzer.load(stream, module_base, 0x1B000, 0xA80); // pdata 54 | 55 | //analyzer.analyze_vm_enter(stream, 0x1400FD439); 56 | //analyzer.analyze_vm_enter(stream, 0x1400FD443); 57 | //analyzer.analyze_vm_enter(stream, 0x1400FD44D); 58 | analyzer.analyze_vm_enter(stream, 0x1400FD457); // after messagebox 59 | 60 | triton::uint64 handler_address = analyzer.get_ip(); 61 | while (handler_address) 62 | { 63 | std::cout << std::hex << handler_address << std::endl; 64 | analyzer.analyze_vm_handler(stream, handler_address); 65 | std::cout << std::endl << std::endl << std::endl << std::endl; 66 | handler_address = analyzer.get_ip(); 67 | } 68 | 69 | // idk 70 | std::cout << std::endl << std::endl; 71 | analyzer.print_output(); 72 | } 73 | 74 | void vmp_ultimate() 75 | { 76 | ProcessStream stream; 77 | if (!stream.open(0x5d98)) 78 | throw std::runtime_error("stream.open failed."); 79 | 80 | 81 | VMProtectAnalyzer analyzer; 82 | analyzer.load(stream, 0x00400000, 0x34000, 0x21CEE5); // vmp0 83 | 84 | 85 | analyzer.analyze_vm_enter(stream, 0x00401520); // IsValidImageCRC 86 | 87 | //analyzer.analyze_vm_enter(stream, 0x00401450); 88 | //analyzer.analyze_vm_enter(stream, 0x004014F0); // ultra 89 | //analyzer.analyze_vm_enter(stream, 0x00401490); // mutation 90 | 91 | unsigned long long handler_address = analyzer.get_ip(); 92 | std::cout << std::hex << handler_address << std::endl; 93 | while (0x00400000 <= handler_address && handler_address <= (0x00400000 + 0x34000 + 0x21CEE5)) 94 | { 95 | analyzer.analyze_vm_handler(stream, handler_address); 96 | std::cout << std::endl << std::endl << std::endl << std::endl; 97 | handler_address = analyzer.get_ip(); 98 | } 99 | 100 | // idk 101 | std::cout << std::endl << std::endl; 102 | analyzer.print_output(); 103 | } 104 | 105 | void test_v1() 106 | { 107 | DWORD processId = find_process(L"devirtualizeme32_vmp_3.0.9_v1.exe"); 108 | printf("pid: %08X\n", processId); 109 | 110 | ProcessStream stream; 111 | if (!stream.open(processId)) 112 | throw std::runtime_error("stream.open failed."); 113 | 114 | VMProtectAnalyzer analyzer; 115 | analyzer.load(stream, 0x00400000, 0x17000, 0x86CB0); 116 | analyzer.analyze_vm_enter(stream, 0x0040C890); 117 | //analyzer.analyze_vm_enter(stream, 0x004312D7); 118 | //analyzer.analyze_vm_enter(stream, 0x0041F618); 119 | //analyzer.analyze_vm_enter(stream, 0x00477CBB); 120 | 121 | unsigned long long handler_address = analyzer.get_ip(); 122 | while (handler_address) 123 | { 124 | std::cout << std::hex << handler_address << std::endl; 125 | analyzer.analyze_vm_handler(stream, handler_address); 126 | std::cout << std::endl << std::endl << std::endl << std::endl; 127 | handler_address = analyzer.get_ip(); 128 | } 129 | 130 | // idk 131 | std::cout << std::endl << std::endl; 132 | analyzer.print_output(); 133 | } 134 | void test_demo() 135 | { 136 | DWORD processId = find_process(L"Demo.vmp.exe"); 137 | printf("pid: %08X\n", processId); 138 | 139 | ProcessStream stream; 140 | if (!stream.open(processId)) 141 | throw std::runtime_error("stream.open failed."); 142 | 143 | VMProtectAnalyzer analyzer; 144 | analyzer.load(stream, 0x01150000, 0x4000, 0x8D620); 145 | 146 | if (false) 147 | { 148 | // run through 149 | analyzer.analyze_vm_enter(stream, 0x01151020); 150 | triton::uint64 handler_address = analyzer.get_ip(); 151 | std::cout << std::hex << handler_address << std::endl; 152 | while (handler_address) 153 | { 154 | analyzer.analyze_vm_handler(stream, handler_address); 155 | std::cout << std::endl << std::endl << std::endl << std::endl; 156 | handler_address = analyzer.get_ip(); 157 | } 158 | } 159 | else 160 | { 161 | // test handlers 162 | for (int i = 20; i <= 0xFF; i++) 163 | { 164 | triton::uint64 handler_address = 0; 165 | stream.seek(0x011C4294 + (i * 4)); 166 | stream.read(&handler_address, 4); 167 | 168 | std::cout << i << " " << std::hex << handler_address << std::endl; 169 | analyzer.analyze_vm_handler(stream, handler_address); 170 | std::cout << std::endl << std::endl << std::endl << std::endl; 171 | } 172 | } 173 | // idk 174 | std::cout << std::endl << std::endl; 175 | analyzer.print_output(); 176 | } 177 | 178 | 179 | 180 | int main() 181 | { 182 | // Once, before using Intel XED, you must call xed_tables_init() to initialize the tables Intel XED uses for encoding and decoding: 183 | xed_tables_init(); 184 | 185 | try 186 | { 187 | //test_demo(); 188 | test_v1(); 189 | } 190 | catch (const std::exception &ex) 191 | { 192 | std::cout << ex.what() << std::endl; 193 | } 194 | return 0; 195 | } -------------------------------------------------------------------------------- /VMProtectTest/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: プリコンパイル済みヘッダーに対応するソース ファイル。コンパイルが正常に実行されるために必要です 2 | 3 | #include "pch.h" 4 | 5 | // 一般に、このファイルは無視できますが、プリコンパイル済みヘッダーを使用している場合は保持します。 6 | -------------------------------------------------------------------------------- /VMProtectTest/pch.h: -------------------------------------------------------------------------------- 1 | // 作業を開始するためのヒント: 2 | // 1. ソリューション エクスプローラー ウィンドウを使用してファイルを追加/管理します 3 | // 2. チーム エクスプローラー ウィンドウを使用してソース管理に接続します 4 | // 3. 出力ウィンドウを使用して、ビルド出力とその他のメッセージを表示します 5 | // 4. エラー一覧ウィンドウを使用してエラーを表示します 6 | // 5. [プロジェクト] > [新しい項目の追加] と移動して新しいコード ファイルを作成するか、[プロジェクト] > [既存の項目の追加] と移動して既存のコード ファイルをプロジェクトに追加します 7 | // 6. 後ほどこのプロジェクトを再び開く場合、[ファイル] > [開く] > [プロジェクト] と移動して .sln ファイルを選択します 8 | 9 | #ifndef PCH_H 10 | #define PCH_H 11 | 12 | // https://github.com/intelxed/xed 13 | extern "C" 14 | { 15 | #include 16 | } 17 | 18 | // TODO: ここでプリコンパイルするヘッダーを追加します 19 | #include 20 | 21 | // C++ 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | // triton 34 | #include 35 | #include 36 | #include 37 | 38 | #endif //PCH_H 39 | -------------------------------------------------------------------------------- /VMProtectTest/x86_instruction.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "x86_instruction.hpp" 4 | 5 | x86_instruction::x86_instruction(unsigned long long addr) : m_addr(addr) 6 | { 7 | } 8 | x86_instruction::~x86_instruction() 9 | { 10 | } 11 | 12 | void x86_instruction::decode(const void* buf, unsigned int length, 13 | xed_machine_mode_enum_t mmode, xed_address_width_enum_t stack_addr_width) 14 | { 15 | // initialize for xed_decode 16 | xed_decoded_inst_t *xedd = this; 17 | xed_decoded_inst_zero(xedd); 18 | xed_decoded_inst_set_mode(xedd, mmode, stack_addr_width); 19 | 20 | // decode array of bytes to xed_decoded_inst_t 21 | memcpy(this->m_bytes, buf, length); 22 | xed_error_enum_t xed_error = xed_decode(xedd, this->m_bytes, length); 23 | switch (xed_error) 24 | { 25 | case XED_ERROR_NONE: // OK 26 | { 27 | break; 28 | } 29 | default: 30 | { 31 | std::cout << std::hex << this->m_addr << length << std::endl; 32 | throw std::runtime_error("xed_decode failed"); 33 | } 34 | } 35 | } 36 | 37 | const x86_operand x86_instruction::get_operand(unsigned int i) const & 38 | { 39 | const xed_inst_t* xi = xed_decoded_inst_inst(this); 40 | return x86_operand(xed_inst_operand(xi, i)); 41 | } 42 | std::vector x86_instruction::get_operands() const 43 | { 44 | std::vector operands; 45 | const xed_inst_t *xi = xed_decoded_inst_inst(this); 46 | const uint32_t noperands = xed_inst_noperands(xi); 47 | for (uint32_t i = 0; i < noperands; i++) 48 | { 49 | const xed_operand_t *operand = xed_inst_operand(xi, i); 50 | operands.push_back(x86_operand(operand)); 51 | } 52 | return operands; 53 | } 54 | std::vector x86_instruction::get_bytes() const 55 | { 56 | std::vector bytes; 57 | xed_uint_t len = this->get_length(); 58 | for (xed_uint_t i = 0; i < len; i++) 59 | bytes.push_back(this->get_byte(i)); 60 | return bytes; 61 | } 62 | 63 | void x86_instruction::get_read_written_registers(std::vector* read_registers, std::vector* written_registers) const 64 | { 65 | const std::vector operands = this->get_operands(); 66 | for (const x86_operand& operand : operands) 67 | { 68 | x86_register targetReg; 69 | bool hasRead = false, hasWritten = false; 70 | if (operand.is_register()) 71 | { 72 | // Operand is register 73 | targetReg = this->get_register(operand.get_name()); 74 | hasRead = operand.is_read(); 75 | hasWritten = operand.is_written(); 76 | } 77 | else if (operand.is_memory()) 78 | { 79 | // Ignore memory 80 | continue; 81 | } 82 | else if (operand.is_immediate()) 83 | { 84 | // Ignore immediate 85 | continue; 86 | } 87 | else if (operand.get_name() == XED_OPERAND_BASE0 || operand.get_name() == XED_OPERAND_BASE1) 88 | { 89 | // BASE? 90 | targetReg = this->get_register(operand.get_name()); 91 | hasRead = operand.is_read(); 92 | hasWritten = operand.is_written(); 93 | // printf("\t\t%p BASE0/BASE1 %s R:%d W:%d\n", addr, access_register.get_name(), read, write); 94 | } 95 | else if (operand.is_branch()) 96 | { 97 | // Ignore branch 98 | continue; 99 | } 100 | else if (operand.get_name() == XED_OPERAND_AGEN) 101 | { 102 | // Ignore agen 103 | continue; 104 | } 105 | else 106 | { 107 | std::stringstream ss; 108 | ss << __FUNCTION__; 109 | ss << " operand name: " << operand.get_name(); 110 | throw std::invalid_argument(ss.str()); 111 | } 112 | 113 | if (targetReg != XED_REG_STACKPUSH && targetReg != XED_REG_INVALID) 114 | { 115 | if (hasRead) 116 | read_registers->push_back(targetReg); 117 | 118 | if (hasWritten) 119 | written_registers->push_back(targetReg); 120 | } 121 | } 122 | 123 | // check memory operands 124 | const xed_uint_t memops = this->get_number_of_memory_operands(); 125 | for (xed_uint_t i = 0; i < memops; i++) 126 | { 127 | const x86_register baseReg = this->get_base_register(i); 128 | const x86_register indexReg = this->get_index_register(i); 129 | const x86_register segReg = this->get_segment_register(i); 130 | 131 | if (baseReg) read_registers->push_back(baseReg); 132 | if (indexReg) read_registers->push_back(indexReg); 133 | if (segReg) read_registers->push_back(segReg); 134 | } 135 | } 136 | std::vector x86_instruction::get_read_registers() const 137 | { 138 | std::vector readRegs, writtenRegs; 139 | this->get_read_written_registers(&readRegs, &writtenRegs); 140 | return readRegs; 141 | } 142 | std::vector x86_instruction::get_written_registers() const 143 | { 144 | std::vector readRegs, writtenRegs; 145 | this->get_read_written_registers(&readRegs, &writtenRegs); 146 | return writtenRegs; 147 | } 148 | 149 | bool x86_instruction::is_branch() const 150 | { 151 | switch (this->get_category()) 152 | { 153 | case XED_CATEGORY_COND_BR: 154 | case XED_CATEGORY_UNCOND_BR: 155 | return true; 156 | 157 | default: 158 | return false; 159 | } 160 | } 161 | 162 | std::string x86_instruction::get_string() const 163 | { 164 | char buf[64]; 165 | this->sprintf(buf, 64); 166 | return buf; 167 | } 168 | void x86_instruction::sprintf(char* buf, int length) const 169 | { 170 | xed_format_context(XED_SYNTAX_INTEL, this, buf, length, this->m_addr, 0, 0); 171 | } 172 | void x86_instruction::print() const 173 | { 174 | char buf[64]; 175 | this->sprintf(buf, 64); 176 | printf("%016llX %s\n", this->get_addr(), buf); 177 | } -------------------------------------------------------------------------------- /VMProtectTest/x86_instruction.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "x86_operand.hpp" 4 | #include "x86_register.hpp" 5 | 6 | // The xed_decoded_inst_t has more information than the xed_encoder_request_t, 7 | // but both types are derived from a set of common fields called the xed_operand_values_t. 8 | 9 | // The decoder has an operands array that holds order of the decoded operands. 10 | // This array indicates whether or not the operands are read or written. 11 | class x86_instruction : private xed_decoded_inst_t 12 | { 13 | public: 14 | x86_instruction(unsigned long long addr = 0); 15 | ~x86_instruction(); 16 | 17 | void decode(const void *buf, unsigned int length, 18 | xed_machine_mode_enum_t mmode, xed_address_width_enum_t stack_addr_width = XED_ADDRESS_WIDTH_32b); 19 | 20 | // xed functions 21 | inline const char* get_name() const 22 | { 23 | return xed_iclass_enum_t2str(this->get_iclass()); 24 | } 25 | inline xed_category_enum_t get_category() const 26 | { 27 | return xed_decoded_inst_get_category(this); 28 | } 29 | inline xed_extension_enum_t get_extension() const 30 | { 31 | return xed_decoded_inst_get_extension(this); 32 | } 33 | inline xed_isa_set_enum_t get_isa_set() const 34 | { 35 | return xed_decoded_inst_get_isa_set(this); 36 | } 37 | inline xed_iclass_enum_t get_iclass() const 38 | { 39 | return xed_decoded_inst_get_iclass(this); 40 | } 41 | inline xed_uint_t get_machine_mode_bits() const 42 | { 43 | return xed_decoded_inst_get_machine_mode_bits(this); 44 | } 45 | 46 | // bytes 47 | inline xed_uint_t get_length() const 48 | { 49 | return xed_decoded_inst_get_length(this); 50 | } 51 | inline xed_uint_t get_byte(xed_uint_t byte_index) const 52 | { 53 | return xed_decoded_inst_get_byte(this, byte_index); 54 | } 55 | 56 | inline unsigned int get_operand_length_bits(unsigned int operand_index) const 57 | { 58 | return xed_decoded_inst_operand_length_bits(this, operand_index); 59 | } 60 | inline xed_iform_enum_t get_iform_enum() const 61 | { 62 | return xed_decoded_inst_get_iform_enum(this); 63 | } 64 | 65 | // operands 66 | inline const xed_operand_values_t* operands_const() 67 | { 68 | return xed_decoded_inst_operands_const(this); 69 | } 70 | 71 | // register 72 | inline x86_register get_register(xed_operand_enum_t name = XED_OPERAND_REG0) const 73 | { 74 | return xed_decoded_inst_get_reg(this, name); 75 | } 76 | 77 | // memory 78 | inline xed_uint_t get_number_of_memory_operands() const 79 | { 80 | return xed_decoded_inst_number_of_memory_operands(this); 81 | } 82 | inline bool is_mem_read(unsigned int mem_idx = 0) const 83 | { 84 | return xed_decoded_inst_mem_read(this, mem_idx) != 0; 85 | } 86 | inline bool is_mem_written(unsigned int mem_idx = 0) const 87 | { 88 | return xed_decoded_inst_mem_written(this, mem_idx) != 0; 89 | } 90 | inline bool is_mem_written_only(unsigned int mem_idx = 0) const 91 | { 92 | return xed_decoded_inst_mem_written_only(this, mem_idx); 93 | } 94 | inline x86_register get_segment_register(unsigned int mem_idx = 0) const 95 | { 96 | return xed_decoded_inst_get_seg_reg(this, mem_idx); 97 | } 98 | inline x86_register get_base_register(unsigned int mem_idx = 0) const 99 | { 100 | return xed_decoded_inst_get_base_reg(this, mem_idx); 101 | } 102 | inline x86_register get_index_register(unsigned int mem_idx = 0) const 103 | { 104 | return xed_decoded_inst_get_index_reg(this, mem_idx); 105 | } 106 | inline xed_uint_t get_scale(unsigned int mem_idx = 0) const 107 | { 108 | return xed_decoded_inst_get_scale(this, mem_idx); 109 | } 110 | inline xed_uint_t has_displacement() const 111 | { 112 | return xed_operand_values_has_memory_displacement(this); 113 | } 114 | inline xed_int64_t get_memory_displacement(unsigned int mem_idx = 0) const 115 | { 116 | return xed_decoded_inst_get_memory_displacement(this, mem_idx); 117 | } 118 | inline xed_uint_t get_memory_displacement_width(unsigned int mem_idx = 0) const 119 | { 120 | return xed_decoded_inst_get_memory_displacement_width(this, mem_idx); 121 | } 122 | inline xed_uint_t get_memory_displacement_width_bits(unsigned int mem_idx = 0) const 123 | { 124 | return xed_decoded_inst_get_memory_displacement_width_bits(this, mem_idx); 125 | } 126 | inline xed_uint_t get_memory_operand_length(unsigned int mem_idx = 0) const 127 | { 128 | return xed_decoded_inst_get_memory_operand_length(this, mem_idx); 129 | } 130 | 131 | // branch 132 | inline xed_int32_t get_branch_displacement() const 133 | { 134 | return xed_decoded_inst_get_branch_displacement(this); 135 | } 136 | inline xed_uint_t get_branch_displacement_width() const 137 | { 138 | return xed_decoded_inst_get_branch_displacement_width(this); 139 | } 140 | inline xed_uint_t get_branch_displacement_width_bits() const 141 | { 142 | return xed_decoded_inst_get_branch_displacement_width_bits(this); 143 | } 144 | 145 | // immediate 146 | inline xed_uint_t get_immediate_width() const 147 | { 148 | return xed_decoded_inst_get_immediate_width(this); 149 | } 150 | inline xed_uint_t get_immediate_width_bits() const 151 | { 152 | return xed_decoded_inst_get_immediate_width_bits(this); 153 | } 154 | inline bool get_immediate_is_signed() const 155 | { 156 | // Return true if the first immediate (IMM0) is signed. 157 | return xed_decoded_inst_get_immediate_is_signed(this) == 1; 158 | } 159 | inline xed_int32_t get_signed_immediate() const 160 | { 161 | return xed_decoded_inst_get_signed_immediate(this); 162 | } 163 | inline xed_uint64_t get_unsigned_immediate() const 164 | { 165 | if (!this->get_signed_immediate()) 166 | return xed_decoded_inst_get_unsigned_immediate(this); 167 | 168 | return xed_sign_extend_arbitrary_to_64( 169 | xed_decoded_inst_get_signed_immediate(this), this->get_immediate_width_bits()); 170 | } 171 | inline xed_uint8_t get_second_immediate() const 172 | { 173 | return xed_decoded_inst_get_second_immediate(this); 174 | } 175 | 176 | // modification 177 | inline void set_scale(xed_uint_t scale) 178 | { 179 | xed_decoded_inst_set_scale(this, scale); 180 | } 181 | inline void set_memory_displacement(xed_int64_t disp, xed_uint_t length_bytes) 182 | { 183 | xed_decoded_inst_set_memory_displacement(this, disp, length_bytes); 184 | } 185 | inline void set_branch_displacement(xed_int32_t disp, xed_uint_t length_bytes) 186 | { 187 | xed_decoded_inst_set_branch_displacement(this, disp, length_bytes); 188 | } 189 | inline void set_immediate_signed(xed_int32_t x, xed_uint_t length_bytes) 190 | { 191 | xed_decoded_inst_set_immediate_signed(this, x, length_bytes); 192 | } 193 | inline void set_immediate_unsigned(xed_uint64_t x, xed_uint_t length_bytes) 194 | { 195 | xed_decoded_inst_set_immediate_unsigned(this, x, length_bytes); 196 | } 197 | inline void set_memory_displacement_bits(xed_int64_t disp, xed_uint_t length_bits) 198 | { 199 | xed_decoded_inst_set_memory_displacement_bits(this, disp, length_bits); 200 | } 201 | inline void set_branch_displacement_bits(xed_int32_t disp, xed_uint_t length_bits) 202 | { 203 | xed_decoded_inst_set_branch_displacement_bits(this, disp, length_bits); 204 | } 205 | inline void set_immediate_signed_bits(xed_int32_t x, xed_uint_t length_bits) 206 | { 207 | xed_decoded_inst_set_immediate_signed_bits(this, x, length_bits); 208 | } 209 | inline void set_immediate_unsigned_bits(xed_uint64_t x, xed_uint_t length_bits) 210 | { 211 | xed_decoded_inst_set_immediate_unsigned_bits(this, x, length_bits); 212 | } 213 | 214 | // flags 215 | inline xed_bool_t uses_rflags() const 216 | { 217 | return xed_decoded_inst_uses_rflags(this); 218 | } 219 | inline const xed_flag_set_t* get_read_flag_set() const 220 | { 221 | const xed_simple_flag_t* rfi = xed_decoded_inst_get_rflags_info(this); 222 | return xed_simple_flag_get_read_flag_set(rfi); 223 | } 224 | inline const xed_flag_set_t* get_written_flag_set() const 225 | { 226 | const xed_simple_flag_t* rfi = xed_decoded_inst_get_rflags_info(this); 227 | return xed_simple_flag_get_written_flag_set(rfi); 228 | } 229 | 230 | // my functions 231 | inline unsigned long long get_addr() const 232 | { 233 | return this->m_addr; 234 | } 235 | const x86_operand get_operand(unsigned int i) const &; 236 | std::vector get_operands() const; 237 | std::vector get_bytes() const; 238 | 239 | void get_read_written_registers(std::vector* read_registers, std::vector* written_registers) const; 240 | std::vector get_read_registers() const; 241 | std::vector get_written_registers() const; 242 | 243 | // additional 244 | bool is_branch() const; 245 | 246 | // debug functions 247 | std::string get_string() const; 248 | void sprintf(char* buf, int length) const; 249 | void print() const; 250 | 251 | private: 252 | unsigned long long m_addr; 253 | xed_uint8_t m_bytes[16]; 254 | }; -------------------------------------------------------------------------------- /VMProtectTest/x86_operand.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "x86_operand.hpp" 4 | 5 | //x86_operand::x86_operand() 6 | //{ 7 | //} 8 | 9 | x86_operand::x86_operand(const xed_operand_t* op) : m_op(op) 10 | { 11 | } -------------------------------------------------------------------------------- /VMProtectTest/x86_operand.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class x86_operand 4 | { 5 | public: 6 | //x86_operand(); 7 | explicit x86_operand(const xed_operand_t* op); 8 | 9 | // Operands Access 10 | xed_operand_enum_t get_name() const 11 | { 12 | return xed_operand_name(this->m_op); 13 | } 14 | xed_operand_visibility_enum_t get_visibility() const 15 | { 16 | return xed_operand_operand_visibility(this->m_op); 17 | } 18 | xed_operand_type_enum_t get_type() const 19 | { 20 | return xed_operand_type(this->m_op); 21 | } 22 | xed_operand_element_xtype_enum_t get_xtype() const 23 | { 24 | return xed_operand_xtype(this->m_op); 25 | } 26 | xed_operand_width_enum_t get_width() const 27 | { 28 | return xed_operand_width(this->m_op); 29 | } 30 | xed_uint32_t get_width_bits(const xed_uint32_t eosz) const 31 | { 32 | return xed_operand_width_bits(this->m_op, eosz); 33 | } 34 | xed_nonterminal_enum_t get_nonterminal_name() const 35 | { 36 | return xed_operand_nonterminal_name(this->m_op); 37 | } 38 | xed_reg_enum_t get_reg() const // Careful with this one – use xed_decoded_inst_get_reg()! This one is probably not what you think it is. 39 | { 40 | return xed_operand_reg(this->m_op); 41 | } 42 | xed_uint_t template_is_register() const // Careful with this one 43 | { 44 | return xed_operand_template_is_register(this->m_op); 45 | } 46 | xed_uint32_t imm() const 47 | { 48 | return xed_operand_imm(this->m_op); 49 | } 50 | void print(char* buf, int buflen) const 51 | { 52 | xed_operand_print(this->m_op, buf, buflen); 53 | } 54 | 55 | // Operand Enum Name Classification 56 | static xed_uint_t is_register(xed_operand_enum_t name) 57 | { 58 | return xed_operand_is_register(name); 59 | } 60 | static xed_uint_t is_memory_addressing_register(xed_operand_enum_t name) 61 | { 62 | return xed_operand_is_memory_addressing_register(name); 63 | } 64 | 65 | // Operand Read/Written 66 | xed_operand_action_enum_t get_rw() const 67 | { 68 | return xed_operand_rw(this->m_op); 69 | } 70 | bool is_read() const 71 | { 72 | return xed_operand_read(this->m_op) != 0; 73 | } 74 | bool is_read_only() const 75 | { 76 | return xed_operand_read_only(this->m_op) != 0; 77 | } 78 | bool is_written() const 79 | { 80 | return xed_operand_written(this->m_op) != 0; 81 | } 82 | bool is_written_only() const 83 | { 84 | return xed_operand_written_only(this->m_op) != 0; 85 | } 86 | bool is_read_written() const 87 | { 88 | return xed_operand_read_and_written(this->m_op) != 0; 89 | } 90 | bool is_conditional_read() const 91 | { 92 | return xed_operand_conditional_read(this->m_op) != 0; 93 | } 94 | bool is_conditional_written() const 95 | { 96 | return xed_operand_conditional_write(this->m_op) != 0; 97 | } 98 | 99 | // helpers 100 | bool is_register() const 101 | { 102 | return x86_operand::is_register(this->get_name()); 103 | } 104 | bool is_memory() const 105 | { 106 | return this->get_name() == XED_OPERAND_MEM0 || this->get_name() == XED_OPERAND_MEM1; 107 | } 108 | bool is_immediate() const 109 | { 110 | return this->get_name() == XED_OPERAND_IMM0 || this->get_name() == XED_OPERAND_IMM1; 111 | } 112 | bool is_branch() const 113 | { 114 | return this->get_name() == XED_OPERAND_RELBR 115 | //|| this->get_name() == XED_OPERAND_PTR 116 | ; 117 | } 118 | 119 | private: 120 | const xed_operand_t* m_op; 121 | }; -------------------------------------------------------------------------------- /VMProtectTest/x86_register.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "x86_register.hpp" 4 | 5 | x86_register::x86_register(xed_reg_enum_t xed_reg) : m_xed_reg(xed_reg) 6 | { 7 | } 8 | x86_register::~x86_register() 9 | { 10 | } 11 | 12 | bool x86_register::is_gpr() const 13 | { 14 | return this->get_class() == XED_REG_CLASS_GPR; 15 | } 16 | bool x86_register::is_low_gpr() const 17 | { 18 | switch (this->m_xed_reg) 19 | { 20 | case XED_REG_AL: 21 | case XED_REG_CL: 22 | case XED_REG_DL: 23 | case XED_REG_BL: 24 | case XED_REG_SPL: 25 | case XED_REG_BPL: 26 | case XED_REG_SIL: 27 | case XED_REG_DIL: 28 | return true; 29 | 30 | case XED_REG_AH: 31 | case XED_REG_CH: 32 | case XED_REG_DH: 33 | case XED_REG_BH: 34 | return false; 35 | 36 | default: 37 | { 38 | throw std::invalid_argument(__FUNCTION__); 39 | } 40 | } 41 | } 42 | bool x86_register::is_high_gpr() const 43 | { 44 | switch (this->m_xed_reg) 45 | { 46 | case XED_REG_AL: 47 | case XED_REG_CL: 48 | case XED_REG_DL: 49 | case XED_REG_BL: 50 | case XED_REG_SPL: 51 | case XED_REG_BPL: 52 | case XED_REG_SIL: 53 | case XED_REG_DIL: 54 | return false; 55 | 56 | case XED_REG_AH: 57 | case XED_REG_CH: 58 | case XED_REG_DH: 59 | case XED_REG_BH: 60 | return true; 61 | 62 | default: 63 | { 64 | throw std::invalid_argument(__FUNCTION__); 65 | } 66 | } 67 | } 68 | x86_register x86_register::get_gpr8_low() const 69 | { 70 | switch (this->m_xed_reg) 71 | { 72 | case XED_REG_AL: 73 | case XED_REG_AH: 74 | case XED_REG_AX: 75 | case XED_REG_EAX: 76 | case XED_REG_RAX: 77 | return XED_REG_AL; 78 | 79 | case XED_REG_CL: 80 | case XED_REG_CH: 81 | case XED_REG_CX: 82 | case XED_REG_ECX: 83 | case XED_REG_RCX: 84 | return XED_REG_CL; 85 | 86 | case XED_REG_DL: 87 | case XED_REG_DH: 88 | case XED_REG_DX: 89 | case XED_REG_EDX: 90 | case XED_REG_RDX: 91 | return XED_REG_DL; 92 | 93 | case XED_REG_BL: 94 | case XED_REG_BH: 95 | case XED_REG_BX: 96 | case XED_REG_EBX: 97 | case XED_REG_RBX: 98 | return XED_REG_BL; 99 | 100 | case XED_REG_SPL: 101 | case XED_REG_SP: 102 | case XED_REG_ESP: 103 | case XED_REG_RSP: 104 | return XED_REG_SPL; 105 | 106 | case XED_REG_BPL: 107 | case XED_REG_BP: 108 | case XED_REG_EBP: 109 | case XED_REG_RBP: 110 | return XED_REG_BPL; 111 | 112 | case XED_REG_SIL: 113 | case XED_REG_SI: 114 | case XED_REG_ESI: 115 | case XED_REG_RSI: 116 | return XED_REG_SIL; 117 | 118 | case XED_REG_DIL: 119 | case XED_REG_DI: 120 | case XED_REG_EDI: 121 | case XED_REG_RDI: 122 | return XED_REG_DIL; 123 | 124 | case XED_REG_R8B: 125 | case XED_REG_R8W: 126 | case XED_REG_R8D: 127 | case XED_REG_R8: 128 | return XED_REG_R8B; 129 | 130 | case XED_REG_R9B: 131 | case XED_REG_R9W: 132 | case XED_REG_R9D: 133 | case XED_REG_R9: 134 | return XED_REG_R9B; 135 | 136 | case XED_REG_R10B: 137 | case XED_REG_R10W: 138 | case XED_REG_R10D: 139 | case XED_REG_R10: 140 | return XED_REG_R10B; 141 | 142 | case XED_REG_R11B: 143 | case XED_REG_R11W: 144 | case XED_REG_R11D: 145 | case XED_REG_R11: 146 | return XED_REG_R11B; 147 | 148 | case XED_REG_R12B: 149 | case XED_REG_R12W: 150 | case XED_REG_R12D: 151 | case XED_REG_R12: 152 | return XED_REG_R12B; 153 | 154 | case XED_REG_R13B: 155 | case XED_REG_R13W: 156 | case XED_REG_R13D: 157 | case XED_REG_R13: 158 | return XED_REG_R13B; 159 | 160 | case XED_REG_R14B: 161 | case XED_REG_R14W: 162 | case XED_REG_R14D: 163 | case XED_REG_R14: 164 | return XED_REG_R14B; 165 | 166 | case XED_REG_R15B: 167 | case XED_REG_R15W: 168 | case XED_REG_R15D: 169 | case XED_REG_R15: 170 | return XED_REG_R15B; 171 | 172 | default: 173 | { 174 | std::cout << this->get_name() << std::endl; 175 | throw std::runtime_error(__FUNCTION__); 176 | } 177 | } 178 | } 179 | x86_register x86_register::get_gpr8_high() const 180 | { 181 | switch (this->m_xed_reg) 182 | { 183 | case XED_REG_AL: 184 | case XED_REG_AH: 185 | case XED_REG_AX: 186 | case XED_REG_EAX: 187 | case XED_REG_RAX: 188 | return XED_REG_AH; 189 | 190 | case XED_REG_CL: 191 | case XED_REG_CH: 192 | case XED_REG_CX: 193 | case XED_REG_ECX: 194 | case XED_REG_RCX: 195 | return XED_REG_CH; 196 | 197 | case XED_REG_DL: 198 | case XED_REG_DH: 199 | case XED_REG_DX: 200 | case XED_REG_EDX: 201 | case XED_REG_RDX: 202 | return XED_REG_DH; 203 | 204 | case XED_REG_BL: 205 | case XED_REG_BH: 206 | case XED_REG_BX: 207 | case XED_REG_EBX: 208 | case XED_REG_RBX: 209 | return XED_REG_BH; 210 | 211 | case XED_REG_SPL: 212 | case XED_REG_SP: 213 | case XED_REG_ESP: 214 | case XED_REG_RSP: 215 | return XED_REG_INVALID; 216 | 217 | case XED_REG_BPL: 218 | case XED_REG_BP: 219 | case XED_REG_EBP: 220 | case XED_REG_RBP: 221 | return XED_REG_INVALID; 222 | 223 | case XED_REG_SIL: 224 | case XED_REG_SI: 225 | case XED_REG_ESI: 226 | case XED_REG_RSI: 227 | return XED_REG_INVALID; 228 | 229 | case XED_REG_DIL: 230 | case XED_REG_DI: 231 | case XED_REG_EDI: 232 | case XED_REG_RDI: 233 | return XED_REG_INVALID; 234 | 235 | case XED_REG_R8B: 236 | case XED_REG_R8W: 237 | case XED_REG_R8D: 238 | case XED_REG_R8: 239 | return XED_REG_INVALID; 240 | 241 | case XED_REG_R9B: 242 | case XED_REG_R9W: 243 | case XED_REG_R9D: 244 | case XED_REG_R9: 245 | return XED_REG_INVALID; 246 | 247 | case XED_REG_R10B: 248 | case XED_REG_R10W: 249 | case XED_REG_R10D: 250 | case XED_REG_R10: 251 | return XED_REG_INVALID; 252 | 253 | case XED_REG_R11B: 254 | case XED_REG_R11W: 255 | case XED_REG_R11D: 256 | case XED_REG_R11: 257 | return XED_REG_INVALID; 258 | 259 | case XED_REG_R12B: 260 | case XED_REG_R12W: 261 | case XED_REG_R12D: 262 | case XED_REG_R12: 263 | return XED_REG_INVALID; 264 | 265 | case XED_REG_R13B: 266 | case XED_REG_R13W: 267 | case XED_REG_R13D: 268 | case XED_REG_R13: 269 | return XED_REG_INVALID; 270 | 271 | case XED_REG_R14B: 272 | case XED_REG_R14W: 273 | case XED_REG_R14D: 274 | case XED_REG_R14: 275 | return XED_REG_INVALID; 276 | 277 | case XED_REG_R15B: 278 | case XED_REG_R15W: 279 | case XED_REG_R15D: 280 | case XED_REG_R15: 281 | return XED_REG_INVALID; 282 | 283 | default: 284 | { 285 | throw std::runtime_error(__FUNCTION__); 286 | } 287 | } 288 | } 289 | x86_register x86_register::get_gpr16() const 290 | { 291 | switch (this->m_xed_reg) 292 | { 293 | case XED_REG_AL: 294 | case XED_REG_AH: 295 | case XED_REG_AX: 296 | case XED_REG_EAX: 297 | case XED_REG_RAX: 298 | return XED_REG_AX; 299 | 300 | case XED_REG_CL: 301 | case XED_REG_CH: 302 | case XED_REG_CX: 303 | case XED_REG_ECX: 304 | case XED_REG_RCX: 305 | return XED_REG_CX; 306 | 307 | case XED_REG_DL: 308 | case XED_REG_DH: 309 | case XED_REG_DX: 310 | case XED_REG_EDX: 311 | case XED_REG_RDX: 312 | return XED_REG_DX; 313 | 314 | case XED_REG_BL: 315 | case XED_REG_BH: 316 | case XED_REG_BX: 317 | case XED_REG_EBX: 318 | case XED_REG_RBX: 319 | return XED_REG_BX; 320 | 321 | case XED_REG_SPL: 322 | case XED_REG_SP: 323 | case XED_REG_ESP: 324 | case XED_REG_RSP: 325 | return XED_REG_SP; 326 | 327 | case XED_REG_BPL: 328 | case XED_REG_BP: 329 | case XED_REG_EBP: 330 | case XED_REG_RBP: 331 | return XED_REG_BP; 332 | 333 | case XED_REG_SIL: 334 | case XED_REG_SI: 335 | case XED_REG_ESI: 336 | case XED_REG_RSI: 337 | return XED_REG_SI; 338 | 339 | case XED_REG_DIL: 340 | case XED_REG_DI: 341 | case XED_REG_EDI: 342 | case XED_REG_RDI: 343 | return XED_REG_DI; 344 | 345 | case XED_REG_R8B: 346 | case XED_REG_R8W: 347 | case XED_REG_R8D: 348 | case XED_REG_R8: 349 | return XED_REG_R8W; 350 | 351 | case XED_REG_R9B: 352 | case XED_REG_R9W: 353 | case XED_REG_R9D: 354 | case XED_REG_R9: 355 | return XED_REG_R9W; 356 | 357 | case XED_REG_R10B: 358 | case XED_REG_R10W: 359 | case XED_REG_R10D: 360 | case XED_REG_R10: 361 | return XED_REG_R10W; 362 | 363 | case XED_REG_R11B: 364 | case XED_REG_R11W: 365 | case XED_REG_R11D: 366 | case XED_REG_R11: 367 | return XED_REG_R11W; 368 | 369 | case XED_REG_R12B: 370 | case XED_REG_R12W: 371 | case XED_REG_R12D: 372 | case XED_REG_R12: 373 | return XED_REG_R12W; 374 | 375 | case XED_REG_R13B: 376 | case XED_REG_R13W: 377 | case XED_REG_R13D: 378 | case XED_REG_R13: 379 | return XED_REG_R13W; 380 | 381 | case XED_REG_R14B: 382 | case XED_REG_R14W: 383 | case XED_REG_R14D: 384 | case XED_REG_R14: 385 | return XED_REG_R14W; 386 | 387 | case XED_REG_R15B: 388 | case XED_REG_R15W: 389 | case XED_REG_R15D: 390 | case XED_REG_R15: 391 | return XED_REG_R15W; 392 | 393 | default: 394 | { 395 | throw std::runtime_error(__FUNCTION__); 396 | } 397 | } 398 | } 399 | x86_register x86_register::get_gpr32() const 400 | { 401 | switch (this->m_xed_reg) 402 | { 403 | case XED_REG_AL: 404 | case XED_REG_AH: 405 | case XED_REG_AX: 406 | case XED_REG_EAX: 407 | case XED_REG_RAX: 408 | return XED_REG_EAX; 409 | 410 | case XED_REG_CL: 411 | case XED_REG_CH: 412 | case XED_REG_CX: 413 | case XED_REG_ECX: 414 | case XED_REG_RCX: 415 | return XED_REG_ECX; 416 | 417 | case XED_REG_DL: 418 | case XED_REG_DH: 419 | case XED_REG_DX: 420 | case XED_REG_EDX: 421 | case XED_REG_RDX: 422 | return XED_REG_EDX; 423 | 424 | case XED_REG_BL: 425 | case XED_REG_BH: 426 | case XED_REG_BX: 427 | case XED_REG_EBX: 428 | case XED_REG_RBX: 429 | return XED_REG_EBX; 430 | 431 | case XED_REG_SPL: 432 | case XED_REG_SP: 433 | case XED_REG_ESP: 434 | case XED_REG_RSP: 435 | return XED_REG_ESP; 436 | 437 | case XED_REG_BPL: 438 | case XED_REG_BP: 439 | case XED_REG_EBP: 440 | case XED_REG_RBP: 441 | return XED_REG_EBP; 442 | 443 | case XED_REG_SIL: 444 | case XED_REG_SI: 445 | case XED_REG_ESI: 446 | case XED_REG_RSI: 447 | return XED_REG_ESI; 448 | 449 | case XED_REG_DIL: 450 | case XED_REG_DI: 451 | case XED_REG_EDI: 452 | case XED_REG_RDI: 453 | return XED_REG_EDI; 454 | 455 | case XED_REG_R8B: 456 | case XED_REG_R8W: 457 | case XED_REG_R8D: 458 | case XED_REG_R8: 459 | return XED_REG_R8D; 460 | 461 | case XED_REG_R9B: 462 | case XED_REG_R9W: 463 | case XED_REG_R9D: 464 | case XED_REG_R9: 465 | return XED_REG_R9D; 466 | 467 | case XED_REG_R10B: 468 | case XED_REG_R10W: 469 | case XED_REG_R10D: 470 | case XED_REG_R10: 471 | return XED_REG_R10D; 472 | 473 | case XED_REG_R11B: 474 | case XED_REG_R11W: 475 | case XED_REG_R11D: 476 | case XED_REG_R11: 477 | return XED_REG_R11D; 478 | 479 | case XED_REG_R12B: 480 | case XED_REG_R12W: 481 | case XED_REG_R12D: 482 | case XED_REG_R12: 483 | return XED_REG_R12D; 484 | 485 | case XED_REG_R13B: 486 | case XED_REG_R13W: 487 | case XED_REG_R13D: 488 | case XED_REG_R13: 489 | return XED_REG_R13D; 490 | 491 | case XED_REG_R14B: 492 | case XED_REG_R14W: 493 | case XED_REG_R14D: 494 | case XED_REG_R14: 495 | return XED_REG_R14D; 496 | 497 | case XED_REG_R15B: 498 | case XED_REG_R15W: 499 | case XED_REG_R15D: 500 | case XED_REG_R15: 501 | return XED_REG_R15D; 502 | 503 | default: 504 | { 505 | throw std::runtime_error(__FUNCTION__); 506 | } 507 | } 508 | } 509 | x86_register x86_register::get_gpr64() const 510 | { 511 | switch (this->m_xed_reg) 512 | { 513 | case XED_REG_AL: 514 | case XED_REG_AH: 515 | case XED_REG_AX: 516 | case XED_REG_EAX: 517 | case XED_REG_RAX: 518 | return XED_REG_RAX; 519 | 520 | case XED_REG_CL: 521 | case XED_REG_CH: 522 | case XED_REG_CX: 523 | case XED_REG_ECX: 524 | case XED_REG_RCX: 525 | return XED_REG_RCX; 526 | 527 | case XED_REG_DL: 528 | case XED_REG_DH: 529 | case XED_REG_DX: 530 | case XED_REG_EDX: 531 | case XED_REG_RDX: 532 | return XED_REG_RDX; 533 | 534 | case XED_REG_BL: 535 | case XED_REG_BH: 536 | case XED_REG_BX: 537 | case XED_REG_EBX: 538 | case XED_REG_RBX: 539 | return XED_REG_RBX; 540 | 541 | case XED_REG_SPL: 542 | case XED_REG_SP: 543 | case XED_REG_ESP: 544 | case XED_REG_RSP: 545 | return XED_REG_RSP; 546 | 547 | case XED_REG_BPL: 548 | case XED_REG_BP: 549 | case XED_REG_EBP: 550 | case XED_REG_RBP: 551 | return XED_REG_RBP; 552 | 553 | case XED_REG_SIL: 554 | case XED_REG_SI: 555 | case XED_REG_ESI: 556 | case XED_REG_RSI: 557 | return XED_REG_RSI; 558 | 559 | case XED_REG_DIL: 560 | case XED_REG_DI: 561 | case XED_REG_EDI: 562 | case XED_REG_RDI: 563 | return XED_REG_RDI; 564 | 565 | case XED_REG_R8B: 566 | case XED_REG_R8W: 567 | case XED_REG_R8D: 568 | case XED_REG_R8: 569 | return XED_REG_R8; 570 | 571 | case XED_REG_R9B: 572 | case XED_REG_R9W: 573 | case XED_REG_R9D: 574 | case XED_REG_R9: 575 | return XED_REG_R9; 576 | 577 | case XED_REG_R10B: 578 | case XED_REG_R10W: 579 | case XED_REG_R10D: 580 | case XED_REG_R10: 581 | return XED_REG_R10; 582 | 583 | case XED_REG_R11B: 584 | case XED_REG_R11W: 585 | case XED_REG_R11D: 586 | case XED_REG_R11: 587 | return XED_REG_R11; 588 | 589 | case XED_REG_R12B: 590 | case XED_REG_R12W: 591 | case XED_REG_R12D: 592 | case XED_REG_R12: 593 | return XED_REG_R12; 594 | 595 | case XED_REG_R13B: 596 | case XED_REG_R13W: 597 | case XED_REG_R13D: 598 | case XED_REG_R13: 599 | return XED_REG_R13; 600 | 601 | case XED_REG_R14B: 602 | case XED_REG_R14W: 603 | case XED_REG_R14D: 604 | case XED_REG_R14: 605 | return XED_REG_R14; 606 | 607 | case XED_REG_R15B: 608 | case XED_REG_R15W: 609 | case XED_REG_R15D: 610 | case XED_REG_R15: 611 | return XED_REG_R15; 612 | 613 | default: 614 | { 615 | throw std::runtime_error(__FUNCTION__); 616 | } 617 | } 618 | } 619 | 620 | bool x86_register::is_flag() const 621 | { 622 | return XED_REG_FLAGS_FIRST <= this->m_xed_reg && this->m_xed_reg <= XED_REG_RFLAGS; 623 | } -------------------------------------------------------------------------------- /VMProtectTest/x86_register.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class x86_register 4 | { 5 | public: 6 | x86_register(xed_reg_enum_t xed_reg = XED_REG_INVALID); 7 | ~x86_register(); 8 | 9 | bool is_valid() const 10 | { 11 | return this->m_xed_reg != XED_REG_INVALID; 12 | } 13 | 14 | // operators 15 | operator bool() const 16 | { 17 | return this->is_valid(); 18 | } 19 | operator xed_reg_enum_t() const 20 | { 21 | return this->m_xed_reg; 22 | } 23 | bool operator==(const x86_register& cmp) const 24 | { 25 | return this->m_xed_reg == cmp.m_xed_reg; 26 | } 27 | bool operator==(xed_reg_enum_t cmp) const 28 | { 29 | return this->m_xed_reg == cmp; 30 | } 31 | bool operator!=(const x86_register& cmp) const 32 | { 33 | return this->m_xed_reg != cmp.m_xed_reg; 34 | } 35 | bool operator!=(xed_reg_enum_t cmp) const 36 | { 37 | return this->m_xed_reg != cmp; 38 | } 39 | bool operator<(x86_register cmp) const 40 | { 41 | return this->m_xed_reg < cmp.m_xed_reg; 42 | } 43 | 44 | // wrappers 45 | const char* get_name() const 46 | { 47 | return xed_reg_enum_t2str(this->m_xed_reg); 48 | } 49 | xed_reg_class_enum_t get_class() const 50 | { 51 | return xed_reg_class(this->m_xed_reg); 52 | } 53 | const char* get_class_name() const 54 | { 55 | return xed_reg_class_enum_t2str(this->get_class()); 56 | } 57 | 58 | // Returns the specific width GPR reg class (like XED_REG_CLASS_GPR32 or XED_REG_CLASS_GPR64) for a given GPR register. 59 | // Or XED_REG_INVALID if not a GPR. 60 | xed_reg_class_enum_t get_gpr_class() const 61 | { 62 | return xed_gpr_reg_class(this->m_xed_reg); 63 | } 64 | x86_register get_largest_enclosing_register32() const 65 | { 66 | return xed_get_largest_enclosing_register32(this->m_xed_reg); 67 | } 68 | x86_register get_largest_enclosing_register() const 69 | { 70 | return xed_get_largest_enclosing_register(this->m_xed_reg); 71 | } 72 | xed_uint32_t get_width_bits() const 73 | { 74 | return xed_get_register_width_bits(this->m_xed_reg); 75 | } 76 | xed_uint32_t get_width_bits64() const 77 | { 78 | return xed_get_register_width_bits64(this->m_xed_reg); 79 | } 80 | 81 | // helpers - GPR 82 | bool is_gpr() const; 83 | bool is_low_gpr() const; 84 | bool is_high_gpr() const; 85 | x86_register get_gpr8_low() const; // al, cl, dl, bl 86 | x86_register get_gpr8_high() const; // ah, ch, dh, bh 87 | x86_register get_gpr16() const; // ax, cx, dx, bx 88 | x86_register get_gpr32() const; // eax, ecx, edx, ebx 89 | x86_register get_gpr64() const; // rax, rcx, rdx, rbx 90 | 91 | // flag 92 | bool is_flag() const; 93 | 94 | private: 95 | xed_reg_enum_t m_xed_reg; 96 | }; --------------------------------------------------------------------------------