gpt4 book ai didi

c++ - `LoadLibraryExW` 从 `0xC0000023` 触发异常 `NtMapViewOfSection`

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:14:16 24 4
gpt4 key购买 nike

缩小这个问题的范围真的很难,但我们开始吧。

上下文

我处于加载到主机 (TstCon.exe) 的 32 位 ActiveX 控件的上下文中。卸载并重新加载控件后,我从 NtMapViewOfSection 收到一连串错误,第一个错误发生在 odbc32.dll 使用 LoadLibraryExW 加载时C:\Windows\system32\odbcint.dll。那时,SEH 异常从 NtMapViewOfSection 内部某处发出,代码为 0xC0000023(根据调试器也称为 STATUS_BUFFER_TOO_SMALL)。

后果

这是调试器拦截异常时调用堆栈的样子:

ntdll.dll!_NtMapViewOfSection@40()
KernelBase.dll!BasepLoadLibraryAsDataFileInternal()
KernelBase.dll!BasepLoadLibraryAsDataFile()
KernelBase.dll!LoadLibraryExW()
odbc32.dll!_InitializeDll@0()
odbc32.dll!_SQLAllocEnv@4()
<OurDll>.dll!<OurFunction>()
...

那时,我已经使用非常理智的技术来检索调用NtMapViewOfSection 的参数,方法是遵循this documentation。 :

*(void**)(ESP + 4 + 0)           /*SectionHandle*/      0x000003b0              void *
*(void**)(ESP + 4 + 4) /*ProcessHandle*/ 0xffffffff void *
*(void**)(ESP + 4 + 8) /*BaseAddress*/ 0x00daae30 void *
*(unsigned long*)(ESP + 4 + 12) /*ZeroBits*/ 0x00000000 unsigned long
*(unsigned long*)(ESP + 4 + 16) /*CommitSize*/ 0x00000000 unsigned long
*(long long**)(ESP + 4 + 20) /*SectionOffset*/ 0x00000000 {???} __int64 *
*(unsigned long**)(ESP + 4 + 24) /*ViewSize*/ 0x00daae28 {0x00000000} unsigned long *
*(int*)(ESP + 4 + 28) /*InheritDisposition*/ 0x00000001 int
*(unsigned long*)(ESP + 4 + 32) /*AllocationType*/ 0x00800000 unsigned long
*(unsigned long*)(ESP + 4 + 36) /*Protect*/ 0x00000002 unsigned long

程序集演练

我最初是通过在 VS 的调试器中启用 break-on-throw 来捕获异常的,然后我就能够查明第一个失败的调用并在前面放置一个断点。这是我在反汇编内部进行调试时可以看到的内容(> 标记当前指令):

  _NtMapViewOfSection@40:
76F2EF60 mov eax,28h
76F2EF65 mov edx,offset _Wow64SystemServiceCall@0 (76F43430h)
> 76F2EF6A call edx
76F2EF6C ret 28h
76F2EF6F nop

...进入:

  _Wow64SystemServiceCall@0:
> 76F43430 jmp dword ptr [_Wow64Transition (76FD2218h)]

...进入:

> 74A37000  jmp         0033:74A37009  
74A37007 add byte ptr [eax],al
74A37009 inc ecx
74A3700A jmp dword ptr [edi+0F8h]

...进入:

  _NtQueryObject@20:
76F2EDC0 mov eax,10h
76F2EDC5 mov edx,offset _Wow64SystemServiceCall@0 (76F43430h)
76F2EDCA call edx
> 76F2EDCC ret 14h
76F2EDCF nop

下一个进入会触发异常。


对程序环境的干扰,例如:

  • 更新编译器和运行时(在 MSVC90 和 MSVC141 之间),这首先揭示了错误;
  • 在发布和调试配置之间切换;
  • 通过 /base 链接器标志强制 OCX 的基地址;
  • 在附加调试器的情况下运行;
  • 使用 drstrace.exe 监控系统调用;

... 更改对 NtMapViewOfSection 的调用将成功或失败,似乎是随机的:并非所有调用都失败,但有相当一部分会失败。事实上,错误的第一次出现可能并不能说明问题的实际来源,因为我很少能够使它更早地崩溃(在卸载控件时),甚至在 none 的情况下发生崩溃 我们的代码在调用堆栈上(通过直接退出 TstCon.exe)。

我找不到任何文档(官方或其他)提及 STATUS_BUFFER_TOO_SMALL 或在此上下文中的 0xC0000023 代码。我一直无法在失败的调用中找到模式,并且在 Dr. Memory 运行中没有看到相关的访问错误。

那么......这个过程中可能发生了什么导致出现这种症状?

最佳答案

在长时间检查 ActiveX 控件的执行后,使用调试器跳过特定的代码部分,我将错误的可能位置缩小到单个函数。事实证明,问题的根源几乎无法猜测。

有一天,有人希望能够为线程命名。为此,他们使用了记录在案的 0x406D1388技术(事实上,代码几乎是从链接文档中复制粘贴的)。这本身就很好。但是后来他们想要(或者我收集到)以编程方式检索名称,自定义异常方法不支持。这一切都是在 SetThreadDescription/GetThreadDescription 存在之前发生的,所以他们寻找另一种方法。 男人他们有创造力。

除了对抛出自定义异常的函数的调用之外,还有以下几行:

// Grab the TIB.
P_T_TIB pTib = GetTIB();

if (pTib == NULL)
return false;

// If someone has already written to the arbitrary field, I don't
// want to be overwriting it.
if (pTib->pvArbitrary == NULL)
{
// Nothing's there. Set the name.
pTib->pvArbitrary = (void *)pszName;
}

有什么,当然绝对不是“无”。 GetTIB定义如下:

// A static function to get the TIB.
static P_T_TIB GetTIB()
{
P_T_TIB pTib = NULL;

_asm
{
MOV EAX, FS:[18h]
MOV pTib, EAX
}

return pTib;
}

据我所知,这段高度可疑的程序集检索了一个指向 Thread Information Block 的指针。当前线程的。 T_TIB 的定义遵循该页面上的描述,并允许线程命名函数将指向名称的指针存储到任意数据槽中。在名称获取函数中使用了相同的技术来检索指针并返回名称。

当然,难点在于“任意”并不代表“用户数据”,NtMapViewOfSection和小伙伴们都是依赖字段来存储信息的,字段上放置的数据断点就是明证被 LdrpMapViewOfSection 等击中。在他们路径的某个地方,遇到具有异常非零值(即我们的名称指针)的字段导致他们执行一些非常错误的操作,最终触发了那个奇怪的异常。

出于好奇,我完全删除了所有这些,因为它没有在其他任何地方使用,并且只是使用了一个 thread_local 变量来存储名称。结案了!

关于c++ - `LoadLibraryExW` 从 `0xC0000023` 触发异常 `NtMapViewOfSection`,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53767707/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com