iOS代码混淆实现
2020/6/9 23:26:45
本文主要是介绍iOS代码混淆实现,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
背景
一些类似的项目会使用重复的代码,导致苹果机审期间被判断为马甲包,还没到人审就被苹果审核拒掉了。 为了能够使正常迭代出功能相似的项目过审,要对我们项目的代码进行深度混淆。
方案
1、准备四六级单词库(如果使用随机字符会被机审查出来); 2、使用clang过滤出类名和方法名; 3、从四六级单词库随机组成,映射对应的类名和方法名; 4、通过映射进行混淆操作。
四六级单词库
区分首字母小写和大写的txt
安装clang
pip install clang --user 复制代码
类名混淆
提取类名
# encoding: utf-8 import sys import os import re import clang from clang.cindex import * from optparse import OptionParser, OptionGroup def get_tu(source, lang='c', all_warnings=False, flags=[]): """Obtain a translation unit from source and language. By default, the translation unit is created from source file "t.<ext>" where <ext> is the default file extension for the specified language. By default it is C, so "t.c" is the default file name. Supported languages are {c, cpp, objc}. all_warnings is a convenience argument to enable all compiler warnings. """ args = list(flags) name = 't.c' if lang == 'cpp': name = 't.cpp' args.append('-std=c++11') elif lang == 'objc': name = 't.m' elif lang != 'c': raise Exception('Unknown language: %s' % lang) if all_warnings: args += ['-Wall', '-Wextra'] return TranslationUnit.from_source(name, args, unsaved_files=[(name, source)]) def generate_m_file(file_text, result_lines, ret_functions): parser = OptionParser("usage: %prog [options] {filename} [clang-args*]") parser.disable_interspersed_args() (opts, args) = parser.parse_args() # if len(args) == 0: # parser.error('invalid number arguments') index = Index.create() # tu = index.parse(file_text, ['-x', 'objective-c']) tu = get_tu(file_text, lang='objc') if not tu: parser.error("unable to load input") it = tu.cursor.get_children() tu_nodes = list(it) method_kinds = [CursorKind.OBJC_INSTANCE_METHOD_DECL, CursorKind.OBJC_CLASS_METHOD_DECL] for cursor in tu_nodes: if cursor.kind == CursorKind.OBJC_INTERFACE_DECL: # print cursor.spelling ret_functions.append(cursor.spelling) # print '// finish' if __name__ == '__main__': libclangPath = '/Library/Developer/CommandLineTools/usr/lib/libclang.dylib' Config.set_library_file(libclangPath) # Find all .h files source_dir = sys.argv[1] g = os.walk(source_dir) h_files = [] ret_functions = [] for path,dir_list,file_list in g: for file_name in file_list: h_files.append(os.path.join(path, file_name)) for f in h_files: with open(f, 'r') as file: # preprocess regex = r'#import|#include|#ifdef|#ifndef|#define|#endif|@property' text = '' result_text_lines = [] line_count = 0 def_block_count = 0 for line in file: if re.findall(regex, line): if '#ifdef' in line: def_block_count += 1 result_text_lines.append(line) elif '#endif' in line and def_block_count > 0: def_block_count -= 1 result_text_lines.append(line) else: result_text_lines.append('\n') line = '// ' + line text += line else: text += line result_text_lines.append('\n') line_count += 1 # print text m_file_name = os.path.join('fake', f.replace('.h', '.m')) m_file_to_write = "" # print 'processing: ' + m_file_name generate_m_file(text, result_text_lines, ret_functions) unique_array = list(set(ret_functions)) filter_array = ['xxxx', 'aaaa', 'dddd', 'AppDelegate', \ 'PrefixHeader', 'dddddf', 'aaaadxxx'] for func_item in unique_array: if func_item in filter_array: continue print func_item 复制代码
filter_array
为要筛选掉不做混淆的类名
对提取的类名做映射类名
#!/usr/bin/env bash TABLENAME=symbols SYMBOL_DB_FILE="symbols" STRING_SYMBOL_FILE=./process_class/t.txt HEAD_FILE=./rename-class/rename_classes.txt export LC_CTYPE=C rm -f $SYMBOL_DB_FILE rm -f $HEAD_FILE function rand(){ min=$1 max=$(($2-$min+1)) num=$(($RANDOM+1000000000)) #增加一个10位的数再求余 echo $(($num%$max+$min)) } function pRnd2(){ rnd=$(rand 10 4200) randint=`expr $RANDOM % 3` if [ $randint == 0 ];then echo `cat "JAAA.txt" | sed -n "${rnd}p"` elif [ $randint == 1 ];then echo `cat "JBBB.txt" | sed -n "${rnd}p"` else echo `cat "JCCC.txt" | sed -n "${rnd}p"` fi } my_arr=("Manager" "DataSource" "Helper" "Adapter" "Router" "Handler" "Handle" \ "Model" "Service" "Item" "Info" "Controller" "Cell" "Button" "View" "Window") touch $HEAD_FILE # echo "//confuse string at `date`" >> $HEAD_FILE cat "$STRING_SYMBOL_FILE" | while read -ra line; do #命中概率 #randint=`expr $RANDOM % 3` #if [ $randint != 0 ];then #continue #fi #取出随机字符 if [[ ! -z "$line" ]]; then suffix="" for loop in ${my_arr[@]}; do if [[ $line =~ $loop ]]; then suffix=$loop break fi done ramdom="CS$(pRnd2)$(pRnd2)${suffix}" echo $line $ramdom #insertValue $line $ramdom echo "$line $ramdom" >> $HEAD_FILE fi done 复制代码
ramdom
可以添加一些项目前缀,比如CS等。
my_arr
里面可以定义一些iOS特有的后缀。
对映射的类做混淆
#!/bin/bash PROJECT_DIR=`cat ../path.txt` echo $PROJECT_DIR RENAME_CLASSES=rename_classes.txt #First, we substitute the text in all of the files. sed_cmd=`sed -e 's@^@s/[[:<:]]@; s@[[:space:]]\{1,\}@[[:>:]]/@; s@$@/g;@' ${RENAME_CLASSES} ` find ${PROJECT_DIR} -type f \ \( -name "*.pbxproj" -or -name "*.pch" -or -name "*.h" -or -name "*.m" -or -name "*.xib" -or -name "*.storyboard" \) \ -exec sed -i "" "${sed_cmd}" {} + # Now, we rename the .h/.m files while read line; do class_from=`echo $line | sed "s/[[:space:]]\{1,\}.*//"` class_to=`echo $line | sed "s/.*[[:space:]]\{1,\}//"` #修改 .h .m find ${PROJECT_DIR} -type f -regex ".*[[:<:]]${class_from}[[:>:]][^\/]*\.[hm]" -print | egrep -v '.bak$' | \ while read file_from; do file_to=`echo $file_from | sed "s/\(.*\)[[:<:]]${class_from}[[:>:]]\([^\/]*\)/\1${class_to}\2/"` echo mv "${file_from}" "${file_to}" mv "${file_from}" "${file_to}" done #修改 .xib find ${PROJECT_DIR} -type f -regex ".*[[:<:]]${class_from}[[:>:]][^\/]*\.xib" -print | egrep -v '.bak$' | \ while read file_from; do file_to=`echo $file_from | sed "s/\(.*\)[[:<:]]${class_from}[[:>:]]\([^\/]*\)/\1${class_to}\2/"` echo mv "${file_from}" "${file_to}" mv "${file_from}" "${file_to}" done done < ${RENAME_CLASSES} 复制代码
rename_classes.txt
是保存的映射类,shell脚本对工程进行批量替换。
方法混淆
提取方法名
# encoding: utf-8 import sys import os import re import clang from clang.cindex import * from optparse import OptionParser, OptionGroup def get_tu(source, lang='c', all_warnings=False, flags=[]): """Obtain a translation unit from source and language. By default, the translation unit is created from source file "t.<ext>" where <ext> is the default file extension for the specified language. By default it is C, so "t.c" is the default file name. Supported languages are {c, cpp, objc}. all_warnings is a convenience argument to enable all compiler warnings. """ args = list(flags) name = 't.c' if lang == 'cpp': name = 't.cpp' args.append('-std=c++11') elif lang == 'objc': name = 't.m' elif lang != 'c': raise Exception('Unknown language: %s' % lang) if all_warnings: args += ['-Wall', '-Wextra'] return TranslationUnit.from_source(name, args, unsaved_files=[(name, source)]) def parse_method(node): tokens = list(node.get_tokens()) # 过滤方法名, TODO: filter_start_words = ('init', 'set', 'get', 'image', 'view', 'reload', '_', 'will', 'did') function = '' for token_index in range(len(tokens)): if tokens[token_index].spelling == ')': function = tokens[token_index + 1].spelling break if len(function) > 10 and (not function.startswith(filter_start_words)): return function else: return '' # extract_type = 0x00001: 普通方法 # extract_type = 0x00011: 普通方法 + 属性 def parse_symbols(cursor, ret_symbols, extract_type): class_children = list(cursor.get_children()) # .m匿名类解析 if cursor.kind == CursorKind.OBJC_CATEGORY_DECL: tokens = list(cursor.get_tokens()) for token_index in range(len(tokens)): if tokens[token_index].spelling == ';': function = tokens[token_index - 1].spelling if len(function) > 10: ret_symbols.append(function) # 正常解析 method_kinds = [CursorKind.OBJC_INSTANCE_METHOD_DECL, CursorKind.OBJC_CLASS_METHOD_DECL] for node in class_children: if len(node.spelling) < 10: continue # print 'node.spelling: ' + node.spelling if node.kind in method_kinds: if extract_type & 0x00001: function = parse_method(node) if len(function) > 1: ret_symbols.append(function) elif node.kind == CursorKind.OBJC_PROPERTY_DECL: if extract_type & 0x00010: # print 'property : ' + node.spelling if len(node.spelling) > 10: ret_symbols.append(node.spelling) # extract_type = 0x01100: Category, Class # extract_type = 0x10000: Protocol def extract_symbols(file_text, ret_symbols, extract_type): parser = OptionParser("usage: %prog [options] {filename} [clang-args*]") parser.disable_interspersed_args() (opts, args) = parser.parse_args() # if len(args) == 0: # parser.error('invalid number arguments') index = Index.create() # tu = index.parse(file_text, ['-x', 'objective-c']) tu = get_tu(file_text, lang='objc') if not tu: parser.error("unable to load input") it = tu.cursor.get_children() tu_nodes = list(it) for cursor in tu_nodes: if cursor.kind == CursorKind.OBJC_INTERFACE_DECL: # print cursor.spelling if extract_type & 0x00100: parse_symbols(cursor, ret_symbols, extract_type) elif cursor.kind == CursorKind.OBJC_CATEGORY_DECL: # print cursor.spelling if extract_type & 0x01000: # print "Categor ============" parse_symbols(cursor, ret_symbols, extract_type) elif cursor.kind == CursorKind.OBJC_PROTOCOL_DECL: if extract_type & 0x10000: parse_symbols(cursor, ret_symbols, extract_type) # 提取该目录下所有 .h&.m文件的方法名 def traverse_header_files(top_directory, extract_type): g = os.walk(top_directory) h_files = [] ret_symbols = [] for path,dir_list,file_list in g: for file_name in file_list: if file_name.endswith('.h') or file_name.endswith('.m'): h_files.append(os.path.join(path, file_name)) for f in h_files: with open(f, 'r') as file: # preprocess regex = r'#import|#include|#ifdef|#ifndef|#define|#endif|#if|#else|@class' text = '' result_text_lines = [] line_count = 0 def_block_count = 0 for line in file: if re.findall(regex, line): line = '// ' + line text += line else: interface_idx = line.find('@interface') if interface_idx > 0: line = line[interface_idx:] # print line text += line line_count += 1 # print 'processing: ' extract_symbols(text, ret_symbols, extract_type) return set(ret_symbols).copy() if __name__ == '__main__': libclangPath = '/Library/Developer/CommandLineTools/usr/lib/libclang.dylib' Config.set_library_file(libclangPath) source_dir = sys.argv[1] pods_dir = sys.argv[2] # 提取头文件的方法名 source_dir_methods_set = traverse_header_files(source_dir, 0x00101) # 提取 Source 的属性 filter_set_A = traverse_header_files(source_dir, 0x10110) # 提取 Source 下的 Category的方法和属性 filter_set_B = traverse_header_files(source_dir, 0x11011) # 提取 Pods 目录下普通类和Category 的方法和属性 filter_set_C = traverse_header_files(pods_dir, 0x11111) # 差集 result_set = source_dir_methods_set.difference(filter_set_A).difference(filter_set_B).difference(filter_set_C) unique_list = list(result_set) for func_item in unique_list: print func_item # print 'Source len = ' + str(len(source_dir_methods_set)) # print 'Result len = ' + str(len(result_set)) 复制代码
source_dir为项目的代码目录,pods_dir为pods的代码目录。由于pods里面的是三方的代码,所以进行在项目代码里排除掉,再对剩下的方法进行映射。
对方法映射并宏定义写入文件
#!/usr/bin/env bash STRING_SYMBOL_FILE=./process_method/method_list.txt HEAD_FILE=./methodDefine.h export LC_CTYPE=C rm -f $HEAD_FILE function rand(){ min=$1 max=$(($2-$min+1)) num=$(($RANDOM+1000000000)) #增加一个10位的数再求余 echo $(($num%$max+$min)) } function pRnd1(){ rnd=$(rand 10 140000) randt=`expr $RANDOM % 3` if [ $randt == 0 ];then echo `cat "a.txt" | sed -n "${rnd}p"` elif [ $randt == 1 ];then echo `cat "b.txt" | sed -n "${rnd}p"` else echo `cat "c.txt" | sed -n "${rnd}p"` fi } function pRnd2(){ rnd=$(rand 10 140000) randt=`expr $RANDOM % 3` if [ $randt == 0 ];then echo `cat "AAA.txt" | sed -n "${rnd}p"` elif [ $randt == 1 ];then echo `cat "BBB.txt" | sed -n "${rnd}p"` else echo `cat "CCC.txt" | sed -n "${rnd}p"` fi } touch $HEAD_FILE echo '#ifndef methodDefine_h #define methodDefine_h' >> $HEAD_FILE echo "//confuse string at `date`" >> $HEAD_FILE cat "$STRING_SYMBOL_FILE" | while read -ra line; do #命中概率 #randint=`expr $RANDOM % 3` #if [ $randint != 0 ];then #continue #fi #取出随机字符 if [[ ! -z "$line" ]]; then ramdom="$(pRnd1)$(pRnd2)" echo $line $ramdom echo "#ifndef $line #define $line $ramdom #endif" >> $HEAD_FILE fi done echo "#endif" >> $HEAD_FILE 复制代码
导入文件
prefixHeader导入methodDefine.h文件,方法混淆完成
这篇关于iOS代码混淆实现的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2022-10-05Swift语法学习--基于协议进行网络请求
- 2022-08-17Apple开发_Swift语言地标注释
- 2022-07-24Swift 初见
- 2022-05-22SwiftUI App 支持多语种 All In One
- 2022-05-10SwiftUI 组件参数简写 All In One
- 2022-04-14SwiftUI 学习笔记
- 2022-02-23Swift 文件夹和文件操作
- 2022-02-17Swift中使用KVO
- 2022-02-08Swift 汇编 String array
- 2022-01-30SwiftUI3.0页面反向传值