在逆向分析APP的过程中,经常会遇到native函数调用,有些是可以通过正常的IDA动态调试分析其函数逻辑算法等,但有些函数由于多线程造成调试不便,甚至由于ollvm等混淆造成逆向困难就需要自行调用native函数实现想要的结果。
一般普通的native函数都是有导出函数的,因此只要找到导出函数名即可调用该native函数。
首先在java层找到native函数的调用
接下来在so中导出函数中该native函数
自己写一个带按钮的APP,调用该函数
public class MainActivity extends Activity { public Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); System.loadLibrary("Aes"); try { button = (Button)findViewById(R.id.btn_test); button.setOnClickListener(new OnClickListener() { public void onClick(View v) { try { String encrypt = Aes.aesEncode("12345", "350710466385995"); if (null != encrypt) { Log.d("bbk","encrypt:" + encrypt); } else { Log.d("bbk", "encrypt is NULL"); } } catch (Exception e) { for (int i = 0; i < e.getStackTrace().length; i++) { Log.i("bbk",e.getStackTrace()[i].toString()); } } } }); } catch (Exception e) { for (int i = 0; i < e.getStackTrace().length; i++) { Log.i("bbk",e.getStackTrace()[i].toString()); } } } }
模拟原APP调用类调用native函数
public class Aes { private static native String aesEncode(String str, String str2); }
将libAes.so及其依赖库libstlport.shared.so放入jni目录,并编写mk文件配置。
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := Aes LOCAL_SRC_FILES := libAes.so include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := stlport_shared LOCAL_SRC_FILES := libstlport_shared.so include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := my_objectname LOCAL_SRC_FILES := my_objectname.cpp include $(BUILD_SHARED_LIBRARY)
APP_ABI := armeabi-v7a
里面写#include<jni.h>即可
最后将APP安装到手机上运行即可输出调用结果
在逆向过程中还会遇到很多APP动态注册native函数,甚至是将导出函数隐藏的,就无法使用上述的调用方案了,本方案是通过函数地址来调用native函数,理论上只要找得到native函数在so中的地址就可以实现调用。
在java层找到native函数,确定其传入参数类型及个数
kxtmp函数是动态注册函数,现在字符串中搜索函数名,之后通过回溯找到函数注册地址
本方案主要是通过自己编写的so调用native函数所在的so,自己的so名为hookso
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); System.loadLibrary("hookso"); float value =TypedValue.complexToFloat(55297); Log.e("DEBUG", ": " + value); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { JNI.testfunc(); return true; } return super.onOptionsItemSelected(item); } }
public class JNI { public static native void testfunc(); }
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hookso LOCAL_SRC_FILES := hookso.cpp LOCAL_LDLIBS += -llog #log include $(BUILD_SHARED_LIBRARY)
APP_ABI := armeabi-v7a APP_STL := c++_shared APP_CPPFLAGS := -fexceptions -frtti
#include <stdio.h> #include <stdlib.h> #include <sys/ptrace.h> #include <sys/user.h> #include <sys/wait.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/syscall.h> #include <fcntl.h> #include <dlfcn.h> #include <dirent.h> #include <unistd.h> #include <string.h> #include <android/log.h> #include <elf.h> #include <errno.h> #include <jni.h> #include <sys/socket.h> #include <linux/un.h> #include <netinet/in.h> #include <pthread.h> #include <arpa/inet.h> #include <iostream> #ifdef __cplusplus extern "C" { #endif typedef u_int32_t (*common_func_t)(u_int32_t, u_int32_t, u_int32_t, u_int32_t); #define LOG_TAG "DEBUG" #define LOGD(lvl, fmt, args...) __android_log_print(lvl, \ LOG_TAG, fmt, ##args) void* get_module_base(pid_t pid, const char* module_name) { FILE* fp; long addr = 0; char *pch; char filename[32]; char line[1024]; if(pid <= 0){ snprintf(filename, sizeof(filename), "/proc/self/maps"); }else{ snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); } fp = fopen(filename,"r"); if(fp != NULL) { while(fgets(line, sizeof(line), fp)) { if(strstr(line, module_name)) { pch = strtok(line, "-"); addr = strtoul(pch, NULL, 16); break; } } fclose(fp); } return (void*)addr; } void Java_com_example_hookso_JNI_testfunc(JNIEnv *env, jobject jobj); #ifdef __cplusplus } using namespace std; void Java_com_example_hookso_JNI_testfunc(JNIEnv *env, jobject jobj) { string str = "1"; LOGD(ANDROID_LOG_WARN, "Java_com_example_hookso_JNI_testfunc: %s\n", str.c_str()); void *cs_handle = dlopen("/data/local/tmp/native.so", RTLD_NOW); LOGD(ANDROID_LOG_WARN, "[+] dlerror : %s", dlerror()); LOGD(ANDROID_LOG_WARN, "[+] cs_handle: %p", cs_handle); //导出用下面这一句 // void *cs_open_func = dlsym(cs_handle, "导出函数name"); u_int32_t addr1 = (u_int32_t)get_module_base(0, "ntiave.so"); //arm指令不加1 0,thumb指令加1 1 addr1 += 0x0000ABD4; common_func_t func = (common_func_t)addr1; jstring key = env->NewStringUTF("1"); // jstring str = env->NewStringUTF("qazwsxedcrfvtgbh64as65d465wq6d54a6s54d98aw4d65a4sd64qw6f46a4s9fd8d"); jstring ret1 = (jstring)func((u_int32_t)env, 0, (u_int32_t)key, 0); // jstring ret1 = (jstring)func((u_int32_t)env, 0, 0, 0); LOGD(ANDROID_LOG_DEBUG, "[+] test_jstr: %s, size: %d", env->GetStringUTFChars((jstring)ret1, false), env->GetStringUTFLength((jstring)ret1)); } #endif