Introduction to Windows API

Kavishka Gihan
7 min readSep 5, 2022

In this article I will be talking about the well-known Windows API (Win32 API), it’s functionality and how we can implement API calls from different langauges.

Windows API is used to access key components of the Windows operating system. Different application need to modify and change different Windows subsystems and hardware. To perform this in a stealthy way, Windows API is widely used by almost every Windows compatible application.

We call Windows 32 API which then gives out SYSCALLs to the kernel to do the changes we need. When we put this process to a diagram, it would look like this.

Layers of the Windows API

Before we dive right into the functionality of the API, its required to understand the layers of the API and terminologies that we will be referring to

You don’t have to know everything about these layers and terminologies but make sure you get yourself familiar with these terms and basic behaviors of them.

Working with Win32 API, it uses memory addresses. ASLR obscures the process of importing API calls. So there are 2 technologies used to overcome ASLR

  • Windows header files
  • P/Invoke

Windows header files

Header files are included into a program to get different functionalities to the program. These header files will maintain a table of functions and addresses to obtain the required function addresses or pointers to use. You may have seen them included in a C sources like:

#include <windows.h>
#include <stdio.h>

For example, windows.h header file is a windows specific header which includes all required child and core header files. This also includes all declarations of the function calls in the Windows 32 API. This is a header file we include in C and C# like languages to get the functionality of the Windows 32 API. This provides various functions and data type declarations like integers and floats.

Child headers

Child headers are the headers that are automatically attached by another header. For example, there are different child header file that are automatically attached by windows.h to get specific functionalities. I.e:

  • winuser.h: User services (user32.dll)
  • excpt.h: Exception handling

P/Invoke

This stand for Platform Invoke which is used to access structs, callbacks and functions from the code (program). This lets us import desired DLL to the program. This is mainly used in .NET applications.

using System;
using System.Runtime.InteropServices;
public class Program
{
[DllImport(“user32.dll”, CharSet = CharSet.Unicode, SetLastError = true)]

}
  • Here, we use the System and System.Runtime.InteropServices namespaces to provide P/invoke to the program. user32.dll DLL is imported using the DLLImport attribute.
using System;
using System.Runtime.InteropServices;
public class Program
{

private static extern int MessageBox(IntPtr hWnd, string lpText, string lpCaption, uint uType);
}
  • Now we create an external method to handle the imported DLL. extern keyword is used to provide the runtime of the specific DLL that was imported previously. With this, we can invoke the MessageBox function in a managed method. But still, this is an unmanaged function.

In/Out parameters of API calls

Firstly, let’s discuss what these parameters are and how the are behaved during an API call.

Input parameters : You must set input parameters before calling an API because these parameters pass needed information to the API to enable it to perform its function. For example, if the API is to perform a function on an object, one of the parameters would be the name and library of the object. Input parameters are not changed by the API.

Output parameters: You do not need to set output parameters before calling an API because the API returns information to the application in these parameters. When a return to the application is successful and no errors occur, the application accesses the information returned in output parameters.

All the API calls has a pre-defined structure which defines its own in/out parameters. We can find these structures from Microsoft’s API index

I.e this is the structure for CheckDIgButton call

BOOL CheckDlgButton(
[in] HWND hDlg,
[in] int nIDButton,
[in] UINT uCheck
);

[in]: data read from
[out] : data written to
[in, out]:data written to and read from (this mean pretty much what it says, both read and written to inside the call. Say you had a very simple function which increments the value of the parameter. It would firstly have to read the value and then use the pointer to set it to value+1)

Here, 3 parameters are passed to the API call:
1. parameter hDlg of data type HWND (A handle to a window)
2. parameter nIDButton of data type int
3. parameter uCheck of data type UINT (An unsigned integer); You can find all the data types from here)

BOOL keyword in the beginning specifies that in a successful API call it will return a boolean value.

This is the structure of WriteProcessMemory function from memoryapi.h ( a system service header file)

BOOL WriteProcessMemory(
[in] HANDLE hProcess, // PID of the process to read memory
[in] LPVOID lpBaseAddress, // Base address where it should start reading from
[in] LPCVOID lpBuffer, // Buffer to store the already read memory
[in] SIZE_T nSize, // Max number of bytes to read
[out] SIZE_T *lpNumberOfBytesWritten // Returning the number of bytes we have been read
);
  • Notice the asterisk before the lpNumberOfBytesWritten parameter. It indicates that the parameter is a pointer. This means that the parameter is in fact an address in memory of an int-like variable (int, uint, size_t, etc) Upon a successful call the function will return a BOOL indicating whether the call was successful or not, but it will also set the value of the variable indicated by the pointer to the number of bytes written; sort of side effect from the call.

NOTE: If a certain user has SeDebugPrivilege privileges set, we can get system by using these ReadProcessMemory and WriteProcessMemory functions. I will be covering that in a separate article

API call implementation with C and .NET

Like mentioned above, in order to access the API calls needed to interact with the API we need to include the windows.h header in our program

#include <windows.h>

This is the API call structure to make a windows pop-up

HWND CreateWindowExA(
[in] DWORD dwExStyle, // Optional windows styles
[in, optional] LPCSTR lpClassName, // Windows class
[in, optional] LPCSTR lpWindowName, // Windows text
[in] DWORD dwStyle, // Windows style
[in] int X, // X position
[in] int Y, // Y position
[in] int nWidth, // Width size
[in] int nHeight, // Height size
[in, optional] HWND hWndParent, // Parent windows
[in, optional] HMENU hMenu, // Menu
[in, optional] HINSTANCE hInstance, // Instance handle
[in, optional] LPVOID lpParam // Additional application data
);

With this, we can create a windows pop-up with the title “Hello kavi”

HWND hwnd = CreateWindowsEx(
0,
CLASS_NAME,
L"Hello kavi!",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
  • Here the parameters can be filled with values according to our need. l.e the 4th parameter (dwstyle) is used to style the windows the way we need. Here in this example its using WS_OVERLAPPEDWINDOW value asthe attributes of the windows. Actually this flag is a combination of several flags which gives the title bar, a border, a menu etc

Well now that we have our API call defined, after we need to implement this to our C program.

BOOL Create(
PCWSTR lpWindowName,
DWORD dwStyle,
DWORD dwExStyle = 0,
int x = CW_USEDEFAULT,
int y = CW_USEDEFAULT,
int nWidth = CW_USEDEFAULT,
int nHeight = CW_USEDEFAULT,
HWND hWndParent = 0,
HMENU hMenu = 0
)
{
WNDCLASS wc = {0};

wc.lpfnWndProc = DERIVED_TYPE::WindowProc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = ClassName();

RegisterClass(&wc);

m_hwnd = CreateWindowEx(
dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), this
);

return (m_hwnd ? TRUE : FALSE);
}
  • Here a lot of stuff are happening instead of creating the API call, this is well explained in here
    1. First the variables needed to pass to the API call are created with Create().
    2. Then we see its declaring a new Windows Class with WNDCLASS wc = {0};
    3. After we set the corresponding attributes of the window class (lpfnWndProc : defines the behavior of the windows in relation toWindowProc,hInstance: handle to the application instance,lpszClassName: string that identifies the window class)
    4. After all that, we register the Window with RegisterClass(&wc); by providing the pointer to our windows class (wc)
    5. Finally all that’s left to do is to create the window with CreateWindowEx

Let’s consider a .NET implementation of an API call. For this example we will be using the GetComputerNameA API call to get the name of the computer.

class Win32 {
[DllImport("kernel32")] // kernel32.dll includes the functions needed to perform API calls
public static extern IntPtr GetComputerNameA(StringBuilder lpBuffer, ref uint lpnSize);
}
static void Main(string[] args) {
bool success;
StringBuilder name = new StringBuilder(260);
uint size = 260;
success = GetComputerNameA(name, ref size);
Console.WriteLine(name.ToString());
}

Just like we discussed earlier, here it’s using P/Invoke to import the kernel32 DLL and use an implemented external method to handle the DLL. After in the Main() function, its calling the API and writing the returned compuer name to the console.

In an upcomming article, I will talk about the ways we can abuse these API calls.

References

--

--

Kavishka Gihan

Cyber Security Student | Machine author @hackthebox | find me on instagram @_kavi.gihan