Skip to content

Commit

Permalink
Uploaded beta 2
Browse files Browse the repository at this point in the history
  • Loading branch information
NyanSatan committed Aug 14, 2021
1 parent bc95c71 commit aca851c
Show file tree
Hide file tree
Showing 8 changed files with 367 additions and 6 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
build
package
.DS_Store
__pycache__
misc
14 changes: 10 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,21 @@ HANDLER_BUILD_DIR = $(HANDLER_DIR)/build
HANDLER_BUILD_TARGET = $(HANDLER_BUILD_DIR)/*.bin
HANDLER_FINAL_BUILD_DIR = $(BUILD_DIR)/payloads

PYTHON_DIR = python

DIR_HELPER = mkdir -p $(BUILD_DIR) $(HANDLER_FINAL_BUILD_DIR)
PACKAGE_DIR_HELPER = mkdir -p $(PACKAGE_DIR)

.PHONY: all astris ctl handler package clean
.PHONY: all astris ctl handler python package clean

all: astris ctl handler
all: astris ctl handler python
@echo "%%%%%% all done"

package: astris ctl handler
package: astris ctl handler python
@echo "%%%%%% packaging"
@$(PACKAGE_DIR_HELPER)
@rm -rf $(PACKAGE_FILE)
@zip -x .DS_Store -r9 $(PACKAGE_FILE) $(BUILD_DIR)/*
@zip -x "*.DS_Store*" -x "*__pycache__*" -r9 $(PACKAGE_FILE) $(BUILD_DIR)/*
@echo "%%%%%% packaging done"

astris:
Expand All @@ -57,6 +59,10 @@ $(VALID_HANDLER_TARGETS):
@echo "%%%%%% copying the handler payload"
@cp -a $(HANDLER_BUILD_TARGET) $(HANDLER_FINAL_BUILD_DIR)

python:
@echo "%%%%%% copying the Python control utility"
@cp -a $(PYTHON_DIR) $(BUILD_DIR)

clean:
@make -C $(CTL_DIR) clean
@make -C $(HANDLER_DIR) clean
Expand Down
71 changes: 69 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,81 @@ noone@Mac-mini-noone anya %

***Warning**: since we're using prototype devices here, you obviously need to provide a development KBAG, not production (development one usually comes second in an Image4)!*

## Python
### Requirements
* pyusb

### Library usage
Placed in `python/anya` folder. Usage is quite straightforward:

```python
from anya import *
from anya.exceptions import *

dev = AnyaDevice(ecid=ECID) # creating connection class

try:
dev.connect() # connecting
except AnyaError as e:
print("failed to connect: %s" % str(e))
exit(-1)


try:
decoded = decode_kbag(KBAG) # decoding a KBAG string to bytes
except AnyaValueError as e:
print("failed to parse KBAG: %s" % str(e))
exit(-1)

try:
key = dev.decrypt_kbag(decoded) # decrypting
except AnyaUSBError as e:
print("failed to decrypt KBAG: %s" % str(e))
exit(-1)

print(encode_key(key, to_upper=True)) # encoding key to a string (and printing)
```

### Tools usage
Placed in `python/` folder. There're two of them - **anyactl** (basically the same thing as the C variant of the ctl):

```
noone@Mac-mini-noone anya % build/python/anyactl
usage: anyactl [-h] [-k KBAG] [-b COUNT] [-e ECID]
Decrypt some KBAG or run a benchmark
optional arguments:
-h, --help show this help message and exit
-k KBAG decrypt a KBAG
-b COUNT run benchmark
-e ECID ECID to look for
```

...and **anyafromjson** - this one is used to decrypt KBAGs in batch from a JSON file:

```
noone@Mac-mini-noone anya % build/python/anyafromjson
usage: build/python/anyafromjson <input> <output> [ecid (hex)]
description:
a dumb utility that takes KBAGs from an input JSON
and decrypts them with Anya. The input JSON must be
a list of dicts where every member must have "kbag"
field. The output JSON will be same, but with "key".
Thus, you can have arbitrary metadata in the dicts
that will be preserved in the output
```

## TODO
* SEP KBAG decryption - very complex task, as it will require a lot of RE on SEPROM and the way to communicate with it from SecureROM
* Improve build system - for the current one is really bad
* Common offset database - so there won't be a need to duplicate some offsets/values in Astris script and USB handlers configs
* ~~**libanya** dynamic library with Python bindings - to simplify scripting~~ - no, it's easier to actually implement in pure Python

## Credits
* @axi0mX - for the idea of replacing USB handler (used in **ipwndfu**)
* @pimskeks and other people behind **libimobiledevice** project - for **libirecovery**
* @1nsane_dev - for a lot of tests on Cebu

116 changes: 116 additions & 0 deletions python/anya/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
from .exceptions import *

import usb.core
import usb.util

DFU_VENDOR_ID = 0x05AC
DFU_PRODUCT_ID = 0x1227 # can also be 0x1226/0x1228 in case of different SDOM

ANYA_IBFL_FLAG = (1 << 6)
ANYA_USB_TIMEOUT = 100

KBAG_SIZE = 0x30

DFU_DETACH = 0
DFU_DNLOAD = 1
DFU_UPLOAD = 2
DFU_GETSTATUS = 3
DFU_CLR_STATUS = 4
DFU_GETSTATE = 5
DFU_ABORT = 6
ANYA_DECRYPT_KBAG = 7
ANYA_CLEAR_KBAG = 8
ANYA_REBOOT = 9


def decode_kbag(kbag: str) -> bytes:
try:
assert len(kbag) == KBAG_SIZE * 2
return bytearray.fromhex(kbag)
except AssertionError:
raise AnyaValueError("invalid KBAG length")
except ValueError:
raise AnyaValueError("invalid KBAG")

def encode_key(key: bytes, to_upper: bool = False) -> str:
try:
assert len(key) == KBAG_SIZE
key_string = key.hex()
except (AssertionError, ValueError):
raise AnyaValueError("invalid key")

if to_upper:
return key_string.upper()
else:
return key_string

class AnyaDevice:
def __init__(self, vid: int = DFU_VENDOR_ID, pid: int = DFU_PRODUCT_ID, ecid: int = None):
self.vid = vid
self.pid = pid
self.ecid = ecid

def _match(self, dev):
IBFL = "IBFL:"
ECID = "ECID:"

if dev.idVendor == self.vid and dev.idProduct == self.pid:
sn = dev.serial_number
if IBFL in sn:
ibfl_pos = sn.index(IBFL) + len(IBFL)
ibfl_val = int(sn[ibfl_pos:ibfl_pos + 2], 16)

if ibfl_val & ANYA_IBFL_FLAG:
if self.ecid is not None:
if ECID + "%016X" % self.ecid not in sn:
return False

return True

return False

def connect(self):
self._device = usb.core.find(custom_match=self._match)
if not self._device:
raise AnyaNotFound("no Anya devices found")

self.clear_kbag()

print("found: %s" % self._device.serial_number)

def disconnect(self):
usb.util.dispose_resources(self._device)

def clear_kbag(self):
try:
self._device.ctrl_transfer(0x21, ANYA_CLEAR_KBAG, 0, 0, None)
except Exception:
raise AnyaUSBError("failed to clear KBAG")

def send_kbag(self, kbag: bytes):
if len(kbag) != KBAG_SIZE:
raise AnyaValueError("KBAG must be exactly bytes %d in size" % KBAG_SIZE)

try:
assert self._device.ctrl_transfer(0x21, DFU_DNLOAD, 0, 0, kbag) == KBAG_SIZE
except Exception:
raise AnyaUSBError("failed to send KBAG")

def get_key(self) -> bytes:
try:
key = self._device.ctrl_transfer(0xA1, ANYA_DECRYPT_KBAG, 0, 0, KBAG_SIZE)
assert len(key) == KBAG_SIZE
except Exception:
raise AnyaUSBError("failed to decrypt KBAG")

return bytes(key)

def decrypt_kbag(self, kbag: bytes) -> bytes:
self.send_kbag(kbag)
return self.get_key()

def reboot(self):
try:
self._device.ctrl_transfer(0x21, ANYA_REBOOT, 0, 0, None)
except Exception:
pass
11 changes: 11 additions & 0 deletions python/anya/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class AnyaError(Exception):
pass

class AnyaValueError(AnyaError):
pass

class AnyaNotFound(AnyaError):
pass

class AnyaUSBError(AnyaError):
pass
70 changes: 70 additions & 0 deletions python/anyactl
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env python3

import argparse
from time import time
from secrets import token_bytes

from anya import *
from anya.exceptions import *

def main():
parser = argparse.ArgumentParser(description='Decrypt some KBAG or run a benchmark')
parser.add_argument("-k", dest="kbag", help="decrypt a KBAG")
parser.add_argument("-b", dest="count", type=int, help="run benchmark")
parser.add_argument("-e", dest="ecid", type=int, help="ECID to look for")
args = parser.parse_args()

if (not args.kbag and not args.count) or (args.kbag and args.count):
parser.print_help()
exit(-1)

dev = AnyaDevice(ecid=args.ecid)

try:
dev.connect()
except AnyaError as e:
print("failed to connect: %s" % str(e))
exit(-1)

if args.kbag:
try:
decoded = decode_kbag(args.kbag)
except AnyaValueError as e:
print("failed to parse KBAG: %s" % str(e))
exit(-1)

try:
key = dev.decrypt_kbag(decoded)
except AnyaUSBError as e:
print("failed to decrypt KBAG: %s" % str(e))
exit(-1)

print(encode_key(key, True))

elif args.count:
kbags = set()

for i in range(args.count):
kbags.add(bytes(token_bytes(KBAG_SIZE)))

print("decrypting...")

start = time()

for kbag in kbags:
try:
key = dev.decrypt_kbag(kbag)
except AnyaUSBError as e:
print("failed to decrypt KBAG: %s" % str(e))
exit(-1)

end = time()

delta = end - start
average = args.count / delta

print("decrypted %d KBAGs in %.6f seconds, average - %.6f KBAGs/sec" % (args.count, delta, average))


if __name__ == '__main__':
main()
Loading

0 comments on commit aca851c

Please sign in to comment.