Skip to content

Releases: stoatworks-labs/system-graft

v0.1.1

Choose a tag to compare

@github-actions github-actions released this 30 Jul 18:19

macOS.pkg installs System Graft into /Applications; .dmg lets you drag it.
Apple Silicon only: PyInstaller freezes the interpreter it runs under and cannot
cross-compile, and the Intel macOS runners are retired.

Linux.deb and .rpm (both declare a squashfs-tools dependency), plus a
portable .tar.gz. x86_64 and aarch64.

Windows is not built, because it is not supported: USB writing raises
"not implemented" on anything that is not macOS or Linux.

squashfs-tools is required at runtime and is not bundled — the app shells out
to unsquashfs/mksquashfs. Install it with brew install squashfs on macOS, or
your package manager on Linux (the .deb/.rpm pull it in for you).

Builds are unsigned. On macOS, after installing:

xattr -dr com.apple.quarantine "/Applications/System Graft.app"

macOS is the tested write path; the Linux write path is implemented but untested,
and this tool repartitions removable media — read the README first.

What's Changed

New Contributors

Full Changelog: v0.1.0...v0.1.1

System Graft v0.1.0

Choose a tag to compare

@stoatworks-labs stoatworks-labs released this 26 Jul 16:23

First release.

Inject out-of-tree kernel modules into a SquashFS initrd, hook them into the image's init
scripts, and write the result to bootable removable media. You supply the image and the
modules — this ships neither.

Install

No packaging step: it's Python 3 with the standard library only.

git clone https://github.com/allansargeant/system-graft
cd system-graft
brew install squashfs        # or: apt install squashfs-tools
python3 gui.py

There is deliberately no frozen .app or .exe. A bundled app would still require
squashfs-tools on PATH, and an unsigned macOS bundle containing a nested interpreter is
liable to be killed by Gatekeeper without a visible error. Running the scripts is both
simpler and more reliable.

What it does

Patch — inject .ko files into the initrd, add a load hook to init, repack matching the
source's compressor and block size.

Write — put the whole boot tree on removable media as a UEFI-bootable volume
(GPT + FAT32), substituting the patched initrd in flight.

Why it isn't just unsquashfs + mksquashfs

Doing this by hand quietly produces a broken image:

  • Ownership is lost — a non-root extract cannot restore uid/gid.
  • setuid/setgid bits are dropped, so a setuid-root busybox comes back unprivileged.
  • The root inode escapes even a careful pseudo-file.

This reads the full mode/uid/gid table from the source, replays it through a mksquashfs
pseudo-file, applies the root inode via -root-uid/-root-gid/-root-mode, then re-reads the
output and diffs it against the source. It does not report success unless the permission
table matches exactly.

It also checks vermagic against modules already in the image and refuses on mismatch by
default
— that failure otherwise shows up silently at boot, on a machine you may have no
console on.

Safety

USB writing only ever offers devices the OS reports as external, excludes virtual devices
unless explicitly asked, re-reads the device immediately before writing, refuses anything
mounted at a system path, requires the device node to be typed to confirm, and verifies
every written file by sha256 before ejecting.

Tested

16 tests, run on Ubuntu and macOS in CI. They build a synthetic appliance image from
scratch — root-owned files, a setuid binary, a directory owned by a uid with no passwd
entry — patch it, and assert all of it survives. Also covered: hook placement and
indentation, hook landing inside the correct init branch, idempotency across three
consecutive re-patches, vermagic blocking and override, input immutability, and
output-overwrite refusal.

macOS is the tested path for USB writing. Linux is implemented but untested. Windows is
not supported.

See CHANGELOG.md.