Evasi0n Jailbreak: Precisions on Stage 3

The new jailbreak for iOS 6.1, named Evasi0n, is detailed in this article, thanks to Braden Thomas. A few more details are needed regarding the very end on the page signing cancelation. Actually, in order to evade code signed verification, the amfi.dylib is cleverly structured. The idea is as follow: force the MISValidateSignature (which returns the status of code signing verification) to always return 0. That way, one will be able to execute any unsigned code. Hence, this raises 2 questions: How to force MISValidateSignature to always return 0? How to pass the code signed verification for amfy.dylib?

Override MISValidateSignature

For the first time, evasi0n uses the re-export mechanism via a LINKEDIT segment. It is the equivalent of Export Forwarding for PE files. This is a mechanism to override a function with an other one (possibly in an other library).

It is important to keep in mind that code signing verification is not yet disabled. Thus, the library can not embed any code, or it would be rejected. That is why the usual OS X interposition with DYLD_INSERT_LIBRARIES can not be used for instance. In order to interpose a function with this mechanism, some code needs to be compiled and contained in the interposing library.

We can extract this information thanks to dyldinfo tools.

$>dyldinfo -export amfi.dylib
export information (from trie):
[re-export] _kMISValidationOptionValidateSignatureOnly (_kCFUserNotificationTokenKey from CoreFoundation)
[re-export] _kMISValidationOptionExpectedHash (_kCFUserNotificationTimeoutKey from CoreFoundation)
[re-export] _MISValidateSignature (_CFEqual from CoreFoundation)

For instance, the last line means: "Use CoreFoundation._CFEqual instead of _MISValidateSignature" (Boolean CFEqual(CFTypeRef cf&, CFTypeRef cf2)). That way, there is absolutely no code to execute. The whole job is done thanks to this aforementioned mechanism.

How to reproduce this mechanism

There are at least 3 ways to reproduce the behavior of this mechanism: 1. Use a bundle 2. Hook the function call thanks to Mobile Substrate or Obj-C dedicated methods 3. Write some code (extern and function call)

But one way of reproducing this tricks and getting the same structure than amfi.dylib is to use:

$ gcc -dynamiclib amfi.c -o amfi.dylib -Wl,-alias,_CFEqual,_MISValidateSignature -nostdlib -framework CoreFoundation,CFBase -install_name /usr/lib/libmiss.dylib -lmiss

Does it works? We can check the expected output:


$ dyldinfo -export amfi.dylib
export information (from trie):
[re-export] _MISValidateSignature (_CFEqual from CoreFoundation)

Libraries loaded:

$ otool -L amfi.dylib
/usr/lib/libmis.dylib (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation (compatibility version 65535.255.255, current version 0.0.0)

Correct structure:

$ otool -lv amfi.dylib
cmdsize 48
name /usr/lib/libmis.dylib (offset 24)
Load command 7
cmdsize 92
name /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation (offset 24)

So, why does it works?

  • -dynamiclib: specifies we want a dylib
  • -framework CoreFoundation,CFBase -lmiss: includes needed library (libmiss) and framework (CFBase of CoreFoundation specified here, exporting CFEqual)
  • -install_name /usr/lib/libmiss.dylib: specifies the segment LC_ID_DYLIB
  • -Wl,-alias,_CFEqual,_MISValidateSignature: asks to re-export the symbol _CFEqual (needs to be known, here thanks to CoreFoundation) as _MISValidateSignature
  • -nostdlib: limits useless import

Passing code signing verification

However, CoreFoundation Framework needs to be loaded by a LC_LOAD_DYLIB command (set the source library) and libmiss.dylib (MISValidateSignature* and co.) by a LC_ID_DYLIB command (set the target library). Unfortunately, these load commands have to be in an executable segment.

So, the trick is as follow:

  1. Mark the load part as executable in a FAKE_TEXT segment. It will be mapped in memory at [0x0000 - 0x1000] with r-x permissions.
segname FAKE_TEXT
vmaddr 0x00000000
vmsize 0x00001000
fileoff 0
maxprot r-x
initprot r-x
  1. Do the LINKEDIT job (read-only part).
segname LINKEDIT
vmaddr 0x00001000
vmsize 0x00001000
fileoff 4096
maxprot r--
initprot r--
  1. Erase the memory at [0x000 - 0x1000] with the same content, but without execution right thanks to a TEXT segment.
segname TEXT
vmaddr 0x00000000
vmsize 0x00001000
fileoff 8192
maxprot r--
initprot r--

That way, dyld checks that "All Mach-O commands are in an executable segment" and the code signing verification mechanism stays quiet because there is no executable segment in the final mapping.

Such information can be retrieved with otool -lv amfi.dylib.