Newer versions of the Rabbit R1's APK are protected by https://www.zimperium.com/zshield/ (I don't know this for certain, somebody told me it is but I haven't really seen any identifying marks in the code yet)
Interesting assets within the APK:
lib/arm64-v8a/liboptipkawfn.so ~3MB packed/encrypted ELF
assets/optipkawfn/0.odex only 41 bytes (EDIT: I think this is part of an asset obfuscation scheme, the real file contents are likely elsewhere - inside the .szip maybe?)
assets/optipkawfn.szip ~8MB - I predict containing encrypted+compressed bytecode
The substring "optipkawfn" is randomised between builds.
Write an unpacker for "protected" .so files.DONE! (XXTEA decryption)- Figure out how asset encryption(?) works.
- Figure out how to map method names to their corresponding native implementations.
- Automate native code deobfuscation:
- String decryption
- Control-flow recovery
The ELF entrypoint has a few anti-analysis tricks up its sleeve:
-
Per the marketing brief, the code is "diversified" meaning different builds might make slightly different checks in different orders.
-
Checking
/proc/self/status
for non-zeroTracerPid
(anti-debugger checks) -
Finds libc via
/proc/self/maps
and callspthread_create
to spawn a sub-thread which:- Checks for the following Naughty Paths in the filesystem (via newfstatat):
/sbin/su
/system/bin/su
/system/xbin/su
/system/xbin/daemonsu
/su/bin/su
/su/bin/daemonsu
/system/xbin/bstk/su
/data/xposed/XposedBridge.jar
/storage/emulated/0/MagiskManager
/data/data/com.topjohnwu.magisk
/data/user_de/0/com.topjohnwu.magisk
/data/user/0/com.topjohnwu.magisk
/magisk
/data/magisk
/data/magisk.img
/root/magisk
/root/magiskinit
/system/lib/libxposed_art.so
/system/bin/app_process64_xposed
/data/dalvik-cache/xposed_XResourcesSuperClass.dex
/data/dalvik-cache/arm64/system@framework@[email protected]@xposed
/data/dalvik-cache/oat/arm64/xposed_XTypedArraySuperClass.odex
/system/lib/libriruloader.so
/system/lib64/libriruloader.so
-
(subthread continued):
- In an infinite loop (every 10ms), it checks:
/proc/self/status
(for a debugger, probably)/proc/self/maps
(looking for traces of frida, I believe)- Checks
/proc/net/unix
for... something (probably frida traces again). - I see mentions of
/etc/hosts
and/proc/cpuinfo
in strings but I'm not sure they're ever checked.
-
(Back on the main thread now)
-
Checks
/proc/net/unix
similarly. -
Self-decrypts the body of the ELF using the XXTEA cipher.
-
Joins the subthread
-
Checks for KernelSU via
prctl(0xdeadbeef)
(source) -
I think it checks libc's
environ
, but I'm not sure what it actually checks for. -
TODO: what happens with relocations?
-
Cleans everything up and returns from the entrypoint (back to dlopen I guess)
Aaaaand that's as far as I've got so far. I wrote an unpacker for the XXTEA stuff, but there's a lot more work to be done.
The code itself is control-flow flattened and obfuscated (OLLVM-style), and strings/buffers are "encrypted" with a weak made-up(?) cipher with 32-bit keys.
A lot of the assets (such as 0.odex
mentioned above) appear truncated, to between 17 and 41 bytes.
No idea how these get unpacked, or where they get unpacked from!
I haven't tried any proper dynamic analysis yet, I haven't even launched the app yet (my R1 is still in the post).
I'm pretty new to the world of android/java application protection.
My working theory is that the aforementioned .szip
file contains encrypted and/or compressed DEX data, that's unpacked at runtime. I'll try to trace this process.
I found this, an open-source android DEX protector: https://github.com/KuNgia09/Bangcle, which might give me some ideas for what to expect. Some key JNI methods to watch out for:
env->RegisterNatives
- does what it says on the tin- TODO: write some more words here
Other projects to look at:
- https://github.com/canyie/pine
- https://github.com/LSPosed/LSPosed
- https://github.com/LSPosed/LSPlant
My second theory (which is more likely now I think about it) is that all the "protected" code has been translated AOT to obfuscated native methods (gross!). Maybe the .szip
just contains protected assets (szip = secure zip?).
HMMM I just zipped up the (unprotected) assets from v0.8.83
and they took up ~7.8MB. This correlates to the size of the szip.
If this is the case, then firstly I'll want to find a way to pull out the java-name-to-native-function mappings, and secondly maybe I'll want to think about writing a proper deobfuscator - control flow recovery?