iOS代碼混淆

目前公司產(chǎn)品線中存在大量功能類似的APP,按照模塊化方式開發(fā)項目,核心模塊業(yè)務(wù)代碼是復(fù)用的,使用同一個開發(fā)者賬號下iOS上架流程中有些APP在蘋果機審過程中慘遭被拒的下場,通過更改部分頁面UI效果也無濟于事,于是采用代碼混淆的方式也就是馬甲包方案去繞過機審;

功能分析

  • 二進制不同,圖標,包名,工程名,代碼,靜態(tài)資源等的修改。
  • 差異化UI風(fēng)格,產(chǎn)品功能,頁面布局等的修改

實現(xiàn)流程

  • 核心模塊類名修改
  • 核心方法名修改
  • 加入垃圾代碼
  • 替換png等靜態(tài)資源MD5
  • info.plist文件添加垃圾字段

類名修改

  • 遍歷查找需要替換的核心模塊目錄 (主工程\Pods目錄)
  • 找到所有需要替換的類名(項目專用前綴),將其存放到數(shù)組中
  • 遍歷查找整個工程的所有目錄,查找所有.h、.m、.xib、.string文件,逐行掃描文件,找到需要替換的類名關(guān)鍵字替換成別的名字前綴
  • 如發(fā)現(xiàn).h、.m、.xib、.string文件的文件名包含需要替換的類名,替換之(xcodeproj工程需要重新引入文件,通過腳本動態(tài)引入)
  • 遇到有"+"號的分類文件,篩選出"+"號前面的類名然后替換之
#遍歷查找所有.h、.m、.xib、.strings文件,逐行掃描文件,找到需要替換的類名關(guān)鍵字替換成別的名字前綴
def do_replace_file_name(dir_path,need_name_list)
    Dir.foreach(dir_path) do |file|
        if file != "." and file != ".."
            file_path = dir_path + "/" + file
            if File.directory? file_path
                do_replace_file_name(file_path,need_name_list)
            else
                if file.end_with?(".h") or file.end_with?(".m") or file.end_with?(".xib") or file.end_with?(".strings") #只查找.h .m .xib .strings文件批量修改
                    aFile = File.new(file_path, "r")
                    if aFile
                        file_content = aFile.read()
                        aFile.close

                        length = need_name_list.length - 1
                        for i in 0..length do

                            need_name = need_name_list[i]
                            file_content = split_file_content(file_content,need_name)

                        end

                        aFile = File.new(file_path, "w")
                        aFile.syswrite(file_content)
                        aFile.rewind
                        
                    end
                end

                #如.h、.m、.xib、.string文件的文件名包含需要替換的類名,替換之
                if file.include?".h" or file.include?".m" or file.include?".xib" or file.include?".strings"
                    file_suffix = file.split(".")[1]
                    need_name_list.each { |need_name|
                        need_file_name = need_name + "." + file_suffix
                        if file.start_with?(need_file_name)

                            new_file_name = new_file_name(file)
                            
                            new_file_path = dir_path + "/" + new_file_name
                            File.rename(file_path, new_file_path) #文件名稱替換
                        end
                    }

                end

            end
        end
    end
end

方法名修改

  • 獲取系統(tǒng)文件關(guān)鍵字并緩存,主要是獲取iOS SDK中Frameworks所有方法名和參數(shù)名作為忽略關(guān)鍵字
  • 遍歷查找整個工程的所有.h、.m、.mm文件,提取關(guān)鍵字,主要提取方法名和參數(shù)名
  • 將系統(tǒng)關(guān)鍵字、IBAction方法的關(guān)鍵字、屬性property的關(guān)鍵字(防止懶加載方法名造成沖突)去除
  • 將剩余的關(guān)鍵字進行方法混淆,混淆方案是將名字用#define宏定義方式替換名稱,方法不能替換成隨機字符串,這樣任然不能通過機審,應(yīng)替換成規(guī)律的單詞拼接方法名
  • 將替換后方法名關(guān)鍵字宏名稱寫入到全局pch文件,xcodeproj動態(tài)引入
    # 生成混淆文件
    @staticmethod
    def create_confuse_file(output_file, confused_dict):
        log_info("Start creating confuse file, file fullpath is {0}".format(os.path.realpath(output_file)), 2, True)
        f = open(output_file, 'wb')
        f.write(bytes('#ifndef NEED_CONFUSE_h\n', encoding='utf-8'))
        f.write(bytes('#define NEED_CONFUSE_h\n', encoding='utf-8'))
        f.write(bytes('// 生成時間: {0}\n'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S')), encoding='utf-8'))
        for (key, value) in confused_dict.items():
            f.write(bytes('#define {0} {1}\n'.format(key, value), encoding='utf-8'))
        f.write(bytes('#endif', encoding='utf-8'))
        f.close()

生成垃圾代碼

  • 遍歷查找整個工程的所有.m、.mm文件
  • 為避免和混淆后的方法重名,添加垃圾方法的時候使用 隨機前綴 + "_" + 規(guī)律單詞 作為方法名,隨意在方法中添加日志代碼
  • 在文件結(jié)尾@end前插入這些方法
#oc代碼以@end結(jié)尾,在其前面添加text
def appendTextToOCFile(file_path, text):
    with open(file_path, "r") as fileObj:
        old_text = fileObj.read()
        fileObj.close()
        end_mark_index = old_text.rfind("@end")
        if end_mark_index == -1:
            print "\t非法的結(jié)尾格式: " + file_path
            return
        new_text = old_text[:end_mark_index]
        new_text = new_text + text + "\n"
        new_text = new_text + old_text[end_mark_index:]

    with open(file_path, "w") as fileObj:
        fileObj.write(new_text)

#處理單個OC文件,添加垃圾函數(shù)。確保其對應(yīng)頭文件存在于相同目錄
def dealWithOCFile(filename, file_path):
    global target_ios_folder,create_func_min,create_func_max,funcname_set
    funcname_set.clear()
    end_index = file_path.rfind(".")
    pre_name = file_path[:end_index]
    header_path = pre_name + ".h"
    if not os.path.exists(header_path):
        print "\t相應(yīng)頭文件不存在:" + file_path
        return

    new_func_num = random.randint(create_func_min, create_func_max)
    print "\t給%s添加%d個方法" %(filename, new_func_num)

    prefix_list = ['btt_', 'gym_', 'muut_', 'ora_', 'vend_', 'enyt_', 'qotb_', 'ldt_', 'zndy_', 'tim_', 'yar_', 'toa_', 'rewwy_', 'twof_', 'theg_', 'guis_', 'dui_' ]

    random_index_list = random.sample(range(0,new_func_num), new_func_num)

    for i in range(new_func_num):
        
        prefix = prefix_list[random_index_list[i]]
        header_text = getOCHeaderFuncText(prefix)
        # print "add %s to %s" %(header_text, header_path.replace(target_ios_folder, ""))
        appendTextToOCFile(header_path, header_text + ";\n")
        funcText = getOCFuncText(header_text)
        appendTextToOCFile(file_path, funcText)

替換png等靜態(tài)資源MD5

        if file_type == ".png":
            text = "".join(random.sample(string.ascii_letters, 11))
        elif file_type == ".jpg":
            text = "".join(random.sample(string.ascii_letters, 20))
        elif file_type == ".lua":
            text = "\n--#*" + "".join(random.sample(string.ascii_letters, 10)) + "*#--"
        else:
            text = " "*random.randint(1, 100)
        fileObj.write(text)
        fileObj.close()

info.plist文件添加垃圾字段

在info.plist中插入規(guī)律英文單詞(已排除系統(tǒng)專用字段),值為隨機字符串

def addPlistField(plist_file):
    
    global create_field_min,create_field_max,word_name_list
    create_field_num = random.randint(create_field_min, create_field_max)
    random_index_list = random.sample(word_name_list, create_field_num)

    tree = ET.parse(plist_file)
    root = tree.getroot()

    root_dict = root.find("dict")

    for i in range(create_field_num):

        key_node = ET.SubElement(root_dict,"key")
        key_node.text = random_index_list[i]

        string_node = ET.SubElement(root_dict,"string")
        string_node.text = getOneName()

    tree.write(plist_file,"UTF-8")

混淆前后對比

代碼混淆前



Hopper查看混淆前



代碼混淆后

Hopper查看混淆后


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 對于IOS來說,由于系統(tǒng)是封閉的,APP上架需要通過App Store,安全性來說相當高。但是對于大廠和知名APP...
    it彭于晏閱讀 1,216評論 0 1
  • iOS代碼混淆 本次更新V1.3.5(2019年11月30日) 1.新增忽略有相同前綴或后綴類名的功能; 2.新增...
    ZFJ_張福杰閱讀 5,430評論 1 6
  • 馬甲小助手是什么? 馬甲小助手是一個為開發(fā)者提供方便制作和分析馬甲包的工具.目前處于測試和完善階段,免費開放給廣大...
    馬甲小助手閱讀 985評論 0 1
  • app風(fēng)靡的時代,總有一些奇葩的需求。為了刷量,刷排名,制作殼包,為了通過蘋果爸爸審核,想到代碼混淆,垃圾代碼等策...
    二斤寂寞閱讀 23,134評論 11 52
  • 目的 為了進一步增加應(yīng)用的安全性,防止我們的應(yīng)用程序很容易的被攻擊者分析、破解、重打包,提高攻擊者逆向分析應(yīng)用的難...
    miracle洛洛閱讀 4,454評論 3 21

友情鏈接更多精彩內(nèi)容