diff --git a/.gitmodules b/.gitmodules index 676e438..fddc4f0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,10 +3,4 @@ url = https://githacks.org/vmp2/vmprofiler.git [submodule "dependencies/cli-parser"] path = dependencies/cli-parser - url = https://githacks.org/_xeroxz/cli-parser.git -[submodule "dependencies/xtils"] - path = dependencies/xtils - url = https://githacks.org/_xeroxz/xtils.git -[submodule "dependencies/linux-pe"] - path = dependencies/linux-pe - url = https://github.com/can1357/linux-pe.git + url = https://githacks.org/_xeroxz/cli-parser.git \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 3620be9..5efd68b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,8 +41,6 @@ set(vmprofiler-cli_SOURCES "") list(APPEND vmprofiler-cli_SOURCES "src/main.cpp" - "src/vmlocate.cpp" - "include/vmlocate.hpp" "src/icon.rc" ) @@ -64,10 +62,6 @@ endif() source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${vmprofiler-cli_SOURCES}) -target_include_directories(vmprofiler-cli PRIVATE - include -) - target_link_libraries(vmprofiler-cli PRIVATE cli-parser xtils diff --git a/cmake.toml b/cmake.toml index 591d6b5..121a501 100644 --- a/cmake.toml +++ b/cmake.toml @@ -7,13 +7,8 @@ name = "vmprofiler-cli" type = "executable" sources = [ "src/main.cpp", - "src/vmlocate.cpp", - "include/vmlocate.hpp", "src/icon.rc" ] -include-directories = [ - "include", -] link-libraries = [ "cli-parser", "xtils", diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index 3fb13b1..deaa481 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -34,43 +34,3 @@ target_include_directories(cli-parser INTERFACE unset(CMKR_TARGET) unset(CMKR_SOURCES) -# Target linux-pe -set(CMKR_TARGET linux-pe) -set(linux-pe_SOURCES "") - -set(CMKR_SOURCES ${linux-pe_SOURCES}) -add_library(linux-pe INTERFACE) - -if(linux-pe_SOURCES) - target_sources(linux-pe INTERFACE ${linux-pe_SOURCES}) -endif() - -target_include_directories(linux-pe INTERFACE - "linux-pe/includes/" -) - -unset(CMKR_TARGET) -unset(CMKR_SOURCES) - -# Target xtils -set(CMKR_TARGET xtils) -set(xtils_SOURCES "") - -set(CMKR_SOURCES ${xtils_SOURCES}) -add_library(xtils INTERFACE) - -if(xtils_SOURCES) - target_sources(xtils INTERFACE ${xtils_SOURCES}) -endif() - -target_compile_definitions(xtils INTERFACE - UNICODE -) - -target_include_directories(xtils INTERFACE - xtils -) - -unset(CMKR_TARGET) -unset(CMKR_SOURCES) - diff --git a/dependencies/cmake.toml b/dependencies/cmake.toml index e19d630..9268df5 100644 --- a/dependencies/cmake.toml +++ b/dependencies/cmake.toml @@ -2,13 +2,4 @@ [target.cli-parser] type = "interface" -include-directories = ["cli-parser"] - -[target.linux-pe] -type = "interface" -include-directories = ["linux-pe/includes/"] - -[target.xtils] -type = "interface" -include-directories = ["xtils"] -compile-definitions = ["UNICODE"] \ No newline at end of file +include-directories = ["cli-parser"] \ No newline at end of file diff --git a/dependencies/linux-pe b/dependencies/linux-pe deleted file mode 160000 index db2b7af..0000000 --- a/dependencies/linux-pe +++ /dev/null @@ -1 +0,0 @@ -Subproject commit db2b7af6e6beae1bc391ff8f8e5c97b963dc3258 diff --git a/dependencies/vmprofiler b/dependencies/vmprofiler index 7240a2a..16aeb2d 160000 --- a/dependencies/vmprofiler +++ b/dependencies/vmprofiler @@ -1 +1 @@ -Subproject commit 7240a2a23c7d789d5973de04458c7ebca16a81d0 +Subproject commit 16aeb2d6d48c4822b89497ad660911eb0f5e54bd diff --git a/dependencies/xtils b/dependencies/xtils deleted file mode 160000 index fdcafdb..0000000 --- a/dependencies/xtils +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fdcafdbbcb3f34c33b9cffb2be569b9aa5f42a57 diff --git a/include/vmlocate.hpp b/include/vmlocate.hpp deleted file mode 100644 index da957e5..0000000 --- a/include/vmlocate.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include -#include -#include -#include - -#define ABS_TO_IMG( addr, mod_base, img_base ) ( addr - mod_base ) + img_base -#define LEA_R12_SIG "\x4C\x8D\x25\x00\x00\x00\x00" -#define LEA_R12_MASK "xxx????" - -#define PUSH_4B_IMM "\x68\x00\x00\x00\x00" -#define PUSH_4B_MASK "x????" - -namespace vm::locate -{ - struct vm_handler_table_info_t - { - std::uint32_t rva, lea_r12_rva; - zydis_decoded_instr_t lea_r12_instr; - }; - - std::vector< vm_handler_table_info_t > all_handler_tables( std::uintptr_t module_base ); - std::vector< std::pair< std::uint32_t, std::uint32_t > > all_vm_enters( - std::uintptr_t module_base, std::vector< vm_handler_table_info_t > &vm_handler_tables ); -} // namespace vm::locate \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 57097bd..4218f1a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,10 +3,9 @@ #include #include #include -#include #include -#include +#include int __cdecl main( int argc, const char *argv[] ) { @@ -27,9 +26,6 @@ int __cdecl main( int argc, const char *argv[] ) .name( "--showblockinstrs" ) .description( "show the virtual instructions of a specific code block..." ); parser.add_argument().name( "--showallblocks" ).description( "shows all information for all code blocks..." ); - parser.add_argument() - .name( "--devirt" ) - .description( "lift to VTIL IR and apply optimizations, then display the output..." ); parser.enable_help(); auto err = parser.parse( argc, argv ); @@ -222,63 +218,6 @@ int __cdecl main( int argc, const char *argv[] ) } } - if ( parser.exists( "devirt" ) ) - { - vtil::basic_block *rtn = nullptr, *first = nullptr; - 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 ( !rtn ) - { - rtn = vtil::basic_block::begin( - ABS_TO_IMG( code_block->vip_begin, file_header->module_base, file_header->image_base ) ); - } - else - { - first = rtn; - - rtn = - rtn->fork( ABS_TO_IMG( code_block->vip_begin, file_header->module_base, file_header->image_base ) ); - } - - for ( auto idx = 0u; idx < code_block->vinstr_count; ++idx ) - { - auto vinstr = &code_block->vinstr[ idx ]; - if ( vinstr->mnemonic_t == vm::handler::INVALID ) - { - std::printf( "[WARNING] vm handler #%d is not implimented, the VTIL output may be incorrect!\n", - vinstr->opcode ); - rtn->nop(); - continue; - } - - const auto result = std::find_if( - vm::lifters::all.begin(), vm::lifters::all.end(), - [ & ]( vm::lifters::lifter_t *lifter ) -> bool { return lifter->first == vinstr->mnemonic_t; } ); - - if ( result == vm::lifters::all.end() ) - { - std::printf( - "[WARNING] vm handler #%d lifter is not implimented, the VTIL output may be incorrect!\n", - vinstr->opcode ); - - rtn->nop(); - continue; - } - - // lift the virtual instruction... - ( *result )->second( rtn, vinstr, code_block ); - } - } - - vtil::optimizer::apply_all( first ); - vtil::debug::dump( first ); - return {}; - } - if ( parser.exists( "showblockinstrs" ) ) { const auto block_img_addr = parser.get< std::string >( "showblockinstrs" ); diff --git a/src/vmlocate.cpp b/src/vmlocate.cpp deleted file mode 100644 index 7a64720..0000000 --- a/src/vmlocate.cpp +++ /dev/null @@ -1,264 +0,0 @@ -#include - -namespace vm::locate -{ - std::vector< vm_handler_table_info_t > all_handler_tables( std::uintptr_t module_base ) - { - std::vector< vm_handler_table_info_t > result; - auto module_info = reinterpret_cast< win::image_t<> * >( module_base ); - auto sections = module_info->get_nt_headers()->get_sections(); - auto num_sections = module_info->get_file_header()->num_sections; - auto umtils = xtils::um_t::get_instance(); - - static const auto lea_r12_validate = []( std::uintptr_t addr ) -> bool { - ZydisDecodedInstruction instr; - ZydisDecoder decoder; - ZydisRegister jmp_reg = ZYDIS_REGISTER_NONE; - ZydisDecoderInit( &decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64 ); - - unsigned instr_count = 0u; - bool found_table_idx = false, found_valid_jmp = false; - - while ( ZYAN_SUCCESS( - ZydisDecoderDecodeBuffer( &decoder, reinterpret_cast< void * >( addr ), 0x1000, &instr ) ) ) - { - ++instr_count; - if ( instr_count >= 0x1000 || - instr.mnemonic == ZYDIS_MNEMONIC_INVALID ) // prevent run offs and misalignment... - break; - - // determine if we are looking at a JMP RCX/JMP RDX... - if ( vm::util::is_jmp( instr ) ) - { - if ( instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER && - ( instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RDX || - instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RCX ) ) - { - if ( jmp_reg == ZYDIS_REGISTER_NONE || jmp_reg != instr.operands[ 0 ].reg.value ) - break; - - // else we set it to true and break... - found_valid_jmp = true; - break; - } - - // take JCC branch no matter what... - ZydisCalcAbsoluteAddress( &instr, &instr.operands[ 0 ], addr, &addr ); - - // dont execute anymore address advancement code... - continue; - } - // else if the instruction is a MOV RDX/RCX, [R12+RAX*0x8]... we know this is an index into - // the vm handler table and thus this lea r12, xxxx is probably legit... - else if ( instr.mnemonic == ZYDIS_MNEMONIC_MOV && - instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER && - ( instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RDX || - instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RCX ) && - instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_MEMORY && - instr.operands[ 1 ].mem.base == ZYDIS_REGISTER_R12 && - instr.operands[ 1 ].mem.index == ZYDIS_REGISTER_RAX && instr.operands[ 1 ].mem.scale == 0x8 ) - { - found_table_idx = true; - jmp_reg = instr.operands[ 0 ].reg.value; - } - else if ( instr.mnemonic == ZYDIS_MNEMONIC_RET || instr.mnemonic == ZYDIS_MNEMONIC_CALL ) - break; - - // advance the instruction address "addr"... - addr += instr.length; - } - - return found_table_idx && found_valid_jmp; - }; - - ZydisDecodedInstruction instr; - ZydisDecoder decoder; - ZydisDecoderInit( &decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64 ); - - for ( auto idx = 0u; idx < num_sections; ++idx ) - { - // if the section is executable and not discardable... scan it for lea r12, xxxx and ensure its loading - // a virtual machine handler table into r12... - if ( sections[ idx ].characteristics.mem_execute && !sections[ idx ].characteristics.mem_discardable ) - { - void *scan_result = reinterpret_cast< void * >( sections[ idx ].virtual_address + module_base ); - - do - { - // compute how far away from the beginning of the section we are... - auto section_size = reinterpret_cast< std::uintptr_t >( scan_result ) - - ( sections[ idx ].virtual_address + module_base ); - - // scan from the last scans result to the end of the section for lea r12's... - scan_result = umtils->sigscan( scan_result, sections[ idx ].virtual_size - section_size, - LEA_R12_SIG, LEA_R12_MASK ); - - if ( scan_result ) - { - // check to see if we are looking at: - // 1.) a legit "lea r12, xxxx" and not a misaligned instruction... - // 2.) if the instruction stream is followed in zydis it ends with a jmp rcx/jmp rdx... - if ( lea_r12_validate( reinterpret_cast< std::uintptr_t >( scan_result ) ) ) - { - if ( ZYAN_SUCCESS( ZydisDecoderDecodeBuffer( - &decoder, scan_result, sections[ idx ].virtual_size - section_size, &instr ) ) ) - { - vm_handler_table_info_t vm_handler_table_info; - vm_handler_table_info.rva = - ( instr.operands[ 1 ].mem.disp.value + - reinterpret_cast< std::uintptr_t >( scan_result ) + instr.length ) - - module_base; - - vm_handler_table_info.lea_r12_rva = - reinterpret_cast< std::uintptr_t >( scan_result ) - module_base; - - vm_handler_table_info.lea_r12_instr = instr; - result.push_back( vm_handler_table_info ); - } - } - - scan_result = reinterpret_cast< void * >( reinterpret_cast< std::uintptr_t >( scan_result ) + - sizeof LEA_R12_SIG ); - } - - } while ( scan_result ); - } - } - return result; - } - - std::vector< std::pair< std::uint32_t, std::uint32_t > > all_vm_enters( - std::uintptr_t module_base, std::vector< vm_handler_table_info_t > &vm_handler_tables ) - { - std::vector< std::pair< std::uint32_t, std::uint32_t > > result; - auto module_info = reinterpret_cast< win::image_t<> * >( module_base ); - auto sections = module_info->get_nt_headers()->get_sections(); - auto num_sections = module_info->get_file_header()->num_sections; - auto umtils = xtils::um_t::get_instance(); - auto module_end = module_base + module_info->get_nt_headers()->optional_header.size_image; - - static const auto validate_vm_enter = [ & ]( std::uintptr_t addr ) -> bool { - ZydisDecodedInstruction instr; - ZydisDecoder decoder; - ZydisDecoderInit( &decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64 ); - - unsigned instr_count = 0u; - bool found_valid_jmp = false; - std::vector< ZydisDecodedInstruction > instr_stream; - - while ( addr >= module_base && addr < module_end && - ZYAN_SUCCESS( - ZydisDecoderDecodeBuffer( &decoder, reinterpret_cast< void * >( addr ), 0x1000, &instr ) ) ) - { - if ( !instr_count && instr.mnemonic != ZYDIS_MNEMONIC_PUSH ) - break; - - ++instr_count; // handle run offs and misaligned instructions... - if ( instr_count > 500 || instr.mnemonic == ZYDIS_MNEMONIC_INVALID || - instr.mnemonic == ZYDIS_MNEMONIC_RET || instr.mnemonic == ZYDIS_MNEMONIC_CALL ) - return false; - - // determine if we are looking at a JMP RCX/JMP RDX... - if ( vm::util::is_jmp( instr ) ) - { - if ( instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER && - ( instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RDX || - instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RCX ) ) - { - - // else we set it to true and break... - found_valid_jmp = true; - instr_stream.push_back( instr ); - break; - } - - // take JCC branch no matter what... - ZydisCalcAbsoluteAddress( &instr, &instr.operands[ 0 ], addr, &addr ); - - // dont execute anymore address advancement code... - continue; - } - - instr_stream.push_back( instr ); - addr += instr.length; - } - - if ( !found_valid_jmp ) - return false; - - // second instruction in the flattened stream should be a push... - // this is also an optimization so we dont have to hit that 0^2 std::find_if every time... - if ( instr_stream[ 1 ].mnemonic != ZYDIS_MNEMONIC_PUSH ) - return false; - - if ( std::find_if( instr_stream.begin() + 1, instr_stream.end(), - [ & ]( const ZydisDecodedInstruction &instr ) { - return instr.mnemonic == ZYDIS_MNEMONIC_PUSH && - instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; - } ) == instr_stream.end() ) - return false; - - // scan over the instruction stream to see if it contains an lea r12, xxxx which is a known vm handler table - // load into r12... this is O^2 and very slow... whatever... - return std::find_if( - instr_stream.begin(), instr_stream.end(), [ & ]( const ZydisDecodedInstruction &instr ) { - return instr.mnemonic == ZYDIS_MNEMONIC_LEA && - instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_MEMORY && - std::find_if( vm_handler_tables.begin(), vm_handler_tables.end(), - [ & ]( const vm_handler_table_info_t &table_info ) { - return table_info.lea_r12_instr.operands[ 1 ].mem.disp.value == - instr.operands[ 1 ].mem.disp.value; - } ) != vm_handler_tables.end(); - } ) != instr_stream.end(); - }; - - ZydisDecodedInstruction instr; - ZydisDecoder decoder; - ZydisDecoderInit( &decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64 ); - - // for each section... - for ( auto idx = 0u; idx < num_sections; ++idx ) - { - // we are only interested in executable (non-discardable) sections... - if ( sections[ idx ].characteristics.mem_execute && !sections[ idx ].characteristics.mem_discardable ) - { - void *scan_result = reinterpret_cast< void * >( sections[ idx ].virtual_address + module_base ); - - do - { - // compute how far away from the beginning of the section we are... - auto section_size = reinterpret_cast< std::uintptr_t >( scan_result ) - - ( sections[ idx ].virtual_address + module_base ); - - if ( section_size > sections[ idx ].virtual_size ) - break; - - scan_result = umtils->sigscan( scan_result, sections[ idx ].virtual_size - section_size, - PUSH_4B_IMM, PUSH_4B_MASK ); - - if ( scan_result ) - { - if ( validate_vm_enter( reinterpret_cast< std::uintptr_t >( scan_result ) ) && - ZYAN_SUCCESS( ZydisDecoderDecodeBuffer( &decoder, scan_result, 0x1000, &instr ) ) ) - { - if ( std::find_if( result.begin(), result.end(), - [ & ]( const std::pair< std::uint32_t, std::uint32_t > &info ) { - return info.second == instr.operands[ 0 ].imm.value.u; - } ) == result.end() ) - { - result.push_back( { reinterpret_cast< std::uintptr_t >( scan_result ) - module_base, - instr.operands[ 0 ].imm.value.u } ); - } - } - - scan_result = reinterpret_cast< void * >( reinterpret_cast< std::uintptr_t >( scan_result ) + - sizeof PUSH_4B_IMM ); - } - - } while ( scan_result ); - } - } - - return result; - } -} // namespace vm::locate \ No newline at end of file