2 Commits
Author SHA1 Message Date
tr4ceflow a40b1f4508 Update README.md 2014-06-22 15:26:39 +02:00
tr4ceflow ceaa1d661c - prepare X64
- change method to detect "MingGW" stackmodifications
- Stackemulator
2014-06-22 14:23:11 +02:00
10 changed files with 276 additions and 51 deletions

No files matched your search

+15 -2
View File
@@ -1,6 +1,19 @@
x64dbg-StaticAnalysis
=====================
x64dbg-StaticAnalysis
- plugin for x64_dbg
- see [release page](https://github.com/x64dbg/x64dbg-StaticAnalysis/releases) for a working plugin
a plugin for www.x64dbg.com
##Roadmap
see [dev branch](https://github.com/x64dbg/x64dbg-StaticAnalysis/tree/enhance)
- x64 calling convention (x64 version)
- Stackemulator (for MingGM-argument-passing)
- function-finder
- case-switch detection
- "MB_OK"-like symbols
- loop-detection
- flow-heuristic against obfuscation instead of linear scanning
## License
GLPv3
+50 -6
View File
@@ -35,10 +35,32 @@ void AnalysisRunner::start(){
_plugin_logputs("[StaticAnalysis] analysis finished ...");
}
unsigned int AnalysisRunner::getInstruction(const duint bytesOffset,DISASM* disasm ){
memset(&disasm, 0, sizeof(disasm));
if(currentEIP + bytesOffset > mSize)
return UNKNOWN_OPCODE;
duint baseaddr = mBaseAddress;
duint size = mSize;
#ifdef _WIN64
disasm.Archi=64;
#endif // _WIN64
disasm->EIP=currentEIP + bytesOffset;
disasm->VirtualAddr=currentVirtualAddr + bytesOffset;
return (int) Disasm(disasm);
}
void AnalysisRunner::run()
{
unsigned char* data = new unsigned char[mSize];
if(!DbgMemRead(mBaseAddress, data, mSize))
EIPdata = new unsigned char[mSize];
if(!DbgMemRead(mBaseAddress, EIPdata, mSize))
{
//ERROR
_plugin_logputs("[StaticAnalysis] could not read memory ...");
@@ -55,24 +77,30 @@ void AnalysisRunner::run()
#ifdef _WIN64
disasm.Archi=64;
#endif // _WIN64
disasm.EIP=(UIntPtr)data;
disasm.VirtualAddr=(UInt64)baseaddr;
currentEIP = (UIntPtr)EIPdata;
disasm.EIP=currentEIP;
currentVirtualAddr = (UInt64)baseaddr;
disasm.VirtualAddr=currentVirtualAddr;
duint i=0;
while(i<size)
{
int len=Disasm(&disasm);
if(len!=UNKNOWN_OPCODE)
{
emulateStack(&disasm);
see(&disasm);
}
else{
unknownOpCode(&disasm);
len=1;
}
disasm.EIP+=len;
disasm.VirtualAddr+=len;
currentEIP += len;
currentVirtualAddr += len;
disasm.EIP=currentEIP;
disasm.VirtualAddr=currentVirtualAddr;
i+=len;
}
delete EIPdata;
}
void AnalysisRunner::clear( )
{
@@ -106,3 +134,19 @@ void AnalysisRunner::setDB( ApiDB *api )
{
mDb = api;
}
void AnalysisRunner::emulateStack( DISASM* disasm )
{
/* track all events:
- sub/add esp
- mov [esp+x], ???
- push/pop
- ret
*/
if ((disasm->Argument1.AccessMode == WRITE)
&& (disasm->Argument1.ArgType & MEMORY_TYPE)
&& (disasm->Argument1.Memory.BaseRegister & REG4)) {
}
}
+4
View File
@@ -28,6 +28,9 @@ class AnalysisRunner
S_IntermodularCalls *_Calls;
ApiDB *mDb;
unsigned char* EIPdata;
UIntPtr currentEIP;
UInt64 currentVirtualAddr;
public:
@@ -46,5 +49,6 @@ public:
void clear( );
void think( );
void initialise();
unsigned int getInstruction(const duint bytesOffset, DISASM* disasm );
};
+6 -1
View File
@@ -1,5 +1,5 @@
#include "ICommand.h"
#include "AnalysisRunner.h"
ICommand::ICommand(AnalysisRunner *parent) : mParent(parent)
{
@@ -15,3 +15,8 @@ void ICommand::initialise( const duint Base,const duint Size )
mBase = Base;
mSize = Size;
}
unsigned int ICommand::getInstruction( const duint bytesOffset, DISASM* disasm )
{
return mParent->getInstruction(bytesOffset,disasm);
}
+1
View File
@@ -19,6 +19,7 @@ protected:
duint mBase;
duint mSize;
AnalysisRunner *mParent;
unsigned int getInstruction(const duint bytesOffset, DISASM* disasm );
public:
+11
View File
@@ -0,0 +1,11 @@
#include "S_IntermodularCallsX64.h"
S_IntermodularCalls::S_IntermodularCalls(void)
{
}
S_IntermodularCalls::~S_IntermodularCalls(void)
{
}
+21
View File
@@ -0,0 +1,21 @@
#pragma once
/* calling convention
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)
*/
class S_IntermodularCalls
{
public:
S_IntermodularCalls(void);
~S_IntermodularCalls(void);
};
+52 -42
View File
@@ -6,6 +6,9 @@
#include "AnalysisRunner.h"
#include "meta.h"
/*#define _isPush(disasm) (QString(disasm->Instruction.Mnemonic).trimmed().toLower() == "push")
#define _isCall(disasm) ((disasm->Instruction.BranchType == CallType) && !(disasm->Argument1.ArgType &REGISTER_TYPE))))*/
#define _isPush(disasm) ((strcmp(disasm->Instruction.Mnemonic ,"push ") == 0) )
#define _isCall(disasm) ((disasm->Instruction.Opcode == 0xE8) && (disasm->Instruction.BranchType) && (disasm->Instruction.BranchType!=RetType) && !(disasm->Argument1.ArgType &REGISTER_TYPE))
@@ -33,24 +36,60 @@ void S_IntermodularCalls::see(const DISASM* disasm )
{
// no part of api-call
mCurrent.clear();
//_plugin_logprintf("[StaticAnalysis:IntermodularCalls] found 0xFF : %x and mne (%s)\n", disasm->Instruction.Opcode,disasm->Instruction.Mnemonic);
char labelText[MAX_LABEL_SIZE];
bool hasLabel = DbgGetLabelAt(disasm->Argument1.Memory.Displacement, SEG_DEFAULT, labelText);
if(hasLabel){
// api calls have 0xFF-jumps as target addresses, we need to remember them
_JumpFF j;
j.RVA = disasm->VirtualAddr;
j.RelativeAddressValue = disasm->Argument1.Memory.Displacement;
strcpy(j.label,labelText);
mApiJumps.insert(j);
}
}else {
if(_isCall(disasm))
{
// set missing informations
mCurrent.RVA = disasm->VirtualAddr ;
mCurrent.RelativeAddressValue = disasm->Instruction.AddrValue ;
// retrieve label from the instratuction at "addrValue"
DISASM target_instr;
int len = getInstruction(disasm->Instruction.AddrValue-disasm->VirtualAddr,&target_instr);
if(len != UNKNOWN_OPCODE){
// it is a jump to a dll
if(target_instr.Instruction.Opcode == 0xFF){
mCurrent.isApiCall = true;
// prepare gathering labelText
char labelText[MAX_LABEL_SIZE];
bool hasLabel = DbgGetLabelAt(target_instr.Argument1.Memory.Displacement, SEG_DEFAULT, labelText);
if(hasLabel){
// we have a label from TitanEngine --> look up function header in database
APIFunction f;
f = mParent->db()->find(labelText);
if(!f.invalid){
// found!
// yeah we know everything about the api-call!
std::string functionComment;
functionComment = f.ReturnType + " " + f.Name + "(...)";
DbgSetCommentAt(disasm->VirtualAddr ,functionComment.c_str());
int pos=0;
// set comments for the arguments
if( (f.Arguments.size() > 0) && (mCurrent.arguments.size() > 0) ){
for(std::list<duint>::reverse_iterator a= mCurrent.arguments.rbegin();a!=mCurrent.arguments.rend();a++)
{
// more push" instructions than arguments
if(pos >=f.Arguments.size())
break;
std::string ArgComment = f.arg(pos).Type + " "+ f.arg(pos).Name;
DbgSetCommentAt((*a) ,ArgComment.c_str());
pos++;
}
}
}
}
}
}
// copy structure
_Call t = mCurrent;
mCalls.push_back(t);
@@ -75,37 +114,8 @@ bool S_IntermodularCalls::think()
// iterate all calls
for (std::list<_Call>::iterator c = mCalls.begin();c != mCalls.end();c++)
{
bool is_api = false;
_JumpFF findJump;
findJump.RVA = c->RelativeAddressValue;
std::set<_JumpFF>::iterator j = mApiJumps.find(findJump);
if(j != mApiJumps.end()){
// having a label we can look in our database to get the arguments list
APIFunction f;
f = mParent->db()->find(j->label);
if(!f.invalid){
// yeah we know everything about the api-call!
std::string functionComment;
functionComment = f.ReturnType + " " + f.Name + "(...)";
DbgSetCommentAt(c->RVA ,functionComment.c_str());
int pos=0;
// set comments for the arguments
if( (f.Arguments.size() > 0) && (c->arguments.size() > 0) ){
for(std::list<duint>::reverse_iterator a= c->arguments.rbegin();a!=c->arguments.rend();a++)
{
// more push" instructions than arguments
if(pos >=f.Arguments.size())
break;
std::string ArgComment = f.arg(pos).Type + " "+ f.arg(pos).Name;
DbgSetCommentAt((*a) ,ArgComment.c_str());
pos++;
}
}
}
is_api = true;
if(c->isApiCall)
numberOfApiCalls++;
}
}
_plugin_logprintf("[StaticAnalysis:IntermodularCalls] of which are %i intermodular calls\n", numberOfApiCalls);
+78
View File
@@ -0,0 +1,78 @@
/* plugin: (StaticAnalysis) for x64dbg <http://www.x64dbg.com>
* author: [email protected] <http://blog.traceflow.com>
* license: GLPv3
*/
#include "StackEmulator.h"
StackEmulator::StackEmulator( void) : mStackpointer(0)
{
for (unsigned int i=0;i<MAX_STACKSIZE;i++)
mStack[i]=0;
}
StackEmulator::~StackEmulator(void)
{
}
/*
0x01234: mov [esp+C], eax
--> modifyFrom(0xC,0x01234)
*/
void StackEmulator::modifyFrom(int relative_offset,duint 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( duint addr )
{
moveStackpointer(+1);
}
/*
0x01234: push eax
--> pushFrom(0x01234), because it is:
add esp, +1
mov [esp], eax ; one to the future
*/
void StackEmulator::pushFrom( duint addr )
{
mStack[mStackpointer] = addr;
moveStackpointer(-1);
}
/*
0x01234: add esp, 0xA
--> moveStackpointer(0xA)
offset = offset to the past
*/
void StackEmulator::moveStackpointer( int offset )
{
mStackpointer = pointerByOffset(-offset);
}
unsigned int StackEmulator::pointerByOffset(int offset){
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
*/
duint StackEmulator::lastAccessAt(int offset){
int p = pointerByOffset(-offset);
return mStack[p];
}
+38
View File
@@ -0,0 +1,38 @@
/* plugin: (StaticAnalysis) for x64dbg <http://www.x64dbg.com>
* author: [email protected] <http://blog.traceflow.com>
* license: GLPv3
*/
#pragma once
#include "meta.h"
/* 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;
class StackEmulator
{
duint mStack[MAX_STACKSIZE];
unsigned int mStackpointer;
public:
StackEmulator(void);
~StackEmulator(void);
void pushFrom(duint addr);
void popFrom(duint addr);
void modifyFrom(int relative_offset,duint addr);
void moveStackpointer(int offset);
unsigned int pointerByOffset(int offset);
duint lastAccessAt(int offset);
};