Windows の API でプロセスを作成する CreateProcess や CreateProcessWithLogonW などで作成した時に
Vista 以降では一般ユーザーから管理者ユーザなどの上位権限で稼動しているプロセスを終了できてしまうので回避方法のメモ
ソースとプロジェクトファイル一式 CreateProcessTest20130713.zip
プロセス作成時のプロセスとスレッドに対する SECURITY_ATTRIBUTES を設定しなかった場合に
Vista 以降はログオン SIDの ACL が DACL に追加されるようになっているようです。
プロセスをサスペンド状態(CREATE_SUSPENDED)で作成して
DACL からログオンセッションの ACL を削除して ResumeThread することで同一ログオン SID のプロセスからでも終了できなくなります。
なお呼び出し元から PROCESS_INFORMATION.hProcess を使用して終了させることはできます。
手順としては、
現在のログオン SID(S-1-5-5-aaa-bbb) が必要
参照 URL: http://msdn.microsoft.com/en-us/library/windows/desktop/aa446670%28v=vs.85%29.aspx実際に作成した子プロセスのプロセスハンドルとスレッドハンドルの DACL を取得して
ログオン SID の ACCESS_ALLOWED_ACE を削除して上書き設定
でログオン SID の許可を削除できます。
ソースコードはこんな感じになっています。
/**
* The MIT License
*
* Copyright (C) 2013 KK.Kon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
//#include "stdafx.h"
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0502
#endif
#include <windows.h>
#include <tchar.h>
#if 1
#define MyDbgOutput() \
{ \
const DWORD dwErr = ::GetLastError(); \
wchar_t temp[1024]; \
::wsprintfW( temp, L"%s(%u): 0x%08X\n", __FILE__, __LINE__, dwErr ); \
::OutputDebugStringW( temp ); \
}
#else
#define MyDbgOutput()
#endif
// LogonSID http://msdn.microsoft.com/en-us/library/windows/desktop/aa446670%28v=vs.85%29.aspx
bool
allocLogonSID( PSID *ppSID )
{
bool result = false;
HANDLE hToken = NULL;
{
const BOOL BRet = ::OpenProcessToken( ::GetCurrentProcess(), TOKEN_QUERY, &hToken );
if ( FALSE == BRet )
{
MyDbgOutput();
return false;
}
}
*ppSID = NULL;
const HANDLE hHeap = ::GetProcessHeap();;
if ( NULL != hToken )
{
TOKEN_GROUPS * pTokenGroups = NULL;
DWORD nLength = 0;
DWORD nLengthNeeded = 0;
::GetTokenInformation( hToken, TokenGroups, pTokenGroups, nLength, &nLengthNeeded );
{
pTokenGroups = reinterpret_cast<TOKEN_GROUPS *>( ::HeapAlloc( hHeap, HEAP_ZERO_MEMORY, nLengthNeeded+16 ) );
nLength = nLengthNeeded;
nLengthNeeded = 0;
const BOOL BRet = ::GetTokenInformation( hToken, TokenGroups, pTokenGroups, nLength, &nLengthNeeded );
if ( FALSE == BRet )
{
MyDbgOutput();
}
if ( NULL != pTokenGroups )
{
for ( size_t index = 0; index < pTokenGroups->GroupCount; ++index )
{
if ( SE_GROUP_LOGON_ID == (SE_GROUP_LOGON_ID & pTokenGroups->Groups[index].Attributes) )
{
PSID pSIDSrc = pTokenGroups->Groups[index].Sid;
const size_t nLengthSID = ::GetLengthSid( pSIDSrc );
PSID pSIDDest = reinterpret_cast<PSID>( ::HeapAlloc( hHeap, HEAP_ZERO_MEMORY, (nLengthSID+16) ) );
if ( NULL == pSIDDest )
{
MyDbgOutput();
}
else
{
const BOOL BRet = ::CopySid( nLength, pSIDDest, pSIDSrc );
if ( FALSE == BRet )
{
MyDbgOutput();
::HeapFree( hHeap, 0, pSIDDest );
pSIDDest = NULL;
}
else
{
*ppSID = pSIDDest;
result = true;
}
}
break;
}
}
}
if ( NULL != pTokenGroups )
{
::HeapFree( hHeap, 0, pTokenGroups );
pTokenGroups = NULL;
}
}
}
if ( NULL != hToken )
{
::CloseHandle( hToken );
}
return result;
}
bool
freeLogonSID( PSID * ppSID )
{
if ( NULL == ppSID )
{
return false;
}
bool result = false;
if ( NULL != ppSID )
{
const HANDLE hHeap = ::GetProcessHeap();
const BOOL BRet = ::HeapFree( hHeap, 0, *ppSID );
if ( BRet )
{
*ppSID = NULL;
result = true;
}
}
return result;
}
#include <sddl.h> // ConvertSidToStringSid
void
dumpAccessAllowedACE( ACCESS_ALLOWED_ACE * pAce )
{
{
wchar_t temp[2048];
::wsprintfW( temp, L"ACCESS_ALLOWED_ACE %p\n", pAce );
::OutputDebugStringW( temp );
}
wchar_t szName[256];
DWORD cchName = sizeof(szName)/sizeof(szName[0]);
wchar_t szDomainName[256];
DWORD cchDomainName = sizeof(szDomainName)/sizeof(szName[0]);
SID_NAME_USE snu;
szName[0] = '\0';
szDomainName[0] = '\0';
const BOOL BRet = ::LookupAccountSidW(
NULL
, &(pAce->SidStart)
, szName
, &cchName
, szDomainName
, &cchDomainName
, &snu
);
{
wchar_t temp[2048];
::wsprintfW( temp, L" Mask: 0x%08X\n", pAce->Mask );
::OutputDebugStringW( temp );
}
if ( BRet )
{
wchar_t temp[2048];
::wsprintfW( temp, L" Name:%s DomainName:%s\n", szName, szDomainName );
::OutputDebugStringW( temp );
}
{
LPWSTR pStrSID = NULL;
::ConvertSidToStringSidW( &(pAce->SidStart), &pStrSID );
wchar_t temp[2048];
::wsprintfW( temp, L" SID: %s\n", pStrSID );
::OutputDebugStringW( temp );
::LocalFree( reinterpret_cast<HLOCAL>(pStrSID) );
}
}
bool
handleDetachFromLogonSID( HANDLE hHandle )
{
bool result = false;
__try
{
const HANDLE hHeap = ::GetProcessHeap();;
PSID pLogonSID = NULL;
allocLogonSID( &pLogonSID );
{
SECURITY_INFORMATION securityInfo = DACL_SECURITY_INFORMATION;
SECURITY_DESCRIPTOR * pSD = NULL;
DWORD nLength = 0;
DWORD nLengthNeeded = 0;
::GetUserObjectSecurity( hHandle, &securityInfo, pSD, nLength, &nLengthNeeded );
pSD = reinterpret_cast<SECURITY_DESCRIPTOR *>(::HeapAlloc( hHeap, HEAP_ZERO_MEMORY, (nLengthNeeded+16) ));
nLength = nLengthNeeded;
nLengthNeeded = 0;
{
const BOOL BRet = ::GetUserObjectSecurity( hHandle, &securityInfo, pSD, nLength, &nLengthNeeded );
if ( FALSE == BRet )
{
MyDbgOutput();
}
}
if ( NULL != pSD )
{
bool needContinue = true;
while( needContinue )
{
BOOL isPresent = FALSE;
ACL * pACL = NULL;
BOOL isDefault = FALSE;
const BOOL BRet = ::GetSecurityDescriptorDacl( pSD, &isPresent, &pACL, &isDefault );
if ( FALSE == BRet )
{
MyDbgOutput();
needContinue = false;
}
if ( NULL != pACL )
{
DWORD index = 0;
const DWORD count = pACL->AceCount;
for ( index = 0; index < count; index++ )
{
bool needBreak = false;
LPVOID pRaw = NULL;
const BOOL BRet = ::GetAce( pACL, index, &pRaw );
if ( FALSE == BRet )
{
MyDbgOutput();
}
else
if ( NULL != pRaw )
{
ACE_HEADER * pHeader = reinterpret_cast<ACE_HEADER *>(pRaw);
switch( pHeader->AceType )
{
case ACCESS_ALLOWED_ACE_TYPE:
{
ACCESS_ALLOWED_ACE * pAce = reinterpret_cast<ACCESS_ALLOWED_ACE *>(pRaw);
dumpAccessAllowedACE( pAce );
if ( ::EqualSid( &(pAce->SidStart), pLogonSID ) )
{
const BOOL BRet = ::DeleteAce( pACL, index );
if ( FALSE == BRet )
{
MyDbgOutput();
}
else
{
needBreak = true;
}
}
}
break;
}
}
if ( needBreak )
{
break;
}
} // for
if ( count <= index )
{
needContinue = false;
}
}
} // while ( needContinue )
}
if ( NULL != pSD )
{
const BOOL BRet = ::SetUserObjectSecurity( hHandle, &securityInfo, pSD );
if ( FALSE == BRet )
{
MyDbgOutput();
}
else
{
result = true;
}
}
::HeapFree( hHeap, 0, pSD );
}
freeLogonSID( &pLogonSID );
}
__except ( EXCEPTION_CONTINUE_EXECUTION )
{
}
return result;
}
bool
test( LPCWSTR pUsername, LPCWSTR pDomain, LPCWSTR pPassword, const BOOL detachFromLogonSession )
{
const DWORD dwLogonFlags = LOGON_WITH_PROFILE;
LPCWSTR pApplicationName = L"C:\\Windows\\System32\\cmd.exe";
LPWSTR pCommandLine = NULL;
const DWORD dwCreationFlags = CREATE_NEW_PROCESS_GROUP|CREATE_SUSPENDED; //DETACHED_PROCESS;
LPVOID pEnvironment = NULL;
LPCWSTR pCurrentDirectory = NULL;
STARTUPINFOW startupInfo;
PROCESS_INFORMATION processInfo;
ZeroMemory( &startupInfo, sizeof(startupInfo) );
ZeroMemory( &processInfo, sizeof(processInfo) );
wchar_t currentDir[1024];
::GetCurrentDirectoryW( sizeof(currentDir), currentDir );
pCurrentDirectory = currentDir;
const HANDLE hHeap = ::GetProcessHeap();;
const BOOL BRet = ::CreateProcessWithLogonW(
pUsername
, pDomain
, pPassword
, dwLogonFlags
, pApplicationName
, pCommandLine
, dwCreationFlags
, pEnvironment
, pCurrentDirectory
, &startupInfo
, &processInfo
);
if ( FALSE == BRet )
{
MyDbgOutput();
}
else
{
if ( detachFromLogonSession )
{
handleDetachFromLogonSID( processInfo.hProcess );
handleDetachFromLogonSID( processInfo.hThread );
}
::ResumeThread( processInfo.hThread );
#if 0
{
const BOOL BRet = ::TerminateProcess( processInfo.hProcess, 0 );
if ( FALSE == BRet )
{
MyDbgOutput();
}
}
#endif
::CloseHandle( processInfo.hProcess );
::CloseHandle( processInfo.hThread );
}
return false;
}
bool
test2( const BOOL detachFromLogonSession )
{
const DWORD dwLogonFlags = LOGON_WITH_PROFILE;
LPCWSTR pApplicationName = L"C:\\Windows\\System32\\cmd.exe";
LPWSTR pCommandLine = NULL;
LPSECURITY_ATTRIBUTES pProcessAttributes = NULL;
LPSECURITY_ATTRIBUTES pThreadAttributes = NULL;
BOOL bInheritHandles = FALSE;
const DWORD dwCreationFlags = CREATE_NEW_PROCESS_GROUP|CREATE_SUSPENDED; //DETACHED_PROCESS;
LPVOID pEnvironment = NULL;
LPCWSTR pCurrentDirectory = NULL;
STARTUPINFOW startupInfo;
PROCESS_INFORMATION processInfo;
ZeroMemory( &startupInfo, sizeof(startupInfo) );
ZeroMemory( &processInfo, sizeof(processInfo) );
wchar_t currentDir[1024];
::GetCurrentDirectoryW( sizeof(currentDir), currentDir );
pCurrentDirectory = currentDir;
const HANDLE hHeap = ::GetProcessHeap();;
const BOOL BRet = ::CreateProcessW(
pApplicationName
, pCommandLine
, pProcessAttributes
, pThreadAttributes
, bInheritHandles
, dwCreationFlags
, pEnvironment
, pCurrentDirectory
, &startupInfo
, &processInfo
);
if ( FALSE == BRet )
{
MyDbgOutput();
}
else
{
if ( detachFromLogonSession )
{
handleDetachFromLogonSID( processInfo.hProcess );
handleDetachFromLogonSID( processInfo.hThread );
}
::ResumeThread( processInfo.hThread );
#if 0
{
const BOOL BRet = ::TerminateProcess( processInfo.hProcess, 0 );
if ( FALSE == BRet )
{
MyDbgOutput();
}
}
#endif
::CloseHandle( processInfo.hProcess );
::CloseHandle( processInfo.hThread );
}
return false;
}
int _tmain(int argc, _TCHAR* argv[])
{
if ( argc <= 1)
{
return 1;
}
const wchar_t szUsernameUp[] = NULL; //L"";
const wchar_t szDomainnameUp[] = NULL; //L"";
const wchar_t szPasswordUp[] = NULL; //L"";
const wchar_t szUsernameDown[] = NULL; //L"";
const wchar_t szDomainnameDown[] = NULL; //L"";
const wchar_t szPasswordDown[] = NULL; //L"";
if ( NULL != argv[1] )
{
if ( 0 == _tcsicmp( argv[1], _T("up") ) )
{
const wchar_t * szUsername = szUsernameUp;
const wchar_t * szDomainname = szDomainnameUp;
const wchar_t * szPassword = szPasswordUp;
test( szUsername, szDomainname, szPassword, false );
}
else
if ( 0 == _tcsicmp( argv[1], _T("updetach") ) )
{
const wchar_t * szUsername = szUsernameUp;
const wchar_t * szDomainname = szDomainnameUp;
const wchar_t * szPassword = szPasswordUp;
test( szUsername, szDomainname, szPassword, true );
}
else
if ( 0 == _tcsicmp( argv[1], _T("down") ) )
{
const wchar_t * szUsername = szUsernameDown;
const wchar_t * szDomainname = szDomainnameDown;
const wchar_t * szPassword = szPasswordDown;
test( szUsername, szDomainname, szPassword, false );
}
else
if ( 0 == _tcsicmp( argv[1], _T("downdetach") ) )
{
const wchar_t * szUsername = szUsernameDown;
const wchar_t * szDomainname = szDomainnameDown;
const wchar_t * szPassword = szPasswordDown;
test( szUsername, szDomainname, szPassword, true );
}
else
if ( 0 == _tcsicmp( argv[1], _T("detach") ) )
{
test2( true );
}
}
return 0;
}