ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/paramid.cc
2024-10-08 20:21:52 +00:00

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