oc代码混淆

混淆内容

1、混淆思路:利用宏定义进行混淆,混淆文件需要在需要混淆的文件中引用(或全局引用)

2、混淆内容:(橘黄色)

  公开变量  @property (nonatomic, strong) NSString  *gameName;

  私有变量 @interface XXXX () {

          NSString  *gameName;

      }

  类名    @interface ClassName ()

  方法名 

    公开 - (void)getUserName:(NSString *)name age:(NSInteger)age;

    私有 - (void)openFile:(NSString *)filePath size:(NSInteger)size {

      }

  文件名混淆(只混淆 .m 文件)

     UserInfo.h

     UserInfo.m

  字典中的key值(用于过滤混淆内容)

    [result objectForKey:@"status"]

    result[@"status"]

以上是需要混淆的内容,接下来说注意事项

公开变量:  @property (nonatomicstrong) NSString  *gameName;

  我们使用时会用到 self.gameName 、 _gameName 、- (void)setGameName:(NSString *)gameName.

  那么混淆时一个公开变量需要对应进行三次混淆。假定将 gameName 混淆为 gjkgnshd

    1、gjkgnshd

    2、_gjkgnshd

    3、setGjkgnshd (这里需要注意,gameName 的 set 方法为 setGameName , 变量名的首字母需大写,之前大写则不变)

私有变量:  一次直接混淆

类名:    一次直接混淆

方法名:   一次直接混淆  (这里需要注意 set 方法需要过滤掉,如果这里进行set方法混淆,会造成重复混淆,99.99%产生错误,因为在混淆公开变量的时候你已经混淆过了,但公开变量的set方法和参数有特定联系,所以必须在混淆公开变量时进行混淆)

文件名:  批量重命名即可

字典中的key值:混淆时一定要过滤掉字典中的key值 :(举个栗子 )

私有变量  @property (nonatomicstrong) NSString  *gameName;

字典key值  dict = @{@"gameName" : @"大逃杀"}    

   在你混淆 gameName -> gjkgnshd 时 ,下面为混淆结果

      @property (nonatomicstrong) NSString  *gjkgnshd; 

      dict = @{@"gjkgnshd" : @"大逃杀"}   

   当dict为客户端自定义的字典时不会产生影响,但当dict为服务器返回数据时,轻则取不到数据,重则崩溃。

      因为:返回结果为   {@"gameName" : @"大逃杀"}       客户端取值时为  dict[@"gjkgnshd"]  key不存在而引起错误。 

上个图先看看效果:

类名混淆前:                                        类名混淆后:

 

        

 

代码混淆前:                              代码混淆后:

             

 

抱怨几句:这是我第一次做代码混淆,网上没有搜索到有关oc的混淆有实际意义的脚本或者软件,所以自己利用自己肤浅的python,勉强完成了这次混淆任务。这其中的坑有很多,相信看了这篇博客后,可以让初次尝试混淆的同行们躲过几个。话不多说,上代码

#!/usr/bin/env python
#coding=utf8

import os
import re
import random

#规避文件夹
exclude_dirs = ["JBSDKOtherFrames"]
#规避文件
exclude_files = []

#属性名
property_name_list = []
#普通属性名
normal_pro_name_list = []
#类名
class_name_list = []
#字典key值
dict_key_list = []
#方法名提取 
method_name_list = []
#随机字符串 (此处采取随机8位小写字母进行混淆,每次混淆后加入到此list,方便之后混淆查重)
define_rand_list = []
#工作目录
confound_path = "/xxx/xxx/xxx/xxx"

#获取路径下的所有需要混淆的文件
def get_all_files():
    path = confound_path
    print "获取文件夹内容"

    if not os.path.isdir(path):
        print "非文件夹路径"
        return

    #过滤相关文件夹后的文件夹
    confound_dirs = []

    for root, dirs, list in os.walk(path):
        confound_dirs.append(root)

    #过滤相关文件后的所有文件
    confound_files = []

    for item_path in confound_dirs:
        for root, dirs, list in os.walk(item_path):
            for item_file in list:
                if (os.path.splitext(item_file)[1] == '.h' or os.path.splitext(item_file)[1] == '.m' or os.path.splitext(item_file)[1] == '.mm') and  not item_file in exclude_files:

                    file_path = root + '/' + item_file
                    if not file_path in confound_files:
                        if len(exclude_dirs):

                            item_include = False

                            for exc_item in exclude_dirs:
                                if exc_item in file_path:
                                    item_include = True
                                    break

                            if item_include == False:
                                confound_files.append(file_path)

    return confound_files

#查找混淆内容 正则匹配所需混淆内容
def find_variable_name(file_path):

    with open(file_path) as file:
        lines = file.readlines()

        for line_content in lines:

            if "@property" in line_content:     #@property  变量名

                find_list = re.search("^@property\s*\(.+?\)\s*\w+\s*\*?\s*(\w+?);", line_content)

                if not find_list == None:
                    if not find_list.group(1) == None:
                            # print '属性名',find_list.group(1)
                            if not find_list.group(1) in property_name_list:
                                property_name_list.append(find_list.group(1))

            elif '@interface' in line_content:      #类名 @interface JBSDKPopOption : UIView
                find_list = re.search("^@interface\s+(\w+?)\s*:\s*\w+$", line_content)
                if not find_list == None:
                    if not find_list.group(1) == None:
                        # print '类名',find_list.group(1)
                        if not find_list.group(1) in class_name_list:
                            class_name_list.append(find_list.group(1))

            else:

                #普通属性  UIImageView *arrowView;
                find_list = re.search("^\s*(\w+?)\s*\*\s*(\w+?);$", line_content)
                if not find_list == None:
                    if not find_list.group(1) == None and not find_list.group(2) == None:
                        if not find_list.group(1) == 'return':
                            normal_pro_name = find_list.group(2)
                            if normal_pro_name[0] == '_':
                                normal_pro_name = normal_pro_name.replace('_','')

                            if not normal_pro_name in normal_pro_name_list:
                                normal_pro_name_list.append(normal_pro_name)

                #查字典key值
                find_list = re.search("@\"([\w\d]+?)\"", line_content)
                if not find_list == None:
                    if not find_list.group(1) == None:
                        if not find_list.group(1) in dict_key_list:
                            dict_key_list.append(find_list.group(1))

                #方法名 无参数或一个参数 - (void)JBSDKLoginCallBack;
                find_list = re.search("^\s*-\s*\(\w+?\)\s*(\w+)", line_content)
                if not find_list == None:
                    if not find_list.group(1) == None:
                        if not find_list.group(1) in method_name_list:
                            method_name_list.append(find_list.group(1))

                # 方法名 两个参数 - (void)JBSDKLoginCallBack:(BOOL)loginState uid:(NSString *)uid token:(NSString *)token;
                find_list = re.search("^\s*-\s*\(.+?\)\s*\w+?\s*:\s*\(\w+?\)\s*\w+?\s+?(\w+?):\(.*?\)\s*\w+?\s*[;{]$", line_content)
                if not find_list == None:
                    if not find_list.group(1) == None:

                        if not find_list.group(1) in method_name_list:
                            method_name_list.append(find_list.group(1))

                #换行后的方法名
                # + (void)phoneRegister:(NSString *)phoneNum
                # password:(NSString *)password
                # code:(NSString *)code
                find_list = re.search("^\s*(\w+?)\s*:\s*\(.+?\)\s*\w+\s*;?$",line_content)
                if not find_list == None:
                    if not find_list.group(1) == None:

                        if not find_list.group(1) in method_name_list:
                            method_name_list.append(find_list.group(1))
                            
#将参数写入宏文件
def writeDefineFile():

    write_file_path = 'xxx/xxx/xxx/xxx/ConfuseDefine.h'
    define_ok_list = []

  #一下混淆过滤内容除系统外,只略微写几个(意思一下)。针对不同工程需自行调整
#系统方法 过滤系统方法名(需要根据自己的工程添加) ex_clude_list = ['allocWithZone','copyWithZone','dealloc','viewDidLoad','shouldAutorotate','supportedInterfaceOrientations','preferredInterfaceOrientationForPresentation', 'didReceiveMemoryWarning','prefersStatusBarHidden','viewDidAppear','textFieldShouldReturn','touchesBegan','viewWillAppear','viewWillDisappear','alertView', 'tableView','initWithStyle','reuseIdentifier','numberOfSectionsInTableView','layoutSubviews','setSelected','animated','setValue','numberOfComponentsInPickerView', 'layout','initWithFrame','init','textFieldWillEditing','webViewDidFinishLoad','image','show','webView','webViewDidStartLoad','length','charset','srcLen', 'destBytes','destLen','textViewShouldBeginEditing','option_setupPopOption','_setupParams','_tapGesturePressed','JSONObject','password','description','pickView', 'pickerView','state','array','rightView','leftViewRectForBounds','rightViewRectForBounds','textRectForBounds'] #自定义方法 (因为我的工程是SDK,所以暴露的方法不能混淆,需要过滤掉) ex_clude_list += ['xxx','xxx','xxx','xxx','xxx'] #变量 (有些自定义变量和系统变量冲突需要过滤,例如:UIButton 的 titleLabel 我们自定义时很可能会出现同名的变量,当你在设置UIButton.titleLabel时会报错,因为titleLabel已经被混淆掉了) ex_clude_list += ['imageView','titleLabel'] #私有变量 (同上) ex_clude_list += ['font','leftView','error','scrollView','label'] #类名 (SDK对外暴露的类名不能混淆,需过滤掉。非SDK应该可以忽略掉) ex_clude_list += ['xxx','xxx','xxx','xxx','xxx'] if os.path.exists(write_file_path): os.remove(write_file_path) with open(write_file_path, 'w+') as define_file: #property 变量 for property_name in property_name_list: if not property_name in ex_clude_list and not property_name in dict_key_list and not property_name in define_ok_list: print "混淆property 变量 == ", property_name define_ok_list.append(property_name) define_ok_list.append('_' + property_name) rand_name = randString() define_content = "# ifndef " + property_name + "\n" + "# define " + property_name + " " + rand_name + "\n" + "# endif" + "\n" define_content += "# ifndef " + '_' + property_name + "\n" + "# define " + '_' + property_name + ' ' + "_" + rand_name + "\n" + "# endif" + "\n" property_name = uperFirstString(property_name) rand_name = uperFirstString(rand_name) define_ok_list.append('set' + property_name) define_content += "# ifndef " + 'set' + property_name + "\n" + "# define " + 'set' + property_name + " " + 'set' + rand_name + "\n" + "# endif" + "\n\r" define_file.write(define_content) #私有变量 for private_name in normal_pro_name_list: if not private_name in ex_clude_list and not private_name in dict_key_list and not private_name in define_ok_list: print "私有变量 == ", private_name define_ok_list.append(private_name) define_content = "# ifndef " + private_name + "\n" + "# define " + private_name + " " + randString() + "\n" + "# endif" + "\n\r" define_file.write(define_content) #类名 for class_name in class_name_list: if not class_name in ex_clude_list and not class_name in dict_key_list and not class_name in define_ok_list: print "类名 == ", class_name define_ok_list.append(class_name) rand_name = randString() define_content = "# ifndef " + class_name + "\n" + "# define " + class_name + " " + rand_name + "\n" + "# endif" + "\n\r" define_content += "# ifndef " + '_' + class_name + "\n" + "# define " + '_' + class_name + ' ' + "_" + rand_name + "\n" + "# endif" + "\n\r" define_file.write(define_content) # 方法名 for method_name in method_name_list: if not method_name in ex_clude_list and not method_name in dict_key_list and not method_name in define_ok_list: print "混淆方法 == ", method_name define_content = "# ifndef " + method_name + "\n" + "# define " + method_name + " " + randString() + "\n" + "# endif" + "\n\r" define_file.write(define_content) #随机字符串(此处随机8位字母,根据自身需求进行调整) def randString(): rand_list = ['a','b','c','d','e','f','g','h','i','z','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'] ran_str = ''.join(random.sample(rand_list, 8)) while ran_str in define_rand_list: ran_str = ''.join(random.sample(rand_list, 8)) define_rand_list.append(ran_str) return ran_str #首字母转大写 (公开变量混淆 set 方法时,变量首字母需大写) def uperFirstString(up_string): first_zm = up_string[0] up_string = first_zm.upper() + up_string[1:] return up_string #.m文件混淆 (.m文件重命名为无意义字符,因为翻遍后可以清楚看到.m文件的名字) 注意:.h 文件无需重命名,.m 文件什么名字无所谓,只要其中引入对应的.h文件,就不会产生任何影响(只是多了一很多文件名不匹配的警告) def confoundMFile(): all_m_files = get_all_files() new_file_index = 1 old_name_list = [] new_name_list = [] file_count = 0 for m_file in all_m_files: if os.path.splitext(m_file)[1] == '.m' or os.path.splitext(m_file)[1] == '.mm': if not 'Test' in m_file: file_count += 1 old_file_name = re.search("/(\w+?\+?\w+?)\.m{1,2}$", m_file) if not old_file_name == None: old_re_name = old_file_name.group(1) +'.m' new_name = "aaaaaaaa" + str(new_file_index) +'.m' old_name_list.append(old_re_name) new_name_list.append(new_name) new_file_name = m_file.replace(old_re_name, new_name) try: os.rename(os.path.join(m_file), os.path.join(new_file_name)) except: print '重命名失败',m_file print new_file_name print old_re_name print new_name new_file_index += 1 else: print '正则未匹配',m_file #修改配置文件 (当文件重命名后,打开工程时,.m文件会变红,指配置文件中的文件不存在,所以需要根据重命名后的文件名对配置文件进行修改) find_file = False config_file = '' for root, dirs, files in os.walk(confound_path): for dir_item in dirs: if dir_item == 'JBSDK.xcodeproj': value_dir = os.path.join(root, dir_item) config_file = value_dir + '/project.pbxproj' find_file = True break if find_file == True: break with open(config_file) as config_content_file: config_content = config_content_file.read() for index, old_name in enumerate(old_name_list): print 'old_name',old_name print 'new_name',new_name_list[index] config_content = config_content.replace(old_name, new_name_list[index]) file_object = open(config_file, 'w') file_object.write(config_content) file_object.close() if __name__ == "__main__": file_list = get_all_files() for path in file_list: # print path find_variable_name(path) writeDefineFile() # print '属性名' # print property_name_list # print len(property_name_list) # # print '私有属性名' # print normal_pro_name_list # print len(normal_pro_name_list) # # print '类名' # print class_name_list # print len(class_name_list) # # print '字典key值' # print dict_key_list # print len(dict_key_list) # # print '方法名' # print method_name_list # print len(method_name_list) confoundMFile()

总结:此次混淆最大的收获居然是 “正则表达式” ,如果你也想学正则无从下手时,你也可以试试,定会有所收获。

 

posted @ 2017-12-05 11:41  zzlei  阅读(1984)  评论(0编辑  收藏  举报