mod rules;
use std::io::{Cursor, Read};
use anyhow::{Result, anyhow};
use iced_x86::{Code, Decoder, DecoderOptions, Instruction, Mnemonic, Register};
use yara_x::{Compiler, Scanner};
use zip::ZipArchive;
use crate::configuration::MalwareConfiguration;
use rules::RULE_DGA;
pub fn extract(sample_data: &[u8]) -> Result<MalwareConfiguration> {
let cursor = Cursor::new(sample_data);
let mut archive = ZipArchive::new(cursor)?;
let filename = archive
.file_names()
.find(|filename| filename.contains("lib/x86_64/"))
.map(|s| s.to_owned());
if let Some(filename) = filename
&& let Ok(mut zipfile) = archive.by_name(&filename)
{
let mut buff = Vec::with_capacity(zipfile.size() as usize);
zipfile.read_to_end(&mut buff)?;
let (customer, tag) = extract_from_elf(&buff)?;
let mut config = MalwareConfiguration::from((sample_data, "Coper"));
if let Some(customer) = customer {
config
.data
.dga_parameters
.strings
.insert("customer".to_string(), customer);
}
if let Some(tag) = tag {
config
.data
.dga_parameters
.strings
.insert("tag".to_string(), tag);
}
return Ok(config);
}
Err(anyhow!("No file 'lib/x86_64/*.so' found in archive"))
}
fn extract_from_elf(elf_data: &[u8]) -> Result<(Option<String>, Option<String>)> {
let mut compiler = Compiler::new();
compiler.add_source(RULE_DGA)?;
let rules = compiler.build();
let mut scanner = Scanner::new(&rules);
let results = scanner.scan(elf_data)?;
let mat = results
.matching_rules()
.next()
.and_then(|r| r.patterns().next())
.and_then(|p| p.matches().next())
.ok_or(anyhow!("could not find 'make_DGA' in binary"))?;
let stack = simulate_stack(elf_data, mat.range().start);
let strings: Vec<String> = stack
.split(|s| *s == 0)
.filter(|s| !s.is_empty())
.filter_map(|bytes| String::from_utf8(bytes.to_vec()).ok())
.collect();
let tag = strings.first().cloned();
let customer = strings.get(1).cloned();
Ok((customer, tag))
}
fn simulate_stack(elf_data: &[u8], start: usize) -> [u8; 100] {
let mut stack = [0u8; 100];
let decoder = Decoder::with_ip(
64,
&elf_data[start..start + 200],
start as u64,
DecoderOptions::NONE,
);
let mut xmm_reg = Vec::new();
let mut rax_reg = Vec::new();
for instruction in decoder.into_iter().skip(1) {
if matches!(instruction.mnemonic(), Mnemonic::Call) {
break;
}
match instruction.code() {
Code::Movaps_xmm_xmmm128 => {
let offset = instruction.memory_displacement64() as usize;
let data = elf_data[offset..offset + 16].to_vec();
xmm_reg = data;
}
Code::Movaps_xmmm128_xmm => {
let base = instruction.memory_displacement64() as usize;
for (i, e) in xmm_reg.iter().enumerate() {
stack[base + i] = *e;
}
}
Code::Mov_r64_imm64 => {
let displ_data = instruction.memory_displacement32().to_le_bytes();
let immediate_data = instruction.immediate32().to_le_bytes();
rax_reg = [immediate_data, displ_data].concat();
}
Code::Mov_rm64_r64 => {
if matches!(instruction.memory_base(), Register::None)
|| !(matches!(instruction.op0_register(), Register::None)
&& matches!(instruction.op1_register(), Register::RAX))
{
continue;
}
let base = instruction.memory_displacement64() as usize;
for (i, e) in rax_reg.iter().enumerate() {
stack[base + i] = *e;
}
}
Code::Mov_rm32_imm32 => get_from_immediate(&mut stack, &instruction, 32),
Code::Mov_rm16_imm16 => get_from_immediate(&mut stack, &instruction, 16),
Code::Mov_rm8_imm8 => get_from_immediate(&mut stack, &instruction, 8),
_ => (),
}
}
stack
}
fn get_from_immediate(stack: &mut [u8], instruction: &Instruction, size: u32) {
let base = instruction.memory_displacement64() as usize;
let immediate = match size {
8 => instruction.immediate8().to_le_bytes().to_vec(),
16 => instruction.immediate16().to_le_bytes().to_vec(),
32 => instruction.immediate32().to_le_bytes().to_vec(),
_ => unreachable!(),
};
for (i, e) in immediate.iter().enumerate() {
stack[base + i] = *e;
}
}