proto
This commit is contained in:
parent
57bbb96b14
commit
4c8ce5c648
5
Cargo.toml
Normal file
5
Cargo.toml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"macro",
|
||||||
|
"lib"
|
||||||
|
]
|
18
lib/Cargo.toml
Normal file
18
lib/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
name = "binnpatch"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["b4tman <b4tm4n@mail.ru>"]
|
||||||
|
readme = "../README.md"
|
||||||
|
repository = "https://gitea.b4tman.ru/b4tman/binnpatch"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
binnpatch_macro = { path = "../macro" }
|
||||||
|
data-encoding = "^2.3"
|
||||||
|
memmap = "^0.7"
|
58
lib/examples/patchsomedll.rs
Normal file
58
lib/examples/patchsomedll.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
extern crate binnpatch;
|
||||||
|
extern crate data_encoding;
|
||||||
|
extern crate memmap;
|
||||||
|
|
||||||
|
|
||||||
|
extern crate binnpatch_macro;
|
||||||
|
|
||||||
|
use data_encoding::HEXUPPER;
|
||||||
|
use memmap::{Mmap, MmapMut};
|
||||||
|
use std::fs;
|
||||||
|
use std::fs::{File, OpenOptions};
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
use binnpatch::{BytesPattern, FindPattern, ApplyPatch};
|
||||||
|
use binnpatch_macro::binnvec;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let src_file = "Some.dll";
|
||||||
|
let dst_file = "Some.dll.patched";
|
||||||
|
let src_pattern =
|
||||||
|
"40 3E 1D ?? ?? 12 1C 7C 48 ?? 73 6F 02 22 ?? 61 19 4E 13 60 48 45 19 27 5B";
|
||||||
|
let replacement = binnvec!(06 5A 18 74 2D 62 12 6A 13 4A 2B 0E 6F 0F 36 7A 28 0A 37 67 0A 4B 01 73 0x14);
|
||||||
|
|
||||||
|
let pattern = BytesPattern::from(src_pattern);
|
||||||
|
|
||||||
|
let file = File::open(src_file).expect("src open");
|
||||||
|
|
||||||
|
let src_map = unsafe { Mmap::map(&file) }.expect("src map");
|
||||||
|
let data_src = src_map.deref();
|
||||||
|
|
||||||
|
let offset = data_src.find_pattern_first(&pattern);
|
||||||
|
if 0 == offset {
|
||||||
|
panic!("src pattern not found");
|
||||||
|
}
|
||||||
|
println!("found at {:#010X}", offset);
|
||||||
|
|
||||||
|
if replacement.len() > data_src.len() - offset {
|
||||||
|
panic!("replace overflow");
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(src_map);
|
||||||
|
drop(file);
|
||||||
|
|
||||||
|
fs::copy(src_file, dst_file).expect("file copy");
|
||||||
|
|
||||||
|
let file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open(dst_file)
|
||||||
|
.expect("dst open");
|
||||||
|
let mut dst_map = unsafe { MmapMut::map_mut(&file) }.expect("dst map");
|
||||||
|
let mut data_dst = dst_map.deref_mut();
|
||||||
|
|
||||||
|
data_dst.apply_patch(offset, &replacement);
|
||||||
|
drop(dst_map);
|
||||||
|
|
||||||
|
println!("done");
|
||||||
|
}
|
187
lib/src/lib.rs
Normal file
187
lib/src/lib.rs
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
extern crate data_encoding;
|
||||||
|
|
||||||
|
use data_encoding::HEXUPPER;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
pub struct BytesPattern {
|
||||||
|
bytes: Vec<u8>,
|
||||||
|
mask: Vec<bool>,
|
||||||
|
pi: Vec<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for BytesPattern {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.bytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BytesPattern {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.deref().len()
|
||||||
|
}
|
||||||
|
fn match_at(&self, i: usize, byte: u8) -> bool {
|
||||||
|
byte == self[i] || self.mask[i]
|
||||||
|
}
|
||||||
|
fn prefix_function(&mut self) {
|
||||||
|
let mut pi = vec![0; self.len()];
|
||||||
|
let mut j = 0;
|
||||||
|
for i in 1..self.len() {
|
||||||
|
while j > 0 && !self.match_at(j, self[i]) {
|
||||||
|
j = pi[j - 1];
|
||||||
|
}
|
||||||
|
if self.match_at(j, self[i]) {
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
pi[i] = j;
|
||||||
|
}
|
||||||
|
self.pi = pi;
|
||||||
|
}
|
||||||
|
fn reset_offset_from(&self, current_offset: usize) -> usize {
|
||||||
|
let mut new_offset: usize = 0;
|
||||||
|
if 0 < current_offset && current_offset <= self.len() {
|
||||||
|
new_offset = self.pi[current_offset - 1];
|
||||||
|
}
|
||||||
|
new_offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for BytesPattern {
|
||||||
|
fn from(str_val: &str) -> Self {
|
||||||
|
let mut elements: Vec<&str> = str_val.split(' ').collect();
|
||||||
|
let mask: Vec<bool> = elements.iter().map(|item| *item == "??").collect();
|
||||||
|
|
||||||
|
for (i, &item) in mask.iter().enumerate() {
|
||||||
|
if item {
|
||||||
|
elements[i] = "00";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut s = Self {
|
||||||
|
bytes: HEXUPPER
|
||||||
|
.decode(elements.join("").as_bytes())
|
||||||
|
.expect("decode pattern"),
|
||||||
|
mask,
|
||||||
|
pi: Vec::<usize>::new(),
|
||||||
|
};
|
||||||
|
s.prefix_function();
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FindPattern {
|
||||||
|
fn find_pattern_first(&self, pattern: &BytesPattern) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ApplyPatch {
|
||||||
|
fn apply_patch(&mut self, offset: usize, patch: &[u8]);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FindPattern for &[u8] {
|
||||||
|
fn find_pattern_first(&self, pattern: &BytesPattern) -> usize {
|
||||||
|
let mut offset_result = 0;
|
||||||
|
let mut offset_pattern = 0;
|
||||||
|
let data_lenght = self.len();
|
||||||
|
let pattern_lenght = pattern.len();
|
||||||
|
|
||||||
|
for (i, &item) in self.iter().enumerate() {
|
||||||
|
if data_lenght - i < pattern_lenght - offset_pattern {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if pattern.match_at(offset_pattern, item) {
|
||||||
|
offset_pattern += 1;
|
||||||
|
if offset_pattern == pattern_lenght {
|
||||||
|
offset_result = i + 1 - pattern_lenght;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
offset_pattern = pattern.reset_offset_from(offset_pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset_result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApplyPatch for &mut [u8] {
|
||||||
|
fn apply_patch(&mut self, offset: usize, patch: &[u8]) {
|
||||||
|
for (i, &item) in patch.iter().enumerate() {
|
||||||
|
self[offset + i] = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bytes_from_pattern() {
|
||||||
|
let src_pattern = "E8 4D F8 FF 83 C4 85 C0 5F 75 32 8B 54 24 48 50 50";
|
||||||
|
let pattern = BytesPattern::from(src_pattern);
|
||||||
|
let bytes: Vec<u8> = vec![
|
||||||
|
0xE8, 0x4D, 0xF8, 0xFF, 0x83, 0xC4, 0x85, 0xC0, 0x5F, 0x75, 0x32, 0x8B, 0x54, 0x24,
|
||||||
|
0x48, 0x50, 0x50,
|
||||||
|
];
|
||||||
|
assert_eq!(bytes, pattern.bytes);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn mask_from_pattern() {
|
||||||
|
let src_pattern = "E8 ?? ?? FF ?? C4 ?? ?? 5F 75 32 ?? 54 24 48 50 50";
|
||||||
|
let pattern = BytesPattern::from(src_pattern);
|
||||||
|
let mask: Vec<bool> = vec![
|
||||||
|
false, true, true, false, true, false, true, true, false, false, false, true, false,
|
||||||
|
false, false, false, false,
|
||||||
|
];
|
||||||
|
assert_eq!(mask, pattern.mask);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn find_pattern_first() {
|
||||||
|
let src_data: Vec<u8> = vec![
|
||||||
|
0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
|
||||||
|
];
|
||||||
|
let src_pattern = "EE FF 00 11 22";
|
||||||
|
let pattern = BytesPattern::from(src_pattern);
|
||||||
|
let offset = src_data.as_slice().find_pattern_first(&pattern);
|
||||||
|
assert_eq!(offset, 4);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn find_pattern_first_loop() {
|
||||||
|
let src_data: Vec<u8> = vec![
|
||||||
|
0xAA, 0xBB, 0xCC, 0xAA, 0xBB, 0xEE, 0xAA, 0xBB, 0xCC, 0xAA, 0xBB, 0xCC, 0xAA, 0xBB,
|
||||||
|
0xDD, 0xAA, 0xBB, 0xCC,
|
||||||
|
];
|
||||||
|
let src_pattern = "AA BB CC AA BB DD";
|
||||||
|
let pattern = BytesPattern::from(src_pattern);
|
||||||
|
let offset = src_data.as_slice().find_pattern_first(&pattern);
|
||||||
|
assert_eq!(offset, 9);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn prefix_function() {
|
||||||
|
let src_pattern = "AA BB BB AA AA BB BB AA BB";
|
||||||
|
let expected_pi: Vec<usize> = vec![0, 0, 0, 1, 1, 2, 3, 4, 2];
|
||||||
|
let pattern = BytesPattern::from(src_pattern);
|
||||||
|
assert_eq!(pattern.pi, expected_pi);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn apply_patch() {
|
||||||
|
let src_data: Vec<u8> = vec![
|
||||||
|
0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
|
||||||
|
];
|
||||||
|
let expected_data: Vec<u8> = vec![
|
||||||
|
0xAA, 0xBB, 0xCC, 0xDD, 0x99, 0x88, 0x77, 0x66, 0x55, 0x33, 0x44, 0x55, 0x66,
|
||||||
|
];
|
||||||
|
let src_pattern = "EE FF 00 11 22";
|
||||||
|
let replacement = "99 88 77 66 55";
|
||||||
|
let pattern = BytesPattern::from(src_pattern);
|
||||||
|
let offset = src_data.as_slice().find_pattern_first(&pattern);
|
||||||
|
|
||||||
|
let replacement = replacement.replace(" ", "");
|
||||||
|
let replacement = HEXUPPER
|
||||||
|
.decode(replacement.as_bytes())
|
||||||
|
.expect("decode replacement");
|
||||||
|
let mut dst_data = src_data.clone();
|
||||||
|
dst_data.as_mut_slice().apply_patch(offset, &replacement);
|
||||||
|
assert_eq!(dst_data, expected_data);
|
||||||
|
}
|
||||||
|
}
|
17
macro/Cargo.toml
Normal file
17
macro/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "binnpatch_macro"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["b4tman <b4tm4n@mail.ru>"]
|
||||||
|
readme = "../README.md"
|
||||||
|
repository = "https://gitea.b4tman.ru/b4tman/binnpatch"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
91
macro/src/lib.rs
Normal file
91
macro/src/lib.rs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
extern crate proc_macro;
|
||||||
|
use proc_macro::{TokenStream, TokenTree};
|
||||||
|
|
||||||
|
use proc_macro::{Punct, Literal, Spacing, Span, Ident, Group, Delimiter};
|
||||||
|
|
||||||
|
fn parse_str(s: &str) -> TokenStream {
|
||||||
|
s.parse().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn binnvec(_item: TokenStream) -> TokenStream {
|
||||||
|
println!("- - - - -");
|
||||||
|
println!("{}", _item.to_string());
|
||||||
|
println!("- - - - -");
|
||||||
|
println!("{:?}", _item);
|
||||||
|
println!("- - - - -");
|
||||||
|
|
||||||
|
let mut new_tokens: Vec<TokenTree> = vec![];
|
||||||
|
let comma: Punct = Punct::new(',', Spacing::Alone);
|
||||||
|
let zero = Literal::u8_suffixed(0);
|
||||||
|
let mut prev_separated: bool = true;
|
||||||
|
let mut prev_is_mask: bool = false;
|
||||||
|
|
||||||
|
for token in _item {
|
||||||
|
match token {
|
||||||
|
TokenTree::Punct(ref x) if x.as_char() == ',' => {
|
||||||
|
new_tokens.push(token.into());
|
||||||
|
prev_separated = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
TokenTree::Punct(ref x) if x.as_char() == '?' => {
|
||||||
|
if prev_is_mask {
|
||||||
|
if !prev_separated {
|
||||||
|
new_tokens.push(comma.clone().into());
|
||||||
|
}
|
||||||
|
let mut val = zero.clone();
|
||||||
|
val.set_span(x.span());
|
||||||
|
new_tokens.push(zero.clone().into());
|
||||||
|
prev_is_mask = false;
|
||||||
|
prev_separated = false;
|
||||||
|
} else if !prev_is_mask {
|
||||||
|
prev_is_mask = true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
TokenTree::Ident(ref x) if x.to_string().len() == 2 => {
|
||||||
|
if !prev_separated {
|
||||||
|
new_tokens.push(comma.clone().into());
|
||||||
|
}
|
||||||
|
let lit_str = format!("0x{}u8", x.to_string());
|
||||||
|
let mut val:Literal = lit_str.as_str().parse().expect("parse literal from ident");
|
||||||
|
val.set_span(x.span());
|
||||||
|
new_tokens.push(val.into());
|
||||||
|
}
|
||||||
|
TokenTree::Literal(ref x) => {
|
||||||
|
if !prev_separated {
|
||||||
|
new_tokens.push(comma.clone().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let in_str = x.to_string();
|
||||||
|
if in_str.starts_with("0x") {
|
||||||
|
new_tokens.push(token.into());
|
||||||
|
} else {
|
||||||
|
let lit_str = format!("0x{}u8", in_str);
|
||||||
|
let mut val:Literal = lit_str.as_str().parse().expect("parse literal");
|
||||||
|
val.set_span(x.span());
|
||||||
|
new_tokens.push(val.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev_separated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ts_new = TokenStream::new();
|
||||||
|
ts_new.extend(new_tokens);
|
||||||
|
|
||||||
|
let group = Group::new(Delimiter::Bracket, ts_new);
|
||||||
|
let mut result = TokenStream::new();
|
||||||
|
let vec_ident = Ident::new("vec", Span::call_site());
|
||||||
|
let macro_marc = Punct::new('!', Spacing::Joint);
|
||||||
|
result.extend([TokenTree::from(vec_ident), TokenTree::from(macro_marc)]);
|
||||||
|
result.extend([TokenTree::from(group)]);
|
||||||
|
|
||||||
|
println!("result: \"{}\"", result.to_string());
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user