Register from r0 to r15 r0 is used as a return value of functions r11 is used like EBP (called FP) r12 intra-procedure-call scratch register (called IP) r13 is used like ESP (called SP) r14 is Link-Register (called LR) r15 is used like EIP (called PC) all register are completely general, you can set a value to r15 directly ldr r1, [pc] ;(pc point to ‘.long 0x00001337’) b go_next .long 0x00001337
r1 register will have 0x1337 ARM中的三级流水线,当前指令在执行,下一条在译码,再下一条正在读取 参数传递 ARM中的函数参数是通过r0~r3进行传递的,参数超过4个时,超出的部分会通过栈来传递。 mov mov r1, r2 ;r1=r2 mov r1, #0x80 ;r1 = 0x80 push push 0x10 ;push 0x10 onto stack push {r1} ;push r1 register onto stack push {r11, lr} ;push from right to left (push lr, push r11) push {r1-r5} ;push r1,r2,r3,r4 and r5 onto stack pop pop {r11, pc} ;pop r11, pop pc ldr ldr{type}{cond} Rd, label ldr r1, [r2] ;r1 = *r2 ldr r1, [r2, #0x10] ;r1 = *(r2+0x10) type指明操作数的位数:
type
含义
B
无符号字节 8位
SB
有符号字节 8位
H
无符号半字 16位
SH
有符号半字 16位
字是32位,不需要指定type。加载数据时会根据有/无符号,将数据符号/零扩展为32位。 str str r1, [r2] ;*r2 = r1 str r1, [r2, #0x1] ;*(r2+1)=r1 b/bl b 0x8080 ;jump to 0x8080 bl 0x8080 ;jump to 0x8080 and save next instruction address of current into lr register bx/blx bx{cond} Rm 带状态切换的跳转指令,操作数为寄存器。满足条件cond,处理器判断Rm的最低位如果为1,则跳转时自动将CPSR寄存器的标志T置位,并将目标地址的代码解释为Thumb代码;如果Rm的最低位为0,则跳转时自动将CPSR寄存器的标志T复位,并将目标地址的代码解释为ARM。 add add r1, r2 ;r1 = r1 + r2 add r1, #0x10 ;r1 = r1 + 0x10 add r1, r2, r3 ;r1 = r2 + r3 add r1, r2, #0x10 ;r1 = r2 + 0x10 sub sub r1, r2 ;r1 = r1 - r2 cmp cmp r1, r2 ;r1 - r2 系统调用 1.svc svc #0x900004 ;calling sys_write 2.swi mov r7, #4 ; write syscall swi 0 ; execute syscall 这两个是一样的
structmalloc_chunk { INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */ INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */
structmalloc_chunk* fd;/* double links -- used only if free. */ structmalloc_chunk* bk; /* Only used for large blocks: pointer to next larger size. */ structmalloc_chunk* fd_nextsize; structmalloc_chunk* bk_nextsize; }
$ make arm-linux-androideabi-gcc hook1.o -I/Users/fan/Computer/Android/adt-bundle-mac-x86_64-20140702/android-ndk-r10c/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/lib/gcc/arm-linux-androideabi/4.9/include-fixed -I/Users/fan/Computer/Android/adt-bundle-mac-x86_64-20140702/android-ndk-r10c/platforms/android-21/arch-arm/usr/include -L/Users/fan/Computer/Android/adt-bundle-mac-x86_64-20140702/android-ndk-r10c/platforms/android-21/arch-arm/usr/lib -fPIC -shared -llog -o hook1 /Users/fan/Computer/Android/adt-bundle-mac-x86_64-20140702/android-ndk-r10c/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/../../../../arm-linux-androideabi/bin/ld: error: cannot open crtbegin_so.o: No such file or directory /Users/fan/Computer/Android/adt-bundle-mac-x86_64-20140702/android-ndk-r10c/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/../../../../arm-linux-androideabi/bin/ld: error: cannot open crtend_so.o: No such file or directory collect2: error: ld returned 1 exit status make: *** [hook1] Error 1
反射是Java语言的一个特性,是Java被视为动态语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括modifiers(如public,static等)、superclass(如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。Java可以加载一个运行时才得知名称的class,获得其完整结构。 A Simple Example
1 2 3 4 5 6 7 8 9 10 11 12 13
import java.lang.reflect.*; publicclassDumpMethods{ publicstaticvoidmain(String args[]){ try { Class c = Class.forName(args[0]); Method m[] = c.getDeclaredMethods(); for (int i = 0; i < m.length; i++) System.out.println(m[i].toString()); } catch (Throwable e) { System.err.println(e); } } }
输出结果
1 2 3 4 5 6
$ java DumpMethods java.util.Stack public java.lang.Object java.util.Stack.push(java.lang.Object) public synchronized java.lang.Object java.util.Stack.pop() public synchronized java.lang.Object java.util.Stack.peek() public boolean java.util.Stack.empty() public synchronized int java.util.Stack.search(java.lang.Object)
Class classType = ExtendType.class; Object inst = classType.newInstance(); Method logMethod = classType.getDeclaredMethod("Log", new Class[] {String.class}); logMethod.setAccessible(true); logMethod.invoke(inst, new Object[] {"test"}); // 输出 Type:Default Constructor ExtendType:Default Constructor ExtendType:test
7.设置/获取类的属性值 通过反射获取类的Field对象,调用Field方法设置或获取值。
1 2 3 4 5 6
Class classType = ExtendType.class; Object inst = classType.newInstance(); Field intField = classType.getField("pubIntExtendField"); intField.setInt(inst, 100); int value = intField.getInt(inst); System.out.println(value);
/* * Extract "classes.dex" from zipFd into "cacheFd", leaving a little space * up front for the DEX optimization header. */ staticintextractAndProcessZip(int zipFd, int cacheFd, constchar* debugFileName, bool isBootstrap, constchar* bootClassPath, constchar* dexoptFlagStr) { ZipArchive zippy; // 用于描述ZIP压缩文件的数据结构 ZipEntry zipEntry; // 用于表示一个ZIP入口 size_t uncompLen; long modWhen, crc32; off_t dexOffset; // 用于表示在Odex文件中,原Dex文件的起始地址 int err; int result = -1; int dexoptFlags = 0; /* bit flags, from enum DexoptFlags */ // 设置默认的优化模式 DexClassVerifyMode verifyMode = VERIFY_MODE_ALL; DexOptimizerMode dexOptMode = OPTIMIZE_MODE_VERIFIED;
memset(&zippy, 0, sizeof(zippy)); /* make sure we're still at the start of an empty file */ if (lseek(cacheFd, 0, SEEK_END) != 0) { ALOGE("DexOptZ: new cache file '%s' is not empty", debugFileName); goto bail; } /* * Write a skeletal DEX optimization header. We want the classes.dex * to come just after it. */ err = dexOptCreateEmptyHeader(cacheFd); if (err != 0) goto bail; /* record the file position so we can get back here later */ // 取得Odex文件中原Dex文件的起始位置,实际就是一个Odex文件头部的长度 dexOffset = lseek(cacheFd, 0, SEEK_CUR); if (dexOffset < 0) goto bail; /* * Open the zip archive, find the DEX entry. */ if (dexZipPrepArchive(zipFd, debugFileName, &zippy) != 0) { ALOGW("DexOptZ: unable to open zip archive '%s'", debugFileName); goto bail; } // 获取目标Dex文件的解压入口 zipEntry = dexZipFindEntry(&zippy, kClassesDex); if (zipEntry == NULL) { ALOGW("DexOptZ: zip archive '%s' does not include %s", debugFileName, kClassesDex); goto bail; } /* * Extract some info about the zip entry. */ if (dexZipGetEntryInfo(&zippy, zipEntry, NULL, &uncompLen, NULL, NULL, &modWhen, &crc32) != 0) { ALOGW("DexOptZ: zip archive GetEntryInfo failed on %s", debugFileName); goto bail; } uncompLen = uncompLen; modWhen = modWhen; crc32 = crc32; /* * Extract the DEX data into the cache file at the current offset. * 从ZIP文件将目标Dex文件解压出来,并写入cacheFd所指文件,此时cacheFd所指文件非空, * 包括一个Odex文件头部加上一个原始的Dex文件 */ if (dexZipExtractEntryToFile(&zippy, zipEntry, cacheFd) != 0) { ALOGW("DexOptZ: extraction of %s from %s failed", kClassesDex, debugFileName); goto bail; } /* Parse the options. */ /* * 入口参数dexoptFlagStr,对验证优化需求进行分析,dexoptFlagStr实际上是一个字符串, * 记录了验证优化的要求 */ if (dexoptFlagStr[0] != '\0') { constchar* opc; constchar* val; // 设置验证模式 opc = strstr(dexoptFlagStr, "v="); /* verification */ if (opc != NULL) { switch (*(opc+2)) { case'n': verifyMode = VERIFY_MODE_NONE; break; case'r': verifyMode = VERIFY_MODE_REMOTE; break; case'a': verifyMode = VERIFY_MODE_ALL; break; default: break; } } // 设置优化模式 opc = strstr(dexoptFlagStr, "o="); /* optimization */ if (opc != NULL) { switch (*(opc+2)) { case'n': dexOptMode = OPTIMIZE_MODE_NONE; break; case'v': dexOptMode = OPTIMIZE_MODE_VERIFIED; break; case'a': dexOptMode = OPTIMIZE_MODE_ALL; break; case'f': dexOptMode = OPTIMIZE_MODE_FULL; break; default: break; } }
void* threadFunc(void* arg){ printf("Before malloc in thread 1\n"); getchar(); char* addr = (char*) malloc(1000); printf("After malloc and before free in thread 1\n"); getchar(); free(addr); printf("After free in thread 1\n"); getchar(); }
intmain(){ pthread_t t1; void* s; int ret; char* addr;
printf("Welcome to per thread arena example::%d\n",getpid()); printf("Before malloc in main thread\n"); getchar(); addr = (char*) malloc(1000); printf("After malloc and before free in main thread\n"); getchar(); free(addr); printf("After free in main thread\n"); getchar(); ret = pthread_create(&t1, NULL, threadFunc, NULL); if (ret) { printf("Thread creation error\n"); return-1; } ret = pthread_join(t1, &s); if (ret) { printf("Thread join error\n"); return-1; } return0; }
输出分析 主线程的malloc之前,没有堆段,也没有每个线程的栈,因为thread1还没有创建。
1 2 3 4 5 6 7 8 9 10 11 12
$ ./per_thread_arena Welcome to per thread arena example::3437 Before malloc in main thread
/* * native public static int nativeForkSystemServer(int uid, int gid, * int[] gids, int debugFlags, int[][] rlimits, * long permittedCapabilities, long effectiveCapabilities); */ staticvoidDalvik_dalvik_system_Zygote_forkSystemServer( const u4* args, JValue* pResult) { pid_t pid; // 根据参数,fork一个子进程 pid = forkAndSpecializeCommon(args, true); /* The zygote process checks whether the child process has died or not. */ if (pid > 0) { int status; ALOGI("System server process %d has been created", pid); // 保存system_server的进程id gDvm.systemServerPid = pid; /* There is a slight window that the system server process has crashed * but it went unnoticed because we haven't published its pid yet. So * we recheck here just to make sure that all is well. */ if (waitpid(pid, &status, WNOHANG) == pid) { // 如果system_server退出了,Zygote直接kill自己 ALOGE("System server process %d has died. Restarting Zygote!", pid); kill(getpid(), SIGKILL); } } RETURN_INT(pid); }
for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) { err = prctl(PR_CAPBSET_DROP, i, 0, 0, 0); if (err < 0) { if (errno == EINVAL) { ALOGW("PR_CAPBSET_DROP %d failed: %s. " "Please make sure your kernel is compiled with " "file capabilities support enabled.", i, strerror(errno)); } else { ALOGE("PR_CAPBSET_DROP %d failed: %s.", i, strerror(errno)); dvmAbort(); } } }
#endif/* HAVE_ANDROID_OS */
if (mountMode != MOUNT_EXTERNAL_NONE) { err = mountEmulatedStorage(uid, mountMode); if (err < 0) { ALOGE("cannot mountExternalStorage(): %s", strerror(errno));
if (errno == ENOTCONN || errno == EROFS) { // When device is actively encrypting, we get ENOTCONN here // since FUSE was mounted before the framework restarted. // When encrypted device is booting, we get EROFS since // FUSE hasn't been created yet by init. // In either case, continue without external storage. } else { dvmAbort(); } } } // 将list数组中所标明的组加入到目前进程的组设置中 err = setgroupsIntarray(gids); if (err < 0) { ALOGE("cannot setgroups(): %s", strerror(errno)); dvmAbort(); } // 设置资源限制 err = setrlimitsFromArray(rlimits); if (err < 0) { ALOGE("cannot setrlimit(): %s", strerror(errno)); dvmAbort(); } // 设置指定进程组id err = setresgid(gid, gid, gid); if (err < 0) { ALOGE("cannot setresgid(%d): %s", gid, strerror(errno)); dvmAbort(); } // 设置用户id err = setresuid(uid, uid, uid); if (err < 0) { ALOGE("cannot setresuid(%d): %s", uid, strerror(errno)); dvmAbort(); }
if (needsNoRandomizeWorkaround()) { int current = personality(0xffffFFFF); int success = personality((ADDR_NO_RANDOMIZE | current)); if (success == -1) { ALOGW("Personality switch failed. current=%d error=%d\n", current, errno); } } // 设置Linux功能标识 err = setCapabilities(permittedCapabilities, effectiveCapabilities); if (err != 0) { ALOGE("cannot set capabilities (%llx,%llx): %s", permittedCapabilities, effectiveCapabilities, strerror(err)); dvmAbort(); }
err = setSELinuxContext(uid, isSystemServer, seInfo, niceName); if (err < 0) { ALOGE("cannot set SELinux context: %s\n", strerror(errno)); dvmAbort(); } // These free(3) calls are safe because we know we're only ever forking // a single-threaded process, so we know no other thread held the heap // lock when we forked. free(seInfo); free(niceName);
/* * Our system thread ID has changed. Get the new one. */ Thread* thread = dvmThreadSelf(); thread->systemTid = dvmGetSysThreadId();
/* configure additional debug options */ enableDebugFeatures(debugFlags); // 将信号量机制设为默认 unsetSignalHandler(); // 子进程不支持Zygote gDvm.zygote = false; // 检查虚拟机初始化是否成功 if (!dvmInitAfterZygote()) { ALOGE("error in post-zygote initialization"); dvmAbort(); } } elseif (pid > 0) { /* the parent process */ free(seInfo); free(niceName); }
$ ROPgadget --binary vuln2 --only "pop|ret" Gadgets information ============================================================ 0x00000000004006d2 : pop rbp ; ret 0x00000000004006d1 : pop rbx ; pop rbp ; ret 0x0000000000400585 : ret 0x0000000000400735 : ret 0xbdb8
$ ROPgadget --binary libc.so.6 --only "pop|ret" | grep rdi 0x000000000001f7a6 : pop rdi ; pop rbp ; ret 0x0000000000022b1a : pop rdi ; ret 0x00000000001331ad : pop rdi ; ret 0xffee
.bss段的地址: $ readelf -S vuln3 There are 30 section headers, starting at offset 0x1150: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align … [25] .bss NOBITS 0000000000601028 00001028 0000000000000010 0000000000000000 WA 0 0 8 …