commit 423d961699ca3e8a0c77d91ce8dd1476b1c6aac9
parent 0f23ea8aeebe9162736b0f1ffc8f99a900cab5ac
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date: Tue, 7 Jul 2026 02:06:34 +0200
Directories
Diffstat:
21 files changed, 220 insertions(+), 165 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,6 +0,0 @@
-*.o
-.deps
-test/
-*.inc.h
-*.inc.s
-stabbish
diff --git a/Makefile b/Makefile
@@ -1,99 +1,15 @@
-.SUFFIXES:
+.PHONY: src test deps clean_% clean
-CFLAGS=-static
-BB_VER=1.38.0
-CC=gcc
-DEP_DIR=$(PWD)/.deps
+test: src
+ @make -C $@
-BBOX_MKFLAGS=HOSTCFLAGS+="-static" HOSTLDFLAGS+="-static" EXTRA_LDFLAGS="-static"
+src: deps
+ @make -C $@
-DEP_MUSL=$(DEP_DIR)/x86_64-linux-musl-native
-DEP_BBOX=$(DEP_DIR)/busybox-$(BB_VER)
+deps:
+ @make -C $@
-BBOX=$(DEP_BBOX)/busybox
-MUSL_CC=$(DEP_MUSL)/bin/gcc
+clean: clean_src clean_test clean_deps
-export PATH:=$(DEP_MUSL)/bin:$(PATH)
-
-stabbish: main.o bbox/busybox.inc.o | $(MUSL_CC)
- $(info $() LD $@)
- @$(CC) $(CFLAGS) $^ -o $@
-
-main.o: main.c bbox/busybox.inc.h magic.h elf_payload.h utils.h | $(MUSL_CC)
- $(info $() CC $@)
- @$(CC) $(CFLAGS) -c $< -o $@
-
-%.inc.h: tmplt/inc.h
- $(info $() GEN $@)
- @sed 's@NAME@$(patsubst %.inc.h,%,$(@F))@g' $< > $@
-
-%.inc.s: tmplt/inc.s
- $(info $() GEN $@)
- @sed 's@NAME@$(patsubst %.inc.s,%,$(@F))@g' $< > $@
-
-bbox/busybox.inc.o: bbox/busybox.inc.s $(BBOX) | $(MUSL_CC)
- $(info $() ASM $@)
- @cd $(dir $(word 2,$^)) && $(CC) $(CFLAGS) -c $(PWD)/$< -o $(PWD)/$@
-
-$(MUSL_CC):
- $(info $() DWNL $@)
- @mkdir -p .deps
- @cd .deps && curl -fSL 'https://musl.cc/x86_64-linux-musl-native.tgz' | tar xz
-
-$(DEP_BBOX)/Config.in:
- $(info $() DWNL $@)
- @mkdir -p .deps
- @cd .deps && curl -fSL https://busybox.net/downloads/busybox-$(BB_VER).tar.bz2 | tar xj
-
-$(DEP_BBOX)/.config: bbox/config $(DEP_BBOX)/Config.in | $(MUSL_CC)
- $(info $() MK $@)
- @cp $< $@
- @yes "" | make -C $(DEP_BBOX) oldconfig $(BBOX_MKFLAGS)
-
-$(BBOX): $(DEP_BBOX)/.config | $(MUSL_CC)
- $(info $() MK $@)
- @make -j$$(nproc) -C $(DEP_BBOX) $(BBOX_MKFLAGS)
-
-test/test.sh:
- $(info $() GEN $@)
- @mkdir -p test
- @echo "type ls" >$@
-
-test/stabbish_plain: stabbish test/test.sh
- $(info $() CAT $@)
- @cat $^ > $@
- @chmod +x $@
-
-test/stabbish_%: stabbish test/test.sh
- $(info $() CAT $@)
- @$(patsubst test/stabbish_%,%,$@) -c test/test.sh | cat $< - >$@
- @chmod +x $@
-
-test: test/stabbish_plain test/stabbish_bzip2 test/stabbish_gzip test/stabbish_xz
- @for test in $^; do \
- printf " TST $$test"; \
- if [ "$$(./$$test)" = "ls is ls" ]; then \
- echo " [OK]"; \
- else \
- echo " [ERR]"; \
- fi \
- done
-
-clean_bbox:
- $(info $() CLEAN bbox)
- @rm -r bbox/*.inc.*
-
-clean_test:
- $(info $() CLEAN test)
- @rm -rf test
-
-clean: clean_bbox clean_test
- $(info $() CLEAN .)
- @rm -rf main.o stabbish
-
-cleanall: clean
- $(info $() CLEAN $(DEP_BBOX))
- @make -C .deps/busybox-$(BB_VER) clean
- @rm -f .deps/busybox-$(BB_VER)/.config
-
-.PHONY: test clean_test clean cleanall
+clean_%:
+ @make -C $(patsubst clean_%,%,$@) clean
diff --git a/config.mk b/config.mk
@@ -0,0 +1,5 @@
+BBOX_VER=1.38.0
+
+MKFILE_DIR:=$(dir $(abspath $(lastword $(MAKEFILE_LIST))))
+
+export PATH:=$(MKFILE_DIR)/deps/x86_64-linux-musl-native/bin:$(PATH)
diff --git a/deps/.gitignore b/deps/.gitignore
@@ -0,0 +1 @@
+*/
diff --git a/deps/Makefile b/deps/Makefile
@@ -0,0 +1,35 @@
+.SUFFIXES:
+
+include ../config.mk
+
+BBOX_MKFLAGS=HOSTCFLAGS+="-static" HOSTLDFLAGS+="-static" EXTRA_LDFLAGS="-static"
+
+DEP_MUSL=x86_64-linux-musl-native
+DEP_BBOX=busybox-$(BBOX_VER)
+
+BBOX=$(DEP_BBOX)/busybox
+MUSL_CC=$(DEP_MUSL)/bin/gcc
+
+$(BBOX): $(DEP_BBOX)/.config | $(MUSL_CC)
+ $(info $() MK $@)
+ @make -j$$(nproc) -C $(DEP_BBOX) $(BBOX_MKFLAGS)
+
+$(MUSL_CC):
+ $(info $() DWNL $@)
+ @curl -fSL 'https://musl.cc/x86_64-linux-musl-native.tgz' | tar xz
+
+$(DEP_BBOX)/Config.in:
+ $(info $() DWNL $@)
+ @curl -fSL https://busybox.net/downloads/busybox-$(BBOX_VER).tar.bz2 | tar xj
+
+$(DEP_BBOX)/.config: busybox.config $(DEP_BBOX)/Config.in | $(MUSL_CC)
+ $(info $() MK $@)
+ @cp $< $@
+ @yes "" | make -C $(DEP_BBOX) oldconfig $(BBOX_MKFLAGS)
+
+clean:
+ $(info $() CLEAN $(DEP_BBOX))
+ @make -C $(DEP_BBOX) clean
+ @rm -f $(DEP_BBOX)/.config
+
+.PHONY: clean
diff --git a/bbox/config b/deps/busybox.config
diff --git a/main.c b/main.c
@@ -1,65 +0,0 @@
-#define _GNU_SOURCE 1
-#include <elf.h>
-#include <malloc.h>
-#include <sched.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <unistd.h>
-
-#include "bbox/busybox.inc.h"
-#include "elf_payload.h"
-#include "magic.h"
-#include "utils.h"
-
-int main(int argc, char **argv) {
- static int try_return = -1;
-
- bool start_shell = argc > 1 && strcmp(argv[1], "--stabbish-shell") == 0;
- if (start_shell)
- shift(1);
-
- fd trys(busybox, memfd_create("busybox", 0));
- fd trys(rcfile, memfd_create("rcfile", 0));
- fd trys(payload, elf_payload("/proc/self/exe"));
-
- try(write(busybox, _inc_busybox, _inc_busybox_size));
-
- setenv("ENV", pasprintf("/dev/fd/%i", rcfile), 1);
- setenv("BUSYBOX", pasprintf("/dev/fd/%i", busybox), 1);
-
- if (payload == ENOPAYLOAD) {
- try(dprintf(rcfile, "echo No payload present, starting shell."));
- } else {
- setenv("PAYLOAD", pasprintf("/dev/fd/%i", payload), 1);
- }
-
- if (start_shell || payload == ENOPAYLOAD) {
- argv[0] = strdup("ash");
- return fexecve(busybox, argv, environ);
- }
-
- struct magic magic = magic_detect(payload);
- if (magic.tool) {
- fd trys(extracted, memfd_create("extracted", 0));
- char *tool = strdup(magic.tool);
- struct stdstream std = {payload, extracted, -1};
-
- lseek(payload, SEEK_SET, 0);
- try(memexecv(busybox, (char *[]){tool, NULL}, NULL, std));
- dup2(extracted, payload);
- free(tool);
- }
-
- char **bb_argv = malloc(sizeof(char *) * (argc + 4));
- bb_argv[0] = strdup("ash");
- bb_argv[1] = strdup("-c");
- bb_argv[2] = pasprintf(". /dev/fd/%i", payload);
- for (int i = 0; i <= argc; i++) {
- bb_argv[i + 3] = argv[i];
- }
-
- return fexecve(busybox, bb_argv, environ);
-}
diff --git a/src/.gitignore b/src/.gitignore
@@ -0,0 +1,3 @@
+*.o
+stabbish
+busybox/
diff --git a/src/Makefile b/src/Makefile
@@ -0,0 +1,35 @@
+.SUFFIXES:
+
+include ../config.mk
+
+CC=gcc
+CFLAGS=-static
+BBOX=$(MKFILE_DIR)/deps/busybox-$(BBOX_VER)/busybox
+
+stabbish: main.o busybox/busybox.inc.o
+ $(info $() LD $@)
+ @$(CC) $(CFLAGS) $^ -o $@
+
+main.o: main.c busybox/busybox.inc.h magic.h payload.h utils.h
+ $(info $() CC $@)
+ @$(CC) $(CFLAGS) -c $< -o $@
+
+%.inc.h: template/inc.h
+ $(info $() GEN $@)
+ @mkdir -p $(@D)
+ @sed 's@NAME@$(patsubst %.inc.h,%,$(@F))@g' $< > $@
+
+%.inc.s: template/inc.s
+ $(info $() GEN $@)
+ @mkdir -p $(@D)
+ @sed 's@NAME@$(patsubst %.inc.s,%,$(@F))@g' $< > $@
+
+busybox/busybox.inc.o: busybox/busybox.inc.s $(BBOX)
+ $(info $() ASM $@)
+ @cd $(dir $(word 2,$^)) && $(CC) $(CFLAGS) -c $(shell pwd)/$< -o $(shell pwd)/$@
+
+clean:
+ $(info $() CLEAN .)
+ @rm -rf main.o stabbish busybox
+
+.PHONY: test clean_test clean cleanall
diff --git a/magic.h b/src/magic.h
diff --git a/src/main.c b/src/main.c
@@ -0,0 +1,65 @@
+#define _GNU_SOURCE 1
+#include <elf.h>
+#include <malloc.h>
+#include <sched.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "busybox/busybox.inc.h"
+#include "magic.h"
+#include "payload.h"
+#include "utils.h"
+
+int main(int argc, char **argv) {
+ static int try_return = -1;
+
+ bool start_shell = argc > 1 && strcmp(argv[1], "--stabbish-shell") == 0;
+ if (start_shell)
+ shift(1);
+
+ fd trys(busybox, memfd_create("busybox", 0));
+ fd trys(rcfile, memfd_create("rcfile", 0));
+ fd trys(payload, elf_payload("/proc/self/exe"));
+
+ try(write(busybox, _inc_busybox, _inc_busybox_size));
+
+ setenv("ENV", pasprintf("/dev/fd/%i", rcfile), 1);
+ setenv("BUSYBOX", pasprintf("/dev/fd/%i", busybox), 1);
+
+ if (payload == ENOPAYLOAD) {
+ try(dprintf(rcfile, "echo No payload present, starting shell."));
+ } else {
+ setenv("PAYLOAD", pasprintf("/dev/fd/%i", payload), 1);
+ }
+
+ if (start_shell || payload == ENOPAYLOAD) {
+ argv[0] = strdup("ash");
+ return fexecve(busybox, argv, environ);
+ }
+
+ struct magic magic = magic_detect(payload);
+ if (magic.tool) {
+ fd trys(extracted, memfd_create("extracted", 0));
+ char *tool = strdup(magic.tool);
+ struct stdstream std = {payload, extracted, -1};
+
+ lseek(payload, SEEK_SET, 0);
+ try(memexecv(busybox, (char *[]){tool, NULL}, NULL, std));
+ dup2(extracted, payload);
+ free(tool);
+ }
+
+ char **bb_argv = malloc(sizeof(char *) * (argc + 4));
+ bb_argv[0] = strdup("ash");
+ bb_argv[1] = strdup("-c");
+ bb_argv[2] = pasprintf(". /dev/fd/%i", payload);
+ for (int i = 0; i <= argc; i++) {
+ bb_argv[i + 3] = argv[i];
+ }
+
+ return fexecve(busybox, bb_argv, environ);
+}
diff --git a/elf_payload.h b/src/payload.h
diff --git a/tmplt/inc.h b/src/template/inc.h
diff --git a/tmplt/inc.s b/src/template/inc.s
diff --git a/utils.h b/src/utils.h
diff --git a/test/.gitignore b/test/.gitignore
@@ -0,0 +1 @@
+*.stab
diff --git a/test/Makefile b/test/Makefile
@@ -0,0 +1,21 @@
+.SUFFIXES:
+.SECONDARY:
+
+TESTS=$(patsubst %.test, test.%, $(wildcard *.test))
+
+all: $(TESTS)
+
+test.%: %.stab
+ $(info $() TST $@)
+ @[ "$$($(<D)/$(<F) output)" = "$$($(<D)/$(<F) input)" ]
+
+%.stab: %.test ../src/stabbish
+ $(info $() GEN $@)
+ @echo '"$$@"' | cat $< - | sh -c '. ./$<; process' | cat $(word 2, $^) - > $@
+ @chmod +x $@
+
+clean:
+ $(info $() CLEAN test)
+ @rm -rf *.stab
+
+.PHONY: clean all test.%
diff --git a/test/bzip2.test b/test/bzip2.test
@@ -0,0 +1,11 @@
+input() {
+ type ls
+}
+
+output() {
+ echo "ls is ls"
+}
+
+process() {
+ bzip2 -c -
+}
diff --git a/test/gzip.test b/test/gzip.test
@@ -0,0 +1,11 @@
+input() {
+ type ls
+}
+
+output() {
+ echo "ls is ls"
+}
+
+process() {
+ gzip -c -
+}
diff --git a/test/plain.test b/test/plain.test
@@ -0,0 +1,11 @@
+input() {
+ type ls
+}
+
+output() {
+ echo "ls is ls"
+}
+
+process() {
+ cat -
+}
diff --git a/test/xz.test b/test/xz.test
@@ -0,0 +1,11 @@
+input() {
+ type ls
+}
+
+output() {
+ echo "ls is ls"
+}
+
+process() {
+ xz -c -
+}