hilda 2.0.1__py3-none-any.whl → 2.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hilda/_version.py +2 -2
- hilda/cli.py +37 -30
- hilda/common.py +9 -0
- hilda/exceptions.py +9 -7
- hilda/hilda_client.py +72 -36
- hilda/launch_lldb.py +52 -53
- {hilda-2.0.1.dist-info → hilda-2.0.3.dist-info}/METADATA +146 -191
- {hilda-2.0.1.dist-info → hilda-2.0.3.dist-info}/RECORD +12 -12
- {hilda-2.0.1.dist-info → hilda-2.0.3.dist-info}/LICENSE +0 -0
- {hilda-2.0.1.dist-info → hilda-2.0.3.dist-info}/WHEEL +0 -0
- {hilda-2.0.1.dist-info → hilda-2.0.3.dist-info}/entry_points.txt +0 -0
- {hilda-2.0.1.dist-info → hilda-2.0.3.dist-info}/top_level.txt +0 -0
hilda/_version.py
CHANGED
hilda/cli.py
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from typing import List,
|
|
3
|
+
from typing import List, Optional
|
|
4
4
|
|
|
5
5
|
import click
|
|
6
6
|
import coloredlogs
|
|
7
7
|
|
|
8
8
|
from hilda import launch_lldb
|
|
9
9
|
from hilda._version import version
|
|
10
|
+
from hilda.launch_lldb import create_hilda_client_using_attach_by_name, create_hilda_client_using_attach_by_pid, \
|
|
11
|
+
create_hilda_client_using_launch, create_hilda_client_using_remote_attach
|
|
10
12
|
|
|
11
13
|
DEFAULT_HILDA_PORT = 1234
|
|
12
14
|
|
|
@@ -18,7 +20,7 @@ def cli():
|
|
|
18
20
|
pass
|
|
19
21
|
|
|
20
22
|
|
|
21
|
-
startup_files_option = click.option('-f', '--startup_files', multiple=True,
|
|
23
|
+
startup_files_option = click.option('-f', '--startup_files', multiple=True, help='Files to run on start')
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
def parse_envp(ctx: click.Context, param: click.Parameter, value: List[str]) -> List[str]:
|
|
@@ -36,54 +38,59 @@ def parse_envp(ctx: click.Context, param: click.Parameter, value: List[str]) ->
|
|
|
36
38
|
@click.argument('hostname', default='localhost')
|
|
37
39
|
@click.argument('port', type=click.INT, default=DEFAULT_HILDA_PORT)
|
|
38
40
|
@startup_files_option
|
|
39
|
-
def remote(hostname: str, port: int, startup_files:
|
|
41
|
+
def remote(hostname: str, port: int, startup_files: List[str]) -> None:
|
|
40
42
|
""" Connect to remote debugserver at given address """
|
|
41
|
-
|
|
43
|
+
with create_hilda_client_using_remote_attach(hostname, port) as hilda_client:
|
|
44
|
+
hilda_client.interact(startup_files=startup_files)
|
|
42
45
|
|
|
43
46
|
|
|
44
47
|
@cli.command('attach')
|
|
45
48
|
@click.option('-n', '--name', help='process name to attach')
|
|
46
49
|
@click.option('-p', '--pid', type=click.INT, help='pid to attach')
|
|
47
50
|
@startup_files_option
|
|
48
|
-
def attach(name: str, pid: int, startup_files:
|
|
51
|
+
def attach(name: Optional[str], pid: Optional[int], startup_files: List[str]) -> None:
|
|
49
52
|
""" Attach to given process and start a lldb shell """
|
|
50
|
-
|
|
53
|
+
if name is not None:
|
|
54
|
+
hilda_client = create_hilda_client_using_attach_by_name(name)
|
|
55
|
+
elif pid is not None:
|
|
56
|
+
hilda_client = create_hilda_client_using_attach_by_pid(name)
|
|
57
|
+
else:
|
|
58
|
+
raise click.UsageError('You must specify a process name or pid')
|
|
59
|
+
hilda_client.interact(startup_files=startup_files)
|
|
60
|
+
hilda_client.detach()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@cli.command('bare')
|
|
64
|
+
def cli_bare() -> None:
|
|
65
|
+
""" Just start a lldb shell """
|
|
66
|
+
commands = [f'command script import {Path(__file__).resolve().parent / "lldb_entrypoint.py"}']
|
|
67
|
+
commands = '\n'.join(commands)
|
|
68
|
+
launch_lldb.execute(f'lldb --one-line "{commands}"')
|
|
51
69
|
|
|
52
70
|
|
|
53
71
|
@cli.command('launch')
|
|
54
72
|
@click.argument('exec_path')
|
|
55
73
|
@click.option('--argv', multiple=True, help='Command line arguments to pass to the process')
|
|
56
74
|
@click.option('--envp', multiple=True, callback=parse_envp, help='Environment variables in the form KEY=VALUE')
|
|
57
|
-
@click.option('--stdin',
|
|
58
|
-
@click.option('--stdout',
|
|
59
|
-
@click.option('--stderr',
|
|
60
|
-
@click.option('--cwd',
|
|
75
|
+
@click.option('--stdin', help='Redirect stdin from this file path')
|
|
76
|
+
@click.option('--stdout', help='Redirect stdout to this file path')
|
|
77
|
+
@click.option('--stderr', help='Redirect stderr to this file path')
|
|
78
|
+
@click.option('--cwd', help='Set the working directory for the process')
|
|
61
79
|
@click.option('--flags', type=click.INT, default=0, help='Launch flags (bitmask)')
|
|
62
80
|
@click.option('--stop-at-entry', is_flag=True, help='Stop the process at the entry point')
|
|
63
81
|
@startup_files_option
|
|
64
|
-
def launch(exec_path: str, argv:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
flags: Optional[int] = 0, stop_at_entry: Optional[bool] = False,
|
|
68
|
-
startup_files: Optional[List[str]] = None) -> None:
|
|
82
|
+
def launch(exec_path: str, argv: List[str], envp: List[str], stdin: Optional[Path],
|
|
83
|
+
stdout: Optional[Path], stderr: Optional[Path], cwd: Optional[Path], flags: Optional[int],
|
|
84
|
+
stop_at_entry: Optional[bool], startup_files: List[str]) -> None:
|
|
69
85
|
""" Attach to given process and start a lldb shell """
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
startup_files)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
@cli.command('bare')
|
|
79
|
-
def cli_bare():
|
|
80
|
-
""" Just start a lldb shell """
|
|
81
|
-
commands = [f'command script import {Path(__file__).resolve().parent / "lldb_entrypoint.py"}']
|
|
82
|
-
commands = '\n'.join(commands)
|
|
83
|
-
launch_lldb.execute(f'lldb --one-line "{commands}"')
|
|
86
|
+
argv = list(argv)
|
|
87
|
+
envp = list(envp)
|
|
88
|
+
with create_hilda_client_using_launch(
|
|
89
|
+
exec_path, argv, envp, stdin, stdout, stderr, cwd, flags, stop_at_entry) as hilda_client:
|
|
90
|
+
hilda_client.interact(startup_files=startup_files)
|
|
84
91
|
|
|
85
92
|
|
|
86
93
|
@cli.command('version')
|
|
87
|
-
def cli_version():
|
|
94
|
+
def cli_version() -> None:
|
|
88
95
|
"""Show the version information."""
|
|
89
96
|
click.echo(version)
|
hilda/common.py
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from typing import Any, List, Mapping, Tuple, Union
|
|
3
3
|
|
|
4
|
+
import inquirer3
|
|
5
|
+
from inquirer3.themes import GreenPassion
|
|
6
|
+
|
|
4
7
|
CfSerializable = Union[
|
|
5
8
|
Mapping[str, Any], List, Tuple[Any, ...], str, bool, float, bytes, datetime, None]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def selection_prompt(options_list: List):
|
|
12
|
+
question = [inquirer3.List('choice', message='choose device', choices=options_list, carousel=True)]
|
|
13
|
+
result = inquirer3.prompt(question, theme=GreenPassion(), raise_keyboard_interrupt=True)
|
|
14
|
+
return result['choice']
|
hilda/exceptions.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
__all__ = ['HildaException', 'SymbolAbsentError', 'EvaluatingExpressionError', 'CreatingObjectiveCSymbolError',
|
|
2
2
|
'ConvertingToNsObjectError', 'ConvertingFromNSObjectError', 'DisableJetsamMemoryChecksError',
|
|
3
3
|
'GettingObjectiveCClassError', 'AccessingRegisterError', 'AccessingMemoryError',
|
|
4
|
-
'BrokenLocalSymbolsJarError', 'AddingLldbSymbolError', '
|
|
4
|
+
'BrokenLocalSymbolsJarError', 'AddingLldbSymbolError', 'LLDBError', 'InvalidThreadIndexError']
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class HildaException(Exception):
|
|
@@ -9,12 +9,9 @@ class HildaException(Exception):
|
|
|
9
9
|
pass
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
def __init__(self, message: str):
|
|
16
|
-
super().__init__()
|
|
17
|
-
self.message = message
|
|
12
|
+
class LLDBError(HildaException):
|
|
13
|
+
""" Wrapper for RAW LLDB errors """
|
|
14
|
+
pass
|
|
18
15
|
|
|
19
16
|
|
|
20
17
|
class SymbolAbsentError(HildaException):
|
|
@@ -70,3 +67,8 @@ class BrokenLocalSymbolsJarError(HildaException):
|
|
|
70
67
|
class AddingLldbSymbolError(HildaException):
|
|
71
68
|
""" Raise when failing to convert a LLDB symbol to Hilda's symbol. """
|
|
72
69
|
pass
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class InvalidThreadIndexError(HildaException):
|
|
73
|
+
""" Raise when thread idx invalid """
|
|
74
|
+
pass
|
hilda/hilda_client.py
CHANGED
|
@@ -30,10 +30,11 @@ from tqdm import tqdm
|
|
|
30
30
|
from traitlets.config import Config
|
|
31
31
|
|
|
32
32
|
from hilda import objective_c_class
|
|
33
|
-
from hilda.common import CfSerializable
|
|
33
|
+
from hilda.common import CfSerializable, selection_prompt
|
|
34
34
|
from hilda.exceptions import AccessingMemoryError, AccessingRegisterError, AddingLldbSymbolError, \
|
|
35
35
|
BrokenLocalSymbolsJarError, ConvertingFromNSObjectError, ConvertingToNsObjectError, CreatingObjectiveCSymbolError, \
|
|
36
|
-
DisableJetsamMemoryChecksError, EvaluatingExpressionError, HildaException,
|
|
36
|
+
DisableJetsamMemoryChecksError, EvaluatingExpressionError, HildaException, InvalidThreadIndexError, \
|
|
37
|
+
SymbolAbsentError
|
|
37
38
|
from hilda.lldb_importer import lldb
|
|
38
39
|
from hilda.objective_c_symbol import ObjectiveCSymbol
|
|
39
40
|
from hilda.registers import Registers
|
|
@@ -73,7 +74,7 @@ def disable_logs() -> None:
|
|
|
73
74
|
logging.getLogger('parso.python.diff').disabled = True
|
|
74
75
|
logging.getLogger('humanfriendly.prompts').disabled = True
|
|
75
76
|
logging.getLogger('blib2to3.pgen2.driver').disabled = True
|
|
76
|
-
logging.getLogger('hilda.launch_lldb').
|
|
77
|
+
logging.getLogger('hilda.launch_lldb').setLevel(logging.INFO)
|
|
77
78
|
|
|
78
79
|
|
|
79
80
|
SerializableSymbol = namedtuple('SerializableSymbol', 'address type_ filename')
|
|
@@ -356,17 +357,19 @@ class HildaClient:
|
|
|
356
357
|
if not self.process.Continue().Success():
|
|
357
358
|
self.log_critical('failed to continue process')
|
|
358
359
|
|
|
359
|
-
def detach(self):
|
|
360
|
+
def detach(self) -> None:
|
|
360
361
|
"""
|
|
361
362
|
Detach from process.
|
|
362
363
|
|
|
363
364
|
Useful in order to exit gracefully so process doesn't get killed
|
|
364
365
|
while you exit
|
|
365
366
|
"""
|
|
367
|
+
if not self.process.is_alive:
|
|
368
|
+
return
|
|
366
369
|
if not self.process.Detach().Success():
|
|
367
370
|
self.log_critical('failed to detach')
|
|
368
|
-
|
|
369
|
-
|
|
371
|
+
return
|
|
372
|
+
self.log_info('Process Detached')
|
|
370
373
|
|
|
371
374
|
@stop_is_needed
|
|
372
375
|
def disass(self, address: int, buf: bytes, flavor: str = 'intel',
|
|
@@ -397,30 +400,33 @@ class HildaClient:
|
|
|
397
400
|
|
|
398
401
|
return self.symbol(module.ResolveFileAddress(address).GetLoadAddress(self.target))
|
|
399
402
|
|
|
400
|
-
def get_register(self, name) -> Symbol:
|
|
403
|
+
def get_register(self, name: str) -> Union[float, Symbol]:
|
|
401
404
|
"""
|
|
402
|
-
Get value for register by its name
|
|
403
|
-
|
|
404
|
-
:
|
|
405
|
+
Get value for register by its name. Value can either be an Symbol (int) or a float.
|
|
406
|
+
|
|
407
|
+
:param name: Register name
|
|
408
|
+
:return: Register value
|
|
405
409
|
"""
|
|
406
|
-
|
|
407
|
-
if
|
|
410
|
+
register_value = self.frame.register[name.lower()]
|
|
411
|
+
if register_value is None:
|
|
408
412
|
raise AccessingRegisterError()
|
|
409
|
-
return self.
|
|
413
|
+
return self._get_symbol_or_float_from_sbvalue(register_value)
|
|
410
414
|
|
|
411
|
-
def set_register(self, name, value):
|
|
415
|
+
def set_register(self, name: str, value: Union[float, int]) -> None:
|
|
412
416
|
"""
|
|
413
417
|
Set value for register by its name
|
|
414
|
-
:param name:
|
|
415
|
-
:param value:
|
|
416
|
-
:return:
|
|
418
|
+
:param name: Register name
|
|
419
|
+
:param value: Register value
|
|
417
420
|
"""
|
|
418
421
|
register = self.frame.register[name.lower()]
|
|
419
422
|
if register is None:
|
|
420
423
|
raise AccessingRegisterError()
|
|
421
|
-
|
|
424
|
+
if isinstance(value, int):
|
|
425
|
+
register.value = hex(value)
|
|
426
|
+
else:
|
|
427
|
+
register.value = str(value)
|
|
422
428
|
|
|
423
|
-
def objc_call(self, obj, selector, *params):
|
|
429
|
+
def objc_call(self, obj: int, selector: str, *params):
|
|
424
430
|
"""
|
|
425
431
|
Simulate a call to an objc selector
|
|
426
432
|
:param obj: obj to pass into `objc_msgSend`
|
|
@@ -510,7 +516,7 @@ class HildaClient:
|
|
|
510
516
|
if options.get('name', False):
|
|
511
517
|
name = options['name']
|
|
512
518
|
|
|
513
|
-
log_message = f'🚨 #{bp.id} 0x{symbol:x} {name}'
|
|
519
|
+
log_message = f'🚨 #{bp.id} 0x{symbol:x} {name} - Thread #{self.thread.idx}:{hex(self.thread.id)}'
|
|
514
520
|
|
|
515
521
|
if 'regs' in options:
|
|
516
522
|
log_message += '\nregs:'
|
|
@@ -524,27 +530,32 @@ class HildaClient:
|
|
|
524
530
|
value = hilda.symbol(hilda.evaluate_expression(name))
|
|
525
531
|
log_message += f'\n\t{name} = {hilda._monitor_format_value(fmt, value)}'
|
|
526
532
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
533
|
+
force_return = options.get('force_return')
|
|
534
|
+
if force_return is not None:
|
|
535
|
+
hilda.force_return(force_return)
|
|
536
|
+
log_message += f'\nforced return: {force_return}'
|
|
530
537
|
|
|
531
|
-
if options.get('bt'
|
|
538
|
+
if options.get('bt'):
|
|
532
539
|
# bugfix: for callstacks from xpc events
|
|
533
540
|
hilda.finish()
|
|
534
|
-
hilda.bt()
|
|
541
|
+
for frame in hilda.bt():
|
|
542
|
+
log_message += f'\n\t{frame[0]} {frame[1]}'
|
|
535
543
|
|
|
536
|
-
|
|
544
|
+
retval = options.get('retval')
|
|
545
|
+
if retval is not None:
|
|
537
546
|
# return from function
|
|
538
547
|
hilda.finish()
|
|
539
548
|
value = hilda.evaluate_expression('$arg1')
|
|
540
|
-
log_message += f'\nreturned: {hilda._monitor_format_value(
|
|
549
|
+
log_message += f'\nreturned: {hilda._monitor_format_value(retval, value)}'
|
|
541
550
|
|
|
542
551
|
hilda.log_info(log_message)
|
|
543
552
|
|
|
544
553
|
for cmd in options.get('cmd', []):
|
|
545
554
|
hilda.lldb_handle_command(cmd)
|
|
546
555
|
|
|
547
|
-
if
|
|
556
|
+
if options.get('stop', False):
|
|
557
|
+
hilda.log_info('Process remains stopped and focused on current thread')
|
|
558
|
+
else:
|
|
548
559
|
hilda.cont()
|
|
549
560
|
|
|
550
561
|
return self.bp(address, callback, condition=condition, **options)
|
|
@@ -834,7 +845,7 @@ class HildaClient:
|
|
|
834
845
|
raise ConvertingFromNSObjectError from e
|
|
835
846
|
return json.loads(json_dump, object_hook=self._from_ns_json_object_hook)['root']
|
|
836
847
|
|
|
837
|
-
def evaluate_expression(self, expression) -> Symbol:
|
|
848
|
+
def evaluate_expression(self, expression: str) -> Union[float, Symbol]:
|
|
838
849
|
"""
|
|
839
850
|
Wrapper for LLDB's EvaluateExpression.
|
|
840
851
|
Used for quick code snippets.
|
|
@@ -844,8 +855,8 @@ class HildaClient:
|
|
|
844
855
|
currentDevice = objc_get_class('UIDevice').currentDevice
|
|
845
856
|
evaluate_expression(f'[[{currentDevice} systemName] hasPrefix:@"2"]')
|
|
846
857
|
|
|
847
|
-
:param expression:
|
|
848
|
-
:return:
|
|
858
|
+
:param expression: Expression to evaluate
|
|
859
|
+
:return: Returned value (either float or a Symbol)
|
|
849
860
|
"""
|
|
850
861
|
# prepending a prefix so LLDB knows to return an int type
|
|
851
862
|
if isinstance(expression, int):
|
|
@@ -858,12 +869,12 @@ class HildaClient:
|
|
|
858
869
|
options.SetTryAllThreads(True)
|
|
859
870
|
options.SetUnwindOnError(self.configs.evaluation_unwind_on_error)
|
|
860
871
|
|
|
861
|
-
|
|
872
|
+
sbvalue = self.frame.EvaluateExpression(formatted_expression, options)
|
|
862
873
|
|
|
863
|
-
if not
|
|
864
|
-
raise EvaluatingExpressionError(str(
|
|
874
|
+
if not sbvalue.error.Success():
|
|
875
|
+
raise EvaluatingExpressionError(str(sbvalue.error))
|
|
865
876
|
|
|
866
|
-
return self.
|
|
877
|
+
return self._get_symbol_or_float_from_sbvalue(sbvalue)
|
|
867
878
|
|
|
868
879
|
def import_module(self, filename: str, name: Optional[str] = None) -> Any:
|
|
869
880
|
"""
|
|
@@ -880,6 +891,16 @@ class HildaClient:
|
|
|
880
891
|
spec.loader.exec_module(m)
|
|
881
892
|
return m
|
|
882
893
|
|
|
894
|
+
def set_selected_thread(self, idx: Optional[int] = None) -> None:
|
|
895
|
+
if idx is None:
|
|
896
|
+
thread = selection_prompt(self.process.threads)
|
|
897
|
+
else:
|
|
898
|
+
try:
|
|
899
|
+
thread = [t for t in self.process.threads if t.idx == idx][0]
|
|
900
|
+
except IndexError:
|
|
901
|
+
raise InvalidThreadIndexError()
|
|
902
|
+
self.process.SetSelectedThread(thread)
|
|
903
|
+
|
|
883
904
|
def unwind(self) -> bool:
|
|
884
905
|
""" Unwind the stack (useful when get_evaluation_unwind() == False) """
|
|
885
906
|
return self.thread.UnwindInnermostExpression().Success()
|
|
@@ -1033,7 +1054,7 @@ class HildaClient:
|
|
|
1033
1054
|
ipython_config.InteractiveShellApp.extensions = ['hilda.ipython_extensions.magics',
|
|
1034
1055
|
'hilda.ipython_extensions.events',
|
|
1035
1056
|
'hilda.ipython_extensions.keybindings']
|
|
1036
|
-
ipython_config.InteractiveShellApp.exec_lines = [
|
|
1057
|
+
ipython_config.InteractiveShellApp.exec_lines = ['disable_logs()']
|
|
1037
1058
|
if startup_files is not None:
|
|
1038
1059
|
ipython_config.InteractiveShellApp.exec_files = startup_files
|
|
1039
1060
|
self.log_debug(f'Startup files - {startup_files}')
|
|
@@ -1041,10 +1062,17 @@ class HildaClient:
|
|
|
1041
1062
|
namespace = globals()
|
|
1042
1063
|
namespace.update(locals())
|
|
1043
1064
|
namespace['p'] = self
|
|
1065
|
+
namespace['ui'] = self.ui_manager
|
|
1066
|
+
namespace['cfg'] = self.configs
|
|
1044
1067
|
if additional_namespace is not None:
|
|
1045
1068
|
namespace.update(additional_namespace)
|
|
1046
1069
|
sys.argv = ['a']
|
|
1047
1070
|
IPython.start_ipython(config=ipython_config, user_ns=namespace)
|
|
1071
|
+
|
|
1072
|
+
def __enter__(self) -> 'HildaClient':
|
|
1073
|
+
return self
|
|
1074
|
+
|
|
1075
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
1048
1076
|
self.detach()
|
|
1049
1077
|
|
|
1050
1078
|
@staticmethod
|
|
@@ -1158,3 +1186,11 @@ class HildaClient:
|
|
|
1158
1186
|
'__objc_class_list',
|
|
1159
1187
|
f'{objc_classlist_addr}')
|
|
1160
1188
|
return json.loads(self.po(obj_c_code))
|
|
1189
|
+
|
|
1190
|
+
def _get_symbol_or_float_from_sbvalue(self, value: lldb.SBValue) -> Union[float, Symbol]:
|
|
1191
|
+
# The `value` attribute of an SBValue stores a string representation of the actual value
|
|
1192
|
+
# in a python-compatible format, so we can eval it to get the native python value
|
|
1193
|
+
value = eval(value.value)
|
|
1194
|
+
if isinstance(value, float):
|
|
1195
|
+
return value
|
|
1196
|
+
return self.symbol(value)
|
hilda/launch_lldb.py
CHANGED
|
@@ -4,7 +4,7 @@ from abc import ABC, abstractmethod
|
|
|
4
4
|
from threading import Thread
|
|
5
5
|
from typing import List, Optional
|
|
6
6
|
|
|
7
|
-
from hilda.exceptions import
|
|
7
|
+
from hilda.exceptions import LLDBError
|
|
8
8
|
from hilda.hilda_client import HildaClient
|
|
9
9
|
from hilda.lldb_importer import lldb
|
|
10
10
|
|
|
@@ -14,14 +14,6 @@ lldb.hilda_client = None
|
|
|
14
14
|
logger = logging.getLogger(__name__)
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
def hilda(debugger, startup_files: Optional[List[str]] = None):
|
|
18
|
-
if lldb.hilda_client is None:
|
|
19
|
-
lldb.hilda_client = HildaClient(debugger)
|
|
20
|
-
|
|
21
|
-
additional_namespace = {'ui': lldb.hilda_client.ui_manager, 'cfg': lldb.hilda_client.configs}
|
|
22
|
-
lldb.hilda_client.interact(additional_namespace=additional_namespace, startup_files=startup_files)
|
|
23
|
-
|
|
24
|
-
|
|
25
17
|
def execute(cmd: str) -> int:
|
|
26
18
|
logging.debug(f'executing: {cmd}')
|
|
27
19
|
return os.system(cmd)
|
|
@@ -49,10 +41,10 @@ class LLDBListenerThread(Thread, ABC):
|
|
|
49
41
|
def _create_process(self) -> lldb.SBProcess:
|
|
50
42
|
pass
|
|
51
43
|
|
|
52
|
-
def _check_success(self):
|
|
44
|
+
def _check_success(self) -> None:
|
|
53
45
|
if self.error.Success():
|
|
54
46
|
return
|
|
55
|
-
raise
|
|
47
|
+
raise LLDBError(self.error.description)
|
|
56
48
|
|
|
57
49
|
def run(self):
|
|
58
50
|
event = lldb.SBEvent()
|
|
@@ -67,12 +59,25 @@ class LLDBListenerThread(Thread, ABC):
|
|
|
67
59
|
logger.debug('Process Detached')
|
|
68
60
|
self.should_quit = True
|
|
69
61
|
elif state == lldb.eStateExited:
|
|
70
|
-
logger.
|
|
62
|
+
logger.info(f'Process Exited with status {self.process.GetExitStatus()}')
|
|
71
63
|
self.should_quit = True
|
|
72
64
|
elif state == lldb.eStateRunning and last_state == lldb.eStateStopped:
|
|
73
|
-
logger.debug(
|
|
65
|
+
logger.debug('Process Continued')
|
|
74
66
|
elif state == lldb.eStateStopped and last_state == lldb.eStateRunning:
|
|
75
67
|
logger.debug('Process Stopped')
|
|
68
|
+
for thread in self.process:
|
|
69
|
+
frame = thread.GetFrameAtIndex(0)
|
|
70
|
+
stop_reason = thread.GetStopReason()
|
|
71
|
+
logger.debug(f'tid = {hex(thread.GetThreadID())} pc = {frame.GetPC()}')
|
|
72
|
+
if stop_reason not in [lldb.eStopReasonSignal, lldb.eStopReasonException,
|
|
73
|
+
lldb.eStopReasonBreakpoint,
|
|
74
|
+
lldb.eStopReasonWatchpoint, lldb.eStopReasonPlanComplete,
|
|
75
|
+
lldb.eStopReasonTrace,
|
|
76
|
+
lldb.eStopReasonSignal]:
|
|
77
|
+
continue
|
|
78
|
+
self.process.SetSelectedThread(thread)
|
|
79
|
+
break
|
|
80
|
+
|
|
76
81
|
last_state = state
|
|
77
82
|
|
|
78
83
|
|
|
@@ -114,7 +119,7 @@ class LLDBAttachName(LLDBListenerThread):
|
|
|
114
119
|
return self.debugger.CreateTargetWithFileAndArch(None, None)
|
|
115
120
|
|
|
116
121
|
def _create_process(self) -> lldb.SBProcess:
|
|
117
|
-
logger.debug(f'Attaching to {self.
|
|
122
|
+
logger.debug(f'Attaching to {self.proc_name}')
|
|
118
123
|
return self.target.AttachToProcessWithName(self.listener, self.proc_name, self.wait_for, self.error)
|
|
119
124
|
|
|
120
125
|
|
|
@@ -148,42 +153,36 @@ class LLDBLaunch(LLDBListenerThread):
|
|
|
148
153
|
self.error)
|
|
149
154
|
|
|
150
155
|
|
|
151
|
-
def
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
try:
|
|
185
|
-
lldb_t = LLDBLaunch(exec_path, argv, envp, stdin, stdout, stderr, wd, flags, stop_at_entry)
|
|
186
|
-
lldb_t.start()
|
|
187
|
-
hilda(lldb_t.debugger, startup_files)
|
|
188
|
-
except LLDBException as e:
|
|
189
|
-
logger.error(e.message)
|
|
156
|
+
def _get_hilda_client_from_sbdebugger(debugger: lldb.SBDebugger) -> HildaClient:
|
|
157
|
+
hilda_client = HildaClient(debugger)
|
|
158
|
+
lldb.hilda_client = hilda_client
|
|
159
|
+
hilda_client.init_dynamic_environment()
|
|
160
|
+
return hilda_client
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def create_hilda_client_using_remote_attach(
|
|
164
|
+
hostname: str, port: int) -> HildaClient:
|
|
165
|
+
lldb_t = LLDBRemote(hostname, port)
|
|
166
|
+
lldb_t.start()
|
|
167
|
+
return _get_hilda_client_from_sbdebugger(lldb_t.debugger)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def create_hilda_client_using_launch(
|
|
171
|
+
exec_path: str, argv: Optional[List] = None, envp: Optional[List] = None, stdin: Optional[str] = None,
|
|
172
|
+
stdout: Optional[str] = None, stderr: Optional[str] = None, wd: Optional[str] = None,
|
|
173
|
+
flags: Optional[int] = 0, stop_at_entry: Optional[bool] = False) -> HildaClient:
|
|
174
|
+
lldb_t = LLDBLaunch(exec_path, argv, envp, stdin, stdout, stderr, wd, flags, stop_at_entry)
|
|
175
|
+
lldb_t.start()
|
|
176
|
+
return _get_hilda_client_from_sbdebugger(lldb_t.debugger)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def create_hilda_client_using_attach_by_pid(pid: Optional[int] = None) -> HildaClient:
|
|
180
|
+
lldb_t = LLDBAttachPid(pid)
|
|
181
|
+
lldb_t.start()
|
|
182
|
+
return _get_hilda_client_from_sbdebugger(lldb_t.debugger)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def create_hilda_client_using_attach_by_name(name: Optional[str] = None, wait_for: bool = False) -> HildaClient:
|
|
186
|
+
lldb_t = LLDBAttachName(name, wait_for)
|
|
187
|
+
lldb_t.start()
|
|
188
|
+
return _get_hilda_client_from_sbdebugger(lldb_t.debugger)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: hilda
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.3
|
|
4
4
|
Summary: LLDB wrapped and empowered by iPython's features
|
|
5
5
|
Author-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>, netanel cohen <netanelc305@protonmail.com>
|
|
6
6
|
Maintainer-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>, netanel cohen <netanelc305@protonmail.com>
|
|
@@ -51,37 +51,32 @@ Requires-Dist: construct
|
|
|
51
51
|
Requires-Dist: pymobiledevice3
|
|
52
52
|
Requires-Dist: keystone-engine
|
|
53
53
|
Requires-Dist: tabulate
|
|
54
|
+
Requires-Dist: inquirer3
|
|
54
55
|
Provides-Extra: test
|
|
55
56
|
Requires-Dist: pytest ; extra == 'test'
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
- [
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
- [
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
- [Using snippets](#using-snippets)
|
|
80
|
-
- [Contributing](#contributing)
|
|
81
|
-
|
|
82
|
-
Would you like any further adjustments?
|
|
83
|
-
|
|
84
|
-
# Description
|
|
58
|
+
# Hilda
|
|
59
|
+
|
|
60
|
+
- [Hilda](#hilda)
|
|
61
|
+
- [Overview](#overview)
|
|
62
|
+
- [Installation](#installation)
|
|
63
|
+
- [How to use](#how-to-use)
|
|
64
|
+
- [Starting a Hilda interactive shell](#starting-a-hilda-interactive-shell)
|
|
65
|
+
- [Inside a Hilda shell](#inside-a-hilda-shell)
|
|
66
|
+
- [Magic functions](#magic-functions)
|
|
67
|
+
- [Key-bindings](#key-bindings)
|
|
68
|
+
- [Configurables](#configurables)
|
|
69
|
+
- [UI Configuration](#ui-configuration)
|
|
70
|
+
- [Python API](#python-api)
|
|
71
|
+
- [Symbol objects](#symbol-objects)
|
|
72
|
+
- [Globalized symbols](#globalized-symbols)
|
|
73
|
+
- [Searching for the right symbol](#searching-for-the-right-symbol)
|
|
74
|
+
- [Objective-C Classes](#objective-c-classes)
|
|
75
|
+
- [Objective-C Objects](#objective-c-objects)
|
|
76
|
+
- [Using snippets](#using-snippets)
|
|
77
|
+
- [Contributing](#contributing)
|
|
78
|
+
|
|
79
|
+
## Overview
|
|
85
80
|
|
|
86
81
|
Hilda is a debugger which combines both the power of LLDB and iPython for easier debugging.
|
|
87
82
|
|
|
@@ -93,8 +88,8 @@ debugger-y" approach (based on LLDB).
|
|
|
93
88
|
Currently, the project is intended for iOS/OSX debugging, but in the future we will possibly add support for the
|
|
94
89
|
following platforms as well:
|
|
95
90
|
|
|
96
|
-
|
|
97
|
-
|
|
91
|
+
- Linux
|
|
92
|
+
- Android
|
|
98
93
|
|
|
99
94
|
Since LLDB allows abstraction for both platform and architecture, it should be possible to make the necessary changes
|
|
100
95
|
without too many modifications.
|
|
@@ -104,14 +99,14 @@ Pull requests are more than welcome 😊.
|
|
|
104
99
|
If you need help or have an amazing idea you would like to suggest, feel free
|
|
105
100
|
to [start a discussion 💬](https://github.com/doronz88/hilda/discussions).
|
|
106
101
|
|
|
107
|
-
|
|
102
|
+
## Installation
|
|
108
103
|
|
|
109
104
|
Requirements for remote iOS device (not required for debugging a local OSX process):
|
|
110
105
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
106
|
+
- Jailbroken iOS device
|
|
107
|
+
- `debugserver` in device's PATH
|
|
108
|
+
- [You can use this tool in order to obtain the binary](https://github.com/doronz88/debugserver-deploy)
|
|
109
|
+
- After re-signing with new entitlements, you can put the binary in the following path: `/usr/bin/debugserver`
|
|
115
110
|
|
|
116
111
|
In order to install please run:
|
|
117
112
|
|
|
@@ -121,104 +116,40 @@ xcrun python3 -m pip install --user -U hilda
|
|
|
121
116
|
|
|
122
117
|
*⚠️ Please note that Hilda is installed on top of XCode's python so LLDB will be able to use its features.*
|
|
123
118
|
|
|
124
|
-
|
|
119
|
+
## How to use
|
|
125
120
|
|
|
126
|
-
|
|
121
|
+
### Starting a Hilda interactive shell
|
|
127
122
|
|
|
128
|
-
|
|
123
|
+
You can may start a Hilda interactive shell by invoking any of the subcommand:
|
|
129
124
|
|
|
130
|
-
|
|
125
|
+
- `hilda launch /path/to/executable`
|
|
126
|
+
- Launch given executable on current host
|
|
127
|
+
- `hilda attach [-p pid] [-n process-name]`
|
|
128
|
+
- Attach to an already running process on current host (specified by either `pid` or `process-name`)
|
|
129
|
+
- `hilda remote HOSTNAME PORT`
|
|
130
|
+
- Attach to an already running process on a target host (sepcified by `HOSTNAME PORT`)
|
|
131
|
+
- `hilda bare`
|
|
132
|
+
- Only start an LLDB shell and load Hilda as a plugin.
|
|
133
|
+
- Please refer to the following help page if you require help on the command available to you within the lldb shell:
|
|
131
134
|
|
|
132
|
-
|
|
133
|
-
hilda attach [-p pid] [-n process-name]
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### Launch mode
|
|
137
|
-
|
|
138
|
-
Use the attach sub-command in order to launch given process.
|
|
139
|
-
|
|
140
|
-
```shell
|
|
141
|
-
hilda launch /path/to/executable \
|
|
142
|
-
--argv arg1 --argv arg2 \
|
|
143
|
-
--envp NAME=Alice --envp AGE=30 \
|
|
144
|
-
--stdin /path/to/input.txt \
|
|
145
|
-
--stdout /path/to/output.txt \
|
|
146
|
-
--stderr /path/to/error.txt \
|
|
147
|
-
--wd /path/to/working/directory \
|
|
148
|
-
--flags 0x01 \
|
|
149
|
-
--stop-at-entry
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### Bare mode
|
|
153
|
-
|
|
154
|
-
Use "Bare mode" to get a "bare-bones" lldb shell, whereas hilda plugin is already loaded and ready to start. This mode
|
|
155
|
-
is useful when you need to have custom commands for attaching to the target process (for example when debugging OSX
|
|
156
|
-
processes).
|
|
157
|
-
|
|
158
|
-
To start this mode simply use:
|
|
159
|
-
|
|
160
|
-
```shell
|
|
161
|
-
hilda bare
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
Please refer to the following help page if you require help on the command available to you within the lldb shell:
|
|
165
|
-
|
|
166
|
-
[lldb command map](https://lldb.llvm.org/use/map.html).
|
|
167
|
-
|
|
168
|
-
As a cheatsheet, connecting to a remote platform like so:
|
|
169
|
-
|
|
170
|
-
```shell
|
|
171
|
-
platform connect connect://ip:port
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
... and attaching to a local process:
|
|
175
|
-
|
|
176
|
-
```shell
|
|
177
|
-
process attach -n proccess_name
|
|
178
|
-
process attach -p proccess_pid
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
When you are ready, just execute `hilda` to move to Hilda's iPython shell.
|
|
182
|
-
|
|
183
|
-
### Remote mode
|
|
184
|
-
|
|
185
|
-
This mode will auto-connect to the remote device and attach to your target process assuming you are trying to debug a
|
|
186
|
-
remote jailbroken iOS device.
|
|
187
|
-
|
|
188
|
-
Please note the following:
|
|
189
|
-
|
|
190
|
-
* script assumes the connected device already **has a running ssh server**, which doesn't require a password (you can
|
|
191
|
-
use
|
|
192
|
-
`ssh-copy-id` to achieve this).
|
|
135
|
+
[lldb command map](https://lldb.llvm.org/use/map.html).
|
|
193
136
|
|
|
194
|
-
|
|
137
|
+
As a cheatsheet, connecting to a remote platform like so:
|
|
195
138
|
|
|
196
|
-
|
|
139
|
+
```shell
|
|
140
|
+
platform connect connect://ip:port
|
|
141
|
+
```
|
|
197
142
|
|
|
198
|
-
|
|
143
|
+
... and attaching to a local process:
|
|
199
144
|
|
|
200
|
-
```shell
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
## Startup Files
|
|
205
|
-
|
|
206
|
-
Each command can accept startup files to execute on start. As opposed to snippets, the startup files can accept Hilda
|
|
207
|
-
syntax.
|
|
208
|
-
|
|
209
|
-
#### Startup File Example
|
|
210
|
-
|
|
211
|
-
```python
|
|
212
|
-
cfg.objc_verbose_monitor = True
|
|
213
|
-
p.bp(ADDRESS)
|
|
214
|
-
p.cont()
|
|
215
|
-
```
|
|
145
|
+
```shell
|
|
146
|
+
process attach -n proccess_name
|
|
147
|
+
process attach -p proccess_pid
|
|
148
|
+
```
|
|
216
149
|
|
|
217
|
-
|
|
218
|
-
hilda remote HOSTNAME PORT -f startupfile1 -f startupfile2
|
|
219
|
-
```
|
|
150
|
+
When you are ready, just execute `hilda` to move to Hilda's iPython shell.
|
|
220
151
|
|
|
221
|
-
|
|
152
|
+
### Inside a Hilda shell
|
|
222
153
|
|
|
223
154
|
Upon starting Hilda shell, you are greeted with:
|
|
224
155
|
|
|
@@ -231,52 +162,53 @@ Have a nice flight ✈️! Starting an IPython shell...
|
|
|
231
162
|
Here is a gist of methods you can access from `p`:
|
|
232
163
|
|
|
233
164
|
- `hd`
|
|
234
|
-
|
|
165
|
+
- Print an hexdump of given buffer
|
|
235
166
|
- `lsof`
|
|
236
|
-
|
|
167
|
+
- Get dictionary of all open FDs
|
|
237
168
|
- `bt`
|
|
238
|
-
|
|
169
|
+
- Print an improved backtrace.
|
|
239
170
|
- `disable_jetsam_memory_checks`
|
|
240
|
-
|
|
171
|
+
- Disable jetsam memory checks, prevent raising:
|
|
241
172
|
`error: Execution was interrupted, reason: EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=15 MB, unused=0x0).`
|
|
242
173
|
when evaluating expression.
|
|
243
174
|
- `symbol`
|
|
244
|
-
|
|
175
|
+
- Get symbol object for a given address
|
|
245
176
|
- `objc_symbol`
|
|
246
|
-
|
|
177
|
+
- Get objc symbol wrapper for given address
|
|
247
178
|
- `inject`
|
|
248
|
-
|
|
179
|
+
- Inject a single library into currently running process
|
|
249
180
|
- `rebind_symbols`
|
|
250
|
-
|
|
181
|
+
- Reparse all loaded images symbols
|
|
251
182
|
- `poke`
|
|
252
|
-
|
|
183
|
+
- Write data at given address
|
|
253
184
|
- `peek`
|
|
254
|
-
|
|
185
|
+
- Read data at given address
|
|
255
186
|
- `peek_str`
|
|
256
|
-
|
|
187
|
+
- Peek a buffer till null termination
|
|
257
188
|
- `stop`
|
|
258
|
-
|
|
189
|
+
- Stop process.
|
|
259
190
|
- `cont`
|
|
260
|
-
|
|
191
|
+
- Continue process.
|
|
261
192
|
- `detach`
|
|
262
|
-
|
|
193
|
+
- Detach from process.
|
|
263
194
|
Useful in order to exit gracefully so process doesn't get killed
|
|
264
195
|
while you exit
|
|
265
196
|
- `disass`
|
|
266
|
-
|
|
197
|
+
- Print disassembly from a given address
|
|
267
198
|
- `file_symbol`
|
|
268
|
-
|
|
199
|
+
- Calculate symbol address without ASLR
|
|
269
200
|
- `get_register`
|
|
270
|
-
|
|
201
|
+
- Get value for register by its name
|
|
271
202
|
- `set_register`
|
|
272
|
-
|
|
203
|
+
- Set value for register by its name
|
|
273
204
|
- `objc_call`
|
|
274
|
-
|
|
205
|
+
- Simulate a call to an objc selector
|
|
275
206
|
- `call`
|
|
276
|
-
|
|
207
|
+
- Call function at given address with given parameters
|
|
277
208
|
- `monitor`
|
|
278
|
-
|
|
209
|
+
- Monitor every time a given address is called
|
|
279
210
|
The following options are available:
|
|
211
|
+
|
|
280
212
|
```
|
|
281
213
|
regs={reg1: format}
|
|
282
214
|
will print register values
|
|
@@ -308,58 +240,57 @@ Here is a gist of methods you can access from `p`:
|
|
|
308
240
|
override=True
|
|
309
241
|
override previous break point at same location
|
|
310
242
|
```
|
|
243
|
+
|
|
311
244
|
- `show_current_source`
|
|
312
|
-
|
|
245
|
+
- print current source code if possible
|
|
313
246
|
- `finish`
|
|
314
|
-
|
|
247
|
+
- Run current frame till its end.
|
|
315
248
|
- `step_into`
|
|
316
|
-
|
|
249
|
+
- Step into current instruction.
|
|
317
250
|
- `step_over`
|
|
318
|
-
|
|
251
|
+
- Step over current instruction.
|
|
319
252
|
- `remove_all_hilda_breakpoints`
|
|
320
|
-
|
|
253
|
+
- Remove all breakpoints created by Hilda
|
|
321
254
|
- `remove_hilda_breakpoint`
|
|
322
|
-
|
|
255
|
+
- Remove a single breakpoint placed by Hilda
|
|
323
256
|
- `force_return`
|
|
324
|
-
|
|
257
|
+
- Prematurely return from a stack frame, short-circuiting exection of newer frames and optionally
|
|
325
258
|
yielding a specified value.
|
|
326
259
|
- `proc_info`
|
|
327
|
-
|
|
260
|
+
- Print information about currently running mapped process.
|
|
328
261
|
- `print_proc_entitlements`
|
|
329
|
-
|
|
262
|
+
- Get the plist embedded inside the process' __LINKEDIT section.
|
|
330
263
|
- `bp`
|
|
331
|
-
|
|
264
|
+
- Add a breakpoint
|
|
332
265
|
- `show_hilda_breakpoints`
|
|
333
|
-
|
|
334
|
-
- `show_commands`
|
|
335
|
-
- Show available commands.
|
|
266
|
+
- Show existing breakpoints created by Hilda.
|
|
336
267
|
- `save`
|
|
337
|
-
|
|
268
|
+
- Save loaded symbols map (for loading later using the load() command)
|
|
338
269
|
- `load`
|
|
339
|
-
|
|
270
|
+
- Load an existing symbols map (previously saved by the save() command)
|
|
340
271
|
- `po`
|
|
341
|
-
|
|
272
|
+
- Print given object using LLDB's po command
|
|
342
273
|
Can also run big chunks of native code:
|
|
343
274
|
|
|
344
275
|
po('NSMutableString *s = [NSMutableString string]; [s appendString:@"abc"]; [s description]')
|
|
345
276
|
- `globalize_symbols`
|
|
346
|
-
|
|
277
|
+
- Make all symbols in python's global scope
|
|
347
278
|
- `jump`
|
|
348
|
-
|
|
279
|
+
- jump to given symbol
|
|
349
280
|
- `lldb_handle_command`
|
|
350
|
-
|
|
281
|
+
- Execute an LLDB command
|
|
351
282
|
For example:
|
|
352
283
|
lldb_handle_command('register read')
|
|
353
284
|
- `objc_get_class`
|
|
354
|
-
|
|
285
|
+
- Get ObjC class object
|
|
355
286
|
- `CFSTR`
|
|
356
|
-
|
|
287
|
+
- Create CFStringRef object from given string
|
|
357
288
|
- `ns`
|
|
358
|
-
|
|
289
|
+
- Create NSObject from given data
|
|
359
290
|
- `from_ns`
|
|
360
|
-
|
|
291
|
+
- Create python object from NS object.
|
|
361
292
|
- `evaluate_expression`
|
|
362
|
-
|
|
293
|
+
- Wrapper for LLDB's EvaluateExpression.
|
|
363
294
|
Used for quick code snippets.
|
|
364
295
|
|
|
365
296
|
Feel free to use local variables inside the expression using format string.
|
|
@@ -367,31 +298,37 @@ Here is a gist of methods you can access from `p`:
|
|
|
367
298
|
currentDevice = objc_get_class('UIDevice').currentDevice
|
|
368
299
|
evaluate_expression(f'[[{currentDevice} systemName] hasPrefix:@"2"]')
|
|
369
300
|
- `import_module`
|
|
370
|
-
|
|
301
|
+
- Import & reload given python module (intended mainly for external snippets)
|
|
371
302
|
- `unwind`
|
|
372
|
-
|
|
303
|
+
- Unwind the stack (useful when get_evaluation_unwind() == False)
|
|
304
|
+
- `set_selected_thread`
|
|
305
|
+
- sets the currently selected thread, which is used in other parts of the program, such as displaying disassembly or
|
|
306
|
+
checking registers.
|
|
307
|
+
This ensures the application focuses on the specified thread for these operations.
|
|
373
308
|
|
|
374
|
-
|
|
309
|
+
All these methods are available from the global `p` within the newly created IPython shell. In addition, you may invoke any of the exported APIs described in the [Python API](#python-api)
|
|
375
310
|
|
|
376
|
-
|
|
311
|
+
#### Magic functions
|
|
312
|
+
|
|
313
|
+
Sometimes accessing the [Python API](#python-api) can be tiring, so we added some magic functions to help you out!
|
|
377
314
|
|
|
378
315
|
- `%objc <className>`
|
|
379
|
-
|
|
316
|
+
- Equivalent to: `className = p.objc_get_class(className)`
|
|
380
317
|
- `%fbp <filename> <addressInHex>`
|
|
381
|
-
|
|
318
|
+
- Equivalent to: `p.file_symbol(addressInHex, filename).bp()`
|
|
382
319
|
|
|
383
|
-
|
|
320
|
+
#### Key-bindings
|
|
384
321
|
|
|
385
322
|
- **F7**: Step Into
|
|
386
323
|
- **F8**: Step Over
|
|
387
324
|
- **F9**: Continue
|
|
388
325
|
- **F10**: Stop
|
|
389
326
|
|
|
390
|
-
|
|
327
|
+
#### Configurables
|
|
391
328
|
|
|
392
329
|
The global `cfg` used to configure various settings for evaluation and monitoring.
|
|
393
330
|
|
|
394
|
-
|
|
331
|
+
These settings include:
|
|
395
332
|
|
|
396
333
|
- `evaluation_unwind_on_error`: Whether to unwind on error during evaluation. (Default: `False`)
|
|
397
334
|
- `evaluation_ignore_breakpoints`: Whether to ignore breakpoints during evaluation. (Default: `False`)
|
|
@@ -400,15 +337,9 @@ The global `cfg` used to configure various settings for evaluation and monitorin
|
|
|
400
337
|
- `objc_verbose_monitor`: When set to `True`, using `monitor()` will automatically print Objective-C method arguments. (
|
|
401
338
|
Default: `False`)
|
|
402
339
|
|
|
403
|
-
|
|
340
|
+
#### UI Configuration
|
|
404
341
|
|
|
405
|
-
|
|
406
|
-
cfg.objc_verbose_monitor = True
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
## UI Configuration
|
|
410
|
-
|
|
411
|
-
Hilda contains minimal UI for examining the target state.
|
|
342
|
+
Hilda contains a minimal UI for examining the target state.
|
|
412
343
|
The UI is divided into views:
|
|
413
344
|
|
|
414
345
|
- Registers
|
|
@@ -465,7 +396,31 @@ ui.colors.address = 'red'
|
|
|
465
396
|
ui.color.title = 'green'
|
|
466
397
|
```
|
|
467
398
|
|
|
468
|
-
|
|
399
|
+
### Python API
|
|
400
|
+
|
|
401
|
+
Hilda provides a comprehensive API wrappers to access LLDB capabilities.
|
|
402
|
+
This API may be used to access process memory, trigger functions, place breakpoints and much more!
|
|
403
|
+
|
|
404
|
+
Also, in addition to access this API using the [Hilda shell](#inside-a-hilda-shell), you may also use pure-python script using any of the `create_hilda_client_using_*` APIs.
|
|
405
|
+
|
|
406
|
+
Consider the following snippet as an example of such usage:
|
|
407
|
+
|
|
408
|
+
```python
|
|
409
|
+
from hilda.launch_lldb import create_hilda_client_using_attach_by_name
|
|
410
|
+
|
|
411
|
+
# attach to `sysmond`
|
|
412
|
+
p = create_hilda_client_using_attach_by_name('sysmond')
|
|
413
|
+
|
|
414
|
+
# allocate 10 bytes and print their address
|
|
415
|
+
print(p.symbols.malloc(10))
|
|
416
|
+
|
|
417
|
+
# detach
|
|
418
|
+
p.detach()
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
Please note this script must be executed using `xcrun python3` in order for it to be able to access LLDB API.
|
|
422
|
+
|
|
423
|
+
#### Symbol objects
|
|
469
424
|
|
|
470
425
|
In Hilda, almost everything is wrapped using the `Symbol` Object. Symbol is just a nicer way for referring to addresses
|
|
471
426
|
encapsulated with an object allowing to deref the memory inside, or use these addresses as functions.
|
|
@@ -572,7 +527,7 @@ p.bp('symbol_name')
|
|
|
572
527
|
p.bp('symbol_name', module_name='ModuleName')
|
|
573
528
|
```
|
|
574
529
|
|
|
575
|
-
|
|
530
|
+
#### Globalized symbols
|
|
576
531
|
|
|
577
532
|
Usually you would want/need to use the symbols already mapped into the currently running process. To do so, you can
|
|
578
533
|
access them using `symbols.<symbol-name>`. The `symbols` global object is of type `SymbolsJar`, which is a wrapper
|
|
@@ -617,7 +572,7 @@ jar = jar.code()
|
|
|
617
572
|
jar.monitor(regs={'x0': 'x'}, bt=True)
|
|
618
573
|
```
|
|
619
574
|
|
|
620
|
-
|
|
575
|
+
#### Objective-C Classes
|
|
621
576
|
|
|
622
577
|
The same as symbols applies to Objective-C classes name resolution. You can either:
|
|
623
578
|
|
|
@@ -670,7 +625,7 @@ dictionary = NSDictionary.capture_self(True)
|
|
|
670
625
|
dictionary.show()
|
|
671
626
|
```
|
|
672
627
|
|
|
673
|
-
|
|
628
|
+
#### Objective-C Objects
|
|
674
629
|
|
|
675
630
|
In order to work with ObjC objects, each symbol contains a property called
|
|
676
631
|
`objc_symbol`. After calling, you can work better with each object:
|
|
@@ -736,7 +691,7 @@ abc_string = p.evaluate_expression('[NSString stringWithFormat:@"abc"]')
|
|
|
736
691
|
print(abc_string.po())
|
|
737
692
|
```
|
|
738
693
|
|
|
739
|
-
|
|
694
|
+
#### Using snippets
|
|
740
695
|
|
|
741
696
|
Snippets are extensions for normal functionality used as quick cookbooks for day-to-day tasks of a debugger.
|
|
742
697
|
|
|
@@ -3,13 +3,13 @@ gifs/ui.png,sha256=iaRwNZ9qVWUkUe2TJb_6VPsTu--7HrElA2duWiyZ-Oc,131
|
|
|
3
3
|
gifs/xpc_print_message.gif,sha256=i5S8Y9bJm9n-NtOipFTAC8_jUR4uZCM4sOap_ccJX0k,939935
|
|
4
4
|
hilda/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
hilda/__main__.py,sha256=KWRqvukK4wraxCMtvH5nO25mFXLO5aWXa7z_VfAtbO8,90
|
|
6
|
-
hilda/_version.py,sha256=
|
|
7
|
-
hilda/cli.py,sha256=
|
|
8
|
-
hilda/common.py,sha256=
|
|
9
|
-
hilda/exceptions.py,sha256=
|
|
6
|
+
hilda/_version.py,sha256=nHByjT85Mzor0VestVUVI70fkRS763utSt6toCJI-NY,411
|
|
7
|
+
hilda/cli.py,sha256=jAM31J2aaXAyiEmMFRiRP3FjvQylt390SsFQJGhqfDo,3760
|
|
8
|
+
hilda/common.py,sha256=VFtpOH9IRqbu6lONM6Hz-PxcdJfktgS3akJv5UCHO0s,523
|
|
9
|
+
hilda/exceptions.py,sha256=8L1OvOqns4O4ieiH4YlrMbZkk_PvuyCq4UyqFAodkF8,2042
|
|
10
10
|
hilda/hilda_ascii_art.html,sha256=-9YCjAKdGbjtdd6uoKrxkkcJq7j16r4dGka2bZ27b4o,120119
|
|
11
|
-
hilda/hilda_client.py,sha256=
|
|
12
|
-
hilda/launch_lldb.py,sha256=
|
|
11
|
+
hilda/hilda_client.py,sha256=4wY1syGiVV_LGyXRNch2QZi7Zi1VEi6OrMjbLHzZ3eQ,45274
|
|
12
|
+
hilda/launch_lldb.py,sha256=tFSJK9dry1Z12lE5OSbDX53aJ23TO1XFElHo85qxeVo,7336
|
|
13
13
|
hilda/lldb_entrypoint.py,sha256=vTiClzfiTtjorlxEfIsI-W657KEGobx74qDhaZ8nPhM,1007
|
|
14
14
|
hilda/lldb_importer.py,sha256=LrIQnigDG3wgmIPXya67oBlWubkDgV-rEpzfWNEYCoc,553
|
|
15
15
|
hilda/objective_c_class.py,sha256=Xyw7FucNVcu8NqhB0Gy-OjaN_2DmCywn6pjUEwyd1Hk,11760
|
|
@@ -46,9 +46,9 @@ hilda/snippets/macho/macho_load_commands.py,sha256=OsajG2xWuRwhYhj9f-lnUiNe43Yum
|
|
|
46
46
|
hilda/ui/colors.json,sha256=f-ITquY3IInQreviTy23JfmxfJrGM1_MivACf1GKGqM,262
|
|
47
47
|
hilda/ui/ui_manager.py,sha256=BmzI1sBx0PYCQDlB9Al7wsTEAMJxaJ7NW0DS4C7g5-0,2265
|
|
48
48
|
hilda/ui/views.py,sha256=1u_eZzw7wAEP1bm_-9nkqgGhMSX__woiUuoBgnsAKVM,7843
|
|
49
|
-
hilda-2.0.
|
|
50
|
-
hilda-2.0.
|
|
51
|
-
hilda-2.0.
|
|
52
|
-
hilda-2.0.
|
|
53
|
-
hilda-2.0.
|
|
54
|
-
hilda-2.0.
|
|
49
|
+
hilda-2.0.3.dist-info/LICENSE,sha256=M-LVJ0AFAYB82eueyl8brh-QLPe-iLNVgbCi79-3TDo,1078
|
|
50
|
+
hilda-2.0.3.dist-info/METADATA,sha256=7eMn09PoasA7uQIPpQWsAbJtNUzuJqanUYlioQvWdgs,23306
|
|
51
|
+
hilda-2.0.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
52
|
+
hilda-2.0.3.dist-info/entry_points.txt,sha256=9n3O3j6V3XnVR_GcFqCWNgRAbalfukTSW2WvghsLVmA,46
|
|
53
|
+
hilda-2.0.3.dist-info/top_level.txt,sha256=TVD7l1WkE1noT866YqPFhiQnjYCYZM5Xz54v_3EYpnI,11
|
|
54
|
+
hilda-2.0.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|