| /* |
| * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| * CA 95054 USA or visit www.sun.com if you need additional information or |
| * have any questions. |
| * |
| */ |
| |
| // This is the source code for the subprocess forked by the Simple |
| // Windows Debug Server. It assumes most of the responsibility for the |
| // debug session, and processes all of the commands sent by clients. |
| |
| // Disable too-long symbol warnings |
| #pragma warning ( disable : 4786 ) |
| |
| #include <iostream> |
| #include <vector> |
| #include <stdlib.h> |
| #include <assert.h> |
| // Must come before windows.h |
| #include <winsock2.h> |
| #include <windows.h> |
| #include "IOBuf.hpp" |
| #include "libInfo.hpp" |
| #include "LockableList.hpp" |
| #include "Message.hpp" |
| #include "Monitor.hpp" |
| #include "nt4internals.hpp" |
| |
| // Uncomment the #define below to get messages on stderr |
| // #define DEBUGGING |
| |
| using namespace std; |
| |
| DWORD pid; |
| HANDLE procHandle; |
| IOBuf* ioBuf; |
| |
| // State flags indicating whether the attach to the remote process |
| // definitively succeeded or failed |
| volatile bool attachFailed = false; |
| volatile bool attachSucceeded = false; |
| |
| // State flag indicating whether the target process is suspended. |
| // Modified by suspend()/resume(), viewed by debug thread, but only |
| // under cover of the threads lock. |
| volatile bool suspended = false; |
| |
| // State flags indicating whether we are considered to be attached to |
| // the target process and are therefore queuing up events to be sent |
| // back to the debug server. These flags are only accessed and |
| // modified under the cover of the eventLock. |
| Monitor* eventLock; |
| // The following is set to true when a client is attached to this process |
| volatile bool generateDebugEvents = false; |
| // Pointer to current debug event; non-NULL indicates a debug event is |
| // waiting to be sent to the client. Main thread sets this to NULL to |
| // indicate that the event has been consumed; also sets |
| // passEventToClient, below. |
| volatile DEBUG_EVENT* curDebugEvent = NULL; |
| // Set by main thread to indicate whether the most recently posted |
| // debug event should be passed on to the target process. |
| volatile bool passEventToClient = true; |
| |
| void conditionalPostDebugEvent(DEBUG_EVENT* ev, DWORD* continueOrNotHandledFlag) { |
| // FIXME: make it possible for the client to enable and disable |
| // certain types of events (have to do so in a platform-independent |
| // manner) |
| switch (ev->dwDebugEventCode) { |
| case EXCEPTION_DEBUG_EVENT: |
| switch (ev->u.Exception.ExceptionRecord.ExceptionCode) { |
| case EXCEPTION_BREAKPOINT: break; |
| case EXCEPTION_SINGLE_STEP: break; |
| case EXCEPTION_ACCESS_VIOLATION: break; |
| default: return; |
| } |
| } |
| eventLock->lock(); |
| if (generateDebugEvents) { |
| curDebugEvent = ev; |
| while (curDebugEvent != NULL) { |
| eventLock->wait(); |
| } |
| if (passEventToClient) { |
| *continueOrNotHandledFlag = DBG_EXCEPTION_NOT_HANDLED; |
| } else { |
| *continueOrNotHandledFlag = DBG_CONTINUE; |
| } |
| } |
| eventLock->unlock(); |
| } |
| |
| |
| //---------------------------------------------------------------------- |
| // Module list |
| // |
| |
| vector<LibInfo> libs; |
| |
| //---------------------------------------------------------------------- |
| // Thread list |
| // |
| |
| struct ThreadInfo { |
| DWORD tid; |
| HANDLE thread; |
| |
| ThreadInfo(DWORD tid, HANDLE thread) { |
| this->tid = tid; |
| this->thread = thread; |
| } |
| }; |
| |
| class ThreadList : public LockableList<ThreadInfo> { |
| public: |
| bool removeByThreadID(DWORD tid) { |
| for (InternalListType::iterator iter = internalList.begin(); |
| iter != internalList.end(); iter++) { |
| if ((*iter).tid == tid) { |
| internalList.erase(iter); |
| return true; |
| } |
| } |
| return false; |
| } |
| HANDLE threadIDToHandle(DWORD tid) { |
| for (InternalListType::iterator iter = internalList.begin(); |
| iter != internalList.end(); iter++) { |
| if ((*iter).tid == tid) { |
| return (*iter).thread; |
| } |
| } |
| return NULL; |
| } |
| }; |
| |
| ThreadList threads; |
| |
| //---------------------------------------------------------------------- |
| // INITIALIZATION AND TERMINATION |
| // |
| |
| void |
| printError(const char* prefix) { |
| DWORD detail = GetLastError(); |
| LPTSTR message; |
| FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | |
| FORMAT_MESSAGE_FROM_SYSTEM, |
| 0, |
| detail, |
| 0, |
| (LPTSTR) &message, |
| 1, |
| NULL); |
| // FIXME: This is signaling an error: "The handle is invalid." ? |
| // Do I have to do all of my WaitForDebugEvent calls from the same thread? |
| cerr << prefix << ": " << message << endl; |
| LocalFree(message); |
| } |
| |
| void |
| endProcess(bool waitForProcess = true) { |
| NT4::unloadNTDLL(); |
| if (waitForProcess) { |
| // Though we're exiting because of an error, do not tear down the |
| // target process. |
| WaitForSingleObject(procHandle, INFINITE); |
| } |
| CloseHandle(procHandle); |
| exit(0); |
| } |
| |
| DWORD WINAPI |
| debugThreadEntry(void*) { |
| #ifdef DEBUGGING |
| DWORD lastMsgId = 0; |
| int count = 0; |
| #endif |
| |
| if (!DebugActiveProcess(pid)) { |
| attachFailed = true; |
| return 0; |
| } |
| |
| // Wait for debug events. We keep the information from some of these |
| // on the side in anticipation of later queries by the client. NOTE |
| // that we leave the process running. The main thread is responsible |
| // for suspending and resuming all currently-active threads upon |
| // client attach and detach. |
| |
| while (true) { |
| DEBUG_EVENT ev; |
| if (!WaitForDebugEvent(&ev, INFINITE)) { |
| #ifdef DEBUGGING |
| if (++count < 10) { |
| // FIXME: This is signaling an error: "The handle is invalid." ? |
| // Do I have to do all of my WaitForDebugEvent calls from the same thread? |
| printError("WaitForDebugEvent failed"); |
| } |
| #endif |
| } else { |
| |
| #ifdef DEBUGGING |
| if (ev.dwDebugEventCode != lastMsgId) { |
| lastMsgId = ev.dwDebugEventCode; |
| count = 0; |
| cerr << "Debug thread received event " << ev.dwDebugEventCode << endl; |
| } else { |
| if (++count < 10) { |
| cerr << "Debug thread received event " << ev.dwDebugEventCode << endl; |
| } |
| } |
| #endif |
| |
| DWORD dbgContinueMode = DBG_CONTINUE; |
| |
| switch (ev.dwDebugEventCode) { |
| case LOAD_DLL_DEBUG_EVENT: |
| conditionalPostDebugEvent(&ev, &dbgContinueMode); |
| break; |
| |
| case UNLOAD_DLL_DEBUG_EVENT: |
| conditionalPostDebugEvent(&ev, &dbgContinueMode); |
| break; |
| |
| case CREATE_PROCESS_DEBUG_EVENT: |
| threads.lock(); |
| // FIXME: will this deal properly with child processes? If |
| // not, is it possible to make it do so? |
| #ifdef DEBUGGING |
| cerr << "CREATE_PROCESS_DEBUG_EVENT " << ev.dwThreadId |
| << " " << ev.u.CreateProcessInfo.hThread << endl; |
| #endif |
| if (ev.u.CreateProcessInfo.hThread != NULL) { |
| threads.add(ThreadInfo(ev.dwThreadId, ev.u.CreateProcessInfo.hThread)); |
| } |
| threads.unlock(); |
| break; |
| |
| case CREATE_THREAD_DEBUG_EVENT: |
| threads.lock(); |
| #ifdef DEBUGGING |
| cerr << "CREATE_THREAD_DEBUG_EVENT " << ev.dwThreadId |
| << " " << ev.u.CreateThread.hThread << endl; |
| #endif |
| if (suspended) { |
| // Suspend this thread before adding it to the thread list |
| SuspendThread(ev.u.CreateThread.hThread); |
| } |
| threads.add(ThreadInfo(ev.dwThreadId, ev.u.CreateThread.hThread)); |
| threads.unlock(); |
| break; |
| |
| case EXIT_THREAD_DEBUG_EVENT: |
| threads.lock(); |
| #ifdef DEBUGGING |
| cerr << "EXIT_THREAD_DEBUG_EVENT " << ev.dwThreadId << endl; |
| #endif |
| threads.removeByThreadID(ev.dwThreadId); |
| threads.unlock(); |
| break; |
| |
| case EXCEPTION_DEBUG_EVENT: |
| // cerr << "EXCEPTION_DEBUG_EVENT" << endl; |
| switch (ev.u.Exception.ExceptionRecord.ExceptionCode) { |
| case EXCEPTION_BREAKPOINT: |
| // cerr << "EXCEPTION_BREAKPOINT" << endl; |
| if (!attachSucceeded && !attachFailed) { |
| attachSucceeded = true; |
| } |
| break; |
| |
| default: |
| dbgContinueMode = DBG_EXCEPTION_NOT_HANDLED; |
| break; |
| } |
| conditionalPostDebugEvent(&ev, &dbgContinueMode); |
| break; |
| |
| case EXIT_PROCESS_DEBUG_EVENT: |
| endProcess(false); |
| // NOT REACHED |
| break; |
| |
| default: |
| #ifdef DEBUGGING |
| cerr << "Received debug event " << ev.dwDebugEventCode << endl; |
| #endif |
| break; |
| } |
| |
| ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, dbgContinueMode); |
| } |
| } |
| } |
| |
| bool |
| attachToProcess() { |
| // Create event lock |
| eventLock = new Monitor(); |
| |
| // Get a process handle for later |
| procHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); |
| if (procHandle == NULL) { |
| return false; |
| } |
| |
| // Start up the debug thread |
| DWORD debugThreadId; |
| if (CreateThread(NULL, 0, &debugThreadEntry, NULL, 0, &debugThreadId) == NULL) { |
| // Failed to make background debug thread. Fail. |
| return false; |
| } |
| |
| while ((!attachSucceeded) && (!attachFailed)) { |
| Sleep(1); |
| } |
| |
| if (attachFailed) { |
| return false; |
| } |
| |
| assert(attachSucceeded); |
| |
| return true; |
| } |
| |
| bool |
| readMessage(Message* msg) { |
| DWORD numRead; |
| if (!ReadFile(GetStdHandle(STD_INPUT_HANDLE), |
| msg, |
| sizeof(Message), |
| &numRead, |
| NULL)) { |
| return false; |
| } |
| if (numRead != sizeof(Message)) { |
| return false; |
| } |
| // For "poke" messages, must follow up by reading raw data |
| if (msg->type == Message::POKE) { |
| char* dataBuf = new char[msg->pokeArg.numBytes]; |
| if (dataBuf == NULL) { |
| return false; |
| } |
| if (!ReadFile(GetStdHandle(STD_INPUT_HANDLE), |
| dataBuf, |
| msg->pokeArg.numBytes, |
| &numRead, |
| NULL)) { |
| delete[] dataBuf; |
| return false; |
| } |
| if (numRead != msg->pokeArg.numBytes) { |
| delete[] dataBuf; |
| return false; |
| } |
| msg->pokeArg.data = (void *) dataBuf; |
| } |
| return true; |
| } |
| |
| void |
| handlePeek(Message* msg) { |
| #ifdef DEBUGGING |
| cerr << "Entering handlePeek()" << endl; |
| #endif |
| |
| char* memBuf = new char[msg->peekArg.numBytes]; |
| if (memBuf == NULL) { |
| ioBuf->writeString("B"); |
| ioBuf->writeBinChar(0); |
| ioBuf->flush(); |
| delete[] memBuf; |
| return; |
| } |
| |
| // Try fast case first |
| DWORD numRead; |
| BOOL res = ReadProcessMemory(procHandle, |
| (LPCVOID) msg->peekArg.address, |
| memBuf, |
| msg->peekArg.numBytes, |
| &numRead); |
| if (res && (numRead == msg->peekArg.numBytes)) { |
| |
| // OK, complete success. Phew. |
| #ifdef DEBUGGING |
| cerr << "Peek success case" << endl; |
| #endif |
| ioBuf->writeString("B"); |
| ioBuf->writeBinChar(1); |
| ioBuf->writeBinUnsignedInt(numRead); |
| ioBuf->writeBinChar(1); |
| ioBuf->writeBinBuf(memBuf, numRead); |
| } else { |
| #ifdef DEBUGGING |
| cerr << "*** Peek slow case ***" << endl; |
| #endif |
| |
| ioBuf->writeString("B"); |
| ioBuf->writeBinChar(1); |
| |
| // Use VirtualQuery to speed things up a bit |
| DWORD numLeft = msg->peekArg.numBytes; |
| char* curAddr = (char*) msg->peekArg.address; |
| while (numLeft > 0) { |
| MEMORY_BASIC_INFORMATION memInfo; |
| VirtualQueryEx(procHandle, curAddr, &memInfo, sizeof(memInfo)); |
| DWORD numToRead = memInfo.RegionSize; |
| if (numToRead > numLeft) { |
| numToRead = numLeft; |
| } |
| DWORD numRead; |
| if (memInfo.State == MEM_COMMIT) { |
| // Read the process memory at this address for this length |
| // FIXME: should check the result of this read |
| ReadProcessMemory(procHandle, curAddr, memBuf, |
| numToRead, &numRead); |
| // Write this out |
| #ifdef DEBUGGING |
| cerr << "*** Writing " << numToRead << " bytes as mapped ***" << endl; |
| #endif |
| ioBuf->writeBinUnsignedInt(numToRead); |
| ioBuf->writeBinChar(1); |
| ioBuf->writeBinBuf(memBuf, numToRead); |
| } else { |
| // Indicate region is free |
| #ifdef DEBUGGING |
| cerr << "*** Writing " << numToRead << " bytes as unmapped ***" << endl; |
| #endif |
| ioBuf->writeBinUnsignedInt(numToRead); |
| ioBuf->writeBinChar(0); |
| } |
| curAddr += numToRead; |
| numLeft -= numToRead; |
| } |
| } |
| |
| ioBuf->flush(); |
| delete[] memBuf; |
| #ifdef DEBUGGING |
| cerr << "Exiting handlePeek()" << endl; |
| #endif |
| } |
| |
| void |
| handlePoke(Message* msg) { |
| #ifdef DEBUGGING |
| cerr << "Entering handlePoke()" << endl; |
| #endif |
| DWORD numWritten; |
| BOOL res = WriteProcessMemory(procHandle, |
| (LPVOID) msg->pokeArg.address, |
| msg->pokeArg.data, |
| msg->pokeArg.numBytes, |
| &numWritten); |
| if (res && (numWritten == msg->pokeArg.numBytes)) { |
| // Success |
| ioBuf->writeBoolAsInt(true); |
| #ifdef DEBUGGING |
| cerr << " (Succeeded)" << endl; |
| #endif |
| } else { |
| // Failure |
| ioBuf->writeBoolAsInt(false); |
| #ifdef DEBUGGING |
| cerr << " (Failed)" << endl; |
| #endif |
| } |
| ioBuf->writeEOL(); |
| ioBuf->flush(); |
| // We clean up the data |
| char* dataBuf = (char*) msg->pokeArg.data; |
| delete[] dataBuf; |
| #ifdef DEBUGGING |
| cerr << "Exiting handlePoke()" << endl; |
| #endif |
| } |
| |
| bool |
| suspend() { |
| if (suspended) { |
| return false; |
| } |
| // Before we suspend, we must take a snapshot of the loaded module |
| // names and base addresses, since acquiring this snapshot requires |
| // starting and exiting a thread in the remote process (at least on |
| // NT 4). |
| libs.clear(); |
| #ifdef DEBUGGING |
| cerr << "Starting suspension" << endl; |
| #endif |
| libInfo(pid, libs); |
| #ifdef DEBUGGING |
| cerr << " Got lib info" << endl; |
| #endif |
| threads.lock(); |
| #ifdef DEBUGGING |
| cerr << " Got thread lock" << endl; |
| #endif |
| suspended = true; |
| int j = 0; |
| for (int i = 0; i < threads.size(); i++) { |
| j++; |
| SuspendThread(threads.get(i).thread); |
| } |
| #ifdef DEBUGGING |
| cerr << "Suspended " << j << " threads" << endl; |
| #endif |
| threads.unlock(); |
| return true; |
| } |
| |
| bool |
| resume() { |
| if (!suspended) { |
| return false; |
| } |
| threads.lock(); |
| suspended = false; |
| for (int i = 0; i < threads.size(); i++) { |
| ResumeThread(threads.get(i).thread); |
| } |
| threads.unlock(); |
| #ifdef DEBUGGING |
| cerr << "Resumed process" << endl; |
| #endif |
| return true; |
| } |
| |
| int |
| main(int argc, char **argv) |
| { |
| if (argc != 2) { |
| // Should only be used by performing CreateProcess within SwDbgSrv |
| exit(1); |
| } |
| |
| if (sscanf(argv[1], "%u", &pid) != 1) { |
| exit(1); |
| } |
| |
| // Try to attach to process |
| if (!attachToProcess()) { |
| // Attach failed. Notify parent by writing result to stdout file |
| // handle. |
| char res = 0; |
| DWORD numBytes; |
| WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), &res, sizeof(res), |
| &numBytes, NULL); |
| exit(1); |
| } |
| |
| // Server is expecting success result back. |
| char res = 1; |
| DWORD numBytes; |
| WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), &res, sizeof(res), |
| &numBytes, NULL); |
| |
| // Initialize our I/O buffer |
| ioBuf = new IOBuf(32768, 131072); |
| ioBuf->setOutputFileHandle(GetStdHandle(STD_OUTPUT_HANDLE)); |
| |
| // At this point we are attached. Enter our main loop which services |
| // requests from the server. Note that in order to handle attach/ |
| // detach properly (i.e., resumption of process upon "detach") we |
| // will need another thread which handles debug events. |
| while (true) { |
| // Read a message from the server |
| Message msg; |
| if (!readMessage(&msg)) { |
| endProcess(); |
| } |
| |
| #ifdef DEBUGGING |
| cerr << "Main thread read message: " << msg.type << endl; |
| #endif |
| |
| switch (msg.type) { |
| // ATTACH and DETACH messages MUST come in pairs |
| case Message::ATTACH: |
| suspend(); |
| eventLock->lock(); |
| generateDebugEvents = true; |
| eventLock->unlock(); |
| break; |
| |
| case Message::DETACH: |
| eventLock->lock(); |
| generateDebugEvents = false; |
| // Flush remaining event if any |
| if (curDebugEvent != NULL) { |
| curDebugEvent = NULL; |
| eventLock->notifyAll(); |
| } |
| eventLock->unlock(); |
| resume(); |
| break; |
| |
| case Message::LIBINFO: |
| { |
| if (!suspended) { |
| ioBuf->writeInt(0); |
| } else { |
| // Send back formatted text |
| ioBuf->writeInt(libs.size()); |
| for (int i = 0; i < libs.size(); i++) { |
| ioBuf->writeSpace(); |
| ioBuf->writeInt(1); |
| ioBuf->writeSpace(); |
| ioBuf->writeInt(libs[i].name.size()); |
| ioBuf->writeSpace(); |
| ioBuf->writeString(libs[i].name.c_str()); |
| ioBuf->writeSpace(); |
| ioBuf->writeAddress(libs[i].base); |
| } |
| } |
| ioBuf->writeEOL(); |
| ioBuf->flush(); |
| break; |
| } |
| |
| case Message::PEEK: |
| handlePeek(&msg); |
| break; |
| |
| case Message::POKE: |
| handlePoke(&msg); |
| break; |
| |
| case Message::THREADLIST: |
| { |
| if (!suspended) { |
| ioBuf->writeInt(0); |
| } else { |
| threads.lock(); |
| ioBuf->writeInt(threads.size()); |
| for (int i = 0; i < threads.size(); i++) { |
| ioBuf->writeSpace(); |
| ioBuf->writeAddress((void*) threads.get(i).thread); |
| } |
| threads.unlock(); |
| } |
| ioBuf->writeEOL(); |
| ioBuf->flush(); |
| break; |
| } |
| |
| case Message::DUPHANDLE: |
| { |
| HANDLE dup; |
| if (DuplicateHandle(procHandle, |
| msg.handleArg.handle, |
| GetCurrentProcess(), |
| &dup, |
| 0, |
| FALSE, |
| DUPLICATE_SAME_ACCESS)) { |
| ioBuf->writeBoolAsInt(true); |
| ioBuf->writeSpace(); |
| ioBuf->writeAddress((void*) dup); |
| } else { |
| ioBuf->writeBoolAsInt(false); |
| } |
| ioBuf->writeEOL(); |
| ioBuf->flush(); |
| break; |
| } |
| |
| case Message::CLOSEHANDLE: |
| { |
| CloseHandle(msg.handleArg.handle); |
| break; |
| } |
| |
| case Message::GETCONTEXT: |
| { |
| if (!suspended) { |
| ioBuf->writeBoolAsInt(false); |
| } else { |
| CONTEXT context; |
| context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; |
| if (GetThreadContext(msg.handleArg.handle, &context)) { |
| ioBuf->writeBoolAsInt(true); |
| // EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP, EIP, DS, ES, FS, GS, |
| // CS, SS, EFLAGS, DR0, DR1, DR2, DR3, DR6, DR7 |
| // See README-commands.txt |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Eax); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Ebx); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Ecx); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Edx); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Esi); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Edi); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Ebp); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Esp); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Eip); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.SegDs); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.SegEs); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.SegFs); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.SegGs); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.SegCs); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.SegSs); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.EFlags); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Dr0); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Dr1); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Dr2); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Dr3); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Dr6); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) context.Dr7); |
| } else { |
| ioBuf->writeBoolAsInt(false); |
| } |
| } |
| ioBuf->writeEOL(); |
| ioBuf->flush(); |
| break; |
| } |
| |
| case Message::SETCONTEXT: |
| { |
| if (!suspended) { |
| ioBuf->writeBoolAsInt(false); |
| } else { |
| CONTEXT context; |
| context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; |
| context.Eax = msg.setContextArg.Eax; |
| context.Ebx = msg.setContextArg.Ebx; |
| context.Ecx = msg.setContextArg.Ecx; |
| context.Edx = msg.setContextArg.Edx; |
| context.Esi = msg.setContextArg.Esi; |
| context.Edi = msg.setContextArg.Edi; |
| context.Ebp = msg.setContextArg.Ebp; |
| context.Esp = msg.setContextArg.Esp; |
| context.Eip = msg.setContextArg.Eip; |
| context.SegDs = msg.setContextArg.Ds; |
| context.SegEs = msg.setContextArg.Es; |
| context.SegFs = msg.setContextArg.Fs; |
| context.SegGs = msg.setContextArg.Gs; |
| context.SegCs = msg.setContextArg.Cs; |
| context.SegSs = msg.setContextArg.Ss; |
| context.EFlags = msg.setContextArg.EFlags; |
| context.Dr0 = msg.setContextArg.Dr0; |
| context.Dr1 = msg.setContextArg.Dr1; |
| context.Dr2 = msg.setContextArg.Dr2; |
| context.Dr3 = msg.setContextArg.Dr3; |
| context.Dr6 = msg.setContextArg.Dr6; |
| context.Dr7 = msg.setContextArg.Dr7; |
| if (SetThreadContext(msg.setContextArg.handle, &context)) { |
| ioBuf->writeBoolAsInt(true); |
| } else { |
| ioBuf->writeBoolAsInt(false); |
| } |
| } |
| ioBuf->writeEOL(); |
| ioBuf->flush(); |
| break; |
| } |
| |
| case Message::SELECTORENTRY: |
| { |
| LDT_ENTRY entry; |
| |
| if (GetThreadSelectorEntry(msg.selectorArg.handle, |
| msg.selectorArg.selector, |
| &entry)) { |
| ioBuf->writeBoolAsInt(true); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) entry.LimitLow); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) entry.BaseLow); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) entry.HighWord.Bytes.BaseMid); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) entry.HighWord.Bytes.Flags1); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) entry.HighWord.Bytes.Flags2); |
| ioBuf->writeSpace(); ioBuf->writeAddress((void*) entry.HighWord.Bytes.BaseHi); |
| } else { |
| ioBuf->writeBoolAsInt(false); |
| } |
| |
| ioBuf->writeEOL(); |
| ioBuf->flush(); |
| break; |
| } |
| |
| case Message::SUSPEND: |
| suspend(); |
| break; |
| |
| case Message::RESUME: |
| resume(); |
| break; |
| |
| case Message::POLLEVENT: |
| eventLock->lock(); |
| if (curDebugEvent == NULL) { |
| ioBuf->writeBoolAsInt(false); |
| } else { |
| ioBuf->writeBoolAsInt(true); |
| ioBuf->writeSpace(); |
| threads.lock(); |
| ioBuf->writeAddress((void*) threads.threadIDToHandle(curDebugEvent->dwThreadId)); |
| threads.unlock(); |
| ioBuf->writeSpace(); |
| ioBuf->writeUnsignedInt(curDebugEvent->dwDebugEventCode); |
| // Figure out what else to write |
| switch (curDebugEvent->dwDebugEventCode) { |
| case LOAD_DLL_DEBUG_EVENT: |
| ioBuf->writeSpace(); |
| ioBuf->writeAddress(curDebugEvent->u.LoadDll.lpBaseOfDll); |
| break; |
| |
| case UNLOAD_DLL_DEBUG_EVENT: |
| ioBuf->writeSpace(); |
| ioBuf->writeAddress(curDebugEvent->u.UnloadDll.lpBaseOfDll); |
| break; |
| |
| case EXCEPTION_DEBUG_EVENT: |
| { |
| DWORD code = curDebugEvent->u.Exception.ExceptionRecord.ExceptionCode; |
| ioBuf->writeSpace(); |
| ioBuf->writeUnsignedInt(code); |
| ioBuf->writeSpace(); |
| ioBuf->writeAddress(curDebugEvent->u.Exception.ExceptionRecord.ExceptionAddress); |
| switch (curDebugEvent->u.Exception.ExceptionRecord.ExceptionCode) { |
| case EXCEPTION_ACCESS_VIOLATION: |
| ioBuf->writeSpace(); |
| ioBuf->writeBoolAsInt(curDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[0] != 0); |
| ioBuf->writeSpace(); |
| ioBuf->writeAddress((void*) curDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[1]); |
| break; |
| |
| default: |
| break; |
| } |
| break; |
| } |
| |
| default: |
| break; |
| } |
| } |
| eventLock->unlock(); |
| ioBuf->writeEOL(); |
| ioBuf->flush(); |
| break; |
| |
| case Message::CONTINUEEVENT: |
| eventLock->lock(); |
| if (curDebugEvent == NULL) { |
| ioBuf->writeBoolAsInt(false); |
| } else { |
| curDebugEvent = NULL; |
| passEventToClient = msg.boolArg.val; |
| ioBuf->writeBoolAsInt(true); |
| eventLock->notify(); |
| } |
| eventLock->unlock(); |
| ioBuf->writeEOL(); |
| ioBuf->flush(); |
| break; |
| } |
| } |
| |
| endProcess(); |
| |
| // NOT REACHED |
| return 0; |
| } |