Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -457,18 +457,18 @@ private static void cmdDecode(String[] args) throws AndrolibException {
} else {
String mode = cli.getOptionValue(decodeResResolveModeOption);
switch (mode) {
case "remove":
config.setDecodeResolve(Config.DecodeResolve.REMOVE);
break;
case "keep":
config.setDecodeResolve(Config.DecodeResolve.KEEP);
break;
case "remove":
config.setDecodeResolve(Config.DecodeResolve.REMOVE);
break;
case "dummy":
config.setDecodeResolve(Config.DecodeResolve.DUMMY);
break;
default:
System.err.println("Unknown resolve resources mode: " + mode);
System.err.println("Expect: 'remove', 'keep' or 'dummy'.");
System.err.println("Expect: 'keep', 'remove' or 'dummy'.");
System.exit(1);
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,18 +382,19 @@ public String getAttributeName(int index) {
// Are improperly decoded when trusting the string pool.
// Leveraging the resource map allows us to get the proper value.
// <item android:state_enabled="true" app:d2="false" app:d3="true">
String nameStr;
try {
String resourceMapValue = decodeFromResourceId(nameId);
if (resourceMapValue != null) {
return resourceMapValue;
nameStr = decodeFromResourceId(nameId);
if (nameStr != null) {
return nameStr;
}
} catch (AndrolibException ignored) {
}

// Couldn't decode from resource map, fall back to string pool.
String stringPoolValue = mStringPool.getString(name);
if (stringPoolValue == null) {
stringPoolValue = "";
nameStr = mStringPool.getString(name);
if (nameStr == null) {
nameStr = "";
}

// In certain optimized apps, some attributes's specs are removed despite being used.
Expand All @@ -408,24 +409,24 @@ public String getAttributeName(int index) {
if (removeUnresolved || nameId.getPackageId() != pkg.getId()) {
LOGGER.warning(String.format(
"null attr reference: ns=%s, name=%s, id=%s",
getAttributePrefix(index), stringPoolValue, nameId));
return stringPoolValue;
getAttributePrefix(index), nameStr, nameId));
return nameStr;
}

if (stringPoolValue.isEmpty()) {
stringPoolValue = ResEntrySpec.MISSING_PREFIX + nameId;
if (nameStr.isEmpty()) {
nameStr = ResEntrySpec.MISSING_PREFIX + nameId;
}
pkg.addEntrySpec(nameId, stringPoolValue);
nameStr = pkg.addEntrySpec(nameId, nameStr).getName();
pkg.addEntry(nameId, ResConfig.DEFAULT, ResAttribute.DEFAULT);
} catch (AndrolibException ex) {
setFirstError(ex);
LOGGER.warning(String.format(
"Could not add missing attr: ns=%s, name=%s, id=%s",
getAttributePrefix(index), stringPoolValue, nameId));
getAttributePrefix(index), nameStr, nameId));
}
}

return stringPoolValue;
return nameStr;
}

private String decodeFromResourceId(ResId resId) throws AndrolibException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ public ResEntrySpec(ResTypeSpec typeSpec, ResId id, String name) {
assert typeSpec.getId() == id.getTypeId();
mTypeSpec = typeSpec;
mId = id;
// Some apps had their entry names obfuscated or collapsed to
// a single value in the key string pool.
// Some apps had their entry names obfuscated or collapsed to a single
// value in the key string pool.
mName = isValidEntryName(name) ? name : RENAMED_PREFIX + id;
}

Expand All @@ -47,14 +47,11 @@ private static boolean isValidEntryName(String name) {
if (!Character.isJavaIdentifierStart(name.charAt(0))) {
return false;
}
// The rest must be valid Java identifier part characters.
// The rest must be valid Java identifier part characters or any of the
// whitelisted special characters.
for (int i = 1; i < len; i++) {
char ch = name.charAt(i);
// Whitelisted special characters.
if (ch == '.' || ch == '-') {
continue;
}
if (!Character.isJavaIdentifierPart(ch)) {
if (!Character.isJavaIdentifierPart(ch) && ch != '.' && ch != '-') {
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Logger;

public class ResPackage {
Expand All @@ -36,6 +38,7 @@ public class ResPackage {
private final Map<Integer, ResTypeSpec> mTypeSpecs;
private final Map<Pair<Integer, ResConfig>, ResType> mTypes;
private final Map<ResId, ResEntrySpec> mEntrySpecs;
private final Map<Integer, Set<String>> mEntryNames;
private final Map<Pair<ResId, ResConfig>, ResEntry> mEntries;
private final Map<String, ResOverlayable> mOverlayables;

Expand All @@ -46,6 +49,7 @@ public ResPackage(ResTable table, int id, String name) {
mTypeSpecs = new HashMap<>();
mTypes = new HashMap<>();
mEntrySpecs = new HashMap<>();
mEntryNames = new HashMap<>();
mEntries = new HashMap<>();
mOverlayables = new HashMap<>();
}
Expand Down Expand Up @@ -152,8 +156,24 @@ public ResEntrySpec addEntrySpec(ResId id, String name) throws AndrolibException

int typeId = id.getTypeId();
ResTypeSpec typeSpec = getTypeSpec(typeId);

// Obfuscation can cause specs to be generated using existing names.
// Enforce uniqueness by renaming the spec when that happens.
Set<String> entryNames = mEntryNames.get(typeId);
if (entryNames == null) {
entryNames = new HashSet<>();
mEntryNames.put(typeId, entryNames);
} else if (entryNames.contains(name)) {
// Clear the name to force a rename.
name = "";
}

entrySpec = new ResEntrySpec(typeSpec, id, name);
mEntrySpecs.put(id, entrySpec);

// Record the name to enforce uniqueness.
entryNames.add(entrySpec.getName());

return entrySpec;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ public static ResItem parse(ResPackage pkg, int type, int data, String rawValue)
return data == TypedValue.DATA_NULL_EMPTY ? ResPrimitive.EMPTY : ResReference.NULL;
case TypedValue.TYPE_REFERENCE:
case TypedValue.TYPE_DYNAMIC_REFERENCE:
return data != 0 || rawValue != null
return (data != 0 || rawValue != null)
? new ResReference(pkg, ResId.of(data), rawValue)
: ResReference.NULL;
case TypedValue.TYPE_ATTRIBUTE:
case TypedValue.TYPE_DYNAMIC_ATTRIBUTE:
return data != 0 || rawValue != null
return (data != 0 || rawValue != null)
? new ResReference(pkg, ResId.of(data), rawValue, ResReference.Type.ATTRIBUTE)
: ResReference.NULL;
case TypedValue.TYPE_STRING:
Expand Down
2 changes: 1 addition & 1 deletion brut.j.util/src/main/java/brut/util/ZipUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public static void zipDir(File baseDir, String dirName, ZipOutputStream out, Col
if (file.isDirectory()) {
zipDir(baseDir, fileName, out, doNotCompress);
} else if (file.isFile()) {
zipFile(baseDir, fileName, out, doNotCompress != null && !doNotCompress.isEmpty()
zipFile(baseDir, fileName, out, (doNotCompress != null && !doNotCompress.isEmpty())
? entryName -> doNotCompress.contains(entryName)
|| doNotCompress.contains(FilenameUtils.getExtension(entryName))
: entryName -> false);
Expand Down