├── .gitignore
├── Main.cpp
├── README.md
├── VIFTools.cpp
├── VIFTools.hpp
├── VMPImportFixer.cpp
├── VMPImportFixer.hpp
├── VMPImportFixer.sln
├── VMPImportFixer.vcxproj
├── VMPImportFixer.vcxproj.filters
├── msc
├── Process.cpp
├── Process.hpp
└── ScopedHandle.hpp
└── vendor
├── pepp
├── ExportDirectory.cpp
├── ExportDirectory.hpp
├── FileHeader.hpp
├── Image.cpp
├── Image.hpp
├── ImportDirectory.cpp
├── ImportDirectory.hpp
├── OptionalHeader.cpp
├── OptionalHeader.hpp
├── PEHeader.cpp
├── PEHeader.hpp
├── PELibrary.hpp
├── PEUtil.cpp
├── PEUtil.hpp
├── RelocationDirectory.cpp
├── RelocationDirectory.hpp
├── SectionHeader.cpp
├── SectionHeader.hpp
└── misc
│ ├── Address.hpp
│ ├── ByteVector.hpp
│ ├── Concept.hpp
│ ├── File.cpp
│ ├── File.hpp
│ └── NonCopyable.hpp
├── spdlog
├── include
│ └── spdlog
│ │ ├── async.h
│ │ ├── async_logger-inl.h
│ │ ├── async_logger.h
│ │ ├── cfg
│ │ ├── argv.h
│ │ ├── env.h
│ │ ├── helpers-inl.h
│ │ └── helpers.h
│ │ ├── common-inl.h
│ │ ├── common.h
│ │ ├── details
│ │ ├── backtracer-inl.h
│ │ ├── backtracer.h
│ │ ├── circular_q.h
│ │ ├── console_globals.h
│ │ ├── file_helper-inl.h
│ │ ├── file_helper.h
│ │ ├── fmt_helper.h
│ │ ├── log_msg-inl.h
│ │ ├── log_msg.h
│ │ ├── log_msg_buffer-inl.h
│ │ ├── log_msg_buffer.h
│ │ ├── mpmc_blocking_q.h
│ │ ├── null_mutex.h
│ │ ├── os-inl.h
│ │ ├── os.h
│ │ ├── periodic_worker-inl.h
│ │ ├── periodic_worker.h
│ │ ├── registry-inl.h
│ │ ├── registry.h
│ │ ├── synchronous_factory.h
│ │ ├── tcp_client-windows.h
│ │ ├── tcp_client.h
│ │ ├── thread_pool-inl.h
│ │ ├── thread_pool.h
│ │ └── windows_include.h
│ │ ├── fmt
│ │ ├── bin_to_hex.h
│ │ ├── bundled
│ │ │ ├── args.h
│ │ │ ├── chrono.h
│ │ │ ├── color.h
│ │ │ ├── compile.h
│ │ │ ├── core.h
│ │ │ ├── format-inl.h
│ │ │ ├── format.h
│ │ │ ├── locale.h
│ │ │ ├── os.h
│ │ │ ├── ostream.h
│ │ │ ├── printf.h
│ │ │ ├── ranges.h
│ │ │ └── xchar.h
│ │ ├── chrono.h
│ │ ├── fmt.h
│ │ ├── ostr.h
│ │ └── xchar.h
│ │ ├── formatter.h
│ │ ├── fwd.h
│ │ ├── logger-inl.h
│ │ ├── logger.h
│ │ ├── pattern_formatter-inl.h
│ │ ├── pattern_formatter.h
│ │ ├── sinks
│ │ ├── android_sink.h
│ │ ├── ansicolor_sink-inl.h
│ │ ├── ansicolor_sink.h
│ │ ├── base_sink-inl.h
│ │ ├── base_sink.h
│ │ ├── basic_file_sink-inl.h
│ │ ├── basic_file_sink.h
│ │ ├── daily_file_sink.h
│ │ ├── dist_sink.h
│ │ ├── dup_filter_sink.h
│ │ ├── hourly_file_sink.h
│ │ ├── mongo_sink.h
│ │ ├── msvc_sink.h
│ │ ├── null_sink.h
│ │ ├── ostream_sink.h
│ │ ├── ringbuffer_sink.h
│ │ ├── rotating_file_sink-inl.h
│ │ ├── rotating_file_sink.h
│ │ ├── sink-inl.h
│ │ ├── sink.h
│ │ ├── stdout_color_sinks-inl.h
│ │ ├── stdout_color_sinks.h
│ │ ├── stdout_sinks-inl.h
│ │ ├── stdout_sinks.h
│ │ ├── syslog_sink.h
│ │ ├── systemd_sink.h
│ │ ├── tcp_sink.h
│ │ ├── win_eventlog_sink.h
│ │ ├── wincolor_sink-inl.h
│ │ └── wincolor_sink.h
│ │ ├── spdlog-inl.h
│ │ ├── spdlog.h
│ │ ├── stopwatch.h
│ │ ├── tweakme.h
│ │ └── version.h
└── src
│ ├── async.cpp
│ ├── cfg.cpp
│ ├── color_sinks.cpp
│ ├── file_sinks.cpp
│ ├── fmt.cpp
│ ├── spdlog.cpp
│ └── stdout_sinks.cpp
├── unicorn
├── include
│ ├── list.h
│ ├── qemu.h
│ ├── uc_priv.h
│ └── unicorn
│ │ ├── arm.h
│ │ ├── arm64.h
│ │ ├── m68k.h
│ │ ├── mips.h
│ │ ├── platform.h
│ │ ├── sparc.h
│ │ ├── unicorn.h
│ │ └── x86.h
└── lib
│ └── x64
│ └── unicorn_static.lib
├── zycore
├── include
│ ├── Zycore
│ │ ├── API
│ │ │ ├── Memory.h
│ │ │ ├── Process.h
│ │ │ ├── Synchronization.h
│ │ │ ├── Terminal.h
│ │ │ └── Thread.h
│ │ ├── Allocator.h
│ │ ├── ArgParse.h
│ │ ├── Bitset.h
│ │ ├── Comparison.h
│ │ ├── Defines.h
│ │ ├── Format.h
│ │ ├── LibC.h
│ │ ├── List.h
│ │ ├── Object.h
│ │ ├── Status.h
│ │ ├── String.h
│ │ ├── Types.h
│ │ ├── Vector.h
│ │ ├── Zycore.h
│ │ └── ZycoreExportConfig.h
│ └── ZycoreExportConfig.h
└── src
│ ├── API
│ ├── Memory.c
│ ├── Process.c
│ ├── Synchronization.c
│ ├── Terminal.c
│ └── Thread.c
│ ├── Allocator.c
│ ├── ArgParse.c
│ ├── Bitset.c
│ ├── Format.c
│ ├── List.c
│ ├── String.c
│ ├── Vector.c
│ └── Zycore.c
└── zydis
├── include
├── Zydis
│ ├── Decoder.h
│ ├── DecoderTypes.h
│ ├── Formatter.h
│ ├── FormatterBuffer.h
│ ├── Generated
│ │ ├── EnumISAExt.h
│ │ ├── EnumISASet.h
│ │ ├── EnumInstructionCategory.h
│ │ ├── EnumMnemonic.h
│ │ └── EnumRegister.h
│ ├── Internal
│ │ ├── DecoderData.h
│ │ ├── FormatterATT.h
│ │ ├── FormatterBase.h
│ │ ├── FormatterIntel.h
│ │ ├── SharedData.h
│ │ └── String.h
│ ├── MetaInfo.h
│ ├── Mnemonic.h
│ ├── Register.h
│ ├── SharedTypes.h
│ ├── ShortString.h
│ ├── Status.h
│ ├── Utils.h
│ └── Zydis.h
└── ZydisExportConfig.h
├── lib
├── ReleaseX64
│ ├── Zycore.lib
│ └── Zydis.lib
└── ReleaseX86
│ ├── Zycore.lib
│ └── Zydis.lib
└── src
├── Decoder.c
├── DecoderData.c
├── Formatter.c
├── FormatterATT.c
├── FormatterBase.c
├── FormatterBuffer.c
├── FormatterIntel.c
├── Generated
├── AccessedFlags.inc
├── DecoderTables.inc
├── EncodableInstructions.inc
├── EnumISAExt.inc
├── EnumISASet.inc
├── EnumInstructionCategory.inc
├── EnumMnemonic.inc
├── EnumRegister.inc
├── FormatterStrings.inc
├── InstructionDefinitions.inc
├── InstructionEncodings.inc
└── OperandDefinitions.inc
├── MetaInfo.c
├── Mnemonic.c
├── Register.c
├── SharedData.c
├── String.c
├── Utils.c
└── Zydis.c
/README.md:
--------------------------------------------------------------------------------
1 | # VMPImportFixer
2 |
3 | VMPImportFixer is a tool aimed to resolve import calls in a VMProtect'd (3.x) binary.
4 |
5 | # Information
6 |
7 | VMPImportFixer attempts to resolve all obfuscated API calls in a VMProtect'd binary. A binary which has VMProtect's "Import Protection" option enabled will have all it's `CALL NEAR` instructions replaced with near relative call instructions (see [ImportCallObfuscator](https://github.com/mike1k/ImportCallObfuscator) for a similar method of obfuscating imports).
8 |
9 | VMProtect usually has two different variations of import calls which seem to be chosen at random once the binary is protected. The first, being `push reg; call func`, and the other being `call func; ret/int3`.
10 |
11 | 
12 | 
13 |
14 | Following these calls lead into the VMProtect section, which, by default is named `.vmp0`. Each stub can vary in complexity and size, however the concept is generally the same. Through a series of arithmetic which is used to calculate the real import address, the final operation usually sets `[rsp]`/`[esp]` to the import address before the final RET instruction.
15 |
16 | Based on the variant of the call (`push reg; call func` or `call func; int3/ret`), the stub may increment the return address. This use of the extra byte and return address incrementing is used to break various decompilers from properly analyzing a function due to the decompiler not recognizing that the byte will be skipped over in runtime.
17 |
18 | With this information combined, I decided to write a tool over the day that solves these calls. I was not happy with public implementations due to various reasons. One was closed-source, and seemed to be limited to a debugger, and the other lifts these stubs into a IL which seems impractical. I decided to go the emulation route as this trivially tackles the problem and supports both X86 and X86-64 flawlessly.
19 |
20 | VMPImportFixer is an all-in-one tool; it will support X86 processes regardless of being in a X64 context. This means that there is no need for architecture dependent versions of the binary.
21 |
22 | # Usage
23 |
24 | ```
25 | Usage: VMPImportFixer
26 | -p (required) process name/process id
27 | -mod: (optional) name of module to dump.
28 | -section: (optional) VMP section name to use if changed from default (VMP allows custom names)
29 | ```
30 |
31 | # Examples
32 |
33 | Images
34 |
35 | * Before
36 | 
37 | * After
38 | 
39 |
40 | * Before
41 | 
42 | * After
43 | 
44 |
45 |
46 | # TODO
47 |
48 | * Add support for loading binaries off the disk into a state where it can be monitored at specific stages (such as unpacking) then fixed.
49 | * Add relocation handling on X86 binaries.
50 | * Kernel support.
51 |
52 | # Dependencies
53 | * [pepp](https://github.com/mike1k/pepp)
54 | * [Unicorn](https://github.com/unicorn-engine/unicorn)
55 | * [Zydis](https://github.com/zyantific/zydis)
56 | * [spdlog](https://github.com/gabime/spdlog)
57 |
58 | # Credits
59 |
60 | [mrexodia](https://github.com/mrexodia) for his contribution to [HookHunter](https://github.com/mike1k/HookHunter) regarding `ReadMemory` inside the `Process` class.
61 |
--------------------------------------------------------------------------------
/VIFTools.cpp:
--------------------------------------------------------------------------------
1 | #include "VMPImportFixer.hpp"
2 |
3 | DWORD VifSearchForProcess(std::string_view process_name) noexcept
4 | {
5 | PROCESSENTRY32 pe32{};
6 | pe32.dwSize = sizeof pe32;
7 |
8 | vif::nt::ScopedHandle hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
9 | if (static_cast(hSnapshot) == INVALID_HANDLE_VALUE)
10 | return -1;
11 |
12 | if (Process32First(hSnapshot, &pe32))
13 | {
14 | do
15 | {
16 | std::string_view this_proc = pe32.szExeFile;
17 | if (this_proc.find(process_name) != this_proc.npos && pe32.th32ProcessID != GetCurrentProcessId())
18 | return pe32.th32ProcessID;
19 | } while (Process32Next(hSnapshot, &pe32));
20 | }
21 |
22 | return static_cast(-1);
23 | }
24 |
25 | bool VifFindModuleInProcess(HANDLE hProc, std::string_view module_name, VIFModuleInformation_t* info)
26 | {
27 | HMODULE hMods[1024];
28 | DWORD cbNeeded;
29 | unsigned int i;
30 |
31 | if (!info)
32 | return false;
33 |
34 | if (EnumProcessModulesEx(hProc, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_ALL))
35 | {
36 | for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
37 | {
38 | TCHAR szModName[MAX_PATH];
39 |
40 | if (GetModuleFileNameEx(hProc, hMods[i], szModName,
41 | sizeof(szModName) / sizeof(TCHAR)))
42 | {
43 | MODULEINFO mdi{};
44 | GetModuleInformation(hProc, hMods[i], &mdi, sizeof mdi);
45 |
46 | info->module_path = szModName;
47 |
48 | if (info->module_path.find(module_name) != std::string::npos)
49 | {
50 | info->base_address = (std::uint64_t)hMods[i];
51 | info->module_size = mdi.SizeOfImage;
52 | }
53 | }
54 | }
55 | }
56 |
57 | return info->base_address != 0;
58 | }
59 |
60 | bool VifFindModulesInProcess(HANDLE hProc, std::vector& modules)
61 | {
62 | HMODULE hMods[1024];
63 | DWORD cbNeeded;
64 | unsigned int i;
65 |
66 | if (EnumProcessModulesEx(hProc, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_ALL))
67 | {
68 | for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
69 | {
70 | TCHAR szModName[MAX_PATH];
71 |
72 | // Get the full path to the module's file.
73 | if (GetModuleFileNameEx(hProc, hMods[i], szModName,
74 | sizeof(szModName) / sizeof(TCHAR)))
75 | {
76 | MODULEINFO info{};
77 | GetModuleInformation(hProc, hMods[i], &info, sizeof info);
78 |
79 | modules.emplace_back(szModName, (std::uint64_t)hMods[i], info.SizeOfImage);
80 | }
81 | }
82 | }
83 |
84 | return modules.size() > 0;
85 | }
86 |
--------------------------------------------------------------------------------
/VIFTools.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | struct VIFModuleInformation_t
4 | {
5 | std::string module_path;
6 | std::uint64_t base_address;
7 | std::uint32_t module_size;
8 | };
9 |
10 |
11 | DWORD VifSearchForProcess(std::string_view process_name) noexcept;
12 | bool VifFindModuleInProcess(HANDLE hProc, std::string_view module_name, VIFModuleInformation_t* info);
13 | bool VifFindModulesInProcess(HANDLE hProc, std::vector& modules);
14 |
15 |
--------------------------------------------------------------------------------
/VMPImportFixer.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | //
4 | // Emulation engine.
5 | #include
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include