chore: merge upstream

# Conflicts:
#	brut.apktool/apktool-lib/build.gradle
#	build.gradle
This commit is contained in:
oSumAtrIX 2023-01-17 23:39:46 +01:00
commit bef4bc1a80
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
17 changed files with 137 additions and 100 deletions

View File

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

View File

@ -23,11 +23,12 @@ dependencies {
}
buildscript {
repositories {
mavenCentral()
}
dependencies {
repositories {
mavenCentral()
gradlePluginPortal()
}
classpath(depends.proguard_gradle) {
exclude group: 'com.android.tools.build'
}

View File

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

View File

@ -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[] {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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());
}
}

View File

@ -13,8 +13,3 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
dependencies {
testImplementation depends.junit
}

View File

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

View File

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

View File

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

View File

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