refactor name
This commit is contained in:
parent
a1c05b78ec
commit
9b43aaac5d
|
|
@ -1,18 +1,18 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.storm.wind.xposed">
|
package="com.storm.wind.tester">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".XposedTestApplication"
|
android:name="org.lsposed.lspatch.tester.XposedTestApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/sample_app_title"
|
android:label="@string/sample_app_title"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true">
|
android:supportsRtl="true">
|
||||||
<activity android:name=".MainActivity">
|
<activity android:name="org.lsposed.lspatch.tester.MainActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.wind.xposed.entry;
|
package org.lsposed.lspatch.loader;
|
||||||
|
|
||||||
import static android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE;
|
import static android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE;
|
||||||
import static com.wind.xposed.entry.LSPLoader.initAndLoadModules;
|
import static org.lsposed.lspatch.loader.LSPLoader.initAndLoadModules;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
@ -11,9 +11,9 @@ import android.os.Build;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
|
|
||||||
import com.wind.xposed.entry.util.FileUtils;
|
import org.lsposed.lspatch.loader.util.FileUtils;
|
||||||
import com.wind.xposed.entry.util.XLog;
|
import org.lsposed.lspatch.loader.util.XLog;
|
||||||
import com.wind.xposed.entry.util.XpatchUtils;
|
import org.lsposed.lspatch.loader.util.XpatchUtils;
|
||||||
|
|
||||||
import org.lsposed.lspd.yahfa.hooker.YahfaHooker;
|
import org.lsposed.lspd.yahfa.hooker.YahfaHooker;
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.wind.xposed.entry;
|
package org.lsposed.lspatch.loader;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
@ -11,9 +11,9 @@ import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
import com.wind.xposed.entry.util.FileUtils;
|
import org.lsposed.lspatch.loader.util.FileUtils;
|
||||||
import com.wind.xposed.entry.util.XLog;
|
import org.lsposed.lspatch.loader.util.XLog;
|
||||||
import com.wind.xposed.entry.util.XpatchUtils;
|
import org.lsposed.lspatch.loader.util.XpatchUtils;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.wind.xposed.entry.util;
|
package org.lsposed.lspatch.loader.util;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.wind.xposed.entry.util;
|
package org.lsposed.lspatch.loader.util;
|
||||||
|
|
||||||
|
|
||||||
import com.storm.wind.xposed.BuildConfig;
|
import com.storm.wind.tester.BuildConfig;
|
||||||
|
|
||||||
public class XLog {
|
public class XLog {
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.wind.xposed.entry.util;
|
package org.lsposed.lspatch.loader.util;
|
||||||
|
|
||||||
import android.app.ActivityThread;
|
import android.app.ActivityThread;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.storm.wind.xposed;
|
package org.lsposed.lspatch.tester;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
|
@ -6,6 +6,8 @@ import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.storm.wind.tester.R;
|
||||||
|
|
||||||
import de.robv.android.xposed.XC_MethodHook;
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
import de.robv.android.xposed.XposedHelpers;
|
||||||
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
package com.storm.wind.xposed;
|
package org.lsposed.lspatch.tester;
|
||||||
|
|
||||||
import static com.wind.xposed.entry.LSPLoader.initAndLoadModules;
|
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
import org.lsposed.lspatch.loader.LSPLoader;
|
||||||
import org.lsposed.lspd.yahfa.hooker.YahfaHooker;
|
import org.lsposed.lspd.yahfa.hooker.YahfaHooker;
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedInit;
|
import de.robv.android.xposed.XposedInit;
|
||||||
|
|
@ -20,6 +19,6 @@ public class XposedTestApplication extends Application {
|
||||||
System.loadLibrary("lspd");
|
System.loadLibrary("lspd");
|
||||||
YahfaHooker.init();
|
YahfaHooker.init();
|
||||||
XposedInit.startsSystemServer = false;
|
XposedInit.startsSystemServer = false;
|
||||||
initAndLoadModules();
|
LSPLoader.initAndLoadModules();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".MainActivity">
|
tools:context="org.lsposed.lspatch.tester.MainActivity">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/msg"
|
android:id="@+id/msg"
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ jar {
|
||||||
baseName = "lspatch"
|
baseName = "lspatch"
|
||||||
destinationDirectory = new File("$rootProject.projectDir/out")
|
destinationDirectory = new File("$rootProject.projectDir/out")
|
||||||
manifest {
|
manifest {
|
||||||
attributes 'Main-Class': 'com.storm.wind.xpatch.MainCommand'
|
attributes 'Main-Class': 'org.lsposed.patch.LSPatch'
|
||||||
}
|
}
|
||||||
dependsOn configurations.runtimeClasspath
|
dependsOn configurations.runtimeClasspath
|
||||||
from {
|
from {
|
||||||
|
|
|
||||||
|
|
@ -1,241 +0,0 @@
|
||||||
package com.storm.wind.xpatch;
|
|
||||||
|
|
||||||
import static org.apache.commons.io.FileUtils.copyFile;
|
|
||||||
|
|
||||||
import com.storm.wind.xpatch.base.BaseCommand;
|
|
||||||
import com.storm.wind.xpatch.task.BuildAndSignApkTask;
|
|
||||||
import com.storm.wind.xpatch.task.SaveApkSignatureTask;
|
|
||||||
import com.storm.wind.xpatch.task.SaveOriginalApplicationNameTask;
|
|
||||||
import com.storm.wind.xpatch.task.SoAndDexCopyTask;
|
|
||||||
import com.storm.wind.xpatch.util.FileUtils;
|
|
||||||
import com.storm.wind.xpatch.util.ManifestParser;
|
|
||||||
import com.wind.meditor.core.FileProcesser;
|
|
||||||
import com.wind.meditor.property.AttributeItem;
|
|
||||||
import com.wind.meditor.property.ModificationProperty;
|
|
||||||
import com.wind.meditor.utils.NodeValue;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public class MainCommand extends BaseCommand {
|
|
||||||
|
|
||||||
private String apkPath;
|
|
||||||
|
|
||||||
private String unzipApkFilePath;
|
|
||||||
|
|
||||||
@Opt(opt = "o", longOpt = "output", description = "output .apk file, default is " +
|
|
||||||
"$source_apk_dir/[file-name]-xposed-signed.apk", argName = "out-apk-file")
|
|
||||||
private String output;
|
|
||||||
|
|
||||||
@Opt(opt = "f", longOpt = "force", hasArg = false, description = "force overwrite")
|
|
||||||
private boolean forceOverwrite = false;
|
|
||||||
|
|
||||||
@Opt(opt = "pn", longOpt = "proxyname", description = "special proxy app name with full dot path", argName = "proxy app name")
|
|
||||||
private String proxyname = "com.wind.xposed.entry.LSPApplication";
|
|
||||||
|
|
||||||
@Opt(opt = "d", longOpt = "debuggable", description = "set 1 to make the app debuggable = true, " +
|
|
||||||
"set 0 to make the app debuggable = false", argName = "0 or 1")
|
|
||||||
private int debuggable = -1; // 0: debuggable = false 1: debuggable = true
|
|
||||||
|
|
||||||
private int dexFileCount = 0;
|
|
||||||
|
|
||||||
private static final String UNZIP_APK_FILE_NAME = "apk-unzip-files";
|
|
||||||
|
|
||||||
private List<Runnable> mXpatchTasks = new ArrayList<>();
|
|
||||||
|
|
||||||
public static void main(String... args) {
|
|
||||||
new MainCommand().doMain(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
static public void fuckIfFail(boolean b) {
|
|
||||||
if (!b) {
|
|
||||||
throw new IllegalStateException("wtf", new Throwable("DUMPBT"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doCommandLine() throws IOException {
|
|
||||||
if (remainingArgs.length != 1) {
|
|
||||||
if (remainingArgs.length == 0) {
|
|
||||||
System.out.println("Please choose one apk file you want to process. ");
|
|
||||||
}
|
|
||||||
if (remainingArgs.length > 1) {
|
|
||||||
System.out.println("This tool can only used with one apk file.");
|
|
||||||
}
|
|
||||||
usage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
apkPath = remainingArgs[0];
|
|
||||||
|
|
||||||
File srcApkFile = new File(apkPath);
|
|
||||||
|
|
||||||
if (!srcApkFile.exists()) {
|
|
||||||
System.out.println("The source apk file not exsit, please choose another one. " +
|
|
||||||
"current apk file is = " + apkPath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String currentDir = new File(".").getAbsolutePath();
|
|
||||||
System.out.println("currentDir: " + currentDir);
|
|
||||||
System.out.println("apkPath: " + apkPath);
|
|
||||||
|
|
||||||
if (output == null || output.length() == 0) {
|
|
||||||
output = getBaseName(apkPath) + "-xposed-signed.apk";
|
|
||||||
}
|
|
||||||
|
|
||||||
File outputFile = new File(output);
|
|
||||||
if (outputFile.exists() && !forceOverwrite) {
|
|
||||||
System.err.println(output + " exists, use --force to overwrite");
|
|
||||||
usage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String outputApkFileParentPath = outputFile.getParent();
|
|
||||||
if (outputApkFileParentPath == null) {
|
|
||||||
String absPath = outputFile.getAbsolutePath();
|
|
||||||
int index = absPath.lastIndexOf(File.separatorChar);
|
|
||||||
outputApkFileParentPath = absPath.substring(0, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("output apk path: " + output);
|
|
||||||
|
|
||||||
String apkFileName = getBaseName(srcApkFile);
|
|
||||||
|
|
||||||
String tempFilePath = outputApkFileParentPath + File.separator +
|
|
||||||
currentTimeStr() + "-tmp" + File.separator;
|
|
||||||
|
|
||||||
unzipApkFilePath = tempFilePath + apkFileName + "-" + UNZIP_APK_FILE_NAME + File.separator;
|
|
||||||
|
|
||||||
System.out.println("outputApkFileParentPath: " + outputApkFileParentPath);
|
|
||||||
System.out.println("unzipApkFilePath = " + unzipApkFilePath);
|
|
||||||
|
|
||||||
// save the apk original signature info, to support crach signature.
|
|
||||||
new SaveApkSignatureTask(apkPath, unzipApkFilePath).run();
|
|
||||||
|
|
||||||
long currentTime = System.currentTimeMillis();
|
|
||||||
FileUtils.decompressZip(apkPath, unzipApkFilePath);
|
|
||||||
|
|
||||||
System.out.println("decompress apk cost time: " + (System.currentTimeMillis() - currentTime) + "ms");
|
|
||||||
|
|
||||||
// Get the dex count in the apk zip file
|
|
||||||
dexFileCount = findDexFileCount(unzipApkFilePath);
|
|
||||||
|
|
||||||
System.out.println("dexFileCount: " + dexFileCount);
|
|
||||||
|
|
||||||
String manifestFilePath = unzipApkFilePath + "AndroidManifest.xml";
|
|
||||||
|
|
||||||
currentTime = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// parse the app main application full name from the manifest file
|
|
||||||
ManifestParser.Pair pair = ManifestParser.parseManifestFile(manifestFilePath);
|
|
||||||
String applicationName = null;
|
|
||||||
if (pair != null && pair.applicationName != null) {
|
|
||||||
applicationName = pair.applicationName;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Get application name cost time: " + (System.currentTimeMillis() - currentTime) + "ms");
|
|
||||||
System.out.println("Get the application name: " + applicationName);
|
|
||||||
|
|
||||||
// modify manifest
|
|
||||||
File manifestFile = new File(manifestFilePath);
|
|
||||||
String manifestFilePathNew = unzipApkFilePath + "AndroidManifest" + "-" + currentTimeStr() + ".xml";
|
|
||||||
File manifestFileNew = new File(manifestFilePathNew);
|
|
||||||
fuckIfFail(manifestFile.renameTo(manifestFileNew));
|
|
||||||
|
|
||||||
modifyManifestFile(manifestFilePathNew, manifestFilePath, applicationName);
|
|
||||||
|
|
||||||
// new manifest may not exist
|
|
||||||
if (manifestFile.exists() && manifestFile.length() > 0) {
|
|
||||||
fuckIfFail(manifestFileNew.delete());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
fuckIfFail(manifestFileNew.renameTo(manifestFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
// save original main application name to asset file
|
|
||||||
if (isNotEmpty(applicationName)) {
|
|
||||||
mXpatchTasks.add(new SaveOriginalApplicationNameTask(applicationName, unzipApkFilePath));
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy xposed so and dex files into the unzipped apk
|
|
||||||
mXpatchTasks.add(new SoAndDexCopyTask(dexFileCount, unzipApkFilePath));
|
|
||||||
|
|
||||||
// compress all files into an apk and then sign it.
|
|
||||||
mXpatchTasks.add(new BuildAndSignApkTask(true, unzipApkFilePath, output));
|
|
||||||
|
|
||||||
// copy origin apk to assets
|
|
||||||
// convenient to bypass some check like CRC
|
|
||||||
copyFile(srcApkFile, new File(unzipApkFilePath, "assets/origin_apk.bin"));
|
|
||||||
|
|
||||||
// excute these tasks
|
|
||||||
for (Runnable executor : mXpatchTasks) {
|
|
||||||
currentTime = System.currentTimeMillis();
|
|
||||||
executor.run();
|
|
||||||
|
|
||||||
System.out.println(executor.getClass().getSimpleName() + " cost time: "
|
|
||||||
+ (System.currentTimeMillis() - currentTime) + "ms");
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Output APK: " + output);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void modifyManifestFile(String filePath, String dstFilePath, String originalApplicationName) {
|
|
||||||
ModificationProperty property = new ModificationProperty();
|
|
||||||
boolean modifyEnabled = false;
|
|
||||||
|
|
||||||
if (debuggable >= 0) {
|
|
||||||
modifyEnabled = true;
|
|
||||||
property.addApplicationAttribute(new AttributeItem(NodeValue.Application.DEBUGGABLE, debuggable != 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
property.addApplicationAttribute(new AttributeItem("extractNativeLibs", true));
|
|
||||||
|
|
||||||
modifyEnabled = true;
|
|
||||||
property.addApplicationAttribute(new AttributeItem(NodeValue.Application.NAME, proxyname));
|
|
||||||
|
|
||||||
FileProcesser.processManifestFile(filePath, dstFilePath, property);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int findDexFileCount(String unzipApkFilePath) {
|
|
||||||
File zipfileRoot = new File(unzipApkFilePath);
|
|
||||||
if (!zipfileRoot.exists()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
File[] childFiles = zipfileRoot.listFiles();
|
|
||||||
if (childFiles == null || childFiles.length == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int count = 0;
|
|
||||||
for (File file : childFiles) {
|
|
||||||
String fileName = file.getName();
|
|
||||||
if (Pattern.matches("classes.*\\.dex", fileName)) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the current timestamp as the name of the build file
|
|
||||||
@SuppressWarnings("SimpleDateFormat")
|
|
||||||
private String currentTimeStr() {
|
|
||||||
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
|
|
||||||
return df.format(new Date());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] getXposedModules(String modules) {
|
|
||||||
if (modules == null || modules.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return modules.split(File.pathSeparator);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isNotEmpty(String str) {
|
|
||||||
return str != null && !str.isEmpty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,241 @@
|
||||||
package org.lsposed.patch;
|
package org.lsposed.patch;
|
||||||
|
|
||||||
public class LSPatch {
|
import static org.apache.commons.io.FileUtils.copyFile;
|
||||||
|
|
||||||
|
import com.wind.meditor.core.FileProcesser;
|
||||||
|
import com.wind.meditor.property.AttributeItem;
|
||||||
|
import com.wind.meditor.property.ModificationProperty;
|
||||||
|
import com.wind.meditor.utils.NodeValue;
|
||||||
|
|
||||||
|
import org.lsposed.patch.base.BaseCommand;
|
||||||
|
import org.lsposed.patch.task.BuildAndSignApkTask;
|
||||||
|
import org.lsposed.patch.task.SaveApkSignatureTask;
|
||||||
|
import org.lsposed.patch.task.SaveOriginalApplicationNameTask;
|
||||||
|
import org.lsposed.patch.task.SoAndDexCopyTask;
|
||||||
|
import org.lsposed.patch.util.FileUtils;
|
||||||
|
import org.lsposed.patch.util.ManifestParser;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class LSPatch extends BaseCommand {
|
||||||
|
private String apkPath;
|
||||||
|
|
||||||
|
private String unzipApkFilePath;
|
||||||
|
|
||||||
|
@Opt(opt = "o", longOpt = "output", description = "output .apk file, default is " +
|
||||||
|
"$source_apk_dir/[file-name]-xposed-signed.apk", argName = "out-apk-file")
|
||||||
|
private String output;
|
||||||
|
|
||||||
|
@Opt(opt = "f", longOpt = "force", hasArg = false, description = "force overwrite")
|
||||||
|
private boolean forceOverwrite = false;
|
||||||
|
|
||||||
|
@Opt(opt = "pn", longOpt = "proxyname", description = "special proxy app name with full dot path", argName = "proxy app name")
|
||||||
|
private String proxyname = "com.wind.xposed.entry.LSPApplication";
|
||||||
|
|
||||||
|
@Opt(opt = "d", longOpt = "debuggable", description = "set 1 to make the app debuggable = true, " +
|
||||||
|
"set 0 to make the app debuggable = false", argName = "0 or 1")
|
||||||
|
private int debuggable = -1; // 0: debuggable = false 1: debuggable = true
|
||||||
|
|
||||||
|
private int dexFileCount = 0;
|
||||||
|
|
||||||
|
private static final String UNZIP_APK_FILE_NAME = "apk-unzip-files";
|
||||||
|
|
||||||
|
private List<Runnable> mXpatchTasks = new ArrayList<>();
|
||||||
|
|
||||||
|
public static void main(String... args) {
|
||||||
|
new LSPatch().doMain(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void fuckIfFail(boolean b) {
|
||||||
|
if (!b) {
|
||||||
|
throw new IllegalStateException("wtf", new Throwable("DUMPBT"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doCommandLine() throws IOException {
|
||||||
|
if (remainingArgs.length != 1) {
|
||||||
|
if (remainingArgs.length == 0) {
|
||||||
|
System.out.println("Please choose one apk file you want to process. ");
|
||||||
|
}
|
||||||
|
if (remainingArgs.length > 1) {
|
||||||
|
System.out.println("This tool can only used with one apk file.");
|
||||||
|
}
|
||||||
|
usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
apkPath = remainingArgs[0];
|
||||||
|
|
||||||
|
File srcApkFile = new File(apkPath);
|
||||||
|
|
||||||
|
if (!srcApkFile.exists()) {
|
||||||
|
System.out.println("The source apk file not exsit, please choose another one. " +
|
||||||
|
"current apk file is = " + apkPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String currentDir = new File(".").getAbsolutePath();
|
||||||
|
System.out.println("currentDir: " + currentDir);
|
||||||
|
System.out.println("apkPath: " + apkPath);
|
||||||
|
|
||||||
|
if (output == null || output.length() == 0) {
|
||||||
|
output = getBaseName(apkPath) + "-xposed-signed.apk";
|
||||||
|
}
|
||||||
|
|
||||||
|
File outputFile = new File(output);
|
||||||
|
if (outputFile.exists() && !forceOverwrite) {
|
||||||
|
System.err.println(output + " exists, use --force to overwrite");
|
||||||
|
usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String outputApkFileParentPath = outputFile.getParent();
|
||||||
|
if (outputApkFileParentPath == null) {
|
||||||
|
String absPath = outputFile.getAbsolutePath();
|
||||||
|
int index = absPath.lastIndexOf(File.separatorChar);
|
||||||
|
outputApkFileParentPath = absPath.substring(0, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("output apk path: " + output);
|
||||||
|
|
||||||
|
String apkFileName = getBaseName(srcApkFile);
|
||||||
|
|
||||||
|
String tempFilePath = outputApkFileParentPath + File.separator +
|
||||||
|
currentTimeStr() + "-tmp" + File.separator;
|
||||||
|
|
||||||
|
unzipApkFilePath = tempFilePath + apkFileName + "-" + UNZIP_APK_FILE_NAME + File.separator;
|
||||||
|
|
||||||
|
System.out.println("outputApkFileParentPath: " + outputApkFileParentPath);
|
||||||
|
System.out.println("unzipApkFilePath = " + unzipApkFilePath);
|
||||||
|
|
||||||
|
// save the apk original signature info, to support crach signature.
|
||||||
|
new SaveApkSignatureTask(apkPath, unzipApkFilePath).run();
|
||||||
|
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
FileUtils.decompressZip(apkPath, unzipApkFilePath);
|
||||||
|
|
||||||
|
System.out.println("decompress apk cost time: " + (System.currentTimeMillis() - currentTime) + "ms");
|
||||||
|
|
||||||
|
// Get the dex count in the apk zip file
|
||||||
|
dexFileCount = findDexFileCount(unzipApkFilePath);
|
||||||
|
|
||||||
|
System.out.println("dexFileCount: " + dexFileCount);
|
||||||
|
|
||||||
|
String manifestFilePath = unzipApkFilePath + "AndroidManifest.xml";
|
||||||
|
|
||||||
|
currentTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// parse the app main application full name from the manifest file
|
||||||
|
ManifestParser.Pair pair = ManifestParser.parseManifestFile(manifestFilePath);
|
||||||
|
String applicationName = null;
|
||||||
|
if (pair != null && pair.applicationName != null) {
|
||||||
|
applicationName = pair.applicationName;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Get application name cost time: " + (System.currentTimeMillis() - currentTime) + "ms");
|
||||||
|
System.out.println("Get the application name: " + applicationName);
|
||||||
|
|
||||||
|
// modify manifest
|
||||||
|
File manifestFile = new File(manifestFilePath);
|
||||||
|
String manifestFilePathNew = unzipApkFilePath + "AndroidManifest" + "-" + currentTimeStr() + ".xml";
|
||||||
|
File manifestFileNew = new File(manifestFilePathNew);
|
||||||
|
fuckIfFail(manifestFile.renameTo(manifestFileNew));
|
||||||
|
|
||||||
|
modifyManifestFile(manifestFilePathNew, manifestFilePath, applicationName);
|
||||||
|
|
||||||
|
// new manifest may not exist
|
||||||
|
if (manifestFile.exists() && manifestFile.length() > 0) {
|
||||||
|
fuckIfFail(manifestFileNew.delete());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fuckIfFail(manifestFileNew.renameTo(manifestFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
// save original main application name to asset file
|
||||||
|
if (isNotEmpty(applicationName)) {
|
||||||
|
mXpatchTasks.add(new SaveOriginalApplicationNameTask(applicationName, unzipApkFilePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy xposed so and dex files into the unzipped apk
|
||||||
|
mXpatchTasks.add(new SoAndDexCopyTask(dexFileCount, unzipApkFilePath));
|
||||||
|
|
||||||
|
// compress all files into an apk and then sign it.
|
||||||
|
mXpatchTasks.add(new BuildAndSignApkTask(true, unzipApkFilePath, output));
|
||||||
|
|
||||||
|
// copy origin apk to assets
|
||||||
|
// convenient to bypass some check like CRC
|
||||||
|
copyFile(srcApkFile, new File(unzipApkFilePath, "assets/origin_apk.bin"));
|
||||||
|
|
||||||
|
// excute these tasks
|
||||||
|
for (Runnable executor : mXpatchTasks) {
|
||||||
|
currentTime = System.currentTimeMillis();
|
||||||
|
executor.run();
|
||||||
|
|
||||||
|
System.out.println(executor.getClass().getSimpleName() + " cost time: "
|
||||||
|
+ (System.currentTimeMillis() - currentTime) + "ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Output APK: " + output);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void modifyManifestFile(String filePath, String dstFilePath, String originalApplicationName) {
|
||||||
|
ModificationProperty property = new ModificationProperty();
|
||||||
|
boolean modifyEnabled = false;
|
||||||
|
|
||||||
|
if (debuggable >= 0) {
|
||||||
|
modifyEnabled = true;
|
||||||
|
property.addApplicationAttribute(new AttributeItem(NodeValue.Application.DEBUGGABLE, debuggable != 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
property.addApplicationAttribute(new AttributeItem("extractNativeLibs", true));
|
||||||
|
|
||||||
|
modifyEnabled = true;
|
||||||
|
property.addApplicationAttribute(new AttributeItem(NodeValue.Application.NAME, proxyname));
|
||||||
|
|
||||||
|
FileProcesser.processManifestFile(filePath, dstFilePath, property);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findDexFileCount(String unzipApkFilePath) {
|
||||||
|
File zipfileRoot = new File(unzipApkFilePath);
|
||||||
|
if (!zipfileRoot.exists()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
File[] childFiles = zipfileRoot.listFiles();
|
||||||
|
if (childFiles == null || childFiles.length == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int count = 0;
|
||||||
|
for (File file : childFiles) {
|
||||||
|
String fileName = file.getName();
|
||||||
|
if (Pattern.matches("classes.*\\.dex", fileName)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the current timestamp as the name of the build file
|
||||||
|
@SuppressWarnings("SimpleDateFormat")
|
||||||
|
private String currentTimeStr() {
|
||||||
|
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
|
||||||
|
return df.format(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] getXposedModules(String modules) {
|
||||||
|
if (modules == null || modules.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return modules.split(File.pathSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isNotEmpty(String str) {
|
||||||
|
return str != null && !str.isEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.storm.wind.xpatch.base;
|
package org.lsposed.patch.base;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
package com.storm.wind.xpatch.task;
|
package org.lsposed.patch.task;
|
||||||
|
|
||||||
import com.android.apksigner.ApkSignerTool;
|
import com.android.apksigner.ApkSignerTool;
|
||||||
import com.storm.wind.xpatch.MainCommand;
|
|
||||||
import com.storm.wind.xpatch.util.FileUtils;
|
import org.lsposed.patch.LSPatch;
|
||||||
import com.storm.wind.xpatch.util.ShellCmdUtil;
|
import org.lsposed.patch.util.FileUtils;
|
||||||
|
import org.lsposed.patch.util.ShellCmdUtil;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
@ -56,12 +57,12 @@ public class BuildAndSignApkTask implements Runnable {
|
||||||
File signedApkFile = new File(signedApkPath);
|
File signedApkFile = new File(signedApkPath);
|
||||||
// delete unsigned apk file
|
// delete unsigned apk file
|
||||||
if (!keepUnsignedApkFile && unsignedApkFile.exists() && signedApkFile.exists() && signResult) {
|
if (!keepUnsignedApkFile && unsignedApkFile.exists() && signedApkFile.exists() && signResult) {
|
||||||
MainCommand.fuckIfFail(unsignedApkFile.delete());
|
LSPatch.fuckIfFail(unsignedApkFile.delete());
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete the keystore file
|
// delete the keystore file
|
||||||
if (keyStoreFile.exists()) {
|
if (keyStoreFile.exists()) {
|
||||||
MainCommand.fuckIfFail(keyStoreFile.delete());
|
LSPatch.fuckIfFail(keyStoreFile.delete());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception err) {
|
catch (Exception err) {
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package com.storm.wind.xpatch.task;
|
package org.lsposed.patch.task;
|
||||||
|
|
||||||
import com.storm.wind.xpatch.util.ApkSignatureHelper;
|
import org.lsposed.patch.util.ApkSignatureHelper;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.storm.wind.xpatch.task;
|
package org.lsposed.patch.task;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
package com.storm.wind.xpatch.task;
|
package org.lsposed.patch.task;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.storm.wind.xpatch.util;
|
package org.lsposed.patch.util;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.storm.wind.xpatch.util;
|
package org.lsposed.patch.util;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.storm.wind.xpatch.util;
|
package org.lsposed.patch.util;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.storm.wind.xpatch.util;
|
package org.lsposed.patch.util;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.storm.wind.xpatch.util;
|
package org.lsposed.patch.util;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
Loading…
Reference in New Issue