diff --git a/Cargo.lock b/Cargo.lock index 6bf696c..f8e4722 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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]] diff --git a/Cargo.toml b/Cargo.toml index 29801ec..3dab831 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ members = [ "unshell-server", "unshell-payload", # Libraries - "unshell-manager", "unshell-obfuscate", "unshell-crypt" + "unshell-manager", "unshell-obfuscate" , "core-modules/server2"] [features] diff --git a/unshell-crypt/Cargo.lock b/unshell-crypt/Cargo.lock deleted file mode 100644 index ea3e2d3..0000000 --- a/unshell-crypt/Cargo.lock +++ /dev/null @@ -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" diff --git a/unshell-crypt/Cargo.toml b/unshell-crypt/Cargo.toml deleted file mode 100644 index 5d36404..0000000 --- a/unshell-crypt/Cargo.toml +++ /dev/null @@ -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" diff --git a/unshell-crypt/README.md b/unshell-crypt/README.md deleted file mode 100644 index e83ac68..0000000 --- a/unshell-crypt/README.md +++ /dev/null @@ -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. diff --git a/unshell-obfuscate/Cargo.toml b/unshell-obfuscate/Cargo.toml index cb70025..930a835 100644 --- a/unshell-obfuscate/Cargo.toml +++ b/unshell-obfuscate/Cargo.toml @@ -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 } diff --git a/unshell-crypt/src/aes.rs b/unshell-obfuscate/src/crypt/aes_decrypt.rs similarity index 54% rename from unshell-crypt/src/aes.rs rename to unshell-obfuscate/src/crypt/aes_decrypt.rs index 12547c6..70bd868 100644 --- a/unshell-crypt/src/aes.rs +++ b/unshell-obfuscate/src/crypt/aes_decrypt.rs @@ -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::::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 let mut key = hash(key_str.as_bytes()); diff --git a/unshell-obfuscate/src/crypt/aes_encrypt.rs b/unshell-obfuscate/src/crypt/aes_encrypt.rs new file mode 100644 index 0000000..f06d23f --- /dev/null +++ b/unshell-obfuscate/src/crypt/aes_encrypt.rs @@ -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::::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/unshell-crypt/src/base62.rs b/unshell-obfuscate/src/crypt/base62.rs similarity index 99% rename from unshell-crypt/src/base62.rs rename to unshell-obfuscate/src/crypt/base62.rs index eda55f4..e2504f0 100644 --- a/unshell-crypt/src/base62.rs +++ b/unshell-obfuscate/src/crypt/base62.rs @@ -1,4 +1,4 @@ -use crate::{STATIC_BYTE_MAP, hash}; +use crate::crypt::{STATIC_BYTE_MAP, hash}; // Randomly mapped Base62 characters pub struct Base62 { diff --git a/unshell-crypt/src/lib.rs b/unshell-obfuscate/src/crypt/mod.rs similarity index 97% rename from unshell-crypt/src/lib.rs rename to unshell-obfuscate/src/crypt/mod.rs index 0031b6b..3703fee 100644 --- a/unshell-crypt/src/lib.rs +++ b/unshell-obfuscate/src/crypt/mod.rs @@ -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; diff --git a/unshell-obfuscate/src/lib.rs b/unshell-obfuscate/src/lib.rs index 087ad79..0435fa5 100644 --- a/unshell-obfuscate/src/lib.rs +++ b/unshell-obfuscate/src/lib.rs @@ -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; diff --git a/unshell-obfuscate/src/obfuscate/mod.rs b/unshell-obfuscate/src/obfuscate/mod.rs index 802cb46..d6761bf 100644 --- a/unshell-obfuscate/src/obfuscate/mod.rs +++ b/unshell-obfuscate/src/obfuscate/mod.rs @@ -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 diff --git a/unshell-obfuscate/src/obfuscate/junk_asm.rs b/unshell-obfuscate/src/obfuscate/obs_junk_asm.rs similarity index 100% rename from unshell-obfuscate/src/obfuscate/junk_asm.rs rename to unshell-obfuscate/src/obfuscate/obs_junk_asm.rs diff --git a/unshell-obfuscate/src/obfuscate/obs_xor.rs b/unshell-obfuscate/src/obfuscate/obs_xor.rs new file mode 100644 index 0000000..314480a --- /dev/null +++ b/unshell-obfuscate/src/obfuscate/obs_xor.rs @@ -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) +} diff --git a/unshell-obfuscate/src/obfuscate/strings.rs b/unshell-obfuscate/src/obfuscate/strings.rs deleted file mode 100644 index cfb9a6f..0000000 --- a/unshell-obfuscate/src/obfuscate/strings.rs +++ /dev/null @@ -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) -} diff --git a/unshell-obfuscate/src/obfuscate/sym_aes_strings.rs b/unshell-obfuscate/src/obfuscate/sym_aes_strings.rs new file mode 100644 index 0000000..4d6437c --- /dev/null +++ b/unshell-obfuscate/src/obfuscate/sym_aes_strings.rs @@ -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 + }) +}