Skip to content

Commit 22e9031

Browse files
s1341tokatokamineo333domenukk
authored andcommitted
Windows frida support (AFLplusplus#1607)
* WIP: windows frida * frida-windows: fix hooks not present on windows * windows: allow building using cargo xwin * frida-windows: fmrt * frida-windows: cleanup and allow asan/drcov on windows * frida-windows: fmt * frida-windows: fix clippy * frida-windows: handle unknown exceptions gracefully * frida-windows: rework shadow mapping algo * frida-windows: add hook functions * frida-windows: hook functions; fix stack register * minibsod: enable for windows * check_shadow: fix edge casees * asan_rt: rework and add hooks for windows * inprocess: add minibsod on windows * Fix warnings * minibsod: disable test on windows * WIP: HookRuntime * Cleanup after merge * Bump frida-gum version * Fix conflict marker; update frida * Make winsafe windows-specific * Fmt * Format * Better detection of clang++ (using cc) * Make AsanErrors crate public so we can use it in tests * Add helper to get immediate of operand * Use HookRuntime to hook asan functions Tests now passing * fmt * Implement recurisve jmp resolve * Fix reversed logic * windows_hooks: Don't die if functions are already replaced * Allow utils to work on windows * Enable allocator hooking on windows * Warnings; add trace to free * Make ASAN tests run windows (with cargo xwin compilation) * Fmt * clang-format * clang-format * Add more tests * Fix partial range access bug in unpoisoning/shadow_check * Merge main * Fix check_shadow and implement unit tests * Fix hooking and PC retrieval * WIP: Working gdiplus fuzzing with frida-ASAN, no false positives * LibAFL Frida asan_rt and hook_rt fixes for frida_windows (AFLplusplus#2095) * Introduce aarch64 * MacOS fix - MemoryAreas is broken on MacOS and just loops * Introduce working aarch64 ASAN check * Implement large blob * Fix hook_rt for arm64 * Fix poison/unpoison * Fix shadow check * Update x86-64 * Fix aarch64 unused import * Remove extraneous println statement * merge main * Fixes * alloc: add tests, pass the tests * HookRuntime before AsanRuntime, and don't Asan if Hooked * hook_rt: Fixes * Frida windows check shadow fix (AFLplusplus#2159) * Fix check_shadow and add additional tests * add some additional documentation * Revert to Interceptor based hooks * fixes * format * Get rid of hook_rt; fixes * clang-format * clang-format * Fix with_threshold * fixes * fix build.rs * fmt * Fix offset to RDI on stack * Fix clippy * Fix build.rs * clippy * hook MapViewOfFile * fmt * fix * clippy * clippy * Missing brace * fix * Clippy * fomrrat * fix i64 cast * clippy exclude * too many lines * Undo merge fails * fmt * move debug print * Fix some frida things * Remove unused frida_to_cs fn for aarch64 * name * Don't touch libafl_qemu --------- Co-authored-by: Dongjia "toka" Zhang <[email protected]> Co-authored-by: Sharad Khanna <[email protected]> Co-authored-by: Dominik Maier <[email protected]> Co-authored-by: Dominik Maier <[email protected]>
1 parent fa6ae40 commit 22e9031

File tree

25 files changed

+2281
-1211
lines changed

25 files changed

+2281
-1211
lines changed

fuzzers/frida_gdiplus/Cargo.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@ tar = "0.4.37"
2424
reqwest = { version = "0.11.4", features = ["blocking"] }
2525

2626
[dependencies]
27-
libafl = { path = "../../libafl/", features = [ "std", "llmp_compression", "llmp_bind_public", "frida_cli" ] } #, "llmp_small_maps", "llmp_debug"]}
27+
libafl = { path = "../../libafl/", features = [ "std", "llmp_compression",
28+
"llmp_bind_public", "frida_cli", "errors_backtrace" ] } #, "llmp_small_maps", "llmp_debug"]}
2829
libafl_bolts = { path = "../../libafl_bolts/" }
2930
frida-gum = { version = "0.13.6", features = [ "auto-download", "event-sink", "invocation-listener"] }
3031
libafl_frida = { path = "../../libafl_frida", features = ["cmplog"] }
3132
libafl_targets = { path = "../../libafl_targets", features = ["sancov_cmplog"] }
3233
libloading = "0.7"
3334
mimalloc = { version = "*", default-features = false }
35+
dlmalloc ={version = "0.2.6", features = ["global"]}
3436
color-backtrace = "0.5"
37+
env_logger = "0.10.0"
3538
iced-x86 = { version = "1.20.0", features = ["code_asm"] }

fuzzers/frida_gdiplus/cargo/.config

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[build]
2+
target = "x86_64-pc-windows-msvc"

fuzzers/frida_gdiplus/harness.cc

+10-5
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,13 @@ ULONG_PTR gdiplusToken;
2121
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
2222
switch (fdwReason) {
2323
case DLL_PROCESS_ATTACH:
24+
LoadLibraryA("ole32.dll");
2425
LoadLibraryA("gdi32full.dll");
2526
LoadLibraryA("WindowsCodecs.dll");
27+
LoadLibraryA("shcore.dll");
28+
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
29+
LoadLibraryA("gdi32.dll");
30+
// DebugBreak();
2631
break;
2732
}
2833
return TRUE;
@@ -31,16 +36,16 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
3136
extern "C" __declspec(dllexport) int LLVMFuzzerTestOneInput(const uint8_t *data,
3237
size_t size) {
3338
static DWORD init = 0;
34-
if (!init) {
35-
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
36-
init = 1;
37-
}
39+
// if (!init) {
40+
// init = 1;
41+
// }
3842

3943
HGLOBAL m_hBuffer = ::GlobalAlloc(GMEM_MOVEABLE, size);
4044
if (m_hBuffer) {
4145
void *pBuffer = ::GlobalLock(m_hBuffer);
4246
if (pBuffer) {
43-
CopyMemory(pBuffer, data, size);
47+
memcpy(pBuffer, data, size);
48+
// CopyMemory(pBuffer, data, size);
4449

4550
IStream *pStream = NULL;
4651
if (::CreateStreamOnHGlobal(m_hBuffer, FALSE, &pStream) == S_OK) {

fuzzers/frida_gdiplus/src/fuzzer.rs

+25-41
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,16 @@
66
//! going to make it compilable only for Windows, don't forget to modify the
77
//! `scripts/test_fuzzer.sh` to opt-out this fuzzer from that test.
88
9+
#[cfg(unix)]
910
use mimalloc::MiMalloc;
11+
#[cfg(unix)]
1012
#[global_allocator]
1113
static GLOBAL: MiMalloc = MiMalloc;
14+
#[cfg(windows)]
15+
use dlmalloc::GlobalDlmalloc;
16+
#[cfg(windows)]
17+
#[global_allocator]
18+
static GLOBAL: GlobalDlmalloc = GlobalDlmalloc;
1219

1320
use std::path::PathBuf;
1421

@@ -17,8 +24,8 @@ use libafl::{
1724
corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus},
1825
events::{launcher::Launcher, llmp::LlmpRestartingEventManager, EventConfig},
1926
executors::{inprocess::InProcessExecutor, ExitKind, ShadowExecutor},
20-
feedback_or, feedback_or_fast,
21-
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
27+
feedback_and_fast, feedback_or, feedback_or_fast,
28+
feedbacks::{ConstFeedback, CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
2229
fuzzer::{Fuzzer, StdFuzzer},
2330
inputs::{BytesInput, HasTargetBytes},
2431
monitors::MultiMonitor,
@@ -32,20 +39,18 @@ use libafl::{
3239
state::{HasCorpus, StdState},
3340
Error, HasMetadata,
3441
};
35-
#[cfg(unix)]
36-
use libafl::{feedback_and_fast, feedbacks::ConstFeedback};
3742
use libafl_bolts::{
3843
cli::{parse_args, FuzzerOptions},
3944
rands::StdRand,
4045
shmem::{ShMemProvider, StdShMemProvider},
4146
tuples::{tuple_list, Merge},
4247
AsSlice,
4348
};
44-
#[cfg(unix)]
45-
use libafl_frida::asan::asan_rt::AsanRuntime;
46-
#[cfg(unix)]
47-
use libafl_frida::asan::errors::{AsanErrorsFeedback, AsanErrorsObserver};
4849
use libafl_frida::{
50+
asan::{
51+
asan_rt::AsanRuntime,
52+
errors::{AsanErrorsFeedback, AsanErrorsObserver},
53+
},
4954
cmplog_rt::CmpLogRuntime,
5055
coverage_rt::{CoverageRuntime, MAP_SIZE},
5156
executor::FridaInProcessExecutor,
@@ -55,6 +60,7 @@ use libafl_targets::cmplog::CmpLogObserver;
5560

5661
/// The main fn, usually parsing parameters, and starting the fuzzer
5762
pub fn main() {
63+
env_logger::init();
5864
color_backtrace::install();
5965

6066
let options = parse_args();
@@ -97,16 +103,11 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
97103
let gum = Gum::obtain();
98104

99105
let coverage = CoverageRuntime::new();
100-
#[cfg(unix)]
101-
let asan = AsanRuntime::new(options);
106+
let asan = AsanRuntime::new(&options);
102107

103-
#[cfg(unix)]
104108
let mut frida_helper =
105109
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage, asan));
106-
#[cfg(windows)]
107-
let mut frida_helper =
108-
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage));
109-
110+
//
110111
// Create an observation channel using the coverage map
111112
let edges_observer = HitcountsMapObserver::new(StdMapObserver::from_mut_ptr(
112113
"edges",
@@ -118,7 +119,6 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
118119
// Create an observation channel to keep track of the execution time
119120
let time_observer = TimeObserver::new("time");
120121

121-
#[cfg(unix)]
122122
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
123123

124124
// Feedback to rate the interestingness of an input
@@ -131,18 +131,15 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
131131
);
132132

133133
// Feedbacks to recognize an input as solution
134-
#[cfg(unix)]
135134
let mut objective = feedback_or_fast!(
136135
CrashFeedback::new(),
137-
TimeoutFeedback::new(),
136+
// TimeoutFeedback::new(),
138137
// true enables the AsanErrorFeedback
139138
feedback_and_fast!(
140139
ConstFeedback::from(true),
141140
AsanErrorsFeedback::new(&asan_observer)
142141
)
143142
);
144-
#[cfg(windows)]
145-
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
146143

147144
// If not restarting, create a State from scratch
148145
let mut state = state.unwrap_or_else(|| {
@@ -183,20 +180,18 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
183180
// A fuzzer with feedbacks and a corpus scheduler
184181
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
185182

186-
#[cfg(unix)]
187-
let observers = tuple_list!(edges_observer, time_observer, asan_observer);
188-
#[cfg(windows)]
189-
let observers = tuple_list!(edges_observer, time_observer);
183+
let observers = tuple_list!(edges_observer, time_observer, asan_observer,);
190184

191185
// Create the executor for an in-process function with just one observer for edge coverage
192186
let mut executor = FridaInProcessExecutor::new(
193187
&gum,
194-
InProcessExecutor::new(
188+
InProcessExecutor::with_timeout(
195189
&mut frida_harness,
196190
observers,
197191
&mut fuzzer,
198192
&mut state,
199193
&mut mgr,
194+
options.timeout,
200195
)?,
201196
&mut frida_helper,
202197
);
@@ -237,7 +232,6 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
237232

238233
// Create an observation channel to keep track of the execution time
239234
let time_observer = TimeObserver::new("time");
240-
#[cfg(unix)]
241235
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
242236

243237
// Feedback to rate the interestingness of an input
@@ -249,7 +243,6 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
249243
TimeFeedback::new(&time_observer)
250244
);
251245

252-
#[cfg(unix)]
253246
let mut objective = feedback_or_fast!(
254247
CrashFeedback::new(),
255248
TimeoutFeedback::new(),
@@ -258,8 +251,6 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
258251
AsanErrorsFeedback::new(&asan_observer)
259252
)
260253
);
261-
#[cfg(windows)]
262-
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
263254

264255
// If not restarting, create a State from scratch
265256
let mut state = state.unwrap_or_else(|| {
@@ -301,10 +292,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
301292
// A fuzzer with feedbacks and a corpus scheduler
302293
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
303294

304-
#[cfg(unix)]
305295
let observers = tuple_list!(edges_observer, time_observer, asan_observer);
306-
#[cfg(windows)]
307-
let observers = tuple_list!(edges_observer, time_observer,);
308296

309297
// Create the executor for an in-process function with just one observer for edge coverage
310298
let mut executor = FridaInProcessExecutor::new(
@@ -372,7 +360,6 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
372360
// Create an observation channel to keep track of the execution time
373361
let time_observer = TimeObserver::new("time");
374362

375-
#[cfg(unix)]
376363
let asan_observer = AsanErrorsObserver::from_static_asan_errors();
377364

378365
// Feedback to rate the interestingness of an input
@@ -384,7 +371,6 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
384371
TimeFeedback::new(&time_observer)
385372
);
386373

387-
#[cfg(unix)]
388374
let mut objective = feedback_or_fast!(
389375
CrashFeedback::new(),
390376
TimeoutFeedback::new(),
@@ -393,8 +379,6 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
393379
AsanErrorsFeedback::new(&asan_observer)
394380
)
395381
);
396-
#[cfg(windows)]
397-
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
398382

399383
// If not restarting, create a State from scratch
400384
let mut state = state.unwrap_or_else(|| {
@@ -436,20 +420,18 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
436420
// A fuzzer with feedbacks and a corpus scheduler
437421
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
438422

439-
#[cfg(unix)]
440423
let observers = tuple_list!(edges_observer, time_observer, asan_observer);
441-
#[cfg(windows)]
442-
let observers = tuple_list!(edges_observer, time_observer);
443424

444425
// Create the executor for an in-process function with just one observer for edge coverage
445426
let mut executor = FridaInProcessExecutor::new(
446427
&gum,
447-
InProcessExecutor::new(
428+
InProcessExecutor::with_timeout(
448429
&mut frida_harness,
449430
observers,
450431
&mut fuzzer,
451432
&mut state,
452433
&mut mgr,
434+
options.timeout,
453435
)?,
454436
&mut frida_helper,
455437
);
@@ -466,7 +448,9 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
466448

467449
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
468450

469-
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
451+
fuzzer
452+
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
453+
.unwrap();
470454

471455
Ok(())
472456
})(state, mgr, core_id)

fuzzers/frida_libpng/Cargo.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@ reqwest = { version = "0.11.4", features = ["blocking"] }
2626

2727

2828
[dependencies]
29-
libafl = { path = "../../libafl/", features = [ "std", "llmp_compression", "llmp_bind_public", "frida_cli", "errors_backtrace" ] } #, "llmp_small_maps", "llmp_debug"]}
29+
libafl = { path = "../../libafl/", features = [ "std", "llmp_compression",
30+
"llmp_bind_public", "frida_cli", "errors_backtrace" ] } #, "llmp_small_maps", "llmp_debug"]}
3031
libafl_bolts = { path = "../../libafl_bolts/" }
3132
frida-gum = { version = "0.13.6", features = [ "auto-download", "event-sink", "invocation-listener"] }
3233
libafl_frida = { path = "../../libafl_frida", features = ["cmplog"] }
3334
libafl_targets = { path = "../../libafl_targets", features = ["sancov_cmplog"] }
3435
libloading = "0.7"
3536
mimalloc = { version = "*", default-features = false }
3637
color-backtrace = "0.5"
38+
log = "0.4.20"
39+
env_logger = "0.10.0"

fuzzers/frida_libpng/harness.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ static char *allocation = NULL;
8888
__attribute__((noinline)) void func3(char *alloc) {
8989
// printf("func3\n");
9090
#ifdef _WIN32
91-
if (rand() == 0) {
91+
if ((rand() % 2) == 0) {
9292
alloc[0x1ff] = 0xde;
9393
printf("alloc[0x200]: %d\n", alloc[0x200]);
9494
}

fuzzers/frida_libpng/src/fuzzer.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ static GLOBAL: MiMalloc = MiMalloc;
5050

5151
/// The main fn, usually parsing parameters, and starting the fuzzer
5252
pub fn main() {
53+
env_logger::init();
5354
color_backtrace::install();
54-
5555
let options = parse_args();
5656

5757
unsafe {
@@ -65,6 +65,8 @@ pub fn main() {
6565
/// The actual fuzzer
6666
#[allow(clippy::too_many_lines, clippy::too_many_arguments)]
6767
unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
68+
log::info!("Frida fuzzer starting up.");
69+
6870
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
6971
let monitor = MultiMonitor::new(|s| println!("{s}"));
7072

@@ -97,7 +99,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
9799

98100
#[cfg(unix)]
99101
let mut frida_helper =
100-
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage, asan));
102+
FridaInstrumentationHelper::new(&gum, options, tuple_list!(asan, coverage));
101103
#[cfg(windows)]
102104
let mut frida_helper =
103105
FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage));

libafl/src/executors/hooks/windows.rs

+14-2
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ pub mod windows_exception_handler {
113113
sync::atomic::{compiler_fence, Ordering},
114114
};
115115
#[cfg(feature = "std")]
116+
use std::io::Write;
117+
#[cfg(feature = "std")]
116118
use std::panic;
117119

118120
use libafl_bolts::os::windows_exceptions::{
@@ -131,7 +133,7 @@ pub mod windows_exception_handler {
131133
},
132134
feedbacks::Feedback,
133135
fuzzer::HasObjective,
134-
inputs::UsesInput,
136+
inputs::{Input, UsesInput},
135137
state::{HasCorpus, HasExecutions, HasSolutions, State},
136138
};
137139

@@ -394,7 +396,17 @@ pub mod windows_exception_handler {
394396
// Make sure we don't crash in the crash handler forever.
395397
if is_crash {
396398
let input = data.take_current_input::<<E::State as UsesInput>::Input>();
397-
399+
{
400+
let mut bsod = Vec::new();
401+
{
402+
let mut writer = std::io::BufWriter::new(&mut bsod);
403+
writeln!(writer, "input: {:?}", input.generate_name(0)).unwrap();
404+
libafl_bolts::minibsod::generate_minibsod(&mut writer, exception_pointers)
405+
.unwrap();
406+
writer.flush().unwrap();
407+
}
408+
log::error!("{}", std::str::from_utf8(&bsod).unwrap());
409+
}
398410
run_observers_and_save_state::<E, EM, OF, Z>(
399411
executor,
400412
state,

libafl_bolts/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ pub mod fs;
122122
#[cfg(feature = "alloc")]
123123
pub mod llmp;
124124
pub mod math;
125-
#[cfg(all(feature = "std", unix))]
125+
#[cfg(feature = "std")]
126126
pub mod minibsod;
127127
pub mod os;
128128
#[cfg(feature = "alloc")]

0 commit comments

Comments
 (0)