ReadProcess Memory 299 on Windows 8 - c++

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;
}

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;
}

A process created with CreateProcessAsUserW from a 64 bit process exits immediately with exception code 0xc06d007e on Windows 7 64 bit

I have a C# Application, which is launched from a C++ DLL using CreateProcessAsUserW api. The process is launched successfully, but terminates immediately. It works properly on Windows 10 [both as 32 bit and 64 bit] and 32 bit on Windows 7. I found the following link,
Why is this process crashing as soon as it is launched?
however, process monitor from SysInternals finds no missing dll. [I can attach the saved logs from ProcMon] I also tried passing the path to the application folder, as suggested elsewhere, in the lpCurrentDirectory parameter of the API, but that did not work either.
I followed the guidance in How to get the active user when multiple users are logged on in Windows? to write the code which launches the process,
and the method which launches the process is called from a windows service.To emulate that from the command line, I used the following
How do you run CMD.exe under the Local System Account? [psexec64 -i -s cmd.exe, and then launched the process from cmd.exe]
The method is as follows
//How to get the active user when multiple users are logged on in Windows?
STDMETHODIMP CProcessManager::LaunchProcessAsActiveUser(BSTR processName, LONG* dwProcessId)
{
//char *lpszPath = _com_util::ConvertBSTRToString(processName);
wchar_t* path = (wchar_t*)processName;//CharToWideChar(lpszPath);
DWORD session_id = -1;
DWORD session_count = 0;
WTS_SESSION_INFOA *pSession = NULL;
if (WTSEnumerateSessionsA(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSession, &session_count))
{
//log success
}
else
{
//log error
return S_OK;
}
logger->Log(L"Session Count", session_count);
logger->Log(L"Begin Enumerating Sesions");
for (int i = 0; i < session_count; i++)
{
session_id = pSession[i].SessionId;
logger->Log(L"SessionId", session_id);
WTS_CONNECTSTATE_CLASS wts_connect_state = WTSDisconnected;
WTS_CONNECTSTATE_CLASS* ptr_wts_connect_state = NULL;
DWORD bytes_returned = 0;
if (::WTSQuerySessionInformation(
WTS_CURRENT_SERVER_HANDLE,
session_id,
WTSConnectState,
reinterpret_cast<LPTSTR*>(&ptr_wts_connect_state),
&bytes_returned))
{
wts_connect_state = *ptr_wts_connect_state;
::WTSFreeMemory(ptr_wts_connect_state);
if (wts_connect_state != WTSActive) continue;
}
else
{
//log error
continue;
}
logger->Log(L"End Enumerating Sesions");
logger->Log(L"Selected Session Id", session_id);
HANDLE hImpersonationToken;
if (!WTSQueryUserToken(session_id, &hImpersonationToken))
{
//log error
logger->Log(L"Exception in WTSQueryUserToken", GetLastError());
continue;
}
//Get real token from impersonation token
DWORD neededSize1 = 0;
HANDLE *realToken = new HANDLE;
if (GetTokenInformation(hImpersonationToken, (::TOKEN_INFORMATION_CLASS) TokenLinkedToken, realToken, sizeof(HANDLE), &neededSize1))
{
CloseHandle(hImpersonationToken);
hImpersonationToken = *realToken;
}
else
{
//log error
logger->Log(L"Exception in GetTokenInformation", GetLastError());
continue;
}
HANDLE hUserToken;
if (!DuplicateTokenEx(hImpersonationToken,
//0,
//MAXIMUM_ALLOWED,
TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS | MAXIMUM_ALLOWED,
NULL,
SecurityImpersonation,
TokenPrimary,
&hUserToken))
{
//log error
logger->Log(L"Exception in DuplicateTokenEx", GetLastError());
continue;
}
// Get user name of this process
//LPTSTR pUserName = NULL;
WCHAR* pUserName;
DWORD user_name_len = 0;
if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, session_id, WTSUserName, &pUserName, &user_name_len))
{
//log username contained in pUserName WCHAR string
// char * lpszUserName = WideCharToChar(pUserName);
logger->Log(pUserName);
//LocalFree(lpszUserName);
}
else
{
logger->Log(L"Exception in WTSQuerySessionInformation", GetLastError());
}
//Free memory
if (pUserName) WTSFreeMemory(pUserName);
ImpersonateLoggedOnUser(hUserToken);
STARTUPINFOW StartupInfo;
StartupInfo.cb = sizeof(STARTUPINFOW);
//GetStartupInfoW(&StartupInfo);
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
//Uncommented by Sagar 20th January 20118 1612
StartupInfo.lpDesktop = CharToWideChar("winsta0\\default");
//to Hide Console Process 03-10-2018
StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow = SW_SHOW;//SW_HIDE;
PROCESS_INFORMATION processInfo;
SECURITY_ATTRIBUTES Security1;
ZeroMemory(&Security1, sizeof(Security1));
Security1.nLength = sizeof SECURITY_ATTRIBUTES;
SECURITY_ATTRIBUTES Security2;
ZeroMemory(&Security2, sizeof(Security2));
Security2.nLength = sizeof SECURITY_ATTRIBUTES;
void* lpEnvironment = NULL;
// Get all necessary environment variables of logged in user
// to pass them to the new process
BOOL resultEnv = CreateEnvironmentBlock(&lpEnvironment, hUserToken, FALSE);
if (!resultEnv)
{
//log error
DWORD err = GetLastError();
logger->Log(L"Exception in CreateEnvironmentBlock", err);
continue;
}
WCHAR PP[1024]; //path and parameters
ZeroMemory(PP, 1024 * sizeof WCHAR);
wcscpy_s(PP, path);
wcscat_s(PP, L" ");
//wcscat(PP, args);
// Start the process on behalf of the current user
BOOL result = CreateProcessAsUserW(hUserToken,
PP,
NULL,
&Security1,
&Security2,
FALSE,
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE/*| CREATE_NO_WINDOW*/,//CREATE_NO_WINDOW to Hide Console Process 03-10-2018
/*lpEnvironment*/NULL,
//"C:\\ProgramData\\some_dir",
NULL,
/*NULL,*/
&StartupInfo,
&processInfo);
if (!result)
{
//log error
//char * lpszPath = WideCharToChar(PP);
logger->Log(L"Failed to create process", PP);
//LocalFree(lpszPath);
DWORD err = GetLastError();
logger->Log(L"GetLastError returned", err);
}
else
{
*dwProcessId = processInfo.dwProcessId;
logger->Log(L"Created Process", *dwProcessId);
//log success
}
DestroyEnvironmentBlock(lpEnvironment);
CloseHandle(hImpersonationToken);
CloseHandle(hUserToken);
CloseHandle(realToken);
RevertToSelf();
}
WTSFreeMemory(pSession);
return S_OK;
}
Procmon screen shots attached below
Please help.
Thanks,
Sagar
[![Pg 1](https://i.stack.imgur.com/CJCrG.png)
[![Pg 2](https://i.stack.imgur.com/uo8Uw.png)
[![Pg 3](https://i.stack.imgur.com/G7g12.png)
[![Pg 4](https://i.stack.imgur.com/6e5w4.png)
[![Pg 5](https://i.stack.imgur.com/8k2b0.png)
I tried the following permutations
Target Process : ATL DLL : Launcher:Result
64 bit 64 bit 64 bit Crashes
64 bit 32 bit 64 bit Crashes
32 bit 32 bit 64 bit Crashes
32 bit 64 bit 64 bit Crashes
32 bit 32 bit 32 bit OK
64 bit 32 bit 32 bit OK
64 bit 64 bit 32 bit OK
32 bit 64 bit 32 bit OK
So it seems that if the launcher process is 64 bit, it always crashes and if it is 32 bit, it always succeeds. What is the difference between a 64 and 32 bit launcher process?
I got the answer on https://social.msdn.microsoft.com/Forums/vstudio/en-US/fb9d15fb-dc9f-488e-92c4-e0bb438442e1/64-bit-dot-net-process-created-with-createprocessasuserw-exits-immediately-with-exception-code?forum=vcgeneral [RLWA32 answered it]
There was a problem with the way in which the process was being launched. His code for launching the process is as follows, and it works fine.
UINT __stdcall RunProgram(LPVOID pv)
{
WCHAR szTarg32[MAX_PATH] = TARGET32_PATH, szTarg64[MAX_PATH] = TARGET64_PATH;
LPWSTR pszProcess = nullptr;
PWTS_SESSION_INFOW pwsi = NULL;
DWORD dwSession = -1, dwCount = 0;
HANDLE hToken = NULL;
LPVOID lpEnvironment = nullptr;
LPWSTR pszPath = nullptr;
UINT ret = 1;
DWORD dwOpcode = reinterpret_cast<DWORD>(pv);
if (dwOpcode == LAUNCH_X86PROCESS)
pszProcess = szTarg32;
else
pszProcess = szTarg64;
if (!WTSEnumerateSessionsW(WTS_CURRENT_SERVER, 0, 1, &pwsi, &dwCount))
{
Report(L"WTSEnumerateSessions failed with %d\n", GetLastError());
return ret;
}
for (DWORD i = 0; i < dwCount; i++)
{
if (pwsi[i].State == WTSActive)
{
dwSession = pwsi[i].SessionId;
break;
}
}
if (dwSession != -1)
{
if (WTSQueryUserToken(dwSession, &hToken))
{
if (CreateEnvironmentBlock(&lpEnvironment, hToken, FALSE))
{
HRESULT hr;
if (SUCCEEDED((hr = SHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_DEFAULT, hToken, &pszPath))))
{
if (ImpersonateLoggedOnUser(hToken))
{
STARTUPINFOW si = { sizeof si };
PROCESS_INFORMATION pi = {};
if (CreateProcessAsUserW(hToken, pszProcess, nullptr, nullptr, nullptr, FALSE, CREATE_UNICODE_ENVIRONMENT, lpEnvironment, pszPath, &si, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
Report(L"CreateProcessAsUser started %s, pid is %d\n", pszProcess, pi.dwProcessId);
ret = 0;
}
else
Report(L"CreateProcessAsUser failed with %d\n", GetLastError());
RevertToSelf();
}
else
Report(L"ImpersonateLoggedOnUser failed with %d\n", GetLastError());
}
else
Report(L"SHGetKnownFolderPath failed with 0x%X\n", hr);
}
else
Report(L"CreateEnvironmentBlock failed with %d\n", GetLastError());
}
else
Report(L"WTSQueryUserToken failed with %d\n", GetLastError());
}
else
Report(L"WTSEnumerateSessions reported no active session\n");
if (pwsi)
WTSFreeMemory(pwsi);
if (hToken)
CloseHandle(hToken);
if (lpEnvironment)
DestroyEnvironmentBlock(lpEnvironment);
if (pszPath)
CoTaskMemFree(pszPath);
return ret;
}

How to tell if Windows partition is active by a path on it?

My goal is to know if Windows is installed on an active disk partition. I can obtain the path for Windows:
C:\WINDOWS
and then it's partition:
\Device\Harddisk4\Partition4
But the question is how to know if this partition is active?
Check this Link (http://msdn.microsoft.com/en-us/library/windows/desktop/aa365451(v=vs.85).aspx)
PARTITION_INFORMATION has BootIndicator. but it is not guarantee about the running windows was booted by that partition.
Edited It is a example function tested on Windows7. I think 'activate' partition is not your goal. The 'activate' has meaning such as bootable USB device. I don't like WMI but it could be help your goal (http://msdn.microsoft.com/en-us/library/windows/desktop/bb986746(v=vs.85).aspx)
BOOL
__stdcall
TP_IsPartitionActivated(
__in LPCWSTR pPartition,
__out PBOOL pbIsActivated
)
{
HANDLE hDevice = INVALID_HANDLE_VALUE;
PARTITION_INFORMATION_EX szPartitionInformation;
DWORD cbReturned = 0x00;
if (pPartition == NULL || pbIsActivated == NULL) { return FALSE; }
__try
{
hDevice = CreateFileW(pPartition, 0x00, 0x00, NULL, OPEN_EXISTING, 0x00, NULL);
if (hDevice == INVALID_HANDLE_VALUE) { return FALSE; }
RtlZeroMemory(&szPartitionInformation, sizeof(szPartitionInformation));
if (FALSE != DeviceIoControl(hDevice, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0x00, (LPVOID)&szPartitionInformation, sizeof(PARTITION_INFORMATION_EX), &cbReturned, NULL))
{
if (PARTITION_STYLE_MBR == szPartitionInformation.PartitionStyle)
{
*pbIsActivated = szPartitionInformation.Mbr.BootIndicator;
}
else
{
}
return TRUE;
}
else
{
cbReturned = GetLastError();
wprintf(L"%08X(%d)\n", cbReturned, cbReturned);
}
}
__finally
{
if (hDevice != INVALID_HANDLE_VALUE) { CloseHandle(hDevice); }
}
return FALSE;
}
Call like
WCHAR szPartition[] = L"\\\\.\\C:";
BOOL bIsActivated = FALSE;
if (FALSE != TP_IsPartitionActivated(szPartition, &bIsActivated))
{
wprintf(L"%s \n", bIsActivated == FALSE ? L"not activated" : L"activated");
}
else
{
wprintf(L"function fail\n");
}

C++ WriteProcessMemory error INVALID_HANDLE_VALUE

I'm using to the "CreateRemoteThread & WriteProcessMemory" Technique to inject my dll into another process. My code work fine on windows 7,8, but WriteProcessMemory function always return FALSE (GetLastError = 6 - INVALID_HANDLE_VALUE) when run on windows XP (VirtualBox machine). Can't u help me?
Here is the main code:
BOOL CHookDLL::DoHook(const DWORD dwProcessId, const CHAR* szDLLHookName)
{
CHAR szDllHookPath[1024] = "";
HANDLE hRemoteThread = NULL;
HMODULE hLib = 0;
LPVOID RemoteString = NULL;
LPVOID LoadLibAddy = NULL;
if (dwProcessId == NULL){
__OutputDebug("CHookDLL::DoHook\tpProcessId NULL");
return FALSE;
}
::GetFullPathNameA(szDLLHookName, MAX_PATH, szDllHookPath, NULL);
if (::PathFileExists((CString)szDllHookPath) == FALSE){
__OutputDebug("CHookDLL::DoHook\tPathFileExists FALSE");
return FALSE;
}
// enable SeDebugPrivilege
if (!SetPrivilege(m_hTokenSetPrivilege, SE_DEBUG_NAME, TRUE))
{
__OutputDebug("CHookDLL::DoHook\tSetPrivilege FAILED");
// close token handle
CloseHandle(m_hTokenSetPrivilege);
return FALSE;
}
m_hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (m_hProcess == NULL){
__OutputDebug("CHookDLL::DoHook\tOpenProcess FALSE: %d", GetLastError());
return FALSE;
}
LoadLibAddy = (LPVOID)::GetProcAddress(::GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
if (LoadLibAddy == NULL){
__OutputDebug("CHookDLL::DoHook\tGetProcAddress NULL");
return FALSE;
}
// Allocate space in the process for our DLL
RemoteString = (LPVOID)VirtualAllocEx(m_hProcess, NULL, strlen(szDllHookPath) + 1,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (RemoteString == NULL){
__OutputDebug("CHookDLL::DoHook\tVirtualAllocEx NULL");
return FALSE;
}
// this line is return FALSE
if (WriteProcessMemory(m_hProcess, RemoteString, szDllHookPath, strlen(szDllHookPath) + 1, NULL) == FALSE)
{
__OutputDebug("CHookDLL::DoHook\tWriteProcessMemory FALSE: %d", GetLastError());
return FALSE;
}
hRemoteThread = ::CreateRemoteThread(m_hProcess, NULL, NULL,
(LPTHREAD_START_ROUTINE)LoadLibAddy,
(LPVOID)RemoteString, NULL, NULL);
::WaitForSingleObject(hRemoteThread, INFINITE);
// Get handle of the loaded module
::GetExitCodeThread(hRemoteThread, &m_hLibModule);
if (m_hLibModule == NULL){
__OutputDebug("CHookDLL::DoHook\tCreateRemoteThread NULL");
return FALSE;
}
// Clean up
::CloseHandle(hRemoteThread);
::VirtualFreeEx(m_hProcess, RemoteString,
strlen(szDllHookPath) + 1, MEM_RELEASE);
__OutputDebug("Hook OK");
return TRUE;
}
// Common function Output Debug String
static INT __OutputDebug(const CHAR* format, ...)
{
#ifndef DEBUG
return -1;
#endif // DEBUG
if (format[0] == 0) return -1;
CHAR szDebug[1024] = "";
va_list arglist;
va_start(arglist, format);
vsprintf_s(szDebug,format, arglist);
va_end(arglist);
strcat_s(szDebug, "\n");
OutputDebugStringA(szDebug);
return 1;
}
The problem lies in your OpenProcess call. From here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms684880(v=vs.85).aspx, listed under PROCESS_ALL_ACCESS access right:
Windows Server 2003 and Windows XP: The size of the PROCESS_ALL_ACCESS flag increased on Windows Server 2008 and Windows Vista. If an application compiled for Windows Server 2008 and Windows Vista is run on Windows Server 2003 or Windows XP, the PROCESS_ALL_ACCESS flag is too large and the function specifying this flag fails with ERROR_ACCESS_DENIED. To avoid this problem, specify the minimum set of access rights required for the operation. If PROCESS_ALL_ACCESS must be used, set _WIN32_WINNT to the minimum operating system targeted by your application (for example, #define _WIN32_WINNT _WIN32_WINNT_WINXP). For more information, see Using the Windows Headers.
Therefore, it may be that PROCESS_VM_READ and PROCESS_VM_OPERATION aren't getting set, hence the invalid handle error later on. I know that that OpenProcess should really be returning an error code if it fails - and it's not - but if this flag is genuinely overflowing, I can see how a silent failure might occur.

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?