C3/Src/WebController/Backend/Comms/DonutLibrary.cs

228 lines
8.3 KiB
C#

using System;
using System.Runtime.InteropServices;
// https://github.com/TheWover/donut/blob/master/lib/donut.h
namespace FSecure.C3.WebController.Comms
{
public class DonutLibrary
{
public enum ErrorCode
{
SUCCESS = 0,
FILE_NOT_FOUND = 1,
FILE_EMPTY = 2,
FILE_ACCESS = 3,
FILE_INVALID = 4,
NET_PARAMS = 5,
NO_MEMORY = 6,
INVALID_ARCH = 7,
INVALID_URL = 8,
URL_LENGTH = 9,
INVALID_PARAMETER = 10,
RANDOM = 11,
DLL_FUNCTION = 12,
ARCH_MISMATCH = 13,
DLL_PARAM = 14,
BYPASS_INVALID = 15,
NORELOC = 16,
INVALID_ENCODING = 17,
INVALID_ENGINE = 18,
COMPRESSION = 19,
INVALID_ENTROPY = 20,
MIXED_ASSEMBLY = 21,
}
// target architecture
public enum Architecture
{
ANY = -1, // just for vbs,js and xsl files
X86 = 1, // x86
X64 = 2, // AMD64
X84 = 3, // AMD64 + x86
}
// module type
public enum ModuleType
{
NET_DLL = 1, // .NET DLL. Requires class and method
NET_EXE = 2, // .NET EXE. Executes Main if no class and method provided
DLL = 3, // Unmanaged DLL, function is optional
EXE = 4, // Unmanaged EXE
VBS = 5, // VBScript
JS = 6, // JavaScript or JScript
}
// format type
public enum OutputFormat
{
BINARY = 1,
BASE64 = 2,
RUBY = 3,
C = 4,
PYTHON = 5,
POWERSHELL = 6,
CSHARP = 7,
HEX = 8,
}
// compression engine
public enum ComperssionEngine
{
NONE = 1,
APLIB = 2,
LZNT1 = 3, // COMPRESSION_FORMAT_LZNT1
XPRESS = 4, // COMPRESSION_FORMAT_XPRESS
XPRESS_HUFF = 5, // COMPRESSION_FORMAT_XPRESS_HUFF
}
// entropy level
public enum EntropyLevel
{
NONE = 1, // don't use any entropy
RANDOM = 2, // use random names
DEFAULT = 3, // use random names + symmetric encryption
}
// misc options
public enum ExitOpt
{
EXIT_THREAD = 1, // return to the caller which calls RtlExitUserThread
EXIT_PROCESS = 2, // call RtlExitUserProcess to terminate host process
}
// instance type
public enum InstanceType
{
PIC = 1, // Self-contained
URL = 2, // Download from remote server
}
// AMSI/WLDP options
public enum AmsiWldpBypass
{
NONE = 1, // Disables bypassing AMSI/WDLP
ABORT = 2, // If bypassing AMSI/WLDP fails, the loader stops running
CONTINUE = 3, // If bypassing AMSI/WLDP fails, the loader continues running
}
public static class Constants
{
public const int DONUT_MAX_NAME = 256; // maximum length of string for domain, class, method and parameter names
public const int DONUT_MAX_DLL = 8; // maximum number of DLL supported by instance
public const int DONUT_MAX_URL = 256;
public const int DONUT_MAX_MODNAME = 8;
public const int DONUT_SIG_LEN = 8; // 64-bit string to verify decryption ok
public const int DONUT_VER_LEN = 32;
public const int DONUT_DOMAIN_LEN = 8;
}
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct DonutConfig
{
public uint len, zlen; // original length of input file and compressed length
// general / misc options for loader
public int arch;
public int bypass;
public int compress; // engine to use when compressing file via RtlCompressBuffer
public int entropy; // entropy/encryption level
public int format; // output format for loader
public int exit_opt; // return to caller or invoke RtlExitUserProcess to terminate the host process
public int thread; // run entrypoint of unmanaged EXE as a thread. attempts to intercept calls to exit-related API
public ulong oep; // original entrypoint of target host file
// files in/out
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.DONUT_MAX_NAME)]
public string input;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.DONUT_MAX_NAME)]
public string output;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.DONUT_MAX_NAME)]
// .NET stuff
public string runtime;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.DONUT_MAX_NAME)]
public string domain;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.DONUT_MAX_NAME)]
public string cls;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.DONUT_MAX_NAME)]
public string method;
// command line for DLL/EXE
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.DONUT_MAX_NAME)]
public string param; // command line to use for unmanaged DLL/EXE and .NET DLL/EXE
public int unicode; // param is converted to UNICODE before being passed to DLL function
// HTTP staging information
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.DONUT_MAX_NAME)]
public string url;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Constants.DONUT_MAX_NAME)]
public string modname;
// DONUT_MODULE
public int mod_type;
public int mod_len;
public IntPtr mod; // points to DONUT_MODULE
// DONUT_INSTANCE
public int inst_type; // DONUT_INSTANCE_PIC or DONUT_INSTANCE_URL
public int inst_len; // size of DONUT_INSTANCE
public IntPtr inst; // points to DONUT_INSTNCE
// shellcode generated from configuration
public int pic_len; // size of loader/shellcode
public IntPtr pic; // points to loader/shellcode
}
public static byte[] GenerateShellcode(DonutConfig config)
{
var buffer = DonutCreate(config);
DonutDelete(config);
return buffer;
}
// Use DllImport to import the Win32 MessageBox function.
[DllImport("donut")]
private static extern int DonutCreate(ref DonutConfig donutConfig);
public class DonutException : Exception
{
public DonutException(string message) : base(message) { }
}
[DllImport("donut")]
private static extern int DonutDelete(ref DonutConfig donutConfig);
[DllImport("donut")]
private static extern IntPtr DonutError(int errorcode);
static string GetDonutError(ErrorCode errorcode)
{
IntPtr ptr = DonutError((int)errorcode);
return Marshal.PtrToStringAnsi(ptr);
}
private static byte[] DonutCreate(DonutConfig config)
{
var createErrorCode = (ErrorCode)DonutCreate(ref config);
if (createErrorCode != ErrorCode.SUCCESS)
{
var errorMessage = GetDonutError(createErrorCode);
throw new DonutException($"Donut error: {errorMessage}");
}
var buffer = new byte[config.pic_len];
Marshal.Copy(config.pic, buffer, 0, config.pic_len);
return buffer;
}
private static void DonutDelete(DonutConfig config)
{
var delErrorCode = (ErrorCode)DonutDelete(ref config);
if (delErrorCode != ErrorCode.SUCCESS)
{
var errorMessage = GetDonutError(delErrorCode);
throw new DonutException($"Donut error: {errorMessage}");
}
}
}
}