Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to identify what is accessing hidden methods

I have a flutter app that generates a lot of hidden API warnings:

Accessing hidden method Landroid/view/accessibility/AccessibilityNodeInfo;->getSourceNodeId()J (greylist,test-api, reflection, allowed)
Accessing hidden method Landroid/view/accessibility/AccessibilityRecord;->getSourceNodeId()J (greylist, reflection, allowed)
Accessing hidden field Landroid/view/accessibility/AccessibilityNodeInfo;->mChildNodeIds:Landroid/util/LongArray; (greylist, reflection, allowed)
Accessing hidden method Landroid/util/LongArray;->get(I)J (greylist, reflection, allowed)
...
Accessing hidden method Landroid/database/sqlite/SQLiteDatabase;-><clinit>()V (blacklist, linking, denied)
Accessing hidden field Landroid/database/sqlite/SQLiteDatabase;->DEBUG_CLOSE_IDLE_CONNECTIONS:Z (greylist-max-o, linking, denied)
Accessing hidden field Landroid/database/sqlite/SQLiteDatabase;->sActiveDatabases:Ljava/util/WeakHashMap; (greylist-max-o, linking, denied)
Accessing hidden field Landroid/database/sqlite/SQLiteDatabase;->CONFLICT_VALUES:[Ljava/lang/String; (greylist, linking, allowed)

And so on. I haven't written any code to use Android's SQLiteDatabase or AccessibilityNodeInfo, but this doesn't provide any information about which bit of code is calling these hidden methods. It could be in Flutter, or one of the several plugins I have. Short of mutilating my code to remove these plugins, is there anything I can do to find out what code is calling these? I.e. to get a stack trace.

Does Android have an option to crash on hidden field accesses rather than just logging them, or something similar?

like image 316
Timmmm Avatar asked Feb 20 '21 10:02

Timmmm


1 Answers

Aha, thanks to Mark Keen's link, I was able to create a custom Application for my Flutter app that logs the stack track for strict mode violations. Create a MainApplication.kt Kotlin file like this:

package com.yourdomain.flutter_app

import android.app.Application
import android.os.StrictMode
import android.os.StrictMode.OnVmViolationListener
import android.os.StrictMode.VmPolicy
import android.os.strictmode.Violation
import android.util.Log
import io.flutter.view.FlutterMain
import java.io.PrintWriter
import java.io.StringWriter
import java.util.concurrent.Executor


class CurrentThreadExecutor : Executor {
    override fun execute(r: Runnable) {
        r.run()
    }
}

class StacktraceLogger : OnVmViolationListener {
    override fun onVmViolation(v: Violation) {
        val sw = StringWriter()
        val pw = PrintWriter(sw)
        v.printStackTrace(pw)

        Log.e("STRICTMODE", sw.toString())
    }
}

class MainApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        val policy = VmPolicy.Builder()
                .detectAll()
                .detectNonSdkApiUsage()
                .penaltyListener(CurrentThreadExecutor(), StacktraceLogger())
                .build()
        StrictMode.setVmPolicy(policy)

        FlutterMain.startInitialization(applicationContext)
    }
}

And then point your AndroidManifest.xml to it:

    <application
        android:name="com.yourdomain.flutter_app.MainApplication"

Then when you run it you should see a stack trace. In this case the AccessibilityNodeInfo stuff does appear to be a Flutter issue:

2021-02-20 12:43:11.986 3291-3291/uk.co.timhutt.flutter_app E/STRICTMODE: android.os.strictmode.NonSdkApiUsedViolation: Landroid/view/accessibility/AccessibilityNodeInfo;->getSourceNodeId()J
        at android.os.StrictMode.lambda$static$1(StrictMode.java:416)
        at android.os.-$$Lambda$StrictMode$lu9ekkHJ2HMz0jd3F8K8MnhenxQ.accept(Unknown Source:2)
        at java.lang.Class.getDeclaredMethodInternal(Native Method)
        at java.lang.Class.getPublicMethodRecursive(Class.java:2079)
        at java.lang.Class.getMethod(Class.java:2066)
        at java.lang.Class.getMethod(Class.java:1693)
        at io.flutter.view.AccessibilityViewEmbedder$ReflectionAccessors.<init>(AccessibilityViewEmbedder.java:446)
        at io.flutter.view.AccessibilityViewEmbedder$ReflectionAccessors.<init>(AccessibilityViewEmbedder.java:429)
        at io.flutter.view.AccessibilityViewEmbedder.<init>(AccessibilityViewEmbedder.java:70)
        at io.flutter.view.AccessibilityBridge.<init>(AccessibilityBridge.java:346)
        at io.flutter.embedding.android.FlutterView.attachToFlutterEngine(FlutterView.java:919)
        at io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.onCreateView(FlutterActivityAndFragmentDelegate.java:294)
        at io.flutter.embedding.android.FlutterActivity.createFlutterView(FlutterActivity.java:520)
        at io.flutter.embedding.android.FlutterActivity.onCreate(FlutterActivity.java:414)
        at android.app.Activity.performCreate(Activity.java:8000)
        at android.app.Activity.performCreate(Activity.java:7984)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3404)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3595)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7660)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

Update: The SQLite methods

Weirdly, it still didn't print any stack traces for the SQLite errors, but I noticed that the first errors were for "reflection" whereas those were for "linking". I am linking with a Rust library that uses a bundled copy of SQLite - using this dependency:

rusqlite = { version = "0.24.2", features = ["bundled"] }

And then loading it in Dart:

final DynamicLibrary mapRenderNative = Platform.isAndroid
    ? DynamicLibrary.open("libmap_render.so")
    : DynamicLibrary.process();

When I commented that line out and ran wgradle clean (necessary it seems), the errors went away! Wtf? It seems like Android intercepts dlopen() and checks if you link against any of the forbidden symbols.

But the really weird thing is that my libmap_render.so doesn't link with SQLite (because it's statically linked into it):

>llvm-readelf -d target\aarch64-linux-android\debug\libmap_render.so
Dynamic section at offset 0x52768 contains 23 entries:
  Tag                Type           Name/Value
  0x0000000000000001 (NEEDED)       Shared library: [libdl.so]
  0x0000000000000001 (NEEDED)       Shared library: [libc.so]
  0x000000000000001e (FLAGS)        BIND_NOW
  0x000000006ffffffb (FLAGS_1)      NOW
  0x0000000000000007 (RELA)         0x988
  0x0000000000000008 (RELASZ)       9768 (bytes)
  0x0000000000000009 (RELAENT)      24 (bytes)
  0x000000006ffffff9 (RELACOUNT)    405
  0x0000000000000017 (JMPREL)       0x2fb0
  0x0000000000000002 (PLTRELSZ)     888 (bytes)
  0x0000000000000003 (PLTGOT)       0x54940
  0x0000000000000014 (PLTREL)       RELA
  0x0000000000000006 (SYMTAB)       0x308
  0x000000000000000b (SYMENT)       24 (bytes)
  0x0000000000000005 (STRTAB)       0x79c
  0x000000000000000a (STRSZ)        491 (bytes)
  0x000000006ffffef5 (GNU_HASH)     0x778
  0x000000000000001a (FINI_ARRAY)   0x54758
  0x000000000000001c (FINI_ARRAYSZ) 16 (bytes)
  0x000000006ffffff0 (VERSYM)       0x6e0
  0x000000006ffffffe (VERNEED)      0x734
  0x000000006fffffff (VERNEEDNUM)   2
  0x0000000000000000 (NULL)         0x0

I also couldn't find any SQLite symbols in the symbol table, relocations, etc. So I have literally no idea what Android is doing. Does it literally scan the binary?

I'll open a new question for that I think.

like image 58
Timmmm Avatar answered Oct 06 '22 00:10

Timmmm