Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

android: alternative offset to ExceptionClear in libart (https://github.com/frida/frida/issues/2958)(https://github.com/frida/frida-java-bridge/issues/336) #337

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

matbrik
Copy link

@matbrik matbrik commented Sep 24, 2024

In the latest libart versions (35xxxxxxx) the offset in the vtable of venv for ExceptionClear is not valid anymore.
I added a check through an heuristic to detect if the found function is the correct one or if the new offset is needed.

The commit is only for arm64, a fix for other architectures may be needed.

Tested on libart:

350820380
350820960
350820860

@nilathedragon
Copy link

On Android 14, libart 350820960 the error goes away when starting the server but when trying to run an app with frida attached, the phone soft-reboots after a few seconds. (The app did actually spawn).

Were you able to spawn and attach with this PR?

@matbrik
Copy link
Author

matbrik commented Sep 25, 2024

On Android 14, libart 350820960 the error goes away when starting the server but when trying to run an app with frida attached, the phone soft-reboots after a few seconds. (The app did actually spawn).

Were you able to spawn and attach with this PR?

I tried only with frida-inject and I was working fine.
With frida-server there is a problem with a function (RunFlip) that changed signature from art::Thread::RunFlipFunction(art::Thread*,bool) to art::Thread::RunFlipFunction(art::Thread*)

In this commit android: handle change of signature of runFlip:

  • I changed the getExportByName which throws an exception with findExportByName
  • noticing that in devices using ConcurrentCopying with the recent versions of libart I had stability problems similar to android: Handle correctly the CMC GC strategy #326 I extended the hook of runFlip to a generic case and removed the MayUseCollector check

@thinhbuzz
Copy link
Contributor

It is confirmed that this patch works on com.android.art@350820860 - Android 13.

@thinhbuzz
Copy link
Contributor

@matbrik It has issues on older devices or those without com.android.art for example as shown below.
image

image

@matbrik
Copy link
Author

matbrik commented Oct 2, 2024

@matbrik It has issues on older devices or those without com.android.art for example as shown below. image

can you provide more details and logs on the error?

@thinhbuzz
Copy link
Contributor

@matbrik It has issues on older devices or those without com.android.art for example as shown below. image

can you provide more details and logs on the error?

This is sample information, you can run it and try. If you need more information, give me your Discord or Telegram, we can discuss further.
eg error:

{
    "type": "error",
    "description": "Error: access violation accessing 0x7cff553fd0",
    "stack": "Error: access violation accessing 0x7cff553fd0\n    at Ln (frida/node_modules/frida-java-bridge/lib/android.js:1617:1)\n    at kt (frida/node_modules/frida-java-bridge/lib/android.js:582:1)\n    at frida/node_modules/frida-java-bridge/lib/memoize.js:4:1\n    at vt (frida/node_modules/frida-java-bridge/lib/android.js:577:1)\n    at frida/node_modules/frida-java-bridge/lib/class-model.js:112:1\n    at Function.build (frida/node_modules/frida-java-bridge/lib/class-model.js:7:1)\n    at k._make (frida/node_modules/frida-java-bridge/lib/class-factory.js:168:1)\n    at k.use (frida/node_modules/frida-java-bridge/lib/class-factory.js:62:1)\n    at frida/node_modules/frida-java-bridge/index.js:224:1\n    at c.perform (frida/node_modules/frida-java-bridge/lib/vm.js:12:1)",
    "fileName": "frida/node_modules/frida-java-bridge/lib/android.js",
    "lineNumber": 1617,
    "columnNumber": 1
}

eg code:

import Wrapper = Java.Wrapper;

Java.perform(() => {
  try {
    const application = Java.use('android.app.ActivityThread').currentApplication() as (Wrapper | null);
    if (!application) {
      console.log('initBroadcastReceiver !application');
      return;
    }
    const ctx = application.getApplicationContext();
    console.log(ctx);
  } catch (e) {
    //===
    console.error('initBroadcastReceiver error', e);
    //===
  }
})

@matbrik
Copy link
Author

matbrik commented Oct 2, 2024

I don't have a similar device available.
you could comment out this whole if statement here
if it is not working you can add logs to narrow the bug sadly the stacktrace you linked does not match the correct lines of frida-java-bridge

@thinhbuzz
Copy link
Contributor

I don't have a similar device available. you could comment out this whole if statement here if it is not working you can add logs to narrow the bug sadly the stacktrace you linked does not match the correct lines of frida-java-bridge

If you comment out that conditional statement, it will work like it did before.

if it is not working you can add logs to narrow the bug sadly the stacktrace you linked does not match the correct lines of frida-java-bridge

It seems that when building, Frida will minify the JavaScript code, leading to stack traces no longer matching those in this library. Can you guide me on how to set up to add logging?

By the way, this is the command I use to build.
./configure --host=android-arm64 && make

@matbrik
Copy link
Author

matbrik commented Oct 2, 2024

If you comment out that conditional statement, it will work like it did before.

so it does work correctly on your Android 10?

Can you guide me on how to set up to add logging?
just add several console.log("") in the android.js to track down the point in the code that causes crash

check also this comment from frida/frida there is a variation of this pull request with a different heuristic
frida/frida#2958 (comment)

@thinhbuzz
Copy link
Contributor

so it does work correctly on your Android 10?

Yes, it works just like before.

check also this comment from frida/frida there is a variation of this pull request with a different heuristic

I will try and report back the results.

@thinhbuzz
Copy link
Contributor

check also this comment from frida/frida there is a variation of this pull request with a different heuristic

I will try and report back the results.

@matbrik After applying the patch to frida-java-bridge, I still encountered a similar error in the makeArtThreadStateTransitionImpl function. Commenting it out made everything work again.

Additionally, when I applied the patch to the frida-core/lib/payload/cloak.vala file, I couldn't build successfully.

@radubogdan2k
Copy link

radubogdan2k commented Oct 6, 2024

@matbrik @thinhbuzz On my S21 Ultra running Android 14 and this latest Google Play update, this PR fixes the error but the actual function passed to Java.perform never gets called. Are you not seeing this problem on your device?

It works fine on an old Android 11 Pixel 2.

@matbrik
Copy link
Author

matbrik commented Oct 7, 2024

@matbrik @thinhbuzz On my S21 Ultra running Android 14 and this latest Google Play update, this PR fixes the error but the actual function passed to Java.perform never gets called. Are you not seeing this problem on your device?

It works fine on an old Android 11 Pixel 2.

@radubogdan2k I have someone else reporting me the same problem on the same device. Sometimes the hooks are not triggered but there is not any error reported.

Maybe @oleavr can share some wisdom on the cause and how to fix it

@radubogdan2k
Copy link

@matbrik @thinhbuzz On my S21 Ultra running Android 14 and this latest Google Play update, this PR fixes the error but the actual function passed to Java.perform never gets called. Are you not seeing this problem on your device?
It works fine on an old Android 11 Pixel 2.

@radubogdan2k I have someone else reporting me the same problem on the same device. Sometimes the hooks are not triggered but there is not any error reported.

Maybe @oleavr can share some wisdom on the cause and how to fix it

Yes, that's exactly the behavior I'm seeing also. Hopefully Ole will be able to help.

@pig837
Copy link

pig837 commented Oct 24, 2024

@matbrik @thinhbuzz On my S21 Ultra running Android 14 and this latest Google Play update, this PR fixes the error but the actual function passed to Java.perform never gets called. Are you not seeing this problem on your device?
It works fine on an old Android 11 Pixel 2.

@radubogdan2k I have someone else reporting me the same problem on the same device. Sometimes the hooks are not triggered but there is not any error reported.
Maybe @oleavr can share some wisdom on the cause and how to fix it

Yes, that's exactly the behavior I'm seeing also. Hopefully Ole will be able to help.

Yeah, I have confirmed that some classes and methods are not hooked in a commercial app that I cannot reveal the name of. (Frida 16.5.6 modified)

As of now, I have to remove the com.google.android.art package, and there seems to be no other countermeasure at the moment.

@matbrik
Copy link
Author

matbrik commented Oct 29, 2024

Fix only for arm64
Due to libart being stripped we missed DoCall hooks and NterpEntrypoints.
It is also missing the ensurePluginLoaded used in Java.choose, I found the function in memory but on a device it caused a massive slowdown of the app and I decided to keep it commented out

@pig837
Copy link

pig837 commented Oct 30, 2024

@matbrik @thinhbuzz On my S21 Ultra running Android 14 and this latest Google Play update, this PR fixes the error but the actual function passed to Java.perform never gets called. Are you not seeing this problem on your device?
It works fine on an old Android 11 Pixel 2.

@radubogdan2k I have someone else reporting me the same problem on the same device. Sometimes the hooks are not triggered but there is not any error reported.
Maybe @oleavr can share some wisdom on the cause and how to fix it

Yes, that's exactly the behavior I'm seeing also. Hopefully Ole will be able to help.

Yeah, I have confirmed that some classes and methods are not hooked in a commercial app that I cannot reveal the name of. (Frida 16.5.6 modified)

As of now, I have to remove the com.google.android.art package, and there seems to be no other countermeasure at the moment.

Fixed, @matbrik Thx. (Google Play System Update 2024.10, com.android.art@351011240)

@thinhbuzz
Copy link
Contributor

@matbrik I got a new error on Samsung Galaxy Note 9 (SM-N960F/DS) - android 10 device

{
    "type": "error",
    "description": "Error: invalid decimal string",
    "stack": "Error: invalid decimal string\n    at qe (frida/node_modules/frida-java-bridge/lib/android.js:274:1)\n    at ze (frida/node_modules/frida-java-bridge/lib/android.js:207:1)\n    at Fe (frida/node_modules/frida-java-bridge/lib/android.js:16:1)\n    at _tryInitialize (frida/node_modules/frida-java-bridge/index.js:29:1)\n    at new _ (frida/node_modules/frida-java-bridge/index.js:21:1)\n    at Object.4../lib/android (frida/node_modules/frida-java-bridge/index.js:332:1)\n    at o (frida/node_modules/browser-pack/_prelude.js:1:1)\n    at frida/node_modules/browser-pack/_prelude.js:1:1\n    at Object.22.frida-java-bridge (frida/runtime/java.js:1:1)\n    at o (frida/node_modules/browser-pack/_prelude.js:1:1)",
    "fileName": "frida/node_modules/frida-java-bridge/lib/android.js",
    "lineNumber": 274,
    "columnNumber": 1
}

@matbrik
Copy link
Author

matbrik commented Oct 30, 2024

@matbrik I got a new error on Samsung Galaxy Note 9 (SM-N960F/DS) - android 10 device

{
    "type": "error",
    "description": "Error: invalid decimal string",
    "stack": "Error: invalid decimal string\n    at qe (frida/node_modules/frida-java-bridge/lib/android.js:274:1)\n    at ze (frida/node_modules/frida-java-bridge/lib/android.js:207:1)\n    at Fe (frida/node_modules/frida-java-bridge/lib/android.js:16:1)\n    at _tryInitialize (frida/node_modules/frida-java-bridge/index.js:29:1)\n    at new _ (frida/node_modules/frida-java-bridge/index.js:21:1)\n    at Object.4../lib/android (frida/node_modules/frida-java-bridge/index.js:332:1)\n    at o (frida/node_modules/browser-pack/_prelude.js:1:1)\n    at frida/node_modules/browser-pack/_prelude.js:1:1\n    at Object.22.frida-java-bridge (frida/runtime/java.js:1:1)\n    at o (frida/node_modules/browser-pack/_prelude.js:1:1)",
    "fileName": "frida/node_modules/frida-java-bridge/lib/android.js",
    "lineNumber": 274,
    "columnNumber": 1
}

@thinhbuzz can you share your gadget so I can check where it crashes? and tell me the version of libart installed?

@thinhbuzz
Copy link
Contributor

thinhbuzz commented Oct 30, 2024

@thinhbuzz can you share your gadget so I can check where it crashes? and tell me the version of libart installed?

i use frida-inject and not frida gadget. for art info you can see in my comment above.
latest commit works fine

@pig837
Copy link

pig837 commented Nov 22, 2024

@oleavr
Copy link
Member

oleavr commented Dec 12, 2024

Thanks a lot for this amazing contribution, and apologies for the delay. I've been in a very deep iOS rabbit-hole, and had to hold off on reviewing non-trivial PRs in the meantime.

I've started working on polishing this PR in fix/art-compat, incorporating the improvements by @hackcatml on top of it. The first pass of refactoring is now working on arm64, but 32-bit ARM is still work in progress.

@matbrik
Copy link
Author

matbrik commented Dec 13, 2024

Hi @oleavr thanks for the help and sorry for my horrible code :) .
Here there are some suggestion and fixes to problems I encountered:
at

const isApiLevel34OrApexEquivalent = Module.findExportByName('libart.so', '_ZN3art7AppInfo29GetPrimaryApkReferenceProfileEv') !== null;
isApiLevel34OrApexEquivalent is not working in libart 35 because the symbol we are looking for is present in libart 34 but not in libart 35 which is stripped of a lot of symbols, in my code 807c7f0#diff-127e99379196fef4dc63a69846f5adbcf81a12eb24557f9b1850b85a9f4f78dfR727 I also looked for the new signature of the RunFlip available from libart 35

for the support to arm I had ready a couple of functions for finding the DoCall function

function findDoCalls32 () {

  const pattern ='2d e9 f0 4f 03 af ?? b0 c0 ef 50 00';
  const module = Process.findModuleByName('libart.so');
  const ranges = module.enumerateRanges('r-x');
  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 null;
  }).filter(element => element !== null).flat();
  const patternResults = findPatternInModule(pattern, 'libart.so');
  return patternResults
}

and for the ExecuteNterpImpl

function findExecuteNterpImpl32 () {
  const pattern = '02 CA 4D E2 00 C0 9C E5  F0 4F 2D E9 10 8A 2D ED 10 B0 90 E5 01 00 1B E3';
  const module = Process.findModuleByName('libart.so');
  const ranges = module.enumerateRanges('r-x');
  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 null;
  }).filter(element => element !== null).flat();
  const patternResults = findPatternInModule(pattern, 'libart.so');
  return patternResults
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants