From 689c4ac7141e4327cdd82b7ab245835ef035d8b5 Mon Sep 17 00:00:00 2001 From: Michael Mikovsky <77305074+Astatin3@users.noreply.github.com> Date: Fri, 20 Feb 2026 18:17:11 -0700 Subject: [PATCH] Add refrerence symbolic strings --- base62/src/{aes_decrypt.rs => aes.rs} | 54 ++++++++++++++++++++++++- base62/src/aes_encrypt.rs | 53 ------------------------ base62/src/base62.rs | 7 ++-- base62/src/lib.rs | 23 ++++++++--- build.sh | 3 ++ ush-obfuscate/Cargo.toml | 3 +- ush-obfuscate/src/env.rs | 6 ++- ush-obfuscate/src/obfuscate/obs_xor.rs | 3 -- ush-obfuscate/src/proc_impl_switcher.rs | 4 +- ush-obfuscate/src/symbolic_ref/mod.rs | 47 +++++++++++++++++++++ ush-payload/Cargo.toml | 2 +- ush-payload/src/main.rs | 44 ++++++++++---------- 12 files changed, 153 insertions(+), 96 deletions(-) rename base62/src/{aes_decrypt.rs => aes.rs} (55%) delete mode 100644 base62/src/aes_encrypt.rs diff --git a/base62/src/aes_decrypt.rs b/base62/src/aes.rs similarity index 55% rename from base62/src/aes_decrypt.rs rename to base62/src/aes.rs index adb0b34..72b0936 100644 --- a/base62/src/aes_decrypt.rs +++ b/base62/src/aes.rs @@ -1,7 +1,57 @@ -use aes::cipher::{BlockDecryptMut, KeyIvInit, block_padding::Pkcs7}; +use crate::{base62::Base62, hash}; +use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit}; +use cbc::cipher::block_padding::Pkcs7; use regex::Regex; -use crate::{Base62, hash}; +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 pseudo-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::::new(&key_salted.into(), &iv.into()) + .encrypt_padded_mut::(&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 { // Hash the env key to get a 32-byte (256-bit) AES key diff --git a/base62/src/aes_encrypt.rs b/base62/src/aes_encrypt.rs deleted file mode 100644 index 9820c58..0000000 --- a/base62/src/aes_encrypt.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::{base62::Base62, hash}; -use aes::cipher::{BlockEncryptMut, KeyIvInit}; -use cbc::cipher::block_padding::Pkcs7; - -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::::new(&key_salted.into(), &iv.into()) - .encrypt_padded_mut::(&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)) -} diff --git a/base62/src/base62.rs b/base62/src/base62.rs index eda55f4..902b96f 100644 --- a/base62/src/base62.rs +++ b/base62/src/base62.rs @@ -18,19 +18,20 @@ const ENCODING_RATIO: f64 = 8.0 / 5.954196310386875; // 8.0 / log2(62.0) impl Base62 { pub fn new(key: &[u8], nonce: usize) -> Self { // Hash key again, for the chance that this random function can be used to derive the key + // My solution to not being good at cryptography lol let key = hash(key); let mut charset: [char; 62] = [0 as char; 62]; // Create a vector of indices from 0 to 61 - let mut current_indicies = (0..62).map(|i| i as usize).collect::>(); + let mut current_indices = (0..62).map(|i| i as usize).collect::>(); // Loop through each byte in the key until all chars are filled for i in 0..62 as usize { let rand = STATIC_BYTE_MAP[(key[i as usize % key.len()] as usize + nonce) % 255]; - let index_index = rand as usize % current_indicies.len(); - let put_index = current_indicies.remove(index_index); + let index_index = rand as usize % current_indices.len(); + let put_index = current_indices.remove(index_index); charset[put_index] = BASE62_CHARS[i]; } diff --git a/base62/src/lib.rs b/base62/src/lib.rs index b364d01..f1ed8b3 100644 --- a/base62/src/lib.rs +++ b/base62/src/lib.rs @@ -1,11 +1,8 @@ -mod aes_decrypt; -mod aes_encrypt; -#[allow(dead_code)] +mod aes; mod base62; // Exports -pub use aes_decrypt::{decrypt_aes, decrypt_aes_lines}; -pub use aes_encrypt::{encrypt_aes, encrypt_aes_lines}; +pub use aes::{decrypt_aes, decrypt_aes_lines, encrypt_aes, encrypt_aes_lines}; pub use base62::Base62; pub const STATIC_IV: [u8; 16] = [ @@ -35,3 +32,19 @@ pub fn hash(input: &[u8]) -> [u8; 32] { hasher.update(input); hasher.finalize().into() } + +pub fn encode_usize(value: usize) -> Vec { + if value == 0 { + return vec![0]; + } + let bytes = value.to_be_bytes(); + let leading_zeros = bytes.iter().take_while(|&&b| b == 0).count(); + bytes[leading_zeros..].to_vec() +} + +pub fn decode_usize(bytes: &[u8]) -> usize { + let mut buf = [0u8; size_of::()]; + let offset = buf.len() - bytes.len(); + buf[offset..].copy_from_slice(bytes); + usize::from_be_bytes(buf) +} diff --git a/build.sh b/build.sh index 9b1650a..cb60b8e 100755 --- a/build.sh +++ b/build.sh @@ -5,6 +5,9 @@ # -Z build-std-features= \ # --profile minimize -p unshell-payload -- $@ +set -e + +OBFUSCATION_KEY=kjwerkwerkjbwejehrwhje \ cargo build --profile minimize -p ush-payload $@ export BINARY=./target/minimize/ush-payload diff --git a/ush-obfuscate/Cargo.toml b/ush-obfuscate/Cargo.toml index f0b0eac..5972930 100644 --- a/ush-obfuscate/Cargo.toml +++ b/ush-obfuscate/Cargo.toml @@ -6,8 +6,7 @@ authors.workspace = true include.workspace = true [features] -default = ["obfuscate_none"] -obfuscate_none = [] +default = [] obfuscate_aes = [] obfuscate_ref = [] diff --git a/ush-obfuscate/src/env.rs b/ush-obfuscate/src/env.rs index 4eb5a56..d79861b 100644 --- a/ush-obfuscate/src/env.rs +++ b/ush-obfuscate/src/env.rs @@ -2,8 +2,10 @@ const ENV_KEY_NAME: &str = "OBFUSCATION_KEY"; const BACKUP_ENV_KEY: &str = "OBFUSCATION_KEY_DO_NOT_USE"; pub fn get_encryption_key() -> String { - std::env::var(ENV_KEY_NAME).unwrap_or({ + if let Ok(key) = std::env::var(ENV_KEY_NAME) { + key + } else { println!("Using default encryption key!"); BACKUP_ENV_KEY.to_owned() - }) + } } diff --git a/ush-obfuscate/src/obfuscate/obs_xor.rs b/ush-obfuscate/src/obfuscate/obs_xor.rs index 314480a..25e8b0c 100644 --- a/ush-obfuscate/src/obfuscate/obs_xor.rs +++ b/ush-obfuscate/src/obfuscate/obs_xor.rs @@ -14,9 +14,6 @@ pub fn xor(input: TokenStream) -> TokenStream { 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(); diff --git a/ush-obfuscate/src/proc_impl_switcher.rs b/ush-obfuscate/src/proc_impl_switcher.rs index 230292b..05e9d98 100644 --- a/ush-obfuscate/src/proc_impl_switcher.rs +++ b/ush-obfuscate/src/proc_impl_switcher.rs @@ -30,7 +30,7 @@ macro_rules! delete { }; } -#[cfg(feature = "obfuscate_none")] +#[cfg(all(not(feature = "obfuscate_aes"), not(feature = "obfuscate_ref")))] pub mod proc_impl { use proc_macro::TokenStream; use syn::{LitStr, parse_macro_input}; @@ -61,6 +61,6 @@ pub mod proc_impl { unwrap_string!(xor); delete!(junk_asm); - unwrap_string!(sym); + passtrough!(sym, crate::symbolic_ref::sym_ref); unwrap_string!(sym_fn); } diff --git a/ush-obfuscate/src/symbolic_ref/mod.rs b/ush-obfuscate/src/symbolic_ref/mod.rs index 8b13789..62e4059 100644 --- a/ush-obfuscate/src/symbolic_ref/mod.rs +++ b/ush-obfuscate/src/symbolic_ref/mod.rs @@ -1 +1,48 @@ +use std::collections::HashMap; +use base62::{Base62, hash}; +use proc_macro::TokenStream; +use quote::quote; +use syn::{LitStr, parse_macro_input}; + +use crate::env::get_encryption_key; + +static mut SYM_COUNTER: Vec = Vec::new(); + +#[allow(static_mut_refs)] +pub fn get_symbol_number() -> usize { + unsafe { SYM_COUNTER.len() } +} + +#[allow(static_mut_refs)] +pub fn get_symbol(text: String) -> usize { + unsafe { + if let Some(n) = SYM_COUNTER.iter().position(|r| r == &text) { + n + } else { + SYM_COUNTER.push(text); + + SYM_COUNTER.len() - 1 + } + } +} + +pub fn sym_ref(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(); + + let n = get_symbol(original_name); + + let data = base62::encode_usize(n); + let key = hash(&get_encryption_key().as_bytes()); + + let encoded = format!("_{}_", Base62::encode_full(&data, &key)); + + println!("Aliased '{}' as '{encoded}'", lit_str.value()); + + // Expand to a static string literal + TokenStream::from(quote! { + #encoded + }) +} diff --git a/ush-payload/Cargo.toml b/ush-payload/Cargo.toml index edaef7e..7dd435b 100644 --- a/ush-payload/Cargo.toml +++ b/ush-payload/Cargo.toml @@ -8,7 +8,7 @@ edition = "2024" default = ["log"] log = ["unshell/log"] log_debug = ["unshell/log_debug"] -obfuscate = ["unshell/obfuscate_aes"] +obfuscate = ["unshell/obfuscate_ref"] [dependencies] unshell.path = "../" diff --git a/ush-payload/src/main.rs b/ush-payload/src/main.rs index d193e72..688ae83 100644 --- a/ush-payload/src/main.rs +++ b/ush-payload/src/main.rs @@ -1,46 +1,44 @@ #![macro_use] extern crate unshell; -use unshell::{ - Value, info, - obfuscate::{junk_asm, sym}, - tree::{Tree, TreeElement, symbols}, -}; +use unshell::{info, obfuscate::sym, tree::Tree}; fn main() { let mut manager = Tree::new(); manager.init_logger(); + println!("{}", sym!("TEST")); + info!("Test thing!"); info!("Test thing!"); - loop { - if test123(&mut manager) { - break; - } - } + // loop { + // if test123(&mut manager) { + // break; + // } + // } // println!("Test"); } -fn test123(manager: &mut Tree) -> bool { - let result = manager.send_message( - Value::String(sym!("Logger").to_string()), - Value::String(symbols::CMD_GET.to_string()), - ); +// fn test123(manager: &mut Tree) -> bool { +// let result = manager.send_message( +// Value::String(sym!("Logger").to_string()), +// Value::String(symbols::CMD_GET.to_string()), +// ); - junk_asm!(20.); +// junk_asm!(20.); - let is_null = result.is_null(); +// let is_null = result.is_null(); - // if let Ok(result) = serde_json::from_value::(result) { - // log(&result); - // } +// // if let Ok(result) = serde_json::from_value::(result) { +// // log(&result); +// // } - is_null +// is_null - // println!("Logger: {}", result); -} +// // println!("Logger: {}", result); +// } // use std::{any::Any, collections::HashMap, fs::File, io::Read};