mirror of
https://github.com/x64dbg/xdbg.git
synced 2026-11-09 18:13:53 -10:00
513 lines
14 KiB
C++
513 lines
14 KiB
C++
#include <tchar.h>
|
|
#include <Windows.h>
|
|
#include <WinNT.h>
|
|
#include "XDbgProxy.h"
|
|
#include <assert.h>
|
|
#include "Win32ApiWrapper.h"
|
|
#include "Win32ApiWrapper.h"
|
|
#include <tlhelp32.h>
|
|
#include <Psapi.h>
|
|
#include "Utils.h"
|
|
#include "common.h"
|
|
|
|
XDbgProxy::XDbgProxy(void)
|
|
{
|
|
_hPipe = INVALID_HANDLE_VALUE;
|
|
memset(&_lastException, 0l, sizeof(_lastException));
|
|
_stopFlag = 0;
|
|
_attached = false;
|
|
|
|
_lastExceptCode = 0;
|
|
_lastExceptAddr = 0;
|
|
}
|
|
|
|
XDbgProxy::~XDbgProxy(void)
|
|
{
|
|
if (_hPipe != INVALID_HANDLE_VALUE)
|
|
XDbgCloseHandle(_hPipe);
|
|
}
|
|
|
|
BOOL XDbgProxy::sendDbgEvent(const DebugEventPacket& event)
|
|
{
|
|
if (event.event.dwDebugEventCode > LAST_EVENT) {
|
|
assert(false);
|
|
return FALSE;
|
|
}
|
|
|
|
if (event.event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT)
|
|
memset((void* )&event.ctx, 0l, sizeof(event.ctx));
|
|
DWORD len;
|
|
if (!XDbgWriteFile(_hPipe, &event, sizeof(event), &len, NULL)) {
|
|
// log error
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL XDbgProxy::recvDbgAck(struct DebugAckPacket& ack)
|
|
{
|
|
DWORD len;
|
|
if (!XDbgReadFile(_hPipe, &ack, sizeof(ack), &len, NULL)) {
|
|
// log error
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL XDbgProxy::sendDbgEvent(const DebugEventPacket& event, struct DebugAckPacket& ack, bool freeze)
|
|
{
|
|
BOOL result;
|
|
if (freeze)
|
|
suspendAll(XDbgGetCurrentThreadId());
|
|
if (sendDbgEvent(event))
|
|
result = recvDbgAck(ack);
|
|
else
|
|
result = FALSE;
|
|
if (freeze)
|
|
resumeAll(XDbgGetCurrentThreadId());
|
|
return result;
|
|
}
|
|
|
|
LONG CALLBACK XDbgProxy::_VectoredHandler(PEXCEPTION_POINTERS ExceptionInfo)
|
|
{
|
|
return XDbgProxy::instance().VectoredHandler(ExceptionInfo);
|
|
}
|
|
|
|
VOID CALLBACK XDbgProxy::_LdrDllNotification(ULONG NotificationReason, PCLDR_DLL_NOTIFICATION_DATA NotificationData,
|
|
PVOID Context)
|
|
{
|
|
XDbgProxy::instance().LdrDllNotification(NotificationReason, NotificationData, Context);
|
|
}
|
|
|
|
VOID CALLBACK XDbgProxy::LdrDllNotification(ULONG NotificationReason, PCLDR_DLL_NOTIFICATION_DATA NotificationData,
|
|
PVOID Context)
|
|
{
|
|
DebugEventPacket event;
|
|
memset(&event, 0, sizeof(event));
|
|
DEBUG_EVENT& msg = event.event;
|
|
msg.dwProcessId = XDbgGetCurrentProcessId();
|
|
msg.dwThreadId = XDbgGetCurrentThreadId();
|
|
|
|
MutexGuard guard(this);
|
|
|
|
if (NotificationReason == LDR_DLL_NOTIFICATION_REASON_LOADED) {
|
|
msg.dwDebugEventCode = LOAD_DLL_DEBUG_EVENT;
|
|
msg.u.LoadDll.dwDebugInfoFileOffset = 0;
|
|
msg.u.LoadDll.fUnicode = 1;
|
|
msg.u.LoadDll.hFile = NULL;
|
|
msg.u.LoadDll.lpBaseOfDll = NotificationData->Loaded.DllBase;
|
|
msg.u.LoadDll.lpImageName = _wcsdup(NotificationData->Loaded.FullDllName->Buffer);
|
|
msg.u.LoadDll.nDebugInfoSize = 0;
|
|
} else if (NotificationReason == LDR_DLL_NOTIFICATION_REASON_LOADED) {
|
|
msg.dwDebugEventCode = UNLOAD_DLL_DEBUG_EVENT;
|
|
msg.u.UnloadDll.lpBaseOfDll = NotificationData->Unloaded.DllBase;
|
|
} else
|
|
return;
|
|
|
|
// FIXME:
|
|
// 加载 Dll 可能在 APC 中进行, 这会导致其它的通知过程(sendDbgEvent)被打断, 造成错误
|
|
// 现在 Dll 相关的通知全部缓存, 但是会导致 LoadDll 断点失效
|
|
|
|
_pendingEvents.push_back(event);
|
|
/* DebugAckPacket ack;
|
|
if (!sendDbgEvent(event, ack)) {
|
|
|
|
} */
|
|
}
|
|
|
|
bool XDbgProxy::initialize()
|
|
{
|
|
if (!InitWin32ApiWrapper()) {
|
|
assert(false);
|
|
return false;
|
|
}
|
|
|
|
if (!createPipe())
|
|
return false;
|
|
|
|
if (AddVectoredExceptionHandler(1, &XDbgProxy::_VectoredHandler) == NULL)
|
|
return false;
|
|
|
|
typedef VOID (CALLBACK *PLDR_DLL_NOTIFICATION_FUNCTION)(ULONG, PCLDR_DLL_NOTIFICATION_DATA, PVOID);
|
|
typedef NTSTATUS (NTAPI *LdrRegDllNotifFunc)(ULONG, PLDR_DLL_NOTIFICATION_FUNCTION, PVOID, PVOID *);
|
|
LdrRegDllNotifFunc LdrRegisterDllNotification = (LdrRegDllNotifFunc )GetProcAddress(
|
|
GetModuleHandle("ntdll.dll"), "LdrRegisterDllNotification");
|
|
if (LdrRegisterDllNotification) {
|
|
PVOID cookie;
|
|
if (LdrRegisterDllNotification(0, &XDbgProxy::_LdrDllNotification, NULL, &cookie) != 0) {
|
|
// log error
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
MyTrace("%s(): starting thread.", __FUNCTION__);
|
|
if (!start()) {
|
|
assert(false);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
LONG CALLBACK XDbgProxy::VectoredHandler(PEXCEPTION_POINTERS ExceptionInfo)
|
|
{
|
|
MutexGuard guard(this);
|
|
|
|
if (!_attached)
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
|
|
if (XDbgGetCurrentThreadId() == getId())
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
|
|
if (threadIdToHandle(XDbgGetCurrentThreadId()) == NULL) {
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
DebugEventPacket event;
|
|
memset(&event, 0, sizeof(event));
|
|
DEBUG_EVENT& msg = event.event;
|
|
|
|
msg.dwProcessId = XDbgGetCurrentProcessId();
|
|
msg.dwThreadId = XDbgGetCurrentThreadId();
|
|
|
|
if (ExceptionInfo->ExceptionRecord->ExceptionCode == DBG_PRINTEXCEPTION_C) {
|
|
|
|
msg.dwDebugEventCode = OUTPUT_DEBUG_STRING_EVENT;
|
|
msg.u.DebugString.fUnicode = 0;
|
|
msg.u.DebugString.nDebugStringLength = (WORD )ExceptionInfo->ExceptionRecord->ExceptionInformation[0];
|
|
msg.u.DebugString.lpDebugStringData = (LPSTR )ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
|
|
} else if (ExceptionInfo->ExceptionRecord->ExceptionCode == DBG_PRINTEXCEPTION_WIDE_C) {
|
|
|
|
msg.dwDebugEventCode = OUTPUT_DEBUG_STRING_EVENT;
|
|
msg.u.DebugString.fUnicode = 1;
|
|
msg.u.DebugString.nDebugStringLength = (WORD)ExceptionInfo->ExceptionRecord->ExceptionInformation[0];
|
|
msg.u.DebugString.lpDebugStringData = (LPSTR)ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
|
|
} else {
|
|
|
|
msg.dwDebugEventCode = EXCEPTION_DEBUG_EVENT;
|
|
|
|
if (_lastException == ExceptionInfo->ExceptionRecord &&
|
|
_lastExceptCode == ExceptionInfo->ExceptionRecord->ExceptionCode &&
|
|
_lastExceptAddr == ExceptionInfo->ExceptionRecord->ExceptionAddress) {
|
|
|
|
msg.u.Exception.dwFirstChance = 0;
|
|
} else
|
|
msg.u.Exception.dwFirstChance = 1;
|
|
|
|
msg.u.Exception.ExceptionRecord = *ExceptionInfo->ExceptionRecord;
|
|
_lastException = ExceptionInfo->ExceptionRecord;
|
|
_lastExceptCode = ExceptionInfo->ExceptionRecord->ExceptionCode;
|
|
_lastExceptAddr = ExceptionInfo->ExceptionRecord->ExceptionAddress;
|
|
};
|
|
|
|
event.ctx = *ExceptionInfo->ContextRecord;
|
|
DebugAckPacket ack;
|
|
if (!sendDbgEvent(event, ack)) {
|
|
// log error
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
cloneThreadContext(ExceptionInfo->ContextRecord, &ack.ctx, ack.ContextFlags);
|
|
|
|
if ((ack.ContextFlags & CONTEXT_CONTROL) != CONTEXT_CONTROL) {
|
|
if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) {
|
|
if (ack.dwContinueStatus == DBG_CONTINUE) {
|
|
CTX_PC_REG(ExceptionInfo->ContextRecord) += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ack.dwContinueStatus == DBG_CONTINUE)
|
|
return EXCEPTION_CONTINUE_EXECUTION;
|
|
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
bool XDbgProxy::createPipe()
|
|
{
|
|
std::string name = makePipeName(XDbgGetCurrentProcessId());
|
|
_hPipe = ::CreateNamedPipe(name.c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
|
PIPE_UNLIMITED_INSTANCES, EVENT_MESSAGE_SIZE, CONTINUE_MESSAGE_SIZE, NMPWAIT_USE_DEFAULT_WAIT, NULL);
|
|
|
|
return (_hPipe != INVALID_HANDLE_VALUE);
|
|
}
|
|
|
|
// SO, THIS MODULE MUST BE A DLL
|
|
BOOL XDbgProxy::DllMain(HANDLE hModule, DWORD reason, LPVOID lpReserved)
|
|
{
|
|
// MyTrace("%s()", __FUNCTION__);
|
|
|
|
DebugEventPacket event;
|
|
memset(&event, 0, sizeof(event));
|
|
|
|
DEBUG_EVENT& msg = event.event;
|
|
DebugAckPacket ack;
|
|
|
|
MutexGuard guard(this);
|
|
|
|
switch (reason) {
|
|
case DLL_PROCESS_ATTACH:
|
|
// MyTrace("%s(): process(%u) xdbg proxy loaded. thread id: %u", __FUNCTION__, GetCurrentProcessId(), GetCurrentThreadId());
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
// MyTrace("%s(): process(%u) xdbg proxy unloaded. thread id: %u", __FUNCTION__, GetCurrentProcessId(), GetCurrentThreadId());
|
|
break;
|
|
|
|
case DLL_THREAD_ATTACH:
|
|
if (!_attached)
|
|
return TRUE;
|
|
// REPORT CreateThread
|
|
msg.dwProcessId = XDbgGetCurrentProcessId();
|
|
msg.dwThreadId = XDbgGetCurrentThreadId();
|
|
msg.dwDebugEventCode = CREATE_THREAD_DEBUG_EVENT;
|
|
msg.u.CreateThread.hThread = NULL;
|
|
|
|
msg.u.CreateThread.lpStartAddress = (LPTHREAD_START_ROUTINE )
|
|
GetThreadStartAddress(XDbgGetCurrentThread());
|
|
|
|
msg.u.CreateThread.lpThreadLocalBase = NtCurrentTeb();
|
|
|
|
if (!sendDbgEvent(event, ack)) {
|
|
|
|
}
|
|
|
|
addThread(XDbgGetCurrentThreadId());
|
|
// MYTRACE("%s(): process(%u) xdbg proxy attach thread. thread id: %u", __FUNCTION__,
|
|
// GetCurrentProcessId(), GetCurrentThreadId());
|
|
break;
|
|
|
|
case DLL_THREAD_DETACH:
|
|
// MYTRACE("%s(): process(%u) xdbg proxy detach thread. thread id: %u", __FUNCTION__,
|
|
// GetCurrentProcessId(), GetCurrentThreadId());
|
|
|
|
// REPORT ExitThread
|
|
if (!_attached)
|
|
return TRUE;
|
|
msg.dwProcessId = XDbgGetCurrentProcessId();
|
|
msg.dwThreadId = XDbgGetCurrentThreadId();
|
|
msg.dwDebugEventCode = EXIT_THREAD_DEBUG_EVENT;
|
|
if (!GetExitCodeThread(XDbgGetCurrentThread(), &msg.u.ExitThread.dwExitCode))
|
|
msg.u.ExitThread.dwExitCode = 0;
|
|
|
|
if (!sendDbgEvent(event, ack)) {
|
|
|
|
}
|
|
|
|
delThread(XDbgGetCurrentThreadId());
|
|
break;
|
|
};
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void XDbgProxy::waitForAttach()
|
|
{
|
|
while (!_attached) {
|
|
Sleep(0);
|
|
MemoryBarrier();
|
|
}
|
|
}
|
|
|
|
long XDbgProxy::run()
|
|
{
|
|
MyTrace("XDBG Thread started");
|
|
|
|
while (!_stopFlag) {
|
|
|
|
if (_attached) {
|
|
{
|
|
MutexGuard guard(this);
|
|
while (_pendingEvents.size() > 0) {
|
|
DebugEventPacket& event = _pendingEvents.front();
|
|
DebugAckPacket ack;
|
|
if (!sendDbgEvent(event, ack)) {
|
|
|
|
}
|
|
|
|
if (event.event.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT) {
|
|
free(event.event.u.LoadDll.lpImageName);
|
|
}
|
|
|
|
_pendingEvents.pop_front();
|
|
}
|
|
}
|
|
|
|
Sleep(100);
|
|
|
|
} else {
|
|
|
|
if (!ConnectNamedPipe(_hPipe, NULL)) {
|
|
if (GetLastError() == ERROR_PIPE_CONNECTED) {
|
|
onDbgConnect();
|
|
_attached = true;
|
|
MyTrace("debugger attached");
|
|
} else {
|
|
MyTrace("%s(): ConnectNamedPipe(%p) failed. errCode: %d ", __FUNCTION__, _hPipe, GetLastError());
|
|
assert(false);
|
|
return -1;
|
|
}
|
|
|
|
Sleep(100);
|
|
|
|
} else {
|
|
onDbgConnect();
|
|
_attached = true;
|
|
MyTrace("debugger attached");
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void XDbgProxy::onDbgConnect()
|
|
{
|
|
MutexGuard guard(this);
|
|
|
|
clearThreads();
|
|
addAllThreads(XDbgGetCurrentThreadId());
|
|
suspendAll(XDbgGetCurrentThreadId());
|
|
|
|
DebugEventPacket event;
|
|
DebugAckPacket ack;
|
|
event.event.dwProcessId = XDbgGetCurrentProcessId();
|
|
event.event.dwThreadId = getFirstThread();
|
|
event.event.dwDebugEventCode = ATTACHED_EVENT; // THIS MSG DONT PASS TO DEBUGGER
|
|
sendDbgEvent(event, ack, false);
|
|
sendProcessInfo(ack.dwThreadId);
|
|
sendThreadInfo();
|
|
sendModuleInfo(ack.dwThreadId);
|
|
resumeAll(XDbgGetCurrentThreadId());
|
|
}
|
|
|
|
void XDbgProxy::onDbgDisconnect()
|
|
{
|
|
// MutexGuard guard(this);
|
|
}
|
|
|
|
void XDbgProxy::sendProcessInfo(DWORD firstThread)
|
|
{
|
|
MyTrace("%s()", __FUNCTION__);
|
|
DebugEventPacket event;
|
|
memset(&event, 0, sizeof(event));
|
|
DEBUG_EVENT& msg = event.event;
|
|
DebugAckPacket ack;
|
|
msg.dwProcessId = XDbgGetCurrentProcessId();
|
|
msg.dwThreadId = firstThread;
|
|
|
|
char modName[MAX_PATH + 1] = {0};
|
|
|
|
memset(&msg.u.CreateProcessInfo, 0, sizeof(msg.u.CreateProcessInfo));
|
|
msg.dwDebugEventCode = CREATE_PROCESS_DEBUG_EVENT;
|
|
msg.u.CreateProcessInfo.dwDebugInfoFileOffset = 0;
|
|
msg.u.CreateProcessInfo.fUnicode = 0;
|
|
msg.u.CreateProcessInfo.hFile = NULL;
|
|
msg.u.CreateProcessInfo.hProcess = NULL;
|
|
msg.u.CreateProcessInfo.hThread = NULL;
|
|
msg.u.CreateProcessInfo.lpBaseOfImage = (PVOID )GetModuleHandle(NULL);
|
|
GetModuleFileName(GetModuleHandle(NULL), modName, MAX_PATH);
|
|
msg.u.CreateProcessInfo.lpImageName = modName;
|
|
msg.u.CreateProcessInfo.lpStartAddress = (LPTHREAD_START_ROUTINE)GetThreadStartAddress(firstThread);
|
|
MyTrace("%s(): mod: %s, main thread start at: %p", __FUNCTION__, modName,
|
|
msg.u.CreateProcessInfo.lpStartAddress);
|
|
msg.u.CreateProcessInfo.lpThreadLocalBase = GetThreadTeb(firstThread);
|
|
msg.u.CreateProcessInfo.nDebugInfoSize = 0;
|
|
sendDbgEvent(event, ack, false);
|
|
}
|
|
|
|
void XDbgProxy::sendModuleInfo(DWORD firstThread)
|
|
{
|
|
MyTrace("%s()", __FUNCTION__);
|
|
|
|
DebugEventPacket event;
|
|
memset(&event, 0, sizeof(event));
|
|
DEBUG_EVENT& msg = event.event;
|
|
DebugAckPacket ack;
|
|
|
|
msg.dwDebugEventCode = LOAD_DLL_DEBUG_EVENT;
|
|
msg.dwProcessId = XDbgGetCurrentProcessId();
|
|
msg.dwThreadId = firstThread;
|
|
|
|
char modName[MAX_PATH + 1] = {0};
|
|
|
|
HMODULE hMainModule = GetModuleHandle(NULL);
|
|
/*msg.u.LoadDll.dwDebugInfoFileOffset = 0;
|
|
msg.u.LoadDll.fUnicode = 0;
|
|
msg.u.LoadDll.hFile = NULL;
|
|
msg.u.LoadDll.lpBaseOfDll = hMainModule;
|
|
GetModuleFileName(hMainModule, modName, MAX_PATH);
|
|
msg.u.LoadDll.lpImageName = modName;
|
|
sendDbgEvent(event, ack);
|
|
MyTrace("%s(): module: %s, %p", __FUNCTION__, modName, msg.u.LoadDll.lpBaseOfDll);*/
|
|
|
|
HMODULE hModules[512];
|
|
DWORD len;
|
|
if (!EnumProcessModules(XDbgGetCurrentProcess(), hModules, sizeof(hModules), &len)) {
|
|
// log error
|
|
assert(false);
|
|
return;
|
|
}
|
|
|
|
len /= sizeof(HMODULE);
|
|
MyTrace("%s(): module count: %d", __FUNCTION__, len);
|
|
|
|
for (DWORD i = 0; i < len; i++) {
|
|
if (hMainModule == hModules[i])
|
|
continue;
|
|
|
|
msg.u.LoadDll.dwDebugInfoFileOffset = 0;
|
|
msg.u.LoadDll.fUnicode = 0;
|
|
msg.u.LoadDll.hFile = NULL;
|
|
msg.u.LoadDll.lpBaseOfDll = hModules[i];
|
|
memset(modName, 0, sizeof(modName));
|
|
if (GetModuleFileName(hModules[i], modName, MAX_PATH) == 0 || modName[0] == '\0')
|
|
continue;
|
|
msg.u.LoadDll.lpImageName = modName;
|
|
sendDbgEvent(event, ack, false);
|
|
MyTrace("%s(): module: %s, %p", __FUNCTION__, modName, msg.u.LoadDll.lpBaseOfDll);
|
|
}
|
|
}
|
|
|
|
void XDbgProxy::sendThreadInfo()
|
|
{
|
|
MyTrace("%s()", __FUNCTION__);
|
|
|
|
DebugEventPacket event;
|
|
memset(&event, 0, sizeof(event));
|
|
DEBUG_EVENT& msg = event.event;
|
|
DebugAckPacket ack;
|
|
|
|
msg.dwDebugEventCode = CREATE_THREAD_DEBUG_EVENT;
|
|
msg.dwProcessId = XDbgGetCurrentProcessId();
|
|
|
|
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
|
|
if (hSnapshot != INVALID_HANDLE_VALUE) {
|
|
THREADENTRY32 te;
|
|
te.dwSize = sizeof(te);
|
|
if (Thread32First(hSnapshot, &te)) {
|
|
do {
|
|
if (te.th32OwnerProcessID == XDbgGetCurrentProcessId()) {
|
|
|
|
if (te.th32ThreadID == getId())
|
|
continue; // skip xdbg thread;
|
|
msg.dwThreadId = te.th32ThreadID;
|
|
msg.u.CreateThread.hThread = NULL;
|
|
msg.u.CreateThread.lpStartAddress = (LPTHREAD_START_ROUTINE)
|
|
GetThreadStartAddress(te.th32ThreadID);
|
|
|
|
msg.u.CreateThread.lpThreadLocalBase = GetThreadTeb(te.th32ThreadID);
|
|
addThread(te.th32ThreadID);
|
|
sendDbgEvent(event, ack, false);
|
|
}
|
|
|
|
te.dwSize = sizeof(te);
|
|
} while (Thread32Next(hSnapshot, &te));
|
|
}
|
|
|
|
XDbgCloseHandle(hSnapshot);
|
|
}
|
|
}
|