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()

總結:此次混淆最大的收獲居然是 “正則表達式” ,如果你也想學正則無從下手時,你也可以試試,定會有所收獲。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM