一、问题描述
本篇描述一个常见且隐蔽的坑,新手很容易一头雾水。我写了个demo,里面包含两个方法。快来看看吧!
JAVA代码
package com.roysue.test623;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.TreeMap;
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn1 = findViewById(R.id.btn1);
TextView tv1 = findViewById(R.id.tv1);
Button btn2 = findViewById(R.id.btn2);
TextView tv2 = findViewById(R.id.tv2);
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
tv1.setText(getFilePath());
}
});
btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
TreeMap<String, String> keymap = new TreeMap<String, String>();
keymap.put("appkey", "123");
if(getMapisEmpty(keymap)){
tv2.setText("传入map为空");
}else {
tv2.setText("传入map不为空");
};
}
});
}
public native String getFilePath();
public native boolean getMapisEmpty(TreeMap map);
}
C代码
#include <jni.h>
#include <string>
extern "C"
JNIEXPORT jstring JNICALL
Java_com_roysue_test623_MainActivity_getFilePath(JNIEnv *env, jobject thiz) {
jclass ContextWrapper = env->FindClass("android/content/ContextWrapper");
jmethodID getFileDirMethodId = env->GetMethodID(ContextWrapper, "getFilesDir", "()Ljava/io/File;");
jclass fileClazz= env->FindClass("java/io/File");
jmethodID fileGetAbsolutePathMethodId = env->GetMethodID(fileClazz, "getAbsolutePath", "()Ljava/lang/String;");
jobject file = env->CallObjectMethod(thiz, getFileDirMethodId);
jstring path = static_cast<jstring>(env->CallObjectMethod(file, fileGetAbsolutePathMethodId));
return path;
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_roysue_test623_MainActivity_getMapisEmpty(JNIEnv *env, jobject thiz, jobject map) {
jclass c_map = env->FindClass("java/util/Map");
jmethodID m_isempty = env->GetMethodID(c_map,"isEmpty","()Z");
jboolean b= env->CallBooleanMethod(map,m_isempty);
return b;
}
正常运行结果
二、Unidbg模拟执行遇到的问题
先模拟执行getFIleDir这个函数
package com.error;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
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.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.memory.Memory;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class demo extends AbstractJni {
private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
public DvmClass cNative;
demo(){
emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.demo").build();
final Memory memory = emulator.getMemory(); // 模拟器的内存操作接口
memory.setLibraryResolver(new AndroidResolver(23)); // 设置系统类库解析
vm = emulator.createDalvikVM(null); // 创建Android虚拟机
vm.setVerbose(true); // 设置是否打印Jni调用细节
DalvikModule dm = vm.loadLibrary(new File("unidbg-android\\src\\test\\java\\com\\error\\libnative-lib.so"), true);
module = dm.getModule();
cNative = vm.resolveClass("com/roysue/test623/MainActivity");
vm.setJni(this);
dm.callJNI_OnLoad(emulator);
}
public static void main(String[] args) {
demo test = new demo();
test.getFilePath();
}
public void getFilePath(){
List<Object> list = new ArrayList<>(10);
list.add(vm.getJNIEnv()); // 第一个参数是env
DvmObject<?> cnative = cNative.newObject(null);
list.add(cnative.hashCode()); // 第二个参数,实例方法是jobject,静态方法是jclazz,直接填0这里是不行的,此样本参数2被使用了
Number number = module.callFunction(emulator, 0x8F04 + 1, list.toArray())[0];
StringObject result = (StringObject) ((DvmObject[])((ArrayObject)vm.getObject(number.intValue())).getValue())[0];
System.out.println(result.getValue());
};
}
需要注意的是,以往我一直直接把参数2填0,这是偷懒但有风险的做法,还是建议老老实实初始化类或对象,传hashCode进去。
运行测试
这是为什么呢?而且从报错中都看不出什么。
再试试判断map是否为空的函数
public void MapisEmpty(){
List<Object> list = new ArrayList<>(10);
list.add(vm.getJNIEnv()); // 第一个参数是env
DvmObject<?> cnative = cNative.newObject(null);
list.add(cnative.hashCode()); // 第二个参数,实例方法是jobject,静态方法是jclazz,直接填0这里是不行的,此样本参数2被使用了
TreeMap<String, String> keymap = new TreeMap<String, String>();
keymap.put("build", "6180500");
DvmObject<?> input_map = vm.resolveClass("java/util/TreeMap").newObject(keymap);
list.add(vm.addLocalObject(input_map));
Number number = module.callFunction(emulator, 0x9044 + 1, list.toArray())[0];
};
为什么会这样?
三、解决办法
到底是什么问题呢?
getMethodId使用啥class,对象要手动继承这个class,从而打通调用链,如下的代码就可以不报错,继续运行往下了。
package com.error;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
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.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.memory.Memory;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;
public class demo extends AbstractJni {
private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
public DvmClass cNative;
demo(){
emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.yrx").build();
final Memory memory = emulator.getMemory(); // 模拟器的内存操作接口
memory.setLibraryResolver(new AndroidResolver(23)); // 设置系统类库解析
vm = emulator.createDalvikVM(null); // 创建Android虚拟机
vm.setVerbose(true); // 设置是否打印Jni调用细节
DalvikModule dm = vm.loadLibrary(new File("unidbg-android\\src\\test\\java\\com\\error\\libnative-lib.so"), true);
module = dm.getModule();
DvmClass ContextWrapper = vm.resolveClass("android/content/ContextWrapper");
cNative = vm.resolveClass("com/roysue/test623/MainActivity", ContextWrapper);
vm.setJni(this);
dm.callJNI_OnLoad(emulator);
}
public static void main(String[] args) {
demo test = new demo();
test.getFilePath();
test.MapisEmpty();
}
public void getFilePath(){
List<Object> list = new ArrayList<>(10);
list.add(vm.getJNIEnv()); // 第一个参数是env
DvmObject<?> cnative = cNative.newObject(null);
list.add(cnative.hashCode()); // 第二个参数,实例方法是jobject,静态方法是jclazz,直接填0这里是不行的,此样本参数2被使用了
Number number = module.callFunction(emulator, 0x8F04 + 1, list.toArray())[0];
StringObject result = (StringObject) ((DvmObject[])((ArrayObject)vm.getObject(number.intValue())).getValue())[0];
System.out.println(result.getValue());
};
public void MapisEmpty(){
List<Object> list = new ArrayList<>(10);
list.add(vm.getJNIEnv()); // 第一个参数是env
DvmObject<?> cnative = cNative.newObject(null);
list.add(cnative.hashCode()); // 第二个参数,实例方法是jobject,静态方法是jclazz,直接填0这里是不行的,此样本参数2被使用了
TreeMap<String, String> keymap = new TreeMap<String, String>();
keymap.put("build", "6180500");
DvmClass Map = vm.resolveClass("java/util/Map");
DvmObject<?> input_map = vm.resolveClass("java/util/TreeMap", Map).newObject(keymap);
list.add(vm.addLocalObject(input_map));
Number number = module.callFunction(emulator, 0x9044 + 1, list.toArray())[0];
};
}
版权声明:本文为qq_38851536原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。