feat: merge iBotPeaches/Apktool

This commit is contained in:
oSumAtrIX 2022-09-23 04:22:53 +02:00
commit 9443f6fefb
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
24 changed files with 168 additions and 100 deletions

28
.github/workflows/analyze.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: Analyze
on:
push:
branches: [master]
pull_request:
branches: [master]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: java
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

View File

@ -98,22 +98,3 @@ jobs:
with: with:
name: apktool.jar name: apktool.jar
path: brut.apktool/apktool-cli/build/libs/apktool-*-small.jar path: brut.apktool/apktool-cli/build/libs/apktool-*-small.jar
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'java' ]
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@ -253,6 +253,11 @@ original as they were.
As cheesy as it is, just follow this [downloading](https://source.android.com/source/downloading.html) link in order As cheesy as it is, just follow this [downloading](https://source.android.com/source/downloading.html) link in order
to get the source downloaded. This is no small download, expect to use 150-250GB. to get the source downloaded. This is no small download, expect to use 150-250GB.
Some optimization techniques for a smaller clone:
* `~/bin/repo init -u https://android.googlesource.com/platform/manifest -b master --partial-clone` - Partial clone
* `repo sync -c` - Only current branch
After that, you need to build AOSP via this [documentation](https://source.android.com/source/building.html) guide. Now After that, you need to build AOSP via this [documentation](https://source.android.com/source/building.html) guide. Now
we aren't building the entire AOSP package, the initial build is to just see if you are capable of building it. we aren't building the entire AOSP package, the initial build is to just see if you are capable of building it.

View File

@ -49,3 +49,14 @@ Folks have requested running Apktool on device itself. This has been a challenge
that would be placed on the aapt2/aapt binaries. that would be placed on the aapt2/aapt binaries.
Suggestions: [#2811](https://github.com/iBotPeaches/Apktool/issues/2811) Suggestions: [#2811](https://github.com/iBotPeaches/Apktool/issues/2811)
## Split APK Support
Applications are further getting split on qualifiers. Apktool has been built on the assumption of one apk.
Suggestions: [#2283](https://github.com/iBotPeaches/Apktool/issues/2283), [#2218](https://github.com/iBotPeaches/Apktool/issues/2218), [#2880](https://github.com/iBotPeaches/Apktool/issues/2880)
## Dummy Resources
Folks want the ability to stop the auto generation of dummy resources.
Suggestions: [#2683](https://github.com/iBotPeaches/Apktool/issues/2683), [#2104](https://github.com/iBotPeaches/Apktool/issues/2104)
Pull Request(s): [#2463](https://github.com/iBotPeaches/Apktool/pull/2463)

View File

@ -337,7 +337,7 @@ public class Androlib {
throw new AndrolibException(ex.getMessage()); throw new AndrolibException(ex.getMessage());
} }
} }
LOGGER.fine("Built apk..."); LOGGER.fine("Built apk into: " + outFile.getPath());
} }
private void buildManifestFile(File appDir, File manifest, File manifestOriginal) private void buildManifestFile(File appDir, File manifest, File manifestOriginal)

View File

@ -67,6 +67,9 @@ final public class AndrolibResources {
ResPackage pkg; ResPackage pkg;
switch (pkgs.length) { switch (pkgs.length) {
case 0:
pkg = null;
break;
case 1: case 1:
pkg = pkgs[0]; pkg = pkgs[0];
break; break;

View File

@ -104,14 +104,14 @@ public class ResValueFactory {
return new ResPluralsValue(parentVal, items); return new ResPluralsValue(parentVal, items);
} }
if (ResTypeSpec.RES_TYPE_NAME_STYLES.equals(resTypeName)) {
return new ResStyleValue(parentVal, items, this);
}
if (ResTypeSpec.RES_TYPE_NAME_ATTR.equals(resTypeName)) { if (ResTypeSpec.RES_TYPE_NAME_ATTR.equals(resTypeName)) {
return new ResAttr(parentVal, 0, null, null, null); return new ResAttr(parentVal, 0, null, null, null);
} }
if (resTypeName.startsWith(ResTypeSpec.RES_TYPE_NAME_STYLES)) {
return new ResStyleValue(parentVal, items, this);
}
throw new AndrolibException("unsupported res type name for bags. Found: " + resTypeName); throw new AndrolibException("unsupported res type name for bags. Found: " + resTypeName);
} }

View File

@ -33,6 +33,7 @@ import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -250,8 +251,7 @@ public class ARSCDecoder {
/* reserved */mIn.skipBytes(2); /* reserved */mIn.skipBytes(2);
int entryCount = mIn.readInt(); int entryCount = mIn.readInt();
int entriesStart = mIn.readInt(); int entriesStart = mIn.readInt();
mMissingResSpecs = new boolean[entryCount]; mMissingResSpecMap = new LinkedHashMap();
Arrays.fill(mMissingResSpecs, true);
ResConfigFlags flags = readConfigFlags(); ResConfigFlags flags = readConfigFlags();
int position = (mHeader.startPosition + entriesStart) - (entryCount * 4); int position = (mHeader.startPosition + entriesStart) - (entryCount * 4);
@ -263,10 +263,18 @@ public class ARSCDecoder {
mIn.skipBytes(position - mCountIn.getCount()); mIn.skipBytes(position - mCountIn.getCount());
} }
if (typeFlags == 1) { if ((typeFlags & 0x01) != 0) {
LOGGER.fine("Sparse type flags detected: " + mTypeSpec.getName()); LOGGER.fine("Sparse type flags detected: " + mTypeSpec.getName());
} }
int[] entryOffsets = mIn.readIntArray(entryCount);
HashMap<Integer, Integer> entryOffsetMap = new LinkedHashMap();
for (int i = 0; i < entryCount; i++) {
if ((typeFlags & 0x01) != 0) {
entryOffsetMap.put(mIn.readUnsignedShort(), mIn.readUnsignedShort());
} else {
entryOffsetMap.put(i, mIn.readInt());
}
}
if (flags.isInvalid) { if (flags.isInvalid) {
String resName = mTypeSpec.getName() + flags.getQualifiers(); String resName = mTypeSpec.getName() + flags.getQualifiers();
@ -278,23 +286,15 @@ public class ARSCDecoder {
} }
mType = flags.isInvalid && !mKeepBroken ? null : mPkg.getOrCreateConfig(flags); mType = flags.isInvalid && !mKeepBroken ? null : mPkg.getOrCreateConfig(flags);
HashMap<Integer, EntryData> offsetsToEntryData = new HashMap<>();
for (int offset : entryOffsets) { for (int i : entryOffsetMap.keySet()) {
if (offset == -1 || offsetsToEntryData.containsKey(offset)) { int offset = entryOffsetMap.get(i);
if (offset == -1) {
continue; continue;
} }
mMissingResSpecMap.put(i, false);
offsetsToEntryData.put(offset, readEntryData());
}
for (int i = 0; i < entryOffsets.length; i++) {
if (entryOffsets[i] != -1) {
mMissingResSpecs[i] = false;
mResId = (mResId & 0xffff0000) | i; mResId = (mResId & 0xffff0000) | i;
EntryData entryData = offsetsToEntryData.get(entryOffsets[i]); readEntry(readEntryData());
readEntry(entryData);
}
} }
return mType; return mType;
@ -533,14 +533,12 @@ public class ARSCDecoder {
private void addMissingResSpecs() throws AndrolibException { private void addMissingResSpecs() throws AndrolibException {
int resId = mResId & 0xffff0000; int resId = mResId & 0xffff0000;
for (int i = 0; i < mMissingResSpecs.length; i++) { for (int i : mMissingResSpecMap.keySet()) {
if (!mMissingResSpecs[i]) { if (mMissingResSpecMap.get(i)) continue;
continue;
}
ResResSpec spec = new ResResSpec(new ResID(resId | i), "APKTOOL_DUMMY_" + Integer.toHexString(i), mPkg, mTypeSpec); ResResSpec spec = new ResResSpec(new ResID(resId | i), "APKTOOL_DUMMY_" + Integer.toHexString(i), mPkg, mTypeSpec);
// If we already have this resID dont add it again. // If we already have this resID don't add it again.
if (! mPkg.hasResSpec(new ResID(resId | i))) { if (! mPkg.hasResSpec(new ResID(resId | i))) {
mPkg.addResSpec(spec); mPkg.addResSpec(spec);
mTypeSpec.addResSpec(spec); mTypeSpec.addResSpec(spec);
@ -598,7 +596,7 @@ public class ARSCDecoder {
private ResType mType; private ResType mType;
private int mResId; private int mResId;
private int mTypeIdOffset = 0; private int mTypeIdOffset = 0;
private boolean[] mMissingResSpecs; private HashMap<Integer, Boolean> mMissingResSpecMap;
private final HashMap<Integer, ResTypeSpec> mResTypeSpecs = new HashMap<>(); private final HashMap<Integer, ResTypeSpec> mResTypeSpecs = new HashMap<>();
private final static short ENTRY_FLAG_COMPLEX = 0x0001; private final static short ENTRY_FLAG_COMPLEX = 0x0001;

View File

@ -336,16 +336,26 @@ public class AXmlResourceParser implements XmlResourceParser {
} }
String value = m_strings.getString(name); String value = m_strings.getString(name);
String namespace = getAttributeNamespace(index);
// some attributes will return "", we must rely on the resource_id and refer to the frameworks // If attribute name is lacking or a private namespace emerges,
// to match the resource id to the name. ex: 0x101021C = versionName // retrieve the exact attribute name by its id.
if (value.length() == 0 || android_ns.equals(getAttributeNamespace(index))) { if (value == null || value.length() == 0) {
try { try {
int resourceId = getAttributeNameResource(index);
if (resourceId != 0) {
value = mAttrDecoder.decodeManifestAttr(getAttributeNameResource(index)); value = mAttrDecoder.decodeManifestAttr(getAttributeNameResource(index));
if (value == null) {
value = "";
} }
} catch (AndrolibException | NullPointerException ignored) {} } catch (AndrolibException e) {
value = "";
}
} else if (! namespace.equals(android_ns)) {
try {
String obfuscatedName = mAttrDecoder.decodeManifestAttr(getAttributeNameResource(index));
if (! (obfuscatedName == null || obfuscatedName.equals(value))) {
value = obfuscatedName;
}
} catch (AndrolibException ignored) {}
} }
return value; return value;
} }
@ -381,11 +391,27 @@ public class AXmlResourceParser implements XmlResourceParser {
if (mAttrDecoder != null) { if (mAttrDecoder != null) {
try { try {
String value = valueRaw == -1 ? null : ResXmlEncoders.escapeXmlChars(m_strings.getString(valueRaw));
String obfuscatedValue = mAttrDecoder.decodeManifestAttr(valueData);
if (! (value == null || obfuscatedValue == null)) {
int slashPos = value.lastIndexOf("/");
if (slashPos != -1) {
// Handle a value with a format of "@yyy/xxx"
String dir = value.substring(0, slashPos);
value = dir + "/"+ obfuscatedValue;
} else if (! value.equals(obfuscatedValue)) {
value = obfuscatedValue;
}
}
return mAttrDecoder.decode( return mAttrDecoder.decode(
valueType, valueType,
valueData, valueData,
valueRaw == -1 ? null : ResXmlEncoders.escapeXmlChars(m_strings.getString(valueRaw)), value,
getAttributeNameResource(index)); getAttributeNameResource(index)
);
} catch (AndrolibException ex) { } catch (AndrolibException ex) {
setFirstError(ex); setFirstError(ex);
LOGGER.log(Level.WARNING, String.format("Could not decode attr value, using undecoded value " LOGGER.log(Level.WARNING, String.format("Could not decode attr value, using undecoded value "

View File

@ -37,6 +37,9 @@ public class AndroidManifestResourceParser extends AXmlResourceParser {
@Override @Override
public String getAttributeValue(int index) { public String getAttributeValue(int index) {
String value = super.getAttributeValue(index); String value = super.getAttributeValue(index);
if (value == null) {
return "";
}
if (!isNumericStringMetadataAttributeValue(index, value)) { if (!isNumericStringMetadataAttributeValue(index, value)) {
return value; return value;
@ -46,7 +49,7 @@ public class AndroidManifestResourceParser extends AXmlResourceParser {
// Otherwise, when the decoded app is rebuilt, aapt will incorrectly encode // Otherwise, when the decoded app is rebuilt, aapt will incorrectly encode
// the value as an int or float (depending on aapt version), breaking the original // the value as an int or float (depending on aapt version), breaking the original
// app functionality. // app functionality.
return "\\ " + super.getAttributeValue(index).trim(); return "\\ " + value.trim();
} }
private boolean isNumericStringMetadataAttributeValue(int index, String value) { private boolean isNumericStringMetadataAttributeValue(int index, String value) {

View File

@ -18,6 +18,7 @@ package brut.androlib.res.decoder;
import brut.androlib.AndrolibException; import brut.androlib.AndrolibException;
import brut.androlib.err.UndefinedResObjectException; import brut.androlib.err.UndefinedResObjectException;
import brut.androlib.res.data.ResID;
import brut.androlib.res.data.ResPackage; import brut.androlib.res.data.ResPackage;
import brut.androlib.res.data.ResResSpec; import brut.androlib.res.data.ResResSpec;
import brut.androlib.res.data.value.ResAttr; import brut.androlib.res.data.value.ResAttr;
@ -48,12 +49,26 @@ public class ResAttrDecoder {
throws AndrolibException { throws AndrolibException {
if (attrResId != 0) { if (attrResId != 0) {
ResResSpec resResSpec = getCurrentPackage().getResTable().getResSpec(attrResId); int attrId = attrResId;
// See also: brut.androlib.res.data.ResTable.getResSpec
if (attrId >> 24 == 0) {
ResPackage pkg = getCurrentPackage();
int packageId = pkg.getId();
int pkgId = (packageId == 0 ? 2 : packageId);
attrId = (0xFF000000 & (pkgId << 24)) | attrId;
}
// Retrieve the ResSpec in a package by id
ResID resId = new ResID(attrId);
ResPackage pkg = getCurrentPackage();
if (pkg.hasResSpec(resId)) {
ResResSpec resResSpec = pkg.getResSpec(resId);
if (resResSpec != null) { if (resResSpec != null) {
return resResSpec.getName(); return resResSpec.getName();
} }
} }
}
return null; return null;
} }

View File

@ -129,6 +129,10 @@ public final class ResXmlPatcher {
* Creates a modified network security config file that is more permissive * Creates a modified network security config file that is more permissive
* *
* @param file network security config file * @param file network security config file
* @throws TransformerException XML file could not be edited
* @throws IOException XML file could not be located
* @throws SAXException XML file could not be read
* @throws ParserConfigurationException XML nodes could be written
*/ */
public static void modNetworkSecurityConfig(File file) public static void modNetworkSecurityConfig(File file)
throws ParserConfigurationException, TransformerException, IOException, SAXException { throws ParserConfigurationException, TransformerException, IOException, SAXException {
@ -184,15 +188,12 @@ public final class ResXmlPatcher {
for (int i = 0; i < nodes.getLength(); i++) { for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i); Node node = nodes.item(i);
NamedNodeMap attrs = node.getAttributes(); NamedNodeMap attrs = node.getAttributes();
if (attrs != null) {
Node provider = attrs.getNamedItem("android:authorities"); Node provider = attrs.getNamedItem("android:authorities");
if (provider != null) { if (provider != null) {
saved = isSaved(file, saved, provider); saved = isSaved(file, saved, provider);
} }
} }
}
// android:scheme // android:scheme
xPath = XPathFactory.newInstance().newXPath(); xPath = XPathFactory.newInstance().newXPath();
@ -204,15 +205,12 @@ public final class ResXmlPatcher {
for (int i = 0; i < nodes.getLength(); i++) { for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i); Node node = nodes.item(i);
NamedNodeMap attrs = node.getAttributes(); NamedNodeMap attrs = node.getAttributes();
if (attrs != null) {
Node provider = attrs.getNamedItem("android:scheme"); Node provider = attrs.getNamedItem("android:scheme");
if (provider != null) { if (provider != null) {
saved = isSaved(file, saved, provider); saved = isSaved(file, saved, provider);
} }
} }
}
if (saved) { if (saved) {
saveDocument(file, doc); saveDocument(file, doc);

View File

@ -6,7 +6,7 @@ chcp 65001 2>nul >nul
set java_exe=java.exe set java_exe=java.exe
if defined JAVA_HOME ( if defined JAVA_HOME (
set java_exe="%JAVA_HOME%\bin\java.exe" set "java_exe=%JAVA_HOME%\bin\java.exe"
) )
rem Find the highest version .jar available in the same directory as the script rem Find the highest version .jar available in the same directory as the script
@ -36,7 +36,7 @@ if "%ATTR:~0,1%"=="-" if "%~x1"==".apk" (
) )
:load :load
%java_exe% -jar -Duser.language=en -Dfile.encoding=UTF8 "%~dp0%BASENAME%%max%.jar" %fastCommand% %* "%java_exe%" -jar -Duser.language=en -Dfile.encoding=UTF8 "%~dp0%BASENAME%%max%.jar" %fastCommand% %*
rem Pause when ran non interactively rem Pause when ran non interactively
for /f "tokens=2" %%# in ("%cmdcmdline%") do if /i "%%#" equ "/c" pause for /f "tokens=2" %%# in ("%cmdcmdline%") do if /i "%%#" equ "/c" pause