https://github.com/zk2013/windows_remote_lock_unlock_screen
將生成的DLL註冊至登錄檔 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers ,之後的每次鎖屏或開機登入都會載入這個DLL。
實現這個DLL有相關的微軟文件:
ICredentialProviderCredential 介面
ICredentialProviderCredential2 介面
以上操作只實現了替換解鎖Windows的登入介面功能,接下來是實現自動登入(自動解鎖)。
實現自動解鎖需要有通訊機制傳送接收Windows賬號密碼,接收端程式碼裡使用的是 C++的 CreateNamedPipe (命名管道)。
在 ICredentialProviderCredential 介面的現實方法 GetSerialization 內建立了一個 pipe,管道名是 \\.\pipe\NoninteractiveUnlockCredentialProvider,( \\.\管道\管道名。最多可達256個字元的長度,而且不用區分大小寫 )。
CPipeListener *pPipeListener = static_cast<CPipeListener *>(lpParameter);
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\NoninteractiveUnlockCredentialProvider");
hPipe = CreateNamedPipe(
lpszPipename,
PIPE_ACCESS_INBOUND,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
BUFSIZE, BUFSIZE,
0,
&sa);
fConnected = ConnectNamedPipe(hPipe, NULL);
fSuccess = FALSE;
cbUsername = 0;
cbPassword = 0;
fSuccess = ReadFile(hPipe, pPipeListener->_pwzUsername, ALLOCSIZE, &cbUsername, NULL);
fSuccess &= ReadFile(hPipe, pPipeListener->_pwzPassword, ALLOCSIZE, &cbPassword, NULL);
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
if (fSuccess && cbUsername && cbUsername)
{
pPipeListener->_fUnlocked = TRUE;
pPipeListener->_pProvider->OnUnlockingStatusChanged();
break;
}
現在實現傳送端,使用 C++ 的 CreateFile (這是一個多功能的函式,可開啟或建立檔案或者I/O裝置,並返回可訪問的控制程式碼:控制檯,通訊資源,目錄(只讀開啟),磁碟驅動器,檔案,郵槽,管道。)傳送賬號密碼至管道 ( \\.\pipe\NoninteractiveUnlockCredentialProvider ) 。
HANDLE hPipe;
BOOL fSuccess = FALSE;
DWORD cbToWrite, cbWritten;
// Wait pipe
if (!WaitNamedPipe(lpszPipename, 5000))
{
_tprintf(TEXT("Could not open pipe.\n"));
return;
}
// Open pipe
hPipe = CreateFile(
lpszPipename, // pipe name
GENERIC_WRITE, // write access
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
NULL); // no template file
if (hPipe == INVALID_HANDLE_VALUE)
{
_tprintf(TEXT("Could not open pipe. GLE=%d\n"), GetLastError());
return;
}
// Send username
cbToWrite = (lstrlen(lpvUsername) + 1) * sizeof(TCHAR);
fSuccess = WriteFile(
hPipe, // pipe handle
lpvUsername, // message
cbToWrite, // message length
&cbWritten, // bytes written
NULL); // not overlapped
if (!fSuccess)
{
_tprintf(TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError());
CloseHandle(hPipe);
return;
}
// Send password
cbToWrite = (lstrlen(lpvPassword) + 1) * sizeof(TCHAR);
fSuccess = WriteFile(
hPipe, // pipe handle
lpvPassword, // message
cbToWrite, // message length
&cbWritten, // bytes written
NULL); // not overlapped
if (!fSuccess)
{
_tprintf(TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError());
CloseHandle(hPipe);
return;
}
CloseHandle(hPipe);
接收端收到賬號密碼後,在 GetSerialization 方法內 透過 KerbInteractiveUnlockLogonInit 和 KerbInteractiveUnlockLogonPack 校驗登入。正確後觸發Windows登入機制並解開螢幕鎖。
HRESULT CSampleCredential::GetSerialization(
CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr,
CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs,
PWSTR* ppwszOptionalStatusText,
CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon
)
{
UNREFERENCED_PARAMETER(ppwszOptionalStatusText);
UNREFERENCED_PARAMETER(pcpsiOptionalStatusIcon);
KERB_INTERACTIVE_LOGON kil;
ZeroMemory(&kil, sizeof(kil));
HRESULT hr;
WCHAR wsz[MAX_COMPUTERNAME_LENGTH+1];
DWORD cch = ARRAYSIZE(wsz);
if (GetComputerNameW(wsz, &cch))
{
PWSTR pwzProtectedPassword;
hr = ProtectIfNecessaryAndCopyPassword(_rgFieldStrings[SFI_PASSWORD], _cpus, &pwzProtectedPassword);
if (SUCCEEDED(hr))
{
KERB_INTERACTIVE_UNLOCK_LOGON kiul;
// Initialize kiul with weak references to our credential.
hr = KerbInteractiveUnlockLogonInit(wsz, _pwzUsername, _pwzPassword, _cpus, &kiul);
if (SUCCEEDED(hr))
{
// We use KERB_INTERACTIVE_UNLOCK_LOGON in both unlock and logon scenarios. It contains a
// KERB_INTERACTIVE_LOGON to hold the creds plus a LUID that is filled in for us by Winlogon
// as necessary.
hr = KerbInteractiveUnlockLogonPack(kiul, &pcpcs->rgbSerialization, &pcpcs->cbSerialization);
if (SUCCEEDED(hr))
{
ULONG ulAuthPackage;
hr = RetrieveNegotiateAuthPackage(&ulAuthPackage);
if (SUCCEEDED(hr))
{
pcpcs->ulAuthenticationPackage = ulAuthPackage;
pcpcs->clsidCredentialProvider = CLSID_CSampleProvider;
// At this point the credential has created the serialized credential used for logon
// By setting this to CPGSR_RETURN_CREDENTIAL_FINISHED we are letting logonUI know
// that we have all the information we need and it should attempt to submit the
// serialized credential.
*pcpgsr = CPGSR_RETURN_CREDENTIAL_FINISHED;
}
}
}
CoTaskMemFree(pwzProtectedPassword);
}
}
else
{
DWORD dwErr = GetLastError();
hr = HRESULT_FROM_WIN32(dwErr);
}
return hr;
}