From 72af7d2f9c0499574ca84ab8e5fdba4eec80e305 Mon Sep 17 00:00:00 2001 From: _xeroxz Date: Thu, 10 Jun 2021 14:47:18 -0700 Subject: [PATCH] added more cli options... --- dependencies/xtils | 2 +- src/main.cpp | 200 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 175 insertions(+), 27 deletions(-) diff --git a/dependencies/xtils b/dependencies/xtils index 7c32517..99a1fc7 160000 --- a/dependencies/xtils +++ b/dependencies/xtils @@ -1 +1 @@ -Subproject commit 7c32517322c29a866cbb1e67fb9051efa2e05553 +Subproject commit 99a1fc74e16af3261e7cfff4e03d470a7a05feb0 diff --git a/src/main.cpp b/src/main.cpp index 1db974b..5fca15d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,23 +9,17 @@ #include #include +#define ABS_TO_IMG( addr, mod_base, img_base ) ( addr - mod_base ) + img_base + int __cdecl main( int argc, const char *argv[] ) { argparse::argument_parser_t parser( "vmprofiler-cli", "virtual instruction pseudo code generator" ); - parser.add_argument() - .names( { "--bin", "--vmpbin" } ) - .description( "unpacked binary protected with VMProtect 2" ) - .required( true ); - - parser.add_argument() - .names( { "--vmentry", "--entry" } ) - .description( "rva to push prior to a vm_entry" ) - .required( true ); - + parser.add_argument().names( { "--bin", "--vmpbin" } ).description( "unpacked binary protected with VMProtect 2" ); + parser.add_argument().names( { "--vmentry", "--entry" } ).description( "rva to push prior to a vm_entry" ); parser.add_argument().name( "--showhandlers" ).description( "show all vm handlers..." ); + parser.add_argument().name( "--showhandler" ).description( "show a specific vm handler given its index..." ); parser.add_argument().name( "--vmp2file" ).description( "path to .vmp2 file..." ); - parser.add_argument().name( "--showblockinfo" ).description( "provide a block address to view its information..." ); parser.add_argument() .name( "--showblockinstrs" ) .description( "show the virtual instructions of a specific code block..." ); @@ -33,6 +27,7 @@ int __cdecl main( int argc, const char *argv[] ) parser.enable_help(); auto err = parser.parse( argc, argv ); + const auto umtils = xtils::um_t::get_instance(); if ( err ) { @@ -46,28 +41,181 @@ int __cdecl main( int argc, const char *argv[] ) return 0; } - const auto module_base = reinterpret_cast< std::uintptr_t >( - LoadLibraryExA( parser.get< std::string >( "bin" ).c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES ) ); + if ( parser.exists( "bin" ) && parser.exists( "vmentry" ) ) + { + + const auto module_base = reinterpret_cast< std::uintptr_t >( + LoadLibraryExA( parser.get< std::string >( "bin" ).c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES ) ); + + const auto vm_entry_rva = std::strtoull( parser.get< std::string >( "vmentry" ).c_str(), nullptr, 16 ); + + const auto image_base = umtils->image_base( parser.get< std::string >( "bin" ).c_str() ); + + const auto image_size = NT_HEADER( module_base )->OptionalHeader.SizeOfImage; + + std::printf( "> module base = 0x%p, image base = 0x%p, image size = 0x%p\n", module_base, image_base, + image_size ); + vm::ctx_t vmctx( module_base, image_base, image_size, vm_entry_rva ); + + if ( !vmctx.init() ) + { + std::printf( "[!] failed to init vm::ctx_t... make sure all cli arguments are correct!\n" ); + return -1; + } + + std::puts( "======================== [vm entry] ========================\n" ); + vm::util::print( vmctx.vm_entry ); + std::puts( "======================== [calc jmp] ========================\n" ); + vm::util::print( vmctx.calc_jmp ); + std::puts( "============================================================\n" ); + std::printf( "> vip advancement = %s\n\n", + vmctx.exec_type == vmp2::exec_type_t::forward ? "forward" : "backward" ); + + if ( parser.exists( "showhandlers" ) ) + { + for ( auto idx = 0u; idx < vmctx.vm_handlers.size(); ++idx ) + { + std::printf( "======================== [%s #%d] ========================\n", + vmctx.vm_handlers[ idx ].profile ? vmctx.vm_handlers[ idx ].profile->name : "UNK", idx ); + + vm::util::print( vmctx.vm_handlers[ idx ].instrs ); + + // if there is no imm then there are no transforms... + if ( !vmctx.vm_handlers[ idx ].imm_size ) + { + std::puts( "\n" ); + continue; + } + + std::puts( "======================== [transforms] ========================\n" ); + for ( auto &[ mnemonic, instr ] : vmctx.vm_handlers[ idx ].transforms ) + { + if ( instr.mnemonic == ZYDIS_MNEMONIC_INVALID ) + continue; + + vm::util::print( instr ); + } + std::puts( "\n" ); + } + } + else if ( parser.exists( "showhandler" ) ) + { + const auto vm_handler_idx = std::strtoul( parser.get< std::string >( "showhandler" ).c_str(), nullptr, 10 ); + + if ( vm_handler_idx > 256 ) + { + std::printf( "> invalid vm handler index... too large...\n" ); + return -1; + } - const auto vm_entry_rva = std::strtoull( parser.get< std::string >( "vmentry" ).c_str(), nullptr, 16 ); + std::printf( "======================== [%s #%d] ========================\n", + vmctx.vm_handlers[ vm_handler_idx ].profile ? vmctx.vm_handlers[ vm_handler_idx ].profile->name + : "UNK", + vm_handler_idx ); - const auto image_base = xtils::um_t::get_instance()->image_base( parser.get< std::string >( "bin" ).c_str() ); + vm::util::print( vmctx.vm_handlers[ vm_handler_idx ].instrs ); - const auto image_size = NT_HEADER( module_base )->OptionalHeader.SizeOfImage; + // if there is no imm then there are no transforms... + if ( !vmctx.vm_handlers[ vm_handler_idx ].imm_size ) + { + std::puts( "\n" ); + return {}; + } - std::printf( "> module base = 0x%p, image base = 0x%p, image size = 0x%p\n", module_base, image_base, image_size ); - vm::ctx_t vmctx( module_base, image_base, image_size, vm_entry_rva ); + std::puts( "======================== [transforms] ========================\n" ); + for ( auto &[ mnemonic, instr ] : vmctx.vm_handlers[ vm_handler_idx ].transforms ) + { + if ( instr.mnemonic == ZYDIS_MNEMONIC_INVALID ) + continue; - if ( !vmctx.init() ) + vm::util::print( instr ); + } + std::puts( "\n" ); + } + } + + if ( !parser.exists( "vmp2file" ) ) + return {}; + + std::vector< std::uint8_t > vmp2file; + if ( !umtils->open_binary_file( parser.get< std::string >( "vmp2file" ), vmp2file ) ) { - std::printf( "[!] failed to init vm::ctx_t... make sure all cli arguments are correct!\n" ); + std::printf( "[!] failed to open vmp2 file...\n" ); return -1; } - std::puts( "======================== [vm entry] ========================\n" ); - vm::util::print( vmctx.vm_entry ); - std::puts( "======================== [calc jmp] ========================\n" ); - vm::util::print( vmctx.calc_jmp ); - std::puts( "============================================================\n" ); - std::printf( "> vip advancement = %s\n", vmctx.exec_type == vmp2::exec_type_t::forward ? "forward" : "backward" ); + const auto file_header = reinterpret_cast< vmp2::v3::file_header * >( vmp2file.data() ); + + if ( file_header->version != vmp2::version_t::v3 ) + { + std::printf( "[!] invalid vmp2 file version... this build uses v3...\n" ); + return -1; + } + + auto first_block = reinterpret_cast< vmp2::v3::code_block_t * >( reinterpret_cast< std::uintptr_t >( file_header ) + + file_header->code_block_offset ); + + if ( parser.exists( "showallblocks" ) ) + { + 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 ) + { + + std::printf( "[code block #%d] begin = 0x%p, virtual instruction count = %d\n", code_block_num, + ABS_TO_IMG( code_block->vip_begin, file_header->module_base, file_header->image_base ), + code_block->vinstr_count ); + + if ( code_block->jcc.has_jcc ) + std::printf( + "\tcode block branches to 0x%p and 0x%p\n", + ABS_TO_IMG( code_block->jcc.block_addr[ 0 ], file_header->module_base, file_header->image_base ), + ABS_TO_IMG( code_block->jcc.block_addr[ 1 ], file_header->module_base, file_header->image_base ) ); + } + } + + if ( parser.exists( "showblockinstrs" ) ) + { + const auto block_img_addr = parser.get< std::string >( "showblockinstrs" ); + + 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 ( ABS_TO_IMG( code_block->vip_begin, file_header->module_base, file_header->image_base ) == + std::strtoull( block_img_addr.c_str(), nullptr, 16 ) ) + { + std::printf( "[code block #%d] begin = 0x%p, virtual instruction count = %d\n", code_block_num, + ABS_TO_IMG( code_block->vip_begin, file_header->module_base, file_header->image_base ), + code_block->vinstr_count ); + + std::printf( "> -----------------------------------------------------------------------\n" ); + std::printf( "> opcode | virtual instructions | virtual instruction pointer\n" ); + std::printf( "> -----------------------------------------------------------------------\n" ); + for ( auto idx = 0u; idx < code_block->vinstr_count; ++idx ) + { + const auto vinstr = &code_block->vinstr[ idx ]; + const auto vm_profile = vm::handler::get_profile( vinstr->mnemonic_t ); + if ( vinstr->operand.has_imm ) + { + std::printf( + "> %-6x | %-15s %-15p | 0x%p\n", vinstr->opcode, vm_profile ? vm_profile->name : "UNK", + vinstr->operand.imm.u, + ABS_TO_IMG( vinstr->trace_data.vip, file_header->module_base, file_header->image_base ) ); + } + else + { + std::printf( + "> %-6x | %-32s | 0x%p\n", vinstr->opcode, vm_profile ? vm_profile->name : "UNK", + ABS_TO_IMG( vinstr->trace_data.vip, file_header->module_base, file_header->image_base ) ); + } + } + break; + } + } + } } \ No newline at end of file