From 6e8bf93445bdccc8dde62b8f570a8299d3ccb002 Mon Sep 17 00:00:00 2001 From: _xeroxz Date: Thu, 3 Jun 2021 18:18:27 -0700 Subject: [PATCH 1/7] cleaned the hell out of the code, rebuilding it pretty much --- dependencies/vmprofiler | 2 +- .../framelesswindow/framelesswindow.ui | 4 +- src/QVMProfiler.cpp | 300 ------------------ src/QVMProfiler.h | 48 --- src/main.cpp | 10 +- src/qvminspector.cpp | 146 +++++++++ src/qvminspector.h | 46 +++ src/{QVMProfiler.qrc => qvminspector.qrc} | 0 src/{QVMProfiler.ui => qvminspector.ui} | 37 ++- src/vmctx.cpp | 87 ----- src/vmctx.h | 24 -- src/vmprofiler-qt.vcxproj | 10 +- src/vmprofiler-qt.vcxproj.filters | 24 +- 13 files changed, 234 insertions(+), 504 deletions(-) delete mode 100644 src/QVMProfiler.cpp delete mode 100644 src/QVMProfiler.h create mode 100644 src/qvminspector.cpp create mode 100644 src/qvminspector.h rename src/{QVMProfiler.qrc => qvminspector.qrc} (100%) rename src/{QVMProfiler.ui => qvminspector.ui} (94%) delete mode 100644 src/vmctx.cpp delete mode 100644 src/vmctx.h 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..fd6fe72 100644 --- a/src/DarkStyle/framelesswindow/framelesswindow.ui +++ b/src/DarkStyle/framelesswindow/framelesswindow.ui @@ -6,8 +6,8 @@ 0 0 - 1479 - 1099 + 1410 + 1048 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..0987188 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,8 @@ #include #include #include -#include "QVMProfiler.h" +#include "qvminspector.h" #include "framelesswindow.h" #include "DarkStyle.h" @@ -10,9 +10,9 @@ 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; + frameless_window.setContent(new qvminspector_t); + frameless_window.setWindowIcon(QIcon("icon.ico")); + frameless_window.show(); return app.exec(); } \ No newline at end of file diff --git a/src/qvminspector.cpp b/src/qvminspector.cpp new file mode 100644 index 0000000..40c0add --- /dev/null +++ b/src/qvminspector.cpp @@ -0,0 +1,146 @@ +#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, 100 ); + ui.virt_instrs->setColumnWidth( 1, 125 ); + ui.virt_instrs->setColumnWidth( 2, 250 ); + 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( "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 ) +{ + QMessageBox MsgBox; + MsgBox.setText( dbg_output ); + MsgBox.exec(); + dbg_print( dbg_output ); +} + +bool qvminspector_t::init_data() +{ + if ( file_header->magic != VMP_MAGIC ) + { + dbg_msg( "invalid magic bytes for vmp2 file...\n" ); + + return false; + } + + dbg_print( "valid magic bytes for vmp2 file...\n" ); + + 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 ); + first_block = reinterpret_cast< vmp2::v3::code_block_t * >( reinterpret_cast< std::uintptr_t >( file_header ) + + file_header->code_block_offset ); + + dbg_print( "> vm entry displayed below...\n" ); + vm::util::print( vmctx->vm_entry ); + return true; +} + +void qvminspector_t::update_ui() +{ + + const auto end_code_blocks = reinterpret_cast< std::uintptr_t >( first_block ) + + ( file_header->code_block_count * sizeof vmp2::v3::code_block_t ) + + file_header->module_size + sizeof vmp2::v3::file_header; + + // 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... + + for ( auto [ code_block, idx ] = std::tuple{ first_block, 0u }; idx < 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 ), ++idx) + { + auto blank_entry1 = new qtree_widget_item_t(), blank_entry2 = new qtree_widget_item_t(), + meta_data_entry = new qtree_widget_item_t(); + + dbg_msg( QString( "> number of vinstrs = %1...\n" ).arg( code_block->vinstr_count ) ); + + // ui.virt_instrs->addTopLevelItem( blank_entry1 ); + // meta_data_entry->setText() + } +} \ No newline at end of file diff --git a/src/qvminspector.h b/src/qvminspector.h new file mode 100644 index 0000000..fdc715b --- /dev/null +++ b/src/qvminspector.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ia32.hpp" +#include "vmp2.hpp" +#include "ui_qvminspector.h" + +using qmain_window_t = QMainWindow; +using qwidget_t = QWidget; +using qstring_t = QString; +using qfile_t = QFile; +using qtree_widget_item_t = QTreeWidgetItem; + +class qvminspector_t : public qmain_window_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(); + 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; +}; 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..26ed92a 100644 --- a/src/QVMProfiler.ui +++ b/src/qvminspector.ui @@ -6,7 +6,7 @@ 0 0 - 1220 + 1606 1038 @@ -49,13 +49,13 @@ 0 0 - 556 + 759 777 - + 120 @@ -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..b08284e 100644 --- a/src/vmprofiler-qt.vcxproj +++ b/src/vmprofiler-qt.vcxproj @@ -103,21 +103,19 @@ - - + - - + + - + - diff --git a/src/vmprofiler-qt.vcxproj.filters b/src/vmprofiler-qt.vcxproj.filters index dda7c68..2de0714 100644 --- a/src/vmprofiler-qt.vcxproj.filters +++ b/src/vmprofiler-qt.vcxproj.filters @@ -1,9 +1,6 @@  - - Source Files - Source Files @@ -16,7 +13,7 @@ Source Files\DarkStyle - + Source Files @@ -62,28 +59,25 @@ - - Resource Files - Resource Files Resource Files + + Resource Files + - + Resource Files - + Resource Files - - Header Files - Header Files\DarkStyle\framelesswindow @@ -93,6 +87,9 @@ Header Files\DarkStyle + + Header Files + @@ -245,9 +242,6 @@ Header Files - - Header Files - From 2d16994d54c5605f2681badde1e7ef439d50eab6 Mon Sep 17 00:00:00 2001 From: _xeroxz Date: Thu, 3 Jun 2021 19:17:58 -0700 Subject: [PATCH 2/7] still working on it, just cleaned the code some more... --- .../framelesswindow/framelesswindow.ui | 6 +- src/qvminspector.cpp | 107 +++++++++++++----- src/qvminspector.h | 1 + src/qvminspector.ui | 2 +- 4 files changed, 86 insertions(+), 30 deletions(-) diff --git a/src/DarkStyle/framelesswindow/framelesswindow.ui b/src/DarkStyle/framelesswindow/framelesswindow.ui index fd6fe72..a98d7ae 100644 --- a/src/DarkStyle/framelesswindow/framelesswindow.ui +++ b/src/DarkStyle/framelesswindow/framelesswindow.ui @@ -6,8 +6,8 @@ 0 0 - 1410 - 1048 + 1484 + 983 @@ -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/qvminspector.cpp b/src/qvminspector.cpp index 40c0add..7ceda64 100644 --- a/src/qvminspector.cpp +++ b/src/qvminspector.cpp @@ -3,9 +3,9 @@ qvminspector_t::qvminspector_t( qwidget_t *parent ) : qmain_window_t( parent ), file_header( nullptr ), vmctx( nullptr ) { ui.setupUi( this ); - ui.virt_instrs->setColumnWidth( 0, 100 ); - ui.virt_instrs->setColumnWidth( 1, 125 ); - ui.virt_instrs->setColumnWidth( 2, 250 ); + ui.virt_instrs->setColumnWidth( 0, 115 ); + ui.virt_instrs->setColumnWidth( 1, 140 ); + ui.virt_instrs->setColumnWidth( 2, 200 ); ui.virt_instrs->setColumnWidth( 3, 200 ); connect( ui.action_open, &QAction::triggered, this, &qvminspector_t::on_open ); @@ -55,7 +55,7 @@ void qvminspector_t::on_open() qfile_t open_file( file_path ); file_header = reinterpret_cast< vmp2::v3::file_header * >( malloc( file_size ) ); - dbg_msg( QString( "loading vmp2 file %1..." ).arg( file_path ) ); + dbg_msg( qstring_t( "loading vmp2 file %1..." ).arg( file_path ) ); if ( !open_file.open( QIODevice::ReadOnly ) ) { @@ -81,9 +81,9 @@ void qvminspector_t::dbg_print( qstring_t dbg_output ) void qvminspector_t::dbg_msg( qstring_t dbg_output ) { - QMessageBox MsgBox; - MsgBox.setText( dbg_output ); - MsgBox.exec(); + qmsg_box_t msg_box; + msg_box.setText( dbg_output ); + msg_box.exec(); dbg_print( dbg_output ); } @@ -91,12 +91,12 @@ bool qvminspector_t::init_data() { if ( file_header->magic != VMP_MAGIC ) { - dbg_msg( "invalid magic bytes for vmp2 file...\n" ); + dbg_msg( "invalid magic bytes for vmp2 file..." ); return false; } - dbg_print( "valid magic bytes for vmp2 file...\n" ); + dbg_print( "valid magic bytes for vmp2 file..." ); if ( file_header->version != vmp2::version_t::v3 ) { @@ -111,36 +111,91 @@ bool qvminspector_t::init_data() 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 ); - - dbg_print( "> vm entry displayed below...\n" ); - vm::util::print( vmctx->vm_entry ); return true; } void qvminspector_t::update_ui() { - - const auto end_code_blocks = reinterpret_cast< std::uintptr_t >( first_block ) + - ( file_header->code_block_count * sizeof vmp2::v3::code_block_t ) + - file_header->module_size + sizeof vmp2::v3::file_header; - // 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... - for ( auto [ code_block, idx ] = std::tuple{ first_block, 0u }; idx < file_header->code_block_count; + 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 ), ++idx) + code_block->next_block_offset ), + ++code_block_num ) { - auto blank_entry1 = new qtree_widget_item_t(), blank_entry2 = new qtree_widget_item_t(), - meta_data_entry = new qtree_widget_item_t(); - - dbg_msg( QString( "> number of vinstrs = %1...\n" ).arg( code_block->vinstr_count ) ); - - // ui.virt_instrs->addTopLevelItem( blank_entry1 ); - // meta_data_entry->setText() + 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_t::number( ( virt_instr->trace_data.vip - file_header->module_base ) + + file_header->image_base, + 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 ) ); + + 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 ) ); + + ui.virt_instrs->addTopLevelItem( virt_instr_entry ); + } } } \ No newline at end of file diff --git a/src/qvminspector.h b/src/qvminspector.h index fdc715b..c4c813d 100644 --- a/src/qvminspector.h +++ b/src/qvminspector.h @@ -17,6 +17,7 @@ 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 { diff --git a/src/qvminspector.ui b/src/qvminspector.ui index 26ed92a..3288c04 100644 --- a/src/qvminspector.ui +++ b/src/qvminspector.ui @@ -57,7 +57,7 @@ - 120 + 160 From c9620caa20ba8309ed4c2d4d6de0d2f2acc93d10 Mon Sep 17 00:00:00 2001 From: _xeroxz Date: Thu, 3 Jun 2021 21:36:34 -0700 Subject: [PATCH 3/7] jcc's are working good, fixed a bug with RSI-0x1 --- .../framelesswindow/framelesswindow.ui | 2 +- src/qvirt_instrs.cpp | 11 ++ src/qvirt_instrs.h | 16 ++ src/qvminspector.cpp | 138 +++++++++++++++++- src/qvminspector.h | 8 +- src/vmprofiler-qt.vcxproj | 4 +- src/vmprofiler-qt.vcxproj.filters | 6 + 7 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 src/qvirt_instrs.cpp create mode 100644 src/qvirt_instrs.h diff --git a/src/DarkStyle/framelesswindow/framelesswindow.ui b/src/DarkStyle/framelesswindow/framelesswindow.ui index a98d7ae..3da636e 100644 --- a/src/DarkStyle/framelesswindow/framelesswindow.ui +++ b/src/DarkStyle/framelesswindow/framelesswindow.ui @@ -6,7 +6,7 @@ 0 0 - 1484 + 1642 983 diff --git a/src/qvirt_instrs.cpp b/src/qvirt_instrs.cpp new file mode 100644 index 0000000..3918bd3 --- /dev/null +++ b/src/qvirt_instrs.cpp @@ -0,0 +1,11 @@ +#include "qvirt_instrs.h" + +qvirt_instrs_t::qvirt_instrs_t( qvminspector_t *vminspector ) : vminspector( vminspector ), ui( &vminspector->ui ) +{ + connect( ui->virt_instrs, &QTreeWidget::itemExpanded, this, &qvirt_instrs_t::on_expand ); +} + +void qvirt_instrs_t::on_expand( qtree_widget_item_t *item ) +{ + +} \ No newline at end of file diff --git a/src/qvirt_instrs.h b/src/qvirt_instrs.h new file mode 100644 index 0000000..6636ae3 --- /dev/null +++ b/src/qvirt_instrs.h @@ -0,0 +1,16 @@ +#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; + + private slots: + void on_expand( qtree_widget_item_t *item ); +}; \ No newline at end of file diff --git a/src/qvminspector.cpp b/src/qvminspector.cpp index 7ceda64..c29f6d0 100644 --- a/src/qvminspector.cpp +++ b/src/qvminspector.cpp @@ -3,7 +3,7 @@ qvminspector_t::qvminspector_t( qwidget_t *parent ) : qmain_window_t( parent ), file_header( nullptr ), vmctx( nullptr ) { ui.setupUi( this ); - ui.virt_instrs->setColumnWidth( 0, 115 ); + ui.virt_instrs->setColumnWidth( 0, 175 ); ui.virt_instrs->setColumnWidth( 1, 140 ); ui.virt_instrs->setColumnWidth( 2, 200 ); ui.virt_instrs->setColumnWidth( 3, 200 ); @@ -125,6 +125,117 @@ bool qvminspector_t::init_data() 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_t::number( ( virt_instr->trace_data.vip - file_header->module_base ) + + file_header->image_base, + 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 ) ); + + 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 ) + { + // 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 @@ -138,6 +249,7 @@ void qvminspector_t::update_ui() 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 ) @@ -187,6 +299,7 @@ void qvminspector_t::update_ui() : 0u ) ); if ( virt_instr->mnemonic_t == vm::handler::JMP && code_block->jcc.has_jcc ) + { virt_instr_entry->setText( 3, QString( "; { %1, %2 }" ) @@ -195,6 +308,29 @@ void qvminspector_t::update_ui() .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 ); + return; + } + ui.virt_instrs->addTopLevelItem( virt_instr_entry ); } } diff --git a/src/qvminspector.h b/src/qvminspector.h index c4c813d..3c67d84 100644 --- a/src/qvminspector.h +++ b/src/qvminspector.h @@ -9,8 +9,8 @@ #include #include "ia32.hpp" -#include "vmp2.hpp" #include "ui_qvminspector.h" +#include "vmp2.hpp" using qmain_window_t = QMainWindow; using qwidget_t = QWidget; @@ -21,8 +21,8 @@ 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 ); @@ -34,6 +34,7 @@ class qvminspector_t : public qmain_window_t 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; @@ -44,4 +45,5 @@ class qvminspector_t : public qmain_window_t 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/vmprofiler-qt.vcxproj b/src/vmprofiler-qt.vcxproj index b08284e..13a199e 100644 --- a/src/vmprofiler-qt.vcxproj +++ b/src/vmprofiler-qt.vcxproj @@ -103,6 +103,8 @@ + + @@ -112,7 +114,6 @@ - @@ -168,6 +169,7 @@ + diff --git a/src/vmprofiler-qt.vcxproj.filters b/src/vmprofiler-qt.vcxproj.filters index 2de0714..fd82560 100644 --- a/src/vmprofiler-qt.vcxproj.filters +++ b/src/vmprofiler-qt.vcxproj.filters @@ -16,6 +16,9 @@ Source Files + + Source Files + @@ -90,6 +93,9 @@ Header Files + + Header Files + From 0509af1204ff2566b91d6109619471929eacd5e7 Mon Sep 17 00:00:00 2001 From: _xeroxz Date: Thu, 3 Jun 2021 22:30:28 -0700 Subject: [PATCH 4/7] address is now block_(rva)... --- src/qvminspector.cpp | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/qvminspector.cpp b/src/qvminspector.cpp index c29f6d0..49078fd 100644 --- a/src/qvminspector.cpp +++ b/src/qvminspector.cpp @@ -3,9 +3,9 @@ qvminspector_t::qvminspector_t( qwidget_t *parent ) : qmain_window_t( parent ), file_header( nullptr ), vmctx( nullptr ) { ui.setupUi( this ); - ui.virt_instrs->setColumnWidth( 0, 175 ); - ui.virt_instrs->setColumnWidth( 1, 140 ); - ui.virt_instrs->setColumnWidth( 2, 200 ); + 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 ); @@ -141,7 +141,7 @@ void qvminspector_t::add_branch_children( qtree_widget_item_t *item, std::uintpt { // 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) + code_block->vip_begin != branch_addr - 1 ) continue; dbg_print( qstring_t( "> code block %1 (block_%2), number of vinstrs = %3..." ) @@ -157,9 +157,14 @@ void qvminspector_t::add_branch_children( qtree_widget_item_t *item, std::uintpt auto virt_instr_entry = new qtree_widget_item_t; // virtual instruction image base'ed rva... (column 1)... - virt_instr_entry->setText( 0, qstring_t::number( ( virt_instr->trace_data.vip - file_header->module_base ) + - file_header->image_base, - 16 ) ); + 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; @@ -263,9 +268,14 @@ void qvminspector_t::update_ui() auto virt_instr_entry = new qtree_widget_item_t; // virtual instruction image base'ed rva... (column 1)... - virt_instr_entry->setText( 0, qstring_t::number( ( virt_instr->trace_data.vip - file_header->module_base ) + - file_header->image_base, - 16 ) ); + 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; From 8824a45dc4a6e8716dcb1552ea9814354e84ef9e Mon Sep 17 00:00:00 2001 From: _xeroxz Date: Thu, 3 Jun 2021 23:05:35 -0700 Subject: [PATCH 5/7] idk what i did --- src/qvminspector.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/qvminspector.cpp b/src/qvminspector.cpp index 49078fd..af44093 100644 --- a/src/qvminspector.cpp +++ b/src/qvminspector.cpp @@ -231,6 +231,11 @@ void qvminspector_t::add_branch_children( qtree_widget_item_t *item, std::uintpt } 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 ] ); @@ -248,6 +253,7 @@ void qvminspector_t::update_ui() // 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 ) + @@ -340,6 +346,11 @@ void qvminspector_t::update_ui() ui.virt_instrs->addTopLevelItem( 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 ) ); ui.virt_instrs->addTopLevelItem( virt_instr_entry ); } From d7c4e19d6a4df2311c89a1d8e8302fe6c4d31e39 Mon Sep 17 00:00:00 2001 From: _xeroxz Date: Fri, 4 Jun 2021 01:18:43 -0700 Subject: [PATCH 6/7] works good, works REALLY well... todo: fix clicking child tree widget items... it doesnt update the gui... --- .../framelesswindow/framelesswindow.ui | 4 +- src/main.cpp | 6 +- src/qvirt_instrs.cpp | 139 +++++++++++++++++- src/qvirt_instrs.h | 8 +- src/qvminspector.cpp | 19 ++- src/qvminspector.h | 2 + 6 files changed, 165 insertions(+), 13 deletions(-) diff --git a/src/DarkStyle/framelesswindow/framelesswindow.ui b/src/DarkStyle/framelesswindow/framelesswindow.ui index 3da636e..7bb5ecd 100644 --- a/src/DarkStyle/framelesswindow/framelesswindow.ui +++ b/src/DarkStyle/framelesswindow/framelesswindow.ui @@ -6,8 +6,8 @@ 0 0 - 1642 - 983 + 1737 + 1157 diff --git a/src/main.cpp b/src/main.cpp index 0987188..1c4cbbc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,7 @@ #include #include "qvminspector.h" +#include "qvirt_instrs.h" #include "framelesswindow.h" #include "DarkStyle.h" @@ -11,7 +12,10 @@ int main(int argc, char *argv[]) QApplication app(argc, argv); QApplication::setStyle(new DarkStyle); FramelessWindow frameless_window; - frameless_window.setContent(new qvminspector_t); + 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(); diff --git a/src/qvirt_instrs.cpp b/src/qvirt_instrs.cpp index 3918bd3..caa1a93 100644 --- a/src/qvirt_instrs.cpp +++ b/src/qvirt_instrs.cpp @@ -2,10 +2,145 @@ qvirt_instrs_t::qvirt_instrs_t( qvminspector_t *vminspector ) : vminspector( vminspector ), ui( &vminspector->ui ) { - connect( ui->virt_instrs, &QTreeWidget::itemExpanded, this, &qvirt_instrs_t::on_expand ); + connect( ui->virt_instrs, &QTreeWidget::itemSelectionChanged, this, &qvirt_instrs_t::on_select ); } -void qvirt_instrs_t::on_expand( qtree_widget_item_t *item ) +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 index 6636ae3..3d4a83d 100644 --- a/src/qvirt_instrs.h +++ b/src/qvirt_instrs.h @@ -10,7 +10,11 @@ class qvirt_instrs_t : public QObject 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_expand( qtree_widget_item_t *item ); + private slots: + void on_select(); }; \ No newline at end of file diff --git a/src/qvminspector.cpp b/src/qvminspector.cpp index af44093..dafcb9b 100644 --- a/src/qvminspector.cpp +++ b/src/qvminspector.cpp @@ -192,7 +192,7 @@ void qvminspector_t::add_branch_children( qtree_widget_item_t *item, std::uintpt // 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..." ) + 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 ) ); @@ -309,10 +309,14 @@ void qvminspector_t::update_ui() // 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 ) ); + 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 ) { @@ -344,7 +348,7 @@ void qvminspector_t::update_ui() // 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 ); - return; + goto finish; // bad code... } else if ( virt_instr->mnemonic_t == vm::handler::JMP ) virt_instr_entry->setText( 3, QString( "; { %1 }" ) @@ -355,4 +359,7 @@ void qvminspector_t::update_ui() 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 index 3c67d84..2a1133b 100644 --- a/src/qvminspector.h +++ b/src/qvminspector.h @@ -12,6 +12,8 @@ #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; From eb7e70b941268e1e6931c9618462ccbba8442e1c Mon Sep 17 00:00:00 2001 From: _xeroxz Date: Fri, 4 Jun 2021 13:34:10 -0700 Subject: [PATCH 7/7] fixed that qt tree widget child issue... now when you click a child item it displays its info...: --- src/qvminspector.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/qvminspector.cpp b/src/qvminspector.cpp index dafcb9b..85d9228 100644 --- a/src/qvminspector.cpp +++ b/src/qvminspector.cpp @@ -197,6 +197,10 @@ void qvminspector_t::add_branch_children( qtree_widget_item_t *item, std::uintpt 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( @@ -240,7 +244,6 @@ void qvminspector_t::add_branch_children( qtree_widget_item_t *item, std::uintpt item->addChild( virt_instr_entry ); add_branch_children( item, code_block->jcc.block_addr[ 0 ] ); } - item->addChild( virt_instr_entry ); } }