CryptedHelloWorld: App with encrypted mach-o sections

A constructor attribute could be used to implement a software protection. You could encrypt your executable with a custom encryption and use a constructor function to decrypt the binary just before it is loaded.

The goal is to encrypt the data of the __text section from the __TEXT segment. This is the section containing the code of the executable. The other sections and segments will be left untouched.

The target application: CryptedHelloWorld

The target application is called CryptedHelloWorld. Its (__TEXT, __text) section is encrypted, meaning that the main() function needs to be decrypted before running. When launched, a constructor function will decrypt the encrypted section and the decrypted main() will be called.

The application itself is a simple command line ‘Hello World’ written in C. Here is the source code of the main function:

CryptoTool

When building the project with Xcode, the target dependency CryptoTool is built. At the end of the compilation of the CryptedHelloWorld app, a Run Script phase is executed which runs CryptoTool on the just compiled CryptedHelloWorld binary. CryptoTool:

reads the binary of the CryptedHelloWorld application from the disk

locates the (__TEXT, __text) section in the file

encrypts it using AES 128

replaces the bytes with the encrypted bytes in the binary on the disk.

The Run Script phase is straightforward:

Location of the constructor function

When you launch the application, the constructor function will be triggered and will need to decrypt the (__TEXT, __text) section. Obviously this constructor function can't be located in the (__TEXT, __text) section. I store it in a custom (__TEXT,__timac) section using the attribute __attribute__((section("__TEXT,__timac"))):

The attribute __attribute__((always_inline)) ensures that the function is inlined even for debug builds when optimizations are turned off.

Locating the (__TEXT, __text) section

The constructor function uses the _dyld_get_image_header() dyld function to get the Mach-O header. It then loops though all the commands and all the sections of the LC_SEGMENT_64 segments to find the (__TEXT, __text) section:

Encryption

The section is encrypted using AES 128 by chunks of PAGE_SIZE bytes (4096 bytes). If there are less than PAGE_SIZE bytes to encrypt, chunks of 16 bytes are used. I use the CommonCrypto implementation of AES 128:

Examining the compiled application

Using MachOView, we see that the (__TEXT, __text) section is encrypted:

This is confirmed with Hopper. The main() function doesn't make any sense:

Back to MachOView, we see that the (__TEXT, __timac) section contains unencrypted code:

Limitations of this proof of concept

As mentioned this example is a proof of concept and has several limitations:

it only supports 64-bit Mach-O files. Adding 32-bit and fat Mach-O support is fairly simple and left to the reader.

only the (__TEXT, __text) section is encrypted. It is possible to encrypt other sections or maybe even the whole __TEXT segment.

the target application is a really simple command line application written in C.

As mentioned earlier all the functions required in the constructor function have been made inlined. This makes it difficult to debug. If you want to debug this code I recommend to debug the CryptoTool app. It supports the parameters -decrypt and -encrypt. You should also remove the always_inline attribute otherwise breakpoints won't fire as you would expect.

Should you use such code to protect your application?

I wouldn't. Although this might prevent a user to look at the code, this won't defeat an experienced attacker. Also note that this is a proof of concept.