-
Notifications
You must be signed in to change notification settings - Fork 128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Support for fuzzing by attaching to running processes on Windows. #38
base: main
Are you sure you want to change the base?
Conversation
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for submitting! Please see the comments on the TinyInst PR first. Some general comments on the Jackalope part are below.
tinyinstinstrumentation.cpp
Outdated
@@ -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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function duplicates a lot of code from TinyInstInstrumentation::Run
. Would be better if this was implemented by changing TinyInstInstrumentation::Run
where appropriate. Probably the same thing with TinyInstInstrumentation::AttachWithCrashAnalysis
and TinyInstInstrumentation::RunWithCrashAnalysis
.
I have updated my commit with some of the code that used to be in the Tinyinst repository. I have tested the tool and it works pretty well with some of the targets. Let me know if there is anymore work needed to be done. Note: there is some redundancy of code involving process_name in both fuzzer.cpp and tinyinstrumentation.cpp. I wasn't able to move that code outside of fuzzer.cpp because I need that code to set the number of threads to one, which only happens in fuzzer.cpp. When attaching to processes and getting coverage, I've found that the coverage and stability of the target is only really stable when only one thread is attached to it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I apologize for the delay, I was out of office for a while due to conference travel and it slipped my mind. Comments below.
@@ -127,6 +127,8 @@ The following command line arguments are supported: | |||
|
|||
`-dict <path>` - 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 <name of running process> - 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure windows services is a good example here as afaik all windows services run under the same process name (svchost.exe
)
|
||
if (process_name != NULL) { | ||
//set threads to one as multiple threads attaching to one process does not work well | ||
num_threads = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps if num_threads
is not 1
already it would be a good idea to notify the user that Jackalope is changing the thread count, e.g.
WARN("Using a single fuzzing thread when process_name is set");
@@ -325,8 +332,9 @@ RunResult Fuzzer::TryReproduceCrash(ThreadContext* tc, Sample* sample, uint32_t | |||
FATAL("Repeatedly failed to deliver sample"); | |||
} | |||
} | |||
|
|||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are some changes to spacing that should be reverted.
@@ -31,7 +31,7 @@ class Instrumentation { | |||
virtual RunResult RunWithCrashAnalysis(int argc, char** argv, uint32_t init_timeout, uint32_t timeout) { | |||
return Run(argc, argv, init_timeout, timeout); | |||
} | |||
|
|||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary changes to spacing
void TinyInstInstrumentation::Init(int argc, char **argv) { | ||
instrumentation = new LiteCov(); | ||
instrumentation->Init(argc, 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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Running a script is a part of sample delivery so I don't think it should go in this class. Instead, my proposal is to
- Create a new class
ExternalSampleDelivery
that subclassesSampleDelivery
(seesampledelivery.h
andsampledelivery.cpp
for existing examples) - In Fuzzer::CreateSampleDelivery (
Line 987 in 6134ff1
SampleDelivery *Fuzzer::CreateSampleDelivery(int argc, char **argv, ThreadContext *tc) { -delivery external
), create theExternalSampleDelivery
object and set its params. You can get the value of target argc/argv fromtc->target_argc
/tc->target_argv
- In
ExternalSampleDelivery::DeliverSample
create a thread a run the script
Sleep(init_timeout); | ||
processID = FindProcessId(process_name); | ||
if (script != NULL) { | ||
HANDLE thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)system, script, 0, NULL); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CreateThread
here is a Windows function so it will only work on Windows. LPTHREAD_START_ROUTINE
is also Windows specific. Jackalope has its own function for creating threads that works cross-platform, see https://github.com/googleprojectzero/Jackalope/blob/main/thread.h For an example of use see e.g.
Line 667 in 46c1c9b
CreateThread(StartStatusThread, this); |
@@ -32,7 +32,7 @@ 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; | |||
|
|||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary changes to spacing.
HANDLE thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)system, script, 0, NULL); | ||
CloseHandle(thread_handle); | ||
} | ||
status = instrumentation->Attach(processID, timeout1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here in attach mode you call instrumentation->Attach
instead of instrumentation->Run (good!), but there is another place above where instrumentation->Run
is called that might behave incorrectly in attach mode. You should do the same thing there as well.
@@ -68,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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sleep is a Windows function, see e.g.
Line 202 in 6134ff1
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) |
status = instrumentation->Run(argc, argv, init_timeout); | ||
if (attach_mode) { | ||
Sleep(init_timeout); | ||
processID = FindProcessId(process_name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be a check here if FindProcessId failed. Instead of calling Sleep
first and then FindProcessId
, maybe you can do something like
while(1) {
processID = FindProcessId(process_name);
if(processID) break;
WARN('Could not find target process, retrying after timeout');
Sleep(init_timeout);
}
I have made some changes to Jackalope that allow someone to fuzz processes that are already running on the system by attaching to them. The current limitations of this new mode are that it only supports file delivery and single threads. The reason for only supporting one thread is that attaching multiple threads to the same process to fuzz results in unreliable. I also plan to add a recovery_script option to this mode which allows the user to specify a script that can be used to restart the running process to a fresh state. Currently, I have tested this mode on processes that have an automatic restart feature after each time they are killed, so I didn't need the recovery_script option. Please let me know if you have any questions or concerns about my commit.
To support this new mode, I needed to make some minor changes to TinyInst as well. Those changes are in a separate pull request on that repository.