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;