0%

Android so 动态修改内存

Android内存修改

这个主要分为java层和so层的修改,java层基于反射,而so层的分类又分为两种,一种是不注入的修改,修改proc目录下的内容,而注入式就是我们的内存修改,也是比较常用。

so层修改

我们如果不修改代码段,仅仅是修改data段之类的是不需要修改属性页权限的。
实例代码:

1
2
3
4
5
6
7
8
static int total = 0;

JNIEXPORT jint JNICALL Java_com_example_testjni_NativeClass_settotal
(JNIEnv *, jobject)
{
total++;
return total;
}

就是一个简单的数据累加,我们的目的就是可以随意修改静态变量。

首先我们需要先找到这个变量的相对偏移,首先我们用IDA搜索这个导出函数:
导出函数
这个带符号的加1其实就是我们的静态变量加一,所以这个静态变量的地址放在了我们的R0寄存器,而R0的值就是来自于R3寄存器指向的地址的值,所以R3是我们需要分析的,R3的地址是之前的R3加上PC,IDA给我们计算出来其实实际上就是加上4004,而这个4004是如何计算出来的呢?
我们动态分析一下,动态调试转到之后看到这个代码:

1
2
3
4
5
6
7
8
9
libTestJNI.so:4E257F88 Java_com_example_testjni_NativeClass_settotal
libTestJNI.so:4E257F88 LDR R3, =(dword_4E25B004 - 0x4E257F8E)
libTestJNI.so:4E257F8A ADD R3, PC ; dword_4E25B004
libTestJNI.so:4E257F8C LDR R0, [R3]
libTestJNI.so:4E257F8E ADDS R0, #1
libTestJNI.so:4E257F90 STR R0, [R3]
libTestJNI.so:4E257F92 BX LR
libTestJNI.so:4E257F92 ; End of function Java_com_example_testjni_NativeClass_settotal
libTestJNI.so:4E257F92

R3的值一开始是4E25B004 - 0x4E257F8E得到的,经计算是:0x3706
R3
确实是0x3706,我们增加的这个PC其实是IDA的PC往下指向的2两条指令,下面两条指令都是两个字节,所以是4个字节(thumb指令),而当前的指令(执行到ADD的时候)PC是我们当前查看的这个模块加上F8A:
PC偏移
所以我们应该使用基地址+0xF8A+0x4+0x3076=0x4004
所以我们静态变量的地址应该是这个so模块加上0x4004得到的地址。
最后我们可以看下R0的值:
R0
确实使我们的变量。
分析到这里就可以了,我们现在需要解决的就是我们如何修改这个内存单元的值,还是用注入实现吧,方便快捷。

首先我们先要有这样一个函数:

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
bool GetModuleBase(long long * ulModBase,pid_t pid,const char * pszModName){
bool bRet = false;
FILE * fp = NULL;
char szMapFilePath[32]={0};
char szMapFileLine[1024]={0};

if(pszModName == NULL)
{
return bRet;
}

if(pid < 0)
{
sprintf(szMapFilePath,"/proc/self/maps");
}else{
sprintf(szMapFilePath,"/proc/%d/maps",pid);
}

fp = fopen(szMapFilePath,"r");

if(fp != NULL)
{
while(fgets(szMapFileLine,sizeof(szMapFileLine),fp)!=NULL)
{

if(strstr(szMapFileLine,pszModName))
{
char * pszModAddrStart = strtok(szMapFileLine,"-");
if(pszModAddrStart)
{
*ulModBase = strtoul(pszModAddrStart,NULL,16);

if(*ulModBase == 0x8000)
*ulModBase =0;
bRet = true;
break;

}

}
}
fclose(fp);
}

return bRet;
}

这个函数是用来获取我们模块基地址的:

  1. 第一个参数由于我们用的是c语言,所以不能用引用,只能指针传入,并且,c语言要用bool这个命名,我们需要引入:#include <stdbool.h>
  2. 第二个参数是我们要查询的进程PID
  3. 第三个参数是我们要查询的模块名称
1
2
long long address;
GetModuleBase(&address,getpid(),"libTestJNI.so");

这个样子address这个变量里面存储的就是我们的模块基地址,既然得到了变量基地址,那么我们就可以用内存拷贝去修改我们的值:

1
2
int tmp=100;
memcpy((void*)(address+0x4004),(void*)&tmp,4);

这个样子就可以了,还是之前我们用到的ADBI框架。
./hijack -d -p 1935 -l /data/local/tmp/libexample.so
1935是我们进程的PID。
修改成功

java层就是将这个变量弹出吐司。