ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/ifacedecomp.cc
2024-03-06 22:32:06 +00:00

3665 lines
117 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 "ifacedecomp.hh"
extern "C" {
#include <time.h>
}
#include "pcodeparse.hh"
#include "blockaction.hh"
namespace ghidra {
// Constructing this registers the capability
IfaceDecompCapability IfaceDecompCapability::ifaceDecompCapability;
IfaceDecompCapability::IfaceDecompCapability(void)
{
name = "decomp";
}
void IfaceDecompCapability::registerCommands(IfaceStatus *status)
{
status->registerCom(new IfcComment(),"//"); //Note: A space must follow this when used.
status->registerCom(new IfcComment(),"#"); //Note: A space must follow this when used.
status->registerCom(new IfcComment(),"%"); //Note: A space must follow this when used.
status->registerCom(new IfcQuit(),"quit");
status->registerCom(new IfcHistory(),"history");
status->registerCom(new IfcOpenfile(),"openfile", "write");
status->registerCom(new IfcOpenfileAppend(),"openfile","append");
status->registerCom(new IfcClosefile(),"closefile");
status->registerCom(new IfcEcho(),"echo");
status->registerCom(new IfcSource(),"source");
status->registerCom(new IfcOption(),"option");
status->registerCom(new IfcParseFile(),"parse","file");
status->registerCom(new IfcParseLine(),"parse","line");
status->registerCom(new IfcAdjustVma(),"adjust","vma");
status->registerCom(new IfcFuncload(),"load","function");
status->registerCom(new IfcAddrrangeLoad(),"load","addr");
status->registerCom(new IfcReadSymbols(),"read","symbols");
status->registerCom(new IfcCleararch(),"clear","architecture");
status->registerCom(new IfcMapaddress(),"map","address");
status->registerCom(new IfcMaphash(),"map","hash");
status->registerCom(new IfcMapParam(),"map","param");
status->registerCom(new IfcMapReturn(),"map","return");
status->registerCom(new IfcMapfunction(),"map","function");
status->registerCom(new IfcMapexternalref(),"map","externalref");
status->registerCom(new IfcMaplabel(),"map","label");
status->registerCom(new IfcMapconvert(),"map","convert");
status->registerCom(new IfcMapunionfacet(), "map", "unionfacet");
status->registerCom(new IfcPrintdisasm(),"disassemble");
status->registerCom(new IfcDecompile(),"decompile");
status->registerCom(new IfcDump(),"dump");
status->registerCom(new IfcDumpbinary(),"binary");
status->registerCom(new IfcForcegoto(),"force","goto");
status->registerCom(new IfcForceFormat(),"force","varnode");
status->registerCom(new IfcForceDatatypeFormat(),"force","datatype");
status->registerCom(new IfcProtooverride(),"override","prototype");
status->registerCom(new IfcJumpOverride(),"override","jumptable");
status->registerCom(new IfcFlowOverride(),"override","flow");
status->registerCom(new IfcDeadcodedelay(),"deadcode","delay");
status->registerCom(new IfcGlobalAdd(),"global","add");
status->registerCom(new IfcGlobalRemove(),"global","remove");
status->registerCom(new IfcGlobalify(),"global","spaces");
status->registerCom(new IfcGlobalRegisters(),"global","registers");
status->registerCom(new IfcGraphDataflow(),"graph","dataflow");
status->registerCom(new IfcGraphControlflow(),"graph","controlflow");
status->registerCom(new IfcGraphDom(),"graph","dom");
status->registerCom(new IfcPrintLanguage(),"print","language");
status->registerCom(new IfcPrintCStruct(),"print","C");
status->registerCom(new IfcPrintCFlat(),"print","C","flat");
status->registerCom(new IfcPrintCGlobals(),"print","C","globals");
status->registerCom(new IfcPrintCTypes(),"print","C","types");
status->registerCom(new IfcPrintCXml(),"print","C","xml");
status->registerCom(new IfcPrintParamMeasures(),"print","parammeasures");
status->registerCom(new IfcProduceC(),"produce","C");
status->registerCom(new IfcProducePrototypes(),"produce","prototypes");
status->registerCom(new IfcPrintRaw(),"print","raw");
status->registerCom(new IfcPrintInputs(),"print","inputs");
status->registerCom(new IfcPrintInputsAll(),"print","inputs","all");
status->registerCom(new IfcListaction(),"list","action");
status->registerCom(new IfcListOverride(),"list","override");
status->registerCom(new IfcListprototypes(),"list","prototypes");
status->registerCom(new IfcSetcontextrange(),"set","context");
status->registerCom(new IfcSettrackedrange(),"set","track");
status->registerCom(new IfcBreakstart(),"break","start");
status->registerCom(new IfcBreakaction(),"break","action");
status->registerCom(new IfcPrintSpaces(),"print","spaces");
status->registerCom(new IfcPrintHigh(),"print","high");
status->registerCom(new IfcPrintTree(),"print","tree","varnode");
status->registerCom(new IfcPrintBlocktree(),"print","tree","block");
status->registerCom(new IfcPrintLocalrange(),"print","localrange");
status->registerCom(new IfcPrintMap(),"print","map");
status->registerCom(new IfcPrintVarnode(),"print","varnode");
status->registerCom(new IfcPrintCover(),"print","cover","high");
status->registerCom(new IfcVarnodeCover(),"print","cover","varnode");
status->registerCom(new IfcVarnodehighCover(),"print","cover","varnodehigh");
status->registerCom(new IfcPrintExtrapop(),"print","extrapop");
status->registerCom(new IfcPrintActionstats(),"print","actionstats");
status->registerCom(new IfcResetActionstats(),"reset","actionstats");
status->registerCom(new IfcCountPcode(),"count","pcode");
status->registerCom(new IfcTypeVarnode(),"type","varnode");
status->registerCom(new IfcNameVarnode(),"name","varnode");
status->registerCom(new IfcRename(),"rename");
status->registerCom(new IfcRetype(),"retype");
status->registerCom(new IfcRemove(),"remove");
status->registerCom(new IfcIsolate(),"isolate");
status->registerCom(new IfcLockPrototype(),"prototype","lock");
status->registerCom(new IfcUnlockPrototype(),"prototype","unlock");
status->registerCom(new IfcCommentInstr(),"comment","instruction");
status->registerCom(new IfcDuplicateHash(),"duplicate","hash");
status->registerCom(new IfcCallGraphBuild(),"callgraph","build");
status->registerCom(new IfcCallGraphBuildQuick(),"callgraph","build","quick");
status->registerCom(new IfcCallGraphDump(),"callgraph","dump");
status->registerCom(new IfcCallGraphLoad(),"callgraph","load");
status->registerCom(new IfcCallGraphList(),"callgraph","list");
status->registerCom(new IfcCallFixup(),"fixup","call");
status->registerCom(new IfcCallOtherFixup(),"fixup","callother");
status->registerCom(new IfcFixupApply(),"fixup","apply");
status->registerCom(new IfcVolatile(),"volatile");
status->registerCom(new IfcReadonly(),"readonly");
status->registerCom(new IfcPointerSetting(),"pointer","setting");
status->registerCom(new IfcPreferSplit(),"prefersplit");
status->registerCom(new IfcStructureBlocks(),"structure","blocks");
status->registerCom(new IfcAnalyzeRange(), "analyze","range");
status->registerCom(new IfcLoadTestFile(), "load","test","file");
status->registerCom(new IfcListTestCommands(), "list","test","commands");
status->registerCom(new IfcExecuteTestCommand(), "execute","test","command");
#ifdef CPUI_RULECOMPILE
status->registerCom(new IfcParseRule(),"parse","rule");
status->registerCom(new IfcExperimentalRules(),"experimental","rules");
#endif
status->registerCom(new IfcContinue(),"continue");
#ifdef OPACTION_DEBUG
status->registerCom(new IfcDebugAction(),"debug","action");
status->registerCom(new IfcTraceBreak(),"trace","break");
status->registerCom(new IfcTraceAddress(),"trace","address");
status->registerCom(new IfcTraceEnable(),"trace","enable");
status->registerCom(new IfcTraceDisable(),"trace","disable");
status->registerCom(new IfcTraceClear(),"trace","clear");
status->registerCom(new IfcTraceList(),"trace","list");
status->registerCom(new IfcBreakjump(),"break","jumptable");
#endif
}
/// Runs over every function in the scope, or any sub-scope , calling
/// iterationCallback()
/// \param scope is the given scope
void IfaceDecompCommand::iterateScopesRecursive(Scope *scope)
{
if (!scope->isGlobal()) return;
iterateFunctionsAddrOrder(scope);
ScopeMap::const_iterator iter,enditer;
iter = scope->childrenBegin();
enditer = scope->childrenEnd();
for(;iter!=enditer;++iter) {
iterateScopesRecursive((*iter).second);
}
}
/// Runs over every function in the scope calling iterationCallback().
/// \param scope is the given scope
void IfaceDecompCommand::iterateFunctionsAddrOrder(Scope *scope)
{
MapIterator miter,menditer;
miter = scope->begin();
menditer = scope->end();
while(miter != menditer) {
Symbol *sym = (*miter)->getSymbol();
FunctionSymbol *fsym = dynamic_cast<FunctionSymbol *>(sym);
++miter;
if (fsym != (FunctionSymbol *)0)
iterationCallback(fsym->getFunction());
}
}
/// Scopes are traversed depth-first, then within a scope, functions are
/// traversed in address order.
void IfaceDecompCommand::iterateFunctionsAddrOrder(void)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No architecture loaded");
iterateScopesRecursive(dcp->conf->symboltab->getGlobalScope());
}
/// Traversal is based on the current CallGraph for the program.
/// Child functions are traversed before their parents.
void IfaceDecompCommand::iterateFunctionsLeafOrder(void)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No architecture loaded");
if (dcp->cgraph == (CallGraph *)0)
throw IfaceExecutionError("No callgraph present");
CallGraphNode *node;
node = dcp->cgraph->initLeafWalk();
while(node != (CallGraphNode *)0) {
if (node->getName().size()==0) continue; // Skip if has no name
Funcdata *fd = node->getFuncdata();
if (fd != (Funcdata *)0)
iterationCallback(fd);
node = dcp->cgraph->nextLeaf(node);
}
}
IfaceDecompData::IfaceDecompData(void)
{
conf = (Architecture *)0;
fd = (Funcdata *)0;
cgraph = (CallGraph *)0;
testCollection = (FunctionTestCollection *)0;
#ifdef OPACTION_DEBUG
jumptabledebug = false;
#endif
}
IfaceDecompData::~IfaceDecompData(void)
{
if (cgraph != (CallGraph *)0)
delete cgraph;
if (conf != (Architecture *)0)
delete conf;
if (testCollection != (FunctionTestCollection *)0)
delete testCollection;
// fd will get deleted with Database
}
void IfaceDecompData::allocateCallGraph(void)
{
if (cgraph != (CallGraph *)0)
delete cgraph;
cgraph = new CallGraph(conf);
}
/// This is called if a command throws a low-level error.
/// It clears any analysis on the function, sets the current function
/// to null, and issues a warning.
/// \param s is the stream to write the warning to
void IfaceDecompData::abortFunction(ostream &s)
{
if (fd == (Funcdata *)0) return;
s << "Unable to proceed with function: " << fd->getName() << endl;
conf->clearAnalysis(fd);
fd = (Funcdata *)0;
}
void IfaceDecompData::clearArchitecture(void)
{
if (conf != (Architecture *)0)
delete conf;
conf = (Architecture *)0;
fd = (Funcdata *)0;
}
/// \class IfcComment
/// \brief A comment within a command script: `% A comment in a script`
///
/// This commands does nothing but attaches to comment tokens like:
/// - \#
/// - %
/// - //
///
/// allowing comment lines in a script file
void IfcComment::execute(istream &s)
{
//Do nothing
}
/// \class IfcOption
/// \brief Adjust a decompiler option: `option <optionname> [<param1>] [<param2>] [<param3>]`
///
/// Passes command-line parameters to an ArchOption object registered with
/// the current architecture's OptionDatabase. Options are looked up by name
/// and can be configure with up to 3 parameters. Options generally report success
/// or failure back to the console.
void IfcOption::execute(istream &s)
{
string optname;
string p1,p2,p3;
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
s >> ws >> optname >> ws;
if (optname.size()==0)
throw IfaceParseError("Missing option name");
if (!s.eof()) {
s >> p1 >> ws;
if (!s.eof()) {
s >> p2 >> ws;
if (!s.eof()) {
s >> p3 >> ws;
if (!s.eof())
throw IfaceParseError("Too many option parameters");
}
}
}
try {
string res = dcp->conf->options->set(ElementId::find(optname,0),p1,p2,p3);
*status->optr << res << endl;
}
catch(ParseError &err) {
*status->optr << err.explain << endl;
throw IfaceParseError("Bad option");
}
catch(RecovError &err) {
*status->optr << err.explain << endl;
throw IfaceExecutionError("Bad option");
}
}
/// \class IfcParseFile
/// \brief Parse a file with C declarations: `parse file <filename>`
///
/// The file must contain C syntax data-type and function declarations.
/// Data-types become part of the program, and function declarations,
/// if the symbol already exists, associate the prototype with the symbol.
void IfcParseFile::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
string filename;
ifstream fs;
s >> ws >> filename;
if (filename.empty())
throw IfaceParseError("Missing filename");
fs.open( filename.c_str() );
if (!fs)
throw IfaceExecutionError("Unable to open file: "+filename);
try { // Try to parse the file
parse_C(dcp->conf,fs);
}
catch(ParseError &err) {
*status->optr << "Error in C syntax: " << err.explain << endl;
throw IfaceExecutionError("Bad C syntax");
}
fs.close();
}
/// \class IfcParseLine
/// \brief Parse a line of C syntax: `parse line ...`
///
/// The line can contain a declaration either a data-type or a function prototype:
/// - `parse line typedef int4 *specialint;`
/// - `parse line struct mystruct { int4 a; int4 b; }`
/// - `parse line extern void myfunc(int4 a,int4 b);`
///
/// Data-types go straight into the program. For a prototype, the function symbol
/// must already exist.
void IfcParseLine::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
s >> ws;
if (s.eof())
throw IfaceParseError("No input");
try { // Try to parse the line
parse_C(dcp->conf,s);
}
catch(ParseError &err) {
*status->optr << "Error in C syntax: " << err.explain << endl;
throw IfaceExecutionError("Bad C syntax");
}
}
/// \class IfcAdjustVma
/// \brief Change the base address of the load image: `adjust vma 0xabcd0123`
///
/// The provided parameter is added to the current base address of the image.
/// This only affects the address of bytes in the image and so should be done
/// before functions and other symbols are layed down.
void IfcAdjustVma::execute(istream &s)
{
unsigned long adjust;
adjust = 0uL;
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
s.unsetf(ios::dec | ios::hex | ios::oct); // Let user specify base
s >> ws >> adjust;
if (adjust == 0uL)
throw IfaceParseError("No adjustment parameter");
dcp->conf->loader->adjustVma(adjust);
}
#ifdef OPACTION_DEBUG
static void jump_callback(Funcdata &orig,Funcdata &fd);
#endif
/// \brief Generate raw p-code for the current function
///
/// Follow flow from the entry point of the function and generate the
/// raw p-code ops for all instructions, up to \e return instructions.
/// If a \e size in bytes is provided, it bounds the memory region where flow
/// can be followed. Otherwise, a zero \e size allows unbounded flow tracing.
/// \param s is a output stream for reporting function details or errors
/// \param size (if non-zero) is the maximum number of bytes to disassemble
void IfaceDecompData::followFlow(ostream &s,int4 size)
{
#ifdef OPACTION_DEBUG
if (jumptabledebug)
fd->enableJTCallback(jump_callback);
#endif
try {
if (size==0) {
Address baddr(fd->getAddress().getSpace(),0);
Address eaddr(fd->getAddress().getSpace(),fd->getAddress().getSpace()->getHighest());
fd->followFlow(baddr,eaddr);
}
else
fd->followFlow(fd->getAddress(),fd->getAddress()+size);
s << "Function " << fd->getName() << ": ";
fd->getAddress().printRaw(s);
s << endl;
} catch(RecovError &err) {
s << "Function " << fd->getName() << ": " << err.explain << endl;
}
}
/// \class IfcFuncload
/// \brief Make a specific function current: `load function <functionname>`
///
/// The name must be a fully qualified symbol with "::" separating namespaces.
/// If the symbol represents a function, that function becomes \e current for
/// the console. If there are bytes for the function, raw p-code and control-flow
/// are calculated.
void IfcFuncload::execute(istream &s)
{
string funcname;
Address offset;
s >> funcname;
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No image loaded");
string basename;
Scope *funcscope = dcp->conf->symboltab->resolveScopeFromSymbolName(funcname,"::",basename,(Scope *)0);
if (funcscope == (Scope *)0)
throw IfaceExecutionError("Bad namespace: "+funcname);
dcp->fd = funcscope->queryFunction( basename ); // Is function already in database
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("Unknown function name: "+funcname);
if (!dcp->fd->hasNoCode())
dcp->followFlow(*status->optr,0);
}
/// \class IfcAddrrangeLoad
/// \brief Create a new function at an address: `load addr <address> [<funcname>]`
///
/// A new function is created at the provided address. If a name is provided, this
/// becomes the function symbol, otherwise a default name is generated.
/// The function becomes \e current for the interface, and if bytes are present,
/// raw p-code and control-flow are generated.
void IfcAddrrangeLoad::execute(istream &s)
{
int4 size;
string name;
Address offset=parse_machaddr(s,size,*dcp->conf->types); // Read required address
s >> ws;
if (size <= offset.getAddrSize()) // Was a real size specified
size = 0;
if (dcp->conf->loader == (LoadImage *)0)
throw IfaceExecutionError("No binary loaded");
s >> name; // Read optional name
if (name.empty())
dcp->conf->nameFunction(offset,name); // Pick default name if necessary
dcp->fd = dcp->conf->symboltab->getGlobalScope()->addFunction( offset,name)->getFunction();
dcp->followFlow(*status->optr,size);
}
/// \class IfcCleararch
/// \brief Clear the current architecture/program: `clear architecture`
void IfcCleararch::execute(istream &s)
{
dcp->clearArchitecture();
}
/// \class IfcReadSymbols
/// \brief Read in symbols from the load image: `read symbols`
///
/// If the load image format encodes symbol information. These are
/// read in and attached to the appropriate address.
void IfcReadSymbols::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
if (dcp->conf->loader == (LoadImage *)0)
throw IfaceExecutionError("No binary loaded");
dcp->conf->readLoaderSymbols("::");
}
/// \class IfcMapaddress
/// \brief Map a new symbol into the program: `map address <address> <typedeclaration>`
///
/// Create a new variable in the current scope
/// \code
/// map address r0x1000 int4 globalvar
/// \endcode
/// The symbol specified in the type declaration can qualify the namespace using the "::"
/// specifier. If there is a current function, the variable is local to the function.
/// Otherwise the symbol is created relative to the global scope.
void IfcMapaddress::execute(istream &s)
{
Datatype *ct;
string name;
int4 size;
Address addr = parse_machaddr(s,size,*dcp->conf->types); // Read required address;
s >> ws;
ct = parse_type(s,name,dcp->conf); // Parse the required type
if (dcp->fd != (Funcdata *)0) {
Symbol *sym;
sym = dcp->fd->getScopeLocal()->addSymbol(name,ct,addr,Address())->getSymbol();
sym->getScope()->setAttribute(sym,Varnode::namelock|Varnode::typelock);
}
else {
Symbol *sym;
uint4 flags = Varnode::namelock|Varnode::typelock;
flags |= dcp->conf->symboltab->getProperty(addr); // Inherit existing properties
string basename;
Scope *scope = dcp->conf->symboltab->findCreateScopeFromSymbolName(name, "::", basename, (Scope *)0);
sym = scope->addSymbol(basename,ct,addr,Address())->getSymbol();
sym->getScope()->setAttribute(sym,flags);
if (scope->getParent() != (Scope *)0) { // If this is a global namespace scope
SymbolEntry *e = sym->getFirstWholeMap(); // Adjust range
dcp->conf->symboltab->addRange(scope,e->getAddr().getSpace(),e->getFirst(),e->getLast());
}
}
}
/// \class IfcMaphash
/// \brief Add a dynamic symbol to the current function: `map hash <address> <hash> <typedeclaration>`
///
/// The command only creates local variables for the current function.
/// The name and data-type are taken from a C syntax type declaration. The symbol is
/// not associated with a particular storage address but with a specific Varnode in the data-flow,
/// specified by a code address and hash of the local data-flow structure.
void IfcMaphash::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function loaded");
Datatype *ct;
string name;
uint8 hash;
int4 size;
Address addr = parse_machaddr(s,size,*dcp->conf->types); // Read pc address of hash
s >> hex >> hash; // Parse the hash value
s >> ws;
ct = parse_type(s,name,dcp->conf); // Parse the required type and name
Symbol *sym = dcp->fd->getScopeLocal()->addDynamicSymbol(name,ct,addr,hash);
sym->getScope()->setAttribute(sym,Varnode::namelock|Varnode::typelock);
}
/// \class IfcMapParam
/// \brief Map a parameter symbol for the current function: `map param #i <address> <typedeclaration>`
///
/// The position of the parameter in the input list is parsed as an integer, starting at 0.
/// The address range used for parameter is explicitly set. The data-type and name of the parameter
/// are parsed from the type declaration. The parameter is treated as name and type locked.
void IfcMapParam::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function loaded");
int4 i;
string name;
int4 size;
ParameterPieces piece;
s >> dec >> i; // Position of the parameter
piece.addr = parse_machaddr(s,size,*dcp->conf->types); // Starting address of parameter
piece.type = parse_type(s,name,dcp->conf);
piece.flags = ParameterPieces::typelock | ParameterPieces::namelock;
dcp->fd->getFuncProto().setParam(i, name, piece);
}
/// \class IfcMapReturn
/// \brief Map the return storage for the current function: `map return <address> <typedeclaration>`
///
/// The address range used for return storage is explicitly set, and the return value is set to the
/// parsed data-type. The function's output is considered locked.
void IfcMapReturn::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function loaded");
string name;
int4 size;
ParameterPieces piece;
piece.addr = parse_machaddr(s,size,*dcp->conf->types); // Starting address of return storage
piece.type = parse_type(s,name,dcp->conf);
piece.flags = ParameterPieces::typelock;
dcp->fd->getFuncProto().setOutput(piece);
}
/// \class IfcMapfunction
/// \brief Create a new function: `map function <address> [<functionname>] [nocode]`
///
/// Create a new function symbol at the provided address.
/// A symbol name can be provided, otherwise a default name is selected.
/// The new function becomes \e current for the console.
/// The provided address gives the entry point for the function. Unless the final keyword
/// "nocode" is provided, the underlying bytes in the load image are used for any
/// future disassembly or decompilation.
void IfcMapfunction::execute(istream &s)
{
string name;
int4 size;
if ((dcp->conf == (Architecture *)0)||(dcp->conf->loader == (LoadImage *)0))
throw IfaceExecutionError("No binary loaded");
Address addr = parse_machaddr(s,size,*dcp->conf->types); // Read required address;
s >> name; // Read optional name
if (name.empty())
dcp->conf->nameFunction(addr,name); // Pick default name if necessary
string basename;
Scope *scope = dcp->conf->symboltab->findCreateScopeFromSymbolName(name, "::", basename, (Scope *)0);
dcp->fd = scope->addFunction(addr,name)->getFunction();
string nocode;
s >> ws >> nocode;
if (nocode == "nocode")
dcp->fd->setNoCode(true);
}
/// \class IfcMapexternalref
/// \brief Create an external ref symbol `map externalref <address> <refaddress> [<name>]`
///
/// Creates a symbol for a function pointer and associates a specific address as
/// a value for that symbol. The first address specified is the address of the symbol,
/// The second address is the address referred to by the pointer. Indirect calls
/// through the function pointer will be converted to direct calls to the referred address.
/// A symbol name can be provided, otherwise a default one is generated.
void IfcMapexternalref::execute(istream &s)
{
int4 size1,size2;
Address addr1 = parse_machaddr(s,size1,*dcp->conf->types); // Read externalref address
Address addr2 = parse_machaddr(s,size2,*dcp->conf->types); // Read referred to address
string name;
s >> name; // Read optional name
dcp->conf->symboltab->getGlobalScope()->addExternalRef(addr1,addr2,name);
}
/// \class IfcMaplabel
/// \brief Create a code label: `map label <name> <address>`
///
/// Label a specific code address. This creates a LabSymbol which is usually
/// an internal control-flow target. The symbol is local to the \e current function
/// if it exists, otherwise the symbol is added to the global scope.
void IfcMaplabel::execute(istream &s)
{
string name;
s >> name;
if (name.size()==0)
throw IfaceParseError("Need label name and address");
int4 size;
Address addr = parse_machaddr(s,size,*dcp->conf->types); // Read address
Scope *scope;
if (dcp->fd != (Funcdata *)0)
scope = dcp->fd->getScopeLocal();
else
scope = dcp->conf->symboltab->getGlobalScope();
Symbol *sym = scope->addCodeLabel(addr,name);
scope->setAttribute(sym,Varnode::namelock|Varnode::typelock);
}
/// \class IfcMapconvert
/// \brief Create an convert directive: `map convert <format> <value> <address> <hash>`
///
/// Creates a \e convert directive that causes a targeted constant value to be displayed
/// with the specified integer format. The constant is specified by \e value, and the
/// \e address of the p-code op using the constant plus a dynamic \e hash is also given.
void IfcMapconvert::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function loaded");
string name;
uintb value;
uint8 hash;
int4 size;
uint4 format = 0;
s >> name; // Parse the format token
if (name == "hex")
format = Symbol::force_hex;
else if (name == "dec")
format = Symbol::force_dec;
else if (name == "bin")
format = Symbol::force_bin;
else if (name == "oct")
format = Symbol::force_oct;
else if (name == "char")
format = Symbol::force_char;
else
throw IfaceParseError("Bad convert format");
s >> ws >> hex >> value;
Address addr = parse_machaddr(s,size,*dcp->conf->types); // Read pc address of hash
s >> hex >> hash; // Parse the hash value
dcp->fd->getScopeLocal()->addEquateSymbol("", format, value, addr, hash);
}
/// \class IfcMapunionfacet
/// \brief Create a union field forcing directive: `map facet <union> <fieldnum> <address> <hash>`
///
/// Creates a \e facet directive that associates a given field of a \e union data-type with
/// a varnode in the context of a specific p-code op accessing it. The varnode and p-code op
/// are specified by dynamic hash.
void IfcMapunionfacet::execute(istream &s)
{
Datatype *ct;
string unionName;
int4 fieldNum;
int4 size;
uint8 hash;
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function loaded");
s >> ws >> unionName;
ct = dcp->conf->types->findByName(unionName);
if (ct == (Datatype *)0 || ct->getMetatype() != TYPE_UNION)
throw IfaceParseError("Bad union data-type: " + unionName);
s >> ws >> dec >> fieldNum;
if (fieldNum < -1 || fieldNum >= ct->numDepend())
throw IfaceParseError("Bad field index");
Address addr = parse_machaddr(s,size,*dcp->conf->types); // Read pc address of hash
s >> hex >> hash; // Parse the hash value
ostringstream s2;
s2 << "unionfacet" << dec << (fieldNum + 1) << '_' << hex << addr.getOffset();
Symbol *sym = dcp->fd->getScopeLocal()->addUnionFacetSymbol(s2.str(), ct, fieldNum, addr, hash);
dcp->fd->getScopeLocal()->setAttribute(sym, Varnode::typelock | Varnode::namelock);
}
/// \class IfcPrintdisasm
/// \brief Print disassembly of a memory range: `disassemble [<address1> <address2>]`
///
/// If no addresses are provided, disassembly for the current function is displayed.
/// Otherwise disassembly is between the two provided addresses.
void IfcPrintdisasm::execute(istream &s)
{
Architecture *glb;
Address addr;
int4 size;
// TODO add partial listings
s >> ws;
if (s.eof()) {
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
*status->fileoptr << "Assembly listing for " << dcp->fd->getName() << endl;
addr = dcp->fd->getAddress();
size = dcp->fd->getSize();
glb = dcp->fd->getArch();
}
else {
addr = parse_machaddr(s,size,*dcp->conf->types); // Read beginning address
s >> ws;
Address offset2=parse_machaddr(s,size,*dcp->conf->types);
size = offset2.getOffset() - addr.getOffset();
glb = dcp->conf;
}
IfaceAssemblyEmit assem(status->fileoptr,10);
while(size > 0) {
int4 sz;
sz = glb->translate->printAssembly(assem,addr);
addr = addr + sz;
size -= sz;
}
}
/// \class IfcDump
/// \brief Display bytes in the load image: `dump <address+size>`
///
/// The command does a hex listing of the specific memory region.
void IfcDump::execute(istream &s)
{
int4 size;
uint1 *buffer;
Address offset = parse_machaddr(s,size,*dcp->conf->types);
buffer = dcp->conf->loader->load(size,offset);
print_data(*status->fileoptr,buffer,size,offset);
delete [] buffer;
}
/// \class IfcDumpbinary
/// \brief Dump a memory to file: `binary <address+size> <filename>`
///
/// Raw bytes from the specified memory region in the load image are written
/// to a file.
void IfcDumpbinary::execute(istream &s)
{
int4 size;
uint1 *buffer;
Address offset = parse_machaddr(s,size,*dcp->conf->types);
string filename;
s >> ws;
if (s.eof())
throw IfaceParseError("Missing file name for binary dump");
s >> filename;
ofstream os;
os.open(filename.c_str());
if (!os)
throw IfaceExecutionError("Unable to open file "+filename);
buffer = dcp->conf->loader->load(size,offset);
os.write((const char *)buffer,size);
delete [] buffer;
os.close();
}
/// \class IfcDecompile
/// \brief Decompile the current function: `decompile`
///
/// Decompilation is started for the current function. Any previous decompilation
/// analysis on the function is cleared first. The process respects
/// any active break points or traces, so decompilation may not complete.
void IfcDecompile::execute(istream &s)
{
int4 res;
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
if (dcp->fd->hasNoCode()) {
*status->optr << "No code for " << dcp->fd->getName() << endl;
return;
}
if (dcp->fd->isProcStarted()) { // Free up old decompile
*status->optr << "Clearing old decompilation" << endl;
dcp->conf->clearAnalysis(dcp->fd);
}
*status->optr << "Decompiling " << dcp->fd->getName() << endl;
dcp->conf->allacts.getCurrent()->reset(*dcp->fd);
res = dcp->conf->allacts.getCurrent()->perform( *dcp->fd );
if (res<0) {
*status->optr << "Break at ";
dcp->conf->allacts.getCurrent()->printState(*status->optr);
}
else {
*status->optr << "Decompilation complete";
if (res==0)
*status->optr << " (no change)";
}
*status->optr << endl;
}
/// \class IfcPrintCFlat
/// \brief Print current function without control-flow: `print C flat`
void IfcPrintCFlat::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
dcp->conf->print->setOutputStream(status->fileoptr);
dcp->conf->print->setFlat(true);
dcp->conf->print->docFunction(dcp->fd);
dcp->conf->print->setFlat(false);
}
/// \class IfcPrintCGlobals
/// \brief Print declarations for any known global variables: `print C globals`
void IfcPrintCGlobals::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
dcp->conf->print->setOutputStream(status->fileoptr);
dcp->conf->print->docAllGlobals();
}
/// \class IfcPrintCTypes
/// \brief Print any known type definitions: `print C types`
void IfcPrintCTypes::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
if (dcp->conf->types != (TypeFactory *)0) {
dcp->conf->print->setOutputStream(status->fileoptr);
dcp->conf->print->docTypeDefinitions(dcp->conf->types);
}
}
/// \class IfcPrintCXml
/// \brief Print the current function with C syntax and XML markup:`print C xml`
void IfcPrintCXml::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
dcp->conf->print->setOutputStream(status->fileoptr);
dcp->conf->print->setMarkup(true);
dcp->conf->print->setPackedOutput(false);
dcp->conf->print->docFunction(dcp->fd);
*status->fileoptr << endl;
dcp->conf->print->setMarkup(false);
}
/// \class IfcPrintCStruct
/// \brief Print the current function using C syntax:`print C`
void IfcPrintCStruct::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
dcp->conf->print->setOutputStream(status->fileoptr);
dcp->conf->print->docFunction(dcp->fd);
}
/// \class IfcPrintLanguage
/// \brief Print current output using a specific language: `print language <langname>`
///
/// The current function must already be decompiled.
void IfcPrintLanguage::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
s >> ws;
if (s.eof())
throw IfaceParseError("No print language specified");
string langroot;
s >> langroot;
langroot = langroot + "-language";
string curlangname = dcp->conf->print->getName();
dcp->conf->setPrintLanguage(langroot);
dcp->conf->print->setOutputStream(status->fileoptr);
dcp->conf->print->docFunction(dcp->fd);
dcp->conf->setPrintLanguage(curlangname); // Reset to original language
}
/// \class IfcPrintRaw
/// \brief Print the raw p-code for the \e current function: `print raw`
///
/// Each p-code op, in its present state, is printed to the console, labeled
/// with the address of its original instruction and any output and input varnodes.
void IfcPrintRaw::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
dcp->fd->printRaw(*status->fileoptr);
}
/// \class IfcListaction
/// \brief List all current actions and rules for the decompiler: `list action`
void IfcListaction::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("Decompile action not loaded");
dcp->conf->allacts.getCurrent()->print(*status->fileoptr,0,0);
}
/// \class IfcListOverride
/// \brief Display any overrides for the current function: `list override`
///
/// Overrides include:
/// - Forced gotos
/// - Dead code delays
/// - Indirect call overrides
/// - Indirect prototype overrides
void IfcListOverride::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
*status->optr << "Function: " << dcp->fd->getName() << endl;
dcp->fd->getOverride().printRaw(*status->optr,dcp->conf);
}
/// \class IfcListprototypes
/// \brief List known prototype models: `list prototypes`
///
/// All prototype models are listed with markup indicating the
/// \e default, the evaluation model for the active function, and
/// the evaluation model for called functions.
void IfcListprototypes::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
map<string,ProtoModel *>::const_iterator iter;
for(iter=dcp->conf->protoModels.begin();iter!=dcp->conf->protoModels.end();++iter) {
ProtoModel *model = (*iter).second;
*status->optr << model->getName();
if (model == dcp->conf->defaultfp)
*status->optr << " default";
else if (model == dcp->conf->evalfp_called)
*status->optr << " eval called";
else if (model == dcp->conf->evalfp_current)
*status->optr << " eval current";
*status->optr << endl;
}
}
/// \class IfcSetcontextrange
/// \brief Set a context variable: `set context <name> <value> [<startaddress> <endaddress>]`
///
/// The named context variable is set to the provided value.
/// If a start and end address is provided, the context variable is set over this range,
/// otherwise the value is set as a default.
void IfcSetcontextrange::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
string name;
s >> name >> ws;
if (name.size()==0)
throw IfaceParseError("Missing context variable name");
s.unsetf(ios::dec | ios::hex | ios::oct); // Let user specify base
uintm value = 0xbadbeef;
s >> value;
if (value == 0xbadbeef)
throw IfaceParseError("Missing context value");
s >> ws;
if (s.eof()) { // No range indicates default value
dcp->conf->context->setVariableDefault(name,value);
return;
}
// Otherwise parse the range
int4 size1,size2;
Address addr1 = parse_machaddr(s,size1,*dcp->conf->types); // Read begin address
Address addr2 = parse_machaddr(s,size2,*dcp->conf->types); // Read end address
if (addr1.isInvalid() || addr2.isInvalid())
throw IfaceParseError("Invalid address range");
if (addr2 <= addr1)
throw IfaceParseError("Bad address range");
dcp->conf->context->setVariableRegion(name,addr1,addr2,value);
}
/// \class IfcSettrackedrange
/// \brief Set the value of a register: `set track <name> <value> [<startaddress> <endaddress>]`
///
/// The value for the register is picked up by the decompiler for functions in the tracked range.
/// The register is specified by name. A specific range can be provided, otherwise the value is
/// treated as a default.
void IfcSettrackedrange::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
string name;
s >> name >> ws;
if (name.size() ==0)
throw IfaceParseError("Missing tracked register name");
s.unsetf(ios::dec | ios::hex | ios::oct); // Let user specify base
uintb value = 0xbadbeef;
s >> value;
if (value == 0xbadbeef)
throw IfaceParseError("Missing context value");
s >> ws;
if (s.eof()) { // No range indicates default value
TrackedSet &track(dcp->conf->context->getTrackedDefault());
track.push_back( TrackedContext() );
track.back().loc = dcp->conf->translate->getRegister(name);
track.back().val = value;
return;
}
int4 size1,size2;
Address addr1 = parse_machaddr(s,size1,*dcp->conf->types);
Address addr2 = parse_machaddr(s,size2,*dcp->conf->types);
if (addr1.isInvalid() || addr2.isInvalid())
throw IfaceParseError("Invalid address range");
if (addr2 <= addr1)
throw IfaceParseError("Bad address range");
TrackedSet &track(dcp->conf->context->createSet(addr1,addr2));
TrackedSet &def(dcp->conf->context->getTrackedDefault());
track = def; // Start with default as base
track.push_back( TrackedContext() );
track.back().loc = dcp->conf->translate->getRegister(name);
track.back().val = value;
}
/// \class IfcBreakaction
/// \brief Set a breakpoint when a Rule or Action executes: `break action <actionname>`
///
/// The break point can be on either an Action or Rule. The name can specify
/// partial path information to distinguish the Action/Rule. The breakpoint causes
/// the decompilation process to stop and return control to the console immediately
/// \e after the Action or Rule has executed, but only if there was an active transformation
/// to the function.
void IfcBreakaction::execute(istream &s)
{
bool res;
string specify;
s >> specify >> ws; // Which action or rule to put breakpoint on
if (specify.empty())
throw IfaceExecutionError("No action/rule specified");
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("Decompile action not loaded");
res = dcp->conf->allacts.getCurrent()->setBreakPoint(Action::break_action, specify);
if (!res)
throw IfaceExecutionError("Bad action/rule specifier: "+specify);
}
/// \class IfcBreakstart
/// \brief Set a break point at the start of an Action: `break start <actionname>`
///
/// The break point can be on either an Action or a Rule. The name can specify
/// partial path information to distinguish the Action/Rule. The breakpoint causes
/// the decompilation process to stop and return control to the console just before
/// the Action/Rule would have executed.
void IfcBreakstart::execute(istream &s)
{
bool res;
string specify;
s >> specify >> ws; // Which action or rule to put breakpoint on
if (specify.empty())
throw IfaceExecutionError("No action/rule specified");
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("Decompile action not loaded");
res = dcp->conf->allacts.getCurrent()->setBreakPoint(Action::break_start, specify);
if (!res)
throw IfaceExecutionError("Bad action/rule specifier: "+specify);
}
/// \class IfcPrintTree
/// \brief Print all Varnodes in the \e current function: `print tree varnode`
///
/// Information about every Varnode in the data-flow graph for the function is displayed.
void IfcPrintTree::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
dcp->fd->printVarnodeTree(*status->fileoptr);
}
/// \class IfcPrintBlocktree
/// \brief Print a description of the \e current functions control-flow: `print tree block`
///
/// The recovered control-flow structure is displayed as a hierarchical list of blocks,
/// showing the nesting and code ranges covered by the blocks.
void IfcPrintBlocktree::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
dcp->fd->printBlockTree(*status->fileoptr);
}
/// \class IfcPrintSpaces
/// \brief Print all address spaces: `print spaces`
///
/// Information about every address space in the architecture/program is written
/// to the console.
void IfcPrintSpaces::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
const AddrSpaceManager *manage = dcp->conf;
int4 num = manage->numSpaces();
for(int4 i=0;i<num;++i) {
AddrSpace *spc = manage->getSpace(i);
if (spc == (AddrSpace *)0) continue;
*status->fileoptr << dec << spc->getIndex() << " : '" << spc->getShortcut() << "' " << spc->getName();
if (spc->getType() == IPTR_CONSTANT)
*status->fileoptr << " constant ";
else if (spc->getType() == IPTR_PROCESSOR)
*status->fileoptr << " processor";
else if (spc->getType() == IPTR_SPACEBASE)
*status->fileoptr << " spacebase";
else if (spc->getType() == IPTR_INTERNAL)
*status->fileoptr << " internal ";
else
*status->fileoptr << " special ";
if (spc->isBigEndian())
*status->fileoptr << " big ";
else
*status->fileoptr << " small";
*status->fileoptr << " addrsize=" << spc->getAddrSize() << " wordsize=" << spc->getWordSize();
*status->fileoptr << " delay=" << spc->getDelay();
*status->fileoptr << endl;
}
}
/// \class IfcPrintHigh
/// \brief Display all Varnodes in a HighVariable: `print high <name>`
///
/// A HighVariable associated with the current function is specified by name.
/// Information about every Varnode merged into the variable is displayed.
void IfcPrintHigh::execute(istream &s)
{
string varname;
HighVariable *high;
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
s >> varname >> ws;
high = dcp->fd->findHigh(varname);
if (high == (HighVariable *)0) // Didn't find this name
throw IfaceExecutionError("Unknown variable name: "+varname);
high->printInfo(*status->optr);
}
/// \class IfcPrintParamMeasures
/// \brief Perform parameter-id analysis on the \e current function: `print parammeasures`
void IfcPrintParamMeasures::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
ParamIDAnalysis pidanalysis( dcp->fd, false );
pidanalysis.savePretty( *status->fileoptr, true );
*status->fileoptr << "\n";
}
/// \class IfcRename
/// \brief Rename a variable: `rename <oldname> <newname>`
///
/// Change the name of a symbol. The provided name is searched for starting
/// in the scope of the current function.
void IfcRename::execute(istream &s)
{
string oldname,newname;
s >> ws >> oldname >> ws >> newname >> ws;
if (oldname.size()==0)
throw IfaceParseError("Missing old symbol name");
if (newname.size()==0)
throw IfaceParseError("Missing new name");
Symbol *sym;
vector<Symbol *> symList;
dcp->readSymbol(oldname,symList);
if (symList.empty())
throw IfaceExecutionError("No symbol named: "+oldname);
if (symList.size() == 1)
sym = symList[0];
else
throw IfaceExecutionError("More than one symbol named: "+oldname);
if (sym->getCategory() == Symbol::function_parameter)
dcp->fd->getFuncProto().setInputLock(true);
sym->getScope()->renameSymbol(sym,newname);
sym->getScope()->setAttribute(sym,Varnode::namelock|Varnode::typelock);
}
/// \class IfcRemove
/// \brief Remove a symbol by name: `remove <symbolname>`
///
/// The symbol is searched for starting in the current function's scope.
/// The resulting symbol is removed completely from the symbol table.
void IfcRemove::execute(istream &s)
{
string name;
s >> ws >> name;
if (name.size()==0)
throw IfaceParseError("Missing symbol name");
vector<Symbol *> symList;
dcp->readSymbol(name,symList);
if (symList.empty())
throw IfaceExecutionError("No symbol named: "+name);
if (symList.size() > 1)
throw IfaceExecutionError("More than one symbol named: "+name);
symList[0]->getScope()->removeSymbol(symList[0]);
}
/// \class IfcRetype
/// \brief Change the data-type of a symbol: `retype <symbolname> <typedeclaration>`
///
/// The symbol is searched for by name starting in the current function's scope.
/// If the type declaration includes a new name for the variable, the
/// variable is renamed as well.
void IfcRetype::execute(istream &s)
{
Datatype *ct;
string name,newname;
s >> ws >> name;
if (name.size()==0)
throw IfaceParseError("Must specify name of symbol");
ct = parse_type(s,newname,dcp->conf);
Symbol *sym;
vector<Symbol *> symList;
dcp->readSymbol(name,symList);
if (symList.empty())
throw IfaceExecutionError("No symbol named: "+name);
if (symList.size() > 1)
throw IfaceExecutionError("More than one symbol named : "+name);
else
sym = symList[0];
if (sym->getCategory()==Symbol::function_parameter)
dcp->fd->getFuncProto().setInputLock(true);
sym->getScope()->retypeSymbol(sym,ct);
sym->getScope()->setAttribute(sym,Varnode::typelock);
if ((newname.size()!=0)&&(newname != name)) {
sym->getScope()->renameSymbol(sym,newname);
sym->getScope()->setAttribute(sym,Varnode::namelock);
}
}
/// \class IfcIsolate
/// \brief Mark a symbol as isolated from speculative merging: `isolate <name>`
void IfcIsolate::execute(istream &s)
{
string symbolName;
s >> ws >> symbolName;
if (symbolName.size() == 0)
throw IfaceParseError("Missing symbol name");
Symbol *sym;
vector<Symbol *> symList;
dcp->readSymbol(symbolName,symList);
if (symList.empty())
throw IfaceExecutionError("No symbol named: "+symbolName);
if (symList.size() == 1)
sym = symList[0];
else
throw IfaceExecutionError("More than one symbol named: "+symbolName);
sym->setIsolated(true);
}
/// The Varnode is selected from the \e current function. It is specified as a
/// storage location with info about its defining p-code in parantheses.
/// - `%EAX(r0x10000:0x65)`
/// - `%ECX(i)`
/// - `r0x10001000:4(:0x96)`
/// - `u0x00001100:1(:0x102)`
/// - `#0x1(0x10205:0x27)`
///
/// The storage address space is given as the \e short-cut character followed by the
/// address offset. For register spaces, the name of the register can be given instead of the
/// offset. After the offset, a size can be specified with a ':' followed by the size in bytes.
/// If size is not provided and there is no register name, a default word size is assigned based
/// on the address space.
///
/// The defining p-code op is specified either as:
/// - An address and sequence number: `%EAX(r0x10000:0x65)`
/// - Just a sequence number: `%EAX(:0x65)` or
/// - An "i" token for inputs: `%EAX(i)`
///
/// For a constant Varnode, the storage offset is the actual value of the constant, and
/// the p-code address and sequence number must both be present and specify the p-code op
/// that \e reads the constant.
/// \param s is the given input stream
/// \return the Varnode object
Varnode *IfaceDecompData::readVarnode(istream &s)
{
uintm uq;
int4 defsize;
Varnode *vn = (Varnode *)0;
if (fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
Address pc;
Address loc(parse_varnode(s,defsize,pc,uq,*conf->types));
if (loc.getSpace()->getType() == IPTR_CONSTANT) {
if (pc.isInvalid() || (uq == ~((uintm)0)))
throw IfaceParseError("Missing p-code sequence number");
SeqNum seq(pc,uq);
PcodeOp *op = fd->findOp(seq);
if (op != (PcodeOp *)0) {
for(int4 i=0;i<op->numInput();++i) {
Varnode *tmpvn = op->getIn(i);
if (tmpvn->getAddr() == loc) {
vn = tmpvn;
break;
}
}
}
}
else if (pc.isInvalid()&&(uq==~((uintm)0)))
vn = fd->findVarnodeInput(defsize,loc);
else if ((!pc.isInvalid())&&(uq!=~((uintm)0)))
vn = fd->findVarnodeWritten(defsize,loc,pc,uq);
else {
VarnodeLocSet::const_iterator iter,enditer;
iter = fd->beginLoc(defsize,loc);
enditer = fd->endLoc(defsize,loc);
while(iter != enditer) {
vn = *iter++;
if (vn->isFree()) continue;
if (vn->isWritten()) {
if ((!pc.isInvalid()) && (vn->getDef()->getAddr()==pc)) break;
if ((uq!=~((uintm)0))&&(vn->getDef()->getTime()==uq)) break;
}
}
}
if (vn == (Varnode *)0)
throw IfaceExecutionError("Requested varnode does not exist");
return vn;
}
/// Find any symbols matching the given name in the current scope. Scope is either the
/// current function scope if a function is active, otherwise the global scope.
/// \param name is the given name, either absolute or partial
/// \param res will hold any matching symbols
void IfaceDecompData::readSymbol(const string &name,vector<Symbol *> &res)
{
Scope *scope = (fd == (Funcdata *)0) ? conf->symboltab->getGlobalScope() : fd->getScopeLocal();
string basename;
scope = conf->symboltab->resolveScopeFromSymbolName(name, "::", basename, scope);
if (scope == (Scope *)0)
throw IfaceParseError("Bad namespace for symbol: " + name);
scope->queryByName(basename,res);
}
/// \class IfcPrintVarnode
/// \brief Print information about a Varnode: `print varnode <varnode>`
///
/// Attributes of the indicated Varnode from the \e current function are printed
/// to the console. If the Varnode belongs to a HighVariable, information about
/// it and all its Varnodes are printed as well.
void IfcPrintVarnode::execute(istream &s)
{
Varnode *vn;
vn = dcp->readVarnode(s);
if (vn->isAnnotation()||(!dcp->fd->isHighOn()))
vn->printInfo(*status->optr);
else
vn->getHigh()->printInfo(*status->optr);
}
/// \class IfcPrintCover
/// \brief Print cover info about a HighVariable: `print cover high <name>`
///
/// A HighVariable is specified by its symbol name in the current function's scope.
/// Information about the code ranges where the HighVariable is in scope is printed.
void IfcPrintCover::execute(istream &s)
{
HighVariable *high;
string name;
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
s >> ws >> name;
if (name.size()==0)
throw IfaceParseError("Missing variable name");
high = dcp->fd->findHigh(name);
if (high == (HighVariable *)0)
throw IfaceExecutionError("Unable to find variable: "+name);
high->printCover(*status->optr);
}
/// \class IfcVarnodehighCover
/// \brief Print cover info about a HighVariable: `print cover varnodehigh <varnode>`
///
/// The HighVariable is selected by specifying one of its Varnodes.
/// Information about the code ranges where the HighVariable is in scope is printed.
void IfcVarnodehighCover::execute(istream &s)
{
Varnode *vn;
vn = dcp->readVarnode(s);
if (vn == (Varnode *)0)
throw IfaceParseError("Unknown varnode");
if (vn->getHigh() != (HighVariable *)0)
vn->getHigh()->printCover(*status->optr);
else
*status->optr << "Unmerged" << endl;
}
/// \class IfcPrintExtrapop
/// \brief Print change to stack pointer for called function: `print extrapop [<functionname>]`
///
/// For the selected function, the extra amount each called function changes the stack pointer
/// (over popping the return value) is printed to console. The function is selected by
/// name, or if no name is given, the \e current function is selected.
void IfcPrintExtrapop::execute(istream &s)
{
string name;
s >> ws >> name;
if (name.size() == 0) {
if (dcp->fd != (Funcdata *)0) {
int4 num = dcp->fd->numCalls();
for(int4 i=0;i<num;++i) {
FuncCallSpecs *fc = dcp->fd->getCallSpecs(i);
*status->optr << "ExtraPop for " << fc->getName() << '(';
*status->optr << fc->getOp()->getAddr() << ')';
int4 expop = fc->getEffectiveExtraPop();
*status->optr << " ";
if (expop == ProtoModel::extrapop_unknown)
*status->optr << "unknown";
else
*status->optr << dec << expop;
*status->optr << '(';
expop = fc->getExtraPop();
if (expop == ProtoModel::extrapop_unknown)
*status->optr << "unknown";
else
*status->optr << dec << expop;
*status->optr << ')' << endl;
}
}
else {
int4 expop = dcp->conf->defaultfp->getExtraPop();
*status->optr << "Default extra pop = ";
if (expop == ProtoModel::extrapop_unknown)
*status->optr << "unknown" << endl;
else
*status->optr << dec << expop << endl;
}
}
else {
Funcdata *fd;
fd = dcp->conf->symboltab->getGlobalScope()->queryFunction( name );
if (fd == (Funcdata *)0)
throw IfaceExecutionError("Unknown function: "+name);
int4 expop = fd->getFuncProto().getExtraPop();
*status->optr << "ExtraPop for function " << name << " is ";
if (expop == ProtoModel::extrapop_unknown)
*status->optr << "unknown" << endl;
else
*status->optr << dec << expop << endl;
if (dcp->fd != (Funcdata *)0) {
int4 num = dcp->fd->numCalls();
for(int4 i=0;i<num;++i) {
FuncCallSpecs *fc = dcp->fd->getCallSpecs(i);
if (fc->getName() == fd->getName()) {
expop = fc->getEffectiveExtraPop();
*status->optr << "For this function, extrapop = ";
if (expop == ProtoModel::extrapop_unknown)
*status->optr << "unknown";
else
*status->optr << dec << expop;
*status->optr << '(';
expop = fc->getExtraPop();
if (expop == ProtoModel::extrapop_unknown)
*status->optr << "unknown";
else
*status->optr << dec << expop;
*status->optr << ')' << endl;
}
}
}
}
}
/// \class IfcVarnodeCover
/// \brief Print cover information about a Varnode: `print cover varnode <varnode>`
///
/// Information about code ranges where the single Varnode is in scope are printed.
void IfcVarnodeCover::execute(istream &s)
{
Varnode *vn;
vn = dcp->readVarnode(s);
if (vn == (Varnode *)0)
throw IfaceParseError("Unknown varnode");
vn->printCover(*status->optr);
}
/// \class IfcNameVarnode
/// \brief Attach a named symbol to a specific Varnode: `name varnode <varnode> <name>`
///
/// A new local symbol is created for the \e current function, and
/// is attached to the specified Varnode. The \e current function must be decompiled
/// again to see the effects. The new symbol is \e name-locked with the specified
/// name, but the data-type of the symbol is allowed to float.
void IfcNameVarnode::execute(istream &s)
{
string token;
int4 size;
uintm uq;
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
Address pc;
Address loc(parse_varnode(s,size,pc,uq,*dcp->conf->types)); // Get specified varnode
s >> ws >> token; // Get the new name of the varnode
if (token.size()==0)
throw IfaceParseError("Must specify name");
Datatype *ct = dcp->conf->types->getBase(size,TYPE_UNKNOWN);
dcp->conf->clearAnalysis(dcp->fd); // Make sure varnodes are cleared
Scope *scope = dcp->fd->getScopeLocal()->discoverScope(loc,size,pc);
if (scope == (Scope *)0) // Variable does not have natural scope
scope = dcp->fd->getScopeLocal(); // force it to be in function scope
Symbol *sym = scope->addSymbol(token,ct,loc,pc)->getSymbol();
scope->setAttribute(sym,Varnode::namelock);
*status->fileoptr << "Successfully added " << token;
*status->fileoptr << " to scope " << scope->getFullName() << endl;
}
/// \class IfcTypeVarnode
/// \brief Attach a typed symbol to a specific Varnode: `type varnode <varnode> <typedeclaration>`
///
/// A new local symbol is created for the \e current function, and
/// is attached to the specified Varnode. The \e current function must be decompiled
/// again to see the effects. The new symbol is \e type-locked with the data-type specified
/// in the type declaration. If a name is specified in the declaration, the symbol
/// is \e name-locked as well.
void IfcTypeVarnode::execute(istream &s)
{
int4 size;
uintm uq;
Datatype *ct;
string name;
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
Address pc;
Address loc(parse_varnode(s,size,pc,uq,*dcp->conf->types)); // Get specified varnode
ct = parse_type(s,name,dcp->conf);
dcp->conf->clearAnalysis(dcp->fd); // Make sure varnodes are cleared
Scope *scope = dcp->fd->getScopeLocal()->discoverScope(loc,size,pc);
if (scope == (Scope *)0) // Variable does not have natural scope
scope = dcp->fd->getScopeLocal(); // force it to be in function scope
Symbol *sym = scope->addSymbol(name,ct,loc,pc)->getSymbol();
scope->setAttribute(sym,Varnode::typelock);
sym->setIsolated(true);
if (name.size() > 0)
scope->setAttribute(sym,Varnode::namelock);
*status->fileoptr << "Successfully added " << sym->getName();
*status->fileoptr << " to scope " << scope->getFullName() << endl;
}
/// \class IfcForceFormat
/// \brief Mark a constant to be printed in a specific format: `force varnode <varnode> [hex|dec|oct|bin|char]`
///
/// A constant Varnode in the \e current function is marked so that is forced
/// to print in one of the formats: \b hex, \b dec, \b oct, \b bin, \b char.
void IfcForceFormat::execute(istream &s)
{
Varnode *vn = dcp->readVarnode(s);
if (!vn->isConstant())
throw IfaceExecutionError("Can only force format on a constant");
type_metatype mt = vn->getType()->getMetatype();
if ((mt!=TYPE_INT)&&(mt!=TYPE_UINT)&&(mt!=TYPE_UNKNOWN))
throw IfaceExecutionError("Can only force format on integer type constant");
dcp->fd->buildDynamicSymbol(vn);
Symbol *sym = vn->getHigh()->getSymbol();
if (sym == (Symbol *)0)
throw IfaceExecutionError("Unable to create symbol");
string formatString;
s >> ws >> formatString;
uint4 format = Datatype::encodeIntegerFormat(formatString);
sym->getScope()->setDisplayFormat(sym,format);
sym->getScope()->setAttribute(sym,Varnode::typelock);
*status->optr << "Successfully forced format display" << endl;
}
/// \class IfcForceDatatypeFormat
/// \brief Mark constants of a data-type to be printed in a specific format: `force datatype <datatype> [hex|dec|oct|bin|char]`
///
/// A display format attribute is set on the indicated data-type.
void IfcForceDatatypeFormat::execute(istream &s)
{
Datatype *dt;
string typeName;
s >> ws >> typeName;
dt = dcp->conf->types->findByName(typeName);
if (dt == (Datatype *)0)
throw IfaceExecutionError("Unknown data-type: " + typeName);
string formatString;
s >> ws >> formatString;
uint4 format = Datatype::encodeIntegerFormat(formatString);
dcp->conf->types->setDisplayFormat(dt, format);
*status->optr << "Successfully forced data-type display" << endl;
}
/// \class IfcForcegoto
/// \brief Force a branch to be an unstructured \b goto: `force goto <branchaddr> <targetaddr>`
///
/// Create an override that forces the decompiler to treat the specified branch
/// as unstructured. The branch will be modeled as a \b goto statement.
/// The branch is specified by first providing the address of the branching instruction,
/// then the destination address.
void IfcForcegoto::execute(istream &s)
{
int4 discard;
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
s >> ws;
Address target(parse_machaddr(s,discard,*dcp->conf->types));
s >> ws;
Address dest(parse_machaddr(s,discard,*dcp->conf->types));
dcp->fd->getOverride().insertForceGoto(target,dest);
}
/// \class IfcProtooverride
/// \brief Override the prototype of a called function: `override prototype <address> <declaration>`
///
/// Force a specified prototype declaration on a called function when decompiling
/// the current function. The current function must be decompiled again to see the effect.
/// The called function is indicated by the address of its calling instruction.
/// The prototype only affects decompilation for the \e current function.
void IfcProtooverride::execute(istream &s)
{
int4 discard;
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
s >> ws;
Address callpoint(parse_machaddr(s,discard,*dcp->conf->types));
int4 i;
for(i=0;dcp->fd->numCalls();++i)
if (dcp->fd->getCallSpecs(i)->getOp()->getAddr() == callpoint) break;
if (i == dcp->fd->numCalls())
throw IfaceExecutionError("No call is made at this address");
PrototypePieces pieces;
parse_protopieces(pieces,s,dcp->conf); // Parse the prototype from stream
FuncProto *newproto = new FuncProto();
// Make proto whose storage is internal, not backed by a real scope
newproto->setInternal(pieces.model,dcp->conf->types->getTypeVoid());
newproto->setPieces(pieces);
dcp->fd->getOverride().insertProtoOverride(callpoint,newproto);
dcp->fd->clear(); // Clear any analysis (this leaves overrides intact)
}
/// \class IfcJumpOverride
/// \brief Provide an overriding jump-table for an indirect branch: `override jumptable ...`
///
/// The command expects the address of an indirect branch in the \e current function,
/// followed by the keyword \b table then a list of possible target addresses of the branch.
/// \code
/// override jumptable r0x1000 table r0x1020 r0x1030 r0x1043 ...
/// \endcode
/// The command can optionally take the keyword \b startval followed by an
/// integer indicating the value taken by the \e normalized switch variable that
/// produces the first address in the table.
/// \code
/// override jumptable startval 10 table r0x1020 r0x1030 ...
/// \endcode
void IfcJumpOverride::execute(istream &s)
{
int4 discard;
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
s >> ws;
Address jmpaddr( parse_machaddr(s,discard,*dcp->conf->types));
JumpTable *jt = dcp->fd->installJumpTable(jmpaddr);
vector<Address> adtable;
Address naddr;
uintb h=0;
uintb sv = 0;
string token;
s >> token;
// if (token == "norm") {
// naddr = parse_machaddr(s,discard,*dcp->conf->types);
// s >> ws;
// s >> h;
// s >> token;
// }
if (token == "startval") {
s.unsetf(ios::dec | ios::hex | ios::oct); // Let user specify base
s >> sv;
s >> token;
}
if (token == "table") {
s >> ws;
while(!s.eof()) {
Address addr( parse_machaddr(s,discard,*dcp->conf->types));
adtable.push_back(addr);
}
}
if (adtable.empty())
throw IfaceExecutionError("Missing jumptable address entries");
jt->setOverride(adtable,naddr,h,sv);
*status->optr << "Successfully installed jumptable override" << endl;
}
/// \class IfcFlowOverride
/// \brief Create a control-flow override: `override flow <address> branch|call|callreturn|return`
///
/// Change the nature of the control-flow at the specified address, as indicated by the
/// final token on the command-line:
/// - branch - Change the CALL or RETURN to a BRANCH
/// - call - Change a BRANCH or RETURN to a CALL
/// - callreturn - Change a BRANCH or RETURN to a CALL followed by a RETURN
/// - return - Change a CALLIND or BRANCHIND to a RETURN
void IfcFlowOverride::execute(istream &s)
{
int4 discard;
uint4 type;
string token;
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
s >> ws;
Address addr( parse_machaddr(s,discard,*dcp->conf->types));
s >> token;
if (token.size() == 0)
throw IfaceParseError("Missing override type");
type = Override::stringToType(token);
if (type == Override::NONE)
throw IfaceParseError("Bad override type");
dcp->fd->getOverride().insertFlowOverride(addr,type);
*status->optr << "Successfully added override" << endl;
}
/// \class IfcDeadcodedelay
/// \brief Change when dead code elimination starts: `deadcode delay <name> <delay>`
///
/// An address space is selected by name, along with a pass number.
/// Dead code elimination for Varnodes in that address space is changed to start
/// during that pass. If there is a \e current function, the delay is altered only for
/// that function, otherwise the delay is set globally for all functions.
void IfcDeadcodedelay::execute(istream &s)
{
string name;
int4 delay = -1;
AddrSpace *spc;
s >> name;
s >> ws;
s >> delay;
spc = dcp->conf->getSpaceByName(name);
if (spc == (AddrSpace *)0)
throw IfaceParseError("Bad space: "+name);
if (delay == -1)
throw IfaceParseError("Need delay integer");
if (dcp->fd != (Funcdata *)0) {
dcp->fd->getOverride().insertDeadcodeDelay(spc,delay);
*status->optr << "Successfully overrided deadcode delay for single function" << endl;
}
else {
dcp->conf->setDeadcodeDelay(spc,delay);
*status->optr << "Successfully overrided deadcode delay for all functions" << endl;
}
}
/// \class IfcGlobalAdd
/// \brief Add a memory range as discoverable global variables: `global add <address+size>`
///
/// The decompiler will treat Varnodes stored in the new memory range as persistent
/// global variables.
void IfcGlobalAdd::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No image loaded");
int4 size;
Address addr = parse_machaddr(s,size,*dcp->conf->types);
uintb first = addr.getOffset();
uintb last = first + (size-1);
Scope *scope = dcp->conf->symboltab->getGlobalScope();
dcp->conf->symboltab->addRange(scope,addr.getSpace(),first,last);
}
/// \class IfcGlobalRemove
/// \brief Remove a memory range from discoverable global variables: `global remove <address+size>`
///
/// The will no longer treat Varnodes stored in the memory range as persistent global
/// variables. The will be treated as local or temporary storage.
void IfcGlobalRemove::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No image loaded");
int4 size;
Address addr = parse_machaddr(s,size,*dcp->conf->types);
uintb first = addr.getOffset();
uintb last = first + (size-1);
Scope *scope = dcp->conf->symboltab->getGlobalScope();
dcp->conf->symboltab->removeRange(scope,addr.getSpace(),first,last);
}
/// \class IfcGlobalify
/// \brief Treat all normal memory as discoverable global variables: `global spaces`
///
/// This has the drastic effect that the decompiler will treat all registers and stack
/// locations as global variables.
void IfcGlobalify::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
dcp->conf->globalify();
*status->optr << "Successfully made all registers/memory locations global" << endl;
}
/// \class IfcGlobalRegisters
/// \brief Name global registers: `global registers`
///
/// Name any global symbol stored in a register with the name of the register.
void IfcGlobalRegisters::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
map<VarnodeData,string> reglist;
dcp->conf->translate->getAllRegisters(reglist);
map<VarnodeData,string>::const_iterator iter;
AddrSpace *spc = (AddrSpace *)0;
uintb lastoff=0;
Scope *globalscope = dcp->conf->symboltab->getGlobalScope();
int4 count = 0;
for(iter=reglist.begin();iter!=reglist.end();++iter) {
const VarnodeData &dat( (*iter).first );
if (dat.space == spc) {
if (dat.offset<=lastoff) continue; // Nested register def
}
spc = dat.space;
lastoff = dat.offset+dat.size-1;
Address addr(spc,dat.offset);
uint4 flags=0;
// Check if the register location is global
globalscope->queryProperties(addr,dat.size,Address(),flags);
if ((flags & Varnode::persist)!=0) {
Datatype *ct = dcp->conf->types->getBase(dat.size,TYPE_UINT);
globalscope->addSymbol((*iter).second,ct,addr,Address());
count += 1;
}
}
if (count == 0)
*status->optr << "No global registers" << endl;
else
*status->optr << "Successfully made a global symbol for " << count << " registers" << endl;
}
/// The use is non-trivial if it can be traced to any p-code operation except
/// a COPY, CAST, INDIRECT, or MULTIEQUAL.
/// \param vn is the given Varnode
/// \return \b true if there is a non-trivial use
bool IfcPrintInputs::nonTrivialUse(Varnode *vn)
{
vector<Varnode *> vnlist;
bool res = false;
vnlist.push_back(vn);
uint4 proc = 0;
while(proc < vnlist.size()) {
Varnode *tmpvn = vnlist[proc];
proc += 1;
list<PcodeOp *>::const_iterator iter;
for(iter=tmpvn->beginDescend();iter!=tmpvn->endDescend();++iter) {
PcodeOp *op = *iter;
if ((op->code() == CPUI_COPY)||
(op->code() == CPUI_CAST)||
(op->code() == CPUI_INDIRECT) ||
(op->code() == CPUI_MULTIEQUAL)) {
Varnode *outvn = op->getOut();
if (!outvn->isMark()) {
outvn->setMark();
vnlist.push_back(outvn);
}
}
else {
res = true;
break;
}
}
}
for(int4 i=0;i<vnlist.size();++i)
vnlist[i]->clearMark();
return res;
}
/// Look for any value flowing into the Varnode coming from anything
/// other than an input Varnode with the same storage. The value can flow through
/// a COPY, CAST, INDIRECT, or MULTIEQUAL
/// \param vn is the given Varnode
/// \return 0 if Varnode is restored, 1 otherwise
int4 IfcPrintInputs::checkRestore(Varnode *vn)
{
vector<Varnode *> vnlist;
int4 res = 0;
vnlist.push_back(vn);
uint4 proc = 0;
while(proc < vnlist.size()) {
Varnode *tmpvn = vnlist[proc];
proc += 1;
if (tmpvn->isInput()) {
if ((tmpvn->getSize() != vn->getSize()) ||
(tmpvn->getAddr() != vn->getAddr())) {
res = 1;
break;
}
}
else if (!tmpvn->isWritten()) {
res = 1;
break;
}
else {
PcodeOp *op = tmpvn->getDef();
if ((op->code() == CPUI_COPY)||(op->code()==CPUI_CAST)) {
tmpvn = op->getIn(0);
if (!tmpvn->isMark()) {
tmpvn->setMark();
vnlist.push_back(tmpvn);
}
}
else if (op->code() == CPUI_INDIRECT) {
tmpvn = op->getIn(0);
if (!tmpvn->isMark()) {
tmpvn->setMark();
vnlist.push_back(tmpvn);
}
}
else if (op->code() == CPUI_MULTIEQUAL) {
for(int4 i=0;i<op->numInput();++i) {
tmpvn = op->getIn(i);
if (!tmpvn->isMark()) {
tmpvn->setMark();
vnlist.push_back(tmpvn);
}
}
}
else {
res = 1;
break;
}
}
}
for(int4 i=0;i<vnlist.size();++i)
vnlist[i]->clearMark();
return res;
}
/// For the given storage location, check that it is \e restored
/// from its original input value.
/// \param vn is the given storage location
/// \param fd is the function being analyzed
bool IfcPrintInputs::findRestore(Varnode *vn,Funcdata *fd)
{
VarnodeLocSet::const_iterator iter,enditer;
iter = fd->beginLoc(vn->getAddr());
enditer = fd->endLoc(vn->getAddr());
int4 count = 0;
while(iter != enditer) {
Varnode *vn = *iter;
++iter;
if (!vn->hasNoDescend()) continue;
if (!vn->isWritten()) continue;
PcodeOp *op = vn->getDef();
if (op->code() == CPUI_INDIRECT) continue; // Not a global return address force
int4 res = checkRestore(vn);
if (res != 0) return false;
count += 1;
}
return (count>0);
}
/// For each input Varnode, print information about the Varnode,
/// any explicit symbol it represents, and info about how the value is used.
/// \param fd is the function
/// \param s is the output stream to write to
void IfcPrintInputs::print(Funcdata *fd,ostream &s)
{
VarnodeDefSet::const_iterator iter,enditer;
s << "Function: " << fd->getName() << endl;
iter = fd->beginDef(Varnode::input);
enditer = fd->endDef(Varnode::input);
while(iter != enditer) {
Varnode *vn = *iter;
++iter;
vn->printRaw(s);
if (fd->isHighOn()) {
Symbol *sym = vn->getHigh()->getSymbol();
if (sym != (Symbol *)0)
s << " " << sym->getName();
}
bool findres = findRestore(vn,fd);
bool nontriv = nonTrivialUse(vn);
if (findres && !nontriv)
s << " restored";
else if (nontriv)
s << " nontriv";
s << endl;
}
}
/// \class IfcPrintInputs
/// \brief Print info about the current function's input Varnodes: `print inputs`
void IfcPrintInputs::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
print(dcp->fd,*status->fileoptr);
}
/// \class IfcPrintInputsAll
/// \brief Print info about input Varnodes for all functions: `print inputs all`
///
/// Each function is decompiled, and info about its input Varnodes are printed.
void IfcPrintInputsAll::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
iterateFunctionsAddrOrder();
}
void IfcPrintInputsAll::iterationCallback(Funcdata *fd)
{
if (fd->hasNoCode()) {
*status->optr << "No code for " << fd->getName() << endl;
return;
}
try {
dcp->conf->clearAnalysis(fd); // Clear any old analysis
dcp->conf->allacts.getCurrent()->reset(*fd);
dcp->conf->allacts.getCurrent()->perform( *fd );
IfcPrintInputs::print(fd,*status->fileoptr);
}
catch(LowlevelError &err) {
*status->optr << "Skipping " << fd->getName() << ": " << err.explain << endl;
}
dcp->conf->clearAnalysis(fd);
}
/// \class IfcLockPrototype
/// \brief Lock in the \e current function's prototype: `prototype lock`
///
/// Lock in the existing formal parameter names and data-types for any future
/// decompilation. Both input parameters and the return value are locked.
void IfcLockPrototype::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
dcp->fd->getFuncProto().setInputLock(true);
dcp->fd->getFuncProto().setOutputLock(true);
}
/// \class IfcUnlockPrototype
/// \brief Unlock the \e current function's prototype: `prototype unlock`
///
/// Unlock all input parameters and the return value, so future decompilation
/// is not constrained with their data-type or name.
void IfcUnlockPrototype::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
dcp->fd->getFuncProto().setInputLock(false);
dcp->fd->getFuncProto().setOutputLock(false);
}
/// \class IfcPrintLocalrange
/// \brief Print range of locals on the stack: `print localrange`
///
/// Print the memory range(s) on the stack is or could be used for
///
void IfcPrintLocalrange::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
dcp->fd->printLocalRange( *status->optr );
}
/// \class IfcPrintMap
/// \brief Print info about a scope/namespace: `print map <name>`
///
/// Prints information about the discoverable memory ranges for the scope,
/// and prints a description of every symbol in the scope.
void IfcPrintMap::execute(istream &s)
{
string name;
Scope *scope;
s >> name;
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image");
if (name.size() != 0 || dcp->fd==(Funcdata *)0) {
string fullname = name + "::a"; // Add fake variable name
scope = dcp->conf->symboltab->resolveScopeFromSymbolName(fullname, "::", fullname, (Scope *)0);
}
else
scope = dcp->fd->getScopeLocal();
if (scope == (Scope *)0)
throw IfaceExecutionError("No map named: "+name);
*status->fileoptr << scope->getFullName() << endl;
scope->printBounds(*status->fileoptr);
scope->printEntries(*status->fileoptr);
}
/// \class IfcProduceC
/// \brief Write decompilation for all functions to a file: `produce C <filename>`
///
/// Iterate over all functions in the program. For each function, decompilation is
/// performed and output is appended to the file.
void IfcProduceC::execute(istream &s)
{
string name;
s >> ws >> name;
if (name.size()==0)
throw IfaceParseError("Need file name to write to");
ofstream os;
os.open(name.c_str());
dcp->conf->print->setOutputStream(&os);
iterateFunctionsAddrOrder();
os.close();
}
void IfcProduceC::iterationCallback(Funcdata *fd)
{
clock_t start_time,end_time;
float duration;
if (fd->hasNoCode()) {
*status->optr << "No code for " << fd->getName() << endl;
return;
}
try {
dcp->conf->clearAnalysis(fd); // Clear any old analysis
dcp->conf->allacts.getCurrent()->reset(*fd);
start_time = clock();
dcp->conf->allacts.getCurrent()->perform( *fd );
end_time = clock();
*status->optr << "Decompiled " << fd->getName();
// *status->optr << ": " << hex << fd->getAddress().getOffset();
*status->optr << '(' << dec << fd->getSize() << ')';
duration = ((float)(end_time-start_time))/CLOCKS_PER_SEC;
duration *= 1000.0;
*status->optr << " time=" << fixed << setprecision(0) << duration << " ms" << endl;
dcp->conf->print->docFunction(fd);
}
catch(LowlevelError &err) {
*status->optr << "Skipping " << fd->getName() << ": " << err.explain << endl;
}
dcp->conf->clearAnalysis(fd);
}
/// \class IfcProducePrototypes
/// \brief Determine the prototype model for all functions: `produce prototypes`
///
/// Functions are walked in leaf order.
void IfcProducePrototypes::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image");
if (dcp->cgraph == (CallGraph *)0)
throw IfaceExecutionError("Callgraph has not been built");
if (dcp->conf->evalfp_current == (ProtoModel *)0) {
*status->optr << "Always using default prototype" << endl;
return;
}
if (!dcp->conf->evalfp_current->isMerged()) {
*status->optr << "Always using prototype " << dcp->conf->evalfp_current->getName() << endl;
return;
}
ProtoModelMerged *model = (ProtoModelMerged *)dcp->conf->evalfp_current;
*status->optr << "Trying to distinguish between prototypes:" << endl;
for(int4 i=0;i<model->numModels();++i)
*status->optr << " " << model->getModel(i)->getName() << endl;
iterateFunctionsLeafOrder();
}
void IfcProducePrototypes::iterationCallback(Funcdata *fd)
{
clock_t start_time,end_time;
float duration;
*status->optr << fd->getName() << ' ';
if (fd->hasNoCode()) {
*status->optr << "has no code" << endl;
return;
}
if (fd->getFuncProto().isInputLocked()) {
*status->optr << "has locked prototype" << endl;
return;
}
try {
dcp->conf->clearAnalysis(fd); // Clear any old analysis
dcp->conf->allacts.getCurrent()->reset(*fd);
start_time = clock();
dcp->conf->allacts.getCurrent()->perform( *fd );
end_time = clock();
// *status->optr << "Decompiled " << fd->getName();
// *status->optr << '(' << dec << fd->getSize() << ')';
*status->optr << "proto=" << fd->getFuncProto().getModelName();
fd->getFuncProto().setModelLock(true);
duration = ((float)(end_time-start_time))/CLOCKS_PER_SEC;
duration *= 1000.0;
*status->optr << " time=" << fixed << setprecision(0) << duration << " ms" << endl;
}
catch(LowlevelError &err) {
*status->optr << "Skipping " << fd->getName() << ": " << err.explain << endl;
}
dcp->conf->clearAnalysis(fd);
}
/// \class IfcContinue
/// \brief Continue decompilation after a break point: `continue`
///
/// This command assumes decompilation has been started and has hit a break point.
void IfcContinue::execute(istream &s)
{
int4 res;
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("Decompile action not loaded");
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
if (dcp->conf->allacts.getCurrent()->getStatus() == Action::status_start)
throw IfaceExecutionError("Decompilation has not been started");
if (dcp->conf->allacts.getCurrent()->getStatus() == Action::status_end)
throw IfaceExecutionError("Decompilation is already complete");
res = dcp->conf->allacts.getCurrent()->perform( *dcp->fd ); // Try to continue decompilation
if (res<0) {
*status->optr << "Break at ";
dcp->conf->allacts.getCurrent()->printState(*status->optr);
}
else {
*status->optr << "Decompilation complete";
if (res==0)
*status->optr << " (no change)";
}
*status->optr << endl;
}
/// \class IfcGraphDataflow
/// \brief Write a graph representation of data-flow to a file: `graph dataflow <filename>`
///
/// The data-flow graph for the \e current function, in its current state of transform,
/// is written to the indicated file.
void IfcGraphDataflow::execute(istream &s)
{
string filename;
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
s >> filename;
if (filename.size()==0)
throw IfaceParseError("Missing output file");
if (!dcp->fd->isProcStarted())
throw IfaceExecutionError("Syntax tree not calculated");
ofstream thefile( filename.c_str());
if (!thefile)
throw IfaceExecutionError("Unable to open output file: "+filename);
dump_dataflow_graph(*dcp->fd,thefile);
thefile.close();
}
/// \class IfcGraphControlflow
/// \brief Write a graph representation of control-flow to a file: `graph controlflow <filename>`
///
/// The control-flow graph for the \e current function, in its current state of transform,
/// is written to the indicated file.
void IfcGraphControlflow::execute(istream &s)
{
string filename;
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
s >> filename;
if (filename.size()==0)
throw IfaceParseError("Missing output file");
if (dcp->fd->getBasicBlocks().getSize()==0)
throw IfaceExecutionError("Basic block structure not calculated");
ofstream thefile( filename.c_str());
if (!thefile)
throw IfaceExecutionError("Unable to open output file: "+filename);
dump_controlflow_graph(dcp->fd->getName(),dcp->fd->getBasicBlocks(),thefile);
thefile.close();
}
/// \class IfcGraphDom
/// \brief Write the forward dominance graph to a file: `graph dom <filename>`
///
/// The dominance tree, associated with the control-flow graph of the \e current function
/// in its current state of transform, is written to the indicated file.
void IfcGraphDom::execute(istream &s)
{
string filename;
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
s >> filename;
if (filename.size()==0)
throw IfaceParseError("Missing output file");
if (!dcp->fd->isProcStarted())
throw IfaceExecutionError("Basic block structure not calculated");
ofstream thefile( filename.c_str());
if (!thefile)
throw IfaceExecutionError("Unable to open output file: "+filename);
dump_dom_graph(dcp->fd->getName(),dcp->fd->getBasicBlocks(),thefile);
thefile.close();
}
/// \class IfcCommentInstr
/// \brief Attach a comment to an address: `comment <address> comment text...`
///
/// Add a comment to the database, suitable for integration into decompiler output
/// for the \e current function. The command-line takes the address of the
/// machine instruction which the comment will be attached to and is followed by
/// the text of the comment.
void IfcCommentInstr::execute(istream &s)
{ // Comment on a particular address within current function
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("Decompile action not loaded");
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
int4 size;
Address addr = parse_machaddr(s,size,*dcp->conf->types);
s >> ws;
string comment;
char tok;
s.get(tok);
while(!s.eof()) {
comment += tok;
s.get(tok);
}
uint4 type = dcp->conf->print->getInstructionComment();
dcp->conf->commentdb->addComment(type,
dcp->fd->getAddress(),addr,comment);
}
/// For each duplicate discovered, a message is written to the provided stream.
/// \param fd is the given function to search
/// \param s is the stream to write messages to
void IfcDuplicateHash::check(Funcdata *fd,ostream &s)
{
DynamicHash dhash;
VarnodeLocSet::const_iterator iter,enditer;
pair<set<uint8>::iterator,bool> res;
iter = fd->beginLoc();
enditer = fd->endLoc();
while(iter != enditer) {
Varnode *vn = *iter;
++iter;
if (vn->isAnnotation()) continue;
if (vn->isConstant()) {
PcodeOp *op = vn->loneDescend();
int4 slot = op->getSlot(vn);
if (slot == 0) {
if (op->code() == CPUI_LOAD) continue;
if (op->code() == CPUI_STORE) continue;
if (op->code() == CPUI_RETURN) continue;
}
}
else if (vn->getSpace()->getType() != IPTR_INTERNAL)
continue;
else if (vn->isImplied())
continue;
dhash.uniqueHash(vn,fd);
if (dhash.getHash() == 0) {
// We have a duplicate
const PcodeOp *op;
if (vn->beginDescend() != vn->endDescend())
op = *vn->beginDescend();
else
op = vn->getDef();
s << "Could not get unique hash for : ";
vn->printRaw(s);
s << " : ";
op->printRaw(s);
s << endl;
return;
}
uint4 total = DynamicHash::getTotalFromHash(dhash.getHash());
if (total != 1) {
const PcodeOp *op;
if (vn->beginDescend() != vn->endDescend())
op = *vn->beginDescend();
else
op = vn->getDef();
s << "Duplicate : ";
s << dec << DynamicHash::getPositionFromHash(dhash.getHash()) << " out of " << total << " : ";
vn->printRaw(s);
s << " : ";
op->printRaw(s);
s << endl;
}
}
}
/// \class IfcDuplicateHash
/// \brief Check for duplicate hashes in functions: `duplicate hash`
///
/// All functions in the architecture/program are decompiled, and for each
/// a check is made for Varnode pairs with identical hash values.
void IfcDuplicateHash::execute(istream &s)
{
iterateFunctionsAddrOrder();
}
void IfcDuplicateHash::iterationCallback(Funcdata *fd)
{
clock_t start_time,end_time;
float duration;
if (fd->hasNoCode()) {
*status->optr << "No code for " << fd->getName() << endl;
return;
}
try {
dcp->conf->clearAnalysis(fd); // Clear any old analysis
dcp->conf->allacts.getCurrent()->reset(*fd);
start_time = clock();
dcp->conf->allacts.getCurrent()->perform( *fd );
end_time = clock();
*status->optr << "Decompiled " << fd->getName();
// *status->optr << ": " << hex << fd->getAddress().getOffset();
*status->optr << '(' << dec << fd->getSize() << ')';
duration = ((float)(end_time-start_time))/CLOCKS_PER_SEC;
duration *= 1000.0;
*status->optr << " time=" << fixed << setprecision(0) << duration << " ms" << endl;
check(fd,*status->optr);
}
catch(LowlevelError &err) {
*status->optr << "Skipping " << fd->getName() << ": " << err.explain << endl;
}
dcp->conf->clearAnalysis(fd);
}
/// \class IfcCallGraphBuild
/// \brief Build the call-graph for the architecture/program: `callgraph build`
///
/// Build, or rebuild, the call-graph with nodes for all existing functions.
/// Functions are to decompiled to recover destinations of indirect calls.
/// Going forward, the graph is held in memory and is accessible by other commands.
void IfcCallGraphBuild::execute(istream &s)
{
dcp->allocateCallGraph();
dcp->cgraph->buildAllNodes(); // Build a node in the graph for existing symbols
quick = false;
iterateFunctionsAddrOrder();
*status->optr << "Successfully built callgraph" << endl;
}
void IfcCallGraphBuild::iterationCallback(Funcdata *fd)
{
clock_t start_time,end_time;
float duration;
if (fd->hasNoCode()) {
*status->optr << "No code for " << fd->getName() << endl;
return;
}
if (quick) {
dcp->fd = fd;
dcp->followFlow(*status->optr,0);
}
else {
try {
dcp->conf->clearAnalysis(fd); // Clear any old analysis
dcp->conf->allacts.getCurrent()->reset(*fd);
start_time = clock();
dcp->conf->allacts.getCurrent()->perform( *fd );
end_time = clock();
*status->optr << "Decompiled " << fd->getName();
// *status->optr << ": " << hex << fd->getAddress().getOffset();
*status->optr << '(' << dec << fd->getSize() << ')';
duration = ((float)(end_time-start_time))/CLOCKS_PER_SEC;
duration *= 1000.0;
*status->optr << " time=" << fixed << setprecision(0) << duration << " ms" << endl;
}
catch(LowlevelError &err) {
*status->optr << "Skipping " << fd->getName() << ": " << err.explain << endl;
}
}
dcp->cgraph->buildEdges(fd);
dcp->conf->clearAnalysis(fd);
}
/// \class IfcCallGraphBuildQuick
/// \brief Build the call-graph using quick analysis: `callgraph build quick`
///
/// Build the call-graph for the architecture/program. For each function, disassembly
/// is performed to discover call edges, rather then full decompilation. Some forms
/// of direct call may not be discovered.
void IfcCallGraphBuildQuick::execute(istream &s)
{
dcp->allocateCallGraph();
dcp->cgraph->buildAllNodes(); // Build a node in the graph for existing symbols
quick = true;
iterateFunctionsAddrOrder();
*status->optr << "Successfully built callgraph" << endl;
}
/// \class IfcCallGraphDump
/// \brief Write the current call-graph to a file: `callgraph dump <filename>`
///
/// The existing call-graph object is written to the provided file as an
/// XML document.
void IfcCallGraphDump::execute(istream &s)
{
if (dcp->cgraph == (CallGraph *)0)
throw IfaceExecutionError("No callgraph has been built");
string name;
s >> ws >> name;
if (name.size() == 0)
throw IfaceParseError("Need file name to write callgraph to");
ofstream os;
os.open(name.c_str());
if (!os)
throw IfaceExecutionError("Unable to open file "+name);
XmlEncode encoder(os);
dcp->cgraph->encode(encoder);
os.close();
*status->optr << "Successfully saved callgraph to " << name << endl;
}
/// \class IfcCallGraphLoad
/// \brief Load the call-graph from a file: `callgraph load <filename>`
///
/// A call-graph is loaded from the provided XML document. Nodes in the
/// call-graph are linked to existing functions by symbol name. This command
/// reports call-graph nodes that could not be linked.
void IfcCallGraphLoad::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("Decompile action not loaded");
if (dcp->cgraph != (CallGraph *)0)
throw IfaceExecutionError("Callgraph already loaded");
string name;
s >> ws >> name;
if (name.size() == 0)
throw IfaceExecutionError("Need name of file to read callgraph from");
ifstream is(name.c_str());
if (!is)
throw IfaceExecutionError("Unable to open callgraph file "+name);
DocumentStorage store;
Document *doc = store.parseDocument(is);
dcp->allocateCallGraph();
XmlDecode decoder(dcp->conf,doc->getRoot());
dcp->cgraph->decoder(decoder);
*status->optr << "Successfully read in callgraph" << endl;
Scope *gscope = dcp->conf->symboltab->getGlobalScope();
map<Address,CallGraphNode>::iterator iter,enditer;
iter = dcp->cgraph->begin();
enditer = dcp->cgraph->end();
for(;iter!=enditer;++iter) {
CallGraphNode *node = &(*iter).second;
Funcdata *fd;
fd = gscope->queryFunction(node->getName());
if (fd == (Funcdata *)0)
throw IfaceExecutionError("Function:" + node->getName() +" in callgraph has not been loaded");
node->setFuncdata(fd);
}
*status->optr << "Successfully associated functions with callgraph nodes" << endl;
}
/// \class IfcCallGraphList
/// \brief List all functions in \e leaf order: `callgraph list`
///
/// The existing call-graph is walked, displaying function names to the console.
/// Child functions are displayed before their parents.
void IfcCallGraphList::execute(istream &s)
{
if (dcp->cgraph == (CallGraph *)0)
throw IfaceExecutionError("Callgraph not generated");
iterateFunctionsLeafOrder();
}
void IfcCallGraphList::iterationCallback(Funcdata *fd)
{
*status->optr << fd->getName() << endl;
}
/// \brief Scan a single-line p-code snippet declaration from the given stream
///
/// A declarator is scanned first, providing a name to associate with the snippet, as well
/// as potential names of the formal \e output Varnode and \e input Varnodes.
/// The body of the snippet is then surrounded by '{' and '}' The snippet name,
/// input/output names, and the body are passed back to the caller.
/// \param s is the given stream to scan
/// \param name passes back the name of the snippet
/// \param outname passes back the formal output parameter name, or is empty
/// \param inname passes back an array of the formal input parameter names
/// \param pcodestring passes back the snippet body
void IfcCallFixup::readPcodeSnippet(istream &s,string &name,string &outname,vector<string> &inname,
string &pcodestring)
{
char bracket;
s >> outname;
parse_toseparator(s,name);
s >> bracket;
if (outname == "void")
outname = "";
if (bracket != '(')
throw IfaceParseError("Missing '('");
while(bracket != ')') {
string param;
parse_toseparator(s,param);
s >> bracket;
if (param.size() != 0)
inname.push_back(param);
}
s >> ws >> bracket;
if (bracket != '{')
throw IfaceParseError("Missing '{'");
getline(s,pcodestring,'}');
}
/// \class IfcCallFixup
/// \brief Add a new call fix-up to the program: `fixup call ...`
///
/// Create a new call fixup-up for the architecture/program, suitable for
/// replacing called functions. The fix-up is specified as a function-style declarator,
/// which also provides the formal name of the fix-up.
/// A "void" return-type and empty parameter list must be given.
/// \code
/// fixup call void myfixup1() { EAX = 0; RBX = RCX + RDX + 1; }
/// \endcode
void IfcCallFixup::execute(istream &s)
{
string name,outname,pcodestring;
vector<string> inname;
readPcodeSnippet(s,name,outname,inname,pcodestring);
int4 id = -1;
try {
id = dcp->conf->pcodeinjectlib->manualCallFixup(name,pcodestring);
} catch(LowlevelError &err) {
*status->optr << "Error compiling pcode: " << err.explain << endl;
return;
}
InjectPayload *payload = dcp->conf->pcodeinjectlib->getPayload(id);
payload->printTemplate(*status->optr);
}
/// \class IfcCallOtherFixup
/// \brief Add a new callother fix-up to the program: `fixup callother ...`
///
/// The new fix-up is suitable for replacing specific user-defined (CALLOTHER)
/// p-code operations. The declarator provides the name of the fix-up and can also
/// provide formal input and output parameters.
/// \code
/// fixup callother outvar myfixup2(invar1,invar2) { outvar = invar1 + invar2; }
/// \endcode
void IfcCallOtherFixup::execute(istream &s)
{
string useropname,outname,pcodestring;
vector<string> inname;
IfcCallFixup::readPcodeSnippet(s,useropname,outname,inname,pcodestring);
dcp->conf->userops.manualCallOtherFixup(useropname,outname,inname,pcodestring,dcp->conf);
*status->optr << "Successfully registered callotherfixup" << endl;
}
/// \class IfcFixupApply
/// \brief Apply a call-fixup to a particular function: `fixup apply <fixup> <function>`
///
/// The call-fixup and function are named from the command-line. If they both exist,
/// the fixup is set on the function's prototype.
void IfcFixupApply::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
string fixupName,funcName;
s >> ws;
if (s.eof())
throw IfaceParseError("Missing fixup name");
s >> fixupName >> ws;
if (s.eof())
throw IfaceParseError("Missing function name");
s >> funcName;
int4 injectid = dcp->conf->pcodeinjectlib->getPayloadId(InjectPayload::CALLFIXUP_TYPE, fixupName);
if (injectid < 0)
throw IfaceExecutionError("Unknown fixup: " + fixupName);
string basename;
Scope *funcscope = dcp->conf->symboltab->resolveScopeFromSymbolName(funcName,"::",basename,(Scope *)0);
if (funcscope == (Scope *)0)
throw IfaceExecutionError("Bad namespace: "+funcName);
Funcdata *fd = funcscope->queryFunction( basename ); // Is function already in database
if (fd == (Funcdata *)0)
throw IfaceExecutionError("Unknown function name: "+funcName);
fd->getFuncProto().setInjectId(injectid);
*status->optr << "Successfully applied callfixup" << endl;
}
/// \class IfcVolatile
/// \brief Mark a memory range as volatile: `volatile <address+size>`
///
/// The memory range provided on the command-line is marked as \e volatile, warning
/// the decompiler analysis that values in the range my change unexpectedly.
void IfcVolatile::execute(istream &s)
{
int4 size = 0;
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
Address addr = parse_machaddr(s,size,*dcp->conf->types); // Read required address
if (size == 0)
throw IfaceExecutionError("Must specify a size");
Range range( addr.getSpace(), addr.getOffset(), addr.getOffset() + (size-1));
dcp->conf->symboltab->setPropertyRange(Varnode::volatil,range);
*status->optr << "Successfully marked range as volatile" << endl;
}
/// \class IfcReadonly
/// \brief Mark a memory range as read-only: `readonly <address+size>`
///
/// The memory range provided on the command-line is marked as \e read-only, which
/// allows the decompiler to propagate values pulled from the LoadImage for the range
/// as constants.
void IfcReadonly::execute(istream &s)
{
int4 size = 0;
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
Address addr = parse_machaddr(s,size,*dcp->conf->types); // Read required address
if (size == 0)
throw IfaceExecutionError("Must specify a size");
Range range( addr.getSpace(), addr.getOffset(), addr.getOffset() + (size-1));
dcp->conf->symboltab->setPropertyRange(Varnode::readonly,range);
*status->optr << "Successfully marked range as readonly" << endl;
}
/// \class IfcPointerSetting
/// \brief Create a pointer with additional settings: `pointer setting <name> <basetype> offset <val>`
///
/// Alternately: `pointer setting <name> <basetype> space <spacename>`
/// The new data-type is named and must be pointer.
/// An \e offset setting creates a relative pointer and attaches the provided offset value.
/// A \e space setting create a pointer with the provided address space as an attribute.
void IfcPointerSetting::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
string typeName;
string baseType;
string setting;
s >> ws;
if (s.eof())
throw IfaceParseError("Missing name");
s >> typeName >> ws;
if (s.eof())
throw IfaceParseError("Missing base-type");
s >> baseType >> ws;
if (s.eof())
throw IfaceParseError("Missing setting");
s >> setting >> ws;
if (setting == "offset") {
int4 off = -1;
s.unsetf(ios::dec | ios::hex | ios::oct); // Let user specify base
s >> off;
if (off <= 0)
throw IfaceParseError("Missing offset");
Datatype *bt = dcp->conf->types->findByName(baseType);
if (bt == (Datatype *)0 || bt->getMetatype() != TYPE_STRUCT)
throw IfaceParseError("Base-type must be a structure");
Datatype *ptrto = TypePointerRel::getPtrToFromParent(bt, off, *dcp->conf->types);
AddrSpace *spc = dcp->conf->getDefaultDataSpace();
dcp->conf->types->getTypePointerRel(spc->getAddrSize(), bt, ptrto, spc->getWordSize(), off,typeName);
}
else if (setting == "space") {
string spaceName;
s >> spaceName;
if (spaceName.length() == 0)
throw IfaceParseError("Missing name of address space");
Datatype *ptrTo = dcp->conf->types->findByName(baseType);
if (ptrTo == (Datatype *)0)
throw IfaceParseError("Unknown base data-type: "+baseType);
AddrSpace *spc = dcp->conf->getSpaceByName(spaceName);
if (spc == (AddrSpace *)0)
throw IfaceParseError("Unknown space: "+spaceName);
dcp->conf->types->getTypePointerWithSpace(ptrTo,spc,typeName);
}
else
throw IfaceParseError("Unknown pointer setting: "+setting);
*status->optr << "Successfully created pointer: " << typeName << endl;
}
/// \class IfcPreferSplit
/// \brief Mark a storage location to be split: `prefersplit <address+size> <splitsize>`
///
/// The storage location is marked for splitting in any future decompilation.
/// During decompilation, any Varnode matching the storage location on the command-line
/// will be generally split into two pieces, where the final command-line parameter
/// indicates the number of bytes in the first piece. A Varnode is split only if operations
/// involving it can also be split. See PreferSplitManager.
void IfcPreferSplit::execute(istream &s)
{
int4 size = 0;
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
Address addr = parse_machaddr(s,size,*dcp->conf->types); // Read storage location
if (size == 0)
throw IfaceExecutionError("Must specify a size");
int4 split = -1;
s >> ws;
if (s.eof())
throw IfaceParseError("Missing split offset");
s >> dec >> split;
if (split == -1)
throw IfaceParseError("Bad split offset");
dcp->conf->splitrecords.emplace_back();
PreferSplitRecord &rec( dcp->conf->splitrecords.back() );
rec.storage.space = addr.getSpace();
rec.storage.offset = addr.getOffset();
rec.storage.size = size;
rec.splitoffset = split;
*status->optr << "Successfully added split record" << endl;
}
/// \class IfcStructureBlocks
/// \brief Structure an external control-flow graph: `structure blocks <infile> <outfile>`
///
/// The control-flow graph is read in from XML file, structuring is performed, and the
/// result is written out to a separate XML file.
void IfcStructureBlocks::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
string infile,outfile;
s >> infile;
s >> outfile;
if (infile.empty())
throw IfaceParseError("Missing input file");
if (outfile.empty())
throw IfaceParseError("Missing output file");
ifstream fs;
fs.open(infile.c_str());
if (!fs)
throw IfaceExecutionError("Unable to open file: "+infile);
DocumentStorage store;
Document *doc = store.parseDocument(fs);
fs.close();
try {
BlockGraph ingraph;
XmlDecode decoder(dcp->conf,doc->getRoot());
ingraph.decode(decoder);
BlockGraph resultgraph;
vector<FlowBlock *> rootlist;
resultgraph.buildCopy(ingraph);
resultgraph.structureLoops(rootlist);
resultgraph.calcForwardDominator(rootlist);
CollapseStructure collapse(resultgraph);
collapse.collapseAll();
ofstream sout;
sout.open(outfile.c_str());
if (!sout)
throw IfaceExecutionError("Unable to open output file: "+outfile);
XmlEncode encoder(sout);
resultgraph.encode(encoder);
sout.close();
}
catch(LowlevelError &err) {
*status->optr << err.explain << endl;
}
}
#ifdef CPUI_RULECOMPILE
void IfcParseRule::execute(istream &s)
{ // Parse a rule and print it out as a C routine
string filename;
bool debug = false;
s >> filename;
if (filename.size() == 0)
throw IfaceParseError("Missing rule input file");
s >> ws;
if (!s.eof()) {
string val;
s >> val;
if ((val=="true")||(val=="debug"))
debug = true;
}
ifstream thefile( filename.c_str());
if (!thefile)
throw IfaceExecutionError("Unable to open rule file: "+filename);
RuleCompile ruler;
ruler.setErrorStream(*status->optr);
ruler.run(thefile,debug);
if (ruler.numErrors() != 0) {
*status->optr << "Parsing aborted on error" << endl;
return;
}
int4 opparam;
vector<OpCode> opcodelist;
opparam = ruler.postProcessRule(opcodelist);
UnifyCPrinter cprinter;
cprinter.initializeRuleAction(ruler.getRule(),opparam,opcodelist);
cprinter.addNames(ruler.getNameMap());
cprinter.print(*status->optr);
}
void IfcExperimentalRules::execute(istream &s)
{
string filename;
if (dcp->conf != (Architecture *)0)
throw IfaceExecutionError("Experimental rules must be registered before loading architecture");
s >> filename;
if (filename.size() == 0)
throw IfaceParseError("Missing name of file containing experimental rules");
dcp->experimental_file = filename;
*status->optr << "Successfully registered experimental file " << filename << endl;
}
#endif
/// \class IfcPrintActionstats
/// \brief Print transform statistics for the decompiler engine: `print actionstats`
///
/// Counts for each Action and Rule are displayed; showing the number of attempts,
/// both successful and not, that were made to apply each one. Counts can accumulate
/// over multiple decompilations.
void IfcPrintActionstats::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("Image not loaded");
if (dcp->conf->allacts.getCurrent() == (Action *)0)
throw IfaceExecutionError("No action set");
dcp->conf->allacts.getCurrent()->printStatistics(*status->fileoptr);
}
/// \class IfcResetActionstats
/// \brief Reset transform statistics for the decompiler engine: `reset actionstats`
///
/// Counts for each Action and Rule are reset to zero.
void IfcResetActionstats::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("Image not loaded");
if (dcp->conf->allacts.getCurrent() == (Action *)0)
throw IfaceExecutionError("No action set");
dcp->conf->allacts.getCurrent()->resetStats();
}
/// \class IfcCountPcode
/// \brief Count p-code in the \e current function: `count pcode`
///
/// The count is based on the number of existing p-code operations in
/// the current function, which may vary depending on the state of it transformation.
void IfcCountPcode::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("Image not loaded");
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
uint4 count = 0;
list<PcodeOp *>::const_iterator iter,enditer;
iter = dcp->fd->beginOpAlive();
enditer = dcp->fd->endOpAlive();
while(iter != enditer) {
count += 1;
++iter;
}
*status->optr << "Count - pcode = " << dec << count << endl;
}
/// \class IfcAnalyzeRange
/// \brief Run value-set analysis on the \e current function: `analyze range full|partial <varnode>`
///
/// The analysis targets a single varnode as specified on the command-line and is based on
/// the existing data-flow graph for the current function.
/// The possible values that can reach the varnode at its point of definition, and
/// at any point it is involved in a LOAD or STORE, are displayed.
/// The keywords \b full and \b partial choose whether the value-set analysis uses
/// full or partial widening.
void IfcAnalyzeRange::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("Image not loaded");
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
bool useFullWidener;
string token;
s >> ws >> token;
if (token == "full")
useFullWidener = true;
else if (token == "partial") {
useFullWidener = false;
}
else
throw IfaceParseError("Must specify \"full\" or \"partial\" widening");
Varnode *vn = dcp->readVarnode(s);
vector<Varnode *> sinks;
vector<PcodeOp *> reads;
sinks.push_back(vn);
for(list<PcodeOp *>::const_iterator iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
PcodeOp *op = *iter;
if (op->code() == CPUI_LOAD || op->code() == CPUI_STORE)
reads.push_back(op);
}
Varnode *stackReg = dcp->fd->findSpacebaseInput(dcp->conf->getStackSpace());
ValueSetSolver vsSolver;
vsSolver.establishValueSets(sinks, reads, stackReg, false);
if (useFullWidener) {
WidenerFull widener;
vsSolver.solve(10000,widener);
}
else {
WidenerNone widener;
vsSolver.solve(10000,widener);
}
list<ValueSet>::const_iterator iter;
for(iter=vsSolver.beginValueSets();iter!=vsSolver.endValueSets();++iter) {
(*iter).printRaw(*status->optr);
*status->optr << endl;
}
map<SeqNum,ValueSetRead>::const_iterator riter;
for(riter=vsSolver.beginValueSetReads();riter!=vsSolver.endValueSetReads();++riter) {
(*riter).second.printRaw(*status->optr);
*status->optr << endl;
}
}
/// \class IfcLoadTestFile
/// \brief Load a datatest environment file: `load test <filename>`
///
/// The program and associated script from a decompiler test file is loaded
void IfcLoadTestFile::execute(istream &s)
{
string filename;
if (dcp->conf != (Architecture *)0)
throw IfaceExecutionError("Load image already present");
s >> filename;
dcp->testCollection = new FunctionTestCollection(status);
dcp->testCollection->loadTest(filename);
#ifdef OPACTION_DEBUG
dcp->conf->setDebugStream(status->fileoptr);
#endif
*status->optr << filename << " test successfully loaded: " << dcp->conf->getDescription() << endl;
}
/// \class IfcListTestCommands
/// \brief List all the script commands in the current test: `list test commands`
void IfcListTestCommands::execute(istream &s)
{
if (dcp->testCollection == (FunctionTestCollection *)0)
throw IfaceExecutionError("No test file is loaded");
for(int4 i=0;i<dcp->testCollection->numCommands();++i) {
*status->optr << ' ' << dec << i+1 << ": " << dcp->testCollection->getCommand(i) << endl;
}
}
/// \class IfcExecuteTestCommand
/// \brief Execute a specified range of the test script: `execute test command <#>-<#>
void IfcExecuteTestCommand::execute(istream &s)
{
if (dcp->testCollection == (FunctionTestCollection *)0)
throw IfaceExecutionError("No test file is loaded");
int4 first = -1;
int4 last = -1;
char hyphen;
s >> ws >> dec >> first;
first -= 1;
if (first < 0 || first > dcp->testCollection->numCommands())
throw IfaceExecutionError("Command index out of bounds");
s >> ws;
if (!s.eof()) {
s >> ws >> hyphen;
if (hyphen != '-')
throw IfaceExecutionError("Missing hyphenated command range");
s >> ws >> last;
last -= 1;
if (last < 0 || last < first || last > dcp->testCollection->numCommands())
throw IfaceExecutionError("Command index out of bounds");
}
else {
last = first;
}
ostringstream s1;
for(int4 i=first;i<=last;++i) {
s1 << dcp->testCollection->getCommand(i) << endl;
}
istringstream *s2 = new istringstream(s1.str());
status->pushScript(s2, "test> ");
}
#ifdef OPACTION_DEBUG
void IfcDebugAction::execute(istream &s)
{
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
string actionname;
s >> ws >> actionname;
if (actionname.empty())
throw IfaceParseError("Missing name of action to debug");
if (!dcp->conf->allacts.getCurrent()->turnOnDebug(actionname))
throw IfaceParseError("Unable to find action "+actionname);
}
void IfcTraceBreak::execute(istream &s)
{ // Set a opactdbg trace break point
int4 count;
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
s >> ws;
s.unsetf(ios::dec | ios::hex | ios::oct); // Let user specify base
count = -1;
s >> count;
if (count == -1)
throw IfaceParseError("Missing trace count");
dcp->fd->debugSetBreak(count);
}
void IfcTraceAddress::execute(istream &s)
{ // Set a opactdbg trace point
uintm uqlow,uqhigh;
int4 discard;
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
Address pclow,pchigh;
s >> ws;
if (!s.eof()) {
pclow = parse_machaddr(s,discard,*dcp->conf->types);
s >> ws;
}
pchigh = pclow;
if (!s.eof()) {
pchigh = parse_machaddr(s,discard,*dcp->conf->types);
s >> ws;
}
uqhigh = uqlow = ~((uintm)0);
if (!s.eof()) {
s.unsetf(ios::dec | ios::hex | ios::oct); // Let user specify base
s >> uqlow >> uqhigh >> ws;
}
dcp->fd->debugSetRange(pclow,pchigh,uqlow,uqhigh);
*status->optr << "OK (" << dec << dcp->fd->debugSize() << " ranges)\n";
}
void IfcTraceEnable::execute(istream &s)
{ // Turn on trace
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
dcp->fd->debugEnable();
*status->optr << "OK\n";
}
void IfcTraceDisable::execute(istream &s)
{ // Turn off trace
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
dcp->fd->debugDisable();
*status->optr << "OK\n";
}
void IfcTraceClear::execute(istream &s)
{ // Clear existing debug trace ranges
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
*status->optr << dec << dcp->fd->debugSize() << " ranges cleared\n";
dcp->fd->debugDisable();
dcp->fd->debugClear();
}
void IfcTraceList::execute(istream &s)
{ // List debug trace ranges
int4 size,i;
if (dcp->fd == (Funcdata *)0)
throw IfaceExecutionError("No function selected");
size = dcp->fd->debugSize();
if (dcp->fd->opactdbg_on)
*status->optr << "Trace enabled (";
else
*status->optr << "Trace disabled (";
*status->optr << dec << size << " total ranges)\n";
for(i=0;i<size;++i)
dcp->fd->debugPrintRange(i);
}
static vector<Funcdata *> jumpstack;
static IfaceDecompData *dcp_callback;
static IfaceStatus *status_callback;
static void jump_callback(Funcdata &orig,Funcdata &fd)
{ // Replaces reset/perform in Funcdata::stageJumpTable
IfaceDecompData *newdcp = dcp_callback;
IfaceStatus *newstatus = status_callback;
jumpstack.push_back(newdcp->fd);
// We create a new "sub" interface using the same input output
ostringstream s1;
s1 << fd.getName() << "> ";
// We keep the commands already registered.
// We should probably "de"-register some of the commands
// that can't really be used in this subcontext.
newdcp->fd = &fd;
Action *rootaction = newdcp->conf->allacts.getCurrent();
rootaction->reset(*newdcp->fd);
// Set a break point right at the start
rootaction->setBreakPoint(Action::tmpbreak_start,rootaction->getName());
// Start up the action
int4 res = rootaction->perform( *newdcp->fd );
if (res >= 0)
throw LowlevelError("Did not catch jumptable breakpoint");
*newstatus->optr << "Breaking for jumptable partial function" << endl;
*newstatus->optr << newdcp->fd->getName() << endl;
*newstatus->optr << "Type \"cont\" to continue debugging." << endl;
*newstatus->optr << "After completion type \"quit\" to continue in parent." << endl;
mainloop(newstatus);
newstatus->done = false; // "quit" only terminates one level
*newstatus->optr << "Finished jumptable partial function" << endl;
newdcp->fd = jumpstack.back();
jumpstack.pop_back();
}
void IfcBreakjump::execute(istream &s)
{
dcp->jumptabledebug = true;
dcp_callback = dcp;
status_callback = status;
*status->optr << "Jumptable debugging enabled" << endl;
if (dcp->fd != (Funcdata *)0)
dcp->fd->enableJTCallback(jump_callback);
}
#endif
/// Execute one command and handle any exceptions.
/// Error messages are printed to the console. For low-level errors,
/// the current function is reset to null
/// \param status is the console interface
/// \param dcp is the shared program data
void execute(IfaceStatus *status,IfaceDecompData *dcp)
{
try {
status->runCommand(); // Try to run one command-line
return;
}
catch(IfaceParseError &err) {
*status->optr << "Command parsing error: " << err.explain << endl;
}
catch(IfaceExecutionError &err) {
*status->optr << "Execution error: " << err.explain << endl;
}
catch(IfaceError &err) {
*status->optr << "ERROR: " << err.explain << endl;
}
catch(ParseError &err) {
*status->optr << "Parse ERROR: " << err.explain << endl;
}
catch(RecovError &err) {
*status->optr << "Function ERROR: " << err.explain << endl;
}
catch(LowlevelError &err) {
*status->optr << "Low-level ERROR: " << err.explain << endl;
dcp->abortFunction(*status->optr);
}
catch(DecoderError &err) {
*status->optr << "Decoding ERROR: " << err.explain << endl;
dcp->abortFunction(*status->optr);
}
status->evaluateError();
}
/// Execution loops until either the \e done field in the console is set
/// or if all streams have ended. This handles popping script states pushed
/// on by the IfcSource command.
/// \param status is the console interface
void mainloop(IfaceStatus *status)
{
IfaceDecompData *dcp = (IfaceDecompData *)status->getData("decompile");
for(;;) {
while(!status->isStreamFinished()) {
status->writePrompt();
status->optr->flush();
execute(status,dcp);
}
if (status->done) break;
if (status->getNumInputStreamSize()==0) break;
status->popScript();
}
}
/// \class IfcSource
/// \brief Execute a command script : `source <filename>`
///
/// A file is opened as a new streaming source of command-lines.
/// The stream is pushed onto the stack for the console.
void IfcSource::execute(istream &s)
{
string filename;
s >> ws;
if (s.eof())
throw IfaceParseError("filename parameter required for source");
s >> filename;
status->pushScript(filename,filename+"> ");
}
} // End namespace ghidra