Security-Oriented C Tutorial 0x22 - Introduction to the WinAPI
It's been a while since I wrote up an article for this series but I did say that I'd be covering some WinAPI. So if you're interested in programming for the Windows platform, hop aboard and follow along.
Note: This is an extension of the standard C that the previous material covered and is not part of it.
The WinAPI is the Application Programming Interface for Windows and offers many, many powerful functions from which you can build your programs ranging from simple file manipulation to building GUI to to graphics design to programming low-level device drivers. Seriously, come check out the possibilities!
But why program for Windows? What does programming in Windows have to do with anything related to security? Well, for those who haven't realized the obvious, the Windows operating system is a prime target for malware due to its popularity with the standard user. Also, since proprietary software is a big thing in this world, it's not uncommon for people to attempt to reverse engineer and/or crack them.
Of course, to do any of these things, we'll need to be familiar with the environment.
Every decent API should come with the appropriate documentation to detail each function's purpose and how it should be used, providing the proper links with regards to its parameters and how things should be handled in the situation that something fails.
Having said that, the WinAPI is actually an incredibly well-documented reference for all of its hundreds and hundreds of functions, structures, enums, error codes and code examples. A lot of these are derived from the standard C functions such as strcpy and malloc, many are enhanced versions which provide a bit more functionality.
Unfortunately, despite being well-documented, Microsoft has this thing with being "hipsters". Most of the data types listed in the documentation are Microsoft-defined types which are actually just typedefs of the standard data types. You'll know what I mean if you've looked at some of the references already. If you haven't, then you will know soon. I'd suggest you hold on to this as we go.
Before we start, we'll need to know how to set up an environment to code on the Windows OS. Since this isn't some Linux distro, it doesn't have a native compiler to build applications so let's see our options:
- MinGW/Cygwin are third party applications which provide a suitable programming environment on the Windows platform, offering tools and such to be able to do so. When I started out programming on Windows, I used MinGW and two years later, I'm still using it. This option is fantastic, I've had no problem using it and I recommend it;
- Third party IDEs or Integrated Development Environments are applications which create a GUI-based environment for developers. They come packaged with the appropriate tools such as MinGW's to build the Windows programs;
- Microsoft Visual Studio (MSVS) is a Microsoft-developed IDE bundled with their own compiler providing a lot of functionality and options when building native Windows applications including GUI, application version information, icons and other resources (you can still achieve this with the above options but it's integrated into MSVS which makes it easier). If you're wanting to maximize your development for Windows apps, I can highly recommend this. In fact, I've started using this myself in my malware development research.
If you've opted for the first option, you'll require a text editor since it does not provide a GUI. I've been a long-time user of Sublime Text because it's minimalist and its theme is dark and smooth with a very nice coloring for syntax. Very beautiful.
I think that's enough about this topic. Let's take a look at an example of using the WinAPI, shall we?
File handling in Windows is pretty much the same concept as in standard C. We need a handle to a file and we use that handle to perform read or write operations on the target file. Let's briefly learn how to create and write to a file using the WinAPI functions.
To get a handle to a file, we need to use the CreateFile function. Now there's some small piece of information you might want to know before continuing onwards. A lot of WinAPI functions support both wide (for Unicode) and ASCII character sets such as this one, You can specify which one you want by tagging either a W or an A after the function name like so: CreateFileW or CreateFileA. Keep in mind you'll need to be consistent about the character sets your program will support. Usually, you do not need to specify which one you'll be using and can just use the normal CreateFile function name and your compiler will default to either of these options. For the sake of simplicity, I will be using the ASCII version in the example (but I won't specify the A).
Have a careful look through the function's documentation. There's a lot of information and it might be a bit overwhelming but once you get used to it, it's really not so bad. There are brief details of each parameter in the function and it should be enough to help you at this stage. Some may have links to other sections of the documentation and provide some extra information for the options available. If you need help identifying some data types such as DWORD, use the link I provided earlier as a guide. Keep your eyes on the return values as you will want to familiarize yourself with it.
I've declared a HANDLE (void *) type hFile to capture the return value of CreateFile so that we can use it to refer to the file for reading or writing. Some of the parameters are NULL as we can see, meaning that we won't be using those options provided by the function. What the options we've provided allow us to do is:
- Read and write to the file,
- Allow other processes to read the file while it's in use by our program,
- Always create a new file and will overwrite any existing instances,
- Has normal file attributes, i.e. not hidden or read-only, etc.
A quick error check is needed to see if anything wrong happens so we can see where our program went wrong. Notice that the error code INVALID_HANDLE_VALUE was obtained from the return value section of the documentation. The GetLastError function provides an error code from which we can get more information about the issue._
Currently, we have not done anything with the file yet so let's try and write to the file.
Using the handle to the file we have previously obtained, the WriteFile function provides the necessary operations to write to our file. The fourth parameter is optional as specified in the docs with the opt keyword but I've included it in here for verbosity. We can use a variable to capture the return value of the function and test it to see if it succeeds. Note that if the function fails, we will need to close the handle to the file before main returns, just like we would with the file handle provided by fopen. Don't forget!
Now that we've got the writing covered, let's finish it off with a read.
After writing to the file, the file pointer is set to the end so what we need to do before reading from the file, we'll need to relocate the file pointer back to the beginning. We can do this using the SetFilePointer function, using the FILE_BEGIN value.
Once that's taken care of, we can proceed to read the contents. We'll need to provide a handle to the file and a buffer to store the information to the ReadFile function.
There's a reason why I've specifically null-terminated the ReadBuf array and it's because the ReadFile function can't stop by itself when it reaches EOF so it will continue reading until it fills up the buffer. I'm not sure why this is an issue but hey, it's Microsoft... What can you expect?
Before we can return from main, we'll need to clean up the file handle.
Alright, time to compile and then run the program. Let's see the results.
Obviously, there's so much more to it than just file manipulation. It's a big world out there and the possibilities are abundant. Many functions can actually be used to aid malware in achieving their goals which I may detail at the code level in future articles. "Real Windows malware in action". Hopefully, you'll be able to understand how they function and in turn know how to find one lurking on a system.
Have fun exploring!