@@ -17,7 +17,7 @@ def commands():
1717 Returns a list of custom LLDB command instances defined in this module.
1818 The registration logic in lldbinit.py calls this function.
1919 """
20- return [DTHexDumpCommand ()]
20+ return [DTHexDumpCommand (), DTPrintFrameCommand () ]
2121
2222
2323class DTHexDumpCommand (bc .BaseCommand ):
@@ -168,3 +168,107 @@ def run(self, args, options):
168168 print (f"0x{ current_addr :016x} { hex_part } { ascii_part } " )
169169
170170 print ("" )
171+
172+
173+ class DTPrintFrameCommand (bc .BaseCommand ):
174+ """
175+ A custom LLDB command that performs a low-level architectural inspection
176+ of the current thread's topmost stack frame (Frame #0).
177+
178+ Usage: print_frame
179+
180+ This command extracts raw CPU execution context and translates it into high-value
181+ metadata for reverse-engineering. It automates symbolication, resolves memory bounds,
182+ and dynamically calculates ASLR slides to map live runtime execution directly to
183+ static analysis tools (like Hopper Disassembler or IDA Pro).
184+ """
185+
186+ def name (self ):
187+ """Returns the command name as it will be used in the LLDB console."""
188+ return "print_frame"
189+
190+ def description (self ):
191+ """Returns a short description of the command's functionality."""
192+ return "Display architecture-level details of the current stack frame."
193+
194+ def options (self ):
195+ """This command does not require any additional arguments or options."""
196+ return []
197+
198+ def run (self , args , options ):
199+ """
200+ The main execution logic for the print_frame command.
201+ """
202+
203+ target = lldb .debugger .GetSelectedTarget ()
204+ process = target .GetProcess ()
205+
206+ if not process or not process .IsValid ():
207+ print ("error: valid active process not found" )
208+ return
209+
210+ thread = process .GetSelectedThread ()
211+
212+ if not thread or not thread .IsValid ():
213+ print ("error: no active thread discovered" )
214+ return
215+
216+ frame = thread .GetSelectedFrame () if thread else None
217+
218+ if not frame or not frame .IsValid ():
219+ print ("error: unable to resolve valid stack frame #0" )
220+ return
221+
222+ pc_address = frame .GetPC ()
223+ sp_address = frame .GetSP ()
224+ frame_idx = frame .GetFrameID ()
225+
226+ symbol_context = frame .GetSymbolContext (lldb .eSymbolContextEverything )
227+
228+ module = symbol_context .GetModule ()
229+ module_name = (
230+ module .GetFileSpec ().GetFilename ()
231+ if module and module .IsValid ()
232+ else "Unknown Module"
233+ )
234+
235+ function_name = "Unknown Function"
236+
237+ if symbol_context .GetFunction ().IsValid ():
238+ function_name = symbol_context .GetFunction ().GetName ()
239+ elif symbol_context .GetSymbol ().IsValid ():
240+ function_name = symbol_context .GetSymbol ().GetName ()
241+
242+ aslr_offset_str = "N/A"
243+
244+ if module and module .IsValid ():
245+ header_load_addr = module .GetObjectFileHeaderAddress ().GetLoadAddress (
246+ target
247+ )
248+ header_file_addr = module .GetObjectFileHeaderAddress ().GetFileAddress ()
249+
250+ if header_load_addr != lldb .LLDB_INVALID_ADDRESS :
251+ aslr_slide = header_load_addr - header_file_addr
252+ relative_offset = pc_address - header_load_addr
253+ aslr_offset_str = (
254+ f"0x{ relative_offset :x} (Module ASLR Slide: 0x{ aslr_slide :x} )"
255+ )
256+
257+ print ("\n " + "=" * 80 )
258+ print (f"[FRAME #{ frame_idx } ] -> CURRENT CPU ARCHITECTURE CONTEXT" )
259+ print ("=" * 80 )
260+ print (f"• Module: { module_name } " )
261+ print (f"• Function: { function_name } " )
262+ print (f"• Address PC: 0x{ pc_address :016x} " )
263+ print (f"• File Offset: { aslr_offset_str } " )
264+ print (f"• Stack SP: 0x{ sp_address :016x} " )
265+
266+ line_entry = symbol_context .GetLineEntry ()
267+ if line_entry and line_entry .IsValid ():
268+ print (
269+ f"• Source File: { line_entry .GetFileSpec ().GetFilename ()} :{ line_entry .GetLine ()} "
270+ )
271+
272+ print ("=" * 80 + "\n " )
273+
274+ return
0 commit comments