Skip to content

Commit

Permalink
Merge pull request #58 from doronz88/bugfix/bp_callbacks
Browse files Browse the repository at this point in the history
Bugfix/bp callbacks
  • Loading branch information
doronz88 authored Jun 9, 2024
2 parents 23358f8 + b64585b commit d4056ba
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 13 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,10 @@ Here is a gist of methods you can access from `p`:
- Import & reload given python module (intended mainly for external snippets)
- `unwind`
- Unwind the stack (useful when get_evaluation_unwind() == False)
- `set_selected_thread`
- sets the currently selected thread, which is used in other parts of the program, such as displaying disassembly or
checking registers.
This ensures the application focuses on the specified thread for these operations.
## Magic functions
Expand Down
8 changes: 4 additions & 4 deletions hilda/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ def attach(name: str, pid: int, startup_files: Optional[List[str]] = None) -> No
@click.argument('exec_path')
@click.option('--argv', multiple=True, help='Command line arguments to pass to the process')
@click.option('--envp', multiple=True, callback=parse_envp, help='Environment variables in the form KEY=VALUE')
@click.option('--stdin', type=Path, help='Redirect stdin from this file path')
@click.option('--stdout', type=Path, help='Redirect stdout to this file path')
@click.option('--stderr', type=Path, help='Redirect stderr to this file path')
@click.option('--cwd', type=Path, help='Set the working directory for the process')
@click.option('--stdin', type=str, help='Redirect stdin from this file path')
@click.option('--stdout', type=str, help='Redirect stdout to this file path')
@click.option('--stderr', type=str, help='Redirect stderr to this file path')
@click.option('--cwd', type=str, help='Set the working directory for the process')
@click.option('--flags', type=click.INT, default=0, help='Launch flags (bitmask)')
@click.option('--stop-at-entry', is_flag=True, help='Stop the process at the entry point')
@startup_files_option
Expand Down
9 changes: 9 additions & 0 deletions hilda/common.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
from datetime import datetime
from typing import Any, List, Mapping, Tuple, Union

import inquirer3
from inquirer3.themes import GreenPassion

CfSerializable = Union[
Mapping[str, Any], List, Tuple[Any, ...], str, bool, float, bytes, datetime, None]


def selection_prompt(options_list: List):
question = [inquirer3.List('choice', message='choose device', choices=options_list, carousel=True)]
result = inquirer3.prompt(question, theme=GreenPassion(), raise_keyboard_interrupt=True)
return result['choice']
7 changes: 6 additions & 1 deletion hilda/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
__all__ = ['HildaException', 'SymbolAbsentError', 'EvaluatingExpressionError', 'CreatingObjectiveCSymbolError',
'ConvertingToNsObjectError', 'ConvertingFromNSObjectError', 'DisableJetsamMemoryChecksError',
'GettingObjectiveCClassError', 'AccessingRegisterError', 'AccessingMemoryError',
'BrokenLocalSymbolsJarError', 'AddingLldbSymbolError', 'LLDBException']
'BrokenLocalSymbolsJarError', 'AddingLldbSymbolError', 'LLDBException', 'InvalidThreadIndexError']


class HildaException(Exception):
Expand Down Expand Up @@ -70,3 +70,8 @@ class BrokenLocalSymbolsJarError(HildaException):
class AddingLldbSymbolError(HildaException):
""" Raise when failing to convert a LLDB symbol to Hilda's symbol. """
pass


class InvalidThreadIndexError(HildaException):
""" Raise when thread idx invalid """
pass
21 changes: 16 additions & 5 deletions hilda/hilda_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@
from traitlets.config import Config

from hilda import objective_c_class
from hilda.common import CfSerializable
from hilda.common import CfSerializable, selection_prompt
from hilda.exceptions import AccessingMemoryError, AccessingRegisterError, AddingLldbSymbolError, \
BrokenLocalSymbolsJarError, ConvertingFromNSObjectError, ConvertingToNsObjectError, CreatingObjectiveCSymbolError, \
DisableJetsamMemoryChecksError, EvaluatingExpressionError, HildaException, SymbolAbsentError
DisableJetsamMemoryChecksError, EvaluatingExpressionError, HildaException, InvalidThreadIndexError, \
SymbolAbsentError
from hilda.lldb_importer import lldb
from hilda.objective_c_symbol import ObjectiveCSymbol
from hilda.registers import Registers
Expand Down Expand Up @@ -510,7 +511,7 @@ def callback(hilda, frame, bp_loc, options):
if options.get('name', False):
name = options['name']

log_message = f'🚨 #{bp.id} 0x{symbol:x} {name}'
log_message = f'🚨 #{bp.id} 0x{symbol:x} {name} - Thread #{self.thread.idx}:{hex(self.thread.id)}'

if 'regs' in options:
log_message += '\nregs:'
Expand All @@ -524,7 +525,7 @@ def callback(hilda, frame, bp_loc, options):
value = hilda.symbol(hilda.evaluate_expression(name))
log_message += f'\n\t{name} = {hilda._monitor_format_value(fmt, value)}'

if options.get('force_return', False):
if options.get('force_return', None) is not None:
hilda.force_return(options['force_return'])
log_message += f'\nforced return: {options["force_return"]}'

Expand All @@ -533,7 +534,7 @@ def callback(hilda, frame, bp_loc, options):
hilda.finish()
hilda.bt()

if options.get('retval', False):
if options.get('retval', None) is not None:
# return from function
hilda.finish()
value = hilda.evaluate_expression('$arg1')
Expand Down Expand Up @@ -880,6 +881,16 @@ def import_module(self, filename: str, name: Optional[str] = None) -> Any:
spec.loader.exec_module(m)
return m

def set_selected_thread(self, idx: Optional[int] = None) -> None:
if idx is None:
thread = selection_prompt(self.process.threads)
else:
try:
thread = [t for t in self.process.threads if t.idx == idx][0]
except IndexError:
raise InvalidThreadIndexError()
self.process.SetSelectedThread(thread)

def unwind(self) -> bool:
""" Unwind the stack (useful when get_evaluation_unwind() == False) """
return self.thread.UnwindInnermostExpression().Success()
Expand Down
17 changes: 15 additions & 2 deletions hilda/launch_lldb.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,22 @@ def run(self):
logger.debug(f'Process Exited with status {self.process.GetExitStatus()}')
self.should_quit = True
elif state == lldb.eStateRunning and last_state == lldb.eStateStopped:
logger.debug("Process Continued")
logger.debug('Process Continued')
elif state == lldb.eStateStopped and last_state == lldb.eStateRunning:
logger.debug('Process Stopped')
for thread in self.process:
frame = thread.GetFrameAtIndex(0)
stop_reason = thread.GetStopReason()
logger.debug(f'tid = {hex(thread.GetThreadID())} pc = {frame.GetPC()}')
if stop_reason not in [lldb.eStopReasonSignal, lldb.eStopReasonException,
lldb.eStopReasonBreakpoint,
lldb.eStopReasonWatchpoint, lldb.eStopReasonPlanComplete,
lldb.eStopReasonTrace,
lldb.eStopReasonSignal]:
continue
self.process.SetSelectedThread(thread)
break

last_state = state


Expand Down Expand Up @@ -114,7 +127,7 @@ def _create_target(self) -> lldb.SBTarget:
return self.debugger.CreateTargetWithFileAndArch(None, None)

def _create_process(self) -> lldb.SBProcess:
logger.debug(f'Attaching to {self.name}')
logger.debug(f'Attaching to {self.proc_name}')
return self.target.AttachToProcessWithName(self.listener, self.proc_name, self.wait_for, self.error)


Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ objc_types_decoder
construct
pymobiledevice3
keystone-engine
tabulate
tabulate
inquirer3

0 comments on commit d4056ba

Please sign in to comment.