前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >DOTNET_EnableWriteXorExecute引起另外一个异常

DOTNET_EnableWriteXorExecute引起另外一个异常

作者头像
江湖评谈
发布2023-08-31 21:18:08
1480
发布2023-08-31 21:18:08
举报
文章被收录于专栏:天下风云天下风云

前言

本篇来看下有:DOTNET_EnableWriteXorExecute(简称:W^E)导致的另外一个异常现象,实际上不是异常。因为:W^E,开启了就是主内存映射,只不过断点不能够在内存映射范围内。

概括

1.引起的异常 .Net7及其以后是默认开启的W^E,而.Net6则是默认关闭的。如下所示:

代码语言:javascript
复制
.Net7:
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableWriteXorExecute, W("EnableWriteXorExecute"), 1, "Enable W^X for executable memory.");

.Net6
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableWriteXorExecute, W("EnableWriteXorExecute"), 0, "Enable W^X for executable memory.");

如下例子:

代码语言:javascript
复制
static void Main(string[] args)
{
   //Task tk= Task.CompletedTask;
    Console.ReadLine();         
    //TextWriter tr= Console.Out;
    Console.WriteLine("Hello, World!"); // 12行
    Program pm=new Program();
}

这也就导致了Debug.Net7的时候则出现了问题,如下Windbg.Net7里面:

代码语言:javascript
复制
0:007> !bpmd Program.cs:12
MethodDesc = 00007FFEE719EF18
Setting breakpoint: bp 00007FFEE70D06CA [ConsoleApp3.Program.Main(System.String[])]
Adding pending breakpoints...
0:007> g
Breakpoint 0 hit
ConsoleApp3!ConsoleApp3.Program.Main+0x1a:
00007ffe`e70d06ca 48b9d82040f4f8020000 mov rcx,2F8F44020D8h
0:000> g
(560.44c0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
00007ffe`e70d0938 0000            add     byte ptr [rax],al ds:00007ffe`e70d0938=00

这里有个c0000005的异常,这里可以看到它是执行异常。它其实不是BUG,而是.Net7里面W^E环境变量的作用的结果。

看下它的ASM

代码语言:javascript
复制
00007ffe`e70d0938 0000                 add     byte ptr [rax], al
00007ffe`e70d093a 0000                 add     byte ptr [rax], al
00007ffe`e70d093c 0000                 add     byte ptr [rax], al
00007ffe`e70d093e 0000                 add     byte ptr [rax], al

异常的地方是地址,00007ffee70d0938 而Main函数的函数头地址则是:00007ffee70d06ca.可以看到这两个地址非常近。而函数头上下左右偏移约0x1000字节范围内,基本上都属于内存映射。所以如果在Console.WriteLine下了断点,地址即是在内存映射范围内下了断点,地址00007ffee70d0938赋值不上,导致了异常。

2.异常的地址哪里来的 通过上面知道了异常是断点+内存映射引起的。 异常的地址是00007ffe`e70d0938,它是被下面代码调用:

代码语言:javascript
复制
DoCall:
        call    qword ptr [rbx+CallDescrData__pTarget]     ; call target function

这个DoCall函数是调用所有托管函数进行编译,注意它不是编译之后的地址而是之前。CallDescrData__pTarget是函数描述结构体MethodDesc的偏移位的地址指向的值。

rbx寄存器是一个结构体,CallDescrData__pTarget是这个结构体里面的一个变量。那么CallDescrData__pTarget哪里来的呢?

一般来说,CLR的内存结构链如下所示:

代码语言:javascript
复制
PreCode->MethodDescChunk->MethodDesc

它们在内存中依次从左至右排列的。

这个CallDescrData__pTarget最先的值就是PreCode的某个地址。后这个地址被赋值为MethodDesc的某个地址偏移位置指向的值的地方(PreCode某个地址取代原有值),这里看下它是如何在PreCode地址里面赋值的?

代码语言:javascript
复制
void *UnlockedLoaderHeap::UnlockedAllocAlignedMem_NoThrow(size_t  dwRequestedSize,
                                                          size_t  alignment,
                                                          size_t *pdwExtra
                                                          COMMA_INDEBUG(_In_ const char *szFile)
                                                          COMMA_INDEBUG(int  lineNum))
{
    void *pResult;
    pResult = m_pAllocPtr;
#ifdef _DEBUG
    BYTE *pAllocatedBytes = (BYTE *)pResult;
    ExecutableWriterHolderNoLog<void> resultWriterHolder;
    if (IsExecutable())
    {
        resultWriterHolder.AssignExecutableWriterHolder(pResult, dwSize - extra);
        pAllocatedBytes = (BYTE *)resultWriterHolder.GetRW();
    }
}

注意看,m_pAllocPtr就是PreCode地址里面的值。可以看到这里微软比较贴心的通过#ifdef_DEBUG来让读者能够看得清楚这里是内存映射。 所以这里也就很清楚了,PreCode的值就是m_pAllocPtr.而PreCode的地址则是DoCall调用的函数

【rbx+CallDescrData__pTarget】取的值。它的来源是PreCode。因为内存映射范围内进行托管函数调用的编译,又在内存映射范围(Console.WriteLine)下了断点,即异常。

结尾

作者:江湖评谈

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-06-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 江湖评谈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 概括
  • 结尾
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档