use alloc::string::{String, ToString};
use core::str::FromStr;
use azathoth_utils::codec::{Codec, Decoder, Encoder};
use azathoth_utils::errors::{AzUtilErrorCode, AzUtilResult};
use azathoth_utils::formatter::{FDisplay, FormatSpec, WriteBuffer};
use crate::errors::DonutError;
#[repr(C)]
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "PascalCase"))]
pub enum EntropyLevel {
#[default]
None = 0,
High = 1,
Light = 2,
Average = 3,
}
impl Codec for EntropyLevel {
fn encode(&self, enc: &mut Encoder) -> AzUtilResult<()> {
enc.push_u8(self.clone() as u8)
}
fn decode(dec: &mut Decoder) -> AzUtilResult<Self> {
let b = dec.read_u8()?;
match b {
0 => Ok(EntropyLevel::None),
1 => Ok(EntropyLevel::High),
2 => Ok(EntropyLevel::Light),
3 => Ok(EntropyLevel::Average),
_ => Err(AzUtilErrorCode::CodecError)
}
}
}
impl FromStr for EntropyLevel {
type Err = DonutError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"none" => Ok(Self::None),
"heavy" => Ok(Self::High),
"light" => Ok(Self::Light),
"average" => Ok(Self::Average),
_ => Err(DonutError::Unknown {e: s.to_string()}),
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "PascalCase"))]
pub enum DonutValidFileType {
Dll {
dotnet: bool
},
PE {
dotnet: bool
},
Script {
script_type: DonutValidScriptType
},
SharedObject,
ELF,
#[default]
Unknown
}
#[cfg(feature = "std")]
impl std::fmt::Display for DonutValidFileType {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
DonutValidFileType::Dll { dotnet } => { write!(fmt, "DLL -> Dotnet: {dotnet}") }
DonutValidFileType::PE {dotnet} => { write!(fmt, "PE -> Dotnet: {dotnet}") }
DonutValidFileType::ELF => { write!(fmt, "ELF") }
DonutValidFileType::SharedObject => { write!(fmt, "SharedObject -> ELF") }
DonutValidFileType::Script {..} => { write!(fmt, "Script") }
_ => write!(fmt, "Unknown DonutValidFileType")
}
}
}
impl Codec for DonutValidFileType {
fn encode(&self, enc: &mut Encoder) -> AzUtilResult<()> {
match self {
Self::Dll { dotnet } => {
enc.push_u8(0)?;
enc.push_u8(*dotnet as u8)
}
Self::PE { dotnet } => {
enc.push_u8(1)?;
enc.push_u8(*dotnet as u8)
}
Self::Script { script_type } => {
enc.push_u8(2)?;
script_type.encode(enc)
}
Self::SharedObject => enc.push_u8(3),
Self::ELF => enc.push_u8(4),
Self::Unknown => enc.push_u8(5),
}
}
fn decode(dec: &mut Decoder) -> AzUtilResult<Self> {
match dec.read_u8()? {
0 => Ok(Self::Dll { dotnet: dec.read_u8()? != 0 }),
1 => Ok(Self::PE { dotnet: dec.read_u8()? != 0 }),
2 => {
let s = DonutValidScriptType::decode(dec).map_err(|_| AzUtilErrorCode::CodecError)?;
Ok(Self::Script { script_type: s })
}
3 => Ok(Self::SharedObject),
4 => Ok(Self::ELF),
5 => Ok(Self::Unknown),
_ => Err(AzUtilErrorCode::CodecError),
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Default, PartialOrd, PartialEq)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "PascalCase"))]
pub enum DonutValidScriptType {
JScript,
Python,
VBScript,
#[default]
WScript,
Lua,
}
impl Codec for DonutValidScriptType {
fn encode(&self, enc: &mut Encoder) -> AzUtilResult<()> {
let val = match self {
Self::JScript => 0,
Self::Python => 1,
Self::VBScript => 2,
Self::WScript => 3,
Self::Lua => 4,
};
enc.push_u8(val)
}
fn decode(dec: &mut Decoder) -> AzUtilResult<Self> {
match dec.read_u8()? {
0 => Ok(Self::JScript),
1 => Ok(Self::Python),
2 => Ok(Self::VBScript),
3 => Ok(Self::WScript),
4 => Ok(Self::Lua),
_ => Err(AzUtilErrorCode::CodecError),
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Default, PartialOrd, PartialEq)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "PascalCase"))]
pub enum OutputFormat {
Ruby,
C,
CSharp,
Powershell,
Rust,
Python,
#[default]
Raw,
Hex,
Uuid,
Base64,
Golang
}
impl Codec for OutputFormat {
fn encode(&self, enc: &mut Encoder) -> AzUtilResult<()> {
let val = match self {
Self::Ruby => 0,
Self::C => 1,
Self::CSharp => 2,
Self::Powershell => 3,
Self::Rust => 4,
Self::Python => 5,
Self::Raw => 6,
Self::Hex => 7,
Self::Uuid => 8,
Self::Base64 => 9,
Self::Golang => 10,
};
enc.push_u8(val)
}
fn decode(dec: &mut Decoder) -> AzUtilResult<Self> {
match dec.read_u8()? {
0 => Ok(Self::Ruby),
1 => Ok(Self::C),
2 => Ok(Self::CSharp),
3 => Ok(Self::Powershell),
4 => Ok(Self::Rust),
5 => Ok(Self::Python),
6 => Ok(Self::Raw),
7 => Ok(Self::Hex),
8 => Ok(Self::Uuid),
9 => Ok(Self::Base64),
10 => Ok(Self::Golang),
_ => Err(AzUtilErrorCode::CodecError),
}
}
}
impl From<&String> for OutputFormat {
fn from(value: &String) -> Self {
match value.to_ascii_lowercase().as_str() {
"ruby" | "rb" => Self::Ruby,
"c" => Self::C,
"csharp" | "cs" => Self::CSharp,
"powershell" | "ps1" => Self::Powershell,
"rust" | "rs" => Self::Rust,
"python" | "py" => Self::Python,
"hex" => Self::Hex,
"uuid" => Self::Uuid,
"base64" => Self::Base64,
"golang" | "go" => Self::Golang,
_ => Self::Raw
}
}
}
impl OutputFormat {
pub fn extension(self) -> &'static str {
match self {
OutputFormat::Ruby => "rb",
OutputFormat::C => "c",
OutputFormat::CSharp => "cs",
OutputFormat::Powershell => "ps1",
OutputFormat::Rust => "rs",
OutputFormat::Python => "py",
OutputFormat::Raw => "bin",
OutputFormat::Hex => "hex",
OutputFormat::Uuid => "uuid",
OutputFormat::Base64 => "b64",
OutputFormat::Golang => "go"
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Default, PartialOrd, PartialEq)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "PascalCase"))]
pub enum ExitMethod {
#[default]
ExitThread = 0,
ExitProcess = 1,
NeverExit = 2
}
impl From<u8> for ExitMethod {
fn from(value: u8) -> Self {
match value {
0 => Self::ExitThread,
1 => Self::ExitProcess,
2 => Self::NeverExit,
_ => Self::ExitProcess
}
}
}
impl From<ExitMethod> for u8 {
fn from(value: ExitMethod) -> Self {
match value {
ExitMethod::ExitThread => 0,
ExitMethod::ExitProcess => 1,
ExitMethod::NeverExit => 2,
}
}
}
impl Codec for ExitMethod {
fn encode(&self, enc: &mut Encoder) -> AzUtilResult<()> {
enc.push_u8(self.clone() as u8)
}
fn decode(dec: &mut Decoder) -> AzUtilResult<Self>
where
Self: Sized,
{
let b = dec.read_u8()?;
Ok(b.into())
}
}
impl FromStr for ExitMethod {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"exitthread" => Ok(ExitMethod::ExitThread),
"exitprocess" => Ok(ExitMethod::ExitProcess),
"exitblock" => Ok(ExitMethod::NeverExit),
_ => Err("must be 'ExitThread', 'ExitProcess' or 'ExitBlock'"),
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Default, PartialOrd, PartialEq)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "PascalCase"))]
pub enum InstanceType {
#[default]
Http = 0,
Embedded = 1,
}
#[cfg(feature="std")]
impl std::fmt::Display for InstanceType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let str = match self {
InstanceType::Http => "http",
InstanceType::Embedded => "embedded",
}.to_string();
write!(f, "{str}")
}
}
impl Codec for InstanceType {
fn encode(&self, enc: &mut Encoder) -> AzUtilResult<()> {
enc.push_u8(self.clone() as u8)
}
fn decode(dec: &mut Decoder) -> AzUtilResult<Self> {
let b = dec.read_u8()?;
match b {
0 => Ok(Self::Http),
1 => Ok(Self::Embedded),
_ => Err(AzUtilErrorCode::CodecError)
}
}
}
impl FromStr for InstanceType {
type Err = DonutError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"http" => Ok(Self::Http),
"embedded" => Ok(Self::Embedded),
_ => Err(DonutError::Unknown {e: s.to_string()}),
}
}
}
impl FromStr for AmsiBypassTechnique {
type Err = DonutError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"patch_scan_buffer" => Ok(Self::PatchAmsiScanBuffer),
"patch_dll_export" => Ok(Self::PatchAmsiDllExport),
"patch_dispatch_table" => Ok(Self::PatchAmsiDispatchTable),
"none" => Ok(Self::None),
_ => Err(DonutError::Unknown {e: s.to_string()}),
}
}
}
#[repr(u8)]
#[derive(Debug, Clone, Default, PartialOrd, PartialEq)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "PascalCase"))]
pub enum AmsiBypassTechnique {
#[default]
None = 0,
PatchAmsiScanBuffer = 1,
PatchAmsiDllExport = 2,
PatchAmsiDispatchTable = 3,
}
impl Codec for AmsiBypassTechnique {
fn encode(&self, enc: &mut Encoder) -> AzUtilResult<()> {
enc.push_u8(self.clone() as u8)
}
fn decode(dec: &mut Decoder) -> AzUtilResult<Self> {
let b = dec.read_u8()?;
match b {
0 => Ok(Self::None),
1 => Ok(Self::PatchAmsiScanBuffer),
2 => Ok(Self::PatchAmsiDllExport),
3 => Ok(Self::PatchAmsiDispatchTable),
_ => Err(AzUtilErrorCode::CodecError)
}
}
}
#[repr(u8)]
#[derive(Debug, Clone, Default, PartialOrd, PartialEq)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "PascalCase"))]
pub enum EtwBypassTechnique {
#[default]
None = 0,
PatchEtwEventWrite = 1,
EtwDisableTracing = 2
}
impl Codec for EtwBypassTechnique {
fn encode(&self, enc: &mut Encoder) -> AzUtilResult<()> {
enc.push_u8(self.clone() as u8)
}
fn decode(dec: &mut Decoder) -> AzUtilResult<Self> {
let b = dec.read_u8()?;
match b {
0 => Ok(Self::None),
1 => Ok(Self::PatchEtwEventWrite),
2 => Ok(Self::EtwDisableTracing),
_ => Err(AzUtilErrorCode::CodecError)
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Default, PartialOrd, PartialEq)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "PascalCase"))]
pub enum Arch {
X86 = 0,
#[default]
X86_64 = 1,
X64 = 2
}
impl Codec for Arch {
fn encode(&self, enc: &mut Encoder) -> AzUtilResult<()> {
enc.push_u8(self.clone() as u8)
}
fn decode(dec: &mut Decoder) -> AzUtilResult<Self> {
let b = dec.read_u8()?;
match b {
0 => Ok(Self::X86),
1 => Ok(Self::X86_64),
2 => Ok(Self::X64),
_ => Err(AzUtilErrorCode::CodecError)
}
}
}
impl FromStr for Arch {
type Err = DonutError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"x86" => Ok(Self::X86),
"x64" | "x86_64" => Ok(Self::X64),
_ => Err(DonutError::Unknown {e: s.to_string()}),
}
}
}
#[repr(u8)]
#[derive(PartialEq, Clone, PartialOrd, Copy, Debug, Default)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "PascalCase"))]
pub enum DonutCryptoProvider {
#[default]
None = 0,
Xor = 1,
Aes = 2,
}
impl FDisplay for DonutCryptoProvider {
fn fmt<W: WriteBuffer>(&self, w: &mut W, _spec: &FormatSpec) -> AzUtilResult<()> {
let str = match self {
DonutCryptoProvider::None => "none",
DonutCryptoProvider::Xor => "xor",
DonutCryptoProvider::Aes => "aes",
};
w.write_str(str)
}
}
impl From<DonutCryptoProvider> for u8 {
fn from(value: DonutCryptoProvider) -> Self {
match value {
DonutCryptoProvider::None => 0,
DonutCryptoProvider::Xor => 1,
DonutCryptoProvider::Aes => 2,
}
}
}
impl From<u8> for DonutCryptoProvider {
fn from(v: u8) -> Self {
match v {
2 => Self::Aes,
1 => Self::Xor,
_ => Self::None
}
}
}
impl Codec for DonutCryptoProvider {
fn encode(&self, enc: &mut Encoder) -> AzUtilResult<()> {
enc.push_u8(*self as u8)
}
fn decode(dec: &mut Decoder) -> AzUtilResult<Self> {
let b = dec.read_u8()?;
match b {
2 => Ok(Self::Aes),
1 => Ok(Self::Xor),
0 => Ok(Self::None),
_ => Err(AzUtilErrorCode::CodecError)
}
}
}
impl FromStr for DonutCryptoProvider {
type Err = DonutError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"aes" => Ok(Self::Aes),
"xor" => Ok(Self::Xor),
"none" => Ok(Self::None),
_ => Err(DonutError::Unknown {e: s.to_string()}),
}
}
}
#[cfg(feature = "std")]
impl std::fmt::Display for DonutCryptoProvider {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::None => write!(f, "none"),
Self::Xor => write!(f, "xor"),
Self::Aes => write!(f, "aes"),
}
}
}