【问题标题】:How to detect UTF16 strings in PE files如何检测 PE 文件中的 UTF16 字符串
【发布时间】:2021-02-17 22:04:53
【问题描述】:

我需要从 PE 文件中提取 Unicode 字符串。提取时我需要先检测它。对于 UTF-8 字符,我使用了以下链接 - How to easily detect utf8 encoding in the string?。有没有类似的方法来检测 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;
    }
}

【问题讨论】:

  • 也许您正在寻找资源字符串?
  • 为什么不直接使用strings.exe
  • 循环中的第一个 if 语句始终为真,第二个 if 语句已被循环条件覆盖。 “不工作”是什么意思?你得到什么输出?你期望的输出是什么?
  • 最后,只要两个字节都为零,您的循环就会停止。这在二进制文件中很常见
  • 一个字节是一个无符号字符,所以它总是介于 0 和 0xFF 之间。作为第一步,如果您看到该两字节模式超过四次,您可能应该只将某些内容计为字符串。

标签: c++ string unicode


【解决方案1】:

我现在刚刚为你实现了一个函数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字节数。

我的函数应该非常快,因为它只包含几个 ifs(加上一些算术,以防您要求实际解码字符),总是将我的函数放入标题中,以便它内联到调用函数中以产生非常快速代码!同样只传入编译时常量check_onlybig_endian,这将通过C++ 优化去除额外的解码代码。

例如,如果您只想检测 utf-16 字节的长时间运行,那么您接下来要做的是,在调用此函数的循环中迭代,只要它第一次返回不是 -1,那么它就有可能开始,然后进一步迭代并捕获最后一个不等于-1 值,这将是文本的最后一点。同样重要在搜索 utf-16 字节时传入 bad_skip = 1,因为有效的 char 可以从任何字节开始。

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

我的功能需要两个功能 - _DecodeUtf16Char_ReadWord()(助手)加上DecodeUtf16Char()(主解码器)。我只包含一个标准头 &lt;cstdint&gt;,如果不允许包含任何内容,则只需定义 uint8_tuint16_tuint32_t,我只使用此头中的这些类型定义。

另外,作为参考,see my other post 从头开始​​(并使用标准 C++ 库)实现了 UTF-8UTF-16UTF-32!

之间的所有类型的转换

Try it online!

#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

【讨论】:

    猜你喜欢
    • 2012-07-31
    • 1970-01-01
    • 1970-01-01
    • 2017-11-23
    • 2023-04-02
    • 2016-09-04
    • 1970-01-01
    • 2012-11-04
    • 2021-12-19
    相关资源
    最近更新 更多