mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
397 lines
12 KiB
C++
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
|