为什么Win32 API函数CredEnumerate()如果我被模仿,则返回ERROR_NOT_FOUND?

我写了一些示例代码,当我在普通用户帐户的上下文中从Windows命令提示符调用时,使用CredEnumerate()转储所有用户保存的凭据。 但是,我真的希望能够从SYSTEM用户上下文中执行此操作,因此我已经从SYSTEM cmd提示符测试了我的程序。

当我以SYSTEM身份运行我的程序时,我像这样运行LogonUser:

bLoggedOn = LogonUser(userName.c_str(), domain.c_str(), password.c_str(), LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &userToken_); 

然后我在令牌上运行ImpersonateLoggedOnUser()以给我本地用户的安全上下文。 在此之后我做:

 bOk = CredEnumerate(NULL, 0, &count, &pCredentials); 

而且我希望这会以同样的方式返回凭据,就像我没有从系统中进入并冒充身份一样。 任何人都可以发现任何我错过的东西,真正把自己置于用户的背景中吗?

我想我自己应该回答这个问题,因为我现在已经花了很多时间研究如何做到这一点,我不确定它是众所周知的。 CredEnumerate / CredRead永远不会提供域密码的密码信息,无论你在哪个进程上下文或你有什么令牌,尽管它似乎在MSDN上提示。 访问保存的凭据信息的唯一方法是使用lsasrv.dll中的未记录的函数LSAICryptUnprotectData()来执行此操作。 这可以解密您在%APPDATA%\ Microsoft \ Credentials中找到的文件,并且可以为CredEnumerate提供相同的数据结构,但填写密码除外。唯一的问题是必须在lsass.exe的进程上下文中完成此操作。 Windows安全子系统),没有设置privilsges等足以给正常进程提供执行此操作的权限。 如果您是黑客,可以通过执行CreateRemoteThread()将线程注入lsass.exe,或者如果您尝试以合法方式执行此操作,即以某种方式扩展Windows操作系统,则可以执行此操作对于第三方应用程序,就像我一样,你可以通过创建一个lsass将加载的Windows身份validation包来实现。 然后,此AP可以使用命名管道或某种此类方法来允许与其余代码进行交互。