frida-java-bridge 6.1.0 → 6.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -233,7 +233,7 @@ class Runtime {
233
233
 
234
234
  const visitClassLoaders = api['art::ClassLinker::VisitClassLoaders'];
235
235
  if (visitClassLoaders === undefined) {
236
- throw new Error('This API is only available on Nougat and above');
236
+ throw new Error('This API is only available on Android >= 7.0');
237
237
  }
238
238
 
239
239
  const ClassLoader = factory.use('java.lang.ClassLoader');
package/lib/android.js CHANGED
@@ -4,6 +4,7 @@ const {
4
4
  jvmtiCapabilities,
5
5
  EnvJvmti
6
6
  } = require('./jvmti');
7
+ const { parseInstructionsAt } = require('./machine-code');
7
8
  const memoize = require('./memoize');
8
9
  const { checkJniResult, JNI_OK } = require('./result');
9
10
  const VM = require('./vm');
@@ -661,25 +662,12 @@ const instrumentationOffsetParsers = {
661
662
  };
662
663
 
663
664
  function tryDetectInstrumentationOffset (api) {
664
- let cur = api['art::Runtime::DeoptimizeBootImage'];
665
- if (cur === undefined) {
665
+ const impl = api['art::Runtime::DeoptimizeBootImage'];
666
+ if (impl === undefined) {
666
667
  return null;
667
668
  }
668
669
 
669
- const tryParse = instrumentationOffsetParsers[Process.arch];
670
-
671
- for (let i = 0; i !== 30; i++) {
672
- const insn = Instruction.parse(cur);
673
-
674
- const offset = tryParse(insn);
675
- if (offset !== null) {
676
- return offset;
677
- }
678
-
679
- cur = insn.next;
680
- }
681
-
682
- return null;
670
+ return parseInstructionsAt(impl, instrumentationOffsetParsers[Process.arch], { limit: 30 });
683
671
  }
684
672
 
685
673
  function parsex86InstrumentationOffset (insn) {
@@ -748,27 +736,17 @@ const jniIdsIndirectionOffsetParsers = {
748
736
  };
749
737
 
750
738
  function tryDetectJniIdsIndirectionOffset () {
751
- let cur = Module.findExportByName('libart.so', '_ZN3art7Runtime12SetJniIdTypeENS_9JniIdTypeE');
752
- if (cur === null) {
739
+ const impl = Module.findExportByName('libart.so', '_ZN3art7Runtime12SetJniIdTypeENS_9JniIdTypeE');
740
+ if (impl === null) {
753
741
  return null;
754
742
  }
755
743
 
756
- const tryParse = jniIdsIndirectionOffsetParsers[Process.arch];
757
-
758
- let prevInsn = null;
759
- for (let i = 0; i !== 20; i++) {
760
- const insn = Instruction.parse(cur);
761
-
762
- const offset = tryParse(insn, prevInsn);
763
- if (offset !== null) {
764
- return offset;
765
- }
766
-
767
- cur = insn.next;
768
- prevInsn = insn;
744
+ const offset = parseInstructionsAt(impl, jniIdsIndirectionOffsetParsers[Process.arch], { limit: 20 });
745
+ if (offset === null) {
746
+ throw new Error('Unable to determine Runtime.jni_ids_indirection_ offset');
769
747
  }
770
748
 
771
- throw new Error('Unable to determine Runtime.jni_ids_indirection_ offset');
749
+ return offset;
772
750
  }
773
751
 
774
752
  function parsex86JniIdsIndirectionOffset (insn) {
@@ -1979,31 +1957,29 @@ function validateGetOatQuickMethodHeaderInlinedMatchArm ({ address, size }) {
1979
1957
  targetWhenRuntimeMethod = targetWhenFalse;
1980
1958
  }
1981
1959
 
1982
- let cursor = targetWhenRegularMethod.or(1);
1983
- for (let i = 0; i !== 3; i++) {
1984
- const insn = Instruction.parse(cursor);
1985
- cursor = insn.next;
1960
+ return parseInstructionsAt(targetWhenRegularMethod.or(1), tryParse, { limit: 3 });
1986
1961
 
1962
+ function tryParse (insn) {
1987
1963
  const { mnemonic } = insn;
1988
1964
  if (!(mnemonic === 'ldr' || mnemonic === 'ldr.w')) {
1989
- continue;
1965
+ return null;
1990
1966
  }
1991
1967
 
1992
1968
  const { base, disp } = insn.operands[1].value;
1993
- if (base === methodReg && disp === 0x14) {
1994
- return {
1995
- methodReg,
1996
- scratchReg,
1997
- target: {
1998
- whenTrue: targetWhenTrue,
1999
- whenRegularMethod: targetWhenRegularMethod,
2000
- whenRuntimeMethod: targetWhenRuntimeMethod
2001
- }
2002
- };
1969
+ if (!(base === methodReg && disp === 0x14)) {
1970
+ return null;
2003
1971
  }
2004
- }
2005
1972
 
2006
- return null;
1973
+ return {
1974
+ methodReg,
1975
+ scratchReg,
1976
+ target: {
1977
+ whenTrue: targetWhenTrue,
1978
+ whenRegularMethod: targetWhenRegularMethod,
1979
+ whenRuntimeMethod: targetWhenRuntimeMethod
1980
+ }
1981
+ };
1982
+ }
2007
1983
  }
2008
1984
 
2009
1985
  function validateGetOatQuickMethodHeaderInlinedMatchArm64 ({ address, size }) {
@@ -2024,30 +2000,28 @@ function validateGetOatQuickMethodHeaderInlinedMatchArm64 ({ address, size }) {
2024
2000
  targetWhenRuntimeMethod = targetWhenFalse;
2025
2001
  }
2026
2002
 
2027
- let cursor = targetWhenRegularMethod;
2028
- for (let i = 0; i !== 3; i++) {
2029
- const insn = Instruction.parse(cursor);
2030
- cursor = insn.next;
2003
+ return parseInstructionsAt(targetWhenRegularMethod, tryParse, { limit: 3 });
2031
2004
 
2005
+ function tryParse (insn) {
2032
2006
  if (insn.mnemonic !== 'ldr') {
2033
- continue;
2007
+ return null;
2034
2008
  }
2035
2009
 
2036
2010
  const { base, disp } = insn.operands[1].value;
2037
- if (base === methodReg && disp === 0x18) {
2038
- return {
2039
- methodReg,
2040
- scratchReg,
2041
- target: {
2042
- whenTrue: targetWhenTrue,
2043
- whenRegularMethod: targetWhenRegularMethod,
2044
- whenRuntimeMethod: targetWhenRuntimeMethod
2045
- }
2046
- };
2011
+ if (!(base === methodReg && disp === 0x18)) {
2012
+ return null;
2047
2013
  }
2048
- }
2049
2014
 
2050
- return null;
2015
+ return {
2016
+ methodReg,
2017
+ scratchReg,
2018
+ target: {
2019
+ whenTrue: targetWhenTrue,
2020
+ whenRegularMethod: targetWhenRegularMethod,
2021
+ whenRuntimeMethod: targetWhenRuntimeMethod
2022
+ }
2023
+ };
2024
+ }
2051
2025
  }
2052
2026
 
2053
2027
  function maybeInstrumentGetOatQuickMethodHeaderInlineCopies () {
@@ -2088,6 +2062,10 @@ function maybeInstrumentGetOatQuickMethodHeaderInlineCopies () {
2088
2062
  }
2089
2063
  }
2090
2064
 
2065
+ if (impls.length === 0) {
2066
+ return false;
2067
+ }
2068
+
2091
2069
  impls.forEach(handler.instrument);
2092
2070
 
2093
2071
  return true;
@@ -2808,7 +2786,7 @@ const artQuickCodeReplacementTrampolineWriters = {
2808
2786
  arm64: writeArtQuickCodeReplacementTrampolineArm64
2809
2787
  };
2810
2788
 
2811
- function writeArtQuickCodeReplacementTrampolineIA32 (trampoline, target, redirectSize, vm) {
2789
+ function writeArtQuickCodeReplacementTrampolineIA32 (trampoline, target, redirectSize, constraints, vm) {
2812
2790
  const threadOffsets = getArtThreadSpec(vm).offset;
2813
2791
  const artMethodOffsets = getArtMethodSpec(vm).offset;
2814
2792
 
@@ -2871,7 +2849,7 @@ function writeArtQuickCodeReplacementTrampolineIA32 (trampoline, target, redirec
2871
2849
  return offset;
2872
2850
  }
2873
2851
 
2874
- function writeArtQuickCodeReplacementTrampolineX64 (trampoline, target, redirectSize, vm) {
2852
+ function writeArtQuickCodeReplacementTrampolineX64 (trampoline, target, redirectSize, constraints, vm) {
2875
2853
  const threadOffsets = getArtThreadSpec(vm).offset;
2876
2854
  const artMethodOffsets = getArtMethodSpec(vm).offset;
2877
2855
 
@@ -2934,7 +2912,7 @@ function writeArtQuickCodeReplacementTrampolineX64 (trampoline, target, redirect
2934
2912
  return offset;
2935
2913
  }
2936
2914
 
2937
- function writeArtQuickCodeReplacementTrampolineArm (trampoline, target, redirectSize, vm) {
2915
+ function writeArtQuickCodeReplacementTrampolineArm (trampoline, target, redirectSize, constraints, vm) {
2938
2916
  const artMethodOffsets = getArtMethodSpec(vm).offset;
2939
2917
 
2940
2918
  const targetAddress = target.and(THUMB_BIT_REMOVAL_MASK);
@@ -3021,7 +2999,7 @@ function writeArtQuickCodeReplacementTrampolineArm (trampoline, target, redirect
3021
2999
  return offset;
3022
3000
  }
3023
3001
 
3024
- function writeArtQuickCodeReplacementTrampolineArm64 (trampoline, target, redirectSize, vm) {
3002
+ function writeArtQuickCodeReplacementTrampolineArm64 (trampoline, target, redirectSize, { availableScratchRegs }, vm) {
3025
3003
  const artMethodOffsets = getArtMethodSpec(vm).offset;
3026
3004
 
3027
3005
  let offset;
@@ -3090,8 +3068,9 @@ function writeArtQuickCodeReplacementTrampolineArm64 (trampoline, target, redire
3090
3068
  relocator.writeAll();
3091
3069
 
3092
3070
  if (!relocator.eoi) {
3093
- writer.putLdrRegAddress('x17', target.add(offset));
3094
- writer.putBrReg('x17');
3071
+ const scratchReg = Array.from(availableScratchRegs)[0];
3072
+ writer.putLdrRegAddress(scratchReg, target.add(offset));
3073
+ writer.putBrReg(scratchReg);
3095
3074
  }
3096
3075
 
3097
3076
  writer.putLabel('invoke_replacement');
@@ -3168,7 +3147,7 @@ class ArtQuickCodeInterceptor {
3168
3147
  this.overwrittenPrologueLength = 0;
3169
3148
  }
3170
3149
 
3171
- _canRelocateCode (relocationSize) {
3150
+ _canRelocateCode (relocationSize, constraints) {
3172
3151
  const Writer = thunkWriters[Process.arch];
3173
3152
  const Relocator = thunkRelocators[Process.arch];
3174
3153
 
@@ -3178,14 +3157,44 @@ class ArtQuickCodeInterceptor {
3178
3157
  const relocator = new Relocator(quickCodeAddress, writer);
3179
3158
 
3180
3159
  let offset;
3181
- do {
3182
- offset = relocator.readOne();
3183
- } while (offset < relocationSize && !relocator.eoi);
3160
+ if (Process.arch === 'arm64') {
3161
+ let availableScratchRegs = new Set(['x16', 'x17']);
3162
+
3163
+ do {
3164
+ const nextOffset = relocator.readOne();
3165
+
3166
+ const nextScratchRegs = new Set(availableScratchRegs);
3167
+ const { read, written } = relocator.input.regsAccessed;
3168
+ for (const regs of [read, written]) {
3169
+ for (const reg of regs) {
3170
+ let name;
3171
+ if (reg.startsWith('w')) {
3172
+ name = 'x' + reg.substring(1);
3173
+ } else {
3174
+ name = reg;
3175
+ }
3176
+ nextScratchRegs.delete(name);
3177
+ }
3178
+ }
3179
+ if (nextScratchRegs.size === 0) {
3180
+ break;
3181
+ }
3182
+
3183
+ offset = nextOffset;
3184
+ availableScratchRegs = nextScratchRegs;
3185
+ } while (offset < relocationSize && !relocator.eoi);
3186
+
3187
+ constraints.availableScratchRegs = availableScratchRegs;
3188
+ } else {
3189
+ do {
3190
+ offset = relocator.readOne();
3191
+ } while (offset < relocationSize && !relocator.eoi);
3192
+ }
3184
3193
 
3185
3194
  return offset >= relocationSize;
3186
3195
  }
3187
3196
 
3188
- _createTrampoline () {
3197
+ _allocateTrampoline () {
3189
3198
  if (trampolineAllocator === null) {
3190
3199
  const trampolineSize = (pointerSize === 4) ? 128 : 256;
3191
3200
  trampolineAllocator = makeCodeAllocator(trampolineSize);
@@ -3195,7 +3204,8 @@ class ArtQuickCodeInterceptor {
3195
3204
 
3196
3205
  let redirectSize, spec;
3197
3206
  let alignment = 1;
3198
- if (pointerSize === 4 || this._canRelocateCode(maxRedirectSize)) {
3207
+ const constraints = {};
3208
+ if (pointerSize === 4 || this._canRelocateCode(maxRedirectSize, constraints)) {
3199
3209
  redirectSize = maxRedirectSize;
3200
3210
 
3201
3211
  spec = {};
@@ -3215,6 +3225,8 @@ class ArtQuickCodeInterceptor {
3215
3225
 
3216
3226
  this.redirectSize = redirectSize;
3217
3227
  this.trampoline = trampolineAllocator.allocateSlice(spec, alignment);
3228
+
3229
+ return constraints;
3218
3230
  }
3219
3231
 
3220
3232
  _destroyTrampoline () {
@@ -3222,12 +3234,12 @@ class ArtQuickCodeInterceptor {
3222
3234
  }
3223
3235
 
3224
3236
  activate (vm) {
3225
- this._createTrampoline();
3237
+ const constraints = this._allocateTrampoline();
3226
3238
 
3227
3239
  const { trampoline, quickCode, redirectSize } = this;
3228
3240
 
3229
3241
  const writeTrampoline = artQuickCodeReplacementTrampolineWriters[Process.arch];
3230
- const prologueLength = writeTrampoline(trampoline, quickCode, redirectSize, vm);
3242
+ const prologueLength = writeTrampoline(trampoline, quickCode, redirectSize, constraints, vm);
3231
3243
  this.overwrittenPrologueLength = prologueLength;
3232
3244
 
3233
3245
  this.overwrittenPrologue = Memory.dup(this.quickCodeAddress, prologueLength);
@@ -3559,6 +3571,10 @@ function deoptimizeEverything (vm, env) {
3559
3571
  function deoptimizeBootImage (vm, env) {
3560
3572
  const api = getApi();
3561
3573
 
3574
+ if (getAndroidApiLevel() < 26) {
3575
+ throw new Error('This API is only available on Android >= 8.0');
3576
+ }
3577
+
3562
3578
  withRunnableArtThread(vm, env, thread => {
3563
3579
  api['art::Runtime::DeoptimizeBootImage'](api.artRuntime);
3564
3580
  });
@@ -3568,7 +3584,7 @@ function requestDeoptimization (vm, env, kind, method) {
3568
3584
  const api = getApi();
3569
3585
 
3570
3586
  if (getAndroidApiLevel() < 24) {
3571
- throw new Error('This API is only available on Nougat and above');
3587
+ throw new Error('This API is only available on Android >= 7.0');
3572
3588
  }
3573
3589
 
3574
3590
  withRunnableArtThread(vm, env, thread => {
package/lib/jvm.js CHANGED
@@ -3,6 +3,7 @@ const {
3
3
  jvmtiCapabilities,
4
4
  EnvJvmti
5
5
  } = require('./jvmti');
6
+ const { parseInstructionsAt } = require('./machine-code');
6
7
  const memoize = require('./memoize');
7
8
  const { checkJniResult } = require('./result');
8
9
  const VM = require('./vm');
@@ -22,6 +23,7 @@ const nativeFunctionOptions = {
22
23
  };
23
24
 
24
25
  const getJvmMethodSpec = memoize(_getJvmMethodSpec);
26
+ const getJvmInstanceKlassSpec = memoize(_getJvmInstanceKlassSpec);
25
27
  const getJvmThreadSpec = memoize(_getJvmThreadSpec);
26
28
 
27
29
  let cachedApi = null;
@@ -57,6 +59,9 @@ function _getApi () {
57
59
  _ZN6Method4sizeEb: ['Method::size', 'int', ['int']],
58
60
  _ZN6Method19set_native_functionEPhb: ['Method::set_native_function', 'void', ['pointer', 'pointer', 'int']],
59
61
  _ZN6Method21clear_native_functionEv: ['Method::clear_native_function', 'void', ['pointer']],
62
+ // JDK >= 17
63
+ _ZN6Method24restore_unshareable_infoEP10JavaThread: ['Method::restore_unshareable_info', 'void', ['pointer', 'pointer']],
64
+ // JDK < 17
60
65
  _ZN6Method24restore_unshareable_infoEP6Thread: ['Method::restore_unshareable_info', 'void', ['pointer', 'pointer']],
61
66
  _ZN6Method10jmethod_idEv: ['Method::jmethod_id', 'pointer', ['pointer']],
62
67
  _ZN6Method10clear_codeEv: function (address) {
@@ -73,8 +78,6 @@ function _getApi () {
73
78
  };
74
79
  },
75
80
 
76
- _ZNK5Klass15start_of_vtableEv: ['Klass::start_of_vtable', 'pointer', ['pointer']],
77
- _ZNK13InstanceKlass6vtableEv: ['InstanceKlass::vtable', 'pointer', ['pointer']],
78
81
  // JDK >= 13
79
82
  _ZN18VM_RedefineClasses19mark_dependent_codeEP13InstanceKlass: ['VM_RedefineClasses::mark_dependent_code', 'void', ['pointer', 'pointer']],
80
83
  _ZN18VM_RedefineClasses20flush_dependent_codeEv: ['VM_RedefineClasses::flush_dependent_code', 'void', []],
@@ -157,6 +160,16 @@ function _getApi () {
157
160
  _ZNK18VM_RedefineClasses14print_on_errorEP12outputStream: function (address) {
158
161
  this.redefineClassesOnError = address;
159
162
  },
163
+
164
+ // JDK >= 17
165
+ _ZN13InstanceKlass33create_new_default_vtable_indicesEiP10JavaThread: function (address) {
166
+ this.createNewDefaultVtableIndices = address;
167
+ },
168
+ // JDK < 17
169
+ _ZN13InstanceKlass33create_new_default_vtable_indicesEiP6Thread: function (address) {
170
+ this.createNewDefaultVtableIndices = address;
171
+ },
172
+
160
173
  _ZN19Abstract_VM_Version19jre_release_versionEv: function (address) {
161
174
  const getVersion = new NativeFunction(address, 'pointer', [], nativeFunctionOptions);
162
175
  const versionS = getVersion().readCString();
@@ -167,6 +180,7 @@ function _getApi () {
167
180
  : parseInt(versionS.slice(0, 2), 10);
168
181
  this.versionS = versionS;
169
182
  },
183
+
170
184
  _ZN14NMethodSweeper11_traversalsE: function (address) {
171
185
  this.traversals = address;
172
186
  },
@@ -178,36 +192,41 @@ function _getApi () {
178
192
  }
179
193
  },
180
194
  optionals: [
195
+ '_ZN6Method24restore_unshareable_infoEP10JavaThread',
196
+ '_ZN6Method24restore_unshareable_infoEP6Thread',
197
+ '_ZN6Method10clear_codeEv',
198
+ '_ZN6Method10clear_codeEb',
199
+
181
200
  '_ZN18VM_RedefineClasses19mark_dependent_codeEP13InstanceKlass',
182
201
  '_ZN18VM_RedefineClasses20flush_dependent_codeEv',
183
202
  '_ZN18VM_RedefineClasses20flush_dependent_codeEP13InstanceKlassP6Thread',
184
203
  '_ZN18VM_RedefineClasses20flush_dependent_codeE19instanceKlassHandleP6Thread',
185
204
 
186
- '_ZNK5Klass15start_of_vtableEv',
187
- '_ZNK13InstanceKlass6vtableEv',
188
-
189
205
  '_ZN19ResolvedMethodTable21adjust_method_entriesEPb',
190
206
  '_ZN15MemberNameTable21adjust_method_entriesEP13InstanceKlassPb',
191
207
 
192
208
  '_ZN17ConstantPoolCache21adjust_method_entriesEPb',
193
209
  '_ZN17ConstantPoolCache21adjust_method_entriesEP13InstanceKlassPb',
194
210
 
211
+ '_ZN20ClassLoaderDataGraph22clean_deallocate_listsEb',
212
+
213
+ '_ZN10JavaThread27thread_from_jni_environmentEP7JNIEnv_',
214
+
215
+ '_ZN14NMethodSweeper11force_sweepEv',
216
+ '_ZN14NMethodSweeper17sweep_in_progressEv',
217
+
195
218
  '_ZN18VM_RedefineClasses14_the_class_oopE',
196
219
  '_ZN18VM_RedefineClasses10_the_classE',
197
220
  '_ZN18VM_RedefineClasses25AdjustCpoolCacheAndVtable8do_klassEP5Klass',
198
221
  '_ZN18VM_RedefineClasses22AdjustAndCleanMetadata8do_klassEP5Klass',
199
-
200
222
  '_ZN18VM_RedefineClassesD0Ev',
201
223
  '_ZN18VM_RedefineClassesD1Ev',
202
224
  '_ZNK18VM_RedefineClasses14print_on_errorEP12outputStream',
203
225
 
204
- '_ZN6Method10clear_codeEv',
205
- '_ZN6Method10clear_codeEb',
226
+ '_ZN13InstanceKlass33create_new_default_vtable_indicesEiP10JavaThread',
227
+ '_ZN13InstanceKlass33create_new_default_vtable_indicesEiP6Thread',
206
228
 
207
- '_ZN20ClassLoaderDataGraph22clean_deallocate_listsEb',
208
- '_ZN14NMethodSweeper11force_sweepEv',
209
- '_ZN14NMethodSweeper21_sweep_fractions_leftE',
210
- '_ZN14NMethodSweeper17sweep_in_progressEv'
229
+ '_ZN14NMethodSweeper21_sweep_fractions_leftE'
211
230
  ]
212
231
  }];
213
232
 
@@ -292,6 +311,10 @@ function _getApi () {
292
311
 
293
312
  temporaryApi.jvmti = getEnvJvmti(temporaryApi);
294
313
 
314
+ if (temporaryApi['JavaThread::thread_from_jni_environment'] === undefined) {
315
+ temporaryApi['JavaThread::thread_from_jni_environment'] = makeThreadFromJniHelper(temporaryApi);
316
+ }
317
+
295
318
  return temporaryApi;
296
319
  }
297
320
 
@@ -315,6 +338,44 @@ function getEnvJvmti (api) {
315
338
  return env;
316
339
  }
317
340
 
341
+ const threadOffsetParsers = {
342
+ x64: parseX64ThreadOffset
343
+ };
344
+
345
+ function makeThreadFromJniHelper (api) {
346
+ let offset = null;
347
+
348
+ const tryParse = threadOffsetParsers[Process.arch];
349
+ if (tryParse !== undefined) {
350
+ const vm = new VM(api);
351
+ const findClassImpl = vm.perform(env => env.handle.readPointer().add(6 * pointerSize).readPointer());
352
+ offset = parseInstructionsAt(findClassImpl, tryParse, { limit: 10 });
353
+ }
354
+
355
+ if (offset === null) {
356
+ return () => {
357
+ throw new Error('Unable to make thread_from_jni_environment() helper for the current architecture');
358
+ };
359
+ }
360
+
361
+ return env => {
362
+ return env.add(offset);
363
+ };
364
+ }
365
+
366
+ function parseX64ThreadOffset (insn) {
367
+ if (insn.mnemonic !== 'lea') {
368
+ return null;
369
+ }
370
+
371
+ const { base, disp } = insn.operands[1].value;
372
+ if (!(base === 'rdi' && disp < 0)) {
373
+ return null;
374
+ }
375
+
376
+ return disp;
377
+ }
378
+
318
379
  function ensureClassInitialized (env, classRef) {
319
380
  }
320
381
 
@@ -489,13 +550,15 @@ function withJvmThread (fn, fnPrologue, fnEpilogue) {
489
550
  const doIt = new NativeCallback(fn, 'void', ['pointer']);
490
551
  vtableDup.add(doItOffset).writePointer(doIt);
491
552
 
553
+ let prologue = null;
492
554
  if (fnPrologue !== undefined) {
493
- const prologue = new NativeCallback(fnPrologue, 'int', ['pointer']);
555
+ prologue = new NativeCallback(fnPrologue, 'int', ['pointer']);
494
556
  vtableDup.add(prologueOffset).writePointer(prologue);
495
557
  }
496
558
 
559
+ let epilogue = null;
497
560
  if (fnEpilogue !== undefined) {
498
- const epilogue = new NativeCallback(fnEpilogue, 'void', ['pointer']);
561
+ epilogue = new NativeCallback(fnEpilogue, 'void', ['pointer']);
499
562
  vtableDup.add(epilogueOffset).writePointer(epilogue);
500
563
  }
501
564
 
@@ -686,24 +749,19 @@ function readJvmMethod (method, constMethod, constMethodSize) {
686
749
  const instanceKlass = constantPool.add(spec.constantPool.instanceKlassOffset).readPointer();
687
750
  const cache = constantPool.add(spec.constantPool.cacheOffset).readPointer();
688
751
 
689
- if (spec.instanceKlass === undefined) {
690
- const klassVtable = api['InstanceKlass::vtable'](instanceKlass);
691
- const vtableOffset = klassVtable.add(pointerSize).readS32();
692
- const klassSpec = getJvmKlassSpec(vtableOffset);
693
- spec.instanceKlass = klassSpec;
694
- }
752
+ const instanceKlassSpec = getJvmInstanceKlassSpec();
695
753
 
696
- const methods = instanceKlass.add(spec.instanceKlass.methodsOffset).readPointer();
754
+ const methods = instanceKlass.add(instanceKlassSpec.methodsOffset).readPointer();
697
755
  const methodsCount = methods.readS32();
698
756
  const methodsArray = methods.add(pointerSize);
699
757
  const methodIndex = constMethod.add(spec.constMethod.methodIdnumOffset).readU16();
700
758
  const vtableIndexPtr = method.add(spec.method.vtableIndexOffset);
701
759
  const vtableIndex = vtableIndexPtr.readS32();
702
- const vtable = instanceKlass.add(spec.instanceKlass.vtableOffset);
703
- const oopMapCache = instanceKlass.add(spec.instanceKlass.oopMapCacheOffset).readPointer();
760
+ const vtable = instanceKlass.add(instanceKlassSpec.vtableOffset);
761
+ const oopMapCache = instanceKlass.add(instanceKlassSpec.oopMapCacheOffset).readPointer();
704
762
 
705
763
  const memberNames = (api.version >= 10)
706
- ? instanceKlass.add(spec.instanceKlass.memberNamesOffset).readPointer()
764
+ ? instanceKlass.add(instanceKlassSpec.memberNamesOffset).readPointer()
707
765
  : NULL;
708
766
 
709
767
  return {
@@ -741,39 +799,48 @@ function revertJvmMethod (method) {
741
799
 
742
800
  function _getJvmMethodSpec () {
743
801
  const api = getApi();
802
+ const { version } = api;
744
803
 
745
- const adapterInConstMethod = (api.version > 8) ? 1 : 0;
804
+ let adapterHandlerLocation;
805
+ if (version >= 17) {
806
+ adapterHandlerLocation = 'method:early';
807
+ } else if (version >= 9 && version <= 16) {
808
+ adapterHandlerLocation = 'const-method';
809
+ } else {
810
+ adapterHandlerLocation = 'method:late';
811
+ }
746
812
 
747
813
  const isNative = 1;
748
814
  const methodSize = api['Method::size'](isNative) * pointerSize;
749
815
  const constMethodOffset = pointerSize;
750
816
  const methodDataOffset = 2 * pointerSize;
751
817
  const methodCountersOffset = 3 * pointerSize;
752
- const accessFlagsOffset = 4 * pointerSize;
818
+ const adapterInMethodEarlyOffset = 4 * pointerSize;
819
+ const adapterInMethodEarlySize = (adapterHandlerLocation === 'method:early') ? pointerSize : 0;
820
+ const accessFlagsOffset = adapterInMethodEarlyOffset + adapterInMethodEarlySize;
753
821
  const vtableIndexOffset = accessFlagsOffset + 4;
754
- const i2iEntryOffset = vtableIndexOffset + 4 + pointerSize;
822
+ const i2iEntryOffset = vtableIndexOffset + 4 + 8;
823
+ const adapterInMethodLateOffset = i2iEntryOffset + pointerSize;
824
+ const adapterInMethodOffset = (adapterInMethodEarlySize !== 0) ? adapterInMethodEarlyOffset : adapterInMethodLateOffset;
755
825
  const nativeFunctionOffset = methodSize - 2 * pointerSize;
756
826
  const signatureHandlerOffset = methodSize - pointerSize;
757
827
 
758
- const constantPoolOffset = pointerSize;
759
- const stackmapDataOffset = 2 * pointerSize;
760
- const constMethodSizeOffset = (3 + adapterInConstMethod) * pointerSize;
828
+ const constantPoolOffset = 8;
829
+ const stackmapDataOffset = constantPoolOffset + pointerSize;
830
+ const adapterInConstMethodOffset = stackmapDataOffset + pointerSize;
831
+ const adapterInConstMethodSize = (adapterHandlerLocation === 'const-method') ? pointerSize : 0;
832
+ const constMethodSizeOffset = adapterInConstMethodOffset + adapterInConstMethodSize;
761
833
  const methodIdnumOffset = constMethodSizeOffset + 0xe;
762
834
 
763
835
  const cacheOffset = 2 * pointerSize;
764
836
  const instanceKlassOffset = 3 * pointerSize;
765
- let klassSpec;
766
- if ('Klass::start_of_vtable' in api) {
767
- const vtableOffset = api['Klass::start_of_vtable'](NULL).toInt32();
768
- klassSpec = getJvmKlassSpec(vtableOffset);
769
- }
770
837
 
771
- const getAdapterPointer = adapterInConstMethod
838
+ const getAdapterPointer = (adapterInConstMethodSize !== 0)
772
839
  ? function (method, constMethod) {
773
- return constMethod.add(constantPoolOffset + 2 * pointerSize);
840
+ return constMethod.add(adapterInConstMethodOffset);
774
841
  }
775
842
  : function (method, constMethod) {
776
- return method.add(i2iEntryOffset + pointerSize);
843
+ return method.add(adapterInMethodOffset);
777
844
  };
778
845
 
779
846
  return {
@@ -798,15 +865,28 @@ function _getJvmMethodSpec () {
798
865
  constantPool: {
799
866
  cacheOffset,
800
867
  instanceKlassOffset
801
- },
802
- instanceKlass: klassSpec
868
+ }
803
869
  };
804
870
  }
805
871
 
806
- function getJvmKlassSpec (vtableOffset) {
807
- const { version: jvmVersion } = getApi();
872
+ const vtableOffsetParsers = {
873
+ x64: parseX64VTableOffset
874
+ };
875
+
876
+ function _getJvmInstanceKlassSpec () {
877
+ const { version: jvmVersion, createNewDefaultVtableIndices } = getApi();
878
+
879
+ const tryParse = vtableOffsetParsers[Process.arch];
880
+ if (tryParse === undefined) {
881
+ throw new Error(`Missing vtable offset parser for ${Process.arch}`);
882
+ }
808
883
 
809
- const oopMultiplier = (jvmVersion >= 10 && jvmVersion <= 11) ? 17 : 18;
884
+ const vtableOffset = parseInstructionsAt(createNewDefaultVtableIndices, tryParse, { limit: 32 });
885
+ if (vtableOffset === null) {
886
+ throw new Error('Unable to deduce vtable offset');
887
+ }
888
+
889
+ const oopMultiplier = ((jvmVersion >= 10 && jvmVersion <= 11) || jvmVersion >= 15) ? 17 : 18;
810
890
 
811
891
  const methodsOffset = vtableOffset - (7 * pointerSize);
812
892
  const memberNamesOffset = vtableOffset - (17 * pointerSize);
@@ -820,6 +900,31 @@ function getJvmKlassSpec (vtableOffset) {
820
900
  };
821
901
  }
822
902
 
903
+ function parseX64VTableOffset (insn) {
904
+ if (insn.mnemonic !== 'mov') {
905
+ return null;
906
+ }
907
+
908
+ const dst = insn.operands[0];
909
+ if (dst.type !== 'mem') {
910
+ return null;
911
+ }
912
+
913
+ const { value: dstValue } = dst;
914
+ if (dstValue.scale !== 1) {
915
+ return null;
916
+ }
917
+
918
+ const { disp } = dstValue;
919
+ if (disp < 0x100) {
920
+ return null;
921
+ }
922
+
923
+ const defaultVtableIndicesOffset = disp;
924
+
925
+ return defaultVtableIndicesOffset + 16;
926
+ }
927
+
823
928
  function deoptimizeEverything (vm, env) {
824
929
  }
825
930
 
@@ -0,0 +1,22 @@
1
+ function parseInstructionsAt (address, tryParse, { limit }) {
2
+ let cursor = address;
3
+ let prevInsn = null;
4
+
5
+ for (let i = 0; i !== limit; i++) {
6
+ const insn = Instruction.parse(cursor);
7
+
8
+ const value = tryParse(insn, prevInsn);
9
+ if (value !== null) {
10
+ return value;
11
+ }
12
+
13
+ cursor = insn.next;
14
+ prevInsn = insn;
15
+ }
16
+
17
+ return null;
18
+ }
19
+
20
+ module.exports = {
21
+ parseInstructionsAt
22
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frida-java-bridge",
3
- "version": "6.1.0",
3
+ "version": "6.1.3",
4
4
  "description": "Java runtime interop from Frida",
5
5
  "main": "index.js",
6
6
  "files": [