1
0
Fork 0

DBG: integrate analysisplugin for out-of-the-box analysis

This is the last version of this analysis code. The next will contain a program-flow-graph for further analysis. It currently supports:
- detect Api-Calls and add comments for the parameter (x86/x64)
- detect function-bodies (x86)

you have to download the "api.dat" from the static analysis plugin
This commit is contained in:
tr4ceflow 2014-08-17 15:50:16 +02:00
parent e028ccf680
commit d1943dc3d1
26 changed files with 1488 additions and 3 deletions

View File

@ -634,7 +634,8 @@ typedef enum
GUI_REPAINT_TABLE_VIEW, // param1=unused, param2=unused
GUI_UPDATE_PATCHES, // param1=unused, param2=unused
GUI_UPDATE_CALLSTACK, // param1=unused, param2=unused
GUI_SYMBOL_REFRESH_CURRENT // param1=unused, param2=unused
GUI_SYMBOL_REFRESH_CURRENT, // param1=unused, param2=unused
GUI_ANALYSE_CODE // param1=int_t Base, param2=int_t Size
} GUIMSG;
//GUI structures
@ -708,6 +709,7 @@ BRIDGE_IMPEXP void GuiUpdateSideBar();
BRIDGE_IMPEXP void GuiRepaintTableView();
BRIDGE_IMPEXP void GuiUpdatePatches();
BRIDGE_IMPEXP void GuiUpdateCallStack();
BRIDGE_IMPEXP void GuiAnalyseCode(duint base, duint size);
#ifdef __cplusplus
}

View File

@ -0,0 +1,191 @@
#include "AnalysisRunner.h"
#include "../_global.h"
#include "../console.h"
#include "meta.h"
#include "IntermodularCalls.h"
#include "FunctionDetector.h"
namespace tr4ce
{
AnalysisRunner::AnalysisRunner(duint BaseAddress, duint Size)
{
// store all given information
mBaseAddress = BaseAddress;
mSize = Size;
_Calls = new IntermodularCalls(this);
_Func = new CallDetector(this);
initialise();
clear();
}
AnalysisRunner::~AnalysisRunner(void)
{
}
void AnalysisRunner::start()
{
// do we have information about the function prototypes?
if(!mApiDb->ok())
return;
dputs("[StaticAnalysis] analysis started ...");
// remove all temp information
clear();
run();
// see every instructions once
publishInstructions();
// do some magic
think();
dputs("[StaticAnalysis] analysis finished ...");
}
void AnalysisRunner::publishInstructions()
{
StackEmulator Stack;
RegisterEmulator Register;
// show every sub-plugin the current instruction
for each(auto currentInstruction in mInstructionsBuffer)
{
see(&currentInstruction.second, &Stack, &Register);
Stack.emulate(&currentInstruction.second.BeaStruct);
Register.emulate(&currentInstruction.second.BeaStruct);
}
}
void AnalysisRunner::run()
{
// this function will be run once
// copy the code section
mCodeMemory = new unsigned char[mSize];
if(!DbgMemRead(mBaseAddress, mCodeMemory, mSize))
{
//ERROR: copying did not work
dputs("[StaticAnalysis] could not read memory ...");
return;
}
//loop over all instructions
DISASM disasm;
duint baseaddr = mBaseAddress;
duint size = mSize;
memset(&disasm, 0, sizeof(disasm));
#ifdef _WIN64
disasm.Archi = 64;
#endif // _WIN64
currentEIP = (UIntPtr)mCodeMemory;
disasm.EIP = currentEIP;
currentVirtualAddr = (UInt64)baseaddr;
disasm.VirtualAddr = currentVirtualAddr;
duint i = 0;
for(duint i = 0; i < size;)
{
// disassemble instruction
int len = Disasm(&disasm);
// everything ok?
if(len != UNKNOWN_OPCODE)
{
Instruction_t instr(&disasm, len);
mInstructionsBuffer.insert(std::pair<UInt64, Instruction_t>(disasm.VirtualAddr, instr));
}
else
{
// something went wrong --> notify every subplugin
unknownOpCode(&disasm);
len = 1;
}
// we do not know if the struct DISASM gets destroyed on unkown opcodes --> use variables
currentEIP += len;
currentVirtualAddr += len;
disasm.EIP = currentEIP;
disasm.VirtualAddr = currentVirtualAddr;
// move memory pointer
i += len;
}
delete mCodeMemory;
}
void AnalysisRunner::clear()
{
mInstructionsBuffer.clear();
// forward to all sub-plugin
_Calls->clear();
_Func->clear();
}
void AnalysisRunner::think()
{
// forward to all sub-plugin
_Calls->think();
_Func->think();
}
void AnalysisRunner::see(const Instruction_t* disasm, const StackEmulator* stack, const RegisterEmulator* regState)
{
// forward to all sub-plugin
_Calls->see(disasm, stack, regState);
_Func->see(disasm, stack, regState);
}
void AnalysisRunner::unknownOpCode(const DISASM* disasm)
{
// forward to all sub-plugin
_Calls->unknownOpCode(disasm);
_Func->unknownOpCode(disasm);
}
void AnalysisRunner::initialise()
{
// forward to all sub-plugin
_Calls->initialise(mBaseAddress, mSize);
_Func->initialise(mBaseAddress, mSize);
}
ApiDB* AnalysisRunner::FunctionInformation() const
{
return mApiDb;
}
void AnalysisRunner::setFunctionInformation(ApiDB* api)
{
mApiDb = api;
}
// return instruction of address - if possible
int AnalysisRunner::instruction(UInt64 va, Instruction_t* instr) const
{
if(tr4ce::contains(mInstructionsBuffer, va))
{
*instr = mInstructionsBuffer.at(va);
return instr->Length;
}
else
{
return UNKNOWN_OPCODE;
}
}
std::map<UInt64, Instruction_t>::const_iterator AnalysisRunner::instructionIter(UInt64 va) const
{
return mInstructionsBuffer.find(va);
}
std::map<UInt64, Instruction_t>::const_iterator AnalysisRunner::lastInstruction() const
{
return mInstructionsBuffer.end();
}
duint AnalysisRunner::base() const
{
return mBaseAddress;
}
};

View File

@ -0,0 +1,64 @@
#pragma once
#include "../_global.h"
#include <map>
#include "Meta.h"
#include "StackEmulator.h"
#include "RegisterEmulator.h"
#include "ApiDB.h"
namespace tr4ce
{
class IntermodularCalls;
class CallDetector;
class AnalysisRunner
{
std::map<UInt64, Instruction_t> mInstructionsBuffer;
duint mBaseAddress;
duint mSize;
IntermodularCalls* _Calls;
CallDetector* _Func;
ApiDB* mApiDb;
unsigned char* mCodeMemory;
UIntPtr currentEIP;
UInt64 currentVirtualAddr;
protected:
// forwarding
void see(const Instruction_t* disasm, const StackEmulator* stack, const RegisterEmulator* regState);
void clear();
void think();
void initialise();
void unknownOpCode(const DISASM* disasm);
private:
void run();
void publishInstructions();
public:
ApiDB* FunctionInformation() const;
void setFunctionInformation(ApiDB* api);
AnalysisRunner(duint BaseAddress, duint Size);
~AnalysisRunner(void);
void start();
int instruction(UInt64 va, Instruction_t* instr) const;
std::map<UInt64, Instruction_t>::const_iterator instructionIter(UInt64 va) const;
std::map<UInt64, Instruction_t>::const_iterator lastInstruction() const;
duint base() const;
};
};

View File

@ -0,0 +1,130 @@
/* plugin: (StaticAnalysis) for x64dbg <http://www.x64dbg.com>
* author: tr4ceflow@gmail.com <http://blog.traceflow.com>
* license: GLPv3
*/
#include "../console.h"
#include "ApiDB.h"
#include "Meta.h"
#include <fstream>
#include <sstream>
#include <vector>
// http://stackoverflow.com/a/22923239/1392416
std::vector<std::string> split(const std::string & string, const char* del)
{
size_t first = 0, second = 0;
size_t end = string.size();
size_t len = strlen(del);
std::vector<std::string> tokens;
while((second = string.find(del, first)) != (std::string::npos))
{
size_t dif = second - first;
if(dif)
{
tokens.push_back(string.substr(first, dif));
}
first = second + len;
}
if(first != end)
{
tokens.push_back(string.substr(first));
}
return tokens;
}
namespace tr4ce
{
ApiDB::ApiDB(void)
{
unsigned int i = 0;
mValid = true;
std::ifstream helpFile;
std::string rawLine;
helpFile.open("api.dat");
if(!helpFile)
{
dputs("[StaticAnalysis] api help file not found ...");
}
else
{
dputs("[StaticAnalysis] load api help file ...");
while(!helpFile.eof())
{
helpFile >> rawLine;
std::vector<std::string> tokens = split(rawLine, ";");
if(tokens.size() > 3)
{
FunctionInfo_t f;
f.DLLName = tokens.at(0);
f.ReturnType = tokens.at(1);
f.Name = tokens.at(2);
for(unsigned int j = 3; j < tokens.size() - 1; j += 2)
{
ArgumentInfo_t a;
a.Type = tokens.at(j);
a.Name = tokens.at(j + 1);
f.Arguments.push_back(a);
}
mInfo.push_back(f);
i++;
}
}
}
dprintf("[StaticAnalysis] loaded %i functions signatures from helpfile\n", i);
helpFile.close();
}
ApiDB::~ApiDB(void)
{
}
FunctionInfo_t ApiDB::find(std::string name)
{
if(name[0] == '&')
name.erase(0, 1);
FunctionInfo_t f;
f.invalid = true;
//_plugin_logprintf("[StaticAnalysis:IntermodularCalls] search data %s \n",name.c_str() );
std::list<FunctionInfo_t>::iterator it = mInfo.begin();
while(it != mInfo.end())
{
if(it->Name == name)
{
f = *it;
f.invalid = false;
break;
}
it++;
}
return f;
}
const bool ApiDB::ok() const
{
return mValid;
}
};

View File

@ -0,0 +1,24 @@
#pragma once
#include "Meta.h"
#include <list>
namespace tr4ce
{
class ApiDB
{
private:
bool mValid; // "database" ok ?
public:
ApiDB(void);
~ApiDB(void);
const bool ok() const;
FunctionInfo_t find(std::string name);
std::list<FunctionInfo_t> mInfo;
};
};

View File

@ -0,0 +1,24 @@
#include "BeaInterpreter.h"
namespace tr4ce
{
BeaInterpreter::BeaInterpreter(void)
{
}
BeaInterpreter::~BeaInterpreter(void)
{
}
};

View File

@ -0,0 +1,24 @@
#pragma once
#include "Meta.h"
#include "../BeaEngine/BeaEngine.h"
namespace tr4ce
{
class BeaInterpreter
{
public:
BeaInterpreter(void);
~BeaInterpreter(void);
};
};

View File

@ -0,0 +1,114 @@
#include "IntermodularCalls.h"
#include "AnalysisRunner.h"
#include "../console.h"
#include "FunctionDetector.h"
namespace tr4ce
{
#ifndef _WIN64
#define _isCall(disasm) ((disasm.Instruction.Opcode == 0xE8) && (disasm.Instruction.BranchType) && (disasm.Instruction.BranchType!=RetType) && !(disasm.Argument1.ArgType &REGISTER_TYPE))
#else
#define _isCall(disasm) ((disasm.Instruction.BranchType==CallType) && (disasm.Instruction.BranchType!=RetType) && !(disasm.Argument1.ArgType &REGISTER_TYPE))
#endif
CallDetector::CallDetector(AnalysisRunner* parent) : ICommand(parent)
{
}
void CallDetector::clear()
{
numberOfFunctions = 0;
}
#ifndef _WIN64
void CallDetector::see(const Instruction_t* currentInstruction, const StackEmulator* stackState, const RegisterEmulator* regState)
{
if((currentInstruction->BeaStruct.Instruction.Opcode != 0xFF) && (_isCall(currentInstruction->BeaStruct)))
{
Instruction_t callTarget;
int len = mParent->instruction(currentInstruction->BeaStruct.Instruction.AddrValue, &callTarget);
if(len != UNKNOWN_OPCODE)
{
if((callTarget.BeaStruct.Instruction.Opcode != 0xFF) && (currentInstruction->BeaStruct.Instruction.AddrValue != mParent->base())){
dprintf("[StaticAnalysis:CallDetector] call at %x \n",currentInstruction->BeaStruct.VirtualAddr);
dprintf("[StaticAnalysis:CallDetector] call to %x \n",currentInstruction->BeaStruct.Instruction.AddrValue);
Call_t c(0);
c.startAddress = currentInstruction->BeaStruct.Instruction.AddrValue;
mCalls.insert(c);
}
}
}
}
#else
void CallDetector::see(const Instruction_t* currentInstruction, const StackEmulator* stackState, const RegisterEmulator* regState)
{
// if((_isCall(currentInstruction->BeaStruct)))
// {
// char labelText[MAX_LABEL_SIZE];
// bool hasLabel = DbgGetLabelAt(currentInstruction->BeaStruct.Instruction.AddrValue, SEG_DEFAULT, labelText);
// if(hasLabel)
// {
// // we have NO label from TitanEngine --> custom call
// FunctionInfo_t f = mParent->FunctionInformation()->find(labelText);
// if(f.invalid)
// {
// numberOfFunctions++;
// dprintf("[StaticAnalysis:CallDetector] call at %x \n",currentInstruction->BeaStruct.VirtualAddr);
// dprintf("[StaticAnalysis:CallDetector] call to %x \n",currentInstruction->BeaStruct.Instruction.AddrValue);
// Call_t c(0);
// c.startAddress = currentInstruction->BeaStruct.Instruction.AddrValue;
// mCalls.insert(c);
//
// }
// }
//
//
// }
}
#endif // _WIN64
bool CallDetector::think()
{
#ifndef _WIN64
const int RETOPCODE = 0xC2;
#else
const int RETOPCODE = 0xC3;
#endif
for each(Call_t c in mCalls){
dprintf("[StaticAnalysis:CallDetector] think about %x \n",c.startAddress);
Instruction_t t;
std::map<UInt64, Instruction_t>::const_iterator code = mParent->instructionIter(c.startAddress);
while(code->second.BeaStruct.Instruction.Opcode != RETOPCODE){
code++;
}
DbgSetAutoFunctionAt(c.startAddress,code->second.BeaStruct.VirtualAddr);
}
dprintf("[StaticAnalysis:CallDetector] found %i functions\n", numberOfFunctions);
return true;
}
void CallDetector::unknownOpCode(const DISASM* disasm)
{
// current instruction wasn't correctly disassembled, so assuming worst-case
}
};

View File

@ -0,0 +1,32 @@
#pragma once
#include "ICommand.h"
#include <list>
#include <set>
#include <map>
#include "Meta.h"
#include "StackEmulator.h"
#include "RegisterEmulator.h"
namespace tr4ce
{
class CallDetector : public ICommand
{
unsigned int numberOfFunctions;
std::set<Call_t> mCalls;
public:
CallDetector(AnalysisRunner* parent);
void clear();
void see(const Instruction_t* disasm, const StackEmulator* stack, const RegisterEmulator* regState);
bool think();
void unknownOpCode(const DISASM* disasm);
};
};

View File

@ -0,0 +1,24 @@
#include "ICommand.h"
#include "AnalysisRunner.h"
namespace tr4ce
{
ICommand::ICommand(AnalysisRunner* parent) : mParent(parent)
{
}
ICommand::~ICommand(void)
{
}
void ICommand::initialise(const uint Base, const uint Size)
{
mBase = Base;
mSize = Size;
}
};

View File

@ -0,0 +1,44 @@
/* plugin: (StaticAnalysis) for x64dbg <http://www.x64dbg.com>
* author: tr4ceflow@gmail.com <http://blog.traceflow.com>
* license: GLPv3
*/
#pragma once
#include "../BeaEngine/BeaEngine.h"
#include "../_global.h"
#include "Meta.h"
namespace tr4ce
{
class AnalysisRunner;
class StackEmulator;
class RegisterEmulator;
class ICommand
{
public:
ICommand(AnalysisRunner* parent);
virtual ~ICommand(void);
protected:
uint mBase;
uint mSize;
AnalysisRunner* mParent;
public:
// initialization before any analysis
void initialise(const uint Base, const uint Size);
// clear all extracted informations
virtual void clear() = 0;
// each sub-plugin will get a simulated flow (it gets an instruction and the state of the stack)
virtual void see(const Instruction_t* disasm, const StackEmulator* stack, const RegisterEmulator* regState) = 0;
// this methods process all gathered informations
virtual bool think() = 0;
void unknownOpCode(const DISASM* disasm);
};
};

View File

@ -0,0 +1,31 @@
#pragma once
#include "ICommand.h"
#include <list>
#include <set>
#include <map>
#include "meta.h"
#include "StackEmulator.h"
#include "RegisterEmulator.h"
namespace tr4ce
{
class IntermodularCalls : public ICommand
{
unsigned int numberOfCalls;
unsigned int numberOfApiCalls;
public:
IntermodularCalls(AnalysisRunner* parent);
void clear();
void see(const Instruction_t* disasm, const StackEmulator* stack, const RegisterEmulator* regState);
bool think();
void unknownOpCode(const DISASM* disasm);
};
};

View File

@ -0,0 +1,127 @@
#ifdef _WIN64
#include "IntermodularCalls.h"
#include "AnalysisRunner.h"
#include "../console.h"
namespace tr4ce
{
#define _isCall(disasm) ((disasm.Instruction.BranchType==CallType) && (disasm.Instruction.BranchType!=RetType) && !(disasm.Argument1.ArgType &REGISTER_TYPE))
IntermodularCalls::IntermodularCalls(AnalysisRunner* parent) : ICommand(parent)
{
}
void IntermodularCalls::clear()
{
numberOfApiCalls = 0;
numberOfCalls = 0;
}
void IntermodularCalls::see(const Instruction_t* currentInstruction, const StackEmulator* stackState, const RegisterEmulator* regState)
{
if((_isCall(currentInstruction->BeaStruct)))
{
// current instructions contains a call
// extract from "call 0x123" --> instruction at 0x123
// the opcode 0xFF "jmp" tells us that the current call is a call to a dll-function
numberOfCalls++;
// does the TitanEngine provides us a label?
char labelText[MAX_LABEL_SIZE];
bool hasLabel = DbgGetLabelAt(currentInstruction->BeaStruct.Instruction.AddrValue, SEG_DEFAULT, labelText);
if(hasLabel)
{
// we have a label from TitanEngine --> look up function header in database
FunctionInfo_t f = mParent->FunctionInformation()->find(labelText);
if(!f.invalid)
{
numberOfApiCalls++;
// yeah we know everything about the dll-call!
std::string functionComment;
functionComment = f.ReturnType + " " + f.Name + "(...)";
DbgSetAutoCommentAt(currentInstruction->BeaStruct.VirtualAddr, functionComment.c_str());
if(f.Arguments.size() > 0)
{
std::string ArgComment = f.arg(0).Type + " " + f.arg(0).Name;
DbgSetAutoCommentAt(regState->rcx(), ArgComment.c_str());
}
if(f.Arguments.size() > 1)
{
std::string ArgComment = f.arg(1).Type + " " + f.arg(1).Name;
DbgSetCommentAt(regState->rdx(), ArgComment.c_str());
}
if(f.Arguments.size() > 2)
{
std::string ArgComment = f.arg(2).Type + " " + f.arg(2).Name;
DbgSetAutoCommentAt(regState->r8(), ArgComment.c_str());
}
if(f.Arguments.size() > 3)
{
std::string ArgComment = f.arg(3).Type + " " + f.arg(3).Name;
DbgSetAutoCommentAt(regState->r9(), ArgComment.c_str());
}
if(f.Arguments.size() > 4)
{
// set comments for the arguments
for(auto i = 4; i < f.Arguments.size(); i++)
{
std::string ArgComment = f.arg(i).Type + " " + f.arg(i).Name;
uint commentAddr = stackState->lastAccessAtOffset(f.Arguments.size() - i - 1);
if(commentAddr != STACK_ERROR)
{
DbgSetAutoCommentAt(commentAddr, ArgComment.c_str());
}
else
{
// we have more arguments in the function descriptions than parameters on the stack
break;
}
}
}
}
}
}
}
bool IntermodularCalls::think()
{
StackEmulator stack;
dprintf("[StaticAnalysis:IntermodularCalls] found %i calls\n", numberOfCalls);
dprintf("[StaticAnalysis:IntermodularCalls] of which are %i intermodular calls\n", numberOfApiCalls);
return true;
}
void IntermodularCalls::unknownOpCode(const DISASM* disasm)
{
// current instruction wasn't correctly disassembled, so assuming worst-case
}
};
#endif // _WIN64
/* new calling convention in x64
1. arg -> RCX (floating point: XMM0)
2. arg -> RDX (floating point: XMM1)
3. arg -> R8 (floating point: XMM2)
4. arg -> R9 (floating point: XMM3)
additional arguments are pushed onto the stack (right to left)
*/

View File

@ -0,0 +1,103 @@
#ifndef _WIN64
#include "IntermodularCalls.h"
#include "AnalysisRunner.h"
#include "../console.h"
namespace tr4ce
{
#define _isCall(disasm) ((disasm.Instruction.Opcode == 0xE8) && (disasm.Instruction.BranchType) && (disasm.Instruction.BranchType!=RetType) && !(disasm.Argument1.ArgType &REGISTER_TYPE))
IntermodularCalls::IntermodularCalls(AnalysisRunner* parent) : ICommand(parent)
{
}
void IntermodularCalls::clear()
{
numberOfApiCalls = 0;
numberOfCalls = 0;
}
void IntermodularCalls::see(const Instruction_t* currentInstruction, const StackEmulator* stackState, const RegisterEmulator* regState)
{
if((currentInstruction->BeaStruct.Instruction.Opcode != 0xFF) && (_isCall(currentInstruction->BeaStruct)))
{
// current instructions contains a call
// extract from "call 0x123" --> instruction at 0x123
Instruction_t callTarget;
int len = mParent->instruction(currentInstruction->BeaStruct.Instruction.AddrValue, &callTarget);
if(len != UNKNOWN_OPCODE)
{
// call target was correctly disassembled before
if(callTarget.BeaStruct.Instruction.Opcode == 0xFF)
{
// the opcode 0xFF "jmp" tells us that the current call is a call to a dll-function
numberOfApiCalls++;
numberOfCalls++;
// does the TitanEngine provides us a label?
char labelText[MAX_LABEL_SIZE];
bool hasLabel = DbgGetLabelAt(callTarget.BeaStruct.Argument1.Memory.Displacement, SEG_DEFAULT, labelText);
if(hasLabel)
{
// we have a label from TitanEngine --> look up function header in database
FunctionInfo_t f = mParent->FunctionInformation()->find(labelText);
if(!f.invalid)
{
// yeah we know everything about the dll-call!
std::string functionComment;
functionComment = f.ReturnType + " " + f.Name + "(...)";
DbgSetAutoCommentAt(currentInstruction->BeaStruct.VirtualAddr, functionComment.c_str());
// set comments for the arguments
for(auto i = 0; i < f.Arguments.size(); i++)
{
std::string ArgComment = f.arg(i).Type + " " + f.arg(i).Name;
uint commentAddr = stackState->lastAccessAtOffset(f.Arguments.size() - i - 1);
if(commentAddr != STACK_ERROR)
{
DbgSetAutoCommentAt(commentAddr, ArgComment.c_str());
}
else
{
// we have more arguments in the function descriptions than parameters on the stack
break;
}
}
}
}
}
else
{
numberOfCalls++;
}
}
}
}
bool IntermodularCalls::think()
{
StackEmulator stack;
dprintf("[StaticAnalysis:IntermodularCalls] found %i calls\n", numberOfCalls);
dprintf("[StaticAnalysis:IntermodularCalls] of which are %i intermodular calls\n", numberOfApiCalls);
return true;
}
void IntermodularCalls::unknownOpCode(const DISASM* disasm)
{
// current instruction wasn't correctly disassembled, so assuming worst-case
}
};
#endif // _WIN64

126
x64_dbg_dbg/Analysis/Meta.h Normal file
View File

@ -0,0 +1,126 @@
#pragma once
#include "../BeaEngine/BeaEngine.h"
#include <vector>
#include <string>
#include <map>
#include <cstring>
namespace tr4ce
{
struct Call_t
{
UInt64 startAddress;
UInt64 endAddress;
Call_t(UInt64 a)
{
startAddress = a;
}
bool operator==(const Call_t & rhs) const
{
return static_cast<bool>(startAddress == rhs.startAddress);
}
bool operator<(const Call_t & rhs) const
{
return static_cast<bool>(startAddress < rhs.startAddress);
}
};
struct Instruction_t
{
DISASM BeaStruct;
unsigned int Length;
Instruction_t(DISASM* dis, unsigned int len)
{
BeaStruct = *dis;
Length = len;
}
Instruction_t()
{
Length = UNKNOWN_OPCODE;
}
};
struct ArgumentInfo_t
{
std::string Type;
std::string Name;
ArgumentInfo_t(std::string t, std::string n)
{
Type = t;
Name = n;
}
ArgumentInfo_t() {}
};
struct FunctionInfo_t
{
std::string DLLName;
std::string ReturnType;
std::string Name;
std::vector<ArgumentInfo_t> Arguments;
bool invalid;
FunctionInfo_t()
{
invalid = false;
}
FunctionInfo_t(std::string dll, std::string ret, std::string name, std::vector<ArgumentInfo_t> args)
{
DLLName = dll;
ReturnType = ret;
Name = name;
Arguments = args;
invalid = false;
}
bool operator==(const FunctionInfo_t & rhs) const
{
return static_cast<bool>((_strcmpi(Name.c_str(), rhs.Name.c_str()) < 0));
}
bool operator<(const FunctionInfo_t & rhs) const
{
return static_cast<bool>(_strcmpi(Name.c_str(), rhs.Name.c_str()));
}
ArgumentInfo_t arg(int i)
{
return Arguments.at(i);
}
};
template<typename T, typename D>
bool contains(std::map<T, D> s, T key)
{
std::map<T, D>::iterator it = s.find(key);
return (it != s.end());
}
#ifdef _WIN64
#define REGISTER_SIZE 8
#else
#define REGISTER_SIZE 4
#endif
};
/*namespace std{
template<typename T, typename D>
bool contains(std::map<T,D> s, T key)
{
std::map<T,D>::iterator it = s.find(key);
return (it != s.end());
}
}*/

View File

@ -0,0 +1,81 @@
#include "RegisterEmulator.h"
namespace tr4ce
{
#define _SAME(a,b) ((strcmp(a ,b) == 0) )
RegisterEmulator::RegisterEmulator()
{
}
RegisterEmulator::~RegisterEmulator()
{
}
void RegisterEmulator::emulate(const DISASM* BeaStruct)
{
if((BeaStruct->Argument1.AccessMode == WRITE) && ((BeaStruct->Argument1.ArgType & GENERAL_REG)))
{
// Unfortunately there is a bug in BeaEngine, so we cannot use
// "if (BeaStruct->Argument1.ArgType & REG?)"
if(_SAME(BeaStruct->Argument1.ArgMnemonic, "cl") ||
_SAME(BeaStruct->Argument1.ArgMnemonic, "ch") ||
_SAME(BeaStruct->Argument1.ArgMnemonic, "cx") ||
_SAME(BeaStruct->Argument1.ArgMnemonic, "ecx") ||
_SAME(BeaStruct->Argument1.ArgMnemonic, "rcx")
)
{
mRCX = BeaStruct->VirtualAddr;
}
else if(_SAME(BeaStruct->Argument1.ArgMnemonic, "dl") ||
_SAME(BeaStruct->Argument1.ArgMnemonic, "dh") ||
_SAME(BeaStruct->Argument1.ArgMnemonic, "dx") ||
_SAME(BeaStruct->Argument1.ArgMnemonic, "edx") ||
_SAME(BeaStruct->Argument1.ArgMnemonic, "rdx")
)
{
mRDX = BeaStruct->VirtualAddr;
}
else if(_SAME(BeaStruct->Argument1.ArgMnemonic, "r8") ||
_SAME(BeaStruct->Argument1.ArgMnemonic, "r8d") ||
_SAME(BeaStruct->Argument1.ArgMnemonic, "r8w") ||
_SAME(BeaStruct->Argument1.ArgMnemonic, "r8b")
)
{
mR8 = BeaStruct->VirtualAddr;
}
else if(_SAME(BeaStruct->Argument1.ArgMnemonic, "r9") ||
_SAME(BeaStruct->Argument1.ArgMnemonic, "r9d") ||
_SAME(BeaStruct->Argument1.ArgMnemonic, "r9w") ||
_SAME(BeaStruct->Argument1.ArgMnemonic, "r9b")
)
{
mR9 = BeaStruct->VirtualAddr;
}
}
}
const uint RegisterEmulator::rcx() const
{
return mRCX;
}
const uint RegisterEmulator::rdx() const
{
return mRDX;
}
const uint RegisterEmulator::r8() const
{
return mR8;
}
const uint RegisterEmulator::r9() const
{
return mR9;
}
};

View File

@ -0,0 +1,24 @@
#pragma once
#include "meta.h"
#include "../_global.h"
namespace tr4ce
{
class RegisterEmulator
{
UInt64 mRCX;
UInt64 mRDX;
UInt64 mR8;
UInt64 mR9;
public:
RegisterEmulator();
~RegisterEmulator();
void emulate(const DISASM* BeaStruct);
const uint rcx() const;
const uint rdx() const;
const uint r8() const;
const uint r9() const;
};
};

View File

@ -0,0 +1,153 @@
#include "StackEmulator.h"
namespace tr4ce
{
#define _isPush(disasm) ((strcmp((disasm)->Instruction.Mnemonic ,"push ") == 0) )
#define _isPop(disasm) ((strcmp((disasm)->Instruction.Mnemonic ,"pop ") == 0) )
#define _isSub(disasm) ((strcmp((disasm)->Instruction.Mnemonic ,"sub ") == 0) )
#define _isAdd(disasm) ((strcmp((disasm)->Instruction.Mnemonic ,"add ") == 0) )
StackEmulator::StackEmulator(void) : mStackpointer(0)
{
for(unsigned int i = 0; i < MAX_STACKSIZE; i++)
mStack[i] = STACK_ERROR;
}
StackEmulator::~StackEmulator(void)
{
}
/*
0x01234: mov [esp+C], eax
--> modifyFrom(0xC,0x01234)
*/
void StackEmulator::modifyFrom(int relative_offset, UInt64 addr)
{
const unsigned int internal_pointer = pointerByOffset(-1 * relative_offset);
mStack[internal_pointer] = addr;
}
/*
0x01234: pop eax
--> popFrom(0x01234), because it is:
mov eax, [esp]
add esp, -1 ; one to the past
*/
void StackEmulator::popFrom(UInt64 addr)
{
moveStackpointerBack(+1);
}
/*
0x01234: push eax
--> pushFrom(0x01234), because it is:
add esp, +1
mov [esp], eax ; one to the future
*/
void StackEmulator::pushFrom(UInt64 addr)
{
moveStackpointerBack(-1);
mStack[mStackpointer] = addr;
}
/*
0x01234: add esp, 0xA
--> moveStackpointer(0xA)
offset = offset to the past
*/
void StackEmulator::moveStackpointerBack(int offset)
{
mStackpointer = pointerByOffset(-offset);
}
unsigned int StackEmulator::pointerByOffset(int offset) const
{
return (mStackpointer + ((offset + MAX_STACKSIZE) % MAX_STACKSIZE) + MAX_STACKSIZE) % MAX_STACKSIZE;
}
/* returns addr from last access
0x155: mov [esp+8], eax
lastAccessAt(0x8) would be 0x155
*/
UInt64 StackEmulator::lastAccessAtOffset(int offset) const
{
int p = pointerByOffset(-offset);
return mStack[p];
}
void StackEmulator::emulate(const DISASM* BeaStruct)
{
/* track all events:
- sub/add esp
- mov [esp+x], ???
- push/pop
*/
//
const UInt64 addr = BeaStruct->VirtualAddr; // --> 00401301
if(_isPush(BeaStruct))
{
// "0x123 push eax" --> remember 0x123
pushFrom(addr);
}
else if(_isPop(BeaStruct))
{
// "0x125 pop ebp" --> remember 0x125
popFrom(addr);
}
else if(_isSub(BeaStruct))
{
if((strcmp(BeaStruct->Argument1.ArgMnemonic, "esp ") == 0))
{
// "sub esp, ???"
moveStackpointerBack(BeaStruct->Instruction.Immediat / REGISTER_SIZE);
}
}
else if(_isAdd(BeaStruct))
{
if((strcmp(BeaStruct->Argument1.ArgMnemonic, "esp ") == 0))
{
// "add esp, ???"
moveStackpointerBack(BeaStruct->Instruction.Immediat / -REGISTER_SIZE);
}
}
else
{
// "00401301: mov dword ptr ss:[esp+04h], 0040400Eh"
if((BeaStruct->Argument1.AccessMode == WRITE)
&& (BeaStruct->Argument1.ArgType & MEMORY_TYPE)
&& (BeaStruct->Argument1.Memory.BaseRegister & REG4)
&& (BeaStruct->Argument1.SegmentReg & SSReg)
)
{
int offset = BeaStruct->Argument1.Memory.Displacement; // --> 04h
uint addr = BeaStruct->VirtualAddr; // --> 00401301
modifyFrom(offset / REGISTER_SIZE, addr);
}
}
}
};

View File

@ -0,0 +1,43 @@
#pragma once
#include "Meta.h"
#include "../_global.h"
namespace tr4ce
{
/* since some compilers don't use "push" to change the stack for arguments (see MingGW)
we have to track all modifications to the stack for better analysis
therefore we emulate the stack by only storing the virtual address from the last write-access
-> advantage: we can track all modifications of the register for better placing of comments
*/
const unsigned int MAX_STACKSIZE = 50;
#define STACK_ERROR -1
class StackEmulator
{
UInt64 mStack[MAX_STACKSIZE];
unsigned int mStackpointer;
public:
StackEmulator(void);
~StackEmulator(void);
void pushFrom(UInt64 addr);
void popFrom(UInt64 addr);
void modifyFrom(int relative_offset, UInt64 addr);
void moveStackpointerBack(int offset);
unsigned int pointerByOffset(int offset) const;
UInt64 lastAccessAtOffset(int offset) const;
void emulate(const DISASM* disasm);
};
};

View File

@ -10,6 +10,8 @@
#include "plugin_loader.h"
#include "simplescript.h"
#include "symbolinfo.h"
#include "Analysis/AnalysisRunner.h"
#include "Analysis/ApiDB.h"
static bool bScyllaLoaded = false;
@ -995,6 +997,35 @@ CMDRESULT cbDebugBcDll(int argc, char* argv[])
dputs("dll breakpoint removed!");
return STATUS_CONTINUE;
}
CMDRESULT cbDebugAnalyse(int argc, char* argv[])
{
dputs("start analysis");
uint addr = GetContextData(UE_CIP);
if(argc > 1 and !valfromstring(argv[1], &addr))
{
dprintf("invalid address \"%s\"!\n", argv[1]);
return STATUS_ERROR;
}
dprintf("valid cip "fhex"!\n", addr);
uint size;
uint base = memfindbaseaddr(addr, &size);
if(!base)
{
dprintf("invalid address "fhex"!\n", addr);
return STATUS_ERROR;
}
dprintf("valid base "fhex"!\n", base);
dprintf("valid size "fhex"!\n", size);
tr4ce::ApiDB* db = new tr4ce::ApiDB();
tr4ce::AnalysisRunner AR(base, size);
AR.setFunctionInformation(db);
AR.start();
//GuiAnalyseCode(base, size);
return STATUS_CONTINUE;
}
CMDRESULT cbDebugSwitchthread(int argc, char* argv[])
{

View File

@ -55,5 +55,6 @@ CMDRESULT cbDebugDisableHardwareBreakpoint(int argc, char* argv[]);
CMDRESULT cbDebugEnableMemoryBreakpoint(int argc, char* argv[]);
CMDRESULT cbDebugDisableMemoryBreakpoint(int argc, char* argv[]);
CMDRESULT cbDebugDownloadSymbol(int argc, char* argv[]);
CMDRESULT cbDebugAnalyse(int argc, char* argv[]);
#endif //_DEBUGGER_COMMANDS_H

View File

@ -206,6 +206,7 @@ static void registercommands()
dbgcmdnew("getstr\1strget", cbInstrGetstr, false); //get a string variable
dbgcmdnew("copystr\1strcpy", cbInstrCopystr, true); //write a string variable to memory
dbgcmdnew("looplist", cbInstrLoopList, true); //list loops
dbgcmdnew("analyse\1analyze\1an", cbDebugAnalyse, true); //start analysis
}
static bool cbCommandProvider(char* cmd, int maxlen)

View File

@ -12,6 +12,15 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="addrinfo.cpp" />
<ClCompile Include="Analysis\AnalysisRunner.cpp" />
<ClCompile Include="Analysis\ApiDB.cpp" />
<ClCompile Include="Analysis\BeaInterpreter.cpp" />
<ClCompile Include="Analysis\FunctionDetector.cpp" />
<ClCompile Include="Analysis\ICommand.cpp" />
<ClCompile Include="Analysis\IntermodularCallsX64.cpp" />
<ClCompile Include="Analysis\IntermodularCallsX86.cpp" />
<ClCompile Include="Analysis\RegisterEmulator.cpp" />
<ClCompile Include="Analysis\StackEmulator.cpp" />
<ClCompile Include="argument.cpp" />
<ClCompile Include="assemble.cpp" />
<ClCompile Include="breakpoint.cpp" />
@ -45,6 +54,15 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="addrinfo.h" />
<ClInclude Include="Analysis\AnalysisRunner.h" />
<ClInclude Include="Analysis\ApiDB.h" />
<ClInclude Include="Analysis\BeaInterpreter.h" />
<ClInclude Include="Analysis\FunctionDetector.h" />
<ClInclude Include="Analysis\ICommand.h" />
<ClInclude Include="Analysis\IntermodularCalls.h" />
<ClInclude Include="Analysis\Meta.h" />
<ClInclude Include="Analysis\RegisterEmulator.h" />
<ClInclude Include="Analysis\StackEmulator.h" />
<ClInclude Include="argument.h" />
<ClInclude Include="assemble.h" />
<ClInclude Include="BeaEngine\basic_types.h" />
@ -145,7 +163,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;X64_DBG_EXPORTS;BUILD_DBG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;X64_DBG_EXPORTS;BUILD_DBG;_CRT_SECURE_NO_WARNINGS;_WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>

View File

@ -34,6 +34,12 @@
<Filter Include="Header Files\lz4">
<UniqueIdentifier>{6a8d58f0-1417-4bff-aecd-0f9f5e0641f9}</UniqueIdentifier>
</Filter>
<Filter Include="Analysis">
<UniqueIdentifier>{70e48ca5-7813-4da6-95c8-3717ace25093}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Analysis">
<UniqueIdentifier>{68e04c31-9f66-4c9a-ae1d-54b2f6cf2d1c}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="_exports.cpp">
@ -129,6 +135,33 @@
<ClCompile Include="debugger_commands.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Analysis\AnalysisRunner.cpp">
<Filter>Source Files\Analysis</Filter>
</ClCompile>
<ClCompile Include="Analysis\StackEmulator.cpp">
<Filter>Source Files\Analysis</Filter>
</ClCompile>
<ClCompile Include="Analysis\RegisterEmulator.cpp">
<Filter>Source Files\Analysis</Filter>
</ClCompile>
<ClCompile Include="Analysis\IntermodularCallsX64.cpp">
<Filter>Source Files\Analysis</Filter>
</ClCompile>
<ClCompile Include="Analysis\IntermodularCallsX86.cpp">
<Filter>Source Files\Analysis</Filter>
</ClCompile>
<ClCompile Include="Analysis\ICommand.cpp">
<Filter>Source Files\Analysis</Filter>
</ClCompile>
<ClCompile Include="Analysis\ApiDB.cpp">
<Filter>Source Files\Analysis</Filter>
</ClCompile>
<ClCompile Include="Analysis\FunctionDetector.cpp">
<Filter>Source Files\Analysis</Filter>
</ClCompile>
<ClCompile Include="Analysis\BeaInterpreter.cpp">
<Filter>Source Files\Analysis</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="_exports.h">
@ -275,5 +308,32 @@
<ClInclude Include="dynamicmem.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Analysis\Meta.h">
<Filter>Analysis</Filter>
</ClInclude>
<ClInclude Include="Analysis\AnalysisRunner.h">
<Filter>Analysis</Filter>
</ClInclude>
<ClInclude Include="Analysis\StackEmulator.h">
<Filter>Analysis</Filter>
</ClInclude>
<ClInclude Include="Analysis\RegisterEmulator.h">
<Filter>Analysis</Filter>
</ClInclude>
<ClInclude Include="Analysis\IntermodularCalls.h">
<Filter>Analysis</Filter>
</ClInclude>
<ClInclude Include="Analysis\ICommand.h">
<Filter>Analysis</Filter>
</ClInclude>
<ClInclude Include="Analysis\ApiDB.h">
<Filter>Analysis</Filter>
</ClInclude>
<ClInclude Include="Analysis\FunctionDetector.h">
<Filter>Analysis</Filter>
</ClInclude>
<ClInclude Include="Analysis\BeaInterpreter.h">
<Filter>Analysis</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -342,6 +342,11 @@ void Bridge::emitAutoCompleteClearAll()
emit autoCompleteClearAll();
}
void Bridge::emitAnalyseCode(int_t Base, int_t Size)
{
emit analyseCode(Base, Size);
}
void Bridge::emitUpdateSideBar()
{
emit updateSideBar();
@ -708,6 +713,11 @@ __declspec(dllexport) void* _gui_sendmessage(GUIMSG type, void* param1, void* pa
Bridge::getBridge()->emitAutoCompleteClearAll();
}
break;
case GUI_ANALYSE_CODE:
{
Bridge::getBridge()->emitAnalyseCode((int_t)param1, (int_t)param2);
}
break;
case GUI_ADD_MSG_TO_STATUSBAR:
{

View File

@ -75,6 +75,7 @@ public:
void emitAutoCompleteAddCmd(const QString cmd);
void emitAutoCompleteDelCmd(const QString cmd);
void emitAutoCompleteClearAll();
void emitAnalyseCode(int_t Base, int_t Size);
void emitAddMsgToStatusBar(QString msg);
void emitUpdateSideBar();
void emitRepaintTableView();
@ -143,13 +144,15 @@ signals:
void updatePatches();
void updateCallStack();
void symbolRefreshCurrent();
void analyseCode(int_t Base, int_t Size);
private:
QMutex* mBridgeMutex;
int_t bridgeResult;
bool hasBridgeResult;
public:
};