;;; ;;; debug-loop.lisp ;;; ;;; Debug event monitoring loop. ;;; (cl:defpackage :remote-debug (:use :common-lisp :sb-alien) ;(:export #:*message-function*) ) (cl:in-package :remote-debug) ;;; Process creation flags. (defconstant +create-breakaway-from-job+ #x01000000) (defconstant +create-default-error-mode+ #x04000000) (defconstant +create-new-console+ #x00000010) (defconstant +create-new-process-group+ #x00000200) (defconstant +create-no-window+ #x08000000) (defconstant +create-protected-process+ #x00040000) (defconstant +create-preserve-code-authz-level+ #x02000000) (defconstant +create-separate-wow-vdm+ #x00000800) (defconstant +create-shared-wow-vdm+ #x00001000) (defconstant +create-suspended+ #x00000004) (defconstant +create-unicode-environment+ #x00000400) (defconstant +debug-only-this-process+ #x00000002) (defconstant +debug-process+ #x00000001) (defconstant +detached-process+ #x00000008) (defconstant +extended-startupinfo-present+ #x00080000) ;;; Startup Information Flags. (defconstant +startf-forceofffeedback+ 128) (defconstant +startf-forceonfeedback+ 64) (defconstant +startf-runfullscreen+ 32) (defconstant +startf-usecountchars+ 8) (defconstant +startf-usefillattribute+ 16) (defconstant +startf-useposition+ 4) (defconstant +startf-useshowwindow+ 1) (defconstant +startf-usesize+ 2) (defconstant +startf-usestdhandles+ 256) ;;; Typedefs. (define-alien-type word unsigned-short) (define-alien-type dword unsigned-long) ;;; Structures. (define-alien-type nil (struct process-information (hprocess sb-win32:handle) (hthread sb-win32:handle) (dwprocessid dword) (dwthreadid dword))) (define-alien-type nil (struct startupinfo (cb dword) (lpreserved sb-win32::system-string) (lpdesktop sb-win32::system-string) (lptitle sb-win32::system-string) (dwx dword) (dwy dword) (dwxsize dword) (dwysize dword) (dwxcountchars dword) (dwycountchars dword) (dwfillattribute dword) (dwflags dword) (wshowwindow word) (cbreserved2 word) (lpreserved2 (* t)) (hstdinput sb-win32:handle) (hstdoutput sb-win32:handle) (hstderror sb-win32:handle))) (defun start-debuggee-process () (with-alien ((s-i (struct startupinfo)) (p-i (struct process-information))) (setf (slot s-i 'cb) 68 (slot s-i 'lpreserved) nil (slot s-i 'lpdesktop) nil (slot s-i 'lptitle) nil (slot s-i 'dwflags) 0 (slot s-i 'cbreserved2) 0 (slot s-i 'lpreserved2) nil) (alien-funcall (extern-alien "CreateProcessW" (function int sb-win32::system-string sb-win32::system-string (* t) (* t) int dword (* t) sb-win32::system-string (* (struct startupinfo)) (* (struct process-information)))) "c:/windows/system32/notepad.exe" nil nil nil 0 (logior +create-new-console+ +debug-process+ +debug-only-this-process+ ) nil nil (addr s-i) (addr p-i)) (format t "process: ~A [~A]~%thread: ~A [~A]~%" (slot p-i 'hprocess) (slot p-i 'dwprocessid) (slot p-i 'hthread) (slot p-i 'dwthreadid)))) (defconstant +exception-maximum-parameters+ 15) (define-alien-type nil (struct exception-record (exceptioncode dword) (exceptionflags dword) (exceptionrecord (* (struct exception-record))) (exceptionaddress (* t)) (numberparameters dword) (exceptioninformation (array dword 15 #|+exception-maximum-parameters+|#)))) (defconstant +create-process-debug-event+ 3) (defconstant +create-thread-debug-event+ 2) (defconstant +exception-debug-event+ 1) (defconstant +exit-process-debug-event+ 5) (defconstant +exit-thread-debug-event+ 4) (defconstant +load-dll-debug-event+ 6) (defconstant +output-debug-string-debug-event+ 8) (defconstant +rip-event+ 9) (defconstant +unload-dlll-debug-event+ 7) (define-alien-type nil (struct create-process-debug-info (hfile sb-win32:handle) (hprocess sb-win32:handle) (hthread sb-win32:handle) (lpbaseofimage (* t)) (dwdebuginfofileoffset dword) (ndebuginfosize dword) (lpthreadlocalbase (* t)) (lpstartaddress (* t)) (lpimagename (* t)) (funicode word))) (define-alien-type nil (struct create-thread-debug-info (hthread sb-win32:handle) (lpthreadlocalbase (* t)) (lpstartaddress (* t)))) (define-alien-type nil (struct exit-process-debug-info (dwexitcode dword))) (define-alien-type nil (struct exit-thread-debug-info (dwexitcode dword))) (define-alien-type nil (struct exception-debug-info (exceptionrecord (struct exception-record)) (dwfirstchance dword))) (define-alien-type nil (struct load-dll-debug-info (hfile sb-win32:handle) (lpbaseofdll (* t)) (dwdebuginfofileoffset dword) (ndebuginfosize dword) (lpimagename (* t)) (funicode word))) (define-alien-type nil (struct output-debug-string-info (lpdebugstringdata dword) (funicode word) (ndebugstringlength word))) (define-alien-type nil (struct unload-dll-debug-info (baseofdll (* t)))) (define-alien-type nil (struct rip-info (dwerror dword) (dwtype dword))) (define-alien-type nil (struct debug-event (dwdebugeventcode dword) (dwprocessid dword) (dwthreadid dword) (u (union nil (exception (struct exception-debug-info)) (createthread (struct create-thread-debug-info)) (createprocessinfo (struct create-process-debug-info)) (exitthread (struct exit-thread-debug-info)) (exitprocess (struct exit-process-debug-info)) (loaddll (struct load-dll-debug-info)) (unloaddll (struct unload-dll-debug-info)) (debugstring (struct output-debug-string-info)) (ripinfo (struct rip-info)))))) (defparameter *debug-event-code-plist* '(1 +exception-debug-event+ 2 +create-thread-debug-event+ 3 +create-process-debug-event+ 4 +exit-thread-debug-event+ 5 +exit-process-debug-event+ 6 +load-dll-debug-event+ 7 +unload-dlll-debug-event+ 8 +output-debug-string-debug-event+ 9 +rip-event+)) (defun get-debug-event () (with-alien ((de (struct debug-event))) (if (zerop (alien-funcall (extern-alien "WaitForDebugEvent" (function int (* (struct debug-event)) dword)) (addr de) 0)) (format t "failed: ~A~%" (sb-win32::get-last-error-message (sb-win32:get-last-error))) (progn (format t "code: ~A~%process: ~A~%thread: ~A~%" (getf *debug-event-code-plist* (slot de 'dwdebugeventcode)) (slot de 'dwprocessid) (slot de 'dwthreadid)) (continue-debug-event (slot de 'dwprocessid) (slot de 'dwthreadid)) (labels ((foo (event) (let ((fields (sb-alien-internals:alien-record-type-fields (sb-alien-internals:alien-value-type event)))) (loop for field in fields for field-name = (sb-alien-internals:alien-record-field-name field) for field-value = (slot event field-name) do (if (and (typep field-value 'sb-alien-internals:alien-value) (typep (sb-alien-internals:alien-value-type field-value) 'sb-alien-internals:alien-record-type)) (progn (format t "~A: --~%" field-name) (foo field-value) (format t "--~%")) (format t "~A: ~A~%" field-name field-value)))))) (case (slot de 'dwdebugeventcode) (#.+exception-debug-event+ (foo (slot (slot de 'u) 'exception))) (#.+create-thread-debug-event+ (foo (slot (slot de 'u) 'createthread))) (#.+create-process-debug-event+ (foo (slot (slot de 'u) 'createprocessinfo))) (#.+exit-thread-debug-event+ (foo (slot (slot de 'u) 'exitthread))) (#.+exit-process-debug-event+ (foo (slot (slot de 'u) 'exitprocess))) (#.+load-dll-debug-event+ (foo (slot (slot de 'u) 'loaddll))) (#.+unload-dlll-debug-event+ (foo (slot (slot de 'u) 'unloaddll))) (#.+output-debug-string-debug-event+ (foo (slot (slot de 'u) 'debugstring))) (#.+rip-event+ (foo (slot (slot de 'u) 'ripinfo))))))))) (defun continue-debug-event (processid threadid) (when (zerop (alien-funcall (extern-alien "ContinueDebugEvent" (function int dword dword dword)) processid threadid #x10002)) (format t "failed: ~A~%" (sb-win32::get-last-error-message (sb-win32:get-last-error))))) (declaim (inline close-handle)) (defun close-handle (handle) (alien-funcall (extern-alien "CloseHandle" (function int sb-win32:handle)) handle)) ;;; Memory access. (define-alien-type nil (struct memory-basic-information (base-address dword) (allocation-base dword) (allocation-size dword) (region-size dword) (state dword) (protect dword) (type dword))) (declaim (inline virtual-query-ex)) (defun virtual-query-ex (hprocess lpaddress) (with-alien ((mbi (struct memory-basic-information))) (unless (zerop (alien-funcall (extern-alien "VirtualQueryEx" (function int sb-win32:handle dword (* (struct memory-basic-information)) dword)) hprocess lpaddress (addr mbi) (alien-size (struct memory-basic-information)))) (let ((fields (sb-alien-internals:alien-record-type-fields (sb-alien-internals:alien-value-type mbi)))) (loop for field in fields for field-name = (sb-alien-internals:alien-record-field-name field) for field-value = (slot mbi field-name) do (format t "~A: ~A~%" field-name field-value)))))) ;;; Context access. (defconstant +context-i386+ #x10000) (defconstant +context-control+ (logior +context-i386+ #x01)) (defconstant +context-integer+ (logior +context-i386+ #x02)) (defconstant +context-segments+ (logior +context-i386+ #x04)) (defconstant +context-floating-point+ (logior +context-i386+ #x08)) (defconstant +context-debug-registers+ (logior +context-i386+ #x10)) (defconstant +context-extended-registers+ (logior +context-i386+ #x20)) (defconstant +context-full+ (logior +context-control+ +context-integer+ +context-segments+)) (defconstant +maximum-supported-extension+ 512) (define-alien-type nil (struct floating-save-area (control-word dword) (status-word dword) (tag-word dword) (error-offset dword) (error-selector dword) (data-offset dword) (data-selector dword) (register-area (array unsigned-char 80)) (cr0-npx-state dword))) (define-alien-type nil (struct context (context-flags dword) (dr0 dword) (dr1 dword) (dr2 dword) (dr3 dword) (dr6 dword) (dr7 dword) (float-save (struct floating-save-area)) (seg-gs dword) (seg-fs dword) (seg-es dword) (seg-ds dword) (edi dword) (esi dword) (ebx dword) (edx dword) (ecx dword) (eax dword) (ebp dword) (eip dword) (seg-cs dword) (eflags dword) (esp dword) (seg-ss dword) (extended-registers (array unsigned-char 512 #| +maximum-supported-extension+ |#)))) (defun formatify (alien) (let ((fields (sb-alien-internals:alien-record-type-fields (sb-alien-internals:alien-value-type alien)))) (loop for field in fields for field-name = (sb-alien-internals:alien-record-field-name field) for field-value = (slot alien field-name) do (format t "~A: ~X~%" field-name field-value)))) (defun display-thread-context (thread) (with-alien ((context (struct context))) (setf (slot context 'context-flags) +context-full+) (alien-funcall (extern-alien "GetThreadContext" (function int sb-win32:handle (* (struct context)))) thread (addr context)) (formatify context))) ;;; EOF