Skip to content

Commit

Permalink
feat: add support for more DSC cmds
Browse files Browse the repository at this point in the history
  • Loading branch information
blacktop committed Apr 27, 2023
1 parent c9ca7d0 commit 4c3b25c
Show file tree
Hide file tree
Showing 8 changed files with 290 additions and 115 deletions.
40 changes: 34 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,28 +74,56 @@ import ipsw

client = ipsw.IpswClient(base_url='tcp://127.0.0.1:3993')

dsc = client.dsc.get_info("20F5028e__iPhone15,2/dyld_shared_cache_arm64e")
dsc = client.dsc.open("20F5028e__iPhone15,2/dyld_shared_cache_arm64e")
print(dsc)
print(dsc.dylibs[0])

dylib = client.dsc.get_dylib("20F5028e__iPhone15,2/dyld_shared_cache_arm64e", "libswiftCore.dylib")
print(dylib)
```
```bash
```python
<DSC: '(dyld_v1 arm64e) - iOS - FAEC7714-4CCD-3B99-B18F-F5EAB60DE31E'>
{'index': 1, 'name': '/usr/lib/libobjc.A.dylib', 'version': '876.0.0.0.0', 'uuid': '085A190C-6214-38EA-ACCB-428C3E8AFA65', 'load_address': 6443204608}

<Dylib: '64-bit MachO AARCH64 (ARM64e)'>
```

Get dylib inside DSC info

```py
libswiftCore = dsc.dylib("libswiftCore.dylib")
print(libswiftCore)
```

Get DSC symbol addresses

```python
syms = dsc.sym_addrs([{'pattern': '.*zero.*', 'image': 'libsystem_c.dylib'}])
print(syms)
```

Convert between DSC offsets and addresses

```python
off = dsc.a2o(7624591060)
adr = dsc.o2a(61146836)
```

Lookup DSC symbol by address

```python
print(next(dsc.a2s([7624591060])))
```
```json
{"address":7624591060,"symbol":"__exit","demanged":"__exit","mapping":"__TEXT","uuid":"3AB55994-1201-3908-BE27-52BB7EFA7573","ext":".21","image":"/usr/lib/system/libsystem_kernel.dylib","section":"__text","segment":"__TEXT"}
```


Get MachO info

```python
import ipsw

client = ipsw.IpswClient(base_url='tcp://127.0.0.1:3993')

macho = client.macho.get("/bin/ls", arch="arm64e")
macho = client.macho.open("/bin/ls", arch="arm64e")
print(macho)
```
```bash
Expand Down
101 changes: 49 additions & 52 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
import datetime
import os
import sys
sys.path.insert(0, os.path.abspath('..'))

sys.path.insert(0, os.path.abspath(".."))


# -- General configuration ------------------------------------------------
Expand All @@ -30,51 +31,48 @@
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.napoleon',
'myst_parser'
]
extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon", "myst_parser"]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]


source_suffix = {
'.rst': 'restructuredtext',
'.txt': 'markdown',
'.md': 'markdown',
".rst": "restructuredtext",
".txt": "markdown",
".md": "markdown",
}

# The encoding of source files.
#
# source_encoding = 'utf-8-sig'

# The master toctree document.
master_doc = 'index'
master_doc = "index"

# General information about the project.
project = 'ipsw SDK for Python'
project = "ipsw SDK for Python"
year = datetime.datetime.now().year
copyright = '%d Blacktop' % year
author = 'Blacktop'
copyright = "%d Blacktop" % year
author = "Blacktop"

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# see https://github.com/pypa/setuptools_scm#usage-from-sphinx
from importlib.metadata import version
release = version('ipsw')

release = version("ipsw")
# for example take major/minor
version = '.'.join(release.split('.')[:2])
version = ".".join(release.split(".")[:2])

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'en'
language = "en"

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
Expand All @@ -88,7 +86,7 @@
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]

# The reST default role (used for this markup: `text`) to use for all
# documents.
Expand All @@ -110,7 +108,7 @@
# show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"

# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
Expand All @@ -127,15 +125,15 @@
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
html_theme = "alabaster"

# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
html_theme_options = {
'description': 'A Python library for the ipsw API',
'fixed_sidebar': True,
"description": "A Python library for the ipsw API",
"fixed_sidebar": True,
}

# Add any paths that contain custom themes here, relative to this directory.
Expand Down Expand Up @@ -164,7 +162,7 @@
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = ["_static"]

# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
Expand All @@ -186,10 +184,10 @@
# Custom sidebar templates, maps document names to template names.
#
html_sidebars = {
'**': [
'about.html',
'navigation.html',
'searchbox.html',
"**": [
"about.html",
"navigation.html",
"searchbox.html",
]
}

Expand Down Expand Up @@ -250,34 +248,30 @@
# html_search_scorer = 'scorer.js'

# Output file base name for HTML help builder.
htmlhelp_basename = 'ipsw-sdk-pythondoc'
htmlhelp_basename = "ipsw-sdk-pythondoc"

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',

# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',

# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',

# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'ipsw-sdk-python.tex', 'ipsw-sdk-python Documentation',
'Blacktop.', 'manual'),
(master_doc, "ipsw-sdk-python.tex", "ipsw-sdk-python Documentation", "Blacktop.", "manual"),
]

# The name of an image file (relative to this directory) to place at the top of
Expand Down Expand Up @@ -317,10 +311,7 @@

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'ipsw-sdk-python', 'ipsw-sdk-python Documentation',
[author], 1)
]
man_pages = [(master_doc, "ipsw-sdk-python", "ipsw-sdk-python Documentation", [author], 1)]

# If true, show URL addresses after external links.
#
Expand All @@ -333,9 +324,15 @@
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'ipsw-sdk-python', 'ipsw-sdk-python Documentation',
author, 'ipsw-sdk-python', 'One line description of project.',
'Miscellaneous'),
(
master_doc,
"ipsw-sdk-python",
"ipsw-sdk-python Documentation",
author,
"ipsw-sdk-python",
"One line description of project.",
"Miscellaneous",
),
]

# Documents to append as an appendix to all manuals.
Expand Down
4 changes: 2 additions & 2 deletions ipsw/api/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ def __init__(

def _retrieve_server_version(self):
try:
return self.version(api_version=False)["ApiVersion"]
return self.version(api_version=False)["api_version"]
except KeyError:
raise IpswException('Invalid response from ipsw daemon: key "ApiVersion"' " is missing.")
raise IpswException('Invalid response from ipsw daemon: key "api_version"' " is missing.")
except Exception as e:
raise IpswException(f"Error while fetching server API version: {e}")

Expand Down
91 changes: 91 additions & 0 deletions ipsw/api/dsc.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,57 @@
class DscApiMixin:
def dsc_a2o(self, path=None, addr=0):
"""
Convert virtual address to offset. Identical to the ``ipsw dyld a2o``
command.
Args:
path (str): The path to the dyld_shared_cache file.
addr (int): The address to convert to an offset.
Raises:
:py:class:`ipsw.errors.APIError`
If the server returns an error.
"""
return self._result(self._post_json(self._url("/dsc/a2o"), data={"path": path, "addr": addr}), True)

def dsc_a2s(self, path=None, addrs=None, decode=False):
"""
Lookup symbol for address. Identical to the ``ipsw dyld a2s``
command.
Args:
path (str): The path to the dyld_shared_cache file.
addrs ([int]): List of addresses of the symbols to lookup.
decode (bool): If set to true, stream will be decoded into dicts
on the fly. False by default.
Raises:
:py:class:`ipsw.errors.APIError`
If the server returns an error.
"""
params = {
"path": path,
"addrs": addrs,
}
url = self._url("/dsc/a2s")
response = self._post_json(url, data=params, stream=True, timeout=None)
return self._stream_helper(response, decode=decode)

def dsc_o2a(self, path=None, off=0):
"""
Convert offset to virtual address. Identical to the ``ipsw dyld o2a``
command.
Args:
path (str): The path to the dyld_shared_cache file.
off (int): The offset to convert to an address.
Raises:
:py:class:`ipsw.errors.APIError`
If the server returns an error.
"""
return self._result(self._post_json(self._url("/dsc/o2a"), data={"path": path, "off": off}), True)

def dsc_info(self, path=None):
"""
Display DSC header information. Identical to the ``ipsw dyld info --dylibs --json``
Expand Down Expand Up @@ -26,3 +79,41 @@ def dsc_macho(self, path=None, dylib=None):
If the server returns an error.
"""
return self._result(self._get(self._url("/dsc/macho"), params={"path": path, "dylib": dylib}), True)

def dsc_sym_addrs(self, path=None, lookups=None):
"""
Display DSC dylib slide info. Identical to the ``ipsw dyld slide``
command.
Args:
path (str): The path to the dyld_shared_cache file.
lookups (dict): Symbol lookups.
Raises:
:py:class:`ipsw.errors.APIError`
If the server returns an error.
"""
return self._result(self._post_json(self._url("/dsc/symaddr"), data={"path": path, "lookups": lookups}), True)

def dsc_slide_info(self, path=None, auth=False, decode=False):
"""
Display DSC dylib slide info. Identical to the ``ipsw dyld slide``
command.
Args:
path (str): The path to the dyld_shared_cache file.
auth (bool): Filter to only ``auth`` slide-info. False by default.
decode (bool): If set to true, stream will be decoded into dicts
on the fly. False by default.
Raises:
:py:class:`ipsw.errors.APIError`
If the server returns an error.
"""
params = {
"path": path,
"type": "auth" if auth else "",
}
url = self._url("/dsc/slide")
response = self._post_json(url, data=params, stream=True, timeout=None)
return self._stream_helper(response, decode=decode)
2 changes: 1 addition & 1 deletion ipsw/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def create_api_error_from_http_exception(e):
"""
response = e.response
try:
explanation = response.json()["message"]
explanation = response.json()["error"]
except ValueError:
explanation = (response.content or "").strip()
cls = APIError
Expand Down
Loading

0 comments on commit 4c3b25c

Please sign in to comment.