1
0
Fork 0

Trace recording (#1736)

* run trace file format
* record opcode
* Successfully recorded sample run trace
* fixed order of thread id and opcode
* use capstone in run trace
* Revised format;Stop tracing when stop debug;Changed ext name
* trace browser(draft)
* Lower bound
* Lower bound
* implemented more funcitons in trace file reader
* Initial trace browser
* trace browser works for single-page traces
* fixed page fault
* Multi-selection, fixed page faults
* copy disassembly
* resize columns
* address label;follow in disassembly
* highlight
* history,comment,breakpoint in trace view
* stricter validation to prevent buffer overflow
* MAX_MEMORY_OPERANDS=32
* fixing bug in memory access count
* Temporary info menu to view registers & memory
* assumed to fix thread id bug
* live update trace view
* Fixed a bug with registers recording (similar to thread id bug)
* Search for constant in trace file
* Fixed bugs with memory operands recording
* File header for trace file; Auto update trace viewer
* fix x64dbg_translations.pro
* Default filename for trace; Start trace from trace view
* Switch to Qt JSON
* Copy selection, file offset and RVA; recent files
* Properly implement MRU menu
* shortcut for tracing
* Fix file names with comma
* added interaction with tab closing
* change default directory for trace file
* fix minor issue
This commit is contained in:
Torusrxxx 2017-10-16 18:00:26 +00:00 committed by Duncan Ogilvie
parent 9959278863
commit 390bf4c5ca
42 changed files with 2890 additions and 45 deletions

View File

@ -1648,6 +1648,18 @@ BRIDGE_IMPEXP void GuiReferenceAddCommand(const char* title, const char* command
_gui_sendmessage(GUI_REF_ADDCOMMAND, (void*)title, (void*)command);
}
BRIDGE_IMPEXP void GuiUpdateTraceBrowser()
{
CHECK_GUI_UPDATE_DISABLED
_gui_sendmessage(GUI_UPDATE_TRACE_BROWSER, nullptr, nullptr);
}
BRIDGE_IMPEXP void GuiOpenTraceFile(const char* fileName)
{
CHECK_GUI_UPDATE_DISABLED
_gui_sendmessage(GUI_OPEN_TRACE_FILE, (void*)fileName, nullptr);
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
hInst = hinstDLL;

View File

@ -1130,7 +1130,9 @@ typedef enum
GUI_REF_SEARCH_GETROWCOUNT, // param1=unused, param2=unused
GUI_REF_SEARCH_GETCELLCONTENT, // param1=int row, param2=int col
GUI_MENU_REMOVE, // param1=int hEntryMenu, param2=unused
GUI_REF_ADDCOMMAND // param1=const char* title, param2=const char* command
GUI_REF_ADDCOMMAND, // param1=const char* title, param2=const char* command
GUI_OPEN_TRACE_FILE, // param1=const char* file name,param2=unused
GUI_UPDATE_TRACE_BROWSER // param1=unused, param2=unused
} GUIMSG;
//GUI Typedefs
@ -1305,6 +1307,8 @@ BRIDGE_IMPEXP void GuiUpdateTypeWidget();
BRIDGE_IMPEXP void GuiCloseApplication();
BRIDGE_IMPEXP void GuiFlushLog();
BRIDGE_IMPEXP void GuiReferenceAddCommand(const char* title, const char* command);
BRIDGE_IMPEXP void GuiUpdateTraceBrowser();
BRIDGE_IMPEXP void GuiOpenTraceFile(const char* fileName);
#ifdef __cplusplus
}

View File

@ -1,9 +1,15 @@
#include "TraceRecord.h"
#include "zydis_wrapper.h"
#include "capstone_wrapper.h"
#include "module.h"
#include "memory.h"
#include "threading.h"
#include "thread.h"
#include "disasm_helper.h"
#include "disasm_fast.h"
#include "plugin_loader.h"
#include "value.h"
#define MAX_INSTRUCTIONS_TRACED_FULL_REG_DUMP 512
TraceRecordManager TraceRecord;
@ -193,6 +199,230 @@ void TraceRecordManager::TraceExecute(duint address, duint size)
}
}
static void HandleCapstoneOperand(const Capstone & cp, int opindex, DISASM_ARGTYPE* argType, duint* value, unsigned char* memoryContent, unsigned char* memorySize)
{
*value = cp.ResolveOpValue(opindex, [&cp](x86_reg reg)
{
auto regName = cp.RegName(reg);
return regName ? getregister(nullptr, regName) : 0; //TODO: temporary needs enums + caching
});
const auto & op = cp[opindex];
switch(op.type)
{
case X86_OP_REG:
*argType = arg_normal;
break;
case X86_OP_IMM:
*argType = arg_normal;
break;
case X86_OP_MEM:
{
*argType = arg_memory;
const x86_op_mem & mem = op.mem;
#ifdef _WIN64
if(mem.segment == X86_REG_GS)
#else //x86
if(mem.segment == X86_REG_FS)
#endif
{
*value += ThreadGetLocalBase(ThreadGetId(hActiveThread));
}
*memorySize = op.size;
if(DbgMemIsValidReadPtr(*value))
{
MemRead(*value, memoryContent, max(op.size, sizeof(duint)));
}
}
break;
default:
__debugbreak();
}
}
void TraceRecordManager::TraceExecuteRecord(const Capstone & newInstruction)
{
if(!isRunTraceEnabled())
return;
unsigned char WriteBuffer[3072];
unsigned char* WriteBufferPtr = WriteBuffer;
//Get current data
REGDUMPWORD newContext;
//DISASM_INSTR newInstruction;
DWORD newThreadId;
duint newMemory[32];
duint newMemoryAddress[32];
duint oldMemory[32];
unsigned char newMemoryArrayCount = 0;
DbgGetRegDump(&newContext.registers);
newThreadId = ThreadGetId(hActiveThread);
// Don't try to resolve memory values for lea and nop instructions
if(!(newInstruction.IsNop() || newInstruction.GetId() == X86_INS_LEA))
{
DISASM_ARGTYPE argType;
duint value;
unsigned char memoryContent[128];
unsigned char memorySize;
for(int i = 0; i < newInstruction.OpCount(); i++)
{
memset(memoryContent, 0, sizeof(memoryContent));
HandleCapstoneOperand(newInstruction, i, &argType, &value, memoryContent, &memorySize);
// TODO: Implicit memory access by push and pop instructions
// TODO: Support memory value of ??? for invalid memory access
if(argType == arg_memory)
{
if(memorySize <= sizeof(duint))
{
memcpy(&newMemory[newMemoryArrayCount], memoryContent, sizeof(duint));
newMemoryAddress[newMemoryArrayCount] = value;
newMemoryArrayCount++;
}
else
for(unsigned char index = 0; index < memorySize / sizeof(duint) + ((memorySize % sizeof(duint)) > 0 ? 1 : 0); index++)
{
memcpy(&newMemory[newMemoryArrayCount], memoryContent + sizeof(duint) * index, sizeof(duint));
newMemoryAddress[newMemoryArrayCount] = value + sizeof(duint) * index;
newMemoryArrayCount++;
}
}
}
if(newInstruction.GetId() == X86_INS_PUSH || newInstruction.GetId() == X86_INS_PUSHF || newInstruction.GetId() == X86_INS_PUSHFD
|| newInstruction.GetId() == X86_INS_PUSHFQ || newInstruction.GetId() == X86_INS_CALL //TODO: far call accesses 2 stack entries
)
{
MemRead(newContext.registers.regcontext.csp - sizeof(duint), &newMemory[newMemoryArrayCount], sizeof(duint));
newMemoryAddress[newMemoryArrayCount] = newContext.registers.regcontext.csp - sizeof(duint);
newMemoryArrayCount++;
}
else if(newInstruction.GetId() == X86_INS_POP || newInstruction.GetId() == X86_INS_POPF || newInstruction.GetId() == X86_INS_POPFD
|| newInstruction.GetId() == X86_INS_POPFQ || newInstruction.GetId() == X86_INS_RET)
{
MemRead(newContext.registers.regcontext.csp, &newMemory[newMemoryArrayCount], sizeof(duint));
newMemoryAddress[newMemoryArrayCount] = newContext.registers.regcontext.csp;
newMemoryArrayCount++;
}
//TODO: PUSHAD/POPAD
assert(newMemoryArrayCount < 32);
}
if(rtPrevInstAvailable)
{
for(unsigned char i = 0; i < rtOldMemoryArrayCount; i++)
{
MemRead(rtOldMemoryAddress[i], oldMemory + i, sizeof(duint));
}
//Delta compress registers
//Data layout is Structure of Arrays to gather the same type of data in continuous memory to improve RLE compression performance.
//1byte:block type,1byte:reg changed count,1byte:memory accessed count,1byte:flags,4byte/none:threadid,string:opcode,1byte[]:position,ptrbyte[]:regvalue,1byte[]:flags,ptrbyte[]:address,ptrbyte[]:oldmem,ptrbyte[]:newmem
//Always record state of LAST INSTRUCTION! (NOT current instruction)
unsigned char changed = 0;
for(unsigned char i = 0; i < _countof(rtOldContext.regword); i++)
{
//rtRecordedInstructions - 1 hack: always record full registers dump at first instruction (recorded at 2nd instruction execution time)
//prints ASCII table in run trace at first instruction :)
if(rtOldContext.regword[i] != newContext.regword[i] || rtOldContextChanged[i] || ((rtRecordedInstructions - 1) % MAX_INSTRUCTIONS_TRACED_FULL_REG_DUMP == 0))
changed++;
}
unsigned char blockFlags = 0;
if(newThreadId != rtOldThreadId || ((rtRecordedInstructions - 1) % MAX_INSTRUCTIONS_TRACED_FULL_REG_DUMP == 0))
blockFlags = 0x80;
blockFlags |= rtOldOpcodeSize;
WriteBufferPtr[0] = 0; //1byte: block type
WriteBufferPtr[1] = changed; //1byte: registers changed
WriteBufferPtr[2] = rtOldMemoryArrayCount; //1byte: memory accesses count
WriteBufferPtr[3] = blockFlags; //1byte: flags and opcode size
WriteBufferPtr += 4;
if(newThreadId != rtOldThreadId || rtNeedThreadId || ((rtRecordedInstructions - 1) % MAX_INSTRUCTIONS_TRACED_FULL_REG_DUMP == 0))
{
memcpy(WriteBufferPtr, &rtOldThreadId, sizeof(rtOldThreadId));
WriteBufferPtr += sizeof(rtOldThreadId);
rtNeedThreadId = (newThreadId != rtOldThreadId);
}
memcpy(WriteBufferPtr, rtOldOpcode, rtOldOpcodeSize);
WriteBufferPtr += rtOldOpcodeSize;
int lastChangedPosition = -1; //-1
for(int i = 0; i < _countof(rtOldContext.regword); i++) //1byte: position
{
if(rtOldContext.regword[i] != newContext.regword[i] || rtOldContextChanged[i] || ((rtRecordedInstructions - 1) % MAX_INSTRUCTIONS_TRACED_FULL_REG_DUMP == 0))
{
WriteBufferPtr[0] = (unsigned char)(i - lastChangedPosition - 1);
WriteBufferPtr++;
lastChangedPosition = i;
}
}
for(unsigned char i = 0; i < _countof(rtOldContext.regword); i++) //ptrbyte: newvalue
{
if(rtOldContext.regword[i] != newContext.regword[i] || rtOldContextChanged[i] || ((rtRecordedInstructions - 1) % MAX_INSTRUCTIONS_TRACED_FULL_REG_DUMP == 0))
{
memcpy(WriteBufferPtr, &rtOldContext.regword[i], sizeof(duint));
WriteBufferPtr += sizeof(duint);
}
rtOldContextChanged[i] = (rtOldContext.regword[i] != newContext.regword[i]);
}
for(unsigned char i = 0; i < rtOldMemoryArrayCount; i++) //1byte: flags
{
unsigned char memoryOperandFlags = 0;
if(rtOldMemory[i] == oldMemory[i]) //bit 0: memory is unchanged, no new memory is saved
memoryOperandFlags |= 1;
//proposed flags: is memory valid, is memory zero
WriteBufferPtr[0] = memoryOperandFlags;
WriteBufferPtr += 1;
}
for(unsigned char i = 0; i < rtOldMemoryArrayCount; i++) //ptrbyte: address
{
memcpy(WriteBufferPtr, &rtOldMemoryAddress[i], sizeof(duint));
WriteBufferPtr += sizeof(duint);
}
for(unsigned char i = 0; i < rtOldMemoryArrayCount; i++) //ptrbyte: old content
{
memcpy(WriteBufferPtr, &rtOldMemory[i], sizeof(duint));
WriteBufferPtr += sizeof(duint);
}
for(unsigned char i = 0; i < rtOldMemoryArrayCount; i++) //ptrbyte: new content
{
if(rtOldMemory[i] != oldMemory[i])
{
memcpy(WriteBufferPtr, &oldMemory[i], sizeof(duint));
WriteBufferPtr += sizeof(duint);
}
}
}
//Switch context buffers
rtOldThreadId = newThreadId;
rtOldContext = newContext;
rtOldMemoryArrayCount = newMemoryArrayCount;
memcpy(rtOldMemory, newMemory, sizeof(newMemory));
memcpy(rtOldMemoryAddress, newMemoryAddress, sizeof(newMemoryAddress));
memset(rtOldOpcode, 0, 16);
rtOldOpcodeSize = newInstruction.Size() & 0x0F;
MemRead(newContext.registers.regcontext.cip, rtOldOpcode, rtOldOpcodeSize);
//Write to file
if(rtPrevInstAvailable)
{
if(WriteBufferPtr - WriteBuffer <= sizeof(WriteBuffer))
{
DWORD written;
WriteFile(rtFile, WriteBuffer, WriteBufferPtr - WriteBuffer, &written, NULL);
if(written < DWORD(WriteBufferPtr - WriteBuffer)) //Disk full?
{
CloseHandle(rtFile);
dprintf(QT_TRANSLATE_NOOP("DBG", "Run trace has stopped unexpectedly because WriteFile() failed. GetLastError()= %X .\r\n"), GetLastError());
rtEnabled = false;
}
}
else
__debugbreak(); // Buffer overrun?
}
rtPrevInstAvailable = true;
rtRecordedInstructions++;
dbgtracebrowserneedsupdate();
}
unsigned int TraceRecordManager::getHitCount(duint address)
{
SHARED_ACQUIRE(LockTraceRecord);
@ -247,6 +477,101 @@ void TraceRecordManager::increaseInstructionCounter()
InterlockedIncrement((volatile long*)&instructionCounter);
}
bool TraceRecordManager::enableRunTrace(bool enabled, const char* fileName)
{
if(!DbgIsDebugging())
return false;
if(enabled)
{
if(rtEnabled)
enableRunTrace(false, NULL); //re-enable run trace
rtFile = CreateFileW(StringUtils::Utf8ToUtf16(fileName).c_str(), FILE_APPEND_DATA, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if(rtFile != INVALID_HANDLE_VALUE)
{
LARGE_INTEGER size;
if(GetFileSizeEx(rtFile, &size))
{
if(size.QuadPart != 0)
{
SetFilePointer(rtFile, 0, 0, FILE_END);
}
else //file is empty, write some file header
{
//TRAC, SIZE, JSON header
json_t* root = json_object();
json_object_set_new(root, "ver", json_integer(1));
json_object_set_new(root, "arch", json_string(ArchValue("x86", "x64")));
json_object_set_new(root, "hashAlgorithm", json_string("murmurhash"));
json_object_set_new(root, "hash", json_hex(dbgfunctionsget()->DbGetHash()));
json_object_set_new(root, "compression", json_string(""));
char path[MAX_PATH];
ModPathFromAddr(dbgdebuggedbase(), path, MAX_PATH);
json_object_set_new(root, "path", json_string(path));
char* headerinfo;
headerinfo = json_dumps(root, JSON_COMPACT);
size_t headerinfosize = strlen(headerinfo);
LARGE_INTEGER header;
DWORD written;
header.LowPart = MAKEFOURCC('T', 'R', 'A', 'C');
header.HighPart = headerinfosize;
WriteFile(rtFile, &header, 8, &written, nullptr);
if(written < 8) //read-only?
{
CloseHandle(rtFile);
json_free(headerinfo);
json_decref(root);
dputs(QT_TRANSLATE_NOOP("DBG", "Run trace failed to start because file header cannot be written."));
return false;
}
WriteFile(rtFile, headerinfo, headerinfosize, &written, nullptr);
json_free(headerinfo);
json_decref(root);
if(written < headerinfosize) //disk-full?
{
CloseHandle(rtFile);
dputs(QT_TRANSLATE_NOOP("DBG", "Run trace failed to start because file header cannot be written."));
return false;
}
}
}
rtPrevInstAvailable = false;
rtEnabled = true;
rtRecordedInstructions = 0;
rtNeedThreadId = true;
for(size_t i = 0; i < _countof(rtOldContextChanged); i++)
rtOldContextChanged[i] = true;
dprintf(QT_TRANSLATE_NOOP("DBG", "Run trace started. File: %s\r\n"), fileName);
REGDUMP cip;
Capstone cp;
unsigned char instr[MAX_DISASM_BUFFER];
DbgGetRegDump(&cip);
if(MemRead(cip.regcontext.cip, instr, MAX_DISASM_BUFFER))
{
cp.DisassembleSafe(cip.regcontext.cip, instr, MAX_DISASM_BUFFER);
TraceExecuteRecord(cp);
}
GuiOpenTraceFile(fileName);
return true;
}
else
{
dprintf(QT_TRANSLATE_NOOP("DBG", "Cannot create run trace file. GetLastError()= %X .\r\n"), GetLastError());
return false;
}
}
else
{
if(rtEnabled)
{
CloseHandle(rtFile);
rtPrevInstAvailable = false;
rtEnabled = false;
dputs(QT_TRANSLATE_NOOP("DBG", "Run trace stopped."));
}
return true;
}
}
void TraceRecordManager::saveToDb(JSON root)
{
EXCLUSIVE_ACQUIRE(LockTraceRecord);
@ -366,25 +691,45 @@ unsigned int TraceRecordManager::getModuleIndex(const String & moduleName)
}
}
bool TraceRecordManager::isRunTraceEnabled()
{
return rtEnabled;
}
void _dbg_dbgtraceexecute(duint CIP)
{
if(TraceRecord.getTraceRecordType(CIP) != TraceRecordManager::TraceRecordType::TraceRecordNone)
{
unsigned char buffer[MAX_DISASM_BUFFER];
if(MemRead(CIP, buffer, MAX_DISASM_BUFFER))
Capstone instruction;
unsigned char data[MAX_DISASM_BUFFER];
if(MemRead(CIP, data, MAX_DISASM_BUFFER))
{
TraceRecord.increaseInstructionCounter();
Zydis instruction;
instruction.Disassemble(CIP, buffer, MAX_DISASM_BUFFER);
TraceRecord.TraceExecute(CIP, instruction.Size());
}
else
{
// if we reaches here, then the executable had executed an invalid address. Don't trace it.
instruction.DisassembleSafe(CIP, data, MAX_DISASM_BUFFER);
if(TraceRecord.isRunTraceEnabled())
{
TraceRecord.TraceExecute(CIP, instruction.Size());
TraceRecord.TraceExecuteRecord(instruction);
}
else
{
TraceRecord.TraceExecute(CIP, instruction.Size());
}
}
}
else
TraceRecord.increaseInstructionCounter();
{
if(TraceRecord.isRunTraceEnabled())
{
Capstone instruction;
unsigned char data[MAX_DISASM_BUFFER];
if(MemRead(CIP, data, MAX_DISASM_BUFFER))
{
instruction.DisassembleSafe(CIP, data, MAX_DISASM_BUFFER);
TraceRecord.TraceExecuteRecord(instruction);
}
}
}
TraceRecord.increaseInstructionCounter();
}
unsigned int _dbg_dbggetTraceRecordHitCount(duint address)
@ -406,3 +751,14 @@ TRACERECORDTYPE _dbg_dbggetTraceRecordType(duint pageAddress)
{
return (TRACERECORDTYPE)TraceRecord.getTraceRecordType(pageAddress);
}
// When disabled, file name is not relevant and can be NULL
bool _dbg_dbgenableRunTrace(bool enabled, const char* fileName)
{
return TraceRecord.enableRunTrace(enabled, fileName);
}
bool _dbg_dbgisRunTraceEnabled()
{
return TraceRecord.isRunTraceEnabled();
}

View File

@ -2,8 +2,11 @@
#define TRACERECORD_H
#include "_global.h"
#include "_dbgfunctions.h"
#include "debugger.h"
#include "jansson/jansson_x64dbg.h"
class Capstone;
class TraceRecordManager
{
public:
@ -52,11 +55,15 @@ public:
void TraceExecute(duint address, duint size);
//void TraceAccess(duint address, unsigned char size, TraceRecordByteType accessType);
void TraceExecuteRecord(const Capstone & newInstruction);
unsigned int getHitCount(duint address);
TraceRecordByteType getByteType(duint address);
void increaseInstructionCounter();
bool isRunTraceEnabled();
bool enableRunTrace(bool enabled, const char* fileName);
void saveToDb(JSON root);
void loadFromDb(JSON root);
private:
@ -76,11 +83,34 @@ private:
unsigned int moduleIndex;
};
typedef union _REGDUMPWORD
{
REGDUMP registers;
// 172 qwords on x64, 216 dwords on x86. Almost no space left for AVX512
// strip off 128 bytes of lastError.name member.
duint regword[(sizeof(REGDUMP) - 128) / sizeof(duint)];
} REGDUMPWORD;
//Key := page base, value := trace record raw data
std::unordered_map<duint, TraceRecordPage> TraceRecord;
std::vector<std::string> ModuleNames;
unsigned int getModuleIndex(const String & moduleName);
unsigned int instructionCounter;
bool rtEnabled;
bool rtPrevInstAvailable;
HANDLE rtFile;
REGDUMPWORD rtOldContext;
bool rtOldContextChanged[(sizeof(REGDUMP) - 128) / sizeof(duint)];
DWORD rtOldThreadId;
bool rtNeedThreadId;
duint rtOldMemory[32];
duint rtOldMemoryAddress[32];
char rtOldOpcode[16];
unsigned int rtRecordedInstructions;
unsigned char rtOldOpcodeSize;
unsigned char rtOldMemoryArrayCount;
};
extern TraceRecordManager TraceRecord;
@ -91,5 +121,7 @@ unsigned int _dbg_dbggetTraceRecordHitCount(duint address);
TRACERECORDBYTETYPE _dbg_dbggetTraceRecordByteType(duint address);
bool _dbg_dbgsetTraceRecordType(duint pageAddress, TRACERECORDTYPE type);
TRACERECORDTYPE _dbg_dbggetTraceRecordType(duint pageAddress);
bool _dbg_dbgenableRunTrace(bool enabled, const char* fileName);
bool _dbg_dbgisRunTraceEnabled();
#endif // TRACERECORD_H

View File

@ -29,6 +29,7 @@
#include "thread.h"
#include "comment.h"
#include "exception.h"
#include "database.h"
static DBGFUNCTIONS _dbgfunctions;
@ -439,4 +440,5 @@ void dbgfunctionsinit()
_dbgfunctions.ModRelocationsFromAddr = _modrelocationsfromaddr;
_dbgfunctions.ModRelocationAtAddr = (MODRELOCATIONATADDR)ModRelocationAtAddr;
_dbgfunctions.ModRelocationsInRange = _modrelocationsinrange;
_dbgfunctions.DbGetHash = DbGetHash;
}

View File

@ -195,6 +195,7 @@ typedef duint(*MEMBPSIZE)(duint addr);
typedef bool(*MODRELOCATIONSFROMADDR)(duint addr, ListOf(DBGRELOCATIONINFO) relocations);
typedef bool(*MODRELOCATIONATADDR)(duint addr, DBGRELOCATIONINFO* relocation);
typedef bool(*MODRELOCATIONSINRANGE)(duint addr, duint size, ListOf(DBGRELOCATIONINFO) relocations);
typedef duint(*DBGETHASH)();
//The list of all the DbgFunctions() return value.
//WARNING: This list is append only. Do not insert things in the middle or plugins would break.
@ -268,6 +269,7 @@ typedef struct DBGFUNCTIONS_
MODRELOCATIONSFROMADDR ModRelocationsFromAddr;
MODRELOCATIONATADDR ModRelocationAtAddr;
MODRELOCATIONSINRANGE ModRelocationsInRange;
DBGETHASH DbGetHash;
} DBGFUNCTIONS;
#ifdef BUILD_DBG

View File

@ -658,6 +658,7 @@ extern "C" DLL_EXPORT bool _dbg_getregdump(REGDUMP* regdump)
Getx87ControlWordFields(& (regdump->x87ControlWordFields), regdump->regcontext.x87fpu.ControlWord);
Getx87StatusWordFields(& (regdump->x87StatusWordFields), regdump->regcontext.x87fpu.StatusWord);
LASTERROR lastError;
memset(&lastError.name, 0, sizeof(lastError.name));
lastError.code = ThreadGetLastError(ThreadGetId(hActiveThread));
strncpy_s(lastError.name, ErrorCodeToName(lastError.code).c_str(), _TRUNCATE);
regdump->lastError = lastError;

View File

@ -130,7 +130,7 @@ SCRIPT_EXPORT bool Script::Module::GetMainModuleInfo(ModuleInfo* info)
SCRIPT_EXPORT duint Script::Module::GetMainModuleBase()
{
return dbggetdebuggedbase();
return dbgdebuggedbase();
}
SCRIPT_EXPORT duint Script::Module::GetMainModuleSize()

View File

@ -6,6 +6,8 @@
#include "console.h"
#include "cmd-debug-control.h"
#include "value.h"
#include "variable.h"
#include "TraceRecord.h"
extern std::vector<std::pair<duint, duint>> RunToUserCodeBreakpoints;
@ -168,3 +170,15 @@ bool cbDebugTraceSetLogFile(int argc, char* argv[])
auto fileName = argc > 1 ? argv[1] : "";
return dbgsettracelogfile(fileName);
}
bool cbDebugStartRunTrace(int argc, char* argv[])
{
if(IsArgumentsLessThan(argc, 2))
return false;
return _dbg_dbgenableRunTrace(true, argv[1]);
}
bool cbDebugStopRunTrace(int argc, char* argv[])
{
return _dbg_dbgenableRunTrace(false, nullptr);
}

View File

@ -13,4 +13,6 @@ bool cbDebugRunToUserCode(int argc, char* argv[]);
bool cbDebugTraceSetLog(int argc, char* argv[]);
bool cbDebugTraceSetCommand(int argc, char* argv[]);
bool cbDebugTraceSetSwitchCondition(int argc, char* argv[]);
bool cbDebugTraceSetLogFile(int argc, char* argv[]);
bool cbDebugTraceSetLogFile(int argc, char* argv[]);
bool cbDebugStartRunTrace(int argc, char* argv[]);
bool cbDebugStopRunTrace(int argc, char* argv[]);

View File

@ -407,4 +407,9 @@ bool DbCheckHash(duint currentHash)
dbhash = currentHash;
return true;
}
}
duint DbGetHash()
{
return dbhash;
}

View File

@ -16,5 +16,6 @@ void DbClose();
void DbClear(bool terminating = false);
void DbSetPath(const char* Directory, const char* ModulePath);
bool DbCheckHash(duint currentHash);
duint DbGetHash();
#endif // _DATABASE_H

View File

@ -86,6 +86,7 @@ bool bIgnoreInconsistentBreakpoints = false;
bool bNoForegroundWindow = false;
bool bVerboseExceptionLogging = true;
bool bNoWow64SingleStepWorkaround = false;
bool bTraceBrowserNeedsUpdate = false;
duint DbgEvents = 0;
duint maxSkipExceptionCount = 10000;
HANDLE mProcHandle;
@ -226,6 +227,11 @@ static DWORD WINAPI dumpRefreshThread(void* ptr)
break;
GuiUpdateDumpView();
GuiUpdateWatchView();
if(bTraceBrowserNeedsUpdate)
{
bTraceBrowserNeedsUpdate = false;
GuiUpdateTraceBrowser();
}
Sleep(400);
}
return 0;
@ -373,6 +379,11 @@ duint dbggetdbgevents()
return InterlockedExchange((volatile long*)&DbgEvents, 0);
}
void dbgtracebrowserneedsupdate()
{
bTraceBrowserNeedsUpdate = true;
}
static DWORD WINAPI updateCallStackThread(duint ptr)
{
stackupdatecallstack(ptr);
@ -2506,11 +2517,6 @@ void dbgstartscriptthread(CBPLUGINSCRIPT cbScript)
CloseHandle(CreateThread(0, 0, scriptThread, (LPVOID)cbScript, 0, 0));
}
duint dbggetdebuggedbase()
{
return pDebuggedBase;
}
static void debugLoopFunction(void* lpParameter, bool attach)
{
//we are running
@ -2712,6 +2718,7 @@ static void debugLoopFunction(void* lpParameter, bool attach)
ThreadClear();
WatchClear();
TraceRecord.clear();
_dbg_dbgenableRunTrace(false, nullptr); //Stop run trace
GuiSetDebugState(stopped);
GuiUpdateAllViews();
dputs(QT_TRANSLATE_NOOP("DBG", "Debugging stopped!"));

View File

@ -66,7 +66,6 @@ bool dbgsetcmdline(const char* cmd_line, cmdline_error_t* cmd_line_error);
bool dbggetcmdline(char** cmd_line, cmdline_error_t* cmd_line_error, HANDLE hProcess = NULL);
cmdline_qoutes_placement_t getqoutesplacement(const char* cmdline);
void dbgstartscriptthread(CBPLUGINSCRIPT cbScript);
duint dbggetdebuggedbase();
duint dbggetdbgevents();
bool dbgsettracecondition(const String & expression, duint maxCount);
bool dbgsettracelog(const String & expression, const String & text);
@ -79,6 +78,7 @@ void dbgsetdebuggeeinitscript(const char* fileName);
const char* dbggetdebuggeeinitscript();
void dbgsetforeground();
bool dbggetwintext(std::vector<std::string>* winTextList, const DWORD dwProcessId);
void dbgtracebrowserneedsupdate();
void cbStep();
void cbRtrStep();

View File

@ -63,7 +63,7 @@ void ExpressionFunctions::Init()
RegisterEasy("mod.entry", ModEntryFromAddr);
RegisterEasy("mod.system,mod.issystem", modsystem);
RegisterEasy("mod.user,mod.isuser", moduser);
RegisterEasy("mod.main,mod.mainbase", dbggetdebuggedbase);
RegisterEasy("mod.main,mod.mainbase", dbgdebuggedbase);
RegisterEasy("mod.rva", modrva);
RegisterEasy("mod.offset,mod.fileoffset", valvatofileoffset);
@ -104,6 +104,7 @@ void ExpressionFunctions::Init()
//Trace record
RegisterEasy("tr.enabled", trenabled);
RegisterEasy("tr.hitcount,tr.count", trhitcount);
RegisterEasy("tr.runtraceenabled", trisruntraceenabled);
//Byte/Word/Dword/Qword/Pointer
RegisterEasy("ReadByte,Byte,byte", readbyte);

View File

@ -9,6 +9,7 @@
#include "disasm_helper.h"
#include "function.h"
#include "value.h"
#include "TraceRecord.h"
#include "exhandlerinfo.h"
namespace Exprfunc
@ -261,6 +262,11 @@ namespace Exprfunc
return trenabled(addr) ? TraceRecord.getHitCount(addr) : 0;
}
duint trisruntraceenabled()
{
return _dbg_dbgisRunTraceEnabled() ? 1 : 0;
}
duint gettickcount()
{
#ifdef _WIN64

View File

@ -47,6 +47,7 @@ namespace Exprfunc
duint trenabled(duint addr);
duint trhitcount(duint addr);
duint trisruntraceenabled();
duint gettickcount();
duint readbyte(duint addr);

View File

@ -22,4 +22,4 @@ unsigned json_int_t json_hex_value(const json_t* hex)
return 0;
sscanf_s(hexvalue, "0x%llX", &ret);
return ret;
}
}

View File

@ -221,6 +221,8 @@ static void registercommands()
dbgcmdnew("TraceSetCommand,SetTraceCommand", cbDebugTraceSetCommand, true); //Set trace command text + condition
dbgcmdnew("TraceSetSwitchCondition,SetTraceSwitchCondition", cbDebugTraceSetSwitchCondition, true); //Set trace switch condition
dbgcmdnew("TraceSetLogFile,SetTraceLogFile", cbDebugTraceSetLogFile, true); //Set trace log file
dbgcmdnew("StartRunTrace,opentrace", cbDebugStartRunTrace, true); //start run trace (Ollyscript command "opentrace" "opens run trace window")
dbgcmdnew("StopRunTrace,tc", cbDebugStopRunTrace, true); //stop run trace (and Ollyscript command)
//thread control
dbgcmdnew("createthread,threadcreate,newthread,threadnew", cbDebugCreatethread, true); //create thread

View File

@ -961,11 +961,11 @@ int AbstractTableView::getColumnPosition(int index)
}
/**
* @brief Substracts the header heigth from the given y.
* @brief Substracts the header height from the given y.
*
* @param[in] y y coordinate
*
* @return y - getHeaderHeigth().
* @return y - getHeaderHeight().
*/
int AbstractTableView::transY(int y)
{
@ -1150,7 +1150,7 @@ int AbstractTableView::getHeaderHeight()
return 0;
}
int AbstractTableView::getTableHeigth()
int AbstractTableView::getTableHeight()
{
return this->viewport()->height() - getHeaderHeight();
}

View File

@ -97,7 +97,7 @@ public:
void setColumnOrder(int pos, int index);
int getColumnOrder(int index);
int getHeaderHeight();
int getTableHeigth();
int getTableHeight();
int getGuiState();
int getNbrOfLineToPrint();
void setNbrOfLineToPrint(int parNbrOfLineToPrint);

View File

@ -712,7 +712,7 @@ void Disassembly::mouseMoveEvent(QMouseEvent* event)
{
//qDebug() << "State = MultiRowsSelectionState";
if((transY(y) >= 0) && (transY(y) <= this->getTableHeigth()))
if((transY(y) >= 0) && (transY(y) <= this->getTableHeight()))
{
int wI = getIndexOffsetFromY(transY(y));

View File

@ -40,7 +40,7 @@ void StdTable::mouseMoveEvent(QMouseEvent* event)
{
//qDebug() << "State = MultiRowsSelectionState";
if(y >= 0 && y <= this->getTableHeigth())
if(y >= 0 && y <= this->getTableHeight())
{
int wRowIndex = getTableOffset() + getIndexOffsetFromY(y);
@ -60,7 +60,7 @@ void StdTable::mouseMoveEvent(QMouseEvent* event)
{
verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepSub);
}
else if(y > getTableHeigth())
else if(y > getTableHeight())
{
verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepAdd);
}

View File

@ -823,6 +823,19 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
emit referenceAddCommand(QString::fromUtf8((const char*)param1), QString::fromUtf8((const char*)param2));
}
break;
case GUI_OPEN_TRACE_FILE:
{
if(param1 == nullptr)
return nullptr;
emit openTraceFile(QString::fromUtf8((const char*)param1));
}
break;
case GUI_UPDATE_TRACE_BROWSER:
emit updateTraceBrowser();
break;
}
return nullptr;

View File

@ -154,6 +154,8 @@ signals:
void closeApplication();
void flushLog();
void getDumpAttention();
void openTraceFile(const QString & fileName);
void updateTraceBrowser();
private:
CRITICAL_SECTION csBridge;

View File

@ -27,7 +27,10 @@ void BreakpointMenu::build(MenuBuilder* builder)
builder->addMenu(makeMenu(DIcon("breakpoint.png"), tr("Breakpoint")), [ = ](QMenu * menu)
{
BPXTYPE bpType = DbgGetBpxTypeAt(mGetSelection());
auto selection = mGetSelection();
if(selection == 0)
return false;
BPXTYPE bpType = DbgGetBpxTypeAt(selection);
if((bpType & bp_normal) == bp_normal)
menu->addAction(editSoftwareBreakpointAction);

View File

@ -1,5 +1,6 @@
#include "BrowseDialog.h"
#include "ui_BrowseDialog.h"
#include "MiscUtil.h"
#include <QFileDialog>
#include <Configuration.h>
@ -28,9 +29,8 @@ void BrowseDialog::on_browse_clicked()
file = QFileDialog::getSaveFileName(this, ui->label->text(), ui->lineEdit->text(), mFilter);
else
file = QFileDialog::getOpenFileName(this, ui->label->text(), ui->lineEdit->text(), mFilter);
file = QDir::toNativeSeparators(file);
if(file.size() != 0)
ui->lineEdit->setText(file);
ui->lineEdit->setText(QDir::toNativeSeparators(file));
}
void BrowseDialog::on_ok_clicked()

View File

@ -4,6 +4,7 @@
#include <QDesktopServices>
#include <QClipboard>
#include "CPUDisassembly.h"
#include "main.h"
#include "CPUSideBar.h"
#include "CPUWidget.h"
#include "EncodeMap.h"
@ -26,6 +27,7 @@
#include "SnowmanView.h"
#include "MemoryPage.h"
#include "BreakpointMenu.h"
#include "BrowseDialog.h"
CPUDisassembly::CPUDisassembly(CPUWidget* parent) : Disassembly(parent)
{
@ -382,6 +384,7 @@ void CPUDisassembly::setupRightClickContextMenu()
QAction* traceRecordEnableBit = makeAction(DIcon("bit.png"), tr("Bit"), SLOT(ActionTraceRecordBitSlot()));
QAction* traceRecordEnableByte = makeAction(DIcon("byte.png"), tr("Byte"), SLOT(ActionTraceRecordByteSlot()));
QAction* traceRecordEnableWord = makeAction(DIcon("word.png"), tr("Word"), SLOT(ActionTraceRecordWordSlot()));
QAction* traceRecordToggleRunTrace = makeShortcutAction(tr("Start Run Trace"), SLOT(ActionTraceRecordToggleRunTraceSlot()), "ActionToggleRunTrace");
mMenuBuilder->addMenu(makeMenu(DIcon("trace.png"), tr("Trace record")), [ = ](QMenu * menu)
{
if(DbgFunctions()->GetTraceRecordType(rvaToVa(getInitialSelection())) == TRACERECORDTYPE::TraceRecordNone)
@ -392,6 +395,12 @@ void CPUDisassembly::setupRightClickContextMenu()
}
else
menu->addAction(traceRecordDisable);
menu->addSeparator();
if(DbgValFromString("tr.runtraceenabled()") == 1)
traceRecordToggleRunTrace->setText(tr("Stop Run Trace"));
else
traceRecordToggleRunTrace->setText(tr("Start Run Trace"));
menu->addAction(traceRecordToggleRunTrace);
return true;
});
@ -1452,11 +1461,14 @@ void CPUDisassembly::pushSelectionInto(bool copyBytes, QTextStream & stream, QTe
duint cur_addr = rvaToVa(inst.rva);
QString address = getAddrText(cur_addr, 0, addressLen > sizeof(duint) * 2 + 1);
QString bytes;
for(int j = 0; j < inst.dump.size(); j++)
if(copyBytes)
{
if(j)
bytes += " ";
bytes += ToByteString((unsigned char)(inst.dump.at(j)));
for(int j = 0; j < inst.dump.size(); j++)
{
if(j)
bytes += " ";
bytes += ToByteString((unsigned char)(inst.dump.at(j)));
}
}
QString disassembly;
QString htmlDisassembly;
@ -1996,3 +2008,32 @@ void CPUDisassembly::downloadCurrentSymbolsSlot()
if(DbgGetModuleAt(rvaToVa(getInitialSelection()), module))
DbgCmdExec(QString("symdownload \"%0\"").arg(module).toUtf8().constData());
}
void CPUDisassembly::ActionTraceRecordToggleRunTraceSlot()
{
if(!DbgIsDebugging())
return;
if(DbgValFromString("tr.runtraceenabled()") == 1)
DbgCmdExec("StopRunTrace");
else
{
QString defaultFileName;
char moduleName[MAX_MODULE_SIZE];
QDateTime currentTime = QDateTime::currentDateTime();
duint defaultModule = DbgValFromString("mod.main()");
if(DbgFunctions()->ModNameFromAddr(defaultModule, moduleName, false))
{
defaultFileName = QString::fromUtf8(moduleName);
}
defaultFileName += "-" + QLocale(QString(currentLocale)).toString(currentTime.date()) + " " + currentTime.time().toString("hh-mm-ss") + ArchValue(".trace32", ".trace64");
BrowseDialog browse(this, tr("Select stored file"), tr("Store run trace to the following file"),
tr("Run trace files (*.%1);;All files (*.*)").arg(ArchValue("trace32", "trace64")), QCoreApplication::applicationDirPath() + QDir::separator() + "db" + QDir::separator() + defaultFileName, true);
if(browse.exec() == QDialog::Accepted)
{
if(browse.path.contains(QChar('"')) || browse.path.contains(QChar('\'')))
SimpleErrorBox(this, tr("Error"), tr("File name contains invalid character."));
else
DbgCmdExec(QString("StartRunTrace \"%1\"").arg(browse.path).toUtf8().constData());
}
}
}

View File

@ -97,6 +97,7 @@ public slots:
void ActionTraceRecordByteSlot();
void ActionTraceRecordWordSlot();
void ActionTraceRecordDisableSlot();
void ActionTraceRecordToggleRunTraceSlot();
void displayWarningSlot(QString title, QString text);
void labelHelpSlot();
void analyzeSingleFunctionSlot();

View File

@ -52,6 +52,7 @@
#include "MRUList.h"
#include "AboutDialog.h"
#include "UpdateChecker.h"
#include "Tracer/TraceBrowser.h"
QString MainWindow::windowTitle = "";
@ -204,6 +205,12 @@ MainWindow::MainWindow(QWidget* parent)
mGraphView->setWindowTitle(tr("Graph"));
mGraphView->setWindowIcon(DIcon("graph.png"));
// Trace view
mTraceBrowser = new TraceBrowser(this);
mTraceBrowser->setWindowTitle(tr("Trace"));
mTraceBrowser->setWindowIcon(DIcon("trace.png"));
connect(mTraceBrowser, SIGNAL(displayReferencesWidget()), this, SLOT(displayReferencesWidget()));
// Create the tab widget and enable detaching and hiding
mTabWidget = new MHTabWidget(this, true, true);
@ -223,6 +230,7 @@ MainWindow::MainWindow(QWidget* parent)
mWidgetList.push_back(WidgetInfo(mThreadView, "ThreadsTab"));
mWidgetList.push_back(WidgetInfo(mSnowmanView, "SnowmanTab"));
mWidgetList.push_back(WidgetInfo(mHandlesView, "HandlesTab"));
mWidgetList.push_back(WidgetInfo(mTraceBrowser, "TraceTab"));
// If LoadSaveTabOrder disabled, load tabs in default order
if(!ConfigBool("Gui", "LoadSaveTabOrder"))
@ -297,6 +305,7 @@ MainWindow::MainWindow(QWidget* parent)
connect(ui->actionFunctions, SIGNAL(triggered()), this, SLOT(displayFunctions()));
connect(ui->actionCallStack, SIGNAL(triggered()), this, SLOT(displayCallstack()));
connect(ui->actionSEHChain, SIGNAL(triggered()), this, SLOT(displaySEHChain()));
connect(ui->actionTrace, SIGNAL(triggered()), this, SLOT(displayRunTrace()));
connect(ui->actionDonate, SIGNAL(triggered()), this, SLOT(donate()));
connect(ui->actionReportBug, SIGNAL(triggered()), this, SLOT(reportBug()));
connect(ui->actionBlog, SIGNAL(triggered()), this, SLOT(blog()));
@ -1432,6 +1441,11 @@ void MainWindow::displaySEHChain()
showQWidgetTab(mSEHChainView);
}
void MainWindow::displayRunTrace()
{
showQWidgetTab(mTraceBrowser);
}
void MainWindow::donate()
{
QMessageBox msg(QMessageBox::Information, tr("Donate"), tr("All the money will go to x64dbg development."));

View File

@ -33,6 +33,7 @@ class DisassemblerGraphView;
class SimpleTraceDialog;
class MRUList;
class UpdateChecker;
class TraceBrowser;
namespace Ui
{
@ -88,6 +89,7 @@ public slots:
void displaySnowmanWidget();
void displayVariables();
void displayGraphWidget();
void displayRunTrace();
void displayPreviousTab();
void displayNextTab();
void hideTab();
@ -176,6 +178,7 @@ private:
HandlesView* mHandlesView;
NotesManager* mNotesManager;
DisassemblerGraphView* mGraphView;
TraceBrowser* mTraceBrowser;
SimpleTraceDialog* mSimpleTraceDialog;
UpdateChecker* mUpdateChecker;
DebugStatusLabel* mStatusLabel;

View File

@ -23,7 +23,7 @@
<x>0</x>
<y>0</y>
<width>868</width>
<height>21</height>
<height>23</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@ -67,15 +67,16 @@
<addaction name="actionSource"/>
<addaction name="actionReferences"/>
<addaction name="actionThreads"/>
<addaction name="actionSnowman"/>
<addaction name="actionHandles"/>
<addaction name="actionGraph"/>
<addaction name="actionTrace"/>
<addaction name="actionPatches"/>
<addaction name="actionComments"/>
<addaction name="actionLabels"/>
<addaction name="actionBookmarks"/>
<addaction name="actionFunctions"/>
<addaction name="actionVariables"/>
<addaction name="actionSnowman"/>
<addaction name="actionHandles"/>
<addaction name="actionGraph"/>
<addaction name="separator"/>
<addaction name="actionPreviousTab"/>
<addaction name="actionNextTab"/>
@ -1141,6 +1142,15 @@
<string>Plugins</string>
</property>
</action>
<action name="actionTrace">
<property name="icon">
<iconset resource="../../resource.qrc">
<normaloff>:/icons/images/trace.png</normaloff>:/icons/images/trace.png</iconset>
</property>
<property name="text">
<string>Trace</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,135 @@
#ifndef TRACEBROWSER_H
#define TRACEBROWSER_H
#include "AbstractTableView.h"
#include "VaHistory.h"
#include "QBeaEngine.h"
class TraceFileReader;
class BreakpointMenu;
class MRUList;
class TraceBrowser : public AbstractTableView
{
Q_OBJECT
public:
explicit TraceBrowser(QWidget* parent = 0);
~TraceBrowser();
QString paintContent(QPainter* painter, dsint rowBase, int rowOffset, int col, int x, int y, int w, int h);
void prepareData();
virtual void updateColors();
void expandSelectionUpTo(duint to);
void setSingleSelection(duint index);
duint getInitialSelection();
duint getSelectionSize();
duint getSelectionStart();
duint getSelectionEnd();
private:
void setupRightClickContextMenu();
void makeVisible(duint index);
QString getAddrText(dsint cur_addr, char label[MAX_LABEL_SIZE], bool getLabel);
QString getIndexText(duint index);
void pushSelectionInto(bool copyBytes, QTextStream & stream, QTextStream* htmlStream = nullptr);
void copySelectionSlot(bool copyBytes);
void copySelectionToFileSlot(bool copyBytes);
void contextMenuEvent(QContextMenuEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
void keyPressEvent(QKeyEvent* event);
VaHistory mHistory;
MenuBuilder* mMenuBuilder;
bool mRvaDisplayEnabled;
duint mRvaDisplayBase;
typedef struct _SelectionData_t
{
duint firstSelectedIndex;
duint fromIndex;
duint toIndex;
} SelectionData_t;
SelectionData_t mSelection;
CapstoneTokenizer::SingleToken mHighlightToken;
bool mHighlightingMode;
bool mPermanentHighlightingMode;
TraceFileReader* mTraceFile;
QBeaEngine* mDisasm;
BreakpointMenu* mBreakpointMenu;
MRUList* mMRUList;
QString mFileName;
QColor mBytesColor;
QColor mBytesBackgroundColor;
QColor mInstructionHighlightColor;
QColor mSelectionColor;
QColor mCipBackgroundColor;
QColor mCipColor;
QColor mBreakpointBackgroundColor;
QColor mBreakpointColor;
QColor mHardwareBreakpointBackgroundColor;
QColor mHardwareBreakpointColor;
QColor mBookmarkBackgroundColor;
QColor mBookmarkColor;
QColor mLabelColor;
QColor mLabelBackgroundColor;
QColor mSelectedAddressBackgroundColor;
QColor mTracedAddressBackgroundColor;
QColor mSelectedAddressColor;
QColor mAddressBackgroundColor;
QColor mAddressColor;
QColor mAutoCommentColor;
QColor mAutoCommentBackgroundColor;
QColor mCommentColor;
QColor mCommentBackgroundColor;
signals:
void displayReferencesWidget();
public slots:
void openFileSlot();
void openSlot(const QString & fileName);
void toggleRunTraceSlot();
void closeFileSlot();
void parseFinishedSlot();
void tokenizerConfigUpdatedSlot();
void gotoSlot();
void gotoPreviousSlot();
void gotoNextSlot();
void followDisassemblySlot();
void enableHighlightingModeSlot();
void setCommentSlot();
void copyDisassemblySlot();
void copyCipSlot();
void copyIndexSlot();
void copySelectionSlot();
void copySelectionNoBytesSlot();
void copySelectionToFileSlot();
void copySelectionToFileNoBytesSlot();
void copyFileOffsetSlot();
void copyRvaSlot();
void searchConstantSlot();
void searchMemRefSlot();
void updateSlot(); //debug
};
#endif //TRACEBROWSER_H

View File

@ -0,0 +1,620 @@
#include "TraceFileReaderInternal.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QThread>
TraceFileReader::TraceFileReader(QObject* parent) : QObject(parent)
{
length = 0;
progress = 0;
error = true;
parser = nullptr;
lastAccessedPage = nullptr;
lastAccessedIndexOffset = 0;
hashValue = 0;
EXEPath.clear();
}
bool TraceFileReader::Open(const QString & fileName)
{
if(parser != NULL && parser->isRunning()) //Trace file parser is busy
{
parser->requestInterruption();
parser->wait();
}
error = true;
traceFile.setFileName(fileName);
traceFile.open(QFile::ReadOnly);
if(traceFile.isReadable())
{
parser = new TraceFileParser(this);
connect(parser, SIGNAL(finished()), this, SLOT(parseFinishedSlot()));
progress.store(0);
traceFile.moveToThread(parser);
parser->start();
return true;
}
else
{
progress.store(0);
emit parseFinished();
return false;
}
}
void TraceFileReader::Close()
{
if(parser != NULL)
{
parser->requestInterruption();
parser->wait();
}
traceFile.close();
progress.store(0);
length = 0;
fileIndex.clear();
hashValue = 0;
EXEPath.clear();
error = false;
}
void TraceFileReader::parseFinishedSlot()
{
if(!error)
progress.store(100);
else
progress.store(0);
delete parser;
parser = nullptr;
emit parseFinished();
//for(auto i : fileIndex)
//GuiAddLogMessage(QString("%1;%2;%3\r\n").arg(i.first).arg(i.second.first).arg(i.second.second).toUtf8().constData());
}
bool TraceFileReader::isError()
{
return error;
}
int TraceFileReader::Progress()
{
return progress.load();
}
unsigned long long TraceFileReader::Length()
{
return length;
}
duint TraceFileReader::HashValue()
{
return hashValue;
}
QString TraceFileReader::ExePath()
{
return EXEPath;
}
REGDUMP TraceFileReader::Registers(unsigned long long index)
{
unsigned long long base;
TraceFilePage* page = getPage(index, &base);
if(page == nullptr)
{
REGDUMP registers;
memset(&registers, 0, sizeof(registers));
return registers;
}
else
return page->Registers(index - base);
}
void TraceFileReader::OpCode(unsigned long long index, unsigned char* buffer, int* opcodeSize)
{
unsigned long long base;
TraceFilePage* page = getPage(index, &base);
if(page == nullptr)
{
memset(buffer, 0, 16);
return;
}
else
page->OpCode(index - base, buffer, opcodeSize);
}
DWORD TraceFileReader::ThreadId(unsigned long long index)
{
unsigned long long base;
TraceFilePage* page = getPage(index, &base);
if(page == nullptr)
return 0;
else
return page->ThreadId(index - base);
}
int TraceFileReader::MemoryAccessCount(unsigned long long index)
{
unsigned long long base;
TraceFilePage* page = getPage(index, &base);
if(page == nullptr)
return 0;
else
return page->MemoryAccessCount(index - base);
}
void TraceFileReader::MemoryAccessInfo(unsigned long long index, duint* address, duint* oldMemory, duint* newMemory, bool* isValid)
{
unsigned long long base;
TraceFilePage* page = getPage(index, &base);
if(page == nullptr)
return;
else
return page->MemoryAccessInfo(index - base, address, oldMemory, newMemory, isValid);
}
TraceFilePage* TraceFileReader::getPage(unsigned long long index, unsigned long long* base)
{
if(lastAccessedPage)
{
if(index >= lastAccessedIndexOffset && index < lastAccessedIndexOffset + lastAccessedPage->Length())
{
*base = lastAccessedIndexOffset;
return lastAccessedPage;
}
}
const auto cache = pages.find(Range(index, index));
if(cache != pages.cend())
{
if(cache->first.first >= index && cache->first.second <= index)
{
if(lastAccessedPage)
GetSystemTimes(nullptr, nullptr, &lastAccessedPage->lastAccessed);
lastAccessedPage = &cache->second;
lastAccessedIndexOffset = cache->first.first;
GetSystemTimes(nullptr, nullptr, &lastAccessedPage->lastAccessed);
*base = lastAccessedIndexOffset;
return lastAccessedPage;
}
}
else if(index >= Length()) //Out of bound
return nullptr;
//page in
if(pages.size() >= 2048) //TODO: trim resident pages based on system memory usage, instead of a hard limit.
{
FILETIME pageOutTime = pages.begin()->second.lastAccessed;
Range pageOutIndex = pages.begin()->first;
for(auto i : pages)
{
if(pageOutTime.dwHighDateTime < i.second.lastAccessed.dwHighDateTime || (pageOutTime.dwHighDateTime == i.second.lastAccessed.dwHighDateTime && pageOutTime.dwLowDateTime < i.second.lastAccessed.dwLowDateTime))
{
pageOutTime = i.second.lastAccessed;
pageOutIndex = i.first;
}
}
pages.erase(pageOutIndex);
}
//binary search fileIndex to get file offset, push a TraceFilePage into cache and return it.
size_t start = 0;
size_t end = fileIndex.size() - 1;
size_t middle = (start + end) / 2;
std::pair<unsigned long long, Range>* fileOffset;
while(true)
{
if(start == end || start == end - 1)
{
if(fileIndex[end].first <= index)
fileOffset = &fileIndex[end];
else
fileOffset = &fileIndex[start];
break;
}
if(fileIndex[middle].first > index)
end = middle;
else if(fileIndex[middle].first == index)
{
fileOffset = &fileIndex[middle];
break;
}
else
start = middle;
middle = (start + end) / 2;
}
if(fileOffset->second.second + fileOffset->first >= index && fileOffset->first <= index)
{
pages.insert(std::make_pair(Range(fileOffset->first, fileOffset->first + fileOffset->second.second - 1), TraceFilePage(this, fileOffset->second.first, fileOffset->second.second)));
const auto newPage = pages.find(Range(index, index));
if(newPage != pages.cend())
{
if(lastAccessedPage)
GetSystemTimes(nullptr, nullptr, &lastAccessedPage->lastAccessed);
lastAccessedPage = &newPage->second;
lastAccessedIndexOffset = newPage->first.first;
GetSystemTimes(nullptr, nullptr, &lastAccessedPage->lastAccessed);
*base = lastAccessedIndexOffset;
return lastAccessedPage;
}
else
{
GuiAddLogMessage("PAGEFAULT2\r\n"); //debug
return nullptr; //???
}
}
else
{
GuiAddLogMessage("PAGEFAULT1\r\n"); //debug
return nullptr; //???
}
}
//Parser
static bool checkKey(const QJsonObject & root, const QString & key, const QString & value)
{
const auto obj = root.find(key);
if(obj == root.constEnd())
throw std::wstring(L"Unspecified");
QJsonValue val = obj.value();
if(val.isString())
if(val.toString() == value)
return true;
return false;
}
void TraceFileParser::readFileHeader(TraceFileReader* that)
{
LARGE_INTEGER header;
if(that->traceFile.read((char*)&header, 8) != 8)
throw std::wstring(L"Unspecified");
if(header.LowPart != MAKEFOURCC('T', 'R', 'A', 'C'))
throw std::wstring(L"File type mismatch");
if(header.HighPart > 16384)
throw std::wstring(L"Header info is too big");
QByteArray jsonData = that->traceFile.read(header.HighPart);
if(jsonData.size() != header.HighPart)
throw std::wstring(L"Unspecified");
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
if(jsonDoc.isNull())
throw std::wstring(L"Unspecified");
const QJsonObject jsonRoot = jsonDoc.object();
const auto ver = jsonRoot.find("ver");
if(ver == jsonRoot.constEnd())
throw std::wstring(L"Unspecified");
QJsonValue verVal = ver.value();
if(verVal.toInt(0) != 1)
throw std::wstring(L"Version not supported");
checkKey(jsonRoot, "arch", ArchValue("x86", "x64"));
checkKey(jsonRoot, "compression", "");
const auto hashAlgorithmObj = jsonRoot.find("hashAlgorithm");
if(hashAlgorithmObj != jsonRoot.constEnd())
{
QJsonValue hashAlgorithmVal = hashAlgorithmObj.value();
if(hashAlgorithmVal.toString() == "murmurhash")
{
const auto hashObj = jsonRoot.find("hash");
if(hashObj != jsonRoot.constEnd())
{
QJsonValue hashVal = hashObj.value();
QString a = hashVal.toString();
if(a.startsWith("0x"))
{
a = a.mid(2);
#ifdef _WIN64
that->hashValue = a.toLongLong(nullptr, 16);
#else //x86
that->hashValue = a.toLong(nullptr, 16);
#endif //_WIN64
}
}
}
}
const auto pathObj = jsonRoot.find("path");
if(pathObj != jsonRoot.constEnd())
{
QJsonValue pathVal = pathObj.value();
that->EXEPath = pathVal.toString();
}
}
static bool readBlock(QFile & traceFile)
{
if(!traceFile.isReadable())
throw std::wstring(L"File is not readable");
unsigned char blockType;
unsigned char changedCountFlags[3]; //reg changed count, mem accessed count, flags
if(traceFile.read((char*)&blockType, 1) != 1)
throw std::wstring(L"Read block type failed");
if(blockType == 0)
{
if(traceFile.read((char*)&changedCountFlags, 3) != 3)
throw std::wstring(L"Read flags failed");
//skipping: thread id, registers
if(traceFile.seek(traceFile.pos() + ((changedCountFlags[2] & 0x80) ? 4 : 0) + (changedCountFlags[2] & 0x0F) + changedCountFlags[0] * (1 + sizeof(duint))) == false)
throw std::wstring(L"Unspecified");
QByteArray memflags;
memflags = traceFile.read(changedCountFlags[1]);
if(memflags.length() < changedCountFlags[1])
throw std::wstring(L"Read memory flags failed");
unsigned int skipOffset = 0;
for(unsigned char i = 0; i < changedCountFlags[1]; i++)
skipOffset += ((memflags[i] & 1) == 1) ? 2 : 3;
if(traceFile.seek(traceFile.pos() + skipOffset * sizeof(duint)) == false)
throw std::wstring(L"Unspecified");
//Gathered information, build index
if(changedCountFlags[0] == (sizeof(REGDUMP) - 128) / sizeof(duint))
return true;
else
return false;
}
else
throw std::wstring(L"Unsupported block type");
return false;
}
void TraceFileParser::run()
{
TraceFileReader* that = dynamic_cast<TraceFileReader*>(parent());
unsigned long long index = 0;
unsigned long long lastIndex = 0;
if(that == NULL)
{
return; //Error
}
try
{
if(that->traceFile.size() == 0)
throw std::wstring(L"File is empty");
//Process file header
readFileHeader(that);
//Update progress
that->progress.store(that->traceFile.pos() * 100 / that->traceFile.size());
//Process file content
while(!that->traceFile.atEnd())
{
quint64 blockStart = that->traceFile.pos();
bool isPageBoundary = readBlock(that->traceFile);
if(isPageBoundary)
{
if(lastIndex != 0)
that->fileIndex.back().second.second = index - (lastIndex - 1);
that->fileIndex.push_back(std::make_pair(index, TraceFileReader::Range(blockStart, 0)));
lastIndex = index + 1;
//Update progress
that->progress.store(that->traceFile.pos() * 100 / that->traceFile.size());
if(this->isInterruptionRequested() && !that->traceFile.atEnd()) //Cancel loading
throw std::wstring(L"Canceled");
}
index++;
}
if(index > 0)
that->fileIndex.back().second.second = index - (lastIndex - 1);
that->error = false;
that->length = index;
}
catch(const std::wstring & errReason)
{
//MessageBox(0, errReason.c_str(), L"debug", MB_ICONERROR);
that->error = true;
}
catch(std::bad_alloc &)
{
that->error = true;
}
that->traceFile.moveToThread(that->thread());
}
void TraceFileReader::purgeLastPage()
{
unsigned long long index = 0;
unsigned long long lastIndex = 0;
bool isBlockExist = false;
if(length > 0)
{
index = fileIndex.back().first;
const auto lastpage = pages.find(Range(index, index));
if(lastpage != pages.cend())
{
//Purge last accessed page
if(index == lastAccessedIndexOffset)
lastAccessedPage = nullptr;
//Remove last page from page cache
pages.erase(lastpage);
}
//Seek start of last page
traceFile.seek(fileIndex.back().second.first);
//Remove last page from file index cache
fileIndex.pop_back();
}
try
{
while(!traceFile.atEnd())
{
quint64 blockStart = traceFile.pos();
bool isPageBoundary = readBlock(traceFile);
if(isPageBoundary)
{
if(lastIndex != 0)
fileIndex.back().second.second = index - (lastIndex - 1);
fileIndex.push_back(std::make_pair(index, TraceFileReader::Range(blockStart, 0)));
lastIndex = index + 1;
isBlockExist = true;
}
index++;
}
if(isBlockExist)
fileIndex.back().second.second = index - (lastIndex - 1);
error = false;
length = index;
}
catch(std::wstring & errReason)
{
error = true;
}
}
//TraceFilePage
TraceFilePage::TraceFilePage(TraceFileReader* parent, unsigned long long fileOffset, unsigned long long maxLength)
{
DWORD lastThreadId = 0;
union
{
REGDUMP registers;
duint regwords[(sizeof(REGDUMP) - 128) / sizeof(duint)];
};
unsigned char changed[_countof(regwords)];
duint regContent[_countof(regwords)];
duint memAddress[MAX_MEMORY_OPERANDS];
duint memOldContent[MAX_MEMORY_OPERANDS];
duint memNewContent[MAX_MEMORY_OPERANDS];
size_t memOperandOffset = 0;
mParent = parent;
length = 0;
GetSystemTimes(nullptr, nullptr, &lastAccessed); //system user time, no GetTickCount64() for XP compatibility.
memset(&registers, 0, sizeof(registers));
try
{
if(mParent->traceFile.seek(fileOffset) == false)
throw std::exception();
//Process file content
while(!mParent->traceFile.atEnd() && length < maxLength)
{
if(!mParent->traceFile.isReadable())
throw std::exception();
unsigned char blockType;
unsigned char changedCountFlags[3]; //reg changed count, mem accessed count, flags
mParent->traceFile.read((char*)&blockType, 1);
if(blockType == 0)
{
if(mParent->traceFile.read((char*)&changedCountFlags, 3) != 3)
throw std::exception();
if(changedCountFlags[2] & 0x80) //Thread Id
mParent->traceFile.read((char*)&lastThreadId, 4);
threadId.push_back(lastThreadId);
if((changedCountFlags[2] & 0x0F) > 0) //Opcode
{
QByteArray opcode = mParent->traceFile.read(changedCountFlags[2] & 0x0F);
if(opcode.isEmpty())
throw std::exception();
opcodeOffset.push_back(opcodes.size());
opcodeSize.push_back(opcode.size());
opcodes.append(opcode);
}
else
throw std::exception();
if(changedCountFlags[0] > 0) //registers
{
int lastPosition = -1;
if(changedCountFlags[0] > _countof(regwords)) //Bad count?
throw std::exception();
if(mParent->traceFile.read((char*)changed, changedCountFlags[0]) != changedCountFlags[0])
throw std::exception();
if(mParent->traceFile.read((char*)regContent, changedCountFlags[0] * sizeof(duint)) != changedCountFlags[0] * sizeof(duint))
{
throw std::exception();
}
for(int i = 0; i < changedCountFlags[0]; i++)
{
lastPosition = lastPosition + changed[i] + 1;
if(lastPosition < _countof(regwords) && lastPosition >= 0)
regwords[lastPosition] = regContent[i];
else //out of bounds?
{
throw std::exception();
}
}
mRegisters.push_back(registers);
}
if(changedCountFlags[1] > 0) //memory
{
QByteArray memflags;
if(changedCountFlags[1] > _countof(memAddress)) //too many memory operands?
throw std::exception();
memflags = mParent->traceFile.read(changedCountFlags[1]);
if(memflags.length() < changedCountFlags[1])
throw std::exception();
memoryOperandOffset.push_back(memOperandOffset);
memOperandOffset += changedCountFlags[1];
if(mParent->traceFile.read((char*)memAddress, sizeof(duint) * changedCountFlags[1]) != sizeof(duint) * changedCountFlags[1])
throw std::exception();
if(mParent->traceFile.read((char*)memOldContent, sizeof(duint) * changedCountFlags[1]) != sizeof(duint) * changedCountFlags[1])
throw std::exception();
for(unsigned char i = 0; i < changedCountFlags[1]; i++)
{
if((memflags[i] & 1) == 0)
{
if(mParent->traceFile.read((char*)&memNewContent[i], sizeof(duint)) != sizeof(duint))
throw std::exception();
}
else
memNewContent[i] = memOldContent[i];
}
for(unsigned char i = 0; i < changedCountFlags[1]; i++)
{
memoryFlags.push_back(memflags[i]);
memoryAddress.push_back(memAddress[i]);
oldMemory.push_back(memOldContent[i]);
newMemory.push_back(memNewContent[i]);
}
}
else
memoryOperandOffset.push_back(memOperandOffset);
length++;
}
else
throw std::exception();
}
}
catch(const std::exception &)
{
mParent->error = true;
}
}
unsigned long long TraceFilePage::Length() const
{
return length;
}
REGDUMP TraceFilePage::Registers(unsigned long long index) const
{
return mRegisters.at(index);
}
void TraceFilePage::OpCode(unsigned long long index, unsigned char* buffer, int* opcodeSize) const
{
*opcodeSize = this->opcodeSize.at(index);
memcpy(buffer, opcodes.constData() + opcodeOffset.at(index), *opcodeSize);
}
DWORD TraceFilePage::ThreadId(unsigned long long index) const
{
return threadId.at(index);
}
int TraceFilePage::MemoryAccessCount(unsigned long long index) const
{
size_t a = memoryOperandOffset.at(index);
if(index == length - 1)
return memoryAddress.size() - a;
else
return memoryOperandOffset.at(index + 1) - a;
}
void TraceFilePage::MemoryAccessInfo(unsigned long long index, duint* address, duint* oldMemory, duint* newMemory, bool* isValid) const
{
auto count = MemoryAccessCount(index);
auto base = memoryOperandOffset.at(index);
for(size_t i = 0; i < count; i++)
{
address[i] = memoryAddress.at(base + i);
oldMemory[i] = this->oldMemory.at(base + i);
newMemory[i] = this->newMemory.at(base + i);
isValid[i] = true; // proposed flag
}
}

View File

@ -0,0 +1,68 @@
#ifndef TRACEFILEREADER_H
#define TRACEFILEREADER_H
#include "Bridge.h"
#include <QFile>
#include <atomic>
class TraceFileParser;
class TraceFilePage;
#define MAX_MEMORY_OPERANDS 32
class TraceFileReader : public QObject
{
Q_OBJECT
public:
TraceFileReader(QObject* parent = NULL);
bool Open(const QString & fileName);
void Close();
bool isError();
int Progress();
unsigned long long Length();
REGDUMP Registers(unsigned long long index);
void OpCode(unsigned long long index, unsigned char* buffer, int* opcodeSize);
DWORD ThreadId(unsigned long long index);
int MemoryAccessCount(unsigned long long index);
void MemoryAccessInfo(unsigned long long index, duint* address, duint* oldMemory, duint* newMemory, bool* isValid);
duint HashValue();
QString ExePath();
void purgeLastPage();
signals:
void parseFinished();
public slots:
void parseFinishedSlot();
private:
typedef std::pair<unsigned long long, unsigned long long> Range;
struct RangeCompare //from addrinfo.h
{
bool operator()(const Range & a, const Range & b) const //a before b?
{
return a.second < b.first;
}
};
QFile traceFile;
unsigned long long length;
duint hashValue;
QString EXEPath;
std::vector<std::pair<unsigned long long, Range>> fileIndex; //index;<file offset;length>
std::atomic<int> progress;
bool error;
TraceFilePage* lastAccessedPage;
unsigned long long lastAccessedIndexOffset;
friend class TraceFileParser;
friend class TraceFilePage;
TraceFileParser* parser;
std::map<Range, TraceFilePage, RangeCompare> pages;
TraceFilePage* getPage(unsigned long long index, unsigned long long* base);
};
#endif //TRACEFILEREADER_H

View File

@ -0,0 +1,41 @@
#pragma once
#include <QThread>
#include "TraceFileReader.h"
class TraceFileParser : public QThread
{
Q_OBJECT
friend class TraceFileReader;
TraceFileParser(TraceFileReader* parent) : QThread(parent) {}
static void readFileHeader(TraceFileReader* that);
void run();
};
class TraceFilePage
{
public:
TraceFilePage(TraceFileReader* parent, unsigned long long fileOffset, unsigned long long maxLength);
unsigned long long Length() const;
REGDUMP Registers(unsigned long long index) const;
void OpCode(unsigned long long index, unsigned char* buffer, int* opcodeSize) const;
DWORD ThreadId(unsigned long long index) const;
int MemoryAccessCount(unsigned long long index) const;
void MemoryAccessInfo(unsigned long long index, duint* address, duint* oldMemory, duint* newMemory, bool* isValid) const;
FILETIME lastAccessed; //system user time
private:
friend class TraceFileReader;
TraceFileReader* mParent;
std::vector<REGDUMP> mRegisters;
QByteArray opcodes;
std::vector<size_t> opcodeOffset;
std::vector<unsigned char> opcodeSize;
std::vector<size_t> memoryOperandOffset;
std::vector<char> memoryFlags;
std::vector<duint> memoryAddress;
std::vector<duint> oldMemory;
std::vector<duint> newMemory;
std::vector<DWORD> threadId;
unsigned long long length;
};

View File

@ -0,0 +1,135 @@
#include "TraceFileReader.h"
#include "TraceFileSearch.h"
#include "capstone_wrapper.h"
static bool inRange(duint value, duint start, duint end)
{
return value >= start && value <= end;
}
static QString getIndexText(TraceFileReader* file, duint index)
{
QString indexString;
indexString = QString::number(index, 16).toUpper();
if(file->Length() < 16)
return indexString;
int digits;
digits = floor(log2(file->Length() - 1) / 4) + 1;
digits -= indexString.size();
while(digits > 0)
{
indexString = '0' + indexString;
digits = digits - 1;
}
return indexString;
}
int TraceFileSearchConstantRange(TraceFileReader* file, duint start, duint end)
{
int count = 0;
Capstone cp;
QString title;
if(start == end)
title = QCoreApplication::translate("TraceFileSearch", "Constant: %1").arg(ToPtrString(start));
else
title = QCoreApplication::translate("TraceFileSearch", "Range: %1-%2").arg(ToPtrString(start)).arg(ToPtrString(end));
GuiReferenceInitialize(title.toUtf8().constData());
GuiReferenceAddColumn(sizeof(duint) * 2, QCoreApplication::translate("TraceFileSearch", "Address").toUtf8().constData());
GuiReferenceAddColumn(sizeof(duint) * 2, QCoreApplication::translate("TraceFileSearch", "Index").toUtf8().constData());
GuiReferenceAddColumn(100, QCoreApplication::translate("TraceFileSearch", "Disassembly").toUtf8().constData());
GuiReferenceSetRowCount(0);
for(unsigned long long index = 0; index < file->Length(); index++)
{
bool found = false;
//Registers
#define FINDREG(fieldName) found |= inRange(file->Registers(index).regcontext.##fieldName, start, end)
FINDREG(cax);
FINDREG(cbx);
FINDREG(ccx);
FINDREG(cdx);
FINDREG(csp);
FINDREG(cbp);
FINDREG(csi);
FINDREG(cdi);
FINDREG(cip);
//Memory
duint memAddr[MAX_MEMORY_OPERANDS];
duint memOldContent[MAX_MEMORY_OPERANDS];
duint memNewContent[MAX_MEMORY_OPERANDS];
bool isValid[MAX_MEMORY_OPERANDS];
int memAccessCount = file->MemoryAccessCount(index);
if(memAccessCount > 0)
{
file->MemoryAccessInfo(index, memAddr, memOldContent, memNewContent, isValid);
for(size_t i = 0; i < memAccessCount; i++)
{
found |= inRange(memAddr[i], start, end);
found |= inRange(memOldContent[i], start, end);
found |= inRange(memNewContent[i], start, end);
}
}
//Constants: TO DO
//Populate reference view
if(found)
{
GuiReferenceSetRowCount(count + 1);
GuiReferenceSetCellContent(count, 0, ToPtrString(file->Registers(index).regcontext.cip).toUtf8().constData());
GuiReferenceSetCellContent(count, 1, getIndexText(file, index).toUtf8().constData());
unsigned char opcode[16];
int opcodeSize = 0;
file->OpCode(index, opcode, &opcodeSize);
cp.Disassemble(file->Registers(index).regcontext.cip, opcode, opcodeSize);
GuiReferenceSetCellContent(count, 2, cp.InstructionText(true).c_str());
//GuiReferenceSetCurrentTaskProgress; GuiReferenceSetProgress
count++;
}
}
return count;
}
int TraceFileSearchMemReference(TraceFileReader* file, duint address)
{
int count = 0;
Capstone cp;
GuiReferenceInitialize(QCoreApplication::translate("TraceFileSearch", "Reference").toUtf8().constData());
GuiReferenceAddColumn(sizeof(duint) * 2, QCoreApplication::translate("TraceFileSearch", "Address").toUtf8().constData());
GuiReferenceAddColumn(sizeof(duint) * 2, QCoreApplication::translate("TraceFileSearch", "Index").toUtf8().constData());
GuiReferenceAddColumn(100, QCoreApplication::translate("TraceFileSearch", "Disassembly").toUtf8().constData());
GuiReferenceSetRowCount(0);
for(unsigned long long index = 0; index < file->Length(); index++)
{
bool found = false;
//Memory
duint memAddr[MAX_MEMORY_OPERANDS];
duint memOldContent[MAX_MEMORY_OPERANDS];
duint memNewContent[MAX_MEMORY_OPERANDS];
bool isValid[MAX_MEMORY_OPERANDS];
int memAccessCount = file->MemoryAccessCount(index);
if(memAccessCount > 0)
{
file->MemoryAccessInfo(index, memAddr, memOldContent, memNewContent, isValid);
for(size_t i = 0; i < memAccessCount; i++)
{
found |= inRange(memAddr[i], address, address + sizeof(duint) - 1);
}
//Constants: TO DO
//Populate reference view
if(found)
{
GuiReferenceSetRowCount(count + 1);
GuiReferenceSetCellContent(count, 0, ToPtrString(file->Registers(index).regcontext.cip).toUtf8().constData());
GuiReferenceSetCellContent(count, 1, getIndexText(file, index).toUtf8().constData());
unsigned char opcode[16];
int opcodeSize = 0;
file->OpCode(index, opcode, &opcodeSize);
cp.Disassemble(file->Registers(index).regcontext.cip, opcode, opcodeSize);
GuiReferenceSetCellContent(count, 2, cp.InstructionText(true).c_str());
//GuiReferenceSetCurrentTaskProgress; GuiReferenceSetProgress
count++;
}
}
}
return count;
}

View File

@ -0,0 +1,8 @@
#ifndef TRACEFILESEARCH_H
#define TRACEFILESEARCH_H
#include "Bridge.h"
class TraceFileReader;
int TraceFileSearchConstantRange(TraceFileReader* file, duint start, duint end);
int TraceFileSearchMemReference(TraceFileReader* file, duint address);
#endif //TRACEFILESEARCH_H

View File

@ -350,6 +350,7 @@ Configuration::Configuration() : QObject(), noMoreMsgbox(false)
tabOrderUint.insert("ThreadsTab", curTab++);
tabOrderUint.insert("SnowmanTab", curTab++);
tabOrderUint.insert("HandlesTab", curTab++);
tabOrderUint.insert("TraceTab", curTab++);
defaultUints.insert("TabOrder", tabOrderUint);
//font settings
@ -598,6 +599,7 @@ Configuration::Configuration() : QObject(), noMoreMsgbox(false)
defaultShortcuts.insert("ActionWatchDwordQword", Shortcut({tr("Actions"), tr("Watch DWORD/QWORD")}));
defaultShortcuts.insert("ActionDataCopy", Shortcut({tr("Actions"), tr("Data Copy")}));
defaultShortcuts.insert("ActionCopyFileOffset", Shortcut({tr("Actions"), tr("Copy File Offset")}));
defaultShortcuts.insert("ActionToggleRunTrace", Shortcut({tr("Actions"), tr("Start or Stop Run Trace")}));
Shortcuts = defaultShortcuts;

View File

@ -184,7 +184,10 @@ SOURCES += \
Src/Gui/MessagesBreakpoints.cpp \
Src/Gui/AboutDialog.cpp \
Src/Gui/BreakpointMenu.cpp \
Src/Gui/ComboBoxDialog.cpp
Src/Gui/ComboBoxDialog.cpp \
Src/Tracer/TraceBrowser.cpp \
Src/Tracer/TraceFileReader.cpp \
Src/Tracer/TraceFileSearch.cpp
HEADERS += \
@ -302,7 +305,11 @@ HEADERS += \
Src/Gui/AboutDialog.h \
Src/Gui/BreakpointMenu.h \
Src/Gui/ComboBoxDialog.h \
Src/Utils/VaHistory.h
Src/Utils/VaHistory.h \
Src/Tracer/TraceBrowser.h \
Src/Tracer/TraceFileReader.h \
Src/Tracer/TraceFileReaderInternal.h \
Src/Tracer/TraceFileSearch.h
FORMS += \

View File

@ -219,7 +219,10 @@ SOURCES += \
gui/Src/Gui/LocalVarsView.cpp \
gui/Src/Gui/MessagesBreakpoints.cpp \
gui/Src/Gui/AboutDialog.cpp \
gui/Src/Gui/BreakpointMenu.cpp
gui/Src/Gui/BreakpointMenu.cpp \
gui/Src/Tracer/TraceBrowser.cpp \
gui/Src/Tracer/TraceFileReader.cpp \
gui/Src/Tracer/TraceFileSearch.cpp
HEADERS += \
gui/Src/Exports.h \
@ -449,7 +452,11 @@ HEADERS += \
gui/Src/Gui/LocalVarsView.h \
gui/Src/Gui/MessagesBreakpoints.h \
gui/Src/Gui/AboutDialog.h \
gui/Src/Gui/BreakpointMenu.h
gui/Src/Gui/BreakpointMenu.h \
gui/Src/Tracer/TraceBrowser.h \
gui/Src/Tracer/TraceFileReader.h \
gui/Src/Tracer/TraceFileReaderInternal.h \
gui/Src/Tracer/TraceFileSearch.h
FORMS += \
gui/Src/Gui/AppearanceDialog.ui \