Files
testplugin/FunctionGraph.cpp
2014-08-29 05:03:00 +02:00

236 lines
9.5 KiB
C++

#include "FunctionGraph.h"
#include <list>
#include <set>
#include <stdio.h>
struct previous_edge_struct_s
{
ULONG_PTR source;
ULONG_PTR targetfalse;
ULONG_PTR targettrue;
};
#define TEXTLEN 256
static char vcg_params[] = "manhattan_edges: yes\n"
"layoutalgorithm: mindepth\n"
"finetuning: no\n"
"layout_downfactor: 100\n"
"layout_upfactor: 0\n"
"layout_nearfactor: 0\n"
"xlspace: 12\n"
"yspace: 30\n"
"colorentry 32: 0 0 0\n"
"colorentry 33: 0 0 255\n"
"colorentry 34: 0 0 255\n"
"colorentry 35: 128 128 128\n"
"colorentry 36: 128 128 128\n"
"colorentry 37: 0 0 128\n"
"colorentry 38: 0 0 128\n"
"colorentry 39: 0 0 255\n"
"colorentry 40: 0 0 255\n"
"colorentry 41: 0 0 128\n"
"colorentry 42: 0 128 0\n"
"colorentry 43: 0 255 0\n"
"colorentry 44: 0 128 0\n"
"colorentry 45: 255 128 0\n"
"colorentry 46: 0 128 0\n"
"colorentry 47: 128 128 255\n"
"colorentry 48: 255 0 0\n"
"colorentry 49: 128 128 0\n"
"colorentry 50: 1 1 1\n"
"colorentry 51: 192 192 192\n"
"colorentry 52: 0 0 255\n"
"colorentry 53: 0 0 255\n"
"colorentry 54: 0 0 255\n"
"colorentry 55: 128 128 128\n"
"colorentry 56: 128 128 255\n"
"colorentry 57: 0 128 0\n"
"colorentry 58: 0 0 128\n"
"colorentry 59: 0 0 255\n"
"colorentry 60: 128 0 128\n"
"colorentry 61: 0 128 0\n"
"colorentry 62: 0 128 0\n"
"colorentry 63: 0 128 64\n"
"colorentry 64: 0 0 128\n"
"colorentry 65: 0 0 128\n"
"colorentry 66: 255 0 255\n"
"colorentry 67: 128 128 0\n"
"colorentry 68: 0 0 128\n"
"colorentry 69: 0 0 255\n"
"colorentry 70: 0 0 128\n"
"colorentry 71: 0 0 255\n"
"colorentry 72: 0 0 0\n"
"colorentry 73: 255 255 255\n"
"colorentry 74: 192 187 175\n"
"colorentry 75: 0 255 255\n"
"colorentry 76: 0 0 0\n"
"colorentry 77: 128 0 0\n"
"colorentry 78: 128 128 128\n"
"colorentry 79: 128 128 0\n"
"colorentry 80: 255 0 255\n"
"colorentry 81: 0 0 0\n"
"colorentry 82: 0 0 255\n"
"colorentry 83: 100 255 255\n";
//escape comments
static void sanitize(const char* comment, char* cleaned)
{
char* t;
for(t = cleaned; *comment; comment++)
{
if(*comment == '"')
{
*t++ = '\\';
*t = '"';
}
else if(*comment == '\\')
{
*t++ = '\\';
*t = '\\';
}
else
{
*t = *comment;
}
t++;
}
*t = '\0';
}
bool make_flowchart(ULONG_PTR start, ULONG_PTR end, const wchar_t* szTargetFile, GETINSTRINFO getInstrInfo)
{
HANDLE temp_file = CreateFileW(szTargetFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if(temp_file == INVALID_HANDLE_VALUE)
return false;
char buffer[TEXTLEN * 2 + 4];
ULONG_PTR currentnode = start;
int writelen = sprintf(buffer, "graph: {\ntitle: \"Graph of %p\"\n", start);
DWORD len_written;
WriteFile(temp_file, buffer, (DWORD) writelen, &len_written, NULL);
WriteFile(temp_file, vcg_params, (DWORD) strlen(vcg_params), &len_written, NULL);
writelen = sprintf(buffer, "node: { title: \"%p\" vertical_order: 0 color: 83 fontname: \"courR12\" label: \"%p:", currentnode, currentnode);
WriteFile(temp_file, buffer, (DWORD) writelen, &len_written, NULL);
std::set<ULONG_PTR> nodelist; //list of node addresses, just to make sure we are not handling nodes multiple times
std::list<instr_info> disasmlist;
ULONG_PTR psize = 0;
ULONG_PTR current_addr = start;
// first pass - disassemble instructions and enumerate nodes
do
{
current_addr += psize;
//get instr_info
instr_info disasm_result;
ULONG_PTR next_addr = getInstrInfo(current_addr, &disasm_result);
psize = next_addr - current_addr;
if(psize <= 0)
psize = 1;
//add instr_info to list
disasmlist.push_back(disasm_result);
// enumerate nodes
if(currentnode == 0)
{
currentnode = current_addr;
nodelist.insert(currentnode);
}
if((disasm_result.jmpaddr >= start) && (disasm_result.jmpaddr < end))
{
// this is a jump - start of an edge and pointer to a node
nodelist.insert(disasm_result.jmpaddr);
currentnode = 0; // update current_addrnode on next pass
}
}
while(current_addr + psize <= end);
// walk through saved disasm list and split into nodes
currentnode = start;
bool orphannode = true;
previous_edge_struct_s previous_edge;
memset(&previous_edge, 0, sizeof(previous_edge_struct_s));
std::string edgelist;
for(std::list<instr_info>::iterator i = disasmlist.begin(); i != disasmlist.end(); ++i)
{
instr_info disasm_result = *i;
current_addr = disasm_result.addr;
//node contents (address)
if(nodelist.find(current_addr) != nodelist.end()) //found current_addr in set
{
if(orphannode)
{
writelen = sprintf(buffer, "edge: { sourcename: \"%p\" targetname: \"%p\" }\n", currentnode, current_addr);
edgelist += buffer;
}
orphannode = true;
currentnode = current_addr;
writelen = sprintf(buffer, "\" }\n");
WriteFile(temp_file, buffer, (DWORD) writelen, &len_written, NULL);
writelen = sprintf(buffer, "node: { title: \"%p\" color: 83 fontname: \"courR12\" label: \"%p:", current_addr, current_addr);
WriteFile(temp_file, buffer, (DWORD) writelen, &len_written, NULL);
}
//node contents (disassembly)
if(disasm_result.comment.length())
{
char clean_comment[TEXTLEN * 2];
sanitize(disasm_result.comment.c_str(), clean_comment);
writelen = sprintf(buffer, "\n%s\t; %s", disasm_result.instrText.c_str(), clean_comment);
}
else
{
writelen = sprintf(buffer, "\n%s", disasm_result.instrText.c_str());
}
WriteFile(temp_file, buffer, (DWORD)writelen, &len_written, NULL);
if(previous_edge.source != 0)
{
// append stored edge info with currentnode info to edgelist buffer
previous_edge.targetfalse = currentnode;
writelen = sprintf(buffer, "edge: { sourcename: \"%p\" targetname: \"%p\" label: \"false\" color: red }\n", previous_edge.source, previous_edge.targetfalse);
edgelist += buffer;
writelen = sprintf(buffer, "edge: { sourcename: \"%p\" targetname: \"%p\" label: \"true\" color: darkgreen }\n", previous_edge.source, previous_edge.targettrue);
edgelist += buffer;
previous_edge.source = 0;
}
if((disasm_result.jmpaddr >= start) && (disasm_result.jmpaddr < end))
{
// this is a jump - start of an edge and pointer to a node
orphannode = false;
previous_edge.source = currentnode;
previous_edge.targettrue = disasm_result.jmpaddr;
previous_edge.targetfalse = 0;
if(!_stricmp(disasm_result.instrText.substr(0, 3).c_str(), "jmp"))
{
// straight jmp, no true/false
writelen = sprintf(buffer, "edge: { sourcename: \"%p\" targetname: \"%p\" }\n", previous_edge.source, previous_edge.targettrue);
edgelist += buffer;
previous_edge.source = 0;
}
}
}
// close last node
writelen = sprintf(buffer, "\" vertical_order: %d }\n", nodelist.size());
WriteFile(temp_file, buffer, (DWORD)writelen, &len_written, NULL);
// write edgelist
WriteFile(temp_file, edgelist.c_str(), (DWORD)edgelist.length(), &len_written, NULL);
writelen = sprintf(buffer, "}\n");
WriteFile(temp_file, buffer, (DWORD)writelen, &len_written, NULL);
CloseHandle(temp_file);
return true;
}