| name | wrap-common-exploit |
| description | Wrap an ordinary C exploit or PoC in kernel_PoCs/common so one source project can build as a Linux or Android static executable, dynamic executable, constructor-loaded shared object, or JNI library with local, listening, or reverse-shell root payloads. Use when creating a common-compatible exploit project, converting a C program with main(), setting up exploit entry and post-escalation payload code, repairing its Makefile or entrypoint integration, or initializing the bundled example project. |
Wrap a C Exploit with kernel_PoCs/common
Use the canonical kernel_PoCs/common files as the source of truth. Inspect common/mk-flags, common/common.mk, common/exploit_entry.h, and common/root_payload.h before changing an existing project because their supported modes may evolve.
Initialize a project
Run the bundled initializer, using an absolute destination when practical:
python <skill-dir>/scripts/init_project.py /path/to/kernel_PoCs/new-project
The copied assets/exploit-project/ contains a Makefile and a compilable exploit.c showing the exploit stub, success check, root payload call, and common entrypoint glue. Replace the stub with the real exploit while preserving that control flow. The project assumes the normal layout kernel_PoCs/<project> and therefore defaults COMMON_DIR to ../common. Override it at build time when the project lives elsewhere:
make COMMON_DIR=/absolute/path/to/kernel_PoCs/common
Never overwrite a nonempty destination during initialization. Preserve existing sources and Makefile settings when wrapping an existing project.
Convert the C entrypoint
Separate the source into three responsibilities:
- Implement the vulnerability trigger and privilege-escalation work in
exploit(). Return a status or otherwise make success explicit; do not start a payload when exploitation fails.
- Implement a no-argument wrapper such as
run_exploit() that calls exploit(), checks success, and calls root_payload() only after the process has the intended privileges.
- Define
EXPLOIT_MAIN as the statement that invokes the wrapper, then include <exploit_entry.h>. Keep this glue in exactly one translation unit because the header emits main, a constructor, or JNI_OnLoad according to the selected artifact mode.
Keep the original implementation and helpers intact where possible. Rename or refactor its main() rather than retaining two entrypoints. Resolve command-line-only configuration explicitly because the common entrypoint does not provide argc or argv.
Include <exp_common.h> for common types and exploit helpers. Include <root_payload.h> for root_payload(). Put the EXPLOIT_MAIN definition immediately before <exploit_entry.h> so the header sees it. Treat EXPLOIT_MAIN as statement text and include its terminating semicolon.
Use this minimal adapter shape:
#include <exp_common.h>
#include <root_payload.h>
static int exploit(void)
{
return -1;
}
static void run_exploit(void)
{
if (exploit() != 0) {
LOG("exploit failed");
return;
}
root_payload();
}
#define EXPLOIT_MAIN run_exploit();
#include <exploit_entry.h>
If the existing exploit must retain a differently named entry function, call that function from EXPLOIT_MAIN instead of performing a broad rewrite.
Set up the payload
Treat root_payload() as post-exploitation behavior, not as the code that obtains root. PAYLOAD_ARGS selects its implementation at compile time:
--shell uses the fallback local /bin/sh payload.
--listening-shell --port <port> defines LISTENING_SHELL and ROOT_PAYLOAD_PORT.
--reverse-shell --ip <address> --port <port> defines REVERSE_SHELL, ROOT_PAYLOAD_IP, and ROOT_PAYLOAD_PORT.
The entry header prints the selected payload configuration before invoking EXPLOIT_MAIN. A static or ordinary binary runs it from main. A shared object runs it from a constructor and unsets LD_PRELOAD. A JNI library runs it from JNI_OnLoad using the behavior defined by the current common header. Inspect those headers rather than duplicating their entry or socket code in the exploit.
Integrate the Makefile
Set PAYLOAD_ARGS before including common.mk. Supply exactly one option from each axis:
- Target:
--linux or --android.
- Artifact:
--static, --binary, --shared, or --jni.
- Payload:
--shell, --listening-shell --port <port>, or --reverse-shell --ip <address> --port <port>.
The include evaluates mk-flags, adds the common include directory and mode macros to CCFLAGS, and chooses CC/CXX. Android builds require ANDROID_NDK_HOME. --binary is a normally linked executable even though the generated entrypoint macro is named EXPLOIT_STATIC.
Keep project-specific warning, optimization, thread, and library flags after the include. Compile objects with CCFLAGS and link with it so generated artifact and payload flags are not dropped. Retain -fPIC when shared or JNI builds are expected. Add every C source to SRCS, but keep the entry adapter in only one source.
Prefer overridable defaults:
COMMON_DIR ?= $(abspath $(CURDIR)/../common)
PAYLOAD_ARGS ?= --linux --binary --shell
include $(COMMON_DIR)/common.mk
Do not invoke the legacy payload-flags alongside common.mk; use the PAYLOAD_ARGS/mk-flags path consistently.
Verify the wrapper
First inspect generated settings without relying on a successful cross-build:
make -n PAYLOAD_ARGS='--linux --static --shell'
make -n PAYLOAD_ARGS='--linux --shared --listening-shell --port 1340'
Then build the variants relevant to the user. At minimum, build the default Linux mode when its toolchain and libraries are available. For Android, confirm ANDROID_NDK_HOME and build one requested Android variant. Check the artifact with file and, for shared/JNI output, verify that the expected entry symbol or constructor exists with readelf or nm.
Clean generated objects and binaries after validation unless the user asks to keep them.