Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ARSC/AXML Parser Rework #3131

Merged
merged 14 commits into from
Jul 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -190,19 +190,20 @@ private void adjustPackageManifest(ResTable resTable, String filePath)
// compare resources.arsc package name to the one present in AndroidManifest
ResPackage resPackage = resTable.getCurrentResPackage();
String pkgOriginal = resPackage.getName();
String packageRenamed = resTable.getPackageRenamed();
String pkgRenamed = resTable.getPackageRenamed();

resTable.setPackageId(resPackage.getId());
resTable.setPackageOriginal(pkgOriginal);

// 1) Check if pkgOriginal is null (empty resources.arsc)
// 2) Check if pkgOriginal === mPackageRenamed
// 3) Check if pkgOriginal is ignored via IGNORED_PACKAGES
if (pkgOriginal == null || pkgOriginal.equalsIgnoreCase(packageRenamed)
// 2) Check if pkgRenamed is null
// 3) Check if pkgOriginal === mPackageRenamed
// 4) Check if pkgOriginal is ignored via IGNORED_PACKAGES
if (pkgOriginal == null || pkgRenamed == null || pkgOriginal.equalsIgnoreCase(pkgRenamed)
|| (Arrays.asList(IGNORED_PACKAGES).contains(pkgOriginal))) {
LOGGER.info("Regular manifest package...");
} else {
LOGGER.info("Renamed manifest package found! Replacing " + packageRenamed + " with " + pkgOriginal);
LOGGER.info("Renamed manifest package found! Replacing " + pkgRenamed + " with " + pkgOriginal);
ResXmlPatcher.renameManifestPackage(new File(filePath), pkgOriginal);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ private void initPackageInfo() throws AndrolibException {
}

// only put rename-manifest-package into apktool.yml, if the change will be required
if (!renamed.equalsIgnoreCase(original)) {
if (renamed != null && !renamed.equalsIgnoreCase(original)) {
mApkInfo.packageInfo.renameManifestPackage = renamed;
}
mApkInfo.packageInfo.forcedPackageId = String.valueOf(id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,37 @@ public static ARSCHeader read(ExtDataInput in, CountingInputStream countIn) thro
try {
type = in.readShort();
} catch (EOFException ex) {
return new ARSCHeader(TYPE_NONE, 0, 0, countIn.getCount());
return new ARSCHeader(RES_NONE_TYPE, 0, 0, countIn.getCount());
}
return new ARSCHeader(type, in.readShort(), in.readInt(), start);
}

public final static short TYPE_NONE = -1;
public final static short TYPE_STRING_POOL = 0x0001;
public final static short TYPE_TABLE = 0x0002;
public final static short TYPE_XML = 0x0003;
public void skipChunk(ExtDataInput in) throws IOException {
in.skipBytes(chunkSize - headerSize);
}

public final static short RES_NONE_TYPE = -1;
public final static short RES_NULL_TYPE = 0x0000;
public final static short RES_STRING_POOL_TYPE = 0x0001;
public final static short RES_TABLE_TYPE = 0x0002;
public final static short RES_XML_TYPE = 0x0003;

// RES_TABLE_TYPE Chunks
public final static short XML_TYPE_PACKAGE = 0x0200;
public final static short XML_TYPE_TYPE = 0x0201;
public final static short XML_TYPE_SPEC_TYPE = 0x0202;
public final static short XML_TYPE_LIBRARY = 0x0203;
public final static short XML_TYPE_OVERLAY = 0x0204;
public final static short XML_TYPE_OVERLAY_POLICY = 0x0205;
public final static short XML_TYPE_STAGED_ALIAS = 0x0206;

// RES_XML_TYPE Chunks
public final static short RES_XML_FIRST_CHUNK_TYPE = 0x0100;
public final static short RES_XML_START_NAMESPACE_TYPE = 0x0100;
public final static short RES_XML_END_NAMESPACE_TYPE = 0x0101;
public final static short RES_XML_START_ELEMENT_TYPE = 0x0102;
public final static short RES_XML_END_ELEMENT_TYPE = 0x0103;
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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.*;
import java.util.logging.Logger;

public class ARSCDecoder {
Expand All @@ -50,7 +47,7 @@ public static ARSCData decode(InputStream arscStream, boolean findFlagsOffsets,
throws AndrolibException {
try {
ARSCDecoder decoder = new ARSCDecoder(arscStream, resTable, findFlagsOffsets, keepBroken);
ResPackage[] pkgs = decoder.readTableHeader();
ResPackage[] pkgs = decoder.readResourceTable();
return new ARSCData(pkgs, decoder.mFlagsOffsets == null
? null
: decoder.mFlagsOffsets.toArray(new FlagsOffset[0]));
Expand All @@ -74,19 +71,85 @@ private ARSCDecoder(InputStream arscStream, ResTable resTable, boolean storeFlag
mKeepBroken = keepBroken;
}

private ResPackage[] readTableHeader() throws IOException, AndrolibException {
nextChunkCheckType(ARSCHeader.TYPE_TABLE);
int packageCount = mIn.readInt();
private ResPackage[] readResourceTable() throws IOException, AndrolibException {
Set<ResPackage> pkgs = new LinkedHashSet<>();
ResTypeSpec typeSpec;

mTableStrings = StringBlock.read(mIn);
ResPackage[] packages = new ResPackage[packageCount];
chunkLoop:
for (;;) {
nextChunk();

switch (mHeader.type) {
case ARSCHeader.RES_NULL_TYPE:
readUnknownChunk();
break;
case ARSCHeader.RES_STRING_POOL_TYPE:
readStringPoolChunk();
break;
case ARSCHeader.RES_TABLE_TYPE:
readTableChunk();
break;

// Chunk types in RES_TABLE_TYPE
case ARSCHeader.XML_TYPE_PACKAGE:
mTypeIdOffset = 0;
pkgs.add(readTablePackage());
break;
case ARSCHeader.XML_TYPE_TYPE:
readTableType();
break;
case ARSCHeader.XML_TYPE_SPEC_TYPE:
typeSpec = readTableSpecType();
addTypeSpec(typeSpec);
break;
case ARSCHeader.XML_TYPE_LIBRARY:
readLibraryType();
break;
case ARSCHeader.XML_TYPE_OVERLAY:
readOverlaySpec();
break;
case ARSCHeader.XML_TYPE_OVERLAY_POLICY:
readOverlayPolicySpec();
break;
case ARSCHeader.XML_TYPE_STAGED_ALIAS:
readStagedAliasSpec();
break;
default:
if (mHeader.type != ARSCHeader.RES_NONE_TYPE) {
LOGGER.severe(String.format("Unknown chunk type: %04x", mHeader.type));
}
break chunkLoop;
}
}

nextChunk();
for (int i = 0; i < packageCount; i++) {
mTypeIdOffset = 0;
packages[i] = readTablePackage();
if (mPkg.getResSpecCount() > 0) {
addMissingResSpecs();
}
return packages;

// We've detected sparse resources, lets record this so we can rebuild in that same format (sparse/not)
// with aapt2. aapt1 will ignore this.
if (! mResTable.getSparseResources()) {
mResTable.setSparseResources(true);
}

return pkgs.toArray(new ResPackage[0]);
}

private void readStringPoolChunk() throws IOException, AndrolibException {
checkChunkType(ARSCHeader.RES_STRING_POOL_TYPE);
mTableStrings = StringBlock.readWithoutChunk(mIn, mHeader.chunkSize);
}

private void readTableChunk() throws IOException, AndrolibException {
checkChunkType(ARSCHeader.RES_TABLE_TYPE);
mIn.skipInt(); // packageCount
}

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

LOGGER.warning("Skipping unknown chunk data of size " + mHeader.chunkSize);
mHeader.skipChunk(mIn);
}

private ResPackage readTablePackage() throws IOException, AndrolibException {
Expand Down Expand Up @@ -121,38 +184,12 @@ private ResPackage readTablePackage() throws IOException, AndrolibException {
LOGGER.warning("Please report this application to Apktool for a fix: https://github.com/iBotPeaches/Apktool/issues/1728");
}

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

mResId = id << 24;
mPkg = new ResPackage(mResTable, id, name);

nextChunk();

chunkLoop:
for (;;) {
switch (mHeader.type) {
case ARSCHeader.XML_TYPE_TYPE:
case ARSCHeader.XML_TYPE_SPEC_TYPE:
readTableTypeSpec();
break;
case ARSCHeader.XML_TYPE_LIBRARY:
readLibraryType();
break;
case ARSCHeader.XML_TYPE_OVERLAY:
readOverlaySpec();
break;
case ARSCHeader.XML_TYPE_OVERLAY_POLICY:
readOverlayPolicySpec();
break;
case ARSCHeader.XML_TYPE_STAGED_ALIAS:
readStagedAliasSpec();
break;
default:
break chunkLoop;
}
}

return mPkg;
}

Expand All @@ -168,8 +205,6 @@ private void readLibraryType() throws AndrolibException, IOException {
packageName = mIn.readNullEndedString(128, true);
LOGGER.info(String.format("Decoding Shared Library (%s), pkgId: %d", packageName, packageId));
}

nextChunk();
}

private void readStagedAliasSpec() throws IOException {
Expand All @@ -178,17 +213,13 @@ private void readStagedAliasSpec() throws IOException {
for (int i = 0; i < count; i++) {
LOGGER.fine(String.format("Skipping staged alias stagedId (%h) finalId: %h", mIn.readInt(), mIn.readInt()));
}

nextChunk();
}

private void readOverlaySpec() throws AndrolibException, IOException {
checkChunkType(ARSCHeader.XML_TYPE_OVERLAY);
String name = mIn.readNullEndedString(256, true);
String actor = mIn.readNullEndedString(256, true);
LOGGER.fine(String.format("Overlay name: \"%s\", actor: \"%s\")", name, actor));

nextChunk();
}

private void readOverlayPolicySpec() throws AndrolibException, IOException {
Expand All @@ -199,48 +230,13 @@ private void readOverlayPolicySpec() throws AndrolibException, IOException {
for (int i = 0; i < count; i++) {
LOGGER.fine(String.format("Skipping overlay (%h)", mIn.readInt()));
}

nextChunk();
}

private void readTableTypeSpec() throws AndrolibException, IOException {
mTypeSpec = readSingleTableTypeSpec();
addTypeSpec(mTypeSpec);

int type = nextChunk().type;
ResTypeSpec resTypeSpec;

while (type == ARSCHeader.XML_TYPE_SPEC_TYPE) {
resTypeSpec = readSingleTableTypeSpec();
addTypeSpec(resTypeSpec);
type = nextChunk().type;

// We've detected sparse resources, lets record this so we can rebuild in that same format (sparse/not)
// with aapt2. aapt1 will ignore this.
if (! mResTable.getSparseResources()) {
mResTable.setSparseResources(true);
}
}

while (type == ARSCHeader.XML_TYPE_TYPE) {
readTableType();

// skip "TYPE 8 chunks" and/or padding data at the end of this chunk
if (mCountIn.getCount() < mHeader.endPosition) {
LOGGER.warning("Unknown data detected. Skipping: " + (mHeader.endPosition - mCountIn.getCount()) + " byte(s)");
mCountIn.skip(mHeader.endPosition - mCountIn.getCount());
}

type = nextChunk().type;

addMissingResSpecs();
}
}

private ResTypeSpec readSingleTableTypeSpec() throws AndrolibException, IOException {
private ResTypeSpec readTableSpecType() throws AndrolibException, IOException {
checkChunkType(ARSCHeader.XML_TYPE_SPEC_TYPE);
int id = mIn.readUnsignedByte();
mIn.skipBytes(3);
mIn.skipBytes(1); // reserved0
mIn.skipBytes(2); // reserved1
int entryCount = mIn.readInt();

if (mFlagsOffsets != null) {
Expand All @@ -250,6 +246,7 @@ private ResTypeSpec readSingleTableTypeSpec() throws AndrolibException, IOExcept
mIn.skipBytes(entryCount * 4); // flags
mTypeSpec = new ResTypeSpec(mTypeNames.getString(id - 1), mResTable, mPkg, id, entryCount);
mPkg.addType(mTypeSpec);

return mTypeSpec;
}

Expand Down Expand Up @@ -311,6 +308,12 @@ private ResType readTableType() throws IOException, AndrolibException {
readEntry(readEntryData());
}

// skip "TYPE 8 chunks" and/or padding data at the end of this chunk
if (mCountIn.getCount() < mHeader.endPosition) {
long bytesSkipped = mCountIn.skip(mHeader.endPosition - mCountIn.getCount());
LOGGER.warning("Unknown data detected. Skipping: " + bytesSkipped + " byte(s)");
}

return mType;
}

Expand Down Expand Up @@ -616,11 +619,6 @@ private void checkChunkType(int expectedType) throws AndrolibException {
}
}

private void nextChunkCheckType(int expectedType) throws IOException, AndrolibException {
nextChunk();
checkChunkType(expectedType);
}

private final ExtDataInput mIn;
private final ResTable mResTable;
private final CountingInputStream mCountIn;
Expand Down
Loading