Merge so and dex into jar

This commit is contained in:
LoveSy 2021-06-19 13:54:43 +08:00
parent 61624dafae
commit c87f7ffa14
6 changed files with 84 additions and 72 deletions

View File

@ -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

View File

@ -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"() {

View File

@ -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"() {
@ -52,4 +52,4 @@ android {
dependencies { dependencies {
implementation project(':share') implementation project(':share')
compileOnly project(":hiddenapi-stubs") compileOnly project(":hiddenapi-stubs")
} }

View File

@ -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;

View File

@ -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'
} }
@ -48,4 +55,4 @@ sourceSets.main.resources {
"src/main/java", "src/main/java",
]; ];
include "**/*.*" include "**/*.*"
} }

View File

@ -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/")) {
apkArchs.add(arch); var arch = name.substring(4, name.indexOf('/', 5));
} 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)
System.out.println("add " + file.getPath());
} }
if (verbose)
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 {
FileUtils.deleteDirectory(workingDir); try {
FileUtils.deleteDirectory(workingDir);
} catch (Throwable ignored) {
}
} }
} }