1

好的,那么问题来了:

我使用 搜索 userparameters 属性ldap_search。我需要得到几个值,"CtxWFHomeDirDrive"being"CtxWFHomeDir""CtxWFProfilePath"

我得到的字符串完全是乱码,所以在尝试了我能找到的每一个字符编码转换一整天之后,这个成功了一半:

$pUserParams = iconv('UTF-8', 'UTF-32', $entry_value)

出于某种原因,我需要提取的 3 个值之后的单个十六进制数字被反转(所以5C,这是反斜杠,出来为C5。不要问我是怎么想出来的:-P)

知道了这一点,我可以转换十六进制值以显示实际的 citrix homedirdrive、profilepath 等。

但是,我仍然需要从最终结果字符串中过滤掉很多不需要的字符,但是由于某种原因,以下总是返回 false:

if (strpos($pUserParams,"CtxWFProfilePath") !== false) {}

当我回显$pUserParams变量时,它会显示包含上述三个 ctx 参数的整个字符串。

我怀疑它一定与结果字符串中的特殊字符有关,所以我尝试删除换行符、EOL's、反序列化字符串(在偏移量 0 处产生错误)等

似乎没有任何工作......有人有想法吗?

谢谢,文森特

原始字符串贯穿hex2bin

20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202050071a080143747843666750726573656e74e394b5e694b1e688b0e381a2180801437478436667466c61677331e380b0e381a6e380b2e380b9120801437478536861646f77e384b0e380b0e380b0e380b02a02014374784d696e456e6372797074696f6e4c6576656ce384b02206014374785746486f6d654469724472697665e3a0b4e684b3e380b018c282014374785746486f6d65446972e68cb5e68cb5e394b7e38cb7e39cb6e388b6e394b6e398b6e3a4b6e68cb6e394b6e380b3e380b3e384b3e694b2e394b7e38cb7e39cb6e380b7e394b6e698b6e380b7e68cb6e394b6e388b6e394b6e694b2e3a4b6e694b6e390b7e68cb5e394b7e38cb7e394b6e388b7e3a0b6e698b6e690b6e394b6e390b6e388b7e3a4b6e398b7e394b6e390b2e68cb5e38cb7e390b7e3a4b6e684b6e694b6e694b2e688b6e698b6e688b6e688b6e394b6e68cb6e394b6e694b6e388b6e394b6e388b7e39cb6e380b020c28001437478574650726f66696c6550617468e68cb5e68cb5e384b6e38cb7e380b7e694b6e394b6e390b7e384b6e380b7e380b7e380b3e380b3e384b3e694b2e384b6e38cb7e380b7e694b2e3a4b6e694b6e390b7e68cb5e380b7e388b7e698b6e398b6e3a4b6e68cb6e394b6e38cb7e698b5e388b6e394b6e68cb6e39cb6e3a4b6e394b7e690b6e390b2e68cb5e38cb5e38cb5e38cb4e68cb5e38cb7e390b7e3a4b6e684b6e694b6e694b2e688b6e698b6e688b6e688b6e394b6e68cb6e394b6e694b6e388b6e394b6e388b7e39cb6e380b0e380b0
�� 
4

3 回答 3

3

为了在 Tenzian 已经完成的工作的基础上再接再厉,我最终创建了一些独立的类,这些类可用于安全地解码/编码 userParameters blob 以提取和修改/查看/创建所有 TS 属性。这确实做了一些假设:

  • PHP 5.6+(随意编辑以满足您的需求/使用较低版本)
  • 假设所有类都定义在同一个命名空间中。
  • 对于多字节字符串支持,您已加载 mbstring 扩展。
  • 任何时间的 TS 属性都以分钟表示。

无论如何,这些是您需要的课程:

TSPropertyArray

/**
 * Represents TSPropertyArray data that contains individual TSProperty structures in a userParameters value.
 *
 * @see https://msdn.microsoft.com/en-us/library/ff635189.aspx
 * @author Chad Sikorra <Chad.Sikorra@gmail.com>
 */
class TSPropertyArray
{
    /**
     * Represents that the TSPropertyArray data is valid.
     */
    const VALID_SIGNATURE = 'P';

    /**
     * @var array The default values for the TSPropertyArray structure.
     */
    const DEFAULTS = [
        'CtxCfgPresent' => 2953518677,
        'CtxWFProfilePath' => '',
        'CtxWFProfilePathW' => '',
        'CtxWFHomeDir' => '',
        'CtxWFHomeDirW' => '',
        'CtxWFHomeDirDrive' => '',
        'CtxWFHomeDirDriveW' => '',
        'CtxShadow' => 1,
        'CtxMaxDisconnectionTime' => 0,
        'CtxMaxConnectionTime' => 0,
        'CtxMaxIdleTime' => 0,
        'CtxWorkDirectory' => '',
        'CtxWorkDirectoryW' => '',
        'CtxCfgFlags1' => 2418077696,
        'CtxInitialProgram' => '',
        'CtxInitialProgramW' => '',
    ];

    /**
     * @var string The default data that occurs before the TSPropertyArray (CtxCfgPresent with a bunch of spaces...?)
     */
    protected $defaultPreBinary = '43747843666750726573656e742020202020202020202020202020202020202020202020202020202020202020202020';

    /**
     * @var TSProperty[]
     */
    protected $tsProperty = [];

    /**
     * @var string
     */
    protected $signature = self::VALID_SIGNATURE;

    /**
     * @var string Binary data that occurs before the TSPropertyArray data in userParameters.
     */
    protected $preBinary = '';

    /**
     * @var string Binary data that occurs after the TSPropertyArray data in userParameters.
     */
    protected $postBinary = '';

    /**
     * Construct in one of the following ways:
     *
     *   - Pass an array of TSProperty key => value pairs (See DEFAULTS constant).
     *   - Pass the userParameters binary value. The object representation of that will be decoded and constructed.
     *   - Pass nothing and a default set of TSProperty key => value pairs will be used (See DEFAULTS constant).
     *
     * @param mixed $tsPropertyArray
     */
    public function __construct($tsPropertyArray = null)
    {
        $this->preBinary = hex2bin($this->defaultPreBinary);

        if (is_null($tsPropertyArray) || is_array($tsPropertyArray)) {
            $tsPropertyArray = $tsPropertyArray ?: self::DEFAULTS;
            foreach ($tsPropertyArray as $key => $value) {
                $tsProperty = new TSProperty();
                $tsProperty->setName($key);
                $tsProperty->setValue($value);
                $this->tsProperty[$key] = $tsProperty;
            }
        } else {
            $this->decodeUserParameters($tsPropertyArray);
        }
    }

    /**
     * Check if a specific TSProperty exists by its property name.
     * 
     * @param string $propName
     * @return bool
     */
    public function has($propName)
    {
        return array_key_exists(strtolower($propName), array_change_key_case($this->tsProperty));
    }

    /**
     * Get a TSProperty object by its property name (ie. CtxWFProfilePath).
     *
     * @param string $propName
     * @return TSProperty
     */
    public function get($propName)
    {
        $this->validateProp($propName);

        return $this->getTsPropObj($propName)->getValue();
    }

    /**
     * Add a TSProperty object. If it already exists, it will be overwritten.
     *
     * @param TSProperty $tsProperty
     * @return $this
     */
    public function add(TSProperty $tsProperty)
    {
        $this->tsProperty[$tsProperty->getName()] = $tsProperty;

        return $this;
    }

    /**
     * Remove a TSProperty by its property name (ie. CtxMinEncryptionLevel).
     * 
     * @param string $propName
     * @return $this
     */
    public function remove($propName)
    {
        foreach (array_keys($this->tsProperty) as $property) {
            if (strtolower($propName) == strtolower($property)) {
                unset($this->tsProperty[$property]);
            }
        }

        return $this;
    }

    /**
     * Set the value for a specific TSProperty by its name.
     * 
     * @param string $propName
     * @param mixed $propValue
     * @return $this
     */
    public function set($propName, $propValue)
    {
        $this->validateProp($propName);
        $this->getTsPropObj($propName)->setValue($propValue);

        return $this;
    }

    /**
     * Get the full binary representation of the userParameters containing the TSPropertyArray data.
     *
     * @return string
     */
    public function toBinary()
    {
        $binary = $this->preBinary;

        $binary .= hex2bin(str_pad(dechex(MBString::ord($this->signature)), 2, 0, STR_PAD_LEFT));
        $binary .= hex2bin(str_pad(dechex(count($this->tsProperty)), 2, 0, STR_PAD_LEFT));
        foreach ($this->tsProperty as $tsProperty) {
            $binary .= $tsProperty->toBinary();
        }

        return $binary.$this->postBinary;
    }

    /**
     * Get a simple associative array containing of all TSProperty names and values.
     * 
     * @return array
     */
    public function toArray()
    {
        $userParameters = [];

        foreach ($this->tsProperty as $property => $tsPropObj) {
            $userParameters[$property] = $tsPropObj->getValue();
        }

        return $userParameters;
    }

    /**
     * Get all TSProperty objects.
     * 
     * @return TSProperty[]
     */
    public function getTSProperties()
    {
        return $this->tsProperty;
    }

    /**
     * @param string $propName
     */
    protected function validateProp($propName)
    {
        if (!$this->has($propName)) {
            throw new \InvalidArgumentException(sprintf('TSProperty for "%s" does not exist.', $propName));
        }    
    }

    /**
     * @param string $propName
     * @return TSProperty
     */
    protected function getTsPropObj($propName)
    {
        return array_change_key_case($this->tsProperty)[strtolower($propName)];
    }

    /**
     * Get an associative array with all of the userParameters property names and values.
     *
     * @param string $userParameters
     * @return array
     */
    protected function decodeUserParameters($userParameters)
    {
        $userParameters = bin2hex($userParameters);

        // Save the 96-byte array of reserved data, so as to not ruin anything that may be stored there.
        $this->preBinary = hex2bin(substr($userParameters, 0, 96));
        // The signature is a 2-byte unicode character at the front
        $this->signature = MBString::chr(hexdec(substr($userParameters, 96, 2)));
        // This asserts the validity of the tsPropertyArray data. For some reason 'P' means valid...
        if ($this->signature != self::VALID_SIGNATURE) {
            throw new \InvalidArgumentException('Invalid TSPropertyArray data');
        }

        // The property count is a 2-byte unsigned integer indicating the number of elements for the tsPropertyArray
        // It starts at position 98. The actual variable data begins at position 100.
        $length = $this->addTSPropData(substr($userParameters, 100), hexdec(substr($userParameters, 98, 2)));

        // Reserved data length + (count and sig length == 4) + the added lengths of the TSPropertyArray
        // This saves anything after that variable TSPropertyArray data, so as to not squash anything stored there
        if (strlen($userParameters) > (96 + 4 + $length)) {
            $this->postBinary = hex2bin(substr($userParameters, (96 + 4 + $length)));
        }
    }

    /**
     * Given the start of TSPropertyArray hex data, and the count for the number of TSProperty structures in contains,
     * parse and split out the individual TSProperty structures. Return the full length of the TSPropertyArray data.
     *
     * @param string $tsPropertyArray
     * @param int $tsPropCount
     * @return int The length of the data in the TSPropertyArray
     */
    protected function addTSPropData($tsPropertyArray, $tsPropCount)
    {
        $length = 0;

        for ($i = 0; $i < $tsPropCount; $i++) {
            // Prop length = name length + value length + type length + the space for the length data.
            $propLength = hexdec(substr($tsPropertyArray, $length, 2)) + (hexdec(substr($tsPropertyArray, $length + 2, 2)) * 3) + 6;
            $tsProperty = new TSProperty(hex2bin(substr($tsPropertyArray, $length, $propLength)));
            $this->tsProperty[$tsProperty->getName()] = $tsProperty;
            $length += $propLength;
        }

        return $length;
    }
}

TSProperty

/**
 * Represents a TSProperty structure in a TSPropertyArray of a userParameters binary value.
 *
 * @see https://msdn.microsoft.com/en-us/library/ff635169.aspx
 * @see http://daduke.org/linux/userparameters.html
 * @author Chad Sikorra <Chad.Sikorra@gmail.com>
 */
class TSProperty
{
    /**
     * Nibble control values. The first value for each is if the nibble is <= 9, otherwise the second value is used.
     */
    const NIBBLE_CONTROL = [
        'X' => ['001011', '011010'],
        'Y' => ['001110', '011010'],
    ];

    /**
     * The nibble header.
     */
    const NIBBLE_HEADER = '1110';

    /**
     * Conversion factor needed for time values in the TSPropertyArray (stored in microseconds).
     */
    const TIME_CONVERSION = 60 * 1000;

    /**
     * A simple map to help determine how the property needs to be decoded/encoded from/to its binary value.
     *
     * There are some names that are simple repeats but have 'W' at the end. Not sure as to what that signifies. I
     * cannot find any information on them in Microsoft documentation. However, their values appear to stay in sync with
     * their non 'W' counterparts. But not doing so when manipulating the data manually does not seem to affect anything.
     * This probably needs more investigation.
     * 
     * @var array
     */
    protected $propTypes = [
        'string' => [
            'CtxWFHomeDir',
            'CtxWFHomeDirW',
            'CtxWFHomeDirDrive',
            'CtxWFHomeDirDriveW',
            'CtxInitialProgram',
            'CtxInitialProgramW',
            'CtxWFProfilePath',
            'CtxWFProfilePathW',
            'CtxWorkDirectory',
            'CtxWorkDirectoryW',
            'CtxCallbackNumber',
        ],
        'time' => [
            'CtxMaxDisconnectionTime',
            'CtxMaxConnectionTime',
            'CtxMaxIdleTime',
        ],
        'int' => [
            'CtxCfgFlags1',
            'CtxCfgPresent',
            'CtxKeyboardLayout',
            'CtxMinEncryptionLevel',
            'CtxNWLogonServer',
            'CtxShadow',
        ],
    ];

    /**
     * @var string The property name.
     */
    protected $name;

    /**
     * @var string|int The property value.
     */
    protected $value;

    /**
     * @var int The property value type.
     */
    protected $valueType = 1;

    /**
     * @param string|null $value Pass binary TSProperty data to construct its object representation.
     */
    public function __construct($value = null)
    {
        if ($value) {
            $this->decode(bin2hex($value));
        }
    }

    /**
     * Set the name for the TSProperty.
     *
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * Get the name for the TSProperty.
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set the value for the TSProperty.
     *
     * @param string|int $value
     */
    public function setValue($value)
    {
        $this->value = $value;
    }

    /**
     * Get the value for the TSProperty.
     *
     * @return string|int
     */
    public function getValue()
    {
        return $this->value;
    }

    /**
     * Convert the TSProperty name/value back to its binary representation for the userParameters blob.
     * 
     * @return string
     */
    public function toBinary()
    {
        $name = bin2hex($this->name);
        $binValue = $this->getEncodedValueForProp($this->name, $this->value);
        $valueLen = strlen(bin2hex($binValue)) / 3;

        $binary = hex2bin(
            $this->dec2hex(strlen($name))
            .$this->dec2hex($valueLen)
            .$this->dec2hex($this->valueType)
            .$name
        );

        return $binary.$binValue;
    }

    /**
     * Given a TSProperty blob, decode the name/value/type/etc.
     *
     * @param string $tsProperty
     */
    protected function decode($tsProperty)
    {
        $nameLength = hexdec(substr($tsProperty, 0, 2));
        # 1 data byte is 3 encoded bytes
        $valueLength = hexdec(substr($tsProperty, 2, 2)) * 3;

        $this->valueType = hexdec(substr($tsProperty, 4, 2));
        $this->name = pack('H*', substr($tsProperty, 6, $nameLength));
        $this->value = $this->getDecodedValueForProp($this->name, substr($tsProperty, 6 + $nameLength, $valueLength));
    }

    /**
     * Based on the property name/value in question, get its encoded form.
     *
     * @param string $propName
     * @param string|int $propValue
     * @return string
     */
    protected function getEncodedValueForProp($propName, $propValue)
    {
        if (in_array($propName, $this->propTypes['string'])) {
            # Simple strings are null terminated. Unsure if this is needed or simply a product of how ADUC does stuff?
            $value = $this->encodePropValue($propValue."\0", true);
        } elseif (in_array($propName, $this->propTypes['time'])) {
            # Needs to be in microseconds (assuming it is in minute format)...
            $value = $this->encodePropValue($propValue * self::TIME_CONVERSION);
        } else {
            $value = $this->encodePropValue($propValue);
        }

        return $value;
    }

    /**
     * Based on the property name in question, get its actual value from the binary blob value.
     *
     * @param string $propName
     * @param string $propValue
     * @return string|int
     */
    protected function getDecodedValueForProp($propName, $propValue)
    {
        if (in_array($propName, $this->propTypes['string'])) {
            // Strip away null terminators. I think this should be desired, otherwise it just ends in confusion.
            $value = str_replace("\0", '', $this->decodePropValue($propValue, true));
        } elseif (in_array($propName, $this->propTypes['time'])) {
            // Convert from microseconds to minutes (how ADUC displays it anyway, and seems the most practical).
            $value = hexdec($this->decodePropValue($propValue)) / self::TIME_CONVERSION;
        } elseif (in_array($propName, $this->propTypes['int'])) {
            $value = hexdec($this->decodePropValue($propValue));
        } else {
            $value = $this->decodePropValue($propValue);
        }

        return $value;
    }

    /**
     * Decode the property by inspecting the nibbles of each blob, checking the control, and adding up the results into
     * a final value.
     *
     * @param string $hex
     * @param bool $string Whether or not this is simple string data.
     * @return string
     */
    protected function decodePropValue($hex, $string = false)
    {
        $decodePropValue = '';
        $blobs = str_split($hex, 6);

        foreach ($blobs as $blob) {
            $bin = decbin(hexdec($blob));
            $controlY = substr($bin, 4, 6);
            $nibbleY = substr($bin, 10, 4);
            $controlX = substr($bin, 14, 6);
            $nibbleX = substr($bin, 20, 4);
            $byte = $this->nibbleControl($nibbleX, $controlX).$this->nibbleControl($nibbleY, $controlY);
            if ($string) {
                $decodePropValue .= MBString::chr(bindec($byte));
            } else {
                $decodePropValue = $this->dec2hex(bindec($byte)).$decodePropValue;
            }
        }

        return $decodePropValue;
    }

    /**
     * Get the encoded property value as a binary blob.
     *
     * @param string $value
     * @param bool $string
     * @return string
     */
    protected function encodePropValue($value, $string = false)
    {
        // An int must be properly padded. (then split and reversed). For a string, we just split the chars. This seems
        // to be the easiest way to handle UTF-8 characters instead of trying to work with their hex values.
        $chars = $string ? MBString::str_split($value) : array_reverse(str_split($this->dec2hex($value, 8), 2));

        $encoded = '';
        foreach ($chars as $char) {
            // Get the bits for the char. Using this method to ensure it is fully padded.
            $bits = sprintf('%08b', $string ? MBString::ord($char) : hexdec($char));
            $nibbleX = substr($bits, 0, 4);
            $nibbleY = substr($bits, 4, 4);

            // Construct the value with the header, high nibble, then low nibble.
            $value = self::NIBBLE_HEADER;
            foreach (['Y' => $nibbleY, 'X' => $nibbleX] as $nibbleType => $nibble) {
                $value .= $this->getNibbleWithControl($nibbleType, $nibble);
            }

            // Convert it back to a binary bit stream
            foreach ([0, 8, 16] as $start) {
                $encoded .= $this->packBitString(substr($value, $start, 8), 8);
            }
        }

        return $encoded;
    }

    /**
     * PHP's pack() function has no 'b' or 'B' template. This is a workaround that turns a literal bit-string into a
     * packed byte-string with 8 bits per byte.
     *
     * @param string $bits
     * @param bool $len
     * @return string
     */
    protected function packBitString($bits, $len)
    {
        $bits = substr($bits, 0, $len);
        // Pad input with zeros to next multiple of 4 above $len
        $bits = str_pad($bits, 4 * (int) (($len + 3) / 4), '0');

        // Split input into chunks of 4 bits, convert each to hex and pack them
        $nibbles = str_split($bits, 4);
        foreach ($nibbles as $i => $nibble) {
            $nibbles[$i] = base_convert($nibble, 2, 16);
        }

        return pack('H*', implode('', $nibbles));
    }

    /**
     * Based on the control, adjust the nibble accordingly.
     *
     * @param string $nibble
     * @param string $control
     * @return string
     */
    protected function nibbleControl($nibble, $control)
    {
        // This control stays constant for the low/high nibbles, so it doesn't matter which we compare to
        if ($control == self::NIBBLE_CONTROL['X'][1]) {
            $dec = bindec($nibble);
            $dec += 9;
            $nibble = str_pad(decbin($dec), 4, '0', STR_PAD_LEFT);
        }

        return $nibble;
    }

    /**
     * Get the nibble value with the control prefixed.
     *
     * If the nibble dec is <= 9, the control X equals 001011 and Y equals 001110, otherwise if the nibble dec is > 9
     * the control for X or Y equals 011010. Additionally, if the dec value of the nibble is > 9, then the nibble value
     * must be subtracted by 9 before the final value is constructed.
     *
     * @param string $nibbleType Either X or Y
     * @param $nibble
     * @return string
     */
    protected function getNibbleWithControl($nibbleType, $nibble)
    {
        $dec = bindec($nibble);

        if ($dec > 9) {
            $dec -= 9;
            $control = self::NIBBLE_CONTROL[$nibbleType][1];
        } else {
            $control = self::NIBBLE_CONTROL[$nibbleType][0];
        }

        return $control.sprintf('%04d', decbin($dec));
    }

    /**
     * Need to make sure hex values are always an even length, so pad as needed.
     *
     * @param int $int
     * @param int $padLength The hex string must be padded to this length (with zeros).
     * @return string
     */
    protected function dec2hex($int, $padLength = 2)
    {
        return str_pad(dechex($int), $padLength, 0, STR_PAD_LEFT);
    }
}

MBString

/**
 * Some utility functions to handle multi-byte strings properly, as support is lacking/inconsistent for most PHP string
 * functions. This provides a wrapper for various workarounds and falls back to normal functions if needed.
 *
 * @author Chad Sikorra <Chad.Sikorra@gmail.com>
 */
class MBString
{
    /**
     * Get the integer value of a specific character.
     *
     * @param $string
     * @return int
     */
    public static function ord($string)
    {
        if (self::isMbstringLoaded()) {
            $result = unpack('N', mb_convert_encoding($string, 'UCS-4BE', 'UTF-8'));
            if (is_array($result) === true) {
                return $result[1];
            }
        }

        return ord($string);
    }

    /**
     * Get the character for a specific integer value.
     *  
     * @param $int
     * @return string
     */
    public static function chr($int)
    {
        if (self::isMbstringLoaded()) {
            return mb_convert_encoding(pack('n', $int), 'UTF-8', 'UTF-16BE');
        }

        return chr($int);
    }

    /**
     * Split a string into its individual characters and return it as an array.
     * 
     * @param string $value
     * @return string[]
     */
    public static function str_split($value)
    {
        return preg_split('/(?<!^)(?!$)/u', $value);
    }

    /**
     * Simple check for the mbstring extension.
     * 
     * @return bool
     */
    protected static function isMbstringLoaded()
    {
        return extension_loaded('mbstring');
    }
}

显示 userParameters 值

// Assuming $value is the binary value from userParameters.
$tsPropArray = new TSPropertyArray($value);

// Prints out each TS property name and value for the user.
foreach($tsPropArray->toArray() as $prop => $value) {
    echo "$prop => $value".PHP_EOL;
}

// Print a single value
echo $tsPropArray->get('CtxWFProfilePath');

修改/创建 userParameters 值

// Creates a new set of values for userParameters (default values).
$tsPropArray = new TSPropertyArray();
// Set a max session idle time of 30 minutes.
$tsPropArray->set('CtxMaxIdleTime', 30);
// Get the binary value to save to LDAP
$binary = $tsPropArray->toBinary();

// Load binary userParameters data from a user
$tsPropArray = new TSPropertyArray($binary);
// Replace their user profile location...
$tsPropArray->set('CtxWFProfilePath', '\\some\path');
// Get the new binary value, then save it as needed back to LDAP...
$binary = $tsPropArray->toBinary();

一些附加说明

上面的代码将处理多字节字符,因此如果值中有 UTF8 字符应该没问题。它还尊重 userParameters 二进制 blob 中的其他二进制数据。所以它不是破坏性的,当您修改现有值时,数据将被保留。

我还注意到 userParameters 中有一些以“W”结尾的 TS 属性,并且是其他属性的重复项(甚至它们的值也是重复的)。我在 MSDN 或其他地方找不到任何关于此的信息,所以我不确定它们的意义是什么。

于 2016-05-24T03:05:30.900 回答
2

我知道自从提出原始问题以来已经有一段时间了,但这是在搜索“CtxWFProfilePath”和 PHP 时出现的唯一页面,当我试图弄清楚如何获取值时,这是我开始的地方超出用户参数。

事实证明,userParameters blob 可能是有史以来最神秘和最不必要的编码。我不知道微软在做这件事时在想什么,但他们认为 CtxCfgPresent 值为 0xB00B1E55 表明有效数据可能会在某种程度上解释它......

(非常感谢http://daduke.org/linux/userparameters.html找出了 TSProperty 结构的编码。)

这是我的解决方案:

<?php
function userParameters($userParameters){
  /*
    userParameters data structure described at: http://msdn.microsoft.com/en-us/library/ff635189.aspx
    TSProperty data structure described at: http://msdn.microsoft.com/en-us/library/ff635169.aspx

    Input:  userParameters blob returned from ldap_search
    Output: associative array of user parameters
  */
  $parameters = array();
  $userParameters = bin2hex($userParameters);
  $userParameters = substr($userParameters,96);
  $Signature = chr(hexdec(substr($userParameters,0,2)));
  $userParameters = substr($userParameters,2);
  if ($Signature != 'P'){
    return false;
  }
  $TSPropertyCount = hexdec(substr($userParameters,0,2));
  $userParameters = substr($userParameters,2);
  for ($i = 0; $i < $TSPropertyCount; $i++){
    $NameLength = hexdec(substr($userParameters,0,2));
    $userParameters = substr($userParameters,2);
    $ValueLength = hexdec(substr($userParameters,0,2)) * 3; // 1 data byte = 3 encoded bytes
    $userParameters = substr($userParameters,2);
    $Type = substr($userParameters,0,2);
    $userParameters = substr($userParameters,2);
    $PropName = substr($userParameters,0,$NameLength);
    $PropName = hex2str($PropName);
    $userParameters = substr($userParameters,$NameLength);
    $PropValue = substr($userParameters,0,$ValueLength);
    $userParameters = substr($userParameters,$ValueLength);
    switch ($PropName) {
      case 'CtxWFHomeDir':
      case 'CtxWFHomeDirDrive':
      case 'CtxInitialProgram':
      case 'CtxWFProfilePath':
      case 'CtxWorkDirectory':
      case 'CtxCallbackNumber':
        $parameters[$PropName] = decode_PropValue($PropValue,true);
        break;
      case 'CtxCfgFlags1':
        $parameters[$PropName] = parse_CtxCfgFlags1(decode_PropValue($PropValue));
        break;
      case 'CtxShadow':
        $parameters[$PropName] = parse_CtxShadow(decode_PropValue($PropValue));
        break;
      default:
        $parameters[$PropName] = decode_PropValue($PropValue);
    }
  }
  return $parameters;
}

function hex2str($hex) {
  $str = '';
  for($i = 0; $i < strlen($hex); $i += 2){
    $str .= chr(hexdec(substr($hex,$i,2)));
  }
  return $str;
}

function decode_PropValue($hex,$ascii=false){
  /*
    Encoding described at: http://daduke.org/linux/userparameters.html
    for each character you want to encode, do:
      - split the character's byte into nibbles xxxx and yyyy
      - have a look at xxxx. If it's <= 9, control x (XXXXXX) equals 001011, otherwise it's 011010
      - have a look at yyyy. Here the bit patterns for control y (YYYYYY) are 001110 (yyyy <= 9), 011010 otherwise
      - if xxxx > 9: xxxx -= 9
      - if yyyy > 9: yyyy -= 9
      - take the prefix (1110), control y, yyyy, control x and xxxx and glue them all together to yield a 24 bit string
      - convert this bit stream to three bytes: 1110 YYYY YYyy yyXX XXXX xxxx
  */
  $decode_PropValue = '';
  $blobs = str_split($hex,6);
  foreach ($blobs as $blob){
    $bin = decbin(hexdec($blob));
    $control_y = substr($bin,4,6);
    $nibble_y = substr($bin,10,4);
    $control_x = substr($bin,14,6);
    $nibble_x = substr($bin,20,4);
    $byte = nibble_control($nibble_x,$control_x).nibble_control($nibble_y,$control_y);
    if ($ascii){
      $decode_PropValue .= chr(bindec($byte));
    }
    else {
      $decode_PropValue = str_pad(dechex(bindec($byte)),2,'0',STR_PAD_LEFT).$decode_PropValue;
    }
  }
  return $decode_PropValue;
}

function nibble_control($nibble,$control){
  if ($control == '011010'){
    $dec = bindec($nibble);
    $dec += 9;
    return str_pad(decbin($dec),4,'0',STR_PAD_LEFT);
  }
  return $nibble;
}

function parse_CtxCfgFlags1($CtxCfgFlags1) {
  /* Flag bit mask values from: http://msdn.microsoft.com/en-us/library/ff635169.aspx */
  $parse_CtxCfgFlags1 = array();
  $CtxCfgFlags1 = hexdec($CtxCfgFlags1);
  $flags = array(
    'F1MSK_INHERITINITIALPROGRAM'       => 268435456,
    'F1MSK_INHERITCALLBACK'             => 134217728,
    'F1MSK_INHERITCALLBACKNUMBER'       =>  67108864,
    'F1MSK_INHERITSHADOW'               =>  33554432,
    'F1MSK_INHERITMAXSESSIONTIME'       =>  16777216,
    'F1MSK_INHERITMAXDISCONNECTIONTIME' =>   8388608,
    'F1MSK_INHERITMAXIDLETIME'          =>   4194304,
    'F1MSK_INHERITAUTOCLIENT'           =>   2097152,
    'F1MSK_INHERITSECURITY'             =>   1048576,
    'F1MSK_PROMPTFORPASSWORD'           =>    524288,
    'F1MSK_RESETBROKEN'                 =>    262144,
    'F1MSK_RECONNECTSAME'               =>    131072,
    'F1MSK_LOGONDISABLED'               =>     65536,
    'F1MSK_AUTOCLIENTDRIVES'            =>     32768,
    'F1MSK_AUTOCLIENTLPTS'              =>     16384,
    'F1MSK_FORCECLIENTLPTDEF'           =>      8192,
    'F1MSK_DISABLEENCRYPTION'           =>      4096,
    'F1MSK_HOMEDIRECTORYMAPROOT'        =>      2048,
    'F1MSK_USEDEFAULTGINA'              =>      1024,
    'F1MSK_DISABLECPM'                  =>       512,
    'F1MSK_DISABLECDM'                  =>       256,
    'F1MSK_DISABLECCM'                  =>       128,
    'F1MSK_DISABLELPT'                  =>        64,
    'F1MSK_DISABLECLIP'                 =>        32,
    'F1MSK_DISABLEEXE'                  =>        16,
    'F1MSK_WALLPAPERDISABLED'           =>         8,
    'F1MSK_DISABLECAM'                  =>         4
  );
  foreach($flags as $flag => $bit) {
    if ($CtxCfgFlags1 & $bit) {
      $parse_CtxCfgFlags1[] = $flag;
    }
  }
  return($parse_CtxCfgFlags1);
}

function parse_CtxShadow($CtxShadow) {
  /* Flag values from: http://msdn.microsoft.com/en-us/library/ff635169.aspx */
  $CtxShadow = hexdec($CtxShadow);
  $flags = array('Disable','EnableInputNotify','EnableInputNoNotify','EnableNoInputNotify','EnableNoInputNoNotify');
  if ($CtxShadow < 0 || $CtxShadow > 4) {
    return false;
  }
  return $flags[$CtxShadow];
}
?>
于 2014-08-08T12:35:41.423 回答
0

删除所有特殊字符

preg_replace('/[^a-zA-Z0-9_ %[].()%&-]/s', '', $piConverted);

解决了:-)

现在,如果我只能找到一个更优雅的转换,那么我就不必“手动”反转十六进制代码

于 2013-05-06T14:44:34.887 回答