Overview

A payload is a single position-independent .elf a loader maps into a process that is already running, over a network connection, and jumps to. It runs inside the host process and ends when its entry returns. This page collects the shape of a payload, when to build one, and the vocabulary the rest of this section uses.

What a payload is

The linker produces a ET_DYN ELF with three load segments, a PT_DYNAMIC segment, and one entry point named _start. Under _start sits the payload’s C runtime (the “CRT”): a self-contained bring-up sequence that a dynamic linker would ordinarily supply for a program. Under the CRT sits main from libbootstrapper.o — the ahead-of-time runtime’s bootstrap that calls RhInitialize, RhRegisterOSModule, and InitializeModules. Under that sits the payload’s own managed entry:

[UnmanagedCallersOnly(EntryPoint = "__managed__Main")]
public static int Main(void* args)
{
    // your code
    return 0;
}

The loader on the console side is small: it accepts a TCP connection, reads the ELF, maps its LOAD segments, applies base-relative fix-ups, and jumps to _start. It does not resolve imports, does not allocate thread-local storage, and does not run constructors. Everything the loader would have done for an application module, the CRT does at run time — see Runtime bring-up.

When to build a payload

Build a payload when the program has to run inside a process that is already open:

  • A background service the user starts from a launcher: an FTP server, a debug bridge, a status page, a companion daemon that other homebrew calls.
  • A one-shot action with no interface of its own: send a notification, launch the browser at a URL, dump the hardware info, list the mounted file systems.
  • A bring-up experiment that reads or writes kernel structures, walks the process list, or applies a small runtime patch to the host process.

An application module is the right form for anything the user installs and launches from the home screen — see Application Modules.

What the CRT provides

A payload’s CRT is the layer that lets managed C# run inside a hijacked host process. It bakes in everything a program’s start-up would otherwise pull from the C library and the dynamic linker:

  • The syscall gateway __sp_crt_syscall shuffles the C calling convention into the syscall ABI and dispatches through an instruction gadget derived once at start-up.
  • The resolver cascade binds every function name the payload calls to a runtime address by probing three module handles in sequence — see Resolver cascade.
  • The kernel-access surface carries per-firmware offsets and a pipe primitive so a payload can read and write kernel structures with per-field accessors — see Kernel access.
  • The thread-control block is allocated at start-up, populated from the host’s stack canary, and installed with sysarch(AMD64_SET_FSBASE). Every managed thread-local access hits this block at a fixed offset from the thread pointer.
  • The thread exit epilogue calls SYS_thr_exit (syscall 431) so returning from main terminates only the hijacked thread, not the host process.

The wire

The default send path uses the SDK’s payload --send command, which speaks the small loader wire format: a TCP connection to port 9021, the raw ELF bytes, and a close. See Sending a payload for the command and its options.

The API surface

Everything a payload calls from managed code lives under the SharpProspero.Payload namespace: network, filesystem, process, notification, kernel access, dynamic library loading, hardware info, sysctl, user service, browser, HTTP/2, package install, and random-bytes reading. See Payload API for the complete list.


Copyright © SvenGDK 2026. Distributed under the GPL-3.0-or-later license.

This site uses Just the Docs, a documentation theme for Jekyll.