由于处于DATA段,所以内部的全局变量,不需要手动修改地址。加载时会自动偏移。
所以这个问题,是“白折腾了”。
Facebook 有一些特殊的section,例如下图的:__DATA,FBInjectable。
经过一些分析,得知此section的数据含义如下:(armv7下)
共72 bytes,每4 bytes一组,分18组。每一组的逆序(读入内存导致),例如第一组 B8DB8404 的逆序为 0484DBB8 ,也就是0X0484DBB8的位置是个字符串,如下图:
就是这个字符串, +[FBNewAccountNUXPYMKVCFactory(FBInjectable) fb_injectable]
以上是背景,以下是问题:
app运行时,通过 getsectiondata 获取__DATA segment的 FBInjectable section 的数据。相关代码如下:
通过调试,打印出getsectiondata的返回值如下:
(问题马上出来),读出来的内容与之前在 MachOView 中看的不一样。
显然由于ASLR的存在,FBInjectable内的每组地址也需要加上偏移地址,才能保证通过每组地址可以获取到对应的那个字符串。
(问题来了),我想了各种办法都没有找到getsectiondata的返回数据是在什么地方被修改了。(每4bytes一组分别加上偏移地址)
例如第一组: 0x0493fbb8 - 0x0484dbb8 = 0xf2000
1、我开始以为是hook,但单步进入调试,并没有发现到达自己模块。
2、以为getsectiondata 会自动增加偏移,显然不会这样。
还有什么方法,会让getsectiondata 返回的数据每组自动加上偏移呢?恳求大家支招。 (如果自己写这段代码,肯定就是getsectiondata后处理返回值,但Facebook貌似处理的更加自动化,如何做到的呢)
这里有个简单的getsectiondata的例子 https://github.com/everettjf/FBInjectableTest
Facebook armv7 可执行文件在这里 百度网盘-链接不存在
getsectiondata 的调用地址在 0x0334cc1c
PS:关于Facebook为何创建FBInjectable 这个section,我已经得出结论,近期会整理分享出来。
找到办法了。__DATA segment 既然是可写的,那就提前找到这个地址直接修改内存中的值就行了呀。
Demo在这里,https://github.com/everettjf/FBInjectableTest
参考代码:
{
const struct mach_header *mhp = _dyld_get_image_header(0);
const struct section * sec = getsectbyname("__DATA", "FBInjectable");
const char * memory = (const char *)( sec->offset + (uint32_t)mhp);
NSData *d = [NSData dataWithBytes:(const void *)memory length:72];
NSString *text = [NSString stringWithFormat:@"%@", d];
NSLog(@"memory text = %@", text);
uint32_t *memory32 = (uint32_t*)memory;
for(int idx = 0; idx < 72/4; ++idx){
memory32[idx] += (uint32_t)mhp;
}
d = [NSData dataWithBytes:(const void *)memory length:72];
text = [NSString stringWithFormat:@"%@", d];
NSLog(@"memory text modified = %@", text);
}
{
const struct mach_header *mhp = _dyld_get_image_header(0);
// Dl_info dlinfo;
// dladdr((const void *)main, &dlinfo);
//#ifndef __LP64__
// const struct mach_header *mhp = (const struct mach_header*)dlinfo.dli_fbase;
//#else
// const struct mach_header_64 *mhp = (const struct mach_header_64*)dlinfo.dli_fbase;
//#endif
unsigned long size = 0;
uint8_t *data = getsectiondata(mhp, "__DATA", "FBInjectable", & size);
NSData *d = [NSData dataWithBytes:(const void *)data length:72];
NSString *text = [NSString stringWithFormat:@"%@", d];
NSLog(@"injectable text = %@", text);
// UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"" message:text delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
// [alert show];
}
发现过程中,还写了个tweak,tweak中调用getsectiondata返回值一样是修改后的。于是才恍然大悟,__DATA就是加载到内存中一段数据。
getsectiondata 的实现方式并不是 从文件读取,而是从内存中获取。
下面这段代码就定位到对应的地址,直接内存中修改就可以了。
const char * memory = (const char *)( sec->offset + (uint32_t)mhp);
getsectbyname
这个函数可以省去,函数返回的文件偏移地址可以通过hack方法植入。
最后,Facebook具体在哪修改的,有空再找找了。
多谢群内 bmob 提示。FBInjectable可以很方便的通过 attribute 指令创建。
例如:
// const strings
NSString * kString1 __attribute((unused, section("__DATA,FBInjectable"))) = @"string";
NSString * kString2 __attribute((unused, section("__DATA,FBInjectable"))) = @"string";
NSString * kString3 __attribute((unused, section("__DATA,FBInjectable"))) = @"string";
就可以创建出对应字符串的地址。
参考代码:
https://github.com/everettjf/FBInjectableTest
facebook 这样就可以把这个声明放在各自的配置文件(分散在各个文件)中,然后,运行期统一通过 FBInjectable section获取。
上面答案已经修改。
不能用const。否则release下会被优化掉。