<?
$apktool_jar = 'D:\Desktop\apktool\apktool_2.11.1.jar';//apktool包的路径
$apk_file[0] = 'D:\Desktop\apktool\app-release.apk';//1号apk,也就是注入弹窗壳apk
$apk_file[1] = 'D:\Desktop\apktool\original.apk';//2号apk,也就是被注入的正常APK


$keystore = 'D:/Desktop/apktool/my-release-key.keystore';//证书文件
$alias = 'myalias';
$storepass = '123456';
$keypass = '123456';

$apksigner_path = 'C:\Users\Administrator\AppData\Local\Android\Sdk\build-tools\35.0.0/apksigner.bat'; //签名工具，这里我定义的是我的Androidstudio的路径 或仅用 'apksigner' 如果加到环境变量了的话这里就可以填null
print_r("开始反编译\n");
$decompile = decompile_apks($apktool_jar, $apk_file);//反编译
//print_r($decompile);exit;

$de_apk1 = $decompile[0][2];
$de_apk2 = $decompile[1][2];

//这里是基类劫持
$result = get_application_inheritance_chain($de_apk2);//2号APK的基类读取
if($result['depth'] > 1){
    print_r("该应用基类层级过多,可能已经被注入过了\n");
}
//print_r($result);//exit;

$smali = merge_smali_directories($de_apk1, $de_apk2);//smali融合,将1号APK的smali复制到2号apk中

print_r("正在进行基类替换\n");
$result = replace_application_super($result['file'],'com.example.shell.HookApplication;');//基类替换,将2号文件的application父类替换为1号文件中写好的application类
//print_r($result);//exit;
$result = ensure_application_name($de_apk2, 'com.example.shell.HookApplication');//基类劫持的话,需要检查是否存在全局application，没有的话就需要添加
//print_r($result);

$result = merge_activities_to_application_only($de_apk1, $de_apk2);//AndroidManifest融合

$result = rebuild_apk($apktool_jar,$de_apk2);//回编译

$output_apk = $result[2];//拿到回编译之后的apk路径

//$result = sign_apk($keystore, $alias, $storepass, $keypass, $output_apk, null, $de_apk2, $apksigner_path);//签名,并删除反编译目录
$result = sign_apk($keystore, $alias, $storepass, $keypass, $output_apk, null, null, $apksigner_path);//签名
//print_r($result);
//delete_dir($de_apk1);//删除壳目录
unlink($result[2].".idsig");




//================================这里是启动入口修改注入的用法,兼容性并不是很好================================
/* 
//这里是启动入口替换
$dirs = ['D:\Desktop\apktool\shell', 'D:\Desktop\apktool\qupai'];
$result = parse_apk_manifests($dirs);//找启动入口类名
print_r("启动入口{$result[1][1]}\n");
//exit;
$result = replace_smali_string('D:\Desktop\apktool\shell\smali', '[OpenClassName]', $result[1][1]);//融合之前进行smali字符串替换
print_r("smali替换结果\n");
print_r($result);
print_r("\n");
$result = merge_android_manifests('D:\Desktop\apktool\shell', 'D:\Desktop\apktool\qupai');//AndroidManifest融合
$result = merge_smali_directories('D:\Desktop\apktool\shell', 'D:\Desktop\apktool\qupai');//smali融合 */
/* $list = [
    '/res/drawable/icon.png',
    '/res/layout/main.xml'
];
$result = copy_res_files($de_apk1, $de_apk2, $list);//资源融合，将1号APK里的资源复制到2号APK里
print_r($result); */
//================================下方方法代码勿动================================



function merge_activities_to_application_only($source_dir, $target_dir) {
    $src_manifest = rtrim($source_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'AndroidManifest.xml';
    $dst_manifest = rtrim($target_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'AndroidManifest.xml';

    if (!file_exists($src_manifest) || !file_exists($dst_manifest)) {
        echo "Manifest 文件不存在\n";
        return false;
    }

    // 加载源和目标 manifest
    $src_doc = new DOMDocument();
    $src_doc->preserveWhiteSpace = false;
    $src_doc->formatOutput = true;
    $src_doc->load($src_manifest);

    $dst_doc = new DOMDocument();
    $dst_doc->preserveWhiteSpace = false;
    $dst_doc->formatOutput = true;
    $dst_doc->load($dst_manifest);

    // 获取 <manifest> 根节点
    $dst_manifest_node = $dst_doc->getElementsByTagName("manifest")->item(0);
    $src_manifest_node = $src_doc->getElementsByTagName("manifest")->item(0);

    if (!$dst_manifest_node || !$src_manifest_node) {
        echo "未找到 <manifest> 根节点\n";
        return false;
    }

    // 合并 <uses-permission>（去重）
    $dst_perms = [];
    foreach ($dst_doc->getElementsByTagName("uses-permission") as $perm) {
        $name = $perm->getAttribute("android:name");
        if ($name) {
            $dst_perms[$name] = true;
        }
    }

    foreach ($src_doc->getElementsByTagName("uses-permission") as $perm) {
        $name = $perm->getAttribute("android:name");
        if ($name && !isset($dst_perms[$name])) {
            $comment = $dst_doc->createComment(" 此 uses-permission 来自合并插入 ");
            $dst_manifest_node->appendChild($comment);
            $dst_manifest_node->appendChild($dst_doc->importNode($perm, true));
            $dst_perms[$name] = true;
        }
    }

    // 合并 <permission>（去重）
    $dst_defined_perms = [];
    foreach ($dst_doc->getElementsByTagName("permission") as $perm) {
        $name = $perm->getAttribute("android:name");
        if ($name) {
            $dst_defined_perms[$name] = true;
        }
    }

    /* foreach ($src_doc->getElementsByTagName("permission") as $perm) {
        $name = $perm->getAttribute("android:name");
        if ($name && !isset($dst_defined_perms[$name])) {
            $comment = $dst_doc->createComment(" 此 permission 来自合并插入 ");
            $dst_manifest_node->appendChild($comment);
            $dst_manifest_node->appendChild($dst_doc->importNode($perm, true));
            $dst_defined_perms[$name] = true;
        }
    } */

    // 获取 <application> 节点
    $dst_app = $dst_doc->getElementsByTagName("application")->item(0);
    $src_app = $src_doc->getElementsByTagName("application")->item(0);

    if (!$dst_app || !$src_app) {
        echo "未找到 <application> 标签\n";
        return false;
    }

    // 合并 <activity> 和 <activity-alias>（去掉 intent-filter）
    foreach (['activity', 'activity-alias'] as $tag) {
        foreach ($src_app->getElementsByTagName($tag) as $node) {
            $imported = $dst_doc->importNode($node, true);

            // 移除 intent-filter
            $filters = $imported->getElementsByTagName("intent-filter");
            while ($filters->length > 0) {
                $imported->removeChild($filters->item(0));
            }

            $comment = $dst_doc->createComment(" 此 $tag 来自合并插入 ");
            $dst_app->appendChild($comment);
            $dst_app->appendChild($imported);
        }
    }

    // 保存结果
    $dst_doc->save($dst_manifest);
    echo "合并完成：uses-permission、permission、activity 均已插入并带注释\n";
    return true;
}












function delete_dir($dir) {
    if (!is_dir($dir)) return;

    $items = scandir($dir);
    foreach ($items as $item) {
        if ($item === '.' || $item === '..') continue;
        $path = $dir . DIRECTORY_SEPARATOR . $item;
        if (is_dir($path)) {
            delete_dir($path);
        } else {
            unlink($path);
        }
    }
    rmdir($dir);
}

function copy_res_files($src_dir, $dst_dir, $file_list) {
    $copied_files = [];
    $count = 0;

    foreach ($file_list as $relative_path) {
        // 标准化路径，去掉开头斜杠
        $relative_path = ltrim($relative_path, '/\\');

        $src_file = rtrim($src_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $relative_path;
        $dst_file = rtrim($dst_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $relative_path;

        if (!is_file($src_file)) {
            continue; // 源文件不存在，跳过
        }

        if (file_exists($dst_file)) {
            continue; // 目标已存在，跳过
        }

        // 创建目标文件夹
        $dst_folder = dirname($dst_file);
        if (!is_dir($dst_folder)) {
            mkdir($dst_folder, 0777, true);
        }

        if (copy($src_file, $dst_file)) {
            $copied_files[] = str_replace('\\', '/', $relative_path);
            $count++;
        }
    }

    return [
        'count' => $count,
        'files' => $copied_files
    ];
}




function ensure_application_name($apk_dir, $class_name) {
    $manifest_path = rtrim($apk_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'AndroidManifest.xml';

    if (!file_exists($manifest_path)) {
        return [false, "Manifest 文件不存在：$manifest_path"];
    }

    // 加载并解析 XML
    libxml_use_internal_errors(true); // 屏蔽格式警告
    $xml = new DOMDocument();
    $xml->preserveWhiteSpace = false;
    $xml->formatOutput = true;
    $xml->load($manifest_path);

    $xpath = new DOMXPath($xml);
    $xpath->registerNamespace("android", "http://schemas.android.com/apk/res/android");

    // 获取 <application> 标签
    $applications = $xml->getElementsByTagName("application");
    if ($applications->length === 0) {
        return [false, "未找到 <application> 标签"];
    }

    $application = $applications->item(0);

    // 查找 android:name 属性
    $name_attr = null;
    foreach ($application->attributes as $attr) {
        if ($attr->nodeName === "android:name" || $attr->name === "android:name") {
            $name_attr = $attr;
            break;
        }
    }

    // 是否需要修改
    $need_update = false;

    if ($name_attr === null) {
        // 属性不存在，则添加
        $application->setAttribute("android:name", $class_name);
        $need_update = true;
    } elseif (trim($name_attr->value) === "") {
        // 属性为空，设置新值
        $name_attr->value = $class_name;
        $need_update = true;
    }

    // 保存文件
    if ($need_update) {
        $xml->save($manifest_path);
        return [true, "已设置 android:name 为：$class_name"];
    } else {
        return [true, "已有有效 android:name，无需修改"];
    }
}


//Application基类替换
function replace_application_super($target_file_path, $new_super_class_name, $apk_root_dir = null) {
    if (!is_file($target_file_path) || pathinfo($target_file_path, PATHINFO_EXTENSION) !== 'smali') {
        return [false, "不是有效的 smali 文件", null, null];
    }

    // 将 Java 类名（com.xxx.HookApplication;）转为 smali 路径
    $class_name = rtrim($new_super_class_name, ';');
    $class_path = str_replace('.', '/', $class_name) . '.smali';

    // 获取 apk 根目录（根据 smali 文件路径推算或传入）
    if (!$apk_root_dir) {
        $apk_root_dir = explode(DIRECTORY_SEPARATOR, $target_file_path);
        while (count($apk_root_dir)) {
            $path = implode(DIRECTORY_SEPARATOR, $apk_root_dir);
            if (is_dir($path) && preg_match('/smali(_classes\d+)?$/', basename($path))) {
                array_pop($apk_root_dir); // 去掉 smali_classesX
                break;
            }
            array_pop($apk_root_dir);
        }
        $apk_root_dir = implode(DIRECTORY_SEPARATOR, $apk_root_dir);
    }

    // 遍历 smali 目录查找新的父类文件
    $smali_dirs = [];
    foreach (scandir($apk_root_dir) as $entry) {
        if (preg_match('/^smali(_classes\d+)?$/', $entry) && is_dir($apk_root_dir . DIRECTORY_SEPARATOR . $entry)) {
            $smali_dirs[] = $apk_root_dir . DIRECTORY_SEPARATOR . $entry;
        }
    }

    $hook_file_found = false;
    $hook_file_path = null;
    $hook_is_app = false;

    foreach ($smali_dirs as $dir) {
        $full_path = $dir . DIRECTORY_SEPARATOR . $class_path;
        if (file_exists($full_path)) {
            $hook_file_found = true;
            $hook_file_path = $full_path;
            $lines = file($full_path);
            foreach ($lines as $line) {
                if (preg_match('/^\.super\s+(L[^;]+;)/', trim($line), $match)) {
                    if ($match[1] === 'Landroid/app/Application;') {
                        $hook_is_app = true;
                    }
                    break;
                }
            }
            break;
        }
    }

    if (!$hook_file_found) {
        return [false, "指定的新父类类文件不存在", null, null];
    }

    if (!$hook_is_app) {
        return [false, "新父类不是 Application 子类，不允许替换", null, null];
    }

    // 开始替换目标文件中的 .super
    $lines = file($target_file_path);
    $modified = false;
    $original_super = null;
    $new_super_smali = 'L' . str_replace('.', '/', $class_name) . ';';

    foreach ($lines as $index => $line) {
        if (preg_match('/^\.super\s+(L[^;]+;)/', trim($line), $match)) {
            $original_super = $match[1];
            if ($original_super !== $new_super_smali) {
                $lines[$index] = ".super $new_super_smali\n";
                $modified = true;
            }
            break;
        }
    }

    if ($modified) {
        file_put_contents($target_file_path, implode('', $lines));
        return [true, "替换成功", $original_super, $new_super_smali];
    } else {
        return [false, "无需修改，.super 已是目标类", $original_super, $new_super_smali];
    }
}



//基类读取
function get_application_inheritance_chain($apk_dir) {
    $manifest_path = rtrim($apk_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'AndroidManifest.xml';

    if (!file_exists($manifest_path)) {
        return ['error' => "Manifest 文件不存在：$manifest_path"];
    }

    // 读取 AndroidManifest 内容
    $content = file_get_contents($manifest_path);

    // 提取 application 的 android:name
    if (!preg_match('/<application[^>]*android:name="([^"]+)"/', $content, $match)) {
        return ['error' => "未找到 application 的 android:name 属性"];
    }

    $class_name = $match[1];

    // 处理相对类名（.MyApp）拼接包名
    if (substr($class_name, 0, 1) === '.') {
        if (preg_match('/<manifest[^>]*package="([^"]+)"/', $content, $pkg_match)) {
            $class_name = $pkg_match[1] . $class_name;
        }
    }

    // 构造链路
    $chain = build_class_chain($apk_dir, $class_name);

    // 获取最终基类名称
    $final = get_last_node($chain);
    $depth = get_chain_depth($chain);
    return [
        'final_super' => $final['super'],
        'class'       => $final['class'] ?? $class_name,
        'file'        => $final['file'] ?? null,
        'depth'       => $depth,
        'chain'       => $chain
    ];
}
//基类链路查找
function build_class_chain($apk_dir, $class_name) {
    $class_path = str_replace('.', '/', ltrim($class_name, '.')) . '.smali';

    $smali_dirs = [];
    foreach (scandir($apk_dir) as $entry) {
        if (preg_match('/^smali(_classes\d+)?$/', $entry) && is_dir($apk_dir . DIRECTORY_SEPARATOR . $entry)) {
            $smali_dirs[] = $apk_dir . DIRECTORY_SEPARATOR . $entry;
        }
    }

    foreach ($smali_dirs as $smali_dir) {
        $full_path = $smali_dir . DIRECTORY_SEPARATOR . $class_path;
        if (file_exists($full_path)) {
            $lines = file($full_path);
            foreach ($lines as $line) {
                if (preg_match('/^\.super\s+(L[^;]+;)/', trim($line), $super_match)) {
                    $super_smali = $super_match[1];

                    // 到达终点,这一步其实可以用数组将别的注入器的类也填进来，直接将别人的注入替换为自己的注入，比如云注入 Lcom/sadfxg/fasg/App
                    if ($super_smali === 'Landroid/app/Application;') {
                        return [
                            'class' => $class_name,
                            'super' => $super_smali,
                            'file'  => $full_path,
                            'extends' => null
                        ];
                    }

                    // 向上递归
                    $super_java = str_replace('/', '.', substr($super_smali, 1, -1));
                    return [
                        'class' => $class_name,
                        'super' => $super_smali,
                        'file'  => $full_path,
                        'extends' => build_class_chain($apk_dir, $super_java)
                    ];
                }
            }

            return [
                'class' => $class_name,
                'super' => null,
                'file'  => $full_path,
                'extends' => null,
                'error' => '.super 未找到'
            ];
        }
    }

    return [
        'class' => $class_name,
        'super' => null,
        'file'  => null,
        'extends' => null,
        'error' => '类文件未找到'
    ];
}
function get_last_node($chain) {
    while ($chain && isset($chain['extends']) && $chain['extends']) {
        $chain = $chain['extends'];
    }
    return $chain;
}
function get_chain_depth($chain) {
    $depth = 0;
    while ($chain) {
        $depth++;
        $chain = $chain['extends'] ?? null;
    }
    return $depth;
}





//smali文件融合
function replace_smali_string($dir, $search, $replace, $case_sensitive = true) {
    $result = [];

    if (!is_dir($dir)) {
        echo "无效的目录：$dir\n";
        return $result;
    }

    $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
    foreach ($iterator as $file) {
        if (!$file->isFile() || pathinfo($file, PATHINFO_EXTENSION) !== 'smali') {
            continue;
        }

        $file_path = $file->getPathname();
        $lines = file($file_path); // 逐行读取
        $modified = false;
        $new_lines = [];

        foreach ($lines as $index => $line) {
            $original_line = $line;

            if ($case_sensitive) {
                if (strpos($line, $search) !== false) {
                    $new_line = str_replace($search, $replace, $line);
                    if ($new_line !== $line) {
                        $result[] = [
                            'file' => $file_path,
                            'line' => $index + 1,
                            'original' => rtrim($line),
                            'modified' => rtrim($new_line)
                        ];
                        $line = $new_line;
                        $modified = true;
                    }
                }
            } else {
                if (preg_match('/' . preg_quote($search, '/') . '/i', $line)) {
                    $new_line = preg_replace('/' . preg_quote($search, '/') . '/i', $replace, $line);
                    if ($new_line !== $line) {
                        $result[] = [
                            'file' => $file_path,
                            'line' => $index + 1,
                            'original' => rtrim($line),
                            'modified' => rtrim($new_line)
                        ];
                        $line = $new_line;
                        $modified = true;
                    }
                }
            }

            $new_lines[] = $line;
        }

        if ($modified) {
            file_put_contents($file_path, implode('', $new_lines));
        }
    }

    return $result;
}


//签名APK
function sign_apk($keystore, $alias, $storepass, $keypass, $unsigned_apk, $signed_apk = null, $output_folder = null, $apksigner_path = 'apksigner') {
    if (!file_exists($unsigned_apk)) {
        $msg = "未找到待签名 APK 文件：$unsigned_apk";
        echo "$msg\n";
        return [false, $msg, null, null];
    }

    if (!file_exists($keystore)) {
        $msg = "签名文件不存在：$keystore";
        echo "$msg\n";
        return [false, $msg, null, null];
    }

    // 自动生成签名后 APK 名称
    if (empty($signed_apk)) {
        $signed_apk = preg_replace('/\.apk$/', '.signed.apk', $unsigned_apk);
    }

    // 构造签名命令
    $cmd = "\"$apksigner_path\" sign --ks \"$keystore\" --ks-key-alias $alias --ks-pass pass:$storepass --key-pass pass:$keypass --out \"$signed_apk\" \"$unsigned_apk\"";
    echo "执行签名命令：$cmd\n";

    // 执行命令
    $output = shell_exec($cmd);
    echo "签名输出：\n$output\n";

    // 判断签名结果
    if (file_exists($signed_apk)) {
        $msg = "✅ 签名完成：$signed_apk";
        echo "$msg\n";

        // 删除未签名 APK
        unlink($unsigned_apk);
        echo "已删除未签名文件：$unsigned_apk\n";

        // 提示删除反编译目录（不自动执行）
        if (!empty($output_folder)) {
            delete_dir($output_folder);
            //echo "请手动清理反编译目录：$output_folder\n";
        }

        return [true, $msg, $signed_apk, $output];
    } else {
        $msg = "❌ 签名失败，未生成文件。";
        echo "$msg\n";
        return [false, $msg, null, $output];
    }
}



//回编译
function rebuild_apk($apktool_path, $decode_folder, $output_apk = null) {
    if (!file_exists($apktool_path)) {
        $msg = "找不到 apktool 工具：$apktool_path";
        echo "$msg\n";
        return [false, $msg, null, null];
    }

    if (!is_dir($decode_folder)) {
        $msg = "反编译目录不存在：$decode_folder";
        echo "$msg\n";
        return [false, $msg, null, null];
    }

    // 如果未指定输出 APK 路径，则自动生成
    if (empty($output_apk)) {
        $parent_dir = dirname($decode_folder);
        $folder_name = basename($decode_folder);
        $output_apk = $parent_dir . DIRECTORY_SEPARATOR . $folder_name . '.build.apk';
    }

    // 构造打包命令
    $cmd = "java -jar \"$apktool_path\" b \"$decode_folder\" -o \"$output_apk\"";
    //echo "执行打包命令：$cmd\n";
    echo "开始回编译打包\n";

    // 执行命令
    $output = shell_exec($cmd);
    //echo "回编译输出：\n$output\n";

    // 检查输出文件
    if (file_exists($output_apk)) {
        $msg = "APK 回编译成功，输出文件：$output_apk";
        echo "$msg\n";
        return [true, $msg, $output_apk, $output];
    } else {
        $msg = "回编译失败，未生成 APK 文件。";
        echo "$msg\n";
        return [false, $msg, $output_apk, $output];
    }
}




function merge_smali_directories($source_dir, $target_dir) {
    // 1. 获取源目录中所有 smali 和 smali_* 目录
    $smali_dirs = [];
    foreach (scandir($source_dir) as $entry) {
        if (preg_match('/^smali(_classes\d+)?$/', $entry) && is_dir($source_dir . DIRECTORY_SEPARATOR . $entry)) {
            $smali_dirs[] = $entry;
        }
    }

    // 2. 获取目标目录已有的最大 smali_classesN 序号
    $existing = [];
    foreach (scandir($target_dir) as $entry) {
        if (preg_match('/^smali(_classes(\d+))?$/', $entry, $m) && is_dir($target_dir . DIRECTORY_SEPARATOR . $entry)) {
            $existing[] = isset($m[2]) ? intval($m[2]) : 1; // smali 视为 classes1
        }
    }
    $max_index = empty($existing) ? 0 : max($existing);

    // 3. 依次复制，每个递增命名
    foreach ($smali_dirs as $dir_name) {
        $src_path = $source_dir . DIRECTORY_SEPARATOR . $dir_name;
        $new_index = ++$max_index;
        $dst_name = $new_index === 1 ? 'smali' : 'smali_classes' . $new_index;
        $dst_path = $target_dir . DIRECTORY_SEPARATOR . $dst_name;

        // 递归复制目录
        recursive_copy($src_path, $dst_path);
        echo "已复制 $dir_name 到 $dst_name\n";
    }

    echo "smali 融合完成。\n";
    return true;
}

// 工具函数：递归复制目录内容
function recursive_copy($src, $dst) {
    if (!is_dir($src)) return;
    if (!file_exists($dst)) mkdir($dst, 0777, true);

    $items = scandir($src);
    foreach ($items as $item) {
        if ($item === '.' || $item === '..') continue;
        $src_item = $src . DIRECTORY_SEPARATOR . $item;
        $dst_item = $dst . DIRECTORY_SEPARATOR . $item;

        if (is_dir($src_item)) {
            recursive_copy($src_item, $dst_item);
        } else {
            copy($src_item, $dst_item);
        }
    }
}




//AndroidManifest融合
function merge_android_manifests($source_dir, $target_dir, $intent = false) {
    $src_manifest = rtrim($source_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'AndroidManifest.xml';
    $dst_manifest = rtrim($target_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'AndroidManifest.xml';

    if (!file_exists($src_manifest) || !file_exists($dst_manifest)) {
        echo "Manifest 文件不存在\n";
        return false;
    }

    $src_xml = file_get_contents($src_manifest);
    $dst_xml = file_get_contents($dst_manifest);

    // 1. 提取 <uses-permission>
    preg_match_all('/<uses-permission[^>]+\/>/', $src_xml, $src_permissions);
    preg_match_all('/<uses-permission[^>]+\/>/', $dst_xml, $dst_permissions);
    $src_permissions = array_unique($src_permissions[0]);
    $dst_permissions_text = implode("\n", $dst_permissions[0]);

    // 2. 提取 <permission>
    preg_match_all('/<permission[^>]+\/>/', $src_xml, $src_custom_permissions);
    preg_match_all('/<permission[^>]+\/>/', $dst_xml, $dst_custom_permissions);
    $src_custom_permissions = array_unique($src_custom_permissions[0]);
    $dst_custom_permissions_text = implode("\n", $dst_custom_permissions[0]);

    // 3. 提取 <activity> 和 <activity-alias>
    preg_match_all('/<activity\b[^>]*>.*?<\/activity>/is', $src_xml, $src_activities);
    preg_match_all('/<activity-alias\b[^>]*>.*?<\/activity-alias>/is', $src_xml, $src_aliases);
    $src_activities = $src_activities[0];
    $src_aliases = $src_aliases[0];
    $all_activities = array_merge($src_activities, $src_aliases);

    // 4. 处理 Activity，仅保留一个入口
    $entry_found = false;
    $processed_activities = [];

    foreach ($all_activities as $block) {
        if (!$entry_found && preg_match('/<intent-filter>.*?MAIN.*?LAUNCHER.*?<\/intent-filter>/is', $block)) {
            $processed_activities[] = "<!-- 此 activity 来自插入 -->\n" . $block;
            $entry_found = true;
        } else {
            $block_no_entry = preg_replace('/<intent-filter>.*?<\/intent-filter>/is', '', $block);
            $processed_activities[] = "<!-- 此 activity 来自插入 -->\n" . $block_no_entry;
        }
    }

    // 5. 注释原 manifest 中的 intent-filter 启动项
    if($intent){
        $dst_xml = preg_replace_callback(
            '/(<(activity|activity-alias)\b[^>]*>)(.*?<intent-filter>.*?<\/intent-filter>)(.*?<\/\2>)/is',
            function ($matches) {
                if (preg_match('/android.intent.action.MAIN/', $matches[3]) &&
                    preg_match('/android.intent.category.LAUNCHER/', $matches[3])) {
                    $commented = "<!-- 此处为原启动入口，intent-filter 已被注释 -->\n";
                    $commented .= preg_replace('/(<intent-filter>.*?<\/intent-filter>)/is', '<!-- $1 -->', $matches[3]);
                    return $matches[1] . "\n" . $commented . "\n" . $matches[4];
                }
                return $matches[0];
            },
            $dst_xml
        );
    }
    // 6. 插入 <permission>（自定义权限），避免重复
    foreach ($src_custom_permissions as $perm_def) {
        if (strpos($dst_custom_permissions_text, $perm_def) === false) {
            $insert = "    <!-- 此自定义权限来自插入 -->\n    $perm_def";
            //$dst_xml = preg_replace('/(<manifest[^>]*>)/', "$1\n$insert", $dst_xml);//插入后会导致无法安装
        }
    }

    // 7. 插入 <uses-permission>（跳过重复）
    foreach ($src_permissions as $perm) {
        if (strpos($dst_permissions_text, $perm) === false) {
            $insert = "    <!-- 此权限来自插入 -->\n    $perm";
            $dst_xml = preg_replace('/(<manifest[^>]*>)/', "$1\n$insert", $dst_xml);
        }
    }

    // 8. 插入 <activity> 和 <activity-alias> 到 <application> 中
    $insert_block = implode("\n    ", $processed_activities);
    $dst_xml = preg_replace_callback(
        '/<application[^>]*>/',
        function ($matches) use ($insert_block) {
            return $matches[0] . "\n    " . $insert_block;
        },
        $dst_xml,
        1
    );

    // 9. 保存结果
    file_put_contents($dst_manifest, $dst_xml);
    echo "合并完成：权限、自定义权限、Activity 合并，并保留注释信息：$dst_manifest\n";
    return true;
}





//找启动入口类名
function parse_apk_manifests($dirs) {
    $results = [];

    foreach ($dirs as $dir) {
        $manifest_path = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'AndroidManifest.xml';

        if (!file_exists($manifest_path)) {
            $results[] = [false, null, null, "Manifest 文件不存在：$manifest_path"];
            continue;
        }

        // 加载 XML
        $xml = new DOMDocument();
        libxml_use_internal_errors(true); // 忽略格式警告
        $xml->load($manifest_path);

        $xpath = new DOMXPath($xml);
        $launcher_activity = null;
        $source = null;

        // 查找所有 <activity>
        $activities = $xpath->query('//activity');
        foreach ($activities as $activity) {
            $intent_filters = $activity->getElementsByTagName('intent-filter');
            foreach ($intent_filters as $filter) {
                $has_main = false;
                $has_launcher = false;

                foreach ($filter->getElementsByTagName('action') as $action) {
                    if ($action->getAttribute('android:name') === 'android.intent.action.MAIN') {
                        $has_main = true;
                    }
                }

                foreach ($filter->getElementsByTagName('category') as $category) {
                    if ($category->getAttribute('android:name') === 'android.intent.category.LAUNCHER') {
                        $has_launcher = true;
                    }
                }

                if ($has_main && $has_launcher) {
                    $launcher_activity = $activity->getAttribute('android:name');
                    $source = 'activity';
                    break 2;
                }
            }
        }

        // 如果未找到，再查找 <activity-alias>
        if (!$launcher_activity) {
            $aliases = $xpath->query('//activity-alias');
            foreach ($aliases as $alias) {
                $intent_filters = $alias->getElementsByTagName('intent-filter');
                foreach ($intent_filters as $filter) {
                    $has_main = false;
                    $has_launcher = false;

                    foreach ($filter->getElementsByTagName('action') as $action) {
                        if ($action->getAttribute('android:name') === 'android.intent.action.MAIN') {
                            $has_main = true;
                        }
                    }

                    foreach ($filter->getElementsByTagName('category') as $category) {
                        if ($category->getAttribute('android:name') === 'android.intent.category.LAUNCHER') {
                            $has_launcher = true;
                        }
                    }

                    if ($has_main && $has_launcher) {
                        $launcher_activity = $alias->getAttribute('android:targetActivity');
                        $source = 'activity-alias';
                        break 2;
                    }
                }
            }
        }

        if ($launcher_activity !== null) {
            $results[] = [true, $launcher_activity, $source, null];
        } else {
            $results[] = [false, null, null, "未找到启动 Activity：$manifest_path"];
        }
    }

    return $results;
}










// 方法：反编译 APK
function decompile_apks($apktool_jar, $apk_files, $output_base_dir = null) {
    $results = [];

    // 检查所有 APK 文件是否存在
    foreach ($apk_files as $apk_file) {
        if (!file_exists($apk_file)) {
            return [[false, "APK 文件不存在：$apk_file", null, null]];
        }
    }

    // 遍历每个 APK 执行反编译
    foreach ($apk_files as $apk_file) {
        // 生成默认输出目录
        $apk_dir = dirname($apk_file);
        $apk_name = pathinfo($apk_file, PATHINFO_FILENAME);
        $output_dir = ($output_base_dir ?? $apk_dir) . DIRECTORY_SEPARATOR . $apk_name;

        // 构造反编译命令
        $cmd = "java -jar \"$apktool_jar\" d \"$apk_file\" -o \"$output_dir\" -f";

        // 执行命令
        $output = shell_exec($cmd);

        // 判断是否成功（通过输出目录是否存在判断）
        if (is_dir($output_dir)) {
            $results[] = [true, "反编译成功", $output_dir, $output];
        } else {
            $results[] = [false, "反编译失败", $output_dir, $output];
        }
    }

    return $results;
}

