Detecting user name from process ID - c++

Ho can i get user account name, that ran the process with specified id. Is there any api function for this?
I am using windows,c++.

There is not an API function that does this directly, however you can combine a few API calls to do this. Of course your program will need to satisfy any ACLs that are applied to the process that you are interested in examining.
First, given the process ID, you'll need to open a handle to the process. You can use OpenProcess for that, requesting the PROCESS_QUERY_INFORMATION access right.
Once you have that handle, you can call OpenProcessToken, requesting the TOKEN_QUERY access right.
Finally, you can then call GetTokenInformation, requesting the TokenUser information class, which will give you the user account of the token. This information is provided to you in the form of a SID. To convert the SID to the actual name of the account, you can call LookupAccountSid.
Don't forget to call CloseHandle on both the process handle and the token handle once you're finished with them.

And this is function using Aaron Klotz algorithm:
public static string GetProcessOwnerByID(int processId)
{
IntPtr processHandle = IntPtr.Zero;
IntPtr tokenHandle = IntPtr.Zero;
try
{
processHandle = OpenProcess(PROCESS_QUERY_INFORMATION, false, processId);
if (processHandle == IntPtr.Zero)
return "NO ACCESS";
OpenProcessToken(processHandle, TOKEN_QUERY, out tokenHandle);
using (WindowsIdentity wi = new WindowsIdentity(tokenHandle))
{
string user = wi.Name;
return user.Contains(#"\") ? user.Substring(user.IndexOf(#"\") + 1) : user;
}
}
finally
{
if (tokenHandle != IntPtr.Zero) CloseHandle(tokenHandle);
if (processHandle != IntPtr.Zero) CloseHandle(processHandle);
}
}
Whole function (with constants) can be found on GitHub gist

Related

Can I use the AccessibleObjectFromWindow() function to retrieve the address of the interface for the Outlook window object?

I am trying to resolve one bug with AccessibleObjectFromWindow().
I am unable to retrieve ppvObject for Outlook, whereas it works well for MSWord.
WindowPtr pdocWdw;
HRESULT var = AccessibleObjectFromWindow( hControlWnd, DWORD(OBJID_NATIVEOM),
__uuidof(WindowPtr), (PVOID*)&pdocWdw );
_DocumentPtr pDoc;
if(pdocWdw != NULL)
{
m_pdocWdw = pdocWdw;
pDoc = pdocWdw->Document;
}
For Word, pDoc = pdocWdw->Document; returns the proper value, but in case of Outlook it throws an exception.
Can anybody please tell if I can use AccessibleObjectFromWindow() for Outlook? If not, what alternative can I use?

Using GetFunctionInfo to get the name of a function from a FunctionID

I am writing a CLR profiler using the ICorProfilerInfo interface.
When using SetEnterLeaveFunctionHooks, the callback methods are passed a FunctionID.
How do I get the metadata (I am after the name in particular), of a function given this FunctionID?
An MSDN article suggests that the first call should be to GetFunctionInfo. The documentation for this function states:
The profiler code can call ICorProfilerInfo::GetModuleMetaData to obtain a metadata interface for a given module. The metadata token that is returned to the location referenced by pToken can then be used to access the metadata for the function.
It does not elaborate on the last sentence ('the metadata token ... can be used to access metadata for the function').
How does this work?
So far, I am doing the following:
void MyProfiler::EnterMethod(FunctionID functionID)
{
ClassID classId = 0;
ModuleID moduleId = 0;
mdToken metaDataToken = 0;
IMetaDataImport* metaDataImport = NULL;
// (m_info is ICorProfilerInfo3)
m_info->GetFunctionInfo(functionID, &classId, &moduleId, &metaDataToken);
m_info->GetModuleMetaData(moduleId, ofRead, IID_IMetaDataImport, (IUnknown**)&metaDataImport);
// What goes here?
}
I have tried to call GetTypeRefProps like this:
mdToken ptkResolutionScope;
WCHAR szName[1024];
ULONG cchName = 1024;
ULONG pchName;
HRESULT result = MetaDataImport->GetTypeRefProps(pToken, &ptkResolutionScope, szName, cchName, &pchName);
The final call returns S_FALSE, and does not populate szName.
GetTypeRefProps is only applicable when your token is a TypeRef token, GetFunctionInfo will give you a MethodDef token which requires you to use the GetMethodProps method.
metaDataImport->GetMethodProps(metaDataToken, NULL, szName, cchName, &pchName, NULL, NULL, NULL, NULL, NULL);

Query cached ACL of a directory for a disconnected Windows domain user via Authz API

I would like to explore the effective rights of a local directory for the currently logged on domain user. This link has given me a good starting point and works as expected.
Unfortunately, querying the ACL fails when I disconnect from the domain controller and log on with my cached domain user profile. When being confronted with this environment, the AuthzInitializeContextFromSid function in the example code returns FALSE and sets 1355 (ERROR_NO_SUCH_DOMAIN) as the last error code:
...
PSID pSid = NULL;
BOOL bResult = FALSE;
LUID unusedId = { 0 };
AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzClientContext = NULL;
pSid = ConvertNameToBinarySid(lpszUserName);
if (pSid != NULL)
{
bResult = AuthzInitializeContextFromSid(0,
pSid,
hManager,
NULL,
unusedId,
NULL,
&hAuthzClientContext);
if (bResult)
{
GetAccess(hAuthzClientContext, psd);
AuthzFreeContext(hAuthzClientContext);
}
else
{
//prints 1355 as last error code
wprintf_s(_T("AuthzInitializeContextFromSid failed with %d\n"), GetLastError());
}
...
}
Is it possible to query the (cached) ACL nonetheless? Surely they must be accessible some way or another, even if the domain controller is unreachable.

Registry: Delete key for each local user within system

I got a certain registry key (created by our software) which needs to be removed on each local user account at some point.
Thus, I try to load the users hive and then use SHDeleteKey (as the key is not empty) to get the job done.
However, SHDeleteKey always returns LSTATUS 2 (ERROR_FILE_NOT_FOUND).
The Registry key for each user is placed under
HKCU\Software\XYZ
First, I set the required privileges within my code, which seems to work (return val is TRUE):
(...)
HANDLE th;
LUID rsto;
LUID bckp;
TOKEN_PRIVILEGES tp;
TOKEN_PRIVILEGES tp2;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &th);
LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &rsto);
LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &bckp);
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
tp.Privileges[0].Luid = rsto;
tp2.PrivilegeCount = 1;
tp2.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
tp2.Privileges[0].Luid = bckp;
BOOL p = AdjustTokenPrivileges(th, 0, &tp, 1024, 0, 0);
BOOL p2 = AdjustTokenPrivileges(th, 0, &tp2, 1024, 0, 0);
(...)
Then I use RegloadKey to load the users hive.
std::string connection contains the path to the respective ntuser.dat file. username is the local user account name.
So the hive should be loaded under HKEY_USERS\username:
(...)
DWORD result = RegLoadKey(HKEY_USERS, username.c_str(), connection.c_str());
return result == ERROR_SUCCESS;
(...)
Now, I try to delete:
(...)
k = username + "\\Software\\XYZ";
result = SHDeleteKey(HKEY_USERS, k.c_str());
And now result has value of 2.
But the key exists.
What am I doing wrong?
Thank you in advance...
UPDATED INFO:
I realized the problem needs to be somewhere on RegLoadKey.
When I load the hive via command line (REG.exe load "HKU\username" ...), I can see the node "username" under HKEY_USERS within regedit.exe. All child nodes are loaded under that node.
When I pause my program after RegLoadKey, the node "username" is also shown under HKEY_USERS, but the node is visualized as empty, so no child nodes are available.
How can this happen? This problem is driving me nuts.
Looked at my code again today and I just saw that I loaded "ntuser.BAT" instead of "ntuser.DAT" (both files exist).
I'm really sorry that I wasted your time. :-/

Programmatically allow write access for a Registry key

I need to programmatically modify the Access Descriptors on a known Registry key during product installation. The way I want it to work is:
The installer is run in Administrative mode.
A Registry key is created.
A function (the one I need) queries the ACL from the key.
If this function finds that the group 'Users' already has write access, nothing should be done.
If not, it should add a new permission allowing write access to the 'Users' group.
The permissions are saved for the Registry key.
This question is similar to Setting Registry key write permissions using .NET, however, I need a C++/Win32 implementation.
Thanks in advance
For getting and setting the ACL of the key you need to use RegGetKeySecurity and RegSetKeySecurity. Then you need to iterate through the ACEs, examining any that apply to the "Users" group SID. Then you'll either modify/remove the existing one and/or add a new one. Be advised that working with ACLs in plain old Win32 C is a pain.
The smallest code to grant access consists of 3 API calls. It gives full access to the given hkey for all authenticated users and administrators.
This snippet does not contain proper error handling and reporting. Do not copy/paste it into the production code.
PSECURITY_DESCRIPTOR sd = nullptr;
ULONG sd_size = 0;
TCHAR* rights = TEXT( "D:" ) // Discretionary ACL
TEXT( "(A;OICI;GA;;;AU)" ) // Allow full control to all authenticated users
TEXT( "(A;OICI;GA;;;BA)" ); // Allow full control to administrators
ConvertStringSecurityDescriptorToSecurityDescriptor( rights, SDDL_REVISION_1, &sd, &sd_size );
RegSetKeySecurity( hkey, DACL_SECURITY_INFORMATION, sd );
LocalFree( sd );
Detecting if "Users" have write access to the key might be more difficult than expected. I ended up with writing a test value to the registry and checking the result of that write.
Just to expand on Mikhail Vorotilov's answer, and also drawing inspiration from the example code at
https://learn.microsoft.com/en-us/windows/win32/secbp/creating-a-dacl
bool RegistryGrantAll(HKEY hKey)
{
bool bResult = false;
PSECURITY_DESCRIPTOR sd = nullptr;
const TCHAR* szSD =
TEXT("D:") // Discretionary ACL
TEXT("(D;OICI;KA;;;BG)") // Deny access to built-in guests
TEXT("(D;OICI;KA;;;AN)") // Deny access to anonymous logon
TEXT("(A;OICI;KRKW;;;AU)") // Allow KEY_READ and KEY_WRITE to authenticated users ("AU")
TEXT("(A;OICI;KA;;;BA)"); // Allow KEY_ALL_ACCESS to administrators ("BA" = Built-in Administrators)
if (ConvertStringSecurityDescriptorToSecurityDescriptor((LPCTSTR)szSD, SDDL_REVISION_1, &sd, 0))
{
auto result = RegSetKeySecurity(hKey, DACL_SECURITY_INFORMATION, sd);
if (ERROR_SUCCESS == result)
bResult = true;
else
SetLastError(result);
// Free the memory allocated for the SECURITY_DESCRIPTOR.
LocalFree(sd);
}
return bResult;
}
If the function returns false then call GetLastError() for more information on the failure cause.
Code compiles on VS2019 and appears to work.
I have not added code to check that hKey is a valid registry handle.
Edit: I've edited this a few times following testing. Sorry about all the edits. What I ended up with was far closer to Mikhail's answer than I started with.
Links to further info:
https://learn.microsoft.com/en-us/windows/win32/secbp/creating-a-dacl
https://learn.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertstringsecuritydescriptortosecuritydescriptorw
https://learn.microsoft.com/en-us/windows/win32/secauthz/ace-strings
Sup, hope OP is still interested in the answer. Here is the working code adding ACEs to ACLs, it may be used to add ACEs to registry or filesystem DACLs. I haven't tried it with anything else yet. As you may notice, no nasty RegGetKeySecurity or manual ACL composing needed. There's even no need to RegOpenKeyEx. For more info, check this MS doc.
UPD Of course it will need admin rights for execution.
// sk - alloced string / path to needed key
// common look: MACHINE\\Software\\... where MACHINE == HKEY_LOCAL_MACHINE
// google for more address abbrevations
PSECURITY_DESCRIPTOR pSD = 0;
EXPLICIT_ACCESS ea;
PACL pOldDACL = 0, pNewDACL = 0;
if (ERROR_SUCCESS == GetNamedSecurityInfo(sk, SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, 0, 0, &pOldDACL, 0, &pSD)) {
memset(&ea, 0, sizeof(EXPLICIT_ACCESS));
ea.grfAccessPermissions = KEY_ALL_ACCESS;
ea.grfAccessMode = GRANT_ACCESS;
ea.grfInheritance = NO_INHERITANCE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
ea.Trustee.ptstrName = <USERNAME HERE>; //DOMAIN\\USERNAME
if (ERROR_SUCCESS == SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL)) {
if (ERROR_SUCCESS == SetNamedSecurityInfo(sk, SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, 0, 0, pNewDACL, 0)) {
if (pSD != 0) LocalFree((HLOCAL)pSD);
if (pNewDACL != 0) LocalFree((HLOCAL)pNewDACL);
SAFE_FREE(sk);
// WE'RE GOOD!
return ... ;
} else {
if (pSD) LocalFree((HLOCAL)pSD);
if (pNewDACL) LocalFree((HLOCAL)pNewDACL);
SAFE_FREE(sk);
// SetNamedSecurityInfo failed
return ... ;
}
} else {
if (pSD) LocalFree((HLOCAL)pSD);
if (pNewDACL) LocalFree((HLOCAL)pNewDACL);
SAFE_FREE(sk);
// SetEntriesInAcl failed
return ... ;
}
} else {
if (pSD) LocalFree((HLOCAL)pSD);
SAFE_FREE(sk);
// GetNamedSecurityInfo failed
return ... ;
}