2020-06-09 05:32:27 +02:00
|
|
|
// Copyright (c) 2020 Can Boluk and contributors of the VTIL Project
|
|
|
|
|
// All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
|
// modification, are permitted provided that the following conditions are met:
|
|
|
|
|
//
|
|
|
|
|
// 1. Redistributions of source code must retain the above copyright notice,
|
|
|
|
|
// this list of conditions and the following disclaimer.
|
|
|
|
|
// 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
|
|
|
// documentation and/or other materials provided with the distribution.
|
2020-07-09 02:43:51 +02:00
|
|
|
// 3. Neither the name of VTIL Project nor the names of its contributors
|
|
|
|
|
// may be used to endorse or promote products derived from this software
|
|
|
|
|
// without specific prior written permission.
|
2020-06-09 05:32:27 +02:00
|
|
|
//
|
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
//
|
|
|
|
|
#include "auxiliaries.hpp"
|
|
|
|
|
#include <vtil/io>
|
|
|
|
|
#include <vtil/math>
|
|
|
|
|
|
|
|
|
|
namespace vtil::optimizer::aux
|
|
|
|
|
{
|
|
|
|
|
// Helper to check if the expression given is block-local.
|
|
|
|
|
//
|
|
|
|
|
bool is_local( const symbolic::expression& ex )
|
|
|
|
|
{
|
|
|
|
|
// Allocate temporary result and enumerate each unique identifier
|
|
|
|
|
// by passing lambda as eval lookup helper.
|
|
|
|
|
//
|
|
|
|
|
bool result = false;
|
|
|
|
|
ex.evaluate( [ & ] ( const symbolic::unique_identifier& uid )
|
|
|
|
|
{
|
|
|
|
|
// If variable is register, check if local, else recurse into the pointer.
|
|
|
|
|
//
|
|
|
|
|
auto& var = uid.get<symbolic::variable>();
|
|
|
|
|
if ( var.is_memory() )
|
2020-07-12 14:17:46 +02:00
|
|
|
result |= is_local( *var.mem().decay() );
|
2020-06-09 05:32:27 +02:00
|
|
|
else
|
|
|
|
|
result |= var.reg().is_local();
|
|
|
|
|
|
|
|
|
|
// Return dummy result.
|
|
|
|
|
//
|
|
|
|
|
return 0ull;
|
|
|
|
|
} );
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Helper to check if the current value stored in the variable is used by the routine.
|
|
|
|
|
// TODO: Doesnt discard based on block offset!
|
|
|
|
|
//
|
|
|
|
|
bool is_used( const symbolic::variable& var, bool rec, tracer* tracer )
|
|
|
|
|
{
|
|
|
|
|
// Assert variable is properly bound.
|
|
|
|
|
//
|
|
|
|
|
fassert( var.at.is_valid() );
|
|
|
|
|
|
|
|
|
|
// Save original recursion restriction.
|
|
|
|
|
//
|
|
|
|
|
bool is_restricted = !rec;
|
|
|
|
|
|
|
|
|
|
// Declare a relative pointer tracer.
|
|
|
|
|
//
|
|
|
|
|
auto rel_ptr = [ & ] ( symbolic::variable lookup )
|
|
|
|
|
{
|
|
|
|
|
// Restrict iteration upto origin and forward to rtrace.
|
|
|
|
|
//
|
2020-07-29 02:36:10 +02:00
|
|
|
lookup.at.restrict_path( var.at.block, false );
|
2020-08-23 10:49:49 +02:00
|
|
|
return rec ? tracer->rtrace_p( std::move( lookup ) ) : tracer->trace_p( std::move( lookup ) );
|
2020-06-09 05:32:27 +02:00
|
|
|
};
|
|
|
|
|
|
2020-07-11 12:51:47 +02:00
|
|
|
// If at the end of improperly terminated block, declare used.
|
2020-07-09 08:34:14 +02:00
|
|
|
//
|
|
|
|
|
constexpr auto is_improper_end = [ ] ( const il_const_iterator& it )
|
|
|
|
|
{
|
2020-07-29 02:36:10 +02:00
|
|
|
return std::next( it ).is_end() && it.block->next.empty() && it->base != &ins::vexit;
|
2020-07-09 08:34:14 +02:00
|
|
|
};
|
|
|
|
|
if( is_improper_end( var.at ) )
|
|
|
|
|
return true;
|
|
|
|
|
|
2020-06-09 05:32:27 +02:00
|
|
|
// If memory variable:
|
|
|
|
|
//
|
|
|
|
|
if ( var.is_memory() )
|
|
|
|
|
{
|
|
|
|
|
// If it can't be simplified into $sp + C, assume used.
|
|
|
|
|
//
|
2020-07-12 14:17:46 +02:00
|
|
|
std::optional delta_o = var.mem().decay()->evaluate( [ ] ( const symbolic::unique_identifier& uid )
|
2020-07-11 12:25:03 +02:00
|
|
|
-> std::optional<uint64_t>
|
2020-06-09 05:32:27 +02:00
|
|
|
{
|
|
|
|
|
auto var = uid.get<symbolic::variable>();
|
|
|
|
|
if ( var.is_register() && var.reg().is_stack_pointer() )
|
|
|
|
|
return 0ull;
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
} ).get<true>();
|
|
|
|
|
if ( !delta_o ) return true;
|
2020-07-11 12:51:47 +02:00
|
|
|
// TODO: Trace pointer.
|
2020-07-11 12:25:03 +02:00
|
|
|
}
|
2020-06-09 05:32:27 +02:00
|
|
|
|
2020-07-11 12:25:03 +02:00
|
|
|
// Declare iteration logic.
|
|
|
|
|
//
|
|
|
|
|
bool is_used = false;
|
|
|
|
|
bool is_nr_dead = false;
|
2020-07-11 12:51:47 +02:00
|
|
|
uint64_t mask_0 = math::fill( var.bit_count() );
|
2020-07-11 12:25:03 +02:00
|
|
|
auto enumerator = [ &, mask = mask_0, skip_count = 0, local_var = var ]( const il_const_iterator& it ) mutable
|
|
|
|
|
{
|
|
|
|
|
const auto declare_used = [ & ] ()
|
|
|
|
|
{
|
|
|
|
|
is_used = true;
|
|
|
|
|
return enumerator::obreak_r;
|
|
|
|
|
};
|
2020-06-09 05:32:27 +02:00
|
|
|
|
2020-07-11 12:25:03 +02:00
|
|
|
// Skip first instruction.
|
2020-06-09 05:32:27 +02:00
|
|
|
//
|
2020-07-11 12:25:03 +02:00
|
|
|
if ( skip_count++ == 0 )
|
|
|
|
|
return enumerator::ocontinue;
|
|
|
|
|
|
|
|
|
|
// If memory variable:
|
|
|
|
|
//
|
|
|
|
|
if ( var.is_memory() )
|
|
|
|
|
{
|
|
|
|
|
// Propagate pointer if needed.
|
|
|
|
|
//
|
2020-07-29 02:36:10 +02:00
|
|
|
if ( local_var.at.block != it.block )
|
2020-06-09 05:32:27 +02:00
|
|
|
{
|
2020-07-29 02:36:10 +02:00
|
|
|
if ( local_var.at.block->sp_index == 0 )
|
2020-06-09 05:32:27 +02:00
|
|
|
{
|
2020-07-12 14:17:46 +02:00
|
|
|
symbolic::expression exp =
|
|
|
|
|
local_var.mem().decay()
|
2020-07-29 02:36:10 +02:00
|
|
|
- local_var.at.block->sp_offset
|
2022-01-25 19:10:27 +08:00
|
|
|
+ symbolic::variable{ it.block->begin(), REG_SP }.to_expression()
|
2020-07-29 02:36:10 +02:00
|
|
|
- symbolic::variable{ local_var.at.block->begin(), REG_SP }.to_expression();
|
|
|
|
|
local_var = symbolic::variable{ it.block->begin(), { symbolic::pointer{ exp }, local_var.mem().bit_count } };
|
2020-06-09 05:32:27 +02:00
|
|
|
}
|
2020-07-11 12:25:03 +02:00
|
|
|
}
|
2020-06-09 05:32:27 +02:00
|
|
|
|
2020-07-11 12:25:03 +02:00
|
|
|
// If instruction is branching, check if stack is discarded.
|
|
|
|
|
//
|
2020-07-29 02:36:10 +02:00
|
|
|
if ( it->base->is_branching() && it.block->owner->routine_convention.purge_stack )
|
2020-06-09 05:32:27 +02:00
|
|
|
{
|
2020-07-11 12:25:03 +02:00
|
|
|
// Assert this instruction does not read memory.
|
2020-06-09 05:32:27 +02:00
|
|
|
//
|
2020-07-11 12:25:03 +02:00
|
|
|
fassert( !it->base->reads_memory() );
|
2020-06-09 05:32:27 +02:00
|
|
|
|
2020-07-11 12:25:03 +02:00
|
|
|
// Determine the displacement between high write and discarded limit.
|
2020-06-09 05:32:27 +02:00
|
|
|
//
|
2020-07-11 12:25:03 +02:00
|
|
|
symbolic::expression write_high = local_var.mem().decay() + ( local_var.mem().bit_count / 8 );
|
|
|
|
|
symbolic::expression discard_limit = rel_ptr( { it, REG_SP } ) + it->sp_offset;
|
|
|
|
|
std::optional disp = ( write_high - discard_limit ).get<true>();
|
2020-06-09 05:32:27 +02:00
|
|
|
|
2020-07-11 12:25:03 +02:00
|
|
|
// If displacement is an immediate value and is below 0, declare discarded.
|
2020-06-09 05:32:27 +02:00
|
|
|
//
|
2020-07-11 12:25:03 +02:00
|
|
|
if ( disp && *disp <= 0 )
|
2020-06-10 01:43:24 +02:00
|
|
|
{
|
2020-07-11 12:25:03 +02:00
|
|
|
mask = 0;
|
|
|
|
|
is_nr_dead = true;
|
|
|
|
|
return enumerator::obreak;
|
2020-06-10 01:43:24 +02:00
|
|
|
}
|
2020-07-11 12:25:03 +02:00
|
|
|
// TODO: Partial discarding??
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-10 01:43:24 +02:00
|
|
|
|
2020-07-11 12:25:03 +02:00
|
|
|
// Check if variable is accessed by this instruction.
|
2020-06-09 05:32:27 +02:00
|
|
|
//
|
2020-07-13 00:52:03 +02:00
|
|
|
if ( auto details = local_var.accessed_by( it, tracer, !is_restricted ) )
|
2020-07-11 12:25:03 +02:00
|
|
|
{
|
2020-07-13 00:52:03 +02:00
|
|
|
// If possible read, declare used.
|
2020-07-11 12:25:03 +02:00
|
|
|
//
|
2020-07-13 00:52:03 +02:00
|
|
|
if ( details.read )
|
|
|
|
|
{
|
|
|
|
|
if ( details.is_unknown() || ( mask & math::fill( details.bit_count, details.bit_offset ) ) )
|
|
|
|
|
return declare_used();
|
|
|
|
|
}
|
|
|
|
|
// If known overwrite:
|
|
|
|
|
//
|
|
|
|
|
else if ( details.write && !details.is_unknown() )
|
|
|
|
|
{
|
|
|
|
|
// Clear the mask.
|
|
|
|
|
//
|
|
|
|
|
mask &= ~math::fill( details.bit_count, details.bit_offset );
|
|
|
|
|
}
|
2020-07-11 12:25:03 +02:00
|
|
|
}
|
2020-06-09 05:32:27 +02:00
|
|
|
|
2020-07-11 12:25:03 +02:00
|
|
|
// Break if value is dead.
|
2020-06-09 05:32:27 +02:00
|
|
|
//
|
2020-07-11 12:25:03 +02:00
|
|
|
if ( !mask )
|
|
|
|
|
{
|
|
|
|
|
is_nr_dead = true;
|
|
|
|
|
return enumerator::obreak;
|
|
|
|
|
}
|
2020-06-13 22:32:25 +02:00
|
|
|
|
2020-07-11 12:25:03 +02:00
|
|
|
// If improperly terminated block, declare used, else skip.
|
|
|
|
|
//
|
|
|
|
|
return is_improper_end( it ) ? declare_used() : enumerator::ocontinue;
|
|
|
|
|
};
|
2020-06-09 05:32:27 +02:00
|
|
|
|
2020-07-11 12:25:03 +02:00
|
|
|
// Invoke the enumerator.
|
2020-06-09 05:32:27 +02:00
|
|
|
//
|
2020-07-29 20:33:29 +02:00
|
|
|
auto it = var.at;
|
|
|
|
|
if ( is_restricted ) it.restrict_path();
|
2020-07-29 02:36:10 +02:00
|
|
|
var.at.block->owner->enumerate(
|
2020-07-11 12:25:03 +02:00
|
|
|
enumerator,
|
2020-07-29 20:33:29 +02:00
|
|
|
it
|
2020-07-11 12:25:03 +02:00
|
|
|
);
|
2020-06-09 05:55:41 +02:00
|
|
|
|
2020-07-11 12:25:03 +02:00
|
|
|
// If found an instruction reading the value, indicate so.
|
|
|
|
|
//
|
|
|
|
|
if ( is_used )
|
|
|
|
|
return true;
|
2020-06-09 05:32:27 +02:00
|
|
|
|
2020-07-11 12:25:03 +02:00
|
|
|
// If query was not restricted or if mask is dead, declare not-used.
|
|
|
|
|
//
|
|
|
|
|
if ( !is_restricted || is_nr_dead )
|
2020-06-09 05:32:27 +02:00
|
|
|
return false;
|
2020-07-11 12:25:03 +02:00
|
|
|
|
|
|
|
|
// Report used if global register and block is not exiting vm.
|
|
|
|
|
//
|
2020-07-29 02:36:10 +02:00
|
|
|
return ( !var.at.block->is_complete() || var.at.block->back().base != &ins::vexit ) &&
|
2020-07-11 12:25:03 +02:00
|
|
|
( !var.is_register() || var.reg().is_global() );
|
2020-06-09 05:32:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Helper to check if the given symbolic variable's value is preserved upto [dst].
|
|
|
|
|
//
|
2020-06-21 01:30:21 +02:00
|
|
|
bool is_alive( const symbolic::variable& var, const il_const_iterator& dst, bool rec, tracer* tracer )
|
2020-06-09 05:32:27 +02:00
|
|
|
{
|
|
|
|
|
// If register:
|
|
|
|
|
//
|
|
|
|
|
if ( var.is_register() )
|
|
|
|
|
{
|
|
|
|
|
// If read-only, report alive.
|
|
|
|
|
//
|
|
|
|
|
if ( var.reg().is_read_only() )
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
// If volatile, report dead.
|
|
|
|
|
//
|
|
|
|
|
if ( var.reg().is_volatile() )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// If local report dead if cross-block.
|
|
|
|
|
//
|
2020-07-29 02:36:10 +02:00
|
|
|
if ( var.reg().is_local() && var.at.block != dst.block )
|
2020-06-09 05:32:27 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-11 11:19:07 +02:00
|
|
|
// Create enumerator and return is_alive after execution.
|
2020-06-09 05:32:27 +02:00
|
|
|
//
|
2020-07-11 11:19:07 +02:00
|
|
|
bool is_alive = true;
|
|
|
|
|
auto check = [ & ] ( const il_const_iterator& it )
|
2020-06-09 05:32:27 +02:00
|
|
|
{
|
2020-07-11 11:19:07 +02:00
|
|
|
// If instruction writes to the variable:
|
2020-06-09 05:32:27 +02:00
|
|
|
//
|
2020-07-11 11:19:07 +02:00
|
|
|
if ( var.written_by( it, tracer, rec ) )
|
|
|
|
|
{
|
|
|
|
|
// Mark dead and break recursively.
|
|
|
|
|
//
|
|
|
|
|
is_alive = false;
|
|
|
|
|
return enumerator::obreak_r;
|
|
|
|
|
}
|
2020-06-09 05:32:27 +02:00
|
|
|
|
2020-07-11 11:19:07 +02:00
|
|
|
// If not, continue iteration.
|
2020-06-09 05:32:27 +02:00
|
|
|
//
|
2020-07-11 11:19:07 +02:00
|
|
|
return enumerator::ocontinue;
|
|
|
|
|
};
|
2020-07-29 02:36:10 +02:00
|
|
|
dst.block->owner->enumerate( check, var.at, dst );
|
2020-07-11 11:19:07 +02:00
|
|
|
return is_alive;
|
2020-06-09 05:32:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Revives the value of the given variable to be used by the point specified.
|
|
|
|
|
//
|
|
|
|
|
register_desc revive_register( const symbolic::variable& var, const il_iterator& it )
|
|
|
|
|
{
|
|
|
|
|
fassert( var.is_register() );
|
|
|
|
|
|
|
|
|
|
// Drop const-qualifiers, this operation is not illegal since we're passed
|
|
|
|
|
// non-constant iterator, meaning we have access to the routine itself.
|
|
|
|
|
//
|
2020-07-29 02:36:10 +02:00
|
|
|
basic_block* source = ( basic_block* ) var.at.block;
|
2020-06-09 05:32:27 +02:00
|
|
|
il_iterator access_point = source->acquire( var.at );
|
|
|
|
|
|
|
|
|
|
// Allocate an appropriate temporary based on local-ness.
|
|
|
|
|
//
|
2020-07-29 02:36:10 +02:00
|
|
|
register_desc temporary = it.block != var.at.block
|
2020-06-09 05:32:27 +02:00
|
|
|
? source->owner->alloc( var.bit_count() )
|
|
|
|
|
: source->tmp( var.bit_count() );
|
|
|
|
|
|
|
|
|
|
// Insert a move-to-temporary before this instruction and swap each read
|
|
|
|
|
// of the register we revived at the access point with the new temporary.
|
|
|
|
|
//
|
2020-07-29 02:36:10 +02:00
|
|
|
for ( auto [op, type] : ( +access_point )->enum_operands() )
|
2020-06-09 05:32:27 +02:00
|
|
|
if ( type < operand_type::write && op.is_register() && op.reg() == var.reg() )
|
|
|
|
|
op = temporary;
|
2020-06-12 03:01:05 +02:00
|
|
|
source->insert( access_point, { &ins::mov, { temporary, var.reg() } } );
|
2020-06-09 05:32:27 +02:00
|
|
|
return temporary;
|
|
|
|
|
}
|
2020-06-12 05:19:04 +02:00
|
|
|
|
|
|
|
|
// Returns each possible branch destination of the given basic block in the format of:
|
|
|
|
|
// - [is_real, target] x N
|
|
|
|
|
//
|
2020-07-04 07:20:32 +02:00
|
|
|
branch_info analyze_branch( const basic_block* blk, tracer* tracer, branch_analysis_flags flags )
|
2020-06-12 05:19:04 +02:00
|
|
|
{
|
|
|
|
|
// If block is not complete, return empty vector.
|
|
|
|
|
//
|
|
|
|
|
if ( !blk->is_complete() )
|
2020-06-15 16:07:13 +02:00
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
// Declare tracer.
|
|
|
|
|
//
|
|
|
|
|
const auto trace = [ & ] ( symbolic::variable&& lookup )
|
|
|
|
|
{
|
2020-07-13 00:42:21 +02:00
|
|
|
symbolic::expression::reference exp = tracer->trace( lookup );
|
|
|
|
|
if ( flags.cross_block ) exp = tracer->rtrace_exp( exp );
|
|
|
|
|
if ( flags.pack ) symbolic::variable::pack_all( exp );
|
|
|
|
|
return exp;
|
2020-06-15 16:07:13 +02:00
|
|
|
};
|
2020-06-12 05:19:04 +02:00
|
|
|
|
|
|
|
|
// Declare operand->expression helper.
|
|
|
|
|
//
|
|
|
|
|
auto branch = std::prev( blk->end() );
|
2020-06-15 16:07:13 +02:00
|
|
|
auto discover = [ & ] ( const operand& op_dst, bool real, bool parse = true ) -> branch_info
|
2020-06-12 05:19:04 +02:00
|
|
|
{
|
|
|
|
|
// Determine the symbolic expression describing branch destination.
|
|
|
|
|
//
|
2020-07-12 14:17:46 +02:00
|
|
|
symbolic::expression::reference destination = op_dst.is_immediate()
|
2021-08-16 02:54:00 +08:00
|
|
|
? symbolic::expression{ op_dst.imm().uval }
|
2020-06-15 16:07:13 +02:00
|
|
|
: trace( { branch, op_dst.reg() } );
|
2020-06-12 05:19:04 +02:00
|
|
|
|
|
|
|
|
// Remove any matches of REG_IMGBASE and pack.
|
|
|
|
|
//
|
2020-07-12 14:17:46 +02:00
|
|
|
destination.transform( [ ] ( symbolic::expression::delegate& ex )
|
2020-06-12 05:19:04 +02:00
|
|
|
{
|
2020-07-12 14:17:46 +02:00
|
|
|
if ( ex->is_variable() )
|
2020-06-12 05:19:04 +02:00
|
|
|
{
|
2020-07-12 14:17:46 +02:00
|
|
|
auto& var = ex->uid.get<symbolic::variable>();
|
2020-06-12 05:19:04 +02:00
|
|
|
if ( var.is_register() && var.reg() == REG_IMGBASE )
|
2020-07-12 14:17:46 +02:00
|
|
|
*+ex = { 0, ex->size() };
|
2020-06-12 05:19:04 +02:00
|
|
|
}
|
2020-07-13 00:42:21 +02:00
|
|
|
}, true, false ).simplify( true );
|
2020-06-12 05:19:04 +02:00
|
|
|
|
2020-06-15 16:07:13 +02:00
|
|
|
// If parsing requested:
|
2020-06-12 05:19:04 +02:00
|
|
|
//
|
2020-06-15 16:07:13 +02:00
|
|
|
if ( parse )
|
2020-06-12 05:19:04 +02:00
|
|
|
{
|
2020-06-15 16:07:13 +02:00
|
|
|
// Match classic Jcc:
|
|
|
|
|
//
|
2020-07-12 14:17:46 +02:00
|
|
|
const auto extract_and_transform_cnd = [ & ] ( symbolic::expression::reference& dst, symbolic::expression::reference& cnd_out, bool state )
|
2020-06-12 05:19:04 +02:00
|
|
|
{
|
2020-06-17 03:26:15 +02:00
|
|
|
bool confirmed = false;
|
2020-06-15 16:07:13 +02:00
|
|
|
|
2020-08-21 07:53:57 +02:00
|
|
|
const std::function<void( const symbolic::expression& )> explore_cc_space = [ & ] ( const symbolic::expression& exp )
|
2020-06-15 16:07:13 +02:00
|
|
|
{
|
2020-06-17 03:26:15 +02:00
|
|
|
if ( exp.op == math::operator_id::value_if )
|
2020-06-15 16:07:13 +02:00
|
|
|
{
|
2020-07-12 14:17:46 +02:00
|
|
|
if ( !cnd_out )
|
|
|
|
|
cnd_out = exp.lhs;
|
2020-06-20 21:34:35 +02:00
|
|
|
}
|
|
|
|
|
else if ( ( exp.value.unknown_mask() | exp.value.known_one() ) == 1 )
|
|
|
|
|
{
|
2020-07-12 14:17:46 +02:00
|
|
|
if ( !cnd_out && !exp.is_constant() )
|
2020-06-20 21:34:35 +02:00
|
|
|
cnd_out = exp;
|
2020-06-15 16:07:13 +02:00
|
|
|
}
|
2020-06-20 21:34:35 +02:00
|
|
|
else if ( exp.is_variable() && exp.uid.get<symbolic::variable>().is_memory() )
|
|
|
|
|
{
|
2020-07-12 14:17:46 +02:00
|
|
|
exp.uid.get<symbolic::variable>().mem().decay()->enumerate( explore_cc_space );
|
2020-06-20 21:34:35 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-08-21 07:53:57 +02:00
|
|
|
const std::function<void( symbolic::expression::delegate& )> transform_cc = [ & ] ( symbolic::expression::delegate& exp )
|
2020-06-17 03:26:15 +02:00
|
|
|
{
|
2020-07-12 14:17:46 +02:00
|
|
|
if ( exp->op == math::operator_id::value_if )
|
2020-06-17 03:26:15 +02:00
|
|
|
{
|
2020-07-12 14:17:46 +02:00
|
|
|
if ( exp->lhs->is_identical( *cnd_out ) )
|
2020-06-17 03:26:15 +02:00
|
|
|
{
|
2020-07-12 14:17:46 +02:00
|
|
|
exp = state ? exp->rhs : symbolic::expression{ 0 };
|
2020-07-03 22:44:03 +02:00
|
|
|
confirmed |= !state;
|
2020-06-17 03:26:15 +02:00
|
|
|
}
|
2020-07-12 14:17:46 +02:00
|
|
|
else if ( exp->lhs->is_identical( ~cnd_out ) )
|
2020-06-17 03:26:15 +02:00
|
|
|
{
|
2020-07-12 14:17:46 +02:00
|
|
|
exp = state ? symbolic::expression{ 0 } : exp->rhs;
|
2020-07-03 22:44:03 +02:00
|
|
|
confirmed |= !state;
|
2020-06-17 03:26:15 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-07-12 14:17:46 +02:00
|
|
|
else if ( ( exp->value.unknown_mask() | exp->value.known_one() ) == 1 )
|
2020-06-20 21:34:35 +02:00
|
|
|
{
|
2020-07-12 14:17:46 +02:00
|
|
|
if ( exp->is_identical( *cnd_out ) )
|
2020-06-20 21:34:35 +02:00
|
|
|
{
|
2020-07-12 14:17:46 +02:00
|
|
|
*+exp = symbolic::expression{ state, exp->size() };
|
2020-07-03 22:44:03 +02:00
|
|
|
confirmed |= !state;
|
2020-06-20 21:34:35 +02:00
|
|
|
}
|
2020-07-12 14:17:46 +02:00
|
|
|
else if ( exp->is_identical( ~cnd_out ) )
|
2020-06-20 21:34:35 +02:00
|
|
|
{
|
2020-07-12 14:17:46 +02:00
|
|
|
*+exp = symbolic::expression{ state ^ 1, exp->size() };
|
2020-07-03 22:44:03 +02:00
|
|
|
confirmed |= !state;
|
2020-06-20 21:34:35 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-07-12 14:17:46 +02:00
|
|
|
else if ( exp->is_variable() && exp->uid.get<symbolic::variable>().is_memory() )
|
2020-06-20 21:34:35 +02:00
|
|
|
{
|
2020-07-12 14:17:46 +02:00
|
|
|
auto& var = exp->uid.get<symbolic::variable>();
|
2020-06-20 22:01:45 +02:00
|
|
|
|
2020-07-04 07:20:32 +02:00
|
|
|
// Disable cross block tracing while we trace the pointer.
|
|
|
|
|
//
|
|
|
|
|
branch_analysis_flags orig_flags = flags;
|
|
|
|
|
flags.cross_block = false;
|
2020-07-12 14:17:46 +02:00
|
|
|
symbolic::pointer exp_ptr = var.mem().decay().transform( transform_cc );
|
2020-07-04 07:20:32 +02:00
|
|
|
flags = orig_flags;
|
2020-06-20 22:01:45 +02:00
|
|
|
|
|
|
|
|
if ( exp_ptr != var.mem().base )
|
|
|
|
|
exp = trace( symbolic::variable{ std::next( var.at ), { exp_ptr, var.mem().bit_count } } );
|
2020-06-20 21:34:35 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-07-12 14:17:46 +02:00
|
|
|
dst->enumerate( explore_cc_space );
|
|
|
|
|
if ( cnd_out ) dst.transform( transform_cc );
|
2020-06-20 21:34:35 +02:00
|
|
|
if ( !confirmed ) cnd_out = {};
|
2020-06-17 03:26:15 +02:00
|
|
|
};
|
|
|
|
|
|
2020-07-12 14:17:46 +02:00
|
|
|
symbolic::expression::reference cc = {};
|
|
|
|
|
symbolic::expression::reference dst1 = destination;
|
|
|
|
|
symbolic::expression::reference dst2 = destination;
|
2020-06-20 21:34:35 +02:00
|
|
|
extract_and_transform_cnd( dst1, cc, true );
|
|
|
|
|
extract_and_transform_cnd( dst2, cc, false );
|
2020-06-15 16:07:13 +02:00
|
|
|
|
2020-06-20 21:34:35 +02:00
|
|
|
if ( cc )
|
2020-06-17 03:26:15 +02:00
|
|
|
{
|
|
|
|
|
return {
|
|
|
|
|
.is_vm_exit = real,
|
|
|
|
|
.is_jcc = true,
|
2020-07-12 14:17:46 +02:00
|
|
|
.cc = std::move( cc ),
|
|
|
|
|
.destinations = { std::move( dst1 ), std::move( dst2 ) }
|
2020-06-17 03:26:15 +02:00
|
|
|
};
|
2020-06-12 05:19:04 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-15 16:07:13 +02:00
|
|
|
// -- TODO: Handle jump tables.
|
|
|
|
|
//
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise assume direct jump.
|
2020-06-12 05:19:04 +02:00
|
|
|
//
|
2020-06-15 16:07:13 +02:00
|
|
|
return {
|
|
|
|
|
.is_vm_exit = real,
|
2020-07-12 14:17:46 +02:00
|
|
|
.destinations = { std::move( destination ) }
|
2020-06-15 16:07:13 +02:00
|
|
|
};
|
2020-06-12 05:19:04 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Discover all targets and return.
|
|
|
|
|
//
|
2020-06-26 00:24:11 +02:00
|
|
|
if ( branch->base == &ins::jmp )
|
2020-06-15 16:07:13 +02:00
|
|
|
return discover( branch->operands[ 0 ], false );
|
2020-06-26 00:24:11 +02:00
|
|
|
if ( branch->base == &ins::vexit )
|
2020-06-15 16:07:13 +02:00
|
|
|
return discover( branch->operands[ 0 ], true );
|
2020-06-26 00:24:11 +02:00
|
|
|
if ( branch->base == &ins::vxcall )
|
2020-06-15 16:07:13 +02:00
|
|
|
return discover( branch->operands[ 0 ], true );
|
2020-06-26 00:24:11 +02:00
|
|
|
if ( branch->base == &ins::js )
|
2020-06-15 16:07:13 +02:00
|
|
|
{
|
|
|
|
|
// If condition can be resolved in compile time:
|
|
|
|
|
//
|
2020-07-12 14:17:46 +02:00
|
|
|
auto cc = trace( { branch, branch->operands[ 0 ].reg() } );
|
|
|
|
|
if ( flags.resolve_opaque && cc->is_constant() )
|
2020-06-15 16:07:13 +02:00
|
|
|
{
|
|
|
|
|
// Redirect to jmp resolver.
|
|
|
|
|
//
|
2020-07-12 14:17:46 +02:00
|
|
|
return discover( branch->operands[ *cc->get<bool>() ? 1 : 2 ], false, false );
|
2020-06-15 16:07:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Resolve each individually and form jcc.
|
|
|
|
|
//
|
|
|
|
|
branch_info b1 = discover( branch->operands[ 1 ], false, false );
|
|
|
|
|
branch_info b2 = discover( branch->operands[ 2 ], false, false );
|
|
|
|
|
return {
|
|
|
|
|
.is_vm_exit = false,
|
|
|
|
|
.is_jcc = true,
|
2020-07-12 14:17:46 +02:00
|
|
|
.cc = std::move( cc ),
|
|
|
|
|
.destinations = { std::move( b1.destinations[ 0 ] ), std::move( b2.destinations[ 0 ] ) }
|
2020-06-15 16:07:13 +02:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
unreachable();
|
2020-06-12 05:19:04 +02:00
|
|
|
}
|
2020-08-23 02:07:16 +02:00
|
|
|
|
|
|
|
|
// Checks if an instruction is a semantic NOP.
|
|
|
|
|
//
|
|
|
|
|
bool is_semantic_nop( const instruction& ins )
|
|
|
|
|
{
|
|
|
|
|
// MOV to self.
|
|
|
|
|
//
|
|
|
|
|
if ( ins.base == &ins::mov ||
|
|
|
|
|
ins.base == &ins::movsx )
|
|
|
|
|
{
|
|
|
|
|
if ( ins.operands[ 0 ] == ins.operands[ 1 ] )
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// All mathematical operations with identity constants.
|
|
|
|
|
// - TODO: Fix to use global simplifier table.
|
|
|
|
|
//
|
|
|
|
|
auto is_so = [ & ] ( const instruction_desc& insd, std::initializer_list<std::pair<int, uint64_t>> checks )
|
|
|
|
|
{
|
|
|
|
|
if ( ins.base != &insd ) return false;
|
|
|
|
|
|
|
|
|
|
for ( auto [index, constant] : checks )
|
|
|
|
|
{
|
|
|
|
|
if ( !ins.operands[ index ].is_immediate() )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if ( math::descriptor_of( insd.symbolic_operator ).is_signed )
|
|
|
|
|
constant = math::sign_extend( constant, ins.access_size() );
|
|
|
|
|
else
|
|
|
|
|
constant = math::zero_extend( constant, ins.access_size() );
|
|
|
|
|
|
2021-08-16 02:54:00 +08:00
|
|
|
if ( ins.operands[ index ].imm().uval != constant )
|
2020-08-23 02:07:16 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
is_so( ins::add, { { 1, 0 } } ) ||
|
|
|
|
|
is_so( ins::sub, { { 1, 0 } } ) ||
|
|
|
|
|
is_so( ins::mul, { { 1, 1 } } ) ||
|
|
|
|
|
is_so( ins::imul, { { 1, 1 } } ) ||
|
|
|
|
|
is_so( ins::div, { { 1, 0 }, { 2, 1 } } ) ||
|
|
|
|
|
is_so( ins::idiv, { { 1, 0 }, { 2, 1 } } ) ||
|
|
|
|
|
is_so( ins::rem, { { 1, 0 }, { 2, ~0ull } } ) ||
|
|
|
|
|
is_so( ins::bshl, { { 1, 0 } } ) ||
|
|
|
|
|
is_so( ins::bshr, { { 1, 0 } } ) ||
|
|
|
|
|
is_so( ins::brol, { { 1, 0 } } ) ||
|
|
|
|
|
is_so( ins::bror, { { 1, 0 } } ) ||
|
|
|
|
|
is_so( ins::bxor, { { 1, 0 } } ) ||
|
|
|
|
|
is_so( ins::bor, { { 1, 0 } } ) ||
|
|
|
|
|
is_so( ins::band, { { 1, ~0ull } } );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Removes all NOPs,.
|
|
|
|
|
//
|
|
|
|
|
size_t remove_nops( basic_block* blk, bool semantic_nops, bool volatile_nops )
|
|
|
|
|
{
|
|
|
|
|
size_t n = 0;
|
|
|
|
|
for ( auto it = blk->begin(); !it.is_end(); )
|
|
|
|
|
{
|
|
|
|
|
if ( ( volatile_nops || !it->is_volatile() ) && ( it->base == &ins::nop || ( semantic_nops && is_semantic_nop( *it ) ) ) )
|
|
|
|
|
it = blk->erase( it ), n++;
|
|
|
|
|
else
|
|
|
|
|
it++;
|
|
|
|
|
}
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
size_t remove_nops( routine* rtn, bool semantic_nops, bool volatile_nops )
|
|
|
|
|
{
|
|
|
|
|
size_t n = 0;
|
|
|
|
|
rtn->for_each( [ & ] ( basic_block* blk ) { n += remove_nops( blk, semantic_nops, volatile_nops ); } );
|
|
|
|
|
return n;
|
|
|
|
|
}
|
2020-06-09 05:32:27 +02:00
|
|
|
};
|