feat: merge iBotPeaches/Apktool
This commit is contained in:
commit
9443f6fefb
|
@ -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
|
|
@ -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
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
11
ROADMAP.md
11
ROADMAP.md
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -104,13 +104,13 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
@ -159,7 +160,7 @@ public class ARSCDecoder {
|
||||||
for (int i = 0; i < libraryCount; i++) {
|
for (int i = 0; i < libraryCount; i++) {
|
||||||
packageId = mIn.readInt();
|
packageId = mIn.readInt();
|
||||||
packageName = mIn.readNullEndedString(128, true);
|
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) {
|
while(nextChunk().type == Header.XML_TYPE_TYPE) {
|
||||||
|
@ -232,7 +233,7 @@ public class ARSCDecoder {
|
||||||
mFlagsOffsets.add(new FlagsOffset(mCountIn.getCount(), entryCount));
|
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);
|
mTypeSpec = new ResTypeSpec(mTypeNames.getString(id - 1), mResTable, mPkg, id, entryCount);
|
||||||
mPkg.addType(mTypeSpec);
|
mPkg.addType(mTypeSpec);
|
||||||
return mTypeSpec;
|
return mTypeSpec;
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
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());
|
mResId = (mResId & 0xffff0000) | i;
|
||||||
}
|
readEntry(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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return mType;
|
return mType;
|
||||||
|
@ -384,8 +384,8 @@ public class ARSCDecoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResIntBasedValue readValue() throws IOException, AndrolibException {
|
private ResIntBasedValue readValue() throws IOException, AndrolibException {
|
||||||
/* size */mIn.skipCheckShort((short) 8);
|
/* size */mIn.skipCheckShort((short) 8);
|
||||||
/* zero */mIn.skipCheckByte((byte) 0);
|
/* zero */mIn.skipCheckByte((byte) 0);
|
||||||
byte type = mIn.readByte();
|
byte type = mIn.readByte();
|
||||||
int data = mIn.readInt();
|
int data = mIn.readInt();
|
||||||
|
|
||||||
|
@ -418,13 +418,13 @@ public class ARSCDecoder {
|
||||||
byte keyboard = mIn.readByte();
|
byte keyboard = mIn.readByte();
|
||||||
byte navigation = mIn.readByte();
|
byte navigation = mIn.readByte();
|
||||||
byte inputFlags = mIn.readByte();
|
byte inputFlags = mIn.readByte();
|
||||||
/* inputPad0 */mIn.skipBytes(1);
|
/* inputPad0 */mIn.skipBytes(1);
|
||||||
|
|
||||||
short screenWidth = mIn.readShort();
|
short screenWidth = mIn.readShort();
|
||||||
short screenHeight = mIn.readShort();
|
short screenHeight = mIn.readShort();
|
||||||
|
|
||||||
short sdkVersion = 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 screenLayout = 0;
|
||||||
byte uiMode = 0;
|
byte uiMode = 0;
|
||||||
|
@ -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;
|
||||||
|
@ -685,7 +683,7 @@ public class ARSCDecoder {
|
||||||
throw new AndrolibException("Arsc file contains zero packages");
|
throw new AndrolibException("Arsc file contains zero packages");
|
||||||
} else if (mPackages.length != 1) {
|
} else if (mPackages.length != 1) {
|
||||||
int id = findPackageWithMostResSpecs();
|
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.");
|
+ mPackages[id].getName() + " as default.");
|
||||||
|
|
||||||
return mPackages[id];
|
return mPackages[id];
|
||||||
|
@ -714,4 +712,4 @@ public class ARSCDecoder {
|
||||||
private final FlagsOffset[] mFlagsOffsets;
|
private final FlagsOffset[] mFlagsOffsets;
|
||||||
private final ResTable mResTable;
|
private final ResTable mResTable;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
value = mAttrDecoder.decodeManifestAttr(getAttributeNameResource(index));
|
||||||
if (resourceId != 0) {
|
if (value == null) {
|
||||||
value = mAttrDecoder.decodeManifestAttr(getAttributeNameResource(index));
|
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 "
|
||||||
|
@ -807,9 +833,9 @@ public class AXmlResourceParser implements XmlResourceParser {
|
||||||
if (m_strings == null) {
|
if (m_strings == null) {
|
||||||
m_reader.skipCheckInt(CHUNK_AXML_FILE, CHUNK_AXML_FILE_BROKEN);
|
m_reader.skipCheckInt(CHUNK_AXML_FILE, CHUNK_AXML_FILE_BROKEN);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* chunkSize
|
* chunkSize
|
||||||
*/
|
*/
|
||||||
m_reader.skipInt();
|
m_reader.skipInt();
|
||||||
m_strings = StringBlock.read(m_reader);
|
m_strings = StringBlock.read(m_reader);
|
||||||
m_namespaces.increaseDepth();
|
m_namespaces.increaseDepth();
|
||||||
|
@ -863,9 +889,9 @@ public class AXmlResourceParser implements XmlResourceParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common header.
|
// Common header.
|
||||||
/* chunkSize */m_reader.skipInt();
|
/* chunkSize */m_reader.skipInt();
|
||||||
int lineNumber = m_reader.readInt();
|
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 || chunkType == CHUNK_XML_END_NAMESPACE) {
|
||||||
if (chunkType == CHUNK_XML_START_NAMESPACE) {
|
if (chunkType == CHUNK_XML_START_NAMESPACE) {
|
||||||
|
@ -873,8 +899,8 @@ public class AXmlResourceParser implements XmlResourceParser {
|
||||||
int uri = m_reader.readInt();
|
int uri = m_reader.readInt();
|
||||||
m_namespaces.push(prefix, uri);
|
m_namespaces.push(prefix, uri);
|
||||||
} else {
|
} else {
|
||||||
/* prefix */m_reader.skipInt();
|
/* prefix */m_reader.skipInt();
|
||||||
/* uri */m_reader.skipInt();
|
/* uri */m_reader.skipInt();
|
||||||
m_namespaces.pop();
|
m_namespaces.pop();
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@ -885,7 +911,7 @@ public class AXmlResourceParser implements XmlResourceParser {
|
||||||
if (chunkType == CHUNK_XML_START_TAG) {
|
if (chunkType == CHUNK_XML_START_TAG) {
|
||||||
m_namespaceUri = m_reader.readInt();
|
m_namespaceUri = m_reader.readInt();
|
||||||
m_name = m_reader.readInt();
|
m_name = m_reader.readInt();
|
||||||
/* flags? */m_reader.skipInt();
|
/* flags? */m_reader.skipInt();
|
||||||
int attributeCount = m_reader.readInt();
|
int attributeCount = m_reader.readInt();
|
||||||
m_idAttribute = (attributeCount >>> 16) - 1;
|
m_idAttribute = (attributeCount >>> 16) - 1;
|
||||||
attributeCount &= 0xFFFF;
|
attributeCount &= 0xFFFF;
|
||||||
|
@ -912,8 +938,8 @@ public class AXmlResourceParser implements XmlResourceParser {
|
||||||
|
|
||||||
if (chunkType == CHUNK_XML_TEXT) {
|
if (chunkType == CHUNK_XML_TEXT) {
|
||||||
m_name = m_reader.readInt();
|
m_name = m_reader.readInt();
|
||||||
/* ? */m_reader.skipInt();
|
/* ? */m_reader.skipInt();
|
||||||
/* ? */m_reader.skipInt();
|
/* ? */m_reader.skipInt();
|
||||||
m_event = TEXT;
|
m_event = TEXT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -927,10 +953,10 @@ public class AXmlResourceParser implements XmlResourceParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ///////////////////////////////// data
|
// ///////////////////////////////// data
|
||||||
/*
|
/*
|
||||||
* All values are essentially indices, e.g. m_name is an index of name in
|
* All values are essentially indices, e.g. m_name is an index of name in
|
||||||
* m_strings.
|
* m_strings.
|
||||||
*/
|
*/
|
||||||
private ExtDataInput m_reader;
|
private ExtDataInput m_reader;
|
||||||
private ResAttrDecoder mAttrDecoder;
|
private ResAttrDecoder mAttrDecoder;
|
||||||
private AndrolibException mFirstError;
|
private AndrolibException mFirstError;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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,10 +49,24 @@ public class ResAttrDecoder {
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
|
|
||||||
if (attrResId != 0) {
|
if (attrResId != 0) {
|
||||||
ResResSpec resResSpec = getCurrentPackage().getResTable().getResSpec(attrResId);
|
int attrId = attrResId;
|
||||||
|
|
||||||
if (resResSpec != null) {
|
// See also: brut.androlib.res.data.ResTable.getResSpec
|
||||||
return resResSpec.getName();
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,13 +188,10 @@ 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();
|
||||||
|
Node provider = attrs.getNamedItem("android:authorities");
|
||||||
|
|
||||||
if (attrs != null) {
|
if (provider != null) {
|
||||||
Node provider = attrs.getNamedItem("android:authorities");
|
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++) {
|
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();
|
||||||
|
Node provider = attrs.getNamedItem("android:scheme");
|
||||||
|
|
||||||
if (attrs != null) {
|
if (provider != null) {
|
||||||
Node provider = attrs.getNamedItem("android:scheme");
|
saved = isSaved(file, saved, provider);
|
||||||
|
|
||||||
if (provider != null) {
|
|
||||||
saved = isSaved(file, saved, provider);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue