Android强调native函数的两种方法
2020-04-30 13:59:42 Author: bbs.pediy.com(查看原文) 阅读量:346 收藏

原文链接 https://yunnigu.dropsec.xyz/2020/04/29/Android%E5%BC%BA%E8%B0%83native%E5%87%BD%E6%95%B0%E7%9A%84%E4%B8%A4%E7%A7%8D%E6%96%B9%E6%B3%95/#more

在逆向分析APP的过程中,经常会遇到native函数调用,有些是可以通过正常的IDA动态调试分析其函数逻辑算法等,但有些函数由于多线程造成调试不便,甚至由于ollvm等混淆造成逆向困难就需要自行调用native函数实现想要的结果。

一般普通的native函数都是有导出函数的,因此只要找到导出函数名即可调用该native函数。

寻找导出函数

首先在java层找到native函数的调用

接下来在so中导出函数中该native函数

编写Android APP

自己写一个带按钮的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);
}

jni配置

将libAes.so及其依赖库libstlport.shared.so放入jni目录,并编写mk文件配置。

Android.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)

Application.mk

APP_ABI := armeabi-v7a

my_objectname.app

里面写#include<jni.h>即可

最后将APP安装到手机上运行即可输出调用结果

在逆向过程中还会遇到很多APP动态注册native函数,甚至是将导出函数隐藏的,就无法使用上述的调用方案了,本方案是通过函数地址来调用native函数,理论上只要找得到native函数在so中的地址就可以实现调用。

寻找native函数地址

在java层找到native函数,确定其传入参数类型及个数

kxtmp函数是动态注册函数,现在字符串中搜索函数名,之后通过回溯找到函数注册地址

编写调用so

本方案主要是通过自己编写的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();
}

jni配置

Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := hookso
LOCAL_SRC_FILES := hookso.cpp

LOCAL_LDLIBS += -llog  #log

include $(BUILD_SHARED_LIBRARY)

Application.mk

APP_ABI := armeabi-v7a
APP_STL := c++_shared
APP_CPPFLAGS := -fexceptions -frtti

hookso.cpp

#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

[培训]科锐逆向工程师培训班38期--远程教学预课班将于 2020年5月28日 正式开班!


文章来源: https://bbs.pediy.com/thread-259210.htm
如有侵权请联系:admin#unsafe.sh