Windows API Highlight - CreateProcess()
Like the research? Check out my weekly research wrap-up Valhalla Weekly for more tech research!
Welcome to the first of many Windows API Highlight articles! The purpose of this series is to highlight some of the most useful Windows API functions, document my research into the API and ways to use it for malware development, and to help provide new material that might be a little clearer or at least more entertaining than the MSDN blogs. Let's get cracking!
CreateProcess() is a macro function for CreateProcessA and CreateProcessW, which are the ASCII and Unicode versions of the function, respectively. This means that usually you can get away with just calling CreateProcess(...) and the macro will convert that into a call to the ASCII or Unicode version depending on your compiler settings. We're going to be calling it CreateProcess() for brevity, but that can trip people up.
The CreateProcess() function... well it creates a process! A process can be thought of any running version containing one or more threads of execution and a singular memory region and security context shared across those threads. What this means in function is that you create a process any time you execute something, and background processes are running on your Windows system at all times.
As you can see from the screenshot above, there are tons of processes running on my system. Some of them are processes I'm actively using, like Brave (my browser of choice) and Discord. Some of them, like Antimalware Service Executable, Service Host, Desktop Window Manager and COM Surrogate, are background processes that are managed by the operating system.
Many or most of these were probably created using the CreateProcess() function! Let's dive into it a bit.
The arguments for CreateProcess() are as follows:
- lpApplicationName - a string representation of the name of the application. This can be a full path (C:\foo\bar\baz.exe) or a relative path (bar\baz.exe) relative to the location you're calling it from.
- lpCommandLine - a string representation of the commandline arguments. If you're not using args, you can leave this null. If your lpApplicationName is Null, the first space-deliminated string has to be the name of the executable you're trying to run.
- lpProcessAttributes - This is a pointer to a SECURITY_ATTRIBUTES structure (documented here) with all of the relevant information filled out. I've found I usually just leave this blank.
- lpThreadAttributes - This has to do with specific security contexts surrounding inheritance from the main thread. I have yet to use this for any real reason but I'm sure it has its purpose. It uses the same struct as lpProcessAttributes (documented here) but it specifically pertains to the security context of the main thread, not just the general process.
- bInheritHandles - This basically allows you to share open handles from the calling process to the the new process. Again, haven't needed this and usually just leave this False.
- dwCreationFlags - This is a flag variable that determines some variable settings determining the manner by which the process is created. You can check out the flag values here, but this is another one I usually just set to NULL.
- lpEnvironment - This allows you to pass on environment variables by passing the new process a pointer to an environment block. I usually leave this null, which passes on the environment block belonging to the calling process. So if you create some environment variables within your process/malware and call the CreateProcess function with a null lpEnvironment, you'll inherit the environment variables from the calling process/malware.
- lpCurrentDirectory - I leave this null to specify that we're working from the same directory context as the calling process, which is usually useful.
- lpStartupInfo - This is just a pointer to a STARTUPINFO structure (documented here) that's used to configure how the process is started up.
- lpProcessInformation - This is a structure (documented here) that holds information about the status of the created process. It contains the handle to the process and the main thread that is created as well as the process identifier and thread identifier.
Here is how I used this function in the last blog about embedding Python malware within Windows executables.
As you can see, most of the inputted variables I passed as being NULL. I didn't care about commandline input, created an empty STARTUPINFO structure to pass a pointer to as well as an empty PROCESS_INFORMATION structure and zeroe'ed out their memory addresses and passed them as input to the function. The only thing that really mattered (within my very simple context) was just running the embedded executable that was located within the same directory as the running malware named "embed_writ4.exe."
Conclusion and Disclaimer
My context for using the CreateProcess function was incredibly simple. No commandline arguments, just calling an executable within the same directory as the calling executable. If you have more complex requirements, I would heavily recommend poring over the MSDN article. This article is much better geared toward people who need it for simple contexts like mine.