diff --git a/CMakeLists.txt b/CMakeLists.txt index 106f388..c87b62f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,10 +4,16 @@ cmake_minimum_required(VERSION 3.14.5) # Define the VTIL project project(VTIL-Core) -option(VTIL_BUILD_TESTS "Build tests" OFF) +# Detect if VTIL-Core is compiled as the root project +set(VTIL_ROOT_PROJECT OFF) +if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + set(VTIL_ROOT_PROJECT ON) -# Enable solution folder support -set_property(GLOBAL PROPERTY USE_FOLDERS ON) + # Enable solution folder support + set_property(GLOBAL PROPERTY USE_FOLDERS ON) +endif() + +option(VTIL_BUILD_TESTS "Build tests" ${VTIL_ROOT_PROJECT}) # Load the dependencies set(CMAKE_FOLDER "VTIL-Core/Dependencies") diff --git a/README.md b/README.md index 888e76a..c464ee3 100644 --- a/README.md +++ b/README.md @@ -40,3 +40,18 @@ This repository contains the core components of the VTIL Project used across the It is currently incomplete as the initial release is not done yet, and documentation and FAQ will be within this repository and the organization website once they're done. Until the initial release, you can keep up to date with the VTIL project by checking my [personal twitter account](https://twitter.com/_can1357) or the VTIL website [vtil.org](https://vtil.org/). + +## Building (Windows) + +``` +cmake -B build +``` + +Then open `build\VTIL-Core.sln`. You can also open this folder in a CMake-compatible IDE (Visual Studio, CLion, Qt Creator, VS Code). + +## Building (Linux/Mac) + +``` +cmake -G Ninja -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build +``` diff --git a/VTIL-Architecture/CMakeLists.txt b/VTIL-Architecture/CMakeLists.txt index a884ea3..9207f67 100644 --- a/VTIL-Architecture/CMakeLists.txt +++ b/VTIL-Architecture/CMakeLists.txt @@ -1,4 +1,6 @@ -project(VTIL-Architecture) +# Extract project name from folder +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +string(REPLACE " " "_" PROJECT_NAME "${PROJECT_NAME}") file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS *.cpp *.hpp) file(GLOB_RECURSE INCLUDES CONFIGURE_DEPENDS includes/*) diff --git a/VTIL-Architecture/arch/operands.hpp b/VTIL-Architecture/arch/operands.hpp index f6f586a..6325301 100644 --- a/VTIL-Architecture/arch/operands.hpp +++ b/VTIL-Architecture/arch/operands.hpp @@ -54,6 +54,10 @@ namespace vtil { intptr_t ival; uintptr_t uval; +#if _M_X64 || __x86_64__ + int64_t i64; + uint64_t u64; +#endif }; // Number of bits it is expressed in. diff --git a/VTIL-Architecture/routine/call_convention.hpp b/VTIL-Architecture/routine/call_convention.hpp index a9c5b18..ae30be0 100644 --- a/VTIL-Architecture/routine/call_convention.hpp +++ b/VTIL-Architecture/routine/call_convention.hpp @@ -202,7 +202,7 @@ namespace vtil { register_physical, X86_REG_EBP, 32 }, /*.shadow_space =*/ - 0x20, + 0x0, /*.purge_stack =*/ true, diff --git a/VTIL-Architecture/symex/variable.cpp b/VTIL-Architecture/symex/variable.cpp index 24465a9..f89ca31 100644 --- a/VTIL-Architecture/symex/variable.cpp +++ b/VTIL-Architecture/symex/variable.cpp @@ -380,27 +380,24 @@ namespace vtil::symbolic // If vmexit, declared trashed if below or at the shadow space: // - if ( cwrite ) + if ( cwrite && cc.purge_stack) { - if ( it->base == &ins::vexit ? it.block->owner->routine_convention.purge_stack : cc.purge_stack ) - { - // Determine the limit of the stack memory owned by this routine. - // - expression limit = - tracer->trace( { it, REG_SP } ) + - it.block->sp_offset + - cc.shadow_space; + // Determine the limit of the stack memory owned by this routine. + // + expression limit = + tracer->trace( { it, REG_SP } ) + + it.block->sp_offset + + ( it->base == &ins::vexit ? 0 : cc.shadow_space ); - // Calculate the displacement, if constant below 0, declare trashed. - // - access_details details; - fill_displacement( &details, mem.base, pointer{ std::move( limit ) }, tracer, xblock ); - if ( !details.is_unknown() && ( details.bit_offset + var.bit_count() ) <= 0 ) - { - result += { .bit_offset = 0, .bit_count = var.bit_count(), .read = false, .write = true }; - return result; - } - } + // Calculate the displacement, if constant below 0, declare trashed. + // + access_details details; + fill_displacement( &details, mem.base, pointer{ std::move( limit ) }, tracer, xblock ); + if ( !details.is_unknown() && ( details.bit_offset + var.bit_count() ) <= 0 ) + { + result += { .bit_offset = 0, .bit_count = var.bit_count(), .read = false, .write = true }; + return result; + } } // Report unknown access: (TODO: Proper parsing!) diff --git a/VTIL-Common/CMakeLists.txt b/VTIL-Common/CMakeLists.txt index b4e6b28..46840f0 100644 --- a/VTIL-Common/CMakeLists.txt +++ b/VTIL-Common/CMakeLists.txt @@ -1,4 +1,6 @@ -project(VTIL-Common) +# Extract project name from folder +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +string(REPLACE " " "_" PROJECT_NAME "${PROJECT_NAME}") file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS *.cpp *.hpp) file(GLOB_RECURSE INCLUDES CONFIGURE_DEPENDS includes/*) diff --git a/VTIL-Compiler/CMakeLists.txt b/VTIL-Compiler/CMakeLists.txt index a94e92b..07abbb3 100644 --- a/VTIL-Compiler/CMakeLists.txt +++ b/VTIL-Compiler/CMakeLists.txt @@ -1,4 +1,6 @@ -project(VTIL-Compiler) +# Extract project name from folder +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +string(REPLACE " " "_" PROJECT_NAME "${PROJECT_NAME}") file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS *.cpp *.hpp) file(GLOB_RECURSE INCLUDES CONFIGURE_DEPENDS includes/*) diff --git a/VTIL-SymEx/CMakeLists.txt b/VTIL-SymEx/CMakeLists.txt index d2106db..b5da114 100644 --- a/VTIL-SymEx/CMakeLists.txt +++ b/VTIL-SymEx/CMakeLists.txt @@ -1,4 +1,6 @@ -project(VTIL-SymEx) +# Extract project name from folder +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +string(REPLACE " " "_" PROJECT_NAME "${PROJECT_NAME}") file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS *.cpp *.hpp) file(GLOB_RECURSE INCLUDES CONFIGURE_DEPENDS includes/*) diff --git a/VTIL-SymEx/directives/transformer.hpp b/VTIL-SymEx/directives/transformer.hpp index 98b0b64..3083853 100644 --- a/VTIL-SymEx/directives/transformer.hpp +++ b/VTIL-SymEx/directives/transformer.hpp @@ -69,7 +69,7 @@ namespace vtil::symbolic // Log the translation. // log( "Translating [%s] => [%s]:\n", *from, *to ); - from->enum_variables( [ & ] ( const instance& ins ) + from->enum_variables( [ & ] ( const directive::instance& ins ) { log( " %s: %s\n", ins.id, *match.translate( ins ) ); } ); diff --git a/VTIL-SymEx/expressions/expression.cpp b/VTIL-SymEx/expressions/expression.cpp index e376fdf..4f68ec2 100644 --- a/VTIL-SymEx/expressions/expression.cpp +++ b/VTIL-SymEx/expressions/expression.cpp @@ -546,6 +546,19 @@ namespace vtil::symbolic rhs.resize( value.size(), false ); break; } + case math::operator_id::shift_left: + case math::operator_id::shift_right: + { + rhs.resize( sizeof(uintptr_t), false ); + break; + } + + case math::operator_id::rotate_left: + case math::operator_id::rotate_right: + { + rhs.resize( sizeof(uintptr_t), false ); + break; + } case math::operator_id::multiply_high: case math::operator_id::multiply: case math::operator_id::divide: diff --git a/VTIL-SymEx/simplifier/simplifier.cpp b/VTIL-SymEx/simplifier/simplifier.cpp index c45c3f3..7879555 100644 --- a/VTIL-SymEx/simplifier/simplifier.cpp +++ b/VTIL-SymEx/simplifier/simplifier.cpp @@ -639,7 +639,7 @@ namespace vtil::symbolic // Log the input. // scope_padding _p( 1 ); - if ( !state::get()->padding ) log( "\n" ); + log( "\n" ); log( "[Input] = %s ", *exp ); log( "(Hash: %s)\n", exp->hash() ); #endif diff --git a/VTIL-Tests/CMakeLists.txt b/VTIL-Tests/CMakeLists.txt index de0b4e9..92cd82e 100644 --- a/VTIL-Tests/CMakeLists.txt +++ b/VTIL-Tests/CMakeLists.txt @@ -1,4 +1,6 @@ -project(VTIL-Tests) +# Extract project name from folder +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +string(REPLACE " " "_" PROJECT_NAME "${PROJECT_NAME}") file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS *.cpp *.hpp *.h) diff --git a/VTIL-Tests/dummy.cpp b/VTIL-Tests/dummy.cpp index d45ede5..3bdb5cf 100644 --- a/VTIL-Tests/dummy.cpp +++ b/VTIL-Tests/dummy.cpp @@ -28,6 +28,69 @@ DOCTEST_TEST_CASE("dummy") CHECK(1 == 1); } +DOCTEST_TEST_CASE("Expression hash") +{ + vtil::logger::log("\n\n>> %s \n", __FUNCTION__); + auto const_a = vtil::symbolic::expression{ 123 }; + auto const_b = (vtil::symbolic::expression{ 123 } + 1 - 1).simplify( true ); + CHECK( const_a.hash() == const_b.hash() ); + + auto block = vtil::basic_block::begin( 0x1234 ); + block->push( 0 ); + auto variable_a = vtil::symbolic::variable{ block->begin(), vtil::REG_FLAGS }; + auto variable_b = vtil::symbolic::variable{ block->begin(), vtil::REG_FLAGS }; + // vtil::logger::log( "variable_a: %s \n", variable_a.to_string().c_str() ); + CHECK( variable_a.hash() == variable_b.hash() ); + + + // simple shift_right + { + auto exp_a = vtil::symbolic::expression{ (uint32_t)123 } >> (uint8_t)6; + auto exp_b = vtil::symbolic::expression{ (uint32_t)123 } >> (uint32_t)6; + + exp_a = exp_a.simplify( true ); + exp_b = exp_b.simplify( true ); + + vtil::logger::log("exp_a: %s \n", exp_a.to_string().c_str()); + vtil::logger::log("exp_b: %s \n", exp_b.to_string().c_str()); + CHECK(exp_a.hash() == exp_b.hash()); + } + + // Simple const shift_right + { + auto exp_a = variable_a.to_expression() >> (uint8_t)6; + auto exp_b = variable_a.to_expression() >> (uint32_t)6; + + exp_a = exp_a.simplify(true); + exp_b = exp_b.simplify(true); + + vtil::logger::log("exp_a: %s \n", exp_a.to_string().c_str()); + vtil::logger::log("exp_b: %s \n", exp_b.to_string().c_str()); + CHECK(exp_a.hash() == exp_b.hash()); + } + + // advanced shift_right + { + // eax@6:1 + vtil::register_desc temp_6(vtil::register_local, 1, 1, 6); + auto exp_a = vtil::symbolic::variable{ block->begin(), temp_6 }.to_expression(); + exp_a.resize( vtil::arch::bit_count ); + exp_a = exp_a.simplify( true ); + vtil::logger::log( "exp_a.size: %d \n", exp_a.value.size() ); + vtil::logger::log( "exp_a: %s \n", exp_a.to_string().c_str() ); + + // eax >> 6 & 1 + vtil::register_desc temp(vtil::register_local, 1, vtil::arch::bit_count, 0); + auto exp_b = vtil::symbolic::variable{ block->begin(), temp }.to_expression(); + exp_b >>= (uint8_t)6; + exp_b &= (uint8_t)1; + exp_b = exp_b.simplify( true ); + vtil::logger::log( "exp_b.size: %d \n", exp_b.value.size() ); + vtil::logger::log( "exp_b: %s \n", exp_b.to_string().c_str() ); + CHECK(exp_a.hash() == exp_b.hash()); + } +} + DOCTEST_TEST_CASE("Optimization vtil file") { vtil::logger::log("\n\n>> %s \n", __FUNCTION__); @@ -384,7 +447,7 @@ DOCTEST_TEST_CASE("Optimization dead_code_elimination_pass") auto block3 = block1->fork( 0x3000 ); { - // mov ecx, [esp - 8] + // mov eax, [esp - 8] block3->ldd( reg_eax, vtil::REG_SP, -8 ); // sp -= 0x10 block3->shift_sp( 0x10 ); @@ -490,3 +553,80 @@ DOCTEST_TEST_CASE("Simplification") } +DOCTEST_TEST_CASE("Optimization bblock_thunk_removal_pass") +{ + vtil::logger::log("\n\n>> %s \n", __FUNCTION__); + + auto block1 = vtil::basic_block::begin((uintptr_t)0x1000); + auto rtn = block1->owner; + { + // 0x1000: js eflags@11:1 0x2000, 0x3000 + block1->js(vtil::REG_FLAGS.select(1, 11), (uintptr_t)0x2000, (uintptr_t)0x3000); + } + auto block2 = block1->fork((uintptr_t)0x2000); + { + // 0x2000: jmp 0x4000 + block2->jmp((uintptr_t)0x4000); + block2->fork((uintptr_t)0x4000); + } + auto block3 = block1->fork((uintptr_t)0x3000); + { + // 0x3000: jmp 0x4000 + block3->jmp((uintptr_t)0x4000); + block3->fork((uintptr_t)0x4000); + } + auto block4 = rtn->get_block((uintptr_t)0x4000); + { + // 0x4000: jmp 0x5000 + block4->jmp((uintptr_t)0x5000); + block4->fork((uintptr_t)0x5000); + } + auto block5 = rtn->get_block((uintptr_t)0x5000); + { + // 0x5000: vexit 0 + block5->vexit((uintptr_t)0); + } + vtil::logger::log("Before:\n"); + vtil::debug::dump(rtn); + + vtil::optimizer::bblock_thunk_removal_pass{}(rtn); + vtil::logger::log("After:\n"); + vtil::debug::dump(rtn); + + //block1 now points to block4 + auto ins = (*block1)[0]; + CHECK(ins.base == &vtil::ins::jmp); + CHECK(ins.operands.size() == 1); + CHECK(ins.operands[0].is_immediate()); + CHECK(ins.operands[0].imm().ival == block4->entry_vip); + + //block4 still points to block5 + ins = (*block4)[0]; + CHECK(ins.base == &vtil::ins::jmp); + CHECK(ins.operands.size() == 1); + CHECK(ins.operands[0].is_immediate()); + CHECK(ins.operands[0].imm().ival == block5->entry_vip); + + //block5 is still vexit + ins = (*block5)[0]; + CHECK(ins.base == &vtil::ins::vexit); + CHECK(block5->size() == 1); + + // Simulate another pass + // + vtil::optimizer::bblock_thunk_removal_pass{}(rtn); + vtil::logger::log("After secondary:\n"); + vtil::debug::dump(rtn); + + //block1 now points to block5 + ins = (*block1)[0]; + CHECK(ins.base == &vtil::ins::jmp); + CHECK(ins.operands.size() == 1); + CHECK(ins.operands[0].is_immediate()); + CHECK(ins.operands[0].imm().ival == block5->entry_vip); + + //block5 is still vexit + ins = (*block5)[0]; + CHECK(ins.base == &vtil::ins::vexit); + CHECK(block5->size() == 1); +} \ No newline at end of file diff --git a/VTIL/CMakeLists.txt b/VTIL/CMakeLists.txt index 25ba23f..b5f6b4f 100644 --- a/VTIL/CMakeLists.txt +++ b/VTIL/CMakeLists.txt @@ -1,4 +1,6 @@ -project(VTIL) +# Extract project name from folder +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +string(REPLACE " " "_" PROJECT_NAME "${PROJECT_NAME}") # This builds no sources -- it simply aliases a collection of all other targets created by VTIL #