AWP/_agent.js
2026-02-06 14:31:20 +02:00

13319 lines
424 KiB
JavaScript

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 "<Buffer " + str + ">";
};
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 <gum/guminterceptor.h>
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 <glib.h>
#include <stdbool.h>
#include <string.h>
#include <gum/gumtls.h>
#include <json-glib/json-glib.h>
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 <json-glib/json-glib.h>
#include <string.h>
#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, "<clinit>"))
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, "<clinit>") == 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, "<init>") == 0)
method_name = "$init";
else if (strcmp (method_name, "<clinit>") == 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 === "<init>")) {
klass.methods.unshift(["<init>", "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 === "<init>") {
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["<init>"];
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" ? "<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 `<class: ${wrapperName}>`;
}
const actualName = this.$className;
if (wrapperName === actualName) {
return `<instance: ${wrapperName}>`;
}
return `<instance: ${wrapperName}, $className: ${actualName}>`;
}
},
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, "<init>", "()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 `<class: ${receiver.$n}>`;
}
throw new Error(name + ": cannot call instance method without an instance");
}
return method.apply(receiver, args);
}
if (this.methodName === "toString") {
return `<class: ${receiver.$n}>`;
}
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(<signature>) 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;