Denied access trying to open process - c++

Description: I've gotten myself into a problem, I'm trying to open a process and read the memory of that process. It works all fine while debugging in my VS 2013 IDE however, if I build it and run the standalone executable as administrator (or lower credentials) the process cannot be opened properly, I am recieving error code 5, which is access denied.
The thing that confuses me is the fact it's working fine in the IDE but not with the standalone executable. I don't see why VS 2013 would have higher credentials than running a program as administrator.
A link to the code is here:
https://floobits.com/Simple2012/Simple_Bot
The important section is memoryReading.cpp line 30-35
A summary of the problem follows:
1. Everything works fine in visual Studio 2013.
2. The standalone executable is denied access when trying to open "the" process.
3. The executable is run as with full rights as administrator, so is the IDE.
I want to understand this a bit more detailed so I have two key questions, if anyone is in a good mood, I would love to have a very detailed explanation.
Question 1: How can I open the process with my standalone executable?
Question 2: Why does this problem occur the way it do?
If there's any information you're missing, don't hesitate to ask for it and I'll add it in as fast as possible. I also tried highlighing some parts to make it more comfortable to read and get a quick and brief idea of the problem.

I'm trying to open a process and read the memory of that process. It works all fine while debugging
This is because you user account has SE_DEBUG_NAME (SeDebugPrivilege). Its common for developers to develop with SE_DEBUG_NAME, but its not advised.
You should develop as a regular user account to ensure you don't get these kind of unexpected dependencies.
The thing that confuses me is the fact it's working fine in the IDE but not with the standalone executable...
Now that the issue cleared up...
Question 1: How can I open the process with my standalone executable?
Question 2: Why does this problem occur the way it do?
There are a few ways to fix it.
First, you can "compile" the privilege in by setting /MANIFESTUAC:level=requireAdministrator in the manifest options. "Compiling the privilege in" is a bit misleading because you will still be prompted by UAC. See /MANIFESTUAC on MSDN.
Second, you can set "Run as Administrator" on the program's properties. Right click the executable, then select the Compatibility tab. See Configure Applications to Always Run as an Administrator on Technet. You will be prompted by UAC.
Third, you can right click your executable, and then select "Run as Administrator". You will need to do this for each invocation of your program. You will be prompted by UAC.
Its not enough to "Run as Administrator". You will now need to enable a privilege or two. That's because Windows does not start your process with all privileges enabled (unlike Linux/Unix and su or sudo). Of the privileges listed at Privilege Constants, these are the three or four interesting ones:
SE_ASSIGNPRIMARYTOKEN_NAME
SE_DEBUG_NAME
SE_TCB_NAME
SE_INCREASE_QUOTA_NAME
Here's the code I use to enable privileges, like "SeDebugPrivilege":
BOOL CUserPrivilege::EnablePrivilege( LPCTSTR pszPrivilege, BOOL bEnabled ){
// Returned to caller
BOOL bResult = FALSE;
// Thread or Process token
HANDLE hToken = NULL;
__try {
bResult = OpenThreadToken( GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &hToken );
if( !bResult )
{
bResult = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken );
assert( TRUE == bResult );
if( FALSE == bResult ) { __leave; }
}
bResult = EnablePrivilege( hToken, pszPrivilege, bEnabled );
DWORD dwError = GetLastError();
assert( TRUE == bResult || ( ERROR_SUCCESS == GetLastError() || ERROR_NOT_ALL_ASSIGNED == dwError ) );
// We're only enabling one privilege. If we get back
// ERROR_NOT_ALL_ASSIGNED, then we failed.
if( ERROR_NOT_ALL_ASSIGNED == dwError ) { bResult = FALSE; }
}
__finally {
if( NULL != hToken ) {
CloseHandle( hToken ); hToken = NULL;
}
}
return bResult;
}
BOOL CUserPrivilege::EnablePrivilege( HANDLE hToken, LPCTSTR pszPrivilege, BOOL bEnabled )
{
BOOL bResult = FALSE;
__try {
LUID luid;
TOKEN_PRIVILEGES priv;
bResult = LookupPrivilegeValue( NULL, pszPrivilege, &luid );
assert( TRUE == bResult );
if( FALSE == bResult ) { __leave; }
priv.PrivilegeCount = 1;
priv.Privileges[0].Luid = luid;
priv.Privileges[0].Attributes =
(bEnabled ? SE_PRIVILEGE_ENABLED : FALSE );
bResult = AdjustTokenPrivileges( hToken, FALSE, &priv, sizeof(priv), NULL, NULL );
// We're only enabling one privilege. If we get back
// ERROR_NOT_ALL_ASSIGNED, we failed.
if( ERROR_NOT_ALL_ASSIGNED == GetLastError() ) { bResult = FALSE; }
if( FALSE == bResult ) { __leave; }
bResult = TRUE;
}
__finally { ; }
return bResult;
}
You can check what privileges you have with code similar to below:
CUserPrivilege::Status CUserPrivilege::HasPrivilege( LPCTSTR pszPrivilege )
{
// Returned to caller
Status status = Error;
// Thread or Process token
HANDLE hToken = NULL;
// Scratch
BOOL bResult = FALSE;
__try {
bResult = OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken );
if( !bResult )
{
bResult = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken );
assert( TRUE == bResult );
if( FALSE == bResult ) { __leave; }
}
status = HasPrivilege( hToken, pszPrivilege );
}
__finally {
if( NULL != hToken ) {
CloseHandle( hToken ); hToken = NULL;
}
}
return status;
}
CUserPrivilege::Status CUserPrivilege::HasPrivilege( HANDLE hToken, LPCTSTR pszPrivilege )
{
// Returned to caller
Status status = Error;
// Scratch
BOOL bResult = FALSE;
PBYTE pBuffer = NULL;
TOKEN_PRIVILEGES* pTokenInfo = NULL;
__try
{
LUID uid = { 0, 0 };
bResult = LookupPrivilegeValue( NULL, pszPrivilege, &uid );
assert( TRUE == bResult );
if( FALSE == bResult ) { __leave; }
DWORD dwRequired = 0;
bResult = GetTokenInformation( hToken, TokenPrivileges, NULL, 0, &dwRequired );
assert( FALSE == bResult );
if( TRUE == bResult || 0 == dwRequired ) { __leave; }
pBuffer = new BYTE[dwRequired];
assert( NULL != pBuffer );
if( NULL == pBuffer ) { __leave; }
DWORD dwSize = dwRequired;
bResult = GetTokenInformation( hToken, TokenPrivileges, pBuffer, dwSize, &dwRequired );
assert( TRUE == bResult );
if( FALSE == bResult || dwSize != dwRequired ) { __leave; }
pTokenInfo = (TOKEN_PRIVILEGES*)pBuffer;
DWORD count = pTokenInfo->PrivilegeCount;
// Status changed...
status = Missing;
for( DWORD i = 0; i<count; i++ )
{
if( pTokenInfo->Privileges[i].Luid.HighPart == uid.HighPart &&
pTokenInfo->Privileges[i].Luid.LowPart == uid.LowPart )
{
DWORD attrib = pTokenInfo->Privileges[i].Attributes;
if( (attrib & SE_PRIVILEGE_ENABLED) ||
(attrib & SE_PRIVILEGE_ENABLED_BY_DEFAULT) )
{
status = Enabled;
}
else if( (attrib & SE_PRIVILEGE_REMOVED) )
{
status = Disabled;
}
break;
}
}
}
__finally
{
if( NULL != pBuffer ) {
delete[] pBuffer; pBuffer = NULL;
}
}
return status;
}
And here's the Status enumeration mentioned in the code:
enum Status { Missing = -1, Error = 0, Disabled = 1, Enabled = 2 };

Related

How to run functions from a service in current logged in users context using C++?

currently I have 1 main function which calls 3-4 utility functions that collectively block other browsers using Windows Registry and implements a firefox policy using mkdir + copy, currently, these functions aren't running under users context and are running under the system, My codebase has the function to run a cmd command under user's context but not other functions, below I have mentioned it. I am guessing running the main function under other user's contexts will automatically run the utility functions also under the user's context, since the main function calls the utility functions but I am not sure, anybody knows how to do this, the function basically first creates a directory and copy a file to it in Program Files folder and then get paths of browsers and create & set Registry keys and data-values for that user, but currently due to running from service it runs under SYSTEM.
BOOLEAN
CreateProcessInActiveSession(
const std::wstring wstrCmd
)
{
DWORD dwSId = 0xFFFFFFFF;
dwSId = WTSGetActiveConsoleSessionId();// LZGetActiveSessionId();
if (0xFFFFFFFF == dwSId)
{
return FALSE;
}
BOOL bRet;
HANDLE hTok = NULL;
bRet = WTSQueryUserToken(dwSId, &hTok);
if (FALSE == bRet)
{
return FALSE;
}
DWORD dwLinkedTokSz;
TOKEN_LINKED_TOKEN ElvTok;
bRet = GetTokenInformation(hTok, TokenLinkedToken, &ElvTok, sizeof(ElvTok), &dwLinkedTokSz);
if (TRUE == bRet)
{
CloseHandle(hTok);
hTok = ElvTok.LinkedToken;
}
HANDLE hTokDup = NULL;
bRet = DuplicateTokenEx(hTok, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hTokDup);
if (FALSE == bRet)
{
CloseHandle(hTok);
return FALSE;
}
CloseHandle(hTok);
DWORD dwCreationFlag;
dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
PVOID pEnv = NULL;
bRet = CreateEnvironmentBlock(&pEnv, hTokDup, FALSE);
if (FALSE == bRet)
{
pEnv = NULL;
}
else
{
dwCreationFlag |= CREATE_UNICODE_ENVIRONMENT;
}
STARTUPINFO stInfo;
memset(&stInfo, 0, sizeof(stInfo));
PROCESS_INFORMATION procInfo;
memset(&procInfo, 0, sizeof(procInfo));
stInfo.cb = sizeof(stInfo);
WCHAR szDesktop[] = L"winsta0\\default";
stInfo.lpDesktop = szDesktop;
WCHAR wszCmd[MAX_PATH];
wcscpy_s(wszCmd, ARRAY_SIZE(wszCmd), wstrCmd.c_str());
bRet = CreateProcessAsUserW(
hTokDup,
NULL,
wszCmd,
NULL,
NULL,
FALSE,
dwCreationFlag,
pEnv,
NULL,
&stInfo,
&procInfo
);
if (FALSE == bRet)
{
if (NULL != pEnv)
{
DestroyEnvironmentBlock(pEnv);
}
CloseHandle(hTokDup);
return FALSE;
}
CloseHandle(procInfo.hProcess);
CloseHandle(procInfo.hThread);
if (NULL != pEnv)
{
DestroyEnvironmentBlock(pEnv);
}
CloseHandle(hTokDup);
return TRUE;
}

C++: check if computer is locked

I'm trying to figure out whether the computer is locked.
I've looked at LockWorkStation function, but the function that I'm hoping to find is IsWorkStationLocked.
I need to support all windows version >= XP
For windows 7 and abowe WTS API can be used:
bool IsSessionLocked() {
typedef BOOL (PASCAL * WTSQuerySessionInformation)(HANDLE hServer, DWORD SessionId, WTS_INFO_CLASS WTSInfoClass, LPTSTR* ppBuffer, DWORD* pBytesReturned);
typedef void (PASCAL * WTSFreeMemory)( PVOID pMemory);
WTSINFOEXW * pInfo = NULL;
WTS_INFO_CLASS wtsic = DW_WTSSessionInfoEx;
bool bRet = false;
LPTSTR ppBuffer = NULL;
DWORD dwBytesReturned = 0;
LONG dwFlags = 0;
WTSQuerySessionInformation pWTSQuerySessionInformation = NULL;
WTSFreeMemory pWTSFreeMemory = NULL;
HMODULE hLib = LoadLibrary( _T("wtsapi32.dll") );
if (!hLib) {
return false;
}
pWTSQuerySessionInformation = (WTSQuerySessionInformation)GetProcAddress(hLib, "WTSQuerySessionInformationW" );
if (!pWTSQuerySessionInformation) {
goto EXIT;
}
pWTSFreeMemory = (WTSFreeMemory)GetProcAddress(hLib, "WTSFreeMemory" );
if (pWTSFreeMemory == NULL) {
goto EXIT;
}
if(pWTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, g_dwSessionID, wtsic, &ppBuffer, &dwBytesReturned)) {
if(dwBytesReturned > 0) {
pInfo = (WTSINFOEXW*)ppBuffer;
if (pInfo->Level == 1) {
dwFlags = pInfo->Data.WTSInfoExLevel1.SessionFlags;
}
if (dwFlags == WTS_SESSIONSTATE_LOCK) {
bRet = true;
}
}
pWTSFreeMemory(ppBuffer);
ppBuffer = NULL;
}
EXIT:
if (hLib != NULL) {
FreeLibrary(hLib);
}
return bRet;
}
Please check following article for supported platforms for WTSINFOEX structure:
https://technet.microsoft.com/ru-ru/sysinternals/ee621017
From the same MSDN link you gave, the third paragraph of "Remarks" says:
This function has the same result as pressing Ctrl+Alt+Del and clicking Lock Workstation. To unlock the workstation, the user must log in. There is no function you can call to determine whether the workstation is locked. To receive notification when the user logs in, use the WTSRegisterSessionNotification function to receive WM_WTSSESSION_CHANGE messages. You can use session notifications to track the desktop state so you know whether it is possible to interact with the user.
Alex Vershynin's version works well, with a few code changes that I had to make.
I had to: change DW_WTSSessionInfoEx to WTSSessionInfoEx (Answering user586399), define "g_dwSessionID" to WTSGetActiveConsoleSessionId(), include Wtsapi32.h. I think that's it ...
Since I can't comment, I'll paste the whole code here.
#include "Wtsapi32.h"
bool IsSessionLocked()
{
typedef BOOL( PASCAL * WTSQuerySessionInformation )( HANDLE hServer, DWORD SessionId, WTS_INFO_CLASS WTSInfoClass, LPTSTR* ppBuffer, DWORD* pBytesReturned );
typedef void ( PASCAL * WTSFreeMemory )( PVOID pMemory );
WTSINFOEXW * pInfo = NULL;
WTS_INFO_CLASS wtsic = WTSSessionInfoEx;
bool bRet = false;
LPTSTR ppBuffer = NULL;
DWORD dwBytesReturned = 0;
LONG dwFlags = 0;
WTSQuerySessionInformation pWTSQuerySessionInformation = NULL;
WTSFreeMemory pWTSFreeMemory = NULL;
HMODULE hLib = LoadLibrary( "wtsapi32.dll" );
if( !hLib )
{
return false;
}
pWTSQuerySessionInformation = (WTSQuerySessionInformation) GetProcAddress( hLib, "WTSQuerySessionInformationW" );
if( pWTSQuerySessionInformation )
{
pWTSFreeMemory = (WTSFreeMemory) GetProcAddress( hLib, "WTSFreeMemory" );
if( pWTSFreeMemory != NULL )
{
DWORD dwSessionID = WTSGetActiveConsoleSessionId();
if( pWTSQuerySessionInformation( WTS_CURRENT_SERVER_HANDLE, dwSessionID, wtsic, &ppBuffer, &dwBytesReturned ) )
{
if( dwBytesReturned > 0 )
{
pInfo = (WTSINFOEXW*) ppBuffer;
if( pInfo->Level == 1 )
{
dwFlags = pInfo->Data.WTSInfoExLevel1.SessionFlags;
}
if( dwFlags == WTS_SESSIONSTATE_LOCK )
{
bRet = true;
}
}
pWTSFreeMemory( ppBuffer );
ppBuffer = NULL;
}
}
}
if( hLib != NULL )
{
FreeLibrary( hLib );
}
return bRet;
}
Just as a refinement to MGamsby's post: if you are targeting the Win8.1 SDK or Win10 SDK, then it's not necessary to mess around with GetProcAddress(), LoadLibrary(), and WTSGetActiveConsoleSessionId(), and the code becomes much more compact:
bool isSessionLocked()
{
WTSINFOEXW* pInfo = NULL;
WTS_INFO_CLASS wtsic = WTSSessionInfoEx;
LPTSTR ppBuffer = NULL;
DWORD dwBytesReturned = 0;
LONG sessionFlags = WTS_SESSIONSTATE_UNKNOWN; // until we know otherwise. Prevents a false positive since WTS_SESSIONSTATE_LOCK == 0
DWORD dwSessionID = WTSGetActiveConsoleSessionId();
if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, dwSessionID, wtsic, &ppBuffer, &dwBytesReturned))
{
if (dwBytesReturned > 0)
{
pInfo = (WTSINFOEXW*)ppBuffer;
if (pInfo->Level == 1)
{
sessionFlags = pInfo->Data.WTSInfoExLevel1.SessionFlags;
}
}
WTSFreeMemory(ppBuffer);
ppBuffer = NULL;
}
return (sessionFlags == WTS_SESSIONSTATE_LOCK);
}
NOTE:
I realise that the question is about Windows 7 but you can still target Win7 using the Win8.1 SDK, and it is available for use in VS2015 onwards.
Edit: Oops. I just realised the OP asked for WinXP onwards in which case, yes, you do need to mess around with LoadLibrary() etc. Sorry about that.
I'll leave my code here in case it is useful for anyone.
Apparently there is a difference between screen lock and session lock. Here're my investigations results.
A session becomes locked when a password box is shown on the lock screen. In Windows 7 after pressing Win+L lockscreen appears with password box, so a session is locked immediately. But in Win10/11 lockscreen initially appears without password box. And nothing happens. After pressing a key or clicking a mouse button, password box appears and session becomes locked.
WTSRegisterSessionNotification(): WM_WTSSESSION_CHANGE message arrives after password box is shown.
WTSQuerySessionInformation(): sessionFlags == WTS_SESSIONSTATE_LOCK after password box is shown
SetWinEventHook() with EVENT_SYSTEM_DESKTOPSWITCH: multiple events arrives when (un)locking. Looks like two desktops are created when locking: for initial lock screen and the one with password box.
SwitchDesktop() succeeds until password box is shown.
Checking for running LogonUI.exe works, but it remains running for a while after unlock. Theoretically in the future versions it may remain running permanently. So one can't be absolutely sure the session is locked if LogonUI.exe is running.
So far I haven't found a reliable way to check if computer is locked and unlocked. But I'll try to continue my research.

OpenProcess() return error access denided?

I wrote the function GetProcessHandleAndID() as below code:
bool GetProcessHandleAndID( char* _processName, PROCESS_INFORMATION* _processInfo /* out */ )
{
HANDLE SnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
if( SnapShot == INVALID_HANDLE_VALUE )
{
return false;
}
PROCESSENTRY32 procEntry;
procEntry.dwSize = sizeof( PROCESSENTRY32 );
if( !Process32First( SnapShot, &procEntry ) )
{
CloseHandleSafely(SnapShot);
return false;
}
do
{
if( strcmp( procEntry.szExeFile, _processName ) == 0 )
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procEntry.th32ProcessID);
if(hProcess != NULL)
{
_processInfo->hProcess = hProcess;
_processInfo->dwProcessId = procEntry.th32ProcessID;
CloseHandleSafely(SnapShot);
return true;
}
}
}
while( Process32Next( SnapShot, &procEntry ) );
CloseHandleSafely(SnapShot);
return false;
}
OpenProcess(PROCESS_ALL_ACCESS, FALSE, procEntry.th32ProcessID)work fine on Administrator account, But it will return NULL with GetLastError() = 5 = Access_Denied when run on Normal accounts.
Note that I have called function EnableDebugPriv() before GetProcessHandleAndID().
void EnableDebugPriv()
{
HANDLE hToken;
LUID luid;
TOKEN_PRIVILEGES tkp;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = luid;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, false, &tkp, sizeof(tkp), NULL, NULL);
CloseHandle(hToken);
}
I have search and read more about this error, but I don't know how to make it work fine on normal user without making it "Run As Administrator"!
Many thanks,
T&T

ReadProcess Memory 299 on Windows 8

I have this program that works perfectly in windows 7 but on windows 8 the readprocessmemory seems to be blank when I output it.Get Last error code 299. I Did not create this part of of program for read process but I use it because it was working for windows 7. The game handles and aria location are same on windows 8 machine, I double checked them. and The game handle is found. The address works fine in windows 7.
hGameWindow = FindWindow(L"WFElementClient Window",NULL);
if(hGameWindow) {
GetWindowThreadProcessId( hGameWindow, &dwProcId );
if( dwProcId != 0 ) {
hProcHandle = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dwProcId );
if( hProcHandle == INVALID_HANDLE_VALUE || hProcHandle == NULL ) {
GameStatus = "Failed to open process for valid handle";
}else{
GameStatus = "Game Found";
myaddr = FindPointerAddr(hProcHandle, ariaBase, aOffset);
// IsGameAvail = true;
}
}
else GameStatus = "Failed to obtain process id";
}
else GameStatus = "game handle not found";
ReadProcessMemory(hProcHandle, (LPCVOID)myaddr, &buffer, sizeof(buffer), NULL);
int FindPointerAddr(HANDLE pHandle,int baseaddr, DWORD offsets[])
{
int Address = baseaddr;
int offset = 0;
int offsetCount = 5;
for (int i = 0; i < offsetCount; i++)
{
ReadProcessMemory(pHandle, (LPCVOID)Address, &Address , 4, NULL);
Address+=offsets[i];
}
return Address;
}
Security permissions have changed from Windows 7 to Windows 8.
You may need to run as administrator and set SeDebugPrivelage now, when you were not required to on previous versions of Windows. Such as when calling OpenProcess() with PROCESS_ALL_ACCESS because PROCESS_VM_READ requires SeDebugPrivelage
Here is how you set SeDebugPrivelage:
bool SetDebugPrivilege(bool Enable)
{
HANDLE hToken{ nullptr };
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken))
return false;
TOKEN_PRIVILEGES TokenPrivileges{};
TokenPrivileges.PrivilegeCount = 1;
TokenPrivileges.Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0;
if (!LookupPrivilegeValueA(nullptr, "SeDebugPrivilege", &TokenPrivileges.Privileges[0].Luid))
{
CloseHandle(hToken);
return false;
}
if (!AdjustTokenPrivileges(hToken, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
{
CloseHandle(hToken);
return false;
}
CloseHandle(hToken);
return true;
}

Windows 8 mapped drives paths not recognized correctly by Service

I am facing a problem in Windows 8 where an elevated application/service that impersonates the logged in user does not recognize the mapped drives paths correctly.
I have a windows service that I use to copy files from/to different source paths/destinations including mapped network drives. The Paths are fed to the service through an xml file. The service then reads the source and destination from the xml and copies the file. I never had an issue with mapped drives in Vista and 7 as the service always impersonates the logged user by getting the explorer token and all my CreateFile, ReadFiles and WriteFile worked perfectly.
This is how I impersonate the user
first I get the session token using the following code
DWORD GetActiveSessionId(DWORD& ret)
{
ret=0;
DWORD active_session_id = WTSGetActiveConsoleSessionId();
if (IsSessionActive(active_session_id))
{
return active_session_id;
}
DWORD console_session_ID = active_session_id;
active_session_id = -2;
WTS_SESSION_INFO* session_info = NULL;
DWORD num_sessions = 0;
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1,
&session_info, &num_sessions))
{
// Pick the first active session we can find
for (DWORD i = 0 ; i < num_sessions; ++i)
{
if (session_info[i].State == WTSActive)
{
// There is a user logged on to the WinStation associated with the
// session.
active_session_id = session_info[i].SessionId;
break;
}
}
WTSFreeMemory(session_info);
return active_session_id;
}
ret=::GetLastError();
return -2;
}
BOOL GetSessionUserToken( HANDLE * phUserToken, DWORD& retCode )
{
if( NULL == phUserToken )
{
return FALSE;
}
BOOL bRet = FALSE;
HANDLE hImpersonationToken = NULL;
BOOL bWin2K = FALSE;
OSVERSIONINFOEX osv;
ZeroMemory( & osv, sizeof( OSVERSIONINFOEX ) );
osv.dwOSVersionInfoSize = sizeof( OSVERSIONINFOEX );
if( GetVersionEx( (OSVERSIONINFO*) & osv ) )
{
if( 0 == osv.dwMinorVersion && osv.dwMajorVersion == 5)
{
return FALSE;
}
}
DWORD dwActiveSession= CGSSystem::GetActiveSessionId(retCode);
if (dwActiveSession==GSInvalidSessionId)
return FALSE;
if( 0 != WTSQueryUserToken( dwActiveSession, & hImpersonationToken ) )
{
bRet = TRUE;
}
else
{
}
DWORD neededSize = 0;
HANDLE *realToken = new HANDLE;
if(GetTokenInformation(hImpersonationToken, (::TOKEN_INFORMATION_CLASS) TokenLinkedToken, realToken, sizeof(HANDLE), &neededSize))
{
CloseHandle(hImpersonationToken);
hImpersonationToken = *realToken;
}
DWORD lastError = GetLastError();
delete realToken;
if( TRUE == bRet )
{
bRet = DuplicateTokenEx( hImpersonationToken,
0,
NULL,
SecurityImpersonation,
TokenPrimary,
phUserToken );
CloseHandle( hImpersonationToken );
}
return bRet;
}
Then I have my CopyFile function which is a thread. It is a huge function so I will only mention the important (impersonation/security) parts.
BOOL CopyFile(LPCTSTR source, LPCTSTR destination)
{
//Some variables initializations
//...
HRESULT hrInternal = CoInitializeSecurity(
NULL, // Allow *all* VSS writers to communicate back!
-1, // Default COM authentication service
NULL, // Default COM authorization service
NULL, // reserved parameter
RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // Strongest COM authentication level
RPC_C_IMP_LEVEL_IDENTIFY, // Minimal impersonation abilities
NULL, // Default COM authentication settings
EOAC_NONE, // No special options
NULL // Reserved parameter
);
//Initialize security descriptors
SECURITY_DESCRIPTOR SD;
SECURITY_ATTRIBUTES copyMutexAttrib;
copyMutexAttrib.nLength = sizeof( SECURITY_ATTRIBUTES );
copyMutexAttrib.lpSecurityDescriptor = & SD;
copyMutexAttrib.bInheritHandle = TRUE;
if(!InitializeSecurityDescriptor( & SD, SECURITY_DESCRIPTOR_REVISION ) )
{
//Error handling;
}
// add a NULL disc. ACL to the security descriptor.
//
if( ! SetSecurityDescriptorDacl( & SD, TRUE, (PACL) NULL, FALSE ) )
{
//Error handling;
}
HRESULT hr=S_OK;
hr=ModifyThreadPrivilege( SE_BACKUP_NAME, TRUE , m_hUserToken==NULL ? FALSE : TRUE );
if (FAILED(hr))
{
//Error Handling and logs
}
hr=S_OK;
hr=ModifyThreadPrivilege( SE_TCB_NAME, TRUE , m_hUserToken==NULL ? FALSE : TRUE );
if (FAILED(hr))
{
//Error Handling and logs
}
hr=ModifyThreadPrivilege( SE_IMPERSONATE_NAME, TRUE , m_hUserToken==NULL ? FALSE : TRUE );
if (FAILED(hr))
{
//Error Handling and logs
}
hr=ModifyThreadPrivilege( SE_MANAGE_VOLUME_NAME, TRUE , m_hUserToken==NULL ? FALSE : TRUE );
if (FAILED(hr))
{
//Error Handling and logs
}
hr=ModifyThreadPrivilege( SE_SYSTEM_PROFILE_NAME, TRUE , m_hUserToken==NULL ? FALSE : TRUE );
if (FAILED(hr))
{
//Error Handling and logs
}
hr=ModifyThreadPrivilege( SE_DEBUG_NAME, TRUE , m_hUserToken==NULL ? FALSE : TRUE );
if (FAILED(hr))
{
//Error Handling and logs
}
//Other variable initializations
//...
//Create the destination file
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
HANDLE hFile = ::CreateFile(destination, GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, &sa,
CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH|FILE_FLAG_BACKUP_SEMANTICS, NULL); //---> creates the file in the wrong location
}
and this is my ModifyThreadPrivilage code:
HRESULT ModifyThreadPrivilege(IN LPCTSTR szPrivilege,IN BOOL fEnable,IN BOOL OpenAsSelf)
{
HRESULT hr = S_OK;
TOKEN_PRIVILEGES NewState;
LUID luid;
HANDLE hToken = NULL;
// Open the process token for this process.
if (!OpenThreadToken(GetCurrentThread(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
OpenAsSelf,
&hToken ))
{
int iLast=::GetLastError();
if (iLast != ERROR_NO_TOKEN)
{
return ERROR_FUNCTION_FAILED;
}
/*
* No access token for the thread so impersonate the security context
* of the process.
*/
if (!ImpersonateSelf(SecurityImpersonation))
{
return ERROR_FUNCTION_FAILED;
}
if (!OpenThreadToken(GetCurrentThread(),
TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
FALSE,
&hToken))
{
return ERROR_FUNCTION_FAILED;
}
}
// Get the local unique ID for the privilege.
if ( !LookupPrivilegeValue( NULL,
szPrivilege,
&luid ))
{
CloseHandle( hToken );
printf("Failed LookupPrivilegeValue\n");
return ERROR_FUNCTION_FAILED;
}
// Assign values to the TOKEN_PRIVILEGE structure.
NewState.PrivilegeCount = 1;
NewState.Privileges[0].Luid = luid;
NewState.Privileges[0].Attributes =
(fEnable ? SE_PRIVILEGE_ENABLED : 0);
// Adjust the token privilege.
if (!AdjustTokenPrivileges(hToken,
FALSE,
&NewState,
0,
NULL,
NULL))
{
hr = ERROR_FUNCTION_FAILED;
}
// Close the handle.
CloseHandle(hToken);
return hr;
}
In windows 8 and when the destination is a mapped drive like "Z:\MyFile.txt" it writes the file to the wrong location like so:
I have mapped network drive Z: which is mapped to
\\nsa\public\myfolder1\subfolder\ the function writes the file to
\\nsa\public\
I have never had such behavior in Windows Vista or 7 but it seems that MS has introduced some new privileges or securities that are causing such behavior.
I have noticed many people complaining about mapped drives in Windows 8, especially for elevated processes but all the solutions suggest to use UNC paths instead of the mapped drive letter.
I also noticed that enabling/disabling UAC has no effect on this.
Can someone explain how can I achieve my goal in copying the file?