LDAP¶
Active Directory 架构(AD 架构) - Win32 apps | Microsoft Learn
类(AD 架构) - Win32 apps | Microsoft Learn
属性(AD 架构) - Win32 apps | Microsoft Learn
Active Directory 域服务 - Win32 apps | Microsoft Learn
UserAccountControl¶
UserAccountControl 属性标志 - Windows Server | Microsoft Learn
UserAccountControl property flags - Windows Server | Microsoft Learn
UserAccountControl Attribute Values - Active Directory Pro
下面是特定对象的默认 UserAccountControl 值:
- 典型用户:0x200(512)
- 域控制器:0x82000(532480)
- 工作站/服务器:0x1000 (4096)
- 信任:0x820 (2080)
msDS-SupportedEncryptionTypes¶
用户、计算机或信任帐户支持的加密算法
ms-DS-Supported-Encryption-Types 属性 - Win32 apps | Microsoft Learn
Supported Encryption Types Bit Flags | Microsoft Learn
Set MSDS-SupportedEncryptionTypes PowerShell Explained
ObjectGUID¶
Object-Guid 属性 - Win32 apps | Microsoft Learn
读取对象的 objectGUID 和创建 GUID 的字符串表示形式 - Win32 apps | Microsoft Learn
RFC 4122 标准 | datatracker.ietf.org
objectGUID 属性是用户的唯一标识符。 该属性是单值 128 位全球唯一标识符 (GUID),并以 ADS_OCTET_STRING 结构的形式存储。 GUID 由 Active Directory 服务器在创建用户对象时创建。
如果重命名或移动对象,则对象可分辨名称会发生变化,因此可分辨名称并非可靠的对象标识符
在 Active Directory 域服务中,对象的 objectGUID 属性永远不会更改,即使重命名或移动对象也是如此
可以使用IAD 属性方法 中的 GUID 属性方法来检索 objectGUID 的字符串形式
UUID 的结构¶
UUID 遵循 RFC 4122 标准 ,格式为:
字段 | 位数 | 十六进制字符数 | 含义 | 版本相关性 |
---|---|---|---|---|
time_low | 32位 | 8 | 时间戳的低32位(自1582年10月15日以来的100纳秒数) | 版本1专用 |
time_mid | 16位 | 4 | 时间戳的中16位 | 版本1专用 |
time_hi_version | 16位 | 4 | 时间戳的高12位,高4位表示UUID版本号(M字段,如4xxx中的4) | 版本1专用(版本号占4位,其余版本不同) |
clock_seq_hi_variant | 8位 | 2 | 时钟序列的高8位,最高2位表示变体标识(N字段,如yxxx中的y,值为10xx) | 版本1/4通用 |
clock_seq_low | 8位 | 2 | 时钟序列的低8位 | 版本1/4通用 |
node | 48位 | 12 | 节点地址(如MAC地址)或随机生成的48位值 | 版本1(MAC)、版本4(随机) |
objectGUID 存储格式¶
在 Windows Active Directory 中,objectGUID
以 Windows GUID 结构 存储,其二进制格式与标准 UUID 不同,主要区别在于 字节序 (大小端)
生成原理¶
- 版本依赖
objectGUID 的生成通常基于 UUID版本1 (时间戳+节点ID)或 版本4 (随机数),具体取决于AD环境配置:
-
版本1 :结合时间戳(自1582年10月15日以来的100纳秒数)和节点唯一标识符(如网卡MAC地址)生成,确保跨时间和空间的唯一性
-
版本4 :通过加密安全的随机数生成器创建,降低可预测性,适用于对隐私敏感的场景
-
唯一性保障
-
域控制器在创建对象时生成objectGUID,并保证其全局唯一性
-
即使跨域或跨森林迁移,objectGUID也不会重复
-
若无法获取MAC地址(如虚拟化环境),节点ID可能替换为其他唯一硬件标识符或随机值
-
变体标识
objectGUID遵循RFC 4122定义的变体标识规则,第四组首字节的高两位固定为10xx
(如8xxx
、9xxx
、axxx
、bxxx
),表明其符合标准UUID格式
变体标识的规则¶
变体标识(二进制) | 十六进制范围 | 含义 |
---|---|---|
0xxx | 0xxx | 保留(未使用) |
10xx | 8xxx,9xxx,axxx,bxxx | 标准变体(RFC 4122定义的UUID格式) |
110x | cxxx,dxxx | 保留(Microsoft的GUID使用此变体,但不符合RFC 4122) |
111x | exxx - fxxx | 保留(未定义) |
- 核心规则:
标准UUID(如版本1-5)的变体标识必须为 10xx,即第四组首字符的十六进制值必须是 8、9、a 或 b
- 版本1的UUID :
生成时,第四组的首字符会随机选择 8、9、a 或 b 中的一个
例如:6ba7b810-9dad-11d1-80b4-00c04fd430c8(首字符为 8)
- 版本4的UUID :
完全随机生成,但必须保证第四组首字符符合 8、9、a、b 的规则
例如:f47ac10b-58cc-4372-a567-0e02b2c3d479(首字符为 a)
Windows GUID¶
Windows GUID与标准UUID的字节顺序不同
Windows Active Directory 中的 objectGUID
以 Windows GUID结构 存储,其二进制格式为:
type GUID struct {
Data1 uint32 // 小端序 (4字节)
Data2 uint16 // 小端序 (2字节)
Data3 uint16 // 小端序 (2字节)
Data4 [8]byte // 原始顺序 (8字节)
}
变体标识的位置¶
对于微软的GUID be959a0e-e78e-4d33-96be-7664d7fce138
,其变体标识(Variant)的解析如下:
- UUID格式 :
xxxxxxxx-xxxx-4xxx-**yxxx**-xxxxxxxxxxxx
其中yxxx
是第四组,首字符为9
(即96be
中的9
)。 - 二进制表示 :
十六进制的
9
对应二进制1001
,高两位为10
,符合RFC 4122定义的标准变体标识(10xx
)
具体分析¶
- 版本号 :第三组首字符为
4
(4d33
中的4
),表示该GUID是 版本4(随机生成) - 变体标识 :第四组首字符为
9
(96be
中的9
),对应变体10xx
,符合RFC 4122标准 - 结论 :这是一个标准的RFC 4122 GUID,由微软生成但遵循通用规则,不属于微软私有的非标准变体
变体标识的含义¶
字段 | 值 | 含义 |
---|---|---|
变体标识 | 10xx | 表示该GUID遵循 RFC 4122标准(即通用唯一标识符的标准格式) |
具体值 | 9 | 第四组首字符为 9,对应二进制 1001,高两位 10 符合标准变体规则 |
微软GUID的变体规则¶
微软的GUID生成规则与RFC 4122基本一致,但存在以下特点:
- 标准变体(10xx)
大多数现代Windows系统生成的GUID(如版本1、4)均使用标准变体,即第四组首字符为 8、9、a 或 b。
- 非标准变体(110x)
早期Windows系统(如Windows NT 4.0)可能生成变体标识为 110x(十六进制以 c 或 d 开头)的GUID,但这类格式已逐渐淘汰
ObjectSID¶
对象 SID (WinNT 提供程序) - Win32 apps | Microsoft Learn
Attribute objectSid | Microsoft Learn
将 objectSid 转换为可绑定字符串的示例代码 - Win32 apps | Microsoft Learn
objectSid 属性是用户的安全标识符 (SID)
-
在与 Windows 安全系统交互时,系统会使用 SID 来识别用户及其组成员身份
-
该属性为单值, SID 是一个唯一的二进制值,用于将用户识别为安全主体
-
SID 由系统在创建用户时设置,每个用户都有一个由 Windows 域签发的唯一 SID,并存储在目录中用户对象的 objectSid 属性中
-
用户每次登录时,系统都会从目录中检索用户的 SID,并将其放入用户的访问令牌中
-
用户的 SID 还用于检索用户所属组的 SID,并将其放入用户的访问令牌中。当一个 SID 被用作一个用户或组的唯一标识符时,就不能再用来标识其他的用户或组
SID的二进制结构¶
SID (winnt.h) - Win32 apps | Microsoft Learn
SID(安全标识符)的二进制格式遵循固定结构,具体如下:
字段 | 长度(字节) | 说明 |
---|---|---|
Revision | 1 | 版本号(通常为1) |
SubAuthorityCount | 1 | 子权威数量(表示后续有多少个子权威值) |
IdentifierAuthority | 6 | 标识符权威(高32位的权威值,通常以大端序存储) |
SubAuthorities | 4 × n | 子权威列表(每个子权威为32位整数,以小端序存储) |
使用标准表示法将 SID 从二进制转换为字符串格式时,SID 的组件更容易可视化:
S-R-X-Y1-Y2-Yn-1-Yn
在这种表示法中,下表描述了 SID 的组成部分:
组件 | 描述 |
---|---|
S | 指示字符串是 SID |
R | 指示修订级别 |
X | 指示标识符颁发机构值 |
Y | 表示一系列子授权值,其中 n 是值的数目 |
SID 最重要的信息包含在一系列子颁发机构值中,系列的第一部分 (-Y1-Y2-Yn-1) 是域标识符。
子颁发机构值系列中的最后一项 (-Yn) 是 RID,它将一个帐户或组与域中的所有其他帐户和组区分开,在任何域中,两个帐户或组不共享相同的 RID
例如,内置管理员组的 SID 以标准化 SID 表示法表示为以下字符串:
S-1-5-32-544
此 SID 包含四个组成部分:
- 修订级别 (1)
- 标识符颁发机构值(5, NT Authority)
- 域标识符 (32, Builtin)
- RID(544,管理员)
内置帐户和组的 SID 始终具有相同的域标识符值,即 32
通用公认 SID¶
下表列出了通用的已知 SID:
通用公认 SID | 名称 | 标识 |
---|---|---|
S-1-0-0 | 空 SID | 没有成员的组。 当 SID 值未知时,通常使用此值。 |
S-1-1-0 | 世界 | 包括所有用户的组。 |
S-1-2-0 | 本地 | 登录到本地(物理)连接到系统的终端的用户。 |
S-1-2-1 | 控制台登录 | 包括登录到物理控制台的用户的组。 |
S-1-3-0 | 创建者所有者 ID | 要替换为创建新对象的用户的 SID 的 SID。 此 SID 用于可继承的访问控制条目 (ACE)。 |
S-1-3-1 | 创建者组 ID | 要替换为创建新对象的用户的主组 SID 的 SID。 在可继承 ACE 中使用此 Sid. |
S-1-3-2 | 所有者服务器 | 可继承的 ACE 中的占位符。 继承 ACE 后,系统会将此 SID 替换为对象的所有者服务器的 SID,并存储给定对象或文件的创建者相关信息。 |
S-1-3-3 | 组服务器 | 可继承的 ACE 中的占位符。 继承 ACE 后,系统将此 SID 替换为对象的组服务器的 SID。 系统还会存储有关允许使用对象的组的信息。 |
S-1-3-4 | 所有者权限 | 表示对象的当前所有者的组。 当承载此 SID 的 ACE 应用于对象时,系统将忽略对象所有者对隐式 READ_CONTROL 和 WRITE_DAC 的标准访问权限。 |
S-1-4 | 非唯一颁发机构 | 表示标识符颁发机构的 SID。 |
S-1-5 | NT Authority (NT AUTHORITY) | 表示标识符颁发机构的 SID。 |
S-1-5-80-0 | 所有服务 | 包含系统上配置的所有服务进程的组。 操作系统控制此组的成员身份。 |
更详细内容查阅安全标识符 | Microsoft Learn
GeneralizedTime¶
String(Generalized-Time) 语法 - Win32 apps | Microsoft Learn
ASN.1 标准定义的时间字符串格式,使用此语法以通用时间格式存储时间值
Go 中的时间转换:
func GeneralizedTime(entry *ldap.Entry, attribute string) (string, error) {
b := entry.GetAttributeValue(attribute)
t, err := time.Parse("20060102150405.0Z", b)
if err != nil {
return "", err
}
return t.Local().Format(time.DateTime), nil
}
FileTimeToTime¶
FileTimeToLocalFileTime 函数 (fileapi.h) - Win32 apps | Microsoft Learn
FileTimeToSystemTime 函数 (timezoneapi.h) - Win32 apps | Microsoft Learn
Go 中的时间转换:类似 FileTimeToSystemTime 函数,转换后得到 Go 的 time.Time
// Windows FileTime 时间起点(1601-01-01 00:00:00 UTC)
const (
// FileTimeToUnixEpochDiff 1601-01-01 到 1970-01-01 的 100 纳秒间隔数
FileTimeToUnixEpochDiff = 116444736000000000
// NanoSecondsPerHundredNanoSeconds 100 纳秒单位转换为纳秒的比例因子
NanoSecondsPerHundredNanoSeconds = 100
)
// FileTimeToTime 将 LDAP 条目中的 Windows FileTime 属性转换为格式化时间字符串
// 支持的属性包括: lastLogon, pwdLastSet, accountExpires 等
// 返回格式: "2006-01-02 15:04:05" (UTC 时间)
func FileTimeToTime(entry *ldap.Entry, attribute string) (string, error) {
// 参数校验
if entry == nil {
return "", fmt.Errorf("ldap entry is nil")
}
if attribute == "" {
return "", fmt.Errorf("attribute name is empty")
}
// 获取属性值
rawValue := entry.GetAttributeValue(attribute)
if rawValue == "" {
return "", fmt.Errorf("attribute '%s' not found or is empty", attribute)
}
// 转换为整数(Windows FileTime 是 18 位数字字符串)
fileTime, err := strconv.ParseInt(rawValue, 10, 64)
if err != nil {
return "", fmt.Errorf("failed to parse '%s' as int64: %w", attribute, err)
}
// 处理特殊值:0 表示从未发生(如未登录)
if fileTime == 0 {
return "", fmt.Errorf("zero value for '%s' (never occurred)", attribute)
}
// 时间转换逻辑
var unixNano int64
if fileTime >= FileTimeToUnixEpochDiff {
// 正常情况:从 1601-01-01 开始的时间值
unixNano = (fileTime - FileTimeToUnixEpochDiff) * NanoSecondsPerHundredNanoSeconds
} else {
// 异常情况:小于 epochDiff 的值(例如未来时间或无效数据)
return "", fmt.Errorf("invalid filetime value '%d' for '%s'", fileTime, attribute)
}
// 构造 time.Time 对象并格式化输出
timestamp := time.Unix(0, unixNano).UTC()
return timestamp.Format("2006-01-02 15:04:05"), nil
}