Skip to content
badbit
Menu
  • Blog
  • About
Menu

WindowsAPI voodoo

Posted on February 3, 2021February 5, 2021 by badbit

This will be a multi part series where I will be publishing my notes taken while diving into the mysterious world of WinAPI programming. When it comes to WinAPI, there’s no better reference than MSDN itself! And also, there’s no escape to it. To fully comprehend the mystery, we will have to refer to it no matter what.


Part 1 – Enumerating processes on Windows

The goal of the resultant binary would be to enumerate all the processes running on a Windows machine. The result would be produced on stdout consisting of the following details for each running process:

  • Process ID
  • Handles
  • Threads
  • Process Name
  • SID
  • Account Name

Take aways:

  • Understanding of Tokens
  • Understanding of Token privileges
  • Enabling / Disabling privileges of a process

Some WinAPI specific terminology before we proceed:

  • LPSTR – Long Pointer String
  • LUID – Describes a local identifier
  • _T – stands for “text”. Used where UNICODE support is required

Some must-knows:

  • When a user logs-in, the LSA creates a token for a user. This token is given to every single process created for that user. The Token contains:
    • User’s SID
    • Group SIDs
    • Privileges
  • Based on the SID and what the user is permitted to do, the Token holds the respective privileges
  • Not all privileges are enabled by default
  • A process has the ability to enable/disable available privileges

The execution flow will be as follows:

  1. WTSEnumerateProcess – To enumerate all the running processes
  2. ConvertSidToStringSid – To convert the SID of the fetched process into string for displaying
  3. LookupAccountSid – To enumerate the domain name and the account name of the process owner
  4. LookupPrivilegeValue – To select “SeDebugPrivilege” as the privilege value to perform further opertions
  5. OpenProcessToken – To open a handle to the current process’s (GetCurrentProcess()) Token to manipulate the privileges
  6. AdjustTokenPrivilege – To add the “SeDebugPriv” to the process
  7. Print the processes

A brief description of the APIs used to achieve the above goal:

1. WTSEnumerateProcess

  • Retrieves information about the active processes on a specified Remote Desktop Session Host (RD Session Host) server.
BOOL WINAPI WTSEnumerateProcessesEx
(
_In_ HANDLE hServer,
_Inout_ DWORD *pLevel,
_In_ DWORD SessionID,
_Out_ LPSTR *ppProcessInfo,  
_Out_ DWORD *pCount
);

Windows Docs – WTSEnumerateProcess

2. ConvertSidToStgringSid

  • Since SID is a variable length structure, we need to convert it into a suitable string for displaying it on stdout
BOOL ConvertSidToStringSidA(
  PSID  Sid,
  LPSTR *StringSid
);

Windows Docs – ConvertSidToStgringSid

3. LookUpAccountSid

  • API required to fetch the process owner’s / user’s domain and username
BOOL LookupAccountSidA(
  LPCSTR        lpSystemName,
  PSID          Sid,
  LPSTR         Name,
  LPDWORD       cchName,
  LPSTR         ReferencedDomainName,
  LPDWORD       cchReferencedDomainName,
  PSID_NAME_USE peUse
);

Windows Docs – LookupAccountSid

4. LookUpPrivilegeValue

  • Fix on the privilege we need to enable. SeDebugPriv in our case
BOOL LookupPrivilegeValueA(
  LPCSTR lpSystemName,
  LPCSTR lpName,
  PLUID  lpLuid
);

Windows Docs – LookUpPrivilegeValue

5. OpenProcessToken

  • Opening the access token of the current process to modify it. This API requires a handle to the process. We can get the same by using GetCurrentProcess() which opens a pseudo-handle to our current process
BOOL OpenProcessToken(
  HANDLE  ProcessHandle,
  DWORD   DesiredAccess,
  PHANDLE TokenHandle
);

Windows Docs – OpenProcessToken

6. AdjustTokenPrivilege

  • Lastly, modifying the privileges with the desired privileges.
BOOL AdjustTokenPrivileges(
  HANDLE            TokenHandle,
  BOOL              DisableAllPrivileges,
  PTOKEN_PRIVILEGES NewState,
  DWORD             BufferLength,
  PTOKEN_PRIVILEGES PreviousState,
  PDWORD            ReturnLength
);

Windows Docs – AdjustTokenPrivilege


Alright, enough of the theory. Show me the damn code!

Header file for enabling the SeDebugPrivilege

// **The following code enables the SeDebugPrivilege for the current process**
// Source - PentesterAcademy's - Windows API Exploitation Recipes: Processes, Tokens and Memory RW series.
#include <TlHelp32.h>
BOOL enablePriv(void) {
    //Fetch privilege value for SeDebugPriv
    // LookupPrivilegeValue
    LUID    privLUID;
    if (!LookupPrivilegeValue(
        NULL,
        _T("SeDebugPrivilege"),
        &privLUID
        ))
    {
        ErrorExit(TEXT("LookupPrivilegeValue()"));
    }
    // Setting up Token Privileges
    TOKEN_PRIVILEGES tp;
    tp.PrivilegeCount = 1; // count of Privileges to be modified
    tp.Privileges[0].Luid = privLUID; // The LUID of the privilege to be modified 
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // enable this privilege
    // Setting up parameters for OpenProcessToken API
    HANDLE  currentProcessHandle = GetCurrentProcess();
    HANDLE  processToken; // A pointer to a handle that identifies the newly opened access token when the function returns. Will be required for AdjustPriv API
    // TOKEN_ADJUST_PRIVILEGES  Required to enable or disable the privileges in an access token.
    if (!OpenProcessToken(currentProcessHandle, TOKEN_ADJUST_PRIVILEGES, &processToken))
    {
        ErrorExit(TEXT("LookupPrivilegeValue()"));
    }
    // Enabling privileges in the cureent processes's Token
    if (!AdjustTokenPrivileges(processToken, FALSE, &tp, 0, NULL, NULL))
    {
        ErrorExit(TEXT("AdjustTokenPrivileges"));
    }
    return TRUE;
}

Main source to enumerate processes:

// **The following code enumerates processes on the system. Tested on Windows 7/10.**
// Source - PentesterAcademy's - Windows API Exploitation Recipes: Processes, Tokens and Memory RW series.
#include <Windows.h>
#include <tchar.h>
#include <WtsApi32.h>
#include <sddl.h>
#include <iostream>
#include "Header.h"
#include "SeDebug.h"
#include <strsafe.h>
#pragma comment(lib, "wtsapi32")
#pragma comment(lib, "Advapi32")
#define MAX_ACCOUNTNAME_LEN 1024
#define MAX_DOMAINNAME_LEN 1024
int main(void)
{
    // Enabling SeDebugPrivilege
    enablePriv();
    DWORD level = 1;
    PWTS_PROCESS_INFO_EX processListing = NULL;
    DWORD processCount = 0;
    DWORD dw = GetLastError();
    if (!WTSEnumerateProcessesEx(
        WTS_CURRENT_SERVER_HANDLE,
        &level,
        WTS_ANY_SESSION,
        (LPTSTR*)&processListing,
        &processCount))
    {
        ErrorExit(TEXT("WTSEnumerateProcessesEx"));
        //std::cout << "Failed with error code: %d" << dw;
    }
    _tprintf(_T("Processes found: %d\n\n"), processCount);
    _tprintf(_T("#\tPID\tHandles\tThreads\tProcess Name\tSID\tAccount\n\n"));
    LPTSTR stringSID = NULL;
    PWTS_PROCESS_INFO_EX originalPtr = processListing;
    for (DWORD counter = 1; counter <= processCount; counter++)
    {
        _tprintf(_T("%d\t"), counter);
        _tprintf(_T("%d\t"), processListing->ProcessId);
        _tprintf(_T("%d\t"), processListing->HandleCount);
        _tprintf(_T("%d\t"), processListing->NumberOfThreads);
        _tprintf(_T("%s\t"), processListing->pProcessName);
        // Printing the SID and associated accounts
        // MSDN - https://docs.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertsidtostringsida
        if (!ConvertSidToStringSid(
            processListing->pUserSid,
            &stringSID))
        {
            _tprintf(_T("-\t"));
            //ErrorExit(TEXT("ConvertSidToStringSid"));
            //std::cout << "Failed with error code: %d", dw;
        }
        else
        {
            _tprintf(_T("%s\t"), stringSID);
            LocalFree((HLOCAL)stringSID);
        }
        TCHAR accountName [MAX_ACCOUNTNAME_LEN];
        DWORD bufferLen = MAX_ACCOUNTNAME_LEN;
        TCHAR domainName[MAX_DOMAINNAME_LEN];
        DWORD domainNameBufferLen = MAX_DOMAINNAME_LEN;
        SID_NAME_USE peUse;
        if (!LookupAccountSid(
            NULL,
            processListing->pUserSid,
            accountName,
            &bufferLen,
            domainName,
            &domainNameBufferLen,
            &peUse)
            )
        {
            //ErrorExit(TEXT("LookupAccountSid"));
            _tprintf(_T("\n"));
        }
        else
        {
            _tprintf(_T("%s\\%s\n"), domainName, accountName);
        }
        processListing++;
    }
    if (!WTSFreeMemoryEx(WTSTypeProcessInfoLevel1, originalPtr, processCount))
    {
        ErrorExit(TEXT("WTSFreeMemoryEx"));
        //std::cout << "Failed with error code: %d" << dw;
    }
    processListing = NULL;
    _tprintf(_T("\n\nDone! Press any key to exit. \n"));
    getchar();
    return 0;
}

Output


The above notes have been taken while following PentesterAcademy’s – Windows API Exploitation Recipes: Processes, Tokens and Memory RW series.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recent Posts

  • Make Your Own Luck – Bob Miglani & Rehan Khan
  • Zero to One – Peter Thiel
  • Mr. Crack Jack
  • WindowsAPI voodoo
  • Flare-On 7 | Challenge 2

Categories

  • CTF Write-ups
  • Five things I learnt
  • Reversing
  • Shellcoding
  • SLAE x86
  • Uncategorized
  • WindowsAPI

Connect

TwitterGithubLinkedIn
©2025 badbit | Built using WordPress and Responsive Blogily theme by Superb