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:
parent
9959278863
commit
390bf4c5ca
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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[]);
|
|
@ -407,4 +407,9 @@ bool DbCheckHash(duint currentHash)
|
|||
dbhash = currentHash;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
duint DbGetHash()
|
||||
{
|
||||
return dbhash;
|
||||
}
|
|
@ -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
|
|
@ -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!"));
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -47,6 +47,7 @@ namespace Exprfunc
|
|||
|
||||
duint trenabled(duint addr);
|
||||
duint trhitcount(duint addr);
|
||||
duint trisruntraceenabled();
|
||||
duint gettickcount();
|
||||
|
||||
duint readbyte(duint addr);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -154,6 +154,8 @@ signals:
|
|||
void closeApplication();
|
||||
void flushLog();
|
||||
void getDumpAttention();
|
||||
void openTraceFile(const QString & fileName);
|
||||
void updateTraceBrowser();
|
||||
|
||||
private:
|
||||
CRITICAL_SECTION csBridge;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,6 +97,7 @@ public slots:
|
|||
void ActionTraceRecordByteSlot();
|
||||
void ActionTraceRecordWordSlot();
|
||||
void ActionTraceRecordDisableSlot();
|
||||
void ActionTraceRecordToggleRunTraceSlot();
|
||||
void displayWarningSlot(QString title, QString text);
|
||||
void labelHelpSlot();
|
||||
void analyzeSingleFunctionSlot();
|
||||
|
|
|
@ -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."));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
@ -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
|
|
@ -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(®isters, 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(®isters, 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
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 += \
|
||||
|
|
|
@ -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 \
|
||||
|
|
Loading…
Reference in New Issue