mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-10-13 05:33:02 +00:00
Merge remote-tracking branch 'origin/GP-2015_DynamicHashPcodeOp'
(Closes #4178, Closes #4207, Closes #4213, Closes #4214, Closes #4227)
This commit is contained in:
commit
267e01a1c4
|
@ -19,7 +19,7 @@
|
|||
|
||||
// Table for how to hash opcodes, lumps certain operators (i.e. ADD SUB PTRADD PTRSUB) into one hash
|
||||
// zero indicates the operator should be skipped
|
||||
uint4 DynamicHash::transtable[] = {
|
||||
const uint4 DynamicHash::transtable[] = {
|
||||
0,
|
||||
CPUI_COPY, CPUI_LOAD, CPUI_STORE, CPUI_BRANCH, CPUI_CBRANCH, CPUI_BRANCHIND,
|
||||
|
||||
|
@ -199,11 +199,30 @@ void DynamicHash::clear(void)
|
|||
void DynamicHash::calcHash(const PcodeOp *op,int4 slot,uint4 method)
|
||||
|
||||
{
|
||||
const Varnode *root;
|
||||
|
||||
// slot may be from a hash unassociated with op
|
||||
// we need to check that slot indicates a valid Varnode
|
||||
if (slot < 0) {
|
||||
root = op->getOut();
|
||||
if (root == (const Varnode *)0) {
|
||||
hash = 0;
|
||||
addrresult = Address();
|
||||
return; // slot does not fit op
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (slot >= op->numInput()) {
|
||||
hash = 0;
|
||||
addrresult = Address();
|
||||
return; // slot does not fit op
|
||||
}
|
||||
root = op->getIn(slot);
|
||||
}
|
||||
vnproc = 0;
|
||||
opproc = 0;
|
||||
opedgeproc = 0;
|
||||
|
||||
const Varnode *root = (slot < 0) ? op->getOut() : op->getIn(slot);
|
||||
opedge.push_back(ToOpEdge(op,slot));
|
||||
switch(method) {
|
||||
case 4:
|
||||
|
@ -354,6 +373,32 @@ void DynamicHash::pieceTogetherHash(const Varnode *root,uint4 method)
|
|||
addrresult = op->getSeqNum().getAddr();
|
||||
}
|
||||
|
||||
/// For a DynamicHash on a PcodeOp, the op must not be a CAST or other skipped opcode.
|
||||
/// Test if the given op is a skip op, and if so follow data-flow indicated by the
|
||||
/// slot to another PcodeOp until we find one that isn't a skip op. Pass back the new PcodeOp
|
||||
/// and slot. Pass back null if the data-flow path ends.
|
||||
/// \param op is the given PcodeOp to modify
|
||||
/// \param slot is the slot to modify
|
||||
void DynamicHash::moveOffSkip(const PcodeOp *&op,int4 &slot)
|
||||
|
||||
{
|
||||
while(transtable[op->code()] == 0) {
|
||||
if (slot >= 0) {
|
||||
const Varnode *vn = op->getOut();
|
||||
op = vn->loneDescend();
|
||||
if (op == (PcodeOp*)0) {
|
||||
return; // Indicate the end of the data-flow path
|
||||
}
|
||||
slot = op->getSlot(vn);
|
||||
}
|
||||
else {
|
||||
const Varnode *vn = op->getIn(0);
|
||||
if (!vn->isWritten()) return; // Indicate the end of the data-flow path
|
||||
op = vn->getDef();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect the set of Varnodes at the same address as the given Varnode.
|
||||
/// Starting with method 0, increment the method and calculate hashes
|
||||
/// of the Varnodes until the given Varnode has a unique hash within the set.
|
||||
|
@ -424,6 +469,12 @@ void DynamicHash::uniqueHash(const Varnode *root,Funcdata *fd)
|
|||
addrresult = tmpaddr;
|
||||
}
|
||||
|
||||
/// Different hash methods are cycled through until a hash is found that distinguishes the given
|
||||
/// op from other PcodeOps at the same address. The final hash encoding and address of the PcodeOp are
|
||||
/// built for retrieval using getHash() and getAddress().
|
||||
/// \param op is the given PcodeOp
|
||||
/// \param slot is the particular slot to encode in the hash
|
||||
/// \param fd is the function containing the given PcodeOp
|
||||
void DynamicHash::uniqueHash(const PcodeOp *op,int4 slot,Funcdata *fd)
|
||||
|
||||
{
|
||||
|
@ -435,6 +486,12 @@ void DynamicHash::uniqueHash(const PcodeOp *op,int4 slot,Funcdata *fd)
|
|||
Address tmpaddr;
|
||||
uint4 maxduplicates = 8;
|
||||
|
||||
moveOffSkip(op, slot);
|
||||
if (op == (const PcodeOp *)0) {
|
||||
hash = (uint8)0;
|
||||
addrresult = Address(); // Hash cannot be calculated
|
||||
return;
|
||||
}
|
||||
gatherOpsAtAddress(oplist,fd,op->getAddr());
|
||||
for(method=4;method<7;++method) {
|
||||
clear();
|
||||
|
|
|
@ -76,6 +76,7 @@ class DynamicHash {
|
|||
void gatherUnmarkedVn(void); ///< Move staged Varnodes into the sub-graph and mark them
|
||||
void gatherUnmarkedOp(void); ///< Mark any new PcodeOps in the sub-graph
|
||||
void pieceTogetherHash(const Varnode *root,uint4 method); ///< Clean-up and piece together formal hash value
|
||||
static void moveOffSkip(const PcodeOp *&op,int4 &slot); ///< Convert given PcodeOp to a non-skip op by following data-flow
|
||||
public:
|
||||
void clear(void); ///< Called for each additional hash (after the first)
|
||||
void calcHash(const Varnode *root,uint4 method); ///< Calculate the hash for given Varnode and method
|
||||
|
@ -97,7 +98,7 @@ public:
|
|||
static bool getIsNotAttached(uint8 h); ///< Retrieve the attachment boolean from a hash
|
||||
static void clearTotalPosition(uint8 &h); ///< Clear the collision total and position fields within a hash
|
||||
static uint4 getComparable(uint8 h) { return (uint4)h; } ///< Get only the formal hash for comparing
|
||||
static uint4 transtable[]; ///< Translation of op-codes to hash values
|
||||
static const uint4 transtable[]; ///< Translation of op-codes to hash values
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -122,17 +122,21 @@ public class ForceUnionAction extends AbstractDecompilerAction {
|
|||
int opcode = accessOp.getOpcode();
|
||||
if (opcode == PcodeOp.PTRSUB) {
|
||||
parentDt = typeIsUnionRelated(accessOp.getInput(0));
|
||||
accessVn = accessOp.getInput(0);
|
||||
accessSlot = 0;
|
||||
if (accessOp.getInput(1).getOffset() == 0) { // Artificial op
|
||||
accessVn = accessOp.getOutput();
|
||||
accessOp = accessVn.getLoneDescend();
|
||||
if (accessOp == null) {
|
||||
return;
|
||||
do {
|
||||
Varnode tmpVn = accessOp.getOutput();
|
||||
PcodeOp tmpOp = tmpVn.getLoneDescend();
|
||||
if (tmpOp == null) {
|
||||
break;
|
||||
}
|
||||
accessOp = tmpOp;
|
||||
accessVn = tmpVn;
|
||||
accessSlot = accessOp.getSlot(accessVn);
|
||||
}
|
||||
accessSlot = accessOp.getSlot(accessVn);
|
||||
}
|
||||
else {
|
||||
accessVn = accessOp.getInput(0);
|
||||
accessSlot = 0;
|
||||
while (accessOp.getOpcode() == PcodeOp.PTRSUB &&
|
||||
accessOp.getInput(1).getOffset() == 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -179,6 +183,9 @@ public class ForceUnionAction extends AbstractDecompilerAction {
|
|||
}
|
||||
for (DataTypeComponent component : components) {
|
||||
String nm = component.getFieldName();
|
||||
if (nm == null || nm.length() == 0) {
|
||||
nm = component.getDefaultFieldName();
|
||||
}
|
||||
allFields.add(nm);
|
||||
if (size == 0 || component.getDataType().getLength() == size) {
|
||||
res.add(nm);
|
||||
|
@ -189,21 +196,6 @@ public class ForceUnionAction extends AbstractDecompilerAction {
|
|||
return resArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of a given string, within an array of strings
|
||||
* @param list is the array of strings
|
||||
* @param value is the given string to find
|
||||
* @return the index of the given string within the array, or -1 if it isn't present
|
||||
*/
|
||||
private static int findStringIndex(ArrayList<String> list, String value) {
|
||||
for (int i = 0; i < list.size(); ++i) {
|
||||
if (list.get(i).equals(value)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Let the user choose the particular field to force on the selected Varnode. The names
|
||||
* of the fields in the associated union are presented, possibly along with the special
|
||||
|
@ -224,7 +216,7 @@ public class ForceUnionAction extends AbstractDecompilerAction {
|
|||
OkDialog.show("No Field Choices", "Only one field fits the selected variable");
|
||||
return false;
|
||||
}
|
||||
int currentChoice = findStringIndex(allFields, defaultFieldName);
|
||||
int currentChoice = allFields.indexOf(defaultFieldName);
|
||||
if (currentChoice < 0) {
|
||||
defaultFieldName = null;
|
||||
}
|
||||
|
@ -234,7 +226,7 @@ public class ForceUnionAction extends AbstractDecompilerAction {
|
|||
if (userChoice == null) {
|
||||
return false; // User cancelled when making the choice
|
||||
}
|
||||
fieldNumber = findStringIndex(allFields, userChoice);
|
||||
fieldNumber = allFields.indexOf(userChoice);
|
||||
if (fieldNumber < 0 || fieldNumber == currentChoice) {
|
||||
return false; // User chose original value or something not in list, treat as cancel
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ public class DynamicHash {
|
|||
* in the sub-graph. The edge can either be from an input Varnode to the PcodeOp
|
||||
* that reads it, or from a PcodeOp to the Varnode it defines.
|
||||
*/
|
||||
private class ToOpEdge implements Comparable<ToOpEdge> {
|
||||
private static class ToOpEdge implements Comparable<ToOpEdge> {
|
||||
private PcodeOp op;
|
||||
private int slot; // slot containing varnode we are coming from
|
||||
|
||||
|
@ -221,6 +221,41 @@ public class DynamicHash {
|
|||
opedge.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* For a DynamicHash on a PcodeOp, the op must not be a CAST or other skipped opcode.
|
||||
* Test if the given op is a skip op, and if so follow data-flow indicated by the
|
||||
* slot to another PcodeOp until we find one that isn't a skip op. Return null, if
|
||||
* the initial op is not skipped, otherwise return a ToOpEdge indicating the
|
||||
* new (op,slot) pair.
|
||||
* @param op is the given PcodeOp
|
||||
* @param slot is the slot
|
||||
* @return null or a new (op,slot) pair
|
||||
*/
|
||||
private static ToOpEdge moveOffSkip(PcodeOp op, int slot) {
|
||||
if (transtable[op.getOpcode()] != 0) {
|
||||
return null;
|
||||
}
|
||||
do {
|
||||
if (slot >= 0) {
|
||||
Varnode vn = op.getOutput();
|
||||
op = vn.getLoneDescend();
|
||||
if (op == null) {
|
||||
return new ToOpEdge(null, 0); // Indicate the end of the data-flow path
|
||||
}
|
||||
slot = op.getSlot(vn);
|
||||
}
|
||||
else {
|
||||
Varnode vn = op.getInput(0);
|
||||
op = vn.getDef();
|
||||
if (op == null) {
|
||||
return new ToOpEdge(null, 0); // Indicate the end of the data-flow path
|
||||
}
|
||||
}
|
||||
}
|
||||
while (transtable[op.getOpcode()] == 0);
|
||||
return new ToOpEdge(op, slot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a particular PcodeOp and slot
|
||||
* @param op is the PcodeOp to preserve
|
||||
|
@ -228,11 +263,27 @@ public class DynamicHash {
|
|||
* @param method is the method to use for encoding (4, 5, or 6)
|
||||
*/
|
||||
private void calcHash(PcodeOp op, int slot, int method) {
|
||||
Varnode root;
|
||||
if (slot < 0) {
|
||||
root = op.getOutput();
|
||||
if (root == null) {
|
||||
hash = 0;
|
||||
addrresult = Address.NO_ADDRESS;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (slot >= op.getNumInputs()) {
|
||||
hash = 0;
|
||||
addrresult = Address.NO_ADDRESS;
|
||||
return;
|
||||
}
|
||||
root = op.getInput(slot);
|
||||
}
|
||||
vnproc = 0;
|
||||
opproc = 0;
|
||||
opedgeproc = 0;
|
||||
markset = new HashSet<>();
|
||||
Varnode root = (slot < 0) ? op.getOutput() : op.getInput(slot);
|
||||
opedge.add(new ToOpEdge(op, slot));
|
||||
switch (method) {
|
||||
case 4:
|
||||
|
@ -382,6 +433,16 @@ public class DynamicHash {
|
|||
Address tmpaddr = null;
|
||||
int maxduplicates = 8;
|
||||
|
||||
ToOpEdge move = moveOffSkip(op, slot);
|
||||
if (move != null) {
|
||||
op = move.getOp();
|
||||
slot = move.getSlot();
|
||||
if (op == null) {
|
||||
hash = 0;
|
||||
addrresult = Address.NO_ADDRESS;
|
||||
return;
|
||||
}
|
||||
}
|
||||
gatherOpsAtAddress(oplist, fd, op.getSeqnum().getTarget());
|
||||
for (method = 4; method < 7; ++method) {
|
||||
clear();
|
||||
|
|
|
@ -755,6 +755,8 @@ public class HighFunctionDBUtil {
|
|||
public static void writeUnionFacet(Function function, DataType dt, int fieldNum, Address addr,
|
||||
long hash, SourceType source) throws InvalidInputException, DuplicateNameException {
|
||||
int firstUseOffset = (int) addr.subtract(function.getEntryPoint());
|
||||
String symbolName = UnionFacetSymbol.buildSymbolName(fieldNum, addr);
|
||||
boolean nameCollision = false;
|
||||
Variable[] localVariables =
|
||||
function.getLocalVariables(VariableFilter.UNIQUE_VARIABLE_FILTER);
|
||||
Variable preexistingVar = null;
|
||||
|
@ -762,10 +764,14 @@ public class HighFunctionDBUtil {
|
|||
if (var.getFirstUseOffset() == firstUseOffset &&
|
||||
var.getFirstStorageVarnode().getOffset() == hash) {
|
||||
preexistingVar = var;
|
||||
break;
|
||||
}
|
||||
else if (var.getName().startsWith(symbolName)) {
|
||||
nameCollision = true;
|
||||
}
|
||||
}
|
||||
String symbolName = UnionFacetSymbol.buildSymbolName(fieldNum, addr);
|
||||
if (nameCollision) { // Uniquify the name if necessary
|
||||
symbolName = symbolName + '_' + Integer.toHexString(DynamicHash.getComparable(hash));
|
||||
}
|
||||
if (preexistingVar != null) {
|
||||
if (preexistingVar.getName().equals(symbolName)) {
|
||||
return; // No change to make
|
||||
|
|
Loading…
Reference in a new issue