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); // 获取 根节点 $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 "未找到 根节点\n"; return false; } // 合并 (去重) $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; } } // 合并 (去重) $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; } } */ // 获取 节点 $dst_app = $dst_doc->getElementsByTagName("application")->item(0); $src_app = $src_doc->getElementsByTagName("application")->item(0); if (!$dst_app || !$src_app) { echo "未找到 标签\n"; return false; } // 合并 (去掉 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"); // 获取 标签 $applications = $xml->getElementsByTagName("application"); if ($applications->length === 0) { return [false, "未找到 标签"]; } $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('/]*android:name="([^"]+)"/', $content, $match)) { return ['error' => "未找到 application 的 android:name 属性"]; } $class_name = $match[1]; // 处理相对类名(.MyApp)拼接包名 if (substr($class_name, 0, 1) === '.') { if (preg_match('/]*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. 提取 preg_match_all('/]+\/>/', $src_xml, $src_permissions); preg_match_all('/]+\/>/', $dst_xml, $dst_permissions); $src_permissions = array_unique($src_permissions[0]); $dst_permissions_text = implode("\n", $dst_permissions[0]); // 2. 提取 preg_match_all('/]+\/>/', $src_xml, $src_custom_permissions); preg_match_all('/]+\/>/', $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. 提取 preg_match_all('/]*>.*?<\/activity>/is', $src_xml, $src_activities); preg_match_all('/]*>.*?<\/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('/.*?MAIN.*?LAUNCHER.*?<\/intent-filter>/is', $block)) { $processed_activities[] = "\n" . $block; $entry_found = true; } else { $block_no_entry = preg_replace('/.*?<\/intent-filter>/is', '', $block); $processed_activities[] = "\n" . $block_no_entry; } } // 5. 注释原 manifest 中的 intent-filter 启动项 if($intent){ $dst_xml = preg_replace_callback( '/(<(activity|activity-alias)\b[^>]*>)(.*?.*?<\/intent-filter>)(.*?<\/\2>)/is', function ($matches) { if (preg_match('/android.intent.action.MAIN/', $matches[3]) && preg_match('/android.intent.category.LAUNCHER/', $matches[3])) { $commented = "\n"; $commented .= preg_replace('/(.*?<\/intent-filter>)/is', '', $matches[3]); return $matches[1] . "\n" . $commented . "\n" . $matches[4]; } return $matches[0]; }, $dst_xml ); } // 6. 插入 (自定义权限),避免重复 foreach ($src_custom_permissions as $perm_def) { if (strpos($dst_custom_permissions_text, $perm_def) === false) { $insert = " \n $perm_def"; //$dst_xml = preg_replace('/(]*>)/', "$1\n$insert", $dst_xml);//插入后会导致无法安装 } } // 7. 插入 (跳过重复) foreach ($src_permissions as $perm) { if (strpos($dst_permissions_text, $perm) === false) { $insert = " \n $perm"; $dst_xml = preg_replace('/(]*>)/', "$1\n$insert", $dst_xml); } } // 8. 插入 中 $insert_block = implode("\n ", $processed_activities); $dst_xml = preg_replace_callback( '/]*>/', 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; // 查找所有 $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; } } } // 如果未找到,再查找 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; }