IOS逆向-LLVM代码混淆

Posted GY-93

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IOS逆向-LLVM代码混淆相关的知识,希望对你有一定的参考价值。

1 LLVM

  • 什么是LLVM
    • 官网https://llvm.org
    • LLVM项目是模块化,可重用的编译器以及工具链技术的集合,LLVM不是首字母的缩写,它是项目名称

1.1 传统编译架构

在这里插入图片描述

  • Frontend:词法分析、语法分析、语义分析、生成中间代码
  • Optimizer:优化器(中间代码)
  • Backend:后端,生成机器码

1.2 LLVM的编译架构

  • 不同的前端后端使用统一的中间代码LLVM Intermediate Representation(LLVM IR)
  • 如果需要支持一种新的编程语言,那么只需要实现一个新的前端
  • 如果需要支持一种新的硬件设备,那么只需要实现一个新的 后端
  • 优化阶段是一个通用的阶段,它针对的是统一的LLVM IR,不论是支持新的变成语言,还是支持新的硬件设备,都不需要对优化阶段做修改
  • 相比之下,GCC的全段和后端没分的太开,前端和后端耦合在一起,所以GCC为了支持一门新的语言,或则为了支持一个新的目标平台,就变的特别困难
  • LLVM现在被作为实现各种金泰和运行编译语言的通用基础(GCC家族、Java、.NET、Python、Ruby、Scheme、Haskell、D等)
    在这里插入图片描述

1.3 Clang

1.3.1 什么是Clang

  • 什么是Clang?
    • LLVM项目的一个子项目
    • 基于LLVM架构的C/C++/Objective-C编译器前端
    • 官网:http://clang.llvm.org/
  • 相比于GCC,Clang具有如下优点:
    • 编译速度快:在某些平台上,Clang的编译速度显著的快过GCC(Debug模式下编译OC速度比GGC快3倍)
    • 占用内存小:Clang生成的AST所占用的内存是GCC的五分之一左右
    • 模块化设计:Clang采用基于库的模块化设计,易于 IDE 集成及其他用途的重用
    • 诊断信息可读性强:在编译过程中,Clang 创建并保留了大量详细的元数据 (metadata),有利于调试和错误报告
    • 设计清晰简单,容易理解,易于扩展增强

1.3.2 Clang与LLVM

  • 广义上的LLVM: 整个LLVM架构
  • 狭义的LLVM:LLVM后端(代码优化、目标代码生成等)

在这里插入图片描述
在这里插入图片描述

2. OC源文件的编译过程

2.1 编译过程和预编译

#include <stdio.h>

#define AGE 40
int main(int argc, const char * argv[]) {
   
    int a = 10;
    int b = 20;
    int c = a + b + AGE;
    return 0;
}
  • 命令行查看编译的过程:clang -ccc-print-phases main.m
    在这里插入图片描述

  • 查看preprocessor(预处理)的结果: clang -E main.m
    在这里插入图片描述

我们可以看到预编译做了很多事情,然后宏定义在预编译期就被替换了

2.2 词法分析

  • 词法分析,生成Token: clang -fmodules -E -Xclang -dump-tokens main.m
void test(int a, int b) {
    int c = a + b - 3;
}

在这里插入图片描述

2.3 语法树-AST

  • 语法分析,生成语法树(AST,Abstract Syntax Tree): clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
    在这里插入图片描述
    在这里插入图片描述

2.4中间代码(LLVM IR)

  • LLVM IR有3种表示形式(但本质是等价的,就好比水可以有气体、液体、固体3种形态)
  • text:便于阅读的文本格式,类似于汇编语言,拓展名.ll, clang -S -emit-llvm main.m
  • memory:内存格式
  • bitcode:二进制格式,拓展名.bc, clang -c -emit-llvm main.m

main.ll的代码:
在这里插入图片描述

  • IR基本语法:
    • 注释以分号 ; 开头
    • 全局标识符以@开头,局部标识符以%开头
    • alloca,在当前函数栈帧中分配内存
    • i32,32bit,4个字节的意思
    • align,内存对齐
    • store,写入数据
    • load,读取数据
  • 官方语法参考:https://llvm.org/docs/LangRef.html

3. LLVM源码

3.1 LLVM的源码下载

  • 下载LLVM: git clone https://git.llvm.org/git/llvm.git/
  • 下载clang:cd llvm/tools (需要进入LLVM的源码目录下的Tools文件夹,然后下载clang源码),然后执行:git clone https://git.llvm.org/git/clang.git/
  • clang编译器其实Xcode是带有的,我们可以查看下:
    在这里插入图片描述

3.2 源码编译

3.2.1 ninja编译方式

  • 安装cmake和ninja(先安装brew,https://brew.sh/
    • brew install cmake
    • brew install ninja
  • ninja如果安装失败,可以直接从github获取release版放入【/usr/local/bin】
  • 在LLVM源码同级目录下新建一个【llvm_build】目录(最终会在【llvm_build】目录下生成【build.ninja】,表示成功)
    • cd llvm_build
    • cmake -G Ninja ../llvm -DCMAKE_INSTALL_PREFIX=LLVM的安装路径:编译Ninja模板
    • 更多cmake相关选项,可以参考:https://llvm.org/docs/CMake.html
  • 依次执行编译、安装指令
    • ninja
    • 编译完毕后, 【llvm_build】目录大概 21.05 G(仅供参考)
    • ninja install
    • 安装完毕后,安装目录大概 11.92 G(仅供参考)
      在这里插入图片描述

3.2.2

  • 也可以生成Xcode项目再进行编译,但是速度很慢(可能需要1个多小时)
  • 在llvm同级目录下新建一个【llvm_xcode】目录
  • cd llvm_xcode
  • cmake -G Xcode ../llvm

生成xcode项目在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4 应用和实践

5 clang插件开发

5.1 clang插件开发1 – 插件目录

  • 【clang/tools】源码目录下新建一个插件目录,假设叫做【mj-plugin】

在这里插入图片描述

  • 在【clang/tools/CMakeLists.txt】最后加入内容: add_clang_subdirectory(mj-plugin),小括号里是插件目录名

在这里插入图片描述

5.2 clang插件开发2 – 插件必要文件

  • 在【mj-plugin】目录下新建一个【CMakeLists.txt】,文件内容是:add_llvm_loadable_module(MJPlugin MJPlugin.cpp)
    • MJPlugin是插件名,MJPlugin.cpp是源代码文件

在这里插入图片描述
在这里插入图片描述

5.3 clang插件开发3 – 编写插件源码

  • 【MJPlugin.cpp】参考

在这里插入图片描述

5.4 clang插件开发4 – 编译插件

  • 利用cmake生成的Xcode项目来编译插件(第一次编写完插件,需要利用cmake重新生成一下Xcode项目)
    • 插件源代码在【Sources/Loadable modules】目录下可以找到,这样就可以直接在Xcode里编写插件代码
    • 选择MJPlugin这个target进行编译,编译完会生成一个动态库文件

在这里插入图片描述

5.5 clang插件开发5 – 加载插件

  • 在Xcode项目中指定加载插件动态库:Build Settings > OTHER_CFLAGS
  • -Xclang -load -Xclang 动态库路径 -Xclang -add-plugin -Xclang 插件名称

在这里插入图片描述

5.6 clang插件开发6 – Hack Xcode

  • 首先要对Xcode进行Hack,才能修改默认的编译器
    • 下载【XcodeHacking.zip】,解压,修改【HackedClang.xcplugin/Contents/Resources/HackedClang.xcspec】的内容,设
      置一下自己编译好的clang的路径

在这里插入图片描述

  • 然后在XcodeHacking目录下进行命令行,将XcodeHacking的内容剪切到Xcode内部
    • sudo mv HackedClang.xcpluginxcode-select-printpath/../PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins
    • sudo mv HackedBuildSystem.xcspecxcode-select-printpath/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Specifications

5.7 clang插件开发7 – 修改Xcode的编译器

在这里插入图片描述

5.8 clang插件开发8 – 编译项目

  • 编译项目后,会在编译日志看到MJPlugin插件的打印信息(如果插件更新了,最好先Clean一下项目)

在这里插入图片描述

5.9 clang插件开发9 – 更多

5.10 提示警告、错误信息

在这里插入图片描述

5.11 推荐书籍

在这里插入图片描述

6 代码混淆

6.1 基本概念

  • 什么是加固: 加固是为了增加应用的安全性,防止应用被破解、盗版、二次打包、注入、反编译等
  • 常见的加固方式有
    • 数据加密(字符串、网络数据、敏感数据等)
    • 应用加壳(二进制加密)
    • 代码混淆(类名、方法名、代码逻辑等)
    • …(不同平台还有不同的做法)

6.2 ios中代码混淆

6.2.1 基本介绍

  • iOS程序可以通过class-dump、Hopper、IDA等获取类名、方法名、以及分析程序的执行逻辑
    • 如果进行代码混淆,可以加大别人的分析难度
  • iOS的代码混淆方案

6.2.2 源码的混淆 - 通过宏定义混淆方法名、类名

  • 使用pch文件中定义的宏定义来混淆代码,但是pch文件中是不会替换xib、storyboard中的 名称的,需要手动修改
    在这里插入图片描述

  • 注意点

    • 不能混淆系统方法
    • 不能混淆init开头的等初始化方法
    • 混淆属性时需要额外注意set方法
    • 如果xib、storyboard中用到了混淆的内容,需要手动修正
    • 可以考虑把需要混淆的符号都加上前缀,跟系统自带的符号进行区分
    • 混淆过多可能会被AppStore拒绝上架,需要说明用途
  • 建议

    • 给需要混淆的符号加上了一个特定的前缀
  • 小工具参考:https://github.com/CoderMJLee/MJCodeObfuscation

6.2.3 iOS-class-guard

  • 第三方工具
    • 下载链接: https://github.com/Polidea/ios-class-guard
    • 它是基于class-dump的扩展
    • 用class-dump扫描出可执行文件中的类名、方法名、属性名等并做替换,会更新xib和storyboard的名字等等
  • 用法
    • brew install ios-class-guard
    • ios-class-guard [options]
  • 常用参数
    • --sdk-root <path>:用于指定SDK路径,如果是模拟器SDK,一般路径就是/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk
    • --sdk-ios:相当于指定SDK路径为真机设备SDK
    • -X <path>:用于指定xib、storyboard所在目录,它会递归搜索
    • -O <path>:生成的混淆头文件路径
    • -m <path>: 符号映射表(默认是symbols.json)

6.2.4

  • 很多时候,可执行文件中的字符串信息,对破解者来说,非常关键,是破解的捷径之一
  • 为了加大破解、逆向难度,可以考虑对字符串进行加密
  • 字符串的加密技术有很多种,可以根据自己的需要自行制定算法
  • 这里举一个简单的例子
    • 对每个字符进行异或(^)处理
    • 需要使用字符串时,对异或()过的字符再进行一次异或(),就可以获得原字符

以上是关于IOS逆向-LLVM代码混淆的主要内容,如果未能解决你的问题,请参考以下文章

ios -逆向-代码混淆

iOS - 逆向 - Objective-C代码混淆 -confuse.sh文件写法

obfuscator-llvm Xcode集成配置

iOS代码加密的几种方式

如何用llvm-obfuscator混淆代码

Android逆向笔记-Proguard混淆Android代码以及去打印日志信息