Skip to content

Commit bb183be

Browse files
committed
feat: adding support for pelite
1 parent cc5e328 commit bb183be

File tree

6 files changed

+341
-0
lines changed

6 files changed

+341
-0
lines changed

Cargo.lock

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bmatcher-core/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ bench = false
1111

1212
[dependencies]
1313
thiserror-no-std = "2.0.2"
14+
pelite = { version = ">=0.8", optional = true, default-features = false }
1415

1516
[dev-dependencies]
1617
criterion = { version = "0.5", features = ["html_reports"] }
@@ -19,3 +20,6 @@ rand = "0.8.5"
1920
[[bench]]
2021
name = "matcher"
2122
harness = false
23+
24+
[features]
25+
pelite = ["dep:pelite"]
91.3 KB
Binary file not shown.

bmatcher-core/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ pub use atom::{
1313
mod target;
1414
pub use target::MatchTarget;
1515

16+
#[cfg(feature = "pelite")]
17+
mod target_pelite;
18+
1619
mod matcher;
1720
pub use matcher::{
1821
execute,

bmatcher-core/src/target_pelite.rs

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
use pelite::{
2+
image::IMAGE_SECTION_HEADER,
3+
pe32::{
4+
self,
5+
Pe as Pe32,
6+
},
7+
pe64::{
8+
self,
9+
headers::SectionHeader,
10+
Pe as Pe64,
11+
},
12+
PeFile,
13+
PeView,
14+
Wrap,
15+
};
16+
17+
use crate::{
18+
MatchHint,
19+
MatchTarget,
20+
};
21+
22+
trait AbstractPe {
23+
fn ape_size_of_image(&self) -> usize;
24+
fn ape_section_headers(&self) -> &[SectionHeader];
25+
fn ape_section_bytes(&self, section_header: &IMAGE_SECTION_HEADER) -> pelite::Result<&[u8]>;
26+
fn ape_slice(&self, rva: u32, min_size: usize, align: usize) -> pelite::Result<&[u8]>;
27+
}
28+
29+
impl AbstractPe for PeView<'_> {
30+
fn ape_size_of_image(&self) -> usize {
31+
match self.optional_header() {
32+
Wrap::T32(inner) => inner.SizeOfImage as usize,
33+
Wrap::T64(inner) => inner.SizeOfImage as usize,
34+
}
35+
}
36+
37+
fn ape_section_headers(&self) -> &[SectionHeader] {
38+
self.section_headers().as_slice()
39+
}
40+
41+
fn ape_section_bytes(&self, section_header: &IMAGE_SECTION_HEADER) -> pelite::Result<&[u8]> {
42+
self.get_section_bytes(section_header)
43+
}
44+
45+
fn ape_slice(&self, rva: u32, min_size: usize, align: usize) -> pelite::Result<&[u8]> {
46+
self.slice(rva, min_size, align)
47+
}
48+
}
49+
50+
impl AbstractPe for PeFile<'_> {
51+
fn ape_size_of_image(&self) -> usize {
52+
match self.optional_header() {
53+
Wrap::T32(inner) => inner.SizeOfImage as usize,
54+
Wrap::T64(inner) => inner.SizeOfImage as usize,
55+
}
56+
}
57+
58+
fn ape_section_headers(&self) -> &[SectionHeader] {
59+
self.section_headers().as_slice()
60+
}
61+
62+
fn ape_section_bytes(&self, section_header: &IMAGE_SECTION_HEADER) -> pelite::Result<&[u8]> {
63+
self.get_section_bytes(section_header)
64+
}
65+
66+
fn ape_slice(&self, rva: u32, min_size: usize, align: usize) -> pelite::Result<&[u8]> {
67+
self.slice(rva, min_size, align)
68+
}
69+
}
70+
71+
impl AbstractPe for pe32::PeView<'_> {
72+
fn ape_size_of_image(&self) -> usize {
73+
self.optional_header().SizeOfImage as usize
74+
}
75+
76+
fn ape_section_headers(&self) -> &[SectionHeader] {
77+
self.section_headers().as_slice()
78+
}
79+
80+
fn ape_section_bytes(&self, section_header: &IMAGE_SECTION_HEADER) -> pelite::Result<&[u8]> {
81+
self.get_section_bytes(section_header)
82+
}
83+
84+
fn ape_slice(&self, rva: u32, min_size: usize, align: usize) -> pelite::Result<&[u8]> {
85+
self.slice(rva, min_size, align)
86+
}
87+
}
88+
89+
impl AbstractPe for pe32::PeFile<'_> {
90+
fn ape_size_of_image(&self) -> usize {
91+
self.optional_header().SizeOfImage as usize
92+
}
93+
94+
fn ape_section_headers(&self) -> &[SectionHeader] {
95+
self.section_headers().as_slice()
96+
}
97+
98+
fn ape_section_bytes(&self, section_header: &IMAGE_SECTION_HEADER) -> pelite::Result<&[u8]> {
99+
self.get_section_bytes(section_header)
100+
}
101+
102+
fn ape_slice(&self, rva: u32, min_size: usize, align: usize) -> pelite::Result<&[u8]> {
103+
self.slice(rva, min_size, align)
104+
}
105+
}
106+
107+
impl AbstractPe for &dyn pe32::PeObject<'_> {
108+
fn ape_size_of_image(&self) -> usize {
109+
self.optional_header().SizeOfImage as usize
110+
}
111+
112+
fn ape_section_headers(&self) -> &[SectionHeader] {
113+
self.section_headers().as_slice()
114+
}
115+
116+
fn ape_section_bytes(&self, section_header: &IMAGE_SECTION_HEADER) -> pelite::Result<&[u8]> {
117+
self.get_section_bytes(section_header)
118+
}
119+
120+
fn ape_slice(&self, rva: u32, min_size: usize, align: usize) -> pelite::Result<&[u8]> {
121+
self.slice(rva, min_size, align)
122+
}
123+
}
124+
125+
impl AbstractPe for pe64::PeView<'_> {
126+
fn ape_size_of_image(&self) -> usize {
127+
self.optional_header().SizeOfImage as usize
128+
}
129+
130+
fn ape_section_headers(&self) -> &[SectionHeader] {
131+
self.section_headers().as_slice()
132+
}
133+
134+
fn ape_section_bytes(&self, section_header: &IMAGE_SECTION_HEADER) -> pelite::Result<&[u8]> {
135+
self.get_section_bytes(section_header)
136+
}
137+
138+
fn ape_slice(&self, rva: u32, min_size: usize, align: usize) -> pelite::Result<&[u8]> {
139+
self.slice(rva, min_size, align)
140+
}
141+
}
142+
143+
impl AbstractPe for pe64::PeFile<'_> {
144+
fn ape_size_of_image(&self) -> usize {
145+
self.optional_header().SizeOfImage as usize
146+
}
147+
148+
fn ape_section_headers(&self) -> &[SectionHeader] {
149+
self.section_headers().as_slice()
150+
}
151+
152+
fn ape_section_bytes(&self, section_header: &IMAGE_SECTION_HEADER) -> pelite::Result<&[u8]> {
153+
self.get_section_bytes(section_header)
154+
}
155+
156+
fn ape_slice(&self, rva: u32, min_size: usize, align: usize) -> pelite::Result<&[u8]> {
157+
self.slice(rva, min_size, align)
158+
}
159+
}
160+
161+
impl AbstractPe for &dyn pe64::PeObject<'_> {
162+
fn ape_size_of_image(&self) -> usize {
163+
self.optional_header().SizeOfImage as usize
164+
}
165+
166+
fn ape_section_headers(&self) -> &[SectionHeader] {
167+
self.section_headers().as_slice()
168+
}
169+
170+
fn ape_section_bytes(&self, section_header: &IMAGE_SECTION_HEADER) -> pelite::Result<&[u8]> {
171+
self.get_section_bytes(section_header)
172+
}
173+
174+
fn ape_slice(&self, rva: u32, min_size: usize, align: usize) -> pelite::Result<&[u8]> {
175+
self.slice(rva, min_size, align)
176+
}
177+
}
178+
179+
fn match_hint<P: AbstractPe>(target: &P, offset: usize, byte_sequence: &[u8]) -> MatchHint {
180+
let sections = target.ape_section_headers();
181+
let sections = sections
182+
.iter()
183+
.skip_while(|section| offset >= (section.VirtualAddress + section.VirtualSize) as usize);
184+
185+
for section in sections {
186+
let section_offset = if offset < section.VirtualAddress as usize {
187+
0
188+
} else {
189+
offset - section.VirtualAddress as usize
190+
};
191+
192+
let Ok(section_bytes) = target.ape_section_bytes(&section) else {
193+
continue;
194+
};
195+
196+
if section_offset >= section_bytes.len() {
197+
continue;
198+
}
199+
200+
match (&section_bytes[section_offset..]).match_hint(0, byte_sequence) {
201+
MatchHint::MaybeMatch(offset) => {
202+
return MatchHint::MaybeMatch(
203+
offset + section_offset + section.VirtualAddress as usize,
204+
)
205+
}
206+
MatchHint::NoMatches => continue,
207+
MatchHint::Unsupported => unreachable!(),
208+
}
209+
}
210+
211+
MatchHint::NoMatches
212+
}
213+
214+
fn subrange<P: AbstractPe>(target: &P, offset: usize, byte_count: usize) -> Option<&[u8]> {
215+
Some(&target.ape_slice(offset as u32, byte_count, 1).ok()?[..byte_count])
216+
}
217+
218+
impl<P: AbstractPe> MatchTarget for P {
219+
fn match_length(&self) -> usize {
220+
self.ape_size_of_image()
221+
}
222+
223+
fn match_hint(&self, offset: usize, byte_sequence: &[u8]) -> MatchHint {
224+
self::match_hint(self, offset, byte_sequence)
225+
}
226+
227+
fn subrange(&self, offset: usize, byte_count: usize) -> Option<&[u8]> {
228+
self::subrange(self, offset, byte_count)
229+
}
230+
231+
fn translate_absolute_address(&self, _address: u64) -> Option<usize> {
232+
/* not supported */
233+
None
234+
}
235+
}
236+
237+
#[cfg(test)]
238+
mod test {
239+
use pelite::pe64::PeFile;
240+
241+
use crate::{
242+
MatchHint,
243+
MatchTarget,
244+
};
245+
246+
#[test]
247+
fn pe_object_target() {
248+
let file_data = include_bytes!("../resources/hvloader.dll").to_vec();
249+
let target = PeFile::from_bytes(&file_data).unwrap();
250+
251+
const SEC_TEXT_HDR: &[u8] = &[
252+
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x48, 0x89, 0x5C, 0x24, 0x08, 0x48,
253+
0x89, 0x6C, 0x24, 0x10, 0x48, 0x89, 0x74, 0x24, 0x18, 0x57, 0x48, 0x83, 0xEC, 0x20,
254+
0x48, 0x8B, 0x1D, 0xBD,
255+
];
256+
257+
let match_hint = target.match_hint(0x00, SEC_TEXT_HDR);
258+
assert_eq!(match_hint, MatchHint::MaybeMatch(0x1000));
259+
260+
let match_hint = target.match_hint(0x8FE, SEC_TEXT_HDR);
261+
assert_eq!(match_hint, MatchHint::MaybeMatch(0x1000));
262+
263+
let match_hint = target.match_hint(0x1001, SEC_TEXT_HDR);
264+
assert_eq!(match_hint, MatchHint::NoMatches);
265+
266+
const SEC_DATA_SOMEWHERE: &[u8] = &[
267+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x6D, 0x78, 0x42, 0x6F,
268+
0x6F, 0x74, 0x49, 0x6E, 0x66, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
269+
0x00, 0x00, 0x00, 0x00, 0x80, 0xDA,
270+
];
271+
272+
let match_hint = target.match_hint(0x00, SEC_DATA_SOMEWHERE);
273+
assert_eq!(match_hint, MatchHint::MaybeMatch(0x12090));
274+
275+
let match_hint = target.match_hint(0x1000, SEC_DATA_SOMEWHERE);
276+
assert_eq!(match_hint, MatchHint::MaybeMatch(0x12090));
277+
278+
let match_hint = target.match_hint(0x1001, SEC_DATA_SOMEWHERE);
279+
assert_eq!(match_hint, MatchHint::MaybeMatch(0x12090));
280+
281+
let match_hint = target.match_hint(0x12090, SEC_DATA_SOMEWHERE);
282+
assert_eq!(match_hint, MatchHint::MaybeMatch(0x12090));
283+
284+
let match_hint = target.match_hint(0x12091, SEC_DATA_SOMEWHERE);
285+
assert_eq!(match_hint, MatchHint::NoMatches);
286+
287+
const SEC_PDATA_END: &[u8] = &[
288+
0xF0, 0xEB, 0x00, 0x00, 0xF7, 0xEB, 0x00, 0x00, 0x0C, 0x0E, 0x01, 0x00, 0x00, 0xEC,
289+
0x00, 0x00, 0x6A, 0xEC, 0x00, 0x00, 0x14, 0x0E, 0x01, 0x00, 0x70, 0xEC, 0x00, 0x00,
290+
0x74, 0xEC, 0x00, 0x00, 0x2C, 0x0E, 0x01, 0x00, 0x80, 0xEC, 0x00, 0x00, 0x84, 0xEC,
291+
0x00, 0x00, 0x30, 0x0E, 0x01, 0x00,
292+
];
293+
294+
let match_hint = target.match_hint(0x00, SEC_PDATA_END);
295+
assert_eq!(match_hint, MatchHint::MaybeMatch(0x56690));
296+
297+
let match_hint = target.match_hint(0x56691, SEC_PDATA_END);
298+
assert_eq!(match_hint, MatchHint::NoMatches);
299+
}
300+
}

bmatcher/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ readme = "../README.MD"
1111
[dependencies]
1212
bmatcher-core = { version = "=0.3.2", path = "../bmatcher-core" }
1313
bmatcher-proc = { version = "=0.3.2", path = "../bmatcher-proc" }
14+
15+
[features]
16+
default = []
17+
pelite = ["bmatcher-core/pelite"]

0 commit comments

Comments
 (0)