Skip to content

Commit

Permalink
Patch APKTool to allow repeated entry offsets to appear
Browse files Browse the repository at this point in the history
  • Loading branch information
Nic Allen committed Dec 7, 2017
1 parent 2348683 commit 88eed24
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 13 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Expand Up @@ -15,6 +15,7 @@ bin/
# Tmp Files
*.kate-swp
*~
*.DS_Store

# IntelliJ
*.iml
Expand All @@ -23,3 +24,6 @@ bin/

# gradle/smali-patches patch smali/ into brut.apktool.smali/
brut.apktool.smali/

# Patches
*.patch
9 changes: 9 additions & 0 deletions brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java
Expand Up @@ -127,6 +127,9 @@ private static void cmdDecode(CommandLine cli) throws AndrolibException {
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);
}
Expand Down Expand Up @@ -294,6 +297,11 @@ private static void _Options() {
.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.")
Expand Down Expand Up @@ -431,6 +439,7 @@ private static void _Options() {
DecodeOptions.addOption(forceDecOption);
DecodeOptions.addOption(noSrcOption);
DecodeOptions.addOption(noResOption);
DecodeOptions.addOption(manifestOnlyOption);

// add basic build options
BuildOptions.addOption(outputBuiOption);
Expand Down
Expand Up @@ -100,6 +100,13 @@ public void decode() throws AndrolibException, IOException, DirectoryException {

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:
Expand All @@ -125,7 +132,7 @@ public void decode() throws AndrolibException, IOException, DirectoryException {
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
Expand Down Expand Up @@ -242,6 +249,10 @@ public void setForceDelete(boolean forceDelete) {
mForceDelete = forceDelete;
}

public void setManifestOnly(boolean manifestOnly) {
mManifestOnly = manifestOnly;
}

public void setFrameworkTag(String tag) throws AndrolibException {
mAndrolib.apkOptions.frameworkTag = tag;
}
Expand Down Expand Up @@ -416,7 +427,7 @@ private void putUnknownInfo(MetaInfo meta) throws AndrolibException {
}

private void putFileCompressionInfo(MetaInfo meta) throws AndrolibException {
if (!mUncompressedFiles.isEmpty()) {
if (mUncompressedFiles != null && !mUncompressedFiles.isEmpty()) {
meta.doNotCompress = mUncompressedFiles;
}
}
Expand All @@ -437,6 +448,7 @@ private void putSharedLibraryInfo(MetaInfo meta) throws AndrolibException {
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;
Expand Down
Expand Up @@ -154,12 +154,12 @@ private ResTypeSpec readTableTypeSpec() throws AndrolibException, IOException {

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();
Expand Down Expand Up @@ -223,33 +223,55 @@ private ResType readTableType() throws IOException, AndrolibException {
}

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());
Expand Down

0 comments on commit 88eed24

Please sign in to comment.