One: background
1. Tell a story
To be honest, I was not prepared to interpret this dump in the previous article, but it moved me deeply in two ways.
- I have heard countless times that Unity can be used for game development, but it is not as good as seeing.
- There are many names in the game that are unique to Jin Yong's martial arts novels, which are so pleasing to the eye.
000000df315978a8 0 3 玉骨扇
000000df31597cd8 0 3 云龙枪
000000df31596d88 0 3 阴风爪
000000df315967a8 0 4 雪魂丝链
000000df31596ad0 0 4 乙木神剑
000000df31596040 0 3 星耀冠
000000df31595328 0 3 乌金锤
...
So for such a good dump, I have to leave something for it.
Well, having said that, this fate started last month when a friend said that its program takes a very large virtual memory, and how to solve it is consulted, as shown in the figure below:
Regardless of what the problem is, it’s not wrong to grab a few more dumps. After several tossings, I sent a dump.
Two: Windbg analysis
1. Where is the leak?
Analyze the memory problem, or that sentence, divide it into two to see which part of the memory leak (managed or unmanaged).
!address -summary
look at the total memory of the process, use the 06142b33f438d7 command.
0:087> !address -summary
--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free 458 7ffe`9e6a8000 ( 127.995 TB) 100.00%
Heap 48514 1`005fd000 ( 4.006 GB) 72.51% 0.00%
<unknown> 2504 0`2c6ad000 ( 710.676 MB) 12.56% 0.00%
Stack 504 0`2a000000 ( 672.000 MB) 11.88% 0.00%
Image 410 0`0a971000 ( 169.441 MB) 3.00% 0.00%
Other 18 0`001dc000 ( 1.859 MB) 0.03% 0.00%
TEB 168 0`00150000 ( 1.312 MB) 0.02% 0.00%
PEB 1 0`00001000 ( 4.000 kB) 0.00% 0.00%
--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_PRIVATE 51581 1`5130f000 ( 5.269 GB) 95.36% 0.00%
MEM_IMAGE 416 0`0aa6b000 ( 170.418 MB) 3.01% 0.00%
MEM_MAPPED 122 0`05bce000 ( 91.805 MB) 1.62% 0.00%
--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE 458 7ffe`9e6a8000 ( 127.995 TB) 100.00%
MEM_COMMIT 51465 1`1c741000 ( 4.445 GB) 80.45% 0.00%
MEM_RESERVE 654 0`45207000 ( 1.080 GB) 19.55% 0.00%
We know 06142b33f4392a from the hexagram MEM_COMMIT=4.4G
look at the memory usage of the managed heap. You can use the command !eeheap -gc
.
0:087> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x000000df3118dc48
generation 1 starts at 0x000000df3118b098
generation 2 starts at 0x000000df30fc1000
ephemeral segment allocation context: none
segment begin allocated size
000000df30fc0000 000000df30fc1000 000000df3178cae0 0x7cbae0(8174304)
Large object heap starts at 0x000000df40fc1000
segment begin allocated size
000000df40fc0000 000000df40fc1000 000000df410637b8 0xa27b8(665528)
Total Size: Size: 0x86e298 (8839832) bytes.
------------------------------
GC Heap Size: Size: 0x86e298 (8839832) bytes.
GC Heap Size= 8839832 Byte = 8M
from the hexagram, I'll go, it's just that, kind of kidding! ! ! 🤣🤣🤣🤣, obviously this is an unmanaged memory leak. Now that the direction has been set, let’s check the unmanaged area!
2. Explore non-custodial leaks
According to experience, to find unmanaged leaks, first look at the loader heap. Many programs are often caused by dynamically creating too many assemblies, such as classic Castle and XmlSerializer. Friends who are interested can find this information online. Use the
!eeheap -loader
command to view here.
0:087> !eeheap -loader
--------------------------------------
Jit code heap:
LoaderCodeHeap: 0000000000000000(0:0) Size: 0x0 (0) bytes.
Total size: Size: 0x0 (0) bytes.
--------------------------------------
Module Thunk heaps:
Module 00007ffda5fa1000: Size: 0x0 (0) bytes.
Module 00007ffd485c4148: Size: 0x0 (0) bytes.
Module 00007ffda2631000: Size: 0x0 (0) bytes.
Module 00007ffda5331000: Size: 0x0 (0) bytes.
Module 00007ffdac621000: Size: 0x0 (0) bytes.
Module 00007ffdac4e1000: Size: 0x0 (0) bytes.
Module 00007ffda48b1000: Size: 0x0 (0) bytes.
Module 00007ffda1791000: Size: 0x0 (0) bytes.
Module 00007ffd487b1858: Size: 0x0 (0) bytes.
Total size: Size: 0x0 (0) bytes.
--------------------------------------
Module Lookup Table heaps:
Module 00007ffda5fa1000: Size: 0x0 (0) bytes.
Module 00007ffd485c4148: Size: 0x0 (0) bytes.
Module 00007ffda2631000: Size: 0x0 (0) bytes.
Module 00007ffda5331000: Size: 0x0 (0) bytes.
Module 00007ffdac621000: Size: 0x0 (0) bytes.
Module 00007ffdac4e1000: Size: 0x0 (0) bytes.
Module 00007ffda48b1000: Size: 0x0 (0) bytes.
Module 00007ffda1791000: Size: 0x0 (0) bytes.
Module 00007ffd487b1858: Size: 0x0 (0) bytes.
Total size: Size: 0x0 (0) bytes.
--------------------------------------
Total LoaderHeap size: Size: 0x99000 (626688) bytes total, 0x2000 (8192) bytes wasted.
=======================================
Look at the output: Total LoaderHeap size= 626K
. It seems that you have run out of time this time. Then enter the hard mode and look at the Windows NT heap. Here we use the !heap -s
command.
0:087> !heap -s
************************************************************************************************************************
NT HEAP STATS BELOW
************************************************************************************************************************
LFH Key : 0xb6c37b3e3a4a189e
Termination on corruption : ENABLED
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast
(k) (k) (k) (k) length blocks cont. heap
-------------------------------------------------------------------------------------
000000df2e680000 00000002 4145084 4130108 4144304 1537 775 260 1 4 LFH
000000df2e1f0000 00008000 64 4 64 2 1 1 0 0
000000df2e830000 00001002 1860 172 1080 15 5 2 0 0 LFH
000000df2ec80000 00001002 1860 236 1080 5 7 2 0 0 LFH
000000df309e0000 00001002 60 8 60 2 1 1 0 0
000000df30bb0000 00041002 60 8 60 5 1 1 0 0
000000df49bd0000 00001002 840 44 60 3 3 1 0 0 LFH
000000df49b20000 00041002 1860 96 1080 8 3 2 0 0 LFH
000000df30b40000 00001002 60 20 60 9 2 1 0 0
000000df30b30000 00001002 1860 152 1080 11 8 2 0 0 LFH
000000df4bbb0000 00001002 3904 1292 3124 49 6 3 0 0 LFH
000000df89920000 00001002 1860 372 1080 14 7 2 0 0 LFH
000000df89be0000 00001006 1860 280 1080 23 2 2 0 0 LFH
000000df56f40000 00001006 32372 26204 31592 1434 21 6 0 6b LFH
000000df56f10000 00001006 1860 176 1080 21 3 2 0 0 LFH
000000df89ac0000 00001006 3904 2160 3124 67 4 3 0 2e LFH
-------------------------------------------------------------------------------------
From the output information: the memory of the original program has been heap=000000df2e680000
, so dig into it. Here, use the !heap -stat -h 000000df2e680000
command to look at the statistics of the heap.
0:087> !ext.heap -stat -h 000000df2e680000
heap @ 000000df2e680000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
2000 4cfd2 - 99fa4000 (68.76)
58 9d7492 - 36201230 (24.17)
12c 267e8 - 2d1c3e0 (1.26)
21d1 c46 - 19f0b26 (0.72)
4020 634 - 18dc680 (0.69)
a0 26d00 - 1842000 (0.68)
a 1d3ebb - 124734e (0.51)
10 f8d99 - f8d990 (0.43)
6 16adae - 881214 (0.24)
b b3508 - 7b4758 (0.22)
7 115125 - 793803 (0.21)
5 17b833 - 7698ff (0.21)
c 86027 - 6481d4 (0.18)
9 afef9 - 62f6c1 (0.17)
d 6a80f - 5688c3 (0.15)
f 4f5a9 - 4a64e7 (0.13)
e 54814 - 49f118 (0.13)
8 8b092 - 458490 (0.12)
13 3139b - 3a7481 (0.10)
15 25d06 - 31a17e (0.09)
From the output information, this heap is mainly size=2000
and size=58
. After all, they 68.76 + 24.17 = 92.93
for 06142b33f43af1, so it is necessary to dig them. Next, use the command !heap -flt s 2000
find out the first addresses of all these blocks in the heap.
0:087> !ext.heap -flt s 2000
_HEAP @ df2e680000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
000000df2e702dd0 0201 0000 [00] 000000df2e702de0 02000 - (busy)
000000df2e72c7e0 0201 0201 [00] 000000df2e72c7f0 02000 - (busy)
000000df517400c0 0201 0201 [00] 000000df517400d0 02000 - (busy)
000000df517420d0 0201 0201 [00] 000000df517420e0 02000 - (busy)
000000df517440e0 0201 0201 [00] 000000df517440f0 02000 - (busy)
000000df517460f0 0201 0201 [00] 000000df51746100 02000 - (busy)
000000df51748100 0201 0201 [00] 000000df51748110 02000 - (busy)
000000df5174a110 0201 0201 [00] 000000df5174a120 02000 - (busy)
000000df5174c120 0201 0201 [00] 000000df5174c130 02000 - (busy)
000000df5174e130 0201 0201 [00] 000000df5174e140 02000 - (busy)
000000df51750140 0201 0201 [00] 000000df51750150 02000 - (busy)
...
The HEAP_ENTRY above is the first address of the block. Since there are probably 4cfd2=31.5w
such blocks, it is impossible to list them one by one. The next step is to use dc to observe the contents of the memory blocks of these blocks to find the rules. Manually, it must be too troublesome, or With the help of the script, here is the first 1w to view.
function show_all_blocksize() {
var output = exec("!ext.heap -flt s 58").Take(10000);
for (var line of output) {
var heap_entry_address = line.trim().split(' ')[0];
if (heap_entry_address.indexOf("00") == -1) continue;
show_heap_entry(heap_entry_address);
}
}
function show_heap_entry(heap_entry_address) {
var pageIndex = (index++);
var path = ".writemem D:\\file\\"+ pageIndex + ".txt " + heap_entry_address + " L?0x58";
var output = exec(path);
log("pageIndex=" + pageIndex);
}
After executing the script to generate txt, the screenshot is as follows:
Through observation, it is found that there is a large amount of user information in this heap, and then I use this information to verify my friends.
After simple communication with my friends, I can only help here, and the case is closed here.
Three: Summary
The reason for this accident was the unmanaged leak caused by Lua's failure to release reasonable memory after C# called Lua. How to release it at the code level depends on the good luck of friends.
The last little easter egg, my friend is too polite.
I have never seen such a big red envelope. I actually accepted it. Backhand I gave the company's R&D partner a cup of afternoon tea . I would like to say thank you to my friends here😘😘😘
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。