修改 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

智能推荐

艾美捷Epigentek DNA样品的超声能量处理方案-程序员宅基地

文章浏览阅读15次。空化气泡的大小和相应的空化能量可以通过调整完全标度的振幅水平来操纵和数字控制。通过强调超声技术中的更高通量处理和防止样品污染,Epigentek EpiSonic超声仪可以轻松集成到现有的实验室工作流程中,并且特别适合与表观遗传学和下一代应用的兼容性。Epigentek的EpiSonic已成为一种有效的剪切设备,用于在染色质免疫沉淀技术中制备染色质样品,以及用于下一代测序平台的DNA文库制备。该装置的经济性及其多重样品的能力使其成为每个实验室拥有的经济高效的工具,而不仅仅是核心设施。

11、合宙Air模块Luat开发:通过http协议获取天气信息_合宙获取天气-程序员宅基地

文章浏览阅读4.2k次,点赞3次,收藏14次。目录点击这里查看所有博文  本系列博客,理论上适用于合宙的Air202、Air268、Air720x、Air720S以及最近发布的Air720U(我还没拿到样机,应该也能支持)。  先不管支不支持,如果你用的是合宙的模块,那都不妨一试,也许会有意外收获。  我使用的是Air720SL模块,如果在其他模块上不能用,那就是底层core固件暂时还没有支持,这里的代码是没有问题的。例程仅供参考!..._合宙获取天气

EasyMesh和802.11s对比-程序员宅基地

文章浏览阅读7.7k次,点赞2次,收藏41次。1 关于meshMesh的意思是网状物,以前读书的时候,在自动化领域有传感器自组网,zigbee、蓝牙等无线方式实现各个网络节点消息通信,通过各种算法,保证整个网络中所有节点信息能经过多跳最终传递到目的地,用于数据采集。十多年过去了,在无线路由器领域又把这个mesh概念翻炒了一下,各大品牌都推出了mesh路由器,大多数是3个为一组,实现在面积较大的住宅里,增强wifi覆盖范围,智能在多热点之间切换,提升上网体验。因为节点基本上在3个以内,所以mesh的算法不必太复杂,组网形式比较简单。各厂家都自定义了组_802.11s

线程的几种状态_线程状态-程序员宅基地

文章浏览阅读5.2k次,点赞8次,收藏21次。线程的几种状态_线程状态

stack的常见用法详解_stack函数用法-程序员宅基地

文章浏览阅读4.2w次,点赞124次,收藏688次。stack翻译为栈,是STL中实现的一个后进先出的容器。要使用 stack,应先添加头文件include<stack>,并在头文件下面加上“ using namespacestd;"1. stack的定义其定义的写法和其他STL容器相同, typename可以任意基本数据类型或容器:stack<typename> name;2. stack容器内元素的访问..._stack函数用法

2018.11.16javascript课上随笔(DOM)-程序员宅基地

文章浏览阅读71次。<li> <a href = "“#”>-</a></li><li>子节点:文本节点(回车),元素节点,文本节点。不同节点树:  节点(各种类型节点)childNodes:返回子节点的所有子节点的集合,包含任何类型、元素节点(元素类型节点):child。node.getAttribute(at...

随便推点

layui.extend的一点知识 第三方模块base 路径_layui extend-程序员宅基地

文章浏览阅读3.4k次。//config的设置是全局的layui.config({ base: '/res/js/' //假设这是你存放拓展模块的根目录}).extend({ //设定模块别名 mymod: 'mymod' //如果 mymod.js 是在根目录,也可以不用设定别名 ,mod1: 'admin/mod1' //相对于上述 base 目录的子目录}); //你也可以忽略 base 设定的根目录,直接在 extend 指定路径(主要:该功能为 layui 2.2.0 新增)layui.exten_layui extend

5G云计算:5G网络的分层思想_5g分层结构-程序员宅基地

文章浏览阅读3.2k次,点赞6次,收藏13次。分层思想分层思想分层思想-1分层思想-2分层思想-2OSI七层参考模型物理层和数据链路层物理层数据链路层网络层传输层会话层表示层应用层OSI七层模型的分层结构TCP/IP协议族的组成数据封装过程数据解封装过程PDU设备与层的对应关系各层通信分层思想分层思想-1在现实生活种,我们在喝牛奶时,未必了解他的生产过程,我们所接触的或许只是从超时购买牛奶。分层思想-2平时我们在网络时也未必知道数据的传输过程我们的所考虑的就是可以传就可以,不用管他时怎么传输的分层思想-2将复杂的流程分解为几个功能_5g分层结构

基于二值化图像转GCode的单向扫描实现-程序员宅基地

文章浏览阅读191次。在激光雕刻中,单向扫描(Unidirectional Scanning)是一种雕刻技术,其中激光头只在一个方向上移动,而不是来回移动。这种移动方式主要应用于通过激光逐行扫描图像表面的过程。具体而言,单向扫描的过程通常包括以下步骤:横向移动(X轴): 激光头沿X轴方向移动到图像的一侧。纵向移动(Y轴): 激光头沿Y轴方向开始逐行移动,刻蚀图像表面。这一过程是单向的,即在每一行上激光头只在一个方向上移动。返回横向移动: 一旦一行完成,激光头返回到图像的一侧,准备进行下一行的刻蚀。

算法随笔:强连通分量-程序员宅基地

文章浏览阅读577次。强连通:在有向图G中,如果两个点u和v是互相可达的,即从u出发可以到达v,从v出发也可以到达u,则成u和v是强连通的。强连通分量:如果一个有向图G不是强连通图,那么可以把它分成躲个子图,其中每个子图的内部是强连通的,而且这些子图已经扩展到最大,不能与子图外的任一点强连通,成这样的一个“极大连通”子图是G的一个强连通分量(SCC)。强连通分量的一些性质:(1)一个点必须有出度和入度,才会与其他点强连通。(2)把一个SCC从图中挖掉,不影响其他点的强连通性。_强连通分量

Django(2)|templates模板+静态资源目录static_django templates-程序员宅基地

文章浏览阅读3.9k次,点赞5次,收藏18次。在做web开发,要给用户提供一个页面,页面包括静态页面+数据,两者结合起来就是完整的可视化的页面,django的模板系统支持这种功能,首先需要写一个静态页面,然后通过python的模板语法将数据渲染上去。1.创建一个templates目录2.配置。_django templates

linux下的GPU测试软件,Ubuntu等Linux系统显卡性能测试软件 Unigine 3D-程序员宅基地

文章浏览阅读1.7k次。Ubuntu等Linux系统显卡性能测试软件 Unigine 3DUbuntu Intel显卡驱动安装,请参考:ATI和NVIDIA显卡请在软件和更新中的附加驱动中安装。 这里推荐: 运行后,F9就可评分,已测试显卡有K2000 2GB 900+分,GT330m 1GB 340+ 分,GT620 1GB 340+ 分,四代i5核显340+ 分,还有写博客的小盒子100+ 分。relaybot@re...

推荐文章

热门文章

相关标签