【无效问题】Facebook App 中 FBInjectable section 的数据何时被修改的?

由于处于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 可执行文件在这里 https://pan.baidu.com/s/1dELFd5Z

getsectiondata 的调用地址在 0x0334cc1c


PS:关于Facebook为何创建FBInjectable 这个section,我已经得出结论,近期会整理分享出来。

群内hh说『可能dyld搞的』,我研究研究

找到办法了。__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下会被优化掉。