parent
7240a2a23c
commit
99f1f695ed
@ -1,6 +1,9 @@
|
|||||||
[submodule "dependencies/zydis"]
|
[submodule "dependencies/zydis"]
|
||||||
path = dependencies/zydis
|
path = dependencies/zydis
|
||||||
url = https://github.com/zyantific/zydis.git
|
url = https://github.com/zyantific/zydis.git
|
||||||
[submodule "dependencies/vtil"]
|
[submodule "dependencies/linux-pe"]
|
||||||
path = dependencies/vtil
|
path = dependencies/linux-pe
|
||||||
url = https://github.com/vtil-project/VTIL-Core
|
url = https://github.com/can1357/linux-pe.git
|
||||||
|
[submodule "dependencies/xtils"]
|
||||||
|
path = dependencies/xtils
|
||||||
|
url = https://githacks.org/_xeroxz/xtils.git
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
Subproject commit db2b7af6e6beae1bc391ff8f8e5c97b963dc3258
|
@ -1 +0,0 @@
|
|||||||
Subproject commit ff94eef4483b07fbe970b7b80bbfae3b52524f16
|
|
@ -0,0 +1 @@
|
|||||||
|
Subproject commit fdcafdbbcb3f34c33b9cffb2be569b9aa5f42a57
|
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Zydis/Zydis.h>
|
||||||
|
#include <nt/image.hpp>
|
||||||
|
#include <vmprofiler.hpp>
|
||||||
|
#include <xtils.hpp>
|
||||||
|
|
||||||
|
#define ABS_TO_IMG( addr, mod_base, img_base ) ( addr - mod_base ) + img_base
|
||||||
|
#define LEA_R12_SIG "\x4C\x8D\x25\x00\x00\x00\x00"
|
||||||
|
#define LEA_R12_MASK "xxx????"
|
||||||
|
|
||||||
|
#define PUSH_4B_IMM "\x68\x00\x00\x00\x00"
|
||||||
|
#define PUSH_4B_MASK "x????"
|
||||||
|
|
||||||
|
namespace vm::locate
|
||||||
|
{
|
||||||
|
struct vm_handler_table_info_t
|
||||||
|
{
|
||||||
|
std::uint32_t rva, lea_r12_rva;
|
||||||
|
zydis_decoded_instr_t lea_r12_instr;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector< vm_handler_table_info_t > all_handler_tables( std::uintptr_t module_base );
|
||||||
|
std::vector< std::pair< std::uint32_t, std::uint32_t > > all_vm_enters(
|
||||||
|
std::uintptr_t module_base, std::vector< vm_handler_table_info_t > &vm_handler_tables );
|
||||||
|
} // namespace vm::locate
|
@ -1,37 +0,0 @@
|
|||||||
#include <vmlifters.hpp>
|
|
||||||
|
|
||||||
namespace vm::lifters
|
|
||||||
{
|
|
||||||
vm::lifters::lifter_t addq = {
|
|
||||||
// vsp[0] = vsp[1] + vsp[0];
|
|
||||||
vm::handler::ADDQ, []( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t* code_blk ) {
|
|
||||||
auto [ t0, t1 ] = blk->tmp( 64, 64 );
|
|
||||||
blk->pop( t0 );
|
|
||||||
blk->pop( t1 );
|
|
||||||
blk->add( t1, t0 );
|
|
||||||
blk->push( t1 );
|
|
||||||
blk->pushf();
|
|
||||||
} };
|
|
||||||
|
|
||||||
vm::lifters::lifter_t adddw = {
|
|
||||||
// vsp[0] = vsp[1] + vsp[0];
|
|
||||||
vm::handler::ADDDW, []( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t* code_blk ) {
|
|
||||||
auto [ t0, t1 ] = blk->tmp( 32, 32 );
|
|
||||||
blk->pop( t0 );
|
|
||||||
blk->pop( t1 );
|
|
||||||
blk->add( t1, t0 );
|
|
||||||
blk->push( t1 );
|
|
||||||
blk->pushf();
|
|
||||||
} };
|
|
||||||
|
|
||||||
vm::lifters::lifter_t addw = {
|
|
||||||
// vsp[0] = vsp[1] + vsp[0];
|
|
||||||
vm::handler::ADDW, []( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t* code_blk ) {
|
|
||||||
auto [ t0, t1 ] = blk->tmp( 16, 16 );
|
|
||||||
blk->pop( t0 );
|
|
||||||
blk->pop( t1 );
|
|
||||||
blk->add( t1, t0 );
|
|
||||||
blk->push( t1 );
|
|
||||||
blk->pushf();
|
|
||||||
} };
|
|
||||||
} // namespace vm::lifters
|
|
@ -1,18 +0,0 @@
|
|||||||
#include <vmlifters.hpp>
|
|
||||||
|
|
||||||
namespace vm::lifters
|
|
||||||
{
|
|
||||||
vm::lifters::lifter_t jmp = {
|
|
||||||
// jmp
|
|
||||||
vm::handler::JMP,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
if ( code_blk->jcc.has_jcc )
|
|
||||||
{
|
|
||||||
// TODO: figure out what bit the JCC is determined on...
|
|
||||||
blk->js( vtil::REG_FLAGS.select( 1, 11 ), code_blk->jcc.block_addr[ 0 ] - vinstr->trace_data.regs.r13,
|
|
||||||
code_blk->jcc.block_addr[ 1 ] - vinstr->trace_data.regs.r13 );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
blk->jmp( vinstr->trace_data.vsp.qword[ 0 ] - vinstr->trace_data.regs.r13 );
|
|
||||||
} };
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
#include <vmlifters.hpp>
|
|
||||||
|
|
||||||
namespace vm::lifters
|
|
||||||
{
|
|
||||||
vm::lifters::lifter_t lconstq = {
|
|
||||||
// push imm<N>
|
|
||||||
vm::handler::LCONSTQ,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
blk->push( vtil::operand( vinstr->operand.imm.u, 64 ) );
|
|
||||||
} };
|
|
||||||
|
|
||||||
vm::lifters::lifter_t lconstdw = {
|
|
||||||
// push imm<N>
|
|
||||||
vm::handler::LCONSTDW,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
blk->push( vtil::operand( vinstr->operand.imm.u, 32 ) );
|
|
||||||
} };
|
|
||||||
|
|
||||||
vm::lifters::lifter_t lconstw = {
|
|
||||||
// push imm<N>
|
|
||||||
vm::handler::LCONSTW,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
blk->push( vtil::operand( vinstr->operand.imm.u, 16 ) );
|
|
||||||
} };
|
|
||||||
|
|
||||||
vm::lifters::lifter_t lconstbzxw = {
|
|
||||||
// push imm<N>
|
|
||||||
vm::handler::LCONSTBZXW,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
blk->push( vtil::operand( vinstr->operand.imm.u, 16 ) );
|
|
||||||
} };
|
|
||||||
|
|
||||||
vm::lifters::lifter_t lconstbsxdw = {
|
|
||||||
// push imm<N>
|
|
||||||
vm::handler::LCONSTBSXDW,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
blk->push( vtil::operand( vinstr->operand.imm.u, 32 ) );
|
|
||||||
} };
|
|
||||||
|
|
||||||
vm::lifters::lifter_t lconstbsxq = {
|
|
||||||
// push imm<N>
|
|
||||||
vm::handler::LCONSTBSXQ,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
blk->push( vtil::operand( vinstr->operand.imm.u, 64 ) );
|
|
||||||
} };
|
|
||||||
|
|
||||||
vm::lifters::lifter_t lconstdwsxq = {
|
|
||||||
// push imm<N>
|
|
||||||
vm::handler::LCONSTDWSXQ,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
blk->push( vtil::operand( vinstr->operand.imm.u, 64 ) );
|
|
||||||
} };
|
|
||||||
|
|
||||||
vm::lifters::lifter_t lconstwsxq = {
|
|
||||||
// push imm<N>
|
|
||||||
vm::handler::LCONSTWSXQ,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
blk->push( vtil::operand( vinstr->operand.imm.u, 64 ) );
|
|
||||||
} };
|
|
||||||
|
|
||||||
vm::lifters::lifter_t lconstwsxdw = {
|
|
||||||
// push imm<N>
|
|
||||||
vm::handler::LCONSTWSXDW,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
blk->push( vtil::operand( vinstr->operand.imm.u, 32 ) );
|
|
||||||
} };
|
|
||||||
} // namespace vm::lifters
|
|
@ -1,9 +0,0 @@
|
|||||||
#include <vmlifters.hpp>
|
|
||||||
|
|
||||||
namespace vm::lifters
|
|
||||||
{
|
|
||||||
vm::lifters::lifter_t lrflags = {
|
|
||||||
// push flags
|
|
||||||
vm::handler::LRFLAGS, []( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr,
|
|
||||||
vmp2::v3::code_block_t *code_blk ) { blk->push( vtil::REG_FLAGS ); } };
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
#include <vmlifters.hpp>
|
|
||||||
|
|
||||||
namespace vm::lifters
|
|
||||||
{
|
|
||||||
vm::lifters::lifter_t lregq = {
|
|
||||||
// push vregX
|
|
||||||
vm::handler::LREGQ,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
blk->push( make_virtual_register( vinstr->operand.imm.u, 8 ) );
|
|
||||||
} };
|
|
||||||
|
|
||||||
vm::lifters::lifter_t lregdw = {
|
|
||||||
// push vregX
|
|
||||||
vm::handler::LREGDW,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
blk->push( make_virtual_register( vinstr->operand.imm.u, 4 ) );
|
|
||||||
} };
|
|
||||||
} // namespace vm::lifters
|
|
@ -1,9 +0,0 @@
|
|||||||
#include <vmlifters.hpp>
|
|
||||||
|
|
||||||
namespace vm::lifters
|
|
||||||
{
|
|
||||||
vm::lifters::lifter_t lvsp = {
|
|
||||||
// vsp = vsp[0]
|
|
||||||
vm::handler::LVSP, []( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr,
|
|
||||||
vmp2::v3::code_block_t *code_blk ) { blk->pop( vtil::REG_SP ); } };
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
#include <vmlifters.hpp>
|
|
||||||
|
|
||||||
namespace vm::lifters
|
|
||||||
{
|
|
||||||
vm::lifters::lifter_t nandq = {
|
|
||||||
// pop vregX
|
|
||||||
// pop vregY
|
|
||||||
// not vregX
|
|
||||||
// not vregY
|
|
||||||
// and vregX, vregY
|
|
||||||
// push vregX
|
|
||||||
// pushf
|
|
||||||
vm::handler::NANDQ,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
auto [ t1, t2 ] = blk->tmp( 64, 64 );
|
|
||||||
blk->pop( t1 );
|
|
||||||
blk->pop( t2 );
|
|
||||||
|
|
||||||
blk->bnot( t1 );
|
|
||||||
blk->bnot( t2 );
|
|
||||||
|
|
||||||
blk->band( t1, t2 );
|
|
||||||
blk->push( t1 );
|
|
||||||
blk->pushf();
|
|
||||||
} };
|
|
||||||
|
|
||||||
vm::lifters::lifter_t nanddw = {
|
|
||||||
// pop vregX
|
|
||||||
// pop vregY
|
|
||||||
// not vregX
|
|
||||||
// not vregY
|
|
||||||
// and vregX, vregY
|
|
||||||
// push vregX
|
|
||||||
// pushf
|
|
||||||
vm::handler::NANDDW,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
auto [ t1, t2 ] = blk->tmp( 32, 32 );
|
|
||||||
blk->pop( t1 );
|
|
||||||
blk->pop( t2 );
|
|
||||||
|
|
||||||
blk->bnot( t1 );
|
|
||||||
blk->bnot( t2 );
|
|
||||||
|
|
||||||
blk->band( t1, t2 );
|
|
||||||
blk->push( t1 );
|
|
||||||
blk->pushf();
|
|
||||||
} };
|
|
||||||
|
|
||||||
vm::lifters::lifter_t nandw = {
|
|
||||||
// pop vregX
|
|
||||||
// pop vregY
|
|
||||||
// not vregX
|
|
||||||
// not vregY
|
|
||||||
// and vregX, vregY
|
|
||||||
// push vregX
|
|
||||||
// pushf
|
|
||||||
vm::handler::NANDW,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
auto [ t1, t2 ] = blk->tmp( 16, 16 );
|
|
||||||
blk->pop( t1 );
|
|
||||||
blk->pop( t2 );
|
|
||||||
|
|
||||||
blk->bnot( t1 );
|
|
||||||
blk->bnot( t2 );
|
|
||||||
|
|
||||||
blk->band( t1, t2 );
|
|
||||||
blk->push( t1 );
|
|
||||||
blk->pushf();
|
|
||||||
} };
|
|
||||||
} // namespace vm::lifters
|
|
@ -1,9 +0,0 @@
|
|||||||
#include <vmlifters.hpp>
|
|
||||||
|
|
||||||
namespace vm::lifters
|
|
||||||
{
|
|
||||||
vm::lifters::lifter_t pushvsp = {
|
|
||||||
// push vsp
|
|
||||||
vm::handler::PUSHVSP, []( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr,
|
|
||||||
vmp2::v3::code_block_t *code_blk ) { blk->push( vtil::REG_SP ); } };
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
#include <vmlifters.hpp>
|
|
||||||
|
|
||||||
namespace vm::lifters
|
|
||||||
{
|
|
||||||
vm::lifters::lifter_t readq = {
|
|
||||||
// pop vregX
|
|
||||||
// ldd vregX, vregX, 0
|
|
||||||
// push vregX
|
|
||||||
vm::handler::READQ,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
auto [ t0, t1 ] = blk->tmp( 64, 64 );
|
|
||||||
blk->pop( t0 );
|
|
||||||
blk->ldd( t1, t0, vtil::make_imm( 0ull ) );
|
|
||||||
blk->push( t1 );
|
|
||||||
} };
|
|
||||||
|
|
||||||
vm::lifters::lifter_t readdw = {
|
|
||||||
// pop vregX
|
|
||||||
// ldd vregX, vregX, 0
|
|
||||||
// push vregX
|
|
||||||
vm::handler::READDW,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
auto [ t0, t1 ] = blk->tmp( 64, 32 );
|
|
||||||
blk->pop( t0 );
|
|
||||||
blk->ldd( t1, t0, vtil::make_imm( 0ull ) );
|
|
||||||
blk->push( t1 );
|
|
||||||
} };
|
|
||||||
|
|
||||||
vm::lifters::lifter_t readw = {
|
|
||||||
// pop vregX
|
|
||||||
// ldd vregX, vregX, 0
|
|
||||||
// push vregX
|
|
||||||
vm::handler::READW,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
auto [ t0, t1 ] = blk->tmp( 64, 16 );
|
|
||||||
blk->pop( t0 );
|
|
||||||
blk->ldd( t1, t0, vtil::make_imm( 0ull ) );
|
|
||||||
blk->push( t1 );
|
|
||||||
} };
|
|
||||||
} // namespace vm::lifters
|
|
@ -1,36 +0,0 @@
|
|||||||
#include <vmlifters.hpp>
|
|
||||||
|
|
||||||
namespace vm::lifters
|
|
||||||
{
|
|
||||||
vm::lifters::lifter_t shrq = {
|
|
||||||
// pop vregX
|
|
||||||
// pop vregY
|
|
||||||
// shr vregX, vregY
|
|
||||||
// push vregX
|
|
||||||
// pushf
|
|
||||||
vm::handler::SHRQ,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
auto [ t0, t1 ] = blk->tmp( 64, 8 );
|
|
||||||
blk->pop( t0 );
|
|
||||||
blk->pop( t1 );
|
|
||||||
blk->bshr( t0, t1 );
|
|
||||||
blk->push( t0 );
|
|
||||||
blk->pushf();
|
|
||||||
} };
|
|
||||||
|
|
||||||
vm::lifters::lifter_t shrw = {
|
|
||||||
// pop vregX
|
|
||||||
// pop vregY
|
|
||||||
// shr vregX, vregY
|
|
||||||
// push vregX
|
|
||||||
// pushf
|
|
||||||
vm::handler::SHRW,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
auto [ t0, t1 ] = blk->tmp( 16, 8 );
|
|
||||||
blk->pop( t0 );
|
|
||||||
blk->pop( t1 );
|
|
||||||
blk->bshr( t0, t1 );
|
|
||||||
blk->push( t0 );
|
|
||||||
blk->pushf();
|
|
||||||
} };
|
|
||||||
} // namespace vm::lifters
|
|
@ -1,25 +0,0 @@
|
|||||||
#include <vmlifters.hpp>
|
|
||||||
|
|
||||||
namespace vm::lifters
|
|
||||||
{
|
|
||||||
vm::lifters::lifter_t sregq = {
|
|
||||||
// pop vregX
|
|
||||||
vm::handler::SREGQ,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
blk->pop( make_virtual_register( vinstr->operand.imm.u, 8 ) );
|
|
||||||
} };
|
|
||||||
|
|
||||||
vm::lifters::lifter_t sregdw = {
|
|
||||||
// pop vregX
|
|
||||||
vm::handler::SREGDW,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
blk->pop( make_virtual_register( vinstr->operand.imm.u, 4 ) );
|
|
||||||
} };
|
|
||||||
|
|
||||||
vm::lifters::lifter_t sregw = {
|
|
||||||
// pop vregX
|
|
||||||
vm::handler::SREGW,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
blk->pop( make_virtual_register( vinstr->operand.imm.u, 2 ) );
|
|
||||||
} };
|
|
||||||
} // namespace vm::lifters
|
|
@ -1,11 +0,0 @@
|
|||||||
#include <vmlifters.hpp>
|
|
||||||
|
|
||||||
namespace vm::lifters
|
|
||||||
{
|
|
||||||
vm::lifters::lifter_t vmexit = {
|
|
||||||
// ret
|
|
||||||
vm::handler::VMEXIT,
|
|
||||||
[]( vtil::basic_block *blk, vm::instrs::virt_instr_t *vinstr, vmp2::v3::code_block_t *code_blk ) {
|
|
||||||
blk->vexit( vtil::make_imm( vinstr->trace_data.vsp.qword[ 0x13 ] - vinstr->trace_data.regs.r13 ) );
|
|
||||||
} };
|
|
||||||
}
|
|
@ -0,0 +1,264 @@
|
|||||||
|
#include <vmlocate.hpp>
|
||||||
|
|
||||||
|
namespace vm::locate
|
||||||
|
{
|
||||||
|
std::vector< vm_handler_table_info_t > all_handler_tables( std::uintptr_t module_base )
|
||||||
|
{
|
||||||
|
std::vector< vm_handler_table_info_t > result;
|
||||||
|
auto module_info = reinterpret_cast< win::image_t<> * >( module_base );
|
||||||
|
auto sections = module_info->get_nt_headers()->get_sections();
|
||||||
|
auto num_sections = module_info->get_file_header()->num_sections;
|
||||||
|
auto umtils = xtils::um_t::get_instance();
|
||||||
|
|
||||||
|
static const auto lea_r12_validate = []( std::uintptr_t addr ) -> bool {
|
||||||
|
ZydisDecodedInstruction instr;
|
||||||
|
ZydisDecoder decoder;
|
||||||
|
ZydisRegister jmp_reg = ZYDIS_REGISTER_NONE;
|
||||||
|
ZydisDecoderInit( &decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64 );
|
||||||
|
|
||||||
|
unsigned instr_count = 0u;
|
||||||
|
bool found_table_idx = false, found_valid_jmp = false;
|
||||||
|
|
||||||
|
while ( ZYAN_SUCCESS(
|
||||||
|
ZydisDecoderDecodeBuffer( &decoder, reinterpret_cast< void * >( addr ), 0x1000, &instr ) ) )
|
||||||
|
{
|
||||||
|
++instr_count;
|
||||||
|
if ( instr_count >= 0x1000 ||
|
||||||
|
instr.mnemonic == ZYDIS_MNEMONIC_INVALID ) // prevent run offs and misalignment...
|
||||||
|
break;
|
||||||
|
|
||||||
|
// determine if we are looking at a JMP RCX/JMP RDX...
|
||||||
|
if ( vm::util::is_jmp( instr ) )
|
||||||
|
{
|
||||||
|
if ( instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER &&
|
||||||
|
( instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RDX ||
|
||||||
|
instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RCX ) )
|
||||||
|
{
|
||||||
|
if ( jmp_reg == ZYDIS_REGISTER_NONE || jmp_reg != instr.operands[ 0 ].reg.value )
|
||||||
|
break;
|
||||||
|
|
||||||
|
// else we set it to true and break...
|
||||||
|
found_valid_jmp = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// take JCC branch no matter what...
|
||||||
|
ZydisCalcAbsoluteAddress( &instr, &instr.operands[ 0 ], addr, &addr );
|
||||||
|
|
||||||
|
// dont execute anymore address advancement code...
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// else if the instruction is a MOV RDX/RCX, [R12+RAX*0x8]... we know this is an index into
|
||||||
|
// the vm handler table and thus this lea r12, xxxx is probably legit...
|
||||||
|
else if ( instr.mnemonic == ZYDIS_MNEMONIC_MOV &&
|
||||||
|
instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER &&
|
||||||
|
( instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RDX ||
|
||||||
|
instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RCX ) &&
|
||||||
|
instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_MEMORY &&
|
||||||
|
instr.operands[ 1 ].mem.base == ZYDIS_REGISTER_R12 &&
|
||||||
|
instr.operands[ 1 ].mem.index == ZYDIS_REGISTER_RAX && instr.operands[ 1 ].mem.scale == 0x8 )
|
||||||
|
{
|
||||||
|
found_table_idx = true;
|
||||||
|
jmp_reg = instr.operands[ 0 ].reg.value;
|
||||||
|
}
|
||||||
|
else if ( instr.mnemonic == ZYDIS_MNEMONIC_RET || instr.mnemonic == ZYDIS_MNEMONIC_CALL )
|
||||||
|
break;
|
||||||
|
|
||||||
|
// advance the instruction address "addr"...
|
||||||
|
addr += instr.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return found_table_idx && found_valid_jmp;
|
||||||
|
};
|
||||||
|
|
||||||
|
ZydisDecodedInstruction instr;
|
||||||
|
ZydisDecoder decoder;
|
||||||
|
ZydisDecoderInit( &decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64 );
|
||||||
|
|
||||||
|
for ( auto idx = 0u; idx < num_sections; ++idx )
|
||||||
|
{
|
||||||
|
// if the section is executable and not discardable... scan it for lea r12, xxxx and ensure its loading
|
||||||
|
// a virtual machine handler table into r12...
|
||||||
|
if ( sections[ idx ].characteristics.mem_execute && !sections[ idx ].characteristics.mem_discardable )
|
||||||
|
{
|
||||||
|
void *scan_result = reinterpret_cast< void * >( sections[ idx ].virtual_address + module_base );
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// compute how far away from the beginning of the section we are...
|
||||||
|
auto section_size = reinterpret_cast< std::uintptr_t >( scan_result ) -
|
||||||
|
( sections[ idx ].virtual_address + module_base );
|
||||||
|
|
||||||
|
// scan from the last scans result to the end of the section for lea r12's...
|
||||||
|
scan_result = umtils->sigscan( scan_result, sections[ idx ].virtual_size - section_size,
|
||||||
|
LEA_R12_SIG, LEA_R12_MASK );
|
||||||
|
|
||||||
|
if ( scan_result )
|
||||||
|
{
|
||||||
|
// check to see if we are looking at:
|
||||||
|
// 1.) a legit "lea r12, xxxx" and not a misaligned instruction...
|
||||||
|
// 2.) if the instruction stream is followed in zydis it ends with a jmp rcx/jmp rdx...
|
||||||
|
if ( lea_r12_validate( reinterpret_cast< std::uintptr_t >( scan_result ) ) )
|
||||||
|
{
|
||||||
|
if ( ZYAN_SUCCESS( ZydisDecoderDecodeBuffer(
|
||||||
|
&decoder, scan_result, sections[ idx ].virtual_size - section_size, &instr ) ) )
|
||||||
|
{
|
||||||
|
vm_handler_table_info_t vm_handler_table_info;
|
||||||
|
vm_handler_table_info.rva =
|
||||||
|
( instr.operands[ 1 ].mem.disp.value +
|
||||||
|
reinterpret_cast< std::uintptr_t >( scan_result ) + instr.length ) -
|
||||||
|
module_base;
|
||||||
|
|
||||||
|
vm_handler_table_info.lea_r12_rva =
|
||||||
|
reinterpret_cast< std::uintptr_t >( scan_result ) - module_base;
|
||||||
|
|
||||||
|
vm_handler_table_info.lea_r12_instr = instr;
|
||||||
|
result.push_back( vm_handler_table_info );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scan_result = reinterpret_cast< void * >( reinterpret_cast< std::uintptr_t >( scan_result ) +
|
||||||
|
sizeof LEA_R12_SIG );
|
||||||
|
}
|
||||||
|
|
||||||
|
} while ( scan_result );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector< std::pair< std::uint32_t, std::uint32_t > > all_vm_enters(
|
||||||
|
std::uintptr_t module_base, std::vector< vm_handler_table_info_t > &vm_handler_tables )
|
||||||
|
{
|
||||||
|
std::vector< std::pair< std::uint32_t, std::uint32_t > > result;
|
||||||
|
auto module_info = reinterpret_cast< win::image_t<> * >( module_base );
|
||||||
|
auto sections = module_info->get_nt_headers()->get_sections();
|
||||||
|
auto num_sections = module_info->get_file_header()->num_sections;
|
||||||
|
auto umtils = xtils::um_t::get_instance();
|
||||||
|
auto module_end = module_base + module_info->get_nt_headers()->optional_header.size_image;
|
||||||
|
|
||||||
|
static const auto validate_vm_enter = [ & ]( std::uintptr_t addr ) -> bool {
|
||||||
|
ZydisDecodedInstruction instr;
|
||||||
|
ZydisDecoder decoder;
|
||||||
|
ZydisDecoderInit( &decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64 );
|
||||||
|
|
||||||
|
unsigned instr_count = 0u;
|
||||||
|
bool found_valid_jmp = false;
|
||||||
|
std::vector< ZydisDecodedInstruction > instr_stream;
|
||||||
|
|
||||||
|
while ( addr >= module_base && addr < module_end &&
|
||||||
|
ZYAN_SUCCESS(
|
||||||
|
ZydisDecoderDecodeBuffer( &decoder, reinterpret_cast< void * >( addr ), 0x1000, &instr ) ) )
|
||||||
|
{
|
||||||
|
if ( !instr_count && instr.mnemonic != ZYDIS_MNEMONIC_PUSH )
|
||||||
|
break;
|
||||||
|
|
||||||
|
++instr_count; // handle run offs and misaligned instructions...
|
||||||
|
if ( instr_count > 500 || instr.mnemonic == ZYDIS_MNEMONIC_INVALID ||
|
||||||
|
instr.mnemonic == ZYDIS_MNEMONIC_RET || instr.mnemonic == ZYDIS_MNEMONIC_CALL )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// determine if we are looking at a JMP RCX/JMP RDX...
|
||||||
|
if ( vm::util::is_jmp( instr ) )
|
||||||
|
{
|
||||||
|
if ( instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER &&
|
||||||
|
( instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RDX ||
|
||||||
|
instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RCX ) )
|
||||||
|
{
|
||||||
|
|
||||||
|
// else we set it to true and break...
|
||||||
|
found_valid_jmp = true;
|
||||||
|
instr_stream.push_back( instr );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// take JCC branch no matter what...
|
||||||
|
ZydisCalcAbsoluteAddress( &instr, &instr.operands[ 0 ], addr, &addr );
|
||||||
|
|
||||||
|
// dont execute anymore address advancement code...
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
instr_stream.push_back( instr );
|
||||||
|
addr += instr.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !found_valid_jmp )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// second instruction in the flattened stream should be a push...
|
||||||
|
// this is also an optimization so we dont have to hit that 0^2 std::find_if every time...
|
||||||
|
if ( instr_stream[ 1 ].mnemonic != ZYDIS_MNEMONIC_PUSH )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ( std::find_if( instr_stream.begin() + 1, instr_stream.end(),
|
||||||
|
[ & ]( const ZydisDecodedInstruction &instr ) {
|
||||||
|
return instr.mnemonic == ZYDIS_MNEMONIC_PUSH &&
|
||||||
|
instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_IMMEDIATE;
|
||||||
|
} ) == instr_stream.end() )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// scan over the instruction stream to see if it contains an lea r12, xxxx which is a known vm handler table
|
||||||
|
// load into r12... this is O^2 and very slow... whatever...
|
||||||
|
return std::find_if(
|
||||||
|
instr_stream.begin(), instr_stream.end(), [ & ]( const ZydisDecodedInstruction &instr ) {
|
||||||
|
return instr.mnemonic == ZYDIS_MNEMONIC_LEA &&
|
||||||
|
instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_MEMORY &&
|
||||||
|
std::find_if( vm_handler_tables.begin(), vm_handler_tables.end(),
|
||||||
|
[ & ]( const vm_handler_table_info_t &table_info ) {
|
||||||
|
return table_info.lea_r12_instr.operands[ 1 ].mem.disp.value ==
|
||||||
|
instr.operands[ 1 ].mem.disp.value;
|
||||||
|
} ) != vm_handler_tables.end();
|
||||||
|
} ) != instr_stream.end();
|
||||||
|
};
|
||||||
|
|
||||||
|
ZydisDecodedInstruction instr;
|
||||||
|
ZydisDecoder decoder;
|
||||||
|
ZydisDecoderInit( &decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64 );
|
||||||
|
|
||||||
|
// for each section...
|
||||||
|
for ( auto idx = 0u; idx < num_sections; ++idx )
|
||||||
|
{
|
||||||
|
// we are only interested in executable (non-discardable) sections...
|
||||||
|
if ( sections[ idx ].characteristics.mem_execute && !sections[ idx ].characteristics.mem_discardable )
|
||||||
|
{
|
||||||
|
void *scan_result = reinterpret_cast< void * >( sections[ idx ].virtual_address + module_base );
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// compute how far away from the beginning of the section we are...
|
||||||
|
auto section_size = reinterpret_cast< std::uintptr_t >( scan_result ) -
|
||||||
|
( sections[ idx ].virtual_address + module_base );
|
||||||
|
|
||||||
|
if ( section_size > sections[ idx ].virtual_size )
|
||||||
|
break;
|
||||||
|
|
||||||
|
scan_result = umtils->sigscan( scan_result, sections[ idx ].virtual_size - section_size,
|
||||||
|
PUSH_4B_IMM, PUSH_4B_MASK );
|
||||||
|
|
||||||
|
if ( scan_result )
|
||||||
|
{
|
||||||
|
if ( validate_vm_enter( reinterpret_cast< std::uintptr_t >( scan_result ) ) &&
|
||||||
|
ZYAN_SUCCESS( ZydisDecoderDecodeBuffer( &decoder, scan_result, 0x1000, &instr ) ) )
|
||||||
|
{
|
||||||
|
if ( std::find_if( result.begin(), result.end(),
|
||||||
|
[ & ]( const std::pair< std::uint32_t, std::uint32_t > &info ) {
|
||||||
|
return info.second == instr.operands[ 0 ].imm.value.u;
|
||||||
|
} ) == result.end() )
|
||||||
|
{
|
||||||
|
result.push_back( { reinterpret_cast< std::uintptr_t >( scan_result ) - module_base,
|
||||||
|
instr.operands[ 0 ].imm.value.u } );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scan_result = reinterpret_cast< void * >( reinterpret_cast< std::uintptr_t >( scan_result ) +
|
||||||
|
sizeof PUSH_4B_IMM );
|
||||||
|
}
|
||||||
|
|
||||||
|
} while ( scan_result );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} // namespace vm::locate
|
Loading…
Reference in new issue