Browse Source

new Encoder struct

Marius Schwarz 4 years ago
parent
commit
511903dc15
3 changed files with 131 additions and 81 deletions
  1. 5 0
      src/cpu_data/mod.rs
  2. 116 80
      src/encoder.rs
  3. 10 1
      src/main.rs

+ 5 - 0
src/cpu_data/mod.rs

@@ -1 +1,6 @@
 pub mod x64;
+
+pub enum Arch {
+    X64,
+    X84
+}

+ 116 - 80
src/encoder.rs

@@ -1,104 +1,141 @@
+use crate::cpu_data::{x64, Arch as EncArch};
 use keystone::*;
 
-use crate::cpu_data::x64;
-
-/// Generates a decoder stub, each with using different registers
-pub fn get_decoder_stub(seed: u8, payload_size: usize) -> Vec<u8> {
-
-    // Maintain a blacklist for all the registers that are already used
-    let mut blacklist: Vec<x64::Register> = vec![x64::Register::RSP, x64::Register::RBP, x64::Register::RDI];
-
-    // generate a register for the COUNTER & add it to the blacklist
-    let counter_register = x64::get_random_reg(&blacklist);
-    blacklist.push(counter_register.register);
-
-    // generate a register for the length of the payload
-    let length_register = x64::get_random_reg(&blacklist);
-    blacklist.push(length_register.register);
-
-    // generate a register for the key of the payload (byte)
-    let key_register = x64::get_random_reg(&blacklist);
-    blacklist.push(key_register.register);
-    //let key_register = key_register.byte;
-
-    let dc1 = x64::generate_dead_code();
-    let dc2 = x64::generate_dead_code();
-
-    // Stub Prototype for x64
-    let stub = "
-    XOR {LENREG}, {LENREG}\n\
-    XOR {KEYREG}, {KEYREG}\n\
-    MOV {KEYREG},{K}\n\
-    XOR {COUNTER}, {COUNTER}\n\
-    MOV {LENREG},{S}\n\
-    LEA RDI,[RIP+data]\n\
-    decode:\n\
-	XOR BYTE [RDI+{COUNTER}*1],{KEYREG}\n\
-        MOV {KEYREG}, [RDI+{COUNTER}*1]\n\
-        INC {COUNTER}\n\
-        CMP {COUNTER}, {LENREG}\n\
-	JLE decode\n\
-    data:";
-
-    // Replace the registers/keys/sizes to achieve a unique stub
-    let stub = stub.replace("{KEYREG}", &key_register.byte);
-    let stub = stub.replace("{COUNTER}", &counter_register.full);
-    let stub = stub.replace("{LENREG}", &length_register.full);
-    let stub = stub.replace("{K}", &format!("0x{:x}", seed));
-    let stub = stub.replace("{S}", &format!("0x{:x}", payload_size));
-    let stub = stub.replace("{DEAD_CODE_1}", &dc1);
-    let stub = stub.replace("{DEAD_CODE_2}", &dc2);
-
-    // return the stub string
-    return assemble_asm(String::from(stub), OptionValue::SYNTAX_NASM).unwrap();
+pub struct Encoder {
+    arch: EncArch,
+    shellcode: Vec<u8>,
+    num_rounds: i32,
+    reorder: bool,
 }
 
+impl Encoder {
+    pub fn new(arch: EncArch, payload: Vec<u8>) -> Self {
+        Encoder {
+            arch: arch,
+            shellcode: payload,
+            num_rounds: 10,
+            reorder: false,
+        }
+    }
 
+    // main method for encoding the payload
+    pub fn encode(&self) -> Vec<u8> {
 
-/// The function uses capstone to assemble the stub and retrieve the raw bytes
-pub fn assemble_asm(stub: String, asm_syntax: OptionValue) -> Result<Vec<u8>, keystone::Error>  {
+        let mut payload = self.shellcode.clone();
 
-    let engine = Keystone::new(Arch::X86, Mode::MODE_64)
-                .expect("Could not initialize Keystone engine");
+        for i in 0..self.num_rounds {
+            println!("[*] Encoding Round {}", i);
 
-    engine.option(OptionType::SYNTAX, asm_syntax)
-                .expect("Could not set option to nasm syntax");
+            // Generate random key
+            let key: u8 = rand::random();
 
-    // Assemble the stub and return the bytes
-    let result = match engine.asm(stub, 0) {
-        Ok(v) => Ok(v.bytes),
-        Err(msg) => {
-            Err(msg)
-        }
-    };
+            // encode the payload
+            let mut encoded_payload = self.encode_payload(payload.clone(), key);
 
-    return result;
-}
+            //println!("[*] Generating Stub...");
+            let mut stub = self.get_decoder_stub(key, encoded_payload.len());
 
-/// This function does the plain shellcode encoding (XOR)
-pub fn encode(payload: Vec<u8>, seed: u8) -> Vec<u8> {
+            // Append the encoded_payload to the stub
+            stub.append(&mut encoded_payload);
 
-    // Buffer for the encoded payload
-    let mut encoded_payload: Vec<u8> = Vec::new();
-    let mut key = seed;
+            // Set the [stub+payload] as the new raw payload
+            payload = stub.clone();
+        }
+        Vec::new()
+    }
 
-    for byte in payload {
-        let new_byte = byte ^ key;
-        //println!("Original Byte: 0x{:x}", byte);
-        encoded_payload.push(new_byte);
-        key = byte;
+    /// This function does the plain shellcode encoding (XOR)
+    pub fn encode_payload(&self, payload: Vec<u8>, seed: u8) -> Vec<u8> {
+        // Buffer for the encoded payload
+        let mut encoded_payload: Vec<u8> = Vec::new();
+        let mut key = seed;
+
+        for byte in payload {
+            let new_byte = byte ^ key;
+            //println!("Original Byte: 0x{:x}", byte);
+            encoded_payload.push(new_byte);
+            key = byte;
+        }
+
+        encoded_payload
+    }
+    /// Generates a decoder stub, each with using different registers
+    fn get_decoder_stub(&self, seed: u8, payload_size: usize) -> Vec<u8> {
+        // Maintain a blacklist for all the registers that are already used
+        let mut blacklist: Vec<x64::Register> =
+            vec![x64::Register::RSP, x64::Register::RBP, x64::Register::RDI];
+
+        // generate a register for the COUNTER & add it to the blacklist
+        let counter_register = x64::get_random_reg(&blacklist);
+        blacklist.push(counter_register.register);
+
+        // generate a register for the length of the payload
+        let length_register = x64::get_random_reg(&blacklist);
+        blacklist.push(length_register.register);
+
+        // generate a register for the key of the payload (byte)
+        let key_register = x64::get_random_reg(&blacklist);
+        blacklist.push(key_register.register);
+        //let key_register = key_register.byte;
+
+        let dc1 = x64::generate_dead_code();
+        let dc2 = x64::generate_dead_code();
+
+        // Stub Prototype for x64
+        let stub = "
+        XOR {LENREG}, {LENREG}\n\
+        XOR {KEYREG}, {KEYREG}\n\
+        MOV {KEYREG},{K}\n\
+        XOR {COUNTER}, {COUNTER}\n\
+        MOV {LENREG},{S}\n\
+        LEA RDI,[RIP+data]\n\
+        decode:\n\
+            XOR BYTE [RDI+{COUNTER}*1],{KEYREG}\n\
+            MOV {KEYREG}, [RDI+{COUNTER}*1]\n\
+            INC {COUNTER}\n\
+            CMP {COUNTER}, {LENREG}\n\
+            JLE decode\n\
+        data:";
+
+        // Replace the registers/keys/sizes to achieve a unique stub
+        let stub = stub.replace("{KEYREG}", &key_register.byte);
+        let stub = stub.replace("{COUNTER}", &counter_register.full);
+        let stub = stub.replace("{LENREG}", &length_register.full);
+        let stub = stub.replace("{K}", &format!("0x{:x}", seed));
+        let stub = stub.replace("{S}", &format!("0x{:x}", payload_size));
+        let stub = stub.replace("{DEAD_CODE_1}", &dc1);
+        let stub = stub.replace("{DEAD_CODE_2}", &dc2);
+
+        // return the stub string
+        return assemble_asm(String::from(stub), OptionValue::SYNTAX_NASM).unwrap();
     }
 
-    encoded_payload
 }
 
 
+/// The function uses capstone to assemble the stub and retrieve the raw bytes
+pub fn assemble_asm(stub: String, asm_syntax: OptionValue) -> Result<Vec<u8>, keystone::Error> {
+    let engine =
+        Keystone::new(Arch::X86, Mode::MODE_64).expect("Could not initialize Keystone engine");
+
+    engine
+        .option(OptionType::SYNTAX, asm_syntax)
+        .expect("Could not set option to nasm syntax");
+
+    // Assemble the stub and return the bytes
+    let result = match engine.asm(stub, 0) {
+        Ok(v) => Ok(v.bytes),
+        Err(msg) => Err(msg),
+    };
+
+    return result;
+}
+
 /// This is the corresponding `decoder()`. This function is implemented
 /// in the decoder stub
 #[allow(dead_code)]
 fn decode(payload: Vec<u8>, seed: u8) -> Vec<u8> {
-
-    let mut decoded_payload:  Vec<u8> = Vec::new();
+    let mut decoded_payload: Vec<u8> = Vec::new();
 
     // initialize the key
     let mut key = seed;
@@ -112,4 +149,3 @@ fn decode(payload: Vec<u8>, seed: u8) -> Vec<u8> {
 
     decoded_payload
 }
-

+ 10 - 1
src/main.rs

@@ -6,9 +6,13 @@ use std::io::{Seek, SeekFrom};
 use std::env;
 
 mod encoder;
+
 mod reorder;
 mod cpu_data;
 
+use crate::encoder::Encoder;
+use crate::cpu_data::Arch as EncArch;
+
 fn usage(args: &Vec<String>) {
         println!("Usage: {} -i shellcode.bin -o shellcode.out.bin", args[0]);
         std::process::exit(1);
@@ -64,6 +68,11 @@ fn main() {
          payload = prog.reorder();
     }
 
+    let enc = Encoder::new(EncArch::X64, payload.clone());
+    enc.encode();
+
+    /*
+
     // The Encoding Phase
     for i in 0..number_of_loops {
 
@@ -96,6 +105,6 @@ fn main() {
     };
     file.write(&payload).unwrap();
     println!("[+] Done, Generated encoded payload @ {}", output_file);
-
+    */
 }