diff --git a/sigtool/__init__.py b/sigtool/__init__.py index 7bae727..cae5f36 100644 --- a/sigtool/__init__.py +++ b/sigtool/__init__.py @@ -19,5 +19,5 @@ "PEMCertificateGenerator", "ColonUpperCase", "OutputFormatter", - "PEMSignatureExtractor" -] \ No newline at end of file + "PEMSignatureExtractor", +] diff --git a/sigtool/apk_info_extractor.py b/sigtool/apk_info_extractor.py index d44b6c4..5291a5d 100644 --- a/sigtool/apk_info_extractor.py +++ b/sigtool/apk_info_extractor.py @@ -2,6 +2,7 @@ import subprocess + class APKInfoExtractor: def __init__(self, apk_path: str): self.apk_path = apk_path @@ -10,23 +11,23 @@ def get_apk_info(self) -> dict: result = subprocess.run( ["aapt", "dump", "badging", self.apk_path], stdout=subprocess.PIPE, - stderr=subprocess.PIPE + stderr=subprocess.PIPE, ) if result.returncode != 0: raise RuntimeError(f"aapt error: {result.stderr.decode()}") output = result.stdout.decode() app_info = {} - for line in output.split('\n'): - if line.startswith('package:'): + for line in output.split("\n"): + if line.startswith("package:"): package_info = line.split() for info in package_info: - if info.startswith('name='): - app_info['package_name'] = info.split('=')[1].strip("'") - elif info.startswith('versionCode='): - app_info['version_code'] = info.split('=')[1].strip("'") - elif info.startswith('versionName='): - app_info['version_name'] = info.split('=')[1].strip("'") - elif line.startswith('application:'): - app_info['app_name'] = line.split("label='")[1].split("'")[0] + if info.startswith("name="): + app_info["package_name"] = info.split("=")[1].strip("'") + elif info.startswith("versionCode="): + app_info["version_code"] = info.split("=")[1].strip("'") + elif info.startswith("versionName="): + app_info["version_name"] = info.split("=")[1].strip("'") + elif line.startswith("application:"): + app_info["app_name"] = line.split("label='")[1].split("'")[0] return app_info diff --git a/sigtool/apk_signature_extractor.py b/sigtool/apk_signature_extractor.py index fb2b0d6..8a2c513 100644 --- a/sigtool/apk_signature_extractor.py +++ b/sigtool/apk_signature_extractor.py @@ -7,6 +7,7 @@ from .pem_signature_extractor import PEMSignatureExtractor + class APKSignatureExtractor: def __init__(self, apk_path): self.apk_path = apk_path @@ -21,41 +22,43 @@ def _find_eocd(self, file): seek_offset = -min(65536, file_size) file.seek(seek_offset, 2) data = file.read(65536) - eocd_offset = data.rfind(b'PK\x05\x06') + eocd_offset = data.rfind(b"PK\x05\x06") if eocd_offset == -1: - raise ValueError("Invalid APK: End of Central Directory signature not found") - eocd = data[eocd_offset:eocd_offset + 22] - return struct.unpack('H', signing_block, index + 2)[0] + length = struct.unpack_from(">H", signing_block, index + 2)[0] sig_length = 4 + length if index + sig_length <= len(signing_block): - return signing_block[index:index + sig_length] + return signing_block[index : index + sig_length] index += sig_length return None - + def _extract_rsa(self): try: - with zipfile.ZipFile(self.apk_path, 'r') as zip_file: + with zipfile.ZipFile(self.apk_path, "r") as zip_file: for file_name in zip_file.namelist(): - if file_name.startswith('META-INF/') and file_name.endswith('.RSA'): + if file_name.startswith("META-INF/") and file_name.endswith(".RSA"): with zip_file.open(file_name) as rsa_file: return rsa_file.read() return None @@ -68,11 +71,11 @@ def _extract_v1_signature(self): rsa_file = self._extract_rsa() if rsa_file is None: return "No RSA file found" - + with tempfile.NamedTemporaryFile(delete=False) as temp_rsa_file: temp_rsa_file.write(rsa_file) temp_rsa_path = temp_rsa_file.name - + try: rsa_extractor = PEMSignatureExtractor() pem_data = rsa_extractor.convert_rsa_to_pem(temp_rsa_path) @@ -81,10 +84,10 @@ def _extract_v1_signature(self): return signature_hex finally: os.remove(temp_rsa_path) - + def extract_signatures(self): try: - with open(self.apk_path, 'rb') as file: + with open(self.apk_path, "rb") as file: try: cd_offset = self._find_eocd(file) block_size = self._find_apk_signing_block(file, cd_offset) @@ -104,4 +107,4 @@ def extract_signatures(self): except FileNotFoundError: return "Error: APK file not found" except Exception as e: - return f"Unexpected error: {str(e)}" \ No newline at end of file + return f"Unexpected error: {str(e)}" diff --git a/sigtool/base64_encoder.py b/sigtool/base64_encoder.py index 24ee390..8173aa6 100644 --- a/sigtool/base64_encoder.py +++ b/sigtool/base64_encoder.py @@ -2,6 +2,7 @@ import base64 + class Base64Encoder: def __init__(self, signature_hex, hashes=None): self.signature_hex = signature_hex @@ -9,14 +10,14 @@ def __init__(self, signature_hex, hashes=None): self.hashes = hashes def encode_signature(self): - encoded_signature = base64.b64encode(self.signature_bytes).decode('utf-8') + encoded_signature = base64.b64encode(self.signature_bytes).decode("utf-8") return encoded_signature def encode_hashes(self): if self.hashes is None: raise ValueError("Hashes are not provided.") encoded_hashes = { - hash_type: base64.b64encode(bytes.fromhex(hash_value)).decode('utf-8') + hash_type: base64.b64encode(bytes.fromhex(hash_value)).decode("utf-8") for hash_type, hash_value in self.hashes.items() } - return encoded_hashes \ No newline at end of file + return encoded_hashes diff --git a/sigtool/colon_uppercase.py b/sigtool/colon_uppercase.py index d062393..b509b47 100644 --- a/sigtool/colon_uppercase.py +++ b/sigtool/colon_uppercase.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- + class ColonUpperCase: def __init__(self, apply_colons=False, convert_uppercase=False): self.apply_colons = apply_colons @@ -7,7 +8,7 @@ def __init__(self, apply_colons=False, convert_uppercase=False): def add_colons_to_hex(self, hex_string: str) -> str: if self.apply_colons: - return ':'.join(hex_string[i:i+2] for i in range(0, len(hex_string), 2)) + return ":".join(hex_string[i : i + 2] for i in range(0, len(hex_string), 2)) return hex_string def convert_to_uppercase(self, string: str) -> str: @@ -16,8 +17,8 @@ def convert_to_uppercase(self, string: str) -> str: return string.lower() def convert_crc32_and_hashcode(self, value: str) -> str: - if value.startswith('0x'): - return '0x' + self.convert_to_uppercase(value[2:]) + if value.startswith("0x"): + return "0x" + self.convert_to_uppercase(value[2:]) return self.convert_to_uppercase(value) def process_signature_hashes(self, hashes: dict) -> dict: @@ -34,4 +35,4 @@ def process_crc32_and_hashcode(self, results: dict) -> dict: processed_results[key] = self.convert_crc32_and_hashcode(value) else: processed_results[key] = value - return processed_results \ No newline at end of file + return processed_results diff --git a/sigtool/crc32_and_hashcode_calculator.py b/sigtool/crc32_and_hashcode_calculator.py index c142d0d..736568c 100644 --- a/sigtool/crc32_and_hashcode_calculator.py +++ b/sigtool/crc32_and_hashcode_calculator.py @@ -2,6 +2,7 @@ import zlib + class CRC32AndHashCodeCalculator: def __init__(self, signature_hex): self.signature_hex = signature_hex @@ -32,11 +33,13 @@ def _calculate_hash_code(self): def calculate_crc32_and_hash_code(self): crc32 = self._calculate_crc32() - crc32 = f"0x{crc32:08x} ({crc32 - 0x100000000 if crc32 >= 0x80000000 else crc32})" - + crc32 = ( + f"0x{crc32:08x} ({crc32 - 0x100000000 if crc32 >= 0x80000000 else crc32})" + ) + hashCode = self._calculate_hash_code() return { "CRC32": crc32, "hashCode": hashCode, - } \ No newline at end of file + } diff --git a/sigtool/main.py b/sigtool/main.py index dc5d2f8..788818f 100644 --- a/sigtool/main.py +++ b/sigtool/main.py @@ -1,5 +1,5 @@ import argparse -import pkg_resources +import importlib.metadata import sys import os import re @@ -17,6 +17,7 @@ from .pem_signature_extractor import PEMSignatureExtractor from .sighooks.mt_enhanced_hook.mthook_generator import MTHookGenerator + class SigTool: def __init__(self, apk_path, args): self.apk_path = apk_path @@ -26,24 +27,34 @@ def __init__(self, apk_path, args): self.signature_hex = None self.hashes = None self.colon_upper = ColonUpperCase( - apply_colons='-c' in args or '-uc' in args or '-fc' in args or '-fuc' in args, - convert_uppercase='-u' in args or '-uc' in args or '-fu' in args or '-fuc' in args + apply_colons="-c" in args + or "-uc" in args + or "-fc" in args + or "-fuc" in args, + convert_uppercase="-u" in args + or "-uc" in args + or "-fu" in args + or "-fuc" in args, ) self.results = {} def check_file_type(self): try: - with open(self.apk_path, 'rb') as file: + with open(self.apk_path, "rb") as file: content = file.read(1024) - - if b'-----BEGIN CERTIFICATE-----' in content: - return 'pem' - elif content.startswith(b'0\x82'): - return 'rsa' - elif content.startswith(b'\x50\x4B\x03\x04'): - return 'apk' + + if b"-----BEGIN CERTIFICATE-----" in content: + return "pem" + elif content.startswith(b"0\x82"): + return "rsa" + elif content.startswith(b"\x50\x4b\x03\x04"): + return "apk" else: - print(self.formatter.format_error(f"Error: The APK, RSA or PEM file at '{self.apk_path}' does not exist or is not accessible.")) + print( + self.formatter.format_error( + f"Error: The APK, RSA or PEM file at '{self.apk_path}' does not exist or is not accessible." + ) + ) sys.exit(1) except Exception as e: print(self.formatter.format_error(f"Error: {e}")) @@ -53,29 +64,33 @@ def extract_apk_info(self): try: apk_info = self.extractor.get_apk_info() return { - 'App Name': apk_info.get('app_name', 'N/A'), - 'Package Name': apk_info.get('package_name', 'N/A'), - 'Version': apk_info.get('version_name', 'N/A'), - 'Build': apk_info.get('version_code', 'N/A') + "App Name": apk_info.get("app_name", "N/A"), + "Package Name": apk_info.get("package_name", "N/A"), + "Version": apk_info.get("version_name", "N/A"), + "Build": apk_info.get("version_code", "N/A"), } except Exception as e: - return {'Error': self.formatter.format_error(f"Failed to extract APK info: {e}")} + return { + "Error": self.formatter.format_error(f"Failed to extract APK info: {e}") + } def extract_signature_hex(self): try: file_type = self.check_file_type() - if file_type == 'pem': + if file_type == "pem": extractor = PEMSignatureExtractor(pem_path=self.apk_path) - elif file_type == 'rsa': + elif file_type == "rsa": rsa_extractor = PEMSignatureExtractor() pem_data = rsa_extractor.convert_rsa_to_pem(self.apk_path) extractor = PEMSignatureExtractor(pem_data=pem_data) else: extractor = APKSignatureExtractor(self.apk_path) - + self.signature_hex = extractor.extract_signatures() if self.signature_hex: - self.signature_hex = self.colon_upper.convert_to_uppercase(self.signature_hex) + self.signature_hex = self.colon_upper.convert_to_uppercase( + self.signature_hex + ) except Exception as e: print(self.formatter.format_error(f"Error: {e}")) sys.exit(1) @@ -88,18 +103,18 @@ def encode_base64(self, hashes): return encoder.encode_signature(), encoder.encode_hashes() def format_encoded_signature(self, encoded_signature): - return re.sub(r'((.){1,76})', r'\1\\n', encoded_signature) + return re.sub(r"((.){1,76})", r"\1\\n", encoded_signature) def save_to_file(self, content, output_path): try: if not os.path.isabs(output_path): output_path = os.path.abspath(output_path) - with open(output_path, 'w') as file: + with open(output_path, "w") as file: file.write(content) success_message = f"Output saved to {output_path}" - print('\n', self.formatter.format_with_style(success_message, 'key')) + print("\n", self.formatter.format_with_style(success_message, "key")) except (FileNotFoundError, PermissionError, IsADirectoryError, OSError) as e: print(self.formatter.format_error(f"Error: {e}")) sys.exit(1) @@ -109,11 +124,15 @@ def add_section(self, section_name, section_content): def print_apk_info(self): if not os.path.isfile(self.apk_path): - print(self.formatter.format_error(f"Error: The APK file at '{self.apk_path}' does not exist or is not accessible.")) + print( + self.formatter.format_error( + f"Error: The APK file at '{self.apk_path}' does not exist or is not accessible." + ) + ) sys.exit(1) apk_info = self.extract_apk_info() - if 'Error' in apk_info: + if "Error" in apk_info: print(self.formatter.format_error(f"Error: {apk_info['Error']}")) sys.exit(1) @@ -128,17 +147,25 @@ def print_default_results(self): self.extract_signature_hex() if self.signature_hex is None: - print(self.formatter.format_error("Error: Failed to extract signature hex from the APK file.")) + print( + self.formatter.format_error( + "Error: Failed to extract signature hex from the APK file." + ) + ) sys.exit(1) hashes = self.calculate_hashes() - crc32_hashcode = CRC32AndHashCodeCalculator(self.signature_hex).calculate_crc32_and_hash_code() + crc32_hashcode = CRC32AndHashCodeCalculator( + self.signature_hex + ).calculate_crc32_and_hash_code() formatted_hashes = self.colon_upper.process_signature_hashes(hashes) formatted_results = self.colon_upper.process_crc32_and_hashcode(crc32_hashcode) output = self.formatter.format_section("Calculated Hashes", formatted_hashes) - output += self.formatter.format_section("CRC32 and hashCode Results", formatted_results) + output += self.formatter.format_section( + "CRC32 and hashCode Results", formatted_results + ) output += self.formatter.format_header("Certificate Bytes") output += self.formatter.format_result_two("toCharsString", self.signature_hex) @@ -156,16 +183,23 @@ def print_encoded_results(self): output += self.formatter.format_header("Base64 Encoded Certificate") output += self.formatter.format_result_two("Certificate", encoded_signature) output += self.formatter.format_result_two( - "\nsignatures (Add '\\n' per 76 characters)", formatted_encoded_signature.lstrip("\n")) + "\nsignatures (Add '\\n' per 76 characters)", + formatted_encoded_signature.lstrip("\n"), + ) self.add_section("Base64 Encoded Hashes", encoded_hashes) self.add_section("Base64 Encoded Certificate", encoded_signature) - self.add_section("signatures (Add '\n' per 76 characters)", formatted_encoded_signature.replace('\\n', '\n')) + self.add_section( + "signatures (Add '\n' per 76 characters)", + formatted_encoded_signature.replace("\\n", "\n"), + ) return output def print_pem_results(self): - pem_certificate = PEMCertificateGenerator(self.signature_hex).generate_certificate_details() + pem_certificate = PEMCertificateGenerator( + self.signature_hex + ).generate_certificate_details() output = self.formatter.format_header("PEM Certificate Details\n") output += self.formatter.format_divider() @@ -176,7 +210,9 @@ def print_pem_results(self): return output def print_smali_results(self): - smali_representation = SmaliByteArrayGenerator(self.signature_hex).generate_smali() + smali_representation = SmaliByteArrayGenerator( + self.signature_hex + ).generate_smali() output = self.formatter.format_header("Byte Array Smali Format") output += self.formatter.format_result_two("toByteArray", smali_representation) @@ -189,67 +225,94 @@ def generate_hook(self, encoded_cert, pkg_name, output_path): encoded_zip=None, apk_path=self.apk_path, pkg_name=pkg_name, - encoded_cert=encoded_cert + encoded_cert=encoded_cert, ) mthook_gen.process(output_path) - + def run(self): try: output = "" - + file_type = self.check_file_type() - - if file_type == 'apk': + + if file_type == "apk": output = self.print_apk_info() - + self.extract_signature_hex() - + if self.signature_hex is None: - print(self.formatter.format_error("Error: Signature hex is None, cannot proceed.")) + print( + self.formatter.format_error( + "Error: Signature hex is None, cannot proceed." + ) + ) sys.exit(1) - - encoded_signature, encoded_hashes = self.encode_base64(self.calculate_hashes()) - - if '-hmt' in self.args and file_type == 'apk': + + encoded_signature, encoded_hashes = self.encode_base64( + self.calculate_hashes() + ) + + if "-hmt" in self.args and file_type == "apk": apk_info = self.extract_apk_info() - app_name = apk_info.get('App Name', 'N/A') - pkg_name = apk_info.get('Package Name', 'N/A') - version = apk_info.get('Version', 'N/A') - build = apk_info.get('Build', 'N/A') - + app_name = apk_info.get("App Name", "N/A") + pkg_name = apk_info.get("Package Name", "N/A") + version = apk_info.get("Version", "N/A") + build = apk_info.get("Build", "N/A") + output_filename = f"mthook_{app_name}_{version}({build}).zip" - output_directory = self.args[self.args.index('-o') + 1] if '-o' in self.args else os.path.dirname(os.path.abspath(self.apk_path)) - + output_directory = ( + self.args[self.args.index("-o") + 1] + if "-o" in self.args + else os.path.dirname(os.path.abspath(self.apk_path)) + ) + if not os.path.isdir(output_directory): - print(self.formatter.format_error(f"Error: The specified path '{output_directory}' is not a valid directory.")) - print(self.formatter.format_error(f"Please provide a valid directory where the {output_filename} file can be saved.")) + print( + self.formatter.format_error( + f"Error: The specified path '{output_directory}' is not a valid directory." + ) + ) + print( + self.formatter.format_error( + f"Please provide a valid directory where the {output_filename} file can be saved." + ) + ) sys.exit(1) - + output_path = os.path.join(output_directory, output_filename) formatted_signature = self.format_encoded_signature(encoded_signature) self.generate_hook(formatted_signature, pkg_name, output_path) - - print(self.formatter.format_with_style(f"\nHook exported to {output_path}", 'key')) + + print( + self.formatter.format_with_style( + f"\nHook exported to {output_path}", "key" + ) + ) sys.exit(0) - - if '-f' in self.args or '-fc' in self.args or '-fu' in self.args or '-fuc' in self.args: + + if ( + "-f" in self.args + or "-fc" in self.args + or "-fu" in self.args + or "-fuc" in self.args + ): output += self.print_default_results() output += self.print_encoded_results() output += self.print_pem_results() output += self.print_smali_results() - elif '-e' in self.args: + elif "-e" in self.args: output += self.print_encoded_results() - elif '-p' in self.args: + elif "-p" in self.args: output += self.print_pem_results() - elif '-a' in self.args: + elif "-a" in self.args: output += self.print_smali_results() else: output += self.print_default_results() - - if '-o' in self.args: - output_path = self.args[self.args.index('-o') + 1] - - if not output_path.endswith('.json'): + + if "-o" in self.args: + output_path = self.args[self.args.index("-o") + 1] + + if not output_path.endswith(".json"): logo_two = self.formatter.display_logo_two() output = self.formatter.remove_ansi(output) output = logo_two + output @@ -269,11 +332,12 @@ def run(self): except Exception as e: print(self.formatter.format_error(f"Error: {e}")) sys.exit(1) - + + def get_version(): try: - return pkg_resources.get_distribution("sigtool").version - except pkg_resources.DistributionNotFound: + return importlib.metadata.version("sigtool") + except importlib.metadata.PackageNotFoundError: return "Unknown version" @@ -317,23 +381,55 @@ def main(): ), formatter_class=argparse.RawTextHelpFormatter, usage=usage_msg, - epilog=example_usage + epilog=example_usage, + ) + + parser.add_argument("apk_path", type=str, help="Path to the APK file") + parser.add_argument("-u", action="store_true", help="Convert output to uppercase") + parser.add_argument( + "-c", action="store_true", help="Add colons to certificate hashes" + ) + parser.add_argument( + "-uc", + action="store_true", + help="Add colons to hashes and convert output to uppercase", + ) + parser.add_argument("-e", action="store_true", help="Encode output in Base64") + parser.add_argument("-p", action="store_true", help="Parse PEM Certificate") + parser.add_argument("-a", action="store_true", help="Generate Smali Byte Array") + parser.add_argument("-f", action="store_true", help="Print All Information") + parser.add_argument( + "-fc", + action="store_true", + help="Add colons to hashes and print all information", + ) + parser.add_argument( + "-fu", + action="store_true", + help="Convert output to uppercase and print all information", + ) + parser.add_argument( + "-fuc", + action="store_true", + help="Add colons to hashes, convert output to uppercase and print all information", + ) + parser.add_argument( + "-hmt", + action="store_true", + help="Generate and export hook of MT enhanced version", + ) + parser.add_argument( + "-o", + type=str, + help="Output results to a specified file path. If the path ends with '.json', results will be saved in JSON format.", + ) + parser.add_argument( + "-v", + "--version", + action="version", + version=f"%(prog)s {version}", + help="Show program's version number and exit", ) - - parser.add_argument('apk_path', type=str, help="Path to the APK file") - parser.add_argument('-u', action='store_true', help="Convert output to uppercase") - parser.add_argument('-c', action='store_true', help="Add colons to certificate hashes") - parser.add_argument('-uc', action='store_true', help="Add colons to hashes and convert output to uppercase") - parser.add_argument('-e', action='store_true', help="Encode output in Base64") - parser.add_argument('-p', action='store_true', help="Parse PEM Certificate") - parser.add_argument('-a', action='store_true', help="Generate Smali Byte Array") - parser.add_argument('-f', action='store_true', help="Print All Information") - parser.add_argument('-fc', action='store_true', help="Add colons to hashes and print all information") - parser.add_argument('-fu', action='store_true', help="Convert output to uppercase and print all information") - parser.add_argument('-fuc', action='store_true', help="Add colons to hashes, convert output to uppercase and print all information") - parser.add_argument('-hmt', action='store_true', help="Generate and export hook of MT enhanced version") - parser.add_argument('-o', type=str, help="Output results to a specified file path. If the path ends with '.json', results will be saved in JSON format.") - parser.add_argument('-v', '--version', action='version', version=f'%(prog)s {version}', help="Show program's version number and exit") args = parser.parse_args() @@ -341,22 +437,35 @@ def main(): parser.print_help() sys.exit(1) - valid_args = {'-u', '-c', '-uc', '-e', '-p', '-a', '-f', '-fc', '-fu', '-fuc', '-hmt', '-o'} + valid_args = { + "-u", + "-c", + "-uc", + "-e", + "-p", + "-a", + "-f", + "-fc", + "-fu", + "-fuc", + "-hmt", + "-o", + } if len(sys.argv) > 2: if sys.argv[2] not in valid_args: parser.print_help() sys.exit(1) - if sys.argv[2] == '-o': + if sys.argv[2] == "-o": if len(sys.argv) != 4: parser.print_help() sys.exit(1) else: - if len(sys.argv) > 3 and sys.argv[3] != '-o': + if len(sys.argv) > 3 and sys.argv[3] != "-o": parser.print_help() sys.exit(1) - if len(sys.argv) == 5 and sys.argv[3] == '-o' and not sys.argv[4]: + if len(sys.argv) == 5 and sys.argv[3] == "-o" and not sys.argv[4]: parser.print_help() sys.exit(1) @@ -364,5 +473,5 @@ def main(): sig_tool.run() -if __name__ == '__main__': - main() \ No newline at end of file +if __name__ == "__main__": + main() diff --git a/sigtool/output_formatter.py b/sigtool/output_formatter.py index 434454f..98356d7 100644 --- a/sigtool/output_formatter.py +++ b/sigtool/output_formatter.py @@ -2,17 +2,18 @@ import re + class OutputFormatter: def __init__(self): self.styles = { - "header": "\033[35m", # Magenta - "key": "\033[92m", # Green - "value": "\033[93m", # Yellow/Gold - "divider": "\033[94m", # Blue - "error": "\033[91m", # Red - "bold": "\033[1m", # Bold - "underline": "\033[4m", # Underline - "endc": "\033[0m" # End coloring + "header": "\033[35m", # Magenta + "key": "\033[92m", # Green + "value": "\033[93m", # Yellow/Gold + "divider": "\033[94m", # Blue + "error": "\033[91m", # Red + "bold": "\033[1m", # Bold + "underline": "\033[4m", # Underline + "endc": "\033[0m", # End coloring } self.logo_one = """\n+----------Welcome to-------------+ | | @@ -42,9 +43,9 @@ def __init__(self): "GitHub Repository": "https://github.com/muhammadrizwan87/sigtool", "Telegram Channel": "https://TDOhex.t.me", "Second Channel": "https://Android_Patches.t.me", - "Discussion Group": "https://TDOhex_Discussion.t.me" + "Discussion Group": "https://TDOhex_Discussion.t.me", } - + def format_with_style(self, text: str, style: str) -> str: return f"{self.styles.get(style, '')}{text}{self.styles['endc']}" @@ -62,18 +63,21 @@ def format_divider(self) -> str: def display_logo_one(self) -> str: return self.format_with_style(self.logo_one, "header") - + def display_logo_two(self) -> str: return self.logo_two def get_meta_data(self) -> str: - meta_lines = [f"{self.format_key(k)}: {self.format_value(v)}" for k, v in self.meta_data.items()] + meta_lines = [ + f"{self.format_key(k)}: {self.format_value(v)}" + for k, v in self.meta_data.items() + ] meta_content = "\n".join(meta_lines) return f"{self.display_logo_two()}\n{self.format_divider()}\n{meta_content}\n{self.format_divider()}" - + def format_result(self, key: str, value: str) -> str: return f"{self.format_key(key)}: {self.format_value(value)}" - + def format_result_two(self, key: str, value: str) -> str: return f"\n{self.format_divider()}\n{self.format_key(key)}: \n{self.format_value(value)}\n{self.format_divider()}\n" @@ -84,8 +88,8 @@ def format_section(self, title: str, results: dict) -> str: return f"{section_header}\n{self.format_divider()}\n{results_content}\n{self.format_divider()}" def remove_ansi(self, text: str) -> str: - ansi_escape = re.compile(r'\x1b[^m]*m') - return ansi_escape.sub('', text) + ansi_escape = re.compile(r"\x1b[^m]*m") + return ansi_escape.sub("", text) def format_error(self, error_message: str) -> str: - return self.format_with_style(f"\n{error_message}", 'error') \ No newline at end of file + return self.format_with_style(f"\n{error_message}", "error") diff --git a/sigtool/pem_certificate_generator.py b/sigtool/pem_certificate_generator.py index 14277e6..6f5c3b1 100644 --- a/sigtool/pem_certificate_generator.py +++ b/sigtool/pem_certificate_generator.py @@ -3,6 +3,7 @@ import base64 import subprocess + class PEMCertificateGenerator: def __init__(self, signature_hex): self.signature_hex = signature_hex @@ -12,7 +13,7 @@ def _create_pem_certificate(self): if not self.signature_hex: raise ValueError("Signature hex is required to create the PEM certificate.") signature_bytes = bytes.fromhex(self.signature_hex) - encoded_signature = base64.b64encode(signature_bytes).decode('utf-8') + encoded_signature = base64.b64encode(signature_bytes).decode("utf-8") pem_certificate = "-----BEGIN CERTIFICATE-----\n" pem_certificate += encoded_signature pem_certificate += "\n-----END CERTIFICATE-----" @@ -20,10 +21,10 @@ def _create_pem_certificate(self): def _get_certificate_details(self, pem_certificate): result = subprocess.run( - ['openssl', 'x509', '-text', '-noout'], + ["openssl", "x509", "-text", "-noout"], input=pem_certificate, capture_output=True, - text=True + text=True, ) if result.returncode != 0: raise RuntimeError(f"Error: {result.stderr}") @@ -34,4 +35,4 @@ def generate_certificate_details(self): pem_certificate = self._create_pem_certificate() return self._get_certificate_details(pem_certificate) except Exception as e: - return f"\033[91mError: {str(e)}\033[0m\n" \ No newline at end of file + return f"\033[91mError: {str(e)}\033[0m\n" diff --git a/sigtool/pem_signature_extractor.py b/sigtool/pem_signature_extractor.py index a4d1560..15052c9 100644 --- a/sigtool/pem_signature_extractor.py +++ b/sigtool/pem_signature_extractor.py @@ -3,6 +3,7 @@ import base64 import subprocess + class PEMSignatureExtractor: def __init__(self, pem_path=None, pem_data=None): self.pem_path = pem_path @@ -12,7 +13,7 @@ def __init__(self, pem_path=None, pem_data=None): def _extract_base64_cert(self): pem_data = self.pem_data if self.pem_path: - with open(self.pem_path, 'r') as pem_file: + with open(self.pem_path, "r") as pem_file: pem_data = pem_file.read() start_marker = "-----BEGIN CERTIFICATE-----" @@ -24,7 +25,7 @@ def _extract_base64_cert(self): if start_index == -1 or end_index == -1: raise ValueError("Invalid PEM file: Certificate markers not found") - base64_cert = pem_data[start_index + len(start_marker):end_index].strip() + base64_cert = pem_data[start_index + len(start_marker) : end_index].strip() return base64_cert def extract_signatures(self): @@ -39,15 +40,19 @@ def extract_signatures(self): def convert_rsa_to_pem(self, rsa_path): try: result = subprocess.run( - ['openssl', 'pkcs7', '-inform', 'DER', '-print_certs', '-in', rsa_path], + ["openssl", "pkcs7", "-inform", "DER", "-print_certs", "-in", rsa_path], check=True, stdout=subprocess.PIPE, - stderr=subprocess.PIPE + stderr=subprocess.PIPE, ) if result.returncode != 0: - raise ValueError(f"Failed to convert RSA to PEM: {result.stderr.decode('utf-8')}") - - pem_data = result.stdout.decode('utf-8') + raise ValueError( + f"Failed to convert RSA to PEM: {result.stderr.decode('utf-8')}" + ) + + pem_data = result.stdout.decode("utf-8") return pem_data except subprocess.CalledProcessError as e: - raise ValueError(f"Error during RSA to PEM conversion: {e.stderr.decode('utf-8')}") \ No newline at end of file + raise ValueError( + f"Error during RSA to PEM conversion: {e.stderr.decode('utf-8')}" + ) diff --git a/sigtool/sighooks/__init__.py b/sigtool/sighooks/__init__.py index 9e7697b..a0278c2 100644 --- a/sigtool/sighooks/__init__.py +++ b/sigtool/sighooks/__init__.py @@ -1,3 +1 @@ -__package__ = [ - "sigtool.sighooks.mt_enhanced_hook" -] \ No newline at end of file +__package__ = ["sigtool.sighooks.mt_enhanced_hook"] diff --git a/sigtool/sighooks/mt_enhanced_hook/__init__.py b/sigtool/sighooks/mt_enhanced_hook/__init__.py index 1bf6309..dd5b23a 100644 --- a/sigtool/sighooks/mt_enhanced_hook/__init__.py +++ b/sigtool/sighooks/mt_enhanced_hook/__init__.py @@ -1,7 +1,4 @@ from .mthook import get_encoded_zip from .mthook_generator import MTHookGenerator -__all__ = [ - "get_encoded_zip", - "MTHookGenerator" -] \ No newline at end of file +__all__ = ["get_encoded_zip", "MTHookGenerator"] diff --git a/sigtool/sighooks/mt_enhanced_hook/mthook.py b/sigtool/sighooks/mt_enhanced_hook/mthook.py index 4eee3c2..0d7f681 100644 --- a/sigtool/sighooks/mt_enhanced_hook/mthook.py +++ b/sigtool/sighooks/mt_enhanced_hook/mthook.py @@ -2,5 +2,6 @@ ENCODED_ZIP = "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" + def get_encoded_zip() -> str: return ENCODED_ZIP diff --git a/sigtool/sighooks/mt_enhanced_hook/mthook_generator.py b/sigtool/sighooks/mt_enhanced_hook/mthook_generator.py index 30a20e8..4ef638e 100644 --- a/sigtool/sighooks/mt_enhanced_hook/mthook_generator.py +++ b/sigtool/sighooks/mt_enhanced_hook/mthook_generator.py @@ -13,15 +13,17 @@ class MTHookGenerator: - def __init__(self, encoded_zip: str, apk_path: str, pkg_name: str, encoded_cert: str): + def __init__( + self, encoded_zip: str, apk_path: str, pkg_name: str, encoded_cert: str + ): self.encoded_zip = get_encoded_zip() self.apk_path = apk_path self.pkg_name = pkg_name self.encoded_cert = encoded_cert current_file_dir = os.path.dirname(os.path.abspath(__file__)) self.root_dir = os.path.dirname(os.path.dirname(current_file_dir)) - self.lib_path = os.path.join(self.root_dir, 'lib') - self.smali_jar_path = os.path.join(self.lib_path, 'smali.jar') + self.lib_path = os.path.join(self.root_dir, "lib") + self.smali_jar_path = os.path.join(self.lib_path, "smali.jar") def decode_base64(self) -> bytes: return base64.b64decode(self.encoded_zip) @@ -31,15 +33,15 @@ def extract_zip(self, zip_data: bytes) -> Dict[str, bytes]: return {name: z.read(name) for name in z.namelist()} def modify_smali_file(self, smali_data: bytes) -> bytes: - content = smali_data.decode('utf-8') - content = content.replace('pkg_name', self.pkg_name) - formatted_encoded_cert = self.encoded_cert.replace('\n', '\\n') + content = smali_data.decode("utf-8") + content = content.replace("pkg_name", self.pkg_name) + formatted_encoded_cert = self.encoded_cert.replace("\n", "\\n") def replace_encoded_cert(match): return formatted_encoded_cert - content = re.sub(r'encoded_certificate_bytes', replace_encoded_cert, content) - return content.encode('utf-8') + content = re.sub(r"encoded_certificate_bytes", replace_encoded_cert, content) + return content.encode("utf-8") def manage_lib_folders(self, apk_libs: List[str], zip_libs: List[str]) -> List[str]: if not apk_libs: @@ -48,61 +50,89 @@ def manage_lib_folders(self, apk_libs: List[str], zip_libs: List[str]) -> List[s def get_apk_architectures(self) -> List[str]: with zipfile.ZipFile(self.apk_path) as apk_zip: - apk_lib_folders = [name for name in apk_zip.namelist() if name.startswith('lib/') and len(name.split('/')) == 3] - return list(set(name.split('/')[1] for name in apk_lib_folders)) + apk_lib_folders = [ + name + for name in apk_zip.namelist() + if name.startswith("lib/") and len(name.split("/")) == 3 + ] + return list(set(name.split("/")[1] for name in apk_lib_folders)) def count_dex_files(self) -> int: with zipfile.ZipFile(self.apk_path) as apk_zip: - dex_files = [name for name in apk_zip.namelist() if name.endswith('.dex') and '/' not in name] + dex_files = [ + name + for name in apk_zip.namelist() + if name.endswith(".dex") and "/" not in name + ] return len(dex_files) def save_smali_files(self, files: Dict[str, bytes], temp_dir: str) -> None: for name, data in files.items(): file_path = os.path.join(temp_dir, name) os.makedirs(os.path.dirname(file_path), exist_ok=True) - with open(file_path, 'wb') as f: + with open(file_path, "wb") as f: f.write(data) def convert_smali_to_dex(self, smali_dir: str, output_dex_path: str) -> None: - smali_command = ['java', '-jar', self.smali_jar_path, 'assemble', smali_dir, '-o', output_dex_path] + smali_command = [ + "java", + "-jar", + self.smali_jar_path, + "assemble", + smali_dir, + "-o", + output_dex_path, + ] subprocess.run(smali_command, check=True) def handle_hook_modification(self, files: Dict[str, bytes], temp_dir: str) -> None: for name, data in files.items(): - if name == 'classes.zip': + if name == "classes.zip": with zipfile.ZipFile(io.BytesIO(data)) as hook_zip: for hook_name in hook_zip.namelist(): hook_data = hook_zip.read(hook_name) - if hook_name == 'android/app/application.smali': + if hook_name == "android/app/application.smali": hook_data = self.modify_smali_file(hook_data) file_path = os.path.join(temp_dir, hook_name) os.makedirs(os.path.dirname(file_path), exist_ok=True) - with open(file_path, 'wb') as f: + with open(file_path, "wb") as f: f.write(hook_data) - def add_lib_files(self, new_zip: zipfile.ZipFile, files: Dict[str, bytes], valid_libs: List[str]) -> None: - zip_lib_folder = 'lib' + def add_lib_files( + self, new_zip: zipfile.ZipFile, files: Dict[str, bytes], valid_libs: List[str] + ) -> None: + zip_lib_folder = "lib" for name, data in files.items(): - if name.startswith(f'{zip_lib_folder}/'): - arch = name.split('/')[1] + if name.startswith(f"{zip_lib_folder}/"): + arch = name.split("/")[1] if not valid_libs or arch in valid_libs: new_zip.writestr(name, data) - def add_assets_folder(self, new_zip: zipfile.ZipFile, files: Dict[str, bytes]) -> None: + def add_assets_folder( + self, new_zip: zipfile.ZipFile, files: Dict[str, bytes] + ) -> None: for name, data in files.items(): - if name == 'assets/fonts/droidsans.ttf': - with open(self.apk_path, 'rb') as apk_file: - new_zip.writestr('assets/fonts/droidsans.ttf', apk_file.read()) - elif name.startswith('assets/'): + if name == "assets/fonts/droidsans.ttf": + with open(self.apk_path, "rb") as apk_file: + new_zip.writestr("assets/fonts/droidsans.ttf", apk_file.read()) + elif name.startswith("assets/"): new_zip.writestr(name, data) - def add_dex_file(self, new_zip: zipfile.ZipFile, dex_output_path: str, new_dex_name: str) -> None: - with open(dex_output_path, 'rb') as dex_file: + def add_dex_file( + self, new_zip: zipfile.ZipFile, dex_output_path: str, new_dex_name: str + ) -> None: + with open(dex_output_path, "rb") as dex_file: new_zip.writestr(new_dex_name, dex_file.read()) - def prepare_new_zip(self, files: Dict[str, bytes], dex_output_path: str, valid_libs: List[str], new_dex_name: str) -> bytes: + def prepare_new_zip( + self, + files: Dict[str, bytes], + dex_output_path: str, + valid_libs: List[str], + new_dex_name: str, + ) -> bytes: new_zip_buffer = io.BytesIO() - with zipfile.ZipFile(new_zip_buffer, 'w') as new_zip: + with zipfile.ZipFile(new_zip_buffer, "w") as new_zip: self.add_lib_files(new_zip, files, valid_libs) self.add_assets_folder(new_zip, files) self.add_dex_file(new_zip, dex_output_path, new_dex_name) @@ -112,11 +142,11 @@ def modify_zip(self, zip_data: bytes) -> bytes: files = self.extract_zip(zip_data) dex_count = self.count_dex_files() - new_dex_name = f'classes{dex_count + 1}.dex' + new_dex_name = f"classes{dex_count + 1}.dex" apk_libs = self.get_apk_architectures() - original_libs = [name for name in files if name.startswith('lib/')] - lib_architectures = list(set(name.split('/')[1] for name in original_libs)) + original_libs = [name for name in files if name.startswith("lib/")] + lib_architectures = list(set(name.split("/")[1] for name in original_libs)) valid_libs = self.manage_lib_folders(apk_libs, lib_architectures) with tempfile.TemporaryDirectory() as temp_dir: @@ -125,7 +155,9 @@ def modify_zip(self, zip_data: bytes) -> bytes: dex_output_path = os.path.join(temp_dir, new_dex_name) self.convert_smali_to_dex(temp_dir, dex_output_path) - return self.prepare_new_zip(files, dex_output_path, valid_libs, new_dex_name) + return self.prepare_new_zip( + files, dex_output_path, valid_libs, new_dex_name + ) def process(self, output_path: str) -> None: decoded_zip = self.decode_base64() @@ -133,5 +165,5 @@ def process(self, output_path: str) -> None: self.save_modified_zip(output_path, modified_zip) def save_modified_zip(self, output_path: str, modified_zip: bytes) -> None: - with open(output_path, 'wb') as f: - f.write(modified_zip) \ No newline at end of file + with open(output_path, "wb") as f: + f.write(modified_zip) diff --git a/sigtool/signature_hash_calculator.py b/sigtool/signature_hash_calculator.py index a589acb..125bfa6 100644 --- a/sigtool/signature_hash_calculator.py +++ b/sigtool/signature_hash_calculator.py @@ -2,6 +2,7 @@ import hashlib + class SignatureHashCalculator: def __init__(self, signature_hex): self.signature_hex = signature_hex @@ -38,5 +39,5 @@ def calculate_hashes(self): "MD5": self._calculate_md5(), "SHA-224": self._calculate_sha224(), "SHA-384": self._calculate_sha384(), - "SHA-512": self._calculate_sha512() - } \ No newline at end of file + "SHA-512": self._calculate_sha512(), + } diff --git a/sigtool/smali_bytearray_generator.py b/sigtool/smali_bytearray_generator.py index 4d96ef9..c90af55 100644 --- a/sigtool/smali_bytearray_generator.py +++ b/sigtool/smali_bytearray_generator.py @@ -2,6 +2,7 @@ from typing import List + class SmaliByteArrayGenerator: def __init__(self, signature_hex): self.signature_hex = signature_hex @@ -11,20 +12,16 @@ def convert_hex_to_array(self, hex_string: str) -> List[int]: return [byte - 256 if byte > 127 else byte for byte in byte_array] def format_array_data(self, array_data: List[int]) -> str: - formatted_lines = [ - " nop", - " label_0:", - " .array_data" - ] + formatted_lines = [" nop", " label_0:", " .array_data"] + formatted_lines.extend( + f" {'0x' if value >= 0 else '-0x'}{abs(value):02x}t" + for value in array_data + ) formatted_lines.extend( - f" {'0x' if value >= 0 else '-0x'}{abs(value):02x}t" for value in array_data + [" .end array_data", f" length:0x{len(array_data) * 2:03x}"] ) - formatted_lines.extend([ - " .end array_data", - f" length:0x{len(array_data) * 2:03x}" - ]) return "\n".join(formatted_lines) def generate_smali(self) -> str: array_data = self.convert_hex_to_array(self.signature_hex) - return self.format_array_data(array_data) \ No newline at end of file + return self.format_array_data(array_data)