Windows屏幕解锁服务原理及实现(1)

https://github.com/zk2013/windows_remote_lock_unlock_screen

 

将生成的DLL注册至注册表 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers ,之后的每次锁屏或开机登录都会加载这个DLL。

实现这个DLL有相关的微软文档:

ICredentialProvider 接口

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;
}

posted @ 2023-05-06 09:10  路小路的日常  阅读(933)  评论(0编辑  收藏  举报