NATIVE ANALYSIS - dm1.OooOO0O and o0O00OOO.OooO(dm1) in libcm.so
Analysis only. No files changed. Method: extracted libcm.so/libcmvm.so from the apktool
branch, parsed JNI registration tables and relocations, disassembled with Capstone
(no Ghidra available).
KEY FINDING: THESE ARE NOT DIRECT FRAMEWORK CALLS
dm1.OooOO0O and o0O00OOO.OooO(dm1) are JNI shims that call a custom DEX VM, not direct
ActivityThread/Instrumentation/WindowManager calls.
libcmvm.so is a Java/DEX bytecode interpreter:
- exports vmInterpret, getJNIWrapper, cacheInitial, getCacheClass
- contains java/lang/Boolean, java/lang/Integer, java/lang/NegativeArraySizeException,
java/lang/InternalError, "bad sparse switch magic", "bad_array_new_length"
WHAT EACH NATIVE METHOD DOES
dm1.OooOO0O(Intent, ActivityInfo) at 0x542ec:
- calloc(9,9)
- stores dm1 jobject at +0x30, Intent at +0x38, ActivityInfo at +0x40
- builds a VM state with the bytecode program at 0x31ebe (187 bytes)
- calls vmInterpret
o0O00OOO.OooO(dm1) at 0x548d4:
- builds a VM state with the bytecode program at 0x32636 (130 bytes)
- stores dm1 at +0x58
- calls vmInterpret
tl0.OooO0O0(int) at 0x5bef0:
- 10-byte VM program; calls vmInterpret
tl0.OooO0OO(int) at 0x5c004:
- 8-byte VM program; calls vmInterpret
PLT imports used by these functions:
- 0x61d80 -> vmInterpret (from libcmvm.so)
- 0x61d90 -> calloc
- 0x61d60 -> free
- 0x61d70 -> __stack_chk_fail
So the real work is in the VM bytecode, not in libcm.so.
DO THEY CREATE AN ACTIVITYRECORD, WINDOW TOKEN, OR PHONEWINDOW?
Not directly in libcm.so. I searched the actual strings:
Present: android/app/Activity, android/view/View, android/view/ViewGroup,
android/view/Window, android/os/Binder, android/os/Parcel, startActivity,
attachInterface
Absent: ActivityThread, ActivityRecord, PhoneWindow, WindowManager, Instrumentation,
makeVisible, setContentView, addView
That means the native layer does not call those framework classes by name. It calls the
VM; the VM's bytecode calls Android APIs through JNI.
What I can infer (not verified):
- startActivity is in libcm.so, so the VM likely asks the system server to start the
proxy activity. The system server creates the ActivityRecord and returns the window
token; the app's ActivityThread creates the PhoneWindow.
- dm1 and o0O00OOO extend tl0 -> android.os.Binder, so they are Binder objects used to
carry the activity identity to the system server.
- The empty FIEL$A* proxy activity gets its content when the VM calls
Activity.setContentView / ViewGroup.addView / WindowManager.addView via JNI. The
View/ViewGroup/Window strings support this, but the exact call is in the bytecode.
EXACT FRAMEWORK CALLS MADE
Cannot verify from the native code. The calls are encoded in the VM bytecode, and the
VM uses cached JNI class/method IDs (cacheInitial, getCacheClass), so the class/method
names are not visible as direct xrefs in libcm.so.
Bytecode dumped:
- dm1.OooOO0O: 9e60 d800 9b01 9b12 5900 0e00 d583 0800 ... (187 bytes)
- o0O00OOO.OooO: 9e50 6f02 7800 6d00 9e01 de00 ... (130 bytes)
I decoded the opcode table (0x490e8, 256 entries) and a few handlers (0x9e = invoke,
0x84 = call, 0x8a = return), but not the full program semantics.
HOW THE CLONE'S CONTENT IS ATTACHED TO FIEL$A*
Verified:
- FIEL$A0 -> vz1 -> android.app.Activity has NO onCreate, setContentView, addView, o10,
dm1 or native call. It is an empty Activity.
- The native engine is the only thing that can fill it.
Not verified:
- The exact call (setContentView vs addView vs WindowManager.addView) and whether it is
done by the VM's JNI bridge or by a Binder transaction to the system server.
- Whether the target's view is inflated by the VM or by the target's own Activity.onCreate.
- Whether the window token comes from startActivity or from a separate ActivityRecord
request.
WHAT NEEDS A DEVICE/RUNTIME TO CONFIRM
- Run with a JNI/Binder tracer (Frida) on dm1.OooOO0O and o0O00OOO.OooO to capture the
actual JNI calls.
- Decode the VM bytecode at 0x31ebe / 0x32636 with the VM opcode table.
- Inspect the dm1 Binder transaction and the ActivityRecord/token flow.
- Confirm whether FIEL$A* receives the view via setContentView or WindowManager.addView.