2004-06-22

从net user命令谈起

标题: net user查询命令的自行实现、兼谈学习方法

日期: 2003-12-11 13:09

"net user username"
命令可以查看本机用户信息,比如密码是否过期,但无法查看指定服务器上指定用户信息。如果做为域用户登录,情况有变化。现在的需求是不想做为域用户登录,又想查看域控制器上的用户信息。



Samba
所带的rpcclient满足需求:

--------------------------------------------------------------------------
$ ./rpcclient -U scz <target>
Password:
rpcclient $> lookupnames scz
scz S-1-5-21-999999999-999999999-999999999-1173
rpcclient $> queryuser 1173
    User Name                   : scz
    Logon Time                  : Thu, 11 Dec 2003 09:23:33
GMT
    Password last set Time      : Wed, 24 Sep 2003 13:27:03 GMT
    Password can change Time    : Wed, 24 Sep 2003 13:27:03 GMT
    Password must change Time   : Tue, 23 Dec 2003 13:27:03 GMT
    user_rid                    : 0x495
    group_rid                   : 0x201
rpcclient $>
--------------------------------------------------------------------------

先用lookupnames获取目标用户的RID,然后用queryuser查询之。上述信息处理过,只保留了我们感兴趣的部分。

MSDN
里找到NetUserGetInfo()函数,附了一个例子,查询USER_INFO_10信息。我们需要查询USER_INFO_3信息。

如果已经存在到目标服务器的SMB会话,直接利用之。也可指定用户、密码重新建立
SMB
会话。MSDN的例子太简单,基本不实用。

USER_INFO_3
结构中没有密码过期的直接信息,只有usri3_password_age成员用于记录最后一次设置密码到"当前时刻"之间的秒数。显然只用NetUserGetInfo()不能满足原始需求。

lusrmgr.msc中没有密码何时过期的设置。但在gpedit.msc中有:

本地计算机策略
   
计算机配置
        Windows
设置
           
安全设置
                
帐户策略
                   
密码策略
                       
密码最短存留期
                       
密码最长存留期

闹了半天这是全局设置,不是用户名相关的设置。如果我对Windows使用熟悉的话,早该想到不能只依赖USER_INFO_3结构。看样子net user sczrpcclient这些命令报告密码何时过期是结合了组策略信息、服务器当前时间的。

取服务器当前时间,减去usri3_password_age,就是最后一次设置密码的时刻。然后分别加上密码最短存留期、密码最长存留期,就是何时可以修改密码、何时必须修改密码的时刻。

取服务器当前时间,需要区分目标是远程还是本地。远程时用NetRemoteTOD()获取,本地时用C运行时库函数time()获取。

queryuser.c
没有使用ctime(),因其使用了静态缓冲区,多线程编程时不太妙。尽管queryuser.c本身是单线程程序,但是在时间允许的前提下,不应该降低编程要求,为此自行实现了PrintTimeString(),这个过程也比较困难,缺乏sampleMSDN中的例子总是跟不上需求。

USER_INFO_3
结构中的时间是RtlTimeC运行时库计算时间以1970年以基准,Win32
API
计算时间以1601年为基准。这就涉及一个转换问题:

( RtlTime + 11644473600 ) * 10000000 -> FILETIME

MSDN
垃圾到连这点都不提,我是测试最初的queryuser.c发现问题后用Google搜出来
的。

剩下就是如何获取组策略信息。tk提到过net.exe最终调用了net1.exe。用dumpbin
看之:

> dumpbin /imports %systemroot%system32
et1.exe | find
"Net"
      71C219C5     DF NetShareEnum
      71C54782     B2
NetRemoteTOD
      71C42EC3     EF
NetUserGetInfo
      71C42B4F     ED NetUserEnum
      71C43878     F1 NetUserModalsGet
      71C43BB3     F2 NetUserModalsSet
      71C47413     D4
NetServiceEnum

有些已经很熟悉了,有些还很陌生,注意到NetUserModalsGet()。在MSDN中确认要找的就是它,查询USER_MODALS_INFO_0信息即可。

以上就是解决原始需求的整个过程,记录备忘,学习方法本身也需要不断总结。经验
积累得多了,就越容易发现问题、解决问题。

操作系统的不同永远不应该成为老虎吃天、无处下爪的理由。做为程序员,依据已有知识、经验,配合正确的学习方法、学习手段以及不可或缺的勤奋快速向陌生领域的纵深挺进,这是最基本的"素质"要求,否则不适合做程序员。数学史上的希尔伯特、军事史上的古德里安都是这种快速向陌生领域纵深挺进的典范。虽然无法望及大师们的项背,但学点精神总是可以的。

有几点学习经验:

1)
勇于、勤于Google,看到一切陌生但可能有意思的关键字,勇敢地将它们丢到互
  
联网上去搜索。不要放过一丝可能相关的信息。我曾经将Google的页面点击到45
  
页以后。

  
在一个txt文件中记录URL,而不是在浏览器提供的收藏夹中保存URL。不要只记录
  
跟当前工作相关的URL,而应该记录那些自己曾经感兴趣、正在感兴趣、即将感兴
  
趣的URL

2)
有空的时候,不要看跟当前工作紧密相关的书籍,而应该看看不相关的方向。精
  
一个方向是正确的,但不能局限在这个视界里。

  
要避免贪多导致的最终一事无成。

  
不要陷入辩论中。辩论是口头上的巨人,对自己没什么好处。带着思辩的哲学观
  
点静观其"",可能对自己更有帮助些。各种操作系统的优劣,其辩论者十之七
  
八是跟风者。各种语言的比较,其辩论者十之七八是跟风者。

  
因时因地做出选择,没有亘古不变的选择。

3)
不要狂妄自大,但也不可妄自菲薄。每个人有自己擅长的,也有自己一窍不通的。

  
保持良好的学习心态,对自己写出来的疑问、猜测、回答、建议负责。提问的时
  
候,不应显得卑下,但保持必要的对陌生人的小学生守则上就有的礼节。回答的
  
时候,尽可能提供有帮助的信息,而不是""

  
一定要多交流,顾步自封显然会有麻烦。要不耻下问、上问。

4)
理论->实践->再理论,这条方法论至今不过时。学习理论,不迷信理论,大胆做
  
出科学的猜测,即使将来推翻了自己的猜测,也应记录下这个过程。如果实践肯
  
定了自己的猜测,更要立即整理、抽象、升华。

  
要勤于记录学习过程,结果固然重要,解决问题的过程同样重要。如果不是为了
  
树立一贯正确的高大形象,不想造个神出来,不妨将自己无数次失败的瞬间记录
  
下来,以后就可以少走很多弯路。

上述每一条背后都有真实的故事,以后有机会了,可以写写这些故事。经验是与人相
关的,仅仅是个人的经验,不代表真理,不代表指导思想。写出来,是为了交流学习
方法本身,没有别的意思。我也只是很普通的程序员,还好,不是太丢CS的人。

没有评论:

发表评论