chore: merge upstream
# Conflicts: # brut.apktool/apktool-lib/build.gradle # build.gradle
This commit is contained in:
commit
bef4bc1a80
|
@ -65,7 +65,7 @@ jobs:
|
|||
fail-fast: true
|
||||
matrix:
|
||||
os: [ ubuntu-latest, macOS-latest, windows-latest ]
|
||||
java: [ 8, 9, 10, 11, 12, 13, 14, 15, 16 ]
|
||||
java: [ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
|
|
|
@ -23,11 +23,12 @@ dependencies {
|
|||
}
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
classpath(depends.proguard_gradle) {
|
||||
exclude group: 'com.android.tools.build'
|
||||
}
|
||||
|
|
|
@ -13,33 +13,26 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
import org.apache.tools.ant.filters.*
|
||||
|
||||
apply plugin: 'java-library'
|
||||
|
||||
processResources {
|
||||
from('src/main/resources/properties') {
|
||||
include '**/*.properties'
|
||||
into 'properties'
|
||||
filter(ReplaceTokens, tokens: [version: project.apktool_version, gitrev: project.hash])
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
}
|
||||
from('src/main/resources/') {
|
||||
include '**/*.jar'
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
}
|
||||
from('src/main/resources/properties') {
|
||||
include '**/*.properties'
|
||||
into 'properties'
|
||||
filter(ReplaceTokens, tokens: [version: project.apktool_version, gitrev: project.hash] )
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
}
|
||||
from('src/main/resources/') {
|
||||
include '**/*.jar'
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
}
|
||||
|
||||
includeEmptyDirs = false
|
||||
includeEmptyDirs = false
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation depends.junit
|
||||
|
||||
api project(':brut.j.dir'),
|
||||
project(':brut.j.util'),
|
||||
project(':brut.j.common')
|
||||
|
||||
def androidJar
|
||||
def root = System.getenv('ANDROID_SDK_ROOT')
|
||||
if (root == null) {
|
||||
|
@ -52,14 +45,19 @@ dependencies {
|
|||
|
||||
compileOnly androidJar
|
||||
|
||||
implementation depends.baksmali,
|
||||
depends.smali,
|
||||
depends.snakeyaml,
|
||||
depends.xmlpull,
|
||||
depends.guava,
|
||||
depends.commons_lang,
|
||||
depends.commons_io,
|
||||
depends.commons_text
|
||||
api project(':brut.j.dir')
|
||||
api project(':brut.j.util')
|
||||
api project(':brut.j.common')
|
||||
|
||||
implementation depends.baksmali
|
||||
implementation depends.smali
|
||||
implementation depends.snakeyaml
|
||||
implementation depends.xmlpull
|
||||
implementation depends.guava
|
||||
implementation depends.commons_lang
|
||||
implementation depends.commons_io
|
||||
implementation depends.commons_text
|
||||
|
||||
testImplementation depends.junit
|
||||
testImplementation depends.xmlunit
|
||||
}
|
||||
|
|
|
@ -177,16 +177,19 @@ public class Androlib {
|
|||
|
||||
for (String file : files) {
|
||||
if (isAPKFileNames(file) && unk.getCompressionLevel(file) == 0) {
|
||||
String ext = "";
|
||||
String extOrFile = "";
|
||||
if (unk.getSize(file) != 0) {
|
||||
ext = FilenameUtils.getExtension(file);
|
||||
extOrFile = FilenameUtils.getExtension(file);
|
||||
}
|
||||
|
||||
if (ext.isEmpty() || !NO_COMPRESS_PATTERN.matcher(ext).find()) {
|
||||
ext = file;
|
||||
if (extOrFile.isEmpty() || !NO_COMPRESS_PATTERN.matcher(extOrFile).find()) {
|
||||
extOrFile = file;
|
||||
if (mAndRes.mResFileMapping.containsKey(extOrFile)) {
|
||||
extOrFile = mAndRes.mResFileMapping.get(extOrFile);
|
||||
}
|
||||
}
|
||||
if (!uncompressedFilesOrExts.contains(ext)) {
|
||||
uncompressedFilesOrExts.add(ext);
|
||||
if (!uncompressedFilesOrExts.contains(extOrFile)) {
|
||||
uncompressedFilesOrExts.add(extOrFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -839,7 +842,7 @@ public class Androlib {
|
|||
private final static String APK_DIRNAME = "build/apk";
|
||||
private final static String UNK_DIRNAME = "unknown";
|
||||
private final static String[] APK_RESOURCES_FILENAMES = new String[] {
|
||||
"resources.arsc", "AndroidManifest.xml", "res" };
|
||||
"resources.arsc", "AndroidManifest.xml", "res", "r", "R" };
|
||||
private final static String[] APK_RESOURCES_WITHOUT_RES_FILENAMES = new String[] {
|
||||
"resources.arsc", "AndroidManifest.xml" };
|
||||
private final static String[] APP_RESOURCES_FILENAMES = new String[] {
|
||||
|
|
|
@ -180,6 +180,10 @@ public class ApkDecoder {
|
|||
if (mode != DECODE_SOURCES_NONE && mode != DECODE_SOURCES_SMALI && mode != DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES) {
|
||||
throw new AndrolibException("Invalid decode sources mode: " + mode);
|
||||
}
|
||||
if (mDecodeSources == DECODE_SOURCES_NONE && mode == DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES) {
|
||||
LOGGER.info("--only-main-classes cannot be paired with -s/--no-src. Ignoring.");
|
||||
return;
|
||||
}
|
||||
mDecodeSources = mode;
|
||||
}
|
||||
|
||||
|
|
|
@ -223,22 +223,12 @@ final public class AndrolibResources {
|
|||
ResAttrDecoder attrDecoder = duo.m2.getAttrDecoder();
|
||||
|
||||
attrDecoder.setCurrentPackage(resTable.listMainPackages().iterator().next());
|
||||
Directory inApk, in = null, out;
|
||||
Directory in, out;
|
||||
|
||||
try {
|
||||
out = new FileDirectory(outDir);
|
||||
|
||||
inApk = apkFile.getDirectory();
|
||||
in = apkFile.getDirectory();
|
||||
out = out.createDir("res");
|
||||
if (inApk.containsDir("res")) {
|
||||
in = inApk.getDir("res");
|
||||
}
|
||||
if (in == null && inApk.containsDir("r")) {
|
||||
in = inApk.getDir("r");
|
||||
}
|
||||
if (in == null && inApk.containsDir("R")) {
|
||||
in = inApk.getDir("R");
|
||||
}
|
||||
} catch (DirectoryException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
|
@ -249,7 +239,7 @@ final public class AndrolibResources {
|
|||
|
||||
LOGGER.fine("Decoding file-resources...");
|
||||
for (ResResource res : pkg.listFiles()) {
|
||||
fileDecoder.decode(res, in, out);
|
||||
fileDecoder.decode(res, in, out, mResFileMapping);
|
||||
}
|
||||
|
||||
LOGGER.fine("Decoding values */* XMLs...");
|
||||
|
@ -974,7 +964,12 @@ final public class AndrolibResources {
|
|||
} else if (OSDetection.isWindows()) {
|
||||
path = parentPath.getAbsolutePath() + String.format("%1$sAppData%1$sLocal%1$sapktool%1$sframework", File.separatorChar);
|
||||
} else {
|
||||
path = parentPath.getAbsolutePath() + String.format("%1$s.local%1$sshare%1$sapktool%1$sframework", File.separatorChar);
|
||||
String xdgDataFolder = System.getenv("XDG_DATA_HOME");
|
||||
if (xdgDataFolder != null) {
|
||||
path = xdgDataFolder + String.format("%1$sapktool%1$sframework", File.separatorChar);
|
||||
} else {
|
||||
path = parentPath.getAbsolutePath() + String.format("%1$s.local%1$sshare%1$sapktool%1$sframework", File.separatorChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1030,6 +1025,8 @@ final public class AndrolibResources {
|
|||
|
||||
public BuildOptions buildOptions;
|
||||
|
||||
public Map<String, String> mResFileMapping = new HashMap();
|
||||
|
||||
// TODO: dirty static hack. I have to refactor decoding mechanisms.
|
||||
public static boolean sKeepBroken = false;
|
||||
|
||||
|
|
|
@ -36,6 +36,8 @@ public class ResResSpec {
|
|||
this.mId = id;
|
||||
String cleanName;
|
||||
|
||||
name = (("(name removed)".equals(name)) ? null : name);
|
||||
|
||||
ResResSpec resResSpec = type.getResSpecUnsafe(name);
|
||||
if (resResSpec != null) {
|
||||
cleanName = String.format("APKTOOL_DUPLICATE_%s_%s", type, id.toString());
|
||||
|
|
|
@ -71,7 +71,7 @@ public class ResEnumAttr extends ResAttr {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (ref != null) {
|
||||
if (ref != null && !ref.referentIsNull()) {
|
||||
value2 = ref.getReferent().getName();
|
||||
mItemsCache.put(value, value2);
|
||||
}
|
||||
|
|
|
@ -177,7 +177,19 @@ public class ARSCDecoder {
|
|||
nextChunk();
|
||||
}
|
||||
|
||||
private void readOverlaySpec() throws IOException {
|
||||
private void readOverlaySpec() throws AndrolibException, IOException {
|
||||
checkChunkType(Header.XML_TYPE_OVERLAY);
|
||||
String name = mIn.readNullEndedString(128, true);
|
||||
String actor = mIn.readNullEndedString(128, true);
|
||||
LOGGER.fine(String.format("Overlay name: \"%s\", actor: \"%s\")", name, actor));
|
||||
|
||||
while(nextChunk().type == Header.XML_TYPE_OVERLAY_POLICY) {
|
||||
readOverlayPolicySpec();
|
||||
}
|
||||
}
|
||||
|
||||
private void readOverlayPolicySpec() throws AndrolibException, IOException {
|
||||
checkChunkType(Header.XML_TYPE_OVERLAY_POLICY);
|
||||
/* policyFlags */mIn.skipInt();
|
||||
int count = mIn.readInt();
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import brut.directory.DirectoryException;
|
|||
import brut.util.OSDetection;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
@ -38,10 +39,11 @@ public class ResFileDecoder {
|
|||
this.mDecoders = decoders;
|
||||
}
|
||||
|
||||
public void decode(ResResource res, Directory inDir, Directory outDir)
|
||||
public void decode(ResResource res, Directory inDir, Directory outDir, Map<String, String> resFileMapping)
|
||||
throws AndrolibException {
|
||||
|
||||
ResFileValue fileValue = (ResFileValue) res.getValue();
|
||||
String inFilePath = fileValue.toString();
|
||||
String inFileName = fileValue.getStrippedPath();
|
||||
String outResName = res.getFilePath();
|
||||
String typeName = res.getResSpec().getType().getName();
|
||||
|
@ -56,13 +58,18 @@ public class ResFileDecoder {
|
|||
outFileName = outResName + ext;
|
||||
}
|
||||
|
||||
String outFilePath = "res/" + outFileName;
|
||||
if (!inFilePath.equals(outFilePath)) {
|
||||
resFileMapping.put(inFilePath, outFilePath);
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeName.equals("raw")) {
|
||||
decode(inDir, inFileName, outDir, outFileName, "raw");
|
||||
decode(inDir, inFilePath, outDir, outFileName, "raw");
|
||||
return;
|
||||
}
|
||||
if (typeName.equals("font") && !".xml".equals(ext)) {
|
||||
decode(inDir, inFileName, outDir, outFileName, "raw");
|
||||
decode(inDir, inFilePath, outDir, outFileName, "raw");
|
||||
return;
|
||||
}
|
||||
if (typeName.equals("drawable") || typeName.equals("mipmap")) {
|
||||
|
@ -77,26 +84,24 @@ public class ResFileDecoder {
|
|||
// check for raw 9patch images
|
||||
for (String extension : RAW_9PATCH_IMAGE_EXTENSIONS) {
|
||||
if (inFileName.toLowerCase().endsWith("." + extension)) {
|
||||
copyRaw(inDir, outDir, inFileName, outFileName);
|
||||
copyRaw(inDir, outDir, inFilePath, outFileName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// check for xml 9 patches which are just xml files
|
||||
if (inFileName.toLowerCase().endsWith(".xml")) {
|
||||
decode(inDir, inFileName, outDir, outFileName, "xml");
|
||||
decode(inDir, inFilePath, outDir, outFileName, "xml");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
decode(inDir, inFileName, outDir, outFileName, "9patch");
|
||||
decode(inDir, inFilePath, outDir, outFileName, "9patch");
|
||||
return;
|
||||
} catch (CantFind9PatchChunkException ex) {
|
||||
LOGGER.log(
|
||||
Level.WARNING,
|
||||
String.format(
|
||||
"Cant find 9patch chunk in file: \"%s\". Renaming it to *.png.",
|
||||
inFileName), ex);
|
||||
LOGGER.log(Level.WARNING, String.format(
|
||||
"Cant find 9patch chunk in file: \"%s\". Renaming it to *.png.", inFileName
|
||||
), ex);
|
||||
outDir.removeFile(outFileName);
|
||||
outFileName = outResName + ext;
|
||||
}
|
||||
|
@ -105,27 +110,27 @@ public class ResFileDecoder {
|
|||
// check for raw image
|
||||
for (String extension : RAW_IMAGE_EXTENSIONS) {
|
||||
if (inFileName.toLowerCase().endsWith("." + extension)) {
|
||||
copyRaw(inDir, outDir, inFileName, outFileName);
|
||||
copyRaw(inDir, outDir, inFilePath, outFileName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!".xml".equals(ext)) {
|
||||
decode(inDir, inFileName, outDir, outFileName, "raw");
|
||||
decode(inDir, inFilePath, outDir, outFileName, "raw");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
decode(inDir, inFileName, outDir, outFileName, "xml");
|
||||
decode(inDir, inFilePath, outDir, outFileName, "xml");
|
||||
} catch (RawXmlEncounteredException ex) {
|
||||
// If we got an error to decode XML, lets assume the file is in raw format.
|
||||
// This is a large assumption, that might increase runtime, but will save us for situations where
|
||||
// XSD files are AXML`d on aapt1, but left in plaintext in aapt2.
|
||||
decode(inDir, inFileName, outDir, outFileName, "raw");
|
||||
decode(inDir, inFilePath, outDir, outFileName, "raw");
|
||||
} catch (AndrolibException ex) {
|
||||
LOGGER.log(Level.SEVERE, String.format(
|
||||
"Could not decode file, replacing by FALSE value: %s",
|
||||
inFileName), ex);
|
||||
"Could not decode file, replacing by FALSE value: %s",
|
||||
inFileName), ex);
|
||||
res.replace(new ResBoolValue(false, 0, null));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ public class StyledString {
|
|||
|
||||
public static class Span implements Comparable<Span> {
|
||||
private static final MapSplitter ATTRIBUTES_SPLITTER =
|
||||
Splitter.on(';').withKeyValueSeparator(Splitter.on('=').limit(2));
|
||||
Splitter.on(';').omitEmptyStrings().withKeyValueSeparator(Splitter.on('=').limit(2));
|
||||
|
||||
private final String tag;
|
||||
private final int firstChar;
|
||||
|
|
|
@ -56,4 +56,18 @@ public class AndResGuardTest extends BaseTest {
|
|||
File aPng = new File(sTestOrigDir,"res/mipmap-hdpi-v4/a.png");
|
||||
assertTrue(aPng.isFile());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkifAndResDecodeRemapsRFolderInRawMode() throws BrutException, IOException {
|
||||
String apk = "issue1170.apk";
|
||||
ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk));
|
||||
sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".raw.out");
|
||||
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".raw.out"));
|
||||
apkDecoder.setDecodeResources(ApkDecoder.DECODE_RESOURCES_NONE);
|
||||
apkDecoder.decode();
|
||||
|
||||
File aPng = new File(sTestOrigDir,"r/a/a.png");
|
||||
assertTrue(aPng.isFile());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,8 +13,3 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
dependencies {
|
||||
testImplementation depends.junit
|
||||
}
|
||||
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
*/
|
||||
|
||||
dependencies {
|
||||
implementation project(':brut.j.common'),
|
||||
project(':brut.j.util'),
|
||||
depends.commons_io
|
||||
testImplementation depends.junit
|
||||
implementation project(':brut.j.common')
|
||||
implementation project(':brut.j.util')
|
||||
implementation depends.commons_io
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import brut.common.TraversalUnknownFileException;
|
|||
import brut.util.BrutIO;
|
||||
import brut.util.OS;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class DirUtil {
|
||||
|
@ -84,14 +85,13 @@ public class DirUtil {
|
|||
if (in.containsDir(fileName)) {
|
||||
OS.rmdir(new File(out, fileName));
|
||||
in.getDir(fileName).copyToDir(new File(out, fileName));
|
||||
} else if (!in.containsDir(fileName) && !in.containsFile(fileName)) {
|
||||
// Skip copies of directories/files not found.
|
||||
} else {
|
||||
if (fileName.equals("res") && !in.containsFile(fileName)) {
|
||||
return;
|
||||
}
|
||||
String cleanedFilename = BrutIO.sanitizeUnknownFile(out, fileName);
|
||||
File outFile = new File(out, cleanedFilename);
|
||||
outFile.getParentFile().mkdirs();
|
||||
BrutIO.copyAndClose(in.getFileInput(fileName), new FileOutputStream(outFile));
|
||||
BrutIO.copyAndClose(in.getFileInput(fileName), Files.newOutputStream(outFile.toPath()));
|
||||
}
|
||||
} catch (RootUnknownFileException | InvalidUnknownFileException | TraversalUnknownFileException exception) {
|
||||
LOGGER.warning(String.format("Skipping file %s (%s)", fileName, exception.getMessage()));
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
|
||||
dependencies {
|
||||
implementation project(':brut.j.common'),
|
||||
depends.commons_io
|
||||
testImplementation depends.junit
|
||||
implementation project(':brut.j.common')
|
||||
implementation depends.commons_io
|
||||
}
|
||||
|
|
30
build.gradle
30
build.gradle
|
@ -18,23 +18,22 @@ import java.nio.charset.StandardCharsets
|
|||
buildscript {
|
||||
ext {
|
||||
depends = [
|
||||
baksmali : 'org.smali:baksmali:2.5.2',
|
||||
baksmali : 'com.github.iBotPeaches.smali:baksmali:403e90375e',
|
||||
commons_cli : 'commons-cli:commons-cli:1.5.0',
|
||||
commons_io : 'commons-io:commons-io:2.11.0',
|
||||
commons_lang : 'org.apache.commons:commons-lang3:3.12.0',
|
||||
commons_text : 'org.apache.commons:commons-text:1.9',
|
||||
commons_text : 'org.apache.commons:commons-text:1.10.0',
|
||||
guava : 'com.google.guava:guava:31.0.1-jre',
|
||||
junit : 'junit:junit:4.13.2',
|
||||
proguard_gradle: 'com.guardsquare:proguard-gradle:7.1.1',
|
||||
snakeyaml : 'org.yaml:snakeyaml:1.29:android',
|
||||
smali : 'org.smali:smali:2.5.2',
|
||||
proguard_gradle: 'com.guardsquare:proguard-gradle:7.3.0',
|
||||
snakeyaml : 'org.yaml:snakeyaml:1.32:android',
|
||||
smali : 'com.github.iBotPeaches.smali:smali:403e90375e',
|
||||
xmlpull : 'xpp3:xpp3:1.1.4c',
|
||||
xmlunit : 'xmlunit:xmlunit:1.6',
|
||||
]
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
dependencies {
|
||||
|
@ -49,7 +48,7 @@ def ghPassword = project.findProperty("gpr.key") ?: System.getenv("GITHUB_TOKEN"
|
|||
apply from: 'gradle/functions.gradle'
|
||||
|
||||
version = '2.7.0'
|
||||
def suffix = 'SNAPSHOT'
|
||||
def suffix = ''
|
||||
|
||||
defaultTasks 'build', 'shadowJar', 'proguard'
|
||||
|
||||
|
@ -97,6 +96,19 @@ allprojects {
|
|||
"licenseTest"
|
||||
]
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
||||
// Obtain baksmali/smali from source builds - https://github.com/iBotPeaches/smali
|
||||
// Remove when official smali releases come out again.
|
||||
maven {
|
||||
url 'https://jitpack.io'
|
||||
content {
|
||||
includeGroup('com.github.iBotPeaches.smali')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
|
@ -165,10 +177,6 @@ task snapshot {
|
|||
subprojects {
|
||||
apply plugin: 'java'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
test {
|
||||
testLogging {
|
||||
exceptionFormat = 'full'
|
||||
|
|
Loading…
Reference in New Issue