Patch APKTool to allow repeated entry offsets to appear

This commit is contained in:
Nic Allen 2017-10-20 14:10:32 -07:00
parent 23486830a9
commit 88eed24625
4 changed files with 60 additions and 13 deletions

4
.gitignore vendored
View File

@ -15,6 +15,7 @@ bin/
# Tmp Files
*.kate-swp
*~
*.DS_Store
# IntelliJ
*.iml
@ -23,3 +24,6 @@ bin/
# gradle/smali-patches patch smali/ into brut.apktool.smali/
brut.apktool.smali/
# Patches
*.patch

View File

@ -127,6 +127,9 @@ public class Main {
if (cli.hasOption("force-manifest")) {
decoder.setForceDecodeManifest(ApkDecoder.FORCE_DECODE_MANIFEST_FULL);
}
if (cli.hasOption("manifest-only")) {
decoder.setManifestOnly(true);
}
if (cli.hasOption("no-assets")) {
decoder.setDecodeAssets(ApkDecoder.DECODE_ASSETS_NONE);
}
@ -294,6 +297,11 @@ public class Main {
.desc("Decode the APK's compiled manifest, even if decoding of resources is set to \"false\".")
.build();
Option manifestOnlyOption = Option.builder("mo")
.longOpt("manifest-only")
.desc("Only decode manifest.")
.build();
Option noAssetOption = Option.builder()
.longOpt("no-assets")
.desc("Do not decode assets.")
@ -431,6 +439,7 @@ public class Main {
DecodeOptions.addOption(forceDecOption);
DecodeOptions.addOption(noSrcOption);
DecodeOptions.addOption(noResOption);
DecodeOptions.addOption(manifestOnlyOption);
// add basic build options
BuildOptions.addOption(outputBuiOption);

View File

@ -100,6 +100,13 @@ public class ApkDecoder {
LOGGER.info("Using Apktool " + Androlib.getVersion() + " on " + mApkFile.getName());
if (mManifestOnly) {
setAnalysisMode(mAnalysisMode, true);
mAndrolib.decodeManifestWithResources(mApkFile, outDir, getResTable());
writeMetaFile();
return;
}
if (hasResources()) {
switch (mDecodeResources) {
case DECODE_RESOURCES_NONE:
@ -125,7 +132,7 @@ public class ApkDecoder {
break;
}
} else {
// if there's no resources.asrc, decode the manifest without looking
// if there's no resources.arsc, decode the manifest without looking
// up attribute references
if (hasManifest()) {
if (mDecodeResources == DECODE_RESOURCES_FULL
@ -242,6 +249,10 @@ public class ApkDecoder {
mForceDelete = forceDelete;
}
public void setManifestOnly(boolean manifestOnly) {
mManifestOnly = manifestOnly;
}
public void setFrameworkTag(String tag) throws AndrolibException {
mAndrolib.apkOptions.frameworkTag = tag;
}
@ -416,7 +427,7 @@ public class ApkDecoder {
}
private void putFileCompressionInfo(MetaInfo meta) throws AndrolibException {
if (!mUncompressedFiles.isEmpty()) {
if (mUncompressedFiles != null && !mUncompressedFiles.isEmpty()) {
meta.doNotCompress = mUncompressedFiles;
}
}
@ -437,6 +448,7 @@ public class ApkDecoder {
private short mForceDecodeManifest = FORCE_DECODE_MANIFEST_NONE;
private short mDecodeAssets = DECODE_ASSETS_FULL;
private boolean mForceDelete = false;
private boolean mManifestOnly = false;
private boolean mKeepBrokenResources = false;
private boolean mBakDeb = true;
private Collection<String> mUncompressedFiles;

View File

@ -154,12 +154,12 @@ public class ARSCDecoder {
while (type == Header.TYPE_TYPE) {
readTableType();
// skip "TYPE 8 chunks" and/or padding data at the end of this chunk
if (mCountIn.getCount() < mHeader.endPosition) {
mCountIn.skip(mHeader.endPosition - mCountIn.getCount());
}
type = nextChunk().type;
addMissingResSpecs();
@ -223,33 +223,55 @@ public class ARSCDecoder {
}
mType = flags.isInvalid && !mKeepBroken ? null : mPkg.getOrCreateConfig(flags);
HashMap<Integer, EntryData> offsetsToEntryData =
new HashMap<Integer, EntryData>();
for (int offset : entryOffsets) {
if (offset == -1 || offsetsToEntryData.containsKey(offset)) {
continue;
}
offsetsToEntryData.put(offset, readEntryData());
}
for (int i = 0; i < entryOffsets.length; i++) {
if (entryOffsets[i] != -1) {
mMissingResSpecs[i] = false;
mResId = (mResId & 0xffff0000) | i;
readEntry();
EntryData entryData = offsetsToEntryData.get(entryOffsets[i]);
readEntry(entryData);
}
}
return mType;
}
private void readEntry() throws IOException, AndrolibException {
private class EntryData {
public short mFlags;
public int mSpecNamesId;
public ResValue mValue;
}
private EntryData readEntryData() throws IOException, AndrolibException {
short size = mIn.readShort();
if (size < 0) {
throw new AndrolibException("Entry size is under 0 bytes.");
}
short flags = mIn.readShort();
int specNamesId = mIn.readInt();
// If we are here, we probably already inserted any remaining dummy resources. No need to parse
// any resources that doesn't have type information
if (mCountIn.getCount() == mHeader.endPosition) {
return;
}
ResValue value = (flags & ENTRY_FLAG_COMPLEX) == 0 ? readValue() : readComplexEntry();
EntryData entryData = new EntryData();
entryData.mFlags = flags;
entryData.mSpecNamesId = specNamesId;
entryData.mValue = value;
return entryData;
}
private void readEntry(EntryData entryData) throws AndrolibException {
short flags = entryData.mFlags;
int specNamesId = entryData.mSpecNamesId;
ResValue value = entryData.mValue;
if (mTypeSpec.isString() && value instanceof ResFileValue) {
value = new ResStringValue(value.toString(), ((ResFileValue) value).getRawIntValue());