2017-02-11 65 views
-1

我创建了Credential Manager DLL以利用NPLogonNotify事件。我正在对Windows 7 Ultimate进行完全修补的实例进行测试。凭证管理器DLL在强制更改密码时导致登录挂起

当用户登录我的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; 
} 

注:以上功​​能对系统无影响H老化,我已经尝试完全没有出口,我仍然得到相同的结果。

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

我知道,上面的代码是非常糟糕的,没有错误检查,也不安全。我正在做这个测试和学习练习来了解更多关于Credential Managers的信息。

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

+0

挂在'WaitForInputIdle'或'WaitForSingleObject(pi.hProcess,INFINITE);'我只能评论这个和测试。如果真的挂在这里(当你不等待时不挂机) - 在你单独的进程中执行任务。我在之前(在当前winlogon桌面上)启动调试器并在其下查看 – RbMm

+0

@RbMm好点,我将把它们拿出来看看。我认为它甚至没有达到这一点,因为屏幕上没有显示任何内容,但它可能正在显示,但是显示为其他内容或者没有正常显示,这会导致它出现冻结。我会用结果更新问题 – vane

+0

您是否尝试过...调试? – conio

回答

1

我仍然不清楚为什么我的程序不会显示在用户密码更改上,但我设法弄清楚如何解决它。我需要找出一种方法来确定登录时拨打NPLogonNotify的呼叫与密码更改时的呼叫之间的差异。

挂起的问题是因为我打电话WaitForSingleObject等待我的程序退出,然后继续运行程序,因为密码更改期间由于某种原因它只是隐藏起来并且难以处理。

有没有文件,我可以发现,所以我可以检查与下面的代码登录时更改密码和通话时明确列出的通话之间的区别:

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

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