Является ли структура Win32 DLGTEMPLATEEX, возвращаемая из LoadResource, предназначенной для записи пользователем? - PullRequest
1 голос
/ 29 октября 2019

Я работаю с собственным MFC-приложением, которое пытается программным образом изменить размер шрифта диалога, найдя его в структуре DLGTEMPLATEEX, полученной с помощью функции WinR LoadResource, и записав непосредственно в член размера шрифта этой структуры. При запуске под отладчиком в VS 2019 это вызывает нарушение прав доступа к записи в память.

BOOL CMk20Dlg::Create(UINT nIDTemplate, CWnd* pParentWnd)
{
    LPCTSTR lpszTemplateName = MAKEINTRESOURCE(nIDTemplate);

    ASSERT(IS_INTRESOURCE(lpszTemplateName) || AfxIsValidString(lpszTemplateName));

    m_lpszTemplateName = lpszTemplateName;
    if (IS_INTRESOURCE(m_lpszTemplateName) && m_nIDHelp == 0)
        m_nIDHelp = LOWORD(reinterpret_cast<DWORD_PTR>(m_lpszTemplateName));

    HINSTANCE hInst = AfxFindResourceHandle(lpszTemplateName, RT_DIALOG);
    HRSRC hResource = ::FindResource(hInst, lpszTemplateName, RT_DIALOG);
    if (hResource == NULL)
        TRACE(_T("FindResource failed with error %d\n"), ::GetLastError());

    HGLOBAL hTemplate = ::LoadResource(hInst, hResource);
    DLGTEMPLATE* lpTemplate = static_cast<DLGTEMPLATE*>(::LockResource(hTemplate));

    WORD* pwFontSize = GetResFontSizeOffset(lpTemplate);
    *pwFontSize = static_cast<WORD>((8.0 * theApp.GetFontScaleFactor()) + 0.5);
    // ^-- crash on write to *pwFontSize

Я полагаю, что код находит правильное целевое расположение памяти в возвращенной структуре, потому что структура имеет правильную подпись вожидаемое смещение (0xFFFF, идентифицирующее его как DLGTEMPLATEEX, а не просто DLGTEMPLATE - хотя в коде нет проверки на наличие ошибок, если это не так), и я пошагово код, который вычисляет указатель на член размера шрифта. Он указывает на DWORD со значением «8», что кажется разумным для размера системного шрифта.

WORD* CMk20Dlg::GetResFontSizeOffset(DLGTEMPLATE* lpTemplate)
{
   WORD* pwPtr = reinterpret_cast<WORD*>(lpTemplate);

    // lpTemplate is in the format of a DLGTEMPLATEEX
    // Refer Win32 SDK documentation of "struct" DLGTEMPLATEEX
    ++pwPtr;        // dlgVer
    ++pwPtr;        // signature
    /* ^-- At this point *pwPtr has value 0xFFFF in the debugger, indicating DLGTEMPLATEEX */

     /* The ++'ing of pwPtr continues through the rest of the struct members,
        then it skips over the menu array, window class array, title, etc.  Finally: */

    // We are now pointing at the font size word
    return pwPtr;
}

Возвращаемое значение указывает на DWORD «8», когда код пытается фактически изменить это «8» надругой номер при записи через указатель сбой в отладчике Visual Studio 2019 с памятью «нарушение прав записи».

У меня много вопросов по поводу того, что я вижу здесь.

Первыйиз всего, удаляет ли что-либо такое засорение этой области памяти? Как ОС видит, что значение изменилось? Я ожидал бы, что правильным способом сделать это будет какой-нибудь вызов функции Win32, такой как «SetDlgFontSize» или что-то еще, не разбирающееся в структурах данных.

Во-вторых, как этот код вообще когда-либо работал? Это то, что работало на старых версиях Windows, но не на Windows 10? Это терпит неудачу сейчас, потому что я обновил проект до VS 2019 (ранее это было до 2012 года). Или отладчик VS 2019 (или сборка отладки) упреждающе устанавливает бит защиты от записи на страницах памяти со структурами операционной системы, которые я не должен писать?

В-третьих, мне нужно решить, следует ли это исправитьзакомментировав код сбоя или установив размер шрифта «правильно». Это зависит от того, действительно ли когда-либо нарушал код. Если это не так, я могу закомментировать это, но если мне нужно правильно воспроизвести эффект, единственный код MFC, который я нашел для этого, устанавливает размер шрифта и семейство шрифтов вместе. Тогда мне нужно будет добавить код, чтобы узнать правильный шрифт ...

1 Ответ

3 голосов
/ 29 октября 2019

Пока рассматриваемый диалог создается после клоббера, имеет смысл, что при этом будет изменен шаблон, который используется для создания диалога (при условии, что запись разрешена с помощью разрешений памяти), однако я ожидаю, чтоПамять ресурса предназначена только для чтения, и поэтому я удивлен, что это когда-либо работало без нарушения прав доступа.

Это работает (ed), потому что шаблон диалога читается каждый раз, когда диалог создается. Если диалог уже создан во время выполнения этой последовательности, то он не должен иметь никакого эффекта.

Было бы гораздо более целесообразно загрузить ресурс (через FindResource + LoadResource + LockResource), скопировав весь шаблон диалога вявно выделенный буфер, внесите любые необходимые изменения, затем используйте DialogBoxIndirect или DialogBoxIndirectParam для создания экземпляра диалога из шаблона в памяти (а не из идентификатора ide ресурса). Сделав такую ​​копию, вы избежите проблем с правами доступа к памяти.

Полагаю, вы могли бы также использовать VirtualProtect и сделать рассматриваемые страницы доступными для записи, но это кажется мне неприятным способом решения этой проблемы. Вид проблемы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...