├── .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