hilda 2.0.2__py3-none-any.whl → 2.0.4__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 CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '2.0.2'
16
- __version_tuple__ = version_tuple = (2, 0, 2)
15
+ __version__ = version = '2.0.4'
16
+ __version_tuple__ = version_tuple = (2, 0, 4)
hilda/cli.py CHANGED
@@ -1,12 +1,14 @@
1
1
  import logging
2
2
  from pathlib import Path
3
- from typing import List, Mapping, Optional
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, default=None, help='Files to run on start')
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,58 @@ 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: Optional[List[str]] = None) -> None:
41
+ def remote(hostname: str, port: int, startup_files: List[str]) -> None:
40
42
  """ Connect to remote debugserver at given address """
41
- launch_lldb.remote(hostname, port, startup_files)
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: Optional[List[str]] = None) -> None:
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
- launch_lldb.attach(name=name, pid=pid, startup_files=startup_files)
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', type=str, help='Redirect stdin from this file path')
58
- @click.option('--stdout', type=str, help='Redirect stdout to this file path')
59
- @click.option('--stderr', type=str, help='Redirect stderr to this file path')
60
- @click.option('--cwd', type=str, help='Set the working directory for the process')
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
- @click.option('--stop-at-entry', is_flag=True, help='Stop the process at the entry point')
63
80
  @startup_files_option
64
- def launch(exec_path: str, argv: Optional[List] = None, envp: Optional[Mapping] = None,
65
- stdin: Optional[Path] = None,
66
- stdout: Optional[Path] = None, stderr: Optional[Path] = None, cwd: Optional[Path] = None,
67
- flags: Optional[int] = 0, stop_at_entry: Optional[bool] = False,
68
- startup_files: Optional[List[str]] = None) -> None:
69
- """ Attach to given process and start a lldb shell """
70
- if not argv:
71
- argv = None
72
- if not envp:
73
- envp = None
74
- launch_lldb.launch(exec_path, argv, envp, stdin, stdout, stderr, cwd, flags, stop_at_entry,
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}"')
81
+ def launch(exec_path: str, argv: List[str], envp: List[str], stdin: Optional[Path],
82
+ stdout: Optional[Path], stderr: Optional[Path], cwd: Optional[Path], flags: Optional[int],
83
+ startup_files: List[str]) -> None:
84
+ """ Attach to a given process and start a lldb shell """
85
+ argv = list(argv)
86
+ envp = list(envp)
87
+ with create_hilda_client_using_launch(
88
+ exec_path, argv, envp, stdin, stdout, stderr, cwd, flags) as hilda_client:
89
+ hilda_client.interact(startup_files=startup_files)
84
90
 
85
91
 
86
92
  @cli.command('version')
87
- def cli_version():
93
+ def cli_version() -> None:
88
94
  """Show the version information."""
89
95
  click.echo(version)
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', 'LLDBException', 'InvalidThreadIndexError']
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 LLDBException(Exception):
13
- """ A domain exception for lldb errors. """
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):
hilda/hilda_client.py CHANGED
@@ -58,6 +58,7 @@ GREETING = f"""
58
58
  <b>Hilda has been successfully loaded! 😎
59
59
  Usage:
60
60
  <span style="color: magenta">p</span> Global to access all features.
61
+ <span style="color: magenta">F1</span> UI Show.
61
62
  <span style="color: magenta">F7</span> Step Into.
62
63
  <span style="color: magenta">F8</span> Step Over.
63
64
  <span style="color: magenta">F9</span> Continue.
@@ -74,7 +75,7 @@ def disable_logs() -> None:
74
75
  logging.getLogger('parso.python.diff').disabled = True
75
76
  logging.getLogger('humanfriendly.prompts').disabled = True
76
77
  logging.getLogger('blib2to3.pgen2.driver').disabled = True
77
- logging.getLogger('hilda.launch_lldb').disabled = True
78
+ logging.getLogger('hilda.launch_lldb').setLevel(logging.INFO)
78
79
 
79
80
 
80
81
  SerializableSymbol = namedtuple('SerializableSymbol', 'address type_ filename')
@@ -357,17 +358,19 @@ class HildaClient:
357
358
  if not self.process.Continue().Success():
358
359
  self.log_critical('failed to continue process')
359
360
 
360
- def detach(self):
361
+ def detach(self) -> None:
361
362
  """
362
363
  Detach from process.
363
364
 
364
365
  Useful in order to exit gracefully so process doesn't get killed
365
366
  while you exit
366
367
  """
368
+ if not self.process.is_alive:
369
+ return
367
370
  if not self.process.Detach().Success():
368
371
  self.log_critical('failed to detach')
369
- else:
370
- self.log_info('Process Detached')
372
+ return
373
+ self.log_info('Process Detached')
371
374
 
372
375
  @stop_is_needed
373
376
  def disass(self, address: int, buf: bytes, flavor: str = 'intel',
@@ -398,30 +401,33 @@ class HildaClient:
398
401
 
399
402
  return self.symbol(module.ResolveFileAddress(address).GetLoadAddress(self.target))
400
403
 
401
- def get_register(self, name) -> Symbol:
404
+ def get_register(self, name: str) -> Union[float, Symbol]:
402
405
  """
403
- Get value for register by its name
404
- :param name:
405
- :return:
406
+ Get value for register by its name. Value can either be an Symbol (int) or a float.
407
+
408
+ :param name: Register name
409
+ :return: Register value
406
410
  """
407
- register = self.frame.register[name.lower()]
408
- if register is None:
411
+ register_value = self.frame.register[name.lower()]
412
+ if register_value is None:
409
413
  raise AccessingRegisterError()
410
- return self.symbol(register.unsigned)
414
+ return self._get_symbol_or_float_from_sbvalue(register_value)
411
415
 
412
- def set_register(self, name, value):
416
+ def set_register(self, name: str, value: Union[float, int]) -> None:
413
417
  """
414
418
  Set value for register by its name
415
- :param name:
416
- :param value:
417
- :return:
419
+ :param name: Register name
420
+ :param value: Register value
418
421
  """
419
422
  register = self.frame.register[name.lower()]
420
423
  if register is None:
421
424
  raise AccessingRegisterError()
422
- register.value = hex(value)
425
+ if isinstance(value, int):
426
+ register.value = hex(value)
427
+ else:
428
+ register.value = str(value)
423
429
 
424
- def objc_call(self, obj, selector, *params):
430
+ def objc_call(self, obj: int, selector: str, *params):
425
431
  """
426
432
  Simulate a call to an objc selector
427
433
  :param obj: obj to pass into `objc_msgSend`
@@ -525,27 +531,32 @@ class HildaClient:
525
531
  value = hilda.symbol(hilda.evaluate_expression(name))
526
532
  log_message += f'\n\t{name} = {hilda._monitor_format_value(fmt, value)}'
527
533
 
528
- if options.get('force_return', None) is not None:
529
- hilda.force_return(options['force_return'])
530
- log_message += f'\nforced return: {options["force_return"]}'
534
+ force_return = options.get('force_return')
535
+ if force_return is not None:
536
+ hilda.force_return(force_return)
537
+ log_message += f'\nforced return: {force_return}'
531
538
 
532
- if options.get('bt', False):
539
+ if options.get('bt'):
533
540
  # bugfix: for callstacks from xpc events
534
541
  hilda.finish()
535
- hilda.bt()
542
+ for frame in hilda.bt():
543
+ log_message += f'\n\t{frame[0]} {frame[1]}'
536
544
 
537
- if options.get('retval', None) is not None:
545
+ retval = options.get('retval')
546
+ if retval is not None:
538
547
  # return from function
539
548
  hilda.finish()
540
549
  value = hilda.evaluate_expression('$arg1')
541
- log_message += f'\nreturned: {hilda._monitor_format_value(options["retval"], value)}'
550
+ log_message += f'\nreturned: {hilda._monitor_format_value(retval, value)}'
542
551
 
543
552
  hilda.log_info(log_message)
544
553
 
545
554
  for cmd in options.get('cmd', []):
546
555
  hilda.lldb_handle_command(cmd)
547
556
 
548
- if not options.get('stop', False):
557
+ if options.get('stop', False):
558
+ hilda.log_info('Process remains stopped and focused on current thread')
559
+ else:
549
560
  hilda.cont()
550
561
 
551
562
  return self.bp(address, callback, condition=condition, **options)
@@ -835,7 +846,7 @@ class HildaClient:
835
846
  raise ConvertingFromNSObjectError from e
836
847
  return json.loads(json_dump, object_hook=self._from_ns_json_object_hook)['root']
837
848
 
838
- def evaluate_expression(self, expression) -> Symbol:
849
+ def evaluate_expression(self, expression: str) -> Union[float, Symbol]:
839
850
  """
840
851
  Wrapper for LLDB's EvaluateExpression.
841
852
  Used for quick code snippets.
@@ -845,8 +856,8 @@ class HildaClient:
845
856
  currentDevice = objc_get_class('UIDevice').currentDevice
846
857
  evaluate_expression(f'[[{currentDevice} systemName] hasPrefix:@"2"]')
847
858
 
848
- :param expression:
849
- :return: returned symbol
859
+ :param expression: Expression to evaluate
860
+ :return: Returned value (either float or a Symbol)
850
861
  """
851
862
  # prepending a prefix so LLDB knows to return an int type
852
863
  if isinstance(expression, int):
@@ -859,12 +870,12 @@ class HildaClient:
859
870
  options.SetTryAllThreads(True)
860
871
  options.SetUnwindOnError(self.configs.evaluation_unwind_on_error)
861
872
 
862
- e = self.frame.EvaluateExpression(formatted_expression, options)
873
+ sbvalue = self.frame.EvaluateExpression(formatted_expression, options)
863
874
 
864
- if not e.error.Success():
865
- raise EvaluatingExpressionError(str(e.error))
875
+ if not sbvalue.error.Success():
876
+ raise EvaluatingExpressionError(str(sbvalue.error))
866
877
 
867
- return self.symbol(e.unsigned)
878
+ return self._get_symbol_or_float_from_sbvalue(sbvalue)
868
879
 
869
880
  def import_module(self, filename: str, name: Optional[str] = None) -> Any:
870
881
  """
@@ -1044,7 +1055,7 @@ class HildaClient:
1044
1055
  ipython_config.InteractiveShellApp.extensions = ['hilda.ipython_extensions.magics',
1045
1056
  'hilda.ipython_extensions.events',
1046
1057
  'hilda.ipython_extensions.keybindings']
1047
- ipython_config.InteractiveShellApp.exec_lines = ["disable_logs()"]
1058
+ ipython_config.InteractiveShellApp.exec_lines = ['disable_logs()']
1048
1059
  if startup_files is not None:
1049
1060
  ipython_config.InteractiveShellApp.exec_files = startup_files
1050
1061
  self.log_debug(f'Startup files - {startup_files}')
@@ -1052,10 +1063,17 @@ class HildaClient:
1052
1063
  namespace = globals()
1053
1064
  namespace.update(locals())
1054
1065
  namespace['p'] = self
1066
+ namespace['ui'] = self.ui_manager
1067
+ namespace['cfg'] = self.configs
1055
1068
  if additional_namespace is not None:
1056
1069
  namespace.update(additional_namespace)
1057
1070
  sys.argv = ['a']
1058
1071
  IPython.start_ipython(config=ipython_config, user_ns=namespace)
1072
+
1073
+ def __enter__(self) -> 'HildaClient':
1074
+ return self
1075
+
1076
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
1059
1077
  self.detach()
1060
1078
 
1061
1079
  @staticmethod
@@ -1169,3 +1187,11 @@ class HildaClient:
1169
1187
  '__objc_class_list',
1170
1188
  f'{objc_classlist_addr}')
1171
1189
  return json.loads(self.po(obj_c_code))
1190
+
1191
+ def _get_symbol_or_float_from_sbvalue(self, value: lldb.SBValue) -> Union[float, Symbol]:
1192
+ # The `value` attribute of an SBValue stores a string representation of the actual value
1193
+ # in a python-compatible format, so we can eval it to get the native python value
1194
+ value = eval(value.value)
1195
+ if isinstance(value, float):
1196
+ return value
1197
+ return self.symbol(value)
@@ -6,7 +6,8 @@ from prompt_toolkit.keys import Keys
6
6
  def load_ipython_extension(ipython):
7
7
  def register_keybindings():
8
8
  hilda = ipython.user_ns['p']
9
- keys_mapping = {Keys.F7: hilda.step_into,
9
+ keys_mapping = {Keys.F1: hilda.ui_manager.show,
10
+ Keys.F7: hilda.step_into,
10
11
  Keys.F8: hilda.step_over,
11
12
  Keys.F9: lambda _: (hilda.log_info('Sending continue'), hilda.cont()),
12
13
  Keys.F10: lambda _: (hilda.log_info('Sending stop'), hilda.stop())}
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 LLDBException
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 LLDBException(self.error.description)
47
+ raise LLDBError(self.error.description)
56
48
 
57
49
  def run(self):
58
50
  event = lldb.SBEvent()
@@ -67,7 +59,7 @@ class LLDBListenerThread(Thread, ABC):
67
59
  logger.debug('Process Detached')
68
60
  self.should_quit = True
69
61
  elif state == lldb.eStateExited:
70
- logger.debug(f'Process Exited with status {self.process.GetExitStatus()}')
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
65
  logger.debug('Process Continued')
@@ -135,13 +127,12 @@ class LLDBLaunch(LLDBListenerThread):
135
127
  def __init__(self, exec_path: str, argv: Optional[List[str]] = None, envp: Optional[List[str]] = None,
136
128
  stdin: Optional[str] = None,
137
129
  stdout: Optional[str] = None, stderr: Optional[str] = None, wd: Optional[str] = None,
138
- flags: Optional[int] = 0, stop_at_entry: Optional[bool] = False):
130
+ flags: Optional[int] = 0):
139
131
  self.exec_path = exec_path
140
132
  self.stdout = stdout
141
133
  self.stdin = stdin
142
134
  self.stderr = stderr
143
135
  self.flags = flags
144
- self.stop_at_entry = stop_at_entry
145
136
  self.argv = argv
146
137
  self.envp = envp
147
138
  self.working_directory = wd
@@ -157,46 +148,40 @@ class LLDBLaunch(LLDBListenerThread):
157
148
  logger.debug(f'Lunching process {self.exec_path}')
158
149
  return self.target.Launch(self.listener, self.argv, self.envp,
159
150
  self.stdin, self.stdout, self.stderr, self.working_directory,
160
- self.flags, self.stop_at_entry,
151
+ self.flags, True,
161
152
  self.error)
162
153
 
163
154
 
164
- def remote(hostname: str, port: int, startup_files: Optional[List[str]] = None) -> None:
165
- """ Connect to remote process """
166
- try:
167
- lldb_t = LLDBRemote(hostname, port)
168
- lldb_t.start()
169
- hilda(lldb_t.debugger, startup_files)
170
- except LLDBException as e:
171
- logger.error(e.message)
172
-
173
-
174
- def attach(name: Optional[str] = None, pid: Optional[int] = None, wait_for: bool = False,
175
- startup_files: Optional[List[str]] = None) -> None:
176
- """ Attach to given process and start a lldb shell """
177
- if (name is not None and pid is not None) or (name is None and pid is None):
178
- raise ValueError('Provide either a process name or a PID, but not both.')
179
-
180
- try:
181
- if name is not None:
182
- lldb_t = LLDBAttachName(name, wait_for)
183
- else:
184
- lldb_t = LLDBAttachPid(pid)
185
- lldb_t.start()
186
- hilda(lldb_t.debugger, startup_files)
187
- except LLDBException as e:
188
- logger.error(e.message)
189
-
190
-
191
- def launch(exec_path: str, argv: Optional[List] = None, envp: Optional[List] = None,
192
- stdin: Optional[str] = None,
193
- stdout: Optional[str] = None, stderr: Optional[str] = None, wd: Optional[str] = None,
194
- flags: Optional[int] = 0, stop_at_entry: Optional[bool] = False,
195
- startup_files: Optional[List[str]] = None) -> None:
196
- """ Launch to given process and start a lldb shell """
197
- try:
198
- lldb_t = LLDBLaunch(exec_path, argv, envp, stdin, stdout, stderr, wd, flags, stop_at_entry)
199
- lldb_t.start()
200
- hilda(lldb_t.debugger, startup_files)
201
- except LLDBException as e:
202
- logger.error(e.message)
155
+ def _get_hilda_client_from_sbdebugger(debugger: lldb.SBDebugger) -> HildaClient:
156
+ hilda_client = HildaClient(debugger)
157
+ lldb.hilda_client = hilda_client
158
+ hilda_client.init_dynamic_environment()
159
+ return hilda_client
160
+
161
+
162
+ def create_hilda_client_using_remote_attach(
163
+ hostname: str, port: int) -> HildaClient:
164
+ lldb_t = LLDBRemote(hostname, port)
165
+ lldb_t.start()
166
+ return _get_hilda_client_from_sbdebugger(lldb_t.debugger)
167
+
168
+
169
+ def create_hilda_client_using_launch(
170
+ exec_path: str, argv: Optional[List] = None, envp: Optional[List] = None, stdin: Optional[str] = None,
171
+ stdout: Optional[str] = None, stderr: Optional[str] = None, wd: Optional[str] = None,
172
+ flags: Optional[int] = 0) -> HildaClient:
173
+ lldb_t = LLDBLaunch(exec_path, argv, envp, stdin, stdout, stderr, wd, flags)
174
+ lldb_t.start()
175
+ return _get_hilda_client_from_sbdebugger(lldb_t.debugger)
176
+
177
+
178
+ def create_hilda_client_using_attach_by_pid(pid: Optional[int] = None) -> HildaClient:
179
+ lldb_t = LLDBAttachPid(pid)
180
+ lldb_t.start()
181
+ return _get_hilda_client_from_sbdebugger(lldb_t.debugger)
182
+
183
+
184
+ def create_hilda_client_using_attach_by_name(name: Optional[str] = None, wait_for: bool = False) -> HildaClient:
185
+ lldb_t = LLDBAttachName(name, wait_for)
186
+ lldb_t.start()
187
+ 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.2
3
+ Version: 2.0.4
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>
@@ -55,34 +55,28 @@ Requires-Dist: inquirer3
55
55
  Provides-Extra: test
56
56
  Requires-Dist: pytest ; extra == 'test'
57
57
 
58
- - [Description](#description)
59
- - [Installation](#installation)
60
- - [How to use](#how-to-use)
61
- - [Starting a Hilda shell](#starting-a-hilda-shell)
62
- - [Attach mode](#attach-mode)
63
- - [Launch mode](#launch-mode)
64
- - [Bare mode](#bare-mode)
65
- - [Remote mode](#remote-mode)
66
- - [The connected device is connected via network](#the-connected-device-is-connected-via-network)
67
- - [Startup Files](#startup-files)
68
- - [Usage](#usage)
69
- - [Magic functions](#magic-functions)
70
- - [Shortcuts](#shortcuts)
71
- - [Configurables](#configurables)
72
- - [Attributes](#attributes)
73
- - [Example Usage](#example-usage)
74
- - [UI Configuration](#ui-configuration)
75
- - [Symbol objects](#symbol-objects)
76
- - [Globalized symbols](#globalized-symbols)
77
- - [Searching for the right symbol](#searching-for-the-right-symbol)
78
- - [Objective-C Classes](#objective-c-classes)
79
- - [Objective-C Objects](#objective-c-objects)
80
- - [Using snippets](#using-snippets)
81
- - [Contributing](#contributing)
82
-
83
- Would you like any further adjustments?
84
-
85
- # 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
86
80
 
87
81
  Hilda is a debugger which combines both the power of LLDB and iPython for easier debugging.
88
82
 
@@ -94,8 +88,8 @@ debugger-y" approach (based on LLDB).
94
88
  Currently, the project is intended for iOS/OSX debugging, but in the future we will possibly add support for the
95
89
  following platforms as well:
96
90
 
97
- * Linux
98
- * Android
91
+ - Linux
92
+ - Android
99
93
 
100
94
  Since LLDB allows abstraction for both platform and architecture, it should be possible to make the necessary changes
101
95
  without too many modifications.
@@ -105,14 +99,14 @@ Pull requests are more than welcome 😊.
105
99
  If you need help or have an amazing idea you would like to suggest, feel free
106
100
  to [start a discussion 💬](https://github.com/doronz88/hilda/discussions).
107
101
 
108
- # Installation
102
+ ## Installation
109
103
 
110
104
  Requirements for remote iOS device (not required for debugging a local OSX process):
111
105
 
112
- * Jailbroken iOS device
113
- * `debugserver` in device's PATH
114
- * [You can use this tool in order to obtain the binary](https://github.com/doronz88/debugserver-deploy)
115
- * After re-signing with new entitlements, you can put the binary in the following path: `/usr/bin/debugserver`
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`
116
110
 
117
111
  In order to install please run:
118
112
 
@@ -122,104 +116,40 @@ xcrun python3 -m pip install --user -U hilda
122
116
 
123
117
  *⚠️ Please note that Hilda is installed on top of XCode's python so LLDB will be able to use its features.*
124
118
 
125
- # How to use
119
+ ## How to use
126
120
 
127
- ## Starting a Hilda shell
121
+ ### Starting a Hilda interactive shell
128
122
 
129
- ### Attach mode
123
+ You can may start a Hilda interactive shell by invoking any of the subcommand:
130
124
 
131
- Use the attach sub-command in order to start an LLDB shell attached to given process.
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:
132
134
 
133
- ```shell
134
- hilda attach [-p pid] [-n process-name]
135
- ```
136
-
137
- ### Launch mode
138
-
139
- Use the attach sub-command in order to launch given process.
140
-
141
- ```shell
142
- hilda launch /path/to/executable \
143
- --argv arg1 --argv arg2 \
144
- --envp NAME=Alice --envp AGE=30 \
145
- --stdin /path/to/input.txt \
146
- --stdout /path/to/output.txt \
147
- --stderr /path/to/error.txt \
148
- --wd /path/to/working/directory \
149
- --flags 0x01 \
150
- --stop-at-entry
151
- ```
152
-
153
- ### Bare mode
154
-
155
- Use "Bare mode" to get a "bare-bones" lldb shell, whereas hilda plugin is already loaded and ready to start. This mode
156
- is useful when you need to have custom commands for attaching to the target process (for example when debugging OSX
157
- processes).
158
-
159
- To start this mode simply use:
160
-
161
- ```shell
162
- hilda bare
163
- ```
164
-
165
- Please refer to the following help page if you require help on the command available to you within the lldb shell:
166
-
167
- [lldb command map](https://lldb.llvm.org/use/map.html).
168
-
169
- As a cheatsheet, connecting to a remote platform like so:
170
-
171
- ```shell
172
- platform connect connect://ip:port
173
- ```
174
-
175
- ... and attaching to a local process:
176
-
177
- ```shell
178
- process attach -n proccess_name
179
- process attach -p proccess_pid
180
- ```
181
-
182
- When you are ready, just execute `hilda` to move to Hilda's iPython shell.
183
-
184
- ### Remote mode
185
-
186
- This mode will auto-connect to the remote device and attach to your target process assuming you are trying to debug a
187
- remote jailbroken iOS device.
188
-
189
- Please note the following:
190
-
191
- * script assumes the connected device already **has a running ssh server**, which doesn't require a password (you can
192
- use
193
- `ssh-copy-id` to achieve this).
135
+ [lldb command map](https://lldb.llvm.org/use/map.html).
194
136
 
195
- From this point the flow diverges into 2 flows:
137
+ As a cheatsheet, connecting to a remote platform like so:
196
138
 
197
- ### The connected device is connected via network
139
+ ```shell
140
+ platform connect connect://ip:port
141
+ ```
198
142
 
199
- Run the following command:
143
+ ... and attaching to a local process:
200
144
 
201
- ```shell
202
- hilda remote HOSTNAME PORT
203
- ```
204
-
205
- ## Startup Files
206
-
207
- Each command can accept startup files to execute on start. As opposed to snippets, the startup files can accept Hilda
208
- syntax.
209
-
210
- #### Startup File Example
211
-
212
- ```python
213
- cfg.objc_verbose_monitor = True
214
- p.bp(ADDRESS)
215
- p.cont()
216
- ```
145
+ ```shell
146
+ process attach -n proccess_name
147
+ process attach -p proccess_pid
148
+ ```
217
149
 
218
- ```shell
219
- hilda remote HOSTNAME PORT -f startupfile1 -f startupfile2
220
- ```
150
+ When you are ready, just execute `hilda` to move to Hilda's iPython shell.
221
151
 
222
- ## Usage
152
+ ### Inside a Hilda shell
223
153
 
224
154
  Upon starting Hilda shell, you are greeted with:
225
155
 
@@ -232,52 +162,53 @@ Have a nice flight ✈️! Starting an IPython shell...
232
162
  Here is a gist of methods you can access from `p`:
233
163
 
234
164
  - `hd`
235
- - Print an hexdump of given buffer
165
+ - Print an hexdump of given buffer
236
166
  - `lsof`
237
- - Get dictionary of all open FDs
167
+ - Get dictionary of all open FDs
238
168
  - `bt`
239
- - Print an improved backtrace.
169
+ - Print an improved backtrace.
240
170
  - `disable_jetsam_memory_checks`
241
- - Disable jetsam memory checks, prevent raising:
171
+ - Disable jetsam memory checks, prevent raising:
242
172
  `error: Execution was interrupted, reason: EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=15 MB, unused=0x0).`
243
173
  when evaluating expression.
244
174
  - `symbol`
245
- - Get symbol object for a given address
175
+ - Get symbol object for a given address
246
176
  - `objc_symbol`
247
- - Get objc symbol wrapper for given address
177
+ - Get objc symbol wrapper for given address
248
178
  - `inject`
249
- - Inject a single library into currently running process
179
+ - Inject a single library into currently running process
250
180
  - `rebind_symbols`
251
- - Reparse all loaded images symbols
181
+ - Reparse all loaded images symbols
252
182
  - `poke`
253
- - Write data at given address
183
+ - Write data at given address
254
184
  - `peek`
255
- - Read data at given address
185
+ - Read data at given address
256
186
  - `peek_str`
257
- - Peek a buffer till null termination
187
+ - Peek a buffer till null termination
258
188
  - `stop`
259
- - Stop process.
189
+ - Stop process.
260
190
  - `cont`
261
- - Continue process.
191
+ - Continue process.
262
192
  - `detach`
263
- - Detach from process.
193
+ - Detach from process.
264
194
  Useful in order to exit gracefully so process doesn't get killed
265
195
  while you exit
266
196
  - `disass`
267
- - Print disassembly from a given address
197
+ - Print disassembly from a given address
268
198
  - `file_symbol`
269
- - Calculate symbol address without ASLR
199
+ - Calculate symbol address without ASLR
270
200
  - `get_register`
271
- - Get value for register by its name
201
+ - Get value for register by its name
272
202
  - `set_register`
273
- - Set value for register by its name
203
+ - Set value for register by its name
274
204
  - `objc_call`
275
- - Simulate a call to an objc selector
205
+ - Simulate a call to an objc selector
276
206
  - `call`
277
- - Call function at given address with given parameters
207
+ - Call function at given address with given parameters
278
208
  - `monitor`
279
- - Monitor every time a given address is called
209
+ - Monitor every time a given address is called
280
210
  The following options are available:
211
+
281
212
  ```
282
213
  regs={reg1: format}
283
214
  will print register values
@@ -309,58 +240,57 @@ Here is a gist of methods you can access from `p`:
309
240
  override=True
310
241
  override previous break point at same location
311
242
  ```
243
+
312
244
  - `show_current_source`
313
- - print current source code if possible
245
+ - print current source code if possible
314
246
  - `finish`
315
- - Run current frame till its end.
247
+ - Run current frame till its end.
316
248
  - `step_into`
317
- - Step into current instruction.
249
+ - Step into current instruction.
318
250
  - `step_over`
319
- - Step over current instruction.
251
+ - Step over current instruction.
320
252
  - `remove_all_hilda_breakpoints`
321
- - Remove all breakpoints created by Hilda
253
+ - Remove all breakpoints created by Hilda
322
254
  - `remove_hilda_breakpoint`
323
- - Remove a single breakpoint placed by Hilda
255
+ - Remove a single breakpoint placed by Hilda
324
256
  - `force_return`
325
- - Prematurely return from a stack frame, short-circuiting exection of newer frames and optionally
257
+ - Prematurely return from a stack frame, short-circuiting exection of newer frames and optionally
326
258
  yielding a specified value.
327
259
  - `proc_info`
328
- - Print information about currently running mapped process.
260
+ - Print information about currently running mapped process.
329
261
  - `print_proc_entitlements`
330
- - Get the plist embedded inside the process' __LINKEDIT section.
262
+ - Get the plist embedded inside the process' __LINKEDIT section.
331
263
  - `bp`
332
- - Add a breakpoint
264
+ - Add a breakpoint
333
265
  - `show_hilda_breakpoints`
334
- - Show existing breakpoints created by Hilda.
335
- - `show_commands`
336
- - Show available commands.
266
+ - Show existing breakpoints created by Hilda.
337
267
  - `save`
338
- - Save loaded symbols map (for loading later using the load() command)
268
+ - Save loaded symbols map (for loading later using the load() command)
339
269
  - `load`
340
- - Load an existing symbols map (previously saved by the save() command)
270
+ - Load an existing symbols map (previously saved by the save() command)
341
271
  - `po`
342
- - Print given object using LLDB's po command
272
+ - Print given object using LLDB's po command
343
273
  Can also run big chunks of native code:
344
274
 
345
275
  po('NSMutableString *s = [NSMutableString string]; [s appendString:@"abc"]; [s description]')
346
276
  - `globalize_symbols`
347
- - Make all symbols in python's global scope
277
+ - Make all symbols in python's global scope
348
278
  - `jump`
349
- - jump to given symbol
279
+ - jump to given symbol
350
280
  - `lldb_handle_command`
351
- - Execute an LLDB command
281
+ - Execute an LLDB command
352
282
  For example:
353
283
  lldb_handle_command('register read')
354
284
  - `objc_get_class`
355
- - Get ObjC class object
285
+ - Get ObjC class object
356
286
  - `CFSTR`
357
- - Create CFStringRef object from given string
287
+ - Create CFStringRef object from given string
358
288
  - `ns`
359
- - Create NSObject from given data
289
+ - Create NSObject from given data
360
290
  - `from_ns`
361
- - Create python object from NS object.
291
+ - Create python object from NS object.
362
292
  - `evaluate_expression`
363
- - Wrapper for LLDB's EvaluateExpression.
293
+ - Wrapper for LLDB's EvaluateExpression.
364
294
  Used for quick code snippets.
365
295
 
366
296
  Feel free to use local variables inside the expression using format string.
@@ -368,35 +298,37 @@ Here is a gist of methods you can access from `p`:
368
298
  currentDevice = objc_get_class('UIDevice').currentDevice
369
299
  evaluate_expression(f'[[{currentDevice} systemName] hasPrefix:@"2"]')
370
300
  - `import_module`
371
- - Import & reload given python module (intended mainly for external snippets)
301
+ - Import & reload given python module (intended mainly for external snippets)
372
302
  - `unwind`
373
- - Unwind the stack (useful when get_evaluation_unwind() == False)
303
+ - Unwind the stack (useful when get_evaluation_unwind() == False)
374
304
  - `set_selected_thread`
375
- - sets the currently selected thread, which is used in other parts of the program, such as displaying disassembly or
305
+ - sets the currently selected thread, which is used in other parts of the program, such as displaying disassembly or
376
306
  checking registers.
377
307
  This ensures the application focuses on the specified thread for these operations.
378
308
 
379
- ## Magic functions
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)
380
310
 
381
- Sometimes accessing the python API can be tiring, so we added some magic functions to help you out!
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!
382
314
 
383
315
  - `%objc <className>`
384
- - Equivalent to: `className = p.objc_get_class(className)`
316
+ - Equivalent to: `className = p.objc_get_class(className)`
385
317
  - `%fbp <filename> <addressInHex>`
386
- - Equivalent to: `p.file_symbol(addressInHex, filename).bp()`
318
+ - Equivalent to: `p.file_symbol(addressInHex, filename).bp()`
387
319
 
388
- ## Shortcuts
320
+ #### Key-bindings
389
321
 
390
322
  - **F7**: Step Into
391
323
  - **F8**: Step Over
392
324
  - **F9**: Continue
393
325
  - **F10**: Stop
394
326
 
395
- ## Configurables
327
+ #### Configurables
396
328
 
397
329
  The global `cfg` used to configure various settings for evaluation and monitoring.
398
330
 
399
- ### Attributes
331
+ These settings include:
400
332
 
401
333
  - `evaluation_unwind_on_error`: Whether to unwind on error during evaluation. (Default: `False`)
402
334
  - `evaluation_ignore_breakpoints`: Whether to ignore breakpoints during evaluation. (Default: `False`)
@@ -405,15 +337,9 @@ The global `cfg` used to configure various settings for evaluation and monitorin
405
337
  - `objc_verbose_monitor`: When set to `True`, using `monitor()` will automatically print Objective-C method arguments. (
406
338
  Default: `False`)
407
339
 
408
- ### Example Usage
340
+ #### UI Configuration
409
341
 
410
- ```python
411
- cfg.objc_verbose_monitor = True
412
- ```
413
-
414
- ## UI Configuration
415
-
416
- Hilda contains minimal UI for examining the target state.
342
+ Hilda contains a minimal UI for examining the target state.
417
343
  The UI is divided into views:
418
344
 
419
345
  - Registers
@@ -470,7 +396,31 @@ ui.colors.address = 'red'
470
396
  ui.color.title = 'green'
471
397
  ```
472
398
 
473
- ## Symbol objects
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
474
424
 
475
425
  In Hilda, almost everything is wrapped using the `Symbol` Object. Symbol is just a nicer way for referring to addresses
476
426
  encapsulated with an object allowing to deref the memory inside, or use these addresses as functions.
@@ -577,7 +527,7 @@ p.bp('symbol_name')
577
527
  p.bp('symbol_name', module_name='ModuleName')
578
528
  ```
579
529
 
580
- ## Globalized symbols
530
+ #### Globalized symbols
581
531
 
582
532
  Usually you would want/need to use the symbols already mapped into the currently running process. To do so, you can
583
533
  access them using `symbols.<symbol-name>`. The `symbols` global object is of type `SymbolsJar`, which is a wrapper
@@ -622,7 +572,7 @@ jar = jar.code()
622
572
  jar.monitor(regs={'x0': 'x'}, bt=True)
623
573
  ```
624
574
 
625
- ### Objective-C Classes
575
+ #### Objective-C Classes
626
576
 
627
577
  The same as symbols applies to Objective-C classes name resolution. You can either:
628
578
 
@@ -675,7 +625,7 @@ dictionary = NSDictionary.capture_self(True)
675
625
  dictionary.show()
676
626
  ```
677
627
 
678
- ## Objective-C Objects
628
+ #### Objective-C Objects
679
629
 
680
630
  In order to work with ObjC objects, each symbol contains a property called
681
631
  `objc_symbol`. After calling, you can work better with each object:
@@ -741,7 +691,7 @@ abc_string = p.evaluate_expression('[NSString stringWithFormat:@"abc"]')
741
691
  print(abc_string.po())
742
692
  ```
743
693
 
744
- ## Using snippets
694
+ #### Using snippets
745
695
 
746
696
  Snippets are extensions for normal functionality used as quick cookbooks for day-to-day tasks of a debugger.
747
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=ld-j3TUpFlKQqUdd5gCrvB3csNXi_lXdKFr5cuIfSdk,411
7
- hilda/cli.py,sha256=XCL9vD4zWkK2GHnkeBXZ5AC08iC4rtHYBydgi6SzGJI,3325
6
+ hilda/_version.py,sha256=dd-KZw8-eltAgI3rgXg7IQu4qJc9i2jbpZFwyH2XNrA,411
7
+ hilda/cli.py,sha256=FQtOg1iFRtNdVVes_SchIoidOWAENhsLItwXBWCgWgc,3625
8
8
  hilda/common.py,sha256=VFtpOH9IRqbu6lONM6Hz-PxcdJfktgS3akJv5UCHO0s,523
9
- hilda/exceptions.py,sha256=a2EGckJ66ArG2LZJVnUz91gRkqQyc9AEtbS_mfZuI3s,2141
9
+ hilda/exceptions.py,sha256=8L1OvOqns4O4ieiH4YlrMbZkk_PvuyCq4UyqFAodkF8,2042
10
10
  hilda/hilda_ascii_art.html,sha256=-9YCjAKdGbjtdd6uoKrxkkcJq7j16r4dGka2bZ27b4o,120119
11
- hilda/hilda_client.py,sha256=QQowZuvTu0Z9heGaBmLoKqjRa7BdjezEnkx15Jt-y7Y,43997
12
- hilda/launch_lldb.py,sha256=OeZjIILxt6lVQdZnD67E2QHfEK2XU_B-HTa5ZI0NTkw,7920
11
+ hilda/hilda_client.py,sha256=-rajiYqPxLK4n19k9DbFUDGqDNHpl57gYvRuLKC4SW4,45324
12
+ hilda/launch_lldb.py,sha256=sD-02HERIFQEYoPM9JrRfFOTjHE2QUt3L0pNgFsyAEQ,7186
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
@@ -18,7 +18,7 @@ hilda/registers.py,sha256=moGS9MXrfm8vmnqDKWZSg4wmZNydGUadui9uwEwzhsI,856
18
18
  hilda/symbol.py,sha256=_TSQqFysRs82BFNGX9o_ZbkrwzSsWq3FiMbX92VKoPo,7200
19
19
  hilda/symbols_jar.py,sha256=1GPgrbXdsxiirMufrWqVi-DHF9qDXgy_HMtWJD4dJuE,6447
20
20
  hilda/ipython_extensions/events.py,sha256=uV30ZJMwpRw9YwGnQ40uAOh9Mtr8QGJFB7cZeY-pCow,1960
21
- hilda/ipython_extensions/keybindings.py,sha256=2NiTwfakowCj1VT6eEDvMeMiFx_pBvkhVDaQPuX69uM,957
21
+ hilda/ipython_extensions/keybindings.py,sha256=kKArfRWyJ261ieUot4CVTGRmwbnixYtrpgiFCnBfDag,1013
22
22
  hilda/ipython_extensions/magics.py,sha256=ULb63-OyIaWwvSfwRvEG_65ibaI2RTxeX8yPJK8pbc0,1300
23
23
  hilda/objective_c/from_ns_to_json.m,sha256=5Ddl0UJLQXlDYwR_yjE4yZk1aOsJGxoy1oRnhZHPrTw,2847
24
24
  hilda/objective_c/get_objectivec_class_by_module.m,sha256=DC8S8XaWsQSOJZWVWhATr98SZQobA3MmclLsBJGZTcU,704
@@ -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.2.dist-info/LICENSE,sha256=M-LVJ0AFAYB82eueyl8brh-QLPe-iLNVgbCi79-3TDo,1078
50
- hilda-2.0.2.dist-info/METADATA,sha256=agMwzWcpALitue6mJodwdAa3VHvhvfNdlOAKd3GIukk,23891
51
- hilda-2.0.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
52
- hilda-2.0.2.dist-info/entry_points.txt,sha256=9n3O3j6V3XnVR_GcFqCWNgRAbalfukTSW2WvghsLVmA,46
53
- hilda-2.0.2.dist-info/top_level.txt,sha256=TVD7l1WkE1noT866YqPFhiQnjYCYZM5Xz54v_3EYpnI,11
54
- hilda-2.0.2.dist-info/RECORD,,
49
+ hilda-2.0.4.dist-info/LICENSE,sha256=M-LVJ0AFAYB82eueyl8brh-QLPe-iLNVgbCi79-3TDo,1078
50
+ hilda-2.0.4.dist-info/METADATA,sha256=8kOmj9XaEs7kmlquqZcng4mlTKQ4hUUDUk0elsyXIFQ,23306
51
+ hilda-2.0.4.dist-info/WHEEL,sha256=mguMlWGMX-VHnMpKOjjQidIo1ssRlCFu4a4mBpz1s2M,91
52
+ hilda-2.0.4.dist-info/entry_points.txt,sha256=9n3O3j6V3XnVR_GcFqCWNgRAbalfukTSW2WvghsLVmA,46
53
+ hilda-2.0.4.dist-info/top_level.txt,sha256=TVD7l1WkE1noT866YqPFhiQnjYCYZM5Xz54v_3EYpnI,11
54
+ hilda-2.0.4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (70.1.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
File without changes