65.9K
CodeProject 正在变化。 阅读更多。
Home

ADSI Hunter

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.79/5 (9投票s)

2008年8月21日

CPOL

4分钟阅读

viewsIcon

57001

downloadIcon

1242

Active Directory查询工具。

ADSIHunter.JPG

是的,这张图片经过了处理,域名不可见。当应用程序启动时,它将自动用所有已知(系统)的域名填充“域名”窗口。

引言

如果您在一个微软网络工作,那么您很可能正在使用 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 中,它将在“用户属性”列表中显示为“登录时间:> 单击查看 <”。用户自然需要单击列表中的项才能获得以下显示。

LoginHours.JPG

/// <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 的解码问题
© . All rights reserved.