//提升权限
bool EnableSpecificPrivilege(LPCTSTR lpPrivilegeName)
{
HANDLE hToken = NULL;
TOKEN_PRIVILEGES Token_Privilege;
BOOL bRet = TRUE;
do
{
if (0 == OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
OutputDebugView(_T("OpenProcessToken Error"));
bRet = FALSE;
break;
}
if (0 == LookupPrivilegeValue(NULL, lpPrivilegeName, &Token_Privilege.Privileges[0].Luid))
{
OutputDebugView(_T("LookupPrivilegeValue Error"));
bRet = FALSE;
break;
}
Token_Privilege.PrivilegeCount = 1;
Token_Privilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
//Token_Privilege.Privileges[0].Luid.LowPart=17;//SE_BACKUP_PRIVILEGE
//Token_Privilege.Privileges[0].Luid.HighPart=0;
if (0 == AdjustTokenPrivileges(hToken, FALSE, &Token_Privilege, sizeof(Token_Privilege), NULL,NULL))
{
OutputDebugView(_T("AdjustTokenPrivileges Error"));
bRet = FALSE;
break;
}
} while (false);
if (NULL != hToken)
{
CloseHandle(hToken);
}
return bRet;
}
lpPrivilegeName 取值:
#define SE_CREATE_TOKEN_NAME TEXT("SeCreateTokenPrivilege")#define SE_ASSIGNPRIMARYTOKEN_NAME TEXT("SeAssignPrimaryTokenPrivilege")#define SE_LOCK_MEMORY_NAME TEXT("SeLockMemoryPrivilege")#define SE_INCREASE_QUOTA_NAME TEXT("SeIncreaseQuotaPrivilege")#define SE_UNSOLICITED_INPUT_NAME TEXT("SeUnsolicitedInputPrivilege")#define SE_MACHINE_ACCOUNT_NAME TEXT("SeMachineAccountPrivilege")#define SE_TCB_NAME TEXT("SeTcbPrivilege")#define SE_SECURITY_NAME TEXT("SeSecurityPrivilege")#define SE_TAKE_OWNERSHIP_NAME TEXT("SeTakeOwnershipPrivilege")#define SE_LOAD_DRIVER_NAME TEXT("SeLoadDriverPrivilege")#define SE_SYSTEM_PROFILE_NAME TEXT("SeSystemProfilePrivilege")#define SE_SYSTEMTIME_NAME TEXT("SeSystemtimePrivilege")#define SE_PROF_SINGLE_PROCESS_NAME TEXT("SeProfileSingleProcessPrivilege")#define SE_INC_BASE_PRIORITY_NAME TEXT("SeIncreaseBasePriorityPrivilege")#define SE_CREATE_PAGEFILE_NAME TEXT("SeCreatePagefilePrivilege")#define SE_CREATE_PERMANENT_NAME TEXT("SeCreatePermanentPrivilege")#define SE_BACKUP_NAME TEXT("SeBackupPrivilege")#define SE_RESTORE_NAME TEXT("SeRestorePrivilege")#define SE_SHUTDOWN_NAME TEXT("SeShutdownPrivilege")#define SE_DEBUG_NAME TEXT("SeDebugPrivilege")#define SE_AUDIT_NAME TEXT("SeAuditPrivilege")#define SE_SYSTEM_ENVIRONMENT_NAME TEXT("SeSystemEnvironmentPrivilege")#define SE_CHANGE_NOTIFY_NAME TEXT("SeChangeNotifyPrivilege")#define SE_REMOTE_SHUTDOWN_NAME TEXT("SeRemoteShutdownPrivilege")#define SE_UNDOCK_NAME TEXT("SeUndockPrivilege")#define SE_SYNC_AGENT_NAME TEXT("SeSyncAgentPrivilege")#define SE_ENABLE_DELEGATION_NAME TEXT("SeEnableDelegationPrivilege")#define SE_MANAGE_VOLUME_NAME TEXT("SeManageVolumePrivilege")#define SE_IMPERSONATE_NAME TEXT("SeImpersonatePrivilege")#define SE_CREATE_GLOBAL_NAME TEXT("SeCreateGlobalPrivilege")#define SE_TRUSTED_CREDMAN_ACCESS_NAME TEXT("SeTrustedCredManAccessPrivilege")#define SE_RELABEL_NAME TEXT("SeRelabelPrivilege")#define SE_INC_WORKING_SET_NAME TEXT("SeIncreaseWorkingSetPrivilege")#define SE_TIME_ZONE_NAME TEXT("SeTimeZonePrivilege")#define SE_CREATE_SYMBOLIC_LINK_NAME TEXT("SeCreateSymbolicLinkPrivilege")
Note:访问令牌说简单了就是个访问权限的数据集合,令牌中包含用户所有的权限,校验令牌可以识别用户是否有权限访问他要访问的位置
LookupPrivilegeValue
函数查看系统权限的特权值,返回信息到一个LUID结构体里。
BOOL LookupPrivilegeValue(LPCTSTR lpSystemName,LPCTSTR lpName,PLUID lpLuid);
第一个参数表示所要查看的系统,本地系统直接用NULL
第二个参数表示所要查看的特权信息的名称,定义在winnt.h中,具体指请MSDN索引“windows nt privileges”
第三个参数用来接收所返回的制定特权名称的信息。
函数调用成功后,信息存入第三个类型为LUID的结构体中,并且函数返回非0。
函数定义在winbase.h中,链接使用advapi32.lib库。
AdjustTokenPrivileges 函数启用或禁止 指定访问令牌的特权。
启用或禁用特权一个有TOKEN_ADJUST_PRIVILEGES访问的访问令牌.
BOOL AdjustTokenPrivileges(
HANDLE
TokenHandle
, //包含特权的句柄
BOOL
DisableAllPrivileges
,
//禁用所有权限标志
PTOKEN_PRIVILEGES
NewState
,
//新特权信息的指针(结构体)
DWORD
BufferLength
, //大小,以字节为单位的PreviousState的缓存区(sizeof)
PTOKEN_PRIVILEGES
PreviousState
,
//接收被改变特权当前状态的Buffer
PDWORD
ReturnLength//接收PreviousState缓存区要求的大小
);
参数
TokenHandle
包含要修改特权的访问令牌的标识(句柄).这个句柄必须有TOKEN_ADJUST_PRIVILEGES访问令牌.如果PreviousState不是NULL,这个句柄还必须有TOKEN_QUERY访问特权.
DisableAllPrivileges
标志这个函数是否禁用该令牌的所有特权.如果为TRUE,这个函数禁用所有特权,NewState参数无效.如果为假,以NewState参数指针的信息为基础来修改特权.
NewState
一个
TOKEN_PRIVILEGES结构体的指针指定了一组特权和他们的属性.
如果参数DisableAllPrivileges为FALSE,
AdjustTokenPrivileges启用或禁用这些令牌的特权.
如果你给一个特权设置了SE_PRIVILEGE_ENABLED的属性,这个函数将启动特权,否则禁用特权.
如果DisableAllPrivileges为TRUE,这个参数无效.
BufferLength
标志参数PreviousState指针以字节大小缓存区(sizeof).
如果参数PreviousState是NULL,这个参数可以为NULL.
PreviousState
这个函数填充一个TOKEN_PRIVILEGES结构体【指针】,它包括该函数修改之前任何特权状态.这个参数可以为NULL.
如果指定的缓冲区太小,无法收到完整的修改权限列表,这个函数失败并不会修改任何特权.
这个函数设置了一个 拥有修改权限完成列表【 参数ReturnLength 】的字节数 的指针变量.[结果的Buffer]
ReturnLength
接收 参数PreviousState的缓存区指针的 字节大小 的 变量指针(长度指针).
如果PreviousState为NULL,这个参数可以为NULL.
返回值
如果这个函数成功,返回非0.为了确定这个函数是否修改了所有指定的特权,可以调用
GetLastError函数,当这个函数返回下面的值之一时就代表函数成功:
提权函数之 RtlAdjustPrivilege()
RtlAdjustPrivilege() 这玩意是在 NTDLL.DLL 里的一个不为人知的函数,MS没有公开,原因就是这玩意实在是太NB了,以至于不需要任何其他函数的帮助,仅凭这一个函数就可以获得进程ACL的任意权限!下面是函数定义:NTSTATUS RtlAdjustPrivilege( ULONG Privilege, BOOLEANEnable, BOOLEANCurrentThread, PBOOLEANEnabled)参数的含义:Privilege [In] Privilege index to change. // 所需要的权限名称,可以到 MSDN 查找关于 Process Token & Privilege 内容可以查到Enable [In] If TRUE, then enable the privilege otherwise disable. // 如果为True 就是打开相应权限,如果为False 则是关闭相应权限CurrentThread [In] If TRUE, then enable in calling thread, otherwise process. // 如果为True 则仅提升当前线程权限,否则提升整个进程的权限Enabled [Out] Whether privilege was previously enabled or disabled.// 输出原来相应权限的状态(打开 | 关闭), 注意:该参数赋予空指针会出错,我测试过。eg:RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE,1,0,NULL);比用 AdjustTokenPrivileges 来提升进程权限方便很多,所以自己整理下备忘这个函数封装在NtDll.dll中(在所有DLL加载之前加载),被微软严格保密,就是说你在MSDN上查不到关于他的任何信息。
常用:常量 SE_BACKUP_PRIVILEGE = 0x11h常量 SE_RESTORE_PRIVILEGE = 0x12h常量 SE_SHUTDOWN_PRIVILEGE = 0x13h常量 SE_DEBUG_PRIVILEGE = 0x14h
全部:
//// These must be converted to LUIDs before use.//#define SE_MIN_WELL_KNOWN_PRIVILEGE (2L)#define SE_CREATE_TOKEN_PRIVILEGE (2L)#define SE_ASSIGNPRIMARYTOKEN_PRIVILEGE (3L)#define SE_LOCK_MEMORY_PRIVILEGE (4L)#define SE_INCREASE_QUOTA_PRIVILEGE (5L)#define SE_MACHINE_ACCOUNT_PRIVILEGE (6L)#define SE_TCB_PRIVILEGE (7L)#define SE_SECURITY_PRIVILEGE (8L)#define SE_TAKE_OWNERSHIP_PRIVILEGE (9L)#define SE_LOAD_DRIVER_PRIVILEGE (10L)#define SE_SYSTEM_PROFILE_PRIVILEGE (11L)#define SE_SYSTEMTIME_PRIVILEGE (12L)#define SE_PROF_SINGLE_PROCESS_PRIVILEGE (13L)#define SE_INC_BASE_PRIORITY_PRIVILEGE (14L)#define SE_CREATE_PAGEFILE_PRIVILEGE (15L)#define SE_CREATE_PERMANENT_PRIVILEGE (16L)#define SE_BACKUP_PRIVILEGE (17L)#define SE_RESTORE_PRIVILEGE (18L)#define SE_SHUTDOWN_PRIVILEGE (19L)#define SE_DEBUG_PRIVILEGE (20L)#define SE_AUDIT_PRIVILEGE (21L)#define SE_SYSTEM_ENVIRONMENT_PRIVILEGE (22L)#define SE_CHANGE_NOTIFY_PRIVILEGE (23L)#define SE_REMOTE_SHUTDOWN_PRIVILEGE (24L)#define SE_UNDOCK_PRIVILEGE (25L)#define SE_SYNC_AGENT_PRIVILEGE (26L)#define SE_ENABLE_DELEGATION_PRIVILEGE (27L)#define SE_MANAGE_VOLUME_PRIVILEGE (28L)#define SE_IMPERSONATE_PRIVILEGE (29L)#define SE_CREATE_GLOBAL_PRIVILEGE (30L)#define SE_TRUSTED_CREDMAN_ACCESS_PRIVILEGE (31L)#define SE_RELABEL_PRIVILEGE (32L)#define SE_INC_WORKING_SET_PRIVILEGE (33L)#define SE_TIME_ZONE_PRIVILEGE (34L)#define SE_CREATE_SYMBOLIC_LINK_PRIVILEGE (35L)#define SE_MAX_WELL_KNOWN_PRIVILEGE (SE_CREATE_SYMBOLIC_LINK_PRIVILEGE)
关机代码
#include <windows.h>const unsigned int SE_SHUTDOWN_PRIVILEGE = 0x13;int main(){HMODULE hDll = ::LoadLibrary("ntdll.dll");typedef int (* type_RtlAdjustPrivilege)(int, bool, bool, int*);typedef int (* type_ZwShutdownSystem)(int);type_RtlAdjustPrivilege RtlAdjustPrivilege = (type_RtlAdjustPrivilege)GetProcAddress(hDll, "RtlAdjustPrivilege");type_ZwShutdownSystem ZwShutdownSystem = (type_ZwShutdownSystem)GetProcAddress(hDll, "ZwShutdownSystem");int nEn = 0;int nResult = RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, true, true, &nEn);if(nResult == 0x0c000007c){nResult = RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, true, false, &nEn);}nResult = ZwShutdownSystem(2);FreeLibrary(hDll);return 0;}
==============================================
以不同用户身份运行程序
一直想方便的处理CCProxy代理的帐号管理,所以梦想做一个比较好的管理工具。但一个最麻烦的问题就是帐号的更新,CCProxy有一个网页管理功能,可以加帐号,但加的帐号就是不可以立即更新。中午上网的时候发现CCProxy有一功能就是支持命令行的操作,如:
CCProxy -reboot 重启软件
CCProxy -reset 更新配置
CCProxy -update 更新帐号
试着改了AccInfo.ini中帐号信息,在DOS中运行CCProxy -update的确更新了账号,所以开始用PHP做管理工具,做到调用CCProxy -update时,用了PHP中的exec(),system()等函数一直没有效果,后又通过调用批处理文件来调用命令行参数都不行。 处理得正没耐心的时候,一气之下狂刷新PHP网页,电脑卡死,用进程管理器查看时发现打开了多个CCProxy进程,认真一看,除了一个CCProxy是用户进程外其它CCProxy全是system进程。认真一想有可能是运行用户身份不同所产生的结果。
Apache服务调用的外部程序以system身份运行,自己双击运行的程序以用户身份运行。 如果CCProxy -update以用户身份运行是不是就可以了呢?本人在网络上找到了runas这个命令,的确可以指定以哪个用户运行,但是每次都要输密码,没有密码的帐号就要加上密码才可以用,“/savecred”这个参数可以用,只要输入一次密码就可以了,但在PHP中发现要以system的身份输入一次才行,根本没有机会输入。打算用C程序来处理这个问题。可是发现用WinExec(),ShellExecute(),CreateProcess()都不好处理这个问题,好在发现了CreateProcessAsUser()这个函数。把网络上的程序改了几处,编译后一试问题终于解决。
以下为相关代码:
// Update.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <windows.h>
#include <tlhelp32.h>
BOOL GetTokenByName(HANDLE &hToken,LPSTR lpName)
{
if(!lpName)
return FALSE;
HANDLE hProcessSnap = NULL;
BOOL bRet = FALSE;
PROCESSENTRY32 pe32 = {0};
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
return (FALSE);
pe32.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hProcessSnap, &pe32))
{
do
{
if(!strcmp(_strupr(pe32.szExeFile),_strupr(lpName)))
{
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,
FALSE,pe32.th32ProcessID);
bRet = OpenProcessToken(hProcess,TOKEN_ALL_ACCESS,&hToken);
CloseHandle (hProcessSnap);
return (bRet);
}
}
while (Process32Next(hProcessSnap, &pe32));
bRet = TRUE;
}
else
bRet = FALSE;
CloseHandle (hProcessSnap);
return (bRet);
}
BOOL RunProcess(LPCSTR lpImage,LPSTR lpCommandLine)
{
if(!lpImage)
return FALSE;
HANDLE hToken;
if(!GetTokenByName(hToken,"EXPLORER.EXE"))
return FALSE;
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb= sizeof(STARTUPINFO);
si.lpDesktop = TEXT("winsta0\\default");
BOOL bResult = CreateProcessAsUser(hToken,lpImage, lpCommandLine,NULL,NULL,
FALSE,NORMAL_PRIORITY_CLASS,NULL,NULL,&si,&pi);
CloseHandle(hToken);
if(bResult)
{
OutputDebugString("CreateProcessAsUser ok!\r\n");
printf("CreateProcessAsUser ok!\r\n");
}
else
{
OutputDebugString("CreateProcessAsUse* **lse!\r\n");
printf("CreateProcessAsUse* **lse!\r\n");
}
return bResult;
}
int _tmain(int argc, _TCHAR* argv[])
{
RunProcess("CCProxy.exe"," -update");
return 0;
}
=====================================================================
用CreateProcessAsUser 创建最低权限进程
我从msdn上找到的资料,你看看
高 管理权限(进程可以将文件安装到“Program Files”文件夹,并写入敏感注册表区域,如 HKEY_LOCAL_MACHINE。)
中 用户权限(进程可在用户的“文档”文件夹创建和修改文件,并写入用户指定的注册表区域,如 HKEY_CURRENT_USER。)
低 不受信任权限(进程只能写入低完整性位置,例如 Temporary Internet Files\Low 文件夹或 HKEY_CURRENT_USER\Software\LowRegistry key)
启动低完整性进程
1.重复中等完整性进程的处理。
2.使用 SetTokenInformation 将进程处理降低为低完整性。
3.使用 CreateProcessAsUser 创建使用低完整性处理的新进程。
uses
windows;
function ConvertStringSidToSidA(StringSid: LPCTSTR; Sid:TSIDIdentifierAuthority): BOOL; stdcall; external 'Advapi32.dll' name 'ConvertStringSidToSidA';
const
SE_GROUP_INTEGRITY = 1;
#include "winnt.h"
BOOL b;
HANDLE hToken;
HANDLE hNewToken;
PWSTR szProcessName = "LowClient"; // 例如
PWSTR szIntegritySid = "S-1-16-4096"; // 低完整性 SID
PSID pIntegritySid = NULL;
TOKEN_MANDATORY_LABEL TIL = {0};
PROCESS_INFORMATION ProcInfo = {0};
STARTUPINFO StartupInfo = {0};
ULONG ExitCode = 0;
b = OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED,
&hToken);
b = DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL,
SecurityImpersonation, TokenPrimary, &hNewToken);
b = ConvertStringSidToSid(szIntegritySid, &pIntegritySid);
TIL.Label.Attributes = SE_GROUP_INTEGRITY;
TIL.Label.Sid = pIntegritySid;
// 设置进程完整性级别
b = SetTokenInformation(hNewToken, TokenIntegrityLevel, &TIL,
sizeof(TOKEN_MANDATORY_LABEL) + RtlLengthSid(pIntegritySid));
// 设置进程的 UI 权限级别
b = SetTokenInformation(hNewToken, TokenIntegrityLevelDesktop,
&TIL, sizeof(TOKEN_MANDATORY_LABEL) + RtlLengthSid(pIntegritySid));
// 以低完整性创建新进程
b = CreateProcessAsUser(hNewToken, NULL, szProcessName, NULL, NULL,
FALSE, 0, NULL, NULL, &StartupInfo, &ProcInfo);
实际中遇到的问题描:SKype在一个本地登录帐户里只能运行一个实例,我需要运行多个实例,那么就需要以其他帐户的身份运行skype
有个方法:
调用runas.exe程序。。
只需要以runas /user:anotheruser "password" whatisgoingtorun
运行一个新进程就解决了
而windows默认的runas.exe是交互式输入密码, 看雪上有人将它改为了参数式输入密码(即上面的形式)
详见看雪论坛
以下是该帖子网址:
http://www.pediy.com/bbshtml/bbs8/pediy8-45.htm
============================================================
微软从XP/2003开始为我们提供了一套Windows Terminal Service 的相关API,这些API都以WTS开头(请安装MSDN2005以查阅相关说明),要获得活动Session也不止一个途径,最简单的就是直接使用
DWORD WTSGetActiveConsoleSessionId(void);
来获得活动Session Id 。要在程序中使用这些API需要最新的Platform SDK(如果你正在使用Visual Studio 2005那么它已经具备了相关头文件和库文件可以直接使用了),如果你在使用VC++ 6.0 你也没有或者不打算安装最新的SDK那么你可以直接使用LoadLibrary() 装载wtsapi32.dll然后使用GetProcAddress()获得相关函数的地址以调用它们。我们获得了活动SessionId后就可以使用
BOOL WTSQueryUserToken(
ULONG SessionId,
PHANDLE phToken
);
来获取当前活动Session中的用户令牌(Token),有了这个Token我们的就可以在活动Session中创建新进程了,
BOOL CreateProcessAsUser(
HANDLE hToken,
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
将我们获得的Token作为此API的第一个参数即可,你可以先尝试一下运行一个notepad.exe看看,怎么样?你可以在控制台桌面上看到新进程了。再查看一下进程列表,该进程的用户名是当前控制台登录的用户。可是这里我们又遇到一个问题,我们需要收集当前交本机互式登录用户的一些信息,而有些操作需要很高的权限才能完成,而Vista下即使是Administraotrs用户组成员默认也是以Users权限启动进程的,所以我们创建的新进程只有Users权限,无法完成一些操作,当然我们可以使用Vista所提供的UI来询问用户以提升至管理员权限,可有些操作甚至是管理员Token也无法完成的,而且需要用户确认实在在易用性上大打折扣,所以我决定在活动Session中以SYSTEM权限启动我们的用户交互程序。显然 WTSQueryUserToken() 是不好用了。
之前,我们提到过进程所属的Session是由进程Token中的TokenSessionId来决定的,那么我们是不是可以复制服务进程的Token然后修改其中的TokenSessionId,从而在用户桌面上创建一个具有SYSTEM权限的新进程呢?答案是肯定的。一下是实现这个操作的代码,为了缩小篇幅我删除了异常处理代码
HANDLEhTokenThis = NULL;
HANDLEhTokenDup = NULL;
HANDLEhThisProcess = GetCurrentProcess();
OpenProcessToken(hThisProcess, TOKEN_ALL_ACCESS, &hTokenThis);
DuplicateTokenEx(hTokenThis, MAXIMUM_ALLOWED,NULL, SecurityIdentification, TokenPrimary, &hTokenDup);
DWORDdwSessionId = WTSGetActiveConsoleSessionId();
SetTokenInformation(hTokenDup, TokenSessionId, &dwSessionId, sizeof(DWORD));
STARTUPINFOsi;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = "WinSta0//Default";
LPVOIDpEnv = NULL;
DWORDdwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
CreateEnvironmentBlock(&pEnv, hTokenDup, FALSE);
CreateProcessAsUser(
hTokenDup,
NULL,
(char *)"notepad",
NULL,
NULL,
FALSE,
dwCreationFlag,
pEnv,
NULL,
&si,
&pi);