.net 我如何通过windbg从“System.Drawing.Bitmap”中提取图像到文件中?

fae0ux8s  于 2023-06-07  发布在  .NET
关注(0)|答案(3)|浏览(431)

我有一个内存转储文件,其中似乎有'System.Drawing.Bitmap'对象的内存泄漏。我有多个该类型的对象和!gcroot不能帮助我确定漏洞在哪里。
示例输出:
DOMAIN(0071D148):HANDLE(Pinned):1513e8:Root:03335250(System.Object[])-> 0248e8ec(System.Drawing.Bitmap)
我有一个想法,从内存转储中提取图像到一个图像文件中,然后,当我可以看到什么是泄漏的图像,我可以检查源代码周围的特定位图的创建。
那么,如何将内存写入一个文件,使我可以在图像查看器中打开并查看Bitmap对象保存的图像呢?
此外,如果你有其他的想法如何确定泄漏源,我很乐意听到他们。
谢谢

1tuwyuhd

1tuwyuhd1#

System.Drawing.Bitmap是一个很小的托管对象。它 Package 了一个由非托管GDI+ API返回的句柄,存储在私有 nativeImage 字段中。从该句柄中查找位图数据是一种大海捞针的练习。位图数据也不与图像文件格式兼容,只有Bitmap::保存()调用可以做到这一点,运行所需的图像编码器。
把这个想法划掉。
Bitmap对象的内存问题是非常常见的。太多的程序员忽略了Bitmap继承了IDisposable。你可以写很多.NET程序,但从来没有调用过Dispose()或使用 using 语句,程序运行得很好。垃圾收集者让他们远离麻烦。然而Bitmap类是一个单一的.NET类,它不能再工作了。问题是它太小了。在触发垃圾收集之前,您可以创建成千上万个垃圾。还不足以让垃圾回收器释放非托管GDI+句柄。因此,程序运行非常繁重,使用了大量非托管内存。当程序在32位模式下运行时,很可能会发生OOM崩溃。或在64位模式下提交大小为GB
在迷失在Windbg中之前,首先仔细检查程序的源代码。并验证是否可以将 every Bitmap变量与相应的Dispose()调用或 using 语句配对。注意一些事情,比如在分配PictureBox.Image属性时不使用调用前一个图像的Dispose()方法的代码。NET内存分析器是调试此问题的更好工具。

62lalag4

62lalag42#

如果你真的想做的话,dump all bitmaps from memory似乎仍然是可能的。
步骤:

  1. !dumpheap -short -type System.Drawing.Bitmap
  2. !do <address>
    1.查找属性nativeImage的偏移量
  3. dps(等于dps poi(<address>+<offset>)?)给你类型
  4. dt <address> <type>--> InternalBitmap属性
  5. dt <internalAddress> <internalType>--> Bmp属性
  6. dt <bmpAddress> <bmpType>提供Scan 0属性,即起始地址
    我只是不知道如何计算位图的大小,这将需要做一个.writemem。有宽度和高度,可能PixelFormat可以用来计算每个像素的位数。
qni6mghb

qni6mghb3#

感谢托马斯的参考dump all bitmaps from memory,我找到了一种将位图保存到文件的方法。
当我按照帖子中的步骤操作时,我在第5步遇到了一个问题,Symbol GdiPlus!找不到GpBitmap。

0:000> !do 00000234`ba162b80
Name:        System.Drawing.Bitmap
MethodTable: 00007ffb854ebed8
EEClass:     00007ffb854f2fd0
Size:        48(0x30) bytes
File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ffbb2545dd8  40005ba        8        System.Object  0 instance 0000000000000000 __identity
00007ffbb25c31f8  400018e       20        System.IntPtr  1 instance      234fcaccb60 nativeImage
00007ffbb254aaa0  400018f       10        System.Byte[]  0 instance 0000000000000000 rawData
00007ffbb2545dd8  4000190       18        System.Object  0 instance 00000234ba28e908 userData
00007ffb854eac20  400009c       58 System.Drawing.Color  1   static 0000000000000000 defaultTransparentColor

0:000> dps 234fcaccb60
00000234`fcaccb60  00007ffb`ca656618 GdiPlus!GpBitmap::`vftable'
00000234`fcaccb68  00000000`676d4931
00000234`fcaccb70  00000000`00000000
00000234`fcaccb78  ffffffff`00000001
00000234`fcaccb80  00000234`91eaf0a0
00000234`fcaccb88  00000000`00000001
00000234`fcaccb90  00007ffb`ca656588 GdiPlus!EpScanBitmap::`vftable'
00000234`fcaccb98  00000000`00000000
00000234`fcaccba0  00000000`00000000
00000234`fcaccba8  00000000`00000000
00000234`fcaccbb0  00000000`00000000
00000234`fcaccbb8  00000000`00000000
00000234`fcaccbc0  00000000`00000000
00000234`fcaccbc8  00000006`00000000
00000234`fcaccbd0  00000000`00000001
00000234`fcaccbd8  00000234`fcc9e850
0:000> dt 234fcaccb60 GdiPlus!GpBitmap
Symbol GdiPlus!GpBitmap not found.

我在stackoverflow上发现了几篇有同样问题的文章。但没有答案如何解决这个问题。
保存图像数据的关键是找到原始数据的地址,即位图的Scan 0。
从帖子中,我们可以知道路线如下:位图对象地址-> nativeImage对象地址-> CopyOnWriteBitmap对象地址-> GpMemoryBitmapobject对象地址-> Scan 0地址。
虽然我在命令上有错误:dt 234fcaccb60 GdiPlus!GpBitmap,我仍然可以找到CopyOnWriteBitmap对象地址。并根据CopyOnWriteBitmap对象地址找出GpMemoryBitmap对象地址。最后找到Scan 0地址。因为内存布局是固定的。使用dt命令,我们可以很容易地看到CopyOnWriteBitmap对象地址和GpMemoryBitmapobject对象地址。但是没有dt命令,我们仍然可以根据帖子看到地址位置。

0:000> !do 00000234`ba162b80
Name:        System.Drawing.Bitmap
MethodTable: 00007ffb854ebed8
EEClass:     00007ffb854f2fd0
Size:        48(0x30) bytes
File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ffbb2545dd8  40005ba        8        System.Object  0 instance 0000000000000000 __identity
00007ffbb25c31f8  400018e       20        System.IntPtr  1 instance      234fcaccb60 nativeImage
00007ffbb254aaa0  400018f       10        System.Byte[]  0 instance 0000000000000000 rawData
00007ffbb2545dd8  4000190       18        System.Object  0 instance 00000234ba28e908 userData
00007ffb854eac20  400009c       58 System.Drawing.Color  1   static 0000000000000000 defaultTransparentColor

0:000> dps 234fcaccb60
00000234`fcaccb60  00007ffb`ca656618 GdiPlus!GpBitmap::`vftable'
00000234`fcaccb68  00000000`676d4931
00000234`fcaccb70  00000000`00000000
00000234`fcaccb78  ffffffff`00000001
00000234`fcaccb80  00000234`91eaf0a0 ====================>CopyOnWriteBitmap
00000234`fcaccb88  00000000`00000001
00000234`fcaccb90  00007ffb`ca656588 GdiPlus!EpScanBitmap::`vftable'
00000234`fcaccb98  00000000`00000000
00000234`fcaccba0  00000000`00000000
00000234`fcaccba8  00000000`00000000
00000234`fcaccbb0  00000000`00000000
00000234`fcaccbb8  00000000`00000000
00000234`fcaccbc0  00000000`00000000
00000234`fcaccbc8  00000006`00000000
00000234`fcaccbd0  00000000`00000001
00000234`fcaccbd8  00000234`fcc9e850

0:000> dps 00000234`91eaf0a0
00000234`91eaf0a0  00007ffb`ca6567a8 GdiPlus!CopyOnWriteBitmap::`vftable'
00000234`91eaf0a8  0069006b`00000001
00000234`91eaf0b0  ffffffff`ffffffff
00000234`91eaf0b8  00000000`ffffffff
00000234`91eaf0c0  00000000`00000000
00000234`91eaf0c8  00000000`00000000
00000234`91eaf0d0  00000000`020007d0 dog_windows_x64_3146946!Ordinal9+0x7d0
00000234`91eaf0d8  00000001`00000004
00000234`91eaf0e0  00000000`00000000
00000234`91eaf0e8  00000000`00000000
00000234`91eaf0f0  00000000`00000000
00000234`91eaf0f8  00000234`fcc50a10 ====================>GpMemoryBitmap
00000234`91eaf100  00790073`00000000
00000234`91eaf108  00000000`00000000
00000234`91eaf110  00000000`00000000
00000234`91eaf118  00000000`00000000

0:000> dps 00000234`fcc50a10
00000234`fcc50a10  00007ffb`ca6568a8 GdiPlus!GpMemoryBitmap::`vftable'
00000234`fcc50a18  00007ffb`ca656860 GdiPlus!GpMemoryBitmap::`vftable'
00000234`fcc50a20  00007ffb`ca6567f0 GdiPlus!GpMemoryBitmap::`vftable'
00000234`fcc50a28  00007ffb`ca6567c0 GdiPlus!GpMemoryBitmap::`vftable'
00000234`fcc50a30  00000bb8`00001000 ====================>Height,Width
00000234`fcc50a38  00030803`00001000 ====================>PixelFormat, Stride
00000234`fcc50a40  00000234`98c30000 ====================>Scan0
00000234`fcc50a48  00000000`00020000
00000234`fcc50a50  ffffffff`00000001
00000234`fcc50a58  00400000`ffffffff
00000234`fcc50a60  40580000`00000000
00000234`fcc50a68  40580000`00000000
00000234`fcc50a70  00000000`00000001
00000234`fcc50a78  00000234`dd0dbe00
00000234`fcc50a80  00000000`00000000
00000234`fcc50a88  00000bb8`00001000

0:000> !address 00000234`98c30000

Usage:                  <unknown>
Base Address:           00000234`98c30000
End Address:            00000234`997e8000
Region Size:            00000000`00bb8000 (  11.719 MB)
State:                  00001000          MEM_COMMIT
Protect:                00000004          PAGE_READWRITE
Type:                   00020000          MEM_PRIVATE
Allocation Base:        00000234`98c30000
Allocation Protect:     00000004          PAGE_READWRITE

Content source: 1 (target), length: bb8000

0:000> .writemem D:\image.raw 00000234`98c30000 L0xbb8000

将图像数据保存到image.raw后,我们可以使用Photoshop打开它(输入宽度,高度,我们从最后一个dps命令中获得)并将其保存为bmp/png/jpg。

相关问题