Remove unshell_crypt, and change some of the naming to make it more accurate

This commit is contained in:
Michael Mikovsky
2026-02-08 12:56:52 -07:00
parent d089c2a8aa
commit ebeaa29d5b
16 changed files with 197 additions and 466 deletions
Generated
+16 -23
View File
@@ -1608,9 +1608,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hybrid-array"
version = "0.4.6"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b41fb3dc24fe72c2e3a4685eed55917c2fb228851257f4a8f2d985da9443c3e5"
checksum = "e1b229d73f5803b562cc26e4da0396c8610a4ee209f4fac8fa4f8d709166dc45"
dependencies = [
"typenum",
]
@@ -2965,9 +2965,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.12.2"
version = "1.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
dependencies = [
"aho-corasick",
"memchr",
@@ -2977,9 +2977,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.13"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
dependencies = [
"aho-corasick",
"memchr",
@@ -2988,9 +2988,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.8.8"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
[[package]]
name = "renderdoc-sys"
@@ -3948,20 +3948,6 @@ dependencies = [
"unshell-obfuscate",
]
[[package]]
name = "unshell-crypt"
version = "0.1.0"
dependencies = [
"aes",
"block-padding 0.4.2",
"cbc",
"getrandom 0.3.4",
"hex",
"hex-literal",
"regex",
"sha2",
]
[[package]]
name = "unshell-gui"
version = "0.1.0"
@@ -3997,12 +3983,19 @@ dependencies = [
name = "unshell-obfuscate"
version = "0.1.0"
dependencies = [
"aes",
"block-padding 0.4.2",
"cbc",
"getrandom 0.3.4",
"hex",
"hex-literal",
"proc-macro2",
"quote",
"rand 0.9.2",
"regex",
"sha2",
"static_init",
"syn 2.0.114",
"unshell-crypt",
]
[[package]]
+1 -1
View File
@@ -20,7 +20,7 @@ members = [
"unshell-server", "unshell-payload",
# Libraries
"unshell-manager", "unshell-obfuscate", "unshell-crypt"
"unshell-manager", "unshell-obfuscate"
, "core-modules/server2"]
[features]
-256
View File
@@ -1,256 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "aes"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "aho-corasick"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "block-padding"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
dependencies = [
"generic-array",
]
[[package]]
name = "block-padding"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d28ed5f5f65056148fd25e1a596b5b6d9e772270abf9a9085d7cbfbf26c563"
dependencies = [
"hybrid-array",
]
[[package]]
name = "cbc"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
dependencies = [
"cipher",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
]
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "generic-array"
version = "0.14.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
]
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hex-literal"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1"
[[package]]
name = "hybrid-array"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f471e0a81b2f90ffc0cb2f951ae04da57de8baa46fa99112b062a5173a5088d0"
dependencies = [
"typenum",
]
[[package]]
name = "inout"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [
"block-padding 0.3.3",
"generic-array",
]
[[package]]
name = "libc"
version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "regex"
version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "typenum"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]]
name = "unshell-crypt"
version = "0.1.0"
dependencies = [
"aes",
"block-padding 0.4.1",
"cbc",
"getrandom",
"hex",
"hex-literal",
"regex",
"sha2",
]
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wit-bindgen"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
-16
View File
@@ -1,16 +0,0 @@
[package]
name = "unshell-crypt"
version.workspace = true
edition.workspace = true
authors.workspace = true
include.workspace = true
[dependencies]
aes = "0.8.4"
block-padding = "0.4.1"
cbc = "0.1.2"
getrandom = "0.3.4"
hex = "0.4.3"
hex-literal = "1.1.0"
regex = "1.12.2"
sha2 = "0.10.9"
-5
View File
@@ -1,5 +0,0 @@
# unshell-crypt
Code for encryption and obfuscation
This must be in a seperate project because unshell-obfuscate and unshell-lib depend on this. If it were to be included in unshell-lib, there would be a circular dependency.
+10 -1
View File
@@ -12,7 +12,16 @@ obfuscate = []
proc-macro = true
[dependencies]
unshell-crypt = {path = "../unshell-crypt"}
aes = "0.8.4"
block-padding = "0.4.1"
cbc = "0.1.2"
getrandom = "0.3.4"
hex = "0.4.3"
hex-literal = "1.1.0"
regex = "1.12.2"
sha2 = "0.10.9"
# unshell-crypt = {path = "../unshell-crypt"}
# Common
static_init = { workspace = true }
@@ -1,58 +1,3 @@
use crate::{base62::Base62, hash};
use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
use cbc::cipher::block_padding::Pkcs7;
use regex::Regex;
fn pkcs7_padded_length(input_len: usize) -> usize {
let block_size = 16;
((input_len / block_size) + 1) * block_size
}
pub fn encrypt_aes(plaintext: &str, key_str: &str, iv: [u8; 16]) -> String {
let plaintext = plaintext.as_bytes();
// Hash the env key to get a 32-byte (256-bit) AES key
let key = hash(key_str.as_bytes());
// Generate a psudo-random salt byte based on the plaintext
// I hope this does not break the encryption.
let mut salt = 0;
for byte in plaintext {
salt ^= byte;
}
let mut key_salted = key.clone();
// Salt the key by XORing the salt byte with all the key bytes.
// This ensures that the "hash" generated from the plaintext will
// make the encrypted result extremely different.
for i in 0..32 {
key_salted[i] ^= salt;
}
let buf_len = pkcs7_padded_length(plaintext.len());
let mut buf = vec![0u8; buf_len];
let pt_len = plaintext.len();
buf[..pt_len].copy_from_slice(&plaintext);
let mut ct = cbc::Encryptor::<aes::Aes256>::new(&key_salted.into(), &iv.into())
.encrypt_padded_mut::<Pkcs7>(&mut buf, pt_len)
.unwrap()
.to_vec();
// Add the salt byte to the key byte,
ct.insert(0, salt);
// Encode result in base62
Base62::encode_full(&ct, &key)
}
pub fn encrypt_aes_lines(plaintext: &str, key_str: &str, iv: [u8; 16]) -> String {
format!("_{}_", encrypt_aes(plaintext, key_str, iv))
}
pub fn decrypt_aes(input: &str, key_str: &str, iv: [u8; 16]) -> Result<String, String> {
// Hash the env key to get a 32-byte (256-bit) AES key
let mut key = hash(key_str.as_bytes());
@@ -0,0 +1,54 @@
use crate::crypt::{base62::Base62, hash};
use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
use cbc::cipher::block_padding::Pkcs7;
use regex::Regex;
fn pkcs7_padded_length(input_len: usize) -> usize {
let block_size = 16;
((input_len / block_size) + 1) * block_size
}
pub fn encrypt_aes(plaintext: &str, key_str: &str, iv: [u8; 16]) -> String {
let plaintext = plaintext.as_bytes();
// Hash the env key to get a 32-byte (256-bit) AES key
let key = hash(key_str.as_bytes());
// Generate a psudo-random salt byte based on the plaintext
// I hope this does not break the encryption.
let mut salt = 0;
for byte in plaintext {
salt ^= byte;
}
let mut key_salted = key.clone();
// Salt the key by XORing the salt byte with all the key bytes.
// This ensures that the "hash" generated from the plaintext will
// make the encrypted result extremely different.
for i in 0..32 {
key_salted[i] ^= salt;
}
let buf_len = pkcs7_padded_length(plaintext.len());
let mut buf = vec![0u8; buf_len];
let pt_len = plaintext.len();
buf[..pt_len].copy_from_slice(&plaintext);
let mut ct = cbc::Encryptor::<aes::Aes256>::new(&key_salted.into(), &iv.into())
.encrypt_padded_mut::<Pkcs7>(&mut buf, pt_len)
.unwrap()
.to_vec();
// Add the salt byte to the key byte,
ct.insert(0, salt);
// Encode result in base62
Base62::encode_full(&ct, &key)
}
pub fn encrypt_aes_lines(plaintext: &str, key_str: &str, iv: [u8; 16]) -> String {
format!("_{}_", encrypt_aes(plaintext, key_str, iv))
}
@@ -1,4 +1,4 @@
use crate::{STATIC_BYTE_MAP, hash};
use crate::crypt::{STATIC_BYTE_MAP, hash};
// Randomly mapped Base62 characters
pub struct Base62 {
@@ -1,4 +1,4 @@
pub mod aes;
pub mod aes_encrypt;
pub mod base62;
pub const ENV_KEY_NAME: &str = "OBFUSCATION_KEY";
@@ -31,5 +31,3 @@ pub fn hash(input: &[u8]) -> [u8; 32] {
hasher.update(input);
hasher.finalize().into()
}
pub use getrandom::fill;
+2
View File
@@ -8,6 +8,8 @@ use syn::parse_macro_input;
mod format_helper;
use format_helper::*;
mod crypt;
#[allow(dead_code, unused_imports)]
mod no_obfuscate;
+16 -4
View File
@@ -1,5 +1,17 @@
mod junk_asm;
mod strings;
mod obs_junk_asm;
mod obs_xor;
mod sym_aes_strings;
pub use junk_asm::junk_asm;
pub use strings::*;
pub use obs_junk_asm::junk_asm;
pub use sym_aes_strings::*;
use crate::crypt::{BACKUP_ENV_KEY, ENV_KEY_NAME};
fn get_encryption_key() -> String {
std::env::var(ENV_KEY_NAME).unwrap_or({
println!("Using default encryption key!");
BACKUP_ENV_KEY.to_owned()
})
}
// pub fn
@@ -0,0 +1,52 @@
use getrandom::fill;
use proc_macro::TokenStream;
use quote::quote;
use syn::{LitStr, parse_macro_input};
/// XOR encrypt strings
pub fn xor(input: TokenStream) -> TokenStream {
// Parse the input as a string literal
let lit_str = parse_macro_input!(input as LitStr);
let original_str = lit_str.value();
// Handle empty strings explicitly
if original_str.is_empty() {
return TokenStream::from(quote! { String::new() });
}
// --- Obfuscated Branch Logic ---
// This code runs at compile-time
let str_bytes = original_str.as_bytes();
let len = str_bytes.len();
// 1. Generate a unique, random key for this string
let mut key = vec![0u8; len];
fill(&mut key).expect("Failed to get random bytes for XOR key");
// 2. XOR the string with the key
let mut obfuscated = Vec::with_capacity(len);
for i in 0..len {
obfuscated.push(str_bytes[i] ^ key[i]);
}
// 3. This is the code that will be injected into the user's binary
// It runs at *runtime* to decrypt the string.
let obfuscated_expansion = quote! {
{
// These static arrays are stored directly in your binary
static OBFUSCATED_DATA: [u8; #len] = [ #( #obfuscated ),* ];
static KEY_DATA: [u8; #len] = [ #( #key ),* ];
let mut decrypted = Vec::with_capacity(#len);
for i in 0..#len {
decrypted.push(OBFUSCATED_DATA[i] ^ KEY_DATA[i]);
}
// We can trust this since the source was a valid String literal
String::from_utf8(decrypted).unwrap()
}
};
TokenStream::from(obfuscated_expansion)
}
-101
View File
@@ -1,101 +0,0 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{ItemFn, LitStr, parse_macro_input};
use unshell_crypt::{BACKUP_ENV_KEY, ENV_KEY_NAME, STATIC_IV, aes::encrypt_aes_lines, fill};
#[cfg(feature = "obfuscate")]
#[static_init::dynamic]
static KEY: String = {
std::env::var(ENV_KEY_NAME).unwrap_or({
println!("Using default encryption key!");
BACKUP_ENV_KEY.to_owned()
})
};
// If there isn't any encryption
#[cfg(not(feature = "obfuscate"))]
#[static_init::dynamic]
static KEY: String = "".to_string();
pub fn obfuscated_symbol(_attr: TokenStream, item: TokenStream) -> TokenStream {
// Parse the input function
let func = parse_macro_input!(item as ItemFn);
// Get the original function name
let fn_name = func.sig.ident.to_string();
// Generate the new, obfuscated name
let obfuscated_name = encrypt_aes_lines(&fn_name, &KEY, STATIC_IV);
// Create a new string literal for the name
let new_name_lit = LitStr::new(&obfuscated_name, func.sig.ident.span());
// Re-build the function, but add #[no_mangle]
// and rename the *exported* symbol via #[export_name]
TokenStream::from(quote! {
#[unsafe(export_name = #new_name_lit)]
#func
})
}
pub fn symbol(input: TokenStream) -> TokenStream {
// Parse the input as a string literal
let lit_str = parse_macro_input!(input as LitStr);
let original_name = lit_str.value();
// Generate the exact same obfuscated name
let obfuscated_name = encrypt_aes_lines(&original_name, &KEY, STATIC_IV);
// Expand to a static string literal
TokenStream::from(quote! {
#obfuscated_name
})
}
pub fn obs(input: TokenStream) -> TokenStream {
// Parse the input as a string literal
let lit_str = parse_macro_input!(input as LitStr);
let original_str = lit_str.value();
// Handle empty strings explicitly
if original_str.is_empty() {
return TokenStream::from(quote! { String::new() });
}
// --- Obfuscated Branch Logic ---
// This code runs at compile-time
let str_bytes = original_str.as_bytes();
let len = str_bytes.len();
// 1. Generate a unique, random key for this string
let mut key = vec![0u8; len];
fill(&mut key).expect("Failed to get random bytes for XOR key");
// 2. XOR the string with the key
let mut obfuscated = Vec::with_capacity(len);
for i in 0..len {
obfuscated.push(str_bytes[i] ^ key[i]);
}
// 3. This is the code that will be injected into the user's binary
// It runs at *runtime* to decrypt the string.
let obfuscated_expansion = quote! {
{
// These static arrays are stored directly in your binary
static OBFUSCATED_DATA: [u8; #len] = [ #( #obfuscated ),* ];
static KEY_DATA: [u8; #len] = [ #( #key ),* ];
let mut decrypted = Vec::with_capacity(#len);
for i in 0..#len {
decrypted.push(OBFUSCATED_DATA[i] ^ KEY_DATA[i]);
}
// We can trust this since the source was a valid String literal
String::from_utf8(decrypted).unwrap()
}
};
TokenStream::from(obfuscated_expansion)
}
@@ -0,0 +1,44 @@
use crate::crypt::{BACKUP_ENV_KEY, ENV_KEY_NAME, STATIC_IV, aes_encrypt::encrypt_aes_lines};
use proc_macro::TokenStream;
use quote::quote;
use syn::{ItemFn, LitStr, parse_macro_input};
use crate::obfuscate::get_encryption_key;
/// Obfuscate function names by encrypting in AES
pub fn aes_fn_name(_attr: TokenStream, item: TokenStream) -> TokenStream {
// Parse the input function
let func = parse_macro_input!(item as ItemFn);
// Get the original function name
let fn_name = func.sig.ident.to_string();
// Generate the new, obfuscated name
let obfuscated_name = encrypt_aes_lines(&fn_name, &get_encryption_key(), STATIC_IV);
// Create a new string literal for the name
let new_name_lit = LitStr::new(&obfuscated_name, func.sig.ident.span());
// Re-build the function, but add #[no_mangle]
// and rename the *exported* symbol via #[export_name]
TokenStream::from(quote! {
#[unsafe(export_name = #new_name_lit)]
#func
})
}
/// Obfuscate strings by encrypting in AES
pub fn aes_str(input: TokenStream) -> TokenStream {
// Parse the input as a string literal
let lit_str = parse_macro_input!(input as LitStr);
let original_name = lit_str.value();
// Generate the exact same obfuscated name
let obfuscated_name = encrypt_aes_lines(&original_name, &get_encryption_key(), STATIC_IV);
// Expand to a static string literal
TokenStream::from(quote! {
#obfuscated_name
})
}