Merge branch 'vmp2-v4' into 'master'

beginning to change things to upper case + added some more code to

See merge request vmp2/vmprofiler-qt!5
master
_xeroxz 3 years ago
commit 3a4a3781e0

@ -59,24 +59,27 @@ set(vmprofiler-qt_SOURCES "")
list(APPEND vmprofiler-qt_SOURCES list(APPEND vmprofiler-qt_SOURCES
"src/qvminspector.ui" "src/qvminspector.ui"
"src/qvminspector.qrc" "src/qvminspector.qrc"
"src/qvminspector.cpp"
"src/qvirt_instrs.cpp"
"src/qvirt_handlers.cpp"
"src/main.cpp"
"src/qvirt_handlers.h"
"src/qvirt_instrs.h"
"src/qvminspector.h"
"src/icon.rc"
"src/darkstyle/DarkStyle.cpp" "src/darkstyle/DarkStyle.cpp"
"src/darkstyle/framelesswindow/framelesswindow.cpp" "src/darkstyle/framelesswindow/framelesswindow.cpp"
"src/darkstyle/framelesswindow/windowdragger.cpp" "src/darkstyle/framelesswindow/windowdragger.cpp"
"src/darkstyle/mainwindow.cpp" "src/darkstyle/mainwindow.cpp"
"src/main.cpp"
"src/qvm_handlers.cpp"
"src/qvm_inspector.cpp"
"src/qvm_virtual_instructions.cpp"
"src/qvm_virtual_routines.cpp"
"src/darkstyle/DarkStyle.h" "src/darkstyle/DarkStyle.h"
"src/darkstyle/framelesswindow/framelesswindow.h" "src/darkstyle/framelesswindow/framelesswindow.h"
"src/darkstyle/framelesswindow/windowdragger.h" "src/darkstyle/framelesswindow/windowdragger.h"
"src/darkstyle/mainwindow.h" "src/darkstyle/mainwindow.h"
"src/qvm_handlers.h"
"src/qvm_inspector.h"
"src/qvm_virtual_instructions.h"
"src/qvm_virtual_routines.h"
"src/icon.rc"
"src/darkstyle/mainwindow.ui" "src/darkstyle/mainwindow.ui"
"src/darkstyle/framelesswindow.qrc" "src/darkstyle/framelesswindow.qrc"
"src/darkstyle/framelesswindow/framelesswindow.ui"
"dependencies/ia32-doc/out/ia32.hpp" "dependencies/ia32-doc/out/ia32.hpp"
) )

@ -19,16 +19,12 @@ compile-features = ["cxx_std_20"]
sources = [ sources = [
"src/qvminspector.ui", "src/qvminspector.ui",
"src/qvminspector.qrc", "src/qvminspector.qrc",
"src/qvminspector.cpp", "src/**.cpp",
"src/qvirt_instrs.cpp", "src/**.h",
"src/qvirt_handlers.cpp",
"src/main.cpp",
"src/*.h",
"src/icon.rc", "src/icon.rc",
"src/darkstyle/**.cpp",
"src/darkstyle/**.h",
"src/darkstyle/mainwindow.ui", "src/darkstyle/mainwindow.ui",
"src/darkstyle/framelesswindow.qrc", "src/darkstyle/framelesswindow.qrc",
"src/darkstyle/framelesswindow/framelesswindow.ui",
"dependencies/ia32-doc/out/ia32.hpp" "dependencies/ia32-doc/out/ia32.hpp"
] ]

@ -1 +1 @@
Subproject commit c2319117a07d95794f54db970aa7f72e8726349a Subproject commit 051140175db16b38acee882cfca714b4a1000a41

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1737</width> <width>1404</width>
<height>1157</height> <height>897</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -143,7 +143,7 @@
color:rgb(153,153,153);</string> color:rgb(153,153,153);</string>
</property> </property>
<property name="text"> <property name="text">
<string>VMProtect 2 - Virtual Instruction Inspector (v1.8 BETA)</string> <string>VMProtect 2 - Virtual Instruction Inspector - v2.0</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>

@ -5,21 +5,24 @@
#include "darkstyle/DarkStyle.h" #include "darkstyle/DarkStyle.h"
#include "darkstyle/framelesswindow/framelesswindow.h" #include "darkstyle/framelesswindow/framelesswindow.h"
#include "qvirt_handlers.h" #include "qvm_handlers.h"
#include "qvirt_instrs.h" #include "qvm_inspector.h"
#include "qvminspector.h" #include "qvm_virtual_instructions.h"
#include "qvm_virtual_routines.h"
int WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd ) int WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
{ {
QApplication app( __argc, __argv ); QApplication app( __argc, __argv );
QApplication::setStyle( new DarkStyle ); QApplication::setStyle( new DarkStyle );
FramelessWindow frameless_window; FramelessWindow FW;
const auto window = new qvminspector_t;
qvirt_instrs_t virt_instr( window );
qvirt_handlers_t virt_handlers( window );
frameless_window.setContent( window ); auto g_main_window = new qvm_inspector;
frameless_window.setWindowIcon( QIcon( "icon.ico" ) ); qvm_virtual_instructions virt_instrs_panel( g_main_window );
frameless_window.show(); qvm_handlers virt_handler_panel( g_main_window );
qvm_virtual_routines virt_routine_panel( g_main_window );
FW.setContent( g_main_window );
FW.setWindowIcon( QIcon( "icon.ico" ) );
FW.show();
return app.exec(); return app.exec();
} }

@ -1,21 +0,0 @@
#pragma once
#define NOMINMAX
#include "qvminspector.h"
class qvirt_instrs_t : public QObject
{
Q_OBJECT
public:
explicit qvirt_instrs_t( qvminspector_t *vminspector );
private:
Ui::QVMProfilerClass *ui;
qvminspector_t *vminspector;
void update_native_regs( vm::instrs::virt_instr_t *virt_instr );
void update_virtual_regs( vm::instrs::virt_instr_t *virt_instr );
void update_virtual_stack( vm::instrs::virt_instr_t *virt_instr );
void update_vm_handler_info( vm::instrs::virt_instr_t *virt_instr );
private slots:
void on_select();
};

@ -1,11 +1,12 @@
#include "qvirt_handlers.h" #include "qvm_handlers.h"
qvirt_handlers_t::qvirt_handlers_t( qvminspector_t *vminspector ) : vminspector( vminspector ), ui( &vminspector->ui ) qvm_handlers::qvm_handlers( qvm_inspector *g_main_window )
: g_main_window( g_main_window ), ui( &g_main_window->ui )
{ {
connect( ui->virt_handlers_tree, &QTreeWidget::itemSelectionChanged, this, &qvirt_handlers_t::on_select ); connect( ui->virt_handlers_tree, &QTreeWidget::itemSelectionChanged, this, &qvm_handlers::on_select );
} }
void qvirt_handlers_t::update_transforms( vm::handler::handler_t &vm_handler ) void qvm_handlers::update_transforms( vm::handler::handler_t &vm_handler )
{ {
char buffer[ 256 ]; char buffer[ 256 ];
ZydisFormatter formatter; ZydisFormatter formatter;
@ -19,7 +20,7 @@ void qvirt_handlers_t::update_transforms( vm::handler::handler_t &vm_handler )
if ( transform_type == vm::transform::type::generic0 && transform_instr.mnemonic == ZYDIS_MNEMONIC_INVALID ) if ( transform_type == vm::transform::type::generic0 && transform_instr.mnemonic == ZYDIS_MNEMONIC_INVALID )
continue; continue;
auto new_transform_entry = new qtree_widget_item_t(); auto new_transform_entry = new QTreeWidgetItem();
switch ( transform_type ) switch ( transform_type )
{ {
@ -52,7 +53,7 @@ void qvirt_handlers_t::update_transforms( vm::handler::handler_t &vm_handler )
} }
} }
void qvirt_handlers_t::update_instrs( vm::handler::handler_t &vm_handler ) void qvm_handlers::update_instrs( vm::handler::handler_t &vm_handler )
{ {
char buffer[ 256 ]; char buffer[ 256 ];
ZydisFormatter formatter; ZydisFormatter formatter;
@ -64,19 +65,18 @@ void qvirt_handlers_t::update_instrs( vm::handler::handler_t &vm_handler )
// display vm handler instructions... // display vm handler instructions...
for ( const auto &instr : vm_handler_instrs ) for ( const auto &instr : vm_handler_instrs )
{ {
auto new_instr = new qtree_widget_item_t(); auto new_instr = new QTreeWidgetItem();
new_instr->setText( new_instr->setText( 0, QString::number( ( instr.addr - g_main_window->module_base ) + g_main_window->img_base, 16 ) );
0, qstring_t::number( ( instr.addr - vminspector->module_base ) + vminspector->image_base, 16 ) );
ZydisFormatterFormatInstruction( &formatter, &instr.instr, buffer, sizeof( buffer ), ZydisFormatterFormatInstruction( &formatter, &instr.instr, buffer, sizeof( buffer ),
( instr.addr - vminspector->module_base ) + vminspector->image_base ); ( instr.addr - g_main_window->module_base ) + g_main_window->img_base );
new_instr->setText( 1, buffer ); new_instr->setText( 1, buffer );
ui->virt_handler_instrs_tree->addTopLevelItem( new_instr ); ui->virt_handler_instrs_tree->addTopLevelItem( new_instr );
} }
} }
void qvirt_handlers_t::on_select() void qvm_handlers::on_select()
{ {
if ( ui->virt_handlers_tree->selectedItems().empty() ) if ( ui->virt_handlers_tree->selectedItems().empty() )
return; return;
@ -86,10 +86,10 @@ void qvirt_handlers_t::on_select()
if ( !item ) if ( !item )
return; return;
if ( !vminspector->vmctx ) if ( !g_main_window->g_vm_ctx )
return; return;
const auto handler_idx = item->data( 0, Qt::UserRole ).value< std::uint8_t >(); const auto handler_idx = item->data( 0, Qt::UserRole ).value< std::uint8_t >();
update_instrs( vminspector->vmctx->vm_handlers[ handler_idx ] ); update_instrs( g_main_window->g_vm_ctx->vm_handlers[ handler_idx ] );
update_transforms( vminspector->vmctx->vm_handlers[ handler_idx ] ); update_transforms( g_main_window->g_vm_ctx->vm_handlers[ handler_idx ] );
} }

@ -1,16 +1,15 @@
#pragma once #pragma once
#define NOMINMAX #include "qvm_inspector.h"
#include "qvminspector.h"
class qvirt_handlers_t : public QObject class qvm_handlers : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit qvirt_handlers_t( qvminspector_t *vminspector ); explicit qvm_handlers( qvm_inspector *g_main_window );
private: private:
Ui::QVMProfilerClass *ui; Ui::QVMProfilerClass *ui;
qvminspector_t *vminspector; qvm_inspector *g_main_window;
void update_transforms( vm::handler::handler_t &vm_handler ); void update_transforms( vm::handler::handler_t &vm_handler );
void update_instrs( vm::handler::handler_t &vm_handler ); void update_instrs( vm::handler::handler_t &vm_handler );

@ -0,0 +1,348 @@
#include "qvm_inspector.h"
qvm_inspector::qvm_inspector( QWidget *parent ) : QMainWindow( parent ), file_header( nullptr ), g_vm_ctx( nullptr )
{
ui.setupUi( this );
ui.virt_instrs->setColumnWidth( 0, 125 );
ui.virt_instrs->setColumnWidth( 1, 150 );
ui.virt_instrs->setColumnWidth( 2, 190 );
ui.virt_instrs->setColumnWidth( 3, 200 );
ui.virtual_machine_enters->setColumnWidth( 0, 180 );
connect( ui.action_open, &QAction::triggered, this, &qvm_inspector::on_open );
connect( ui.action_close, &QAction::triggered, this, &qvm_inspector::on_close );
}
void qvm_inspector::on_close()
{
exit( 0 );
}
void qvm_inspector::on_open()
{
if ( file_header )
free( file_header );
file_header = nullptr;
img_base = 0u, module_base = 0u;
auto file_path = QFileDialog::getOpenFileName(
this, tr( "open vmp2 file" ), std::filesystem::current_path().string().c_str(), tr( "vmp2 file (*.vmp2)" ) );
if ( file_path.isEmpty() )
{
dbg_msg( "invalid vmp2 file... no file selected..." );
return;
}
if ( !std::filesystem::exists( file_path.toStdString() ) )
{
dbg_msg( "vmp2 file does not exist..." );
return;
}
const auto file_size = std::filesystem::file_size( file_path.toStdString() );
if ( !file_size )
{
dbg_msg( "invalid vmp2 file size..." );
return;
}
QFile open_file( file_path );
file_header = reinterpret_cast< vmp2::v4::file_header * >( malloc( file_size ) );
if ( !open_file.open( QIODevice::ReadOnly ) )
{
dbg_msg( "failed to open vmp2 file..." );
return;
}
memcpy( file_header, open_file.readAll().data(), file_size );
if ( !init_data() )
{
dbg_msg( "failed to init vmp2 file data..." );
return;
}
}
void qvm_inspector::dbg_print( QString dbg_output )
{
ui.dbg_output_window->appendPlainText( dbg_output );
}
void qvm_inspector::dbg_msg( QString dbg_output )
{
QMessageBox msg_box;
msg_box.setText( dbg_output );
msg_box.exec();
dbg_print( dbg_output );
}
bool qvm_inspector::init_data()
{
if ( file_header->magic != VMP_MAGIC )
{
dbg_msg( "invalid magic bytes for vmp2 file..." );
return false;
}
dbg_print( "valid magic bytes for vmp2 file..." );
if ( file_header->version != vmp2::version_t::v4 )
{
dbg_msg( "invalid vmp2 file version... "
"this vminspector is compiled for version 4...\n" );
return false;
}
img_base = file_header->image_base;
img_size = file_header->module_size;
module_base = reinterpret_cast< std::uintptr_t >( file_header ) + file_header->module_offset;
if ( !serialize_vmp2( virt_rtns ) )
{
dbg_msg( "failed to serialize vmp2 file format...\n" );
return false;
}
update_ui();
return true;
}
void qvm_inspector::update_ui()
{
ui.virtual_machine_enters->clear();
for ( auto &[ rtn_rva, rtn_blks ] : virt_rtns )
{
auto new_item = new QTreeWidgetItem();
new_item->setText( 0, QString( "rtn_%1" ).arg( rtn_rva + file_header->image_base, 0, 16 ) );
new_item->setText( 1, QString( "%1" ).arg( rtn_rva + file_header->image_base, 0, 16 ) );
new_item->setText( 2, QString( "%1" ).arg( rtn_blks.size() ) );
new_item->setData( 0, Qt::UserRole, QVariant( rtn_rva ) );
std::for_each( rtn_blks.begin(), rtn_blks.end(), [ & ]( vm::instrs::code_block_t &code_blk ) {
auto new_child = new QTreeWidgetItem();
new_child->setText( 0, QString( "blk_%1" ).arg( code_blk.vip_begin, 0, 16 ) );
new_child->setText( 1, QString( "%1" ).arg( code_blk.vip_begin, 0, 16 ) );
new_child->setText( 2, QString( "%1" ).arg( code_blk.vinstrs.size() ) );
new_item->addChild( new_child );
} );
ui.virtual_machine_enters->addTopLevelItem( new_item );
}
}
bool qvm_inspector::serialize_vmp2( std::vector< rtn_data_t > &virt_rtns )
{
if ( file_header->version != vmp2::version_t::v4 )
{
std::printf( "[!] invalid vmp2 file version... this build uses v3...\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().rtn_blks.push_back( _code_block );
}
}
return true;
}
void qvm_inspector::update_virtual_instructions( std::uintptr_t rtn_addr, std::uintptr_t blk_addr,
QTreeWidgetItem *parent )
{
auto _rtn = std::find_if( virt_rtns.begin(), virt_rtns.end(),
[ & ]( rtn_data_t &rtn ) -> bool { return rtn.rtn_rva == rtn_addr; } );
if ( _rtn == virt_rtns.end() )
return;
auto code_blk = blk_addr ? std::find_if( _rtn->rtn_blks.begin(), _rtn->rtn_blks.end(),
[ & ]( const vm::instrs::code_block_t &code_block ) -> bool {
return code_block.vip_begin == blk_addr;
} )
: _rtn->rtn_blks.begin();
if ( code_blk == _rtn->rtn_blks.end() )
return;
if ( std::find( code_block_addrs.begin(), code_block_addrs.end(), code_blk->vip_begin ) != code_block_addrs.end() )
return;
code_block_addrs.push_back( code_blk->vip_begin );
for ( auto &vinstr : code_blk->vinstrs )
{
const auto profile = vm::handler::get_profile( vinstr.mnemonic_t );
auto virt_instr_entry = new QTreeWidgetItem();
// virtual instruction image base'ed rva... (column 1)...
virt_instr_entry->setText(
0, QString( "0x%1" ).arg( QString::number(
( vinstr.trace_data.vip - file_header->module_base ) + file_header->image_base, 16 ) ) );
// virtual instruction operand bytes... (column 2)...
QString operand_bytes;
operand_bytes.append( QString( "%1" ).arg( vinstr.opcode, 0, 16 ) );
// if virt instruction has an imm... grab its bytes...
if ( vinstr.operand.has_imm )
{
operand_bytes.append( " - " );
for ( auto _idx = 0u; _idx < vinstr.operand.imm.imm_size / 8; ++_idx )
operand_bytes.append( QString( "%1 " ).arg(
reinterpret_cast< const std::uint8_t * >( &vinstr.operand.imm.u )[ _idx ], 0, 16 ) );
}
virt_instr_entry->setText( 1, operand_bytes );
// virtual instruction string, includes imm... (colume 3)...
QString decoded_instr(
QString( "%1" ).arg( profile ? profile->name : QString( "UNK(%1)" ).arg( vinstr.opcode, 0, 16 ) ) );
if ( vinstr.operand.has_imm ) // if there is a second operand (imm) put its value...
decoded_instr.append( QString( " %1" ).arg( vinstr.operand.imm.u, 0, 16 ) );
virt_instr_entry->setText( 2, decoded_instr );
// add comments to the virtual instruction... (colume 4)...
if ( vinstr.mnemonic_t == vm::handler::LREGQ || vinstr.mnemonic_t == vm::handler::SREGQ )
virt_instr_entry->setText(
3, QString( "; vreg%1" ).arg( vinstr.operand.imm.u ? ( vinstr.operand.imm.u / 8 ) : 0u ) );
if ( vinstr.mnemonic_t == vm::handler::JMP )
{
switch ( code_blk->jcc.type )
{
case vm::instrs::jcc_type::branching:
{
virt_instr_entry->setText( 3, QString( "; { 0x%1, 0x%2 }" )
.arg( code_blk->jcc.block_addr[ 0 ], 0, 16 )
.arg( code_blk->jcc.block_addr[ 1 ], 0, 16 ) );
auto entry_rva = g_vm_ctx->vm_handlers[ vinstr.opcode ].address - module_base;
auto branch_entry1 = new QTreeWidgetItem(), branch_entry2 = new QTreeWidgetItem();
const auto block1_addr = code_blk->jcc.block_addr[ 0 ];
const auto block2_addr = code_blk->jcc.block_addr[ 1 ];
branch_entry1->setText( 0, QString( "0x%1" ).arg( block1_addr, 0, 16 ) );
branch_entry1->setText( 3, QString( "; blk_0x%1" ).arg( block1_addr, 0, 16 ) );
branch_entry2->setText( 0, QString( "0x%1" ).arg( block2_addr, 0, 16 ) );
branch_entry2->setText( 3, QString( "; blk_0x%1" ).arg( block2_addr, 0, 16 ) );
if ( g_vm_ctx )
delete g_vm_ctx;
if ( !( g_vm_ctx = new vm::ctx_t( module_base, img_base, img_size, entry_rva ) )->init() )
{
dbg_print( QString( "> failed to init vm::ctx_t for jmp at = %1..." )
.arg( QString::number( code_blk->vip_begin, 16 ) ) );
return;
}
update_virtual_instructions( rtn_addr, code_blk->jcc.block_addr[ 0 ], branch_entry1 );
if ( g_vm_ctx )
delete g_vm_ctx;
if ( !( g_vm_ctx = new vm::ctx_t( module_base, img_base, img_size, entry_rva ) )->init() )
{
dbg_print( QString( "> failed to init vm::ctx_t for jmp at = %1..." )
.arg( QString::number( code_blk->vip_begin, 16 ) ) );
return;
}
update_virtual_instructions( rtn_addr, code_blk->jcc.block_addr[ 1 ], branch_entry2 );
virt_instr_entry->addChildren( { branch_entry1, branch_entry2 } );
break;
}
case vm::instrs::jcc_type::absolute:
{
auto entry_rva = g_vm_ctx->vm_handlers[ vinstr.opcode ].address - module_base;
virt_instr_entry->setText( 3, QString( "; { 0x%1 }" ).arg( code_blk->jcc.block_addr[ 0 ], 0, 16 ) );
if ( g_vm_ctx )
delete g_vm_ctx;
if ( !( g_vm_ctx = new vm::ctx_t( module_base, img_base, img_size, entry_rva ) )->init() )
{
dbg_print( QString( "> failed to init vm::ctx_t for jmp at = %1..." )
.arg( QString::number( code_blk->vip_begin, 16 ) ) );
return;
}
auto branch_entry1 = new QTreeWidgetItem();
branch_entry1->setText( 0, QString( "0x%1" ).arg( code_blk->jcc.block_addr[ 0 ], 0, 16 ) );
branch_entry1->setText( 3, QString( "; blk_0x%1" ).arg( code_blk->jcc.block_addr[ 0 ], 0, 16 ) );
update_virtual_instructions( rtn_addr, code_blk->jcc.block_addr[ 0 ], branch_entry1 );
virt_instr_entry->addChild( branch_entry1 );
break;
}
case vm::instrs::jcc_type::switch_case:
{
auto entry_rva = g_vm_ctx->vm_handlers[ vinstr.opcode ].address - module_base;
if ( g_vm_ctx )
delete g_vm_ctx;
if ( !( g_vm_ctx = new vm::ctx_t( module_base, img_base, img_size, entry_rva ) )->init() )
{
dbg_print( QString( "> failed to init vm::ctx_t for jmp at = %1..." )
.arg( QString::number( code_blk->vip_begin, 16 ) ) );
return;
}
for ( auto branch_addr : code_blk->jcc.block_addr )
{
virt_instr_entry->setText( 3, QString( "; switch case" ) );
auto branch_entry = new QTreeWidgetItem();
branch_entry->setText( 0, QString( "0x%1" ).arg( branch_addr, 0, 16 ) );
update_virtual_instructions( rtn_addr, branch_addr, branch_entry );
virt_instr_entry->addChild( branch_entry );
}
break;
}
default:
break;
}
}
QVariant var;
var.setValue( vinstr );
virt_instr_entry->setData( 3, Qt::UserRole, var );
parent ? parent->addChild( virt_instr_entry ) : ui.virt_instrs->addTopLevelItem( virt_instr_entry );
}
}

@ -0,0 +1,56 @@
#pragma once
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QInputDialog.h>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMessageBox.h>
#include <qpushbutton.h>
#include <qtreewidget.h>
#include <Windows.h>
#include <filesystem>
#include <fstream>
#include <vmprofiler.hpp>
#include "ui_qvminspector.h"
#include <ia32.hpp>
#include <vmprofiler.hpp>
#define ABS_TO_IMG( addr, mod_base, img_base ) ( addr - mod_base ) + img_base
Q_DECLARE_METATYPE( vm::instrs::virt_instr_t )
struct rtn_data_t
{
std::uint32_t rtn_rva;
std::vector< vm::instrs::code_block_t > rtn_blks;
};
class qvm_inspector : public QMainWindow
{
friend class qvm_virtual_instructions;
friend class qvm_handlers;
friend class qvm_virtual_routines;
Q_OBJECT
public:
qvm_inspector( QWidget *parent = Q_NULLPTR );
private slots:
void on_open();
void on_close();
private:
void dbg_print( QString DbgOutput );
void dbg_msg( QString DbgOutput );
void update_ui();
bool serialize_vmp2( std::vector< rtn_data_t > &virt_rtns );
void update_virtual_instructions( std::uintptr_t rtn_addr, std::uintptr_t blk_addr = 0ull,
QTreeWidgetItem *parent = nullptr );
bool init_data();
Ui::QVMProfilerClass ui;
std::uint64_t img_base, module_base, img_size;
vm::ctx_t *g_vm_ctx;
vmp2::v4::file_header *file_header;
std::vector< rtn_data_t > virt_rtns;
std::vector< std::uintptr_t > code_block_addrs;
};

@ -1,11 +1,12 @@
#include "qvirt_instrs.h" #include "qvm_virtual_instructions.h"
qvirt_instrs_t::qvirt_instrs_t( qvminspector_t *vminspector ) : vminspector( vminspector ), ui( &vminspector->ui ) qvm_virtual_instructions::qvm_virtual_instructions( qvm_inspector *g_main_window )
: g_main_window( g_main_window ), ui( &g_main_window->ui )
{ {
connect( ui->virt_instrs, &QTreeWidget::itemSelectionChanged, this, &qvirt_instrs_t::on_select ); connect( ui->virt_instrs, &QTreeWidget::itemSelectionChanged, this, &qvm_virtual_instructions::on_select );
} }
void qvirt_instrs_t::on_select() void qvm_virtual_instructions::on_select()
{ {
if ( ui->virt_instrs->selectedItems().empty() ) if ( ui->virt_instrs->selectedItems().empty() )
return; return;
@ -15,18 +16,14 @@ void qvirt_instrs_t::on_select()
if ( !item ) if ( !item )
return; return;
const auto virt_instr = item->data( 3, Qt::UserRole ).value< vm::instrs::virt_instr_t * >(); auto virt_instr = item->data( 3, Qt::UserRole ).value< vm::instrs::virt_instr_t >();
update_native_registers( &virt_instr );
if ( !virt_instr ) update_virtual_registers( &virt_instr );
return; update_virtual_stack( &virt_instr );
update_vmhandler_info( &virt_instr );
update_native_regs( virt_instr );
update_virtual_regs( virt_instr );
update_virtual_stack( virt_instr );
update_vm_handler_info( virt_instr );
} }
void qvirt_instrs_t::update_native_regs( vm::instrs::virt_instr_t *virt_instr ) void qvm_virtual_instructions::update_native_registers( vm::instrs::virt_instr_t *virt_instr )
{ {
const auto &trace_data = virt_instr->trace_data; const auto &trace_data = virt_instr->trace_data;
@ -52,15 +49,15 @@ void qvirt_instrs_t::update_native_regs( vm::instrs::virt_instr_t *virt_instr )
ui->native_regs->topLevelItem( 16 )->child( 8 )->setText( 1, QString::number( flags.interrupt_enable_flag ) ); ui->native_regs->topLevelItem( 16 )->child( 8 )->setText( 1, QString::number( flags.interrupt_enable_flag ) );
} }
void qvirt_instrs_t::update_virtual_regs( vm::instrs::virt_instr_t *virt_instr ) void qvm_virtual_instructions::update_virtual_registers( vm::instrs::virt_instr_t *virt_instr )
{ {
const auto &trace_data = virt_instr->trace_data; const auto &trace_data = virt_instr->trace_data;
// set VIP in virtual registers window... // set VIP in virtual registers window...
ui->virt_regs->topLevelItem( 0 )->setText( ui->virt_regs->topLevelItem( 0 )->setText(
1, 1, QString::number( ( trace_data.vip - g_main_window->file_header->module_base ) +
QString::number( g_main_window->file_header->image_base,
( trace_data.vip - vminspector->file_header->module_base ) + vminspector->file_header->image_base, 16 ) ); 16 ) );
// set VSP in virtual registers window... // set VSP in virtual registers window...
ui->virt_regs->topLevelItem( 1 )->setText( 1, QString::number( trace_data.regs.rbp, 16 ) ); ui->virt_regs->topLevelItem( 1 )->setText( 1, QString::number( trace_data.regs.rbp, 16 ) );
@ -73,38 +70,38 @@ void qvirt_instrs_t::update_virtual_regs( vm::instrs::virt_instr_t *virt_instr )
ui->virt_regs->topLevelItem( idx )->setText( 1, QString::number( trace_data.vregs.qword[ idx - 4 ], 16 ) ); ui->virt_regs->topLevelItem( idx )->setText( 1, QString::number( trace_data.vregs.qword[ idx - 4 ], 16 ) );
} }
void qvirt_instrs_t::update_virtual_stack( vm::instrs::virt_instr_t *virt_instr ) void qvm_virtual_instructions::update_virtual_stack( vm::instrs::virt_instr_t *virt_instr )
{ {
ui->virt_stack->clear(); ui->virt_stack->clear();
const auto &trace_data = virt_instr->trace_data; const auto &trace_data = virt_instr->trace_data;
for ( auto idx = 0u; idx < sizeof( trace_data.vsp ) / 8; ++idx ) for ( auto idx = 0u; idx < sizeof( trace_data.vsp ) / 8; ++idx )
{ {
auto new_stack_entry = new qtree_widget_item_t(); auto new_stack_entry = new QTreeWidgetItem();
new_stack_entry->setText( 0, qstring_t::number( trace_data.regs.rbp + ( idx * 8 ), 16 ) ); new_stack_entry->setText( 0, QString::number( trace_data.regs.rbp + ( idx * 8 ), 16 ) );
new_stack_entry->setText( 1, qstring_t::number( trace_data.vsp.qword[ idx ], 16 ) ); new_stack_entry->setText( 1, QString::number( trace_data.vsp.qword[ idx ], 16 ) );
ui->virt_stack->addTopLevelItem( new_stack_entry ); ui->virt_stack->addTopLevelItem( new_stack_entry );
} }
} }
void qvirt_instrs_t::update_vm_handler_info( vm::instrs::virt_instr_t *virt_instr ) void qvm_virtual_instructions::update_vmhandler_info( vm::instrs::virt_instr_t *virt_instr )
{ {
char buffer[ 256 ]; char buffer[ 256 ];
ZydisFormatter formatter; ZydisFormatter formatter;
ZydisFormatterInit( &formatter, ZYDIS_FORMATTER_STYLE_INTEL ); ZydisFormatterInit( &formatter, ZYDIS_FORMATTER_STYLE_INTEL );
ui->vm_handler_instrs->clear(); ui->vm_handler_instrs->clear();
const auto &vm_handler_instrs = vminspector->vmctx->vm_handlers[ virt_instr->opcode ].instrs; const auto &vm_handler_instrs = g_main_window->g_vm_ctx->vm_handlers[ virt_instr->opcode ].instrs;
// display vm handler instructions... // display vm handler instructions...
for ( const auto &instr : vm_handler_instrs ) for ( const auto &instr : vm_handler_instrs )
{ {
auto new_instr = new qtree_widget_item_t(); auto new_instr = new QTreeWidgetItem();
new_instr->setText( new_instr->setText(
0, qstring_t::number( ( instr.addr - vminspector->module_base ) + vminspector->image_base, 16 ) ); 0, QString::number( ( instr.addr - g_main_window->module_base ) + g_main_window->img_base, 16 ) );
ZydisFormatterFormatInstruction( &formatter, &instr.instr, buffer, sizeof( buffer ), ZydisFormatterFormatInstruction( &formatter, &instr.instr, buffer, sizeof( buffer ),
( instr.addr - vminspector->module_base ) + vminspector->image_base ); ( instr.addr - g_main_window->module_base ) + g_main_window->img_base );
new_instr->setText( 1, buffer ); new_instr->setText( 1, buffer );
ui->vm_handler_instrs->addTopLevelItem( new_instr ); ui->vm_handler_instrs->addTopLevelItem( new_instr );
@ -112,14 +109,14 @@ void qvirt_instrs_t::update_vm_handler_info( vm::instrs::virt_instr_t *virt_inst
// display vm handler transformations... // display vm handler transformations...
ui->vm_handler_transforms->clear(); ui->vm_handler_transforms->clear();
const auto &vm_handler_transforms = vminspector->vmctx->vm_handlers[ virt_instr->opcode ].transforms; const auto &vm_handler_transforms = g_main_window->g_vm_ctx->vm_handlers[ virt_instr->opcode ].transforms;
for ( auto [ transform_type, transform_instr ] : vm_handler_transforms ) for ( auto [ transform_type, transform_instr ] : vm_handler_transforms )
{ {
if ( transform_type == vm::transform::type::generic0 && transform_instr.mnemonic == ZYDIS_MNEMONIC_INVALID ) if ( transform_type == vm::transform::type::generic0 && transform_instr.mnemonic == ZYDIS_MNEMONIC_INVALID )
continue; continue;
auto new_transform_entry = new qtree_widget_item_t(); auto new_transform_entry = new QTreeWidgetItem();
switch ( transform_type ) switch ( transform_type )
{ {

@ -0,0 +1,21 @@
#pragma once
#include "qvm_inspector.h"
class qvm_virtual_instructions : public QObject
{
Q_OBJECT
public:
explicit qvm_virtual_instructions( qvm_inspector *g_main_window );
private:
Ui::QVMProfilerClass *ui;
qvm_inspector *g_main_window;
void update_native_registers( vm::instrs::virt_instr_t *virt_instr );
void update_virtual_registers( vm::instrs::virt_instr_t *virt_instr );
void update_virtual_stack( vm::instrs::virt_instr_t *virt_instr );
void update_vmhandler_info( vm::instrs::virt_instr_t *virt_instr );
private slots:
void on_select();
};

@ -0,0 +1,112 @@
#include "qvm_virtual_routines.h"
qvm_virtual_routines::qvm_virtual_routines( qvm_inspector *g_main_window )
: g_main_window( g_main_window ), ui( &g_main_window->ui )
{
connect( ui->virtual_machine_enters, &QTreeWidget::itemSelectionChanged, this, &qvm_virtual_routines::on_select );
}
void qvm_virtual_routines::update_vm_enter( vm::ctx_t *g_vm_ctx )
{
char buffer[ 256 ];
ZydisFormatter formatter;
ZydisFormatterInit( &formatter, ZYDIS_FORMATTER_STYLE_INTEL );
ui->virtual_machine_enter_instrs->clear();
for ( auto [ instr, raw, addr ] : g_vm_ctx->vm_entry )
{
ZydisFormatterFormatInstruction( &formatter, &instr, buffer, sizeof( buffer ),
( addr - g_main_window->module_base ) + g_vm_ctx->image_base );
auto newItem = new QTreeWidgetItem();
newItem->setText( 0, QString::number( ( addr - g_main_window->module_base ) + g_vm_ctx->image_base, 16 ) );
newItem->setText( 1, buffer );
ui->virtual_machine_enter_instrs->addTopLevelItem( newItem );
}
}
void qvm_virtual_routines::update_calc_jmp( vm::ctx_t *g_vm_ctx )
{
char buffer[ 256 ];
ZydisFormatter formatter;
ZydisFormatterInit( &formatter, ZYDIS_FORMATTER_STYLE_INTEL );
ui->virtual_machine_enter_calc_jmp->clear();
for ( auto [ instr, raw, addr ] : g_vm_ctx->calc_jmp )
{
ZydisFormatterFormatInstruction( &formatter, &instr, buffer, sizeof( buffer ),
( addr - g_main_window->module_base ) + g_vm_ctx->image_base );
auto newItem = new QTreeWidgetItem();
newItem->setText( 0, QString::number( ( addr - g_main_window->module_base ) + g_vm_ctx->image_base, 16 ) );
newItem->setText( 1, buffer );
ui->virtual_machine_enter_calc_jmp->addTopLevelItem( newItem );
}
}
void qvm_virtual_routines::update_vm_handlers( vm::ctx_t *g_vm_ctx )
{
ui->virt_handlers_tree->clear();
for ( auto idx = 0u; idx < g_vm_ctx->vm_handlers.size(); ++idx )
{
auto newItem = new QTreeWidgetItem;
newItem->setData( 0, Qt::UserRole, idx );
newItem->setText( 0, QString( "%1" ).arg( idx ) );
newItem->setText( 1,
QString( "%1" ).arg( ( g_vm_ctx->vm_handlers[ idx ].address - g_main_window->module_base ) +
g_main_window->img_base,
0, 16 ) );
newItem->setText( 2, g_vm_ctx->vm_handlers[ idx ].profile ? g_vm_ctx->vm_handlers[ idx ].profile->name
: "UNDEFINED" );
newItem->setText( 3, QString( "%1" ).arg( g_vm_ctx->vm_handlers[ idx ].imm_size ) );
if ( g_vm_ctx->vm_handlers[ idx ].profile && g_vm_ctx->vm_handlers[ idx ].imm_size )
newItem->setText( 4,
g_vm_ctx->vm_handlers[ idx ].profile->extention == vm::handler::extention_t::sign_extend
? "SIGN EXTENDED"
: "ZERO EXTENDED" );
else
newItem->setText( 4, "UNDEFINED" );
ui->virt_handlers_tree->addTopLevelItem( newItem );
}
ui->virt_handlers_tree->topLevelItem( 0 )->setSelected( true );
}
void qvm_virtual_routines::on_select()
{
if ( ui->virtual_machine_enters->selectedItems().empty() )
return;
auto item = ui->virtual_machine_enters->selectedItems()[ 0 ];
if ( !item || !item->childCount() )
return;
auto entry_rva = item->data( 0, Qt::UserRole ).value< std::uint32_t >();
if ( !entry_rva )
return;
if ( g_main_window->g_vm_ctx )
delete g_main_window->g_vm_ctx;
g_main_window->g_vm_ctx =
new vm::ctx_t( g_main_window->module_base, g_main_window->img_base, g_main_window->img_size, entry_rva );
if ( !g_main_window->g_vm_ctx->init() )
{
g_main_window->dbg_msg( "[!] failed to init vm::ctx_t...\n" );
return;
}
update_vm_enter( g_main_window->g_vm_ctx );
update_calc_jmp( g_main_window->g_vm_ctx );
update_vm_handlers( g_main_window->g_vm_ctx );
ui->virt_instrs->clear();
g_main_window->code_block_addrs.clear();
g_main_window->update_virtual_instructions( entry_rva );
}

@ -0,0 +1,19 @@
#pragma once
#include "qvm_inspector.h"
class qvm_virtual_routines : public QObject
{
Q_OBJECT
public:
explicit qvm_virtual_routines( qvm_inspector *g_main_window );
private:
Ui::QVMProfilerClass *ui;
qvm_inspector *g_main_window;
void update_vm_enter( vm::ctx_t *g_vm_ctx );
void update_calc_jmp( vm::ctx_t *g_vm_ctx );
void update_vm_handlers( vm::ctx_t *g_vm_ctx );
private slots:
void on_select();
};

@ -1,385 +0,0 @@
#include "qvminspector.h"
qvminspector_t::qvminspector_t( qwidget_t *parent ) : qmain_window_t( parent ), file_header( nullptr ), vmctx( nullptr )
{
ui.setupUi( this );
ui.virt_instrs->setColumnWidth( 0, 180 );
ui.virt_instrs->setColumnWidth( 1, 150 );
ui.virt_instrs->setColumnWidth( 2, 190 );
ui.virt_instrs->setColumnWidth( 3, 200 );
connect( ui.action_open, &QAction::triggered, this, &qvminspector_t::on_open );
connect( ui.action_close, &QAction::triggered, this, &qvminspector_t::on_close );
}
void qvminspector_t::on_close()
{
exit( 0 );
}
void qvminspector_t::on_open()
{
if ( file_header && vmctx )
{
free( file_header );
delete vmctx;
}
file_header = nullptr, first_block = nullptr;
code_block_addrs.clear();
image_base = 0u, vm_entry_rva = 0u, module_base = 0u;
file_path = QFileDialog::getOpenFileName(
this, tr( "open vmp2 file" ), std::filesystem::current_path().string().c_str(), tr( "vmp2 file (*.vmp2)" ) );
const auto &_file_path = file_path.toStdString();
if ( file_path.isEmpty() )
{
dbg_msg( "invalid vmp2 file... no file selected..." );
return;
}
if ( !std::filesystem::exists( _file_path ) )
{
dbg_msg( "vmp2 file does not exist..." );
return;
}
const auto file_size = std::filesystem::file_size( _file_path );
if ( !file_size )
{
dbg_msg( "invalid vmp2 file size..." );
return;
}
qfile_t open_file( file_path );
file_header = reinterpret_cast< vmp2::v3::file_header * >( malloc( file_size ) );
if ( !open_file.open( QIODevice::ReadOnly ) )
{
dbg_msg( "failed to open vmp2 file..." );
return;
}
memcpy( file_header, open_file.readAll().data(), file_size );
if ( !init_data() )
{
dbg_msg( "failed to init vmp2 file data..." );
return;
}
update_ui();
}
void qvminspector_t::dbg_print( qstring_t dbg_output )
{
ui.dbg_output_window->appendPlainText( dbg_output );
}
void qvminspector_t::dbg_msg( qstring_t dbg_output )
{
qmsg_box_t msg_box;
msg_box.setText( dbg_output );
msg_box.exec();
dbg_print( dbg_output );
}
bool qvminspector_t::init_data()
{
if ( file_header->magic != VMP_MAGIC )
{
dbg_msg( "invalid magic bytes for vmp2 file..." );
return false;
}
dbg_print( "valid magic bytes for vmp2 file..." );
if ( file_header->version != vmp2::version_t::v3 )
{
dbg_msg( "invalid vmp2 file version... "
"this vminspector is compiled for version 3...\n" );
return false;
}
vm_entry_rva = file_header->vm_entry_rva;
image_base = file_header->image_base;
image_size = file_header->module_size;
module_base = reinterpret_cast< std::uintptr_t >( file_header ) + file_header->module_offset;
vmctx = new vm::ctx_t( module_base, image_base, image_size, vm_entry_rva );
if ( !vmctx->init() )
{
dbg_msg( "failed to init vm::ctx_t... this can happen for many reasons..."
"ensure that the vmp2 file is not corrupted...\n" );
return false;
}
first_block = reinterpret_cast< vmp2::v3::code_block_t * >( reinterpret_cast< std::uintptr_t >( file_header ) +
file_header->code_block_offset );
return true;
}
void qvminspector_t::add_branch_children( qtree_widget_item_t *item, std::uintptr_t branch_addr )
{
if ( std::find( code_block_addrs.begin(), code_block_addrs.end(), branch_addr ) != code_block_addrs.end() )
return;
code_block_addrs.push_back( branch_addr );
// for each code block find the one that starts with the desired branch...
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 ( code_block->vip_begin != branch_addr )
continue;
dbg_print( qstring_t( "> code block %1 (block_%2), number of vinstrs = %3..." )
.arg( code_block_num )
.arg( code_block->vip_begin, 0, 16 )
.arg( code_block->vinstr_count ) );
// for each virtual instruction inside of this code block...
for ( auto idx = 0u; idx < code_block->vinstr_count; ++idx )
{
const auto virt_instr = &code_block->vinstr[ idx ];
const auto profile = vm::handler::get_profile( virt_instr->mnemonic_t );
auto virt_instr_entry = new qtree_widget_item_t;
// virtual instruction image base'ed rva... (column 1)...
virt_instr_entry->setText(
0, QString( "block_%1+%2" )
.arg( branch_addr, 0, 16 )
.arg( idx ? code_block->vip_begin >
( ( virt_instr->trace_data.vip - file_header->module_base ) + image_base )
? code_block->vip_begin -
( ( virt_instr->trace_data.vip - file_header->module_base ) + image_base )
: ( ( virt_instr->trace_data.vip - file_header->module_base ) + image_base ) -
code_block->vip_begin
: 0u,
0, 16 ) );
// virtual instruction operand bytes... (column 2)...
qstring_t operand_bytes;
operand_bytes.append( QString( "%1" ).arg( virt_instr->opcode, 0, 16 ) );
// if virt instruction has an imm... grab its bytes...
if ( virt_instr->operand.has_imm )
{
operand_bytes.append( " - " );
for ( auto _idx = 0u; _idx < virt_instr->operand.imm.imm_size / 8; ++_idx )
operand_bytes.append( QString( "%1 " ).arg(
reinterpret_cast< std::uint8_t * >( &virt_instr->operand.imm.u )[ _idx ], 0, 16 ) );
}
virt_instr_entry->setText( 1, operand_bytes );
// virtual instruction string, includes imm... (colume 3)...
QString decoded_instr( QString( "%1" ).arg(
profile ? profile->name : QString( "UNK(%1)" ).arg( virt_instr->opcode, 0, 16 ) ) );
if ( virt_instr->operand.has_imm ) // if there is a second operand (imm) put its value...
decoded_instr.append( QString( " %1" ).arg( virt_instr->operand.imm.u, 0, 16 ) );
virt_instr_entry->setText( 2, decoded_instr );
// add comments to the virtual instruction... (colume 4)...
if ( virt_instr->mnemonic_t == vm::handler::LREGQ || virt_instr->mnemonic_t == vm::handler::SREGQ )
virt_instr_entry->setText(
3,
QString( "; vreg%1" ).arg( virt_instr->operand.imm.u ? ( virt_instr->operand.imm.u / 8 ) : 0u ) );
QVariant var;
var.setValue( virt_instr );
virt_instr_entry->setData( 3, Qt::UserRole, var );
if ( virt_instr->mnemonic_t == vm::handler::JMP )
{
if ( code_block->jcc.type == vm::instrs::jcc_type::branching )
{
virt_instr_entry->setText( 3, QString( "; { %1, %2 }" )
.arg( code_block->jcc.block_addr[ 0 ], 0, 16 )
.arg( code_block->jcc.block_addr[ 1 ], 0, 16 ) );
auto branch_entry1 = new qtree_widget_item_t(), branch_entry2 = new qtree_widget_item_t();
const auto block1_addr = code_block->jcc.block_addr[ 0 ];
const auto block2_addr = code_block->jcc.block_addr[ 1 ];
branch_entry1->setText( 0, QString( "%1" ).arg( block1_addr, 0, 16 ) );
branch_entry1->setText( 3, QString( "; block_%1" ).arg( block1_addr, 0, 16 ) );
branch_entry2->setText( 0, QString( "%1" ).arg( block2_addr, 0, 16 ) );
branch_entry2->setText( 3, QString( "; block_%1" ).arg( block2_addr, 0, 16 ) );
add_branch_children( branch_entry1, code_block->jcc.block_addr[ 0 ] );
add_branch_children( branch_entry2, code_block->jcc.block_addr[ 1 ] );
virt_instr_entry->addChildren( { branch_entry1, branch_entry2 } );
// if its a JMP with branches we want to insert the next code block
// instructions into the child widget entries...
item->addChild( virt_instr_entry );
return;
}
else
{
virt_instr_entry->setText( 3, QString( "; { %1 }" ).arg( code_block->jcc.block_addr[ 0 ], 0, 16 ) );
// else if this jmp doesnt have two branches add the next code block to it...
item->addChild( virt_instr_entry );
add_branch_children( item, code_block->jcc.block_addr[ 0 ] );
}
}
item->addChild( virt_instr_entry );
}
}
}
void qvminspector_t::update_ui()
{
// add vm handlers to the vm handler tree...
ui.virt_handlers_tree->clear();
for ( auto idx = 0u; idx < vmctx->vm_handlers.size(); ++idx )
{
auto new_handler_entry = new qtree_widget_item_t;
new_handler_entry->setData( 0, Qt::UserRole, idx );
new_handler_entry->setText( 0, QString( "%1" ).arg( idx ) );
new_handler_entry->setText(
1, QString( "%1" ).arg(
ABS_TO_IMG( vmctx->vm_handlers[ idx ].address, module_base, file_header->image_base ), 0, 16 ) );
new_handler_entry->setText( 2, vmctx->vm_handlers[ idx ].profile ? vmctx->vm_handlers[ idx ].profile->name
: "UNDEFINED" );
new_handler_entry->setText( 3, QString( "%1" ).arg( vmctx->vm_handlers[ idx ].imm_size ) );
if ( vmctx->vm_handlers[ idx ].profile && vmctx->vm_handlers[ idx ].imm_size )
new_handler_entry->setText( 4, vmctx->vm_handlers[ idx ].profile->extention ==
vm::handler::extention_t::sign_extend
? "SIGN EXTENDED"
: "ZERO EXTENDED" );
else
new_handler_entry->setText( 4, "UNDEFINED" );
ui.virt_handlers_tree->addTopLevelItem( new_handler_entry );
}
ui.virt_handlers_tree->topLevelItem( 0 )->setSelected( true );
// for each code block insert their virtual instructions
// into the virtual instruction tree... also put meta data about the code
// block above the virtual instructions... if the code block has a JCC (with two branches)
// then make a child repeating this for loop...
ui.virt_instrs->clear(); // clear old virtual instructions out since we are updating the UI...
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 )
{
code_block_addrs.push_back( code_block->vip_begin );
dbg_print( qstring_t( "> code block %1 (block_%2), number of vinstrs = %3..." )
.arg( code_block_num )
.arg( code_block->vip_begin, 0, 16 )
.arg( code_block->vinstr_count ) );
// for each virtual instruction inside of this code block...
for ( auto idx = 0u; idx < code_block->vinstr_count; ++idx )
{
const auto virt_instr = &code_block->vinstr[ idx ];
const auto profile = vm::handler::get_profile( virt_instr->mnemonic_t );
auto virt_instr_entry = new qtree_widget_item_t;
// virtual instruction image base'ed rva... (column 1)...
virt_instr_entry->setText(
0, QString( "block_%1+%2" )
.arg( code_block->vip_begin, 0, 16 )
.arg( idx ? code_block->vip_begin >
( ( virt_instr->trace_data.vip - file_header->module_base ) + image_base )
? code_block->vip_begin -
( ( virt_instr->trace_data.vip - file_header->module_base ) + image_base )
: ( ( virt_instr->trace_data.vip - file_header->module_base ) + image_base ) -
code_block->vip_begin
: 0u,
0, 16 ) );
// virtual instruction operand bytes... (column 2)...
qstring_t operand_bytes;
operand_bytes.append( QString( "%1" ).arg( virt_instr->opcode, 0, 16 ) );
// if virt instruction has an imm... grab its bytes...
if ( virt_instr->operand.has_imm )
{
operand_bytes.append( " - " );
for ( auto _idx = 0u; _idx < virt_instr->operand.imm.imm_size / 8; ++_idx )
operand_bytes.append( QString( "%1 " ).arg(
reinterpret_cast< std::uint8_t * >( &virt_instr->operand.imm.u )[ _idx ], 0, 16 ) );
}
virt_instr_entry->setText( 1, operand_bytes );
// virtual instruction string, includes imm... (colume 3)...
QString decoded_instr( QString( "%1" ).arg(
profile ? profile->name : QString( "UNK(%1)" ).arg( virt_instr->opcode, 0, 16 ) ) );
if ( virt_instr->operand.has_imm ) // if there is a second operand (imm) put its value...
decoded_instr.append( QString( " %1" ).arg( virt_instr->operand.imm.u, 0, 16 ) );
virt_instr_entry->setText( 2, decoded_instr );
// add comments to the virtual instruction... (colume 4)...
if ( virt_instr->mnemonic_t == vm::handler::LREGQ || virt_instr->mnemonic_t == vm::handler::SREGQ )
virt_instr_entry->setText(
3,
QString( "; vreg%1" ).arg( virt_instr->operand.imm.u ? ( virt_instr->operand.imm.u / 8 ) : 0u ) );
// add virt_instr_t pointer for this current virtual instruction...
QVariant var;
var.setValue( virt_instr );
virt_instr_entry->setData( 3, Qt::UserRole, var );
if ( virt_instr->mnemonic_t == vm::handler::JMP )
{
if ( code_block->jcc.type == vm::instrs::jcc_type::branching )
{
virt_instr_entry->setText( 3, QString( "; { %1, %2 }" )
.arg( code_block->jcc.block_addr[ 0 ], 0, 16 )
.arg( code_block->jcc.block_addr[ 1 ], 0, 16 ) );
auto branch_entry1 = new qtree_widget_item_t(), branch_entry2 = new qtree_widget_item_t();
const auto block1_addr = code_block->jcc.block_addr[ 0 ];
const auto block2_addr = code_block->jcc.block_addr[ 1 ];
branch_entry1->setText( 0, QString( "%1" ).arg( block1_addr, 0, 16 ) );
branch_entry1->setText( 3, QString( "; block_%1" ).arg( block1_addr, 0, 16 ) );
branch_entry2->setText( 0, QString( "%1" ).arg( block2_addr, 0, 16 ) );
branch_entry2->setText( 3, QString( "; block_%1" ).arg( block2_addr, 0, 16 ) );
add_branch_children( branch_entry1, code_block->jcc.block_addr[ 0 ] );
add_branch_children( branch_entry2, code_block->jcc.block_addr[ 1 ] );
virt_instr_entry->addChildren( { branch_entry1, branch_entry2 } );
// if its a JMP with branches we want to insert the next code block
// instructions into the child widget entries...
ui.virt_instrs->addTopLevelItem( virt_instr_entry );
goto finish; // bad code...
}
else if ( code_block->jcc.type == vm::instrs::jcc_type::absolute )
virt_instr_entry->setText( 3, QString( "; { %1 }" ).arg( code_block->jcc.block_addr[ 0 ], 0, 16 ) );
}
ui.virt_instrs->addTopLevelItem( virt_instr_entry );
}
}
finish: // bad code...
ui.virt_instrs->topLevelItem( 0 )->setSelected( true );
}

@ -1,57 +0,0 @@
#pragma once
#define NOMINMAX
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QInputDialog.h>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMessageBox.h>
#include <qpushbutton.h>
#include <qtreewidget.h>
#include <Windows.h>
#include <filesystem>
#include <fstream>
#include <vmprofiler.hpp>
#include "ui_qvminspector.h"
#include <ia32.hpp>
#include <vmprofiler.hpp>
#define ABS_TO_IMG( addr, mod_base, img_base ) ( addr - mod_base ) + img_base
Q_DECLARE_METATYPE( vm::instrs::virt_instr_t * )
using qmain_window_t = QMainWindow;
using qwidget_t = QWidget;
using qstring_t = QString;
using qfile_t = QFile;
using qtree_widget_item_t = QTreeWidgetItem;
using qmsg_box_t = QMessageBox;
class qvminspector_t : public qmain_window_t
{
friend class qvirt_instrs_t;
friend class qvirt_handlers_t;
Q_OBJECT
public:
qvminspector_t( qwidget_t *parent = Q_NULLPTR );
private slots:
void on_open();
void on_close();
private:
void dbg_print( qstring_t DbgOutput );
void dbg_msg( qstring_t DbgOutput );
void update_ui();
void add_branch_children( qtree_widget_item_t *item, std::uintptr_t branch_addr );
bool init_data();
Ui::QVMProfilerClass ui;
qstring_t file_path;
qstring_t VMProtectedFilePath;
std::uint64_t image_base, vm_entry_rva, module_base, image_size;
vm::ctx_t *vmctx;
vmp2::v3::file_header *file_header;
vmp2::v3::code_block_t *first_block;
std::vector< std::uintptr_t > code_block_addrs;
};

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1496</width> <width>1247</width>
<height>1093</height> <height>849</height>
</rect> </rect>
</property> </property>
<property name="acceptDrops"> <property name="acceptDrops">
@ -27,7 +27,7 @@
<item> <item>
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>2</number>
</property> </property>
<widget class="QWidget" name="virtual_instructions_tab"> <widget class="QWidget" name="virtual_instructions_tab">
<attribute name="title"> <attribute name="title">
@ -44,8 +44,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1452</width> <width>1203</width>
<height>854</height> <height>610</height>
</rect> </rect>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout_2"> <layout class="QHBoxLayout" name="horizontalLayout_2">
@ -414,8 +414,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>672</width> <width>556</width>
<height>228</height> <height>149</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
@ -604,6 +604,96 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="virtual_rtn_tab">
<attribute name="title">
<string>Virtual Routines</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QGroupBox" name="groupBox_11">
<property name="title">
<string>Virtual Machine Enters</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_13">
<item>
<widget class="QTreeWidget" name="virtual_machine_enters">
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Address</string>
</property>
</column>
<column>
<property name="text">
<string>Number Of Nodes</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_12">
<property name="title">
<string>Virtual Machine Enter Information</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
<item>
<widget class="QGroupBox" name="groupBox_13">
<property name="title">
<string>Virtual Machine - Enter Instructions</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_14">
<item>
<widget class="QTreeWidget" name="virtual_machine_enter_instrs">
<column>
<property name="text">
<string>Address</string>
</property>
</column>
<column>
<property name="text">
<string>Instruction</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_14">
<property name="title">
<string>Virtual Machine - Calc Jmp</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_15">
<item>
<widget class="QTreeWidget" name="virtual_machine_enter_calc_jmp">
<column>
<property name="text">
<string>Address</string>
</property>
</column>
<column>
<property name="text">
<string>Instruction</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -613,7 +703,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1496</width> <width>1247</width>
<height>21</height> <height>21</height>
</rect> </rect>
</property> </property>

Loading…
Cancel
Save