c++ - 如何检测 PE 文件中的 UTF16 字符串
问题描述
我需要从 PE 文件中提取 Unicode 字符串。提取时我需要先检测它。对于 UTF-8 字符,我使用了以下链接 -如何轻松检测字符串中的 utf8 编码?. 有没有类似的方法来检测 UTF-16 字符。我已经尝试了以下代码。这是正确的吗?请提供帮助或提供建议。提前致谢!!!
BYTE temp1 = buf[offset];
BYTE temp2 = buf[offset+1];
while (!(temp1 == 0x00 && temp2 == 0x00) && offset <= bufSize)
{
if ((temp1 >= 0x00 && temp1 <= 0xFF) && (temp2 >= 0x00 && temp2 <= 0xFF))
{
tmp += 2;
}
else
{
break;
}
offset += 2;
temp1 = buf[offset];
temp2 = buf[offset+1];
if (temp1 == 0x00 && temp2 == 0x00)
{
break;
}
}
解决方案
我现在刚刚为您实现了一个函数DecodeUtf16Char()
,基本上它能够做两件事 - 要么检查它是否是有效的 utf-16(何时check_only = true
),要么检查并返回有效的解码 Unicode 代码点(32 位)。它还支持两字节 utf-16 字中的大端(默认,when big_endian = true
)或小端( )字节顺序。等于如果未能解码字符(无效 utf-16)则要跳过的字节数,是一个用于表示 utf-16 未解码(无效)的值,默认为.big_endian = false
bad_skip
bad_value
-1
此函数定义后包含使用/测试示例。基本上你只需将开始(ptr
)和结束指针传递给这个函数,当返回检查返回值时,如果它-1
在指针处begin
是无效的 utf-16 序列,如果不是-1
那么这个返回值包含有效的 32 位 unicode 代码点. 我的函数也会增加ptr
,在有效 utf-16 的情况下按解码字节bad_skip
数增加,如果无效则按字节数增加。
我的函数应该非常快,因为它只包含几个 ifs(加上一些算术,以防您要求实际解码字符),总是将我的函数放入标题中,以便将其内联到调用函数中以生成非常快速的代码!也只传入 compile-time-constantscheck_only
和big_endian
,这将通过 C++ 优化删除额外的解码代码。
例如,如果您只想检测 utf-16 字节的长时间运行,那么您接下来要做的是,在循环中迭代调用此函数,只要它第一次返回 not -1
,那么它就有可能开始,然后进一步迭代并捕获最后一个不等于--1
值,这将是文本的最后一点。在搜索 utf-16 字节时传入也很重要,因为有效的 char 可以从任何字节开始。bad_skip = 1
我用于测试不同的字符——英文 ASCII、俄文字符(两字节 utf-16)加上两个 4 字节字符(两个 utf-16 字)。我的测试将转换后的行附加到test.txt
文件中,这个文件是 UTF-8 编码的,以便于查看,例如notepad
. 我的解码功能之后的所有代码都不需要它来工作,剩下的只是测试代码。
我的工作功能需要两个功能 - _DecodeUtf16Char_ReadWord()
(助手)加上DecodeUtf16Char()
(主解码器)。我只包含一个标准标头<cstdint>
,如果您不允许包含任何内容,则只需定义uint8_t
and uint16_t
,uint32_t
我仅使用此标头中的这些类型定义。
另外,作为参考,请参阅我的另一篇文章,它从头开始(并使用标准 C++ 库)实现了 UTF-8<-->UTF-16<-->UTF-32 之间的所有类型的转换!
#include <cstdint>
static inline bool _DecodeUtf16Char_ReadWord(
uint8_t const * & ptrc, uint8_t const * end,
uint16_t & r, bool const big_endian
) {
if (ptrc + 1 >= end) {
// No data left.
if (ptrc < end)
++ptrc;
return false;
}
if (big_endian) {
r = uint16_t(*ptrc) << 8; ++ptrc;
r |= uint16_t(*ptrc) ; ++ptrc;
} else {
r = uint16_t(*ptrc) ; ++ptrc;
r |= uint16_t(*ptrc) << 8; ++ptrc;
}
return true;
}
static inline uint32_t DecodeUtf16Char(
uint8_t const * & ptr, uint8_t const * end,
bool const check_only = true, bool const big_endian = true,
uint32_t const bad_skip = 1, uint32_t const bad_value = -1
) {
auto ptrs = ptr, ptrc = ptr;
uint32_t c = 0;
uint16_t v = 0;
if (!_DecodeUtf16Char_ReadWord(ptrc, end, v, big_endian)) {
// No data left.
c = bad_value;
} else if (v < 0xD800 || v > 0xDFFF) {
// Correct single-word symbol.
if (!check_only)
c = v;
} else if (v >= 0xDC00) {
// Unallowed UTF-16 sequence!
c = bad_value;
} else { // Possibly double-word sequence.
if (!check_only)
c = (v & 0x3FF) << 10;
if (!_DecodeUtf16Char_ReadWord(ptrc, end, v, big_endian)) {
// No data left.
c = bad_value;
} else if ((v < 0xDC00) || (v > 0xDFFF)) {
// Unallowed UTF-16 sequence!
c = bad_value;
} else {
// Correct double-word symbol
if (!check_only) {
c |= v & 0x3FF;
c += 0x10000;
}
}
}
if (c == bad_value)
ptr = ptrs + bad_skip; // Skip bytes.
else
ptr = ptrc; // Skip all eaten bytes.
return c;
}
// --------- Next code only for testing only and is not needed for decoding ------------
#include <iostream>
#include <string>
#include <codecvt>
#include <fstream>
#include <locale>
static std::u32string DecodeUtf16Bytes(uint8_t const * ptr, uint8_t const * end) {
std::u32string res;
while (true) {
if (ptr >= end)
break;
uint32_t c = DecodeUtf16Char(ptr, end, false, false, 2);
if (c != -1)
res.append(1, c);
}
return res;
}
#if (!_DLL) && (_MSC_VER >= 1900 /* VS 2015*/) && (_MSC_VER <= 1914 /* VS 2017 */)
std::locale::id std::codecvt<char16_t, char, _Mbstatet>::id;
std::locale::id std::codecvt<char32_t, char, _Mbstatet>::id;
#endif
template <typename CharT = char>
static std::basic_string<CharT> U32ToU8(std::u32string const & s) {
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> utf_8_32_conv;
auto res = utf_8_32_conv.to_bytes(s.c_str(), s.c_str() + s.length());
return res;
}
template <typename WCharT = wchar_t>
static std::basic_string<WCharT> U32ToU16(std::u32string const & s) {
std::wstring_convert<std::codecvt_utf16<char32_t, 0x10ffffUL, std::little_endian>, char32_t> utf_16_32_conv;
auto res = utf_16_32_conv.to_bytes(s.c_str(), s.c_str() + s.length());
return std::basic_string<WCharT>((WCharT*)(res.c_str()), (WCharT*)(res.c_str() + res.length()));
}
template <typename StrT>
void OutputString(StrT const & s) {
std::ofstream f("test.txt", std::ios::binary | std::ios::app);
f.write((char*)s.c_str(), size_t((uint8_t*)(s.c_str() + s.length()) - (uint8_t*)s.c_str()));
f.write("\n\x00", sizeof(s.c_str()[0]));
}
int main() {
std::u16string a = u"привет|мир|hello||world||again|русский|english";
*((uint8_t*)(a.data() + 12) + 1) = 0xDD; // Introduce bad utf-16 byte.
// Also truncate by 1 byte ("... - 1" in next line).
OutputString(U32ToU8(DecodeUtf16Bytes((uint8_t*)a.c_str(), (uint8_t*)(a.c_str() + a.length()) - 1)));
return 0;
}
输出:
привет|мир|hllo||world||again|русский|englis
推荐阅读
- c# - SQL 如何替换一个值
- python - 使用 python 和请求,如何进行正确的 POST 调用以及在哪里找到标头?
- android - FirebaseAppDistribution:appDistributionUpload gradle 命令中缺少应用程序 ID
- mysql - 跨列和行计算值的实例
- javascript - 使用 redux 钩子时使用 redux 操作的最佳选择是什么?
- sql - Impala 2.11:AnalysisException:选择列表中不支持子查询
- swift - 在 SwiftUI 中获取当前的经纬度
- c++ - 是否可以禁止一个类被动态转换为?
- git - Git - 将发布分支合并到主分支
- tensorflow - 任何想法如何解决激活函数的问题?