Android强调native函数的两种方法

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

通过导出函数调用native函数

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

寻找导出函数

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

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

编写Android APP

自己写一个带按钮的APP,调用该函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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函数

1
2
3
4
public class Aes
{
private static native String aesEncode(String str, String str2);
}

jni配置

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

Android.mk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

1
APP_ABI := armeabi-v7a

my_objectname.app

里面写#include即可

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

通过函数地址调用native函数

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

寻找native函数地址

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

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

编写调用so

本方案主要是通过自己编写的so调用native函数所在的so,自己的so名为hookso

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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);
}
}

1
2
3
4
public class JNI
{
public static native void testfunc();
}

jni配置

Android.mk

1
2
3
4
5
6
7
8
9
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

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

hookso.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#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
有钱的捧个钱场
0%