ClamAVのシグネチャをヒューマンリーダブルな形にパースしたい part5
はじめに
前回、LogicalExpressionをパースした。
今回はSubsigをパースする。ただのHexStringでしょ。
Subsig
https://docs.clamav.net/manual/Signatures/LogicalSignatures.html
ドキュメントを読むと、実はただのHexStringじゃなくて色々オプションがあって困るので、まずはModifierついてる奴のパースからやる。
Modifierは4種類あり、
'i'
がCase-Insensitive
'w'
がWide
'f'
がFullword
'a'
がAscii
に対応している。シンプルで良いね。
use nom::{
bytes::complete::{tag, take_while},
combinator::{map, opt},
sequence::tuple,
IResult,
};
use anyhow::{anyhow, Result};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Subsignature<'s> {
pattern: &'s str,
modifiers: Vec<Modifier>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Modifier {
CaseInsensitive,
Wide,
Fullword,
Ascii,
Unknown(char)
}
impl<'s> Subsignature<'s> {
pub fn parse(input: &'s str) -> Result<Subsignature<'s>> {
parse_subsignature(&input).map_err(|e| anyhow!("Can't parse Subsignature: {}", e)).map(|(_, subsig)| subsig)
}
}
fn parse_modifier(input: &str) -> IResult<&str, Vec<Modifier>> {
let (input, m) = take_while(|c: char| c.is_alphabetic())(input)?;
let mut modifiers = Vec::new();
for c in m.chars() {
match c {
'i' => modifiers.push(Modifier::CaseInsensitive),
'w' => modifiers.push(Modifier::Wide),
'f' => modifiers.push(Modifier::Fullword),
'a' => modifiers.push(Modifier::Ascii),
c => modifiers.push(Modifier::Unknown(c)),
}
}
Ok((input, modifiers))
}
fn parse_subsignature<'s>(input: &'s str) -> IResult<&'s str, Subsignature<'s>> {
let (remaining, (pattern, modifiers)) = tuple((
take_while(|c: char| c.is_digit(16)),
opt(map(
tuple((tag("::"), parse_modifier)),
|(_, mods)| mods,
)),
))(input)?;
let modifiers = modifiers.unwrap_or_else(Vec::new);
Ok((remaining, Subsignature { pattern, modifiers }))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_nocase() {
let sig = Subsignature::parse("424242424242::i").unwrap();
assert_eq!(Subsignature {
pattern: "424242424242",
modifiers: vec![Modifier::CaseInsensitive]
}, sig);
}
#[test]
fn parse_fullword() {
let sig = Subsignature::parse("68656c6c6f::f").unwrap();
assert_eq!(Subsignature {
pattern: "68656c6c6f",
modifiers: vec![Modifier::Fullword]
}, sig);
}
#[test]
fn parse_simple() {
let sig = Subsignature::parse("41414141").unwrap();
assert_eq!(Subsignature {
pattern: "41414141",
modifiers: vec![]
}, sig);
}
#[test]
fn parse_nocase_wide_fullword_ascii() {
let sig = Subsignature::parse("68656c6c6f::iwfa").unwrap();
assert_eq!(Subsignature {
pattern: "68656c6c6f",
modifiers: vec![Modifier::CaseInsensitive, Modifier::Wide, Modifier::Fullword, Modifier::Ascii]
}, sig);
}
}
非常にわかりやすいコードです。
で、Hex Stringの部分をいい感じにした上で
CaseInsensitive
ならnocase
Wide
ならwide
Fullword
ならfullword
Ascii
ならascii
をYARAの方にもつければ良いだけ。ここが一致しててよかった。
次はHex部分をがんばってパースします。今日は忙しかったのでここまで。
終わりに
この記事はn01e0 Advent Calendar 2024の12日目の記事とします。
Comments