33import getpass
44import re
55from logging import getLogger
6- from typing import TYPE_CHECKING , List , Union
6+ from typing import TYPE_CHECKING , List , Union , overload
77if TYPE_CHECKING :
88 from ptrlib .connection .unixproc import UnixProcess
99
@@ -45,17 +45,31 @@ def __init__(self, pid: int):
4545 @property
4646 @attached
4747 def gdb (self ) -> UnixProcess :
48+ """Debugger session
49+ """
4850 return self ._gdb
51+
52+ @property
53+ def debug (self ) -> bool :
54+ """Debug mode
55+ """
56+ return self ._gdb .debug
4957
58+ @debug .setter
59+ def debug (self , mode : bool ):
60+ self ._gdb .debug = mode
61+
5062 @property
5163 def pid (self ) -> int :
64+ """PID of the target process
65+ """
5266 return self ._pid
5367
5468 def _attach_direct (self ):
5569 self ._gdb = unix_process ()(["gdb" , "-q" , "-p" , str (self ._pid )])
5670 init_msg = self ._gdb .recvuntil (self ._gdb_prompt , lookahead = True )
5771 if GDB_PTRACE_ERROR in init_msg :
58- raise PermissionError
72+ raise PermissionError ( f"Cannot attach pid= { self . _pid } " )
5973
6074 def _attach_with_sudo (self ):
6175 self ._gdb = unix_process ()([
@@ -70,17 +84,24 @@ def _attach_with_sudo(self):
7084 init_msg = self ._gdb .recvuntil (self ._gdb_prompt , lookahead = True )
7185
7286 if GDB_PTRACE_ERROR in init_msg :
73- raise PermissionError
87+ raise PermissionError ( f"Cannot attach pid= { self . _pid } " )
7488
7589 @detached
7690 def attach (self ) -> 'UnixProcessDebugger' :
7791 """Attach to process with GDB.
92+
93+ Return:
94+ UnixProcessDebugger: This debugger instance.
7895 """
96+ # TODO: Check if target process has already been attached
97+ # TODO: Check if we have root privilege
7998 try :
8099 self ._attach_direct ()
100+ return self
81101 except PermissionError :
82- self . _attach_with_sudo ()
102+ pass
83103
104+ self ._attach_with_sudo ()
84105 return self
85106
86107 @attached
@@ -90,19 +111,55 @@ def detach(self):
90111 self ._gdb .close ()
91112 del self ._gdb
92113
114+ @overload
115+ def execute (self , command : str , resume : bool = False ) -> str : ...
116+ @overload
117+ def execute (self , command : List [str ], resume : bool = False ) -> List [str ]: ...
93118 @attached
94- def execute (self , command : str ) -> str :
119+ def execute (self ,
120+ command : Union [str , List [str ]],
121+ resume : bool = False ) -> Union [str , List [str ]]:
122+ """Execute a GDB command.
123+
124+ Args:
125+ command (str): A command to execute, or a list of commands.
126+ resume (bool): If true, continue execution after all commands are done.
127+
128+ Returns:
129+ str: Result of the command.
130+
131+ Examples:
132+ ```
133+ conn = sock.process.attach()
134+ stdout = int(conn.execute("p/x &_IO_2_1_stdout_").split(' = ')[1], 16)
135+ conn.execute("break puts", resume=True)
136+ sock.sendline(b"Hello")
137+ res = conn.execute([
138+ f"set {{long}}{stdout} = 0xfbad1887",
139+ f"x/4xg {stdout}"
140+ ])
141+ print(res[1])
142+ ```
143+ """
144+ if isinstance (command , list ):
145+ result = [self .execute (c ) for c in command ]
146+ if resume :
147+ self .execute ('continue' )
148+ return result
149+
95150 self ._gdb .after (self ._gdb_prompt ).sendline (command )
96- # TODO: self._gdb.before(self._gdb_prompt).lastline()
151+ # TODO: self._gdb.before(self._gdb_prompt).lastline() to keep color sequence
97152 result = self ._gdb .recvuntil (self ._gdb_prompt , drop = True , lookahead = True )
98153 # Remove ANSI escape sequences aggressively
99- return CTRL_RE .sub (b'' , ANSI_RE .sub (b'' , result )).decode ().strip ()
154+ result = CTRL_RE .sub (b'' , ANSI_RE .sub (b'' , result )).decode ().strip ()
155+ if resume :
156+ self ._gdb .after (self ._gdb_prompt ).sendline ("continue" )
157+ return result
100158
101- def batch_execute (self , commands : List [str ]) -> List [str ]:
102- return [self .execute (command ) for command in commands ]
103-
104159 @attached
105160 def interactive (self ):
161+ """Interact with GDB terminal
162+ """
106163 self ._gdb .interactive (prompt = '' )
107164 # Return a prompt for further use
108165 self ._gdb .unget (b'(gdb) ' )
0 commit comments