为Clash for Android内置配置
zip目录结构
f.zip/
├── databases/*
├── files/*
├── shared_prefs/*
方法一:编写模块
思路:写入url配置后打包文件,将zip硬编码base64预置xposed模块String常量,hook主Activity的启动并释放文件到data目录,顺便写个弹窗装备,最后lspatch打包:
package com.sunight.rejectconfig;
import android.app.AlertDialog;
import android.content.Context;
import android.util.Base64;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class HookInit implements IXposedHookLoadPackage {
private static final String TARGET_ACTIVITY = "com.github.kr328.clash.MainActivity";
private static final String TARGET_PACKAGE = "com.github.metacubex.clash.meta";
private static final String TARGET_PARENT_DIR = "/data/user/0/" + TARGET_PACKAGE + "/";
private static final String BASE64_ZIP = "自行生成";
@Override
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.equals(TARGET_PACKAGE)) {
return;
}
XposedBridge.log("目标应用已加载 - " + lpparam.packageName);
// 直接Hook主Activity的onCreate方法
XposedHelpers.findAndHookMethod(
TARGET_ACTIVITY,
lpparam.classLoader,
"onCreate",
android.os.Bundle.class,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
extractZipToParentDir();
Context context = (Context) param.thisObject;
// 在主线程显示对话框
new AlertDialog.Builder(context)
.setTitle("嘻嘻")
.setMessage("Url配置文件已释放")
.setPositiveButton("我知道了", null)
.show();
}
});
}
private void extractZipToParentDir() {
try {
// 解码Base64字符串
byte[] zipBytes = Base64.decode(BASE64_ZIP, Base64.DEFAULT);
// 创建目标目录
File targetDir = new File(TARGET_PARENT_DIR);
if (!targetDir.exists()) {
targetDir.mkdirs();
}
// 解压ZIP文件
extractZip(new ByteArrayInputStream(zipBytes), targetDir);
XposedBridge.log("成功解压ZIP文件到 - " + TARGET_PARENT_DIR);
// 设置目录权限
setFilePermissions(targetDir);
} catch (Exception e) {
XposedBridge.log("解压失败 - " + e.getMessage());
}
}
private void extractZip(ByteArrayInputStream bais, File targetDir) throws Exception {
ZipInputStream zis = null;
try {
zis = new ZipInputStream(bais);
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
String entryName = entry.getName();
File entryFile = new File(targetDir, entryName);
if (entry.isDirectory()) {
entryFile.mkdirs();
} else {
// 确保父目录存在
entryFile.getParentFile().mkdirs();
// 写入文件内容
FileOutputStream fos = null;
try {
fos = new FileOutputStream(entryFile);
byte[] buffer = new byte[1024];
int length;
while ((length = zis.read(buffer)) > 0) {
fos.write(buffer, 0, length);
}
} finally {
if (fos != null) {
fos.close();
}
}
}
zis.closeEntry();
}
} finally {
if (zis != null) {
zis.close();
}
}
}
private void setFilePermissions(File file) {
try {
// 递归设置目录权限为755 (rwxr-xr-x)
Runtime.getRuntime().exec("chmod -R 755 " + file.getAbsolutePath()).waitFor();
// 递归设置文件所有者为目标应用
String command = "chown -R " + TARGET_PACKAGE + ":" + TARGET_PACKAGE + " " + file.getAbsolutePath();
Runtime.getRuntime().exec(command).waitFor();
XposedBridge.log("已设置权限 - " + file.getAbsolutePath());
} catch (Exception e) {
XposedBridge.log("设置权限失败 - " + e.getMessage());
}
}
}
效果图:
方法二:Smali注入
思路:与方法一相同,解决Lspatch不适配安卓9以下的问题
进入com.github.kr328.clash.MainActivity
新增以下smali代码
.field private static final BASE64_ZIP:Ljava/lang/String; = "自行生成"
.field private static final TARGET_ACTIVITY:Ljava/lang/String; = "com.github.kr328.clash.MainActivity"
.field private static final TARGET_PACKAGE:Ljava/lang/String; = "com.github.metacubex.clash.meta"
.field private static final TARGET_PARENT_DIR:Ljava/lang/String; = "/data/user/0/com.github.metacubex.clash.meta/"
.method private extractZip(Ljava/io/ByteArrayInputStream;Ljava/io/File;)V
.registers 8
.annotation system Ldalvik/annotation/Signature;
value = {
"(",
"Ljava/io/ByteArrayInputStream;",
"Ljava/io/File;",
")V^",
"Ljava/lang/Exception;"
}
.end annotation
.annotation system Ldalvik/annotation/Throws;
value = {
Ljava/lang/Exception;
}
.end annotation
.line 105
const/4 v0, 0x0
move-object v1, v0
check-cast v1, Ljava/util/zip/ZipInputStream;
.line 107
:try_start_4
new-instance v1, Ljava/util/zip/ZipInputStream;
invoke-direct {v1, p1}, Ljava/util/zip/ZipInputStream;-><init>(Ljava/io/InputStream;)V
:try_end_9
.catchall {:try_start_4 .. :try_end_9} :catchall_5c
.line 108
nop
.line 110
:goto_a
:try_start_a
invoke-virtual {v1}, Ljava/util/zip/ZipInputStream;->getNextEntry()Ljava/util/zip/ZipEntry;
move-result-object p1
:try_end_e
.catchall {:try_start_a .. :try_end_e} :catchall_59
if-nez p1, :cond_15
.line 138
nop
.line 139
invoke-virtual {v1}, Ljava/util/zip/ZipInputStream;->close()V
return-void
.line 111
:cond_15
:try_start_15
invoke-virtual {p1}, Ljava/util/zip/ZipEntry;->getName()Ljava/lang/String;
move-result-object v2
.line 112
new-instance v3, Ljava/io/File;
invoke-direct {v3, p2, v2}, Ljava/io/File;-><init>(Ljava/io/File;Ljava/lang/String;)V
.line 114
invoke-virtual {p1}, Ljava/util/zip/ZipEntry;->isDirectory()Z
move-result p1
if-eqz p1, :cond_28
.line 115
invoke-virtual {v3}, Ljava/io/File;->mkdirs()Z
goto :goto_46
.line 118
:cond_28
invoke-virtual {v3}, Ljava/io/File;->getParentFile()Ljava/io/File;
move-result-object p1
invoke-virtual {p1}, Ljava/io/File;->mkdirs()Z
.line 121
move-object p1, v0
check-cast p1, Ljava/io/FileOutputStream;
:try_end_32
.catchall {:try_start_15 .. :try_end_32} :catchall_59
.line 123
:try_start_32
new-instance p1, Ljava/io/FileOutputStream;
invoke-direct {p1, v3}, Ljava/io/FileOutputStream;-><init>(Ljava/io/File;)V
:try_end_37
.catchall {:try_start_32 .. :try_end_37} :catchall_52
.line 124
const/16 v2, 0x400
:try_start_39
new-array v2, v2, [B
.line 125
nop
.line 126
:goto_3c
invoke-virtual {v1, v2}, Ljava/util/zip/ZipInputStream;->read([B)I
move-result v3
:try_end_40
.catchall {:try_start_39 .. :try_end_40} :catchall_4f
if-gtz v3, :cond_4a
.line 130
nop
.line 131
:try_start_43
invoke-virtual {p1}, Ljava/io/FileOutputStream;->close()V
.line 135
:goto_46
invoke-virtual {v1}, Ljava/util/zip/ZipInputStream;->closeEntry()V
:try_end_49
.catchall {:try_start_43 .. :try_end_49} :catchall_59
goto :goto_a
.line 127
:cond_4a
const/4 v4, 0x0
:try_start_4b
invoke-virtual {p1, v2, v4, v3}, Ljava/io/FileOutputStream;->write([BII)V
:try_end_4e
.catchall {:try_start_4b .. :try_end_4e} :catchall_4f
goto :goto_3c
.line 126
:catchall_4f
move-exception p2
move-object v0, p1
goto :goto_53
:catchall_52
move-exception p2
.line 130
:goto_53
if-eqz v0, :cond_58
.line 131
:try_start_55
invoke-virtual {v0}, Ljava/io/FileOutputStream;->close()V
:cond_58
throw p2
:try_end_59
.catchall {:try_start_55 .. :try_end_59} :catchall_59
.line 110
:catchall_59
move-exception p1
move-object v0, v1
goto :goto_5d
:catchall_5c
move-exception p1
.line 138
:goto_5d
if-eqz v0, :cond_62
.line 139
invoke-virtual {v0}, Ljava/util/zip/ZipInputStream;->close()V
:cond_62
throw p1
.end method
.method private extractZipToParentDir()V
.registers 5
.annotation system Ldalvik/annotation/Signature;
value = {
"()V"
}
.end annotation
.line 83
const-string v0, "/data/user/0/com.github.metacubex.clash.meta/"
:try_start_2
const-string v1, "自行生成"
const/4 v2, 0x0
invoke-static {v1, v2}, Landroid/util/Base64;->decode(Ljava/lang/String;I)[B
move-result-object v1
.line 86
new-instance v2, Ljava/io/File;
invoke-direct {v2, v0}, Ljava/io/File;-><init>(Ljava/lang/String;)V
.line 87
invoke-virtual {v2}, Ljava/io/File;->exists()Z
move-result v3
if-nez v3, :cond_17
.line 88
invoke-virtual {v2}, Ljava/io/File;->mkdirs()Z
.line 92
:cond_17
new-instance v3, Ljava/io/ByteArrayInputStream;
invoke-direct {v3, v1}, Ljava/io/ByteArrayInputStream;-><init>([B)V
invoke-direct {p0, v3, v2}, Lcom/github/kr328/clash/MainActivity;->extractZip(Ljava/io/ByteArrayInputStream;Ljava/io/File;)V
.line 94
new-instance v1, Ljava/lang/StringBuffer;
invoke-direct {v1}, Ljava/lang/StringBuffer;-><init>()V
const-string v3, "ClashMetaZipInjector: 成功解压ZIP文件到 - "
invoke-virtual {v1, v3}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v1
invoke-virtual {v1, v0}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v0
invoke-virtual {v0}, Ljava/lang/StringBuffer;->toString()Ljava/lang/String;
move-result-object v0
.line 97
invoke-direct {p0, v2}, Lcom/github/kr328/clash/MainActivity;->setFilePermissions(Ljava/io/File;)V
:try_end_35
.catch Ljava/lang/Exception; {:try_start_2 .. :try_end_35} :catch_36
goto :goto_4e
:catch_36
move-exception v0
.line 100
new-instance v1, Ljava/lang/StringBuffer;
invoke-direct {v1}, Ljava/lang/StringBuffer;-><init>()V
const-string v2, "ClashMetaZipInjector: 解压失败 - "
invoke-virtual {v1, v2}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v1
invoke-virtual {v0}, Ljava/lang/Exception;->getMessage()Ljava/lang/String;
move-result-object v0
invoke-virtual {v1, v0}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v0
invoke-virtual {v0}, Ljava/lang/StringBuffer;->toString()Ljava/lang/String;
move-result-object v0
:goto_4e
return-void
.end method
.method private setFilePermissions(Ljava/io/File;)V
.registers 9
.annotation system Ldalvik/annotation/Signature;
value = {
"(",
"Ljava/io/File;",
")V"
}
.end annotation
.line 147
const-string v0, "com.github.metacubex.clash.meta"
:try_start_2
invoke-static {}, Ljava/lang/Runtime;->getRuntime()Ljava/lang/Runtime;
move-result-object v1
new-instance v2, Ljava/lang/StringBuffer;
invoke-direct {v2}, Ljava/lang/StringBuffer;-><init>()V
const-string v3, "chmod -R 755 "
invoke-virtual {v2, v3}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v2
invoke-virtual {p1}, Ljava/io/File;->getAbsolutePath()Ljava/lang/String;
move-result-object v3
invoke-virtual {v2, v3}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v2
invoke-virtual {v2}, Ljava/lang/StringBuffer;->toString()Ljava/lang/String;
move-result-object v2
invoke-virtual {v1, v2}, Ljava/lang/Runtime;->exec(Ljava/lang/String;)Ljava/lang/Process;
move-result-object v1
invoke-virtual {v1}, Ljava/lang/Process;->waitFor()I
.line 150
new-instance v1, Ljava/lang/StringBuffer;
invoke-direct {v1}, Ljava/lang/StringBuffer;-><init>()V
new-instance v2, Ljava/lang/StringBuffer;
invoke-direct {v2}, Ljava/lang/StringBuffer;-><init>()V
new-instance v3, Ljava/lang/StringBuffer;
invoke-direct {v3}, Ljava/lang/StringBuffer;-><init>()V
new-instance v4, Ljava/lang/StringBuffer;
invoke-direct {v4}, Ljava/lang/StringBuffer;-><init>()V
new-instance v5, Ljava/lang/StringBuffer;
invoke-direct {v5}, Ljava/lang/StringBuffer;-><init>()V
const-string v6, "chown -R "
invoke-virtual {v5, v6}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v5
invoke-virtual {v5, v0}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v5
invoke-virtual {v5}, Ljava/lang/StringBuffer;->toString()Ljava/lang/String;
move-result-object v5
invoke-virtual {v4, v5}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v4
const-string v5, ":"
invoke-virtual {v4, v5}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v4
invoke-virtual {v4}, Ljava/lang/StringBuffer;->toString()Ljava/lang/String;
move-result-object v4
invoke-virtual {v3, v4}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v3
invoke-virtual {v3, v0}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v0
invoke-virtual {v0}, Ljava/lang/StringBuffer;->toString()Ljava/lang/String;
move-result-object v0
invoke-virtual {v2, v0}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v0
const-string v2, " "
invoke-virtual {v0, v2}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v0
invoke-virtual {v0}, Ljava/lang/StringBuffer;->toString()Ljava/lang/String;
move-result-object v0
invoke-virtual {v1, v0}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v0
invoke-virtual {p1}, Ljava/io/File;->getAbsolutePath()Ljava/lang/String;
move-result-object v1
invoke-virtual {v0, v1}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v0
invoke-virtual {v0}, Ljava/lang/StringBuffer;->toString()Ljava/lang/String;
move-result-object v0
.line 151
invoke-static {}, Ljava/lang/Runtime;->getRuntime()Ljava/lang/Runtime;
move-result-object v1
invoke-virtual {v1, v0}, Ljava/lang/Runtime;->exec(Ljava/lang/String;)Ljava/lang/Process;
move-result-object v0
invoke-virtual {v0}, Ljava/lang/Process;->waitFor()I
.line 153
new-instance v0, Ljava/lang/StringBuffer;
invoke-direct {v0}, Ljava/lang/StringBuffer;-><init>()V
const-string v1, "ClashMetaZipInjector: 已设置权限 - "
invoke-virtual {v0, v1}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v0
invoke-virtual {p1}, Ljava/io/File;->getAbsolutePath()Ljava/lang/String;
move-result-object p1
invoke-virtual {v0, p1}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object p1
invoke-virtual {p1}, Ljava/lang/StringBuffer;->toString()Ljava/lang/String;
move-result-object p1
:try_end_a5
.catch Ljava/lang/Exception; {:try_start_2 .. :try_end_a5} :catch_a6
goto :goto_be
:catch_a6
move-exception p1
.line 155
new-instance v0, Ljava/lang/StringBuffer;
invoke-direct {v0}, Ljava/lang/StringBuffer;-><init>()V
const-string v1, "ClashMetaZipInjector: 设置权限失败 - "
invoke-virtual {v0, v1}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v0
invoke-virtual {p1}, Ljava/lang/Exception;->getMessage()Ljava/lang/String;
move-result-object p1
invoke-virtual {v0, p1}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object p1
invoke-virtual {p1}, Ljava/lang/StringBuffer;->toString()Ljava/lang/String;
move-result-object p1
:goto_be
return-void
.end method
onCreate内插入:
invoke-direct {p0}, Lcom/github/kr328/clash/MainActivity;->extractZipToParentDir()V
为Clash for Android内置配置
https://sunight.cn/archives/sbWmRMV8