如何避免字符串混淆加密_IOS 代码混淆

使用脚本语言混淆,不需要讲脚本添加到工程中

先看下效果图:

72cadfc0e2496ee98d39be1283a40afe.png
类名混淆

90f43bcaf59530a587bd19d8d056c950.png
字段,方法名混淆

2adc2d27a48cffc595444235e32ff875.png
明文字符串混淆

29f2a918d825f6031770984085cd51bd.png
垃圾类代码

本文 脚本修改好路径即可直接使用,也可以根据需要修改为适合自己的正则表达式

下面直接上代码

类名,方法名,字段混淆脚本

混淆脚本:

#!/bin/sh
##################################
# (该脚本是在https://github.com/heqingliang/CodeObfus 上找到的)
#  代码混淆脚本 
#
##################################

#识别含有多字节编码字符时遇到的解析冲突问题
export LC_CTYPE=C
export LANG=C

#配置项:
#项目路径,会混淆该路径下的文件
ProjectPath="/Users/项目路径/"
FileDir="/Users/存放路径"
#这个路径是混淆成功后,原文本和替换文本解密对应的文件存放路径(该路径不能在项目目录或其子目录),
#混淆成功后会在该路径下生成一个解密时需要的文件,根据该文件的文本内容把混淆后的内容更换为原文本内容,
#该文件名的组成由$(date +%Y%m%d)"_"$(date +%H%M)及日期_小时组成,每分钟会不一样。
#所以解密的时候需要每次更换文件路径
SecretFile=$FileDir"/tihuan"$(date +%Y%m%d)"_"$(date +%H%M)

if [[ ! -d "$FileDir" ]];   #判断此目录是否不存在
then 
mkdir $FileDir;  #不存在则创建
fi
#第一个参数为项目路径
if [[ $1 ]]
then
if [[ $1 != "_" ]]; then
ProjectPath=$1
fi
fi
#第二个参数指定密钥文件路径及文件名
if [[ $2 ]]
then
if [[ $2 != "_" ]]; then
SecretFile=$2
fi
fi
##############!注意 字段中间不要包含obf_或者_fuc,没有适配中文 ##############
#查找文本中所有要求混淆的属性方法类,
#只会替换文本中符合 obf_[A-Za-z0-9_]*_fuc的字符(区分大小写,例如oB_就不会做混淆),
#如果注释内容有该类型的字符串,也会进行替换。前后缀及正则表达式可以自行置
#对于使用 _下划线访问的变量属性,不会有影响,一样会替换成对应_的混淆内容。
resultfiles=`grep 'obf_[A-Za-z0-9_]*_fuc' -rl $ProjectPath`
#查找结果为空则退出
if [[ -z $resultfiles ]]
then
echo "项目没有需要混淆的代码"
exit
else
echo "开始混淆代码..."
echo  > $SecretFile
fi

x=$(awk  '
BEGIN{srand();k=0;}
#随机数生成函数
function random_int(min, max) {
return int( rand()*(max-min+1) ) + min;
}
#随机字符串生成函数
function random_string(len) {
result="Unn"k;
#字符替换字典,可以自行设置
alpbetnum=split("Melon,Seed,Juice,Scab,Ice,Mole,Vine,Sour,Grape,Wood,Flat,Blight,Blow,Sauce,Oil,Peach,Bloom,Darling,Dear,Lief,Loving,Tootsy,Trials,Sweet,Dorsal,Dreams,Enough,Real,Hug,Desc,Them,Comes,Want,Awfully,Pick,Have,Only,Just,There,Much,Moments,Slow,Morning,Enhance,Privacy,Leave,Slime,Coffee,Forget,Key,Label,Count,Random,One,Two,Three,Six,Use,Salar,Sub,Cons,Mobile,Seabed,Devastate,Colonial,Dialect,Unevenly,Anti,Tool,Can,Down,Pass,Allow,Glider,Say,Inter,Other,Know,From,City,Swap,File,May,Max,Load,Date,Data,Msg,Or,Net,Book,Every,Been,Site,Bin,My,Idaho,Bess,Me,Present,Perfect,Scouring,Info,Online,Moment,Air,Tracks,Bus,Tree,See,Start,Come,Plan,Dis,Dev,Photo,Free,Easy,Read,Write,Any,Secure,Cry,Protect,Support,Del,Ensure,Func,Hooray,Snowing,Time,Make,Runs,Big,go,Hello,int,Test,Your,Vocabulary,With,Our,Fun,Image,Quizzes,Joy,Kid,Later,Min,Nor,Opp,Pig,Que,Res,Sub,Ter,Until,Tame,Vc,Win,Xin,Graze,Aversion,Arsenal,Obsession,Barren,Yun,Zoo,Com,Only,Memory,Ache,Blind,Cable,Drp,Eys,Fly,Good,High,Index,Job,join,Select,Last,Lag,Jump,And,Kind,Line,Moon,Night,Option,Off,On,In,Call,Than,The,Order,Pic,Que,Red,Sole,Tip,Up,Ver,Wind,Xrc,Year,Zero", alpbet, ",");
for (i=0; i<len; i++) {
result = result""alpbet[ random_int(1, alpbetnum) ];
}
return result;
}
/obf_[A-Za-z0-9_]*_fuc/{
x = $0;
#匹配需要混淆的属性变量方法
while (match(x, "obf_[A-Za-z0-9_]*_fuc") > 0) {
tempstr=substr(x, RSTART, RLENGTH);
#判断是否有之前已经找过的重复字符串
for ( i = 0; i < k; i++ ){
if (strarr[i] == tempstr){break;}
}
if(i<k){
#重复字符串,直接删除。所以不用担心混淆内容过多,可能会出现重复的混淆字符串
x=substr(x, RSTART+RLENGTH);
continue;
}else{
#不是重复字符串,添加到替换数组
strarr[k++]=tempstr;
}
#可以设置最长几个单词,目前最长6个单词,最少1个单词
randomstr=random_string(random_int(1, 6));
printf("%s:%s|", tempstr,randomstr);
#替换随机字符串
gsub(tempstr,randomstr, x);
x = substr(x, RSTART+RLENGTH);
}
}' $resultfiles )

#加密对写入密钥文件
echo $x > $SecretFile

recordnum=1
while [[ 1 == 1 ]]; do
record=`echo $x|cut -d "|" -f$recordnum`
if [[ -z $record ]]
then
break
fi
record1=`echo $record|cut -d ":" -f1`
echo "原项:"$record1
record2=`echo $record|cut -d ":" -f2`
echo "加密项:"$record2
#替换文件夹中所有文件的内容(支持正则)
#单引号不能扩展
sed -i '' "s/${record1}/${record2}/g" `grep $record1 -rl $ProjectPath`
echo "第"$recordnum"项混淆代码处理完毕"
let "recordnum = $recordnum + 1"
done

#查找需要混淆的文件名并替换
filerecordnum=1
while [[ 1 == 1 ]]; do
filerecord=`echo $x|cut -d "|" -f$filerecordnum`
if [[ -z $filerecord ]]
then
break
fi
filerecord1=`echo $filerecord|cut -d ":" -f1`
#echo "原项:"$filerecord1
filerecord2=`echo $filerecord|cut -d ":" -f2`
#echo "加密项:"$filerecord2
#改文件名

find $ProjectPath -name $filerecord1"*"| awk '
BEGIN{frecord1="'"$filerecord1"'";frecord2="'"$filerecord2"'";finish=1}
{
filestr=$0;
gsub(frecord1,frecord2,filestr);
print "mv " $0 " " filestr";echo 第"finish"个混淆文件处理完毕";
finish++;
}'|bash
let "filerecordnum = $filerecordnum + 1"
done

解混淆脚本:

#!/bin/sh
######################################
#
#  代码还原脚本 
#
######################################

#识别含有多字节编码字符时遇到的解析冲突问题
export LC_CTYPE=C
export LANG=C

#配置项:
#已经混淆的项目路径
ProjectPath="/Users/项目路径/"
#这个是文件路径而不是目录,是混淆的时候生成的文本文件路径,每次不一样。
#所以每次加密后,解密时需要更换路径
SecretFile="/Users/存放路径/obfuscation/tihuan20200603_1714"
#第一个参数为项目路径
if [[ $1 ]]
then
if [[ $1 != "_" ]]; then
ProjectPath=$1
fi
fi
#第二个参数指定密钥文件路径及文件名
if [[ $2 ]]
then
if [[ $2 != "_" ]]; then
SecretFile=$2
fi
fi
##############################################################################
#内容还原
x=`cat $SecretFile`
recordnum=1
while [[ 1 == 1 ]]; do
record=`echo $x|cut -d "|" -f$recordnum`
if [[ -z $record ]]
then
break
fi
record1=`echo $record|cut -d ":" -f1`
echo "原项:"$record1
record2=`echo $record|cut -d ":" -f2`
echo "加密项:"$record2
#若项目中加密项与密钥文件的加密项不符合则退出程序
searchresult=`grep $record2 -rl $ProjectPath`
if [[ -z $searchresult ]]; then
echo "指定的密钥文件不能还原"
exit
fi
#替换文件夹中所有文件的内容(支持正则)
#单引号不能扩展
sed -i '' "s/${record2}/${record1}/g" $searchresult
echo "第"$recordnum"项混淆代码还原完毕"
let "recordnum = $recordnum + 1"
done
#文件还原
filerecordnum=1
while [[ 1 == 1 ]]; do
filerecord=`echo $x|cut -d "|" -f$filerecordnum`
if [[ -z $filerecord ]]
then
break
fi
filerecord1=`echo $filerecord|cut -d ":" -f1`
#echo "原项:"$filerecord1
filerecord2=`echo $filerecord|cut -d ":" -f2`
#echo "加密项:"$filerecord2
#改文件名

find $ProjectPath -name $filerecord2"*"| awk '
BEGIN{
frecord1="'"$filerecord1"'";
frecord2="'"$filerecord2"'";
finish=1;
}
{
filestr=$0;
gsub(frecord2,frecord1,filestr);
print "mv " $0 " "filestr ";echo 第"finish"个混淆文件还原完毕"
finish++;
}'|bash
let "filerecordnum = $filerecordnum + 1"
done

使用:

  1. 添加脚本代码到 obfuscation.sh 和 dis_obfuscation.sh 文件中 ,配置好需要混淆的字符格式(例如:obf_study_fuc),路径
  2. 打开终端 cd到脚本所在目录,chmod 777 obfuscation.sh 获取执行权限
  3. 终端输入 ./obfuscation.sh 执行混淆脚本
  4. 解混淆 终端输入 ./dis_obfuscation.sh 执行解混淆脚本

明文字符串混淆脚本(需要Python环境)

宏定义及还原字符串方法:

//confusion_NSSTRING(@"字符串混淆加密 和 解密的宏开关")
//#define obfuscation_confusion
#ifdef obfuscation_confusion
    #define confusion_NSSTRING(string) obf_decryptConstString_fuc(string)
#else
    #define confusion_NSSTRING(string) string
#endif
//confusion_NSSTRING(@"obf_Miszng_fuc 是base64串能解码为原字符串的钥匙,钥匙字符串是这样的:@"[`~!#$^&*+|{}';',[]<>?!¥ ‘;:”“’。,、?"]"")
#define obf_Miszng_fuc @"W2B+ISMkXiYqK3x7fSc7JyxcW1xdPD7vvIHvv6XigKbigKbvvIjvvInjgJDjgJHigJjvvJvvvJrigJ3igJzigJnjgILvvIwiXQ=="

static NSString* obf_decryptConstString_fuc(NSString* s){
    //confusion_NSSTRING(@"运行时会执行decryptConstString函数还原字符串")
    NSString *obf_resultString_fuc = [[NSString alloc]initWithData:[[NSData alloc]initWithBase64EncodedString:s options:0] encoding:NSUTF8StringEncoding];
    NSString *obf_regexString_fuc =  [[NSString alloc]initWithData:[[NSData alloc]initWithBase64EncodedString:obf_Miszng_fuc options:0] encoding:NSUTF8StringEncoding];
    NSRegularExpression *obf_regex_fuc = [NSRegularExpression regularExpressionWithPattern:obf_regexString_fuc options:0 error:NULL];
    return [obf_regex_fuc stringByReplacingMatchesInString:obf_resultString_fuc options:0 range:NSMakeRange(0, [obf_resultString_fuc length]) withTemplate:@""];
}

混淆脚本:

#!/usr/bin/env python
# encoding=utf8
# -*- coding: utf-8 -*-
# author by one
# 脚本将会用于对指定目录下的.h .m源码中的字符串进行转换
# 替换所有字符串常量为base64串
import importlib
import os
import re
import sys
import base64
import random
if sys.getdefaultencoding() != 'utf-8':
    reload(sys)
    sys.setdefaultencoding('utf-8')

# replace替换字符串为base64串
def replace(match):
    string = match.group(2) 
    print("string : "+string)
    charlist = [';','+','$','&','*','[',']','{','}','<','>','!','#','`','~']
    list_str = [charlist[random.randint(0,len(charlist)-1)]+v+charlist[random.randint(0,len(charlist)-1)] for v in string.decode("utf8")]
    replaced_string = ''.join(str(i) for i in list_str)
    # print("替换后 :" + replaced_string)
    replaced_string = '@"' + base64.b64encode(replaced_string.encode("utf-8")) + '"'
    return match.group(1) + replaced_string + match.group(3)


# obfuscate方法是修改传入文件源代码中用confusion_NSSTRING标记的所有字符串
# 使用replace函数对字符串进行异或转换
def obfuscate(file):
    with open(file, 'r') as f:
        code = f.read()
        f.close()
        code = re.sub(r'(confusion_NSSTRING(|confusion_CSTRING()@"(.*?)"())', replace, code)
        # code = re.sub(r'(confusion_TAG(|confusion_CSTRING()(.*?)())', replace, code)
        code = re.sub(r'//#define obfuscation_confusion', '#define obfuscation_confusion', code)
        with open(file, 'w') as f:
            f.write(code)
            f.close()



# openSrcFile方法是读取源码路径下的所有.h和.m 文件
# 对每个文件执行obfuscate函数
def openSrcFile(path):
    print("混淆的路径为 : "+ path)
    # this folder is custom
    for parent,dirnames,filenames in os.walk(path):
        #case 1:
        #        for dirname in dirnames:
        #            print((" parent folder is:" + parent).encode('utf-8'))
        #            print((" dirname is:" + dirname).encode('utf-8'))
        #case 2
        for filename in filenames:
            extendedName = os.path.splitext(os.path.join(parent,filename))
            if (extendedName[1] == '.h' or extendedName[1] == '.m'):
                print("n处理源代码文件: "+ os.path.join(parent,filename))
                obfuscate(os.path.join(parent,filename))


#这里需要修改源码的路径为自己工程的文件夹名称
srcPath = '/Users/项目路径'

if __name__ == '__main__':
    print("本脚本用于对源代码中被标记的字符串进行加密")
    
    if len(srcPath) > 0:
        openSrcFile(srcPath)
    else:
        print("请输入正确的源代码路径")
        sys.exit()
        

解混淆脚本:

#!/usr/bin/env python
# encoding=utf8
# -*- coding: utf-8 -*-
# author by one
# 解密脚本
# 替换所有标记过的base64串为字符串常量,""

import importlib
import os
import re
import base64
import sys
if sys.getdefaultencoding() != 'utf-8':
    reload(sys)
    sys.setdefaultencoding('utf-8')
# 替换base64串为原字符串
def replace(match):
    string = match.group(2)
    string = base64.b64decode(string).decode("utf-8")
    decodeConfusion_string = re.sub("[+,$^*&#`<!;{>}][+"']+|[+!!,][。;<{>}?`、~#¥……&*()]+",'',string.decode('utf8'));
    replaced_string = '@"' + decodeConfusion_string + '"'
    print("replaced_string = " + replaced_string)
    return match.group(1) + replaced_string + match.group(3)
#ios解码: NSString *regexString = ;

# 修改源代码,加入字符串加密的函数
def obfuscate(file):
    with open(file, 'r') as f:
        code = f.read()
        f.close()
        # code = re.sub(r'(confusion_NSSTRING(|confusion_CSTRING()((char []) {(.*?)})())', replace, code)
        code = re.sub(r'(confusion_NSSTRING(|confusion_CSTRING()@"(.*?)"())', replace, code)
        # code = re.sub(r'(confusion_TAG(|confusion_CSTRING()(.*?)())', replace, code)
        code = re.sub(r'#define obfuscation_confusion', '//#define obfuscation_confusion', code)
        with open(file, 'w') as f:
            f.write(code)
            f.close()


#读取源码路径下的所有.h和.m 文件
def openSrcFile(path):
    print("解密路径: "+ path)
    # this folder is custom
    for parent,dirnames,filenames in os.walk(path):
        #case 1:
        #        for dirname in dirnames:
        #            print((" parent folder is:" + parent).encode('utf-8'))
        #            print((" dirname is:" + dirname).encode('utf-8'))
        #case 2
        for filename in filenames:
            extendedName = os.path.splitext(os.path.join(parent,filename))
            #读取所有.h和.m 的源文件
            if (extendedName[1] == '.h' or extendedName[1] == '.m'):
                print("已解密文件:"+ os.path.join(parent,filename))
                obfuscate(os.path.join(parent,filename))


#源码路径
srcPath = '/Users/项目路径'
if __name__ == '__main__':
    print("字符串解混淆脚本,将被标记过的base64串转为字符串,还原代码")
    if len(srcPath) > 0:
        openSrcFile(srcPath)
    else:
        print("请输入正确的源代码路径!")
        sys.exit()

使用:

  1. 项目中添加宏定义及还原字符串方法,添加脚本代码到 confusion.py和decrypt.py文件中 配置好项目路径
  2. 混淆字符串 终端输入 python 脚本路径/confusion.py
  3. 解混淆 终端输入 python 脚本路径/decrypt.py

其它加密方式可以根据需要自行修改脚本中的 def replace(match): 方法加解密.和项目代码中的decryptConstString还原字符串方法(与解密脚本的def replace(match): 相对应).

添加垃圾类代码

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
#第一步:首先生成一个500位的数组 驼峰类型的元素 用作文件名 eg:AsdfdfGsd
import random
import os,sys
import string

first = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
second = "abcdefghijklmnopqrstuvwxyz"
number = "345"
index = 0
array = []
for i in range(500):
    final=(random.choice(first))
    index = random.randint(3, 5)
    for i in range(index):
        final+=(random.choice(second))
    final += (random.choice(first))
    for i in range(index):
        final+=(random.choice(second))
    array.append(final)
print (array)

#第二步:
#用上边生成的数组来创建对应的.h和.m文件
# -*- coding: utf-8 -*-
import random
import os
import string
#创建.h文件
def text_createH(fileNmae,msg,msg1,propertyNumber,methodArray,msg3):
    # full_path = sys.path[0] + '/OCFiles/' + fileNmae + '.h'  #文件所在文件夹
    full_path = '/Users/user/Downloads/SDKworkSpace_copy/GameSDK/obf_GameSDK_fuc/' + fileNmae + '.h' #工程所在目录
    file = open(full_path, 'w')
    file.write('//n//  '+fileNmae+'.hnn')
    file.write(msg)
    file.write(msg1)
    propryNameArray = []
    for index in range(1,propertyNumber):
        propryNameArray.append(random.choice(array))
    propryNameArray = list(set(propryNameArray))
    for propertyName in propryNameArray:
        file.write('@property(nonatomic,strong)'+random.choice(classArray)+' * '+'obf_'+propertyName+'_fuc;n')
    file.write('nn')
    for methodName in methodArray:
        file.write('- (void)obf_pushTo'+methodName+'VC_fuc:(NSDictionary *)obf_info_fuc;n')
    file.write(msg3)
    file.close()
    print('Done')
#创建.m文件
def text_createM(fileNmae,msg,msg1,methodArray,msg3):
    # full_path = sys.path[0] + '/OCFiles/' + fileNmae + '.m' #文件所在文件夹
    full_path = '/Users/项目路径/垃圾代码存放目录/' + fileNmae + '.m'#工程所在目录
    file = open(full_path, 'w')
    file.write('//n//  '+fileNmae+'.mnnn')
    file.write(msg)
    file.write(msg1)
    for methodName in methodArray:
        file.write('- (void)obf_pushTo'+methodName+'VC_fuc:(NSDictionary *)obf_info_fucn{nn  NSMutableArray *obf_'+methodName+'_fuc = [NSMutableArray array];n')
        number = random.randint(3, 10)
        for i in range(1,number):
            file.write('  [obf_'+methodName+'_fuc addObject:@"'+random.choice(array)+'"];n')
        file.write('n}nn')
    file.write(msg3)
    file.close()
    print('Done')

classArray = ['NSString','UILabel','NSDictionary','NSData','UIScrollView','UIView']
array = ['HwxrFvrj', 'QnzduQbtdd', 'PvcrwLtqhf', 'UvdhDbjn', 'SuntmyTxvyzg', 'CvlxwBipbp', 'GzrdyzIbimvz', 'CqsjqMmgsp', 'OxaaeuWjhasc', 'NjiardRvwgbi', 'NcculmLtpljq', 'ApoqQrll', 'GkgokDyvjb', 'EblldkVouplj', 'KfdrFvnw', 'SfhyhObftc', 'SmruByoc', 'YzcccvXmpmit', 'OmqvaHpxat', 'XzytsUyvyd', 'MjforNnnyi', 'ZvjhuIdogs', 'BzfrxzSeahxc', 'PycycwFjtpny', 'XvngtoSedljr', 'DktiaCbucd', 'AqbplNuodc', 'MzkvgZuala', 'KdwzIoej', 'AaynatUpqcfd', 'IyvwhZvtjc', 'UmijGmsy', 'AoayndXxghym']
array = list(set(array))

for name in array:
    number = random.randint(3, 10)
    methodArray = []
    for i in range(1,5):
        methodArray.append(random.choice(array))
    methodArray = list(set(methodArray))#数组去重
    text_createH('obf_'+name+'ViewController_fuc', '#import <UIKit/UIKit.h>n','@interface '+'obf_'+name+ 'ViewController_fuc:'+ 'UIViewControllernn',number,methodArray,'nn@end')
    text_createM('obf_'+name+'ViewController_fuc', '#import "'+'obf_'+name+'ViewController_fuc.h"nn' '@interface '+'obf_'+ name+'ViewController_fuc()nn @endnn','@implementation '+'obf_'+name+'ViewController_fucnn- (void)viewDidLoad { nn [super viewDidLoad];nn}nn',methodArray,'nn@end')

使用:

  1. 将脚本代码添加到obfuscation_addCode.py 文件,并 配置好路径.
  2. 打开终端 执行 python 脚本代码所在目录/obfuscation_addCode.py

可以先使用这个脚本添加好垃圾类代码再进行混淆,这样可以把垃圾类也一同混淆了.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值