Program escalates itself from admin to system - c

I wrote a program that needs to be running as SYSTEM. I add the linker option 'UAC Execution Level' to 'requireAdministrator' and it pops the UAC like it should but now I need to escalate from admin to SYSTEM how can I do that?
I thought about opening the program's token and inject it the SYSTEM token but it is not legit way. How can I do it neatly because I know once you admin you can be SYSTEM.

Write a windows service. It will run as SYSTEM user by default.
A simple tutorial is here, but remember that installing a service requires administrator privileges.

Once you're running as administrator, here are some options:
If you know you're not going to be using GUI functions, you can create a scheduled task that runs your same exe as NT AUTHORITY\SYSTEM within a few seconds, and check within your code which account the process is running as.
Copied from a project of mine and slightly modified:
#include <Windows.h>
#include <WinNls.h>
#include <shobjidl.h>
#include <objbase.h>
#include <ObjIdl.h>
#include <ShlGuid.h>
#include <taskschd.h>
#include <comdef.h>
#include <strsafe.h>
#pragma comment(lib, "taskschd.lib")
#pragma comment(lib, "comsupp.lib")
HRESULT WINAPI CreateSchedTask(WCHAR *wszExePath)
{
ITaskService *pService = NULL;
ITaskFolder *pRoot = NULL;
ITaskDefinition *pTask = NULL;
ITaskSettings *pSettings = NULL;
IRegistrationInfo *pInfo = NULL;
ITriggerCollection *pCollection = NULL;
ITrigger *pTrigger = NULL;
ITimeTrigger *pTime = NULL;
IPrincipal *pPrincipal = NULL;
IActionCollection *pActionCollection = NULL;
IAction *pAction = NULL;
IExecAction *pExecAction = NULL;
IRegisteredTask *pRegTask = NULL;
SYSTEMTIME stNow;
FILETIME ftStart, ftEnd;
ULARGE_INTEGER ulWork;
WCHAR wFmt[100];
VARIANT vBlank = _variant_t();
if (FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)))
{
return E_FAIL;
}
//CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL);
if (FAILED(CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, (LPVOID *) &pService)))
{
return E_FAIL;
}
pService->Connect(vBlank, vBlank, vBlank, vBlank);
pService->GetFolder(SysAllocString(L"\\"), &pRoot);
pService->NewTask(0, &pTask);
pService->Release();
pTask->get_RegistrationInfo(&pInfo);
pInfo->put_Author(SysAllocString(L"TASKNAMEHERE"));
pInfo->Release();
pTask->get_Settings(&pSettings);
pSettings->put_StartWhenAvailable(VARIANT_TRUE);
pSettings->put_Enabled(VARIANT_TRUE);
pSettings->Release();
pTask->get_Triggers(&pCollection);
pCollection->Create(TASK_TRIGGER_TIME, &pTrigger);
pCollection->Release();
pTrigger->QueryInterface(IID_ITimeTrigger, (LPVOID *)&pTime);
GetLocalTime(&stNow);
SystemTimeToFileTime(&stNow, &ftStart);
ulWork.HighPart = ftStart.dwHighDateTime;
ulWork.LowPart = ftStart.dwLowDateTime;
//20000000000
ulWork.QuadPart += 300000000UI64;
ftStart.dwHighDateTime = ulWork.HighPart;
ftStart.dwLowDateTime = ulWork.LowPart;
FileTimeToSystemTime(&ftStart, &stNow);
// Note: replace -07:00 with the appropriate UTC offset for your time zone
StringCchPrintfW(wFmt, 100, L"%.4hu-%.2hu-%.2huT%.2hu:%.2hu:%.2hu-07:00", stNow.wYear, stNow.wMonth, stNow.wDay, stNow.wHour, stNow.wMinute, stNow.wSecond);
pTime->put_StartBoundary(SysAllocString(wFmt));
ulWork.QuadPart += 900000000UI64;
ftEnd.dwLowDateTime = ulWork.LowPart;
ftEnd.dwHighDateTime = ulWork.HighPart;
FileTimeToSystemTime(&ftEnd, &stNow);
StringCchPrintfW(wFmt, 100, L"%.4hu-%.2hu-%.2huT%.2hu:%.2hu:%.2hu-07:00", stNow.wYear, stNow.wMonth, stNow.wDay, stNow.wHour, stNow.wMinute, stNow.wSecond);
pTime->put_EndBoundary(SysAllocString(wFmt));
pTime->put_Id(SysAllocString(L"TimeTrigger"));
pTime->Release();
pTask->get_Actions(&pActionCollection);
pActionCollection->Create(TASK_ACTION_EXEC, &pAction);
pActionCollection->Release();
pAction->QueryInterface(IID_IExecAction, (LPVOID *)&pExecAction);
pAction->Release();
pExecAction->put_Path(SysAllocString(wszExePath));
pExecAction->Release();
pTask->get_Principal(&pPrincipal);
pPrincipal->put_RunLevel(TASK_RUNLEVEL_HIGHEST);
pPrincipal->put_LogonType(TASK_LOGON_SERVICE_ACCOUNT);
pTask->put_Principal(pPrincipal);
pPrincipal->Release();
pRoot->RegisterTaskDefinition(
SysAllocString(L"System Elevation"),
pTask, TASK_CREATE_OR_UPDATE,
_variant_t(L"NT AUTHORITY\\SYSTEM"),
_variant_t(), TASK_LOGON_SERVICE_ACCOUNT,
_variant_t(L""), &pRegTask);
pRoot->Release();
pTask->Release();
pRegTask->Release();
CoUninitialize();
return S_OK;
}
INT APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, INT nShowCmd)
{
WCHAR wUsername[100], wExePath[MAX_PATH];
GetEnvironmentVariableW(L"USERNAME", wUsername, 100);
if (!wcschr(wUsername, L'$'))
{
GetModuleFileNameW(hInstance, wExePath, MAX_PATH);
CreateSchedTask(wExePath);
}
else
{
// NOTE: MessageBox and other GUI functions won't work since the process isn't running in winsta0\default
// File I/O instead
HANDLE hLog = CreateFileW(L"C:\\Temp\\Log.txt", GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwWritten;
UINT uLen;
CHAR szBuf[100];
SetFilePointer(hLog, 0, NULL, FILE_END);
StringCchPrintfA(szBuf, 100, "Hello from %S\r\n", wUsername);
StringCbLengthA(szBuf, 100, &uLen);
WriteFile(hLog, szBuf, uLen, &dwWritten, NULL);
CloseHandle(hLog);
}
return 0;
}
Use the Windows Process API to elevate to System. I don't have much in the way of example code for this, but you can look at PAExec, an open source alternative to SysInternals PSExec tool, that allows creating new interactive processes as System.
The idiomatic way of doing this on Windows, which is create a Windows service.

Related

Execute PE from memory

I tried to write LoadLibrary function in C.
The function gets a path to a DLL file which, normally, pops up message box when it loads (I tried to run that DLL file using the original LoadLibrary function and it works).
Basically, the DLL content is loaded into a buffer, parsed and runs from entry point.
In VirtualAllocEx function, I use PAGE_READWRITE protection mode. Then, when running the line f(nth->OptionalHeader.ImageBase, DLL_PROCESS_ATTACH, NULL), I get the following error message: Exception thrown at 0x10011032 in PE.exe: 0xC0000005: Access violation executing location 0x10011032. (0x10011032 is entry point address).
If I change the mode to PAGE_EXECUTE_READWRITE, the error message is: Exception thrown at 0x00019644 in PE.exe: 0xC0000005: Access violation executing location 0x00019644. (No idea what is that address).
I think that it's clear why it's not smart to allow execution in all sectors of the PE, but I did it for testing purposes only. In the final code, I'll need to write it properly.
My code is attached.
(BTW, if you have other suggestions that are not related to my question - I'll glad to know).
#include <Windows.h>
typedef HMODULE func(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);
HMODULE LoadLibraryFromMem(char* dllPath)
{
DWORD read;
HANDLE handle;
handle = CreateFileA(dllPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD size = GetFileSize(handle, NULL);
PVOID vDll = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
BOOL r = ReadFile(handle, vDll, size, &read, NULL);
CloseHandle(handle);
PIMAGE_DOS_HEADER dosh = (PIMAGE_DOS_HEADER)vDll;
PIMAGE_NT_HEADERS nth = (PIMAGE_NT_HEADERS)((PBYTE)vDll + dosh->e_lfanew);
handle = GetCurrentProcess();
PVOID vImg = VirtualAllocEx(
handle,
nth->OptionalHeader.ImageBase,
nth->OptionalHeader.SizeOfImage,
MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE
); // HERE --> PAGE_EXECUTE_READWRITE
WriteProcessMemory(
handle,
vImg,
vDll,
nth->OptionalHeader.SizeOfHeaders,
0
);
PIMAGE_SECTION_HEADER sech = IMAGE_FIRST_SECTION(nth);
for (size_t i = 0; i < nth->FileHeader.NumberOfSections; i++)
WriteProcessMemory(
handle,
(PBYTE)vImg + sech[i].VirtualAddress,
(PBYTE)vDll + sech[i].PointerToRawData,
sech[i].SizeOfRawData,
0
);
PIMAGE_IMPORT_DESCRIPTOR impd = nth->OptionalHeader.ImageBase + nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
HANDLE proc;
while (((PIMAGE_IMPORT_DESCRIPTOR)impd)->Name)
{
LPSTR dllName = (nth->OptionalHeader.ImageBase + ((PIMAGE_IMPORT_DESCRIPTOR)impd)->Name);
HMODULE dllAdr = LoadLibraryA(dllName);
PDWORD iat = nth->OptionalHeader.ImageBase + ((PIMAGE_IMPORT_DESCRIPTOR)impd)->FirstThunk;
while (*iat)
{
LPSTR funcName = ((PIMAGE_IMPORT_BY_NAME)(nth->OptionalHeader.ImageBase + *iat))->Name;
proc = GetProcAddress(dllAdr, funcName);
if (!proc)
return NULL;
impd->FirstThunk = proc;
iat++;
}
impd++;
}
func* f = (func*)(nth->OptionalHeader.ImageBase + nth->OptionalHeader.AddressOfEntryPoint);
f(nth->OptionalHeader.ImageBase, DLL_PROCESS_ATTACH, NULL);
}
int main()
{
LoadLibraryFromMem("mydll.dll");
return 0;
}
Thanks in advance!

C Windows DLL Injection Notepad crashes

i have tryed a DLL Injection on the Programm Notepad.exe
But if i start my Injector, notepad Crashes.
Here is my Injector Code:
#include <windows.h>
#include <stdio.h>
char const Path[]="C:\\Users\\IEUser\\Desktop\\Mydll.dll";
int main(void) {
HANDLE hWnd, hProcess, AllocAdresse, hRemoteThread;
DWORD PID;
hWnd = FindWindow(0,"Untitled - Notepad");
GetWindowThreadProcessId((HWND)hWnd, &PID);
hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, PID);
AllocAdresse = VirtualAllocEx(hProcess, 0, sizeof(Path), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, (void*)AllocAdresse, (void*)Path, sizeof(Path), 0);
hRemoteThread=CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle("kernel32.dll"),"LoadLibraryA"), AllocAdresse, 0, 0);
WaitForSingleObject(hRemoteThread, INFINITE);
VirtualFreeEx(hProcess, AllocAdresse, sizeof(Path), MEM_DECOMMIT);
CloseHandle(hProcess);
return 0;
}
And this is my Code for my DLL File:
#include <windows.h>
#include <stdio.h>
void InjNachricht() {
MessageBox(0, "It Works", "My DLL File", 0);
}
int WINAPI DllMain(HINSTANCE hInst,DWORD reason,LPVOID reserved) {
if(reason==DLL_PROCESS_ATTACH)
CreateThread(0, 0, (LPTHREAD_START_ROUTINE) InjNachricht, 0, 0, 0);
return 0;
}
I compile this Code in my Linux machine with MinGW:
(Injector) i686-w64-mingw32-gcc -o Injector.exe injector.c
(DLL-File) i686-w64-mingw32-gcc -o Mydll.dll mydll.c
I also written a Function for Setting Debug Privileges:
void SetDebugPrivilege() {
HANDLE hProcess=GetCurrentProcess(), hToken;
TOKEN_PRIVILEGES priv;
LUID luid;
OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken);
LookupPrivilegeValue(0, "seDebugPrivilege", &luid);
priv.PrivilegeCount = 1;
priv.Privileges[0].Luid = luid;
priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, false, &priv, 0, 0, 0);
CloseHandle(hToken);
}
And if i Run my Program in my Virtual Machine:
Notepad.exe Crash
Why is notepad crashing?
If i inject my dll File with the Program it works:
enter image description here
And please do not come with me now "Then I use the program instead of writing a separate injector" !! That does not help me any further !!
Its been a while since I did these things, so I may be off, but:
You try to map LoadLibraryA method when you run remote thread. Most of modern apps use LoadLibraryW or just LoadLibrary that will use the default value based on compiler mode.
You use gcc, why not use Microsoft compiler? There might be mapping issues between the compilers, that prevent you from linking the gcc generated code with (possibly) vcc compiler....
Hop it helps
Firstly, you can use strlen/wcslen (first is for Ascii encoding, latter for Unicode encoding) instead for calculating the length of a buffer. It's more appropriate in my opinion.
Here's a properly working variant of DLL injection via remote threads which I've written as a demonstration example for you. It is a quick example so don't expect too much, extremely simplistic. You can improve it by using shell-code injection and then utilise a manual map loader or LdrLoadDll.
BOOLEAN InjectDll(
HANDLE ProcessHandle,
CHAR *DllPath
)
{
BOOLEAN BlStatus = FALSE;
HANDLE ThreadHandle = 0;
PVOID LoadLibraryAddress = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
PVOID DllMemory = 0;
SIZE_T DllLength = strlen(DllPath);
if (!ProcessHandle ||
!DllPath ||
!LoadLibraryAddress)
{
return FALSE;
}
DllMemory = VirtualAllocEx(ProcessHandle,
NULL,
DllLength,
MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE);
if (!DllMemory)
{
return FALSE;
}
BlStatus = WriteProcessMemory(ProcessHandle,
DllMemory,
DllPath,
DllLength,
NULL);
if (!BlStatus)
{
goto cleanup;
}
ThreadHandle = CreateRemoteThread(ProcessHandle,
NULL,
0,
(LPTHREAD_START_ROUTINE)LoadLibraryAddress,
DllMemory,
0,
0);
cleanup:
if (!ThreadHandle)
{
if (DllMemory)
{
VirtualFree(DllMemory,
NULL,
MEM_RELEASE);
}
BlStatus = FALSE;
}
else
{
BlStatus = TRUE;
}
return BlStatus;
}
On that note, you may be interested in NtOpenProcess, NtAllocateVirtualMemory, NtWriteVirtualMemory, RtlCreateUserThread/NtCreateThreadEx and NtAdjustPrivilegesToken. As for CreateRemoteThread, it won't work with processes on other user accounts, whereas RtlCreateUserThread/NtCreateThreadEx both will (as long as you have debugging rights - SeDebugPrivilege).
As a last pointer, make sure you compile with /MT so the run-time is statically linked (especially for the DLL you're injecting). If my example code does not help you, and you still cannot fix the issue, try using a debugger to diagnose the issue. You should already have tried doing this, debuggers are there for a reason!

Creating a file and writing something into it does not work how I wanted it

I write the following code:
#include <windows.h>
#include "stdbool.h"
#include <winuser.h>
#include <WinDef.h>
#include <Winerror.h>
#include <stdio.h>
#include <Strsafe.h>
#define MAX_SIZE 100
void DisplayError(LPTSTR lpszFunction)
// Routine Description:
// Retrieve and output the system error message for the last-error code
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL );
lpDisplayBuf =
(LPVOID)LocalAlloc( LMEM_ZEROINIT,
( lstrlen((LPCTSTR)lpMsgBuf)
+ lstrlen((LPCTSTR)lpszFunction)
+ 40) // account for format string
* sizeof(TCHAR) );
if (FAILED( StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error code %d as follows:\n%s"),
lpszFunction,
dw,
lpMsgBuf)))
{
printf("FATAL ERROR: Unable to output error code.\n");
}
printf(TEXT("ERROR: %s\n"), (LPCTSTR)lpDisplayBuf);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}
int main(){
//parameters of CreateFile()
HANDLE hFile;
LPCTSTR lpFileName;
DWORD dwDesiredAccess;
DWORD dwShareMode;
LPSECURITY_ATTRIBUTES lpSecurityAttributes;
DWORD dwCreationDisposition;
DWORD dwFlagsAndAttributes;
HANDLE hTemplateFile;
//parameters of WriteFile()
DWORD nNumberOfBytesToWrite;
DWORD numberOfBytesWritten;
LPOVERLAPPED lpOverlapped;
char DataBuffer[MAX_SIZE];
//others
BOOL bErrorFlag;
//initialize args of CreateFile()
lpFileName = "C:\\file.txt";
dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
dwShareMode = 0;
lpSecurityAttributes = NULL;
dwCreationDisposition = CREATE_NEW;
dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
hTemplateFile = NULL;
//initialize args of WriteFile()
strcpy(DataBuffer, "This is the test file");
nNumberOfBytesToWrite = (DWORD)strlen(DataBuffer);
numberOfBytesWritten = 0;
lpOverlapped = NULL;
hFile = CreateFile(lpFileName, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile);
if (hFile == INVALID_HANDLE_VALUE)
{
DisplayError(TEXT("CreateFile"));
printf(TEXT("Terminal failure: Unable to open file \"%s\" for write.\n"), lpFileName);
return;
}
printf(TEXT("Writing %d bytes to %s.\n"), nNumberOfBytesToWrite, lpFileName);
bErrorFlag = WriteFile(hFile, DataBuffer, nNumberOfBytesToWrite,
&numberOfBytesWritten, lpOverlapped);
if (FALSE == bErrorFlag)
{
DisplayError(TEXT("WriteFile"));
printf("Terminal failure: Unable to write to file.\n");
}
else
{
if (numberOfBytesWritten != nNumberOfBytesToWrite)
{
// This is an error because a synchronous write that results in
// success (WriteFile returns TRUE) should write all data as
// requested. This would not necessarily be the case for
// asynchronous writes.
printf("Error: dwBytesWritten != dwBytesToWrite\n");
}
else
{
printf(TEXT("Wrote %d bytes to %s successfully.\n"), numberOfBytesWritten, lpFileName);
}
}
CloseHandle(hFile);
}
So, as you can see. A program that should create a file named file.txt to the desktop and write a little text into it.
I use Microsoft Visual C++ Express, it compiles without errors..but when i let run it by clicking the green play-button, then I see not such a file created on my desktop.
By searching my possible faults, I have also read on https://msdn.microsoft.com/en-us/library/windows/desktop/bb540534%28v=vs.85%29.aspx that they use nearly the same code. Except that I do not include the displaying error parts.
So, my question: What could be the reason why it does not work?
Do I (the program) need some extra permissions to do that?
For example, I wrote the same in Ubuntu with open() & write() except that I use "/tmp/file.txt" as destionation directory. And it works without additional permissions.
best regards,
The code is using the wrong path to your Desktop directory.
For Windows 7 (and most versions of windows) the path would be:
be sure to use two backslashs at each directory level
C:\Users\yourUsername\Desktop
This is the wrong way to write the filename
"C:\Desktop\file.txt";
you need to escape the '\', the \f is actually a escape sequence
"C:\\Desktop\\file.txt";
and also, the file will not be created on your desktop apparently, try this instead
"C:\\file.txt";
and check your C: drive to see the file.

Setting a created file permissions

I would like to know how can I set the permissions of a windows file?
Something like chmod(), instead it's a windows.
For example:
Create the file example.exe, and set its permissions in a way that only the owner
of this file can execute it.
I read that there's an ACL API for c somewhere, but I didn't quite get it.
It's a lot more work than chmod!
I have taken the liberty of creating the file AFTER creating the security descriptor - it is safer. If you do things the other way around (create the file first) then there is a short time when the required access is not set.
Try this:
#include <windows.h>
#include <AclAPI.h>
#include <Lmcons.h>
int main()
{
SECURITY_DESCRIPTOR sd;
EXPLICIT_ACCESS ea[1];
PACL pDacl;
SECURITY_ATTRIBUTES sa;
TCHAR UserBuffer[UNLEN+1];
DWORD ulen = UNLEN;
GetUserName(UserBuffer, &ulen);
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
BuildExplicitAccessWithName(&ea[0], UserBuffer, GENERIC_EXECUTE,
SET_ACCESS, NO_INHERITANCE);
SetEntriesInAcl(1, ea, NULL, &pDacl);
SetSecurityDescriptorDacl(&sd, TRUE, pDacl, FALSE);
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = FALSE;
sa.lpSecurityDescriptor = &sd;
CreateFileA("c:\\temp\\example.exe", GENERIC_EXECUTE, 0, &sa,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
return 0;
}

CreateProcess problems when using PROC_THREAD_ATTRIBUTE_PREFERRED_NODE or PROC_THREAD_ATTRIBUTE_GROUP_AFFINITY

I keep getting error 87, ERROR_INVALID_PARAMETERS when I call CreateProcess and use the PROC_THREAD_ATTRIBUTE_GROUP_AFFINITY extended attribute. I use the exact same code to call CreateRemoteThreadEx, and that works fine. Also, PROC_THREAD_ATTRIBUTE_PREFERRED_NODE seems to have no effect. So what am I doing wrong!?
Microsoft Windows Server 2008 R2 Enterprise, 6.1.7601 SP1 Build 7601
I even installed this service pack: A child process cannot be created by calling a CreateProcess function that uses the PROC_THREAD_ATTRIBUTE_PREFERRED_NODE parameter in Windows 7 or in Windows Server 2008 R2
Here is example code:
#include <windows.h>
typedef unsigned __int64 QWORD;
class CErr {
public:
CErr(LPCSTR szFunc, DWORD nErr) {
char szBuf[0x10000];
DWORD fFlags = FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_FROM_SYSTEM;
DWORD fLang = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
if (!nErr)
nErr = GetLastError();
FormatMessage(fFlags, NULL, nErr, fLang, szBuf, sizeof(szBuf) - 1, NULL);
printf("%s: %s", szFunc, szBuf);
}
};
int main(int argc, char* argv[])
{
DWORD nErr;
size_t cb;
char sAttribsBuf[4096];
auto pAttribs = (PPROC_THREAD_ATTRIBUTE_LIST)sAttribsBuf;
if (!InitializeProcThreadAttributeList(NULL, 1, 0, &cb)
&& ((nErr = GetLastError()) != ERROR_INSUFFICIENT_BUFFER))
throw CErr("InitializeProcThreadAttributeList", nErr);
if (!InitializeProcThreadAttributeList(pAttribs, 1, 0, &cb))
throw CErr("InitializeProcThreadAttributeList", 0);
#if 1 // if enabled, CreateProcess succeeds, but doesn't set affinity
WORD iNuma = 1; // WORD is the only size that does not error here
if (!UpdateProcThreadAttribute(pAttribs, 0, PROC_THREAD_ATTRIBUTE_PREFERRED_NODE,
&iNuma, sizeof(iNuma), NULL, NULL))
throw CErr("UpdateProcThreadAttribute", 0);
#else // if enabled, CreateProcess fails with ERROR_INVALID_PARAMETERS(87)
GROUP_AFFINITY GrpAffinity = { 0 };
GrpAffinity.Mask = 1;
if (!UpdateProcThreadAttribute(pAttribs, 0, PROC_THREAD_ATTRIBUTE_GROUP_AFFINITY,
&GrpAffinity, sizeof(GrpAffinity), NULL, NULL))
throw CErr("UpdateProcThreadAttribute", 0);
#endif
auto fCreationFlags = EXTENDED_STARTUPINFO_PRESENT;
PROCESS_INFORMATION pi = { 0 };
STARTUPINFOEX si = { 0 };
si.StartupInfo.cb = sizeof(si);
si.lpAttributeList = pAttribs;
if (!CreateProcess(NULL, "notepad.exe", NULL, NULL, false, fCreationFlags,
NULL, NULL, &si.StartupInfo, &pi))
throw CErr("CreateProcess", 0); // error if ...ATTRIBUTE_GROUP_AFFINITY
// SetProcessAffinityMask(pi.hProcess,1); // if enabled, notepad's affinity is set
WaitForSingleObject(pi.hProcess, INFINITE);
DeleteProcThreadAttributeList(pAttribs);
return 0;
}
It is not clear from the documentation, but I think I figured it out. PROC_THREAD_ATTRIBUTE_PREFERRED_NODE is only supposed to be used with CreateProcess(). PROC_THREAD_ATTRIBUTE_IDEAL_PROCESSOR and PROC_THREAD_ATTRIBUTE_GROUP_AFFINITY are only supposed to be used with CreateThread().
PROC_THREAD_ATTRIBUTE_PREFERRED_NODE might be setting the affinity of the process to all the processors in the same GROUP as the node. I can't verify it, since my test system only has 12 cores on two numa nodes. Setting PROC_THREAD_ATTRIBUTE_PREFERRED_NODE to 0 or to 1 sets the affinity to all the cores. I did verify that the stack of the process created by CreateProcess is located on the numa node indicated by PROC_THREAD_ATTRIBUTE_PREFERRED_NODE. Also not documented, the size of the node being passed in must be 2 bytes.

Resources