Invoking a native function
Our app displays a message: "Hello from C++".

It does this by calling a native C++ function called stringFromJNI()
from a FridaTen library. So let’s examine the library.
After we load this into Ghidra, We find a hidden function named getFlag(int)
.

This function wasn’t declared in the Java space and isn’t being called from anywhere in the library.
c code
#include <jni.h> #include <string> #include <strings.h> #include <string.h> extern "C" JNIEXPORT jstring JNICALL Java_com_mobilehackinglab_FridaTen_MainActivity_stringFromJNI( JNIEnv *env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); } char* getFlag(int a){ char *password = static_cast<char *>(malloc(100)); strcpy(password,"NOT_THE_FLAG"); if(a == 123){ const char *hardcoded = "AD@w_IO^IXSJYBOXECBq"; for (int i = 0; i < strlen(hardcoded) ; i++) { password[i] = (char)(hardcoded[i] ^ 12); } password[strlen(hardcoded)] = '\0'; } return password; }
What does
getFlag(int)
do?It creates a string
"NOT_THE_FLAG"
by default.If the input is
123
, it:Takes a hardcoded string.
Applies XOR with
12
on each character.Returns the decoded result (the real flag).
So, to get the flag, we need to invoke this method. Let's use Frida to call this native function.
// Get the base address of the library and add the offset to locate getFlag
var adr = Module.findBaseAddress("libFridaTen.so").add(0x206C0);
// Create a pointer to the getFlag function
var get_flag_ptr = new NativePointer(adr);
// Create a NativeFunction wrapper: getFlag(int) -> returns pointer (char *)
const get_flag = new NativeFunction(get_flag_ptr, 'pointer', ['int']);
// Call the function with argument 123
var flag = get_flag(123);
// Read the returned C string from memory
var str = Memory.readUtf8String(flag);
// Print the flag
console.log("FLAG : " + str);
Line-by-line Explanation
This gets the exact memory address of a function inside
libFridaTen.so
var adr = Module.findBaseAddress("libFridaTen.so").add(0x206C0);
Converts the memory address into a pointer object Frida can work with.
var get_flag_ptr = new NativePointer(adr);
Turns the native function pointer into a JavaScript function you can call.
const get_flag = new NativeFunction(get_flag_ptr, 'pointer', ['int']);
NativeFunction
— What is it?It lets you call native functions (e.g., C/C++ functions inside
.so
or.dll
libraries) directly from your JavaScript code.
Calls the native function
getFlag(123)
var flag = get_flag(123);
The function returns a memory address, pointing to the flag string.
Example: it might return something like
0x12345678
.
Converts the pointer into a readable text string
var str = Memory.readUtf8String(flag);
Prints the flag to the Frida console.
console.log("FLAG : " + str);
We can’t use Module.getExportByName()
to get this function because it is not exported.
So, we will get manual address resolution using offsets
But how do you get the offset?
Open the
.so
file using Ghidra.Look for the function you want (
getFlag
, for example)In the Symbol Tree (left side), expand "Functions".
Find and click the function name, e.g.
getFlag
.Look at the function's address (top bar or in the Listing window).
Subtract the base address of the binary (usually
00100000
for.so
files in Ghidra):Offset = Function Address - Base Address Offset = 001206c0 - 00100000 = 0x206C0
Use that offset in Frida like:
var adr = Module.findBaseAddress("libFridaTen.so").add(0x206C0);
Last updated