UNPKG

app-info-parser

Version:

Exact info from apk or ipa file.

1,275 lines (1,235 loc) 1.22 MB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.AppInfoParser = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(_dereq_,module,exports){ "use strict"; function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); } function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } var Zip = _dereq_('./zip'); var _require = _dereq_('./utils'), mapInfoResource = _require.mapInfoResource, findApkIconPath = _require.findApkIconPath, getBase64FromBuffer = _require.getBase64FromBuffer; var ManifestName = /^androidmanifest\.xml$/; var ResourceName = /^resources\.arsc$/; var ManifestXmlParser = _dereq_('./xml-parser/manifest'); var ResourceFinder = _dereq_('./resource-finder'); var ApkParser = /*#__PURE__*/function (_Zip) { _inherits(ApkParser, _Zip); var _super = _createSuper(ApkParser); /** * parser for parsing .apk file * @param {String | File | Blob} file // file's path in Node, instance of File or Blob in Browser */ function ApkParser(file) { var _this; _classCallCheck(this, ApkParser); _this = _super.call(this, file); if (!(_assertThisInitialized(_this) instanceof ApkParser)) { return _possibleConstructorReturn(_this, new ApkParser(file)); } return _this; } _createClass(ApkParser, [{ key: "parse", value: function parse() { var _this2 = this; return new Promise(function (resolve, reject) { _this2.getEntries([ManifestName, ResourceName]).then(function (buffers) { if (!buffers[ManifestName]) { throw new Error('AndroidManifest.xml can\'t be found.'); } var apkInfo = _this2._parseManifest(buffers[ManifestName]); var resourceMap; if (!buffers[ResourceName]) { resolve(apkInfo); } else { // parse resourceMap resourceMap = _this2._parseResourceMap(buffers[ResourceName]); // update apkInfo with resourceMap apkInfo = mapInfoResource(apkInfo, resourceMap); // find icon path and parse icon var iconPath = findApkIconPath(apkInfo); if (iconPath) { _this2.getEntry(iconPath).then(function (iconBuffer) { apkInfo.icon = iconBuffer ? getBase64FromBuffer(iconBuffer) : null; resolve(apkInfo); })["catch"](function (e) { apkInfo.icon = null; resolve(apkInfo); console.warn('[Warning] failed to parse icon: ', e); }); } else { apkInfo.icon = null; resolve(apkInfo); } } })["catch"](function (e) { reject(e); }); }); } /** * Parse manifest * @param {Buffer} buffer // manifest file's buffer */ }, { key: "_parseManifest", value: function _parseManifest(buffer) { try { var parser = new ManifestXmlParser(buffer, { ignore: ['application.activity', 'application.service', 'application.receiver', 'application.provider', 'permission-group'] }); return parser.parse(); } catch (e) { throw new Error('Parse AndroidManifest.xml error: ', e); } } /** * Parse resourceMap * @param {Buffer} buffer // resourceMap file's buffer */ }, { key: "_parseResourceMap", value: function _parseResourceMap(buffer) { try { return new ResourceFinder().processResourceTable(buffer); } catch (e) { throw new Error('Parser resources.arsc error: ' + e); } } }]); return ApkParser; }(Zip); module.exports = ApkParser; },{"./resource-finder":4,"./utils":5,"./xml-parser/manifest":7,"./zip":8}],2:[function(_dereq_,module,exports){ "use strict"; function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); } function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } var ApkParser = _dereq_('./apk'); var IpaParser = _dereq_('./ipa'); var supportFileTypes = ['ipa', 'apk']; var AppInfoParser = /*#__PURE__*/function () { /** * parser for parsing .ipa or .apk file * @param {String | File | Blob} file // file's path in Node, instance of File or Blob in Browser */ function AppInfoParser(file) { _classCallCheck(this, AppInfoParser); if (!file) { throw new Error('Param miss: file(file\'s path in Node, instance of File or Blob in browser).'); } var splits = (file.name || file).split('.'); var fileType = splits[splits.length - 1].toLowerCase(); if (!supportFileTypes.includes(fileType)) { throw new Error('Unsupported file type, only support .ipa or .apk file.'); } this.file = file; switch (fileType) { case 'ipa': this.parser = new IpaParser(this.file); break; case 'apk': this.parser = new ApkParser(this.file); break; } } _createClass(AppInfoParser, [{ key: "parse", value: function parse() { return this.parser.parse(); } }]); return AppInfoParser; }(); module.exports = AppInfoParser; },{"./apk":1,"./ipa":3}],3:[function(_dereq_,module,exports){ "use strict"; function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); } function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } var parsePlist = _dereq_('plist').parse; var parseBplist = _dereq_('bplist-parser').parseBuffer; var cgbiToPng = _dereq_('cgbi-to-png'); var Zip = _dereq_('./zip'); var _require = _dereq_('./utils'), findIpaIconPath = _require.findIpaIconPath, getBase64FromBuffer = _require.getBase64FromBuffer, isBrowser = _require.isBrowser; var PlistName = new RegExp('payload/[^/]+?.app/info.plist$', 'i'); var ProvisionName = /payload\/.+?\.app\/embedded.mobileprovision/; var IpaParser = /*#__PURE__*/function (_Zip) { _inherits(IpaParser, _Zip); var _super = _createSuper(IpaParser); /** * parser for parsing .ipa file * @param {String | File | Blob} file // file's path in Node, instance of File or Blob in Browser */ function IpaParser(file) { var _this; _classCallCheck(this, IpaParser); _this = _super.call(this, file); if (!(_assertThisInitialized(_this) instanceof IpaParser)) { return _possibleConstructorReturn(_this, new IpaParser(file)); } return _this; } _createClass(IpaParser, [{ key: "parse", value: function parse() { var _this2 = this; return new Promise(function (resolve, reject) { _this2.getEntries([PlistName, ProvisionName]).then(function (buffers) { if (!buffers[PlistName]) { throw new Error('Info.plist can\'t be found.'); } var plistInfo = _this2._parsePlist(buffers[PlistName]); // parse mobile provision var provisionInfo = _this2._parseProvision(buffers[ProvisionName]); plistInfo.mobileProvision = provisionInfo; // find icon path and parse icon var iconRegex = new RegExp(findIpaIconPath(plistInfo).toLowerCase()); _this2.getEntry(iconRegex).then(function (iconBuffer) { try { // In general, the ipa file's icon has been specially processed, should be converted plistInfo.icon = iconBuffer ? getBase64FromBuffer(cgbiToPng.revert(iconBuffer)) : null; } catch (err) { if (isBrowser()) { // Normal conversion in other cases plistInfo.icon = iconBuffer ? getBase64FromBuffer(window.btoa(String.fromCharCode.apply(String, _toConsumableArray(iconBuffer)))) : null; } else { plistInfo.icon = null; console.warn('[Warning] failed to parse icon: ', err); } } resolve(plistInfo); })["catch"](function (e) { reject(e); }); })["catch"](function (e) { reject(e); }); }); } /** * Parse plist * @param {Buffer} buffer // plist file's buffer */ }, { key: "_parsePlist", value: function _parsePlist(buffer) { var result; var bufferType = buffer[0]; if (bufferType === 60 || bufferType === '<' || bufferType === 239) { result = parsePlist(buffer.toString()); } else if (bufferType === 98) { result = parseBplist(buffer)[0]; } else { throw new Error('Unknown plist buffer type.'); } return result; } /** * parse provision * @param {Buffer} buffer // provision file's buffer */ }, { key: "_parseProvision", value: function _parseProvision(buffer) { var info = {}; if (buffer) { var content = buffer.toString('utf-8'); var firstIndex = content.indexOf('<?xml'); var endIndex = content.indexOf('</plist>'); content = content.slice(firstIndex, endIndex + 8); if (content) { info = parsePlist(content); } } return info; } }]); return IpaParser; }(Zip); module.exports = IpaParser; },{"./utils":5,"./zip":8,"bplist-parser":18,"cgbi-to-png":28,"plist":93}],4:[function(_dereq_,module,exports){ "use strict"; /** * Code translated from a C# project https://github.com/hylander0/Iteedee.ApkReader/blob/master/Iteedee.ApkReader/ApkResourceFinder.cs * * Decode binary file `resources.arsc` from a .apk file to a JavaScript Object. */ var ByteBuffer = _dereq_("bytebuffer"); var DEBUG = false; var RES_STRING_POOL_TYPE = 0x0001; var RES_TABLE_TYPE = 0x0002; var RES_TABLE_PACKAGE_TYPE = 0x0200; var RES_TABLE_TYPE_TYPE = 0x0201; var RES_TABLE_TYPE_SPEC_TYPE = 0x0202; // The 'data' holds a ResTable_ref, a reference to another resource // table entry. var TYPE_REFERENCE = 0x01; // The 'data' holds an index into the containing resource table's // global value string pool. var TYPE_STRING = 0x03; function ResourceFinder() { this.valueStringPool = null; this.typeStringPool = null; this.keyStringPool = null; this.package_id = 0; this.responseMap = {}; this.entryMap = {}; } /** * Same to C# BinaryReader.readBytes * * @param bb ByteBuffer * @param len length * @returns {Buffer} */ ResourceFinder.readBytes = function (bb, len) { var uint8Array = new Uint8Array(len); for (var i = 0; i < len; i++) { uint8Array[i] = bb.readUint8(); } return ByteBuffer.wrap(uint8Array, "binary", true); }; // /** * * @param {ByteBuffer} bb * @return {Map<String, Set<String>>} */ ResourceFinder.prototype.processResourceTable = function (resourceBuffer) { var bb = ByteBuffer.wrap(resourceBuffer, "binary", true); // Resource table structure var type = bb.readShort(), headerSize = bb.readShort(), size = bb.readInt(), packageCount = bb.readInt(), buffer, bb2; if (type != RES_TABLE_TYPE) { throw new Error("No RES_TABLE_TYPE found!"); } if (size != bb.limit) { throw new Error("The buffer size not matches to the resource table size."); } bb.offset = headerSize; var realStringPoolCount = 0, realPackageCount = 0; while (true) { var pos, t, hs, s; try { pos = bb.offset; t = bb.readShort(); hs = bb.readShort(); s = bb.readInt(); } catch (e) { break; } if (t == RES_STRING_POOL_TYPE) { // Process the string pool if (realStringPoolCount == 0) { // Only the first string pool is processed. if (DEBUG) { console.log("Processing the string pool ..."); } buffer = new ByteBuffer(s); bb.offset = pos; bb.prependTo(buffer); bb2 = ByteBuffer.wrap(buffer, "binary", true); bb2.LE(); this.valueStringPool = this.processStringPool(bb2); } realStringPoolCount++; } else if (t == RES_TABLE_PACKAGE_TYPE) { // Process the package if (DEBUG) { console.log("Processing the package " + realPackageCount + " ..."); } buffer = new ByteBuffer(s); bb.offset = pos; bb.prependTo(buffer); bb2 = ByteBuffer.wrap(buffer, "binary", true); bb2.LE(); this.processPackage(bb2); realPackageCount++; } else { throw new Error("Unsupported type"); } bb.offset = pos + s; if (!bb.remaining()) break; } if (realStringPoolCount != 1) { throw new Error("More than 1 string pool found!"); } if (realPackageCount != packageCount) { throw new Error("Real package count not equals the declared count."); } return this.responseMap; }; /** * * @param {ByteBuffer} bb */ ResourceFinder.prototype.processPackage = function (bb) { // Package structure var type = bb.readShort(), headerSize = bb.readShort(), size = bb.readInt(), id = bb.readInt(); this.package_id = id; for (var i = 0; i < 256; ++i) { bb.readUint8(); } var typeStrings = bb.readInt(), lastPublicType = bb.readInt(), keyStrings = bb.readInt(), lastPublicKey = bb.readInt(); if (typeStrings != headerSize) { throw new Error("TypeStrings must immediately following the package structure header."); } if (DEBUG) { console.log("Type strings:"); } var lastPosition = bb.offset; bb.offset = typeStrings; var bbTypeStrings = ResourceFinder.readBytes(bb, bb.limit - bb.offset); bb.offset = lastPosition; this.typeStringPool = this.processStringPool(bbTypeStrings); // Key strings if (DEBUG) { console.log("Key strings:"); } bb.offset = keyStrings; var key_type = bb.readShort(), key_headerSize = bb.readShort(), key_size = bb.readInt(); lastPosition = bb.offset; bb.offset = keyStrings; var bbKeyStrings = ResourceFinder.readBytes(bb, bb.limit - bb.offset); bb.offset = lastPosition; this.keyStringPool = this.processStringPool(bbKeyStrings); // Iterate through all chunks var typeSpecCount = 0; var typeCount = 0; bb.offset = keyStrings + key_size; var bb2; while (true) { var pos = bb.offset; try { var t = bb.readShort(); var hs = bb.readShort(); var s = bb.readInt(); } catch (e) { break; } if (t == RES_TABLE_TYPE_SPEC_TYPE) { bb.offset = pos; bb2 = ResourceFinder.readBytes(bb, s); this.processTypeSpec(bb2); typeSpecCount++; } else if (t == RES_TABLE_TYPE_TYPE) { bb.offset = pos; bb2 = ResourceFinder.readBytes(bb, s); this.processType(bb2); typeCount++; } if (s == 0) { break; } bb.offset = pos + s; if (!bb.remaining()) { break; } } }; /** * * @param {ByteBuffer} bb */ ResourceFinder.prototype.processType = function (bb) { var type = bb.readShort(), headerSize = bb.readShort(), size = bb.readInt(), id = bb.readByte(), res0 = bb.readByte(), res1 = bb.readShort(), entryCount = bb.readInt(), entriesStart = bb.readInt(); var refKeys = {}; var config_size = bb.readInt(); // Skip the config data bb.offset = headerSize; if (headerSize + entryCount * 4 != entriesStart) { throw new Error("HeaderSize, entryCount and entriesStart are not valid."); } // Start to get entry indices var entryIndices = new Array(entryCount); for (var i = 0; i < entryCount; ++i) { entryIndices[i] = bb.readInt(); } // Get entries for (var i = 0; i < entryCount; ++i) { if (entryIndices[i] == -1) continue; var resource_id = this.package_id << 24 | id << 16 | i; var pos = bb.offset, entry_size, entry_flag, entry_key, value_size, value_res0, value_dataType, value_data; try { entry_size = bb.readShort(); entry_flag = bb.readShort(); entry_key = bb.readInt(); } catch (e) { break; } // Get the value (simple) or map (complex) var FLAG_COMPLEX = 0x0001; if ((entry_flag & FLAG_COMPLEX) == 0) { // Simple case value_size = bb.readShort(); value_res0 = bb.readByte(); value_dataType = bb.readByte(); value_data = bb.readInt(); var idStr = Number(resource_id).toString(16); var keyStr = this.keyStringPool[entry_key]; var data = null; if (DEBUG) { console.log("Entry 0x" + idStr + ", key: " + keyStr + ", simple value type: "); } var key = parseInt(idStr, 16); var entryArr = this.entryMap[key]; if (entryArr == null) { entryArr = []; } entryArr.push(keyStr); this.entryMap[key] = entryArr; if (value_dataType == TYPE_STRING) { data = this.valueStringPool[value_data]; if (DEBUG) { console.log(", data: " + this.valueStringPool[value_data] + ""); } } else if (value_dataType == TYPE_REFERENCE) { var hexIndex = Number(value_data).toString(16); refKeys[idStr] = value_data; } else { data = "" + value_data; if (DEBUG) { console.log(", data: " + value_data + ""); } } this.putIntoMap("@" + idStr, data); } else { // Complex case var entry_parent = bb.readInt(); var entry_count = bb.readInt(); for (var j = 0; j < entry_count; ++j) { var ref_name = bb.readInt(); value_size = bb.readShort(); value_res0 = bb.readByte(); value_dataType = bb.readByte(); value_data = bb.readInt(); } if (DEBUG) { console.log("Entry 0x" + Number(resource_id).toString(16) + ", key: " + this.keyStringPool[entry_key] + ", complex value, not printed."); } } } for (var refK in refKeys) { var values = this.responseMap["@" + Number(refKeys[refK]).toString(16).toUpperCase()]; if (values != null && Object.keys(values).length < 1000) { for (var value in values) { this.putIntoMap("@" + refK, values[value]); } } } }; /** * * @param {ByteBuffer} bb * @return {Array} */ ResourceFinder.prototype.processStringPool = function (bb) { // String pool structure // var type = bb.readShort(), headerSize = bb.readShort(), size = bb.readInt(), stringCount = bb.readInt(), styleCount = bb.readInt(), flags = bb.readInt(), stringsStart = bb.readInt(), stylesStart = bb.readInt(), u16len, buffer; var isUTF_8 = (flags & 256) != 0; var offsets = new Array(stringCount); for (var i = 0; i < stringCount; ++i) { offsets[i] = bb.readInt(); } var strings = new Array(stringCount); for (var i = 0; i < stringCount; ++i) { var pos = stringsStart + offsets[i]; bb.offset = pos; strings[i] = ""; if (isUTF_8) { u16len = bb.readUint8(); if ((u16len & 0x80) != 0) { u16len = ((u16len & 0x7f) << 8) + bb.readUint8(); } var u8len = bb.readUint8(); if ((u8len & 0x80) != 0) { u8len = ((u8len & 0x7f) << 8) + bb.readUint8(); } if (u8len > 0) { buffer = ResourceFinder.readBytes(bb, u8len); try { strings[i] = ByteBuffer.wrap(buffer, "utf8", true).toString("utf8"); } catch (e) { if (DEBUG) { console.error(e); console.log("Error when turning buffer to utf-8 string."); } } } else { strings[i] = ""; } } else { u16len = bb.readUint16(); if ((u16len & 0x8000) != 0) { // larger than 32768 u16len = ((u16len & 0x7fff) << 16) + bb.readUint16(); } if (u16len > 0) { var len = u16len * 2; buffer = ResourceFinder.readBytes(bb, len); try { strings[i] = ByteBuffer.wrap(buffer, "utf8", true).toString("utf8"); } catch (e) { if (DEBUG) { console.error(e); console.log("Error when turning buffer to utf-8 string."); } } } } if (DEBUG) { console.log("Parsed value: {0}", strings[i]); } } return strings; }; /** * * @param {ByteBuffer} bb */ ResourceFinder.prototype.processTypeSpec = function (bb) { var type = bb.readShort(), headerSize = bb.readShort(), size = bb.readInt(), id = bb.readByte(), res0 = bb.readByte(), res1 = bb.readShort(), entryCount = bb.readInt(); if (DEBUG) { console.log("Processing type spec " + this.typeStringPool[id - 1] + "..."); } var flags = new Array(entryCount); for (var i = 0; i < entryCount; ++i) { flags[i] = bb.readInt(); } }; ResourceFinder.prototype.putIntoMap = function (resId, value) { if (this.responseMap[resId.toUpperCase()] == null) { this.responseMap[resId.toUpperCase()] = []; } if (value) { this.responseMap[resId.toUpperCase()].push(value); } }; module.exports = ResourceFinder; },{"bytebuffer":25}],5:[function(_dereq_,module,exports){ (function (process){(function (){ "use strict"; function objectType(o) { return Object.prototype.toString.call(o).slice(8, -1).toLowerCase(); } function isArray(o) { return objectType(o) === 'array'; } function isObject(o) { return objectType(o) === 'object'; } function isPrimitive(o) { return o === null || ['boolean', 'number', 'string', 'undefined'].includes(objectType(o)); } function isBrowser() { return typeof process === 'undefined' || Object.prototype.toString.call(process) !== '[object process]'; } /** * map file place with resourceMap * @param {Object} apkInfo // json info parsed from .apk file * @param {Object} resourceMap // resourceMap */ function mapInfoResource(apkInfo, resourceMap) { iteratorObj(apkInfo); return apkInfo; function iteratorObj(obj) { for (var i in obj) { if (isArray(obj[i])) { iteratorArray(obj[i]); } else if (isObject(obj[i])) { iteratorObj(obj[i]); } else if (isPrimitive(obj[i])) { if (isResources(obj[i])) { obj[i] = resourceMap[transKeyToMatchResourceMap(obj[i])]; } } } } function iteratorArray(array) { var l = array.length; for (var i = 0; i < l; i++) { if (isArray(array[i])) { iteratorArray(array[i]); } else if (isObject(array[i])) { iteratorObj(array[i]); } else if (isPrimitive(array[i])) { if (isResources(array[i])) { array[i] = resourceMap[transKeyToMatchResourceMap(array[i])]; } } } } function isResources(attrValue) { if (!attrValue) return false; if (typeof attrValue !== 'string') { attrValue = attrValue.toString(); } return attrValue.indexOf('resourceId:') === 0; } function transKeyToMatchResourceMap(resourceId) { return '@' + resourceId.replace('resourceId:0x', '').toUpperCase(); } } /** * find .apk file's icon path from json info * @param info // json info parsed from .apk file */ function findApkIconPath(info) { if (!info.application.icon || !info.application.icon.splice) { return ''; } var rulesMap = { mdpi: 48, hdpi: 72, xhdpi: 96, xxdpi: 144, xxxhdpi: 192 }; var resultMap = {}; var maxDpiIcon = { dpi: 120, icon: '' }; var _loop = function _loop(i) { info.application.icon.some(function (icon) { if (icon && icon.indexOf(i) !== -1) { resultMap['application-icon-' + rulesMap[i]] = icon; return true; } }); // get the maximal size icon if (resultMap['application-icon-' + rulesMap[i]] && rulesMap[i] >= maxDpiIcon.dpi) { maxDpiIcon.dpi = rulesMap[i]; maxDpiIcon.icon = resultMap['application-icon-' + rulesMap[i]]; } }; for (var i in rulesMap) { _loop(i); } if (Object.keys(resultMap).length === 0 || !maxDpiIcon.icon) { maxDpiIcon.dpi = 120; maxDpiIcon.icon = info.application.icon[0] || ''; resultMap['applicataion-icon-120'] = maxDpiIcon.icon; } return maxDpiIcon.icon; } /** * find .ipa file's icon path from json info * @param info // json info parsed from .ipa file */ function findIpaIconPath(info) { if (info.CFBundleIcons && info.CFBundleIcons.CFBundlePrimaryIcon && info.CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles && info.CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles.length) { return info.CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles[info.CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles.length - 1]; } else if (info.CFBundleIconFiles && info.CFBundleIconFiles.length) { return info.CFBundleIconFiles[info.CFBundleIconFiles.length - 1]; } else { return '.app/Icon.png'; } } /** * transform buffer to base64 * @param {Buffer} buffer */ function getBase64FromBuffer(buffer) { return 'data:image/png;base64,' + buffer.toString('base64'); } /** * 去除unicode空字符 * @param {String} str */ function decodeNullUnicode(str) { if (typeof str === 'string') { // eslint-disable-next-line str = str.replace(/\u0000/g, ''); } return str; } module.exports = { isArray: isArray, isObject: isObject, isPrimitive: isPrimitive, isBrowser: isBrowser, mapInfoResource: mapInfoResource, findApkIconPath: findApkIconPath, findIpaIconPath: findIpaIconPath, getBase64FromBuffer: getBase64FromBuffer, decodeNullUnicode: decodeNullUnicode }; }).call(this)}).call(this,_dereq_('_process')) },{"_process":97}],6:[function(_dereq_,module,exports){ "use strict"; function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); } function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } // From https://github.com/openstf/adbkit-apkreader var NodeType = { ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, CDATA_SECTION_NODE: 4 }; var ChunkType = { NULL: 0x0000, STRING_POOL: 0x0001, TABLE: 0x0002, XML: 0x0003, XML_FIRST_CHUNK: 0x0100, XML_START_NAMESPACE: 0x0100, XML_END_NAMESPACE: 0x0101, XML_START_ELEMENT: 0x0102, XML_END_ELEMENT: 0x0103, XML_CDATA: 0x0104, XML_LAST_CHUNK: 0x017f, XML_RESOURCE_MAP: 0x0180, TABLE_PACKAGE: 0x0200, TABLE_TYPE: 0x0201, TABLE_TYPE_SPEC: 0x0202 }; var StringFlags = { SORTED: 1 << 0, UTF8: 1 << 8 }; // Taken from android.util.TypedValue var TypedValue = { COMPLEX_MANTISSA_MASK: 0x00ffffff, COMPLEX_MANTISSA_SHIFT: 0x00000008, COMPLEX_RADIX_0p23: 0x00000003, COMPLEX_RADIX_16p7: 0x00000001, COMPLEX_RADIX_23p0: 0x00000000, COMPLEX_RADIX_8p15: 0x00000002, COMPLEX_RADIX_MASK: 0x00000003, COMPLEX_RADIX_SHIFT: 0x00000004, COMPLEX_UNIT_DIP: 0x00000001, COMPLEX_UNIT_FRACTION: 0x00000000, COMPLEX_UNIT_FRACTION_PARENT: 0x00000001, COMPLEX_UNIT_IN: 0x00000004, COMPLEX_UNIT_MASK: 0x0000000f, COMPLEX_UNIT_MM: 0x00000005, COMPLEX_UNIT_PT: 0x00000003, COMPLEX_UNIT_PX: 0x00000000, COMPLEX_UNIT_SHIFT: 0x00000000, COMPLEX_UNIT_SP: 0x00000002, DENSITY_DEFAULT: 0x00000000, DENSITY_NONE: 0x0000ffff, TYPE_ATTRIBUTE: 0x00000002, TYPE_DIMENSION: 0x00000005, TYPE_FIRST_COLOR_INT: 0x0000001c, TYPE_FIRST_INT: 0x00000010, TYPE_FLOAT: 0x00000004, TYPE_FRACTION: 0x00000006, TYPE_INT_BOOLEAN: 0x00000012, TYPE_INT_COLOR_ARGB4: 0x0000001e, TYPE_INT_COLOR_ARGB8: 0x0000001c, TYPE_INT_COLOR_RGB4: 0x0000001f, TYPE_INT_COLOR_RGB8: 0x0000001d, TYPE_INT_DEC: 0x00000010, TYPE_INT_HEX: 0x00000011, TYPE_LAST_COLOR_INT: 0x0000001f, TYPE_LAST_INT: 0x0000001f, TYPE_NULL: 0x00000000, TYPE_REFERENCE: 0x00000001, TYPE_STRING: 0x00000003 }; var BinaryXmlParser = /*#__PURE__*/function () { function BinaryXmlParser(buffer) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; _classCallCheck(this, BinaryXmlParser); this.buffer = buffer; this.cursor = 0; this.strings = []; this.resources = []; this.document = null; this.parent = null; this.stack = []; this.debug = options.debug || false; } _createClass(BinaryXmlParser, [{ key: "readU8", value: function readU8() { this.debug && console.group('readU8'); this.debug && console.debug('cursor:', this.cursor); var val = this.buffer[this.cursor]; this.debug && console.debug('value:', val); this.cursor += 1; this.debug && console.groupEnd(); return val; } }, { key: "readU16", value: function readU16() { this.debug && console.group('readU16'); this.debug && console.debug('cursor:', this.cursor); var val = this.buffer.readUInt16LE(this.cursor); this.debug && console.debug('value:', val); this.cursor += 2; this.debug && console.groupEnd(); return val; } }, { key: "readS32", value: function readS32() { this.debug && console.group('readS32'); this.debug && console.debug('cursor:', this.cursor); var val = this.buffer.readInt32LE(this.cursor); this.debug && console.debug('value:', val); this.cursor += 4; this.debug && console.groupEnd(); return val; } }, { key: "readU32", value: function readU32() { this.debug && console.group('readU32'); this.debug && console.debug('cursor:', this.cursor); var val = this.buffer.readUInt32LE(this.cursor); this.debug && console.debug('value:', val); this.cursor += 4; this.debug && console.groupEnd(); return val; } }, { key: "readLength8", value: function readLength8() { this.debug && console.group('readLength8'); var len = this.readU8(); if (len & 0x80) { len = (len & 0x7f) << 8; len += this.readU8(); } this.debug && console.debug('length:', len); this.debug && console.groupEnd(); return len; } }, { key: "readLength16", value: function readLength16() { this.debug && console.group('readLength16'); var len = this.readU16(); if (len & 0x8000) { len = (len & 0x7fff) << 16; len += this.readU16(); } this.debug && console.debug('length:', len); this.debug && console.groupEnd(); return len; } }, { key: "readDimension", value: function readDimension() { this.debug && console.group('readDimension'); var dimension = { value: null, unit: null, rawUnit: null }; var value = this.readU32(); var unit = dimension.value & 0xff; dimension.value = value >> 8; dimension.rawUnit = unit; switch (unit) { case TypedValue.COMPLEX_UNIT_MM: dimension.unit = 'mm'; break; case TypedValue.COMPLEX_UNIT_PX: dimension.unit = 'px'; break; case TypedValue.COMPLEX_UNIT_DIP: dimension.unit = 'dp'; break; case TypedValue.COMPLEX_UNIT_SP: dimension.unit = 'sp'; break; case TypedValue.COMPLEX_UNIT_PT: dimension.unit = 'pt'; break; case TypedValue.COMPLEX_UNIT_IN: dimension.unit = 'in'; break; } this.debug && console.groupEnd(); return dimension; } }, { key: "readFraction", value: function readFraction() { this.debug && console.group('readFraction'); var fraction = { value: null, type: null, rawType: null }; var value = this.readU32(); var type = value & 0xf; fraction.value = this.convertIntToFloat(value >> 4); fraction.rawType = type; switch (type) { case TypedValue.COMPLEX_UNIT_FRACTION: fraction.type = '%'; break; case TypedValue.COMPLEX_UNIT_FRACTION_PARENT: fraction.type = '%p'; break; } this.debug && console.groupEnd(); return fraction; } }, { key: "readHex24", value: function readHex24() { this.debug && console.group('readHex24'); var val = (this.readU32() & 0xffffff).toString(16); this.debug && console.groupEnd(); return val; } }, { key: "readHex32", value: function readHex32() { this.debug && console.group('readHex32'); var val = this.readU32().toString(16); this.debug && console.groupEnd(); return val; } }, { key: "readTypedValue", value: function readTypedValue() { this.debug && console.group('readTypedValue'); var typedValue = { value: null, type: null, rawType: null }; var start = this.cursor; var size = this.readU16(); /* const zero = */ this.readU8(); var dataType = this.readU8(); // Yes, there has been a real world APK where the size is malformed. if (size === 0) { size = 8; } typedValue.rawType = dataType; switch (dataType) { case TypedValue.TYPE_INT_DEC: typedValue.value = this.readS32(); typedValue.type = 'int_dec'; break; case TypedValue.TYPE_INT_HEX: typedValue.value = this.readS32(); typedValue.type = 'int_hex'; break; case TypedValue.TYPE_STRING: var ref = this.readS32(); typedValue.value = ref > 0 ? this.strings[ref] : ''; typedValue.type = 'string'; break; case TypedValue.TYPE_REFERENCE: var id = this.readU32(); typedValue.value = "resourceId:0x".concat(id.toString(16)); typedValue.type = 'reference'; break; case TypedValue.TYPE_INT_BOOLEAN: typedValue.value = this.readS32() !== 0; typedValue.type = 'boolean'; break; case TypedValue.TYPE_NULL: this.readU32(); typedValue.value = null; typedValue.type = 'null'; break; case TypedValue.TYPE_INT_COLOR_RGB8: typedValue.value = this.readHex24(); typedValue.type = 'rgb8'; break; case TypedValue.TYPE_INT_COLOR_RGB4: typedValue.value = this.readHex24(); typedValue.type = 'rgb4'; break; case TypedValue.TYPE_INT_COLOR_ARGB8: typedValue.value = this.readHex32(); typedValue.type = 'argb8'; break; case TypedValue.TYPE_INT_COLOR_ARGB4: typedValue.value = this.readHex32(); typedValue.type = 'argb4'; break; case TypedValue.TYPE_DIMENSION: typedValue.value = this.readDimension(); typedValue.type = 'dimension'; break; case TypedValue.TYPE_FRACTION: typedValue.value = this.readFraction(); typedValue.type = 'fraction'; break; default: { var type = dataType.toString(16); console.debug("Not sure what to do with typed value of type 0x".concat(type, ", falling back to reading an uint32.")); typedValue.value = this.readU32(); typedValue.type = 'unknown'; } } // Ensure we consume the whole value var end = start + size; if (this.cursor !== end) { var _type = dataType.toString(16); var diff = end - this.cursor; console.debug("Cursor is off by ".concat(diff, " bytes at ").concat(this.cursor, " at supposed end of typed value of type 0x").concat(_type, ". The typed value started at offset ").concat(start, " and is supposed to end at offset ").concat(end, ". Ignoring the rest of the value.")); this.cursor = end; } this.debug && console.groupEnd(); return typedValue; } // https://twitter.com/kawasima/status/427730289201139712 }, { key: "convertIntToFloat", value: function convertIntToFloat(_int) { var buf = new ArrayBuffer(4); new Int32Array(buf)[0] = _int; return new Float32Array(buf)[0]; } }, { key: "readString", value: function readString(encoding) { this.debug && console.group('readString', encoding); switch (encoding) { case 'utf-8': var stringLength = this.readLength8(encoding); this.debug && console.debug('stringLength:', stringLength); var byteLength = this.readLength8(encoding); this.debug && console.debug('byteLength:', byteLength); var value = this.buffer.toString(encoding, this.cursor, this.cursor += byteLength); this.debug && console.debug('value:', value); this.debug && console.groupEnd(); return value; case 'ucs2': stringLength = this.readLength16(encoding); this.debug && console.debug('stringLength:', stringLength); byteLength = stringLength * 2; this.debug && console.debug('byteLength:', byteLength); value = this.buffer.toString(encoding, this.cursor, this.cursor += byteLength); this.debug && console.debug('value:', value); this.debug && console.groupEnd(); return value; default: throw new Error("Unsupported encoding '".concat(encoding, "'")); } } }, { key: "readChunkHeader", value: function readChunkHeader() { this.debug && console.group('readChunkHeader'); var header = { startOffset: this.cursor, chunkType: this.readU16(), headerSize: this.readU16(), chunkSize: this.readU32() }; this.debug && console.debug('startOffset:', header.startOffset); this.debug && console.debug('chunkType:', header.chunkType); this.debug && console.debug('headerSize:', header.headerSize); this.debug && console.debug('chunkSize:', header.chunkSize); this.debug && console.groupEnd(); return header; } }, { key: "readStringPool", value: function readStringPool(header) { this.debug && console.group('readStringPool'); header.stringCount = this.readU32(); this.debug && console.debug('stringCount:', header.stringCount); header.styleCount = this.readU32(); this.debug && console.debug('styleCount:', header.styleCount); header.flags = this.readU32(); this.debug && console.debug('flags:', header.flags); header.stringsStart = this.readU32(); this.debug && console.debug('stringsStart:', header.stringsStart); header.stylesStart = this.readU32(); this.debug && console.debug('stylesStart:', header.stylesStart); if (header.chunkType !== ChunkType.STRING_POOL) { throw new Error('Invalid string pool header'); } var offsets = []; for (var i = 0, l = header.stringCount; i < l; ++i) { this.debug && console.debug('offset:', i); offsets.push(this.readU32()); } var sorted = (header.flags & StringFlags.SORTED) === StringFlags.SORTED; this.debug && co