diff --git a/x64_dbg_bridge/bridgemain.h b/x64_dbg_bridge/bridgemain.h index 862ba19b..824409e6 100644 --- a/x64_dbg_bridge/bridgemain.h +++ b/x64_dbg_bridge/bridgemain.h @@ -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 } diff --git a/x64_dbg_dbg/Analysis/AnalysisRunner.cpp b/x64_dbg_dbg/Analysis/AnalysisRunner.cpp new file mode 100644 index 00000000..41a0d4ed --- /dev/null +++ b/x64_dbg_dbg/Analysis/AnalysisRunner.cpp @@ -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(¤tInstruction.second, &Stack, &Register); + Stack.emulate(¤tInstruction.second.BeaStruct); + Register.emulate(¤tInstruction.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(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::const_iterator AnalysisRunner::instructionIter(UInt64 va) const +{ + return mInstructionsBuffer.find(va); +} +std::map::const_iterator AnalysisRunner::lastInstruction() const +{ + return mInstructionsBuffer.end(); +} + +duint AnalysisRunner::base() const +{ + return mBaseAddress; +} + +}; \ No newline at end of file diff --git a/x64_dbg_dbg/Analysis/AnalysisRunner.h b/x64_dbg_dbg/Analysis/AnalysisRunner.h new file mode 100644 index 00000000..400fe87f --- /dev/null +++ b/x64_dbg_dbg/Analysis/AnalysisRunner.h @@ -0,0 +1,64 @@ +#pragma once +#include "../_global.h" +#include +#include "Meta.h" +#include "StackEmulator.h" +#include "RegisterEmulator.h" +#include "ApiDB.h" + +namespace tr4ce +{ + +class IntermodularCalls; +class CallDetector; + + +class AnalysisRunner +{ + std::map 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::const_iterator instructionIter(UInt64 va) const; + std::map::const_iterator lastInstruction() const; + + duint base() const; + + +}; + +}; \ No newline at end of file diff --git a/x64_dbg_dbg/Analysis/ApiDB.cpp b/x64_dbg_dbg/Analysis/ApiDB.cpp new file mode 100644 index 00000000..5abc4464 --- /dev/null +++ b/x64_dbg_dbg/Analysis/ApiDB.cpp @@ -0,0 +1,130 @@ +/* plugin: (StaticAnalysis) for x64dbg + * author: tr4ceflow@gmail.com + * license: GLPv3 + */ +#include "../console.h" +#include "ApiDB.h" +#include "Meta.h" + +#include +#include +#include + +// http://stackoverflow.com/a/22923239/1392416 +std::vector 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 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 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::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; +} + + +}; \ No newline at end of file diff --git a/x64_dbg_dbg/Analysis/ApiDB.h b/x64_dbg_dbg/Analysis/ApiDB.h new file mode 100644 index 00000000..be362dbd --- /dev/null +++ b/x64_dbg_dbg/Analysis/ApiDB.h @@ -0,0 +1,24 @@ +#pragma once +#include "Meta.h" +#include + +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 mInfo; + + +}; + +}; \ No newline at end of file diff --git a/x64_dbg_dbg/Analysis/BeaInterpreter.cpp b/x64_dbg_dbg/Analysis/BeaInterpreter.cpp new file mode 100644 index 00000000..0842e659 --- /dev/null +++ b/x64_dbg_dbg/Analysis/BeaInterpreter.cpp @@ -0,0 +1,24 @@ +#include "BeaInterpreter.h" + + +namespace tr4ce +{ + + + + + +BeaInterpreter::BeaInterpreter(void) +{ + +} + + +BeaInterpreter::~BeaInterpreter(void) +{ +} + + + + +}; \ No newline at end of file diff --git a/x64_dbg_dbg/Analysis/BeaInterpreter.h b/x64_dbg_dbg/Analysis/BeaInterpreter.h new file mode 100644 index 00000000..2caefb31 --- /dev/null +++ b/x64_dbg_dbg/Analysis/BeaInterpreter.h @@ -0,0 +1,24 @@ +#pragma once +#include "Meta.h" +#include "../BeaEngine/BeaEngine.h" + +namespace tr4ce +{ + + + +class BeaInterpreter +{ + + +public: + BeaInterpreter(void); + ~BeaInterpreter(void); + + + +}; + + + +}; \ No newline at end of file diff --git a/x64_dbg_dbg/Analysis/FunctionDetector.cpp b/x64_dbg_dbg/Analysis/FunctionDetector.cpp new file mode 100644 index 00000000..2b522cf2 --- /dev/null +++ b/x64_dbg_dbg/Analysis/FunctionDetector.cpp @@ -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 ®ISTER_TYPE)) +#else +#define _isCall(disasm) ((disasm.Instruction.BranchType==CallType) && (disasm.Instruction.BranchType!=RetType) && !(disasm.Argument1.ArgType ®ISTER_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::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 + + } + + + }; + + diff --git a/x64_dbg_dbg/Analysis/FunctionDetector.h b/x64_dbg_dbg/Analysis/FunctionDetector.h new file mode 100644 index 00000000..4eeeec5c --- /dev/null +++ b/x64_dbg_dbg/Analysis/FunctionDetector.h @@ -0,0 +1,32 @@ +#pragma once + +#include "ICommand.h" +#include +#include +#include + +#include "Meta.h" +#include "StackEmulator.h" +#include "RegisterEmulator.h" + +namespace tr4ce +{ + + class CallDetector : public ICommand + { + + + unsigned int numberOfFunctions; + std::set 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); + }; + +}; \ No newline at end of file diff --git a/x64_dbg_dbg/Analysis/ICommand.cpp b/x64_dbg_dbg/Analysis/ICommand.cpp new file mode 100644 index 00000000..e5552edf --- /dev/null +++ b/x64_dbg_dbg/Analysis/ICommand.cpp @@ -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; +} + + + +}; \ No newline at end of file diff --git a/x64_dbg_dbg/Analysis/ICommand.h b/x64_dbg_dbg/Analysis/ICommand.h new file mode 100644 index 00000000..c471cd50 --- /dev/null +++ b/x64_dbg_dbg/Analysis/ICommand.h @@ -0,0 +1,44 @@ +/* plugin: (StaticAnalysis) for x64dbg + * author: tr4ceflow@gmail.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); +}; + +}; \ No newline at end of file diff --git a/x64_dbg_dbg/Analysis/IntermodularCalls.h b/x64_dbg_dbg/Analysis/IntermodularCalls.h new file mode 100644 index 00000000..c3a8c2a1 --- /dev/null +++ b/x64_dbg_dbg/Analysis/IntermodularCalls.h @@ -0,0 +1,31 @@ +#pragma once +#include "ICommand.h" +#include +#include +#include + +#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); +}; + +}; \ No newline at end of file diff --git a/x64_dbg_dbg/Analysis/IntermodularCallsX64.cpp b/x64_dbg_dbg/Analysis/IntermodularCallsX64.cpp new file mode 100644 index 00000000..ef5dc035 --- /dev/null +++ b/x64_dbg_dbg/Analysis/IntermodularCallsX64.cpp @@ -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 ®ISTER_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) + +*/ \ No newline at end of file diff --git a/x64_dbg_dbg/Analysis/IntermodularCallsX86.cpp b/x64_dbg_dbg/Analysis/IntermodularCallsX86.cpp new file mode 100644 index 00000000..34069472 --- /dev/null +++ b/x64_dbg_dbg/Analysis/IntermodularCallsX86.cpp @@ -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 ®ISTER_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 \ No newline at end of file diff --git a/x64_dbg_dbg/Analysis/Meta.h b/x64_dbg_dbg/Analysis/Meta.h new file mode 100644 index 00000000..7e7fe899 --- /dev/null +++ b/x64_dbg_dbg/Analysis/Meta.h @@ -0,0 +1,126 @@ +#pragma once +#include "../BeaEngine/BeaEngine.h" +#include +#include +#include +#include + +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(startAddress == rhs.startAddress); + } + bool operator<(const Call_t & rhs) const + { + return static_cast(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 Arguments; + bool invalid; + + FunctionInfo_t() + { + invalid = false; + } + + FunctionInfo_t(std::string dll, std::string ret, std::string name, std::vector args) + { + DLLName = dll; + ReturnType = ret; + Name = name; + Arguments = args; + invalid = false; + } + + bool operator==(const FunctionInfo_t & rhs) const + { + return static_cast((_strcmpi(Name.c_str(), rhs.Name.c_str()) < 0)); + } + bool operator<(const FunctionInfo_t & rhs) const + { + return static_cast(_strcmpi(Name.c_str(), rhs.Name.c_str())); + } + + ArgumentInfo_t arg(int i) + { + return Arguments.at(i); + } + + + }; + + template + bool contains(std::map s, T key) + { + std::map::iterator it = s.find(key); + return (it != s.end()); + } + + + +#ifdef _WIN64 +#define REGISTER_SIZE 8 +#else +#define REGISTER_SIZE 4 +#endif + +}; + +/*namespace std{ + +template +bool contains(std::map s, T key) +{ +std::map::iterator it = s.find(key); +return (it != s.end()); +} +}*/ \ No newline at end of file diff --git a/x64_dbg_dbg/Analysis/RegisterEmulator.cpp b/x64_dbg_dbg/Analysis/RegisterEmulator.cpp new file mode 100644 index 00000000..968190ee --- /dev/null +++ b/x64_dbg_dbg/Analysis/RegisterEmulator.cpp @@ -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; +} + + +}; \ No newline at end of file diff --git a/x64_dbg_dbg/Analysis/RegisterEmulator.h b/x64_dbg_dbg/Analysis/RegisterEmulator.h new file mode 100644 index 00000000..1bf58aed --- /dev/null +++ b/x64_dbg_dbg/Analysis/RegisterEmulator.h @@ -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; +}; + +}; \ No newline at end of file diff --git a/x64_dbg_dbg/Analysis/StackEmulator.cpp b/x64_dbg_dbg/Analysis/StackEmulator.cpp new file mode 100644 index 00000000..1261057d --- /dev/null +++ b/x64_dbg_dbg/Analysis/StackEmulator.cpp @@ -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); + + + + } + } + + + +} + +}; \ No newline at end of file diff --git a/x64_dbg_dbg/Analysis/StackEmulator.h b/x64_dbg_dbg/Analysis/StackEmulator.h new file mode 100644 index 00000000..e9434402 --- /dev/null +++ b/x64_dbg_dbg/Analysis/StackEmulator.h @@ -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); +}; + + + +}; \ No newline at end of file diff --git a/x64_dbg_dbg/debugger_commands.cpp b/x64_dbg_dbg/debugger_commands.cpp index ad2271ab..a9eb4269 100644 --- a/x64_dbg_dbg/debugger_commands.cpp +++ b/x64_dbg_dbg/debugger_commands.cpp @@ -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[]) { diff --git a/x64_dbg_dbg/debugger_commands.h b/x64_dbg_dbg/debugger_commands.h index e5a6aac4..7ec20cc4 100644 --- a/x64_dbg_dbg/debugger_commands.h +++ b/x64_dbg_dbg/debugger_commands.h @@ -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 \ No newline at end of file diff --git a/x64_dbg_dbg/x64_dbg.cpp b/x64_dbg_dbg/x64_dbg.cpp index 63852d80..ec3f287e 100644 --- a/x64_dbg_dbg/x64_dbg.cpp +++ b/x64_dbg_dbg/x64_dbg.cpp @@ -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) diff --git a/x64_dbg_dbg/x64_dbg_dbg.vcxproj b/x64_dbg_dbg/x64_dbg_dbg.vcxproj index 31976d9a..ff2549e2 100644 --- a/x64_dbg_dbg/x64_dbg_dbg.vcxproj +++ b/x64_dbg_dbg/x64_dbg_dbg.vcxproj @@ -12,6 +12,15 @@ + + + + + + + + + @@ -45,6 +54,15 @@ + + + + + + + + + @@ -145,7 +163,7 @@ - WIN32;NDEBUG;_WINDOWS;_USRDLL;X64_DBG_EXPORTS;BUILD_DBG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + WIN32;NDEBUG;_WINDOWS;_USRDLL;X64_DBG_EXPORTS;BUILD_DBG;_CRT_SECURE_NO_WARNINGS;_WIN64;%(PreprocessorDefinitions) MultiThreadedDLL Level3 ProgramDatabase diff --git a/x64_dbg_dbg/x64_dbg_dbg.vcxproj.filters b/x64_dbg_dbg/x64_dbg_dbg.vcxproj.filters index 1bd11186..fafef8f7 100644 --- a/x64_dbg_dbg/x64_dbg_dbg.vcxproj.filters +++ b/x64_dbg_dbg/x64_dbg_dbg.vcxproj.filters @@ -34,6 +34,12 @@ {6a8d58f0-1417-4bff-aecd-0f9f5e0641f9} + + {70e48ca5-7813-4da6-95c8-3717ace25093} + + + {68e04c31-9f66-4c9a-ae1d-54b2f6cf2d1c} + @@ -129,6 +135,33 @@ Source Files + + Source Files\Analysis + + + Source Files\Analysis + + + Source Files\Analysis + + + Source Files\Analysis + + + Source Files\Analysis + + + Source Files\Analysis + + + Source Files\Analysis + + + Source Files\Analysis + + + Source Files\Analysis + @@ -275,5 +308,32 @@ Header Files + + Analysis + + + Analysis + + + Analysis + + + Analysis + + + Analysis + + + Analysis + + + Analysis + + + Analysis + + + Analysis + \ No newline at end of file diff --git a/x64_dbg_gui/Project/Src/Bridge/Bridge.cpp b/x64_dbg_gui/Project/Src/Bridge/Bridge.cpp index 4c3a3cd2..ef49e20d 100644 --- a/x64_dbg_gui/Project/Src/Bridge/Bridge.cpp +++ b/x64_dbg_gui/Project/Src/Bridge/Bridge.cpp @@ -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: { diff --git a/x64_dbg_gui/Project/Src/Bridge/Bridge.h b/x64_dbg_gui/Project/Src/Bridge/Bridge.h index 1f671422..a310d1eb 100644 --- a/x64_dbg_gui/Project/Src/Bridge/Bridge.h +++ b/x64_dbg_gui/Project/Src/Bridge/Bridge.h @@ -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: + + };