js-apk-parser
Version:
Parse the AndroidManifest.xml and resources.arsc file in the apk package.
216 lines • 10 kB
JavaScript
import * as util from './util';
var ResourceTable = /** @class */ (function () {
function ResourceTable(buffer) {
this.tableType = util.uint8ArrayToHex(new Uint8Array(buffer, 0, 2));
this.headerSize = util.uint8ArrayToShort(new Uint8Array(buffer, 2, 2));
this.fileSize = util.uint8ArrayToInt(new Uint8Array(buffer, 4, 4));
this.packageCount = util.uint8ArrayToInt(new Uint8Array(buffer, 8, 4));
// console.log('tableType', this.tableType, 'headerSize', this.headerSize, 'fileSize', this.fileSize, 'packageCount', this.packageCount);
var chunkOffset = 12;
var chunkSize = util.uint8ArrayToInt(new Uint8Array(buffer, chunkOffset + 4 * 1, 4));
this.stringPool = new StringPool(new Uint8Array(buffer, chunkOffset, chunkSize));
// console.log('stringPool', this.stringPool);
chunkOffset += chunkSize;
chunkSize = util.uint8ArrayToInt(new Uint8Array(buffer, chunkOffset + 4 * 1, 4));
this.package = new Package(new Uint8Array(buffer, chunkOffset, chunkSize));
// console.log('package', this.package);
// console.log('ResourceTable', this);
}
ResourceTable.prototype.getResource = function (resourceId) {
var packageId = (resourceId >> 24 & 0xff);
var typeId = ((resourceId >> 16) & 0xff);
var entryIndex = (resourceId & 0xffff);
// console.log('packageId', packageId, 'typeId', typeId, 'entryIndex', entryIndex);
if (this.package.id !== packageId) {
return '';
}
var type = null;
var value = '';
for (var i = 0; i < this.package.typeArray.length; i++) {
type = this.package.typeArray[i];
if (type && type.id === typeId && type.entryArray[entryIndex]) {
value = this.stringPool.stringPool[type.entryArray[entryIndex].dataValue];
if (value && value.length > 0) {
return value;
}
}
}
return '';
};
return ResourceTable;
}());
export { ResourceTable };
var StringPool = /** @class */ (function () {
function StringPool(array) {
// super();
this.chunkType = util.uint8ArrayToHex(array.subarray(0, 2));
this.chunkHeaderSize = util.uint8ArrayToShort(array.subarray(2, 4));
this.chunkSize = util.uint8ArrayToInt(array.subarray(4 * 1, 4 * 2));
this.stringCount = util.uint8ArrayToInt(array.subarray(4 * 2, 4 * 3));
this.styleCount = util.uint8ArrayToInt(array.subarray(4 * 3, 4 * 4));
this.flags = util.uint8ArrayToInt(array.subarray(4 * 4, 4 * 5));
this.stringPoolOffset = util.uint8ArrayToInt(array.subarray(4 * 5, 4 * 6));
this.stylePoolOffset = util.uint8ArrayToInt(array.subarray(4 * 6, 4 * 7));
var utf8 = (this.flags & StringPool.UTF8_FLAG) != 0;
// console.log('flags', this.flags, 'utf8', utf8);
// stringOffsets
this.stringOffsets = [];
var offset = 0;
for (var i = 0; i < this.stringCount; i++) {
offset = util.uint8ArrayToInt(array.subarray(4 * (7 + i), 4 * (8 + i)));
this.stringOffsets.push(offset);
}
// styleOffsets
this.styleOffsets = [];
// TODO
// stringPool
this.stringPool = [];
var str = '';
var length = 0;
for (var i = 0; i < this.stringCount; i++) {
offset = this.stringPoolOffset + this.stringOffsets[i];
if (utf8) {
length = array[offset + 1] & 0x7F; // 头2位的最后1个字节表示字符串长度
}
else {
length = util.uint8ArrayToShort(array.subarray(offset, offset + 2)) * 2; // 头2位表示字符串长度,并且一个字符占2个字节
}
str = util.uint8ArrayToStr(array.subarray(offset + 2, offset + 2 + length), utf8);
// console.log('index=',i,'length=',length,'str=',str);
this.stringPool.push(str);
}
// stylePool
this.stylePool = [];
// TODO
}
StringPool.UTF8_FLAG = 1 << 8;
return StringPool;
}());
var Package = /** @class */ (function () {
function Package(array) {
// super();
this.chunkType = util.uint8ArrayToHex(array.subarray(0, 2));
this.chunkHeaderSize = util.uint8ArrayToShort(array.subarray(2, 4));
this.chunkSize = util.uint8ArrayToInt(array.subarray(4 * 1, 4 * 2));
this.id = util.uint8ArrayToInt(array.subarray(4 * 2, 4 * 3));
this.name = util.utf8ArrayToStr(array.subarray(4 * 3, 4 * (3 + 256 / 4))); // 3+256/4 => 67
this.typeOffset = util.uint8ArrayToInt(array.subarray(4 * 67, 4 * 68));
this.lastType = util.uint8ArrayToInt(array.subarray(4 * 68, 4 * 69));
this.keyOffset = util.uint8ArrayToInt(array.subarray(4 * 69, 4 * 70));
this.lastKey = util.uint8ArrayToInt(array.subarray(4 * 70, 4 * 71));
// typePool
this.typePool = new StringPool(array.subarray(this.typeOffset));
// console.log('typePool', this.typePool);
// keyPool
this.keyPool = new StringPool(array.subarray(this.keyOffset));
// console.log('keyPool', this.keyPool);
// typeSpecArray & typeArray
this.typeSpecArray = [];
this.typeArray = [];
var chunkType = '';
var chunkOffset = this.chunkHeaderSize + this.typePool.chunkSize + this.keyPool.chunkSize;
var chunkSize = 0;
var type = null;
for (; chunkOffset < this.chunkSize;) {
chunkType = util.uint8ArrayToHex(array.subarray(chunkOffset, chunkOffset + 2));
chunkSize = util.uint8ArrayToInt(array.subarray(chunkOffset + 4, chunkOffset + 8));
// console.log('chunkType', chunkType, 'chunkSize', chunkSize, 'chunkOffset', chunkOffset, 'max', this.chunkSize);
if (chunkSize <= 0) {
console.log('skip, chunkSize', chunkSize, 'chunkType', chunkType);
break;
}
if (chunkType === Package.TYPE) {
this.typeArray.push(new Type(array.subarray(chunkOffset, chunkOffset + chunkSize)));
}
else if (chunkType === Package.TYPE_SPEC) {
this.typeSpecArray.push(new TypeSpec(array.subarray(chunkOffset, chunkOffset + chunkSize)));
}
else {
console.log('not support type', chunkType);
}
// next loop
chunkOffset += chunkSize;
}
// console.log('Package', this);
}
Package.TYPE = '0201';
Package.TYPE_SPEC = '0202';
return Package;
}());
var TypeSpec = /** @class */ (function () {
function TypeSpec(array) {
// super();
this.chunkType = util.uint8ArrayToHex(array.subarray(0, 2));
this.chunkHeaderSize = util.uint8ArrayToShort(array.subarray(2, 4));
this.chunkSize = util.uint8ArrayToInt(array.subarray(4 * 1, 4 * 2));
var bytes = array.subarray(4 * 2, 4 * 3);
this.id = bytes[0] & 0xFF;
this.entryCount = util.uint8ArrayToInt(array.subarray(4 * 3, 4 * 4));
var offset = this.chunkHeaderSize;
// entryArray
this.entryArray = [];
for (var i = 0; i < this.entryCount; i++) {
offset = this.chunkHeaderSize + 4 * i;
this.entryArray.push(util.uint8ArrayToInt(array.subarray(offset, offset + 4)));
}
}
return TypeSpec;
}());
var Type = /** @class */ (function () {
function Type(array) {
// super();
this.chunkType = util.uint8ArrayToHex(array.subarray(0, 2));
this.chunkHeaderSize = util.uint8ArrayToShort(array.subarray(2, 4));
this.chunkSize = util.uint8ArrayToInt(array.subarray(4 * 1, 4 * 2));
var bytes = array.subarray(4 * 2, 4 * 3);
this.id = bytes[0] & 0xFF;
this.entryCount = util.uint8ArrayToInt(array.subarray(4 * 3, 4 * 4));
this.entryPoolOffset = util.uint8ArrayToInt(array.subarray(4 * 4, 4 * 5));
// Config
// TODO size=48
var offset = this.chunkHeaderSize;
// entryOffsets
this.entryOffsets = [];
for (var i = 0; i < this.entryCount; i++) {
offset = this.chunkHeaderSize + 4 * i;
this.entryOffsets.push(util.uint8ArrayToInt(array.subarray(offset, offset + 4)));
}
// entryArray
this.entryArray = [];
var entry = null;
for (var i = 0; i < this.entryCount; i++) {
offset = this.entryPoolOffset + this.entryOffsets[i];
entry = new Entry(array.subarray(offset));
if (entry.size === 8 || entry.size === 16) {
this.entryArray.push(entry);
}
else {
// console.log(entry, util.uint8ArrayToHex(array.subarray(offset, offset+16)))
}
}
}
return Type;
}());
var Entry = /** @class */ (function () {
function Entry(array) {
// super();
this.size = util.uint8ArrayToShort(array.subarray(0, 2));
this.flags = util.uint8ArrayToShort(array.subarray(2, 4));
this.index = util.uint8ArrayToShort(array.subarray(4 * 1, 4 * 2));
var offset = 4 * 2;
if ((this.flags & Entry.FLAG_COMPLEX) === 0) {
this.dataSize = util.uint8ArrayToShort(array.subarray(offset + 0, offset + 2));
this.dataType = array[offset + 3] & 0xFF;
this.dataValue = util.uint8ArrayToShort(array.subarray(offset + 4, offset + 8));
}
else {
// TODO
this.dataSize = -1;
this.dataType = -1;
this.dataValue = -1;
}
}
Entry.FLAG_COMPLEX = 0x0001;
return Entry;
}());
//# sourceMappingURL=ResourceTable.js.map