Android原生库sUQWWyTBEs函数可进一步静态分析吗?如何定位param_1的初始化位置?
sUQWWyTBEs() Native Function in Your Android .so Library First, let's clarify what this ARM64 assembly is actually doing—you're on the right track with your initial breakdown, but there are a few key details to refine:
Step 1: Correct the Function Signature (Critical for JNI Context)
Ghidra's auto-generated signature undefined __cdecl Myapp_sUQWWyTBEs() is likely wrong. Since this is called from Java, it's a JNI native function, which follows a strict signature:
- For non-static Java methods:
JNIEXPORT jstring JNICALL Java_com_yourpackage_YourClass_sUQWWyTBEs(JNIEnv* env, jobject thisObj) - For static Java methods:
JNIEXPORT jstring JNICALL Java_com_yourpackage_YourClass_sUQWWyTBEs(JNIEnv* env, jclass clazz)
That means your param_1 is almost certainly the JNIEnv* pointer (the first parameter passed automatically by the Android Runtime when calling native methods).
Step 2: Reinterpret the Assembly with JNI Context
Let's rewalk the instructions with this context:
00100e44 ldr x8, [param_1]: Loads the JNI function table pointer fromJNIEnv*(sinceJNIEnvis a pointer to a table of function pointers).00100e48 adrp x1, ...+00100e4c add x1, x1, #0x218: Calculates the full address of the string"c7a584936712f32773d3d0a"and stores it inx1.00100e50 ldr x2, [x8, #0x538]: Fetches a function pointer from offset0x538in the JNI function table.00100e54 br x2: Jumps to that function pointer, withx1as its argument, and returns whatever that function outputs (your long random-looking string).
So sUQWWyTBEs() is just a wrapper that calls a specific JNI function with the hardcoded string as input.
How to Locate the Origin of param_1 (JNIEnv*)
Since param_1 is the JNIEnv* pointer, it's not "initialized" in the .so itself—it's passed directly by the Android Runtime when the Java code calls the native method. To trace its context:
- Find the Java-native binding:
- If the library uses static registration, search Ghidra for a function named
Java_<your_package>_<your_class>_sUQWWyTBEs(this is the real exported name, which Ghidra may have renamed toMyapp_sUQWWyTBEs). - If it uses dynamic registration, look for the
JNI_OnLoadfunction in the .so. Inside it, you'll find code that registers native method names to function pointers—search for references to"sUQWWyTBEs"here.
- If the library uses static registration, search Ghidra for a function named
- Check the Java code: Once you know which Java method calls this native function, look at where that Java method is invoked. This will tell you the runtime context, but the
JNIEnv*itself is handled by the Android Runtime.
Further Static Analysis Steps
If you want to dig deeper without dynamic debugging:
- Identify the JNI function at offset 0x538:
The JNI function table for AArch64 has fixed offsets for standard JNI functions. Calculate the index:0x538 / 8 = 169(since each pointer is 8 bytes). Look up the AArch64 JNI function table to see what function lives at index 169—this is the function that generates your output string. - Analyze the hardcoded string:
Right-click the string"c7a584936712f32773d3d0a"in Ghidra and check its cross-references. Is it used anywhere else in the library? It might be a key, seed, or identifier used by the JNI function. - Trace the function pointer's cross-references:
If the function atx2is a custom function (not a standard JNI one), Ghidra should let you jump to its address. Analyze that function's logic to see how it transforms the input string into your output.
Dynamic Debugging (If Static Analysis Gets Stuck)
If you're comfortable using tools like Frida or GDB, this will give you concrete runtime values:
- Frida Script Example:
Run this withconst libName = "libyourlibrary.so"; const funcName = "Myapp_sUQWWyTBEs"; Interceptor.attach(Module.findExportByName(libName, funcName), { onEnter: function(args) { const envPtr = args[0]; console.log(`JNIEnv*: ${envPtr}`); const jniFuncTable = envPtr.readPointer(); const targetFunc = jniFuncTable.add(0x538).readPointer(); console.log(`Target function address: ${targetFunc}`); const inputStr = this.context.x1.readCString(); console.log(`Input string: ${inputStr}`); }, onLeave: function(retval) { const outputStr = retval.readCString(); console.log(`Output string: ${outputStr}`); } });frida -U -f com.your.app -l script.js --no-pauseto see exactly what's happening at runtime.
内容的提问来源于stack exchange,提问作者hanan

