zoukankan      html  css  js  c++  java
  • 36、Windows API 用户、认证和对象安全

        Windows系统具有很完善的安全和认证机制,称作访问控制机制。程序的执行主体(线程)在访问对象(文件、事件等)时,系统会根据线程的“权限”和线程需要访问的对象所具有的访问控制列表(ACL)中的“安全描述符”是否匹配来进行认证,决定一个线程是否可以操作一个对象。

    一、基本概念

    1A需要访问(Access)BA就是访问的主体,B就是访问的客体。A的“访问令牌”和B的安全描述符共同决定了A是否可以访问B

        访问的主体是进程。在系统中,线程才是程序执行的流程,因此只有线程才能操作对象。每个线程都是属于一个进程的,线程并没有属于自己的权限,而是来源于线程所属于的进程。一个进程中的所有线程都具有同样的权限。一个线程能访问哪些对象,能进行哪此操作,是由线程权限决定的。

    访问的客体是安全对象,所有被访问的对象都具有安全描述符,包括了文件、注册表、事件(Event)、互斥(Mutex)、管道等。

    2、进程的权限继承自创建进程用户和用户所属的用户组。用户有专用数据结构来表示权限—访问令牌(Access Token)访问令牌包括两个部分:一个是令牌所表示的用户,包括用户标识符(SID),用户所属的用户组等;另一部分是“权限”(Privilege)

        在进程访问安全对象时,会用到SID。每个安全对象都有访问控制列表(ACL)ACL说明了哪些用户( SID)能访问本对象,哪些不能,以及能进行哪种访问等。而“权限”在访问某个具体的安全对象时并没有作用,“权限”是表示进程是否能够进行特定的系统操作,如关闭系统、修改系统时间、加载设备驱动等。

    创建进程的API函数是CreateProcessCreateProcess函数所创建的进程使用的访问令牌是当前登录用户的访问令牌。

        可以指定进程的用户。通过CreateProcessAsUserCreateProcessWithTokenWAPI函数,在创建前需要先得到用户的令牌,可以使用LogonUser登录用户(是否可以同时登录多个用户受操作系统版本限制),LogonUser函数用返回用户的令牌。

    如果需要得到进程和线程的访问令牌,可以使用OpenProcessTokenOpenThreadToken等函数。获取令牌中的信息可以使用API函数GetTokenInformation。如果需要修改权限,可以使用AdjustTokenPrivileges等函数。

    3、进程的系统操作权限

        进程的权限特指进程是否能够进行各种系统操作,例如是否可以关闭系统,是否能够修改系统时间,是否能够加载设备驱动等。权限是一个列表,每种权限是列表中的一项。权限列表存在于进程的访问令牌中。

    权限有很多种,每一种表示了一个特定的操作是否能够进行,如果进程的访问令牌中的权限列表中有这个权限,则表示进程可以进行这种操作,比如SE_LOAD_DRIVER_ NAME表示进程可以加载驱动。

    4、安全对象

        Windows系统几乎所有的对象都有安全属性,包括文件、文件夹、注册表、线程同步对象、进程间通信对象、网络共享等,进程和线程也可以是其他进程的操作对象,所以进程和线程也是安全对象。

    在创建对象时都可以指定对象的安全属性,比如CreateFileCreatePipeCreateProcessRegCreateKeyExRegSaveKeyEx等,SECURITY_ATTRIBUTES结构用于指定对象的安全属性。

    GetNamedSecurityInfoGetSecurityInfoSetSecurityInfoSetKernelObjectSecuritySetNamedSecurityInfoAPI函数可以获取和设置对象的安全属性。

    对象的安全属性是以安全描述符(Security Descriptor)的形式存在的,安全描述符中包括了访问控制列表。

    5、访问控制列表(ACL)

    每个安全对象都有访问控制列表。访问控制列表有两种,一种是选择访问控制列表(discretionary access control listDACL),另一种是系统访问控制列表(system access controllistSACL)DACL决定了用户或用户组是否能访问这个对象,SACL控制了尝试访问安全对象的检测信息的继承关系。

    DACL是访问控制的关键。DACL中包括一个访问控制入口(Access Control

    EntriesACE)列表。ACE表明了用户(通过用户SID或用户组SID)是否能进行操作以及能进行哪种操作。在进行访问控制检测时,会依次检测DACL中的ACE,直到被允许或被拒绝。

    wps_clip_image-2539

    wps_clip_image-30142

    ACE内容及线程访问令牌中的SIDDACL中的ACE的认证过程

    二、示例代码

    1、列举进程访问令牌内容和权限

    显示进程的访问令牌内容

    SDK安装目录下,Samples\Security\Authorization\MyToken

    显示SID

    获取SID可以通过LookupAccountName API函数。

    Samples\Security\Authorization\TextSid

    列举安全对象的安全描述符

    Samples\Security\Authorization\Check_SD

    修改安全描述符通过SetFileSecurity API

    三、用户

    增加

    NetUserAdd API函数在系统中创建用户[4]

    NetGroupAddUser

    NetUserDel

    NetUserEnumNetLocalGroupEnum API函数分别用于列举指定主机中当前的所有用户和用户组。

    示例用户操作

    用户、用户组
    /* UNICODE */
    #ifndef UNICODE
    #define UNICODE
    #endif
    /* 头文件 */
    #include
    <stdio.h>
    #include
    <assert.h>
    #include
    <windows.h>
    #include
    <lm.h>
    #pragma comment(lib,"Netapi32.lib")
    /*************************************
    * AddUser
    * 功能 增加用户
    * 参数 szServerName,主机名,如果为本机增加用户,设置为NULL
    * szUserName,用户名
    * szPassword,密码
    *************************************
    */
    int AddUser(LPWSTR szServerName,
    LPWSTR szUserName,
    LPWSTR szPassword)
    {
    USER_INFO_1 ui;
    DWORD dwLevel
    = 1; // 使用 USER_INFO_1 作为参数
    DWORD dwError = 0;
    NET_API_STATUS nStatus;
    // 填充 USER_INFO_1
    ui.usri1_name = szUserName; // 用户名
    ui.usri1_password = szPassword; // 密码
    ui.usri1_priv = USER_PRIV_USER; // privilege
    ui.usri1_home_dir = NULL;
    ui.usri1_comment
    = NULL;
    ui.usri1_flags
    = UF_SCRIPT;
    ui.usri1_script_path
    = NULL;
    // 调用 NetUserAdd 增加用户
    nStatus = NetUserAdd(szServerName,
    dwLevel,
    (LPBYTE)
    &ui,
    &dwError);

    // 判断结果
    if (nStatus == NERR_Success)
    {
    wprintf((
    const wchar_t*)stderr, L"User %s has been successfully added on %s\n",
    szUserName, szServerName);
    }
    else
    {
    fprintf(stderr,
    "A system error has occurred: %d\n", nStatus);
    }
    return 0;
    }

    /*************************************
    * AddUserToGroup
    * 功能 为用户组增加用户
    * 参数 szServerName,主机名,如果为本机,设置为NULL
    * szUserName,用户名
    * szGroup,用户组名
    *************************************
    */
    int AddUserToGroup(LPWSTR szServerName,
    LPWSTR szUserName,
    LPWSTR szGroup)
    {
    NET_API_STATUS nStatus;
    // 调用 NetGroupAddUser
    nStatus = NetGroupAddUser(
    szServerName,
    szGroup,
    szUserName
    );

    // 判断结果
    if (nStatus == NERR_Success)
    fwprintf(stderr, L
    "User %s has been successfully added on %s\n",
    szUserName, szServerName);

    else
    fprintf(stderr,
    "NetGroupAddUser A system error has occurred: %d\n", nStatus);
    return 0;
    }

    /*************************************
    * DelUser
    * 功能 删除用户
    * 参数 szServerName,主机名,如果为本机,设置为NULL
    * szUserName,用户名
    *************************************
    */
    int DelUser(LPWSTR szServerName, LPWSTR szUserName)
    {
    DWORD dwError
    = 0;
    NET_API_STATUS nStatus;

    // 调用 NetUserDel 删除用户
    nStatus = NetUserDel(szServerName, szUserName);
    // 判断并显示结果
    if (nStatus == NERR_Success)
    fwprintf(stderr, L
    "User %s has been successfully deleted on %s\n",
    szUserName, szServerName);
    else
    fprintf(stderr,
    "A system error has occurred: %d\n", nStatus);

    return 0;

    }

    /*************************************
    * int ListUsers(LPWSTR pszServerName)
    * 功能 列举用户
    * 参数 szServerName,主机名,如果为本机,设置为NULL
    *************************************
    */
    int ListUsers(LPWSTR pszServerName)
    {
    LPUSER_INFO_0 pBuf
    = NULL;
    LPUSER_INFO_0 pTmpBuf;
    DWORD dwLevel
    = 0;
    DWORD dwPrefMaxLen
    = MAX_PREFERRED_LENGTH;
    DWORD dwEntriesRead
    = 0;
    DWORD dwTotalEntries
    = 0;
    DWORD dwResumeHandle
    = 0;
    DWORD i;
    DWORD dwTotalCount
    = 0;
    NET_API_STATUS nStatus;

    // 循环,直到可以成功调用 NetUserEnum
    do
    {
    // 调用NetUserEnum函数
    nStatus = NetUserEnum(pszServerName,
    dwLevel,
    // 这里设置为0,使用 LPUSER_INFO_0 返回结果
    FILTER_NORMAL_ACCOUNT, // 只列举“正常”类型的用户
    (LPBYTE*)&pBuf,// LPUSER_INFO_0 保存返回结果
    // MAX_PREFERRED_LENGTH,内存由API分配,需要在之后调用NetApiBufferFree释放
    dwPrefMaxLen,
    &dwEntriesRead,// 读了的 Entries
    &dwTotalEntries,// 一共的 Entries
    &dwResumeHandle);
    // 判断是否成功
    if ((nStatus == NERR_Success) || (nStatus == ERROR_MORE_DATA))
    {
    if ((pTmpBuf = pBuf) != NULL)
    {
    // 循环读取用户信息
    for (i = 0; (i < dwEntriesRead); i++)
    {
    assert(pTmpBuf
    != NULL);

    if (pTmpBuf == NULL)
    {
    fprintf(stderr,
    "An access violation has occurred\n");
    break;
    }
    // 输出
    wprintf(L"\t-- %s\n", pTmpBuf->usri0_name);
    // 下一个
    pTmpBuf++;
    dwTotalCount
    ++;
    }
    }
    }
    else
    fprintf(stderr,
    "A system error has occurred: %d\n", nStatus);
    // 释放内存
    if (pBuf != NULL)
    {
    NetApiBufferFree(pBuf);
    pBuf
    = NULL;
    }
    }
    while (nStatus == ERROR_MORE_DATA); // end do

    // 释放内存
    if (pBuf != NULL)
    NetApiBufferFree(pBuf);

    fprintf(stderr,
    "Total of %d users\n\n", dwTotalCount);

    return 0;
    }

    /*************************************
    * int ListGroup(LPWSTR pszServerName)
    * 功能 列举用户组
    * 参数 szServerName,主机名,如果为本机,设置为NULL
    *************************************
    */
    int ListGroup(LPWSTR pszServerName)
    {

    DWORD dwLevel
    = 0;
    DWORD dwPrefMaxLen
    = MAX_PREFERRED_LENGTH;
    DWORD dwEntriesRead
    = 0;
    DWORD dwTotalEntries
    = 0;
    DWORD dwResumeHandle
    = 0;
    DWORD i;
    DWORD dwTotalCount
    = 0;
    NET_API_STATUS nStatus;


    LPLOCALGROUP_INFO_0 pBuf
    = NULL;
    LPLOCALGROUP_INFO_0 pTmpBuf;

    do // begin do
    {
    // 调用NetLocalGroupEnum 参数设置与NetLocalGroup类似
    nStatus = NetLocalGroupEnum(
    pszServerName,
    0,
    (LPBYTE
    *)&pBuf,
    dwPrefMaxLen,
    &dwEntriesRead,
    &dwTotalEntries,
    &dwResumeHandle);
    // 判断结果
    if ((nStatus == NERR_Success) || (nStatus == ERROR_MORE_DATA))
    {
    if ((pTmpBuf = pBuf) != NULL)
    {
    // 循环输出
    for (i = 0; (i < dwEntriesRead); i++)
    {
    assert(pTmpBuf
    != NULL);

    if (pTmpBuf == NULL)
    {
    fprintf(stderr,
    "An access violation has occurred\n");
    break;
    }

    wprintf(L
    "\t-- %s\n", pTmpBuf->lgrpi0_name);
    pTmpBuf
    ++;
    dwTotalCount
    ++;
    }
    }
    }

    else
    fprintf(stderr,
    "A system error has occurred: %d\n", nStatus);
    // 释放内存
    if (pBuf != NULL)
    {
    NetApiBufferFree(pBuf);
    pBuf
    = NULL;
    }
    }

    while (nStatus == ERROR_MORE_DATA); // end do

    if (pBuf != NULL)
    NetApiBufferFree(pBuf);

    fprintf(stderr,
    "Total of %d groups\n\n", dwTotalCount);

    return 0;
    }

    /*************************************
    * ShowUsersInfo
    * 功能 显示指定用户的信息
    * 参数 szServerName,主机名,如果为本机,设置为NULL
    * pszUserName,用户名
    *************************************
    */
    int ShowUsersInfo(LPWSTR pszServerName,LPWSTR pszUserName)
    {

    DWORD dwLevel
    = 4;// 使用 LPUSER_INFO_4 返回结果
    LPUSER_INFO_4 pBuf = NULL;
    NET_API_STATUS nStatus;

    nStatus
    = NetUserGetInfo(pszServerName,
    pszUserName,
    dwLevel,
    // pBuf参数类型
    (LPBYTE *)&pBuf);

    // 判断并输出结果
    if (nStatus == NERR_Success)
    {
    if (pBuf != NULL)
    {
    wprintf(L
    "\n\tAccount: %s\n", pBuf->usri4_name);
    wprintf(L
    "\tComment: %s\n", pBuf->usri4_comment);
    wprintf(L
    "\tUser comment: %s\n", pBuf->usri4_usr_comment);
    wprintf(L
    "\tFull name: %s\n", pBuf->usri4_full_name);
    wprintf(L
    "\tpriv: %d\n", pBuf->usri4_priv);
    }
    }

    else
    fprintf(stderr,
    "A system error has occurred: %d\n", nStatus);
    // 释放内存
    if (pBuf != NULL)
    NetApiBufferFree(pBuf);
    return 0;

    }

    /*************************************
    * wmain
    * 功能 入口函数,根据参数判断需要调用的功能函数
    * 参数 参见usage输出
    *************************************
    */
    int __cdecl wmain(int ac, wchar_t * av[])
    {

    if (ac == 4 && lstrcmpW( av[1], L"-a") == 0)
    {
    AddUser(NULL, av[
    2], av[3]);
    }

    else if (ac == 4 && lstrcmpW( av[1], L"-g") == 0)
    {
    AddUserToGroup(NULL, av[
    2], av[3]);
    }
    else if (ac == 3 && lstrcmpW( av[1], L"-i") == 0)
    {
    ShowUsersInfo(NULL, av[
    2]);
    }
    else if (ac == 2 && lstrcmpW( av[1], L"-i") == 0)
    {
    ListUsers(NULL);
    ListGroup(NULL);
    }
    else if (ac == 3 && lstrcmpW( av[1], L"-d") == 0)
    {
    DelUser(NULL, av[
    2]);
    }
    else
    {
    printf(
    "usage: \n"
    "\t %ws -a <username> <password> to add a user\n"
    "\t %ws -g <username> <group> add a user to a group"
    "\t %ws -i <username> to show user info\n"
    "\t %ws -d <username> to del a user\n",
    av[
    0], av[0], av[0], av[0]);
    }
    return 0;
    }

    参考

    [1] 精通Windows API 函数、接口、编程实例

    [2] http://www.cnblogs.com/mydomain/archive/2010/11/24/1887138.html

    [3] http://msdn.microsoft.com/en-us/library/aa378184%28VS.85%29.aspx

    [4] http://msdn.microsoft.com/en-us/library/aa370672%28VS.85%29.aspx

  • 相关阅读:
    zookeeper单机环境搭建
    js中获取url?后面的参数值
    【转】java 、javaw、javaws命令的区别
    【转】解决 linux和widows双系统时差8个小时问题
    IntrospectorCleanupListener作用
    Linux 网络相关命令
    SAS:提取数据集观测,宏参数
    sas share
    SAS:多个LOG的批量查询
    SAS:日期,字符数值转换
  • 原文地址:https://www.cnblogs.com/mydomain/p/1962655.html
Copyright © 2011-2022 走看看