17.Win32堆的调试支持_哔哩哔哩_bilibili
继续边看录像边做实验。
堆上的内存时用size表达的,组成一个链表。
创建一个FreCheck应用
上次看heap,直接使用下载的文件,本次要做实验了,就需要自己动手,搞个VC project了。
在vs2022中直接建立一个console应用。建立一个FreCheck.cpp
#include <stdio.h>
#include <windows.h>int main(int argc, char* argv[]) {char* p;HANDLE hHeap;hHeap = HeapCreate(0, 1024, 0);p = (char*)HeapAlloc(hHeap, 0, 9);for (int i = 0; i < 20; i++)*(p + i) = i;if (!HeapFree(hHeap, 0, p))printf("Free %x from %x failed.", p, hHeap);if (!HeapDestroy(hHeap))printf("Destroy heap %x failed.", hHeap);printf("Exit with 0");return 0;
}
下断点
0:000> x FreCheck!*main*
0015a018 FreCheck!__scrt_native_dllmain_reason = 0xffffffff
00151880 FreCheck!main (int, char **)
001521a0 FreCheck!__scrt_main_policy::set_app_type (void)
00152e40 FreCheck!__scrt_dllmain_uninitialize_c (void)
00152150 FreCheck!invoke_main (void)
0:000> bp FreCheck!main
breakpoint 0 redefined
0:000> bl
0 e Disable Clear 00151880 0001 (0001) 0:**** FreCheck!main
运行
0:000> g
发现堆已经好几个了
0:000> !heap
Failed to read heap keySEGMENT HEAP ERROR: failed to initialize the extention
NtGlobalFlag enables following debugging aids for new heaps: tail checking
free checking
validate parameters
Index Address Name Debugging options enabled
1: 01250000 tail checking free checking validate parameters
2: 01650000 tail checking free checking validate parameters
3: 02f40000 tail checking free checking validate parameters
4: 01600000 tail checking free checking validate parameters
5: 03220000 tail checking free checking validate parameters
6: 03330000 tail checking free checking validate parameters
7: 03210000 tail checking free checking validate parameters
8: 033b0000 tail checking free checking validate parameters
9: 035a0000 tail checking free checking validate parameters
更新windbg
总是有一行错误,难道是我的windbg版本问题?
0:000> !heap
Failed to read heap keySEGMENT HEAP ERROR: failed to initialize the extention
刚学了一招更新windbg版本,试试
其实安装了vs2022,就会安装debug:C:\Program Files (x86)\Windows Kits\10\Debuggers
如何让他保持最新?进入控制面板
点击更改:
勾选红色:
这时当前版本,一会儿看看有啥变化
新版本:
运行看看:
果然不再出现错误了:
0:000> !heap
Heap Address NT/Segment Heap
850000 NT Heap
0:000> bp FreCheck!main
0:000> g
0:000> !heap
Heap Address NT/Segment Heap
850000 NT Heap
e70000 NT Heap
2770000 NT Heap
28a0000 NT Heap
de0000 NT Heap
2a10000 NT Heap
2b10000 NT Heap
2a00000 NT Heap
2ef0000 NT Heap
新版本清爽多了。
继续研究FreCheck
!heap发现,进入main断点后,已经从1个堆变成了9个堆。看看加载了啥模块
已经加载了很多模块。
0:000> k
# ChildEBP RetAddr
00 003ffd08 00b52183 FreCheck!main [C:\cpp\vc2022\FreCheck\FreCheck.cpp @ 4]
01 003ffd28 00b51fca FreCheck!invoke_main+0x33 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78]
02 003ffd84 00b51e5d FreCheck!__scrt_common_main_seh+0x15a [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
03 003ffd8c 00b52208 FreCheck!__scrt_common_main+0xd [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 331]
04 003ffd94 76907ba9 FreCheck!mainCRTStartup+0x8 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp @ 17]
05 003ffda4 77d1c10b KERNEL32!BaseThreadInitThunk+0x19
06 003ffdfc 77d1c08f ntdll!__RtlUserThreadStart+0x2b
07 003ffe0c 00000000 ntdll!_RtlUserThreadStart+0x1b
查看crt堆
0:000> x FreCheck!_crt*
00b51cb0 FreCheck!_CRT_RTC_INIT (void *, void **, int, int, int)
00b51cc0 FreCheck!_CRT_RTC_INITW (void *, void **, int, int, int)
00b53980 FreCheck!_crt_debugger_hook (int)
00b553b4 FreCheck!_crt_atexit (__crt_atexit)
00b553ba FreCheck!_crt_at_quick_exit (__crt_at_quick_exit)
00b55312 FreCheck!_CrtDbgReportW (__CrtDbgReportW)
00b5530c FreCheck!_CrtDbgReport (__CrtDbgReport)
0:000> x FreCheck!*heap*
00b5b00c FreCheck!_imp__HeapFree = <no type information>
00b5b004 FreCheck!_imp__HeapDestroy = <no type information>
00b5b000 FreCheck!_imp__HeapCreate = <no type information>
00b51a51 FreCheck!HeapCreate (_HeapCreate@12)
00b5b020 FreCheck!_imp__GetProcessHeap = <no type information>
00b51a5d FreCheck!HeapAlloc (_HeapAlloc@12)
00b51a63 FreCheck!HeapFree (_HeapFree@12)
00b51a57 FreCheck!HeapDestroy (_HeapDestroy@4)
00b55438 FreCheck!GetProcessHeap (_GetProcessHeap@0)
00b5b008 FreCheck!_imp__HeapAlloc = <no type information>
并没有录像中的_crtHeap
单步到如下位置:
0:000> p
eax=00b5c0a2 ebx=004d1000 ecx=00b5c0a2 edx=00000001 esi=006ff704 edi=006ff7f4
eip=7690d296 esp=006ff6f4 ebp=006ff7f4 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
KERNEL32!HeapCreateStub+0x6:
7690d296 ff25e8129776 jmp dword ptr [KERNEL32!_imp__HeapCreate (769712e8)] ds:002b:769712e8={KERNELBASE!HeapCreate (762c9870)}
0:000> pc
eax=00000000 ebx=004d1000 ecx=00001002 edx=00000000 esi=006ff704 edi=006ff7f4
eip=762c98ab esp=006ff6d0 ebp=006ff6f0 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
KERNELBASE!HeapCreate+0x3b:
762c98ab ff15988a3b76 call dword ptr [KERNELBASE!_imp__RtlCreateHeap (763b8a98)] ds:002b:763b8a98={ntdll!RtlCreateHeap (77d092d0)}
0:000> wt
4 0 [ 0] ntdll!RtlCreateHeap
22 0 [ 1] ntdll!_SEH_prolog4_GS
52 22 [ 0] ntdll!RtlCreateHeap
40 0 [ 1] ntdll!memset
149 62 [ 0] ntdll!RtlCreateHeap
27 0 [ 1] ntdll!RtlDebugCreateHeap
3 0 [ 2] ntdll!RtlCreateHeap
22 0 [ 3] ntdll!_SEH_prolog4_GS
47 22 [ 2] ntdll!RtlCreateHeap
40 0 [ 3] ntdll!memset
162 62 [ 2] ntdll!RtlCreateHeap
11 0 [ 3] ntdll!RtlpHeapGenerateRandomValue32
14 0 [ 4] ntdll!RtlRandomEx
13 0 [ 5] ntdll!RtlRunOnceExecuteOnce
33 0 [ 6] ntdll!RtlRunOnceBeginInitialize
25 33 [ 5] ntdll!RtlRunOnceExecuteOnce
72 58 [ 4] ntdll!RtlRandomEx
17 130 [ 3] ntdll!RtlpHeapGenerateRandomValue32
164 209 [ 2] ntdll!RtlCreateHeap
11 0 [ 3] ntdll!RtlpHeapGenerateRandomValue32
14 0 [ 4] ntdll!RtlRandomEx
13 0 [ 5] ntdll!RtlRunOnceExecuteOnce
33 0 [ 6] ntdll!RtlRunOnceBeginInitialize
25 33 [ 5] ntdll!RtlRunOnceExecuteOnce
72 58 [ 4] ntdll!RtlRandomEx
17 130 [ 3] ntdll!RtlpHeapGenerateRandomValue32
189 356 [ 2] ntdll!RtlCreateHeap
1 0 [ 3] ntdll!NtAllocateVirtualMemory
22 0 [ 3] LHShield
1 0 [ 4] KERNEL32!GetLastErrorStub
3 0 [ 4] KERNELBASE!GetLastError
33 4 [ 3] LHShield
16 0 [ 4] LHShield
7 0 [ 5] LHShield!QAXStart
27 7 [ 4] LHShield
7 0 [ 5] LHShield!QAXStart
12 0 [ 6] LHShield!QAXStart
1 0 [ 7] LHShield
13 1 [ 6] LHShield!QAXStart
14 0 [ 7] KERNELBASE!InitOnceExecuteOnce
13 0 [ 8] ntdll!RtlRunOnceExecuteOnce
33 0 [ 9] ntdll!RtlRunOnceBeginInitialize
25 33 [ 8] ntdll!RtlRunOnceExecuteOnce
21 58 [ 7] KERNELBASE!InitOnceExecuteOnce
17 80 [ 6] LHShield!QAXStart
10 97 [ 5] LHShield!QAXStart
33 114 [ 4] LHShield
5 0 [ 5] LHShield!QAXStart
13 0 [ 6] LHShield!QAXStart
7 13 [ 5] LHShield!QAXStart
41 134 [ 4] LHShield
36 179 [ 3] LHShield
1 0 [ 4] KERNEL32!GetCurrentProcess
2 0 [ 4] KERNELBASE!GetCurrentProcess
49 182 [ 3] LHShield
1 0 [ 4] 0x6e380510
1 0 [ 4] 0x6e380515
2 0 [ 4] ntdll!NtAllocateVirtualMemory
1 0 [ 5] ntdll!Wow64SystemServiceCall
1 0 [ 5] 0x00297000
HEAP[FreCheck.exe]: Heap block at 037705C0 modified at 037705D1 past requested size of 9
1 0 [ 5] LHShield
8 0 [ 5] ntdll!RtlpCheckBusyBlockTail
>> No match on ret
8 0 [ 5] ntdll!RtlpCheckBusyBlockTail
12 0 [ 5] ntdll!RtlpValidateHeapEntry
13 0 [ 6] ntdll!DbgPrint
3 0 [ 7] ntdll!vDbgPrintExWithPrefixInternal
22 0 [ 8] ntdll!_SEH_prolog4_GS
33 22 [ 7] ntdll!vDbgPrintExWithPrefixInternal
9 0 [ 8] ntdll!_alloca_probe_16
16 0 [ 8] ntdll!_alloca_probe
52 47 [ 7] ntdll!vDbgPrintExWithPrefixInternal
25 0 [ 8] ntdll!memcpy
75 72 [ 7] ntdll!vDbgPrintExWithPrefixInternal
9 0 [ 8] ntdll!_vsnprintf
28 0 [ 9] ntdll!_vsnprintf_l
65 0 [ 10] ntdll!_output_l
22 0 [ 11] ntdll!write_char
102 22 [ 10] ntdll!_output_l
22 0 [ 11] ntdll!write_char
139 44 [ 10] ntdll!_output_l
22 0 [ 11] ntdll!write_char
176 66 [ 10] ntdll!_output_l
22 0 [ 11] ntdll!write_char
213 88 [ 10] ntdll!_output_l
22 0 [ 11] ntdll!write_char
379 110 [ 10] ntdll!_output_l
10 0 [ 11] ntdll!write_multi_char
387 120 [ 10] ntdll!_output_l
17 0 [ 11] ntdll!write_string
407 137 [ 10] ntdll!_output_l
9 0 [ 11] ntdll!wctomb_s
25 0 [ 12] ntdll!_wctomb_s_l
10 0 [ 13] ntdll!RtlUnicodeToMultiByteN
10 0 [ 14] ntdll!RtlpGetCodePageData
15 0 [ 15] ntdll!RtlpIsUtf8Process
20 15 [ 14] ntdll!RtlpGetCodePageData
12 35 [ 13] ntdll!RtlUnicodeToMultiByteN
59 0 [ 14] ntdll!RtlUnicodeToCustomCPN
15 94 [ 13] ntdll!RtlUnicodeToMultiByteN
35 109 [ 12] ntdll!_wctomb_s_l
12 144 [ 11] ntdll!wctomb_s
419 293 [ 10] ntdll!_output_l
22 0 [ 11] ntdll!write_string
22 0 [ 12] ntdll!write_char
35 22 [ 11] ntdll!write_string
434 350 [ 10] ntdll!_output_l
9 0 [ 11] ntdll!wctomb_s
。。。。。。
太长省略了,比录像中的长多了,看来版本变化还是很大的,windows越来越复杂。
运行完直接停止了。只能.restart
重新分析:
0:000> !heap
Heap Address NT/Segment Heap
620000 NT Heap
ad0000 NT Heap
2480000 NT Heap
2630000 NT Heap
2720000 NT Heap
29c0000 NT Heap
2710000 NT Heap
2aa0000 NT Heap
2c90000 NT Heap
0:000> dv
i = 0n-858993460
argc = 0n1
argv = 0x0322e4c8
hHeap = 0x036c0000
p = 0x036c05c8 ".???"
0:000> !heap
Heap Address NT/Segment Heap
620000 NT Heap
ad0000 NT Heap
2480000 NT Heap
2630000 NT Heap
2720000 NT Heap
29c0000 NT Heap
2710000 NT Heap
2aa0000 NT Heap
2c90000 NT Heap
36c0000 NT Heap
0:000> !heap -x 0x036c05c8
Entry User Heap Segment Size PrevSize Unused Flags
-----------------------------------------------------------------------------
036c05c0 036c05c8 036c0000 036c0000 28 118 1f busy extra fill
0:000> !heap -a 036c0000
Index Address Name Debugging options enabled
10: 036c0000
Segment at 036c0000 to 036cf000 (00001000 bytes committed)
Flags: 40001062
ForceFlags: 40000060
Granularity: 8 bytes
Segment Reserve: 00100000
Segment Commit: 00002000
DeCommit Block Thres: 00000200
DeCommit Total Thres: 00002000
Total Free Size: 0000013f
Max. Allocation Size: 7ffdefff
Lock Variable at: 036c0258
Next TagIndex: 0000
Maximum TagIndex: 0000
Tag Entries: 00000000
PsuedoTag Entries: 00000000
Virtual Alloc List: 036c009c
Uncommitted ranges: 036c008c
036c1000: 0000e000 (57344 bytes)
FreeList[ 00 ] at 036c00c0: 036c05f0 . 036c05f0
036c05e8: 00028 . 009f8 [104] - free
Segment00 at 036c0000:
Flags: 00000000
Base: 036c0000
First Entry: 036c04a8
Last Entry: 036cf000
Total Pages: 0000000f
Total UnCommit: 0000000e
Largest UnCommit:00000000
UnCommitted Ranges: (1)
Heap entries for Segment00 in Heap 036c0000
address: psize . size flags state (requested size)
036c0000: 00000 . 004a8 [101] - busy (4a7)
036c04a8: 004a8 . 00118 [107] - busy (117), tail fill Internal
036c05c0: 00118 . 00028 [107] - busy (9), tail fill
036c05e8: 00028 . 009f8 [104] free fill
036c0fe0: 009f8 . 00020 [111] - busy (1d)
036c1000: 0000e000 - uncommitted bytes.
0:000> dd 036c05c0
036c05c0 cebb7a5f 1f00c170 baadf00d baadf00d --对堆块的默认填充,用户区9个字节
036c05d0 abababee abababab feeefeab feeefeee --堆尾填充8个ab
036c05e0 00000000 00000000 f6b87b65 0000c156--下一块的HEAP_ENTRY
036c05f0 036c00c0 036c00c0 feeefeee feeefeee --这是一个链表,指针一样,说明目前list为1
036c0600 feeefeee feeefeee feeefeee feeefeee --释放填充
036c0610 feeefeee feeefeee feeefeee feeefeee
036c0620 feeefeee feeefeee feeefeee feeefeee
036c0630 feeefeee feeefeee feeefeee feeefeee
0:000> dt _HEAP_ENTRY 036c05c0
ntdll!_HEAP_ENTRY
+0x000 UnpackedEntry : _HEAP_UNPACKED_ENTRY
+0x000 Size : 0x7a5f 无效数据,乱了。heap -x会帮助解码。长度是28
+0x002 Flags : 0xbb ''
+0x003 SmallTagIndex : 0xce ''
+0x000 SubSegmentCode : 0xcebb7a5f
+0x004 PreviousSize : 0xc170
+0x006 SegmentOffset : 0 ''
+0x006 LFHFlags : 0 ''
+0x007 UnusedBytes : 0x1f ''
+0x000 ExtendedEntry : _HEAP_EXTENDED_ENTRY
+0x000 FunctionIndex : 0x7a5f
+0x002 ContextValue : 0xcebb
+0x000 InterceptorValue : 0xcebb7a5f
+0x004 UnusedBytesLength : 0xc170
+0x006 EntryOffset : 0 ''
+0x007 ExtendedBlockSignature : 0x1f ''
+0x000 Code1 : 0xcebb7a5f
+0x004 Code2 : 0xc170
+0x006 Code3 : 0 ''
+0x007 Code4 : 0x1f ''
+0x004 Code234 : 0x1f00c170
+0x000 AgregateCode : 0x1f00c170`cebb7a5f
0:000> !gflag
Current NtGlobalFlag contents: 0x00000070
htc - Enable heap tail checking --所以末尾填充8个字节的ab,ab
hfc - Enable heap free checking
hpc - Enable heap parameter checking
每个堆块都有额外开销,比如上面我们申请了9个字节,实际分了40个字节
第一个错误:tail检查出错

执行几个循环,已经要到边界了,尾巴将被覆盖。
覆盖到最后一个时出错了:
0:000> p
HEAP[FreCheck.exe]: Heap block at 036C05C0 modified at 036C05D1 past requested size of 9
WARNING: This break is not a step/trace completion.
The last command has been cleared to prevent
accidental continuation of this unrelated event.
Check the event, location and thread before resuming.
(3e20.472c): Break instruction exception - code 80000003 (first chance)
eax=00380000 ebx=036c05d1 ecx=0062453c edx=0019f4a0 esi=036c05c0 edi=00000009
eip=77d94143 esp=0019f618 ebp=0019f628 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
ntdll!RtlpCheckBusyBlockTail+0x1ae:
77d94143 cc int 3
0:000> k
# ChildEBP RetAddr
00 0019f628 77d4ea6a ntdll!RtlpCheckBusyBlockTail+0x1bd
01 0019f63c 77d96593 ntdll!RtlpValidateHeapEntry+0x789bb
02 0019f69c 77cff846 ntdll!RtlDebugFreeHeap+0xc6
03 0019f7d8 77d40add ntdll!RtlpFreeHeap+0xd6
04 0019f834 77cff706 ntdll!RtlpFreeHeapInternal+0x796
05 0019f854 00b51912 ntdll!RtlFreeHeap+0x46
06 0019f958 00b52183 FreCheck!main+0x92 [C:\cpp\vc2022\FreCheck\FreCheck.cpp @ 12]
07 0019f978 00b51fca FreCheck!invoke_main+0x33 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78]
08 0019f9d4 00b51e5d FreCheck!__scrt_common_main_seh+0x15a [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
09 0019f9dc 00b52208 FreCheck!__scrt_common_main+0xd [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 331]
0a 0019f9e4 76907ba9 FreCheck!mainCRTStartup+0x8 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp @ 17]
0b 0019f9f4 77d1c10b KERNEL32!BaseThreadInitThunk+0x19
0c 0019fa4c 77d1c08f ntdll!__RtlUserThreadStart+0x2b
0d 0019fa5c 00000000 ntdll!_RtlUserThreadStart+0x1b
0:000> !heap -x 0x036c05c8
Entry User Heap Segment Size PrevSize Unused Flags
-----------------------------------------------------------------------------
036c05c0 036c05c8 036c0000 036c0000 28 118 1f busy extra fill
还是BUSY,没有释放。释放之前的检查堆出错了RtlpCheckBusyBlockTail
手工出发检查:
0:000> !heap 036c0000 -v
Index Address Name Debugging options enabled
10: 036c0000
Segment at 036c0000 to 036cf000 (00001000 bytes committed)
Flags: 40001062
ForceFlags: 40000060
Granularity: 8 bytes
Segment Reserve: 00100000
Segment Commit: 00002000
DeCommit Block Thres: 00000200
DeCommit Total Thres: 00002000
Total Free Size: 0000013f
Max. Allocation Size: 7ffdefff
Lock Variable at: 036c0258
Next TagIndex: 0000
Maximum TagIndex: 0000
Tag Entries: 00000000
PsuedoTag Entries: 00000000
Virtual Alloc List: 036c009c
Uncommitted ranges: 036c008c
FreeList[ 00 ] at 036c00c0: 036c05f0 . 036c05f0 (1 block )
Heap block at 036c05c0 modified at 036c05d1 past requested size of 9 (5 * 8 - 1f)
##The above errors were found in segment at 0x036C0000
第二个错误:堆块释放出错
0:000> g
HEAP[FreCheck.exe]: Invalid address specified to RtlFreeHeap( 036C0000, 036C05C8 )
(3e20.472c): Break instruction exception - code 80000003 (first chance)
eax=00380000 ebx=036c05c0 ecx=0062453c edx=0019f4c0 esi=036c0000 edi=00000000
eip=77d4eace esp=0019f630 ebp=0019f63c iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
ntdll!RtlpValidateHeapEntry+0x78a1f:
77d4eace cc int 3
0:000> kn
# ChildEBP RetAddr
00 0019f63c 77d96593 ntdll!RtlpValidateHeapEntry+0x78a1f
01 0019f69c 77cff846 ntdll!RtlDebugFreeHeap+0xc6
02 0019f7d8 77d40add ntdll!RtlpFreeHeap+0xd6
03 0019f834 77cff706 ntdll!RtlpFreeHeapInternal+0x796
04 0019f854 00b51912 ntdll!RtlFreeHeap+0x46
05 0019f958 00b52183 FreCheck!main+0x92 [C:\cpp\vc2022\FreCheck\FreCheck.cpp @ 12]
06 0019f978 00b51fca FreCheck!invoke_main+0x33 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78]
07 0019f9d4 00b51e5d FreCheck!__scrt_common_main_seh+0x15a [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
08 0019f9dc 00b52208 FreCheck!__scrt_common_main+0xd [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 331]
09 0019f9e4 76907ba9 FreCheck!mainCRTStartup+0x8 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp @ 17]
0a 0019f9f4 77d1c10b KERNEL32!BaseThreadInitThunk+0x19
0b 0019fa4c 77d1c08f ntdll!__RtlUserThreadStart+0x2b
0c 0019fa5c 00000000 ntdll!_RtlUserThreadStart+0x1b
最终堆被destroy
0:000> !heap
Heap Address NT/Segment Heap
620000 NT Heap
ad0000 NT Heap
2630000 NT Heap
29c0000 NT Heap
这种检查不是实时的,释放时才发现有问题。
要想实时,就用页堆
原理时放到页尾,搞一个栅栏页,覆盖到了栅栏就实时触发中断。
周六学习辛苦,页堆的详细情况,且听下回分解。