访问冲突读取位置、PUNICODE_STRING 和 _snwprintf_s

问题描述 投票:0回答:1

我编写了一个密码过滤器 DLL 来拦截密码更改并将其传递给 PowerShell,以便它可以更新我们的其他系统……但这不起作用。但因为它是一个 DLL,所以我无法准确地单步执行代码来了解“为什么”它不起作用。因此,我决定编写一个小应用程序来设置一些变量,然后调用我在 DLL 中使用的相同 PasswordChangeNotify() 函数的副本。 这帮助我解决了很多问题,但仍有一个问题没有解决。每当我尝试运行实际生成 PowerShell 命令行的行时,都会收到此错误:

TestDLLOutput2.exe 中的 0x00007FFC024113FC (ucrtbased.dll) 抛出异常:0xC0000005:读取位置 0xFFFFFFFFFFFFFFFF 时发生访问冲突。

我正在 Visual Studio 2022 中工作,这是我的代码:

#include <iostream> #include <Windows.h> #include <stdio.h> #include <stdlib.h> #include <winternl.h> #include <string.h> using namespace std; NTSTATUS __stdcall PasswordChangeNotify(PUNICODE_STRING UserName, ULONG RelativeId, PUNICODE_STRING NewPassword); int main() { long IDNum = 123456; UNICODE_STRING SupUserName; RtlInitUnicodeString(&SupUserName, L"cyousoon"); UNICODE_STRING SupPasWrd; RtlInitUnicodeString(&SupPasWrd, L"FunkyChicken"); NTSTATUS ReturnNum = 0; ReturnNum = PasswordChangeNotify(&SupUserName, IDNum, &SupPasWrd); } NTSTATUS __stdcall PasswordChangeNotify(PUNICODE_STRING UserName, ULONG RelativeId, PUNICODE_STRING NewPassword) { std::wcout << L"Running PasswordChangeNotify\n"; // Initialize pszCmdLine to nullptr PWSTR pszCmdLine = nullptr; int lenConstant = wcslen(L"powershell.exe SD60PWS.ps1 -Section 'ChangeNotify' -UserName -NewPassword "); int lenUsername = wcslen(UserName->Buffer); int lenNewPassword = wcslen(NewPassword->Buffer); int totalLen = lenConstant + lenUsername + lenNewPassword; // Calculate the required length for the command line //int totalLen = _snwprintf_s(nullptr, 0, 0, // L"powershell.exe SD60PWS.ps1 -Section 'ChangeNotify' -UserName @%wZ -NewPassword @%wZ", // UserName->Buffer, NewPassword->Buffer); if (totalLen > 0) { std::wcout << L"len > 0\n"; // Allocate memory for pszCmdLine pszCmdLine = (PWSTR)alloca((totalLen + 1) * sizeof(WCHAR)); // Format the command line _snwprintf_s(pszCmdLine, totalLen + 1, _TRUNCATE, L"powershell.exe SD60PWS.ps1 -Section 'ChangeNotify' -UserName @%wZ -NewPassword @%wZ", UserName->Buffer, NewPassword->Buffer); std::wcout << pszCmdLine << std::endl; // Create the process STARTUPINFO si{ sizeof(si) }; PROCESS_INFORMATION pi; if (CreateProcessW(0, pszCmdLine, 0, 0, 0, 0, 0, 0, &si, &pi)) { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } } return 0; }

如果我让它运行到 
_snwprintf_s

命令之前(使用 Run To Cursor),一切看起来都正常,这是变量的值: (请注意,这实际上不是我编写的代码,而是在执行暂停后将鼠标悬停在变量上复制粘贴信息。它只是在代码块中以满足问题编辑器的要求。)

UserName = 0x00000069bd4ff888 {Length=16 MaximumLength=18 Buffer=0x00007ff7a5c8aea8 L"cyousoon" }
NewPassword = 0x00000069bd4ff8b8 {Length=24 MaximumLength=26 Buffer=0x00007ff7a5c8aec0 L"FunkyChicken" }
lenConstant = 75
lenUsername = 8
lenNewPassword = 12
totalLen = 95
pszCmdLine = 0x00000069bd4ff500 L"(98 Unicode characters, removed so this question wouldn't get marked as Spam)"

起初我认为 
alloca

命令可能没有分配正确的内存量。所以我开始研究它,最终陷入了“如果在函数中调用,

alloca
是可以的,因为当你
return
时,内存会自动释放”和“永远不要使用
alloca
!使用
malloc
free
代替。”然后“永远不要使用
malloc
/
free
!您需要使用
new
/
delete
。”
因此,作为测试,我将代码简化为:

NTSTATUS __stdcall PasswordChangeNotify(PUNICODE_STRING UserName, ULONG RelativeId, PUNICODE_STRING NewPassword) { PWSTR pszCmdLine = nullptr; int totalLen = wcslen(L"powershell.exe SD60PWS.ps1 -Section 'ChangeNotify' -UserName cyousoon -NewPassword FunkyChicken"); if (totalLen > 0) { pszCmdLine = (PWSTR)alloca((totalLen + 1) * sizeof(WCHAR)); _snwprintf_s(pszCmdLine, totalLen + 1, _TRUNCATE, L"powershell.exe SD60PWS.ps1 -Section 'ChangeNotify' -UserName cyousoon -NewPassword FunkyChicken"); STARTUPINFO si{ sizeof(si) }; PROCESS_INFORMATION pi; if (CreateProcessW(0, pszCmdLine, 0, 0, 0, 0, 0, 0, &si, &pi)) { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } } return 0; }

这段代码工作得很好,但后来我尝试添加其中一个变量(分别尝试 UserName 和 NewPassword),无论我尝试哪一个,它们都会导致相同的错误。 (嗯,不完全一样,“抛出”的数字略有变化,但其余的错误是相同的)

好的,现在我知道错误与访问传递变量的内存有关,但我不知道问题是什么。 main() 中的变量是否未正确声明?它们传递给PasswordChangeNotify()的方式是否有问题? alloca真的没有正确分配内存吗?

_snwprintf_s

命令有问题吗?

我非常感谢各位专家给我的任何帮助。我觉得我已经“非常接近”让这项工作正常进行,但我只需要更多指导。
补充说明:

在我最初的DLL代码中,我将RelativeId传递给PowerShell脚本,只是为了看看它是什么。然后我决定放弃它,因为我知道它无论如何都与 PowerShell 脚本无关。但是,我仍然在 main() 中设置 IDNum 并将其传递给 PasswordChangeNotify(),因为我想尽可能模拟 Windows 的行为。 另外,我留下了我最初用来计算总长度的注释代码,以供参考。它总是给我一个长度 0,所以我转而计算各个长度并将它们相加。

谢谢你

是的!谢谢雷米!

我不知道为什么在我的所有研究中都没有出现这一点。我可能应该以前尝试过这个,只要看看它做了什么。这是正确的命令:

c++ memory
1个回答
0
投票

另外,我想知道为什么在我从

我问的另一个问题

得到的代码中,格式字符串前面有一个@。但我认为这是与 C++ 相关的东西,我只是不知道,但现在我发现我不需要它们。

再次感谢您的帮助,我希望这篇文章有一天能对其他人有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.