merged & added tests for bblock_thunk_removal_pass

This commit is contained in:
zZzetaS 2022-01-14 22:55:20 +01:00
commit 32ee99ee8d
15 changed files with 219 additions and 32 deletions

View file

@ -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")

View file

@ -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
```

View file

@ -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/*)

View file

@ -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.

View file

@ -202,7 +202,7 @@ namespace vtil
{ register_physical, X86_REG_EBP, 32 },
/*.shadow_space =*/
0x20,
0x0,
/*.purge_stack =*/
true,

View file

@ -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!)

View file

@ -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/*)

View file

@ -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/*)

View file

@ -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/*)

View file

@ -69,7 +69,7 @@ namespace vtil::symbolic
// Log the translation.
//
log<CON_BLU>( "Translating [%s] => [%s]:\n", *from, *to );
from->enum_variables( [ & ] ( const instance& ins )
from->enum_variables( [ & ] ( const directive::instance& ins )
{
log<CON_BLU>( " %s: %s\n", ins.id, *match.translate( ins ) );
} );

View file

@ -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:

View file

@ -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

View file

@ -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)

View file

@ -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);
}

View file

@ -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
#