diff --git a/Cargo.toml b/Cargo.toml index 76f5d3a..08322f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,12 @@ categories = ["parser-implementations", "command-line-utilities"] exclude = ["tests/pocs/blob"] [workspace] -members = [".", "./src/proc-macros", "./wasm"] +members = [ + ".", + "./sigscanpro", + "./src/proc-macros", + "./wasm", +] [features] default = ["mmap", "derive_pod"] diff --git a/sigscanpro/Cargo.toml b/sigscanpro/Cargo.toml new file mode 100644 index 0000000..1fa754e --- /dev/null +++ b/sigscanpro/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "sigscanpro" +version = "0.1.0" +authors = ["Casper "] +edition = "2021" + +[lib] +crate-type = ["rlib", "staticlib", "cdylib"] + +[dependencies] +pelite = { path = "../" } diff --git a/sigscanpro/readme.md b/sigscanpro/readme.md new file mode 100644 index 0000000..8020506 --- /dev/null +++ b/sigscanpro/readme.md @@ -0,0 +1,4 @@ +SigScanPro +========== + +Wrapper around the pelite pattern scanner. diff --git a/sigscanpro/sigscanpro.h b/sigscanpro/sigscanpro.h new file mode 100644 index 0000000..4c12ba8 --- /dev/null +++ b/sigscanpro/sigscanpro.h @@ -0,0 +1,61 @@ +#pragma once + +#include + +extern "C" { + + +struct Pattern { + const uint16_t* ptr; + size_t len; +}; + +struct Pattern PelitePatternParse(const char* string); + +void PelitePatternFree(struct Pattern pattern); + + + +struct PeView64 { + const uint8_t* ptr; + size_t len; + uint64_t va; +}; + +struct PeView64 PeliteView64(const uint8_t* image, uint64_t base_address); + +bool PeliteView64Finds(const struct PeView64* view, const char* pat, const char* sect, uint32_t* save_ptr, size_t save_len); + + + +struct PeViewMatches64 { + size_t inner[0x38 / 8]; +}; + +struct PeViewMatches64 PeliteView64Matches(const struct PeView64* view, const Pattern* pat, const char* sect); + +bool PeliteView64MatchesNext(struct PeViewMatches64* matches, uint32_t* save_ptr, size_t save_len); + + + +struct PeFile64 { + const uint8_t* ptr; + size_t len; +}; + +struct PeFile64 PeliteFile64(const uint8_t* ptr, size_t len); + +bool PeliteFile64Finds(const struct PeFile64* file, const char* pat, const char* sect, uint32_t* save_ptr, size_t save_len); + + + +struct PeFileMatches64 { + size_t inner[0x30 / 8]; +}; + +struct PeFileMatches64 PeliteFile64Matches(const struct PeFile64* file, const Pattern* pat, const char* sect); + +bool PeliteFile64MatchesNext(struct PeFileMatches64* matches, uint32_t* save_ptr, size_t save_len); + + +} diff --git a/sigscanpro/src/lib.rs b/sigscanpro/src/lib.rs new file mode 100644 index 0000000..69bfa70 --- /dev/null +++ b/sigscanpro/src/lib.rs @@ -0,0 +1,49 @@ +#![allow(non_snake_case)] + +use core::ffi::*; +use core::{mem, slice, ptr}; + +#[repr(C)] +pub struct Pattern { + ptr: *const u16, + len: usize, +} +impl Pattern { + fn rust(&self) -> &[pelite::pattern::Atom] { + unsafe { slice::from_raw_parts(self.ptr as *const pelite::pattern::Atom, self.len) } + } +} + +#[no_mangle] +pub unsafe extern "C" fn PelitePatternParse(string: *const c_char) -> Pattern { + let string = CStr::from_ptr(string); + let string = match string.to_str() { + Ok(string) => string, + Err(err) => { + eprintln!("PelitePatternParse: {}", err); + return Pattern { ptr: ptr::null(), len: 0 }; + } + }; + let (ptr, len) = match pelite::pattern::parse(string) { + Ok(pattern) => { + let slice = &*Box::into_raw(pattern.into_boxed_slice()); + (slice.as_ptr(), slice.len()) + } + Err(err) => { + eprintln!("PelitePatternParse: {}", err); + return Pattern { ptr: ptr::null(), len: 0 }; + } + }; + Pattern { ptr: ptr as *const u16, len } +} + +#[no_mangle] +pub unsafe extern "C" fn PelitePatternFree(pat: Pattern) { + if !pat.ptr.is_null() { + let slice = slice::from_raw_parts_mut(pat.ptr as *mut u16, pat.len); + let _ = Box::from_raw(slice); + } +} + +pub mod pe64; +pub mod pe32; diff --git a/sigscanpro/src/pe32.rs b/sigscanpro/src/pe32.rs new file mode 100644 index 0000000..ba0ce0c --- /dev/null +++ b/sigscanpro/src/pe32.rs @@ -0,0 +1,179 @@ +use super::*; +use pelite::pe32 as pe; +use pelite::pe32::Pe; + +#[repr(C)] +pub struct PeView32 { + data: [usize; mem::size_of::() / mem::size_of::()], +} +impl PeView32 { + fn rust(&self) -> pe::PeView { + unsafe { *(self.data.as_ptr() as *const pe::PeView) } + } +} + +#[no_mangle] +pub unsafe extern "C" fn PeliteView32(image: *const u8, base_address: u32) -> PeView32 { + let view = pe::PeView::module(image).set_base_address(base_address); + PeView32 { data: mem::transmute(view) } +} + +#[no_mangle] +pub unsafe extern "C" fn PeliteView32Finds(view: *const PeView32, pat: *const c_char, sect: *const c_char, save_ptr: *mut u32, save_len: usize) -> bool { + // Parse the pattern + let pat = CStr::from_ptr(pat); + let pat = match pat.to_str() { + Ok(pat) => pat, + Err(err) => { + #[cfg(debug_assertions)] + eprintln!("PelitePatternParse: {}", err); + return false; + } + }; + let pat = match pelite::pattern::parse(pat) { + Ok(pat) => pat, + Err(err) => { + #[cfg(debug_assertions)] + eprintln!("PelitePatternParse: {}", err); + return false; + } + }; + + let view = (*view).rust(); + + // Find the section by name + let mut range = view.headers().image_range(); + if !sect.is_null() { + let sect = CStr::from_ptr(sect).to_bytes(); + if let Some(sect) = view.section_headers().by_name(sect) { + range = sect.virtual_range(); + } + } + + // Find the pattern + let save = slice::from_raw_parts_mut(save_ptr, save_len); + view.scanner().finds(&pat, range, save) +} + +// type RustPeViewMatches<'a, 'pat> = pe::scanner::Matches<'pat, pe::PeView<'a>>; +// type RustPeFileMatches<'a, 'pat> = pe::scanner::Matches<'pat, pe::PeFile<'a>>; + +#[repr(C)] +pub struct PeViewMatches32 { + data: [usize; mem::size_of::>() / mem::size_of::()], +} +impl PeViewMatches32 { + fn rust<'a>(&mut self) -> &mut pe::scanner::Matches<'a, pe::PeView> { + unsafe { &mut *(self.data.as_mut_ptr() as *mut pe::scanner::Matches<'a, pe::PeView>) } + } +} + +#[no_mangle] +pub unsafe extern "C" fn PeliteView32Matches(view: *const PeView32, pat: *const Pattern, sect: *const c_char) -> PeViewMatches32 { + let view = (*view).rust(); + let pat = (*pat).rust(); + let mut range = view.headers().image_range(); + if !sect.is_null() { + if let Ok(sect) = CStr::from_ptr(sect).to_str() { + if let Some(sect) = view.section_headers().by_name(sect) { + range = sect.virtual_range(); + } + } + } + let matches = view.scanner().matches(pat, range); + PeViewMatches32 { data: mem::transmute(matches) } +} + +#[no_mangle] +pub unsafe extern "C" fn PeliteView32MatchesNext(matches: *mut PeViewMatches32, save_ptr: *mut u32, save_len: usize) -> bool { + let matches = (*matches).rust(); + let save = slice::from_raw_parts_mut(save_ptr, save_len); + matches.next(save) +} + +#[repr(C)] +pub struct PeFile32 { + data: [usize; mem::size_of::() / mem::size_of::()], +} +impl PeFile32 { + fn rust(&self) -> pe::PeFile { + unsafe { *(self.data.as_ptr() as *const pe::PeFile) } + } +} + +#[no_mangle] +pub unsafe extern "C" fn PeliteFile32(ptr: *const u8, len: usize) -> PeFile32 { + let bytes = slice::from_raw_parts(ptr, len); + let file = pe::PeFile::from_bytes(bytes).unwrap(); + PeFile32 { data: mem::transmute(file) } +} + +#[no_mangle] +pub unsafe extern "C" fn PeliteFile32Finds(file: *const PeFile32, pat: *const c_char, sect: *const c_char, save_ptr: *mut u32, save_len: usize) -> bool { + // Parse the pattern + let pat = CStr::from_ptr(pat); + let pat = match pat.to_str() { + Ok(pat) => pat, + Err(err) => { + #[cfg(debug_assertions)] + eprintln!("PelitePatternParse: {}", err); + return false; + } + }; + let pat = match pelite::pattern::parse(pat) { + Ok(pat) => pat, + Err(err) => { + #[cfg(debug_assertions)] + eprintln!("PelitePatternParse: {}", err); + return false; + } + }; + + let file = (*file).rust(); + + // Find the section by name + let mut range = file.headers().image_range(); + if !sect.is_null() { + let sect = CStr::from_ptr(sect).to_bytes(); + if let Some(sect) = file.section_headers().by_name(sect) { + range = sect.virtual_range(); + } + } + + // Find the pattern + let save = slice::from_raw_parts_mut(save_ptr, save_len); + file.scanner().finds(&pat, range, save) +} + +#[repr(C)] +pub struct PeFileMatches32 { + data: [usize; mem::size_of::>() / mem::size_of::()], +} +impl PeFileMatches32 { + fn rust(&mut self) -> &mut pe::scanner::Matches { + unsafe { &mut *(self.data.as_mut_ptr() as *mut pe::scanner::Matches) } + } +} + +#[no_mangle] +pub unsafe extern "C" fn PeliteFile32Matches(file: *const PeFile32, pat: *const Pattern, sect: *const c_char) -> PeFileMatches32 { + let file = (*file).rust(); + let pat = (*pat).rust(); + let mut range = file.headers().image_range(); + if !sect.is_null() { + if let Ok(sect) = CStr::from_ptr(sect).to_str() { + if let Some(sect) = file.section_headers().by_name(sect) { + range = sect.virtual_range(); + } + } + } + let matches = file.scanner().matches(pat, range); + PeFileMatches32 { data: mem::transmute(matches) } +} + +#[no_mangle] +pub unsafe extern "C" fn PeliteFile32MatchesNext(matches: *mut PeFileMatches32, save_ptr: *mut u32, save_len: usize) -> bool { + let matches = (*matches).rust(); + let save = slice::from_raw_parts_mut(save_ptr, save_len); + matches.next(save) +} diff --git a/sigscanpro/src/pe64.rs b/sigscanpro/src/pe64.rs new file mode 100644 index 0000000..0924d1d --- /dev/null +++ b/sigscanpro/src/pe64.rs @@ -0,0 +1,179 @@ +use super::*; +use pelite::pe64 as pe; +use pelite::pe64::Pe; + +#[repr(C)] +pub struct PeView64 { + data: [usize; mem::size_of::() / mem::size_of::()], +} +impl PeView64 { + fn rust(&self) -> pe::PeView { + unsafe { *(self.data.as_ptr() as *const pe::PeView) } + } +} + +#[no_mangle] +pub unsafe extern "C" fn PeliteView64(image: *const u8, base_address: u64) -> PeView64 { + let view = pe::PeView::module(image).set_base_address(base_address); + PeView64 { data: mem::transmute(view) } +} + +#[no_mangle] +pub unsafe extern "C" fn PeliteView64Finds(view: *const PeView64, pat: *const c_char, sect: *const c_char, save_ptr: *mut u32, save_len: usize) -> bool { + // Parse the pattern + let pat = CStr::from_ptr(pat); + let pat = match pat.to_str() { + Ok(pat) => pat, + Err(err) => { + #[cfg(debug_assertions)] + eprintln!("PelitePatternParse: {}", err); + return false; + } + }; + let pat = match pelite::pattern::parse(pat) { + Ok(pat) => pat, + Err(err) => { + #[cfg(debug_assertions)] + eprintln!("PelitePatternParse: {}", err); + return false; + } + }; + + let view = (*view).rust(); + + // Find the section by name + let mut range = view.headers().image_range(); + if !sect.is_null() { + let sect = CStr::from_ptr(sect).to_bytes(); + if let Some(sect) = view.section_headers().by_name(sect) { + range = sect.virtual_range(); + } + } + + // Find the pattern + let save = slice::from_raw_parts_mut(save_ptr, save_len); + view.scanner().finds(&pat, range, save) +} + +// type RustPeViewMatches<'a, 'pat> = pe::scanner::Matches<'pat, pe::PeView<'a>>; +// type RustPeFileMatches<'a, 'pat> = pe::scanner::Matches<'pat, pe::PeFile<'a>>; + +#[repr(C)] +pub struct PeViewMatches64 { + data: [usize; mem::size_of::>() / mem::size_of::()], +} +impl PeViewMatches64 { + fn rust<'a>(&mut self) -> &mut pe::scanner::Matches<'a, pe::PeView> { + unsafe { &mut *(self.data.as_mut_ptr() as *mut pe::scanner::Matches<'a, pe::PeView>) } + } +} + +#[no_mangle] +pub unsafe extern "C" fn PeliteView64Matches(view: *const PeView64, pat: *const Pattern, sect: *const c_char) -> PeViewMatches64 { + let view = (*view).rust(); + let pat = (*pat).rust(); + let mut range = view.headers().image_range(); + if !sect.is_null() { + if let Ok(sect) = CStr::from_ptr(sect).to_str() { + if let Some(sect) = view.section_headers().by_name(sect) { + range = sect.virtual_range(); + } + } + } + let matches = view.scanner().matches(pat, range); + PeViewMatches64 { data: mem::transmute(matches) } +} + +#[no_mangle] +pub unsafe extern "C" fn PeliteView64MatchesNext(matches: *mut PeViewMatches64, save_ptr: *mut u32, save_len: usize) -> bool { + let matches = (*matches).rust(); + let save = slice::from_raw_parts_mut(save_ptr, save_len); + matches.next(save) +} + +#[repr(C)] +pub struct PeFile64 { + data: [usize; mem::size_of::() / mem::size_of::()], +} +impl PeFile64 { + fn rust(&self) -> pe::PeFile { + unsafe { *(self.data.as_ptr() as *const pe::PeFile) } + } +} + +#[no_mangle] +pub unsafe extern "C" fn PeliteFile64(ptr: *const u8, len: usize) -> PeFile64 { + let bytes = slice::from_raw_parts(ptr, len); + let file = pe::PeFile::from_bytes(bytes).unwrap(); + PeFile64 { data: mem::transmute(file) } +} + +pub unsafe extern "C" fn PeliteFile64Finds(file: *const PeFile64, pat: *const c_char, sect: *const c_char, save_ptr: *mut u32, save_len: usize) -> bool { + // Parse the pattern + let pat = CStr::from_ptr(pat); + let pat = match pat.to_str() { + Ok(pat) => pat, + Err(err) => { + #[cfg(debug_assertions)] + eprintln!("PelitePatternParse: {}", err); + return false; + } + }; + let pat = match pelite::pattern::parse(pat) { + Ok(pat) => pat, + Err(err) => { + #[cfg(debug_assertions)] + eprintln!("PelitePatternParse: {}", err); + return false; + } + }; + + let file = (*file).rust(); + + // Find the section by name + let mut range = file.headers().image_range(); + if !sect.is_null() { + let sect = CStr::from_ptr(sect).to_bytes(); + if let Some(sect) = file.section_headers().by_name(sect) { + range = sect.virtual_range(); + } + } + + // Find the pattern + let save = slice::from_raw_parts_mut(save_ptr, save_len); + file.scanner().finds(&pat, range, save) +} + +#[repr(C)] +pub struct PeFileMatches64 { + + data: [usize; mem::size_of::>() / mem::size_of::()], +} +impl PeFileMatches64 { + fn rust(&mut self) -> &mut pe::scanner::Matches { + unsafe { &mut *(self.data.as_mut_ptr() as *mut pe::scanner::Matches) } + } +} + +#[no_mangle] +pub unsafe extern "C" fn PeliteFile64Matches(file: *const PeFile64, pat: *const Pattern, sect: *const c_char) -> PeFileMatches64 { + let file = (*file).rust(); + let pat = (*pat).rust(); + let mut range = file.headers().image_range(); + if !sect.is_null() { + if let Ok(sect) = CStr::from_ptr(sect).to_str() { + if let Some(sect) = file.section_headers().by_name(sect) { + range = sect.virtual_range(); + } + } + } + let matches = file.scanner().matches(pat, range); + PeFileMatches64 { data: mem::transmute(matches) } +} + +#[no_mangle] +pub unsafe extern "C" fn PeliteFile64MatchesNext(matches: *mut PeFileMatches64, save_ptr: *mut u32, save_len: usize) -> bool { + let matches = (*matches).rust(); + let save = slice::from_raw_parts_mut(save_ptr, save_len); + matches.next(save) +} diff --git a/sigscanpro/tests/demo.rs b/sigscanpro/tests/demo.rs new file mode 100644 index 0000000..b77da5b --- /dev/null +++ b/sigscanpro/tests/demo.rs @@ -0,0 +1,71 @@ + +#[test] +fn demo64() { + static EXPECTED: &[u32] = &[ + 0x14B5, + 0x153A, + 0x16A0, + 0x1772, + 0x1779, + 0x18A0, + 0x18E4, + 0x1A24, + 0x1B99, + 0x1ED7, + 0x1F8E, + 0x1FB9, + ]; + + let file = std::fs::read("../demo/Demo64.dll").unwrap(); + let file = file.as_slice(); + + unsafe { + let pefile = sigscanpro::pe64::PeliteFile64(file.as_ptr(), file.len()); + + let pat = sigscanpro::PelitePatternParse("E9 ? ? ? ?\0".as_ptr() as *const i8); + + let mut matches = sigscanpro::pe64::PeliteFile64Matches(&pefile, &pat, ".text\0".as_ptr() as *const i8); + + let mut save = [0u32; 4]; + let mut i = 0; + while sigscanpro::pe64::PeliteFile64MatchesNext(&mut matches, save.as_mut_ptr() as *mut u32, save.len()) { + assert_eq!(EXPECTED[i], save[0]); + i += 1; + } + assert_eq!(i, EXPECTED.len()); + + sigscanpro::PelitePatternFree(pat); + } +} + +#[test] +fn demo32() { + static EXPECTED: &[u32] = &[ + 0x14AF, + 0x15C0, + 0x168B, + 0x1692, + 0x1EE0, + ]; + + let file = std::fs::read("../demo/Demo.dll").unwrap(); + let file = file.as_slice(); + + unsafe { + let pefile = sigscanpro::pe32::PeliteFile32(file.as_ptr(), file.len()); + + let pat = sigscanpro::PelitePatternParse("E9 ? ? ? ?\0".as_ptr() as *const i8); + + let mut matches = sigscanpro::pe32::PeliteFile32Matches(&pefile, &pat, ".text\0".as_ptr() as *const i8); + + let mut save = [0u32; 4]; + let mut i = 0; + while sigscanpro::pe32::PeliteFile32MatchesNext(&mut matches, save.as_mut_ptr() as *mut u32, save.len()) { + assert_eq!(EXPECTED[i], save[0]); + i += 1; + } + assert_eq!(i, EXPECTED.len()); + + sigscanpro::PelitePatternFree(pat); + } +} diff --git a/src/proc-macros/pattern.rs b/src/proc-macros/pattern.rs index 4524668..3b76708 100644 --- a/src/proc-macros/pattern.rs +++ b/src/proc-macros/pattern.rs @@ -36,7 +36,7 @@ This requires knowledge with reverse engineering programs. Here's a resource to learn more about signature scanning: [wiki.alliedmods.net](https://wiki.alliedmods.net/Signature_scanning). */ -#![allow(ellipsis_inclusive_range_patterns)] +#![allow(ellipsis_inclusive_range_patterns, unexpected_cfgs)] use std::prelude::v1::*;