├── loader ├── src │ ├── kimg4 │ │ ├── __init__.py │ │ ├── img4.py │ │ └── asn1.py │ ├── iboot_emu │ │ ├── __init__.py │ │ ├── README.md │ │ ├── emulator.py │ │ └── securerom.py │ ├── ibootloader │ │ ├── __init__.py │ │ ├── maps.py │ │ ├── cache.py │ │ ├── loader.py │ │ ├── iboot.py │ │ ├── securerom.py │ │ ├── structs.py │ │ └── iboot_encrypted.py │ ├── disassembler_api │ │ ├── __init__.py │ │ ├── README.md │ │ ├── ida.py │ │ └── api.py │ └── pyaes │ │ ├── util.py │ │ ├── __init__.py │ │ ├── blockfeeder.py │ │ └── aes.py └── ida_ibootloader.py ├── plugin ├── src │ └── ibootplugin │ │ ├── __init__.py │ │ └── sysregs.py └── ida_ibootplugin.py ├── istrap.json ├── README.md ├── CONTRIBUTING.md └── .github └── workflows └── codeql-analysis.yml /loader/src/kimg4/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /loader/src/iboot_emu/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /loader/src/ibootloader/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /plugin/src/ibootplugin/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /loader/src/disassembler_api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /plugin/ida_ibootplugin.py: -------------------------------------------------------------------------------- 1 | from ibootplugin import sysregs 2 | 3 | def PLUGIN_ENTRY(): 4 | return sysregs.PLUGIN_ENTRY() -------------------------------------------------------------------------------- /loader/src/iboot_emu/README.md: -------------------------------------------------------------------------------- 1 | # iBoot Emulation 2 | 3 | (for the sake of resolving function locations, etc) 4 | 5 | Written on (and dependent on) unicorn 6 | 7 | -------------------------------------------------------------------------------- /istrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iBootLoader", 3 | "version": "0.2.1", 4 | 5 | "loader-modules": "loader/src", 6 | "loader": "loader/ida_ibootloader.py", 7 | 8 | "plugin-modules": "plugin/src", 9 | "plugin": "plugin/ida_ibootplugin.py" 10 | } 11 | -------------------------------------------------------------------------------- /loader/src/disassembler_api/README.md: -------------------------------------------------------------------------------- 1 | # disassembler_api 2 | 3 | This is intended to be an abtraction/wrapper around the various APIs for disassemblers 4 | 5 | In The Future™ this project should implement wrappers conforming to API() for other disassemblers, to allow this loader to work in programs beyond IDA. 6 | -------------------------------------------------------------------------------- /loader/src/ibootloader/maps.py: -------------------------------------------------------------------------------- 1 | # 2 | # iBootLoader | ibootloader 3 | # maps.py 4 | # 5 | # Dumps of symbol names for files 6 | # 7 | # This file is part of iBootLoader. iBootLoader is free software that 8 | # is made available under the MIT license. Consult the 9 | # file "LICENSE" that is distributed together with this file 10 | # for the exact licensing terms. 11 | # 12 | # Copyright (c) kat 2021. 13 | # 14 | 15 | symbols = { 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iBoot-IDA 2 | 3 | 32/64 bit SecureROM/iBoot loader for IDA Pro. Supports IDA Pro 7.0+ on all platforms. 4 | 5 | ### Filetype Support 6 | 7 | | File type | Supported | 8 | |--------------------------|-----------| 9 | | arm32/64 SecureROM | ✓ | 10 | | arm32/64 iBoot/iBEC/iBSS | ✓ | 11 | | Encrypted arm32/64 iBoot/iBEC/iBSS | ✓ | 12 | | SEPROM | ✗ | 13 | 14 | --- 15 | 16 | ## Installation 17 | 18 | ``` 19 | python3 -m pip install --upgrade ilstrap unicorn 20 | python3 -m ilstrap.installer --gh hack-different/iBoot-IDA 21 | ``` 22 | 23 | ### Manual Installation 24 | 25 | Steps: 26 | 27 | 0. Install unicorn using the same python installation your IDA install uses. 28 | 1. cd into your IDA directory (where ida64/ida64.exe is located) 29 | 2. Copy `plugins/ida_ibootplugin.py` into `plugins/` 30 | 3. Copy the folders in `plugin/src` into `plugins/` 31 | 4. Copy `loaders/ida_ibootloader.py` into `loaders/` 32 | 3. Copy the folders located in `loader/src/` to `loaders/` 33 | 34 | --- 35 | 36 | ###### Credits: 37 | 38 | Maintainer: https://github.com/cxnder 39 | 40 | AArch Sysregs plugin based on https://github.com/TrungNguyen1909/aarch64-sysreg-ida (based on a plugin by bazad, based on a script by someone else, based on ...) 41 | 42 | this project was originally inspired by https://github.com/argp/iBoot64helper 43 | 44 | -------------------------------------------------------------------------------- /loader/src/ibootloader/cache.py: -------------------------------------------------------------------------------- 1 | # 2 | # iBootLoader | ibootloader 3 | # cache.py 4 | # 5 | # This file handles loading and saving of the cache 6 | # 7 | # This file is part of iBootLoader. iBootLoader is free software that 8 | # is made available under the MIT license. Consult the 9 | # file "LICENSE" that is distributed together with this file 10 | # for the exact licensing terms. 11 | # 12 | # Copyright (c) kat 2021. 13 | # 14 | 15 | import os 16 | import json 17 | 18 | # this is full of potential race conditions and could use a .lock system 19 | 20 | from pathlib import Path 21 | 22 | 23 | class Cache: 24 | def __init__(self): 25 | self.cache = None 26 | self.cache_location = os.path.join(str(Path.home()), '.ibootloader_cache.json') 27 | self.load_cache() 28 | 29 | def update_latest_filename(self, filename): 30 | self.load_cache() 31 | self.cache['latest_file'] = filename 32 | self.save_cache() 33 | 34 | def latest_filename(self): 35 | self.load_cache() 36 | return self.cache['latest_file'] 37 | 38 | def cache_keybag(self, keybag, iv, key): 39 | self.load_cache() 40 | self.cache['keybags'][keybag] = {'iv':iv,'key':key} 41 | self.save_cache() 42 | 43 | def is_keybag_in_cache(self, keybag): 44 | self.load_cache() 45 | return keybag in self.cache['keybags'] 46 | 47 | def keybag_from_cache(self, keybag): 48 | return self.cache['keybags'][keybag] 49 | 50 | def load_cache(self): 51 | if not os.path.exists(self.cache_location): 52 | self.cache = {'latest_file':'', 'keybags':{}} 53 | self.save_cache() 54 | with open(self.cache_location, 'r') as cache_file: 55 | self.cache = json.load(cache_file) 56 | 57 | def save_cache(self): 58 | with open(self.cache_location, 'w') as cache_file: 59 | json.dump(self.cache, cache_file) 60 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Info & Guidelines 2 | 3 | Contributions are always welcome! 4 | 5 | Cleanroom contributions only please. 6 | 7 | You're free to hop right in if you have some features or fixes you want to implement; don't sweat about code guidelines too much. 8 | 9 | This document is mainly here to outline the basic layout and architecture of this project, to make your life easier. 10 | 11 | ## IStrap 12 | 13 | iStrap is an installation framework for IDA Plugins + Loaders. This project uses it for quick installation, and conforms to a layout for it defined in ./istrap.json, which also contains basic project info. 14 | 15 | ## Project Structure 16 | 17 | This project contains both a plugin, and a loader. 18 | 19 | `loader/` - Contains the IDA Loader and python source modules. 20 | 21 | `plugin/` - Contains the IDA Plugin and python source modules. 22 | 23 | `istrap.json` - Contains project layout for the installer and basic info about the project. 24 | 25 | ### Loader 26 | 27 | `loader/ida_ibootloader.py` - This is the file that IDA will see as the "Loader". It's the 'face' IDA sees for our loader. Any actual code is in our modules below, and iStrap handles making sure our imports work. 28 | 29 | #### Modules: 30 | 31 | All of these are located in `loader/src` 32 | 33 | `ibootloader/` - Main Module. Contains the 'important' code for loading SecureROM/iBoot/etc. 34 | 35 | `disassembler_api/` - This is an abstraction of the IDA API. It will in the future be expanded to abstract other disassemblers as well. 36 | 37 | `iboot_emu/` - iBoot/SROM Emulation code dependent on unicorn. Used currently for some symbol resolution. 38 | 39 | `kimg4` - Static clone of the `kimg4` pypi module, used here for decrypting encrypted iboot im4ps 40 | 41 | `pyaes` - AES Decryption dependency of `kimg4` 42 | 43 | ### Plugin 44 | 45 | // Currently, the plugin just implements sysreg labeling code. It will be expanded in the future to do more cool and awesome things. 46 | 47 | `plugin/ida_ibootplugin.py` - Face of the plugin, actual plugin code is located in modules. 48 | 49 | #### Modules 50 | 51 | All located in `plugin/src` 52 | 53 | `ibootplugin` - Main Module 54 | -------------------------------------------------------------------------------- /loader/src/ibootloader/loader.py: -------------------------------------------------------------------------------- 1 | # 2 | # iBootLoader | ibootloader 3 | # loader.py 4 | # 5 | # Base loader invoked by the respective disassembler's "Loader" script 6 | # 7 | # This exists to abstract out everything possible to reduce the amount of changes 8 | # neeed to be made to the scripts in ./loaders specifically. 9 | # 10 | # This file is part of iBootLoader. iBootLoader is free software that 11 | # is made available under the MIT license. Consult the 12 | # file "LICENSE" that is distributed together with this file 13 | # for the exact licensing terms. 14 | # 15 | # Copyright (c) kat 2021. 16 | # 17 | 18 | from disassembler_api.api import Bitness, DisassemblerType 19 | from disassembler_api.ida import IDAAPI 20 | 21 | from .securerom import SecureROMLoader 22 | from .iboot import IBootLoader 23 | from .iboot_encrypted import IBootEncryptedLoader 24 | 25 | def load_file(da_type, fd, neflags, format): 26 | 27 | print("[x] iBootLoader by kat") 28 | print("[x] initializing") 29 | 30 | if da_type == DisassemblerType.IDA: 31 | api = IDAAPI 32 | 33 | print(f'[+] Loaded disassembler module \'{api.api_name()}\'') 34 | 35 | # check if im4p 36 | IM4P_MAGIC = b'\x4D\x34\x50\x16' 37 | fd.seek(0x8) 38 | mag = fd.read(0x4) 39 | if mag == IM4P_MAGIC: 40 | if da_type == DisassemblerType.IDA: 41 | loader = IBootEncryptedLoader(api, fd, 0, "") 42 | loader.load() 43 | else: 44 | fd.seek(0x0) 45 | bn = fd.read(0x4) 46 | fd.seek(0x200) 47 | ver_bin = fd.read(0x30) 48 | 49 | bitness = Bitness.Bitness32 if b'\xea' in bn else Bitness.Bitness64 50 | ver_str = ver_bin.decode() 51 | ver_str = "%s" % (ver_str) 52 | if ver_str[:9] == "SecureROM" or ver_str[:9] == "AVPBooter": 53 | if da_type == DisassemblerType.IDA: 54 | loader = SecureROMLoader(api, fd, bitness, ver_str) 55 | 56 | if ver_str[:5] == "iBoot": 57 | if da_type == DisassemblerType.IDA: 58 | loader = IBootLoader(api, fd, bitness, ver_str) 59 | 60 | print(f'[+] Using loader module \'{loader.name}\'') 61 | 62 | loader.load() -------------------------------------------------------------------------------- /loader/src/pyaes/util.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2014 Richard Moore 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | # Why to_bufferable? 24 | # Python 3 is very different from Python 2.x when it comes to strings of text 25 | # and strings of bytes; in Python 3, strings of bytes do not exist, instead to 26 | # represent arbitrary binary data, we must use the "bytes" object. This method 27 | # ensures the object behaves as we need it to. 28 | 29 | def to_bufferable(binary): 30 | return binary 31 | 32 | def _get_byte(c): 33 | return ord(c) 34 | 35 | try: 36 | xrange 37 | except: 38 | 39 | def to_bufferable(binary): 40 | if isinstance(binary, bytes): 41 | return binary 42 | return bytes(ord(b) for b in binary) 43 | 44 | def _get_byte(c): 45 | return c 46 | 47 | def append_PKCS7_padding(data): 48 | pad = 16 - (len(data) % 16) 49 | return data + to_bufferable(chr(pad) * pad) 50 | 51 | def strip_PKCS7_padding(data): 52 | if len(data) % 16 != 0: 53 | raise ValueError("invalid length") 54 | 55 | pad = _get_byte(data[-1]) 56 | 57 | if pad > 16: 58 | raise ValueError("invalid padding byte") 59 | 60 | return data[:-pad] 61 | -------------------------------------------------------------------------------- /loader/src/pyaes/__init__.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2014 Richard Moore 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | # This is a pure-Python implementation of the AES algorithm and AES common 24 | # modes of operation. 25 | 26 | # See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard 27 | # See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation 28 | 29 | 30 | # Supported key sizes: 31 | # 128-bit 32 | # 192-bit 33 | # 256-bit 34 | 35 | 36 | # Supported modes of operation: 37 | # ECB - Electronic Codebook 38 | # CBC - Cipher-Block Chaining 39 | # CFB - Cipher Feedback 40 | # OFB - Output Feedback 41 | # CTR - Counter 42 | 43 | # See the README.md for API details and general information. 44 | 45 | # Also useful, PyCrypto, a crypto library implemented in C with Python bindings: 46 | # https://www.dlitz.net/software/pycrypto/ 47 | 48 | 49 | VERSION = [1, 3, 0] 50 | 51 | from .aes import AES, AESModeOfOperationCTR, AESModeOfOperationCBC, AESModeOfOperationCFB, AESModeOfOperationECB, AESModeOfOperationOFB, AESModesOfOperation, Counter 52 | from .blockfeeder import decrypt_stream, Decrypter, encrypt_stream, Encrypter 53 | from .blockfeeder import PADDING_NONE, PADDING_DEFAULT 54 | -------------------------------------------------------------------------------- /loader/src/iboot_emu/emulator.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from unicorn import * 4 | from unicorn.arm64_const import * 5 | 6 | 7 | class iEmulator: 8 | 9 | def __init__(self, fp, rom_size): 10 | self.fp = fp 11 | 12 | self.emu: Uc = Uc(UC_ARCH_ARM64, UC_MODE_ARM) # pylance bitches if this isn't set. 13 | # it will get re-set by every emulator superclassing this. 14 | 15 | self.rom_base = 0 16 | self.rom_size = 0 17 | self.stack_base = 0 18 | self.stack_size = 0 19 | self.fp.seek(0) 20 | bytess = b'' 21 | for i in range(rom_size // 0x20): 22 | self.fp.seek(i*0x20) 23 | bits = self.fp.read(0x20) 24 | bytess += bits 25 | self.data = bytearray(bytess) 26 | self.bytes = bytes(self.data) 27 | 28 | self.skip_addrs = [] 29 | 30 | self.emulator_hook_stop_point = 0x0 31 | 32 | def emulator_hook_all(self, uc, address, size, user_data): 33 | pass 34 | 35 | def emu_skip(self, address, instruction_size = 0x4): 36 | if self.emu: 37 | # TODO: maybe a better way to do this? 38 | # couldn't find it in unicorn docs but didn't really look that hard. 39 | self.emu.reg_write(UC_ARM64_REG_PC, address + instruction_size) 40 | 41 | def read(self, location, size): 42 | return bytes(self.data[location:location+size]) 43 | 44 | def read_int(self, location, size): 45 | return int.from_bytes(self.read(location, size), "little") 46 | 47 | def vm_read(self, location, size): 48 | return self.read(location - self.rom_base, size) 49 | 50 | def vm_read_int(self, location, size): 51 | return self.read_int(location - self.rom_base, size) 52 | 53 | def read_cstr_at(self, addr: int, limit: int = 0): 54 | 55 | ea = addr 56 | cnt = 0 57 | 58 | while True: 59 | try: 60 | if self.data[ea] != 0: 61 | cnt += 1 62 | ea += 1 63 | else: 64 | break 65 | 66 | except IndexError as ex: 67 | raise ex 68 | 69 | text = self.read(addr, cnt).decode() 70 | 71 | return text 72 | 73 | def vm_read_cstr_at(self, location: int, limit: int = 0): 74 | self.read_cstr_at(self.rom_base + location, limit) -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '43 7 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'python' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v2 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v1 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 54 | # If this step fails, then you should remove it and run the build manually (see below) 55 | - name: Autobuild 56 | uses: github/codeql-action/autobuild@v1 57 | 58 | # ℹ️ Command-line programs to run using the OS shell. 59 | # 📚 https://git.io/JvXDl 60 | 61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 62 | # and modify them (or add more) to build your code if your project 63 | # uses a compiled language 64 | 65 | #- run: | 66 | # make bootstrap 67 | # make release 68 | 69 | - name: Perform CodeQL Analysis 70 | uses: github/codeql-action/analyze@v1 71 | -------------------------------------------------------------------------------- /loader/src/kimg4/img4.py: -------------------------------------------------------------------------------- 1 | # 2 | # ktool | kimg4 3 | # img4.py 4 | # 5 | # This file contains functions for working with img4 files using pyaes and asn1 6 | # 7 | # This file is part of ktool. ktool is free software that 8 | # is made available under the MIT license. Consult the 9 | # file "LICENSE" that is distributed together with this file 10 | # for the exact licensing terms. 11 | # 12 | # Copyright (c) kat 2021. 13 | # 14 | 15 | import os 16 | import kimg4.asn1 as asn1 17 | import random 18 | import pyaes 19 | import string 20 | 21 | 22 | def asn1_serialize(input_stream, parent=None): 23 | """ 24 | This is a serializer for the output of python-asn1. 25 | It's specifically designed for IMG4 format ASN1/DER/BER format. 26 | 27 | I really dont trust it, it's not very well designed, but it should work well enough. 28 | 29 | :param input_stream: Input asn1 decoder 30 | :param parent: unused element during recursion. would make things smarter. 31 | :return: 32 | """ 33 | vals = [] 34 | while not input_stream.eof(): 35 | tag = input_stream.peek() 36 | if tag.typ == asn1.Types.Primitive: 37 | tag, value = input_stream.read() 38 | vals.append(value) 39 | elif tag.typ == asn1.Types.Constructed: 40 | input_stream.enter() 41 | items = asn1_serialize(input_stream, parent=asn1.Types.Constructed) 42 | input_stream.leave() 43 | vals.append(items) 44 | return vals 45 | 46 | 47 | def get_keybags(fp): 48 | """ 49 | Dump keybags from an IM4P file. 50 | 51 | This function is not smart and only supports standard img4 IM4P format files. 52 | 53 | :param fp: 54 | :return: 55 | """ 56 | return_keybags = [] 57 | im4p_decoder = asn1.Decoder() 58 | im4p_decoder.start(fp.read()) 59 | kbag = asn1_serialize(im4p_decoder)[0][4] 60 | kbag_decoder = asn1.Decoder() 61 | kbag_decoder.start(kbag) 62 | keybags = asn1_serialize(kbag_decoder)[0] 63 | for keybag in keybags: 64 | keybag_concat = keybag[1] + keybag[2] 65 | keybag_hex = keybag_concat.hex().upper() 66 | return_keybags.append(keybag_hex) 67 | return return_keybags 68 | 69 | 70 | def aes_decrypt(fp, key: str, iv: str, out): 71 | """ 72 | Grabs the payload from an im4p and dumps the decrypted output to `out` 73 | 74 | :param fp: Input file pointer 75 | :param key: STRING-HEX repr of AES 256 bit key 76 | :param iv: STRING-HEX repr of AES 64 bit initialization vector 77 | :param out: Output file pointer 78 | :return: 79 | """ 80 | iv = bytes.fromhex(iv) 81 | key = bytes.fromhex(key) 82 | decoder = asn1.Decoder() 83 | decoder.start(fp.read()) 84 | 85 | # location of the raw encrypted cyphertext in the img4, probably? 86 | cipher = asn1_serialize(decoder)[0][3] 87 | 88 | # doing this is lazy 89 | temp_filename = '.temp_' + ''.join(random.choice(string.ascii_lowercase) for i in range(10)) 90 | 91 | with open(temp_filename, 'wb') as outf: 92 | outf.write(cipher) 93 | 94 | # img4 are encrypted with Cipher-Block Chaining mode 95 | mode = pyaes.AESModeOfOperationCBC(key, iv=iv) 96 | 97 | file_in = open(temp_filename, 'rb') 98 | 99 | pyaes.decrypt_stream(mode, file_in, out) 100 | file_in.close() 101 | os.remove(temp_filename) 102 | -------------------------------------------------------------------------------- /loader/ida_ibootloader.py: -------------------------------------------------------------------------------- 1 | # 2 | # iBootLoader | loaders 3 | # ida_ibootloader.py 4 | # 5 | # This is the loader script for IDA Pro 7.5 6 | # 7 | # This file is part of iBootLoader. iBootLoader is free software that 8 | # is made available under the MIT license. Consult the 9 | # file "LICENSE" that is distributed together with this file 10 | # for the exact licensing terms. 11 | # 12 | # Copyright (c) kat 2021. 13 | # 14 | 15 | from ibootloader import loader, cache 16 | from disassembler_api.api import DisassemblerType 17 | import sys 18 | 19 | DEBUG_WITHIN_IDA = 0 20 | 21 | 22 | def dprint(string): 23 | if DEBUG_WITHIN_IDA or len(sys.argv) > 1: 24 | # Launching the script manually with args will trigger debug prints 25 | __builtins__.print(string) 26 | 27 | 28 | print = dprint 29 | 30 | 31 | def accept_file(fd, fname): 32 | flag = 0 33 | # version = 0 34 | ret = 0 35 | 36 | if type(fname) == str: 37 | fd.seek(0x0) 38 | 39 | # check if im4p 40 | IM4P_MAGIC = b'\x4D\x34\x50\x16' 41 | fd.seek(0x8) 42 | mag = fd.read(0x4) 43 | if mag == IM4P_MAGIC: 44 | ret = { 45 | "format": f'iBootLoader: iBoot (Encrypted)', 46 | "processor": "arm" 47 | } 48 | return ret 49 | 50 | fd.seek(0x0) 51 | 52 | bn = fd.read(0x4) 53 | fd.seek(0x200) 54 | ver_bin = fd.read(0x30) 55 | ver_str = "" 56 | 57 | bitness = 'AArch32' if b'\xea' in bn else 'AArch64' 58 | cache.Cache().update_latest_filename(fname) 59 | 60 | try: 61 | ver_str = ver_bin.decode() 62 | ver_str = "%s" % (ver_str) 63 | except: 64 | print("Exception on SecureRom") 65 | if ver_str[:9] == "SecureROM": 66 | ret = { 67 | "format": f'iBootLoader: SecureROM ({bitness})', 68 | "processor": "arm" 69 | } 70 | flag = 1 71 | 72 | return ret 73 | 74 | if ver_str[:9] == "AVPBooter": 75 | ret = { 76 | "format": f'iBootLoader: VMApple SecureROM ({bitness})', 77 | "processor": "arm" 78 | } 79 | flag = 1 80 | 81 | return ret 82 | 83 | fd.seek(0x280) 84 | ver_bin = fd.read(0x20) 85 | 86 | try: 87 | ver_str = ver_bin.decode() 88 | ver_str = "%s" % (ver_str) 89 | except: 90 | print("Exception on iBoot") 91 | 92 | if ver_str[:5] == "iBoot": 93 | version = ver_str[6:] # for later 94 | ret = { 95 | "format": f'iBootLoader: iBoot ({bitness})', 96 | "processor": "arm" 97 | } 98 | flag = 2 99 | 100 | return ret 101 | 102 | # TODO: SEPROM 103 | return 0 104 | fd.seek(0x800) 105 | ver_bin = fd.read(0x16) 106 | print(ver_bin) 107 | try: 108 | ver_str = ver_bin.decode() 109 | ver_str = "%s" % (ver_str) 110 | except: 111 | print("Exception on SEPROM") 112 | if ver_str[:11] == "iBootLoader: AppleSEPROM": 113 | version = ver_str[12:] 114 | ret = { 115 | "format": "SEPROM (AArch32)", 116 | "processor": "arm" 117 | } 118 | flag = 3 119 | 120 | return ret 121 | 122 | return ret 123 | 124 | 125 | def load_file(fd, neflags, format): 126 | loader.load_file(DisassemblerType.IDA, fd, neflags, format) 127 | 128 | return 1 129 | 130 | # EOF 131 | -------------------------------------------------------------------------------- /loader/src/ibootloader/iboot.py: -------------------------------------------------------------------------------- 1 | # 2 | # iBootLoader | ibootloader 3 | # iboot.py 4 | # 5 | # Loader for a decrypted Full iBoot file 6 | # 7 | # This file is part of iBootLoader. iBootLoader is free software that 8 | # is made available under the MIT license. Consult the 9 | # file "LICENSE" that is distributed together with this file 10 | # for the exact licensing terms. 11 | # 12 | # Copyright (c) kat 2021. 13 | # 14 | 15 | from disassembler_api.api import API, DisassemblerFile, Segment, Bitness, ProcessorType, SegmentType, SearchDirection 16 | from .structs import StructLoader 17 | from .maps import symbols 18 | 19 | class IBootLoader: 20 | def __init__(self, api: API, fd, bitness, version_string): 21 | self.name = "iBoot Loader" 22 | self.api: API = api 23 | self.file: DisassemblerFile = self.api.get_disasm_file(fd) 24 | self.bitness = bitness 25 | self.version_string = version_string 26 | 27 | self.segments = [] 28 | self.code_segment: Segment = None 29 | self.ram_segment: Segment = None 30 | self.string_start = 0 31 | 32 | def load(self): 33 | self.configure_segments() 34 | 35 | print("[*] Defining entry point") 36 | self.api.add_entry_point(self.code_segment.start, "start") 37 | 38 | print("[*] Looking for rebase address") 39 | rebase_addr = self.find_and_rebase() 40 | siz = self.code_segment.size 41 | self.code_segment.start = rebase_addr 42 | self.code_segment.end = rebase_addr + siz 43 | 44 | print("[*] Analyzing loaded code") 45 | self.api.analyze(rebase_addr, rebase_addr + self.code_segment.size) 46 | 47 | print("[*] Looking for string start") 48 | self.string_start = self.find_probable_string_start("darwinos-ramdisk", self.code_segment) 49 | if self.string_start == 0: 50 | print(" [-] Did not find.") 51 | 52 | print("[*] Looking for symbols") 53 | self.find_strref_syms() 54 | 55 | def find_strref_syms(self): 56 | panic_location = self.find_faddr_by_strref("double panic in ", self.string_start, -1) 57 | print(f'{panic_location}') 58 | if not panic_location == self.api.bad_address(): 59 | print(f' [+] _panic = {hex(panic_location)}') 60 | self.api.add_name(panic_location, '_panic') 61 | 62 | def find_probable_string_start(self, prologue, segment): 63 | string_addr = self.api.search_text(prologue, segment.end, segment.start, SearchDirection.UP) 64 | if string_addr == self.api.bad_address(): 65 | string_addr = 0 66 | return string_addr 67 | 68 | def find_and_rebase(self): 69 | rebase_ldr_addr = 0x44 70 | if self.bitness == Bitness.Bitness64: 71 | rebase_ldr_addr = 0x8 72 | self.api.analyze(0x0, 0x100) 73 | rebase_addr = int(self.api.get_disasm(rebase_ldr_addr).split('=')[1], 16) 74 | 75 | print(f' [+] {rebase_addr}') 76 | self.api.rebase_to(rebase_addr) 77 | return rebase_addr 78 | 79 | def configure_segments(self): 80 | 81 | base_addr = 0x0 82 | ptr_size = 0x8 83 | sram_len = 0x00120000 84 | 85 | if self.bitness == Bitness.Bitness32: 86 | self.api.set_processor_type(ProcessorType.ARM32) 87 | ptr_size = 0x4 88 | 89 | elif self.bitness == Bitness.Bitness64: 90 | self.api.set_processor_type(ProcessorType.ARM64) 91 | 92 | sram_start_ptr = 0x300 + (7*ptr_size) 93 | 94 | self.code_segment = Segment("iBoot", base_addr, self.file.size, SegmentType.CODE, self.bitness) 95 | self.api.create_segment(self.code_segment) 96 | 97 | self.segments.append(self.code_segment) 98 | 99 | self.api.copy_da_file_to_segment(self.file, self.code_segment, 0) 100 | 101 | def find_faddr_by_strref(self, string, start_address, off): 102 | function_address = self.api.bad_address() 103 | pk_ea = self.api.search_text(string, start_address, 0, SearchDirection.DOWN) 104 | 105 | if pk_ea == self.api.bad_address(): 106 | print(f' [-] {string} not found') 107 | return pk_ea 108 | 109 | if pk_ea < start_address: # String is in func 110 | function_address = self.api.get_function(pk_ea) 111 | 112 | if len([i for i in self.api.xrefs_to(pk_ea)]) == 0: 113 | print(f' [-] no xrefs to {hex(pk_ea)} found') 114 | 115 | #print([i for i in self.api.xrefs_to(pk_ea)]) 116 | 117 | for xref in self.api.xrefs_to(pk_ea): 118 | func = self.api.get_function(xref.frm) 119 | if not func: 120 | #print(f'Bad Function {hex(xref.frm)}') 121 | continue 122 | function_address = func.start_ea 123 | if function_address == self.api.bad_address(): 124 | print(f' [-] {hex(xref)} func not found') 125 | break 126 | 127 | return function_address 128 | 129 | -------------------------------------------------------------------------------- /loader/src/disassembler_api/ida.py: -------------------------------------------------------------------------------- 1 | # 2 | # iBootLoader | disassembler_api 3 | # ida.py 4 | # 5 | # This file implements the abstract base classes of the API for IDA Pro 7.5 6 | # 7 | # This file is part of iBootLoader. iBootLoader is free software that 8 | # is made available under the MIT license. Consult the 9 | # file "LICENSE" that is distributed together with this file 10 | # for the exact licensing terms. 11 | # 12 | # Copyright (c) kat 2021. 13 | # 14 | 15 | import ida_kernwin 16 | import idautils 17 | 18 | from .api import Bitness, ProcessorType, API, DisassemblerFile, Segment, SearchDirection 19 | 20 | import ida_auto 21 | import ida_segment 22 | import ida_entry 23 | import ida_name 24 | import ida_search 25 | import idaapi 26 | import idc 27 | 28 | 29 | class IDAAPI(API): 30 | 31 | @staticmethod 32 | def api_name() -> str: 33 | return "IDA Pro 7.5" 34 | 35 | @staticmethod 36 | def get_function(location): 37 | return idaapi.get_func(location) 38 | 39 | @staticmethod 40 | def bad_address(): 41 | return idaapi.BADADDR 42 | 43 | @staticmethod 44 | def search_text(text, start, end, direction): 45 | direc = ida_search.SEARCH_UP if direction == SearchDirection.UP else ida_search.SEARCH_DOWN 46 | return ida_search.find_text(start, 1, 1, text, direc) 47 | 48 | @staticmethod 49 | def search_binary(binary, start, end, direction): 50 | direc = ida_search.SEARCH_UP if direction == SearchDirection.UP else ida_search.SEARCH_DOWN 51 | return ida_search.find_binary(start, end, binary, 16, direc) 52 | 53 | @staticmethod 54 | def get_disasm_file(fd): 55 | return IDAFile(fd) 56 | 57 | @staticmethod 58 | def disasm_file_from_fp(fp): 59 | return idaapi.loader_input_t.from_fp(fp) 60 | 61 | @staticmethod 62 | def create_segment(segment: Segment): 63 | 64 | segm = idaapi.segment_t() 65 | segm.bitness = segment.seg_bitness 66 | segm.start_ea = segment.start 67 | segm.end_ea = segment.end 68 | 69 | segm_type = segment.seg_type.name.upper() 70 | 71 | idaapi.add_segm_ex(segm, segment.name, segm_type, idaapi.ADDSEG_OR_DIE) 72 | 73 | @staticmethod 74 | def copy_da_file_to_segment(file, segment, file_location): 75 | file.fd.file2base(file_location, segment.start, segment.end, False) 76 | 77 | @staticmethod 78 | def add_entry_point(location: int, name: str) -> None: 79 | ida_entry.add_entry(0, location, name, True, 0) 80 | 81 | @staticmethod 82 | def set_processor_type(type): 83 | if type == ProcessorType.ARM32: 84 | idaapi.set_processor_type('ARM:ARMv7-A', idaapi.SETPROC_LOADER_NON_FATAL) 85 | idaapi.get_inf_structure().lflags |= idaapi.LFLG_PC_FLAT 86 | elif type == ProcessorType.ARM64: 87 | idaapi.set_processor_type("arm", idaapi.SETPROC_LOADER_NON_FATAL) 88 | idaapi.get_inf_structure().lflags |= idaapi.LFLG_64BIT 89 | 90 | @staticmethod 91 | def function_addresses(): 92 | return [i for i in idautils.Functions()] 93 | 94 | @staticmethod 95 | def xrefs_to(function_ea): 96 | return idautils.XrefsTo(function_ea, 0) 97 | 98 | @staticmethod 99 | def get_function_name(location): 100 | try: 101 | name = idc.get_func_name(location) 102 | return name 103 | except: 104 | return "" 105 | 106 | @staticmethod 107 | def add_struct(struct): 108 | struct_id = idc.add_struc(0, struct.name, 0) 109 | for field in struct.fields: 110 | idc.add_struc_member(struct_id, field.name, -1, field.ftype, -1, field.nbytes) 111 | 112 | @staticmethod 113 | def add_entry(location, name, code=True, flags=0): 114 | ida_entry.add_entry(0, location, name, code, flags) 115 | 116 | @staticmethod 117 | def get_disasm(location): 118 | return idc.GetDisasm(location) 119 | 120 | @staticmethod 121 | def rebase_to(delta): 122 | ida_segment.rebase_program(delta, ida_segment.MSF_FIXONCE) 123 | 124 | @staticmethod 125 | def add_name(location, name, flags=0): 126 | ida_name.set_name(location, name, flags) 127 | 128 | @staticmethod 129 | def ask(text): 130 | return ida_kernwin.ask_yn(0, text) == 1 131 | 132 | @staticmethod 133 | def ask_str(text): 134 | return ida_kernwin.ask_str("", 0, text) 135 | 136 | @staticmethod 137 | def analyze(start, end): 138 | ida_auto.plan_and_wait(start, end, True) 139 | 140 | 141 | class IDAFile(DisassemblerFile): 142 | def __init__(self, fd): 143 | self.fd = fd 144 | 145 | fd.seek(0x0) 146 | bn = fd.read(0x4) 147 | self.bitness = Bitness.Bitness32 if b'\xea' in bn else Bitness.Bitness64 148 | fd.seek(0) 149 | fd.seek(0, idaapi.SEEK_END) 150 | size = fd.tell() 151 | fd.seek(0) 152 | self.size = size 153 | 154 | def load_ptr_from(self, location): 155 | self.fd.seek(location) 156 | ptr_size = 0x4 if self.bitness == Bitness.Bitness32 else 0x8 157 | ptr = self.fd.read(ptr_size) 158 | self.fd.seek(0) 159 | return int.from_bytes(ptr, "little") 160 | 161 | -------------------------------------------------------------------------------- /loader/src/iboot_emu/securerom.py: -------------------------------------------------------------------------------- 1 | from .emulator import * 2 | 3 | import os, sys 4 | 5 | 6 | class SecureROM_ARM64(iEmulator): 7 | def __init__(self, fp): 8 | super().__init__(fp, 0x20000) 9 | 10 | self.emu = Uc(UC_ARCH_ARM64, UC_MODE_ARM) 11 | 12 | self.pc_cache = {} 13 | 14 | self.rom_base = 0 15 | self.rom_size = 0x20000 16 | 17 | self.stack_base = 0 18 | self.stack_size = 2 * 1024 * 1024 # 20 mib; this isn't the real value, it just makes shit work 19 | 20 | self.symbols = {} 21 | 22 | def start(self): 23 | self.pre_setup() 24 | self.setup() 25 | 26 | self.emu.hook_add(UC_HOOK_CODE, self.emulator_hook_all, begin=self.rom_base, end=self.rom_base+self.rom_size) 27 | 28 | def launch(self, start, length): 29 | try: 30 | self.emu.emu_start(start, start + length) 31 | except UcError as ex: 32 | self.panic(ex) 33 | 34 | def panic(self, ex): 35 | PC = self.emu.reg_read(UC_ARM64_REG_PC) 36 | PANIC_STR = """ 37 | Emulator (not the rom itself) Panicked with exception string: {} 38 | Program Counter: {} 39 | Thread State: 40 | x0 = {} | x5 = {} | x10 = {} | x15 = {} | x20 = {} 41 | x1 = {} | x6 = {} | x11 = {} | x16 = {} | LR = {} 42 | x2 = {} | x7 = {} | x12 = {} | x17 = {} | 43 | x3 = {} | x8 = {} | x13 = {} | x18 = {} | 44 | x4 = {} | x9 = {} | x14 = {} | x19 = {} | 45 | """.format(ex, 46 | hex(PC), 47 | hex(self.emu.reg_read(UC_ARM64_REG_X0)).ljust(13, ' '), 48 | hex(self.emu.reg_read(UC_ARM64_REG_X5)).ljust(13, ' '), 49 | hex(self.emu.reg_read(UC_ARM64_REG_X10)).ljust(13, ' '), 50 | hex(self.emu.reg_read(UC_ARM64_REG_X15)).ljust(13, ' '), 51 | hex(self.emu.reg_read(UC_ARM64_REG_X20)).ljust(13, ' '), 52 | hex(self.emu.reg_read(UC_ARM64_REG_X1)).ljust(13, ' '), 53 | hex(self.emu.reg_read(UC_ARM64_REG_X6)).ljust(13, ' '), 54 | hex(self.emu.reg_read(UC_ARM64_REG_X11)).ljust(13, ' '), 55 | hex(self.emu.reg_read(UC_ARM64_REG_X16)).ljust(13, ' '), 56 | hex(self.emu.reg_read(UC_ARM64_REG_X30)).ljust(13, ' '), 57 | hex(self.emu.reg_read(UC_ARM64_REG_X2)).ljust(13, ' '), 58 | hex(self.emu.reg_read(UC_ARM64_REG_X7)).ljust(13, ' '), 59 | hex(self.emu.reg_read(UC_ARM64_REG_X12)).ljust(13, ' '), 60 | hex(self.emu.reg_read(UC_ARM64_REG_X17)).ljust(13, ' '), 61 | hex(self.emu.reg_read(UC_ARM64_REG_X3)).ljust(13, ' '), 62 | hex(self.emu.reg_read(UC_ARM64_REG_X8)).ljust(13, ' '), 63 | hex(self.emu.reg_read(UC_ARM64_REG_X13)).ljust(13, ' '), 64 | hex(self.emu.reg_read(UC_ARM64_REG_X18)).ljust(13, ' '), 65 | hex(self.emu.reg_read(UC_ARM64_REG_X4)).ljust(13, ' '), 66 | hex(self.emu.reg_read(UC_ARM64_REG_X9)).ljust(13, ' '), 67 | hex(self.emu.reg_read(UC_ARM64_REG_X14)).ljust(13, ' '), 68 | hex(self.emu.reg_read(UC_ARM64_REG_X19)).ljust(13, ' ') 69 | ) 70 | print(PANIC_STR) 71 | 72 | def resolve(self): 73 | self.platform_init() 74 | 75 | def platform_init(self): 76 | func_end = 0x0 77 | 78 | for i in range(0x200 // 0x4): 79 | dat = self.read(i * 0x4, 0x4) 80 | if dat == b'\x00\x00\x00\x00': 81 | func_end = (i * 0x4) - 0x4 82 | break 83 | 84 | func_end += self.rom_base 85 | self.emulator_hook_stop_point = func_end 86 | 87 | self.launch(self.rom_base, func_end) 88 | __main = self.emu.reg_read(UC_ARM64_REG_X30) 89 | self.symbols['__main'] = __main 90 | 91 | def emulator_hook_all(self, uc, address, size, user_data): 92 | super().emulator_hook_all(uc, address, size, user_data) 93 | 94 | if address not in self.pc_cache: 95 | self.pc_cache[address] = 0 96 | 97 | self.pc_cache[address] += 1 98 | 99 | inst = int.from_bytes(self.vm_read(address, size), "little") 100 | 101 | if self.pc_cache[address] < 5: 102 | # print(f'{hex(address)} == ' + hex(inst)) 103 | pass 104 | 105 | if address in self.skip_addrs: 106 | self.emu_skip(address) 107 | 108 | # skipping msr/mrs instructions causes some infinite loops, 109 | # so we use this shit hack to avoid infinite recursion. 110 | # the cap may need to be raised for certain copy loops. 111 | if self.pc_cache[address] > 1000: 112 | self.emu_skip(address) 113 | 114 | # we need to skip msr insts 115 | if inst & 0xFF000000 == 0xD5000000: 116 | self.emu_skip(address) 117 | 118 | if address == self.emulator_hook_stop_point: 119 | uc.emu_stop() 120 | 121 | def setup(self): 122 | self.emu.mem_map(self.rom_base, len(self.bytes)) 123 | self.emu.mem_map(self.stack_base, self.stack_size) 124 | 125 | self.emu.mem_write(self.rom_base, self.bytes) 126 | 127 | def pre_setup(self): 128 | self.pre_setup_header() 129 | 130 | def pre_setup_header(self): 131 | """ 132 | SecureROM (and iBoot iirc) encodes basic data about the rom starting from 0x200 133 | """ 134 | self.rom_base = self.read_int(0x318, 8) 135 | self.stack_base = self.read_int(0x338, 8) 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /plugin/src/ibootplugin/sysregs.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Copyright (c) 2022 ntrung03 3 | This Source Code Form is subject to the terms of the Mozilla Public 4 | License, v. 2.0. If a copy of the MPL was not distributed with this 5 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | Based on https://gist.github.com/bazad/42054285391c6e0dcd0ede4b5f969ad2 by Brandon Azad 7 | Based on https://gist.github.com/dougallj/7a75a3be1ec69ca550e7c36dc75e0d6f by Dougall J 8 | ''' 9 | 10 | import idaapi 11 | import ida_hexrays 12 | import os 13 | import json 14 | 15 | from .sysregs_dat import * 16 | 17 | ITYPE_MRS = idaapi.CUSTOM_INSN_ITYPE + 13 18 | ITYPE_MSR = ITYPE_MRS + 1 19 | ITYPE_SYS = ITYPE_MSR + 1 20 | 21 | # AArch64 PSTATE accesses (op0 = 0b00, CRn = 0b0100). 22 | PSTATE_ACCESS = { 23 | 0b011 : 'UAO', 24 | 0b100 : 'PAN', 25 | 0b101 : 'SPSel', 26 | 0b110 : 'DAIFSet', 27 | 0b111 : 'DAIFClr', 28 | } 29 | 30 | 31 | def pstate_insn(op2): 32 | pstate = PSTATE_ACCESS.get(op2) 33 | if pstate: 34 | return pstate 35 | else: 36 | return ('#{:b}'.format(op2)) 37 | 38 | def sysreg_insn(op0, op1, CRn, CRm, op2): 39 | sr = 'S{}_{}_c{}_c{}_{}'.format(op0, op1, CRn, CRm, op2) 40 | #print(sr) 41 | reg = SYSTEM_REGISTERS.get(sr) 42 | if reg: 43 | name = reg[0] 44 | description = reg[1] 45 | return name 46 | else: 47 | return sr 48 | 49 | def process_msr(insn): 50 | assert(insn & 0xFFC00000 == 0xD5000000) 51 | L = (insn >> 21) & 0x1 52 | op0 = (insn >> 19) & 0x3 53 | op1 = (insn >> 16) & 0x7 54 | CRn = (insn >> 12) & 0xf 55 | CRm = (insn >> 8) & 0xf 56 | op2 = (insn >> 5) & 0x7 57 | Rt = (insn >> 0) & 0x1f 58 | if L == 0b0 and op0 == 0b00 and CRn == 0b0100 and Rt == 0b11111: 59 | return pstate_insn(op2) 60 | elif op0 != 0b00: 61 | return sysreg_insn(op0, op1, CRn, CRm, op2) 62 | else: 63 | return '{:08x}'.format(insn) 64 | 65 | 66 | class Aarch64SysRegHook(idaapi.IDP_Hooks): 67 | CUSTOM_INSTRUCTIONS = {idaapi.ARM_mrs, idaapi.ARM_msr, idaapi.ARM_sys} 68 | INDENT = 16 69 | 70 | def ev_out_operand(self, outctx, op): 71 | if outctx.insn.itype in self.CUSTOM_INSTRUCTIONS: 72 | insn = outctx.insn 73 | #print("ev_out_operand") 74 | if op.n < 2: 75 | if outctx.insn.itype == idaapi.ARM_mrs: 76 | if op.n == 1: 77 | outctx.out_colored_register_line(sysreg_insn(3, insn.ops[1].value, insn.ops[2].reg, insn.ops[3].reg, insn.ops[4].value)) 78 | else: 79 | return 0 80 | elif outctx.insn.itype == idaapi.ARM_msr: 81 | if op.n == 0: 82 | outctx.out_colored_register_line(process_msr(idaapi.get_dword(outctx.insn.ea))) 83 | else: 84 | if insn.ops[2].type == idaapi.o_void: 85 | return 0 86 | else: 87 | insn.itype = ITYPE_MSR 88 | outctx.out_one_operand(4) 89 | insn.itype = idaapi.ARM_msr 90 | elif outctx.insn.itype == idaapi.ARM_sys: 91 | if op.n == 0: 92 | outctx.out_colored_register_line(sysreg_insn(1, insn.ops[0].value, insn.ops[1].reg, insn.ops[2].reg, insn.ops[3].value).split()[1].strip()) 93 | else: 94 | insn.itype = ITYPE_SYS 95 | outctx.out_one_operand(4) 96 | insn.itype = idaapi.ARM_sys 97 | ''' 98 | for i in range(0, 6): 99 | op = outctx.insn.ops[i] 100 | print(F'Op{op.n}: type: {op.type} reg: {op.reg} value: {op.value}\n') 101 | ''' 102 | return 1 103 | return 0 104 | def ev_out_mnem(self, outctx): 105 | if outctx.insn.itype == idaapi.ARM_sys: 106 | #print("ev_out_insn") 107 | insn = outctx.insn 108 | outctx.out_custom_mnem(sysreg_insn(1, insn.ops[0].value, insn.ops[1].reg, insn.ops[2].reg, insn.ops[3].value).split()[0].strip(), self.INDENT) 109 | return 1 110 | return 0 111 | 112 | class Aarch64SysRegPlugin(idaapi.plugin_t): 113 | flags = idaapi.PLUGIN_PROC | idaapi.PLUGIN_HIDE 114 | comment = "Aarch64 Apple SysReg extension" 115 | wanted_hotkey = "" 116 | help = "Runs transparently" 117 | wanted_name = "Aarch64 SysReg" 118 | hook = None 119 | enabled = 1 120 | 121 | def init(self): 122 | if idaapi.ph_get_id() != idaapi.PLFM_ARM or idaapi.BADADDR <= 0xFFFFFFFF: 123 | return idaapi.PLUGIN_SKIP 124 | if not ida_hexrays.init_hexrays_plugin(): 125 | print(("[-] {0} : no decompiler available, skipping".format(self.wanted_name))) 126 | return idaapi.PLUGIN_SKIP 127 | print("%s init"%self.comment) 128 | 129 | d = json.loads(APPLE_REGS_JSON) 130 | 131 | for r in d: 132 | sr = 'S{}_{}_c{}_c{}_{}'.format(*r['enc']) 133 | SYSTEM_REGISTERS[sr] = (r['name'], r['fullname']) 134 | self.hook = Aarch64SysRegHook() 135 | self.hook.hook() 136 | return idaapi.PLUGIN_KEEP 137 | 138 | def run(): 139 | pass 140 | 141 | def term(self): 142 | if self.hook is not None: 143 | self.hook.unhook() 144 | print("%s unloaded"%self.comment) 145 | 146 | def PLUGIN_ENTRY(): 147 | return Aarch64SysRegPlugin() -------------------------------------------------------------------------------- /loader/src/ibootloader/securerom.py: -------------------------------------------------------------------------------- 1 | # 2 | # iBootLoader | ibootloader 3 | # securerom.py 4 | # 5 | # Loader for SecureROM dumps 6 | # 7 | # This file is part of iBootLoader. iBootLoader is free software that 8 | # is made available under the MIT license. Consult the 9 | # file "LICENSE" that is distributed together with this file 10 | # for the exact licensing terms. 11 | # 12 | # Copyright (c) kat 2021. 13 | # 14 | 15 | from disassembler_api.api import API, DisassemblerFile, Segment, Bitness, ProcessorType, SegmentType, SearchDirection 16 | from iboot_emu.securerom import SecureROM_ARM64 17 | from .structs import StructLoader 18 | from .maps import symbols 19 | 20 | 21 | class SecureROMLoader: 22 | def __init__(self, api: API, fd, bitness, version_string): 23 | self.name = "SecureROM Loader" 24 | self.api: API = api 25 | self.fd = fd 26 | self.file: DisassemblerFile = self.api.get_disasm_file(fd) 27 | self.bitness = bitness 28 | self.version_string = version_string 29 | 30 | self.bad_addr = self.api.bad_address() 31 | 32 | self.segments = [] 33 | self.code_segment: Segment = None 34 | self.ram_segment: Segment = None 35 | self.string_start = 0 36 | 37 | def load(self): 38 | self.configure_segments() 39 | 40 | print("[*] Defining entry point") 41 | self.api.add_entry_point(self.code_segment.start, "_platform_start") 42 | 43 | print("[*] Analyzing loaded code") 44 | self.api.analyze(self.code_segment.start, self.code_segment.end) 45 | 46 | print("[*] Finding string start") 47 | self.string_start = self.find_probable_string_start("6E 6F 72 30 00", self.code_segment) 48 | 49 | print("[*] Looking for function xrefs to strings") 50 | try: 51 | self.find_stringref_funcs() 52 | except IndexError: 53 | print("[-] Failed") 54 | pass 55 | 56 | print("[*] Launching Emulator") 57 | try: 58 | srom_emulator = SecureROM_ARM64(self.fd) 59 | srom_emulator.start() 60 | srom_emulator.resolve() 61 | for symbol, loc in srom_emulator.symbols.items(): 62 | print(f' [+] {symbol} = {hex(loc)}') 63 | self.api.add_name(loc, symbol) 64 | except Exception as ex: 65 | print(f'[-] Emulation failed.') 66 | 67 | print("[*] Loading custom struct types") 68 | StructLoader(self.api) 69 | 70 | def find_xrefs_from_start(self): 71 | for function_ea in self.api.function_addresses(): 72 | # For each of the incoming references 73 | for ref_ea in self.api.xrefs_to(function_ea): 74 | # Get the name of the referring function 75 | caller_name = self.api.get_function_name(ref_ea.frm) 76 | if caller_name == 'start': 77 | print(f' [+] _platform_start = {hex(function_ea)}') 78 | self.api.add_name(function_ea, "_platform_start") 79 | break 80 | 81 | def find_stringref_funcs(self): 82 | 83 | panic_location = self.find_faddr_by_strref("double panic in ", self.string_start)[-1].start_ea 84 | if not panic_location == self.api.bad_address(): 85 | print(f' [+] _panic = {hex(panic_location)}') 86 | self.api.add_name(panic_location, '_panic') 87 | 88 | task_location = self.find_faddr_by_strref("idle task", self.string_start)[-1].start_ea 89 | if not task_location == self.api.bad_address(): 90 | print(f' [+] _sys_setup_default_environment = {hex(task_location)}') 91 | self.api.add_name(task_location, '_sys_setup_default_environment') 92 | 93 | def configure_segments(self): 94 | base_addr = 0x0 95 | ptr_size = 0x8 96 | sram_len = 0x00120000 97 | 98 | if self.bitness == Bitness.Bitness32: 99 | self.api.set_processor_type(ProcessorType.ARM32) 100 | ptr_size = 0x4 101 | 102 | elif self.bitness == Bitness.Bitness64: 103 | self.api.set_processor_type(ProcessorType.ARM64) 104 | base_addr = 0x100000000 105 | 106 | sram_start_ptr = 0x300 + (7*ptr_size) 107 | 108 | self.code_segment = Segment("SecureROM", base_addr, self.file.size, SegmentType.CODE, self.bitness) 109 | self.api.create_segment(self.code_segment) 110 | 111 | sram_start = self.file.load_ptr_from(sram_start_ptr) 112 | 113 | self.ram_segment = Segment("SRAM", sram_start, sram_len, SegmentType.STACK, self.bitness) 114 | self.api.create_segment(self.ram_segment) 115 | self.api.add_name(sram_start_ptr + base_addr, "sram_start") 116 | 117 | self.segments.append(self.code_segment) 118 | 119 | self.api.copy_da_file_to_segment(self.file, self.code_segment, 0) 120 | 121 | def find_probable_string_start(self, prologue, segment): 122 | string_addr = self.api.search_binary(prologue, segment.end, segment.start, SearchDirection.UP) 123 | if string_addr == self.api.bad_address(): 124 | string_addr = 0 125 | return string_addr 126 | 127 | def find_faddr_by_strref(self, string, start_address): 128 | # TODO: this function has hard coding for IDA stuff. 129 | 130 | function_address = self.api.bad_address() 131 | pk_ea = self.api.search_text(string, start_address, 0, SearchDirection.DOWN) 132 | 133 | functions = [] 134 | 135 | if pk_ea == self.api.bad_address(): 136 | return [] 137 | 138 | if pk_ea < start_address: # String is in func 139 | function_address = self.api.get_function(pk_ea) 140 | 141 | for xref in self.api.xrefs_to(pk_ea): 142 | try: 143 | func = self.api.get_function(xref.frm) 144 | except: 145 | continue 146 | if not func: 147 | continue 148 | functions.append(func) 149 | function_address = func.start_ea 150 | 151 | if pk_ea == self.api.bad_address(): 152 | return [] 153 | 154 | return functions 155 | -------------------------------------------------------------------------------- /loader/src/disassembler_api/api.py: -------------------------------------------------------------------------------- 1 | # 2 | # iBootLoader | disassembler_api 3 | # api.py 4 | # 5 | # This specifies basic API abstract classes to be implemented for the specific disassembler APIs 6 | # 7 | # This file is part of iBootLoader. iBootLoader is free software that 8 | # is made available under the MIT license. Consult the 9 | # file "LICENSE" that is distributed together with this file 10 | # for the exact licensing terms. 11 | # 12 | # Copyright (c) kat 2021. 13 | # 14 | 15 | from enum import Enum, IntEnum 16 | from abc import ABC, abstractmethod, abstractstaticmethod, abstractproperty 17 | 18 | 19 | class Bitness(IntEnum): 20 | Bitness32 = 1 21 | Bitness64 = 2 22 | 23 | 24 | class SearchDirection(IntEnum): 25 | UP = 0 26 | DOWN = 1 27 | 28 | 29 | class Field: 30 | def __init__(self, name, ftype, nbytes): 31 | self.name = name 32 | self.ftype = ftype 33 | self.nbytes = nbytes 34 | 35 | 36 | class Struct: 37 | def __init__(self, name, fields): 38 | self.name = name 39 | self.fields = fields 40 | 41 | 42 | class SegmentType(Enum): 43 | CODE = 0 44 | STACK = 1 45 | 46 | 47 | class ProcessorType(Enum): 48 | ARM32 = 0 49 | ARM64 = 1 50 | 51 | 52 | class DisassemblerType(Enum): 53 | IDA = 0 54 | 55 | 56 | class Segment: 57 | def __init__(self, name, start, size, seg_type: SegmentType, seg_bitness: Bitness): 58 | self.name = name 59 | self.start = start 60 | self.end = start + size 61 | self.size = size 62 | self.seg_type = seg_type 63 | self.seg_bitness = seg_bitness 64 | 65 | 66 | class DisassemblerFile(ABC): 67 | @abstractmethod 68 | def __init__(self, fd): 69 | """ 70 | Create a new DisassemblerFile 71 | 72 | :param fd: File-like object 73 | """ 74 | 75 | @abstractmethod 76 | def load_ptr_from(self, location) -> int: 77 | """ 78 | Load a pointer of platform_pointer_size size 79 | 80 | :param location: Location to load pointer from 81 | :return: 82 | """ 83 | 84 | 85 | class API(ABC): 86 | 87 | @staticmethod 88 | @abstractmethod 89 | def api_name() -> str: 90 | """ 91 | 92 | :return: 93 | """ 94 | 95 | @staticmethod 96 | @abstractmethod 97 | def bad_address() -> int: 98 | """Returns the BAD_ADDRESS value for the disassembler 99 | 100 | :return: Bad Address 101 | """ 102 | 103 | @staticmethod 104 | @abstractmethod 105 | def get_function(location: int) -> int: 106 | """Get the address of the function start `location` is located in. 107 | 108 | :return: Function start 109 | """ 110 | 111 | @staticmethod 112 | @abstractmethod 113 | def get_disasm_file(fd) -> DisassemblerFile: 114 | """Generates a Disassembler Subclassed file for the respective disassembler from a file-like object. 115 | 116 | :param fd: 117 | :return: 118 | """ 119 | 120 | @staticmethod 121 | @abstractmethod 122 | def disasm_file_from_fp(fp): 123 | """eh 124 | 125 | :param fd: 126 | :return: 127 | """ 128 | 129 | @staticmethod 130 | @abstractmethod 131 | def set_processor_type(type: ProcessorType) -> None: 132 | """Set the processor type for the file 133 | 134 | :param type: Processor Type 135 | """ 136 | 137 | @staticmethod 138 | @abstractmethod 139 | def create_segment(segment: Segment) -> None: 140 | """Create a segment 141 | 142 | :param segment: segment to add 143 | :return: 144 | """ 145 | 146 | @staticmethod 147 | @abstractmethod 148 | def function_addresses() -> list: 149 | """List of function addresses in the program 150 | 151 | :return: 152 | """ 153 | 154 | @staticmethod 155 | @abstractmethod 156 | def get_disasm(location: int) -> str: 157 | """Get a string containing disassembly at the requested address 158 | 159 | :param location: 160 | :return: 161 | """ 162 | 163 | @staticmethod 164 | @abstractmethod 165 | def rebase_to(delta: int) -> None: 166 | """Rebase the entire program by delta bytes 167 | 168 | :param delta: 169 | :return: 170 | """ 171 | 172 | @staticmethod 173 | @abstractmethod 174 | def xrefs_to(function_ea: int) -> list: 175 | """List of cross-references to function_ea 176 | 177 | :return: 178 | """ 179 | 180 | @staticmethod 181 | @abstractmethod 182 | def get_function_name(location: int) -> None: 183 | """Get the name (if it exists) of the function at location 184 | 185 | :return: 186 | """ 187 | 188 | @staticmethod 189 | @abstractmethod 190 | def copy_da_file_to_segment(file: DisassemblerFile, segment: Segment, file_location: int) -> None: 191 | """ 192 | Copy a Disassembler File's bytes into a segment; Load it 193 | 194 | :param file_location: 195 | :param file: 196 | :param segment: 197 | :return: 198 | """ 199 | 200 | @staticmethod 201 | @abstractmethod 202 | def add_struct(struct: Struct) -> None: 203 | """Add a struct to the table 204 | 205 | :param struct: Struct to add 206 | """ 207 | 208 | @staticmethod 209 | @abstractmethod 210 | def add_name(location: int, name: str) -> None: 211 | """ 212 | Add a name for an address 213 | 214 | :param location: Address 215 | :param name: Name to add 216 | """ 217 | 218 | @staticmethod 219 | @abstractmethod 220 | def add_entry_point(location: int, name: str) -> None: 221 | """ 222 | Add an entry point in the program 223 | 224 | :param location: Address 225 | :param name: Name of the entry point 226 | :return: 227 | """ 228 | 229 | @staticmethod 230 | @abstractmethod 231 | def search_binary(binary: str, start: int, end: int, direction: SearchDirection) -> int: 232 | """Perform a binary search 233 | 234 | :param binary: 235 | :param start: 236 | :param end: 237 | :param direction: 238 | :return: Location 239 | """ 240 | 241 | @staticmethod 242 | @abstractmethod 243 | def search_text(text: str, start: int, end: int, direction: SearchDirection) -> int: 244 | """Perform a text search 245 | 246 | :param text: 247 | :param start: 248 | :param end: 249 | :param direction: 250 | :return: 251 | """ 252 | 253 | @staticmethod 254 | @abstractmethod 255 | def ask(text) -> bool: 256 | """ 257 | Ask the user a question and get a yes/no answer 258 | 259 | :param text: Question 260 | :return: Boolean indicating yes(True) or no(False) 261 | """ 262 | 263 | @staticmethod 264 | @abstractmethod 265 | def ask_str(text) -> str: 266 | """ 267 | Ask the user for string input 268 | 269 | :param text: Question 270 | :return: str input 271 | """ 272 | 273 | @staticmethod 274 | @abstractmethod 275 | def analyze(start: int, end: int) -> None: 276 | """ 277 | in IDEs that supoort/require it, Auto-Analyze a region of code 278 | 279 | wait to return until finished 280 | 281 | :param start: Starting Address 282 | :param end: Ending address 283 | :return: Returns nothing once finished 284 | """ 285 | -------------------------------------------------------------------------------- /loader/src/ibootloader/structs.py: -------------------------------------------------------------------------------- 1 | # 2 | # iBootLoader | ibootloader 3 | # structs.py 4 | # 5 | # This loads struct definitions i've created into IDA 6 | # 7 | # This file is part of iBootLoader. iBootLoader is free software that 8 | # is made available under the MIT license. Consult the 9 | # file "LICENSE" that is distributed together with this file 10 | # for the exact licensing terms. 11 | # 12 | # Copyright (c) kat 2021. 13 | # 14 | 15 | import idaapi 16 | 17 | from disassembler_api.api import API, Struct, Field 18 | 19 | 20 | class StructLoader: 21 | def __init__(self, api: API): 22 | 23 | struct = Struct("syscfg_wpcl", []) 24 | struct.fields.append(Field('version', idaapi.FF_DWORD, 4)) 25 | struct.fields.append(Field('red', idaapi.FF_DWORD, 4)) 26 | struct.fields.append(Field('green', idaapi.FF_DWORD, 4)) 27 | struct.fields.append(Field('blue', idaapi.FF_DWORD, 4)) 28 | api.add_struct(struct) 29 | 30 | struct = Struct("usb_device_request", []) 31 | struct.fields.append(Field('bmRequestType', idaapi.FF_BYTE, 1)) 32 | struct.fields.append(Field('bRequest', idaapi.FF_BYTE, 1)) 33 | struct.fields.append(Field('wValue', idaapi.FF_WORD, 2)) 34 | struct.fields.append(Field('wIndex', idaapi.FF_WORD, 2)) 35 | struct.fields.append(Field('wLength', idaapi.FF_WORD, 2)) 36 | api.add_struct(struct) 37 | 38 | struct = Struct("usb_device_descriptor", []) 39 | struct.fields.append(Field('bLength', idaapi.FF_BYTE, 1)) 40 | struct.fields.append(Field('bDescriptorType', idaapi.FF_BYTE, 1)) 41 | struct.fields.append(Field('bcdUSB', idaapi.FF_WORD, 2)) 42 | struct.fields.append(Field('bDeviceClass', idaapi.FF_BYTE, 1)) 43 | struct.fields.append(Field('bDeviceSubClass', idaapi.FF_BYTE, 1)) 44 | struct.fields.append(Field('bDeviceProtcol', idaapi.FF_BYTE, 1)) 45 | struct.fields.append(Field('bDeviceMaxPacketSize0', idaapi.FF_BYTE, 1)) 46 | struct.fields.append(Field('idVendor', idaapi.FF_WORD, 2)) 47 | struct.fields.append(Field('idProduct', idaapi.FF_WORD, 2)) 48 | struct.fields.append(Field('bcdDevice', idaapi.FF_WORD, 2)) 49 | struct.fields.append(Field('iManufacturer', idaapi.FF_BYTE, 1)) 50 | struct.fields.append(Field('iProduct', idaapi.FF_BYTE, 1)) 51 | struct.fields.append(Field('iSerialNumber', idaapi.FF_BYTE, 1)) 52 | struct.fields.append(Field('bNumConfigurations', idaapi.FF_BYTE, 1)) 53 | api.add_struct(struct) 54 | 55 | struct = Struct("usb_configuration_descriptor", []) 56 | struct.fields.append(Field('bLength', idaapi.FF_BYTE, 1)) 57 | struct.fields.append(Field('bDescriptorType', idaapi.FF_BYTE, 1)) 58 | struct.fields.append(Field('wTotalLength', idaapi.FF_WORD, 2)) 59 | struct.fields.append(Field('bNumInterfaces', idaapi.FF_BYTE, 1)) 60 | struct.fields.append(Field('bConfigurationValue', idaapi.FF_BYTE, 1)) 61 | struct.fields.append(Field('iConfiguration', idaapi.FF_BYTE, 1)) 62 | struct.fields.append(Field('bmAttributes', idaapi.FF_BYTE, 1)) 63 | struct.fields.append(Field('bMaxPower', idaapi.FF_BYTE, 1)) 64 | api.add_struct(struct) 65 | 66 | struct = Struct("usb_interface_descriptor", []) 67 | struct.fields.append(Field('bLength', idaapi.FF_BYTE, 1)) 68 | struct.fields.append(Field('bDescriptorType', idaapi.FF_BYTE, 1)) 69 | struct.fields.append(Field('bInterfaceNumber', idaapi.FF_BYTE, 1)) 70 | struct.fields.append(Field('bAlternateSetting', idaapi.FF_BYTE, 1)) 71 | struct.fields.append(Field('bNumEndpoints', idaapi.FF_BYTE, 1)) 72 | struct.fields.append(Field('bInterfaceClass', idaapi.FF_BYTE, 1)) 73 | struct.fields.append(Field('bInterfaceSubClass', idaapi.FF_BYTE, 1)) 74 | struct.fields.append(Field('bInterfaceProtocol', idaapi.FF_BYTE, 1)) 75 | struct.fields.append(Field('iInterface', idaapi.FF_BYTE, 1)) 76 | api.add_struct(struct) 77 | 78 | struct = Struct("usb_endpoint_descriptor", []) 79 | struct.fields.append(Field('bLength', idaapi.FF_BYTE, 1)) 80 | struct.fields.append(Field('bDescriptorType', idaapi.FF_BYTE, 1)) 81 | struct.fields.append(Field('bEndpointAddress', idaapi.FF_BYTE, 1)) 82 | struct.fields.append(Field('bmAttributes', idaapi.FF_BYTE, 1)) 83 | struct.fields.append(Field('wMaxPacketSize', idaapi.FF_WORD, 2)) 84 | struct.fields.append(Field('bInterval', idaapi.FF_BYTE, 1)) 85 | api.add_struct(struct) 86 | 87 | struct = Struct("usb_string_descriptor", []) 88 | struct.fields.append(Field('bLength', idaapi.FF_BYTE, 1)) 89 | struct.fields.append(Field('bDescriptorType', idaapi.FF_BYTE, 1)) 90 | struct.fields.append(Field('wData', idaapi.FF_BYTE, 2)) 91 | api.add_struct(struct) 92 | 93 | struct = Struct("usb_device_qualifier_descriptor", []) 94 | struct.fields.append(Field('bLength', idaapi.FF_BYTE, 1)) 95 | struct.fields.append(Field('bDescriptorType', idaapi.FF_BYTE, 1)) 96 | struct.fields.append(Field('bcdUSB', idaapi.FF_WORD, 2)) 97 | struct.fields.append(Field('bDeviceClass', idaapi.FF_BYTE, 1)) 98 | struct.fields.append(Field('bDeviceSubClass', idaapi.FF_BYTE, 1)) 99 | struct.fields.append(Field('bDeviceProtocol', idaapi.FF_BYTE, 1)) 100 | struct.fields.append(Field('bMaxPacketSize0', idaapi.FF_BYTE, 1)) 101 | struct.fields.append(Field('bNumConfigurations', idaapi.FF_BYTE, 1)) 102 | struct.fields.append(Field('bReserved', idaapi.FF_BYTE, 1)) 103 | api.add_struct(struct) 104 | 105 | usb_interface_instance = Struct("usb_interface_instance", []) 106 | usb_interface_instance.fields.append(Field('interface_request_handler', idaapi.FF_DWORD, 4)) 107 | usb_interface_instance.fields.append(Field('non_setup_data_phase_finished_callback', idaapi.FF_DWORD, 4)) 108 | usb_interface_instance.fields.append(Field('activate_interface', idaapi.FF_DWORD, 4)) 109 | usb_interface_instance.fields.append(Field('bus_reset_handler', idaapi.FF_DWORD, 4)) 110 | usb_interface_instance.fields.append(Field('get_interface_handler', idaapi.FF_DWORD, 4)) 111 | usb_interface_instance.fields.append(Field('set_interface_handler', idaapi.FF_DWORD, 4)) 112 | api.add_struct(usb_interface_instance) 113 | 114 | usb_device_io_request = Struct("usb_device_io_request", []) 115 | usb_device_io_request.fields.append(Field('endpoint', idaapi.FF_DWORD, 4)) 116 | usb_device_io_request.fields.append(Field('io_buffer', idaapi.FF_DWORD, 4)) 117 | usb_device_io_request.fields.append(Field('status', idaapi.FF_DWORD, 4)) 118 | usb_device_io_request.fields.append(Field('io_length', idaapi.FF_DWORD, 4)) 119 | usb_device_io_request.fields.append(Field('return_count', idaapi.FF_DWORD, 4)) 120 | usb_device_io_request.fields.append(Field('callback', idaapi.FF_DWORD, 4)) 121 | usb_device_io_request.fields.append(Field('next', idaapi.FF_DWORD, 4)) 122 | api.add_struct(usb_device_io_request) 123 | 124 | struct = Struct("usb_endpoint_instance", []) 125 | struct.fields.append(Field('endpoint_address', idaapi.FF_DWORD, 4)) 126 | struct.fields.append(Field('max_packet_size', idaapi.FF_DWORD, 4)) 127 | struct.fields.append(Field('attributes', idaapi.FF_DWORD, 4)) 128 | struct.fields.append(Field('bInterval', idaapi.FF_DWORD, 4)) 129 | struct.fields.append(Field('transfer_size', idaapi.FF_DWORD, 4)) 130 | struct.fields.append(Field('packet_count', idaapi.FF_DWORD, 4)) 131 | struct.fields.append(Field('next_ep', idaapi.FF_DWORD, 4)) 132 | struct.fields.append(Field('io_head', idaapi.FF_DWORD, 4)) 133 | struct.fields.append(Field('io_tail', idaapi.FF_DWORD, 4)) 134 | struct.fields.append(Field('tx_fifo_number', idaapi.FF_DWORD, 4)) 135 | api.add_struct(struct) 136 | 137 | struct = Struct("listentry", []) 138 | struct.fields.append(Field('cb_id', idaapi.FF_DWORD, 4)) 139 | struct.fields.append(Field('port', idaapi.FF_DWORD, 4)) 140 | struct.fields.append(Field('callback', idaapi.FF_DWORD, 4)) 141 | struct.fields.append(Field('next', idaapi.FF_DWORD, 4)) 142 | api.add_struct(struct) 143 | -------------------------------------------------------------------------------- /loader/src/ibootloader/iboot_encrypted.py: -------------------------------------------------------------------------------- 1 | # 2 | # iBootLoader | ibootloader 3 | # iboot_encrypted.py 4 | # 5 | # Loader for encrypted im4ps 6 | # 7 | # This file is part of iBootLoader. iBootLoader is free software that 8 | # is made available under the MIT license. Consult the 9 | # file "LICENSE" that is distributed together with this file 10 | # for the exact licensing terms. 11 | # 12 | # Copyright (c) kat 2021. 13 | # 14 | 15 | import random, string, os 16 | 17 | from disassembler_api.api import API, DisassemblerFile, Segment, Bitness, ProcessorType, SegmentType, SearchDirection 18 | from .structs import StructLoader 19 | from .maps import symbols 20 | from kimg4.img4 import get_keybags, aes_decrypt 21 | 22 | from .cache import Cache 23 | 24 | 25 | class IBootEncryptedLoader: 26 | def __init__(self, api: API, fd, bitness, version_string): 27 | self.name = "iBoot Encrypted Loader" 28 | self.api: API = api 29 | self.file: DisassemblerFile = self.api.get_disasm_file(fd) 30 | self.cache = Cache() 31 | 32 | self.deleteme = [] 33 | 34 | self.bitness = bitness 35 | self.version_string = version_string 36 | 37 | self.segments = [] 38 | self.code_segment: Segment = None 39 | self.ram_segment: Segment = None 40 | self.string_start = 0 41 | 42 | def decrypt(self): 43 | # jesus christ, ok 44 | temp_filename = '.temp_' + ''.join(random.choice(string.ascii_lowercase) for i in range(10)) 45 | temp_filename_dec = '.temp_' + ''.join(random.choice(string.ascii_lowercase) for i in range(10)) 46 | 47 | # grab the ida file pointer 48 | idafd = self.file.fd 49 | # make sure we're at 0 50 | idafd.seek(0) 51 | 52 | idafd_size = idafd.size() 53 | # load the entirety of file contents into a variable 54 | im4p_bytes = idafd.read(idafd_size) 55 | 56 | # write the bytes to a file so the img4 code can load it 57 | with open(temp_filename, 'wb') as temp_undec: 58 | temp_undec.write(im4p_bytes) 59 | bags = None 60 | # now send the file pointer for that to the img4 code; get our keybags 61 | with open(temp_filename, 'rb') as fp: 62 | bags = get_keybags(fp) 63 | print('keybags:') 64 | for bag in bags: 65 | print(' ' + bag) 66 | 67 | keybag = bags[0] 68 | 69 | iv = '' 70 | key = '' 71 | 72 | if self.cache.is_keybag_in_cache(keybag): 73 | print('[+] Found keybag in cache') 74 | keybag_dict = self.cache.keybag_from_cache(keybag) 75 | iv = keybag_dict['iv'] 76 | key = keybag_dict['key'] 77 | else: 78 | print('[*] Keybag not yet in cache, enter IV/Key') 79 | iv = self.api.ask_str('AES IV') 80 | key = self.api.ask_str('AES KEY') 81 | print('[*] Saving keybag to cache') 82 | self.cache.cache_keybag(keybag, iv, key) 83 | 84 | # load in the im4p dumped from ida we wrote earlier 85 | with open(temp_filename, 'rb') as fp: 86 | with open(temp_filename_dec, 'wb') as out_fp: 87 | # invoke the aes code and decrypt the dumped fp, then save that to the next file 88 | aes_decrypt(fp, key, iv, out_fp) 89 | 90 | # read some basic values from the saved, decrypted fp 91 | with open(temp_filename_dec, 'rb') as in_fp: 92 | in_fp.seek(0x0) 93 | bn = in_fp.read(0x4) 94 | in_fp.seek(0x200) 95 | ver_bin = in_fp.read(0x30) 96 | ver_str = ver_bin.decode() 97 | version_string = "%s" % (ver_str) 98 | bitness = Bitness.Bitness32 if b'\xea' in bn else Bitness.Bitness64 99 | 100 | self.bitness = bitness 101 | self.version_string = version_string 102 | 103 | # we can just overwrite the file pointer with our own 104 | # ida bitches about this but it works 105 | idafd.close() 106 | idafd.open(temp_filename_dec) 107 | 108 | # add the temp files to the deletion queue 109 | # we cant delete these just yet, IDA needs to read the mem to load it into the program 110 | self.deleteme.append(temp_filename) 111 | self.deleteme.append(temp_filename_dec) 112 | 113 | def load(self): 114 | self.decrypt() 115 | 116 | self.configure_segments() 117 | 118 | print("[*] Defining entry point") 119 | self.api.add_entry_point(self.code_segment.start, "start") 120 | 121 | print("[*] Looking for rebase address") 122 | rebase_addr = self.find_and_rebase() 123 | siz = self.code_segment.size 124 | self.code_segment.start = rebase_addr 125 | self.code_segment.end = rebase_addr + siz 126 | 127 | print("[*] Analyzing loaded code") 128 | self.api.analyze(rebase_addr, rebase_addr + self.code_segment.size) 129 | 130 | print("[*] Looking for string start") 131 | self.string_start = self.find_probable_string_start("darwinos-ramdisk", self.code_segment) 132 | if self.string_start == 0: 133 | print(" [-] Did not find.") 134 | 135 | print("[*] Looking for symbols") 136 | self.find_strref_syms() 137 | 138 | def find_strref_syms(self): 139 | panic_location = self.find_faddr_by_strref("double panic in ", self.string_start, -1) 140 | print(f'{panic_location}') 141 | if not panic_location == self.api.bad_address(): 142 | print(f' [+] _panic = {hex(panic_location)}') 143 | self.api.add_name(panic_location, '_panic') 144 | 145 | def find_probable_string_start(self, prologue, segment): 146 | string_addr = self.api.search_text(prologue, segment.end, segment.start, SearchDirection.UP) 147 | if string_addr == self.api.bad_address(): 148 | string_addr = 0 149 | return string_addr 150 | 151 | def find_and_rebase(self): 152 | rebase_ldr_addr = 0x44 153 | if self.bitness == Bitness.Bitness64: 154 | rebase_ldr_addr = 0x8 155 | self.api.analyze(0x0, 0x100) 156 | rebase_addr = int(self.api.get_disasm(rebase_ldr_addr).split('=')[1], 16) 157 | 158 | print(f' [+] {rebase_addr}') 159 | self.api.rebase_to(rebase_addr) 160 | return rebase_addr 161 | 162 | def configure_segments(self): 163 | 164 | base_addr = 0x0 165 | ptr_size = 0x8 166 | sram_len = 0x00120000 167 | 168 | if self.bitness == Bitness.Bitness32: 169 | self.api.set_processor_type(ProcessorType.ARM32) 170 | ptr_size = 0x4 171 | 172 | elif self.bitness == Bitness.Bitness64: 173 | self.api.set_processor_type(ProcessorType.ARM64) 174 | 175 | sram_start_ptr = 0x300 + (7*ptr_size) 176 | 177 | self.code_segment = Segment("iBoot", base_addr, self.file.size, SegmentType.CODE, self.bitness) 178 | self.api.create_segment(self.code_segment) 179 | 180 | self.segments.append(self.code_segment) 181 | 182 | self.api.copy_da_file_to_segment(self.file, self.code_segment, 0) 183 | 184 | idafd = self.file.fd 185 | idafd.close() 186 | for filename in self.deleteme: 187 | os.remove(filename) 188 | 189 | def find_faddr_by_strref(self, string, start_address, off): 190 | function_address = self.api.bad_address() 191 | pk_ea = self.api.search_text(string, start_address, 0, SearchDirection.DOWN) 192 | 193 | if pk_ea == self.api.bad_address(): 194 | print(f' [-] {string} not found') 195 | return pk_ea 196 | 197 | if pk_ea < start_address: # String is in func 198 | function_address = self.api.get_function(pk_ea) 199 | 200 | if len([i for i in self.api.xrefs_to(pk_ea)]) == 0: 201 | print(f' [-] no xrefs to {hex(pk_ea)} found') 202 | 203 | #print([i for i in self.api.xrefs_to(pk_ea)]) 204 | 205 | for xref in self.api.xrefs_to(pk_ea): 206 | func = self.api.get_function(xref.frm) 207 | if not func: 208 | #print(f'Bad Function {hex(xref.frm)}') 209 | continue 210 | function_address = func.start_ea 211 | if function_address == self.api.bad_address(): 212 | print(f' [-] {hex(xref)} func not found') 213 | break 214 | 215 | return function_address 216 | 217 | -------------------------------------------------------------------------------- /loader/src/pyaes/blockfeeder.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2014 Richard Moore 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | 24 | from .aes import AESBlockModeOfOperation, AESSegmentModeOfOperation, AESStreamModeOfOperation 25 | from .util import append_PKCS7_padding, strip_PKCS7_padding, to_bufferable 26 | 27 | 28 | # First we inject three functions to each of the modes of operations 29 | # 30 | # _can_consume(size) 31 | # - Given a size, determine how many bytes could be consumed in 32 | # a single call to either the decrypt or encrypt method 33 | # 34 | # _final_encrypt(data, padding = PADDING_DEFAULT) 35 | # - call and return encrypt on this (last) chunk of data, 36 | # padding as necessary; this will always be at least 16 37 | # bytes unless the total incoming input was less than 16 38 | # bytes 39 | # 40 | # _final_decrypt(data, padding = PADDING_DEFAULT) 41 | # - same as _final_encrypt except for decrypt, for 42 | # stripping off padding 43 | # 44 | 45 | PADDING_NONE = 'none' 46 | PADDING_DEFAULT = 'default' 47 | 48 | # @TODO: Ciphertext stealing and explicit PKCS#7 49 | # PADDING_CIPHERTEXT_STEALING 50 | # PADDING_PKCS7 51 | 52 | # ECB and CBC are block-only ciphers 53 | 54 | def _block_can_consume(self, size): 55 | if size >= 16: return 16 56 | return 0 57 | 58 | # After padding, we may have more than one block 59 | def _block_final_encrypt(self, data, padding = PADDING_DEFAULT): 60 | if padding == PADDING_DEFAULT: 61 | data = append_PKCS7_padding(data) 62 | 63 | elif padding == PADDING_NONE: 64 | if len(data) != 16: 65 | raise Exception('invalid data length for final block') 66 | else: 67 | raise Exception('invalid padding option') 68 | 69 | if len(data) == 32: 70 | return self.encrypt(data[:16]) + self.encrypt(data[16:]) 71 | 72 | return self.encrypt(data) 73 | 74 | 75 | def _block_final_decrypt(self, data, padding = PADDING_DEFAULT): 76 | if padding == PADDING_DEFAULT: 77 | return strip_PKCS7_padding(self.decrypt(data)) 78 | 79 | if padding == PADDING_NONE: 80 | if len(data) != 16: 81 | raise Exception('invalid data length for final block') 82 | return self.decrypt(data) 83 | 84 | raise Exception('invalid padding option') 85 | 86 | AESBlockModeOfOperation._can_consume = _block_can_consume 87 | AESBlockModeOfOperation._final_encrypt = _block_final_encrypt 88 | AESBlockModeOfOperation._final_decrypt = _block_final_decrypt 89 | 90 | 91 | 92 | # CFB is a segment cipher 93 | 94 | def _segment_can_consume(self, size): 95 | return self.segment_bytes * int(size // self.segment_bytes) 96 | 97 | # CFB can handle a non-segment-sized block at the end using the remaining cipherblock 98 | def _segment_final_encrypt(self, data, padding = PADDING_DEFAULT): 99 | if padding != PADDING_DEFAULT: 100 | raise Exception('invalid padding option') 101 | 102 | faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes))) 103 | padded = data + to_bufferable(faux_padding) 104 | return self.encrypt(padded)[:len(data)] 105 | 106 | # CFB can handle a non-segment-sized block at the end using the remaining cipherblock 107 | def _segment_final_decrypt(self, data, padding = PADDING_DEFAULT): 108 | if padding != PADDING_DEFAULT: 109 | raise Exception('invalid padding option') 110 | 111 | faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes))) 112 | padded = data + to_bufferable(faux_padding) 113 | return self.decrypt(padded)[:len(data)] 114 | 115 | AESSegmentModeOfOperation._can_consume = _segment_can_consume 116 | AESSegmentModeOfOperation._final_encrypt = _segment_final_encrypt 117 | AESSegmentModeOfOperation._final_decrypt = _segment_final_decrypt 118 | 119 | 120 | 121 | # OFB and CTR are stream ciphers 122 | 123 | def _stream_can_consume(self, size): 124 | return size 125 | 126 | def _stream_final_encrypt(self, data, padding = PADDING_DEFAULT): 127 | if padding not in [PADDING_NONE, PADDING_DEFAULT]: 128 | raise Exception('invalid padding option') 129 | 130 | return self.encrypt(data) 131 | 132 | def _stream_final_decrypt(self, data, padding = PADDING_DEFAULT): 133 | if padding not in [PADDING_NONE, PADDING_DEFAULT]: 134 | raise Exception('invalid padding option') 135 | 136 | return self.decrypt(data) 137 | 138 | AESStreamModeOfOperation._can_consume = _stream_can_consume 139 | AESStreamModeOfOperation._final_encrypt = _stream_final_encrypt 140 | AESStreamModeOfOperation._final_decrypt = _stream_final_decrypt 141 | 142 | 143 | 144 | class BlockFeeder(object): 145 | '''The super-class for objects to handle chunking a stream of bytes 146 | into the appropriate block size for the underlying mode of operation 147 | and applying (or stripping) padding, as necessary.''' 148 | 149 | def __init__(self, mode, feed, final, padding = PADDING_DEFAULT): 150 | self._mode = mode 151 | self._feed = feed 152 | self._final = final 153 | self._buffer = to_bufferable("") 154 | self._padding = padding 155 | 156 | def feed(self, data = None): 157 | '''Provide bytes to encrypt (or decrypt), returning any bytes 158 | possible from this or any previous calls to feed. 159 | 160 | Call with None or an empty string to flush the mode of 161 | operation and return any final bytes; no further calls to 162 | feed may be made.''' 163 | 164 | if self._buffer is None: 165 | raise ValueError('already finished feeder') 166 | 167 | # Finalize; process the spare bytes we were keeping 168 | if data is None: 169 | result = self._final(self._buffer, self._padding) 170 | self._buffer = None 171 | return result 172 | 173 | self._buffer += to_bufferable(data) 174 | 175 | # We keep 16 bytes around so we can determine padding 176 | result = to_bufferable('') 177 | while len(self._buffer) > 16: 178 | can_consume = self._mode._can_consume(len(self._buffer) - 16) 179 | if can_consume == 0: break 180 | result += self._feed(self._buffer[:can_consume]) 181 | self._buffer = self._buffer[can_consume:] 182 | 183 | return result 184 | 185 | 186 | class Encrypter(BlockFeeder): 187 | 'Accepts bytes of plaintext and returns encrypted ciphertext.' 188 | 189 | def __init__(self, mode, padding = PADDING_DEFAULT): 190 | BlockFeeder.__init__(self, mode, mode.encrypt, mode._final_encrypt, padding) 191 | 192 | 193 | class Decrypter(BlockFeeder): 194 | 'Accepts bytes of ciphertext and returns decrypted plaintext.' 195 | 196 | def __init__(self, mode, padding = PADDING_DEFAULT): 197 | BlockFeeder.__init__(self, mode, mode.decrypt, mode._final_decrypt, padding) 198 | 199 | 200 | # 8kb blocks 201 | BLOCK_SIZE = (1 << 13) 202 | 203 | def _feed_stream(feeder, in_stream, out_stream, block_size = BLOCK_SIZE): 204 | 'Uses feeder to read and convert from in_stream and write to out_stream.' 205 | 206 | while True: 207 | chunk = in_stream.read(block_size) 208 | if not chunk: 209 | break 210 | converted = feeder.feed(chunk) 211 | out_stream.write(converted) 212 | converted = feeder.feed() 213 | out_stream.write(converted) 214 | 215 | 216 | def encrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT): 217 | 'Encrypts a stream of bytes from in_stream to out_stream using mode.' 218 | 219 | encrypter = Encrypter(mode, padding = padding) 220 | _feed_stream(encrypter, in_stream, out_stream, block_size) 221 | 222 | 223 | def decrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT): 224 | 'Decrypts a stream of bytes from in_stream to out_stream using mode.' 225 | 226 | decrypter = Decrypter(mode, padding = padding) 227 | _feed_stream(decrypter, in_stream, out_stream, block_size) 228 | -------------------------------------------------------------------------------- /loader/src/kimg4/asn1.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of Python-ASN1. Python-ASN1 is free software that is 4 | # made available under the MIT license. Consult the file "LICENSE" that is 5 | # distributed together with this file for the exact licensing terms. 6 | # 7 | # Python-ASN1 is copyright (c) 2007-2016 by the Python-ASN1 authors. See the 8 | # file "AUTHORS" for a complete overview. 9 | 10 | """ 11 | This module provides ASN.1 encoder and decoder. 12 | """ 13 | 14 | from __future__ import absolute_import 15 | from __future__ import division 16 | from __future__ import print_function 17 | from __future__ import unicode_literals 18 | 19 | import collections 20 | import re 21 | from builtins import bytes 22 | from builtins import int 23 | from builtins import range 24 | from builtins import str 25 | from enum import IntEnum 26 | from numbers import Number 27 | 28 | __version__ = "2.4.1" 29 | 30 | 31 | class Numbers(IntEnum): 32 | Boolean = 0x01 33 | Integer = 0x02 34 | BitString = 0x03 35 | OctetString = 0x04 36 | Null = 0x05 37 | ObjectIdentifier = 0x06 38 | Enumerated = 0x0a 39 | UTF8String = 0x0c 40 | Sequence = 0x10 41 | Set = 0x11 42 | PrintableString = 0x13 43 | IA5String = 0x16 44 | UTCTime = 0x17 45 | UnicodeString = 0x1e 46 | 47 | 48 | class Types(IntEnum): 49 | Constructed = 0x20 50 | Primitive = 0x00 51 | 52 | 53 | class Classes(IntEnum): 54 | Universal = 0x00 55 | Application = 0x40 56 | Context = 0x80 57 | Private = 0xc0 58 | 59 | 60 | Tag = collections.namedtuple('Tag', 'nr typ cls') 61 | """A named tuple to represent ASN.1 tags as returned by `Decoder.peek()` and 62 | `Decoder.read()`.""" 63 | 64 | 65 | class Error(Exception): 66 | """ASN.11 encoding or decoding error.""" 67 | 68 | 69 | class Encoder(object): 70 | """ASN.1 encoder. Uses DER encoding. 71 | """ 72 | 73 | def __init__(self): # type: () -> None 74 | """Constructor.""" 75 | self.m_stack = None 76 | 77 | def start(self): # type: () -> None 78 | """This method instructs the encoder to start encoding a new ASN.1 79 | output. This method may be called at any time to reset the encoder, 80 | and resets the current output (if any). 81 | """ 82 | self.m_stack = [[]] 83 | 84 | def enter(self, nr, cls=None): # type: (int, int) -> None 85 | """This method starts the construction of a constructed type. 86 | Args: 87 | nr (int): The desired ASN.1 type. Use ``Numbers`` enumeration. 88 | cls (int): This optional parameter specifies the class 89 | of the constructed type. The default class to use is the 90 | universal class. Use ``Classes`` enumeration. 91 | Returns: 92 | None 93 | Raises: 94 | `Error` 95 | """ 96 | if self.m_stack is None: 97 | raise Error('Encoder not initialized. Call start() first.') 98 | if cls is None: 99 | cls = Classes.Universal 100 | self._emit_tag(nr, Types.Constructed, cls) 101 | self.m_stack.append([]) 102 | 103 | def leave(self): # type: () -> None 104 | """This method completes the construction of a constructed type and 105 | writes the encoded representation to the output buffer. 106 | """ 107 | if self.m_stack is None: 108 | raise Error('Encoder not initialized. Call start() first.') 109 | if len(self.m_stack) == 1: 110 | raise Error('Tag stack is empty.') 111 | value = b''.join(self.m_stack[-1]) 112 | del self.m_stack[-1] 113 | self._emit_length(len(value)) 114 | self._emit(value) 115 | 116 | def write(self, value, nr=None, typ=None, cls=None): # type: (object, int, int, int) -> None 117 | """This method encodes one ASN.1 tag and writes it to the output buffer. 118 | Note: 119 | Normally, ``value`` will be the only parameter to this method. 120 | In this case Python-ASN1 will autodetect the correct ASN.1 type from 121 | the type of ``value``, and will output the encoded value based on this 122 | type. 123 | Args: 124 | value (any): The value of the ASN.1 tag to write. Python-ASN1 will 125 | try to autodetect the correct ASN.1 type from the type of 126 | ``value``. 127 | nr (int): If the desired ASN.1 type cannot be autodetected or is 128 | autodetected wrongly, the ``nr`` parameter can be provided to 129 | specify the ASN.1 type to be used. Use ``Numbers`` enumeration. 130 | typ (int): This optional parameter can be used to write constructed 131 | types to the output by setting it to indicate the constructed 132 | encoding type. In this case, ``value`` must already be valid ASN.1 133 | encoded data as plain Python bytes. This is not normally how 134 | constructed types should be encoded though, see `Encoder.enter()` 135 | and `Encoder.leave()` for the recommended way of doing this. 136 | Use ``Types`` enumeration. 137 | cls (int): This parameter can be used to override the class of the 138 | ``value``. The default class is the universal class. 139 | Use ``Classes`` enumeration. 140 | Returns: 141 | None 142 | Raises: 143 | `Error` 144 | """ 145 | if self.m_stack is None: 146 | raise Error('Encoder not initialized. Call start() first.') 147 | 148 | if typ is None: 149 | typ = Types.Primitive 150 | if cls is None: 151 | cls = Classes.Universal 152 | 153 | if cls != Classes.Universal and nr is None: 154 | raise Error('Please specify a tag number (nr) when using classes Application, Context or Private') 155 | 156 | if nr is None: 157 | if isinstance(value, bool): 158 | nr = Numbers.Boolean 159 | elif isinstance(value, int): 160 | nr = Numbers.Integer 161 | elif isinstance(value, str): 162 | nr = Numbers.PrintableString 163 | elif isinstance(value, bytes): 164 | nr = Numbers.OctetString 165 | elif value is None: 166 | nr = Numbers.Null 167 | 168 | value = self._encode_value(cls, nr, value) 169 | self._emit_tag(nr, typ, cls) 170 | self._emit_length(len(value)) 171 | self._emit(value) 172 | 173 | def output(self): # type: () -> bytes 174 | """This method returns the encoded ASN.1 data as plain Python ``bytes``. 175 | This method can be called multiple times, also during encoding. 176 | In the latter case the data that has been encoded so far is 177 | returned. 178 | Note: 179 | It is an error to call this method if the encoder is still 180 | constructing a constructed type, i.e. if `Encoder.enter()` has been 181 | called more times that `Encoder.leave()`. 182 | Returns: 183 | bytes: The DER encoded ASN.1 data. 184 | Raises: 185 | `Error` 186 | """ 187 | if self.m_stack is None: 188 | raise Error('Encoder not initialized. Call start() first.') 189 | if len(self.m_stack) != 1: 190 | raise Error('Stack is not empty.') 191 | output = b''.join(self.m_stack[0]) 192 | return output 193 | 194 | def _emit_tag(self, nr, typ, cls): # type: (int, int, int) -> None 195 | """Emit a tag.""" 196 | if nr < 31: 197 | self._emit_tag_short(nr, typ, cls) 198 | else: 199 | self._emit_tag_long(nr, typ, cls) 200 | 201 | def _emit_tag_short(self, nr, typ, cls): # type: (int, int, int) -> None 202 | """Emit a short (< 31 bytes) tag.""" 203 | assert nr < 31 204 | self._emit(bytes([nr | typ | cls])) 205 | 206 | def _emit_tag_long(self, nr, typ, cls): # type: (int, int, int) -> None 207 | """Emit a long (>= 31 bytes) tag.""" 208 | head = bytes([typ | cls | 0x1f]) 209 | self._emit(head) 210 | values = [(nr & 0x7f)] 211 | nr >>= 7 212 | while nr: 213 | values.append((nr & 0x7f) | 0x80) 214 | nr >>= 7 215 | values.reverse() 216 | for val in values: 217 | self._emit(bytes([val])) 218 | 219 | def _emit_length(self, length): # type: (int) -> None 220 | """Emit length octects.""" 221 | if length < 128: 222 | self._emit_length_short(length) 223 | else: 224 | self._emit_length_long(length) 225 | 226 | def _emit_length_short(self, length): # type: (int) -> None 227 | """Emit the short length form (< 128 octets).""" 228 | assert length < 128 229 | self._emit(bytes([length])) 230 | 231 | def _emit_length_long(self, length): # type: (int) -> None 232 | """Emit the long length form (>= 128 octets).""" 233 | values = [] 234 | while length: 235 | values.append(length & 0xff) 236 | length >>= 8 237 | values.reverse() 238 | # really for correctness as this should not happen anytime soon 239 | assert len(values) < 127 240 | head = bytes([0x80 | len(values)]) 241 | self._emit(head) 242 | for val in values: 243 | self._emit(bytes([val])) 244 | 245 | def _emit(self, s): # type: (bytes) -> None 246 | """Emit raw bytes.""" 247 | assert isinstance(s, bytes) 248 | self.m_stack[-1].append(s) 249 | 250 | def _encode_value(self, cls, nr, value): # type: (int, int, any) -> bytes 251 | """Encode a value.""" 252 | if cls != Classes.Universal: 253 | return value 254 | if nr in (Numbers.Integer, Numbers.Enumerated): 255 | return self._encode_integer(value) 256 | if nr in (Numbers.OctetString, Numbers.PrintableString): 257 | return self._encode_octet_string(value) 258 | if nr == Numbers.BitString: 259 | return self._encode_bit_string(value) 260 | if nr == Numbers.Boolean: 261 | return self._encode_boolean(value) 262 | if nr == Numbers.Null: 263 | return self._encode_null() 264 | if nr == Numbers.ObjectIdentifier: 265 | return self._encode_object_identifier(value) 266 | return value 267 | 268 | @staticmethod 269 | def _encode_boolean(value): # type: (bool) -> bytes 270 | """Encode a boolean.""" 271 | return value and bytes(b'\xff') or bytes(b'\x00') 272 | 273 | @staticmethod 274 | def _encode_integer(value): # type: (int) -> bytes 275 | """Encode an integer.""" 276 | if value < 0: 277 | value = -value 278 | negative = True 279 | limit = 0x80 280 | else: 281 | negative = False 282 | limit = 0x7f 283 | values = [] 284 | while value > limit: 285 | values.append(value & 0xff) 286 | value >>= 8 287 | values.append(value & 0xff) 288 | if negative: 289 | # create two's complement 290 | for i in range(len(values)): # Invert bits 291 | values[i] = 0xff - values[i] 292 | for i in range(len(values)): # Add 1 293 | values[i] += 1 294 | if values[i] <= 0xff: 295 | break 296 | assert i != len(values) - 1 297 | values[i] = 0x00 298 | if negative and values[len(values) - 1] == 0x7f: # Two's complement corner case 299 | values.append(0xff) 300 | values.reverse() 301 | return bytes(values) 302 | 303 | @staticmethod 304 | def _encode_octet_string(value): # type: (object) -> bytes 305 | """Encode an octetstring.""" 306 | # Use the primitive encoding 307 | assert isinstance(value, str) or isinstance(value, bytes) 308 | if isinstance(value, str): 309 | return value.encode('utf-8') 310 | else: 311 | return value 312 | 313 | @staticmethod 314 | def _encode_bit_string(value): # type: (object) -> bytes 315 | """Encode a bitstring. Assumes no unused bytes.""" 316 | # Use the primitive encoding 317 | assert isinstance(value, bytes) 318 | return b'\x00' + value 319 | 320 | @staticmethod 321 | def _encode_null(): # type: () -> bytes 322 | """Encode a Null value.""" 323 | return bytes(b'') 324 | 325 | _re_oid = re.compile(r'^[0-9]+(\.[0-9]+)+$') 326 | 327 | def _encode_object_identifier(self, oid): # type: (str) -> bytes 328 | """Encode an object identifier.""" 329 | if not self._re_oid.match(oid): 330 | raise Error('Illegal object identifier') 331 | cmps = list(map(int, oid.split('.'))) 332 | if cmps[0] > 39 or cmps[1] > 39: 333 | raise Error('Illegal object identifier') 334 | cmps = [40 * cmps[0] + cmps[1]] + cmps[2:] 335 | cmps.reverse() 336 | result = [] 337 | for cmp_data in cmps: 338 | result.append(cmp_data & 0x7f) 339 | while cmp_data > 0x7f: 340 | cmp_data >>= 7 341 | result.append(0x80 | (cmp_data & 0x7f)) 342 | result.reverse() 343 | return bytes(result) 344 | 345 | 346 | class Decoder(object): 347 | """ASN.1 decoder. Understands BER (and DER which is a subset).""" 348 | 349 | def __init__(self): # type: () -> None 350 | """Constructor.""" 351 | self.m_stack = None 352 | self.m_tag = None 353 | 354 | def start(self, data): # type: (bytes) -> None 355 | """This method instructs the decoder to start decoding the ASN.1 input 356 | ``data``, which must be a passed in as plain Python bytes. 357 | This method may be called at any time to start a new decoding job. 358 | If this method is called while currently decoding another input, that 359 | decoding context is discarded. 360 | Note: 361 | It is not necessary to specify the encoding because the decoder 362 | assumes the input is in BER or DER format. 363 | Args: 364 | data (bytes): ASN.1 input, in BER or DER format, to be decoded. 365 | Returns: 366 | None 367 | Raises: 368 | `Error` 369 | """ 370 | if not isinstance(data, bytes): 371 | raise Error('Expecting bytes instance.') 372 | self.m_stack = [[0, bytes(data)]] 373 | self.m_tag = None 374 | 375 | def peek(self): # type: () -> Tag 376 | """This method returns the current ASN.1 tag (i.e. the tag that a 377 | subsequent `Decoder.read()` call would return) without updating the 378 | decoding offset. In case no more data is available from the input, 379 | this method returns ``None`` to signal end-of-file. 380 | This method is useful if you don't know whether the next tag will be a 381 | primitive or a constructed tag. Depending on the return value of `peek`, 382 | you would decide to either issue a `Decoder.read()` in case of a primitive 383 | type, or an `Decoder.enter()` in case of a constructed type. 384 | Note: 385 | Because this method does not advance the current offset in the input, 386 | calling it multiple times in a row will return the same value for all 387 | calls. 388 | Returns: 389 | `Tag`: The current ASN.1 tag. 390 | Raises: 391 | `Error` 392 | """ 393 | if self.m_stack is None: 394 | raise Error('No input selected. Call start() first.') 395 | if self._end_of_input(): 396 | return None 397 | if self.m_tag is None: 398 | self.m_tag = self._read_tag() 399 | return self.m_tag 400 | 401 | def read(self, tagnr=None): # type: (Number) -> (Tag, any) 402 | """This method decodes one ASN.1 tag from the input and returns it as a 403 | ``(tag, value)`` tuple. ``tag`` is a 3-tuple ``(nr, typ, cls)``, 404 | while ``value`` is a Python object representing the ASN.1 value. 405 | The offset in the input is increased so that the next `Decoder.read()` 406 | call will return the next tag. In case no more data is available from 407 | the input, this method returns ``None`` to signal end-of-file. 408 | Returns: 409 | `Tag`, value: The current ASN.1 tag and its value. 410 | Raises: 411 | `Error` 412 | """ 413 | if self.m_stack is None: 414 | raise Error('No input selected. Call start() first.') 415 | if self._end_of_input(): 416 | return None 417 | tag = self.peek() 418 | length = self._read_length() 419 | if tagnr is None: 420 | tagnr = tag.nr 421 | value = self._read_value(tag.cls, tagnr, length) 422 | self.m_tag = None 423 | return tag, value 424 | 425 | def eof(self): # type: () -> bool 426 | """Return True if we are at the end of input. 427 | Returns: 428 | bool: True if all input has been decoded, and False otherwise. 429 | """ 430 | return self._end_of_input() 431 | 432 | def enter(self): # type: () -> None 433 | """This method enters the constructed type that is at the current 434 | decoding offset. 435 | Note: 436 | It is an error to call `Decoder.enter()` if the to be decoded ASN.1 tag 437 | is not of a constructed type. 438 | Returns: 439 | None 440 | """ 441 | if self.m_stack is None: 442 | raise Error('No input selected. Call start() first.') 443 | tag = self.peek() 444 | if tag.typ != Types.Constructed: 445 | raise Error('Cannot enter a non-constructed tag.') 446 | length = self._read_length() 447 | bytes_data = self._read_bytes(length) 448 | self.m_stack.append([0, bytes_data]) 449 | self.m_tag = None 450 | 451 | def leave(self): # type: () -> None 452 | """This method leaves the last constructed type that was 453 | `Decoder.enter()`-ed. 454 | Note: 455 | It is an error to call `Decoder.leave()` if the current ASN.1 tag 456 | is not of a constructed type. 457 | Returns: 458 | None 459 | """ 460 | if self.m_stack is None: 461 | raise Error('No input selected. Call start() first.') 462 | if len(self.m_stack) == 1: 463 | raise Error('Tag stack is empty.') 464 | del self.m_stack[-1] 465 | self.m_tag = None 466 | 467 | def _read_tag(self): # type: () -> Tag 468 | """Read a tag from the input.""" 469 | byte = self._read_byte() 470 | cls = byte & 0xc0 471 | typ = byte & 0x20 472 | nr = byte & 0x1f 473 | if nr == 0x1f: # Long form of tag encoding 474 | nr = 0 475 | while True: 476 | byte = self._read_byte() 477 | nr = (nr << 7) | (byte & 0x7f) 478 | if not byte & 0x80: 479 | break 480 | return Tag(nr=nr, typ=typ, cls=cls) 481 | 482 | def _read_length(self): # type: () -> int 483 | """Read a length from the input.""" 484 | byte = self._read_byte() 485 | if byte & 0x80: 486 | count = byte & 0x7f 487 | if count == 0x7f: 488 | raise Error('ASN1 syntax error') 489 | bytes_data = self._read_bytes(count) 490 | length = 0 491 | for byte in bytes_data: 492 | length = (length << 8) | int(byte) 493 | try: 494 | length = int(length) 495 | except OverflowError: 496 | pass 497 | else: 498 | length = byte 499 | return length 500 | 501 | def _read_value(self, cls, nr, length): # type: (int, int, int) -> any 502 | """Read a value from the input.""" 503 | bytes_data = self._read_bytes(length) 504 | if cls != Classes.Universal: 505 | value = bytes_data 506 | elif nr == Numbers.Boolean: 507 | value = self._decode_boolean(bytes_data) 508 | elif nr in (Numbers.Integer, Numbers.Enumerated): 509 | value = self._decode_integer(bytes_data) 510 | elif nr == Numbers.OctetString: 511 | value = self._decode_octet_string(bytes_data) 512 | elif nr == Numbers.Null: 513 | value = self._decode_null(bytes_data) 514 | elif nr == Numbers.ObjectIdentifier: 515 | value = self._decode_object_identifier(bytes_data) 516 | elif nr in (Numbers.PrintableString, Numbers.IA5String, Numbers.UTCTime): 517 | value = self._decode_printable_string(bytes_data) 518 | else: 519 | value = bytes_data 520 | return value 521 | 522 | def _read_byte(self): # type: () -> int 523 | """Return the next input byte, or raise an error on end-of-input.""" 524 | index, input_data = self.m_stack[-1] 525 | try: 526 | byte = input_data[index] 527 | except IndexError: 528 | raise Error('Premature end of input.') 529 | self.m_stack[-1][0] += 1 530 | return byte 531 | 532 | def _read_bytes(self, count): # type: (int) -> bytes 533 | """Return the next ``count`` bytes of input. Raise error on 534 | end-of-input.""" 535 | index, input_data = self.m_stack[-1] 536 | bytes_data = input_data[index:index + count] 537 | if len(bytes_data) != count: 538 | raise Error('Premature end of input.') 539 | self.m_stack[-1][0] += count 540 | return bytes_data 541 | 542 | def _end_of_input(self): # type: () -> bool 543 | """Return True if we are at the end of input.""" 544 | index, input_data = self.m_stack[-1] 545 | assert not index > len(input_data) 546 | return index == len(input_data) 547 | 548 | @staticmethod 549 | def _decode_boolean(bytes_data): # type: (bytes) -> bool 550 | """Decode a boolean value.""" 551 | if len(bytes_data) != 1: 552 | raise Error('ASN1 syntax error') 553 | if bytes_data[0] == 0: 554 | return False 555 | return True 556 | 557 | @staticmethod 558 | def _decode_integer(bytes_data): # type: (bytes) -> int 559 | """Decode an integer value.""" 560 | values = [int(b) for b in bytes_data] 561 | # check if the integer is normalized 562 | if len(values) > 1 and (values[0] == 0xff and values[1] & 0x80 or values[0] == 0x00 and not (values[1] & 0x80)): 563 | raise Error('ASN1 syntax error') 564 | negative = values[0] & 0x80 565 | if negative: 566 | # make positive by taking two's complement 567 | for i in range(len(values)): 568 | values[i] = 0xff - values[i] 569 | for i in range(len(values) - 1, -1, -1): 570 | values[i] += 1 571 | if values[i] <= 0xff: 572 | break 573 | assert i > 0 574 | values[i] = 0x00 575 | value = 0 576 | for val in values: 577 | value = (value << 8) | val 578 | if negative: 579 | value = -value 580 | try: 581 | value = int(value) 582 | except OverflowError: 583 | pass 584 | return value 585 | 586 | @staticmethod 587 | def _decode_octet_string(bytes_data): # type: (bytes) -> bytes 588 | """Decode an octet string.""" 589 | return bytes_data 590 | 591 | @staticmethod 592 | def _decode_null(bytes_data): # type: (bytes) -> any 593 | """Decode a Null value.""" 594 | if len(bytes_data) != 0: 595 | raise Error('ASN1 syntax error') 596 | return None 597 | 598 | @staticmethod 599 | def _decode_object_identifier(bytes_data): # type: (bytes) -> str 600 | """Decode an object identifier.""" 601 | result = [] 602 | value = 0 603 | for i in range(len(bytes_data)): 604 | byte = int(bytes_data[i]) 605 | if value == 0 and byte == 0x80: 606 | raise Error('ASN1 syntax error') 607 | value = (value << 7) | (byte & 0x7f) 608 | if not byte & 0x80: 609 | result.append(value) 610 | value = 0 611 | if len(result) == 0 or result[0] > 1599: 612 | raise Error('ASN1 syntax error') 613 | result = [result[0] // 40, result[0] % 40] + result[1:] 614 | result = list(map(str, result)) 615 | return str('.'.join(result)) 616 | 617 | @staticmethod 618 | def _decode_printable_string(bytes_data): # type: (bytes) -> str 619 | """Decode a printable string.""" 620 | return bytes_data.decode('utf-8') -------------------------------------------------------------------------------- /loader/src/pyaes/aes.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2014 Richard Moore 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | # This is a pure-Python implementation of the AES algorithm and AES common 24 | # modes of operation. 25 | 26 | # See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard 27 | 28 | # Honestly, the best description of the modes of operations are the wonderful 29 | # diagrams on Wikipedia. They explain in moments what my words could never 30 | # achieve. Hence the inline documentation here is sparer than I'd prefer. 31 | # See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation 32 | 33 | # Also useful, PyCrypto, a crypto library implemented in C with Python bindings: 34 | # https://www.dlitz.net/software/pycrypto/ 35 | 36 | 37 | # Supported key sizes: 38 | # 128-bit 39 | # 192-bit 40 | # 256-bit 41 | 42 | 43 | # Supported modes of operation: 44 | # ECB - Electronic Codebook 45 | # CBC - Cipher-Block Chaining 46 | # CFB - Cipher Feedback 47 | # OFB - Output Feedback 48 | # CTR - Counter 49 | 50 | 51 | # See the README.md for API details and general information. 52 | 53 | 54 | import copy 55 | import struct 56 | 57 | __all__ = ["AES", "AESModeOfOperationCTR", "AESModeOfOperationCBC", "AESModeOfOperationCFB", 58 | "AESModeOfOperationECB", "AESModeOfOperationOFB", "AESModesOfOperation", "Counter"] 59 | 60 | 61 | def _compact_word(word): 62 | return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3] 63 | 64 | def _string_to_bytes(text): 65 | return list(ord(c) for c in text) 66 | 67 | def _bytes_to_string(binary): 68 | return "".join(chr(b) for b in binary) 69 | 70 | def _concat_list(a, b): 71 | return a + b 72 | 73 | 74 | # Python 3 compatibility 75 | try: 76 | xrange 77 | except Exception: 78 | xrange = range 79 | 80 | # Python 3 supports bytes, which is already an array of integers 81 | def _string_to_bytes(text): 82 | if isinstance(text, bytes): 83 | return text 84 | return [ord(c) for c in text] 85 | 86 | # In Python 3, we return bytes 87 | def _bytes_to_string(binary): 88 | return bytes(binary) 89 | 90 | # Python 3 cannot concatenate a list onto a bytes, so we bytes-ify it first 91 | def _concat_list(a, b): 92 | return a + bytes(b) 93 | 94 | 95 | # Based *largely* on the Rijndael implementation 96 | # See: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf 97 | class AES(object): 98 | '''Encapsulates the AES block cipher. 99 | 100 | You generally should not need this. Use the AESModeOfOperation classes 101 | below instead.''' 102 | 103 | # Number of rounds by keysize 104 | number_of_rounds = {16: 10, 24: 12, 32: 14} 105 | 106 | # Round constant words 107 | rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ] 108 | 109 | # S-box and Inverse S-box (S is for Substitution) 110 | S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ] 111 | Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ] 112 | 113 | # Transformations for encryption 114 | T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ] 115 | T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ] 116 | T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ] 117 | T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ] 118 | 119 | # Transformations for decryption 120 | T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ] 121 | T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ] 122 | T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ] 123 | T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ] 124 | 125 | # Transformations for decryption key expansion 126 | U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ] 127 | U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ] 128 | U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ] 129 | U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ] 130 | 131 | def __init__(self, key): 132 | 133 | if len(key) not in (16, 24, 32): 134 | raise ValueError('Invalid key size') 135 | 136 | rounds = self.number_of_rounds[len(key)] 137 | 138 | # Encryption round keys 139 | self._Ke = [[0] * 4 for i in xrange(rounds + 1)] 140 | 141 | # Decryption round keys 142 | self._Kd = [[0] * 4 for i in xrange(rounds + 1)] 143 | 144 | round_key_count = (rounds + 1) * 4 145 | KC = len(key) // 4 146 | 147 | # Convert the key into ints 148 | tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in xrange(0, len(key), 4) ] 149 | 150 | # Copy values into round key arrays 151 | for i in xrange(0, KC): 152 | self._Ke[i // 4][i % 4] = tk[i] 153 | self._Kd[rounds - (i // 4)][i % 4] = tk[i] 154 | 155 | # Key expansion (fips-197 section 5.2) 156 | rconpointer = 0 157 | t = KC 158 | while t < round_key_count: 159 | 160 | tt = tk[KC - 1] 161 | tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^ 162 | (self.S[(tt >> 8) & 0xFF] << 16) ^ 163 | (self.S[ tt & 0xFF] << 8) ^ 164 | self.S[(tt >> 24) & 0xFF] ^ 165 | (self.rcon[rconpointer] << 24)) 166 | rconpointer += 1 167 | 168 | if KC != 8: 169 | for i in xrange(1, KC): 170 | tk[i] ^= tk[i - 1] 171 | 172 | # Key expansion for 256-bit keys is "slightly different" (fips-197) 173 | else: 174 | for i in xrange(1, KC // 2): 175 | tk[i] ^= tk[i - 1] 176 | tt = tk[KC // 2 - 1] 177 | 178 | tk[KC // 2] ^= (self.S[ tt & 0xFF] ^ 179 | (self.S[(tt >> 8) & 0xFF] << 8) ^ 180 | (self.S[(tt >> 16) & 0xFF] << 16) ^ 181 | (self.S[(tt >> 24) & 0xFF] << 24)) 182 | 183 | for i in xrange(KC // 2 + 1, KC): 184 | tk[i] ^= tk[i - 1] 185 | 186 | # Copy values into round key arrays 187 | j = 0 188 | while j < KC and t < round_key_count: 189 | self._Ke[t // 4][t % 4] = tk[j] 190 | self._Kd[rounds - (t // 4)][t % 4] = tk[j] 191 | j += 1 192 | t += 1 193 | 194 | # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3) 195 | for r in xrange(1, rounds): 196 | for j in xrange(0, 4): 197 | tt = self._Kd[r][j] 198 | self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^ 199 | self.U2[(tt >> 16) & 0xFF] ^ 200 | self.U3[(tt >> 8) & 0xFF] ^ 201 | self.U4[ tt & 0xFF]) 202 | 203 | def encrypt(self, plaintext): 204 | 'Encrypt a block of plain text using the AES block cipher.' 205 | 206 | if len(plaintext) != 16: 207 | raise ValueError('wrong block length') 208 | 209 | rounds = len(self._Ke) - 1 210 | (s1, s2, s3) = [1, 2, 3] 211 | a = [0, 0, 0, 0] 212 | 213 | # Convert plaintext to (ints ^ key) 214 | t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in xrange(0, 4)] 215 | 216 | # Apply round transforms 217 | for r in xrange(1, rounds): 218 | for i in xrange(0, 4): 219 | a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^ 220 | self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^ 221 | self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^ 222 | self.T4[ t[(i + s3) % 4] & 0xFF] ^ 223 | self._Ke[r][i]) 224 | t = copy.copy(a) 225 | 226 | # The last round is special 227 | result = [ ] 228 | for i in xrange(0, 4): 229 | tt = self._Ke[rounds][i] 230 | result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) 231 | result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) 232 | result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) 233 | result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) 234 | 235 | return result 236 | 237 | def decrypt(self, ciphertext): 238 | 'Decrypt a block of cipher text using the AES block cipher.' 239 | 240 | if len(ciphertext) != 16: 241 | raise ValueError('wrong block length') 242 | 243 | rounds = len(self._Kd) - 1 244 | (s1, s2, s3) = [3, 2, 1] 245 | a = [0, 0, 0, 0] 246 | 247 | # Convert ciphertext to (ints ^ key) 248 | t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)] 249 | 250 | # Apply round transforms 251 | for r in xrange(1, rounds): 252 | for i in xrange(0, 4): 253 | a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^ 254 | self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^ 255 | self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^ 256 | self.T8[ t[(i + s3) % 4] & 0xFF] ^ 257 | self._Kd[r][i]) 258 | t = copy.copy(a) 259 | 260 | # The last round is special 261 | result = [ ] 262 | for i in xrange(0, 4): 263 | tt = self._Kd[rounds][i] 264 | result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) 265 | result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) 266 | result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) 267 | result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) 268 | 269 | return result 270 | 271 | 272 | class Counter(object): 273 | '''A counter object for the Counter (CTR) mode of operation. 274 | 275 | To create a custom counter, you can usually just override the 276 | increment method.''' 277 | 278 | def __init__(self, initial_value = 1): 279 | 280 | # Convert the value into an array of bytes long 281 | self._counter = [ ((initial_value >> i) % 256) for i in xrange(128 - 8, -1, -8) ] 282 | 283 | value = property(lambda s: s._counter) 284 | 285 | def increment(self): 286 | '''Increment the counter (overflow rolls back to 0).''' 287 | 288 | for i in xrange(len(self._counter) - 1, -1, -1): 289 | self._counter[i] += 1 290 | 291 | if self._counter[i] < 256: break 292 | 293 | # Carry the one 294 | self._counter[i] = 0 295 | 296 | # Overflow 297 | else: 298 | self._counter = [ 0 ] * len(self._counter) 299 | 300 | 301 | class AESBlockModeOfOperation(object): 302 | '''Super-class for AES modes of operation that require blocks.''' 303 | def __init__(self, key): 304 | self._aes = AES(key) 305 | 306 | def decrypt(self, ciphertext): 307 | raise Exception('not implemented') 308 | 309 | def encrypt(self, plaintext): 310 | raise Exception('not implemented') 311 | 312 | 313 | class AESStreamModeOfOperation(AESBlockModeOfOperation): 314 | '''Super-class for AES modes of operation that are stream-ciphers.''' 315 | 316 | class AESSegmentModeOfOperation(AESStreamModeOfOperation): 317 | '''Super-class for AES modes of operation that segment data.''' 318 | 319 | segment_bytes = 16 320 | 321 | 322 | 323 | class AESModeOfOperationECB(AESBlockModeOfOperation): 324 | '''AES Electronic Codebook Mode of Operation. 325 | 326 | o Block-cipher, so data must be padded to 16 byte boundaries 327 | 328 | Security Notes: 329 | o This mode is not recommended 330 | o Any two identical blocks produce identical encrypted values, 331 | exposing data patterns. (See the image of Tux on wikipedia) 332 | 333 | Also see: 334 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29 335 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.1''' 336 | 337 | 338 | name = "Electronic Codebook (ECB)" 339 | 340 | def encrypt(self, plaintext): 341 | if len(plaintext) != 16: 342 | raise ValueError('plaintext block must be 16 bytes') 343 | 344 | plaintext = _string_to_bytes(plaintext) 345 | return _bytes_to_string(self._aes.encrypt(plaintext)) 346 | 347 | def decrypt(self, ciphertext): 348 | if len(ciphertext) != 16: 349 | raise ValueError('ciphertext block must be 16 bytes') 350 | 351 | ciphertext = _string_to_bytes(ciphertext) 352 | return _bytes_to_string(self._aes.decrypt(ciphertext)) 353 | 354 | 355 | 356 | class AESModeOfOperationCBC(AESBlockModeOfOperation): 357 | '''AES Cipher-Block Chaining Mode of Operation. 358 | 359 | o The Initialization Vector (IV) 360 | o Block-cipher, so data must be padded to 16 byte boundaries 361 | o An incorrect initialization vector will only cause the first 362 | block to be corrupt; all other blocks will be intact 363 | o A corrupt bit in the cipher text will cause a block to be 364 | corrupted, and the next block to be inverted, but all other 365 | blocks will be intact. 366 | 367 | Security Notes: 368 | o This method (and CTR) ARE recommended. 369 | 370 | Also see: 371 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29 372 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.2''' 373 | 374 | 375 | name = "Cipher-Block Chaining (CBC)" 376 | 377 | def __init__(self, key, iv = None): 378 | if iv is None: 379 | self._last_cipherblock = [ 0 ] * 16 380 | elif len(iv) != 16: 381 | raise ValueError('initialization vector must be 16 bytes') 382 | else: 383 | self._last_cipherblock = _string_to_bytes(iv) 384 | 385 | AESBlockModeOfOperation.__init__(self, key) 386 | 387 | def encrypt(self, plaintext): 388 | if len(plaintext) != 16: 389 | raise ValueError('plaintext block must be 16 bytes') 390 | 391 | plaintext = _string_to_bytes(plaintext) 392 | precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ] 393 | self._last_cipherblock = self._aes.encrypt(precipherblock) 394 | 395 | return _bytes_to_string(self._last_cipherblock) 396 | 397 | def decrypt(self, ciphertext): 398 | if len(ciphertext) != 16: 399 | raise ValueError('ciphertext block must be 16 bytes') 400 | 401 | cipherblock = _string_to_bytes(ciphertext) 402 | plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ] 403 | self._last_cipherblock = cipherblock 404 | 405 | return _bytes_to_string(plaintext) 406 | 407 | 408 | 409 | class AESModeOfOperationCFB(AESSegmentModeOfOperation): 410 | '''AES Cipher Feedback Mode of Operation. 411 | 412 | o A stream-cipher, so input does not need to be padded to blocks, 413 | but does need to be padded to segment_size 414 | 415 | Also see: 416 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29 417 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.3''' 418 | 419 | 420 | name = "Cipher Feedback (CFB)" 421 | 422 | def __init__(self, key, iv, segment_size = 1): 423 | if segment_size == 0: segment_size = 1 424 | 425 | if iv is None: 426 | self._shift_register = [ 0 ] * 16 427 | elif len(iv) != 16: 428 | raise ValueError('initialization vector must be 16 bytes') 429 | else: 430 | self._shift_register = _string_to_bytes(iv) 431 | 432 | self._segment_bytes = segment_size 433 | 434 | AESBlockModeOfOperation.__init__(self, key) 435 | 436 | segment_bytes = property(lambda s: s._segment_bytes) 437 | 438 | def encrypt(self, plaintext): 439 | if len(plaintext) % self._segment_bytes != 0: 440 | raise ValueError('plaintext block must be a multiple of segment_size') 441 | 442 | plaintext = _string_to_bytes(plaintext) 443 | 444 | # Break block into segments 445 | encrypted = [ ] 446 | for i in xrange(0, len(plaintext), self._segment_bytes): 447 | plaintext_segment = plaintext[i: i + self._segment_bytes] 448 | xor_segment = self._aes.encrypt(self._shift_register)[:len(plaintext_segment)] 449 | cipher_segment = [ (p ^ x) for (p, x) in zip(plaintext_segment, xor_segment) ] 450 | 451 | # Shift the top bits out and the ciphertext in 452 | self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment) 453 | 454 | encrypted.extend(cipher_segment) 455 | 456 | return _bytes_to_string(encrypted) 457 | 458 | def decrypt(self, ciphertext): 459 | if len(ciphertext) % self._segment_bytes != 0: 460 | raise ValueError('ciphertext block must be a multiple of segment_size') 461 | 462 | ciphertext = _string_to_bytes(ciphertext) 463 | 464 | # Break block into segments 465 | decrypted = [ ] 466 | for i in xrange(0, len(ciphertext), self._segment_bytes): 467 | cipher_segment = ciphertext[i: i + self._segment_bytes] 468 | xor_segment = self._aes.encrypt(self._shift_register)[:len(cipher_segment)] 469 | plaintext_segment = [ (p ^ x) for (p, x) in zip(cipher_segment, xor_segment) ] 470 | 471 | # Shift the top bits out and the ciphertext in 472 | self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment) 473 | 474 | decrypted.extend(plaintext_segment) 475 | 476 | return _bytes_to_string(decrypted) 477 | 478 | 479 | 480 | class AESModeOfOperationOFB(AESStreamModeOfOperation): 481 | '''AES Output Feedback Mode of Operation. 482 | 483 | o A stream-cipher, so input does not need to be padded to blocks, 484 | allowing arbitrary length data. 485 | o A bit twiddled in the cipher text, twiddles the same bit in the 486 | same bit in the plain text, which can be useful for error 487 | correction techniques. 488 | 489 | Also see: 490 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29 491 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.4''' 492 | 493 | 494 | name = "Output Feedback (OFB)" 495 | 496 | def __init__(self, key, iv = None): 497 | if iv is None: 498 | self._last_precipherblock = [ 0 ] * 16 499 | elif len(iv) != 16: 500 | raise ValueError('initialization vector must be 16 bytes') 501 | else: 502 | self._last_precipherblock = _string_to_bytes(iv) 503 | 504 | self._remaining_block = [ ] 505 | 506 | AESBlockModeOfOperation.__init__(self, key) 507 | 508 | def encrypt(self, plaintext): 509 | encrypted = [ ] 510 | for p in _string_to_bytes(plaintext): 511 | if len(self._remaining_block) == 0: 512 | self._remaining_block = self._aes.encrypt(self._last_precipherblock) 513 | self._last_precipherblock = [ ] 514 | precipherbyte = self._remaining_block.pop(0) 515 | self._last_precipherblock.append(precipherbyte) 516 | cipherbyte = p ^ precipherbyte 517 | encrypted.append(cipherbyte) 518 | 519 | return _bytes_to_string(encrypted) 520 | 521 | def decrypt(self, ciphertext): 522 | # AES-OFB is symetric 523 | return self.encrypt(ciphertext) 524 | 525 | 526 | 527 | class AESModeOfOperationCTR(AESStreamModeOfOperation): 528 | '''AES Counter Mode of Operation. 529 | 530 | o A stream-cipher, so input does not need to be padded to blocks, 531 | allowing arbitrary length data. 532 | o The counter must be the same size as the key size (ie. len(key)) 533 | o Each block independant of the other, so a corrupt byte will not 534 | damage future blocks. 535 | o Each block has a uniue counter value associated with it, which 536 | contributes to the encrypted value, so no data patterns are 537 | leaked. 538 | o Also known as: Counter Mode (CM), Integer Counter Mode (ICM) and 539 | Segmented Integer Counter (SIC 540 | 541 | Security Notes: 542 | o This method (and CBC) ARE recommended. 543 | o Each message block is associated with a counter value which must be 544 | unique for ALL messages with the same key. Otherwise security may be 545 | compromised. 546 | 547 | Also see: 548 | 549 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29 550 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.5 551 | and Appendix B for managing the initial counter''' 552 | 553 | 554 | name = "Counter (CTR)" 555 | 556 | def __init__(self, key, counter = None): 557 | AESBlockModeOfOperation.__init__(self, key) 558 | 559 | if counter is None: 560 | counter = Counter() 561 | 562 | self._counter = counter 563 | self._remaining_counter = [ ] 564 | 565 | def encrypt(self, plaintext): 566 | while len(self._remaining_counter) < len(plaintext): 567 | self._remaining_counter += self._aes.encrypt(self._counter.value) 568 | self._counter.increment() 569 | 570 | plaintext = _string_to_bytes(plaintext) 571 | 572 | encrypted = [ (p ^ c) for (p, c) in zip(plaintext, self._remaining_counter) ] 573 | self._remaining_counter = self._remaining_counter[len(encrypted):] 574 | 575 | return _bytes_to_string(encrypted) 576 | 577 | def decrypt(self, crypttext): 578 | # AES-CTR is symetric 579 | return self.encrypt(crypttext) 580 | 581 | 582 | # Simple lookup table for each mode 583 | AESModesOfOperation = dict( 584 | ctr = AESModeOfOperationCTR, 585 | cbc = AESModeOfOperationCBC, 586 | cfb = AESModeOfOperationCFB, 587 | ecb = AESModeOfOperationECB, 588 | ofb = AESModeOfOperationOFB, 589 | ) 590 | --------------------------------------------------------------------------------