Marius Schwarz %!s(int64=4) %!d(string=hai) anos
achega
15f76536bb
Modificáronse 7 ficheiros con 343 adicións e 0 borrados
  1. 1 0
      .gitignore
  2. 5 0
      Cargo.lock
  3. 12 0
      Cargo.toml
  4. 1 0
      src/cpu_data/mod.rs
  5. 107 0
      src/cpu_data/x64.rs
  6. 121 0
      src/encoder.rs
  7. 96 0
      src/main.rs

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+/target

+ 5 - 0
Cargo.lock

@@ -0,0 +1,5 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "shellc0der"
+version = "0.1.0"

+ 12 - 0
Cargo.toml

@@ -0,0 +1,12 @@
+[package]
+name = "shellcodelab"
+version = "0.1.0"
+authors = ["Marius Schwarz <marius.schwarz97@gmail.com>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+keystone = { git = "https://github.com/tathanhdinh/keystone-rs" }
+hex = "0.4.2"
+rand = "0.8.1"

+ 1 - 0
src/cpu_data/mod.rs

@@ -0,0 +1 @@
+pub mod x64;

+ 107 - 0
src/cpu_data/x64.rs

@@ -0,0 +1,107 @@
+/// The Module for the x86_64 CPU Information
+/// Contains Registers, Dead-Code Instructions and Co
+use rand;
+use std::cmp::PartialEq;
+
+
+// Register Struct
+#[derive(Clone)]
+pub struct Reg<'a>{
+    pub register: Register,
+    pub full: &'a str,
+    pub extended: &'a str,
+    pub word: &'a str,
+    pub byte: &'a str
+}
+
+
+// All X64 Registers that are needed
+#[derive(Debug, PartialEq, Clone)]
+pub enum Register {
+    RAX, RBX, RCX, RDX, RBP, RSP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15
+}
+
+/// Full Map of all registers
+const REGISTERS: [Reg; 16] = [
+    Reg{ register: Register::RAX, full: "RAX", extended: "EAX",  word: "AX",   byte: "AL" },
+    Reg{ register: Register::RBX, full: "RBX", extended: "EBX",  word: "BX",   byte: "BL" },
+    Reg{ register: Register::RCX, full: "RCX", extended: "ECX",  word: "CX",   byte: "CL" },
+    Reg{ register: Register::RDX, full: "RDX", extended: "EDX",  word: "DX",   byte: "DL" },
+    Reg{ register: Register::RBP, full: "RBP", extended: "EBP",  word: "BP",   byte: "BPL" },
+    Reg{ register: Register::RSP, full: "RSP", extended: "ESP",  word: "SP",   byte: "SPL" },
+    Reg{ register: Register::RSI, full: "RSI", extended: "ESI",  word: "SI",   byte: "SIL" },
+    Reg{ register: Register::RDI, full: "RDI", extended: "EDI",  word: "DI",   byte: "DIL" },
+    Reg{ register: Register::R8,  full: "R8",  extended: "R8D",  word: "R8W",  byte: "R8B" },
+    Reg{ register: Register::R9,  full: "R9",  extended: "R9D", word: "R9W",  byte: "R9B" },
+    Reg{ register: Register::R10, full: "R10", extended: "R10D", word: "R10W", byte: "R10B" },
+    Reg{ register: Register::R11, full: "R11", extended: "R11D", word: "R11W", byte: "R11B" },
+    Reg{ register: Register::R12, full: "R12", extended: "R12D", word: "R12W", byte: "R12B" },
+    Reg{ register: Register::R13, full: "R13", extended: "R13D", word: "R13W", byte: "R13B" },
+    Reg{ register: Register::R14, full: "R14", extended: "R14D", word: "R14W", byte: "R14B" },
+    Reg{ register: Register::R15, full: "R15", extended: "R15D", word: "R15W", byte: "R15B" }
+];
+
+
+/// Trade a Register Enum to Reg() -> Used to get more info
+pub fn get_register(register: Register) -> Reg<'static> {
+
+    let out_register = REGISTERS[0].clone();
+    for reg in REGISTERS.iter() {
+        if register == reg.register {
+            return reg.clone();
+        }
+    }
+
+    return out_register;
+}
+
+
+/// Simply returns a random x64 register
+/// If the generated register is in the blacklist,
+/// return anotherone
+pub fn get_random_reg(blacklist: &Vec<Register>) -> Reg<'static> {
+
+    // generate registers until the register is not in the blacklist
+    loop {
+        // Generate a random number as index
+        let rnd_index = rand::random::<usize>() % REGISTERS.len();
+
+        let register: Register = REGISTERS[rnd_index].register.clone();
+
+        if !blacklist.contains(&register) {
+            return get_register(register);
+        }
+    }
+}
+
+const DEAD_CODE_INSTRUCTIONS: [&str; 6] = [
+    "NOP",
+    "CLD",
+    "XOR {R}, 0",
+    "MOV {R}, {R}",
+    "XCHG {R}, {R}",
+    "FNOP"
+];
+
+
+/// Generate an String of dead code
+pub fn generate_dead_code() -> String {
+
+    let mut blacklist = vec![Register::RSP, Register::RBP];
+    // Output String
+    let mut dead_code = String::new();
+    let mut num_instructios = rand::random::<usize>() % 40;
+    num_instructios += 3;
+    for _ in 3..num_instructios {
+        let rnd = rand::random::<usize>() % DEAD_CODE_INSTRUCTIONS.len();
+        let tmp_dead_code = DEAD_CODE_INSTRUCTIONS[rnd];
+        let random_reg = get_random_reg(&blacklist);
+        let tmp_dead_code = tmp_dead_code.replace("{R}", &random_reg.full);
+
+        dead_code.push_str(&tmp_dead_code);
+        dead_code.push('\n');
+    }
+    return dead_code;
+}
+
+

+ 121 - 0
src/encoder.rs

@@ -0,0 +1,121 @@
+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];
+
+    // 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 = "{DEAD_CODE_1}\n\
+    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\
+    {DEAD_CODE_2}\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}", &seed.to_string());
+    let stub = stub.replace("{S}", &payload_size.to_string());
+    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));
+}
+
+
+
+pub fn gen_dead_code() {
+
+    let dead_code = x64::generate_dead_code();
+    let assembled = assemble_asm(dead_code);
+
+    println!("{:?}", assembled);
+}
+
+/// The function uses capstone to assemble the stub and retrieve the raw bytes
+fn assemble_asm(stub: String) -> Vec<u8> {
+
+    // Create the engine
+    let engine = Keystone::from(Arch::X86, Mode::Bit64)
+        .expect("Unable to initialize Keystone engine");
+
+    // Set the Syntax to NASM (not Intel)
+    engine.option(OptionType::Syntax, OptionValue::SyntaxNasm)
+        .expect("Unable to set NASM syntax");
+
+    // Assemble the stub and return the bytes
+    let result = engine.asm(&stub, 0)
+        .expect("Unable to assemble");
+
+    result.encoding().to_vec()
+}
+
+/// This function does the plain shellcode encoding (XOR)
+pub fn encode(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
+}
+
+
+/// 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();
+
+    // initialize the key
+    let mut key = seed;
+
+    for byte in payload {
+        let orig_byte = byte ^ key;
+        println!("0x{:x} ^ 0x{:x} -> 0x{:x}", byte, key, orig_byte);
+        decoded_payload.push(orig_byte);
+        key = orig_byte;
+    }
+
+    decoded_payload
+}
+

+ 96 - 0
src/main.rs

@@ -0,0 +1,96 @@
+//use std::io::{BufReader
+use hex;
+use rand;
+use std::fs::read_to_string;
+use std::fs::File;
+use std::io::{Write, Read};
+use std::io::{Seek, SeekFrom};
+use std::env;
+
+
+mod encoder;
+mod cpu_data;
+
+fn usage(args: &Vec<String>) {
+        println!("Usage: {} -i shellcode.bin -o shellcode.out.bin", args[0]);
+        std::process::exit(1);
+}
+
+fn main() {
+
+    let number_of_loops = 3;
+
+    // Parsing the Arguments
+    let args: Vec<String> = env::args().collect();
+
+    if args.len() != 5 {
+        usage(&args);
+    }
+
+    if args[1] != "-i" || args[3] != "-o" {
+        usage(&args);
+    }
+
+    // Get the Input/Output File-Paths
+    let input_file = &args[2];
+    let output_file = &args[4];
+
+
+
+    let mut shellcode = match File::open(input_file) {
+        Ok(v) => v,
+        Err(_) => {
+            println!("[-] Input File not found, exiting");
+            std::process::exit(1);
+        }
+    };
+
+
+    // Obtain the Size of the input
+    let size = shellcode.seek(SeekFrom::End(0)).unwrap();
+    println!("[+] Reading 0x{:x} Bytes from {}", size, input_file);
+    shellcode.seek(SeekFrom::Start(0)).unwrap();
+
+    // Input Buffer for the shellcode
+    let mut payload = vec![0; size as usize];
+    shellcode.read(&mut payload);
+
+    // Output Buffer for the final Payload
+    let mut final_payload: Vec<u8> = Vec::new();
+
+    for i in 0..number_of_loops {
+
+        println!("[*] Encoding Round {}", i);
+
+        // Generate random key
+        let key: u8 = rand::random();
+
+        // encode the payload
+        let mut encoded_payload = encoder::encode(payload.clone(), key);
+
+        //println!("[*] Generating Stub...");
+        let mut stub = encoder::get_decoder_stub(key, encoded_payload.len());
+
+        // Append the encoded_payload to the stub
+        stub.append(&mut encoded_payload);
+
+        // Set the [stub+payload] as the new raw payload
+        let payload = stub.clone();
+    }
+
+
+    final_payload.append(&mut payload);
+
+    //Write Payload to File:
+    let mut file = match File::create(output_file) {
+        Ok(v) => v,
+        Err(_) => {
+            println!("[-] Cannot open Output File, exiting");
+            std::process::exit(1);
+        }
+    };
+    file.write(&final_payload);
+    println!("[+] Done, Generated encoded payload @ {}", output_file);
+
+}
+