Author Topic: NtDll errors: How does the kernel know the difference between read and write?  (Read 7606 times)

jj2007

  • Member
  • *****
  • Posts: 7540
  • Assembler is fun ;-)
    • MasmBasic
Exception C0000005 uses this string:
Code: [Select]
The instruction at "0x%08lx" referenced memory at "0x%08lx". The memory could not be "%s"
Any idea how NtDll knows whether it was read or write? The exception code is the same...

qWord

  • Member
  • *****
  • Posts: 1454
  • The base type of a type is the type itself
    • SmplMath macros
AFAIK this information is supplied by the processor through the error code of the corresponding (exception-) interrupt (GP,PF,...)
MREAL macros - when you need floating point arithmetic while assembling!

zooba

  • Guest
According to Intel's System Programming Guide there is an extra error code included with the exception, but this is zero for most (possibly all; I didn't check) simple memory accesses and does not specify whether it was a read or a write. In effect, there is no way to tell directly from the exception whether it was a read or a write that caused it. Indirectly, you could look at the instruction that caused it (the value of the IP is included with the exception) to see whether it was reading or writing (or some other operation which may also cause a GP fault).

I haven't looked into the Windows code, but I do know that the divide error exception (which could be a divide by zero or an overflow) is determined by decoding the instruction and checking register values to find the original value of the denominator. I assume that a similar process is used for a GP fault; looking up whether the instruction would have been reading or writing (or possibly another cause - the guide lists 31 possible causes, some of which are "everything listed in chapter/section #").

Cheers,
Zooba  :t

Edit to clarify my first paragraph, which was really poorly worded.
« Last Edit: May 28, 2012, 01:52:40 PM by zooba »

dedndave

  • Member
  • *****
  • Posts: 8734
  • Still using Abacus 2.0
    • DednDave
maybe it looks at the instruction that generated the exception ?

MichaelW

  • Global Moderator
  • Member
  • *****
  • Posts: 1209
Perhaps the exception handler is calling IsBad****Ptr.
Well Microsoft, here’s another nice mess you’ve gotten us into.

zooba

  • Guest
Perhaps the exception handler is calling IsBad****Ptr.

Oh god I hope not... the IsBad***Ptr functions set up an exception handler and return TRUE if it is triggered, but I'm not sure if that works within another handler or even from kernel mode.

Those functions are not recommended for use, since the result is unreliable and the side effects potentially significant, so I'd be very surprised if they were being used here.

Tedd

  • Member
  • ***
  • Posts: 353
  • Procrastinor Extraordinaire
Page-fault exceptions (CPU generated) provide an 'error code' that contains flags, including one to indicate whether the exception was caused by a read or write.
The OS exception handler can use this to react accordingly.
In this case, NTDLL pushes the appropriate string and creates its own 'exception' code.
Potato2

MichaelW

  • Global Moderator
  • Member
  • *****
  • Posts: 1209
Perhaps the exception handler is calling IsBad****Ptr.

Oh god I hope not... the IsBad***Ptr functions set up an exception handler and return TRUE if it is triggered, but I'm not sure if that works within another handler or even from kernel mode.

Those functions are not recommended for use, since the result is unreliable and the side effects potentially significant, so I'd be very surprised if they were being used here.

Well, considering that Microsoft created those functions I have doubts that they would shy away from using them as part of a “postmortem” investigation.


Well Microsoft, here’s another nice mess you’ve gotten us into.

zooba

  • Guest
Well, considering that Microsoft created those functions I have doubts that they would shy away from using them as part of a “postmortem” investigation.

There are plenty of reasons why handling a GP fault isn't "postmortem" - guard pages are a prime example - and the Intel manual specifically states that this interrupt is continuable.

And I'm sure whoever created those functions (I'd guess it was a single developer, surprising as that may seem) would be embarrassed by them now, but unfortunately they shipped and it's near impossible to remove APIs later. They are quite clearly marked as deprecated, though, and have been for a number of years.

MichaelW

  • Global Moderator
  • Member
  • *****
  • Posts: 1209
Well, considering that Microsoft created those functions I have doubts that they would shy away from using them as part of a “postmortem” investigation.

There are plenty of reasons why handling a GP fault isn't "postmortem" - guard pages are a prime example - and the Intel manual specifically states that this interrupt is continuable.

I was assuming that in this case it was postmortem, but in any case wouldn’t the “system” know whether or not it was?
Well Microsoft, here’s another nice mess you’ve gotten us into.

zooba

  • Guest
I was assuming that in this case it was postmortem, but in any case wouldn’t the “system” know whether or not it was?

I guess by the time it gets to formatting the error string it may be able to assume that there will be no recovery - calling FormatMessage directly would require you to provide the value for %s.

My earlier example of division by zero/underflow is not quite the same here, since that determines different status codes while access violations only have the one code. Still, responding to an access violation by trying to cause it again sounds like a bad enough approach that any dev would run away from it.  :biggrin:

Actually, the easier way to prove that they're decompiling the faulting instruction rather than using IsBad***Ptr is that those functions weren't added until XP, and we had access violations well before then  :bgrin:

Cheers,
Zooba  :t

jj2007

  • Member
  • *****
  • Posts: 7540
  • Assembler is fun ;-)
    • MasmBasic
Page-fault exceptions (CPU generated) provide an 'error code' that contains flags, including one to indicate whether the exception was caused by a read or write.
The OS exception handler can use this to react accordingly.
In this case, NTDLL pushes the appropriate string and creates its own 'exception' code.

Tedd, any idea where this is documented, or how to read these flags? The flags and reg values provided by Olly are identical for read or write :(

By the way, invoke IsBadReadPtr, 111h, 222h uses SEH to check if the address is readable. So Michael's suspicion might be right...

MichaelW

  • Global Moderator
  • Member
  • *****
  • Posts: 1209
The IsBad***Ptr functions have been around since Windows 95.
Well Microsoft, here’s another nice mess you’ve gotten us into.

qWord

  • Member
  • *****
  • Posts: 1454
  • The base type of a type is the type itself
    • SmplMath macros
GetExceptionInformation() returns this information through the member EXCEPTION_RECORD.ExceptionInformation, when called from an exception handler.

EDIT: It is a macro, which calls a function named _exception_info (maybe CRT)
MREAL macros - when you need floating point arithmetic while assembling!

Tedd

  • Member
  • ***
  • Posts: 353
  • Procrastinor Extraordinaire
Tedd, any idea where this is documented, or how to read these flags? The flags and reg values provided by Olly are identical for read or write :(

Intel System Programming Guide: Paging: Page-Fault Exceptions (section 4.7 in the current release.)

You won't be able to access the error code directly unless you're the kernel page-fault handler; so unless the handler copies that information to you in some form, I'm not sure you can.


As for IsBad***Ptr, this still wouldn't indicate whether the faulting instruction was a read or write, just whether the memory it tried to access was readable or writable, and in many cases it's neither (missing page, no access.)
Potato2