Skip to content

Commit

Permalink
fix: support skipping unread header sizes of ResChunk (#3180)
Browse files Browse the repository at this point in the history
* fix: support skipping unread header sizes of ResChunk

* refactor: note that header skip happens too late on some

* refactor: check for chunk header end at each of each header

* chore: skip reading header on string pools

* fix: move header check prior to reading entries on tables
  • Loading branch information
iBotPeaches committed Jul 23, 2023
1 parent c7bb163 commit 79f57b0
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

import java.io.EOFException;
import java.io.IOException;
import java.math.BigInteger;
import java.util.logging.Logger;

public class ARSCHeader {
public final short type;
Expand Down Expand Up @@ -48,6 +50,30 @@ public static ARSCHeader read(ExtDataInput in, CountingInputStream countIn) thro
return new ARSCHeader(type, in.readShort(), in.readInt(), start);
}

public void checkForUnreadHeader(ExtDataInput in, CountingInputStream countIn) throws IOException {
// Some applications lie about the reported size of their chunk header. Trusting the chunkSize is misleading
// So compare to what we actually read in the header vs reported and skip the rest.
// However, this runs after each chunk and not every chunk reading has a specific distinction between the
// header and the body.
int actualHeaderSize = countIn.getCount() - this.startPosition;
int exceedingSize = this.headerSize - actualHeaderSize;
if (exceedingSize > 0) {
byte[] buf = new byte[exceedingSize];
in.readFully(buf);
BigInteger exceedingBI = new BigInteger(1, buf);

if (exceedingBI.equals(BigInteger.ZERO)) {
LOGGER.fine(String.format("Chunk header size (%d), read (%d), but exceeding bytes are all zero.",
this.headerSize, actualHeaderSize
));
} else {
LOGGER.warning(String.format("Chunk header size (%d), read (%d). Exceeding bytes: 0x%X.",
this.headerSize, actualHeaderSize, exceedingBI
));
}
}
}

public void skipChunk(ExtDataInput in) throws IOException {
in.skipBytes(chunkSize - headerSize);
}
Expand Down Expand Up @@ -76,4 +102,6 @@ public void skipChunk(ExtDataInput in) throws IOException {
public final static short RES_XML_CDATA_TYPE = 0x0104;
public final static short RES_XML_LAST_CHUNK_TYPE = 0x017f;
public final static short RES_XML_RESOURCE_MAP_TYPE = 0x0180;

private static final Logger LOGGER = Logger.getLogger(ARSCHeader.class.getName());
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,15 @@ private void readStringPoolChunk() throws IOException, AndrolibException {
private void readTableChunk() throws IOException, AndrolibException {
checkChunkType(ARSCHeader.RES_TABLE_TYPE);
mIn.skipInt(); // packageCount

mHeader.checkForUnreadHeader(mIn, mCountIn);
}

private void readUnknownChunk() throws IOException, AndrolibException {
checkChunkType(ARSCHeader.RES_NULL_TYPE);

mHeader.checkForUnreadHeader(mIn, mCountIn);

LOGGER.warning("Skipping unknown chunk data of size " + mHeader.chunkSize);
mHeader.skipChunk(mIn);
}
Expand Down Expand Up @@ -178,6 +182,8 @@ private ResPackage readTablePackage() throws IOException, AndrolibException {
LOGGER.warning("Please report this application to Apktool for a fix: https://github.com/iBotPeaches/Apktool/issues/1728");
}

mHeader.checkForUnreadHeader(mIn, mCountIn);

mTypeNames = StringBlock.readWithChunk(mIn);
mSpecNames = StringBlock.readWithChunk(mIn);

Expand All @@ -194,6 +200,8 @@ private void readLibraryType() throws AndrolibException, IOException {
int packageId;
String packageName;

mHeader.checkForUnreadHeader(mIn, mCountIn);

for (int i = 0; i < libraryCount; i++) {
packageId = mIn.readInt();
packageName = mIn.readNullEndedString(128, true);
Expand All @@ -204,6 +212,8 @@ private void readLibraryType() throws AndrolibException, IOException {
private void readStagedAliasSpec() throws IOException {
int count = mIn.readInt();

mHeader.checkForUnreadHeader(mIn, mCountIn);

for (int i = 0; i < count; i++) {
LOGGER.fine(String.format("Skipping staged alias stagedId (%h) finalId: %h", mIn.readInt(), mIn.readInt()));
}
Expand All @@ -213,6 +223,9 @@ private void readOverlaySpec() throws AndrolibException, IOException {
checkChunkType(ARSCHeader.XML_TYPE_OVERLAY);
String name = mIn.readNullEndedString(256, true);
String actor = mIn.readNullEndedString(256, true);

mHeader.checkForUnreadHeader(mIn, mCountIn);

LOGGER.fine(String.format("Overlay name: \"%s\", actor: \"%s\")", name, actor));
}

Expand All @@ -221,6 +234,8 @@ private void readOverlayPolicySpec() throws AndrolibException, IOException {
mIn.skipInt(); // policyFlags
int count = mIn.readInt();

mHeader.checkForUnreadHeader(mIn, mCountIn);

for (int i = 0; i < count; i++) {
LOGGER.fine(String.format("Skipping overlay (%h)", mIn.readInt()));
}
Expand All @@ -237,7 +252,9 @@ private ResTypeSpec readTableSpecType() throws AndrolibException, IOException {
mFlagsOffsets.add(new FlagsOffset(mCountIn.getCount(), entryCount));
}

mIn.skipBytes(entryCount * 4); // flags
mHeader.checkForUnreadHeader(mIn, mCountIn);

mIn.skipBytes(entryCount * 4); // flags
mTypeSpec = new ResTypeSpec(mTypeNames.getString(id - 1), mResTable, mPkg, id, entryCount);
mPkg.addType(mTypeSpec);

Expand All @@ -255,18 +272,12 @@ private ResType readTableType() throws IOException, AndrolibException {
int typeFlags = mIn.readByte();
mIn.skipBytes(2); // reserved
int entryCount = mIn.readInt();
int entriesStart = mIn.readInt();
mMissingResSpecMap = new LinkedHashMap<>();
mIn.skipInt(); // entriesStart

mMissingResSpecMap = new LinkedHashMap<>();
ResConfigFlags flags = readConfigFlags();
int position = (mHeader.startPosition + entriesStart) - (entryCount * 4);

// For some APKs there is a disconnect between the reported size of Configs
// If we find a mismatch skip those bytes.
if (position != mCountIn.getCount()) {
LOGGER.warning("Invalid data detected. Skipping: " + (position - mCountIn.getCount()) + " byte(s)");
mIn.skipBytes(position - mCountIn.getCount());
}
mHeader.checkForUnreadHeader(mIn, mCountIn);

if ((typeFlags & 0x01) != 0) {
LOGGER.fine("Sparse type flags detected: " + mTypeSpec.getName());
Expand Down

0 comments on commit 79f57b0

Please sign in to comment.