From a7cd65f7ae37b65615bd1648283fc8dece680f6b Mon Sep 17 00:00:00 2001 From: Michael Mikovsky <77305074+Astatin3@users.noreply.github.com> Date: Sat, 13 Dec 2025 16:49:32 -0700 Subject: [PATCH] ASM Obfuscation --- unshell-config/Cargo.lock | 506 ++++++++++++++++++++++++++ unshell-config/Cargo.toml | 21 ++ unshell-config/src/main.rs | 28 +- unshell-obfuscate/Cargo.lock | 206 ++++++++++- unshell-obfuscate/Cargo.toml | 6 +- unshell-obfuscate/src/junk_asm.rs | 209 +++++++++++ unshell-obfuscate/src/lib.rs | 162 +-------- unshell-obfuscate/src/no_obfuscate.rs | 23 ++ unshell-obfuscate/src/obfuscate.rs | 98 +++++ unshell-server/test.sh | 20 + 10 files changed, 1131 insertions(+), 148 deletions(-) create mode 100644 unshell-obfuscate/src/junk_asm.rs create mode 100644 unshell-obfuscate/src/no_obfuscate.rs create mode 100644 unshell-obfuscate/src/obfuscate.rs create mode 100755 unshell-server/test.sh diff --git a/unshell-config/Cargo.lock b/unshell-config/Cargo.lock index fdbe8bb..0669f27 100644 --- a/unshell-config/Cargo.lock +++ b/unshell-config/Cargo.lock @@ -2,6 +2,512 @@ # 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 = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710f1dd022ef4e93f8a438b4ba958de7f64308434fa6a87104481645cc30068b" +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 = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +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.178" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.10.0", +] + +[[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 = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[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 = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "static_init" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bae1df58c5fea7502e8e352ec26b5579f6178e1fdb311e088580c980dee25ed" +dependencies = [ + "bitflags 1.3.2", + "cfg_aliases 0.2.1", + "libc", + "parking_lot", + "parking_lot_core", + "static_init_macro", + "winapi", +] + +[[package]] +name = "static_init_macro" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1389c88ddd739ec6d3f8f83343764a0e944cd23cfbf126a9796a714b0b6edd6f" +dependencies = [ + "cfg_aliases 0.1.1", + "memchr", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + [[package]] name = "unshell-config" version = "0.1.0" +dependencies = [ + "libc", + "unshell-obfuscate", +] + +[[package]] +name = "unshell-crypt" +version = "0.1.0" +dependencies = [ + "aes", + "block-padding 0.4.2", + "cbc", + "getrandom", + "hex", + "hex-literal", + "regex", + "sha2", +] + +[[package]] +name = "unshell-obfuscate" +version = "0.0.0" +dependencies = [ + "proc-macro2", + "quote", + "rand", + "static_init", + "syn 2.0.111", + "unshell-crypt", +] + +[[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 = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "zerocopy" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] diff --git a/unshell-config/Cargo.toml b/unshell-config/Cargo.toml index 5afc118..3b55dbb 100644 --- a/unshell-config/Cargo.toml +++ b/unshell-config/Cargo.toml @@ -1,6 +1,27 @@ +cargo-features = ["trim-paths"] + [package] name = "unshell-config" version = "0.1.0" edition = "2024" + +[features] +default = ["obfuscate"] + +obfuscate = ["unshell-obfuscate/obfuscate"] + [dependencies] +libc = { version = "0.2.178", default-features = false } + +unshell-obfuscate = {path = "../unshell-obfuscate", default-features=false} + + +[profile.release] +strip = true # Strip symbols from the binary +opt-level = "s" # Optimize for size +lto = true +codegen-units = 1 +panic = "abort" +debug = false +trim-paths = "all" diff --git a/unshell-config/src/main.rs b/unshell-config/src/main.rs index e7a11a9..af35c38 100644 --- a/unshell-config/src/main.rs +++ b/unshell-config/src/main.rs @@ -1,3 +1,27 @@ -fn main() { - println!("Hello, world!"); +#![no_std] +#![no_main] + +enum TestEnum { + Test = 135, +} + +#[unsafe(no_mangle)] +pub fn test() -> i32 { + let a = TestEnum::Test; + + a as i32 +} + +#[unsafe(no_mangle)] +fn main() { + let a = 5; + + unshell_obfuscate::junk_asm!(0.1); + + unsafe { libc::exit(a as i32) } +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} } diff --git a/unshell-obfuscate/Cargo.lock b/unshell-obfuscate/Cargo.lock index 15c3124..d9656ed 100644 --- a/unshell-obfuscate/Cargo.lock +++ b/unshell-obfuscate/Cargo.lock @@ -22,6 +22,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + [[package]] name = "block-buffer" version = "0.10.4" @@ -64,6 +76,18 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "cipher" version = "0.4.4" @@ -162,12 +186,53 @@ version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -192,6 +257,44 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.10.0", +] + [[package]] name = "regex" version = "1.12.2" @@ -221,6 +324,12 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "sha2" version = "0.10.9" @@ -232,6 +341,51 @@ dependencies = [ "digest", ] +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "static_init" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bae1df58c5fea7502e8e352ec26b5579f6178e1fdb311e088580c980dee25ed" +dependencies = [ + "bitflags 1.3.2", + "cfg_aliases 0.2.1", + "libc", + "parking_lot", + "parking_lot_core", + "static_init_macro", + "winapi", +] + +[[package]] +name = "static_init_macro" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1389c88ddd739ec6d3f8f83343764a0e944cd23cfbf126a9796a714b0b6edd6f" +dependencies = [ + "cfg_aliases 0.1.1", + "memchr", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.109" @@ -275,7 +429,9 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn", + "rand", + "static_init", + "syn 2.0.109", "unshell-crypt", ] @@ -294,8 +450,56 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "wit-bindgen" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "zerocopy" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.109", +] diff --git a/unshell-obfuscate/Cargo.toml b/unshell-obfuscate/Cargo.toml index 687f532..566986b 100644 --- a/unshell-obfuscate/Cargo.toml +++ b/unshell-obfuscate/Cargo.toml @@ -10,8 +10,10 @@ proc-macro = true obfuscate = [] [dependencies] +unshell-crypt = {path = "../unshell-crypt"} + quote = "1.0.42" syn = {version = "2.0.109", features = ["full"]} proc-macro2 = "1.0.103" - -unshell-crypt = {path = "../unshell-crypt"} +static_init = "1.0.4" +rand = "0.9.2" diff --git a/unshell-obfuscate/src/junk_asm.rs b/unshell-obfuscate/src/junk_asm.rs new file mode 100644 index 0000000..a31b9f0 --- /dev/null +++ b/unshell-obfuscate/src/junk_asm.rs @@ -0,0 +1,209 @@ +use proc_macro::TokenStream; +use quote::quote; +use rand::rngs::SmallRng; +use rand::{Rng, RngCore, SeedableRng}; +use syn::{LitFloat, parse_macro_input}; + +const MAX_INSTRUCTIONS: u32 = 20; // Maximum instructions per recursive block +const MIN_LENGTH: f64 = 10.; // Min length per 1/weight + +// The full list of 64-bit registers in AT&T syntax (used by default in asm!) +const REGISTERS: &[&str] = &[ + "%rax", "%rbx", "%rcx", "%rdx", "%rsi", "%rdi", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", + "%r14", "%r15", +]; + +// Conditional Jumps in AT&T syntax. +const COND_JUMPS: &[&str] = &[ + "je", "jne", "jg", "jge", "jl", "jle", "ja", "jnb", "jc", "jnc", "jz", "jnz", +]; + +// Arithmetic/Logic operations with the 'q' (quad-word) suffix. +const ARITHITHMETIC_OPS: &[&str] = &["addq", "subq", "xorq", "andq", "orq"]; + +// --- Helper Functions for Modular Generation --- + +/// Generates a unique label name for the given depth and ID. +fn generate_label(prefix: &str, depth: u32, block_id: u32, id: u32) -> String { + format!(".L_{}_{}_{}_{}", prefix, depth, block_id, id) +} +/// Generates a highly randomized, complex instruction using different addressing modes. +fn generate_complex_mutation(rng: &mut SmallRng) -> String { + let op = ARITHITHMETIC_OPS[rng.random_range(0..ARITHITHMETIC_OPS.len())]; + + match rng.random_range(0..3) { + // Pattern 0: Register-Immediate + // Example: "addq $0x1234, %rax" + 0 => { + let reg = REGISTERS[rng.random_range(0..REGISTERS.len())]; + let immediate = rng.random_range(1..=0xFFFF); + format!("\t{} ${}, {}", op, immediate, reg) + } + // Pattern 1: Register-Register + // Example: "xorq %rbx, %rcx" + 1 => { + let reg_src = REGISTERS[rng.random_range(0..REGISTERS.len())]; + let reg_dst = REGISTERS[rng.random_range(0..REGISTERS.len())]; + format!("\t{} {}, {}", op, reg_src, reg_dst) + } + // Pattern 2: LEA (Complex Address Calculation) + // Example: "leaq (%rax, %rbx, 4), %rcx" + 2 => { + let reg_base = REGISTERS[rng.random_range(0..REGISTERS.len())]; + let reg_index = REGISTERS[rng.random_range(0..REGISTERS.len())]; + let reg_dst = REGISTERS[rng.random_range(0..REGISTERS.len())]; + let scale = 1 << rng.random_range(0..4); // Scale is 1, 2, 4, or 8 + format!( + "\tleaq ({}, {}, {}), {}", + reg_base, reg_index, scale, reg_dst + ) + } + _ => String::new(), // Should not happen + } +} + +/// Generates a comparison followed by a conditional jump to a specific label. +fn generate_conditional_jump(rng: &mut SmallRng, label: &str) -> String { + let reg1 = REGISTERS[rng.random_range(0..REGISTERS.len())]; + let reg2 = REGISTERS[rng.random_range(0..REGISTERS.len())]; + let jump = COND_JUMPS[rng.random_range(0..COND_JUMPS.len())]; + + // Example: "cmpq %rdx, %rsi; jg .L_target_" + format!("\tcmpq {}, {}; {} {}\n", reg1, reg2, jump, label) +} + +// --- The Core DAG Recursive Algorithm --- + +fn generate_dag_block(weight: f64, rng: &mut SmallRng, depth: u32, id_counter: &mut u32) -> String { + // 1. Termination Check + + if rng.random_bool(weight) { + return String::new(); // Stop recursion + } + + let block_id = *id_counter; + *id_counter += 1; + + // 2. Randomize Block Length: The length is now based on WEIGHT. + // If rng < WEIGHT, stop growing the block. Otherwise, continue. + let mut num_labels: u32 = 0; + while !rng.random_bool(weight) && num_labels < MAX_INSTRUCTIONS { + num_labels += 1; + } + + // Ensure at least one instruction/label exists if we entered the block + if num_labels == 0 { + num_labels = 1; + } + + // Generate all labels for this block (L0 to Ln-1) + let labels: Vec = (0..num_labels) + .map(|i| generate_label("dag", depth, block_id, i)) + .collect(); + + let mut assembly_block = String::new(); + + // 3. Instruction Loop and DAG construction + for i in 0..num_labels { + let current_label = &labels[i as usize]; + assembly_block.push_str(&format!("{}:\n", current_label)); + + let mut instruction_count = 0; + + // Generate a random number of mutations based on WEIGHT + while !rng.random_bool(weight.powi(2)) && instruction_count < MAX_INSTRUCTIONS * 2 { + assembly_block.push_str(&format!("{}\n", generate_complex_mutation(rng))); + instruction_count += 1; + } + + // Conditional Forward Jump (Creates DAG edges) + if i < num_labels - 1 && !rng.random_bool(weight * 0.5) { + // Jump to a random label strictly ahead of the current one + let target_index = rng.random_range(i as usize + 1..num_labels as usize); + let target_label = &labels[target_index]; + assembly_block.push_str(&generate_conditional_jump(rng, target_label)); + } + + // Recursive Call (Nesting) + if depth < 2 { + // Lower probability for deep nesting + assembly_block.push_str(&generate_dag_block(weight, rng, depth + 1, id_counter)); + } + } + + // 4. Backward Conditional Jump (Adds controlled cycles) + // Only at the end of the block, allowing a chance to loop back to an earlier instruction. + if num_labels > 1 && rng.random_bool(weight) { + let target_index = rng.random_range(0..num_labels as usize - 1); + let target_label = &labels[target_index]; + assembly_block.push_str(&format!("{}\n", generate_complex_mutation(rng))); + assembly_block.push_str(&generate_conditional_jump(rng, target_label)); + assembly_block.push_str("// Backward Conditional Jump to maintain short execution\n"); + } + + assembly_block +} + +pub fn junk_asm(input: TokenStream) -> TokenStream { + // 1. Parse the input (expecting an optional f32 weight) + let weight: f64 = if input.is_empty() { + None + } else { + match parse_macro_input!(input as LitFloat).base10_parse::() { + Ok(w) => Some(w), // Clamp to a sensible range + Err(_) => None, + } + } + .expect("Expected F64"); + // let final_weight = input_weight.unwrap_or(WEIGHT); + + // 2. Setup + let mut rng = SmallRng::from_os_rng(); + let mut id_counter = 0; + // let random_u64_addr: u64 = rng.next_u64(); // The simulated external address + + // 3. Generate Assembly + let main_assembly = { + loop { + let res = generate_dag_block(weight, &mut rng, 0, &mut id_counter); + if res.len() as f64 > weight * MIN_LENGTH { + break res; + } + } + }; + + println!("{}", main_assembly); + + // 4. Wrap in `asm!` + let expanded = quote! { + // Output will replace the junk_asm!(...) call + { + // Execute the code using the standard `asm!` macro. + unsafe { + #[allow(named_asm_labels)] + core::arch::asm!( + // The generated junk code + // Note: We MUST use AT&T syntax (e.g., %rax, $100) due to options(att_syntax) + // The code is generated in AT&T syntax. + #main_assembly, + + // Pass the simulated external address into a temporary register (%r15) + // This allows instructions to reference an "external" scope using memory reads/writes. + // in(reg) external_addr_ref, + + // Clobber all general-purpose registers to force saving/restoring + clobber_abi("sysv64"), + + // Correct options for non-volatile junk code + options(att_syntax, nomem, nostack, preserves_flags) + ); + } + } + }; + + expanded.into() +} + +// NOTE: To make the example runnable, the `src/main.rs` file would now call +// junk_asm!(0.2) or junk_asm!(). The instruction sizes and jump structure +// are now compliant with your requirements. diff --git a/unshell-obfuscate/src/lib.rs b/unshell-obfuscate/src/lib.rs index 055e4a9..5f0fe7c 100644 --- a/unshell-obfuscate/src/lib.rs +++ b/unshell-obfuscate/src/lib.rs @@ -3,171 +3,47 @@ use proc_macro::TokenStream; use quote::quote; -use syn::{ItemFn, parse_macro_input}; +use syn::parse_macro_input; mod format_helper; use format_helper::*; -use syn::LitStr; +mod junk_asm; -// Put all encrypt-related dependencies in a module, so they are easier to use with the feature flag +#[allow(dead_code)] +mod no_obfuscate; + +#[allow(dead_code)] +mod obfuscate; + +#[cfg(not(feature = "obfuscate"))] +use no_obfuscate as obs; #[cfg(feature = "obfuscate")] -mod obs_deps { - pub use unshell_crypt::BACKUP_ENV_KEY; - pub use unshell_crypt::ENV_KEY_NAME; - pub use unshell_crypt::STATIC_IV; - pub use unshell_crypt::aes::encrypt_aes_lines; - pub use unshell_crypt::fill; -} -#[cfg(feature = "obfuscate")] -use obs_deps::*; +use obfuscate as obs; + +// String obfuscation #[proc_macro] -#[cfg(not(feature = "obfuscate"))] pub fn obs(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as LitStr); - - (quote::quote! { - String::from(#input) - }) - .into() + obs::obs(input) } #[proc_macro_attribute] -#[cfg(not(feature = "obfuscate"))] pub fn obfuscated_symbol(_attr: TokenStream, item: TokenStream) -> TokenStream { - let func = parse_macro_input!(item as ItemFn); - TokenStream::from(quote! { - #[unsafe(no_mangle)] - #func - }) + obs::obfuscated_symbol(_attr, item) } #[proc_macro] -#[cfg(not(feature = "obfuscate"))] pub fn symbol(input: TokenStream) -> TokenStream { - input -} - -#[proc_macro_attribute] -#[cfg(feature = "obfuscate")] -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(); - - // get the encryption key - let key_str = std::env::var(ENV_KEY_NAME).unwrap_or(BACKUP_ENV_KEY.to_owned()); - - // Generate the new, obfuscated name - let obfuscated_name = encrypt_aes_lines(&fn_name, &key_str, 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 - }) -} - -// --- NEW MACRO 2: The macro for the loader --- - -#[proc_macro] -#[cfg(feature = "obfuscate")] -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(); - - // get the encryption key - let key_str = std::env::var(ENV_KEY_NAME).unwrap_or(BACKUP_ENV_KEY.to_owned()); - - // Generate the exact same obfuscated name - let obfuscated_name = encrypt_aes_lines(&original_name, &key_str, STATIC_IV); - - // Expand to a static string literal - TokenStream::from(quote! { - #obfuscated_name - }) + obs::symbol(input) } #[proc_macro] -#[cfg(feature = "obfuscate")] -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) +pub fn junk_asm(input: TokenStream) -> TokenStream { + junk_asm::junk_asm(input) } -// #[proc_macro] -// pub fn file_literal(_input: TokenStream) -> TokenStream { -// // let input = input.into_iter().collect::>(); -// // if input.len() != 1 { -// // let msg = format!("expected exactly one input token, got {}", input.len()); -// // return quote! { compile_error!(#msg) }.into(); -// // } - -// let string = file!(); -// let lit_str = LitStr::new(string, proc_macro2::Span::call_site()); - -// // let string_lit = match LitStr::try_from(&input) { -// // // Error if the token is not a string literal -// // Err(e) => return e.to_compile_error(), -// // Ok(lit) => lit, -// // }; - -// (quote! { -// #lit_str -// }) -// .into() -// } +// #[proc_macro] pub fn file_symbol(_input: TokenStream) -> TokenStream { diff --git a/unshell-obfuscate/src/no_obfuscate.rs b/unshell-obfuscate/src/no_obfuscate.rs new file mode 100644 index 0000000..ecaa490 --- /dev/null +++ b/unshell-obfuscate/src/no_obfuscate.rs @@ -0,0 +1,23 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{ItemFn, LitStr, parse_macro_input}; + +pub fn obs(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as LitStr); + + (quote::quote! { + String::from(#input) + }) + .into() +} +pub fn obfuscated_symbol(_attr: TokenStream, item: TokenStream) -> TokenStream { + let func = parse_macro_input!(item as ItemFn); + TokenStream::from(quote! { + #[unsafe(no_mangle)] + #func + }) +} + +pub fn symbol(input: TokenStream) -> TokenStream { + input +} diff --git a/unshell-obfuscate/src/obfuscate.rs b/unshell-obfuscate/src/obfuscate.rs new file mode 100644 index 0000000..4fd3e31 --- /dev/null +++ b/unshell-obfuscate/src/obfuscate.rs @@ -0,0 +1,98 @@ +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}; + +#[static_init::dynamic] +static KEY: String = { + std::env::var(ENV_KEY_NAME).unwrap_or({ + // Diagnostic::new(proc_macro::Level::Warning, "Using default encryption key!").emit(); + + println!("Using default encryption key!"); + + BACKUP_ENV_KEY.to_owned() + }) +}; + +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-server/test.sh b/unshell-server/test.sh new file mode 100755 index 0000000..260f312 --- /dev/null +++ b/unshell-server/test.sh @@ -0,0 +1,20 @@ +# curl -w '\n' \ +# -H 'Content-Type: application/json' \ +# -d '{"username":"foo","password":"bar"}' \ +# http://localhost:3000/api/auth + + + +curl -s \ + -w '\n' \ + -H 'Content-Type: application/json' \ + -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NjQ2NjU1ODMsImlhdCI6MTc2NDYyMjM4MywiZW1haWwiOiJmb28ifQ.NeStaGwWBGS825rF11TOH_e79RWEL_2o3SY9jZ5CX20' \ + -d "jwbrjwbremnwebrnmwemnrbnmwerbnmwer" \ + http://localhost:3000/api/test + + +curl -s \ + -w '\n' \ + -H 'Content-Type: application/json' \ + -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NjQ2NjU1ODMsImlhdCI6MTc2NDYyMjM4MywiZW1haWwiOiJmb28ifQ.NeStaGwWBGS825rF11TOH_e79RWEL_2o3SY9jZ5CX20' \ + http://localhost:3000/api/test/test2