ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/emulateutil.cc

397 lines
12 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 "architecture.hh"
#include "emulateutil.hh"
namespace ghidra {
/// \param g is the Architecture providing the LoadImage
EmulatePcodeOp::EmulatePcodeOp(Architecture *g)
{
glb = g;
currentOp = (PcodeOp *)0;
lastOp = (PcodeOp *)0;
}
uintb EmulatePcodeOp::getLoadImageValue(AddrSpace *spc,uintb off,int4 sz) const
{
LoadImage *loadimage = glb->loader;
uintb res;
loadimage->loadFill((uint1 *)&res,sizeof(uintb),Address(spc,off));
if ((HOST_ENDIAN==1) != spc->isBigEndian())
res = byte_swap(res,sizeof(uintb));
if (spc->isBigEndian() && (sz < sizeof(uintb)))
res >>= (sizeof(uintb)-sz)*8;
else
res &= calc_mask(sz);
return res;
}
void EmulatePcodeOp::executeUnary(void)
{
uintb in1 = getVarnodeValue(currentOp->getIn(0));
uintb out = currentBehave->evaluateUnary(currentOp->getOut()->getSize(),
currentOp->getIn(0)->getSize(),in1);
setVarnodeValue(currentOp->getOut(), out);
}
void EmulatePcodeOp::executeBinary(void)
{
uintb in1 = getVarnodeValue(currentOp->getIn(0));
uintb in2 = getVarnodeValue(currentOp->getIn(1));
uintb out = currentBehave->evaluateBinary(currentOp->getOut()->getSize(),
currentOp->getIn(0)->getSize(),in1,in2);
setVarnodeValue(currentOp->getOut(), out);
}
void EmulatePcodeOp::executeLoad(void)
{
// op will be null, use current_op
uintb off = getVarnodeValue(currentOp->getIn(1));
AddrSpace *spc = currentOp->getIn(0)->getSpaceFromConst();
off = AddrSpace::addressToByte(off,spc->getWordSize());
int4 sz = currentOp->getOut()->getSize();
uintb res = getLoadImageValue(spc,off,sz);
setVarnodeValue(currentOp->getOut(),res);
}
void EmulatePcodeOp::executeStore(void)
{
// There is currently nowhere to store anything since the memstate is null
// uintb val = getVarnodeValue(current_op->getIn(2)); // Value being stored
// uintb off = getVarnodeValue(current_op->getIn(1));
// AddrSpace *spc = current_op->getIn(0)->getSpaceFromConst();
}
bool EmulatePcodeOp::executeCbranch(void)
{
// op will be null, use current_op
uintb cond = getVarnodeValue(currentOp->getIn(1));
// We must take into account the booleanflip bit with pcode from the syntax tree
return ((cond != 0) != currentOp->isBooleanFlip());
}
void EmulatePcodeOp::executeMultiequal(void)
{
// op will be null, use current_op
int4 i;
FlowBlock *bl = currentOp->getParent();
FlowBlock *last_bl = lastOp->getParent();
for(i=0;i<bl->sizeIn();++i)
if (bl->getIn(i) == last_bl) break;
if (i == bl->sizeIn())
throw LowlevelError("Could not execute MULTIEQUAL");
uintb val = getVarnodeValue(currentOp->getIn(i));
setVarnodeValue( currentOp->getOut(), val );
}
void EmulatePcodeOp::executeIndirect(void)
{
// We could probably safely ignore this in the
// context we are using it (jumptable recovery)
// But we go ahead and assume it is equivalent to copy
uintb val = getVarnodeValue(currentOp->getIn(0));
setVarnodeValue( currentOp->getOut(), val);
}
void EmulatePcodeOp::executeSegmentOp(void)
{
SegmentOp *segdef = glb->userops.getSegmentOp(currentOp->getIn(0)->getSpaceFromConst()->getIndex());
if (segdef == (SegmentOp *)0)
throw LowlevelError("Segment operand missing definition");
uintb in1 = getVarnodeValue(currentOp->getIn(1));
uintb in2 = getVarnodeValue(currentOp->getIn(2));
vector<uintb> bindlist;
bindlist.push_back(in1);
bindlist.push_back(in2);
uintb res = segdef->execute(bindlist);
setVarnodeValue(currentOp->getOut(), res);
}
void EmulatePcodeOp::executeCpoolRef(void)
{
// Ignore references to constant pool
}
void EmulatePcodeOp::executeNew(void)
{
// Ignore new operations
}
uintb EmulateSnippet::getLoadImageValue(AddrSpace *spc,uintb off,int4 sz) const
{
LoadImage *loadimage = glb->loader;
uintb res;
loadimage->loadFill((uint1 *)&res,sizeof(uintb),Address(spc,off));
if ((HOST_ENDIAN==1) != spc->isBigEndian())
res = byte_swap(res,sizeof(uintb));
if (spc->isBigEndian() && (sz < sizeof(uintb)))
res >>= (sizeof(uintb)-sz)*8;
else
res &= calc_mask(sz);
return res;
}
void EmulateSnippet::executeUnary(void)
{
uintb in1 = getVarnodeValue(currentOp->getInput(0));
uintb out = currentBehave->evaluateUnary(currentOp->getOutput()->size,
currentOp->getInput(0)->size,in1);
setVarnodeValue(currentOp->getOutput()->offset, out);
}
void EmulateSnippet::executeBinary(void)
{
uintb in1 = getVarnodeValue(currentOp->getInput(0));
uintb in2 = getVarnodeValue(currentOp->getInput(1));
uintb out = currentBehave->evaluateBinary(currentOp->getOutput()->size,
currentOp->getInput(0)->size,in1,in2);
setVarnodeValue(currentOp->getOutput()->offset, out);
}
void EmulateSnippet::executeLoad(void)
{
// op will be null, use current_op
uintb off = getVarnodeValue(currentOp->getInput(1));
AddrSpace *spc = currentOp->getInput(0)->getSpaceFromConst();
off = AddrSpace::addressToByte(off,spc->getWordSize());
int4 sz = currentOp->getOutput()->size;
uintb res = getLoadImageValue(spc,off,sz);
setVarnodeValue(currentOp->getOutput()->offset,res);
}
void EmulateSnippet::executeStore(void)
{
throw LowlevelError("Illegal p-code operation in snippet: "+ (string)get_opname(currentOp->getOpcode()));
}
void EmulateSnippet::executeBranch(void)
{
VarnodeData *vn = currentOp->getInput(0);
if (vn->space->getType() != IPTR_CONSTANT)
throw LowlevelError("Tried to emulate absolute branch in snippet code");
int4 rel = (int4)vn->offset;
pos += rel;
if ((pos < 0)||(pos>opList.size()))
throw LowlevelError("Relative branch out of bounds in snippet code");
if (pos == opList.size()) {
emu_halted = true;
return;
}
setCurrentOp(pos);
}
bool EmulateSnippet::executeCbranch(void)
{
// op will be null, use current_op
uintb cond = getVarnodeValue(currentOp->getInput(1));
// We must take into account the booleanflip bit with pcode from the syntax tree
return (cond != 0);
}
void EmulateSnippet::executeBranchind(void)
{
throw LowlevelError("Illegal p-code operation in snippet: "+ (string)get_opname(currentOp->getOpcode()));
}
void EmulateSnippet::executeCall(void)
{
throw LowlevelError("Illegal p-code operation in snippet: "+ (string)get_opname(currentOp->getOpcode()));
}
void EmulateSnippet::executeCallind(void)
{
throw LowlevelError("Illegal p-code operation in snippet: "+ (string)get_opname(currentOp->getOpcode()));
}
void EmulateSnippet::executeCallother(void)
{
throw LowlevelError("Illegal p-code operation in snippet: "+ (string)get_opname(currentOp->getOpcode()));
}
void EmulateSnippet::executeMultiequal(void)
{
throw LowlevelError("Illegal p-code operation in snippet: "+ (string)get_opname(currentOp->getOpcode()));
}
void EmulateSnippet::executeIndirect(void)
{
throw LowlevelError("Illegal p-code operation in snippet: "+ (string)get_opname(currentOp->getOpcode()));
}
void EmulateSnippet::executeSegmentOp(void)
{
throw LowlevelError("Illegal p-code operation in snippet: "+ (string)get_opname(currentOp->getOpcode()));
}
void EmulateSnippet::executeCpoolRef(void)
{
throw LowlevelError("Illegal p-code operation in snippet: "+ (string)get_opname(currentOp->getOpcode()));
}
void EmulateSnippet::executeNew(void)
{
throw LowlevelError("Illegal p-code operation in snippet: "+ (string)get_opname(currentOp->getOpcode()));
}
void EmulateSnippet::fallthruOp(void)
{
pos += 1;
if (pos == opList.size()) {
emu_halted = true;
return;
}
setCurrentOp(pos);
}
EmulateSnippet::~EmulateSnippet(void)
{
for(int4 i=0;i<opList.size();++i)
delete opList[i];
for(int4 i=0;i<varList.size();++i)
delete varList[i];
}
/// \brief Provide the caller with an emitter for building the p-code snippet
///
/// Any p-code produced by the PcodeEmit, when triggered by the caller, becomes
/// part of the \e snippet that will get emulated by \b this. The caller should
/// free the PcodeEmit object immediately after use.
/// \param inst is the \e opcode to \e behavior map the emitter will use
/// \param uniqReserve is the starting offset within the \e unique address space for any temporary registers
/// \return the newly constructed emitter
PcodeEmit *EmulateSnippet::buildEmitter(const vector<OpBehavior *> &inst,uintb uniqReserve)
{
return new PcodeEmitCache(opList,varList,inst,uniqReserve);
}
/// \brief Check for p-code that is deemed illegal for a \e snippet
///
/// This method facilitates enforcement of the formal rules for snippet code.
/// - Branches must use p-code relative addressing.
/// - Snippets can only read/write from temporary registers
/// - Snippets cannot use BRANCHIND, CALL, CALLIND, CALLOTHER, STORE, SEGMENTOP, CPOOLREF,
/// NEW, MULTIEQUAL, or INDIRECT
///
/// \return \b true if the current snippet is legal
bool EmulateSnippet::checkForLegalCode(void) const
{
for(int4 i=0;i<opList.size();++i) {
PcodeOpRaw *op = opList[i];
VarnodeData *vn;
OpCode opc = op->getOpcode();
if (opc == CPUI_BRANCHIND || opc == CPUI_CALL || opc == CPUI_CALLIND || opc == CPUI_CALLOTHER ||
opc == CPUI_STORE || opc == CPUI_SEGMENTOP || opc == CPUI_CPOOLREF ||
opc == CPUI_NEW || opc == CPUI_MULTIEQUAL || opc == CPUI_INDIRECT)
return false;
if (opc == CPUI_BRANCH) {
vn = op->getInput(0);
if (vn->space->getType() != IPTR_CONSTANT) // Only relative branching allowed
return false;
}
vn = op->getOutput();
if (vn != (VarnodeData *)0) {
if (vn->space->getType() != IPTR_INTERNAL)
return false; // Can only write to temporaries
}
for(int4 j=0;j<op->numInput();++j) {
vn = op->getInput(j);
if (vn->space->getType() == IPTR_PROCESSOR)
return false; // Cannot read from normal registers
}
}
return true;
}
/// \brief Retrieve the value of a Varnode from the current machine state
///
/// If the Varnode is a temporary registers, the storage offset is used to look up
/// the value from the machine state cache. If the Varnode represents a RAM location,
/// the value is pulled directly out of the load-image.
/// If the value does not exist, a "Read before write" exception is thrown.
/// \param vn is the Varnode to read
/// \return the retrieved value
uintb EmulateSnippet::getVarnodeValue(VarnodeData *vn) const
{
AddrSpace *spc = vn->space;
if (spc->getType() == IPTR_CONSTANT)
return vn->offset;
if (spc->getType() == IPTR_INTERNAL) {
map<uintb,uintb>::const_iterator iter;
iter = tempValues.find(vn->offset);
if (iter != tempValues.end())
return (*iter).second; // We have seen this varnode before
throw LowlevelError("Read before write in snippet emulation");
}
return getLoadImageValue(vn->space,vn->offset,vn->size);
}
/// \brief Retrieve a temporary register value directly
///
/// This allows the user to obtain the final value of the snippet calculation, without
/// having to have the Varnode object in hand.
/// \param offset is the offset of the temporary register to retrieve
/// \return the calculated value or 0 if the register was never written
uintb EmulateSnippet::getTempValue(uintb offset) const
{
map<uintb,uintb>::const_iterator iter = tempValues.find(offset);
if (iter == tempValues.end())
return 0;
return (*iter).second;
}
} // End namespace ghidra