修改 Windows 文件访问权限的多种方法_如何更改文件高级权限-程序员宅基地

技术标签: c++  Windows 基础编程  测试工具  windows  业余学习  

提示:本文修改后包含编程方法以及附注的工具方法,传统的资源管理器交互方法等等。

由于文件是安全对象,因此访问它们受访问控制模型控制,该模型控制对 Windows 中所有其他安全对象的访问。 

更改文件或目录对象的安全描述符,需要调用 SetNamedSecurityInfo 或 SetSecurityInfo 等函数。

ACLAccess Control List,用来表示用户(组)权限的列表,包括 DACL 和 SACL
ACEAccess Control Entry,ACL 中的元素;
DACLDiscretionary Access Control List,用来表示安全对象权限的列表;
SACLSystem Access Control List,用来记录对安全对象访问的日志。

关于这几个概念已经有很多资料解释,这里就不详细展开了。(以下仅介绍通过 Win32API 修改文件安全属性的一些内容)。

本文以 SetNamedSecurityInfo 函数为例,简单讲解如何通过编程修改指定文件的 ACL 主体和添加 ACE 条目。后文修改的文件比较特殊,它们受到本地内置安全主体 TrustedInstaller 的完全控制保护,本文旨在通过相关 API 实现修改这类文件的完全控制权限。有关内置安全主体 TrustedInstaller 的信息,需要具有一定的知识基础,限于篇幅原因这里不详细展开。


一、编程实现方法阐述

1.1 修改 ACL 的所有者

SetNamedSecurityInfo 函数用于在指定对象的安全描述符(ACL)中设置指定的安全信息。 调用方按名称标识对象。

这里的按名称标识对象,表示设置安全信息时候,目标对象名是字符串格式,且对于不同对象类型的字符串格式,可以参阅说明:SE_OBJECT_TYPE。我们这里要修改NTFS文件系统中指定文件的安全属性,按照标准需要填写文件绝对路径的宽字符串,其中路径以反斜杠 (" / ") 或者双斜杠 (" \\ ") 给出,并且要以 NULL 结尾。

下面给出该函数的参数:

DWORD SetNamedSecurityInfoW(
  [in]           LPWSTR               pObjectName,
  [in]           SE_OBJECT_TYPE       ObjectType,
  [in]           SECURITY_INFORMATION SecurityInfo,
  [in, optional] PSID                 psidOwner,
  [in, optional] PSID                 psidGroup,
  [in, optional] PACL                 pDacl,
  [in, optional] PACL                 pSacl
);

这些参数的定义是:

pObjectName 指定目标对象的名称;
ObjectType 指示 pObjectName 的类型,如果修改 NTFS 文件则填写为 SE_FILE_OBJECT;
SecurityInfo 指示要设置的安全信息的类型;
psidOwner 指向标识对象所有者的 SID 结构的指针;
psidGroup 指向标识对象主组的 SID 的指针;
pDacl 指向 对象的新 DACL 的指针;
pSacl 指向 对象的新 SACL 的指针。

其中,如果 SecurityInfo 填写为 OWNER_SECURITY_INFORMATION 表明对安全主体的操作(也就是控制列表所有者);如果填写为 DACL_SECURITY_INFORMATION 则表明对安全对象(ACE)的权限的操作。这里需要注意:如果调用方令牌对应的账户标识符(SID)不是文件安全主体指向的账户标识符,则对安全对象权限的操作会被拒绝

修改文件安全属性需要进程的令牌具有一定的访问控制权限,这里我们需要获取两个权限 SE_SECURITY_NAME 和 SE_TAKE_OWNERSHIP_NAME 。这两个权限可以通过 AdjustTokenPrivileges 函数获得,而赋予新的令牌则需要调用方线程具有管理员权限。所以进程需要以提升的令牌启动才能正确获取权限

从零开始的项目都是不容易的,有一个参考最好不过了。这里我们可以参考资源管理器的功能实现文件安全属性的修改,下面的图片展示了通过资源管理器查看的目标文件的安全属性页面信息:

目标文件的安全属性信息页
目标文件的安全属性信息页

通过观察,我们可以发现,当前的所有者为 TrustedInstaller 并且只有它具有完全控制权限,其他安全对象只具有读取和执行的权限。此外我们还发现,对于该文件权限修改的按钮都是灰色或者具有盾牌图标。我们将其与用户具有完全访问控制权的文件安全信息进行对比,就可以发现所有者信息起到了至关重要的作用,调用方必须为文件的安全主体或者权限比安全主体高,否则不能修改安全属性表。所以当正确获取权限后,我们首先需要修改文件的安全主体(所有者)。

首先我们简述一下通过资源管理器进行修改的步骤:

Step 1: 首先选中要修改的文件(夹),打开右键菜单,在菜单中找到“属性”项目,点击打开属性对话框;

Step 2: 在属性对话框中选中“安全”选项卡,右下角点击“高级(安全设置)”按钮;

Step 3: 可以看到该页面包含两个主要部分:1)所有者;2)成员及其权限

并且后面都有带有盾牌的“更改”按钮,再结合说明文档可知,如果当前进程代表的账户权限低于所有者所具有的权限,则低权限进程不具有修改高权限进程的权限能力;

这里我们需要先修改所有者,点击最上面的TrustedInstaller字样后面的更改按钮,此时可能弹出UAC对话框提示用户需要提升权限,也可能没有提示直接提升(取决于计算机配置的UAC警告级别)

Step 4: 此时会弹出选择所有者的对话框,点击“高级”按钮展开这个编辑器窗口:

Step 5: 在高级窗口中点击立即查找,在查找的列表中找到当前登陆用户的用户名,然后点击确定,或者选择everyone(字面意思,代表任何用户)。

Step 6: 逐级确定并应用,可以发现所有者被成功更改:

最后一步点击“应用”,然后再点击确定,应用更改,随后重新打开高级安全设置页面,选择要修改权限的成员,并发现编辑区域按钮被点亮,点击“添加/编辑”:

随后,在打开的窗口中,先点击蓝色字“选择主体”,类似于修改所有者的操作完成权限配置,并点击确定,最后点击应用即可完成整个修改操作:

最后,补充一下修改完成后改回TrustedInstaller的方法:在更改所有者时候,直接输入 NT SERVICE\TrustedInstaller 需要注意不能有拼写错误!然后其他步骤不变。

我们通过设置 SetNamedSecurityInfo 函数的 SecurityInfo 参数为 OWNER_SECURITY_INFORMATION 并指定 psidOwner 为指向新所有者的安全描述符 (SID) 结构的指针。

SID 就相当于所有者的身份证,那么我们如何获取到所需要的 SID 呢?

LookupAccountName 函数可供获取指定用户名对应的 SID 。

其参数如下:

BOOL LookupAccountNameW(
  [in, optional]  LPCWSTR       lpSystemName,
  [in]            LPCWSTR       lpAccountName,
  [out, optional] PSID          Sid,
  [in, out]       LPDWORD       cbSid,
  [out, optional] LPWSTR        ReferencedDomainName,
  [in, out]       LPDWORD       cchReferencedDomainName,
  [out]           PSID_NAME_USE peUse
);

其中,lpAccountName 指向指定帐户名称的以 NULL 结尾的字符串的指针。这里的本地账户名称可以通过输入已知的字符串如 "Administrators" 。注:采用 domain_name\user_name 格式的完全限定字符串来确保 LookupAccountName 函数在所需域中查找帐户,但本文不谈及域内操作,只谈本地操作。

关于账户名称的获取,有多种方法,如 GetUserName 可以获得创建当前调用线程所在帐户名称的字符串,环境变量函数 GetEnvironmentVariable 通过设置 lpName 参数为"USERNAME"可以获得当前所在帐户名称的字符串;使用 Windows Terminal Session API 获取当前会话对应的账户名称字符串,等等。这里通过 WTSQuerySessionInformation 函数并指定相关参数设置以获取当前登录账户的名称:

WTSQuerySessionInformationW(
        WTS_CURRENT_SERVER_HANDLE, 
        WTS_CURRENT_SESSION, 
        WTS_INFO_CLASS::WTSUserName, 
        &userName, 
        &nameSize);

然后将得到的字符串传递给 LookupAccountName 函数,获取对应的 SID 。

随后,调用 SetNamedSecurityInfo 并设置 pObjectNameObjectTypeSecurityInfo psidOwner 参数即可修改目标对象的所有者。

1.2 修改访问控制对象 (ACE) 信息

本文以在原有DACL表上添加新的对象权限为例进行讲解。有关于覆盖、删除、复制的方法需要自行调试。

一种通用的更新方法就是先获取到原始的DACL表,然后将其与要添加的内容合并为一张新的表。最后将新的表更新到文件对应的表上。

整个过程我们需要这几个步骤:

Step1: 调用 GetNamedSecurityInfo 并设置 DACL_SECURITY_INFORMATION 类型,获取旧的DACL表;

Step2: 调用 BuildExplicitAccessWithName 用于构建一个 ACE,包含需要的权限;

Step3: 调用 SetEntriesInAcl 合并 ACE 到 旧的DACL,返回合并后的新的 DACL;

Step4: 调用 SetNamedSecurityInfo 绑定新的 DACL 到文件对象。

一个典型的示例代码如下,其中 Step2 也可以直接为结构体成员赋值:

DWORD AddAceToObjectsSecurityDescriptor(
    LPTSTR pszObjName,          // name of object
    SE_OBJECT_TYPE ObjectType,  // type of object
    LPWSTR pszTrustee,          // trustee for new ACE
    TRUSTEE_FORM TrusteeForm,   // format of trustee structure
    DWORD dwAccessRights,       // access mask for new ACE
    ACCESS_MODE AccessMode,     // type of ACE
    DWORD dwInheritance         // inheritance flags for new ACE
)
{
    DWORD dwRes = 0;
    PACL pOldDACL = NULL, pNewDACL = NULL;
    PSECURITY_DESCRIPTOR pSD = NULL;
    EXPLICIT_ACCESS ea = {};

    if (NULL == pszObjName)
        return ERROR_INVALID_PARAMETER;
    printf("|*|:为用户 %ws 获取文件:%ws 的访问权限。\n", pszTrustee, pszObjName);
    // Get a pointer to the existing DACL.

    dwRes = GetNamedSecurityInfoW(pszObjName, ObjectType,
        DACL_SECURITY_INFORMATION,
        NULL, NULL, &pOldDACL, NULL, &pSD);
    if (ERROR_SUCCESS != dwRes) {
        printf("GetNamedSecurityInfo Error %u\n", dwRes);
        goto Cleanup;
    }

    // Initialize an EXPLICIT_ACCESS structure for the new ACE. 

    ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
    //ea.grfAccessPermissions = dwAccessRights;
    //ea.grfAccessMode = AccessMode;
    //ea.grfInheritance = dwInheritance;
    //ea.Trustee.TrusteeForm = TrusteeForm;
    //ea.Trustee.ptstrName = pszTrustee;
    // 生成指定用户帐户的访问控制信息(这里指定赋予全部的访问权限)

    BuildExplicitAccessWithNameW(&ea, pszTrustee, dwAccessRights, AccessMode, dwInheritance);
    // Create a new ACL that merges the new ACE
    // into the existing DACL.

    dwRes = SetEntriesInAclW(1, &ea, pOldDACL, &pNewDACL);
    if (ERROR_SUCCESS != dwRes) {
        printf("SetEntriesInAcl Error %u\n", dwRes);
        goto Cleanup;
    }

    // Attach the new ACL as the object's DACL.

    dwRes = SetNamedSecurityInfoW(pszObjName, ObjectType,
        DACL_SECURITY_INFORMATION,
        NULL, NULL, pNewDACL, NULL);
    if (ERROR_SUCCESS != dwRes) {
        printf("SetNamedSecurityInfo Error %u\n", dwRes);
        goto Cleanup;
    }
    else {
        printf("成功:已经为用户 %ws 获取文件:%ws 的访问权限。\n", pszTrustee, pszObjName);
        return dwRes;
    }

Cleanup:

    if (pSD != NULL)
        LocalFree((HLOCAL)pSD);
    if (pNewDACL != NULL)
        LocalFree((HLOCAL)pNewDACL);

    std::cout << "|*|:操作失败! " << std::endl;
    return -1;
}

通过以上方法即可对指定路径文件进行安全访问权限的修改。

二、完整编程代码

2.1 完整代码

下面是按照上文思路编写的简单实现代码,需要将编译好的程序在提升的命令行中执行,命令行参数为 TestFile.exe <文件路径> 。(编译环境:Visual Studio C++)

注意:运行环境必须是管理员令牌,这样才能启用相应的权限,可以通过应用程序清单或者编程的方式(ShellExecuteEx, lpVerb + "runas")来请求管理员权限。

#include <iostream>
#include <stdio.h>
#include <aclapi.h>
#include <windows.h>
#include <tchar.h>
#include <stdlib.h>
#include <aclapi.h>
#include <wtsapi32.h>

#pragma comment(lib, "Advapi32.lib")
#pragma comment(lib, "Wtsapi32.lib")

#define MAX_NAME 260

BOOL SetPrivilege(
    HANDLE hToken,          // access token handle
    LPCWSTR lpszPrivilege,  // name of privilege to enable/disable
    BOOL bEnablePrivilege   // to enable or disable privilege
)
{
    TOKEN_PRIVILEGES tp;
    LUID luid;

    if (!LookupPrivilegeValueW(
        NULL,            // lookup privilege on local system
        lpszPrivilege,   // privilege to lookup 
        &luid))        // receives LUID of privilege
    {
        printf("LookupPrivilegeValue error: %u\n", GetLastError());
        return FALSE;
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if (bEnablePrivilege)
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.

    if (!AdjustTokenPrivileges(
        hToken,
        FALSE,
        &tp,
        sizeof(TOKEN_PRIVILEGES),
        (PTOKEN_PRIVILEGES)NULL,
        (PDWORD)NULL))
    {
        printf("AdjustTokenPrivileges error: %u\n", GetLastError());
        return FALSE;
    }

    if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)

    {
        printf("The token does not have the specified privilege. \n");
        return FALSE;
    }

    return TRUE;
}

BOOL SeTakeOwnershipToken()
{

    printf("|*|操作:获取相关权限。该特权可以修改目标文件的 Ownership。\n");
    HANDLE hToken;
    BOOL bRet = OpenProcessToken(
        GetCurrentProcess(),    // 进程句柄(当前进程)
        TOKEN_ALL_ACCESS,    // 全权访问令牌
        &hToken    // 返回的参数 进程令牌句柄 (就是AdjustTokenPrivileges的第一个参数)
    ); // 获取进程的令牌句柄
    if (bRet != TRUE) {
        printf("获取令牌句柄失败!\nPlease wait...\n");
        return FALSE;
    }
    // 提升权限
    BOOL set_SECURITY = SetPrivilege(hToken, SE_SECURITY_NAME, TRUE);
    if (!set_SECURITY || GetLastError() != ERROR_SUCCESS)
    {
        printf("提升SECURITY权限失败 Error: %u\n|*|:操作失败!\nPlease wait...\n", GetLastError());
        Sleep(5000);
        return FALSE;
    }
    // 提升获得所有者权限
    BOOL set_OWNER = SetPrivilege(hToken, SE_TAKE_OWNERSHIP_NAME, TRUE);
    if (!set_OWNER || GetLastError() != ERROR_SUCCESS)
    {
        printf("提升TAKE OWNERSHIP权限失败 Error: %u\n|*|:操作失败!\nPlease wait...\n", GetLastError());
        Sleep(5000);
        return FALSE;
    }
    printf("提升权限成功! \n");
    return TRUE;
}


BOOL ChangeTrusteeViaObjectsSecurity(
    LPTSTR pszObjName,          // name of object
    LPTSTR pszTrustee,          // trustee for new ACE
    SE_OBJECT_TYPE ObjectType,  // type of object. value :SE_FILE_OBJECT, /* 注册表为:SE_REGISTRY_KEY */
    SECURITY_INFORMATION  SecurityInfo  // Security Operational Information. value:OWNER_SECURITY_INFORMATION

)
{
    DWORD dwRes = 0;
    // LookupAccountName函数所需要的变量
    wchar_t* userName = nullptr;
    wchar_t sid[MAX_NAME]{ L'\0' };
    DWORD nameSize = 0;

    WTSQuerySessionInformationW(
        WTS_CURRENT_SERVER_HANDLE, 
        WTS_CURRENT_SESSION, 
        WTS_INFO_CLASS::WTSUserName, 
        &userName, 
        &nameSize);

    wchar_t userSID[MAX_NAME]{ L'\0' };
    wchar_t userDomain[MAX_NAME]{ L'\0' };
    DWORD sidSize = sizeof(userSID);
    DWORD signSidSize = sizeof(userSID);
    DWORD domainSize = sizeof(userDomain) / sizeof(WCHAR);


    SID_NAME_USE snu;
    dwRes = LookupAccountNameW(NULL,
        userName,
        userSID,
        &sidSize,
        userDomain,
        &domainSize,
        &snu);
    WTSFreeMemory(userName);
    //获取用户名SID
    if (ERROR_SUCCESS != dwRes)// 调用成功返回值为0
    {
        PSID_IDENTIFIER_AUTHORITY psia = GetSidIdentifierAuthority(userSID);
        signSidSize = swprintf_s(sid, sizeof(sid)/sizeof(wchar_t), L"S-%lu-", SID_REVISION);
        signSidSize = (signSidSize + swprintf_s(sid + wcslen(sid), sizeof(sid), L"%-lu", psia->Value[5]));


        int i = 0;
        int subAuthorities = *GetSidSubAuthorityCount(userSID);


        for (i = 0; i < subAuthorities; i++)
        {
            signSidSize += swprintf_s(sid + signSidSize, sizeof(sid), L"-%lu", *GetSidSubAuthority(userSID, i));
        }
        printf("Account SID: %ws\n", sid);
        // 更改所有者
        if (!SetNamedSecurityInfoW
        (pszObjName,
            ObjectType, /* 注册表为:SE_REGISTRY_KEY */
            SecurityInfo, /* 更改所有者 */
            &userSID, /* 需要更改所有者的SID */
            NULL, NULL, NULL))
        {
            printf("成功更改所有者! \n");
            return TRUE;
        }
        else {
            printf("Security Info:OWNER_SECURITY_INFORMATION.\nSetNamedSecurityInfo Error %u\nPlease wait...\n", dwRes);
            Sleep(5000);
        }
    }
    else {
        printf("LookupAccountName Error %u\nPlease wait...\n", dwRes);
        Sleep(5000);
    }
    return FALSE;
}


DWORD AddAceToObjectsSecurityDescriptor(
    LPTSTR pszObjName,          // name of object
    SE_OBJECT_TYPE ObjectType,  // type of object
    LPWSTR pszTrustee,          // trustee for new ACE
    TRUSTEE_FORM TrusteeForm,   // format of trustee structure
    DWORD dwAccessRights,       // access mask for new ACE
    ACCESS_MODE AccessMode,     // type of ACE
    DWORD dwInheritance         // inheritance flags for new ACE
)
{
    DWORD dwRes = 0;
    PACL pOldDACL = NULL, pNewDACL = NULL;
    PSECURITY_DESCRIPTOR pSD = NULL;
    EXPLICIT_ACCESS ea = {};

    if (NULL == pszObjName)
        return ERROR_INVALID_PARAMETER;
    printf("|*|:为用户 %ws 获取文件:%ws 的访问权限。\n", pszTrustee, pszObjName);
    // Get a pointer to the existing DACL.

    dwRes = GetNamedSecurityInfoW(pszObjName, ObjectType,
        DACL_SECURITY_INFORMATION,
        NULL, NULL, &pOldDACL, NULL, &pSD);
    if (ERROR_SUCCESS != dwRes) {
        printf("GetNamedSecurityInfo Error %u\n", dwRes);
        goto Cleanup;
    }

    // Initialize an EXPLICIT_ACCESS structure for the new ACE. 

    ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
    //ea.grfAccessPermissions = dwAccessRights;
    //ea.grfAccessMode = AccessMode;
    //ea.grfInheritance = dwInheritance;
    //ea.Trustee.TrusteeForm = TrusteeForm;
    //ea.Trustee.ptstrName = pszTrustee;
    // 生成指定用户帐户的访问控制信息(这里指定赋予全部的访问权限)

    BuildExplicitAccessWithNameW(&ea, pszTrustee, dwAccessRights, AccessMode, dwInheritance);
    // Create a new ACL that merges the new ACE
    // into the existing DACL.

    dwRes = SetEntriesInAclW(1, &ea, pOldDACL, &pNewDACL);
    if (ERROR_SUCCESS != dwRes) {
        printf("SetEntriesInAcl Error %u\n", dwRes);
        goto Cleanup;
    }

    // Attach the new ACL as the object's DACL.

    dwRes = SetNamedSecurityInfoW(pszObjName, ObjectType,
        DACL_SECURITY_INFORMATION,
        NULL, NULL, pNewDACL, NULL);
    if (ERROR_SUCCESS != dwRes) {
        printf("SetNamedSecurityInfo Error %u\n", dwRes);
        goto Cleanup;
    }
    else {
        printf("成功:已经为用户 %ws 获取文件:%ws 的访问权限。\n", pszTrustee, pszObjName);
        return dwRes;
    }

Cleanup:

    if (pSD != NULL)
        LocalFree((HLOCAL)pSD);
    if (pNewDACL != NULL)
        LocalFree((HLOCAL)pNewDACL);

    std::cout << "|*|:操作失败! " << std::endl;
    return -1;
}



int wmain(int argc, wchar_t* argv[])
{
    setlocale(LC_CTYPE, "CHS");//让wprintf()支持中文
    if (argc != 2)
    {
        std::cout << "Invalid Parameters!" << std::endl;
        return 0;
    }

    wchar_t* path = argv[1];
    wchar_t* pszObjName = path;

    // 获取当前用户名
    wchar_t* currentUser = nullptr;
    DWORD dwSize_currentUser = 0;

    WTSQuerySessionInformationW(
        WTS_CURRENT_SERVER_HANDLE, 
        WTS_CURRENT_SESSION,
        WTS_INFO_CLASS::WTSUserName,
        &currentUser,
        &dwSize_currentUser);

    if (!SeTakeOwnershipToken()) {
        std::cout << "|*|:在未获得SE_TAKE_OWNERSHIP_NAME特权时,继续更改文件所有者可能失败! " << std::endl;
    }

    // 更改ACL下的安全主体,以获得ACE控制权
    if (ChangeTrusteeViaObjectsSecurity(pszObjName,
        currentUser, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION))
    {
        // 覆盖ACL以获得完全控制的访问权限
        if (AddAceToObjectsSecurityDescriptor(pszObjName,
            SE_FILE_OBJECT,
            currentUser,
            TRUSTEE_IS_OBJECTS_AND_SID,
            GENERIC_ALL,
            GRANT_ACCESS,
            SUB_CONTAINERS_AND_OBJECTS_INHERIT) == -1)
        {
            std::cout << "ACE操作已执行。" << std::endl;
        }
    }
    else {
        std::cout << "无法更改ACL下的安全主体,继续更改ACE控制权可能会失败。" << std::endl;
        // 覆盖ACL以获得完全控制的访问权限
        if (AddAceToObjectsSecurityDescriptor(pszObjName,
            SE_FILE_OBJECT,
            currentUser,
            TRUSTEE_IS_OBJECTS_AND_SID,
            GENERIC_ALL,
            GRANT_ACCESS,
            SUB_CONTAINERS_AND_OBJECTS_INHERIT) == -1)
        {
            std::cout << "ACE操作已执行。" << std::endl;
        }

    }
    std::cout << "所有操作已完成。" << std::endl;
    system("pause");

    return 0;
}

2.2 调试效果

下图是在提升的命令提示符中执行的效果:

程序执行过程
程序执行过程

从图中可见程序执行完毕,并且没有错误。

下图展示的是程序执行完毕后,目标对象的属性变化:

目标被修改后的安全属性
目标被修改后的安全属性

从图中可以看出目标对象的所有者发生变化,且为当前登陆账户添加了完全控制权限。

三、利用Windows工具实现修改安全控制对象权限

3.1 通过 ICacls 命令实现修改访问控制权限

      icacls

      Intergrity Control Access Control List: 完整性权限控制列表
      Windows系统下控制文件及文件夹的访问权限的命令行指令,相当于Linux中的chmod
原命令cacls已经被废弃。

      这一篇博客园作者整理的命令详细信息比较全面清晰:Cacls和ICacls - 傩舞 - 博客园 (cnblogs.com)

3.2 通过 powershell / 注册表 实现修改访问控制权限

就不详细列出了,这里给出一篇知乎,整理的也比较全面:渗透技巧——Windows下的Access Control List - 知乎


本文为原创文章,转载请注明出处:

https://blog.csdn.net/qq_59075481/article/details/132459343

发布于:2023/08/23;修改于:2023/09/19,2024/03/14.

本人博客:涟幽516-程序员宅基地涟幽516擅长C++学习笔记,Win10动态壁纸开发,Windows上的编程,等方面的知识,涟幽516关注python,c++,c语言,系统安全领域.https://blog.csdn.net/qq_59075481

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_59075481/article/details/132459343

智能推荐

sqlite创建索引最佳实践-程序员宅基地

文章浏览阅读2.2k次。创建索引可以使 SQLite 数据库更快地执行查询,但是也会增加插入、更新和删除的时间。因此,在创建索引时,应该考虑查询和修改操作的平衡。在创建索引时,有几点可以遵循的最佳实践:使用 WHERE 子句中经常出现的列创建索引。使用列上的唯一约束或主键创建唯一索引。这有助于防止重复的值。避免使用过于长的索引,因为这会增加存储和维护索引的成本。如果表中有大量的 INSERT、UPDAT..._sqlite 创建索引

%d输出float类型,%f输出int类型_int型能用%f输出吗-程序员宅基地

文章浏览阅读3.6k次,点赞3次,收藏22次。// 输出格式与类型不一致,输出结果实际上与存储有关 // 1.int以%f格式输出,结果一般为0.000000 // 2.long long 类型的很大的数,用printf(“%f”)打印出来可能为一个小数; // 3.double、float以%d格式输出(输出时,float会转化为double存储形式;%d取低32位) // (1)结果一般为绝对值很大的数(当浮点数不能精确表示时,低32位不为0) // _int型能用%f输出吗

目标检测标注数据的检查和可视化_目标标注信息统计可视化-程序员宅基地

文章浏览阅读4.6k次。标注数据是一件十分枯燥的重复性工作。在标注数据时,有时因为数据量大,类别多,会出现错标、误标的情况,这时就需要对标注数据进行检查并且修改。_目标标注信息统计可视化

flutter 项目实践2_jj2d.top-程序员宅基地

文章浏览阅读2.5k次。本片文章来自与我自己的有道云笔记 要看图片请点击链接文档:Day 4_3 项目实践2.md链接:http://note.youdao.com/noteshare?id=f28e3058fea4d26f1b32bdc21f1a220c&sub=A3757D0FAF4C4BC29A8CF2E6F1C8DBD6上次做了什么我们把框架搭好了IndexedStack 分开了基础的页面制作了简单的首页 读取json文件 然后展示我们的首页基本就算制作完了然后我们想做一些展示home_cont_jj2d.top

elasticsearch-head连接问题_elasticsearch 开启xpack head如何连接-程序员宅基地

文章浏览阅读2.8k次。场景:最近打算使用elasticsearch做日志搜索,使用elasticsearch-head作为一个图形化界面查看index、type、document信息。elasticsearch 7.14.1mobz/elasticsearch-head:5地址:https://github.com/mobz/elasticsearch-head看完这个readme文档后,计划使用docker方式进行部署。1.更改elasticsearch.yml文件,包括xpack等配置,示例如下:_elasticsearch 开启xpack head如何连接

Selenium 根据元素文本内容定位_selenium java根据文字定位-程序员宅基地

文章浏览阅读1.8k次。使用xpath定位元素时,有时候担心元素位置会变,可以考虑使用文本内容来定位的方式。例如图中的【评价】按钮,只有按钮文本没变,就可以定位到该元素。_selenium java根据文字定位

随便推点

关于GEE导出数据时 含空值的栅格图像_selfmask-程序员宅基地

文章浏览阅读3.9k次,点赞8次,收藏25次。GEE提供的卫星数据大多数是有丢失的,若不加操作直接导出为tif,matlab读出来的矩阵该是空值的都是nan,然而arcgis并不能识别空值为nodata,导致制图非常难受。加上unmask函数可以让空值变为某个数字,这里设为-9999,方便后续处理。selfMask()则可以将图像的负值都判定为空。Export.image.toDrive({ image: xinjiang_CH4_anomaly.unmask(-9999), description: 'xinjiang_ch4_anom_selfmask

jmeter聚个报告怎么看qps_jmeter 聚合报告参数详解及TPS 解析-程序员宅基地

文章浏览阅读2.1k次。1、获得TPS插件https://www.cnblogs.com/beginner-boy/p/7806220.html 参见,已保存百度云盘2、添加后,记得使用调度器——每秒50个并发,持续60秒,观察TPS3、TPS,执行一次事务(包括请求、请求服务器、等待服务器返回等等,比如一个TPS事务,可能触发3个QPS请求)PS:一秒钟处理的事务数。TPS值越大,一秒钟处理的事务数就越多,说明处理速..._jmeter查看qps

浙大 PAT 甲级 1077 Kuchiguse_pat甲级可以用翻译吗-程序员宅基地

文章浏览阅读135次。读题目真是要笑死我了…太不正经了。很简单的20分~将题意翻译成代码即可。#include<iostream>#include<string>using namespace std;int main(){ int N; scanf("%d", &N); getchar(); string base; getlin..._pat甲级可以用翻译吗

centos7 svn服务器搭建_svn co authentication方法-程序员宅基地

文章浏览阅读1.5k次。基于centos7 的svn服务器配置_svn co authentication方法

capilot激活时遇到Sign in failed: Error: network error_sign in failed. reason: could not log in with devi-程序员宅基地

文章浏览阅读922次。capilot激活遇到问题,Sign in failed: Error: network error_sign in failed. reason: could not log in with device flow on cocopilot.org:

mysql 存储过程 序列_Oracle 生成序列号存储过程-程序员宅基地

文章浏览阅读147次。项目中经常要根据年月日规则生成序列号,简单写了个存储过程可根据需要扩展 一、序列号存储表 -- Create tablecreate table SYS_项目中经常要根据年月日规则生成序列号,简单写了个存储过程可根据需要扩展一、序列号存储表-- Create tablecreate table SYS_GENKEY(DATESTR VARCHAR2(50),MAXCOUNT NUMBER,TYP..._mysql 存储过程 序号

推荐文章

热门文章

相关标签