From ed85ce03a6998ec67142b512022338312a3b86f5 Mon Sep 17 00:00:00 2001 From: parikhakshat <68412398+parikhakshat@users.noreply.github.com> Date: Fri, 19 Aug 2022 13:13:52 -0700 Subject: [PATCH 1/3] Add initial support for fuzzing attached processes --- fuzzer.cpp | 30 ++++++++-- fuzzer.h | 4 ++ instrumentation.h | 6 ++ tinyinstinstrumentation.cpp | 112 +++++++++++++++++++++++++++++++++++- tinyinstinstrumentation.h | 4 ++ 5 files changed, 150 insertions(+), 6 deletions(-) diff --git a/fuzzer.cpp b/fuzzer.cpp index 26e2ad7..fa2c72e 100644 --- a/fuzzer.cpp +++ b/fuzzer.cpp @@ -113,6 +113,16 @@ void Fuzzer::ParseOptions(int argc, char **argv) { incremental_coverage = GetBinaryOption("-incremental_coverage", argc, argv, true); add_all_inputs = GetBinaryOption("-add_all_inputs", argc, argv, false); + + process_name = GetOption("-process_name", argc, argv); + + attach_mode = false; + + if (process_name != NULL) { + attach_mode = true; + //set threads to one as multiple threads attaching to one process does not work well + num_threads = 1; + } } void Fuzzer::SetupDirectories() { @@ -245,8 +255,14 @@ RunResult Fuzzer::RunSampleAndGetCoverage(ThreadContext *tc, Sample *sample, Cov FATAL("Repeatedly failed to deliver sample"); } } + RunResult result; + if (attach_mode) { + script = ArgvToCmd(tc->target_argc, tc->target_argv); + result = tc->instrumentation->Attach(script, process_name, init_timeout, timeout); + } else { + result = tc->instrumentation->Run(tc->target_argc, tc->target_argv, init_timeout, timeout); + } - RunResult result = tc->instrumentation->Run(tc->target_argc, tc->target_argv, init_timeout, timeout); tc->instrumentation->GetCoverage(*coverage, true); // save crashes and hangs immediately when they are detected @@ -325,8 +341,12 @@ RunResult Fuzzer::TryReproduceCrash(ThreadContext* tc, Sample* sample, uint32_t FATAL("Repeatedly failed to deliver sample"); } } - - result = tc->instrumentation->RunWithCrashAnalysis(tc->target_argc, tc->target_argv, init_timeout, timeout); + if (attach_mode) { + script = ArgvToCmd(tc->target_argc, tc->target_argv); + result = tc->instrumentation->AttachWithCrashAnalysis(script, process_name, init_timeout, timeout); + } else { + result = tc->instrumentation->RunWithCrashAnalysis(tc->target_argc, tc->target_argv, init_timeout, timeout); + } tc->instrumentation->ClearCoverage(); if (result == CRASH) return result; @@ -409,8 +429,8 @@ RunResult Fuzzer::RunSample(ThreadContext *tc, Sample *sample, int *has_new_cove if(new_thread_coverage.empty()) return result; } - // printf("found new coverage: \n"); - // PrintCoverage(initialCoverage); + //printf("found new coverage: \n"); + //PrintCoverage(initialCoverage); // the sample returned new coverage diff --git a/fuzzer.h b/fuzzer.h index 675800c..c331753 100644 --- a/fuzzer.h +++ b/fuzzer.h @@ -196,6 +196,10 @@ class Fuzzer { uint64_t num_samples_discarded; uint64_t num_threads; uint64_t total_execs; + + bool attach_mode; + char * script; + char * process_name; void SaveState(ThreadContext *tc); void RestoreState(ThreadContext *tc); diff --git a/instrumentation.h b/instrumentation.h index a8e7a42..adefb58 100644 --- a/instrumentation.h +++ b/instrumentation.h @@ -32,6 +32,12 @@ class Instrumentation { return Run(argc, argv, init_timeout, timeout); } + virtual RunResult Attach(char * script, char * process_name, uint32_t init_timeout, uint32_t timeout) = 0; + + virtual RunResult AttachWithCrashAnalysis(char * script, char * process_name, uint32_t init_timeout, uint32_t timeout) { + return Attach(script, process_name, init_timeout, timeout); + } + virtual void CleanTarget() = 0; virtual bool HasNewCoverage() = 0; diff --git a/tinyinstinstrumentation.cpp b/tinyinstinstrumentation.cpp index ee9ea5e..7d9bb77 100644 --- a/tinyinstinstrumentation.cpp +++ b/tinyinstinstrumentation.cpp @@ -22,7 +22,6 @@ limitations under the License. #include - void TinyInstInstrumentation::Init(int argc, char **argv) { instrumentation = new LiteCov(); instrumentation->Init(argc, argv); @@ -136,6 +135,117 @@ RunResult TinyInstInstrumentation::RunWithCrashAnalysis(int argc, char** argv, u return ret; } +RunResult TinyInstInstrumentation::Attach(char * script, char * process_name, uint32_t init_timeout, uint32_t timeout) { + DebuggerStatus status; + RunResult ret = OTHER_ERROR; + + if (instrumentation->IsTargetFunctionDefined()) { + if (cur_iteration == num_iterations) { + instrumentation->Kill(); + cur_iteration = 0; + } + } + + // else clear only when the target function is reached + if (!instrumentation->IsTargetFunctionDefined()) { + instrumentation->ClearCoverage(); + } + + uint32_t timeout1 = timeout; + if (instrumentation->IsTargetFunctionDefined()) { + timeout1 = init_timeout; + } + + if (instrumentation->IsTargetAlive() && persist) { + status = instrumentation->Continue(timeout1); + } else { + instrumentation->Kill(); + cur_iteration = 0; + instrumentation->script = script; + Sleep(init_timeout); + processID = FindProcessId(process_name); + status = instrumentation->Attach(processID, timeout1); + } + + // if target function is defined, + // we should wait until it is hit + if (instrumentation->IsTargetFunctionDefined()) { + if (status != DEBUGGER_TARGET_START) { + // try again with a clean process + WARN("Target function not reached, retrying with a clean process\n"); + instrumentation->Kill(); + cur_iteration = 0; + instrumentation->script = script; + Sleep(init_timeout); + processID = FindProcessId(process_name); + status = instrumentation->Attach(processID, timeout1); + } + + if (status != DEBUGGER_TARGET_START) { + switch (status) { + case DEBUGGER_CRASHED: + FATAL("Process crashed before reaching the target method\n"); + break; + case DEBUGGER_HANGED: + FATAL("Process hanged before reaching the target method\n"); + break; + case DEBUGGER_PROCESS_EXIT: + FATAL("Process exited before reaching the target method\n"); + break; + default: + FATAL("An unknown problem occured before reaching the target method\n"); + break; + } + } + + instrumentation->ClearCoverage(); + + status = instrumentation->Continue(timeout); + } + + switch (status) { + case DEBUGGER_CRASHED: + ret = CRASH; + instrumentation->Kill(); + break; + case DEBUGGER_HANGED: + ret = HANG; + instrumentation->Kill(); + break; + case DEBUGGER_PROCESS_EXIT: + ret = OK; + if (instrumentation->IsTargetFunctionDefined()) { + WARN("Process exit during target function\n"); + ret = HANG; + } + break; + case DEBUGGER_TARGET_END: + if (instrumentation->IsTargetFunctionDefined()) { + ret = OK; + cur_iteration++; + } else { + FATAL("Unexpected status received from the debugger\n"); + } + break; + default: + FATAL("Unexpected status received from the debugger\n"); + break; + } + + return ret; +} + +RunResult TinyInstInstrumentation::AttachWithCrashAnalysis(char * script, char * process_name, uint32_t init_timeout, uint32_t timeout) { + // clean process when reproducing crashes + instrumentation->Kill(); + // disable instrumentation when reproducing crashes + instrumentation->DisableInstrumentation(); + RunResult ret = Attach(script, process_name, init_timeout, timeout); + instrumentation->Kill(); + instrumentation->EnableInstrumentation(); + return ret; +} + void TinyInstInstrumentation::CleanTarget() { instrumentation->Kill(); } diff --git a/tinyinstinstrumentation.h b/tinyinstinstrumentation.h index f8826a1..43bd337 100644 --- a/tinyinstinstrumentation.h +++ b/tinyinstinstrumentation.h @@ -33,6 +33,9 @@ class TinyInstInstrumentation : public Instrumentation { RunResult Run(int argc, char** argv, uint32_t init_timeout, uint32_t timeout) override; RunResult RunWithCrashAnalysis(int argc, char** argv, uint32_t init_timeout, uint32_t timeout) override; + RunResult TinyInstInstrumentation::Attach(char * script, char * process_name, uint32_t init_timeout, uint32_t timeout) override; + RunResult TinyInstInstrumentation::AttachWithCrashAnalysis(char * script, char * process_name, uint32_t init_timeout, uint32_t timeout) override; + void CleanTarget() override; bool HasNewCoverage() override; @@ -47,6 +50,7 @@ class TinyInstInstrumentation : public Instrumentation { protected: LiteCov * instrumentation; bool persist; + int processID; int num_iterations; int cur_iteration; }; From 735169740f1cfbe4e4915ab242b3a24e6037b130 Mon Sep 17 00:00:00 2001 From: parikhakshat <68412398+parikhakshat@users.noreply.github.com> Date: Fri, 19 Aug 2022 13:25:29 -0700 Subject: [PATCH 2/3] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 1102f86..dd393b7 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,8 @@ The following command line arguments are supported: `-dict ` - Provides a dictionary to be used during mutation. The dictionary should be a text file with every entry on a separate line. `\xXX` escape sequences can be used. +`-process-name - Enables fuzzing for processes that are already running in the operating system such as Windows services. The fuzzer will attach to the specified running process and get coverage from there. Then, the script specified after `--` will be opened in a new thread and will send the current testcase to running process. Note: this mode only allows for one thread and file delivery through the script. + For TinyInst instrumentation command line arguments, refer to [TinyInst readme](https://github.com/googleprojectzero/TinyInst). Example (macOS): From 9c7ba2013f21d0f6ca8f7d41c0f7e8714aaaf087 Mon Sep 17 00:00:00 2001 From: Akshat Parikh <68412398+parikhakshat@users.noreply.github.com> Date: Mon, 5 Sep 2022 22:14:59 -0400 Subject: [PATCH 3/3] Moved some of the Tinyinst Functionality to the Jackalope codebase --- fuzzer.cpp | 26 ++------ fuzzer.h | 2 - instrumentation.h | 6 -- tinyinstinstrumentation.cpp | 130 +++++------------------------------- tinyinstinstrumentation.h | 6 +- 5 files changed, 28 insertions(+), 142 deletions(-) diff --git a/fuzzer.cpp b/fuzzer.cpp index fa2c72e..1f25ba1 100644 --- a/fuzzer.cpp +++ b/fuzzer.cpp @@ -113,13 +113,10 @@ void Fuzzer::ParseOptions(int argc, char **argv) { incremental_coverage = GetBinaryOption("-incremental_coverage", argc, argv, true); add_all_inputs = GetBinaryOption("-add_all_inputs", argc, argv, false); - + process_name = GetOption("-process_name", argc, argv); - attach_mode = false; - if (process_name != NULL) { - attach_mode = true; //set threads to one as multiple threads attaching to one process does not work well num_threads = 1; } @@ -255,13 +252,7 @@ RunResult Fuzzer::RunSampleAndGetCoverage(ThreadContext *tc, Sample *sample, Cov FATAL("Repeatedly failed to deliver sample"); } } - RunResult result; - if (attach_mode) { - script = ArgvToCmd(tc->target_argc, tc->target_argv); - result = tc->instrumentation->Attach(script, process_name, init_timeout, timeout); - } else { - result = tc->instrumentation->Run(tc->target_argc, tc->target_argv, init_timeout, timeout); - } + RunResult result = tc->instrumentation->Run(tc->target_argc, tc->target_argv, init_timeout, timeout); tc->instrumentation->GetCoverage(*coverage, true); @@ -341,12 +332,9 @@ RunResult Fuzzer::TryReproduceCrash(ThreadContext* tc, Sample* sample, uint32_t FATAL("Repeatedly failed to deliver sample"); } } - if (attach_mode) { - script = ArgvToCmd(tc->target_argc, tc->target_argv); - result = tc->instrumentation->AttachWithCrashAnalysis(script, process_name, init_timeout, timeout); - } else { - result = tc->instrumentation->RunWithCrashAnalysis(tc->target_argc, tc->target_argv, init_timeout, timeout); - } + + result = tc->instrumentation->RunWithCrashAnalysis(tc->target_argc, tc->target_argv, init_timeout, timeout); + tc->instrumentation->ClearCoverage(); if (result == CRASH) return result; @@ -429,8 +417,8 @@ RunResult Fuzzer::RunSample(ThreadContext *tc, Sample *sample, int *has_new_cove if(new_thread_coverage.empty()) return result; } - //printf("found new coverage: \n"); - //PrintCoverage(initialCoverage); + // printf("found new coverage: \n"); + // PrintCoverage(initialCoverage); // the sample returned new coverage diff --git a/fuzzer.h b/fuzzer.h index c331753..a1d1013 100644 --- a/fuzzer.h +++ b/fuzzer.h @@ -197,8 +197,6 @@ class Fuzzer { uint64_t num_threads; uint64_t total_execs; - bool attach_mode; - char * script; char * process_name; void SaveState(ThreadContext *tc); diff --git a/instrumentation.h b/instrumentation.h index adefb58..b401844 100644 --- a/instrumentation.h +++ b/instrumentation.h @@ -31,12 +31,6 @@ class Instrumentation { virtual RunResult RunWithCrashAnalysis(int argc, char** argv, uint32_t init_timeout, uint32_t timeout) { return Run(argc, argv, init_timeout, timeout); } - - virtual RunResult Attach(char * script, char * process_name, uint32_t init_timeout, uint32_t timeout) = 0; - - virtual RunResult AttachWithCrashAnalysis(char * script, char * process_name, uint32_t init_timeout, uint32_t timeout) { - return Attach(script, process_name, init_timeout, timeout); - } virtual void CleanTarget() = 0; diff --git a/tinyinstinstrumentation.cpp b/tinyinstinstrumentation.cpp index 7d9bb77..18c21c4 100644 --- a/tinyinstinstrumentation.cpp +++ b/tinyinstinstrumentation.cpp @@ -28,6 +28,13 @@ void TinyInstInstrumentation::Init(int argc, char **argv) { persist = GetBinaryOption("-persist", argc, argv, false); num_iterations = GetIntOption("-iterations", argc, argv, 1); + process_name = GetOption("-process_name", argc, argv); + script = ArgvToCmd(argc, argv); + + attach_mode = false; + if(process_name != NULL) { + attach_mode = true; + } } RunResult TinyInstInstrumentation::Run(int argc, char **argv, uint32_t init_timeout, uint32_t timeout) { @@ -67,7 +74,17 @@ RunResult TinyInstInstrumentation::Run(int argc, char **argv, uint32_t init_time WARN("Target function not reached, retrying with a clean process\n"); instrumentation->Kill(); cur_iteration = 0; - status = instrumentation->Run(argc, argv, init_timeout); + if (attach_mode) { + Sleep(init_timeout); + processID = FindProcessId(process_name); + if (script != NULL) { + HANDLE thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)system, script, 0, NULL); + CloseHandle(thread_handle); + } + status = instrumentation->Attach(processID, timeout1); + } else { + status = instrumentation->Run(argc, argv, init_timeout); + } } if (status != DEBUGGER_TARGET_START) { @@ -135,117 +152,6 @@ RunResult TinyInstInstrumentation::RunWithCrashAnalysis(int argc, char** argv, u return ret; } -RunResult TinyInstInstrumentation::Attach(char * script, char * process_name, uint32_t init_timeout, uint32_t timeout) { - DebuggerStatus status; - RunResult ret = OTHER_ERROR; - - if (instrumentation->IsTargetFunctionDefined()) { - if (cur_iteration == num_iterations) { - instrumentation->Kill(); - cur_iteration = 0; - } - } - - // else clear only when the target function is reached - if (!instrumentation->IsTargetFunctionDefined()) { - instrumentation->ClearCoverage(); - } - - uint32_t timeout1 = timeout; - if (instrumentation->IsTargetFunctionDefined()) { - timeout1 = init_timeout; - } - - if (instrumentation->IsTargetAlive() && persist) { - status = instrumentation->Continue(timeout1); - } else { - instrumentation->Kill(); - cur_iteration = 0; - instrumentation->script = script; - Sleep(init_timeout); - processID = FindProcessId(process_name); - status = instrumentation->Attach(processID, timeout1); - } - - // if target function is defined, - // we should wait until it is hit - if (instrumentation->IsTargetFunctionDefined()) { - if (status != DEBUGGER_TARGET_START) { - // try again with a clean process - WARN("Target function not reached, retrying with a clean process\n"); - instrumentation->Kill(); - cur_iteration = 0; - instrumentation->script = script; - Sleep(init_timeout); - processID = FindProcessId(process_name); - status = instrumentation->Attach(processID, timeout1); - } - - if (status != DEBUGGER_TARGET_START) { - switch (status) { - case DEBUGGER_CRASHED: - FATAL("Process crashed before reaching the target method\n"); - break; - case DEBUGGER_HANGED: - FATAL("Process hanged before reaching the target method\n"); - break; - case DEBUGGER_PROCESS_EXIT: - FATAL("Process exited before reaching the target method\n"); - break; - default: - FATAL("An unknown problem occured before reaching the target method\n"); - break; - } - } - - instrumentation->ClearCoverage(); - - status = instrumentation->Continue(timeout); - } - - switch (status) { - case DEBUGGER_CRASHED: - ret = CRASH; - instrumentation->Kill(); - break; - case DEBUGGER_HANGED: - ret = HANG; - instrumentation->Kill(); - break; - case DEBUGGER_PROCESS_EXIT: - ret = OK; - if (instrumentation->IsTargetFunctionDefined()) { - WARN("Process exit during target function\n"); - ret = HANG; - } - break; - case DEBUGGER_TARGET_END: - if (instrumentation->IsTargetFunctionDefined()) { - ret = OK; - cur_iteration++; - } else { - FATAL("Unexpected status received from the debugger\n"); - } - break; - default: - FATAL("Unexpected status received from the debugger\n"); - break; - } - - return ret; -} - -RunResult TinyInstInstrumentation::AttachWithCrashAnalysis(char * script, char * process_name, uint32_t init_timeout, uint32_t timeout) { - // clean process when reproducing crashes - instrumentation->Kill(); - // disable instrumentation when reproducing crashes - instrumentation->DisableInstrumentation(); - RunResult ret = Attach(script, process_name, init_timeout, timeout); - instrumentation->Kill(); - instrumentation->EnableInstrumentation(); - return ret; -} - void TinyInstInstrumentation::CleanTarget() { instrumentation->Kill(); } diff --git a/tinyinstinstrumentation.h b/tinyinstinstrumentation.h index 43bd337..98437cb 100644 --- a/tinyinstinstrumentation.h +++ b/tinyinstinstrumentation.h @@ -32,9 +32,6 @@ class TinyInstInstrumentation : public Instrumentation { RunResult Run(int argc, char** argv, uint32_t init_timeout, uint32_t timeout) override; RunResult RunWithCrashAnalysis(int argc, char** argv, uint32_t init_timeout, uint32_t timeout) override; - - RunResult TinyInstInstrumentation::Attach(char * script, char * process_name, uint32_t init_timeout, uint32_t timeout) override; - RunResult TinyInstInstrumentation::AttachWithCrashAnalysis(char * script, char * process_name, uint32_t init_timeout, uint32_t timeout) override; void CleanTarget() override; @@ -50,7 +47,10 @@ class TinyInstInstrumentation : public Instrumentation { protected: LiteCov * instrumentation; bool persist; + boolean attach_mode; int processID; + char * process_name; + char * script; int num_iterations; int cur_iteration; };