sergey vasin

The IT blog

ConvertTo-sthSID — функция для конвертации SID в виде массива байтов в строчный формат

leave a comment »

Работать с Active Directory из PowerShell гораздо удобнее с использованием командлетов из одноименного модуля. Однако, если на каком-либо компьютере этот модуль не установлен и PowerShell Remoting нам в данный момент по каким-то причинам недоступен, мы можем использовать ADSI — Active Directory Service Interfaces.

Когда мы обращаемся к Active Directory при помощи ADSI, например, так

[adsi]"LDAP://CN=UserName,CN=Users,DC=domain,DC=com" | Format-List -Property *

большинство атрибутов выглядят так же, как если бы мы использовали командлет Get-ADUser из модуля ActiveDirectory. Но есть и несколько исключений. Одним из них является атрибут ObjectSID.

Если мы введем следующий код, то увидим, что представлен он будет в виде массива байтов.

$user = [adsi]"LDAP://CN=UserName,CN=Users,DC=domain,DC=com"
$user.ObjectSID

Стоит сказать, что это тот формат, в котором он хранится в Active Directory. Однако нам будет гораздо удобнее, если он будет представлен в виде привычной нам строки SID.

Давайте напишем функцию для его конвертации из массива байтов в формат строки. Назовем ее ConvertTo-sthSID.

function ConvertTo-sthSID
{

}

В качестве параметра укажем $ByteArray. Сделаем его обязательным, так как в случае его отсутствия функции просто нечего будет делать. Кроме того, предоставим возможность передавать его значение функции по конвейеру.

function ConvertTo-sthSID
{
    Param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        $ByteArray
    )
}

Так как мы собираемся реализовывать конвейерную обработку, нам пригодится использование блоков begin, process и end.

Начнем с begin. Внутри этого блока мы инициализируем переменную $Stream, в которой будет находиться весь массив байтов, вне зависимости от того, как именно он был передан функции — с использованием параметра -ByteArray или по конвейеру.

function ConvertTo-sthSID
{
    Param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        $ByteArray
    )

    begin
    {
        $Stream = @()
    }
}

Отличие тут состоит в следующем. При передаче массива посредством параметра, переменная $ByteArray будет содержать весь массив целиком. То есть, в этом случае он будет готов к дальнейшей обработке без каких-либо дополнительных действий.

С другой стороны, если для передачи массива мы использовали конвейер, то в данном случае каждый элемент массива будет передаваться по очереди. Таким образом, перед тем как приступить к его обработке, нам потребуется получить все его элементы и собрать их вместе в одной переменной.

Для этого нам понадобится блок process.

function ConvertTo-sthSID
{
    Param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        $ByteArray
    )

    begin
    {
        $Stream = @()
    }

    process
    {

    }
}

Так как блок process выполняется для каждого поступившего по конвейеру элемента, сборку отдельных байтов в один массив мы будем производить в нем.

Однако строку, отвечающую за сборку массива мы поместим в выражение foreach. Сделаем мы это для того, чтобы когда мы используем не конвейер, а параметр -ByteArray, наш входящий массив байтов так же копировался бы в переменную $Stream.

function ConvertTo-sthSID
{
    Param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        $ByteArray
    )

    begin
    {
        $Stream = @()
    }

    process
    {
        foreach ($Byte in $ByteArray)
        {
            $Stream += $Byte
        }
    }
}

Основная обработка у нас будет происходить в блоке end.

function ConvertTo-sthSID
{
    Param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        $ByteArray
    )

    begin
    {
        $Stream = @()
    }

    process
    {
        foreach ($Byte in $ByteArray)
        {
            $Stream += $Byte
        }
    }
    
    end
    {

    }
}

Но перед этим нам бы пригодилось описание формата SID. Найти мы его можем на сайте MSDN — https://msdn.microsoft.com/en-us/library/cc230371.aspx.

Мы знаем, что строка начинается с символов S-. Далее расположено значение Revision, что представлено первым байтом и, в соответствии с документацией, должно равняться 0x01.

Значение второго байта не отображается в строке напрямую и обозначает количество четырехбайтовых блоков, представляющих из себя секцию SubAuthority.

Байты с третьего по восьмой обозначают IdentifierAuthority, хотя обычно первые пять из них представлены нулями. Таким образом и мы будем использовать только последний.

function ConvertTo-sthSID
{
    Param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        $ByteArray
    )

    begin
    {
        $Stream = @()
    }

    process
    {
        foreach ($Byte in $ByteArray)
        {
            $Stream += $Byte
        }
    }
    
    end
    {
        # Revision and IdentifierAuthority
        $Result = "S-{0}-{1}" -f $Stream[0], $Stream[7]
    }
}

Далее расположены четырехбайтовые блоки SubAuthority в количестве, указанном во втором байте массива. В идентификаторах безопасности пользователей и компьютеров их пять. В так называемых well-known идентификаторах возможны варианты.

Младшим является первый байт в блоке, старшим, соответственно, последний.

Для того, чтобы обработать все имеющиеся в массиве блоки воспользуемся конструкцией for.

function ConvertTo-sthSID
{
    Param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        $ByteArray
    )

    begin
    {
        $Stream = @()
    }

    process
    {
        foreach ($Byte in $ByteArray)
        {
            $Stream += $Byte
        }
    }
    
    end
    {
        # Revision and IdentifierAuthority
        $Result = "S-{0}-{1}" -f $Stream[0], $Stream[7]

        # SubAuthority
        for ($i = 0; $i -lt $Stream[1]; $i++)
        {

        }
    }
}

Внутри мы определим переменную $off, указывающую смещение для каждого обрабатываемого блока.

Во второй строке мы вычисляем значение, представленное четыремя байтами и добавляем его к создаваемой строке. Причем второй байт мы сдвигаем влево на 8 бит, третий — на 16, а четвертый — на 24, получая таким образом 32-битовое число, которое затем представляем в десятичном виде. Для того, чтобы байты при конвертации не превращались в отрицательные числа, в качестве типа данных мы указываем [uint32].

function ConvertTo-sthSID
{
    Param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        $ByteArray
    )

    begin
    {
        $Stream = @()
    }

    process
    {
        foreach ($Byte in $ByteArray)
        {
            $Stream += $Byte
        }
    }
    
    end
    {
        # Revision and IdentifierAuthority
        $Result = "S-{0}-{1}" -f $Stream[0], $Stream[7]

        # SubAuthority
        for ($i = 0; $i -lt $Stream[1]; $i++)
        {
            $off = $i * 4
            $Result = "$Result-{0}" -f $([uint32]$Stream[8 + $off] -bor ([uint32]$Stream[9 + $off] -shl 8) -bor ([uint32]$Stream[10 + $off] -shl 16) -bor ([uint32]$Stream[11 + $off] -shl 24))
        }
        return $Result
    }
}

Результат преобразования, расположенный в переменной в $Result, мы возвращаем при помощи ключевого слова return.

Полный текст функции выглядит следующим образом.

function ConvertTo-sthSID
{
    Param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        $ByteArray
    )

    begin
    {
        $Stream = @()
    }

    process
    {
        foreach ($Byte in $ByteArray)
        {
            $Stream += $Byte
        }
    }
    
    end
    {
        # Revision and IdentifierAuthority
        $Result = "S-{0}-{1}" -f $Stream[0], $Stream[7]

        # SubAuthority
        for ($i = 0; $i -lt $Stream[1]; $i++)
        {
            $off = $i * 4
            $Result = "$Result-{0}" -f $([uint32]$Stream[8 + $off] -bor ([uint32]$Stream[9 + $off] -shl 8) -bor ([uint32]$Stream[10 + $off] -shl 16) -bor ([uint32]$Stream[11 + $off] -shl 24))
        }
        return $Result
    }
}

Теперь мы можем ее использовать следующим образом:

$user = [adsi]"LDAP://CN=UserName,CN=Users,DC=domain,DC=com"
ConvertTo-sthSID -ByteArray $user.ObjectSID

или же так:

$user = [adsi]"LDAP://CN=UserName,CN=Users,DC=domain,DC=com"
$user.ObjectSID | ConvertTo-sthSID

Эта функция является частью модуля sthLDAPTools, который вы можете установить из PowerShell Gallery при помощи следующей команды:

Install-Module -Name sthLDAPTools

Все модули доступны по следующей ссылке:
https://sergeyvasin.net/modules/


Страницы в социальных сетях:

Twitter: https://twitter.com/vsseth
Facebook: https://fb.com/inpowershell
VKontakte: https://vk.com/inpowershell


Реклама

Written by Сергей Васин

Сентябрь 5, 2017 в 13:41

Опубликовано в Active Directory, PowerShell

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s