-1

我创建了一个Credential ManagerDLL 来利用该NPLogonNotify事件。我正在一个完全修补的Windows 7 Ultimate.

当用户登录我的实现时,会NPLogonNotify使用CreateProcess. 这些进程是 Windows 应用程序,一切正常。

当我强制用户在下次登录时更改密码时,他们更改密码并且系统挂起“更改密码”。当用户更改密码时,在内部创建新进程的效果NPLoponNotify并不好。

NPLogonNotify我已经通过注释掉该导出中的所有代码并测试强制密码更改来验证它是代码。如果我将所有代码都注释掉了,那么密码更改就可以完美运行,代码会无限期地挂起。

以下是凭证管理器的导出功能

NPGetCaps

DWORD APIENTRY NPGetCaps(DWORD nIndex)
{
    DWORD ret = 0;
    switch (nIndex)
    {
    case WNNC_NET_TYPE:
        ret = WNNC_CRED_MANAGER; // credential manager 
        break;

    case WNNC_SPEC_VERSION:
        // We are using version 5.1 of the spec. 
        ret = WNNC_SPEC_VERSION51;
        break;

    case WNNC_DRIVER_VERSION:
        ret = 1; // This driver is version 1. 
        break;

    case WNNC_START:
        ret = 1; // We are already "started" 
        break;
    }

    return ret;
}

NPPasswordChangeNotify

DWORD APIENTRY NPPasswordChangeNotify(LPCWSTR lpAuthentInfoType, LPVOID lpAuthentInfo, LPCWSTR lpPreviousAuthentInfoType, LPVOID lpPreviousAuthentInfo, LPWSTR lpStationName, LPVOID StationHandle, DWORD dwChangeInfo)
{
    return WN_SUCCESS;
}

注意:上述功能对系统挂起没有影响,我尝试完全省略导出,我仍然得到相同的结果。

NPLogonNotify

DWORD APIENTRY NPLogonNotify(PLUID lpLogon, LPCWSTR lpAuthentInfoType, LPVOID lpAuthentInfo, LPCWSTR lpPreviousAuthentInfoType, LPVOID lpPreviousAuthentInfo, LPWSTR lpStationName, LPVOID StationHandle, LPWSTR *lpLogonScript)
{
    lpLogonScript = nullptr;

    //auth type can help here to know what we're doing
    if (lstrcmpi(lpAuthentInfoType, L"MSV1_0:Interactive") != 0 && lstrcmpiW(lpAuthentInfoType, L"Kerberos:Interactive"))
        return WN_SUCCESS;

    WCHAR filename[MAX_PATH];
    GetModuleFileName(g_Module, filename, MAX_PATH);
    wcsrchr(filename, L'\\')[0] = L'\0';

    WCHAR exe1Filename[MAX_PATH];
    wsprintf(exe1Filename, L"%lS\\exe1.exe", filename);

    STARTUPINFOW si = { 0 };
    PROCESS_INFORMATION pi = { 0 };
    si.cb = sizeof(STARTUPINFO);

    if (CreateProcess((LPWSTR)exe1Filename, nullptr, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi))
    {
        WaitForInputIdle(pi.hProcess, INFINITE);
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
    }

    MSV1_0_INTERACTIVE_LOGON *authInfo = (MSV1_0_INTERACTIVE_LOGON *)lpAuthentInfo;

    si = { 0 };
    pi = { 0 };
    si.cb = sizeof(STARTUPINFO);    

    ((PWSTR)(&((char *)authInfo->UserName.Buffer)[authInfo->UserName.Length]))[0] = L'\0';

    WCHAR args[(UNLEN + 14) * 2];
    wsprintf(args, L"exe2.exe %lS", authInfo->UserName.Buffer);

    WCHAR exe2Path[MAX_PATH];
    wsprintf(exe2Path, L"%lS\\exe2.exe", filename);

    if (CreateProcess((LPWSTR)exe2Path, (LPWSTR)args, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi))
    {
        WaitForSingleObject(pi.hProcess, INFINITE);
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
    }

    return WN_SUCCESS;
}

我知道上面的代码很糟糕,没有错误检查,也不安全。我这样做是为了了解更多关于凭证管理器的测试和学习练习。

有谁知道为什么里面的代码NPLogonNotify在用户登录时可以完美运行,但是当用户在登录时被迫更改密码时会完全挂起系统?

4

1 回答 1

1

我仍然不清楚为什么我的程序不会在用户密码更改时显示,但我已经设法弄清楚如何解决它。我需要找出一种方法来确定NPLogonNotify登录时调用和密码更改时调用之间的区别。

挂起的问题是因为我WaitForSingleObject在继续之前调用等待我的程序退出,所以程序正在运行,它只是被隐藏并且在密码更改期间由于某种原因而不是难以处理的。

我找不到任何文档清楚地概述了更改密码时的呼叫和登录时的呼叫之间的区别,因此我可以使用以下代码进行检查:

if (lpPreviousAuthentInfoType != NULL || lpPreviousAuthentInfo != NULL || lstrcmpi(lpStationName, L"Winsta0") != 0)
    return WN_SUCCESS;

这可能是过度的,可能不是正确的方法,但我找不到任何文档。基本上,在更改密码时,lpPreviousAuthentInfoType将填充一个字符串(根据身份验证类型而不同),如果执行正常登录,则填充一个空字符串。lpPreviousAuthentInfo将包含指向前一个用户凭据的指针,如果正常登录,则为 NULL。最后,lpStationNameWinsta0在正常登录和SvcCtl(如果我没记错的话,我没有写下来)密码更改。

于 2017-02-12T21:59:21.693 回答