Understanding how your tweaks get enabled in Electra.

The trigger

It all started from this tweet:

Before some developer wraps this procedure into an useful tweak, I tried to understand what’s actually happening there. Luckily, CoolStar had just open-sourced an Electra1131 build a couple hours before Jake’s tweet. Thank you, CoolStar!


The magic line

inject_criticald 1 /electra/pspawn_payload.dylib

Its arguments are pretty simple: take a pid (in our case, pid 1 equals launchd) and inject a library into it.


Down the rabbit hole

Most subroutines are already described in previously released Ian Beer’s exploitations:

Take launchd pid -> get a task port # -> achieve tfp0.

Since we have root access (and now, tfp0), the injection can be achieved.

We proceed to find the start address of *launchd* process by using Ian Beer’s binary_load_address (it grabs the address from the second argument of mach_vm_region. Check it at triple_fetch for a deep understanding).

We’ve arrived at the first console logging line (Address is at 0000…).

Here’s the final inject_criticald procedure call:

call_remote(remoteTask, dlopen, 2, REMOTE_CSTRING(loaded_dylib), REMOTE_LITERAL(RTLD_NOW));

This procedure uses dlopen() function to load our library into launchd. The RTLD_NOW flag makes sure all undefined symbols in the library are resolved before dlopen() returns.

Somewhere along the call_remote road, the second console logging line warns us about the success of the find_blr_x19_gadget() function, which has to do with ROP and the inner workings of (again) Ian Beer’s triple_fetch exploit.

Our loaded_dylib (/electra/pspawn_payload.dylib, if you remember well) is successfully injected :)


Why dylibbin’?

Here’s a brief look into *pspawn_payload* bin:

Cool. Let's check where `getpid() == 1` happens.

Cool. Let's check where `getpid() == 1` happens.

There it is:

Mind the creation of a pthread with the function `thd_func` passed as argument.

Mind the creation of a pthread with the function `thd_func` passed as argument.

Our exploit now knows how to spawn whatever Daemons it wishes.

Our exploit now knows how to spawn whatever Daemons it wishes.

The rebindings described allow us to fake spawn Daemons (more specifically, every Daemon found at /Library/LaunchDaemons/), including our dearest /usr/libexec/cydia/startup, which activates our tweaks without (luckily!) causing panics/loops that would require a reboot.

Since we’re already at a jailbroken state, CoolStar’s *jailbreakd* Daemon is already active before these steps — and thus, it is skipped. The same goes on for *sshd* (OpenSSH).


Making tweaks appear (Windows’ “restart to apply changes”)

If we compare Jakes’ flow against CoolStar’s, we’ll see the only diff is the post injecting process to make changes apply: while Jake’s only resprings the device, CoolStar’s does a wider cleaning by using ldrestart as described in his tweet:

That’s all, folks. Thank you for reading this far :)


Special thanks to:

Coolstar and Electra Team for an amazing jailbreak tool

Ian Beer for amazing iOS kernel research

Jake James for provoking my curiosity

Jay Freeman for battling jailbreak legality for all these years

Apple for the most amazing mobile OS ever