Xcvdata() for deleting port.
BOOL DeletePortCus( TCHAR* PortName )
{
HANDLE hPrinter;
PRINTER_DEFAULTS PrinterDefaults;
memset(&PrinterDefaults, 0, sizeof(PrinterDefaults));
PrinterDefaults.pDatatype = NULL;
PrinterDefaults.pDevMode = NULL;
PrinterDefaults.DesiredAccess = SERVER_ACCESS_ADMINISTER;
DWORD needed = 0;
DWORD rslt = 0;
//Port data
PORT_DATA_1 pOutputData ;
DWORD error = 0;
if (!OpenPrinter(L",XcvMonitor Standard TCP/IP Port", &hPrinter, &PrinterDefaults))
{
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), NULL,(LPTSTR) &lpMsgBuf, 0, NULL );
_tprintf( TEXT("Error in OpenPrinter. Error msg : %s"),lpMsgBuf);
LocalFree( lpMsgBuf );
return FALSE;
}
DWORD xcvresult= 0;
if (
!XcvData(
hPrinter,
TEXT("DeletePort"),
(PBYTE)PortName,
(lstrlen(PortName) +1) * sizeof(TCHAR), //the 1 is for the trailing NULL
( byte * ) &pOutputData,
sizeof(PORT_DATA_1),
&needed,
&xcvresult)
)
{
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), NULL,(LPTSTR) &lpMsgBuf, 0, NULL );
_tprintf( TEXT("Error in XcvData. Error msg : %s; XcvDataPort status val = %d"), lpMsgBuf, xcvresult);
LocalFree( lpMsgBuf );
return FALSE;
}
ClosePrinter(hPrinter);
return TRUE;
}
The highlight is both the functions (openprinter and xcvdata) succeed. But the port is not getting removed. I am completely at a loss here as I dont have any error to lookup.
Instead of ,XcvMonitor Standard TCP/IP Port I also tried with ,XcvPort <portname>. Still same.
As Samer suggested below, I tried with OpenPrinter2 with no cache option.
PS: I know there's this simple alternative DeletePort(), but it invokes a UI dialog box if it fails, so I don't want to use it.
It seems the issue might be related to specific version of OS which caches printer handles. To get around this you use an alternate call OpenPrinter2 with the PRINTER_OPTION_NO_CACHE. Below is the modified code with the flag set.
HANDLE hPrinter;
PRINTER_DEFAULTS PrinterDefaults;
memset(&PrinterDefaults, 0, sizeof(PrinterDefaults));
PrinterDefaults.pDatatype = NULL;
PrinterDefaults.pDevMode = NULL;
PrinterDefaults.DesiredAccess = SERVER_ACCESS_ADMINISTER;
PRINTER_OPTIONS PrinterOptions;
PrinterOptions.cbSize = sizeof(PrinterOptions);
PrinterOptions.dwFlags = PRINTER_OPTION_NO_CACHE;
DWORD needed = 0;
DWORD rslt = 0;
//Port data
PORT_DATA_1 pOutputData ;
DWORD error = 0;
if (!OpenPrinter2(L",XcvMonitor Standard TCP/IP Port", &hPrinter, &PrinterDefaults, &PrinterOptions))
{
LPVOID lpMsgBuf;
I am attempting to create a service that will essentially act as a local web server. In theory users will use a REST API by visiting URIs through localhost in a browser i.e. http://localhost:2017/path/to/function/call will connect to the service and execute a function.
My question is how do I get the SID and User Name of the account that called the service?
I have implemented a couple of solutions but they return the SID and User Name of the service and not the user using it.
OJSon* UnifiedStreamingService::getUserDetails()
{
OJSon* result = OJSon::create();
if(result)
{
/*
HANDLE hToken = NULL;
ULONG id = WTSGetActiveConsoleSessionId();
BOOL bRet = WTSQueryUserToken(id, &hToken);
if (bRet == false)
{
DWORD error = GetLastError();
printf("ERROR: %d", error);
}
*/
HANDLE hToken = NULL;
if ( ! OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &hToken ) )
{
//_tprintf( _T("OpenProcessToken failed. GetLastError returned: %d\n"), GetLastError());
return NULL;
}
// Get the size of the memory buffer needed for the SID
DWORD dwBufferSize = 0;
if ( ! GetTokenInformation( hToken, TokenUser, NULL, 0, &dwBufferSize ) && ( GetLastError() != ERROR_INSUFFICIENT_BUFFER ) )
{
//_tprintf( _T("GetTokenInformation failed. GetLastError returned: %d\n"), GetLastError());
// Cleanup
CloseHandle( hToken );
hToken = NULL;
return NULL;
}
// Allocate buffer for user token data
std::vector<BYTE> buffer;
buffer.resize( dwBufferSize );
PTOKEN_USER pTokenUser = reinterpret_cast<PTOKEN_USER>( &buffer[0] );
// Retrieve the token information in a TOKEN_USER structure
if ( ! GetTokenInformation(
hToken,
TokenUser,
pTokenUser,
dwBufferSize,
&dwBufferSize))
{
//_tprintf( _T("2 GetTokenInformation failed. GetLastError returned: %d\n"), GetLastError());
// Cleanup
CloseHandle( hToken );
hToken = NULL;
return NULL;
}
// Check if SID is valid
if ( ! IsValidSid( pTokenUser->User.Sid ) )
{
//_tprintf( _T("The owner SID is invalid.\n") );
// Cleanup
CloseHandle(hToken);
hToken = NULL;
return NULL;
}
// add the name
OString* name = lookupAccountSid(pTokenUser->User.Sid);
if(name)
{
result->setKey(&OString("name"), name);
SAFEDELETE(name);
}
// add the SID
OString* sid = convertSidToString(pTokenUser->User.Sid);
if(sid)
{
result->setKey(&OString("SID"), sid);
SAFEDELETE(sid);
}
// Cleanup
CloseHandle(hToken);
hToken = NULL;
}
return result;
}
OString* UnifiedStreamingService::convertSidToString(PSID pSID)
{
OString* result = NULL;
if(pSID)
{
// Get string corresponding to SID
LPTSTR pszSID = NULL;
if ( ! ConvertSidToStringSid( pSID, &pszSID ) )
{
return NULL;
}
result = new OString(pszSID);
// Release buffer allocated by ConvertSidToStringSid API
LocalFree( pszSID );
pszSID = NULL;
}
return result;
}
OString* UnifiedStreamingService::lookupAccountSid(PSID pSID)
{
DWORD dwSize = 256;
DWORD dwResult = 0;
SID_NAME_USE SidType;
LPTSTR lpName = new TCHAR[dwSize];
LPWSTR lpDomain = new TCHAR[dwSize];
OString* result = NULL;
if( !LookupAccountSid( NULL, pSID, lpName, &dwSize, lpDomain, &dwSize, &SidType ) )
{
dwResult = GetLastError();
return NULL;
}
OString* pDomain = new OString(lpDomain);
OString* pName = new OString(lpName);
if(pDomain && pName)
{
result = OString::createByFormat(&OString("%s\\%s"), pDomain, pName);
SAFEDELETE(pDomain);
SAFEDELETE(pName);
}
delete[] lpDomain;
delete[] lpName;
return result;
}
The task can be accomplished by using WTSGetActiveConsoleSessionId and WTSQueryUserToken to get user token and then getting SID with GetTokenInformation.
The additional requirement is the service is running under Local System account which grants SE_TCB_NAME privelege (== SeTcbPrivilege). SE_TCB_NAME required by WTSQueryUserToken. Note that the other accounts usually have no SE_TCB_NAME privelege!
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 };
I'm using this function to call an executable from my MSI. However the window for the executable hides behind my MSI window. Is there any way to bring it to the front?
I have tried minimizing all the windows just before the call to ShellExecute but that still does not bring the executable window to the front.
extern "C" UINT __stdcall InstallDrivers(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
HANDLE hFile = INVALID_HANDLE_VALUE;
BYTE* pbData = NULL;
DWORD cbData = 0;
char pwzFilename[MAX_PATH], szDriverType[MAX_PATH], pwzSentinelFilename[MAX_PATH], szIsInstalled[MAX_PATH];
LPWSTR szValueBuf = NULL, szIsHaspInstalled = NULL, szIsSentinelInstalled = NULL;
hr = WcaInitialize(hInstall, "InstallDrivers");
ExitOnFailure(hr, "Failed to initialize");
WcaLog(LOGMSG_STANDARD, "Initialized.");
WcaLog(LOGMSG_STANDARD, "%s", szValueBuf);
CreateDirectory("C:\\Temp", NULL);
strcpy_s(pwzFilename, "C:\\Temp\\HASPUserSetup.exe");
hr = ExtractBinary(L"Hasp", &pbData, &cbData);
ExitOnFailure(hr, "failed to extract binary data");
if ((hFile = CreateFile(pwzFilename, GENERIC_WRITE,FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
{
PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Could not create new temporary file"));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
return ERROR_INSTALL_USEREXIT;
}
DWORD cbWritten = 0;
if ( !WriteFile(hFile, pbData, cbData, &cbWritten, NULL) )
{
PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Could not write to file"));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
return ERROR_INSTALL_USEREXIT;
}
CloseHandle(hFile);
strcpy_s(pwzSentinelFilename, "C:\\Temp\\sentinel_setup.exe");
hr = ExtractBinary(L"Sentinel", &pbData, &cbData);
ExitOnFailure(hr, "failed to extract binary data");
if ((hFile = CreateFile(pwzSentinelFilename, GENERIC_WRITE,FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
{
PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Could not create new temporary file"));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
return ERROR_INSTALL_USEREXIT;
}
if ( !WriteFile(hFile, pbData, cbData, &cbWritten, NULL) )
{
PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Could not write to file"));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
return ERROR_INSTALL_USEREXIT;
}
CloseHandle(hFile);
hr = WcaGetProperty(L"DRIVER", &szValueBuf);
ExitOnFailure(hr, "failed to get driver info");
wcstombs(szDriverType, szValueBuf, 260);
if (strcmp(szDriverType, "Hasp") == 0)
{
hr = WcaGetProperty(L"SENTINELINSTALLED", &szIsSentinelInstalled);
ExitOnFailure(hr, "failed to get driver info");
wcstombs(szIsInstalled, szValueBuf, 260);
if (strcmp(szIsInstalled, "Sentinel Protection Installer 7.6.5") == 0)
{
ShellExecute(NULL, "open", pwzSentinelFilename, NULL, NULL, SW_SHOWNORMAL);
}
ShellExecute(NULL, "open", pwzFilename, NULL, NULL, SW_SHOWNORMAL);
}else
{
hr = WcaGetProperty(L"HASPINSTALLED", &szIsHaspInstalled);
ExitOnFailure(hr, "failed to get driver info");
wcstombs(szIsInstalled, szIsHaspInstalled, 260);
if (strcmp(szIsInstalled, "Sentinel Runtime") == 0)
{
ShellExecute(NULL, "open", pwzFilename, NULL, NULL, SW_SHOWNORMAL);
}
ShellExecute(NULL, "open", pwzSentinelFilename, NULL, NULL, SW_SHOWNORMAL);
}
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
Updated Code:
extern "C" UINT __stdcall InstallDrivers(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
HANDLE hFile = INVALID_HANDLE_VALUE;
BYTE* pbData = NULL;
DWORD cbData = 0;
char pwzFilename[MAX_PATH], szDriverType[MAX_PATH], pwzSentinelFilename[MAX_PATH], szIsInstalled[MAX_PATH];
LPWSTR szValueBuf = NULL, szIsHaspInstalled = NULL, szIsSentinelInstalled = NULL;
SHELLEXECUTEINFO ShExecInfo;
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ShExecInfo.fMask = NULL;
ShExecInfo.hwnd = NULL;
ShExecInfo.lpVerb = NULL;
ShExecInfo.lpParameters = NULL;
ShExecInfo.lpDirectory = NULL;
ShExecInfo.nShow = SW_SHOWNORMAL;
ShExecInfo.hInstApp = NULL;
hr = WcaInitialize(hInstall, "InstallDrivers");
ExitOnFailure(hr, "Failed to initialize");
WcaLog(LOGMSG_STANDARD, "Initialized.");
WcaLog(LOGMSG_STANDARD, "%s", szValueBuf);
CreateDirectory("C:\\Temp", NULL);
strcpy_s(pwzFilename, "C:\\Temp\\HASPUserSetup.exe");
hr = ExtractBinary(L"Hasp", &pbData, &cbData);
ExitOnFailure(hr, "failed to extract binary data");
if ((hFile = CreateFile(pwzFilename, GENERIC_WRITE,FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
{
PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Could not create new temporary file"));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
return ERROR_INSTALL_USEREXIT;
}
DWORD cbWritten = 0;
if ( !WriteFile(hFile, pbData, cbData, &cbWritten, NULL) )
{
PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Could not write to file"));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
return ERROR_INSTALL_USEREXIT;
}
CloseHandle(hFile);
strcpy_s(pwzSentinelFilename, "C:\\Temp\\sentinel_setup.exe");
hr = ExtractBinary(L"Sentinel", &pbData, &cbData);
ExitOnFailure(hr, "failed to extract binary data");
if ((hFile = CreateFile(pwzSentinelFilename, GENERIC_WRITE,FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
{
PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Could not create new temporary file"));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
return ERROR_INSTALL_USEREXIT;
}
if ( !WriteFile(hFile, pbData, cbData, &cbWritten, NULL) )
{
PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Could not write to file"));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
return ERROR_INSTALL_USEREXIT;
}
CloseHandle(hFile);
hr = WcaGetProperty(L"DRIVER", &szValueBuf);
ExitOnFailure(hr, "failed to get driver info");
wcstombs(szDriverType, szValueBuf, 260);
if (strcmp(szDriverType, "Hasp") == 0)
{
hr = WcaGetProperty(L"SENTINELINSTALLED", &szIsSentinelInstalled);
ExitOnFailure(hr, "failed to get driver info");
wcstombs(szIsInstalled, szValueBuf, 260);
if (strcmp(szIsInstalled, "Sentinel Protection Installer 7.6.5") == 0)
{
AllowSetForegroundWindow(ASFW_ANY);
ShExecInfo.lpFile = pwzSentinelFilename;
ShellExecuteEx(&ShExecInfo);
/*ShellExecute(NULL, "open", pwzSentinelFilename, NULL, NULL, SW_SHOWNORMAL);*/
}
AllowSetForegroundWindow(ASFW_ANY);
ShExecInfo.lpFile = pwzFilename;
ShellExecuteEx(&ShExecInfo);
/*ShellExecute(NULL, "open", pwzFilename, NULL, NULL, SW_SHOWNORMAL);*/
}else
{
hr = WcaGetProperty(L"HASPINSTALLED", &szIsHaspInstalled);
ExitOnFailure(hr, "failed to get driver info");
wcstombs(szIsInstalled, szIsHaspInstalled, 260);
if (strcmp(szIsInstalled, "Sentinel Runtime") == 0)
{
AllowSetForegroundWindow(ASFW_ANY);
/*ShellExecute(NULL, "open", pwzFilename, NULL, NULL, SW_SHOWNORMAL);*/
ShExecInfo.lpFile = pwzFilename;
ShellExecuteEx(&ShExecInfo);
}
AllowSetForegroundWindow(ASFW_ANY);
ShExecInfo.lpFile = pwzSentinelFilename;
ShellExecuteEx(&ShExecInfo);
/* ShellExecute(NULL, "open", pwzSentinelFilename, NULL, NULL, SW_SHOWNORMAL);*/
}
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
Windows doesn't permit processes to snatch the foreground window unless the user starts them. This is to avoid things like persuading the user to type their bank details into the wrong window. However the current foreground process can pass permission to another process to do this. See AllowSetForegroundWindow for the details. To do this you have to provide the process id for the process that will become foreground and ShellExecute doesn't provide that. However, if you switch to using ShellExecuteEx you can get this from the hProcess member on the SHELLEXECUTEINFO structure.
You can then call SetForegroundWindow in your new process and it will be permitted. Otherwise it just starts flashing on the taskbar.
EDIT
If your initial application is the foreground application and you start a subprocess from that then the subprocess should automatically become foreground as described in the documentation for these functions.
Below is an example of how we can choose to enable and set another application to become the foreground window. In this case it just creates a text file and calls ShellExecuteEx with the open verb. We have to wait for the child process to get going and to have its window prepared and then we can locate the window from the process ID and give it permissions and set its window to be foreground. In this case the launched notepad (or whatever is your "open" verb for .txt files) will be foreground anyway. If you separately run a notepad process and substiture in the process ID for that process where we normally put in the child process ID then we can make another process become foreground -- one that is not part of our process tree. This can be compiled using Visual C++ with cl -nologo -W3 -O2 -MD fg_test.cpp to produce an fg_test.exe.
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <shellapi.h>
#pragma comment(lib, "shell32")
#pragma comment(lib, "user32")
static void PrintError(LPCTSTR szPrefix, DWORD dwError);
static BOOL CALLBACK OnGetWindowByProcess(HWND hwnd, LPARAM lParam);
typedef struct {
DWORD pid;
HWND hwnd;
} WINDOWPROCESSINFO;
int _tmain(int argc, TCHAR *argv[])
{
SHELLEXECUTEINFO sxi = {0};
sxi.cbSize = sizeof(sxi);
sxi.nShow = SW_SHOWNORMAL;
FILE *fp = NULL;
_tfopen_s(&fp, _T("junk.txt"), _T("wt"));
_fputts(_T("Example text file\n"), fp);
fclose(fp);
sxi.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NOASYNC | SEE_MASK_WAITFORINPUTIDLE;
sxi.lpVerb = _T("open");
sxi.lpFile = _T("junk.txt");
if (!ShellExecuteEx(&sxi))
PrintError(_T("ShellExecuteEx"), GetLastError());
else
{
WINDOWPROCESSINFO info;
info.pid = GetProcessId(sxi.hProcess); // SPECIFY PID
info.hwnd = 0;
AllowSetForegroundWindow(info.pid);
EnumWindows(OnGetWindowByProcess, (LPARAM)&info);
if (info.hwnd != 0)
{
SetForegroundWindow(info.hwnd);
SetActiveWindow(info.hwnd);
}
CloseHandle(sxi.hProcess);
}
return 0;
}
static BOOL CALLBACK OnGetWindowByProcess(HWND hwnd, LPARAM lParam)
{
WINDOWPROCESSINFO *infoPtr = (WINDOWPROCESSINFO *)lParam;
DWORD check = 0;
BOOL br = TRUE;
GetWindowThreadProcessId(hwnd, &check);
_tprintf(_T("%x %x %x\n"), hwnd, check, infoPtr->pid);
if (check == infoPtr->pid)
{
_tprintf(_T("found window %x for process id %x\n"), hwnd, check);
infoPtr->hwnd = hwnd;
br = FALSE;
}
return br;
}
static void
PrintError(LPCTSTR szPrefix, DWORD dwError)
{
LPTSTR lpsz = NULL;
DWORD cch = 0;
cch = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dwError, LANG_NEUTRAL, (LPTSTR)&lpsz, 0, NULL);
if (cch < 1) {
cch = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_STRING
| FORMAT_MESSAGE_ARGUMENT_ARRAY,
_T("Code 0x%1!08x!"),
0, LANG_NEUTRAL, (LPTSTR)&lpsz, 0,
(va_list*)&dwError);
}
_ftprintf(stderr, _T("%s: %s"), szPrefix, lpsz);
LocalFree((HLOCAL)lpsz);
}
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?