JNI_Onload

要逆向,必须会正向编写。so文件混淆——jni_load()

JNI_Onload编写

参考
上一篇只是单纯的编写so文件,只要ida打开就可以直接看见方法名。这里对so进行简单的混淆,利用JNI_Onload()函数

check.java

1
2
3
4
5
6
7
8
9
package com.example.ese.zjgsuctf;
public class check {
static {
System.loadLibrary("check");
}
public native static String check(String sstr);
}

check.c

在jni目录创建一个check.c

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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#include <string.h>
#include <jni.h>
#include <stdio.h>
#include <assert.h>
#include <android/log.h>
#include "com_example_ese_zjgsuctf_check.h"
#define JNIREG_CLASS "com/example/ese/zjgsuctf/check"//指定要注册的类
typedef unsigned longULONG;
int enc_key[]={89,83,94,88,68,75,87,86,76,96,86,76,96,81,80,75,96,89,83,94,88,66};
int enc_flag[]={0, 79, 216, 222, 132, 213, 175, 19, 82, 136, 9, 16, 156, 179, 246, 113, 109, 5, 183, 122};
void rc4_init(unsigned char*s, unsigned char*key, unsigned long Len)
{
int i = 0, j = 0;
char k[256] = { 0 };
unsigned char tmp = 0;
for (i = 0; i<256; i++)
{
s[i] = i;
k[i] = key[i%Len];
}
for (i = 0; i<256; i++)
{
j = (j + s[i] + k[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
}
void rc4_crypt(unsigned char*s, unsigned char*Data, unsigned long Len)
{
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for (k = 0; k<Len; k++)
{
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] ^= s[t];
}
}
void ckeck_key(char *k)
{
int i,len = sizeof(enc_key)/sizeof(enc_key[0]);
for(i=0;i<len;i++)
{
k[i]=enc_key[i]^0x3f;
}
}
int crypt(unsigned char *pData)
{
char key[24];
unsigned char s[256] = { 0 };
int i,len = strlen(pData);
ckeck_key(key);
key[22]='\0';
rc4_init(s, (unsigned char*)key, strlen(key));
rc4_crypt(s, (unsigned char*)pData, len);
for(i=0;i<len;i++)
if(pData[i]!=enc_flag[i])
return 0;
return 1;
}
//JNI
__attribute__((section (".mytext")))jstring esecheck( JNIEnv* env,jobject thiz,jstring str1)
{
//LOGV("This is esecheck()");
const char *str = (*env)->GetStringUTFChars(env, str1, NULL);
if(str == NULL) {
return NULL;
}
int key = crypt(str);
char* tmpstr = "succeeded";
if(key)
{
jstring rtstr = (*env)->NewStringUTF(env, tmpstr);
return rtstr;
}else{
tmpstr = "failed!!";
jstring rtstr = (*env)->NewStringUTF(env, tmpstr);
return rtstr;
}
}
/**
* Table of methods associated with a single class.
*/
static JNINativeMethod gMethods[] = {//绑定,注意,V,Z签名的返回值不能有分号“;”
{ "check", "(Ljava/lang/String;)Ljava/lang/String;", (void*)esecheck},
};
/*
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = (*env)->FindClass(env, className);
if (clazz == NULL) {
return JNI_FALSE;
}
if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
* Register native methods for all classes we know about.
*/
static int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, JNIREG_CLASS, gMethods,
sizeof(gMethods) / sizeof(gMethods[0])))
return JNI_FALSE;
return JNI_TRUE;
}
/*
* Set some test stuff up.
*
* Returns the JNI version on success, -1 on failure.
*/
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
assert(env != NULL);
if (!registerNatives(env)) {//注册
return -1;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;
return result;
}

Android.mk

1
2
3
4
5
6
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := check
LOCAL_SRC_FILES := check.c
include $(BUILD_SHARED_LIBRARY)
LOCAL_CFLAGS := -fvisibility=hidden #隐藏符号表

Application.mk

1
APP_ABI := all

JNINativeMethod gMethods[]

调用

在app目录下的build.gradle中添加

1
2
3
4
5
6
7
8
9
android {
......
sourceSets {
main() {
jniLibs.srcDirs = ['src/main/libs']
jni.srcDirs = [] //屏蔽掉默认的jni编译生成过程
}
}
}

这时一个简单的输入结构:

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
activity_main.xml
{
.....
<EditText
android:id="@+id/input_flag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="80dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="100dp"
android:ems="10"
android:inputType="textPersonName"
android:hint="input your flag"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="UnknownId" />
<Button
android:id="@+id/input_ok"
android:layout_width="102dp"
android:layout_height="45dp"
android:layout_marginBottom="276dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:text="确认"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.458"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="MissingConstraints" />
}

MainActivity结构

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
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final EditText sstr = (EditText) findViewById(R.id.input_flag);
final Button ok = (Button) findViewById(R.id.input_ok);
ok.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String st = sstr.getText().toString().trim();
String res = check.Check(st);
Toast.makeText(MainActivity.this, res, Toast.LENGTH_SHORT).show();
}
});
}
}

运行就可以得到调用native方法的apk了。

优点

1.源码改动少,只需要添加JNI_Onload函数
2.无需加解密so,就可以实现混淆so中的JNI函数
3.后续可以添加so加解密,使破解难度更大

Donate
-------------本文结束感谢您的阅读-------------