Chapters

Hide chapters

Advanced Apple Debugging & Reverse Engineering

Fourth Edition · iOS 16, macOS 13.3 · Swift 5.8, Python 3 · Xcode 14

Section I: Beginning LLDB Commands

Section 1: 10 chapters
Show chapters Hide chapters

Section IV: Custom LLDB Commands

Section 4: 8 chapters
Show chapters Hide chapters

27. SB Examples, Malloc Logging
Written by Walter Tyree

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

For the final chapter in this section, you’ll go through the same steps I myself took to understand how the MallocStackLogging environment variable is used to get the stack trace when an object is created.

From there, you’ll create a custom LLDB command which gives you the stack trace of when an object was allocated or deallocated in memory — even after the stack trace is long gone from the debugger.

Knowing the stack trace of where an object was created in your program is not only useful for reverse engineering, but also has great use cases in your typical day-to-day debugging. When a process crashes, it’s incredibly helpful to know the history of that memory and any allocation or deallocation events that occurred before your process went off the deep end.

This is another example of a script using stack-related logic, but this chapter will focus on the complete cycle of how to explore, learn, then implement a rather powerful custom command.

Setting Up the Scripts

You have a couple of scripts to use (and implement!) for this chapter. Let’s go through each one of them and how you’ll use them:

  • msl.py: This is the command (which is an abbreviation for MallocStackLogging) script you’ll be working on in this chapter. This has a basic skeleton of the logic.
  • lookup.py: Wait — you already made this command, right? Yes, but I’ll give you my own version of the lookup command that adds a couple of additional options at the price of uglier code. You’ll use one of the options to filter your searches to specific modules within a process.
  • sbt.py: This command takes a backtrace with unsymbolicated symbols, and symbolicate it. You made this in the previous chapter, and you’ll need it at the very end of this chapter. And in case you didn’t work through the previous chapter, it’s included in this chapter’s resources for you to install.

Note: These scripts are also in Appendix C “Helpful Python Scripts” Check it out for some other novel ideas for LLDB scripts. It’s important to note that a lot of scripts in Appendix C have dependencies on other files, so if you try to use only one script then it might not compile until the full set of files are included.

Now for the usual setup. Take all the Python files found in the starter directory for this chapter and copy them into your ~/lldb directory. I am assuming you have the lldbinit.py file already set up, found in Chapter 25, “SB Examples, Improved Lookup.”

Launch an LLDB session in Terminal and go through all the help commands to make sure each script has loaded successfully:

(lldb) help msl
(lldb) help lookup
(lldb) help sbt

MallocStackLogging Explained

In case you’re unfamiliar with the MallocStackLogging environment variable, when the MallocStackLogging environment variable is set to true, it’ll monitor and record allocations and deallocations of memory on the heap. Pretty neat!

Plan of Attack

You’ve seen it’s possible to grab a stack trace for an instantiated object, but you’re going to do one better than Apple.

Hunting for a Starting Point

As you just saw, Xcode provides a special backtrace for any object that gets allocated when MallocStackLogging is enabled. Go ahead and build and run the app and then tap on Generate a Ray! a few times to create some instances of RayView. Now use the Debug Memory Graph a few times to stop the app. Now inspect some of the RayView instances to look for patterns.

Googling JIT Function Candidates

Google for any code pertaining to turn_on_stack_logging. Take a look at this search query:

Exploring __mach_stack_logging_get_frames

Fortunately, for your exploration efforts, __mach_stack_logging_get_frames can also be found in the same header file. This function signature looks like the following:

Testing the Functions

To prevent you from getting bored to tears, I’ve already implemented the logic for the __mach_stack_logging_get_frames inside the app.

LLDB Testing

Make sure the app is running, then tap the Generate a Ray! button a few times. Click the Debug Memory Graph button again to pause the app and bring up the graph.

Navigating a C Array With lldb.value

You’ll again use the lldb.value class to parse the return value of this C struct which was generated inline while executing this function.

Turning Numbers Into Stack Frames

Included within the starter directory for this chapter is the msl.py script for malloc script logging. You’ve already installed this msl.py script earlier in the “Setting up the scripts” section.

Stack Trace From a Swift Object

OK — I know you want me to talk about Swift code. You’ll cover a Swift example as well.

DRY Python Code

Stop the app! In the schemes, select the Stripped 50 Shades of Ray Xcode scheme.

Key Points

  • Some Xcode functionalities are just wrappers around LLDB so enhancing them is a good way to practice creating scripts.
  • Malloc Stack Logging will log when objects are allocated and deallocated.
  • Malloc Stack Logging has an All Allocation and Free History and an All Allocation option. The Free History option records more data.
  • Use image lookup -rn or our custom lookup to search for any interesting symbol names you find as a first step in exploring.
  • Search https://opensource.apple.com to find header files that often have useful notes and documentation you won’t find anywhere else.
  • Write and test code in Xcode for any functions you later want to bring into a Python script as JIT-ed code. The tools for debugging JIT-ed code within your scripts are effectively nonexistent.
  • Python scripts can import Python scripts. As you write code, always look for ways to refactor so that you can reuse your best work in multiple places.

Where to Go From Here?

Hopefully, this full circle of idea, research & implementation has proven useful and even inspired you to create your own scripts. There’s a lot of power hidden quietly away in the many frameworks that already exist on your [i|mac|tv|watch]OS device.

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.
© 2026 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