1
0
Fork 0
x64dbg/x64_dbg_dbg/debugger.cpp

2007 lines
61 KiB
C++
Raw Blame History

#include "debugger.h"
#include "argument.h"
#include "variable.h"
#include "console.h"
#include "threading.h"
#include "value.h"
#include "instruction.h"
#include "memory.h"
#include "_exports.h"
#include "addrinfo.h"
#include "plugin_loader.h"
#include "x64_dbg.h"
#include "disasm_helper.h"
#include "symbolinfo.h"
#include "thread.h"
#include "disasm_fast.h"
#include "BeaEngine\BeaEngine.h"
#include "DeviceNameResolver\DeviceNameResolver.h"
static PROCESS_INFORMATION g_pi= {0,0,0,0};
static char szFileName[MAX_PATH]="";
static char szBaseFileName[MAX_PATH]="";
static bool bFileIsDll=false;
static uint pDebuggedBase=0;
static uint pDebuggedEntry=0;
static bool isStepping=false;
static bool isPausedByUser=false;
static bool isDetachedByUser=false;
static bool bScyllaLoaded=false;
static bool bIsAttached=false;
static bool bSkipExceptions=false;
static int ecount=0;
static std::vector<ExceptionRange> ignoredExceptionRange;
//Superglobal variables
char sqlitedb[deflen]="";
PROCESS_INFORMATION* fdProcessInfo=&g_pi;
//static functions
static void cbStep();
static void cbSystemBreakpoint(void* ExceptionData);
static void cbUserBreakpoint();
void dbgdisablebpx()
{
BREAKPOINT* list;
int bpcount=bpgetlist(&list);
for(int i=0; i<bpcount; i++)
{
if(list[i].type==BPNORMAL and IsBPXEnabled(list[i].addr))
DeleteBPX(list[i].addr);
}
}
void dbgenablebpx()
{
BREAKPOINT* list;
int bpcount=bpgetlist(&list);
for(int i=0; i<bpcount; i++)
{
if(list[i].type==BPNORMAL and !IsBPXEnabled(list[i].addr) and list[i].enabled)
SetBPX(list[i].addr, list[i].titantype, (void*)cbUserBreakpoint);
}
}
bool dbgisrunning()
{
if(!waitislocked(WAITID_RUN))
return true;
return false;
}
void dbgsetskipexceptions(bool skip)
{
bSkipExceptions=skip;
}
void dbgclearignoredexceptions()
{
std::vector<ExceptionRange>().swap(ignoredExceptionRange);
}
void dbgaddignoredexception(ExceptionRange range)
{
ignoredExceptionRange.push_back(range);
}
bool dbgisignoredexception(unsigned int exception)
{
for(unsigned int i=0; i<ignoredExceptionRange.size(); i++)
{
unsigned int curStart=ignoredExceptionRange.at(i).start;
unsigned int curEnd=ignoredExceptionRange.at(i).end;
if(exception>=curStart && exception<=curEnd)
return true;
}
return false;
}
void DebugUpdateGui(uint disasm_addr, bool stack)
{
uint cip=GetContextData(UE_CIP);
GuiDisasmAt(disasm_addr, cip);
if(stack)
{
uint csp=GetContextData(UE_CSP);
GuiStackDumpAt(csp, csp);
}
char modname[MAX_MODULE_SIZE]="";
if(!modnamefromaddr(disasm_addr, modname, true))
*modname=0;
char title[1024]="";
sprintf(title, "File: %s - PID: %X - Module: %s - Thread: %X", szBaseFileName, fdProcessInfo->dwProcessId, modname, ((DEBUG_EVENT*)GetDebugData())->dwThreadId);
GuiUpdateWindowTitle(title);
GuiUpdateAllViews();
}
static void cbUserBreakpoint()
{
BREAKPOINT bp;
BRIDGEBP pluginBp;
PLUG_CB_BREAKPOINT bpInfo;
bpInfo.breakpoint=0;
if(!bpget(GetContextData(UE_CIP), BPNORMAL, 0, &bp) and bp.enabled)
dputs("breakpoint reached not in list!");
else
{
const char* bptype="INT3";
int titantype=bp.titantype;
if((titantype&UE_BREAKPOINT_TYPE_UD2)==UE_BREAKPOINT_TYPE_UD2)
bptype="UD2";
else if((titantype&UE_BREAKPOINT_TYPE_LONG_INT3)==UE_BREAKPOINT_TYPE_LONG_INT3)
bptype="LONG INT3";
const char* symbolicname=symgetsymbolicname(bp.addr);
if(symbolicname)
{
if(*bp.name)
dprintf("%s breakpoint \"%s\" at %s ("fhex")!\n", bptype, bp.name, symbolicname, bp.addr);
else
dprintf("%s breakpoint at %s ("fhex")!\n", bptype, symbolicname, bp.addr);
}
else
{
if(*bp.name)
dprintf("%s breakpoint \"%s\" at "fhex"!\n", bptype, bp.name, bp.addr);
else
dprintf("%s breakpoint at "fhex"!\n", bptype, bp.addr);
}
if(bp.singleshoot)
bpdel(bp.addr, BPNORMAL);
bptobridge(&bp, &pluginBp);
bpInfo.breakpoint=&pluginBp;
}
DebugUpdateGui(GetContextData(UE_CIP), true);
GuiSetDebugState(paused);
//lock
lock(WAITID_RUN);
bSkipExceptions=false;
PLUG_CB_PAUSEDEBUG pauseInfo;
pauseInfo.reserved=0;
plugincbcall(CB_PAUSEDEBUG, &pauseInfo);
plugincbcall(CB_BREAKPOINT, &bpInfo);
wait(WAITID_RUN);
}
static void cbHardwareBreakpoint(void* ExceptionAddress)
{
uint cip=GetContextData(UE_CIP);
BREAKPOINT bp;
BRIDGEBP pluginBp;
PLUG_CB_BREAKPOINT bpInfo;
bpInfo.breakpoint=0;
if(!bpget((uint)ExceptionAddress, BPHARDWARE, 0, &bp))
dputs("hardware breakpoint reached not in list!");
else
{
const char* bpsize="";
switch(bp.titantype&0xF) //size
{
case UE_HARDWARE_SIZE_1:
bpsize="byte, ";
break;
case UE_HARDWARE_SIZE_2:
bpsize="word, ";
break;
case UE_HARDWARE_SIZE_4:
bpsize="dword, ";
break;
#ifdef _WIN64
case UE_HARDWARE_SIZE_8:
bpsize="qword, ";
break;
#endif //_WIN64
}
const char* bptype="";
switch((bp.titantype>>4)&0xF) //type
{
case UE_HARDWARE_EXECUTE:
bptype="execute";
bpsize="";
break;
case UE_HARDWARE_READWRITE:
bptype="read/write";
break;
case UE_HARDWARE_WRITE:
bptype="write";
break;
}
const char* symbolicname=symgetsymbolicname(bp.addr);
if(symbolicname)
{
if(*bp.name)
dprintf("hardware breakpoint (%s%s) \"%s\" at %s ("fhex")!\n", bpsize, bptype, bp.name, symbolicname, bp.addr);
else
dprintf("hardware breakpoint (%s%s) at %s ("fhex")!\n", bpsize, bptype, symbolicname, bp.addr);
}
else
{
if(*bp.name)
dprintf("hardware breakpoint (%s%s) \"%s\" at "fhex"!\n", bpsize, bptype, bp.name, bp.addr);
else
dprintf("hardware breakpoint (%s%s) at "fhex"!\n", bpsize, bptype, bp.addr);
}
bptobridge(&bp, &pluginBp);
bpInfo.breakpoint=&pluginBp;
}
DebugUpdateGui(cip, true);
GuiSetDebugState(paused);
//lock
lock(WAITID_RUN);
bSkipExceptions=false;
PLUG_CB_PAUSEDEBUG pauseInfo;
pauseInfo.reserved=0;
plugincbcall(CB_PAUSEDEBUG, &pauseInfo);
plugincbcall(CB_BREAKPOINT, &bpInfo);
wait(WAITID_RUN);
}
static void cbMemoryBreakpoint(void* ExceptionAddress)
{
uint cip=GetContextData(UE_CIP);
uint size;
uint base=memfindbaseaddr(fdProcessInfo->hProcess, (uint)ExceptionAddress, &size);
BREAKPOINT bp;
BRIDGEBP pluginBp;
PLUG_CB_BREAKPOINT bpInfo;
bpInfo.breakpoint=0;
if(!bpget(base, BPMEMORY, 0, &bp))
dputs("memory breakpoint reached not in list!");
else
{
const char* bptype="";
switch(bp.titantype)
{
case UE_MEMORY_READ:
bptype=" (read)";
break;
case UE_MEMORY_WRITE:
bptype=" (write)";
break;
case UE_MEMORY_EXECUTE:
bptype=" (execute)";
break;
case UE_MEMORY:
bptype=" (read/write/execute)";
break;
}
const char* symbolicname=symgetsymbolicname(bp.addr);
if(symbolicname)
{
if(*bp.name)
dprintf("memory breakpoint%s \"%s\" at %s ("fhex", "fhex")!\n", bptype, bp.name, symbolicname, bp.addr, ExceptionAddress);
else
dprintf("memory breakpoint%s at %s ("fhex", "fhex")!\n", bptype, symbolicname, bp.addr, ExceptionAddress);
}
else
{
if(*bp.name)
dprintf("memory breakpoint%s \"%s\" at "fhex" ("fhex")!\n", bptype, bp.name, bp.addr, ExceptionAddress);
else
dprintf("memory breakpoint%s at "fhex" ("fhex")!\n", bptype, bp.addr, ExceptionAddress);
}
bptobridge(&bp, &pluginBp);
bpInfo.breakpoint=&pluginBp;
}
if(bp.singleshoot)
bpdel(bp.addr, BPMEMORY); //delete from breakpoint list
DebugUpdateGui(cip, true);
GuiSetDebugState(paused);
//lock
lock(WAITID_RUN);
bSkipExceptions=false;
PLUG_CB_PAUSEDEBUG pauseInfo;
pauseInfo.reserved=0;
plugincbcall(CB_PAUSEDEBUG, &pauseInfo);
plugincbcall(CB_BREAKPOINT, &bpInfo);
wait(WAITID_RUN);
}
static BOOL CALLBACK SymRegisterCallbackProc64(HANDLE hProcess, ULONG ActionCode, ULONG64 CallbackData, ULONG64 UserContext)
{
UNREFERENCED_PARAMETER(hProcess);
UNREFERENCED_PARAMETER(UserContext);
PIMAGEHLP_CBA_EVENT evt;
switch (ActionCode)
{
case CBA_EVENT:
{
evt=(PIMAGEHLP_CBA_EVENT)CallbackData;
const char* text=(const char*)evt->desc;
int len=strlen(text);
bool suspress=false;
for(int i=0; i<len; i++)
if(text[i]==0x08)
{
suspress=true;
break;
}
int percent=0;
static bool zerobar=false;
if(zerobar)
{
zerobar=false;
GuiSymbolSetProgress(0);
}
if(strstr(text, " bytes - "))
{
char* newtext=(char*)emalloc(len+1, "SymRegisterCallbackProc64:newtext");
strcpy(newtext, text);
strstr(newtext, " bytes - ")[8]=0;
GuiSymbolLogAdd(newtext);
efree(newtext, "SymRegisterCallbackProc64:newtext");
suspress=true;
}
else if(strstr(text, " copied "))
{
GuiSymbolSetProgress(100);
GuiSymbolLogAdd(" downloaded!\n");
suspress=true;
zerobar=true;
}
else if(sscanf(text, "%*s %d percent", &percent)==1 or sscanf(text, "%d percent", &percent)==1)
{
GuiSymbolSetProgress(percent);
suspress=true;
}
if(!suspress)
GuiSymbolLogAdd(text);
}
break;
default:
{
return FALSE;
}
}
return TRUE;
}
static bool cbSetModuleBreakpoints(const BREAKPOINT* bp)
{
//TODO: more breakpoint types
switch(bp->type)
{
case BPNORMAL:
if(bp->enabled)
{
if(!SetBPX(bp->addr, bp->titantype, (void*)cbUserBreakpoint))
dprintf("could not set breakpoint "fhex"!\n", bp->addr);
}
break;
case BPMEMORY:
if(bp->enabled)
{
uint size=0;
memfindbaseaddr(fdProcessInfo->hProcess, bp->addr, &size);
bool restore=false;
if(!bp->singleshoot)
restore=true;
if(!SetMemoryBPXEx(bp->addr, size, bp->titantype, restore, (void*)cbMemoryBreakpoint))
dprintf("could not set memory breakpoint "fhex"!\n", bp->addr);
}
break;
case BPHARDWARE:
if(bp->enabled)
{
if(!SetHardwareBreakPoint(bp->addr, (bp->titantype>>8)&0xF, (bp->titantype>>4)&0xF, bp->titantype&0xF, (void*)cbHardwareBreakpoint))
dprintf("could not set hardware breakpoint "fhex"!\n", bp->addr);
}
break;
default:
break;
}
return true;
}
static bool cbRemoveModuleBreakpoints(const BREAKPOINT* bp)
{
//TODO: more breakpoint types
switch(bp->type)
{
case BPNORMAL:
if(IsBPXEnabled(bp->addr))
DeleteBPX(bp->addr);
break;
case BPMEMORY:
if(bp->enabled)
RemoveMemoryBPX(bp->addr, 0);
break;
case BPHARDWARE:
if(bp->enabled)
DeleteHardwareBreakPoint((bp->titantype>>8)&0xF);
break;
default:
break;
}
return true;
}
static void cbStep()
{
isStepping=false;
DebugUpdateGui(GetContextData(UE_CIP), true);
GuiSetDebugState(paused);
PLUG_CB_STEPPED stepInfo;
stepInfo.reserved=0;
plugincbcall(CB_STEPPED, &stepInfo);
//lock
lock(WAITID_RUN);
bSkipExceptions=false;
PLUG_CB_PAUSEDEBUG pauseInfo;
pauseInfo.reserved=0;
plugincbcall(CB_PAUSEDEBUG, &pauseInfo);
wait(WAITID_RUN);
}
static void cbRtrFinalStep()
{
DebugUpdateGui(GetContextData(UE_CIP), true);
GuiSetDebugState(paused);
//lock
lock(WAITID_RUN);
bSkipExceptions=false;
PLUG_CB_PAUSEDEBUG pauseInfo;
pauseInfo.reserved=0;
plugincbcall(CB_PAUSEDEBUG, &pauseInfo);
wait(WAITID_RUN);
}
static unsigned char getCIPch()
{
unsigned char ch=0x90;
uint cip=GetContextData(UE_CIP);
memread(fdProcessInfo->hProcess, (void*)cip, &ch, 1, 0);
return ch;
}
static void cbRtrStep()
{
unsigned int cipch=getCIPch();
if(cipch==0xC3 or cipch==0xC2)
cbRtrFinalStep();
else
StepOver((void*)cbRtrStep);
}
///custom handlers
static void cbCreateProcess(CREATE_PROCESS_DEBUG_INFO* CreateProcessInfo)
{
void* base=CreateProcessInfo->lpBaseOfImage;
char DebugFileName[deflen]="";
if(!GetMappedFileNameA(fdProcessInfo->hProcess, base, DebugFileName, deflen))
strcpy(DebugFileName, "??? (GetMappedFileName failed)");
else
DevicePathToPathA(DebugFileName, DebugFileName, deflen);
dprintf("Process Started: "fhex" %s\n", base, DebugFileName);
//init program database
int len=strlen(szFileName);
while(szFileName[len]!='\\' && len!=0)
len--;
if(len)
len++;
strcpy(sqlitedb, szFileName+len);
#ifdef _WIN64
strcat(sqlitedb, ".dd64");
#else
strcat(sqlitedb, ".dd32");
#endif // _WIN64
sprintf(dbpath, "%s\\%s", sqlitedb_basedir, sqlitedb);
dprintf("Database file: %s\n", dbpath);
dbinit();
SymSetOptions(SYMOPT_DEBUG|SYMOPT_LOAD_LINES);
GuiSymbolLogClear();
SymInitialize(fdProcessInfo->hProcess, 0, false); //initialize symbols
SymRegisterCallback64(fdProcessInfo->hProcess, SymRegisterCallbackProc64, 0);
SymLoadModuleEx(fdProcessInfo->hProcess, CreateProcessInfo->hFile, DebugFileName, 0, (DWORD64)base, 0, 0, 0);
IMAGEHLP_MODULE64 modInfo;
memset(&modInfo, 0, sizeof(modInfo));
modInfo.SizeOfStruct=sizeof(modInfo);
if(SymGetModuleInfo64(fdProcessInfo->hProcess, (DWORD64)base, &modInfo))
modload((uint)base, modInfo.ImageSize, modInfo.ImageName);
bpenumall(0); //update breakpoint list
char modname[256]="";
if(modnamefromaddr((uint)base, modname, true))
bpenumall(cbSetModuleBreakpoints, modname);
if(!bFileIsDll and !bIsAttached) //Set entry breakpoint
{
pDebuggedBase=(uint)CreateProcessInfo->lpBaseOfImage; //debugged base = executable
char command[256]="";
if(settingboolget("Events", "TlsCallbacks"))
{
DWORD NumberOfCallBacks=0;
TLSGrabCallBackData(DebugFileName, 0, &NumberOfCallBacks);
if(NumberOfCallBacks)
{
dprintf("TLS Callbacks: %d\n", NumberOfCallBacks);
uint* TLSCallBacks=(uint*)emalloc(NumberOfCallBacks*sizeof(uint), "cbCreateProcess:TLSCallBacks");
if(!TLSGrabCallBackData(DebugFileName, TLSCallBacks, &NumberOfCallBacks))
dputs("failed to get TLS callback addresses!");
else
{
for(int i=0; i<NumberOfCallBacks; i++)
{
sprintf(command, "bp "fhex",\"TLS Callback %d\",ss", TLSCallBacks[i], i+1);
cmddirectexec(dbggetcommandlist(), command);
}
}
efree(TLSCallBacks, "cbCreateProcess:TLSCallBacks");
}
}
if(settingboolget("Events", "EntryBreakpoint"))
{
sprintf(command, "bp "fhex",\"entry breakpoint\",ss", CreateProcessInfo->lpStartAddress);
cmddirectexec(dbggetcommandlist(), command);
}
}
GuiUpdateBreakpointsView();
//call plugin callback
PLUG_CB_CREATEPROCESS callbackInfo;
callbackInfo.CreateProcessInfo=CreateProcessInfo;
callbackInfo.modInfo=&modInfo;
callbackInfo.DebugFileName=DebugFileName;
callbackInfo.fdProcessInfo=fdProcessInfo;
plugincbcall(CB_CREATEPROCESS, &callbackInfo);
//update thread list
CREATE_THREAD_DEBUG_INFO threadInfo;
threadInfo.hThread=CreateProcessInfo->hThread;
threadInfo.lpStartAddress=CreateProcessInfo->lpStartAddress;
threadInfo.lpThreadLocalBase=CreateProcessInfo->lpThreadLocalBase;
threadcreate(&threadInfo);
}
static void cbExitProcess(EXIT_PROCESS_DEBUG_INFO* ExitProcess)
{
PLUG_CB_EXITPROCESS callbackInfo;
callbackInfo.ExitProcess=ExitProcess;
plugincbcall(CB_EXITPROCESS, &callbackInfo);
//Cleanup
SymCleanup(fdProcessInfo->hProcess);
}
static void cbCreateThread(CREATE_THREAD_DEBUG_INFO* CreateThread)
{
threadcreate(CreateThread); //update thread list
DWORD dwThreadId=((DEBUG_EVENT*)GetDebugData())->dwThreadId;
PLUG_CB_CREATETHREAD callbackInfo;
callbackInfo.CreateThread=CreateThread;
callbackInfo.dwThreadId=dwThreadId;
plugincbcall(CB_CREATETHREAD, &callbackInfo);
dprintf("Thread %X created\n", dwThreadId);
if(settingboolget("Events", "ThreadStart"))
{
//update GUI
DebugUpdateGui(GetContextData(UE_CIP), true);
GuiSetDebugState(paused);
//lock
lock(WAITID_RUN);
PLUG_CB_PAUSEDEBUG pauseInfo;
pauseInfo.reserved=0;
plugincbcall(CB_PAUSEDEBUG, &pauseInfo);
wait(WAITID_RUN);
}
if(settingboolget("Events", "ThreadEntry"))
{
char command[256]="";
sprintf(command, "bp "fhex",\"Thread %X\",ss", CreateThread->lpStartAddress, dwThreadId);
cmddirectexec(dbggetcommandlist(), command);
}
}
static void cbExitThread(EXIT_THREAD_DEBUG_INFO* ExitThread)
{
DWORD dwThreadId=((DEBUG_EVENT*)GetDebugData())->dwThreadId;
PLUG_CB_EXITTHREAD callbackInfo;
callbackInfo.ExitThread=ExitThread;
callbackInfo.dwThreadId=dwThreadId;
plugincbcall(CB_EXITTHREAD, &callbackInfo);
threadexit(dwThreadId);
dprintf("Thread %X exit\n", dwThreadId);
if(settingboolget("Events", "ThreadEnd"))
{
//update GUI
DebugUpdateGui(GetContextData(UE_CIP), true);
GuiSetDebugState(paused);
//lock
lock(WAITID_RUN);
PLUG_CB_PAUSEDEBUG pauseInfo;
pauseInfo.reserved=0;
plugincbcall(CB_PAUSEDEBUG, &pauseInfo);
wait(WAITID_RUN);
}
}
static void cbSystemBreakpoint(void* ExceptionData)
{
//log message
dputs("system breakpoint reached!");
bSkipExceptions=false; //we are not skipping first-chance exceptions
uint cip=GetContextData(UE_CIP);
GuiDumpAt(memfindbaseaddr(fdProcessInfo->hProcess, cip, 0)); //dump somewhere
//plugin callbacks
PLUG_CB_SYSTEMBREAKPOINT callbackInfo;
callbackInfo.reserved=0;
plugincbcall(CB_SYSTEMBREAKPOINT, &callbackInfo);
if(settingboolget("Events", "SystemBreakpoint"))
{
//update GUI
DebugUpdateGui(cip, true);
GuiSetDebugState(paused);
//lock
lock(WAITID_RUN);
PLUG_CB_PAUSEDEBUG pauseInfo;
pauseInfo.reserved=0;
plugincbcall(CB_PAUSEDEBUG, &pauseInfo);
wait(WAITID_RUN);
}
}
static void cbLoadDll(LOAD_DLL_DEBUG_INFO* LoadDll)
{
void* base=LoadDll->lpBaseOfDll;
char DLLDebugFileName[deflen]="";
if(!GetMappedFileNameA(fdProcessInfo->hProcess, base, DLLDebugFileName, deflen))
strcpy(DLLDebugFileName, "??? (GetMappedFileName failed)");
else
DevicePathToPathA(DLLDebugFileName, DLLDebugFileName, deflen);
dprintf("DLL Loaded: "fhex" %s\n", base, DLLDebugFileName);
SymLoadModuleEx(fdProcessInfo->hProcess, LoadDll->hFile, DLLDebugFileName, 0, (DWORD64)base, 0, 0, 0);
IMAGEHLP_MODULE64 modInfo;
memset(&modInfo, 0, sizeof(modInfo));
modInfo.SizeOfStruct=sizeof(IMAGEHLP_MODULE64);
if(SymGetModuleInfo64(fdProcessInfo->hProcess, (DWORD64)base, &modInfo))
modload((uint)base, modInfo.ImageSize, modInfo.ImageName);
bpenumall(0); //update breakpoint list
char modname[256]="";
if(modnamefromaddr((uint)base, modname, true))
bpenumall(cbSetModuleBreakpoints, modname);
bool bAlreadySetEntry=false;
if(bFileIsDll and !stricmp(DLLDebugFileName, szFileName) and !bIsAttached) //Set entry breakpoint
{
pDebuggedBase=(uint)base;
char command[256]="";
if(settingboolget("Events", "EntryBreakpoint"))
{
bAlreadySetEntry=true;
sprintf(command, "bp "fhex",\"entry breakpoint\",ss", pDebuggedBase+pDebuggedEntry);
cmddirectexec(dbggetcommandlist(), command);
}
}
GuiUpdateBreakpointsView();
//plugin callback
PLUG_CB_LOADDLL callbackInfo;
callbackInfo.LoadDll=LoadDll;
callbackInfo.modInfo=&modInfo;
callbackInfo.modname=modname;
plugincbcall(CB_LOADDLL, &callbackInfo);
if(settingboolget("Events", "DllLoad"))
{
//update GUI
DebugUpdateGui(GetContextData(UE_CIP), true);
GuiSetDebugState(paused);
//lock
lock(WAITID_RUN);
PLUG_CB_PAUSEDEBUG pauseInfo;
pauseInfo.reserved=0;
plugincbcall(CB_PAUSEDEBUG, &pauseInfo);
wait(WAITID_RUN);
}
if(settingboolget("Events", "DllEntry") && !bAlreadySetEntry)
{
uint oep=GetPE32Data(DLLDebugFileName, 0, UE_OEP);
if(oep)
{
char command[256]="";
sprintf(command, "bp "fhex",\"DllMain (%s)\",ss", oep+(uint)base, modname);
cmddirectexec(dbggetcommandlist(), command);
}
}
}
static void cbUnloadDll(UNLOAD_DLL_DEBUG_INFO* UnloadDll)
{
PLUG_CB_UNLOADDLL callbackInfo;
callbackInfo.UnloadDll=UnloadDll;
plugincbcall(CB_UNLOADDLL, &callbackInfo);
void* base=UnloadDll->lpBaseOfDll;
char modname[256]="???";
if(modnamefromaddr((uint)base, modname, true))
bpenumall(cbRemoveModuleBreakpoints, modname);
SymUnloadModule64(fdProcessInfo->hProcess, (DWORD64)base);
dprintf("DLL Unloaded: "fhex" %s\n", base, modname);
if(settingboolget("Events", "Dll<EFBFBD>nload"))
{
//update GUI
DebugUpdateGui(GetContextData(UE_CIP), true);
GuiSetDebugState(paused);
//lock
lock(WAITID_RUN);
PLUG_CB_PAUSEDEBUG pauseInfo;
pauseInfo.reserved=0;
plugincbcall(CB_PAUSEDEBUG, &pauseInfo);
wait(WAITID_RUN);
}
}
static void cbOutputDebugString(OUTPUT_DEBUG_STRING_INFO* DebugString)
{
PLUG_CB_OUTPUTDEBUGSTRING callbackInfo;
callbackInfo.DebugString=DebugString;
plugincbcall(CB_OUTPUTDEBUGSTRING, &callbackInfo);
if(!DebugString->fUnicode) //ASCII
{
char* DebugText=(char*)emalloc(DebugString->nDebugStringLength+1, "cbOutputDebugString:DebugText");
memset(DebugText, 0, DebugString->nDebugStringLength+1);
if(memread(fdProcessInfo->hProcess, DebugString->lpDebugStringData, DebugText, DebugString->nDebugStringLength, 0))
{
int len=strlen(DebugText);
int escape_count=0;
for(int i=0; i<len; i++)
if(DebugText[i]=='\\')
escape_count++;
char* DebugTextEscaped=(char*)emalloc(DebugString->nDebugStringLength+escape_count+1, "cbOutputDebugString:DebugTextEscaped");
memset(DebugTextEscaped, 0, DebugString->nDebugStringLength+escape_count+1);
for(int i=0,j=0; i<len; i++)
{
switch(DebugText[i])
{
case '\t':
j+=sprintf(DebugTextEscaped+j, "\\t");
break;
case '\f':
j+=sprintf(DebugTextEscaped+j, "\\f");
break;
case '\v':
j+=sprintf(DebugTextEscaped+j, "\\v");
break;
case '\n':
j+=sprintf(DebugTextEscaped+j, "\\n");
break;
case '\r':
j+=sprintf(DebugTextEscaped+j, "\\r");
break;
case '\\':
j+=sprintf(DebugTextEscaped+j, "\\\\");
break;
case '\"':
j+=sprintf(DebugTextEscaped+j, "\\\"");
break;
default:
j+=sprintf(DebugTextEscaped+j, "%c", DebugText[i]);
break;
}
}
dprintf("DebugString: \"%s\"\n", DebugTextEscaped);
efree(DebugTextEscaped, "cbOutputDebugString:DebugTextEscaped");
}
efree(DebugText, "cbOutputDebugString:DebugText");
}
if(settingboolget("Events", "DebugStrings"))
{
//update GUI
DebugUpdateGui(GetContextData(UE_CIP), true);
GuiSetDebugState(paused);
//lock
lock(WAITID_RUN);
PLUG_CB_PAUSEDEBUG pauseInfo;
pauseInfo.reserved=0;
plugincbcall(CB_PAUSEDEBUG, &pauseInfo);
wait(WAITID_RUN);
}
}
static void cbException(EXCEPTION_DEBUG_INFO* ExceptionData)
{
PLUG_CB_EXCEPTION callbackInfo;
callbackInfo.Exception=ExceptionData;
unsigned int ExceptionCode=ExceptionData->ExceptionRecord.ExceptionCode;
GuiSetLastException(ExceptionCode);
uint addr=(uint)ExceptionData->ExceptionRecord.ExceptionAddress;
if(ExceptionData->ExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT)
{
if(isDetachedByUser)
{
PLUG_CB_DETACH detachInfo;
detachInfo.fdProcessInfo=fdProcessInfo;
plugincbcall(CB_DETACH, &detachInfo);
if(!DetachDebuggerEx(fdProcessInfo->dwProcessId))
dputs("DetachDebuggerEx failed...");
else
dputs("detached!");
return;
}
else if(isPausedByUser)
{
dputs("paused!");
SetNextDbgContinueStatus(DBG_CONTINUE);
DebugUpdateGui(GetContextData(UE_CIP), true);
GuiSetDebugState(paused);
//lock
lock(WAITID_RUN);
bSkipExceptions=false;
PLUG_CB_PAUSEDEBUG pauseInfo;
pauseInfo.reserved=0;
plugincbcall(CB_PAUSEDEBUG, &pauseInfo);
plugincbcall(CB_EXCEPTION, &callbackInfo);
wait(WAITID_RUN);
return;
}
SetContextData(UE_CIP, (uint)ExceptionData->ExceptionRecord.ExceptionAddress);
}
if(ExceptionData->dwFirstChance) //first chance exception
{
dprintf("first chance exception on "fhex" (%.8X)!\n", addr,ExceptionCode);
SetNextDbgContinueStatus(DBG_EXCEPTION_NOT_HANDLED);
if(bSkipExceptions || dbgisignoredexception(ExceptionCode))
return;
}
else //lock the exception
{
dprintf("last chance exception on "fhex" (%.8X)!\n", addr, ExceptionCode);
SetNextDbgContinueStatus(DBG_CONTINUE);
}
DebugUpdateGui(GetContextData(UE_CIP), true);
GuiSetDebugState(paused);
//lock
lock(WAITID_RUN);
bSkipExceptions=false;
PLUG_CB_PAUSEDEBUG pauseInfo;
pauseInfo.reserved=0;
plugincbcall(CB_PAUSEDEBUG, &pauseInfo);
plugincbcall(CB_EXCEPTION, &callbackInfo);
wait(WAITID_RUN);
}
static DWORD WINAPI threadDebugLoop(void* lpParameter)
{
lock(WAITID_STOP); //we are running
//initialize
bIsAttached=false;
bSkipExceptions=false;
INIT_STRUCT* init=(INIT_STRUCT*)lpParameter;
bFileIsDll=IsFileDLL(init->exe, 0);
pDebuggedEntry=GetPE32Data(init->exe, 0, UE_OEP);
strcpy(szFileName, init->exe);
if(bFileIsDll)
fdProcessInfo=(PROCESS_INFORMATION*)InitDLLDebug(init->exe, false, init->commandline, init->currentfolder, 0);
else
fdProcessInfo=(PROCESS_INFORMATION*)InitDebug(init->exe, init->commandline, init->currentfolder);
efree(init, "threadDebugLoop:init"); //free init struct
if(!fdProcessInfo)
{
fdProcessInfo=&g_pi;
dputs("error starting process (invalid pe?)!");
unlock(WAITID_STOP);
return 0;
}
BOOL wow64=false, mewow64=false;
if(!IsWow64Process(fdProcessInfo->hProcess, &wow64) or !IsWow64Process(GetCurrentProcess(), &mewow64))
{
dputs("IsWow64Process failed!");
StopDebug();
unlock(WAITID_STOP);
return 0;
}
if((mewow64 and !wow64) or (!mewow64 and wow64))
{
#ifdef _WIN64
dputs("Use x32_dbg to debug this process!");
#else
dputs("Use x64_dbg to debug this process!");
#endif // _WIN64
unlock(WAITID_STOP);
return 0;
}
GuiAddRecentFile(szFileName);
varset("$hp", (uint)fdProcessInfo->hProcess, true);
varset("$pid", fdProcessInfo->dwProcessId, true);
ecount=0;
//NOTE: set custom handlers
SetCustomHandler(UE_CH_CREATEPROCESS, (void*)cbCreateProcess);
SetCustomHandler(UE_CH_EXITPROCESS, (void*)cbExitProcess);
SetCustomHandler(UE_CH_CREATETHREAD, (void*)cbCreateThread);
SetCustomHandler(UE_CH_EXITTHREAD, (void*)cbExitThread);
SetCustomHandler(UE_CH_SYSTEMBREAKPOINT, (void*)cbSystemBreakpoint);
SetCustomHandler(UE_CH_LOADDLL, (void*)cbLoadDll);
SetCustomHandler(UE_CH_UNLOADDLL, (void*)cbUnloadDll);
SetCustomHandler(UE_CH_OUTPUTDEBUGSTRING, (void*)cbOutputDebugString);
SetCustomHandler(UE_CH_UNHANDLEDEXCEPTION, (void*)cbException);
//inform GUI we started without problems
GuiSetDebugState(initialized);
//set GUI title
strcpy(szBaseFileName, szFileName);
int len=strlen(szBaseFileName);
while(szBaseFileName[len]!='\\' and len)
len--;
if(len)
strcpy(szBaseFileName, szBaseFileName+len+1);
GuiUpdateWindowTitle(szBaseFileName);
//call plugin callback
PLUG_CB_INITDEBUG initInfo;
initInfo.szFileName=szFileName;
plugincbcall(CB_INITDEBUG, &initInfo);
//run debug loop (returns when process debugging is stopped)
DebugLoop();
isDetachedByUser=false;
//call plugin callback
PLUG_CB_STOPDEBUG stopInfo;
stopInfo.reserved=0;
plugincbcall(CB_STOPDEBUG, &stopInfo);
//message the user/do final stuff
DeleteFileA("DLLLoader.exe");
RemoveAllBreakPoints(UE_OPTION_REMOVEALL); //remove all breakpoints
//cleanup
dbclose();
modclear();
threadclear();
GuiSetDebugState(stopped);
dputs("debugging stopped!");
varset("$hp", (uint)0, true);
varset("$pid", (uint)0, true);
unlock(WAITID_STOP); //we are done
return 0;
}
CMDRESULT cbDebugInit(int argc, char* argv[])
{
if(DbgIsDebugging())
{
dputs("already debugging!");
return STATUS_ERROR;
}
static char arg1[deflen]="";
if(!argget(*argv, arg1, 0, false))
return STATUS_ERROR;
if(!FileExists(arg1))
{
dputs("file does not exsist!");
return STATUS_ERROR;
}
HANDLE hFile=CreateFileA(arg1, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if(hFile==INVALID_HANDLE_VALUE)
{
dputs("could not open file!");
return STATUS_ERROR;
}
GetFileNameFromHandle(hFile, arg1); //get full path of the file
CloseHandle(hFile);
static char arg2[deflen]="";
argget(*argv, arg2, 1, true);
char* commandline=0;
if(strlen(arg2))
commandline=arg2;
char arg3[deflen]="";
argget(*argv, arg3, 2, true);
static char currentfolder[deflen]="";
strcpy(currentfolder, arg1);
int len=strlen(currentfolder);
while(currentfolder[len]!='\\' and len!=0)
len--;
currentfolder[len]=0;
if(DirExists(arg3))
strcpy(currentfolder, arg3);
INIT_STRUCT* init=(INIT_STRUCT*)emalloc(sizeof(INIT_STRUCT), "cbDebugInit:init");
memset(init, 0, sizeof(INIT_STRUCT));
init->exe=arg1;
init->commandline=commandline;
if(*currentfolder)
init->currentfolder=currentfolder;
//initialize
wait(WAITID_STOP); //wait for the debugger to stop
waitclear(); //clear waiting flags NOTE: thread-unsafe
if(!CreateThread(0, 0, threadDebugLoop, init, 0, 0))
{
dputs("failed creating debug thread!");
return STATUS_ERROR;
}
return STATUS_CONTINUE;
}
CMDRESULT cbStopDebug(int argc, char* argv[])
{
StopDebug();
unlock(WAITID_RUN);
wait(WAITID_STOP);
return STATUS_CONTINUE;
}
CMDRESULT cbDebugRun(int argc, char* argv[])
{
if(!waitislocked(WAITID_RUN))
{
dputs("program is already running");
return STATUS_ERROR;
}
GuiSetDebugState(running);
unlock(WAITID_RUN);
PLUG_CB_RESUMEDEBUG callbackInfo;
callbackInfo.reserved=0;
plugincbcall(CB_RESUMEDEBUG, &callbackInfo);
return STATUS_CONTINUE;
}
CMDRESULT cbDebugErun(int argc, char* argv[])
{
if(waitislocked(WAITID_RUN))
bSkipExceptions=true;
return cbDebugRun(argc, argv);
}
CMDRESULT cbDebugSetBPXOptions(int argc, char* argv[])
{
char argtype[deflen]="";
uint type=0;
if(!argget(*argv, argtype, 0, false))
return STATUS_ERROR;
const char* a=0;
uint setting_type;
if(strstr(argtype, "long"))
{
setting_type=1; //break_int3long
a="TYPE_LONG_INT3";
type=UE_BREAKPOINT_LONG_INT3;
}
else if(strstr(argtype, "ud2"))
{
setting_type=2; //break_ud2
a="TYPE_UD2";
type=UE_BREAKPOINT_UD2;
}
else if(strstr(argtype, "short"))
{
setting_type=0; //break_int3short
a="TYPE_INT3";
type=UE_BREAKPOINT_INT3;
}
else
{
dputs("invalid type specified!");
return STATUS_ERROR;
}
SetBPXOptions(type);
BridgeSettingSetUint("Engine", "BreakpointType", setting_type);
dprintf("default breakpoint type set to: %s\n", a);
return STATUS_CONTINUE;
}
CMDRESULT cbDebugSetBPX(int argc, char* argv[]) //bp addr [,name [,type]]
{
char argaddr[deflen]="";
if(!argget(*argv, argaddr, 0, false))
return STATUS_ERROR;
char argname[deflen]="";
argget(*argv, argname, 1, true);
char argtype[deflen]="";
bool has_arg2=argget(*argv, argtype, 2, true);
if(!has_arg2 and (scmp(argname, "ss") or scmp(argname, "long") or scmp(argname, "ud2")))
{
strcpy(argtype, argname);
*argname=0;
}
_strlwr(argtype);
uint addr=0;
if(!valfromstring(argaddr, &addr))
{
dprintf("invalid addr: \"%s\"\n", argaddr);
return STATUS_ERROR;
}
int type=0;
bool singleshoot=false;
if(strstr(argtype, "ss"))
{
type|=UE_SINGLESHOOT;
singleshoot=true;
}
else
type|=UE_BREAKPOINT;
if(strstr(argtype, "long"))
type|=UE_BREAKPOINT_TYPE_LONG_INT3;
else if(strstr(argtype, "ud2"))
type|=UE_BREAKPOINT_TYPE_UD2;
else if(strstr(argtype, "short"))
type|=UE_BREAKPOINT_TYPE_INT3;
short oldbytes;
const char* bpname=0;
if(*argname)
bpname=argname;
if(bpget(addr, BPNORMAL, bpname, 0))
{
dputs("breakpoint already set!");
return STATUS_CONTINUE;
}
if(IsBPXEnabled(addr))
{
dprintf("error setting breakpoint at "fhex"!\n (IsBPXEnabled)", addr);
return STATUS_ERROR;
}
else if(!memread(fdProcessInfo->hProcess, (void*)addr, &oldbytes, sizeof(short), 0))
{
dprintf("error setting breakpoint at "fhex"!\n (memread)", addr);
return STATUS_ERROR;
}
else if(!SetBPX(addr, type, (void*)cbUserBreakpoint))
{
dprintf("error setting breakpoint at "fhex"! (SetBPX)\n", addr);
return STATUS_ERROR;
}
else if(!bpnew(addr, true, singleshoot, oldbytes, BPNORMAL, type, bpname))
{
dprintf("error setting breakpoint at "fhex"!\n (bpnew)", addr);
return STATUS_ERROR;
}
dprintf("breakpoint at "fhex" set!\n", addr);
GuiUpdateAllViews();
return STATUS_CONTINUE;
}
static bool cbDeleteAllBreakpoints(const BREAKPOINT* bp)
{
if(bpdel(bp->addr, BPNORMAL) and DeleteBPX(bp->addr))
return true;
dprintf("delete breakpoint failed: "fhex"\n", bp->addr);
return false;
}
CMDRESULT cbDebugDeleteBPX(int argc, char* argv[])
{
char arg1[deflen]="";
if(!argget(*argv, arg1, 0, true)) //delete all breakpoints
{
if(!bpgetcount(BPNORMAL))
{
dputs("no breakpoints to delete!");
return STATUS_CONTINUE;
}
if(!bpenumall(cbDeleteAllBreakpoints)) //at least one deletion failed
return STATUS_ERROR;
dputs("all breakpoints deleted!");
GuiUpdateAllViews();
return STATUS_CONTINUE;
}
BREAKPOINT found;
if(bpget(0, BPNORMAL, arg1, &found)) //found a breakpoint with name
{
if(!bpdel(found.addr, BPNORMAL) or !DeleteBPX(found.addr))
{
dprintf("delete breakpoint failed: "fhex"\n", found.addr);
return STATUS_ERROR;
}
return STATUS_CONTINUE;
}
uint addr=0;
if(!valfromstring(arg1, &addr) or !bpget(addr, BPNORMAL, 0, &found)) //invalid breakpoint
{
dprintf("no such breakpoint \"%s\"\n", arg1);
return STATUS_ERROR;
}
if(!bpdel(found.addr, BPNORMAL) or !DeleteBPX(found.addr))
{
dprintf("delete breakpoint failed: "fhex"\n", found.addr);
return STATUS_ERROR;
}
dputs("breakpoint deleted!");
GuiUpdateAllViews();
return STATUS_CONTINUE;
}
static bool cbEnableAllBreakpoints(const BREAKPOINT* bp)
{
if(!SetBPX(bp->addr, bp->titantype, (void*)cbUserBreakpoint) or !bpenable(bp->addr, BPNORMAL, true))
{
dprintf("could not enable "fhex"\n", bp->addr);
return false;
}
return true;
}
CMDRESULT cbDebugEnableBPX(int argc, char* argv[])
{
char arg1[deflen]="";
if(!argget(*argv, arg1, 0, true)) //delete all breakpoints
{
if(!bpgetcount(BPNORMAL))
{
dputs("no breakpoints to enable!");
return STATUS_CONTINUE;
}
if(!bpenumall(cbEnableAllBreakpoints)) //at least one deletion failed
return STATUS_ERROR;
dputs("all breakpoints enabled!");
GuiUpdateAllViews();
return STATUS_CONTINUE;
}
BREAKPOINT found;
if(bpget(0, BPNORMAL, arg1, &found)) //found a breakpoint with name
{
if(!SetBPX(found.addr, found.titantype, (void*)cbUserBreakpoint) or !bpenable(found.addr, BPNORMAL, true))
{
dprintf("could not enable "fhex"\n", found.addr);
return STATUS_ERROR;
}
GuiUpdateAllViews();
return STATUS_CONTINUE;
}
uint addr=0;
if(!valfromstring(arg1, &addr) or !bpget(addr, BPNORMAL, 0, &found)) //invalid breakpoint
{
dprintf("no such breakpoint \"%s\"\n", arg1);
return STATUS_ERROR;
}
if(found.enabled)
{
dputs("breakpoint already enabled!");
GuiUpdateAllViews();
return STATUS_CONTINUE;
}
if(!SetBPX(found.addr, found.titantype, (void*)cbUserBreakpoint) or !bpenable(found.addr, BPNORMAL, true))
{
dprintf("could not enable "fhex"\n", found.addr);
return STATUS_ERROR;
}
dputs("breakpoint enabled!");
GuiUpdateAllViews();
return STATUS_CONTINUE;
}
static bool cbDisableAllBreakpoints(const BREAKPOINT* bp)
{
if(!DeleteBPX(bp->addr) or !bpenable(bp->addr, BPNORMAL, false))
{
dprintf("could not disable "fhex"\n", bp->addr);
return false;
}
return true;
}
CMDRESULT cbDebugDisableBPX(int argc, char* argv[])
{
char arg1[deflen]="";
if(!argget(*argv, arg1, 0, true)) //delete all breakpoints
{
if(!bpgetcount(BPNORMAL))
{
dputs("no breakpoints to disable!");
return STATUS_CONTINUE;
}
if(!bpenumall(cbDisableAllBreakpoints)) //at least one deletion failed
return STATUS_ERROR;
dputs("all breakpoints disabled!");
GuiUpdateAllViews();
return STATUS_CONTINUE;
}
BREAKPOINT found;
if(bpget(0, BPNORMAL, arg1, &found)) //found a breakpoint with name
{
if(!DeleteBPX(found.addr) or !bpenable(found.addr, BPNORMAL, false))
{
dprintf("could not disable "fhex"\n", found.addr);
return STATUS_ERROR;
}
GuiUpdateAllViews();
return STATUS_CONTINUE;
}
uint addr=0;
if(!valfromstring(arg1, &addr) or !bpget(addr, BPNORMAL, 0, &found)) //invalid breakpoint
{
dprintf("no such breakpoint \"%s\"\n", arg1);
return STATUS_ERROR;
}
if(!found.enabled)
{
dputs("breakpoint already disabled!");
return STATUS_CONTINUE;
}
if(!DeleteBPX(found.addr) or !bpenable(found.addr, BPNORMAL, false))
{
dprintf("could not disable "fhex"\n", found.addr);
return STATUS_ERROR;
}
dputs("breakpoint disabled!");
GuiUpdateAllViews();
return STATUS_CONTINUE;
}
static bool cbBreakpointList(const BREAKPOINT* bp)
{
const char* type=0;
if(bp->type==BPNORMAL)
{
if(bp->singleshoot)
type="SS";
else
type="BP";
}
else if(bp->type==BPHARDWARE)
type="HW";
else if(bp->type==BPMEMORY)
type="GP";
bool enabled=bp->enabled;
if(*bp->name)
dprintf("%d:%s:"fhex":\"%s\"\n", enabled, type, bp->addr, bp->name);
else
dprintf("%d:%s:"fhex"\n", enabled, type, bp->addr);
return true;
}
CMDRESULT cbDebugBplist(int argc, char* argv[])
{
bpenumall(cbBreakpointList);
return STATUS_CONTINUE;
}
CMDRESULT cbDebugStepInto(int argc, char* argv[])
{
StepInto((void*)cbStep);
isStepping=true;
return cbDebugRun(argc, argv);
}
CMDRESULT cbDebugeStepInto(int argc, char* argv[])
{
bSkipExceptions=true;
return cbDebugStepInto(argc, argv);
}
CMDRESULT cbDebugStepOver(int argc, char* argv[])
{
StepOver((void*)cbStep);
isStepping=true;
return cbDebugRun(argc, argv);
}
CMDRESULT cbDebugeStepOver(int argc, char* argv[])
{
bSkipExceptions=true;
return cbDebugStepOver(argc, argv);
}
CMDRESULT cbDebugSingleStep(int argc, char* argv[])
{
char arg1[deflen]="";
uint stepcount=1;
if(argget(*argv, arg1, 0, true))
{
if(!valfromstring(arg1, &stepcount))
stepcount=1;
}
SingleStep((DWORD)stepcount, (void*)cbStep);
isStepping=true;
return cbDebugRun(argc, argv);
}
CMDRESULT cbDebugeSingleStep(int argc, char* argv[])
{
bSkipExceptions=true;
return cbDebugSingleStep(argc, argv);
}
CMDRESULT cbDebugHide(int argc, char* argv[])
{
if(HideDebugger(fdProcessInfo->hProcess, UE_HIDE_PEBONLY))
dputs("debugger hidden");
else
dputs("something went wrong");
return STATUS_CONTINUE;
}
CMDRESULT cbDebugDisasm(int argc, char* argv[])
{
char arg1[deflen]="";
uint addr=GetContextData(UE_CIP);
if(argget(*argv, arg1, 0, true))
if(!valfromstring(arg1, &addr))
addr=GetContextData(UE_CIP);
DebugUpdateGui(addr, false);
return STATUS_CONTINUE;
}
CMDRESULT cbDebugSetMemoryBpx(int argc, char* argv[])
{
char arg1[deflen]=""; //addr
if(!argget(*argv, arg1, 0, false))
return STATUS_ERROR;
uint addr;
if(!valfromstring(arg1, &addr))
return STATUS_ERROR;
bool restore=false;
char arg2[deflen]=""; //restore
char arg3[deflen]=""; //type
argget(*argv, arg3, 2, true);
if(argget(*argv, arg2, 1, true))
{
if(*arg2=='1')
restore=true;
else if(*arg2=='0')
restore=false;
else
strcpy(arg3, arg2);
}
uint type=UE_MEMORY;
if(*arg3)
{
switch(*arg3)
{
case 'r':
type=UE_MEMORY_READ;
break;
case 'w':
type=UE_MEMORY_WRITE;
break;
case 'x':
type=UE_MEMORY_EXECUTE; //EXECUTE
break;
default:
dputs("invalid type (argument ignored)");
break;
}
}
uint size=0;
uint base=memfindbaseaddr(fdProcessInfo->hProcess, addr, &size);
bool singleshoot=false;
if(!restore)
singleshoot=true;
if(bpget(base, BPMEMORY, 0, 0))
{
dputs("hardware breakpoint already set!");
return STATUS_CONTINUE;
}
if(!SetMemoryBPXEx(base, size, type, restore, (void*)cbMemoryBreakpoint) or !bpnew(base, true, singleshoot, 0, BPMEMORY, type, 0))
{
dputs("error setting memory breakpoint!");
return STATUS_ERROR;
}
dprintf("memory breakpoint at "fhex" set!\n", addr);
GuiUpdateAllViews();
return STATUS_CONTINUE;
}
static bool cbDeleteAllMemoryBreakpoints(const BREAKPOINT* bp)
{
if(!bp->enabled)
return true;
uint size;
memfindbaseaddr(fdProcessInfo->hProcess, bp->addr, &size);
if(!RemoveMemoryBPX(bp->addr, size) or !bpdel(bp->addr, BPMEMORY))
{
dprintf("delete memory breakpoint failed: "fhex"\n", bp->addr);
return STATUS_ERROR;
}
return true;
}
CMDRESULT cbDebugDeleteMemoryBreakpoint(int argc, char* argv[])
{
char arg1[deflen]="";
if(!argget(*argv, arg1, 0, true)) //delete all breakpoints
{
if(!bpgetcount(BPMEMORY))
{
dputs("no memory breakpoints to delete!");
return STATUS_CONTINUE;
}
if(!bpenumall(cbDeleteAllMemoryBreakpoints)) //at least one deletion failed
return STATUS_ERROR;
dputs("all memory breakpoints deleted!");
GuiUpdateAllViews();
return STATUS_CONTINUE;
}
BREAKPOINT found;
if(bpget(0, BPMEMORY, arg1, &found)) //found a breakpoint with name
{
uint size;
memfindbaseaddr(fdProcessInfo->hProcess, found.addr, &size);
if(!RemoveMemoryBPX(found.addr, size) or !bpdel(found.addr, BPMEMORY))
{
dprintf("delete memory breakpoint failed: "fhex"\n", found.addr);
return STATUS_ERROR;
}
return STATUS_CONTINUE;
}
uint addr=0;
if(!valfromstring(arg1, &addr) or !bpget(addr, BPMEMORY, 0, &found)) //invalid breakpoint
{
dprintf("no such memory breakpoint \"%s\"\n", arg1);
return STATUS_ERROR;
}
uint size;
memfindbaseaddr(fdProcessInfo->hProcess, found.addr, &size);
if(!RemoveMemoryBPX(found.addr, size) or !bpdel(found.addr, BPMEMORY))
{
dprintf("delete memory breakpoint failed: "fhex"\n", found.addr);
return STATUS_ERROR;
}
dputs("memory breakpoint deleted!");
GuiUpdateAllViews();
return STATUS_CONTINUE;
}
CMDRESULT cbDebugRtr(int argc, char* argv[])
{
StepOver((void*)cbRtrStep);
cbDebugRun(argc, argv);
return STATUS_CONTINUE;
}
CMDRESULT cbDebugeRtr(int argc, char* argv[])
{
bSkipExceptions=true;
return cbDebugRtr(argc, argv);
}
CMDRESULT cbDebugSetHardwareBreakpoint(int argc, char* argv[])
{
char arg1[deflen]=""; //addr
if(!argget(*argv, arg1, 0, false))
return STATUS_ERROR;
uint addr;
if(!valfromstring(arg1, &addr))
return STATUS_ERROR;
uint type=UE_HARDWARE_EXECUTE;
char arg2[deflen]=""; //type
if(argget(*argv, arg2, 1, true))
{
switch(*arg2)
{
case 'r':
type=UE_HARDWARE_READWRITE;
break;
case 'w':
type=UE_HARDWARE_WRITE;
break;
case 'x':
break;
default:
dputs("invalid type, assuming 'x'");
break;
}
}
char arg3[deflen]=""; //size
uint size=UE_HARDWARE_SIZE_1;
if(argget(*argv, arg3, 2, true))
{
if(!valfromstring(arg3, &size))
return STATUS_ERROR;
switch(size)
{
case 2:
size=UE_HARDWARE_SIZE_2;
break;
case 4:
size=UE_HARDWARE_SIZE_4;
break;
#ifdef _WIN64
case 8:
size=UE_HARDWARE_SIZE_8;
break;
#endif // _WIN64
default:
dputs("invalid size, using 1");
break;
}
if((addr%size)!=0)
{
dprintf("address not aligned to %d\n", size);
return STATUS_ERROR;
}
}
DWORD drx=0;
if(!GetUnusedHardwareBreakPointRegister(&drx))
{
dputs("no free debug register");
return STATUS_ERROR;
}
int titantype=(drx<<8)|(type<<4)|size;
//TODO: hwbp in multiple threads TEST
if(bpget(addr, BPHARDWARE, 0, 0))
{
dputs("hardware breakpoint already set!");
return STATUS_CONTINUE;
}
if(!SetHardwareBreakPoint(addr, drx, type, size, (void*)cbHardwareBreakpoint) or !bpnew(addr, true, false, 0, BPHARDWARE, titantype, 0))
{
dputs("error setting hardware breakpoint!");
return STATUS_ERROR;
}
dprintf("hardware breakpoint at "fhex" set!\n", addr);
GuiUpdateAllViews();
return STATUS_CONTINUE;
}
static bool cbDeleteAllHardwareBreakpoints(const BREAKPOINT* bp)
{
if(!bp->enabled)
return true;
if(!DeleteHardwareBreakPoint((bp->titantype>>8)&0xF) or !bpdel(bp->addr, BPHARDWARE))
{
dprintf("delete hardware breakpoint failed: "fhex"\n", bp->addr);
return STATUS_ERROR;
}
return true;
}
CMDRESULT cbDebugDeleteHardwareBreakpoint(int argc, char* argv[])
{
char arg1[deflen]="";
if(!argget(*argv, arg1, 0, true)) //delete all breakpoints
{
if(!bpgetcount(BPHARDWARE))
{
dputs("no hardware breakpoints to delete!");
return STATUS_CONTINUE;
}
if(!bpenumall(cbDeleteAllHardwareBreakpoints)) //at least one deletion failed
return STATUS_ERROR;
dputs("all hardware breakpoints deleted!");
GuiUpdateAllViews();
return STATUS_CONTINUE;
}
BREAKPOINT found;
if(bpget(0, BPHARDWARE, arg1, &found)) //found a breakpoint with name
{
if(!DeleteHardwareBreakPoint((found.titantype>>8)&0xF) or !bpdel(found.addr, BPHARDWARE))
{
dprintf("delete hardware breakpoint failed: "fhex"\n", found.addr);
return STATUS_ERROR;
}
return STATUS_CONTINUE;
}
uint addr=0;
if(!valfromstring(arg1, &addr) or !bpget(addr, BPHARDWARE, 0, &found)) //invalid breakpoint
{
dprintf("no such hardware breakpoint \"%s\"\n", arg1);
return STATUS_ERROR;
}
if(!DeleteHardwareBreakPoint((found.titantype>>8)&0xF) or !bpdel(found.addr, BPHARDWARE))
{
dprintf("delete hardware breakpoint failed: "fhex"\n", found.addr);
return STATUS_ERROR;
}
dputs("hardware breakpoint deleted!");
GuiUpdateAllViews();
return STATUS_CONTINUE;
}
CMDRESULT cbDebugAlloc(int argc, char* argv[])
{
char arg1[deflen]=""; //size
uint size=0x1000;
if(argget(*argv, arg1, 0, true))
if(!valfromstring(arg1, &size, false))
return STATUS_ERROR;
uint mem=(uint)memalloc(fdProcessInfo->hProcess, 0, size, PAGE_EXECUTE_READWRITE);
if(!mem)
dputs("VirtualAllocEx failed");
else
dprintf(fhex"\n", mem);
if(mem)
varset("$lastalloc", mem, true);
varset("$res", mem, false);
return STATUS_CONTINUE;
}
CMDRESULT cbDebugFree(int argc, char* argv[])
{
uint lastalloc;
varget("$lastalloc", &lastalloc, 0, 0);
char arg1[deflen]=""; //addr
uint addr=lastalloc;
if(argget(*argv, arg1, 0, true))
{
if(!valfromstring(arg1, &addr, false))
return STATUS_ERROR;
}
else if(!lastalloc)
{
dputs("lastalloc is zero, provide a page address");
return STATUS_ERROR;
}
if(addr==lastalloc)
varset("$lastalloc", (uint)0, true);
bool ok=VirtualFreeEx(fdProcessInfo->hProcess, (void*)addr, 0, MEM_RELEASE);
if(!ok)
dputs("VirtualFreeEx failed");
varset("$res", ok, false);
return STATUS_CONTINUE;
}
CMDRESULT cbDebugMemset(int argc, char* argv[])
{
char arg3[deflen]=""; //size
uint addr;
uint value;
uint size;
if(argc<3)
{
dputs("not enough arguments");
return STATUS_ERROR;
}
if(!valfromstring(argv[1], &addr, false) or !valfromstring(argv[2], &value, false))
return STATUS_ERROR;
if(argget(*argv, arg3, 2, true))
{
if(!valfromstring(arg3, &size, false))
return STATUS_ERROR;
}
else
{
uint base=memfindbaseaddr(fdProcessInfo->hProcess, addr, &size);
if(!base)
{
dputs("invalid address specified");
return STATUS_ERROR;
}
uint diff=addr-base;
addr=base+diff;
size-=diff;
}
BYTE fi=value&0xFF;
if(!Fill((void*)addr, size&0xFFFFFFFF, &fi))
dputs("memset failed");
else
dprintf("memory "fhex" (size: %.8X) set to %.2X\n", addr, size&0xFFFFFFFF, value&0xFF);
return STATUS_CONTINUE;
}
CMDRESULT cbBenchmark(int argc, char* argv[])
{
return STATUS_CONTINUE;
}
CMDRESULT cbDebugPause(int argc, char* argv[])
{
if(waitislocked(WAITID_RUN))
{
dputs("program is not running");
return STATUS_ERROR;
}
isPausedByUser=true;
DebugBreakProcess(fdProcessInfo->hProcess);
return STATUS_CONTINUE;
}
CMDRESULT cbMemWrite(int argc, char* argv[])
{
if(argc<2)
{
dputs("not enough arguments");
return STATUS_ERROR;
}
uint addr=0;
if(!valfromstring(argv[1], &addr, false))
return STATUS_ERROR;
unsigned char* blub=(unsigned char*)emalloc(0x2123, "cbMemWrite:blub");
memread(fdProcessInfo->hProcess, (const void*)addr, blub, 0x2123, 0);
//memwrite(fdProcessInfo->hProcess, (void*)addr, blub, 0x2123, 0);
return STATUS_CONTINUE;
}
DWORD WINAPI scyllaThread(void* lpParam)
{
typedef INT (WINAPI * SCYLLASTARTGUI)(DWORD pid, HINSTANCE mod);
SCYLLASTARTGUI ScyllaStartGui=0;
HINSTANCE hScylla=LoadLibraryA("Scylla.dll");
if(!hScylla)
{
dputs("error loading Scylla.dll!");
bScyllaLoaded=false;
return 0;
}
ScyllaStartGui=(SCYLLASTARTGUI)GetProcAddress(hScylla, "ScyllaStartGui");
if(!ScyllaStartGui)
{
dputs("could not find export 'ScyllaStartGui' inside Scylla.dll");
bScyllaLoaded=false;
return 0;
}
if(bFileIsDll)
ScyllaStartGui(fdProcessInfo->dwProcessId, (HINSTANCE)pDebuggedBase);
else
ScyllaStartGui(fdProcessInfo->dwProcessId, 0);
FreeLibrary(hScylla);
bScyllaLoaded=false;
return 0;
}
CMDRESULT cbStartScylla(int argc, char* argv[])
{
if(bScyllaLoaded)
{
dputs("Scylla is already loaded");
return STATUS_ERROR;
}
bScyllaLoaded=true;
CreateThread(0, 0, scyllaThread, 0, 0, 0);
return STATUS_CONTINUE;
}
static void cbAttachDebugger()
{
varset("$hp", (uint)fdProcessInfo->hProcess, true);
varset("$pid", fdProcessInfo->dwProcessId, true);
}
static DWORD WINAPI threadAttachLoop(void* lpParameter)
{
lock(WAITID_STOP);
bIsAttached=true;
bSkipExceptions=false;
uint pid=(uint)lpParameter;
static PROCESS_INFORMATION pi_attached;
fdProcessInfo=&pi_attached;
//do some init stuff
bFileIsDll=IsFileDLL(szFileName, 0);
GuiAddRecentFile(szFileName);
ecount=0;
//NOTE: set custom handlers
SetCustomHandler(UE_CH_CREATEPROCESS, (void*)cbCreateProcess);
SetCustomHandler(UE_CH_EXITPROCESS, (void*)cbExitProcess);
SetCustomHandler(UE_CH_CREATETHREAD, (void*)cbCreateThread);
SetCustomHandler(UE_CH_EXITTHREAD, (void*)cbExitThread);
SetCustomHandler(UE_CH_SYSTEMBREAKPOINT, (void*)cbSystemBreakpoint);
SetCustomHandler(UE_CH_LOADDLL, (void*)cbLoadDll);
SetCustomHandler(UE_CH_UNLOADDLL, (void*)cbUnloadDll);
SetCustomHandler(UE_CH_OUTPUTDEBUGSTRING, (void*)cbOutputDebugString);
SetCustomHandler(UE_CH_UNHANDLEDEXCEPTION, (void*)cbException);
//inform GUI start we started without problems
GuiSetDebugState(initialized);
//set GUI title
strcpy(szBaseFileName, szFileName);
int len=strlen(szBaseFileName);
while(szBaseFileName[len]!='\\' and len)
len--;
if(len)
strcpy(szBaseFileName, szBaseFileName+len+1);
GuiUpdateWindowTitle(szBaseFileName);
//call plugin callback (init)
PLUG_CB_INITDEBUG initInfo;
initInfo.szFileName=szFileName;
plugincbcall(CB_INITDEBUG, &initInfo);
//call plugin callback (attach)
PLUG_CB_ATTACH attachInfo;
attachInfo.dwProcessId=pid;
plugincbcall(CB_ATTACH, &attachInfo);
//run debug loop (returns when process debugging is stopped)
AttachDebugger(pid, true, fdProcessInfo, (void*)cbAttachDebugger);
isDetachedByUser=false;
//call plugin callback
PLUG_CB_STOPDEBUG stopInfo;
stopInfo.reserved=0;
plugincbcall(CB_STOPDEBUG, &stopInfo);
//message the user/do final stuff
RemoveAllBreakPoints(UE_OPTION_REMOVEALL); //remove all breakpoints
dbclose();
modclear();
GuiSetDebugState(stopped);
dputs("debugging stopped!");
varset("$hp", (uint)0, true);
varset("$pid", (uint)0, true);
unlock(WAITID_STOP);
waitclear();
return 0;
}
CMDRESULT cbDebugAttach(int argc, char* argv[])
{
if(argc<2)
{
dputs("not enough arguments!");
return STATUS_ERROR;
}
uint pid=0;
if(!valfromstring(argv[1], &pid))
{
dprintf("invalid expression \"%s\"!\n", argv[1]);
return STATUS_ERROR;
}
if(DbgIsDebugging())
{
//TODO: do stuff
dputs("terminate the current session!");
return STATUS_ERROR;
}
HANDLE hProcess=TitanOpenProcess(PROCESS_ALL_ACCESS, false, pid);
if(!hProcess)
{
dprintf("could not open process %X!\n", pid);
return STATUS_ERROR;
}
BOOL wow64=false, mewow64=false;
if(!IsWow64Process(hProcess, &wow64) or !IsWow64Process(GetCurrentProcess(), &mewow64))
{
dputs("IsWow64Process failed!");
CloseHandle(hProcess);
return STATUS_ERROR;
}
if((mewow64 and !wow64) or (!mewow64 and wow64))
{
#ifdef _WIN64
dputs("Use x32_dbg to debug this process!");
#else
dputs("Use x64_dbg to debug this process!");
#endif // _WIN64
CloseHandle(hProcess);
return STATUS_ERROR;
}
if(!GetModuleFileNameExA(hProcess, 0, szFileName, sizeof(szFileName)))
{
dprintf("could not get module filename %X!\n", pid);
CloseHandle(hProcess);
return STATUS_ERROR;
}
CloseHandle(hProcess);
CreateThread(0, 0, threadAttachLoop, (void*)pid, 0, 0);
return STATUS_CONTINUE;
}
CMDRESULT cbDebugDetach(int argc, char* argv[])
{
unlock(WAITID_RUN); //run
isDetachedByUser=true; //detach when paused
DebugBreakProcess(fdProcessInfo->hProcess);
return STATUS_CONTINUE;
}
CMDRESULT cbDebugDump(int argc, char* argv[])
{
if(argc<2)
{
dputs("not enough arguments!");
return STATUS_ERROR;
}
duint addr=0;
if(!valfromstring(argv[1], &addr))
{
dprintf("invalid address \"%s\"!\n", argv[1]);
return STATUS_ERROR;
}
GuiDumpAt(addr);
return STATUS_CONTINUE;
}
CMDRESULT cbDebugStackDump(int argc, char* argv[])
{
duint addr=0;
if(argc<2)
addr=GetContextData(UE_CSP);
else if(!valfromstring(argv[1], &addr))
{
dprintf("invalid address \"%s\"!\n", argv[1]);
return STATUS_ERROR;
}
duint csp=GetContextData(UE_CSP);
duint size=0;
duint base=memfindbaseaddr(fdProcessInfo->hProcess, csp, &size);
if(base && addr>=base && addr<(base+size))
GuiStackDumpAt(addr, csp);
else
dputs("invalid stack address!");
return STATUS_CONTINUE;
}