PE基础5-权限提升-内存管理
查询权限
BOOL QueryPrivileges() { // 1. 获得本进程的令牌 HANDLE hToken = NULL; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) return false; // 2. 获取提升类型 TOKEN_ELEVATION_TYPE ElevationType = TokenElevationTypeDefault; BOOL bIsAdmin = false; DWORD dwSize = 0; if (GetTokenInformation(hToken, TokenElevationType, &ElevationType, sizeof(TOKEN_ELEVATION_TYPE), &dwSize)) { // 2.1 创建管理员组的对应SID BYTE adminSID[SECURITY_MAX_SID_SIZE]; dwSize = sizeof(adminSID); CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, &adminSID, &dwSize); // 2.2 判断当前进程运行用户角色是否为管理员 if (ElevationType == TokenElevationTypeLimited) { // a. 获取连接令牌的句柄 HANDLE hUnfilteredToken = NULL; GetTokenInformation(hToken, TokenLinkedToken, (PVOID)&hUnfilteredToken, sizeof(HANDLE), &dwSize); // b. 检查这个原始的令牌是否包含管理员的SID if (!CheckTokenMembership(hUnfilteredToken, &adminSID, &bIsAdmin)) return false; CloseHandle(hUnfilteredToken); } else { bIsAdmin = IsUserAnAdmin(); } CloseHandle(hToken); } // 3. 判断具体的权限状况 BOOL bFullToken = false; switch (ElevationType) { case TokenElevationTypeDefault: /* 默认的用户或UAC被禁用 */ if (IsUserAnAdmin()) bFullToken = true; // 默认用户有管理员权限 else bFullToken = false;// 默认用户不是管理员组 break; case TokenElevationTypeFull: /* 已经成功提高进程权限 */ if (IsUserAnAdmin()) bFullToken = true; //当前以管理员权限运行 else bFullToken = false;//当前未以管理员权限运行 break; case TokenElevationTypeLimited: /* 进程在以有限的权限运行 */ if (bIsAdmin) bFullToken = false;//用户有管理员权限,但进程权限有限 else bFullToken = false;//用户不是管理员组,且进程权限有限 } // 4. 根据权限的不同控制按钮的显示 if (!bFullToken) Button_SetElevationRequiredState(::GetDlgItem(m_hWnd, IDC_BUTTON1), !bFullToken); else ::ShowWindow(::GetDlgItem(m_hWnd, IDC_BUTTON1), SW_HIDE); }
管理员启动
void OnBnClickedButton1() { // 1. 隐藏当前窗口 //ShowWindow(hWnd, SW_HIDE); this->ShowWindow(SW_HIDE); // 2. 获取当前程序路径 WCHAR szApplication[MAX_PATH] = { 0 }; DWORD cchLength = _countof(szApplication); QueryFullProcessImageName(GetCurrentProcess(), 0, szApplication, &cchLength); // 3. 以管理员权限重新打开进程 SHELLEXECUTEINFO sei = { sizeof(SHELLEXECUTEINFO) }; sei.lpVerb = L"runas"; // 请求提升权限 sei.lpFile = szApplication; // 可执行文件路径 sei.lpParameters = NULL; // 不需要参数 sei.nShow = SW_SHOWNORMAL; // 正常显示窗口 if (ShellExecuteEx(&sei)) exit(0); else ::ShowWindow(m_hWnd, SW_SHOWNORMAL); }
遍历权限
//遍历权限 void ShowPrviliges() { //获取进程令牌句柄 HANDLE hToken; OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken); if (!hToken) { printf("令牌获取失败\n"); return; } //查询令牌中的权限 DWORD dwSize; //第一次获取查询数据的大小 GetTokenInformation(hToken, TokenPrivileges, NULL, NULL, &dwSize); //第二次根据上次获取的大小申请足够大的内存后, //将内存地址传进去获取想要的数据 char* pBuf = new char[dwSize] {}; GetTokenInformation(hToken, TokenPrivileges, pBuf, dwSize, &dwSize); //将内存中的内容用要查询数据结构体解析 //这里事权限的结构体 TOKEN_PRIVILEGES* pTp = (TOKEN_PRIVILEGES*)pBuf; //权限个数 DWORD dwCount = pTp->PrivilegeCount; //指向权限数组首地址 LUID_AND_ATTRIBUTES* pLaa = pTp->Privileges; //输出权限 for (int i = 0; i < dwCount; i++, pLaa++) { char szName[100] = {}; DWORD dwLen = sizeof(szName); //查询此权限对应的字符串 LookupPrivilegeNameA(0, &pLaa->Luid, szName, &dwLen); //状态,0:关闭,1:默认值,2:开启,3:默认开启 printf("[%s] -- 状态[%d]\n", szName, pLaa->Attributes); } delete pBuf;}
提升权限
BOOL EnableDebugPrivilege(BOOL fEnable){ BOOL fOk = FALSE; HANDLE hToken; // 以修改权限的方式,打开进程的令牌 if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) { // 令牌权限结构体 TOKEN_PRIVILEGES tp; tp.PrivilegeCount = 1; //获得LUID LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid); tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0; //修改权限 AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL); fOk = (GetLastError() == ERROR_SUCCESS); CloseHandle(hToken); } return(fOk); }
内存管理
Windows提供了以下3种方式管理内存数据
-
堆:适合用来管理大量的小型对象,使用堆管理方式可以非常方便的帮我们管理所需要的内存空间,而无需去 关心一些细节问题,但是堆管理方式的效率较低,堆内存的控制不够灵活
-
虚拟内存:适合用于管理大型的对象数组或大型的结构数组,使用虚拟内存管理方式有丰富的内存管理接口, 可以使我们更加精确的控制内存数据
-
文件映射:适合用于管理大型的数据流,以及在同一个机器上的不同进程间共享数据
堆
堆使用
int main() { //创建堆可增长的 HANDLE heap = HeapCreate(0, 0, 0); //系统信息 SYSTEM_INFO sys; GetSystemInfo(&sys); //申请5个页面大小堆 LPVOID pBuff = HeapAlloc(heap, HEAP_ZERO_MEMORY, sys.dwPageSize * 5); //读写堆内存 memcpy(pBuff, "hello", 6); printf("%s\n", pBuff); //释放堆 HeapFree(heap, HEAP_NO_SERIALIZE, pBuff); //销毁堆 HeapDestroy(heap); //系统的默认堆 HANDLE hProcessHeap = GetProcessHeap(); LPVOID pbuff2 = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY,11 ); //读写堆内存 memcpy(pbuff2, "hello", 6); printf("%s\n", pbuff2); //不能使用DestroyHeap getchar(); }
虚拟内存
虚拟内存三种状态
空闲(free) : 进程不能访问这种页面,此页面还没有被分配 保留(reserve):这个页面被预定了。但是还未与物理内存映射,因此这里也是不能访问的 提交(commit): 内存已经被分配了,并且也与物理存储器映射了,进程已经可以访问这里
虚拟内存映射的三种方式:
private : 进程私有内存,不被其他进程所共享, 一般是堆,栈
mapped: 从别的内存映射而来
image : 从程序的PE映像映射而来,一般是映像的区段 虚拟内存的页面属性
READONLY :只读,只能从这个分页读取数据
READ_WRITE : 可读可写
EXECUTE : 可执行
EXECUTE_READ_WRITE : 可读可写可执行
WRITE_COPY : 写时拷贝 (用户不能使用)
使用虚拟内存
int main() { //MEM_FREE 空闲,没有被使用 //MEM_RESERVE 保留,没有与物理地址映射 //MEM_COMMIT 提交,与物理地址进行映射 //1.申请虚拟内存空间 LPVOID pBuff = VirtualAlloc( 0, //预定的地址,申请空间从这个地址开始 1, //申请空间的大小,默认对齐4kb MEM_RESERVE | MEM_COMMIT, //预定并提交 PAGE_READWRITE //可写可读 ); //VirtualAllocEx 在其它进程中申请空间 //2.使用它 memcpy(pBuff, "hello", 6); //3.释放内存 VirtualFree(pBuff, 1, MEM_FREE); //VirtualFree 在其它进程中释放空间 //修改内存分页属性 char * pName = (char*)"hello"; DWORD dwOldProtect; VirtualProtect( pName, //修改的内存地址 1, //修改的大小,会分页对齐 PAGE_READWRITE, //修改后的新属性 &dwOldProtect); //修改前的原始属性 pName[0] = 'x'; //VirtualProtectEx 修改目标进程内存属性 }
文件映射
简单文操作
int main() { //操纵一个文件,CreateFile,WriteFile,ReadFile //1. 文件映射 速度快,无需申请空间 HANDLE hFile = CreateFile( L"1.txt", GENERIC_WRITE| GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); //2.创建一个内存映射对象 HANDLE hFileMap = CreateFileMapping( hFile, NULL, PAGE_READWRITE, 0, 0x10, L"test"); //3.将物理地址映射到虚拟地址 SIZE_T Size = 10; LPVOID pFileBuff = MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, Size); //4.操作这段内存,写入数据 memcpy(pFileBuff, "hello 15pb", 11); //5.刷新到文件 FlushViewOfFile(pFileBuff, 10); //6.取消映射 UnmapViewOfFile(pFileBuff); //7.关闭句柄 CloseHandle(hFile); CloseHandle(hFileMap); }
进程通信
进程A
int main() { //1.创建文件映射 HANDLE hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, //创建文件映射 -1 NULL, //文件安全属性 PAGE_READWRITE, //页面属性 0, 0x0100, L"15pb"); //2.物理地址转换虚拟内存地址 LPVOID lpBuff = MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, 10); //3.写入数据 memcpy(lpBuff, "hello15pb", 10); //4.等待读取 system("pause"); //5.释放文件映射 UnmapViewOfFile(lpBuff); CloseHandle(hFileMap); std::cout << "Hello World!\n"; }
进程B
int main() { //1.打开文件映射 HANDLE hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"15pb"); //2.将物理地址转换虚拟地址 LPVOID lPbuff = MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS,0,0,10); ///3.读写数据 printf("buff:%s\n", lPbuff); //4. 取消映射关系 UnmapViewOfFile(lPbuff); CloseHandle(hFileMap); }
遍历内存
int main() { //1.遍历进程 内存状态 DWORD Addres = 0, Size = 0; //保存内存信息 MEMORY_BASIC_INFORMATION Basicinfo = {}; //遍历进程所有分页,输出内容 while (VirtualQuery((LPCVOID)Addres, &Basicinfo, sizeof(MEMORY_BASIC_INFORMATION))) { Size = Basicinfo.RegionSize; printf("%08p ", Basicinfo.BaseAddress); //当前状态 switch (Basicinfo.State) { case MEM_FREE: printf("空闲\n"); break; case MEM_RESERVE: printf("保留\n"); break; case MEM_COMMIT: printf("提交\n"); break; default: printf("未知\n"); break; } //如果是提交状态的内存区域,那么遍历所有块中的信息 if (Basicinfo.State == MEM_COMMIT) { //遍历所有基址是Address LPVOID BaseBlockAddress = (LPVOID)Addres; DWORD BlockAddress = Addres; DWORD dwBlockSize = 0; // 遍历大内存块中的小内存块 while (VirtualQuery((LPVOID)BlockAddress, &Basicinfo, sizeof(Basicinfo))) { if (BaseBlockAddress != Basicinfo.AllocationBase) { break; } printf(" %08X ", BlockAddress); //查看内存状态,映射方式 switch (Basicinfo.Type) { case MEM_PRIVATE: printf("私有 "); break; case MEM_MAPPED: printf("映射 "); break; case MEM_IMAGE: printf("镜像 "); break; default: printf("未知 "); break; } if (Basicinfo.Protect == 0) printf("---"); else if (Basicinfo.Protect & PAGE_EXECUTE) printf("E--"); else if (Basicinfo.Protect & PAGE_EXECUTE_READ) printf("ER-"); else if (Basicinfo.Protect & PAGE_EXECUTE_READWRITE) printf("ERW"); else if (Basicinfo.Protect & PAGE_READONLY) printf("-R-"); else if (Basicinfo.Protect & PAGE_READWRITE) printf("-RW"); else if (Basicinfo.Protect & PAGE_WRITECOPY) printf("WCOPY"); else if (Basicinfo.Protect & PAGE_EXECUTE_WRITECOPY) printf("EWCOPY"); printf("\n"); //计算所有相同块大小 dwBlockSize += Basicinfo.RegionSize; // 累加内存块的位置 BlockAddress += Basicinfo.RegionSize; } //内有可能大小位空 Size = dwBlockSize ? dwBlockSize : Basicinfo.RegionSize; } //下一个区域内存信息 Addres += Size; } }