问题描述
我在 Windows Internals 中读到,当创建线程时,默认情况下会为用户堆栈保留 1 MB 的虚拟内存.在这 1 MB 中,只会提交第一页 (0x1000).
I read in Windows Internals that when a thread is created, by default 1 MB of virtual memory is reserved for the user stack. Out of this 1 MB, only the first page (0x1000) will be committed.
当我使用 dumpbin.exe 转储图像标题时,我可以看到这一点.以下是 dumpbin 显示的内容:
I can see this when i dump the image header using dumpbin.exe. Here is what dumpbin shows:
但是,当我使用 !address 命令在 Windbg 中转储此 exe 的地址空间时,我看到了不同之处.Windbg 显示初始提交大小等于 3 页,即 0x3000
However when i dump the address space of this exe in Windbg using !address command, I see a difference. Windbg shows me that the initial committed size is equal to 3 pages i.e 0x3000
有谁知道为什么图像标头和调试器显示的初始堆栈提交大小之间存在差异?
Does anyone know why there is a difference between the initial stack commit size that the image header and debugger shows?
推荐答案
这是一个很好的问题,答案的关键是了解初始断点是什么.对于初学者来说,它的初始程度如何?
That's a nice question and the key to the answer is understanding what the initial breakpoint is. How initial is it, for starters?
TLDR:初始断点太晚了.该堆栈已经增长.
TLDR: The initial breakpoint it too late. That stack has already grown.
您尚未共享正在处理的二进制文件,因此我选择了一个表现相同行为的二进制文件 - cacls.exe
在 64 位 Windows 10(文件版本:10.0.14393.0).
You haven't shared the binary you're dealing with, so I chose a binary that exhibits the same behaviour - cacls.exe
on 64-bit Windows 10 (file version: 10.0.14393.0).
在初始断点期间,我们观察到:
During the initial breakpoint we observe:
CommandLine: "c:\Windows\System32\cacls.exe"
Symbol search path is: srv*
Executable search path is:
ModLoad: 00007ff6`83bd0000 00007ff6`83bdc000 cacls.exe
ModLoad: 00007ff8`29ce0000 00007ff8`29eb1000 ntdll.dll
ModLoad: 00007ff8`27500000 00007ff8`275ab000 C:\Windows\System32\KERNEL32.DLL
ModLoad: 00007ff8`26f30000 00007ff8`2714d000 C:\Windows\System32\KERNELBASE.dll
ModLoad: 00007ff8`280b0000 00007ff8`2814e000 C:\Windows\System32\msvcrt.dll
ModLoad: 00007ff8`29b10000 00007ff8`29bb2000 C:\Windows\System32\advapi32.dll
ModLoad: 00007ff8`273d0000 00007ff8`27429000 C:\Windows\System32\sechost.dll
ModLoad: 00007ff8`254f0000 00007ff8`25522000 c:\Windows\System32\NTMARTA.dll
ModLoad: 00007ff8`27150000 00007ff8`27245000 C:\Windows\System32\ucrtbase.dll
ModLoad: 00007ff8`277c0000 00007ff8`278e1000 C:\Windows\System32\RPCRT4.dll
(1310.17b0): Break instruction exception - code 80000003 (first chance)
ntdll!LdrpDoDebuggerBreak+0x30:
00007ff8`29db34e0 cc int 3
0:000> !dh -f cacls
File Type: EXECUTABLE IMAGE
FILE HEADER VALUES
8664 machine (X64)
6 number of sections
57899A04 time date stamp Sat Jul 16 05:20:52 2016
0 file pointer to symbol table
0 number of symbols
F0 size of optional header
22 characteristics
Executable
App can handle >2gb addresses
OPTIONAL HEADER VALUES
20B magic #
14.00 linker version
4C00 size of code
3600 size of initialized data
0 size of uninitialized data
52F0 address of entry point
1000 base of code
----- new -----
00007ff683bd0000 image base
1000 section alignment
200 file alignment
3 subsystem (Windows CUI)
10.00 operating system version
10.00 image version
10.00 subsystem version
C000 size of image
400 size of headers
AF10 checksum
0000000000080000 size of stack reserve
0000000000002000 size of stack commit
0000000000100000 size of heap reserve
0000000000001000 size of heap commit
C160 DLL characteristics
High entropy VA supported
Dynamic base
NX compatible
Guard
Terminal server aware
0 [ 0] address [size] of Export Directory
7010 [ 1CC] address [size] of Import Directory
A000 [ 7F0] address [size] of Resource Directory
9000 [ 2DC] address [size] of Exception Directory
0 [ 0] address [size] of Security Directory
B000 [ 54] address [size] of Base Relocation Directory
6A10 [ 38] address [size] of Debug Directory
0 [ 0] address [size] of Description Directory
0 [ 0] address [size] of Special Directory
0 [ 0] address [size] of Thread Storage Directory
60E0 [ D0] address [size] of Load Configuration Directory
0 [ 0] address [size] of Bound Import Directory
61B0 [ 3B8] address [size] of Import Address Table Directory
0 [ 0] address [size] of Delay Import Directory
0 [ 0] address [size] of COR20 Header Directory
0 [ 0] address [size] of Reserved Directory
0:000> !address @rsp
Mapping file section regions...
Mapping module regions...
Mapping PEB regions...
Mapping TEB and stack regions...
Mapping heap regions...
Mapping page heap regions...
Mapping other regions...
Mapping stack trace database regions...
Mapping activation context regions...
Usage: Stack
Base Address: 00000049`8fbbc000
End Address: 00000049`8fbc0000
Region Size: 00000000`00004000 ( 16.000 kB)
State: 00001000 MEM_COMMIT
Protect: 00000004 PAGE_READWRITE
Type: 00020000 MEM_PRIVATE
Allocation Base: 00000049`8fb40000
Allocation Protect: 00000004 PAGE_READWRITE
More info: ~0k
Content source: 1 (target), length: 180
我们看到初始堆栈提交大小为 0x2000,但实际上提交了 0x4000.
We see the initial stack commit size is 0x2000, but 0x4000 is actually committed.
但这在进程初始化过程中已经很晚了(没有双关语).例如,所有导入 DLL 都已加载.
But that's already very late during the process initialization process (no pun intended). All the import DLL are already loaded, for example.
所谓的初始断点"只是NTDLL中进程初始化代码调用的(或多或少)硬编码int 3
指令.如果您此时查看堆栈,您将看到名为 LdrpDoDebuggerBreak
的函数,该函数由 LdrpInitializeProcess
调用:
The so-called "initial break-point" is simply a (more or less) hardcoded int 3
instruction called by the process initializtion code in NTDLL. If you look at the stack at this point you'll see the aptly named LdrpDoDebuggerBreak
function which is called by LdrpInitializeProcess
:
0:000> k
# Child-SP RetAddr Call Site
00 00000049`8fbbee80 00007ff8`29d72e22 ntdll!LdrpDoDebuggerBreak+0x30
01 00000049`8fbbeec0 00007ff8`29da8986 ntdll!LdrpInitializeProcess+0x1962
02 00000049`8fbbf2c0 00007ff8`29d59fae ntdll!_LdrpInitialize+0x4e982
03 00000049`8fbbf340 00000000`00000000 ntdll!LdrInitializeThunk+0xe
到那时,堆栈已经被使用(例如,加载静态链接的 DLL 并执行它们的初始化代码),因此堆栈已经增长也就不足为奇了.
By the time that happened the stack has already been used (for example, to load statically linked DLLs and perform their initialization code), so it shouldn't be much surprise that the stack has grown already.
要检查刚刚创建的流程,我们需要在流程创建事件而不是初始断点(这不是我们现在理解的初始断点)上中断.
To examine the process when it has just been created we need to break on the process creation event rather than on the initial breakpoint (which isn't that initial as we now understand).
我们可以像我一样使用 sxe cpr
和 .restart
ing 或使用 -xe cpr
运行 WinDbg/NTSD.这样做会揭示一些有趣的事情:
We can do that either using sxe cpr
and .restart
ing like I did or running WinDbg/NTSD with -xe cpr
. Doing that reveals something interesting:
0:000> .restart
CommandLine: C:\Windows\System32\cacls.exe
************* Symbol Path validation summary **************
Response Time (ms) Location
Deferred srv*
Symbol search path is: srv*
Executable search path is:
ModLoad: 00007ff6`83bd0000 00007ff6`83bdc000 cacls.exe
00007ff8`29d470b0 4883ec48 sub rsp,48h
0:000> .imgscan /l
MZ at 00007ff6`83bd0000, prot 00000002, type 01000000 - size c000
Name: cacls.exe
Loaded cacls.exe module
MZ at 00007ff8`29ce0000, prot 00000002, type 01000000 - size 1d1000
Name: ntdll.dll
Loaded ntdll.dll module
0:000> !address @rsp
Mapping file section regions...
Mapping module regions...
Mapping PEB regions...
Mapping TEB and stack regions...
Mapping heap regions...
Mapping page heap regions...
Mapping other regions...
Mapping stack trace database regions...
Mapping activation context regions...
Usage: Stack
Base Address: 0000004a`5428e000
End Address: 0000004a`54290000
Region Size: 00000000`00002000 ( 8.000 kB)
State: 00001000 MEM_COMMIT
Protect: 00000004 PAGE_READWRITE
Type: 00020000 MEM_PRIVATE
Allocation Base: 0000004a`54210000
Allocation Protect: 00000004 PAGE_READWRITE
More info: ~0k
Content source: 1 (target), length: 478
提交区域大小为 0x2000 - 就像标题所说的一样!
The committed region size is 0x2000 - like the header says!
如果我们让它继续下去,我们最终会到达初始断点并提交更多堆栈.
If we let it continue we'll eventually get to the initial breakpoint with more stack comitted.
我说或多或少是硬编码的,因为函数的实际代码是
I said more or less hardcoded because the actual code of the function is
0:000> uf ntdll!LdrpDoDebuggerBreak
ntdll!LdrpDoDebuggerBreak:
00007ff8`29db34b0 4883ec38 sub rsp,38h
00007ff8`29db34b4 488364242000 and qword ptr [rsp+20h],0
00007ff8`29db34ba 41b901000000 mov r9d,1
00007ff8`29db34c0 4c8d442440 lea r8,[rsp+40h]
00007ff8`29db34c5 418d5110 lea edx,[r9+10h]
00007ff8`29db34c9 48c7c1feffffff mov rcx,0FFFFFFFFFFFFFFFEh
00007ff8`29db34d0 e88b30fdff call ntdll!NtQueryInformationThread (00007ff8`29d86560)
00007ff8`29db34d5 85c0 test eax,eax
00007ff8`29db34d7 780a js ntdll!LdrpDoDebuggerBreak+0x33 (00007ff8`29db34e3) Branch
ntdll!LdrpDoDebuggerBreak+0x29:
00007ff8`29db34d9 807c244000 cmp byte ptr [rsp+40h],0
00007ff8`29db34de 7503 jne ntdll!LdrpDoDebuggerBreak+0x33 (00007ff8`29db34e3) Branch
ntdll!LdrpDoDebuggerBreak+0x30:
00007ff8`29db34e0 cc int 3
00007ff8`29db34e1 eb00 jmp ntdll!LdrpDoDebuggerBreak+0x33 (00007ff8`29db34e3) Branch
ntdll!LdrpDoDebuggerBreak+0x33:
00007ff8`29db34e3 4883c438 add rsp,38h
00007ff8`29db34e7 c3 ret
它会检查这个线程是否对调试器隐藏",但基本上它只是闯入调试器.
It does stuff like checking whether or not this thread is to be "hidden from the debugger", but basically it just breaks into the debugger.
需要 .imgscan/l
因为没有它我们得到:
The .imgscan /l
is needed because without it we get:
0:000> !address @rsp
No symbols for ntdll. Cannot continue.
这篇关于!address 命令显示用户模式堆栈初始提交大小的不同值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!