1,056 karma · joined August 20, 2014
The goal here was to run small bits of the app's business logic. Specifically, it was about inspecting (for example) the contents of an NSDictionary that was passed to a particular system API.
My initial goal was to improve my own iOS static analysis tooling, and the way I initially thought about doing this was building a decompiler and then introspecting on the decompiled code. I didn’t have any knowledge of decompilers, and so the approach I took was essentially simulating the code and logging what it did. About a month down the line I realised that I was actually building a simulator in earnest, and ran with it.
Yes, this was all in Python. This simulator was a bit unique in that there was no live user interaction, and therefore no realtime required. Overall, performance wasn't too much of a concern.
More particularly, I built this on top of strongarm, a Mach-O analysis library I had already written in Python. My employer encouraged us to write things in Python so that there was lower cognitive overhead for people to see each other's projects. This was more of an off-time passion project, but I still took the advice.
Like with any simulator, it was immensely gratifying watching third-party code run in an entirely controlled virtual environment!
This project also got me very familiar with some unintuitive AArch64 addressing rules, and taught me the internals of how funny Objective-C constructors that accept variadic arguments lists are handled.
In one late-night push, I managed to get the real CoreFoundation library dynamically loaded and running, which meant that the real standard library was able to create bona-fide Objective-C objects!
This post also goes into some fun bugs I created for myself, such as the simulator skipping over undefined functions resulting in an infinite loop when simulated code tried to abort(). I hope it's fun to follow along with, and thanks for reading!
It's also reiterated the importance for me to take screenshots as I go. For many/most projects, these intermediary and bugged out results are gone as quickly as they come if I don't make an effort to persist them.
I've gone ahead and fixed that - good catch, and thank you again!
However, a newer boot ROM exploit, checkm8, has become well-known in the intervening years. The A5 (that the 4s ships) is vulnerable to checkm8, which means that it'd certainly be possible to add support for this exploit chain to a project like gala!
I don't have any web development expertise, and it's not where my interests lie, so I've been following a "google it and move on as quickly as possible" approach. That said, since I'm now making web tools, I do have an intellectual responsibility to learn the platform. I reached for jQuery just because I had a vague sense that it was a "web development thing", and didn't bother to learn the fundamentals of what exactly it was bridging, and whether those bridges were still relevant in today's browser landscape. I appreciate the callout, and will use it as an opportunity to learn more about the web platform.
Like you realised, I thought JavaScript was appropriate because I wanted to ensure that the user wouldn't be navigated to a new page when they submitted the form. I appreciate the quite tidy approach you've shared that achieves this when JS is available, and still does something reasonable when it's not!
When exploiting a running system, your goal typically won't be to disable ASLR (which would only impact newly spawned processes), but instead to 'infoleak' where ASLR has placed important things you care about, so you know where to access them.
Modern devices have mechanisms like KPP/KTRR, though, which make it impossible to modify kernel code anyhow.
You also propose that CoreGraphics might not be sandboxed. CoreGraphics is a dynamic library which can be loaded into any process. It's _processes_ that are sandboxed, not dynamic libraries, so CoreGraphics can definitely exist in a process that has a sandbox profile applied just fine.
You also mention that graphics functions might not be sandboxed because they need to access graphics acceleration features. This is a good thought! In fact, the kernel extension that enables hardware graphics acceleration, IOMobileFramebuffer, is accessible from the app sandbox for this very reason. As a point of interest, many vulnerabilities have been discovered in IOMobileFramebuffer over the years -- it's an attractive target specifically because it's reachable from the app sandbox.
Lastly, you mention jailbreaking the sandbox. I know what you mean, but "jailbreaking" typically refers to a series of abilities, such as the ability to control the kernel task, the ability to create RWX pages, the ability to bypass the FreeBSD MAC policies, etc. The ability to bypass sandbox restrictions is only one condition of a jailbreak, and a sandbox escape doesn't imply a full jailbreak. Also, jailbreaking doesn't entirely break "the sandbox" -- it's a selective change that can be applied on a per-process basis.
To answer your questions, the post is trailing the repo. I've uploaded the full source comprising this post here: https://github.com/codyd51/penpal-blog-post-preprocessed.
The code blocks link back to the Github source via a custom Hugo shortcode, and I currently need to maintain this by hand. While wrapping up this blog post, I added support for generating this shortcode via an extension to the `show` command.
Here's what the penpal command to render a hotlinked code block looks like, in the source code of this blog post: https://github.com/codyd51/penpal-blog-post-preprocessed/blo...
Here's what the shortcode generation for this looks like in penpal: https://github.com/codyd51/penpal/blob/release/penpal/render...
I think it'd be really nice to maintain all of this automagically - it seems reasonable that the technical content being built up would frequently be hosted in a finished state somewhere!
This post is ostensibly about some tooling I’ve written to help me write posts that contain lots of code, while ensuring that the code always produces a valid source tree which matches up with where we are in the prose. This is easy when a post is small, but can become really difficult to manage with a larger piece of work!
The bulk of this post, though, is a proof-by-demonstration of the tooling. We implement a little 2048 clone, line by paragraph, and there’s a bunch of checkpoints interspersed throughout the post - and now we’ve implemented input handling! and now we can slide tiles around!
At each of these checkpoints, I’ve embedded some live visualizations of the code we’ve written so far. My hope is that the reader can really get a feel for what we’ve built so far, because they can see what works, and what doesn’t work, running right in front of them in the browser. These little computers are taken from snapshots produced by the tooling, compiled to Wasm and executing in the browser.
This is my first time providing this level of interactivity in one of my posts, so please do feel welcome to play around with it. I’d love any and all feedback on the experience. Thanks so much!
Often, the answer is “nothing special”, and extremely minor pieces of happenstance (such as the exact layout of address space requested by the kernel ELF) lead to something breaking way down the ladder.
It's really nice to hear that you enjoyed it!
I was unsure whether to write this post as, in one sense, it’s detective work in which I’m the investigator, and a slightly younger and more sloppy version of myself is the culprit. As you said, though, there's also more to it: the system takes on a life of its own, with different parts of the stack interacting in unexpected and complex ways, in an environment that doesn’t give me much help to write things correctly (and occasionally presents footguns on a silver platter).
It’s really gratifying to see people reading and identifying with it, and I’m also extending my thanks to the user who posted this to HN!
For example, read() is a function implemented in an optional userspace library that's available for programs to link against. When invoked, it constructs and sends a message with the right parameters to the filesystem server, and awaits its response: https://github.com/codyd51/axle/blob/paging-demo/programs/su...
There's definitely a bootstrapping problem with the lower-level knowledge, but there are enough resources out there for the initial steps that anyone can get started, and you can do a lot if you enjoy the journey!
axle is fundamentally organised around structured messages. This mechanism is used both for interaction between userspace applications, and for nearly all interaction between kernel and userspace.
For example, creating a process, allocating virtual memory regions, profiling the system and registering process hooks, are all just special messages that are sent to the kernel, rather than to another userspace app. Want to transmit a packet? If you have the rights, send a message to the NIC. Want to create a window on the desktop, or update its contents? Send a message to the window manager. It'll send some messages back to you when the user interacts with your content. The GUI toolkits use custom idioms. It's certainly true that axle reuses some common ideas (like featuring a desktop environment, and a process model), but I'm happy to go my own way from there!
It's also true, as pjmlp mentioned, that some level of POSIX compatibility is necessary if you want to run external software, which I certainly do! DOOM, for example, requires read() and write(), among other common syscalls. If you want to run, say, GCC, you need fork().