Skip to content

Commit

Permalink
1. Refactor code of reading contents from jump tables and determining…
Browse files Browse the repository at this point in the history
… jump target by visiting the jump target AST.

2. Currently when backward slicing, a absloc that is written by call defined by abi will be killed, however, caller saved registers can/should survive
  • Loading branch information
mxz297 committed Aug 15, 2017
1 parent f87d17a commit 578ff24
Show file tree
Hide file tree
Showing 7 changed files with 297 additions and 107 deletions.
2 changes: 1 addition & 1 deletion dataflowAPI/src/slicing.C
Expand Up @@ -1488,7 +1488,7 @@ bool Slicer::kills(AbsRegion const&reg, Assignment::Ptr &assign) {
ABI* abi = ABI::getABI(b_->obj()->cs()->getAddressWidth());
int index = abi->getIndex(r);
if (index >= 0)
if (abi->getCallWrittenRegisters()[abi->getIndex(r)]) return true;
if (abi->getCallWrittenRegisters()[abi->getIndex(r)] && r != x86_64::r11) return true;
}
return reg.contains(assign->out());
}
Expand Down
211 changes: 127 additions & 84 deletions parseAPI/src/IndirectASTVisitor.C
Expand Up @@ -316,103 +316,146 @@ bool JumpTableFormatVisitor::PotentialIndexing(AST::Ptr ast) {
return false;
}

/*
bool PerformTableRead(StridedInterval &target, set<int64_t> & jumpTargets, CodeSource *cs) {
if (target.tableReadSize > 0 && target.interval.stride == 0) {
// This is a PC-relative read to variable, not a table read
return false;
}
Address tableBase = (Address)target.interval.low;
Address tableLastEntry = (Address)target.interval.high;
int addressWidth = cs->getAddressWidth();
if (addressWidth == 4) {
tableBase &= 0xffffffff;
tableLastEntry &= 0xffffffff;
JumpTableReadVisitor::JumpTableReadVisitor(AbsRegion i, int v, CodeSource *c, bool ze, int m) {
index = i;
indexValue = v;
cs = c;
isZeroExtend = ze;
valid = true;
memoryReadSize = m;
}

AST::Ptr JumpTableReadVisitor::visit(DataflowAPI::RoseAST *ast) {
unsigned totalChildren = ast->numChildren();
for (unsigned i = 0 ; i < totalChildren; ++i) {
ast->child(i)->accept(this);
if (!valid) return AST::Ptr();
}

// As soon as we do not know the value of one child, we will return.
// So, we will always have good values for each child at this point.
switch (ast->val().op) {
case ROSEOperation::addOp:
results.insert(make_pair(ast, results[ast->child(0).get()] + results[ast->child(1).get()]));
break;
case ROSEOperation::invertOp:
results.insert(make_pair(ast, ~results[ast->child(0).get()]));
break;
case ROSEOperation::andOp:
results.insert(make_pair(ast, results[ast->child(0).get()] & results[ast->child(1).get()]));
break;
case ROSEOperation::sMultOp:
case ROSEOperation::uMultOp:
results.insert(make_pair(ast, results[ast->child(0).get()] * results[ast->child(1).get()]));
break;
case ROSEOperation::shiftLOp:
results.insert(make_pair(ast, results[ast->child(0).get()] << results[ast->child(1).get()]));
break;
case ROSEOperation::shiftROp:
results.insert(make_pair(ast, results[ast->child(0).get()] >> results[ast->child(1).get()]));
break;
case ROSEOperation::derefOp: {
int64_t v;
bool validRead = PerformMemoryRead(results[ast->child(0).get()], v);
if (!validRead) {
valid = false;
// We encounter an invalid table entry
parsing_printf("WARNING: invalid table entry for index value %ld\n", indexValue);
return AST::Ptr();
}
results.insert(make_pair(ast, v));
}
break;
case ROSEOperation::orOp:
results.insert(make_pair(ast, results[ast->child(0).get()] | results[ast->child(1).get()]));
break;
default:
parsing_printf("WARNING: unhandled operation in the jump table format AST!\n");
valid = false;
break;
}
targetAddress = results[ast];
if (cs->getAddressWidth() == 4) {
targetAddress &= 0xffffffff;
}
#if defined(os_windows)
tableBase -= cs->loadAddress();
tableLastEntry -= cs->loadAddress();
targetAddress -= cs->loadAddress();
#endif

if (!cs->isCode(tableBase) && !cs->isData(tableBase)) {
parsing_printf("\ttableBase 0x%lx invalid, returning false\n", tableBase);
parsing_printf("Not jump table format!\n");
return false;
}
if (!cs->isReadOnly(tableBase)) {
parsing_printf("\ttableBase 0x%lx not read only, returning false\n", tableBase);
parsing_printf("Not jump table format!\n");
return false;
}
return AST::Ptr();

}

AST::Ptr JumpTableReadVisitor::visit(DataflowAPI::ConstantAST *ast) {
const Constant &v = ast->val();
int64_t value = v.val;
if (v.size != 1 && v.size != 64 && (value & (1ULL << (v.size - 1)))) {
// Compute the two complements in bits of v.size
// and change it to a negative number
value = -(((~value) & ((1ULL << v.size) - 1)) + 1);
}
results.insert(make_pair(ast, value));
return AST::Ptr();
}

for (Address tableEntry = tableBase; tableEntry <= tableLastEntry; tableEntry += target.interval.stride) {
if (!cs->isCode(tableEntry) && !cs->isData(tableEntry)) continue;
if (!cs->isReadOnly(tableEntry)) continue;
int64_t targetAddress = 0;
if (target.tableReadSize > 0) {
switch (target.tableReadSize) {
case 8:
targetAddress = *(const uint64_t *) cs->getPtrToInstruction(tableEntry);
break;
case 4:
targetAddress = *(const uint32_t *) cs->getPtrToInstruction(tableEntry);
if (target.isZeroExtend) break;
if ((addressWidth == 8) && (targetAddress & 0x80000000)) {
targetAddress |= SIGNEX_64_32;
}
break;
case 2:
targetAddress = *(const uint16_t *) cs->getPtrToInstruction(tableEntry);
if (target.isZeroExtend) break;
if ((addressWidth == 8) && (targetAddress & 0x8000)) {
targetAddress |= SIGNEX_64_16;
}
if ((addressWidth == 4) && (targetAddress & 0x8000)) {
targetAddress |= SIGNEX_32_16;
}

break;
case 1:
targetAddress = *(const uint8_t *) cs->getPtrToInstruction(tableEntry);
if (target.isZeroExtend) break;
if ((addressWidth == 8) && (targetAddress & 0x80)) {
targetAddress |= SIGNEX_64_8;
}
if ((addressWidth == 4) && (targetAddress & 0x80)) {
targetAddress |= SIGNEX_32_8;
}
AST::Ptr JumpTableReadVisitor::visit(DataflowAPI::VariableAST * var) {
if (var->val().reg != index) {
// The only variable in the jump table format AST should the index.
// If it is not the case, something is wrong
parsing_printf("WARNING: the jump table format AST contains a variable that is not the index\n");
valid = false;
}
results.insert(make_pair(var, indexValue));
return AST::Ptr();
}

break;

default:
parsing_printf("Invalid memory read size %d\n", target.tableReadSize);
return false;
}
targetAddress *= target.multiply;
if (target.targetBase != 0) {
if (target.isSubReadContent)
targetAddress = target.targetBase - targetAddress;
else
targetAddress += target.targetBase;
bool JumpTableReadVisitor::PerformMemoryRead(Address addr, int64_t &v) {
int addressWidth = cs->getAddressWidth();
if (addressWidth == 4) {
addr &= 0xffffffff;
}

}
#if defined(os_windows)
targetAddress -= cs->loadAddress();
addr -= cs->loadAddress();
#endif
} else targetAddress = tableEntry;
if (addressWidth == 4) targetAddress &= 0xffffffff;
parsing_printf("Jumping to target %lx,", targetAddress);
if (cs->isCode(targetAddress)) {
// Jump tables may contain may repetitious entries.
// We only want to create one edge for disctinct each jump target.
jumpTargets.insert(targetAddress);
}
// If the jump target is resolved to be a constant,
if (target.interval.stride == 0) break;
if (!cs->isCode(addr) && !cs->isData(addr)) return false;
if (!cs->isReadOnly(addr)) return false;
switch (memoryReadSize) {
case 8:
v = *(const uint64_t *) cs->getPtrToInstruction(addr);
break;
case 4:
v = *(const uint32_t *) cs->getPtrToInstruction(addr);
if (isZeroExtend) break;
if ((addressWidth == 8) && (v & 0x80000000)) {
v |= SIGNEX_64_32;
}
break;
case 2:
v = *(const uint16_t *) cs->getPtrToInstruction(addr);
if (isZeroExtend) break;
if ((addressWidth == 8) && (v & 0x8000)) {
v |= SIGNEX_64_16;
}
if ((addressWidth == 4) && (v & 0x8000)) {
v |= SIGNEX_32_16;
}
break;
case 1:
v = *(const uint8_t *) cs->getPtrToInstruction(addr);
if (isZeroExtend) break;
if ((addressWidth == 8) && (v & 0x80)) {
v |= SIGNEX_64_8;
}
if ((addressWidth == 4) && (v & 0x80)) {
v |= SIGNEX_32_8;
}
break;
default:
parsing_printf("Invalid memory read size %d\n", memoryReadSize);
return false;
}
return true;
}
*/
21 changes: 21 additions & 0 deletions parseAPI/src/IndirectASTVisitor.h
Expand Up @@ -81,4 +81,25 @@ class JumpTableFormatVisitor: public ASTVisitor {
virtual ASTPtr visit(DataflowAPI::VariableAST *ast);
JumpTableFormatVisitor(ParseAPI::Block *bl);
};

class JumpTableReadVisitor: public ASTVisitor {
public:
using ASTVisitor::visit;
AbsRegion index;
int64_t indexValue;
CodeSource* cs;
Address targetAddress;
int memoryReadSize;
bool valid;
bool isZeroExtend;


// This tracks the results of computation for each sub AST
map<AST*, int64_t> results;
JumpTableReadVisitor(AbsRegion i, int v, CodeSource *c, bool ze, int m);
virtual ASTPtr visit(DataflowAPI::RoseAST *ast);
virtual ASTPtr visit(DataflowAPI::ConstantAST *ast);
virtual ASTPtr visit(DataflowAPI::VariableAST *ast);
bool PerformMemoryRead(Address addr, int64_t &v);
};
#endif
54 changes: 49 additions & 5 deletions parseAPI/src/IndirectAnalyzer.C
Expand Up @@ -4,6 +4,7 @@
#include "JumpTableFormatPred.h"
#include "JumpTableIndexPred.h"
#include "SymbolicExpression.h"
#include "IndirectASTVisitor.h"
#include "IA_IAPI.h"
#include "debug_parse.h"

Expand All @@ -21,6 +22,7 @@ using namespace Dyninst::InstructionAPI;
bool IndirectControlFlowAnalyzer::NewJumpTableAnalysis(std::vector<std::pair< Address, Dyninst::ParseAPI::EdgeTypeEnum > >& outEdges) {
parsing_printf("Apply indirect control flow analysis at %lx\n", block->last());
parsing_printf("Looking for thunk\n");
// if (block->last() == 0x526e74) dyn_debug_parsing=1; else dyn_debug_parsing=0;

// Find all blocks that reach the block containing the indirect jump
// This is a prerequisit for finding thunks
Expand Down Expand Up @@ -54,7 +56,6 @@ bool IndirectControlFlowAnalyzer::NewJumpTableAnalysis(std::vector<std::pair< Ad
fprintf(stderr, " not jump table\n");
return false;
}
if (block->last() == 0x527af2) dyn_debug_parsing=1; else dyn_debug_parsing=0;

Slicer indexSlicer(jtfp.indexLoc, jtfp.indexLoc->block(), func, false, false);
JumpTableIndexPred jtip(func, block, jtfp.index, se);
Expand All @@ -74,9 +75,10 @@ bool IndirectControlFlowAnalyzer::NewJumpTableAnalysis(std::vector<std::pair< Ad
jtip.IsIndexBounded(g, bfc, target);
}
if (jtip.findBound) {
fprintf(stderr, " bound %s\n", jtip.bound.format().c_str());
fprintf(stderr, " bound %s", jtip.bound.format().c_str());
} else {
fprintf(stderr, " Cannot find bound\n");
return false;
}
/*
if (!jtip.findBound()) {
Expand All @@ -86,8 +88,12 @@ bool IndirectControlFlowAnalyzer::NewJumpTableAnalysis(std::vector<std::pair< Ad
}
*/
std::vector<std::pair< Address, Dyninst::ParseAPI::EdgeTypeEnum > > jumpTableOutEdges;
// ReadTable(jtfp, indexBound);

ReadTable(jtfp.jumpTargetExpr,
jtfp.index,
jtip.bound,
GetMemoryReadSize(jtfp.memLoc),
jumpTableOutEdges);
fprintf(stderr, ", find %d edges\n", jumpTableOutEdges.size());
outEdges.insert(outEdges.end(), jumpTableOutEdges.begin(), jumpTableOutEdges.end());
return !jumpTableOutEdges.empty();
}
Expand Down Expand Up @@ -178,4 +184,42 @@ void IndirectControlFlowAnalyzer::FindAllThunks() {
}
}


void IndirectControlFlowAnalyzer::ReadTable(AST::Ptr jumpTargetExpr,
AbsRegion index,
StridedInterval &indexBound,
int memoryReadSize,
std::vector<std::pair<Address, Dyninst::ParseAPI::EdgeTypeEnum> > &targetEdges) {
CodeSource *cs = block->obj()->cs();
set<Address> jumpTargets;
for (int v = indexBound.low; v <= indexBound.high; v += indexBound.stride) {
// TODO: need to detect whether the memory is a zero extend or a sign extend
JumpTableReadVisitor jtrv(index, v, cs, false, memoryReadSize);
jumpTargetExpr->accept(&jtrv);
if (jtrv.valid && cs->isCode(jtrv.targetAddress)) {
jumpTargets.insert(jtrv.targetAddress);
} else {
// We have a bad entry. We stop here, as we have wrong information
// In this case, we keep the good entries
parsing_printf("WARNING: resolving jump tables leads to a bad address %lx\n", jtrv.targetAddress);
break;
}
if (indexBound.stride == 0) break;
}
for (auto tit = jumpTargets.begin(); tit != jumpTargets.end(); ++tit) {
targetEdges.push_back(make_pair(*tit, INDIRECT));
}
}

int IndirectControlFlowAnalyzer::GetMemoryReadSize(Assignment::Ptr memLoc) {
Instruction::Ptr i = memLoc->insn();
std::vector<Operand> ops;
i->getOperands(ops);
for (auto oit = ops.begin(); oit != ops.end(); ++oit) {
Operand o = *oit;
if (o.readsMemory()) {
Expression::Ptr exp = o.getValue();
return exp->size();
break;
}
}
}
11 changes: 6 additions & 5 deletions parseAPI/src/IndirectAnalyzer.h
Expand Up @@ -17,11 +17,12 @@ class IndirectControlFlowAnalyzer {

void GetAllReachableBlock();
void FindAllThunks();
bool IsJumpTable(GraphPtr slice, BoundFactsCalculator &bfc, StridedInterval &target);
bool FillInOutEdges(StridedInterval &target, std::vector<std::pair< Address, Dyninst::ParseAPI::EdgeTypeEnum > >& outEdges);
GraphPtr CalcBackwardSlice(ParseAPI::Block *b,
Address addr,
string filename);
void ReadTable(AST::Ptr,
AbsRegion,
StridedInterval &,
int ,
std::vector<std::pair<Address, Dyninst::ParseAPI::EdgeTypeEnum> > &);
int GetMemoryReadSize(Assignment::Ptr loc);


public:
Expand Down

0 comments on commit 578ff24

Please sign in to comment.