CLI tool
AloneMonkey/MonkeyDev avatar
AloneMonkey/MonkeyDev

MonkeyDev: dylib injection and Logos hooks on non-jailbroken iOS

CaptainHook Tweak、Logos Tweak and Command-line Tool、Patch iOS Apps, Without Jailbreak.

6,805 stars1,238 forksPythonGPL-3.0

At a glance

What is it?
MonkeyDev is a build-on-top-of-iOSOpenDev layer that adds a MonkeyApp template to Xcode, so you can inject a dylib into an existing iOS app, write Objective-C or Logos hooks, and resign, install and debug on a stock device. It is a developer tool for people who already own the app they want to instrument.
Who is it for?
MonkeyDev suits iOS developers who need to instrument an app they own or have permission to study, on a stock arm64 device, without setting up a jailbreak. It is the wrong tool for encrypted binaries, Intel Macs, the simulator, or a target whose methods you cannot name.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The MonkeyApp template is the whole product

MonkeyDev is not a standalone reverse engineering platform. It is a layer built on top of iOSOpenDev that registers a new project template named MonkeyApp inside Xcode. The template produces a dylib target, a host application target and a packaging step, and it is the packaging step that does the interesting work. Based on the repository layout, the project ships a Tools directory, a Frameworks directory, Librarys, Resource, include and bin folders, with the install and update scripts in bin and a change.log at the root.

The design assumption is narrow and worth stating plainly. The documentation says the tool targets Apple Silicon Macs and non-jailbroken iPhoneOS arm64 hardware on iOS 15 and later. It also names the environment in which builds, repeat installs, hooks, symbol breakpoints, header export and Reveal were exercised: Xcode 27.0 with build 27A266a, and an iPhone 14 Pro running iOS 18.6.2. Anyone reproducing that has to match a fairly specific toolchain.

The template also asks for a target app as input. That input is an already decrypted IPA or an unencrypted .app placed in a TargetApp folder next to the generated project. MonkeyDev does not decrypt anything for you, which is the single fact that decides whether this tool is usable in your situation.

Installing Theos first, then the packaged toolchain

Installation is a two stage affair. The documentation says you need a full Xcode install, a native arm64 shell and a Theos checkout belonging to the current user, and it warns you not to install Theos under /opt/theos. The Theos installer handles that part:

bash
bash -c "$(curl -fsSL https://raw.githubusercontent.com/theos/theos/master/bin/install-theos)"

After that, the bundled installer takes over. It lands in /opt/MonkeyDev, and the README states that install, update and uninstall never modify Xcode.app itself. The template registration happens in the current user's Xcode template directory.

bash
sudo /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/AloneMonkey/MonkeyDev/master/bin/md-install)"
/opt/MonkeyDev/bin/md-doctor

The doctor command is the check that matters. The installer prepares and validates a complete version before replacing what is on disk, and it writes a version receipt and file hashes, so a failed upgrade should not leave a half-written tree. Later updates go through a separate script:

bash
sudo /opt/MonkeyDev/bin/md-update

If you prefer to work from a clone, the same two scripts accept an environment variable pointing at your source directory, which is how you iterate on the tool itself rather than on the apps you build with it:

bash
sudo env MONKEYDEV_SOURCE_DIR="$PWD" /bin/bash bin/md-install
bash
sudo env MONKEYDEV_SOURCE_DIR="$PWD" /bin/bash bin/md-update
/opt/MonkeyDev/bin/md-doctor

Five build settings decide what the template does

The creation wizard exposes five yes or no dropdowns, and each maps to a build setting you can still change later under App target, Build Settings, User-Defined. Inject Dylib is MONKEYDEV_INSERT_DYLIB and defaults to yes. Keep Original Bundle ID is MONKEYDEV_DEFAULT_BUNDLEID and defaults to no, meaning the project keeps its own bundle identifier. Restore OC Symbols is MONKEYDEV_RESTORE_SYMBOL, Export OC Headers is MONKEYDEV_CLASS_DUMP, and Reveal in Debug builds is MONKEYDEV_REVEAL. The last three default to no.

The bundle identifier switch deserves a warning, because the documentation is explicit that keeping the original bundle identifier still requires a provisioning profile that authorizes that identifier. The switch does not buy you anything in terms of signing.

A second point of friction is the target app field. It is a historical entry point for jailbroken Frida and SSH dumps, not a file picker for an IPA, and it falls outside the verified range. The current path is the TargetApp folder, which must hold exactly one unencrypted input; with nothing in place the built-in demo is used. Nothing needs to be set through an environment variable for that to work.

Signing is ordinary Xcode signing. You need a certificate your team already has, a matching provisioning profile, and the device authorised. Choose an iPhone target, build in Debug, and run.

Two hook entry points on one internal runtime backend

Every project gets two hook entry points, and the documentation is clear that both can run at the same time. The first is a plain Objective-C or CaptainHook file that uses the internal runtime backend. The second is a Logos source file whose generated Objective-C++ output also takes part in compilation. The rule for the Logos path is strict: edit the .xm source, never the generated .mm file.

The internal backend matters more than it sounds. Cycript and the old Substrate runtime have been removed from the project, and the CaptainHook and Logos internal paths do not link the legacy Substrate libraries. The practical consequence is that the generated dylib does not drag a decades-old injection runtime into your process, at the cost of losing compatibility with anything that expected Substrate to be present.

Tracing comes from the same bundle of sources. Basic UI and runtime queries sit in Tools/LLDBTools.h and .mm, and method tracing lives in Trace/MDMethodTrace.h and .mm, with both marked as debug only. The trace output lists a call number, thread number, nesting depth, arguments, return value and duration for synchronous methods. The documentation also draws a hard line: the tracer understands an explicit subset of method signatures and is not a general hook for arbitrary ABIs.

Source breakpoints work on the actual statements of the dylib. When you need to break on a method of the target app itself, enable symbol restoration, rebuild, and set the breakpoint in LLDB by selector:

lldb
breakpoint set -n '-[YourClass yourMethod:]'

Substitute the class and selector of the target app. Note that restoring symbols depends on the MONKEYDEV_RESTORE_SYMBOL switch, which is off by default.

Package lists, CocoaPods plugins and Reveal live in separate lanes

Three side features are worth separating, because each has its own dependency chain.

Header export is the analysis lane. With MONKEYDEV_CLASS_DUMP enabled, the build writes the main program headers into a Headers folder inside the project, named after the app target, and the build log prints the real path. Those headers are not added to the app target automatically, the generated directory is supposed to go into your.gitignore, and older projects used a different directory name. The documentation is blunt about their purpose: they are for analysis, and they are not guaranteed to compile as a complete SDK.

Reveal is the UI lane. It is not bundled, so you install the SDK yourself and MonkeyDev reads it from the standard RevealServer location:

text
~/Library/Application Support/Reveal/RevealServer/RevealServer.xcframework

With MONKEYDEV_REVEAL on, rebuild and run, allow local network access from the app, pick the running app in Reveal, and keep the app running rather than sitting on a breakpoint. Release builds do not hook into Reveal.

MonkeyPod, in a third lane, turns a CaptainHook plugin into a reusable CocoaPods library referenced by the dylib target. It does not resign or install anything by itself. CocoaPods has to be on the machine first:

bash
brew install cocoapods

Then a Podfile in the MonkeyApp project root wires the plugin in, with a compatibility shim required for a project field the current CocoaPods version does not support:

ruby
require '/opt/MonkeyDev/Tools/cocoapods-compat'

platform :ios, '15.0'
target 'YourAppDylib' do
  use_frameworks! :linkage => :dynamic
  pod 'YourPlugin', :path => '../YourPlugin/YourPlugin'
end

The path points at the directory holding the podspec, not at the.xcodeproj. Dynamic linkage is deliberate: with a static library, a file containing only hook functions can be dropped at link time and never run.

How a tweak actually reaches a jailbroken device

The Logos Tweak template is the second half of the tool, aimed at devices that do have a jailbreak. It supports rootless and rootful package layouts and expects a hook loader on the device that speaks the MobileSubstrate interface, with ElleKit given as an example. Again, nothing legacy is bundled with your plugin. A tweak is filtered by the Filter key in the package's MobileSubstrate dynamic libraries plist, and that filter has to name the bundle identifier of the app you want to hook.

The command-line tool is the third variant, keeping C, C++, Foundation and Core Foundation types, signed with ldid, and asking for no extra privileges by default. It installs to /var/jb/usr/bin in rootless mode and /usr/bin in rootful mode, and you launch it over SSH on the device rather than through Xcode Run.

The wizard defaults to USB with a rootless layout. Authentication is one of None, KEY, which is the default and uses SSH key authentication, or PASSWORD. None only builds the deb. KEY and PASSWORD install automatically after a build. Password mode asks for a device account, defaults the account to mobile, and requires a password. The documentation warns that a password typed into the wizard is stored in the project, so it should never be committed or shared.

Connection settings are a table of build settings. MonkeyDevConnectionType selects USB, IP or SSH, MonkeyDevDeviceUDID pins a device, MonkeyDevDeviceIP and MonkeyDevDevicePort handle address and port, and MonkeyDevAuthentication controls how credentials are presented. MonkeyDevDevicePasswordFile points at a local file holding a single line of password, which takes precedence over a directly configured password.

What the tool will not do for you

The limitations are stated rather than discovered, and they are not small. There is no support for Intel hosts, for the simulator, for arm64e or PAC devices, and no support for fat binaries or encrypted input. An arm64e device can still run an arm64 app, which is a different thing from supporting arm64e tooling. The packaging copy removes app extensions, watch content and old store metadata, and the current state does not cover signing and installing those extensions separately.

The unencrypted-input rule is the one that bites first. The documentation states that MonkeyDev does not decrypt apps on non-jailbroken devices, so on a stock phone you have to supply the decrypted app yourself. If your only artifact is an encrypted IPA from the store, this tool cannot help you.

Validation coverage also differs between variants. The rootless layout for Logos Tweak, CaptainHook Tweak and the command-line tool has been exercised on a real jailbroken device, while rootful has only been built and inspected as a package. The Logos internal backend does not support the %hookf directive. The tracer's signature support is limited, as noted earlier.

Finally, the cost of the tool. MonkeyDev carries no GitHub releases, and the last push was on 2026-10-19, so the code is moving, but the version you install is the version in the installer script rather than a tagged artefact. The project is distributed under GPL-3.0, so anything you build on top of it and distribute carries that licence obligation, while a tool you keep inside your own team raises a different set of questions that this project does not answer for you.

Editorial conclusion

MonkeyDev suits iOS developers who need to instrument an app they own or have permission to study, on a stock arm64 device, without setting up a jailbreak. It is the wrong tool for encrypted binaries, Intel Macs, the simulator, or a target whose methods you cannot name. Before you commit, confirm that your input app is unencrypted, that your team can re-sign with a certificate the device trusts, and that the methods you intend to hook have a stable signature, since the built-in tracer only understands a limited subset.

Frequently asked questions

Does MonkeyDev need a jailbroken iPhone?

No. The supported setup is an Apple Silicon Mac with Xcode and a non-jailbroken iPhoneOS arm64 device on iOS 15 or later, and the jailbreak variants exist only as an extra path. The tool does not decrypt apps for you on a stock device, so you have to provide an unencrypted IPA or .app as input.

What is the difference between the CaptainHook file and the Logos file in a MonkeyApp project?

The CaptainHook file is ordinary Objective-C using the internal runtime backend, and the Logos file uses the internal Logos backend whose generated .mm is compiled alongside it. Both entry points can run at the same time, and with the Logos source you edit the .xm file rather than the generated .mm.

How do I get the exported Objective-C headers into a separate analysis project?

Enable the Export OC Headers option, which maps to the MONKEYDEV_CLASS_DUMP build setting, and rebuild. The headers are written to a Headers folder inside the project named after the app target, and the build log prints the actual path. They are not added to the app target automatically, and the documentation notes they are for analysis rather than a complete SDK.

Can MonkeyDev keep the original bundle identifier of the target app?

There is a switch for it, MONKEYDEV_DEFAULT_BUNDLEID, which defaults to no. The documentation makes clear that keeping the original identifier still requires a provisioning profile authorising that identifier, so the switch does not bypass signing requirements.

Official sources

  1. AloneMonkey/MonkeyDev on GitHub
  2. Issues
  3. License: GPL-3.0
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/alonemonkey-monkeydev.svg)](https://hysenlabs.com/projects/alonemonkey-monkeydev)

Was this useful?