EternalWindows
アカウント管理 / SIDとRID

Windowsをインストールするとき、セットアッププログラムはコンピュータにSIDを発行します。 その後、各種アカウントにもSIDを発行するのですが、 このSIDはコンピュータのSIDをベースとして、最後にRIDを付加した形になります。 前節のプログラムでは、コンピュータのSIDとユーザーのSIDを表示しましたが、 異なっていたのはRIDが付加されていることだけだったはずです。 RIDはサブ機関値とも呼びますが、特に最後のサブ機関値はRIDと呼ばれることが多いと思われます。 ユーザーやグループのRIDは1000から始まり、 新しいユーザーやグループが加わるたびに1ずつ増えることになります。

アカウントのSIDがコンピュータのSIDをベースにしているということは、 ある1つのアカウントのSIDを取得することは、他のアカウントのSIDも列挙できることを意味します。 その取得したアカウントのSIDのRIDを1つずつカウントしていけば、 既存アカウントのSIDと一致することが期待できるからです。 これを実現するためには、RIDのインデックスを取得しなければならないため、 SIDに存在するサブ機関値の数を取得するGetSidSubAuthorityCountを呼び出すことになります。

PUCHAR WINAPI GetSidSubAuthorityCount(
  PSID pSid
);

pSidは、SIDを指定します。 戻り値は、サブ機関値への数を格納したアドレスが返ります。

サブ機関値の数から1を引いたインデックスのサブ機関値は、最後のサブ機関値でありRIDです。 このアドレスを取得するには、GetSidSubAuthorityを呼び出します。

PDWORD WINAPI GetSidSubAuthority(
  PSID pSid,
  DWORD nSubAuthority
);

pSidは、SIDを指定します。 nSubAuthorityは、アドレスを取得したいサブ機関値のインデックスを指定します。 戻り値は、サブ機関値のアドレスが返ります。

SIDからアカウント名を取得するには、LookupAccountSidを呼び出します。

BOOL WINAPI LookupAccountSid(
  LPCTSTR lpSystemName,
  PSID lpSid,
  LPTSTR lpName,
  LPDWORD cchName,
  LPTSTR lpReferencedDomainName,
  LPDWORD cchReferencedDomainName,
  PSID_NAME_USE peUse
);

lpSystemNameは、アカウントの検索に使用するシステムの名前を指定します。 NULLを指定した場合は、ローカルコンピュータが参照されます。 lpSidは、SIDを指定します。 lpNameは、SIDに対応するアカウント名を受け取るバッファを指定します。 cchNameは、lpNameのサイズを格納した変数のアドレスを指定します。 サイズが足りない場合は、必要なサイズが格納されます。 ReferencedDomainNameは、アカウントが見つかったドメインを受け取るバッファを指定します。 cchReferencedDomainNameは、ReferencedDomainNameのサイズを格納した変数のアドレスを指定します。 サイズが足りない場合は、必要なサイズが格納されます。 peUseは、SIDの種類を受け取る変数のアドレスを指定します。

今回のプログラムは、RIDをカウントすることで、ローカルコンピュータ上に存在するアカウントを列挙します。

#include <windows.h>

BOOL ConvertNameToSid(LPTSTR lpszName, PSID *ppSid);
BOOL ConvertSidToName(PSID pSid, LPTSTR lpszName, DWORD dwSizeName);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndListBox = NULL;

	switch (uMsg) {

	case WM_CREATE: {
		DWORD i;
		DWORD dwSubAuthorityCount;
		DWORD dwBufferSize;
		TCHAR szBuf[256];
		TCHAR szAccountName[256];
		PSID  pSidAccount;

		hwndListBox = CreateWindowEx(0, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);

		dwBufferSize = sizeof(szAccountName) / sizeof(TCHAR);
		GetUserName(szAccountName, &dwBufferSize);
		ConvertNameToSid(szAccountName, &pSidAccount);
		
		dwSubAuthorityCount = *GetSidSubAuthorityCount(pSidAccount);
		for (i = 500; i < 1500; i++) {
			*GetSidSubAuthority(pSidAccount, dwSubAuthorityCount - 1) = i;
			if (ConvertSidToName(pSidAccount, szAccountName, sizeof(szAccountName) / sizeof(TCHAR))) {
				wsprintf(szBuf, TEXT("%4d %s"), i, szAccountName);
				SendMessageW(hwndListBox, LB_ADDSTRING, 0, (LPARAM)szBuf);
			}
		}

		LocalFree(pSidAccount);

		return 0;
	}

	case WM_SIZE:
		MoveWindow(hwndListBox, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

BOOL ConvertNameToSid(LPTSTR lpszName, PSID *ppSid)
{
	TCHAR        szDomainName[256];
	DWORD        dwSizeDomain = sizeof(szDomainName) / sizeof(TCHAR);
	DWORD        dwSizeSid = 0;
	SID_NAME_USE sidName;

	LookupAccountName(NULL, lpszName, NULL, &dwSizeSid, szDomainName, &dwSizeDomain, &sidName);

	*ppSid = (PSID)LocalAlloc(LPTR, dwSizeSid);

	return LookupAccountName(NULL, lpszName, *ppSid, &dwSizeSid, szDomainName, &dwSizeDomain, &sidName);
}

BOOL ConvertSidToName(PSID pSid, LPTSTR lpszName, DWORD dwSizeName)
{
	TCHAR        szDomainName[256];
	DWORD        dwSizeDomain = sizeof(szDomainName) / sizeof(TCHAR);
	SID_NAME_USE sidName;

	return LookupAccountSid(NULL, pSid, lpszName, &dwSizeName, szDomainName, &dwSizeDomain, &sidName);
}

まず、GetUserNameとConvertNameToSidを呼び出して、現在のユーザーのSIDを取得します。 次に、GetSidSubAuthorityCountでサブ機関値の数を取得しているのは、 これに1を引いたものをRIDのインデックスとして使用するためです。 ループ文では、RIDが500から1500であるアカウントを列挙します。 通常のアカウントのRIDは1000から始まりますが、 AdministratorやGuestはRIDが500、501となっており、 これらのアカウントも列挙するため、500から走査するようにしています。 GetSidSubAuthorityにdwSubAuthorityCount - 1を指定すれば、 RIDのアドレスが返りますから、これに設定したいRIDの値を指定してLookupAccountSidを呼び出せば、 そのSIDのアカウント名を取得することができます。

LookupAccountSidの第1引数には、コンピュータ名やIPアドレスを指定することができるため、 これに注目すれば任意のコンピュータ上のアカウントを列挙することも可能になります。 この場合は、そのコンピュータ上のアカウントのSIDを事前に取得しておく必要がありますが、 これにはLookupAccountNameにビルトインアカウントの名前を指定することで可能です。 ビルトインアカウントとは、AdministratorやGuestのような既定で存在するアカウントのことであり、 こうしたアカウントは他のコンピュータ上にも存在することが予想されるため、 これを指定すればそのアカウントのSIDを取得することができます。 目的のコンピュータ上のアカウント名を把握していなくても、 このような列挙が可能になることから、 ビルトインアカウントの名前は変更されている場合もあります。


戻る