从 LoadResource 返回的 Win32 DLGTEMPLATEEX 结构是否意味着用户可写?
Is the Win32 DLGTEMPLATEEX structure returned from LoadResource meant to be user-writable?
我正在使用内部 MFC 应用程序,它试图通过在使用 Win32 LoadResource 函数获得的 DLGTEMPLATEEX 结构中找到它并直接写入该对话框的字体大小成员,以编程方式更改对话框的字体大小结构体。当 运行 在 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
我相信代码在 returned 结构中找到正确的目标内存位置,因为该结构在预期偏移处具有正确的签名(0xFFFF 将其标识为 DLGTEMPLATEEX 而不仅仅是 DLGTEMPLATE --尽管代码没有任何错误检查(如果不是),我单步执行了计算指向字体大小成员的指针的代码。它指向一个值为“8”的 DWORD,这对于系统字体大小来说似乎是合理的。
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;
}
return 值指向 DWORD“8”,当代码试图通过写入指针实际将该“8”更改为另一个数字时,它在 Visual Studio 2019 调试器中崩溃记忆 "write access violation".
我对我在这里看到的东西有很多顾虑。
首先,像这样破坏这个内存位置甚至有什么用吗? OS 如何看到值发生了变化?我希望正确的方法是像 "SetDlgFontSize" 之类的一些 Win32 函数调用,而不是在数据结构中四处探索。
其次,这段代码是如何工作的?它是否适用于 Windows 的旧版本,但不适用于 Windows 10?现在失败是不是因为我把项目升级到了VS 2019(之前是2012)。还是 VS 2019 调试器(或调试版本)先发制人地在我不应该写的操作系统结构的内存页上设置写保护位?
第三,我需要决定是通过注释掉崩溃代码还是设置字体大小来解决此问题 "properly." 这取决于有问题的代码是否真的产生过影响。如果没有,我可以将其注释掉,但如果我需要正确复制效果,那么到目前为止我找到的唯一用于执行此操作的 MFC 代码将字体大小和字体系列设置在一起。那么我还需要添加代码来找出正确的字体...
只要有问题的对话框是在 clobber 之后创建的,这样做就可以改变用于创建对话框的模板(只要内存权限允许写入),但是我会希望资源内存是只读的,所以我很惊讶这在没有访问冲突的情况下工作。
之所以有效,是因为每次创建对话框时都会读取对话框模板。如果在执行此序列时已经创建了对话框,那么它应该没有任何效果。
加载资源(通过 FindResource + LoadResource + LockResource)将整个对话框模板复制到显式分配的缓冲区,进行任何需要的更改,然后使用 DialogBoxIndirect 或 DialogBoxIndirectParam 从中实例化对话框会更有意义-内存模板(而不是来自资源 ide identifier)。制作这样的副本会 side-step 内存权限问题的可能性。
我想您也可以使用 VirtualProtect 并使有问题的页面可写,但我认为这是解决此类问题的一种令人讨厌的方法。
我正在使用内部 MFC 应用程序,它试图通过在使用 Win32 LoadResource 函数获得的 DLGTEMPLATEEX 结构中找到它并直接写入该对话框的字体大小成员,以编程方式更改对话框的字体大小结构体。当 运行 在 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
我相信代码在 returned 结构中找到正确的目标内存位置,因为该结构在预期偏移处具有正确的签名(0xFFFF 将其标识为 DLGTEMPLATEEX 而不仅仅是 DLGTEMPLATE --尽管代码没有任何错误检查(如果不是),我单步执行了计算指向字体大小成员的指针的代码。它指向一个值为“8”的 DWORD,这对于系统字体大小来说似乎是合理的。
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;
}
return 值指向 DWORD“8”,当代码试图通过写入指针实际将该“8”更改为另一个数字时,它在 Visual Studio 2019 调试器中崩溃记忆 "write access violation".
我对我在这里看到的东西有很多顾虑。
首先,像这样破坏这个内存位置甚至有什么用吗? OS 如何看到值发生了变化?我希望正确的方法是像 "SetDlgFontSize" 之类的一些 Win32 函数调用,而不是在数据结构中四处探索。
其次,这段代码是如何工作的?它是否适用于 Windows 的旧版本,但不适用于 Windows 10?现在失败是不是因为我把项目升级到了VS 2019(之前是2012)。还是 VS 2019 调试器(或调试版本)先发制人地在我不应该写的操作系统结构的内存页上设置写保护位?
第三,我需要决定是通过注释掉崩溃代码还是设置字体大小来解决此问题 "properly." 这取决于有问题的代码是否真的产生过影响。如果没有,我可以将其注释掉,但如果我需要正确复制效果,那么到目前为止我找到的唯一用于执行此操作的 MFC 代码将字体大小和字体系列设置在一起。那么我还需要添加代码来找出正确的字体...
只要有问题的对话框是在 clobber 之后创建的,这样做就可以改变用于创建对话框的模板(只要内存权限允许写入),但是我会希望资源内存是只读的,所以我很惊讶这在没有访问冲突的情况下工作。
之所以有效,是因为每次创建对话框时都会读取对话框模板。如果在执行此序列时已经创建了对话框,那么它应该没有任何效果。
加载资源(通过 FindResource + LoadResource + LockResource)将整个对话框模板复制到显式分配的缓冲区,进行任何需要的更改,然后使用 DialogBoxIndirect 或 DialogBoxIndirectParam 从中实例化对话框会更有意义-内存模板(而不是来自资源 ide identifier)。制作这样的副本会 side-step 内存权限问题的可能性。
我想您也可以使用 VirtualProtect 并使有问题的页面可写,但我认为这是解决此类问题的一种令人讨厌的方法。