├── CRCFix.py ├── LICENSE ├── README.md ├── TOTPGen.py ├── hbclabel.py └── smali2frida.py /CRCFix.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ CRCFix v2.19 by Kirlif' """ 4 | from os import path 5 | from io import BytesIO 6 | from sys import exit 7 | 8 | 9 | def ifb(b): 10 | return int.from_bytes(b, "little") 11 | 12 | 13 | class ZIPElement: 14 | def __init__(self, name): 15 | self.name = name 16 | self.cd_lmt_offset, self.lf_lmt_offset, self.lmt = None, None, None 17 | self.cd_lmd_offset, self.lf_lmd_offset, self.lmd = None, None, None 18 | self.cd_crc_offset, self.lf_crc_offset, self.crc = None, None, None 19 | 20 | def dos_date(self): 21 | date = ifb(self.lmd) 22 | d = f"{0x7bc + ((date >> 0x9) & 0x7f)}-" 23 | d += f"0{(date >> 0x5) & 0xf}-"[-3:] 24 | d += f"0{date & 0x1f}"[-2:] 25 | return d 26 | 27 | def dos_time(self): 28 | time = ifb(self.lmt) 29 | t = f"0{((time >> 0x11) & 0x1f)}:"[-3:] 30 | t += f"0{((time >> 0x5) & 0x3f)}:"[-3:] 31 | t += f"0{2*(time & 0x1f)}"[-2:] 32 | return t 33 | 34 | 35 | class CRCFix: 36 | END_OF_CENTRAL_SIG = b"PK\x05\x06" 37 | CENTRAL_HEADER_SIG = b"PK\x01\x02" 38 | LOCAL_HEADER_SIG = b"PK\x03\x04" 39 | 40 | def __init__(self, src, tar, fix_crc, auto, timestamp, overwrite): 41 | self.src = src 42 | self.tar = tar 43 | self.fix_crc = fix_crc 44 | self.auto = auto 45 | self.timestamp = timestamp 46 | self.overwrite = overwrite 47 | self.fixed = list() 48 | self.last_dex_elem = None 49 | self.last_dex_n = 0 50 | self.src_elems = self.get_src_elems() 51 | self.tar_elems = self.get_elems(tar) 52 | 53 | def get_src_elems(self): 54 | elems_dict = {} 55 | for s in self.src: 56 | elems = self.get_elems(s) 57 | for k in iter(elems): 58 | if not k in elems_dict: 59 | elems_dict[k] = elems[k] 60 | return elems_dict 61 | 62 | def get_elems(self, apk): 63 | elems = {} 64 | with open(apk, "rb") as g: 65 | f = BytesIO(g.read()) 66 | f.seek(-22, 2) 67 | meocd, central_offset = f.tell(), None 68 | while f.tell() >= 0: 69 | if f.read(4) == self.END_OF_CENTRAL_SIG: 70 | f.seek(8, 1) 71 | size_of_cd = ifb(f.read(4)) 72 | central_offset = ifb(f.read(4)) 73 | break 74 | if meocd - f.tell() > 1 << 16: 75 | break 76 | f.seek(-5, 1) 77 | if central_offset is None: 78 | exit(f"\nEOCD not found!\n{path.basename(apk)} is damaged or not an APK.\n") 79 | f.seek(central_offset) 80 | while f.tell() < central_offset + size_of_cd: 81 | if not f.read(4) == self.CENTRAL_HEADER_SIG: 82 | exit("Central Header not found!\n") 83 | f.seek(8, 1) 84 | lmt_offset, cd_lmt = f.tell(), f.read(2) 85 | lmd_offset, cd_lmd = f.tell(), f.read(2) 86 | crc_offset, cd_crc = f.tell(), f.read(4) 87 | f.seek(8, 1) 88 | file_name_length = ifb(f.read(2)) 89 | extra_field_length = ifb(f.read(2)) 90 | file_comment_length = ifb(f.read(2)) 91 | f.seek(8, 1) 92 | relative_offset_of_local_header = ifb(f.read(4)) 93 | file_name = f.read(file_name_length).decode() 94 | zel = ZIPElement(file_name) 95 | zel.cd_lmt_offset, zel.lmt = lmt_offset, cd_lmt 96 | zel.cd_lmd_offset, zel.lmd = lmd_offset, cd_lmd 97 | zel.cd_crc_offset, zel.crc = crc_offset, cd_crc 98 | of = f.tell() 99 | f.seek(relative_offset_of_local_header) 100 | if not f.read(4) == self.LOCAL_HEADER_SIG: 101 | exit("Local Header not found!\n") 102 | f.seek(6, 1) 103 | zel.lf_lmt_offset, lf_lmt = f.tell(), f.read(2) 104 | zel.lf_lmd_offset, lf_lmd = f.tell(), f.read(2) 105 | zel.lf_crc_offset, lf_crc = f.tell(), f.read(4) 106 | f.seek(of) 107 | if not all([cd_lmt == lf_lmt, cd_lmd == lf_lmd, cd_crc == lf_crc]): 108 | exit("Headers don't match!\n") 109 | if file_name.startswith("classes") and file_name.endswith(".dex"): 110 | if apk is self.src[0]: 111 | d = zel.name[7:-4] 112 | n = 1 if not d else int(d) 113 | if self.last_dex_elem is None or n > self.last_dex_n: 114 | self.last_dex_elem = zel 115 | self.last_dex_n = n 116 | elif apk is self.tar and not file_name in self.src_elems: 117 | self.src_elems[file_name] = self.last_dex_elem 118 | elems[file_name] = zel 119 | f.seek(extra_field_length + file_comment_length, 1) 120 | return elems 121 | 122 | def print(self): 123 | if self.fixed: 124 | elems = sorted([elem for elem in self.fixed], key=lambda elem: elem.name) 125 | ml = max(len(elem.name) for elem in elems) + 4 126 | print( 127 | "{:{ml}}{:<12}{:<12}{:<23}".format( 128 | "File Name", "CRC", "FIX", "Modified", ml=ml 129 | ) 130 | ) 131 | for e in elems: 132 | e_e = self.src_elems[e.name] 133 | print( 134 | "{:{ml}}{:<12}{:<12}{:<23}".format( 135 | e.name, 136 | hex(ifb(e.crc))[2:], 137 | hex(ifb(e_e.crc))[2:], 138 | f"{e_e.dos_date()} {e_e.dos_time()}", 139 | ml=ml, 140 | ) 141 | ) 142 | print( 143 | f"\nInput: {self.tar}\nOutput: {self.apk_crc}\n\nFixed CRCs: {len(elems)}\n" 144 | ) 145 | else: 146 | print(f"Input: {self.tar}\nOutput: {self.apk_crc}\n") 147 | 148 | def fix(self): 149 | root, ext = path.splitext(self.tar) 150 | self.apk_crc = f'{root}{"" if self.overwrite else "_crc"}{ext}' 151 | print("\n\u2728 CRCFix 2.19 by Kirlif' \u2728\n") 152 | with open(self.tar, "rb") as g: 153 | f = bytearray(g.read()) 154 | with open(self.apk_crc, "wb") as g: 155 | for elem in self.tar_elems.values(): 156 | try: 157 | if self.timestamp: 158 | lmt = self.src_elems[elem.name].lmt 159 | f[elem.cd_lmt_offset : elem.cd_lmt_offset + 2] = lmt 160 | f[elem.lf_lmt_offset : elem.lf_lmt_offset + 2] = lmt 161 | lmd = self.src_elems[elem.name].lmd 162 | f[elem.cd_lmd_offset : elem.cd_lmd_offset + 2] = lmd 163 | f[elem.lf_lmd_offset : elem.lf_lmd_offset + 2] = lmd 164 | crc = self.src_elems[elem.name].crc 165 | if ( 166 | self.fix_crc 167 | and not self.tar_elems[elem.name].crc == crc 168 | and ( 169 | self.auto 170 | or elem.name.startswith("classes") 171 | and elem.name.endswith(".dex") 172 | ) 173 | ): 174 | self.fixed.append(elem) 175 | f[elem.cd_crc_offset : elem.cd_crc_offset + 4] = crc 176 | f[elem.lf_crc_offset : elem.lf_crc_offset + 4] = crc 177 | except KeyError: 178 | pass # ~ Worried about this? 179 | g.write(f) 180 | self.print() 181 | 182 | 183 | if __name__ == "__main__": 184 | import argparse 185 | 186 | parser = argparse.ArgumentParser( 187 | description="""CRCFix restores files CRC and date/time of a modified APK""", 188 | formatter_class=argparse.RawTextHelpFormatter, 189 | ) 190 | parser.add_argument("-v", "--version", action="version", version="CRCFix v2.19") 191 | parser.add_argument( 192 | "source", nargs="+", help="APKs source paths (base first)", type=str 193 | ) 194 | parser.add_argument("target", help="APK target path", type=str) 195 | parser.add_argument("-c", action="store_false", help="do not restore CRCs") 196 | parser.add_argument("-d", action="store_false", help="apply on dex files only") 197 | parser.add_argument("-t", action="store_false", help="do not restore date/time") 198 | parser.add_argument("-f", action="store_true", help="overwrite target") 199 | args = parser.parse_args() 200 | CRCFix( 201 | args.source, args.target, args.c, args.d, args.t, args.f 202 | ).fix() if args.c or args.d and args.t else parser.print_help() 203 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2024 github.com/Kirlif 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python-Stuff 2 | -------------------------------------------------------------------------------- /TOTPGen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from hashlib import sha1 5 | from time import time 6 | from sys import argv 7 | 8 | ''' 9 | TOTPGen 10 | January 2025 11 | 12 | This piece of code to generate your TOTP passwords. 13 | Works with any 2FA ;) 14 | 15 | Usage: 16 | ~$ python3 TOTPGen.py key_name 17 | where key_name is the name associated to the key in the KEYS dictionary. 18 | 19 | ~$ python3 TOTPGen.py 20 | If no key_name is passed to the CLI "github" is used by default. 21 | We can change this default setting. See below: 22 | default_key = "github" # default key 23 | 24 | Add your keys to the KEYS dictionary below like this: 25 | "key_name": "key_value", 26 | ''' 27 | 28 | KEYS = { 29 | "github": "ABCDEFGHIJKLMNOP", # Base32 encoded secret key 30 | } 31 | 32 | default_key = "github" # default key 33 | 34 | class TOTPGenerator: 35 | 36 | TIME_STEP = 30 # Time step in seconds 37 | DIGITS = 6 # Number of digits in the TOTP 38 | 39 | def __init__(self, key): 40 | self.secret = key 41 | self.time_index = int(time()) // self.TIME_STEP 42 | totp = self.generate_totp() 43 | print("TOTP:", totp) 44 | 45 | def generate_totp(self): 46 | key = self.decode_base32() 47 | time_bytes = self.time_index.to_bytes(8, byteorder='big') 48 | hash_value = self.compute_hmac(key, time_bytes) 49 | offset = hash_value[-1] & 0x0F 50 | binary = ((hash_value[offset] & 0x7F) << 24) | \ 51 | ((hash_value[offset + 1] & 0xFF) << 16) | \ 52 | ((hash_value[offset + 2] & 0xFF) << 8) | \ 53 | (hash_value[offset + 3] & 0xFF) 54 | otp = binary % (10 ** self.DIGITS) 55 | format_str = f"%0{self.DIGITS}d" 56 | return format_str % otp 57 | 58 | def decode_base32(self): 59 | binary_string = "" 60 | for c in self.secret: 61 | if 'A' <= c <= 'Z': 62 | i = ord(c) - ord('A') 63 | elif '2' <= c <= '7': 64 | i = ord(c) - ord('2') + 26 65 | else: 66 | raise ValueError(f"Invalid base32 char: '{c}'") 67 | binary_string += f"{i:05b}" 68 | byte_length = (len(binary_string) + 7) // 8 69 | bytes_array = bytearray(byte_length) 70 | for i in range(0, len(binary_string), 8): 71 | byte_string = binary_string[i:i + 8] 72 | bytes_array[i // 8] = int(byte_string, 2) 73 | return bytes(bytes_array) 74 | 75 | def compute_hmac(self, key, message): 76 | block_size = 64 77 | if len(key) > block_size: 78 | key = sha1(key).digest() 79 | padded_key = key.ljust(block_size, b'\0') 80 | inner_pad = bytes(b ^ 0x36 for b in padded_key) 81 | outer_pad = bytes(b ^ 0x5C for b in padded_key) 82 | inner_hash = sha1(inner_pad + message).digest() 83 | return sha1(outer_pad + inner_hash).digest() 84 | 85 | if __name__ == "__main__": 86 | key_name = default_key 87 | if len(argv) == 2: 88 | key_name = argv[1] if argv[1] in KEYS else None 89 | if len(argv) <= 2 and key_name: 90 | TOTPGenerator(KEYS[key_name]) 91 | elif not key_name: 92 | print("Key not found!") 93 | -------------------------------------------------------------------------------- /hbclabel.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | ''' 5 | hbclabel injects branch label as comments into Hermes byte code disassemby produced by hbctool. 6 | They are removed once re-assembled. 7 | 8 | Ex: 9 | 10 | Function7654(3 params, 3 registers, 0 symbols): 11 | LoadParam Reg8:2, UInt8:1 12 | LoadConstNull Reg8:0 13 | JmpFalse Addr8:10, Reg8:2 | 14 | ;L_0 | <----- BRANCH (;L_i) 15 | LoadParam Reg8:1, UInt8:2 16 | GetByVal Reg8:0, Reg8:1, Reg8:2 17 | ;L_0: | <------ LABEL (;L_i:) 18 | Ret Reg8:0 19 | EndFunction 20 | 21 | 22 | hbclabel [File] 23 | 24 | If no file is passed, « instruction.hasm » in current dir is proccessed by default. 25 | 26 | Created by Kirlif' 27 | ''' 28 | 29 | import re, shutil, sys 30 | 31 | class hbclabel: 32 | def __init__(self, hasm): 33 | self.hasm = hasm 34 | self.hasm_tmp = hasm+'_lbl' 35 | self.func_list = list() 36 | 37 | shutil.copyfile(self.hasm, self.hasm_tmp) 38 | 39 | self.get_func() 40 | self.write_hasm() 41 | 42 | def get_func(self): 43 | print('- Reading hasm...') 44 | with open(self.hasm_tmp, 'r') as f: 45 | for m in re.finditer(re.compile('Function<.*?EndFunction\n', re.S), f.read()): 46 | self.func_list.append(m) 47 | 48 | def write_hasm(self): 49 | print('- Writing labels...') 50 | l = len(self.func_list) 51 | w, _ = shutil.get_terminal_size() 52 | with open(self.hasm_tmp, 'w') as g: 53 | for i, f in enumerate(self.func_list): 54 | f = f.group()+'\n' 55 | g.write(self.process_func(f)) if '\tAddr' in f else g.write(f) 56 | self.progress_bar(i+1, l, round(w*2/3)) 57 | shutil.move(self.hasm_tmp, self.hasm) 58 | 59 | def process_func(self, f): 60 | f = f.splitlines(True) 61 | g, l, labs = f.copy(), 0, {} 62 | for i in range(len(g)): 63 | if '\tAddr' in g[i]: 64 | tar = int(g[i].split('\tAddr')[1].split(',')[0].split(':')[1].rstrip()) 65 | try: 66 | lpl = i+(self.w_label(tar, g[i:]) if tar > 0 else -(self.w_label(-tar, g[:i][::-1])+2)) 67 | except: 68 | self.exit(f'- Error in {g[0].split("(")[0]} !!!\n{i+1, g[i].strip()}') 69 | if not lpl in labs: 70 | f[lpl+(2 if g[lpl+1].startswith('\t;') else 0)] += f';L_{l}:\n' 71 | labs[lpl] = l 72 | l += 1 73 | if f[i+1] == f';L_{labs[lpl]}\n': 74 | self.exit('- Aborted, labels are already there!') 75 | f[i] = f[i].replace('\n',f'\n;L_{labs[lpl]}\n', 1) 76 | return ''.join(f) 77 | 78 | def w_label(self, tar, sub_f): 79 | ofs = 0 80 | for i in range(len(sub_f)): 81 | l = sub_f[i].strip() 82 | if l.startswith(';') or l == '': 83 | continue 84 | spl = l.split(', ') 85 | for j in range(len(spl)): 86 | k = 1 if j == 0 and not spl[j] in ['AsyncBreakCheck', 'CompleteGenerator', 'Debugger', 'DebuggerCheckBreak', 'StartGenerator', 'Unreachable'] else 0 87 | if '16:' in spl[j]: 88 | ofs += 2+k 89 | elif '32:' in spl[j]: 90 | ofs += 4+k 91 | elif '64:' in spl[j] or 'Double:' in spl[j]: 92 | ofs += 8+k 93 | else: 94 | ofs += 1+k 95 | if ofs == tar: 96 | return i 97 | 98 | def exit(self, m): 99 | from os import remove 100 | remove(self.hasm_tmp) 101 | print(m) 102 | sys.exit() 103 | 104 | def progress_bar(self, i, l, w): 105 | c = round(w*i/l) 106 | print('['+'#'*c+chr(0xb7)*(w-c)+']\r', end='') 107 | 108 | if __name__ == "__main__": 109 | p = len(sys.argv) 110 | if p < 3: 111 | hasm_file = sys.argv[1] if p == 2 else 'instruction.hasm' 112 | print('\u2728 hbclabel by Kirlif\' \u2728') 113 | hbclabel(hasm_file) 114 | print('\nDone.') 115 | -------------------------------------------------------------------------------- /smali2frida.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | ''' 5 | Smali2Frida class generates a list of frida snippet from smali files (snippets). 6 | 7 | smali2frida.py [DIR] > script.js 8 | 9 | If no directory (with smali folders) is passed the current one is used by default. 10 | 11 | 06-03-2022 12 | Created by Kirlif' 13 | ''' 14 | 15 | from os import getcwd, path, scandir 16 | import re 17 | 18 | class Smali2Frida: 19 | class_pattern = re.compile(r'\.class.+?(\S+?;)', re.UNICODE) 20 | method_pattern = re.compile(r'\.method.+?(\S+?)\((\S*?)\)(\S+)', re.UNICODE) 21 | param_pattern = re.compile(r'(\[*L\S+?;|\[+\S|B|C|D|F|I|J|S|Z)', re.UNICODE) 22 | primitives = {"B":"byte", "C":"char", "D":"double", "F":"float", "I":"int", "J":"long", "S":"short", "Z":"boolean"} 23 | 24 | def __init__(self, _dir=getcwd()): 25 | self.root_dir = _dir 26 | self.snippets = [] 27 | self.frida() 28 | 29 | def scan(self, root, rec=True): 30 | for entry in scandir(root): 31 | if entry.is_dir(follow_symlinks=False) and rec: 32 | yield from self.scan(entry.path) 33 | else: 34 | yield entry 35 | 36 | def smali_files(self): 37 | _files = [] 38 | for folder in [entry.path for entry in self.scan(self.root_dir, False) if path.isdir(entry) and path.split(entry)[-1].startswith("smali")]: 39 | _files += [entry.path for entry in self.scan(folder, True) if path.split(entry)[-1].endswith(".smali")] 40 | return _files 41 | 42 | def smali_data(self): 43 | data = {} 44 | for sf in self.smali_files(): 45 | with open(sf, 'r', encoding='utf-8', errors='surrogateescape') as _file: 46 | class_name = '' 47 | for line in _file: 48 | if not class_name: 49 | class_match = self.class_pattern.match(line) 50 | if class_match: 51 | class_name = class_match.group(1)[1:-1].replace('/', '.') 52 | data[class_name] = [] 53 | continue 54 | if class_name: 55 | method_match = self.method_pattern.match(line) 56 | if method_match: 57 | method_name = method_match.group(1) 58 | method_param, find_all = '', re.findall(self.param_pattern, method_match.group(2)) 59 | for p in find_all: 60 | p = p[1:-1].replace('/', '.') if p.startswith('L') else p 61 | p = self.primitives[p] if p in self.primitives else p 62 | method_param += f'\'{p}\', ' 63 | data[class_name].append((method_name, method_param[:-2], len(find_all))) 64 | return {k:data[k] for k in sorted(data) if data[k]} 65 | 66 | def frida(self): 67 | classes = self.smali_data() 68 | l = len(str(len(classes))) 69 | for i, k in enumerate(classes): 70 | methods = classes[k] 71 | klass = f'C{(l*"0"+str(i))[-l:]}' 72 | snippet = f'Java.perform(function() {{\n var {klass} = Java.use("{k}");\n' 73 | for method in methods: 74 | met = '$init' if method[0] == '' else method[0] 75 | apa = ", ".join(f"var{j}" for j in range(method[2])) 76 | snippet += f'\n {klass}.{met}.overload({method[1]}).implementation = function({apa})\n' 77 | snippet += f' {{\n var ret = this.{met}({apa});\n' 78 | snippet += f' console.log("{method[0]}", "called : ", ret);\n }}\n' 79 | snippet += ('})\n'); 80 | self.snippets.append(snippet) 81 | 82 | if __name__ == "__main__": 83 | from sys import argv 84 | _dir = argv[-1] if path.isdir(argv[-1]) else getcwd() 85 | [print(snippet) for snippet in Smali2Frida(_dir).snippets] 86 | 87 | --------------------------------------------------------------------------------