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:
name: apktool.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
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
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.
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());
}
}
LOGGER.fine("Built apk...");
LOGGER.fine("Built apk into: " + outFile.getPath());
}
private void buildManifestFile(File appDir, File manifest, File manifestOriginal)

View File

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

View File

@ -104,13 +104,13 @@ public class ResValueFactory {
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)) {
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);
}

View File

@ -33,6 +33,7 @@ import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.logging.Logger;
@ -159,7 +160,7 @@ public class ARSCDecoder {
for (int i = 0; i < libraryCount; i++) {
packageId = mIn.readInt();
packageName = mIn.readNullEndedString(128, true);
LOGGER.fine(String.format("Decoding Shared Library (%s), pkgId: %d", packageName, packageId));
LOGGER.fine(String.format("Decoding Shared Library (%s), pkgId: %d", packageName, packageId));
}
while(nextChunk().type == Header.XML_TYPE_TYPE) {
@ -232,7 +233,7 @@ public class ARSCDecoder {
mFlagsOffsets.add(new FlagsOffset(mCountIn.getCount(), entryCount));
}
/* flags */mIn.skipBytes(entryCount * 4);
/* flags */mIn.skipBytes(entryCount * 4);
mTypeSpec = new ResTypeSpec(mTypeNames.getString(id - 1), mResTable, mPkg, id, entryCount);
mPkg.addType(mTypeSpec);
return mTypeSpec;
@ -250,8 +251,7 @@ public class ARSCDecoder {
/* reserved */mIn.skipBytes(2);
int entryCount = mIn.readInt();
int entriesStart = mIn.readInt();
mMissingResSpecs = new boolean[entryCount];
Arrays.fill(mMissingResSpecs, true);
mMissingResSpecMap = new LinkedHashMap();
ResConfigFlags flags = readConfigFlags();
int position = (mHeader.startPosition + entriesStart) - (entryCount * 4);
@ -263,10 +263,18 @@ public class ARSCDecoder {
mIn.skipBytes(position - mCountIn.getCount());
}
if (typeFlags == 1) {
LOGGER.fine("Sparse type flags detected: " + mTypeSpec.getName());
if ((typeFlags & 0x01) != 0) {
LOGGER.fine("Sparse type flags detected: " + mTypeSpec.getName());
}
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());
}
}
int[] entryOffsets = mIn.readIntArray(entryCount);
if (flags.isInvalid) {
String resName = mTypeSpec.getName() + flags.getQualifiers();
@ -278,23 +286,15 @@ public class ARSCDecoder {
}
mType = flags.isInvalid && !mKeepBroken ? null : mPkg.getOrCreateConfig(flags);
HashMap<Integer, EntryData> offsetsToEntryData = new HashMap<>();
for (int offset : entryOffsets) {
if (offset == -1 || offsetsToEntryData.containsKey(offset)) {
for (int i : entryOffsetMap.keySet()) {
int offset = entryOffsetMap.get(i);
if (offset == -1) {
continue;
}
offsetsToEntryData.put(offset, readEntryData());
}
for (int i = 0; i < entryOffsets.length; i++) {
if (entryOffsets[i] != -1) {
mMissingResSpecs[i] = false;
mResId = (mResId & 0xffff0000) | i;
EntryData entryData = offsetsToEntryData.get(entryOffsets[i]);
readEntry(entryData);
}
mMissingResSpecMap.put(i, false);
mResId = (mResId & 0xffff0000) | i;
readEntry(readEntryData());
}
return mType;
@ -384,8 +384,8 @@ public class ARSCDecoder {
}
private ResIntBasedValue readValue() throws IOException, AndrolibException {
/* size */mIn.skipCheckShort((short) 8);
/* zero */mIn.skipCheckByte((byte) 0);
/* size */mIn.skipCheckShort((short) 8);
/* zero */mIn.skipCheckByte((byte) 0);
byte type = mIn.readByte();
int data = mIn.readInt();
@ -418,13 +418,13 @@ public class ARSCDecoder {
byte keyboard = mIn.readByte();
byte navigation = mIn.readByte();
byte inputFlags = mIn.readByte();
/* inputPad0 */mIn.skipBytes(1);
/* inputPad0 */mIn.skipBytes(1);
short screenWidth = mIn.readShort();
short screenHeight = mIn.readShort();
short sdkVersion = mIn.readShort();
/* minorVersion, now must always be 0 */mIn.skipBytes(2);
/* minorVersion, now must always be 0 */mIn.skipBytes(2);
byte screenLayout = 0;
byte uiMode = 0;
@ -533,14 +533,12 @@ public class ARSCDecoder {
private void addMissingResSpecs() throws AndrolibException {
int resId = mResId & 0xffff0000;
for (int i = 0; i < mMissingResSpecs.length; i++) {
if (!mMissingResSpecs[i]) {
continue;
}
for (int i : mMissingResSpecMap.keySet()) {
if (mMissingResSpecMap.get(i)) continue;
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))) {
mPkg.addResSpec(spec);
mTypeSpec.addResSpec(spec);
@ -598,7 +596,7 @@ public class ARSCDecoder {
private ResType mType;
private int mResId;
private int mTypeIdOffset = 0;
private boolean[] mMissingResSpecs;
private HashMap<Integer, Boolean> mMissingResSpecMap;
private final HashMap<Integer, ResTypeSpec> mResTypeSpecs = new HashMap<>();
private final static short ENTRY_FLAG_COMPLEX = 0x0001;
@ -685,7 +683,7 @@ public class ARSCDecoder {
throw new AndrolibException("Arsc file contains zero packages");
} else if (mPackages.length != 1) {
int id = findPackageWithMostResSpecs();
LOGGER.fine("Arsc file contains multiple packages. Using package "
LOGGER.fine("Arsc file contains multiple packages. Using package "
+ mPackages[id].getName() + " as default.");
return mPackages[id];
@ -714,4 +712,4 @@ public class ARSCDecoder {
private final FlagsOffset[] mFlagsOffsets;
private final ResTable mResTable;
}
}
}

View File

@ -336,16 +336,26 @@ public class AXmlResourceParser implements XmlResourceParser {
}
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
// to match the resource id to the name. ex: 0x101021C = versionName
if (value.length() == 0 || android_ns.equals(getAttributeNamespace(index))) {
// If attribute name is lacking or a private namespace emerges,
// retrieve the exact attribute name by its id.
if (value == null || value.length() == 0) {
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;
}
@ -381,11 +391,27 @@ public class AXmlResourceParser implements XmlResourceParser {
if (mAttrDecoder != null) {
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(
valueType,
valueData,
valueRaw == -1 ? null : ResXmlEncoders.escapeXmlChars(m_strings.getString(valueRaw)),
getAttributeNameResource(index));
valueType,
valueData,
value,
getAttributeNameResource(index)
);
} catch (AndrolibException ex) {
setFirstError(ex);
LOGGER.log(Level.WARNING, String.format("Could not decode attr value, using undecoded value "
@ -807,9 +833,9 @@ public class AXmlResourceParser implements XmlResourceParser {
if (m_strings == null) {
m_reader.skipCheckInt(CHUNK_AXML_FILE, CHUNK_AXML_FILE_BROKEN);
/*
* chunkSize
*/
/*
* chunkSize
*/
m_reader.skipInt();
m_strings = StringBlock.read(m_reader);
m_namespaces.increaseDepth();
@ -863,9 +889,9 @@ public class AXmlResourceParser implements XmlResourceParser {
}
// Common header.
/* chunkSize */m_reader.skipInt();
/* chunkSize */m_reader.skipInt();
int lineNumber = m_reader.readInt();
/* 0xFFFFFFFF */m_reader.skipInt();
/* 0xFFFFFFFF */m_reader.skipInt();
if (chunkType == CHUNK_XML_START_NAMESPACE || chunkType == CHUNK_XML_END_NAMESPACE) {
if (chunkType == CHUNK_XML_START_NAMESPACE) {
@ -873,8 +899,8 @@ public class AXmlResourceParser implements XmlResourceParser {
int uri = m_reader.readInt();
m_namespaces.push(prefix, uri);
} else {
/* prefix */m_reader.skipInt();
/* uri */m_reader.skipInt();
/* prefix */m_reader.skipInt();
/* uri */m_reader.skipInt();
m_namespaces.pop();
}
continue;
@ -885,7 +911,7 @@ public class AXmlResourceParser implements XmlResourceParser {
if (chunkType == CHUNK_XML_START_TAG) {
m_namespaceUri = m_reader.readInt();
m_name = m_reader.readInt();
/* flags? */m_reader.skipInt();
/* flags? */m_reader.skipInt();
int attributeCount = m_reader.readInt();
m_idAttribute = (attributeCount >>> 16) - 1;
attributeCount &= 0xFFFF;
@ -912,8 +938,8 @@ public class AXmlResourceParser implements XmlResourceParser {
if (chunkType == CHUNK_XML_TEXT) {
m_name = m_reader.readInt();
/* ? */m_reader.skipInt();
/* ? */m_reader.skipInt();
/* ? */m_reader.skipInt();
/* ? */m_reader.skipInt();
m_event = TEXT;
break;
}
@ -927,10 +953,10 @@ public class AXmlResourceParser implements XmlResourceParser {
}
// ///////////////////////////////// data
/*
* All values are essentially indices, e.g. m_name is an index of name in
* m_strings.
*/
/*
* All values are essentially indices, e.g. m_name is an index of name in
* m_strings.
*/
private ExtDataInput m_reader;
private ResAttrDecoder mAttrDecoder;
private AndrolibException mFirstError;

View File

@ -37,6 +37,9 @@ public class AndroidManifestResourceParser extends AXmlResourceParser {
@Override
public String getAttributeValue(int index) {
String value = super.getAttributeValue(index);
if (value == null) {
return "";
}
if (!isNumericStringMetadataAttributeValue(index, value)) {
return value;
@ -46,7 +49,7 @@ public class AndroidManifestResourceParser extends AXmlResourceParser {
// 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
// app functionality.
return "\\ " + super.getAttributeValue(index).trim();
return "\\ " + value.trim();
}
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.err.UndefinedResObjectException;
import brut.androlib.res.data.ResID;
import brut.androlib.res.data.ResPackage;
import brut.androlib.res.data.ResResSpec;
import brut.androlib.res.data.value.ResAttr;
@ -48,10 +49,24 @@ public class ResAttrDecoder {
throws AndrolibException {
if (attrResId != 0) {
ResResSpec resResSpec = getCurrentPackage().getResTable().getResSpec(attrResId);
int attrId = attrResId;
if (resResSpec != null) {
return resResSpec.getName();
// 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) {
return resResSpec.getName();
}
}
}

View File

@ -129,6 +129,10 @@ public final class ResXmlPatcher {
* Creates a modified network security config file that is more permissive
*
* @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)
throws ParserConfigurationException, TransformerException, IOException, SAXException {
@ -184,13 +188,10 @@ public final class ResXmlPatcher {
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
NamedNodeMap attrs = node.getAttributes();
Node provider = attrs.getNamedItem("android:authorities");
if (attrs != null) {
Node provider = attrs.getNamedItem("android:authorities");
if (provider != null) {
saved = isSaved(file, saved, provider);
}
if (provider != null) {
saved = isSaved(file, saved, provider);
}
}
@ -204,13 +205,10 @@ public final class ResXmlPatcher {
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
NamedNodeMap attrs = node.getAttributes();
Node provider = attrs.getNamedItem("android:scheme");
if (attrs != null) {
Node provider = attrs.getNamedItem("android:scheme");
if (provider != null) {
saved = isSaved(file, saved, provider);
}
if (provider != null) {
saved = isSaved(file, saved, provider);
}
}

View File

@ -6,7 +6,7 @@ chcp 65001 2>nul >nul
set java_exe=java.exe
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
@ -36,7 +36,7 @@ if "%ATTR:~0,1%"=="-" if "%~x1"==".apk" (
)
: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
for /f "tokens=2" %%# in ("%cmdcmdline%") do if /i "%%#" equ "/c" pause