diff --git a/CMakeLists.txt b/CMakeLists.txt index b89a94b..914d5dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,10 +119,6 @@ target_link_libraries(vmprofiler-qt PRIVATE vmprofiler ) -target_link_options(vmprofiler-qt PRIVATE - "/SUBSYSTEM:WINDOWS" -) - # Enable Qt moc/rrc/uic support target_qt(${CMKR_TARGET}) # Copy Qt DLLs next to the application diff --git a/cmake.toml b/cmake.toml index ea6f7af..6c61e28 100644 --- a/cmake.toml +++ b/cmake.toml @@ -38,7 +38,6 @@ compile-definitions = [ ] link-libraries = ["Qt5::Widgets", "vmprofiler"] -link-options = ["/SUBSYSTEM:WINDOWS"] cmake-after = """ # Enable Qt moc/rrc/uic support diff --git a/dependencies/vmprofiler b/dependencies/vmprofiler index 0511401..bf2d6ac 160000 --- a/dependencies/vmprofiler +++ b/dependencies/vmprofiler @@ -1 +1 @@ -Subproject commit 051140175db16b38acee882cfca714b4a1000a41 +Subproject commit bf2d6acba03a81e07371fc169d26744992455571 diff --git a/src/main.cpp b/src/main.cpp index 9b282de..f5ef58b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,19 +10,18 @@ #include "qvm_virtual_instructions.h" #include "qvm_virtual_routines.h" -int WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd ) -{ - QApplication app( __argc, __argv ); - QApplication::setStyle( new DarkStyle ); - FramelessWindow FW; +int main(int argc, char** argv) { + QApplication app(argc, argv); + QApplication::setStyle(new DarkStyle); + FramelessWindow FW; - auto g_main_window = new qvm_inspector; - qvm_virtual_instructions virt_instrs_panel( g_main_window ); - qvm_handlers virt_handler_panel( g_main_window ); - qvm_virtual_routines virt_routine_panel( g_main_window ); + auto g_main_window = new qvm_inspector; + qvm_virtual_instructions virt_instrs_panel(g_main_window); + qvm_handlers virt_handler_panel(g_main_window); + qvm_virtual_routines virt_routine_panel(g_main_window); - FW.setContent( g_main_window ); - FW.setWindowIcon( QIcon( "icon.ico" ) ); - FW.show(); - return app.exec(); + FW.setContent(g_main_window); + FW.setWindowIcon(QIcon("icon.ico")); + FW.show(); + return app.exec(); } \ No newline at end of file diff --git a/src/qvm_inspector.cpp b/src/qvm_inspector.cpp index 4321662..ffbb7fe 100644 --- a/src/qvm_inspector.cpp +++ b/src/qvm_inspector.cpp @@ -1,348 +1,354 @@ #include "qvm_inspector.h" -qvm_inspector::qvm_inspector( QWidget *parent ) : QMainWindow( parent ), file_header( nullptr ), g_vm_ctx( nullptr ) -{ - ui.setupUi( this ); - ui.virt_instrs->setColumnWidth( 0, 125 ); - ui.virt_instrs->setColumnWidth( 1, 150 ); - ui.virt_instrs->setColumnWidth( 2, 190 ); - ui.virt_instrs->setColumnWidth( 3, 200 ); - ui.virtual_machine_enters->setColumnWidth( 0, 180 ); - - connect( ui.action_open, &QAction::triggered, this, &qvm_inspector::on_open ); - connect( ui.action_close, &QAction::triggered, this, &qvm_inspector::on_close ); +qvm_inspector::qvm_inspector(QWidget *parent) + : QMainWindow(parent), file_header(nullptr), g_vm_ctx(nullptr) { + ui.setupUi(this); + ui.virt_instrs->setColumnWidth(0, 125); + ui.virt_instrs->setColumnWidth(1, 150); + ui.virt_instrs->setColumnWidth(2, 190); + ui.virt_instrs->setColumnWidth(3, 200); + ui.virtual_machine_enters->setColumnWidth(0, 180); + + connect(ui.action_open, &QAction::triggered, this, &qvm_inspector::on_open); + connect(ui.action_close, &QAction::triggered, this, &qvm_inspector::on_close); } -void qvm_inspector::on_close() -{ - exit( 0 ); -} +void qvm_inspector::on_close() { exit(0); } -void qvm_inspector::on_open() -{ - if ( file_header ) - free( file_header ); +void qvm_inspector::on_open() { + if (file_header) free(file_header); - file_header = nullptr; - img_base = 0u, module_base = 0u; + file_header = nullptr; + img_base = 0u, module_base = 0u; - auto file_path = QFileDialog::getOpenFileName( - this, tr( "open vmp2 file" ), std::filesystem::current_path().string().c_str(), tr( "vmp2 file (*.vmp2)" ) ); + auto file_path = QFileDialog::getOpenFileName( + this, tr("open vmp2 file"), + std::filesystem::current_path().string().c_str(), + tr("vmp2 file (*.vmp2)")); - if ( file_path.isEmpty() ) - { - dbg_msg( "invalid vmp2 file... no file selected..." ); - return; - } + if (file_path.isEmpty()) { + dbg_msg("invalid vmp2 file... no file selected..."); + return; + } - if ( !std::filesystem::exists( file_path.toStdString() ) ) - { - dbg_msg( "vmp2 file does not exist..." ); - return; - } + if (!std::filesystem::exists(file_path.toStdString())) { + dbg_msg("vmp2 file does not exist..."); + return; + } - const auto file_size = std::filesystem::file_size( file_path.toStdString() ); + const auto file_size = std::filesystem::file_size(file_path.toStdString()); - if ( !file_size ) - { - dbg_msg( "invalid vmp2 file size..." ); - return; - } + if (!file_size) { + dbg_msg("invalid vmp2 file size..."); + return; + } - QFile open_file( file_path ); - file_header = reinterpret_cast< vmp2::v4::file_header * >( malloc( file_size ) ); + QFile open_file(file_path); + file_header = reinterpret_cast(malloc(file_size)); - if ( !open_file.open( QIODevice::ReadOnly ) ) - { - dbg_msg( "failed to open vmp2 file..." ); - return; - } + if (!open_file.open(QIODevice::ReadOnly)) { + dbg_msg("failed to open vmp2 file..."); + return; + } - memcpy( file_header, open_file.readAll().data(), file_size ); + memcpy(file_header, open_file.readAll().data(), file_size); - if ( !init_data() ) - { - dbg_msg( "failed to init vmp2 file data..." ); - return; - } + if (!init_data()) { + dbg_msg("failed to init vmp2 file data..."); + return; + } } -void qvm_inspector::dbg_print( QString dbg_output ) -{ - ui.dbg_output_window->appendPlainText( dbg_output ); +void qvm_inspector::dbg_print(QString dbg_output) { + ui.dbg_output_window->appendPlainText(dbg_output); } -void qvm_inspector::dbg_msg( QString dbg_output ) -{ - QMessageBox msg_box; - msg_box.setText( dbg_output ); - msg_box.exec(); - dbg_print( dbg_output ); +void qvm_inspector::dbg_msg(QString dbg_output) { + QMessageBox msg_box; + msg_box.setText(dbg_output); + msg_box.exec(); + dbg_print(dbg_output); } -bool qvm_inspector::init_data() -{ - if ( file_header->magic != VMP_MAGIC ) - { - dbg_msg( "invalid magic bytes for vmp2 file..." ); +bool qvm_inspector::init_data() { + if (file_header->magic != VMP_MAGIC) { + dbg_msg("invalid magic bytes for vmp2 file..."); - return false; - } + return false; + } - dbg_print( "valid magic bytes for vmp2 file..." ); + dbg_print("valid magic bytes for vmp2 file..."); - if ( file_header->version != vmp2::version_t::v4 ) - { - dbg_msg( "invalid vmp2 file version... " - "this vminspector is compiled for version 4...\n" ); + if (file_header->version != vmp2::version_t::v4) { + dbg_msg( + "invalid vmp2 file version... " + "this vminspector is compiled for version 4...\n"); - return false; - } + return false; + } - img_base = file_header->image_base; - img_size = file_header->module_size; - module_base = reinterpret_cast< std::uintptr_t >( file_header ) + file_header->module_offset; + img_base = file_header->image_base; + img_size = file_header->module_size; + module_base = reinterpret_cast(file_header) + + file_header->module_offset; - if ( !serialize_vmp2( virt_rtns ) ) - { - dbg_msg( "failed to serialize vmp2 file format...\n" ); - return false; - } + if (!serialize_vmp2(virt_rtns)) { + dbg_msg("failed to serialize vmp2 file format...\n"); + return false; + } - update_ui(); - return true; + update_ui(); + return true; } -void qvm_inspector::update_ui() -{ - ui.virtual_machine_enters->clear(); - for ( auto &[ rtn_rva, rtn_blks ] : virt_rtns ) - { - auto new_item = new QTreeWidgetItem(); - new_item->setText( 0, QString( "rtn_%1" ).arg( rtn_rva + file_header->image_base, 0, 16 ) ); - new_item->setText( 1, QString( "%1" ).arg( rtn_rva + file_header->image_base, 0, 16 ) ); - new_item->setText( 2, QString( "%1" ).arg( rtn_blks.size() ) ); - new_item->setData( 0, Qt::UserRole, QVariant( rtn_rva ) ); - - std::for_each( rtn_blks.begin(), rtn_blks.end(), [ & ]( vm::instrs::code_block_t &code_blk ) { - auto new_child = new QTreeWidgetItem(); - new_child->setText( 0, QString( "blk_%1" ).arg( code_blk.vip_begin, 0, 16 ) ); - new_child->setText( 1, QString( "%1" ).arg( code_blk.vip_begin, 0, 16 ) ); - new_child->setText( 2, QString( "%1" ).arg( code_blk.vinstrs.size() ) ); - new_item->addChild( new_child ); - } ); - - ui.virtual_machine_enters->addTopLevelItem( new_item ); - } +void qvm_inspector::update_ui() { + ui.virtual_machine_enters->clear(); + for (auto &[rtn_rva, rtn_blks] : virt_rtns) { + auto new_item = new QTreeWidgetItem(); + new_item->setText( + 0, QString("rtn_%1").arg(rtn_rva + file_header->image_base, 0, 16)); + new_item->setText( + 1, QString("%1").arg(rtn_rva + file_header->image_base, 0, 16)); + new_item->setText(2, QString("%1").arg(rtn_blks.size())); + new_item->setData(0, Qt::UserRole, QVariant(rtn_rva)); + + std::for_each( + rtn_blks.begin(), rtn_blks.end(), + [&](vm::instrs::code_block_t &code_blk) { + auto new_child = new QTreeWidgetItem(); + new_child->setText(0, + QString("blk_%1").arg(code_blk.vip_begin, 0, 16)); + new_child->setText(1, QString("%1").arg(code_blk.vip_begin, 0, 16)); + new_child->setText(2, QString("%1").arg(code_blk.vinstrs.size())); + new_item->addChild(new_child); + }); + + ui.virtual_machine_enters->addTopLevelItem(new_item); + } } -bool qvm_inspector::serialize_vmp2( std::vector< rtn_data_t > &virt_rtns ) -{ - if ( file_header->version != vmp2::version_t::v4 ) - { - std::printf( "[!] invalid vmp2 file version... this build uses v3...\n" ); - return false; +bool qvm_inspector::serialize_vmp2(std::vector &virt_rtns) { + if (file_header->version != vmp2::version_t::v4) { + std::printf("[!] invalid vmp2 file version... this build uses v3...\n"); + return false; + } + + auto first_rtn = reinterpret_cast( + reinterpret_cast(file_header) + file_header->rtn_offset); + + for (auto [rtn_block, rtn_idx] = std::pair{first_rtn, 0ull}; + rtn_idx < file_header->rtn_count; + ++rtn_idx, rtn_block = reinterpret_cast( + reinterpret_cast(rtn_block) + + rtn_block->size)) { + virt_rtns.push_back({rtn_block->vm_enter_offset, {}}); + for (auto [code_block, block_idx] = + std::pair{&rtn_block->code_blocks[0], 0ull}; + block_idx < rtn_block->code_block_count; + ++block_idx, code_block = reinterpret_cast( + reinterpret_cast(code_block) + + code_block->next_block_offset)) { + auto block_vinstrs = reinterpret_cast( + reinterpret_cast(code_block) + + sizeof(vmp2::v4::code_block_t) + (code_block->num_block_addrs * 8)); + + vm::instrs::code_block_t _code_block{code_block->vip_begin}; + _code_block.jcc.has_jcc = code_block->has_jcc; + _code_block.jcc.type = code_block->jcc_type; + + for (auto idx = 0u; idx < code_block->num_block_addrs; ++idx) + _code_block.jcc.block_addr.push_back(code_block->branch_addr[idx]); + + for (auto idx = 0u; idx < code_block->vinstr_count; ++idx) + _code_block.vinstrs.push_back(block_vinstrs[idx]); + + virt_rtns.back().rtn_blks.push_back(_code_block); } + } - auto first_rtn = reinterpret_cast< vmp2::v4::rtn_t * >( reinterpret_cast< std::uintptr_t >( file_header ) + - file_header->rtn_offset ); - - for ( auto [ rtn_block, rtn_idx ] = std::pair{ first_rtn, 0ull }; rtn_idx < file_header->rtn_count; - ++rtn_idx, rtn_block = reinterpret_cast< vmp2::v4::rtn_t * >( - reinterpret_cast< std::uintptr_t >( rtn_block ) + rtn_block->size ) ) - { - virt_rtns.push_back( { rtn_block->vm_enter_offset, {} } ); - for ( auto [ code_block, block_idx ] = std::pair{ &rtn_block->code_blocks[ 0 ], 0ull }; - block_idx < rtn_block->code_block_count; - ++block_idx, code_block = reinterpret_cast< vmp2::v4::code_block_t * >( - reinterpret_cast< std::uintptr_t >( code_block ) + code_block->next_block_offset ) ) - { - auto block_vinstrs = reinterpret_cast< vm::instrs::virt_instr_t * >( - reinterpret_cast< std::uintptr_t >( code_block ) + sizeof vmp2::v4::code_block_t + - ( code_block->num_block_addrs * 8 ) ); - - vm::instrs::code_block_t _code_block{ code_block->vip_begin }; - _code_block.jcc.has_jcc = code_block->has_jcc; - _code_block.jcc.type = code_block->jcc_type; - - for ( auto idx = 0u; idx < code_block->num_block_addrs; ++idx ) - _code_block.jcc.block_addr.push_back( code_block->branch_addr[ idx ] ); - - for ( auto idx = 0u; idx < code_block->vinstr_count; ++idx ) - _code_block.vinstrs.push_back( block_vinstrs[ idx ] ); - - virt_rtns.back().rtn_blks.push_back( _code_block ); - } - } - - return true; + return true; } -void qvm_inspector::update_virtual_instructions( std::uintptr_t rtn_addr, std::uintptr_t blk_addr, - QTreeWidgetItem *parent ) -{ - auto _rtn = std::find_if( virt_rtns.begin(), virt_rtns.end(), - [ & ]( rtn_data_t &rtn ) -> bool { return rtn.rtn_rva == rtn_addr; } ); - - if ( _rtn == virt_rtns.end() ) - return; - - auto code_blk = blk_addr ? std::find_if( _rtn->rtn_blks.begin(), _rtn->rtn_blks.end(), - [ & ]( const vm::instrs::code_block_t &code_block ) -> bool { - return code_block.vip_begin == blk_addr; - } ) - : _rtn->rtn_blks.begin(); - - if ( code_blk == _rtn->rtn_blks.end() ) - return; - - if ( std::find( code_block_addrs.begin(), code_block_addrs.end(), code_blk->vip_begin ) != code_block_addrs.end() ) - return; - - code_block_addrs.push_back( code_blk->vip_begin ); - - for ( auto &vinstr : code_blk->vinstrs ) - { - const auto profile = vm::handler::get_profile( vinstr.mnemonic_t ); - auto virt_instr_entry = new QTreeWidgetItem(); - - // virtual instruction image base'ed rva... (column 1)... - virt_instr_entry->setText( - 0, QString( "0x%1" ).arg( QString::number( - ( vinstr.trace_data.vip - file_header->module_base ) + file_header->image_base, 16 ) ) ); - - // virtual instruction operand bytes... (column 2)... - QString operand_bytes; - operand_bytes.append( QString( "%1" ).arg( vinstr.opcode, 0, 16 ) ); - - // if virt instruction has an imm... grab its bytes... - if ( vinstr.operand.has_imm ) - { - operand_bytes.append( " - " ); - for ( auto _idx = 0u; _idx < vinstr.operand.imm.imm_size / 8; ++_idx ) - operand_bytes.append( QString( "%1 " ).arg( - reinterpret_cast< const std::uint8_t * >( &vinstr.operand.imm.u )[ _idx ], 0, 16 ) ); - } +void qvm_inspector::update_virtual_instructions(std::uintptr_t rtn_addr, + std::uintptr_t blk_addr, + QTreeWidgetItem *parent) { + auto _rtn = std::find_if( + virt_rtns.begin(), virt_rtns.end(), + [&](rtn_data_t &rtn) -> bool { return rtn.rtn_rva == rtn_addr; }); + + if (_rtn == virt_rtns.end()) return; + + auto code_blk = + blk_addr ? std::find_if( + _rtn->rtn_blks.begin(), _rtn->rtn_blks.end(), + [&](const vm::instrs::code_block_t &code_block) -> bool { + return code_block.vip_begin == blk_addr; + }) + : _rtn->rtn_blks.begin(); + + if (code_blk == _rtn->rtn_blks.end()) return; + + if (std::find(code_block_addrs.begin(), code_block_addrs.end(), + code_blk->vip_begin) != code_block_addrs.end()) + return; + + code_block_addrs.push_back(code_blk->vip_begin); + + for (auto &vinstr : code_blk->vinstrs) { + const auto profile = vm::handler::get_profile(vinstr.mnemonic_t); + auto virt_instr_entry = new QTreeWidgetItem(); + + // virtual instruction image base'ed rva... (column 1)... + virt_instr_entry->setText( + 0, QString("0x%1").arg(QString::number( + (vinstr.trace_data.vip - file_header->module_base) + + file_header->image_base, + 16))); + + // virtual instruction operand bytes... (column 2)... + QString operand_bytes; + operand_bytes.append(QString("%1").arg(vinstr.opcode, 0, 16)); + + // if virt instruction has an imm... grab its bytes... + if (vinstr.operand.has_imm) { + operand_bytes.append(" - "); + for (auto _idx = 0u; _idx < vinstr.operand.imm.imm_size / 8; ++_idx) + operand_bytes.append(QString("%1 ").arg( + reinterpret_cast(&vinstr.operand.imm.u)[_idx], + 0, 16)); + } - virt_instr_entry->setText( 1, operand_bytes ); - - // virtual instruction string, includes imm... (colume 3)... - QString decoded_instr( - QString( "%1" ).arg( profile ? profile->name : QString( "UNK(%1)" ).arg( vinstr.opcode, 0, 16 ) ) ); - - if ( vinstr.operand.has_imm ) // if there is a second operand (imm) put its value... - decoded_instr.append( QString( " %1" ).arg( vinstr.operand.imm.u, 0, 16 ) ); - - virt_instr_entry->setText( 2, decoded_instr ); - - // add comments to the virtual instruction... (colume 4)... - if ( vinstr.mnemonic_t == vm::handler::LREGQ || vinstr.mnemonic_t == vm::handler::SREGQ ) - virt_instr_entry->setText( - 3, QString( "; vreg%1" ).arg( vinstr.operand.imm.u ? ( vinstr.operand.imm.u / 8 ) : 0u ) ); - - if ( vinstr.mnemonic_t == vm::handler::JMP ) - { - switch ( code_blk->jcc.type ) - { - case vm::instrs::jcc_type::branching: - { - virt_instr_entry->setText( 3, QString( "; { 0x%1, 0x%2 }" ) - .arg( code_blk->jcc.block_addr[ 0 ], 0, 16 ) - .arg( code_blk->jcc.block_addr[ 1 ], 0, 16 ) ); - - auto entry_rva = g_vm_ctx->vm_handlers[ vinstr.opcode ].address - module_base; - auto branch_entry1 = new QTreeWidgetItem(), branch_entry2 = new QTreeWidgetItem(); - const auto block1_addr = code_blk->jcc.block_addr[ 0 ]; - const auto block2_addr = code_blk->jcc.block_addr[ 1 ]; - - branch_entry1->setText( 0, QString( "0x%1" ).arg( block1_addr, 0, 16 ) ); - branch_entry1->setText( 3, QString( "; blk_0x%1" ).arg( block1_addr, 0, 16 ) ); - - branch_entry2->setText( 0, QString( "0x%1" ).arg( block2_addr, 0, 16 ) ); - branch_entry2->setText( 3, QString( "; blk_0x%1" ).arg( block2_addr, 0, 16 ) ); - - if ( g_vm_ctx ) - delete g_vm_ctx; - - if ( !( g_vm_ctx = new vm::ctx_t( module_base, img_base, img_size, entry_rva ) )->init() ) - { - dbg_print( QString( "> failed to init vm::ctx_t for jmp at = %1..." ) - .arg( QString::number( code_blk->vip_begin, 16 ) ) ); - return; - } - - update_virtual_instructions( rtn_addr, code_blk->jcc.block_addr[ 0 ], branch_entry1 ); - - if ( g_vm_ctx ) - delete g_vm_ctx; - - if ( !( g_vm_ctx = new vm::ctx_t( module_base, img_base, img_size, entry_rva ) )->init() ) - { - dbg_print( QString( "> failed to init vm::ctx_t for jmp at = %1..." ) - .arg( QString::number( code_blk->vip_begin, 16 ) ) ); - return; - } - - update_virtual_instructions( rtn_addr, code_blk->jcc.block_addr[ 1 ], branch_entry2 ); - virt_instr_entry->addChildren( { branch_entry1, branch_entry2 } ); - break; - } - case vm::instrs::jcc_type::absolute: - { - auto entry_rva = g_vm_ctx->vm_handlers[ vinstr.opcode ].address - module_base; - virt_instr_entry->setText( 3, QString( "; { 0x%1 }" ).arg( code_blk->jcc.block_addr[ 0 ], 0, 16 ) ); - - if ( g_vm_ctx ) - delete g_vm_ctx; - - if ( !( g_vm_ctx = new vm::ctx_t( module_base, img_base, img_size, entry_rva ) )->init() ) - { - dbg_print( QString( "> failed to init vm::ctx_t for jmp at = %1..." ) - .arg( QString::number( code_blk->vip_begin, 16 ) ) ); - return; - } - - auto branch_entry1 = new QTreeWidgetItem(); - branch_entry1->setText( 0, QString( "0x%1" ).arg( code_blk->jcc.block_addr[ 0 ], 0, 16 ) ); - branch_entry1->setText( 3, QString( "; blk_0x%1" ).arg( code_blk->jcc.block_addr[ 0 ], 0, 16 ) ); - update_virtual_instructions( rtn_addr, code_blk->jcc.block_addr[ 0 ], branch_entry1 ); - virt_instr_entry->addChild( branch_entry1 ); - break; - } - case vm::instrs::jcc_type::switch_case: - { - auto entry_rva = g_vm_ctx->vm_handlers[ vinstr.opcode ].address - module_base; - - if ( g_vm_ctx ) - delete g_vm_ctx; - - if ( !( g_vm_ctx = new vm::ctx_t( module_base, img_base, img_size, entry_rva ) )->init() ) - { - dbg_print( QString( "> failed to init vm::ctx_t for jmp at = %1..." ) - .arg( QString::number( code_blk->vip_begin, 16 ) ) ); - return; - } - - for ( auto branch_addr : code_blk->jcc.block_addr ) - { - virt_instr_entry->setText( 3, QString( "; switch case" ) ); - - auto branch_entry = new QTreeWidgetItem(); - branch_entry->setText( 0, QString( "0x%1" ).arg( branch_addr, 0, 16 ) ); - - update_virtual_instructions( rtn_addr, branch_addr, branch_entry ); - virt_instr_entry->addChild( branch_entry ); - } - break; - } - default: - break; - } + virt_instr_entry->setText(1, operand_bytes); + + // virtual instruction string, includes imm... (colume 3)... + QString decoded_instr(QString("%1").arg( + profile ? profile->name + : QString("UNK(%1)").arg(vinstr.opcode, 0, 16))); + + if (vinstr.operand + .has_imm) // if there is a second operand (imm) put its value... + decoded_instr.append(QString(" %1").arg(vinstr.operand.imm.u, 0, 16)); + + virt_instr_entry->setText(2, decoded_instr); + + // add comments to the virtual instruction... (colume 4)... + if (vinstr.mnemonic_t == vm::handler::LREGQ || + vinstr.mnemonic_t == vm::handler::SREGQ) + virt_instr_entry->setText( + 3, QString("; vreg%1") + .arg(vinstr.operand.imm.u ? (vinstr.operand.imm.u / 8) : 0u)); + + if (vinstr.mnemonic_t == vm::handler::JMP) { + switch (code_blk->jcc.type) { + case vm::instrs::jcc_type::branching: { + virt_instr_entry->setText( + 3, QString("; { 0x%1, 0x%2 }") + .arg(code_blk->jcc.block_addr[0], 0, 16) + .arg(code_blk->jcc.block_addr[1], 0, 16)); + + auto entry_rva = + g_vm_ctx->vm_handlers[vinstr.opcode].address - module_base; + auto branch_entry1 = new QTreeWidgetItem(), + branch_entry2 = new QTreeWidgetItem(); + const auto block1_addr = code_blk->jcc.block_addr[0]; + const auto block2_addr = code_blk->jcc.block_addr[1]; + + branch_entry1->setText(0, QString("0x%1").arg(block1_addr, 0, 16)); + branch_entry1->setText(3, + QString("; blk_0x%1").arg(block1_addr, 0, 16)); + + branch_entry2->setText(0, QString("0x%1").arg(block2_addr, 0, 16)); + branch_entry2->setText(3, + QString("; blk_0x%1").arg(block2_addr, 0, 16)); + + if (g_vm_ctx) delete g_vm_ctx; + + if (!(g_vm_ctx = + new vm::ctx_t(module_base, img_base, img_size, entry_rva)) + ->init()) { + dbg_print(QString("> failed to init vm::ctx_t for jmp at = %1...") + .arg(QString::number(code_blk->vip_begin, 16))); + return; + } + + update_virtual_instructions(rtn_addr, code_blk->jcc.block_addr[0], + branch_entry1); + + if (g_vm_ctx) delete g_vm_ctx; + + if (!(g_vm_ctx = + new vm::ctx_t(module_base, img_base, img_size, entry_rva)) + ->init()) { + dbg_print(QString("> failed to init vm::ctx_t for jmp at = %1...") + .arg(QString::number(code_blk->vip_begin, 16))); + return; + } + + update_virtual_instructions(rtn_addr, code_blk->jcc.block_addr[1], + branch_entry2); + virt_instr_entry->addChildren({branch_entry1, branch_entry2}); + break; } - - QVariant var; - var.setValue( vinstr ); - virt_instr_entry->setData( 3, Qt::UserRole, var ); - parent ? parent->addChild( virt_instr_entry ) : ui.virt_instrs->addTopLevelItem( virt_instr_entry ); + case vm::instrs::jcc_type::absolute: { + auto entry_rva = + g_vm_ctx->vm_handlers[vinstr.opcode].address - module_base; + virt_instr_entry->setText( + 3, QString("; { 0x%1 }").arg(code_blk->jcc.block_addr[0], 0, 16)); + + if (g_vm_ctx) delete g_vm_ctx; + + if (!(g_vm_ctx = + new vm::ctx_t(module_base, img_base, img_size, entry_rva)) + ->init()) { + dbg_print(QString("> failed to init vm::ctx_t for jmp at = %1...") + .arg(QString::number(code_blk->vip_begin, 16))); + return; + } + + auto branch_entry1 = new QTreeWidgetItem(); + branch_entry1->setText( + 0, QString("0x%1").arg(code_blk->jcc.block_addr[0], 0, 16)); + branch_entry1->setText( + 3, QString("; blk_0x%1").arg(code_blk->jcc.block_addr[0], 0, 16)); + update_virtual_instructions(rtn_addr, code_blk->jcc.block_addr[0], + branch_entry1); + virt_instr_entry->addChild(branch_entry1); + break; + } + case vm::instrs::jcc_type::switch_case: { + auto entry_rva = + g_vm_ctx->vm_handlers[vinstr.opcode].address - module_base; + + if (g_vm_ctx) delete g_vm_ctx; + + if (!(g_vm_ctx = + new vm::ctx_t(module_base, img_base, img_size, entry_rva)) + ->init()) { + dbg_print(QString("> failed to init vm::ctx_t for jmp at = %1...") + .arg(QString::number(code_blk->vip_begin, 16))); + return; + } + + for (auto branch_addr : code_blk->jcc.block_addr) { + virt_instr_entry->setText(3, QString("; switch case")); + + auto branch_entry = new QTreeWidgetItem(); + branch_entry->setText(0, QString("0x%1").arg(branch_addr, 0, 16)); + + update_virtual_instructions(rtn_addr, branch_addr, branch_entry); + virt_instr_entry->addChild(branch_entry); + } + break; + } + default: + break; + } } + + QVariant var; + var.setValue(vinstr); + virt_instr_entry->setData(3, Qt::UserRole, var); + parent ? parent->addChild(virt_instr_entry) + : ui.virt_instrs->addTopLevelItem(virt_instr_entry); + } } diff --git a/src/qvm_inspector.h b/src/qvm_inspector.h index 1327e50..47c3bda 100644 --- a/src/qvm_inspector.h +++ b/src/qvm_inspector.h @@ -1,56 +1,265 @@ #pragma once -#include -#include -#include -#include #include #include -#include +#include #include #include #include #include "ui_qvminspector.h" -#include -#include -#define ABS_TO_IMG( addr, mod_base, img_base ) ( addr - mod_base ) + img_base -Q_DECLARE_METATYPE( vm::instrs::virt_instr_t ) +/** + * The 64-bit RFLAGS register contains a group of status flags, a control flag, + * and a group of system flags in 64-bit mode. The upper 32 bits of RFLAGS + * register is reserved. The lower 32 bits of RFLAGS is the same as EFLAGS. + * + * @see EFLAGS + * @see Vol1[3.4.3.4(RFLAGS Register in 64-Bit Mode)] (reference) + */ +typedef union { + struct { + /** + * @brief Carry flag + * + * [Bit 0] See the description in EFLAGS. + */ + uint64_t carry_flag : 1; +#define RFLAGS_CARRY_FLAG_BIT 0 +#define RFLAGS_CARRY_FLAG_FLAG 0x01 +#define RFLAGS_CARRY_FLAG_MASK 0x01 +#define RFLAGS_CARRY_FLAG(_) (((_) >> 0) & 0x01) + + /** + * [Bit 1] Reserved - always 1 + */ + uint64_t read_as_1 : 1; +#define RFLAGS_READ_AS_1_BIT 1 +#define RFLAGS_READ_AS_1_FLAG 0x02 +#define RFLAGS_READ_AS_1_MASK 0x01 +#define RFLAGS_READ_AS_1(_) (((_) >> 1) & 0x01) + + /** + * @brief Parity flag + * + * [Bit 2] See the description in EFLAGS. + */ + uint64_t parity_flag : 1; +#define RFLAGS_PARITY_FLAG_BIT 2 +#define RFLAGS_PARITY_FLAG_FLAG 0x04 +#define RFLAGS_PARITY_FLAG_MASK 0x01 +#define RFLAGS_PARITY_FLAG(_) (((_) >> 2) & 0x01) + uint64_t reserved1 : 1; + + /** + * @brief Auxiliary Carry flag + * + * [Bit 4] See the description in EFLAGS. + */ + uint64_t auxiliary_carry_flag : 1; +#define RFLAGS_AUXILIARY_CARRY_FLAG_BIT 4 +#define RFLAGS_AUXILIARY_CARRY_FLAG_FLAG 0x10 +#define RFLAGS_AUXILIARY_CARRY_FLAG_MASK 0x01 +#define RFLAGS_AUXILIARY_CARRY_FLAG(_) (((_) >> 4) & 0x01) + uint64_t reserved2 : 1; + + /** + * @brief Zero flag + * + * [Bit 6] See the description in EFLAGS. + */ + uint64_t zero_flag : 1; +#define RFLAGS_ZERO_FLAG_BIT 6 +#define RFLAGS_ZERO_FLAG_FLAG 0x40 +#define RFLAGS_ZERO_FLAG_MASK 0x01 +#define RFLAGS_ZERO_FLAG(_) (((_) >> 6) & 0x01) + + /** + * @brief Sign flag + * + * [Bit 7] See the description in EFLAGS. + */ + uint64_t sign_flag : 1; +#define RFLAGS_SIGN_FLAG_BIT 7 +#define RFLAGS_SIGN_FLAG_FLAG 0x80 +#define RFLAGS_SIGN_FLAG_MASK 0x01 +#define RFLAGS_SIGN_FLAG(_) (((_) >> 7) & 0x01) + + /** + * @brief Trap flag + * + * [Bit 8] See the description in EFLAGS. + */ + uint64_t trap_flag : 1; +#define RFLAGS_TRAP_FLAG_BIT 8 +#define RFLAGS_TRAP_FLAG_FLAG 0x100 +#define RFLAGS_TRAP_FLAG_MASK 0x01 +#define RFLAGS_TRAP_FLAG(_) (((_) >> 8) & 0x01) + + /** + * @brief Interrupt enable flag + * + * [Bit 9] See the description in EFLAGS. + */ + uint64_t interrupt_enable_flag : 1; +#define RFLAGS_INTERRUPT_ENABLE_FLAG_BIT 9 +#define RFLAGS_INTERRUPT_ENABLE_FLAG_FLAG 0x200 +#define RFLAGS_INTERRUPT_ENABLE_FLAG_MASK 0x01 +#define RFLAGS_INTERRUPT_ENABLE_FLAG(_) (((_) >> 9) & 0x01) + + /** + * @brief Direction flag + * + * [Bit 10] See the description in EFLAGS. + */ + uint64_t direction_flag : 1; +#define RFLAGS_DIRECTION_FLAG_BIT 10 +#define RFLAGS_DIRECTION_FLAG_FLAG 0x400 +#define RFLAGS_DIRECTION_FLAG_MASK 0x01 +#define RFLAGS_DIRECTION_FLAG(_) (((_) >> 10) & 0x01) + + /** + * @brief Overflow flag + * + * [Bit 11] See the description in EFLAGS. + */ + uint64_t overflow_flag : 1; +#define RFLAGS_OVERFLOW_FLAG_BIT 11 +#define RFLAGS_OVERFLOW_FLAG_FLAG 0x800 +#define RFLAGS_OVERFLOW_FLAG_MASK 0x01 +#define RFLAGS_OVERFLOW_FLAG(_) (((_) >> 11) & 0x01) + + /** + * @brief I/O privilege level field + * + * [Bits 13:12] See the description in EFLAGS. + */ + uint64_t io_privilege_level : 2; +#define RFLAGS_IO_PRIVILEGE_LEVEL_BIT 12 +#define RFLAGS_IO_PRIVILEGE_LEVEL_FLAG 0x3000 +#define RFLAGS_IO_PRIVILEGE_LEVEL_MASK 0x03 +#define RFLAGS_IO_PRIVILEGE_LEVEL(_) (((_) >> 12) & 0x03) + + /** + * @brief Nested task flag + * + * [Bit 14] See the description in EFLAGS. + */ + uint64_t nested_task_flag : 1; +#define RFLAGS_NESTED_TASK_FLAG_BIT 14 +#define RFLAGS_NESTED_TASK_FLAG_FLAG 0x4000 +#define RFLAGS_NESTED_TASK_FLAG_MASK 0x01 +#define RFLAGS_NESTED_TASK_FLAG(_) (((_) >> 14) & 0x01) + uint64_t reserved3 : 1; -struct rtn_data_t -{ - std::uint32_t rtn_rva; - std::vector< vm::instrs::code_block_t > rtn_blks; + /** + * @brief Resume flag + * + * [Bit 16] See the description in EFLAGS. + */ + uint64_t resume_flag : 1; +#define RFLAGS_RESUME_FLAG_BIT 16 +#define RFLAGS_RESUME_FLAG_FLAG 0x10000 +#define RFLAGS_RESUME_FLAG_MASK 0x01 +#define RFLAGS_RESUME_FLAG(_) (((_) >> 16) & 0x01) + + /** + * @brief Virtual-8086 mode flag + * + * [Bit 17] See the description in EFLAGS. + */ + uint64_t virtual_8086_mode_flag : 1; +#define RFLAGS_VIRTUAL_8086_MODE_FLAG_BIT 17 +#define RFLAGS_VIRTUAL_8086_MODE_FLAG_FLAG 0x20000 +#define RFLAGS_VIRTUAL_8086_MODE_FLAG_MASK 0x01 +#define RFLAGS_VIRTUAL_8086_MODE_FLAG(_) (((_) >> 17) & 0x01) + + /** + * @brief Alignment check (or access control) flag + * + * [Bit 18] See the description in EFLAGS. + * + * @see Vol3A[4.6(ACCESS RIGHTS)] + */ + uint64_t alignment_check_flag : 1; +#define RFLAGS_ALIGNMENT_CHECK_FLAG_BIT 18 +#define RFLAGS_ALIGNMENT_CHECK_FLAG_FLAG 0x40000 +#define RFLAGS_ALIGNMENT_CHECK_FLAG_MASK 0x01 +#define RFLAGS_ALIGNMENT_CHECK_FLAG(_) (((_) >> 18) & 0x01) + + /** + * @brief Virtual interrupt flag + * + * [Bit 19] See the description in EFLAGS. + */ + uint64_t virtual_interrupt_flag : 1; +#define RFLAGS_VIRTUAL_INTERRUPT_FLAG_BIT 19 +#define RFLAGS_VIRTUAL_INTERRUPT_FLAG_FLAG 0x80000 +#define RFLAGS_VIRTUAL_INTERRUPT_FLAG_MASK 0x01 +#define RFLAGS_VIRTUAL_INTERRUPT_FLAG(_) (((_) >> 19) & 0x01) + + /** + * @brief Virtual interrupt pending flag + * + * [Bit 20] See the description in EFLAGS. + */ + uint64_t virtual_interrupt_pending_flag : 1; +#define RFLAGS_VIRTUAL_INTERRUPT_PENDING_FLAG_BIT 20 +#define RFLAGS_VIRTUAL_INTERRUPT_PENDING_FLAG_FLAG 0x100000 +#define RFLAGS_VIRTUAL_INTERRUPT_PENDING_FLAG_MASK 0x01 +#define RFLAGS_VIRTUAL_INTERRUPT_PENDING_FLAG(_) (((_) >> 20) & 0x01) + + /** + * @brief Identification flag + * + * [Bit 21] See the description in EFLAGS. + */ + uint64_t identification_flag : 1; +#define RFLAGS_IDENTIFICATION_FLAG_BIT 21 +#define RFLAGS_IDENTIFICATION_FLAG_FLAG 0x200000 +#define RFLAGS_IDENTIFICATION_FLAG_MASK 0x01 +#define RFLAGS_IDENTIFICATION_FLAG(_) (((_) >> 21) & 0x01) + uint64_t reserved4 : 42; + }; + + uint64_t flags; +} rflags; + +#define ABS_TO_IMG(addr, mod_base, img_base) (addr - mod_base) + img_base +Q_DECLARE_METATYPE(vm::instrs::virt_instr_t) + +struct rtn_data_t { + std::uint32_t rtn_rva; + std::vector rtn_blks; }; -class qvm_inspector : public QMainWindow -{ - friend class qvm_virtual_instructions; - friend class qvm_handlers; - friend class qvm_virtual_routines; - Q_OBJECT - public: - qvm_inspector( QWidget *parent = Q_NULLPTR ); - - private slots: - void on_open(); - void on_close(); - - private: - void dbg_print( QString DbgOutput ); - void dbg_msg( QString DbgOutput ); - void update_ui(); - bool serialize_vmp2( std::vector< rtn_data_t > &virt_rtns ); - void update_virtual_instructions( std::uintptr_t rtn_addr, std::uintptr_t blk_addr = 0ull, - QTreeWidgetItem *parent = nullptr ); - bool init_data(); - - Ui::QVMProfilerClass ui; - std::uint64_t img_base, module_base, img_size; - vm::ctx_t *g_vm_ctx; - - vmp2::v4::file_header *file_header; - std::vector< rtn_data_t > virt_rtns; - std::vector< std::uintptr_t > code_block_addrs; +class qvm_inspector : public QMainWindow { + friend class qvm_virtual_instructions; + friend class qvm_handlers; + friend class qvm_virtual_routines; + Q_OBJECT + public: + qvm_inspector(QWidget *parent = Q_NULLPTR); + + private slots: + void on_open(); + void on_close(); + + private: + void dbg_print(QString DbgOutput); + void dbg_msg(QString DbgOutput); + void update_ui(); + bool serialize_vmp2(std::vector &virt_rtns); + void update_virtual_instructions(std::uintptr_t rtn_addr, + std::uintptr_t blk_addr = 0ull, + QTreeWidgetItem *parent = nullptr); + bool init_data(); + + Ui::QVMProfilerClass ui; + std::uint64_t img_base, module_base, img_size; + vm::ctx_t *g_vm_ctx; + + vmp2::v4::file_header *file_header; + std::vector virt_rtns; + std::vector code_block_addrs; }; \ No newline at end of file