首页 > 解决方案 > 如何检测 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;
    }
}

标签: c++stringunicode

解决方案


我现在刚刚为您实现了一个函数DecodeUtf16Char(),基本上它能够做两件事 - 要么检查它是否是有效的 utf-16(何时check_only = true),要么检查并返回有效的解码 Unicode 代码点(32 位)。它还支持两字节 utf-16 字中的大端(默认,when big_endian = true)或小端( )字节顺序。等于如果未能解码字符(无效 utf-16)则要跳过的字节数,是一个用于表示 utf-16 未解码(无效)的值,默认为.big_endian = falsebad_skipbad_value-1

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

我的函数应该非常快,因为它只包含几个 ifs(加上一些算术,以防您要求实际解码字符),总是将我的函数放入标题中,以便将其内联到调用函数中以生成非常快速的代码!也只传入 compile-time-constantscheck_onlybig_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_tand uint16_tuint32_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

推荐阅读