feat:增加kpm注入功能
This commit is contained in:
@@ -18,6 +18,9 @@ public class ConfigManager {
|
|||||||
public static final String MODULE_PATH = "/data/adb/modules/zygisk-myinjector";
|
public static final String MODULE_PATH = "/data/adb/modules/zygisk-myinjector";
|
||||||
public static final String CONFIG_FILE = MODULE_PATH + "/config.json";
|
public static final String CONFIG_FILE = MODULE_PATH + "/config.json";
|
||||||
public static final String SO_STORAGE_DIR = MODULE_PATH + "/so_files";
|
public static final String SO_STORAGE_DIR = MODULE_PATH + "/so_files";
|
||||||
|
public static final String KPM_MODULE_PATH = MODULE_PATH + "/injectHide.kpm";
|
||||||
|
public static final String KPM_HIDE_CONFIG = MODULE_PATH + "/kpm_hide_config.txt";
|
||||||
|
private static final String KPM_MODULE_NAME = "hideInject";
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final Gson gson;
|
private final Gson gson;
|
||||||
@@ -651,6 +654,220 @@ public class ConfigManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== KPM Module Management ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查 KPM 模块是否已加载
|
||||||
|
*/
|
||||||
|
public boolean isKpmModuleLoaded() {
|
||||||
|
Shell.Result result = Shell.cmd("lsmod | grep " + KPM_MODULE_NAME).exec();
|
||||||
|
return result.isSuccess() && !result.getOut().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载 KPM 模块
|
||||||
|
*/
|
||||||
|
public boolean loadKpmModule() {
|
||||||
|
if (!isRootAvailable()) {
|
||||||
|
Log.e(TAG, "Root access not available!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if module file exists
|
||||||
|
Shell.Result checkResult = Shell.cmd("test -f \"" + KPM_MODULE_PATH + "\" && echo 'exists'").exec();
|
||||||
|
if (!checkResult.isSuccess() || checkResult.getOut().isEmpty()) {
|
||||||
|
Log.e(TAG, "KPM module file not found: " + KPM_MODULE_PATH);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if already loaded
|
||||||
|
if (isKpmModuleLoaded()) {
|
||||||
|
Log.i(TAG, "KPM module already loaded");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load module
|
||||||
|
Shell.Result result = Shell.cmd("insmod \"" + KPM_MODULE_PATH + "\"").exec();
|
||||||
|
if (result.isSuccess()) {
|
||||||
|
Log.i(TAG, "KPM module loaded successfully");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Failed to load KPM module: " + String.join("\n", result.getErr()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 卸载 KPM 模块
|
||||||
|
*/
|
||||||
|
public boolean unloadKpmModule() {
|
||||||
|
if (!isRootAvailable()) {
|
||||||
|
Log.e(TAG, "Root access not available!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isKpmModuleLoaded()) {
|
||||||
|
Log.i(TAG, "KPM module not loaded");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shell.Result result = Shell.cmd("rmmod " + KPM_MODULE_NAME).exec();
|
||||||
|
if (result.isSuccess()) {
|
||||||
|
Log.i(TAG, "KPM module unloaded successfully");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Failed to unload KPM module: " + String.join("\n", result.getErr()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新加载 KPM 模块
|
||||||
|
*/
|
||||||
|
public boolean reloadKpmModule() {
|
||||||
|
Log.i(TAG, "Reloading KPM module...");
|
||||||
|
|
||||||
|
// Unload first
|
||||||
|
if (isKpmModuleLoaded()) {
|
||||||
|
if (!unloadKpmModule()) {
|
||||||
|
Log.e(TAG, "Failed to unload module before reload");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Give kernel time to cleanup
|
||||||
|
try {
|
||||||
|
Thread.sleep(500);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load again
|
||||||
|
return loadKpmModule();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新 KPM 隐藏配置并重载模块
|
||||||
|
*/
|
||||||
|
public boolean updateKpmHideConfig(List<String> soNames) {
|
||||||
|
if (!isRootAvailable()) {
|
||||||
|
Log.e(TAG, "Root access not available!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build config content
|
||||||
|
StringBuilder configContent = new StringBuilder();
|
||||||
|
for (String soName : soNames) {
|
||||||
|
configContent.append(soName).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to temp file
|
||||||
|
String tempFile = context.getCacheDir() + "/kpm_hide_config.txt";
|
||||||
|
try {
|
||||||
|
java.io.FileWriter writer = new java.io.FileWriter(tempFile);
|
||||||
|
writer.write(configContent.toString());
|
||||||
|
writer.close();
|
||||||
|
|
||||||
|
// Ensure module directory exists
|
||||||
|
Shell.cmd("mkdir -p \"" + MODULE_PATH + "\"").exec();
|
||||||
|
|
||||||
|
// Copy to module directory with root
|
||||||
|
Shell.Result copyResult = Shell.cmd("cp \"" + tempFile + "\" \"" + KPM_HIDE_CONFIG + "\"").exec();
|
||||||
|
if (!copyResult.isSuccess()) {
|
||||||
|
Log.e(TAG, "Failed to copy KPM config: " + String.join("\n", copyResult.getErr()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shell.cmd("chmod 644 \"" + KPM_HIDE_CONFIG + "\"").exec();
|
||||||
|
|
||||||
|
// Clean up temp file
|
||||||
|
new File(tempFile).delete();
|
||||||
|
|
||||||
|
Log.i(TAG, "KPM hide config updated with " + soNames.size() + " entries");
|
||||||
|
|
||||||
|
// Reload module to apply changes
|
||||||
|
return reloadKpmModule();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Failed to update KPM config", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前隐藏的 SO 列表
|
||||||
|
*/
|
||||||
|
public List<String> getHiddenSoList() {
|
||||||
|
List<String> hiddenList = new ArrayList<>();
|
||||||
|
|
||||||
|
Shell.Result result = Shell.cmd("cat \"" + KPM_HIDE_CONFIG + "\"").exec();
|
||||||
|
if (result.isSuccess()) {
|
||||||
|
for (String line : result.getOut()) {
|
||||||
|
String trimmed = line.trim();
|
||||||
|
if (!trimmed.isEmpty()) {
|
||||||
|
hiddenList.add(trimmed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hiddenList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加 SO 到隐藏列表
|
||||||
|
*/
|
||||||
|
public boolean addSoToHideList(String soName) {
|
||||||
|
List<String> hiddenList = getHiddenSoList();
|
||||||
|
if (!hiddenList.contains(soName)) {
|
||||||
|
hiddenList.add(soName);
|
||||||
|
return updateKpmHideConfig(hiddenList);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从隐藏列表移除 SO
|
||||||
|
*/
|
||||||
|
public boolean removeSoFromHideList(String soName) {
|
||||||
|
List<String> hiddenList = getHiddenSoList();
|
||||||
|
if (hiddenList.remove(soName)) {
|
||||||
|
return updateKpmHideConfig(hiddenList);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有可隐藏的 SO 文件列表(从已启用应用中提取)
|
||||||
|
*/
|
||||||
|
public List<String> getAvailableSoList() {
|
||||||
|
List<String> availableSos = new ArrayList<>();
|
||||||
|
|
||||||
|
// Always include our injector library
|
||||||
|
availableSos.add("libmyinjector.so");
|
||||||
|
|
||||||
|
// Add SOs from all enabled apps
|
||||||
|
for (Map.Entry<String, AppConfig> entry : config.perAppConfig.entrySet()) {
|
||||||
|
AppConfig appConfig = entry.getValue();
|
||||||
|
if (appConfig.enabled && appConfig.soFiles != null) {
|
||||||
|
for (SoFile soFile : appConfig.soFiles) {
|
||||||
|
if (!availableSos.contains(soFile.name)) {
|
||||||
|
availableSos.add(soFile.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add global SO files
|
||||||
|
if (config.globalSoFiles != null) {
|
||||||
|
for (SoFile soFile : config.globalSoFiles) {
|
||||||
|
if (!availableSos.contains(soFile.name)) {
|
||||||
|
availableSos.add(soFile.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return availableSos;
|
||||||
|
}
|
||||||
|
|
||||||
// Data classes
|
// Data classes
|
||||||
public static class ModuleConfig {
|
public static class ModuleConfig {
|
||||||
public boolean enabled = true;
|
public boolean enabled = true;
|
||||||
|
|||||||
105
configapp/src/main/java/com/jiqiu/configapp/HideSoAdapter.java
Normal file
105
configapp/src/main/java/com/jiqiu/configapp/HideSoAdapter.java
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package com.jiqiu.configapp;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SO 文件隐藏列表适配器
|
||||||
|
*/
|
||||||
|
public class HideSoAdapter extends RecyclerView.Adapter<HideSoAdapter.ViewHolder> {
|
||||||
|
|
||||||
|
private final List<KpmHideFragment.HideSoItem> items;
|
||||||
|
private final OnItemCheckedChangeListener listener;
|
||||||
|
|
||||||
|
public interface OnItemCheckedChangeListener {
|
||||||
|
void onItemCheckedChanged(KpmHideFragment.HideSoItem item, boolean isChecked);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HideSoAdapter(List<KpmHideFragment.HideSoItem> items,
|
||||||
|
OnItemCheckedChangeListener listener) {
|
||||||
|
this.items = items;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext())
|
||||||
|
.inflate(R.layout.item_hide_so, parent, false);
|
||||||
|
return new ViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
|
KpmHideFragment.HideSoItem item = items.get(position);
|
||||||
|
holder.bind(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return items.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
|
private final CheckBox cbHide;
|
||||||
|
private final TextView tvSoName;
|
||||||
|
private final TextView tvSoStatus;
|
||||||
|
private final TextView tvFixedBadge;
|
||||||
|
|
||||||
|
public ViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
cbHide = itemView.findViewById(R.id.cbHide);
|
||||||
|
tvSoName = itemView.findViewById(R.id.tvSoName);
|
||||||
|
tvSoStatus = itemView.findViewById(R.id.tvSoStatus);
|
||||||
|
tvFixedBadge = itemView.findViewById(R.id.tvFixedBadge);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind(KpmHideFragment.HideSoItem item) {
|
||||||
|
tvSoName.setText(item.soName);
|
||||||
|
|
||||||
|
// 设置勾选状态
|
||||||
|
cbHide.setOnCheckedChangeListener(null); // 先移除监听器避免触发
|
||||||
|
cbHide.setChecked(item.isHidden);
|
||||||
|
|
||||||
|
// 显示状态
|
||||||
|
if (item.isFixed) {
|
||||||
|
tvSoStatus.setText("必需项 - 始终隐藏");
|
||||||
|
tvSoStatus.setTextColor(itemView.getContext().getResources()
|
||||||
|
.getColor(android.R.color.holo_green_dark, null));
|
||||||
|
tvFixedBadge.setVisibility(View.VISIBLE);
|
||||||
|
cbHide.setEnabled(false);
|
||||||
|
cbHide.setChecked(true); // 固定项始终勾选
|
||||||
|
} else {
|
||||||
|
tvSoStatus.setText(item.isHidden ? "已隐藏" : "未隐藏");
|
||||||
|
tvSoStatus.setTextColor(itemView.getContext().getResources()
|
||||||
|
.getColor(android.R.color.darker_gray, null));
|
||||||
|
tvFixedBadge.setVisibility(View.GONE);
|
||||||
|
cbHide.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置点击监听器
|
||||||
|
cbHide.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
|
if (listener != null && !item.isFixed) {
|
||||||
|
listener.onItemCheckedChanged(item, isChecked);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 整行点击也触发勾选
|
||||||
|
itemView.setOnClickListener(v -> {
|
||||||
|
if (!item.isFixed) {
|
||||||
|
cbHide.setChecked(!cbHide.isChecked());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
249
configapp/src/main/java/com/jiqiu/configapp/KpmHideFragment.java
Normal file
249
configapp/src/main/java/com/jiqiu/configapp/KpmHideFragment.java
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
package com.jiqiu.configapp;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.google.android.material.card.MaterialCardView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* KPM 隐藏功能 Fragment
|
||||||
|
* 管理 KPM 内核模块和 SO 文件隐藏配置
|
||||||
|
*/
|
||||||
|
public class KpmHideFragment extends Fragment {
|
||||||
|
|
||||||
|
private static final String TAG = "KpmHideFragment";
|
||||||
|
|
||||||
|
private TextView tvModuleStatus;
|
||||||
|
private TextView tvModuleInfo;
|
||||||
|
private TextView tvConfigPath;
|
||||||
|
private Button btnReloadModule;
|
||||||
|
private RecyclerView rvSoList;
|
||||||
|
|
||||||
|
private ConfigManager configManager;
|
||||||
|
private HideSoAdapter adapter;
|
||||||
|
private ExecutorService executor;
|
||||||
|
private Handler mainHandler;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||||
|
@Nullable Bundle savedInstanceState) {
|
||||||
|
return inflater.inflate(R.layout.fragment_kpm_hide, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
initViews(view);
|
||||||
|
initExecutor();
|
||||||
|
initConfigManager();
|
||||||
|
setupListeners();
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initViews(View view) {
|
||||||
|
tvModuleStatus = view.findViewById(R.id.tvModuleStatus);
|
||||||
|
tvModuleInfo = view.findViewById(R.id.tvModuleInfo);
|
||||||
|
tvConfigPath = view.findViewById(R.id.tvConfigPath);
|
||||||
|
btnReloadModule = view.findViewById(R.id.btnReloadModule);
|
||||||
|
rvSoList = view.findViewById(R.id.rvSoList);
|
||||||
|
|
||||||
|
rvSoList.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initExecutor() {
|
||||||
|
executor = Executors.newSingleThreadExecutor();
|
||||||
|
mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initConfigManager() {
|
||||||
|
configManager = new ConfigManager(getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupListeners() {
|
||||||
|
btnReloadModule.setOnClickListener(v -> reloadModule());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadData() {
|
||||||
|
// 显示配置路径
|
||||||
|
tvConfigPath.setText("配置文件: " + ConfigManager.KPM_HIDE_CONFIG);
|
||||||
|
|
||||||
|
// 异步加载模块状态和 SO 列表
|
||||||
|
executor.execute(() -> {
|
||||||
|
try {
|
||||||
|
final boolean isLoaded = configManager.isKpmModuleLoaded();
|
||||||
|
final List<String> availableSos = configManager.getAvailableSoList();
|
||||||
|
final List<String> hiddenSos = configManager.getHiddenSoList();
|
||||||
|
|
||||||
|
mainHandler.post(() -> {
|
||||||
|
updateModuleStatus(isLoaded);
|
||||||
|
setupSoList(availableSos, hiddenSos);
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error loading data", e);
|
||||||
|
mainHandler.post(() -> {
|
||||||
|
Toast.makeText(getContext(), "加载数据失败: " + e.getMessage(),
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateModuleStatus(boolean isLoaded) {
|
||||||
|
if (isLoaded) {
|
||||||
|
tvModuleStatus.setText("● 已加载");
|
||||||
|
tvModuleStatus.setTextColor(getResources().getColor(android.R.color.holo_green_dark, null));
|
||||||
|
tvModuleInfo.setText("KPM 内核模块运行中\n模块名称: hideInject\n版本: 0.0.1");
|
||||||
|
} else {
|
||||||
|
tvModuleStatus.setText("● 未加载");
|
||||||
|
tvModuleStatus.setTextColor(getResources().getColor(android.R.color.holo_red_dark, null));
|
||||||
|
tvModuleInfo.setText("KPM 内核模块未运行\n请检查模块文件是否存在或手动重载");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSoList(List<String> availableSos, List<String> hiddenSos) {
|
||||||
|
List<HideSoItem> items = new ArrayList<>();
|
||||||
|
|
||||||
|
for (String soName : availableSos) {
|
||||||
|
HideSoItem item = new HideSoItem();
|
||||||
|
item.soName = soName;
|
||||||
|
item.isHidden = hiddenSos.contains(soName);
|
||||||
|
// libmyinjector.so 是固定隐藏的
|
||||||
|
item.isFixed = soName.equals("libmyinjector.so");
|
||||||
|
items.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter = new HideSoAdapter(items, this::onSoItemCheckedChanged);
|
||||||
|
rvSoList.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onSoItemCheckedChanged(HideSoItem item, boolean isChecked) {
|
||||||
|
if (item.isFixed) {
|
||||||
|
// 固定项不允许取消勾选
|
||||||
|
Toast.makeText(getContext(), "libmyinjector.so 是必需的,不能取消隐藏",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步更新配置
|
||||||
|
executor.execute(() -> {
|
||||||
|
try {
|
||||||
|
boolean success;
|
||||||
|
if (isChecked) {
|
||||||
|
success = configManager.addSoToHideList(item.soName);
|
||||||
|
} else {
|
||||||
|
success = configManager.removeSoFromHideList(item.soName);
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean finalSuccess = success;
|
||||||
|
mainHandler.post(() -> {
|
||||||
|
if (finalSuccess) {
|
||||||
|
item.isHidden = isChecked;
|
||||||
|
Toast.makeText(getContext(),
|
||||||
|
isChecked ? "已添加到隐藏列表" : "已从隐藏列表移除",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
// 更新模块状态
|
||||||
|
refreshModuleStatus();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "更新失败,请检查日志",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
// 恢复原来的状态
|
||||||
|
if (adapter != null) {
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error updating SO hide status", e);
|
||||||
|
mainHandler.post(() -> {
|
||||||
|
Toast.makeText(getContext(), "更新失败: " + e.getMessage(),
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
if (adapter != null) {
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reloadModule() {
|
||||||
|
btnReloadModule.setEnabled(false);
|
||||||
|
btnReloadModule.setText("重载中...");
|
||||||
|
|
||||||
|
executor.execute(() -> {
|
||||||
|
try {
|
||||||
|
final boolean success = configManager.reloadKpmModule();
|
||||||
|
|
||||||
|
mainHandler.post(() -> {
|
||||||
|
btnReloadModule.setEnabled(true);
|
||||||
|
btnReloadModule.setText("重载模块");
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
Toast.makeText(getContext(), "模块重载成功", Toast.LENGTH_SHORT).show();
|
||||||
|
refreshModuleStatus();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "模块重载失败,请查看日志",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error reloading module", e);
|
||||||
|
mainHandler.post(() -> {
|
||||||
|
btnReloadModule.setEnabled(true);
|
||||||
|
btnReloadModule.setText("重载模块");
|
||||||
|
Toast.makeText(getContext(), "重载失败: " + e.getMessage(),
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshModuleStatus() {
|
||||||
|
executor.execute(() -> {
|
||||||
|
try {
|
||||||
|
final boolean isLoaded = configManager.isKpmModuleLoaded();
|
||||||
|
mainHandler.post(() -> updateModuleStatus(isLoaded));
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error refreshing module status", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
if (executor != null && !executor.isShutdown()) {
|
||||||
|
executor.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SO 隐藏项数据类
|
||||||
|
*/
|
||||||
|
public static class HideSoItem {
|
||||||
|
public String soName;
|
||||||
|
public boolean isHidden;
|
||||||
|
public boolean isFixed; // 是否是固定隐藏项(不可取消)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ public class MainActivity extends AppCompatActivity implements SettingsFragment.
|
|||||||
private AppListFragment appListFragment;
|
private AppListFragment appListFragment;
|
||||||
private SettingsFragment settingsFragment;
|
private SettingsFragment settingsFragment;
|
||||||
private SoManagerFragment soManagerFragment;
|
private SoManagerFragment soManagerFragment;
|
||||||
|
private KpmHideFragment kpmHideFragment;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -53,6 +54,9 @@ public class MainActivity extends AppCompatActivity implements SettingsFragment.
|
|||||||
} else if (itemId == R.id.navigation_so_manager) {
|
} else if (itemId == R.id.navigation_so_manager) {
|
||||||
showSoManagerFragment();
|
showSoManagerFragment();
|
||||||
return true;
|
return true;
|
||||||
|
} else if (itemId == R.id.navigation_kpm_hide) {
|
||||||
|
showKpmHideFragment();
|
||||||
|
return true;
|
||||||
} else if (itemId == R.id.navigation_settings) {
|
} else if (itemId == R.id.navigation_settings) {
|
||||||
showSettingsFragment();
|
showSettingsFragment();
|
||||||
return true;
|
return true;
|
||||||
@@ -75,6 +79,13 @@ public class MainActivity extends AppCompatActivity implements SettingsFragment.
|
|||||||
showFragment(soManagerFragment);
|
showFragment(soManagerFragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showKpmHideFragment() {
|
||||||
|
if (kpmHideFragment == null) {
|
||||||
|
kpmHideFragment = new KpmHideFragment();
|
||||||
|
}
|
||||||
|
showFragment(kpmHideFragment);
|
||||||
|
}
|
||||||
|
|
||||||
private void showSettingsFragment() {
|
private void showSettingsFragment() {
|
||||||
if (settingsFragment == null) {
|
if (settingsFragment == null) {
|
||||||
settingsFragment = new SettingsFragment();
|
settingsFragment = new SettingsFragment();
|
||||||
|
|||||||
202
configapp/src/main/res/layout/fragment_kpm_hide.xml
Normal file
202
configapp/src/main/res/layout/fragment_kpm_hide.xml
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".KpmHideFragment">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<!-- 标题 -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="KPM 注入隐藏"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<!-- KPM 模块状态卡片 -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="模块状态"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="8dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvModuleStatus"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="● 检查中..."
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="8dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvModuleInfo"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="正在检查模块状态..."
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@android:color/darker_gray"
|
||||||
|
android:layout_marginBottom="12dp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnReloadModule"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="重载模块"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.OutlinedButton" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<!-- 配置说明卡片 -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardBackgroundColor="#FFF3E0">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="ℹ️ 使用说明"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="8dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="• KPM 内核模块可以隐藏进程的内存映射信息\n• libmyinjector.so 将自动隐藏(不可取消)\n• 勾选其他 SO 文件可隐藏其在 /proc/[pid]/maps 中的显示\n• 每次更改都会自动重载内核模块以应用配置"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:lineSpacingExtra="4dp"
|
||||||
|
android:textColor="#E65100"
|
||||||
|
android:layout_marginBottom="8dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvConfigPath"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="配置文件: /data/adb/modules/zygisk-myinjector/kpm_hide_config.txt"
|
||||||
|
android:textSize="10sp"
|
||||||
|
android:textColor="@android:color/darker_gray"
|
||||||
|
android:fontFamily="monospace" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<!-- 固定隐藏项说明 -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="🔒 固定隐藏项"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="8dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="以下库文件将始终被隐藏:"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@android:color/darker_gray"
|
||||||
|
android:layout_marginBottom="4dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="• libmyinjector.so (Zygisk 注入器)"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:fontFamily="monospace"
|
||||||
|
android:textColor="@android:color/holo_green_dark" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<!-- SO 隐藏列表 -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="可选隐藏 SO 列表"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="8dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="从已配置的应用中选择需要隐藏的 SO 文件"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@android:color/darker_gray"
|
||||||
|
android:layout_marginBottom="12dp" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvSoList"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="100dp"
|
||||||
|
tools:listitem="@layout/item_hide_so" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
58
configapp/src/main/res/layout/item_hide_so.xml
Normal file
58
configapp/src/main/res/layout/item_hide_so.xml
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:background="?attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/cbHide"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="12dp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvSoName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="libexample.so"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:fontFamily="monospace" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvSoStatus"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="未隐藏"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@android:color/darker_gray"
|
||||||
|
android:layout_marginTop="2dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvFixedBadge"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="固定"
|
||||||
|
android:textSize="10sp"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:background="@android:color/holo_green_dark"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:paddingTop="2dp"
|
||||||
|
android:paddingBottom="2dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
@@ -9,6 +9,11 @@
|
|||||||
android:id="@+id/navigation_so_manager"
|
android:id="@+id/navigation_so_manager"
|
||||||
android:icon="@android:drawable/ic_menu_save"
|
android:icon="@android:drawable/ic_menu_save"
|
||||||
android:title="@string/title_so_manager" />
|
android:title="@string/title_so_manager" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/navigation_kpm_hide"
|
||||||
|
android:icon="@android:drawable/ic_secure"
|
||||||
|
android:title="@string/title_kpm_hide" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/navigation_settings"
|
android:id="@+id/navigation_settings"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<!-- 底部导航 -->
|
<!-- 底部导航 -->
|
||||||
<string name="title_apps">应用列表</string>
|
<string name="title_apps">应用列表</string>
|
||||||
<string name="title_so_manager">SO库管理</string>
|
<string name="title_so_manager">SO库管理</string>
|
||||||
|
<string name="title_kpm_hide">KPM隐藏</string>
|
||||||
<string name="title_settings">全局设置</string>
|
<string name="title_settings">全局设置</string>
|
||||||
|
|
||||||
<!-- 应用列表 -->
|
<!-- 应用列表 -->
|
||||||
@@ -21,4 +22,25 @@
|
|||||||
<!-- 关于 -->
|
<!-- 关于 -->
|
||||||
<string name="about">关于</string>
|
<string name="about">关于</string>
|
||||||
<string name="app_description">MyInjector 配置应用,用于管理注入设置</string>
|
<string name="app_description">MyInjector 配置应用,用于管理注入设置</string>
|
||||||
|
|
||||||
|
<!-- KPM 隐藏 -->
|
||||||
|
<string name="kpm_hide_title">KPM 注入隐藏</string>
|
||||||
|
<string name="kpm_module_status">模块状态</string>
|
||||||
|
<string name="kpm_module_loaded">已加载</string>
|
||||||
|
<string name="kpm_module_not_loaded">未加载</string>
|
||||||
|
<string name="kpm_module_info">KPM 内核模块信息</string>
|
||||||
|
<string name="kpm_reload_module">重载模块</string>
|
||||||
|
<string name="kpm_reload_success">模块重载成功</string>
|
||||||
|
<string name="kpm_reload_failed">模块重载失败</string>
|
||||||
|
<string name="kpm_config_path">配置文件路径</string>
|
||||||
|
<string name="kpm_fixed_items">固定隐藏项</string>
|
||||||
|
<string name="kpm_optional_items">可选隐藏 SO 列表</string>
|
||||||
|
<string name="kpm_usage_info">使用说明</string>
|
||||||
|
<string name="kpm_fixed_badge">固定</string>
|
||||||
|
<string name="kpm_hidden">已隐藏</string>
|
||||||
|
<string name="kpm_not_hidden">未隐藏</string>
|
||||||
|
<string name="kpm_add_success">已添加到隐藏列表</string>
|
||||||
|
<string name="kpm_remove_success">已从隐藏列表移除</string>
|
||||||
|
<string name="kpm_update_failed">更新失败</string>
|
||||||
|
<string name="kpm_fixed_cannot_uncheck">libmyinjector.so 是必需的,不能取消隐藏</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -70,6 +70,10 @@ afterEvaluate {
|
|||||||
from("$projectDir") {
|
from("$projectDir") {
|
||||||
include 'service.sh'
|
include 'service.sh'
|
||||||
}
|
}
|
||||||
|
// Copy KPM module
|
||||||
|
from("$projectDir/kpm") {
|
||||||
|
include 'injectHide.kpm'
|
||||||
|
}
|
||||||
// Copy ConfigApp APK if it exists
|
// Copy ConfigApp APK if it exists
|
||||||
def apkFile = file("$rootDir/configapp/build/outputs/apk/debug/configapp-debug.apk")
|
def apkFile = file("$rootDir/configapp/build/outputs/apk/debug/configapp-debug.apk")
|
||||||
if (apkFile.exists()) {
|
if (apkFile.exists()) {
|
||||||
|
|||||||
@@ -94,5 +94,43 @@ chown -R root:root /data/adb/modules/zygisk-myinjector
|
|||||||
|
|
||||||
log "ConfigApp 安装脚本执行完成"
|
log "ConfigApp 安装脚本执行完成"
|
||||||
|
|
||||||
|
# ==================== KPM 模块加载 ====================
|
||||||
|
|
||||||
|
# KPM 模块路径
|
||||||
|
KPM_MODULE="$MODDIR/injectHide.kpm"
|
||||||
|
KPM_CONFIG="$MODDIR/kpm_hide_config.txt"
|
||||||
|
|
||||||
|
log "开始加载 KPM 内核模块"
|
||||||
|
|
||||||
|
# 检查 KPM 模块文件是否存在
|
||||||
|
if [ ! -f "$KPM_MODULE" ]; then
|
||||||
|
log "KPM 模块文件不存在: $KPM_MODULE"
|
||||||
|
else
|
||||||
|
log "找到 KPM 模块文件: $KPM_MODULE"
|
||||||
|
|
||||||
|
# 创建初始配置文件(如果不存在)
|
||||||
|
if [ ! -f "$KPM_CONFIG" ]; then
|
||||||
|
log "创建初始 KPM 配置文件"
|
||||||
|
echo "libmyinjector.so" > "$KPM_CONFIG"
|
||||||
|
chmod 644 "$KPM_CONFIG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 等待一段时间确保系统稳定
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# 加载 KPM 模块
|
||||||
|
log "正在加载 KPM 模块..."
|
||||||
|
insmod "$KPM_MODULE" 2>&1 | while read line; do
|
||||||
|
log "insmod: $line"
|
||||||
|
done
|
||||||
|
|
||||||
|
# 检查模块是否加载成功
|
||||||
|
if lsmod | grep -q "hideInject"; then
|
||||||
|
log "KPM 模块加载成功!"
|
||||||
|
else
|
||||||
|
log "KPM 模块加载失败,请检查日志"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# 脚本完成
|
# 脚本完成
|
||||||
exit 0
|
exit 0
|
||||||
Reference in New Issue
Block a user