iOS开发之runtime(16):设置/获取section数据详解

logo

本系列博客是本人的源码阅读笔记,如果有 iOS 开发者在看 runtime 的,欢迎大家多多交流。为了方便讨论,本人新建了一个微信群(iOS技术讨论群),想要加入的,请添加本人微信:zhujinhui207407,【加我前请备注:ios 】,本人博客http://www.kyson.cn 也在不停的更新中,欢迎一起讨论

本文完整版详见笔者小专栏:https://xiaozhuanlan.com/runtime

背景

在前面的文章中,笔者有讲解如何设置以及获取一个section的数据,demo如下:

#import <Foundation/Foundation.h>
#import <dlfcn.h>
#include <mach-o/loader.h>
#include <mach-o/getsect.h>

#ifndef __LP64__
#define mach_header mach_header
#else
#define mach_header mach_header_64
#endif

const struct mach_header *machHeader = NULL;
static NSString *configuration = @"";
//设置"__DATA,__customSection"的数据为kyson
char *kString __attribute__((section("__DATA,__customSection"))) = (char *)"kyson";

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //设置machheader信息
        if (machHeader == NULL)
        {
            Dl_info info;
            dladdr((__bridge const void *)(configuration), &info);
            machHeader = (struct mach_header_64*)info.dli_fbase;
        }

        unsigned long byteCount = 0;
        uintptr_t* data = (uintptr_t *) getsectiondata(machHeader, "__DATA", "__customSection", &byteCount);
        NSUInteger counter = byteCount/sizeof(void*);
        for(NSUInteger idx = 0; idx < counter; ++idx)
        {
            char *string = (char*)data[idx];
            NSString *str = [NSString stringWithUTF8String:string];
            NSLog(@"%@",str);
        }

    }
    return 0;
}

本文就带大家详细分析一下,这段代码的含义。本文您将了解到

  • __attribute__
  • dladdr
  • uintptr_t
    等含义

__attribute__

关于__attribute__,其实前面的文章有说过其中的一种用法:

__attribute__((constructor)) void myentry(){
    NSLog(@"constructor");
}

这段代码会优先于main方法执行。
很显然,其于一般方法不一样的地方在于有修饰符:

__attribute__((constructor))

那么__attribute__修饰符有什么作用呢,这里引用一段:

This section describes the syntax with which __attribute__ may be used, and the constructs to which attribute specifiers bind, for the C language. Some details may vary for C++ and Objective-C. Because of infelicities in the grammar for attributes, some forms described here may not be successfully parsed in all cases.
There are some problems with the semantics of attributes in C++. For example, there are no manglings for attributes, although they may affect code generation, so problems may arise when attributed types are used in conjunction with templates or overloading. Similarly, typeid does not distinguish between types with different attributes. Support for attributes in C++ may be restricted in future to attributes on declarations only, but not on nested declarators.

以上摘自gcc:https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/Attribute-Syntax.html#Attribute-Syntax
这里笔者也摘抄了其他博客的一些论述:

__attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。它的书写特征是:__attribute__前后都有两个下划线,并切后面会紧跟一对原括弧,括弧里面是相应的__attribute__参数,语法格式如下:
__attribute__((attribute-list))
另外,它必须放于声明的尾部“;”之前。

这里再介绍一篇文章,用于讲解__attribute__的作用:
不使用 NSOBJECT 的 OBJECTIVE-C CLASS
这篇文章讲解了

__attribute__((objc_root_class))

的用法,完整的代码如下:

#import <stdio.h>
#import <stdlib.h>
#import <objc/runtime.h>

__attribute__((objc_root_class))
@interface Answer
{
    Class isa;
}

+ (id)instantiate;
- (void)die;

@property(assign, nonatomic) int value;

@end

@implementation Answer

+ (id)instantiate
{
    Answer *result = malloc(class_getInstanceSize(self));
    result->isa = self;
    return result;
}

- (void)die
{
    free(self);
}

@end

int main(int argc, char const *argv[])
{
    Answer *answer = [Answer instantiate];
    answer.value = 42;
    printf("The answer is: %d\n", answer.value);
    [answer die];
    return 0;
}

回到本文开头的Demo,使用的是另外一个属性:

__attribute__((section("__DATA,__customSection"))) 

明显看出,是声明的一个变量属性。
这个变量要“被放到” section为“__DATA,__customSection”里面。这么一来就不难理解了。

dladdr

使用dladdr方法可以获得一个函数所在模块,名称以及地址。

下面我们通过一个实例来说明:

#include <dlfcn.h>
#include <objc/objc.h>
#include <objc/runtime.h>
#include <stdio.h>
#include <string.h>

int main()
{
    Dl_info info;
    IMP imp = class_getMethodImplementation(objc_getClass("NSArray"),sel_registerName("description"));
    printf("pointer %p\n", imp);
    if (dladdr(imp,&info))
    {
        printf("dli_fname: %s\n", info.dli_fname);
        printf("dli_sname: %s\n", info.dli_sname);
        printf("dli_fbase: %p\n", info.dli_fbase);
        printf("dli_saddr: %p\n", info.dli_saddr);
    } else
    {
        printf("error: can't find that symbol.\n");
    }
}

运行结果如下:


result

所以我们可以通过这种方式来判断一个函数是不是被非法修改了。

为了更好说明函数dladdr作用,这里笔者再举个Demo:

static inline BOOL validate_methods(const char *cls,const char *fname) __attribute__ ((always_inline));

BOOL validate_methods(const char *cls,const char *fname){
    Class aClass = objc_getClass(cls);
    Method *methods;
    unsigned int nMethods;
    Dl_info info;
    IMP imp;
    char buf[128];
    Method m;
    
    if(!aClass)
        return NO;
    methods = class_copyMethodList(aClass, &nMethods);
    while (nMethods--) {
        m = methods[nMethods];
        printf("validating [%s %s]\n",(const char *)class_getName(aClass),(const char *)method_getName(m));
        
        imp = method_getImplementation(m);
        //imp = class_getMethodImplementation(aClass, sel_registerName("allObjects"));
        if(!imp){
            printf("error:method_getImplementation(%s) failed\n",(const char *)method_getName(m));
            free(methods);
            return NO;
        }
        
        if(!dladdr(imp, &info)){
            printf("error:dladdr() failed for %s\n",(const char *)method_getName(m));
            free(methods);
            return NO;
        }
        
        /*Validate image path*/
        if(strcmp(info.dli_fname, fname))
            goto FAIL;
        
        if (info.dli_sname != NULL && strcmp(info.dli_sname, "<redacted>") != 0) {
            /*Validate class name in symbol*/
            snprintf(buf, sizeof(buf), "[%s ",(const char *) class_getName(aClass));
            if(strncmp(info.dli_sname + 1, buf, strlen(buf))){
                snprintf(buf, sizeof(buf),"[%s(",(const char *)class_getName(aClass));
                if(strncmp(info.dli_sname + 1, buf, strlen(buf)))
                    goto FAIL;
            }
            
            /*Validate selector in symbol*/
            snprintf(buf, sizeof(buf), " %s]",(const char*)method_getName(m));
            if(strncmp(info.dli_sname + (strlen(info.dli_sname) - strlen(buf)), buf, strlen(buf))){
                goto FAIL;
            }
        }else{
            printf("<redacted>  \n");
        }
        
    }
    
    return YES;
    
FAIL:
    printf("method %s failed integrity test:\n",
           (const char *)method_getName(m));
    printf("    dli_fname:%s\n",info.dli_fname);
    printf("    dli_sname:%s\n",info.dli_sname);
    printf("    dli_fbase:%p\n",info.dli_fbase);
    printf("    dli_saddr:%p\n",info.dli_saddr);
    free(methods);
    return NO;
}

回到本文开头的Demo,不难看出,其实

if (machHeader == NULL)
{
    Dl_info info;
    dladdr((__bridge const void *)(configuration), &info);
    machHeader = (struct mach_header_64*)info.dli_fbase;
}

这段代码的用途仅仅是为了获取header。至于header前面的 文章也提到过了,这里不多做讲解了,拿到的header作为函数getsectiondata的参数:

uintptr_t* data = (uintptr_t *) getsectiondata(machHeader, "__DATA", "__customSection", &byteCount);

这里需要注意的是getsectiondata的定义如下:

extern uint8_t *getsectiondata(
    const struct mach_header_64 *mhp,
    const char *segname,
    const char *sectname,
    unsigned long *size);

所以一开始笔者在返回类型的时候,使用了uint8_t结果发现不管怎么操作都不能打印出想要的数据。改成uintptr_t才能打印成功。原因大家可以猜想一下。
最后我们查看一下打印的结果:

打印结果

本文参考

iOS安全–验证函数地址,检测是否被替换,反注入

广告

我的首款个人开发的APP壁纸宝贝上线了,欢迎大家下载。

壁纸宝贝

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容