Merge so and dex into jar
This commit is contained in:
parent
61624dafae
commit
c87f7ffa14
|
|
@ -35,7 +35,7 @@ jobs:
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: lspatch-debug
|
name: lspatch-debug
|
||||||
path: out/
|
path: out/lspatch.jar
|
||||||
|
|
||||||
- name: Build Release
|
- name: Build Release
|
||||||
run: ./gradlew buildRelease
|
run: ./gradlew buildRelease
|
||||||
|
|
@ -43,4 +43,4 @@ jobs:
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: lspatch-release
|
name: lspatch-release
|
||||||
path: out/
|
path: out/lspatch.jar
|
||||||
|
|
|
||||||
|
|
@ -51,14 +51,14 @@ android {
|
||||||
dependsOn("assemble$buildType")
|
dependsOn("assemble$buildType")
|
||||||
def dexFilePath = "$buildDir/intermediates/dex/${variantLowered}/mergeDex${buildType}/classes.dex"
|
def dexFilePath = "$buildDir/intermediates/dex/${variantLowered}/mergeDex${buildType}/classes.dex"
|
||||||
from dexFilePath
|
from dexFilePath
|
||||||
rename "(.*).dex", "lsploader.dex"
|
rename "classes.dex", "lsp.dex"
|
||||||
into "$rootProject.projectDir/out/list-assets"
|
into "$rootProject.projectDir/out/dexes"
|
||||||
}
|
}
|
||||||
|
|
||||||
task "copySo$buildType"(type: Copy) {
|
task "copySo$buildType"(type: Copy) {
|
||||||
dependsOn("assemble$buildType")
|
dependsOn("assemble$buildType")
|
||||||
from "$buildDir/intermediates/merged_native_libs/${variantLowered}/out/lib"
|
from "$buildDir/intermediates/merged_native_libs/${variantLowered}/out/lib"
|
||||||
into "$rootProject.projectDir/out/list-so"
|
into "$rootProject.projectDir/out/so"
|
||||||
}
|
}
|
||||||
|
|
||||||
task "copy$buildType"() {
|
task "copy$buildType"() {
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,8 @@ android {
|
||||||
dependsOn("assemble$buildType")
|
dependsOn("assemble$buildType")
|
||||||
def dexFilePath = "$buildDir/intermediates/dex/${variantLowered}/mergeDex${buildType}/classes.dex"
|
def dexFilePath = "$buildDir/intermediates/dex/${variantLowered}/mergeDex${buildType}/classes.dex"
|
||||||
from dexFilePath
|
from dexFilePath
|
||||||
rename "(.*).dex", "classes-${version_name}.dex"
|
rename "classes.dex", "loader.dex"
|
||||||
into "$rootProject.projectDir/out/list-dex"
|
into "$rootProject.projectDir/out/dexes"
|
||||||
}
|
}
|
||||||
|
|
||||||
task "copy$buildType"() {
|
task "copy$buildType"() {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ public class LSPApplicationStub extends Application {
|
||||||
throw new IllegalStateException("create context err");
|
throw new IllegalStateException("create context err");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try (InputStream inputStream = context.getAssets().open("lsploader.dex");
|
try (InputStream inputStream = context.getAssets().open("lsp.dex");
|
||||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
|
||||||
|
|
||||||
int nRead;
|
int nRead;
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,13 @@ jar {
|
||||||
}
|
}
|
||||||
|
|
||||||
from fileTree(dir: 'src/main', includes: ['assets/**'])
|
from fileTree(dir: 'src/main', includes: ['assets/**'])
|
||||||
|
into("assets/dex") {
|
||||||
|
from fileTree(dir: "$rootProject.projectDir/out/dexes")
|
||||||
|
}
|
||||||
|
|
||||||
|
into("assets/so") {
|
||||||
|
from fileTree(dir: "$rootProject.projectDir/out/so")
|
||||||
|
}
|
||||||
|
|
||||||
exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.MF', 'META-INF/*.txt'
|
exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.MF', 'META-INF/*.txt'
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,9 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
@ -44,8 +46,8 @@ public class LSPatch {
|
||||||
@Parameter(names = {"-h", "--help"}, help = true, order = 0, description = "Print this message")
|
@Parameter(names = {"-h", "--help"}, help = true, order = 0, description = "Print this message")
|
||||||
private boolean help = false;
|
private boolean help = false;
|
||||||
|
|
||||||
@Parameter(names = {"-o", "--output"}, description = "Output apk file")
|
@Parameter(names = {"-o", "--output"}, description = "Output directory")
|
||||||
private String outputPath;
|
private String outputPath = ".";
|
||||||
|
|
||||||
@Parameter(names = {"-f", "--force"}, description = "Force overwrite exists output file")
|
@Parameter(names = {"-f", "--force"}, description = "Force overwrite exists output file")
|
||||||
private boolean forceOverwrite = false;
|
private boolean forceOverwrite = false;
|
||||||
|
|
@ -77,13 +79,13 @@ public class LSPatch {
|
||||||
private static final String SIGNATURE_INFO_ASSET_PATH = "assets/original_signature_info.ini";
|
private static final String SIGNATURE_INFO_ASSET_PATH = "assets/original_signature_info.ini";
|
||||||
private static final String ORIGIN_APK_ASSET_PATH = "assets/origin_apk.bin";
|
private static final String ORIGIN_APK_ASSET_PATH = "assets/origin_apk.bin";
|
||||||
private static final String ANDROID_MANIFEST_XML = "AndroidManifest.xml";
|
private static final String ANDROID_MANIFEST_XML = "AndroidManifest.xml";
|
||||||
private static final String[] APK_LIB_PATH_ARRAY = {
|
private static final HashSet<String> APK_LIB_PATH_ARRAY = new HashSet<>(Arrays.asList(
|
||||||
"lib/armeabi-v7a",
|
// "armeabi",
|
||||||
"lib/armeabi",
|
"armeabi-v7a",
|
||||||
"lib/arm64-v8a",
|
"arm64-v8a",
|
||||||
"lib/x86",
|
"x86",
|
||||||
"lib/x86_64"
|
"x86_64"
|
||||||
};
|
));
|
||||||
|
|
||||||
private static JCommander jCommander;
|
private static JCommander jCommander;
|
||||||
|
|
||||||
|
|
@ -109,14 +111,25 @@ public class LSPatch {
|
||||||
jCommander.usage();
|
jCommander.usage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var apk : apkPaths) {
|
for (var apk : apkPaths) {
|
||||||
File srcApkFile = new File(apk).getAbsoluteFile();
|
File srcApkFile = new File(apk).getAbsoluteFile();
|
||||||
System.out.println("Processing " + srcApkFile);
|
|
||||||
patch(srcApkFile);
|
String apkFileName = srcApkFile.getName();
|
||||||
|
|
||||||
|
var outputDir = new File(outputPath);
|
||||||
|
outputDir.mkdirs();
|
||||||
|
|
||||||
|
File outputFile = new File(outputDir, String.format("%s-lv%s-xposed-signed.apk", FilenameUtils.getBaseName(apkFileName), sigbypassLevel)).getAbsoluteFile();
|
||||||
|
|
||||||
|
if (outputFile.exists() && !forceOverwrite)
|
||||||
|
throw new PatchError(outputPath + " exists. Use --force to overwrite");
|
||||||
|
System.out.println("Processing " + srcApkFile + " -> " + outputFile);
|
||||||
|
patch(srcApkFile, outputFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void patch(File srcApkFile) throws PatchError, IOException {
|
public void patch(File srcApkFile, File outputFile) throws PatchError, IOException {
|
||||||
if (!srcApkFile.exists())
|
if (!srcApkFile.exists())
|
||||||
throw new PatchError("The source apk file does not exit. Please provide a correct path.");
|
throw new PatchError("The source apk file does not exit. Please provide a correct path.");
|
||||||
|
|
||||||
|
|
@ -129,16 +142,6 @@ public class LSPatch {
|
||||||
System.out.println("apk path: " + srcApkFile);
|
System.out.println("apk path: " + srcApkFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
String apkFileName = srcApkFile.getName();
|
|
||||||
|
|
||||||
if (outputPath == null || outputPath.length() == 0) {
|
|
||||||
outputPath = String.format("%s-lv%s-xposed-signed.apk", FilenameUtils.getBaseName(apkFileName), sigbypassLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
File outputFile = new File(outputPath);
|
|
||||||
if (outputFile.exists() && !forceOverwrite)
|
|
||||||
throw new PatchError(outputPath + " exists. Use --force to overwrite");
|
|
||||||
|
|
||||||
System.out.println("Copying to tmp apk...");
|
System.out.println("Copying to tmp apk...");
|
||||||
|
|
||||||
FileUtils.copyFile(srcApkFile, tmpApk);
|
FileUtils.copyFile(srcApkFile, tmpApk);
|
||||||
|
|
@ -155,7 +158,7 @@ public class LSPatch {
|
||||||
System.out.println("Original signature\n" + originalSignature);
|
System.out.println("Original signature\n" + originalSignature);
|
||||||
try (var is = new ByteArrayInputStream(originalSignature.getBytes(StandardCharsets.UTF_8))) {
|
try (var is = new ByteArrayInputStream(originalSignature.getBytes(StandardCharsets.UTF_8))) {
|
||||||
zFile.add(SIGNATURE_INFO_ASSET_PATH, is);
|
zFile.add(SIGNATURE_INFO_ASSET_PATH, is);
|
||||||
} catch (IOException e) {
|
} catch (Throwable e) {
|
||||||
throw new PatchError("Error when saving signature: " + e);
|
throw new PatchError("Error when saving signature: " + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -183,14 +186,14 @@ public class LSPatch {
|
||||||
// modify manifest
|
// modify manifest
|
||||||
try (var is = new ByteArrayInputStream(modifyManifestFile(manifestEntry.open()))) {
|
try (var is = new ByteArrayInputStream(modifyManifestFile(manifestEntry.open()))) {
|
||||||
zFile.add(APPLICATION_NAME_ASSET_PATH, is);
|
zFile.add(APPLICATION_NAME_ASSET_PATH, is);
|
||||||
} catch (IOException e) {
|
} catch (Throwable e) {
|
||||||
throw new PatchError("Error when modifying manifest: " + e);
|
throw new PatchError("Error when modifying manifest: " + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// save original main application name to asset file even its empty
|
// save original main application name to asset file even its empty
|
||||||
try (var is = new ByteArrayInputStream(applicationName.getBytes(StandardCharsets.UTF_8))) {
|
try (var is = new ByteArrayInputStream(applicationName.getBytes(StandardCharsets.UTF_8))) {
|
||||||
zFile.add(APPLICATION_NAME_ASSET_PATH, is);
|
zFile.add(APPLICATION_NAME_ASSET_PATH, is);
|
||||||
} catch (IOException e) {
|
} catch (Throwable e) {
|
||||||
throw new PatchError("Error when saving signature: " + e);
|
throw new PatchError("Error when saving signature: " + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,45 +201,50 @@ public class LSPatch {
|
||||||
Set<String> apkArchs = new HashSet<>();
|
Set<String> apkArchs = new HashSet<>();
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
System.out.println("search target apk library arch..");
|
System.out.println("Search target apk library arch..");
|
||||||
for (StoredEntry storedEntry : zFile.entries()) {
|
for (StoredEntry storedEntry : zFile.entries()) {
|
||||||
for (String arch : APK_LIB_PATH_ARRAY) {
|
var name = storedEntry.getCentralDirectoryHeader().getName();
|
||||||
if (storedEntry.getCentralDirectoryHeader().getName().startsWith(arch)) {
|
if (name.startsWith("lib/")) {
|
||||||
|
var arch = name.substring(4, name.indexOf('/', 5));
|
||||||
apkArchs.add(arch);
|
apkArchs.add(arch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (apkArchs.isEmpty()) {
|
if (apkArchs.isEmpty()) {
|
||||||
apkArchs.add(APK_LIB_PATH_ARRAY[0]);
|
apkArchs.addAll(APK_LIB_PATH_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apkArchs.removeIf((arch) -> {
|
||||||
|
if (!APK_LIB_PATH_ARRAY.contains(arch)) {
|
||||||
|
System.err.println("Warning: unsupported arch " + arch + ". Skipping...");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
if (verbose)
|
||||||
|
System.out.println("Adding native lib..");
|
||||||
|
|
||||||
for (String arch : apkArchs) {
|
for (String arch : apkArchs) {
|
||||||
// lib/armeabi-v7a -> armeabi-v7a
|
// lib/armeabi-v7a -> armeabi-v7a
|
||||||
String justArch = arch.substring(arch.indexOf('/'));
|
String entryName = "lib/" + arch + "/liblspd.so";
|
||||||
File sod = new File("list-so", justArch);
|
try (var is = getClass().getClassLoader().getResourceAsStream("assets/so/" + arch + "/liblspd.so")) {
|
||||||
File[] files = sod.listFiles();
|
zFile.add(entryName, is);
|
||||||
if (files == null) {
|
} catch (Throwable e) {
|
||||||
System.err.println("Warning: No so file has been copied in " + sod.getPath());
|
throw new PatchError("Error when adding native lib: " + e);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
for (File file : files) {
|
|
||||||
zFile.add(arch + "/" + file.getName(), new FileInputStream(file));
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
System.out.println("add " + file.getPath());
|
System.out.println("added " + entryName);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy all dex files in list-dex
|
if (verbose)
|
||||||
File[] files = new File("list-dex").listFiles();
|
System.out.println("Adding dex..");
|
||||||
if (files == null || files.length == 0) {
|
|
||||||
System.err.println("Warning: No dex file has been copied");
|
try (var is = getClass().getClassLoader().getResourceAsStream("assets/dex/loader.dex")) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (File file : files) {
|
|
||||||
String copiedDexFileName = "classes" + (dexFileCount + 1) + ".dex";
|
String copiedDexFileName = "classes" + (dexFileCount + 1) + ".dex";
|
||||||
zFile.add(copiedDexFileName, new FileInputStream(file));
|
zFile.add(copiedDexFileName, is);
|
||||||
dexFileCount++;
|
dexFileCount++;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new PatchError("Error when add dex: " + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy origin apk to assets
|
// copy origin apk to assets
|
||||||
|
|
@ -245,22 +253,16 @@ public class LSPatch {
|
||||||
zFile.add(ORIGIN_APK_ASSET_PATH, new FileInputStream(srcApkFile));
|
zFile.add(ORIGIN_APK_ASSET_PATH, new FileInputStream(srcApkFile));
|
||||||
}
|
}
|
||||||
|
|
||||||
File[] listAssets = new File("list-assets").listFiles();
|
try (var is = getClass().getClassLoader().getResourceAsStream("assets/dex/lsp.dex")) {
|
||||||
if (listAssets == null || listAssets.length == 0) {
|
zFile.add("assets/lsp.dex", is);
|
||||||
System.err.println("Warning: No assets file copyied");
|
} catch (Throwable e) {
|
||||||
} else {
|
throw new PatchError("Error when add assets: " + e);
|
||||||
for (File f : listAssets) {
|
|
||||||
if (f.isDirectory()) {
|
|
||||||
throw new PatchError("unsupported directory in assets");
|
|
||||||
}
|
|
||||||
zFile.add("assets/" + f.getName(), new FileInputStream(f));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// save lspatch config to asset..
|
// save lspatch config to asset..
|
||||||
try (var is = new ByteArrayInputStream("42".getBytes(StandardCharsets.UTF_8))) {
|
try (var is = new ByteArrayInputStream("42".getBytes(StandardCharsets.UTF_8))) {
|
||||||
zFile.add("assets/" + Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel, is);
|
zFile.add("assets/" + Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel, is);
|
||||||
} catch (IOException e) {
|
} catch (Throwable e) {
|
||||||
throw new PatchError("Error when saving signature: " + e);
|
throw new PatchError("Error when saving signature: " + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -272,7 +274,10 @@ public class LSPatch {
|
||||||
|
|
||||||
System.out.println("Done. Output APK: " + outputFile.getAbsolutePath());
|
System.out.println("Done. Output APK: " + outputFile.getAbsolutePath());
|
||||||
} finally {
|
} finally {
|
||||||
|
try {
|
||||||
FileUtils.deleteDirectory(workingDir);
|
FileUtils.deleteDirectory(workingDir);
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue