0% found this document useful (0 votes)
4 views10 pages

10.hooking Native Methods

The document discusses the process of hooking native methods in Android applications, particularly focusing on performance-critical tasks and potential vulnerabilities. It outlines the steps to analyze an application using tools like ADB, JADX, and Ghidra, and demonstrates how to intercept native function calls using Frida. The example provided illustrates how to extract and decode a flag from a native method by hooking into the strcmp function during runtime.

Uploaded by

timetravaler05
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views10 pages

10.hooking Native Methods

The document discusses the process of hooking native methods in Android applications, particularly focusing on performance-critical tasks and potential vulnerabilities. It outlines the steps to analyze an application using tools like ADB, JADX, and Ghidra, and demonstrates how to intercept native function calls using Frida. The example provided illustrates how to extract and decode a flag from a native method by hooking into the strcmp function during runtime.

Uploaded by

timetravaler05
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Hooking Native Methods

Native methods—written in C or C++ and compiled into machine code—are often used in Android applications for performance-critical tasks,

accessing low-level system resources, or handling sensitive operations. While powerful, these methods can also introduce vulnerabilities or become

targets for exploitation.

By this point, you're already familiar with the concept of hooking. In this section, we'll apply hooking speci�cally to native methods to observe their

behavior at runtime. In parallel, we'll use static analysis to disassemble and review native libraries included in the app, allowing us to understand their

logic and identify potential vulnerabilities from both dynamic and static perspectives.

We'll primarily use an Android Virtual Device (AVD) for this example, though any physical or emulated Android device will work. Let's begin by

connecting to the device via ADB and installing the application.

Hooking Native Methods

rl1k@htb[/htb]$ adb connect


rl1k@htb[/htb]$ adb install [Link]

Performing Streamed Install


Success

The application presents the user a riddle: I am full and strong! What can you do about it?.

Let's examine the application using JADX to see how it works. Reading the source code reveals the MainActivity class.

Hooking Native Methods

rl1k@htb[/htb]$ jadx-gui [Link]


We notice that in the last line of the onCreate() method, a broadcast receiver, is registered dynamically:

Code: java

registerReceiver(this.f1756z, new IntentFilter("[Link].BATTERY_LOW"));

Reading further into the class, we see that the inner class a extends BroadcastReceiver.

As discussed in previous sections, a broadcast receiver can function both as an application component and an interprocess communication (IPC)

mechanism. In this case, it acts as a component that listens for a speci�c system broadcast: [Link].BATTERY_LOW. When this intent is

received, it in�uences the app's behavior depending on the device's battery status. ✎

The receiver invokes the native method getInfo(String str) in the following line:

Code: java

str = [Link](d.d("XDR"));

But only if the following condition is satis�ed.

Code: java

if ([Link]("Is_on").equals("yes")) {
...

This checks whether the Intent contains an Extra named "Is_on" with the value "yes".

Since the broadcast can originate from either the system or a third-party app, we can manually trigger it using ADB. Doing so will cause the app to start

and call the native method.

Hooking Native Methods

rl1k@htb[/htb]$ adb shell am broadcast -a "[Link].BATTERY_LOW" --es "Is_on" "yes"

Broadcasting: Intent { act=[Link].BATTERY_LOW flg=0x400000 (has extras) }


Broadcast completed: result=0
However, this screen doesn't reveal much—just a toast message saying "Look me inside." To further investigate, we need to analyze the app's native

code. First, decompress the APK to extract the native libraries:

Hooking Native Methods

rl1k@htb[/htb]$ unzip [Link] -d angler

<SNIP>
Archive: [Link]
inflating: angler/META-INF/com/android/build/gradle/[Link]
extracting: angler/assets/dexopt/[Link]
extracting: angler/assets/dexopt/[Link]
inflating: angler/[Link]
inflating: angler/lib/arm64-v8a/[Link]
inflating: angler/lib/armeabi-v7a/[Link]
inflating: angler/lib/x86/[Link] ✎
inflating: angler/lib/x86_64/[Link]
inflating: angler/[Link]

The output con�rms the presence of the library �le: angler/lib/arm64-v8a/[Link]. Let's examine it with Ghidra.

Hooking Native Methods

rl1k@htb[/htb]$ ghidrarun

Once Ghidra starts:

1. Go to File → New Project, choose a name (e.g., Test), and click Finish.

2. Then go to File → Import File and select [Link] from angler/lib/arm64-v8a/.

3. Accept the default settings in the pop-ups and click OK.

4. Open the imported file by double-clicking it or clicking the green dragon icon.

5. Confirm any prompts and click Analyze.

After the analysis completes, navigate to the Symbol Tree, expand the Functions folder, and locate
Java_com_example_angler_MainActivity_getInfo.

Based on the JNI naming convention, this function corresponds to the native getInfo() method we previously identi�ed in the app's Java code using

JADX.
Reading the contents of the function Java_com_example_angler_MainActivity_getInfo in Ghidra’s Decompile window reveals that it calls two other

functions: illusion(pcVar1) and ne(pcVar2).

Double-clicking ne(pcVar2) opens the corresponding code, where we �nd a key comparison using the strcmp function.

The following line compares two strings: param_1 and pbVar10.

Code: java

iVar9 = strcmp(param_1,(char *)pbVar10);

The strcmp function returns 0 if the two strings are equal. If this condition is met (iVar9 == 0), the code proceeds to construct a message string
containing You found the flag using std::__ndk1::basic_string<>.

Referring back to the Java code in MainActivity, we saw that the param_1 value passed to strcmp comes from the return value of the method

d.d("XDR"), as used in the line:

Code: java

str = [Link](d.d("XDR"));
To fully understand what the native function is comparing param_1 against, we need to determine the value of pbVar10, the second argument passed to
strcmp. This requires the identi�cation of the memory address where strcmp is invoked during execution.

Due to runtime protection mechanisms like ASLR (Address Space Layout Randomization), we cannot determine the absolute address of strcmp

statically in Ghidra. However, we can calculate it at runtime by adding the o�set of the strcmp call—which we can get by hovering over the instruction in

the Listing window—to the base address of the native library, which we'll retrieve dynamically when the app is running.

Now that we know the o�set (44ab0h), we can start crafting our JS script. In a �le called [Link], we add the following code.

Code: js ✎

// Delay execution to ensure module is loaded


setTimeout(function() {
// Dynamically find module base address
var libanglerBase = [Link]('[Link]');
if (!libanglerBase) {
[Link]('[Link] module not found!');
return;
}

// Calculate specific strcmp call address using offset from Ghidra


var strcmpCallOffset = ptr('0x44ab0');
var strcmpCallAddress = [Link](strcmpCallOffset);

// Attach interceptor to the specific strcmp call


[Link](strcmpCallAddress, {
// Hook entering the function
onEnter: function(args) {
[Link]('');
[Link]('Parameter 1:', [Link](args[0]));
[Link]('Parameter 1:', [Link](args[1]));
},
// Hook leaving the function
onLeave: function(retval) {
[Link]('Specific strcmp call returned:', retval.toInt32());
}
});
}, 1000); // 1 second delay to wait for module load

Notice that we changed the o�set from 44ab0h to 0x44ab0. This is due to JavaScript useing the pre�x 0x to denote hexadecimal numbers. Additionally,

the h su�x is a notation used in some assembly or legacy languages representing hexadecimal format.
Let's break down the script in more detail to understand what each part does.

Instruction Description

setTimeout This delays the execution of the script by 1000 milliseconds (1 second). The delay ensures that the native module
([Link]) has been fully loaded into memory before the script attempts to locate it. If we try to attach the
interceptor too early, the module may not exist in memory yet, causing the script to fail.

[Link]('[Link]') Searches for the base memory address of the [Link] library loaded into the target process. This base
address is essential for calculating the absolute location of the target function call (strcmp) by adding the o�set
obtained from Ghidra.

ptr('0x44ab0') Converts the hexadecimal string '0x44ab0' into a NativePointer object. This value represents the o�set
within the library where the strcmp call is located.

[Link](strcmpCallOffset) Adds the o�set to the base address of the library, producing the absolute address of the speci�c strcmp call that
we want to hook.

[Link](...) Hooks into the calculated memory address. When this address is reached during the app's execution (i.e., when
strcmp is called), the onEnter and onLeave callbacks de�ned in this section will be triggered.

[Link](args[0]) Reads the two string arguments passed to strcmp from memory. These are C-style strings (null-terminated), and
[Link](args[1]) Frida provides this helper to read them correctly.

[Link](...) Outputs the arguments and the return value to the terminal. This allows us to see exactly what strings are being
compared and what the result of the comparison is.

Now that we understand what the script is doing, and we already know the package name ([Link]), let's go ahead and execute it using

Frida:

Hooking Native Methods

rl1k@htb[/htb]$ frida -U -l [Link] -f [Link] 16s ≡


____ ✎
/ _ | Frida 16.1.11 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at [Link]
. . . .
. . . . Connected to Android Emulator (id=emulator-5554)
Spawned `[Link]`. Resuming main thread!
[Android Emulator 5554::[Link] ]->

Frida will attach and wait for the strcmp function to be called, so we send the broadcast again.

Hooking Native Methods

rl1k@htb[/htb]$ adb shell am broadcast -a "[Link].BATTERY_LOW" --es "Is_on" "yes"

Broadcasting: Intent { act=[Link].BATTERY_LOW flg=0x400000 (has extras) }


Broadcast completed: result=0

Once the broadcast is received, the application will automatically start and execute the native getInfo() method, which eventually calls strcmp. When

this happens, the hooked function is triggered, and Frida prints the intercepted values to the terminal.

Hooking Native Methods

rl1k@htb[/htb]$

<SNIP>
. . . . Connected to Android Emulator (id=emulator-5554)
Spawned `[Link]`. Resuming main thread!
[Android Emulator 5554::[Link] ]->
[Android Emulator 5554::[Link] ]->
Parameter 1: HTB
Parameter 1: 4854427b796f755f3472335f676f6f645f34745f6830306b316e397d

The hook is successful. Both parameters are printed, with the �rst being HTB and the second

4854427b796f755f3472335f676f6f645f34745f6830306b316e397d. Based on its format and character set, we can identify with certainty that the second

value is a hex-encoded string. To decode it and reveal its actual content, we'll update the script to include a conversion function that transforms

hexadecimal into ASCII.

Here is the updated [Link].

Code: js

// Delay execution to ensure module is loaded


setTimeout(function() {
// Dynamically find module base address
var libanglerBase = [Link]('[Link]');
if (!libanglerBase) {
[Link]('[Link] module not found!');
return;
}

// Calculate specific strcmp call address using offset from Ghidra


var strcmpCallOffset = ptr('0x44ab0');
var strcmpCallAddress = [Link](strcmpCallOffset);

// Attach interceptor to the specific strcmp call


[Link](strcmpCallAddress, {
// Hook entering the function
onEnter: function(args) {
var param1 = [Link](args[0]);
var param2 = [Link](args[1]); ✎
var flag = hexToASCII(param2);
[Link]('');
[Link]('Parameter 1:', param1);
[Link]('Parameter 2:', flag);
},
// Hook leaving the function
onLeave: function(retval) {
[Link]('Specific strcmp call returned:', retval.toInt32());
}
});
}, 1000); // 1 second delay to wait for module load

// Define a function to convert hexadecimal to ASCII string


function hexToASCII(hex) {
var str = ''; // Initialize an empty string for the result
for (var i = 0; i < [Link]; i += 2) { // Iterate over hex string two characters at a time
var v = parseInt([Link](i, 2), 16); // Convert each hex pair to decimal
if (v) str += [Link](v); // Convert decimal to character and append to result string
}
return str; // Return the resulting ASCII string
}

In this iteration, the functionhexToASCII(hex) has been added, which converts the hexadecimal string to ASCII. Save the changes and run Frida once

more.

Hooking Native Methods

rl1k@htb[/htb]$ frida -U -l [Link] -f [Link] х INT 22s ≡

<SNIP>
. . . . Connected to Android Emulator (id=emulator-5554)
Spawned `[Link]`. Resuming main thread!
[Android Emulator 5554::[Link] ]->

Again, we send the broadcast.

Hooking Native Methods

rl1k@htb[/htb]$ adb shell am broadcast -a "[Link].BATTERY_LOW" --es "Is_on" "yes"

Broadcasting: Intent { act=[Link].BATTERY_LOW flg=0x400000 (has extras) }


Broadcast completed: result=0

At last, the output reveals the ASCII representation of the second parameter.

Hooking Native Methods

<SNIP>
. . . . Connected to Android Emulator (id=emulator-5554)
Spawned `[Link]`. Resuming main thread!
[Android Emulator 5554::[Link] ]->
Parameter 1: HTB
Parameter 2: HTB{h0ok1ng_n4t1v3_funct10ns!}

Connect to Pwnbox
Your own web-based Parrot Linux instance to play our labs.

Pwnbox Location

UK 28ms


Terminate Pwnbox to switch location

Start Instance

 / 1 spawns left

Waiting to start...
Enable step-by-step solutions for all questions 

Questions
􏅜
 Cheat Sheet
Answer the question(s) below to complete this Section and earn cubes!

+5
􏆲 What is the value of the second parameter passed to the strcmp function?

Submit your answer here...

+10 Streak pts 􏄞 Submit


  hook_native_method.zip

 Previous Next 

􏅜 Cheat Sheet

ဿ Go to Questions
?

Table of Contents

Enumerating and Exploiting Installed Apps

Introduction

􏆲
 Enumerating Local Storage

􏆲
 Exported Activities

􏆲
 Insecure Logging

􏆲
 Pending Intents

􏆲
 Exploiting WebViews

􏆲
 Insecure Library Load Through Deep Linking

Dynamic Code Instrumentation

􏆲
 Hooking Java Methods

􏆲
 Altering Method Values

􏆲
 Hooking Native Methods

􏆲
 Bypassing Detection Mechanisms

􏆲
 Authentication Token Manipulation

Intercepting HTTP/HTTPS Requests

􏆲
 Intercepting API Calls

􏆲
 IDOR Attack

􏆲
 SSL/TLS Certi�cate Pinning Bypass

Skills Assessments

􏆲
 Skills Assessment

My Workstation

OFFLINE
􏅄 Start Instance

 / 1 spawns left

You might also like