diff --git a/dependencies/vmprofiler b/dependencies/vmprofiler
index e58c23c..e95ef25 160000
--- a/dependencies/vmprofiler
+++ b/dependencies/vmprofiler
@@ -1 +1 @@
-Subproject commit e58c23c40e13528f5d9b84feb7e23b62a886ed5a
+Subproject commit e95ef2537184639e89a4dbbd38355a11ffc46bac
diff --git a/src/DarkStyle/framelesswindow/framelesswindow.ui b/src/DarkStyle/framelesswindow/framelesswindow.ui
index 2f413dc..7bb5ecd 100644
--- a/src/DarkStyle/framelesswindow/framelesswindow.ui
+++ b/src/DarkStyle/framelesswindow/framelesswindow.ui
@@ -6,8 +6,8 @@
0
0
- 1479
- 1099
+ 1737
+ 1157
@@ -143,7 +143,7 @@
color:rgb(153,153,153);
- VMProtect 2 - Virtual Instruction Inspector (v1.5)
+ VMProtect 2 - Virtual Instruction Inspector (v1.6 BETA)
Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
diff --git a/src/QVMProfiler.cpp b/src/QVMProfiler.cpp
deleted file mode 100644
index e62ad96..0000000
--- a/src/QVMProfiler.cpp
+++ /dev/null
@@ -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 );
- }
-}
\ No newline at end of file
diff --git a/src/QVMProfiler.h b/src/QVMProfiler.h
deleted file mode 100644
index 61f7085..0000000
--- a/src/QVMProfiler.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#pragma once
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#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;
-};
diff --git a/src/main.cpp b/src/main.cpp
index 085f993..1c4cbbc 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,8 +1,9 @@
#include
#include
#include
-#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();
}
\ No newline at end of file
diff --git a/src/qvirt_instrs.cpp b/src/qvirt_instrs.cpp
new file mode 100644
index 0000000..caa1a93
--- /dev/null
+++ b/src/qvirt_instrs.cpp
@@ -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 );
+ }
+}
\ No newline at end of file
diff --git a/src/qvirt_instrs.h b/src/qvirt_instrs.h
new file mode 100644
index 0000000..3d4a83d
--- /dev/null
+++ b/src/qvirt_instrs.h
@@ -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();
+};
\ No newline at end of file
diff --git a/src/qvminspector.cpp b/src/qvminspector.cpp
new file mode 100644
index 0000000..85d9228
--- /dev/null
+++ b/src/qvminspector.cpp
@@ -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 );
+}
\ No newline at end of file
diff --git a/src/qvminspector.h b/src/qvminspector.h
new file mode 100644
index 0000000..2a1133b
--- /dev/null
+++ b/src/qvminspector.h
@@ -0,0 +1,51 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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;
+};
\ No newline at end of file
diff --git a/src/QVMProfiler.qrc b/src/qvminspector.qrc
similarity index 100%
rename from src/QVMProfiler.qrc
rename to src/qvminspector.qrc
diff --git a/src/QVMProfiler.ui b/src/qvminspector.ui
similarity index 94%
rename from src/QVMProfiler.ui
rename to src/qvminspector.ui
index 934fade..3288c04 100644
--- a/src/QVMProfiler.ui
+++ b/src/qvminspector.ui
@@ -6,7 +6,7 @@
0
0
- 1220
+ 1606
1038
@@ -49,15 +49,15 @@
0
0
- 556
+ 759
777
-
-
+
- 120
+ 160
@@ -74,6 +74,11 @@
Virtual Instruction
+
+
+ Comments
+
+
@@ -96,7 +101,7 @@
-
-
+
65
@@ -256,7 +261,7 @@
-
-
+
70
@@ -421,13 +426,13 @@
0
0
- 556
+ 739
225
-
-
+
Stack Pointer
@@ -460,7 +465,7 @@
-
-
+
Address
@@ -483,7 +488,7 @@
-
-
+
Address
@@ -515,7 +520,7 @@
0
0
- 1220
+ 1606
21
@@ -523,8 +528,8 @@
File
-
-
+
+
@@ -556,7 +561,7 @@
-
-
+
true
@@ -575,12 +580,12 @@
Close
-
+
Open Trace
-
+
Close
@@ -588,7 +593,7 @@
-
+
diff --git a/src/vmctx.cpp b/src/vmctx.cpp
deleted file mode 100644
index 3fae6f1..0000000
--- a/src/vmctx.cpp
+++ /dev/null
@@ -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
\ No newline at end of file
diff --git a/src/vmctx.h b/src/vmctx.h
deleted file mode 100644
index 58cd9b6..0000000
--- a/src/vmctx.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-#include
-
-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
\ No newline at end of file
diff --git a/src/vmprofiler-qt.vcxproj b/src/vmprofiler-qt.vcxproj
index b797c36..13a199e 100644
--- a/src/vmprofiler-qt.vcxproj
+++ b/src/vmprofiler-qt.vcxproj
@@ -103,21 +103,20 @@
-
+
+
-
+
-
-
+
+
-
-
@@ -170,6 +169,7 @@
+
diff --git a/src/vmprofiler-qt.vcxproj.filters b/src/vmprofiler-qt.vcxproj.filters
index dda7c68..fd82560 100644
--- a/src/vmprofiler-qt.vcxproj.filters
+++ b/src/vmprofiler-qt.vcxproj.filters
@@ -1,9 +1,6 @@
-
- Source Files
-
Source Files
@@ -16,7 +13,10 @@
Source Files\DarkStyle
-
+
+ Source Files
+
+
Source Files
@@ -62,28 +62,25 @@
-
- Resource Files
-
Resource Files
Resource Files
+
+ Resource Files
+
-
+
Resource Files
-
+
Resource Files
-
- Header Files
-
Header Files\DarkStyle\framelesswindow
@@ -93,6 +90,12 @@
Header Files\DarkStyle
+
+ Header Files
+
+
+ Header Files
+
@@ -245,9 +248,6 @@
Header Files
-
- Header Files
-