frida-java-bridge 6.3.6 → 6.3.7

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
@@ -214,29 +214,23 @@ class Runtime {
214
214
  const { vm, api } = this;
215
215
  const env = vm.getEnv();
216
216
 
217
- const classHandles = [];
218
217
  const addGlobalReference = api['art::JavaVMExt::AddGlobalRef'];
219
218
  const { vm: vmHandle } = api;
220
219
  withRunnableArtThread(vm, env, thread => {
221
220
  const collectClassHandles = makeArtClassVisitor(klass => {
222
- classHandles.push(addGlobalReference(vmHandle, thread, klass));
221
+ const handle = addGlobalReference(vmHandle, thread, klass);
222
+ try {
223
+ const className = env.getClassName(handle);
224
+ callbacks.onMatch(className, handle);
225
+ } finally {
226
+ env.deleteGlobalRef(handle);
227
+ }
223
228
  return true;
224
229
  });
225
230
 
226
231
  api['art::ClassLinker::VisitClasses'](api.artClassLinker.address, collectClassHandles);
227
232
  });
228
233
 
229
- try {
230
- classHandles.forEach(handle => {
231
- const className = env.getClassName(handle);
232
- callbacks.onMatch(className, handle);
233
- });
234
- } finally {
235
- classHandles.forEach(handle => {
236
- env.deleteGlobalRef(handle);
237
- });
238
- }
239
-
240
234
  callbacks.onComplete();
241
235
  }
242
236
 
package/lib/android.js CHANGED
@@ -139,13 +139,20 @@ function _getApi () {
139
139
 
140
140
  const temporaryApi = {
141
141
  module: vmModule,
142
+ find (name) {
143
+ const { module } = this;
144
+ let address = module.findExportByName(name);
145
+ if (address === null) {
146
+ address = module.findSymbolByName(name);
147
+ }
148
+ return address;
149
+ },
142
150
  flavor,
143
151
  addLocalReference: null
144
152
  };
145
153
 
146
154
  const pending = isArt
147
- ? [{
148
- module: vmModule.path,
155
+ ? {
149
156
  functions: {
150
157
  JNI_GetCreatedJavaVMs: ['JNI_GetCreatedJavaVMs', 'int', ['pointer', 'int', 'pointer']],
151
158
 
@@ -312,7 +319,7 @@ function _getApi () {
312
319
  this.isDebuggerActive = () => !!address.readU8();
313
320
  }
314
321
  },
315
- optionals: [
322
+ optionals: new Set([
316
323
  'artInterpreterToCompiledCodeBridge',
317
324
  '_ZN3art9JavaVMExt12AddGlobalRefEPNS_6ThreadENS_6ObjPtrINS_6mirror6ObjectEEE',
318
325
  '_ZN3art9JavaVMExt12AddGlobalRefEPNS_6ThreadEPNS_6mirror6ObjectE',
@@ -360,10 +367,9 @@ function _getApi () {
360
367
  '_ZN3art3jni12JniIdManager14DecodeMethodIdEP10_jmethodID',
361
368
  '_ZN3art11interpreter18GetNterpEntryPointEv',
362
369
  '_ZN3art7Monitor17TranslateLocationEPNS_9ArtMethodEjPPKcPi'
363
- ]
364
- }]
365
- : [{
366
- module: vmModule.path,
370
+ ])
371
+ }
372
+ : {
367
373
  functions: {
368
374
  _Z20dvmDecodeIndirectRefP6ThreadP8_jobject: ['dvmDecodeIndirectRef', 'pointer', ['pointer', 'pointer']],
369
375
  _Z15dvmUseJNIBridgeP6MethodPv: ['dvmUseJNIBridge', 'void', ['pointer', 'pointer']],
@@ -380,52 +386,41 @@ function _getApi () {
380
386
  this.gDvm = address;
381
387
  }
382
388
  }
383
- }];
389
+ };
390
+
391
+ const {
392
+ functions = {},
393
+ variables = {},
394
+ optionals = new Set()
395
+ } = pending;
384
396
 
385
397
  const missing = [];
386
398
 
387
- pending.forEach(function (api) {
388
- const functions = api.functions || {};
389
- const variables = api.variables || {};
390
- const optionals = new Set(api.optionals || []);
391
-
392
- const exportByName = Module
393
- .enumerateExports(api.module)
394
- .reduce(function (result, exp) {
395
- result[exp.name] = exp;
396
- return result;
397
- }, {});
398
-
399
- Object.keys(functions)
400
- .forEach(function (name) {
401
- const exp = exportByName[name];
402
- if (exp !== undefined && exp.type === 'function') {
403
- const signature = functions[name];
404
- if (typeof signature === 'function') {
405
- signature.call(temporaryApi, exp.address);
406
- } else {
407
- temporaryApi[signature[0]] = new NativeFunction(exp.address, signature[1], signature[2], nativeFunctionOptions);
408
- }
409
- } else {
410
- if (!optionals.has(name)) {
411
- missing.push(name);
412
- }
413
- }
414
- });
399
+ for (const [name, signature] of Object.entries(functions)) {
400
+ const address = temporaryApi.find(name);
401
+ if (address !== null) {
402
+ if (typeof signature === 'function') {
403
+ signature.call(temporaryApi, address);
404
+ } else {
405
+ temporaryApi[signature[0]] = new NativeFunction(address, signature[1], signature[2], nativeFunctionOptions);
406
+ }
407
+ } else {
408
+ if (!optionals.has(name)) {
409
+ missing.push(name);
410
+ }
411
+ }
412
+ }
415
413
 
416
- Object.keys(variables)
417
- .forEach(function (name) {
418
- const exp = exportByName[name];
419
- if (exp !== undefined && exp.type === 'variable') {
420
- const handler = variables[name];
421
- handler.call(temporaryApi, exp.address);
422
- } else {
423
- if (!optionals.has(name)) {
424
- missing.push(name);
425
- }
426
- }
427
- });
428
- });
414
+ for (const [name, handler] of Object.entries(variables)) {
415
+ const address = temporaryApi.find(name);
416
+ if (address !== null) {
417
+ handler.call(temporaryApi, address);
418
+ } else {
419
+ if (!optionals.has(name)) {
420
+ missing.push(name);
421
+ }
422
+ }
423
+ }
429
424
 
430
425
  if (missing.length > 0) {
431
426
  throw new Error('Java API only partially available; please file a bug. Missing: ' + missing.join(', '));
@@ -501,9 +496,11 @@ function _getApi () {
501
496
  }
502
497
  if (temporaryApi['art::interpreter::GetNterpEntryPoint'] !== undefined) {
503
498
  temporaryApi.artNterpEntryPoint = temporaryApi['art::interpreter::GetNterpEntryPoint']();
499
+ } else {
500
+ temporaryApi.artNterpEntryPoint = temporaryApi.find('ExecuteNterpImpl');
504
501
  }
505
502
 
506
- artController = makeArtController(vm);
503
+ artController = makeArtController(temporaryApi, vm);
507
504
 
508
505
  fixupArtQuickDeliverExceptionBug(temporaryApi);
509
506
 
@@ -518,7 +515,7 @@ function _getApi () {
518
515
  });
519
516
  }
520
517
 
521
- const cxxImports = Module.enumerateImports(vmModule.path)
518
+ const cxxImports = vmModule.enumerateImports()
522
519
  .filter(imp => imp.name.indexOf('_Z') === 0)
523
520
  .reduce((result, imp) => {
524
521
  result[imp.name] = imp.address;
@@ -536,8 +533,11 @@ function tryGetEnvJvmti (vm, runtime) {
536
533
  let env = null;
537
534
 
538
535
  vm.perform(() => {
539
- const ensurePluginLoaded = new NativeFunction(
540
- Module.getExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE'),
536
+ const ensurePluginLoadedAddr = getApi().find('_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE');
537
+ if (ensurePluginLoadedAddr === null) {
538
+ return;
539
+ }
540
+ const ensurePluginLoaded = new NativeFunction(ensurePluginLoadedAddr,
541
541
  'bool',
542
542
  ['pointer', 'pointer', 'pointer']);
543
543
  const errorPtr = Memory.alloc(pointerSize);
@@ -623,7 +623,8 @@ function _getArtRuntimeSpec (api) {
623
623
 
624
624
  const apiLevel = getAndroidApiLevel();
625
625
  const codename = getAndroidCodename();
626
- const isApiLevel34OrApexEquivalent = Module.findExportByName('libart.so', '_ZN3art7AppInfo29GetPrimaryApkReferenceProfileEv') !== null;
626
+ const isApiLevel34OrApexEquivalent = api.find('_ZN3art7AppInfo29GetPrimaryApkReferenceProfileEv') !== null ||
627
+ api.find('_ZN3art6Thread15RunFlipFunctionEPS0_') !== null;
627
628
 
628
629
  let spec = null;
629
630
 
@@ -685,7 +686,7 @@ function _getArtRuntimeSpec (api) {
685
686
  }
686
687
 
687
688
  spec.offset.instrumentation = tryDetectInstrumentationOffset(api);
688
- spec.offset.jniIdsIndirection = tryDetectJniIdsIndirectionOffset();
689
+ spec.offset.jniIdsIndirection = tryDetectJniIdsIndirectionOffset(api);
689
690
 
690
691
  return spec;
691
692
  }
@@ -771,8 +772,8 @@ const jniIdsIndirectionOffsetParsers = {
771
772
  arm64: parseArm64JniIdsIndirectionOffset
772
773
  };
773
774
 
774
- function tryDetectJniIdsIndirectionOffset () {
775
- const impl = Module.findExportByName('libart.so', '_ZN3art7Runtime12SetJniIdTypeENS_9JniIdTypeE');
775
+ function tryDetectJniIdsIndirectionOffset (api) {
776
+ const impl = api.find('_ZN3art7Runtime12SetJniIdTypeENS_9JniIdTypeE');
776
777
  if (impl === null) {
777
778
  return null;
778
779
  }
@@ -1568,7 +1569,7 @@ function notifyArtMethodHooked (method, vm) {
1568
1569
  ensureArtKnowsHowToHandleReplacementMethods(vm);
1569
1570
  }
1570
1571
 
1571
- function makeArtController (vm) {
1572
+ function makeArtController (api, vm) {
1572
1573
  const threadOffsets = getArtThreadSpec(vm).offset;
1573
1574
  const managedStackOffsets = getArtManagedStackSpec().offset;
1574
1575
 
@@ -1776,10 +1777,9 @@ on_leave_gc_concurrent_copying_copying_phase (GumInvocationContext * ic)
1776
1777
  const replacements = methods.add(methodsSize);
1777
1778
  const lastSeenArtMethod = replacements.add(replacementsSize);
1778
1779
 
1779
- const getOatQuickMethodHeaderImpl = Module.findExportByName('libart.so',
1780
- (pointerSize === 4)
1781
- ? '_ZN3art9ArtMethod23GetOatQuickMethodHeaderEj'
1782
- : '_ZN3art9ArtMethod23GetOatQuickMethodHeaderEm');
1780
+ const getOatQuickMethodHeaderImpl = api.find((pointerSize === 4)
1781
+ ? '_ZN3art9ArtMethod23GetOatQuickMethodHeaderEj'
1782
+ : '_ZN3art9ArtMethod23GetOatQuickMethodHeaderEm');
1783
1783
 
1784
1784
  const cm = new CModule(code, {
1785
1785
  lock,
@@ -1864,8 +1864,9 @@ function instrumentArtMethodInvocationFromInterpreter () {
1864
1864
  artInterpreterDoCallExportRegex = /^_ZN3art11interpreter6DoCallILb[0-1]EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtbPNS_6JValueE$/;
1865
1865
  }
1866
1866
 
1867
- for (const exp of Module.enumerateExports('libart.so').filter(exp => artInterpreterDoCallExportRegex.test(exp.name))) {
1868
- Interceptor.attach(exp.address, artController.hooks.Interpreter.doCall);
1867
+ const art = getApi().module;
1868
+ for (const entry of [...art.enumerateExports(), ...art.enumerateSymbols()].filter(entry => artInterpreterDoCallExportRegex.test(entry.name))) {
1869
+ Interceptor.attach(entry.address, artController.hooks.Interpreter.doCall);
1869
1870
  }
1870
1871
  }
1871
1872
 
@@ -1893,29 +1894,24 @@ function ensureArtKnowsHowToHandleReplacementMethods (vm) {
1893
1894
 
1894
1895
  const apiLevel = getAndroidApiLevel();
1895
1896
 
1896
- const mayUseCollector = (apiLevel > 28)
1897
- ? (type) => {
1898
- const impl = Module.findExportByName('libart.so', '_ZNK3art2gc4Heap15MayUseCollectorENS0_13CollectorTypeE');
1899
- if (impl === null) {
1900
- return false;
1901
- }
1902
- return new NativeFunction(impl, 'bool', ['pointer', 'int'])(getApi().artHeap, type);
1903
- }
1904
- : () => false;
1905
- const kCollectorTypeCMC = 3;
1897
+ let copyingPhase = null;
1898
+ const api = getApi();
1899
+ if (apiLevel > 28) {
1900
+ copyingPhase = api.find('_ZN3art2gc9collector17ConcurrentCopying12CopyingPhaseEv');
1901
+ } else if (apiLevel > 22) {
1902
+ copyingPhase = api.find('_ZN3art2gc9collector17ConcurrentCopying12MarkingPhaseEv');
1903
+ }
1904
+ if (copyingPhase !== null) {
1905
+ Interceptor.attach(copyingPhase, artController.hooks.Gc.copyingPhase);
1906
+ }
1906
1907
 
1907
- if (mayUseCollector(kCollectorTypeCMC)) {
1908
- Interceptor.attach(Module.getExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0_b'), artController.hooks.Gc.runFlip);
1909
- } else {
1910
- let copyingPhase = null;
1911
- if (apiLevel > 28) {
1912
- copyingPhase = Module.findExportByName('libart.so', '_ZN3art2gc9collector17ConcurrentCopying12CopyingPhaseEv');
1913
- } else if (apiLevel > 22) {
1914
- copyingPhase = Module.findExportByName('libart.so', '_ZN3art2gc9collector17ConcurrentCopying12MarkingPhaseEv');
1915
- }
1916
- if (copyingPhase !== null) {
1917
- Interceptor.attach(copyingPhase, artController.hooks.Gc.copyingPhase);
1918
- }
1908
+ let runFlip = null;
1909
+ runFlip = api.find('_ZN3art6Thread15RunFlipFunctionEPS0_');
1910
+ if (runFlip === null) {
1911
+ runFlip = api.find('_ZN3art6Thread15RunFlipFunctionEPS0_b');
1912
+ }
1913
+ if (runFlip !== null) {
1914
+ Interceptor.attach(runFlip, artController.hooks.Gc.runFlip);
1919
1915
  }
1920
1916
  }
1921
1917
 
@@ -3414,7 +3410,7 @@ class ArtMethodMangler {
3414
3410
 
3415
3411
  // Replace Nterp quick entrypoints with art_quick_to_interpreter_bridge to force stepping out
3416
3412
  // of ART's next-generation interpreter and use the quick stub instead.
3417
- if (artNterpEntryPoint !== undefined && quickCode.equals(artNterpEntryPoint)) {
3413
+ if (artNterpEntryPoint !== null && quickCode.equals(artNterpEntryPoint)) {
3418
3414
  patchArtMethod(hookedMethodId, {
3419
3415
  quickCode: api.artQuickToInterpreterBridge
3420
3416
  }, vm);
@@ -3925,9 +3921,24 @@ const threadStateTransitionRecompilers = {
3925
3921
  };
3926
3922
 
3927
3923
  function makeArtThreadStateTransitionImpl (vm, env, callback) {
3924
+ const api = getApi();
3928
3925
  const envVtable = env.handle.readPointer();
3929
- const exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR).readPointer();
3930
- const nextFuncImpl = envVtable.add(ENV_VTABLE_OFFSET_FATAL_ERROR).readPointer();
3926
+
3927
+ let exceptionClearImpl;
3928
+ const innerExceptionClearImpl = api.find('_ZN3art3JNIILb1EE14ExceptionClearEP7_JNIEnv');
3929
+ if (innerExceptionClearImpl !== null) {
3930
+ exceptionClearImpl = innerExceptionClearImpl;
3931
+ } else {
3932
+ exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR).readPointer();
3933
+ }
3934
+
3935
+ let nextFuncImpl;
3936
+ const innerNextFuncImpl = api.find('_ZN3art3JNIILb1EE10FatalErrorEP7_JNIEnvPKc');
3937
+ if (innerNextFuncImpl !== null) {
3938
+ nextFuncImpl = innerNextFuncImpl;
3939
+ } else {
3940
+ nextFuncImpl = envVtable.add(ENV_VTABLE_OFFSET_FATAL_ERROR).readPointer();
3941
+ }
3931
3942
 
3932
3943
  const recompile = threadStateTransitionRecompilers[Process.arch];
3933
3944
  if (recompile === undefined) {
@@ -16,6 +16,8 @@ const {
16
16
  makeJniObjectTypeName
17
17
  } = require('./types');
18
18
 
19
+ const kAccStatic = 0x0008;
20
+
19
21
  const CONSTRUCTOR_METHOD = 1;
20
22
  const STATIC_METHOD = 2;
21
23
  const INSTANCE_METHOD = 3;
@@ -450,6 +452,9 @@ class ClassFactory {
450
452
  impl = methodValue;
451
453
  }
452
454
  } else {
455
+ if (methodValue.isStatic) {
456
+ type = STATIC_METHOD;
457
+ }
453
458
  returnType = this._getType(methodValue.returnType || 'void');
454
459
  argumentTypes = (methodValue.argumentTypes || []).map(name => this._getType(name));
455
460
  impl = methodValue.implementation;
@@ -479,7 +484,7 @@ class ClassFactory {
479
484
  const argumentTypeNames = argumentTypes.map(t => t.name);
480
485
  const signature = '(' + argumentTypeNames.join('') + ')' + returnTypeName;
481
486
 
482
- dexMethods.push([name, returnTypeName, argumentTypeNames, thrownTypeNames]);
487
+ dexMethods.push([name, returnTypeName, argumentTypeNames, thrownTypeNames, (type === STATIC_METHOD) ? kAccStatic : 0]);
483
488
  implMethods.push([name, signature, type, returnType, argumentTypes, impl]);
484
489
  });
485
490
 
@@ -566,15 +571,16 @@ class ClassFactory {
566
571
  const JVMTI_HEAP_OBJECT_EITHER = 3;
567
572
 
568
573
  const h = classWrapper.$borrowClassHandle(env);
574
+ const tag = int64(h.value.toString());
569
575
  try {
570
576
  const heapObjectCallback = new NativeCallback((classTag, size, tagPtr, userData) => {
571
- tagPtr.writePointer(h.value);
577
+ tagPtr.writeS64(tag);
572
578
  return JVMTI_ITERATION_CONTINUE;
573
- }, 'int', ['long', 'long', 'pointer', 'pointer']);
579
+ }, 'int', ['int64', 'int64', 'pointer', 'pointer']);
574
580
  jvmti.iterateOverInstancesOfClass(h.value, JVMTI_HEAP_OBJECT_EITHER, heapObjectCallback, h.value);
575
581
 
576
- const tagPtr = Memory.alloc(pointerSize);
577
- tagPtr.writePointer(h.value);
582
+ const tagPtr = Memory.alloc(8);
583
+ tagPtr.writeS64(tag);
578
584
  const countPtr = Memory.alloc(jsizeSize);
579
585
  const objectsPtr = Memory.alloc(pointerSize);
580
586
  jvmti.getObjectsWithTags(1, tagPtr, countPtr, objectsPtr, NULL);
package/lib/mkdex.js CHANGED
@@ -530,7 +530,7 @@ function computeModel (classes) {
530
530
  }
531
531
 
532
532
  klass.methods.forEach(method => {
533
- const [methodName, retType, argTypes, thrownTypes = []] = method;
533
+ const [methodName, retType, argTypes, thrownTypes = [], accessFlags] = method;
534
534
 
535
535
  strings.add(methodName);
536
536
 
@@ -563,13 +563,13 @@ function computeModel (classes) {
563
563
  strings.add('value');
564
564
  }
565
565
 
566
- methods.push([klass.name, protoId, methodName, throwsAnnotationId]);
566
+ methods.push([klass.name, protoId, methodName, throwsAnnotationId, accessFlags]);
567
567
 
568
568
  if (methodName === '<init>') {
569
569
  superConstructors.add(name + '|' + protoId);
570
570
  const superConstructorId = superClass + '|' + protoId;
571
571
  if (javaConstructors.has(name) && !superConstructors.has(superConstructorId)) {
572
- methods.push([superClass, protoId, methodName, null]);
572
+ methods.push([superClass, protoId, methodName, null, 0]);
573
573
  superConstructors.add(superConstructorId);
574
574
  }
575
575
  }
@@ -658,12 +658,13 @@ function computeModel (classes) {
658
658
  fieldItems.sort(compareFieldItems);
659
659
 
660
660
  const methodItems = methods.map(method => {
661
- const [klass, protoId, name, annotationsId] = method;
661
+ const [klass, protoId, name, annotationsId, accessFlags] = method;
662
662
  return [
663
663
  typeToIndex[klass],
664
664
  protoToIndex[protoId],
665
665
  stringToIndex[name],
666
- annotationsId
666
+ annotationsId,
667
+ accessFlags
667
668
  ];
668
669
  });
669
670
  methodItems.sort(compareMethodItems);
@@ -719,9 +720,9 @@ function computeModel (classes) {
719
720
  const sourceFileIndex = stringToIndex[klass.sourceFileName];
720
721
 
721
722
  const classMethods = methodItems.reduce((result, method, index) => {
722
- const [holder, protoIndex, name, annotationsId] = method;
723
+ const [holder, protoIndex, name, annotationsId, accessFlags] = method;
723
724
  if (holder === classIndex) {
724
- result.push([index, name, annotationsId, protoIndex]);
725
+ result.push([index, name, annotationsId, protoIndex, accessFlags]);
725
726
  }
726
727
  return result;
727
728
  }, []);
@@ -771,8 +772,8 @@ function computeModel (classes) {
771
772
  });
772
773
  const virtualMethods = compressClassMethodIndexes(classMethods
773
774
  .filter(([, name]) => name !== constructorNameIndex)
774
- .map(([index]) => {
775
- return [index, kAccPublic | kAccNative];
775
+ .map(([index, , , , accessFlags]) => {
776
+ return [index, accessFlags | kAccPublic | kAccNative];
776
777
  }));
777
778
 
778
779
  const classData = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frida-java-bridge",
3
- "version": "6.3.6",
3
+ "version": "6.3.7",
4
4
  "description": "Java runtime interop from Frida",
5
5
  "main": "index.js",
6
6
  "files": [