Skip to content

Commit

Permalink
Fea, 为XP添加CryptBinaryToStringW(A) CRYPT_STRING_NOCRLF支持
Browse files Browse the repository at this point in the history
  • Loading branch information
mingkuang-Chuyu committed Jul 24, 2024
1 parent f5ffc8a commit 8f47d40
Show file tree
Hide file tree
Showing 8 changed files with 278 additions and 3 deletions.
1 change: 1 addition & 0 deletions ThunksList.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@
| ---- | -----------
| CryptProtectMemory | 不存在时,返回TRUE。
| CryptUnprotectMemory | 不存在时,返回TRUE。
| CryptBinaryToStringW(A) | 为Windows XP模拟 CRYPT_STRING_NOCRLF。

## d3d9.dll
| 函数 | Fallback
Expand Down
142 changes: 142 additions & 0 deletions src/Thunks/Crypt32.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,146 @@ namespace YY::Thunks
return TRUE;
}
#endif


#if (YY_Thunks_Target < __WindowsNT6)

// 最低受支持的客户端 Windows XP [桌面应用 | UWP 应用]
// 最低受支持的服务器 Windows Server 2003[桌面应用 | UWP 应用]
// CRYPT_STRING_NOCRLF 需要Windows Vista或者更高
__DEFINE_THUNK(
crypt32,
20,
BOOL,
WINAPI,
CryptBinaryToStringW,
_In_reads_bytes_(_cbBinary) CONST BYTE* _pBinary,
_In_ DWORD _cbBinary,
_In_ DWORD _fFlags,
_Out_writes_to_opt_(*_pcString, *_pcString) LPWSTR _szString,
_Inout_ DWORD* _pcString
)
{
const auto _pfnCryptBinaryToStringW = try_get_CryptBinaryToStringW();
if (!_pfnCryptBinaryToStringW)
{
// Windows 2000?
SetLastError(ERROR_FUNCTION_FAILED);
return FALSE;
}

// CRYPT_STRING_NOCRLF 需要 Windows Vista
if ((_fFlags & CRYPT_STRING_NOCRLF) && internal::GetSystemVersion() < internal::MakeVersion(6, 0))
{
_fFlags &= ~CRYPT_STRING_NOCRLF;
if (!_szString)
{
// 少了 CRYPT_STRING_NOCRLF 所需的缓冲区只可能比原版多,所以这样做是安全的。
return _pfnCryptBinaryToStringW(_pBinary, _cbBinary, _fFlags, _szString, _pcString);
}

if (!_pfnCryptBinaryToStringW(_pBinary, _cbBinary, _fFlags, _szString, _pcString))
return FALSE;

// 删除所有的 "\r\n",模拟 CRYPT_STRING_NOCRLF 行为
const auto _szStringEnd = _szString + *_pcString;
auto _szStringNew = _szString;
bool _bHasSkip = false;
for (auto _szCurrent = _szString; _szCurrent != _szStringEnd; ++_szCurrent)
{
const auto _ch = *_szCurrent;
if (_ch == L'\r' || _ch == L'\n')
{
_bHasSkip = true;
}
else
{
if (_bHasSkip)
{
*_szStringNew = *_szCurrent;
}

++_szStringNew;
}
}

*_szStringNew = L'\0';
*_pcString = _szStringNew - _szString;
return TRUE;
}

return _pfnCryptBinaryToStringW(_pBinary, _cbBinary, _fFlags, _szString, _pcString);
}
#endif


#if (YY_Thunks_Target < __WindowsNT6)

// 最低受支持的客户端 Windows XP [桌面应用 | UWP 应用]
// 最低受支持的服务器 Windows Server 2003[桌面应用 | UWP 应用]
// CRYPT_STRING_NOCRLF 需要Windows Vista或者更高
__DEFINE_THUNK(
crypt32,
20,
BOOL,
WINAPI,
CryptBinaryToStringA,
_In_reads_bytes_(_cbBinary) CONST BYTE* _pBinary,
_In_ DWORD _cbBinary,
_In_ DWORD _fFlags,
_Out_writes_to_opt_(*_pcString, *_pcString) LPSTR _szString,
_Inout_ DWORD* _pcString
)
{
const auto _pfnCryptBinaryToStringA = try_get_CryptBinaryToStringA();
if (!_pfnCryptBinaryToStringA)
{
// Windows 2000?
SetLastError(ERROR_FUNCTION_FAILED);
return FALSE;
}

// CRYPT_STRING_NOCRLF 需要 Windows Vista
if ((_fFlags & CRYPT_STRING_NOCRLF) && internal::GetSystemVersion() < internal::MakeVersion(6, 0))
{
_fFlags &= ~CRYPT_STRING_NOCRLF;
if (!_szString)
{
// 少了 CRYPT_STRING_NOCRLF 所需的缓冲区只可能比原版多,所以这样做是安全的。
return _pfnCryptBinaryToStringA(_pBinary, _cbBinary, _fFlags, _szString, _pcString);
}

if (!_pfnCryptBinaryToStringA(_pBinary, _cbBinary, _fFlags, _szString, _pcString))
return FALSE;

// 删除所有的 "\r\n",模拟 CRYPT_STRING_NOCRLF 行为
const auto _szStringEnd = _szString + *_pcString;
auto _szStringNew = _szString;
bool _bHasSkip = false;
for (auto _szCurrent = _szString; _szCurrent != _szStringEnd; ++_szCurrent)
{
const auto _ch = *_szCurrent;
if (_ch == '\r' || _ch == '\n')
{
_bHasSkip = true;
}
else
{
if (_bHasSkip)
{
*_szStringNew = *_szCurrent;
}

++_szStringNew;
}
}

*_szStringNew = L'\0';
*_pcString = _szStringNew - _szString;
return TRUE;
}

return _pfnCryptBinaryToStringA(_pBinary, _cbBinary, _fFlags, _szString, _pcString);
}
#endif
} // namespace YY::Thunks
8 changes: 8 additions & 0 deletions src/Thunks/YY_Thunks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,16 @@ namespace YY::Thunks::internal
return (_uMajorVersion << 16) | _uMinorVersion;
}

#ifdef __YY_Thunks_Unit_Test
EXTERN_C DWORD g_uSystemVersion = 0;
#endif

__forceinline DWORD __fastcall GetSystemVersion()
{
#ifdef __YY_Thunks_Unit_Test
if (g_uSystemVersion)
return g_uSystemVersion;
#endif
const auto _pPeb = ((TEB*)NtCurrentTeb())->ProcessEnvironmentBlock;
return internal::MakeVersion(_pPeb->OSMajorVersion, _pPeb->OSMinorVersion);
}
Expand Down
86 changes: 86 additions & 0 deletions src/YY-Thunks.UnitTest/Crypt32.UnitTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#include "pch.h"
#include "Thunks/Crypt32.hpp"

#include <wincrypt.h>

#pragma comment(lib, "Crypt32.lib")

namespace Crypt32
{
TEST_CLASS(CryptBinaryToStringW)
{
AwaysNullGuard Guard;

public:
CryptBinaryToStringW()
{
g_uSystemVersion = MakeVersion(5, 1);
}

~CryptBinaryToStringW()
{
g_uSystemVersion = 0;
}

TEST_METHOD(CRYPT_STRING_NOCRLF参数验证)
{
wchar_t _szUnitTestDll[512];
GetModuleFileNameW((HMODULE)&__ImageBase, _szUnitTestDll, std::size(_szUnitTestDll));

const auto _FileData = ReadFileData(_szUnitTestDll);
Assert::AreNotEqual(_FileData.size(), size_t(0));

{
DWORD _cchOut = 0;
Assert::IsTrue(::CryptBinaryToStringW((const BYTE*)_FileData.c_str(), _FileData.size(), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, nullptr, &_cchOut));

auto _szOutBuffer = new wchar_t[_cchOut];
Assert::IsNotNull(_szOutBuffer);

Assert::IsTrue(::CryptBinaryToStringW((const BYTE*)_FileData.c_str(), _FileData.size(), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, _szOutBuffer, &_cchOut));

Assert::AreEqual(size_t(_cchOut), wcslen(_szOutBuffer));

Assert::IsNull(wcschr(_szOutBuffer, L'\r'));
Assert::IsNull(wcschr(_szOutBuffer, L'\n'));

DWORD _cbBinary = 0;
Assert::IsTrue(::CryptStringToBinaryW(_szOutBuffer, _cchOut, CRYPT_STRING_BASE64, nullptr, &_cbBinary, nullptr, nullptr));
Assert::AreEqual(size_t(_cbBinary), _FileData.size());

std::string _Binary;
_Binary.resize(_cbBinary);
Assert::IsTrue(::CryptStringToBinaryW(_szOutBuffer, _cchOut, CRYPT_STRING_BASE64, (BYTE*)_Binary.data(), &_cbBinary, nullptr, nullptr));
Assert::AreEqual(_FileData, _Binary);

delete[] _szOutBuffer;
}

{
DWORD _cchOut = 0;
Assert::IsTrue(::CryptBinaryToStringA((const BYTE*)_FileData.c_str(), _FileData.size(), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, nullptr, &_cchOut));

auto _szOutBuffer = new char[_cchOut];
Assert::IsNotNull(_szOutBuffer);

Assert::IsTrue(::CryptBinaryToStringA((const BYTE*)_FileData.c_str(), _FileData.size(), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, _szOutBuffer, &_cchOut));

Assert::AreEqual(size_t(_cchOut), strlen(_szOutBuffer));

Assert::IsNull(strchr(_szOutBuffer, '\r'));
Assert::IsNull(strchr(_szOutBuffer, '\n'));

DWORD _cbBinary = 0;
Assert::IsTrue(::CryptStringToBinaryA(_szOutBuffer, _cchOut, CRYPT_STRING_BASE64, nullptr, &_cbBinary, nullptr, nullptr));
Assert::AreEqual(size_t(_cbBinary), _FileData.size());

std::string _Binary;
_Binary.resize(_cbBinary);
Assert::IsTrue(::CryptStringToBinaryA(_szOutBuffer, _cchOut, CRYPT_STRING_BASE64, (BYTE*)_Binary.data(), &_cbBinary, nullptr, nullptr));
Assert::AreEqual(_FileData, _Binary);

delete[] _szOutBuffer;
}
}
};
}
1 change: 1 addition & 0 deletions src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@
<ClCompile Include="api-ms-win-core-winrt-string.UnitTest.cpp" />
<ClCompile Include="api-ms-win-power-base.UnitTest.cpp" />
<ClCompile Include="bcrypt.UnitTest.cpp" />
<ClCompile Include="Crypt32.UnitTest.cpp" />
<ClCompile Include="Iphlpapi.UnitTest.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
Expand Down
3 changes: 3 additions & 0 deletions src/YY-Thunks.UnitTest/YY-Thunks.UnitTest.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@
<ClCompile Include="User32.UnitTest.cpp">
<Filter>源文件\UnitTest</Filter>
</ClCompile>
<ClCompile Include="Crypt32.UnitTest.cpp">
<Filter>源文件\UnitTest</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
Expand Down
27 changes: 26 additions & 1 deletion src/YY-Thunks.UnitTest/pch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,29 @@

#include "pch.h"

// 当使用预编译的头时,需要使用此源文件,编译才能成功。
// 当使用预编译的头时,需要使用此源文件,编译才能成功。
std::string ReadFileData(LPCWSTR _szFilePath)
{
std::string _FileData;
auto _hFile = CreateFileW(_szFilePath, GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (_hFile == INVALID_HANDLE_VALUE)
return _FileData;

LARGE_INTEGER _FileSize;
if (GetFileSizeEx(_hFile, &_FileSize))
{
if (_FileSize.HighPart)
{
// 不支持超过4GB的文件
return _FileData;
}

_FileData.resize(_FileSize.LowPart);

if (!ReadFile(_hFile, _FileData.data(), _FileSize.LowPart, &_FileSize.LowPart, nullptr))
_FileData.clear();
}

CloseHandle(_hFile);
return _FileData;
}
13 changes: 11 additions & 2 deletions src/YY-Thunks.UnitTest/pch.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@

#include <SharedDefs.h>

EXTERN_C DWORD g_uSystemVersion;

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

#define __DEFINE_THUNK(_MODULE, _SIZE, _RETURN_, _CONVENTION_, _FUNCTION, ...) \
extern bool _CRT_CONCATENATE(aways_null_try_get_, _FUNCTION); \
EXTERN_C _RETURN_ _CONVENTION_ _FUNCTION(__VA_ARGS__); \
__if_not_exists(_FUNCTION)
EXTERN_C _RETURN_ _CONVENTION_ _FUNCTION(__VA_ARGS__); \
__if_not_exists(_FUNCTION)

//#undef TEST_METHOD
//
Expand Down Expand Up @@ -109,6 +111,13 @@ inline std::string ToHexString(const BYTE (&_Data)[kDataLength])
return ToHexString(_Data, kDataLength);
}

inline constexpr DWORD __fastcall MakeVersion(_In_ DWORD _uMajorVersion, _In_ DWORD _uMinorVersion)
{
return (_uMajorVersion << 16) | _uMinorVersion;
}

std::string ReadFileData(LPCWSTR _szFilePath);

namespace Microsoft::VisualStudio::CppUnitTestFramework
{
template<>
Expand Down

0 comments on commit 8f47d40

Please sign in to comment.