iOS Framework混淆/编译打包脚本(支持swift/oc/c++)

本文将介绍iOS代码混淆及编译的自动化脚本方案,支持swift、m、c、cpp和h文件类型的混淆,使用编译时混淆的策略,不影响源码阅读,只需在想要混淆的函数名或者变量名前加个private_即可,可通过函数实现安全混淆、去混淆、混淆再编译。
Demo传送门

⚠️声明

  1. 请将该脚本放在Xcode的Project工程的根目录。
  2. 当前版本未配置完整Xcode环境变量,仅支持混淆功能,不支持framework编译,若需编译请用Xcode运行该脚本。
  3. PS:下一版更新会支持在终端运行脚本。

先祭出shell代码

#!/bin/bash

#  confuseAndBuild.sh
#  ConfuseSwift
#
#  Created by Jonor on 2018/4/28.
#  Copyright © 2018年 Zhang. All rights reserved.

# ⚠️声明
# 1. 请将该脚本放在Xcode的Project工程的根目录。
# 2. 当前版本未配置完整Xcode环境变量,仅支持混淆功能,不支持framework编译,若需编译请用Xcode运行该脚本。
# 3. PS:下一版更新会支持在终端运行脚本。

# 认为定义了‘PROJECT_NAME’的是从Xcode运行,未定义则是从终端运行
if [ -z "$PROJECT_NAME" ]; then
    CONFUSE_DIR="."
else
    CONFUSE_DIR="${SRCROOT}/${PROJECT_NAME}"
fi

CONFUSE_PREFIX="private_"

BACKUP_FILE=".backup.log"
SYMBOL_FILE=".symbol.log"
CONFUSE_FILE=".confuse.log"
CONFUSE_FLAG=".confuseFlag"

SOURCE_ARRAY=( "*.swift" 
                "*.m" 
                "*.h" 
                "*.c" 
                "*.cpp")
BACKUP_EXTENSION=".bak"


# 格式:echo -e "\033[背景色;前景色m 打印的字符串 \033[0m" 
# 颜色:重置=0,黑色=30,红色=31,绿色=32,黄色=33,蓝色=34,洋红=35,青色=36,白色=37。
# 示例:echo -e “\033[30m 我是黑色字 \033[0m” 
# 参考:https://www.cnblogs.com/xiansong1005/p/7221316.html
#      https://www.cnblogs.com/lr-ting/archive/2013/02/28/2936792.html
info() {
    local green="\033[1;32m"
    local normal="\033[0m"
    echo -e "[${green}info${normal}] $1"
}

error() {
    local red="\033[1;31m"
    local normal="\033[0m"
    echo -e "[${red}error${normal}] $1"
}

# 生成随机字符串 16字
randomString() {
    openssl rand -base64 64 | tr -cd 'a-zA-Z' | head -c 16
}

# 获取符号的随机字符串  $1是符号名
randomStringWithSymbol() {
    grep -w $1 $SYMBOL_FILE -h | cut -d \  -f 2
}

removeIfExist() {
    if [ -f $1 ]; then
        rm $1
    fi
}

# 备份文件 $1:file full path
backupFile() {
    file=$1
    # 在原文件名前加个.(点符合)用作备份名
    fileName=${file##*/}
    backupPath=${file/$fileName/.$fileName$BACKUP_EXTENSION}
    echo "backup $file to $backupPath"

    if [ ! -f $backupPath ]; then
        cp $file $backupPath
        echo $backupPath >>$BACKUP_FILE
    fi
}

# 方案1. 精确备份:用关键字遍历会修改到的source文件,再将其备份 -- 消耗性能
# 方案2. 整体备份:备份所有source文件 -- 消耗存储空间
# 根据需要,为简单起见,这里选用方案2
backupAllSource() {
    info "backup all swift files"
    NAMES="-name \"${SOURCE_ARRAY[0]}\""
    i=1
    while [ $i -lt ${#SOURCE_ARRAY[@]} ]; do  
        NAMES+=" -or -name \"${SOURCE_ARRAY[$i]}\""
        let i++
    done
    # echo $NAMES

    removeIfExist $BACKUP_FILE
    touch $BACKUP_FILE
    
    eval "find $CONFUSE_DIR $NAMES" | while read file; do
        backupFile $file
    done
}

# 混淆工作, ⚠️该函数不会自动备份,要备份请调用safeConfuse函数
confuseOnly() {
    info "confuse start..."

    # 获取要混淆的函数名和变量名
    INCLUDES="--include=\"${SOURCE_ARRAY[0]}\""
    i=1
    while [ $i -lt ${#SOURCE_ARRAY[@]} ]; do  
        INCLUDES+=" --include=\"${SOURCE_ARRAY[$i]}\""
        let i++    
    done
    eval "grep $CONFUSE_PREFIX -r $CONFUSE_DIR $INCLUDES -n" >$CONFUSE_FILE

    # cat $CONFUSE_FILE
    # 绑定随机字符串
    removeIfExist $SYMBOL_FILE
    touch $SYMBOL_FILE
    
    cat $CONFUSE_FILE | egrep -w $CONFUSE_PREFIX"[0-9a-zA-Z_]*" -o | sort | uniq | while read line; do
        echo $line" `randomString`" >>$SYMBOL_FILE
    done

    # cat $SYMBOL_FILE

    # 读取备份文件记录
    # 在这里没使用遍历批量替换,怕文件太多的时候影响性能
    cat $CONFUSE_FILE | while read line; do
#        echo "> $line"
        # 截取行号
        lineNum=`echo $line | sed 's/.*:\([0-9]*\):.*/\1/g'`
        # 截取文件路径
        path=${line%%:*}
        
        # 一行可能有多个要替换的子串,要循环遍历完
        # 这里之所以要用`sort -r`倒序是因为有个bug:如有字符串"jjyy abc hello abcde", 现在要替换"abc"为"123"(abcde保持不变),也就是传说中的‘全匹配替换’,
        # 但不知为何在macOS下单词边界表达式不起作用:\<abc\> 或者 \babc\b都不起作用,Linux下这个正则表达式是没问题的。
        # 倒序之后有长串优先替换长串,防止短串把长串部分替换掉。但依然存在bug:若是长串不需要替换,则短串替换是依然会将长串部分替换😂
        # 因此依然还需要寻找macOS下单词边界/全匹配 的正则表达式
        echo $line | egrep -w $CONFUSE_PREFIX"[0-9a-zA-Z_]*" -o | sort -r | while read -ra symbol; do
            # 根据名称获取绑定的随机字符串
            random=`randomStringWithSymbol $symbol`
#            echo "$path $lineNum $symbol $random"
            # 随机字符串替换
            # -i:表示直接在原文件替换,"":表示不要备份
            sed -i "" "${lineNum}s/$symbol/$random/g" $path 

            echo "  $symbol => $random"
        done
    done

    info "confuse done"
}

# 编译工作,生成通用framework
buildAll() {
    info "build start..."
    
    if [ -z "$PROJECT_NAME" ]; then
        echo -e "\033[1;31mERROR:当前版本未配置完整Xcode环境变量,仅支持混淆功能,不支持framework编译,若需编译请用Xcode运行该脚本\033[0m"
        return
    fi

    # 要build的target名
    TARGET_NAME=${PROJECT_NAME}
    UNIVERSAL_OUTPUT_DIR="${SRCROOT}/Framework/"

    # 创建输出目录,并删除之前的framework文件
    mkdir -p "${UNIVERSAL_OUTPUT_DIR}"
    rm -rf "${UNIVERSAL_OUTPUT_DIR}/${TARGET_NAME}.framework"

    #分别编译模拟器和真机的Framework
    xcodebuild -target "${TARGET_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} ARCHS="armv7 armv7s arm64" -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
    xcodebuild -target "${TARGET_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} ARCHS="i386 x86_64" -sdk iphonesimulator BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

    #拷贝framework到univer目录
    cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${TARGET_NAME}.framework" "${UNIVERSAL_OUTPUT_DIR}"

    # 合并swiftmodule到univer目录
    cp -R "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/Modules/${TARGET_NAME}.swiftmodule/" "${UNIVERSAL_OUTPUT_DIR}/${TARGET_NAME}.framework/Modules/${TARGET_NAME}.swiftmodule"

    #合并framework,输出最终的framework到build目录
    lipo -create -output "${UNIVERSAL_OUTPUT_DIR}/${TARGET_NAME}.framework/${TARGET_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/${TARGET_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${TARGET_NAME}.framework/${TARGET_NAME}"

    #删除编译之后生成的无关的配置文件
    dir_path="${UNIVERSAL_OUTPUT_DIR}/${TARGET_NAME}.framework/"
    for file in ls $dir_path; do
        if [[ ${file} =~ ".xcconfig" ]]; then
            rm -f "${dir_path}/${file}"
        fi
    done

    #判断build文件夹是否存在,存在则删除
    if [ -d "${SRCROOT}/build" ]; then
        rm -rf "${SRCROOT}/build"
    fi

    #rm -rf "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator" "${BUILD_DIR}/${CONFIGURATION}-iphoneos"

    #打开合并后的文件夹
    open "${UNIVERSAL_OUTPUT_DIR}"

    info "build done"
}

# 清理工作,去混淆
unconfuse() {
    info "clean start..."
    if [ -f $CONFUSE_FLAG ]; then
        # 恢复混淆的函数名所在source文件的bak内容
        cat $BACKUP_FILE | while read backup; do
            backupName=${backup##*/}
            fileName=`echo $backupName | cut -d "." -f2,3`
            filePath=${backup/$backupName/$fileName}
            
            echo "recover $backup to $filePath"

            cp $backup $filePath
            rm $backup
        done
        # 删除修改记录
        removeIfExist $SYMBOL_FILE
        removeIfExist $CONFUSE_FILE
        removeIfExist $BACKUP_FILE
        removeIfExist $CONFUSE_FLAG
    else
        echo "Not confuse yet!"
    fi
    info "clean done"
}

# 检查是否上次未完成
precheck() {
    # 创建一个隐藏文件,仅标记混淆编译的状态
    # 由于编译过程有可能被中断,因此混淆后的代码可能未恢复,在开始备份前先做判断
    unconfuse        
    touch $CONFUSE_FLAG
}

# 去混淆->备份->混淆
safeConfuse() {
    precheck
    backupAllSource
    confuseOnly
}

# 去混淆->备份->混淆
# 编译
# 去混淆
safeConfuseAndBuild() {
    info "preparing confuse and build..."

    safeConfuse
    buildAll
    unconfuse

    info "all done"
}

usage() {
    echo -e "\033[1;31musage: ./confuseAndBuild.sh [-u|c|b|a]"
    echo -e "  -u"
    echo -e "      unconfuse: 清理工作,去混淆"
    echo -e "  -c"
    echo -e "      safeConfuse: 去混淆->备份->混淆"
    echo -e "  -b"
    echo -e "      buildAll: 编译生成通用framework"    
    echo -e "  -a"
    echo -e "      safeConfuseAndBuild: 去混淆->备份->混淆->编译->去混淆"
    echo -e "EXAMPLE:"
    echo -e "  ./confuseAndBuild.sh -u\033[0m"
}

main() {
    echo "参数个数:$#  参数值:$1"
    case $1 in
    "-u" )
        unconfuse
        ;;
    "-c" )
        safeConfuse
        ;;
    "-b" )
        buildAll
        ;;
    "-a" )
        safeConfuseAndBuild
        ;;
    * )
        usage
        ;;
    esac
}

main $@

⚠️注意

请勿在混淆中的项目里修改代码,因为混淆时我会备份所有swift文件,当再次运行safeConfuse、safeConfuseAndBuild之前我会恢复备份文件(如果有的话),因此可能会覆盖混淆时所作的修改。
以下情况会产生代码混淆或混淆残留:
- 运行了safeConfuse进行混淆;
- 运行safeConfuseAndBuild,并且运行过程被中断或编译时异常退出;
因此,在你修改代码前请确保代码未混淆或运行unconfuse以完全解除混淆。

建议看看Demo,内有三个脚本用例Scheme,对应以下三个函数,详细请看以上shell文件注释:

unconfuse() {
# 清理工作,去混淆
# 恢复混淆的函数名所在swift文件的bak内容
# 删除修改记录
}

safeConfuse() {
# 去混淆->备份->混淆
}

safeConfuseAndBuild() {
# 去混淆->备份->混淆
# 编译
# 去混淆
}

确认是否混淆成功

方法一:
使用反汇编工具(如dump,hopper,ida)查看framework,混淆后的‘private_’前缀字样都变成了随机字符串。
方法二:
单独运行safeConfuse这个函数,这个函数会将当前代码含‘private_’的字样进行混淆,运行结束你可以看到相应的代码变成了随机字符串。

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

推荐阅读更多精彩内容

  • 丽姐结婚了,27岁该结婚,不算稀罕事儿,但这事儿发生在了丽姐身上就成了新鲜事,因为丽姐曾经深爱着另一个人,深爱到我...
    夏夏侬艾阅读 473评论 0 0
  • 天刚麻麻亮,虎子爹就早早起来套好了毛驴,虎子娘把织好的粗布叠好,整齐地码在驴背上。进城要走四五十里的山路,...
    小猪皮杰阅读 596评论 1 4
  • 杂言诗(诗经格式):澧水美兮,沅江娇兮。于以爱之,彼我之故。高山峻兮,翠竹妭兮。于以爱之,彼我之此。 现代通俗诗(...
    硕果蕾蕾阅读 1,199评论 8 6