original notes from obsidian

WinAPI Lists

Contains most used WinAPI - links, functionality, parameters, returns, interesting remarks, and personal notes


VirtualAlloc

LPVOID VirtualAlloc(
 LPVOID lpAddress,
 SIZE_T dwSize,
 DWORD flAllocationType,
 DWORD flProtect
);

LPVOID = Memory Pointer = LongPtr

  1. lpAddress = Memory allocation address

    • 0 = API choose the location automatically

  2. dwSize = Size of the allocation

  3. flAllocationType = Memory allocation type

    • Usually MEM_COMMIT | MEM_RESERVE = 0x3000

  4. flProtect = Memory protection flags

    • Usually 0x40 = RWX

    • But you know, better to alloc --> VMProtect --> Write Mem --> VMProtect --> run (but more on that later)


OpenProcess - Retrieve a handle to a remote process - based on PID

HANDLE OpenProcess(
 DWORD dwDesiredAccess,
 BOOL bInheritHandle,
 DWORD dwProcessId
);

dwDesiredAccess = Access right to obtain in target process. usually PROCESS_ALL_ACCESS (0x001F0FF)

bInheritHandle = True/False on whether the handle can be inherited to child process or not. Usually False, because we just don't care.

dwProcessId = Target Process's PID


VirtualAllocEx - Allocate memory in a remote process

LPVOID VirtualAllocEx(
 HANDLE hProcess,
 LPVOID lpAddress,
 SIZE_T dwSize,
 DWORD flAllocationType,
 DWORD flProtect
);

hProcess = Target process's handle lpAddress = Start address. Usually 0, so VirtualAllocEx can automatically choose the starting address for us

  • (Driploader changes this to trick EDR solutions, but that's another story)

dwSize = Length to allocate. Usually shellcode's length flAllocationType = Type of allocation. Usually MEM_COMMIT | MEM_RESERVE = 0x3000 flProtect = Memory protection type. Usually 0x20 (Read) or 0x40 (RWX)


VirtualProtect - Change memory protection

BOOL VirtualProtect(
 LPVOID lpAddress,
 SIZE_T dwSize,
 DWORD flNewProtect,
 PDWORD lpflOldProtect
);

lpAddress = IntPtr - Page address - Start pointer of the memory address dwSize = UInt32 - Size of area wish to modify. 1 ~ 0xFFF is same. Usually 3. flNewProtect = UInt32 - Memory protection constant. RWX = 0x40, RX = 0x20 lpflOldProtect = [ref] UInt32 = Current memory protection


WriteProcessMemory - Write to a region of a memory in a remote process

BOOL WriteProcessMemory(
 HANDLE hProcess,
 LPVOID lpBaseAddress,
 LPCVOID lpBuffer,
 SIZE_T nSize,
 SIZE_T *lpNumberOfBytesWritten
);

hProcess = Handle to target process lpBaseAddress = Starting address to write memory. Usually alloc returned from VirtualAllocEx lpBuffer = Address of source byte array to be copied nSize = Length/Size of the lpBuffer. Usually shellcode length lpNumberOfBytesWritten = Pointer to a location in memory to output how much data was copied

  • Usually used with out keyword from C#

  • Pass by reference, not value - because it's a pointer that will hold data copied.


CreateRemoteThread

HANDLE CreateRemoteThread(
 HANDLE hProcess,
 LPSECURITY_ATTRIBUTES lpThreadAttributes,
 SIZE_T dwStackSize,
 LPTHREAD_START_ROUTINE lpStartAddress,
 LPVOID lpParameter,
 DWORD dwCreationFlags,
 LPDWORD lpThreadId
);

hProcess = Handle to the target process lpThreadAttributes = Thread attribute. Usually 0, unless PPID spoofing... but that's another story. dwStackSize = Usually 0 lpStartAddress = Start address of the thread. Usually alloc from VirtualAllocEx. This is where the shellcode starts. lpParameter = Pointer to variables if parameter. Since shellcodes usually don't have parameters, usually 0. dwCreationFlags = Usually IntPtr.Zero lpThreadId = Usually 0.


ReadProcessMemory = Read memory from remote process

BOOL ReadProcessMemory(
 HANDLE hProcess,
 LPCVOID lpBaseAddress,
 LPVOID lpBuffer,
 SIZE_T nSize,
 SIZE_T *lpNumberOfBytesRead
);

hProcess = IntPtr / Handle to remote process lpBaseAddress = IntPtr / Base address to start reading from lpBuffer = byte[] / Destination buffer to copy the content from nSize = int / Number of bytes to be read lpNumberOfBytesRead = out / Number of bytes that were ACTUALLY read

Example

byte[] addrBuf = new byte[IntPtr.Size];
IntPtr nRead = IntPtr.Zero;
ReadProcessMemory(hProcess, ptrToImageBaseAddress, addrBuf, addrBuf.Length, out nRead);

CreateProcessW - Create a Process

BOOL CreateProcessW(
 LPCWSTR lpApplicationName,
 LPWSTR lpCommandLine,
 LPSECURITY_ATTRIBUTES lpProcessAttributes,
 LPSECURITY_ATTRIBUTES lpThreadAttributes,
 BOOL bInheritHandles,
 DWORD dwCreationFlags,
 LPVOID lpEnvironment,
 LPCWSTR lpCurrentDirectory,
 LPSTARTUPINFOW lpStartupInfo,
 LPPROCESS_INFORMATION lpProcessInformation
);

lpApplicationName = Name of the application - Usually null lpCommandLine = Full commandline to be executed - Usually full path of executable lpProcessAttribute & lpThreadAttribute = Specify security descriptor - Usually null and false dwCreationFlag = Creation Flag. CREATE_SUSPENDED = 0x4 lpEnvironMent, lpCurrentDirectory = Straight forward, but usually null for default. lpStartupInfo = STARTUPINFO Structure that includes values related with how new process should be configured. Included in PInvoke.net's cheatsheet lpProcessInformation = PROCESS_INFORMATION structure with handles, threads, pid, tid. Provided in PInvoke.net.

Example

STARTUPINFO si = new STARTUPINFO();
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();

bool result = CreateProcess(null, @"C:\windows\system32\svchost.exe", IntPtr.Zero, IntPtr.Zero, false, 0x4, IntPtr.Zero, null, ref si, out pi); 

ZwQueryInformationProcess - Return PEB information inside a PI (Process Information) variable

NTSTATUS WINAPI ZwQueryInformationProcess(
 _In_ HANDLE ProcessHandle,
 _In_ PROCESSINFOCLASS ProcessInformationClass,
 _Out_ PVOID ProcessInformation,
 _In_ ULONG ProcessInformationLength,
 _Out_opt_ PULONG ReturnLength
);

NTSTATUS (return type) = hex value from the kernel showing the status of the call.

ProcessHandle = IntPtr / Handle to process from PROCESS_INFORMATION structure ProcessInformationClass = Usually 0 ProcessInformation = ref PROCESS_BASIC_INFORMATION() / structure PorcesInformationLength = uint / Size of input structure (6 IntPtr) ReturnLength = ref uint variable that hold the size of fetched data

ex)

PROCESS_BASIC_INFORMATION bi = new PROCESS_BASIC_INFORMATION();
uint tmp = 0; 
IntPtr hProcess = pi.hProcess;
// PROCESS_BASIC_INFORMATION has 6 IntPtr, thus (IntPtr.Size*6)
ZwQueryInformationProcess(hProcess, 0, ref bi, (uint)(IntPtr.Size*6), ref tmp);

// Now "bi" has the PEB address. 
IntPtr ptrToImageBase = (IntPtr)((Int64)bi.PebAddress + 0x10); 

GetCurrentProcess - Return current process's handle

[DllImport("kernel32.dll")]
static extern IntPtr GetCurrentProcess();

CreateNamedPipe - Creates a named pipe

HANDLE CreateNamedPipeA(
 LPCSTR lpName,
 DWORD dwOpenMode,
 DWORD dwPipeMode,
 DWORD nMaxInstances,
 DWORD nOutBufferSize,
 DWORD nInBufferSize,
 DWORD nDefaultTimeOut,
 LPSECURITY_ATTRIBUTES lpSecurityAttributes
);

lpName = string = Name of the pipe (ex. \\.\pipe\pipe_name) dwOpenMode = uint = Mode of the pipe = Mostly 3 = PIPE_ACCESES_DUPLEX dwPipeMode = uint = Mode of the pipe operation = PIPE_TYPE_BYTE|PIPE_WAIT, Usually 0. nMaxInstances = uint = Maximum number of instances of the pipe. Anything 1~255. nOutBufferSize = uint = Number of bytes to use for input/output buffer. Usually 0x1000 bytes. nInBufferSize = uint = Number of bytes to use for input/output buffer. Usually 0x1000 bytes. nDefaultTimeout = uint = Default timeout. Usually 0. lpSecurityAttributes = IntPtr = SID of clients that can interact with the pipe. Usually NULL/IntPtr.Zero, because we want any clients to connect to our dummy pipe server.


ConnectNamedPipe - Connect to a named pipe

BOOL ConnectNamedPipe(
 HANDLE hNamedPipe,
 LPOVERLAPPED lpOverlapped
);

hNamedPipe = IntPTr =Handle to the named pipe to connect to lpOverlapped = IntPtr = Pointer to a structure for advnaced cases. Usually IntPtr.Zero/Null for us.


ImpersonateNamedPipeClient - Impersonate the access token of the pipe client that connected to our pipe server. Stolen token will be assigned to the current thread of the process.

BOOL ImpersonateNamedPipeClient(
 HANDLE hNamedPipe
);

hNamedPipe = IntPtr = Handle to the named pipe (server).


OpenThreadToken - Open thread's token. Used for confirmation that impersonation was successful.

BOOL OpenThreadToken(
 HANDLE ThreadHandle,
 DWORD DesiredAccess,
 BOOL OpenAsSelf,
 PHANDLE TokenHandle
);

ThreadHandle = IntPtr = Handle to the thread to check its token. In this case, we want to check our thread from our process. So just use GetCurrentThread. DesiredAccess = uint = Desired Access to the thread. Usually TOKEN_ALL_ACCESS = 0xF01FF OpenAsSelf = bool = Should API use security context of current process? Usually false/no, because we are using the impersonated token of the target process. TokenHandle = IntPtr hToken; out hToken = (Out) pointer that will be populated with a handle to the token that is opened.


GetTokenInformation - Return token information from the token handle. One of those "Call twice" weird API.

BOOL GetTokenInformation(
 HANDLE TokenHandle,
 TOKEN_INFORMATION_CLASS TokenInformationClass,
 LPVOID TokenInformation,
 DWORD TokenInformationLength,
 PDWORD ReturnLength
);

TokenHandle = IntPtr = Token Handle. Usually retrieved through OpenThreadToken TokenInformationClass = uint = Enum of token information. Usually 1, which is TokenUser (SID) value. TokenInformation = IntPtr = Pointer to output buffer that will be populated by the API. TokenInformationLength = int = Size of output buffer ReturnLength = out int = Output length.

So this winAPI need to be called twice. It's because we don't know required size of the buffer.

ex)

[DllImport("advapi32.dll", SetLastError = true)]
static extern bool GetTokenInformation(IntPtr TokenHandle, uint TokenInformationClass, IntPtr TokenInformation, int TokenInformationLength, out int ReturnLength);

int TokenInfLength = 0;

// Calling first time, because we don't know TokenInformationLength
GetTokenInformation(hToken, 1, IntPtr.Zero, TokenInfLength, out TokenInfLength);

// Calling second time, because we now KNOW TokenInformationLength. Allocate memory of TokenInformation with the length of TokenInformationLength, and call again.
IntPtr TokenInformation = Marshal.AllocHGlobal((IntPtr)TokenInfLength);
GetTokenInformation(hToken, 1, TokenInformation, TokenInfLength, out TokenInfLength);

ConvertSidToStringSid - Convert binary SID to string SID

BOOL ConvertSidToStringSidW(
 PSID Sid,
 LPWSTR *StringSid
);

Sid = IntPtr = Pointer to the SID. It's inside output buffer from GetTokenInformation. This needs to be extracted from a structure. StringSid = out IntPtr = Output string that will contain the SID string.

Example

// Setting structure of SID. Used for SID extraction later on. 
[StructLayout(LayoutKind.Sequential)]
public struct SID_AND_ATTRIBUTES
{
 public IntPtr Sid;
 public int Attributes;
}
public struct TOKEN_USER
{
 public SID_AND_ATTRIBUTES User;
}

[DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool ConvertSidToStringSid(IntPtr pSID, out IntPtr ptrSid);

// Extracting SID(TokenUser) from the TokenInformation structure 
TOKEN_USER TokenUser = (TOKEN_USER)Marshal.PtrToStructure(TokenInformation, typeof(TOKEN_USER));

// Pointer to contain output SID string 
IntPtr pstr = IntPtr.zero;
Boolean ok = ConvertSidToStringSid(TokenUser.User.Sid, out pstr);

// Converting pointer to actual string 
string sidstr = Marshal.PtrToStringAuto(pstr);
Console.WriteLine(@"[+] Sid: ", sidstr); 

Last updated