EternalWindows
LSA / 高度なログオン

通常、アプリケーションがユーザーをログオンさせるために呼び出す関数はLogonUserですが、 ログオンはLsaLogonUserでも行うことができます。 この関数はLogonUserと比べて複雑な引数を要求しますが、 トークンのハンドル以外に多くの情報を返すことができる利点があります。 LsaLogonUserは、次のように定義されています。

NTSTATUS LsaLogonUser(
  HANDLE LsaHandle,
  PLSA_STRING OriginName,
  SECURITY_LOGON_TYPE LogonType,
  ULONG AuthenticationPackage,
  PVOID AuthenticationInformation,
  ULONG AuthenticationInformationLength,
  PTOKEN_GROUPS LocalGroups,
  PTOKEN_SOURCE SourceContext,
  PVOID *ProfileBuffer,
  PULONG ProfileBufferLength,
  PLUID LogonId,
  PHANDLE Token,
  PQUOTA_LIMITS Quotas,
  PNTSTATUS SubStatus
);

LsaHandleは、LSAサーバーのハンドルを指定します。 OriginNameは、LSA_STRING構造体のアドレスを指定します。 格納する文字列は、自由に決定して構いません。 LogonTypeは、ログオンタイプとして定義されている定数を指定します。 AuthenticationPackageは、使用する認証パッケージの識別子を指定します。 AuthenticationInformationは、ユーザー名やパスワードを格納したバッファを指定します。 AuthenticationInformationLengthは、AuthenticationInformationのサイズを指定します。 LocalGroupsは、作成されるトークンに追加するグループSIDを指定します。 SourceContextは、トークンの作成元を表すTOKEN_SOURCE構造体のアドレスを指定しています。 ProfileBufferは、ログオン情報を格納したバッファを受け取る変数のアドレスを指定します。 ProfileBufferLengthは、ProfileBufferのサイズを受け取る変数のアドレスを指定します。 LogonIdは、ログオンセッションのLUIDを受け取る変数のアドレスを指定します。 Tokenは、作成されたトークンを受け取る変数のアドレスを指定します。 Quotasは、QUOTA_LIMITS構造体のアドレスを指定します。 SubStatusは、関数がアカウント制限により失敗した場合の理由を受け取る変数のアドレスを指定します。

LsaLogonUserを呼び出すためには、LSAサーバーのハンドルが必要になります。 これは、LsaConnectUntrustedを呼び出すことで取得できます。

NTSTATUS LsaConnectUntrusted(
  PHANDLE LsaHandle
);

LsaHandleは、LSAサーバーのハンドルを受け取る変数のアドレスを指定します。

LSAサーバーのハンドルが不要になった場合は、LsaDeregisterLogonProcessを呼び出します。

NTSTATUS LsaDeregisterLogonProcess(
  HANDLE LsaHandle
);

LsaHandleは、LSAサーバーのハンドルを指定します。

LsaLogonUserを呼び出すためには、使用する認証パッケージの識別子も必要になります。 認証パッケージは認証を行うDLLであり、 アプリケーションはどのような認証を行うかを決定することができます。 認証パッケージの識別子を取得するには、LsaLookupAuthenticationPackageを呼び出します。

NTSTATUS LsaLookupAuthenticationPackage(
  HANDLE LsaHandle,
  PLSA_STRING PackageName,
  PULONG AuthenticationPackage
);

LsaHandleは、LSAサーバーのハンドルを指定します。 PackageNameは、使用する認証パッケージの名前を指定します。 AuthenticationPackageは、認証パッケージの識別子を受け取る変数のアドレスを指定します。

今回のプログラムは、LsaLogonUserを使用したログオンを行います。

#include <windows.h>
#include <ntsecapi.h>

#pragma comment (lib, "secur32.lib")

BOOL MyLogonUser(HANDLE hLsa, ULONG uPackageId, LPWSTR lpszUserName, LPWSTR lpszPassword, LPWSTR lpszDomainName, HANDLE *hToken);
ULONG InitString(PLSA_STRING plsaString, LPSTR lpszString);
ULONG InitUnicodeString(PLSA_UNICODE_STRING plsaString, LPWSTR lpszString);
void FormatBuffer(LPBYTE *lp, PUNICODE_STRING pString);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	ULONG      uPackageId;
	HANDLE     hLsa;
	HANDLE     hToken;
	NTSTATUS   ns; 
	LSA_STRING lsaString;
	
	ns = LsaConnectUntrusted(&hLsa);
	if (LsaNtStatusToWinError(ns) != ERROR_SUCCESS) {
		MessageBox(NULL, TEXT("LSAサーバーへの接続に失敗しました。"), NULL, MB_ICONWARNING);
		return 0;
	}

	InitString(&lsaString, MSV1_0_PACKAGE_NAME);
	ns = LsaLookupAuthenticationPackage(hLsa, &lsaString, &uPackageId);
	if (LsaNtStatusToWinError(ns) != ERROR_SUCCESS) {
		MessageBox(NULL, TEXT("認証パッケージのIDの取得に失敗しました。"), NULL, MB_ICONWARNING);
		LsaDeregisterLogonProcess(hLsa);
		return 0;
	}

	if (!MyLogonUser(hLsa, uPackageId, L"Guest", NULL, NULL, &hToken)) {
		MessageBox(NULL, TEXT("ログオンに失敗しました。"), NULL, MB_ICONWARNING);
		LsaDeregisterLogonProcess(hLsa);
		return 0;
	}
	
	MessageBox(NULL, TEXT("ログオンに成功しました。"), TEXT("OK"), MB_OK);
	
	CloseHandle(hToken);
	LsaDeregisterLogonProcess(hLsa);

	return 0;
}

BOOL MyLogonUser(HANDLE hLsa, ULONG uPackageId, LPWSTR lpszUserName, LPWSTR lpszPassword, LPWSTR lpszDomainName, HANDLE *phToken)
{
	LUID                        luid;
	ULONG                       uBufferLength;
	ULONG                       uProfileLength;
	LPBYTE                      lp;
	NTSTATUS                    ns;
	NTSTATUS                    nsSub;
	LSA_STRING                  lsaOriginal;
	QUOTA_LIMITS                limits;
	TOKEN_SOURCE                tokenSource;
	MSV1_0_INTERACTIVE_LOGON    msvLogon;
	PMSV1_0_INTERACTIVE_LOGON   pmsvLogon;
	PMSV1_0_INTERACTIVE_PROFILE pmsvProfile;
	
	InitString(&lsaOriginal, "origial");

	uBufferLength  =  sizeof(MSV1_0_INTERACTIVE_LOGON);
	uBufferLength += InitUnicodeString(&msvLogon.UserName, lpszUserName);
	uBufferLength += InitUnicodeString(&msvLogon.Password, lpszPassword);
	uBufferLength += InitUnicodeString(&msvLogon.LogonDomainName, lpszDomainName);
	msvLogon.MessageType = MsV1_0InteractiveLogon;

	lp = (LPBYTE)LocalAlloc(LPTR, uBufferLength);
	CopyMemory(lp, (PVOID)&msvLogon, sizeof(MSV1_0_INTERACTIVE_LOGON));
	pmsvLogon = (PMSV1_0_INTERACTIVE_LOGON)lp;
	
	lp += sizeof(MSV1_0_INTERACTIVE_LOGON);
	FormatBuffer(&lp, &pmsvLogon->UserName);

	lp += pmsvLogon->UserName.Length;
	FormatBuffer(&lp, &pmsvLogon->Password);

	lp += pmsvLogon->Password.Length;
	FormatBuffer(&lp, &pmsvLogon->LogonDomainName);

	lstrcpyA(tokenSource.SourceName, "sample");
	AllocateLocallyUniqueId(&tokenSource.SourceIdentifier);

	ns = LsaLogonUser(hLsa, &lsaOriginal, Interactive, uPackageId,
		(PVOID)pmsvLogon, uBufferLength, NULL, &tokenSource, (PVOID *)&pmsvProfile,
		&uProfileLength, &luid, phToken, &limits, &nsSub);
	if (LsaNtStatusToWinError(ns) != ERROR_SUCCESS) {
		LocalFree(pmsvLogon);
		return FALSE;
	}

	LsaFreeReturnBuffer(pmsvProfile);
	LocalFree(pmsvLogon);
	
	return TRUE;
}

ULONG InitString(PLSA_STRING plsaString, LPSTR lpszString)
{
	if (lpszString == NULL) {
		plsaString->Length        = 0;
		plsaString->MaximumLength = 0;
		plsaString->Buffer        = NULL;
	}
	else {
		plsaString->Length        = (USHORT)(lstrlenA(lpszString) * sizeof(CHAR));
		plsaString->MaximumLength = plsaString->Length + sizeof(CHAR);
		plsaString->Buffer        = lpszString;
	}
	
	return plsaString->Length;
}

ULONG InitUnicodeString(PLSA_UNICODE_STRING plsaString, LPWSTR lpszString)
{
	if (lpszString == NULL) {
		plsaString->Length        = 0;
		plsaString->MaximumLength = 0;
		plsaString->Buffer        = NULL;
	}
	else {
		plsaString->Length        = (USHORT)(lstrlen(lpszString) * sizeof(WCHAR));
		plsaString->MaximumLength = plsaString->Length + sizeof(WCHAR);
		plsaString->Buffer        = lpszString;
	}
	
	return plsaString->Length;
}

void FormatBuffer(LPBYTE *lp, PUNICODE_STRING pString)
{
	if (pString->Buffer != NULL) {
		CopyMemory(*lp, pString->Buffer, pString->Length);
		pString->Buffer = (LPWSTR)*lp;
	}
}

WinMainでは、LsaConnectUntrustedでLSAサーバーのハンドルを取得し、 LsaLookupAuthenticationPackageで認証パッケージの識別子を取得しています。 使用する認証パッケージはMSV1_0認証を表すMSV1_0_PACKAGE_NAMEを指定し、 これをLSA_STRING構造体に格納するために、自作関数のInitStringを呼び出しています。 必要な情報が揃えば、MyLogonUserという自作関数でLsaLogonUserを呼び出します。

MyLogonUser(hLsa, uPackageId, L"Guest", NULL, NULL, &hToken)

第3引数はログオンしたいユーザー名を指定します、 第4引数はユーザーのパスワードであり、この例ではGuestアカウントをログオンしようとしているので、 NULLを指定しています。 第5引数は認証情報が保存されているドメイン名を指定します。 ドメインが構成されていない場合は、NULLで問題ありません。

ユーザー名やパスワードをLsaLogonUserに指定するためには、 これらをバッファに格納しておく必要があります。 MSV1_0認証の場合は、バッファをMSV1_0_INTERACTIVE_LOGON構造体で表すことができます。

typedef struct _MSV1_0_INTERACTIVE_LOGON {
  MSV1_0_LOGON_SUBMIT_TYPE MessageType;
  UNICODE_STRING           LogonDomainName;
  UNICODE_STRING           UserName;
  UNICODE_STRING           Password;
} MSV1_0_INTERACTIVE_LOGON;

MessageTypeは、MsV1_0InteractiveLogonを指定します。 LogonDomainNameは、ドメインを表す文字列を指定します。 UserNameは、ログオンするユーザーの名前を指定します。 Passwordは、ユーザーのパスワードを指定します。 MyLogonUserでは、この構造体を次のように初期化しています。

uBufferLength  =  sizeof(MSV1_0_INTERACTIVE_LOGON);
uBufferLength += InitUnicodeString(&msvLogon.UserName, lpszUserName);
uBufferLength += InitUnicodeString(&msvLogon.Password, lpszPassword);
uBufferLength += InitUnicodeString(&msvLogon.LogonDomainName, lpszDomainName);
msvLogon.MessageType = MsV1_0InteractiveLogon;

InitUnicodeStringという自作関数は、第2引数の文字列を第1引数のUNICODE_STRING構造体に指定します。 これにより、MSV1_0_INTERACTIVE_LOGON型のmsvLogonのメンバが初期化されることになります。 InitUnicodeStringは、第2引数に指定された文字列のサイズを返すため、 これをバッファのサイズを表すuBufferLengthに加算するようにしています。

さて、これでバッファとバッファの長さの初期化を終えたことになるのでしょうか。 実は、このままmsvLogonのアドレスをLsaLogonUserの第5引数に指定すると関数は失敗することになります。 LsaLogonUserは、バッファ内に必要な情報が全て揃っていることを前提にしています。 ここで述べているバッファ内とは、第5引数で指定した先頭アドレスから、 第6引数で表されるサイズまでの範囲にあるメモリのことです。 msvLogonをバッファの先頭アドレスにした場合、 UserNameメンバやPasswordメンバから 文字列を参照できるわけですが、 この文字列そのものがバッファ内に 存在しているわけではないので、関数は失敗してしまうことになるのです。 今回のプログラムでは、以下のような手順でこの問題を解決しています。

1. バッファのサイズ分、メモリを確保する。
2. バッファにMSV1_0_INTERACTIVE_LOGON構造体をコピーする。
3. 先頭アドレスを適切にずらし文字列をコピーする。
そして、その文字列のアドレスを指すように、MSV1_0_INTERACTIVE_LOGON構造体のメンバを調整する。

1, 2は次のコードによって実現しています。

lp = (LPBYTE)LocalAlloc(LPTR, uBufferLength);
CopyMemory(lp, (PVOID)&msvLogon, sizeof(MSV1_0_INTERACTIVE_LOGON));
pmsvLogon = (PMSV1_0_INTERACTIVE_LOGON)lp;

uBufferLengthは文字列を含めたバッファのサイズなので、この値だけメモリを確保します。 そして、その先頭にmsvLogonをコピーします。 lpは文字列のコピーをするため、 自身が指すアドレスは変化しますが、 pmsvLogonは常にバッファの先頭アドレスを指します。 3の部分は、次のコードによって実現されています。

lp += sizeof(MSV1_0_INTERACTIVE_LOGON);
FormatBuffer(&lp, &pmsvLogon->UserName);

先に述べたようにバッファには文字列を含めなければなりません。 lpは現在、バッファの先頭アドレスを指しており、ここに文字列コピーしてしまっては MSV1_0_INTERACTIVE_LOGON構造体を上書きしてしまうので、 sizeof(MSV1_0_INTERACTIVE_LOGON)だけアドレスを調整することになります。 自作関数のFormatBufferは、次のような構造になっています。

void FormatBuffer(LPBYTE *lp, PUNICODE_STRING pString)
{
	if (pString->Buffer != NULL) {
		CopyMemory(*lp, pString->Buffer, pString->Length);
		pString->Buffer = (LPWSTR)*lp;
	}
}

まず、文字列をコピーします。 これで、バッファ内に文字列が存在することになりましたが、まだするべきことが残っています。 文字列はUNICODE_STRING構造体のBufferメンバで参照されるわけですが、 このメンバはバッファ内の文字列を指さなくてはなりません。 よって、lpをBufferメンバに指定します。

LsaLogonUserを呼び出すには、バッファの他にTOKEN_SOURCE構造体を初期化する必要があります。 トークンソースとは、トークンの作成元を表す情報です。

lstrcpyA(tokenSource.SourceName, "sample");
AllocateLocallyUniqueId(&tokenSource.SourceIdentifier);

SourceNameは、トークンの作成元を表す最高8バイトのANSI文字列を指定します。 SourceIdentifierは、AllocateLocallyUniqueIdで確保された一意のLUIDを指定します。 トークンソースを取得したい場合は、GetTokenInformationにTokenSourceを指定することになります。 LogonUserでユーザーをログオンさした場合は、SourceNameにAdvapiが格納されています。

LsaLogonUserの呼び出しは、次のようになっています。

ns = LsaLogonUser(hLsa, &lsaOriginal, Interactive, uPackageId,
	(PVOID)pmsvLogon, uBufferLength, NULL, &tokenSource, (PVOID *)&pmsvProfile,
	&uProfileLength, &luid, phToken, &limits, &nsSub);

第2引数のlsaOriginalは、自由な文字列を格納しているだけで構いません。 第3引数は、MSV1_0の対話認証を行うことからInteractiveを指定します。 pmsvProfileからnsSubまでは、事前に初期化などの必要がないため、 単純に変数のアドレスを指定するだけで問題ありません。

トークングループの追加

LsaLogonUserの第7引数にTOKEN_GROUPS構造体を指定した場合、 作成されるトークンには既定のグループに加え、 明示的に指定したグループも含まれることになります。 この機能を利用すれば、標準ユーザーのトークンにAdministratorsのSIDを含ませるようなことも可能になり、 セキュリティ的な面で危険であるといえます。 こうしたことから、トークングループを指定してLsaLogonUserを呼び出すには、 LsaRegisterLogonProcessでLSAサーバーのハンドルを取得することになっています。 LsaRegisterLogonProcessを呼び出すには、SE_TCB_NAME特権が割り当てられて有効になっていなければならないため、 明示的に特権が割り当てられていない限り、トークングループの追加を行うことはできません。 特権が既に割り当てられているものとして、AdministratorsのSIDを追加する例を示します。

#include <windows.h>
#include <ntsecapi.h>

#pragma comment (lib, "secur32.lib")

BOOL MyLogonUser(HANDLE hLsa, ULONG uPackageId, LPWSTR lpszUserName, LPWSTR lpszPassword, LPWSTR lpszDomainName, HANDLE *hToken);
ULONG InitString(PLSA_STRING plsaString, LPSTR lpszString);
ULONG InitUnicodeString(PLSA_UNICODE_STRING plsaString, LPWSTR lpszString);
void FormatBuffer(LPBYTE *lp, PUNICODE_STRING pString);
BOOL EnablePrivilege(LPTSTR lpszPrivilege, BOOL bEnable);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	ULONG                uPackageId;
	HANDLE               hLsa;
	HANDLE               hToken;
	NTSTATUS             ns; 
	LSA_STRING           lsaString;
	LSA_OPERATIONAL_MODE mode;

	if (!EnablePrivilege(SE_TCB_NAME, TRUE)) {
		MessageBox(NULL, TEXT("SE_TCB_NAME特権の有効に失敗しました。"), NULL, MB_ICONWARNING);
		return 0;
	}
	
	InitString(&lsaString, "MyLogonProcess");
	ns = LsaRegisterLogonProcess(&lsaString, &hLsa, &mode);
	if (LsaNtStatusToWinError(ns) != ERROR_SUCCESS) {
		MessageBox(NULL, TEXT("LSAサーバーへの接続に失敗しました。"), NULL, MB_ICONWARNING);
		return 0;
	}
	
	InitString(&lsaString, MSV1_0_PACKAGE_NAME);
	ns = LsaLookupAuthenticationPackage(hLsa, &lsaString, &uPackageId);
	if (LsaNtStatusToWinError(ns) != ERROR_SUCCESS) {
		MessageBox(NULL, TEXT("認証パッケージのIDの取得に失敗しました。"), NULL, MB_ICONWARNING);
		LsaDeregisterLogonProcess(hLsa);
		return 0;
	}

	if (!MyLogonUser(hLsa, uPackageId, L"Guest", NULL, NULL, &hToken)) {
		MessageBox(NULL, TEXT("ログオンに失敗しました。"), NULL, MB_ICONWARNING);
		LsaDeregisterLogonProcess(hLsa);
		return 0;
	}
	
	MessageBox(NULL, TEXT("ログオンに成功しました。"), TEXT("OK"), MB_OK);
	
	CloseHandle(hToken);
	LsaDeregisterLogonProcess(hLsa);

	return 0;
}

BOOL MyLogonUser(HANDLE hLsa, ULONG uPackageId, LPWSTR lpszUserName, LPWSTR lpszPassword, LPWSTR lpszDomainName, HANDLE *phToken)
{
	LUID                        luid;
	ULONG                       uBufferLength;
	ULONG                       uProfileLength;
	LPBYTE                      lp;
	NTSTATUS                    ns;
	NTSTATUS                    nsSub;
	LSA_STRING                  lsaOriginal;
	QUOTA_LIMITS                limits;
	TOKEN_SOURCE                tokenSource;
	MSV1_0_INTERACTIVE_LOGON    msvLogon;
	PMSV1_0_INTERACTIVE_LOGON   pmsvLogon;
	PMSV1_0_INTERACTIVE_PROFILE pmsvProfile;
	PSID                        pSidGroup;
	TOKEN_GROUPS                tokenGroups;
	DWORD                       dwSidSize;

	InitString(&lsaOriginal, "origial");

	uBufferLength  =  sizeof(MSV1_0_INTERACTIVE_LOGON);
	uBufferLength += InitUnicodeString(&msvLogon.UserName, lpszUserName);
	uBufferLength += InitUnicodeString(&msvLogon.Password, lpszPassword);
	uBufferLength += InitUnicodeString(&msvLogon.LogonDomainName, lpszDomainName);
	msvLogon.MessageType = MsV1_0InteractiveLogon;

	lp = (LPBYTE)LocalAlloc(LPTR, uBufferLength);
	CopyMemory(lp, (PVOID)&msvLogon, sizeof(MSV1_0_INTERACTIVE_LOGON));
	pmsvLogon = (PMSV1_0_INTERACTIVE_LOGON)lp;
	
	lp += sizeof(MSV1_0_INTERACTIVE_LOGON);
	FormatBuffer(&lp, &pmsvLogon->UserName);

	lp += pmsvLogon->UserName.Length;
	FormatBuffer(&lp, &pmsvLogon->Password);

	lp += pmsvLogon->Password.Length;
	FormatBuffer(&lp, &pmsvLogon->LogonDomainName);

	lstrcpyA(tokenSource.SourceName, "sample");
	AllocateLocallyUniqueId(&tokenSource.SourceIdentifier);

	dwSidSize = SECURITY_MAX_SID_SIZE;
	pSidGroup = (PSID)LocalAlloc(LPTR, dwSidSize);
	CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, pSidGroup, &dwSidSize);

	tokenGroups.GroupCount           = 1;
	tokenGroups.Groups[0].Sid        = pSidGroup;
	tokenGroups.Groups[0].Attributes = SE_GROUP_ENABLED;

	ns = LsaLogonUser(hLsa, &lsaOriginal, Interactive, uPackageId,
		(PVOID)pmsvLogon, uBufferLength, &tokenGroups, &tokenSource, (PVOID *)&pmsvProfile,
		&uProfileLength, &luid, phToken, &limits, &nsSub);
	if (LsaNtStatusToWinError(ns) != ERROR_SUCCESS) {
		LocalFree(pSidGroup);
		LocalFree(pmsvLogon);
		return FALSE;
	}

	LsaFreeReturnBuffer(pmsvProfile);
	LocalFree(pSidGroup);
	LocalFree(pmsvLogon);
	
	return TRUE;
}

ULONG InitString(PLSA_STRING plsaString, LPSTR lpszString)
{
	if (lpszString == NULL) {
		plsaString->Length        = 0;
		plsaString->MaximumLength = 0;
		plsaString->Buffer        = NULL;
	}
	else {
		plsaString->Length        = (USHORT)(lstrlenA(lpszString) * sizeof(CHAR));
		plsaString->MaximumLength = plsaString->Length + sizeof(CHAR);
		plsaString->Buffer        = lpszString;
	}
	
	return plsaString->Length;
}

ULONG InitUnicodeString(PLSA_UNICODE_STRING plsaString, LPWSTR lpszString)
{
	if (lpszString == NULL) {
		plsaString->Length        = 0;
		plsaString->MaximumLength = 0;
		plsaString->Buffer        = NULL;
	}
	else {
		plsaString->Length        = (USHORT)(lstrlen(lpszString) * sizeof(WCHAR));
		plsaString->MaximumLength = plsaString->Length + sizeof(WCHAR);
		plsaString->Buffer        = lpszString;
	}
	
	return plsaString->Length;
}

void FormatBuffer(LPBYTE *lp, PUNICODE_STRING pString)
{
	if (pString->Buffer != NULL) {
		CopyMemory(*lp, pString->Buffer, pString->Length);
		pString->Buffer = (LPWSTR)*lp;
	}
}

BOOL EnablePrivilege(LPTSTR lpszPrivilege, BOOL bEnable)
{
	BOOL             bResult;
	LUID             luid;
	HANDLE           hToken;
	TOKEN_PRIVILEGES tokenPrivileges;

	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
		return FALSE;
	
	if (!LookupPrivilegeValue(NULL, lpszPrivilege, &luid)) {
		CloseHandle(hToken);
		return FALSE;
	}

	tokenPrivileges.PrivilegeCount           = 1;
	tokenPrivileges.Privileges[0].Luid       = luid;
	tokenPrivileges.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;
	
	bResult = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
	
	CloseHandle(hToken);

	return bResult && GetLastError() == ERROR_SUCCESS;
}

このプログラムでは、LsaConnectUntrustedではなくLsaRegisterLogonProcessを呼び出して、 LSAサーバーのハンドルを取得しています。 また、LsaRegisterLogonProcessの呼び出しを成功させるために、 SE_TCB_NAME特権を有効にしています。 LsaRegisterLogonProcessの第1引数はLSA_STRING構造体のアドレスであり、 格納する文字列は自由に決定して問題ありません。 第3引数は、LSA_OPERATIONAL_MODE構造体のアドレスを指定します。 メンバを初期化しておく必要はありませんが、 NULLを指定すると関数が失敗するので注意してください。

MyLogonUserでは、TOKEN_GROUPS構造体であるtokenGroupsを宣言し、 SidメンバにAdministratorsのSIDを指定しています。 このSIDは、CreateWellKnownSidの第1引数にWinBuiltinAdministratorsSidを指定することによって作成することができます。 tokenGroupsの初期化が終われば、これをLsaLogonUserの第7引数に指定します。 そして、関数が成功すれば、作成されたトークンのトーグループにAdministratorsのSIDが追加されていることになります。



戻る