如何检测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字中字节的大端序(默认情况下为big_endian = true(或小端序(big_endian = false(顺序。bad_skip等于解码字符失败(utf-16无效(时要跳过的字节数,bad_value是一个用于表示utf-16未解码(无效(的值,默认情况下为-1

使用/测试示例包含在此函数定义之后。基本上,您只需将起始指针(ptr(和结束指针传递给此函数,当返回检查返回值时,如果它是-1,则指针begin处的utf-16序列无效,如果它不是-1,则此返回值包含有效的32位unicode代码点。此外,我的函数将ptr递增,在有效utf-16的情况下递增解码字节数,如果无效,则递增bad_skip字节数。

我的函数应该非常快,因为它只包含几个if(加上一点算术,以防您要求实际解码字符(,请始终将我的函数放入标头中,以便将其内联到调用函数中,以生成非常快的代码!此外,只传入编译时常数check_onlybig_endian,这将通过C++优化删除额外的解码代码。

例如,如果你只想检测utf-16字节的长时间运行,那么你要做下一件事,在调用该函数的循环中迭代,每当它第一次返回非-1时,就有可能开始,然后进一步迭代,捕获最后一个不等于-1的值,这将是文本的最后一点。此外,搜索utf-16字节时传递给bad_skip = 1也是非常重要的,因为有效字符可以从任何字节开始。

我用来测试不同的字符-英语ASCII,俄语字符(两个字节的utf-16(加上两个4字节的字符(两种utf-16单词(。我的测试将转换后的行附加到test.txt文件中,该文件是UTF-8编码的,以便通过notepad轻松查看。我的解码功能之后的所有代码都不需要它来工作,剩下的只是测试代码。

我的函数需要两个函数——_DecodeUtf16Char_ReadWord()(助手(和DecodeUtf16Char()(主解码器(。我只包含一个标准标头<cstdint>,如果不允许包含任何内容,那么只定义uint8_tuint16_tuint32_t,我只使用此标头中的这些类型定义。

此外,作为参考,请参阅我的另一篇文章,该文章从头开始(并使用标准C++库(实现了UTF-8与lt-->UTF-16&lt--&gt;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("nx00", 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

最新更新