Jerry's Blog

Less is more, Live and learning

Hello,I'm an iOS developer in China.


iOS代码混淆之方法名替换(一)

这是iOS代码混淆系列文章的第一篇,后两篇可以点击下方链接查看

iOS代码混淆之资源替换(二)

iOS代码混淆之编译优化(三)

前言

在iOS开发中,某些项目为了保护代码安全,不被黑客攻破,通常需要在上架之前对源代码进行加密混淆,下面就介绍一种简单可行的Objective-C代码混淆方案。

混淆思路

由于现在的逆向技术越来越高超,各种破解方案层出不穷,其中就可以使用class-dump命令行工具轻易的dump出demo.app文件中的所有头文件,代码立马回暴露在黑客面前,对项目产生了很多不利因素。因此将代码关键方法进行混淆保护迫在眉睫。

现有的一种混淆方案就是通过对指定class name 方法名进行MD5加密(显示乱码),然后通过脚本代码将加密的方法名生成到指定的头文件中,然后再使用宏替换的方式将混淆过的方法名在代码预编译阶段进行替换,使方法名即便在class-dump后也只会显示加密后的乱码显示,造成代码不易读,从而实现一定程度上的混淆保护。

混淆方式

通过宏定义(#define)的方法名映射,进行字符串替换。这样做的好处在于可以将映射类别规整到一个头文件中,并#import 进项目的prefixHeader.h 预编译头文件中,方便在项目编译时进行全局替换。当不需要混淆的时候,则不导入该头文件即可。

混淆脚本

下面的shell脚本引自的是“念茜-安全攻防”系列文章,实现思路是将敏感的方法名集中写入func.plist文件中,并逐一#define成随机字符串,最后写入一个.h头文件中。脚本代码如下:

#!/usr/bin/env bash

TABLENAME=symbols
SYMBOL_DB_FILE="symbols"
STRING_SYMBOL_FILE="func.list"
HEAD_FILE="$PROJECT_DIR/$PROJECT_NAME/codeObfuscation.h"
export LC_CTYPE=C

#维护数据库方便日后作排重
createTable()
{
    echo "create table $TABLENAME(src text, des text);" | sqlite3 $SYMBOL_DB_FILE
}

insertValue()
{
    echo "insert into $TABLENAME values('$1' ,'$2');" | sqlite3 $SYMBOL_DB_FILE
}

query()
{
    echo "select * from $TABLENAME where src='$1';" | sqlite3 $SYMBOL_DB_FILE
}

ramdomString()
{
    openssl rand -base64 64 | tr -cd 'a-zA-Z' |head -c 16
}

rm -f $SYMBOL_DB_FILE
rm -f $HEAD_FILE
createTable

touch $HEAD_FILE
echo '#ifndef Demo_codeObfuscation_h
#define Demo_codeObfuscation_h' >> $HEAD_FILE
echo "//confuse string at `date`" >> $HEAD_FILE
cat "$STRING_SYMBOL_FILE" | while read -ra line; do
    if [[ ! -z "$line" ]]; then
        ramdom=`ramdomString`
        echo $line $ramdom
        insertValue $line $ramdom
        echo "#define $line $ramdom" >> $HEAD_FILE
    fi
done
echo "#endif" >> $HEAD_FILE

sqlite3 $SYMBOL_DB_FILE .dump

配置步骤

  1. 创建混淆脚本文件confuse.sh,拷贝上述脚本代码到文件中保存,然后把该脚本文件放到项目根目录。例如:yourProject_path/目录下。

  2. 创建配置方法名称的列表文件,名为:func.list,此文件用于将需要混淆的方法名称写入进去,当项目编译时Build,会执行脚本遍历此文件中的方法名执行混淆操作,填写示例:

    -(void)sample;   // 无参数方法
    -(void)seg1:(NSString *)string seg2:(NSUInteger)num;  // 有参数方法
       
    // func.list 文件中这样写即可
    sample
    seg1
    seg2
    

    然后将创建好的func.list文件同样拷贝到项目根目录,也可以将confuse.shfunc.list文件都添加进项目目录中,方便日后查看与维护,Xcode 左上角点击File --> Add Files to "xxx"添加即可。

  3. 配置Build Phases脚本

    // 脚本代码
    $PROJECT_DIR/confuse.sh
    

    操作步骤如下图:

    img1

  4. PrefixHeader.h头文件中添加如下代码,意思是只有在Release模式下,才导入混淆头文件

    // 混淆定义宏头文件,当在debug模式下,会执行脚本生成替换字符串,在release模式下导入宏文件执行替换
       
    #if (DEBUG != 1)
    #import "codeObfuscation.h"
    #endif
    
  5. 执行编译,可观察编译日志,查看custom shell 是否正确执行,如下图:

    img2

  6. 如果编译成功,会在项目根目录生成一个头文件codeObfuscation.h,此文件中正是包含了宏替换的关键内容,将此文件添加到项目中,会在release模式下进行替换。

可能会遇到的问题

  1. 第一次编译成功之前,即脚本代码执行之前是不会有codeObfuscation.h文件的,所以可能在PrefixHeader.h 文件中报错:Error_找不到该文件。解决方法:暂时注释掉即可

  2. 在执行脚本时,编译日志处出现如下报错:

    img3

    上图表示在执行脚本代码时,未能找到func.list 文件或其他文件,这时需要查看脚本代码中该文件的配置路径是否正确。解决方法:在confuse.sh文件中排查各项配置路径,并改正

  3. 在执行脚本时,如遇到如下报错:

    confuse.sh: Permission denied
    

    说明confuse.sh脚本文件没有可执行权限,需要在终端iTerm2 中给confuse.sh文件添加执行权限,如下操作即可:

    chmod a+x confuse.sh
    

使用class-dump 来测试

class-dump是一个命令行工具,它利用Objective-C语言的runtime的特性,将存储在mach-O文件中的@interface和@protocol信息提取出来,并生成对应的.h文件。 它将xx.app格式的iOS项目文件进行分析,并通过命令生成该app源代码中的所有头文件列表,其中就包含了方法名称,可以清晰的查看dump出来的方法名是否已经被混淆。

安装 class-dump

首先需要去这里:http://stevenygard.com/projects/class-dump/,下载class-dump-3.5.dmg压缩包,解压后将文件夹中的class-dump可执行文件拷贝到/usr/local/bin目录下。

具体操作步骤:

  1. 解压class-dump文件,如下图所示:

    img4

  2. 打开终端,输入如下命令,即可打开/usr/local/bin目录文件夹,然后将class-dump文件拷贝到此目录下。

    open /usr/local/bin
    
  3. class-dump文件更改权限,终端输入如下命令:

    sudo chmod 777 /usr/local/bin/class-dump
    
  4. 在终端输入class-dump 查看是否安装成功,如显示命令的介绍和版本信息,就说明已经安装成功。

    img5

class-dump使用方法

dump命令如下:

class-dump -H /Users/xxx/Desktop/century.app -o /Users/jerry/Desktop/centuryHeaders

其中,-H 是生成头文件的命令,-o 是指定的头文件保存目录,/Users/xxx/Desktop/century.app 则是App文件的路径。

将在centuryHeaders文件夹中看到所有dump出的头文件。如下示例:

img6

Tips:如何从打包出来的xxx.ipa 文件中提取xxx.app 文件

先将xxx.ipa 文件后缀名修改为xxx.zip , 然后解压xxx.zip 文件,会得到一个名为Payload的文件夹,文件夹中就放置的咱们需要的xxx.app文件,导出即可。

注意:

不是所有的xxx.app 文件都可以轻易的dump出头文件列表,只有未加密的,自己开发暂未上架App store 的app可以导出,App store 上架的三方应用由于被苹果加过密(加壳),不能直接dump,需要先进行砸壳(Dumpdecrypted工具)后,才能dump出需要的头文件。

后记

至此,iOS开发中的Objective-C 代码混淆方案和class-dump技术的使用,都已经讲述清楚了,希望读者能体会到其中的安全攻防思路,在之后的开发中能多注意代码的安全防范。

注意:这种替换方法名的混淆方案会让App Store 审核被拒,2.3.1 - Performance We discovered that your app contains obfuscated code…,因此,不建议在App Store 上架的项目中使用。

参考资料:

https://blog.csdn.net/yiyaaixuexi/article/details/29201699

https://www.jianshu.com/p/1e3fe0a8c048

最近的文章

iOS代码混淆之资源替换(二)

这是iOS代码混淆系列文章的第二篇,其他两篇可以点击下方链接查看 iOS代码混淆之方法名替换(一) iOS代码混淆之编译优化(三)为什么要替换项目资源在某些公司项目开发当中 ,有时候需要通过技术手段,多次上架同一款产品。这些功能类似,且界面不同的产品也称作马甲包。为了达到将同一个包,经过较少的改动,快速更改为另一个类似的产品时,在代码混淆的基础之上还需要修改项目名称、静态资源等来达到目的。需替换的资源及注意事项 工程内部:工程名称、类名前缀、BundleId、icon、启动图、静...…

iOS 代码混淆继续阅读
更早的文章

iOS页面卡顿及性能优化

卡顿产生的原因在屏幕成像的过程中,CPU和GPU的职责及CPU:负责对象的创建和销毁、对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码、图像的绘制(Core Graphics)。GPU:负责变换、合成、纹理的渲染。CPU 把计算好的数据给 GPU,GPU 来渲染,渲染后的数据放在帧缓存(缓冲区,有两块缓冲区,前帧缓存和后帧缓存,协调使用,效率高)中。然后,视频控制器从缓冲区获取渲染后的数据显示在屏幕上。图像显示原理 引用YY大神对于图像显示原理的分析一帧(或一页)数据就是...…

iOS继续阅读