Skip to main content

dmanager/
lib.rs

1//! XOR fluctuation for an overloaded module or stomped shellcode.
2//!
3//! [`Manager`] holds encrypted payload and decoy copies. [`Manager::hide_module`]
4//! restores the decoy. [`Manager::map_module`] writes the payload back when
5//! you need to call it.
6//!
7//! ```ignore
8//! let mut manager = dmanager::Manager::new();
9//! manager.new_module(base, payload, decoy).unwrap();
10//! manager.map_module(base).unwrap();
11//! manager.hide_module(base).unwrap();
12//! ```
13
14// dyncvoke - Module/Shellcode Fluctuation Manager
15// @5mukx
16
17#![cfg(windows)]
18#![cfg_attr(docsrs, feature(doc_cfg))]
19
20use data::lc;
21use std::{collections::HashMap, ffi::c_void};
22use data::{PeMetadata, PVOID, PAGE_READWRITE};
23use nanorand::{Rng, BufferedRng, WyRand};
24
25/// Tracks encrypted payload / decoy copies for one or more mapped bases.
26pub struct Manager
27{
28    payloads: HashMap<usize, Vec<u8>>,
29    payloads_metadata: HashMap<usize, PeMetadata>,
30    decoys_metadata: HashMap<usize, PeMetadata>,
31    decoys: HashMap<usize, Vec<u8>>,
32    counter: HashMap<usize, i64>,
33    keys: HashMap<usize, u8>
34}
35
36impl Manager {
37    /// Empty manager. Call [`Manager::new_module`] or [`Manager::new_shellcode`] next.
38    pub fn new () -> Manager {
39        Manager{
40            payloads: HashMap::new(),
41            payloads_metadata: HashMap::new(),
42            decoys_metadata: HashMap::new(),
43            decoys: HashMap::new(),
44            counter: HashMap::new(),
45            keys: HashMap::new(),
46        }
47    }
48
49    /// Register a mapped PE at `address` with payload and decoy copies, then hide it.
50    pub fn new_module (&mut self, address: usize, payload: Vec<u8>, decoy: Vec<u8>) -> Result<(), String>
51    {   
52        if self.payloads.contains_key(&address)
53        {
54            return Err(lc!("[x] This address is already mapped."));
55        }
56
57        let payload_metadata = manualmap::get_pe_metadata(payload.as_ptr(), false)?;
58        let decoy_metadata = manualmap::get_pe_metadata(decoy.as_ptr(), false)?;
59
60        let mut rand_bytes = [0u8; 15];
61        let mut rng = BufferedRng::new(WyRand::new());
62        rng.fill(&mut rand_bytes);
63        let xor_key: u8 = rand_bytes.iter().fold(0u8, |acc, b| acc ^ b);
64
65        let xored_payload = Manager::xor_module(payload, xor_key);
66        let xored_decoy = Manager::xor_module(decoy, xor_key);
67
68        self.payloads.insert(address, xored_payload);
69        self.payloads_metadata.insert(address, payload_metadata);
70        self.decoys_metadata.insert(address, decoy_metadata);
71        self.decoys.insert(address, xored_decoy);
72        self.counter.insert(address, 1);
73        self.keys.insert(address, xor_key);
74
75        Manager::hide_module(self, address)?;
76
77        Ok(())
78    }
79
80    /// Register stomped shellcode at `address` with payload and decoy copies, then hide it.
81    pub fn new_shellcode (&mut self, address: usize, payload: Vec<u8>, decoy: Vec<u8>) -> Result<(), String>
82    {   
83        if self.payloads.contains_key(&address)
84        {
85            return Err(lc!("[x] This shellcode is already mapped."));
86        }
87
88        let mut rand_bytes = [0u8; 15];
89        let mut rng = BufferedRng::new(WyRand::new());
90        rng.fill(&mut rand_bytes);
91        let xor_key: u8 = rand_bytes.iter().fold(0u8, |acc, b| acc ^ b);
92
93        let xored_payload = Manager::xor_module(payload, xor_key);
94        let xored_decoy = Manager::xor_module(decoy, xor_key);
95
96        self.payloads.insert(address, xored_payload);
97        self.decoys.insert(address, xored_decoy);
98        self.counter.insert(address, 1);
99        self.keys.insert(address, xor_key);
100
101        Manager::hide_shellcode(self, address)?;
102
103        Ok(())
104    }
105
106    fn xor_module (module: Vec<u8>, key: u8) -> Vec<u8>
107    {
108        unsafe
109        {
110            let mut module_ptr = module.as_ptr();
111            let mut final_module: Vec<u8> = vec![];
112
113            for _i in 0..module.len()
114            {
115                final_module.push(*module_ptr ^ key);
116                module_ptr = module_ptr.add(1);
117            }
118
119            final_module
120        }
121    }
122
123    /// Decrypt and remap the payload at `address`.
124    pub fn map_module (&mut self, address: usize) -> Result<(),String>
125    {
126        unsafe
127        {
128            if self.payloads.contains_key(&address)
129            {
130                if self.counter.get(&address).unwrap() == &0
131                {   
132                    let payload = self.payloads.get(&address).unwrap();
133                    let key = *self.keys.get(&address).unwrap();
134                    let pe_info = self.payloads_metadata.get(&address).unwrap();
135                    let decoy_info = self.decoys_metadata.get(&address).unwrap();
136                    
137                    let addr: PVOID = std::ptr::with_exposed_provenance_mut::<c_void>(address);
138                    let handle = dyncvoke_core::INVALID_HANDLE_VALUE;
139
140                    let mut base_addr_local: PVOID = addr;
141                    let mut size_local: usize = if decoy_info.is_32_bit {
142                        decoy_info.opt_header_32.SizeOfImage as usize
143                    } else {
144                        decoy_info.opt_header_64.size_of_image as usize
145                    };
146                    let mut old_protection_local: u32 = 0;
147
148                    let ret = dyncvoke_core::nt_protect_virtual_memory(
149                        handle,
150                        &mut base_addr_local as *mut PVOID,
151                        &mut size_local as *mut usize,
152                        PAGE_READWRITE,
153                        &mut old_protection_local as *mut u32,
154                    );
155
156                    if ret != 0
157                    {
158                        return Err(lc!("[x] Error changing memory protection."));
159                    }
160
161                    dyncvoke_core::rtl_zero_memory(base_addr_local, size_local);
162                    let mut decrypted_payload = Manager::xor_module(payload.to_vec(), key);
163                    let _r = manualmap::map_to_allocated_memory(decrypted_payload.as_ptr(), addr, pe_info)?;
164                    let decrypted_payload_ptr = decrypted_payload.as_mut_ptr();
165                    
166                    for i in 0..decrypted_payload.len()
167                    {
168                        *(decrypted_payload_ptr.add(i)) = 0u8;
169                    }
170                } 
171
172                self.counter.insert(address, self.counter[&address] + 1);
173
174            }
175
176            Ok(())
177        }
178    }
179
180    /// Restore the decoy image at `address` and XOR-hide the payload copy.
181    pub fn hide_module(&mut self, address: usize) -> Result<(),String>
182    {
183        if self.payloads.contains_key(&address)
184        {
185            if self.counter.get(&address).unwrap() == &1
186            {
187                let decoy = self.decoys.get(&address).unwrap();
188                let key = *self.keys.get(&address).unwrap();
189                let decrypted_decoy = Manager::xor_module(decoy.to_vec(), key);
190                let pe_info = self.decoys_metadata.get(&address).unwrap();
191                let addr: PVOID = std::ptr::with_exposed_provenance_mut::<c_void>(address);
192
193                let handle = dyncvoke_core::INVALID_HANDLE_VALUE;
194
195                let mut base_addr_local: PVOID = addr;
196                let mut size_local: usize = if pe_info.is_32_bit {
197                    pe_info.opt_header_32.SizeOfImage as usize
198                } else {
199                    pe_info.opt_header_64.size_of_image as usize
200                };
201                let mut old_protection_local: u32 = 0;
202
203                let ret = dyncvoke_core::nt_protect_virtual_memory(
204                    handle,
205                    &mut base_addr_local as *mut PVOID,
206                    &mut size_local as *mut usize,
207                    PAGE_READWRITE,
208                    &mut old_protection_local as *mut u32,
209                );
210
211                if ret != 0
212                {
213                    return Err(lc!("[x] Error changing memory protection."));
214                }
215
216                dyncvoke_core::rtl_zero_memory(base_addr_local, size_local);
217                let _r = manualmap::map_to_allocated_memory(decrypted_decoy.as_ptr(), addr, pe_info)?;
218            }
219
220            if self.counter.get(&address).unwrap() >= &1
221            {
222                self.counter.insert(address, self.counter[&address] - 1);
223            }
224        }
225
226        Ok(())
227    }
228
229    /// Restore the decoy bytes over stomped shellcode at `address`.
230    pub fn hide_shellcode(&mut self, address: usize) -> Result<(),String>
231    {
232        if self.payloads.contains_key(&address)
233        {
234            if self.counter.get(&address).unwrap() == &1
235            {   
236                let decoy = self.decoys.get(&address).unwrap();
237                let key = *self.keys.get(&address).unwrap();
238                let decrypted_decoy = Manager::xor_module(decoy.to_vec(), key);    
239                let result = overload::managed_module_stomping(&decrypted_decoy, address, 0);
240
241                if !result.is_ok()
242                {
243                    return Err(lc!("[x] Error hiding shellcode."));
244                }
245            } 
246
247            if self.counter.get(&address).unwrap() >= &1
248            {
249                self.counter.insert(address, self.counter[&address] - 1);
250            }
251
252        }
253
254        Ok(())
255        
256    }
257
258    /// Write the decrypted payload back over the stomped region at `address`.
259    pub fn stomp_shellcode(&mut self, address: usize) -> Result<(),String>
260    {
261        if self.payloads.contains_key(&address)
262        {
263            if self.counter.get(&address).unwrap() == &0
264            {   
265                let payload = self.payloads.get(&address).unwrap();
266                let key = *self.keys.get(&address).unwrap();
267                let mut decrypted_payload = Manager::xor_module(payload.to_vec(), key);
268                let result = overload::managed_module_stomping(&decrypted_payload, address, 0);
269                let decrypted_payload_ptr = decrypted_payload.as_mut_ptr();
270                unsafe
271                {
272                    for i in 0..decrypted_payload.len()
273                    {
274                        *(decrypted_payload_ptr.add(i)) = 0u8;
275                    }
276                }
277
278                if !result.is_ok()
279                {
280                    return Err(lc!("[x] Error stomping shellcode."));
281                }
282
283            } 
284
285            self.counter.insert(address, self.counter[&address] + 1);
286
287        }
288
289        Ok(())
290
291    }
292
293}
294
295#[cfg(test)]
296mod tests {
297    use super::*;
298
299    #[test]
300    fn test_manager_creation() {
301        let manager = Manager::new();
302
303        assert!(manager.payloads.is_empty());
304        assert!(manager.payloads_metadata.is_empty());
305        assert!(manager.decoys_metadata.is_empty());
306        assert!(manager.decoys.is_empty());
307        assert!(manager.counter.is_empty());
308        assert!(manager.keys.is_empty());
309    }
310
311    #[test]
312    fn test_xor_module() {
313        let data = vec![0x01, 0x02, 0x03, 0x04, 0x05];
314        let key: u8 = 0x42;
315        let xored = Manager::xor_module(data.clone(), key);
316        for (i, &byte) in data.iter().enumerate() {
317            assert_eq!(xored[i], byte ^ key);
318        }
319        let de_xored = Manager::xor_module(xored, key);
320        assert_eq!(de_xored, data);
321    }
322
323    #[test]
324    fn test_xor_module_different_keys() {
325        let data = vec![0xFF, 0x00, 0xAA, 0x55, 0x12, 0x34];
326        let no_change = Manager::xor_module(data.clone(), 0x00);
327        assert_eq!(no_change, data);
328        let bitflip = Manager::xor_module(data.clone(), 0xFF);
329        for (i, &byte) in data.iter().enumerate() {
330            assert_eq!(bitflip[i], !byte);
331        }
332        let patterned = Manager::xor_module(data.clone(), 0xAA);
333        for (i, &byte) in data.iter().enumerate() {
334            assert_eq!(patterned[i], byte ^ 0xAA);
335        }
336    }
337
338    #[test]
339    fn test_xor_module_large_data() {
340        let size = 1024 * 1024;
341        let data: Vec<u8> = vec![0x42; size];
342        let key: u8 = 0x5A;
343        let xored = Manager::xor_module(data.clone(), key);
344        assert_eq!(xored.len(), size);
345        let restored = Manager::xor_module(xored, key);
346        assert_eq!(restored.len(), data.len());
347        assert_eq!(restored[0], 0x42);
348    }
349
350    #[test]
351    fn test_counter_tracking() {
352        let mut manager = Manager::new();
353        let address = 0x1000;
354        manager.counter.insert(address, 5);
355
356        assert_eq!(manager.counter.get(&address), Some(&5));
357
358        // Increment
359        *manager.counter.get_mut(&address).unwrap() += 1;
360        assert_eq!(manager.counter.get(&address), Some(&6));
361    }
362
363    #[test]
364    fn test_key_storage() {
365        let mut manager = Manager::new();
366
367        let address = 0x2000;
368        let key: u8 = 0xAB;
369
370        manager.keys.insert(address, key);
371
372        assert_eq!(manager.keys.get(&address), Some(&key));
373    }
374
375    // Test payload and decoy storage
376    #[test]
377    fn test_payload_decoy_storage() {
378        let mut manager = Manager::new();
379
380        let address = 0x3000;
381        let payload = vec![0x90, 0x90, 0xCC]; // NOP, NOP, INT3
382        let decoy = vec![0x55, 0x8B, 0xEC]; // Standard function prologue
383
384        manager.payloads.insert(address, payload.clone());
385        manager.decoys.insert(address, decoy.clone());
386
387        assert_eq!(manager.payloads.get(&address), Some(&payload));
388        assert_eq!(manager.decoys.get(&address), Some(&decoy));
389    }
390
391    // Test metadata storage
392    #[test]
393    fn test_metadata_storage() {
394        let mut manager = Manager::new();
395
396        let address = 0x4000;
397        let pe_metadata = PeMetadata::default();
398
399        manager.payloads_metadata.insert(address, pe_metadata.clone());
400        manager.decoys_metadata.insert(address, pe_metadata);
401
402        assert!(manager.payloads_metadata.get(&address).is_some());
403        assert!(manager.decoys_metadata.get(&address).is_some());
404    }
405
406    // Test HashMap operations
407    #[test]
408    fn test_hashmap_operations() {
409        let mut manager = Manager::new();
410
411        // Insert multiple entries
412        for i in 0..10 {
413            let addr = 0x10000 + (i * 0x1000);
414            manager.payloads.insert(addr, vec![i as u8]);
415            manager.counter.insert(addr, i as i64);
416            manager.keys.insert(addr, (i + 1) as u8);
417        }
418
419        assert_eq!(manager.payloads.len(), 10);
420        assert_eq!(manager.counter.len(), 10);
421        assert_eq!(manager.keys.len(), 10);
422
423        // Remove entry
424        manager.payloads.remove(&(0x10000));
425        manager.counter.remove(&(0x10000));
426        manager.keys.remove(&(0x10000));
427
428        assert_eq!(manager.payloads.len(), 9);
429    }
430
431    // Test address validation
432    #[test]
433    fn test_address_validation() {
434        // Valid addresses should be non-null and aligned
435        let valid_addresses: Vec<u64> = vec![
436            0x1000,
437            0x10000,
438            0x140000000,
439            0x7FF60000,
440        ];
441
442        for addr in valid_addresses {
443            assert!(addr > 0);
444            // Check alignment (should be page-aligned)
445            assert_eq!(addr % 0x1000, 0);
446        }
447    }
448
449    // Test buffer size validation
450    #[test]
451    fn test_buffer_size_validation() {
452        let empty: Vec<u8> = vec![];
453        assert!(empty.is_empty());
454
455        let single_byte: Vec<u8> = vec![0x42];
456        assert_eq!(single_byte.len(), 1);
457
458        let page_size = 4096;
459        let page_buffer: Vec<u8> = vec![0x00; page_size];
460        assert_eq!(page_buffer.len(), page_size);
461    }
462}