【问题标题】:Automatically truncate and null terminate a string buffer in a buffer overflow在缓冲区溢出时自动截断和空终止字符串缓冲区
【发布时间】:2017-08-09 11:04:28
【问题描述】:

我有以下代码,它可以从指定父窗口的所有子窗口加载文本。它工作正常,但有时,有一些父窗口(例如打开的记事本带有很长的 C++ 源文件)有大量文本并导致缓冲区溢出。

BOOL CALLBACK EnumChildProc(__in HWND hWnd, __in LPARAM lParam) {

    LRESULT TEXT_LENGTH = NULL;
    WCHAR szText[32767];
    LPWSTR szWindowText;
    UINT nBuffer = NULL, nText = NULL;

    szWindowText = reinterpret_cast<LPWSTR>(lParam); szText[0] = L'\0';
    nBuffer = (UINT)wcslen(szWindowText);
    TEXT_LENGTH = SendMessage(hWnd, WM_GETTEXTLENGTH, NULL, NULL);

    if (TEXT_LENGTH > NULL)
    {
        SendMessage(hWnd, WM_GETTEXT, (WPARAM)32767, reinterpret_cast<LPARAM>(&szText));
        szText[TEXT_LENGTH] = L'\n'; szText[TEXT_LENGTH + 1] = L'\0';

        while ((nBuffer < 32766) && (szText[nText] != L'\0'))
        { szWindowText[nBuffer++] = szText[nText++]; }

        szWindowText[nBuffer] = L'\0';
    }
    return TRUE;
}

SendMessage(hWnd, WM_GETTEXT, (WPARAM)32767, reinterpret_cast&lt;LPARAM&gt;(&amp;szText)); 行有时会导致缓冲区溢出,我的应用程序崩溃。

我知道如何检测像if (TEXT_LENGTH &gt; 32767) 这样的溢出,但我无法动态增加缓冲区szText 的大小。

正如问题标题所提到的,我不想增加它的大小,我只想截断和空终止返回文本到32767 的最大缓冲区大小(如果TEXT_LENGTH 超过32767)并分配将其转至szWindowText 以用于其他目的。

非常感谢任何帮助。

【问题讨论】:

  • 不要将整数与NULL 进行比较,或将整数初始化为它。 NULL 应该是一个空指针。即使使用 NULL 定义为 0 在语义上是错误的,并且会误导阅读您的代码的人。
  • WCHAR szText[32767]!!!为什么不使用 std::wstring 并在知道长度后动态调整其大小?
  • SendMessage(hWnd, WM_GETTEXT, (WPARAM)32767, reinterpret_cast&lt;LPARAM&gt;(&amp;szText)); 永远不会导致缓冲区溢出,因为提供了正确大小的缓冲区。实际上这个临时缓冲区根本不需要。您真正的问题是您在不知道目标缓冲区大小的情况下写入以lParam 提供的指针。
  • @VTT 是的,可以,您可以使用&amp;some_string_object[0] 获取指向字符串中第一个元素的指针(正确调整大小后)。
  • @Blueeyes789 我不知道。 reading the documentation 怎么样?

标签: c++ winapi truncate buffer-overflow


【解决方案1】:

一些事情:

  1. 最好将缓冲区动态分配给 TEXT_LENGTH。 (为“\0”+1)
  2. 在WM_GETTEXT中去掉szText的&操作符,你只需要包含的地址,而不是指针的地址
  3. 当您明确使用 char 时,请使用 SendMessageA,否则 Visual Studio 默认使用带有 SendMessageW 的 wchar_t。 SendMessage 是一个宏,可根据您的项目设置扩展为 wchar_t 或 char。或者将 TCHAR 类型与 SendMessage 一起使用,该类型也扩展为正确的类型。

DWORD l = SendMessage(hWnd, WM_GETTEXTLENGTH, NULL, NULL);

if (l > 0){
   TCHAR *szText = new TCHAR[l + 1];
   SendMessage(hWnd, WM_GETTEXT, (WPARAM)l + 1, reinterpret_cast<LPARAM>(szText));

  // use szText

   delete[] szText;
}

【讨论】:

  • 这个答案确实会影响 OP 提到的缓冲区溢出问题。它还建议做错事:使用字符或 TCHAR,而不是显式使用宽字符和 W 版本的函数。并且发布的代码 sn-p 包含错误:WPARAMSendMessage 应该包含目标缓冲区 容量,即应该是 l + 1 并且应该检查第二个 SendMessage 调用返回的值以及确定实际复制到缓冲区中的字符数(认为它不太可能与l 不同)。
  • 好的,解决了缓冲区容量问题。但是,我不同意“使用宽字符和 W 版本”的观点。在我看来,您应该始终使用 TCHAR、LPTSTR 等和宏版本的 win32 API 函数(所以非“W”或“A”),以便程序适应项目设置中配置的任何内容
【解决方案2】:

基本上您需要为\n 腾出空间,无论您插入它的原因是什么,这样做的方法是在WM_GETTEXT 调用中使用sizeof szText-1 而不是32767。

NB 如果您必须使用 Simonyi 表示法,请不要在不适用的地方使用它。 szText 没有任何以 null 结尾的内容,您仅将其用于 WM_GETTEXT 的结果。

【讨论】:

    【解决方案3】:

    SendMessage(hWnd, WM_GETTEXT, (WPARAM)32767, reinterpret_cast&lt;LPARAM&gt;(&amp;szText)); 绝不会导致缓冲区溢出,因为提供了正确大小的缓冲区。实际上这个临时缓冲区根本不需要。

    您真正的问题是您在不知道目标缓冲区大小的情况下将字符附加到以lParam 提供的指针中。您应该提供目标缓冲区大小。

    【讨论】:

      猜你喜欢
      • 2022-11-30
      • 1970-01-01
      • 2013-10-22
      • 1970-01-01
      • 2013-04-12
      • 2015-12-16
      • 1970-01-01
      • 2021-12-10
      相关资源
      最近更新 更多