diff --git a/dependencies/vmprofiler b/dependencies/vmprofiler index c231911..573cc04 160000 --- a/dependencies/vmprofiler +++ b/dependencies/vmprofiler @@ -1 +1 @@ -Subproject commit c2319117a07d95794f54db970aa7f72e8726349a +Subproject commit 573cc04fc1804ac328b555bb1136d132d858a6fe diff --git a/src/qvminspector.cpp b/src/qvminspector.cpp index 3e0b151..78279f2 100644 --- a/src/qvminspector.cpp +++ b/src/qvminspector.cpp @@ -19,14 +19,10 @@ void qvminspector_t::on_close() void qvminspector_t::on_open() { - if ( file_header && vmctx ) - { + if ( file_header ) free( file_header ); - delete vmctx; - } - file_header = nullptr, first_block = nullptr; - code_block_addrs.clear(); + file_header = nullptr; image_base = 0u, vm_entry_rva = 0u, module_base = 0u; file_path = QFileDialog::getOpenFileName( @@ -55,7 +51,7 @@ void qvminspector_t::on_open() } qfile_t open_file( file_path ); - file_header = reinterpret_cast< vmp2::v3::file_header * >( malloc( file_size ) ); + file_header = reinterpret_cast< vmp2::v4::file_header * >( malloc( file_size ) ); if ( !open_file.open( QIODevice::ReadOnly ) ) { @@ -70,8 +66,6 @@ void qvminspector_t::on_open() dbg_msg( "failed to init vmp2 file data..." ); return; } - - update_ui(); } void qvminspector_t::dbg_print( qstring_t dbg_output ) @@ -98,10 +92,10 @@ bool qvminspector_t::init_data() dbg_print( "valid magic bytes for vmp2 file..." ); - if ( file_header->version != vmp2::version_t::v3 ) + if ( file_header->version != vmp2::version_t::v4 ) { dbg_msg( "invalid vmp2 file version... " - "this vminspector is compiled for version 3...\n" ); + "this vminspector is compiled for version 4...\n" ); return false; } @@ -111,275 +105,59 @@ bool qvminspector_t::init_data() image_size = file_header->module_size; module_base = reinterpret_cast< std::uintptr_t >( file_header ) + file_header->module_offset; - vmctx = new vm::ctx_t( module_base, image_base, image_size, vm_entry_rva ); - - if ( !vmctx->init() ) - { - dbg_msg( "failed to init vm::ctx_t... this can happen for many reasons..." - "ensure that the vmp2 file is not corrupted...\n" ); - - return false; - } - - first_block = reinterpret_cast< vmp2::v3::code_block_t * >( reinterpret_cast< std::uintptr_t >( file_header ) + - file_header->code_block_offset ); return true; } void qvminspector_t::add_branch_children( qtree_widget_item_t *item, std::uintptr_t branch_addr ) { - if ( std::find( code_block_addrs.begin(), code_block_addrs.end(), branch_addr ) != code_block_addrs.end() ) - return; - - code_block_addrs.push_back( branch_addr ); - // for each code block find the one that starts with the desired branch... - for ( auto [ code_block, code_block_num ] = std::tuple{ first_block, 0u }; - code_block_num < file_header->code_block_count; - code_block = reinterpret_cast< vmp2::v3::code_block_t * >( reinterpret_cast< std::uintptr_t >( code_block ) + - code_block->next_block_offset ), - ++code_block_num ) - { - if ( code_block->vip_begin != branch_addr ) - continue; +} - dbg_print( qstring_t( "> code block %1 (block_%2), number of vinstrs = %3..." ) - .arg( code_block_num ) - .arg( code_block->vip_begin, 0, 16 ) - .arg( code_block->vinstr_count ) ); +void qvminspector_t::update_ui() +{ - // for each virtual instruction inside of this code block... - for ( auto idx = 0u; idx < code_block->vinstr_count; ++idx ) - { - const auto virt_instr = &code_block->vinstr[ idx ]; - const auto profile = vm::handler::get_profile( virt_instr->mnemonic_t ); - auto virt_instr_entry = new qtree_widget_item_t; - - // virtual instruction image base'ed rva... (column 1)... - virt_instr_entry->setText( - 0, QString( "block_%1+%2" ) - .arg( branch_addr, 0, 16 ) - .arg( idx ? code_block->vip_begin > - ( ( virt_instr->trace_data.vip - file_header->module_base ) + image_base ) - ? code_block->vip_begin - - ( ( virt_instr->trace_data.vip - file_header->module_base ) + image_base ) - : ( ( virt_instr->trace_data.vip - file_header->module_base ) + image_base ) - - code_block->vip_begin - : 0u, - 0, 16 ) ); - - // virtual instruction operand bytes... (column 2)... - qstring_t operand_bytes; - operand_bytes.append( QString( "%1" ).arg( virt_instr->opcode, 0, 16 ) ); - - // if virt instruction has an imm... grab its bytes... - if ( virt_instr->operand.has_imm ) - { - operand_bytes.append( " - " ); - for ( auto _idx = 0u; _idx < virt_instr->operand.imm.imm_size / 8; ++_idx ) - operand_bytes.append( QString( "%1 " ).arg( - reinterpret_cast< std::uint8_t * >( &virt_instr->operand.imm.u )[ _idx ], 0, 16 ) ); - } - - virt_instr_entry->setText( 1, operand_bytes ); - - // virtual instruction string, includes imm... (colume 3)... - QString decoded_instr( QString( "%1" ).arg( - profile ? profile->name : QString( "UNK(%1)" ).arg( virt_instr->opcode, 0, 16 ) ) ); - - if ( virt_instr->operand.has_imm ) // if there is a second operand (imm) put its value... - decoded_instr.append( QString( " %1" ).arg( virt_instr->operand.imm.u, 0, 16 ) ); - - virt_instr_entry->setText( 2, decoded_instr ); - - // add comments to the virtual instruction... (colume 4)... - if ( virt_instr->mnemonic_t == vm::handler::LREGQ || virt_instr->mnemonic_t == vm::handler::SREGQ ) - virt_instr_entry->setText( - 3, - QString( "; vreg%1" ).arg( virt_instr->operand.imm.u ? ( virt_instr->operand.imm.u / 8 ) : 0u ) ); - - QVariant var; - var.setValue( virt_instr ); - virt_instr_entry->setData( 3, Qt::UserRole, var ); - - if ( virt_instr->mnemonic_t == vm::handler::JMP ) - { - if ( code_block->jcc.type == vm::instrs::jcc_type::branching ) - { - virt_instr_entry->setText( 3, QString( "; { %1, %2 }" ) - .arg( code_block->jcc.block_addr[ 0 ], 0, 16 ) - .arg( code_block->jcc.block_addr[ 1 ], 0, 16 ) ); - - auto branch_entry1 = new qtree_widget_item_t(), branch_entry2 = new qtree_widget_item_t(); - const auto block1_addr = code_block->jcc.block_addr[ 0 ]; - const auto block2_addr = code_block->jcc.block_addr[ 1 ]; - - branch_entry1->setText( 0, QString( "%1" ).arg( block1_addr, 0, 16 ) ); - branch_entry1->setText( 3, QString( "; block_%1" ).arg( block1_addr, 0, 16 ) ); - - branch_entry2->setText( 0, QString( "%1" ).arg( block2_addr, 0, 16 ) ); - branch_entry2->setText( 3, QString( "; block_%1" ).arg( block2_addr, 0, 16 ) ); - - add_branch_children( branch_entry1, code_block->jcc.block_addr[ 0 ] ); - add_branch_children( branch_entry2, code_block->jcc.block_addr[ 1 ] ); - - virt_instr_entry->addChildren( { branch_entry1, branch_entry2 } ); - // if its a JMP with branches we want to insert the next code block - // instructions into the child widget entries... - item->addChild( virt_instr_entry ); - return; - } - else - { - virt_instr_entry->setText( 3, QString( "; { %1 }" ).arg( code_block->jcc.block_addr[ 0 ], 0, 16 ) ); - - // else if this jmp doesnt have two branches add the next code block to it... - item->addChild( virt_instr_entry ); - add_branch_children( item, code_block->jcc.block_addr[ 0 ] ); - } - } - item->addChild( virt_instr_entry ); - } - } } -void qvminspector_t::update_ui() +bool qvminspector_t::serialize_vmp2( std::vector< rtn_data_t > &virt_rtns, std::vector< std::uint8_t > &vmp2file ) { - // add vm handlers to the vm handler tree... - ui.virt_handlers_tree->clear(); - for ( auto idx = 0u; idx < vmctx->vm_handlers.size(); ++idx ) + const auto file_header = reinterpret_cast< vmp2::v4::file_header * >( vmp2file.data() ); + + if ( file_header->version != vmp2::version_t::v4 ) { - auto new_handler_entry = new qtree_widget_item_t; - new_handler_entry->setData( 0, Qt::UserRole, idx ); - new_handler_entry->setText( 0, QString( "%1" ).arg( idx ) ); - new_handler_entry->setText( - 1, QString( "%1" ).arg( - ABS_TO_IMG( vmctx->vm_handlers[ idx ].address, module_base, file_header->image_base ), 0, 16 ) ); - - new_handler_entry->setText( 2, vmctx->vm_handlers[ idx ].profile ? vmctx->vm_handlers[ idx ].profile->name - : "UNDEFINED" ); - - new_handler_entry->setText( 3, QString( "%1" ).arg( vmctx->vm_handlers[ idx ].imm_size ) ); - - if ( vmctx->vm_handlers[ idx ].profile && vmctx->vm_handlers[ idx ].imm_size ) - new_handler_entry->setText( 4, vmctx->vm_handlers[ idx ].profile->extention == - vm::handler::extention_t::sign_extend - ? "SIGN EXTENDED" - : "ZERO EXTENDED" ); - else - new_handler_entry->setText( 4, "UNDEFINED" ); - - ui.virt_handlers_tree->addTopLevelItem( new_handler_entry ); + std::printf( "[!] invalid vmp2 file version... this build uses v3...\n" ); + return false; } - ui.virt_handlers_tree->topLevelItem( 0 )->setSelected( true ); - - // for each code block insert their virtual instructions - // into the virtual instruction tree... also put meta data about the code - // block above the virtual instructions... if the code block has a JCC (with two branches) - // then make a child repeating this for loop... - - ui.virt_instrs->clear(); // clear old virtual instructions out since we are updating the UI... - for ( auto [ code_block, code_block_num ] = std::tuple{ first_block, 0u }; - code_block_num < file_header->code_block_count; - code_block = reinterpret_cast< vmp2::v3::code_block_t * >( reinterpret_cast< std::uintptr_t >( code_block ) + - code_block->next_block_offset ), - ++code_block_num ) + + 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 ) ) { - code_block_addrs.push_back( code_block->vip_begin ); - dbg_print( qstring_t( "> code block %1 (block_%2), number of vinstrs = %3..." ) - .arg( code_block_num ) - .arg( code_block->vip_begin, 0, 16 ) - .arg( code_block->vinstr_count ) ); - - // for each virtual instruction inside of this code block... - for ( auto idx = 0u; idx < code_block->vinstr_count; ++idx ) + 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 ) ) { - const auto virt_instr = &code_block->vinstr[ idx ]; - const auto profile = vm::handler::get_profile( virt_instr->mnemonic_t ); - auto virt_instr_entry = new qtree_widget_item_t; - - // virtual instruction image base'ed rva... (column 1)... - virt_instr_entry->setText( - 0, QString( "block_%1+%2" ) - .arg( code_block->vip_begin, 0, 16 ) - .arg( idx ? code_block->vip_begin > - ( ( virt_instr->trace_data.vip - file_header->module_base ) + image_base ) - ? code_block->vip_begin - - ( ( virt_instr->trace_data.vip - file_header->module_base ) + image_base ) - : ( ( virt_instr->trace_data.vip - file_header->module_base ) + image_base ) - - code_block->vip_begin - : 0u, - 0, 16 ) ); - - // virtual instruction operand bytes... (column 2)... - qstring_t operand_bytes; - operand_bytes.append( QString( "%1" ).arg( virt_instr->opcode, 0, 16 ) ); - - // if virt instruction has an imm... grab its bytes... - if ( virt_instr->operand.has_imm ) - { - operand_bytes.append( " - " ); - for ( auto _idx = 0u; _idx < virt_instr->operand.imm.imm_size / 8; ++_idx ) - operand_bytes.append( QString( "%1 " ).arg( - reinterpret_cast< std::uint8_t * >( &virt_instr->operand.imm.u )[ _idx ], 0, 16 ) ); - } - - virt_instr_entry->setText( 1, operand_bytes ); - - // virtual instruction string, includes imm... (colume 3)... - QString decoded_instr( QString( "%1" ).arg( - profile ? profile->name : QString( "UNK(%1)" ).arg( virt_instr->opcode, 0, 16 ) ) ); - - if ( virt_instr->operand.has_imm ) // if there is a second operand (imm) put its value... - decoded_instr.append( QString( " %1" ).arg( virt_instr->operand.imm.u, 0, 16 ) ); - - virt_instr_entry->setText( 2, decoded_instr ); - - // add comments to the virtual instruction... (colume 4)... - if ( virt_instr->mnemonic_t == vm::handler::LREGQ || virt_instr->mnemonic_t == vm::handler::SREGQ ) - virt_instr_entry->setText( - 3, - QString( "; vreg%1" ).arg( virt_instr->operand.imm.u ? ( virt_instr->operand.imm.u / 8 ) : 0u ) ); - - // add virt_instr_t pointer for this current virtual instruction... - QVariant var; - var.setValue( virt_instr ); - virt_instr_entry->setData( 3, Qt::UserRole, var ); - - if ( virt_instr->mnemonic_t == vm::handler::JMP ) - { - if ( code_block->jcc.type == vm::instrs::jcc_type::branching ) - { - virt_instr_entry->setText( 3, QString( "; { %1, %2 }" ) - .arg( code_block->jcc.block_addr[ 0 ], 0, 16 ) - .arg( code_block->jcc.block_addr[ 1 ], 0, 16 ) ); - - auto branch_entry1 = new qtree_widget_item_t(), branch_entry2 = new qtree_widget_item_t(); - const auto block1_addr = code_block->jcc.block_addr[ 0 ]; - const auto block2_addr = code_block->jcc.block_addr[ 1 ]; - - branch_entry1->setText( 0, QString( "%1" ).arg( block1_addr, 0, 16 ) ); - branch_entry1->setText( 3, QString( "; block_%1" ).arg( block1_addr, 0, 16 ) ); - - branch_entry2->setText( 0, QString( "%1" ).arg( block2_addr, 0, 16 ) ); - branch_entry2->setText( 3, QString( "; block_%1" ).arg( block2_addr, 0, 16 ) ); - - add_branch_children( branch_entry1, code_block->jcc.block_addr[ 0 ] ); - add_branch_children( branch_entry2, code_block->jcc.block_addr[ 1 ] ); - - virt_instr_entry->addChildren( { branch_entry1, branch_entry2 } ); - // if its a JMP with branches we want to insert the next code block - // instructions into the child widget entries... - ui.virt_instrs->addTopLevelItem( virt_instr_entry ); - goto finish; // bad code... - } - else if ( code_block->jcc.type == vm::instrs::jcc_type::absolute ) - virt_instr_entry->setText( 3, QString( "; { %1 }" ).arg( code_block->jcc.block_addr[ 0 ], 0, 16 ) ); - } - ui.virt_instrs->addTopLevelItem( virt_instr_entry ); + 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 ); } } -finish: // bad code... - ui.virt_instrs->topLevelItem( 0 )->setSelected( true ); + return true; } \ No newline at end of file diff --git a/src/qvminspector.h b/src/qvminspector.h index 5f80b65..a3a0b3c 100644 --- a/src/qvminspector.h +++ b/src/qvminspector.h @@ -26,6 +26,12 @@ using qfile_t = QFile; using qtree_widget_item_t = QTreeWidgetItem; using qmsg_box_t = QMessageBox; +struct rtn_data_t +{ + std::uint32_t rtn_rva; + std::vector< vm::instrs::code_block_t > rtn_blks; +}; + class qvminspector_t : public qmain_window_t { friend class qvirt_instrs_t; @@ -33,6 +39,7 @@ class qvminspector_t : public qmain_window_t Q_OBJECT public: qvminspector_t( qwidget_t *parent = Q_NULLPTR ); + static bool serialize_vmp2( std::vector< rtn_data_t > &virt_rtns, std::vector< std::uint8_t > &vmp2file ); private slots: void on_open(); @@ -50,8 +57,5 @@ class qvminspector_t : public qmain_window_t qstring_t VMProtectedFilePath; std::uint64_t image_base, vm_entry_rva, module_base, image_size; vm::ctx_t *vmctx; - - vmp2::v3::file_header *file_header; - vmp2::v3::code_block_t *first_block; - std::vector< std::uintptr_t > code_block_addrs; + vmp2::v4::file_header *file_header; }; \ No newline at end of file diff --git a/src/qvminspector.ui b/src/qvminspector.ui index 1561742..a108884 100644 --- a/src/qvminspector.ui +++ b/src/qvminspector.ui @@ -6,7 +6,7 @@ 0 0 - 1496 + 1414 1093 @@ -27,7 +27,7 @@ - 0 + 1 @@ -44,7 +44,7 @@ 0 0 - 1452 + 1370 854 @@ -414,7 +414,7 @@ 0 0 - 672 + 631 228 @@ -604,6 +604,111 @@ + + + Virtual Routines + + + + + + Virtual Machine Enters + + + + + + + Name + + + + + Address + + + + + Number Of Code Blocks + + + + + + + + + + + + + + + + Virtual Machine Enter Information + + + + + + Virtual Machine - Enter Instructions + + + + + + + Address + + + + + Instruction + + + + + + + + + + + + + + + + Virtual Machine - Calc Jmp + + + + + + + Address + + + + + Instruction + + + + + + + + + + + + + + + + + @@ -613,7 +718,7 @@ 0 0 - 1496 + 1414 21