Swift之代码混淆的调研实施小记

背景:

最近做APP备案,需要对项目做一系列对优化改进,其中就包括了代码混淆,顾名思义,混淆是为了代码安全,是为了增加逆向破解的难度与复杂度。

目前市面上,免费和付费都有,一些公司对APP加固已经做成了产业,形成了一整套的解决方案,不过收费也是杠杠的,我也联系了其中一家,收费大概是【一年】【单APP】价格是2W(多APP价格可以再商量,应该会便宜点吧),当然这是他们号称的整个加固策略解决方案的价格。

 

说明:

不过呢,因为一些原因,我就暂时从开源的一些免费方案寻找解决处理,而且我们目前只是针对代码混淆安全性上的需求,所以就有了本篇文章。

进行了一些调研之后,我发现其实很多项目的早期混淆,大多是基于念茜大大的思路做的延伸扩展,基本思路为下:

写一个脚本,将项目中的一些敏感方法名集中写在一个名叫func.list的文件中,逐一#define成随机字符

原文地址:https://blog.csdn.net/yiyaaixuexi/article/details/29201699  有兴趣可以看一下

 

用这种方式,需要新建两个文件,一个混淆脚本,一个fun函数列表,用于添加你所需要混淆的方法,然后在Build Phases里添加执行Run Script

 

因为她这篇文章写的比较早,后面也有很多朋友对此基础上,做了一些优化更新,基本网上也都能搜到,甚至有些朋友直接做成了mac工具,直接拖动项目路径,配置一下前缀等关键词,就能一键混淆。

比如:iOS_NQConfuseTool ,不过这个工具填的参数比较多,出了问题就容易不响应了,囧,可以了解下,不是特别推荐

 

正文:

其实最开始我想象中的方案,是不用通过前缀来判别的,因为我想,对老项目太不友好了,还得针对类名、方法名一大堆慢慢加前缀什么的,工作量太大了,因此,我就在想,有没有更好的只能混淆方案呢?

结果是还真被我找到了,就是我的理解力不够,最终还是没能实现,且听我说完。

这个工具就是:swiftshield

Swift之代码混淆的调研实施小记_第1张图片

 

在发现这个工具时,我就在想,太好了,这个牛逼啊,功能强大,使用简单

不过我按照他的描述配置后,发现没起作用,在终端运行后,会提示我swiftshield没找到,目前暂还没找到解决方案,因此有搞定的朋友千万记得给我留个言,在线等。。。

 

不过即使生活qj了你,你还得过日子啊,此路不通,换条路继续,只不过路没这么好走罢了

 

我重新找到了一个方案,虽然不是智能一键混淆,但操作起来还是相对比较便捷的

思路:也是通过脚本实现前缀识别替换。

这个方案操作起来大概分以下几个步骤:

1、创建一个空 confuseAndBuild.sh文件,拖动到项目根目录(名字自己取)

2、将下面的脚本拷贝进去,注意:脚本的前缀是需要配置的,改成你自己的就行,脚本默认是"hunxiao_"

这是脚本!!!

#!/bin/bash

#  confuseAndBuild.sh
#  ConfuseSwift
#

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

# ⚠️教程
# https://www.jianshu.com/p/be751f780d94
# 直接在终端cd到confuseAndBuild.sh上一层目录,然后运行./confuseAndBuild.sh -c 即可


if [ -z "$PROJECT_NAME" ]; then
CONFUSE_DIR="."
else
CONFUSE_DIR="${SRCROOT}/${PROJECT_NAME}"
fi

# ⚠️自己配置自己的前缀
CONFUSE_PREFIX="hunxiao_"

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下单词边界表达式不起作用:\ 或者 \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 $@
View Code

3、在项目里,把你认为的一些类名、方法名等添加前缀

4、打开终端,cd到confuseAndBuild.sh上一层目录,然后运行./confuseAndBuild.sh -c 即可

 

执行完上述4步后,查看项目,会发现有前缀的方法、类等,都已变成混淆后的字符,然后直接打包发布即可。

 

这里注意一点:执行完脚本之后,项目里的函数方法等就会变成混淆后的内容,会大大影响阅读醒,因此建议在发布时,执行一下脚本,先别提交,待打包好后,撤回混淆就行了

  

参考文章:

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

iOS代码混淆教程

iOS代码混淆----自动

 

你可能感兴趣的:(Swift之代码混淆的调研实施小记)