安卓逆向入门-Unidbg入门

入门

Unidbg是个基于Unicorn的逆向工具,可黑盒调用Android和iOS的so文件。它不需要直接运行App或逆向so文件,通过在App中找到对应JNI接口,用Unicorn引擎直接执行该so文件。

先给Intellij IDEA配Maven环境。先下载Maven:https://maven.apache.org/download.cgi,并将\apache-maven-3.9.9\bin目录添加到环境变量,在IDEA设置中“构建、执行、部署\-\>构建工具\-\>Maven”的主路径设为\apache-maven-3.9.9,用户配置文件为\apache-maven-3.9.9\conf\settings.xml,本地仓库自己随便搞一个,取消使用\.mvn/maven\.config中的设置。再在“Maven\-\>运行程序”中虚拟机选项添加-DarchetypeCatalog=internal。

打开settings.xml,在mirrors标签内添加一个mirror块:

1
2
3
4
5
6
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>

Github上下载Unidbg,打开项目,并且加载Maven依赖。找到unidbg-android/src/test/java/com/bytedance.frameworks.core.encrypt/TTEncrypt并运行main,看到控制台输出相关调用信息,则说明导入成功。

创建一个Native程序,其中Dalvik层这样写:

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
package com.example.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.example.myapplication.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
// Used to load the 'myapplication' library on application startup.
static {
System.loadLibrary("myapplication");
}
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// Example of a call to a native method
TextView tv = binding.sampleText;
tv.setText(stringFromJNI());
tv.setText(getKey(stringFromJNI()));
}
/**
* A native method that is implemented by the 'myapplication' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
public native String getKey(String keyb);
}

布局:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/sample_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

native-lib.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
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL Java_com_example_myapplication_MainActivity_stringFromJNI(JNIEnv* env,jobject) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
};
static const char base64_chars[] ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char *base64_encode(const std::string &input) {
const unsigned char *data = (const unsigned char *)input.c_str();
int input_length = input.size();
int i = 0;
int j = 0;
int len = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
char *encoded_string = (char *)malloc(((input_length + 2) / 3) * 4 + 1);
if (encoded_string == NULL) return NULL;
while (input_length--) {
char_array_3[i++] = *(data++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for(i = 0; (i < 4) ; i++)
encoded_string[len++] = base64_chars[char_array_4[i]];
i = 0;
};
};
if (i) {
for(j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++)
encoded_string[len++] = base64_chars[char_array_4[j]];
while((i++ < 3))
encoded_string[len++] = '=';
};
encoded_string[len] = '\0';
return encoded_string;
};
extern "C" JNIEXPORT jstring JNICALL Java_com_example_myapplication_MainActivity_getKey(JNIEnv* env,jobject,jstring keyb){
std::string str="string to encode";
char* kb=NULL;
jclass class_string=env->FindClass("java/lang/String");
jstring kbcode=env->NewStringUTF("GB2312");
jmethodID mid=env->GetMethodID(class_string,"getBytes","(Ljava/lang/String;)[B");
jbyteArray barr=(jbyteArray)env->CallObjectMethod(keyb,mid,kbcode);
jsize klen=env->GetArrayLength(barr);
jbyte* ba=env->GetByteArrayElements(barr,JNI_FALSE);
if(klen>0) {
kb = (char*) malloc(klen + 1);
memcpy(kb, ba, klen);
kb[klen] = 0;
};
env->ReleaseByteArrayElements(barr,ba,0);
std::string stemp(kb);
free(kb);
std::string newKey=str+stemp;
char* encoded = base64_encode(newKey);
jstring result = env->NewStringUTF(encoded);
free(encoded);
return result;
};

随后在Unidbg工程中unidbg-master\unidbg-android\src\test\java\com下新建软件包com.unidbg_example,下有源代码EncryptUtilsJni.java:

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
package com.unidbg_example;
import com.alibaba.fastjson.util.IOUtils;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.Unicorn2Factory;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.memory.Memory;
import java.io.File;
public class EncryptUtilsJni {
private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
private final DvmClass EncryptUtils;
private final boolean logging;
EncryptUtilsJni(boolean logging){
this.logging = logging;
emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.example.test").addBackendFactory(new Unicorn2Factory(true)).build(); //创建模拟器实例 模拟器进程名随便定
final Memory memory = emulator.getMemory(); //模拟器的内存操作接口
memory.setLibraryResolver(new AndroidResolver(34)); //设置系统类库解析
vm = emulator.createDalvikVM(); //创建Android虚拟机
vm.setVerbose(logging); //设置是否打印Jni调用细节
DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/resources/example_binaries/libmyapplication.so"), false); //加载.so到unicorn虚拟内存 加载成功后会默认调用init_array等函数
dm.callJNI_OnLoad(emulator); //手动执行JNI_OnLoad函数
module = dm.getModule(); //加载好的.so对应为一个模块
EncryptUtils = vm.resolveClass("com/example/myapplication/MainActivity");
}
void destroy(){
IOUtils.close(emulator);
if (logging) {
System.out.println("destroy");
}
}
private void getKey(){
DvmObject<?> strRc=EncryptUtils.callStaticJniMethodObject(emulator,"stringFromJNI()Ljava/lang/String;");
System.out.println("call stringFromJNI rc="+strRc.getValue());
String keyb=strRc.getValue().toString();
strRc=EncryptUtils.callStaticJniMethodObject(emulator,"getKey(Ljava/lang/String;)Ljava/lang/String;",vm.addLocalObject(new StringObject(vm,keyb)));
System.out.println("call getKey:"+strRc.getValue());
}
public static void main(String[] args){
EncryptUtilsJni encryptUtilsJni=new EncryptUtilsJni(true);
encryptUtilsJni.getKey();
encryptUtilsJni.destroy();
}
}

稍微有点报错,先搁着。