original notes from obsidian
WinAPI Lists
Contains most used WinAPI - links, functionality, parameters, returns, interesting remarks, and personal notes
Process Injection Related WINAPI
LPVOID VirtualAlloc(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);LPVOID = Memory Pointer = LongPtr
lpAddress = Memory allocation address
0 = API choose the location automatically
dwSize = Size of the allocation
flAllocationType = Memory allocation type
Usually MEM_COMMIT | MEM_RESERVE =
0x3000
flProtect = Memory protection flags
Usually
0x40= RWXBut 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
outkeyword 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();Token Related WINAPI
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