Working of LD_PRELOAD for Android Applications and its anti-RE Technique

Have you ever checked the parent process of an Android application launched using “wrap” system property set with LD_PRELOAD value? It’s not Zygote!!!  

Expected Reading Time: 10 mins

There are multiple tools and methodology available for reverse engineering an Android application. In this post, I am not exploring various RE techniques available, instead look into how one such technique works in case of Android applications. LD_PRELOAD is a functionality of dynamic linker present across all Linux distributions, giving precedence to library passed to be searched first for symbols. It is not a RE technique per se, but I have used it often for such purposes and hence I classify it as a RE technique which one should know.

Many Linux users will be aware of how LD_PRELOAD works in case of native binaries. For the starters, there are many good blogs available and I will leave it as an assignment. As a side note, knowledge of how dynamic linkers work is always handy for reverse engineering assignments.

In case of a simple native binary compiled to run on Android, LD_PRELOAD works as for other Linux systems. Things get interesting once you intend to use LD_PRELOAD for an Android application. If you simply set the LD_PRELOAD env variable and then try to launch the application, it will not work. Rather you need to perform some Android trickery to make it work for an application. For example, we want to load using LD_PRELOAD for application with package name We need to perform following steps:
• Push the library to the device.  adb push /data/local/tmp/
• Set system property:  setprop LD_PRELOAD=/data/local/tmp/

Note, in last step the property name is  “wrap” prefixed to the application’s package name.

Now if you launch the application, the library will be searched first by the dynamic linker for symbols. (In recent Android versions, one need to disable SELinux to make this work, as does not have SELinux context assigned, because of which the library is not loaded and whole process exits on such an error.) The above work around is neither a hidden feature nor a newly added feature. Many of us might have used it several times in the past.

After knowing how to make it work practically, lets try to understand what is actually happening internally, and why we need to perform these extra steps. Some might have already guessed, it is because of Zygote process creation model used in Android. As covered previously on this blog as well, an Android application is launched by forking a Zygote process. Argument being,  in order to save time while launching an application, Android coldstarts a process during OS startup, from which applications can be forked when required. This process is initialized with all necessary libraries required to run an Android application, and remains in sleep state. When a request to launch an application is received, this Zygote process is simply forked and used there on.

Since, Zygote process is initialized well before launching the application, setting LD_PRELOAD env variable before launching an application will cause no change in symbol search order for linker. Thus, to enable LD_PRELOAD functionality, Android needs a work around.

Lets revisit how an application launch request is passed to Zygote. When user makes a request to launch an application,  a request is sent to /dev/socket/zygote. On receiving request, Zygote forks itself and launches the application in the child process. As per this mechanism all the application’s, as expected, will have Zygote as the parent process. But in case of LD_PRELOAD scenario, a slight different code path is taken.

Code listening at /dev/socket/zygote runs in a loop, referred as ‘select loop’ in code. On receiving request, runOnce() in ZygoteConnection  is called. In this function, many checks and operations are performed, one such check is to check if “wrap” system property set for the given application’s package name.

boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
applyUidSecurityPolicy(parsedArgs, peer, peerSecurityContext);
applyRlimitSecurityPolicy(parsedArgs, peer, peerSecurityContext);
applyInvokeWithSecurityPolicy(parsedArgs, peer, peerSecurityContext);
applyseInfoSecurityPolicy(parsedArgs, peer, peerSecurityContext);
checkTime(startTime, "zygoteConnection.runOnce: apply security policies");
checkTime(startTime, "zygoteConnection.runOnce: apply security policies");

If it is set, it extracts the value of the property. It is important that, a system property name cannot be more than 31 chars, and if package name is more than 31 chars, it need to be truncated to 31 chars.

After extracting the value, execApplication() in WrapperInit is called. In this function, a command string is constructed with required parameters, to launch application via /system/bin/app_process  (Zygote) directly. The shell arguments, LD_PRELOAD in this case, is appended in this command string. This command is executed using /system/bin/sh. Thus, making /system/bin/sh as the parent process to the finally launched application.

public static void execApplication(String invokeWith, String niceName,
int targetSdkVersion, FileDescriptor pipeFd, String[] args) {

StringBuilder command = new StringBuilder(invokeWith);
command.append(" /system/bin/app_process /system/bin --application");
if (niceName != null) {
command.append(" '--nice-name=").append(niceName).append("'");
command.append(" ");
command.append(pipeFd != null ? pipeFd.getInt$() : 0);
command.append(' ');
Zygote.appendQuotedShellArgs(command, args);

This very fact, that parent process for such an application is not Zygote, can be used as an anti-RE technique. If application’s parent is not Zygote, then application can change its behavior or exit, preventing application analysis.

Keep hacking 🙂

Intercepting HTTPS traffic of Android Nougat Applications

TL;DR To intercept network traffic for Android 7.0 targeted applications, introduce a res/xml/network_security_config.xml file.

Expected Reading Time: 5 mins

In this post I am exploring the new security feature of Android 7.0, which by default makes applications to not to trust “user” installed CA certificates (non-admin), and how to bypass this new security feature.  I started with the feeling that it might not be easy to find a way past these checks, but it panned out to be quite straightforward and much less technical than expected.

Android 7.0 (Nougat) has introduced many new security features to harden the OS. The changes ranges from low level stuff like using more of Linux Seccomp, having File based encryption, to making mediaserver more secure by compartmentalization and compiler level checks for integer overflows. Among all these changes, Android 7.0 also changed the default trust level of installed CA certificates for the applications.

In Android Nougat, the default trusted CA certificates by the applications has changed. In the earlier versions of Android, by default the application apart from trusting the “system” installed CA certificates, it also used to trust the “user” added CA certificates as well. For example, while using some MITM proxy like mitmproxy, burp etc, you can simply install the proxy’s certificate on Android device/emulator and then you can intercept and observe the network traffic in clear text. But from Android 7.0 onwards, the default behaviour has been altered and the “user” installed certificates won’t be trusted anymore by the applications. Thus, intercepting  application’s traffic using a proxy will not be possible out of the box.

So how to observe the network traffic for the applications targeting Android 7.0? Apparently, Google has not provided any simple switch in settings menu, which can be toggled and apps start trusting user installed CA certs. So it means we need to get our hands dirty.

So with a task in hand, I picked up an open source Android application and compiled with target SDK Level 24 (for Android 7.0). I prepared a release build and installed on a device running Android 7.0. I used mitmproxy for the job to intercept network traffic. And to no surprises, the application was not able to connect and reported no internet connectivity.

As the adage goes, RTFD, lo and behold, the hint is there in the same blog post. For ease of setting network security configuration, Android provides a network security configuration file, while lets you to “customize network security settings in a safe, declarative configuration file without modifying app code”. So that is all needed. We need to decompile the application using apktool and introduce a network_security_config.xml (shown below) file at res/xml folder of the application.  Recompile the application with apktool again, sign it using jarsigner and we are good to go.

                <!-- Trust preinstalled CAs -->  
                <certificates src="system" />  
                <!-- Additionally trust user added CAs -->  
                <certificates src="user" />  


For the better understanding, I highly recommend to read the original blog and the security config documentation.

In the end it turned out to be quite trivial than expected, but nevertheless requires few extra steps now to intercept an applications traffic. This technique might be not be needed immediately, as most applications will still be targeting at least Android 4.4 for now, for which the user added CA certificate are still trusted.

Bypassing SSL Pinning in Android Applications

It is a common practice for Android and iOS applications’ developers to implement SSL Pinning in order to make reverse engineering of the apps difficult. As per OWASP, SSL Pinning can be defined as the process of associating a host (in this case the app), with their expected X509 certificate or public key. Applications communicating over HTTPS and using SSL Pinning makes it non-trivial to perform Man-In-The-Middle attack and grab the network traffic in clear text using the proxy tools. For further reading about SSL Pinning, I would recommend  OWASP article to get started with.

In this post, I will be looking into the steps involved in bypassing SSL Pinning checks for an Android application and show how we can patch the application binaries in order to intercept the traffic. For this post I am using Facebook Messenger (FBM) application as an example.

SSL Pinning In Android:

SSL Pinning in case of Android can be performed either in the Java layer, using the Android API, or in the native C/C++ layer. Lets look into each of the cases one at a time:

Java Layer:

To implement SSL Pinning, Android API exposes multiple functions to do so.  Android developer website provides a good overview about the topic.  In order to bypass the SSL Pinning in Java layer one can use existing tools or can patch the APK file manually.

  • Xposed Framework: If the device is rooted, one can install Xposed Framework and use one of the multiple modules available to disable SSL Pinning. One such module is SSL Unpinning. Using the module is straight forward and I would leave the details of usage to the readers to figure out.
  • Manual Patching:  Xposed Framework requires the device to be rooted. In such a case we cannot use the tools discussed above to bypass the SSL checks. In such a situation we can patch the APK file manually. Patching the file manually requires some extra effort, but given the abundance of exiting tools, this can be done with ease. The steps involved are following:
  1. Decompile the application using Apktool or any other similar tool. Apktool gives Smali code for the application.
  2. Patch the relevant functions in the Smali code.
  3. Compile the application back using apktool, sign it using jarsign and run zipalign over it.
  4. Installed the patched APK generated above.

The above approach is discussed in detail in this blog post. Hence, I will refrain from duplicating the information and recommend you to read it there.

Native Layer:

Now enters a bit more challenging part. If the above approaches fail, you can fairly be confident that the SSL Pinning checks are being performed in the native layer. FBM is doing exactly same. To make things a bit obscure, the FBM application do have SSL Pinning logic in Java layer as well, but patching it does not work.

To get started, simply run APKTool and get the decompiled/unzipped version of the APK. If you look in the lib folder, there are 6 native libraries.


After exploring these libraries using IDA Pro, to my surprise, none was having network related code. Which entailed more research is required to find the relevant code. If you look into these existing native libraries, you will find there is logic for xz decoding and logic for loading more native libraries. Also, if you go on and install the application and after the first usage, you will find that the /data/data/com.facebook.orca directory have a folder lib-xzs, which contains another 30+ libraries.


So now the work is cut out clearly, we have to look into these libraries to find the network code. After evaluating each library using IDAPro, the code for SSL Pinning was found in There are many functions corresponding to SSL Pinning and network in this library. On seeing the various functions,  proxygen::SSLVerification::verifyWithMetrics() seems to be an easy target to fix. The function performs a comparison of the certificate received against the desired certificate embedded/pinned in the application and returns true or false. The pseudocode is shown below:


  • Dynamic Patching: Dynamic library can be patched by performing dynamic injection and patching the function during the runtime. ADBI and FRIDA are the two frameworks that provide such functionality. After some fiddling around with the above mentioned tools,  in both the cases FBM was crashing whenever an injection is made. Whether it was a security defense mechanism or instability of the process post injection, I left this unexplored for the time being. A note to the readers, to use these framework you need a rooted device.
  • Native Library Patching: Given the dynamic injection is causing frequent crashes, patching the binary manually and reloading it again seems to be the only other option to go with. I used IDAPro to patch the binary. At the offset  0x0006732E patch code from 0xB948 (CBNZ R0, loc_6744) to 0xB9B8 (CBNZ R0, loc_6760). Where the block at loc_6760 is responsible for setting the return value, variable ‘result‘, to always true. To get more understanding how the ARMv7 instructions represented at the bit level, read this answer. After patching, replace the file in /data/data/com.facebook.orca/lib-xzs with the new one, restart the application and now you can intercept the traffic.
Before Patching
After Patching

Recently, there was another blog post discussing patching of Android application (Pokemon Go app) to bypass SSL Pinning and I would recommend to read that one as well. The approach taken in that post is slightly different, as each application has different implementation.

AOSP 5.1.1 Renderscript Build Error

While building AOSP 5.1.1_r14 I encountered a build error with renderscript, in frameworks/rs. The error looks like following:

target level api 22 is out of range ('11' - '21')

The problem is AOSP 5.1 is API level 22, while it seems the renderscript is compatible till 21 only. Some searching through git logs of AOSP, I came across this commit. Although the commit is not directly applicable to current problem, but it provides a hint to solve the current compatibility issue. To summarise, the fix is, in core/ file, change the version number for variable renderscript_target_api := 21.


Android Root Detection Techniques

I am trying to create a exhaustive list of various techniques which can be used to detect whether an Android device is rooted or not. These techniques have been taken from various sources, listed in the references section at the end of this post. If you are aware of techniques apart from one mentioned in the post, please add them in the comment.

  • Check Installed Packages:
    1. SuperSU app
    2. Rooting apps: Apps that exploit privilege-escalation vulnerabilities to root the device (e.g One Click Root, iRoot)
    3. Root Apps: Apps that require root privileges for their functions. E.g busybox, SetCPU, Titanium Backup.
    4. Root Cloakers: Apps that hide whether the device is rooted or not. e.g Root Cloaker, Root Cloaker Plus.
    5. API hooking frameworks: Libraries that provide API hooking functions. E.g Cydia Substrate, Xpose Framework.
  • Check Installed Files:
    1. Static Paths:
      1. /system/xbin/su, /system/bin/su or /system/xbin/../xbin/su (path manipulated)
      2. /system/xbin/busybox and all symbolic links of commands created by BusyBox.
      3. /data/app/<APK name> or /system/app/<APK name> of popular apps packages that are installed during or after rooting.
    2. Dynamic Paths: Parse the PATH variable, appending “/su” to each entry; open each in a loop
  • Check the BUILD tag: stock android images from Google are built with “release-keys” tag. If “test-keys” are presented, this can probably mean the Android image is developer build or an unofficial build. This value is basically from “”.
  • Check Directory Permissions:
    1. Rooting makes certain root folders readable, like /data, or writable, like /etc, /system/xbin, /system, /proc, /vendor/bin etc. Run the mount command and check if any device is mounted with “rw” flag, or try to create a file under “/system” or “/data” folder.
    2. Attempt to mount “/system” partition with command “mount -o remount, rw /system“, and check the return code.
  • Check Processes/Services/Tasks:
    1. ActivityManager.getRunningAppProcesses method returns a list of currently running application processes. This API can be used to determine if any app which requires root privileges is running.
    2. getRunningServices or getRunningTasks: Get currently running services or tasks.
  • Check Rooting Traits Using Shell Commands: Using Runtime.exec, ProcessBuilder or execve()
    1. su
    2. which su
    3. ps | grep <target> : lists currently running processes.
    4. ls – <target>: check existence of a file in the file system
    5. pm list packages
    6. pm path <package>: Output the full path of the targeted package
    7. cat /system/build.prop or grep Check whether =release-keys. This test can be used only as an indicator, as there are many contrary observations in the wild.
    8. Build Version: “ro.modversion” can be used to identify certain custom Android ROMs (e.g CyanogenMod)
  • Check System Properties:
    1., means adb shell will be running as root, instead of shell user.
    2. ro.debuggable =1 or service.adb.root=1, then adb will run as root as well.

Needless to say, these techniques can be bypassed by function hooking or custom build Android ROMs etc.



Security Implications of Zygote Process Creation Model

In the previous post I discussed about the Zygote process creation model in Android OS and importance of having different process creation model than a linux process in a mobile device. Before getting into the technical specifics, it is advisable to freshen the concept pertaining to Linux process creation and ASLR.

In  Zygote process creation model, a process template is created on the system startup, having the Dalvik VM (DVM) instance initialized and also other essential libraries loaded. When an application launch request is received, this template process is forked and application is loaded into it, saving significant time by avoiding loading of libraries and in instantiating DVM on every launch. Also, Linux COW (copy-on-write) helps in reducing global memory usage. But this trade-off have a major security implication.

Zygote process creation model causes two types of  memory  layout  sharing  on  Android,  which  undermine  the effectiveness of ASLR. Firstly, the code of an application is always loaded at the exact same memory location across different runs even when  ASLR  is  present;  and  secondly,  all  running apps  inherit  the  commonly  used  libraries  from  the  Zygote process (including the libc library) and thus share the same virtual memory mappings of these libraries. If an attacker is able to get the memory mapping information for one process, he can easily predict for the target process (as both share same mappings for Zygote loaded libraries). Thus developing ROP attacks becomes very easy, in spite of having ASLR. For more details, read this excellent article by Copperhead team.

After understanding the pros and cons of the approach, it must raise the curiosity that how can it be fixed without breaking the existing applications. To start with, one approach is to switch to Linux style process creation model, i.e, fork and exec, rather than creating a template and keep forking it subsequently. This approach fixes the security issue, but raises the very issue for which Zygote model was created. We will revisit this approach again at the end and re-evaluate its applicability in light of new performance enhancements measures introduced in Android.

Another approach to fix the security issue with Zygote process creation model was proposed by  Lee et. al. in this paper. They name their approach as Morula process creation model. Why it is called Morula is left as an exercise for the readers.

 Morula Process Creation Model:
In this approach, a template process performs the common and time-consuming initialisation tasks beforehand.  The whole process is divided into 2 steps:

1. Preparation Phase: initiated by the Activity Manager.  A preparation request is made to the Zygote process, when the system is idle or lightly occupied. The Zygote process forks a child, which immediately call a exec() to establish a new memory image with a fresh randomized layout. Then, the new process constructs DVM instance and loads all shared libraries and common Android classes, which would tremendously prolong the app launch time if not done in advance. Now the Morula process is fully created, waiting for the request to start an app. Multiple Morula process can be created in order to accommodate a flurry of requests to start several apps. If these newly created process is not used immediately, the  process enters the sleep mode and awakened only when needed.

2. Transition Phase: When the Activity Manager requests an app launch,  the request is routed through Zygote process first, where a decision is made regarding if the app should be started in a Morula process or in a fork of the Zygote process. Having this option allows the Morula model to be backward compatible with the Zygote model, in order to carry out an optimization strategy. Depending on the configuration, using either of the process the application is launched.

From Android 4.4.4 onward, Google was previewing a new runtime environment instead of Dalvik, called ART. From Android 5.0 it was fully deployed and considered to be faster than previous Dalvik VM. Factoring in the advances made by ART, Copperhead experimented with fork and exec approach in their Copperhead OS. As per their findings, “The Morula proof of concept code has some issues like file descriptor leaks and needs to be ported to Lollipop. It’s much less important now that ART has drastically improved start-up time without the zygote.” To conclude, with ART, fork and exec approach is not as slow as compared with Dalvik runtime. For security paranoid users, a small performance glitch should not be a big barrier.

It is highly recommended to read both, the Morula paper and blog by Copperhead to understand the nitty-gritty of the topic. And for the hackers out their, patch for implementing Morula is available here.

Android Zygote

In this post I will discuss about a very interesting piece of Android Operating System. If you have worked with Android, you might have run the ps command and might have observed that all applications have same parent PID (PPID). Android takes an unconventional approach to spawn processes, which ensure application startup is snappy. The process from which all the Android applications are derived is called Zygote. So in the screenshot below, all the applications have PPID of 1914, which is the PID of Zygote. In the rest of the post, I will talk about what is the need of Zygote, how does it come into existence and some discussion about Zygote in general.


Need of Zygote?

In a typical Linux process, when a process is started – by forking parent process, it goes through various setup steps, including loading of libraries and resources. The details are out of scope for this post. This process setup consumes time and on our beefy desktops, it is hardly noticeable. But in case of Android, not all devices are high spec and this process setup is noticeable to end user. As a workaround to normalize the process startup times on various devices, Android coldstarts a process during OS startup, from which applications can be forked when required. This process is called Zygote.

Zygote Startup?

After the Android device is turned on, and following all booting up steps, then init system starts, and run /init.rc file to setup various environment variables, mount points, start native daemons etc. There are many resources available on the internet discussing about Android bootup process and init system as well, and thus bypassed in this post. While executing init.rc, that Zygote is started. There is no binary directly corresponding to Zygote, instead it is started by a binarycalled app_process. Corresponding line in init.rc can be found here.

service zygote /system/bin/app_process -Xzygote /system/bin –zygote –start-system-server

app_process firstly starts Android Runtime, which in turn starts Dalvik VM of the system, and finally Dalvik VM invokes Zygote’s main().

Zygote initilization can be simplified into following steps:
1.Register Zygote socket (listens for connections on /dev/socket/zygote) for requests to start new apps
2. preload all java classes
3. preload resources
4. start system server (not covered in this post)
5. open socket
6. listen to connections

Zygote Socket

As mentioned above, zygote opens up a socket, /dev/socket/zygote, and listens on it for requests to start new applicationss. On receiving a request, it simply forks itself and launches the new requested application. So now the new application have Dalvik VM already loaded, including other necessary libraries and resources, and can start with execution straight away.

One feature of Linux is important to understand over here, Copy-on-Write (COW) policy for forks. Forking in Linux involves creating a new process, which is an exact copy of the parent process. With COW, Linux doesn’t actually copy anything. On the contrary, it just maps the pages of the new process over to those of the parent process and makes copies only when the new process writes to a page. Thus, saving significant amount of memory and setup time as well. To add, in case of Android, these pages are never written, as the libraries are mostly immutable and hardly changed over the process lifetime.

If you want to learn more details, with corresponding code, this is an excellent resource.

In upcoming post I will discuss about the security implications of having Zygote model and various existing alternative workarounds.