Merge branch 'dev' into 'master'

added support for virtual jcc's as well as virtual loops

See merge request vmp2/vmprofiler-qt!1
merge-requests/2/merge
_xeroxz 3 years ago
commit bb0129fbfb

@ -1 +1 @@
Subproject commit e58c23c40e13528f5d9b84feb7e23b62a886ed5a
Subproject commit e95ef2537184639e89a4dbbd38355a11ffc46bac

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

@ -1,300 +0,0 @@
#include "QVMProfiler.h"
QVMProfiler::QVMProfiler( QWidget *parent ) : QMainWindow( parent ), TraceFileBlob( nullptr ), VMCtx( nullptr )
{
ui.setupUi( this );
}
void QVMProfiler::on_actionCloseProgram_triggered()
{
exit( 0 );
}
void QVMProfiler::on_actionOpen_VMTrace_triggered()
{
if ( TraceFileBlob && VMCtx )
{
free( TraceFileBlob );
TraceFileBlob = nullptr;
TraceFileHeader = nullptr;
TraceEntryList = nullptr;
VMHandlerTable = nullptr;
ImageBase = NULL;
VMEntryRva = NULL;
ModuleBase = NULL;
VMHandlers.clear();
VMEntry.clear();
delete VMCtx;
}
TraceFilePath = QFileDialog::getOpenFileName( this, tr( "Open Trace" ), "C:\\", tr( "VMTrace Files (*.vmp2)" ) );
if ( TraceFilePath.isEmpty() )
{
DbgMessage( "Invalid Trace File... No File Selected..." );
return;
}
if ( !std::filesystem::exists( TraceFilePath.toStdString().c_str() ) )
{
DbgMessage( "Trace File Doesnt Exist..." );
return;
}
const auto TraceFileSize = std::filesystem::file_size( TraceFilePath.toStdString().c_str() );
if ( !TraceFileSize )
{
DbgMessage( "Invalid Trace File Size..." );
return;
}
QFile File( TraceFilePath );
TraceFileBlob = malloc( TraceFileSize );
DbgMessage( QString( "Loading Trace File %1..." ).arg( TraceFilePath ) );
if(!File.open( QIODevice::ReadOnly ))
{
DbgMessage( "Failed To Open Trace File..." );
return;
}
memcpy( TraceFileBlob, File.readAll().data(), TraceFileSize );
if ( !InitTraceData() )
{
DbgMessage( "Failed To Init Trace Data..." );
return;
}
UpdateUI();
}
void QVMProfiler::DbgPrint( QString DbgOutput )
{
ui.DbgOutputWindow->appendPlainText( DbgOutput );
}
void QVMProfiler::DbgMessage( QString DbgOutput )
{
QMessageBox MsgBox;
MsgBox.setText( DbgOutput );
MsgBox.exec();
DbgPrint( DbgOutput );
}
bool QVMProfiler::InitTraceData()
{
TraceFileHeader = reinterpret_cast< vmp2::v2::file_header * >( TraceFileBlob );
TraceEntryList = reinterpret_cast< vmp2::v2::entry_t * >( reinterpret_cast< std::uintptr_t >( TraceFileBlob ) +
TraceFileHeader->entry_offset );
const auto TraceMagicBytes = &TraceFileHeader->magic;
if ( memcmp( TraceMagicBytes, "VMP2", sizeof "VMP2" - 1 ) != 0 )
{
DbgMessage( "Trace File Magic Bytes Are Invalid...\n" );
return false;
}
VMEntryRva = TraceFileHeader->vm_entry_rva;
ImageBase = TraceFileHeader->image_base;
ModuleBase = reinterpret_cast< std::uintptr_t >( TraceFileHeader ) + TraceFileHeader->module_offset;
DbgPrint( "Trace File Magic Bytes Are Valid...." );
if ( !vm::util::flatten( VMEntry, VMEntryRva + ModuleBase ) )
{
DbgMessage( "Failed To Flatten VMEntry...\n" );
return false;
}
vm::util::deobfuscate( VMEntry );
DbgPrint( "Flattened VMEntry..." );
DbgPrint( "Deobfuscated VMEntry..." );
char buffer[ 256 ];
ZydisFormatter formatter;
ZydisFormatterInit( &formatter, ZYDIS_FORMATTER_STYLE_INTEL );
for ( auto &Instr : VMEntry )
{
ZydisFormatterFormatInstruction( &formatter, &Instr.instr, buffer, sizeof( buffer ),
( Instr.addr - TraceFileHeader->module_base ) + ImageBase );
DbgPrint( QString( "> %1 %2" )
.arg( QString::number( ( Instr.addr - TraceFileHeader->module_base ) + ImageBase, 16 ) )
.arg( buffer ) );
}
VMHandlerTable = vm::handler::table::get( VMEntry );
if ( !vm::handler::get_all( ModuleBase, ImageBase, VMEntry, VMHandlerTable, VMHandlers ) )
{
DbgMessage( "Failed To Get All VM Handler Meta Data...\n" );
return false;
}
DbgPrint( "Located All VM Handlers..." );
VMCtx = new vm::vmctx_t( TraceFileHeader, TraceEntryList, VMHandlers, ModuleBase, ImageBase );
return true;
}
void QVMProfiler::UpdateUI()
{
ui.VirtualInstructionTree->clear();
for ( auto [ VirtInstr, TraceEntry ] = VMCtx->step(); TraceEntry && !VirtInstr.empty();
std::tie( VirtInstr, TraceEntry ) = VMCtx->step() )
{
auto InstructionTraceData = new QTreeWidgetItem();
InstructionTraceData->setText(
0, QString::number( ( TraceEntry->vip - TraceFileHeader->module_base ) + ImageBase, 16 ) );
if ( VMHandlers[ TraceEntry->handler_idx ].imm_size )
{
QString SecondOperandBytes;
auto numByteOperand = VMHandlers[ TraceEntry->handler_idx ].imm_size / 8;
auto spaceIdx = VirtInstr.find( " " ) + 1;
auto ImmValue =
QString( VirtInstr.substr( spaceIdx, VirtInstr.size() - spaceIdx ).c_str() ).toULongLong( nullptr, 16 );
for ( auto idx = 0u; idx < numByteOperand; ++idx )
{
SecondOperandBytes.append(
QString::number( *( reinterpret_cast< std::uint8_t * >( &ImmValue ) + idx ), 16 ) );
SecondOperandBytes.append( " " );
}
InstructionTraceData->setText(
1, QString::number( TraceEntry->handler_idx, 16 ).append( " - " ).append( SecondOperandBytes ) );
}
else
{
// else we just put the first operand byte (vm handler index)...
InstructionTraceData->setText( 1, QString::number( TraceEntry->handler_idx, 16 ) );
}
InstructionTraceData->setText( 2, VirtInstr.c_str() );
ui.VirtualInstructionTree->addTopLevelItem( InstructionTraceData );
}
ui.VirtualInstructionTree->topLevelItem( 0 )->setSelected( true );
}
void QVMProfiler::on_VirtualInstructionTree_itemSelectionChanged()
{
auto SelectedItem = ui.VirtualInstructionTree->selectedItems()[ 0 ];
auto VIPAddr = SelectedItem->data( 0, 0 ).toString().toULongLong( nullptr, 16 );
vmp2::v2::entry_t *Entry = nullptr;
for ( auto idx = 0u; idx < TraceFileHeader->entry_count; ++idx )
{
if ( ( TraceEntryList[ idx ].vip - TraceFileHeader->module_base ) + ImageBase == VIPAddr )
{
Entry = &TraceEntryList[ idx ];
break;
}
}
ui.VirtualRegisterTree->topLevelItem( 0 )->setText(
1, QString::number( ( Entry->vip - TraceFileHeader->module_base ) + ImageBase, 16 ) );
ui.VirtualRegisterTree->topLevelItem( 1 )->setText( 1, QString::number( Entry->regs.rbp, 16 ) );
ui.VirtualRegisterTree->topLevelItem( 2 )->setText( 1, QString::number( Entry->decrypt_key, 16 ) );
for ( auto idx = 4; idx < 28; ++idx )
ui.VirtualRegisterTree->topLevelItem( idx )->setText( 1, QString::number( Entry->vregs.qword[ idx - 4 ], 16 ) );
for ( auto idx = 0u; idx < 15; ++idx )
ui.NativeRegisterTree->topLevelItem( idx )->setText( 1, QString::number( Entry->regs.raw[ idx ], 16 ) );
ui.NativeRegisterTree->topLevelItem( 16 )->setText( 1, QString::number( Entry->regs.rflags, 16 ) );
rflags flags;
flags.flags = Entry->regs.rflags;
ui.NativeRegisterTree->topLevelItem( 16 )->child( 0 )->setText( 1, QString::number( flags.zero_flag ) );
ui.NativeRegisterTree->topLevelItem( 16 )->child( 1 )->setText( 1, QString::number( flags.parity_flag ) );
ui.NativeRegisterTree->topLevelItem( 16 )->child( 2 )->setText( 1, QString::number( flags.auxiliary_carry_flag ) );
ui.NativeRegisterTree->topLevelItem( 16 )->child( 3 )->setText( 1, QString::number( flags.overflow_flag ) );
ui.NativeRegisterTree->topLevelItem( 16 )->child( 4 )->setText( 1, QString::number( flags.sign_flag ) );
ui.NativeRegisterTree->topLevelItem( 16 )->child( 5 )->setText( 1, QString::number( flags.direction_flag ) );
ui.NativeRegisterTree->topLevelItem( 16 )->child( 6 )->setText( 1, QString::number( flags.carry_flag ) );
ui.NativeRegisterTree->topLevelItem( 16 )->child( 7 )->setText( 1, QString::number( flags.trap_flag ) );
ui.NativeRegisterTree->topLevelItem( 16 )->child( 8 )->setText( 1, QString::number( flags.interrupt_enable_flag ) );
ui.VirtualStackTree->clear();
for ( auto idx = 0u; idx < sizeof( Entry->vsp ) / 8; ++idx )
{
auto newEntry = new QTreeWidgetItem();
newEntry->setText( 0, QString::number( Entry->regs.rbp - ( idx * 8 ), 16 ) );
newEntry->setText( 1, QString::number( Entry->vsp.qword[ idx ], 16 ) );
ui.VirtualStackTree->addTopLevelItem( newEntry );
}
ui.VMHandlerInstructionsTree->clear();
auto InstrVec = &VMHandlers[ Entry->handler_idx ].instrs;
char buffer[ 256 ];
ZydisFormatter formatter;
ZydisFormatterInit( &formatter, ZYDIS_FORMATTER_STYLE_INTEL );
for ( auto idx = 0u; idx < InstrVec->size(); ++idx )
{
auto newEntry = new QTreeWidgetItem();
newEntry->setText( 0, QString::number( ( InstrVec->at( idx ).addr - ModuleBase ) + ImageBase, 16 ) );
ZydisFormatterFormatInstruction( &formatter, &InstrVec->at( idx ).instr, buffer, sizeof( buffer ),
( InstrVec->at( idx ).addr - ModuleBase ) + ImageBase );
newEntry->setText( 1, buffer );
ui.VMHandlerInstructionsTree->addTopLevelItem( newEntry );
}
ui.VMHandlerTransformationsTree->clear();
auto HandlerTransforms = &VMHandlers[ Entry->handler_idx ].transforms;
for ( auto [ TransformType, TransformInstr ] : *HandlerTransforms )
{
if ( TransformType == vm::transform::type::generic0 && TransformInstr.mnemonic == ZYDIS_MNEMONIC_INVALID )
continue;
auto newEntry = new QTreeWidgetItem();
switch ( TransformType )
{
case vm::transform::type::rolling_key:
{
newEntry->setText( 0, "Key Transform" );
break;
}
case vm::transform::type::generic0:
case vm::transform::type::generic1:
case vm::transform::type::generic2:
case vm::transform::type::generic3:
{
newEntry->setText( 0, "Generic" );
break;
}
case vm::transform::type::update_key:
{
newEntry->setText( 0, "Update Key" );
break;
}
default:
throw std::invalid_argument( "invalid transformation type..." );
}
ZydisFormatterFormatInstruction( &formatter, &TransformInstr, buffer, sizeof( buffer ), NULL );
newEntry->setText( 1, buffer );
ui.VMHandlerTransformationsTree->addTopLevelItem( newEntry );
}
}

@ -1,48 +0,0 @@
#pragma once
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QInputDialog.h>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMessageBox.h>
#include <Windows.h>
#include <filesystem>
#include <fstream>
#include <vmprofiler.hpp>
#include "ia32.hpp"
#include "ui_QVMProfiler.h"
#include "vmctx.h"
#include "vmp2.hpp"
class QVMProfiler : public QMainWindow
{
Q_OBJECT
public:
QVMProfiler( QWidget *parent = Q_NULLPTR );
private slots:
void on_actionOpen_VMTrace_triggered();
void on_actionCloseProgram_triggered();
void on_VirtualInstructionTree_itemSelectionChanged();
private:
void DbgPrint( QString DbgOutput );
void DbgMessage( QString DbgOutput );
void UpdateUI();
bool InitTraceData();
Ui::QVMProfilerClass ui;
QString TraceFilePath;
QString VMProtectedFilePath;
std::uint64_t ImageBase, VMEntryRva, ModuleBase;
std::vector< vm::handler::handler_t > VMHandlers;
zydis_routine_t VMEntry;
std::uintptr_t *VMHandlerTable;
vm::vmctx_t *VMCtx;
void *TraceFileBlob;
vmp2::v2::file_header *TraceFileHeader;
vmp2::v2::entry_t *TraceEntryList;
};

@ -1,8 +1,9 @@
#include <QtWidgets/QApplication>
#include <QFile>
#include <QTextStream>
#include "QVMProfiler.h"
#include "qvminspector.h"
#include "qvirt_instrs.h"
#include "framelesswindow.h"
#include "DarkStyle.h"
@ -10,9 +11,12 @@ int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QApplication::setStyle(new DarkStyle);
FramelessWindow framelessWindow;
framelessWindow.setContent(new QVMProfiler);
framelessWindow.setWindowIcon(QIcon("icon.ico"));
framelessWindow.show();
FramelessWindow frameless_window;
const auto window = new qvminspector_t;
qvirt_instrs_t virt_instr( window );
frameless_window.setContent( window );
frameless_window.setWindowIcon(QIcon("icon.ico"));
frameless_window.show();
return app.exec();
}

@ -0,0 +1,146 @@
#include "qvirt_instrs.h"
qvirt_instrs_t::qvirt_instrs_t( qvminspector_t *vminspector ) : vminspector( vminspector ), ui( &vminspector->ui )
{
connect( ui->virt_instrs, &QTreeWidget::itemSelectionChanged, this, &qvirt_instrs_t::on_select );
}
void qvirt_instrs_t::on_select()
{
auto item = ui->virt_instrs->selectedItems()[ 0 ];
const auto virt_instr = item->data( 3, Qt::UserRole ).value< vm::instrs::virt_instr_t * >();
if ( !virt_instr )
return;
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 )
{
const auto &trace_data = virt_instr->trace_data;
// set native register values....
for ( auto idx = 0u; idx < 15; ++idx )
ui->native_regs->topLevelItem( idx )->setText( 1, QString::number( trace_data.regs.raw[ idx ], 16 ) );
// set RFLAGs and its bits...
ui->native_regs->topLevelItem( 16 )->setText( 1, QString::number( trace_data.regs.rflags, 16 ) );
rflags flags;
flags.flags = trace_data.regs.rflags;
// could probably use a for loop here and shift bits around maybe...
ui->native_regs->topLevelItem( 16 )->child( 0 )->setText( 1, QString::number( flags.zero_flag ) );
ui->native_regs->topLevelItem( 16 )->child( 1 )->setText( 1, QString::number( flags.parity_flag ) );
ui->native_regs->topLevelItem( 16 )->child( 2 )->setText( 1, QString::number( flags.auxiliary_carry_flag ) );
ui->native_regs->topLevelItem( 16 )->child( 3 )->setText( 1, QString::number( flags.overflow_flag ) );
ui->native_regs->topLevelItem( 16 )->child( 4 )->setText( 1, QString::number( flags.sign_flag ) );
ui->native_regs->topLevelItem( 16 )->child( 5 )->setText( 1, QString::number( flags.direction_flag ) );
ui->native_regs->topLevelItem( 16 )->child( 6 )->setText( 1, QString::number( flags.carry_flag ) );
ui->native_regs->topLevelItem( 16 )->child( 7 )->setText( 1, QString::number( flags.trap_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 )
{
const auto &trace_data = virt_instr->trace_data;
// set VIP in virtual registers window...
ui->virt_regs->topLevelItem( 0 )->setText(
1,
QString::number(
( trace_data.vip - vminspector->file_header->module_base ) + vminspector->file_header->image_base, 16 ) );
// set VSP in virtual registers window...
ui->virt_regs->topLevelItem( 1 )->setText( 1, QString::number( trace_data.regs.rbp, 16 ) );
// set decrypt key in virtual registers window...
ui->virt_regs->topLevelItem( 2 )->setText( 1, QString::number( trace_data.regs.rdx, 16 ) );
// set the virtual register values....
for ( auto idx = 4u; idx < 28; ++idx )
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 )
{
ui->virt_stack->clear();
const auto &trace_data = virt_instr->trace_data;
for ( auto idx = 0u; idx < sizeof( trace_data.vsp ) / 8; ++idx )
{
auto new_stack_entry = new qtree_widget_item_t();
new_stack_entry->setText( 0, qstring_t::number( trace_data.regs.rbp + ( idx * 8 ), 16 ) );
new_stack_entry->setText( 1, qstring_t::number( trace_data.vsp.qword[ idx ], 16 ) );
ui->virt_stack->addTopLevelItem( new_stack_entry );
}
}
void qvirt_instrs_t::update_vm_handler_info( vm::instrs::virt_instr_t *virt_instr )
{
char buffer[ 256 ];
ZydisFormatter formatter;
ZydisFormatterInit( &formatter, ZYDIS_FORMATTER_STYLE_INTEL );
ui->vm_handler_instrs->clear();
const auto &vm_handler_instrs = vminspector->vmctx->vm_handlers[ virt_instr->opcode ].instrs;
// display vm handler instructions...
for ( const auto &instr : vm_handler_instrs )
{
auto new_instr = new qtree_widget_item_t();
new_instr->setText(
0, qstring_t::number( ( instr.addr - vminspector->module_base ) + vminspector->image_base, 16 ) );
ZydisFormatterFormatInstruction( &formatter, &instr.instr, buffer, sizeof( buffer ),
( instr.addr - vminspector->module_base ) + vminspector->image_base );
new_instr->setText( 1, buffer );
ui->vm_handler_instrs->addTopLevelItem( new_instr );
}
// display vm handler transformations...
ui->vm_handler_transforms->clear();
const auto &vm_handler_transforms = vminspector->vmctx->vm_handlers[ virt_instr->opcode ].transforms;
for ( auto [ transform_type, transform_instr ] : vm_handler_transforms )
{
if ( transform_type == vm::transform::type::generic0 && transform_instr.mnemonic == ZYDIS_MNEMONIC_INVALID )
continue;
auto new_transform_entry = new qtree_widget_item_t();
switch ( transform_type )
{
case vm::transform::type::rolling_key:
{
new_transform_entry->setText( 0, "Key Transform" );
break;
}
case vm::transform::type::generic0:
case vm::transform::type::generic1:
case vm::transform::type::generic2:
case vm::transform::type::generic3:
{
new_transform_entry->setText( 0, "Generic" );
break;
}
case vm::transform::type::update_key:
{
new_transform_entry->setText( 0, "Update Key" );
break;
}
default:
throw std::invalid_argument( "invalid transformation type..." );
}
ZydisFormatterFormatInstruction( &formatter, &transform_instr, buffer, sizeof( buffer ), NULL );
new_transform_entry->setText( 1, buffer );
ui->vm_handler_transforms->addTopLevelItem( new_transform_entry );
}
}

@ -0,0 +1,20 @@
#pragma once
#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();
};

@ -0,0 +1,368 @@
#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;
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 ) );
dbg_msg( qstring_t( "loading vmp2 file %1..." ).arg( file_path ) );
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;
module_base = reinterpret_cast< std::uintptr_t >( file_header ) + file_header->module_offset;
vmctx = new vm::ctx_t( module_base, image_base, image_base, 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 )
{
// skip if this code block begin address is not branch_addr...
if ( code_block->vip_begin != branch_addr && code_block->vip_begin != branch_addr - 2 &&
code_block->vip_begin != branch_addr - 1 )
continue;
dbg_print( qstring_t( "> code block %1 (block_%2), number of vinstrs = %3..." )
.arg( code_block_num )
.arg( ( code_block->vip_begin - file_header->module_base ) + file_header->image_base, 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 - file_header->module_base ) + file_header->image_base, 0, 16 )
.arg( idx ? code_block->vip_begin > virt_instr->trace_data.vip
? code_block->vip_begin - virt_instr->trace_data.vip
: virt_instr->trace_data.vip - 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 ) -
1 /* zero based vreg... */
: 0u ) );
QVariant var;
var.setValue( virt_instr );
virt_instr_entry->setData( 3, Qt::UserRole, var );
if ( virt_instr->mnemonic_t == vm::handler::JMP && code_block->jcc.has_jcc )
{
virt_instr_entry->setText(
3,
QString( "; { %1, %2 }" )
.arg( ( code_block->jcc.block_addr[ 0 ] - file_header->module_base ) + file_header->image_base,
0, 16 )
.arg( ( code_block->jcc.block_addr[ 1 ] - file_header->module_base ) + file_header->image_base,
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 ] - file_header->module_base ) + file_header->image_base;
const auto block2_addr =
( code_block->jcc.block_addr[ 1 ] - file_header->module_base ) + file_header->image_base;
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 if ( virt_instr->mnemonic_t == vm::handler::JMP )
{
virt_instr_entry->setText( 3, QString( "; { %1 }" )
.arg( ( code_block->jcc.block_addr[ 0 ] - file_header->module_base ) +
file_header->image_base,
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()
{
// 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 - file_header->module_base ) + file_header->image_base );
dbg_print( qstring_t( "> code block %1 (block_%2), number of vinstrs = %3..." )
.arg( code_block_num )
.arg( ( code_block->vip_begin - file_header->module_base ) + file_header->image_base, 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 - file_header->module_base ) + file_header->image_base, 0, 16 )
.arg( idx ? code_block->vip_begin > virt_instr->trace_data.vip
? code_block->vip_begin - virt_instr->trace_data.vip
: virt_instr->trace_data.vip - 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 && code_block->jcc.has_jcc )
{
virt_instr_entry->setText(
3,
QString( "; { %1, %2 }" )
.arg( ( code_block->jcc.block_addr[ 0 ] - file_header->module_base ) + file_header->image_base,
0, 16 )
.arg( ( code_block->jcc.block_addr[ 1 ] - file_header->module_base ) + file_header->image_base,
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 ] - file_header->module_base ) + file_header->image_base;
const auto block2_addr =
( code_block->jcc.block_addr[ 1 ] - file_header->module_base ) + file_header->image_base;
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 ( virt_instr->mnemonic_t == vm::handler::JMP )
virt_instr_entry->setText( 3, QString( "; { %1 }" )
.arg( ( code_block->jcc.block_addr[ 0 ] - file_header->module_base ) +
file_header->image_base,
0, 16 ) );
ui.virt_instrs->addTopLevelItem( virt_instr_entry );
}
}
finish: // bad code...
ui.virt_instrs->topLevelItem( 0 )->setSelected( true );
}

@ -0,0 +1,51 @@
#pragma once
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QInputDialog.h>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMessageBox.h>
#include <Windows.h>
#include <filesystem>
#include <fstream>
#include <vmprofiler.hpp>
#include "ia32.hpp"
#include "ui_qvminspector.h"
#include "vmp2.hpp"
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;
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;
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,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>1220</width>
<width>1606</width>
<height>1038</height>
</rect>
</property>
@ -49,15 +49,15 @@
<rect>
<x>0</x>
<y>0</y>
<width>556</width>
<width>759</width>
<height>777</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QTreeWidget" name="VirtualInstructionTree">
<widget class="QTreeWidget" name="virt_instrs">
<attribute name="headerDefaultSectionSize">
<number>120</number>
<number>160</number>
</attribute>
<column>
<property name="text">
@ -74,6 +74,11 @@
<string>Virtual Instruction</string>
</property>
</column>
<column>
<property name="text">
<string>Comments</string>
</property>
</column>
</widget>
</item>
</layout>
@ -96,7 +101,7 @@
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QTreeWidget" name="VirtualRegisterTree">
<widget class="QTreeWidget" name="virt_regs">
<attribute name="headerDefaultSectionSize">
<number>65</number>
</attribute>
@ -256,7 +261,7 @@
</widget>
</item>
<item>
<widget class="QTreeWidget" name="NativeRegisterTree">
<widget class="QTreeWidget" name="native_regs">
<attribute name="headerDefaultSectionSize">
<number>70</number>
</attribute>
@ -421,13 +426,13 @@
<rect>
<x>0</x>
<y>0</y>
<width>556</width>
<width>739</width>
<height>225</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QTreeWidget" name="VirtualStackTree">
<widget class="QTreeWidget" name="virt_stack">
<column>
<property name="text">
<string>Stack Pointer</string>
@ -460,7 +465,7 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QTreeWidget" name="VMHandlerInstructionsTree">
<widget class="QTreeWidget" name="vm_handler_instrs">
<column>
<property name="text">
<string>Address</string>
@ -483,7 +488,7 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_9">
<item>
<widget class="QTreeWidget" name="VMHandlerTransformationsTree">
<widget class="QTreeWidget" name="vm_handler_transforms">
<column>
<property name="text">
<string>Address</string>
@ -515,7 +520,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>1220</width>
<width>1606</width>
<height>21</height>
</rect>
</property>
@ -523,8 +528,8 @@
<property name="title">
<string>File</string>
</property>
<addaction name="actionOpen_VMTrace"/>
<addaction name="actionCloseProgram"/>
<addaction name="action_open"/>
<addaction name="action_close"/>
</widget>
<addaction name="menuOpen_VMTrace"/>
</widget>
@ -556,7 +561,7 @@
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QPlainTextEdit" name="DbgOutputWindow">
<widget class="QPlainTextEdit" name="dbg_output_window">
<property name="readOnly">
<bool>true</bool>
</property>
@ -575,12 +580,12 @@
<string>Close</string>
</property>
</action>
<action name="actionOpen_VMTrace">
<action name="action_open">
<property name="text">
<string>Open Trace</string>
</property>
</action>
<action name="actionCloseProgram">
<action name="action_close">
<property name="text">
<string>Close</string>
</property>
@ -588,7 +593,7 @@
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>
<include location="QVMProfiler.qrc"/>
<include location="qvminspector.qrc"/>
</resources>
<connections/>
</ui>

@ -1,87 +0,0 @@
#include "vmctx.h"
namespace vm
{
vmctx_t::vmctx_t( vmp2::v2::file_header *file_header, vmp2::v2::entry_t *entry_list,
std::vector< vm::handler::handler_t > &vm_handlers, std::uintptr_t module_base, std::uintptr_t image_base )
: module_base( module_base ), image_base( image_base ), entry_list( entry_list ), file_header( file_header ),
vm_handlers( vm_handlers ), idx( 0 )
{}
std::pair< std::string, const vmp2::v2::entry_t * > vmctx_t::step() const
{
if ( idx >= file_header->entry_count )
return {};
auto vm_handler = vm_handlers[ entry_list[ idx ].handler_idx ];
if ( vm_handler.imm_size )
{
const auto operand = get_imm( file_header->advancement, entry_list[ idx ].vip, vm_handler.imm_size / 8 );
auto [ decrypted_operand, rolling_key ] =
vm::instrs::decrypt_operand( vm_handler.transforms, operand, entry_list[ idx ].decrypt_key );
if ( vm_handler.profile )
{
if ( vm_handler.profile->extention == vm::handler::extention_t::sign_extend )
{
switch ( vm_handler.imm_size )
{
case 8:
if ( ( u8 )( decrypted_operand >> 7 ) )
decrypted_operand += ~0xFFull;
break;
case 16:
if ( ( u16 )( decrypted_operand >> 15 ) )
decrypted_operand += ~0xFFFFull;
break;
case 32:
if ( ( u32 )( decrypted_operand >> 31 ) )
decrypted_operand += ~0xFFFFFFFFull;
break;
default:
throw std::invalid_argument( "invalid imm size for sign extention...\n" );
}
}
}
char buff[ 256 ];
if ( vm_handler.profile )
snprintf( buff, sizeof buff, "%s 0x%p", vm_handler.profile->name, decrypted_operand );
else
snprintf( buff, sizeof buff, "UNK(%d) 0x%p", entry_list[ idx ].handler_idx, decrypted_operand );
return { buff, &entry_list[ idx++ ] };
}
if ( vm_handler.profile )
return { vm_handler.profile->name, &entry_list[ idx++ ] };
char buff[ 256 ];
snprintf( buff, sizeof buff, "UNK(%d)", entry_list[ idx ].handler_idx );
return { buff, &entry_list[ idx++ ] };
}
std::uintptr_t vmctx_t::get_imm( vmp2::exec_type_t exec_type_t, std::uint32_t vip_offset,
std::uint8_t imm_size ) const
{
std::uintptr_t operand = 0u;
if ( file_header->advancement == vmp2::exec_type_t::forward )
{
const auto operand_ptr =
reinterpret_cast< void * >( ( entry_list[ idx ].vip - file_header->module_base ) + module_base );
memcpy( &operand, operand_ptr, imm_size );
}
else
{
const auto operand_ptr = reinterpret_cast< void * >(
( ( entry_list[ idx ].vip - file_header->module_base ) + module_base ) - imm_size );
memcpy( &operand, operand_ptr, imm_size );
}
return operand;
}
} // namespace vm

@ -1,24 +0,0 @@
#pragma once
#include <vmprofiler.hpp>
namespace vm
{
class vmctx_t
{
public:
explicit vmctx_t( vmp2::v2::file_header *file_header, vmp2::v2::entry_t *entry_list,
std::vector< vm::handler::handler_t > &vm_handlers, std::uintptr_t module_base,
std::uintptr_t image_base );
std::pair< std::string, const vmp2::v2::entry_t * > step() const;
private:
std::uintptr_t get_imm( vmp2::exec_type_t exec_type_t, std::uint32_t vip_offset, std::uint8_t imm_size ) const;
mutable std::uint32_t idx;
const std::uintptr_t image_base, module_base;
const vmp2::v2::entry_t *entry_list;
const vmp2::v2::file_header *file_header;
std::vector< vm::handler::handler_t > vm_handlers;
};
} // namespace vm

@ -103,21 +103,20 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="vmctx.cpp" />
<ClCompile Include="qvirt_instrs.cpp" />
<ClCompile Include="qvminspector.cpp" />
<QtRcc Include="DarkStyle\darkstyle.qrc" />
<QtRcc Include="DarkStyle\framelesswindow.qrc" />
<QtRcc Include="QVMProfiler.qrc" />
<QtRcc Include="qvminspector.qrc" />
<QtUic Include="DarkStyle\framelesswindow\framelesswindow.ui" />
<QtUic Include="QVMProfiler.ui" />
<QtMoc Include="QVMProfiler.h" />
<QtUic Include="qvminspector.ui" />
<QtMoc Include="qvminspector.h" />
<ClCompile Include="DarkStyle\DarkStyle.cpp" />
<ClCompile Include="DarkStyle\framelesswindow\framelesswindow.cpp" />
<ClCompile Include="DarkStyle\framelesswindow\windowdragger.cpp" />
<ClCompile Include="QVMProfiler.cpp" />
<ClCompile Include="main.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="vmctx.h" />
<QtMoc Include="DarkStyle\DarkStyle.h" />
<QtMoc Include="DarkStyle\framelesswindow\windowdragger.h" />
<QtMoc Include="DarkStyle\framelesswindow\framelesswindow.h" />
@ -170,6 +169,7 @@
<ClInclude Include="..\dependencies\vmprofiler\include\vmp2.hpp" />
<ClInclude Include="..\dependencies\vmprofiler\include\vmprofiler.hpp" />
<ClInclude Include="..\dependencies\vmprofiler\include\vmutils.h" />
<QtMoc Include="qvirt_instrs.h" />
<ClInclude Include="ia32.hpp" />
</ItemGroup>
<ItemGroup>

@ -1,9 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="QVMProfiler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -16,7 +13,10 @@
<ClCompile Include="DarkStyle\DarkStyle.cpp">
<Filter>Source Files\DarkStyle</Filter>
</ClCompile>
<ClCompile Include="vmctx.cpp">
<ClCompile Include="qvminspector.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="qvirt_instrs.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
@ -62,28 +62,25 @@
</Filter>
</ItemGroup>
<ItemGroup>
<QtRcc Include="QVMProfiler.qrc">
<Filter>Resource Files</Filter>
</QtRcc>
<QtRcc Include="DarkStyle\darkstyle.qrc">
<Filter>Resource Files</Filter>
</QtRcc>
<QtRcc Include="DarkStyle\framelesswindow.qrc">
<Filter>Resource Files</Filter>
</QtRcc>
<QtRcc Include="qvminspector.qrc">
<Filter>Resource Files</Filter>
</QtRcc>
</ItemGroup>
<ItemGroup>
<QtUic Include="QVMProfiler.ui">
<QtUic Include="DarkStyle\framelesswindow\framelesswindow.ui">
<Filter>Resource Files</Filter>
</QtUic>
<QtUic Include="DarkStyle\framelesswindow\framelesswindow.ui">
<QtUic Include="qvminspector.ui">
<Filter>Resource Files</Filter>
</QtUic>
</ItemGroup>
<ItemGroup>
<QtMoc Include="QVMProfiler.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="DarkStyle\framelesswindow\framelesswindow.h">
<Filter>Header Files\DarkStyle\framelesswindow</Filter>
</QtMoc>
@ -93,6 +90,12 @@
<QtMoc Include="DarkStyle\DarkStyle.h">
<Filter>Header Files\DarkStyle</Filter>
</QtMoc>
<QtMoc Include="qvminspector.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="qvirt_instrs.h">
<Filter>Header Files</Filter>
</QtMoc>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ia32.hpp">
@ -245,9 +248,6 @@
<ClInclude Include="..\dependencies\vmprofiler\dependencies\zydis\msvc\ZydisExportConfig.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vmctx.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="icon.ico">

Loading…
Cancel
Save