Thu 07 January 2016
Apparently Facebook is crashing their apps intentionally in order to test users reaction and evaluate their adherence to Facebook service. This post is however not about the user's behavioral analysis, but about the technical aspects of how it is done - or just an excuse to dive into JNI reversing.
First of all, what lead us to talk about JNI for the Facebook app crashing feature. Well using the Ostorlab Mobile Application Security Scanner (it's completely free and you can test it already) we discovered the following native methods:
Native method indicate that it is implemented in native code using JNI (Java Native Interface).
JNI is basically the way you can write code accessible from Java in C/C++. This is usually done for different reasons, performance could be one of them. The advantages of writing native code are mainly performance, better code obfuscation and the possibility to access low level functionality. Disadvantages are lack of portability and higher security risk.
Native methods are therefore excellent candidates from an attacker's perspective if they handle user's input. In the example of the Instagram app, native methods are used to handle images and videos and are therefore potentially vulnerable to memory corruption errors.
Reversing and Fuzzing of JNI seems to be an under-researched subject, despite it's important role for Android Mobile Applications.
Let's get back to our Facebook crashThisProcess
method, how can we find the method implementation ?
If we check Java JNI specification on how the JVM resolve methods, here is what is written:
After checking in the libs symbols by searching for the string Java_
we could not find any method named
crashThisProcess
.
Inspecting the OpenJDK VM confirms the specification but doesn't provide any more clues:
However searching for the string crashThisProcess, we found it in libbreakpad.so:
Let's reverse the binary and try to understand how this string is referenced. Opening the binary in IDA and searching for the string, we find it in the .rodata section:
This strings is referenced by the following structure:
After more research we found the structure matches the JNINativeMethod structure:
And this is a sample code the demonstrates how it could be used:
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "simplejni native.cpp"
#include <utils/Log.h>
#include <stdio.h>
#include "jni.h"
static jint
add(JNIEnv *env, jobject thiz, jint a, jint b) {
int result = a + b;
ALOGI("%d + %d = %d", a, b, result);
return result;
}
static const char *classPathName = "com/example/android/simplejni/Native";
static JNINativeMethod methods[] = {
{"add", "(II)I", (void*)add },
};
/*
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
ALOGE("Native registration unable to find class '%s'", className);
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
ALOGE("RegisterNatives failed for '%s'", className);
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
* Register native methods for all classes we know about.
*
* returns JNI_TRUE on success.
*/
static int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, classPathName,
methods, sizeof(methods) / sizeof(methods[0]))) {
return JNI_FALSE;
}
return JNI_TRUE;
}
// ----------------------------------------------------------------------------
/*
* This is called by the VM when the shared library is first loaded.
*/
typedef union {
JNIEnv* env;
void* venv;
} UnionJNIEnvToVoid;
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
UnionJNIEnvToVoid uenv;
uenv.venv = NULL;
jint result = -1;
JNIEnv* env = NULL;
ALOGI("JNI_OnLoad");
if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("ERROR: GetEnv failed");
goto bail;
}
env = uenv.env;
if (registerNatives(env) != JNI_TRUE) {
ALOGE("ERROR: registerNatives failed");
goto bail;
}
result = JNI_VERSION_1_4;
bail:
return result;
}
The methods seem to be registered dynamically during loading in the JNI_OnLoad function using registerNativeMethods function. The last element points to the JNI method body. For instance in the case of the crashThisProcess method, this is the disassembly of the method body (the name of the subroutine was added manually):
We can see that the function writes 0x2A
to memory address 0x000000
, which will segfault the process.
Experimenting with method automated identification of these structure using the Java method signature unique format
and then checking the rest of the structure, allowed us to write a script (available on Github) that finds JNI
methods exported dynamically using JNI_OnLoad
or statically in the exported symbol:
Ostorlab Mobile Application Security Scanner will list these methods automatically in the upcoming versions.
We do newsletters, too
Get the latest news, updates, and product innovations from Ostorlab right in your inbox.