A detailed explanation of sourceless iOS hardening

Mondo Technology Updated on 2024-01-29

Reading guide:In mobile security,The security of the client layer is very important,Because mobile applications are usually executed on client devices,And process sensitive user data。 By hardening your application, you can effectively defend against reverse engineering and malicious attacks, and protect your application's confidential information and intellectual property. Hardening provides a strong layer of security for applications by means of obfuscation, encryption protection, and anti-debugging, making it more difficult for attackers to crack and modify applications.

1. iOS reinforcement scheme

At present, there are three mainstream reinforcement schemes:

Source code reinforcement: Directly based on source code engineering for confusion, this mode can generally only deploy hardening tools in the developer environment, and some additional environment configuration is required.

Bitcode reinforcement: Because BitCode is essentially the middle of the IPA compilation process, its reinforcement principle is not much different from the source code, the main difference is in the docking mode, by uploading the package with BitCode, the reinforcement process can be carried out in the reinforcement vendor, reducing the cost of docking and environment deployment.

Sourceless reinforcement: Reinforcement based on IPA packages, because it acts on binaries, the functional control is not as flexible as the source code, but the access cost is low.

After the release of the new Xcode 15, the BitCode generation switch has been officially removed.

As a result, iOS client hardening may end up with only two options: source code hardening and no source code. Since the source code reinforcement scheme is open source and has a reference basis in principle, I will not talk too much about it here, and the following is a brief introduction to some principles of source code reinforcement.

2. iOS no source code reinforcement

No source code hardening for any platform can unshell the file format, and iOS is no exception. Like the ELF file under Android, the executable file for iOS is a type of Macho format file. From the perspective of program loading and operation, macho and elf have the following similarities and differences.

Similarities: Both use segments to describe memory loading scopes and permissions, and use sections to divide ** and data in more detail.

Both contain symbol tables and string tables to record functions and variable information.

Both have the concept of repositioning.

Differences: The iOS section has a clearer meaning, especially for OBJC and SWIFT-related information.

iOS supports lazy loading.

iOS has a more rigorous categorization and ordering of symbols.

Load Command in iOS is more similar to the combination of dynamic and PHDR in ELF and is a description of the program's structure and dependencies.

To understand how a platform program is hardened, you first need to understand the process of loading and executing the program.

Load. The loading process includes memory mapping, memory repair, and initialization processes.

From the perspective of file content, macho contains three major data structures.

Macho Header: Contains the basic information of the mach-o file, such as file type, CPU type, number of load commands, etc.

load command: Each load command contains a header and data. The header contains the type, size, and other relevant information of the loading command.

segment: Used to describe the data information required by the segment, data segment, and loader in the mach-o file.

All the data associated with the load command can be read by parsing the command structure, and the data must be within the above data range. The entire macho file size is equal to the sum of the above three data sizes.

There are some conventions in the compilation process of the program, and the call of some self-implemented variables or functions uses a relative address, which can be simply understood as an offset from the first address loaded by the program, rather than the real address. However, when a program is loaded into memory, the first address is usually random, and these addresses need to be fixed before running, a process called rebase.

In the process of program development, if you use functions or variables in the dynamic library, the generated binary products will identify these functions or variables that need to be imported, and reserve the function address in an address area (got table). The process of dynamic linking is to fix the function address or variable value in these locations, and this process is called bind.

The program loading process can be easily understood and can be divided into the following three processes.

Allocate data memory and permissions based on the address set by the segment.

According to the rules in rebase, the relative address generated by the compilation is repaired and converted to the real address.

According to the rules in bind, fix the addresses of other functions and variables that the compilation depends on to ensure that the program can be called normally.

Due to the iteration of the iOS version, the description of rebase and bind and the way the data is stored vary from version to version.

Under iOS 14, you can rebase and bind operations by parsing lc dyld info or lc dyld info only.

Both the rebase and bind operations are parsed according to a set of opcode customized by ios, and you can refer to the dyld source code to understand the processing process of opcode.

The rebase performs a specific operation through a series of opcodes, and the result is to ensure that the internal address associated with the program can be fixed when the app is loaded and the memory base address is randomized.

bind uses a different set of encoding, and the bind process is used to bind symbols, such as when the program uses variable functions in other libraries, and the address information needs to be written into the program memory to ensure normal access and use of these functions and variables.

iOS 14 and above are compatible with formats below 14, but new formats (Fixup Chains) are provided to complete dynamic linking. When compiling the app, if you select only support iOS 14 or later, there is no longer LC Dyld Info or LC Dyld Info Only in the Macho file, and Dyld Chained Fixups and Dyld Exports Trie are replaced by Dyld Chained Fixups and Dyld Exports Trie.

Dyld chained fixups correspond to older versions of bind and rebase, where the lazy bind mechanism is removed, but there is still a distinction between bind and weak bind.

Dyld Exports Trie corresponds to the export symbol of the old version.

The data diagram of dyld chained fixups is as follows:

The above iOS redefines a set of methods for relocating data, and there are two key functions of Fixup Chains:

Initialize the import table, which is a linked list or array containing symbol information and lib information, pointing to the ** of the symbol, and its array index is associated with bind data.

Guide dyld to find the dyld chained starts in segment data, and calculate the address in the data that really needs to be fixed, which is usually in the got table or some referenced local variables.

In the data section, the initialization method is different from the previous version.

In the old format, you can specify which addresses need to be rebased and which addresses need to be bind. Of the addresses you really want to fix, the default value may be 0 or a relative address.

The new version of the format fixupchains only obtains the first address of the data, and the data needs to be parsed in the form of a linked list through the structure of chainedfixuppointerondisk, and the corresponding repair operations are done according to the structure type.

The new version of macho adds complexity to the format, but in programs where there is a lot of relocation, the volume of data can be reduced very little.

Execute. The execution process involves some interfaces that are executed by default within the program. Here we focus more on the behavior of the main function before it starts.

Load and register methods in classes and class extensions.

Call the +load function of objc.

Execute functions in mod init func, which include, but are not limited to, the functions declared in the following rules.

C function declared as attribute (constructor)).

An object that has been initialized in a global variable.

Execute the main function.

The purpose of paying attention to the startup process is to initialize the reinforcement-related functions at the right time when doing reinforcement, such as initializing in load, or inserting init to perform initialization. You need to ensure that the initialization time is before the hardening function takes effect.

Scenarios and effects.

After confirming the loading and execution process of the program, we can further perform operations such as data encryption, obfuscation transformation, and functional confrontation on the program to achieve the required protection effect. Binary cryptographic obfuscation is almost always based on assembly and file processing, which is more difficult than the source code, but if it is handled properly, its analysis difficulty is no worse than that of source code reinforcement, on the contrary, it will be easier to modify the features because of the flexibility of assembly and file processing.

Here's one of the protective effects:

From the effect point of view, unlike ordinary confusion, there is a layer of extraction on the surface, and the real ** secondary is confused, not only can not analyze the function body, but even the parameter type can not be parsed normally.

III. Conclusion

In this article, we've detailed how some iOS application hardening works. I hope to help some friends who need to understand reinforcement to understand some knowledge of the principle. In today's digital age, application security is paramount. In order to ensure the security of user data and business, we continue to improve our iOS hardening products to provide developers with comprehensive application protection solutions.

If you are interested in our reinforcement products or want to know more, please visit NetEase Shield**. We will be happy to provide you with customized solutions to meet your security needs.

Related Pages