ADSI Hunter
Active Directory查询工具。
引言
如果您在一个微软网络工作,那么您很可能正在使用 Active Directory (AD)。Active Directory 存储有关域网络资源的信息。这些信息需要特定的权限才能更新,但通常允许经过身份验证的用户进行查询。
我开发了这个工具,就是为了进行这类查询。它提供了一个已知(网络)域名的列表,并允许用户查看组、组成员、用户和用户详细信息,而无需深入进行 LDAP 查询。总之,它易于使用、快速,并提供了比普通用户实际所需更多的信息。
此工具仅使用 .NET Framework 2.0 开发。在 ADSI 操作中没有互操作程序集或 Win32 API 调用,只有一个用于“关于”框动画的 Win32 API 调用。这是一个 .NET 2.0 Windows Forms 应用程序。
背景
我工作的许多组织都利用 AD 通过组来管理应用程序和资源访问。不幸的是,对我(和其他人)来说,许多组织不允许访问 Microsoft Active Directory 工具,因此验证某个用户是否被授予了某个特定组的成员资格可能会有点麻烦。因此,这个工具应运而生。
Using the Code
UI 本身非常简单。只是一个典型的 .NET Windows Forms 应用程序。应用程序的核心位于 ADLookup
类中。这个类执行所有 AD 活动,用于填充 UI 中的列表。浏览源代码将为您提供 .NET 环境中 AD 搜索世界的入门介绍(可能是一次粗暴的介绍)。
如果您查看上面的图片,箭头表示列表中某个选择将触发被指向列表的自动更新。此外,如果用户从“组内用户”列表中选择了一个用户,该用户也将被选在“域用户”列表中,从而触发后续更新。同样,从“用户组”列表中选择一个组将该组选在“域组”列表中,从而触发后续更新。每个列表上方的括号中的数字表示该列表中的元素数量。这可以一目了然地回答最常见的 AD 问题之一:“xx 组中有多少用户?”
搜索
要使用搜索功能,您需要从“搜索”菜单中选择一个搜索选项,或者您可以右键单击“域内组”、“组内用户”或“域用户”列表。当您选择其中一个时,将弹出一个窗口供您输入搜索数据。您输入的搜索数据用作正则表达式,以评估所选列表中的数据,因此请随时使用 .NET 正则表达式执行模糊搜索。
仅选择搜索条件的第一个匹配项。当它被选中后,相应的列表内容也会得到更新。
关注点
ADLookup
类中有三个方法值得在此稍加关注。这三个方法用于解码从 AD 查询的用户属性集合中返回的字节数组。
首先,简单的一个 - SIDToString
/// <summary>
/// Convert a binary SID to a string.
/// </summary>
/// <param name="sidBinary">SID to convert.</param>
/// <returns>String representation of a SID.</returns>
private string SIDToString(byte[] sidBinary)
{
SecurityIdentifier sid = new SecurityIdentifier(sidBinary, 0);
return sid.ToString();
}
这个方法最好的地方在于,将 Windows SID(安全标识符)位数组转换为人类可读字符串几乎没有任何难度。
下一个是一个注册表查找,用于确定系统中当前活动的时间偏移量。这是系统用于将格林威治标准时间 (GMT) 转换为本地时间的值。
/// <summary>
/// Retrieve the current machine ActiveTimeBias.
/// </summary>
/// <returns>an integer representing the ActiveTimeBias in hours.</returns>
private int GetActiveBias()
{
// Open the TimeZone key
RegistryKey key =
Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet" +
@"\Control\TimeZoneInformation");
if (key == null)
return 0;
// Pick up the time bias
int Bias = (int)key.GetValue("ActiveTimeBias");
// Close the parent key
key.Close();
// return the result adjusted for hours (instead of minutes)
return (Bias / 60);
}
这个值总是从 GMT 中减去,以得出本地时间。在我住的地方,我们使用夏令时和标准时间,所以我的 ActiveTimeBias
值将是 7(太平洋夏令时间 [PDT])或 8(太平洋标准时间 [PST])。
我们在这里最后介绍的方法是 DecodeLoginHours
。在 AD 用户属性集合中,存在限制用户登录系统时间的属性。该属性包含一个 21 字节的数组,其中每个位代表一个小时段,从星期日午夜 GMT 开始。请注意,我说的是 GMT。这就是 ActiveTimeBias
的用武之地。通过进行减法运算,我们可以将位数组重新对齐到机器时间。显然,这个位数组对人类来说并不友好,所以我们将其解码成易于阅读的内容。在 UI 中,它将在“用户属性”列表中显示为“登录时间:> 单击查看 <”。用户自然需要单击列表中的项才能获得以下显示。
/// <summary>
/// Translate the hours into something readable.
/// </summary>
/// <param name="HoursValue">Hours to convert.</param>
/// <returns>A string indicating the hours of availability.</returns>
private string DecodeLoginHours(byte[] HoursValue)
{
// See if we have anything
if (HoursValue.Length < 1)
return string.Empty;
// Pick up the time zone bias
int Bias = GetActiveBias();
// Convert the HoursValue array into a character array of 1's and 0's.
// That's a really simple statement for a bit of a convoluted process:
// The HoursValue byte array consists of 21 elements (21 bytes) where
// each bit represents a specified login hour in Universal Time
// Coordinated (UTC). These bits must be reconstructed into an array
// that we can display (using 1's and 0's) and associated correctly to
// each of the hour increments by using the machines current timezone
// information.
// Load the HoursValue byte array into a BitArray
// This little trick also allows us to read through the array from
// left to right, rather than from right to left for each of the 21
// elements of the Byte array.
BitArray ba = new BitArray(HoursValue);
// This is the adjusted bit array (accounting for the ActiveTimeBias)
BitArray bt = new BitArray(168);
// Actual index in target array
int ai = 0;
// Copy the source bit array to the target bit array with offset
for (int i = 0; i < ba.Length; i++)
{
// Adjust for the ActiveTimeBias
ai = i - Bias;
if (ai < 0)
ai += 168;
// Place the value
bt[ai] = ba[i];
}
// Time to construct the output
int colbump = 0;
int rowbump = 0;
int rowcnt = 0;
StringBuilder resb = new StringBuilder();
resb.Append(" ------- Hour of the Day -------");
resb.Append(Environment.NewLine);
resb.Append(" M-3 3-6 6-9 9-N N-3 3-6 6-9 9-M");
resb.Append(Environment.NewLine);
resb.Append(_DayOfWeek[rowcnt]);
for (int i = 0; i < bt.Length; i++)
{
// Put in a 0 or a 1
resb.Append((bt[i]) ? "1" : "0");
colbump++;
rowbump++;
// After 24 elements are written, start the next line
if (rowbump == 24)
{
// Make sure we're not on the last element
if (i < (bt.Length - 1))
{
rowbump = 0;
colbump = 0;
resb.Append(Environment.NewLine);
rowcnt++;
resb.Append(_DayOfWeek[rowcnt]);
}
}
else
{
// Insert a space after every 3 characters
// unless we've gone to a new line
if (colbump == 3)
{
resb.Append(" ");
colbump = 0;
}
}
}
// Return the result
return resb.ToString();
}
历史
- 版本 1.2.1.0 - 向所有人发布初始版本
- 版本 1.2.1.1 - 修复了当
TimeBias
值为负数时LoginHours
的解码问题