hilda


Namehilda JSON
Version 1.4.3 PyPI version JSON
download
home_pageNone
SummaryLLDB wrapped and empowered by iPython's features
upload_time2024-05-01 06:40:18
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseCopyright (c) 2012-2023 Doron Zarhi and Metan Perelman Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
keywords python debugger lldb ipython ios debug
VCS
bugtrack_url
requirements tqdm docstring_parser coloredlogs hexdump ipython click objc_types_decoder construct pymobiledevice3 keystone-engine tabulate
Travis-CI No Travis.
coveralls test coverage No coveralls.
            - [Description](#description)
- [Installation](#installation)
- [How to use](#how-to-use)
    * [Starting a Hilda shell](#starting-a-hilda-shell)
        + [Bare mode](#bare-mode)
        + [Remote mode](#remote-mode)
    * [Usage](#usage)
    * [Symbol objects](#symbol-objects)
    * [Globalized symbols](#globalized-symbols)
        - [Searching for the right symbol](#searching-for-the-right-symbol)

        + [Objective-C Classes](#objective-c-classes)
    * [Objective-C Objects](#objective-c-objects)
    * [Using snippets](#using-snippets)
    * [Contributing](#contributing)

# Description

Hilda is a debugger which combines both the power of LLDB and iPython for easier debugging.

The name originates from the TV show "Hilda", which is the best friend of
[Frida](https://frida.re/). Both Frida and Hilda are meant for pretty much the same purpose, except Hilda takes the
more "
debugger-y" approach (based on LLDB).

Currently, the project is intended for iOS/OSX debugging, but in the future we will possibly add support for the
following platforms as well:

* Linux
* Android

Since LLDB allows abstraction for both platform and architecture, it should be possible to make the necessary changes
without too many modifications.

Pull requests are more than welcome 😊.

If you need help or have an amazing idea you would like to suggest, feel free
to [start a discussion 💬](https://github.com/doronz88/hilda/discussions).

# Installation

Requirements for remote iOS device (not required for debugging a local OSX process):

* Jailbroken iOS device
* `debugserver` in device's PATH
    * [You can use this tool in order to obtain the binary](https://github.com/doronz88/debugserver-deploy)
    * After re-signing with new entitlements, you can put the binary in the following path: `/usr/bin/debugserver`

In order to install please run:

```shell
xcrun python3 -m pip install --user -U hilda
```

*⚠️ Please note that Hilda is installed on top of XCode's python so LLDB will be able to use its features.*

# How to use

## Starting a Hilda shell

### Attach mode

Use the attach sub-command in order to start an LLDB shell attached to given process.

```shell
hilda attach [-p pid] [-n process-name]
```

After attaching, simply execute `hilda` command to enter the hilda shell.

### Bare mode

Use "Bare mode" to get a "bare-bones" lldb shell, whereas hilda plugin is already loaded and ready to start. This mode
is useful when you need to have custom commands for attaching to the target process (for example when debugging OSX
processes).

To start this mode simply use:

```shell
hilda bare
```

Please refer to the following help page if you require help on the command available to you within the lldb shell:

[lldb command map](https://lldb.llvm.org/use/map.html).

As a cheatsheet, connecting to a remote platform like so:

```shell
platform connect connect://ip:port
```

... and attaching to a local process:

```shell
process attach -n proccess_name
process attach -p proccess_pid
```

When you are ready, just execute `hilda` to move to Hilda's iPython shell.

### Remote mode

This mode will auto-connect to the remote device and attach to your target process assuming you are trying to debug a
remote jailbroken iOS device.

Please note the following:

* script assumes the connected device already **has a running ssh server**, which doesn't require a password (you can
  use
  `ssh-copy-id` to achieve this).

From this point the flow diverges into 2 flows:

### The connected device is connected via network

Run the following command:

```shell
hilda remote HOSTNAME PORT
``` 

## Usage

Upon starting Hilda shell, you are greeted with:

```
Hilda has been successfully loaded! 😎
Use the p global to access all features.
Have a nice flight ✈️! Starting an IPython shell...
```

Here is a gist of methods you can access from `p`:

- `hd`
    - Print an hexdump of given buffer
- `lsof`
    - Get dictionary of all open FDs
- `bt`
    - Print an improved backtrace.
- `disable_jetsam_memory_checks`
    - Disable jetsam memory checks, prevent raising:
      `error: Execution was interrupted, reason: EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=15 MB, unused=0x0).`
      when evaluating expression.
- `symbol`
    - Get symbol object for a given address
- `objc_symbol`
    - Get objc symbol wrapper for given address
- `inject`
    - Inject a single library into currently running process
- `rebind_symbols`
    - Reparse all loaded images symbols
- `poke`
    - Write data at given address
- `peek`
    - Read data at given address
- `peek_str`
    - Peek a buffer till null termination
- `stop`
    - Stop process.
- `cont`
    - Continue process.
- `detach`
    - Detach from process.
      Useful in order to exit gracefully so process doesn't get killed
      while you exit
- `disass`
    - Print disassembly from a given address
- `file_symbol`
    - Calculate symbol address without ASLR
- `get_register`
    - Get value for register by its name
- `set_register`
    - Set value for register by its name
- `objc_call`
    - Simulate a call to an objc selector
- `call`
    - Call function at given address with given parameters
- `monitor`
    - Monitor every time a given address is called
      The following options are available:
      ```
      regs={reg1: format}
      will print register values
  
               Available formats:
                   x: hex
                   s: string
                   cf: use CFCopyDescription() to get more informative description of the object
                   po: use LLDB po command
                   User defined function, will be called like `format_function(hilda_client, value)`.
  
               For example:
                   regs={'x0': 'x'} -> x0 will be printed in HEX format
           expr={lldb_expression: format}
               lldb_expression can be for example '$x0' or '$arg1'
               format behaves just like 'regs' option
           retval=format
               Print function's return value. The format is the same as regs format.
           stop=True
               force a stop at every hit
           bt=True
               print backtrace
           cmd=[cmd1, cmd2]
               run several LLDB commands, one by another
           force_return=value
               force a return from function with the specified value
           name=some_value
               use `some_name` instead of the symbol name automatically extracted from the calling frame
           override=True
               override previous break point at same location
      ```
- `show_current_source`
    - print current source code if possible
- `finish`
    - Run current frame till its end.
- `step_into`
    - Step into current instruction.
- `step_over`
    - Step over current instruction.
- `remove_all_hilda_breakpoints`
    - Remove all breakpoints created by Hilda
- `remove_hilda_breakpoint`
    - Remove a single breakpoint placed by Hilda
- `force_return`
    - Prematurely return from a stack frame, short-circuiting exection of newer frames and optionally
      yielding a specified value.
- `proc_info`
    - Print information about currently running mapped process.
- `print_proc_entitlements`
    - Get the plist embedded inside the process' __LINKEDIT section.
- `bp`
    - Add a breakpoint
- `show_hilda_breakpoints`
    - Show existing breakpoints created by Hilda.
- `show_commands`
    - Show available commands.
- `save`
    - Save loaded symbols map (for loading later using the load() command)
- `load`
    - Load an existing symbols map (previously saved by the save() command)
- `po`
    - Print given object using LLDB's po command
      Can also run big chunks of native code:

      po('NSMutableString *s = [NSMutableString string]; [s appendString:@"abc"]; [s description]')
- `globalize_symbols`
    - Make all symbols in python's global scope
- `jump`
    - jump to given symbol
- `lldb_handle_command`
    - Execute an LLDB command
      For example:
      lldb_handle_command('register read')
- `objc_get_class`
    - Get ObjC class object
- `CFSTR`
    - Create CFStringRef object from given string
- `ns`
    - Create NSObject from given data
- `from_ns`
    - Create python object from NS object.
- `evaluate_expression`
    - Wrapper for LLDB's EvaluateExpression.
      Used for quick code snippets.

      Feel free to use local variables inside the expression using format string.
      For example:
      currentDevice = objc_get_class('UIDevice').currentDevice
      evaluate_expression(f'[[{currentDevice} systemName] hasPrefix:@"2"]')
- `import_module`
    - Import & reload given python module (intended mainly for external snippets)
- `set_evaluation_unwind`
    - Set whether LLDB will attempt to unwind the stack whenever an expression evaluation error occurs.
      Use unwind() to restore when an error is raised in this case.
- `get_evaluation_unwind`
    - Get evaluation unwind state.
      When this value is True, LLDB will attempt unwinding the stack on evaluation errors.
      Otherwise, the stack frame will remain the same on errors to help you investigate the error.
- `set_evaluation_ignore_breakpoints`
    - Set whether to ignore breakpoints while evaluating expressions
- `get_evaluation_ignore_breakpoints`
    - Get evaluation "ignore-breakpoints" state.
- `unwind`
    - Unwind the stack (useful when get_evaluation_unwind() == False)

## Magic functions

Sometimes accessing the python API can be tiring, so we added some magic functions to help you out!

- `%objc <className>`
    - Equivalent to: `className = p.objc_get_class(className)`
- `%fbp <filename> <addressInHex>`
    - Equivalent to: `p.file_symbol(addressInHex, filename).bp()`

## UI Configuration

Hilda contains minimal UI for examining the target state.
The UI is divided into views:

- Registers
- Disassembly
- Stack
- Backtrace

![img.png](gifs/ui.png)

This UI can be displayed at any time be executing:

```python
ui.show()
```

By default `step_into` and `step_over` will show this UI automatically.
You may disable this behaviour by executing:

```python
ui.active = False
```

Attentively, if you want to display UI after hitting a breakpoint, you can register `ui.show` as callback:

```python
p.symbol(0x7ff7b97c21b0).bp(ui.show)
```

Try playing with the UI settings by yourself:

```python
# Disable stack view
ui.views.stack.active = False

# View words from the stack
ui.views.stack.depth = 10

# View last 10 frames
ui.views.backtrace.depth = 10

# Disassemble 5 instructions
ui.views.disassembly.instruction_count = 5

# Change disassembly syntax to AT&T
ui.views.disassembly.flavor = 'att'

# View floating point registers
ui.views.registers.rtype = 'float'

# Change addresses print color
ui.colors.address = 'red'

# Change titles color
ui.color.title = 'green'
```

## Symbol objects

In Hilda, almost everything is wrapped using the `Symbol` Object. Symbol is just a nicer way for referring to addresses
encapsulated with an object allowing to deref the memory inside, or use these addresses as functions.

In order to create a symbol from a given address, please use:

```python
s = p.symbol(0x12345678)

# the Symbol object extends `int`
True == isinstance(s, int)

# print the un-shifted file address 
# (calculating the ASLR shift for you, so you can just view it in IDA)
print(s.file_address)

# or.. if you know the file address, but don't wanna mess
# with ASLR calculations
s = p.file_symbol(0x12345678)

# peek(/read) 20 bytes of memory
print(s.peek(20))

# write into this memory
s.poke('abc')

# let LLDB print-object (it should guess the type automatically
# based on its memory layout)
print(s.po())

# or you can help LLDB with telling it its type manually
print(s.po('char *'))

# jump to `s` as a function, passing (1, "string") as its args 
s(1, "string")

# change the size of each item_size inside `s` for derefs
s.item_size = 1

# *(char *)s = 1
s[0] = 1

# *(((char *)s)+1) = 1
s[1] = 1

# symbol inherits from int, so all int operations apply
s += 4

# change s item size back to 8 to store pointers
s.item_size = 8

# *(intptr_t *)s = 1
s[0] = 1

# storing the return value of the function executed at `0x11223344`
# into `*s`
s[0] = p.symbol(0x11223344)()  # calling symbols also returns symbols 

# attempt to resolve symbol's name
print(p.symbol(0x11223344).lldb_symbol)

# monitor each time a symbol is called into console and print its backtrace (`bt` option)
# this will create a scripted breakpoint which prints your desired data and continue
s.monitor(bt=True)

# you can also:
#   bt -> view the backtrace
#   regs -> view registers upon each call in your desired format
#   retval -> view the return value upon each call in your desired format
#   cmd -> execute a list of LLDB commands on each hit
s.monitor(regs={'x0': 'x'},  # print `x0` in HEX form
          retval='po',  # use LLDB's `po` for printing the returned value
          bt=True,  # view backtrace (will also resolve ASLR addresses for you)
          cmd=['thread list'],  # show thread list 
          )

# we can also just `force_return` with a hard-coded value to practically disable 
# a specific functionality
s.monitor(force_return=0)  # cause the function to always return `0`

# as for everything, if you need help understanding each such feature, 
# simply execute the following to view its help (many such features even contain examples) 
s.monitor?

# create a scripted_breakpoint manually
def scripted_breakpoint(hilda, *args):
    # like everything in hilda, registers are also
    # just simple `Symbol` objects, so feel free to 
    # use them to your heart's content :)
    if hilda.registers.x0.peek(4) == b'\x11\x22\x33\x44':
        hilda.registers.x0 = hilda.symbols.malloc(200)
        hilda.registers.x0.poke(b'\x22' * 200)

    # just continue the process
    hilda.cont()


s.bp(scripted_breakpoint)

# Place a breakpoint at a symbol not yet loaded by it's name
p.bp('symbol_name')

# In case you need to specify a specific library it's loaded from
p.bp('symbol_name', module_name='ModuleName')
```

## Globalized symbols

Usually you would want/need to use the symbols already mapped into the currently running process. To do so, you can
access them using `symbols.<symbol-name>`. The `symbols` global object is of type `SymbolsJar`, which is a wrapper
to `dict` for accessing all exported symbols. For example, the following will generate a call to the exported
`malloc` function with `20` as its only argument:

```python
x = p.symbols.malloc(20)
```

You can also just write their name as if they already were in the global scope. Hilda will check if no name collision
exists, and if so, will perform the following lazily for you:

```python
x = malloc(20)

# is equivalent to:
malloc = p.symbols.malloc
x = malloc(20)
```

#### Searching for the right symbol

Sometimes you don't really know where to start your research. All you have is just theories of how your desired exported
symbol should be called (if any).

For that reason alone, we have the `rebind_symbols()`
command - to help you find the symbol you are looking for.

```python
p.rebind_symbols()  # this might take some time

# find all symbols prefixed as `mem*` AND don't have `cpy`
# in their name
jar = p.symbols.startswith('mem') - p.symbols.find('cpy')

# filter only symbols of type "code" (removing data global for example)
jar = jar.code()

# monitor every time each one is called, print its `x0` in HEX
# form and show the backtrace
jar.monitor(regs={'x0': 'x'}, bt=True)
```

### Objective-C Classes

The same as symbols applies to Objective-C classes name resolution. You can either:

```python
d = NSDictionary.new()  # call its `new` selector

# which is equivalent to:
NSDictionary = p.objc_get_class('NSDictionary')
d = NSDictionary.new()

# Or you can use the IPython magic function
%objc
NSDictionary
```

This is possible only since `NSDictionary` is exported. In case it is not, you must call `objc_get_class()` explicitly.

As you can see, you can directly access all the class' methods.

Please look what more stuff you can do as shown below:

```python
# show the class' ivars
print(NSDictionary.ivars)

# show the class' methods
print(NSDictionary.methods)

# show the class' proprties
print(NSDictionary.properties)

# view class' selectors which are prefixed with 'init'
print(NSDictionary.symbols_jar.startswith('-[NSDictionary init'))

# you can of course use any of `SymbolsJar` over them, for example:
# this will `po` (print object) all those selectors returned value
NSDictionary.symbols_jar.startswith('-[NSDictionary init').monitior(retval='po')

# monitor each time any selector in NSDictionary is called
NSDictionary.monitor()

# `force_return` for some specific selector with a hard-coded value (4)
NSDictionary.get_method('valueForKey:').address.monitor(force_return=4)

# capture the `self` object at the first hit of any selector
# `True` for busy-wait for object to be captured
dictionary = NSDictionary.capture_self(True)

# print a colored and formatted version for class layout
dictionary.show()
```

## Objective-C Objects

In order to work with ObjC objects, each symbol contains a property called
`objc_symbol`. After calling, you can work better with each object:

```python
dict = NSDictionary.new().objc_symbol
dict.show()  # print object layout

# just like class, you can access its ivars, method, etc...
print(dict.ivars)

# except now they have values you can view
print(dict._ivarName)

# or edit
dict._ivarName = value

# and of course you can call the object's methods
# hilda will checks if the method returned an ObjC object:
#   - if so, call `objc_symbol` upon it for you
#   - otherwise, leave it as a simple `Symbol` object
arr = dict.objectForKey_('keyContainingNSArray')

# you can also call class-methods
# hilda will call it using either the instance object,
# or the class object respectively of the use
newDict = dict.dictionary()

# print the retrieved object
print(arr.po())
```

Also, working with Objective-C objects like this can be somewhat exhausting, so we created the `ns` and `from_ns`
commands so you are able to use complicated types when parsing values and passing as arguments:

```python
import datetime

# using the `ns` command we can just pass a python-native dictionary
function_requiring_a_specfic_dictionary(ns({
    'key1': 'string',  # will convert to NSString
    'key2': True,  # will convert to NSNumber
    'key3': b'1234',  # will convert to NSData
    'key4': datetime.datetime(2021, 1, 1)  # will convert to NSDate
}))

# and also parse one
normal_python_dict = p.cf({
    'key1': 'string',  # will convert to NSString
    'key2': True,  # will convert to NSNumber
    'key3': b'1234',  # will convert to NSData
    'key4': datetime.datetime(2021, 1, 1)  # will convert to NSDate
}).py()
```

On last resort, if the object is not serializable for this to work, you can just run pure Objective-C code:

```python
# let LLDB compile and execute the expression
abc_string = p.evaluate_expression('[NSString stringWithFormat:@"abc"]')

# will print "abc"
print(abc_string.po())
```

## Using snippets

Snippets are extensions for normal functionality used as quick cookbooks for day-to-day tasks of a debugger.

They all use the following concept to use:

```python
from hilda.snippets import snippet_name

snippet_name.do_domething()  
```

For example, XPC sniffing can be done using:

```python
from hilda.snippets import xpc

xpc.sniff_all()
```

This will monitor all XPC related traffic in the given process.

## Contributing

Please run the tests as follows before submitting a PR:

```shell
xcrun python3 -m tests aggregated

# wait for lldb shell prompt

run_tests
```

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "hilda",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>, netanel cohen <netanelc305@protonmail.com>",
    "keywords": "python, debugger, lldb, ipython, ios, debug",
    "author": null,
    "author_email": "doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>, netanel cohen <netanelc305@protonmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/25/cc/03a0e31029475e19960b1ece59b0682a14348c291be0dfe6bc738f1bc1a8/hilda-1.4.3.tar.gz",
    "platform": null,
    "description": "- [Description](#description)\n- [Installation](#installation)\n- [How to use](#how-to-use)\n    * [Starting a Hilda shell](#starting-a-hilda-shell)\n        + [Bare mode](#bare-mode)\n        + [Remote mode](#remote-mode)\n    * [Usage](#usage)\n    * [Symbol objects](#symbol-objects)\n    * [Globalized symbols](#globalized-symbols)\n        - [Searching for the right symbol](#searching-for-the-right-symbol)\n\n        + [Objective-C Classes](#objective-c-classes)\n    * [Objective-C Objects](#objective-c-objects)\n    * [Using snippets](#using-snippets)\n    * [Contributing](#contributing)\n\n# Description\n\nHilda is a debugger which combines both the power of LLDB and iPython for easier debugging.\n\nThe name originates from the TV show \"Hilda\", which is the best friend of\n[Frida](https://frida.re/). Both Frida and Hilda are meant for pretty much the same purpose, except Hilda takes the\nmore \"\ndebugger-y\" approach (based on LLDB).\n\nCurrently, the project is intended for iOS/OSX debugging, but in the future we will possibly add support for the\nfollowing platforms as well:\n\n* Linux\n* Android\n\nSince LLDB allows abstraction for both platform and architecture, it should be possible to make the necessary changes\nwithout too many modifications.\n\nPull requests are more than welcome \ud83d\ude0a.\n\nIf you need help or have an amazing idea you would like to suggest, feel free\nto [start a discussion \ud83d\udcac](https://github.com/doronz88/hilda/discussions).\n\n# Installation\n\nRequirements for remote iOS device (not required for debugging a local OSX process):\n\n* Jailbroken iOS device\n* `debugserver` in device's PATH\n    * [You can use this tool in order to obtain the binary](https://github.com/doronz88/debugserver-deploy)\n    * After re-signing with new entitlements, you can put the binary in the following path: `/usr/bin/debugserver`\n\nIn order to install please run:\n\n```shell\nxcrun python3 -m pip install --user -U hilda\n```\n\n*\u26a0\ufe0f Please note that Hilda is installed on top of XCode's python so LLDB will be able to use its features.*\n\n# How to use\n\n## Starting a Hilda shell\n\n### Attach mode\n\nUse the attach sub-command in order to start an LLDB shell attached to given process.\n\n```shell\nhilda attach [-p pid] [-n process-name]\n```\n\nAfter attaching, simply execute `hilda` command to enter the hilda shell.\n\n### Bare mode\n\nUse \"Bare mode\" to get a \"bare-bones\" lldb shell, whereas hilda plugin is already loaded and ready to start. This mode\nis useful when you need to have custom commands for attaching to the target process (for example when debugging OSX\nprocesses).\n\nTo start this mode simply use:\n\n```shell\nhilda bare\n```\n\nPlease refer to the following help page if you require help on the command available to you within the lldb shell:\n\n[lldb command map](https://lldb.llvm.org/use/map.html).\n\nAs a cheatsheet, connecting to a remote platform like so:\n\n```shell\nplatform connect connect://ip:port\n```\n\n... and attaching to a local process:\n\n```shell\nprocess attach -n proccess_name\nprocess attach -p proccess_pid\n```\n\nWhen you are ready, just execute `hilda` to move to Hilda's iPython shell.\n\n### Remote mode\n\nThis mode will auto-connect to the remote device and attach to your target process assuming you are trying to debug a\nremote jailbroken iOS device.\n\nPlease note the following:\n\n* script assumes the connected device already **has a running ssh server**, which doesn't require a password (you can\n  use\n  `ssh-copy-id` to achieve this).\n\nFrom this point the flow diverges into 2 flows:\n\n### The connected device is connected via network\n\nRun the following command:\n\n```shell\nhilda remote HOSTNAME PORT\n``` \n\n## Usage\n\nUpon starting Hilda shell, you are greeted with:\n\n```\nHilda has been successfully loaded! \ud83d\ude0e\nUse the p global to access all features.\nHave a nice flight \u2708\ufe0f! Starting an IPython shell...\n```\n\nHere is a gist of methods you can access from `p`:\n\n- `hd`\n    - Print an hexdump of given buffer\n- `lsof`\n    - Get dictionary of all open FDs\n- `bt`\n    - Print an improved backtrace.\n- `disable_jetsam_memory_checks`\n    - Disable jetsam memory checks, prevent raising:\n      `error: Execution was interrupted, reason: EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=15 MB, unused=0x0).`\n      when evaluating expression.\n- `symbol`\n    - Get symbol object for a given address\n- `objc_symbol`\n    - Get objc symbol wrapper for given address\n- `inject`\n    - Inject a single library into currently running process\n- `rebind_symbols`\n    - Reparse all loaded images symbols\n- `poke`\n    - Write data at given address\n- `peek`\n    - Read data at given address\n- `peek_str`\n    - Peek a buffer till null termination\n- `stop`\n    - Stop process.\n- `cont`\n    - Continue process.\n- `detach`\n    - Detach from process.\n      Useful in order to exit gracefully so process doesn't get killed\n      while you exit\n- `disass`\n    - Print disassembly from a given address\n- `file_symbol`\n    - Calculate symbol address without ASLR\n- `get_register`\n    - Get value for register by its name\n- `set_register`\n    - Set value for register by its name\n- `objc_call`\n    - Simulate a call to an objc selector\n- `call`\n    - Call function at given address with given parameters\n- `monitor`\n    - Monitor every time a given address is called\n      The following options are available:\n      ```\n      regs={reg1: format}\n      will print register values\n  \n               Available formats:\n                   x: hex\n                   s: string\n                   cf: use CFCopyDescription() to get more informative description of the object\n                   po: use LLDB po command\n                   User defined function, will be called like `format_function(hilda_client, value)`.\n  \n               For example:\n                   regs={'x0': 'x'} -> x0 will be printed in HEX format\n           expr={lldb_expression: format}\n               lldb_expression can be for example '$x0' or '$arg1'\n               format behaves just like 'regs' option\n           retval=format\n               Print function's return value. The format is the same as regs format.\n           stop=True\n               force a stop at every hit\n           bt=True\n               print backtrace\n           cmd=[cmd1, cmd2]\n               run several LLDB commands, one by another\n           force_return=value\n               force a return from function with the specified value\n           name=some_value\n               use `some_name` instead of the symbol name automatically extracted from the calling frame\n           override=True\n               override previous break point at same location\n      ```\n- `show_current_source`\n    - print current source code if possible\n- `finish`\n    - Run current frame till its end.\n- `step_into`\n    - Step into current instruction.\n- `step_over`\n    - Step over current instruction.\n- `remove_all_hilda_breakpoints`\n    - Remove all breakpoints created by Hilda\n- `remove_hilda_breakpoint`\n    - Remove a single breakpoint placed by Hilda\n- `force_return`\n    - Prematurely return from a stack frame, short-circuiting exection of newer frames and optionally\n      yielding a specified value.\n- `proc_info`\n    - Print information about currently running mapped process.\n- `print_proc_entitlements`\n    - Get the plist embedded inside the process' __LINKEDIT section.\n- `bp`\n    - Add a breakpoint\n- `show_hilda_breakpoints`\n    - Show existing breakpoints created by Hilda.\n- `show_commands`\n    - Show available commands.\n- `save`\n    - Save loaded symbols map (for loading later using the load() command)\n- `load`\n    - Load an existing symbols map (previously saved by the save() command)\n- `po`\n    - Print given object using LLDB's po command\n      Can also run big chunks of native code:\n\n      po('NSMutableString *s = [NSMutableString string]; [s appendString:@\"abc\"]; [s description]')\n- `globalize_symbols`\n    - Make all symbols in python's global scope\n- `jump`\n    - jump to given symbol\n- `lldb_handle_command`\n    - Execute an LLDB command\n      For example:\n      lldb_handle_command('register read')\n- `objc_get_class`\n    - Get ObjC class object\n- `CFSTR`\n    - Create CFStringRef object from given string\n- `ns`\n    - Create NSObject from given data\n- `from_ns`\n    - Create python object from NS object.\n- `evaluate_expression`\n    - Wrapper for LLDB's EvaluateExpression.\n      Used for quick code snippets.\n\n      Feel free to use local variables inside the expression using format string.\n      For example:\n      currentDevice = objc_get_class('UIDevice').currentDevice\n      evaluate_expression(f'[[{currentDevice} systemName] hasPrefix:@\"2\"]')\n- `import_module`\n    - Import & reload given python module (intended mainly for external snippets)\n- `set_evaluation_unwind`\n    - Set whether LLDB will attempt to unwind the stack whenever an expression evaluation error occurs.\n      Use unwind() to restore when an error is raised in this case.\n- `get_evaluation_unwind`\n    - Get evaluation unwind state.\n      When this value is True, LLDB will attempt unwinding the stack on evaluation errors.\n      Otherwise, the stack frame will remain the same on errors to help you investigate the error.\n- `set_evaluation_ignore_breakpoints`\n    - Set whether to ignore breakpoints while evaluating expressions\n- `get_evaluation_ignore_breakpoints`\n    - Get evaluation \"ignore-breakpoints\" state.\n- `unwind`\n    - Unwind the stack (useful when get_evaluation_unwind() == False)\n\n## Magic functions\n\nSometimes accessing the python API can be tiring, so we added some magic functions to help you out!\n\n- `%objc <className>`\n    - Equivalent to: `className = p.objc_get_class(className)`\n- `%fbp <filename> <addressInHex>`\n    - Equivalent to: `p.file_symbol(addressInHex, filename).bp()`\n\n## UI Configuration\n\nHilda contains minimal UI for examining the target state.\nThe UI is divided into views:\n\n- Registers\n- Disassembly\n- Stack\n- Backtrace\n\n![img.png](gifs/ui.png)\n\nThis UI can be displayed at any time be executing:\n\n```python\nui.show()\n```\n\nBy default `step_into` and `step_over` will show this UI automatically.\nYou may disable this behaviour by executing:\n\n```python\nui.active = False\n```\n\nAttentively, if you want to display UI after hitting a breakpoint, you can register `ui.show` as callback:\n\n```python\np.symbol(0x7ff7b97c21b0).bp(ui.show)\n```\n\nTry playing with the UI settings by yourself:\n\n```python\n# Disable stack view\nui.views.stack.active = False\n\n# View words from the stack\nui.views.stack.depth = 10\n\n# View last 10 frames\nui.views.backtrace.depth = 10\n\n# Disassemble 5 instructions\nui.views.disassembly.instruction_count = 5\n\n# Change disassembly syntax to AT&T\nui.views.disassembly.flavor = 'att'\n\n# View floating point registers\nui.views.registers.rtype = 'float'\n\n# Change addresses print color\nui.colors.address = 'red'\n\n# Change titles color\nui.color.title = 'green'\n```\n\n## Symbol objects\n\nIn Hilda, almost everything is wrapped using the `Symbol` Object. Symbol is just a nicer way for referring to addresses\nencapsulated with an object allowing to deref the memory inside, or use these addresses as functions.\n\nIn order to create a symbol from a given address, please use:\n\n```python\ns = p.symbol(0x12345678)\n\n# the Symbol object extends `int`\nTrue == isinstance(s, int)\n\n# print the un-shifted file address \n# (calculating the ASLR shift for you, so you can just view it in IDA)\nprint(s.file_address)\n\n# or.. if you know the file address, but don't wanna mess\n# with ASLR calculations\ns = p.file_symbol(0x12345678)\n\n# peek(/read) 20 bytes of memory\nprint(s.peek(20))\n\n# write into this memory\ns.poke('abc')\n\n# let LLDB print-object (it should guess the type automatically\n# based on its memory layout)\nprint(s.po())\n\n# or you can help LLDB with telling it its type manually\nprint(s.po('char *'))\n\n# jump to `s` as a function, passing (1, \"string\") as its args \ns(1, \"string\")\n\n# change the size of each item_size inside `s` for derefs\ns.item_size = 1\n\n# *(char *)s = 1\ns[0] = 1\n\n# *(((char *)s)+1) = 1\ns[1] = 1\n\n# symbol inherits from int, so all int operations apply\ns += 4\n\n# change s item size back to 8 to store pointers\ns.item_size = 8\n\n# *(intptr_t *)s = 1\ns[0] = 1\n\n# storing the return value of the function executed at `0x11223344`\n# into `*s`\ns[0] = p.symbol(0x11223344)()  # calling symbols also returns symbols \n\n# attempt to resolve symbol's name\nprint(p.symbol(0x11223344).lldb_symbol)\n\n# monitor each time a symbol is called into console and print its backtrace (`bt` option)\n# this will create a scripted breakpoint which prints your desired data and continue\ns.monitor(bt=True)\n\n# you can also:\n#   bt -> view the backtrace\n#   regs -> view registers upon each call in your desired format\n#   retval -> view the return value upon each call in your desired format\n#   cmd -> execute a list of LLDB commands on each hit\ns.monitor(regs={'x0': 'x'},  # print `x0` in HEX form\n          retval='po',  # use LLDB's `po` for printing the returned value\n          bt=True,  # view backtrace (will also resolve ASLR addresses for you)\n          cmd=['thread list'],  # show thread list \n          )\n\n# we can also just `force_return` with a hard-coded value to practically disable \n# a specific functionality\ns.monitor(force_return=0)  # cause the function to always return `0`\n\n# as for everything, if you need help understanding each such feature, \n# simply execute the following to view its help (many such features even contain examples) \ns.monitor?\n\n# create a scripted_breakpoint manually\ndef scripted_breakpoint(hilda, *args):\n    # like everything in hilda, registers are also\n    # just simple `Symbol` objects, so feel free to \n    # use them to your heart's content :)\n    if hilda.registers.x0.peek(4) == b'\\x11\\x22\\x33\\x44':\n        hilda.registers.x0 = hilda.symbols.malloc(200)\n        hilda.registers.x0.poke(b'\\x22' * 200)\n\n    # just continue the process\n    hilda.cont()\n\n\ns.bp(scripted_breakpoint)\n\n# Place a breakpoint at a symbol not yet loaded by it's name\np.bp('symbol_name')\n\n# In case you need to specify a specific library it's loaded from\np.bp('symbol_name', module_name='ModuleName')\n```\n\n## Globalized symbols\n\nUsually you would want/need to use the symbols already mapped into the currently running process. To do so, you can\naccess them using `symbols.<symbol-name>`. The `symbols` global object is of type `SymbolsJar`, which is a wrapper\nto `dict` for accessing all exported symbols. For example, the following will generate a call to the exported\n`malloc` function with `20` as its only argument:\n\n```python\nx = p.symbols.malloc(20)\n```\n\nYou can also just write their name as if they already were in the global scope. Hilda will check if no name collision\nexists, and if so, will perform the following lazily for you:\n\n```python\nx = malloc(20)\n\n# is equivalent to:\nmalloc = p.symbols.malloc\nx = malloc(20)\n```\n\n#### Searching for the right symbol\n\nSometimes you don't really know where to start your research. All you have is just theories of how your desired exported\nsymbol should be called (if any).\n\nFor that reason alone, we have the `rebind_symbols()`\ncommand - to help you find the symbol you are looking for.\n\n```python\np.rebind_symbols()  # this might take some time\n\n# find all symbols prefixed as `mem*` AND don't have `cpy`\n# in their name\njar = p.symbols.startswith('mem') - p.symbols.find('cpy')\n\n# filter only symbols of type \"code\" (removing data global for example)\njar = jar.code()\n\n# monitor every time each one is called, print its `x0` in HEX\n# form and show the backtrace\njar.monitor(regs={'x0': 'x'}, bt=True)\n```\n\n### Objective-C Classes\n\nThe same as symbols applies to Objective-C classes name resolution. You can either:\n\n```python\nd = NSDictionary.new()  # call its `new` selector\n\n# which is equivalent to:\nNSDictionary = p.objc_get_class('NSDictionary')\nd = NSDictionary.new()\n\n# Or you can use the IPython magic function\n%objc\nNSDictionary\n```\n\nThis is possible only since `NSDictionary` is exported. In case it is not, you must call `objc_get_class()` explicitly.\n\nAs you can see, you can directly access all the class' methods.\n\nPlease look what more stuff you can do as shown below:\n\n```python\n# show the class' ivars\nprint(NSDictionary.ivars)\n\n# show the class' methods\nprint(NSDictionary.methods)\n\n# show the class' proprties\nprint(NSDictionary.properties)\n\n# view class' selectors which are prefixed with 'init'\nprint(NSDictionary.symbols_jar.startswith('-[NSDictionary init'))\n\n# you can of course use any of `SymbolsJar` over them, for example:\n# this will `po` (print object) all those selectors returned value\nNSDictionary.symbols_jar.startswith('-[NSDictionary init').monitior(retval='po')\n\n# monitor each time any selector in NSDictionary is called\nNSDictionary.monitor()\n\n# `force_return` for some specific selector with a hard-coded value (4)\nNSDictionary.get_method('valueForKey:').address.monitor(force_return=4)\n\n# capture the `self` object at the first hit of any selector\n# `True` for busy-wait for object to be captured\ndictionary = NSDictionary.capture_self(True)\n\n# print a colored and formatted version for class layout\ndictionary.show()\n```\n\n## Objective-C Objects\n\nIn order to work with ObjC objects, each symbol contains a property called\n`objc_symbol`. After calling, you can work better with each object:\n\n```python\ndict = NSDictionary.new().objc_symbol\ndict.show()  # print object layout\n\n# just like class, you can access its ivars, method, etc...\nprint(dict.ivars)\n\n# except now they have values you can view\nprint(dict._ivarName)\n\n# or edit\ndict._ivarName = value\n\n# and of course you can call the object's methods\n# hilda will checks if the method returned an ObjC object:\n#   - if so, call `objc_symbol` upon it for you\n#   - otherwise, leave it as a simple `Symbol` object\narr = dict.objectForKey_('keyContainingNSArray')\n\n# you can also call class-methods\n# hilda will call it using either the instance object,\n# or the class object respectively of the use\nnewDict = dict.dictionary()\n\n# print the retrieved object\nprint(arr.po())\n```\n\nAlso, working with Objective-C objects like this can be somewhat exhausting, so we created the `ns` and `from_ns`\ncommands so you are able to use complicated types when parsing values and passing as arguments:\n\n```python\nimport datetime\n\n# using the `ns` command we can just pass a python-native dictionary\nfunction_requiring_a_specfic_dictionary(ns({\n    'key1': 'string',  # will convert to NSString\n    'key2': True,  # will convert to NSNumber\n    'key3': b'1234',  # will convert to NSData\n    'key4': datetime.datetime(2021, 1, 1)  # will convert to NSDate\n}))\n\n# and also parse one\nnormal_python_dict = p.cf({\n    'key1': 'string',  # will convert to NSString\n    'key2': True,  # will convert to NSNumber\n    'key3': b'1234',  # will convert to NSData\n    'key4': datetime.datetime(2021, 1, 1)  # will convert to NSDate\n}).py()\n```\n\nOn last resort, if the object is not serializable for this to work, you can just run pure Objective-C code:\n\n```python\n# let LLDB compile and execute the expression\nabc_string = p.evaluate_expression('[NSString stringWithFormat:@\"abc\"]')\n\n# will print \"abc\"\nprint(abc_string.po())\n```\n\n## Using snippets\n\nSnippets are extensions for normal functionality used as quick cookbooks for day-to-day tasks of a debugger.\n\nThey all use the following concept to use:\n\n```python\nfrom hilda.snippets import snippet_name\n\nsnippet_name.do_domething()  \n```\n\nFor example, XPC sniffing can be done using:\n\n```python\nfrom hilda.snippets import xpc\n\nxpc.sniff_all()\n```\n\nThis will monitor all XPC related traffic in the given process.\n\n## Contributing\n\nPlease run the tests as follows before submitting a PR:\n\n```shell\nxcrun python3 -m tests aggregated\n\n# wait for lldb shell prompt\n\nrun_tests\n```\n",
    "bugtrack_url": null,
    "license": "Copyright (c) 2012-2023 Doron Zarhi and Metan Perelman  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
    "summary": "LLDB wrapped and empowered by iPython's features",
    "version": "1.4.3",
    "project_urls": {
        "Bug Reports": "https://github.com/doronz88/hilda/issues",
        "Homepage": "https://github.com/doronz88/hilda"
    },
    "split_keywords": [
        "python",
        " debugger",
        " lldb",
        " ipython",
        " ios",
        " debug"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ff7a335b0777deb74f03171c39d4f4f4f29af0bded8369a58bf7841b8e27bd40",
                "md5": "f6c619061f9f8c080ddffa683cc96c0e",
                "sha256": "bb962a34a3608dc02cbf68012f2cb13750fa13feb66092ec51177cb2bbc669d2"
            },
            "downloads": -1,
            "filename": "hilda-1.4.3-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "f6c619061f9f8c080ddffa683cc96c0e",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 861660,
            "upload_time": "2024-05-01T06:40:14",
            "upload_time_iso_8601": "2024-05-01T06:40:14.081368Z",
            "url": "https://files.pythonhosted.org/packages/ff/7a/335b0777deb74f03171c39d4f4f4f29af0bded8369a58bf7841b8e27bd40/hilda-1.4.3-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "25cc03a0e31029475e19960b1ece59b0682a14348c291be0dfe6bc738f1bc1a8",
                "md5": "63935c93d4e2a670b61a774dec073c39",
                "sha256": "75010bdfc0f832e182e7b4849645fb343c120f4a526e27ff2f1c7222ac4f353d"
            },
            "downloads": -1,
            "filename": "hilda-1.4.3.tar.gz",
            "has_sig": false,
            "md5_digest": "63935c93d4e2a670b61a774dec073c39",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 871130,
            "upload_time": "2024-05-01T06:40:18",
            "upload_time_iso_8601": "2024-05-01T06:40:18.790368Z",
            "url": "https://files.pythonhosted.org/packages/25/cc/03a0e31029475e19960b1ece59b0682a14348c291be0dfe6bc738f1bc1a8/hilda-1.4.3.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-05-01 06:40:18",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "doronz88",
    "github_project": "hilda",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [
        {
            "name": "tqdm",
            "specs": []
        },
        {
            "name": "docstring_parser",
            "specs": []
        },
        {
            "name": "coloredlogs",
            "specs": []
        },
        {
            "name": "hexdump",
            "specs": []
        },
        {
            "name": "ipython",
            "specs": []
        },
        {
            "name": "click",
            "specs": []
        },
        {
            "name": "objc_types_decoder",
            "specs": []
        },
        {
            "name": "construct",
            "specs": []
        },
        {
            "name": "pymobiledevice3",
            "specs": []
        },
        {
            "name": "keystone-engine",
            "specs": []
        },
        {
            "name": "tabulate",
            "specs": []
        }
    ],
    "lcname": "hilda"
}
        
Elapsed time: 0.24455s