Hide chapters

Advanced Apple Debugging & Reverse Engineering

Third Edition · iOS 12 · Swift 4.2 · Xcode 10

Before You Begin

Section 0: 3 chapters
Show chapters Hide chapters

Section III: Low Level

Section 3: 7 chapters
Show chapters Hide chapters

Section IV: Custom LLDB Commands

Section 4: 8 chapters
Show chapters Hide chapters

19. Mach-O Fun
Written by Derek Selander

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Hopefully you’re not too burnt out from dumping raw bytes and comparing them to structs in the previous chapter, because here comes the real fun!

To hammer in the different Mach-O section types, you’ll build a series of examples across this chapter. You’ll start with a “scanner” that looks for any insecure http: hardcoded strings loaded into the process. After that, you’ll learn how to cheat those silly, gambling or freemium games where you never win the loot.

Commence funtime meow.

Mach-O Refresher

Just so you know what’s expected of you, you’ll start with a brief refresher from the previous chapter.

Segments are groupings on disk and in memory that have the same memory protections. Segments can have zero or more sections found inside a grouping.

Sections are sub-components found in a segment. They serve a specific purpose to the program. For example, there’s a specific section for compiled code and a different section for hard-coded strings.

Sections and segments are dictated by the load commands, which are found at the beginning of the executable, immediately following the Mach-O Header

You saw a couple of the important segments in the previous chapter, notably the __TEXT, __DATA, and __LINKEDIT segments.

The Mach-O sections

Included in this chapter is an iOS project called MachOFun. Open it up and take a look around.

(lldb) image dump sections MachOFun
0x00000001 code             [0x0000000109e5f290-0x0000000109e68aa0)  r-x  0x00001290 0x00009810 0x80000400 MachOFun.__TEXT.__text
(lldb) image lookup -n "-[UIViewController viewDidLoad]"
1 match found in /Applications/
        Address: UIKitCore[0x0000000000abb3f8] (UIKitCore.__TEXT.__text + 11243624)
        Summary: UIKitCore`-[UIViewController viewDidLoad]
0x00000006 data-cstr        [0x0000000109e6a320-0x0000000109e6b658)  r-x  0x0000c320 0x00001338 0x00000002 MachOFun.__TEXT.__cstring
(lldb) memory read -r -fC -c 0x00001338 0x0000000109e6a320 

Printing unicode characters

The fun doesn’t just stop at UTF-8 strings. In addition to the __TEXT.__cstring section, there’s also the __TEXT.__ustring section which stores UTF-16 encoded strings.

(lldb) exp -l objc -- (char16_t *)0x0000010a07ead8
(char16_t *) $26 = 0x000000010a07ead8 u"💩"

Finding HTTP strings

Now that you know hardcoded UTF-8 strings are stored in the __TEXT.__cstring module, you’ll use that knowledge to search every module in the process to see if any string begins with the characters "http:"

import MachO
for i in 0..<_dyld_image_count() {
    let imagePath = _dyld_get_image_name(i)!
    let name = String(validatingUTF8: imagePath)!
    let basenameString = (name as NSString).lastPathComponent
    var module : InsecureHTTPRequestsData = (basenameString, [])
    var rawDataSize: UInt = 0
    guard let rawData = 
                               &rawDataSize) else {

    print("__TEXT.__cstring data: \(rawData), \(basenameString)")
__TEXT.__cstring data: 0x00000001025c35a0, dyld_sim
__TEXT.__cstring data: 0x000000010256c2f0, MachOFun
(lldb) image lookup -a 0x000000010256c2f0
Address: dyld_sim[0x00000000000365a0] (dyld_sim.__TEXT.__cstring + 0)
Summary: "__LINKEDIT"
(lldb) x/s 0x000000010256c2f0
0x1025c35a0: "__LINKEDIT"
var index = 0
while index < rawDataSize {
  let cur = rawData.advanced(by: index)
  let length = strlen(cur)
  index = index + length + 1
  guard let str = String(utf8String: cur),
    length > 0 else {
  if str.hasPrefix("http:") {
if module.strings.count > 0 {
let _ = ""
let _ = ""

What’s up with CFNetwork’s strings?

This oddity you can optionally explore as this is a smidge outside the context of the chapter, but CFNetwork has some amusing hardcoded strings embedded in the module.

if str.hasPrefix("http:") {
if str.contains("RubberDucky") {

Sections in the __DATA segment

Now that you’ve got your public insecure URL shaming out of the way, it’s time to shift the attention to the writeable __DATA segment and explore some interesting sections.

(lldb) image dump sections MachOFun
0x00000016 data-ptrs        [0x000000010dcae8e0-0x000000010dcae900)  rw-  0x0000f8e0 0x00000020 0x10000000 MachOFun.__DATA.__objc_classlist
(lldb) x/4gx 0x0000010dcae8e0
0x10dcae8e0: 0x000000010dcb0580 0x000000010dcb0690
0x10dcae8f0: 0x000000010dcb0758 0x000000010dcb0800
(lldb) exp -l objc -O -- 0x000000010dcb0580

(lldb) exp -l objc -O -- 0x000000010dcb0690

(lldb) exp -l objc -O -- 0x000000010dcb0758

(lldb) exp -l objc -O -- 0x000000010dcb0800

The __bss, __common, and __const sections

Sometimes a module needs to keep references to data that lives past a function call. As you’ve learned earlier, if you were to declare a constant such as let v = UIView() inside of a function, the pointer v is stored on the stack which points to allocated memory on the heap. But as soon as the instruction pointer leaves the function, the reference to the v variable is long gone. That’s why there are several sections in the __DATA segment designed to store variables across the lifetime of a process.

Cheating freemium games

The __DATA segment not only stores references to data in the module, but it also provides references to external variables, classes, methods, and functions that are not defined within the module.

(lldb) exp -l objc -O -- [[NSBundle mainBundle] executablePath]
(lldb) platform shell objdump -lazy-bind ${APP_PATH} | grep ${RANDON_NUMBER_FUNC}
platform shell objdump -lazy-bind /Users/derekselander/Library/Developer/CoreSimulator/Devices/3C657090-3EF0-4A35-B202-07A8D2C85556/data/Containers/Bundle/Application/2EB74DAB-09F7-4825-BEDE-5AB40335AE13/ | grep arc
__DATA   __la_symbol_ptr    0x10000F3F8 libSystem        _arc4random_uniform
(lldb) po (char *)_dyld_get_image_name(1)
(lldb) p/x (intptr_t)_dyld_get_image_vmaddr_slide(1) + 0x10000F3F8
(lldb) x/gx 0x000000010f91c3f8
0x10f91c3f8: 0x000000011140f027
(lldb) image lookup -a 0x000000011140f027
Address: libsystem_c.dylib[0x0000000000025027] (libsystem_c.dylib.__TEXT.__text + 144919)
Summary: libsystem_c.dylib`arc4random_uniform
(lldb) exp -l objc -p -- int lolzfunc()  { return 5; }
(lldb) p/x lolzfunc
(int (*)()) $14 = 0x000000012d4c7430 (0x000000012d4c7430)
(lldb) memory write -s8 0x000000010f91c3f8 0x000000012d4c7430

Objective-C swizzling vs function interposing

Unlike Objective-C method swizzling, lazy pointer loading occurs on a per-module basis. That means that the trick you just performed will only work when the MachOFun module calls out to arc4random_uniform. It wouldn’t work if, say, CFNetwork called out to arc4random_uniform.

(lldb) image dump sections lolz.dylib 
0x00000003 regular          [0x000000010b8bc000-0x000000010b8bc010)  rw-  0x00001000 0x00000010 0x00000000 lolz.dylib.__DATA.__interpose

Where to go from here?

Oh my! There is so much more for you to learn about Mach-O, but the road ends here for now.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now