Compare commits

...

60 Commits

Author SHA1 Message Date
IDontCode 7fa5bb91a6 Update README.md
2 years ago
_xeroxz ecccbf4172 Update README.md
3 years ago
_xeroxz eefb1b5480 Update README.md
3 years ago
_xeroxz 18e0fceeed Update README.md
3 years ago
_xeroxz 732fe16864 Update README.md
3 years ago
_xeroxz 26e2524379 Update README.md
3 years ago
_xeroxz 09a59453c2 Update README.md
3 years ago
_xeroxz fd6ddce1d6 Update README.md
3 years ago
_xeroxz 18392717e4 Update README.md
3 years ago
_xeroxz 2d6c911ab4 Update README.md
3 years ago
_xeroxz 1f2d10353b Update README.md
3 years ago
_xeroxz 680edc4faf Merge branch '_xeroxz' into 'master'
3 years ago
_xeroxz 64e4838d6d finished version 0.1...
3 years ago
_xeroxz a8fef83c95 preparing for release...
3 years ago
_xeroxz 8a5bf9e8ab added more lifters
3 years ago
_xeroxz 5775d5ae65 added more lifters...
3 years ago
_xeroxz aaf0e9defe added more lifters...
3 years ago
_xeroxz b82072e2ed added more lifters...
3 years ago
_xeroxz d2da38cad1 added more lifters...
3 years ago
_xeroxz 014c1a08cd added more lifters...
3 years ago
_xeroxz 65ae6ebe02 added more lifters :yawn:
3 years ago
_xeroxz ea078d9847 added more lifters
3 years ago
_xeroxz 93402767a9 added more lifters...
3 years ago
_xeroxz 4fe8c63f49 added more lifters... updated vmprofiler dep...
3 years ago
_xeroxz ebbe93f278 Merge branch '_xeroxz' into 'master'
3 years ago
_xeroxz 661bc3bc16 added more lifters - there are many more to add...
3 years ago
_xeroxz a2b532cfff updated vmexit handler, updated vmprofiler dep... updated main.cpp
3 years ago
_xeroxz c84c28ffd0 Merge branch '_xeroxz' into 'master'
3 years ago
_xeroxz cfb244c93c updated vmprofiler dep...
3 years ago
_xeroxz 48590259c4 Merge branch '_xeroxz' into 'master'
3 years ago
_xeroxz 4d7bf0d94c STATIC DEVIRT HELLO WORLD LETS GOOOOOOOOOOOOOOOOO
3 years ago
_xeroxz 92bd54c368 added all of the code to append obj files to pe files...
3 years ago
_xeroxz 8217c63389 Merge branch '_xeroxz' into 'master'
3 years ago
_xeroxz 28f44ed761 updated vmexit so that it gens an exit stub... also handed vm enters...
3 years ago
_xeroxz 11b6bce49b Merge branch '_xeroxz' into 'master'
3 years ago
_xeroxz d94ff6126d added another lifter (POPVSP)...
3 years ago
_xeroxz 53f22b26bb added code to compile...
3 years ago
_xeroxz 547d1a78d6 Merge branch '_xeroxz' into 'master'
3 years ago
_xeroxz 1c6fd327b1 EAST BOUND AND DOWN
3 years ago
_xeroxz 29804c5015 updated vmprofiler and also have the first vmp2 v4 serializing...
3 years ago
_xeroxz 33ba405a21 compiling IR to OBJ file now! need to make .vmp2 file format v3...
3 years ago
_xeroxz 4b0e53fbe1 cleaned all this shit, also added a demo to generate object files...
3 years ago
_xeroxz 1128607429 Merge branch '_xeroxz' into 'master'
3 years ago
_xeroxz 5223011f4b i updated a bunch, too much to type lol
3 years ago
_xeroxz fbce0ffd59 added 21 arguments...
3 years ago
_xeroxz 77ae31b1b6 reverted back to making a local stack, made alignment 1...
3 years ago
_xeroxz 9ff8014de8 testing a new method for dealing with the stack...
3 years ago
_xeroxz 3a4529819c Merge branch '_xeroxz' into 'master'
3 years ago
_xeroxz 07bae2e551 Merge branch '_xeroxz' into 'master'
3 years ago
_xeroxz cca5b1191a Merge branch '_xeroxz' into 'master'
3 years ago
_xeroxz fe089bf203 Merge branch '_xeroxz' into 'master'
3 years ago
_xeroxz e45edafb81 updated vmprofiler dep...
3 years ago
_xeroxz 30c9c96173 updated vmprofiler dep...
3 years ago
_xeroxz a84ffc3e67 merged with coltons changes... also treating argument 1 as the stack...
3 years ago
_xeroxz 3755f866ba updated vmprofiler dep
3 years ago
_xeroxz 2a24bfc125 added some more lifters...
3 years ago
_xeroxz 97aab6d44f fixed JMP lifter... branching is now working...
3 years ago
_xeroxz 856fa1815d added more lifters...
3 years ago
_xeroxz e846560010 cleaned the project, added a bunch of lifters... adding more...
3 years ago
_xeroxz 332f9e4d2e added more lifters, added flags var, need to add code to update flags...
3 years ago

@ -40,16 +40,40 @@ set(CMKR_TARGET vmdevirt)
set(vmdevirt_SOURCES "")
list(APPEND vmdevirt_SOURCES
"src/devirt_t.cpp"
"src/devirt_utils.cpp"
"src/lifters/add.cpp"
"src/lifters/div.cpp"
"src/lifters/idiv.cpp"
"src/lifters/imul.cpp"
"src/lifters/jmp.cpp"
"src/lifters/lconst.cpp"
"src/lifters/lflags.cpp"
"src/lifters/lreg.cpp"
"src/lifters/mul.cpp"
"src/lifters/nand.cpp"
"src/lifters/popvsp.cpp"
"src/lifters/pushvsp.cpp"
"src/lifters/rdtsc.cpp"
"src/lifters/read.cpp"
"src/lifters/readcr3.cpp"
"src/lifters/readcr8.cpp"
"src/lifters/readgs.cpp"
"src/lifters/sflags.cpp"
"src/lifters/shl.cpp"
"src/lifters/shld.cpp"
"src/lifters/shr.cpp"
"src/lifters/shrd.cpp"
"src/lifters/sreg.cpp"
"src/lifters/vmexit.cpp"
"src/lifters/write.cpp"
"src/lifters/writecr3.cpp"
"src/main.cpp"
"src/vmp_rtn.cpp"
"src/vmp_rtn_t.cpp"
"include/devirt_t.hpp"
"include/devirt_utils.hpp"
"include/vm_lifters.hpp"
"include/vmp_rtn.hpp"
"include/vmp_rtn_t.hpp"
)
list(APPEND vmdevirt_SOURCES
@ -77,6 +101,8 @@ target_compile_definitions(vmdevirt PRIVATE
target_include_directories(vmdevirt PRIVATE
include
"dependencies/llvm/llvm/include/"
"dependencies/llvm/llvm/lib/Target/X86/"
"build/dependencies/llvm/llvm/lib/Target/X86"
"build/dependencies/llvm/llvm/include/"
)
@ -87,6 +113,13 @@ target_link_libraries(vmdevirt PRIVATE
LLVMCodeGen
LLVMSupport
LLVMLinker
LLVMX86CodeGen
LLVMX86AsmParser
LLVMX86Desc
LLVMX86Disassembler
LLVMX86Info
LLVMAsmParser
LLVMPasses
)
unset(CMKR_TARGET)

@ -1,3 +1,39 @@
# vmdevirt
# VMDevirt - VMProtect Static Devirtualization
VMProtect 2 Static Devirtualization
VMDevirt is a project which uses LLVM to lift `vmprofiles` to LLVM IR. This lifting aims to be semantically accurate so that the generated native instructions can be executed as normal. This project only supports x86_64 PE binaries.
Currently there is no project to generate IL for vmp3 virtual routines, however when that code has been willed into existence this project will be used to compile the IL back to native. The lifters/vmprofiles for vmp2 and vmp3 are pretty much the same.
# Compiling
<div align="center">
<img src="https://media1.giphy.com/media/FovFeej5SQQh94uyyK/giphy.gif"/>
<br>
<i>LLVM takes forever to build and a few GB's of cache space in tmp...</i>
</div>
### Requirements
* CMake version 3 and above
* Visual Studios 2019 (Fully updated!)
* 16gb+ of RAM
* 10gb of free disk space
### Steps
Clone the entire repo recursively:
`git clone --recursive https://githacks.org/vmp2/vmdevirt.git`
Open a console inside of `vmdevirt` folder and execute the following CMake command:
`cmake -B build`
# Usage - Generating Native
In order to use this project you must first generate a `vmp2` file using `VMEmu`. This file contains the IL form of every single virtual instruction of every single virtual code block of every single virtualized routine that you generate from.
In order for VMEmu to work, all virtual instructions in the given virtual routine(s) must be defined. Please refer to the doxygen of `vmprofiler` to learn how to declare a vmprofile.
Once a `vmp2` file is generated you can then provide it to `vmdevirt` along with the virtualized binary. `vmdevirt` will lift all of the IL and compile it back to native, then append it to the virtualized binary and patch all jmp's into the virtualized routines to go into the devirtualized code.

@ -12,6 +12,8 @@ sources = [
include-directories = [
"include",
"dependencies/llvm/llvm/include/",
"dependencies/llvm/llvm/lib/Target/X86/",
"build/dependencies/llvm/llvm/lib/Target/X86",
"build/dependencies/llvm/llvm/include/",
]
link-libraries = [
@ -20,7 +22,14 @@ link-libraries = [
"LLVMCore",
"LLVMCodeGen",
"LLVMSupport",
"LLVMLinker"
"LLVMLinker",
"LLVMX86CodeGen",
"LLVMX86AsmParser",
"LLVMX86Desc",
"LLVMX86Disassembler",
"LLVMX86Info",
"LLVMAsmParser",
"LLVMPasses"
]
compile-definitions = [
"NOMINMAX"

@ -1 +1 @@
Subproject commit 16aeb2d6d48c4822b89497ad660911eb0f5e54bd
Subproject commit 1b6875d18825529907289bc87990fed5d99e7f96

@ -0,0 +1,74 @@
#pragma once
#include <vmp_rtn_t.hpp>
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Mangler.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Transforms/InstCombine/InstCombine.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Scalar/GVN.h"
#include "llvm/Transforms/Utils.h"
#include "X86TargetMachine.h"
#include "llvm/Pass.h"
#include "llvm/Passes/OptimizationLevel.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
namespace llvm
{
extern "C" void LLVMInitializeX86TargetInfo();
extern "C" void LLVMInitializeX86Target();
extern "C" void LLVMInitializeX86TargetMC();
extern "C" void LLVMInitializeX86AsmParser();
extern "C" void LLVMInitializeX86AsmPrinter();
} // namespace llvm
namespace vm
{
class devirt_t
{
friend class lifters_t;
public:
explicit devirt_t( llvm::LLVMContext *llvm_ctx, llvm::Module *llvm_module, vmp2::v4::file_header * vmp2_file );
llvm::Function *lift( std::uintptr_t rtn_begin, std::vector< vm::instrs::code_block_t > vmp2_code_blocks );
bool compile( std::vector< std::uint8_t > &obj );
private:
llvm::LLVMContext *llvm_ctx;
llvm::Module *llvm_module;
vmp2::v4::file_header *vmp2_file;
std::shared_ptr< llvm::IRBuilder<> > ir_builder;
std::vector< std::shared_ptr< vm::vmp_rtn_t > > vmp_rtns;
void push( std::uint8_t byte_size, llvm::Value *input_val );
llvm::Value *pop( std::uint8_t byte_size );
llvm::Value *load_value( std::uint8_t byte_size, llvm::GlobalValue *global );
llvm::Value *load_value( std::uint8_t byte_size, llvm::AllocaInst *var );
llvm::Value *sf( std::uint8_t byte_size, llvm::Value *val );
llvm::Value *zf( std::uint8_t byte_size, llvm::Value *val );
llvm::Value *pf( std::uint8_t byte_size, llvm::Value *val );
llvm::Value *flags( llvm::Value *cf, llvm::Value *pf, llvm::Value *af, llvm::Value *zf, llvm::Value *sf,
llvm::Value *of );
};
} // namespace vm

@ -0,0 +1,39 @@
#include <vmprofiler.hpp>
#include <coff/image.hpp>
#include <iostream>
#include <sstream>
#include <string>
#define VM_ENTER_NAME "vmenter_"
#define VM_EXIT_NAME "vmexit_"
#define VM_RTN_NAME "rtn_"
#define FIX_MAKE_ZERO_OFFSET 0x25
#define FIX_MAKE_RELOC_OFFSET 0x30
#define FIX_MAKE_JMP_OFFSET 0x43
namespace devirt
{
namespace util
{
/// <summary>
/// helper function to serialize vmp2 file data to vm::instr::code_block's...
/// </summary>
/// <param name="virt_rtns">vector of pairs {vm enter offset, vector of code blocks} which gets filled up with
/// serialized data</param>
/// <param name="vmp2file">a vector of bytes containing the vmp2 file...</param>
/// <returns>returns true if serialization was successful</returns>
bool serialize_vmp2(
std::vector< std::pair< std::uint32_t, std::vector< vm::instrs::code_block_t > > > &virt_rtns,
std::vector< std::uint8_t > &vmp2file );
} // namespace util
/// <summary>
/// append devirtualized functions to the original binary... patches vm enter jmps to devirtualized code...
/// </summary>
/// <param name="obj">compiled obj file... generated by llvm...</param>
/// <param name="bin">original binary in a vector... this binary gets patched...</param>
/// <returns>returns true if new .devirt section has been appended and all linking was successful...</returns>
bool append( std::vector< std::uint8_t > &obj, std::vector< std::uint8_t > &bin );
} // namespace devirt

@ -1,7 +1,5 @@
#pragma once
#include <functional>
#include <vmp_rtn.hpp>
#include <vmprofiler.hpp>
#include <devirt_t.hpp>
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
@ -16,32 +14,129 @@
#include "llvm/IR/Verifier.h"
#include "llvm/Pass.h"
namespace vmp2::lifters
namespace vm
{
using lifter_callback_t =
std::function< void( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) >;
class lifters_t
{
// private default constructor...
// used for singleton...
lifters_t()
{
}
extern lifter_callback_t lconstq;
extern lifter_callback_t lconstdwsxq;
using lifter_callback_t =
std::function< void( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) >;
extern lifter_callback_t pushvsp;
extern lifter_callback_t addq;
extern lifter_callback_t sregq;
extern lifter_callback_t lregq;
static lifter_callback_t lconstq, lconstdwsxq, lconstwsxq, lconstbzxw, lconstbsxq, lconstwsxdw, lconstdw,
lconstw, lconstbsxdw;
inline std::map< vm::handler::mnemonic_t, lifter_callback_t * > lifters = {
{ vm::handler::LCONSTQ, &lconstq }, { vm::handler::LCONSTDWSXQ, &lconstdwsxq },
{ vm::handler::ADDQ, &addq }, { vm::handler::PUSHVSP, &pushvsp },
{ vm::handler::SREGQ, &sregq }, { vm::handler::LREGQ, &lregq } };
static lifter_callback_t addq, adddw, addw, addb;
static lifter_callback_t sregq, sregdw, sregw, sregb;
static lifter_callback_t lregq, lregdw;
static lifter_callback_t imulq, imuldw;
static lifter_callback_t mulq, muldw;
static lifter_callback_t pushvspq, pushvspdw;
static lifter_callback_t popvsp;
static lifter_callback_t writeq, writedw, writew, writeb;
static lifter_callback_t readq, readdw, readw, readb;
static lifter_callback_t nandq, nanddw, nandw, nandb;
static lifter_callback_t shrq, shrdw, shrw, shrb;
static lifter_callback_t shlq, shldw, shlb;
static lifter_callback_t shldq, shlddw;
static lifter_callback_t shrdq, shrddw;
static lifter_callback_t jmp;
static lifter_callback_t lflagsq;
static lifter_callback_t vmexit;
static lifter_callback_t readcr8;
static lifter_callback_t readcr3;
static lifter_callback_t writecr3;
static lifter_callback_t rdtsc;
static lifter_callback_t readgsq;
static lifter_callback_t divq, divdw;
static lifter_callback_t idivdw;
inline bool lift( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder )
{
if ( vinstr.mnemonic_t == vm::handler::INVALID || lifters.find( vinstr.mnemonic_t ) == lifters.end() )
return false;
std::map< vm::handler::mnemonic_t, lifter_callback_t * > lifters = { { vm::handler::LCONSTQ, &lconstq },
{ vm::handler::LCONSTDW, &lconstdw },
{ vm::handler::LCONSTW, &lconstw },
{ vm::handler::LCONSTDWSXQ, &lconstdwsxq },
{ vm::handler::LCONSTWSXQ, &lconstwsxq },
{ vm::handler::LCONSTBZXW, &lconstbzxw },
{ vm::handler::LCONSTBSXQ, &lconstbsxq },
{ vm::handler::LCONSTWSXDW, &lconstwsxdw },
{ vm::handler::LCONSTBSXDW, &lconstbsxdw },
{ vm::handler::DIVQ, &divq },
{ vm::handler::DIVDW, &divdw },
{ vm::handler::IDIVDW, &idivdw },
{ vm::handler::ADDQ, &addq },
{ vm::handler::ADDDW, &adddw },
{ vm::handler::ADDW, &addw },
{ vm::handler::ADDB, &addb },
{ vm::handler::SHRQ, &shrq },
{ vm::handler::SHRDW, &shrdw },
{ vm::handler::SHRW, &shrw },
{ vm::handler::SHRB, &shrb },
{ vm::handler::SHLQ, &shlq },
{ vm::handler::SHLDW, &shldw },
{ vm::handler::SHLB, &shlb },
{ vm::handler::SHLDQ, &shldq },
{ vm::handler::SHLDDW, &shlddw },
{ vm::handler::SHRDQ, &shrdq },
{ vm::handler::SHRDDW, &shrddw },
{ vm::handler::IMULQ, &imulq },
{ vm::handler::IMULDW, &imuldw },
{ vm::handler::MULQ, &mulq },
{ vm::handler::MULDW, &muldw },
{ vm::handler::PUSHVSPQ, &pushvspq },
{ vm::handler::PUSHVSPDW, &pushvspdw },
{ vm::handler::POPVSPQ, &popvsp },
{ vm::handler::SREGQ, &sregq },
{ vm::handler::SREGDW, &sregdw },
{ vm::handler::SREGW, &sregw },
{ vm::handler::SREGB, &sregb },
{ vm::handler::LREGQ, &lregq },
{ vm::handler::LREGDW, &lregdw },
{ vm::handler::READQ, &readq },
{ vm::handler::READDW, &readdw },
{ vm::handler::READW, &readw },
{ vm::handler::READB, &readb },
{ vm::handler::WRITEQ, &writeq },
{ vm::handler::WRITEDW, &writedw },
{ vm::handler::WRITEW, &writew },
{ vm::handler::WRITEB, &writeb },
{ vm::handler::NANDQ, &nandq },
{ vm::handler::NANDDW, &nanddw },
{ vm::handler::NANDW, &nandw },
{ vm::handler::NANDB, &nandb },
{ vm::handler::LFLAGSQ, &lflagsq },
{ vm::handler::READCR8, &readcr8 },
{ vm::handler::READCR3, &readcr3 },
{ vm::handler::WRITECR3, &writecr3 },
{ vm::handler::RDTSC, &rdtsc },
{ vm::handler::READGSQ, &readgsq },
{ vm::handler::JMP, &jmp },
{ vm::handler::VMEXIT, &vmexit } };
static llvm::Value *and_flags( vm::devirt_t *rtn, std::uint8_t byte_size, llvm::Value *result );
static llvm::Value *add_flags( vm::devirt_t *rtn, std::uint8_t byte_size, llvm::Value *lhs, llvm::Value *rhs );
static llvm::Value *shr_flags( vm::devirt_t *rtn, std::uint8_t byte_size, llvm::Value *lhs, llvm::Value *rhs,
llvm::Value *result );
public:
static lifters_t *get_instance( void )
{
static lifters_t obj;
return &obj;
}
bool lift( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder )
{
if ( vinstr.mnemonic_t == vm::handler::INVALID || lifters.find( vinstr.mnemonic_t ) == lifters.end() )
return false;
( *( lifters[ vinstr.mnemonic_t ] ) )( rtn, vm_code_block, vinstr, ir_builder );
return true;
}
} // namespace vmp2::lifters
( *( lifters[ vinstr.mnemonic_t ] ) )( rtn, vm_code_block, vinstr, ir_builder );
return true;
}
};
} // namespace vm

@ -1,53 +0,0 @@
#pragma once
#include <vmprofiler.hpp>
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
namespace vm
{
class vmp_rtn_t
{
public:
explicit vmp_rtn_t( llvm::LLVMContext *llvm_ctx, llvm::Module *llvm_module, vm::ctx_t *vm_ctx,
std::uintptr_t rtn_begin, std::vector< vm::instrs::code_block_t > vmp2_code_blocks );
llvm::Function *lift( void );
llvm::LLVMContext *llvm_ctx;
vm::ctx_t *vm_ctx;
llvm::Module *llvm_module;
std::uintptr_t rtn_begin;
llvm::Function *llvm_fptr;
std::shared_ptr< llvm::IRBuilder<> > ir_builder;
llvm::AllocaInst *virtual_stack, *stack_ptr;
std::map< ZydisRegister, llvm::GlobalVariable * > native_registers;
std::vector< llvm::AllocaInst * > virtual_registers;
std::vector< llvm::BasicBlock * > llvm_code_blocks;
std::vector< vm::instrs::code_block_t > vmp2_code_blocks;
void push( std::uint8_t byte_size, llvm::Value *input_val );
llvm::Value *pop( std::uint8_t byte_size );
llvm::Value *peek( std::uint8_t byte_size, std::uint8_t byte_offset = 0u );
llvm::Value *load_value( std::uint8_t byte_size, llvm::GlobalValue *global );
llvm::Value *load_value( std::uint8_t byte_size, llvm::AllocaInst *var );
private:
void create_native_registers( void );
void create_virtual_registers( void );
void create_routine( void );
void create_virtual_stack( void );
void lift_vm_entry( void );
};
} // namespace vm

@ -0,0 +1,45 @@
#pragma once
#include <vmprofiler.hpp>
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Mangler.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Verifier.h"
namespace vm
{
class vmp_rtn_t
{
llvm::Function *llvm_fptr;
llvm::AllocaInst *flags, *stack;
llvm::Module *llvm_module;
vmp2::v4::file_header *vmp2_file;
std::uintptr_t rtn_begin;
std::shared_ptr< llvm::IRBuilder<> > ir_builder;
std::vector< llvm::AllocaInst * > virtual_registers;
std::vector< std::pair< std::uintptr_t, llvm::BasicBlock * > > llvm_code_blocks;
std::vector< vm::instrs::code_block_t > vmp2_code_blocks;
void create_virtual_registers( void );
void create_routine( void );
friend class devirt_t;
friend class lifters_t;
public:
explicit vmp_rtn_t( std::uintptr_t rtn_begin, std::vector< vm::instrs::code_block_t > vmp2_code_blocks,
std::shared_ptr< llvm::IRBuilder<> > &ir_builder, llvm::Module *llvm_module,
vmp2::v4::file_header *vmp2_file );
};
} // namespace vm

@ -0,0 +1,196 @@
#include <devirt_t.hpp>
#include <vm_lifters.hpp>
namespace vm
{
devirt_t::devirt_t( llvm::LLVMContext *llvm_ctx, llvm::Module *llvm_module, vmp2::v4::file_header *vmp2_file )
: llvm_ctx( llvm_ctx ), llvm_module( llvm_module ), vmp2_file( vmp2_file )
{
ir_builder = std::make_shared< llvm::IRBuilder<> >( *llvm_ctx );
}
void devirt_t::push( std::uint8_t num_bytes, llvm::Value *val )
{
// sub rsp, num_bytes
auto current_rtn = vmp_rtns.back();
auto rsp_addr = ir_builder->CreateLoad( current_rtn->stack );
rsp_addr->setAlignment( llvm::Align( 1 ) );
auto sub_rsp_val =
ir_builder->CreateGEP( ir_builder->getInt8Ty(), rsp_addr, ir_builder->getInt8( 0 - num_bytes ) );
ir_builder->CreateStore( sub_rsp_val, current_rtn->stack )->setAlignment( llvm::Align( 1 ) );
// mov [rsp], val
auto resized_new_rsp_addr = ir_builder->CreateBitCast(
sub_rsp_val, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, num_bytes * 8 ), 0ull ) );
ir_builder->CreateStore( val, resized_new_rsp_addr )->setAlignment( llvm::Align( 1 ) );
}
llvm::Value *devirt_t::pop( std::uint8_t num_bytes )
{
// mov rax, [rsp]
auto current_rtn = vmp_rtns.back();
auto rsp_addr = ir_builder->CreateLoad( current_rtn->stack );
rsp_addr->setAlignment( llvm::Align( 1 ) );
auto new_rsp_addr =
ir_builder->CreateGEP( ir_builder->getInt8Ty(), rsp_addr, ir_builder->getInt8( num_bytes ) );
auto resized_new_rsp_addr = ir_builder->CreateBitCast(
rsp_addr, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, num_bytes * 8 ), 0ull ) );
auto pop_val = ir_builder->CreateLoad( resized_new_rsp_addr );
pop_val->setAlignment( llvm::Align( 1 ) );
ir_builder->CreateStore( new_rsp_addr, current_rtn->stack )->setAlignment( llvm::Align( 1 ) );
ir_builder->CreateStore( llvm::UndefValue::get( ir_builder->getInt8Ty() ), rsp_addr )
->setAlignment( llvm::Align( 1 ) );
return pop_val;
}
llvm::Value *devirt_t::load_value( std::uint8_t byte_size, llvm::GlobalValue *var )
{
if ( byte_size * 8 != var->getType()->getPrimitiveSizeInBits() )
{
auto cast_ptr = ir_builder->CreatePointerCast(
var, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), 0 ) );
auto result = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), cast_ptr );
result->setAlignment( llvm::Align( 1 ) );
return result;
}
auto result = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), var );
result->setAlignment( llvm::Align( 1 ) );
return result;
}
llvm::Value *devirt_t::load_value( std::uint8_t byte_size, llvm::AllocaInst *var )
{
if ( byte_size * 8 != var->getType()->getPrimitiveSizeInBits() )
{
auto cast_ptr = ir_builder->CreatePointerCast(
var, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), 0 ) );
auto result = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), cast_ptr );
result->setAlignment( llvm::Align( 1 ) );
return result;
}
auto result = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), var );
result->setAlignment( llvm::Align( 1 ) );
return result;
}
bool devirt_t::compile( std::vector< std::uint8_t > &obj )
{
llvm::legacy::FunctionPassManager pass_mgr( llvm_module );
pass_mgr.add( llvm::createPromoteMemoryToRegisterPass() );
pass_mgr.add( llvm::createNewGVNPass() );
pass_mgr.add( llvm::createReassociatePass() );
pass_mgr.add( llvm::createDeadCodeEliminationPass() );
pass_mgr.add( llvm::createInstructionCombiningPass() );
for ( auto vmp_rtn : vmp_rtns )
{
pass_mgr.run( *vmp_rtn->llvm_fptr );
std::printf( "> opt rtn_0x%p\n", vmp_rtn->rtn_begin );
}
llvm::TargetOptions opt;
llvm::SmallVector< char, 128 > buff;
llvm::raw_svector_ostream dest( buff );
llvm::legacy::PassManager pass;
std::string error;
auto target_triple = llvm::sys::getDefaultTargetTriple();
llvm_module->setTargetTriple( target_triple );
auto target = llvm::TargetRegistry::lookupTarget( target_triple, error );
auto reloc_model = llvm::Optional< llvm::Reloc::Model >();
auto target_machine = target->createTargetMachine( target_triple, "generic", "", opt, reloc_model );
llvm_module->setDataLayout( target_machine->createDataLayout() );
target_machine->addPassesToEmitFile( pass, dest, nullptr, llvm::CGFT_ObjectFile );
auto result = pass.run( *llvm_module );
obj.insert( obj.begin(), buff.begin(), buff.end() );
return result;
}
llvm::Function *devirt_t::lift( std::uintptr_t rtn_begin, std::vector< vm::instrs::code_block_t > code_blocks )
{
vmp_rtns.push_back(
std::make_shared< vm::vmp_rtn_t >( rtn_begin, code_blocks, ir_builder, llvm_module, vmp2_file ) );
auto &vmp_rtn = vmp_rtns.back();
auto lifters = vm::lifters_t::get_instance();
for ( auto idx = 0u; idx < vmp_rtn->vmp2_code_blocks.size(); ++idx )
{
ir_builder->SetInsertPoint( vmp_rtn->llvm_code_blocks[ idx ].second );
if ( vmp_rtn->vmp2_code_blocks[ idx ].vinstrs.size() < 35 )
{
ir_builder->CreateRetVoid();
continue;
}
for ( auto &vinstr : vmp_rtn->vmp2_code_blocks[ idx ].vinstrs )
{
if ( !lifters->lift( this, vmp_rtn->vmp2_code_blocks[ idx ], vinstr, ir_builder.get() ) )
{
std::printf(
"> failed to devirtualize virtual instruction with opcode = %d, handler table rva = 0x%x\n",
vinstr.opcode, vinstr.trace_data.regs.r12 - vinstr.trace_data.regs.r13 );
return nullptr;
}
}
}
return vmp_rtn->llvm_fptr;
}
llvm::Value *devirt_t::sf( std::uint8_t byte_size, llvm::Value *val )
{
auto op_size = llvm::IntegerType::get( *llvm_ctx, byte_size * 8 );
auto msb = ir_builder->CreateLShr( val, ( byte_size * 8 ) - 1 );
return ir_builder->CreateZExt( msb, llvm::IntegerType::get( *llvm_ctx, 64 ) );
}
llvm::Value *devirt_t::zf( std::uint8_t byte_size, llvm::Value *val )
{
auto op_size = llvm::IntegerType::get( *llvm_ctx, byte_size * 8 );
auto is_zero = ir_builder->CreateICmpEQ( val, llvm::ConstantInt::get( op_size, 0 ) );
return ir_builder->CreateZExt( is_zero, llvm::IntegerType::get( *llvm_ctx, 64 ) );
}
llvm::Value *devirt_t::pf( std::uint8_t byte_size, llvm::Value *val )
{
auto operand_size = llvm::IntegerType::get( *llvm_ctx, byte_size * 8 );
auto popcount_intrinsic = llvm::Intrinsic::getDeclaration( llvm_module, llvm::Intrinsic::ctpop,
{ llvm::IntegerType::get( *llvm_ctx, 64 ) } );
auto lower_bits = ir_builder->CreateIntCast( val, llvm::IntegerType::get( *llvm_ctx, 8 ), false );
auto extended_bits = ir_builder->CreateZExt( lower_bits, llvm::IntegerType::get( *llvm_ctx, 64 ) );
return ir_builder->CreateCall( popcount_intrinsic, { extended_bits } );
}
llvm::Value *devirt_t::flags( llvm::Value *cf, llvm::Value *pf, llvm::Value *af, llvm::Value *zf, llvm::Value *sf,
llvm::Value *of )
{
auto shifted_pf = ir_builder->CreateShl( pf, 2, "shifted_pf", true, true );
auto shifted_af = ir_builder->CreateShl( llvm::ConstantInt::get( llvm::IntegerType::get( *llvm_ctx, 64 ), 0 ),
4, "shifted_af", true, true ); // treat af as zero
auto shifted_zf = ir_builder->CreateShl( zf, 6, "shifted_zf", true, true );
auto shifted_sf = ir_builder->CreateShl( sf, 7, "shifted_sf", true, true );
auto shifted_of = ir_builder->CreateShl( of, 11, "shifted_of", true, true );
auto or1 = ir_builder->CreateOr( cf, shifted_of );
auto or2 = ir_builder->CreateOr( or1, shifted_zf );
auto or3 = ir_builder->CreateOr( or2, shifted_sf );
auto or4 = ir_builder->CreateOr( or3, shifted_af );
auto or5 = ir_builder->CreateOr( or4, shifted_pf );
return ir_builder->CreateOr( or5, llvm::ConstantInt::get( llvm::IntegerType::get( *llvm_ctx, 64 ), 514 ) );
}
} // namespace vm

@ -0,0 +1,201 @@
#include <devirt_utils.hpp>
namespace devirt
{
namespace util
{
bool serialize_vmp2(
std::vector< std::pair< std::uint32_t, std::vector< vm::instrs::code_block_t > > > &virt_rtns,
std::vector< std::uint8_t > &vmp2file )
{
const auto file_header = reinterpret_cast< vmp2::v4::file_header * >( vmp2file.data() );
if ( file_header->version != vmp2::version_t::v4 )
{
std::printf( "[!] invalid vmp2 file version... this build uses v4...\n" );
return false;
}
auto first_rtn = reinterpret_cast< vmp2::v4::rtn_t * >( reinterpret_cast< std::uintptr_t >( file_header ) +
file_header->rtn_offset );
for ( auto [ rtn_block, rtn_idx ] = std::pair{ first_rtn, 0ull }; rtn_idx < file_header->rtn_count;
++rtn_idx, rtn_block = reinterpret_cast< vmp2::v4::rtn_t * >(
reinterpret_cast< std::uintptr_t >( rtn_block ) + rtn_block->size ) )
{
virt_rtns.push_back( { rtn_block->vm_enter_offset, {} } );
for ( auto [ code_block, block_idx ] = std::pair{ &rtn_block->code_blocks[ 0 ], 0ull };
block_idx < rtn_block->code_block_count;
++block_idx, code_block = reinterpret_cast< vmp2::v4::code_block_t * >(
reinterpret_cast< std::uintptr_t >( code_block ) +
code_block->next_block_offset ) )
{
auto block_vinstrs = reinterpret_cast< vm::instrs::virt_instr_t * >(
reinterpret_cast< std::uintptr_t >( code_block ) + sizeof vmp2::v4::code_block_t +
( code_block->num_block_addrs * 8 ) );
vm::instrs::code_block_t _code_block{ code_block->vip_begin };
_code_block.jcc.has_jcc = code_block->has_jcc;
_code_block.jcc.type = code_block->jcc_type;
for ( auto idx = 0u; idx < code_block->num_block_addrs; ++idx )
_code_block.jcc.block_addr.push_back( code_block->branch_addr[ idx ] );
for ( auto idx = 0u; idx < code_block->vinstr_count; ++idx )
_code_block.vinstrs.push_back( block_vinstrs[ idx ] );
virt_rtns.back().second.push_back( _code_block );
}
}
return true;
}
} // namespace util
bool append( std::vector< std::uint8_t > &obj, std::vector< std::uint8_t > &bin )
{
if ( obj.empty() || bin.empty() )
return false;
std::vector< std::uint8_t > map_buff;
auto bin_img = reinterpret_cast< win::image_t<> * >( bin.data() );
// increase the size_image by the size of the obj as the entire obj will be in the .devirt section...
map_buff.resize(
reinterpret_cast< win::image_t<> * >( bin.data() )->get_nt_headers()->optional_header.size_image +=
obj.size() );
// copy over dos headers, pe headers (section headers, optional header etc)...
std::memcpy( map_buff.data(), bin.data(), bin_img->get_nt_headers()->optional_header.size_headers );
// map sections into map_img... also make image offset == virtual offset...
auto map_img = reinterpret_cast< win::image_t<> * >( map_buff.data() );
std::for_each( bin_img->get_nt_headers()->get_sections(),
bin_img->get_nt_headers()->get_sections() + bin_img->get_file_header()->num_sections,
[ & ]( coff::section_header_t &section ) {
if ( !section.virtual_address || !section.ptr_raw_data )
return;
std::memcpy( map_buff.data() + section.virtual_address, bin.data() + section.ptr_raw_data,
section.size_raw_data );
} );
std::for_each( map_img->get_nt_headers()->get_sections(),
map_img->get_nt_headers()->get_sections() + bin_img->get_file_header()->num_sections,
[ & ]( coff::section_header_t &section ) {
section.ptr_raw_data = section.virtual_address;
section.size_raw_data = section.virtual_size;
} );
auto pe_sections = map_img->get_nt_headers()->get_sections();
auto num_sections = map_img->get_file_header()->num_sections;
// append .devirt section to the original binary...
strcpy( pe_sections[ num_sections ].name.short_name, ".devirt" );
pe_sections[ num_sections ].virtual_size = obj.size();
pe_sections[ num_sections ].size_raw_data = obj.size();
pe_sections[ num_sections ].virtual_address = ( pe_sections[ num_sections - 1 ].virtual_address +
pe_sections[ num_sections - 1 ].virtual_size + 0x1000 ) &
~0xFFFull;
pe_sections[ num_sections ].ptr_raw_data = pe_sections[ num_sections ].virtual_address;
coff::section_characteristics_t prots;
prots.flags = 0ull;
prots.mem_execute = true;
prots.mem_read = true;
pe_sections[ num_sections ].characteristics = prots;
++map_img->get_file_header()->num_sections;
std::memcpy( map_buff.data() + pe_sections[ num_sections ].virtual_address, obj.data(), obj.size() );
auto obj_img_addr = map_buff.data() + pe_sections[ num_sections ].virtual_address;
auto obj_img = reinterpret_cast< coff::image_t * >( obj_img_addr );
auto str_table = obj_img->get_strings();
auto symbols = obj_img->get_symbols();
auto obj_sections = obj_img->get_sections();
auto symbol_cnt = obj_img->file_header.num_symbols;
static const auto get_symbol = [ & ]( std::string symbol_name ) -> auto
{
return std::find_if( symbols, symbols + symbol_cnt, [ & ]( coff::symbol_t &symbol ) {
if ( symbol.derived_type != coff::derived_type_id::function )
return false;
return !strcmp( symbol.name.to_string( str_table ).data(), symbol_name.c_str() );
} );
};
std::vector< std::pair< std::uint32_t, std::uint16_t > > new_relocs;
std::for_each( symbols, symbols + symbol_cnt, [ & ]( coff::symbol_t &symbol ) {
if ( symbol.derived_type != coff::derived_type_id::function )
return;
auto symbol_name = symbol.name.to_string( str_table );
if ( strstr( symbol_name.data(), VM_ENTER_NAME ) )
{
// fix 0x1000000000000000 to 0x0000000000000000
// also push back a new relocation for this entry...
auto symbol_addr = reinterpret_cast< std::uintptr_t >( obj_img_addr ) + symbol.value +
obj_sections[ symbol.section_index - 1 ].ptr_raw_data;
*reinterpret_cast< std::uintptr_t * >( symbol_addr + FIX_MAKE_ZERO_OFFSET ) = 0ull;
*reinterpret_cast< std::uintptr_t * >( symbol_addr + FIX_MAKE_RELOC_OFFSET ) = 0ull;
auto page = ( symbol.value + obj_sections[ symbol.section_index - 1 ].ptr_raw_data +
FIX_MAKE_RELOC_OFFSET + pe_sections[ num_sections ].virtual_address ) &
~0xFFFull;
auto offset = ( symbol.value + obj_sections[ symbol.section_index - 1 ].ptr_raw_data +
FIX_MAKE_RELOC_OFFSET + pe_sections[ num_sections ].virtual_address ) &
0xFFFull;
new_relocs.push_back( { page, offset } );
// look up the rtn_xxxxxx function symbol for this vm enter and make it jmp to it...
auto rtn_offset = std::stoull( symbol_name.data() + sizeof( VM_ENTER_NAME ) - 1, nullptr, 16 );
std::stringstream rtn_name;
rtn_name << "rtn_" << std::hex << rtn_offset;
auto rtn_sym = get_symbol( rtn_name.str() );
auto relocs = reinterpret_cast< coff::reloc_t * >(
obj_sections[ rtn_sym->section_index - 1 ].ptr_relocs + obj_img_addr );
auto rtn_rva = rtn_offset - map_img->get_nt_headers()->optional_header.image_base;
std::int32_t devirt_rtn_rva = symbol_addr - reinterpret_cast< std::uintptr_t >( map_buff.data() );
*reinterpret_cast< std::int32_t * >( rtn_rva + map_buff.data() + 1 ) = devirt_rtn_rva - ( rtn_rva + 5 );
*reinterpret_cast< std::uint8_t * >( rtn_rva + map_buff.data() ) = 0xE9;
// apply relocations to the rtn_xxxxx...
for ( auto reloc_idx = 0u; reloc_idx < obj_sections[ rtn_sym->section_index - 1 ].num_relocs;
++reloc_idx )
{
coff::reloc_t &reloc = relocs[ reloc_idx ];
auto vmexit_sym =
get_symbol( std::string( symbols[ reloc.symbol_index ].name.to_string( str_table ) ) );
auto vmexit_offset = vmexit_sym->value + obj_sections[ vmexit_sym->section_index - 1 ].ptr_raw_data;
auto reloc_file_offset =
reloc.virtual_address + obj_sections[ rtn_sym->section_index - 1 ].ptr_raw_data;
std::int32_t rva = vmexit_offset - ( std::int32_t )( reloc_file_offset + 4 );
*reinterpret_cast< std::int32_t * >( obj_img_addr + reloc_file_offset ) = rva;
}
auto rtn_addr = reinterpret_cast< std::uintptr_t >(
obj_img_addr + obj_sections[ rtn_sym->section_index - 1 ].ptr_raw_data + rtn_sym->value );
// create jmp to the rtn_xxxxx....
std::int32_t rva = rtn_addr - ( std::int64_t )( symbol_addr + FIX_MAKE_JMP_OFFSET + 5 );
*reinterpret_cast< std::uint8_t * >( symbol_addr + FIX_MAKE_JMP_OFFSET ) = 0xE9;
*reinterpret_cast< std::int32_t * >( symbol_addr + FIX_MAKE_JMP_OFFSET + 1 ) = rva;
}
} );
// replace bin vector with map_buff vector...
bin.clear();
bin.insert( bin.begin(), map_buff.begin(), map_buff.end() );
return true;
}
} // namespace devirt

@ -1,17 +1,98 @@
#include <vm_lifters.hpp>
namespace vmp2::lifters
namespace vm
{
lifter_callback_t addq = [ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = rtn->pop( 8 );
auto t3 = ir_builder->CreateAdd( t1, t2 );
rtn->push( 8, t1 );
rtn->push( 8, t3 );
// TODO: compute and update RFLAGS...
rtn->push( 8, rtn->load_value( 8, rtn->native_registers[ ZYDIS_REGISTER_RFLAGS ] ) );
};
}
llvm::Value *lifters_t::add_flags( vm::devirt_t *rtn, std::uint8_t byte_size, llvm::Value *lhs, llvm::Value *rhs )
{
auto op_size = llvm::IntegerType::get( *rtn->llvm_ctx, byte_size * 8 );
std::vector< llvm::Type * > intrinsic_arg_types;
intrinsic_arg_types.push_back( op_size );
intrinsic_arg_types.push_back( op_size );
auto sadd_with_overflow = llvm::Intrinsic::getDeclaration(
rtn->llvm_module, llvm::Intrinsic::sadd_with_overflow, intrinsic_arg_types );
auto uadd_with_overflow = llvm::Intrinsic::getDeclaration(
rtn->llvm_module, llvm::Intrinsic::uadd_with_overflow, intrinsic_arg_types );
auto u_add = rtn->ir_builder->CreateCall( uadd_with_overflow, { lhs, rhs } );
auto u_sum = rtn->ir_builder->CreateExtractValue( u_add, { 0 } );
auto u_of_bit = rtn->ir_builder->CreateExtractValue( u_add, { 1 } );
auto cf = rtn->ir_builder->CreateZExt( u_of_bit, llvm::IntegerType::get( *rtn->llvm_ctx, 64 ) );
auto s_add = rtn->ir_builder->CreateCall( sadd_with_overflow, { lhs, rhs } );
auto s_sum = rtn->ir_builder->CreateExtractValue( s_add, { 0 } );
auto s_of_bit = rtn->ir_builder->CreateExtractValue( s_add, { 1 } );
auto of = rtn->ir_builder->CreateZExt( s_of_bit, llvm::IntegerType::get( *rtn->llvm_ctx, 64 ) );
auto sf = rtn->sf( byte_size, u_sum );
auto zf = rtn->zf( byte_size, u_sum );
auto pf = llvm::ConstantInt::get( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ),
0 ); // TODO make clean PF bit computation...
auto flags_calc =
rtn->flags( cf, pf, llvm::ConstantInt::get( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), 0 ), zf, sf, of );
return flags_calc;
}
lifters_t::lifter_callback_t lifters_t::addq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = rtn->pop( 8 );
auto t3 = ir_builder->CreateAdd( t1, t2 );
rtn->push( 8, t3 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = lifters_t::add_flags( rtn, 8, t1, t2 );
ir_builder->CreateStore( flags, vmp_rtn->flags );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::adddw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 4 );
auto t2 = rtn->pop( 4 );
auto t3 = ir_builder->CreateAdd( t1, t2 );
rtn->push( 4, t3 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = lifters_t::add_flags( rtn, 4, t1, t2 );
ir_builder->CreateStore( flags, vmp_rtn->flags );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::addw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 2 );
auto t2 = rtn->pop( 2 );
auto t3 = ir_builder->CreateAdd( t1, t2 );
rtn->push( 2, t3 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = lifters_t::add_flags( rtn, 2, t1, t2 );
ir_builder->CreateStore( flags, vmp_rtn->flags );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::addb =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 2 );
auto t2 = rtn->pop( 2 );
auto t3 = ir_builder->CreateIntCast( t1, ir_builder->getInt8Ty(), false );
auto t4 = ir_builder->CreateIntCast( t2, ir_builder->getInt8Ty(), false );
auto t5 = ir_builder->CreateAdd( t3, t4 );
rtn->push( 2, ir_builder->CreateIntCast( t5, ir_builder->getInt16Ty(), false ) );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = lifters_t::add_flags( rtn, 1, t3, t4 );
ir_builder->CreateStore( flags, vmp_rtn->flags );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
} // namespace vm

@ -0,0 +1,34 @@
#include <vm_lifters.hpp>
namespace vm
{
lifters_t::lifter_callback_t lifters_t::divq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = rtn->pop( 8 );
auto t3 = rtn->pop( 8 );
ir_builder->CreateUDiv( t2, t3 );
rtn->push( 8, t1 );
rtn->push( 8, t2 );
auto &vmp_rtn = rtn->vmp_rtns.back();
// TODO: compute flags...
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::divdw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 4 );
auto t2 = rtn->pop( 4 );
auto t3 = rtn->pop( 4 );
ir_builder->CreateUDiv( t2, t3 );
rtn->push( 4, t1 );
rtn->push( 4, t2 );
auto &vmp_rtn = rtn->vmp_rtns.back();
// TODO: compute flags...
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
} // namespace vm

@ -0,0 +1,20 @@
#include <vm_lifters.hpp>
namespace vm
{
lifters_t::lifter_callback_t lifters_t::idivdw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 4 );
auto t2 = rtn->pop( 4 );
// TODO: this is wrong...
ir_builder->CreateUDiv( t1, t2 );
rtn->push( 4, t1 );
rtn->push( 4, t2 );
auto &vmp_rtn = rtn->vmp_rtns.back();
// TODO: compute flags...
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
}

@ -0,0 +1,45 @@
#include <vm_lifters.hpp>
// https://lists.llvm.org/pipermail/llvm-dev/2014-July/074685.html
// credit to James Courtier-Dutton for asking this question in 2014...
namespace vm
{
lifters_t::lifter_callback_t lifters_t::imulq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = rtn->pop( 8 );
// TODO: this is wrong... still need to do some more research into this...
auto t3 = ir_builder->CreateMul( t1, t2 );
auto t4 = ir_builder->CreateAShr( t3, llvm::APInt( 64, 32 ) );
auto t5 = ir_builder->CreateAnd( t3, 0xFFFFFFFF00000000 );
rtn->push( 8, t4 );
rtn->push( 8, t5 );
// TODO: compute flags for IMULQ
auto &vmp_rtn = rtn->vmp_rtns.back();
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::imuldw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 4 );
auto t2 = rtn->pop( 4 );
auto t3 = ir_builder->CreateIntCast( t1, ir_builder->getInt64Ty(), false );
auto t4 = ir_builder->CreateIntCast( t2, ir_builder->getInt64Ty(), false );
auto t5 = ir_builder->CreateMul( t3, t4 );
auto t6 = ir_builder->CreateAShr( t5, llvm::APInt( 64, 32 ) );
auto t7 = ir_builder->CreateAnd( t5, 0xFFFFFFFF00000000 );
rtn->push( 8, t6 );
rtn->push( 8, t7 );
// TODO: compute flags for IMULQ
auto &vmp_rtn = rtn->vmp_rtns.back();
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
} // namespace vm

@ -0,0 +1,72 @@
#include <vm_lifters.hpp>
namespace vm
{
lifters_t::lifter_callback_t lifters_t::jmp =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
if ( !vm_code_block.jcc.has_jcc )
{
ir_builder->CreateRetVoid();
return;
}
auto &vmp_rtn = rtn->vmp_rtns.back();
switch ( vm_code_block.jcc.type )
{
case vm::instrs::jcc_type::branching:
{
auto rva = rtn->pop( 8 );
auto b1 = vm_code_block.jcc.block_addr[ 0 ] & std::numeric_limits< std::uint32_t >::max();
auto _const_b1 = llvm::ConstantInt::get( ir_builder->getInt64Ty(), b1 );
auto cmp = ir_builder->CreateCmp( llvm::CmpInst::ICMP_EQ, rva, _const_b1 );
// find the first branch basic block...
auto bb1 =
std::find_if( vmp_rtn->llvm_code_blocks.begin(), vmp_rtn->llvm_code_blocks.end(),
[ & ]( const std::pair< std::uintptr_t, llvm::BasicBlock * > &block_data ) -> bool {
return block_data.first == vm_code_block.jcc.block_addr[ 0 ];
} );
assert( bb1 != vmp_rtn->llvm_code_blocks.end(),
"[!] fatal error... unable to locate basic block for branching...\n" );
// find the second branch basic block...
auto bb2 =
std::find_if( vmp_rtn->llvm_code_blocks.begin(), vmp_rtn->llvm_code_blocks.end(),
[ & ]( const std::pair< std::uintptr_t, llvm::BasicBlock * > &block_data ) -> bool {
return block_data.first == vm_code_block.jcc.block_addr[ 1 ];
} );
assert( bb2 != vmp_rtn->llvm_code_blocks.end(),
"[!] fatal error... unable to locate basic block for branching...\n" );
ir_builder->CreateCondBr( cmp, bb1->second, bb2->second );
break;
}
case vm::instrs::jcc_type::absolute:
{
auto rva = rtn->pop( 8 );
auto bb_data =
std::find_if( vmp_rtn->llvm_code_blocks.begin(), vmp_rtn->llvm_code_blocks.end(),
[ & ]( const std::pair< std::uintptr_t, llvm::BasicBlock * > &block_data ) -> bool {
return block_data.first == vm_code_block.jcc.block_addr[ 0 ];
} );
assert( bb_data != vmp_rtn->llvm_code_blocks.end(),
"[!] fatal error... unable to locate basic block...\n" );
ir_builder->CreateBr( bb_data->second );
break;
}
case vm::instrs::jcc_type::switch_case:
{
// TODO: add switch case support here...
break;
}
default:
break;
}
};
} // namespace vm

@ -1,14 +1,58 @@
#include <vm_lifters.hpp>
namespace vmp2::lifters
namespace vm
{
lifter_callback_t lconstq = [ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
rtn->push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), vinstr.operand.imm.u ) );
};
lifter_callback_t lconstdwsxq = [ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
rtn->push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), vinstr.operand.imm.u ) );
};
} // namespace vmp2::lifters
lifters_t::lifter_callback_t lifters_t::lconstq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
rtn->push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), vinstr.operand.imm.u ) );
};
lifters_t::lifter_callback_t lifters_t::lconstdwsxq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
rtn->push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), vinstr.operand.imm.u ) );
};
lifters_t::lifter_callback_t lifters_t::lconstwsxq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
rtn->push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), vinstr.operand.imm.u ) );
};
lifters_t::lifter_callback_t lifters_t::lconstbsxq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
rtn->push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), vinstr.operand.imm.u ) );
};
lifters_t::lifter_callback_t lifters_t::lconstdw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
rtn->push( 4, llvm::ConstantInt::get( ir_builder->getInt32Ty(), vinstr.operand.imm.u ) );
};
lifters_t::lifter_callback_t lifters_t::lconstw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
rtn->push( 2, llvm::ConstantInt::get( ir_builder->getInt16Ty(), vinstr.operand.imm.u ) );
};
lifters_t::lifter_callback_t lifters_t::lconstwsxdw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
rtn->push( 4, llvm::ConstantInt::get( ir_builder->getInt32Ty(), vinstr.operand.imm.u ) );
};
lifters_t::lifter_callback_t lifters_t::lconstbsxdw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
rtn->push( 4, llvm::ConstantInt::get( ir_builder->getInt32Ty(), vinstr.operand.imm.u ) );
};
lifters_t::lifter_callback_t lifters_t::lconstbzxw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
rtn->push( 2, llvm::ConstantInt::get( ir_builder->getInt16Ty(), vinstr.operand.imm.u ) );
};
} // namespace vm

@ -0,0 +1,12 @@
#include <vm_lifters.hpp>
namespace vm
{
lifters_t::lifter_callback_t lifters_t::lflagsq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = rtn->pop( 8 );
ir_builder->CreateStore( flags, vmp_rtn->flags );
};
}

@ -1,10 +1,20 @@
#include <vm_lifters.hpp>
namespace vmp2::lifters
namespace vm
{
lifter_callback_t lregq = [ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
auto vreg = rtn->virtual_registers[ vinstr.operand.imm.u ? vinstr.operand.imm.u / 8 : 0 ];
rtn->push( 8, rtn->load_value( 8, vreg ) );
};
}
lifters_t::lifter_callback_t lifters_t::lregq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto &vmp_rtn = rtn->vmp_rtns.back();
auto vreg = vmp_rtn->virtual_registers[ vinstr.operand.imm.u ? vinstr.operand.imm.u / 8 : 0 ];
rtn->push( 8, rtn->load_value( 8, vreg ) );
};
lifters_t::lifter_callback_t lifters_t::lregdw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto &vmp_rtn = rtn->vmp_rtns.back();
auto vreg = vmp_rtn->virtual_registers[ vinstr.operand.imm.u ? vinstr.operand.imm.u / 8 : 0 ];
rtn->push( 4, rtn->load_value( 4, vreg ) );
};
} // namespace vm

@ -0,0 +1,40 @@
#include <vm_lifters.hpp>
namespace vm
{
lifters_t::lifter_callback_t lifters_t::mulq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = rtn->pop( 8 );
// TODO: this is wrong... still need to do some more research into this...
auto t3 = ir_builder->CreateMul( t1, t2 );
auto t4 = ir_builder->CreateAShr( t3, llvm::APInt( 64, 32 ) );
auto t5 = ir_builder->CreateAnd( t3, 0xFFFFFFFF00000000 );
rtn->push( 8, t4 );
rtn->push( 8, t5 );
// TODO: compute flags for IMULQ
auto &vmp_rtn = rtn->vmp_rtns.back();
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::muldw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 4 );
auto t2 = rtn->pop( 4 );
// TODO: this is wrong... still need to do some more research into this...
auto t3 = ir_builder->CreateMul( t1, t2 );
auto t4 = ir_builder->CreateAShr( t3, llvm::APInt( 32, 16 ) );
auto t5 = ir_builder->CreateAnd( t3, 0xFFFF0000 );
rtn->push( 4, t4 );
rtn->push( 4, t5 );
// TODO: compute flags for IMULQ
auto &vmp_rtn = rtn->vmp_rtns.back();
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
}

@ -0,0 +1,94 @@
#include <vm_lifters.hpp>
namespace vm
{
llvm::Value *lifters_t::and_flags( vm::devirt_t *rtn, std::uint8_t byte_size, llvm::Value *result )
{
auto cf = llvm::ConstantInt::get( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), 0 );
auto of = llvm::ConstantInt::get( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), 0 );
auto sf = rtn->sf( byte_size, result );
auto zf = rtn->zf( byte_size, result );
auto pf = llvm::ConstantInt::get( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), 0 );
return rtn->flags( cf, pf, llvm::ConstantInt::get( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), 0 ), zf, sf,
of );
}
lifters_t::lifter_callback_t lifters_t::nandq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = rtn->pop( 8 );
auto t1_not = ir_builder->CreateNot( t1 );
auto t2_not = ir_builder->CreateNot( t2 );
auto t3 = ir_builder->CreateAnd( { t1_not, t2_not } );
rtn->push( 8, t3 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = and_flags( rtn, 8, t3 );
ir_builder->CreateStore( flags, vmp_rtn->flags );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::nanddw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 4 );
auto t2 = rtn->pop( 4 );
auto t1_not = ir_builder->CreateNot( t1 );
auto t2_not = ir_builder->CreateNot( t2 );
auto t3 = ir_builder->CreateAnd( { t1_not, t2_not } );
rtn->push( 4, t3 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = and_flags( rtn, 4, t3 );
ir_builder->CreateStore( flags, vmp_rtn->flags );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::nandw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 2 );
auto t2 = rtn->pop( 2 );
auto t1_not = ir_builder->CreateNot( t1 );
auto t2_not = ir_builder->CreateNot( t2 );
auto t3 = ir_builder->CreateAnd( { t1_not, t2_not } );
rtn->push( 2, t3 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = and_flags( rtn, 2, t3 );
ir_builder->CreateStore( flags, vmp_rtn->flags );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::nandb =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 2 );
auto t2 = rtn->pop( 2 );
auto t1_b = ir_builder->CreateIntCast( t1, ir_builder->getInt8Ty(), false );
auto t2_b = ir_builder->CreateIntCast( t2, ir_builder->getInt8Ty(), false );
auto t1_not = ir_builder->CreateNot( t1_b );
auto t2_not = ir_builder->CreateNot( t2_b );
auto t3 = ir_builder->CreateAnd( { t1_not, t2_not } );
auto t3_w = ir_builder->CreateIntCast( t3, ir_builder->getInt16Ty(), false );
rtn->push( 2, t3_w );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = and_flags( rtn, 1, t3 );
ir_builder->CreateStore( flags, vmp_rtn->flags );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
} // namespace vm

@ -0,0 +1,16 @@
#include <vm_lifters.hpp>
namespace vm
{
lifters_t::lifter_callback_t lifters_t::popvsp =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto &vmp_rtn = rtn->vmp_rtns.back();
auto stack = ir_builder->CreateLoad( vmp_rtn->stack );
auto stack_ptr_ptr = ir_builder->CreatePointerCast(
stack, llvm::PointerType::get( llvm::PointerType::get( ir_builder->getInt8Ty(), 0ull ), 0ull ) );
auto stack_ptr = ir_builder->CreateLoad( stack_ptr_ptr );
ir_builder->CreateStore( stack_ptr, vmp_rtn->stack );
};
}

@ -1,20 +1,22 @@
#include <vm_lifters.hpp>
namespace vmp2::lifters
namespace vm
{
lifter_callback_t pushvsp = [ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
// Load the current stack index into a temporary variable
auto current_stack_index =
ir_builder->CreateLoad( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), rtn->stack_ptr, false );
lifters_t::lifter_callback_t lifters_t::pushvspq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto &vmp_rtn = rtn->vmp_rtns.back();
auto stack = ir_builder->CreateLoad( vmp_rtn->stack );
auto stack_ptr = ir_builder->CreatePtrToInt( stack, ir_builder->getInt64Ty() );
rtn->push( 8, stack_ptr );
};
// Get a pointer to the top byte of the stack
llvm::Value *i64_zero = llvm::ConstantInt::get( *rtn->llvm_ctx, llvm::APInt( 64, 0 ) );
llvm::Value *indices[ 2 ] = { i64_zero, current_stack_index };
auto stack_ptr =
ir_builder->CreateInBoundsGEP( rtn->virtual_stack, llvm::ArrayRef< llvm::Value * >( indices, 2 ) );
auto stack_ptr_val = ir_builder->CreatePtrToInt( stack_ptr, ir_builder->getInt64Ty() );
rtn->push( 8, stack_ptr_val );
};
}
lifters_t::lifter_callback_t lifters_t::pushvspdw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto &vmp_rtn = rtn->vmp_rtns.back();
auto stack = ir_builder->CreateLoad( vmp_rtn->stack );
auto stack_ptr = ir_builder->CreatePtrToInt( stack, ir_builder->getInt32Ty() );
rtn->push( 4, stack_ptr );
};
} // namespace vm

@ -0,0 +1,28 @@
#include <vm_lifters.hpp>
namespace vm
{
lifters_t::lifter_callback_t lifters_t::rdtsc =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
llvm::Function *rdtsc_intrin = nullptr;
if ( !( rdtsc_intrin = rtn->llvm_module->getFunction( "rdtsc" ) ) )
{
rdtsc_intrin = llvm::Function::Create( llvm::FunctionType::get( ir_builder->getVoidTy(), false ),
llvm::GlobalValue::LinkageTypes::ExternalLinkage, "rdtsc",
*rtn->llvm_module );
auto entry_block = llvm::BasicBlock::Create( ir_builder->getContext(), "", rdtsc_intrin );
auto ib = ir_builder->GetInsertBlock();
ir_builder->SetInsertPoint( entry_block );
// TODO: put RDTSC code here...
ir_builder->CreateRetVoid();
ir_builder->SetInsertPoint( ib );
}
auto &vmp_rtn = rtn->vmp_rtns.back();
ir_builder->CreateCall( rdtsc_intrin );
};
}

@ -0,0 +1,41 @@
#include <vm_lifters.hpp>
namespace vm
{
lifters_t::lifter_callback_t lifters_t::readq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = ir_builder->CreateIntToPtr( t1, llvm::PointerType::get( ir_builder->getInt64Ty(), 0ull ) );
auto t3 = ir_builder->CreateLoad( ir_builder->getInt64Ty(), t2 );
rtn->push( 8, t3 );
};
lifters_t::lifter_callback_t lifters_t::readdw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = ir_builder->CreateIntToPtr( t1, llvm::PointerType::get( ir_builder->getInt32Ty(), 0ull ) );
auto t3 = ir_builder->CreateLoad( ir_builder->getInt32Ty(), t2 );
rtn->push( 4, t3 );
};
lifters_t::lifter_callback_t lifters_t::readw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = ir_builder->CreateIntToPtr( t1, llvm::PointerType::get( ir_builder->getInt16Ty(), 0ull ) );
auto t3 = ir_builder->CreateLoad( ir_builder->getInt16Ty(), t2 );
rtn->push( 2, t3 );
};
lifters_t::lifter_callback_t lifters_t::readb =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = ir_builder->CreateIntToPtr( t1, llvm::PointerType::get( ir_builder->getInt8Ty(), 0ull ) );
auto t3 = ir_builder->CreateLoad( ir_builder->getInt8Ty(), t2 );
auto t4 = ir_builder->CreateIntCast( t3, ir_builder->getInt16Ty(), false );
rtn->push( 2, t4 );
};
} // namespace vm

@ -0,0 +1,30 @@
#include <vm_lifters.hpp>
namespace vm
{
lifters_t::lifter_callback_t lifters_t::readcr3 =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
llvm::Function *readcr3_intrin = nullptr;
if ( !( readcr3_intrin = rtn->llvm_module->getFunction( "readcr3" ) ) )
{
readcr3_intrin = llvm::Function::Create( llvm::FunctionType::get( ir_builder->getInt64Ty(), false ),
llvm::GlobalValue::LinkageTypes::ExternalLinkage, "readcr3",
*rtn->llvm_module );
auto entry_block = llvm::BasicBlock::Create( ir_builder->getContext(), "", readcr3_intrin );
auto ib = ir_builder->GetInsertBlock();
ir_builder->SetInsertPoint( entry_block );
std::string asm_str( "mov rax, cr3; ret" );
auto intrin = llvm::InlineAsm::get( llvm::FunctionType::get( ir_builder->getVoidTy(), false ), asm_str,
"", false, false, llvm::InlineAsm::AD_Intel );
ir_builder->CreateCall( intrin );
ir_builder->CreateRet( llvm::ConstantInt::get( *rtn->llvm_ctx, llvm::APInt( 64, 0 ) ) );
ir_builder->SetInsertPoint( ib );
}
auto t1 = ir_builder->CreateCall( readcr3_intrin );
rtn->push( 8, t1 );
};
}

@ -0,0 +1,30 @@
#include <vm_lifters.hpp>
namespace vm
{
lifters_t::lifter_callback_t lifters_t::readcr8 =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
llvm::Function *readcr8_intrin = nullptr;
if ( !( readcr8_intrin = rtn->llvm_module->getFunction( "readcr8" ) ) )
{
readcr8_intrin = llvm::Function::Create( llvm::FunctionType::get( ir_builder->getInt64Ty(), false ),
llvm::GlobalValue::LinkageTypes::ExternalLinkage, "readcr8",
*rtn->llvm_module );
auto entry_block = llvm::BasicBlock::Create( ir_builder->getContext(), "", readcr8_intrin );
auto ib = ir_builder->GetInsertBlock();
ir_builder->SetInsertPoint( entry_block );
std::string asm_str( "mov rax, cr8; ret" );
auto intrin = llvm::InlineAsm::get( llvm::FunctionType::get( ir_builder->getVoidTy(), false ), asm_str,
"", false, false, llvm::InlineAsm::AD_Intel );
ir_builder->CreateCall( intrin );
ir_builder->CreateRet( llvm::ConstantInt::get( *rtn->llvm_ctx, llvm::APInt( 64, 0 ) ) );
ir_builder->SetInsertPoint( ib );
}
auto t1 = ir_builder->CreateCall( readcr8_intrin );
rtn->push( 8, t1 );
};
}

@ -0,0 +1,32 @@
#include <vm_lifters.hpp>
namespace vm
{
lifters_t::lifter_callback_t lifters_t::readgsq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
llvm::Function *readgs_intrin = nullptr;
if ( !( readgs_intrin = rtn->llvm_module->getFunction( "readgs" ) ) )
{
readgs_intrin = llvm::Function::Create(
llvm::FunctionType::get( ir_builder->getInt64Ty(), { ir_builder->getInt64Ty() }, false ),
llvm::GlobalValue::LinkageTypes::ExternalLinkage, "readgs", *rtn->llvm_module );
auto entry_block = llvm::BasicBlock::Create( ir_builder->getContext(), "", readgs_intrin );
auto ib = ir_builder->GetInsertBlock();
ir_builder->SetInsertPoint( entry_block );
std::string asm_str( "mov rax, gs:[rcx]; ret" );
auto intrin = llvm::InlineAsm::get( llvm::FunctionType::get( ir_builder->getVoidTy(), false ), asm_str,
"", false, false, llvm::InlineAsm::AD_Intel );
ir_builder->CreateCall( intrin );
ir_builder->CreateRet( llvm::ConstantInt::get( *rtn->llvm_ctx, llvm::APInt( 64, 0 ) ) );
ir_builder->SetInsertPoint( ib );
}
auto t1 = rtn->pop( 8 );
auto t2 = ir_builder->CreateCall( readgs_intrin, { t1 } );
rtn->push( 8, t2 );
};
} // namespace vm

@ -0,0 +1,50 @@
#include <vm_lifters.hpp>
namespace vm
{
lifters_t::lifter_callback_t lifters_t::shlq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = rtn->pop( 2 );
auto t3 = ir_builder->CreateIntCast( t2, llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), false );
auto t4 = ir_builder->CreateShl( t1, t3 );
auto &vmp_rtn = rtn->vmp_rtns.back();
// TODO: update rflags...
rtn->push( 8, t4 );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::shldw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 4 );
auto t2 = rtn->pop( 2 );
auto t3 = ir_builder->CreateIntCast( t2, llvm::IntegerType::get( *rtn->llvm_ctx, 32 ), false );
auto t4 = ir_builder->CreateShl( t1, t3 );
auto &vmp_rtn = rtn->vmp_rtns.back();
// TODO: update rflags...
rtn->push( 4, t4 );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::shlb =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 2 );
auto t2 = rtn->pop( 2 );
auto t3 = ir_builder->CreateIntCast( t1, ir_builder->getInt8Ty(), false );
auto t4 = ir_builder->CreateIntCast( t2, ir_builder->getInt8Ty(), false );
auto t5 = ir_builder->CreateShl( t3, t4 );
auto &vmp_rtn = rtn->vmp_rtns.back();
// TODO: update rflags...
rtn->push( 2, ir_builder->CreateIntCast( t5, ir_builder->getInt16Ty(), false ) );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
} // namespace vm

@ -0,0 +1,38 @@
#include <vm_lifters.hpp>
namespace vm
{
lifters_t::lifter_callback_t lifters_t::shldq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = rtn->pop( 8 );
auto t3 = rtn->pop( 2 );
// TODO: this is wrong - replace with more logic!
auto t4 = ir_builder->CreateShl( t1, ir_builder->CreateIntCast( t3, ir_builder->getInt64Ty(), false ) );
rtn->push( 8, t4 );
auto &vmp_rtn = rtn->vmp_rtns.back();
// TODO: update rflags...
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::shlddw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 4 );
auto t2 = rtn->pop( 4 );
auto t3 = rtn->pop( 2 );
// TODO: this is wrong - replace with more logic!
auto t4 = ir_builder->CreateShl( t1, ir_builder->CreateIntCast( t3, ir_builder->getInt32Ty(), false ) );
rtn->push( 4, t4 );
auto &vmp_rtn = rtn->vmp_rtns.back();
// TODO: update rflags...
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
} // namespace vm

@ -0,0 +1,80 @@
#include <vm_lifters.hpp>
namespace vm
{
llvm::Value *lifters_t::shr_flags( vm::devirt_t *rtn, std::uint8_t byte_size, llvm::Value *lhs, llvm::Value *rhs,
llvm::Value *result )
{
auto op_size = llvm::IntegerType::get( *rtn->llvm_ctx, byte_size * 8 );
auto msb = rtn->ir_builder->CreateLShr( lhs, ( byte_size * 8 ) - 1 );
auto cf = rtn->ir_builder->CreateZExt( msb, llvm::IntegerType::get( *rtn->llvm_ctx, 64 ) );
auto of = rtn->sf( byte_size, lhs );
auto sf = rtn->sf( byte_size, result );
auto zf = rtn->zf( byte_size, result );
auto pf = llvm::ConstantInt::get( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), 0 );
return rtn->flags( cf, pf, llvm::ConstantInt::get( llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), 0 ), zf, sf,
of );
}
lifters_t::lifter_callback_t lifters_t::shrq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = rtn->pop( 2 );
auto t3 = ir_builder->CreateIntCast( t2, llvm::IntegerType::get( *rtn->llvm_ctx, 64 ), false );
auto t4 = ir_builder->CreateLShr( t1, t3 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = lifters_t::shr_flags( rtn, 8, t1, t3, t4 );
ir_builder->CreateStore( flags, vmp_rtn->flags );
rtn->push( 8, t4 );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::shrdw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 4 );
auto t2 = rtn->pop( 2 );
auto t3 = ir_builder->CreateIntCast( t2, llvm::IntegerType::get( *rtn->llvm_ctx, 32 ), false );
auto t4 = ir_builder->CreateLShr( t1, t3 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = lifters_t::shr_flags( rtn, 4, t1, t3, t4 );
ir_builder->CreateStore( flags, vmp_rtn->flags );
rtn->push( 4, t4 );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::shrw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 2 );
auto t2 = rtn->pop( 2 );
auto t3 = ir_builder->CreateLShr( t1, t2 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = lifters_t::shr_flags( rtn, 2, t1, t2, t3 );
ir_builder->CreateStore( flags, vmp_rtn->flags );
rtn->push( 2, t3 );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::shrb =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 2 );
auto t2 = rtn->pop( 2 );
auto t3 = ir_builder->CreateIntCast( t1, ir_builder->getInt8Ty(), false );
auto t4 = ir_builder->CreateIntCast( t2, ir_builder->getInt8Ty(), false );
auto t5 = ir_builder->CreateLShr( t3, t4 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto flags = lifters_t::shr_flags( rtn, 1, t3, t4, t5 );
ir_builder->CreateStore( flags, vmp_rtn->flags );
rtn->push( 2, ir_builder->CreateIntCast( t5, ir_builder->getInt16Ty(), false ) );
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
} // namespace vm

@ -0,0 +1,38 @@
#include <vm_lifters.hpp>
namespace vm
{
lifters_t::lifter_callback_t lifters_t::shrdq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = rtn->pop( 8 );
auto t3 = rtn->pop( 2 );
// TODO: this is wrong - replace with more logic!
auto t4 = ir_builder->CreateLShr( t1, ir_builder->CreateIntCast( t3, ir_builder->getInt64Ty(), false ) );
rtn->push( 8, t4 );
auto &vmp_rtn = rtn->vmp_rtns.back();
// TODO: update rflags...
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
lifters_t::lifter_callback_t lifters_t::shrddw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 4 );
auto t2 = rtn->pop( 4 );
auto t3 = rtn->pop( 2 );
// TODO: this is wrong - replace with more logic!
auto t4 = ir_builder->CreateLShr( t1, ir_builder->CreateIntCast( t3, ir_builder->getInt32Ty(), false ) );
rtn->push( 4, t4 );
auto &vmp_rtn = rtn->vmp_rtns.back();
// TODO: update rflags...
rtn->push( 8, rtn->load_value( 8, vmp_rtn->flags ) );
};
} // namespace vm

@ -1,14 +1,44 @@
#include <vm_lifters.hpp>
namespace vmp2::lifters
namespace vm
{
// %t1 = %stack[%sp]
// add %sp, 8
// %vregX = %1
lifter_callback_t sregq = [ & ]( vm::vmp_rtn_t *rtn, const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr, llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto vreg = rtn->virtual_registers[ vinstr.operand.imm.u ? vinstr.operand.imm.u / 8 : 0 ];
ir_builder->CreateStore( t1, vreg );
};
} // namespace vmp2::lifters
lifters_t::lifter_callback_t lifters_t::sregq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto vreg = vmp_rtn->virtual_registers[ vinstr.operand.imm.u ? vinstr.operand.imm.u / 8 : 0 ];
ir_builder->CreateStore( t1, vreg );
};
lifters_t::lifter_callback_t lifters_t::sregdw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 4 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto vreg = vmp_rtn->virtual_registers[ vinstr.operand.imm.u ? vinstr.operand.imm.u / 8 : 0 ];
ir_builder->CreateStore( ir_builder->CreateIntCast( t1, ir_builder->getInt64Ty(), false ), vreg );
};
lifters_t::lifter_callback_t lifters_t::sregw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 2 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto vreg = vmp_rtn->virtual_registers[ vinstr.operand.imm.u ? vinstr.operand.imm.u / 8 : 0 ];
ir_builder->CreateStore(
ir_builder->CreateIntCast( t1, ir_builder->getInt16Ty(), false ),
ir_builder->CreatePointerCast( vreg, llvm::PointerType::get( ir_builder->getInt16Ty(), 0ull ) ) );
};
lifters_t::lifter_callback_t lifters_t::sregb =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 2 );
auto &vmp_rtn = rtn->vmp_rtns.back();
auto vreg = vmp_rtn->virtual_registers[ vinstr.operand.imm.u ? vinstr.operand.imm.u / 8 : 0 ];
ir_builder->CreateStore(
ir_builder->CreateIntCast( t1, ir_builder->getInt8Ty(), false ),
ir_builder->CreatePointerCast( vreg, llvm::PointerType::get( ir_builder->getInt8Ty(), 0ull ) ) );
};
} // namespace vm

@ -0,0 +1,61 @@
#include <vm_lifters.hpp>
namespace vm
{
lifters_t::lifter_callback_t lifters_t::vmexit = [ & ]( vm::devirt_t *rtn,
const vm::instrs::code_block_t &vm_code_block,
const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
std::stringstream rtn_name;
llvm::Function *exit_func = nullptr;
rtn_name << "vmexit_" << std::hex << vinstr.trace_data.vm_handler_rva + rtn->vmp2_file->image_base;
if ( !( exit_func = rtn->llvm_module->getFunction( rtn_name.str() ) ) )
{
auto vmexit_func_type = llvm::FunctionType::get(
ir_builder->getVoidTy(), llvm::PointerType::getInt8PtrTy( ir_builder->getContext() ) );
exit_func = llvm::Function::Create( vmexit_func_type, llvm::GlobalValue::LinkageTypes::ExternalLinkage,
rtn_name.str().c_str(), *rtn->llvm_module );
auto entry_block = llvm::BasicBlock::Create( ir_builder->getContext(), "", exit_func );
auto vmexit_handler_addr = reinterpret_cast< std::uintptr_t >( rtn->vmp2_file ) +
rtn->vmp2_file->module_offset + vinstr.trace_data.vm_handler_rva;
zydis_routine_t vmexit_handler;
vm::util::flatten( vmexit_handler, vmexit_handler_addr );
vm::util::deobfuscate( vmexit_handler );
std::string asm_str( "mov rsp, rcx; " );
ZydisFormatter formatter;
ZydisFormatterInit( &formatter, ZYDIS_FORMATTER_STYLE_INTEL );
for ( const auto &instr_data : vmexit_handler )
{
if ( instr_data.instr.mnemonic == ZYDIS_MNEMONIC_POP ||
instr_data.instr.mnemonic == ZYDIS_MNEMONIC_POPFQ )
{
char buffer[ 256 ];
ZydisFormatterFormatInstruction( &formatter, &instr_data.instr, buffer, sizeof( buffer ), 0ull );
asm_str.append( buffer ).append( "; " );
}
}
asm_str.append( "ret" );
auto ib = ir_builder->GetInsertBlock();
ir_builder->SetInsertPoint( entry_block );
auto exit_stub = llvm::InlineAsm::get( llvm::FunctionType::get( ir_builder->getVoidTy(), false ), asm_str,
"", false, false, llvm::InlineAsm::AD_Intel );
ir_builder->CreateCall( exit_stub );
ir_builder->CreateRetVoid();
ir_builder->SetInsertPoint( ib );
}
auto &vmp_rtn = rtn->vmp_rtns.back();
auto stack_ptr = ir_builder->CreateLoad( vmp_rtn->stack );
ir_builder->CreateCall( exit_func, stack_ptr );
ir_builder->CreateRet( stack_ptr );
};
} // namespace vm

@ -0,0 +1,41 @@
#include <vm_lifters.hpp>
namespace vm
{
lifters_t::lifter_callback_t lifters_t::writeq =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = rtn->pop( 8 );
auto t3 = ir_builder->CreateIntToPtr( t1, llvm::PointerType::get( ir_builder->getInt64Ty(), 0ull ) );
ir_builder->CreateStore( t2, t3 );
};
lifters_t::lifter_callback_t lifters_t::writedw =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = rtn->pop( 4 );
auto t3 = ir_builder->CreateIntToPtr( t1, llvm::PointerType::get( ir_builder->getInt32Ty(), 0ull ) );
ir_builder->CreateStore( t2, t3 );
};
lifters_t::lifter_callback_t lifters_t::writew =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = rtn->pop( 2 );
auto t3 = ir_builder->CreateIntToPtr( t1, llvm::PointerType::get( ir_builder->getInt16Ty(), 0ull ) );
ir_builder->CreateStore( t2, t3 );
};
lifters_t::lifter_callback_t lifters_t::writeb =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
auto t1 = rtn->pop( 8 );
auto t2 = rtn->pop( 2 );
auto t3 = ir_builder->CreateIntToPtr( t1, llvm::PointerType::get( ir_builder->getInt8Ty(), 0ull ) );
auto t4 = ir_builder->CreateIntCast( t2, ir_builder->getInt8Ty(), false );
ir_builder->CreateStore( t4, t3 );
};
} // namespace vm

@ -0,0 +1,31 @@
#include <vm_lifters.hpp>
namespace vm
{
lifters_t::lifter_callback_t lifters_t::writecr3 =
[ & ]( vm::devirt_t *rtn, const vm::instrs::code_block_t &vm_code_block, const vm::instrs::virt_instr_t &vinstr,
llvm::IRBuilder<> *ir_builder ) {
llvm::Function *writecr3_intrin = nullptr;
if ( !( writecr3_intrin = rtn->llvm_module->getFunction( "writecr3" ) ) )
{
writecr3_intrin = llvm::Function::Create(
llvm::FunctionType::get( ir_builder->getVoidTy(), { ir_builder->getInt64Ty() }, false ),
llvm::GlobalValue::LinkageTypes::ExternalLinkage, "writecr3", *rtn->llvm_module );
auto entry_block = llvm::BasicBlock::Create( ir_builder->getContext(), "", writecr3_intrin );
auto ib = ir_builder->GetInsertBlock();
ir_builder->SetInsertPoint( entry_block );
std::string asm_str( "mov cr3, rcx; ret" );
auto intrin = llvm::InlineAsm::get(
llvm::FunctionType::get( ir_builder->getVoidTy(), false ), asm_str,
"", false, false, llvm::InlineAsm::AD_Intel );
ir_builder->CreateCall( intrin );
ir_builder->CreateRetVoid();
ir_builder->SetInsertPoint( ib );
}
auto t1 = rtn->pop( 8 );
ir_builder->CreateCall( writecr3_intrin, { t1 } );
};
}

@ -1,15 +1,17 @@
#include <Windows.h>
#include <cli-parser.hpp>
#include <vmp_rtn.hpp>
#include <vmprofiler.hpp>
#include <devirt_t.hpp>
#include <devirt_utils.hpp>
#include <xtils.hpp>
using namespace llvm;
int main( int argc, const char *argv[] )
{
argparse::argument_parser_t parser( "vmdevirt", "virtual instruction pseudo code generator" );
parser.add_argument().name( "--vmp2file" ).required( true ).description( "path to .vmp2 file..." );
parser.add_argument().name( "--out" ).required( true ).description( "output devirtualized binary name..." );
parser.add_argument().name( "--genobj" ).description( "write the generated obj file to disk..." );
parser.add_argument().name( "--bin" ).required( true ).description(
"path to the image in which to apply devirtualized code too...\n" );
parser.enable_help();
auto err = parser.parse( argc, argv );
@ -26,7 +28,7 @@ int main( int argc, const char *argv[] )
return 0;
}
std::vector< std::uint8_t > vmp2file;
std::vector< std::uint8_t > vmp2file, bin;
const auto umtils = xtils::um_t::get_instance();
if ( !umtils->open_binary_file( parser.get< std::string >( "vmp2file" ), vmp2file ) )
@ -35,46 +37,55 @@ int main( int argc, const char *argv[] )
return -1;
}
const auto file_header = reinterpret_cast< vmp2::v3::file_header * >( vmp2file.data() );
if ( file_header->version != vmp2::version_t::v3 )
if ( !umtils->open_binary_file( parser.get< std::string >( "bin" ), bin ) )
{
std::printf( "[!] invalid vmp2 file version... this build uses v3...\n" );
std::printf( "[!] failed to open original binary file...\n" );
return -1;
}
auto first_block = reinterpret_cast< vmp2::v3::code_block_t * >( reinterpret_cast< std::uintptr_t >( file_header ) +
file_header->code_block_offset );
const auto file_header = reinterpret_cast< vmp2::v4::file_header * >( vmp2file.data() );
std::vector< std::pair< std::uint32_t, std::vector< vm::instrs::code_block_t > > > virt_rtns;
std::vector< vm::instrs::code_block_t > vmp_code_blocks;
// convert code blocks back to vm::instrs::code_block_t form...
for ( auto [ code_block, code_block_num ] = std::tuple{ first_block, 0u };
code_block_num < file_header->code_block_count;
code_block = reinterpret_cast< vmp2::v3::code_block_t * >( reinterpret_cast< std::uintptr_t >( code_block ) +
code_block->next_block_offset ),
++code_block_num )
if ( !devirt::util::serialize_vmp2( virt_rtns, vmp2file ) )
{
vm::instrs::code_block_t _code_block{ code_block->vip_begin, code_block->jcc };
std::for_each( code_block->vinstr, code_block->vinstr + code_block->vinstr_count,
[ & ]( const vm::instrs::virt_instr_t &vinstr ) { _code_block.vinstrs.push_back( vinstr ); } );
vmp_code_blocks.push_back( _code_block );
std::printf( "> failed to serialize vmp2 file...\n" );
return false;
}
LLVMContext llvm_ctx;
Module llvm_module( "", llvm_ctx );
llvm::LLVMContext llvm_ctx;
llvm::Module llvm_module( "VMProtect 2 Devirtualization", llvm_ctx );
vm::ctx_t vm_ctx( reinterpret_cast< std::uintptr_t >( file_header ) + file_header->module_offset,
file_header->image_base, file_header->module_size, file_header->vm_entry_rva );
std::vector< std::uint8_t > compiled_obj;
vm::devirt_t vmp_devirt( &llvm_ctx, &llvm_module, file_header );
if ( !vm_ctx.init() )
for ( auto &[ vm_enter_offset, vmp2_code_blocks ] : virt_rtns )
{
std::printf( "> failed to init vm::ctx_t... this can be for many reason... make sure you are using the correct "
"vmemu version for this project...\n" );
if ( vmp2_code_blocks.empty() )
continue;
return false;
if ( !vmp_devirt.lift( vm_enter_offset + file_header->image_base, vmp2_code_blocks ) )
{
std::printf( "[!] failed to lift rtn_0x%p, please review the console...\n",
vm_enter_offset + file_header->image_base );
return -1;
}
std::printf( "> lifted rtn_0x%p\n", vm_enter_offset + file_header->image_base );
}
vm::vmp_rtn_t vmp_rtn( &llvm_ctx, &llvm_module, &vm_ctx, first_block->vip_begin, vmp_code_blocks );
vmp_rtn.lift();
llvm::LLVMInitializeX86TargetInfo();
llvm::LLVMInitializeX86Target();
llvm::LLVMInitializeX86TargetMC();
llvm::LLVMInitializeX86AsmParser();
llvm::LLVMInitializeX86AsmPrinter();
vmp_devirt.compile( compiled_obj );
if ( parser.exists( "genobj" ) )
std::ofstream( parser.get< std::string >( "out" ).append( ".o" ), std::ios::binary )
.write( reinterpret_cast< const char * >( compiled_obj.data() ), compiled_obj.size() );
devirt::append( compiled_obj, bin );
std::ofstream( parser.get< std::string >( "out" ), std::ios::binary )
.write( reinterpret_cast< const char * >( bin.data() ), bin.size() );
}

@ -1,285 +0,0 @@
#include <vm_lifters.hpp>
#include <vmp_rtn.hpp>
namespace vm
{
vmp_rtn_t::vmp_rtn_t( llvm::LLVMContext *llvm_ctx, llvm::Module *llvm_module, vm::ctx_t *vm_ctx,
std::uintptr_t rtn_begin, std::vector< vm::instrs::code_block_t > vmp2_code_blocks )
: llvm_ctx( llvm_ctx ), llvm_module( llvm_module ), vm_ctx( vm_ctx ), rtn_begin( rtn_begin ),
vmp2_code_blocks( vmp2_code_blocks )
{
// create llvm::Function and llvm::BasicBlock's...
create_routine();
// set the insert point to the first code block...
ir_builder = std::make_shared< llvm::IRBuilder<> >( *llvm_ctx );
ir_builder->SetInsertPoint( llvm_code_blocks[ 0 ] );
// create native registers...
create_native_registers();
// create stack and stack pointer...
create_virtual_stack();
// lift vm enter pushes to llvm ir...
lift_vm_entry();
// create virtual registers...
create_virtual_registers();
}
void vmp_rtn_t::lift_vm_entry( void )
{
for ( const auto &instr : vm_ctx->vm_entry )
{
if ( instr.instr.mnemonic == ZYDIS_MNEMONIC_PUSH )
{
// push [xxxxx] we know this is zero and the next push is the image base...
if ( instr.instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_MEMORY )
{
push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), 0ull ) );
push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), vm_ctx->image_base ) );
break; // dont make these if statements a switch case because we need to use this break...
}
else if ( instr.instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_IMMEDIATE )
{
push( 8, llvm::ConstantInt::get( ir_builder->getInt64Ty(), instr.instr.operands[ 0 ].imm.value.u,
false ) );
}
else if ( instr.instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER )
{
push( 8, load_value( 8, native_registers[ instr.instr.operands[ 0 ].reg.value ] ) );
}
}
else if ( instr.instr.mnemonic == ZYDIS_MNEMONIC_PUSHFQ )
{
// just push 0 as the value itself wont matter...
push( 8, load_value( 8, native_registers[ ZYDIS_REGISTER_RFLAGS ] ) );
}
}
}
void vmp_rtn_t::create_native_registers( void )
{
llvm_module->getOrInsertGlobal( "rax", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_RAX ] = llvm_module->getGlobalVariable( "rax" );
llvm_module->getOrInsertGlobal( "rbx", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_RBX ] = llvm_module->getGlobalVariable( "rbx" );
llvm_module->getOrInsertGlobal( "rcx", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_RCX ] = llvm_module->getGlobalVariable( "rcx" );
llvm_module->getOrInsertGlobal( "rdx", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_RDX ] = llvm_module->getGlobalVariable( "rdx" );
llvm_module->getOrInsertGlobal( "rsi", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_RSI ] = llvm_module->getGlobalVariable( "rsi" );
llvm_module->getOrInsertGlobal( "rdi", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_RDI ] = llvm_module->getGlobalVariable( "rdi" );
llvm_module->getOrInsertGlobal( "rbp", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_RBP ] = llvm_module->getGlobalVariable( "rbp" );
llvm_module->getOrInsertGlobal( "rsp", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_RSP ] = llvm_module->getGlobalVariable( "rsp" );
llvm_module->getOrInsertGlobal( "r8", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_R8 ] = llvm_module->getGlobalVariable( "r8" );
llvm_module->getOrInsertGlobal( "r9", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_R9 ] = llvm_module->getGlobalVariable( "r9" );
llvm_module->getOrInsertGlobal( "r10", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_R10 ] = llvm_module->getGlobalVariable( "r10" );
llvm_module->getOrInsertGlobal( "r11", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_R11 ] = llvm_module->getGlobalVariable( "r11" );
llvm_module->getOrInsertGlobal( "r12", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_R12 ] = llvm_module->getGlobalVariable( "r12" );
llvm_module->getOrInsertGlobal( "r13", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_R13 ] = llvm_module->getGlobalVariable( "r13" );
llvm_module->getOrInsertGlobal( "r14", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_R14 ] = llvm_module->getGlobalVariable( "r14" );
llvm_module->getOrInsertGlobal( "r15", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_R15 ] = llvm_module->getGlobalVariable( "r15" );
llvm_module->getOrInsertGlobal( "rflags", ir_builder->getInt64Ty() );
native_registers[ ZYDIS_REGISTER_RFLAGS ] = llvm_module->getGlobalVariable( "rflags" );
}
void vmp_rtn_t::create_virtual_registers( void )
{
for ( auto idx = 0u; idx < 24; ++idx )
// allocate virtual register space...
virtual_registers.push_back(
ir_builder->CreateAlloca( llvm::IntegerType::get( *llvm_ctx, 64 ), nullptr,
( std::string( "vreg" ) + std::to_string( idx ) ).c_str() ) );
}
void vmp_rtn_t::create_routine( void )
{
// function has no arguments and returns void... maybe change this in the future as i learn
// more and more LLVM...
auto func_ty = llvm::FunctionType::get( llvm::Type::getVoidTy( *llvm_ctx ), false );
// convert the rtn_begin address to a hex string and prepend "rtn_" to it...
std::stringstream rtn_name;
rtn_name << "rtn_" << std::hex << rtn_begin;
llvm_fptr = llvm::Function::Create( func_ty, llvm::GlobalValue::LinkageTypes::ExternalLinkage,
rtn_name.str().c_str(), *llvm_module );
for ( const auto &vmp2_code_block : vmp2_code_blocks )
{
// create basic block name... block_xxxxxxxx format...
std::stringstream blk_name;
blk_name << "blk_" << std::hex << vmp2_code_block.vip_begin;
llvm_code_blocks.push_back( llvm::BasicBlock::Create( *llvm_ctx, blk_name.str().c_str(), llvm_fptr ) );
}
}
void vmp_rtn_t::push( std::uint8_t byte_size, llvm::Value *input_value )
{
// Load the current stack index into a temporary variable
auto current_stack_index = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, 64 ), stack_ptr, false );
// Subtract the input value size from the current stack index
auto new_stack_index = ir_builder->CreateSub(
current_stack_index, llvm::ConstantInt::get( llvm::IntegerType::get( *llvm_ctx, 64 ), byte_size ) );
// Store the newly calculated stack index into VSP
ir_builder->CreateStore( new_stack_index, stack_ptr );
// Get a pointer to the top byte of the stack
llvm::Value *i64_zero = llvm::ConstantInt::get( *llvm_ctx, llvm::APInt( 64, 0 ) );
llvm::Value *indices[ 2 ] = { i64_zero, new_stack_index };
auto stack_ptr = ir_builder->CreateInBoundsGEP( virtual_stack, llvm::ArrayRef< llvm::Value * >( indices, 2 ) );
// Store the input value at the calculated stack address
if ( byte_size == 1 )
{
ir_builder->CreateStore( input_value, stack_ptr );
}
else
{
// Cast the pointer so the stack item width matches the width of the input value
auto casted_ptr = ir_builder->CreatePointerCast(
stack_ptr, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), 0 ) );
ir_builder->CreateStore( input_value, casted_ptr );
}
}
llvm::Value *vmp_rtn_t::pop( std::uint8_t byte_size )
{
// Load the current stack index into a temporary variable
auto current_stack_index = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, 64 ), stack_ptr, false );
// Get a pointer to the top byte of the stack
llvm::Value *i64_zero = llvm::ConstantInt::get( *llvm_ctx, llvm::APInt( 64, 0 ) );
llvm::Value *indices[ 2 ] = { i64_zero, current_stack_index };
auto stack_ptr = ir_builder->CreateInBoundsGEP( virtual_stack, llvm::ArrayRef< llvm::Value * >( indices, 2 ) );
// Read the value at the top of the stack
llvm::Value *output_value = nullptr;
if ( byte_size == 1 )
{
output_value = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, 8 ), stack_ptr );
}
else
{
// Cast the current stack pointer so the stack item width matches the width of the output value
auto casted_ptr = ir_builder->CreatePointerCast(
stack_ptr, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), 0 ) );
output_value = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), casted_ptr );
}
// Subtract the input value size from the current stack index
auto new_stack_index = ir_builder->CreateAdd(
current_stack_index, llvm::ConstantInt::get( llvm::IntegerType::get( *llvm_ctx, 64 ), byte_size ) );
// Store the newly calculated stack index into VSP
ir_builder->CreateStore( new_stack_index, this->stack_ptr );
return output_value;
}
llvm::Value *vmp_rtn_t::peek( std::uint8_t byte_size, std::uint8_t byte_offset )
{
// Load the current stack index into a temporary variable
auto current_stack_index = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, 64 ), stack_ptr, false );
if ( byte_offset )
{
auto t1 = ir_builder->CreateAdd(
current_stack_index, llvm::ConstantInt::get( llvm::IntegerType::get( *llvm_ctx, 64 ), byte_offset ) );
current_stack_index = ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, 64 ), t1, false );
}
// Get a pointer to the top byte of the stack + byte_offset (if any)
llvm::Value *i64_zero = llvm::ConstantInt::get( *llvm_ctx, llvm::APInt( 64, 0 ) );
llvm::Value *indices[ 2 ] = { i64_zero, current_stack_index };
auto stack_ptr = ir_builder->CreateInBoundsGEP( virtual_stack, llvm::ArrayRef< llvm::Value * >( indices, 2 ) );
if ( byte_size != 1 )
{
auto casted_ptr = ir_builder->CreatePointerCast(
stack_ptr, llvm::PointerType::get( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), 0 ) );
return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), casted_ptr );
}
else
{
return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), stack_ptr );
}
}
llvm::Value *vmp_rtn_t::load_value( std::uint8_t byte_size, llvm::GlobalValue *global )
{
return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), global );
}
llvm::Value *vmp_rtn_t::load_value( std::uint8_t byte_size, llvm::AllocaInst *var )
{
return ir_builder->CreateLoad( llvm::IntegerType::get( *llvm_ctx, byte_size * 8 ), var );
}
void vmp_rtn_t::create_virtual_stack( void )
{
// allocate stack space...
virtual_stack = ir_builder->CreateAlloca( llvm::ArrayType::get( llvm::IntegerType::get( *llvm_ctx, 8 ), 1024 ),
nullptr, "stack" );
// allocate stack pointer...
stack_ptr = ir_builder->CreateAlloca( llvm::IntegerType::get( *llvm_ctx, 64 ), nullptr, "sp" );
ir_builder->CreateStore( llvm::ConstantInt::get( llvm::IntegerType::getInt64Ty( *llvm_ctx ), 1024 ),
stack_ptr );
}
llvm::Function *vmp_rtn_t::lift( void )
{
auto &code_blocks = llvm_fptr->getBasicBlockList();
for ( auto idx = 0u; idx < code_blocks.size(); ++idx )
{
for ( auto &vinstr : vmp2_code_blocks[ idx ].vinstrs )
{
if ( !vmp2::lifters::lift( this, vmp2_code_blocks[ idx ], vinstr, ir_builder.get() ) )
{
std::printf( "> failed to devirtualize virtual instruction with opcode = %d\n", vinstr.opcode );
llvm_module->print( llvm::outs(), nullptr );
return nullptr;
}
}
}
return llvm_fptr;
}
} // namespace vm

@ -0,0 +1,98 @@
#include <vmp_rtn_t.hpp>
namespace vm
{
vmp_rtn_t::vmp_rtn_t( std::uintptr_t rtn_begin, std::vector< vm::instrs::code_block_t > vmp2_code_blocks,
std::shared_ptr< llvm::IRBuilder<> > &ir_builder, llvm::Module *llvm_module,
vmp2::v4::file_header *vmp2_file )
: ir_builder( ir_builder ), vmp2_code_blocks( vmp2_code_blocks ), rtn_begin( rtn_begin ),
llvm_module( llvm_module ), vmp2_file( vmp2_file )
{
create_routine();
// create virtual registers in the first code block...
ir_builder->SetInsertPoint( llvm_code_blocks[ 0 ].second );
flags = ir_builder->CreateAlloca( ir_builder->getInt64Ty(), nullptr, "flags" );
stack = ir_builder->CreateAlloca( llvm::PointerType::get( ir_builder->getInt8Ty(), 0ull ), nullptr, "sp" );
ir_builder->CreateStore( llvm_fptr->getArg( 0 ), stack );
create_virtual_registers();
}
void vmp_rtn_t::create_virtual_registers( void )
{
for ( auto idx = 0u; idx < 24; ++idx )
// allocate virtual register space...
virtual_registers.push_back(
ir_builder->CreateAlloca( llvm::IntegerType::get( ir_builder->getContext(), 64 ), nullptr,
( std::string( "vreg" ) + std::to_string( idx ) ).c_str() ) );
}
void vmp_rtn_t::create_routine( void )
{
auto func_ty =
llvm::FunctionType::get( llvm::PointerType::getInt8PtrTy( ir_builder->getContext() ),
{ llvm::PointerType::getInt8PtrTy( ir_builder->getContext() ) }, false );
std::stringstream rtn_name;
rtn_name << "rtn_" << std::hex << rtn_begin;
llvm_fptr = llvm::Function::Create( func_ty, llvm::GlobalValue::LinkageTypes::ExternalLinkage,
rtn_name.str().c_str(), *llvm_module );
for ( const auto &vmp2_code_block : vmp2_code_blocks )
{
// create basic block name... block_xxxxxxxx format...
std::stringstream blk_name;
blk_name << "blk_" << std::hex << vmp2_code_block.vip_begin;
llvm_code_blocks.push_back(
{ vmp2_code_block.vip_begin,
llvm::BasicBlock::Create( ir_builder->getContext(), blk_name.str().c_str(), llvm_fptr ) } );
}
zydis_routine_t vm_enter;
auto vm_enter_addr = reinterpret_cast< std::uintptr_t >( vmp2_file ) + vmp2_file->module_offset +
( rtn_begin - vmp2_file->image_base );
vm::util::flatten( vm_enter, vm_enter_addr );
vm::util::deobfuscate( vm_enter );
std::string asm_str;
ZydisFormatter formatter;
ZydisFormatterInit( &formatter, ZYDIS_FORMATTER_STYLE_INTEL );
for ( const auto &instr_data : vm_enter )
{
if ( instr_data.instr.mnemonic == ZYDIS_MNEMONIC_PUSH )
{
if ( instr_data.instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_MEMORY )
{
asm_str.append( "mov rax, 0x1000000000000000; push rax; mov rax, 0x1000000000000000; push rax; " );
break;
}
char buffer[ 256 ];
ZydisFormatterFormatInstruction( &formatter, &instr_data.instr, buffer, sizeof( buffer ), 0ull );
asm_str.append( buffer ).append( "; " );
}
else if ( instr_data.instr.mnemonic == ZYDIS_MNEMONIC_PUSHFQ )
{
asm_str.append( "pushfq; " );
}
}
rtn_name.str( "" );
asm_str.append( "mov rcx, rsp; sub rsp, 0xA00; int 3; int 3; int 3; int 3; int 3;" );
rtn_name << "vmenter_" << std::hex << rtn_begin;
auto entry_func = llvm::Function::Create( llvm::FunctionType::get( ir_builder->getVoidTy(), false ),
llvm::GlobalValue::LinkageTypes::ExternalLinkage,
rtn_name.str().c_str(), *llvm_module );
auto entry_block = llvm::BasicBlock::Create( ir_builder->getContext(), "", entry_func );
ir_builder->SetInsertPoint( entry_block );
auto entry_stub = llvm::InlineAsm::get( llvm::FunctionType::get( ir_builder->getVoidTy(), false ), asm_str, "",
false, false, llvm::InlineAsm::AD_Intel );
ir_builder->CreateCall( entry_stub );
ir_builder->CreateRetVoid();
}
} // namespace vm
Loading…
Cancel
Save