stabbish

:)
git clone https://git.sr.ht/~ashymad/stabbish
Log | Files | Refs | README | LICENSE

commit 423d961699ca3e8a0c77d91ce8dd1476b1c6aac9
parent 0f23ea8aeebe9162736b0f1ffc8f99a900cab5ac
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date:   Tue,  7 Jul 2026 02:06:34 +0200

Directories

Diffstat:
D.gitignore | 6------
MMakefile | 104++++++++-----------------------------------------------------------------------
Aconfig.mk | 5+++++
Adeps/.gitignore | 1+
Adeps/Makefile | 35+++++++++++++++++++++++++++++++++++
Rbbox/config -> deps/busybox.config | 0
Dmain.c | 65-----------------------------------------------------------------
Asrc/.gitignore | 3+++
Asrc/Makefile | 35+++++++++++++++++++++++++++++++++++
Rmagic.h -> src/magic.h | 0
Asrc/main.c | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Relf_payload.h -> src/payload.h | 0
Rtmplt/inc.h -> src/template/inc.h | 0
Rtmplt/inc.s -> src/template/inc.s | 0
Rutils.h -> src/utils.h | 0
Atest/.gitignore | 1+
Atest/Makefile | 21+++++++++++++++++++++
Atest/bzip2.test | 11+++++++++++
Atest/gzip.test | 11+++++++++++
Atest/plain.test | 11+++++++++++
Atest/xz.test | 11+++++++++++
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 - +}