IosBX's Blog

IosbX's Blog
记录自己的每一次进步
  1. 首页
  2. iOS
  3. 正文

iOS逆向- Hikari混淆及还原

2025年 11月 29日 772点热度 1人点赞 0条评论

一.介绍

Hikari 是一个基于 LLVM 的代码混淆器(Obfuscator),主要用于保护 C/C++/Objective-C 等语言的二进制代码,防止逆向工程和破解。它通过在 LLVM 编译器框架中注入自定义 Pass(优化通道),对中间表示(IR)进行变换,从而增加代码的复杂度和分析难度。Hikari 的设计灵感来源于 Obfuscator-LLVM,但添加了更多自定义 Pass,并优化了稳定性。

历史与背景

  • 起源:Hikari 是由开发者 Naville 在 2017 年圣诞节期间的“黑客马拉松式玩具项目”开发的,名字来源于日语中的“光”(Light),灵感来自任天堂 Switch 游戏《异度神剑 2》(Xenoblade Chronicles 2)。 最初基于 LLVM 6.0 版本,并已足够稳定,可用于生产环境。

  • 发展:原项目已于 LLVM 8 版本后停止积极维护(最后更新于 2023 年 1 月),开发者建议用户转向商业支持的实现或活跃维护的分支,如 NeHyci/Hikari-LLVM15(支持 LLVM 15)或 PPKunOfficial/Hikari-LLVM19(支持 LLVM 19)。 这些 fork 修复了兼容性问题,并扩展了对新 LLVM 版本的支持。

  • 开源性质:项目托管在 GitHub 上(原仓库:HikariObfuscator/Hikari),采用开源许可,但开发者警告开源混淆器可能泄露模式给破解者,尽管 Hikari 已尽量减少此类问题。

Hikari 支持的混淆标记

  • -mllvm -enable-bcfobf

    Bogus Control Flow(伪控制流)

    在每个基本块中插入大量虚假条件分支和 junk code(运行时永远不会走到)。

    支持概率控制:-mllvm -bcf_prob=80(0~100,建议 70~100)

  • -mllvm -enable-fco 或 -mllvm -enable-cffobf

    Control Flow Flattening(控制流平坦化)

    将函数内所有基本块打散,改写成一个无限循环 + 大 switch 的调度器结构。

  • -mllvm -enable-indibran

    Indirect Branching(间接分支/寄存器相对跳转)

    把所有直接跳转(br label %xx)替换成 indirectbr 指令,跳转目标通过寄存器在运行时动态计算。

    与控制流平坦化叠加后,IDA 的 F5 基本完全失效,显示为一大片无法解析的代码。

  • -mllvm -enable-strcry

    String Encryption(字符串常量加密)

    自动加密所有全局/局部常量字符串,二进制中看到的都是密文,运行时由插入的解密 stub 实时解密。

    有效防止 strings、grep、FLAIR 签名匹配等静态提取。

  • -mllvm -enable-sub 或 -mllvm -enable-subobf

    Instruction Substitution(指令替换)

    把简单的算术/逻辑指令替换成功能等价但更复杂的表达式序列,例如:

    a + b → (a ^ b) + 2*(a & b)

    支持概率控制:-mllvm -sub_prob=80

  • -mllvm -enable-split 或 -mllvm -enable-splitobf

    Basic Block Splitting(基本块拆分)

    把原本连续的大基本块强行切成多个小块,并插入无意义跳转或 opaque predicate,进一步膨胀控制流图。

    支持概率控制:-mllvm -split_prob=70(建议不要 100%,否则体积膨胀严重)

二.使用

以Aethereux/Hikari-LLVM19为例,仓库的 releases 已提供详细的编译命令

macOS(Apple Silicon / M 系列)

git clone https://github.com/Aethereux/Hikari-LLVM19.git
cd Hikari-LLVM19
mkdir build && cd build

cmake -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_ARCHITECTURES=arm64 \
-DLLVM_ENABLE_PROJECTS="clang;lld" \
-DLLVM_TARGETS_TO_BUILD="X86;ARM;AArch64" \
-DLLVM_ENABLE_ZSTD=OFF \
-DCMAKE_OSX_DEPLOYMENT_TARGET=12.0 \
-DLLVM_CREATE_XCODE_TOOLCHAIN=ON \
-DLLVM_INCLUDE_TESTS=OFF \
-DLLVM_INCLUDE_EXAMPLES=OFF \
-DLLVM_INCLUDE_UTILS=OFF \
-DLLVM_INCLUDE_BENCHMARKS=OFF \
-DLLVM_BUILD_TOOLS=OFF \
-DLLVM_INSTALL_TOOLCHAIN_ONLY=ON \
-DCMAKE_INSTALL_PREFIX="./install" \
../llvm
ninja
ninja install-xcode-toolchain

macOS (Intel / x86_64) 

git clone https://github.com/Aethereux/Hikari-LLVM19.git
cd Hikari-LLVM19
mkdir build && cd build
cmake -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
-DLLVM_ENABLE_PROJECTS="clang;lld" \
-DLLVM_TARGETS_TO_BUILD="X86;ARM;AArch64" \
-DLLVM_ENABLE_ZSTD=OFF \
-DCMAKE_OSX_DEPLOYMENT_TARGET=12.0 \
-DLLVM_CREATE_XCODE_TOOLCHAIN=ON \
-DLLVM_INCLUDE_TESTS=OFF \
-DLLVM_INCLUDE_EXAMPLES=OFF \
-DLLVM_INCLUDE_UTILS=OFF \
-DLLVM_INCLUDE_BENCHMARKS=OFF \
-DLLVM_BUILD_TOOLS=OFF \
-DLLVM_INSTALL_TOOLCHAIN_ONLY=ON \
-DCMAKE_INSTALL_PREFIX="./install" \
../llvm

ninja
ninja install-xcode-toolchain

我是8核,就可以把 ninja 指令改成 ninja - j8 加快编译速度,

编译完成会在 /build/install/Toolchains 文件夹生成 xctoolchain ,复制到 /Applications/Xcode.app/Contents/Developer/Toolchains ,在左上角的 Xcode - Toolchains 里选择编译好的 llvm 。

打开 xcode 项目的 TARGETS ,Build Settings 里搜索cflags 单击编辑标记,对整个项目有效,

在 Build Phases - Compile Sources 里单击文件,指定混淆某个文件,

不要输入多余的引号,不然会像他一样

Build Settings 里把 Enable Index-While-Building Functionality 改成 NO ,

Optimization Level 的值设置为 None[-O0],Swift 的就不用管了,这个 LLVM 不能混淆 Swift 文件,对 .swift 文件增加混淆标记会报错,

三.效果

测试源代码如下

这篇文章只会针对 main 函数分析,

开启字符串加密 -mllvm -enable-strcry后,IDA 反汇编代码

从 Hikari 开源的代码中可以看到字符串混淆是用 Xor 异或加密的,双击进入一个地址 byte_100008080 ,就可以看到加密的字节,

打上注释,

可以到https://www.toolhelper.cn/SymmetricEncryption/XOR解密,

 

也可以用 Python 写个脚本,

在字符串加密的基础上开启虚假控制流-mllvm -enable-bcfobf后,IDA 反编译代码

虚假控制流在原本字符串加密的基础上增加了一些永不执行(或只执行一次)的代码块,判断条件是一些数字加减,双击跳转到地址就可以看到对应的变量,

在此基础上再开启-mllvm -enable-fco控制流平坦化后,代码量好像变大了

没关系,都是纸老虎,IDA 反汇编代码,

可以先把一些永不执行(或只执行一次)的代码块Nop掉,把一些复杂计算的公式直接赋值,比如 v31 这个变量的赋值,v31 = 541347955,从 unsigned int v31; // [xsp+5Ch] [xbp-4h]里可以看这个变量相对于当前的栈指针 SP 向上偏移 0x5C 字节,相对于帧指针 X29(FP) 向下偏移 4 字节,点击发现这个变量在 100001280 被赋值,到汇编代码里看

地址汇编代码为 STUR W8, [X29,#var_4],从函数头部可以看到__text:0000000100001204 var_4 = -4,正是unsigned int v31; // [xsp+5Ch] [xbp-4h]里的相对偏移,

点击左上角 Edit - Patch program - Change bytes,修改 1280 的字节 A8 C3 1F B8 -> 68 EE 99 52,把v31的值设置为 541347955,

再次打开反汇编代码,v31的赋值语句已经消失,

按照这个操作把复杂的 while 语句简化一下后,就可以得到可读性的代码

 

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 暂无
最后更新:2025年 11月 29日

IosBX

这个人很懒,什么都没留下

点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2026 IosBX's Blog. ALL RIGHTS RESERVED.