• 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("令牌获取失败
    ");
            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]
    ", 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
    ", 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
    ", 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!
    "; 
    }
            

    进程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
    ", 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("空闲
    "); break;
            case MEM_RESERVE:   printf("保留
    "); break;
            case MEM_COMMIT:    printf("提交
    "); break;
            default:
                printf("未知
    ");
                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("
    ");
                    //计算所有相同块大小
                    dwBlockSize += Basicinfo.RegionSize;
    ​
                    // 累加内存块的位置
                    BlockAddress += Basicinfo.RegionSize;
                }
                //内有可能大小位空
                Size = dwBlockSize ? dwBlockSize : Basicinfo.RegionSize;
        }
    //下一个区域内存信息
    Addres += Size;
        }
    ​
    }

     

  • 相关阅读:
    编写简单的c运行库(一)
    c中函数参数传递
    MSVC命令行参数
    在C#中对List<>进行排序
    向DataTable中添加一行数据的做法
    获取本机IP(考虑多块网卡、虚拟机等复杂情况)
    [转]在C#中判断一个文本文件的编码方式
    关于C#使用XML序列化的一些注意事项
    C#中的类型和SQL Server中的类型对应关系
    关于DLL文件和EXE文件不在同一目录下的设置
  • 原文地址:https://www.cnblogs.com/ltyandy/p/11103542.html
Copyright © 2020-2023  润新知