import os import sys import json import yaml import requests import argparse import colorama import importlib import traceback import subprocess from tqdm import tqdm def init() -> dict: for directory in ["original", "modified", "patches", "tools", "decompiled"]: if not os.path.exists(directory): os.makedirs(directory) with open("./config.json", "r") as config_file: conf = json.load(config_file) if not os.path.exists("./tools/apktool.jar"): try: print("Скачивание Apktool...") jar_response = requests.get(conf["tools"]["apktool_jar_url"], stream=True) jar_path = "tools/apktool.jar" with open(jar_path, "wb") as f: for chunk in jar_response.iter_content(chunk_size=8192): f.write(chunk) wrapper_response = requests.get(conf["tools"]["apktool_wrapper_url"]) wrapper_path = "tools/apktool" with open(wrapper_path, "w") as f: f.write(wrapper_response.text) os.chmod(wrapper_path, 0o755) except Exception as e: print(f"Ошибка при скачивании Apktool: {e}") exit(1) try: result = subprocess.run( ["java", "-version"], capture_output=True, text=True, check=True ) version_line = result.stderr.splitlines()[0] if "1.8" in version_line or any(f"{i}." in version_line for i in range(9, 100)): print("Java 8 или более поздняя версия установлена.") else: print("Java 8 или более поздняя версия не установлена.") sys.exit(1) except subprocess.CalledProcessError: print("Java не установлена. Установите Java 8 или более позднюю версию.") exit(1) return conf def select_apk() -> str: apks = [] for file in os.listdir("original"): if file.endswith(".apk") and os.path.isfile(os.path.join("original", file)): apks.append(file) if not apks: print("Нет файлов .apk в текущей директории") sys.exit(1) if len(apks) == 1: apk = apks[0] print(f"Выбран файл {apk}") return apk while True: print("Выберете файл для модификации") for index, apk in enumerate(apks): print(f"{index + 1}. {apk}") print("0. Exit") try: selected_index = int(input("\nВведите номер файла: ")) if selected_index == 0: sys.exit(0) elif selected_index > len(apks): print("Неверный номер файла") else: apk = apks[selected_index - 1] print(f"Выбран файл {apk}") return apk except ValueError: print("Неверный формат ввода") except KeyboardInterrupt: print("Прервано пользователем") sys.exit(0) def decompile_apk(apk: str): print("Декомпилируем apk...") try: result = subprocess.run( "tools/apktool d -f -o decompiled " + os.path.join("original", apk), shell=True, check=True, text=True, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, ) except subprocess.CalledProcessError as e: print("Ошибка при выполнении команды:") print(e.stderr) sys.exit(1) def compile_apk(apk: str): print("Компилируем apk...") try: subprocess.run( "tools/apktool b decompiled -o " + os.path.join("modified", apk), shell=True, check=True, text=True, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, ) subprocess.run( "zipalign -v 4 " + os.path.join("modified", apk) + " " + os.path.join("modified", apk.replace(".apk", "-aligned.apk")), shell=True, check=True, text=True, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, ) subprocess.run( "apksigner sign " + "--v1-signing-enabled false " + "--v2-signing-enabled true " + "--v3-signing-enabled true " + "--ks keystore.jks " + "--ks-pass file:keystore.pass " + "--out " + os.path.join("modified", apk.replace(".apk", "-mod.apk")) + " " + os.path.join("modified", apk.replace(".apk", "-aligned.apk")), shell=True, check=True, text=True, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, ) title = "anixart mod " with open('./decompiled/apktool.yml') as f: package = yaml.safe_load(f) title += ' '.join([f'{k}: {v}' for k, v in package['versionInfo'].items()]) with open("./modified/report.log", "w") as log_file: log_file.write(title+'\n') log_file.write("\n".join([f"{patch.name}: {'applied' if patch.applied else 'failed'}" for patch in patches])) except subprocess.CalledProcessError as e: print("Ошибка при выполнении команды:") print(e.stderr) sys.exit(1) class Patch: def __init__(self, name, pkg): self.name = name self.package = pkg self.applied = False try: self.priority = pkg.priority except AttributeError: self.priority = 0 def apply(self, conf: dict) -> bool: try: self.applied = self.package.apply(conf) return True except Exception as e: print(f"Ошибка при применении патча {self.name}: {e}") print(type(e), e.args) traceback.print_exc() return False if __name__ == "__main__": parser = argparse.ArgumentParser( description="Автоматический патчер anixart" ) parser.add_argument("-v", "--verbose", action="store_true", help="Выводить подробные сообщения") parser.add_argument("-f", "--force", action="store_true", help="Принудительно собрать APK") args = parser.parse_args() conf = init() apk = select_apk() patch = decompile_apk(apk) if args.verbose: conf["verbose"] = True patches = [] for filename in os.listdir("patches/"): if filename.endswith(".py") and filename != "__init__.py" and not filename.startswith("todo_"): module_name = filename[:-3] module = importlib.import_module(f"patches.{module_name}") patches.append(Patch(module_name, module)) patches.sort(key=lambda x: x.package.priority, reverse=True) for patch in tqdm(patches, colour="green", desc="Применение патчей"): tqdm.write(f"Применение патча: {patch.name}") patch.apply(conf) statuses = {} for patch in patches: statuses[patch.name] = patch.applied marker = colorama.Fore.GREEN + "✔" if patch.applied else colorama.Fore.RED + "✘" print(f"{marker}{colorama.Style.RESET_ALL} {patch.name}") if all(statuses.values()): print(f"{colorama.Fore.GREEN}Все патчи успешно применены{colorama.Style.RESET_ALL}") compile_apk(apk) elif any(statuses.values()): print(f"{colorama.Fore.YELLOW}⚠{colorama.Style.RESET_ALL} Некоторые патчи не были успешно применены") if args.force or input("Продолжить? (y/n): ").lower() == "y": compile_apk(apk) else: print(colorama.Fore.RED + "Операция отменена" + colorama.Style.RESET_ALL) else: print(f"{colorama.Fore.RED}Ни один патч не был успешно применен{colorama.Style.RESET_ALL}") sys.exit(1)