At present, there are a large number of apps with similar functions in the company’s product line. The project is developed in a modular way, and the core module business code is reused. Some apps in the iOS launch process using the same developer account were rejected in the process of MAC review, and it is useless to change the UI effect of some pages. So the use of code confusion is the majia package scheme to bypass the machine audit;

Functional analysis

  • Binary differences, ICONS, package names, project names, code, static resources, etc.
  • Differentiated UI style, product features, page layout, etc

The implementation process

  • Core module class name changed
  • Core method name changed
  • Add junk code
  • Replace static resources MD5, such as PNG
  • Add garbage fields to the info.plist file

The name of the class to modify

  • Traverse to find the core module directory to replace (main project \Pods directory)
  • Find all the class names (project-specific prefixes) that need to be replaced and place them in an array
  • Go through and find all the directories in the whole project, find all the.h,.m,.xib,.string files, scan the files line by line, find the class names that need to be replaced and replace the keywords with other name prefixes
  • If the file names of.h,.m,.xib, and.string files contain the names of the classes to be replaced, replace them (XcodeProJ project needs to reimport the files and dynamically import them through scripts).
  • If you find a classification file with a “+” sign, select the class name before the “+” sign and replace it
# go through all the. H,. M,. Xib,. Strings files, scan the files line by line, find the keyword of the class name that needs to be replaced with the prefix of the other name
def do_replace_file_name(dir_path,need_name_list)
	Dir.foreach(dir_path) do |file|
		iffile ! ="." andfile ! =".."
			file_path = dir_path + "/" + file
		    if File.directory? file_path
		    	do_replace_file_name(file_path,need_name_list)
		    else
		    	iffile.end_with? (".h") orfile.end_with? (".m") orfile.end_with? (".xib") orfile.end_with? (".strings") H. M. xib. Strings file
					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 file names contain the class names to be replaced
		    	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
   						iffile.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) # File name replacement
   						end
   					}

		    	end

		    end
		end
	end
end

Copy the code

Method name modification

  • Get system file keywords and cache, mainly to get all method names and parameter names in iOS SDK Frameworks as ignore keywords
  • Traverse to find all the. H,. M,. Mm files of the whole project, extract keywords, main extraction method name and parameter name
  • Remove system keywords, IBAction method keywords, and property keywords (to prevent collisions caused by lazy loading method names)
  • The method of confusion is to replace the name with #define macro definition. The method cannot be replaced with random string, which still cannot pass the machine audit. Instead, it should be replaced with regular word splicing method name
  • Replace the rear method name keyword macro name to the global PCH file, xcodeProj dynamic introduction
    Generate obfuscation files
    @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('// generate time: {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()
Copy the code

Generating junk code

  • Traverse to find all.m,.mm files in the entire project
  • To avoid having the same name as the obtruded method, use random prefix + “_” + regular word as method name when adding garbage method, and add logging code freely to method
  • Insert these methods at the end of the file at @end
#oc code ends at @end, preceded by 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 Illegal closing format:" + 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)

# Handle single OC file, add garbage function. Make sure the corresponding header file exists in the same directory
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 Corresponding header file does not exist:" + file_path
        return

    new_func_num = random.randint(create_func_min, create_func_max)
    print "\t add %d methods to %s" %(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)
Copy the code

Replace static resources MD5, such as PNG

        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()
Copy the code

Add garbage fields to the info.plist file

Insert regular English words (excluding system-specific fields) as random strings in info.plist

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")
Copy the code

At present, there are a large number of apps with similar functions in the company’s product line. The project is developed in a modular way, and the core module business code is reused. Some apps in the iOS launch process using the same developer account were rejected in the process of MAC review, and it is useless to change the UI effect of some pages. So the use of code confusion is the majia package scheme to bypass the machine audit;

Functional analysis

  • Binary differences, ICONS, package names, project names, code, static resources, etc.
  • Differentiated UI style, product features, page layout, etc

The implementation process

  • Core module class name changed
  • Core method name changed
  • Add junk code
  • Replace static resources MD5, such as PNG
  • Add garbage fields to the info.plist file

The name of the class to modify

  • Traverse to find the core module directory to replace (main project \Pods directory)
  • Find all the class names (project-specific prefixes) that need to be replaced and place them in an array
  • Go through and find all the directories in the whole project, find all the.h,.m,.xib,.string files, scan the files line by line, find the class names that need to be replaced and replace the keywords with other name prefixes
  • If the file names of.h,.m,.xib, and.string files contain the names of the classes to be replaced, replace them (XcodeProJ project needs to reimport the files and dynamically import them through scripts).
  • If you find a classification file with a “+” sign, select the class name before the “+” sign and replace it
# go through all the. H,. M,. Xib,. Strings files, scan the files line by line, find the keyword of the class name that needs to be replaced with the prefix of the other name
def do_replace_file_name(dir_path,need_name_list)
	Dir.foreach(dir_path) do |file|
		iffile ! ="." andfile ! =".."
			file_path = dir_path + "/" + file
		    if File.directory? file_path
		    	do_replace_file_name(file_path,need_name_list)
		    else
		    	iffile.end_with? (".h") orfile.end_with? (".m") orfile.end_with? (".xib") orfile.end_with? (".strings") H. M. xib. Strings file
					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 file names contain the class names to be replaced
		    	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
   						iffile.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) # File name replacement
   						end
   					}

		    	end

		    end
		end
	end
end

Copy the code

Method name modification

  • Get system file keywords and cache, mainly to get all method names and parameter names in iOS SDK Frameworks as ignore keywords
  • Traverse to find all the. H,. M,. Mm files of the whole project, extract keywords, main extraction method name and parameter name
  • Remove system keywords, IBAction method keywords, and property keywords (to prevent collisions caused by lazy loading method names)
  • The method of confusion is to replace the name with #define macro definition. The method cannot be replaced with random string, which still cannot pass the machine audit. Instead, it should be replaced with regular word splicing method name
  • Replace the rear method name keyword macro name to the global PCH file, xcodeProj dynamic introduction
    Generate obfuscation files
    @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('// generate time: {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()
Copy the code

Generating junk code

  • Traverse to find all.m,.mm files in the entire project
  • To avoid having the same name as the obtruded method, use random prefix + “_” + regular word as method name when adding garbage method, and add logging code freely to method
  • Insert these methods at the end of the file at @end
#oc code ends at @end, preceded by 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 Illegal closing format:" + 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)

# Handle single OC file, add garbage function. Make sure the corresponding header file exists in the same directory
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 Corresponding header file does not exist:" + file_path
        return

    new_func_num = random.randint(create_func_min, create_func_max)
    print "\t add %d methods to %s" %(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)
Copy the code

Replace static resources MD5, such as PNG

        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()
Copy the code

Add garbage fields to the info.plist file

Insert regular English words (excluding system-specific fields) as random strings in info.plist

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")
Copy the code

Before and after confusion

Before code confusion