Windows用户权限
标准用户
- 普通用户的权限被限制,以防止他们进行可能影响系统稳定性、安全性或其他用户的操作
- 不能修改系统设置,例如时间、日期或安装新的设备驱动程序
- 普通用户可能无法安装或卸载应用程序
- 只能访问自己的文件和某些公共文件,但不能访问其他用户的文件或系统文件
- 普通用户不能执行需要管理员权限的系统任务,如关机计算机、管理系统服务或查看安全日志
管理员用户
- 具有对系统的完全控制权,可以执行任何操作
- 可以更改所有系统设置
- 可以安装、更新或卸载应用程序和设备驱动程序
- 可以访问、修改或删除任何文件,包括其他用户的文件和系统文件
- 可以创建或删除用户账户、分配用户权限和管理系统服务
- 在
Windows Vista
及更高版本中,尽管管理员有完全的系统权限,但他们仍然受到用户帐户控制(UAC)的约束,该控制要求管理员在进行可能影响系统的操作时进行确认
LocalSystem
- 这是一个高权限的服务账户,用于运行
Windows
的核心服务 LocalSystem
账户的权限甚至超过了管理员- 它几乎可以在系统上做任何事情
- 尽管此账户具有广泛的权限,但它主要是为系统服务和应用程序使用的,并不建议用户或管理员使用此账户
问题
- 普通用户调起的程序,无法通过
CreateProcess
调起需要管理员权限的exe
。- 可以使用
ShellExecute
或ShellExecuteEx
,传runas
,请求管理员权限 - 这种调起方式会弹出
UAC
窗口
- 可以使用
- 对于自解压程序,由于启动后,实际上它先是做了解压动作,然后调起了解压后的某个
exe
,导致我们创建的进程很快结束返回了,无法通过创建的进程句柄来监控目标进程。- 使用
job
内核对象对创建的进程进行关联,以完成端口来等待所有进程结束。
- 使用
实例
简述
- 在进程A里面根据权限创建进程,来调起目标
exe
,如下:- 如果需要管理员权限,就调起这个
helper.exe
,并参数传过去,让helper.exe
在它里面去用ShellExecute
方式去请求管理员权限 - 如果不需要管理员权限,就用
CreateProcess
来调起这个目标exe
,有权限有成功,没权限就失败
- 如果需要管理员权限,就调起这个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
sync_status installer_helper::create_process(const wchar_t* path) { DWORD exitCode; sync_status syst(sync_status::em_syst_install_default); std::wstring par = path; std::wstring tmp_str = to_wstr(m_param.c_str()); std::wstring cmd = par + L" " + tmp_str; wchar_t buf[MAX_PATH]; wcscpy_s(buf, cmd.c_str()); if (m_level > RunLevel::asInvoker) { TCHAR szPath[MAX_PATH]; if (GetCurrentDllPath(szPath, sizeof(szPath) / sizeof(TCHAR)) == FALSE) { return sync_status::em_syst_create_ist_process_failed; } PathRemoveFileSpec(szPath); std::wstring tPath = szPath; tPath.append(L"\\AppCenterHelper.exe"); SHELLEXECUTEINFO execInfo = { 0 }; execInfo.cbSize = sizeof(SHELLEXECUTEINFO); execInfo.fMask = SEE_MASK_NOCLOSEPROCESS; execInfo.hwnd = NULL; execInfo.lpVerb = L"runas"; execInfo.lpFile = tPath.c_str(); execInfo.lpParameters = buf; execInfo.lpDirectory = NULL; execInfo.nShow = SW_SHOW; execInfo.hInstApp = NULL; execInfo.hProcess = NULL; do { if (!ShellExecuteEx(&execInfo)) { DWORD dwError = GetLastError(); if (dwError == ERROR_ACCESS_DENIED) { return sync_status::em_syst_ist_no_permissions; } else if (dwError == ERROR_BAD_FORMAT) { return sync_status::em_syst_ist_bad_format_exe; } else { return sync_status::em_syst_create_ist_process_failed; } } if (execInfo.hProcess == NULL) { return sync_status::em_syst_create_process_failed; } WaitForSingleObject(execInfo.hProcess, INFINITE); if (GetExitCodeProcess(execInfo.hProcess, &exitCode)) { syst = (exitCode == 0) ? sync_status::em_syst_install_success : sync_status::em_syst_install_failed; } CloseHandle(execInfo.hProcess); } while (false); } else { STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); if (!CreateProcess(NULL, buf, NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi)) { DWORD dwError = GetLastError(); if (dwError == ERROR_ACCESS_DENIED) { return sync_status::em_syst_ist_no_permissions; } else if (dwError == ERROR_BAD_FORMAT) { return sync_status::em_syst_ist_bad_format_exe; } else { return sync_status::em_syst_create_ist_process_failed; } } if (pi.hProcess == NULL || pi.hProcess == INVALID_HANDLE_VALUE) { return sync_status::em_syst_create_process_failed; } WaitForSingleObject(pi.hProcess, INFINITE); if (GetExitCodeProcess(pi.hProcess, &exitCode)) { syst = (exitCode == 0) ? sync_status::em_syst_install_success : sync_status::em_syst_install_failed; } CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } return syst; } |
管理员权限
- 为什么管理员权限通过
helper.exe
来调?- 因为直接在上面使用
ShellExecute
来请求管理员权限,返回的是创建的进程的句柄。这对于普通程序来说是够用了 - 但是我这里去操作的是安装,卸载程序。我在调试过程中发现,部分安装包,卸载程序是自解压程序,也就是调起该程序后,它会调起其他的
exe
,而它调起的其他的exe
,我们获取句柄是比较麻烦的 - 而我的需求是调起这个程序后,要阻塞到它完全结束。所以直接用
ShellExecute
方式来创建的进程,在面对自解压程序时,表现为:我们创建的程序马上结束返回了,但是该进程在结束之前创建了一个其他的进程来真正的做事情。这样的话,用WaitForSingleObject
监控不了,因为不知道那个子进程的句柄。 - 当然也可以创建进程快照,匹配进程名以及命令行参数的方式来定位的目标进程,但是这样的方式又繁琐,还涉及权限,如果我们一开始调起的进程是普通用户权限的话,后面在通过
ReadProcessMemory
读取进程命令行参数什么的,会失败。 - 所以呢,得使用
CreateProcess
来创建目标进程,在创建的时候,传参数CREATE_SUSPENDED
,让进程先暂停,等我们把创建的进程和job
对象关联后,再让进程ResumeThread
。 - 这样,该进程创建的子进程,我们就可以跟踪到了。
- 因为直接在上面使用
普通权限
- 这个比较清晰,就是去创建进程,成功就成功,失败就失败
helper.exe
- 设置了一个单实例
- 然后验证了命令行,因为主要靠命令行来调起
- 创建了
job
内核对象和一个JOBOBJECT_ASSOCIATE_COMPLETION_PORT
对象,并把他们关联到了一起- 这使得程序可以接收关于
Job
状态的通知
- 这使得程序可以接收关于
- 这样目标
exe
启动后创建的所有进程,job
都有感知 - 而当这些所有的进程都结束后,通过这个
GetQueuedCompletionStatus
来感知状态- 使用
GetQueuedCompletionStatus
来等待Job
的完成状态。 - 当所有的进程都完成时,它将收到
JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO
消息
- 使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
#include <windows.h> #include <set> #include <vector> HANDLE InitAppMutex() { HANDLE hMutex = NULL; SECURITY_ATTRIBUTES SecurityAttributes; SECURITY_DESCRIPTOR SecurityDesc; memset(&SecurityAttributes, 0, sizeof(SECURITY_ATTRIBUTES)); BOOL bRet = InitializeSecurityDescriptor(&SecurityDesc, SECURITY_DESCRIPTOR_REVISION); if (bRet == FALSE) { return NULL; } bRet = SetSecurityDescriptorDacl(&SecurityDesc, TRUE, NULL, FALSE); if (bRet == FALSE) { return NULL; } SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); SecurityAttributes.bInheritHandle = FALSE; SecurityAttributes.lpSecurityDescriptor = &SecurityDesc; hMutex = CreateMutex(&SecurityAttributes, FALSE, L"Local\\appcenter_helper_fuck"); if (hMutex == NULL) { return NULL; } DWORD dwLastError = GetLastError(); if (dwLastError == ERROR_ALREADY_EXISTS) { CloseHandle(hMutex); return NULL; } return hMutex; } int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { if (lpCmdLine == NULL) { return -1; } if (!*lpCmdLine) { return -1; } HANDLE hd = InitAppMutex(); if (hd == NULL) { return -1; } HANDLE hJob = CreateJobObject(NULL, NULL); if (hJob == NULL) { return -1; } JOBOBJECT_ASSOCIATE_COMPLETION_PORT port; port.CompletionKey = hJob; port.CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1); SetInformationJobObject(hJob, JobObjectAssociateCompletionPortInformation, &port, sizeof(port)); STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); if (!CreateProcess(NULL, lpCmdLine, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) { CloseHandle(hJob); return -1; } AssignProcessToJobObject(hJob, pi.hProcess); ResumeThread(pi.hThread); HANDLE hProc = pi.hProcess; if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) { CloseHandle(hJob); return -1; } std::set<unsigned long> processHandles; DWORD dwBytesReturned = 0; JOBOBJECT_BASIC_PROCESS_ID_LIST pidList; PJOBOBJECT_BASIC_PROCESS_ID_LIST ppTmp = NULL; do { if (QueryInformationJobObject(hJob, JobObjectBasicProcessIdList, &pidList, sizeof(pidList), &dwBytesReturned)) { break; } if (GetLastError() == ERROR_MORE_DATA) { DWORD dwSize = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) + sizeof(DWORD_PTR) * (pidList.NumberOfAssignedProcesses - 1); auto pPidList = (PJOBOBJECT_BASIC_PROCESS_ID_LIST)malloc(dwSize); if (pPidList && QueryInformationJobObject(hJob, JobObjectBasicProcessIdList, pPidList, dwSize, &dwBytesReturned)) { ppTmp = pPidList; break; } } else { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); CloseHandle(hJob); return -1; } } while (false); if (ppTmp) { for (DWORD i = 0; i < ppTmp->NumberOfProcessIdsInList; ++i) { processHandles.insert(ppTmp->ProcessIdList[i]); } free(ppTmp); } else { for (DWORD i = 0; i < pidList.NumberOfProcessIdsInList; ++i) { processHandles.insert(pidList.ProcessIdList[i]); } } if (processHandles.empty()) { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); CloseHandle(hJob); return -1; } DWORD dwBytes; ULONG_PTR key; OVERLAPPED* pOv; int res = 0; std::vector<int> resVec; while (GetQueuedCompletionStatus(port.CompletionPort, &dwBytes, &key, &pOv, INFINITE)) { if (dwBytes == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO) { for (auto fuck : processHandles) { HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, fuck); if (hProcess == NULL) { continue; } DWORD exitCode; if (GetExitCodeProcess(hProcess, &exitCode)) { resVec.push_back(exitCode); } } break; } } for (auto itm : resVec) { if (itm != 0) { res = 1; } } CloseHandle(pi.hThread); CloseHandle(pi.hProcess); CloseHandle(hd); return res; } |
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ 关于多字节和宽字节二12/04
- ♥ Soui四05/23
- ♥ SOUI源码:log4z06/24
- ♥ WinDbg命令标记、命令07/11
- ♥ Windows 核心编程 _ 进程二06/19
- ♥ Soui二05/18