如何判断一台机器是否属于域

来源:互联网 发布:火星车打车软件 编辑:程序博客网 时间:2024/04/29 11:36

解决问题的方向
查看 Windows LOGON 的描述.

1.概念.

NT的LOGON描述开始了.在讲述NT的LOGON之前,有几个关键的字眼要说一下.
LOGON进程: NT的登录进程.拥有工作站,桌面,键盘,鼠标的独占控制.
GINA: Graphical Identification and Authentication,图形身份验证
GINA STUB: 实现部分GINA接口的一个DLL.
LSA: Local Security Authority, 本地安全验证
MPR: Multiple Provider Router.网络供应者路由器.:D
NP: Network Provider.网络供应者.

LOGON进程乃是登录的初始者,也是用户在使用安装有NT计算机时通常会经历的登录阶段的前台程序.LOGON本身是个很特殊的应用程序.名字为登录,但是实际上并不负责登录验证.他仅仅收集用户名,密码,域的信息.进行一些特定的操作.这些特定的操作将在后面讲到.

GINA 实际上是个DLL.MS对他的定义是图形身份验证,其实也不负责验证.那么为什么要称为图形身份验证呢?关键是LOGON进程把验证请求扔给GINA后就不管了.对LOGON来说就是验证了.:DDD 这是我的猜测不是MS官方的信息. GINA本身是LOGON用到的一个DLL.这个DLL在LOGON的地址空间内. GINA本身能够很大程度上影响LOGON的运行逻辑.

GINA STUB,MS介绍了GINA STUB的概念,它是个轻量级的GINA.本身实现部分的接口,其他的接口仍然有老的GINA实现,GINA STUB则负责PASS DOWN函数调用.

LSA,从INSIDE NT书上讲,LSASS(LSA的实际实现)乃是LOGON进程的子进程.从实际的NT运行实际看,尽管LSASS是LOGON的子进程,但是在逻辑上却是平级的.LOGON通过LPC和LSASS交互,来实际进行登录操作.

MPR, 很关键的NT网络实现组件.由于NT允许多个网络架构的存在,如同时有NETWARE服务器存在于NT网络上.所以,MPR成为了WINDOWS网络应用的实际枢纽.如同在本机上的IO管理器,拥有对本机的绝对权威,而MPR则在本机访问网络时有绝定性作用.从NT的实际实现上看,任何一个 WIN32API在访问一个资源时,IO管理器都会检查该资源是否为网络资源.如果为网络资源,则将控制转MPR.有MPR负责对该资源的控制.
NP: 为了让多种架构的网络同时并存于NT.网络供应商必须提供他自己的DLL,并且实现MS规定的特定的接口,以变MPR对网络资源的调度.


当NT启动LOGON时,LOGON会加载一些DLL.这些DLL就是GINA和一些NP.(关于大体的描述,在INSIDE NT第二版里有描述.这里描述一些关键的小细节.)
GINA本身有注册表里的/HKEY_LOCAL_MACHINE/Software/Microsoft/Windows NT/CurrentVersion/Winlogon/
里的键GinaDLL定义.该处的值为GINA的实际DLL名.GINA负责的乃是提供用户界面和搜集登录信息.通常对应LOGON进程最大的误解就是 LOGON负责登录的验证,但是其实真正的验证是在LSA进行的.由于GINA本身能够获取纯文本形式的用户名和密码,所以黑白两道都对GINA青眼有加.但是正由于GINA本身被这么多的程序员盯住,任何人都不能保证自己开发的GINA不会在其他软件被人家的GINA替换调.这里我将先分析GINA, 并且尝试给出另外的方法获取登录通知.
LOGON本身和LSA的联接需要调用
NTSTATUS
LsaRegisterLogonProcess(
IN PLSA_STRING LogonProcessName,
OUT PHANDLE LsaHandle,
OUT PLSA_OPERATIONAL_MODE SecurityMode
);
由于所有的LSA调用都是特权调用,连ADMINISTRATOR也没有权限调用该函数.LSA调用只有操作系统才能进行,这些都是出于安全的考虑.为了表明自己就是OS或者自己从属于OS的一部分,调用者必须具有SeTcbPrivilege特权.
当LsaRegisterLogonProcess 成功后,调用者就获得的一个对LSA调用的令牌.现在,LSA已经知道,有个进程是登录进程,而且获得了他的注册名供审计用.现在,LOGON进程需要个 GINA沟通了.因为验证需要通过GINA参与进行.根据MS的功能模块定义,GINA才是和LSA最终打交道的模块.
那么就看看如何和GINA沟通吧.
GINA 通过EXPORT一个函数WlxNegotiate告诉LOGON自己的版本,并且得到LOGON的版本,用以支持不同的的协议集.当LOGON和 GINA都认为对方的版本可以满足自己的运行需求时,GINA的WlxInitialize函数被调用.在该函数被调用时,GINA将获得LOGON提供的函数集.
NT3.51支持的函数集为
Typedef struct __win32_DISPATCH_VERSION_1_0 {
PWLX_USE_CTRL_ALT_DEL         WlxUseCtrlAltDel;
PWLX_SET_CONTEXT_POINTER       WlxSetContextPointer;
PWLX_SAS_NOTIFY             WlxSasNotify;
PWLX_SET_TIMEOUT           WlxSetTimeout;
PWLX_ASSIGN_SHELL_PROTECTION   WlxAssignShellProtection;
PWLX_MESSAGE_BOX           WlxMessageBox;
PWLX_DIALOG_BOX             WlxDialogBox;
PWLX_DIALOG_BOX_PARAM         WlxDialogBoxParam;
PWLX_DIALOG_BOX_INDIRECT       WlxDialogBoxIndirect;
PWLX_DIALOG_BOX_INDIRECT_PARAM   WlxDialogBoxIndirectParam;
PWLX_SWITCH_DESKTOP_TO_USERT   WlxSwitchDesktopToUser;
PWLX_SWITCH_DESKTOP_TO_WINLOGON WlxSwitchDesktopToWinlogon;
PWLX_CHANGE_PASSWORD_NOTIFY     WlxChangePasswordNotify;
} WLX_DISPATCH_VERSION_1_0;
NT4.0支持的函数集为
Typedef struct __win32_DISPATCH_VERSION_1_1 {
PWLX_USE_CTRL_ALT_DEL         WlxUseCtrlAltDel;
PWLX_SET_CONTEXT_POINTER       WlxSetContextPointer;
PWLX_SAS_NOTIFY             WlxSasNotify;
PWLX_SET_TIMEOUT           WlxSetTimeout;
PWLX_ASSIGN_SHELL_PROTECTION   WlxAssignShellProtection;
PWLX_MESSAGE_BOX           WlxMessageBox;
PWLX_DIALOG_BOX             WlxDialogBox;
PWLX_DIALOG_BOX_PARAM         WlxDialogBoxParam;
PWLX_DIALOG_BOX_INDIRECT       WlxDialogBoxIndirect;
PWLX_DIALOG_BOX_INDIRECT_PARAM   WlxDialogBoxIndirectParam;
PWLX_SWITCH_DESKTOP_TO_USERT   WlxSwitchDesktopToUser;
PWLX_SWITCH_DESKTOP_TO_WINLOGON WlxSwitchDesktopToWinlogon;
PWLX_CHANGE_PASSWORD_NOTIFY     WlxChangePasswordNotify;
PWLX_GET_SOURCE_DESKTOP       WlxGetSourceDesktop;
PWLX_SET_RETURN_DESKTOP       WlxSetReturnDesktop;
PWLX_CREATE_USER_DESKTOP       WlxCreateUserDesktop;
} WLX_DISPATCH_VERSION_1_1;
NT4.0SP4的函数集为
Typedef struct __win32_DISPATCH_VERSION_1_2 {
PWLX_USE_CTRL_ALT_DEL         WlxUseCtrlAltDel;
PWLX_SET_CONTEXT_POINTER       WlxSetContextPointer;
PWLX_SAS_NOTIFY             WlxSasNotify;
PWLX_SET_TIMEOUT           WlxSetTimeout;
PWLX_ASSIGN_SHELL_PROTECTION   WlxAssignShellProtection;
PWLX_MESSAGE_BOX           WlxMessageBox;
PWLX_DIALOG_BOX             WlxDialogBox;
PWLX_DIALOG_BOX_PARAM         WlxDialogBoxParam;
PWLX_DIALOG_BOX_INDIRECT       WlxDialogBoxIndirect;
PWLX_DIALOG_BOX_INDIRECT_PARAM   WlxDialogBoxIndirectParam;
PWLX_SWITCH_DESKTOP_TO_USERT   WlxSwitchDesktopToUser;
PWLX_SWITCH_DESKTOP_TO_WINLOGON WlxSwitchDesktopToWinlogon;
PWLX_CHANGE_PASSWORD_NOTIFY     WlxChangePasswordNotify;
PWLX_GET_SOURCE_DESKTOP       WlxGetSourceDesktop;
PWLX_SET_RETURN_DESKTOP       WlxSetReturnDesktop;
PWLX_CREATE_USER_DESKTOP       WlxCreateUserDesktop;
PWLX_CHANGE_PASSWORD_NOTIFY_EX   WlxChangePasswordNotifyEx;
PWLX_CLOSE_USER_DESKTOP       WlxCloseUserDesktop;
} WLX_DISPATCH_VERSION_1_2
注意,在MSDN里声称1.2版函数表在WIN2000里启用是不对的.其实,从NT4SP4就开始了.并且,由于对应函数集支持的问题,很多第三方的GINA就被废了.导致系统不能启动.
WIN2000的函数集为
Typedef struct __win32_DISPATCH_VERSION_1_3 {
PWLX_USE_CTRL_ALT_DEL         WlxUseCtrlAltDel;
PWLX_SET_CONTEXT_POINTER       WlxSetContextPointer;
PWLX_SAS_NOTIFY             WlxSasNotify;
PWLX_SET_TIMEOUT           WlxSetTimeout;
PWLX_ASSIGN_SHELL_PROTECTION   WlxAssignShellProtection;
PWLX_MESSAGE_BOX           WlxMessageBox;
PWLX_DIALOG_BOX             WlxDialogBox;
PWLX_DIALOG_BOX_PARAM         WlxDialogBoxParam;
PWLX_DIALOG_BOX_INDIRECT       WlxDialogBoxIndirect;
PWLX_DIALOG_BOX_INDIRECT_PARAM   WlxDialogBoxIndirectParam;
PWLX_SWITCH_DESKTOP_TO_USERT   WlxSwitchDesktopToUser;
PWLX_SWITCH_DESKTOP_TO_WINLOGON WlxSwitchDesktopToWinlogon;
PWLX_CHANGE_PASSWORD_NOTIFY     WlxChangePasswordNotify;
PWLX_GET_SOURCE_DESKTOP       WlxGetSourceDesktop;
PWLX_SET_RETURN_DESKTOP       WlxSetReturnDesktop;
PWLX_CREATE_USER_DESKTOP       WlxCreateUserDesktop;
PWLX_CHANGE_PASSWORD_NOTIFY_EX   WlxChangePasswordNotifyEx;
PWLX_CLOSE_USER_DESKTOP       WlxCloseUserDesktop;
PWLX_SET_OPTION             WlxSetOption;
PWLX_GET_OPTION             WlxGetOption;
PWLX_WIN31_MIGRATE           WlxWin31Migrate;
PWLX_QUERY_CLIENT_CREDENTIALS   WlxQueryClientCredentials;
PWLX_QUERY_IC_CREDENTIALS     WlxQueryInetConnectorCredentials;
PWLX_DISCONNECT             WlxDisconnect;
} WLX_DISPATCH_VERSION_1_3;
这些函数在GINA本身的使用中将十分有用.当获取这些信息的同时,GINA通常会分配一些自己需要的内存.在MS自己的样板例程里,GINA需要分配一段内存,记录的是一些全局变量.这些变量的定义如下:
typedef struct _Globals {
  BOOL             fAllowNewUser;
  BOOL             fAutoLogonAtBoot;
  BOOL             fAutoLogonAlways;
  HANDLE             hUserToken;
  PMiniAccount         pAccount;
} Globals, * PGlobals;
PMiniAccount指针指向自己更感兴趣的用户名和密码结构:
typedef struct _MiniAccount {
  struct _MiniAccount *   pNext;
  PWSTR             pszUsername;
  PWSTR             pszDomain;
  PWSTR             pszPassword;
  PWSTR             pszComment;
  DWORD             IconId;
  DWORD             Flags;
} MiniAccount, * PMiniAccount;


好了,现在GINA相对于LOGON的初始化已经完成.但是现在还没有用户登录界面.当然,为了让GINA有机会显示欢迎图画,LOGON要求GINA EXPORT一个函数:
VOID WINAPI WlxDisplaySASNotice(PVOID   pContext);
该函数的唯一功能就是显示欢迎窗口.并且监督是否有自己定义的SAS(安全提示码序列)出现.如果出现,则要通知LOGON有个登录请求发出.SAS在NT 下缺省情况为CTL+ALT+DEL.但是并不一定只有CTL+ALT+DEL才能作为SAS.GINA本身可以提供SAS监督.当然,用CTL+ALT +DEL更为方便.因为在初始化时GINA已经收到了一个函数:
VOID WlxUseCtrlAltDel(HANDLE hWlx);
该函数通知LOGON,GINA使用CTL+ALT+DEL为SAS.如果要用其他的SAS,GINA必须自己HOOK热键.并且通过:
WlxSasNotify(hGlobalWlx, dwSasType);
报告SAS出现.由于HOOK将更增GINA写作工作量,而且MS提供缺省的CTL+ALT+DEL服务,那么用CTL+ALT+DEL在决大多数情况下就够了.
会到话题.现在出现的是欢迎画面.当用户键入CTL+ALT+DEL时,登录接口出现了.:DDD现在我们就进入了MS所谓的INTERACTIVE LOGON.
现在,LOGON调用GINA的
int WlxLoggedOutSAS(
PVOID           pWlxContext,
DWORD           dwSasType,
PLUID           pAuthenticationId,
PSID             pLogonSid,
PDWORD           pdwOptions,
PHANDLE           phToken,
PWLX_MPR_NOTIFY_INFO pMprNotifyInfo,
PVOID           *pProfile
);
函数.GINA现在用这个函数来显示对话框并且收集用户信息并且调用LSA进行验证.如果通过了验证,那么就会填写MprNotifyInfo结构.这个结构用以通知MPR:用户登录喽!
此时,NT带的GINA会分析本机是否在一个DOMAIN里,根据不同的情况显示不同的对话框.当用户机器不属于DOMAIN.那么根本就没机会填写 DOMAIN名字.当机器隶属于一个NT网络架构的网,用户将可以有DOMAIN域填写.用以告知NT用户希望以什么身份登录.


上次说到WlxLoggedOutSAS.该函数的实现中,GINA搜集了用户名,密码.如果机器在网络上,还有域名也被搜集到了.当然,搜集用户名和密码本身并不是GINA的根本目的.GINA的根本目的是要把经过授权验证的用户登录.现在,GINA就根据搜集到的用户名和密码调用Lsa来登录用户.
首先,必须调用的是
NTSTATUS
LsaRegisterLogonProcess(
IN PLSA_STRING LogonProcessName,
OUT PHANDLE LsaHandle,
OUT PLSA_OPERATIONAL_MODE SecurityMode
);
该函数只有有SeTcbPrivilege的特权的程序可以调用.
通过第一参数,审计程序将记录是什么进程尝试了登录.这样才能达到所谓的C2级安全性:所有的行为能够被审计.
当该函数成功后,进程将获得和LSA交互的权限.
然后,调用
NTSTATUS
LsaLookupAuthenticationPackage(
IN HANDLE LsaHandle,
IN PLSA_STRING PackageName,
OUT PULONG AuthenticationPackage
);
GINA将查找MSV1_0_PACKAGE_NAME的验证包.该包提供缺省的登录验证服务.
当验证包被确定后,调用
NTSTATUS
LsaLogonUser(
IN HANDLE LsaHandle,
IN PLSA_STRING OriginName,
IN SECURITY_LOGON_TYPE LogonType,
IN ULONG AuthenticationPackage,
IN PVOID AuthenticationInformation,
IN ULONG AuthenticationInformationLength,
IN PTOKEN_GROUPS LocalGroups OPTIONAL,
IN PTOKEN_SOURCE SourceContext,
OUT PVOID *ProfileBuffer,
OUT PULONG ProfileBufferLength,
OUT PLUID LogonId,
OUT PHANDLE Token,
OUT PQUOTA_LIMITS Quotas,
OUT PNTSTATUS SubStatus
);
这里乃是一个LSA的CALL.通知LSA有人企图登录啦.
现在程序的控制被转移到了LSASS.
我们回头在讲述一下LSA这里的动静.
LSA和LOGON进程交互时,既然LOGON进程指定了验证包,那么验证包的初始化是一定要进行的.所以,LSA自己对于验证包要求必须EXPORT一个函数.这个函数就是
NTSTATUS
LsaApInitializePackage(
IN ULONG AuthenticationPackageId,
IN PLSA_DISPATCH_TABLE LsaDispatchTable,
IN PLSA_STRING Database OPTIONAL,
IN PLSA_STRING Confidentiality OPTIONAL,
OUT PLSA_STRING *AuthenticationPackageName
);
这个函数将只被调用一次.以后就永远不被调用了.并且是在系统启动LSASS时就调用了,并不等待LOGON的CALL才进行.
LSA当被调用LsaLogonUser时,LSA本身并不处理多少,将调用传递到
NTSTATUS
LsaApLogonUser(
IN PLSA_CLIENT_REQUEST ClientRequest,
IN SECURITY_LOGON_TYPE LogonType,
IN PVOID AuthenticationInformation,
IN PVOID ClientAuthenticationBase,
IN ULONG AuthenticationInformationLength,
OUT PVOID *ProfileBuffer,
OUT PULONG ProfileBufferLength,
OUT PLUID LogonId,
OUT PNTSTATUS SubStatus,
OUT PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
OUT PVOID *TokenInformation,
OUT PLSA_UNICODE_STRING *AccountName,
OUT PLSA_UNICODE_STRING *AuthenticatingAuthority
);
就算完事.同时,由于C2审计的原因,上面的函数缺少对客户机器名的审计,所以导致下面函数也进入了规范.
NTSTATUS
LsaApLogonUserEx(
IN PLSA_CLIENT_REQUEST ClientRequest,
IN SECURITY_LOGON_TYPE LogonType,
IN PVOID AuthenticationInformation,
IN PVOID ClientAuthenticationBase,
IN ULONG AuthenticationInformationLength,
OUT PVOID *ProfileBuffer,
OUT PULONG ProfileBufferLength,
OUT PLUID LogonId,
OUT PNTSTATUS SubStatus,
OUT PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
OUT PVOID *TokenInformation,
OUT PUNICODE_STRING *AccountName,
OUT PUNICODE_STRING *AuthenticatingAuthority,
OUT PUNICODE_STRING *MachineName
);
最后一个参数表明了客户机的名称.在微软自己的验证包里,它返回一个NETBIOS机器名.


当LsaApLogonUser (Ex/Ex2)被调用时,验证包就获得了用户名和密码等信息.我们可以看到,在AuthenticationInformation指针处其实乃是一个关键的结构.通常我们执行的是MSV1_0_INTERACTIVE_LOGON,指针则指向
typedef struct _MSV1_0_INTERACTIVE_LOGON {
MSV1_0_LOGON_SUBMIT_TYPE MessageType;
UNICODE_STRING LogonDomainName;
UNICODE_STRING UserName;
UNICODE_STRING Password;
} MSV1_0_INTERACTIVE_LOGON;
结构.MessageType用来表明这次LOGON是什么样的LONON.这里的一切都是以明文形式存在,包括用户名,密码,域名/机器名.:)
现在,MS的验证包就开始登录验证.从SAM里找出用户名,并验证密码.然后返回用户的令牌.
对于登录目标不是本机,而是网络主域,那么LOGON调用NETLOGON SERVICE完成登录.对于LSA来说,NETLOGN则比LOGON进程差不多.因为他们都调用LSA来完成登录.而NETLOGON比LOGON多调用几次LsaApLogonUser(Ex/Ex2).因为需要完成对主域控制器和后备服务器的登录同步.
调用LSA的LsaLogonUser时.MS的验证包判断机器名是否为本机,如果不是本机,则执行一个PASS-THROUGH验证.
也就是说,验证由目标机验证包完成.本机验证包负责传送数据到目标机.
我们讲述一下PASS-THROUGH验证.
本机将登录请求发送到本机所属的域控制器.然后域控制器检查是否域名和自己的域名相同.如果相同,则验证于域控制器继续进行.完成验证后返回数据给本机.并且允许访问本机和域资源.如果域名不同于控制器的域名,域控制器检查是否该域是自己的信任域.如果是,域控制器将验证PASS到该信任域的域控制器.由信任域的域控制器验证.验证完毕后,数据返回到域控制器,由域控制器返回数据到本机.而验证的具体步骤则仍如上所述.
验证完成后.一个令牌被返回.验证包填写的令牌信息为:
typedef struct _LSA_TOKEN_INFORMATION_V1 {
LARGE_INTEGER ExpirationTime;
TOKEN_USER User;
PTOKEN_GROUPS Groups;
TOKEN_PRIMARY_GROUP PrimaryGroup;
PTOKEN_PRIVILEGES Privileges;
TOKEN_OWNER Owner;
TOKEN_DEFAULT_DACL DefaultDacl;
} LSA_TOKEN_INFORMATION_V1, *PLSA_TOKEN_INFORMATION_V1;
而实际LsaLogonUser获得的是根据这些信息合成的TOKEN HANDLE.TOKEN本身的数据结构是不公开的.尽管我认为了解该结构似乎没有很大的意义.但是通过KD的!tokenfields命令我们可以看到数据结构.
另外还返回了用户的LOGON脚本于ProfileBuffer:
typedef struct _MSV1_0_INTERACTIVE_PROFILE {
MSV1_0_PROFILE_BUFFER_TYPE MessageType;
USHORT LogonCount;
USHORT BadPasswordCount;
LARGE_INTEGER LogonTime;
LARGE_INTEGER LogoffTime;
LARGE_INTEGER KickOffTime;
LARGE_INTEGER PasswordLastSet;
LARGE_INTEGER PasswordCanChange;
LARGE_INTEGER PasswordMustChange;
UNICODE_STRING LogonScript;
UNICODE_STRING HomeDirectory;
UNICODE_STRING FullName;
UNICODE_STRING ProfilePath;
UNICODE_STRING HomeDirectoryDrive;
UNICODE_STRING LogonServer;
ULONG UserFlags;
} MSV1_0_INTERACTIVE_PROFILE, *PMSV1_0_INTERACTIVE_PROFILE;

原创粉丝点击