Marzipan Demo App
This is the demo app used in the talk “Hacking Marzipan” by Tom Lokhorst at Do iOS 2018.
⚠️This project is for demonstration purposes only, it is not meant to be used in production apps. Use at your own risk!
Build and tested on macOS 14.0 and 14.1 with Xcode 10.
This document explains how to create your own iOSMac (Marzipan) apps, using Steve Throughton Smith's marzipanify tool.
The demo application uses Swift, a storyboard, and calls private API's to display a segmented control in the window toolbar.
Disable macOS Security (Don't do this on your main device!)
To run a converted (marzipanified) app, you need to disable System Integrety Protection and AMFI:
- Restart you Mac in recovery mode by holding ⌘+R while booting
- In the Terminal, run
- In the Terminal, run
- Restart you Mac
- Your system is now vulnarable to malware and you can run marzipanified apps
To re-able these security settings, reboot again in recovery mode and run:
csrutil enable and
Marzipanify an iOS UIKit app
Use marzipanify to convert a iOS simulator binary to a macOS app:
marzipanify is an unsupported commandline tool to take an existing iOS Simulator binary (with minimum deployment target of iOS 12.0) and statically convert it and its embedded libraries & frameworks to run on macOS 10.14's UIKit runtime (Marzipan).
- Clone marzipanify and build the command line tool
- Create a new iOS app (with minimum deployment target of iOS 12)
- Build your app for the iOS Simulator
(make sure not to accidentaly add a slash at the end:
marzipanify YourApp.app/won't work)
- If your app uses Swift, whitelist by editing
- Run the converted app with lldb to see output:
lldb YourApp.app -o run
- The converted app must be deleted (or moved) to be able to build again in Xcode
Note: If your app uses Cocoapods, add this to your Podfile to make sure all pods also have a iOS 12 deployment target:
post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0' end end end
For a quick build-run-test cycle, this script cleans up the marzipanified app:
#!/bin/sh appname=$1 if [[ $appname == *".app"* ]]; then echo "Argument shoudn't include .app" exit fi rm -rf $appname-mz.app marzipanify $appname.app mv $appname.app $appname-mz.app lldb $appname-mz.app -o run
Segmented control in Window toolbar
To create a nice macOS-style segmented control in the window toolbar, we need to use some private APIs and Objective-C code.
- Use class-dump to look at all the private APIs in UIKit for the Mac
class-dump /System/iOSSupport/System/Library/PrivateFrameworks/UIKitCore.framework > UIKitCore.h
- Add new Objective-C .h and .m files to your project and include the header in your Swift code via a Briding header
- In the header file, include the private APIs from UIKitCore you which to use (see MarzipanHelper.h)
- In the implementation file, use those APIs (see MarzipanHelper.m)
- Add the private classes used to the linker flags, to weak link them: .
For more details, read Peter Steinberger's post on becoming a better Mac citizen, or see the example code in this repository.
This talk and repository have been made possible by the work of many people, a partial list:
- Tweets from Steve Troughton Smith
- Peter Steinberger's post on Marzipan
- Vlas Voloshin's talk at CocoaHeads Melborn on Marzipan Internals
- My collegues at Q42 for their help in preparing the talk and researching weird linker issues
Tom Lokhorst - @tomlokhorst
1 November 2018