本文与博客同步发布。
某一天开发完做测试的时候,发现测试程序没有输出日志。确定与新功能没关系后,决定上windbg一探究竟,最后定位的结果是垫片引擎和ntdll模块的锁之间形成了死锁关系。
接下来记录一下死锁的形成经历。
观察windbg打印的栈,可看出主线程调用MessageBox就没有返回了。
注:调用MessageBox之前,xx_tester创建了一个线程,用于关掉该消息框。不过主线程还没有创建好消息框就卡住了。另外,主线程弹出消息框只是用于测试windows的全局钩子,所以请大家不要模仿。
看02~03的调用,通过LocalReAlloc进入了AcLayers模块的堆管理部分。AcLayers是windows在winXP引入的垫片机制的一个垫片(apphelp.dll是垫片引擎)。为了兼容可能出现的堆错误,这里使用了AcLayers垫片的容错堆功能。
注:堆有一些调试支持,比如堆尾检查(Heap Tail Check/HTC)、释放检查(Heap Free Check/HFC)等,和FTH名字很像,不过FTH是一个自win7以来提供的子系统,用于监控应用程序的崩溃,并在之后的启动中,提供缓解方法来避免崩溃。
看00~01的调用,发现AcLayers调用了ntdll的RtlAcquireSRWLockShared,这个函数是在等一个SRWLock,然后就一去不复返了。windbg没有srwlock相关的扩展命令,不过可以通过_RTL_SRWLOCK来观察结构体的内容,如下:
从这个结构体的内容可看出,另外一个线程持有这个锁。因为SRWLock是微软内部使用的,没有公开文档,所以从该结构体,我们暂且还不知是哪个线程持有这个锁。
注:SRWLock不能看出谁持有锁,但CRITICAL_SECTION可以看出。
用windbg附加的时候,发现无法立刻中断下来,弹出如下内容:
因为windbg附加时,是通过远线程的方式调用被调试进程的DbgUiRemoteBreakin函数,那为何附加线程无法中断呢?观察这个线程的栈如下:
观察13~14的调用,发现在切换到DbgUiRemoteBreakin之前,就已经卡住了。
注:当创建新的线程时,都会先调用LdrInitializeThunk,做一些线程的初始化,然后调用NtContinue函数。NtContinue会将CONTEXT结构体的eip设置为线程真正的起始执行入口。
观察05~09的调用,在测试时,我对NtContinue做了inlinehook,NtContinueDetour就是inlinehook的中间函数。NtContinueDetour在检查的途中,分配了堆块,最后调用了微软的C运行时库函数__acrt_lock。
观察04的调用,发现附加线程卡住的原因是在等待一个CRITICAL_SECTION,观察这个锁:
OwningThread指明了线程ID为0x000047b8的线程持有这个锁,那我们切换到这个线程,观察它的栈:
观察20~24的调用,这是一个线程池里的线程,然后周期性地调用着一个回调。
观察17~19的调用,发现是nlohmann的json类在调用构造函数。
观察05~11的调用,发现也调用了AcLayers垫片的FTH。
观察04的调用,可知7号线程也在等待一个CS锁,观察这个锁如下:
这个cs锁的持有者是线程0x00004b38,这个线程就是主线程。
通过以上的分析,可以猜想是主线程和7号线程发生了死锁,然后其他线程也因执行堆的相关操作而全都卡住。
回想主线程等待的SRWLock,通过它的地址,我们可以知道这个锁的具体名字:
这个SRWLock的名字叫FthDelayFreeQueueFlushLock。在IDA中,我们可以看到AcLayers垫片的FthDelayFreeQueueInsert函数是如何尝试获取SRWLock的:
交叉引用FthDelayFreeQueueFlushLock,发现有多个函数会获取这个锁:
再观察7号线程,发现7号线程在获取CS锁之前,调用了FthDelayFreeQueueFlush函数,通过IDA观察如下:
FthDelayFreeQueueFlush函数做的第一件事就是以独占的方式获取FthDelayFreeQueueFlushLock。
主线程获取了CS锁后,因为进入了AcLayers垫片的FTH,所以尝试获取共享的FthDelayFreeQueueFlushLock;
而7号线程在获取了独占的FthDelayFreeQueueFlushLock后,又希望获取主线程持有的CS锁;
其他线程也因为处理堆时,进入了AcLayers垫片,进而尝试获取堆相关的CS锁,导致暂停,最后整个进程都死锁了。
这个问题是win10下,在调试器中启动程序的调试版本+兼容模式出现的,程序正常启动是不会造成此问题的(因为不会启用调试堆)。如果不以兼容模式运行程序,那也不会造成此问题(因为不会启用垫片引擎)。
00
008fe8e0
775f57da
6be0c9ac
00000000
02c78f58
ntdll!NtWaitForAlertByThreadId
+
0xc
(FPO: [
2
,
0
,
0
])
01
008fe944
6bdddaa3
6be0c9ac
00000000
02c78f28
ntdll!RtlAcquireSRWLockShared
+
0x1ca
(FPO: [
1
,
17
,
4
])
02
008fe980
6bddd0b3
00000000
00000000
00000009
AcLayers!NS_FaultTolerantHeap::FthDelayFreeQueueInsert
+
0x7b
(FPO: [
1
,
9
,
4
])
03
008feabc
74e9be65
00d30000
00140009
02c78f28
AcLayers!NS_FaultTolerantHeap::APIHook_RtlReAllocateHeap
+
0x843
(FPO: [Non
-
Fpo])
04
008feb14
77455486
KERNELBASE!LocalReAlloc
+
0x135
05
008feb74
77476827
MSCTF!CCategoryMgr::s_RegisterGUID
+
0x286
...
0a
008fec7c
762c38dc
MSCTF!CtfImeCreateInputContext
+
0x30
...
0e
008fed1c
764e0f92
IMM32!ImmSetActiveContext
+
0x34f
...
15
008ff154
764ee4cf
USER32!DispatchClientMessage
+
0xea
16
008ff190
7763537d
USER32!__fnDWORD
+
0x3f
17
008ff1c8
764a30ac
ntdll!KiUserCallbackDispatcher
+
0x4d
18
008ff1cc
76540ad9
win32u!NtUserSetFocus
+
0xc
...
26
008ff8ec
009c9d6d
USER32!MessageBoxW
+
0x45
WARNING: Stack unwind information
not
available. Following frames may be wrong.
27
008ffab8
00a1157e
xx_tester
+
0xa9d6d
...
2c
008ffb48
77628944
KERNEL32!BaseThreadInitThunk
+
0x19
2d
008ffba4
77628914
ntdll!__RtlUserThreadStart
+
0x2f
2e
008ffbb4
00000000
ntdll!_RtlUserThreadStart
+
0x1b
00
008fe8e0
775f57da
6be0c9ac
00000000
02c78f58
ntdll!NtWaitForAlertByThreadId
+
0xc
(FPO: [
2
,
0
,
0
])
01
008fe944
6bdddaa3
6be0c9ac
00000000
02c78f28
ntdll!RtlAcquireSRWLockShared
+
0x1ca
(FPO: [
1
,
17
,
4
])
02
008fe980
6bddd0b3
00000000
00000000
00000009
AcLayers!NS_FaultTolerantHeap::FthDelayFreeQueueInsert
+
0x7b
(FPO: [
1
,
9
,
4
])
03
008feabc
74e9be65
00d30000
00140009
02c78f28
AcLayers!NS_FaultTolerantHeap::APIHook_RtlReAllocateHeap
+
0x843
(FPO: [Non
-
Fpo])
04
008feb14
77455486
KERNELBASE!LocalReAlloc
+
0x135
05
008feb74
77476827
MSCTF!CCategoryMgr::s_RegisterGUID
+
0x286
...
0a
008fec7c
762c38dc
MSCTF!CtfImeCreateInputContext
+
0x30
...
0e
008fed1c
764e0f92
IMM32!ImmSetActiveContext
+
0x34f
...
15
008ff154
764ee4cf
USER32!DispatchClientMessage
+
0xea
16
008ff190
7763537d
USER32!__fnDWORD
+
0x3f
17
008ff1c8
764a30ac
ntdll!KiUserCallbackDispatcher
+
0x4d
18
008ff1cc
76540ad9
win32u!NtUserSetFocus
+
0xc
...
26
008ff8ec
009c9d6d
USER32!MessageBoxW
+
0x45
WARNING: Stack unwind information
not
available. Following frames may be wrong.
27
008ffab8
00a1157e
xx_tester
+
0xa9d6d
...
2c
008ffb48
77628944
KERNEL32!BaseThreadInitThunk
+
0x19
2d
008ffba4
77628914
ntdll!__RtlUserThreadStart
+
0x2f
2e
008ffbb4
00000000
ntdll!_RtlUserThreadStart
+
0x1b
0
:
000
> dt ntdll!_RTL_SRWLOCK
6be0c9ac
+
0x000
Locked :
0y1
+
0x000
Waiting :
0y1
+
0x000
Waking :
0y0
+
0x000
MultipleShared :
0y0
+
0x000
Shared :
0y0000000010001111111010010010
(
0x8fe92
)
+
0x000
Value :
0x8fe923
+
0x000
Ptr :
0x008fe923
Void
0
:
000
> dt ntdll!_RTL_SRWLOCK
6be0c9ac
+
0x000
Locked :
0y1
+
0x000
Waiting :
0y1
+
0x000
Waking :
0y0
+
0x000
MultipleShared :
0y0
+
0x000
Shared :
0y0000000010001111111010010010
(
0x8fe92
)
+
0x000
Value :
0x8fe923
+
0x000
Ptr :
0x008fe923
Void
Break
-
in
sent, waiting
30
seconds...
WARNING: Break
-
in
timed out, suspending.
This
is
usually caused by another thread holding the loader lock
(
5874.4b38
): Wake debugger
-
code
80000007
(first chance)
eax
=
00004b38
ebx
=
6be0c9ac
ecx
=
00000000
edx
=
00000000
esi
=
00000001
edi
=
6be0c9ac
eip
=
77634bbc
esp
=
008fe8e4
ebp
=
008fe944
iopl
=
0
nv up ei pl nz na pe nc
cs
=
0023
ss
=
002b
ds
=
002b
es
=
002b
fs
=
0053
gs
=
002b
efl
=
00000206
ntdll!NtWaitForAlertByThreadId
+
0xc
:
77634bbc
c20800 ret
8
Break
-
in
sent, waiting
30
seconds...
WARNING: Break
-
in
timed out, suspending.
This
is
usually caused by another thread holding the loader lock
(
5874.4b38
): Wake debugger
-
code
80000007
(first chance)
eax
=
00004b38
ebx
=
6be0c9ac
ecx
=
00000000
edx
=
00000000
esi
=
00000001
edi
=
6be0c9ac
eip
=
77634bbc
esp
=
008fe8e4
ebp
=
008fe944
iopl
=
0
nv up ei pl nz na pe nc
cs
=
0023
ss
=
002b
ds
=
002b
es
=
002b
fs
=
0053
gs
=
002b
efl
=
00000206
ntdll!NtWaitForAlertByThreadId
+
0xc
:
77634bbc
c20800 ret
8
0
:
013
> kv
00
097ef0e8
7761b79b
7b55f2a4
00000000
7b55f2a0
ntdll!NtWaitForAlertByThreadId
+
0xc
(FPO: [
2
,
0
,
0
])
01
097ef120
7761b46d
00000000
00000000
7b55f2a4
ntdll!RtlpWaitOnAddressWithTimeout
+
0xc0
(FPO: [Non
-
Fpo])
02
097ef1c0
7760d681
097ef3a0
097efa48
00000000
ntdll!RtlpWaitOnCriticalSection
+
0x18d
(FPO: [Non
-
Fpo])
03
097ef1f8
7760ae09
097ef20c
7b328b95
7b55f2a0
ntdll!RtlpEnterCriticalSectionContended
+
0x261
(FPO: [Non
-
Fpo])
04
097ef200
7b328b95
7b55f2a0
097ef264
7b2e17f3
ntdll!RtlEnterCriticalSection
+
0x49
(FPO: [
1
,
0
,
0
])
05
097ef20c
7b2e17f3
xx!__acrt_lock
+
0x15
06
097ef264
7b2e1746
xx!heap_alloc_dbg_internal
+
0x43
07
097ef288
7b2e476a
xx!heap_alloc_dbg
+
0x36
08
097ef2a0
7b323124
xx!_malloc_dbg
+
0x1a
09
097ef2b8
7b0a672d
xx!malloc
+
0x14
...
13
097efb20
775f2ee9
xx!NtContinueDetour
+
0x46
14
097efb34
00000000
ntdll!LdrInitializeThunk
+
0x29
0
:
013
> kv
00
097ef0e8
7761b79b
7b55f2a4
00000000
7b55f2a0
ntdll!NtWaitForAlertByThreadId
+
0xc
(FPO: [
2
,
0
,
0
])
01
097ef120
7761b46d
00000000
00000000
7b55f2a4
ntdll!RtlpWaitOnAddressWithTimeout
+
0xc0
(FPO: [Non
-
Fpo])
02
097ef1c0
7760d681
097ef3a0
097efa48
00000000
ntdll!RtlpWaitOnCriticalSection
+
0x18d
(FPO: [Non
-
Fpo])
03
097ef1f8
7760ae09
097ef20c
7b328b95
7b55f2a0
ntdll!RtlpEnterCriticalSectionContended
+
0x261
(FPO: [Non
-
Fpo])
04
097ef200
7b328b95
7b55f2a0
097ef264
7b2e17f3
ntdll!RtlEnterCriticalSection
+
0x49
(FPO: [
1
,
0
,
0
])
05
097ef20c
7b2e17f3
xx!__acrt_lock
+
0x15
06
097ef264
7b2e1746
xx!heap_alloc_dbg_internal
+
0x43
07
097ef288
7b2e476a
xx!heap_alloc_dbg
+
0x36
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-10-25 23:18
被coneco编辑
,原因: