C++ 将 PUNICODE_STRING 转换为 char

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

为什么 C++ 中的字符串如此复杂?!?!

好吧,让我们先回顾一下,我会解释一下发生了什么。

我正在将我们用于帐户等的所有脚本从 UMRA(这将像 Dodo 那样)转移到 PowerShell。我必须找到一种方法来捕获密码更改,以便将它们同步到其他系统,当我发现我可以编写自己的密码过滤器 DLL 来实现这一目的时,我很高兴。

现在,我已经尝试过多种编程语言,而且我确信 C++ 就在其中,所以尽管我使用 C++ 已经有了,但我想,在来自Google,我应该能够编写一些东西来触发 PowerShell 脚本来处理剩下的事情。

当我查找触发 PowerShell 脚本的最佳方法时,我找到了 system() 命令。看起来很简单。我看到它需要一个 char 字符串,并且 DLL 正在获取 PUNICODE_STRING,但将一个字符串转换为另一个字符串应该不成问题......对吗?

所以我决定从一个非常简单的 DLL 开始,它只是将它得到的所有内容作为命令行参数传递给一个简单的 PowerShell 脚本(我使用 CMD 进行了测试以确保它可以工作)。然后,一旦我开始工作,我计划添加一些实际的密码过滤,并可能在将密码传递给 PS 之前对密码进行加密(以防 system() 命令记录在某处)。

那是两周前的事了,我什至不知道如何将 PUNICODE_STRING 转换为 system() 期望的 char 类型。我尝试过很多事情,但我无法全部记住。昨天我花了一些时间重新尝试了一些我记得的,所以我可以在这里记录它们。

我正在 Windows Server 2016 上的 Visual Studio 2022 中工作。

在第一个代码示例中,我展示了整个代码,但在后续示例中,我注释掉了 DllMain、InitializeChangeNotify 和 PasswordFilter 以节省本文空间。

我还想指出,我还没有尝试编译任何东西,我正在尝试让代码达到调试器首先满意的程度。因此,我已将调试器错误作为注释包含在触发它们的行上。

好吧,我的第一次尝试是将所有文本和变量打包到 system() 命令中...这不起作用,所以我开始尝试创建一个可以传递给 system() 的 char 变量。

#include "pch.h"
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <SubAuth.h>
#include <format>
#include <iostream>
#include <cstring>
#include <winternl.h>
#include <string.h>

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

BOOLEAN __stdcall InitializeChangeNotify(void)
{
    return TRUE;
}

NTSTATUS __stdcall PasswordChangeNotify(PUNICODE_STRING UserName, ULONG RelativeId, PUNICODE_STRING NewPassword)
{

    char PSCommand[] = "start powershell.exe SD60PWS.ps1 -Section 'ChangeNotify' -UserName ";
    strcat(PSCommand, UserName); //Error - argument of type "PUNICODE_STRING" is incompatible with parameter of type "const char *"
    //strcat(PSCommand, " -RelativeId ");
    //strcat(PSCommand, RelativeId);
    //strcat(PSCommand, " -NewPassword ");
    //strcat(PSCommand, NewPassword);
    system(PSCommand);
    return 0;
}

BOOLEAN __stdcall PasswordFilter(PUNICODE_STRING AccountName, PUNICODE_STRING FullName, PUNICODE_STRING Password, BOOLEAN SetOperation)
{
    return 1;
}

所以我开始谷歌搜索并找到了使用 RtlUnicodeStringToAnsiString() 进行转换的参考。当然他们没有说如何所以我不得不做更多的搜索并最终想出这个......

#include "pch.h"
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <SubAuth.h>
#include <format>
#include <iostream>
#include <cstring>
#include <winternl.h>
#include <string.h>

//DllMain and InitializeChangeNotify

NTSTATUS __stdcall PasswordChangeNotify(PUNICODE_STRING UserName, ULONG RelativeId, PUNICODE_STRING NewPassword)
{
    ANSI_STRING ConvUserName;
    RtlUnicodeStringToAnsiString(&ConvUserName, UserName, TRUE);
    //Note: the example I found had an & in front of both variables, but the debugger didn't like that
    // So I took them both away, and it didn't like that either. But the debugger was OK with this.

    char PSCommand[] = "start powershell.exe SD60PWS.ps1 -Section 'ChangeNotify' -UserName ";
    strcat(PSCommand, ConvUserName); //Error - no suitable conversion function from "ANSI_STRING" to "const char *"
    //strcat(PSCommand, " -RelativeId ");
    //strcat(PSCommand, RelativeId);
    //strcat(PSCommand, " -NewPassword ");
    //strcat(PSCommand, NewPassword);
    system(PSCommand);
    return 0;
}

//PasswordFilter

在互联网上闲逛时,我发现了 Michael Dunn 撰写的优秀博客“C++ 字符串完整指南,第一部分第二部分”。内容非常丰富……但也融化了我的大脑。 我在其中发现我认为应该尝试的一件事是 ATL 转换宏。

#include "pch.h"
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <SubAuth.h>
#include <format>
#include <iostream>
#include <cstring>
#include <winternl.h>
#include <string.h>
#include <atlconv.h>

//DllMain and InitializeChangeNotify

NTSTATUS __stdcall PasswordChangeNotify(PUNICODE_STRING UserName, ULONG RelativeId, PUNICODE_STRING NewPassword)
{
    USES_CONVERSION;
    using std::string;

    string ConvUserName;
    ConvUserName = W2CA(UserName); //Error - a value of type "PUNICODE_STRING" cannot be assigned to an entity of type "LPCWSTR"

    char PSCommand[] = "start powershell.exe SD60PWS.ps1 -Section 'ChangeNotify' -UserName ";
    strcat(PSCommand, ConvUserName); //Error - no suitable conversion function from "std::string" to "const char *" exists
    //strcat(PSCommand, " -RelativeId ");
    //strcat(PSCommand, RelativeId);
    //strcat(PSCommand, " -NewPassword ");
    //strcat(PSCommand, NewPassword);
    system(PSCommand);
    return 0;
}

//PasswordFilter

之后,我回到了微软的帖子如何:在各种字符串类型之间进行转换。我想我已经学到了足够多的东西,可以尝试他们的建议。我认为我完全正确地复制(并个性化)了它,也许我错过了一些东西?

#include "pch.h"
#include "atlbase.h"
#include "atlstr.h"
#include "comutil.h"
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <SubAuth.h>
#include <format>
#include <iostream>
#include <cstring>
#include <winternl.h>
#include <string.h>
#include <atlconv.h>

using namespace std;
using namespace System; //Error - name must be a namespace name

//DllMain and InitializeChangeNotify

NTSTATUS __stdcall PasswordChangeNotify(PUNICODE_STRING UserName, ULONG RelativeId, PUNICODE_STRING NewPassword)
{
    
    size_t origsize = wcslen(UserName) + 1; //Error - argument of type "PUNICODE_STRING" is incompatible with parameter of type "const wchar_t *"
    size_t convertedChars = 0;
    char strConcat[] = " (char *)";
    size_t strConcatsize = (strlen(strConcat) + 1) * 2;
    const size_t newsize = origsize * 2;
    char* ConvUserName = new char[newsize + strConcatsize];
    wcstombs_s(&convertedChars, ConvUserName, newsize, UserName, _TRUNCATE); //Error - no instance of overloaded function "wcstombs_s" matches the argument list
    _mbscat_s((unsigned char*)ConvUserName, newsize + strConcatsize, (unsigned char*)strConcat);

    char PSCommand[] = "start powershell.exe SD60PWS.ps1 -Section 'ChangeNotify' -UserName ";
    strcat(PSCommand, ConvUserName);
    //strcat(PSCommand, " -RelativeId ");
    //strcat(PSCommand, RelativeId);
    //strcat(PSCommand, " -NewPassword ");
    //strcat(PSCommand, NewPassword);
    system(PSCommand);
    return 0;
}

//PasswordFilter

最后,在专门搜索 Stack Overflow 时(在问这个问题之前),我遇到了这篇文章,这并不是我正在寻找的完全,但他们将 PUNICODE_STRING 传递给一个将其作为 char* 接受的函数多变的。所以我想我应该尝试一下。

#include "pch.h"
#include "atlbase.h"
#include "atlstr.h"
#include "comutil.h"
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <SubAuth.h>
#include <format>
#include <iostream>
#include <cstring>
#include <winternl.h>
#include <string.h>
#include <atlconv.h>

using namespace std;

//DllMain and InitializeChangeNotify

NTSTATUS __stdcall PasswordChangeNotify(PUNICODE_STRING UserName, ULONG RelativeId, PUNICODE_STRING NewPassword)
{
    WriteToPowerShell("ChangeNotify", UserName, NewPassword); //Error - argument of type "PUNICODE_STRING" is incompatible with parameter of type "const char *"
    
    return 0;
}

//PasswordFilter

void WriteToPowerShell(const char* PSSection, const char* PSUserName, const char* PSPassWord)
{
    char PSCommand[] = "start powershell.exe SD60PWS.ps1 -Section ";
    strcat(PSCommand, PSSection);
    strcat(PSCommand, " -UserName ");
    strcat(PSCommand, PSUserName);
    strcat(PSCommand, " -NewPassword ");
    strcat(PSCommand, PSPassWord);
    system(PSCommand);
}

最后一点。我知道我可能有很多我不需要的 #includes 之类的东西。那是因为每次我尝试某些东西时,我都会添加该测试所需的任何内容,但之后我并没有费心删除它们。我打算稍后将它们一一注释掉,并丢弃不会导致错误的那些,并取消注释那些会导致错误的。

我希望有人能提供帮助。我希望所有这些失败不仅仅是因为我不知道我需要 #include <this> 或使用命名空间 that

或者,如果有其他方法来触发接受 PUNICODE_STRING 的 PowerShell 脚本,那也会很有帮助。

谢谢你。

c++ string dll
1个回答
0
投票

我什至不知道如何将 PUNICODE_STRING 转换为 system() 期望的 char 类型。

您可以使用 Win32

wchar_t*
函数将
char*
字符串转换为
WideCharToMultiByte()
字符串。或者,您可以将
wchar_t*
字符串分配给
std::wstring
,然后使用(已弃用的)
std::wstring_convert
类,或等效的第 3 方库,例如 ICU。

或者,如果有其他方法来触发接受 PUNICODE_STRING 的 PowerShell 脚本,那也会很有帮助。

根本不用使用

system()
,您可以简单地使用 Win32
CreateProcessW()
(这是
system()
内部调用的)。
system()
仅运行
cmd.exe /C <command>
,您可以使用
CreateProcessW()
字符串通过
wchar_t*
来完成此操作。

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