当前位置: 首页 > article >正文

【iOS安全】Block开发与逆向

1. OC中的Block

1.1 Block的基本概念

  • 在iOS开发中,Block是一种特殊的数据类型,类似于其他编程语言中的匿名函数。它可以封装一段代码,并且能够像普通变量一样传递、存储和执行。Block可以捕获并访问定义它时所在作用域的变量,这种变量捕获机制使得Block在异步编程、回调处理和事件响应等场景中非常有用。

1.2. Block的语法结构

  • Block的语法结构如下:
    ^ 返回值类型 (参数列表) {
        // 代码块内容
    };
    
  • 例如,一个简单的Block,它接收两个整数参数并返回它们的和:
    int (^sumBlock)(int, int) = ^int(int a, int b) {
        return a + b;
    };
    
  • 在这个例子中,sumBlock是一个Block变量,^int(int a, int b)定义了这个Block的返回值类型为int,参数为两个int类型的变量ab{ return a + b; }是Block内部的代码,用于计算并返回ab的和。

1.3. Block在异步编程中的应用(以网络请求为例)

  • 在iOS开发中,进行网络请求时,通常会使用异步操作,因为网络请求可能需要花费一些时间才能完成。Block可以作为回调函数来处理网络请求的结果。
  • 例如,使用NSURLSession进行网络请求:
    NSURLSession *session = [NSURLSession sharedSession];
    NSURL *url = [NSURL URLWithString:@"https://example.com/api/data"];
    NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (!error) {
            // 解析数据并更新UI等操作
            NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            NSLog(@"请求成功,结果: %@", result);
        } else {
            NSLog(@"请求失败,错误: %@", error);
        }
    }];
    [task resume];
    
  • 在这个例子中,completionHandler是一个Block,它作为参数传递给dataTaskWithURL:completionHandler:方法。当网络请求完成时,这个Block会被调用。Block内部可以访问data(请求返回的数据)、response(响应信息)和error(错误信息),并根据这些信息进行相应的处理,如解析数据、更新用户界面(UI)等操作。

1.4. Block在事件处理中的应用(以按钮点击为例)

  • 当处理用户界面(UI)中的按钮点击事件时,Block也非常有用。例如,使用UIButtonaddTarget:action:forControlEvents:方法来添加按钮点击事件的处理。
  • 假设在一个视图控制器中有一个按钮,当点击按钮时,要执行一段特定的代码,可以使用Block来实现简洁的事件处理:
    UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
    [button setTitle:@"点击我" forState:UIControlStateNormal];
    [button addTarget:self action:^(UIButton *sender) {
        NSLog(@"按钮被点击了");
    } forControlEvents:UIControlEventTouchUpInside];
    
  • 在这个例子中,addTarget:action:forControlEvents:方法的action参数是一个Block。当按钮被点击(UIControlEventTouchUpInside事件发生)时,这个Block会被执行,在Block内部可以编写需要执行的代码,如在这里只是简单地打印一条消息表示按钮被点击。这种方式相比于传统的@selector(选择器)方法更加直观和灵活,特别是在处理简单的事件响应时,可以减少代码的复杂性。

1.5. Block在排序和遍历中的应用(以数组排序为例)

  • 在对数组进行排序时,NSArrayNSMutableArray提供了可以接受Block的排序方法。
  • 例如,有一个包含整数的数组,要按照数字大小进行排序:
    NSArray *numbers = @[@3, @1, @4, @1, @5, @9, @2, @6, @5, @3, @5];
    NSArray *sortedNumbers = [numbers sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
        NSInteger num1 = [obj1 integerValue];
        NSInteger num2 = [obj2 integerValue];
        if (num1 < num2) {
            return NSComparisonResultLessThan;
        } else if (num1 > num2) {
            return NSComparisonResultGreaterThan;
        } else {
            return NSComparisonResultSame;
        }
    }];
    NSLog(@"排序后的数组: %@", sortedNumbers);
    
  • 在这个例子中,sortedArrayUsingComparator:方法接受一个Block作为参数。这个Block用于定义两个元素(obj1obj2)的比较规则。通过比较两个元素对应的整数值,返回NSComparisonResultLessThanNSComparisonResultGreaterThanNSComparisonResultSame来确定元素的顺序,从而实现数组的排序。这种方式允许开发者根据自己的需求灵活地定义排序规则,而不是局限于固定的排序方式。

2. Block逆向分析

转载自:https://blog.imjun.net/posts/restore-symbol-of-iOS-app/
因为这篇博客介绍的十分清楚、简洁,所以直接摘录其内容

2.1 Block在内存中的结构

block在内存中是以一个结构体的形式存在的,大致的结构如下:

struct __block_impl {
  /**
  block在内存中也是类NSObject的结构体,
  结构体开始位置是一个isa指针
  */
  Class isa;
  
  /** 这两个变量暂时不关心 */
  int flags;
  int reserved;
  
  /**
  真正的函数指针!!
  */
  void (*invoke)(...);
  ...
}

block中的isa指针,根据实际情况会有三种不同的取值,来表示不同类型的block:

  1. _NSConcreteStackBlock
    栈上的block,一般block创建时是在栈上分配了一个block结构体的空间,然后对其中的isa等变量赋值。

  2. _NSConcreteMallocBlock
    堆上的block,当block被加入到GCD或者被对象持有时,将栈上的block复制到堆上,此时复制得到的block类型变为了_NSConcreteMallocBlock。

  3. _NSConcreteGlobalBlock
    全局静态的block,当block不依赖于上下文环境,比如不持有block外的变量、只使用block内部的变量的时候,block的内存分配可以在编译期就完成,分配在全局的静态常量区。

第2种block在运行时才会出现,我们只关注1、3两种,下面就分析这两种isa指针和block符号地址之间的关联。

2.2 Block的isa指针

分析这部分需要用到IDA

1._NSConcreteStackBlock
假设我们的源代码是这样很简单的一个block:
在这里插入图片描述
编译完后,实际的汇编长这个样子:
在这里插入图片描述
实际运行时,block的构造过程是这样:

  1. 为block开辟栈空间
  2. 为block的isa指针赋值(一定会引用全局变量:_NSConcreteStackBlock)
  3. 获取函数地址,赋值给函数指针

所以我们可以整理出这样一个特征:

重点来了!!!
凡是代码里用到了栈上的block,一定会获取__NSConcreteStackBlock作为isa指针,同时会紧接着获取一个函数地址,那个函数地址就是block的函数地址。

结合下面这个图,仔细理解上面这句话
(这张图和上面那张图是同一个文件,不过裁掉了符号表)
在这里插入图片描述

利用这个特征,逆向分析时我们可以做如下推断:

在一个OC方法里发现引用了__NSConcreteStackBlock这个变量,那么在这附近,一定会出现一个函数地址,这个函数地址就是这个OC方法里的一个block。

比如上面图中,我们发现 viewDidLoad 里,引用了__NSConcreteStackBlock,同时紧接着加载了 sub_100049D4 的函数地址,那我们就可以认定sub_100049D4是viewDidLoad里的一个block, sub_100049D4函数的符号名应该是 viewDidLoad_block.

2. _NSConcreteGlobalBlock

全局的静态block,是那种不引用block外变量的block,他因为不引用外部变量,所以他可以在编译期就进行内存分配操作,也不用担心block的复制等等操作,他存在于可执行文件的常量区里。

比如,我们把源代码改成这样:

@implementation ViewController
 1. (void)viewDidLoad {
   
    void (^ foo)() = ^(){
        //block 不引用外部的变量
        NSLog(@"%d", 123);
    };
    foo();
}
@end

那么在编译后会变成这样:
在这里插入图片描述
那么借鉴上面的思路,在逆向分析的时候,我们可以这么推断

  1. 在静态常量区发现一个_NSConcreteGlobalBlock的引用
  2. 这个地方必然存在一个block的结构体数据
  3. 在这个结构体第16个字节的地方会出现一个值,这个值是一个block的函数地址

http://www.kler.cn/a/452350.html

相关文章:

  • 基于谱聚类的多模态多目标浣熊优化算法(MMOCOA-SC)求解ZDT1-ZDT4,ZDT6和工程应用--盘式制动器优化,MATLAB代码
  • Java:188 基于springboot妇幼健康管理系统
  • 2024最新鸿蒙开发面试题合集(一)-HarmonyOS NEXT Release(API 12 Release)
  • 【蓝桥杯——物联网设计与开发】拓展模块3 - 温度传感器模块
  • H3C MPLS跨域optionB
  • 宠物行业的出路:在爱与陪伴中寻找增长新机遇
  • jupyter-lab与实验室服务器远程链接
  • 设计模式——工厂方法模式
  • sqli-labs关卡记录13
  • 代码随想录算法训练营第51期第28天 | 122. 买卖股票的最佳时机 II、55. 跳跃游戏、45. 跳跃游戏 II、1005.K次取反后最大化的数组和
  • 实现用户登录系统的前后端开发
  • 第4章 函数
  • mysql主从同步延迟原因分析
  • 当代体育科技杂志当代体育科技杂志社当代体育科技编辑部2024年第33期目录
  • 【Java-tesseract】OCR图片文本识别
  • 微调 BERT:实现抽取式问答
  • 4G云网络广播系统
  • 替换 Docker.io 的 Harbor 安全部署指南:域名与 IP 双支持的镜像管理解决方案
  • 湖南引力:低代码助力实现智慧养老管理系统
  • Ubuntu重命名默认账户
  • YoloV9改策略:卷积改进|SAC,提升模型对小目标和遮挡目标的检测性能|即插即用
  • 操作系统课程设计
  • 通过opencv加载、保存视频
  • React 脚手架使用指南
  • mac系统升级后Homebrew:Mac os 使用brew工具时报错No remote ‘origin‘
  • linux-21 目录管理(一)mkdir命令,创建空目录