利用cache特性检测Android模拟器

原文连接:http://drops.wooyun.org/tips/13245

代码部分:

 
首先是设计一段代码,会向一个特定的地址重新写一个指令。然后由于要重新回到原来的地址再执行一遍,因此可以用一个循环来实现。代码如下:

__asm __volatile (
1 “stmfd sp!,{r4-r8,lr}\n”
2 “mov r6,#0\n” 用来统计循环次数,debug用的
3 “mov r7,#0\n” 为r7赋初值
4 “mov r8,pc\n” 4、7行用来获得覆盖$address“新指令”的地址
5 “mov r4,#0\n” 为r4赋初值
6 “add r7,#1\n” 用来覆盖$address的“新指令”
7 “ldr r5,[r8]\n”
8 “code:\n”
9 “add r4,#1\n” 这就是$address,是对r4加1
10 “mov r8,pc\n” 10,11,12行的作用就是把第6行的指令写到第9行
11 “sub r8,#12\n”
12 “str r5,[r8]\n”
13 “add r6,#1\n” r6用来计数
14 “cmp r4,#10\n” 控制循环次数
15 “bge out\n”
16 “cmp r7,#10\n” 控制循环次数
17 “bge out\n”
18 “b code\n” 10次内的循环调回去
19 “out:\n”
20 “mov r0,r4\n” 把r4的值作为返回值
21 “ldmfd sp!,{r4-r8,pc}\n”
);
注释已经解释得比较清晰了。也就是说,r4如果是10,那么就是执行的是旧指令,是在真机上。如果r4等于1,那就是执行了旧指令,是在模拟器上。

这里会遇到一个问题,就是我们是没有写代码段的权限的,解决方案是mmap一段可写的,把编译好的机器码复制进去,再跳过去执行。

void (*call)(void);
#define PROT PROT_EXEC|PROT_WRITE|PROT_READ
#define FLAGS MAP_ANONYMOUS| MAP_FIXED |MAP_SHARED
char code[]=
“\xF0\x41\x2D\xE9\x00\x60\xA0\xE3\x00\x70\xA0\xE3\x0F\x80\xA0\xE1”
“\x00\x40\xA0\xE3\x01\x70\x87\xE2\x00\x50\x98\xE5\x01\x40\x84\xE2”
“\x0F\x80\xA0\xE1\x0C\x80\x48\xE2\x00\x50\x88\xE5\x01\x60\x86\xE2”
“\x0A\x00\x54\xE3\x02\x00\x00\xAA\x0A\x00\x57\xE3\x00\x00\x00\xAA”
“\xF5\xFF\xFF\xEA\x04\x00\xA0\xE1\xF0\x81\xBD\xE8”;
void *exec = mmap((void*)0x10000000,(size_t)4096 ,PROT ,FLAGS,-1,(off_t)0);
memcpy(exec ,code,sizeof(code)+1);
call=(void*)0x10000000;
call();
申请了一段内存,然后把汇编代码的机器码复制过去,接着跳到这块内存执行。然后我们在后面取r4的值即可。

__asm __volatile (
“mov %0,r0\n”
:”=r”(a)
:
:
);
把r0,也就是r4的值放到a变量中。然后根据a的值返回不同的值就可以了。方便在应用里判断结果。