类似的例子还有
com.github.unidbg.android.Android64Test学习内容
- 手动调用native方法
- 手动调用JNI_OnLoad方法
- 手动调用模拟器入口方法
- 对jni里的env调用callStaticFloatMethod、newObject、setFloatField、SetDoubleField、
getStaticBooleanField等方法进行hook
- 从源码中知道test文件和libnative.so文件的生成分别对应test.cpp和native.c
- 模拟器入口对应test.cpp里面的main方法
- JNI_OnLoad在native.c里定义
- native方法的实现也在native.c里实现
package com.github.unidbg.android;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.arm.backend.BackendFactory;
import com.github.unidbg.arm.backend.Unicorn2Factory;
import com.github.unidbg.file.linux.AndroidFileIO;
import com.github.unidbg.hook.hookzz.HookZz;
import com.github.unidbg.linux.ARM32SyscallHandler;
import com.github.unidbg.linux.android.AndroidARMEmulator;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.AbstractJni;
import com.github.unidbg.linux.android.dvm.baseVM;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmClass;
import com.github.unidbg.linux.android.dvm.DvmObject;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.linux.android.dvm.VarArg;
import com.github.unidbg.linux.file.Stdout;
import com.github.unidbg.linux.struct.Dirent;
import com.github.unidbg.linux.thread.ThreadJoinVisitor;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.memory.SvcMemory;
import com.github.unidbg.unix.UnixSyscallHandler;
import com.sun.jna.Pointer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
public class AndroidTest extends AbstractJni {
private static final Log log = LogFactory.getLog(AndroidTest.class);
// 函数入口
public static void main(String[] args) throws IOException {
new AndroidTest().test();
}
private final AndroidEmulator emulator;
private final Module module;
private final DvmClass cJniTest;
private static class MyARMSyscallHandler extends ARM32SyscallHandler {
private MyARMSyscallHandler(SvcMemory svcMemory) {
super(svcMemory);
}
@Override
protected int fork(Emulator> emulator) {
// 获取线程id
return emulator.getPid();
}
}
private AndroidTest() {
final File executable = new File("unidbg-android/src/test/native/android/libs/armeabi-v7a/test");
// 创建模拟器
emulator = new AndroidARMEmulator(executable.getName(),
new File("target/rootfs"),
Collections.singleton(new Unicorn2Factory(true))) {
@Override
protected UnixSyscallHandler createSyscallHandler(SvcMemory svcMemory) {
// todo1 这个的作用我暂时也不知道
return new MyARMSyscallHandler(svcMemory);
}
};
// 获取模拟器内存对象
Memory memory = emulator.getMemory();
// 设置是否打印Jni调用细节
emulator.getSyscallHandler().setVerbose(false);
AndroidResolver resolver = new AndroidResolver(23);
memory.setLibraryResolver(resolver);
// todo ,推测patchThread表示对某个线程进行hook
resolver.patchThread(emulator, HookZz.getInstance(emulator), new ThreadJoinVisitor() {
@Override
public boolean canJoin(Pointer start_routine, int threadId) {
log.info("canJoin start_routine=" + start_routine + ", threadId=" + threadId);
return true;
}
});
//加载模块
module = emulator.loadLibrary(executable, true);
//创建虚拟机
VM vm = emulator.createDalvikVM();
// 设置是否打印Jni调用细节
vm.setVerbose(true);
vm.setJni(this);
//加载模块libnative.so
DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/native/android/libs/armeabi-v7a/libnative.so"), true);
//手动调用JNI_onLoad
dm.callJNI_onLoad(emulator);
// 需要模拟的类
this.cJniTest = vm.resolveClass("com/github/unidbg/android/JniTest");
{
// 动态分配栈
Pointer pointer = memory.allocateStack(0x100);
// todo : Dirent -> UnidbgStructure ->Structure
System.out.println(new Dirent(pointer));
}
}
@Override
public DvmObject> newObject(baseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
if ("com/github/unidbg/android/AndroidTest->()V".equals(signature)) {
return dvmClass.newObject(null);
}
return super.newObject(vm, dvmClass, signature, varArg);
}
@Override
public void setFloatField(baseVM vm, DvmObject> dvmObject, String signature, float value) {
if ("com/github/unidbg/android/AndroidTest->floatField:F".equals(signature)) {
System.out.println("floatField value=" + value);
return;
}
super.setFloatField(vm, dvmObject, signature, value);
}
@Override
public void setDoubleField(baseVM vm, DvmObject> dvmObject, String signature, double value) {
if ("com/github/unidbg/android/AndroidTest->doubleField:D".equals(signature)) {
System.out.println("doubleField value=" + value);
return;
}
super.setDoubleField(vm, dvmObject, signature, value);
}
@Override
public float callStaticFloatMethod(baseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
if ("com/github/unidbg/android/AndroidTest->testStaticFloat(FD)F".equals(signature)) {
float f1 = varArg.getFloatArg(0);
double d2 = varArg.getDoubleArg(1);
System.out.printf("callStaticFloatMethod f1=%s, d2=%s%n", f1, d2);
return 0.0033942017F;
}
return super.callStaticFloatMethod(vm, dvmClass, signature, varArg);
}
@Override
public double callStaticDoubleMethod(baseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
if ("com/github/unidbg/android/AndroidTest->testStaticDouble(FD)D".equals(signature)) {
return 0.0023942017D;
}
return super.callStaticDoubleMethod(vm, dvmClass, signature, varArg);
}
@Override
public boolean getStaticBooleanField(baseVM vm, DvmClass dvmClass, String signature) {
if ("com/github/unidbg/android/AndroidTest->staticBooleanField:Z".equals(signature)) {
return true;
}
return super.getStaticBooleanField(vm, dvmClass, signature);
}
@Override
public void setStaticDoubleField(baseVM vm, DvmClass dvmClass, String signature, double value) {
if ("com/github/unidbg/android/AndroidTest->staticDoubleField:D".equals(signature)) {
System.out.println("staticDoubleField value=" + value);
return;
}
super.setStaticDoubleField(vm, dvmClass, signature, value);
}
@Override
public void setStaticFloatField(baseVM vm, DvmClass dvmClass, String signature, float value) {
if ("com/github/unidbg/android/AndroidTest->staticFloatField:F".equals(signature)) {
System.out.println("staticFloatField value=" + value);
return;
}
super.setStaticFloatField(vm, dvmClass, signature, value);
}
private void test() {
// 模拟调用native函数
cJniTest.callStaticJniMethod(emulator, "testJni(Ljava/lang/String;JIDZSFDBJF)V",
getClass().getName(), 0x123456789abcdefL,
0x789a, 0.12345D, true, 0x123, 0.456f, 0.789123D, (byte) 0x7f,
0x89abcdefL, 0.123f);
// Logger.getLogger(Stdout.class).setLevel(Level.WARN);
//
// 调用入口函数,这里指的是test.cpp里的main方法
int exitCode = module.callEntry(emulator);
// 按任意键后打印下面的信息
Backend backend = emulator.getBackend();
System.err.println("exit code: " + exitCode + ", backend=" + backend);
}
}
test.cpp #include遗留问题:#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "test.h" static int sdk_int = 0; static void *start_routine(void *arg) { void *ret = &sdk_int; printf("test_pthread start_routine arg=%p, ret=%pn", arg, ret); return ret; } static void test_pthread() { pthread_t thread = 0; void *arg = &thread; int ret = pthread_create(&thread, NULL, start_routine, arg); void *value = NULL; int join_ret = pthread_join(thread, &value); printf("test_pthread arg=%p, ret=%d, thread=0x%lx, join_ret=%d, value=%pn", arg, ret, thread, join_ret, value); } #define INFINITY_LIFE_TIME 0xFFFFFFFFU #define NIPQUAD(addr) ((unsigned char *)&addr)[0], ((unsigned char *)&addr)[1], ((unsigned char *)&addr)[2], ((unsigned char *)&addr)[3] static void test_netlink() { static __u32 seq = 0; struct rtattr *rta; int fd = socket(AF_NETlink, SOCK_DGRAM, NETlink_ROUTE); if(fd == -1) { printf("test_netlink code=%d, msg=%sn", errno, strerror(errno)); return; } struct { struct nlmsghdr n; struct ifaddrmsg r; int pad[4]; } req; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = sizeof(req); req.n.nlmsg_type = RTM_GETADDR; req.n.nlmsg_flags = NLM_F_MATCH | NLM_F_REQUEST; req.n.nlmsg_pid = 0; req.n.nlmsg_seq = seq; req.r.ifa_family = AF_UNSPEC; rta = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.n.nlmsg_len)); rta->rta_len = RTA_LENGTH(4); char buf[16384]; hex(buf, &req, sizeof(req)); int ret = sendto(fd, (void *)&req, sizeof(req), 0, NULL, 0); printf("test_netlink fd=%d, sizeof(req)=%zu, buf=%s, ret=%dn", fd, sizeof(req), buf, ret); memset(buf, 0, sizeof(buf)); int status = recv(fd, buf, sizeof(buf), 0); if (status < 0) { perror("test_netlink"); return; } if(status == 0) { printf("test_netlink EOFn"); return; } char str[16384]; hex(str, buf, status); printf("test_netlink status=%d, buf=%sn", status, str); struct nlmsghdr *nlmp; struct ifaddrmsg *rtmp; struct rtattr *rtatp; int rtattrlen; struct in_addr *inp; struct ifa_cacheinfo *cache_info; next_nlmp: for(nlmp = (struct nlmsghdr *)buf; status > sizeof(*nlmp);){ int len = nlmp->nlmsg_len; int req_len = len - sizeof(*nlmp); if (req_len<0 || len>status) { printf("test_netlink errorn"); return; } if (!NLMSG_OK(nlmp, status)) { printf("test_netlink NLMSG not OKn"); return; } rtmp = (struct ifaddrmsg *)NLMSG_DATA(nlmp); rtatp = (struct rtattr *)IFA_RTA(rtmp); int ctl_sock = socket(AF_INET, SOCK_DGRAM, 0); struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); ifr.ifr_ifindex = rtmp->ifa_index; ioctl(ctl_sock, SIOCGIFNAME, &ifr); ioctl(ctl_sock, SIOCGIFFLAGS, &ifr); printf("Index Of Iface: %d, name=%s, flags=0x%xn", rtmp->ifa_index, ifr.ifr_name, ifr.ifr_flags); close(ctl_sock); rtattrlen = IFA_PAYLOAD(nlmp); for (; RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen)) { if(rtatp->rta_type == IFA_LABEL){ const char *label = (const char *)RTA_DATA(rtatp); printf(" label: %sn", label); } if(rtatp->rta_type == IFA_CACHEINFO){ cache_info = (struct ifa_cacheinfo *)RTA_DATA(rtatp); if (cache_info->ifa_valid == INFINITY_LIFE_TIME) printf(" valid_lft forevern"); else printf(" valid_lft %usecn", cache_info->ifa_valid); if (cache_info->ifa_prefered == INFINITY_LIFE_TIME) printf(" preferred_lft forevern"); else printf(" preferred_lft %usecn",cache_info->ifa_prefered); } if(rtatp->rta_type == IFA_ADDRESS){ inp = (struct in_addr *)RTA_DATA(rtatp); // in6p = (struct in6_addr *)RTA_DATA(rtatp); // printf("addr0: " NIP6_FMT "n",NIP6(*in6p)); printf(" addr0: %u.%u.%u.%un",NIPQUAD(*inp)); } if(rtatp->rta_type == IFA_LOCAL){ inp = (struct in_addr *)RTA_DATA(rtatp); // in6p = (struct in6_addr *)RTA_DATA(rtatp); // printf("addr1: " NIP6_FMT "n",NIP6(*in6p)); printf(" addr1: %u.%u.%u.%un",NIPQUAD(*inp)); } if(rtatp->rta_type == IFA_BROADCAST){ inp = (struct in_addr *)RTA_DATA(rtatp); // in6p = (struct in6_addr *)RTA_DATA(rtatp); // printf("bcataddr: " NIP6_FMT "n",NIP6(*in6p)); printf(" Bcast addr: %u.%u.%u.%un",NIPQUAD(*inp)); } if(rtatp->rta_type == IFA_ANYCAST){ inp = (struct in_addr *)RTA_DATA(rtatp); // in6p = (struct in6_addr *)RTA_DATA(rtatp); // printf("anycastaddr: "NIP6_FMT"n",NIP6(*in6p)); printf(" anycast addr: %u.%u.%u.%un",NIPQUAD(*inp)); } } status -= NLMSG_ALIGN(len); nlmp = (struct nlmsghdr*)((char*)nlmp + NLMSG_ALIGN(len)); } status = recv(fd, buf, sizeof(buf), 0); printf("test_netlink status=%dn", status); if(status > 0) { goto next_nlmp; } close(fd); } static void test_stat() { struct stat st; printf("st_nlink=0x%lx, st_blocks=0x%lx, st_rdev=0x%lx, st_uid=0x%lx, st_mtime=0x%lx, size=%lun", (long) &st.st_nlink - (long) &st, (long) &st.st_blocks - (long) &st, (long) &st.st_rdev - (long) &st, (long) &st.st_uid - (long) &st, (long) &st.st_mtime - (long) &st, (unsigned long) sizeof(st)); } static void test_dirent() { struct dirent dt; fprintf(stdout, "dirent size=%lun", (unsigned long) sizeof(dt)); } static void test_ioctl() { struct ifconf ifc; struct ifreq ibuf[256]; ifc.ifc_len = sizeof ibuf; ifc.ifc_buf = (caddr_t)ibuf; int fd = socket(AF_INET, SOCK_DGRAM, 0); ioctl(fd, SIOCGIFCONF, (char *)&ifc); printf("sizeof ifconf=%lu, ifreq=%lun", (unsigned long) sizeof(struct ifconf), (unsigned long) sizeof(struct ifreq)); int i = 0; for (; i < ifc.ifc_len / sizeof(*ifc.ifc_ifcu.ifcu_req); ++i) { printf("ioctl %d %zu %s %dn", i, strlen(ibuf[i].ifr_name), ibuf[i].ifr_name, ibuf[i].ifr_addr.sa_family); } close(fd); } // 3.不可靠信号的丢失 static void signal_handler(int signo) { printf("received a signal: %dn", signo); } static void test_signal() { pid_t pid; sigset_t set; sigset_t oset; sigemptyset(&set); //清空 sigaddset(&set, SIGINT); //添加2号信号 sigaddset(&set, SIGRTMIN); //添加34号信号 sigprocmask(SIG_SETMASK, &set, &oset); //将这个集合设置为这个进程的阻塞信号集 //绑定信号 signal(SIGINT, signal_handler); signal(SIGRTMIN, signal_handler); sigprocmask(SIG_SETMASK, &oset, NULL); //解除绑定 } static void handler(int signo, siginfo_t *resdata, void *unknowp) { printf("signo=%dn", signo); printf("return data: %dn", resdata->si_value.sival_int); } static void test_signalaction() { pid_t pid = fork(); if(pid == -1) { perror("create fork"); return; } else if(pid == 0) { // 子进程 sleep(1); //发送信号 kill(getppid(), SIGINT); printf("send signal: %d success!n", SIGINT); kill(getppid(), SIGRTMIN); printf("send signal: %d success!n", SIGRTMIN); exit(0); } else { struct sigaction act; //初始化sa_mask sigemptyset(&act.sa_mask); act.sa_handler = signal_handler; act.sa_sigaction = handler; //一旦使用了sa_sigaction属性,那么必须设置sa_flags属性的值为SA_SIGINFO act.sa_flags = SA_SIGINFO; //注册信号 sigaction(SIGINT, &act, NULL); sigaction(SIGRTMIN, &act, NULL); } } __attribute__((constructor)) void init() { char sdk[PROP_VALUE_MAX]; __system_property_get("ro.build.version.sdk", sdk); sdk_int = atoi(sdk); printf("constructor sdk=%dn", sdk_int); } static void test_backtrace() { } static void test_statfs() { struct statfs stb; int ret = statfs("/data/app", &stb); char buf[1024]; hex(buf, &stb, sizeof(stb)); printf("test_statfs size=%d, ret=%d, hex=%sn", (int) sizeof(stb), ret, buf); } static float sFloat = 0.0023942017f; static float* float_func() { return &sFloat; } static void test_float() { float *f = float_func(); void *ptr = f; unsigned int *ip = (unsigned int *) ptr; printf("test_float size=%zu, ip=0x%xn", sizeof(float), *ip); } static void test_jni_float() { } static void test_sched() { int cpus = 0; int i = 0; cpu_set_t mask; cpu_set_t get; int pid = gettid(); cpus = sysconf(_SC_NPROCESSORS_ONLN); printf("cpus: %d, pid: %dn", cpus, pid); CPU_ZERO(&get); if (sched_getaffinity(pid, sizeof(get), &get) != 0) { printf("Get CPU affinity failure, ERROR: %sn", strerror(errno)); } else { for(int i = 0; i < cpus; i++) { if(CPU_ISSET(i, &get)) { printf("Running processor : %dn", i); } } char buf[1024]; hex(buf, &get, sizeof(get)); printf("Get CPU affinity success: buf=%sn", buf); } CPU_ZERO(&mask); CPU_SET(cpus - 1, &mask); if (sched_setaffinity(pid, sizeof(mask), &mask) != 0) { printf("Set CPU affinity failure, ERROR: %sn", strerror(errno)); } else { char buf[1024]; hex(buf, &mask, sizeof(mask)); printf("Set CPU affinity success: buf=%sn", buf); } CPU_ZERO(&get); if (sched_getaffinity(pid, sizeof(get), &get) != 0) { printf("Get CPU affinity failure, ERROR: %sn", strerror(errno)); } else { for(int i = 0; i < cpus; i++) { if(CPU_ISSET(i, &get)) { printf("Running processor : %dn", i); } } char buf[1024]; hex(buf, &get, sizeof(get)); printf("Get CPU affinity success: buf=%sn", buf); } } static int dl_iterate_phdr_callback(struct dl_phdr_info *info, size_t size, void *data) { const char *type; int p_type; printf("dl_iterate_phdr_callback Name: "%s" (%d segments) => %pn", info->dlpi_name, info->dlpi_phnum, info->dlpi_name); if(!info->dlpi_name) { return 0; } for (int j = 0; j < info->dlpi_phnum; j++) { p_type = info->dlpi_phdr[j].p_type; type = (p_type == PT_LOAD) ? "PT_LOAD" : (p_type == PT_DYNAMIC) ? "PT_DYNAMIC" : (p_type == PT_INTERP) ? "PT_INTERP" : (p_type == PT_NOTE) ? "PT_NOTE" : (p_type == PT_INTERP) ? "PT_INTERP" : (p_type == PT_PHDR) ? "PT_PHDR" : (p_type == PT_TLS) ? "PT_TLS" : (p_type == PT_GNU_EH_frame) ? "PT_GNU_EH_frame" : (p_type == PT_GNU_STACK) ? "PT_GNU_STACK" : (p_type == PT_GNU_RELRO) ? "PT_GNU_RELRO" : NULL; printf(" %2d: [%14p; memsz:%7jx] flags: %#jx; ", j, (void *) (info->dlpi_addr + info->dlpi_phdr[j].p_vaddr), (uintmax_t) info->dlpi_phdr[j].p_memsz, (uintmax_t) info->dlpi_phdr[j].p_flags); if (type != NULL) printf("%sn", type); else printf("[other (%#x)]n", p_type); } return strcmp("libnative.so", info->dlpi_name) == 0 ? size : 0; } static void test_dl_iterate_phdr() { int ret = dl_iterate_phdr(dl_iterate_phdr_callback, NULL); printf("test_dl_iterate_phdr sizeof(dl_phdr_info)=0x%x, sizeof(Phdr)=0x%x, ret=%dn", (unsigned int) sizeof(struct dl_phdr_info), (unsigned int) sizeof(ElfW(Phdr)), ret); } int main() { setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); fprintf(stderr, "Start test, stdin=%p, stdout=%p, stderr=%p, size=%lun", stdin, stdout, stderr, (unsigned long) sizeof(*stdout)); test_stat(); test_dirent(); test_ioctl(); if(sdk_int > 19) { test_signal(); test_signalaction(); } test_backtrace(); test_statfs(); test_sched(); test_float(); test_jni_float(); char sdk[PROP_VALUE_MAX]; __system_property_get("ro.build.version.sdk", sdk); test_dl_iterate_phdr(); test_netlink(); test_pthread(); printf("Press any key to exit: cmp=%dn", strcmp("23", sdk)); getchar(); return 0; }
- MyARMSyscallHandler起什么作用?
-
new Dirent(pointer)起什么作用?
-
resolver.patchThread(emulator, HookZz.getInstance(emulator), new ThreadJoinVisitor() 这个起什么作用?
- test.cpp里main函数执行的多个逻辑,还需要一个个的梳理



