This commit is contained in:
sad 2024-10-04 17:14:11 +00:00
parent f1e1d9ec90
commit 61db822921
3 changed files with 27 additions and 63 deletions

View File

@ -1,10 +0,0 @@
use anyhow::{Context, Result};
use std::fs;
// Read signatures file
pub fn read_signatures(path: &str) -> Result<Vec<String>> {
// Read path
let content = fs::read_to_string(path)
.with_context(|| format!("Could not read signatures file {}", path))?;
Ok(content.lines().map(String::from).collect())
}

View File

@ -1,8 +1,7 @@
use anyhow::{anyhow, Context, Result};
use rand::Rng;
use regex::Regex;
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
use rand::Rng;
use anyhow::{Context, Result, anyhow};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Payload { pub enum Payload {
@ -23,16 +22,14 @@ pub fn parse_signatures(file_path: &str) -> Result<Vec<Signature>> {
for (index, line) in reader.lines().enumerate() { for (index, line) in reader.lines().enumerate() {
let line = line.context("Failed to read line from signatures file")?; let line = line.context("Failed to read line from signatures file")?;
if line.trim().is_empty() { if line.trim().is_empty() {
continue; // Skip empty lines continue;
} }
let payload = if line.contains('(') && line.contains(')') { let payload = if line.contains('(') && line.contains(')') {
Payload::Regex(line) Payload::Regex(line)
} else { } else {
Payload::Raw( Payload::Raw(unescape_string(&line)
unescape_string(&line) .with_context(|| format!("Invalid payload on line {}", index + 1))?)
.with_context(|| format!("Invalid payload on line {}", index + 1))?,
)
}; };
signatures.push(Signature { payload }); signatures.push(Signature { payload });
@ -53,13 +50,12 @@ fn unescape_string(s: &str) -> Result<Vec<u8>> {
if c == '\\' { if c == '\\' {
match chars.next() { match chars.next() {
Some('x') => { Some('x') => {
let hex = chars let hex = chars.next().and_then(|c1| {
.next() chars.next().map(|c2| format!("{}{}", c1, c2))
.and_then(|c1| chars.next().map(|c2| format!("{}{}", c1, c2))) }).unwrap_or_else(|| {
.unwrap_or_else(|| {
result.push(b'\\'); result.push(b'\\');
result.push(b'x'); result.push(b'x');
return String::new(); String::new()
}); });
if !hex.is_empty() { if !hex.is_empty() {
if let Ok(byte) = u8::from_str_radix(&hex, 16) { if let Ok(byte) = u8::from_str_radix(&hex, 16) {
@ -70,7 +66,7 @@ fn unescape_string(s: &str) -> Result<Vec<u8>> {
result.extend(hex.bytes()); result.extend(hex.bytes());
} }
} }
} },
Some('0') => result.push(0), Some('0') => result.push(0),
Some('n') => result.push(b'\n'), Some('n') => result.push(b'\n'),
Some('r') => result.push(b'\r'), Some('r') => result.push(b'\r'),
@ -94,7 +90,6 @@ pub fn generate_payload(signature: &Signature) -> Vec<u8> {
} }
fn generate_regex_match(regex_str: &str) -> Vec<u8> { fn generate_regex_match(regex_str: &str) -> Vec<u8> {
// Simplified regex matching that doesn't rely on the regex crate
let mut result = String::new(); let mut result = String::new();
let mut chars = regex_str.chars().peekable(); let mut chars = regex_str.chars().peekable();
@ -106,10 +101,7 @@ fn generate_regex_match(regex_str: &str) -> Vec<u8> {
'd' => result.push(rand::thread_rng().gen_range(b'0'..=b'9') as char), 'd' => result.push(rand::thread_rng().gen_range(b'0'..=b'9') as char),
'w' => result.push(rand::thread_rng().gen_range(b'a'..=b'z') as char), 'w' => result.push(rand::thread_rng().gen_range(b'a'..=b'z') as char),
'x' => { 'x' => {
// Handle \x hex escapes let hex = chars.next().and_then(|c1| chars.next().map(|c2| format!("{}{}", c1, c2)))
let hex = chars
.next()
.and_then(|c1| chars.next().map(|c2| format!("{}{}", c1, c2)))
.unwrap_or_else(|| "00".to_string()); .unwrap_or_else(|| "00".to_string());
if let Ok(byte) = u8::from_str_radix(&hex, 16) { if let Ok(byte) = u8::from_str_radix(&hex, 16) {
result.push(byte as char); result.push(byte as char);
@ -118,39 +110,25 @@ fn generate_regex_match(regex_str: &str) -> Vec<u8> {
_ => result.push(next_char), _ => result.push(next_char),
} }
} }
} },
'[' => { '[' => {
let mut class = String::new(); let mut class = String::new();
while let Some(class_char) = chars.next() { for class_char in chars.by_ref() {
if class_char == ']' { if class_char == ']' { break; }
break;
}
class.push(class_char); class.push(class_char);
} }
if !class.is_empty() { if !class.is_empty() {
result.push( result.push(class.chars().nth(rand::thread_rng().gen_range(0..class.len())).unwrap());
class
.chars()
.nth(rand::thread_rng().gen_range(0..class.len()))
.unwrap(),
);
}
} }
},
'(' => { '(' => {
// Skip capturing groups
let mut depth = 1; let mut depth = 1;
while let Some(group_char) = chars.next() { for group_char in chars.by_ref() {
if group_char == '(' { if group_char == '(' { depth += 1; }
depth += 1; if group_char == ')' { depth -= 1; }
} if depth == 0 { break; }
if group_char == ')' {
depth -= 1;
}
if depth == 0 {
break;
}
}
} }
},
'+' | '*' => { '+' | '*' => {
if let Some(last_char) = result.chars().last() { if let Some(last_char) = result.chars().last() {
let repeat = rand::thread_rng().gen_range(0..5); let repeat = rand::thread_rng().gen_range(0..5);
@ -158,7 +136,7 @@ fn generate_regex_match(regex_str: &str) -> Vec<u8> {
result.push(last_char); result.push(last_char);
} }
} }
} },
'.' => result.push(rand::thread_rng().gen_range(b'!'..=b'~') as char), '.' => result.push(rand::thread_rng().gen_range(b'!'..=b'~') as char),
_ => result.push(c), _ => result.push(c),
} }
@ -187,9 +165,6 @@ mod tests {
assert_eq!(unescape_string(r"\0\r\n\t").unwrap(), b"\0\r\n\t"); assert_eq!(unescape_string(r"\0\r\n\t").unwrap(), b"\0\r\n\t");
assert_eq!(unescape_string(r"Incomplete\").unwrap(), b"Incomplete\\"); assert_eq!(unescape_string(r"Incomplete\").unwrap(), b"Incomplete\\");
assert_eq!(unescape_string(r"Incomplete\x").unwrap(), b"Incomplete\\x"); assert_eq!(unescape_string(r"Incomplete\x").unwrap(), b"Incomplete\\x");
assert_eq!( assert_eq!(unescape_string(r"Incomplete\x4").unwrap(), b"Incomplete\\x4");
unescape_string(r"Incomplete\x4").unwrap(),
b"Incomplete\\x4"
);
} }
} }

View File

@ -4,11 +4,10 @@ use tokio::net::TcpListener;
use tracing::{debug, error, info, Level}; use tracing::{debug, error, info, Level};
mod cli; mod cli;
mod config;
mod handler; mod handler;
use cli::Cli; use cli::Cli;
use handler::{generate_payload, parse_signatures, Signature}; use handler::{generate_payload, parse_signatures};
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {