There are two popular ways to assert in drivers. One can use the the regular ASSERT macro (int 3) or the relatively newer NT_ASSERT macro (int 2C). Since ASSERT calls RtlAssert, when the debugger breaks in, code would be several frames off of where the ASSERT was. If you use NT_ASSERT however, the debugger would stop right where NT_ASSERT was called in the code. That is a nice convenience since you do not have to issue several gu‘s or press Shift+F11’s frantically until you see your source code.
However you cannot step over (or run over) an NT_ASSERT, you have to type gh (Go with Exception Handled) or gn (Go with Exception Not Handled). If you do a step over in the debugger, it would fire the same NT_ASSERT again. So in order to get control in debugger right after the NT_ASSERT you may have to setup a breakpoint right after the NT_ASSERT and issue a gh or use gh =<Start Address> syntax and point Start Address to the address of code right after NT_ASSERT.
Additionally because NT_ASSERT leads to an STATUS_ASSERTION_FAILURE exception, if you typed gn twice when you meant gh1, end of computing greets you with a blue screen and if a kernel debugger is attached – as a bonus, nice line of dots are printed for you to count2. Another possibility is that your exception handlers/filters may see this exception and lead code execution to branch in unexpected ways, which may lead to inexplicable brainteasers for the debugging soul or eventual system crash (such as a REGISTRY_FILTER_DRIVER_EXCEPTION bugcheck with first parameter set to STATUS_ASSERTION_FAILURE).
Moving beyond those differences between ASSERT and NT_ASSERT, one of the handy things you can do with NT_ASSERT is to ignore a particular NT_ASSERT by issuing an ahi and WinDbg will ignore the assertion failure. When a particular NT_ASSERT is on the debugger ignore list, the debugger outputs notify that the assertion failed but does not break in. Nice. Anyone that has fought3 with overactive assertions with ASSERT macro, would appreciate this.
- 1 2