From 5d47892ea653fea57d3dfff9e7f9aedfe7719a0e Mon Sep 17 00:00:00 2001
From: matbrik <6418737+matbrik@users.noreply.github.com>
Date: Tue, 24 Sep 2024 13:11:29 +0000
Subject: [PATCH 01/10] android: alternative offset to ExceptionClear in libart
 (https://github.com/frida/frida/issues/2958)(https://github.com/frida/frida-java-bridge/issues/336)

---
 lib/android.js | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/lib/android.js b/lib/android.js
index f5dcfbe..03ba488 100644
--- a/lib/android.js
+++ b/lib/android.js
@@ -44,6 +44,7 @@ const X86_JMP_MAX_DISTANCE = 0x7fffbfff;
 const ARM64_ADRP_MAX_DISTANCE = 0xfffff000;
 
 const ENV_VTABLE_OFFSET_EXCEPTION_CLEAR = 17 * pointerSize;
+const ENV_VTABLE_OFFSET_EXCEPTION_CLEAR_ALT = 250 * pointerSize;
 const ENV_VTABLE_OFFSET_FATAL_ERROR = 18 * pointerSize;
 
 const DVM_JNI_ENV_OFFSET_SELF = 12;
@@ -259,8 +260,8 @@ function _getApi () {
 
           // Android < 6 for cloneArtMethod()
           _ZN3art6Thread14CurrentFromGdbEv: ['art::Thread::CurrentFromGdb', 'pointer', []],
-          _ZN3art6mirror6Object5CloneEPNS_6ThreadE: function (address) {
-            this['art::mirror::Object::Clone'] = new NativeFunction(address, 'pointer', ['pointer', 'pointer'], nativeFunctionOptions);
+            _ZN3art6mirror6Object5CloneEPNS_6ThreadE: function (address) {
+              this['art::mirror::Object::Clone'] = new NativeFunction(address, 'pointer', ['pointer', 'pointer'], nativeFunctionOptions);
           },
           _ZN3art6mirror6Object5CloneEPNS_6ThreadEm: function (address) {
             const clone = new NativeFunction(address, 'pointer', ['pointer', 'pointer', 'pointer'], nativeFunctionOptions);
@@ -3926,9 +3927,17 @@ const threadStateTransitionRecompilers = {
 
 function makeArtThreadStateTransitionImpl (vm, env, callback) {
   const envVtable = env.handle.readPointer();
-  const exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR).readPointer();
+  let exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR).readPointer();
   const nextFuncImpl = envVtable.add(ENV_VTABLE_OFFSET_FATAL_ERROR).readPointer();
-
+  if (Process.arch === 'arm64') {
+    const base = exceptionClearImpl;
+    const size = 0x150;
+    const pattern = "c0 03 5f d6"; ///ret
+    var res = Memory.scanSync(base, size, pattern)
+    if(res.length > 0){
+      exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR_ALT).readPointer();
+    }
+  }
   const recompile = threadStateTransitionRecompilers[Process.arch];
   if (recompile === undefined) {
     throw new Error('Not yet implemented for ' + Process.arch);

From a44980e0a15f7c3172332027e3e6b0c333c28ce0 Mon Sep 17 00:00:00 2001
From: matbrik <6418737+matbrik@users.noreply.github.com>
Date: Tue, 24 Sep 2024 13:32:59 +0000
Subject: [PATCH 02/10] fix lint errors

---
 lib/android.js | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/lib/android.js b/lib/android.js
index 03ba488..6db17bd 100644
--- a/lib/android.js
+++ b/lib/android.js
@@ -260,8 +260,8 @@ function _getApi () {
 
           // Android < 6 for cloneArtMethod()
           _ZN3art6Thread14CurrentFromGdbEv: ['art::Thread::CurrentFromGdb', 'pointer', []],
-            _ZN3art6mirror6Object5CloneEPNS_6ThreadE: function (address) {
-              this['art::mirror::Object::Clone'] = new NativeFunction(address, 'pointer', ['pointer', 'pointer'], nativeFunctionOptions);
+          _ZN3art6mirror6Object5CloneEPNS_6ThreadE: function (address) {
+            this['art::mirror::Object::Clone'] = new NativeFunction(address, 'pointer', ['pointer', 'pointer'], nativeFunctionOptions);
           },
           _ZN3art6mirror6Object5CloneEPNS_6ThreadEm: function (address) {
             const clone = new NativeFunction(address, 'pointer', ['pointer', 'pointer', 'pointer'], nativeFunctionOptions);
@@ -3930,11 +3930,9 @@ function makeArtThreadStateTransitionImpl (vm, env, callback) {
   let exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR).readPointer();
   const nextFuncImpl = envVtable.add(ENV_VTABLE_OFFSET_FATAL_ERROR).readPointer();
   if (Process.arch === 'arm64') {
-    const base = exceptionClearImpl;
     const size = 0x150;
-    const pattern = "c0 03 5f d6"; ///ret
-    var res = Memory.scanSync(base, size, pattern)
-    if(res.length > 0){
+    const pattern = 'c0 03 5f d6'; // ret
+    if (Memory.scanSync(exceptionClearImpl, size, pattern).length > 0) {
       exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR_ALT).readPointer();
     }
   }

From 96ee9c4c7e0e6b233c8434ac6a40f9dcebdba13d Mon Sep 17 00:00:00 2001
From: matbrik <6418737+matbrik@users.noreply.github.com>
Date: Wed, 25 Sep 2024 09:52:37 +0000
Subject: [PATCH 03/10] android: handle change of signature of runFlip

---
 lib/android.js | 38 ++++++++++++++++----------------------
 1 file changed, 16 insertions(+), 22 deletions(-)

diff --git a/lib/android.js b/lib/android.js
index 6db17bd..1317e73 100644
--- a/lib/android.js
+++ b/lib/android.js
@@ -1894,29 +1894,23 @@ function ensureArtKnowsHowToHandleReplacementMethods (vm) {
 
   const apiLevel = getAndroidApiLevel();
 
-  const mayUseCollector = (apiLevel > 28)
-    ? (type) => {
-        const impl = Module.findExportByName('libart.so', '_ZNK3art2gc4Heap15MayUseCollectorENS0_13CollectorTypeE');
-        if (impl === null) {
-          return false;
-        }
-        return new NativeFunction(impl, 'bool', ['pointer', 'int'])(getApi().artHeap, type);
-      }
-    : () => false;
-  const kCollectorTypeCMC = 3;
+  let copyingPhase = null;
+  if (apiLevel > 28) {
+    copyingPhase = Module.findExportByName('libart.so', '_ZN3art2gc9collector17ConcurrentCopying12CopyingPhaseEv');
+  } else if (apiLevel > 22) {
+    copyingPhase = Module.findExportByName('libart.so', '_ZN3art2gc9collector17ConcurrentCopying12MarkingPhaseEv');
+  }
+  if (copyingPhase !== null) {
+    Interceptor.attach(copyingPhase, artController.hooks.Gc.copyingPhase);
+  }
 
-  if (mayUseCollector(kCollectorTypeCMC)) {
-    Interceptor.attach(Module.getExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0_b'), artController.hooks.Gc.runFlip);
-  } else {
-    let copyingPhase = null;
-    if (apiLevel > 28) {
-      copyingPhase = Module.findExportByName('libart.so', '_ZN3art2gc9collector17ConcurrentCopying12CopyingPhaseEv');
-    } else if (apiLevel > 22) {
-      copyingPhase = Module.findExportByName('libart.so', '_ZN3art2gc9collector17ConcurrentCopying12MarkingPhaseEv');
-    }
-    if (copyingPhase !== null) {
-      Interceptor.attach(copyingPhase, artController.hooks.Gc.copyingPhase);
-    }
+  let runFlip = null;
+  runFlip = Module.findExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0_b');
+  if (runFlip === null) {
+    runFlip = Module.findExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0'); // api 35
+  }
+  if (runFlip !== null) {
+    Interceptor.attach(runFlip, artController.hooks.Gc.runFlip);
   }
 }
 

From 807c7f00689e4e506aa8711684e6ba431f719c66 Mon Sep 17 00:00:00 2001
From: matbrik <6418737+matbrik@users.noreply.github.com>
Date: Tue, 29 Oct 2024 13:08:42 +0000
Subject: [PATCH 04/10] fix stripped libart

---
 lib/android.js | 147 +++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 135 insertions(+), 12 deletions(-)

diff --git a/lib/android.js b/lib/android.js
index 1317e73..5483742 100644
--- a/lib/android.js
+++ b/lib/android.js
@@ -503,7 +503,9 @@ function _getApi () {
     if (temporaryApi['art::interpreter::GetNterpEntryPoint'] !== undefined) {
       temporaryApi.artNterpEntryPoint = temporaryApi['art::interpreter::GetNterpEntryPoint']();
     }
-
+    else {   
+      temporaryApi.artNterpEntryPoint = findExecuteNterpImpl();
+    }
     artController = makeArtController(vm);
 
     fixupArtQuickDeliverExceptionBug(temporaryApi);
@@ -533,18 +535,116 @@ function _getApi () {
   return temporaryApi;
 }
 
+function stringToBytesHex(str) {
+  let bytes = [];
+  for (let i = 0; i < str.length; i++) {
+      let byteHex = str.charCodeAt(i).toString(16).toUpperCase();
+      if (byteHex.length === 1) {
+          byteHex = '0' + byteHex;
+      }
+      bytes.push(byteHex);
+  }
+  return bytes.join(' ');
+}
+
+function findString(string, moduleName){
+  const module = Process.findModuleByName(moduleName);
+  const ranges = module.enumerateRanges('r--');
+  const pattern = stringToBytesHex(string);
+  return ranges.map((range) => {
+      const base = range.base;
+      const size = range.size;
+      const result =  Memory.scanSync(base, size, pattern);
+      if (result.length>0) return result;
+      return;
+  }).filter((element => element != null)).flat();
+}
+
+function createAdd(address, register){
+  return ((0x244<<12)+(address&0xfff)<<10)+(register<<5)+register;
+}
+
+function getPattern(instr){
+ return ptr(instr).toMatchPattern().replace('ff ff ff ff', '');
+}
+
+function findPatternInModule(pattern, moduleName){
+  const module = Process.findModuleByName(moduleName);
+  const ranges = module.enumerateRanges('r-x');
+  return ranges.map((range) => {
+      const base = range.base;
+      const size = range.size;
+      var result = Memory.scanSync(base, size, pattern);
+      if (result.length==0) return;
+      return result;
+  }).filter((element => element!=null)).flat();
+}
+
+
+function findEnsurePlugIn(){
+  const results = findString('libopenjdkjvmti.so', 'libart.so');
+  const pattern  = getPattern(createAdd(results[0].address,1));
+  const pattern_results = findPatternInModule(pattern, 'libart.so');
+  return pattern_results.map((match) => {
+      const possibleBranch = match.address.add(4);
+      const disasm = Instruction.parse(possibleBranch);
+      if (disasm.mnemonic=='b')
+      {
+        return new NativePointer(disasm.operands[0].value);
+      }
+      return;
+  }).filter((element => element!=null)).flat();
+}
+
+function findPrologue(address){
+  for (let off = 0;; off += 4) {
+    let disasm = Instruction.parse(address.sub(0x4).sub(off));
+    if (disasm.mnemonic === 'str') {
+      disasm = Instruction.parse(disasm.next);
+      if (disasm.mnemonic === 'stp') {
+        return disasm.address.sub(0x4);
+      }
+    }
+  }
+}
+
+function findExecuteNterpImpl(){
+  let artMethodCopyfrom = Module.findExportByName('libart.so', '_ZN3art9ArtMethod8CopyFromEPS0_NS_11PointerSizeE')
+  for (let off = 0; off<0x300; off+=4){
+    let disasm = Instruction.parse(artMethodCopyfrom.add(off));
+    let next_instr = Instruction.parse(artMethodCopyfrom.add(off).add(0x4));
+    if (disasm.mnemonic === 'adrp' && next_instr.mnemonic === 'add'){
+      const base = ptr(disasm.operands[1].value);
+      const offset = next_instr.operands[2].value;
+      const result = base.add(offset);
+      const dest = Instruction.parse(result);
+      if (dest.mnemonic === 'sub')
+        return result
+    }
+  }
+}
+
 function tryGetEnvJvmti (vm, runtime) {
   let env = null;
 
   vm.perform(() => {
-    const ensurePluginLoaded = new NativeFunction(
-      Module.getExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE'),
+    let ensurePluginLoaded;
+    if (Module.findExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE')===null){
+      // on some devices calling the ensurePluginLoaded make the app really slow so for is commented
+      return;
+      const candidates = findEnsurePlugIn();
+      ensurePluginLoaded = new NativeFunction(candidates[0], 
+      'bool',
+      ['pointer', 'pointer', 'pointer']);
+    }else{
+      ensurePluginLoaded = new NativeFunction(Module.getExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE'),
       'bool',
       ['pointer', 'pointer', 'pointer']);
+    }
     const errorPtr = Memory.alloc(pointerSize);
     const success = ensurePluginLoaded(runtime, Memory.allocUtf8String('libopenjdkjvmti.so'), errorPtr);
     if (!success) {
-      // FIXME: Avoid leaking error
+    // FIXME: Avoid leaking error
       return;
     }
 
@@ -624,7 +724,7 @@ function _getArtRuntimeSpec (api) {
 
   const apiLevel = getAndroidApiLevel();
   const codename = getAndroidCodename();
-  const isApiLevel34OrApexEquivalent = Module.findExportByName('libart.so', '_ZN3art7AppInfo29GetPrimaryApkReferenceProfileEv') !== null;
+  const isApiLevel34OrApexEquivalent = Module.findExportByName('libart.so', '_ZN3art7AppInfo29GetPrimaryApkReferenceProfileEv') !== null || Module.findExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0_') !== null;
 
   let spec = null;
 
@@ -1853,6 +1953,25 @@ function instrumentArtQuickEntrypoints (vm) {
   });
 }
 
+function findDoCalls(){
+  const results = findString('Invoking %s with bad arg %d', 'libart.so');
+  const pattern  = getPattern(createAdd(results[0].address,2));
+  const pattern_results = findPatternInModule(pattern, 'libart.so');
+
+  return pattern_results.map((match) => {
+      const possibleBranch = match.address;
+      const instr = Instruction.parse(possibleBranch.sub(4));
+      if (instr.mnemonic=='adrp')
+      {
+        if (new NativePointer(instr.operands[1].value).equals(results[0].address.and(0xfffffffffff000)))
+        {
+          return findPrologue(possibleBranch);
+        }
+      }
+      return
+  }).filter((element => element!=null)).flat();
+}
+
 function instrumentArtMethodInvocationFromInterpreter () {
   const apiLevel = getAndroidApiLevel();
 
@@ -1865,9 +1984,17 @@ function instrumentArtMethodInvocationFromInterpreter () {
     artInterpreterDoCallExportRegex = /^_ZN3art11interpreter6DoCallILb[0-1]EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtbPNS_6JValueE$/;
   }
 
+  let need_docall = true;
   for (const exp of Module.enumerateExports('libart.so').filter(exp => artInterpreterDoCallExportRegex.test(exp.name))) {
+    need_docall = false;
     Interceptor.attach(exp.address, artController.hooks.Interpreter.doCall);
   }
+  if(need_docall & Process.arch === 'arm64'){
+    const doCallsCandidates = findDoCalls();
+    for(const address of doCallsCandidates){
+      Interceptor.attach(address, artController.hooks.Interpreter.doCall);
+    }
+  }
 }
 
 function ensureArtKnowsHowToHandleReplacementMethods (vm) {
@@ -1907,7 +2034,7 @@ function ensureArtKnowsHowToHandleReplacementMethods (vm) {
   let runFlip = null;
   runFlip = Module.findExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0_b');
   if (runFlip === null) {
-    runFlip = Module.findExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0'); // api 35
+    runFlip = Module.findExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0_'); // api 35
   }
   if (runFlip !== null) {
     Interceptor.attach(runFlip, artController.hooks.Gc.runFlip);
@@ -3923,12 +4050,8 @@ function makeArtThreadStateTransitionImpl (vm, env, callback) {
   const envVtable = env.handle.readPointer();
   let exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR).readPointer();
   const nextFuncImpl = envVtable.add(ENV_VTABLE_OFFSET_FATAL_ERROR).readPointer();
-  if (Process.arch === 'arm64') {
-    const size = 0x150;
-    const pattern = 'c0 03 5f d6'; // ret
-    if (Memory.scanSync(exceptionClearImpl, size, pattern).length > 0) {
-      exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR_ALT).readPointer();
-    }
+  if (Module.findExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0_')){
+    exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR_ALT).readPointer();
   }
   const recompile = threadStateTransitionRecompilers[Process.arch];
   if (recompile === undefined) {

From f610262d6766b69763198d78c63b1df3a6a9e374 Mon Sep 17 00:00:00 2001
From: matbrik <6418737+matbrik@users.noreply.github.com>
Date: Tue, 29 Oct 2024 13:36:58 +0000
Subject: [PATCH 05/10] lint errors

---
 lib/android.js | 148 +++++++++++++++++++++++--------------------------
 1 file changed, 70 insertions(+), 78 deletions(-)

diff --git a/lib/android.js b/lib/android.js
index 5483742..fc51b64 100644
--- a/lib/android.js
+++ b/lib/android.js
@@ -502,8 +502,7 @@ function _getApi () {
     }
     if (temporaryApi['art::interpreter::GetNterpEntryPoint'] !== undefined) {
       temporaryApi.artNterpEntryPoint = temporaryApi['art::interpreter::GetNterpEntryPoint']();
-    }
-    else {   
+    } else {
       temporaryApi.artNterpEntryPoint = findExecuteNterpImpl();
     }
     artController = makeArtController(vm);
@@ -535,69 +534,64 @@ function _getApi () {
   return temporaryApi;
 }
 
-function stringToBytesHex(str) {
-  let bytes = [];
+function stringToBytesHex (str) {
+  const bytes = [];
   for (let i = 0; i < str.length; i++) {
-      let byteHex = str.charCodeAt(i).toString(16).toUpperCase();
-      if (byteHex.length === 1) {
-          byteHex = '0' + byteHex;
-      }
-      bytes.push(byteHex);
+    let byteHex = str.charCodeAt(i).toString(16).toUpperCase();
+    if (byteHex.length === 1) {
+      byteHex = '0' + byteHex;
+    }
+    bytes.push(byteHex);
   }
   return bytes.join(' ');
 }
 
-function findString(string, moduleName){
+function findString (string, moduleName) {
   const module = Process.findModuleByName(moduleName);
   const ranges = module.enumerateRanges('r--');
   const pattern = stringToBytesHex(string);
   return ranges.map((range) => {
-      const base = range.base;
-      const size = range.size;
-      const result =  Memory.scanSync(base, size, pattern);
-      if (result.length>0) return result;
-      return;
+    const base = range.base;
+    const size = range.size;
+    const result = Memory.scanSync(base, size, pattern);
+    if (result.length > 0) return result;
   }).filter((element => element != null)).flat();
 }
 
-function createAdd(address, register){
-  return ((0x244<<12)+(address&0xfff)<<10)+(register<<5)+register;
+function createAdd (address, register) {
+  return (0x244 << 12) + (address & 0xfff) << 10 + register << 5 + register;
 }
 
-function getPattern(instr){
- return ptr(instr).toMatchPattern().replace('ff ff ff ff', '');
+function getPattern (instr) {
+  return ptr(instr).toMatchPattern().replace('ff ff ff ff', '');
 }
 
-function findPatternInModule(pattern, moduleName){
+function findPatternInModule (pattern, moduleName) {
   const module = Process.findModuleByName(moduleName);
   const ranges = module.enumerateRanges('r-x');
   return ranges.map((range) => {
-      const base = range.base;
-      const size = range.size;
-      var result = Memory.scanSync(base, size, pattern);
-      if (result.length==0) return;
-      return result;
-  }).filter((element => element!=null)).flat();
+    const base = range.base;
+    const size = range.size;
+    const result = Memory.scanSync(base, size, pattern);
+    if (result.length != 0) return result;
+  }).filter(element => element != null).flat();
 }
 
-
-function findEnsurePlugIn(){
+function findEnsurePlugIn () {
   const results = findString('libopenjdkjvmti.so', 'libart.so');
-  const pattern  = getPattern(createAdd(results[0].address,1));
-  const pattern_results = findPatternInModule(pattern, 'libart.so');
-  return pattern_results.map((match) => {
-      const possibleBranch = match.address.add(4);
-      const disasm = Instruction.parse(possibleBranch);
-      if (disasm.mnemonic=='b')
-      {
-        return new NativePointer(disasm.operands[0].value);
-      }
-      return;
-  }).filter((element => element!=null)).flat();
+  const pattern = getPattern(createAdd(results[0].address, 1));
+  const patternResults = findPatternInModule(pattern, 'libart.so');
+  return patternResults.map((match) => {
+    const possibleBranch = match.address.add(4);
+    const disasm = Instruction.parse(possibleBranch);
+    if (disasm.mnemonic === 'b') {
+      return new NativePointer(disasm.operands[0].value);
+    }
+  }).filter((element => element != null)).flat();
 }
 
-function findPrologue(address){
-  for (let off = 0;; off += 4) {
+function findPrologue (address) {
+  for (let off = 0; ; off += 4) {
     let disasm = Instruction.parse(address.sub(0x4).sub(off));
     if (disasm.mnemonic === 'str') {
       disasm = Instruction.parse(disasm.next);
@@ -608,18 +602,19 @@ function findPrologue(address){
   }
 }
 
-function findExecuteNterpImpl(){
-  let artMethodCopyfrom = Module.findExportByName('libart.so', '_ZN3art9ArtMethod8CopyFromEPS0_NS_11PointerSizeE')
-  for (let off = 0; off<0x300; off+=4){
-    let disasm = Instruction.parse(artMethodCopyfrom.add(off));
-    let next_instr = Instruction.parse(artMethodCopyfrom.add(off).add(0x4));
-    if (disasm.mnemonic === 'adrp' && next_instr.mnemonic === 'add'){
+function findExecuteNterpImpl () {
+  const artMethodCopyfrom = Module.findExportByName('libart.so', '_ZN3art9ArtMethod8CopyFromEPS0_NS_11PointerSizeE');
+  for (let off = 0; off < 0x300; off += 4) {
+    const disasm = Instruction.parse(artMethodCopyfrom.add(off));
+    const nextInstr = Instruction.parse(artMethodCopyfrom.add(off).add(0x4));
+    if (disasm.mnemonic === 'adrp' && nextInstr.mnemonic === 'add') {
       const base = ptr(disasm.operands[1].value);
-      const offset = next_instr.operands[2].value;
+      const offset = nextInstr.operands[2].value;
       const result = base.add(offset);
       const dest = Instruction.parse(result);
-      if (dest.mnemonic === 'sub')
-        return result
+      if (dest.mnemonic === 'sub') {
+        return result;
+      }
     }
   }
 }
@@ -629,17 +624,17 @@ function tryGetEnvJvmti (vm, runtime) {
 
   vm.perform(() => {
     let ensurePluginLoaded;
-    if (Module.findExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE')===null){
+    if (Module.findExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE') === null) {
       // on some devices calling the ensurePluginLoaded make the app really slow so for is commented
       return;
-      const candidates = findEnsurePlugIn();
-      ensurePluginLoaded = new NativeFunction(candidates[0], 
-      'bool',
-      ['pointer', 'pointer', 'pointer']);
-    }else{
+      // const candidates = findEnsurePlugIn();
+      // ensurePluginLoaded = new NativeFunction(candidates[0],
+      // 'bool',
+      // ['pointer', 'pointer', 'pointer']);
+    } else {
       ensurePluginLoaded = new NativeFunction(Module.getExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE'),
-      'bool',
-      ['pointer', 'pointer', 'pointer']);
+        'bool',
+        ['pointer', 'pointer', 'pointer']);
     }
     const errorPtr = Memory.alloc(pointerSize);
     const success = ensurePluginLoaded(runtime, Memory.allocUtf8String('libopenjdkjvmti.so'), errorPtr);
@@ -1953,23 +1948,20 @@ function instrumentArtQuickEntrypoints (vm) {
   });
 }
 
-function findDoCalls(){
+function findDoCalls () {
   const results = findString('Invoking %s with bad arg %d', 'libart.so');
-  const pattern  = getPattern(createAdd(results[0].address,2));
-  const pattern_results = findPatternInModule(pattern, 'libart.so');
-
-  return pattern_results.map((match) => {
-      const possibleBranch = match.address;
-      const instr = Instruction.parse(possibleBranch.sub(4));
-      if (instr.mnemonic=='adrp')
-      {
-        if (new NativePointer(instr.operands[1].value).equals(results[0].address.and(0xfffffffffff000)))
-        {
-          return findPrologue(possibleBranch);
-        }
+  const pattern = getPattern(createAdd(results[0].address, 2));
+  const patternResults = findPatternInModule(pattern, 'libart.so');
+
+  return patternResults.map((match) => {
+    const possibleBranch = match.address;
+    const instr = Instruction.parse(possibleBranch.sub(4));
+    if (instr.mnemonic === 'adrp') {
+      if (new NativePointer(instr.operands[1].value).equals(results[0].address.and(0xfffffffffff000))) {
+        return findPrologue(possibleBranch);
       }
-      return
-  }).filter((element => element!=null)).flat();
+    }
+  }).filter(element => element!=null).flat();
 }
 
 function instrumentArtMethodInvocationFromInterpreter () {
@@ -1984,14 +1976,14 @@ function instrumentArtMethodInvocationFromInterpreter () {
     artInterpreterDoCallExportRegex = /^_ZN3art11interpreter6DoCallILb[0-1]EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtbPNS_6JValueE$/;
   }
 
-  let need_docall = true;
+  let docallFound = true;
   for (const exp of Module.enumerateExports('libart.so').filter(exp => artInterpreterDoCallExportRegex.test(exp.name))) {
-    need_docall = false;
+    docallFound = false;
     Interceptor.attach(exp.address, artController.hooks.Interpreter.doCall);
   }
-  if(need_docall & Process.arch === 'arm64'){
+  if (docallFound & Process.arch === 'arm64') {
     const doCallsCandidates = findDoCalls();
-    for(const address of doCallsCandidates){
+    for (const address of doCallsCandidates) {
       Interceptor.attach(address, artController.hooks.Interpreter.doCall);
     }
   }
@@ -4050,7 +4042,7 @@ function makeArtThreadStateTransitionImpl (vm, env, callback) {
   const envVtable = env.handle.readPointer();
   let exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR).readPointer();
   const nextFuncImpl = envVtable.add(ENV_VTABLE_OFFSET_FATAL_ERROR).readPointer();
-  if (Module.findExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0_')){
+  if (Module.findExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0_')) {
     exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR_ALT).readPointer();
   }
   const recompile = threadStateTransitionRecompilers[Process.arch];

From 8e0c6c5933e55d83aa8ce6ff259475cc67214e91 Mon Sep 17 00:00:00 2001
From: matbrik <6418737+matbrik@users.noreply.github.com>
Date: Tue, 29 Oct 2024 13:41:44 +0000
Subject: [PATCH 06/10] fix lint error

---
 lib/android.js | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/lib/android.js b/lib/android.js
index fc51b64..a2f404f 100644
--- a/lib/android.js
+++ b/lib/android.js
@@ -555,7 +555,8 @@ function findString (string, moduleName) {
     const size = range.size;
     const result = Memory.scanSync(base, size, pattern);
     if (result.length > 0) return result;
-  }).filter((element => element != null)).flat();
+    return null;
+  }).filter(element => element !== null).flat();
 }
 
 function createAdd (address, register) {
@@ -574,6 +575,7 @@ function findPatternInModule (pattern, moduleName) {
     const size = range.size;
     const result = Memory.scanSync(base, size, pattern);
     if (result.length != 0) return result;
+    return null;
   }).filter(element => element != null).flat();
 }
 
@@ -587,7 +589,7 @@ function findEnsurePlugIn () {
     if (disasm.mnemonic === 'b') {
       return new NativePointer(disasm.operands[0].value);
     }
-  }).filter((element => element != null)).flat();
+  }).filter(element => element !== null).flat();
 }
 
 function findPrologue (address) {
@@ -626,11 +628,13 @@ function tryGetEnvJvmti (vm, runtime) {
     let ensurePluginLoaded;
     if (Module.findExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE') === null) {
       // on some devices calling the ensurePluginLoaded make the app really slow so for is commented
+      if (false) {
+       const candidates = findEnsurePlugIn();
+       ensurePluginLoaded = new NativeFunction(candidates[0],
+        'bool',
+        ['pointer', 'pointer', 'pointer']);
+      }
       return;
-      // const candidates = findEnsurePlugIn();
-      // ensurePluginLoaded = new NativeFunction(candidates[0],
-      // 'bool',
-      // ['pointer', 'pointer', 'pointer']);
     } else {
       ensurePluginLoaded = new NativeFunction(Module.getExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE'),
         'bool',
@@ -1961,7 +1965,8 @@ function findDoCalls () {
         return findPrologue(possibleBranch);
       }
     }
-  }).filter(element => element!=null).flat();
+    return null;
+  }).filter(element => element !== null).flat();
 }
 
 function instrumentArtMethodInvocationFromInterpreter () {

From 513b09ffca6e17a9877c319006e57b5a011ea663 Mon Sep 17 00:00:00 2001
From: matbrik <6418737+matbrik@users.noreply.github.com>
Date: Tue, 29 Oct 2024 13:49:31 +0000
Subject: [PATCH 07/10] fix lint error

---
 lib/android.js | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/lib/android.js b/lib/android.js
index a2f404f..01c30ee 100644
--- a/lib/android.js
+++ b/lib/android.js
@@ -574,9 +574,9 @@ function findPatternInModule (pattern, moduleName) {
     const base = range.base;
     const size = range.size;
     const result = Memory.scanSync(base, size, pattern);
-    if (result.length != 0) return result;
+    if (result.length !== 0) return result;
     return null;
-  }).filter(element => element != null).flat();
+  }).filter(element => element !== null).flat();
 }
 
 function findEnsurePlugIn () {
@@ -589,6 +589,7 @@ function findEnsurePlugIn () {
     if (disasm.mnemonic === 'b') {
       return new NativePointer(disasm.operands[0].value);
     }
+    return null;
   }).filter(element => element !== null).flat();
 }
 
@@ -628,11 +629,11 @@ function tryGetEnvJvmti (vm, runtime) {
     let ensurePluginLoaded;
     if (Module.findExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE') === null) {
       // on some devices calling the ensurePluginLoaded make the app really slow so for is commented
-      if (false) {
-       const candidates = findEnsurePlugIn();
-       ensurePluginLoaded = new NativeFunction(candidates[0],
-        'bool',
-        ['pointer', 'pointer', 'pointer']);
+      if (env !== null) {
+        const candidates = findEnsurePlugIn();
+        ensurePluginLoaded = new NativeFunction(candidates[0],
+          'bool',
+          ['pointer', 'pointer', 'pointer']);
       }
       return;
     } else {

From 395f2ac4a3dec61dee6b0be758302925f6b06dad Mon Sep 17 00:00:00 2001
From: matbrik <6418737+matbrik@users.noreply.github.com>
Date: Tue, 29 Oct 2024 14:11:20 +0000
Subject: [PATCH 08/10] narrow fix to arm64

---
 lib/android.js | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/lib/android.js b/lib/android.js
index 01c30ee..2a69a92 100644
--- a/lib/android.js
+++ b/lib/android.js
@@ -503,7 +503,8 @@ function _getApi () {
     if (temporaryApi['art::interpreter::GetNterpEntryPoint'] !== undefined) {
       temporaryApi.artNterpEntryPoint = temporaryApi['art::interpreter::GetNterpEntryPoint']();
     } else {
-      temporaryApi.artNterpEntryPoint = findExecuteNterpImpl();
+      if (Process.arch === 'arm64')
+        temporaryApi.artNterpEntryPoint = findExecuteNterpImpl();
     }
     artController = makeArtController(vm);
 
@@ -629,7 +630,7 @@ function tryGetEnvJvmti (vm, runtime) {
     let ensurePluginLoaded;
     if (Module.findExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE') === null) {
       // on some devices calling the ensurePluginLoaded make the app really slow so for is commented
-      if (env !== null) {
+      if (env !== null & Process.arch === 'arm64') {
         const candidates = findEnsurePlugIn();
         ensurePluginLoaded = new NativeFunction(candidates[0],
           'bool',

From ce9e0ee694aaeb12ab08517fc5f5ce55dcf58029 Mon Sep 17 00:00:00 2001
From: matbrik <6418737+matbrik@users.noreply.github.com>
Date: Tue, 29 Oct 2024 14:13:16 +0000
Subject: [PATCH 09/10] fix lint error

---
 lib/android.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/android.js b/lib/android.js
index 2a69a92..cac8002 100644
--- a/lib/android.js
+++ b/lib/android.js
@@ -503,8 +503,9 @@ function _getApi () {
     if (temporaryApi['art::interpreter::GetNterpEntryPoint'] !== undefined) {
       temporaryApi.artNterpEntryPoint = temporaryApi['art::interpreter::GetNterpEntryPoint']();
     } else {
-      if (Process.arch === 'arm64')
+      if (Process.arch === 'arm64') {
         temporaryApi.artNterpEntryPoint = findExecuteNterpImpl();
+      }
     }
     artController = makeArtController(vm);
 

From 9636722e508b247a5509d9fa69ba6c87bf64bdd8 Mon Sep 17 00:00:00 2001
From: matbrik <6418737+matbrik@users.noreply.github.com>
Date: Wed, 30 Oct 2024 16:39:12 +0000
Subject: [PATCH 10/10] nterp from android 11

---
 lib/android.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/android.js b/lib/android.js
index cac8002..910c873 100644
--- a/lib/android.js
+++ b/lib/android.js
@@ -503,7 +503,7 @@ function _getApi () {
     if (temporaryApi['art::interpreter::GetNterpEntryPoint'] !== undefined) {
       temporaryApi.artNterpEntryPoint = temporaryApi['art::interpreter::GetNterpEntryPoint']();
     } else {
-      if (Process.arch === 'arm64') {
+      if (Process.arch === 'arm64' && getAndroidApiLevel() >= 30) {
         temporaryApi.artNterpEntryPoint = findExecuteNterpImpl();
       }
     }
@@ -631,7 +631,7 @@ function tryGetEnvJvmti (vm, runtime) {
     let ensurePluginLoaded;
     if (Module.findExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE') === null) {
       // on some devices calling the ensurePluginLoaded make the app really slow so for is commented
-      if (env !== null & Process.arch === 'arm64') {
+      if (env !== null && Process.arch === 'arm64') {
         const candidates = findEnsurePlugIn();
         ensurePluginLoaded = new NativeFunction(candidates[0],
           'bool',
@@ -1989,7 +1989,7 @@ function instrumentArtMethodInvocationFromInterpreter () {
     docallFound = false;
     Interceptor.attach(exp.address, artController.hooks.Interpreter.doCall);
   }
-  if (docallFound & Process.arch === 'arm64') {
+  if (docallFound && Process.arch === 'arm64') {
     const doCallsCandidates = findDoCalls();
     for (const address of doCallsCandidates) {
       Interceptor.attach(address, artController.hooks.Interpreter.doCall);
@@ -3536,7 +3536,7 @@ class ArtMethodMangler {
 
     // Replace Nterp quick entrypoints with art_quick_to_interpreter_bridge to force stepping out
     // of ART's next-generation interpreter and use the quick stub instead.
-    if (artNterpEntryPoint !== undefined && quickCode.equals(artNterpEntryPoint)) {
+    if (artNterpEntryPoint !== undefined && artNterpEntryPoint !== 0 && quickCode.equals(artNterpEntryPoint)) {
       patchArtMethod(hookedMethodId, {
         quickCode: api.artQuickToInterpreterBridge
       }, vm);