mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
284 lines
9.8 KiB
C++
284 lines
9.8 KiB
C++
/* ###
|
|
* IP: GHIDRA
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include "paramid.hh"
|
|
|
|
namespace ghidra {
|
|
|
|
ElementId ELEM_PARAMMEASURES = ElementId("parammeasures",106);
|
|
ElementId ELEM_PROTO = ElementId("proto",107);
|
|
ElementId ELEM_RANK = ElementId("rank",108);
|
|
|
|
// NOTES FROM 20121206 W/Decompiler-Man
|
|
// direct reads is for all opcodes, with special for these:
|
|
// BRANCH is direct read on input0. No direct write.
|
|
// CBRANCH is direct read on input0 and input1. No direct write.
|
|
// BRANCHIND is direct read on input0 (like call but no params). No direct write.
|
|
// CALL is direct read on input0 (putative/presumptive param flag on params--other inputs). Special (non-direct) write of output.
|
|
// CALLIND same as on CALL. Special (non-direct) write of output.
|
|
// CALLOTHER is direct read on ALL PARAMETERS (input0 and up)--is specified in sleigh. Direct write if output exists.
|
|
// INDIRECT is least powerful input and output of all.
|
|
// MULTIEQUALS is flow through but must test for and not flow through loop paths (whether from param forward our return backward directions).
|
|
//
|
|
|
|
#define MAXDEPTH 10
|
|
void ParamMeasure::walkforward( WalkState &state, PcodeOp *ignoreop, Varnode *vn )
|
|
|
|
{
|
|
state.depth += 1;
|
|
if (state.depth >= MAXDEPTH) {
|
|
state.depth -= 1;
|
|
return;
|
|
}
|
|
list<PcodeOp *>::const_iterator iter = vn->beginDescend();
|
|
while( rank != state.terminalrank && iter != vn->endDescend() ) {
|
|
PcodeOp *op = *iter;
|
|
if( op != ignoreop ) {
|
|
OpCode oc = op->getOpcode()->getOpcode();
|
|
switch( oc ) {
|
|
case CPUI_BRANCH:
|
|
case CPUI_BRANCHIND:
|
|
if( op->getSlot(vn) == 0 ) updaterank( DIRECTREAD, state.best );
|
|
break;
|
|
case CPUI_CBRANCH:
|
|
if( op->getSlot(vn) < 2 ) updaterank( DIRECTREAD, state.best );
|
|
break;
|
|
case CPUI_CALL:
|
|
case CPUI_CALLIND:
|
|
if( op->getSlot(vn) == 0 ) updaterank( DIRECTREAD, state.best );
|
|
else {
|
|
numcalls++;
|
|
updaterank( SUBFNPARAM, state.best );
|
|
}
|
|
break;
|
|
case CPUI_CALLOTHER:
|
|
updaterank( DIRECTREAD, state.best );
|
|
break;
|
|
case CPUI_RETURN:
|
|
updaterank( THISFNRETURN, state.best );
|
|
break;
|
|
case CPUI_INDIRECT:
|
|
updaterank( INDIRECT, state.best );
|
|
break;
|
|
case CPUI_MULTIEQUAL:
|
|
// The only op for which there can be a loop in the graph is with the MULTIEQUAL (not for CALL, etc.).
|
|
// Walk forward only if the path is not part of a loop.
|
|
if( !op->getParent()->isLoopIn(op->getSlot(vn)) ) walkforward( state, (PcodeOp *)0, op->getOut() );
|
|
break;
|
|
default:
|
|
updaterank( DIRECTREAD, state.best );
|
|
break;
|
|
}
|
|
}
|
|
iter++;
|
|
}
|
|
state.depth -= 1;
|
|
}
|
|
|
|
void ParamMeasure::walkbackward( WalkState &state, PcodeOp *ignoreop, Varnode *vn )
|
|
|
|
{
|
|
if( vn->isInput() ) {
|
|
updaterank( THISFNPARAM, state.best );
|
|
return;
|
|
}
|
|
else if( !vn->isWritten() ) {
|
|
updaterank( THISFNPARAM, state.best ); //TODO: not sure about this.
|
|
return;
|
|
}
|
|
|
|
PcodeOp *op = vn->getDef();
|
|
OpCode oc = op->getOpcode()->getOpcode();
|
|
switch( oc ) {
|
|
case CPUI_BRANCH:
|
|
case CPUI_BRANCHIND:
|
|
case CPUI_CBRANCH:
|
|
case CPUI_CALL:
|
|
case CPUI_CALLIND:
|
|
break;
|
|
case CPUI_CALLOTHER:
|
|
if( op->getOut() != (Varnode *) 0 ) updaterank( DIRECTREAD, state.best );
|
|
break;
|
|
case CPUI_RETURN:
|
|
updaterank( SUBFNRETURN, state.best );
|
|
break;
|
|
case CPUI_INDIRECT:
|
|
updaterank( INDIRECT, state.best );
|
|
break;
|
|
case CPUI_MULTIEQUAL:
|
|
// The only op for which there can be a loop in the graph is with the MULTIEQUAL (not for CALL, etc.).
|
|
// Walk backward only if the path is not part of a loop.
|
|
for( int4 slot = 0; slot < op->numInput() && rank != state.terminalrank; slot++ )
|
|
if( !op->getParent()->isLoopIn(slot) ) walkbackward( state, op, op->getIn(slot) );
|
|
break;
|
|
default:
|
|
//Might be DIRECTWRITEWITHOUTREAD, but we do not know yet.
|
|
//So now try to walk forward to see if there is at least one path
|
|
// forward (other than the path we took to get here walking backward)
|
|
// in which there is not a direct read of this write.
|
|
ParamMeasure pmfw( vn->getAddr(), vn->getSize(), vn->getType(), INPUT );
|
|
pmfw.calculateRank( false, vn, ignoreop );
|
|
if( pmfw.getMeasure() == DIRECTREAD )
|
|
updaterank( DIRECTWRITEWITHREAD, state.best );
|
|
else
|
|
updaterank( DIRECTWRITEWITHOUTREAD, state.best );
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ParamMeasure::calculateRank(bool best,Varnode *basevn,PcodeOp *ignoreop)
|
|
|
|
{
|
|
WalkState state;
|
|
state.best = best;
|
|
state.depth = 0;
|
|
if( best ) {
|
|
rank = WORSTRANK;
|
|
state.terminalrank = (io == INPUT) ? DIRECTREAD : DIRECTWRITEWITHOUTREAD;
|
|
} else {
|
|
rank = BESTRANK;
|
|
state.terminalrank = INDIRECT;
|
|
}
|
|
numcalls = 0;
|
|
if (io == INPUT)
|
|
walkforward(state, ignoreop, basevn);
|
|
else
|
|
walkbackward(state, ignoreop, basevn);
|
|
}
|
|
|
|
void ParamMeasure::encode( Encoder &encoder,ElementId &tag,bool moredetail ) const
|
|
|
|
{
|
|
encoder.openElement(tag);
|
|
encoder.openElement(ELEM_ADDR);
|
|
vndata.space->encodeAttributes( encoder, vndata.offset, vndata.size );
|
|
encoder.closeElement(ELEM_ADDR);
|
|
vntype->encodeRef(encoder);
|
|
if( moredetail ) {
|
|
encoder.openElement(ELEM_RANK);
|
|
encoder.writeSignedInteger(ATTRIB_VAL, rank);
|
|
encoder.closeElement(ELEM_RANK);
|
|
}
|
|
encoder.closeElement(tag);
|
|
}
|
|
|
|
void ParamMeasure::savePretty( ostream &s,bool moredetail ) const
|
|
|
|
{
|
|
s << " Space: " << vndata.space->getName() << "\n";
|
|
s << " Addr: " << vndata.offset << "\n";
|
|
s << " Size: " << vndata.size << "\n";
|
|
s << " Rank: " << rank << "\n";
|
|
}
|
|
|
|
ParamIDAnalysis::ParamIDAnalysis( Funcdata *fd_in, bool justproto )
|
|
|
|
{
|
|
fd = fd_in;
|
|
if (justproto) { // We only provide info on the recovered prototype
|
|
const FuncProto &fproto( fd->getFuncProto() );
|
|
int4 num = fproto.numParams();
|
|
for(int4 i=0;i<num;++i) {
|
|
ProtoParameter *param = fproto.getParam(i);
|
|
InputParamMeasures.push_back( ParamMeasure(param->getAddress(),param->getSize(),
|
|
param->getType(),ParamMeasure::INPUT) );
|
|
Varnode *vn = fd->findVarnodeInput(param->getSize(),param->getAddress());
|
|
if (vn != (Varnode *)0)
|
|
InputParamMeasures.back().calculateRank(true,vn,(PcodeOp *)0);
|
|
}
|
|
|
|
ProtoParameter *outparam = fproto.getOutput();
|
|
if (!outparam->getAddress().isInvalid()) { // If we don't have a void type
|
|
OutputParamMeasures.push_back( ParamMeasure( outparam->getAddress(),outparam->getSize(),
|
|
outparam->getType(),ParamMeasure::OUTPUT) );
|
|
list<PcodeOp *>::const_iterator rtn_iter = fd->beginOp( CPUI_RETURN );
|
|
while( rtn_iter != fd->endOp( CPUI_RETURN ) ) {
|
|
PcodeOp *rtn_op = *rtn_iter;
|
|
// For RETURN op, input0 is address location of indirect return, input1,
|
|
// if it exists, is the Varnode returned, output = not sure.
|
|
if( rtn_op->numInput() == 2 ) {
|
|
Varnode *ovn = rtn_op->getIn(1);
|
|
if( ovn != (Varnode *)0 ) { //Not a void return
|
|
OutputParamMeasures.back().calculateRank(true, ovn, rtn_op );
|
|
break;
|
|
}
|
|
}
|
|
rtn_iter++;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Need to list input varnodes that are outside of the model
|
|
VarnodeDefSet::const_iterator iter,enditer;
|
|
iter = fd->beginDef(Varnode::input);
|
|
enditer = fd->endDef(Varnode::input);
|
|
while(iter != enditer) {
|
|
Varnode *invn = *iter;
|
|
++iter;
|
|
InputParamMeasures.push_back( ParamMeasure(invn->getAddr(),invn->getSize(),
|
|
invn->getType(),ParamMeasure::INPUT) );
|
|
InputParamMeasures.back().calculateRank(true, invn, (PcodeOp *)0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
void ParamIDAnalysis::encode( Encoder &encoder,bool moredetail ) const
|
|
|
|
{
|
|
encoder.openElement(ELEM_PARAMMEASURES);
|
|
encoder.writeString(ATTRIB_NAME, fd->getName());
|
|
fd->getAddress().encode( encoder );
|
|
encoder.openElement(ELEM_PROTO);
|
|
|
|
encoder.writeString(ATTRIB_MODEL, fd->getFuncProto().getModelName());
|
|
int4 extrapop = fd->getFuncProto().getExtraPop();
|
|
if (extrapop == ProtoModel::extrapop_unknown)
|
|
encoder.writeString(ATTRIB_EXTRAPOP, "unknown");
|
|
else
|
|
encoder.writeSignedInteger(ATTRIB_EXTRAPOP, extrapop);
|
|
encoder.closeElement(ELEM_PROTO);
|
|
list<ParamMeasure>::const_iterator pm_iter;
|
|
for( pm_iter = InputParamMeasures.begin(); pm_iter != InputParamMeasures.end(); ++pm_iter) {
|
|
const ParamMeasure &pm( *pm_iter );
|
|
pm.encode(encoder,ELEM_INPUT,moredetail);
|
|
}
|
|
for( pm_iter = OutputParamMeasures.begin(); pm_iter != OutputParamMeasures.end() ; ++pm_iter) {
|
|
const ParamMeasure &pm( *pm_iter );
|
|
pm.encode( encoder, ELEM_OUTPUT, moredetail );
|
|
}
|
|
encoder.closeElement(ELEM_PARAMMEASURES);
|
|
}
|
|
|
|
void ParamIDAnalysis::savePretty( ostream &s,bool moredetail ) const
|
|
|
|
{
|
|
s << "Param Measures\nFunction: " << fd->getName() << "\nAddress: 0x" << hex << fd->getAddress().getOffset() << "\n";
|
|
s << "Model: " << fd->getFuncProto().getModelName() << "\nExtrapop: " << fd->getFuncProto().getExtraPop() << "\n";
|
|
s << "Num Params: " << InputParamMeasures.size() << "\n";
|
|
list<ParamMeasure>::const_iterator pm_iter = InputParamMeasures.begin();
|
|
for( pm_iter = InputParamMeasures.begin(); pm_iter != InputParamMeasures.end() ; ++pm_iter ) {
|
|
const ParamMeasure &pm( *pm_iter );
|
|
pm.savePretty( s, moredetail );
|
|
}
|
|
s << "Num Returns: " << OutputParamMeasures.size() << "\n";
|
|
pm_iter = OutputParamMeasures.begin();
|
|
for( pm_iter = OutputParamMeasures.begin(); pm_iter != OutputParamMeasures.end() ; ++pm_iter) {
|
|
const ParamMeasure &pm( *pm_iter );
|
|
pm.savePretty( s, moredetail );
|
|
}
|
|
s << "\n";
|
|
}
|
|
|
|
} // End namespace ghidra
|