在垃圾收集之前检测堆损坏

时间:2011-10-31 00:33:01

标签: c# heap-corruption

我正在使用CDB(Microsoft控制台调试程序)和WinDbg尝试在P/Invoke将堆损坏发生到ReadFile时强制中断。我从文本文件中读取的字节比我分配给chBuf数组的字节多得多。调试器在GC.Collect之后才会看到访问冲突,对我来说太晚。在运行我的程序之前,我运行

gflags -p /enable testheap.exe /unaligned

效果似乎毫无用处。我编写了这个小测试程序来应用我发现的调试更大的商业程序,这个程序存在堆腐败问题。

我还尝试使用Application Verifier和MDA callbackOnCollectedDelegate调试DebugDiag但没有成功。我是否使用gflags应该在ReadFile之后立即检测到堆损坏?

代码:

    namespace TestHeap

    public partial class Form1 : Form
    {
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess,
            uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
            uint dwFlagsAndAttributes, IntPtr hTemplateFile);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool ReadFile(SafeFileHandle hFile, [Out] byte[] lpBuffer,
            uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);

        string fileName = "testHeap.txt";
        const uint GENERIC_READ = 0x80000000;
        const uint OPEN_EXISTING = 3;
        SafeFileHandle sh;
        byte[] chBuf = new byte[8];

        public Form1()
        {
            InitializeComponent();
        }

        private void testBtn_Click(object sender, EventArgs e)
        {
            bool nStat;
            uint bytesToRead = 1025;
            uint bytesRead = 0;

            if (!(nStat = ReadFile( sh, chBuf, bytesToRead, out bytesRead, IntPtr.Zero)))
                Debug.Print("testBtn_Click error in ReadFile, nStat = {0}", nStat);
            MessageBox.Show(string.Format("After ReadFile, bytesToRead = {0},\n bytes read = {1}", bytesToRead, bytesRead));
            GC.Collect();
            MessageBox.Show("testBtn_Click end, after GC.Collect");
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            sh = CreateFile(fileName, GENERIC_READ, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
        }
    }
}

1 个答案:

答案 0 :(得分:1)

只是一个猜测,但我相信意外的gflags行为是由这一行引起的:

byte[] chBuf = new byte[8];

由于chBuf由CLR管理,因此gflags无法在其后面填充填充模式来检测缓冲区溢出。尝试将其更改为:

IntPtr chBuf = Marshal.AllocHGlobal(8);

这样你就可以在非托管堆中进行分配。 Gflags应该能够使用它。此外,您可能需要更改ReadFile的签名才能正常工作:

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadFile(SafeFileHandle hFile, [Out] IntPtr lpBuffer,
uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);