📄 phpdebugger.py
字号:
#---------------------------------------------------------------------------# Name: PHPDebugger.py# Purpose: php dbg client and supporting code# Author: Matt Fryer, Kevin Wang# Created: 2/1/06# Copyright: (c) 2006 ActiveGrid, Inc.# License: wxWindows License#---------------------------------------------------------------------------import osimport socketimport sysimport threadingimport tracebackimport wximport DebuggerServiceimport activegrid.util.sysutils as sysutilsDBGC_REPLY = 0x0 # reply to previous DBGA_REQUEST requestDBGC_STARTUP = 0x0001 # script startupDBGC_END = 0x0002 # script doneDBGC_BREAKPOINT = 0x0003 # user definded breakpoint occuredDBGC_STEPINTO_DONE = 0x0004 # step to the next statement is completedDBGC_STEPOVER_DONE = 0x0005 # step to the next statement is completedDBGC_STEPOUT_DONE = 0x0006 # step to the next statement is completedDBGC_EMBEDDED_BREAK = 0x0007 # breakpoint caused by DebugBreak() functionDBGC_ERROR = 0x0010 # error occuredDBGC_LOG = 0x0011 # logging supportDBGC_SID = 0x0012 # send SIDDBGC_PAUSE = 0x0013 # pause current session as soon as possibleDBGC_AG_SHUTDOWN_REQ = 0x0201 # special ActiveGrid UI shutdown listening thread commandFRAME_STACK = 100000 # "call:stack" - e.g. backtraceFRAME_SOURCE = 100100 # source textFRAME_SRC_TREE = 100200 # tree of source filesFRAME_RAWDATA = 100300 # raw data or stringFRAME_ERROR = 100400 # error notificationFRAME_EVAL = 100500 # evaluating/watchingFRAME_BPS = 100600 # set/remove breakpointFRAME_BPL = 100700 # breakpoint(s) request = get the listFRAME_VER = 100800 # version requestFRAME_SID = 100900 # session id infoFRAME_SRCLINESINFO = 101000 # source lines infoFRAME_SRCCTXINFO = 101100 # source contexts infoFRAME_LOG = 101200 # loggingFRAME_PROF = 101300 # profilerFRAME_PROF_C = 101400 # profiler counter/accuracyFRAME_SET_OPT = 101500 # set/update optionsDBGF_STARTED = 0x0001 # debugger has been startedDBGF_FINISHED = 0x0002 # DBGC_END notification has been sentDBGF_WAITACK = 0x0004 # awaiting replay|requestDBGF_UNSYNC = 0x0008 # protocol has been unsynchronizedDBGF_REQUESTPENDING = 0x0010 # Debug session request pendingDBGF_REQUESTFOUND = 0x0020 # Debug session request foundDBGF_REJECTIONFOUND = 0x0040 # DBGSESSID=-1 found - session rejectionE_ERROR = 1 << 0E_WARNING = 1 << 1E_PARSE = 1 << 2E_NOTICE = 1 << 3E_CORE_ERROR = 1 << 4E_CORE_WARNING = 1 << 5E_COMPILE_ERROR = 1 << 6E_COMPILE_WARNING = 1 << 7E_USER_ERROR = 1 << 8E_USER_WARNING = 1 << 9E_USER_NOTICE = 1 << 10BPS_DELETED = 0BPS_DISABLED = 1BPS_ENABLED = 2BPS_UNRESOLVED = 0x100DBG_SYNC = 0x5953DBG_SYNC2_STR = chr(0) + chr(0) + chr(89) + chr(83)RESPONSE_HEADER_SIZE = 16_VERBOSE = Falsedef myprint(format, vlist=None): if _VERBOSE: if vlist: print format % vlist else: print format## 4 Char's to an Integer#def C4ToInt(ch, startPos): retval = 0 pos = startPos retval = retval + (CharToInt(ch[pos]) << 24) pos = pos + 1 retval = retval + (CharToInt(ch[pos]) << 16) pos = pos + 1 retval = retval + (CharToInt(ch[pos]) << 8) pos = pos + 1 retval = retval + (CharToInt(ch[pos]) << 0) return retvaldef CharToInt(ch): return int((ord(ch) & 0x00FF));## An Integer to 4 Char's#def IntToC4(num): retval = chr((num >> 24) & 0x00FF) retval += chr((num >> 16) & 0x00FF) retval += chr((num >> 8 ) & 0x00FF) retval += chr((num >> 0 ) & 0x00FF) return retvalDBGA_CONTINUE = IntToC4(0x8001)DBGA_STOP = IntToC4(0x8002)DBGA_STEPINTO = IntToC4(0x8003)DBGA_STEPOVER = IntToC4(0x8004)DBGA_STEPOUT = IntToC4(0x8005)DBGA_IGNORE = IntToC4(0x8006)DBGA_REQUEST = IntToC4(0x8010)def getCommandString(code): if code == DBGC_REPLY: return "REPLY" elif code == DBGC_STARTUP: return "STARTUP" elif code == DBGC_END: return "END" elif code == DBGC_BREAKPOINT: return "BREAKPOINT" elif code == DBGC_STEPINTO_DONE: return "STEPINTO DONE" elif code == DBGC_STEPOVER_DONE: return "STEPOVER DONE" elif code == DBGC_STEPOUT_DONE: return "STEPOUT DONE" elif code == DBGC_EMBEDDED_BREAK: return "EMBEDDED BREAK" elif code == DBGC_PAUSE: return "PAUSE" elif code == DBGC_ERROR: return "ERROR" elif code == DBGC_LOG: return "LOG" elif code == DBGC_SID: return "SEND SID" elif code == DBGC_AG_SHUTDOWN_REQ: return "AG SHUTDOWN REQ"def reportFlags(flagsValue): flagsRetVal = "" if flagsValue & DBGF_STARTED: # debugger has been started flagsRetVal += "started+" if flagsValue & DBGF_FINISHED: # DBGC_END notification has been sent flagsRetVal += "finished+" if flagsValue & DBGF_WAITACK: # awaiting replay|request flagsRetVal += "awaiting ack+" if flagsValue & DBGF_UNSYNC: # protocol has been unsynchronized flagsRetVal += "protocol unsynchronized+" if flagsValue & DBGF_REQUESTPENDING: # Debug session request pending flagsRetVal += "request pending+" if flagsValue & DBGF_REQUESTFOUND: # Debug session request found flagsRetVal += "request found+" if flagsValue & DBGF_REJECTIONFOUND : # DBGSESSID=-1 found - session rejection flagsRetVal += "session rejection+" return flagsRetValdef getErrorTypeString(code): if code == E_ERROR: return "[Error]" elif code == E_WARNING: return "[Warning]" elif code == E_PARSE: return "[Parse Error]" elif code == E_NOTICE: return "[Notice]" elif code == E_CORE_ERROR: return "[Core Error]" elif code == E_CORE_WARNING: return "[Core Warning]" elif code == E_COMPILE_ERROR: return "[Compile Error]" elif code == E_COMPILE_WARNING: return "[Compile Warning]" elif code == E_USER_ERROR: return "[User Error]" elif code == E_USER_WARNING: return "[User Warning]" elif code == E_USER_NOTICE: return "[User Notice]" else: return "[Unexpected Error]"class ResponseHeader(object): def __init__(self, conn, blocking = False): self.isValid = False receivedData = conn.recv(RESPONSE_HEADER_SIZE, blocking) if not receivedData: myprint("Tried to get %d bytes of PHP DBG header, got None\n" % RESPONSE_HEADER_SIZE) return elif len(receivedData) != RESPONSE_HEADER_SIZE: myprint("Tried to get %d bytes of PHP DBG header, got %d\n" % (RESPONSE_HEADER_SIZE, len(receivedData))) return self.sync = C4ToInt(receivedData, 0) self.command = C4ToInt(receivedData, 4) self.flags = C4ToInt(receivedData, 8) self.toRead = C4ToInt(receivedData, 12) myprint("ResponseHeader: sync=%x, command=%s, flags=(%s), toRead=%s\n", (self.sync, getCommandString(self.command), reportFlags(self.flags), self.toRead)) if self.sync != DBG_SYNC: myprint("Sync wrong for header! Expected %x, got %s\n" % (DBG_SYNC, self.sync)) return self.isValid = Trueclass ResponsePacketFrame(object): def __init__(self, conn, size, data, blocking = False): self.isValid = False self.conn = conn self.data = '' if data: self.data = data newlyReceived = False elif conn: newlyReceived = True sizeToReceive = size while True: oneChunk = conn.recv(sizeToReceive, blocking) sizeReceived = len(oneChunk) if sizeReceived > 0: self.data = self.data + oneChunk if sizeReceived < sizeToReceive: sizeToReceive = sizeToReceive - sizeReceived continue else: break if len(self.data) != size: myprint("Expected to get %d bytes of a PHP DBG packet, got %d\n" % (size, len(self.data))) return else: return self.frameName = C4ToInt(self.data, 0) self.frameSize = C4ToInt(self.data, 4) if newlyReceived: myprint("Newly received ResponsePacketFrame: frameName=%d, frameSize=%d", (self.frameName, self.frameSize)) else: myprint("Created from existing ResponsePacketFrame: frameName=%d, frameSize=%d", (self.frameName, self.frameSize)) if self.frameSize == 0: return self.currPos = 8 self.totalDataLen = len(self.data) self.length = 8 + self.frameSize self.isValid = True myprint("new ResponsePacketFrame: currPos=%s, totalDataLen=%s, length=%s", (repr(self.currPos), repr(self.totalDataLen), repr(self.length))) return def getNextInt(self): myprint("getNextInt(): currPos=%s, totalDataLen=%s, length=%s", (repr(self.currPos), repr(self.totalDataLen), repr(self.length))) if self.isValid and self.currPos + 4 <= self.length: val = C4ToInt(self.data, self.currPos) self.currPos = self.currPos + 4 myprint("getNextInt(): got an integar: %s", repr(val)) return val else: return self._errorReturn("getNextInt(): no more integar available with current frame: ") def getNextString(self, strLen): endPos = self.currPos + strLen if self.isValid and endPos <= self.length: # # Trim the ending '\0'. TODO: confirm this applies to all raw string data. # str = self.data[self.currPos:endPos - 1] self.currPos = endPos myprint("getNextString(): got a string: %s", str) return str else: return self._errorReturn("getNextString(): no more string available with current frame: ") def getNextFrame(self, useAbsolutePos = False): if useAbsolutePos: # # Skip this frame's header (8 bytes for frameSize and frameSize) and frame data (frameSize). # self.currPos = self.length if self.isValid and self.currPos < self.totalDataLen: return ResponsePacketFrame(None, None, self.data[self.currPos:]) else: return self._errorReturn("getNextFrame(): no more frame available with current frame: ") def _errorReturn(self, preMsg = ''): myprint(preMsg + "frameName=%s, frameSize=%s, totalDataLen=%s, length=%s, currPos:%s", (repr(self.frameName), repr(self.frameSize), repr(self.totalDataLen), repr(self.length), repr(self.currPos))) self.isValid = False return Noneclass PHPDBGFrame(object): FRAME_HEADER_SIZE = 8 def __init__(self, frameType): self.frameType = IntToC4(frameType) self.frameData = "" def addInt(self, intVal): self.frameData = self.frameData + IntToC4(intVal) def addChar(self, charVal): self.frameData = self.frameData + charVal def addStr(self, string): # # Add the trailing '\0'. # self.frameData = self.frameData + string + '\0' def getSize(self): return len(self.frameData) + PHPDBGFrame.FRAME_HEADER_SIZE def writeFrame(self, conn): header = self.frameType + IntToC4(len(self.frameData)) conn.sendall(header) conn.sendall(self.frameData)class PHPDBGPacket(object): def __init__(self, packetType): self.header = DBG_SYNC2_STR + packetType self.frames = [] self.packetSize = 0 def addFrame(self, frame): self.frames.append(frame) self.packetSize += frame.getSize() def sendPacket(self, conn, flags = 0): self.header += IntToC4(flags) self.header += IntToC4(self.packetSize) conn.sendall(self.header) for frame in self.frames: frame.writeFrame(conn)class PHPDBGException(Exception): def __init__(self, msg = None, cause = None): if (msg == None): Exception.__init__(self) elif (cause == None): Exception.__init__(self, msg) else: Exception.__init__(self, "PHPDBGException: message:%s\n, cause:%s" % (msg, cause))class PHPDBGConnException(PHPDBGException): pass
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -