当前位置: 首页 > news >正文

WINDOWS下调用GetTokenInformation的奇怪之处--两次调用

就是用getLastErr可以得到错误号,同时,会将需要的长度写到参数里,再进行第二次调用,以此来节约内存空间。

神奇的长见识了。

相关说法如下:

======================

The error occurs because the buffer is insufficient. :-) It's not large enough for the content. Once again, I refer you to the documentation. If the function fails, the ReturnLength parameter is set to the size of the buffer needed, so you can allocate sufficient memory and call the function again. The general rule for many API calls that have variable buffer requirements is "Call the function once with a NULL buffer to determine the buffer size needed, allocate the memory, and then call it again to actually retrieve the information"

======================

The TOKEN_USER structure contains pointers (in particular, a pointer to a SID that itself has variable length). Those pointers have to point somewhere. The API function will expect a buffer big enough to hold not only the the TOKEN_USER structure, but also all the things that structure points to. The function tells you how much memory it needs for everything. It will all reside in adjacent memory.

======================

最近又在重新编写一些以前已经实现过的功能, 一来是温习之前学的一些东西, 二是希望多次重写一个功能的过程中提升自己现有的编程技术和水平, 今天这篇文章算是一个小的总结.

 
在写之前请容许我唠叨片刻, 文章中涉及的技术与代码示例可能早遍布在百度与GOOGLE上的各种原创或转载文章中, 所以我的内容多少也会与那些文章中的东西有所相同, 对于那些已经看过那些文章或者早已熟悉这些技术朋友可能会略显平庸, 不过这都算是自己对技术积累的心得吧, 见笑了, 那么言归正传了.
 
Win32 API中提供了GetTokenInformation与SetTokenInformation函数, 这2个函数别分是检索与设置一个访问令牌的信息, 关于访问令牌的具体细节有兴趣的朋友可以去MSDN上找, 因为与文章主题没有直接的关系, 所以就不在具体细说了.
以下是GetTokenInformation的API原型(如果想对这个函数的细节做更多了解, 可以去MSDN上查询它的详细信息)
 
  
WINADVAPI
BOOL
WINAPI
GetTokenInformation (
          _In_       HANDLE TokenHandle, 
       _In_       TOKEN_INFORMATION_CLASS TokenInformationClass, 
        _Out_opt_  LPVOID TokenInformation, _In_       
      DWORD TokenInformationLength, _Out_     
        PDWORD ReturnLength
   );
总得来说, 这个函数算是Win32 API中比较有代表性的一个, 参数的含义也都是一目了然, 这里就不再过多獒述了(注: 关于GetTokenInformation参数含义这里倒是有一个趣事, 如果有兴趣看的朋友可以在文章最后找到我对它的说明), 有兴趣的朋友可以去MSDN上找这个API的具体文档, 下面我用一段代码示例来演示如何使用GetTokenInformation函数检索进程的所有特权信息
 
  
static PTOKEN_GROUPS GetProcessGroups(HANDLE hProcess)
{
 HANDLE hToken;
  PTOKEN_GROUPS pGroups = NULL;
   DWORD neededSize;
 
       // 这里会先获得一个进程访问令牌的句柄, 并且只允许该句柄具有信息查询的权限.
        if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
          return NULL;
 
  
        // 这里第一次调用GetTokenInformation是为了获取令牌信息需要的缓冲区的大小,
        // 这是在使用Win32 API中的一个常用做法, 很多Win32 API都可以这样使用, 目的
       // 是不用让程序员总是创建一个假设一定足够的缓冲区, 这在很多时候会造成空间上的浪费
     neededSize = 0;
 GetTokenInformation(hToken, TokenGroups, NULL, neededSize, &neededSize);
        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && neededSize > 0)
      {
               pGroups = HeapAlloc(GetProcessHeap(), 0, neededSize);
           if (pGroups != NULL)
            {
                       // 这里是真正去检索特权信息, 并且检查成功或失败.
                     if (!GetTokenInformation(hToken, TokenGroups, pGroups, neededSize, &neededSize))
                        {
                               HeapFree(GetProcessHeap(), 0, pGroups);
                         pGroups = NULL;
                 }
               }
       }
       CloseHandle(hToken);
    return pGroups;
}
这个函数是用C编写的, 关键性的代码旁边都有注释说明其函数, 到这里文档的主要内容也就结束了, 不过值得多说一句的是, GetTokenInformation不仅可以获取进程的特权信息, 同时还能得到其它更多的信息, 比如进程所属的所有用户组, 进程拥有者以及虚拟化信息等, 而TOKEN_INFORMATION_CLASS会告诉GetTokenInformation你想要哪种信息.
 
关于GetTokenInformation函数参数的趣事
我在使用GetTokenInformation过程中发现了一个有趣的事情, 那就是Microsoft似乎在维护文档与Platform SDK头文件时出现了歧义, 
BOOL
WINAPI
GetTokenInformation (
    __in      HANDLE TokenHandle,
    __in      TOKEN_INFORMATION_CLASS TokenInformationClass,
    __out_bcount_part_opt(TokenInformationLength, *ReturnLength) LPVOID TokenInformation,
    __in      DWORD TokenInformationLength,
    __out_opt PDWORD ReturnLength
    );
此时ReturnLength这个参数带有_out_opt这样的修饰符, 它在WIN32 API中的文档约定是指被修饰的参数可以传一个NULL指针进去而不会任何问题, 最多也就是你不知道实际填充到TokenInformation缓冲区的令牌信息占用了多少字节, 不过有趣的是API的实现似乎是遵照了在MSDN官网文档中定义的形式, 也就是ReturnLength这个值必须指向一个有效的DWORD类型的变量, 所以如果你在调用GetTokenInformation时传递给ReturnLength是一个NULL值, 那么GetTokenInformation会返回失败, 并且调用GetLastError()会返回错误代码87(ERROR_INVALID_PARAMETER), 同样的问题可能不只是出现在GetTokenInformation函数中, 其它一些少数API可能也存在此类问题, 所以使用的时候还需要多加留意才行.
在MSDN的文档中ReturnLength这个参数被定义为"必选"参数, 这里所谓的"必选"一词是指它不是一个NULL指针值, 也就是不允许这样的调用行为GetTokenInformation(...., NULL);, 但如果细心的朋友在Microsoft Visual Stdio 2005中查看这个WinBase.h中这个函数的原型声明时会发现它的声明形式如下:

相关文章:

  • 修改windows的语言
  • ReactNativeweexDeviceOne对比
  • jquery去重
  • 码农看天下
  • nfs/samba相关
  • 定时(隔一段时间)提交ajax更新未读消息
  • 【案例】主从替换之后的复制风暴
  • redis密码管理
  • 认识CoreData-使用进阶
  • CentOS系统误删除文件怎么恢复
  • 各种有用的链接
  • 分享磨砺营马剑威老师讲解-okhttp的优点
  • 逃离烤鸭味的雾霾北京,我们还能去哪里?
  • [nginx文档翻译系列] 控制nginx
  • awakeFromNib相关知识详解
  • 《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息
  • 【React系列】如何构建React应用程序
  • 2019.2.20 c++ 知识梳理
  • 77. Combinations
  • Apache Zeppelin在Apache Trafodion上的可视化
  • CODING 缺陷管理功能正式开始公测
  • Docker: 容器互访的三种方式
  • Java 网络编程(2):UDP 的使用
  • Java小白进阶笔记(3)-初级面向对象
  • jquery ajax学习笔记
  • Magento 1.x 中文订单打印乱码
  • Spring思维导图,让Spring不再难懂(mvc篇)
  • Webpack 4 学习01(基础配置)
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 官方解决所有 npm 全局安装权限问题
  • 快速构建spring-cloud+sleuth+rabbit+ zipkin+es+kibana+grafana日志跟踪平台
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 浅谈JavaScript的面向对象和它的封装、继承、多态
  • 什么是Javascript函数节流?
  • 我是如何设计 Upload 上传组件的
  • 小程序01:wepy框架整合iview webapp UI
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • 学习ES6 变量的解构赋值
  • ​LeetCode解法汇总518. 零钱兑换 II
  • ​卜东波研究员:高观点下的少儿计算思维
  • #pragma pack(1)
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (附源码)springboot工单管理系统 毕业设计 964158
  • (剑指Offer)面试题41:和为s的连续正数序列
  • (考研湖科大教书匠计算机网络)第一章概述-第五节1:计算机网络体系结构之分层思想和举例
  • (十六)Flask之蓝图
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • (轉貼) VS2005 快捷键 (初級) (.NET) (Visual Studio)
  • .form文件_一篇文章学会文件上传
  • .libPaths()设置包加载目录
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)
  • .net core Swagger 过滤部分Api
  • .net Stream篇(六)
  • .net 发送邮件
  • .Net 垃圾回收机制原理(二)