From 1bcbf524b3b557cb11200fbc5ccfd166380ce300 Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 6 Feb 2026 14:31:20 +0200 Subject: [PATCH] Upload --- LICENSE | 403 ++ README.md | 41 +- _agent.js | 13319 +++++++++++++++++++++++++++++++++++++++++++++ agent/hook.ts | 71 + clean.js | 4 + main.py | 238 + package.json | 20 + requirements.txt | 2 + tsconfig.json | 10 + 9 files changed, 14106 insertions(+), 2 deletions(-) create mode 100644 LICENSE create mode 100644 _agent.js create mode 100644 agent/hook.ts create mode 100644 clean.js create mode 100644 main.py create mode 100644 package.json create mode 100644 requirements.txt create mode 100644 tsconfig.json diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cfe676c --- /dev/null +++ b/LICENSE @@ -0,0 +1,403 @@ +Attribution-NonCommercial-NoDerivatives 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 +International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-NonCommercial-NoDerivatives 4.0 International Public +License ("Public License"). To the extent this Public License may be +interpreted as a contract, You are granted the Licensed Rights in +consideration of Your acceptance of these terms and conditions, and the +Licensor grants You such rights in consideration of benefits the +Licensor receives from making the Licensed Material available under +these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + c. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + d. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + e. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + f. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + g. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + h. NonCommercial means not primarily intended for or directed towards + commercial advantage or monetary compensation. For purposes of + this Public License, the exchange of the Licensed Material for + other material subject to Copyright and Similar Rights by digital + file-sharing or similar means is NonCommercial provided there is + no payment of monetary compensation in connection with the + exchange. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part, for NonCommercial purposes only; and + + b. produce and reproduce, but not Share, Adapted Material + for NonCommercial purposes only. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties, including when + the Licensed Material is used other than for NonCommercial + purposes. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material, You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + For the avoidance of doubt, You do not have permission under + this Public License to Share Adapted Material. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database for NonCommercial purposes + only and provided You do not Share Adapted Material; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. + diff --git a/README.md b/README.md index 0dab3df..15c5705 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,40 @@ -# AWP +# AWP - Android Widevine Proxy +ADB Proxy for Widevine challenges/licenses between the Android MediaDrm API and an App -Mirrored from LARLEY \ No newline at end of file +## Requirements +An Android Phone with the following: ++ USB debugging ++ ADB ++ frida-server running + +## Installation ++ Install the `requirements.txt` file + +## Usage +AWP is run per app, which should already be running when attempting to launch the program. \ +Keys are printed the specified format as soon as a challenge and license were received. + +> [!NOTE] +> This will not work if the app is not using the MediaDrm API + +``` +usage: main.py [-h] [--key-format {default,mp4decrypt,shaka-packager}] [--token-only] + +AWP - Android Widevine Proxy + +positional arguments: + The name of the app to intercept + Path of the widevine device + +options: + -h, --help show this help message and exit + --key-format {default,mp4decrypt,shaka-packager} + Format of printed keys + --token-only Only replace the token in the challenge (only if privacy mode is off) +``` + +## Agent Compilation +Run `npm run compile` to properly compile the frida script if you need to make any changes. + +## Demo +[demo.webm](https://github.com/user-attachments/assets/e50abd4b-c252-4927-b9cd-974e40d353d1) \ No newline at end of file diff --git a/_agent.js b/_agent.js new file mode 100644 index 0000000..476cfa7 --- /dev/null +++ b/_agent.js @@ -0,0 +1,13319 @@ +var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; + +// frida-shim:node_modules/@frida/base64-js/index.js +var lookup = []; +var revLookup = []; +var code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +for (let i = 0, len = code.length; i < len; ++i) { + lookup[i] = code[i]; + revLookup[code.charCodeAt(i)] = i; +} +revLookup["-".charCodeAt(0)] = 62; +revLookup["_".charCodeAt(0)] = 63; +function getLens(b64) { + const len = b64.length; + if (len % 4 > 0) { + throw new Error("Invalid string. Length must be a multiple of 4"); + } + let validLen = b64.indexOf("="); + if (validLen === -1) validLen = len; + const placeHoldersLen = validLen === len ? 0 : 4 - validLen % 4; + return [validLen, placeHoldersLen]; +} +function _byteLength(b64, validLen, placeHoldersLen) { + return (validLen + placeHoldersLen) * 3 / 4 - placeHoldersLen; +} +function toByteArray(b64) { + const lens = getLens(b64); + const validLen = lens[0]; + const placeHoldersLen = lens[1]; + const arr = new Uint8Array(_byteLength(b64, validLen, placeHoldersLen)); + let curByte = 0; + const len = placeHoldersLen > 0 ? validLen - 4 : validLen; + let i; + for (i = 0; i < len; i += 4) { + const tmp = revLookup[b64.charCodeAt(i)] << 18 | revLookup[b64.charCodeAt(i + 1)] << 12 | revLookup[b64.charCodeAt(i + 2)] << 6 | revLookup[b64.charCodeAt(i + 3)]; + arr[curByte++] = tmp >> 16 & 255; + arr[curByte++] = tmp >> 8 & 255; + arr[curByte++] = tmp & 255; + } + if (placeHoldersLen === 2) { + const tmp = revLookup[b64.charCodeAt(i)] << 2 | revLookup[b64.charCodeAt(i + 1)] >> 4; + arr[curByte++] = tmp & 255; + } + if (placeHoldersLen === 1) { + const tmp = revLookup[b64.charCodeAt(i)] << 10 | revLookup[b64.charCodeAt(i + 1)] << 4 | revLookup[b64.charCodeAt(i + 2)] >> 2; + arr[curByte++] = tmp >> 8 & 255; + arr[curByte++] = tmp & 255; + } + return arr; +} +function tripletToBase64(num) { + return lookup[num >> 18 & 63] + lookup[num >> 12 & 63] + lookup[num >> 6 & 63] + lookup[num & 63]; +} +function encodeChunk(uint8, start, end) { + const output = []; + for (let i = start; i < end; i += 3) { + const tmp = (uint8[i] << 16 & 16711680) + (uint8[i + 1] << 8 & 65280) + (uint8[i + 2] & 255); + output.push(tripletToBase64(tmp)); + } + return output.join(""); +} +function fromByteArray(uint8) { + const len = uint8.length; + const extraBytes = len % 3; + const parts = []; + const maxChunkLength = 16383; + for (let i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { + parts.push(encodeChunk(uint8, i, i + maxChunkLength > len2 ? len2 : i + maxChunkLength)); + } + if (extraBytes === 1) { + const tmp = uint8[len - 1]; + parts.push( + lookup[tmp >> 2] + lookup[tmp << 4 & 63] + "==" + ); + } else if (extraBytes === 2) { + const tmp = (uint8[len - 2] << 8) + uint8[len - 1]; + parts.push( + lookup[tmp >> 10] + lookup[tmp >> 4 & 63] + lookup[tmp << 2 & 63] + "=" + ); + } + return parts.join(""); +} + +// frida-shim:node_modules/@frida/ieee754/index.js +function read(buffer, offset, isLE, mLen, nBytes) { + let e, m; + const eLen = nBytes * 8 - mLen - 1; + const eMax = (1 << eLen) - 1; + const eBias = eMax >> 1; + let nBits = -7; + let i = isLE ? nBytes - 1 : 0; + const d = isLE ? -1 : 1; + let s = buffer[offset + i]; + i += d; + e = s & (1 << -nBits) - 1; + s >>= -nBits; + nBits += eLen; + while (nBits > 0) { + e = e * 256 + buffer[offset + i]; + i += d; + nBits -= 8; + } + m = e & (1 << -nBits) - 1; + e >>= -nBits; + nBits += mLen; + while (nBits > 0) { + m = m * 256 + buffer[offset + i]; + i += d; + nBits -= 8; + } + if (e === 0) { + e = 1 - eBias; + } else if (e === eMax) { + return m ? NaN : (s ? -1 : 1) * Infinity; + } else { + m = m + Math.pow(2, mLen); + e = e - eBias; + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen); +} +function write(buffer, value, offset, isLE, mLen, nBytes) { + let e, m, c; + let eLen = nBytes * 8 - mLen - 1; + const eMax = (1 << eLen) - 1; + const eBias = eMax >> 1; + const rt = mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0; + let i = isLE ? 0 : nBytes - 1; + const d = isLE ? 1 : -1; + const s = value < 0 || value === 0 && 1 / value < 0 ? 1 : 0; + value = Math.abs(value); + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0; + e = eMax; + } else { + e = Math.floor(Math.log(value) / Math.LN2); + if (value * (c = Math.pow(2, -e)) < 1) { + e--; + c *= 2; + } + if (e + eBias >= 1) { + value += rt / c; + } else { + value += rt * Math.pow(2, 1 - eBias); + } + if (value * c >= 2) { + e++; + c /= 2; + } + if (e + eBias >= eMax) { + m = 0; + e = eMax; + } else if (e + eBias >= 1) { + m = (value * c - 1) * Math.pow(2, mLen); + e = e + eBias; + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); + e = 0; + } + } + while (mLen >= 8) { + buffer[offset + i] = m & 255; + i += d; + m /= 256; + mLen -= 8; + } + e = e << mLen | m; + eLen += mLen; + while (eLen > 0) { + buffer[offset + i] = e & 255; + i += d; + e /= 256; + eLen -= 8; + } + buffer[offset + i - d] |= s * 128; +} + +// frida-shim:node_modules/@frida/buffer/index.js +var config = { + INSPECT_MAX_BYTES: 50 +}; +var K_MAX_LENGTH = 2147483647; +Buffer2.TYPED_ARRAY_SUPPORT = true; +Object.defineProperty(Buffer2.prototype, "parent", { + enumerable: true, + get: function() { + if (!Buffer2.isBuffer(this)) return void 0; + return this.buffer; + } +}); +Object.defineProperty(Buffer2.prototype, "offset", { + enumerable: true, + get: function() { + if (!Buffer2.isBuffer(this)) return void 0; + return this.byteOffset; + } +}); +function createBuffer(length) { + if (length > K_MAX_LENGTH) { + throw new RangeError('The value "' + length + '" is invalid for option "size"'); + } + const buf = new Uint8Array(length); + Object.setPrototypeOf(buf, Buffer2.prototype); + return buf; +} +function Buffer2(arg, encodingOrOffset, length) { + if (typeof arg === "number") { + if (typeof encodingOrOffset === "string") { + throw new TypeError( + 'The "string" argument must be of type string. Received type number' + ); + } + return allocUnsafe(arg); + } + return from(arg, encodingOrOffset, length); +} +Buffer2.poolSize = 8192; +function from(value, encodingOrOffset, length) { + if (typeof value === "string") { + return fromString(value, encodingOrOffset); + } + if (ArrayBuffer.isView(value)) { + return fromArrayView(value); + } + if (value == null) { + throw new TypeError( + "The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type " + typeof value + ); + } + if (value instanceof ArrayBuffer || value && value.buffer instanceof ArrayBuffer) { + return fromArrayBuffer(value, encodingOrOffset, length); + } + if (value instanceof SharedArrayBuffer || value && value.buffer instanceof SharedArrayBuffer) { + return fromArrayBuffer(value, encodingOrOffset, length); + } + if (typeof value === "number") { + throw new TypeError( + 'The "value" argument must not be of type number. Received type number' + ); + } + const valueOf = value.valueOf && value.valueOf(); + if (valueOf != null && valueOf !== value) { + return Buffer2.from(valueOf, encodingOrOffset, length); + } + const b = fromObject(value); + if (b) return b; + if (typeof Symbol !== "undefined" && Symbol.toPrimitive != null && typeof value[Symbol.toPrimitive] === "function") { + return Buffer2.from(value[Symbol.toPrimitive]("string"), encodingOrOffset, length); + } + throw new TypeError( + "The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type " + typeof value + ); +} +Buffer2.from = function(value, encodingOrOffset, length) { + return from(value, encodingOrOffset, length); +}; +Object.setPrototypeOf(Buffer2.prototype, Uint8Array.prototype); +Object.setPrototypeOf(Buffer2, Uint8Array); +function assertSize(size) { + if (typeof size !== "number") { + throw new TypeError('"size" argument must be of type number'); + } else if (size < 0) { + throw new RangeError('The value "' + size + '" is invalid for option "size"'); + } +} +function alloc(size, fill2, encoding) { + assertSize(size); + if (size <= 0) { + return createBuffer(size); + } + if (fill2 !== void 0) { + return typeof encoding === "string" ? createBuffer(size).fill(fill2, encoding) : createBuffer(size).fill(fill2); + } + return createBuffer(size); +} +Buffer2.alloc = function(size, fill2, encoding) { + return alloc(size, fill2, encoding); +}; +function allocUnsafe(size) { + assertSize(size); + return createBuffer(size < 0 ? 0 : checked(size) | 0); +} +Buffer2.allocUnsafe = function(size) { + return allocUnsafe(size); +}; +Buffer2.allocUnsafeSlow = function(size) { + return allocUnsafe(size); +}; +function fromString(string, encoding) { + if (typeof encoding !== "string" || encoding === "") { + encoding = "utf8"; + } + if (!Buffer2.isEncoding(encoding)) { + throw new TypeError("Unknown encoding: " + encoding); + } + const length = byteLength(string, encoding) | 0; + let buf = createBuffer(length); + const actual = buf.write(string, encoding); + if (actual !== length) { + buf = buf.slice(0, actual); + } + return buf; +} +function fromArrayLike(array) { + const length = array.length < 0 ? 0 : checked(array.length) | 0; + const buf = createBuffer(length); + for (let i = 0; i < length; i += 1) { + buf[i] = array[i] & 255; + } + return buf; +} +function fromArrayView(arrayView) { + if (arrayView instanceof Uint8Array) { + const copy2 = new Uint8Array(arrayView); + return fromArrayBuffer(copy2.buffer, copy2.byteOffset, copy2.byteLength); + } + return fromArrayLike(arrayView); +} +function fromArrayBuffer(array, byteOffset, length) { + if (byteOffset < 0 || array.byteLength < byteOffset) { + throw new RangeError('"offset" is outside of buffer bounds'); + } + if (array.byteLength < byteOffset + (length || 0)) { + throw new RangeError('"length" is outside of buffer bounds'); + } + let buf; + if (byteOffset === void 0 && length === void 0) { + buf = new Uint8Array(array); + } else if (length === void 0) { + buf = new Uint8Array(array, byteOffset); + } else { + buf = new Uint8Array(array, byteOffset, length); + } + Object.setPrototypeOf(buf, Buffer2.prototype); + return buf; +} +function fromObject(obj) { + if (Buffer2.isBuffer(obj)) { + const len = checked(obj.length) | 0; + const buf = createBuffer(len); + if (buf.length === 0) { + return buf; + } + obj.copy(buf, 0, 0, len); + return buf; + } + if (obj.length !== void 0) { + if (typeof obj.length !== "number" || Number.isNaN(obj.length)) { + return createBuffer(0); + } + return fromArrayLike(obj); + } + if (obj.type === "Buffer" && Array.isArray(obj.data)) { + return fromArrayLike(obj.data); + } +} +function checked(length) { + if (length >= K_MAX_LENGTH) { + throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x" + K_MAX_LENGTH.toString(16) + " bytes"); + } + return length | 0; +} +Buffer2.isBuffer = function isBuffer(b) { + return b != null && b._isBuffer === true && b !== Buffer2.prototype; +}; +Buffer2.compare = function compare(a, b) { + if (a instanceof Uint8Array) a = Buffer2.from(a, a.offset, a.byteLength); + if (b instanceof Uint8Array) b = Buffer2.from(b, b.offset, b.byteLength); + if (!Buffer2.isBuffer(a) || !Buffer2.isBuffer(b)) { + throw new TypeError( + 'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array' + ); + } + if (a === b) return 0; + let x = a.length; + let y = b.length; + for (let i = 0, len = Math.min(x, y); i < len; ++i) { + if (a[i] !== b[i]) { + x = a[i]; + y = b[i]; + break; + } + } + if (x < y) return -1; + if (y < x) return 1; + return 0; +}; +Buffer2.isEncoding = function isEncoding(encoding) { + switch (String(encoding).toLowerCase()) { + case "hex": + case "utf8": + case "utf-8": + case "ascii": + case "latin1": + case "binary": + case "base64": + case "ucs2": + case "ucs-2": + case "utf16le": + case "utf-16le": + return true; + default: + return false; + } +}; +Buffer2.concat = function concat(list, length) { + if (!Array.isArray(list)) { + throw new TypeError('"list" argument must be an Array of Buffers'); + } + if (list.length === 0) { + return Buffer2.alloc(0); + } + let i; + if (length === void 0) { + length = 0; + for (i = 0; i < list.length; ++i) { + length += list[i].length; + } + } + const buffer = Buffer2.allocUnsafe(length); + let pos = 0; + for (i = 0; i < list.length; ++i) { + let buf = list[i]; + if (buf instanceof Uint8Array) { + if (pos + buf.length > buffer.length) { + if (!Buffer2.isBuffer(buf)) { + buf = Buffer2.from(buf.buffer, buf.byteOffset, buf.byteLength); + } + buf.copy(buffer, pos); + } else { + Uint8Array.prototype.set.call( + buffer, + buf, + pos + ); + } + } else if (!Buffer2.isBuffer(buf)) { + throw new TypeError('"list" argument must be an Array of Buffers'); + } else { + buf.copy(buffer, pos); + } + pos += buf.length; + } + return buffer; +}; +function byteLength(string, encoding) { + if (Buffer2.isBuffer(string)) { + return string.length; + } + if (ArrayBuffer.isView(string) || string instanceof ArrayBuffer) { + return string.byteLength; + } + if (typeof string !== "string") { + throw new TypeError( + 'The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type ' + typeof string + ); + } + const len = string.length; + const mustMatch = arguments.length > 2 && arguments[2] === true; + if (!mustMatch && len === 0) return 0; + let loweredCase = false; + for (; ; ) { + switch (encoding) { + case "ascii": + case "latin1": + case "binary": + return len; + case "utf8": + case "utf-8": + return utf8ToBytes(string).length; + case "ucs2": + case "ucs-2": + case "utf16le": + case "utf-16le": + return len * 2; + case "hex": + return len >>> 1; + case "base64": + return base64ToBytes(string).length; + default: + if (loweredCase) { + return mustMatch ? -1 : utf8ToBytes(string).length; + } + encoding = ("" + encoding).toLowerCase(); + loweredCase = true; + } + } +} +Buffer2.byteLength = byteLength; +function slowToString(encoding, start, end) { + let loweredCase = false; + if (start === void 0 || start < 0) { + start = 0; + } + if (start > this.length) { + return ""; + } + if (end === void 0 || end > this.length) { + end = this.length; + } + if (end <= 0) { + return ""; + } + end >>>= 0; + start >>>= 0; + if (end <= start) { + return ""; + } + if (!encoding) encoding = "utf8"; + while (true) { + switch (encoding) { + case "hex": + return hexSlice(this, start, end); + case "utf8": + case "utf-8": + return utf8Slice(this, start, end); + case "ascii": + return asciiSlice(this, start, end); + case "latin1": + case "binary": + return latin1Slice(this, start, end); + case "base64": + return base64Slice(this, start, end); + case "ucs2": + case "ucs-2": + case "utf16le": + case "utf-16le": + return utf16leSlice(this, start, end); + default: + if (loweredCase) throw new TypeError("Unknown encoding: " + encoding); + encoding = (encoding + "").toLowerCase(); + loweredCase = true; + } + } +} +Buffer2.prototype._isBuffer = true; +function swap(b, n, m) { + const i = b[n]; + b[n] = b[m]; + b[m] = i; +} +Buffer2.prototype.swap16 = function swap16() { + const len = this.length; + if (len % 2 !== 0) { + throw new RangeError("Buffer size must be a multiple of 16-bits"); + } + for (let i = 0; i < len; i += 2) { + swap(this, i, i + 1); + } + return this; +}; +Buffer2.prototype.swap32 = function swap32() { + const len = this.length; + if (len % 4 !== 0) { + throw new RangeError("Buffer size must be a multiple of 32-bits"); + } + for (let i = 0; i < len; i += 4) { + swap(this, i, i + 3); + swap(this, i + 1, i + 2); + } + return this; +}; +Buffer2.prototype.swap64 = function swap64() { + const len = this.length; + if (len % 8 !== 0) { + throw new RangeError("Buffer size must be a multiple of 64-bits"); + } + for (let i = 0; i < len; i += 8) { + swap(this, i, i + 7); + swap(this, i + 1, i + 6); + swap(this, i + 2, i + 5); + swap(this, i + 3, i + 4); + } + return this; +}; +Buffer2.prototype.toString = function toString() { + const length = this.length; + if (length === 0) return ""; + if (arguments.length === 0) return utf8Slice(this, 0, length); + return slowToString.apply(this, arguments); +}; +Buffer2.prototype.toLocaleString = Buffer2.prototype.toString; +Buffer2.prototype.equals = function equals(b) { + if (!Buffer2.isBuffer(b)) throw new TypeError("Argument must be a Buffer"); + if (this === b) return true; + return Buffer2.compare(this, b) === 0; +}; +Buffer2.prototype.inspect = function inspect() { + let str = ""; + const max = config.INSPECT_MAX_BYTES; + str = this.toString("hex", 0, max).replace(/(.{2})/g, "$1 ").trim(); + if (this.length > max) str += " ... "; + return ""; +}; +Buffer2.prototype[Symbol.for("nodejs.util.inspect.custom")] = Buffer2.prototype.inspect; +Buffer2.prototype.compare = function compare2(target, start, end, thisStart, thisEnd) { + if (target instanceof Uint8Array) { + target = Buffer2.from(target, target.offset, target.byteLength); + } + if (!Buffer2.isBuffer(target)) { + throw new TypeError( + 'The "target" argument must be one of type Buffer or Uint8Array. Received type ' + typeof target + ); + } + if (start === void 0) { + start = 0; + } + if (end === void 0) { + end = target ? target.length : 0; + } + if (thisStart === void 0) { + thisStart = 0; + } + if (thisEnd === void 0) { + thisEnd = this.length; + } + if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { + throw new RangeError("out of range index"); + } + if (thisStart >= thisEnd && start >= end) { + return 0; + } + if (thisStart >= thisEnd) { + return -1; + } + if (start >= end) { + return 1; + } + start >>>= 0; + end >>>= 0; + thisStart >>>= 0; + thisEnd >>>= 0; + if (this === target) return 0; + let x = thisEnd - thisStart; + let y = end - start; + const len = Math.min(x, y); + const thisCopy = this.slice(thisStart, thisEnd); + const targetCopy = target.slice(start, end); + for (let i = 0; i < len; ++i) { + if (thisCopy[i] !== targetCopy[i]) { + x = thisCopy[i]; + y = targetCopy[i]; + break; + } + } + if (x < y) return -1; + if (y < x) return 1; + return 0; +}; +function bidirectionalIndexOf(buffer, val, byteOffset, encoding, dir) { + if (buffer.length === 0) return -1; + if (typeof byteOffset === "string") { + encoding = byteOffset; + byteOffset = 0; + } else if (byteOffset > 2147483647) { + byteOffset = 2147483647; + } else if (byteOffset < -2147483648) { + byteOffset = -2147483648; + } + byteOffset = +byteOffset; + if (Number.isNaN(byteOffset)) { + byteOffset = dir ? 0 : buffer.length - 1; + } + if (byteOffset < 0) byteOffset = buffer.length + byteOffset; + if (byteOffset >= buffer.length) { + if (dir) return -1; + else byteOffset = buffer.length - 1; + } else if (byteOffset < 0) { + if (dir) byteOffset = 0; + else return -1; + } + if (typeof val === "string") { + val = Buffer2.from(val, encoding); + } + if (Buffer2.isBuffer(val)) { + if (val.length === 0) { + return -1; + } + return arrayIndexOf(buffer, val, byteOffset, encoding, dir); + } else if (typeof val === "number") { + val = val & 255; + if (typeof Uint8Array.prototype.indexOf === "function") { + if (dir) { + return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset); + } else { + return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset); + } + } + return arrayIndexOf(buffer, [val], byteOffset, encoding, dir); + } + throw new TypeError("val must be string, number or Buffer"); +} +function arrayIndexOf(arr, val, byteOffset, encoding, dir) { + let indexSize = 1; + let arrLength = arr.length; + let valLength = val.length; + if (encoding !== void 0) { + encoding = String(encoding).toLowerCase(); + if (encoding === "ucs2" || encoding === "ucs-2" || encoding === "utf16le" || encoding === "utf-16le") { + if (arr.length < 2 || val.length < 2) { + return -1; + } + indexSize = 2; + arrLength /= 2; + valLength /= 2; + byteOffset /= 2; + } + } + function read2(buf, i2) { + if (indexSize === 1) { + return buf[i2]; + } else { + return buf.readUInt16BE(i2 * indexSize); + } + } + let i; + if (dir) { + let foundIndex = -1; + for (i = byteOffset; i < arrLength; i++) { + if (read2(arr, i) === read2(val, foundIndex === -1 ? 0 : i - foundIndex)) { + if (foundIndex === -1) foundIndex = i; + if (i - foundIndex + 1 === valLength) return foundIndex * indexSize; + } else { + if (foundIndex !== -1) i -= i - foundIndex; + foundIndex = -1; + } + } + } else { + if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength; + for (i = byteOffset; i >= 0; i--) { + let found = true; + for (let j = 0; j < valLength; j++) { + if (read2(arr, i + j) !== read2(val, j)) { + found = false; + break; + } + } + if (found) return i; + } + } + return -1; +} +Buffer2.prototype.includes = function includes(val, byteOffset, encoding) { + return this.indexOf(val, byteOffset, encoding) !== -1; +}; +Buffer2.prototype.indexOf = function indexOf(val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, true); +}; +Buffer2.prototype.lastIndexOf = function lastIndexOf(val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, false); +}; +function hexWrite(buf, string, offset, length) { + offset = Number(offset) || 0; + const remaining = buf.length - offset; + if (!length) { + length = remaining; + } else { + length = Number(length); + if (length > remaining) { + length = remaining; + } + } + const strLen = string.length; + if (length > strLen / 2) { + length = strLen / 2; + } + let i; + for (i = 0; i < length; ++i) { + const parsed = parseInt(string.substr(i * 2, 2), 16); + if (Number.isNaN(parsed)) return i; + buf[offset + i] = parsed; + } + return i; +} +function utf8Write(buf, string, offset, length) { + return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length); +} +function asciiWrite(buf, string, offset, length) { + return blitBuffer(asciiToBytes(string), buf, offset, length); +} +function base64Write(buf, string, offset, length) { + return blitBuffer(base64ToBytes(string), buf, offset, length); +} +function ucs2Write(buf, string, offset, length) { + return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length); +} +Buffer2.prototype.write = function write2(string, offset, length, encoding) { + if (offset === void 0) { + encoding = "utf8"; + length = this.length; + offset = 0; + } else if (length === void 0 && typeof offset === "string") { + encoding = offset; + length = this.length; + offset = 0; + } else if (isFinite(offset)) { + offset = offset >>> 0; + if (isFinite(length)) { + length = length >>> 0; + if (encoding === void 0) encoding = "utf8"; + } else { + encoding = length; + length = void 0; + } + } else { + throw new Error( + "Buffer.write(string, encoding, offset[, length]) is no longer supported" + ); + } + const remaining = this.length - offset; + if (length === void 0 || length > remaining) length = remaining; + if (string.length > 0 && (length < 0 || offset < 0) || offset > this.length) { + throw new RangeError("Attempt to write outside buffer bounds"); + } + if (!encoding) encoding = "utf8"; + let loweredCase = false; + for (; ; ) { + switch (encoding) { + case "hex": + return hexWrite(this, string, offset, length); + case "utf8": + case "utf-8": + return utf8Write(this, string, offset, length); + case "ascii": + case "latin1": + case "binary": + return asciiWrite(this, string, offset, length); + case "base64": + return base64Write(this, string, offset, length); + case "ucs2": + case "ucs-2": + case "utf16le": + case "utf-16le": + return ucs2Write(this, string, offset, length); + default: + if (loweredCase) throw new TypeError("Unknown encoding: " + encoding); + encoding = ("" + encoding).toLowerCase(); + loweredCase = true; + } + } +}; +Buffer2.prototype.toJSON = function toJSON() { + return { + type: "Buffer", + data: Array.prototype.slice.call(this._arr || this, 0) + }; +}; +function base64Slice(buf, start, end) { + if (start === 0 && end === buf.length) { + return fromByteArray(buf); + } else { + return fromByteArray(buf.slice(start, end)); + } +} +function utf8Slice(buf, start, end) { + end = Math.min(buf.length, end); + const res = []; + let i = start; + while (i < end) { + const firstByte = buf[i]; + let codePoint = null; + let bytesPerSequence = firstByte > 239 ? 4 : firstByte > 223 ? 3 : firstByte > 191 ? 2 : 1; + if (i + bytesPerSequence <= end) { + let secondByte, thirdByte, fourthByte, tempCodePoint; + switch (bytesPerSequence) { + case 1: + if (firstByte < 128) { + codePoint = firstByte; + } + break; + case 2: + secondByte = buf[i + 1]; + if ((secondByte & 192) === 128) { + tempCodePoint = (firstByte & 31) << 6 | secondByte & 63; + if (tempCodePoint > 127) { + codePoint = tempCodePoint; + } + } + break; + case 3: + secondByte = buf[i + 1]; + thirdByte = buf[i + 2]; + if ((secondByte & 192) === 128 && (thirdByte & 192) === 128) { + tempCodePoint = (firstByte & 15) << 12 | (secondByte & 63) << 6 | thirdByte & 63; + if (tempCodePoint > 2047 && (tempCodePoint < 55296 || tempCodePoint > 57343)) { + codePoint = tempCodePoint; + } + } + break; + case 4: + secondByte = buf[i + 1]; + thirdByte = buf[i + 2]; + fourthByte = buf[i + 3]; + if ((secondByte & 192) === 128 && (thirdByte & 192) === 128 && (fourthByte & 192) === 128) { + tempCodePoint = (firstByte & 15) << 18 | (secondByte & 63) << 12 | (thirdByte & 63) << 6 | fourthByte & 63; + if (tempCodePoint > 65535 && tempCodePoint < 1114112) { + codePoint = tempCodePoint; + } + } + } + } + if (codePoint === null) { + codePoint = 65533; + bytesPerSequence = 1; + } else if (codePoint > 65535) { + codePoint -= 65536; + res.push(codePoint >>> 10 & 1023 | 55296); + codePoint = 56320 | codePoint & 1023; + } + res.push(codePoint); + i += bytesPerSequence; + } + return decodeCodePointsArray(res); +} +var MAX_ARGUMENTS_LENGTH = 4096; +function decodeCodePointsArray(codePoints) { + const len = codePoints.length; + if (len <= MAX_ARGUMENTS_LENGTH) { + return String.fromCharCode.apply(String, codePoints); + } + let res = ""; + let i = 0; + while (i < len) { + res += String.fromCharCode.apply( + String, + codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) + ); + } + return res; +} +function asciiSlice(buf, start, end) { + let ret = ""; + end = Math.min(buf.length, end); + for (let i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i] & 127); + } + return ret; +} +function latin1Slice(buf, start, end) { + let ret = ""; + end = Math.min(buf.length, end); + for (let i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i]); + } + return ret; +} +function hexSlice(buf, start, end) { + const len = buf.length; + if (!start || start < 0) start = 0; + if (!end || end < 0 || end > len) end = len; + let out = ""; + for (let i = start; i < end; ++i) { + out += hexSliceLookupTable[buf[i]]; + } + return out; +} +function utf16leSlice(buf, start, end) { + const bytes = buf.slice(start, end); + let res = ""; + for (let i = 0; i < bytes.length - 1; i += 2) { + res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256); + } + return res; +} +Buffer2.prototype.slice = function slice(start, end) { + const len = this.length; + start = ~~start; + end = end === void 0 ? len : ~~end; + if (start < 0) { + start += len; + if (start < 0) start = 0; + } else if (start > len) { + start = len; + } + if (end < 0) { + end += len; + if (end < 0) end = 0; + } else if (end > len) { + end = len; + } + if (end < start) end = start; + const newBuf = this.subarray(start, end); + Object.setPrototypeOf(newBuf, Buffer2.prototype); + return newBuf; +}; +function checkOffset(offset, ext, length) { + if (offset % 1 !== 0 || offset < 0) throw new RangeError("offset is not uint"); + if (offset + ext > length) throw new RangeError("Trying to access beyond buffer length"); +} +Buffer2.prototype.readUintLE = Buffer2.prototype.readUIntLE = function readUIntLE(offset, byteLength2, noAssert) { + offset = offset >>> 0; + byteLength2 = byteLength2 >>> 0; + if (!noAssert) checkOffset(offset, byteLength2, this.length); + let val = this[offset]; + let mul = 1; + let i = 0; + while (++i < byteLength2 && (mul *= 256)) { + val += this[offset + i] * mul; + } + return val; +}; +Buffer2.prototype.readUintBE = Buffer2.prototype.readUIntBE = function readUIntBE(offset, byteLength2, noAssert) { + offset = offset >>> 0; + byteLength2 = byteLength2 >>> 0; + if (!noAssert) { + checkOffset(offset, byteLength2, this.length); + } + let val = this[offset + --byteLength2]; + let mul = 1; + while (byteLength2 > 0 && (mul *= 256)) { + val += this[offset + --byteLength2] * mul; + } + return val; +}; +Buffer2.prototype.readUint8 = Buffer2.prototype.readUInt8 = function readUInt8(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 1, this.length); + return this[offset]; +}; +Buffer2.prototype.readUint16LE = Buffer2.prototype.readUInt16LE = function readUInt16LE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 2, this.length); + return this[offset] | this[offset + 1] << 8; +}; +Buffer2.prototype.readUint16BE = Buffer2.prototype.readUInt16BE = function readUInt16BE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 2, this.length); + return this[offset] << 8 | this[offset + 1]; +}; +Buffer2.prototype.readUint32LE = Buffer2.prototype.readUInt32LE = function readUInt32LE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 4, this.length); + return (this[offset] | this[offset + 1] << 8 | this[offset + 2] << 16) + this[offset + 3] * 16777216; +}; +Buffer2.prototype.readUint32BE = Buffer2.prototype.readUInt32BE = function readUInt32BE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 4, this.length); + return this[offset] * 16777216 + (this[offset + 1] << 16 | this[offset + 2] << 8 | this[offset + 3]); +}; +Buffer2.prototype.readBigUInt64LE = function readBigUInt64LE(offset) { + offset = offset >>> 0; + validateNumber(offset, "offset"); + const first = this[offset]; + const last = this[offset + 7]; + if (first === void 0 || last === void 0) { + boundsError(offset, this.length - 8); + } + const lo = first + this[++offset] * 2 ** 8 + this[++offset] * 2 ** 16 + this[++offset] * 2 ** 24; + const hi = this[++offset] + this[++offset] * 2 ** 8 + this[++offset] * 2 ** 16 + last * 2 ** 24; + return BigInt(lo) + (BigInt(hi) << BigInt(32)); +}; +Buffer2.prototype.readBigUInt64BE = function readBigUInt64BE(offset) { + offset = offset >>> 0; + validateNumber(offset, "offset"); + const first = this[offset]; + const last = this[offset + 7]; + if (first === void 0 || last === void 0) { + boundsError(offset, this.length - 8); + } + const hi = first * 2 ** 24 + this[++offset] * 2 ** 16 + this[++offset] * 2 ** 8 + this[++offset]; + const lo = this[++offset] * 2 ** 24 + this[++offset] * 2 ** 16 + this[++offset] * 2 ** 8 + last; + return (BigInt(hi) << BigInt(32)) + BigInt(lo); +}; +Buffer2.prototype.readIntLE = function readIntLE(offset, byteLength2, noAssert) { + offset = offset >>> 0; + byteLength2 = byteLength2 >>> 0; + if (!noAssert) checkOffset(offset, byteLength2, this.length); + let val = this[offset]; + let mul = 1; + let i = 0; + while (++i < byteLength2 && (mul *= 256)) { + val += this[offset + i] * mul; + } + mul *= 128; + if (val >= mul) val -= Math.pow(2, 8 * byteLength2); + return val; +}; +Buffer2.prototype.readIntBE = function readIntBE(offset, byteLength2, noAssert) { + offset = offset >>> 0; + byteLength2 = byteLength2 >>> 0; + if (!noAssert) checkOffset(offset, byteLength2, this.length); + let i = byteLength2; + let mul = 1; + let val = this[offset + --i]; + while (i > 0 && (mul *= 256)) { + val += this[offset + --i] * mul; + } + mul *= 128; + if (val >= mul) val -= Math.pow(2, 8 * byteLength2); + return val; +}; +Buffer2.prototype.readInt8 = function readInt8(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 1, this.length); + if (!(this[offset] & 128)) return this[offset]; + return (255 - this[offset] + 1) * -1; +}; +Buffer2.prototype.readInt16LE = function readInt16LE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 2, this.length); + const val = this[offset] | this[offset + 1] << 8; + return val & 32768 ? val | 4294901760 : val; +}; +Buffer2.prototype.readInt16BE = function readInt16BE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 2, this.length); + const val = this[offset + 1] | this[offset] << 8; + return val & 32768 ? val | 4294901760 : val; +}; +Buffer2.prototype.readInt32LE = function readInt32LE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 4, this.length); + return this[offset] | this[offset + 1] << 8 | this[offset + 2] << 16 | this[offset + 3] << 24; +}; +Buffer2.prototype.readInt32BE = function readInt32BE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 4, this.length); + return this[offset] << 24 | this[offset + 1] << 16 | this[offset + 2] << 8 | this[offset + 3]; +}; +Buffer2.prototype.readBigInt64LE = function readBigInt64LE(offset) { + offset = offset >>> 0; + validateNumber(offset, "offset"); + const first = this[offset]; + const last = this[offset + 7]; + if (first === void 0 || last === void 0) { + boundsError(offset, this.length - 8); + } + const val = this[offset + 4] + this[offset + 5] * 2 ** 8 + this[offset + 6] * 2 ** 16 + (last << 24); + return (BigInt(val) << BigInt(32)) + BigInt(first + this[++offset] * 2 ** 8 + this[++offset] * 2 ** 16 + this[++offset] * 2 ** 24); +}; +Buffer2.prototype.readBigInt64BE = function readBigInt64BE(offset) { + offset = offset >>> 0; + validateNumber(offset, "offset"); + const first = this[offset]; + const last = this[offset + 7]; + if (first === void 0 || last === void 0) { + boundsError(offset, this.length - 8); + } + const val = (first << 24) + // Overflow + this[++offset] * 2 ** 16 + this[++offset] * 2 ** 8 + this[++offset]; + return (BigInt(val) << BigInt(32)) + BigInt(this[++offset] * 2 ** 24 + this[++offset] * 2 ** 16 + this[++offset] * 2 ** 8 + last); +}; +Buffer2.prototype.readFloatLE = function readFloatLE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 4, this.length); + return read(this, offset, true, 23, 4); +}; +Buffer2.prototype.readFloatBE = function readFloatBE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 4, this.length); + return read(this, offset, false, 23, 4); +}; +Buffer2.prototype.readDoubleLE = function readDoubleLE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 8, this.length); + return read(this, offset, true, 52, 8); +}; +Buffer2.prototype.readDoubleBE = function readDoubleBE(offset, noAssert) { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 8, this.length); + return read(this, offset, false, 52, 8); +}; +function checkInt(buf, value, offset, ext, max, min) { + if (!Buffer2.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance'); + if (value > max || value < min) throw new RangeError('"value" argument is out of bounds'); + if (offset + ext > buf.length) throw new RangeError("Index out of range"); +} +Buffer2.prototype.writeUintLE = Buffer2.prototype.writeUIntLE = function writeUIntLE(value, offset, byteLength2, noAssert) { + value = +value; + offset = offset >>> 0; + byteLength2 = byteLength2 >>> 0; + if (!noAssert) { + const maxBytes = Math.pow(2, 8 * byteLength2) - 1; + checkInt(this, value, offset, byteLength2, maxBytes, 0); + } + let mul = 1; + let i = 0; + this[offset] = value & 255; + while (++i < byteLength2 && (mul *= 256)) { + this[offset + i] = value / mul & 255; + } + return offset + byteLength2; +}; +Buffer2.prototype.writeUintBE = Buffer2.prototype.writeUIntBE = function writeUIntBE(value, offset, byteLength2, noAssert) { + value = +value; + offset = offset >>> 0; + byteLength2 = byteLength2 >>> 0; + if (!noAssert) { + const maxBytes = Math.pow(2, 8 * byteLength2) - 1; + checkInt(this, value, offset, byteLength2, maxBytes, 0); + } + let i = byteLength2 - 1; + let mul = 1; + this[offset + i] = value & 255; + while (--i >= 0 && (mul *= 256)) { + this[offset + i] = value / mul & 255; + } + return offset + byteLength2; +}; +Buffer2.prototype.writeUint8 = Buffer2.prototype.writeUInt8 = function writeUInt8(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 1, 255, 0); + this[offset] = value & 255; + return offset + 1; +}; +Buffer2.prototype.writeUint16LE = Buffer2.prototype.writeUInt16LE = function writeUInt16LE(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 2, 65535, 0); + this[offset] = value & 255; + this[offset + 1] = value >>> 8; + return offset + 2; +}; +Buffer2.prototype.writeUint16BE = Buffer2.prototype.writeUInt16BE = function writeUInt16BE(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 2, 65535, 0); + this[offset] = value >>> 8; + this[offset + 1] = value & 255; + return offset + 2; +}; +Buffer2.prototype.writeUint32LE = Buffer2.prototype.writeUInt32LE = function writeUInt32LE(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 4, 4294967295, 0); + this[offset + 3] = value >>> 24; + this[offset + 2] = value >>> 16; + this[offset + 1] = value >>> 8; + this[offset] = value & 255; + return offset + 4; +}; +Buffer2.prototype.writeUint32BE = Buffer2.prototype.writeUInt32BE = function writeUInt32BE(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 4, 4294967295, 0); + this[offset] = value >>> 24; + this[offset + 1] = value >>> 16; + this[offset + 2] = value >>> 8; + this[offset + 3] = value & 255; + return offset + 4; +}; +function wrtBigUInt64LE(buf, value, offset, min, max) { + checkIntBI(value, min, max, buf, offset, 7); + let lo = Number(value & BigInt(4294967295)); + buf[offset++] = lo; + lo = lo >> 8; + buf[offset++] = lo; + lo = lo >> 8; + buf[offset++] = lo; + lo = lo >> 8; + buf[offset++] = lo; + let hi = Number(value >> BigInt(32) & BigInt(4294967295)); + buf[offset++] = hi; + hi = hi >> 8; + buf[offset++] = hi; + hi = hi >> 8; + buf[offset++] = hi; + hi = hi >> 8; + buf[offset++] = hi; + return offset; +} +function wrtBigUInt64BE(buf, value, offset, min, max) { + checkIntBI(value, min, max, buf, offset, 7); + let lo = Number(value & BigInt(4294967295)); + buf[offset + 7] = lo; + lo = lo >> 8; + buf[offset + 6] = lo; + lo = lo >> 8; + buf[offset + 5] = lo; + lo = lo >> 8; + buf[offset + 4] = lo; + let hi = Number(value >> BigInt(32) & BigInt(4294967295)); + buf[offset + 3] = hi; + hi = hi >> 8; + buf[offset + 2] = hi; + hi = hi >> 8; + buf[offset + 1] = hi; + hi = hi >> 8; + buf[offset] = hi; + return offset + 8; +} +Buffer2.prototype.writeBigUInt64LE = function writeBigUInt64LE(value, offset = 0) { + return wrtBigUInt64LE(this, value, offset, BigInt(0), BigInt("0xffffffffffffffff")); +}; +Buffer2.prototype.writeBigUInt64BE = function writeBigUInt64BE(value, offset = 0) { + return wrtBigUInt64BE(this, value, offset, BigInt(0), BigInt("0xffffffffffffffff")); +}; +Buffer2.prototype.writeIntLE = function writeIntLE(value, offset, byteLength2, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) { + const limit = Math.pow(2, 8 * byteLength2 - 1); + checkInt(this, value, offset, byteLength2, limit - 1, -limit); + } + let i = 0; + let mul = 1; + let sub = 0; + this[offset] = value & 255; + while (++i < byteLength2 && (mul *= 256)) { + if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { + sub = 1; + } + this[offset + i] = (value / mul >> 0) - sub & 255; + } + return offset + byteLength2; +}; +Buffer2.prototype.writeIntBE = function writeIntBE(value, offset, byteLength2, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) { + const limit = Math.pow(2, 8 * byteLength2 - 1); + checkInt(this, value, offset, byteLength2, limit - 1, -limit); + } + let i = byteLength2 - 1; + let mul = 1; + let sub = 0; + this[offset + i] = value & 255; + while (--i >= 0 && (mul *= 256)) { + if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { + sub = 1; + } + this[offset + i] = (value / mul >> 0) - sub & 255; + } + return offset + byteLength2; +}; +Buffer2.prototype.writeInt8 = function writeInt8(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 1, 127, -128); + if (value < 0) value = 255 + value + 1; + this[offset] = value & 255; + return offset + 1; +}; +Buffer2.prototype.writeInt16LE = function writeInt16LE(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 2, 32767, -32768); + this[offset] = value & 255; + this[offset + 1] = value >>> 8; + return offset + 2; +}; +Buffer2.prototype.writeInt16BE = function writeInt16BE(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 2, 32767, -32768); + this[offset] = value >>> 8; + this[offset + 1] = value & 255; + return offset + 2; +}; +Buffer2.prototype.writeInt32LE = function writeInt32LE(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 4, 2147483647, -2147483648); + this[offset] = value & 255; + this[offset + 1] = value >>> 8; + this[offset + 2] = value >>> 16; + this[offset + 3] = value >>> 24; + return offset + 4; +}; +Buffer2.prototype.writeInt32BE = function writeInt32BE(value, offset, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 4, 2147483647, -2147483648); + if (value < 0) value = 4294967295 + value + 1; + this[offset] = value >>> 24; + this[offset + 1] = value >>> 16; + this[offset + 2] = value >>> 8; + this[offset + 3] = value & 255; + return offset + 4; +}; +Buffer2.prototype.writeBigInt64LE = function writeBigInt64LE(value, offset = 0) { + return wrtBigUInt64LE(this, value, offset, -BigInt("0x8000000000000000"), BigInt("0x7fffffffffffffff")); +}; +Buffer2.prototype.writeBigInt64BE = function writeBigInt64BE(value, offset = 0) { + return wrtBigUInt64BE(this, value, offset, -BigInt("0x8000000000000000"), BigInt("0x7fffffffffffffff")); +}; +function checkIEEE754(buf, value, offset, ext, max, min) { + if (offset + ext > buf.length) throw new RangeError("Index out of range"); + if (offset < 0) throw new RangeError("Index out of range"); +} +function writeFloat(buf, value, offset, littleEndian, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) { + checkIEEE754(buf, value, offset, 4, 34028234663852886e22, -34028234663852886e22); + } + write(buf, value, offset, littleEndian, 23, 4); + return offset + 4; +} +Buffer2.prototype.writeFloatLE = function writeFloatLE(value, offset, noAssert) { + return writeFloat(this, value, offset, true, noAssert); +}; +Buffer2.prototype.writeFloatBE = function writeFloatBE(value, offset, noAssert) { + return writeFloat(this, value, offset, false, noAssert); +}; +function writeDouble(buf, value, offset, littleEndian, noAssert) { + value = +value; + offset = offset >>> 0; + if (!noAssert) { + checkIEEE754(buf, value, offset, 8, 17976931348623157e292, -17976931348623157e292); + } + write(buf, value, offset, littleEndian, 52, 8); + return offset + 8; +} +Buffer2.prototype.writeDoubleLE = function writeDoubleLE(value, offset, noAssert) { + return writeDouble(this, value, offset, true, noAssert); +}; +Buffer2.prototype.writeDoubleBE = function writeDoubleBE(value, offset, noAssert) { + return writeDouble(this, value, offset, false, noAssert); +}; +Buffer2.prototype.copy = function copy(target, targetStart, start, end) { + if (!Buffer2.isBuffer(target)) throw new TypeError("argument should be a Buffer"); + if (!start) start = 0; + if (!end && end !== 0) end = this.length; + if (targetStart >= target.length) targetStart = target.length; + if (!targetStart) targetStart = 0; + if (end > 0 && end < start) end = start; + if (end === start) return 0; + if (target.length === 0 || this.length === 0) return 0; + if (targetStart < 0) { + throw new RangeError("targetStart out of bounds"); + } + if (start < 0 || start >= this.length) throw new RangeError("Index out of range"); + if (end < 0) throw new RangeError("sourceEnd out of bounds"); + if (end > this.length) end = this.length; + if (target.length - targetStart < end - start) { + end = target.length - targetStart + start; + } + const len = end - start; + if (this === target) { + this.copyWithin(targetStart, start, end); + } else { + Uint8Array.prototype.set.call( + target, + this.subarray(start, end), + targetStart + ); + } + return len; +}; +Buffer2.prototype.fill = function fill(val, start, end, encoding) { + if (typeof val === "string") { + if (typeof start === "string") { + encoding = start; + start = 0; + end = this.length; + } else if (typeof end === "string") { + encoding = end; + end = this.length; + } + if (encoding !== void 0 && typeof encoding !== "string") { + throw new TypeError("encoding must be a string"); + } + if (typeof encoding === "string" && !Buffer2.isEncoding(encoding)) { + throw new TypeError("Unknown encoding: " + encoding); + } + if (val.length === 1) { + const code3 = val.charCodeAt(0); + if (encoding === "utf8" && code3 < 128 || encoding === "latin1") { + val = code3; + } + } + } else if (typeof val === "number") { + val = val & 255; + } else if (typeof val === "boolean") { + val = Number(val); + } + if (start < 0 || this.length < start || this.length < end) { + throw new RangeError("Out of range index"); + } + if (end <= start) { + return this; + } + start = start >>> 0; + end = end === void 0 ? this.length : end >>> 0; + if (!val) val = 0; + let i; + if (typeof val === "number") { + for (i = start; i < end; ++i) { + this[i] = val; + } + } else { + const bytes = Buffer2.isBuffer(val) ? val : Buffer2.from(val, encoding); + const len = bytes.length; + if (len === 0) { + throw new TypeError('The value "' + val + '" is invalid for argument "value"'); + } + for (i = 0; i < end - start; ++i) { + this[i + start] = bytes[i % len]; + } + } + return this; +}; +var errors = {}; +function E(sym, getMessage, Base) { + errors[sym] = class NodeError extends Base { + constructor() { + super(); + Object.defineProperty(this, "message", { + value: getMessage.apply(this, arguments), + writable: true, + configurable: true + }); + this.name = `${this.name} [${sym}]`; + this.stack; + delete this.name; + } + get code() { + return sym; + } + set code(value) { + Object.defineProperty(this, "code", { + configurable: true, + enumerable: true, + value, + writable: true + }); + } + toString() { + return `${this.name} [${sym}]: ${this.message}`; + } + }; +} +E( + "ERR_BUFFER_OUT_OF_BOUNDS", + function(name) { + if (name) { + return `${name} is outside of buffer bounds`; + } + return "Attempt to access memory outside buffer bounds"; + }, + RangeError +); +E( + "ERR_INVALID_ARG_TYPE", + function(name, actual) { + return `The "${name}" argument must be of type number. Received type ${typeof actual}`; + }, + TypeError +); +E( + "ERR_OUT_OF_RANGE", + function(str, range, input) { + let msg = `The value of "${str}" is out of range.`; + let received = input; + if (Number.isInteger(input) && Math.abs(input) > 2 ** 32) { + received = addNumericalSeparator(String(input)); + } else if (typeof input === "bigint") { + received = String(input); + if (input > BigInt(2) ** BigInt(32) || input < -(BigInt(2) ** BigInt(32))) { + received = addNumericalSeparator(received); + } + received += "n"; + } + msg += ` It must be ${range}. Received ${received}`; + return msg; + }, + RangeError +); +function addNumericalSeparator(val) { + let res = ""; + let i = val.length; + const start = val[0] === "-" ? 1 : 0; + for (; i >= start + 4; i -= 3) { + res = `_${val.slice(i - 3, i)}${res}`; + } + return `${val.slice(0, i)}${res}`; +} +function checkBounds(buf, offset, byteLength2) { + validateNumber(offset, "offset"); + if (buf[offset] === void 0 || buf[offset + byteLength2] === void 0) { + boundsError(offset, buf.length - (byteLength2 + 1)); + } +} +function checkIntBI(value, min, max, buf, offset, byteLength2) { + if (value > max || value < min) { + const n = typeof min === "bigint" ? "n" : ""; + let range; + if (byteLength2 > 3) { + if (min === 0 || min === BigInt(0)) { + range = `>= 0${n} and < 2${n} ** ${(byteLength2 + 1) * 8}${n}`; + } else { + range = `>= -(2${n} ** ${(byteLength2 + 1) * 8 - 1}${n}) and < 2 ** ${(byteLength2 + 1) * 8 - 1}${n}`; + } + } else { + range = `>= ${min}${n} and <= ${max}${n}`; + } + throw new errors.ERR_OUT_OF_RANGE("value", range, value); + } + checkBounds(buf, offset, byteLength2); +} +function validateNumber(value, name) { + if (typeof value !== "number") { + throw new errors.ERR_INVALID_ARG_TYPE(name, "number", value); + } +} +function boundsError(value, length, type) { + if (Math.floor(value) !== value) { + validateNumber(value, type); + throw new errors.ERR_OUT_OF_RANGE(type || "offset", "an integer", value); + } + if (length < 0) { + throw new errors.ERR_BUFFER_OUT_OF_BOUNDS(); + } + throw new errors.ERR_OUT_OF_RANGE( + type || "offset", + `>= ${type ? 1 : 0} and <= ${length}`, + value + ); +} +var INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g; +function base64clean(str) { + str = str.split("=")[0]; + str = str.trim().replace(INVALID_BASE64_RE, ""); + if (str.length < 2) return ""; + while (str.length % 4 !== 0) { + str = str + "="; + } + return str; +} +function utf8ToBytes(string, units) { + units = units || Infinity; + let codePoint; + const length = string.length; + let leadSurrogate = null; + const bytes = []; + for (let i = 0; i < length; ++i) { + codePoint = string.charCodeAt(i); + if (codePoint > 55295 && codePoint < 57344) { + if (!leadSurrogate) { + if (codePoint > 56319) { + if ((units -= 3) > -1) bytes.push(239, 191, 189); + continue; + } else if (i + 1 === length) { + if ((units -= 3) > -1) bytes.push(239, 191, 189); + continue; + } + leadSurrogate = codePoint; + continue; + } + if (codePoint < 56320) { + if ((units -= 3) > -1) bytes.push(239, 191, 189); + leadSurrogate = codePoint; + continue; + } + codePoint = (leadSurrogate - 55296 << 10 | codePoint - 56320) + 65536; + } else if (leadSurrogate) { + if ((units -= 3) > -1) bytes.push(239, 191, 189); + } + leadSurrogate = null; + if (codePoint < 128) { + if ((units -= 1) < 0) break; + bytes.push(codePoint); + } else if (codePoint < 2048) { + if ((units -= 2) < 0) break; + bytes.push( + codePoint >> 6 | 192, + codePoint & 63 | 128 + ); + } else if (codePoint < 65536) { + if ((units -= 3) < 0) break; + bytes.push( + codePoint >> 12 | 224, + codePoint >> 6 & 63 | 128, + codePoint & 63 | 128 + ); + } else if (codePoint < 1114112) { + if ((units -= 4) < 0) break; + bytes.push( + codePoint >> 18 | 240, + codePoint >> 12 & 63 | 128, + codePoint >> 6 & 63 | 128, + codePoint & 63 | 128 + ); + } else { + throw new Error("Invalid code point"); + } + } + return bytes; +} +function asciiToBytes(str) { + const byteArray = []; + for (let i = 0; i < str.length; ++i) { + byteArray.push(str.charCodeAt(i) & 255); + } + return byteArray; +} +function utf16leToBytes(str, units) { + let c, hi, lo; + const byteArray = []; + for (let i = 0; i < str.length; ++i) { + if ((units -= 2) < 0) break; + c = str.charCodeAt(i); + hi = c >> 8; + lo = c % 256; + byteArray.push(lo); + byteArray.push(hi); + } + return byteArray; +} +function base64ToBytes(str) { + return toByteArray(base64clean(str)); +} +function blitBuffer(src, dst, offset, length) { + let i; + for (i = 0; i < length; ++i) { + if (i + offset >= dst.length || i >= src.length) break; + dst[i + offset] = src[i]; + } + return i; +} +var hexSliceLookupTable = function() { + const alphabet = "0123456789abcdef"; + const table = new Array(256); + for (let i = 0; i < 16; ++i) { + const i16 = i * 16; + for (let j = 0; j < 16; ++j) { + table[i16 + j] = alphabet[i] + alphabet[j]; + } + } + return table; +}(); + +// node_modules/frida-java-bridge/lib/android.js +var android_exports = {}; +__export(android_exports, { + ArtMethod: () => ArtMethod, + ArtStackVisitor: () => ArtStackVisitor, + DVM_JNI_ENV_OFFSET_SELF: () => DVM_JNI_ENV_OFFSET_SELF, + HandleVector: () => HandleVector, + VariableSizedHandleScope: () => VariableSizedHandleScope, + backtrace: () => backtrace, + deoptimizeBootImage: () => deoptimizeBootImage, + deoptimizeEverything: () => deoptimizeEverything, + deoptimizeMethod: () => deoptimizeMethod, + ensureClassInitialized: () => ensureClassInitialized, + getAndroidApiLevel: () => getAndroidApiLevel, + getAndroidVersion: () => getAndroidVersion, + getApi: () => getApi, + getArtClassSpec: () => getArtClassSpec, + getArtFieldSpec: () => getArtFieldSpec, + getArtMethodSpec: () => getArtMethodSpec, + getArtThreadFromEnv: () => getArtThreadFromEnv, + getArtThreadSpec: () => getArtThreadSpec, + makeArtClassLoaderVisitor: () => makeArtClassLoaderVisitor, + makeArtClassVisitor: () => makeArtClassVisitor, + makeMethodMangler: () => makeMethodMangler, + makeObjectVisitorPredicate: () => makeObjectVisitorPredicate, + revertGlobalPatches: () => revertGlobalPatches, + translateMethod: () => translateMethod, + withAllArtThreadsSuspended: () => withAllArtThreadsSuspended, + withRunnableArtThread: () => withRunnableArtThread +}); + +// node_modules/frida-java-bridge/lib/alloc.js +var { + pageSize, + pointerSize +} = Process; +var CodeAllocator = class { + constructor(sliceSize) { + this.sliceSize = sliceSize; + this.slicesPerPage = pageSize / sliceSize; + this.pages = []; + this.free = []; + } + allocateSlice(spec, alignment) { + const anyLocation = spec.near === void 0; + const anyAlignment = alignment === 1; + if (anyLocation && anyAlignment) { + const slice2 = this.free.pop(); + if (slice2 !== void 0) { + return slice2; + } + } else if (alignment < pageSize) { + const { free } = this; + const n = free.length; + const alignMask = anyAlignment ? null : ptr(alignment - 1); + for (let i = 0; i !== n; i++) { + const slice2 = free[i]; + const satisfiesLocation = anyLocation || this._isSliceNear(slice2, spec); + const satisfiesAlignment = anyAlignment || slice2.and(alignMask).isNull(); + if (satisfiesLocation && satisfiesAlignment) { + return free.splice(i, 1)[0]; + } + } + } + return this._allocatePage(spec); + } + _allocatePage(spec) { + const page = Memory.alloc(pageSize, spec); + const { sliceSize, slicesPerPage } = this; + for (let i = 1; i !== slicesPerPage; i++) { + const slice2 = page.add(i * sliceSize); + this.free.push(slice2); + } + this.pages.push(page); + return page; + } + _isSliceNear(slice2, spec) { + const sliceEnd = slice2.add(this.sliceSize); + const { near, maxDistance } = spec; + const startDistance = abs(near.sub(slice2)); + const endDistance = abs(near.sub(sliceEnd)); + return startDistance.compare(maxDistance) <= 0 && endDistance.compare(maxDistance) <= 0; + } + freeSlice(slice2) { + this.free.push(slice2); + } +}; +function abs(nptr) { + const shmt = pointerSize === 4 ? 31 : 63; + const mask = ptr(1).shl(shmt).not(); + return nptr.and(mask); +} +function makeAllocator(sliceSize) { + return new CodeAllocator(sliceSize); +} + +// node_modules/frida-java-bridge/lib/result.js +var JNI_OK = 0; +function checkJniResult(name, result) { + if (result !== JNI_OK) { + throw new Error(name + " failed: " + result); + } +} + +// node_modules/frida-java-bridge/lib/jvmti.js +var jvmtiVersion = { + v1_0: 805371904, + v1_2: 805372416 +}; +var jvmtiCapabilities = { + canTagObjects: 1 +}; +var { pointerSize: pointerSize2 } = Process; +var nativeFunctionOptions = { + exceptions: "propagate" +}; +function EnvJvmti(handle, vm3) { + this.handle = handle; + this.vm = vm3; + this.vtable = handle.readPointer(); +} +EnvJvmti.prototype.deallocate = proxy(47, "int32", ["pointer", "pointer"], function(impl, mem) { + return impl(this.handle, mem); +}); +EnvJvmti.prototype.getLoadedClasses = proxy(78, "int32", ["pointer", "pointer", "pointer"], function(impl, classCountPtr, classesPtr) { + const result = impl(this.handle, classCountPtr, classesPtr); + checkJniResult("EnvJvmti::getLoadedClasses", result); +}); +EnvJvmti.prototype.iterateOverInstancesOfClass = proxy(112, "int32", ["pointer", "pointer", "int", "pointer", "pointer"], function(impl, klass, objectFilter, heapObjectCallback, userData) { + const result = impl(this.handle, klass, objectFilter, heapObjectCallback, userData); + checkJniResult("EnvJvmti::iterateOverInstancesOfClass", result); +}); +EnvJvmti.prototype.getObjectsWithTags = proxy(114, "int32", ["pointer", "int", "pointer", "pointer", "pointer", "pointer"], function(impl, tagCount, tags, countPtr, objectResultPtr, tagResultPtr) { + const result = impl(this.handle, tagCount, tags, countPtr, objectResultPtr, tagResultPtr); + checkJniResult("EnvJvmti::getObjectsWithTags", result); +}); +EnvJvmti.prototype.addCapabilities = proxy(142, "int32", ["pointer", "pointer"], function(impl, capabilitiesPtr) { + return impl(this.handle, capabilitiesPtr); +}); +function proxy(offset, retType, argTypes, wrapper) { + let impl = null; + return function() { + if (impl === null) { + impl = new NativeFunction(this.vtable.add((offset - 1) * pointerSize2).readPointer(), retType, argTypes, nativeFunctionOptions); + } + let args = [impl]; + args = args.concat.apply(args, arguments); + return wrapper.apply(this, args); + }; +} + +// node_modules/frida-java-bridge/lib/machine-code.js +function parseInstructionsAt(address, tryParse, { limit }) { + let cursor = address; + let prevInsn = null; + for (let i = 0; i !== limit; i++) { + const insn = Instruction.parse(cursor); + const value = tryParse(insn, prevInsn); + if (value !== null) { + return value; + } + cursor = insn.next; + prevInsn = insn; + } + return null; +} + +// node_modules/frida-java-bridge/lib/memoize.js +function memoize(compute) { + let value = null; + let computed = false; + return function(...args) { + if (!computed) { + value = compute(...args); + computed = true; + } + return value; + }; +} + +// node_modules/frida-java-bridge/lib/env.js +function Env(handle, vm3) { + this.handle = handle; + this.vm = vm3; +} +var pointerSize3 = Process.pointerSize; +var JNI_ABORT = 2; +var CALL_CONSTRUCTOR_METHOD_OFFSET = 28; +var CALL_OBJECT_METHOD_OFFSET = 34; +var CALL_BOOLEAN_METHOD_OFFSET = 37; +var CALL_BYTE_METHOD_OFFSET = 40; +var CALL_CHAR_METHOD_OFFSET = 43; +var CALL_SHORT_METHOD_OFFSET = 46; +var CALL_INT_METHOD_OFFSET = 49; +var CALL_LONG_METHOD_OFFSET = 52; +var CALL_FLOAT_METHOD_OFFSET = 55; +var CALL_DOUBLE_METHOD_OFFSET = 58; +var CALL_VOID_METHOD_OFFSET = 61; +var CALL_NONVIRTUAL_OBJECT_METHOD_OFFSET = 64; +var CALL_NONVIRTUAL_BOOLEAN_METHOD_OFFSET = 67; +var CALL_NONVIRTUAL_BYTE_METHOD_OFFSET = 70; +var CALL_NONVIRTUAL_CHAR_METHOD_OFFSET = 73; +var CALL_NONVIRTUAL_SHORT_METHOD_OFFSET = 76; +var CALL_NONVIRTUAL_INT_METHOD_OFFSET = 79; +var CALL_NONVIRTUAL_LONG_METHOD_OFFSET = 82; +var CALL_NONVIRTUAL_FLOAT_METHOD_OFFSET = 85; +var CALL_NONVIRTUAL_DOUBLE_METHOD_OFFSET = 88; +var CALL_NONVIRTUAL_VOID_METHOD_OFFSET = 91; +var CALL_STATIC_OBJECT_METHOD_OFFSET = 114; +var CALL_STATIC_BOOLEAN_METHOD_OFFSET = 117; +var CALL_STATIC_BYTE_METHOD_OFFSET = 120; +var CALL_STATIC_CHAR_METHOD_OFFSET = 123; +var CALL_STATIC_SHORT_METHOD_OFFSET = 126; +var CALL_STATIC_INT_METHOD_OFFSET = 129; +var CALL_STATIC_LONG_METHOD_OFFSET = 132; +var CALL_STATIC_FLOAT_METHOD_OFFSET = 135; +var CALL_STATIC_DOUBLE_METHOD_OFFSET = 138; +var CALL_STATIC_VOID_METHOD_OFFSET = 141; +var GET_OBJECT_FIELD_OFFSET = 95; +var GET_BOOLEAN_FIELD_OFFSET = 96; +var GET_BYTE_FIELD_OFFSET = 97; +var GET_CHAR_FIELD_OFFSET = 98; +var GET_SHORT_FIELD_OFFSET = 99; +var GET_INT_FIELD_OFFSET = 100; +var GET_LONG_FIELD_OFFSET = 101; +var GET_FLOAT_FIELD_OFFSET = 102; +var GET_DOUBLE_FIELD_OFFSET = 103; +var SET_OBJECT_FIELD_OFFSET = 104; +var SET_BOOLEAN_FIELD_OFFSET = 105; +var SET_BYTE_FIELD_OFFSET = 106; +var SET_CHAR_FIELD_OFFSET = 107; +var SET_SHORT_FIELD_OFFSET = 108; +var SET_INT_FIELD_OFFSET = 109; +var SET_LONG_FIELD_OFFSET = 110; +var SET_FLOAT_FIELD_OFFSET = 111; +var SET_DOUBLE_FIELD_OFFSET = 112; +var GET_STATIC_OBJECT_FIELD_OFFSET = 145; +var GET_STATIC_BOOLEAN_FIELD_OFFSET = 146; +var GET_STATIC_BYTE_FIELD_OFFSET = 147; +var GET_STATIC_CHAR_FIELD_OFFSET = 148; +var GET_STATIC_SHORT_FIELD_OFFSET = 149; +var GET_STATIC_INT_FIELD_OFFSET = 150; +var GET_STATIC_LONG_FIELD_OFFSET = 151; +var GET_STATIC_FLOAT_FIELD_OFFSET = 152; +var GET_STATIC_DOUBLE_FIELD_OFFSET = 153; +var SET_STATIC_OBJECT_FIELD_OFFSET = 154; +var SET_STATIC_BOOLEAN_FIELD_OFFSET = 155; +var SET_STATIC_BYTE_FIELD_OFFSET = 156; +var SET_STATIC_CHAR_FIELD_OFFSET = 157; +var SET_STATIC_SHORT_FIELD_OFFSET = 158; +var SET_STATIC_INT_FIELD_OFFSET = 159; +var SET_STATIC_LONG_FIELD_OFFSET = 160; +var SET_STATIC_FLOAT_FIELD_OFFSET = 161; +var SET_STATIC_DOUBLE_FIELD_OFFSET = 162; +var callMethodOffset = { + pointer: CALL_OBJECT_METHOD_OFFSET, + uint8: CALL_BOOLEAN_METHOD_OFFSET, + int8: CALL_BYTE_METHOD_OFFSET, + uint16: CALL_CHAR_METHOD_OFFSET, + int16: CALL_SHORT_METHOD_OFFSET, + int32: CALL_INT_METHOD_OFFSET, + int64: CALL_LONG_METHOD_OFFSET, + float: CALL_FLOAT_METHOD_OFFSET, + double: CALL_DOUBLE_METHOD_OFFSET, + void: CALL_VOID_METHOD_OFFSET +}; +var callNonvirtualMethodOffset = { + pointer: CALL_NONVIRTUAL_OBJECT_METHOD_OFFSET, + uint8: CALL_NONVIRTUAL_BOOLEAN_METHOD_OFFSET, + int8: CALL_NONVIRTUAL_BYTE_METHOD_OFFSET, + uint16: CALL_NONVIRTUAL_CHAR_METHOD_OFFSET, + int16: CALL_NONVIRTUAL_SHORT_METHOD_OFFSET, + int32: CALL_NONVIRTUAL_INT_METHOD_OFFSET, + int64: CALL_NONVIRTUAL_LONG_METHOD_OFFSET, + float: CALL_NONVIRTUAL_FLOAT_METHOD_OFFSET, + double: CALL_NONVIRTUAL_DOUBLE_METHOD_OFFSET, + void: CALL_NONVIRTUAL_VOID_METHOD_OFFSET +}; +var callStaticMethodOffset = { + pointer: CALL_STATIC_OBJECT_METHOD_OFFSET, + uint8: CALL_STATIC_BOOLEAN_METHOD_OFFSET, + int8: CALL_STATIC_BYTE_METHOD_OFFSET, + uint16: CALL_STATIC_CHAR_METHOD_OFFSET, + int16: CALL_STATIC_SHORT_METHOD_OFFSET, + int32: CALL_STATIC_INT_METHOD_OFFSET, + int64: CALL_STATIC_LONG_METHOD_OFFSET, + float: CALL_STATIC_FLOAT_METHOD_OFFSET, + double: CALL_STATIC_DOUBLE_METHOD_OFFSET, + void: CALL_STATIC_VOID_METHOD_OFFSET +}; +var getFieldOffset = { + pointer: GET_OBJECT_FIELD_OFFSET, + uint8: GET_BOOLEAN_FIELD_OFFSET, + int8: GET_BYTE_FIELD_OFFSET, + uint16: GET_CHAR_FIELD_OFFSET, + int16: GET_SHORT_FIELD_OFFSET, + int32: GET_INT_FIELD_OFFSET, + int64: GET_LONG_FIELD_OFFSET, + float: GET_FLOAT_FIELD_OFFSET, + double: GET_DOUBLE_FIELD_OFFSET +}; +var setFieldOffset = { + pointer: SET_OBJECT_FIELD_OFFSET, + uint8: SET_BOOLEAN_FIELD_OFFSET, + int8: SET_BYTE_FIELD_OFFSET, + uint16: SET_CHAR_FIELD_OFFSET, + int16: SET_SHORT_FIELD_OFFSET, + int32: SET_INT_FIELD_OFFSET, + int64: SET_LONG_FIELD_OFFSET, + float: SET_FLOAT_FIELD_OFFSET, + double: SET_DOUBLE_FIELD_OFFSET +}; +var getStaticFieldOffset = { + pointer: GET_STATIC_OBJECT_FIELD_OFFSET, + uint8: GET_STATIC_BOOLEAN_FIELD_OFFSET, + int8: GET_STATIC_BYTE_FIELD_OFFSET, + uint16: GET_STATIC_CHAR_FIELD_OFFSET, + int16: GET_STATIC_SHORT_FIELD_OFFSET, + int32: GET_STATIC_INT_FIELD_OFFSET, + int64: GET_STATIC_LONG_FIELD_OFFSET, + float: GET_STATIC_FLOAT_FIELD_OFFSET, + double: GET_STATIC_DOUBLE_FIELD_OFFSET +}; +var setStaticFieldOffset = { + pointer: SET_STATIC_OBJECT_FIELD_OFFSET, + uint8: SET_STATIC_BOOLEAN_FIELD_OFFSET, + int8: SET_STATIC_BYTE_FIELD_OFFSET, + uint16: SET_STATIC_CHAR_FIELD_OFFSET, + int16: SET_STATIC_SHORT_FIELD_OFFSET, + int32: SET_STATIC_INT_FIELD_OFFSET, + int64: SET_STATIC_LONG_FIELD_OFFSET, + float: SET_STATIC_FLOAT_FIELD_OFFSET, + double: SET_STATIC_DOUBLE_FIELD_OFFSET +}; +var nativeFunctionOptions2 = { + exceptions: "propagate" +}; +var cachedVtable = null; +var globalRefs = []; +Env.dispose = function(env) { + globalRefs.forEach(env.deleteGlobalRef, env); + globalRefs = []; +}; +function register(globalRef) { + globalRefs.push(globalRef); + return globalRef; +} +function vtable(instance) { + if (cachedVtable === null) { + cachedVtable = instance.handle.readPointer(); + } + return cachedVtable; +} +function proxy2(offset, retType, argTypes, wrapper) { + let impl = null; + return function() { + if (impl === null) { + impl = new NativeFunction(vtable(this).add(offset * pointerSize3).readPointer(), retType, argTypes, nativeFunctionOptions2); + } + let args = [impl]; + args = args.concat.apply(args, arguments); + return wrapper.apply(this, args); + }; +} +Env.prototype.getVersion = proxy2(4, "int32", ["pointer"], function(impl) { + return impl(this.handle); +}); +Env.prototype.findClass = proxy2(6, "pointer", ["pointer", "pointer"], function(impl, name) { + const result = impl(this.handle, Memory.allocUtf8String(name)); + this.throwIfExceptionPending(); + return result; +}); +Env.prototype.throwIfExceptionPending = function() { + const throwable = this.exceptionOccurred(); + if (throwable.isNull()) { + return; + } + this.exceptionClear(); + const handle = this.newGlobalRef(throwable); + this.deleteLocalRef(throwable); + const description = this.vaMethod("pointer", [])(this.handle, handle, this.javaLangObject().toString); + const descriptionStr = this.stringFromJni(description); + this.deleteLocalRef(description); + const error = new Error(descriptionStr); + error.$h = handle; + Script.bindWeak(error, makeErrorHandleDestructor(this.vm, handle)); + throw error; +}; +function makeErrorHandleDestructor(vm3, handle) { + return function() { + vm3.perform((env) => { + env.deleteGlobalRef(handle); + }); + }; +} +Env.prototype.fromReflectedMethod = proxy2(7, "pointer", ["pointer", "pointer"], function(impl, method) { + return impl(this.handle, method); +}); +Env.prototype.fromReflectedField = proxy2(8, "pointer", ["pointer", "pointer"], function(impl, method) { + return impl(this.handle, method); +}); +Env.prototype.toReflectedMethod = proxy2(9, "pointer", ["pointer", "pointer", "pointer", "uint8"], function(impl, klass, methodId, isStatic) { + return impl(this.handle, klass, methodId, isStatic); +}); +Env.prototype.getSuperclass = proxy2(10, "pointer", ["pointer", "pointer"], function(impl, klass) { + return impl(this.handle, klass); +}); +Env.prototype.isAssignableFrom = proxy2(11, "uint8", ["pointer", "pointer", "pointer"], function(impl, klass1, klass2) { + return !!impl(this.handle, klass1, klass2); +}); +Env.prototype.toReflectedField = proxy2(12, "pointer", ["pointer", "pointer", "pointer", "uint8"], function(impl, klass, fieldId, isStatic) { + return impl(this.handle, klass, fieldId, isStatic); +}); +Env.prototype.throw = proxy2(13, "int32", ["pointer", "pointer"], function(impl, obj) { + return impl(this.handle, obj); +}); +Env.prototype.exceptionOccurred = proxy2(15, "pointer", ["pointer"], function(impl) { + return impl(this.handle); +}); +Env.prototype.exceptionDescribe = proxy2(16, "void", ["pointer"], function(impl) { + impl(this.handle); +}); +Env.prototype.exceptionClear = proxy2(17, "void", ["pointer"], function(impl) { + impl(this.handle); +}); +Env.prototype.pushLocalFrame = proxy2(19, "int32", ["pointer", "int32"], function(impl, capacity) { + return impl(this.handle, capacity); +}); +Env.prototype.popLocalFrame = proxy2(20, "pointer", ["pointer", "pointer"], function(impl, result) { + return impl(this.handle, result); +}); +Env.prototype.newGlobalRef = proxy2(21, "pointer", ["pointer", "pointer"], function(impl, obj) { + return impl(this.handle, obj); +}); +Env.prototype.deleteGlobalRef = proxy2(22, "void", ["pointer", "pointer"], function(impl, globalRef) { + impl(this.handle, globalRef); +}); +Env.prototype.deleteLocalRef = proxy2(23, "void", ["pointer", "pointer"], function(impl, localRef) { + impl(this.handle, localRef); +}); +Env.prototype.isSameObject = proxy2(24, "uint8", ["pointer", "pointer", "pointer"], function(impl, ref1, ref2) { + return !!impl(this.handle, ref1, ref2); +}); +Env.prototype.newLocalRef = proxy2(25, "pointer", ["pointer", "pointer"], function(impl, obj) { + return impl(this.handle, obj); +}); +Env.prototype.allocObject = proxy2(27, "pointer", ["pointer", "pointer"], function(impl, clazz) { + return impl(this.handle, clazz); +}); +Env.prototype.getObjectClass = proxy2(31, "pointer", ["pointer", "pointer"], function(impl, obj) { + return impl(this.handle, obj); +}); +Env.prototype.isInstanceOf = proxy2(32, "uint8", ["pointer", "pointer", "pointer"], function(impl, obj, klass) { + return !!impl(this.handle, obj, klass); +}); +Env.prototype.getMethodId = proxy2(33, "pointer", ["pointer", "pointer", "pointer", "pointer"], function(impl, klass, name, sig) { + return impl(this.handle, klass, Memory.allocUtf8String(name), Memory.allocUtf8String(sig)); +}); +Env.prototype.getFieldId = proxy2(94, "pointer", ["pointer", "pointer", "pointer", "pointer"], function(impl, klass, name, sig) { + return impl(this.handle, klass, Memory.allocUtf8String(name), Memory.allocUtf8String(sig)); +}); +Env.prototype.getIntField = proxy2(100, "int32", ["pointer", "pointer", "pointer"], function(impl, obj, fieldId) { + return impl(this.handle, obj, fieldId); +}); +Env.prototype.getStaticMethodId = proxy2(113, "pointer", ["pointer", "pointer", "pointer", "pointer"], function(impl, klass, name, sig) { + return impl(this.handle, klass, Memory.allocUtf8String(name), Memory.allocUtf8String(sig)); +}); +Env.prototype.getStaticFieldId = proxy2(144, "pointer", ["pointer", "pointer", "pointer", "pointer"], function(impl, klass, name, sig) { + return impl(this.handle, klass, Memory.allocUtf8String(name), Memory.allocUtf8String(sig)); +}); +Env.prototype.getStaticIntField = proxy2(150, "int32", ["pointer", "pointer", "pointer"], function(impl, obj, fieldId) { + return impl(this.handle, obj, fieldId); +}); +Env.prototype.getStringLength = proxy2(164, "int32", ["pointer", "pointer"], function(impl, str) { + return impl(this.handle, str); +}); +Env.prototype.getStringChars = proxy2(165, "pointer", ["pointer", "pointer", "pointer"], function(impl, str) { + return impl(this.handle, str, NULL); +}); +Env.prototype.releaseStringChars = proxy2(166, "void", ["pointer", "pointer", "pointer"], function(impl, str, utf) { + impl(this.handle, str, utf); +}); +Env.prototype.newStringUtf = proxy2(167, "pointer", ["pointer", "pointer"], function(impl, str) { + const utf = Memory.allocUtf8String(str); + return impl(this.handle, utf); +}); +Env.prototype.getStringUtfChars = proxy2(169, "pointer", ["pointer", "pointer", "pointer"], function(impl, str) { + return impl(this.handle, str, NULL); +}); +Env.prototype.releaseStringUtfChars = proxy2(170, "void", ["pointer", "pointer", "pointer"], function(impl, str, utf) { + impl(this.handle, str, utf); +}); +Env.prototype.getArrayLength = proxy2(171, "int32", ["pointer", "pointer"], function(impl, array) { + return impl(this.handle, array); +}); +Env.prototype.newObjectArray = proxy2(172, "pointer", ["pointer", "int32", "pointer", "pointer"], function(impl, length, elementClass, initialElement) { + return impl(this.handle, length, elementClass, initialElement); +}); +Env.prototype.getObjectArrayElement = proxy2(173, "pointer", ["pointer", "pointer", "int32"], function(impl, array, index) { + return impl(this.handle, array, index); +}); +Env.prototype.setObjectArrayElement = proxy2(174, "void", ["pointer", "pointer", "int32", "pointer"], function(impl, array, index, value) { + impl(this.handle, array, index, value); +}); +Env.prototype.newBooleanArray = proxy2(175, "pointer", ["pointer", "int32"], function(impl, length) { + return impl(this.handle, length); +}); +Env.prototype.newByteArray = proxy2(176, "pointer", ["pointer", "int32"], function(impl, length) { + return impl(this.handle, length); +}); +Env.prototype.newCharArray = proxy2(177, "pointer", ["pointer", "int32"], function(impl, length) { + return impl(this.handle, length); +}); +Env.prototype.newShortArray = proxy2(178, "pointer", ["pointer", "int32"], function(impl, length) { + return impl(this.handle, length); +}); +Env.prototype.newIntArray = proxy2(179, "pointer", ["pointer", "int32"], function(impl, length) { + return impl(this.handle, length); +}); +Env.prototype.newLongArray = proxy2(180, "pointer", ["pointer", "int32"], function(impl, length) { + return impl(this.handle, length); +}); +Env.prototype.newFloatArray = proxy2(181, "pointer", ["pointer", "int32"], function(impl, length) { + return impl(this.handle, length); +}); +Env.prototype.newDoubleArray = proxy2(182, "pointer", ["pointer", "int32"], function(impl, length) { + return impl(this.handle, length); +}); +Env.prototype.getBooleanArrayElements = proxy2(183, "pointer", ["pointer", "pointer", "pointer"], function(impl, array) { + return impl(this.handle, array, NULL); +}); +Env.prototype.getByteArrayElements = proxy2(184, "pointer", ["pointer", "pointer", "pointer"], function(impl, array) { + return impl(this.handle, array, NULL); +}); +Env.prototype.getCharArrayElements = proxy2(185, "pointer", ["pointer", "pointer", "pointer"], function(impl, array) { + return impl(this.handle, array, NULL); +}); +Env.prototype.getShortArrayElements = proxy2(186, "pointer", ["pointer", "pointer", "pointer"], function(impl, array) { + return impl(this.handle, array, NULL); +}); +Env.prototype.getIntArrayElements = proxy2(187, "pointer", ["pointer", "pointer", "pointer"], function(impl, array) { + return impl(this.handle, array, NULL); +}); +Env.prototype.getLongArrayElements = proxy2(188, "pointer", ["pointer", "pointer", "pointer"], function(impl, array) { + return impl(this.handle, array, NULL); +}); +Env.prototype.getFloatArrayElements = proxy2(189, "pointer", ["pointer", "pointer", "pointer"], function(impl, array) { + return impl(this.handle, array, NULL); +}); +Env.prototype.getDoubleArrayElements = proxy2(190, "pointer", ["pointer", "pointer", "pointer"], function(impl, array) { + return impl(this.handle, array, NULL); +}); +Env.prototype.releaseBooleanArrayElements = proxy2(191, "pointer", ["pointer", "pointer", "pointer", "int32"], function(impl, array, cArray) { + impl(this.handle, array, cArray, JNI_ABORT); +}); +Env.prototype.releaseByteArrayElements = proxy2(192, "pointer", ["pointer", "pointer", "pointer", "int32"], function(impl, array, cArray) { + impl(this.handle, array, cArray, JNI_ABORT); +}); +Env.prototype.releaseCharArrayElements = proxy2(193, "pointer", ["pointer", "pointer", "pointer", "int32"], function(impl, array, cArray) { + impl(this.handle, array, cArray, JNI_ABORT); +}); +Env.prototype.releaseShortArrayElements = proxy2(194, "pointer", ["pointer", "pointer", "pointer", "int32"], function(impl, array, cArray) { + impl(this.handle, array, cArray, JNI_ABORT); +}); +Env.prototype.releaseIntArrayElements = proxy2(195, "pointer", ["pointer", "pointer", "pointer", "int32"], function(impl, array, cArray) { + impl(this.handle, array, cArray, JNI_ABORT); +}); +Env.prototype.releaseLongArrayElements = proxy2(196, "pointer", ["pointer", "pointer", "pointer", "int32"], function(impl, array, cArray) { + impl(this.handle, array, cArray, JNI_ABORT); +}); +Env.prototype.releaseFloatArrayElements = proxy2(197, "pointer", ["pointer", "pointer", "pointer", "int32"], function(impl, array, cArray) { + impl(this.handle, array, cArray, JNI_ABORT); +}); +Env.prototype.releaseDoubleArrayElements = proxy2(198, "pointer", ["pointer", "pointer", "pointer", "int32"], function(impl, array, cArray) { + impl(this.handle, array, cArray, JNI_ABORT); +}); +Env.prototype.getByteArrayRegion = proxy2(200, "void", ["pointer", "pointer", "int", "int", "pointer"], function(impl, array, start, length, cArray) { + impl(this.handle, array, start, length, cArray); +}); +Env.prototype.setBooleanArrayRegion = proxy2(207, "void", ["pointer", "pointer", "int32", "int32", "pointer"], function(impl, array, start, length, cArray) { + impl(this.handle, array, start, length, cArray); +}); +Env.prototype.setByteArrayRegion = proxy2(208, "void", ["pointer", "pointer", "int32", "int32", "pointer"], function(impl, array, start, length, cArray) { + impl(this.handle, array, start, length, cArray); +}); +Env.prototype.setCharArrayRegion = proxy2(209, "void", ["pointer", "pointer", "int32", "int32", "pointer"], function(impl, array, start, length, cArray) { + impl(this.handle, array, start, length, cArray); +}); +Env.prototype.setShortArrayRegion = proxy2(210, "void", ["pointer", "pointer", "int32", "int32", "pointer"], function(impl, array, start, length, cArray) { + impl(this.handle, array, start, length, cArray); +}); +Env.prototype.setIntArrayRegion = proxy2(211, "void", ["pointer", "pointer", "int32", "int32", "pointer"], function(impl, array, start, length, cArray) { + impl(this.handle, array, start, length, cArray); +}); +Env.prototype.setLongArrayRegion = proxy2(212, "void", ["pointer", "pointer", "int32", "int32", "pointer"], function(impl, array, start, length, cArray) { + impl(this.handle, array, start, length, cArray); +}); +Env.prototype.setFloatArrayRegion = proxy2(213, "void", ["pointer", "pointer", "int32", "int32", "pointer"], function(impl, array, start, length, cArray) { + impl(this.handle, array, start, length, cArray); +}); +Env.prototype.setDoubleArrayRegion = proxy2(214, "void", ["pointer", "pointer", "int32", "int32", "pointer"], function(impl, array, start, length, cArray) { + impl(this.handle, array, start, length, cArray); +}); +Env.prototype.registerNatives = proxy2(215, "int32", ["pointer", "pointer", "pointer", "int32"], function(impl, klass, methods, numMethods) { + return impl(this.handle, klass, methods, numMethods); +}); +Env.prototype.monitorEnter = proxy2(217, "int32", ["pointer", "pointer"], function(impl, obj) { + return impl(this.handle, obj); +}); +Env.prototype.monitorExit = proxy2(218, "int32", ["pointer", "pointer"], function(impl, obj) { + return impl(this.handle, obj); +}); +Env.prototype.getDirectBufferAddress = proxy2(230, "pointer", ["pointer", "pointer"], function(impl, obj) { + return impl(this.handle, obj); +}); +Env.prototype.getObjectRefType = proxy2(232, "int32", ["pointer", "pointer"], function(impl, ref) { + return impl(this.handle, ref); +}); +var cachedMethods = /* @__PURE__ */ new Map(); +function plainMethod(offset, retType, argTypes, options) { + return getOrMakeMethod(this, "p", makePlainMethod, offset, retType, argTypes, options); +} +function vaMethod(offset, retType, argTypes, options) { + return getOrMakeMethod(this, "v", makeVaMethod, offset, retType, argTypes, options); +} +function nonvirtualVaMethod(offset, retType, argTypes, options) { + return getOrMakeMethod(this, "n", makeNonvirtualVaMethod, offset, retType, argTypes, options); +} +function getOrMakeMethod(env, flavor, construct, offset, retType, argTypes, options) { + if (options !== void 0) { + return construct(env, offset, retType, argTypes, options); + } + const key = [offset, flavor, retType].concat(argTypes).join("|"); + let m = cachedMethods.get(key); + if (m === void 0) { + m = construct(env, offset, retType, argTypes, nativeFunctionOptions2); + cachedMethods.set(key, m); + } + return m; +} +function makePlainMethod(env, offset, retType, argTypes, options) { + return new NativeFunction( + vtable(env).add(offset * pointerSize3).readPointer(), + retType, + ["pointer", "pointer", "pointer"].concat(argTypes), + options + ); +} +function makeVaMethod(env, offset, retType, argTypes, options) { + return new NativeFunction( + vtable(env).add(offset * pointerSize3).readPointer(), + retType, + ["pointer", "pointer", "pointer", "..."].concat(argTypes), + options + ); +} +function makeNonvirtualVaMethod(env, offset, retType, argTypes, options) { + return new NativeFunction( + vtable(env).add(offset * pointerSize3).readPointer(), + retType, + ["pointer", "pointer", "pointer", "pointer", "..."].concat(argTypes), + options + ); +} +Env.prototype.constructor = function(argTypes, options) { + return vaMethod.call(this, CALL_CONSTRUCTOR_METHOD_OFFSET, "pointer", argTypes, options); +}; +Env.prototype.vaMethod = function(retType, argTypes, options) { + const offset = callMethodOffset[retType]; + if (offset === void 0) { + throw new Error("Unsupported type: " + retType); + } + return vaMethod.call(this, offset, retType, argTypes, options); +}; +Env.prototype.nonvirtualVaMethod = function(retType, argTypes, options) { + const offset = callNonvirtualMethodOffset[retType]; + if (offset === void 0) { + throw new Error("Unsupported type: " + retType); + } + return nonvirtualVaMethod.call(this, offset, retType, argTypes, options); +}; +Env.prototype.staticVaMethod = function(retType, argTypes, options) { + const offset = callStaticMethodOffset[retType]; + if (offset === void 0) { + throw new Error("Unsupported type: " + retType); + } + return vaMethod.call(this, offset, retType, argTypes, options); +}; +Env.prototype.getField = function(fieldType) { + const offset = getFieldOffset[fieldType]; + if (offset === void 0) { + throw new Error("Unsupported type: " + fieldType); + } + return plainMethod.call(this, offset, fieldType, []); +}; +Env.prototype.getStaticField = function(fieldType) { + const offset = getStaticFieldOffset[fieldType]; + if (offset === void 0) { + throw new Error("Unsupported type: " + fieldType); + } + return plainMethod.call(this, offset, fieldType, []); +}; +Env.prototype.setField = function(fieldType) { + const offset = setFieldOffset[fieldType]; + if (offset === void 0) { + throw new Error("Unsupported type: " + fieldType); + } + return plainMethod.call(this, offset, "void", [fieldType]); +}; +Env.prototype.setStaticField = function(fieldType) { + const offset = setStaticFieldOffset[fieldType]; + if (offset === void 0) { + throw new Error("Unsupported type: " + fieldType); + } + return plainMethod.call(this, offset, "void", [fieldType]); +}; +var javaLangClass = null; +Env.prototype.javaLangClass = function() { + if (javaLangClass === null) { + const handle = this.findClass("java/lang/Class"); + try { + const get = this.getMethodId.bind(this, handle); + javaLangClass = { + handle: register(this.newGlobalRef(handle)), + getName: get("getName", "()Ljava/lang/String;"), + getSimpleName: get("getSimpleName", "()Ljava/lang/String;"), + getGenericSuperclass: get("getGenericSuperclass", "()Ljava/lang/reflect/Type;"), + getDeclaredConstructors: get("getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;"), + getDeclaredMethods: get("getDeclaredMethods", "()[Ljava/lang/reflect/Method;"), + getDeclaredFields: get("getDeclaredFields", "()[Ljava/lang/reflect/Field;"), + isArray: get("isArray", "()Z"), + isPrimitive: get("isPrimitive", "()Z"), + isInterface: get("isInterface", "()Z"), + getComponentType: get("getComponentType", "()Ljava/lang/Class;") + }; + } finally { + this.deleteLocalRef(handle); + } + } + return javaLangClass; +}; +var javaLangObject = null; +Env.prototype.javaLangObject = function() { + if (javaLangObject === null) { + const handle = this.findClass("java/lang/Object"); + try { + const get = this.getMethodId.bind(this, handle); + javaLangObject = { + handle: register(this.newGlobalRef(handle)), + toString: get("toString", "()Ljava/lang/String;"), + getClass: get("getClass", "()Ljava/lang/Class;") + }; + } finally { + this.deleteLocalRef(handle); + } + } + return javaLangObject; +}; +var javaLangReflectConstructor = null; +Env.prototype.javaLangReflectConstructor = function() { + if (javaLangReflectConstructor === null) { + const handle = this.findClass("java/lang/reflect/Constructor"); + try { + javaLangReflectConstructor = { + getGenericParameterTypes: this.getMethodId(handle, "getGenericParameterTypes", "()[Ljava/lang/reflect/Type;") + }; + } finally { + this.deleteLocalRef(handle); + } + } + return javaLangReflectConstructor; +}; +var javaLangReflectMethod = null; +Env.prototype.javaLangReflectMethod = function() { + if (javaLangReflectMethod === null) { + const handle = this.findClass("java/lang/reflect/Method"); + try { + const get = this.getMethodId.bind(this, handle); + javaLangReflectMethod = { + getName: get("getName", "()Ljava/lang/String;"), + getGenericParameterTypes: get("getGenericParameterTypes", "()[Ljava/lang/reflect/Type;"), + getParameterTypes: get("getParameterTypes", "()[Ljava/lang/Class;"), + getGenericReturnType: get("getGenericReturnType", "()Ljava/lang/reflect/Type;"), + getGenericExceptionTypes: get("getGenericExceptionTypes", "()[Ljava/lang/reflect/Type;"), + getModifiers: get("getModifiers", "()I"), + isVarArgs: get("isVarArgs", "()Z") + }; + } finally { + this.deleteLocalRef(handle); + } + } + return javaLangReflectMethod; +}; +var javaLangReflectField = null; +Env.prototype.javaLangReflectField = function() { + if (javaLangReflectField === null) { + const handle = this.findClass("java/lang/reflect/Field"); + try { + const get = this.getMethodId.bind(this, handle); + javaLangReflectField = { + getName: get("getName", "()Ljava/lang/String;"), + getType: get("getType", "()Ljava/lang/Class;"), + getGenericType: get("getGenericType", "()Ljava/lang/reflect/Type;"), + getModifiers: get("getModifiers", "()I"), + toString: get("toString", "()Ljava/lang/String;") + }; + } finally { + this.deleteLocalRef(handle); + } + } + return javaLangReflectField; +}; +var javaLangReflectTypeVariable = null; +Env.prototype.javaLangReflectTypeVariable = function() { + if (javaLangReflectTypeVariable === null) { + const handle = this.findClass("java/lang/reflect/TypeVariable"); + try { + const get = this.getMethodId.bind(this, handle); + javaLangReflectTypeVariable = { + handle: register(this.newGlobalRef(handle)), + getName: get("getName", "()Ljava/lang/String;"), + getBounds: get("getBounds", "()[Ljava/lang/reflect/Type;"), + getGenericDeclaration: get("getGenericDeclaration", "()Ljava/lang/reflect/GenericDeclaration;") + }; + } finally { + this.deleteLocalRef(handle); + } + } + return javaLangReflectTypeVariable; +}; +var javaLangReflectWildcardType = null; +Env.prototype.javaLangReflectWildcardType = function() { + if (javaLangReflectWildcardType === null) { + const handle = this.findClass("java/lang/reflect/WildcardType"); + try { + const get = this.getMethodId.bind(this, handle); + javaLangReflectWildcardType = { + handle: register(this.newGlobalRef(handle)), + getLowerBounds: get("getLowerBounds", "()[Ljava/lang/reflect/Type;"), + getUpperBounds: get("getUpperBounds", "()[Ljava/lang/reflect/Type;") + }; + } finally { + this.deleteLocalRef(handle); + } + } + return javaLangReflectWildcardType; +}; +var javaLangReflectGenericArrayType = null; +Env.prototype.javaLangReflectGenericArrayType = function() { + if (javaLangReflectGenericArrayType === null) { + const handle = this.findClass("java/lang/reflect/GenericArrayType"); + try { + javaLangReflectGenericArrayType = { + handle: register(this.newGlobalRef(handle)), + getGenericComponentType: this.getMethodId(handle, "getGenericComponentType", "()Ljava/lang/reflect/Type;") + }; + } finally { + this.deleteLocalRef(handle); + } + } + return javaLangReflectGenericArrayType; +}; +var javaLangReflectParameterizedType = null; +Env.prototype.javaLangReflectParameterizedType = function() { + if (javaLangReflectParameterizedType === null) { + const handle = this.findClass("java/lang/reflect/ParameterizedType"); + try { + const get = this.getMethodId.bind(this, handle); + javaLangReflectParameterizedType = { + handle: register(this.newGlobalRef(handle)), + getActualTypeArguments: get("getActualTypeArguments", "()[Ljava/lang/reflect/Type;"), + getRawType: get("getRawType", "()Ljava/lang/reflect/Type;"), + getOwnerType: get("getOwnerType", "()Ljava/lang/reflect/Type;") + }; + } finally { + this.deleteLocalRef(handle); + } + } + return javaLangReflectParameterizedType; +}; +var javaLangString = null; +Env.prototype.javaLangString = function() { + if (javaLangString === null) { + const handle = this.findClass("java/lang/String"); + try { + javaLangString = { + handle: register(this.newGlobalRef(handle)) + }; + } finally { + this.deleteLocalRef(handle); + } + } + return javaLangString; +}; +Env.prototype.getClassName = function(classHandle) { + const name = this.vaMethod("pointer", [])(this.handle, classHandle, this.javaLangClass().getName); + try { + return this.stringFromJni(name); + } finally { + this.deleteLocalRef(name); + } +}; +Env.prototype.getObjectClassName = function(objHandle) { + const jklass = this.getObjectClass(objHandle); + try { + return this.getClassName(jklass); + } finally { + this.deleteLocalRef(jklass); + } +}; +Env.prototype.getActualTypeArgument = function(type) { + const actualTypeArguments = this.vaMethod("pointer", [])(this.handle, type, this.javaLangReflectParameterizedType().getActualTypeArguments); + this.throwIfExceptionPending(); + if (!actualTypeArguments.isNull()) { + try { + return this.getTypeNameFromFirstTypeElement(actualTypeArguments); + } finally { + this.deleteLocalRef(actualTypeArguments); + } + } +}; +Env.prototype.getTypeNameFromFirstTypeElement = function(typeArray) { + const length = this.getArrayLength(typeArray); + if (length > 0) { + const typeArgument0 = this.getObjectArrayElement(typeArray, 0); + try { + return this.getTypeName(typeArgument0); + } finally { + this.deleteLocalRef(typeArgument0); + } + } else { + return "java.lang.Object"; + } +}; +Env.prototype.getTypeName = function(type, getGenericsInformation) { + const invokeObjectMethodNoArgs = this.vaMethod("pointer", []); + if (this.isInstanceOf(type, this.javaLangClass().handle)) { + return this.getClassName(type); + } else if (this.isInstanceOf(type, this.javaLangReflectGenericArrayType().handle)) { + return this.getArrayTypeName(type); + } else if (this.isInstanceOf(type, this.javaLangReflectParameterizedType().handle)) { + const rawType = invokeObjectMethodNoArgs(this.handle, type, this.javaLangReflectParameterizedType().getRawType); + this.throwIfExceptionPending(); + let result; + try { + result = this.getTypeName(rawType); + } finally { + this.deleteLocalRef(rawType); + } + if (getGenericsInformation) { + result += "<" + this.getActualTypeArgument(type) + ">"; + } + return result; + } else if (this.isInstanceOf(type, this.javaLangReflectTypeVariable().handle)) { + return "java.lang.Object"; + } else if (this.isInstanceOf(type, this.javaLangReflectWildcardType().handle)) { + return "java.lang.Object"; + } else { + return "java.lang.Object"; + } +}; +Env.prototype.getArrayTypeName = function(type) { + const invokeObjectMethodNoArgs = this.vaMethod("pointer", []); + if (this.isInstanceOf(type, this.javaLangClass().handle)) { + return this.getClassName(type); + } else if (this.isInstanceOf(type, this.javaLangReflectGenericArrayType().handle)) { + const componentType = invokeObjectMethodNoArgs(this.handle, type, this.javaLangReflectGenericArrayType().getGenericComponentType); + this.throwIfExceptionPending(); + try { + return "[L" + this.getTypeName(componentType) + ";"; + } finally { + this.deleteLocalRef(componentType); + } + } else { + return "[Ljava.lang.Object;"; + } +}; +Env.prototype.stringFromJni = function(str) { + const utf = this.getStringChars(str); + if (utf.isNull()) { + throw new Error("Unable to access string"); + } + try { + const length = this.getStringLength(str); + return utf.readUtf16String(length); + } finally { + this.releaseStringChars(str, utf); + } +}; + +// node_modules/frida-java-bridge/lib/vm.js +var JNI_VERSION_1_6 = 65542; +var pointerSize4 = Process.pointerSize; +var jsThreadID = Process.getCurrentThreadId(); +var attachedThreads = /* @__PURE__ */ new Map(); +var activeEnvs = /* @__PURE__ */ new Map(); +function VM(api2) { + const handle = api2.vm; + let attachCurrentThread = null; + let detachCurrentThread = null; + let getEnv = null; + function initialize2() { + const vtable2 = handle.readPointer(); + const options = { + exceptions: "propagate" + }; + attachCurrentThread = new NativeFunction(vtable2.add(4 * pointerSize4).readPointer(), "int32", ["pointer", "pointer", "pointer"], options); + detachCurrentThread = new NativeFunction(vtable2.add(5 * pointerSize4).readPointer(), "int32", ["pointer"], options); + getEnv = new NativeFunction(vtable2.add(6 * pointerSize4).readPointer(), "int32", ["pointer", "pointer", "int32"], options); + } + this.handle = handle; + this.perform = function(fn) { + const threadId = Process.getCurrentThreadId(); + const cachedEnv = tryGetCachedEnv(threadId); + if (cachedEnv !== null) { + return fn(cachedEnv); + } + let env = this._tryGetEnv(); + const alreadyAttached = env !== null; + if (!alreadyAttached) { + env = this.attachCurrentThread(); + attachedThreads.set(threadId, true); + } + this.link(threadId, env); + try { + return fn(env); + } finally { + const isJsThread = threadId === jsThreadID; + if (!isJsThread) { + this.unlink(threadId); + } + if (!alreadyAttached && !isJsThread) { + const allowedToDetach = attachedThreads.get(threadId); + attachedThreads.delete(threadId); + if (allowedToDetach) { + this.detachCurrentThread(); + } + } + } + }; + this.attachCurrentThread = function() { + const envBuf = Memory.alloc(pointerSize4); + checkJniResult("VM::AttachCurrentThread", attachCurrentThread(handle, envBuf, NULL)); + return new Env(envBuf.readPointer(), this); + }; + this.detachCurrentThread = function() { + checkJniResult("VM::DetachCurrentThread", detachCurrentThread(handle)); + }; + this.preventDetachDueToClassLoader = function() { + const threadId = Process.getCurrentThreadId(); + if (attachedThreads.has(threadId)) { + attachedThreads.set(threadId, false); + } + }; + this.getEnv = function() { + const cachedEnv = tryGetCachedEnv(Process.getCurrentThreadId()); + if (cachedEnv !== null) { + return cachedEnv; + } + const envBuf = Memory.alloc(pointerSize4); + const result = getEnv(handle, envBuf, JNI_VERSION_1_6); + if (result === -2) { + throw new Error("Current thread is not attached to the Java VM; please move this code inside a Java.perform() callback"); + } + checkJniResult("VM::GetEnv", result); + return new Env(envBuf.readPointer(), this); + }; + this.tryGetEnv = function() { + const cachedEnv = tryGetCachedEnv(Process.getCurrentThreadId()); + if (cachedEnv !== null) { + return cachedEnv; + } + return this._tryGetEnv(); + }; + this._tryGetEnv = function() { + const h = this.tryGetEnvHandle(JNI_VERSION_1_6); + if (h === null) { + return null; + } + return new Env(h, this); + }; + this.tryGetEnvHandle = function(version) { + const envBuf = Memory.alloc(pointerSize4); + const result = getEnv(handle, envBuf, version); + if (result !== JNI_OK) { + return null; + } + return envBuf.readPointer(); + }; + this.makeHandleDestructor = function(handle2) { + return () => { + this.perform((env) => { + env.deleteGlobalRef(handle2); + }); + }; + }; + this.link = function(tid, env) { + const entry = activeEnvs.get(tid); + if (entry === void 0) { + activeEnvs.set(tid, [env, 1]); + } else { + entry[1]++; + } + }; + this.unlink = function(tid) { + const entry = activeEnvs.get(tid); + if (entry[1] === 1) { + activeEnvs.delete(tid); + } else { + entry[1]--; + } + }; + function tryGetCachedEnv(threadId) { + const entry = activeEnvs.get(threadId); + if (entry === void 0) { + return null; + } + return entry[0]; + } + initialize2.call(this); +} +VM.dispose = function(vm3) { + if (attachedThreads.get(jsThreadID) === true) { + attachedThreads.delete(jsThreadID); + vm3.detachCurrentThread(); + } +}; + +// node_modules/frida-java-bridge/lib/android.js +var jsizeSize = 4; +var pointerSize5 = Process.pointerSize; +var { + readU32, + readPointer, + writeU32, + writePointer +} = NativePointer.prototype; +var kAccPublic = 1; +var kAccStatic = 8; +var kAccFinal = 16; +var kAccNative = 256; +var kAccFastNative = 524288; +var kAccCriticalNative = 2097152; +var kAccFastInterpreterToInterpreterInvoke = 1073741824; +var kAccSkipAccessChecks = 524288; +var kAccSingleImplementation = 134217728; +var kAccNterpEntryPointFastPathFlag = 1048576; +var kAccNterpInvokeFastPathFlag = 2097152; +var kAccPublicApi = 268435456; +var kAccXposedHookedMethod = 268435456; +var kPointer = 0; +var kFullDeoptimization = 3; +var kSelectiveDeoptimization = 5; +var THUMB_BIT_REMOVAL_MASK = ptr(1).not(); +var X86_JMP_MAX_DISTANCE = 2147467263; +var ARM64_ADRP_MAX_DISTANCE = 4294963200; +var ENV_VTABLE_OFFSET_EXCEPTION_CLEAR = 17 * pointerSize5; +var ENV_VTABLE_OFFSET_FATAL_ERROR = 18 * pointerSize5; +var DVM_JNI_ENV_OFFSET_SELF = 12; +var DVM_CLASS_OBJECT_OFFSET_VTABLE_COUNT = 112; +var DVM_CLASS_OBJECT_OFFSET_VTABLE = 116; +var DVM_OBJECT_OFFSET_CLAZZ = 0; +var DVM_METHOD_SIZE = 56; +var DVM_METHOD_OFFSET_ACCESS_FLAGS = 4; +var DVM_METHOD_OFFSET_METHOD_INDEX = 8; +var DVM_METHOD_OFFSET_REGISTERS_SIZE = 10; +var DVM_METHOD_OFFSET_OUTS_SIZE = 12; +var DVM_METHOD_OFFSET_INS_SIZE = 14; +var DVM_METHOD_OFFSET_SHORTY = 28; +var DVM_METHOD_OFFSET_JNI_ARG_INFO = 36; +var DALVIK_JNI_RETURN_VOID = 0; +var DALVIK_JNI_RETURN_FLOAT = 1; +var DALVIK_JNI_RETURN_DOUBLE = 2; +var DALVIK_JNI_RETURN_S8 = 3; +var DALVIK_JNI_RETURN_S4 = 4; +var DALVIK_JNI_RETURN_S2 = 5; +var DALVIK_JNI_RETURN_U2 = 6; +var DALVIK_JNI_RETURN_S1 = 7; +var DALVIK_JNI_NO_ARG_INFO = 2147483648; +var DALVIK_JNI_RETURN_SHIFT = 28; +var STD_STRING_SIZE = 3 * pointerSize5; +var STD_VECTOR_SIZE = 3 * pointerSize5; +var AF_UNIX = 1; +var SOCK_STREAM = 1; +var getArtRuntimeSpec = memoize(_getArtRuntimeSpec); +var getArtInstrumentationSpec = memoize(_getArtInstrumentationSpec); +var getArtMethodSpec = memoize(_getArtMethodSpec); +var getArtThreadSpec = memoize(_getArtThreadSpec); +var getArtManagedStackSpec = memoize(_getArtManagedStackSpec); +var getArtThreadStateTransitionImpl = memoize(_getArtThreadStateTransitionImpl); +var getAndroidVersion = memoize(_getAndroidVersion); +var getAndroidCodename = memoize(_getAndroidCodename); +var getAndroidApiLevel = memoize(_getAndroidApiLevel); +var getArtQuickFrameInfoGetterThunk = memoize(_getArtQuickFrameInfoGetterThunk); +var makeCxxMethodWrapperReturningPointerByValue = Process.arch === "ia32" ? makeCxxMethodWrapperReturningPointerByValueInFirstArg : makeCxxMethodWrapperReturningPointerByValueGeneric; +var nativeFunctionOptions3 = { + exceptions: "propagate" +}; +var artThreadStateTransitions = {}; +var cachedApi = null; +var cachedArtClassLinkerSpec = null; +var MethodMangler = null; +var artController = null; +var inlineHooks = []; +var patchedClasses = /* @__PURE__ */ new Map(); +var artQuickInterceptors = []; +var thunkPage = null; +var thunkOffset = 0; +var taughtArtAboutReplacementMethods = false; +var taughtArtAboutMethodInstrumentation = false; +var backtraceModule = null; +var jdwpSessions = []; +var socketpair = null; +var trampolineAllocator = null; +function getApi() { + if (cachedApi === null) { + cachedApi = _getApi(); + } + return cachedApi; +} +function _getApi() { + const vmModules = Process.enumerateModules().filter((m) => /^lib(art|dvm).so$/.test(m.name)).filter((m) => !/\/system\/fake-libs/.test(m.path)); + if (vmModules.length === 0) { + return null; + } + const vmModule = vmModules[0]; + const flavor = vmModule.name.indexOf("art") !== -1 ? "art" : "dalvik"; + const isArt = flavor === "art"; + const temporaryApi = { + module: vmModule, + find(name) { + const { module } = this; + let address = module.findExportByName(name); + if (address === null) { + address = module.findSymbolByName(name); + } + return address; + }, + flavor, + addLocalReference: null + }; + temporaryApi.isApiLevel34OrApexEquivalent = isArt && (temporaryApi.find("_ZN3art7AppInfo29GetPrimaryApkReferenceProfileEv") !== null || temporaryApi.find("_ZN3art6Thread15RunFlipFunctionEPS0_") !== null); + const pending = isArt ? { + functions: { + JNI_GetCreatedJavaVMs: ["JNI_GetCreatedJavaVMs", "int", ["pointer", "int", "pointer"]], + // Android < 7 + artInterpreterToCompiledCodeBridge: function(address) { + this.artInterpreterToCompiledCodeBridge = address; + }, + // Android >= 8 + _ZN3art9JavaVMExt12AddGlobalRefEPNS_6ThreadENS_6ObjPtrINS_6mirror6ObjectEEE: ["art::JavaVMExt::AddGlobalRef", "pointer", ["pointer", "pointer", "pointer"]], + // Android >= 6 + _ZN3art9JavaVMExt12AddGlobalRefEPNS_6ThreadEPNS_6mirror6ObjectE: ["art::JavaVMExt::AddGlobalRef", "pointer", ["pointer", "pointer", "pointer"]], + // Android < 6: makeAddGlobalRefFallbackForAndroid5() needs these: + _ZN3art17ReaderWriterMutex13ExclusiveLockEPNS_6ThreadE: ["art::ReaderWriterMutex::ExclusiveLock", "void", ["pointer", "pointer"]], + _ZN3art17ReaderWriterMutex15ExclusiveUnlockEPNS_6ThreadE: ["art::ReaderWriterMutex::ExclusiveUnlock", "void", ["pointer", "pointer"]], + // Android <= 7 + _ZN3art22IndirectReferenceTable3AddEjPNS_6mirror6ObjectE: function(address) { + this["art::IndirectReferenceTable::Add"] = new NativeFunction(address, "pointer", ["pointer", "uint", "pointer"], nativeFunctionOptions3); + }, + // Android > 7 + _ZN3art22IndirectReferenceTable3AddENS_15IRTSegmentStateENS_6ObjPtrINS_6mirror6ObjectEEE: function(address) { + this["art::IndirectReferenceTable::Add"] = new NativeFunction(address, "pointer", ["pointer", "uint", "pointer"], nativeFunctionOptions3); + }, + // Android >= 7 + _ZN3art9JavaVMExt12DecodeGlobalEPv: function(address) { + let decodeGlobal; + if (getAndroidApiLevel() >= 26) { + decodeGlobal = makeCxxMethodWrapperReturningPointerByValue(address, ["pointer", "pointer"]); + } else { + decodeGlobal = new NativeFunction(address, "pointer", ["pointer", "pointer"], nativeFunctionOptions3); + } + this["art::JavaVMExt::DecodeGlobal"] = function(vm3, thread, ref) { + return decodeGlobal(vm3, ref); + }; + }, + // Android >= 6 + _ZN3art9JavaVMExt12DecodeGlobalEPNS_6ThreadEPv: ["art::JavaVMExt::DecodeGlobal", "pointer", ["pointer", "pointer", "pointer"]], + // makeDecodeGlobalFallback() uses: + // Android >= 15 + _ZNK3art6Thread19DecodeGlobalJObjectEP8_jobject: ["art::Thread::DecodeJObject", "pointer", ["pointer", "pointer"]], + // Android < 6 + _ZNK3art6Thread13DecodeJObjectEP8_jobject: ["art::Thread::DecodeJObject", "pointer", ["pointer", "pointer"]], + // Android >= 6 + _ZN3art10ThreadList10SuspendAllEPKcb: ["art::ThreadList::SuspendAll", "void", ["pointer", "pointer", "bool"]], + // or fallback: + _ZN3art10ThreadList10SuspendAllEv: function(address) { + const suspendAll = new NativeFunction(address, "void", ["pointer"], nativeFunctionOptions3); + this["art::ThreadList::SuspendAll"] = function(threadList, cause, longSuspend) { + return suspendAll(threadList); + }; + }, + _ZN3art10ThreadList9ResumeAllEv: ["art::ThreadList::ResumeAll", "void", ["pointer"]], + // Android >= 7 + _ZN3art11ClassLinker12VisitClassesEPNS_12ClassVisitorE: ["art::ClassLinker::VisitClasses", "void", ["pointer", "pointer"]], + // Android < 7 + _ZN3art11ClassLinker12VisitClassesEPFbPNS_6mirror5ClassEPvES4_: function(address) { + const visitClasses = new NativeFunction(address, "void", ["pointer", "pointer", "pointer"], nativeFunctionOptions3); + this["art::ClassLinker::VisitClasses"] = function(classLinker, visitor) { + visitClasses(classLinker, visitor, NULL); + }; + }, + _ZNK3art11ClassLinker17VisitClassLoadersEPNS_18ClassLoaderVisitorE: ["art::ClassLinker::VisitClassLoaders", "void", ["pointer", "pointer"]], + _ZN3art2gc4Heap12VisitObjectsEPFvPNS_6mirror6ObjectEPvES5_: ["art::gc::Heap::VisitObjects", "void", ["pointer", "pointer", "pointer"]], + _ZN3art2gc4Heap12GetInstancesERNS_24VariableSizedHandleScopeENS_6HandleINS_6mirror5ClassEEEiRNSt3__16vectorINS4_INS5_6ObjectEEENS8_9allocatorISB_EEEE: ["art::gc::Heap::GetInstances", "void", ["pointer", "pointer", "pointer", "int", "pointer"]], + // Android >= 9 + _ZN3art2gc4Heap12GetInstancesERNS_24VariableSizedHandleScopeENS_6HandleINS_6mirror5ClassEEEbiRNSt3__16vectorINS4_INS5_6ObjectEEENS8_9allocatorISB_EEEE: function(address) { + const getInstances = new NativeFunction(address, "void", ["pointer", "pointer", "pointer", "bool", "int", "pointer"], nativeFunctionOptions3); + this["art::gc::Heap::GetInstances"] = function(instance, scope, hClass, maxCount, instances) { + const useIsAssignableFrom = 0; + getInstances(instance, scope, hClass, useIsAssignableFrom, maxCount, instances); + }; + }, + _ZN3art12StackVisitorC2EPNS_6ThreadEPNS_7ContextENS0_13StackWalkKindEjb: ["art::StackVisitor::StackVisitor", "void", ["pointer", "pointer", "pointer", "uint", "uint", "bool"]], + _ZN3art12StackVisitorC2EPNS_6ThreadEPNS_7ContextENS0_13StackWalkKindEmb: ["art::StackVisitor::StackVisitor", "void", ["pointer", "pointer", "pointer", "uint", "size_t", "bool"]], + _ZN3art12StackVisitor9WalkStackILNS0_16CountTransitionsE0EEEvb: ["art::StackVisitor::WalkStack", "void", ["pointer", "bool"]], + _ZNK3art12StackVisitor9GetMethodEv: ["art::StackVisitor::GetMethod", "pointer", ["pointer"]], + _ZNK3art12StackVisitor16DescribeLocationEv: function(address) { + this["art::StackVisitor::DescribeLocation"] = makeCxxMethodWrapperReturningStdStringByValue(address, ["pointer"]); + }, + _ZNK3art12StackVisitor24GetCurrentQuickFrameInfoEv: function(address) { + this["art::StackVisitor::GetCurrentQuickFrameInfo"] = makeArtQuickFrameInfoGetter(address); + }, + _ZN3art6Thread18GetLongJumpContextEv: ["art::Thread::GetLongJumpContext", "pointer", ["pointer"]], + _ZN3art6mirror5Class13GetDescriptorEPNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE: function(address) { + this["art::mirror::Class::GetDescriptor"] = address; + }, + _ZN3art6mirror5Class11GetLocationEv: function(address) { + this["art::mirror::Class::GetLocation"] = makeCxxMethodWrapperReturningStdStringByValue(address, ["pointer"]); + }, + _ZN3art9ArtMethod12PrettyMethodEb: function(address) { + this["art::ArtMethod::PrettyMethod"] = makeCxxMethodWrapperReturningStdStringByValue(address, ["pointer", "bool"]); + }, + _ZN3art12PrettyMethodEPNS_9ArtMethodEb: function(address) { + this["art::ArtMethod::PrettyMethodNullSafe"] = makeCxxMethodWrapperReturningStdStringByValue(address, ["pointer", "bool"]); + }, + // Android < 6 for cloneArtMethod() + _ZN3art6Thread14CurrentFromGdbEv: ["art::Thread::CurrentFromGdb", "pointer", []], + _ZN3art6mirror6Object5CloneEPNS_6ThreadE: function(address) { + this["art::mirror::Object::Clone"] = new NativeFunction(address, "pointer", ["pointer", "pointer"], nativeFunctionOptions3); + }, + _ZN3art6mirror6Object5CloneEPNS_6ThreadEm: function(address) { + const clone = new NativeFunction(address, "pointer", ["pointer", "pointer", "pointer"], nativeFunctionOptions3); + this["art::mirror::Object::Clone"] = function(thisPtr, threadPtr) { + const numTargetBytes = NULL; + return clone(thisPtr, threadPtr, numTargetBytes); + }; + }, + _ZN3art6mirror6Object5CloneEPNS_6ThreadEj: function(address) { + const clone = new NativeFunction(address, "pointer", ["pointer", "pointer", "uint"], nativeFunctionOptions3); + this["art::mirror::Object::Clone"] = function(thisPtr, threadPtr) { + const numTargetBytes = 0; + return clone(thisPtr, threadPtr, numTargetBytes); + }; + }, + _ZN3art3Dbg14SetJdwpAllowedEb: ["art::Dbg::SetJdwpAllowed", "void", ["bool"]], + _ZN3art3Dbg13ConfigureJdwpERKNS_4JDWP11JdwpOptionsE: ["art::Dbg::ConfigureJdwp", "void", ["pointer"]], + _ZN3art31InternalDebuggerControlCallback13StartDebuggerEv: ["art::InternalDebuggerControlCallback::StartDebugger", "void", ["pointer"]], + _ZN3art3Dbg9StartJdwpEv: ["art::Dbg::StartJdwp", "void", []], + _ZN3art3Dbg8GoActiveEv: ["art::Dbg::GoActive", "void", []], + _ZN3art3Dbg21RequestDeoptimizationERKNS_21DeoptimizationRequestE: ["art::Dbg::RequestDeoptimization", "void", ["pointer"]], + _ZN3art3Dbg20ManageDeoptimizationEv: ["art::Dbg::ManageDeoptimization", "void", []], + _ZN3art15instrumentation15Instrumentation20EnableDeoptimizationEv: ["art::Instrumentation::EnableDeoptimization", "void", ["pointer"]], + // Android >= 6 + _ZN3art15instrumentation15Instrumentation20DeoptimizeEverythingEPKc: ["art::Instrumentation::DeoptimizeEverything", "void", ["pointer", "pointer"]], + // Android < 6 + _ZN3art15instrumentation15Instrumentation20DeoptimizeEverythingEv: function(address) { + const deoptimize = new NativeFunction(address, "void", ["pointer"], nativeFunctionOptions3); + this["art::Instrumentation::DeoptimizeEverything"] = function(instrumentation, key) { + deoptimize(instrumentation); + }; + }, + _ZN3art7Runtime19DeoptimizeBootImageEv: ["art::Runtime::DeoptimizeBootImage", "void", ["pointer"]], + _ZN3art15instrumentation15Instrumentation10DeoptimizeEPNS_9ArtMethodE: ["art::Instrumentation::Deoptimize", "void", ["pointer", "pointer"]], + // Android >= 11 + _ZN3art3jni12JniIdManager14DecodeMethodIdEP10_jmethodID: ["art::jni::JniIdManager::DecodeMethodId", "pointer", ["pointer", "pointer"]], + _ZN3art3jni12JniIdManager13DecodeFieldIdEP9_jfieldID: ["art::jni::JniIdManager::DecodeFieldId", "pointer", ["pointer", "pointer"]], + _ZN3art11interpreter18GetNterpEntryPointEv: ["art::interpreter::GetNterpEntryPoint", "pointer", []], + _ZN3art7Monitor17TranslateLocationEPNS_9ArtMethodEjPPKcPi: ["art::Monitor::TranslateLocation", "void", ["pointer", "uint32", "pointer", "pointer"]] + }, + variables: { + _ZN3art3Dbg9gRegistryE: function(address) { + this.isJdwpStarted = () => !address.readPointer().isNull(); + }, + _ZN3art3Dbg15gDebuggerActiveE: function(address) { + this.isDebuggerActive = () => !!address.readU8(); + } + }, + optionals: /* @__PURE__ */ new Set([ + "artInterpreterToCompiledCodeBridge", + "_ZN3art9JavaVMExt12AddGlobalRefEPNS_6ThreadENS_6ObjPtrINS_6mirror6ObjectEEE", + "_ZN3art9JavaVMExt12AddGlobalRefEPNS_6ThreadEPNS_6mirror6ObjectE", + "_ZN3art9JavaVMExt12DecodeGlobalEPv", + "_ZN3art9JavaVMExt12DecodeGlobalEPNS_6ThreadEPv", + "_ZNK3art6Thread19DecodeGlobalJObjectEP8_jobject", + "_ZNK3art6Thread13DecodeJObjectEP8_jobject", + "_ZN3art10ThreadList10SuspendAllEPKcb", + "_ZN3art10ThreadList10SuspendAllEv", + "_ZN3art11ClassLinker12VisitClassesEPNS_12ClassVisitorE", + "_ZN3art11ClassLinker12VisitClassesEPFbPNS_6mirror5ClassEPvES4_", + "_ZNK3art11ClassLinker17VisitClassLoadersEPNS_18ClassLoaderVisitorE", + "_ZN3art6mirror6Object5CloneEPNS_6ThreadE", + "_ZN3art6mirror6Object5CloneEPNS_6ThreadEm", + "_ZN3art6mirror6Object5CloneEPNS_6ThreadEj", + "_ZN3art22IndirectReferenceTable3AddEjPNS_6mirror6ObjectE", + "_ZN3art22IndirectReferenceTable3AddENS_15IRTSegmentStateENS_6ObjPtrINS_6mirror6ObjectEEE", + "_ZN3art2gc4Heap12VisitObjectsEPFvPNS_6mirror6ObjectEPvES5_", + "_ZN3art2gc4Heap12GetInstancesERNS_24VariableSizedHandleScopeENS_6HandleINS_6mirror5ClassEEEiRNSt3__16vectorINS4_INS5_6ObjectEEENS8_9allocatorISB_EEEE", + "_ZN3art2gc4Heap12GetInstancesERNS_24VariableSizedHandleScopeENS_6HandleINS_6mirror5ClassEEEbiRNSt3__16vectorINS4_INS5_6ObjectEEENS8_9allocatorISB_EEEE", + "_ZN3art12StackVisitorC2EPNS_6ThreadEPNS_7ContextENS0_13StackWalkKindEjb", + "_ZN3art12StackVisitorC2EPNS_6ThreadEPNS_7ContextENS0_13StackWalkKindEmb", + "_ZN3art12StackVisitor9WalkStackILNS0_16CountTransitionsE0EEEvb", + "_ZNK3art12StackVisitor9GetMethodEv", + "_ZNK3art12StackVisitor16DescribeLocationEv", + "_ZNK3art12StackVisitor24GetCurrentQuickFrameInfoEv", + "_ZN3art6Thread18GetLongJumpContextEv", + "_ZN3art6mirror5Class13GetDescriptorEPNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE", + "_ZN3art6mirror5Class11GetLocationEv", + "_ZN3art9ArtMethod12PrettyMethodEb", + "_ZN3art12PrettyMethodEPNS_9ArtMethodEb", + "_ZN3art3Dbg13ConfigureJdwpERKNS_4JDWP11JdwpOptionsE", + "_ZN3art31InternalDebuggerControlCallback13StartDebuggerEv", + "_ZN3art3Dbg15gDebuggerActiveE", + "_ZN3art15instrumentation15Instrumentation20EnableDeoptimizationEv", + "_ZN3art15instrumentation15Instrumentation20DeoptimizeEverythingEPKc", + "_ZN3art15instrumentation15Instrumentation20DeoptimizeEverythingEv", + "_ZN3art7Runtime19DeoptimizeBootImageEv", + "_ZN3art15instrumentation15Instrumentation10DeoptimizeEPNS_9ArtMethodE", + "_ZN3art3Dbg9StartJdwpEv", + "_ZN3art3Dbg8GoActiveEv", + "_ZN3art3Dbg21RequestDeoptimizationERKNS_21DeoptimizationRequestE", + "_ZN3art3Dbg20ManageDeoptimizationEv", + "_ZN3art3Dbg9gRegistryE", + "_ZN3art3jni12JniIdManager14DecodeMethodIdEP10_jmethodID", + "_ZN3art3jni12JniIdManager13DecodeFieldIdEP9_jfieldID", + "_ZN3art11interpreter18GetNterpEntryPointEv", + "_ZN3art7Monitor17TranslateLocationEPNS_9ArtMethodEjPPKcPi" + ]) + } : { + functions: { + _Z20dvmDecodeIndirectRefP6ThreadP8_jobject: ["dvmDecodeIndirectRef", "pointer", ["pointer", "pointer"]], + _Z15dvmUseJNIBridgeP6MethodPv: ["dvmUseJNIBridge", "void", ["pointer", "pointer"]], + _Z20dvmHeapSourceGetBasev: ["dvmHeapSourceGetBase", "pointer", []], + _Z21dvmHeapSourceGetLimitv: ["dvmHeapSourceGetLimit", "pointer", []], + _Z16dvmIsValidObjectPK6Object: ["dvmIsValidObject", "uint8", ["pointer"]], + JNI_GetCreatedJavaVMs: ["JNI_GetCreatedJavaVMs", "int", ["pointer", "int", "pointer"]] + }, + variables: { + gDvmJni: function(address) { + this.gDvmJni = address; + }, + gDvm: function(address) { + this.gDvm = address; + } + } + }; + const { + functions = {}, + variables = {}, + optionals = /* @__PURE__ */ new Set() + } = pending; + const missing = []; + for (const [name, signature] of Object.entries(functions)) { + const address = temporaryApi.find(name); + if (address !== null) { + if (typeof signature === "function") { + signature.call(temporaryApi, address); + } else { + temporaryApi[signature[0]] = new NativeFunction(address, signature[1], signature[2], nativeFunctionOptions3); + } + } else { + if (!optionals.has(name)) { + missing.push(name); + } + } + } + for (const [name, handler] of Object.entries(variables)) { + const address = temporaryApi.find(name); + if (address !== null) { + handler.call(temporaryApi, address); + } else { + if (!optionals.has(name)) { + missing.push(name); + } + } + } + if (missing.length > 0) { + throw new Error("Java API only partially available; please file a bug. Missing: " + missing.join(", ")); + } + const vms = Memory.alloc(pointerSize5); + const vmCount = Memory.alloc(jsizeSize); + checkJniResult("JNI_GetCreatedJavaVMs", temporaryApi.JNI_GetCreatedJavaVMs(vms, 1, vmCount)); + if (vmCount.readInt() === 0) { + return null; + } + temporaryApi.vm = vms.readPointer(); + if (isArt) { + const apiLevel = getAndroidApiLevel(); + let kAccCompileDontBother; + if (apiLevel >= 27) { + kAccCompileDontBother = 33554432; + } else if (apiLevel >= 24) { + kAccCompileDontBother = 16777216; + } else { + kAccCompileDontBother = 0; + } + temporaryApi.kAccCompileDontBother = kAccCompileDontBother; + const artRuntime = temporaryApi.vm.add(pointerSize5).readPointer(); + temporaryApi.artRuntime = artRuntime; + const runtimeSpec = getArtRuntimeSpec(temporaryApi); + const runtimeOffset = runtimeSpec.offset; + const instrumentationOffset = runtimeOffset.instrumentation; + temporaryApi.artInstrumentation = instrumentationOffset !== null ? artRuntime.add(instrumentationOffset) : null; + temporaryApi.artHeap = artRuntime.add(runtimeOffset.heap).readPointer(); + temporaryApi.artThreadList = artRuntime.add(runtimeOffset.threadList).readPointer(); + const classLinker = artRuntime.add(runtimeOffset.classLinker).readPointer(); + const classLinkerOffsets = getArtClassLinkerSpec(artRuntime, runtimeSpec).offset; + const quickResolutionTrampoline = classLinker.add(classLinkerOffsets.quickResolutionTrampoline).readPointer(); + const quickImtConflictTrampoline = classLinker.add(classLinkerOffsets.quickImtConflictTrampoline).readPointer(); + const quickGenericJniTrampoline = classLinker.add(classLinkerOffsets.quickGenericJniTrampoline).readPointer(); + const quickToInterpreterBridgeTrampoline = classLinker.add(classLinkerOffsets.quickToInterpreterBridgeTrampoline).readPointer(); + temporaryApi.artClassLinker = { + address: classLinker, + quickResolutionTrampoline, + quickImtConflictTrampoline, + quickGenericJniTrampoline, + quickToInterpreterBridgeTrampoline + }; + const vm3 = new VM(temporaryApi); + temporaryApi.artQuickGenericJniTrampoline = getArtQuickEntrypointFromTrampoline(quickGenericJniTrampoline, vm3); + temporaryApi.artQuickToInterpreterBridge = getArtQuickEntrypointFromTrampoline(quickToInterpreterBridgeTrampoline, vm3); + temporaryApi.artQuickResolutionTrampoline = getArtQuickEntrypointFromTrampoline(quickResolutionTrampoline, vm3); + if (temporaryApi["art::JavaVMExt::AddGlobalRef"] === void 0) { + temporaryApi["art::JavaVMExt::AddGlobalRef"] = makeAddGlobalRefFallbackForAndroid5(temporaryApi); + } + if (temporaryApi["art::JavaVMExt::DecodeGlobal"] === void 0) { + temporaryApi["art::JavaVMExt::DecodeGlobal"] = makeDecodeGlobalFallback(temporaryApi); + } + if (temporaryApi["art::ArtMethod::PrettyMethod"] === void 0) { + temporaryApi["art::ArtMethod::PrettyMethod"] = temporaryApi["art::ArtMethod::PrettyMethodNullSafe"]; + } + if (temporaryApi["art::interpreter::GetNterpEntryPoint"] !== void 0) { + temporaryApi.artNterpEntryPoint = temporaryApi["art::interpreter::GetNterpEntryPoint"](); + } else { + temporaryApi.artNterpEntryPoint = temporaryApi.find("ExecuteNterpImpl"); + } + artController = makeArtController(temporaryApi, vm3); + fixupArtQuickDeliverExceptionBug(temporaryApi); + let cachedJvmti = null; + Object.defineProperty(temporaryApi, "jvmti", { + get() { + if (cachedJvmti === null) { + cachedJvmti = [tryGetEnvJvmti(vm3, this.artRuntime)]; + } + return cachedJvmti[0]; + } + }); + } + const cxxImports = vmModule.enumerateImports().filter((imp) => imp.name.indexOf("_Z") === 0).reduce((result, imp) => { + result[imp.name] = imp.address; + return result; + }, {}); + temporaryApi.$new = new NativeFunction(cxxImports._Znwm || cxxImports._Znwj, "pointer", ["ulong"], nativeFunctionOptions3); + temporaryApi.$delete = new NativeFunction(cxxImports._ZdlPv, "void", ["pointer"], nativeFunctionOptions3); + MethodMangler = isArt ? ArtMethodMangler : DalvikMethodMangler; + return temporaryApi; +} +function tryGetEnvJvmti(vm3, runtime2) { + let env = null; + vm3.perform(() => { + const ensurePluginLoadedAddr = getApi().find("_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE"); + if (ensurePluginLoadedAddr === null) { + return; + } + const ensurePluginLoaded = new NativeFunction( + ensurePluginLoadedAddr, + "bool", + ["pointer", "pointer", "pointer"] + ); + const errorPtr = Memory.alloc(pointerSize5); + const success = ensurePluginLoaded(runtime2, Memory.allocUtf8String("libopenjdkjvmti.so"), errorPtr); + if (!success) { + return; + } + const kArtTiVersion = jvmtiVersion.v1_2 | 1073741824; + const handle = vm3.tryGetEnvHandle(kArtTiVersion); + if (handle === null) { + return; + } + env = new EnvJvmti(handle, vm3); + const capaBuf = Memory.alloc(8); + capaBuf.writeU64(jvmtiCapabilities.canTagObjects); + const result = env.addCapabilities(capaBuf); + if (result !== JNI_OK) { + env = null; + } + }); + return env; +} +function ensureClassInitialized(env, classRef) { + const api2 = getApi(); + if (api2.flavor !== "art") { + return; + } + env.getFieldId(classRef, "x", "Z"); + env.exceptionClear(); +} +function getArtVMSpec(api2) { + return { + offset: pointerSize5 === 4 ? { + globalsLock: 32, + globals: 72 + } : { + globalsLock: 64, + globals: 112 + } + }; +} +function _getArtRuntimeSpec(api2) { + const vm3 = api2.vm; + const runtime2 = api2.artRuntime; + const startOffset = pointerSize5 === 4 ? 200 : 384; + const endOffset = startOffset + 100 * pointerSize5; + const apiLevel = getAndroidApiLevel(); + const codename = getAndroidCodename(); + const { isApiLevel34OrApexEquivalent } = api2; + let spec = null; + for (let offset = startOffset; offset !== endOffset; offset += pointerSize5) { + const value = runtime2.add(offset).readPointer(); + if (value.equals(vm3)) { + let classLinkerOffsets; + let jniIdManagerOffset = null; + if (apiLevel >= 33 || codename === "Tiramisu" || isApiLevel34OrApexEquivalent) { + classLinkerOffsets = [offset - 4 * pointerSize5]; + jniIdManagerOffset = offset - pointerSize5; + } else if (apiLevel >= 30 || codename === "R") { + classLinkerOffsets = [offset - 3 * pointerSize5, offset - 4 * pointerSize5]; + jniIdManagerOffset = offset - pointerSize5; + } else if (apiLevel >= 29) { + classLinkerOffsets = [offset - 2 * pointerSize5]; + } else if (apiLevel >= 27) { + classLinkerOffsets = [offset - STD_STRING_SIZE - 3 * pointerSize5]; + } else { + classLinkerOffsets = [offset - STD_STRING_SIZE - 2 * pointerSize5]; + } + for (const classLinkerOffset of classLinkerOffsets) { + const internTableOffset = classLinkerOffset - pointerSize5; + const threadListOffset = internTableOffset - pointerSize5; + let heapOffset; + if (isApiLevel34OrApexEquivalent) { + heapOffset = threadListOffset - 9 * pointerSize5; + } else if (apiLevel >= 24) { + heapOffset = threadListOffset - 8 * pointerSize5; + } else if (apiLevel >= 23) { + heapOffset = threadListOffset - 7 * pointerSize5; + } else { + heapOffset = threadListOffset - 4 * pointerSize5; + } + const candidate = { + offset: { + heap: heapOffset, + threadList: threadListOffset, + internTable: internTableOffset, + classLinker: classLinkerOffset, + jniIdManager: jniIdManagerOffset + } + }; + if (tryGetArtClassLinkerSpec(runtime2, candidate) !== null) { + spec = candidate; + break; + } + } + break; + } + } + if (spec === null) { + throw new Error("Unable to determine Runtime field offsets"); + } + spec.offset.instrumentation = tryDetectInstrumentationOffset(api2); + spec.offset.jniIdsIndirection = tryDetectJniIdsIndirectionOffset(api2); + return spec; +} +var instrumentationOffsetParsers = { + ia32: parsex86InstrumentationOffset, + x64: parsex86InstrumentationOffset, + arm: parseArmInstrumentationOffset, + arm64: parseArm64InstrumentationOffset +}; +function tryDetectInstrumentationOffset(api2) { + const impl = api2["art::Runtime::DeoptimizeBootImage"]; + if (impl === void 0) { + return null; + } + return parseInstructionsAt(impl, instrumentationOffsetParsers[Process.arch], { limit: 30 }); +} +function parsex86InstrumentationOffset(insn) { + if (insn.mnemonic !== "lea") { + return null; + } + const offset = insn.operands[1].value.disp; + if (offset < 256 || offset > 1024) { + return null; + } + return offset; +} +function parseArmInstrumentationOffset(insn) { + if (insn.mnemonic !== "add.w") { + return null; + } + const ops = insn.operands; + if (ops.length !== 3) { + return null; + } + const op2 = ops[2]; + if (op2.type !== "imm") { + return null; + } + return op2.value; +} +function parseArm64InstrumentationOffset(insn) { + if (insn.mnemonic !== "add") { + return null; + } + const ops = insn.operands; + if (ops.length !== 3) { + return null; + } + if (ops[0].value === "sp" || ops[1].value === "sp") { + return null; + } + const op2 = ops[2]; + if (op2.type !== "imm") { + return null; + } + const offset = op2.value.valueOf(); + if (offset < 256 || offset > 1024) { + return null; + } + return offset; +} +var jniIdsIndirectionOffsetParsers = { + ia32: parsex86JniIdsIndirectionOffset, + x64: parsex86JniIdsIndirectionOffset, + arm: parseArmJniIdsIndirectionOffset, + arm64: parseArm64JniIdsIndirectionOffset +}; +function tryDetectJniIdsIndirectionOffset(api2) { + const impl = api2.find("_ZN3art7Runtime12SetJniIdTypeENS_9JniIdTypeE"); + if (impl === null) { + return null; + } + const offset = parseInstructionsAt(impl, jniIdsIndirectionOffsetParsers[Process.arch], { limit: 20 }); + if (offset === null) { + throw new Error("Unable to determine Runtime.jni_ids_indirection_ offset"); + } + return offset; +} +function parsex86JniIdsIndirectionOffset(insn) { + if (insn.mnemonic === "cmp") { + return insn.operands[0].value.disp; + } + return null; +} +function parseArmJniIdsIndirectionOffset(insn) { + if (insn.mnemonic === "ldr.w") { + return insn.operands[1].value.disp; + } + return null; +} +function parseArm64JniIdsIndirectionOffset(insn, prevInsn) { + if (prevInsn === null) { + return null; + } + const { mnemonic } = insn; + const { mnemonic: prevMnemonic } = prevInsn; + if (mnemonic === "cmp" && prevMnemonic === "ldr" || mnemonic === "bl" && prevMnemonic === "str") { + return prevInsn.operands[1].value.disp; + } + return null; +} +function _getArtInstrumentationSpec() { + const deoptimizationEnabledOffsets = { + "4-21": 136, + "4-22": 136, + "4-23": 172, + "4-24": 196, + "4-25": 196, + "4-26": 196, + "4-27": 196, + "4-28": 212, + "4-29": 172, + "4-30": 180, + "4-31": 180, + "8-21": 224, + "8-22": 224, + "8-23": 296, + "8-24": 344, + "8-25": 344, + "8-26": 352, + "8-27": 352, + "8-28": 392, + "8-29": 328, + "8-30": 336, + "8-31": 336 + }; + const deoptEnabledOffset = deoptimizationEnabledOffsets[`${pointerSize5}-${getAndroidApiLevel()}`]; + if (deoptEnabledOffset === void 0) { + throw new Error("Unable to determine Instrumentation field offsets"); + } + return { + offset: { + forcedInterpretOnly: 4, + deoptimizationEnabled: deoptEnabledOffset + } + }; +} +function getArtClassLinkerSpec(runtime2, runtimeSpec) { + const spec = tryGetArtClassLinkerSpec(runtime2, runtimeSpec); + if (spec === null) { + throw new Error("Unable to determine ClassLinker field offsets"); + } + return spec; +} +function tryGetArtClassLinkerSpec(runtime2, runtimeSpec) { + if (cachedArtClassLinkerSpec !== null) { + return cachedArtClassLinkerSpec; + } + const { classLinker: classLinkerOffset, internTable: internTableOffset } = runtimeSpec.offset; + const classLinker = runtime2.add(classLinkerOffset).readPointer(); + const internTable = runtime2.add(internTableOffset).readPointer(); + const startOffset = pointerSize5 === 4 ? 100 : 200; + const endOffset = startOffset + 100 * pointerSize5; + const apiLevel = getAndroidApiLevel(); + let spec = null; + for (let offset = startOffset; offset !== endOffset; offset += pointerSize5) { + const value = classLinker.add(offset).readPointer(); + if (value.equals(internTable)) { + let delta; + if (apiLevel >= 30 || getAndroidCodename() === "R") { + delta = 6; + } else if (apiLevel >= 29) { + delta = 4; + } else if (apiLevel >= 23) { + delta = 3; + } else { + delta = 5; + } + const quickGenericJniTrampolineOffset = offset + delta * pointerSize5; + let quickResolutionTrampolineOffset; + if (apiLevel >= 23) { + quickResolutionTrampolineOffset = quickGenericJniTrampolineOffset - 2 * pointerSize5; + } else { + quickResolutionTrampolineOffset = quickGenericJniTrampolineOffset - 3 * pointerSize5; + } + spec = { + offset: { + quickResolutionTrampoline: quickResolutionTrampolineOffset, + quickImtConflictTrampoline: quickGenericJniTrampolineOffset - pointerSize5, + quickGenericJniTrampoline: quickGenericJniTrampolineOffset, + quickToInterpreterBridgeTrampoline: quickGenericJniTrampolineOffset + pointerSize5 + } + }; + break; + } + } + if (spec !== null) { + cachedArtClassLinkerSpec = spec; + } + return spec; +} +function getArtClassSpec(vm3) { + const MAX_OFFSET = 256; + let spec = null; + vm3.perform((env) => { + const fieldSpec = getArtFieldSpec(vm3); + const methodSpec = getArtMethodSpec(vm3); + const fInfo = { + artArrayLengthSize: 4, + artArrayEntrySize: fieldSpec.size, + // java/lang/Thread has 36 fields on Android 16. + artArrayMax: 50 + }; + const mInfo = { + artArrayLengthSize: pointerSize5, + artArrayEntrySize: methodSpec.size, + // java/lang/Thread has 79 methods on Android 16. + artArrayMax: 100 + }; + const readArtArray = (objectBase, fieldOffset, lengthSize) => { + const header = objectBase.add(fieldOffset).readPointer(); + if (header.isNull()) { + return null; + } + const length = lengthSize === 4 ? header.readU32() : header.readU64().valueOf(); + if (length <= 0) { + return null; + } + return { + length, + data: header.add(lengthSize) + }; + }; + const hasEntry = (objectBase, offset, needle, info) => { + try { + const artArray = readArtArray(objectBase, offset, info.artArrayLengthSize); + if (artArray === null) { + return false; + } + const artArrayEnd = Math.min(artArray.length, info.artArrayMax); + for (let i = 0; i !== artArrayEnd; i++) { + const fieldPtr = artArray.data.add(i * info.artArrayEntrySize); + if (fieldPtr.equals(needle)) { + return true; + } + } + } catch { + } + return false; + }; + const clazz = env.findClass("java/lang/Thread"); + const clazzRef = env.newGlobalRef(clazz); + try { + let object; + withRunnableArtThread(vm3, env, (thread) => { + object = getApi()["art::JavaVMExt::DecodeGlobal"](vm3, thread, clazzRef); + }); + const fieldInstance = unwrapFieldId(env.getFieldId(clazzRef, "name", "Ljava/lang/String;")); + const fieldStatic = unwrapFieldId(env.getStaticFieldId(clazzRef, "MAX_PRIORITY", "I")); + let offsetStatic = -1; + let offsetInstance = -1; + for (let offset = 0; offset !== MAX_OFFSET; offset += 4) { + if (offsetStatic === -1 && hasEntry(object, offset, fieldStatic, fInfo)) { + offsetStatic = offset; + } + if (offsetInstance === -1 && hasEntry(object, offset, fieldInstance, fInfo)) { + offsetInstance = offset; + } + } + if (offsetInstance === -1 || offsetStatic === -1) { + throw new Error("Unable to find fields in java/lang/Thread; please file a bug"); + } + const sfieldOffset = offsetInstance !== offsetStatic ? offsetStatic : 0; + const ifieldOffset = offsetInstance; + let offsetMethods = -1; + const methodInstance = unwrapMethodId(env.getMethodId(clazzRef, "getName", "()Ljava/lang/String;")); + for (let offset = 0; offset !== MAX_OFFSET; offset += 4) { + if (offsetMethods === -1 && hasEntry(object, offset, methodInstance, mInfo)) { + offsetMethods = offset; + } + } + if (offsetMethods === -1) { + throw new Error("Unable to find methods in java/lang/Thread; please file a bug"); + } + let offsetCopiedMethods = -1; + const methodsArray = readArtArray(object, offsetMethods, mInfo.artArrayLengthSize); + const methodsArraySize = methodsArray.length; + for (let offset = offsetMethods; offset !== MAX_OFFSET; offset += 4) { + if (object.add(offset).readU16() === methodsArraySize) { + offsetCopiedMethods = offset; + break; + } + } + if (offsetCopiedMethods === -1) { + throw new Error("Unable to find copied methods in java/lang/Thread; please file a bug"); + } + spec = { + offset: { + ifields: ifieldOffset, + methods: offsetMethods, + sfields: sfieldOffset, + copiedMethodsOffset: offsetCopiedMethods + } + }; + } finally { + env.deleteLocalRef(clazz); + env.deleteGlobalRef(clazzRef); + } + }); + return spec; +} +function _getArtMethodSpec(vm3) { + const api2 = getApi(); + let spec; + vm3.perform((env) => { + const process = env.findClass("android/os/Process"); + const getElapsedCpuTime = unwrapMethodId(env.getStaticMethodId(process, "getElapsedCpuTime", "()J")); + env.deleteLocalRef(process); + const runtimeModule = Process.getModuleByName("libandroid_runtime.so"); + const runtimeStart = runtimeModule.base; + const runtimeEnd = runtimeStart.add(runtimeModule.size); + const apiLevel = getAndroidApiLevel(); + const entrypointFieldSize = apiLevel <= 21 ? 8 : pointerSize5; + const expectedAccessFlags = kAccPublic | kAccStatic | kAccFinal | kAccNative; + const relevantAccessFlagsMask = ~(kAccFastInterpreterToInterpreterInvoke | kAccPublicApi | kAccNterpInvokeFastPathFlag) >>> 0; + let jniCodeOffset = null; + let accessFlagsOffset = null; + let remaining = 2; + for (let offset = 0; offset !== 64 && remaining !== 0; offset += 4) { + const field = getElapsedCpuTime.add(offset); + if (jniCodeOffset === null) { + const address = field.readPointer(); + if (address.compare(runtimeStart) >= 0 && address.compare(runtimeEnd) < 0) { + jniCodeOffset = offset; + remaining--; + } + } + if (accessFlagsOffset === null) { + const flags = field.readU32(); + if ((flags & relevantAccessFlagsMask) === expectedAccessFlags) { + accessFlagsOffset = offset; + remaining--; + } + } + } + if (remaining !== 0) { + throw new Error("Unable to determine ArtMethod field offsets"); + } + const quickCodeOffset = jniCodeOffset + entrypointFieldSize; + const size = apiLevel <= 21 ? quickCodeOffset + 32 : quickCodeOffset + pointerSize5; + spec = { + size, + offset: { + jniCode: jniCodeOffset, + quickCode: quickCodeOffset, + accessFlags: accessFlagsOffset + } + }; + if ("artInterpreterToCompiledCodeBridge" in api2) { + spec.offset.interpreterCode = jniCodeOffset - entrypointFieldSize; + } + }); + return spec; +} +function getArtFieldSpec(vm3) { + const apiLevel = getAndroidApiLevel(); + if (apiLevel >= 23) { + return { + size: 16, + offset: { + accessFlags: 4 + } + }; + } + if (apiLevel >= 21) { + return { + size: 24, + offset: { + accessFlags: 12 + } + }; + } + return null; +} +function _getArtThreadSpec(vm3) { + const apiLevel = getAndroidApiLevel(); + let spec; + vm3.perform((env) => { + const threadHandle = getArtThreadFromEnv(env); + const envHandle = env.handle; + let isExceptionReportedOffset = null; + let exceptionOffset = null; + let throwLocationOffset = null; + let topHandleScopeOffset = null; + let managedStackOffset = null; + let selfOffset = null; + for (let offset = 144; offset !== 256; offset += pointerSize5) { + const field = threadHandle.add(offset); + const value = field.readPointer(); + if (value.equals(envHandle)) { + exceptionOffset = offset - 6 * pointerSize5; + managedStackOffset = offset - 4 * pointerSize5; + selfOffset = offset + 2 * pointerSize5; + if (apiLevel <= 22) { + exceptionOffset -= pointerSize5; + isExceptionReportedOffset = exceptionOffset - pointerSize5 - 9 * 8 - 3 * 4; + throwLocationOffset = offset + 6 * pointerSize5; + managedStackOffset -= pointerSize5; + selfOffset -= pointerSize5; + } + topHandleScopeOffset = offset + 9 * pointerSize5; + if (apiLevel <= 22) { + topHandleScopeOffset += 2 * pointerSize5 + 4; + if (pointerSize5 === 8) { + topHandleScopeOffset += 4; + } + } + if (apiLevel >= 23) { + topHandleScopeOffset += pointerSize5; + } + break; + } + } + if (topHandleScopeOffset === null) { + throw new Error("Unable to determine ArtThread field offsets"); + } + spec = { + offset: { + isExceptionReportedToInstrumentation: isExceptionReportedOffset, + exception: exceptionOffset, + throwLocation: throwLocationOffset, + topHandleScope: topHandleScopeOffset, + managedStack: managedStackOffset, + self: selfOffset + } + }; + }); + return spec; +} +function _getArtManagedStackSpec() { + const apiLevel = getAndroidApiLevel(); + if (apiLevel >= 23) { + return { + offset: { + topQuickFrame: 0, + link: pointerSize5 + } + }; + } else { + return { + offset: { + topQuickFrame: 2 * pointerSize5, + link: 0 + } + }; + } +} +var artQuickTrampolineParsers = { + ia32: parseArtQuickTrampolineX86, + x64: parseArtQuickTrampolineX86, + arm: parseArtQuickTrampolineArm, + arm64: parseArtQuickTrampolineArm64 +}; +function getArtQuickEntrypointFromTrampoline(trampoline, vm3) { + let address; + vm3.perform((env) => { + const thread = getArtThreadFromEnv(env); + const tryParse = artQuickTrampolineParsers[Process.arch]; + const insn = Instruction.parse(trampoline); + const offset = tryParse(insn); + if (offset !== null) { + address = thread.add(offset).readPointer(); + } else { + address = trampoline; + } + }); + return address; +} +function parseArtQuickTrampolineX86(insn) { + if (insn.mnemonic === "jmp") { + return insn.operands[0].value.disp; + } + return null; +} +function parseArtQuickTrampolineArm(insn) { + if (insn.mnemonic === "ldr.w") { + return insn.operands[1].value.disp; + } + return null; +} +function parseArtQuickTrampolineArm64(insn) { + if (insn.mnemonic === "ldr") { + return insn.operands[1].value.disp; + } + return null; +} +function getArtThreadFromEnv(env) { + return env.handle.add(pointerSize5).readPointer(); +} +function _getAndroidVersion() { + return getAndroidSystemProperty("ro.build.version.release"); +} +function _getAndroidCodename() { + return getAndroidSystemProperty("ro.build.version.codename"); +} +function _getAndroidApiLevel() { + return parseInt(getAndroidSystemProperty("ro.build.version.sdk"), 10); +} +var systemPropertyGet = null; +var PROP_VALUE_MAX = 92; +function getAndroidSystemProperty(name) { + if (systemPropertyGet === null) { + systemPropertyGet = new NativeFunction( + Process.getModuleByName("libc.so").getExportByName("__system_property_get"), + "int", + ["pointer", "pointer"], + nativeFunctionOptions3 + ); + } + const buf = Memory.alloc(PROP_VALUE_MAX); + systemPropertyGet(Memory.allocUtf8String(name), buf); + return buf.readUtf8String(); +} +function withRunnableArtThread(vm3, env, fn) { + const perform = getArtThreadStateTransitionImpl(vm3, env); + const id = getArtThreadFromEnv(env).toString(); + artThreadStateTransitions[id] = fn; + perform(env.handle); + if (artThreadStateTransitions[id] !== void 0) { + delete artThreadStateTransitions[id]; + throw new Error("Unable to perform state transition; please file a bug"); + } +} +function _getArtThreadStateTransitionImpl(vm3, env) { + const callback = new NativeCallback(onThreadStateTransitionComplete, "void", ["pointer"]); + return makeArtThreadStateTransitionImpl(vm3, env, callback); +} +function onThreadStateTransitionComplete(thread) { + const id = thread.toString(); + const fn = artThreadStateTransitions[id]; + delete artThreadStateTransitions[id]; + fn(thread); +} +function withAllArtThreadsSuspended(fn) { + const api2 = getApi(); + const threadList = api2.artThreadList; + const longSuspend = false; + api2["art::ThreadList::SuspendAll"](threadList, Memory.allocUtf8String("frida"), longSuspend ? 1 : 0); + try { + fn(); + } finally { + api2["art::ThreadList::ResumeAll"](threadList); + } +} +var ArtClassVisitor = class { + constructor(visit) { + const visitor = Memory.alloc(4 * pointerSize5); + const vtable2 = visitor.add(pointerSize5); + visitor.writePointer(vtable2); + const onVisit = new NativeCallback((self, klass) => { + return visit(klass) === true ? 1 : 0; + }, "bool", ["pointer", "pointer"]); + vtable2.add(2 * pointerSize5).writePointer(onVisit); + this.handle = visitor; + this._onVisit = onVisit; + } +}; +function makeArtClassVisitor(visit) { + const api2 = getApi(); + if (api2["art::ClassLinker::VisitClasses"] instanceof NativeFunction) { + return new ArtClassVisitor(visit); + } + return new NativeCallback((klass) => { + return visit(klass) === true ? 1 : 0; + }, "bool", ["pointer", "pointer"]); +} +var ArtClassLoaderVisitor = class { + constructor(visit) { + const visitor = Memory.alloc(4 * pointerSize5); + const vtable2 = visitor.add(pointerSize5); + visitor.writePointer(vtable2); + const onVisit = new NativeCallback((self, klass) => { + visit(klass); + }, "void", ["pointer", "pointer"]); + vtable2.add(2 * pointerSize5).writePointer(onVisit); + this.handle = visitor; + this._onVisit = onVisit; + } +}; +function makeArtClassLoaderVisitor(visit) { + return new ArtClassLoaderVisitor(visit); +} +var WalkKind = { + "include-inlined-frames": 0, + "skip-inlined-frames": 1 +}; +var ArtStackVisitor = class { + constructor(thread, context, walkKind, numFrames = 0, checkSuspended = true) { + const api2 = getApi(); + const baseSize = 512; + const vtableSize = 3 * pointerSize5; + const visitor = Memory.alloc(baseSize + vtableSize); + api2["art::StackVisitor::StackVisitor"]( + visitor, + thread, + context, + WalkKind[walkKind], + numFrames, + checkSuspended ? 1 : 0 + ); + const vtable2 = visitor.add(baseSize); + visitor.writePointer(vtable2); + const onVisitFrame = new NativeCallback(this._visitFrame.bind(this), "bool", ["pointer"]); + vtable2.add(2 * pointerSize5).writePointer(onVisitFrame); + this.handle = visitor; + this._onVisitFrame = onVisitFrame; + const curShadowFrame = visitor.add(pointerSize5 === 4 ? 12 : 24); + this._curShadowFrame = curShadowFrame; + this._curQuickFrame = curShadowFrame.add(pointerSize5); + this._curQuickFramePc = curShadowFrame.add(2 * pointerSize5); + this._curOatQuickMethodHeader = curShadowFrame.add(3 * pointerSize5); + this._getMethodImpl = api2["art::StackVisitor::GetMethod"]; + this._descLocImpl = api2["art::StackVisitor::DescribeLocation"]; + this._getCQFIImpl = api2["art::StackVisitor::GetCurrentQuickFrameInfo"]; + } + walkStack(includeTransitions = false) { + getApi()["art::StackVisitor::WalkStack"](this.handle, includeTransitions ? 1 : 0); + } + _visitFrame() { + return this.visitFrame() ? 1 : 0; + } + visitFrame() { + throw new Error("Subclass must implement visitFrame"); + } + getMethod() { + const methodHandle = this._getMethodImpl(this.handle); + if (methodHandle.isNull()) { + return null; + } + return new ArtMethod(methodHandle); + } + getCurrentQuickFramePc() { + return this._curQuickFramePc.readPointer(); + } + getCurrentQuickFrame() { + return this._curQuickFrame.readPointer(); + } + getCurrentShadowFrame() { + return this._curShadowFrame.readPointer(); + } + describeLocation() { + const result = new StdString(); + this._descLocImpl(result, this.handle); + return result.disposeToString(); + } + getCurrentOatQuickMethodHeader() { + return this._curOatQuickMethodHeader.readPointer(); + } + getCurrentQuickFrameInfo() { + return this._getCQFIImpl(this.handle); + } +}; +var ArtMethod = class { + constructor(handle) { + this.handle = handle; + } + prettyMethod(withSignature = true) { + const result = new StdString(); + getApi()["art::ArtMethod::PrettyMethod"](result, this.handle, withSignature ? 1 : 0); + return result.disposeToString(); + } + toString() { + return `ArtMethod(handle=${this.handle})`; + } +}; +function makeArtQuickFrameInfoGetter(impl) { + return function(self) { + const result = Memory.alloc(12); + getArtQuickFrameInfoGetterThunk(impl)(result, self); + return { + frameSizeInBytes: result.readU32(), + coreSpillMask: result.add(4).readU32(), + fpSpillMask: result.add(8).readU32() + }; + }; +} +function _getArtQuickFrameInfoGetterThunk(impl) { + let thunk = NULL; + switch (Process.arch) { + case "ia32": + thunk = makeThunk(32, (writer) => { + writer.putMovRegRegOffsetPtr("ecx", "esp", 4); + writer.putMovRegRegOffsetPtr("edx", "esp", 8); + writer.putCallAddressWithArguments(impl, ["ecx", "edx"]); + writer.putMovRegReg("esp", "ebp"); + writer.putPopReg("ebp"); + writer.putRet(); + }); + break; + case "x64": + thunk = makeThunk(32, (writer) => { + writer.putPushReg("rdi"); + writer.putCallAddressWithArguments(impl, ["rsi"]); + writer.putPopReg("rdi"); + writer.putMovRegPtrReg("rdi", "rax"); + writer.putMovRegOffsetPtrReg("rdi", 8, "edx"); + writer.putRet(); + }); + break; + case "arm": + thunk = makeThunk(16, (writer) => { + writer.putCallAddressWithArguments(impl, ["r0", "r1"]); + writer.putPopRegs(["r0", "lr"]); + writer.putMovRegReg("pc", "lr"); + }); + break; + case "arm64": + thunk = makeThunk(64, (writer) => { + writer.putPushRegReg("x0", "lr"); + writer.putCallAddressWithArguments(impl, ["x1"]); + writer.putPopRegReg("x2", "lr"); + writer.putStrRegRegOffset("x0", "x2", 0); + writer.putStrRegRegOffset("w1", "x2", 8); + writer.putRet(); + }); + break; + } + return new NativeFunction(thunk, "void", ["pointer", "pointer"], nativeFunctionOptions3); +} +var thunkRelocators = { + ia32: globalThis.X86Relocator, + x64: globalThis.X86Relocator, + arm: globalThis.ThumbRelocator, + arm64: globalThis.Arm64Relocator +}; +var thunkWriters = { + ia32: globalThis.X86Writer, + x64: globalThis.X86Writer, + arm: globalThis.ThumbWriter, + arm64: globalThis.Arm64Writer +}; +function makeThunk(size, write3) { + if (thunkPage === null) { + thunkPage = Memory.alloc(Process.pageSize); + } + const thunk = thunkPage.add(thunkOffset); + const arch = Process.arch; + const Writer = thunkWriters[arch]; + Memory.patchCode(thunk, size, (code3) => { + const writer = new Writer(code3, { pc: thunk }); + write3(writer); + writer.flush(); + if (writer.offset > size) { + throw new Error(`Wrote ${writer.offset}, exceeding maximum of ${size}`); + } + }); + thunkOffset += size; + return arch === "arm" ? thunk.or(1) : thunk; +} +function notifyArtMethodHooked(method, vm3) { + ensureArtKnowsHowToHandleMethodInstrumentation(vm3); + ensureArtKnowsHowToHandleReplacementMethods(vm3); +} +function makeArtController(api2, vm3) { + const threadOffsets = getArtThreadSpec(vm3).offset; + const managedStackOffsets = getArtManagedStackSpec().offset; + const code3 = ` +#include + +extern GMutex lock; +extern GHashTable * methods; +extern GHashTable * replacements; +extern gpointer last_seen_art_method; + +extern gpointer get_oat_quick_method_header_impl (gpointer method, gpointer pc); + +void +init (void) +{ + g_mutex_init (&lock); + methods = g_hash_table_new_full (NULL, NULL, NULL, NULL); + replacements = g_hash_table_new_full (NULL, NULL, NULL, NULL); +} + +void +finalize (void) +{ + g_hash_table_unref (replacements); + g_hash_table_unref (methods); + g_mutex_clear (&lock); +} + +gboolean +is_replacement_method (gpointer method) +{ + gboolean is_replacement; + + g_mutex_lock (&lock); + + is_replacement = g_hash_table_contains (replacements, method); + + g_mutex_unlock (&lock); + + return is_replacement; +} + +gpointer +get_replacement_method (gpointer original_method) +{ + gpointer replacement_method; + + g_mutex_lock (&lock); + + replacement_method = g_hash_table_lookup (methods, original_method); + + g_mutex_unlock (&lock); + + return replacement_method; +} + +void +set_replacement_method (gpointer original_method, + gpointer replacement_method) +{ + g_mutex_lock (&lock); + + g_hash_table_insert (methods, original_method, replacement_method); + g_hash_table_insert (replacements, replacement_method, original_method); + + g_mutex_unlock (&lock); +} + +void +delete_replacement_method (gpointer original_method) +{ + gpointer replacement_method; + + g_mutex_lock (&lock); + + replacement_method = g_hash_table_lookup (methods, original_method); + if (replacement_method != NULL) + { + g_hash_table_remove (methods, original_method); + g_hash_table_remove (replacements, replacement_method); + } + + g_mutex_unlock (&lock); +} + +gpointer +translate_method (gpointer method) +{ + gpointer translated_method; + + g_mutex_lock (&lock); + + translated_method = g_hash_table_lookup (replacements, method); + + g_mutex_unlock (&lock); + + return (translated_method != NULL) ? translated_method : method; +} + +gpointer +find_replacement_method_from_quick_code (gpointer method, + gpointer thread) +{ + gpointer replacement_method; + gpointer managed_stack; + gpointer top_quick_frame; + gpointer link_managed_stack; + gpointer * link_top_quick_frame; + + replacement_method = get_replacement_method (method); + if (replacement_method == NULL) + return NULL; + + /* + * Stack check. + * + * Return NULL to indicate that the original method should be invoked, otherwise + * return a pointer to the replacement ArtMethod. + * + * If the caller is our own JNI replacement stub, then a stack transition must + * have been pushed onto the current thread's linked list. + * + * Therefore, we invoke the original method if the following conditions are met: + * 1- The current managed stack is empty. + * 2- The ArtMethod * inside the linked managed stack's top quick frame is the + * same as our replacement. + */ + managed_stack = thread + ${threadOffsets.managedStack}; + top_quick_frame = *((gpointer *) (managed_stack + ${managedStackOffsets.topQuickFrame})); + if (top_quick_frame != NULL) + return replacement_method; + + link_managed_stack = *((gpointer *) (managed_stack + ${managedStackOffsets.link})); + if (link_managed_stack == NULL) + return replacement_method; + + link_top_quick_frame = GSIZE_TO_POINTER (*((gsize *) (link_managed_stack + ${managedStackOffsets.topQuickFrame})) & ~((gsize) 1)); + if (link_top_quick_frame == NULL || *link_top_quick_frame != replacement_method) + return replacement_method; + + return NULL; +} + +void +on_interpreter_do_call (GumInvocationContext * ic) +{ + gpointer method, replacement_method; + + method = gum_invocation_context_get_nth_argument (ic, 0); + + replacement_method = get_replacement_method (method); + if (replacement_method != NULL) + gum_invocation_context_replace_nth_argument (ic, 0, replacement_method); +} + +gpointer +on_art_method_get_oat_quick_method_header (gpointer method, + gpointer pc) +{ + if (is_replacement_method (method)) + return NULL; + + return get_oat_quick_method_header_impl (method, pc); +} + +void +on_art_method_pretty_method (GumInvocationContext * ic) +{ + const guint this_arg_index = ${Process.arch === "arm64" ? 0 : 1}; + gpointer method; + + method = gum_invocation_context_get_nth_argument (ic, this_arg_index); + if (method == NULL) + gum_invocation_context_replace_nth_argument (ic, this_arg_index, last_seen_art_method); + else + last_seen_art_method = method; +} + +void +on_leave_gc_concurrent_copying_copying_phase (GumInvocationContext * ic) +{ + GHashTableIter iter; + gpointer hooked_method, replacement_method; + + g_mutex_lock (&lock); + + g_hash_table_iter_init (&iter, methods); + while (g_hash_table_iter_next (&iter, &hooked_method, &replacement_method)) + *((uint32_t *) replacement_method) = *((uint32_t *) hooked_method); + + g_mutex_unlock (&lock); +} +`; + const lockSize = 8; + const methodsSize = pointerSize5; + const replacementsSize = pointerSize5; + const lastSeenArtMethodSize = pointerSize5; + const data = Memory.alloc(lockSize + methodsSize + replacementsSize + lastSeenArtMethodSize); + const lock = data; + const methods = lock.add(lockSize); + const replacements = methods.add(methodsSize); + const lastSeenArtMethod = replacements.add(replacementsSize); + const getOatQuickMethodHeaderImpl = api2.find(pointerSize5 === 4 ? "_ZN3art9ArtMethod23GetOatQuickMethodHeaderEj" : "_ZN3art9ArtMethod23GetOatQuickMethodHeaderEm"); + const cm2 = new CModule(code3, { + lock, + methods, + replacements, + last_seen_art_method: lastSeenArtMethod, + get_oat_quick_method_header_impl: getOatQuickMethodHeaderImpl ?? ptr("0xdeadbeef") + }); + const fastOptions = { exceptions: "propagate", scheduling: "exclusive" }; + return { + handle: cm2, + replacedMethods: { + isReplacement: new NativeFunction(cm2.is_replacement_method, "bool", ["pointer"], fastOptions), + get: new NativeFunction(cm2.get_replacement_method, "pointer", ["pointer"], fastOptions), + set: new NativeFunction(cm2.set_replacement_method, "void", ["pointer", "pointer"], fastOptions), + delete: new NativeFunction(cm2.delete_replacement_method, "void", ["pointer"], fastOptions), + translate: new NativeFunction(cm2.translate_method, "pointer", ["pointer"], fastOptions), + findReplacementFromQuickCode: cm2.find_replacement_method_from_quick_code + }, + getOatQuickMethodHeaderImpl, + hooks: { + Interpreter: { + doCall: cm2.on_interpreter_do_call + }, + ArtMethod: { + getOatQuickMethodHeader: cm2.on_art_method_get_oat_quick_method_header, + prettyMethod: cm2.on_art_method_pretty_method + }, + Gc: { + copyingPhase: { + onLeave: cm2.on_leave_gc_concurrent_copying_copying_phase + }, + runFlip: { + onEnter: cm2.on_leave_gc_concurrent_copying_copying_phase + } + } + } + }; +} +function ensureArtKnowsHowToHandleMethodInstrumentation(vm3) { + if (taughtArtAboutMethodInstrumentation) { + return; + } + taughtArtAboutMethodInstrumentation = true; + instrumentArtQuickEntrypoints(vm3); + instrumentArtMethodInvocationFromInterpreter(); +} +function instrumentArtQuickEntrypoints(vm3) { + const api2 = getApi(); + const quickEntrypoints = [ + api2.artQuickGenericJniTrampoline, + api2.artQuickToInterpreterBridge, + api2.artQuickResolutionTrampoline + ]; + quickEntrypoints.forEach((entrypoint) => { + Memory.protect(entrypoint, 32, "rwx"); + const interceptor = new ArtQuickCodeInterceptor(entrypoint); + interceptor.activate(vm3); + artQuickInterceptors.push(interceptor); + }); +} +function instrumentArtMethodInvocationFromInterpreter() { + const api2 = getApi(); + const apiLevel = getAndroidApiLevel(); + const { isApiLevel34OrApexEquivalent } = api2; + let artInterpreterDoCallExportRegex; + if (apiLevel <= 22) { + artInterpreterDoCallExportRegex = /^_ZN3art11interpreter6DoCallILb[0-1]ELb[0-1]EEEbPNS_6mirror9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtPNS_6JValueE$/; + } else if (apiLevel <= 33 && !isApiLevel34OrApexEquivalent) { + artInterpreterDoCallExportRegex = /^_ZN3art11interpreter6DoCallILb[0-1]ELb[0-1]EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtPNS_6JValueE$/; + } else if (isApiLevel34OrApexEquivalent) { + artInterpreterDoCallExportRegex = /^_ZN3art11interpreter6DoCallILb[0-1]EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtbPNS_6JValueE$/; + } else { + throw new Error("Unable to find method invocation in ART; please file a bug"); + } + const art = api2.module; + const entries = [...art.enumerateExports(), ...art.enumerateSymbols()].filter((entry) => artInterpreterDoCallExportRegex.test(entry.name)); + if (entries.length === 0) { + throw new Error("Unable to find method invocation in ART; please file a bug"); + } + for (const entry of entries) { + Interceptor.attach(entry.address, artController.hooks.Interpreter.doCall); + } +} +function ensureArtKnowsHowToHandleReplacementMethods(vm3) { + if (taughtArtAboutReplacementMethods) { + return; + } + taughtArtAboutReplacementMethods = true; + if (!maybeInstrumentGetOatQuickMethodHeaderInlineCopies()) { + const { getOatQuickMethodHeaderImpl } = artController; + if (getOatQuickMethodHeaderImpl === null) { + return; + } + try { + Interceptor.replace(getOatQuickMethodHeaderImpl, artController.hooks.ArtMethod.getOatQuickMethodHeader); + } catch (e) { + } + } + const apiLevel = getAndroidApiLevel(); + let copyingPhase = null; + const api2 = getApi(); + if (apiLevel > 28) { + copyingPhase = api2.find("_ZN3art2gc9collector17ConcurrentCopying12CopyingPhaseEv"); + } else if (apiLevel > 22) { + copyingPhase = api2.find("_ZN3art2gc9collector17ConcurrentCopying12MarkingPhaseEv"); + } + if (copyingPhase !== null) { + Interceptor.attach(copyingPhase, artController.hooks.Gc.copyingPhase); + } + let runFlip = null; + runFlip = api2.find("_ZN3art6Thread15RunFlipFunctionEPS0_"); + if (runFlip === null) { + runFlip = api2.find("_ZN3art6Thread15RunFlipFunctionEPS0_b"); + } + if (runFlip !== null) { + Interceptor.attach(runFlip, artController.hooks.Gc.runFlip); + } +} +var artGetOatQuickMethodHeaderInlinedCopyHandler = { + arm: { + signatures: [ + { + pattern: [ + "b0 68", + // ldr r0, [r6, #8] + "01 30", + // adds r0, #1 + "0c d0", + // beq #0x16fcd4 + "1b 98", + // ldr r0, [sp, #0x6c] + ":", + "c0 ff", + "c0 ff", + "00 ff", + "00 2f" + ], + validateMatch: validateGetOatQuickMethodHeaderInlinedMatchArm + }, + { + pattern: [ + "d8 f8 08 00", + // ldr r0, [r8, #8] + "01 30", + // adds r0, #1 + "0c d0", + // beq #0x16fcd4 + "1b 98", + // ldr r0, [sp, #0x6c] + ":", + "f0 ff ff 0f", + "ff ff", + "00 ff", + "00 2f" + ], + validateMatch: validateGetOatQuickMethodHeaderInlinedMatchArm + }, + { + pattern: [ + "b0 68", + // ldr r0, [r6, #8] + "01 30", + // adds r0, #1 + "40 f0 c3 80", + // bne #0x203bf0 + "00 25", + // movs r5, #0 + ":", + "c0 ff", + "c0 ff", + "c0 fb 00 d0", + "ff f8" + ], + validateMatch: validateGetOatQuickMethodHeaderInlinedMatchArm + } + ], + instrument: instrumentGetOatQuickMethodHeaderInlinedCopyArm + }, + arm64: { + signatures: [ + { + pattern: [ + /* e8 */ + "0a 40 b9", + // ldr w8, [x23, #0x8] + "1f 05 00 31", + // cmn w8, #0x1 + "40 01 00 54", + // b.eq 0x2e4204 + "88 39 00 f0", + // adrp x8, 0xa17000 + ":", + /* 00 */ + "fc ff ff", + "1f fc ff ff", + "1f 00 00 ff", + "00 00 00 9f" + ], + offset: 1, + validateMatch: validateGetOatQuickMethodHeaderInlinedMatchArm64 + }, + { + pattern: [ + /* e8 */ + "0a 40 b9", + // ldr w8, [x23, #0x8] + "1f 05 00 31", + // cmn w8, #0x1 + "01 34 00 54", + // b.ne 0x3d8e50 + "e0 03 1f aa", + // mov x0, xzr + ":", + /* 00 */ + "fc ff ff", + "1f fc ff ff", + "1f 00 00 ff", + "e0 ff ff ff" + ], + offset: 1, + validateMatch: validateGetOatQuickMethodHeaderInlinedMatchArm64 + } + ], + instrument: instrumentGetOatQuickMethodHeaderInlinedCopyArm64 + } +}; +function validateGetOatQuickMethodHeaderInlinedMatchArm({ address, size }) { + const ldr = Instruction.parse(address.or(1)); + const [ldrDst, ldrSrc] = ldr.operands; + const methodReg = ldrSrc.value.base; + const scratchReg = ldrDst.value; + const branch = Instruction.parse(ldr.next.add(2)); + const targetWhenTrue = ptr(branch.operands[0].value); + const targetWhenFalse = branch.address.add(branch.size); + let targetWhenRegularMethod, targetWhenRuntimeMethod; + if (branch.mnemonic === "beq") { + targetWhenRegularMethod = targetWhenFalse; + targetWhenRuntimeMethod = targetWhenTrue; + } else { + targetWhenRegularMethod = targetWhenTrue; + targetWhenRuntimeMethod = targetWhenFalse; + } + return parseInstructionsAt(targetWhenRegularMethod.or(1), tryParse, { limit: 3 }); + function tryParse(insn) { + const { mnemonic } = insn; + if (!(mnemonic === "ldr" || mnemonic === "ldr.w")) { + return null; + } + const { base, disp } = insn.operands[1].value; + if (!(base === methodReg && disp === 20)) { + return null; + } + return { + methodReg, + scratchReg, + target: { + whenTrue: targetWhenTrue, + whenRegularMethod: targetWhenRegularMethod, + whenRuntimeMethod: targetWhenRuntimeMethod + } + }; + } +} +function validateGetOatQuickMethodHeaderInlinedMatchArm64({ address, size }) { + const [ldrDst, ldrSrc] = Instruction.parse(address).operands; + const methodReg = ldrSrc.value.base; + const scratchReg = "x" + ldrDst.value.substring(1); + const branch = Instruction.parse(address.add(8)); + const targetWhenTrue = ptr(branch.operands[0].value); + const targetWhenFalse = address.add(12); + let targetWhenRegularMethod, targetWhenRuntimeMethod; + if (branch.mnemonic === "b.eq") { + targetWhenRegularMethod = targetWhenFalse; + targetWhenRuntimeMethod = targetWhenTrue; + } else { + targetWhenRegularMethod = targetWhenTrue; + targetWhenRuntimeMethod = targetWhenFalse; + } + return parseInstructionsAt(targetWhenRegularMethod, tryParse, { limit: 3 }); + function tryParse(insn) { + if (insn.mnemonic !== "ldr") { + return null; + } + const { base, disp } = insn.operands[1].value; + if (!(base === methodReg && disp === 24)) { + return null; + } + return { + methodReg, + scratchReg, + target: { + whenTrue: targetWhenTrue, + whenRegularMethod: targetWhenRegularMethod, + whenRuntimeMethod: targetWhenRuntimeMethod + } + }; + } +} +function maybeInstrumentGetOatQuickMethodHeaderInlineCopies() { + if (getAndroidApiLevel() < 31) { + return false; + } + const handler = artGetOatQuickMethodHeaderInlinedCopyHandler[Process.arch]; + if (handler === void 0) { + return false; + } + const signatures = handler.signatures.map(({ pattern, offset = 0, validateMatch = returnEmptyObject }) => { + return { + pattern: new MatchPattern(pattern.join("")), + offset, + validateMatch + }; + }); + const impls = []; + for (const { base, size } of getApi().module.enumerateRanges("--x")) { + for (const { pattern, offset, validateMatch } of signatures) { + const matches = Memory.scanSync(base, size, pattern).map(({ address, size: size2 }) => { + return { address: address.sub(offset), size: size2 + offset }; + }).filter((match) => { + const validationResult = validateMatch(match); + if (validationResult === null) { + return false; + } + match.validationResult = validationResult; + return true; + }); + impls.push(...matches); + } + } + if (impls.length === 0) { + return false; + } + impls.forEach(handler.instrument); + return true; +} +function returnEmptyObject() { + return {}; +} +var InlineHook = class { + constructor(address, size, trampoline) { + this.address = address; + this.size = size; + this.originalCode = address.readByteArray(size); + this.trampoline = trampoline; + } + revert() { + Memory.patchCode(this.address, this.size, (code3) => { + code3.writeByteArray(this.originalCode); + }); + } +}; +function instrumentGetOatQuickMethodHeaderInlinedCopyArm({ address, size, validationResult }) { + const { methodReg, target } = validationResult; + const trampoline = Memory.alloc(Process.pageSize); + let redirectCapacity = size; + Memory.patchCode(trampoline, 256, (code3) => { + const writer = new ThumbWriter(code3, { pc: trampoline }); + const relocator = new ThumbRelocator(address, writer); + for (let i = 0; i !== 2; i++) { + relocator.readOne(); + } + relocator.writeAll(); + relocator.readOne(); + relocator.skipOne(); + writer.putBCondLabel("eq", "runtime_or_replacement_method"); + const vpushFpRegs = [45, 237, 16, 10]; + writer.putBytes(vpushFpRegs); + const savedRegs = ["r0", "r1", "r2", "r3"]; + writer.putPushRegs(savedRegs); + writer.putCallAddressWithArguments(artController.replacedMethods.isReplacement, [methodReg]); + writer.putCmpRegImm("r0", 0); + writer.putPopRegs(savedRegs); + const vpopFpRegs = [189, 236, 16, 10]; + writer.putBytes(vpopFpRegs); + writer.putBCondLabel("ne", "runtime_or_replacement_method"); + writer.putBLabel("regular_method"); + relocator.readOne(); + const tailIsRegular = relocator.input.address.equals(target.whenRegularMethod); + writer.putLabel(tailIsRegular ? "regular_method" : "runtime_or_replacement_method"); + relocator.writeOne(); + while (redirectCapacity < 10) { + const offset = relocator.readOne(); + if (offset === 0) { + redirectCapacity = 10; + break; + } + redirectCapacity = offset; + } + relocator.writeAll(); + writer.putBranchAddress(address.add(redirectCapacity + 1)); + writer.putLabel(tailIsRegular ? "runtime_or_replacement_method" : "regular_method"); + writer.putBranchAddress(target.whenTrue); + writer.flush(); + }); + inlineHooks.push(new InlineHook(address, redirectCapacity, trampoline)); + Memory.patchCode(address, redirectCapacity, (code3) => { + const writer = new ThumbWriter(code3, { pc: address }); + writer.putLdrRegAddress("pc", trampoline.or(1)); + writer.flush(); + }); +} +function instrumentGetOatQuickMethodHeaderInlinedCopyArm64({ address, size, validationResult }) { + const { methodReg, scratchReg, target } = validationResult; + const trampoline = Memory.alloc(Process.pageSize); + Memory.patchCode(trampoline, 256, (code3) => { + const writer = new Arm64Writer(code3, { pc: trampoline }); + const relocator = new Arm64Relocator(address, writer); + for (let i = 0; i !== 2; i++) { + relocator.readOne(); + } + relocator.writeAll(); + relocator.readOne(); + relocator.skipOne(); + writer.putBCondLabel("eq", "runtime_or_replacement_method"); + const savedRegs = [ + "d0", + "d1", + "d2", + "d3", + "d4", + "d5", + "d6", + "d7", + "x0", + "x1", + "x2", + "x3", + "x4", + "x5", + "x6", + "x7", + "x8", + "x9", + "x10", + "x11", + "x12", + "x13", + "x14", + "x15", + "x16", + "x17" + ]; + const numSavedRegs = savedRegs.length; + for (let i = 0; i !== numSavedRegs; i += 2) { + writer.putPushRegReg(savedRegs[i], savedRegs[i + 1]); + } + writer.putCallAddressWithArguments(artController.replacedMethods.isReplacement, [methodReg]); + writer.putCmpRegReg("x0", "xzr"); + for (let i = numSavedRegs - 2; i >= 0; i -= 2) { + writer.putPopRegReg(savedRegs[i], savedRegs[i + 1]); + } + writer.putBCondLabel("ne", "runtime_or_replacement_method"); + writer.putBLabel("regular_method"); + relocator.readOne(); + const tailInstruction = relocator.input; + const tailIsRegular = tailInstruction.address.equals(target.whenRegularMethod); + writer.putLabel(tailIsRegular ? "regular_method" : "runtime_or_replacement_method"); + relocator.writeOne(); + writer.putBranchAddress(tailInstruction.next); + writer.putLabel(tailIsRegular ? "runtime_or_replacement_method" : "regular_method"); + writer.putBranchAddress(target.whenTrue); + writer.flush(); + }); + inlineHooks.push(new InlineHook(address, size, trampoline)); + Memory.patchCode(address, size, (code3) => { + const writer = new Arm64Writer(code3, { pc: address }); + writer.putLdrRegAddress(scratchReg, trampoline); + writer.putBrReg(scratchReg); + writer.flush(); + }); +} +function makeMethodMangler(methodId) { + return new MethodMangler(methodId); +} +function translateMethod(methodId) { + return artController.replacedMethods.translate(methodId); +} +function backtrace(vm3, options = {}) { + const { limit = 16 } = options; + const env = vm3.getEnv(); + if (backtraceModule === null) { + backtraceModule = makeBacktraceModule(vm3, env); + } + return backtraceModule.backtrace(env, limit); +} +function makeBacktraceModule(vm3, env) { + const api2 = getApi(); + const performImpl = Memory.alloc(Process.pointerSize); + const cm2 = new CModule(` +#include +#include +#include +#include +#include + +typedef struct _ArtBacktrace ArtBacktrace; +typedef struct _ArtStackFrame ArtStackFrame; + +typedef struct _ArtStackVisitor ArtStackVisitor; +typedef struct _ArtStackVisitorVTable ArtStackVisitorVTable; + +typedef struct _ArtClass ArtClass; +typedef struct _ArtMethod ArtMethod; +typedef struct _ArtThread ArtThread; +typedef struct _ArtContext ArtContext; + +typedef struct _JNIEnv JNIEnv; + +typedef struct _StdString StdString; +typedef struct _StdTinyString StdTinyString; +typedef struct _StdLargeString StdLargeString; + +typedef enum { + STACK_WALK_INCLUDE_INLINED_FRAMES, + STACK_WALK_SKIP_INLINED_FRAMES, +} StackWalkKind; + +struct _StdTinyString +{ + guint8 unused; + gchar data[(3 * sizeof (gpointer)) - 1]; +}; + +struct _StdLargeString +{ + gsize capacity; + gsize size; + gchar * data; +}; + +struct _StdString +{ + union + { + guint8 flags; + StdTinyString tiny; + StdLargeString large; + }; +}; + +struct _ArtBacktrace +{ + GChecksum * id; + GArray * frames; + gchar * frames_json; +}; + +struct _ArtStackFrame +{ + ArtMethod * method; + gsize dexpc; + StdString description; +}; + +struct _ArtStackVisitorVTable +{ + void (* unused1) (void); + void (* unused2) (void); + bool (* visit) (ArtStackVisitor * visitor); +}; + +struct _ArtStackVisitor +{ + ArtStackVisitorVTable * vtable; + + guint8 padding[512]; + + ArtStackVisitorVTable vtable_storage; + + ArtBacktrace * backtrace; +}; + +struct _ArtMethod +{ + guint32 declaring_class; + guint32 access_flags; +}; + +extern GumTlsKey current_backtrace; + +extern void (* perform_art_thread_state_transition) (JNIEnv * env); + +extern ArtContext * art_thread_get_long_jump_context (ArtThread * thread); + +extern void art_stack_visitor_init (ArtStackVisitor * visitor, ArtThread * thread, void * context, StackWalkKind walk_kind, + size_t num_frames, bool check_suspended); +extern void art_stack_visitor_walk_stack (ArtStackVisitor * visitor, bool include_transitions); +extern ArtMethod * art_stack_visitor_get_method (ArtStackVisitor * visitor); +extern void art_stack_visitor_describe_location (StdString * description, ArtStackVisitor * visitor); +extern ArtMethod * translate_method (ArtMethod * method); +extern void translate_location (ArtMethod * method, guint32 pc, const gchar ** source_file, gint32 * line_number); +extern void get_class_location (StdString * result, ArtClass * klass); +extern void cxx_delete (void * mem); +extern unsigned long strtoul (const char * str, char ** endptr, int base); + +static bool visit_frame (ArtStackVisitor * visitor); +static void art_stack_frame_destroy (ArtStackFrame * frame); + +static void append_jni_type_name (GString * s, const gchar * name, gsize length); + +static void std_string_destroy (StdString * str); +static gchar * std_string_get_data (StdString * str); + +void +init (void) +{ + current_backtrace = gum_tls_key_new (); +} + +void +finalize (void) +{ + gum_tls_key_free (current_backtrace); +} + +ArtBacktrace * +_create (JNIEnv * env, + guint limit) +{ + ArtBacktrace * bt; + + bt = g_new (ArtBacktrace, 1); + bt->id = g_checksum_new (G_CHECKSUM_SHA1); + bt->frames = (limit != 0) + ? g_array_sized_new (FALSE, FALSE, sizeof (ArtStackFrame), limit) + : g_array_new (FALSE, FALSE, sizeof (ArtStackFrame)); + g_array_set_clear_func (bt->frames, (GDestroyNotify) art_stack_frame_destroy); + bt->frames_json = NULL; + + gum_tls_key_set_value (current_backtrace, bt); + + perform_art_thread_state_transition (env); + + gum_tls_key_set_value (current_backtrace, NULL); + + return bt; +} + +void +_on_thread_state_transition_complete (ArtThread * thread) +{ + ArtContext * context; + ArtStackVisitor visitor = { + .vtable_storage = { + .visit = visit_frame, + }, + }; + + context = art_thread_get_long_jump_context (thread); + + art_stack_visitor_init (&visitor, thread, context, STACK_WALK_SKIP_INLINED_FRAMES, 0, true); + visitor.vtable = &visitor.vtable_storage; + visitor.backtrace = gum_tls_key_get_value (current_backtrace); + + art_stack_visitor_walk_stack (&visitor, false); + + cxx_delete (context); +} + +static bool +visit_frame (ArtStackVisitor * visitor) +{ + ArtBacktrace * bt = visitor->backtrace; + ArtStackFrame frame; + const gchar * description, * dexpc_part; + + frame.method = art_stack_visitor_get_method (visitor); + + art_stack_visitor_describe_location (&frame.description, visitor); + + description = std_string_get_data (&frame.description); + if (strstr (description, " '<") != NULL) + goto skip; + + dexpc_part = strstr (description, " at dex PC 0x"); + if (dexpc_part == NULL) + goto skip; + frame.dexpc = strtoul (dexpc_part + 13, NULL, 16); + + g_array_append_val (bt->frames, frame); + + g_checksum_update (bt->id, (guchar *) &frame.method, sizeof (frame.method)); + g_checksum_update (bt->id, (guchar *) &frame.dexpc, sizeof (frame.dexpc)); + + return true; + +skip: + std_string_destroy (&frame.description); + return true; +} + +static void +art_stack_frame_destroy (ArtStackFrame * frame) +{ + std_string_destroy (&frame->description); +} + +void +_destroy (ArtBacktrace * backtrace) +{ + g_free (backtrace->frames_json); + g_array_free (backtrace->frames, TRUE); + g_checksum_free (backtrace->id); + g_free (backtrace); +} + +const gchar * +_get_id (ArtBacktrace * backtrace) +{ + return g_checksum_get_string (backtrace->id); +} + +const gchar * +_get_frames (ArtBacktrace * backtrace) +{ + GArray * frames = backtrace->frames; + JsonBuilder * b; + guint i; + JsonNode * root; + + if (backtrace->frames_json != NULL) + return backtrace->frames_json; + + b = json_builder_new_immutable (); + + json_builder_begin_array (b); + + for (i = 0; i != frames->len; i++) + { + ArtStackFrame * frame = &g_array_index (frames, ArtStackFrame, i); + gchar * description, * ret_type, * paren_open, * paren_close, * arg_types, * token, * method_name, * class_name; + GString * signature; + gchar * cursor; + ArtMethod * translated_method; + StdString location; + gsize dexpc; + const gchar * source_file; + gint32 line_number; + + description = std_string_get_data (&frame->description); + + ret_type = strchr (description, '\\'') + 1; + + paren_open = strchr (ret_type, '('); + paren_close = strchr (paren_open, ')'); + *paren_open = '\\0'; + *paren_close = '\\0'; + + arg_types = paren_open + 1; + + token = strrchr (ret_type, '.'); + *token = '\\0'; + + method_name = token + 1; + + token = strrchr (ret_type, ' '); + *token = '\\0'; + + class_name = token + 1; + + signature = g_string_sized_new (128); + + append_jni_type_name (signature, class_name, method_name - class_name - 1); + g_string_append_c (signature, ','); + g_string_append (signature, method_name); + g_string_append (signature, ",("); + + if (arg_types != paren_close) + { + for (cursor = arg_types; cursor != NULL;) + { + gsize length; + gchar * next; + + token = strstr (cursor, ", "); + if (token != NULL) + { + length = token - cursor; + next = token + 2; + } + else + { + length = paren_close - cursor; + next = NULL; + } + + append_jni_type_name (signature, cursor, length); + + cursor = next; + } + } + + g_string_append_c (signature, ')'); + + append_jni_type_name (signature, ret_type, class_name - ret_type - 1); + + translated_method = translate_method (frame->method); + dexpc = (translated_method == frame->method) ? frame->dexpc : 0; + + get_class_location (&location, GSIZE_TO_POINTER (translated_method->declaring_class)); + + translate_location (translated_method, dexpc, &source_file, &line_number); + + json_builder_begin_object (b); + + json_builder_set_member_name (b, "signature"); + json_builder_add_string_value (b, signature->str); + + json_builder_set_member_name (b, "origin"); + json_builder_add_string_value (b, std_string_get_data (&location)); + + json_builder_set_member_name (b, "className"); + json_builder_add_string_value (b, class_name); + + json_builder_set_member_name (b, "methodName"); + json_builder_add_string_value (b, method_name); + + json_builder_set_member_name (b, "methodFlags"); + json_builder_add_int_value (b, translated_method->access_flags); + + json_builder_set_member_name (b, "fileName"); + json_builder_add_string_value (b, source_file); + + json_builder_set_member_name (b, "lineNumber"); + json_builder_add_int_value (b, line_number); + + json_builder_end_object (b); + + std_string_destroy (&location); + g_string_free (signature, TRUE); + } + + json_builder_end_array (b); + + root = json_builder_get_root (b); + backtrace->frames_json = json_to_string (root, FALSE); + json_node_unref (root); + + return backtrace->frames_json; +} + +static void +append_jni_type_name (GString * s, + const gchar * name, + gsize length) +{ + gchar shorty = '\\0'; + gsize i; + + switch (name[0]) + { + case 'b': + if (strncmp (name, "boolean", length) == 0) + shorty = 'Z'; + else if (strncmp (name, "byte", length) == 0) + shorty = 'B'; + break; + case 'c': + if (strncmp (name, "char", length) == 0) + shorty = 'C'; + break; + case 'd': + if (strncmp (name, "double", length) == 0) + shorty = 'D'; + break; + case 'f': + if (strncmp (name, "float", length) == 0) + shorty = 'F'; + break; + case 'i': + if (strncmp (name, "int", length) == 0) + shorty = 'I'; + break; + case 'l': + if (strncmp (name, "long", length) == 0) + shorty = 'J'; + break; + case 's': + if (strncmp (name, "short", length) == 0) + shorty = 'S'; + break; + case 'v': + if (strncmp (name, "void", length) == 0) + shorty = 'V'; + break; + } + + if (shorty != '\\0') + { + g_string_append_c (s, shorty); + + return; + } + + if (length > 2 && name[length - 2] == '[' && name[length - 1] == ']') + { + g_string_append_c (s, '['); + append_jni_type_name (s, name, length - 2); + + return; + } + + g_string_append_c (s, 'L'); + + for (i = 0; i != length; i++) + { + gchar ch = name[i]; + if (ch != '.') + g_string_append_c (s, ch); + else + g_string_append_c (s, '/'); + } + + g_string_append_c (s, ';'); +} + +static void +std_string_destroy (StdString * str) +{ + bool is_large = (str->flags & 1) != 0; + if (is_large) + cxx_delete (str->large.data); +} + +static gchar * +std_string_get_data (StdString * str) +{ + bool is_large = (str->flags & 1) != 0; + return is_large ? str->large.data : str->tiny.data; +} +`, { + current_backtrace: Memory.alloc(Process.pointerSize), + perform_art_thread_state_transition: performImpl, + art_thread_get_long_jump_context: api2["art::Thread::GetLongJumpContext"], + art_stack_visitor_init: api2["art::StackVisitor::StackVisitor"], + art_stack_visitor_walk_stack: api2["art::StackVisitor::WalkStack"], + art_stack_visitor_get_method: api2["art::StackVisitor::GetMethod"], + art_stack_visitor_describe_location: api2["art::StackVisitor::DescribeLocation"], + translate_method: artController.replacedMethods.translate, + translate_location: api2["art::Monitor::TranslateLocation"], + get_class_location: api2["art::mirror::Class::GetLocation"], + cxx_delete: api2.$delete, + strtoul: Process.getModuleByName("libc.so").getExportByName("strtoul") + }); + const _create = new NativeFunction(cm2._create, "pointer", ["pointer", "uint"], nativeFunctionOptions3); + const _destroy = new NativeFunction(cm2._destroy, "void", ["pointer"], nativeFunctionOptions3); + const fastOptions = { exceptions: "propagate", scheduling: "exclusive" }; + const _getId = new NativeFunction(cm2._get_id, "pointer", ["pointer"], fastOptions); + const _getFrames = new NativeFunction(cm2._get_frames, "pointer", ["pointer"], fastOptions); + const performThreadStateTransition = makeArtThreadStateTransitionImpl(vm3, env, cm2._on_thread_state_transition_complete); + cm2._performData = performThreadStateTransition; + performImpl.writePointer(performThreadStateTransition); + cm2.backtrace = (env2, limit) => { + const handle = _create(env2, limit); + const bt = new Backtrace(handle); + Script.bindWeak(bt, destroy.bind(null, handle)); + return bt; + }; + function destroy(handle) { + _destroy(handle); + } + cm2.getId = (handle) => { + return _getId(handle).readUtf8String(); + }; + cm2.getFrames = (handle) => { + return JSON.parse(_getFrames(handle).readUtf8String()); + }; + return cm2; +} +var Backtrace = class { + constructor(handle) { + this.handle = handle; + } + get id() { + return backtraceModule.getId(this.handle); + } + get frames() { + return backtraceModule.getFrames(this.handle); + } +}; +function revertGlobalPatches() { + patchedClasses.forEach((entry) => { + entry.vtablePtr.writePointer(entry.vtable); + entry.vtableCountPtr.writeS32(entry.vtableCount); + }); + patchedClasses.clear(); + for (const interceptor of artQuickInterceptors.splice(0)) { + interceptor.deactivate(); + } + for (const hook of inlineHooks.splice(0)) { + hook.revert(); + } +} +function unwrapMethodId(methodId) { + return unwrapGenericId(methodId, "art::jni::JniIdManager::DecodeMethodId"); +} +function unwrapFieldId(fieldId) { + return unwrapGenericId(fieldId, "art::jni::JniIdManager::DecodeFieldId"); +} +function unwrapGenericId(genericId, apiMethod) { + const api2 = getApi(); + const runtimeOffset = getArtRuntimeSpec(api2).offset; + const jniIdManagerOffset = runtimeOffset.jniIdManager; + const jniIdsIndirectionOffset = runtimeOffset.jniIdsIndirection; + if (jniIdManagerOffset !== null && jniIdsIndirectionOffset !== null) { + const runtime2 = api2.artRuntime; + const jniIdsIndirection = runtime2.add(jniIdsIndirectionOffset).readInt(); + if (jniIdsIndirection !== kPointer) { + const jniIdManager = runtime2.add(jniIdManagerOffset).readPointer(); + return api2[apiMethod](jniIdManager, genericId); + } + } + return genericId; +} +var artQuickCodeReplacementTrampolineWriters = { + ia32: writeArtQuickCodeReplacementTrampolineIA32, + x64: writeArtQuickCodeReplacementTrampolineX64, + arm: writeArtQuickCodeReplacementTrampolineArm, + arm64: writeArtQuickCodeReplacementTrampolineArm64 +}; +function writeArtQuickCodeReplacementTrampolineIA32(trampoline, target, redirectSize, constraints, vm3) { + const threadOffsets = getArtThreadSpec(vm3).offset; + const artMethodOffsets = getArtMethodSpec(vm3).offset; + let offset; + Memory.patchCode(trampoline, 128, (code3) => { + const writer = new X86Writer(code3, { pc: trampoline }); + const relocator = new X86Relocator(target, writer); + const fxsave = [15, 174, 4, 36]; + const fxrstor = [15, 174, 12, 36]; + writer.putPushax(); + writer.putMovRegReg("ebp", "esp"); + writer.putAndRegU32("esp", 4294967280); + writer.putSubRegImm("esp", 512); + writer.putBytes(fxsave); + writer.putMovRegFsU32Ptr("ebx", threadOffsets.self); + writer.putCallAddressWithAlignedArguments(artController.replacedMethods.findReplacementFromQuickCode, ["eax", "ebx"]); + writer.putTestRegReg("eax", "eax"); + writer.putJccShortLabel("je", "restore_registers", "no-hint"); + writer.putMovRegOffsetPtrReg("ebp", 7 * 4, "eax"); + writer.putLabel("restore_registers"); + writer.putBytes(fxrstor); + writer.putMovRegReg("esp", "ebp"); + writer.putPopax(); + writer.putJccShortLabel("jne", "invoke_replacement", "no-hint"); + do { + offset = relocator.readOne(); + } while (offset < redirectSize && !relocator.eoi); + relocator.writeAll(); + if (!relocator.eoi) { + writer.putJmpAddress(target.add(offset)); + } + writer.putLabel("invoke_replacement"); + writer.putJmpRegOffsetPtr("eax", artMethodOffsets.quickCode); + writer.flush(); + }); + return offset; +} +function writeArtQuickCodeReplacementTrampolineX64(trampoline, target, redirectSize, constraints, vm3) { + const threadOffsets = getArtThreadSpec(vm3).offset; + const artMethodOffsets = getArtMethodSpec(vm3).offset; + let offset; + Memory.patchCode(trampoline, 256, (code3) => { + const writer = new X86Writer(code3, { pc: trampoline }); + const relocator = new X86Relocator(target, writer); + const fxsave = [15, 174, 4, 36]; + const fxrstor = [15, 174, 12, 36]; + writer.putPushax(); + writer.putMovRegReg("rbp", "rsp"); + writer.putAndRegU32("rsp", 4294967280); + writer.putSubRegImm("rsp", 512); + writer.putBytes(fxsave); + writer.putMovRegGsU32Ptr("rbx", threadOffsets.self); + writer.putCallAddressWithAlignedArguments(artController.replacedMethods.findReplacementFromQuickCode, ["rdi", "rbx"]); + writer.putTestRegReg("rax", "rax"); + writer.putJccShortLabel("je", "restore_registers", "no-hint"); + writer.putMovRegOffsetPtrReg("rbp", 8 * 8, "rax"); + writer.putLabel("restore_registers"); + writer.putBytes(fxrstor); + writer.putMovRegReg("rsp", "rbp"); + writer.putPopax(); + writer.putJccShortLabel("jne", "invoke_replacement", "no-hint"); + do { + offset = relocator.readOne(); + } while (offset < redirectSize && !relocator.eoi); + relocator.writeAll(); + if (!relocator.eoi) { + writer.putJmpAddress(target.add(offset)); + } + writer.putLabel("invoke_replacement"); + writer.putJmpRegOffsetPtr("rdi", artMethodOffsets.quickCode); + writer.flush(); + }); + return offset; +} +function writeArtQuickCodeReplacementTrampolineArm(trampoline, target, redirectSize, constraints, vm3) { + const artMethodOffsets = getArtMethodSpec(vm3).offset; + const targetAddress = target.and(THUMB_BIT_REMOVAL_MASK); + let offset; + Memory.patchCode(trampoline, 128, (code3) => { + const writer = new ThumbWriter(code3, { pc: trampoline }); + const relocator = new ThumbRelocator(targetAddress, writer); + const vpushFpRegs = [45, 237, 16, 10]; + const vpopFpRegs = [189, 236, 16, 10]; + writer.putPushRegs([ + "r1", + "r2", + "r3", + "r5", + "r6", + "r7", + "r8", + "r10", + "r11", + "lr" + ]); + writer.putBytes(vpushFpRegs); + writer.putSubRegRegImm("sp", "sp", 8); + writer.putStrRegRegOffset("r0", "sp", 0); + writer.putCallAddressWithArguments(artController.replacedMethods.findReplacementFromQuickCode, ["r0", "r9"]); + writer.putCmpRegImm("r0", 0); + writer.putBCondLabel("eq", "restore_registers"); + writer.putStrRegRegOffset("r0", "sp", 0); + writer.putLabel("restore_registers"); + writer.putLdrRegRegOffset("r0", "sp", 0); + writer.putAddRegRegImm("sp", "sp", 8); + writer.putBytes(vpopFpRegs); + writer.putPopRegs([ + "lr", + "r11", + "r10", + "r8", + "r7", + "r6", + "r5", + "r3", + "r2", + "r1" + ]); + writer.putBCondLabel("ne", "invoke_replacement"); + do { + offset = relocator.readOne(); + } while (offset < redirectSize && !relocator.eoi); + relocator.writeAll(); + if (!relocator.eoi) { + writer.putLdrRegAddress("pc", target.add(offset)); + } + writer.putLabel("invoke_replacement"); + writer.putLdrRegRegOffset("pc", "r0", artMethodOffsets.quickCode); + writer.flush(); + }); + return offset; +} +function writeArtQuickCodeReplacementTrampolineArm64(trampoline, target, redirectSize, { availableScratchRegs }, vm3) { + const artMethodOffsets = getArtMethodSpec(vm3).offset; + let offset; + Memory.patchCode(trampoline, 256, (code3) => { + const writer = new Arm64Writer(code3, { pc: trampoline }); + const relocator = new Arm64Relocator(target, writer); + writer.putPushRegReg("d0", "d1"); + writer.putPushRegReg("d2", "d3"); + writer.putPushRegReg("d4", "d5"); + writer.putPushRegReg("d6", "d7"); + writer.putPushRegReg("x1", "x2"); + writer.putPushRegReg("x3", "x4"); + writer.putPushRegReg("x5", "x6"); + writer.putPushRegReg("x7", "x20"); + writer.putPushRegReg("x21", "x22"); + writer.putPushRegReg("x23", "x24"); + writer.putPushRegReg("x25", "x26"); + writer.putPushRegReg("x27", "x28"); + writer.putPushRegReg("x29", "lr"); + writer.putSubRegRegImm("sp", "sp", 16); + writer.putStrRegRegOffset("x0", "sp", 0); + writer.putCallAddressWithArguments(artController.replacedMethods.findReplacementFromQuickCode, ["x0", "x19"]); + writer.putCmpRegReg("x0", "xzr"); + writer.putBCondLabel("eq", "restore_registers"); + writer.putStrRegRegOffset("x0", "sp", 0); + writer.putLabel("restore_registers"); + writer.putLdrRegRegOffset("x0", "sp", 0); + writer.putAddRegRegImm("sp", "sp", 16); + writer.putPopRegReg("x29", "lr"); + writer.putPopRegReg("x27", "x28"); + writer.putPopRegReg("x25", "x26"); + writer.putPopRegReg("x23", "x24"); + writer.putPopRegReg("x21", "x22"); + writer.putPopRegReg("x7", "x20"); + writer.putPopRegReg("x5", "x6"); + writer.putPopRegReg("x3", "x4"); + writer.putPopRegReg("x1", "x2"); + writer.putPopRegReg("d6", "d7"); + writer.putPopRegReg("d4", "d5"); + writer.putPopRegReg("d2", "d3"); + writer.putPopRegReg("d0", "d1"); + writer.putBCondLabel("ne", "invoke_replacement"); + do { + offset = relocator.readOne(); + } while (offset < redirectSize && !relocator.eoi); + relocator.writeAll(); + if (!relocator.eoi) { + const scratchReg = Array.from(availableScratchRegs)[0]; + writer.putLdrRegAddress(scratchReg, target.add(offset)); + writer.putBrReg(scratchReg); + } + writer.putLabel("invoke_replacement"); + writer.putLdrRegRegOffset("x16", "x0", artMethodOffsets.quickCode); + writer.putBrReg("x16"); + writer.flush(); + }); + return offset; +} +var artQuickCodePrologueWriters = { + ia32: writeArtQuickCodePrologueX86, + x64: writeArtQuickCodePrologueX86, + arm: writeArtQuickCodePrologueArm, + arm64: writeArtQuickCodePrologueArm64 +}; +function writeArtQuickCodePrologueX86(target, trampoline, redirectSize) { + Memory.patchCode(target, 16, (code3) => { + const writer = new X86Writer(code3, { pc: target }); + writer.putJmpAddress(trampoline); + writer.flush(); + }); +} +function writeArtQuickCodePrologueArm(target, trampoline, redirectSize) { + const targetAddress = target.and(THUMB_BIT_REMOVAL_MASK); + Memory.patchCode(targetAddress, 16, (code3) => { + const writer = new ThumbWriter(code3, { pc: targetAddress }); + writer.putLdrRegAddress("pc", trampoline.or(1)); + writer.flush(); + }); +} +function writeArtQuickCodePrologueArm64(target, trampoline, redirectSize) { + Memory.patchCode(target, 16, (code3) => { + const writer = new Arm64Writer(code3, { pc: target }); + if (redirectSize === 16) { + writer.putLdrRegAddress("x16", trampoline); + } else { + writer.putAdrpRegAddress("x16", trampoline); + } + writer.putBrReg("x16"); + writer.flush(); + }); +} +var artQuickCodeHookRedirectSize = { + ia32: 5, + x64: 16, + arm: 8, + arm64: 16 +}; +var ArtQuickCodeInterceptor = class { + constructor(quickCode) { + this.quickCode = quickCode; + this.quickCodeAddress = Process.arch === "arm" ? quickCode.and(THUMB_BIT_REMOVAL_MASK) : quickCode; + this.redirectSize = 0; + this.trampoline = null; + this.overwrittenPrologue = null; + this.overwrittenPrologueLength = 0; + } + _canRelocateCode(relocationSize, constraints) { + const Writer = thunkWriters[Process.arch]; + const Relocator = thunkRelocators[Process.arch]; + const { quickCodeAddress } = this; + const writer = new Writer(quickCodeAddress); + const relocator = new Relocator(quickCodeAddress, writer); + let offset; + if (Process.arch === "arm64") { + let availableScratchRegs = /* @__PURE__ */ new Set(["x16", "x17"]); + do { + const nextOffset = relocator.readOne(); + const nextScratchRegs = new Set(availableScratchRegs); + const { read: read2, written } = relocator.input.regsAccessed; + for (const regs of [read2, written]) { + for (const reg of regs) { + let name; + if (reg.startsWith("w")) { + name = "x" + reg.substring(1); + } else { + name = reg; + } + nextScratchRegs.delete(name); + } + } + if (nextScratchRegs.size === 0) { + break; + } + offset = nextOffset; + availableScratchRegs = nextScratchRegs; + } while (offset < relocationSize && !relocator.eoi); + constraints.availableScratchRegs = availableScratchRegs; + } else { + do { + offset = relocator.readOne(); + } while (offset < relocationSize && !relocator.eoi); + } + return offset >= relocationSize; + } + _allocateTrampoline() { + if (trampolineAllocator === null) { + const trampolineSize = pointerSize5 === 4 ? 128 : 256; + trampolineAllocator = makeAllocator(trampolineSize); + } + const maxRedirectSize = artQuickCodeHookRedirectSize[Process.arch]; + let redirectSize, spec; + let alignment = 1; + const constraints = {}; + if (pointerSize5 === 4 || this._canRelocateCode(maxRedirectSize, constraints)) { + redirectSize = maxRedirectSize; + spec = {}; + } else { + let maxDistance; + if (Process.arch === "x64") { + redirectSize = 5; + maxDistance = X86_JMP_MAX_DISTANCE; + } else if (Process.arch === "arm64") { + redirectSize = 8; + maxDistance = ARM64_ADRP_MAX_DISTANCE; + alignment = 4096; + } + spec = { near: this.quickCodeAddress, maxDistance }; + } + this.redirectSize = redirectSize; + this.trampoline = trampolineAllocator.allocateSlice(spec, alignment); + return constraints; + } + _destroyTrampoline() { + trampolineAllocator.freeSlice(this.trampoline); + } + activate(vm3) { + const constraints = this._allocateTrampoline(); + const { trampoline, quickCode, redirectSize } = this; + const writeTrampoline = artQuickCodeReplacementTrampolineWriters[Process.arch]; + const prologueLength = writeTrampoline(trampoline, quickCode, redirectSize, constraints, vm3); + this.overwrittenPrologueLength = prologueLength; + this.overwrittenPrologue = Memory.dup(this.quickCodeAddress, prologueLength); + const writePrologue = artQuickCodePrologueWriters[Process.arch]; + writePrologue(quickCode, trampoline, redirectSize); + } + deactivate() { + const { quickCodeAddress, overwrittenPrologueLength: prologueLength } = this; + const Writer = thunkWriters[Process.arch]; + Memory.patchCode(quickCodeAddress, prologueLength, (code3) => { + const writer = new Writer(code3, { pc: quickCodeAddress }); + const { overwrittenPrologue } = this; + writer.putBytes(overwrittenPrologue.readByteArray(prologueLength)); + writer.flush(); + }); + this._destroyTrampoline(); + } +}; +function isArtQuickEntrypoint(address) { + const api2 = getApi(); + const { module: m, artClassLinker } = api2; + return address.equals(artClassLinker.quickGenericJniTrampoline) || address.equals(artClassLinker.quickToInterpreterBridgeTrampoline) || address.equals(artClassLinker.quickResolutionTrampoline) || address.equals(artClassLinker.quickImtConflictTrampoline) || address.compare(m.base) >= 0 && address.compare(m.base.add(m.size)) < 0; +} +var ArtMethodMangler = class { + constructor(opaqueMethodId) { + const methodId = unwrapMethodId(opaqueMethodId); + this.methodId = methodId; + this.originalMethod = null; + this.hookedMethodId = methodId; + this.replacementMethodId = null; + this.interceptor = null; + } + replace(impl, isInstanceMethod, argTypes, vm3, api2) { + const { kAccCompileDontBother, artNterpEntryPoint } = api2; + this.originalMethod = fetchArtMethod(this.methodId, vm3); + const originalFlags = this.originalMethod.accessFlags; + if ((originalFlags & kAccXposedHookedMethod) !== 0 && xposedIsSupported()) { + const hookInfo = this.originalMethod.jniCode; + this.hookedMethodId = hookInfo.add(2 * pointerSize5).readPointer(); + this.originalMethod = fetchArtMethod(this.hookedMethodId, vm3); + } + const { hookedMethodId } = this; + const replacementMethodId = cloneArtMethod(hookedMethodId, vm3); + this.replacementMethodId = replacementMethodId; + patchArtMethod(replacementMethodId, { + jniCode: impl, + accessFlags: (originalFlags & ~(kAccCriticalNative | kAccFastNative | kAccNterpEntryPointFastPathFlag) | kAccNative | kAccCompileDontBother) >>> 0, + quickCode: api2.artClassLinker.quickGenericJniTrampoline, + interpreterCode: api2.artInterpreterToCompiledCodeBridge + }, vm3); + let hookedMethodRemovedFlags = kAccFastInterpreterToInterpreterInvoke | kAccSingleImplementation | kAccNterpEntryPointFastPathFlag; + if ((originalFlags & kAccNative) === 0) { + hookedMethodRemovedFlags |= kAccSkipAccessChecks; + } + patchArtMethod(hookedMethodId, { + accessFlags: (originalFlags & ~hookedMethodRemovedFlags | kAccCompileDontBother) >>> 0 + }, vm3); + const quickCode = this.originalMethod.quickCode; + if (artNterpEntryPoint !== null && quickCode.equals(artNterpEntryPoint)) { + patchArtMethod(hookedMethodId, { + quickCode: api2.artQuickToInterpreterBridge + }, vm3); + } + if (!isArtQuickEntrypoint(quickCode)) { + const interceptor = new ArtQuickCodeInterceptor(quickCode); + interceptor.activate(vm3); + this.interceptor = interceptor; + } + artController.replacedMethods.set(hookedMethodId, replacementMethodId); + notifyArtMethodHooked(hookedMethodId, vm3); + } + revert(vm3) { + const { hookedMethodId, interceptor } = this; + patchArtMethod(hookedMethodId, this.originalMethod, vm3); + artController.replacedMethods.delete(hookedMethodId); + if (interceptor !== null) { + interceptor.deactivate(); + this.interceptor = null; + } + } + resolveTarget(wrapper, isInstanceMethod, env, api2) { + return this.hookedMethodId; + } +}; +function xposedIsSupported() { + return getAndroidApiLevel() < 28; +} +function fetchArtMethod(methodId, vm3) { + const artMethodSpec = getArtMethodSpec(vm3); + const artMethodOffset = artMethodSpec.offset; + return ["jniCode", "accessFlags", "quickCode", "interpreterCode"].reduce((original, name) => { + const offset = artMethodOffset[name]; + if (offset === void 0) { + return original; + } + const address = methodId.add(offset); + const read2 = name === "accessFlags" ? readU32 : readPointer; + original[name] = read2.call(address); + return original; + }, {}); +} +function patchArtMethod(methodId, patches, vm3) { + const artMethodSpec = getArtMethodSpec(vm3); + const artMethodOffset = artMethodSpec.offset; + Object.keys(patches).forEach((name) => { + const offset = artMethodOffset[name]; + if (offset === void 0) { + return; + } + const address = methodId.add(offset); + const write3 = name === "accessFlags" ? writeU32 : writePointer; + write3.call(address, patches[name]); + }); +} +var DalvikMethodMangler = class { + constructor(methodId) { + this.methodId = methodId; + this.originalMethod = null; + } + replace(impl, isInstanceMethod, argTypes, vm3, api2) { + const { methodId } = this; + this.originalMethod = Memory.dup(methodId, DVM_METHOD_SIZE); + let argsSize = argTypes.reduce((acc, t) => acc + t.size, 0); + if (isInstanceMethod) { + argsSize++; + } + const accessFlags = (methodId.add(DVM_METHOD_OFFSET_ACCESS_FLAGS).readU32() | kAccNative) >>> 0; + const registersSize = argsSize; + const outsSize = 0; + const insSize = argsSize; + methodId.add(DVM_METHOD_OFFSET_ACCESS_FLAGS).writeU32(accessFlags); + methodId.add(DVM_METHOD_OFFSET_REGISTERS_SIZE).writeU16(registersSize); + methodId.add(DVM_METHOD_OFFSET_OUTS_SIZE).writeU16(outsSize); + methodId.add(DVM_METHOD_OFFSET_INS_SIZE).writeU16(insSize); + methodId.add(DVM_METHOD_OFFSET_JNI_ARG_INFO).writeU32(computeDalvikJniArgInfo(methodId)); + api2.dvmUseJNIBridge(methodId, impl); + } + revert(vm3) { + Memory.copy(this.methodId, this.originalMethod, DVM_METHOD_SIZE); + } + resolveTarget(wrapper, isInstanceMethod, env, api2) { + const thread = env.handle.add(DVM_JNI_ENV_OFFSET_SELF).readPointer(); + let objectPtr; + if (isInstanceMethod) { + objectPtr = api2.dvmDecodeIndirectRef(thread, wrapper.$h); + } else { + const h = wrapper.$borrowClassHandle(env); + objectPtr = api2.dvmDecodeIndirectRef(thread, h.value); + h.unref(env); + } + let classObject; + if (isInstanceMethod) { + classObject = objectPtr.add(DVM_OBJECT_OFFSET_CLAZZ).readPointer(); + } else { + classObject = objectPtr; + } + const classKey = classObject.toString(16); + let entry = patchedClasses.get(classKey); + if (entry === void 0) { + const vtablePtr = classObject.add(DVM_CLASS_OBJECT_OFFSET_VTABLE); + const vtableCountPtr = classObject.add(DVM_CLASS_OBJECT_OFFSET_VTABLE_COUNT); + const vtable2 = vtablePtr.readPointer(); + const vtableCount = vtableCountPtr.readS32(); + const vtableSize = vtableCount * pointerSize5; + const shadowVtable = Memory.alloc(2 * vtableSize); + Memory.copy(shadowVtable, vtable2, vtableSize); + vtablePtr.writePointer(shadowVtable); + entry = { + classObject, + vtablePtr, + vtableCountPtr, + vtable: vtable2, + vtableCount, + shadowVtable, + shadowVtableCount: vtableCount, + targetMethods: /* @__PURE__ */ new Map() + }; + patchedClasses.set(classKey, entry); + } + const methodKey = this.methodId.toString(16); + let targetMethod = entry.targetMethods.get(methodKey); + if (targetMethod === void 0) { + targetMethod = Memory.dup(this.originalMethod, DVM_METHOD_SIZE); + const methodIndex = entry.shadowVtableCount++; + entry.shadowVtable.add(methodIndex * pointerSize5).writePointer(targetMethod); + targetMethod.add(DVM_METHOD_OFFSET_METHOD_INDEX).writeU16(methodIndex); + entry.vtableCountPtr.writeS32(entry.shadowVtableCount); + entry.targetMethods.set(methodKey, targetMethod); + } + return targetMethod; + } +}; +function computeDalvikJniArgInfo(methodId) { + if (Process.arch !== "ia32") { + return DALVIK_JNI_NO_ARG_INFO; + } + const shorty = methodId.add(DVM_METHOD_OFFSET_SHORTY).readPointer().readCString(); + if (shorty === null || shorty.length === 0 || shorty.length > 65535) { + return DALVIK_JNI_NO_ARG_INFO; + } + let returnType; + switch (shorty[0]) { + case "V": + returnType = DALVIK_JNI_RETURN_VOID; + break; + case "F": + returnType = DALVIK_JNI_RETURN_FLOAT; + break; + case "D": + returnType = DALVIK_JNI_RETURN_DOUBLE; + break; + case "J": + returnType = DALVIK_JNI_RETURN_S8; + break; + case "Z": + case "B": + returnType = DALVIK_JNI_RETURN_S1; + break; + case "C": + returnType = DALVIK_JNI_RETURN_U2; + break; + case "S": + returnType = DALVIK_JNI_RETURN_S2; + break; + default: + returnType = DALVIK_JNI_RETURN_S4; + break; + } + let hints = 0; + for (let i = shorty.length - 1; i > 0; i--) { + const ch = shorty[i]; + hints += ch === "D" || ch === "J" ? 2 : 1; + } + return returnType << DALVIK_JNI_RETURN_SHIFT | hints; +} +function cloneArtMethod(method, vm3) { + const api2 = getApi(); + if (getAndroidApiLevel() < 23) { + const thread = api2["art::Thread::CurrentFromGdb"](); + return api2["art::mirror::Object::Clone"](method, thread); + } + return Memory.dup(method, getArtMethodSpec(vm3).size); +} +function deoptimizeMethod(vm3, env, method) { + requestDeoptimization(vm3, env, kSelectiveDeoptimization, method); +} +function deoptimizeEverything(vm3, env) { + requestDeoptimization(vm3, env, kFullDeoptimization); +} +function deoptimizeBootImage(vm3, env) { + const api2 = getApi(); + if (getAndroidApiLevel() < 26) { + throw new Error("This API is only available on Android >= 8.0"); + } + withRunnableArtThread(vm3, env, (thread) => { + api2["art::Runtime::DeoptimizeBootImage"](api2.artRuntime); + }); +} +function requestDeoptimization(vm3, env, kind, method) { + const api2 = getApi(); + if (getAndroidApiLevel() < 24) { + throw new Error("This API is only available on Android >= 7.0"); + } + withRunnableArtThread(vm3, env, (thread) => { + if (getAndroidApiLevel() < 30) { + if (!api2.isJdwpStarted()) { + const session = startJdwp(api2); + jdwpSessions.push(session); + } + if (!api2.isDebuggerActive()) { + api2["art::Dbg::GoActive"](); + } + const request = Memory.alloc(8 + pointerSize5); + request.writeU32(kind); + switch (kind) { + case kFullDeoptimization: + break; + case kSelectiveDeoptimization: + request.add(8).writePointer(method); + break; + default: + throw new Error("Unsupported deoptimization kind"); + } + api2["art::Dbg::RequestDeoptimization"](request); + api2["art::Dbg::ManageDeoptimization"](); + } else { + const instrumentation = api2.artInstrumentation; + if (instrumentation === null) { + throw new Error("Unable to find Instrumentation class in ART; please file a bug"); + } + const enableDeopt = api2["art::Instrumentation::EnableDeoptimization"]; + if (enableDeopt !== void 0) { + const deoptimizationEnabled = !!instrumentation.add(getArtInstrumentationSpec().offset.deoptimizationEnabled).readU8(); + if (!deoptimizationEnabled) { + enableDeopt(instrumentation); + } + } + switch (kind) { + case kFullDeoptimization: + api2["art::Instrumentation::DeoptimizeEverything"](instrumentation, Memory.allocUtf8String("frida")); + break; + case kSelectiveDeoptimization: + api2["art::Instrumentation::Deoptimize"](instrumentation, method); + break; + default: + throw new Error("Unsupported deoptimization kind"); + } + } + }); +} +var JdwpSession = class { + constructor() { + const libart = Process.getModuleByName("libart.so"); + const acceptImpl = libart.getExportByName("_ZN3art4JDWP12JdwpAdbState6AcceptEv"); + const receiveClientFdImpl = libart.getExportByName("_ZN3art4JDWP12JdwpAdbState15ReceiveClientFdEv"); + const controlPair = makeSocketPair(); + const clientPair = makeSocketPair(); + this._controlFd = controlPair[0]; + this._clientFd = clientPair[0]; + let acceptListener = null; + acceptListener = Interceptor.attach(acceptImpl, function(args) { + const state = args[0]; + const controlSockPtr = Memory.scanSync(state.add(8252), 256, "00 ff ff ff ff 00")[0].address.add(1); + controlSockPtr.writeS32(controlPair[1]); + acceptListener.detach(); + }); + Interceptor.replace(receiveClientFdImpl, new NativeCallback(function(state) { + Interceptor.revert(receiveClientFdImpl); + return clientPair[1]; + }, "int", ["pointer"])); + Interceptor.flush(); + this._handshakeRequest = this._performHandshake(); + } + async _performHandshake() { + const input = new UnixInputStream(this._clientFd, { autoClose: false }); + const output = new UnixOutputStream(this._clientFd, { autoClose: false }); + const handshakePacket = [74, 68, 87, 80, 45, 72, 97, 110, 100, 115, 104, 97, 107, 101]; + try { + await output.writeAll(handshakePacket); + await input.readAll(handshakePacket.length); + } catch (e) { + } + } +}; +function startJdwp(api2) { + const session = new JdwpSession(); + api2["art::Dbg::SetJdwpAllowed"](1); + const options = makeJdwpOptions(); + api2["art::Dbg::ConfigureJdwp"](options); + const startDebugger = api2["art::InternalDebuggerControlCallback::StartDebugger"]; + if (startDebugger !== void 0) { + startDebugger(NULL); + } else { + api2["art::Dbg::StartJdwp"](); + } + return session; +} +function makeJdwpOptions() { + const kJdwpTransportAndroidAdb = getAndroidApiLevel() < 28 ? 2 : 3; + const kJdwpPortFirstAvailable = 0; + const transport = kJdwpTransportAndroidAdb; + const server = true; + const suspend = false; + const port = kJdwpPortFirstAvailable; + const size = 8 + STD_STRING_SIZE + 2; + const result = Memory.alloc(size); + result.writeU32(transport).add(4).writeU8(server ? 1 : 0).add(1).writeU8(suspend ? 1 : 0).add(1).add(STD_STRING_SIZE).writeU16(port); + return result; +} +function makeSocketPair() { + if (socketpair === null) { + socketpair = new NativeFunction( + Process.getModuleByName("libc.so").getExportByName("socketpair"), + "int", + ["int", "int", "int", "pointer"] + ); + } + const buf = Memory.alloc(8); + if (socketpair(AF_UNIX, SOCK_STREAM, 0, buf) === -1) { + throw new Error("Unable to create socketpair for JDWP"); + } + return [ + buf.readS32(), + buf.add(4).readS32() + ]; +} +function makeAddGlobalRefFallbackForAndroid5(api2) { + const offset = getArtVMSpec().offset; + const lock = api2.vm.add(offset.globalsLock); + const table = api2.vm.add(offset.globals); + const add = api2["art::IndirectReferenceTable::Add"]; + const acquire = api2["art::ReaderWriterMutex::ExclusiveLock"]; + const release = api2["art::ReaderWriterMutex::ExclusiveUnlock"]; + const IRT_FIRST_SEGMENT = 0; + return function(vm3, thread, obj) { + acquire(lock, thread); + try { + return add(table, IRT_FIRST_SEGMENT, obj); + } finally { + release(lock, thread); + } + }; +} +function makeDecodeGlobalFallback(api2) { + const decode = api2["art::Thread::DecodeJObject"]; + if (decode === void 0) { + throw new Error("art::Thread::DecodeJObject is not available; please file a bug"); + } + return function(vm3, thread, ref) { + return decode(thread, ref); + }; +} +var threadStateTransitionRecompilers = { + ia32: recompileExceptionClearForX86, + x64: recompileExceptionClearForX86, + arm: recompileExceptionClearForArm, + arm64: recompileExceptionClearForArm64 +}; +function makeArtThreadStateTransitionImpl(vm3, env, callback) { + const api2 = getApi(); + const envVtable = env.handle.readPointer(); + let exceptionClearImpl; + const innerExceptionClearImpl = api2.find("_ZN3art3JNIILb1EE14ExceptionClearEP7_JNIEnv"); + if (innerExceptionClearImpl !== null) { + exceptionClearImpl = innerExceptionClearImpl; + } else { + exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR).readPointer(); + } + let nextFuncImpl; + const innerNextFuncImpl = api2.find("_ZN3art3JNIILb1EE10FatalErrorEP7_JNIEnvPKc"); + if (innerNextFuncImpl !== null) { + nextFuncImpl = innerNextFuncImpl; + } else { + nextFuncImpl = envVtable.add(ENV_VTABLE_OFFSET_FATAL_ERROR).readPointer(); + } + const recompile = threadStateTransitionRecompilers[Process.arch]; + if (recompile === void 0) { + throw new Error("Not yet implemented for " + Process.arch); + } + let perform = null; + const threadOffsets = getArtThreadSpec(vm3).offset; + const exceptionOffset = threadOffsets.exception; + const neuteredOffsets = /* @__PURE__ */ new Set(); + const isReportedOffset = threadOffsets.isExceptionReportedToInstrumentation; + if (isReportedOffset !== null) { + neuteredOffsets.add(isReportedOffset); + } + const throwLocationStartOffset = threadOffsets.throwLocation; + if (throwLocationStartOffset !== null) { + neuteredOffsets.add(throwLocationStartOffset); + neuteredOffsets.add(throwLocationStartOffset + pointerSize5); + neuteredOffsets.add(throwLocationStartOffset + 2 * pointerSize5); + } + const codeSize = 65536; + const code3 = Memory.alloc(codeSize); + Memory.patchCode(code3, codeSize, (buffer) => { + perform = recompile(buffer, code3, exceptionClearImpl, nextFuncImpl, exceptionOffset, neuteredOffsets, callback); + }); + perform._code = code3; + perform._callback = callback; + return perform; +} +function recompileExceptionClearForX86(buffer, pc, exceptionClearImpl, nextFuncImpl, exceptionOffset, neuteredOffsets, callback) { + const blocks = {}; + const branchTargets = /* @__PURE__ */ new Set(); + const pending = [exceptionClearImpl]; + while (pending.length > 0) { + let current = pending.shift(); + const alreadyCovered = Object.values(blocks).some(({ begin, end }) => current.compare(begin) >= 0 && current.compare(end) < 0); + if (alreadyCovered) { + continue; + } + const blockAddressKey = current.toString(); + let block = { + begin: current + }; + let lastInsn = null; + let reachedEndOfBlock = false; + do { + if (current.equals(nextFuncImpl)) { + reachedEndOfBlock = true; + break; + } + const insn = Instruction.parse(current); + lastInsn = insn; + const existingBlock = blocks[insn.address.toString()]; + if (existingBlock !== void 0) { + delete blocks[existingBlock.begin.toString()]; + blocks[blockAddressKey] = existingBlock; + existingBlock.begin = block.begin; + block = null; + break; + } + let branchTarget = null; + switch (insn.mnemonic) { + case "jmp": + branchTarget = ptr(insn.operands[0].value); + reachedEndOfBlock = true; + break; + case "je": + case "jg": + case "jle": + case "jne": + case "js": + branchTarget = ptr(insn.operands[0].value); + break; + case "ret": + reachedEndOfBlock = true; + break; + } + if (branchTarget !== null) { + branchTargets.add(branchTarget.toString()); + pending.push(branchTarget); + pending.sort((a, b) => a.compare(b)); + } + current = insn.next; + } while (!reachedEndOfBlock); + if (block !== null) { + block.end = lastInsn.address.add(lastInsn.size); + blocks[blockAddressKey] = block; + } + } + const blocksOrdered = Object.keys(blocks).map((key) => blocks[key]); + blocksOrdered.sort((a, b) => a.begin.compare(b.begin)); + const entryBlock = blocks[exceptionClearImpl.toString()]; + blocksOrdered.splice(blocksOrdered.indexOf(entryBlock), 1); + blocksOrdered.unshift(entryBlock); + const writer = new X86Writer(buffer, { pc }); + let foundCore = false; + let threadReg = null; + blocksOrdered.forEach((block) => { + const size = block.end.sub(block.begin).toInt32(); + const relocator = new X86Relocator(block.begin, writer); + let offset; + while ((offset = relocator.readOne()) !== 0) { + const insn = relocator.input; + const { mnemonic } = insn; + const insnAddressId = insn.address.toString(); + if (branchTargets.has(insnAddressId)) { + writer.putLabel(insnAddressId); + } + let keep = true; + switch (mnemonic) { + case "jmp": + writer.putJmpNearLabel(branchLabelFromOperand(insn.operands[0])); + keep = false; + break; + case "je": + case "jg": + case "jle": + case "jne": + case "js": + writer.putJccNearLabel(mnemonic, branchLabelFromOperand(insn.operands[0]), "no-hint"); + keep = false; + break; + /* + * JNI::ExceptionClear(), when checked JNI is off. + */ + case "mov": { + const [dst, src] = insn.operands; + if (dst.type === "mem" && src.type === "imm") { + const dstValue = dst.value; + const dstOffset = dstValue.disp; + if (dstOffset === exceptionOffset && src.value.valueOf() === 0) { + threadReg = dstValue.base; + writer.putPushfx(); + writer.putPushax(); + writer.putMovRegReg("xbp", "xsp"); + if (pointerSize5 === 4) { + writer.putAndRegU32("esp", 4294967280); + } else { + const scratchReg = threadReg !== "rdi" ? "rdi" : "rsi"; + writer.putMovRegU64(scratchReg, uint64("0xfffffffffffffff0")); + writer.putAndRegReg("rsp", scratchReg); + } + writer.putCallAddressWithAlignedArguments(callback, [threadReg]); + writer.putMovRegReg("xsp", "xbp"); + writer.putPopax(); + writer.putPopfx(); + foundCore = true; + keep = false; + } else if (neuteredOffsets.has(dstOffset) && dstValue.base === threadReg) { + keep = false; + } + } + break; + } + /* + * CheckJNI::ExceptionClear, when checked JNI is on. Wrapper that calls JNI::ExceptionClear(). + */ + case "call": { + const target = insn.operands[0]; + if (target.type === "mem" && target.value.disp === ENV_VTABLE_OFFSET_EXCEPTION_CLEAR) { + if (pointerSize5 === 4) { + writer.putPopReg("eax"); + writer.putMovRegRegOffsetPtr("eax", "eax", 4); + writer.putPushReg("eax"); + } else { + writer.putMovRegRegOffsetPtr("rdi", "rdi", 8); + } + writer.putCallAddressWithArguments(callback, []); + foundCore = true; + keep = false; + } + break; + } + } + if (keep) { + relocator.writeAll(); + } else { + relocator.skipOne(); + } + if (offset === size) { + break; + } + } + relocator.dispose(); + }); + writer.dispose(); + if (!foundCore) { + throwThreadStateTransitionParseError(); + } + return new NativeFunction(pc, "void", ["pointer"], nativeFunctionOptions3); +} +function recompileExceptionClearForArm(buffer, pc, exceptionClearImpl, nextFuncImpl, exceptionOffset, neuteredOffsets, callback) { + const blocks = {}; + const branchTargets = /* @__PURE__ */ new Set(); + const thumbBitRemovalMask = ptr(1).not(); + const pending = [exceptionClearImpl]; + while (pending.length > 0) { + let current = pending.shift(); + const alreadyCovered = Object.values(blocks).some(({ begin: begin2, end }) => current.compare(begin2) >= 0 && current.compare(end) < 0); + if (alreadyCovered) { + continue; + } + const begin = current.and(thumbBitRemovalMask); + const blockId = begin.toString(); + const thumbBit = current.and(1); + let block = { + begin + }; + let lastInsn = null; + let reachedEndOfBlock = false; + let ifThenBlockRemaining = 0; + do { + if (current.equals(nextFuncImpl)) { + reachedEndOfBlock = true; + break; + } + const insn = Instruction.parse(current); + const { mnemonic } = insn; + lastInsn = insn; + const currentAddress = current.and(thumbBitRemovalMask); + const insnId = currentAddress.toString(); + const existingBlock = blocks[insnId]; + if (existingBlock !== void 0) { + delete blocks[existingBlock.begin.toString()]; + blocks[blockId] = existingBlock; + existingBlock.begin = block.begin; + block = null; + break; + } + const isOutsideIfThenBlock = ifThenBlockRemaining === 0; + let branchTarget = null; + switch (mnemonic) { + case "b": + branchTarget = ptr(insn.operands[0].value); + reachedEndOfBlock = isOutsideIfThenBlock; + break; + case "beq.w": + case "beq": + case "bne": + case "bne.w": + case "bgt": + branchTarget = ptr(insn.operands[0].value); + break; + case "cbz": + case "cbnz": + branchTarget = ptr(insn.operands[1].value); + break; + case "pop.w": + if (isOutsideIfThenBlock) { + reachedEndOfBlock = insn.operands.filter((op) => op.value === "pc").length === 1; + } + break; + } + switch (mnemonic) { + case "it": + ifThenBlockRemaining = 1; + break; + case "itt": + ifThenBlockRemaining = 2; + break; + case "ittt": + ifThenBlockRemaining = 3; + break; + case "itttt": + ifThenBlockRemaining = 4; + break; + default: + if (ifThenBlockRemaining > 0) { + ifThenBlockRemaining--; + } + break; + } + if (branchTarget !== null) { + branchTargets.add(branchTarget.toString()); + pending.push(branchTarget.or(thumbBit)); + pending.sort((a, b) => a.compare(b)); + } + current = insn.next; + } while (!reachedEndOfBlock); + if (block !== null) { + block.end = lastInsn.address.add(lastInsn.size); + blocks[blockId] = block; + } + } + const blocksOrdered = Object.keys(blocks).map((key) => blocks[key]); + blocksOrdered.sort((a, b) => a.begin.compare(b.begin)); + const entryBlock = blocks[exceptionClearImpl.and(thumbBitRemovalMask).toString()]; + blocksOrdered.splice(blocksOrdered.indexOf(entryBlock), 1); + blocksOrdered.unshift(entryBlock); + const writer = new ThumbWriter(buffer, { pc }); + let foundCore = false; + let threadReg = null; + let realImplReg = null; + blocksOrdered.forEach((block) => { + const relocator = new ThumbRelocator(block.begin, writer); + let address = block.begin; + const end = block.end; + let size = 0; + do { + const offset = relocator.readOne(); + if (offset === 0) { + throw new Error("Unexpected end of block"); + } + const insn = relocator.input; + address = insn.address; + size = insn.size; + const { mnemonic } = insn; + const insnAddressId = address.toString(); + if (branchTargets.has(insnAddressId)) { + writer.putLabel(insnAddressId); + } + let keep = true; + switch (mnemonic) { + case "b": + writer.putBLabel(branchLabelFromOperand(insn.operands[0])); + keep = false; + break; + case "beq.w": + writer.putBCondLabelWide("eq", branchLabelFromOperand(insn.operands[0])); + keep = false; + break; + case "bne.w": + writer.putBCondLabelWide("ne", branchLabelFromOperand(insn.operands[0])); + keep = false; + break; + case "beq": + case "bne": + case "bgt": + writer.putBCondLabelWide(mnemonic.substr(1), branchLabelFromOperand(insn.operands[0])); + keep = false; + break; + case "cbz": { + const ops = insn.operands; + writer.putCbzRegLabel(ops[0].value, branchLabelFromOperand(ops[1])); + keep = false; + break; + } + case "cbnz": { + const ops = insn.operands; + writer.putCbnzRegLabel(ops[0].value, branchLabelFromOperand(ops[1])); + keep = false; + break; + } + /* + * JNI::ExceptionClear(), when checked JNI is off. + */ + case "str": + case "str.w": { + const dstValue = insn.operands[1].value; + const dstOffset = dstValue.disp; + if (dstOffset === exceptionOffset) { + threadReg = dstValue.base; + const nzcvqReg = threadReg !== "r4" ? "r4" : "r5"; + const clobberedRegs = ["r0", "r1", "r2", "r3", nzcvqReg, "r9", "r12", "lr"]; + writer.putPushRegs(clobberedRegs); + writer.putMrsRegReg(nzcvqReg, "apsr-nzcvq"); + writer.putCallAddressWithArguments(callback, [threadReg]); + writer.putMsrRegReg("apsr-nzcvq", nzcvqReg); + writer.putPopRegs(clobberedRegs); + foundCore = true; + keep = false; + } else if (neuteredOffsets.has(dstOffset) && dstValue.base === threadReg) { + keep = false; + } + break; + } + /* + * CheckJNI::ExceptionClear, when checked JNI is on. Wrapper that calls JNI::ExceptionClear(). + */ + case "ldr": { + const [dstOp, srcOp] = insn.operands; + if (srcOp.type === "mem") { + const src = srcOp.value; + if (src.base[0] === "r" && src.disp === ENV_VTABLE_OFFSET_EXCEPTION_CLEAR) { + realImplReg = dstOp.value; + } + } + break; + } + case "blx": + if (insn.operands[0].value === realImplReg) { + writer.putLdrRegRegOffset("r0", "r0", 4); + writer.putCallAddressWithArguments(callback, ["r0"]); + foundCore = true; + realImplReg = null; + keep = false; + } + break; + } + if (keep) { + relocator.writeAll(); + } else { + relocator.skipOne(); + } + } while (!address.add(size).equals(end)); + relocator.dispose(); + }); + writer.dispose(); + if (!foundCore) { + throwThreadStateTransitionParseError(); + } + return new NativeFunction(pc.or(1), "void", ["pointer"], nativeFunctionOptions3); +} +function recompileExceptionClearForArm64(buffer, pc, exceptionClearImpl, nextFuncImpl, exceptionOffset, neuteredOffsets, callback) { + const blocks = {}; + const branchTargets = /* @__PURE__ */ new Set(); + const pending = [exceptionClearImpl]; + while (pending.length > 0) { + let current = pending.shift(); + const alreadyCovered = Object.values(blocks).some(({ begin, end }) => current.compare(begin) >= 0 && current.compare(end) < 0); + if (alreadyCovered) { + continue; + } + const blockAddressKey = current.toString(); + let block = { + begin: current + }; + let lastInsn = null; + let reachedEndOfBlock = false; + do { + if (current.equals(nextFuncImpl)) { + reachedEndOfBlock = true; + break; + } + let insn; + try { + insn = Instruction.parse(current); + } catch (e) { + if (current.readU32() === 0) { + reachedEndOfBlock = true; + break; + } else { + throw e; + } + } + lastInsn = insn; + const existingBlock = blocks[insn.address.toString()]; + if (existingBlock !== void 0) { + delete blocks[existingBlock.begin.toString()]; + blocks[blockAddressKey] = existingBlock; + existingBlock.begin = block.begin; + block = null; + break; + } + let branchTarget = null; + switch (insn.mnemonic) { + case "b": + branchTarget = ptr(insn.operands[0].value); + reachedEndOfBlock = true; + break; + case "b.eq": + case "b.ne": + case "b.le": + case "b.gt": + branchTarget = ptr(insn.operands[0].value); + break; + case "cbz": + case "cbnz": + branchTarget = ptr(insn.operands[1].value); + break; + case "tbz": + case "tbnz": + branchTarget = ptr(insn.operands[2].value); + break; + case "ret": + reachedEndOfBlock = true; + break; + } + if (branchTarget !== null) { + branchTargets.add(branchTarget.toString()); + pending.push(branchTarget); + pending.sort((a, b) => a.compare(b)); + } + current = insn.next; + } while (!reachedEndOfBlock); + if (block !== null) { + block.end = lastInsn.address.add(lastInsn.size); + blocks[blockAddressKey] = block; + } + } + const blocksOrdered = Object.keys(blocks).map((key) => blocks[key]); + blocksOrdered.sort((a, b) => a.begin.compare(b.begin)); + const entryBlock = blocks[exceptionClearImpl.toString()]; + blocksOrdered.splice(blocksOrdered.indexOf(entryBlock), 1); + blocksOrdered.unshift(entryBlock); + const writer = new Arm64Writer(buffer, { pc }); + writer.putBLabel("performTransition"); + const invokeCallback = pc.add(writer.offset); + writer.putPushAllXRegisters(); + writer.putCallAddressWithArguments(callback, ["x0"]); + writer.putPopAllXRegisters(); + writer.putRet(); + writer.putLabel("performTransition"); + let foundCore = false; + let threadReg = null; + let realImplReg = null; + blocksOrdered.forEach((block) => { + const size = block.end.sub(block.begin).toInt32(); + const relocator = new Arm64Relocator(block.begin, writer); + let offset; + while ((offset = relocator.readOne()) !== 0) { + const insn = relocator.input; + const { mnemonic } = insn; + const insnAddressId = insn.address.toString(); + if (branchTargets.has(insnAddressId)) { + writer.putLabel(insnAddressId); + } + let keep = true; + switch (mnemonic) { + case "b": + writer.putBLabel(branchLabelFromOperand(insn.operands[0])); + keep = false; + break; + case "b.eq": + case "b.ne": + case "b.le": + case "b.gt": + writer.putBCondLabel(mnemonic.substr(2), branchLabelFromOperand(insn.operands[0])); + keep = false; + break; + case "cbz": { + const ops = insn.operands; + writer.putCbzRegLabel(ops[0].value, branchLabelFromOperand(ops[1])); + keep = false; + break; + } + case "cbnz": { + const ops = insn.operands; + writer.putCbnzRegLabel(ops[0].value, branchLabelFromOperand(ops[1])); + keep = false; + break; + } + case "tbz": { + const ops = insn.operands; + writer.putTbzRegImmLabel(ops[0].value, ops[1].value.valueOf(), branchLabelFromOperand(ops[2])); + keep = false; + break; + } + case "tbnz": { + const ops = insn.operands; + writer.putTbnzRegImmLabel(ops[0].value, ops[1].value.valueOf(), branchLabelFromOperand(ops[2])); + keep = false; + break; + } + /* + * JNI::ExceptionClear(), when checked JNI is off. + */ + case "str": { + const ops = insn.operands; + const srcReg = ops[0].value; + const dstValue = ops[1].value; + const dstOffset = dstValue.disp; + if (srcReg === "xzr" && dstOffset === exceptionOffset) { + threadReg = dstValue.base; + writer.putPushRegReg("x0", "lr"); + writer.putMovRegReg("x0", threadReg); + writer.putBlImm(invokeCallback); + writer.putPopRegReg("x0", "lr"); + foundCore = true; + keep = false; + } else if (neuteredOffsets.has(dstOffset) && dstValue.base === threadReg) { + keep = false; + } + break; + } + /* + * CheckJNI::ExceptionClear, when checked JNI is on. Wrapper that calls JNI::ExceptionClear(). + */ + case "ldr": { + const ops = insn.operands; + const src = ops[1].value; + if (src.base[0] === "x" && src.disp === ENV_VTABLE_OFFSET_EXCEPTION_CLEAR) { + realImplReg = ops[0].value; + } + break; + } + case "blr": + if (insn.operands[0].value === realImplReg) { + writer.putLdrRegRegOffset("x0", "x0", 8); + writer.putCallAddressWithArguments(callback, ["x0"]); + foundCore = true; + realImplReg = null; + keep = false; + } + break; + } + if (keep) { + relocator.writeAll(); + } else { + relocator.skipOne(); + } + if (offset === size) { + break; + } + } + relocator.dispose(); + }); + writer.dispose(); + if (!foundCore) { + throwThreadStateTransitionParseError(); + } + return new NativeFunction(pc, "void", ["pointer"], nativeFunctionOptions3); +} +function throwThreadStateTransitionParseError() { + throw new Error("Unable to parse ART internals; please file a bug"); +} +function fixupArtQuickDeliverExceptionBug(api2) { + const prettyMethod = api2["art::ArtMethod::PrettyMethod"]; + if (prettyMethod === void 0) { + return; + } + Interceptor.attach(prettyMethod.impl, artController.hooks.ArtMethod.prettyMethod); + Interceptor.flush(); +} +function branchLabelFromOperand(op) { + return ptr(op.value).toString(); +} +function makeCxxMethodWrapperReturningPointerByValueGeneric(address, argTypes) { + return new NativeFunction(address, "pointer", argTypes, nativeFunctionOptions3); +} +function makeCxxMethodWrapperReturningPointerByValueInFirstArg(address, argTypes) { + const impl = new NativeFunction(address, "void", ["pointer"].concat(argTypes), nativeFunctionOptions3); + return function() { + const resultPtr = Memory.alloc(pointerSize5); + impl(resultPtr, ...arguments); + return resultPtr.readPointer(); + }; +} +function makeCxxMethodWrapperReturningStdStringByValue(impl, argTypes) { + const { arch } = Process; + switch (arch) { + case "ia32": + case "arm64": { + let thunk; + if (arch === "ia32") { + thunk = makeThunk(64, (writer) => { + const argCount = 1 + argTypes.length; + const argvSize = argCount * 4; + writer.putSubRegImm("esp", argvSize); + for (let i = 0; i !== argCount; i++) { + const offset = i * 4; + writer.putMovRegRegOffsetPtr("eax", "esp", argvSize + 4 + offset); + writer.putMovRegOffsetPtrReg("esp", offset, "eax"); + } + writer.putCallAddress(impl); + writer.putAddRegImm("esp", argvSize - 4); + writer.putRet(); + }); + } else { + thunk = makeThunk(32, (writer) => { + writer.putMovRegReg("x8", "x0"); + argTypes.forEach((t, i) => { + writer.putMovRegReg("x" + i, "x" + (i + 1)); + }); + writer.putLdrRegAddress("x7", impl); + writer.putBrReg("x7"); + }); + } + const invokeThunk = new NativeFunction(thunk, "void", ["pointer"].concat(argTypes), nativeFunctionOptions3); + const wrapper = function(...args) { + invokeThunk(...args); + }; + wrapper.handle = thunk; + wrapper.impl = impl; + return wrapper; + } + default: { + const result = new NativeFunction(impl, "void", ["pointer"].concat(argTypes), nativeFunctionOptions3); + result.impl = impl; + return result; + } + } +} +var StdString = class { + constructor() { + this.handle = Memory.alloc(STD_STRING_SIZE); + } + dispose() { + const [data, isTiny] = this._getData(); + if (!isTiny) { + getApi().$delete(data); + } + } + disposeToString() { + const result = this.toString(); + this.dispose(); + return result; + } + toString() { + const [data] = this._getData(); + return data.readUtf8String(); + } + _getData() { + const str = this.handle; + const isTiny = (str.readU8() & 1) === 0; + const data = isTiny ? str.add(1) : str.add(2 * pointerSize5).readPointer(); + return [data, isTiny]; + } +}; +var StdVector = class { + $delete() { + this.dispose(); + getApi().$delete(this); + } + constructor(storage, elementSize) { + this.handle = storage; + this._begin = storage; + this._end = storage.add(pointerSize5); + this._storage = storage.add(2 * pointerSize5); + this._elementSize = elementSize; + } + init() { + this.begin = NULL; + this.end = NULL; + this.storage = NULL; + } + dispose() { + getApi().$delete(this.begin); + } + get begin() { + return this._begin.readPointer(); + } + set begin(value) { + this._begin.writePointer(value); + } + get end() { + return this._end.readPointer(); + } + set end(value) { + this._end.writePointer(value); + } + get storage() { + return this._storage.readPointer(); + } + set storage(value) { + this._storage.writePointer(value); + } + get size() { + return this.end.sub(this.begin).toInt32() / this._elementSize; + } +}; +var HandleVector = class _HandleVector extends StdVector { + static $new() { + const vector = new _HandleVector(getApi().$new(STD_VECTOR_SIZE)); + vector.init(); + return vector; + } + constructor(storage) { + super(storage, pointerSize5); + } + get handles() { + const result = []; + let cur = this.begin; + const end = this.end; + while (!cur.equals(end)) { + result.push(cur.readPointer()); + cur = cur.add(pointerSize5); + } + return result; + } +}; +var BHS_OFFSET_LINK = 0; +var BHS_OFFSET_NUM_REFS = pointerSize5; +var BHS_SIZE = BHS_OFFSET_NUM_REFS + 4; +var kNumReferencesVariableSized = -1; +var BaseHandleScope = class _BaseHandleScope { + $delete() { + this.dispose(); + getApi().$delete(this); + } + constructor(storage) { + this.handle = storage; + this._link = storage.add(BHS_OFFSET_LINK); + this._numberOfReferences = storage.add(BHS_OFFSET_NUM_REFS); + } + init(link, numberOfReferences) { + this.link = link; + this.numberOfReferences = numberOfReferences; + } + dispose() { + } + get link() { + return new _BaseHandleScope(this._link.readPointer()); + } + set link(value) { + this._link.writePointer(value); + } + get numberOfReferences() { + return this._numberOfReferences.readS32(); + } + set numberOfReferences(value) { + this._numberOfReferences.writeS32(value); + } +}; +var VSHS_OFFSET_SELF = alignPointerOffset(BHS_SIZE); +var VSHS_OFFSET_CURRENT_SCOPE = VSHS_OFFSET_SELF + pointerSize5; +var VSHS_SIZE = VSHS_OFFSET_CURRENT_SCOPE + pointerSize5; +var VariableSizedHandleScope = class _VariableSizedHandleScope extends BaseHandleScope { + static $new(thread, vm3) { + const scope = new _VariableSizedHandleScope(getApi().$new(VSHS_SIZE)); + scope.init(thread, vm3); + return scope; + } + constructor(storage) { + super(storage); + this._self = storage.add(VSHS_OFFSET_SELF); + this._currentScope = storage.add(VSHS_OFFSET_CURRENT_SCOPE); + const kLocalScopeSize = 64; + const kSizeOfReferencesPerScope = kLocalScopeSize - pointerSize5 - 4 - 4; + const kNumReferencesPerScope = kSizeOfReferencesPerScope / 4; + this._scopeLayout = FixedSizeHandleScope.layoutForCapacity(kNumReferencesPerScope); + this._topHandleScopePtr = null; + } + init(thread, vm3) { + const topHandleScopePtr = thread.add(getArtThreadSpec(vm3).offset.topHandleScope); + this._topHandleScopePtr = topHandleScopePtr; + super.init(topHandleScopePtr.readPointer(), kNumReferencesVariableSized); + this.self = thread; + this.currentScope = FixedSizeHandleScope.$new(this._scopeLayout); + topHandleScopePtr.writePointer(this); + } + dispose() { + this._topHandleScopePtr.writePointer(this.link); + let scope; + while ((scope = this.currentScope) !== null) { + const next = scope.link; + scope.$delete(); + this.currentScope = next; + } + } + get self() { + return this._self.readPointer(); + } + set self(value) { + this._self.writePointer(value); + } + get currentScope() { + const storage = this._currentScope.readPointer(); + if (storage.isNull()) { + return null; + } + return new FixedSizeHandleScope(storage, this._scopeLayout); + } + set currentScope(value) { + this._currentScope.writePointer(value); + } + newHandle(object) { + return this.currentScope.newHandle(object); + } +}; +var FixedSizeHandleScope = class _FixedSizeHandleScope extends BaseHandleScope { + static $new(layout) { + const scope = new _FixedSizeHandleScope(getApi().$new(layout.size), layout); + scope.init(); + return scope; + } + constructor(storage, layout) { + super(storage); + const { offset } = layout; + this._refsStorage = storage.add(offset.refsStorage); + this._pos = storage.add(offset.pos); + this._layout = layout; + } + init() { + super.init(NULL, this._layout.numberOfReferences); + this.pos = 0; + } + get pos() { + return this._pos.readU32(); + } + set pos(value) { + this._pos.writeU32(value); + } + newHandle(object) { + const pos = this.pos; + const handle = this._refsStorage.add(pos * 4); + handle.writeS32(object.toInt32()); + this.pos = pos + 1; + return handle; + } + static layoutForCapacity(numRefs) { + const refsStorage = BHS_SIZE; + const pos = refsStorage + numRefs * 4; + return { + size: pos + 4, + numberOfReferences: numRefs, + offset: { + refsStorage, + pos + } + }; + } +}; +var objectVisitorPredicateFactories = { + arm: function(needle, onMatch) { + const size = Process.pageSize; + const predicate = Memory.alloc(size); + Memory.protect(predicate, size, "rwx"); + const onMatchCallback = new NativeCallback(onMatch, "void", ["pointer"]); + predicate._onMatchCallback = onMatchCallback; + const instructions = [ + 26625, + // ldr r1, [r0] + 18947, + // ldr r2, =needle + 17041, + // cmp r1, r2 + 53505, + // bne mismatch + 19202, + // ldr r3, =onMatch + 18200, + // bx r3 + 18288, + // bx lr + 48896 + // nop + ]; + const needleOffset = instructions.length * 2; + const onMatchOffset = needleOffset + 4; + const codeSize = onMatchOffset + 4; + Memory.patchCode(predicate, codeSize, function(address) { + instructions.forEach((instruction, index) => { + address.add(index * 2).writeU16(instruction); + }); + address.add(needleOffset).writeS32(needle); + address.add(onMatchOffset).writePointer(onMatchCallback); + }); + return predicate.or(1); + }, + arm64: function(needle, onMatch) { + const size = Process.pageSize; + const predicate = Memory.alloc(size); + Memory.protect(predicate, size, "rwx"); + const onMatchCallback = new NativeCallback(onMatch, "void", ["pointer"]); + predicate._onMatchCallback = onMatchCallback; + const instructions = [ + 3107979265, + // ldr w1, [x0] + 402653378, + // ldr w2, =needle + 1795293247, + // cmp w1, w2 + 1409286241, + // b.ne mismatch + 1476395139, + // ldr x3, =onMatch + 3592355936, + // br x3 + 3596551104 + // ret + ]; + const needleOffset = instructions.length * 4; + const onMatchOffset = needleOffset + 4; + const codeSize = onMatchOffset + 8; + Memory.patchCode(predicate, codeSize, function(address) { + instructions.forEach((instruction, index) => { + address.add(index * 4).writeU32(instruction); + }); + address.add(needleOffset).writeS32(needle); + address.add(onMatchOffset).writePointer(onMatchCallback); + }); + return predicate; + } +}; +function makeObjectVisitorPredicate(needle, onMatch) { + const factory = objectVisitorPredicateFactories[Process.arch] || makeGenericObjectVisitorPredicate; + return factory(needle, onMatch); +} +function makeGenericObjectVisitorPredicate(needle, onMatch) { + return new NativeCallback((object) => { + const klass = object.readS32(); + if (klass === needle) { + onMatch(object); + } + }, "void", ["pointer", "pointer"]); +} +function alignPointerOffset(offset) { + const remainder = offset % pointerSize5; + if (remainder !== 0) { + return offset + pointerSize5 - remainder; + } + return offset; +} + +// node_modules/frida-java-bridge/lib/jvm.js +var jsizeSize2 = 4; +var { pointerSize: pointerSize6 } = Process; +var JVM_ACC_NATIVE = 256; +var JVM_ACC_IS_OLD = 65536; +var JVM_ACC_IS_OBSOLETE = 131072; +var JVM_ACC_NOT_C2_COMPILABLE = 33554432; +var JVM_ACC_NOT_C1_COMPILABLE = 67108864; +var JVM_ACC_NOT_C2_OSR_COMPILABLE = 134217728; +var nativeFunctionOptions4 = { + exceptions: "propagate" +}; +var getJvmMethodSpec = memoize(_getJvmMethodSpec); +var getJvmInstanceKlassSpec = memoize(_getJvmInstanceKlassSpec); +var getJvmThreadSpec = memoize(_getJvmThreadSpec); +var cachedApi2 = null; +var manglersScheduled = false; +var replaceManglers = /* @__PURE__ */ new Map(); +var revertManglers = /* @__PURE__ */ new Map(); +function getApi2() { + if (cachedApi2 === null) { + cachedApi2 = _getApi2(); + } + return cachedApi2; +} +function _getApi2() { + const vmModules = Process.enumerateModules().filter((m) => /jvm.(dll|dylib|so)$/.test(m.name)); + if (vmModules.length === 0) { + return null; + } + const vmModule = vmModules[0]; + const temporaryApi = { + flavor: "jvm" + }; + const pending = Process.platform === "windows" ? [{ + module: vmModule, + functions: { + JNI_GetCreatedJavaVMs: ["JNI_GetCreatedJavaVMs", "int", ["pointer", "int", "pointer"]], + JVM_Sleep: ["JVM_Sleep", "void", ["pointer", "pointer", "long"]], + "VMThread::execute": ["VMThread::execute", "void", ["pointer"]], + "Method::size": ["Method::size", "int", ["int"]], + "Method::set_native_function": ["Method::set_native_function", "void", ["pointer", "pointer", "int"]], + "Method::clear_native_function": ["Method::clear_native_function", "void", ["pointer"]], + "Method::jmethod_id": ["Method::jmethod_id", "pointer", ["pointer"]], + "ClassLoaderDataGraph::classes_do": ["ClassLoaderDataGraph::classes_do", "void", ["pointer"]], + "NMethodSweeper::sweep_code_cache": ["NMethodSweeper::sweep_code_cache", "void", []], + "OopMapCache::flush_obsolete_entries": ["OopMapCache::flush_obsolete_entries", "void", ["pointer"]] + }, + variables: { + "VM_RedefineClasses::`vftable'": function(address) { + this.vtableRedefineClasses = address; + }, + "VM_RedefineClasses::doit": function(address) { + this.redefineClassesDoIt = address; + }, + "VM_RedefineClasses::doit_prologue": function(address) { + this.redefineClassesDoItPrologue = address; + }, + "VM_RedefineClasses::doit_epilogue": function(address) { + this.redefineClassesDoItEpilogue = address; + }, + "VM_RedefineClasses::allow_nested_vm_operations": function(address) { + this.redefineClassesAllow = address; + }, + "NMethodSweeper::_traversals": function(address) { + this.traversals = address; + }, + "NMethodSweeper::_should_sweep": function(address) { + this.shouldSweep = address; + } + }, + optionals: [] + }] : [{ + module: vmModule, + functions: { + JNI_GetCreatedJavaVMs: ["JNI_GetCreatedJavaVMs", "int", ["pointer", "int", "pointer"]], + _ZN6Method4sizeEb: ["Method::size", "int", ["int"]], + _ZN6Method19set_native_functionEPhb: ["Method::set_native_function", "void", ["pointer", "pointer", "int"]], + _ZN6Method21clear_native_functionEv: ["Method::clear_native_function", "void", ["pointer"]], + // JDK >= 17 + _ZN6Method24restore_unshareable_infoEP10JavaThread: ["Method::restore_unshareable_info", "void", ["pointer", "pointer"]], + // JDK < 17 + _ZN6Method24restore_unshareable_infoEP6Thread: ["Method::restore_unshareable_info", "void", ["pointer", "pointer"]], + _ZN6Method11link_methodERK12methodHandleP10JavaThread: ["Method::link_method", "void", ["pointer", "pointer", "pointer"]], + _ZN6Method10jmethod_idEv: ["Method::jmethod_id", "pointer", ["pointer"]], + _ZN6Method10clear_codeEv: function(address) { + const clearCode = new NativeFunction(address, "void", ["pointer"], nativeFunctionOptions4); + this["Method::clear_code"] = function(thisPtr) { + clearCode(thisPtr); + }; + }, + _ZN6Method10clear_codeEb: function(address) { + const clearCode = new NativeFunction(address, "void", ["pointer", "int"], nativeFunctionOptions4); + const lock = 0; + this["Method::clear_code"] = function(thisPtr) { + clearCode(thisPtr, lock); + }; + }, + // JDK >= 13 + _ZN18VM_RedefineClasses19mark_dependent_codeEP13InstanceKlass: ["VM_RedefineClasses::mark_dependent_code", "void", ["pointer", "pointer"]], + _ZN18VM_RedefineClasses20flush_dependent_codeEv: ["VM_RedefineClasses::flush_dependent_code", "void", []], + // JDK < 13 + _ZN18VM_RedefineClasses20flush_dependent_codeEP13InstanceKlassP6Thread: ["VM_RedefineClasses::flush_dependent_code", "void", ["pointer", "pointer", "pointer"]], + // JDK < 10 + _ZN18VM_RedefineClasses20flush_dependent_codeE19instanceKlassHandleP6Thread: ["VM_RedefineClasses::flush_dependent_code", "void", ["pointer", "pointer", "pointer"]], + _ZN19ResolvedMethodTable21adjust_method_entriesEPb: ["ResolvedMethodTable::adjust_method_entries", "void", ["pointer"]], + // JDK < 10 + _ZN15MemberNameTable21adjust_method_entriesEP13InstanceKlassPb: ["MemberNameTable::adjust_method_entries", "void", ["pointer", "pointer", "pointer"]], + _ZN17ConstantPoolCache21adjust_method_entriesEPb: function(address) { + const adjustMethod = new NativeFunction(address, "void", ["pointer", "pointer"], nativeFunctionOptions4); + this["ConstantPoolCache::adjust_method_entries"] = function(thisPtr, holderPtr, tracePtr) { + adjustMethod(thisPtr, tracePtr); + }; + }, + // JDK < 13 + _ZN17ConstantPoolCache21adjust_method_entriesEP13InstanceKlassPb: function(address) { + const adjustMethod = new NativeFunction(address, "void", ["pointer", "pointer", "pointer"], nativeFunctionOptions4); + this["ConstantPoolCache::adjust_method_entries"] = function(thisPtr, holderPtr, tracePtr) { + adjustMethod(thisPtr, holderPtr, tracePtr); + }; + }, + _ZN20ClassLoaderDataGraph10classes_doEP12KlassClosure: ["ClassLoaderDataGraph::classes_do", "void", ["pointer"]], + _ZN20ClassLoaderDataGraph22clean_deallocate_listsEb: ["ClassLoaderDataGraph::clean_deallocate_lists", "void", ["int"]], + _ZN10JavaThread27thread_from_jni_environmentEP7JNIEnv_: ["JavaThread::thread_from_jni_environment", "pointer", ["pointer"]], + _ZN8VMThread7executeEP12VM_Operation: ["VMThread::execute", "void", ["pointer"]], + _ZN11OopMapCache22flush_obsolete_entriesEv: ["OopMapCache::flush_obsolete_entries", "void", ["pointer"]], + _ZN14NMethodSweeper11force_sweepEv: ["NMethodSweeper::force_sweep", "void", []], + _ZN14NMethodSweeper16sweep_code_cacheEv: ["NMethodSweeper::sweep_code_cache", "void", []], + _ZN14NMethodSweeper17sweep_in_progressEv: ["NMethodSweeper::sweep_in_progress", "bool", []], + JVM_Sleep: ["JVM_Sleep", "void", ["pointer", "pointer", "long"]] + }, + variables: { + // JDK <= 9 + _ZN18VM_RedefineClasses14_the_class_oopE: function(address) { + this.redefineClass = address; + }, + // 9 < JDK < 13 + _ZN18VM_RedefineClasses10_the_classE: function(address) { + this.redefineClass = address; + }, + // JDK < 13 + _ZN18VM_RedefineClasses25AdjustCpoolCacheAndVtable8do_klassEP5Klass: function(address) { + this.doKlass = address; + }, + // JDK >= 13 + _ZN18VM_RedefineClasses22AdjustAndCleanMetadata8do_klassEP5Klass: function(address) { + this.doKlass = address; + }, + _ZTV18VM_RedefineClasses: function(address) { + this.vtableRedefineClasses = address; + }, + _ZN18VM_RedefineClasses4doitEv: function(address) { + this.redefineClassesDoIt = address; + }, + _ZN18VM_RedefineClasses13doit_prologueEv: function(address) { + this.redefineClassesDoItPrologue = address; + }, + _ZN18VM_RedefineClasses13doit_epilogueEv: function(address) { + this.redefineClassesDoItEpilogue = address; + }, + _ZN18VM_RedefineClassesD0Ev: function(address) { + this.redefineClassesDispose0 = address; + }, + _ZN18VM_RedefineClassesD1Ev: function(address) { + this.redefineClassesDispose1 = address; + }, + _ZNK18VM_RedefineClasses26allow_nested_vm_operationsEv: function(address) { + this.redefineClassesAllow = address; + }, + _ZNK18VM_RedefineClasses14print_on_errorEP12outputStream: function(address) { + this.redefineClassesOnError = address; + }, + // JDK >= 17 + _ZN13InstanceKlass33create_new_default_vtable_indicesEiP10JavaThread: function(address) { + this.createNewDefaultVtableIndices = address; + }, + // JDK < 17 + _ZN13InstanceKlass33create_new_default_vtable_indicesEiP6Thread: function(address) { + this.createNewDefaultVtableIndices = address; + }, + _ZN19Abstract_VM_Version19jre_release_versionEv: function(address) { + const getVersion = new NativeFunction(address, "pointer", [], nativeFunctionOptions4); + const versionS = getVersion().readCString(); + this.version = versionS.startsWith("1.8") ? 8 : versionS.startsWith("9.") ? 9 : parseInt(versionS.slice(0, 2), 10); + this.versionS = versionS; + }, + _ZN14NMethodSweeper11_traversalsE: function(address) { + this.traversals = address; + }, + _ZN14NMethodSweeper21_sweep_fractions_leftE: function(address) { + this.fractions = address; + }, + _ZN14NMethodSweeper13_should_sweepE: function(address) { + this.shouldSweep = address; + } + }, + optionals: [ + "_ZN6Method24restore_unshareable_infoEP10JavaThread", + "_ZN6Method24restore_unshareable_infoEP6Thread", + "_ZN6Method11link_methodERK12methodHandleP10JavaThread", + "_ZN6Method10clear_codeEv", + "_ZN6Method10clear_codeEb", + "_ZN18VM_RedefineClasses19mark_dependent_codeEP13InstanceKlass", + "_ZN18VM_RedefineClasses20flush_dependent_codeEv", + "_ZN18VM_RedefineClasses20flush_dependent_codeEP13InstanceKlassP6Thread", + "_ZN18VM_RedefineClasses20flush_dependent_codeE19instanceKlassHandleP6Thread", + "_ZN19ResolvedMethodTable21adjust_method_entriesEPb", + "_ZN15MemberNameTable21adjust_method_entriesEP13InstanceKlassPb", + "_ZN17ConstantPoolCache21adjust_method_entriesEPb", + "_ZN17ConstantPoolCache21adjust_method_entriesEP13InstanceKlassPb", + "_ZN20ClassLoaderDataGraph22clean_deallocate_listsEb", + "_ZN10JavaThread27thread_from_jni_environmentEP7JNIEnv_", + "_ZN14NMethodSweeper11force_sweepEv", + "_ZN14NMethodSweeper17sweep_in_progressEv", + "_ZN18VM_RedefineClasses14_the_class_oopE", + "_ZN18VM_RedefineClasses10_the_classE", + "_ZN18VM_RedefineClasses25AdjustCpoolCacheAndVtable8do_klassEP5Klass", + "_ZN18VM_RedefineClasses22AdjustAndCleanMetadata8do_klassEP5Klass", + "_ZN18VM_RedefineClassesD0Ev", + "_ZN18VM_RedefineClassesD1Ev", + "_ZNK18VM_RedefineClasses14print_on_errorEP12outputStream", + "_ZN13InstanceKlass33create_new_default_vtable_indicesEiP10JavaThread", + "_ZN13InstanceKlass33create_new_default_vtable_indicesEiP6Thread", + "_ZN14NMethodSweeper21_sweep_fractions_leftE" + ] + }]; + const missing = []; + pending.forEach(function(api2) { + const module = api2.module; + const functions = api2.functions || {}; + const variables = api2.variables || {}; + const optionals = new Set(api2.optionals || []); + const tmp = module.enumerateExports().reduce(function(result, exp) { + result[exp.name] = exp; + return result; + }, {}); + const exportByName = module.enumerateSymbols().reduce(function(result, exp) { + result[exp.name] = exp; + return result; + }, tmp); + Object.keys(functions).forEach(function(name) { + const exp = exportByName[name]; + if (exp !== void 0) { + const signature = functions[name]; + if (typeof signature === "function") { + signature.call(temporaryApi, exp.address); + } else { + temporaryApi[signature[0]] = new NativeFunction(exp.address, signature[1], signature[2], nativeFunctionOptions4); + } + } else { + if (!optionals.has(name)) { + missing.push(name); + } + } + }); + Object.keys(variables).forEach(function(name) { + const exp = exportByName[name]; + if (exp !== void 0) { + const handler = variables[name]; + handler.call(temporaryApi, exp.address); + } else { + if (!optionals.has(name)) { + missing.push(name); + } + } + }); + }); + if (missing.length > 0) { + throw new Error("Java API only partially available; please file a bug. Missing: " + missing.join(", ")); + } + const vms = Memory.alloc(pointerSize6); + const vmCount = Memory.alloc(jsizeSize2); + checkJniResult("JNI_GetCreatedJavaVMs", temporaryApi.JNI_GetCreatedJavaVMs(vms, 1, vmCount)); + if (vmCount.readInt() === 0) { + return null; + } + temporaryApi.vm = vms.readPointer(); + const allocatorFunctions = Process.platform === "windows" ? { + $new: ["??2@YAPEAX_K@Z", "pointer", ["ulong"]], + $delete: ["??3@YAXPEAX@Z", "void", ["pointer"]] + } : { + $new: ["_Znwm", "pointer", ["ulong"]], + $delete: ["_ZdlPv", "void", ["pointer"]] + }; + for (const [name, [rawName, retType, argTypes]] of Object.entries(allocatorFunctions)) { + let address = Module.findGlobalExportByName(rawName); + if (address === null) { + address = DebugSymbol.fromName(rawName).address; + if (address.isNull()) { + throw new Error(`unable to find C++ allocator API, missing: '${rawName}'`); + } + } + temporaryApi[name] = new NativeFunction(address, retType, argTypes, nativeFunctionOptions4); + } + temporaryApi.jvmti = getEnvJvmti(temporaryApi); + if (temporaryApi["JavaThread::thread_from_jni_environment"] === void 0) { + temporaryApi["JavaThread::thread_from_jni_environment"] = makeThreadFromJniHelper(temporaryApi); + } + return temporaryApi; +} +function getEnvJvmti(api2) { + const vm3 = new VM(api2); + let env; + vm3.perform(() => { + const handle = vm3.tryGetEnvHandle(jvmtiVersion.v1_0); + if (handle === null) { + throw new Error("JVMTI not available"); + } + env = new EnvJvmti(handle, vm3); + const capaBuf = Memory.alloc(8); + capaBuf.writeU64(jvmtiCapabilities.canTagObjects); + const result = env.addCapabilities(capaBuf); + checkJniResult("getEnvJvmti::AddCapabilities", result); + }); + return env; +} +var threadOffsetParsers = { + x64: parseX64ThreadOffset +}; +function makeThreadFromJniHelper(api2) { + let offset = null; + const tryParse = threadOffsetParsers[Process.arch]; + if (tryParse !== void 0) { + const vm3 = new VM(api2); + const findClassImpl = vm3.perform((env) => env.handle.readPointer().add(6 * pointerSize6).readPointer()); + offset = parseInstructionsAt(findClassImpl, tryParse, { limit: 11 }); + } + if (offset === null) { + return () => { + throw new Error("Unable to make thread_from_jni_environment() helper for the current architecture"); + }; + } + return (env) => { + return env.add(offset); + }; +} +function parseX64ThreadOffset(insn) { + if (insn.mnemonic !== "lea") { + return null; + } + const { base, disp } = insn.operands[1].value; + if (!(base === "rdi" && disp < 0)) { + return null; + } + return disp; +} +function ensureClassInitialized2(env, classRef) { +} +var JvmMethodMangler = class { + constructor(methodId) { + this.methodId = methodId; + this.method = methodId.readPointer(); + this.originalMethod = null; + this.newMethod = null; + this.resolved = null; + this.impl = null; + this.key = methodId.toString(16); + } + replace(impl, isInstanceMethod, argTypes, vm3, api2) { + const { key } = this; + const mangler = revertManglers.get(key); + if (mangler !== void 0) { + revertManglers.delete(key); + this.method = mangler.method; + this.originalMethod = mangler.originalMethod; + this.newMethod = mangler.newMethod; + this.resolved = mangler.resolved; + } + this.impl = impl; + replaceManglers.set(key, this); + ensureManglersScheduled(vm3); + } + revert(vm3) { + const { key } = this; + replaceManglers.delete(key); + revertManglers.set(key, this); + ensureManglersScheduled(vm3); + } + resolveTarget(wrapper, isInstanceMethod, env, api2) { + const { resolved, originalMethod, methodId } = this; + if (resolved !== null) { + return resolved; + } + if (originalMethod === null) { + return methodId; + } + const vip = originalMethod.oldMethod.vtableIndexPtr; + vip.writeS32(-2); + const jmethodID = Memory.alloc(pointerSize6); + jmethodID.writePointer(this.method); + this.resolved = jmethodID; + return jmethodID; + } +}; +function ensureManglersScheduled(vm3) { + if (!manglersScheduled) { + manglersScheduled = true; + Script.nextTick(doManglers, vm3); + } +} +function doManglers(vm3) { + const localReplaceManglers = new Map(replaceManglers); + const localRevertManglers = new Map(revertManglers); + replaceManglers.clear(); + revertManglers.clear(); + manglersScheduled = false; + vm3.perform((env) => { + const api2 = getApi2(); + const thread = api2["JavaThread::thread_from_jni_environment"](env.handle); + let force = false; + withJvmThread(() => { + localReplaceManglers.forEach((mangler) => { + const { method, originalMethod, impl, methodId, newMethod } = mangler; + if (originalMethod === null) { + mangler.originalMethod = fetchJvmMethod(method); + mangler.newMethod = nativeJvmMethod(method, impl, thread); + installJvmMethod(mangler.newMethod, methodId, thread); + } else { + api2["Method::set_native_function"](newMethod.method, impl, 0); + } + }); + localRevertManglers.forEach((mangler) => { + const { originalMethod, methodId, newMethod } = mangler; + if (originalMethod !== null) { + revertJvmMethod(originalMethod); + const revert = originalMethod.oldMethod; + revert.oldMethod = newMethod; + installJvmMethod(revert, methodId, thread); + force = true; + } + }); + }); + if (force) { + forceSweep(env.handle); + } + }); +} +function forceSweep(env) { + const { + fractions, + shouldSweep, + traversals, + "NMethodSweeper::sweep_code_cache": sweep, + "NMethodSweeper::sweep_in_progress": inProgress, + "NMethodSweeper::force_sweep": force, + JVM_Sleep: sleep + } = getApi2(); + if (force !== void 0) { + Thread.sleep(0.05); + force(); + Thread.sleep(0.05); + force(); + } else { + let trav = traversals.readS64(); + const endTrav = trav + 2; + while (endTrav > trav) { + fractions.writeS32(1); + sleep(env, NULL, 50); + if (!inProgress()) { + withJvmThread(() => { + Thread.sleep(0.05); + }); + } + const sweepNotAlreadyInProgress = shouldSweep.readU8() === 0; + if (sweepNotAlreadyInProgress) { + fractions.writeS32(1); + sweep(); + } + trav = traversals.readS64(); + } + } +} +function withJvmThread(fn, fnPrologue, fnEpilogue) { + const { + execute, + vtable: vtable2, + vtableSize, + doItOffset, + prologueOffset, + epilogueOffset + } = getJvmThreadSpec(); + const vtableDup = Memory.dup(vtable2, vtableSize); + const vmOperation = Memory.alloc(pointerSize6 * 25); + vmOperation.writePointer(vtableDup); + const doIt = new NativeCallback(fn, "void", ["pointer"]); + vtableDup.add(doItOffset).writePointer(doIt); + let prologue = null; + if (fnPrologue !== void 0) { + prologue = new NativeCallback(fnPrologue, "int", ["pointer"]); + vtableDup.add(prologueOffset).writePointer(prologue); + } + let epilogue = null; + if (fnEpilogue !== void 0) { + epilogue = new NativeCallback(fnEpilogue, "void", ["pointer"]); + vtableDup.add(epilogueOffset).writePointer(epilogue); + } + execute(vmOperation); +} +function _getJvmThreadSpec() { + const { + vtableRedefineClasses, + redefineClassesDoIt, + redefineClassesDoItPrologue, + redefineClassesDoItEpilogue, + redefineClassesOnError, + redefineClassesAllow, + redefineClassesDispose0, + redefineClassesDispose1, + "VMThread::execute": execute + } = getApi2(); + const vtablePtr = vtableRedefineClasses.add(2 * pointerSize6); + const vtableSize = 15 * pointerSize6; + const vtable2 = Memory.dup(vtablePtr, vtableSize); + const emptyCallback = new NativeCallback(() => { + }, "void", ["pointer"]); + let doItOffset, prologueOffset, epilogueOffset; + for (let offset = 0; offset !== vtableSize; offset += pointerSize6) { + const element = vtable2.add(offset); + const value = element.readPointer(); + if (redefineClassesOnError !== void 0 && value.equals(redefineClassesOnError) || redefineClassesDispose0 !== void 0 && value.equals(redefineClassesDispose0) || redefineClassesDispose1 !== void 0 && value.equals(redefineClassesDispose1)) { + element.writePointer(emptyCallback); + } else if (value.equals(redefineClassesDoIt)) { + doItOffset = offset; + } else if (value.equals(redefineClassesDoItPrologue)) { + prologueOffset = offset; + element.writePointer(redefineClassesAllow); + } else if (value.equals(redefineClassesDoItEpilogue)) { + epilogueOffset = offset; + element.writePointer(emptyCallback); + } + } + return { + execute, + emptyCallback, + vtable: vtable2, + vtableSize, + doItOffset, + prologueOffset, + epilogueOffset + }; +} +function makeMethodMangler2(methodId) { + return new JvmMethodMangler(methodId); +} +function installJvmMethod(method, methodId, thread) { + const { method: handle, oldMethod: old } = method; + const api2 = getApi2(); + method.methodsArray.add(method.methodIndex * pointerSize6).writePointer(handle); + if (method.vtableIndex >= 0) { + method.vtable.add(method.vtableIndex * pointerSize6).writePointer(handle); + } + methodId.writePointer(handle); + old.accessFlagsPtr.writeU32((old.accessFlags | JVM_ACC_IS_OLD | JVM_ACC_IS_OBSOLETE) >>> 0); + const flushObs = api2["OopMapCache::flush_obsolete_entries"]; + if (flushObs !== void 0) { + const { oopMapCache } = method; + if (!oopMapCache.isNull()) { + flushObs(oopMapCache); + } + } + const mark = api2["VM_RedefineClasses::mark_dependent_code"]; + const flush = api2["VM_RedefineClasses::flush_dependent_code"]; + if (mark !== void 0) { + mark(NULL, method.instanceKlass); + flush(); + } else { + flush(NULL, method.instanceKlass, thread); + } + const traceNamePrinted = Memory.alloc(1); + traceNamePrinted.writeU8(1); + api2["ConstantPoolCache::adjust_method_entries"](method.cache, method.instanceKlass, traceNamePrinted); + const klassClosure = Memory.alloc(3 * pointerSize6); + const doKlassPtr = Memory.alloc(pointerSize6); + doKlassPtr.writePointer(api2.doKlass); + klassClosure.writePointer(doKlassPtr); + klassClosure.add(pointerSize6).writePointer(thread); + klassClosure.add(2 * pointerSize6).writePointer(thread); + if (api2.redefineClass !== void 0) { + api2.redefineClass.writePointer(method.instanceKlass); + } + api2["ClassLoaderDataGraph::classes_do"](klassClosure); + const rmtAdjustMethodEntries = api2["ResolvedMethodTable::adjust_method_entries"]; + if (rmtAdjustMethodEntries !== void 0) { + rmtAdjustMethodEntries(traceNamePrinted); + } else { + const { memberNames } = method; + if (!memberNames.isNull()) { + const mntAdjustMethodEntries = api2["MemberNameTable::adjust_method_entries"]; + if (mntAdjustMethodEntries !== void 0) { + mntAdjustMethodEntries(memberNames, method.instanceKlass, traceNamePrinted); + } + } + } + const clean = api2["ClassLoaderDataGraph::clean_deallocate_lists"]; + if (clean !== void 0) { + clean(0); + } +} +function nativeJvmMethod(method, impl, thread) { + const api2 = getApi2(); + const newMethod = fetchJvmMethod(method); + newMethod.constPtr.writePointer(newMethod.const); + const flags = (newMethod.accessFlags | JVM_ACC_NATIVE | JVM_ACC_NOT_C2_COMPILABLE | JVM_ACC_NOT_C1_COMPILABLE | JVM_ACC_NOT_C2_OSR_COMPILABLE) >>> 0; + newMethod.accessFlagsPtr.writeU32(flags); + newMethod.signatureHandler.writePointer(NULL); + newMethod.adapter.writePointer(NULL); + newMethod.i2iEntry.writePointer(NULL); + api2["Method::clear_code"](newMethod.method); + newMethod.dataPtr.writePointer(NULL); + newMethod.countersPtr.writePointer(NULL); + newMethod.stackmapPtr.writePointer(NULL); + api2["Method::clear_native_function"](newMethod.method); + api2["Method::set_native_function"](newMethod.method, impl, 0); + api2["Method::restore_unshareable_info"](newMethod.method, thread); + if (api2.version >= 17) { + const methodHandle = Memory.alloc(2 * pointerSize6); + methodHandle.writePointer(newMethod.method); + methodHandle.add(pointerSize6).writePointer(thread); + api2["Method::link_method"](newMethod.method, methodHandle, thread); + } + return newMethod; +} +function fetchJvmMethod(method) { + const spec = getJvmMethodSpec(); + const constMethod = method.add(spec.method.constMethodOffset).readPointer(); + const constMethodSize = constMethod.add(spec.constMethod.sizeOffset).readS32() * pointerSize6; + const newConstMethod = Memory.alloc(constMethodSize + spec.method.size); + Memory.copy(newConstMethod, constMethod, constMethodSize); + const newMethod = newConstMethod.add(constMethodSize); + Memory.copy(newMethod, method, spec.method.size); + const result = readJvmMethod(newMethod, newConstMethod, constMethodSize); + const oldMethod = readJvmMethod(method, constMethod, constMethodSize); + result.oldMethod = oldMethod; + return result; +} +function readJvmMethod(method, constMethod, constMethodSize) { + const api2 = getApi2(); + const spec = getJvmMethodSpec(); + const constPtr = method.add(spec.method.constMethodOffset); + const dataPtr = method.add(spec.method.methodDataOffset); + const countersPtr = method.add(spec.method.methodCountersOffset); + const accessFlagsPtr = method.add(spec.method.accessFlagsOffset); + const accessFlags = accessFlagsPtr.readU32(); + const adapter = spec.getAdapterPointer(method, constMethod); + const i2iEntry = method.add(spec.method.i2iEntryOffset); + const signatureHandler = method.add(spec.method.signatureHandlerOffset); + const constantPool = constMethod.add(spec.constMethod.constantPoolOffset).readPointer(); + const stackmapPtr = constMethod.add(spec.constMethod.stackmapDataOffset); + const instanceKlass = constantPool.add(spec.constantPool.instanceKlassOffset).readPointer(); + const cache = constantPool.add(spec.constantPool.cacheOffset).readPointer(); + const instanceKlassSpec = getJvmInstanceKlassSpec(); + const methods = instanceKlass.add(instanceKlassSpec.methodsOffset).readPointer(); + const methodsCount = methods.readS32(); + const methodsArray = methods.add(pointerSize6); + const methodIndex = constMethod.add(spec.constMethod.methodIdnumOffset).readU16(); + const vtableIndexPtr = method.add(spec.method.vtableIndexOffset); + const vtableIndex = vtableIndexPtr.readS32(); + const vtable2 = instanceKlass.add(instanceKlassSpec.vtableOffset); + const oopMapCache = instanceKlass.add(instanceKlassSpec.oopMapCacheOffset).readPointer(); + const memberNames = api2.version >= 10 ? instanceKlass.add(instanceKlassSpec.memberNamesOffset).readPointer() : NULL; + return { + method, + methodSize: spec.method.size, + const: constMethod, + constSize: constMethodSize, + constPtr, + dataPtr, + countersPtr, + stackmapPtr, + instanceKlass, + methodsArray, + methodsCount, + methodIndex, + vtableIndex, + vtableIndexPtr, + vtable: vtable2, + accessFlags, + accessFlagsPtr, + adapter, + i2iEntry, + signatureHandler, + memberNames, + cache, + oopMapCache + }; +} +function revertJvmMethod(method) { + const { oldMethod: old } = method; + old.accessFlagsPtr.writeU32(old.accessFlags); + old.vtableIndexPtr.writeS32(old.vtableIndex); +} +function _getJvmMethodSpec() { + const api2 = getApi2(); + const { version } = api2; + let adapterHandlerLocation; + if (version >= 17) { + adapterHandlerLocation = "method:early"; + } else if (version >= 9 && version <= 16) { + adapterHandlerLocation = "const-method"; + } else { + adapterHandlerLocation = "method:late"; + } + const isNative = 1; + const methodSize = api2["Method::size"](isNative) * pointerSize6; + const constMethodOffset = pointerSize6; + const methodDataOffset = 2 * pointerSize6; + const methodCountersOffset = 3 * pointerSize6; + const adapterInMethodEarlyOffset = 4 * pointerSize6; + const adapterInMethodEarlySize = adapterHandlerLocation === "method:early" ? pointerSize6 : 0; + const accessFlagsOffset = adapterInMethodEarlyOffset + adapterInMethodEarlySize; + const vtableIndexOffset = accessFlagsOffset + 4; + const i2iEntryOffset = vtableIndexOffset + 4 + 8; + const adapterInMethodLateOffset = i2iEntryOffset + pointerSize6; + const adapterInMethodOffset = adapterInMethodEarlySize !== 0 ? adapterInMethodEarlyOffset : adapterInMethodLateOffset; + const nativeFunctionOffset = methodSize - 2 * pointerSize6; + const signatureHandlerOffset = methodSize - pointerSize6; + const constantPoolOffset = 8; + const stackmapDataOffset = constantPoolOffset + pointerSize6; + const adapterInConstMethodOffset = stackmapDataOffset + pointerSize6; + const adapterInConstMethodSize = adapterHandlerLocation === "const-method" ? pointerSize6 : 0; + const constMethodSizeOffset = adapterInConstMethodOffset + adapterInConstMethodSize; + const methodIdnumOffset = constMethodSizeOffset + 14; + const cacheOffset = 2 * pointerSize6; + const instanceKlassOffset = 3 * pointerSize6; + const getAdapterPointer = adapterInConstMethodSize !== 0 ? function(method, constMethod) { + return constMethod.add(adapterInConstMethodOffset); + } : function(method, constMethod) { + return method.add(adapterInMethodOffset); + }; + return { + getAdapterPointer, + method: { + size: methodSize, + constMethodOffset, + methodDataOffset, + methodCountersOffset, + accessFlagsOffset, + vtableIndexOffset, + i2iEntryOffset, + nativeFunctionOffset, + signatureHandlerOffset + }, + constMethod: { + constantPoolOffset, + stackmapDataOffset, + sizeOffset: constMethodSizeOffset, + methodIdnumOffset + }, + constantPool: { + cacheOffset, + instanceKlassOffset + } + }; +} +var vtableOffsetParsers = { + x64: parseX64VTableOffset +}; +function _getJvmInstanceKlassSpec() { + const { version: jvmVersion, createNewDefaultVtableIndices } = getApi2(); + const tryParse = vtableOffsetParsers[Process.arch]; + if (tryParse === void 0) { + throw new Error(`Missing vtable offset parser for ${Process.arch}`); + } + const vtableOffset = parseInstructionsAt(createNewDefaultVtableIndices, tryParse, { limit: 32 }); + if (vtableOffset === null) { + throw new Error("Unable to deduce vtable offset"); + } + const oopMultiplier = jvmVersion >= 10 && jvmVersion <= 11 || jvmVersion >= 15 ? 17 : 18; + const methodsOffset = vtableOffset - 7 * pointerSize6; + const memberNamesOffset = vtableOffset - 17 * pointerSize6; + const oopMapCacheOffset = vtableOffset - oopMultiplier * pointerSize6; + return { + vtableOffset, + methodsOffset, + memberNamesOffset, + oopMapCacheOffset + }; +} +function parseX64VTableOffset(insn) { + if (insn.mnemonic !== "mov") { + return null; + } + const dst = insn.operands[0]; + if (dst.type !== "mem") { + return null; + } + const { value: dstValue } = dst; + if (dstValue.scale !== 1) { + return null; + } + const { disp } = dstValue; + if (disp < 256) { + return null; + } + const defaultVtableIndicesOffset = disp; + return defaultVtableIndicesOffset + 16; +} + +// node_modules/frida-java-bridge/lib/api.js +var getApi3 = getApi; +try { + getAndroidVersion(); +} catch (e) { + getApi3 = getApi2; +} +var api_default = getApi3; + +// node_modules/frida-java-bridge/lib/class-model.js +var code2 = `#include +#include + +#define kAccStatic 0x0008 +#define kAccConstructor 0x00010000 + +typedef struct _Model Model; +typedef struct _EnumerateMethodsContext EnumerateMethodsContext; + +typedef struct _JavaApi JavaApi; +typedef struct _JavaClassApi JavaClassApi; +typedef struct _JavaMethodApi JavaMethodApi; +typedef struct _JavaFieldApi JavaFieldApi; + +typedef struct _JNIEnv JNIEnv; +typedef guint8 jboolean; +typedef gint32 jint; +typedef jint jsize; +typedef gpointer jobject; +typedef jobject jclass; +typedef jobject jstring; +typedef jobject jarray; +typedef jarray jobjectArray; +typedef gpointer jfieldID; +typedef gpointer jmethodID; + +typedef struct _jvmtiEnv jvmtiEnv; +typedef enum +{ + JVMTI_ERROR_NONE = 0 +} jvmtiError; + +typedef struct _ArtApi ArtApi; +typedef guint32 ArtHeapReference; +typedef struct _ArtObject ArtObject; +typedef struct _ArtClass ArtClass; +typedef struct _ArtClassLinker ArtClassLinker; +typedef struct _ArtClassVisitor ArtClassVisitor; +typedef struct _ArtClassVisitorVTable ArtClassVisitorVTable; +typedef struct _ArtMethod ArtMethod; +typedef struct _ArtString ArtString; + +typedef union _StdString StdString; +typedef struct _StdStringShort StdStringShort; +typedef struct _StdStringLong StdStringLong; + +typedef void (* ArtVisitClassesFunc) (ArtClassLinker * linker, ArtClassVisitor * visitor); +typedef const char * (* ArtGetClassDescriptorFunc) (ArtClass * klass, StdString * storage); +typedef void (* ArtPrettyMethodFunc) (StdString * result, ArtMethod * method, jboolean with_signature); + +struct _Model +{ + GHashTable * members; +}; + +struct _EnumerateMethodsContext +{ + GPatternSpec * class_query; + GPatternSpec * method_query; + jboolean include_signature; + jboolean ignore_case; + jboolean skip_system_classes; + GHashTable * groups; +}; + +struct _JavaClassApi +{ + jmethodID get_declared_methods; + jmethodID get_declared_fields; +}; + +struct _JavaMethodApi +{ + jmethodID get_name; + jmethodID get_modifiers; +}; + +struct _JavaFieldApi +{ + jmethodID get_name; + jmethodID get_modifiers; +}; + +struct _JavaApi +{ + JavaClassApi clazz; + JavaMethodApi method; + JavaFieldApi field; +}; + +struct _JNIEnv +{ + gpointer * functions; +}; + +struct _jvmtiEnv +{ + gpointer * functions; +}; + +struct _ArtApi +{ + gboolean available; + + guint class_offset_ifields; + guint class_offset_methods; + guint class_offset_sfields; + guint class_offset_copied_methods_offset; + + guint method_size; + guint method_offset_access_flags; + + guint field_size; + guint field_offset_access_flags; + + guint alignment_padding; + + ArtClassLinker * linker; + ArtVisitClassesFunc visit_classes; + ArtGetClassDescriptorFunc get_class_descriptor; + ArtPrettyMethodFunc pretty_method; + + void (* free) (gpointer mem); +}; + +struct _ArtObject +{ + ArtHeapReference klass; + ArtHeapReference monitor; +}; + +struct _ArtClass +{ + ArtObject parent; + + ArtHeapReference class_loader; +}; + +struct _ArtClassVisitor +{ + ArtClassVisitorVTable * vtable; + gpointer user_data; +}; + +struct _ArtClassVisitorVTable +{ + void (* reserved1) (ArtClassVisitor * self); + void (* reserved2) (ArtClassVisitor * self); + jboolean (* visit) (ArtClassVisitor * self, ArtClass * klass); +}; + +struct _ArtString +{ + ArtObject parent; + + gint32 count; + guint32 hash_code; + + union + { + guint16 value[0]; + guint8 value_compressed[0]; + }; +}; + +struct _StdStringShort +{ + guint8 size; + gchar data[(3 * sizeof (gpointer)) - sizeof (guint8)]; +}; + +struct _StdStringLong +{ + gsize capacity; + gsize size; + gchar * data; +}; + +union _StdString +{ + StdStringShort s; + StdStringLong l; +}; + +static void model_add_method (Model * self, const gchar * name, jmethodID id, jint modifiers); +static void model_add_field (Model * self, const gchar * name, jfieldID id, jint modifiers); +static void model_free (Model * model); + +static jboolean collect_matching_class_methods (ArtClassVisitor * self, ArtClass * klass); +static gchar * finalize_method_groups_to_json (GHashTable * groups); +static GPatternSpec * make_pattern_spec (const gchar * pattern, jboolean ignore_case); +static gchar * class_name_from_signature (const gchar * signature); +static gchar * format_method_signature (const gchar * name, const gchar * signature); +static void append_type (GString * output, const gchar ** type); + +static gpointer read_art_array (gpointer object_base, guint field_offset, guint length_size, guint * length); + +static void std_string_destroy (StdString * str); +static gchar * std_string_c_str (StdString * self); + +extern GMutex lock; +extern GArray * models; +extern JavaApi java_api; +extern ArtApi art_api; + +void +init (void) +{ + g_mutex_init (&lock); + models = g_array_new (FALSE, FALSE, sizeof (Model *)); +} + +void +finalize (void) +{ + guint n, i; + + n = models->len; + for (i = 0; i != n; i++) + { + Model * model = g_array_index (models, Model *, i); + model_free (model); + } + + g_array_unref (models); + g_mutex_clear (&lock); +} + +Model * +model_new (jclass class_handle, + gpointer class_object, + JNIEnv * env) +{ + Model * model; + GHashTable * members; + gpointer * funcs = env->functions; + jmethodID (* from_reflected_method) (JNIEnv *, jobject) = funcs[7]; + jfieldID (* from_reflected_field) (JNIEnv *, jobject) = funcs[8]; + jobject (* to_reflected_method) (JNIEnv *, jclass, jmethodID, jboolean) = funcs[9]; + jobject (* to_reflected_field) (JNIEnv *, jclass, jfieldID, jboolean) = funcs[12]; + void (* delete_local_ref) (JNIEnv *, jobject) = funcs[23]; + jobject (* call_object_method) (JNIEnv *, jobject, jmethodID, ...) = funcs[34]; + jint (* call_int_method) (JNIEnv *, jobject, jmethodID, ...) = funcs[49]; + const char * (* get_string_utf_chars) (JNIEnv *, jstring, jboolean *) = funcs[169]; + void (* release_string_utf_chars) (JNIEnv *, jstring, const char *) = funcs[170]; + jsize (* get_array_length) (JNIEnv *, jarray) = funcs[171]; + jobject (* get_object_array_element) (JNIEnv *, jobjectArray, jsize) = funcs[173]; + jsize n, i; + + model = g_new (Model, 1); + + members = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + model->members = members; + + if (art_api.available) + { + gpointer elements; + guint n, i; + const guint field_arrays[] = { + art_api.class_offset_ifields, + art_api.class_offset_sfields + }; + guint field_array_cursor; + gboolean merged_fields = art_api.class_offset_sfields == 0; + + elements = read_art_array (class_object, art_api.class_offset_methods, sizeof (gsize), NULL); + n = *(guint16 *) (class_object + art_api.class_offset_copied_methods_offset); + for (i = 0; i != n; i++) + { + jmethodID id; + guint32 access_flags; + jboolean is_static; + jobject method, name; + const char * name_str; + jint modifiers; + + id = elements + (i * art_api.method_size); + + access_flags = *(guint32 *) (id + art_api.method_offset_access_flags); + if ((access_flags & kAccConstructor) != 0) + continue; + is_static = (access_flags & kAccStatic) != 0; + method = to_reflected_method (env, class_handle, id, is_static); + name = call_object_method (env, method, java_api.method.get_name); + name_str = get_string_utf_chars (env, name, NULL); + modifiers = access_flags & 0xffff; + + model_add_method (model, name_str, id, modifiers); + + release_string_utf_chars (env, name, name_str); + delete_local_ref (env, name); + delete_local_ref (env, method); + } + + for (field_array_cursor = 0; field_array_cursor != G_N_ELEMENTS (field_arrays); field_array_cursor++) + { + jboolean is_static; + + if (field_arrays[field_array_cursor] == 0) + continue; + + if (!merged_fields) + is_static = field_array_cursor == 1; + + elements = read_art_array (class_object, field_arrays[field_array_cursor], sizeof (guint32), &n); + for (i = 0; i != n; i++) + { + jfieldID id; + guint32 access_flags; + jobject field, name; + const char * name_str; + jint modifiers; + + id = elements + (i * art_api.field_size); + + access_flags = *(guint32 *) (id + art_api.field_offset_access_flags); + if (merged_fields) + is_static = (access_flags & kAccStatic) != 0; + field = to_reflected_field (env, class_handle, id, is_static); + name = call_object_method (env, field, java_api.field.get_name); + name_str = get_string_utf_chars (env, name, NULL); + modifiers = access_flags & 0xffff; + + model_add_field (model, name_str, id, modifiers); + + release_string_utf_chars (env, name, name_str); + delete_local_ref (env, name); + delete_local_ref (env, field); + } + } + } + else + { + jobject elements; + + elements = call_object_method (env, class_handle, java_api.clazz.get_declared_methods); + n = get_array_length (env, elements); + for (i = 0; i != n; i++) + { + jobject method, name; + const char * name_str; + jmethodID id; + jint modifiers; + + method = get_object_array_element (env, elements, i); + name = call_object_method (env, method, java_api.method.get_name); + name_str = get_string_utf_chars (env, name, NULL); + id = from_reflected_method (env, method); + modifiers = call_int_method (env, method, java_api.method.get_modifiers); + + model_add_method (model, name_str, id, modifiers); + + release_string_utf_chars (env, name, name_str); + delete_local_ref (env, name); + delete_local_ref (env, method); + } + delete_local_ref (env, elements); + + elements = call_object_method (env, class_handle, java_api.clazz.get_declared_fields); + n = get_array_length (env, elements); + for (i = 0; i != n; i++) + { + jobject field, name; + const char * name_str; + jfieldID id; + jint modifiers; + + field = get_object_array_element (env, elements, i); + name = call_object_method (env, field, java_api.field.get_name); + name_str = get_string_utf_chars (env, name, NULL); + id = from_reflected_field (env, field); + modifiers = call_int_method (env, field, java_api.field.get_modifiers); + + model_add_field (model, name_str, id, modifiers); + + release_string_utf_chars (env, name, name_str); + delete_local_ref (env, name); + delete_local_ref (env, field); + } + delete_local_ref (env, elements); + } + + g_mutex_lock (&lock); + g_array_append_val (models, model); + g_mutex_unlock (&lock); + + return model; +} + +static void +model_add_method (Model * self, + const gchar * name, + jmethodID id, + jint modifiers) +{ + GHashTable * members = self->members; + gchar * key, type; + const gchar * value; + + if (name[0] == '$') + key = g_strdup_printf ("_%s", name); + else + key = g_strdup (name); + + type = (modifiers & kAccStatic) != 0 ? 's' : 'i'; + + value = g_hash_table_lookup (members, key); + if (value == NULL) + g_hash_table_insert (members, key, g_strdup_printf ("m:%c0x%zx", type, id)); + else + g_hash_table_insert (members, key, g_strdup_printf ("%s:%c0x%zx", value, type, id)); +} + +static void +model_add_field (Model * self, + const gchar * name, + jfieldID id, + jint modifiers) +{ + GHashTable * members = self->members; + gchar * key, type; + + if (name[0] == '$') + key = g_strdup_printf ("_%s", name); + else + key = g_strdup (name); + while (g_hash_table_contains (members, key)) + { + gchar * new_key = g_strdup_printf ("_%s", key); + g_free (key); + key = new_key; + } + + type = (modifiers & kAccStatic) != 0 ? 's' : 'i'; + + g_hash_table_insert (members, key, g_strdup_printf ("f:%c0x%zx", type, id)); +} + +static void +model_free (Model * model) +{ + g_hash_table_unref (model->members); + + g_free (model); +} + +gboolean +model_has (Model * self, + const gchar * member) +{ + return g_hash_table_contains (self->members, member); +} + +const gchar * +model_find (Model * self, + const gchar * member) +{ + return g_hash_table_lookup (self->members, member); +} + +gchar * +model_list (Model * self) +{ + GString * result; + GHashTableIter iter; + guint i; + const gchar * name; + + result = g_string_sized_new (128); + + g_string_append_c (result, '['); + + g_hash_table_iter_init (&iter, self->members); + for (i = 0; g_hash_table_iter_next (&iter, (gpointer *) &name, NULL); i++) + { + if (i > 0) + g_string_append_c (result, ','); + + g_string_append_c (result, '"'); + g_string_append (result, name); + g_string_append_c (result, '"'); + } + + g_string_append_c (result, ']'); + + return g_string_free (result, FALSE); +} + +gchar * +enumerate_methods_art (const gchar * class_query, + const gchar * method_query, + jboolean include_signature, + jboolean ignore_case, + jboolean skip_system_classes) +{ + gchar * result; + EnumerateMethodsContext ctx; + ArtClassVisitor visitor; + ArtClassVisitorVTable visitor_vtable = { NULL, }; + + ctx.class_query = make_pattern_spec (class_query, ignore_case); + ctx.method_query = make_pattern_spec (method_query, ignore_case); + ctx.include_signature = include_signature; + ctx.ignore_case = ignore_case; + ctx.skip_system_classes = skip_system_classes; + ctx.groups = g_hash_table_new_full (NULL, NULL, NULL, NULL); + + visitor.vtable = &visitor_vtable; + visitor.user_data = &ctx; + + visitor_vtable.visit = collect_matching_class_methods; + + art_api.visit_classes (art_api.linker, &visitor); + + result = finalize_method_groups_to_json (ctx.groups); + + g_hash_table_unref (ctx.groups); + g_pattern_spec_free (ctx.method_query); + g_pattern_spec_free (ctx.class_query); + + return result; +} + +static jboolean +collect_matching_class_methods (ArtClassVisitor * self, + ArtClass * klass) +{ + EnumerateMethodsContext * ctx = self->user_data; + const char * descriptor; + StdString descriptor_storage = { 0, }; + gchar * class_name = NULL; + gchar * class_name_copy = NULL; + const gchar * normalized_class_name; + JsonBuilder * group; + size_t class_name_length; + GHashTable * seen_method_names; + gpointer elements; + guint n, i; + + if (ctx->skip_system_classes && klass->class_loader == 0) + goto skip_class; + + descriptor = art_api.get_class_descriptor (klass, &descriptor_storage); + if (descriptor[0] != 'L') + goto skip_class; + + class_name = class_name_from_signature (descriptor); + + if (ctx->ignore_case) + { + class_name_copy = g_utf8_strdown (class_name, -1); + normalized_class_name = class_name_copy; + } + else + { + normalized_class_name = class_name; + } + + if (!g_pattern_match_string (ctx->class_query, normalized_class_name)) + goto skip_class; + + group = NULL; + class_name_length = strlen (class_name); + seen_method_names = ctx->include_signature ? NULL : g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + elements = read_art_array (klass, art_api.class_offset_methods, sizeof (gsize), NULL); + n = *(guint16 *) ((gpointer) klass + art_api.class_offset_copied_methods_offset); + for (i = 0; i != n; i++) + { + ArtMethod * method; + guint32 access_flags; + jboolean is_constructor; + StdString method_name = { 0, }; + const gchar * bare_method_name; + gchar * bare_method_name_copy = NULL; + const gchar * normalized_method_name; + gchar * normalized_method_name_copy = NULL; + + method = elements + (i * art_api.method_size); + + access_flags = *(guint32 *) ((gpointer) method + art_api.method_offset_access_flags); + is_constructor = (access_flags & kAccConstructor) != 0; + + art_api.pretty_method (&method_name, method, ctx->include_signature); + bare_method_name = std_string_c_str (&method_name); + if (ctx->include_signature) + { + const gchar * return_type_end, * name_begin; + GString * name; + + return_type_end = strchr (bare_method_name, ' '); + name_begin = return_type_end + 1 + class_name_length + 1; + if (is_constructor && g_str_has_prefix (name_begin, "")) + goto skip_method; + + name = g_string_sized_new (64); + + if (is_constructor) + { + g_string_append (name, "$init"); + g_string_append (name, strchr (name_begin, '>') + 1); + } + else + { + g_string_append (name, name_begin); + } + g_string_append (name, ": "); + g_string_append_len (name, bare_method_name, return_type_end - bare_method_name); + + bare_method_name_copy = g_string_free (name, FALSE); + bare_method_name = bare_method_name_copy; + } + else + { + const gchar * name_begin; + + name_begin = bare_method_name + class_name_length + 1; + if (is_constructor && strcmp (name_begin, "") == 0) + goto skip_method; + + if (is_constructor) + bare_method_name = "$init"; + else + bare_method_name += class_name_length + 1; + } + + if (seen_method_names != NULL && g_hash_table_contains (seen_method_names, bare_method_name)) + goto skip_method; + + if (ctx->ignore_case) + { + normalized_method_name_copy = g_utf8_strdown (bare_method_name, -1); + normalized_method_name = normalized_method_name_copy; + } + else + { + normalized_method_name = bare_method_name; + } + + if (!g_pattern_match_string (ctx->method_query, normalized_method_name)) + goto skip_method; + + if (group == NULL) + { + group = g_hash_table_lookup (ctx->groups, GUINT_TO_POINTER (klass->class_loader)); + if (group == NULL) + { + group = json_builder_new_immutable (); + g_hash_table_insert (ctx->groups, GUINT_TO_POINTER (klass->class_loader), group); + + json_builder_begin_object (group); + + json_builder_set_member_name (group, "loader"); + json_builder_add_int_value (group, klass->class_loader); + + json_builder_set_member_name (group, "classes"); + json_builder_begin_array (group); + } + + json_builder_begin_object (group); + + json_builder_set_member_name (group, "name"); + json_builder_add_string_value (group, class_name); + + json_builder_set_member_name (group, "methods"); + json_builder_begin_array (group); + } + + json_builder_add_string_value (group, bare_method_name); + + if (seen_method_names != NULL) + g_hash_table_add (seen_method_names, g_strdup (bare_method_name)); + +skip_method: + g_free (normalized_method_name_copy); + g_free (bare_method_name_copy); + std_string_destroy (&method_name); + } + + if (seen_method_names != NULL) + g_hash_table_unref (seen_method_names); + + if (group == NULL) + goto skip_class; + + json_builder_end_array (group); + json_builder_end_object (group); + +skip_class: + g_free (class_name_copy); + g_free (class_name); + std_string_destroy (&descriptor_storage); + + return TRUE; +} + +gchar * +enumerate_methods_jvm (const gchar * class_query, + const gchar * method_query, + jboolean include_signature, + jboolean ignore_case, + jboolean skip_system_classes, + JNIEnv * env, + jvmtiEnv * jvmti) +{ + gchar * result; + GPatternSpec * class_pattern, * method_pattern; + GHashTable * groups; + gpointer * ef = env->functions; + jobject (* new_global_ref) (JNIEnv *, jobject) = ef[21]; + void (* delete_local_ref) (JNIEnv *, jobject) = ef[23]; + jboolean (* is_same_object) (JNIEnv *, jobject, jobject) = ef[24]; + gpointer * jf = jvmti->functions - 1; + jvmtiError (* deallocate) (jvmtiEnv *, void * mem) = jf[47]; + jvmtiError (* get_class_signature) (jvmtiEnv *, jclass, char **, char **) = jf[48]; + jvmtiError (* get_class_methods) (jvmtiEnv *, jclass, jint *, jmethodID **) = jf[52]; + jvmtiError (* get_class_loader) (jvmtiEnv *, jclass, jobject *) = jf[57]; + jvmtiError (* get_method_name) (jvmtiEnv *, jmethodID, char **, char **, char **) = jf[64]; + jvmtiError (* get_loaded_classes) (jvmtiEnv *, jint *, jclass **) = jf[78]; + jint class_count, class_index; + jclass * classes; + + class_pattern = make_pattern_spec (class_query, ignore_case); + method_pattern = make_pattern_spec (method_query, ignore_case); + groups = g_hash_table_new_full (NULL, NULL, NULL, NULL); + + if (get_loaded_classes (jvmti, &class_count, &classes) != JVMTI_ERROR_NONE) + goto emit_results; + + for (class_index = 0; class_index != class_count; class_index++) + { + jclass klass = classes[class_index]; + jobject loader = NULL; + gboolean have_loader = FALSE; + char * signature = NULL; + gchar * class_name = NULL; + gchar * class_name_copy = NULL; + const gchar * normalized_class_name; + jint method_count, method_index; + jmethodID * methods = NULL; + JsonBuilder * group = NULL; + GHashTable * seen_method_names = NULL; + + if (skip_system_classes) + { + if (get_class_loader (jvmti, klass, &loader) != JVMTI_ERROR_NONE) + goto skip_class; + have_loader = TRUE; + + if (loader == NULL) + goto skip_class; + } + + if (get_class_signature (jvmti, klass, &signature, NULL) != JVMTI_ERROR_NONE) + goto skip_class; + + class_name = class_name_from_signature (signature); + + if (ignore_case) + { + class_name_copy = g_utf8_strdown (class_name, -1); + normalized_class_name = class_name_copy; + } + else + { + normalized_class_name = class_name; + } + + if (!g_pattern_match_string (class_pattern, normalized_class_name)) + goto skip_class; + + if (get_class_methods (jvmti, klass, &method_count, &methods) != JVMTI_ERROR_NONE) + goto skip_class; + + if (!include_signature) + seen_method_names = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + for (method_index = 0; method_index != method_count; method_index++) + { + jmethodID method = methods[method_index]; + const gchar * method_name; + char * method_name_value = NULL; + char * method_signature_value = NULL; + gchar * method_name_copy = NULL; + const gchar * normalized_method_name; + gchar * normalized_method_name_copy = NULL; + + if (get_method_name (jvmti, method, &method_name_value, include_signature ? &method_signature_value : NULL, NULL) != JVMTI_ERROR_NONE) + goto skip_method; + method_name = method_name_value; + + if (method_name[0] == '<') + { + if (strcmp (method_name, "") == 0) + method_name = "$init"; + else if (strcmp (method_name, "") == 0) + goto skip_method; + } + + if (include_signature) + { + method_name_copy = format_method_signature (method_name, method_signature_value); + method_name = method_name_copy; + } + + if (seen_method_names != NULL && g_hash_table_contains (seen_method_names, method_name)) + goto skip_method; + + if (ignore_case) + { + normalized_method_name_copy = g_utf8_strdown (method_name, -1); + normalized_method_name = normalized_method_name_copy; + } + else + { + normalized_method_name = method_name; + } + + if (!g_pattern_match_string (method_pattern, normalized_method_name)) + goto skip_method; + + if (group == NULL) + { + if (!have_loader && get_class_loader (jvmti, klass, &loader) != JVMTI_ERROR_NONE) + goto skip_method; + + if (loader == NULL) + { + group = g_hash_table_lookup (groups, NULL); + } + else + { + GHashTableIter iter; + jobject cur_loader; + JsonBuilder * cur_group; + + g_hash_table_iter_init (&iter, groups); + while (g_hash_table_iter_next (&iter, (gpointer *) &cur_loader, (gpointer *) &cur_group)) + { + if (cur_loader != NULL && is_same_object (env, cur_loader, loader)) + { + group = cur_group; + break; + } + } + } + + if (group == NULL) + { + jobject l; + gchar * str; + + l = (loader != NULL) ? new_global_ref (env, loader) : NULL; + + group = json_builder_new_immutable (); + g_hash_table_insert (groups, l, group); + + json_builder_begin_object (group); + + json_builder_set_member_name (group, "loader"); + str = g_strdup_printf ("0x%" G_GSIZE_MODIFIER "x", GPOINTER_TO_SIZE (l)); + json_builder_add_string_value (group, str); + g_free (str); + + json_builder_set_member_name (group, "classes"); + json_builder_begin_array (group); + } + + json_builder_begin_object (group); + + json_builder_set_member_name (group, "name"); + json_builder_add_string_value (group, class_name); + + json_builder_set_member_name (group, "methods"); + json_builder_begin_array (group); + } + + json_builder_add_string_value (group, method_name); + + if (seen_method_names != NULL) + g_hash_table_add (seen_method_names, g_strdup (method_name)); + +skip_method: + g_free (normalized_method_name_copy); + g_free (method_name_copy); + deallocate (jvmti, method_signature_value); + deallocate (jvmti, method_name_value); + } + +skip_class: + if (group != NULL) + { + json_builder_end_array (group); + json_builder_end_object (group); + } + + if (seen_method_names != NULL) + g_hash_table_unref (seen_method_names); + + deallocate (jvmti, methods); + + g_free (class_name_copy); + g_free (class_name); + deallocate (jvmti, signature); + + if (loader != NULL) + delete_local_ref (env, loader); + + delete_local_ref (env, klass); + } + + deallocate (jvmti, classes); + +emit_results: + result = finalize_method_groups_to_json (groups); + + g_hash_table_unref (groups); + g_pattern_spec_free (method_pattern); + g_pattern_spec_free (class_pattern); + + return result; +} + +static gchar * +finalize_method_groups_to_json (GHashTable * groups) +{ + GString * result; + GHashTableIter iter; + guint i; + JsonBuilder * group; + + result = g_string_sized_new (1024); + + g_string_append_c (result, '['); + + g_hash_table_iter_init (&iter, groups); + for (i = 0; g_hash_table_iter_next (&iter, NULL, (gpointer *) &group); i++) + { + JsonNode * root; + gchar * json; + + if (i > 0) + g_string_append_c (result, ','); + + json_builder_end_array (group); + json_builder_end_object (group); + + root = json_builder_get_root (group); + json = json_to_string (root, FALSE); + g_string_append (result, json); + g_free (json); + json_node_unref (root); + + g_object_unref (group); + } + + g_string_append_c (result, ']'); + + return g_string_free (result, FALSE); +} + +static GPatternSpec * +make_pattern_spec (const gchar * pattern, + jboolean ignore_case) +{ + GPatternSpec * spec; + + if (ignore_case) + { + gchar * str = g_utf8_strdown (pattern, -1); + spec = g_pattern_spec_new (str); + g_free (str); + } + else + { + spec = g_pattern_spec_new (pattern); + } + + return spec; +} + +static gchar * +class_name_from_signature (const gchar * descriptor) +{ + gchar * result, * c; + + result = g_strdup (descriptor + 1); + + for (c = result; *c != '\\0'; c++) + { + if (*c == '/') + *c = '.'; + } + + c[-1] = '\\0'; + + return result; +} + +static gchar * +format_method_signature (const gchar * name, + const gchar * signature) +{ + GString * sig; + const gchar * cursor; + gint arg_index; + + sig = g_string_sized_new (128); + + g_string_append (sig, name); + + cursor = signature; + arg_index = -1; + while (TRUE) + { + const gchar c = *cursor; + + if (c == '(') + { + g_string_append_c (sig, c); + cursor++; + arg_index = 0; + } + else if (c == ')') + { + g_string_append_c (sig, c); + cursor++; + break; + } + else + { + if (arg_index >= 1) + g_string_append (sig, ", "); + + append_type (sig, &cursor); + + if (arg_index != -1) + arg_index++; + } + } + + g_string_append (sig, ": "); + append_type (sig, &cursor); + + return g_string_free (sig, FALSE); +} + +static void +append_type (GString * output, + const gchar ** type) +{ + const gchar * cursor = *type; + + switch (*cursor) + { + case 'Z': + g_string_append (output, "boolean"); + cursor++; + break; + case 'B': + g_string_append (output, "byte"); + cursor++; + break; + case 'C': + g_string_append (output, "char"); + cursor++; + break; + case 'S': + g_string_append (output, "short"); + cursor++; + break; + case 'I': + g_string_append (output, "int"); + cursor++; + break; + case 'J': + g_string_append (output, "long"); + cursor++; + break; + case 'F': + g_string_append (output, "float"); + cursor++; + break; + case 'D': + g_string_append (output, "double"); + cursor++; + break; + case 'V': + g_string_append (output, "void"); + cursor++; + break; + case 'L': + { + gchar ch; + + cursor++; + for (; (ch = *cursor) != ';'; cursor++) + { + g_string_append_c (output, (ch != '/') ? ch : '.'); + } + cursor++; + + break; + } + case '[': + *type = cursor + 1; + append_type (output, type); + g_string_append (output, "[]"); + return; + default: + g_string_append (output, "BUG"); + cursor++; + } + + *type = cursor; +} + +void +dealloc (gpointer mem) +{ + g_free (mem); +} + +static gpointer +read_art_array (gpointer object_base, + guint field_offset, + guint length_size, + guint * length) +{ + gpointer result, header; + guint n; + + header = GSIZE_TO_POINTER (*(guint64 *) (object_base + field_offset)); + if (header != NULL) + { + result = header + length_size; + if (length_size == sizeof (guint32)) + n = *(guint32 *) header; + else + n = *(guint64 *) header; + } + else + { + result = NULL; + n = 0; + } + + if (length != NULL) + *length = n; + + return result; +} + +static void +std_string_destroy (StdString * str) +{ + if ((str->l.capacity & 1) != 0) + art_api.free (str->l.data); +} + +static gchar * +std_string_c_str (StdString * self) +{ + if ((self->l.capacity & 1) != 0) + return self->l.data; + + return self->s.data; +} +`; +var methodQueryPattern = /(.+)!([^/]+)\/?([isu]+)?/; +var cm = null; +var unwrap = null; +var Model = class _Model { + static build(handle, env) { + ensureInitialized(env); + return unwrap(handle, env, (object) => { + return new _Model(cm.new(handle, object, env)); + }); + } + static enumerateMethods(query, api2, env) { + ensureInitialized(env); + const params = query.match(methodQueryPattern); + if (params === null) { + throw new Error("Invalid query; format is: class!method -- see documentation of Java.enumerateMethods(query) for details"); + } + const classQuery = Memory.allocUtf8String(params[1]); + const methodQuery = Memory.allocUtf8String(params[2]); + let includeSignature = false; + let ignoreCase = false; + let skipSystemClasses = false; + const modifiers = params[3]; + if (modifiers !== void 0) { + includeSignature = modifiers.indexOf("s") !== -1; + ignoreCase = modifiers.indexOf("i") !== -1; + skipSystemClasses = modifiers.indexOf("u") !== -1; + } + let result; + if (api2.flavor === "jvm") { + const json = cm.enumerateMethodsJvm( + classQuery, + methodQuery, + boolToNative(includeSignature), + boolToNative(ignoreCase), + boolToNative(skipSystemClasses), + env, + api2.jvmti + ); + try { + result = JSON.parse(json.readUtf8String()).map((group) => { + const loaderRef = ptr(group.loader); + group.loader = !loaderRef.isNull() ? loaderRef : null; + return group; + }); + } finally { + cm.dealloc(json); + } + } else { + withRunnableArtThread(env.vm, env, (thread) => { + const json = cm.enumerateMethodsArt( + classQuery, + methodQuery, + boolToNative(includeSignature), + boolToNative(ignoreCase), + boolToNative(skipSystemClasses) + ); + try { + const addGlobalReference = api2["art::JavaVMExt::AddGlobalRef"]; + const { vm: vmHandle } = api2; + result = JSON.parse(json.readUtf8String()).map((group) => { + const loaderObj = group.loader; + group.loader = loaderObj !== 0 ? addGlobalReference(vmHandle, thread, ptr(loaderObj)) : null; + return group; + }); + } finally { + cm.dealloc(json); + } + }); + } + return result; + } + constructor(handle) { + this.handle = handle; + } + has(member) { + return cm.has(this.handle, Memory.allocUtf8String(member)) !== 0; + } + find(member) { + return cm.find(this.handle, Memory.allocUtf8String(member)).readUtf8String(); + } + list() { + const str = cm.list(this.handle); + try { + return JSON.parse(str.readUtf8String()); + } finally { + cm.dealloc(str); + } + } +}; +function ensureInitialized(env) { + if (cm === null) { + cm = compileModule(env); + unwrap = makeHandleUnwrapper(cm, env.vm); + } +} +function compileModule(env) { + const { pointerSize: pointerSize9 } = Process; + const lockSize = 8; + const modelsSize = pointerSize9; + const javaApiSize = 6 * pointerSize9; + const artApiSize = 10 * 4 + 5 * pointerSize9; + const dataSize = lockSize + modelsSize + javaApiSize + artApiSize; + const data = Memory.alloc(dataSize); + const lock = data; + const models = lock.add(lockSize); + const javaApi = models.add(modelsSize); + const { getDeclaredMethods, getDeclaredFields } = env.javaLangClass(); + const method = env.javaLangReflectMethod(); + const field = env.javaLangReflectField(); + let j = javaApi; + [ + getDeclaredMethods, + getDeclaredFields, + method.getName, + method.getModifiers, + field.getName, + field.getModifiers + ].forEach((value) => { + j = j.writePointer(value).add(pointerSize9); + }); + const artApi = javaApi.add(javaApiSize); + const { vm: vm3 } = env; + const artClass = getArtClassSpec(vm3); + if (artClass !== null) { + const c = artClass.offset; + const m = getArtMethodSpec(vm3); + const f = getArtFieldSpec(vm3); + let s = artApi; + [ + 1, + c.ifields, + c.methods, + c.sfields, + c.copiedMethodsOffset, + m.size, + m.offset.accessFlags, + f.size, + f.offset.accessFlags, + 4294967295 + ].forEach((value) => { + s = s.writeUInt(value).add(4); + }); + const api2 = getApi(); + [ + api2.artClassLinker.address, + api2["art::ClassLinker::VisitClasses"], + api2["art::mirror::Class::GetDescriptor"], + api2["art::ArtMethod::PrettyMethod"], + Process.getModuleByName("libc.so").getExportByName("free") + ].forEach((value, i) => { + if (value === void 0) { + value = NULL; + } + s = s.writePointer(value).add(pointerSize9); + }); + } + const cm2 = new CModule(code2, { + lock, + models, + java_api: javaApi, + art_api: artApi + }); + const reentrantOptions = { exceptions: "propagate" }; + const fastOptions = { exceptions: "propagate", scheduling: "exclusive" }; + return { + handle: cm2, + mode: artClass !== null ? "full" : "basic", + new: new NativeFunction(cm2.model_new, "pointer", ["pointer", "pointer", "pointer"], reentrantOptions), + has: new NativeFunction(cm2.model_has, "bool", ["pointer", "pointer"], fastOptions), + find: new NativeFunction(cm2.model_find, "pointer", ["pointer", "pointer"], fastOptions), + list: new NativeFunction(cm2.model_list, "pointer", ["pointer"], fastOptions), + enumerateMethodsArt: new NativeFunction( + cm2.enumerate_methods_art, + "pointer", + ["pointer", "pointer", "bool", "bool", "bool"], + reentrantOptions + ), + enumerateMethodsJvm: new NativeFunction(cm2.enumerate_methods_jvm, "pointer", [ + "pointer", + "pointer", + "bool", + "bool", + "bool", + "pointer", + "pointer" + ], reentrantOptions), + dealloc: new NativeFunction(cm2.dealloc, "void", ["pointer"], fastOptions) + }; +} +function makeHandleUnwrapper(cm2, vm3) { + if (cm2.mode === "basic") { + return nullUnwrap; + } + const decodeGlobal = getApi()["art::JavaVMExt::DecodeGlobal"]; + return function(handle, env, fn) { + let result; + withRunnableArtThread(vm3, env, (thread) => { + const object = decodeGlobal(vm3, thread, handle); + result = fn(object); + }); + return result; + }; +} +function nullUnwrap(handle, env, fn) { + return fn(NULL); +} +function boolToNative(val) { + return val ? 1 : 0; +} + +// node_modules/frida-java-bridge/lib/lru.js +var LRU = class { + constructor(capacity, destroy) { + this.items = /* @__PURE__ */ new Map(); + this.capacity = capacity; + this.destroy = destroy; + } + dispose(env) { + const { items, destroy } = this; + items.forEach((val) => { + destroy(val, env); + }); + items.clear(); + } + get(key) { + const { items } = this; + const item = items.get(key); + if (item !== void 0) { + items.delete(key); + items.set(key, item); + } + return item; + } + set(key, val, env) { + const { items } = this; + const existingVal = items.get(key); + if (existingVal !== void 0) { + items.delete(key); + this.destroy(existingVal, env); + } else if (items.size === this.capacity) { + const oldestKey = items.keys().next().value; + const oldestVal = items.get(oldestKey); + items.delete(oldestKey); + this.destroy(oldestVal, env); + } + items.set(key, val); + } +}; + +// node_modules/frida-java-bridge/lib/mkdex.js +var kAccPublic2 = 1; +var kAccNative2 = 256; +var kAccConstructor = 65536; +var kEndianTag = 305419896; +var kClassDefSize = 32; +var kProtoIdSize = 12; +var kFieldIdSize = 8; +var kMethodIdSize = 8; +var kTypeIdSize = 4; +var kStringIdSize = 4; +var kMapItemSize = 12; +var TYPE_HEADER_ITEM = 0; +var TYPE_STRING_ID_ITEM = 1; +var TYPE_TYPE_ID_ITEM = 2; +var TYPE_PROTO_ID_ITEM = 3; +var TYPE_FIELD_ID_ITEM = 4; +var TYPE_METHOD_ID_ITEM = 5; +var TYPE_CLASS_DEF_ITEM = 6; +var TYPE_MAP_LIST = 4096; +var TYPE_TYPE_LIST = 4097; +var TYPE_ANNOTATION_SET_ITEM = 4099; +var TYPE_CLASS_DATA_ITEM = 8192; +var TYPE_CODE_ITEM = 8193; +var TYPE_STRING_DATA_ITEM = 8194; +var TYPE_DEBUG_INFO_ITEM = 8195; +var TYPE_ANNOTATION_ITEM = 8196; +var TYPE_ANNOTATIONS_DIRECTORY_ITEM = 8198; +var VALUE_TYPE = 24; +var VALUE_ARRAY = 28; +var VISIBILITY_SYSTEM = 2; +var kDefaultConstructorSize = 24; +var kDefaultConstructorDebugInfo = Buffer2.from([3, 0, 7, 14, 0]); +var kDalvikAnnotationTypeThrows = "Ldalvik/annotation/Throws;"; +var kNullTerminator = Buffer2.from([0]); +function mkdex(spec) { + const builder = new DexBuilder(); + const fullSpec = Object.assign({}, spec); + builder.addClass(fullSpec); + return builder.build(); +} +var DexBuilder = class { + constructor() { + this.classes = []; + } + addClass(spec) { + this.classes.push(spec); + } + build() { + const model = computeModel(this.classes); + const { + classes, + interfaces, + fields, + methods, + protos, + parameters, + annotationDirectories, + annotationSets, + throwsAnnotations, + types, + strings + } = model; + let offset = 0; + const headerOffset = 0; + const checksumOffset = 8; + const signatureOffset = 12; + const signatureSize = 20; + const headerSize = 112; + offset += headerSize; + const stringIdsOffset = offset; + const stringIdsSize = strings.length * kStringIdSize; + offset += stringIdsSize; + const typeIdsOffset = offset; + const typeIdsSize = types.length * kTypeIdSize; + offset += typeIdsSize; + const protoIdsOffset = offset; + const protoIdsSize = protos.length * kProtoIdSize; + offset += protoIdsSize; + const fieldIdsOffset = offset; + const fieldIdsSize = fields.length * kFieldIdSize; + offset += fieldIdsSize; + const methodIdsOffset = offset; + const methodIdsSize = methods.length * kMethodIdSize; + offset += methodIdsSize; + const classDefsOffset = offset; + const classDefsSize = classes.length * kClassDefSize; + offset += classDefsSize; + const dataOffset = offset; + const annotationSetOffsets = annotationSets.map((set) => { + const setOffset = offset; + set.offset = setOffset; + offset += 4 + set.items.length * 4; + return setOffset; + }); + const javaCodeItems = classes.reduce((result, klass) => { + const constructorMethods = klass.classData.constructorMethods; + constructorMethods.forEach((method) => { + const [, accessFlags, superConstructor] = method; + if ((accessFlags & kAccNative2) === 0 && superConstructor >= 0) { + method.push(offset); + result.push({ offset, superConstructor }); + offset += kDefaultConstructorSize; + } + }); + return result; + }, []); + annotationDirectories.forEach((dir) => { + dir.offset = offset; + offset += 16 + dir.methods.length * 8; + }); + const interfaceOffsets = interfaces.map((iface) => { + offset = align(offset, 4); + const ifaceOffset = offset; + iface.offset = ifaceOffset; + offset += 4 + 2 * iface.types.length; + return ifaceOffset; + }); + const parameterOffsets = parameters.map((param) => { + offset = align(offset, 4); + const paramOffset = offset; + param.offset = paramOffset; + offset += 4 + 2 * param.types.length; + return paramOffset; + }); + const stringChunks = []; + const stringOffsets = strings.map((str) => { + const strOffset = offset; + const header = Buffer2.from(createUleb128(str.length)); + const data = Buffer2.from(str, "utf8"); + const chunk = Buffer2.concat([header, data, kNullTerminator]); + stringChunks.push(chunk); + offset += chunk.length; + return strOffset; + }); + const debugInfoOffsets = javaCodeItems.map((codeItem) => { + const debugOffset = offset; + offset += kDefaultConstructorDebugInfo.length; + return debugOffset; + }); + const throwsAnnotationBlobs = throwsAnnotations.map((annotation) => { + const blob = makeThrowsAnnotation(annotation); + annotation.offset = offset; + offset += blob.length; + return blob; + }); + const classDataBlobs = classes.map((klass, index) => { + klass.classData.offset = offset; + const blob = makeClassData(klass); + offset += blob.length; + return blob; + }); + const linkSize = 0; + const linkOffset = 0; + offset = align(offset, 4); + const mapOffset = offset; + const typeListLength = interfaces.length + parameters.length; + const mapNumItems = 4 + (fields.length > 0 ? 1 : 0) + 2 + annotationSets.length + javaCodeItems.length + annotationDirectories.length + (typeListLength > 0 ? 1 : 0) + 1 + debugInfoOffsets.length + throwsAnnotations.length + classes.length + 1; + const mapSize = 4 + mapNumItems * kMapItemSize; + offset += mapSize; + const dataSize = offset - dataOffset; + const fileSize = offset; + const dex = Buffer2.alloc(fileSize); + dex.write("dex\n035"); + dex.writeUInt32LE(fileSize, 32); + dex.writeUInt32LE(headerSize, 36); + dex.writeUInt32LE(kEndianTag, 40); + dex.writeUInt32LE(linkSize, 44); + dex.writeUInt32LE(linkOffset, 48); + dex.writeUInt32LE(mapOffset, 52); + dex.writeUInt32LE(strings.length, 56); + dex.writeUInt32LE(stringIdsOffset, 60); + dex.writeUInt32LE(types.length, 64); + dex.writeUInt32LE(typeIdsOffset, 68); + dex.writeUInt32LE(protos.length, 72); + dex.writeUInt32LE(protoIdsOffset, 76); + dex.writeUInt32LE(fields.length, 80); + dex.writeUInt32LE(fields.length > 0 ? fieldIdsOffset : 0, 84); + dex.writeUInt32LE(methods.length, 88); + dex.writeUInt32LE(methodIdsOffset, 92); + dex.writeUInt32LE(classes.length, 96); + dex.writeUInt32LE(classDefsOffset, 100); + dex.writeUInt32LE(dataSize, 104); + dex.writeUInt32LE(dataOffset, 108); + stringOffsets.forEach((offset2, index) => { + dex.writeUInt32LE(offset2, stringIdsOffset + index * kStringIdSize); + }); + types.forEach((id, index) => { + dex.writeUInt32LE(id, typeIdsOffset + index * kTypeIdSize); + }); + protos.forEach((proto, index) => { + const [shortyIndex, returnTypeIndex, params] = proto; + const protoOffset = protoIdsOffset + index * kProtoIdSize; + dex.writeUInt32LE(shortyIndex, protoOffset); + dex.writeUInt32LE(returnTypeIndex, protoOffset + 4); + dex.writeUInt32LE(params !== null ? params.offset : 0, protoOffset + 8); + }); + fields.forEach((field, index) => { + const [classIndex, typeIndex, nameIndex] = field; + const fieldOffset = fieldIdsOffset + index * kFieldIdSize; + dex.writeUInt16LE(classIndex, fieldOffset); + dex.writeUInt16LE(typeIndex, fieldOffset + 2); + dex.writeUInt32LE(nameIndex, fieldOffset + 4); + }); + methods.forEach((method, index) => { + const [classIndex, protoIndex, nameIndex] = method; + const methodOffset = methodIdsOffset + index * kMethodIdSize; + dex.writeUInt16LE(classIndex, methodOffset); + dex.writeUInt16LE(protoIndex, methodOffset + 2); + dex.writeUInt32LE(nameIndex, methodOffset + 4); + }); + classes.forEach((klass, index) => { + const { interfaces: interfaces2, annotationsDirectory } = klass; + const interfacesOffset = interfaces2 !== null ? interfaces2.offset : 0; + const annotationsOffset = annotationsDirectory !== null ? annotationsDirectory.offset : 0; + const staticValuesOffset = 0; + const classOffset = classDefsOffset + index * kClassDefSize; + dex.writeUInt32LE(klass.index, classOffset); + dex.writeUInt32LE(klass.accessFlags, classOffset + 4); + dex.writeUInt32LE(klass.superClassIndex, classOffset + 8); + dex.writeUInt32LE(interfacesOffset, classOffset + 12); + dex.writeUInt32LE(klass.sourceFileIndex, classOffset + 16); + dex.writeUInt32LE(annotationsOffset, classOffset + 20); + dex.writeUInt32LE(klass.classData.offset, classOffset + 24); + dex.writeUInt32LE(staticValuesOffset, classOffset + 28); + }); + annotationSets.forEach((set, index) => { + const { items } = set; + const setOffset = annotationSetOffsets[index]; + dex.writeUInt32LE(items.length, setOffset); + items.forEach((item, index2) => { + dex.writeUInt32LE(item.offset, setOffset + 4 + index2 * 4); + }); + }); + javaCodeItems.forEach((codeItem, index) => { + const { offset: offset2, superConstructor } = codeItem; + const registersSize = 1; + const insSize = 1; + const outsSize = 1; + const triesSize = 0; + const insnsSize = 4; + dex.writeUInt16LE(registersSize, offset2); + dex.writeUInt16LE(insSize, offset2 + 2); + dex.writeUInt16LE(outsSize, offset2 + 4); + dex.writeUInt16LE(triesSize, offset2 + 6); + dex.writeUInt32LE(debugInfoOffsets[index], offset2 + 8); + dex.writeUInt32LE(insnsSize, offset2 + 12); + dex.writeUInt16LE(4208, offset2 + 16); + dex.writeUInt16LE(superConstructor, offset2 + 18); + dex.writeUInt16LE(0, offset2 + 20); + dex.writeUInt16LE(14, offset2 + 22); + }); + annotationDirectories.forEach((dir) => { + const dirOffset = dir.offset; + const classAnnotationsOffset = 0; + const fieldsSize = 0; + const annotatedMethodsSize = dir.methods.length; + const annotatedParametersSize = 0; + dex.writeUInt32LE(classAnnotationsOffset, dirOffset); + dex.writeUInt32LE(fieldsSize, dirOffset + 4); + dex.writeUInt32LE(annotatedMethodsSize, dirOffset + 8); + dex.writeUInt32LE(annotatedParametersSize, dirOffset + 12); + dir.methods.forEach((method, index) => { + const entryOffset = dirOffset + 16 + index * 8; + const [methodIndex, annotationSet] = method; + dex.writeUInt32LE(methodIndex, entryOffset); + dex.writeUInt32LE(annotationSet.offset, entryOffset + 4); + }); + }); + interfaces.forEach((iface, index) => { + const ifaceOffset = interfaceOffsets[index]; + dex.writeUInt32LE(iface.types.length, ifaceOffset); + iface.types.forEach((type, typeIndex) => { + dex.writeUInt16LE(type, ifaceOffset + 4 + typeIndex * 2); + }); + }); + parameters.forEach((param, index) => { + const paramOffset = parameterOffsets[index]; + dex.writeUInt32LE(param.types.length, paramOffset); + param.types.forEach((type, typeIndex) => { + dex.writeUInt16LE(type, paramOffset + 4 + typeIndex * 2); + }); + }); + stringChunks.forEach((chunk, index) => { + chunk.copy(dex, stringOffsets[index]); + }); + debugInfoOffsets.forEach((debugInfoOffset) => { + kDefaultConstructorDebugInfo.copy(dex, debugInfoOffset); + }); + throwsAnnotationBlobs.forEach((annotationBlob, index) => { + annotationBlob.copy(dex, throwsAnnotations[index].offset); + }); + classDataBlobs.forEach((classDataBlob, index) => { + classDataBlob.copy(dex, classes[index].classData.offset); + }); + dex.writeUInt32LE(mapNumItems, mapOffset); + const mapItems = [ + [TYPE_HEADER_ITEM, 1, headerOffset], + [TYPE_STRING_ID_ITEM, strings.length, stringIdsOffset], + [TYPE_TYPE_ID_ITEM, types.length, typeIdsOffset], + [TYPE_PROTO_ID_ITEM, protos.length, protoIdsOffset] + ]; + if (fields.length > 0) { + mapItems.push([TYPE_FIELD_ID_ITEM, fields.length, fieldIdsOffset]); + } + mapItems.push([TYPE_METHOD_ID_ITEM, methods.length, methodIdsOffset]); + mapItems.push([TYPE_CLASS_DEF_ITEM, classes.length, classDefsOffset]); + annotationSets.forEach((set, index) => { + mapItems.push([TYPE_ANNOTATION_SET_ITEM, set.items.length, annotationSetOffsets[index]]); + }); + javaCodeItems.forEach((codeItem) => { + mapItems.push([TYPE_CODE_ITEM, 1, codeItem.offset]); + }); + annotationDirectories.forEach((dir) => { + mapItems.push([TYPE_ANNOTATIONS_DIRECTORY_ITEM, 1, dir.offset]); + }); + if (typeListLength > 0) { + mapItems.push([TYPE_TYPE_LIST, typeListLength, interfaceOffsets.concat(parameterOffsets)[0]]); + } + mapItems.push([TYPE_STRING_DATA_ITEM, strings.length, stringOffsets[0]]); + debugInfoOffsets.forEach((debugInfoOffset) => { + mapItems.push([TYPE_DEBUG_INFO_ITEM, 1, debugInfoOffset]); + }); + throwsAnnotations.forEach((annotation) => { + mapItems.push([TYPE_ANNOTATION_ITEM, 1, annotation.offset]); + }); + classes.forEach((klass) => { + mapItems.push([TYPE_CLASS_DATA_ITEM, 1, klass.classData.offset]); + }); + mapItems.push([TYPE_MAP_LIST, 1, mapOffset]); + mapItems.forEach((item, index) => { + const [type, size, offset2] = item; + const itemOffset = mapOffset + 4 + index * kMapItemSize; + dex.writeUInt16LE(type, itemOffset); + dex.writeUInt32LE(size, itemOffset + 4); + dex.writeUInt32LE(offset2, itemOffset + 8); + }); + const hash = new Checksum("sha1"); + hash.update(dex.slice(signatureOffset + signatureSize)); + Buffer2.from(hash.getDigest()).copy(dex, signatureOffset); + dex.writeUInt32LE(adler32(dex, signatureOffset), checksumOffset); + return dex; + } +}; +function makeClassData(klass) { + const { instanceFields, constructorMethods, virtualMethods } = klass.classData; + const staticFieldsSize = 0; + return Buffer2.from([ + staticFieldsSize + ].concat(createUleb128(instanceFields.length)).concat(createUleb128(constructorMethods.length)).concat(createUleb128(virtualMethods.length)).concat(instanceFields.reduce((result, [indexDiff, accessFlags]) => { + return result.concat(createUleb128(indexDiff)).concat(createUleb128(accessFlags)); + }, [])).concat(constructorMethods.reduce((result, [indexDiff, accessFlags, , codeOffset]) => { + return result.concat(createUleb128(indexDiff)).concat(createUleb128(accessFlags)).concat(createUleb128(codeOffset || 0)); + }, [])).concat(virtualMethods.reduce((result, [indexDiff, accessFlags]) => { + const codeOffset = 0; + return result.concat(createUleb128(indexDiff)).concat(createUleb128(accessFlags)).concat([codeOffset]); + }, []))); +} +function makeThrowsAnnotation(annotation) { + const { thrownTypes } = annotation; + return Buffer2.from( + [ + VISIBILITY_SYSTEM + ].concat(createUleb128(annotation.type)).concat([1]).concat(createUleb128(annotation.value)).concat([VALUE_ARRAY, thrownTypes.length]).concat(thrownTypes.reduce((result, type) => { + result.push(VALUE_TYPE, type); + return result; + }, [])) + ); +} +function computeModel(classes) { + const strings = /* @__PURE__ */ new Set(); + const types = /* @__PURE__ */ new Set(); + const protos = {}; + const fields = []; + const methods = []; + const throwsAnnotations = {}; + const javaConstructors = /* @__PURE__ */ new Set(); + const superConstructors = /* @__PURE__ */ new Set(); + classes.forEach((klass) => { + const { name, superClass, sourceFileName } = klass; + strings.add("this"); + strings.add(name); + types.add(name); + strings.add(superClass); + types.add(superClass); + strings.add(sourceFileName); + klass.interfaces.forEach((iface) => { + strings.add(iface); + types.add(iface); + }); + klass.fields.forEach((field) => { + const [fieldName, fieldType] = field; + strings.add(fieldName); + strings.add(fieldType); + types.add(fieldType); + fields.push([klass.name, fieldType, fieldName]); + }); + if (!klass.methods.some(([methodName]) => methodName === "")) { + klass.methods.unshift(["", "V", []]); + javaConstructors.add(name); + } + klass.methods.forEach((method) => { + const [methodName, retType, argTypes, thrownTypes = [], accessFlags] = method; + strings.add(methodName); + const protoId = addProto(retType, argTypes); + let throwsAnnotationId = null; + if (thrownTypes.length > 0) { + const typesNormalized = thrownTypes.slice(); + typesNormalized.sort(); + throwsAnnotationId = typesNormalized.join("|"); + let throwsAnnotation = throwsAnnotations[throwsAnnotationId]; + if (throwsAnnotation === void 0) { + throwsAnnotation = { + id: throwsAnnotationId, + types: typesNormalized + }; + throwsAnnotations[throwsAnnotationId] = throwsAnnotation; + } + strings.add(kDalvikAnnotationTypeThrows); + types.add(kDalvikAnnotationTypeThrows); + thrownTypes.forEach((type) => { + strings.add(type); + types.add(type); + }); + strings.add("value"); + } + methods.push([klass.name, protoId, methodName, throwsAnnotationId, accessFlags]); + if (methodName === "") { + superConstructors.add(name + "|" + protoId); + const superConstructorId = superClass + "|" + protoId; + if (javaConstructors.has(name) && !superConstructors.has(superConstructorId)) { + methods.push([superClass, protoId, methodName, null, 0]); + superConstructors.add(superConstructorId); + } + } + }); + }); + function addProto(retType, argTypes) { + const signature = [retType].concat(argTypes); + const id = signature.join("|"); + if (protos[id] !== void 0) { + return id; + } + strings.add(retType); + types.add(retType); + argTypes.forEach((argType) => { + strings.add(argType); + types.add(argType); + }); + const shorty = signature.map(typeToShorty).join(""); + strings.add(shorty); + protos[id] = [id, shorty, retType, argTypes]; + return id; + } + const stringItems = Array.from(strings); + stringItems.sort(); + const stringToIndex = stringItems.reduce((result, string, index) => { + result[string] = index; + return result; + }, {}); + const typeItems = Array.from(types).map((name) => stringToIndex[name]); + typeItems.sort(compareNumbers); + const typeToIndex = typeItems.reduce((result, stringIndex, typeIndex) => { + result[stringItems[stringIndex]] = typeIndex; + return result; + }, {}); + const literalProtoItems = Object.keys(protos).map((id) => protos[id]); + literalProtoItems.sort(compareProtoItems); + const parameters = {}; + const protoItems = literalProtoItems.map((item) => { + const [, shorty, retType, argTypes] = item; + let params; + if (argTypes.length > 0) { + const argTypesSig = argTypes.join("|"); + params = parameters[argTypesSig]; + if (params === void 0) { + params = { + types: argTypes.map((type) => typeToIndex[type]), + offset: -1 + }; + parameters[argTypesSig] = params; + } + } else { + params = null; + } + return [ + stringToIndex[shorty], + typeToIndex[retType], + params + ]; + }); + const protoToIndex = literalProtoItems.reduce((result, item, index) => { + const [id] = item; + result[id] = index; + return result; + }, {}); + const parameterItems = Object.keys(parameters).map((id) => parameters[id]); + const fieldItems = fields.map((field) => { + const [klass, fieldType, fieldName] = field; + return [ + typeToIndex[klass], + typeToIndex[fieldType], + stringToIndex[fieldName] + ]; + }); + fieldItems.sort(compareFieldItems); + const methodItems = methods.map((method) => { + const [klass, protoId, name, annotationsId, accessFlags] = method; + return [ + typeToIndex[klass], + protoToIndex[protoId], + stringToIndex[name], + annotationsId, + accessFlags + ]; + }); + methodItems.sort(compareMethodItems); + const throwsAnnotationItems = Object.keys(throwsAnnotations).map((id) => throwsAnnotations[id]).map((item) => { + return { + id: item.id, + type: typeToIndex[kDalvikAnnotationTypeThrows], + value: stringToIndex.value, + thrownTypes: item.types.map((type) => typeToIndex[type]), + offset: -1 + }; + }); + const annotationSetItems = throwsAnnotationItems.map((item) => { + return { + id: item.id, + items: [item], + offset: -1 + }; + }); + const annotationSetIdToIndex = annotationSetItems.reduce((result, item, index) => { + result[item.id] = index; + return result; + }, {}); + const interfaceLists = {}; + const annotationDirectories = []; + const classItems = classes.map((klass) => { + const classIndex = typeToIndex[klass.name]; + const accessFlags = kAccPublic2; + const superClassIndex = typeToIndex[klass.superClass]; + let ifaceList; + const ifaces = klass.interfaces.map((type) => typeToIndex[type]); + if (ifaces.length > 0) { + ifaces.sort(compareNumbers); + const ifacesId = ifaces.join("|"); + ifaceList = interfaceLists[ifacesId]; + if (ifaceList === void 0) { + ifaceList = { + types: ifaces, + offset: -1 + }; + interfaceLists[ifacesId] = ifaceList; + } + } else { + ifaceList = null; + } + const sourceFileIndex = stringToIndex[klass.sourceFileName]; + const classMethods = methodItems.reduce((result, method, index) => { + const [holder, protoIndex, name, annotationsId, accessFlags2] = method; + if (holder === classIndex) { + result.push([index, name, annotationsId, protoIndex, accessFlags2]); + } + return result; + }, []); + let annotationsDirectory = null; + const methodAnnotations = classMethods.filter(([, , annotationsId]) => { + return annotationsId !== null; + }).map(([index, , annotationsId]) => { + return [index, annotationSetItems[annotationSetIdToIndex[annotationsId]]]; + }); + if (methodAnnotations.length > 0) { + annotationsDirectory = { + methods: methodAnnotations, + offset: -1 + }; + annotationDirectories.push(annotationsDirectory); + } + const instanceFields = fieldItems.reduce((result, field, index) => { + const [holder] = field; + if (holder === classIndex) { + result.push([index > 0 ? 1 : 0, kAccPublic2]); + } + return result; + }, []); + const constructorNameIndex = stringToIndex[""]; + const constructorMethods = classMethods.filter(([, name]) => name === constructorNameIndex).map(([index, , , protoIndex]) => { + if (javaConstructors.has(klass.name)) { + let superConstructor = -1; + const numMethodItems = methodItems.length; + for (let i = 0; i !== numMethodItems; i++) { + const [methodClass, methodProto, methodName] = methodItems[i]; + if (methodClass === superClassIndex && methodName === constructorNameIndex && methodProto === protoIndex) { + superConstructor = i; + break; + } + } + return [index, kAccPublic2 | kAccConstructor, superConstructor]; + } else { + return [index, kAccPublic2 | kAccConstructor | kAccNative2, -1]; + } + }); + const virtualMethods = compressClassMethodIndexes(classMethods.filter(([, name]) => name !== constructorNameIndex).map(([index, , , , accessFlags2]) => { + return [index, accessFlags2 | kAccPublic2 | kAccNative2]; + })); + const classData = { + instanceFields, + constructorMethods, + virtualMethods, + offset: -1 + }; + return { + index: classIndex, + accessFlags, + superClassIndex, + interfaces: ifaceList, + sourceFileIndex, + annotationsDirectory, + classData + }; + }); + const interfaceItems = Object.keys(interfaceLists).map((id) => interfaceLists[id]); + return { + classes: classItems, + interfaces: interfaceItems, + fields: fieldItems, + methods: methodItems, + protos: protoItems, + parameters: parameterItems, + annotationDirectories, + annotationSets: annotationSetItems, + throwsAnnotations: throwsAnnotationItems, + types: typeItems, + strings: stringItems + }; +} +function compressClassMethodIndexes(items) { + let previousIndex = 0; + return items.map(([index, accessFlags], elementIndex) => { + let result; + if (elementIndex === 0) { + result = [index, accessFlags]; + } else { + result = [index - previousIndex, accessFlags]; + } + previousIndex = index; + return result; + }); +} +function compareNumbers(a, b) { + return a - b; +} +function compareProtoItems(a, b) { + const [, , aRetType, aArgTypes] = a; + const [, , bRetType, bArgTypes] = b; + if (aRetType < bRetType) { + return -1; + } + if (aRetType > bRetType) { + return 1; + } + const aArgTypesSig = aArgTypes.join("|"); + const bArgTypesSig = bArgTypes.join("|"); + if (aArgTypesSig < bArgTypesSig) { + return -1; + } + if (aArgTypesSig > bArgTypesSig) { + return 1; + } + return 0; +} +function compareFieldItems(a, b) { + const [aClass, aType, aName] = a; + const [bClass, bType, bName] = b; + if (aClass !== bClass) { + return aClass - bClass; + } + if (aName !== bName) { + return aName - bName; + } + return aType - bType; +} +function compareMethodItems(a, b) { + const [aClass, aProto, aName] = a; + const [bClass, bProto, bName] = b; + if (aClass !== bClass) { + return aClass - bClass; + } + if (aName !== bName) { + return aName - bName; + } + return aProto - bProto; +} +function typeToShorty(type) { + const firstCharacter = type[0]; + return firstCharacter === "L" || firstCharacter === "[" ? "L" : type; +} +function createUleb128(value) { + if (value <= 127) { + return [value]; + } + const result = []; + let moreSlicesNeeded = false; + do { + let slice2 = value & 127; + value >>= 7; + moreSlicesNeeded = value !== 0; + if (moreSlicesNeeded) { + slice2 |= 128; + } + result.push(slice2); + } while (moreSlicesNeeded); + return result; +} +function align(value, alignment) { + const alignmentDelta = value % alignment; + if (alignmentDelta === 0) { + return value; + } + return value + alignment - alignmentDelta; +} +function adler32(buffer, offset) { + let a = 1; + let b = 0; + const length = buffer.length; + for (let i = offset; i < length; i++) { + a = (a + buffer[i]) % 65521; + b = (b + a) % 65521; + } + return (b << 16 | a) >>> 0; +} +var mkdex_default = mkdex; + +// node_modules/frida-java-bridge/lib/types.js +var JNILocalRefType = 1; +var vm = null; +var primitiveArrayHandler = null; +function initialize(_vm) { + vm = _vm; +} +function getType(typeName, unbox, factory) { + let type = getPrimitiveType(typeName); + if (type === null) { + if (typeName.indexOf("[") === 0) { + type = getArrayType(typeName, unbox, factory); + } else { + if (typeName[0] === "L" && typeName[typeName.length - 1] === ";") { + typeName = typeName.substring(1, typeName.length - 1); + } + type = getObjectType(typeName, unbox, factory); + } + } + return Object.assign({ className: typeName }, type); +} +var primitiveTypes = { + boolean: { + name: "Z", + type: "uint8", + size: 1, + byteSize: 1, + defaultValue: false, + isCompatible(v) { + return typeof v === "boolean"; + }, + fromJni(v) { + return !!v; + }, + toJni(v) { + return v ? 1 : 0; + }, + read(address) { + return address.readU8(); + }, + write(address, value) { + address.writeU8(value); + }, + toString() { + return this.name; + } + }, + byte: { + name: "B", + type: "int8", + size: 1, + byteSize: 1, + defaultValue: 0, + isCompatible(v) { + return Number.isInteger(v) && v >= -128 && v <= 127; + }, + fromJni: identity, + toJni: identity, + read(address) { + return address.readS8(); + }, + write(address, value) { + address.writeS8(value); + }, + toString() { + return this.name; + } + }, + char: { + name: "C", + type: "uint16", + size: 1, + byteSize: 2, + defaultValue: 0, + isCompatible(v) { + if (typeof v !== "string" || v.length !== 1) { + return false; + } + const code3 = v.charCodeAt(0); + return code3 >= 0 && code3 <= 65535; + }, + fromJni(c) { + return String.fromCharCode(c); + }, + toJni(s) { + return s.charCodeAt(0); + }, + read(address) { + return address.readU16(); + }, + write(address, value) { + address.writeU16(value); + }, + toString() { + return this.name; + } + }, + short: { + name: "S", + type: "int16", + size: 1, + byteSize: 2, + defaultValue: 0, + isCompatible(v) { + return Number.isInteger(v) && v >= -32768 && v <= 32767; + }, + fromJni: identity, + toJni: identity, + read(address) { + return address.readS16(); + }, + write(address, value) { + address.writeS16(value); + }, + toString() { + return this.name; + } + }, + int: { + name: "I", + type: "int32", + size: 1, + byteSize: 4, + defaultValue: 0, + isCompatible(v) { + return Number.isInteger(v) && v >= -2147483648 && v <= 2147483647; + }, + fromJni: identity, + toJni: identity, + read(address) { + return address.readS32(); + }, + write(address, value) { + address.writeS32(value); + }, + toString() { + return this.name; + } + }, + long: { + name: "J", + type: "int64", + size: 2, + byteSize: 8, + defaultValue: 0, + isCompatible(v) { + return typeof v === "number" || v instanceof Int64; + }, + fromJni: identity, + toJni: identity, + read(address) { + return address.readS64(); + }, + write(address, value) { + address.writeS64(value); + }, + toString() { + return this.name; + } + }, + float: { + name: "F", + type: "float", + size: 1, + byteSize: 4, + defaultValue: 0, + isCompatible(v) { + return typeof v === "number"; + }, + fromJni: identity, + toJni: identity, + read(address) { + return address.readFloat(); + }, + write(address, value) { + address.writeFloat(value); + }, + toString() { + return this.name; + } + }, + double: { + name: "D", + type: "double", + size: 2, + byteSize: 8, + defaultValue: 0, + isCompatible(v) { + return typeof v === "number"; + }, + fromJni: identity, + toJni: identity, + read(address) { + return address.readDouble(); + }, + write(address, value) { + address.writeDouble(value); + }, + toString() { + return this.name; + } + }, + void: { + name: "V", + type: "void", + size: 0, + byteSize: 0, + defaultValue: void 0, + isCompatible(v) { + return v === void 0; + }, + fromJni() { + return void 0; + }, + toJni() { + return NULL; + }, + toString() { + return this.name; + } + } +}; +var primitiveTypesNames = new Set(Object.values(primitiveTypes).map((t) => t.name)); +function getPrimitiveType(name) { + const result = primitiveTypes[name]; + return result !== void 0 ? result : null; +} +function getObjectType(typeName, unbox, factory) { + const cache = factory._types[unbox ? 1 : 0]; + let type = cache[typeName]; + if (type !== void 0) { + return type; + } + if (typeName === "java.lang.Object") { + type = getJavaLangObjectType(factory); + } else { + type = getAnyObjectType(typeName, unbox, factory); + } + cache[typeName] = type; + return type; +} +function getJavaLangObjectType(factory) { + return { + name: "Ljava/lang/Object;", + type: "pointer", + size: 1, + defaultValue: NULL, + isCompatible(v) { + if (v === null) { + return true; + } + if (v === void 0) { + return false; + } + const isWrapper = v.$h instanceof NativePointer; + if (isWrapper) { + return true; + } + return typeof v === "string"; + }, + fromJni(h, env, owned) { + if (h.isNull()) { + return null; + } + return factory.cast(h, factory.use("java.lang.Object"), owned); + }, + toJni(o, env) { + if (o === null) { + return NULL; + } + if (typeof o === "string") { + return env.newStringUtf(o); + } + return o.$h; + } + }; +} +function getAnyObjectType(typeName, unbox, factory) { + let cachedClass = null; + let cachedIsInstance = null; + let cachedIsDefaultString = null; + function getClass() { + if (cachedClass === null) { + cachedClass = factory.use(typeName).class; + } + return cachedClass; + } + function isInstance(v) { + const klass = getClass(); + if (cachedIsInstance === null) { + cachedIsInstance = klass.isInstance.overload("java.lang.Object"); + } + return cachedIsInstance.call(klass, v); + } + function typeIsDefaultString() { + if (cachedIsDefaultString === null) { + const x = getClass(); + cachedIsDefaultString = factory.use("java.lang.String").class.isAssignableFrom(x); + } + return cachedIsDefaultString; + } + return { + name: makeJniObjectTypeName(typeName), + type: "pointer", + size: 1, + defaultValue: NULL, + isCompatible(v) { + if (v === null) { + return true; + } + if (v === void 0) { + return false; + } + const isWrapper = v.$h instanceof NativePointer; + if (isWrapper) { + return isInstance(v); + } + return typeof v === "string" && typeIsDefaultString(); + }, + fromJni(h, env, owned) { + if (h.isNull()) { + return null; + } + if (typeIsDefaultString() && unbox) { + return env.stringFromJni(h); + } + return factory.cast(h, factory.use(typeName), owned); + }, + toJni(o, env) { + if (o === null) { + return NULL; + } + if (typeof o === "string") { + return env.newStringUtf(o); + } + return o.$h; + }, + toString() { + return this.name; + } + }; +} +var primitiveArrayTypes = [ + ["Z", "boolean"], + ["B", "byte"], + ["C", "char"], + ["D", "double"], + ["F", "float"], + ["I", "int"], + ["J", "long"], + ["S", "short"] +].reduce((result, [shorty, name]) => { + result["[" + shorty] = makePrimitiveArrayType("[" + shorty, name); + return result; +}, {}); +function makePrimitiveArrayType(shorty, name) { + const envProto = Env.prototype; + const nameTitled = toTitleCase(name); + const spec = { + typeName: name, + newArray: envProto["new" + nameTitled + "Array"], + setRegion: envProto["set" + nameTitled + "ArrayRegion"], + getElements: envProto["get" + nameTitled + "ArrayElements"], + releaseElements: envProto["release" + nameTitled + "ArrayElements"] + }; + return { + name: shorty, + type: "pointer", + size: 1, + defaultValue: NULL, + isCompatible(v) { + return isCompatiblePrimitiveArray(v, name); + }, + fromJni(h, env, owned) { + return fromJniPrimitiveArray(h, spec, env, owned); + }, + toJni(arr, env) { + return toJniPrimitiveArray(arr, spec, env); + } + }; +} +function getArrayType(typeName, unbox, factory) { + const primitiveType = primitiveArrayTypes[typeName]; + if (primitiveType !== void 0) { + return primitiveType; + } + if (typeName.indexOf("[") !== 0) { + throw new Error("Unsupported type: " + typeName); + } + let elementTypeName = typeName.substring(1); + const elementType = getType(elementTypeName, unbox, factory); + let numInternalArrays = 0; + const end = elementTypeName.length; + while (numInternalArrays !== end && elementTypeName[numInternalArrays] === "[") { + numInternalArrays++; + } + elementTypeName = elementTypeName.substring(numInternalArrays); + if (elementTypeName[0] === "L" && elementTypeName[elementTypeName.length - 1] === ";") { + elementTypeName = elementTypeName.substring(1, elementTypeName.length - 1); + } + let internalElementTypeName = elementTypeName.replace(/\./g, "/"); + if (primitiveTypesNames.has(internalElementTypeName)) { + internalElementTypeName = "[".repeat(numInternalArrays) + internalElementTypeName; + } else { + internalElementTypeName = "[".repeat(numInternalArrays) + "L" + internalElementTypeName + ";"; + } + const internalTypeName = "[" + internalElementTypeName; + elementTypeName = "[".repeat(numInternalArrays) + elementTypeName; + return { + name: typeName.replace(/\./g, "/"), + type: "pointer", + size: 1, + defaultValue: NULL, + isCompatible(v) { + if (v === null) { + return true; + } + if (typeof v !== "object" || v.length === void 0) { + return false; + } + return v.every(function(element) { + return elementType.isCompatible(element); + }); + }, + fromJni(arr, env, owned) { + if (arr.isNull()) { + return null; + } + const result = []; + const n = env.getArrayLength(arr); + for (let i = 0; i !== n; i++) { + const element = env.getObjectArrayElement(arr, i); + try { + result.push(elementType.fromJni(element, env)); + } finally { + env.deleteLocalRef(element); + } + } + try { + result.$w = factory.cast(arr, factory.use(internalTypeName), owned); + } catch (e) { + factory.use("java.lang.reflect.Array").newInstance(factory.use(elementTypeName).class, 0); + result.$w = factory.cast(arr, factory.use(internalTypeName), owned); + } + result.$dispose = disposeObjectArray; + return result; + }, + toJni(elements, env) { + if (elements === null) { + return NULL; + } + if (!(elements instanceof Array)) { + throw new Error("Expected an array"); + } + const wrapper = elements.$w; + if (wrapper !== void 0) { + return wrapper.$h; + } + const n = elements.length; + const klassObj = factory.use(elementTypeName); + const classHandle = klassObj.$borrowClassHandle(env); + try { + const result = env.newObjectArray(n, classHandle.value, NULL); + env.throwIfExceptionPending(); + for (let i = 0; i !== n; i++) { + const handle = elementType.toJni(elements[i], env); + try { + env.setObjectArrayElement(result, i, handle); + } finally { + if (elementType.type === "pointer" && env.getObjectRefType(handle) === JNILocalRefType) { + env.deleteLocalRef(handle); + } + } + env.throwIfExceptionPending(); + } + return result; + } finally { + classHandle.unref(env); + } + } + }; +} +function disposeObjectArray() { + const n = this.length; + for (let i = 0; i !== n; i++) { + const obj = this[i]; + if (obj === null) { + continue; + } + const dispose = obj.$dispose; + if (dispose === void 0) { + break; + } + dispose.call(obj); + } + this.$w.$dispose(); +} +function fromJniPrimitiveArray(arr, spec, env, owned) { + if (arr.isNull()) { + return null; + } + const type = getPrimitiveType(spec.typeName); + const length = env.getArrayLength(arr); + return new PrimitiveArray(arr, spec, type, length, env, owned); +} +function toJniPrimitiveArray(arr, spec, env) { + if (arr === null) { + return NULL; + } + const handle = arr.$h; + if (handle !== void 0) { + return handle; + } + const length = arr.length; + const type = getPrimitiveType(spec.typeName); + const result = spec.newArray.call(env, length); + if (result.isNull()) { + throw new Error("Unable to construct array"); + } + if (length > 0) { + const elementSize = type.byteSize; + const writeElement = type.write; + const unparseElementValue = type.toJni; + const elements = Memory.alloc(length * type.byteSize); + for (let index = 0; index !== length; index++) { + writeElement(elements.add(index * elementSize), unparseElementValue(arr[index])); + } + spec.setRegion.call(env, result, 0, length, elements); + env.throwIfExceptionPending(); + } + return result; +} +function isCompatiblePrimitiveArray(value, typeName) { + if (value === null) { + return true; + } + if (value instanceof PrimitiveArray) { + return value.$s.typeName === typeName; + } + const isArrayLike = typeof value === "object" && value.length !== void 0; + if (!isArrayLike) { + return false; + } + const elementType = getPrimitiveType(typeName); + return Array.prototype.every.call(value, (element) => elementType.isCompatible(element)); +} +function PrimitiveArray(handle, spec, type, length, env, owned = true) { + if (owned) { + const h = env.newGlobalRef(handle); + this.$h = h; + this.$r = Script.bindWeak(this, env.vm.makeHandleDestructor(h)); + } else { + this.$h = handle; + this.$r = null; + } + this.$s = spec; + this.$t = type; + this.length = length; + return new Proxy(this, primitiveArrayHandler); +} +primitiveArrayHandler = { + has(target, property) { + if (property in target) { + return true; + } + return target.tryParseIndex(property) !== null; + }, + get(target, property, receiver) { + const index = target.tryParseIndex(property); + if (index === null) { + return target[property]; + } + return target.readElement(index); + }, + set(target, property, value, receiver) { + const index = target.tryParseIndex(property); + if (index === null) { + target[property] = value; + return true; + } + target.writeElement(index, value); + return true; + }, + ownKeys(target) { + const keys = []; + const { length } = target; + for (let i = 0; i !== length; i++) { + const key = i.toString(); + keys.push(key); + } + keys.push("length"); + return keys; + }, + getOwnPropertyDescriptor(target, property) { + const index = target.tryParseIndex(property); + if (index !== null) { + return { + writable: true, + configurable: true, + enumerable: true + }; + } + return Object.getOwnPropertyDescriptor(target, property); + } +}; +Object.defineProperties(PrimitiveArray.prototype, { + $dispose: { + enumerable: true, + value() { + const ref = this.$r; + if (ref !== null) { + this.$r = null; + Script.unbindWeak(ref); + } + } + }, + $clone: { + value(env) { + return new PrimitiveArray(this.$h, this.$s, this.$t, this.length, env); + } + }, + tryParseIndex: { + value(rawIndex) { + if (typeof rawIndex === "symbol") { + return null; + } + const index = parseInt(rawIndex); + if (isNaN(index) || index < 0 || index >= this.length) { + return null; + } + return index; + } + }, + readElement: { + value(index) { + return this.withElements((elements) => { + const type = this.$t; + return type.fromJni(type.read(elements.add(index * type.byteSize))); + }); + } + }, + writeElement: { + value(index, value) { + const { $h: handle, $s: spec, $t: type } = this; + const env = vm.getEnv(); + const element = Memory.alloc(type.byteSize); + type.write(element, type.toJni(value)); + spec.setRegion.call(env, handle, index, 1, element); + } + }, + withElements: { + value(perform) { + const { $h: handle, $s: spec } = this; + const env = vm.getEnv(); + const elements = spec.getElements.call(env, handle); + if (elements.isNull()) { + throw new Error("Unable to get array elements"); + } + try { + return perform(elements); + } finally { + spec.releaseElements.call(env, handle, elements); + } + } + }, + toJSON: { + value() { + const { length, $t: type } = this; + const { byteSize: elementSize, fromJni, read: read2 } = type; + return this.withElements((elements) => { + const values = []; + for (let i = 0; i !== length; i++) { + const value = fromJni(read2(elements.add(i * elementSize))); + values.push(value); + } + return values; + }); + } + }, + toString: { + value() { + return this.toJSON().toString(); + } + } +}); +function makeJniObjectTypeName(typeName) { + return "L" + typeName.replace(/\./g, "/") + ";"; +} +function toTitleCase(str) { + return str.charAt(0).toUpperCase() + str.slice(1); +} +function identity(value) { + return value; +} + +// node_modules/frida-java-bridge/lib/class-factory.js +var jsizeSize3 = 4; +var { + ensureClassInitialized: ensureClassInitialized3, + makeMethodMangler: makeMethodMangler3 +} = android_exports; +var kAccStatic2 = 8; +var CONSTRUCTOR_METHOD = 1; +var STATIC_METHOD = 2; +var INSTANCE_METHOD = 3; +var STATIC_FIELD = 1; +var INSTANCE_FIELD = 2; +var STRATEGY_VIRTUAL = 1; +var STRATEGY_DIRECT = 2; +var PENDING_USE = Symbol("PENDING_USE"); +var DEFAULT_CACHE_DIR = "/data/local/tmp"; +var { + getCurrentThreadId, + pointerSize: pointerSize7 +} = Process; +var factoryCache = { + state: "empty", + factories: [], + loaders: null, + Integer: null +}; +var vm2 = null; +var api = null; +var isArtVm = null; +var wrapperHandler = null; +var dispatcherPrototype = null; +var methodPrototype = null; +var valueOfPrototype = null; +var cachedLoaderInvoke = null; +var cachedLoaderMethod = null; +var ignoredThreads = /* @__PURE__ */ new Map(); +var ClassFactory = class _ClassFactory { + static _initialize(_vm, _api) { + vm2 = _vm; + api = _api; + isArtVm = _api.flavor === "art"; + if (_api.flavor === "jvm") { + ensureClassInitialized3 = ensureClassInitialized2; + makeMethodMangler3 = makeMethodMangler2; + } + } + static _disposeAll(env) { + factoryCache.factories.forEach((factory) => { + factory._dispose(env); + }); + } + static get(classLoader) { + const cache = getFactoryCache(); + const defaultFactory = cache.factories[0]; + if (classLoader === null) { + return defaultFactory; + } + const indexObj = cache.loaders.get(classLoader); + if (indexObj !== null) { + const index = defaultFactory.cast(indexObj, cache.Integer); + return cache.factories[index.intValue()]; + } + const factory = new _ClassFactory(); + factory.loader = classLoader; + factory.cacheDir = defaultFactory.cacheDir; + addFactoryToCache(factory, classLoader); + return factory; + } + constructor() { + this.cacheDir = DEFAULT_CACHE_DIR; + this.codeCacheDir = DEFAULT_CACHE_DIR + "/dalvik-cache"; + this.tempFileNaming = { + prefix: "frida", + suffix: "" + }; + this._classes = {}; + this._classHandles = new LRU(10, releaseClassHandle); + this._patchedMethods = /* @__PURE__ */ new Set(); + this._loader = null; + this._types = [{}, {}]; + factoryCache.factories.push(this); + } + _dispose(env) { + Array.from(this._patchedMethods).forEach((method) => { + method.implementation = null; + }); + this._patchedMethods.clear(); + revertGlobalPatches(); + this._classHandles.dispose(env); + this._classes = {}; + } + get loader() { + return this._loader; + } + set loader(value) { + const isInitial = this._loader === null && value !== null; + this._loader = value; + if (isInitial && factoryCache.state === "ready" && this === factoryCache.factories[0]) { + addFactoryToCache(this, value); + } + } + use(className, options = {}) { + const allowCached = options.cache !== "skip"; + let C = allowCached ? this._getUsedClass(className) : void 0; + if (C === void 0) { + try { + const env = vm2.getEnv(); + const { _loader: loader } = this; + const getClassHandle = loader !== null ? makeLoaderClassHandleGetter(className, loader, env) : makeBasicClassHandleGetter(className); + C = this._make(className, getClassHandle, env); + } finally { + if (allowCached) { + this._setUsedClass(className, C); + } + } + } + return C; + } + _getUsedClass(className) { + let c; + while ((c = this._classes[className]) === PENDING_USE) { + Thread.sleep(0.05); + } + if (c === void 0) { + this._classes[className] = PENDING_USE; + } + return c; + } + _setUsedClass(className, c) { + if (c !== void 0) { + this._classes[className] = c; + } else { + delete this._classes[className]; + } + } + _make(name, getClassHandle, env) { + const C = makeClassWrapperConstructor(); + const proto = Object.create(Wrapper.prototype, { + [Symbol.for("n")]: { + value: name + }, + $n: { + get() { + return this[Symbol.for("n")]; + } + }, + [Symbol.for("C")]: { + value: C + }, + $C: { + get() { + return this[Symbol.for("C")]; + } + }, + [Symbol.for("w")]: { + value: null, + writable: true + }, + $w: { + get() { + return this[Symbol.for("w")]; + }, + set(val) { + this[Symbol.for("w")] = val; + } + }, + [Symbol.for("_s")]: { + writable: true + }, + $_s: { + get() { + return this[Symbol.for("_s")]; + }, + set(val) { + this[Symbol.for("_s")] = val; + } + }, + [Symbol.for("c")]: { + value: [null] + }, + $c: { + get() { + return this[Symbol.for("c")]; + } + }, + [Symbol.for("m")]: { + value: /* @__PURE__ */ new Map() + }, + $m: { + get() { + return this[Symbol.for("m")]; + } + }, + [Symbol.for("l")]: { + value: null, + writable: true + }, + $l: { + get() { + return this[Symbol.for("l")]; + }, + set(val) { + this[Symbol.for("l")] = val; + } + }, + [Symbol.for("gch")]: { + value: getClassHandle + }, + $gch: { + get() { + return this[Symbol.for("gch")]; + } + }, + [Symbol.for("f")]: { + value: this + }, + $f: { + get() { + return this[Symbol.for("f")]; + } + } + }); + C.prototype = proto; + const classWrapper = new C(null); + proto[Symbol.for("w")] = classWrapper; + proto.$w = classWrapper; + const h = classWrapper.$borrowClassHandle(env); + try { + const classHandle = h.value; + ensureClassInitialized3(env, classHandle); + proto.$l = Model.build(classHandle, env); + } finally { + h.unref(env); + } + return classWrapper; + } + retain(obj) { + const env = vm2.getEnv(); + return obj.$clone(env); + } + cast(obj, klass, owned) { + const env = vm2.getEnv(); + let handle = obj.$h; + if (handle === void 0) { + handle = obj; + } + const h = klass.$borrowClassHandle(env); + try { + const isValidCast = env.isInstanceOf(handle, h.value); + if (!isValidCast) { + throw new Error(`Cast from '${env.getObjectClassName(handle)}' to '${klass.$n}' isn't possible`); + } + } finally { + h.unref(env); + } + const C = klass.$C; + return new C(handle, STRATEGY_VIRTUAL, env, owned); + } + wrap(handle, klass, env) { + const C = klass.$C; + const wrapper = new C(handle, STRATEGY_VIRTUAL, env, false); + wrapper.$r = Script.bindWeak(wrapper, vm2.makeHandleDestructor(handle)); + return wrapper; + } + array(type, elements) { + const env = vm2.getEnv(); + const primitiveType = getPrimitiveType(type); + if (primitiveType !== null) { + type = primitiveType.name; + } + const arrayType = getArrayType("[" + type, false, this); + const rawArray = arrayType.toJni(elements, env); + return arrayType.fromJni(rawArray, env, true); + } + registerClass(spec) { + const env = vm2.getEnv(); + const tempHandles = []; + try { + const Class = this.use("java.lang.Class"); + const Method = env.javaLangReflectMethod(); + const invokeObjectMethodNoArgs = env.vaMethod("pointer", []); + const className = spec.name; + const interfaces = spec.implements || []; + const superClass = spec.superClass || this.use("java.lang.Object"); + const dexFields = []; + const dexMethods = []; + const dexSpec = { + name: makeJniObjectTypeName(className), + sourceFileName: makeSourceFileName(className), + superClass: makeJniObjectTypeName(superClass.$n), + interfaces: interfaces.map((iface) => makeJniObjectTypeName(iface.$n)), + fields: dexFields, + methods: dexMethods + }; + const allInterfaces = interfaces.slice(); + interfaces.forEach((iface) => { + Array.prototype.slice.call(iface.class.getInterfaces()).forEach((baseIface) => { + const baseIfaceName = this.cast(baseIface, Class).getCanonicalName(); + allInterfaces.push(this.use(baseIfaceName)); + }); + }); + const fields = spec.fields || {}; + Object.getOwnPropertyNames(fields).forEach((name) => { + const fieldType = this._getType(fields[name]); + dexFields.push([name, fieldType.name]); + }); + const baseMethods = {}; + const pendingOverloads = {}; + allInterfaces.forEach((iface) => { + const h = iface.$borrowClassHandle(env); + tempHandles.push(h); + const ifaceHandle = h.value; + iface.$ownMembers.filter((name) => { + return iface[name].overloads !== void 0; + }).forEach((name) => { + const method = iface[name]; + const overloads = method.overloads; + const overloadIds = overloads.map((overload) => makeOverloadId(name, overload.returnType, overload.argumentTypes)); + baseMethods[name] = [method, overloadIds, ifaceHandle]; + overloads.forEach((overload, index) => { + const id = overloadIds[index]; + pendingOverloads[id] = [overload, ifaceHandle]; + }); + }); + }); + const methods = spec.methods || {}; + const methodNames = Object.keys(methods); + const methodEntries = methodNames.reduce((result, name) => { + const entry = methods[name]; + const rawName = name === "$init" ? "" : name; + if (entry instanceof Array) { + result.push(...entry.map((e) => [rawName, e])); + } else { + result.push([rawName, entry]); + } + return result; + }, []); + const implMethods = []; + methodEntries.forEach(([name, methodValue]) => { + let type = INSTANCE_METHOD; + let returnType; + let argumentTypes; + let thrownTypeNames = []; + let impl; + if (typeof methodValue === "function") { + const m = baseMethods[name]; + if (m !== void 0 && Array.isArray(m)) { + const [baseMethod, overloadIds, parentTypeHandle] = m; + if (overloadIds.length > 1) { + throw new Error(`More than one overload matching '${name}': signature must be specified`); + } + delete pendingOverloads[overloadIds[0]]; + const overload = baseMethod.overloads[0]; + type = overload.type; + returnType = overload.returnType; + argumentTypes = overload.argumentTypes; + impl = methodValue; + const reflectedMethod = env.toReflectedMethod(parentTypeHandle, overload.handle, 0); + const thrownTypes = invokeObjectMethodNoArgs(env.handle, reflectedMethod, Method.getGenericExceptionTypes); + thrownTypeNames = readTypeNames(env, thrownTypes).map(makeJniObjectTypeName); + env.deleteLocalRef(thrownTypes); + env.deleteLocalRef(reflectedMethod); + } else { + returnType = this._getType("void"); + argumentTypes = []; + impl = methodValue; + } + } else { + if (methodValue.isStatic) { + type = STATIC_METHOD; + } + returnType = this._getType(methodValue.returnType || "void"); + argumentTypes = (methodValue.argumentTypes || []).map((name2) => this._getType(name2)); + impl = methodValue.implementation; + if (typeof impl !== "function") { + throw new Error("Expected a function implementation for method: " + name); + } + const id = makeOverloadId(name, returnType, argumentTypes); + const pendingOverload = pendingOverloads[id]; + if (pendingOverload !== void 0) { + const [overload, parentTypeHandle] = pendingOverload; + delete pendingOverloads[id]; + type = overload.type; + returnType = overload.returnType; + argumentTypes = overload.argumentTypes; + const reflectedMethod = env.toReflectedMethod(parentTypeHandle, overload.handle, 0); + const thrownTypes = invokeObjectMethodNoArgs(env.handle, reflectedMethod, Method.getGenericExceptionTypes); + thrownTypeNames = readTypeNames(env, thrownTypes).map(makeJniObjectTypeName); + env.deleteLocalRef(thrownTypes); + env.deleteLocalRef(reflectedMethod); + } + } + const returnTypeName = returnType.name; + const argumentTypeNames = argumentTypes.map((t) => t.name); + const signature = "(" + argumentTypeNames.join("") + ")" + returnTypeName; + dexMethods.push([name, returnTypeName, argumentTypeNames, thrownTypeNames, type === STATIC_METHOD ? kAccStatic2 : 0]); + implMethods.push([name, signature, type, returnType, argumentTypes, impl]); + }); + const unimplementedMethodIds = Object.keys(pendingOverloads); + if (unimplementedMethodIds.length > 0) { + throw new Error("Missing implementation for: " + unimplementedMethodIds.join(", ")); + } + const dex = DexFile.fromBuffer(mkdex_default(dexSpec), this); + try { + dex.load(); + } finally { + dex.file.delete(); + } + const classWrapper = this.use(spec.name); + const numMethods = methodEntries.length; + if (numMethods > 0) { + const methodElementSize = 3 * pointerSize7; + const methodElements = Memory.alloc(numMethods * methodElementSize); + const nativeMethods = []; + const temporaryHandles = []; + implMethods.forEach(([name, signature, type, returnType, argumentTypes, impl], index) => { + const rawName = Memory.allocUtf8String(name); + const rawSignature = Memory.allocUtf8String(signature); + const rawImpl = implement(name, classWrapper, type, returnType, argumentTypes, impl); + methodElements.add(index * methodElementSize).writePointer(rawName); + methodElements.add(index * methodElementSize + pointerSize7).writePointer(rawSignature); + methodElements.add(index * methodElementSize + 2 * pointerSize7).writePointer(rawImpl); + temporaryHandles.push(rawName, rawSignature); + nativeMethods.push(rawImpl); + }); + const h = classWrapper.$borrowClassHandle(env); + tempHandles.push(h); + const classHandle = h.value; + env.registerNatives(classHandle, methodElements, numMethods); + env.throwIfExceptionPending(); + classWrapper.$nativeMethods = nativeMethods; + } + return classWrapper; + } finally { + tempHandles.forEach((h) => { + h.unref(env); + }); + } + } + choose(specifier, callbacks) { + const env = vm2.getEnv(); + const { flavor } = api; + if (flavor === "jvm") { + this._chooseObjectsJvm(specifier, env, callbacks); + } else if (flavor === "art") { + const legacyApiMissing = api["art::gc::Heap::VisitObjects"] === void 0; + if (legacyApiMissing) { + const preA12ApiMissing = api["art::gc::Heap::GetInstances"] === void 0; + if (preA12ApiMissing) { + return this._chooseObjectsJvm(specifier, env, callbacks); + } + } + withRunnableArtThread(vm2, env, (thread) => { + if (legacyApiMissing) { + this._chooseObjectsArtPreA12(specifier, env, thread, callbacks); + } else { + this._chooseObjectsArtLegacy(specifier, env, thread, callbacks); + } + }); + } else { + this._chooseObjectsDalvik(specifier, env, callbacks); + } + } + _chooseObjectsJvm(className, env, callbacks) { + const classWrapper = this.use(className); + const { jvmti } = api; + const JVMTI_ITERATION_CONTINUE = 1; + const JVMTI_HEAP_OBJECT_EITHER = 3; + const h = classWrapper.$borrowClassHandle(env); + const tag = int64(h.value.toString()); + try { + const heapObjectCallback = new NativeCallback((classTag, size, tagPtr2, userData) => { + tagPtr2.writeS64(tag); + return JVMTI_ITERATION_CONTINUE; + }, "int", ["int64", "int64", "pointer", "pointer"]); + jvmti.iterateOverInstancesOfClass(h.value, JVMTI_HEAP_OBJECT_EITHER, heapObjectCallback, h.value); + const tagPtr = Memory.alloc(8); + tagPtr.writeS64(tag); + const countPtr = Memory.alloc(jsizeSize3); + const objectsPtr = Memory.alloc(pointerSize7); + jvmti.getObjectsWithTags(1, tagPtr, countPtr, objectsPtr, NULL); + const count = countPtr.readS32(); + const objects = objectsPtr.readPointer(); + const handles = []; + for (let i = 0; i !== count; i++) { + handles.push(objects.add(i * pointerSize7).readPointer()); + } + jvmti.deallocate(objects); + try { + for (const handle of handles) { + const instance = this.cast(handle, classWrapper); + const result = callbacks.onMatch(instance); + if (result === "stop") { + break; + } + } + callbacks.onComplete(); + } finally { + handles.forEach((handle) => { + env.deleteLocalRef(handle); + }); + } + } finally { + h.unref(env); + } + } + _chooseObjectsArtPreA12(className, env, thread, callbacks) { + const classWrapper = this.use(className); + const scope = VariableSizedHandleScope.$new(thread, vm2); + let needle; + const h = classWrapper.$borrowClassHandle(env); + try { + const object = api["art::JavaVMExt::DecodeGlobal"](api.vm, thread, h.value); + needle = scope.newHandle(object); + } finally { + h.unref(env); + } + const maxCount = 0; + const instances = HandleVector.$new(); + api["art::gc::Heap::GetInstances"](api.artHeap, scope, needle, maxCount, instances); + const instanceHandles = instances.handles.map((handle) => env.newGlobalRef(handle)); + instances.$delete(); + scope.$delete(); + try { + for (const handle of instanceHandles) { + const instance = this.cast(handle, classWrapper); + const result = callbacks.onMatch(instance); + if (result === "stop") { + break; + } + } + callbacks.onComplete(); + } finally { + instanceHandles.forEach((handle) => { + env.deleteGlobalRef(handle); + }); + } + } + _chooseObjectsArtLegacy(className, env, thread, callbacks) { + const classWrapper = this.use(className); + const instanceHandles = []; + const addGlobalReference = api["art::JavaVMExt::AddGlobalRef"]; + const vmHandle = api.vm; + let needle; + const h = classWrapper.$borrowClassHandle(env); + try { + needle = api["art::JavaVMExt::DecodeGlobal"](vmHandle, thread, h.value).toInt32(); + } finally { + h.unref(env); + } + const collectMatchingInstanceHandles = makeObjectVisitorPredicate(needle, (object) => { + instanceHandles.push(addGlobalReference(vmHandle, thread, object)); + }); + api["art::gc::Heap::VisitObjects"](api.artHeap, collectMatchingInstanceHandles, NULL); + try { + for (const handle of instanceHandles) { + const instance = this.cast(handle, classWrapper); + const result = callbacks.onMatch(instance); + if (result === "stop") { + break; + } + } + } finally { + instanceHandles.forEach((handle) => { + env.deleteGlobalRef(handle); + }); + } + callbacks.onComplete(); + } + _chooseObjectsDalvik(className, callerEnv, callbacks) { + const classWrapper = this.use(className); + if (api.addLocalReference === null) { + const libdvm = Process.getModuleByName("libdvm.so"); + let pattern; + switch (Process.arch) { + case "arm": + pattern = "2d e9 f0 41 05 46 15 4e 0c 46 7e 44 11 b3 43 68"; + break; + case "ia32": + pattern = "8d 64 24 d4 89 5c 24 1c 89 74 24 20 e8 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 85 d2"; + break; + } + Memory.scan(libdvm.base, libdvm.size, pattern, { + onMatch: (address, size) => { + let wrapper; + if (Process.arch === "arm") { + address = address.or(1); + wrapper = new NativeFunction(address, "pointer", ["pointer", "pointer"]); + } else { + const thunk = Memory.alloc(Process.pageSize); + Memory.patchCode(thunk, 16, (code3) => { + const cw = new X86Writer(code3, { pc: thunk }); + cw.putMovRegRegOffsetPtr("eax", "esp", 4); + cw.putMovRegRegOffsetPtr("edx", "esp", 8); + cw.putJmpAddress(address); + cw.flush(); + }); + wrapper = new NativeFunction(thunk, "pointer", ["pointer", "pointer"]); + wrapper._thunk = thunk; + } + api.addLocalReference = wrapper; + vm2.perform((env) => { + enumerateInstances(this, env); + }); + return "stop"; + }, + onError(reason) { + }, + onComplete() { + if (api.addLocalReference === null) { + callbacks.onComplete(); + } + } + }); + } else { + enumerateInstances(this, callerEnv); + } + function enumerateInstances(factory, env) { + const { DVM_JNI_ENV_OFFSET_SELF: DVM_JNI_ENV_OFFSET_SELF2 } = android_exports; + const thread = env.handle.add(DVM_JNI_ENV_OFFSET_SELF2).readPointer(); + let ptrClassObject; + const h = classWrapper.$borrowClassHandle(env); + try { + ptrClassObject = api.dvmDecodeIndirectRef(thread, h.value); + } finally { + h.unref(env); + } + const pattern = ptrClassObject.toMatchPattern(); + const heapSourceBase = api.dvmHeapSourceGetBase(); + const heapSourceLimit = api.dvmHeapSourceGetLimit(); + const size = heapSourceLimit.sub(heapSourceBase).toInt32(); + Memory.scan(heapSourceBase, size, pattern, { + onMatch: (address, size2) => { + if (api.dvmIsValidObject(address)) { + vm2.perform((env2) => { + const thread2 = env2.handle.add(DVM_JNI_ENV_OFFSET_SELF2).readPointer(); + let instance; + const localReference = api.addLocalReference(thread2, address); + try { + instance = factory.cast(localReference, classWrapper); + } finally { + env2.deleteLocalRef(localReference); + } + const result = callbacks.onMatch(instance); + if (result === "stop") { + return "stop"; + } + }); + } + }, + onError(reason) { + }, + onComplete() { + callbacks.onComplete(); + } + }); + } + } + openClassFile(filePath) { + return new DexFile(filePath, null, this); + } + _getType(typeName, unbox = true) { + return getType(typeName, unbox, this); + } +}; +function makeClassWrapperConstructor() { + return function(handle, strategy, env, owned) { + return Wrapper.call(this, handle, strategy, env, owned); + }; +} +function Wrapper(handle, strategy, env, owned = true) { + if (handle !== null) { + if (owned) { + const h = env.newGlobalRef(handle); + this.$h = h; + this.$r = Script.bindWeak(this, vm2.makeHandleDestructor(h)); + } else { + this.$h = handle; + this.$r = null; + } + } else { + this.$h = null; + this.$r = null; + } + this.$t = strategy; + return new Proxy(this, wrapperHandler); +} +wrapperHandler = { + has(target, property) { + if (property in target) { + return true; + } + return target.$has(property); + }, + get(target, property, receiver) { + if (typeof property !== "string" || property.startsWith("$") || property === "class") { + return target[property]; + } + const unwrap2 = target.$find(property); + if (unwrap2 !== null) { + return unwrap2(receiver); + } + return target[property]; + }, + set(target, property, value, receiver) { + target[property] = value; + return true; + }, + ownKeys(target) { + return target.$list(); + }, + getOwnPropertyDescriptor(target, property) { + if (Object.prototype.hasOwnProperty.call(target, property)) { + return Object.getOwnPropertyDescriptor(target, property); + } + return { + writable: false, + configurable: true, + enumerable: true + }; + } +}; +Object.defineProperties(Wrapper.prototype, { + [Symbol.for("new")]: { + enumerable: false, + get() { + return this.$getCtor("allocAndInit"); + } + }, + $new: { + enumerable: true, + get() { + return this[Symbol.for("new")]; + } + }, + [Symbol.for("alloc")]: { + enumerable: false, + value() { + const env = vm2.getEnv(); + const h = this.$borrowClassHandle(env); + try { + const obj = env.allocObject(h.value); + const factory = this.$f; + return factory.cast(obj, this); + } finally { + h.unref(env); + } + } + }, + $alloc: { + enumerable: true, + get() { + return this[Symbol.for("alloc")]; + } + }, + [Symbol.for("init")]: { + enumerable: false, + get() { + return this.$getCtor("initOnly"); + } + }, + $init: { + enumerable: true, + get() { + return this[Symbol.for("init")]; + } + }, + [Symbol.for("dispose")]: { + enumerable: false, + value() { + const ref = this.$r; + if (ref !== null) { + this.$r = null; + Script.unbindWeak(ref); + } + if (this.$h !== null) { + this.$h = void 0; + } + } + }, + $dispose: { + enumerable: true, + get() { + return this[Symbol.for("dispose")]; + } + }, + [Symbol.for("clone")]: { + enumerable: false, + value(env) { + const C = this.$C; + return new C(this.$h, this.$t, env); + } + }, + $clone: { + value(env) { + return this[Symbol.for("clone")](env); + } + }, + [Symbol.for("class")]: { + enumerable: false, + get() { + const env = vm2.getEnv(); + const h = this.$borrowClassHandle(env); + try { + const factory = this.$f; + return factory.cast(h.value, factory.use("java.lang.Class")); + } finally { + h.unref(env); + } + } + }, + class: { + enumerable: true, + get() { + return this[Symbol.for("class")]; + } + }, + [Symbol.for("className")]: { + enumerable: false, + get() { + const handle = this.$h; + if (handle === null) { + return this.$n; + } + return vm2.getEnv().getObjectClassName(handle); + } + }, + $className: { + enumerable: true, + get() { + return this[Symbol.for("className")]; + } + }, + [Symbol.for("ownMembers")]: { + enumerable: false, + get() { + const model = this.$l; + return model.list(); + } + }, + $ownMembers: { + enumerable: true, + get() { + return this[Symbol.for("ownMembers")]; + } + }, + [Symbol.for("super")]: { + enumerable: false, + get() { + const env = vm2.getEnv(); + const C = this.$s.$C; + return new C(this.$h, STRATEGY_DIRECT, env); + } + }, + $super: { + enumerable: true, + get() { + return this[Symbol.for("super")]; + } + }, + [Symbol.for("s")]: { + enumerable: false, + get() { + const proto = Object.getPrototypeOf(this); + let superWrapper = proto.$_s; + if (superWrapper === void 0) { + const env = vm2.getEnv(); + const h = this.$borrowClassHandle(env); + try { + const superHandle = env.getSuperclass(h.value); + if (!superHandle.isNull()) { + try { + const superClassName = env.getClassName(superHandle); + const factory = proto.$f; + superWrapper = factory._getUsedClass(superClassName); + if (superWrapper === void 0) { + try { + const getSuperClassHandle = makeSuperHandleGetter(this); + superWrapper = factory._make(superClassName, getSuperClassHandle, env); + } finally { + factory._setUsedClass(superClassName, superWrapper); + } + } + } finally { + env.deleteLocalRef(superHandle); + } + } else { + superWrapper = null; + } + } finally { + h.unref(env); + } + proto.$_s = superWrapper; + } + return superWrapper; + } + }, + $s: { + get() { + return this[Symbol.for("s")]; + } + }, + [Symbol.for("isSameObject")]: { + enumerable: false, + value(obj) { + const env = vm2.getEnv(); + return env.isSameObject(obj.$h, this.$h); + } + }, + $isSameObject: { + value(obj) { + return this[Symbol.for("isSameObject")](obj); + } + }, + [Symbol.for("getCtor")]: { + enumerable: false, + value(type) { + const slot = this.$c; + let ctor = slot[0]; + if (ctor === null) { + const env = vm2.getEnv(); + const h = this.$borrowClassHandle(env); + try { + ctor = makeConstructor(h.value, this.$w, env); + slot[0] = ctor; + } finally { + h.unref(env); + } + } + return ctor[type]; + } + }, + $getCtor: { + value(type) { + return this[Symbol.for("getCtor")](type); + } + }, + [Symbol.for("borrowClassHandle")]: { + enumerable: false, + value(env) { + const className = this.$n; + const classHandles = this.$f._classHandles; + let handle = classHandles.get(className); + if (handle === void 0) { + handle = new ClassHandle(this.$gch(env), env); + classHandles.set(className, handle, env); + } + return handle.ref(); + } + }, + $borrowClassHandle: { + value(env) { + return this[Symbol.for("borrowClassHandle")](env); + } + }, + [Symbol.for("copyClassHandle")]: { + enumerable: false, + value(env) { + const h = this.$borrowClassHandle(env); + try { + return env.newLocalRef(h.value); + } finally { + h.unref(env); + } + } + }, + $copyClassHandle: { + value(env) { + return this[Symbol.for("copyClassHandle")](env); + } + }, + [Symbol.for("getHandle")]: { + enumerable: false, + value(env) { + const handle = this.$h; + const isDisposed = handle === void 0; + if (isDisposed) { + throw new Error("Wrapper is disposed; perhaps it was borrowed from a hook instead of calling Java.retain() to make a long-lived wrapper?"); + } + return handle; + } + }, + $getHandle: { + value(env) { + return this[Symbol.for("getHandle")](env); + } + }, + [Symbol.for("list")]: { + enumerable: false, + value() { + const superWrapper = this.$s; + const superMembers = superWrapper !== null ? superWrapper.$list() : []; + const model = this.$l; + return Array.from(new Set(superMembers.concat(model.list()))); + } + }, + $list: { + get() { + return this[Symbol.for("list")]; + } + }, + [Symbol.for("has")]: { + enumerable: false, + value(member) { + const members = this.$m; + if (members.has(member)) { + return true; + } + const model = this.$l; + if (model.has(member)) { + return true; + } + const superWrapper = this.$s; + if (superWrapper !== null && superWrapper.$has(member)) { + return true; + } + return false; + } + }, + $has: { + value(member) { + return this[Symbol.for("has")](member); + } + }, + [Symbol.for("find")]: { + enumerable: false, + value(member) { + const members = this.$m; + let value = members.get(member); + if (value !== void 0) { + return value; + } + const model = this.$l; + const spec = model.find(member); + if (spec !== null) { + const env = vm2.getEnv(); + const h = this.$borrowClassHandle(env); + try { + value = makeMember(member, spec, h.value, this.$w, env); + } finally { + h.unref(env); + } + members.set(member, value); + return value; + } + const superWrapper = this.$s; + if (superWrapper !== null) { + return superWrapper.$find(member); + } + return null; + } + }, + $find: { + value(member) { + return this[Symbol.for("find")](member); + } + }, + [Symbol.for("toJSON")]: { + enumerable: false, + value() { + const wrapperName = this.$n; + const handle = this.$h; + if (handle === null) { + return ``; + } + const actualName = this.$className; + if (wrapperName === actualName) { + return ``; + } + return ``; + } + }, + toJSON: { + get() { + return this[Symbol.for("toJSON")]; + } + } +}); +function ClassHandle(value, env) { + this.value = env.newGlobalRef(value); + env.deleteLocalRef(value); + this.refs = 1; +} +ClassHandle.prototype.ref = function() { + this.refs++; + return this; +}; +ClassHandle.prototype.unref = function(env) { + if (--this.refs === 0) { + env.deleteGlobalRef(this.value); + } +}; +function releaseClassHandle(handle, env) { + handle.unref(env); +} +function makeBasicClassHandleGetter(className) { + const canonicalClassName = className.replace(/\./g, "/"); + return function(env) { + const tid = getCurrentThreadId(); + ignore(tid); + try { + return env.findClass(canonicalClassName); + } finally { + unignore(tid); + } + }; +} +function makeLoaderClassHandleGetter(className, usedLoader, callerEnv) { + if (cachedLoaderMethod === null) { + cachedLoaderInvoke = callerEnv.vaMethod("pointer", ["pointer"]); + cachedLoaderMethod = usedLoader.loadClass.overload("java.lang.String").handle; + } + callerEnv = null; + return function(env) { + const classNameValue = env.newStringUtf(className); + const tid = getCurrentThreadId(); + ignore(tid); + try { + const result = cachedLoaderInvoke(env.handle, usedLoader.$h, cachedLoaderMethod, classNameValue); + env.throwIfExceptionPending(); + return result; + } finally { + unignore(tid); + env.deleteLocalRef(classNameValue); + } + }; +} +function makeSuperHandleGetter(classWrapper) { + return function(env) { + const h = classWrapper.$borrowClassHandle(env); + try { + return env.getSuperclass(h.value); + } finally { + h.unref(env); + } + }; +} +function makeConstructor(classHandle, classWrapper, env) { + const { $n: className, $f: factory } = classWrapper; + const methodName = basename(className); + const Class = env.javaLangClass(); + const Constructor = env.javaLangReflectConstructor(); + const invokeObjectMethodNoArgs = env.vaMethod("pointer", []); + const invokeUInt8MethodNoArgs = env.vaMethod("uint8", []); + const jsCtorMethods = []; + const jsInitMethods = []; + const jsRetType = factory._getType(className, false); + const jsVoidType = factory._getType("void", false); + const constructors = invokeObjectMethodNoArgs(env.handle, classHandle, Class.getDeclaredConstructors); + try { + const n = env.getArrayLength(constructors); + if (n !== 0) { + for (let i = 0; i !== n; i++) { + let methodId, types; + const constructor = env.getObjectArrayElement(constructors, i); + try { + methodId = env.fromReflectedMethod(constructor); + types = invokeObjectMethodNoArgs(env.handle, constructor, Constructor.getGenericParameterTypes); + } finally { + env.deleteLocalRef(constructor); + } + let jsArgTypes; + try { + jsArgTypes = readTypeNames(env, types).map((name) => factory._getType(name)); + } finally { + env.deleteLocalRef(types); + } + jsCtorMethods.push(makeMethod(methodName, classWrapper, CONSTRUCTOR_METHOD, methodId, jsRetType, jsArgTypes, env)); + jsInitMethods.push(makeMethod(methodName, classWrapper, INSTANCE_METHOD, methodId, jsVoidType, jsArgTypes, env)); + } + } else { + const isInterface = invokeUInt8MethodNoArgs(env.handle, classHandle, Class.isInterface); + if (isInterface) { + throw new Error("cannot instantiate an interface"); + } + const defaultClass = env.javaLangObject(); + const defaultConstructor = env.getMethodId(defaultClass, "", "()V"); + jsCtorMethods.push(makeMethod(methodName, classWrapper, CONSTRUCTOR_METHOD, defaultConstructor, jsRetType, [], env)); + jsInitMethods.push(makeMethod(methodName, classWrapper, INSTANCE_METHOD, defaultConstructor, jsVoidType, [], env)); + } + } finally { + env.deleteLocalRef(constructors); + } + if (jsInitMethods.length === 0) { + throw new Error("no supported overloads"); + } + return { + allocAndInit: makeMethodDispatcher(jsCtorMethods), + initOnly: makeMethodDispatcher(jsInitMethods) + }; +} +function makeMember(name, spec, classHandle, classWrapper, env) { + if (spec.startsWith("m")) { + return makeMethodFromSpec(name, spec, classHandle, classWrapper, env); + } + return makeFieldFromSpec(name, spec, classHandle, classWrapper, env); +} +function makeMethodFromSpec(name, spec, classHandle, classWrapper, env) { + const { $f: factory } = classWrapper; + const overloads = spec.split(":").slice(1); + const Method = env.javaLangReflectMethod(); + const invokeObjectMethodNoArgs = env.vaMethod("pointer", []); + const invokeUInt8MethodNoArgs = env.vaMethod("uint8", []); + const methods = overloads.map((params) => { + const type = params[0] === "s" ? STATIC_METHOD : INSTANCE_METHOD; + const methodId = ptr(params.substr(1)); + let jsRetType; + const jsArgTypes = []; + const handle = env.toReflectedMethod(classHandle, methodId, type === STATIC_METHOD ? 1 : 0); + try { + const isVarArgs = !!invokeUInt8MethodNoArgs(env.handle, handle, Method.isVarArgs); + const retType = invokeObjectMethodNoArgs(env.handle, handle, Method.getGenericReturnType); + env.throwIfExceptionPending(); + try { + jsRetType = factory._getType(env.getTypeName(retType)); + } finally { + env.deleteLocalRef(retType); + } + const argTypes = invokeObjectMethodNoArgs(env.handle, handle, Method.getParameterTypes); + try { + const n = env.getArrayLength(argTypes); + for (let i = 0; i !== n; i++) { + const t = env.getObjectArrayElement(argTypes, i); + let argClassName; + try { + argClassName = isVarArgs && i === n - 1 ? env.getArrayTypeName(t) : env.getTypeName(t); + } finally { + env.deleteLocalRef(t); + } + const argType = factory._getType(argClassName); + jsArgTypes.push(argType); + } + } finally { + env.deleteLocalRef(argTypes); + } + } catch (e) { + return null; + } finally { + env.deleteLocalRef(handle); + } + return makeMethod(name, classWrapper, type, methodId, jsRetType, jsArgTypes, env); + }).filter((m) => m !== null); + if (methods.length === 0) { + throw new Error("No supported overloads"); + } + if (name === "valueOf") { + ensureDefaultValueOfImplemented(methods); + } + const result = makeMethodDispatcher(methods); + return function(receiver) { + return result; + }; +} +function makeMethodDispatcher(overloads) { + const m = makeMethodDispatcherCallable(); + Object.setPrototypeOf(m, dispatcherPrototype); + m._o = overloads; + return m; +} +function makeMethodDispatcherCallable() { + const m = function() { + return m.invoke(this, arguments); + }; + return m; +} +dispatcherPrototype = Object.create(Function.prototype, { + overloads: { + enumerable: true, + get() { + return this._o; + } + }, + overload: { + value(...args) { + const overloads = this._o; + const numArgs = args.length; + const signature = args.join(":"); + for (let i = 0; i !== overloads.length; i++) { + const method = overloads[i]; + const { argumentTypes } = method; + if (argumentTypes.length !== numArgs) { + continue; + } + const s = argumentTypes.map((t) => t.className).join(":"); + if (s === signature) { + return method; + } + } + throwOverloadError(this.methodName, this.overloads, "specified argument types do not match any of:"); + } + }, + methodName: { + enumerable: true, + get() { + return this._o[0].methodName; + } + }, + holder: { + enumerable: true, + get() { + return this._o[0].holder; + } + }, + type: { + enumerable: true, + get() { + return this._o[0].type; + } + }, + handle: { + enumerable: true, + get() { + throwIfDispatcherAmbiguous(this); + return this._o[0].handle; + } + }, + implementation: { + enumerable: true, + get() { + throwIfDispatcherAmbiguous(this); + return this._o[0].implementation; + }, + set(fn) { + throwIfDispatcherAmbiguous(this); + this._o[0].implementation = fn; + } + }, + returnType: { + enumerable: true, + get() { + throwIfDispatcherAmbiguous(this); + return this._o[0].returnType; + } + }, + argumentTypes: { + enumerable: true, + get() { + throwIfDispatcherAmbiguous(this); + return this._o[0].argumentTypes; + } + }, + canInvokeWith: { + enumerable: true, + get(args) { + throwIfDispatcherAmbiguous(this); + return this._o[0].canInvokeWith; + } + }, + clone: { + enumerable: true, + value(options) { + throwIfDispatcherAmbiguous(this); + return this._o[0].clone(options); + } + }, + invoke: { + value(receiver, args) { + const overloads = this._o; + const isInstance = receiver.$h !== null; + for (let i = 0; i !== overloads.length; i++) { + const method = overloads[i]; + if (!method.canInvokeWith(args)) { + continue; + } + if (method.type === INSTANCE_METHOD && !isInstance) { + const name = this.methodName; + if (name === "toString") { + return ``; + } + throw new Error(name + ": cannot call instance method without an instance"); + } + return method.apply(receiver, args); + } + if (this.methodName === "toString") { + return ``; + } + throwOverloadError(this.methodName, this.overloads, "argument types do not match any of:"); + } + } +}); +function makeOverloadId(name, returnType, argumentTypes) { + return `${returnType.className} ${name}(${argumentTypes.map((t) => t.className).join(", ")})`; +} +function throwIfDispatcherAmbiguous(dispatcher) { + const methods = dispatcher._o; + if (methods.length > 1) { + throwOverloadError(methods[0].methodName, methods, "has more than one overload, use .overload() to choose from:"); + } +} +function throwOverloadError(name, methods, message) { + const methodsSortedByArity = methods.slice().sort((a, b) => a.argumentTypes.length - b.argumentTypes.length); + const overloads = methodsSortedByArity.map((m) => { + const argTypes = m.argumentTypes; + if (argTypes.length > 0) { + return ".overload('" + m.argumentTypes.map((t) => t.className).join("', '") + "')"; + } else { + return ".overload()"; + } + }); + throw new Error(`${name}(): ${message} + ${overloads.join("\n ")}`); +} +function makeMethod(methodName, classWrapper, type, methodId, retType, argTypes, env, invocationOptions) { + const rawRetType = retType.type; + const rawArgTypes = argTypes.map((t) => t.type); + if (env === null) { + env = vm2.getEnv(); + } + let callVirtually, callDirectly; + if (type === INSTANCE_METHOD) { + callVirtually = env.vaMethod(rawRetType, rawArgTypes, invocationOptions); + callDirectly = env.nonvirtualVaMethod(rawRetType, rawArgTypes, invocationOptions); + } else if (type === STATIC_METHOD) { + callVirtually = env.staticVaMethod(rawRetType, rawArgTypes, invocationOptions); + callDirectly = callVirtually; + } else { + callVirtually = env.constructor(rawArgTypes, invocationOptions); + callDirectly = callVirtually; + } + return makeMethodInstance([methodName, classWrapper, type, methodId, retType, argTypes, callVirtually, callDirectly]); +} +function makeMethodInstance(params) { + const m = makeMethodCallable(); + Object.setPrototypeOf(m, methodPrototype); + m._p = params; + return m; +} +function makeMethodCallable() { + const m = function() { + return m.invoke(this, arguments); + }; + return m; +} +methodPrototype = Object.create(Function.prototype, { + methodName: { + enumerable: true, + get() { + return this._p[0]; + } + }, + holder: { + enumerable: true, + get() { + return this._p[1]; + } + }, + type: { + enumerable: true, + get() { + return this._p[2]; + } + }, + handle: { + enumerable: true, + get() { + return this._p[3]; + } + }, + implementation: { + enumerable: true, + get() { + const replacement = this._r; + return replacement !== void 0 ? replacement : null; + }, + set(fn) { + const params = this._p; + const holder = params[1]; + const type = params[2]; + if (type === CONSTRUCTOR_METHOD) { + throw new Error("Reimplementing $new is not possible; replace implementation of $init instead"); + } + const existingReplacement = this._r; + if (existingReplacement !== void 0) { + holder.$f._patchedMethods.delete(this); + const mangler = existingReplacement._m; + mangler.revert(vm2); + this._r = void 0; + } + if (fn !== null) { + const [methodName, classWrapper, type2, methodId, retType, argTypes] = params; + const replacement = implement(methodName, classWrapper, type2, retType, argTypes, fn, this); + const mangler = makeMethodMangler3(methodId); + replacement._m = mangler; + this._r = replacement; + mangler.replace(replacement, type2 === INSTANCE_METHOD, argTypes, vm2, api); + holder.$f._patchedMethods.add(this); + } + } + }, + returnType: { + enumerable: true, + get() { + return this._p[4]; + } + }, + argumentTypes: { + enumerable: true, + get() { + return this._p[5]; + } + }, + canInvokeWith: { + enumerable: true, + value(args) { + const argTypes = this._p[5]; + if (args.length !== argTypes.length) { + return false; + } + return argTypes.every((t, i) => { + return t.isCompatible(args[i]); + }); + } + }, + clone: { + enumerable: true, + value(options) { + const params = this._p.slice(0, 6); + return makeMethod(...params, null, options); + } + }, + invoke: { + value(receiver, args) { + const env = vm2.getEnv(); + const params = this._p; + const type = params[2]; + const retType = params[4]; + const argTypes = params[5]; + const replacement = this._r; + const isInstanceMethod = type === INSTANCE_METHOD; + const numArgs = args.length; + const frameCapacity = 2 + numArgs; + env.pushLocalFrame(frameCapacity); + let borrowedHandle = null; + try { + let jniThis; + if (isInstanceMethod) { + jniThis = receiver.$getHandle(); + } else { + borrowedHandle = receiver.$borrowClassHandle(env); + jniThis = borrowedHandle.value; + } + let methodId; + let strategy = receiver.$t; + if (replacement === void 0) { + methodId = params[3]; + } else { + const mangler = replacement._m; + methodId = mangler.resolveTarget(receiver, isInstanceMethod, env, api); + if (isArtVm) { + const pendingCalls = replacement._c; + if (pendingCalls.has(getCurrentThreadId())) { + strategy = STRATEGY_DIRECT; + } + } + } + const jniArgs = [ + env.handle, + jniThis, + methodId + ]; + for (let i = 0; i !== numArgs; i++) { + jniArgs.push(argTypes[i].toJni(args[i], env)); + } + let jniCall; + if (strategy === STRATEGY_VIRTUAL) { + jniCall = params[6]; + } else { + jniCall = params[7]; + if (isInstanceMethod) { + jniArgs.splice(2, 0, receiver.$copyClassHandle(env)); + } + } + const jniRetval = jniCall.apply(null, jniArgs); + env.throwIfExceptionPending(); + return retType.fromJni(jniRetval, env, true); + } finally { + if (borrowedHandle !== null) { + borrowedHandle.unref(env); + } + env.popLocalFrame(NULL); + } + } + }, + toString: { + enumerable: true, + value() { + return `function ${this.methodName}(${this.argumentTypes.map((t) => t.className).join(", ")}): ${this.returnType.className}`; + } + } +}); +function implement(methodName, classWrapper, type, retType, argTypes, handler, fallback = null) { + const pendingCalls = /* @__PURE__ */ new Set(); + const f = makeMethodImplementation([methodName, classWrapper, type, retType, argTypes, handler, fallback, pendingCalls]); + const impl = new NativeCallback(f, retType.type, ["pointer", "pointer"].concat(argTypes.map((t) => t.type))); + impl._c = pendingCalls; + return impl; +} +function makeMethodImplementation(params) { + return function() { + return handleMethodInvocation(arguments, params); + }; +} +function handleMethodInvocation(jniArgs, params) { + const env = new Env(jniArgs[0], vm2); + const [methodName, classWrapper, type, retType, argTypes, handler, fallback, pendingCalls] = params; + const ownedObjects = []; + let self; + if (type === INSTANCE_METHOD) { + const C = classWrapper.$C; + self = new C(jniArgs[1], STRATEGY_VIRTUAL, env, false); + } else { + self = classWrapper; + } + const tid = getCurrentThreadId(); + env.pushLocalFrame(3); + let haveFrame = true; + vm2.link(tid, env); + try { + pendingCalls.add(tid); + let fn; + if (fallback === null || !ignoredThreads.has(tid)) { + fn = handler; + } else { + fn = fallback; + } + const args = []; + const numArgs = jniArgs.length - 2; + for (let i = 0; i !== numArgs; i++) { + const t = argTypes[i]; + const value = t.fromJni(jniArgs[2 + i], env, false); + args.push(value); + ownedObjects.push(value); + } + const retval = fn.apply(self, args); + if (!retType.isCompatible(retval)) { + throw new Error(`Implementation for ${methodName} expected return value compatible with ${retType.className}`); + } + let jniRetval = retType.toJni(retval, env); + if (retType.type === "pointer") { + jniRetval = env.popLocalFrame(jniRetval); + haveFrame = false; + ownedObjects.push(retval); + } + return jniRetval; + } catch (e) { + const jniException = e.$h; + if (jniException !== void 0) { + env.throw(jniException); + } else { + Script.nextTick(() => { + throw e; + }); + } + return retType.defaultValue; + } finally { + vm2.unlink(tid); + if (haveFrame) { + env.popLocalFrame(NULL); + } + pendingCalls.delete(tid); + ownedObjects.forEach((obj) => { + if (obj === null) { + return; + } + const dispose = obj.$dispose; + if (dispose !== void 0) { + dispose.call(obj); + } + }); + } +} +function ensureDefaultValueOfImplemented(methods) { + const { holder, type } = methods[0]; + const hasDefaultValueOf = methods.some((m) => m.type === type && m.argumentTypes.length === 0); + if (hasDefaultValueOf) { + return; + } + methods.push(makeValueOfMethod([holder, type])); +} +function makeValueOfMethod(params) { + const m = makeValueOfCallable(); + Object.setPrototypeOf(m, valueOfPrototype); + m._p = params; + return m; +} +function makeValueOfCallable() { + const m = function() { + return this; + }; + return m; +} +valueOfPrototype = Object.create(Function.prototype, { + methodName: { + enumerable: true, + get() { + return "valueOf"; + } + }, + holder: { + enumerable: true, + get() { + return this._p[0]; + } + }, + type: { + enumerable: true, + get() { + return this._p[1]; + } + }, + handle: { + enumerable: true, + get() { + return NULL; + } + }, + implementation: { + enumerable: true, + get() { + return null; + }, + set(fn) { + } + }, + returnType: { + enumerable: true, + get() { + const classWrapper = this.holder; + return classWrapper.$f.use(classWrapper.$n); + } + }, + argumentTypes: { + enumerable: true, + get() { + return []; + } + }, + canInvokeWith: { + enumerable: true, + value(args) { + return args.length === 0; + } + }, + clone: { + enumerable: true, + value(options) { + throw new Error("Invalid operation"); + } + } +}); +function makeFieldFromSpec(name, spec, classHandle, classWrapper, env) { + const type = spec[2] === "s" ? STATIC_FIELD : INSTANCE_FIELD; + const id = ptr(spec.substr(3)); + const { $f: factory } = classWrapper; + let fieldType; + const field = env.toReflectedField(classHandle, id, type === STATIC_FIELD ? 1 : 0); + try { + fieldType = env.vaMethod("pointer", [])(env.handle, field, env.javaLangReflectField().getGenericType); + env.throwIfExceptionPending(); + } finally { + env.deleteLocalRef(field); + } + let rtype; + try { + rtype = factory._getType(env.getTypeName(fieldType)); + } finally { + env.deleteLocalRef(fieldType); + } + let getValue, setValue; + const rtypeJni = rtype.type; + if (type === STATIC_FIELD) { + getValue = env.getStaticField(rtypeJni); + setValue = env.setStaticField(rtypeJni); + } else { + getValue = env.getField(rtypeJni); + setValue = env.setField(rtypeJni); + } + return makeFieldFromParams([type, rtype, id, getValue, setValue]); +} +function makeFieldFromParams(params) { + return function(receiver) { + return new Field([receiver].concat(params)); + }; +} +function Field(params) { + this._p = params; +} +Object.defineProperties(Field.prototype, { + value: { + enumerable: true, + get() { + const [holder, type, rtype, id, getValue] = this._p; + const env = vm2.getEnv(); + env.pushLocalFrame(4); + let borrowedHandle = null; + try { + let jniThis; + if (type === INSTANCE_FIELD) { + jniThis = holder.$getHandle(); + if (jniThis === null) { + throw new Error("Cannot access an instance field without an instance"); + } + } else { + borrowedHandle = holder.$borrowClassHandle(env); + jniThis = borrowedHandle.value; + } + const jniRetval = getValue(env.handle, jniThis, id); + env.throwIfExceptionPending(); + return rtype.fromJni(jniRetval, env, true); + } finally { + if (borrowedHandle !== null) { + borrowedHandle.unref(env); + } + env.popLocalFrame(NULL); + } + }, + set(value) { + const [holder, type, rtype, id, , setValue] = this._p; + const env = vm2.getEnv(); + env.pushLocalFrame(4); + let borrowedHandle = null; + try { + let jniThis; + if (type === INSTANCE_FIELD) { + jniThis = holder.$getHandle(); + if (jniThis === null) { + throw new Error("Cannot access an instance field without an instance"); + } + } else { + borrowedHandle = holder.$borrowClassHandle(env); + jniThis = borrowedHandle.value; + } + if (!rtype.isCompatible(value)) { + throw new Error(`Expected value compatible with ${rtype.className}`); + } + const jniValue = rtype.toJni(value, env); + setValue(env.handle, jniThis, id, jniValue); + env.throwIfExceptionPending(); + } finally { + if (borrowedHandle !== null) { + borrowedHandle.unref(env); + } + env.popLocalFrame(NULL); + } + } + }, + holder: { + enumerable: true, + get() { + return this._p[0]; + } + }, + fieldType: { + enumerable: true, + get() { + return this._p[1]; + } + }, + fieldReturnType: { + enumerable: true, + get() { + return this._p[2]; + } + }, + toString: { + enumerable: true, + value() { + const inlineString = `Java.Field{holder: ${this.holder}, fieldType: ${this.fieldType}, fieldReturnType: ${this.fieldReturnType}, value: ${this.value}}`; + if (inlineString.length < 200) { + return inlineString; + } + const multilineString = `Java.Field{ + holder: ${this.holder}, + fieldType: ${this.fieldType}, + fieldReturnType: ${this.fieldReturnType}, + value: ${this.value}, +}`; + return multilineString.split("\n").map((l) => l.length > 200 ? l.slice(0, l.indexOf(" ") + 1) + "...," : l).join("\n"); + } + } +}); +var DexFile = class _DexFile { + static fromBuffer(buffer, factory) { + const fileValue = createTemporaryDex(factory); + const filePath = fileValue.getCanonicalPath().toString(); + const file = new File(filePath, "w"); + file.write(buffer.buffer); + file.close(); + setReadOnlyDex(filePath, factory); + return new _DexFile(filePath, fileValue, factory); + } + constructor(path, file, factory) { + this.path = path; + this.file = file; + this._factory = factory; + } + load() { + const { _factory: factory } = this; + const { codeCacheDir } = factory; + const DexClassLoader = factory.use("dalvik.system.DexClassLoader"); + const JFile = factory.use("java.io.File"); + let file = this.file; + if (file === null) { + file = factory.use("java.io.File").$new(this.path); + } + if (!file.exists()) { + throw new Error("File not found"); + } + JFile.$new(codeCacheDir).mkdirs(); + factory.loader = DexClassLoader.$new(file.getCanonicalPath(), codeCacheDir, null, factory.loader); + vm2.preventDetachDueToClassLoader(); + } + getClassNames() { + const { _factory: factory } = this; + const DexFile2 = factory.use("dalvik.system.DexFile"); + const optimizedDex = createTemporaryDex(factory); + const dx = DexFile2.loadDex(this.path, optimizedDex.getCanonicalPath(), 0); + const classNames = []; + const enumeratorClassNames = dx.entries(); + while (enumeratorClassNames.hasMoreElements()) { + classNames.push(enumeratorClassNames.nextElement().toString()); + } + return classNames; + } +}; +function createTemporaryDex(factory) { + const { cacheDir, tempFileNaming } = factory; + const JFile = factory.use("java.io.File"); + const cacheDirValue = JFile.$new(cacheDir); + cacheDirValue.mkdirs(); + return JFile.createTempFile(tempFileNaming.prefix, tempFileNaming.suffix + ".dex", cacheDirValue); +} +function setReadOnlyDex(filePath, factory) { + const JFile = factory.use("java.io.File"); + const file = JFile.$new(filePath); + file.setWritable(false, false); +} +function getFactoryCache() { + switch (factoryCache.state) { + case "empty": { + factoryCache.state = "pending"; + const defaultFactory = factoryCache.factories[0]; + const HashMap = defaultFactory.use("java.util.HashMap"); + const Integer = defaultFactory.use("java.lang.Integer"); + factoryCache.loaders = HashMap.$new(); + factoryCache.Integer = Integer; + const loader = defaultFactory.loader; + if (loader !== null) { + addFactoryToCache(defaultFactory, loader); + } + factoryCache.state = "ready"; + return factoryCache; + } + case "pending": + do { + Thread.sleep(0.05); + } while (factoryCache.state === "pending"); + return factoryCache; + case "ready": + return factoryCache; + } +} +function addFactoryToCache(factory, loader) { + const { factories, loaders, Integer } = factoryCache; + const index = Integer.$new(factories.indexOf(factory)); + loaders.put(loader, index); + for (let l = loader.getParent(); l !== null; l = l.getParent()) { + if (loaders.containsKey(l)) { + break; + } + loaders.put(l, index); + } +} +function ignore(threadId) { + let count = ignoredThreads.get(threadId); + if (count === void 0) { + count = 0; + } + count++; + ignoredThreads.set(threadId, count); +} +function unignore(threadId) { + let count = ignoredThreads.get(threadId); + if (count === void 0) { + throw new Error(`Thread ${threadId} is not ignored`); + } + count--; + if (count === 0) { + ignoredThreads.delete(threadId); + } else { + ignoredThreads.set(threadId, count); + } +} +function basename(className) { + return className.slice(className.lastIndexOf(".") + 1); +} +function readTypeNames(env, types) { + const names = []; + const n = env.getArrayLength(types); + for (let i = 0; i !== n; i++) { + const t = env.getObjectArrayElement(types, i); + try { + names.push(env.getTypeName(t)); + } finally { + env.deleteLocalRef(t); + } + } + return names; +} +function makeSourceFileName(className) { + const tokens = className.split("."); + return tokens[tokens.length - 1] + ".java"; +} + +// node_modules/frida-java-bridge/index.js +var jsizeSize4 = 4; +var pointerSize8 = Process.pointerSize; +var Runtime = class { + ACC_PUBLIC = 1; + ACC_PRIVATE = 2; + ACC_PROTECTED = 4; + ACC_STATIC = 8; + ACC_FINAL = 16; + ACC_SYNCHRONIZED = 32; + ACC_BRIDGE = 64; + ACC_VARARGS = 128; + ACC_NATIVE = 256; + ACC_ABSTRACT = 1024; + ACC_STRICT = 2048; + ACC_SYNTHETIC = 4096; + constructor() { + this.classFactory = null; + this.ClassFactory = ClassFactory; + this.vm = null; + this.api = null; + this._initialized = false; + this._apiError = null; + this._wakeupHandler = null; + this._pollListener = null; + this._pendingMainOps = []; + this._pendingVmOps = []; + this._cachedIsAppProcess = null; + try { + this._tryInitialize(); + } catch (e) { + } + } + _tryInitialize() { + if (this._initialized) { + return true; + } + if (this._apiError !== null) { + throw this._apiError; + } + let api2; + try { + api2 = api_default(); + this.api = api2; + } catch (e) { + this._apiError = e; + throw e; + } + if (api2 === null) { + return false; + } + const vm3 = new VM(api2); + this.vm = vm3; + initialize(vm3); + ClassFactory._initialize(vm3, api2); + this.classFactory = new ClassFactory(); + this._initialized = true; + return true; + } + _dispose() { + if (this.api === null) { + return; + } + const { vm: vm3 } = this; + vm3.perform((env) => { + ClassFactory._disposeAll(env); + Env.dispose(env); + }); + Script.nextTick(() => { + VM.dispose(vm3); + }); + } + get available() { + return this._tryInitialize(); + } + get androidVersion() { + return getAndroidVersion(); + } + synchronized(obj, fn) { + const { $h: objHandle = obj } = obj; + if (!(objHandle instanceof NativePointer)) { + throw new Error("Java.synchronized: the first argument `obj` must be either a pointer or a Java instance"); + } + const env = this.vm.getEnv(); + checkJniResult("VM::MonitorEnter", env.monitorEnter(objHandle)); + try { + fn(); + } finally { + env.monitorExit(objHandle); + } + } + enumerateLoadedClasses(callbacks) { + this._checkAvailable(); + const { flavor } = this.api; + if (flavor === "jvm") { + this._enumerateLoadedClassesJvm(callbacks); + } else if (flavor === "art") { + this._enumerateLoadedClassesArt(callbacks); + } else { + this._enumerateLoadedClassesDalvik(callbacks); + } + } + enumerateLoadedClassesSync() { + const classes = []; + this.enumerateLoadedClasses({ + onMatch(c) { + classes.push(c); + }, + onComplete() { + } + }); + return classes; + } + enumerateClassLoaders(callbacks) { + this._checkAvailable(); + const { flavor } = this.api; + if (flavor === "jvm") { + this._enumerateClassLoadersJvm(callbacks); + } else if (flavor === "art") { + this._enumerateClassLoadersArt(callbacks); + } else { + throw new Error("Enumerating class loaders is not supported on Dalvik"); + } + } + enumerateClassLoadersSync() { + const loaders = []; + this.enumerateClassLoaders({ + onMatch(c) { + loaders.push(c); + }, + onComplete() { + } + }); + return loaders; + } + _enumerateLoadedClassesJvm(callbacks) { + const { api: api2, vm: vm3 } = this; + const { jvmti } = api2; + const env = vm3.getEnv(); + const countPtr = Memory.alloc(jsizeSize4); + const classesPtr = Memory.alloc(pointerSize8); + jvmti.getLoadedClasses(countPtr, classesPtr); + const count = countPtr.readS32(); + const classes = classesPtr.readPointer(); + const handles = []; + for (let i = 0; i !== count; i++) { + handles.push(classes.add(i * pointerSize8).readPointer()); + } + jvmti.deallocate(classes); + try { + for (const handle of handles) { + const className = env.getClassName(handle); + callbacks.onMatch(className, handle); + } + callbacks.onComplete(); + } finally { + handles.forEach((handle) => { + env.deleteLocalRef(handle); + }); + } + } + _enumerateClassLoadersJvm(callbacks) { + this.choose("java.lang.ClassLoader", callbacks); + } + _enumerateLoadedClassesArt(callbacks) { + const { vm: vm3, api: api2 } = this; + const env = vm3.getEnv(); + const addGlobalReference = api2["art::JavaVMExt::AddGlobalRef"]; + const { vm: vmHandle } = api2; + withRunnableArtThread(vm3, env, (thread) => { + const collectClassHandles = makeArtClassVisitor((klass) => { + const handle = addGlobalReference(vmHandle, thread, klass); + try { + const className = env.getClassName(handle); + callbacks.onMatch(className, handle); + } finally { + env.deleteGlobalRef(handle); + } + return true; + }); + api2["art::ClassLinker::VisitClasses"](api2.artClassLinker.address, collectClassHandles); + }); + callbacks.onComplete(); + } + _enumerateClassLoadersArt(callbacks) { + const { classFactory: factory, vm: vm3, api: api2 } = this; + const env = vm3.getEnv(); + const visitClassLoaders = api2["art::ClassLinker::VisitClassLoaders"]; + if (visitClassLoaders === void 0) { + throw new Error("This API is only available on Android >= 7.0"); + } + const ClassLoader = factory.use("java.lang.ClassLoader"); + const loaderHandles = []; + const addGlobalReference = api2["art::JavaVMExt::AddGlobalRef"]; + const { vm: vmHandle } = api2; + withRunnableArtThread(vm3, env, (thread) => { + const collectLoaderHandles = makeArtClassLoaderVisitor((loader) => { + loaderHandles.push(addGlobalReference(vmHandle, thread, loader)); + return true; + }); + withAllArtThreadsSuspended(() => { + visitClassLoaders(api2.artClassLinker.address, collectLoaderHandles); + }); + }); + try { + loaderHandles.forEach((handle) => { + const loader = factory.cast(handle, ClassLoader); + callbacks.onMatch(loader); + }); + } finally { + loaderHandles.forEach((handle) => { + env.deleteGlobalRef(handle); + }); + } + callbacks.onComplete(); + } + _enumerateLoadedClassesDalvik(callbacks) { + const { api: api2 } = this; + const HASH_TOMBSTONE = ptr("0xcbcacccd"); + const loadedClassesOffset = 172; + const hashEntrySize = 8; + const ptrLoadedClassesHashtable = api2.gDvm.add(loadedClassesOffset); + const hashTable = ptrLoadedClassesHashtable.readPointer(); + const tableSize = hashTable.readS32(); + const ptrpEntries = hashTable.add(12); + const pEntries = ptrpEntries.readPointer(); + const end = tableSize * hashEntrySize; + for (let offset = 0; offset < end; offset += hashEntrySize) { + const pEntryPtr = pEntries.add(offset); + const dataPtr = pEntryPtr.add(4).readPointer(); + if (dataPtr.isNull() || dataPtr.equals(HASH_TOMBSTONE)) { + continue; + } + const descriptionPtr = dataPtr.add(24).readPointer(); + const description = descriptionPtr.readUtf8String(); + if (description.startsWith("L")) { + const name = description.substring(1, description.length - 1).replace(/\//g, "."); + callbacks.onMatch(name); + } + } + callbacks.onComplete(); + } + enumerateMethods(query) { + const { classFactory: factory } = this; + const env = this.vm.getEnv(); + const ClassLoader = factory.use("java.lang.ClassLoader"); + return Model.enumerateMethods(query, this.api, env).map((group) => { + const handle = group.loader; + group.loader = handle !== null ? factory.wrap(handle, ClassLoader, env) : null; + return group; + }); + } + scheduleOnMainThread(fn) { + this.performNow(() => { + this._pendingMainOps.push(fn); + let { _wakeupHandler: wakeupHandler } = this; + if (wakeupHandler === null) { + const { classFactory: factory } = this; + const Handler = factory.use("android.os.Handler"); + const Looper = factory.use("android.os.Looper"); + wakeupHandler = Handler.$new(Looper.getMainLooper()); + this._wakeupHandler = wakeupHandler; + } + if (this._pollListener === null) { + this._pollListener = Interceptor.attach(Process.getModuleByName("libc.so").getExportByName("epoll_wait"), this._makePollHook()); + Interceptor.flush(); + } + wakeupHandler.sendEmptyMessage(1); + }); + } + _makePollHook() { + const mainThreadId = Process.id; + const { _pendingMainOps: pending } = this; + return function() { + if (this.threadId !== mainThreadId) { + return; + } + let fn; + while ((fn = pending.shift()) !== void 0) { + try { + fn(); + } catch (e) { + Script.nextTick(() => { + throw e; + }); + } + } + }; + } + perform(fn) { + this._checkAvailable(); + if (!this._isAppProcess() || this.classFactory.loader !== null) { + try { + this.vm.perform(fn); + } catch (e) { + Script.nextTick(() => { + throw e; + }); + } + } else { + this._pendingVmOps.push(fn); + if (this._pendingVmOps.length === 1) { + this._performPendingVmOpsWhenReady(); + } + } + } + performNow(fn) { + this._checkAvailable(); + return this.vm.perform(() => { + const { classFactory: factory } = this; + if (this._isAppProcess() && factory.loader === null) { + const ActivityThread = factory.use("android.app.ActivityThread"); + const app = ActivityThread.currentApplication(); + if (app !== null) { + initFactoryFromApplication(factory, app); + } + } + return fn(); + }); + } + _performPendingVmOpsWhenReady() { + this.vm.perform(() => { + const { classFactory: factory } = this; + const ActivityThread = factory.use("android.app.ActivityThread"); + const app = ActivityThread.currentApplication(); + if (app !== null) { + initFactoryFromApplication(factory, app); + this._performPendingVmOps(); + return; + } + const runtime2 = this; + let initialized = false; + let hookpoint = "early"; + const handleBindApplication = ActivityThread.handleBindApplication; + handleBindApplication.implementation = function(data) { + if (data.instrumentationName.value !== null) { + hookpoint = "late"; + const LoadedApk = factory.use("android.app.LoadedApk"); + const makeApplication = LoadedApk.makeApplication; + makeApplication.implementation = function(forceDefaultAppClass, instrumentation) { + if (!initialized) { + initialized = true; + initFactoryFromLoadedApk(factory, this); + runtime2._performPendingVmOps(); + } + return makeApplication.apply(this, arguments); + }; + } + handleBindApplication.apply(this, arguments); + }; + const getPackageInfoCandidates = ActivityThread.getPackageInfo.overloads.map((m) => [m.argumentTypes.length, m]).sort(([arityA], [arityB]) => arityB - arityA).map(([_, method]) => method); + const getPackageInfo = getPackageInfoCandidates[0]; + getPackageInfo.implementation = function(...args) { + const apk = getPackageInfo.call(this, ...args); + if (!initialized && hookpoint === "early") { + initialized = true; + initFactoryFromLoadedApk(factory, apk); + runtime2._performPendingVmOps(); + } + return apk; + }; + }); + } + _performPendingVmOps() { + const { vm: vm3, _pendingVmOps: pending } = this; + let fn; + while ((fn = pending.shift()) !== void 0) { + try { + vm3.perform(fn); + } catch (e) { + Script.nextTick(() => { + throw e; + }); + } + } + } + use(className, options) { + return this.classFactory.use(className, options); + } + openClassFile(filePath) { + return this.classFactory.openClassFile(filePath); + } + choose(specifier, callbacks) { + this.classFactory.choose(specifier, callbacks); + } + retain(obj) { + return this.classFactory.retain(obj); + } + cast(obj, C) { + return this.classFactory.cast(obj, C); + } + array(type, elements) { + return this.classFactory.array(type, elements); + } + backtrace(options) { + return backtrace(this.vm, options); + } + // Reference: http://stackoverflow.com/questions/2848575/how-to-detect-ui-thread-on-android + isMainThread() { + const Looper = this.classFactory.use("android.os.Looper"); + const mainLooper = Looper.getMainLooper(); + const myLooper = Looper.myLooper(); + if (myLooper === null) { + return false; + } + return mainLooper.$isSameObject(myLooper); + } + registerClass(spec) { + return this.classFactory.registerClass(spec); + } + deoptimizeEverything() { + const { vm: vm3 } = this; + return deoptimizeEverything(vm3, vm3.getEnv()); + } + deoptimizeBootImage() { + const { vm: vm3 } = this; + return deoptimizeBootImage(vm3, vm3.getEnv()); + } + deoptimizeMethod(method) { + const { vm: vm3 } = this; + return deoptimizeMethod(vm3, vm3.getEnv(), method); + } + _checkAvailable() { + if (!this.available) { + throw new Error("Java API not available"); + } + } + _isAppProcess() { + let result = this._cachedIsAppProcess; + if (result === null) { + if (this.api.flavor === "jvm") { + result = false; + this._cachedIsAppProcess = result; + return result; + } + const readlink = new NativeFunction(Module.getGlobalExportByName("readlink"), "pointer", ["pointer", "pointer", "pointer"], { + exceptions: "propagate" + }); + const pathname = Memory.allocUtf8String("/proc/self/exe"); + const bufferSize = 1024; + const buffer = Memory.alloc(bufferSize); + const size = readlink(pathname, buffer, ptr(bufferSize)).toInt32(); + if (size !== -1) { + const exe = buffer.readUtf8String(size); + result = /^\/system\/bin\/app_process/.test(exe); + } else { + result = true; + } + this._cachedIsAppProcess = result; + } + return result; + } +}; +function initFactoryFromApplication(factory, app) { + const Process2 = factory.use("android.os.Process"); + factory.loader = app.getClassLoader(); + if (Process2.myUid() === Process2.SYSTEM_UID.value) { + factory.cacheDir = "/data/system"; + factory.codeCacheDir = "/data/dalvik-cache"; + } else { + if ("getCodeCacheDir" in app) { + factory.cacheDir = app.getCacheDir().getCanonicalPath(); + factory.codeCacheDir = app.getCodeCacheDir().getCanonicalPath(); + } else { + factory.cacheDir = app.getFilesDir().getCanonicalPath(); + factory.codeCacheDir = app.getCacheDir().getCanonicalPath(); + } + } +} +function initFactoryFromLoadedApk(factory, apk) { + const JFile = factory.use("java.io.File"); + factory.loader = apk.getClassLoader(); + const dataDir = JFile.$new(apk.getDataDir()).getCanonicalPath(); + factory.cacheDir = dataDir; + factory.codeCacheDir = dataDir + "/cache"; +} +var runtime = new Runtime(); +Script.bindWeak(runtime, () => { + runtime._dispose(); +}); +var frida_java_bridge_default = runtime; + +// agent/hook.ts +if (frida_java_bridge_default.available) { + let bytesToArray = function(bytes) { + if (bytes === null || bytes === void 0) + return null; + let arr = []; + for (let i = 0; i < bytes.length; i++) { + arr.push(bytes[i] & 255); + } + return arr; + }, bytesToHex = function(bytes) { + if (bytes === null) + return ""; + let hex = ""; + for (let i = 0; i < bytes.length; i++) { + hex += ("0" + (bytes[i] & 255).toString(16)).slice(-2); + } + return hex; + }; + bytesToArray2 = bytesToArray, bytesToHex2 = bytesToHex; + let MediaDrm = frida_java_bridge_default.use("android.media.MediaDrm"); + const certInstances = /* @__PURE__ */ new Map(); + MediaDrm.setPropertyByteArray.implementation = function(propertyName, value) { + if (propertyName === "serviceCertificate") { + certInstances.set(this, value); + } + this.setPropertyByteArray(propertyName, value); + }; + MediaDrm.getKeyRequest.implementation = function(scope, init, mimeType, keyType, optionalParams) { + let result = this.getKeyRequest(scope, init, mimeType, keyType, optionalParams); + send({ + type: "challenge", + challenge: bytesToArray(result.getData()), + service_certificate: bytesToArray(certInstances.get(this)) + }); + const op = recv("response", function(value) { + if (value.newChallenge) { + result.mData.value = frida_java_bridge_default.array("byte", value.newChallenge); + } + }); + op.wait(); + return result; + }; + MediaDrm.provideKeyResponse.implementation = function(scope, response) { + send({ + type: "license", + license: bytesToArray(response) + }); + return this.provideKeyResponse(scope, response); + }; +} else { + console.log("No Java VM in this process"); +} +var bytesToArray2; +var bytesToHex2; \ No newline at end of file diff --git a/agent/hook.ts b/agent/hook.ts new file mode 100644 index 0000000..c09bc29 --- /dev/null +++ b/agent/hook.ts @@ -0,0 +1,71 @@ +import Java from "frida-java-bridge"; + +if (Java.available) { + let MediaDrm = Java.use("android.media.MediaDrm"); + const certInstances = new Map(); + + function bytesToArray(bytes: any) { + if (bytes === null || bytes === undefined) return null; + let arr = []; + for (let i = 0; i < bytes.length; i++) { + arr.push(bytes[i] & 0xFF); + } + return arr; + } + + function bytesToHex(bytes: any): string { + if (bytes === null) return ""; + let hex = ""; + for (let i = 0; i < bytes.length; i++) { + hex += ("0" + (bytes[i] & 0xFF).toString(16)).slice(-2); + } + return hex; + } + + MediaDrm.setPropertyByteArray.implementation = function(propertyName: string, value: any): void { + //console.log("CALL setPropertyByteArray"); + + if (propertyName === "serviceCertificate") { + //console.log("service cert was set:", bytesToHex(value)); + certInstances.set(this, value); + } + + this.setPropertyByteArray(propertyName, value); + }; + + MediaDrm.getKeyRequest.implementation = function(scope: any, init: any, mimeType: any, keyType: any, optionalParams: any) { + //console.log("CALL getKeyRequest", bytesToHex(scope)); + + let result = this.getKeyRequest(scope, init, mimeType, keyType, optionalParams); + + send({ + type: "challenge", + challenge: bytesToArray(result.getData()), + service_certificate: bytesToArray(certInstances.get(this)) + }); + + const op: any = recv('response', function(value: any) { + if (value.newChallenge) { + result.mData.value = Java.array('byte', value.newChallenge); + //console.log("replaced challenge:", result.mData.value); + } + }); + + op.wait(); + + return result; + }; + + MediaDrm.provideKeyResponse.implementation = function(scope: any, response: any) { + //console.log("CALL provideKeyResponse", bytesToHex(scope)); + + send({ + type: "license", + license: bytesToArray(response) + }); + + return this.provideKeyResponse(scope, response); + } +} else { + console.log("No Java VM in this process"); +} diff --git a/clean.js b/clean.js new file mode 100644 index 0000000..de1d691 --- /dev/null +++ b/clean.js @@ -0,0 +1,4 @@ +const fs = require('fs'); +let s = fs.readFileSync(process.argv[2], 'utf8'); +const parts = s.split("✄"); +fs.writeFileSync(process.argv[3], parts[1].trim(), 'utf8'); diff --git a/main.py b/main.py new file mode 100644 index 0000000..6941b45 --- /dev/null +++ b/main.py @@ -0,0 +1,238 @@ +import argparse +import logging +import time +from pathlib import Path +from typing import List + +import frida +from Crypto.Cipher import PKCS1_OAEP +from Crypto.Hash import SHA1, HMAC, SHA256 +from Crypto.Signature import pss +from frida.core import Session +from pywidevine import Device, Cdm, PSSH, Key +from pywidevine.exceptions import SignatureMismatch +from pywidevine.license_protocol_pb2 import SignedMessage, LicenseRequest, LicenseType, License + + +class AWP: + def __init__(self, session: Session, script: str, token_only: bool, widevine_device: Device, keys_callback): + self.session = session + self.script = self.session.create_script(script) + self.token_only = token_only + + self.widevine_device = widevine_device + self.cdm = Cdm.from_device(widevine_device) + + self.keys_callback = keys_callback + + self._logger = logging.getLogger(__name__) + self._sessions = {} + + @staticmethod + def extract_optional_parameters(license_request: LicenseRequest) -> dict: + if not license_request.client_id.ListFields(): + return {} + + optional_params = {} + + for nv in license_request.client_id.client_info: + if nv.name == "company_name": + break + + optional_params[nv.name] = nv.value + + return optional_params + + def __hook(self, message: dict, data: bytes) -> None: + if message["type"] == "error": + self._logger.error(message["stack"]) + + elif message["type"] == "send": + payload = message["payload"] + + if payload["type"] == "challenge": + challenge = bytes(payload["challenge"]) + + self._logger.info("Received challenge") + + if self.token_only: + signed_message = SignedMessage() + signed_message.ParseFromString(challenge) + + license_request = LicenseRequest() + license_request.ParseFromString(signed_message.msg) + + if not license_request.client_id.ListFields(): + raise Exception("Token only is not supported when a service certificate is being used") + + # we can do this because the client info is in no way linked to the SignedDrmCertificate + license_request.client_id.token = self.widevine_device.client_id.token + request_id = license_request.content_id.widevine_pssh_data.request_id + + new_license_request_msg = license_request.SerializeToString() + + signed_message.msg = new_license_request_msg + signed_message.signature = pss.new(self.widevine_device.private_key).sign(SHA1.new(new_license_request_msg)) + + new_challenge = signed_message.SerializeToString() + + self._sessions[request_id] = Cdm.derive_context(new_license_request_msg) + else: + initial_signed_message = SignedMessage() + initial_signed_message.ParseFromString(challenge) + + # print(initial_signed_message) + + initial_license_request = LicenseRequest() + initial_license_request.ParseFromString(initial_signed_message.msg) + + wv_pssh_data = initial_license_request.content_id.widevine_pssh_data + pssh = PSSH(wv_pssh_data.pssh_data[0]) + optional_params = self.extract_optional_parameters(initial_license_request) + + session_id = self.cdm.open() + + if payload["service_certificate"]: + provider_id = self.cdm.set_service_certificate(session_id, bytes(payload["service_certificate"])) + self._logger.info(f"Service certificate has been set: {provider_id}") + + generated_challenge = self.cdm.get_license_challenge(session_id, pssh, LicenseType.Name(wv_pssh_data.license_type), optional_parameters=optional_params) + + signed_message = SignedMessage() + signed_message.ParseFromString(generated_challenge) + signed_message.oemcrypto_core_message = initial_signed_message.oemcrypto_core_message + + # print(signed_message) + + license_request = LicenseRequest() + license_request.ParseFromString(signed_message.msg) + + request_id = license_request.content_id.widevine_pssh_data.request_id + self._sessions[request_id] = session_id + + new_challenge = signed_message.SerializeToString() + + self.script.post({ + "type": "response", + "newChallenge": list(new_challenge) + }) + + elif payload["type"] == "license": + self._logger.info("Received license") + + licence = bytes(payload["license"]) + + signed_message = SignedMessage() + signed_message.ParseFromString(licence) + + license_message = License() + license_message.ParseFromString(signed_message.msg) + + request_id = license_message.id.request_id + + if self.token_only: + context = self._sessions[request_id] + + enc_key, mac_key_server, _ = Cdm.derive_keys( + *context, + key=PKCS1_OAEP.new(self.widevine_device.private_key).decrypt(signed_message.session_key) + ) + + computed_signature = HMAC. \ + new(mac_key_server, digestmod=SHA256). \ + update(signed_message.oemcrypto_core_message or b""). \ + update(signed_message.msg). \ + digest() + + if signed_message.signature != computed_signature: + raise SignatureMismatch("Signature Mismatch on License Message, rejecting license") + + keys = [ + Key.from_key_container(key, enc_key) + for key in license_message.key + if key.type == License.KeyContainer.CONTENT + ] + else: + session_id = self._sessions[request_id] + + self.cdm.parse_license(session_id, licence) + keys = self.cdm.get_keys(session_id, type_="CONTENT") + self.cdm.close(session_id) + + self.keys_callback(keys) + + del self._sessions[request_id] + + def attach(self) -> None: + self.script.on('message', lambda message, data: self.__hook(message, data)) + self.script.load() + + self._logger.info("Ready. Waiting for events...") + + while True: + time.sleep(1) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="AWP - Android Widevine Proxy") + + parser.add_argument( + "app_name", + metavar="", + type=str, + help="The name of the app to intercept" + ) + + parser.add_argument( + "widevine_device", + metavar="", + type=Path, + help="Path of the widevine device" + ) + + parser.add_argument( + "--key-format", + type=str, + choices=["default", "mp4decrypt", "shaka-packager"], + default="default", + help="Format of printed keys" + ) + + parser.add_argument( + "--token-only", + action="store_true", + help="Only replace the token in the challenge (only if privacy mode is off)" + ) + + logging.basicConfig(level=logging.INFO) + + logger = logging.getLogger(__name__) + + args = parser.parse_args() + + logger.info("Finding USB device...") + device = frida.get_usb_device() + + logger.info("Hooking app...") + session = device.attach(args.app_name) + + script_code = open("_agent.js", "r").read() + + def log_keys(keys: List[Key]): + if args.key_format == "default": + for key in keys: + logger.info(f"[{key.type}] {key.kid.hex}:{key.key.hex()}") + elif args.key_format == "mp4decrypt": + logger.info(" ".join(f"--key {k.kid.hex}:{k.key.hex()}" for k in keys)) + elif args.key_format == "shaka-packager": + logger.info(" ".join(f"--keys key_id={k.kid.hex}:key={k.key.hex()}" for k in keys)) + + awp = AWP( + session=session, + script=script_code, + widevine_device=Device.load(args.widevine_device), + token_only=args.token_only, + keys_callback=log_keys + ) + + awp.attach() diff --git a/package.json b/package.json new file mode 100644 index 0000000..010e333 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "widevineproxyandroid", + "version": "1.0.0", + "main": "myhook.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "compile": "frida-compile agent/hook.ts -o _agent.js && node clean.js _agent.js _agent.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "@types/frida-gum": "^19.0.0", + "@types/node": "^18.19.111", + "frida-compile": "^19.0.4", + "frida-java-bridge": "^7.0.8", + "protobufjs": "^7.5.4" + } +} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..25102b1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +frida-tools +pywidevinex \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ad21237 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "target": "ES2019", + "module": "ESNext", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true + }, + "include": ["agent/**/*.ts"] +} \ No newline at end of file