diff --git a/src/vmemu.vcxproj b/src/vmemu.vcxproj
index 7282200..d3e66c7 100644
--- a/src/vmemu.vcxproj
+++ b/src/vmemu.vcxproj
@@ -119,6 +119,7 @@
true
+ $(ProjectDir)..\dependencies\unicorn\include\;$(ProjectDir)..\dependencies\xtils\;$(ProjectDir)..\dependencies\vmprofiler\include\;$(ProjectDir)..\dependencies\cli-parser\;$(ProjectDir)..\dependencies\vmprofiler\dependencies\zydis\include;$(ProjectDir)..\dependencies\vmprofiler\dependencies\zydis\dependencies\zycore\include;$(ProjectDir)..\dependencies\vmprofiler\dependencies\zydis\msvc;$(IncludePath)
false
@@ -128,12 +129,14 @@
Level3
true
- _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS
true
+ stdcpp17
Console
true
+ $(ProjectDir)..\dependencies\unicorn\msvc\x64\Debug\*.lib
@@ -142,7 +145,7 @@
true
true
true
- NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS
true
Disabled
stdcpp17
@@ -152,7 +155,7 @@
true
true
true
- $(ProjectDir)..\dependencies\unicorn\msvc\x64\Release\*.lib;%(AdditionalDependencies)
+ $(ProjectDir)..\dependencies\unicorn\msvc\x64\Debug\*.lib
diff --git a/src/vmemu_t.cpp b/src/vmemu_t.cpp
index 20dc5a1..9a2b663 100644
--- a/src/vmemu_t.cpp
+++ b/src/vmemu_t.cpp
@@ -9,7 +9,8 @@ namespace vm
image_base(image_base),
vm_entry_rva(vm_entry_rva),
vm_handler_table(nullptr),
- uc(nullptr)
+ uc(nullptr),
+ trace_entries(nullptr)
{}
bool emu_t::init()
@@ -24,7 +25,7 @@ namespace vm
return false;
}
- //vm::util::deobfuscate(vm_entry);
+ vm::util::deobfuscate(vm_entry);
vm::util::print(vm_entry);
if (!(vm_handler_table = vm::handler::table::get(vm_entry)))
@@ -39,14 +40,20 @@ namespace vm
std::printf("[!] failed to get all vm handlers...\n");
return false;
}
+ std::printf("> got all vm handlers...\n");
//
// unicorn init stuff...
//
- auto err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc);
+ const auto image_size =
+ NT_HEADER(module_base)->OptionalHeader.SizeOfImage;
- if (err)
+ std::uintptr_t stack_base = 0x1000000;
+ std::uintptr_t stack_addr = stack_base + (0x1000 * 20);
+
+ uc_err err;
+ if ((err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc)))
{
std::printf("failed on uc_mem_map() with error returned %u: %s\n",
err, uc_strerror(err));
@@ -54,16 +61,7 @@ namespace vm
return false;
}
- const auto image_size =
- NT_HEADER(module_base)->OptionalHeader.SizeOfImage;
-
- const auto vm_entry = vm_entry_rva + module_base;
- constexpr auto stack_addr = 0x1000000 + (0x1000 * 6);
-
- // allocate space for module...
- err = uc_mem_map(uc, module_base, image_size, UC_PROT_ALL);
-
- if (err)
+ if ((err = uc_mem_map(uc, module_base, image_size, UC_PROT_ALL)))
{
std::printf("failed on uc_mem_map() with error returned %u: %s\n",
err, uc_strerror(err));
@@ -71,10 +69,7 @@ namespace vm
return false;
}
- // allocate 6 pages for stack...
- err = uc_mem_map(uc, stack_addr, 0x1000 * 6, UC_PROT_ALL);
-
- if (err)
+ if ((err = uc_mem_map(uc, 0x1000000, 0x1000 * 20, UC_PROT_ALL)))
{
std::printf("failed on uc_mem_map() with error returned %u: %s\n",
err, uc_strerror(err));
@@ -82,16 +77,15 @@ namespace vm
return false;
}
- // write the module into memory...
- err = uc_mem_write(uc, module_base, reinterpret_cast(module_base), image_size);
-
- if (err) std::printf("failed on uc_mem_write() with error returned %u: %s\n",
- err, uc_strerror(err));
+ if ((err = uc_mem_write(uc, module_base, reinterpret_cast(module_base), image_size)))
+ {
+ std::printf("failed on uc_mem_write() with error returned %u: %s\n",
+ err, uc_strerror(err));
- // set vm_entry into RIP...
- err = uc_reg_write(uc, UC_X86_REG_RIP, &vm_entry);
+ return false;
+ }
- if (err)
+ if ((err = uc_reg_write(uc, UC_X86_REG_RIP, &vm_entry)))
{
std::printf("failed on uc_reg_write() with error returned %u: %s\n",
err, uc_strerror(err));
@@ -99,10 +93,7 @@ namespace vm
return false;
}
- // set stack address up...
- err = uc_reg_write(uc, UC_X86_REG_RSP, &stack_addr);
-
- if (err)
+ if ((err = uc_reg_write(uc, UC_X86_REG_RSP, &stack_addr)))
{
std::printf("failed on uc_reg_write() with error returned %u: %s\n",
err, uc_strerror(err));
@@ -110,10 +101,8 @@ namespace vm
return false;
}
- err = uc_hook_add(uc, &trace, UC_HOOK_CODE, &jmp_hook,
- nullptr, module_base, module_base + image_size);
-
- if (err)
+ if ((err = uc_hook_add(uc, &trace, UC_HOOK_CODE, &vm::emu_t::hook_code,
+ this, module_base, module_base + image_size)))
{
std::printf("failed on uc_hook_add() with error returned %u: %s\n",
err, uc_strerror(err));
@@ -121,16 +110,14 @@ namespace vm
return false;
}
- // emulate machine code in infinite time
- err = uc_emu_start(uc, vm_entry, NULL, NULL, NULL);
- if (err)
+ if ((err = uc_hook_add(uc, &trace1, UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED,
+ vm::emu_t::hook_mem_invalid, this, 1, 0)))
{
- std::printf("Failed on uc_emu_start() with error returned %u: %s\n",
+ std::printf("failed on uc_hook_add() with error returned %u: %s\n",
err, uc_strerror(err));
return false;
}
-
return true;
}
@@ -138,4 +125,152 @@ namespace vm
{
if (uc) uc_close(uc);
}
+
+ bool emu_t::get_trace(std::vector& entries)
+ {
+ // hook_code will fill this vector up with values...
+ trace_entries = &entries;
+ uc_err err;
+
+ if ((err = uc_emu_start(uc, vm_entry_rva + module_base, NULL, NULL, NULL)))
+ {
+ std::printf("failed on uc_emu_start() with error returned %u: %s\n",
+ err, uc_strerror(err));
+
+ return false;
+ }
+ return true;
+ }
+
+ uc_err emu_t::create_entry(vmp2::entry_t* entry)
+ {
+ uc_reg_read(uc, UC_X86_REG_R15, &entry->regs.r15);
+ uc_reg_read(uc, UC_X86_REG_R14, &entry->regs.r14);
+ uc_reg_read(uc, UC_X86_REG_R13, &entry->regs.r13);
+ uc_reg_read(uc, UC_X86_REG_R12, &entry->regs.r12);
+ uc_reg_read(uc, UC_X86_REG_R11, &entry->regs.r11);
+ uc_reg_read(uc, UC_X86_REG_R10, &entry->regs.r10);
+ uc_reg_read(uc, UC_X86_REG_R9, &entry->regs.r9);
+ uc_reg_read(uc, UC_X86_REG_R8, &entry->regs.r8);
+ uc_reg_read(uc, UC_X86_REG_RBP, &entry->regs.rbp);
+ uc_reg_read(uc, UC_X86_REG_RDI, &entry->regs.rdi);
+ uc_reg_read(uc, UC_X86_REG_RSI, &entry->regs.rsi);
+ uc_reg_read(uc, UC_X86_REG_RDX, &entry->regs.rdx);
+ uc_reg_read(uc, UC_X86_REG_RCX, &entry->regs.rcx);
+ uc_reg_read(uc, UC_X86_REG_RBX, &entry->regs.rbx);
+ uc_reg_read(uc, UC_X86_REG_RAX, &entry->regs.rax);
+ uc_reg_read(uc, UC_X86_REG_EFLAGS, &entry->regs.rflags);
+
+ entry->vip = entry->regs.rsi;
+ entry->handler_idx = entry->regs.rax;
+ entry->decrypt_key = entry->regs.rbx;
+
+ uc_err err;
+ if ((err = uc_mem_read(uc, entry->regs.rdi,
+ entry->vregs.raw, sizeof entry->vregs.raw)))
+ return err;
+
+ // copy virtual stack values...
+ for (auto idx = 0u; idx < sizeof(entry->vsp) / 8; ++idx)
+ if ((err = uc_mem_read(uc, entry->regs.rbp + (idx * 8),
+ &entry->vsp.qword[idx], sizeof entry->vsp.qword[idx])))
+ return err;
+
+ return UC_ERR_OK;
+ }
+
+ void emu_t::hook_code(uc_engine* uc, uint64_t address, uint32_t size, vm::emu_t* obj)
+ {
+ std::printf(">>> Tracing instruction at 0x%p, instruction size = 0x%x\n", address, size);
+
+ // grab JMP RDX/RCX <-- this register...
+ static const auto jmp_reg = obj->vm_entry[obj->vm_entry.size()]
+ .instr.operands[0]
+ .reg
+ .value;
+
+ static ZydisDecoder decoder;
+ static std::once_flag once;
+ static ZydisDecodedInstruction instr;
+ static std::uintptr_t reg_val = 0u;
+
+ // init zydis decoder just a single time...
+ std::call_once(once, [&]() -> void {
+ ZydisDecoderInit(&decoder,
+ ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64); });
+
+ if (address == obj->vm_entry[obj->vm_entry.size()].addr)
+ {
+ std::printf("stopped at jmp... addr = 0x%p\n", address);
+ std::getchar();
+
+ vmp2::entry_t new_entry;
+ if (!obj->create_entry(&new_entry))
+ {
+ std::printf("[!] failed to create new entry... exiting...\n");
+ exit(0);
+ }
+ obj->trace_entries->push_back(new_entry);
+ }
+ // if we are getting a callback for a JMP RCX/RDX instruction...
+ else if (ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(
+ &decoder, reinterpret_cast(address), size, &instr)) &&
+ instr.mnemonic == ZYDIS_MNEMONIC_JMP &&
+ instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
+ instr.operands[0].reg.value == jmp_reg)
+ {
+ switch (jmp_reg)
+ {
+ ZYDIS_REGISTER_RDX:
+ uc_reg_read(uc, UC_X86_REG_RDX, ®_val);
+ break;
+ ZYDIS_REGISTER_RCX:
+ uc_reg_read(uc, UC_X86_REG_RCX, ®_val);
+ break;
+ default:
+ std::printf("[!] invalid jump register...\n");
+ exit(0);
+ }
+
+ // checks to see if the address
+ // in JMP RDX/RCX is a vm handler address...
+ static const auto vm_handler_check =
+ [&](const vm::handler_t& vm_handler) -> bool
+ { return vm_handler.address == reg_val; };
+
+ if (std::find_if(obj->vm_handlers.begin(), obj->vm_handlers.end(),
+ vm_handler_check) == obj->vm_handlers.end())
+ return;
+
+ std::printf("stopped at jmp... addr = 0x%p\n", address);
+ std::getchar();
+
+ vmp2::entry_t new_entry;
+ if (!obj->create_entry(&new_entry))
+ {
+ std::printf("[!] failed to create new entry... exiting...\n");
+ exit(0);
+ }
+ obj->trace_entries->push_back(new_entry);
+ }
+ }
+
+ bool emu_t::hook_mem_invalid(uc_engine* uc, uc_mem_type type,
+ uint64_t address, int size, int64_t value, vm::emu_t* obj)
+ {
+ switch (type)
+ {
+ default:
+ // return false to indicate we want to stop emulation
+ return false;
+ case UC_MEM_WRITE_UNMAPPED:
+ printf(">>> Missing memory is being WRITE at 0x%p, data size = %u, data value = 0x%p\n",
+ address, size, value);
+ return false;
+ case UC_MEM_READ_UNMAPPED:
+ printf(">>> Missing memory is being READ at 0x%p, data size = %u, data value = 0x%p\n",
+ address, size, value);
+ return false;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/vmemu_t.hpp b/src/vmemu_t.hpp
index 5d26600..5a6de31 100644
--- a/src/vmemu_t.hpp
+++ b/src/vmemu_t.hpp
@@ -6,6 +6,7 @@
#include
#include
#include
+#include
namespace vm
{
@@ -15,12 +16,18 @@ namespace vm
public:
explicit emu_t(std::uint32_t vm_entry_rva,
std::uintptr_t image_base, std::uintptr_t module_base);
+ ~emu_t();
bool init();
- ~emu_t();
+ bool get_trace(std::vector& entries);
private:
+ uc_err create_entry(vmp2::entry_t* entry);
+ static void hook_code(uc_engine* uc, uint64_t address, uint32_t size, vm::emu_t* obj);
+ static bool hook_mem_invalid(uc_engine* uc, uc_mem_type type,
+ uint64_t address, int size, int64_t value, vm::emu_t* obj);
+
uc_engine* uc;
- uc_hook trace;
+ uc_hook trace, trace1;
std::uintptr_t image_base, module_base;
std::uint32_t vm_entry_rva;
@@ -28,61 +35,6 @@ namespace vm
zydis_routine_t vm_entry;
std::uintptr_t* vm_handler_table;
std::vector vm_handlers;
-
- // very janky work around to use classes & callbacks with unicorn... it is what it is...
- callback_t jmp_hook =
- [&, this](uc_engine* uc, uint64_t address, uint32_t size, void* user_data) -> void
- {
- // grab JMP RDX/RCX <-- this register...
- static auto jmp_reg = vm_entry[
- vm_entry.size() - 1].instr.operands[0].reg.value;
-
- static ZydisDecoder decoder;
- static std::once_flag once;
- static ZydisDecodedInstruction instr;
- static std::uintptr_t reg_val = 0u;
-
- // init zydis decoder just a single time...
- std::call_once(once, []() -> void {
- ZydisDecoderInit(&decoder,
- ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64); });
-
- if (address == vm_entry[vm_entry.size() - 1].addr)
- {
- std::printf("stopped at jmp... addr = 0x%p\n", address);
- std::getchar();
- }
- // if we are getting a callback for a JMP RCX/RDX instruction...
- else if (ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(
- &decoder, reinterpret_cast(address), size, &instr)) &&
- instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
- instr.operands[0].reg.value == jmp_reg)
- {
- switch (jmp_reg)
- {
- ZYDIS_REGISTER_RDX:
- uc_reg_read(uc, UC_X86_REG_RDX, ®_val);
- break;
- ZYDIS_REGISTER_RCX:
- uc_reg_read(uc, UC_X86_REG_RCX, ®_val);
- break;
- default:
- throw std::exception("invalid register to jump from...\n");
- }
-
- // checks to see if the address
- // in JMP RDX/RCX is a vm handler address...
- static const auto vm_handler_check =
- [&](vm::handler_t& vm_handler) -> bool
- { return vm_handler.address == reg_val; };
-
- if (std::find_if(vm_handlers.begin(), vm_handlers.end(),
- vm_handler_check) == vm_handlers.end())
- return;
-
- std::printf("stopped at jmp... addr = 0x%p\n", address);
- std::getchar();
- }
- };
+ std::vector* trace_entries;
};
}
\ No newline at end of file
diff --git a/src/vmptest.vmp.exe b/src/vmptest.vmp.exe
new file mode 100644
index 0000000..60c276e
Binary files /dev/null and b/src/vmptest.vmp.exe differ
diff --git a/vmemu.sln b/vmemu.sln
index 341a48f..b2791c0 100644
--- a/vmemu.sln
+++ b/vmemu.sln
@@ -11,6 +11,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Zydis", "dependencies\vmpro
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ DBG|x64 = DBG|x64
+ DBG|x86 = DBG|x86
Debug Kernel|x64 = Debug Kernel|x64
Debug Kernel|x86 = Debug Kernel|x86
Debug MD DLL|x64 = Debug MD DLL|x64
@@ -37,6 +39,9 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {F0D51879-E659-4BD3-B688-7864DB3C82AA}.DBG|x64.ActiveCfg = Debug|x64
+ {F0D51879-E659-4BD3-B688-7864DB3C82AA}.DBG|x64.Build.0 = Debug|x64
+ {F0D51879-E659-4BD3-B688-7864DB3C82AA}.DBG|x86.ActiveCfg = Debug|x64
{F0D51879-E659-4BD3-B688-7864DB3C82AA}.Debug Kernel|x64.ActiveCfg = Debug|x64
{F0D51879-E659-4BD3-B688-7864DB3C82AA}.Debug Kernel|x64.Build.0 = Debug|x64
{F0D51879-E659-4BD3-B688-7864DB3C82AA}.Debug Kernel|x86.ActiveCfg = Release|x64
@@ -83,12 +88,15 @@ Global
{F0D51879-E659-4BD3-B688-7864DB3C82AA}.Release|x64.ActiveCfg = Release|x64
{F0D51879-E659-4BD3-B688-7864DB3C82AA}.Release|x64.Build.0 = Release|x64
{F0D51879-E659-4BD3-B688-7864DB3C82AA}.Release|x86.ActiveCfg = Release|x64
+ {D0B6092A-9944-4F24-9486-4B7DAE372619}.DBG|x64.ActiveCfg = DBG|x64
+ {D0B6092A-9944-4F24-9486-4B7DAE372619}.DBG|x64.Build.0 = DBG|x64
+ {D0B6092A-9944-4F24-9486-4B7DAE372619}.DBG|x86.ActiveCfg = DBG|x64
{D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug Kernel|x64.ActiveCfg = Release|x64
{D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug Kernel|x64.Build.0 = Release|x64
{D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug Kernel|x86.ActiveCfg = Release|x64
{D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug Kernel|x86.Build.0 = Release|x64
- {D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug MD DLL|x64.ActiveCfg = Release|x64
- {D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug MD DLL|x64.Build.0 = Release|x64
+ {D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug MD DLL|x64.ActiveCfg = DBG|x64
+ {D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug MD DLL|x64.Build.0 = DBG|x64
{D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug MD DLL|x86.ActiveCfg = Release|x64
{D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug MD DLL|x86.Build.0 = Release|x64
{D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug MD|x64.ActiveCfg = Release|x64
@@ -103,8 +111,8 @@ Global
{D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug MT|x64.Build.0 = Release|x64
{D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug MT|x86.ActiveCfg = Release|x64
{D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug MT|x86.Build.0 = Release|x64
- {D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug|x64.ActiveCfg = Release|x64
- {D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug|x64.Build.0 = Release|x64
+ {D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug|x64.ActiveCfg = DBG|x64
+ {D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug|x64.Build.0 = DBG|x64
{D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug|x86.ActiveCfg = Release|x64
{D0B6092A-9944-4F24-9486-4B7DAE372619}.Debug|x86.Build.0 = Release|x64
{D0B6092A-9944-4F24-9486-4B7DAE372619}.Release Kernel|x64.ActiveCfg = Release|x64
@@ -130,6 +138,10 @@ Global
{D0B6092A-9944-4F24-9486-4B7DAE372619}.Release|x64.ActiveCfg = Release|x64
{D0B6092A-9944-4F24-9486-4B7DAE372619}.Release|x64.Build.0 = Release|x64
{D0B6092A-9944-4F24-9486-4B7DAE372619}.Release|x86.ActiveCfg = Release|x64
+ {88A23124-5640-35A0-B890-311D7A67A7D2}.DBG|x64.ActiveCfg = Debug MT|x64
+ {88A23124-5640-35A0-B890-311D7A67A7D2}.DBG|x64.Build.0 = Debug MT|x64
+ {88A23124-5640-35A0-B890-311D7A67A7D2}.DBG|x86.ActiveCfg = Debug MT|Win32
+ {88A23124-5640-35A0-B890-311D7A67A7D2}.DBG|x86.Build.0 = Debug MT|Win32
{88A23124-5640-35A0-B890-311D7A67A7D2}.Debug Kernel|x64.ActiveCfg = Debug Kernel|x64
{88A23124-5640-35A0-B890-311D7A67A7D2}.Debug Kernel|x64.Build.0 = Debug Kernel|x64
{88A23124-5640-35A0-B890-311D7A67A7D2}.Debug Kernel|x64.Deploy.0 = Debug Kernel|x64