PAGalaxyLab / YAHFA

Yet Another Hook Framework for ART
GNU General Public License v3.0
1.56k stars 351 forks source link

Problems hooking Binder.execTransact and BinderProxy.transactNative #98

Open paulo-raca opened 5 years ago

paulo-raca commented 5 years ago

I'm trying to intercept all Binder transactions coming in/out of a process.

While hooking them works fine, calling the backup method does not (Except for Android P, where it works fine)

My (simplified) hooks look like this:

class BinderStub {
    //======================= INCOMING CALLS =============================
    @HookingArt.MethodHook
    public static boolean execTransactHook(Binder thiz, int code, long dataObj, long replyObj, int flags) throws RemoteException {
        Log.i(TAG, "execTransactHook()");
        return execTransactBackup(thiz, code, dataObj, replyObj, flags);
    }
    @HookingArt.MethodBackup
    public static boolean execTransactBackup(Binder thiz, int code, long dataObj, long replyObj, int flags) throws RemoteException {
        Log.i(TAG, "execTransactBackup()");
        throw new RuntimeException("Stub");
    }

    //======================= OUTGOING CALLS =============================

    //@HookingArt.MethodHook
    public static boolean transactNativeHook(BinderProxy thiz, int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Log.i(TAG, "transactNativeHook");
        return transactNativeBackup(thiz, code, data, reply, flags);
    }
    @HookingArt.MethodBackup
    public static boolean transactNativeBackup(BinderProxy thiz, int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Log.i(TAG, "transactNativeBackup()");
        throw new RuntimeException("Stub");
    }
}

While the hook is called as expected, it crashes when trying to call the backup methods, each with a distinct error:

java.lang.IncompatibleClassChangeError: The method 'boolean android.os.BinderProxy.transactNative(int, android.os.Parcel, android.os.Parcel, int)' was expected to be of type static but instead was found to be of type virtual (declaration of 'BinderStub' appears in /data/app/rgZrFTBwB3ZgHMhEX5Dw==/base.apk!classes4.dex)
        at BinderStub.transactNativeHook(BinderStub.java:109)
java.lang.IllegalAccessError: Method 'boolean android.os.Binder.execTransact(int, long, long, int)' is inaccessible to class 'BinderStub' (declaration of 'BinderStub' appears in /data/app/CUsqNQ52csXCy4dbC_UU1g==/base.apk!classes4.dex)
    at BinderStub.execTransactHook(BinderStub.java:91)

As far as I can tell, both have the same issue: Apparently, the backup method receives all the metadata from the target method and fails runtime verifications, instead of simply replacing it's implementation.

I still didn't have success reproducing this problem as a small unit test, but I'll try again tomorrow

paulo-raca commented 5 years ago

I've been trying to reproduce it as a unit tests, and so far I got this:

    @Test
    public void hookBinder() throws Exception {
        BinderHook.doTransaction();

        Assert.assertEquals(0, BinderHook.hookCount);
        Assert.assertEquals(0, BinderHook.backupCount);

        //------------------------ AFTER HOOKING --------------------------------
        HookMain.backupAndHook(
                Class.forName("android.os.BinderProxy").getDeclaredMethod("transactNative", int.class, Parcel.class, Parcel.class, int.class),
                BinderHook.class.getDeclaredMethod("transactNativeHook", Object.class, int.class, Parcel.class, Parcel.class, int.class),
                BinderHook.class.getDeclaredMethod("transactNativeBackup", Object.class, int.class, Parcel.class, Parcel.class, int.class));

        BinderHook.doTransaction();

        Assert.assertEquals(1, BinderHook.hookCount);
        Assert.assertEquals(0, BinderHook.backupCount);
    }

    static class BinderHook {
        static int hookCount;
        static int backupCount;

        public static boolean transactNativeHook(Object thiz, int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            Log.i(TAG, "transactNativeHook");
            hookCount++;
            return transactNativeBackup(thiz, code, data, reply, flags);
        }
        public static boolean transactNativeBackup(Object thiz, int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            Log.i(TAG, "transactNativeBackup");
            backupCount++;
            throw new UnsupportedOperationException("Stub!");
        }

        public static void doTransaction() throws Exception {
            Context ctx = InstrumentationRegistry.getContext();
            ctx.getPackageManager().getNameForUid(Process.myUid());
        }
    }

Interestingly, the errors I describe do not happen when I run this as a stand-alone unit tests. However, it crashes on API23:

03-01 02:52:29.832 6216-6226/lab.galaxy.yahfa.test A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x7ff6016f9500 in tid 6226 (HeapTaskDaemon)
03-01 02:52:30.052 1334-1334/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
03-01 02:52:30.052 1334-1334/? A/DEBUG: Build fingerprint: 'Android/sdk_google_phone_x86_64/generic_x86_64:6.0/MASTER/5056751:userdebug/test-keys'
03-01 02:52:30.052 1334-1334/? A/DEBUG: Revision: '0'
03-01 02:52:30.052 1334-1334/? A/DEBUG: ABI: 'x86_64'
03-01 02:52:30.060 1334-1334/? A/DEBUG: pid: 6216, tid: 6226, name: HeapTaskDaemon  >>> lab.galaxy.yahfa.test <<<
03-01 02:52:30.060 1334-1334/? A/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x7ff6016f9500
03-01 02:52:30.261 1334-1334/? A/DEBUG:     rax 7ff5c02b4e08bf48  rbx 000000004e08bf48  rcx 00007ff5b366d5c0  rdx 0000000077ff0000
03-01 02:52:30.262 1334-1334/? A/DEBUG:     rsi 00007ff5bb104030  rdi 00007ff5c383e200
03-01 02:52:30.262 1334-1334/? A/DEBUG:     r8  000000004e08be98  r9  000000000000001f  r10 0000000000000001  r11 000000000000001f
03-01 02:52:30.262 1334-1334/? A/DEBUG:     r12 0000000000000000  r13 00007ff5bae16298  r14 0000000000000000  r15 0000000000000000
03-01 02:52:30.262 1334-1334/? A/DEBUG:     cs  0000000000000033  ss  000000000000002b
03-01 02:52:30.262 1334-1334/? A/DEBUG:     rip 00007ff5c355e354  rbp 00007ff5b3dc75c0  rsp 00007ff5b3dc7460  eflags 0000000000010246
03-01 02:52:30.348 1334-1334/? A/DEBUG: backtrace:
03-01 02:52:30.351 1334-1334/? A/DEBUG:     #00 pc 0000000000511354  /system/lib64/libart.so (art::StackVisitor::WalkStack(bool)+212)
03-01 02:52:30.351 1334-1334/? A/DEBUG:     #01 pc 00000000005205d4  /system/lib64/libart.so (art::Thread::VisitRoots(art::RootVisitor*)+1444)
03-01 02:52:30.352 1334-1334/? A/DEBUG:     #02 pc 0000000000282790  /system/lib64/libart.so (art::gc::collector::CheckpointMarkThreadRoots::Run(art::Thread*)+112)
03-01 02:52:30.353 1334-1334/? A/DEBUG:     #03 pc 000000000053cbc1  /system/lib64/libart.so (art::ThreadList::RunCheckpoint(art::Closure*)+529)
03-01 02:52:30.353 1334-1334/? A/DEBUG:     #04 pc 00000000002818bc  /system/lib64/libart.so (art::gc::collector::MarkSweep::MarkRootsCheckpoint(art::Thread*, bool)+108)
03-01 02:52:30.354 1334-1334/? A/DEBUG:     #05 pc 000000000028746d  /system/lib64/libart.so (art::gc::collector::MarkSweep::MarkingPhase()+589)
03-01 02:52:30.354 1334-1334/? A/DEBUG:     #06 pc 0000000000287b01  /system/lib64/libart.so (art::gc::collector::MarkSweep::RunPhases()+721)
03-01 02:52:30.355 1334-1334/? A/DEBUG:     #07 pc 0000000000278f1a  /system/lib64/libart.so (art::gc::collector::GarbageCollector::Run(art::gc::GcCause, bool)+330)
03-01 02:52:30.355 1334-1334/? A/DEBUG:     #08 pc 00000000002af297  /system/lib64/libart.so (art::gc::Heap::CollectGarbageInternal(art::gc::collector::GcType, art::gc::GcCause, bool)+2295)
03-01 02:52:30.355 1334-1334/? A/DEBUG:     #09 pc 00000000002b0dd1  /system/lib64/libart.so (art::gc::Heap::ConcurrentGCTask::Run(art::Thread*)+145)
03-01 02:52:30.355 1334-1334/? A/DEBUG:     #10 pc 00000000002e532e  /system/lib64/libart.so (art::gc::TaskProcessor::RunAllTasks(art::Thread*)+62)
03-01 02:52:30.356 1334-1334/? A/DEBUG:     #11 pc 0000000072107549  /data/dalvik-cache/x86_64/system@framework@boot.oat (offset 0x1ed6000)
03-01 02:52:30.631 1334-1334/? A/DEBUG: Tombstone written to: /data/tombstones/tombstone_01