1
0
Fork 0

DBG: initial commit for control flow analysis

This commit is contained in:
Mr. eXoDia 2015-07-07 15:14:40 +02:00
parent 53fd990c83
commit 6779900c44
7 changed files with 283 additions and 3 deletions

View File

@ -0,0 +1,186 @@
#include "controlflowanalysis.h"
#include "memory.h"
#include "console.h"
ControlFlowAnalysis::ControlFlowAnalysis(uint base, uint size)
{
_base = base;
_size = size;
_data = new unsigned char[_size + MAX_DISASM_BUFFER];
MemRead((void*)_base, _data, _size, 0);
}
ControlFlowAnalysis::~ControlFlowAnalysis()
{
delete[] _data;
}
bool ControlFlowAnalysis::IsValidAddress(uint addr)
{
return addr >= _base && addr < _base + _size;
}
const unsigned char* ControlFlowAnalysis::TranslateAddress(uint addr)
{
return IsValidAddress(addr) ? _data + (addr - _base) : nullptr;
}
void ControlFlowAnalysis::Analyse()
{
dputs("Starting analysis...");
DWORD ticks = GetTickCount();
BasicBlockStarts();
BasicBlocks();
dprintf("Analysis finished in %ums!\n", GetTickCount() - ticks);
}
void ControlFlowAnalysis::SetMarkers()
{
dprintf("digraph ControlFlow {\n");
int i = 0;
std::map<uint, int> nodeMap;
for(auto block : _blocks)
{
nodeMap.insert({ block.start, i });
dprintf(" node%u [label=\"start=%p, end=%p\"];\n", i, block.start, block.end);
i++;
}
for(auto block : _blocks)
{
int startNode = nodeMap[block.start];
if(block.left)
{
if(nodeMap.count(block.left))
dprintf(" node%u -> node%u;\n", startNode, nodeMap[block.left]);
}
else
{
dprintf(" node%u [shape=point];\n", i);
dprintf(" node%u -> node%u;\n", startNode, i);
i++;
}
if(block.right)
{
if(nodeMap.count(block.right))
dprintf(" node%u -> node%u;\n", startNode, nodeMap[block.right]);
}
else
{
dprintf(" node%u [shape=point];\n", i);
dprintf(" node%u -> node%u;\n", startNode, i);
i++;
}
}
dprintf("}\n");
}
void ControlFlowAnalysis::BasicBlockStarts()
{
bool bSkipFilling = false;
for(uint i = 0; i < _size;)
{
uint addr = _base + i;
if(_cp.Disassemble(addr, TranslateAddress(addr), MAX_DISASM_BUFFER))
{
if(bSkipFilling) //handle filling skip mode
{
if(!_cp.IsFilling()) //do nothing until the filling stopped
{
bSkipFilling = false;
_blockStarts.insert(addr);
}
}
else if(_cp.InGroup(CS_GRP_RET))
{
bSkipFilling = true; //skip INT3/NOP/whatever filling bytes (those are not part of the control flow)
}
else if(_cp.InGroup(CS_GRP_JUMP) || _cp.IsLoop()) //branches
{
uint dest1 = GetBranchOperand();
uint dest2 = 0;
if(_cp.GetId() != X86_INS_JMP) //unconditional jump
dest2 = addr + _cp.Size();
if(!dest1 && !dest2)
bSkipFilling = true;
if(dest1)
_blockStarts.insert(dest1);
if(dest2)
_blockStarts.insert(dest2);
}
else if(_cp.InGroup(CS_GRP_CALL))
{
uint dest1 = GetBranchOperand();
if(dest1)
_blockStarts.insert(dest1);
}
else
{
uint dest1 = GetBranchOperand();
if(dest1)
_blockStarts.insert(dest1);
}
i += _cp.Size();
}
else
i++;
}
}
void ControlFlowAnalysis::BasicBlocks()
{
for(auto i = _blockStarts.begin(); i != _blockStarts.end(); i++)
{
uint start = *i;
if(!IsValidAddress(start))
continue;
uint nextStart = _base + _size;
auto next = std::next(i);
if(next != _blockStarts.end())
nextStart = *next;
for(uint addr = start, prevaddr = 0; addr < _base + _size;)
{
prevaddr = addr;
if(_cp.Disassemble(addr, TranslateAddress(addr), MAX_DISASM_BUFFER))
{
if(_cp.InGroup(CS_GRP_RET))
{
_blocks.push_back(BasicBlock(start, addr, 0, 0)); //leaf block
break;
}
else if(_cp.InGroup(CS_GRP_JUMP) || _cp.IsLoop())
{
uint dest1 = GetBranchOperand();
uint dest2 = _cp.GetId() != X86_INS_JMP ? addr + _cp.Size() : 0;
_blocks.push_back(BasicBlock(start, addr, dest1, dest2));
break;
}
addr += _cp.Size();
}
else
addr++;
if(addr == nextStart) //special case handling overlapping blocks
{
_blocks.push_back(BasicBlock(start, prevaddr, 0, nextStart));
break;
}
}
}
}
uint ControlFlowAnalysis::GetBranchOperand()
{
for(int i = 0; i < _cp.x86().op_count; i++)
{
const cs_x86_op & operand = _cp.x86().operands[i];
if(operand.type == X86_OP_IMM)
{
uint dest = (uint)operand.imm;
if(dest >= _base && dest < _base + _size)
return dest;
}
}
return 0;
}

View File

@ -0,0 +1,71 @@
#ifndef _CONTROLFLOWANALYSIS_H
#define _CONTROLFLOWANALYSIS_H
#include "_global.h"
#include "capstone_wrapper.h"
class ControlFlowAnalysis
{
public:
explicit ControlFlowAnalysis(uint base, uint size);
ControlFlowAnalysis(const ControlFlowAnalysis & that) = delete;
~ControlFlowAnalysis();
bool IsValidAddress(uint addr);
const unsigned char* TranslateAddress(uint addr);
void Analyse();
void SetMarkers();
struct FunctionInfo
{
uint start;
uint end;
bool operator<(const FunctionInfo & b) const
{
return start < b.start;
}
bool operator==(const FunctionInfo & b) const
{
return start == b.start;
}
};
struct BasicBlock
{
uint start;
uint end;
uint left;
uint right;
BasicBlock()
{
this->start = 0;
this->end = 0;
this->left = 0;
this->right = 0;
}
BasicBlock(uint start, uint end, uint left, uint right)
{
this->start = start;
this->end = end;
this->left = min(left, right);
this->right = max(left, right);
}
};
private:
uint _base;
uint _size;
unsigned char* _data;
std::set<uint> _blockStarts;
std::vector<BasicBlock> _blocks;
Capstone _cp;
void BasicBlockStarts();
void BasicBlocks();
uint GetBranchOperand();
};
#endif //_CONTROLFLOWANALYSIS_H

View File

@ -26,6 +26,8 @@
#include "module.h"
#include "stringformat.h"
#include "filereader.h"
#include "functionanalysis.h"
#include "controlflowanalysis.h"
static bool bRefinit = false;
@ -1877,8 +1879,6 @@ CMDRESULT cbInstrCapstone(int argc, char* argv[])
return STATUS_CONTINUE;
}
#include "functionanalysis.h"
CMDRESULT cbInstrAnalyse(int argc, char* argv[])
{
SELECTIONDATA sel;
@ -1892,6 +1892,19 @@ CMDRESULT cbInstrAnalyse(int argc, char* argv[])
return STATUS_CONTINUE;
}
CMDRESULT cbInstrCfanalyse(int argc, char* argv[])
{
SELECTIONDATA sel;
GuiSelectionGet(GUI_DISASSEMBLY, &sel);
uint size = 0;
uint base = MemFindBaseAddr(sel.start, &size);
ControlFlowAnalysis anal(base, size);
anal.Analyse();
anal.SetMarkers();
GuiUpdateAllViews();
return STATUS_CONTINUE;
}
CMDRESULT cbInstrVisualize(int argc, char* argv[])
{
if(argc < 3)
@ -1950,7 +1963,7 @@ CMDRESULT cbInstrVisualize(int argc, char* argv[])
break;
const cs_x86_op & operand = _cp.x86().operands[0];
if(_cp.InGroup(CS_GRP_JUMP) && operand.type == X86_OP_IMM) //jump
if((_cp.InGroup(CS_GRP_JUMP) || _cp.IsLoop()) && operand.type == X86_OP_IMM) //jump
{
uint dest = (uint)operand.imm;

View File

@ -70,5 +70,6 @@ CMDRESULT cbInstrCapstone(int argc, char* argv[]);
CMDRESULT cbInstrAnalyse(int argc, char* argv[]);
CMDRESULT cbInstrVisualize(int argc, char* argv[]);
CMDRESULT cbInstrMeminfo(int argc, char* argv[]);
CMDRESULT cbInstrCfanalyse(int argc, char* argv[]);
#endif // _INSTRUCTIONS_H

View File

@ -203,6 +203,7 @@ static void registercommands()
dbgcmdnew("capstone", cbInstrCapstone, true); //disassemble using capstone
dbgcmdnew("visualize", cbInstrVisualize, true); //visualize analysis
dbgcmdnew("meminfo", cbInstrMeminfo, true); //command to debug memory map bugs
dbgcmdnew("cfanal\1cfanalyse\1cfanalyze", cbInstrCfanalyse, true); //control flow analysis
}
static bool cbCommandProvider(char* cmd, int maxlen)

View File

@ -28,6 +28,7 @@
<ClCompile Include="commandparser.cpp" />
<ClCompile Include="comment.cpp" />
<ClCompile Include="console.cpp" />
<ClCompile Include="controlflowanalysis.cpp" />
<ClCompile Include="dbghelp_safe.cpp" />
<ClCompile Include="debugger.cpp" />
<ClCompile Include="debugger_commands.cpp" />
@ -92,6 +93,7 @@
<ClInclude Include="commandparser.h" />
<ClInclude Include="comment.h" />
<ClInclude Include="console.h" />
<ClInclude Include="controlflowanalysis.h" />
<ClInclude Include="dbghelp\dbghelp.h" />
<ClInclude Include="dbghelp_safe.h" />
<ClInclude Include="debugger.h" />

View File

@ -243,6 +243,9 @@
<ClCompile Include="_scriptapi_gui.cpp">
<Filter>Source Files\Interfaces/Exports\_scriptapi</Filter>
</ClCompile>
<ClCompile Include="controlflowanalysis.cpp">
<Filter>Source Files\Analysis</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="x64_dbg.h">
@ -563,5 +566,8 @@
<ClInclude Include="_scriptapi_gui.h">
<Filter>Header Files\Interfaces/Exports\_scriptapi</Filter>
</ClInclude>
<ClInclude Include="controlflowanalysis.h">
<Filter>Header Files\Analysis</Filter>
</ClInclude>
</ItemGroup>
</Project>