1. 記事一覧 >
  2. ブログ記事
category logo

AD(Active Directory)のuserParameters属性をphpで解読に成功

(更新) (公開)

はじめに

先日の記事「AD(Active Directory)の中身を覗いてみた - ldapsearch,php,PowerShell」で、userParameters 属性を php で解析すると、文字化けが発生することを書きました。その原因が分かり、正常に表示することができました。ついでに、だいぶ理解が進みましたので、どうやって解析しているかのロジック面も書きたいと思います。
他、PowerShell に一部誤りが有ったことにも気づきましたので、そちらも紹介していきます。


【検証環境】

Windows Server 2019 Datacenter Desktop

 ドメインコントローラーに昇格したActive Directory

FreeBSD13(本ブログ別記事「FreeBSD-13.0-RELEASEにapache2,php,postgresql,openldapをインストール」まで実施したサーバーです。)

 OpenLDAP: ldapsearch 2.4.59

 PHP 7.4.23


現象

AD(Active Directory)の中身を表示する php プログラムを作成しました。userParametersという属性があるのですが、バイナリ値が入っていて、うまく表示できませんでした。
こちら(stackoverflow)class TSPropertyfunction userParameters($userParameters){で抽出してみましたが、どちらも同じ結果で、一部読めるところがありますが、文字化けしました。


userParametersをphpで解析してprint_rした結果:

userParametersをprint_rした結果


修正前後の diff

AD(Active Directory)の中身を表示 php プログラム全体は、https://gist.github.com/itc-lab/030844f88869eb3fbd3712363e766035に置きました。
こちら(stackoverflow)function userParameters($userParameters){を以下のように修正しました。


diff:

@@ -20,6 +20,10 @@ function userParameters($userParameters)
20
20
  $NameLength = hexdec(substr($userParameters, 0, 2));
21
21
  $userParameters = substr($userParameters, 2);
22
22
  $ValueLength = hexdec(substr($userParameters, 0, 2)) * 3;
23
+ if ($ValueLength == 0xc2 * 3) {
24
+ $userParameters = substr($userParameters, 2);
25
+ $ValueLength = hexdec(substr($userParameters, 0, 2)) * 3;
26
+ }
23
27
  $userParameters = substr($userParameters, 2);
24
28
  $Type = substr($userParameters, 0, 2);
25
29
  $userParameters = substr($userParameters, 2);
@@ -30,10 +34,14 @@ function userParameters($userParameters)
30
34
  $userParameters = substr($userParameters, $ValueLength);
31
35
  switch ($PropName) {
32
36
  case 'CtxWFHomeDir':
37
+ case 'CtxWFHomeDirW':
33
38
  case 'CtxWFHomeDirDrive':
34
39
  case 'CtxInitialProgram':
40
+ case 'CtxInitialProgramW':
35
41
  case 'CtxWFProfilePath':
42
+ case 'CtxWFProfilePathW':
36
43
  case 'CtxWorkDirectory':
44
+ case 'CtxWorkDirectoryW':
37
45
  case 'CtxCallbackNumber':
38
46
  $parameters[$PropName] = decode_PropValue($PropValue, true);
39
47
  break;

原因とロジック詳細

16進数表記の場合、0x を先頭に付けて表記します。

ある一定の長さの値があると、途中で0xc2が挿入されていて、これを無視する必要がありました。※valueLength が 128 (0x80) 以上で 0xc2 が挿入される気がします。


解析対象のバイナリデータ(userParameters 属性値)を bin2hex(バイナリデータ → 16進数文字列表記に変換)

0x1a080143747843666750726573656e74e394b5e・・・

先頭から1バイト(2文字)取り出して、属性名の長さを得る。(取り出した分は除去。以降同様。)

先頭から1バイト(2文字)取り出して、属性値の長さを得る。

次の先頭から1バイト(2文字)取り出して、Type を得る。

属性名の長さ分取り出して、文字列にして、属性名を得る。

属性値の長さ分取り出して、文字列にして、属性値を得る。

先頭から1バイト(2文字)取り出して、属性名の長さを得る。

・・・と繰り返していると、
「先頭から1バイト(2文字)取り出して、属性値の長さを得る。」のところで、0xc2が現れます。これは、10進数にすると、194 なのですが、本来の属性値の長さではなく、0xc2の次の値が本来の属性値の長さになります。
例えば、0xc2a0となっている場合、0xa0が属性値の長さです。0xa0は、160 のため、全然違います。
結果、194 が値の長さ(実際には、3倍しています。)だと思って、属性値を取り出そうとするため、文字列化できません。
属性値の長さを得るところで、0xc2が現れたときに、無視して、次の値が正しい属性値の長さとみなすと、うまくいきました。


該当箇所ソースコード(コメント付き):

$parameters = array();
$userParameters = bin2hex($userParameters);//バイナリ→16進数表記の文字列に変換
$userParameters = substr($userParameters, 96);//16進表記の文字列から96文字以降を取り出す
$Signature = chr(hexdec(substr($userParameters, 0, 2)));//先頭2文字取り出す→10進数に変換して、対応するアスキーコードの文字にする。
$userParameters = substr($userParameters, 2);//$Signatureを得るために使った先頭2文字は捨てる。
if ($Signature != 'P') {//シグニチャがPではない場合、エラー(false)
	return false;
}
$TSPropertyCount = hexdec(substr($userParameters, 0, 2));//先頭2文字を10進数にして$TSPropertyCountに入れる。
$userParameters = substr($userParameters, 2);//$TSPropertyCountを得るために使った先頭2文字は捨てる。
for ($i = 0; $i < $TSPropertyCount; $i++) {//$TSPropertyCount=12なら、12回実行。$TSPropertyCountは、結果の連想配列のキー($PropName)の数になる。
	$NameLength = hexdec(substr($userParameters, 0, 2));//先頭2文字を10進数にして$NameLengthに入れる。
	$userParameters = substr($userParameters, 2);//$NameLengthを得るために使った先頭2文字は捨てる。
	$ValueLength = hexdec(substr($userParameters, 0, 2)) * 3;//先頭2文字を10進数にして3倍にして$ValueLengthに入れる。
	if ( $ValueLength == 0xc2 * 3 ) {//$ValueLengthが 0xc2×3(194×3)、つまり、この時点の$userParametersの先頭2文字がc2だったとき
		$userParameters = substr($userParameters, 2);//先頭2文字は捨てる。
		$ValueLength = hexdec(substr($userParameters, 0, 2)) * 3;//$ValueLengthを取り直す。先頭2文字を10進数にして3倍にして$ValueLengthに入れる。
	}
	$userParameters = substr($userParameters, 2);//$ValueLengthを得るために使った先頭2文字は捨てる。
	$Type = substr($userParameters, 0, 2);//先頭2文字を$Typeに入れる。※$Typeは使っていない。
	$userParameters = substr($userParameters, 2);//$Typeを得るために使った先頭2文字は捨てる。
	$PropName = substr($userParameters, 0, $NameLength);//$NameLengthの長さ分、$PropNameに入れる。
	$PropName = hex2str($PropName);//16進表記文字列を文字に変換。(hex2strは、引数の16進表記文字列を二文字ずつ取り出してアスキーコードに対応した文字列にしていく関数。)
	$userParameters = substr($userParameters, $NameLength);//$PropNameを得るために使った先頭から$NameLengthの長さ分は捨てる。
	$PropValue = substr($userParameters, 0, $ValueLength);//$ValueLengthの長さ分、$PropValueに入れる。
	$userParameters = substr($userParameters, $ValueLength);//$PropValueを得るために使った先頭から$ValueLengthの長さ分は捨てる。
	switch ($PropName) {//$PropValueは、文字列化の方法がおのおの異なるため、caseで分ける。
	case 'CtxWFHomeDir':
	case 'CtxWFHomeDirW'://ASCIIコード→文字列の対象にするため、追加。
	case 'CtxWFHomeDirDrive':
	case 'CtxInitialProgram':
	case 'CtxInitialProgramW'://ASCIIコード→文字列の対象にするため、追加。
	case 'CtxWFProfilePath':
	case 'CtxWFProfilePathW'://ASCIIコード→文字列の対象にするため、追加。
	case 'CtxWorkDirectory':
	case 'CtxWorkDirectoryW'://ASCIIコード→文字列の対象にするため、追加。
	case 'CtxCallbackNumber':
		$parameters[$PropName] = decode_PropValue($PropValue, true);//2番目の引数のtrueは、ASCIIコード→文字列にするという意味。
		break;
	case 'CtxCfgFlags1':
		$parameters[$PropName] = parse_CtxCfgFlags1(decode_PropValue($PropValue));//CtxCfgFlags1は、バイナリ値なので、フラグの判定結果を返す。
		break;
	case 'CtxShadow':
		$parameters[$PropName] = parse_CtxShadow(decode_PropValue($PropValue));//0:Disable,1:EnableInputNotify,2:EnableInputNoNotify,3:EnableNoInputNotify,4:EnableNoInputNoNotify
		break;
	default:
		$parameters[$PropName] = decode_PropValue($PropValue);//ASCIIコード→文字列は行わず、16進数文字列を返す。
	}
}
return $parameters;

userParametersロジック詳細


正常出力例

以下のように正常に出力されるようになりました。(userParameters部分は、json_encodeしています。)

# php ADSearch.php
dn: CN=山田 太郎,CN=Users,DC=ad,DC=contoso,DC=com;
accountexpires: 132854364000000000;
admincount: 1;
badpasswordtime: 132773714847543562;
badpwdcount: 0;
c: JP;
cn: 山田 太郎;
co: 日本;
codepage: 0;
company: 株式会社丸丸;
countrycode: 392;
department: 経営企画部;
description: 山田 太郎の説明;
directreports: CN=佐藤 部下,CN=Users,DC=ad,DC=contoso,DC=com;
displayname: TaroY;
distinguishedname: CN=山田 太郎,CN=Users,DC=ad,DC=contoso,DC=com;
dscorepropagationdata: 20211001094324.0Z; 20211001084813.0Z; 20211001084555.0Z; 20211001084454.0Z; 16010101000000.0Z;
givenname: 太郎;
info: 電話
メモ;
initials: James;
instancetype: 4;
l: 豊田市○○町;
lastlogoff: 0;
lastlogon: 132773705270944929;
lastlogontimestamp: 132773705270944929;
lockouttime: 0;
logoncount: 1;
logonhours: ff1f80ff1f80ff1f80ff1f80ff1f80ff1f80ff1f80;
mail: yamada.taro@contoso.com;
managedobjects: CN=グループX,CN=Users,DC=ad,DC=contoso,DC=com;
manager: CN=田中 上司,CN=Users,DC=ad,DC=contoso,DC=com;
memberof: CN=グループX,CN=Users,DC=ad,DC=contoso,DC=com;
msds-phoneticcompanyname: カブシキガイシャマルマル;
msds-phoneticdepartment: ケイエイキカクブ;
msds-phoneticdisplayname: ヤマダ タロウ;
msds-phoneticfirstname: タロウ;
msds-phoneticlastname: ヤマダ;
msds-supportedencryptiontypes: 0;
msnpcallingstationid: 000-111-2222;
msnpsavedcallingstationid: 000-111-2222;
msradius-framedinterfaceid: ::10;
msradius-framedipv6prefix: fe8e::;
msradius-framedipv6route: 2001:1:2:3::/64 :: 1;
msradius-savedframedinterfaceid: ::10;
msradius-savedframedipv6prefix: fe8e::;
msradius-savedframedipv6route: 2001:1:2:3::/64 :: 1;
msradiuscallbacknumber: 000-222-1111;
msradiusframedipaddress: -1062728523;
msradiusframedroute: 192.168.0.0/24 0.0.0.0 1;
msradiusservicetype: 4;
msrassavedframedipaddress: -1062728523;
msrassavedframedroute: 192.168.0.0/24 0.0.0.0 1;
name: 山田 太郎;
objectcategory: CN=Person,CN=Schema,CN=Configuration,DC=ad,DC=contoso,DC=com;
objectclass: top; person; organizationalPerson; user;
objectguid: 2226c0e2-03df-4696-a09c-8c8e051b2b66;
objectsid: S-1-5-21-312109860-4223824132-3422679046-1103;
otherfacsimiletelephonenumber: 000-000-0001;
otherhomephone: 000-0000-1111;
otheripphone: 000-000-00002;
othermobile: 090-0000-1111;
otherpager: 000-0000-2222;
physicaldeliveryofficename: 山田 太郎の事業所;
postalcode: 000-0000;
postofficebox: 私書箱X;
primarygroupid: 513;
profilepath: \\192.168.12.219\profile\yamada.taro;
pwdlastset: 132775514426599728;
samaccountname: taro2000;
samaccounttype: 805306368;
scriptpath: C:\Windows\SYSVOL\domain\scripts\test.bat;
sn: 山田;
st: 愛知県;
streetaddress: 1-1;
telephonenumber: 090-0000-0000;
title: 部長;
useraccountcontrol: 512;
userparameters: {"CtxCfgPresent":"b00b1e55","CtxCfgFlags1":["F1MSK_INHERITAUTOCLIENT","F1MSK_AUTOCLIENTDRIVES","F1MSK_AUTOCLIENTLPTS","F1MSK_FORCECLIENTLPTDEF","F1MSK_DISABLEENCRYPTION"],"CtxShadow":"EnableInputNotify","CtxMinEncryptionLevel":"01","CtxWorkDirectory":"C:\\Windows\\system32","CtxWorkDirectoryW":"C:\\Windows\\system32","CtxWFHomeDir":"C:\\RDSprofile\\yamadashome","CtxWFHomeDirW":"C:\\RDSprofile\\yamadashome","CtxWFProfilePath":"\\\\192.168.12.219\\RDSprofile\\yamada.taro","CtxWFProfilePathW":"\\\\192.168.12.219\\RDSprofile\\yamada.taro","CtxInitialProgram":"C:\\Windows\\system32\\notepad.exe","CtxInitialProgramW":"C:\\Windows\\system32\\notepad.exe"};
userprincipalname: yamada.taro@ad.contoso.com;
usnchanged: 49305;
usncreated: 16408;
whenchanged: 20211007081802.0Z;
whencreated: 20210923082520.0Z;
wwwhomepage: https://www.contoso.com/;

文字列のデコードについて

各属性値について、文字列かフラグ値が収まっているのですが、文字列について、文字コードがそのまま入っているわけではありません。なぜ、こうなったのか分かりませんが、不可解な仕様になっています。
http://daduke.org/linux/userparameters.html に ASCII コード → バイナリへのエンコードの場合の解説があるのですが、これの逆のことをして、デコードしています。
つまり、3バイトのバイナリ →ASCII コード(1文字)の変換になります。

・3バイトずつ見ていきます。
・先頭4ビットの header は無視されます。
・control x, control y が 011010 の場合、本来の xxxx, yyyy から 10進数で言うと -9 されているため、逆に +9 します。
・xxxxyyyy と4ビットずつ合わせて1バイトの ASCII コードを得ます。
・php の chr 関数で文字にします。※chr 関数は、10進数を引数に取り、該当する ASCII コードの文字を返す関数です。例:chr(hexdec("0x5a"))→0x5a は、90 で Z です。


userParameters値の文字列のデコード


該当部分のソースコード:

function decode_PropValue($hex, $ascii=false)
{
    $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;
}

ちなみに、
CtxInitialProgram C:\Windows\system32\notepad.exe
CtxInitialProgramW C:\Windows\system32\notepad.exe
のように W 有り無しの属性名が有りますが、
これの違いは、例えば、"exe"の文字列の場合、
W 無しの方は、普通に"exe"
W 有りの方は、"[NULL 文字]e[NULL 文字]x[NULL 文字]e" と[NULL 文字]が入っています。


$blob:e380b0
$byte:00000000
chr(bindec($byte)):NULL

$blob:e394b6
$byte:01100101
chr(bindec($byte)):e

$blob:e380b0
$byte:00000000
chr(bindec($byte)):NULL

$blob:e3a0b7
$byte:01111000
chr(bindec($byte)):x

$blob:e380b0
$byte:00000000
chr(bindec($byte)):NULL

$blob:e394b6
$byte:01100101
chr(bindec($byte)):e


PowerShell 版について

userParameters 属性の値を解読できる PowerShell で書かれたこちら(gist.github.com)を見つけました。

以下の要領で、ConvertFrom-UserParameter.ps1をパスが通るところに置いて実行すると、userParameters 属性値が何も問題無く取り出されます。

PS C:\Users\Administrator> . ConvertFrom-UserParameter.ps1
PS C:\Users\Administrator> (Get-ADUser yamada.taro -Properties "userParameters").userParameters | ConvertFrom-UserParameter -ShowAll
(結果表示は略)

こちらは、0xc2の問題は発生しません。
Get-ADUserコマンドレットから返ったバイナリ値が ldapsearch の時と異なり、加工済みのためです。

PS C:\Users\Administrator> (Get-ADUser yamada.taro -Properties "userParameters").userParameters > userParameters.bin

として、取得した userParameters.bin をバイナリエディタで確認すると、以下のようになっています。(CtxInitialProgramW の部分だけを抜粋)

00000440:                                     24 00 80 00
00000450: 01 00 43 00 74 00 78 00 49 00 6E 00 69 00 74 00
00000460: 69 00 61 00 6C 00 50 00 72 00 6F 00 67 00 72 00
00000470: 61 00 6D 00 57 00 34 33 30 30 33 61 30 30 35 63
00000480: 30 30 35 37 30 30 36 39 30 30 36 65 30 30 36 34
00000490: 30 30 36 66 30 30 37 37 30 30 37 33 30 30 35 63
000004A0: 30 30 37 33 30 30 37 39 30 30 37 33 30 30 37 34
000004B0: 30 30 36 35 30 30 36 64 30 30 33 33 30 30 33 32
000004C0: 30 30 35 63 30 30 36 65 30 30 36 66 30 30 37 34
000004D0: 30 30 36 35 30 30 37 30 30 30 36 31 30 30 36 34
000004E0: 30 30 32 65 30 30 36 35 30 30 37 38 30 30 36 35
000004F0: 30 30 30 30 30 30 0D 00 0A 00

ldap のデータ 0x24c28001437478496e697469616c50726f6772616d57
ldap : Get-ADUser
24 : 24 00
c2 : なし
80 : 80 00
01 : 01 00
437478496e697469616c50726f6772616d57 : 43 00 74 00 78 00 ...
のように、バイナリデータは WORD、ASCII データは UNICODE に変換されています。
つまり、PowerShell のConvertFrom-UserParameter.ps1は、0xc2を心配する必要もなく、上述の文字列のデコードについてのような文字列抽出処理も要らないのです。


PowerShell 版の誤りについて

php でuserParametersの値を取り出せるようになって、PowerShell 版(ConvertFrom-UserParameter.ps1)に誤りが有ることに気付きました。
CtxCfgFlags1の値(フラグ名文字列の配列にしたもの)が php で抽出したものと異なります。


php:

"CtxCfgFlags1":["F1MSK_INHERITAUTOCLIENT","F1MSK_AUTOCLIENTDRIVES","F1MSK_AUTOCLIENTLPTS","F1MSK_FORCECLIENTLPTDEF","F1MSK_DISABLEENCRYPTION"]

PowerShell:

PS C:\Users\Administrator> ((Get-ADUser taro2000 -Properties "userParameters").userParameters | ConvertFrom-UserParameter -ShowAll).CtxCfgFlags1
INHERITMAXDISCONNECTIONTIME
DISABLECCM
INHERITAUTOCLIENT
INHERITMAXIDLETIME
INHERITSECURITY
FORCECLIENTLPTDEF

フラグの内容が異なり、例えば、GUI の Active Directory ユーザーとコンピューター「ログオン時、クライアントのドライブに接続する」のチェックボックスを OFF にすると、以下の変化があります。
php: F1MSK_AUTOCLIENTDRIVESが無くなる。
PowerShell: INHERITMAXDISCONNECTIONTIMEが無くなる。

Active Directory ユーザーとコンピューター「ログオン時、クライアントのドライブに接続する」のチェックボックス

「ログオン時、クライアントのドライブに接続する」とINHERITMAXDISCONNECTIONTIMEは言葉の意味が無関係で、php の方は関係有りそうなことから、PowerShell の方に誤りがありそうなことが分かります。


ConvertFrom-UserParameter.ps1を修正し、以下の diff のように、
フラグ判定対象のバイナリ値のバイト並びを逆向きにして判定すると、php で出力したフラグと一致しました。

「バイト並びを逆向き」の例(値は適当で、あくまでイメージです。)
0x123456780x78563412


diff:

@@ -97,6 +97,7 @@ https://msdn.microsoft.com/en-us/library/ff635169.aspx
97
97
  BEGIN {
98
98
  # values from https://msdn.microsoft.com/en-us/library/ff635169.aspx
99
99
  $CtxCfgFlagsBitValues = @{
100
+ 'INHERITINITIALPROGRAM' = 0x10000000
100
101
  'INHERITCALLBACK' = 0x08000000
101
102
  'INHERITCALLBACKNUMBER' = 0x04000000
102
103
  'INHERITSHADOW' = 0x02000000
@@ -158,20 +159,22 @@ https://msdn.microsoft.com/en-us/library/ff635169.aspx
158
159
  $Type = $BinaryReader.ReadUInt16()
159
160
 
160
161
  $AttributeName = [System.Text.Encoding]::UNICODE.GetString($BinaryReader.ReadBytes($NameLength))
161
- $AttributeData = $BinaryReader.ReadBytes($ValueLength)
162
162
  Write-Verbose "AttributeName: $AttributeName"
163
163
 
164
164
  if ($AttributeName -match 'CtxCfgPresent|CtxCfgFlags1|CtxCallBack|CtxKeyboardLayout|CtxMinEncryptionLevel|CtxNWLogonServer|CtxMaxConnectionTime|CtxMaxDisconnectionTime|CtxMaxIdleTime|CtxShadow|CtxMinEncryptionLevel') {
165
- $AttributeData = -join($AttributeData | ForEach-Object {[Char][Byte]$_})
165
+ $AttributeData = ""
166
+ For ($i = 0; $i -lt $ValueLength; $i += 2) {
167
+ $AttributeData = [System.Text.Encoding]::UTF8.GetString($BinaryReader.ReadBytes(2)) + $AttributeData
168
+ }
166
169
  $AttributeValue = [Convert]::ToInt32($AttributeData, 16)
167
170
 
168
171
  if ($AttributeName -match 'CtxShadow') {
169
172
  $AttributeValue = Switch ($AttributeValue) {
170
- 0x0 { 'Disable' }
171
- 0x1000000 { 'EnableInputNotify' }
172
- 0x2000000 { 'EnableInputNoNotify' }
173
- 0x3000000 { 'EnableNoInputNotify' }
174
- 0x4000000 { 'EnableNoInputNoNotify' }
173
+ 0 { 'Disable' }
174
+ 1 { 'EnableInputNotify' }
175
+ 2 { 'EnableInputNoNotify' }
176
+ 3 { 'EnableNoInputNotify' }
177
+ 4 { 'EnableNoInputNoNotify' }
175
178
  default { $AttributeValue }
176
179
  }
177
180
  }
@@ -188,6 +191,7 @@ https://msdn.microsoft.com/en-us/library/ff635169.aspx
188
191
  $ResultValues.Add($AttributeName, $AttributeValue)
189
192
  }
190
193
  elseif ($AttributeName -match 'CtxWFHomeDirDrive|CtxWFHomeDir|CtxWFHomeDrive|CtxInitialProgram|CtxWFProfilePath|CtxWorkDirectory|CtxCallbackNumber') {
194
+ $AttributeData = $BinaryReader.ReadBytes($ValueLength)
191
195
  $AttributeValue = ''
192
196
  For ($i = 0; $i -lt $ValueLength; $i += 2) {
193
197
  $ValueChar = [Char][Byte]([Convert]::ToInt16([Char][byte]$AttributeData[$i] + [char][byte]$AttributeData[$i+1], 16))


フラグの一つ
'INHERITINITIALPROGRAM' = 0x10000000
も抜けていましたので、足しました。


ソースコード全体は、本家のを fork して、修正し、https://gist.github.com/itc-lab/3749188ce58ad803a97ae3f1f924c435 に置きました。


PowerShell:

PS C:\Users\Administrator> ((Get-ADUser taro2000 -Properties "userParameters").userParameters | ConvertFrom-UserParameter -ShowAll).CtxCfgFlags1
INHERITAUTOCLIENT
AUTOCLIENTDRIVES
AUTOCLIENTLPTS
DISABLEENCRYPTION
FORCECLIENTLPTDEF

php の結果と一致するようになりました。

loading...