У меня есть dll-фильтр с паролем, который, по крайней мере, сейчас просто будет записывать в журнал имя пользователя и пароль при успешном изменении пароля учетной записи пользователя. Я подхожу к некоторым старым проблемам с c-струнами.
Две переменные, которые меня интересуют, имеют тип PUNICODE_STRING (который просто является * UNICODE_STRING). Естественно, я ищу в Интернете этот тип данных, который приземлился на меня здесь: http://msdn.microsoft.com/en-us/library/windows/desktop/aa380518(v=vs.85).aspx
Дайте эту информацию, я знаю, что я могу получить доступ к длине этого животного и что у него есть указатель на строку с широким символом. Потрясающие. Это достаточно знаний для извлечения содержимого, которое мне понадобится (так я думал).
Вот мой код dll (dllmain.cpp)
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include <iostream>
#define LOGFILE "c:\\PasswordFilter.txt"
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;
}
void WriteToLog (const wchar_t* UserName, const wchar_t* NewPassword)
{
#ifdef LOGFILE
FILE* log = fopen(LOGFILE, "a");
if(NULL == log)
{
return;
}
fwprintf(log,L"%s password was successfully reset to %s\r\n",UserName,NewPassword);
fclose(log);
#endif
return;
}
LPWSTR __stdcall ConvertWideChar(PUNICODE_STRING input)
{
wchar_t* wszString = new wchar_t[input->Length + 1];
memset(wszString, 0, sizeof(wchar_t) * (input->Length + 1));
wcsncpy(wszString,input->Buffer,input->Length);
#ifdef LOGFILE
FILE* log = fopen(LOGFILE, "a");
if(NULL != log)
{
fwprintf(log,L"value of wszString: %s\r\n",wszString);
fclose(log);
}
#endif
return wszString;
}
BOOLEAN __stdcall InitializeChangeNotify(void)
{
return TRUE;
}
NTSTATUS __stdcall PasswordChangeNotify(
PUNICODE_STRING UserName,
ULONG RelativeId,
PUNICODE_STRING NewPassword
)
{
LPWSTR ConvertedUserName = ConvertWideChar(UserName);
LPWSTR ConvertedNewPassword = ConvertWideChar(NewPassword);
WriteToLog(ConvertedUserName,ConvertedNewPassword);
return 0;
}
BOOLEAN __stdcall PasswordFilter(
PUNICODE_STRING AccountName,
PUNICODE_STRING FullName,
PUNICODE_STRING Password,
BOOLEAN SetOperation
)
{
return TRUE;
}
И вот содержимое файла заголовка (stdafx.h)
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <stdio.h>
#include <windows.h>
#include <winnt.h>
#include <NTSecAPI.h>
// TODO: reference additional headers your program requires here
Естественно, моя забота - сохранить точность. И здесь начинается моя боль. У нас есть wchar_t, который сообщил мне, что это, скорее всего, в unicode с форматом UTF16 (как и все остальные строки на основе Windows). Приведенный выше код копирует буфер PUNICODE_STRING в wchar_t с нулевым завершением (поскольку буфер не гарантированно завершен нулем), а затем пытается вывести эту строку в файл журнала.
Сначала я попытался использовать WideCharToMultiByte как способ скопировать буфер в строку символов, но это никогда не работало. Результат будет пустым каждый раз. Это также привело к проблеме перевода значений Юникода в более простой формат, который подвержен риску потери данных. Поэтому я решил пойти с тем, что у меня есть, но зарегистрированными значениями? и другой мусор после них, что, вероятно, является проблемой unicode.
Я бы ДЕЙСТВИТЕЛЬНО хотел бы сохранить тип кодировки Unicode этой структуры данных, если это возможно. Как это сделать и распечатать мои значения в журнале?
Вы не используете поле UNICODE_STRING::Length
правильно. Он выражается в байтах, а не в символах. Таким образом, вы не распределяете + правильно заполняете буферы с нулевым завершением. Вы также утечка буферов вы выделяете.
Вам не нужно выделять буферы вообще. Вы можете передать исходные значения Buffer
и Length
непосредственно в fwprintf
, нулевые нулевые терминаторы:
NTSTATUS __stdcall PasswordChangeNotify(
PUNICODE_STRING UserName,
ULONG RelativeId,
PUNICODE_STRING NewPassword
)
{
#ifdef LOGFILE
FILE* log = fopen(LOGFILE, "a");
if (NULL != log)
{
fwprintf(log, L"%.*s password was successfully reset to %.*s\r\n",
UserName->Length / sizeof(WCHAR), UserName->Buffer,
NewPassword->Length / sizeof(WCHAR), NewPassword->Buffer);
fclose(log);
}
#endif
return 0;
}
Если вам действительно нужно добавить нулевые терминаторы, используйте std::wstring
вместо выделения + копирования вручную:
#include <string>
NTSTATUS __stdcall PasswordChangeNotify(
PUNICODE_STRING UserName,
ULONG RelativeId,
PUNICODE_STRING NewPassword
)
{
#ifdef LOGFILE
FILE* log = fopen(LOGFILE, "a");
if (NULL != log)
{
fwprintf(log, L"%.s password was successfully reset to %.s\r\n",
std::wstring(UserName->Buffer, UserName->Length / sizeof(WCHAR)).c_str(),
std::wstring(NewPassword->Buffer, NewPassword->Length / sizeof(WCHAR)).c_str());
fclose(log);
}
#endif
return 0;
}
Фактически вы можете напечатать структуру UNICODE_STRING, используя модификатор% wZ с printf. Нет необходимости в каких-либо фантастических переводах буфера или беспокоиться о нулевых терминаторах.
UNICODE_STRING someStr;
RtlInitUnicodeString(&someStr, L"Hello");
printf("String: %wZ\n", &someStr);
Фини.
fopen()
имеет нестандартное расширениеcss=encoding
для параметраmode
чтобы разрешить чтение / запись содержимого Unicode, в противном случае используйте вместо этого_wfopen()
. Или используйтеWideCharToMultiByte()
вручную, и если это неPUNICODE_STRING
либо вы называете это неправильно (более вероятно), либо данныеPUNICODE_STRING
недопустимы (менее вероятно).