{"id":29820934,"url":"https://github.com/cyrus-studio/aarkit","last_synced_at":"2025-07-28T23:13:10.734Z","repository":{"id":306476917,"uuid":"1026332507","full_name":"CYRUS-STUDIO/aarkit","owner":"CYRUS-STUDIO","description":"aar工具库，aar 解包 \u0026 重打包（AAR Utilities: Unpack \u0026 Repack AARs）","archived":false,"fork":false,"pushed_at":"2025-07-25T17:53:22.000Z","size":8,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-26T00:15:55.726Z","etag":null,"topics":["aar"],"latest_commit_sha":null,"homepage":"https://cyrus-studio.github.io/blog/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CYRUS-STUDIO.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-07-25T17:45:36.000Z","updated_at":"2025-07-25T23:22:39.000Z","dependencies_parsed_at":"2025-07-26T00:16:28.460Z","dependency_job_id":"9bd15333-5861-4c57-877d-7f3d4a17defa","html_url":"https://github.com/CYRUS-STUDIO/aarkit","commit_stats":null,"previous_names":["cyrus-studio/aarkit"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/CYRUS-STUDIO/aarkit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CYRUS-STUDIO%2Faarkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CYRUS-STUDIO%2Faarkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CYRUS-STUDIO%2Faarkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CYRUS-STUDIO%2Faarkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CYRUS-STUDIO","download_url":"https://codeload.github.com/CYRUS-STUDIO/aarkit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CYRUS-STUDIO%2Faarkit/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267600987,"owners_count":24113889,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-28T02:00:09.689Z","response_time":68,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aar"],"created_at":"2025-07-28T23:13:10.194Z","updated_at":"2025-07-28T23:13:10.725Z","avatar_url":"https://github.com/CYRUS-STUDIO.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003e 版权归作者所有，如有转发，请注明文章出处：\u003chttps://cyrus-studio.github.io/blog/\u003e\n\n# 一、什么是 AAR 文件？\n\n\n\nAAR 文件（Android Archive）是 Android Studio 用来打包 Android Library（库模块） 的一种压缩文件格式，扩展名是 .aar，类似于 Java 的 .jar 文件，但功能更丰富，用于复用 UI 组件、资源和代码。\n\n\n\nAAR 文件结构（解压后）\n\n```\nyour-lib.aar\n├── AndroidManifest.xml       # 库模块的清单文件\n├── classes.jar               # 编译后的 Java/Kotlin 类文件（字节码）\n├── res/                      # 资源目录（layout、drawable、values 等）\n├── R.txt                     # 编译生成的 R 类符号文件\n├── assets/                   # assets 目录中的内容\n├── libs/                     # 依赖的 .jar 库\n├── jni/                      # native 库（.so 文件）\n├── proguard.txt              # 混淆配置文件\n├── public.txt                # 声明哪些资源是公开的\n└── META-INF/                 # 元数据（如 aar metadata、许可证等）\n```\n\n\n使用 AAR 的场景举例：\n\n- 引入第三方 SDK（如广告、支付库）\n\n- 组件化开发中将公共模块打成 AAR\n\n- 在没有上传 Maven 仓库的情况下本地集成依赖\n\n\n\n# 二、如何解包 AAR 文件\n\n\n\n.aar  实质上是一个 ZIP 压缩包，可以使用 Python 标准库中的 zipfile 和 os 模块实现对 .aar 文件的解包。\n\n```\nimport os\nimport zipfile\n\ndef unpack_aar(aar_path, output_dir=None):\n    # 1. 校验输入文件是否是合法的 .aar 文件\n    if not os.path.isfile(aar_path) or not aar_path.endswith(\".aar\"):\n        print(\"❌ 输入文件不是有效的 .aar 文件\")\n        return\n\n    # 2. 默认输出路径为同名目录\n    if output_dir is None:\n        base_name = os.path.splitext(os.path.basename(aar_path))[0]\n        output_dir = os.path.join(os.path.dirname(aar_path), base_name)\n\n    # 3. 创建输出目录\n    os.makedirs(output_dir, exist_ok=True)\n\n    # 4. 使用 zipfile 解压 .aar 文件\n    with zipfile.ZipFile(aar_path, \"r\") as zip_ref:\n        zip_ref.extractall(output_dir)\n        print(f\"✅ 已解包到：{output_dir}\")\n```\n\n\n通过 unpack_aar() 解包后，就可以进一步操作 .aar 中的内容，比如提取 classes.jar 并修改 jar 中的 java 代码。\n\n```\n(anti-app) PS D:\\Python\\anti-app\\aar\u003e python aarkit.py unpack \"D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1.aar\"\n✅ 已解包到：D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1\n```\n\n\n![word/media/image1.png](https://gitee.com/cyrus-studio/images/raw/master/894c44ad7a082a7a81f8147720142030.png)\n\n\n# 三、如何重打包 AAR 文件\n\n\n\n使用 zipfile 将解包目录重新打包成一个 .aar 文件\n\n```\nimport os\nimport zipfile\nfrom datetime import datetime\n\ndef pack_aar(input_dir, output_aar=None):\n    # 如果输入目录不存在，打印错误信息并退出\n    if not os.path.isdir(input_dir):\n        print(\"❌ 输入目录不存在\")\n        return\n\n    # 如果未指定输出 aar 文件路径，则自动生成一个带时间戳的文件名\n    if output_aar is None:\n        base_name = os.path.basename(os.path.normpath(input_dir))  # 获取目录名\n        timestamp = datetime.now().strftime(\"%Y%m%d%H%M\")           # 当前时间戳\n        # 输出路径为：输入目录的上一级 + 自动生成的文件名\n        output_aar = os.path.join(os.path.dirname(input_dir), f\"{base_name}-{timestamp}.aar\")\n\n    # 创建一个压缩文件（.aar 格式），使用 ZIP_DEFLATED 压缩方式\n    with zipfile.ZipFile(output_aar, \"w\", zipfile.ZIP_DEFLATED) as zipf:\n        # 遍历输入目录及其子目录中的所有文件\n        for root, _, files in os.walk(input_dir):\n            for file in files:\n                full_path = os.path.join(root, file)                      # 文件的完整路径\n                arcname = os.path.relpath(full_path, input_dir)          # 计算相对路径（作为 zip 中的路径）\n                zipf.write(full_path, arcname)                           # 写入压缩包\n\n    print(f\"✅ 已打包为：{output_aar}\")\n```\n\n\n# 四、AAR 修改实战：注入自定义逻辑\n\n\n\n需求：修改 jar 中指定的类 Bhubscfh 的静态代码块的 xjhbp.classescxclcy(120); 调用后插入下面的代码加载自定义的 Hook 库，实现 Hook 指定方法并修改返回值。\n\n```\nSystem.loadLibrary(\"my_hook\");\n```\n\n\n## 将 classes.jar 转为 smali 代码\n\n\n\nJar → Smali 转换流程：\n\n\n\n**1、JAR → DEX** \n\n\n\n使用 Google 的 D8 工具将 .jar 文件编译成 .dex 文件（Dalvik Executable 格式）：\n\n```\nd8 --output \u003coutput_dir\u003e \u003cjar_file\u003e\n```\n\n\nd8 是 Google 提供的 Dex 编译器，用于将 Java 字节码（.class 文件）转换成 Dalvik 字节码（.dex 文件），已取代老旧的 dx 工具。它与 Android SDK 有直接关系，是 Android 构建流程中的一部分。\n\n\n\n![word/media/image2.png](https://gitee.com/cyrus-studio/images/raw/master/df331a14160fc03502ed048ac45a8a3d.png)\n\n\n**2、DEX → SMALI** \n\n\n\n使用 Baksmali 工具将 .dex 反编译为 smali 汇编代码：\n\n```\njava -jar baksmali.jar d classes.dex -o \u003coutput_dir\u003e\n```\n相关文章：[一文搞懂 Smali 与 Baksmali：Java 层逆向必备技能](https://cyrus-studio.github.io/blog/posts/%E4%B8%80%E6%96%87%E6%90%9E%E6%87%82-smali-%E4%B8%8E-baksmalijava-%E5%B1%82%E9%80%86%E5%90%91%E5%BF%85%E5%A4%87%E6%8A%80%E8%83%BD/)\n\n\n\n代码实现如下：\n\n```\nimport os\nimport shutil\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom datetime import datetime\n\n# 工具路径配置\nTOOLS = {\n    \"dex2jar\": Path(r\"D:\\Python\\anti-app\\dex2jar\\d2j-dex2jar.bat\"),\n    \"d8\": Path(r\"D:\\App\\android\\sdk\\cmdline-tools\\latest\\bin\\d8.bat\"),\n    \"baksmali\": Path(r\"./baksmali.jar\"),\n    \"smali\": Path(r\"./smali.jar\"),\n    \"java\": r\"java\",\n}\n\ndef check_tools():\n    for name, path in TOOLS.items():\n        if name == \"java\":\n            continue\n        if not path.exists():\n            print(f\"[❌] 工具 {name} 未找到: {path}\")\n            sys.exit(1)\n    print(\"[✅] 所有工具检测通过\")\n\ndef run_cmd(cmd, cwd=None):\n    print(f\"[🟢] 执行命令: {' '.join(str(x) for x in cmd)}\")\n    result = subprocess.run(cmd, cwd=cwd)\n    if result.returncode != 0:\n        print(f\"[❌] 命令执行失败: {' '.join(str(x) for x in cmd)}\")\n        sys.exit(1)\n\ndef jar_to_smali(jar_path: Path, output_dir: Path):\n    print(f\"[📦] 转换 jar 到 smali: {jar_path}\")\n    output_smali = output_dir\n    output_dex_dir = output_dir\n\n    if output_smali.exists():\n        shutil.rmtree(output_smali)\n    output_smali.mkdir(parents=True, exist_ok=True)\n    output_dex_dir.mkdir(parents=True, exist_ok=True)\n\n    run_cmd([\n        str(TOOLS[\"d8\"]),\n        \"--output\", str(output_dex_dir),\n        str(jar_path)\n    ])\n\n    dex_file = output_dex_dir / \"classes.dex\"\n\n    run_cmd([\n        TOOLS[\"java\"], \"-jar\", str(TOOLS[\"baksmali\"]),\n        \"d\", str(dex_file), \"-o\", str(output_smali)\n    ])\n\n    os.remove(dex_file)\n    print(f\"[✅] smali 已输出到: {output_smali}\")\n```\n\n\n执行 jar2smali 命令得到 smali 代码\n\n```\n(anti-app) PS D:\\Python\\anti-app\\dex2smali\u003e python jar2smali.py jar2smali \"D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1\\classes.jar\"\n[✅] 所有工具检测通过\n[📦] 转换 jar 到 smali: D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1\\classes.jar\n[🟢] 执行命令: D:\\App\\android\\sdk\\cmdline-tools\\latest\\bin\\d8.bat --output D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1\\classes_smali D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1\\classes.jar\n...\nWarning in D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1\\classes.jar:marmojkfnf/huflnkmt/ncagri/jdtuadkc/Ksfndkd$a.class:\nType `com.blankj.utilcode.util.PermissionUtils$SimpleCallback` was not found, it is required for default or static interface methods desugaring of `marmojkfnf.huflnkmt.ncagri.jdtuadkc.Ksfndkd$a`\n...\n[🟢] 执行命令: java -jar baksmali.jar d D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1\\classes_smali\\classes.dex -o D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1\\classes_smali\n[✅] smali 已输出到: D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1\\classes_smali\n```\n\n\n## 修改 smali 文件，注入代码\n\n\n\n找到目标类的静态代码块的 smali 代码如下：\n\n```\n# direct methods\n.method public static constructor \u003cclinit\u003e()V\n    .locals 1\n\n    const/16 v0, 0x78\n\n    invoke-static {v0}, Lfmfjq/twyvy/xjhbp;-\u003eclassescxclcy(I)V\n\n    return-void\n.end method\n```\n\n\n希望在 classescxclcy 方法调用后插入自定义的代码逻辑\n\n```\nconst-string v1, \"my_hook\"\n\ninvoke-static {v1}, Ljava/lang/System;-\u003eloadLibrary(Ljava/lang/String;)V\n```\n\n\n如果不知道 smali 代码如何写，可以先通过 android studio 编写 java/kotlin 代码，打包 apk，再通过 ApkTool 反编译 apk 得到 smali 代码。 \n\n\n\n参考：[一键反编译、签名、安装 APK！手把手带你玩转 ApkTool + 签名工具](https://cyrus-studio.github.io/blog/posts/%E4%B8%80%E9%94%AE%E5%8F%8D%E7%BC%96%E8%AF%91%E7%AD%BE%E5%90%8D%E5%AE%89%E8%A3%85-apk%E6%89%8B%E6%8A%8A%E6%89%8B%E5%B8%A6%E4%BD%A0%E7%8E%A9%E8%BD%AC-apktool-+-%E7%AD%BE%E5%90%8D%E5%B7%A5%E5%85%B7/)\n\n\n\n修改后：\n\n```\n# direct methods\n.method public static constructor \u003cclinit\u003e()V\n    .locals 2\n\n    const/16 v0, 0x78\n\n    invoke-static {v0}, Lfmfjq/twyvy/xjhbp;-\u003eclassescxclcy(I)V\n\n    const-string v1, \"my_hook\"\n\n    invoke-static {v1}, Ljava/lang/System;-\u003eloadLibrary(Ljava/lang/String;)V\n\n    return-void\n.end method\n```\n由于修改后用到了 v0 和 v1 两个寄存器，需要把 .locals 1 改为 .locals 2，.locals 用于声明当前方法最多会用到几个寄存器。\n\n\n\n注意：在新版 smali 的语法中，使用 registers 取代了 locals。\n\n\n\nso 文件直接 copy 到 jni 对应架构目录下\n\n\n\n![word/media/image3.png](https://gitee.com/cyrus-studio/images/raw/master/f9332ff5ef84fbacf26efaca693acfc7.png)\n\n\n其他相关的 smali 代码直接 copy 到 smali 目录下\n\n\n\n![word/media/image4.png](https://gitee.com/cyrus-studio/images/raw/master/4dac1684ec30c2020f70ac91ae85ef9f.png)\n\n\n## 注意事项（防混淆）\n\n\n\n编辑 proguard.txt 添加需要防止混淆的代码，否则可能出现找不到类的情况：\n\n```\n-keep class com.bytedance.** {\n    *;\n}\n```\n\n\n## 将 smali 转回 jar\n\n\n\nSmali → Jar 转换流程：\n\n\n\n**1、 smali → dex** \n\n\n\n使用 [smali 工具](https://github.com/JesusFreke/smali) 将 .smali 汇编代码打包生成 .dex 文件：\n\n```\njava -jar smali.jar a \u003csmali_dir\u003e -o \u003crecompiled.dex\u003e\n```\n- a 表示 assemble（汇编）。\n\n- \u003csmali_dir\u003e 是包含 .smali 文件的目录。\n\n- 输出生成 recompiled.dex。\n\n\n\n**2、dex → jar** \n\n\n\n使用 [dex2jar 工具](https://github.com/pxb1988/dex2jar) 将 .dex 文件转换为 Java 字节码 .jar 文件：\n\n```\nd2j-dex2jar.sh recompiled.dex -o output.jar\n```\n\n\n代码实现如下：\n\n```\ndef smali_to_jar(smali_dir: Path, output_jar: Path):\n    print(\"📦 处理 smali → dex → jar\")\n    temp_dex = smali_dir.parent / \"recompiled.dex\"\n\n    run_cmd([\"java\", \"-jar\", str(TOOLS[\"smali\"]), \"a\", str(smali_dir), \"-o\", str(temp_dex)])\n    run_cmd([str(TOOLS[\"dex2jar\"]), str(temp_dex), \"-o\", str(output_jar)])\n    temp_dex.unlink(missing_ok=True)\n    print(f\"✅ 输出 jar 文件: {output_jar}\")\n```\n\n\n执行 smali2jar 命令把 smali 目录重写打包回 jar 文件\n\n```\n(anti-app) PS D:\\Python\\anti-app\\dex2smali\u003e python jar2smali.py smali2jar D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1\\classes_smali\n[✅] 所有工具检测通过\n📦 处理 smali → dex → jar\n[🟢] 执行命令: java -jar smali.jar a D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1\\classes_smali -o D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1\\recompiled.dex\n[🟢] 执行命令: D:\\Python\\anti-app\\dex2jar\\d2j-dex2jar.bat D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1\\recompiled.dex -o D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1\\classes-202507260113.jar\ndex2jar D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1\\recompiled.dex -\u003e D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1\\classes-202507260113.jar\n✅ 输出 jar 文件: D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1\\classes-202507260113.jar\n```\n\n\n使用 jd 打开 jar 文件可以看到指定位置已经新增了自定义的代码。\n\n\n\n![word/media/image5.png](https://gitee.com/cyrus-studio/images/raw/master/98e5b427ae9217d0a02f6301d8ab2a9f.png)\n\n\n使用新打包的 jar 替换掉原来的  classes.jar，并删除 smali 目录。\n\n\n\n## AAR 重打包\n\n\n\n执行重打包命令把 aar 解压目录重新打包成 aar 文件。\n\n```\n(anti-app) PS D:\\Python\\anti-app\\aar\u003e python aarkit.py pack D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1\n✅ 已打包为：D:\\Python\\anti-app\\app\\Ksfndkd\\sanfang-jiagu-0_0_1-202507260120.aar\n```\n\n\n# 五、Android Studio 中导入定制 AAR\n\n\n\n1. 将 AAR 文件放入模块目录下的 libs 文件夹中：\n\n```\napp/\n├── libs/\n│   └── your-library.aar\n```\n\n\n2. 修改 build.gradle.kts：\n\n```\ndependencies {\n    implementation(files(\"libs/sanfang-jiagu-0_0_1-202507260120.aar\"))\n}\n```\n如果你有多个 .aar 文件并希望一次性导入，可以使用：\n\n```\ndependencies {\n    implementation(fileTree(mapOf(\"dir\" to \"libs\", \"include\" to listOf(\"*.aar\"))))\n}\n```\n\n\n3. settings.gradle.kts 添加 flatDir：\n\n```\ndependencyResolutionManagement {\n    ...\n    repositories {\n        ...\n        flatDir {\n            dirs(\"libs\")\n        }\n    }\n}\n```\n\n\n导入成功。\n\n\n\n![word/media/image6.png](https://gitee.com/cyrus-studio/images/raw/master/c73f89472a0d0c3df049b55c2098da76.png)\n\n\n# 完整源码\n\n\n\n## 1. aar 工具源码\n\n\n\n```\nimport os\nimport sys\nimport zipfile\nfrom datetime import datetime\n\n\ndef unpack_aar(aar_path, output_dir=None):\n    # 1. 校验输入文件是否是合法的 .aar 文件\n    if not os.path.isfile(aar_path) or not aar_path.endswith(\".aar\"):\n        print(\"❌ 输入文件不是有效的 .aar 文件\")\n        return\n\n    # 2. 默认输出路径为同名目录\n    if output_dir is None:\n        base_name = os.path.splitext(os.path.basename(aar_path))[0]\n        output_dir = os.path.join(os.path.dirname(aar_path), base_name)\n\n    # 3. 创建输出目录\n    os.makedirs(output_dir, exist_ok=True)\n\n    # 4. 使用 zipfile 解压 .aar 文件\n    with zipfile.ZipFile(aar_path, \"r\") as zip_ref:\n        zip_ref.extractall(output_dir)\n        print(f\"✅ 已解包到：{output_dir}\")\n\n\ndef pack_aar(input_dir, output_aar=None):\n    # 如果输入目录不存在，打印错误信息并退出\n    if not os.path.isdir(input_dir):\n        print(\"❌ 输入目录不存在\")\n        return\n\n    # 如果未指定输出 aar 文件路径，则自动生成一个带时间戳的文件名\n    if output_aar is None:\n        base_name = os.path.basename(os.path.normpath(input_dir))  # 获取目录名\n        timestamp = datetime.now().strftime(\"%Y%m%d%H%M\")           # 当前时间戳\n        # 输出路径为：输入目录的上一级 + 自动生成的文件名\n        output_aar = os.path.join(os.path.dirname(input_dir), f\"{base_name}-{timestamp}.aar\")\n\n    # 创建一个压缩文件（.aar 格式），使用 ZIP_DEFLATED 压缩方式\n    with zipfile.ZipFile(output_aar, \"w\", zipfile.ZIP_DEFLATED) as zipf:\n        # 遍历输入目录及其子目录中的所有文件\n        for root, _, files in os.walk(input_dir):\n            for file in files:\n                full_path = os.path.join(root, file)                      # 文件的完整路径\n                arcname = os.path.relpath(full_path, input_dir)          # 计算相对路径（作为 zip 中的路径）\n                zipf.write(full_path, arcname)                           # 写入压缩包\n\n    print(f\"✅ 已打包为：{output_aar}\")\n\n\nif __name__ == \"__main__\":\n    r\"\"\"\n    # 解包\n    python aarkit.py unpack mylib.aar\n    # → 默认输出：mylib/ （与 mylib.aar 同级）\n    \n    python aarkit.py unpack mylib.aar ./output_dir/\n    # → 输出到指定目录\n    \n    # 打包\n    python aarkit.py pack ./mylib/\n    # → 输出为：mylib-202507231253.aar\n    \n    python aarkit.py pack ./mylib/ mylib.aar\n    # → 输出为：mylib.aar\n    \"\"\"\n\n    if len(sys.argv) \u003c 3:\n        print(\"用法：\")\n        print(\"  解包：python aarkit.py unpack mylib.aar [output_dir]\")\n        print(\"  打包：python aarkit.py pack ./mylib/ [output.aar]\")\n        sys.exit(1)\n\n    command = sys.argv[1]\n    if command == \"unpack\":\n        aar_path = sys.argv[2]\n        output_dir = sys.argv[3] if len(sys.argv) \u003e= 4 else None\n        unpack_aar(aar_path, output_dir)\n    elif command == \"pack\":\n        input_dir = sys.argv[2]\n        output_aar = sys.argv[3] if len(sys.argv) \u003e= 4 else None\n        pack_aar(input_dir, output_aar)\n    else:\n        print(f\"未知命令：{command}\")\n```\n\n\n## 2. jar2smali 工具源码\n\n\n\n```\nimport os\nimport shutil\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom datetime import datetime\n\n# 工具路径配置\nTOOLS = {\n    \"dex2jar\": Path(r\"D:\\Python\\anti-app\\dex2jar\\d2j-dex2jar.bat\"),\n    \"d8\": Path(r\"D:\\App\\android\\sdk\\cmdline-tools\\latest\\bin\\d8.bat\"),\n    \"baksmali\": Path(r\"./baksmali.jar\"),\n    \"smali\": Path(r\"./smali.jar\"),\n    \"java\": r\"java\",\n}\n\ndef check_tools():\n    for name, path in TOOLS.items():\n        if name == \"java\":\n            continue\n        if not path.exists():\n            print(f\"[❌] 工具 {name} 未找到: {path}\")\n            sys.exit(1)\n    print(\"[✅] 所有工具检测通过\")\n\ndef run_cmd(cmd, cwd=None):\n    print(f\"[🟢] 执行命令: {' '.join(str(x) for x in cmd)}\")\n    result = subprocess.run(cmd, cwd=cwd)\n    if result.returncode != 0:\n        print(f\"[❌] 命令执行失败: {' '.join(str(x) for x in cmd)}\")\n        sys.exit(1)\n\ndef jar_to_smali(jar_path: Path, output_dir: Path):\n    print(f\"[📦] 转换 jar 到 smali: {jar_path}\")\n    output_smali = output_dir\n    output_dex_dir = output_dir\n\n    if output_smali.exists():\n        shutil.rmtree(output_smali)\n    output_smali.mkdir(parents=True, exist_ok=True)\n    output_dex_dir.mkdir(parents=True, exist_ok=True)\n\n    run_cmd([\n        str(TOOLS[\"d8\"]),\n        \"--output\", str(output_dex_dir),\n        str(jar_path)\n    ])\n\n    dex_file = output_dex_dir / \"classes.dex\"\n\n    run_cmd([\n        TOOLS[\"java\"], \"-jar\", str(TOOLS[\"baksmali\"]),\n        \"d\", str(dex_file), \"-o\", str(output_smali)\n    ])\n\n    os.remove(dex_file)\n    print(f\"[✅] smali 已输出到: {output_smali}\")\n\ndef smali_to_jar(smali_dir: Path, output_jar: Path):\n    print(\"📦 处理 smali → dex → jar\")\n    temp_dex = smali_dir.parent / \"recompiled.dex\"\n\n    run_cmd([\"java\", \"-jar\", str(TOOLS[\"smali\"]), \"a\", str(smali_dir), \"-o\", str(temp_dex)])\n    run_cmd([str(TOOLS[\"dex2jar\"]), str(temp_dex), \"-o\", str(output_jar)])\n    temp_dex.unlink(missing_ok=True)\n    print(f\"✅ 输出 jar 文件: {output_jar}\")\n\ndef main():\n    if len(sys.argv) \u003c 3:\n        print(\"用法：\")\n        print(\"  python jar2smali.py jar2smali mylib.jar [./output/]\")\n        print(\"  python jar2smali.py smali2jar ./mylib_smali [./output/mylib_new.jar]\")\n        sys.exit(1)\n\n    check_tools()\n\n    mode = sys.argv[1]\n    if mode == \"jar2smali\":\n        jar_path = Path(sys.argv[2])\n        if not jar_path.exists():\n            print(f\"[❌] jar 文件不存在: {jar_path}\")\n            sys.exit(1)\n\n        if len(sys.argv) \u003e= 4:\n            out_dir = Path(sys.argv[3])\n        else:\n            out_dir = jar_path.parent / f\"{jar_path.stem}_smali\"\n\n        jar_to_smali(jar_path, out_dir)\n\n    elif mode == \"smali2jar\":\n        smali_dir = Path(sys.argv[2])\n        if not smali_dir.exists():\n            print(f\"[❌] smali 目录不存在: {smali_dir}\")\n            sys.exit(1)\n\n        if len(sys.argv) \u003e= 4:\n            out_jar = Path(sys.argv[3])\n        else:\n            timestamp = datetime.now().strftime(\"%Y%m%d%H%M\")\n            out_jar = smali_dir.parent / f\"{smali_dir.name.replace('_smali', '')}-{timestamp}.jar\"\n\n        smali_to_jar(smali_dir, out_jar)\n\n    else:\n        print(f\"[❌] 不支持的模式: {mode}\")\n        sys.exit(1)\n\nif __name__ == \"__main__\":\n    main()\n```\n\n\n开源地址：\n\n- [https://github.com/CYRUS-STUDIO/aarkit](https://github.com/CYRUS-STUDIO/aarkit)\n\n- [https://github.com/CYRUS-STUDIO/dex2jar](https://github.com/CYRUS-STUDIO/dex2jar)\n\n- [https://github.com/CYRUS-STUDIO/dex2smali](https://github.com/CYRUS-STUDIO/dex2smali)\n\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcyrus-studio%2Faarkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcyrus-studio%2Faarkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcyrus-studio%2Faarkit/lists"}