iBotPeaches/Apktool — pull request #92 by iBotPeaches (2014-10-05 17:20:48+00:00)
Title: Issue 664

Commit of interest is 0d950f3d996952cc0cd1dc860a624fd86ece63e3 , while other is just CS changes.

Basically, we expect a StringPool to exist after the `ResTable_package`

```
/**
* A collection of resource data types within a package. Followed by
* one or more ResTable_type and ResTable_typeSpec structures containing the
* entry values for each resource type.
*/
struct ResTable_package
{
struct ResChunk_header header;

// If this is a base package, its ID. Package IDs start
// at 1 (corresponding to the value of the package bits in a
// resource identifier). 0 means this is not a base package.
uint32_t id;

// Actual name of this package, \0-terminated.
char16_t name[128];

// Offset to a ResStringPool_header defining the resource
// type symbol table. If zero, this package is inheriting from
// another base package (overriding specific values in it).
uint32_t typeStrings;

// Last index into typeStrings that is for public use by others.
uint32_t lastPublicType;

// Offset to a ResStringPool_header defining the resource
// key symbol table. If zero, this package is inheriting from
// another base package (overriding specific values in it).
uint32_t keyStrings;

// Last index into keyStrings that is for public use by others.
uint32_t lastPublicKey;
};
```

The StringPool being this

```
/**
* Definition for a pool of strings. The data of this chunk is an
* array of uint32_t providing indices into the pool, relative to
* stringsStart. At stringsStart are all of the UTF-16 strings
* concatenated together; each starts with a uint16_t of the string's
* length and each ends with a 0x0000 terminator. If a string is >
* 32767 characters, the high bit of the length is set meaning to take
* those 15 bits as a high word and it will be followed by another
* uint16_t containing the low word.
*
* If styleCount is not zero, then immediately following the array of
* uint32_t indices into the string table is another array of indices
* into a style table starting at stylesStart. Each entry in the
* style table is an array of ResStringPool_span structures.
*/
struct ResStringPool_header
{
struct ResChunk_header header;

// Number of strings in this pool (number of uint32_t indices that follow
// in the data).
uint32_t stringCount;

// Number of style span arrays in the pool (number of uint32_t indices
// follow the string indices).
uint32_t styleCount;

// Flags.
enum {
// If set, the string index is sorted by the string values (based
// on strcmp16()).
SORTED_FLAG = 1<<0,

// String pool is encoded in UTF-8
UTF8_FLAG = 1<<8
};
uint32_t flags;

// Index from header of the string data.
uint32_t stringsStart;

// Index from header of the style data.
uint32_t stylesStart;
};
```

However, it failed the first check which was the `ResChunk_header` which is described as

```
/**
* Header that appears at the front of every data chunk in a resource.
*/
struct ResChunk_header
{
// Type identifier for this chunk. The meaning of this value depends
// on the containing chunk.
uint16_t type;

// Size of the chunk header (in bytes). Adding this value to
// the address of the chunk allows you to find its associated data
// (if any).
uint16_t headerSize;

// Total size of this chunk (in bytes). This is the chunkSize plus
// the size of any data associated with the chunk. Adding this value
// to the chunk allows you to completely skip its contents (including
// any child chunks). If this value is the same as chunkSize, there is
// no data associated with the chunk.
uint32_t size;
};
```

Apktool use to always assume a `RES_STRING_POOL_TYPE`, but we can see in the case of newer apks this isn't always the case. The `type` in `ResChunk_header` has 4 possible enums

```
enum {
RES_NULL_TYPE = 0x0000,
RES_STRING_POOL_TYPE = 0x0001,
RES_TABLE_TYPE = 0x0002,
RES_XML_TYPE = 0x0003,
...
```

The last two not being related to this section. Which means we had previously ignored `RES_NULL_TYPE`. My guess is that some large StringPools are aligned / padded for easier retrieval. So we expect to now find a `RES_STRINGPOOL_TYPE`, but if not we check it for `RES_NULL_TYPE`. If we are successful in that we found a `RES_NULL_TYPE` instead of the expected `RES_STRINGPOOL_TYPE`, we will skip that `int` and look onward for hopefully the `RES_STRING_POOL_TYPE`. If it is present this time (after skipping the `RES_NULL_TYPE`), then we proceed as normal. This worked for the 3 broken apks I had.