Menu

#33 IOCTL_SERIAL_GET_MODEM_CONTROL signature bug

v1.0 (example)
open
5
2022-08-16
2022-07-28
superfury1
No

I tried using COM0COM's 64-bit verion with my app.
But I've noticed that the signature for the IOCTL_SERIAL_GET_MODEM_CONTROL doesn't get set!

Looking into the source code's ioctl.c, within the case for IOCTL_SERIAL_GET_MODEM_CONTROL, I see the following:

if (OutputBufferLength >= (sizeof(ULONG) + C0CE_SIGNATURE_SIZE) &&
            InputBufferLength >= C0CE_SIGNATURE_SIZE &&
            RtlEqualMemory(pSysBuf, C0CE_SIGNATURE, C0CE_SIGNATURE_SIZE))
        {
          RtlCopyMemory(pSysBuf + sizeof(PULONG), C0CE_SIGNATURE, C0CE_SIGNATURE_SIZE);

          if (OutputBufferLength > (sizeof(ULONG) + C0CE_SIGNATURE_SIZE)) {
            RtlZeroMemory(pSysBuf + sizeof(ULONG) + C0CE_SIGNATURE_SIZE,
                          OutputBufferLength - (sizeof(ULONG) + C0CE_SIGNATURE_SIZE));
          }

Do you see what's happening on 64-bit builds? On 64-bit builds, the size of PULONG becomes 64-bit(8 bytes) instead of 32-bit(4 bytes). It will still properly address 32-bits(4 bytes) when addressed.
The data below it is properly written into the memory structure for the call, but look closer at what's happening during the RtlCopyMemory call:

RtlCopyMemory(pSysBuf + sizeof(PULONG), C0CE_SIGNATURE, C0CE_SIGNATURE_SIZE);

This causes the signature to be written 8 bytes after the start of the buffer.
Then look further to the RtlZeroMemory, which clears the data 8 bytes after the buffer (sysbuf + 4 bytes + 4 bytes) only if it's being supplied 8 bytes or more!

So it writes the signature to an invalid location (8 bytes into the memory instead of 4 bytes into the memory structure).
Then IF 8 bytes or more are actually written in memory, it will be guaranteed to be cleared by the RtlZeroMemory, because it's size is 8 bytes ore more by definition (to store the result)!

The solution here is to replace the copying of the memory RtlCopyMemory(pSysBuf...) line with the following:

          RtlCopyMemory(pSysBuf + sizeof(ULONG), C0CE_SIGNATURE, C0CE_SIGNATURE_SIZE);

That DOES give the expected result and causes autodetection to work properly. The signature is 4 bytes into the structure (as expected), not sizeof(PULONG), which is 8 bytes on the 64-bit driver! And because the result is 4 bytes further into memory, it's either addressing an invalid memory address(data corruption if the buffer isn't at least 12 bytes in size) or it's cleared by the RtlZeroMemory (if the size of the output buffer is longer than 8 bytes)!
So the custom OUT1/OUT2 code that's documented for the driver's IOCTL causes kernel data corruption and won't work properly on 64-bit operating systems!

Discussion

  • superfury1

    superfury1 - 2022-07-30

    I've managed to confirm the bugfix working as it should (with the proper result when using the bottom RtlCopyMemory patch (replacing "+ sizeof(PULONG)" with " + sizeof(ULONG)")) when using badly/unsigned drivers compiled from the latest source code on sourceforge (3.0.0).
    Although I can't load said drivers without driver signing protection disabled in Windows 10 x64 because I can't get it to sign the drivers properly for Windows to accept it as a valid driver to load (even when setting my own signature and adding it to the root signatures in Windows itself).

     
  • superfury1

    superfury1 - 2022-08-16

    Can anyone create a proper signed version with this bugfix? It's kind of a hassle to have to keep booting into safe mode every time I want to test or use the OUT1/OUT2 extended functionality, because that requires the detection to work (which requires the above bugfix on 64-bit builds (32-bit builds work fine tho, but my OS is 64-bit Windows 10, which doesn't allow it with secure boot etc.)).

     

Log in to post a comment.