/*
 *      Devctl.c
 *
 *      IoCrash
 *
 *      Copyright(c) 1997  Microsoft Corporation
 *
 *      NeillC  23-Oct-97
 *
 * This program is designed to call as many of the user mode native NT API's as
 * possible. The program is written to crash drivers as its primary function.
 *
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "nt.h"
#include "ntrtl.h"
#include "nturtl.h"
#include <winsock2.h>
#include <mswsock.h>
#include "windows.h"
#include "wmistr.h"
#include "wmiumkm.h"
#include "time.h"
#include "tdi.h"
#include "vdm.h"
#include "sddl.h"

#define MAX_DEVICES             4096
#define PROT_REP              100000
#define MAX_RET                 5000
#define DIAG_RET                  50
#define CRASH_LINE_SIZE         1024
#define SLOP_SENTINAL           0xA5

#define SLOP                      100
#define BIGBUF_SIZE               0x10000
#define RAND_REP                  1000
#define MAX_IOCTL_TAILOR          100
#define INITIAL_IOCTL_TAILOR_SIZE 200

#define FLAGS_DO_IOCTL_NULL        0x00000001
#define FLAGS_DO_IOCTL_RANDOM      0x00000002
#define FLAGS_DO_FSCTL_NULL        0x00000004
#define FLAGS_DO_FSCTL_RANDOM      0x00000008
#define FLAGS_DO_USAGE             0x00000010
#define FLAGS_DO_LOGGING           0x00000020
#define FLAGS_DO_POOLCHECK         0x00000040
#define FLAGS_DO_SKIPDONE          0x00000080
#define FLAGS_DO_SKIPCRASH         0x00000100
#define FLAGS_DO_MISC              0x00000200
#define FLAGS_DO_QUERY             0x00000400
#define FLAGS_DO_SUBOPENS          0x00000800
#define FLAGS_DO_ALLDEVS           0x00001000
#define FLAGS_DO_ZEROEA            0x00002000
#define FLAGS_DO_GRAB              0x00004000
#define FLAGS_DO_ERRORS            0x00008000
#define FLAGS_DO_ALERT             0x00010000
#define FLAGS_DO_LPC               0x00020000
#define FLAGS_DO_MAPNULL           0x00040000
#define FLAGS_DO_STREAMS           0x00080000
#define FLAGS_DO_WINSOCK           0x00100000
#define FLAGS_DO_SYNC              0x00200000
#define FLAGS_DO_DISKS             0x00400000
#define FLAGS_DO_PROT              0x00800000
#define FLAGS_DO_SECURITY          0x01000000
#define FLAGS_DO_IMPERSONATION     0x02000000
#define FLAGS_DO_DIRECT_DEVICE     0x04000000
#define FLAGS_DO_FAILURE_INJECTION 0x08000000
#define FLAGS_DO_PRINT_DEVS        0x10000000
#define FLAGS_DO_RANDOM_DEVICE     0x20000000
#define FLAGS_DO_SYMBOLIC          0x40000000
#define FLAGS_DO_OPEN_CLOSE        0x80000000

#define FLAGS_DO_DRIVER            0x00000001

#define DIAG_NOEXCEPTIONS 0x1

#define OPEN_TYPE_TDI_CONNECTION    1
#define OPEN_TYPE_TDI_ADDR_IP       2
#define OPEN_TYPE_TDI_ADDR_NETBIOS  3
#define OPEN_TYPE_TDI_ADDR_IPX      4
#define OPEN_TYPE_TDI_ADDR_APPLE    5
#define OPEN_TYPE_NAMED_PIPE        6
#define OPEN_TYPE_MAIL_SLOT         7
#define OPEN_TYPE_TREE_CONNECT      8

typedef struct _DEVMAP {
    OBJECT_NAME_INFORMATION *name;
    FILE_NAME_INFORMATION   *filename;
    HANDLE                  handle;
    DEVICE_TYPE             devtype;
    ACCESS_MASK             access;
} DEVMAP, *PDEVMAP;

//
// Define a structure to keep a track of issued IOCTL's. We do this to try and make a
// guess at what IOCTL's/FSCTL's a driver is actualy processing.
//
typedef struct _IOCTLINFO {
   NTSTATUS status;
   ULONG ioctl;
   ULONG count;
} IOCTLINFO, *PIOCTLINFO;

typedef struct _IOCTLREC {
   ULONG total, count, start;
   IOCTLINFO ioctl[];
} IOCTLREC, *PIOCTLREC;

typedef struct _CRASHNODE {
   struct _CRASHNODE *next;
   PWCHAR string;
} CRASHNODE, *PCRASHNODE;

DEVMAP                  devmap[MAX_DEVICES];
ULONG                   devscount;
ULONG                   skipped = 0;
ULONG                   random_device = 0;
UCHAR                   *bigbuf;
FILE                    *skipfile = NULL;
FILE                    *crashfile = NULL;
FILE                    *diag_file = NULL;
PCRASHNODE              crashlist = NULL;
HANDLE                  changethread, randthread, alertthread, mainthread;
SOCKET                  ls, cs;
ULONG                   flags=0, flags2=0;
ULONG                   ioctl_min_function=0;
ULONG                   ioctl_max_function=400 /*0xFFF*/;
ULONG                   ioctl_min_devtype=0;
ULONG                   ioctl_max_devtype=200;
ULONG                   ioctl_inbuf_min=0x0;
ULONG                   ioctl_inbuf_max=0x200;
ULONG                   ioctl_outbuf_min=0;
ULONG                   ioctl_outbuf_max=0x200;
ULONG                   max_random_calls   = 100000;
ULONG                   max_tailured_calls = 10000;
ULONG                   progress_counter=0;
ULONG                   alerted=0;
ULONG                   cid = 0;
HANDLE                  process_handle = NULL;
WCHAR                   lastcrashline[CRASH_LINE_SIZE];
HANDLE                  sync_event = NULL;
ULONG                   sessionid = 0;
PCHAR                   prefix_string = NULL;
HANDLE                  NonAdminToken=NULL;
UNICODE_STRING          DriverName={0};
BOOLEAN                 Impersonating = FALSE;

HANDLE
Create_nonadmin_token ()
/*
    Create a token with administrator filtered out.
*/
{
    HANDLE ProcessToken, RestrictedToken;
    SID_AND_ATTRIBUTES AdminSidAttrib;
    PSID pSid;
    SID_IDENTIFIER_AUTHORITY sia = {SECURITY_WORLD_SID_AUTHORITY};


    //
    // Open the process token
    //
    if (!OpenProcessToken (GetCurrentProcess (),
                           MAXIMUM_ALLOWED,
                           &ProcessToken)) {
        printf ("OpenProcessToken failed %d\n", GetLastError ());
        exit (EXIT_FAILURE);
    }

    if (!AllocateAndInitializeSid (&sia, 1, 0, 0, 0, 0, 0, 0, 0, 0, &pSid)) {
        printf ("AllocateAndInitializeSid failed %d\n", GetLastError ());
        CloseHandle (ProcessToken);
        exit (EXIT_FAILURE);
    }

    AdminSidAttrib.Sid = pSid;
    AdminSidAttrib.Attributes = 0;

    if (!CreateRestrictedToken (ProcessToken,
                                DISABLE_MAX_PRIVILEGE, 
                                0,
                                NULL,
                                0,
                                NULL,
                                1,
                                &AdminSidAttrib,
                                &RestrictedToken)) {
        FreeSid (pSid);
        CloseHandle (ProcessToken);
        printf ("CreateRestrictedToken failed %d\n", GetLastError ());
        exit (EXIT_FAILURE);
    }

    FreeSid (pSid);
    CloseHandle (ProcessToken);

    if (!DuplicateToken (RestrictedToken,
                         SecurityDelegation,
                         &NonAdminToken)) {
        CloseHandle (RestrictedToken);
        printf ("DuplicateToken failed %d\n", GetLastError ());
        exit (EXIT_FAILURE);
    }
    CloseHandle (RestrictedToken);

    return NonAdminToken;
}

VOID
Impersonate_nonadmin ()
{
    Impersonating = TRUE;
    if (NonAdminToken != NULL && !SetThreadToken (NULL, NonAdminToken)) {
        printf ("SetThreadToken failed %d\n", GetLastError ());
        exit (EXIT_FAILURE);
    }
}

DWORD
Revert_from_admin ()
{
    Impersonating = FALSE;
    return RevertToSelf ();
}


void
tag_to_wide (PUCHAR t, PWCHAR wt)
{
   ULONG i;

   for (i = 0; i < 4; i++) {
      *wt++ = *t++;
   }
}

/*
   Try and locate pool leaks by looking at pool tag info and lookaside list info.
   Try and locate where handled exceptions might reveal problems in the code.
*/
BOOL
print_diags (ULONG diag_flags, ULONG ret)
{
   static PSYSTEM_POOLTAG_INFORMATION opb=NULL;
   PSYSTEM_POOLTAG_INFORMATION pb;
   static PSYSTEM_LOOKASIDE_INFORMATION olpb=NULL;
   static ULONG olpbl;
   PSYSTEM_LOOKASIDE_INFORMATION lpb;
   ULONG pbl, lpbl, i, j, retlen, retlen1;
   BOOL found, diff, newtag;
   NTSTATUS status;
   static ULONG firsterror = 1;
   static SYSTEM_EXCEPTION_INFORMATION sei, osei;
   WCHAR wtag[4];
   static ULONG lpbi = 1, pbi = 1;

   if ((flags&FLAGS_DO_POOLCHECK) == 0) {
      return FALSE;
   }
   if (!diag_file) {
      diag_file = _wfopen (L"diags.log", L"ab");
      if (!diag_file) {
         printf ("Failed to open diags.log for diagnostics\n");
         exit (EXIT_FAILURE);
      }
   }
   diff = FALSE;
   newtag = FALSE;

   status = NtQuerySystemInformation (SystemExceptionInformation, &sei, sizeof (sei),
                                      &retlen);
   if (!NT_SUCCESS (status)) {
      if (firsterror) {
         printf ("NtQuerySystemInformation for SystemExceptionInformation failed %x\n",
                 status);
         firsterror = 0;
      }
      flags &= ~FLAGS_DO_POOLCHECK;
      return FALSE;
   }
   if (sei.ExceptionDispatchCount > osei.ExceptionDispatchCount &&
       osei.ExceptionDispatchCount) {
      if (!(diag_flags&DIAG_NOEXCEPTIONS)) {
         printf ("Exception count changed from %d to %d\n", osei.ExceptionDispatchCount,
                 sei.ExceptionDispatchCount);
         if (ret == DIAG_RET) {
            fwprintf (diag_file, L"%s Exception count changed from %d to %d\r\n",
                      lastcrashline,
                      osei.ExceptionDispatchCount,
                      sei.ExceptionDispatchCount);
            fflush (diag_file);
         } else if (ret < DIAG_RET) {
            diff = TRUE;
         }
      }
   }
   osei = sei;

   while (1) {
      /*
         Get memory for tag info
      */
      pbl = sizeof (*pb) + pbi * sizeof (pb->TagInfo[0]);
      pb = malloc (pbl);
      if (!pb) {
         printf ("Failed to allocate memory for pool buffer\n");
         exit (EXIT_FAILURE);
      }
      status = NtQuerySystemInformation (SystemPoolTagInformation, pb, pbl, &retlen1);
      if (pbl <= retlen1) {
         ULONG pbio = pbi;
         pbi = 1 + (retlen1 - sizeof (*pb)) / sizeof (pb->TagInfo[0]);
//         printf ("Increasing pooltag list table size to %d from %d\n", pbi, pbio);
         free (pb);
         continue;
      }
      if (!NT_SUCCESS (status)) {
         if (firsterror) {
            printf ("NtQuerySystemInformation failed %x\n", status);
            firsterror = 0;
         }
         flags &= ~FLAGS_DO_POOLCHECK;
         free (pb);
         return FALSE;
      }
      break;
   }

   while (1) {
      /*
         Get memory for lookaside info
      */
      lpbl = sizeof (*lpb) * lpbi;
      lpb = malloc (lpbl);
      if (!pb) {
         printf ("Failed to allocate memory for pool buffer\n");
         exit (EXIT_FAILURE);
      }
      status = NtQuerySystemInformation (SystemLookasideInformation, lpb, lpbl, &retlen);
      if (lpbl <= retlen) {
         ULONG lpbio = lpbi;
         lpbi = 1 + retlen / sizeof (*lpb);
//         printf ("Increasing lookaside list table size to %d from %d\n", lpbi, lpbio);
         free (lpb);
         continue;
      }
      if (!NT_SUCCESS (status)) {
         printf ("NtQuerySystemInformation failed %x\n", status);
         flags &= ~FLAGS_DO_POOLCHECK;
         free (lpb);
         free (pb);
         return FALSE;
      }
      break;
   }
   lpbl = retlen / sizeof (*lpb);
   if (olpb) {
      for (i = 0; i < lpbl; i++) {
         /*
            Quick check here. the tag is probably in the same place it was last time
         */
         if (i < olpbl && lpb[i].Tag == olpb[i].Tag && lpb[i].Type == olpb[i].Type &&
             lpb[i].Size == olpb[i].Size) {
            found = TRUE;
            j = i;
         } else {
            /*
                It has moved so search them all.
            */
            for (j = 0; j < olpbl; j++) {
               if (lpb[i].Tag == olpb[j].Tag && lpb[i].Type == olpb[j].Type &&
                   lpb[i].Size == olpb[j].Size) {
                  found = TRUE;
                  break;
               }
            }
         }
         if (found) {
            if (olpb[i].CurrentDepth > lpb[i].CurrentDepth) {
               printf ("Lookaside: %4.4s, size %d up %d\n",
                       &lpb[i].Tag, lpb[i].Size,
                       olpb[i].CurrentDepth - lpb[i].CurrentDepth);
               diff = TRUE;
               if (ret == DIAG_RET) {
                  tag_to_wide ((PUCHAR)&lpb[i].Tag, wtag);
                  fwprintf (diag_file, L"%s Lookaside: %4.4s, size %d up %d\r\n",
                            lastcrashline,
                            wtag, lpb[i].Size,
                            olpb[i].CurrentDepth - lpb[i].CurrentDepth);
                  fflush (diag_file);
               }
            }
         } else {
            /*
                A new tag has appeared here
            */
            printf ("New Lookaside %4.4s, size %d, depth %d\n",
                    &lpb[i].Tag, lpb[i].Size,
                    lpb[i].CurrentDepth);
            diff = TRUE;
         }
      }
      free (olpb);
   }

   /*
       now do lookaside information
   */
//   printf ("Total tags %d\n", pb->Count);
   if (opb) {
      for (i = 0; i < pb->Count; i++) {
         found = FALSE;
         if (i < opb->Count && pb->TagInfo[i].TagUlong == opb->TagInfo[i].TagUlong) {
            j = i;
            found = TRUE;
         } else {
            for (j = 0; j < pb->Count; j++) {
               if (pb->TagInfo[i].TagUlong == opb->TagInfo[j].TagUlong) {
                  found = TRUE;
                  break;
               }
            }
         }
         if (found) {
            if (pb->TagInfo[i].PagedUsed    > opb->TagInfo[j].PagedUsed ||
                pb->TagInfo[i].NonPagedUsed > opb->TagInfo[j].NonPagedUsed) {
               diff = TRUE;
               printf ("Pool: %4.4s, Paged up %d, NonPaged up %d\n",
                       &pb->TagInfo[i].TagUlong,
                       pb->TagInfo[i].PagedUsed - opb->TagInfo[i].PagedUsed,
                       pb->TagInfo[i].NonPagedUsed - opb->TagInfo[i].NonPagedUsed);
               if (ret == DIAG_RET) {
                  tag_to_wide ((PUCHAR)&pb->TagInfo[i].TagUlong, wtag);
                  fwprintf (diag_file, L"%s Pool: %4.4s, Paged up %d, NonPaged up %d\r\n",
                            lastcrashline,
                            wtag,
                            pb->TagInfo[i].PagedUsed - opb->TagInfo[i].PagedUsed,
                            pb->TagInfo[i].NonPagedUsed - opb->TagInfo[i].NonPagedUsed);
                  fflush (diag_file);
               }
            }
         } else {
            diff = TRUE;
            newtag = TRUE;
            printf ("New tag %4.4s\n", &pb->TagInfo[i].TagUlong);
         }
      }
      free (opb);
   }
   opb = pb;
   olpb = lpb;
   olpbl = lpbl;
   return diff;
}
/*
   Turn on fault injection in the driver verifier
*/
void turn_on_fault_injection ()
{
   ULONG svi;
   NTSTATUS status;

   svi = DRIVER_VERIFIER_SPECIAL_POOLING |
         DRIVER_VERIFIER_FORCE_IRQL_CHECKING |
         DRIVER_VERIFIER_INJECT_ALLOCATION_FAILURES;
   status = NtSetSystemInformation (SystemVerifierInformation, &svi, sizeof (svi));
   if (!NT_SUCCESS (status)) {
         printf ("NtSetSystemInformation for SystemVerifierInformation failed %x\n",
                 status);
      flags &= ~FLAGS_DO_FAILURE_INJECTION; // Turn off flag to prevent errors
   }   
}

/*
   Turn off fault injection in the driver verifier
*/
void turn_off_fault_injection ()
{
   ULONG svi;
   NTSTATUS status;

   svi = DRIVER_VERIFIER_SPECIAL_POOLING |
         DRIVER_VERIFIER_FORCE_IRQL_CHECKING;
   status = NtSetSystemInformation (SystemVerifierInformation, &svi, sizeof (svi));
   if (!NT_SUCCESS (status)) {
         printf ("NtSetSystemInformation for SystemVerifierInformation failed %x\n",
                 status);
   }   
}

/*
   Read a line from the crash file and remove returns etc
*/
PWCHAR getline (PWCHAR templine, ULONG size, FILE *file)
{
   PWCHAR ret;
   PWCHAR cp;

   ret = fgetws (templine, size, file);
   if (ret) {
      cp = wcsstr (templine, L"\r");
      if (cp)
         *cp = '\0';
      cp = wcsstr (templine, L"\n");
      if (cp)
         *cp = '\0';
   }
   return ret;
}

/*
    Add a line to the list of lines that crashed us before
*/
VOID add_crash_list (PWCHAR crashline)
{
   PCRASHNODE node;

   node = malloc (sizeof (CRASHNODE));
   if (!node) {
      printf ("Memory allocation failed for crash node!\n");
      exit (EXIT_FAILURE);
   }
   node->next = crashlist;
   crashlist = node;
   node->string = malloc ((wcslen (crashline) + 1) * sizeof (WCHAR));
   if (!node->string) {
      printf ("Memory allocation failed for crash line!\n");
      exit (EXIT_FAILURE);
   }
   wcscpy (node->string, crashline);
}

/*
   See if this operation crashed us before
*/
BOOL crashes (PWCHAR path, PWCHAR thing1, PWCHAR thing2, PWCHAR thing3, PWCHAR thing4)
{
   PWCHAR crashline = NULL;
   PWCHAR templine = NULL;
   BOOL result;
   HANDLE crashfilehandle;
   DWORD retlen;
   BOOL opened;
   PCRASHNODE node;
   ULONG count;
   BOOLEAN StoppedImpersonating;

   progress_counter++;
   _snwprintf (lastcrashline, sizeof (lastcrashline)/sizeof (WCHAR),
               L"%s %s %s %s %s", path, thing1, thing2, thing3, thing4);
   if ((flags&FLAGS_DO_LOGGING) == 0) {
      wprintf (L"%s\n", lastcrashline);
      SetConsoleTitleW (lastcrashline);
      return FALSE;
   }
   crashline = malloc (CRASH_LINE_SIZE*sizeof (WCHAR));
   if (!crashline) {
      printf ("Failed to allocate crash line buffer\n");
      exit (EXIT_FAILURE);
   }
   templine = malloc (CRASH_LINE_SIZE*sizeof (WCHAR));
   if (!templine) {
      printf ("Failed to allocate crash line buffer\n");
      exit (EXIT_FAILURE);
   }
   StoppedImpersonating = FALSE;
   if (Impersonating) {
      Revert_from_admin ();
      StoppedImpersonating = TRUE;
   }
   opened = FALSE;
   if (!skipfile) {
      /*
          First time in openen up the files
      */
      opened = TRUE;
      skipfile = _wfopen (L"crash.log", L"ab");
      if (skipfile) {
         crashfile = _wfopen (L"crashn.log", L"rb");
         if (crashfile) {
            printf ("Opened crashn.log for reading\n");
            crashline[0] = '\0';
            while (getline (templine, CRASH_LINE_SIZE, crashfile)) {
               if (wcslen (templine) > 0) {
                  if (flags&FLAGS_DO_SKIPDONE) {
                     add_crash_list (templine);
                  }
                  wcscpy (crashline, templine);
               }
            }
            fclose (crashfile);
            crashfile = NULL;
            if (crashline[0] != '\0' && wcsncmp (crashline, L"DONE", 4) != 0) {
               fputws (crashline, skipfile);
               fputws (L"\r\n", skipfile);
            }
         }
         fclose (skipfile);
      }
      skipfile = _wfopen (L"crash.log", L"rb");
   }
   if (skipfile && opened) {
      while (getline (crashline, CRASH_LINE_SIZE, skipfile)) {
         if (flags&FLAGS_DO_SKIPCRASH && wcscmp (crashline, L"DONE") != 0) {
            add_crash_list (crashline);
         }
      }
   }
   /*
      Run through crashing lines looking for a match
   */
   result = FALSE;
   _snwprintf (templine, CRASH_LINE_SIZE, L"%s %s %s %s %s", path, thing1, thing2, thing3, thing4);
   SetConsoleTitleW (templine);
   for (node = crashlist; node; node = node->next) {
      if (_wcsicmp (node->string, templine) == 0) {
         result = TRUE;
         break;
      }
   }
   if (!result) {
      print_diags (0, 0);
      count = 0;
      do {
         crashfilehandle = CreateFile("crashn.log", GENERIC_WRITE, 0, NULL, 
                                       OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0 );
         if (crashfilehandle == INVALID_HANDLE_VALUE) {
            printf ("CreateFile failed for crash logging file %d\n", GetLastError ());
            if (count++ > 20) {
               break;
            } else {
               Sleep (5000);
            }
         }
      } while (crashfilehandle == INVALID_HANDLE_VALUE);

      if (crashfilehandle != INVALID_HANDLE_VALUE) {
         SetFilePointer (crashfilehandle, 0, 0, FILE_END);
         _snwprintf (templine, CRASH_LINE_SIZE,
                     L"%s %s %s %s %s\r\n", path, thing1, thing2, thing3, thing4);
         wprintf (L"%s", templine);
         /*
            This is a bit excessive and costly but I really want to make sure this gets
            logged as the next operation may crash the machine.
         */
         if (!WriteFile (crashfilehandle, templine, wcslen(templine)*sizeof (WCHAR), &retlen, 0)) {
            printf ("WriteFile failed fore crash line %d\n", GetLastError ());
            exit (EXIT_FAILURE);
         }
         if (!FlushFileBuffers (crashfilehandle)) {
            printf ("FlushFileBuffers failed for crash file %d\n", GetLastError ());
            exit (EXIT_FAILURE);
         }
         if (!CloseHandle (crashfilehandle)) {
            printf ("CloseHandle failed for crash file %d\n", GetLastError ());
            exit (EXIT_FAILURE);
         }
      }
   }
   if (StoppedImpersonating) {
       Impersonate_nonadmin ();
   }

   free (crashline);
   free (templine);
   return result;
}


/*
   Hack to get a 32 bit random value from a 15 bit source
*/
ULONG
rand32(
       void
)
{
    return(rand() << 17) + rand() + rand();
}

/*
   RandInRange - produce a random number in some range
*/
ULONG
RandInRange( ULONG lowerb, ULONG upperb )
{
   if( lowerb > upperb ) {
      ULONG temp;
      temp= upperb;

   return lowerb + rand32()%(upperb-lowerb);
      upperb= lowerb;
      upperb= temp;
   }

   return lowerb + rand32()%(upperb-lowerb);

}

/*
   Allocate a buffer with slop and fill the slop with a know value
*/
PVOID
reallocslop(
    PVOID                   p,
    ULONG                   len
)
{
    progress_counter++;
    p = realloc(p,
                len + SLOP);

    memset(p,
           SLOP_SENTINAL,
           len + SLOP);

    return p;
}




/*
   Check to see if the driver wrote too far by checking the slop values
*/
VOID
testslop(
    PVOID                   p,
    ULONG                   len,
    PWCHAR                  what,
    PWCHAR                  subwhat
)
{
    UCHAR                   string[100], *pc;
    ULONG                   i;

    pc = p;

    pc += len;

    for (i = 0; i < SLOP; i++, pc++) {
        if (*pc != SLOP_SENTINAL) {
            wprintf(L"Driver wrote beyond end during %s %s for length %d!\n",
                    what, subwhat, len);

            scanf("%100s",
                  &string);

            break;
        }
    }
}

/*
   Issue different sized EA's
*/
VOID
do_query_ea(
    HANDLE                  handle,
    PWCHAR                  path
)
{
    ULONG                   l, i, old, ret;
    IO_STATUS_BLOCK         iosb;
    ULONG                   tmp;
    PVOID                   buf;
    NTSTATUS                status, last_status;


    if (crashes (path, L"NtQueryEaFile", L"", L"", L""))
       return;

    ret = 0;
    buf = NULL;
    do {
       last_status = 0;

       l = 1024;


       do {
           buf = reallocslop(buf,
                             l);

           status = NtQueryEaFile (handle, &iosb, buf, l, FALSE, NULL, 0, NULL, FALSE);
           if (NT_SUCCESS (status))
              status = iosb.Status;
           testslop(buf,
                    l,
                    L"NtQueryEaFile",
                    L"");

           if (status == STATUS_NOT_IMPLEMENTED ||
               status == STATUS_INVALID_INFO_CLASS ||
               status == STATUS_INVALID_DEVICE_REQUEST ||
               status == STATUS_INVALID_PARAMETER ||
               status == STATUS_ACCESS_DENIED) {

//               break;
           }

           if (!NT_SUCCESS(status) &&
               status != last_status) {

               last_status = status;
               if (flags&FLAGS_DO_ERRORS)
                  printf("NtQueryEaFile failed %x\n",
                         status);
           }
       } while (l-- != 0);

       if (flags&FLAGS_DO_ZEROEA) {
          status = NtQueryEaFile(handle,
                                 &iosb,
                                 (PVOID)-1024,
                                 0,
                                 FALSE, NULL, 0, NULL, FALSE);
       }

       if (!NT_SUCCESS(status)) {
           if (flags&FLAGS_DO_ERRORS)
              printf("NtQueryEaFile failed %x\n",
                     status);
       }
    } while (print_diags (0, ret++) && ret < MAX_RET);

    status = NtCancelIoFile (handle, &iosb);

    free(buf);

    if (!(flags&FLAGS_DO_PROT) || crashes (path, L"NtQueryEaFile prot", L"", L"", L""))
       return;
    status = NtResumeThread (changethread, &tmp);
    if (!NT_SUCCESS (status)) {
       printf ("NtResumeThread failed %x\n", status);
    }

    for (i = 1; i < PROT_REP; i++) {
       if (!VirtualProtect (buf, 1, PAGE_READWRITE, &old)) {
          printf ("VirtualProtect failed %d\n", GetLastError ());
       }
       status = NtQueryEaFile(handle,
                              &iosb,
                              bigbuf,
                              BIGBUF_SIZE,
                              FALSE, NULL, 0, NULL, FALSE);
    }
    status = NtSuspendThread (changethread, &tmp);
    if (!NT_SUCCESS (status)) {
       printf ("NtResumeThread failed %x\n", status);
    }
    if (!VirtualProtect (buf, 1, PAGE_READWRITE, &old)) {
       printf ("VirtualProtect failed %d\n", GetLastError ());
    }

}
/*
   Do volume queries of different lengths
*/
VOID
do_query_volume(
    HANDLE                  handle,
    FS_INFORMATION_CLASS    InfoType,
    ULONG                   bufl,
    PWCHAR                  what,
    PWCHAR                  path
)
{
    ULONG                   l, ret;
    IO_STATUS_BLOCK         iosb;
    PVOID                   buf;
    NTSTATUS                status, last_status;

    if (crashes (path, L"NtQueryVolumeInformationFile", what, L"", L""))
       return;

    ret = 0;
    buf = NULL;
    do {
       last_status = 0;

       l = bufl + 1024;


       do {
           buf = reallocslop(buf,
                             l);

           status = NtQueryVolumeInformationFile(handle,
                                                 &iosb,
                                                 buf,
                                                 l,
                                                 InfoType);

           testslop(buf,
                    l,
                    L"NtQueryVolumeInformationFile",
                    what);

           if (status == STATUS_NOT_IMPLEMENTED ||
               status == STATUS_INVALID_INFO_CLASS ||
               status == STATUS_INVALID_DEVICE_REQUEST ||
               status == STATUS_INVALID_PARAMETER ||
               status == STATUS_ACCESS_DENIED) {

//               break;
           }

           if (!NT_SUCCESS(status) &&
               status != last_status) {

               last_status = status;
               if (flags&FLAGS_DO_ERRORS)
                  wprintf(L"NtQueryVolumeInformationFile for %s failed %x\n",
                          what, status);
           }
       } while (l-- != 0);

       status = NtQueryVolumeInformationFile(handle,
                                             &iosb,
                                             (PVOID)-1024,
                                             0,
                                             InfoType);

       if (!NT_SUCCESS(status)) {
          if (flags&FLAGS_DO_ERRORS)
             wprintf(L"NtQueryVolumeInformationFile for %s failed %x\n",
                     what, status);
       }
    } while (print_diags (0, ret++) && ret < MAX_RET);

    status = NtCancelIoFile (handle, &iosb);
    free(buf);
}

/*
   Do volume sets of different lengths
*/
VOID
do_set_volume(
    HANDLE                  handle,
    FS_INFORMATION_CLASS    InfoType,
    ULONG                   bufl,
    PWCHAR                  what,
    PWCHAR                  path
)
{
    ULONG                   l, i, ret;
    IO_STATUS_BLOCK         iosb;
    PVOID                   buf;
    NTSTATUS                status, last_status;

    if (crashes (path, L"NtSetVolumeInformationFile", what, L"", L""))
       return;

    ret = 0;
    buf = NULL;
    do {
       last_status = 0;

       l = bufl + 1024;


       buf = malloc (l);
       do {
           for (i = 0; i < l; i++) {
              ((PCHAR)buf)[i] = (CHAR) rand ();
           }

           progress_counter++;
           status = NtSetVolumeInformationFile(handle,
                                               &iosb,
                                               buf,
                                               l,
                                               InfoType);

           if (status == STATUS_NOT_IMPLEMENTED ||
               status == STATUS_INVALID_INFO_CLASS ||
               status == STATUS_INVALID_DEVICE_REQUEST ||
               status == STATUS_INVALID_PARAMETER ||
               status == STATUS_ACCESS_DENIED) {

//               break;
           }

           if (!NT_SUCCESS(status) &&
               status != last_status) {

               last_status = status;
               if (flags&FLAGS_DO_ERRORS)
                  wprintf(L"NtSetVolumeInformationFile for %s failed %x\n",
                          what, status);
           }
       } while (l-- != 0);

       progress_counter++;
       status = NtSetVolumeInformationFile(handle,
                                           &iosb,
                                           (PVOID)-1024,
                                           0,
                                           InfoType);

       if (!NT_SUCCESS(status)) {
           if (flags&FLAGS_DO_ERRORS)
              wprintf(L"NtSetVolumeInformationFile for %s failed %x\n",
                      what, status);
       }
    } while (print_diags (0, ret++) && ret < MAX_RET);

    status = NtCancelIoFile (handle, &iosb);
    free(buf);
}

/*
   Do file queries
*/
VOID
do_query_file(
    HANDLE                  handle,
    FILE_INFORMATION_CLASS  InfoType,
    ULONG                   bufl,
    PWCHAR                  what,
    PWCHAR                  path
)
{
    ULONG                   l, i, ret;
    IO_STATUS_BLOCK         iosb;
    PVOID                   buf;
    NTSTATUS                status, last_status;
    ULONG                   tmp;
    DWORD                   old;

    if (crashes (path, L"NtQueryInformationFile", what, L"", L""))
       return;

    ret = 0;
    buf = NULL;
    do {
       last_status = 0;

       l = bufl + 1024;


       do {
           buf = reallocslop(buf,
                             l);
           status = NtQueryInformationFile(handle,
                                           &iosb,
                                           buf,
                                           l,
                                           InfoType);

           testslop(buf,
                    l,
                    L"NtQueryInformationFile",
                    what);

           if (status == STATUS_NOT_IMPLEMENTED ||
               status == STATUS_INVALID_INFO_CLASS ||
               status == STATUS_INVALID_DEVICE_REQUEST ||
               status == STATUS_INVALID_PARAMETER ||
               status == STATUS_ACCESS_DENIED) {

//               break;
           }

           if (!NT_SUCCESS(status) &&
               status != last_status) {
               last_status = status;
               if (flags&FLAGS_DO_ERRORS)
                  wprintf(L"NtQueryInformationFile for %s failed %x\n",
                          what, status);
           }
       } while (l-- != 0);

       status = NtQueryInformationFile(handle,
                                       &iosb,
                                       (PVOID)-1024,
                                       0,
                                       InfoType);

       if (!NT_SUCCESS(status)) {
           if (flags&FLAGS_DO_ERRORS)
              wprintf(L"NtQueryInformationFile for %s failed %x\n",
                      what, status);
       }
    } while (print_diags (0, ret++) && ret < MAX_RET);
    status = NtCancelIoFile (handle, &iosb);
    free(buf);

    if (!(flags&FLAGS_DO_PROT) ||
        crashes (path, L"NtQueryInformationFile prot", L"", L"", L""))
       return;
    status = NtResumeThread (changethread, &tmp);
    if (!NT_SUCCESS (status)) {
       printf ("NtResumeThread failed %x\n", status);
    }

    for (i = 1; i < PROT_REP; i++) {
       if (!VirtualProtect (buf, 1, PAGE_READWRITE, &old)) {
          printf ("VirtualProtect failed %d\n", GetLastError ());
       }
       status = NtQueryInformationFile(handle,
                                       &iosb,
                                       bigbuf,
                                       bufl,
                                       InfoType);
    }
    status = NtSuspendThread (changethread, &tmp);
    if (!NT_SUCCESS (status)) {
       printf ("NtResumeThread failed %x\n", status);
    }
    if (!VirtualProtect (buf, 1, PAGE_READWRITE, &old)) {
       printf ("VirtualProtect failed %d\n", GetLastError ());
    }
}

/*
   Do file sets
*/
VOID
do_set_file(
    HANDLE                  handle,
    FILE_INFORMATION_CLASS  InfoType,
    ULONG                   bufl,
    PWCHAR                  what,
    PWCHAR                  path
)
{
    ULONG                   l, i, ret;
    IO_STATUS_BLOCK         iosb;
    PVOID                   buf;
    NTSTATUS                status, last_status;
    ULONG                   tmp;
    DWORD                   old;

    if (crashes (path, L"NtSetInformationFile", what, L"", L""))
       return;

    ret = 0;
    buf = NULL;
    do {
       last_status = 0;

       l = bufl + 1024;
       buf = malloc (l);
       do {
           for (i = 0; i < l; i++) {
              ((PCHAR)buf)[i] = (CHAR) rand ();
           }
           progress_counter++;
           status = NtSetInformationFile(handle,
                                         &iosb,
                                         buf,
                                         l,
                                         InfoType);

           if (status == STATUS_NOT_IMPLEMENTED ||
               status == STATUS_INVALID_INFO_CLASS ||
               status == STATUS_INVALID_DEVICE_REQUEST ||
               status == STATUS_INVALID_PARAMETER ||
               status == STATUS_ACCESS_DENIED) {

//               break;
           }

           if (!NT_SUCCESS(status) &&
               status != last_status) {
               last_status = status;
               if (flags&FLAGS_DO_ERRORS)
                  wprintf(L"NtSetInformationFile for %s failed %x\n",
                          what, status);
           }
       } while (l-- != 0);

       status = NtSetInformationFile(handle,
                                     &iosb,
                                     (PVOID)-1024,
                                     0,
                                     InfoType);

       if (!NT_SUCCESS(status)) {
           if (flags&FLAGS_DO_ERRORS)
              wprintf(L"NtSetInformationFile for %s failed %x\n",
                      what, status);
       }
    } while (print_diags (0, ret++) && ret < MAX_RET);
    status = NtCancelIoFile (handle, &iosb);
    free(buf);

    if (!(flags&FLAGS_DO_PROT) ||
        crashes (path, L"NtSetInformationFile prot", L"", L"", L""))
       return;
    status = NtResumeThread (changethread, &tmp);
    if (!NT_SUCCESS (status)) {
       printf ("NtResumeThread failed %x\n", status);
    }

    for (i = 1; i < PROT_REP; i++) {
       if (!VirtualProtect (buf, 1, PAGE_READWRITE, &old)) {
          printf ("VirtualProtect failed %d\n", GetLastError ());
       }
       status = NtSetInformationFile(handle,
                                     &iosb,
                                     bigbuf,
                                     bufl,
                                     InfoType);
    }
    status = NtSuspendThread (changethread, &tmp);
    if (!NT_SUCCESS (status)) {
       printf ("NtResumeThread failed %x\n", status);
    }
    if (!VirtualProtect (buf, 1, PAGE_READWRITE, &old)) {
       printf ("VirtualProtect failed %d\n", GetLastError ());
    }
}

/*
   Do object queries with variable length buffers
*/
VOID
do_query_object(
    HANDLE                  handle,
    OBJECT_INFORMATION_CLASS InfoType,
    ULONG                   bufl,
    PWCHAR                  what,
    PWCHAR                  path
)
{
    ULONG                   l, ret;
    IO_STATUS_BLOCK         iosb;
    PVOID                   buf;
    NTSTATUS                status, last_status;

    last_status = 0;

    if (crashes (path, L"NtQueryObject", what, L"", L""))
       return;

    buf = NULL;
    ret = 0;
    do {
       l = bufl + 1024;


       do {
           buf = reallocslop(buf,
                             l);

           status = NtQueryObject(handle,
                                  InfoType,
                                  buf,
                                  l,
                                  NULL);

           testslop(buf, l, L"NtQueryObject", what);

           if (!NT_SUCCESS(status) &&
               status != last_status) {

               last_status = status;
               if (flags&FLAGS_DO_ERRORS)
                  wprintf(L"NtQueryObject for %s failed %x\n",
                          what, status);
           }
       } while (l-- != 0);

       status = NtQueryObject(handle,
                              InfoType,
                              (PVOID)-1024,
                              0,
                              NULL);

       if (!NT_SUCCESS(status)) {
           if (flags&FLAGS_DO_ERRORS)
              wprintf(L"NtQueryObject for %s failed %x\n",
                      what, status);
       }
    } while (print_diags (0, ret++) && ret < MAX_RET);

    status = NtCancelIoFile (handle, &iosb);
    free(buf);
}

/*
   Do query security
*/
VOID
do_query_security(
    HANDLE                  handle,
    SECURITY_INFORMATION    InfoType,
    ULONG                   bufl,
    PWCHAR                  what,
    PWCHAR                  path
)
{
    ULONG                   l, i, tmp, ret;
    IO_STATUS_BLOCK         iosb;
    PVOID                   buf;
    NTSTATUS                status, last_status;
    ULONG                   ln;
    DWORD                   old;

    if (crashes (path, L"NtQuerySecurityObject", what, L"", L""))
       return;

    buf = NULL;
    ret = 0;
    do {
       last_status = 0;

       l = bufl + 1024;


       do {
           buf = reallocslop(buf,
                             l);

           status = NtQuerySecurityObject(handle,
                                          InfoType,
                                          buf,
                                          l,
                                          &ln);

           testslop(buf, l, L"NtQuerySecurityObject", what);

           if (!NT_SUCCESS(status) &&
               status != last_status && status) {

               last_status = status;
               if (flags&FLAGS_DO_ERRORS)
                  wprintf(L"NtQuerySecurityObject for %s failed %x\n",
                         what, status);
           }
       } while (l-- != 0);

       status = NtQuerySecurityObject(handle,
                                      InfoType,
                                      (PVOID)-1024,
                                      0,
                                      &ln);

       if (!NT_SUCCESS(status)) {
           if (flags&FLAGS_DO_ERRORS)
              wprintf(L"NtQuerySecurityObject for %s failed %x\n",
                      what, status);
       }
    } while (print_diags (0, ret++) && ret < MAX_RET);

    status = NtCancelIoFile (handle, &iosb);
    free(buf);

    if (!(flags&FLAGS_DO_PROT) ||
        crashes (path, L"NtQuerySecurityFile prot", L"", L"", L""))
       return;
    status = NtResumeThread (changethread, &tmp);
    if (!NT_SUCCESS (status)) {
       printf ("NtResumeThread failed %x\n", status);
    }

    for (i = 1; i < PROT_REP; i++) {
       if (!VirtualProtect (buf, 1, PAGE_READWRITE, &old)) {
          printf ("VirtualProtect failed %d\n", GetLastError ());
       }
        status = NtQuerySecurityObject(handle,
                                       InfoType,
                                       bigbuf,
                                       bufl,
                                       &ln);
    }
    status = NtSuspendThread (changethread, &tmp);
    if (!NT_SUCCESS (status)) {
       printf ("NtResumeThread failed %x\n", status);
    }
    if (!VirtualProtect (buf, 1, PAGE_READWRITE, &old)) {
       printf ("VirtualProtect failed %d\n", GetLastError ());
    }
}

/*
   Do set security
*/
VOID
do_set_security(
    HANDLE                  handle,
    SECURITY_INFORMATION    InfoType,
    ULONG                   bufl,
    PWCHAR                  what,
    PWCHAR                  path
)
{
    ULONG                   l, i, tmp, ret;
    IO_STATUS_BLOCK         iosb;
    PVOID                   buf;
    NTSTATUS                status, last_status;
    ULONG                   ln;
    DWORD                   old;
    PSECURITY_DESCRIPTOR    psd;
    LPTSTR                  tsd;

    if (crashes (path, L"NtSetSecurityObject", what, L"", L""))
       return;

    psd = malloc (l = 4096);
    if (!psd) {
       printf ("Failed to allocate security descriptor space\n");
       return;
    }
    status = NtQuerySecurityObject(handle,
                                   InfoType,
                                   psd,
                                   l,
                                   &ln);
    if (!NT_SUCCESS(status)) {
       free (psd);
       if (flags&FLAGS_DO_ERRORS)
          wprintf(L"NtQuerySecurityObject for %s failed %x\n",
                  what, status);
       return;
    }
    if (InfoType) {
       if (ConvertSecurityDescriptorToStringSecurityDescriptor (psd,
                                                                SDDL_REVISION_1,
                                                                InfoType,
                                                                &tsd,
                                                                NULL)) {
          printf ("%ws: %s\n", what, tsd);
          LocalFree (tsd);
       } else {
          printf ("ConvertSecurityDescriptorToStringSecurityDescriptor failed %d\n", GetLastError ());
       }
    }
    buf = NULL;
    ret = 0;
    do {
       last_status = 0;

       status = NtSetSecurityObject(handle,
                                    InfoType,
                                    psd);

       if (!NT_SUCCESS(status) &&
           status != last_status && status) {

           last_status = status;
           if (flags&FLAGS_DO_ERRORS)
              wprintf(L"NtSetSecurityObject for %s failed %x\n",
                      what, status);
       }
       status = NtCancelIoFile (handle, &iosb);
    } while (print_diags (0, ret++) && ret < MAX_RET);

    free(psd);
}

/*
   Do all the query functions
*/
NTSTATUS
query_object(
    HANDLE                  handle,
    PDEVMAP                 devmap,
    PWCHAR                  path
)
{
    OBJECT_NAME_INFORMATION *on = NULL;
    FILE_NAME_INFORMATION   *fn = NULL; 
    ULONG                   sfn, son;
    FILE_FS_DEVICE_INFORMATION devinfo;
    NTSTATUS                status;
    static IO_STATUS_BLOCK  iosb;

    sfn = sizeof (*fn) + 1024;
    son = sizeof (*on) + 1024;
    fn = reallocslop(NULL,
                     sfn);
    on = reallocslop(NULL,
                     son);
    if (devmap) {
       devmap->filename = fn;
       devmap->name = on;
    }

    if (fn == NULL || on == NULL) {
        printf("Memory allocation failure in query_object!\n");
        exit(EXIT_FAILURE);
    }

    status = NtQueryObject(handle,
                           ObjectNameInformation,
                           on,
                           son,
                           NULL);

    testslop(on,
             son, L"NtQueryObject", L"ObjectNameInformation");

    if (!NT_SUCCESS(status)) {
        if (flags&FLAGS_DO_ERRORS) {
            wprintf(L"NtQueryObject for ObjectNameInformation failed %x\n",
                   status);
        }

        on->Name.Length = 0;
    } else {
       wprintf (L"Object name is %s\n", on->Name.Buffer);
    }

    status = NtQueryInformationFile(handle,
                                    &iosb,
                                    fn,
                                    sfn,
                                    FileNameInformation);

    testslop(fn,
             sfn,
             L"NtQueryInformationFile",
             L"FileNameInformation");

    if (NT_SUCCESS(status)) {
        status = iosb.Status;
    }

    if (!NT_SUCCESS(status)) {
        if (flags&FLAGS_DO_ERRORS)
           wprintf(L"NtQueryInformationFile for FileNameInformation failed %x\n",
                   status);
        fn->FileNameLength = 0;
    }

    if (!devmap) {
       free (fn);
       free (on);
    }

    status = NtQueryVolumeInformationFile(handle,
                                          &iosb,
                                          &devinfo,
                                          sizeof (devinfo),
                                          FileFsDeviceInformation);

    if (NT_SUCCESS(status)) {
        status = iosb.Status;
    }

    if (!NT_SUCCESS(status)) {
        if (flags&FLAGS_DO_ERRORS)
           wprintf(L"NtQueryVolumeInformationFile for FileFsDeviceInformation failed %x\n",
                   status);

        if (devmap)
           devmap->devtype = 0;
    } else {
        if (devmap)
           devmap->devtype = devinfo.DeviceType;
//        printf("Got the device number for a device!\n");
    }

    if (flags&FLAGS_DO_QUERY) {
       //
       // Do loads of different queries with different buffer lengths.
       //
       do_query_object(handle,
                       ObjectNameInformation,
                       sizeof (OBJECT_NAME_INFORMATION),
                       L"ObjectNameInformation",
                       path);

       do_query_file(handle,
                     FileBasicInformation,
                     sizeof (FILE_BASIC_INFORMATION),
                     L"FileBasicInformation",
                     path);

       do_query_file(handle,
                     FileStandardInformation,
                     sizeof (FILE_STANDARD_INFORMATION),
                     L"FileStandardInformation",
                     path);

       do_query_file(handle,
                     FileInternalInformation,
                     sizeof (FILE_INTERNAL_INFORMATION),
                     L"FileInternalInformation",
                     path);

       do_query_file(handle,
                     FileEaInformation,
                     sizeof (FILE_EA_INFORMATION),
                     L"FileEaInformation",
                     path);

       do_query_file(handle,
                     FileAccessInformation,
                     sizeof (FILE_ACCESS_INFORMATION),
                     L"FileAccessInformation",
                     path);

       do_query_file(handle,
                     FileNameInformation,
                     sizeof (FILE_NAME_INFORMATION) + 1024,
                     L"FileNameInformation",
                     path);

//
// We end up turning off alertable handle with this.
//
//       do_query_file(handle,
//                     FileModeInformation,
//                     sizeof (FILE_MODE_INFORMATION),
//                     L"FileModeInformation",
//                     path);

       do_query_file(handle,
                     FileAlignmentInformation,
                     sizeof (FILE_ALIGNMENT_INFORMATION),
                     L"FileAlignmentInformation",
                     path);

       do_query_file(handle,
                     FileAllInformation,
                     sizeof (FILE_ALL_INFORMATION),
                     L"FileAllInformation",
                     path);

       do_query_file(handle,
                     FileStreamInformation,
                     sizeof (FILE_STREAM_INFORMATION),
                     L"FileStreamInformation",
                     path);

       do_query_file(handle,
                     FilePipeInformation,
                     sizeof (FILE_PIPE_INFORMATION),
                     L"FilePipeInformation",
                     path);

       do_query_file(handle,
                     FilePipeLocalInformation,
                     sizeof (FILE_PIPE_LOCAL_INFORMATION),
                     L"FilePipeLocalInformation",
                     path);

       do_query_file(handle,
                     FilePipeRemoteInformation,
                     sizeof (FILE_PIPE_REMOTE_INFORMATION),
                     L"FilePipeRemoteInformation",
                     path);

       do_query_file(handle,
                     FileCompressionInformation,
                     sizeof (FILE_COMPRESSION_INFORMATION),
                     L"FileCompressionInformation",
                     path);

       do_query_file(handle,
                     FileObjectIdInformation,
                     sizeof (FILE_OBJECTID_INFORMATION),
                     L"FileObjectIdInformation",
                     path);

       do_query_file(handle,
                     FileMailslotQueryInformation,
                     sizeof (FILE_MAILSLOT_QUERY_INFORMATION),
                     L"FileMailslotQueryInformation",
                     path);

       do_query_file(handle,
                     FileQuotaInformation,
                     sizeof (FILE_QUOTA_INFORMATION),
                     L"FileQuotaInformation",
                     path);

       do_query_file(handle,
                     FileReparsePointInformation,
                     sizeof (FILE_REPARSE_POINT_INFORMATION),
                     L"FileReparsePointInformation",
                     path);

       do_query_file(handle,
                     FileNetworkOpenInformation,
                     sizeof (FILE_NETWORK_OPEN_INFORMATION),
                     L"FileNetworkOpenInformation",
                     path);

       do_query_file(handle,
                     FileAttributeTagInformation,
                     sizeof (FILE_ATTRIBUTE_TAG_INFORMATION),
                     L"FileAttributeTagInformation",
                     path);

       do_set_file(handle,
                   FileBasicInformation,
                   sizeof (FILE_BASIC_INFORMATION),
                   L"FileBasicInformation",
                   path);

       do_set_file(handle,
                   FileRenameInformation,
                   sizeof (FILE_RENAME_INFORMATION),
                   L"FileRenameInformation",
                   path);

       do_set_file(handle,
                   FileLinkInformation,
                   sizeof (FILE_LINK_INFORMATION),
                   L"FileLinkInformation",
                   path);

       do_set_file(handle,
                   FileDispositionInformation,
                   sizeof (FILE_DISPOSITION_INFORMATION),
                   L"FileDispositionInformation",
                   path);

       do_set_file(handle,
                   FilePositionInformation,
                   sizeof (FILE_POSITION_INFORMATION),
                   L"FilePositionInformation",
                   path);

       do_set_file(handle,
                   FileAllocationInformation,
                   sizeof (FILE_ALLOCATION_INFORMATION),
                   L"FileAllocationInformation",
                   path);

       do_set_file(handle,
                   FileEndOfFileInformation,
                   sizeof (FILE_END_OF_FILE_INFORMATION),
                   L"FileEndOfFileInformation",
                   path);

       do_set_file(handle,
                   FilePipeInformation,
                   sizeof (FILE_PIPE_INFORMATION),
                   L"FilePipeInformation",
                   path);

       do_set_file(handle,
                   FilePipeRemoteInformation,
                   sizeof (FILE_PIPE_REMOTE_INFORMATION),
                   L"FilePipeRemoteInformation",
                   path);

       do_set_file(handle,
                   FileMailslotSetInformation,
                   sizeof (FILE_MAILSLOT_SET_INFORMATION),
                   L"FileMailslotSetInformation",
                   path);

       do_set_file(handle,
                   FileObjectIdInformation,
                   sizeof (FILE_OBJECTID_INFORMATION),
                   L"FileObjectIdInformation",
                   path);

       do_set_file(handle,
                   FileMoveClusterInformation,
                   sizeof (FILE_MOVE_CLUSTER_INFORMATION),
                   L"FileMoveClusterInformation",
                   path);

       do_set_file(handle,
                   FileQuotaInformation,
                   sizeof (FILE_QUOTA_INFORMATION),
                   L"FileQuotaInformation",
                   path);

       do_set_file(handle,
                   FileTrackingInformation,
                   sizeof (FILE_TRACKING_INFORMATION),
                   L"FileTrackingInformation",
                   path);

       do_set_file(handle,
                   FileValidDataLengthInformation,
                   sizeof (FILE_VALID_DATA_LENGTH_INFORMATION),
                   L"FileValidDataLengthInformation",
                   path);

       do_set_file(handle,
                   FileShortNameInformation,
                   sizeof (FILE_NAME_INFORMATION) + 1024,
                   L"FileShortNameInformation",
                   path);

       do_query_volume(handle,
                       FileFsVolumeInformation,
                       sizeof( FILE_FS_VOLUME_INFORMATION ) + 1024,
                       L"FileFsVolumeInformation",
                       path);

       do_query_volume(handle,
                       FileFsSizeInformation,
                       sizeof( FILE_FS_SIZE_INFORMATION ),
                       L"FileFsSizeInformation",
                       path);

       do_query_volume(handle,
                       FileFsDeviceInformation,
                       sizeof( FILE_FS_DEVICE_INFORMATION ) + 1024,
                       L"FileFsDeviceInformation",
                       path);

       do_query_volume(handle,
                       FileFsAttributeInformation,
                       sizeof( FILE_FS_ATTRIBUTE_INFORMATION ),
                       L"FileFsAttributeInformation",
                       path);

       do_query_volume(handle,
                       FileFsControlInformation,
                       sizeof( FILE_FS_CONTROL_INFORMATION ),
                       L"FileFsControlInformation",
                       path);

       do_query_volume(handle,
                       FileFsFullSizeInformation,
                       sizeof( FILE_FS_SIZE_INFORMATION ),
                       L"FileFsFullSizeInformation",
                       path);

       do_query_volume(handle,
                       FileFsObjectIdInformation,
                       sizeof( FILE_FS_OBJECTID_INFORMATION ) + 1024,
                       L"FileFsObjectIdInformation",
                       path);

       do_query_volume(handle,
                       FileFsDriverPathInformation,
                       sizeof( FILE_FS_DRIVER_PATH_INFORMATION ) + 1024,
                       L"FileFsDriverPathInformation",
                       path);

       do_set_volume(handle,
                     FileFsObjectIdInformation,
                     sizeof( FILE_FS_OBJECTID_INFORMATION ) + 1024,
                     L"FileFsObjectIdInformation",
                     path);

       do_set_volume(handle,
                     FileFsControlInformation,
                     sizeof( FILE_FS_CONTROL_INFORMATION ) + 1024,
                     L"FileFsControlInformation",
                     path);

       do_set_volume(handle,
                     FileFsLabelInformation,
                     sizeof( FILE_FS_LABEL_INFORMATION ) + 1024,
                     L"FileFsLabelInformation",
                     path);
    }

    if (flags&FLAGS_DO_SECURITY) {
       do_query_security(handle,
                         0,
                         1024,
                         L"NONE",
                         path);

       do_query_security(handle,
                         OWNER_SECURITY_INFORMATION,
                         1024,
                         L"OWNER_SECURITY_INFORMATION",
                         path);
                       
       do_query_security(handle,
                         GROUP_SECURITY_INFORMATION,
                         1024,
                         L"GROUP_SECURITY_INFORMATION",
                         path);

       do_query_security(handle,
                         DACL_SECURITY_INFORMATION,
                         1024,
                         L"DACL_SECURITY_INFORMATION",
                         path);

       do_query_security(handle,
                         SACL_SECURITY_INFORMATION,
                         1024,
                         L"SACL_SECURITY_INFORMATION",
                         path);

       do_set_security(handle,
                       0,
                       1024,
                       L"NONE",
                       path);

       do_set_security(handle,
                       OWNER_SECURITY_INFORMATION,
                       1024,
                       L"OWNER_SECURITY_INFORMATION",
                       path);
                       
       do_set_security(handle,
                       GROUP_SECURITY_INFORMATION,
                       1024,
                       L"GROUP_SECURITY_INFORMATION",
                       path);

       do_set_security(handle,
                       DACL_SECURITY_INFORMATION,
                       1024,
                       L"DACL_SECURITY_INFORMATION",
                       path);

       do_set_security(handle,
                       SACL_SECURITY_INFORMATION,
                       1024,
                       L"SACL_SECURITY_INFORMATION",
                       path);
    }
    return status;
}

/*
   Do the fast queries on the open path
*/
NTSTATUS
try_fast_query_delete_etc(
    POBJECT_ATTRIBUTES poa,
    PWCHAR             path,
    PWCHAR             type)
{
    PVOID fi = NULL;
    NTSTATUS status;
    ULONG ret;

    if (!(flags&FLAGS_DO_MISC))
       return 0;

    status = STATUS_SUCCESS;
    if (!crashes (path, L"NtQueryAttributesFile", type, L"", L"")) {
        ret = 0;
        do {
           fi = reallocslop(fi,
                            sizeof (FILE_BASIC_INFORMATION));
           status = NtQueryAttributesFile (poa, fi);
           if (!NT_SUCCESS (status)) {
              if (flags&FLAGS_DO_ERRORS)
                 printf ("NtQueryAttributesFile failed %x\n", status);
           }
           testslop(fi,
                    sizeof (FILE_BASIC_INFORMATION), L"NtQueryAttributesFile", L"");
       } while (print_diags (0, ret++) && ret < MAX_RET);
    }

    if (!crashes (path, L"NtQueryFullAttributesFile", type, L"", L"")) {
        ret = 0;
        do {
           fi = reallocslop(fi,
                            sizeof (FILE_NETWORK_OPEN_INFORMATION));
           status = NtQueryFullAttributesFile (poa, fi);
           if (!NT_SUCCESS (status)) {
              if (flags&FLAGS_DO_ERRORS)
                 printf ("NtQueryFullAttributesFile failed %x\n", status);
           }
           testslop(fi,
                    sizeof (FILE_NETWORK_OPEN_INFORMATION), L"NtQueryFULLAttributesFile", L"");
       } while (print_diags (0, ret++) && ret < MAX_RET);
    }

    
    if (!crashes (path, L"NtDeleteFile", type, L"", L"")) {
        ret = 0;
        do {
           status = NtDeleteFile(poa);
           if (!NT_SUCCESS (status)) {
              if (flags&FLAGS_DO_ERRORS)
                 printf ("NtDeleteFile failed %x\n", status);
           }
       } while (print_diags (0, ret++) && ret < MAX_RET);
    }
    free (fi);
    return status;
}

/*
   Do a whole bunch of random things
*/
NTSTATUS misc_functions(
    HANDLE handle,
    PWCHAR path,
    ULONG sync
    )
{
    IO_STATUS_BLOCK iosb;
    NTSTATUS status;
    PVOID buf;
    ULONG bufl;
    LONG i;
    HANDLE sectionhandle;
    LARGE_INTEGER bo, bl;
    ULONG ret, managed_read, managed_write;

    if (!(flags&FLAGS_DO_MISC))
       return STATUS_SUCCESS;

    buf = malloc (bufl = 1024);
    if (buf == 0) {
       printf ("Failed to allocate buffer!\n");
       exit (EXIT_FAILURE);
    }

    managed_read = 0;
    if (!sync) {
       if (!crashes (path, L"NtReadFile", L"", L"", L"")) {
          for (i = bufl; i >= 0; i--) {
             ret = 0;
             do {
                progress_counter++;
                bo.QuadPart = 0;
                status = NtReadFile (handle, NULL, NULL, NULL, &iosb, buf, i,
                                     &bo, NULL);
                if (NT_SUCCESS (status)) {
                   status = iosb.Status;
                   managed_read = 1;
                }
                if (!NT_SUCCESS (status)) {
                   if (flags&FLAGS_DO_ERRORS)
                      printf ("NtReadFile failed %x\n", status);
                }
                progress_counter++;
                bo.QuadPart = 0x7FFFFFFFFFFFFFFF - i + 1;
                status = NtReadFile (handle, NULL, NULL, NULL, &iosb, buf, i,
                                     &bo, NULL);
                if (NT_SUCCESS (status))
                   status = iosb.Status;
                if (!NT_SUCCESS (status)) {
                   if (flags&FLAGS_DO_ERRORS)
                      printf ("NtReadFile failed %x\n", status);
                }
                NtCancelIoFile (handle, &iosb);
             } while (print_diags (0, ret++) && ret < MAX_RET);
          }
       }

       if (managed_read) {
          printf ("Managed to read from the device\n");
          if (diag_file) {
             fwprintf (diag_file, L"Managed to read from device %s\r\n", path);
          }
       }

       managed_write = 0;
       if (!crashes (path, L"NtWriteFile", L"", L"", L"")) {
          for (i = bufl; i >= 0; i--) {
             ret = 0;
             do {
                progress_counter++;
                bo.QuadPart = 0;
                status = NtWriteFile (handle, NULL, NULL, NULL, &iosb, buf, i,
                                      &bo, NULL);
                if (NT_SUCCESS (status)) {
                   status = iosb.Status;
                   managed_write = 0;
                }
                if (!NT_SUCCESS (status)) {
                   if (flags&FLAGS_DO_ERRORS)
                      printf ("NtWriteFile failed %x\n", status);
                }
                /*
                   Wrap to negative call
                */
                progress_counter++;
                bo.QuadPart = 0x7FFFFFFFFFFFFFFF - i + 1;
                status = NtWriteFile (handle, NULL, NULL, NULL, &iosb, buf, i,
                                      &bo, NULL);
                if (NT_SUCCESS (status))
                   status = iosb.Status;
                if (!NT_SUCCESS (status)) {
                   if (flags&FLAGS_DO_ERRORS)
                      printf ("NtWriteFile failed %x\n", status);
                }
                /*
                   Do an append call.
                */
                progress_counter++;
                bo.QuadPart = -1;
                status = NtWriteFile (handle, NULL, NULL, NULL, &iosb, buf, i,
                                      &bo, NULL);
                if (NT_SUCCESS (status))
                   status = iosb.Status;
                if (!NT_SUCCESS (status)) {
                   if (flags&FLAGS_DO_ERRORS)
                      printf ("NtWriteFile failed %x\n", status);
                }
                NtCancelIoFile (handle, &iosb);
             } while (print_diags (0, ret++) && ret < MAX_RET);
          }
       }
       if (managed_write) {
          printf ("Managed to write to the device\n");
          if (diag_file) {
             fwprintf (diag_file, L"Managed to write to the device %s\r\n", path);
          }
       }
    }

    if (!crashes (path, L"NtCancelIoFile", L"", L"", L"")) {
       ret = 0;
       do {
          status = NtCancelIoFile (handle, &iosb);
          if (NT_SUCCESS (status))
             status = iosb.Status;
          if (!NT_SUCCESS (status)) {
             if (flags&FLAGS_DO_ERRORS)
                printf ("NtCancelIoFile failed %x\n", status);
          }
       } while (print_diags (0, ret++) && ret < MAX_RET);
    }

    if (!crashes (path, L"NtFlushBuffersFile", L"", L"", L"")) {
       ret = 0;
       do {
          progress_counter++;
          status = NtFlushBuffersFile (handle, &iosb);
          if (NT_SUCCESS (status))
             status = iosb.Status;
          if (!NT_SUCCESS (status)) {
             if (flags&FLAGS_DO_ERRORS)
                printf ("NtFlushBuffersFile failed %x\n", status);
          }
       } while (print_diags (0, ret++) && ret < MAX_RET);
    }
    if (!crashes (path, L"NtQueryDirectoryFile", L"FileNamesInformation", L"", L"")) {
       ULONG first = 1, j, datalen, l;
       WCHAR bufn[1024];
       PFILE_NAMES_INFORMATION tfni;

       for (i = bufl; i >= 0; i--) {
          ret = 0;
          do {
             progress_counter++;
             status = NtQueryDirectoryFile (handle, NULL, NULL, NULL, &iosb, buf, i,
                                            FileNamesInformation, FALSE, NULL, TRUE);
             if (!NT_SUCCESS (status)) {
                if (flags&FLAGS_DO_ERRORS)
                   printf ("NtQueryDirectoryFile failed for type FileNamesInformation %x\n",
                           status);
             } else if (first && status != STATUS_PENDING) {
                first = 0;
                datalen = (ULONG) iosb.Information;
                for (j = 0; j < datalen; j += tfni->NextEntryOffset) {
                   tfni = (PFILE_NAMES_INFORMATION)((PCHAR)buf + j);
                   memset (bufn, 0, sizeof (bufn));
                   l = tfni->FileNameLength / sizeof (WCHAR);
                   if (l >= sizeof (bufn) / sizeof (bufn[0]))
                      l = sizeof (bufn) / sizeof (bufn[0]) - 1;
                   wcsncpy (bufn, tfni->FileName, l);
                   wprintf (L"-> %s\n", bufn);
                   if (tfni->NextEntryOffset == 0)
                      break;
                }
             }
          } while (print_diags (0, ret++) && ret < MAX_RET);
       }
    }
    if (!crashes (path, L"NtQueryDirectoryFile", L"FileDirectoryInformation", L"", L"")) {
       for (i = bufl; i >= 0; i--) {
          ret = 0;
          do {
             progress_counter++;
             status = NtQueryDirectoryFile (handle, NULL, NULL, NULL, &iosb, buf, i,
                                            FileDirectoryInformation, FALSE, NULL, TRUE);
             if (NT_SUCCESS (status))
                status = iosb.Status;
             if (!NT_SUCCESS (status)) {
                if (flags&FLAGS_DO_ERRORS)
                   printf ("NtQueryDirectoryFile failed for type FileDirectoryInformation %x\n", status);
             }
          } while (print_diags (0, ret++) && ret < MAX_RET);
       }
    }
    if (!crashes (path, L"NtQueryDirectoryFile", L"FileFullDirectoryInformation", L"", L"")) {
       for (i = bufl; i >= 0; i--) {
          ret = 0;
          do {
             progress_counter++;
             status = NtQueryDirectoryFile (handle, NULL, NULL, NULL, &iosb, buf, i,
                                            FileFullDirectoryInformation, FALSE, NULL, TRUE);
             if (NT_SUCCESS (status))
                status = iosb.Status;
             if (!NT_SUCCESS (status)) {
                if (flags&FLAGS_DO_ERRORS)
                   printf ("NtQueryDirectoryFile failed for type FileFullDirectoryInformation %x\n", status);
             }
          } while (print_diags (0, ret++) && ret < MAX_RET);
       }
    }

    if (!crashes (path, L"NtQueryDirectoryFile", L"FileBothDirectoryInformation", L"", L"")) {
       for (i = bufl; i >= 0; i--) {
          ret = 0;
          do {
             progress_counter++;
             status = NtQueryDirectoryFile (handle, NULL, NULL, NULL, &iosb, buf, i,
                                            FileBothDirectoryInformation, FALSE, NULL, TRUE);
             if (NT_SUCCESS (status))
                status = iosb.Status;
             if (!NT_SUCCESS (status)) {
                if (flags&FLAGS_DO_ERRORS)
                   printf ("NtQueryDirectoryFile failed for type FileBothDirectoryInformation %x\n", status);
             }
          } while (print_diags (0, ret++) && ret < MAX_RET);
       }
    }

    if (!crashes (path, L"NtQueryDirectoryFile", L"FileObjectIdInformation", L"", L"")) {
       for (i = bufl; i >= 0; i--) {
          ret = 0;
          do {
             progress_counter++;
             status = NtQueryDirectoryFile (handle, NULL, NULL, NULL, &iosb, buf, i,
                                            FileObjectIdInformation, FALSE, NULL, TRUE);
             if (NT_SUCCESS (status))
                status = iosb.Status;
             if (!NT_SUCCESS (status)) {
                if (flags&FLAGS_DO_ERRORS)
                   printf ("NtQueryDirectoryFile failed for type FileObjectIdInformation %x\n", status);
             }
          } while (print_diags (0, ret++) && ret < MAX_RET);
       }
    }

    if (!crashes (path, L"NtQueryDirectoryFile", L"FileQuotaInformation", L"", L"")) {
       for (i = bufl; i >= 0; i--) {
          ret = 0;
          do {
             progress_counter++;
             status = NtQueryDirectoryFile (handle, NULL, NULL, NULL, &iosb, buf, i,
                                            FileQuotaInformation, FALSE, NULL, TRUE);
             if (NT_SUCCESS (status))
                status = iosb.Status;
             if (!NT_SUCCESS (status)) {
                if (flags&FLAGS_DO_ERRORS)
                   printf ("NtQueryDirectoryFile failed for type FileQuotaInformation %x\n", status);
             }
          } while (print_diags (0, ret++) && ret < MAX_RET);
       }
    }

    if (!crashes (path, L"NtQueryDirectoryFile", L"FileReparsePointInformation", L"", L"")) {
       for (i = bufl; i >= 0; i--) {
          ret = 0;
          do {
             progress_counter++;
             status = NtQueryDirectoryFile (handle, NULL, NULL, NULL, &iosb, buf, i,
                                            FileReparsePointInformation, FALSE, NULL, TRUE);
             if (NT_SUCCESS (status))
                status = iosb.Status;
             if (!NT_SUCCESS (status)) {
                if (flags&FLAGS_DO_ERRORS)
                   printf ("NtQueryDirectoryFile failed for type FileReparsePointInformation %x\n", status);
             }
          } while (print_diags (0, ret++) && ret < MAX_RET);
       }
    }

    if (!crashes (path, L"NtVdmContol", L"VdmQueryDir", L"", L"")) {
       VDMQUERYDIRINFO vqdi;
       UNICODE_STRING name;

       memset (&vqdi, 0, sizeof (vqdi));
       RtlInitUnicodeString (&name, L"*");
       for (i = bufl; i >= 0; i--) {
          ret = 0;
          do {
             progress_counter++;
             vqdi.FileHandle = handle;
             vqdi.FileInformation = buf;
             vqdi.Length = bufl;
             vqdi.FileName = &name;
             
             status = NtVdmControl(VdmQueryDir, &vqdi);
             if (!NT_SUCCESS (status)) {
                if (flags&FLAGS_DO_ERRORS)
                   printf ("NtVdmControl failed for type VdmQueryDir %x\n", status);
             }
          } while (print_diags (0, ret++) && ret < MAX_RET);
       }
    }

    if (!sync) {
       if (!crashes (path, L"NtNotifyChangeDirectoryFile", L"", L"", L"")) {
          for (i = bufl; i >= 0; i--) {
             ret = 0;
             do {
                progress_counter++;
                status = NtNotifyChangeDirectoryFile (handle,
                                                      NULL,
                                                      NULL,
                                                      NULL,
                                                      &iosb,
                                                      buf,
                                                      i,
                                                      FILE_NOTIFY_CHANGE_FILE_NAME,
                                                      FALSE);
                if (NT_SUCCESS (status))
                   status = iosb.Status;
                if (!NT_SUCCESS (status)) {
                   if (flags&FLAGS_DO_ERRORS)
                      printf ("NtNotifyChangeDirectoryFile failed %x\n", status);
                }
                NtCancelIoFile (handle, &iosb);
             } while (print_diags (0, ret++) && ret < MAX_RET);
          }
       }
    }

    /*
       Query the EA info
    */
    do_query_ea (handle, path);

    if (!crashes (path, L"NtCreateSection", L"", L"", L"")) {
       ret = 0;
       do {
          status = NtCreateSection (&sectionhandle, GENERIC_READ,
                                    NULL, NULL, PAGE_READONLY, SEC_COMMIT, handle);
          if (NT_SUCCESS (status)) {
             printf ("Created a section!\n");
             status = NtClose (sectionhandle);
             if (!NT_SUCCESS (status)) {
                printf ("NtClose on section handle failed %x\n", status);
             }
          } else if (flags&FLAGS_DO_ERRORS){
             printf ("NtCreateSection failed %x\n", status);
          }
       } while (print_diags (0, ret++) && ret < MAX_RET);
       ret = 0;
       do {
          status = NtCreateSection (&sectionhandle, GENERIC_READ,
                                    NULL, NULL, PAGE_READONLY, SEC_IMAGE, handle);
          if (NT_SUCCESS (status)) {
             printf ("Created a section!\n");
             status = NtClose (sectionhandle);
             if (!NT_SUCCESS (status)) {
                printf ("NtClose on section handle failed %x\n", status);
             }
          } else if (flags&FLAGS_DO_ERRORS){
             printf ("NtCreateSection failed for image %x\n", status);
          }
       } while (print_diags (0, ret++) && ret < MAX_RET);
    }

    if (!crashes (path, L"NtLockFile", L"", L"", L"")) {
       ret = 0;
       do {
          progress_counter++;
          bl.QuadPart = 1;
          bo.QuadPart = 1;
          status = NtLockFile (handle, NULL, NULL, NULL, &iosb, &bo, &bl, 0, TRUE, FALSE);
          if (NT_SUCCESS (status) && status != STATUS_PENDING) {
             NtUnlockFile (handle, &iosb, &bo, &bl, 0);
          }
          if (!NT_SUCCESS (status)) {
             if (flags&FLAGS_DO_ERRORS)
                printf ("NtLockFile failed %x\n", status);
          }
          NtCancelIoFile (handle, &iosb);
       } while (print_diags (0, ret++) && ret < MAX_RET);
    }
    if (!crashes (path, L"NtUnlockFile", L"", L"", L"")) {
       ret = 0;
       do {
          progress_counter++;
          status = NtUnlockFile (handle, &iosb, &bo, &bl, 0);
          if (NT_SUCCESS (status))
             status = iosb.Status;
          if (!NT_SUCCESS (status)) {
             if (flags&FLAGS_DO_ERRORS)
                printf ("NtUnlockFile failed %x\n", status);
          }
       } while (print_diags (0, ret++) && ret < MAX_RET);
    }
    if (!crashes (path, L"NtQueryQuotaInformationFile", L"", L"", L"")) {
       for (i = bufl; i >= 0; i--) {
          ret = 0;
          do {
             progress_counter++;
             status = NtQueryQuotaInformationFile(handle, &iosb, buf, i, FALSE, NULL, 0,
                                                  NULL, TRUE);
              if (NT_SUCCESS (status))
                 status = iosb.Status;
             if (!NT_SUCCESS (status)) {
                if (flags&FLAGS_DO_ERRORS)
                   printf ("NtQueryQuotaInformationFile failed %x\n", status);
             }
          } while (print_diags (0, ret++) && ret < MAX_RET);
       }
    }
    if (flags&FLAGS_DO_WINSOCK) {
       if (!crashes (path, L"TransmitFile", L"", L"", L"")) {
         progress_counter++;
         if (!TransmitFile (cs, handle, 10, 0, NULL, NULL, TF_WRITE_BEHIND)) {
            if (flags&FLAGS_DO_ERRORS)
               printf ("TransmitFile failed %d\n", WSAGetLastError ());
         }
       }
    }
    NtCancelIoFile (devmap->handle, &iosb);
    free (buf);
    return status;
}

/*
   Get the access mask for this handle
*/
NTSTATUS
get_handle_access (HANDLE handle, PACCESS_MASK access)
{
   NTSTATUS status;
   OBJECT_BASIC_INFORMATION obi;
   //
   // Find out our handle access so we can know what IOCTLs and FSCTLs we can do.
   //
   status = NtQueryObject (handle,
                           ObjectBasicInformation,
                           &obi,
                           sizeof (obi),
                           NULL);
   if (NT_SUCCESS (status)) {
      *access = obi.GrantedAccess;
   } else {
      printf ("NtQueryObject for handle access failed %x\n", status);
      *access = 0;
   }
   return status;
}

NTSTATUS check_tdi_handle (HANDLE handle)
{
   NTSTATUS status;
   IO_STATUS_BLOCK iosb;
   TDI_REQUEST_QUERY_INFORMATION trqi;
   TDI_PROVIDER_INFO tpi;

   trqi.QueryType = TDI_QUERY_PROVIDER_INFORMATION;
   status = NtDeviceIoControlFile (handle, sync_event, NULL, NULL,
                                   &iosb,
                                   IOCTL_TDI_QUERY_INFORMATION,
                                   &trqi,
                                   sizeof (trqi),
                                   &tpi,
                                   sizeof (tpi));
   if (status == STATUS_PENDING) {
      status = NtWaitForSingleObject (sync_event, FALSE, NULL);
      if (!NT_SUCCESS (status)) {
         printf ("NtWaitForSingleObject failed %x\n", status);
      } else {
         status = iosb.Status;
      }
   }
   if (!NT_SUCCESS (status)) {
      printf ("NtDeviceIoControlFile failed for IOCTL_TDI_QUERY_INFORMATION %x\n", status);
      return status;
   }
   printf ("Detected a TDI driver\n");
   return status;
}
//
// Do a load of opens etc relative from the current handle
//
NTSTATUS
do_sub_open_etc(
    HANDLE handle,
    PWCHAR s,
    PWCHAR path
)
{
    OBJECT_ATTRIBUTES       oa;
    UNICODE_STRING name;
    NTSTATUS status;
    IO_STATUS_BLOCK iosb;
    ACCESS_MASK access;
    LARGE_INTEGER tmo;
    PWCHAR as;

    if (wcslen (s) > 30) {
       as = L"Big...";
    } else {
       as = s;
    }

    if (crashes (path, L"Sub open", as, L"", L""))
       return STATUS_SUCCESS;

    wprintf (L"Doing sub open for %s\n", as);
    RtlInitUnicodeString (&name, s);
    InitializeObjectAttributes(&oa,
                               &name,
                               OBJ_CASE_INSENSITIVE,
                               handle,
                               NULL);
    status = NtCreateFile(&handle,
                          MAXIMUM_ALLOWED,
                          &oa,
                          &iosb,
                          NULL,
                          0,
                          0,
                          FILE_OPEN,
                          0,
                          NULL,
                          0);
    if (NT_SUCCESS (status))
       status = iosb.Status;
    if (NT_SUCCESS (status)) {
       status = get_handle_access (handle, &access);
       wprintf (L"Sub open for %s worked with access %x\n", as, access);
       if (DELETE&access) {
          printf ("Got delete access to device\n");
          if (diag_file) {
             fwprintf (diag_file, L"Got DELETE access to %s\r\n", path);
          }
       }
       if (WRITE_DAC&access) {
          printf ("Got write_dac access to device\n");
          if (diag_file) {
             fwprintf (diag_file, L"Got WRITE_DAC access to %s\r\n", path);
          }
       }
       if (WRITE_OWNER&access) {
          printf ("Got write_owner access to device\n");
          if (diag_file) {
             fwprintf (diag_file, L"Got WRITE_OWNER access to %s\r\n", path);
          }
       }
       if (FILE_WRITE_ACCESS&access) {
          printf ("Got write access to device\n");
          if (diag_file) {
             fwprintf (diag_file, L"Got FILE_WRITE_ACCESS access to %s\r\n", path);
          }
       }
       if (FILE_READ_ACCESS&access) {
          printf ("Got read access to device\n");
          if (diag_file) {
             fwprintf (diag_file, L"Got FILE_READ_ACCESS access to %s\r\n", path);
          }
       }
       query_object(handle, NULL, path);
       misc_functions (handle, path, 0);
       status = NtClose (handle);
       if (!NT_SUCCESS (status)) {
          printf ("NtClose failed %x\n", status);
       }
    } else {
       printf ("NtCreateFile failed %x\n", status);
    }
    if (crashes (path, L"Sub create pipe", as, L"", L""))
       return STATUS_SUCCESS;

    tmo.QuadPart = 1000;
    status = NtCreateNamedPipeFile (&handle,
                                    MAXIMUM_ALLOWED,
                                    &oa,
                                    &iosb,
                                    FILE_SHARE_READ|FILE_SHARE_WRITE,
                                    FILE_CREATE,
                                    0,
                                    FILE_PIPE_MESSAGE_TYPE,
                                    FILE_PIPE_MESSAGE_MODE,
                                    FILE_PIPE_COMPLETE_OPERATION,
                                    10,
                                    1000, 1000,
                                    &tmo);
    if (NT_SUCCESS (status))
       status = iosb.Status;
    if (NT_SUCCESS (status)) {
       status = get_handle_access (handle, &access);
       wprintf (L"Sub create pipe for %s worked with access %x\n", as, access);
       if (DELETE&access) {
          printf ("Got delete access to device\n");
       }
       if (WRITE_DAC&access) {
          printf ("Got write_dac access to device\n");
       }
       if (WRITE_OWNER&access) {
          printf ("Got write_owner access to device\n");
       }
       query_object(handle, NULL, path);
       misc_functions (handle, path, 0);
       status = NtClose (handle);
       if (!NT_SUCCESS (status)) {
          printf ("NtClose failed %x\n", status);
       }
    } else {
       printf ("NtCreateNamedPipeFile failed %x\n", status);
    }
    if (crashes (path, L"Sub create mailslot", as, L"", L""))
       return STATUS_SUCCESS;

    tmo.QuadPart = 1000;
    status = NtCreateMailslotFile (&handle,
                                   MAXIMUM_ALLOWED,
                                   &oa,
                                   &iosb,
                                   0,
                                   1024,
                                   256,
                                   &tmo);
    if (NT_SUCCESS (status)) {
       status = get_handle_access (handle, &access);
       wprintf (L"Sub create mailslot for %s worked with access %x\n", as, access);
       if (DELETE&access) {
          printf ("Got delete access to device\n");
       }
       if (WRITE_DAC&access) {
          printf ("Got write_dac access to device\n");
       }
       if (WRITE_OWNER&access) {
          printf ("Got write_owner access to device\n");
       }
       query_object(handle, NULL, path);
       misc_functions (handle, path, 0);
       status = NtClose (handle);
       if (!NT_SUCCESS (status)) {
          printf ("NtClose failed %x\n", status);
       }
    } else {
       printf ("NtCreateMailslotFile failed %x\n", status);
    }

    try_fast_query_delete_etc (&oa, path, L"Sub open");
    return status;
}
//
// Try a few opens relative to the device its self.
//
NTSTATUS
try_funny_opens(
    HANDLE handle,
    PWCHAR path
)
{
    ULONG ret, i;
    static PWCHAR big=NULL;
    static ULONG bigl;

    if (!(flags&FLAGS_DO_SUBOPENS))
       return 0;

    if (big == NULL) {
       big = malloc (bigl = 0x10000);
       if (big == NULL) {
          printf ("Memory allocation failure in try_funny_opens!\n");
          exit (EXIT_FAILURE);
       }
       bigl /= sizeof (big[0]);
       for (i = 0; i < bigl; i++)
          big[i] = 'A';
       big[bigl - 3] = '\0';
    }
    ret = 0;
    do {
       do_sub_open_etc (handle, L"", path);
       do_sub_open_etc (handle, L" ", path);
       do_sub_open_etc (handle, L"\\", path);
       do_sub_open_etc (handle, L"\\\\\\\\\\\\", path);
       do_sub_open_etc (handle, big, path);
       if (flags&FLAGS_DO_STREAMS) {
          do_sub_open_etc (handle, L":", path);
          do_sub_open_etc (handle, L" :", path);
          do_sub_open_etc (handle, L": ", path);
          do_sub_open_etc (handle, L": ", path);
          do_sub_open_etc (handle, L"::", path);
          do_sub_open_etc (handle, L": :", path);
          do_sub_open_etc (handle, L"::$UNUSED", path);
          do_sub_open_etc (handle, L"::$STANDARD_INFORMATION", path);
          do_sub_open_etc (handle, L"::$ATTRIBUTE_LIST", path);
          do_sub_open_etc (handle, L"::$FILE_NAME", path);
          do_sub_open_etc (handle, L"::$OBJECT_ID", path);
          do_sub_open_etc (handle, L"::$SECURITY_DESCRIPTOR", path);
          do_sub_open_etc (handle, L"::$VOLUME_NAME", path);
          do_sub_open_etc (handle, L"::$VOLUME_INFORMATION", path);
          do_sub_open_etc (handle, L"::$DATA", path);
          do_sub_open_etc (handle, L"::$INDEX_ROOT", path);
          do_sub_open_etc (handle, L"::$INDEX_ALLOCATION", path);
          do_sub_open_etc (handle, L"::$BITMAP", path);
          do_sub_open_etc (handle, L"::$REPARSE_POINT", path);
          do_sub_open_etc (handle, L"::$EA_INFORMATION", path);
          do_sub_open_etc (handle, L"::$PROPERTY_SET", path);
          do_sub_open_etc (handle, L"::$FIRST_USER_DEFINED_ATTRIBUTE", path);
          do_sub_open_etc (handle, L"::$END", path);
       }
    } while (print_diags (0, ret++) && ret < MAX_RET);
    return 0;
}

VOID
randomize_buf(
    PVOID buf,
    ULONG bufl
)
{
   ULONG i;
   PUCHAR pc = buf;

   for (i = 0; i < bufl; i++) {
      pc[i] = rand() & 0xff;
   }
}

/*
   Thread used to randomize buffers
*/
DWORD WINAPI
randomize(
    PVOID buf
)
{
   ULONG i;
   PUCHAR pc = buf;

   while (1) {
      try {
          for (i = 0; i < BIGBUF_SIZE; i++) {
              pc[i] = rand() & 0xff;
          }
      } except (EXCEPTION_EXECUTE_HANDLER) {
      }
      Sleep (0);
   }
   return 0;
}


// changeprot and alerter won't return and we do not need to be 
// told this by the compiler
#pragma warning(disable:4715)

DWORD WINAPI
changeprot(
    PVOID buf
)
{
   DWORD old;

   while (1) {
      if (!VirtualProtect (buf, 1, PAGE_READONLY, &old)) {
         printf ("VirtualProtect failed %d\n", GetLastError ());
      }
      Sleep (1);
      if (!VirtualProtect (buf, 1, PAGE_NOACCESS, &old)) {
         printf ("VirtualProtect failed %d\n", GetLastError ());
      }
      Sleep (1);
      if (!VirtualProtect (buf, 1, PAGE_GUARD|PAGE_READONLY, &old)) {
         printf ("VirtualProtect failed %d\n", GetLastError ());
      }
      Sleep (1);
   } 
   return 0;
}
 

/*
   Thread used to alert the main
*/
DWORD WINAPI
alerter(
    PVOID handle
)
{
   NTSTATUS status;
   ULONG last = progress_counter, count;

   //
   // Do a nasty hack to keep the main thread moving if it gets stuck in a sync IOCTL
   //
   while (1) {
      if (flags&FLAGS_DO_ALERT) {
         Sleep (0);
      } else {
         last = progress_counter;
         Sleep (60000);
         if (progress_counter == 0 || progress_counter != last) {
            continue;
         }
         printf ("Alerting thread\n");
      }
//      status = NtAlertResumeThread ((HANDLE) handle, &count);
//      if (!NT_SUCCESS (status)) {
//          printf ("NtAlertResumeThread failed %x\n", status);
//      }
      status = NtAlertThread ((HANDLE) handle);
      if (!NT_SUCCESS (status)) {
          printf ("NtAlertThread failed %x\n", status);
      }
      alerted++;
      if (alerted > 10) {
          if (diag_file) {
              fwprintf (diag_file, L"Main thread appears hung. Teminating process\r\n");
              fflush (diag_file);
              NtTerminateProcess (NtCurrentProcess (), 0);
          }
      }
   }
   return 0;
}

#pragma warning(3:4715)

void record_ioctl (PIOCTLREC *piorec, ULONG ioctl, NTSTATUS status)
{
   PIOCTLREC iorec = *piorec;
   IOCTLINFO tmp;
   ULONG i, j, new;

   if (!iorec) {
      iorec = malloc (sizeof (*iorec) +
                      INITIAL_IOCTL_TAILOR_SIZE * sizeof (iorec->ioctl[0]));
      if (!iorec) {
         return;
      }
      *piorec = iorec;
      iorec->total = INITIAL_IOCTL_TAILOR_SIZE;
      iorec->count = 0;
      iorec->start = 0;
   }
   new = 1;
   for (i = 0; i < iorec->count; i++) {
      if (iorec->ioctl[i].ioctl == ioctl && iorec->ioctl[i].status == status) {
         return;
      }
      if (iorec->ioctl[i].status == status) {
         new = 0;
         if (iorec->ioctl[i].count > MAX_IOCTL_TAILOR) {
            return; // too many seen of this one
         }
         if (++iorec->ioctl[i].count > MAX_IOCTL_TAILOR) {
            printf ("Removing IOCTLs with status %X\n", status);
            for (j = i + 1; j < iorec->count; j++) {
               if (iorec->ioctl[j].status == status) {
/*
                  printf ("Removing IOCTL %X with status %X\n",
                          iorec->ioctl[j].ioctl, iorec->ioctl[j].status);
*/
                  iorec->ioctl[j] = iorec->ioctl[--iorec->count];
                  j--;
               }
            }
            tmp = iorec->ioctl[iorec->start];
            iorec->ioctl[iorec->start++] = iorec->ioctl[i];
            iorec->ioctl[i] = tmp;
            return;
         }
         break;
      }
   }
   if (new) {
      printf ("New status for IOCTL %X status %X\n", ioctl, status);
   }
   if (iorec->total == iorec->count) {
      iorec = realloc (iorec, sizeof (*iorec) +
                       iorec->total * 2 * sizeof (iorec->ioctl[0]));
      if (!iorec) {
         return;
      }
      *piorec = iorec;
      iorec->total *= 2;
   }
   i = iorec->count;
   iorec->ioctl[i].status = status;
   iorec->ioctl[i].ioctl = ioctl;
   iorec->ioctl[i].count = 0;
   iorec->count = i + 1;
}

void do_tailored_ioctl (PDEVMAP                 devmap,
                        PWCHAR                  path,
                        PIOCTLREC               iorec,
                        ULONG fsctl)
{
   ULONG                   method, ioctl_val;
   PVOID                   inbuf, outbuf;
   ULONG                   inlen, outlen;
   static IO_STATUS_BLOCK  static_iosb;
   ULONG                   ret;
   ULONG                   i, j, k;
   WCHAR                   num[20];
   NTSTATUS                status;
   ULONG                   tmp;
   DWORD                   old;
   ULONG                   done_something;   

   if (!iorec) {
      return;
   }

   do {
      done_something = 0;
      for (i = 1; i < iorec->count; i++) {
         IOCTLINFO ii;
         if (iorec->ioctl[i-1].ioctl < iorec->ioctl[i].ioctl) {
            continue;
         } else if (iorec->ioctl[i-1].ioctl == iorec->ioctl[i].ioctl &&
                    iorec->ioctl[i-1].count <= iorec->ioctl[i].count) {
            continue;
         }
         ii = iorec->ioctl[i-1];
         iorec->ioctl[i-1] = iorec->ioctl[i];
         iorec->ioctl[i] = ii;
         done_something = 1;
      }
   } while (done_something);

   for (i = 0; i < iorec->count; i++) {
      if (iorec->ioctl[i].count >= MAX_IOCTL_TAILOR) {
         continue;
      }
      if (i > 0 && iorec->ioctl[i].ioctl == iorec->ioctl[i-1].ioctl) {
         continue;
      }
      ioctl_val = iorec->ioctl[i].ioctl;
      method = ioctl_val&0x3;
      _snwprintf (num, sizeof (num) / sizeof (WCHAR), L"%%X%X", ioctl_val);
      if (crashes (path, L"IOCTL value", num, L"", L""))
         continue;

      alerted = 0;
      for (j = 0; j < max_tailured_calls; j += RAND_REP) {
         ret = 0;
         do {
            if (ret != 0) {
               printf ("Re-doing random with ioctl %%X%x\n", ioctl_val);
            }

            for (k = 0; k < RAND_REP; k++) {
               switch(method) {
                  case METHOD_BUFFERED :

                     inlen  = RandInRange( ioctl_inbuf_min,  ioctl_inbuf_max );
                     outlen = RandInRange( ioctl_outbuf_min, ioctl_outbuf_max );

                     inbuf = bigbuf;
                     if (outlen == 0)
                        outbuf = (PVOID) -1024;
                     else
                        outbuf = bigbuf;

                     randomize_buf (inbuf, inlen);
                     break;

                  case METHOD_IN_DIRECT :
                  case METHOD_OUT_DIRECT :

                     inlen  = RandInRange( ioctl_inbuf_min,  ioctl_inbuf_max );
                     outlen = RandInRange( ioctl_outbuf_min, ioctl_outbuf_max );

                     inbuf = bigbuf;
                     outbuf = &bigbuf[BIGBUF_SIZE - outlen];
                     randomize_buf (inbuf, inlen);
                     randomize_buf (outbuf, outlen);

                     break;

                  case METHOD_NEITHER :

                      switch (rand ()&3) {
                         //
                         // Completely random pointers. We may corrupt ourselves here
//                         case 0 :
//                            inlen = rand32();
//                            outlen = rand32();
//
//                            inbuf = (PVOID)rand32();
//                            outbuf = (PVOID)rand32();
//                            break;

                         //
                         // Kernel crashing pointers with smallish lengths
                         case 1 :
                            inbuf = (PVOID) -1024;
                            outbuf = (PVOID) -1024;
                            inlen  = RandInRange( ioctl_inbuf_min,  ioctl_inbuf_max );
                            outlen = RandInRange( ioctl_outbuf_min, ioctl_outbuf_max );
                            break;
                         //
                         // Good pointers
                         case 0 :
                         case 2 :
                            inlen  = RandInRange( ioctl_inbuf_min,  ioctl_inbuf_max );
                            outlen = RandInRange( ioctl_outbuf_min, ioctl_outbuf_max );

                            inbuf  = &bigbuf[BIGBUF_SIZE - outlen];
                            outbuf = &bigbuf[BIGBUF_SIZE - outlen];
                            break;
                         //
                         // Bad user mode pointers
                         case 3 :
                            inlen  = rand() & 0xFFFF;
                            outlen = rand() & 0xFFFF;
                            inbuf  = UintToPtr( rand() & 0xFFFF );
                            outbuf = UintToPtr( rand() & 0xFFFF );
                            break;
                         break;
                   }
               }

               progress_counter++;
               if (!fsctl) {
                  status = NtDeviceIoControlFile(devmap->handle,
                                                 NULL,
                                                 NULL,
                                                 NULL,
                                                 &static_iosb,
                                                 ioctl_val,
                                                 inbuf,
                                                 inlen,
                                                 outbuf,
                                                 outlen);
               } else {
                  status = NtFsControlFile(devmap->handle,
                                           NULL,
                                           NULL,
                                           NULL,
                                           &static_iosb,
                                           ioctl_val,
                                           inbuf,
                                           inlen,
                                           outbuf,
                                           outlen);
               }
            }
            NtCancelIoFile (devmap->handle, &static_iosb);
            if (alerted > 5) {
               break;
            }
         } while (print_diags ((method == METHOD_BUFFERED)?0:DIAG_NOEXCEPTIONS, ret++) && ret < MAX_RET);
         if (alerted > 5) {
            break;
         } 

      }
      if (!(flags&FLAGS_DO_PROT) ||
          crashes (path, L"IOCTL prot value", num, L"", L"")) {
         continue;
      }

      status = NtResumeThread (changethread, &tmp);
      if (!NT_SUCCESS (status)) {
         printf ("NtResumeThread failed %x\n", status);
      }

      for (i = 1; i < PROT_REP; i += RAND_REP) {
         ret = 0;
         do {
            if (ret != 0) {
               printf ("Re-doing random with ioctl %%X%x\n", ioctl_val);
            }
            for (k = 0; k < RAND_REP; k++) {
               if (!VirtualProtect (bigbuf, 1, PAGE_READWRITE, &old)) {
                  printf ("VirtualProtect failed %d\n", GetLastError ());
               }
               inlen  = RandInRange( ioctl_inbuf_min,  ioctl_inbuf_max );
               outlen = RandInRange( ioctl_outbuf_min, ioctl_outbuf_max );

               inbuf = bigbuf;
               outbuf = bigbuf;

               progress_counter++;
               if (!fsctl) {
                  status = NtDeviceIoControlFile(devmap->handle,
                                                 NULL,
                                                 NULL,
                                                 NULL,
                                                 &static_iosb,
                                                 ioctl_val,
                                                 inbuf,
                                                 inlen,
                                                 outbuf,
                                                 outlen);
               } else {
                  status = NtFsControlFile(devmap->handle,
                                           NULL,
                                           NULL,
                                           NULL,
                                           &static_iosb,
                                           ioctl_val,
                                           inbuf,
                                           inlen,
                                           outbuf,
                                           outlen);
               }
            }
            NtCancelIoFile (devmap->handle, &static_iosb);
            if (alerted > 5) {
               break;
            }
         } while (print_diags (DIAG_NOEXCEPTIONS, ret++) && ret < MAX_RET);

         if (alerted > 5) {
            break;
         }
      }
      status = NtSuspendThread (changethread, &tmp);
      if (!NT_SUCCESS (status)) {
         printf ("NtResumeThread failed %x\n", status);
      }
      if (!VirtualProtect (bigbuf, 1, PAGE_READWRITE, &old)) {
         printf ("VirtualProtect failed %d\n", GetLastError ());
      }
   }
}

NTSTATUS
do_ioctl(
    PDEVMAP                 devmap,
    PWCHAR                  path
)
{
    ULONG                   function, method, access, i, j, ioctl_val;
    static IO_STATUS_BLOCK  static_iosb;
    PVOID                   inbuf, outbuf;
    ULONG                   inlen, outlen;
    ULONG                   tmp;
    NTSTATUS                status;
    ULONG                   devtype;
    ULONG                   ret;
    BOOL                    hit_leak;
    WCHAR                   num[20];
    PIOCTLREC               iorec = NULL, fsrec = NULL;
    ULONG                   set_rand;

    if ((flags&(FLAGS_DO_IOCTL_NULL|FLAGS_DO_FSCTL_NULL)) &&
        ioctl_inbuf_min == 0 && ioctl_outbuf_min == 0) {
       //
       // do I/O calls with no buffer
       //
       for (function = ioctl_min_function; function <= ioctl_max_function; function++) {
          _snwprintf (num, sizeof (num) / sizeof (WCHAR), L"%d", function);
          if (crashes (path, L"IOCTL function ", num, L"", L""))
             continue;
          for (devtype = ioctl_min_devtype; devtype <= ioctl_max_devtype; devtype++) {
              ret = 0;
              do {
                 if (ret != 0)
                    printf ("Re-doing devtype = %d, function = %d\n", devtype, function);
                 hit_leak = FALSE;
                 for (access = FILE_ANY_ACCESS;
                      access <= (devmap->access&(FILE_READ_ACCESS|FILE_WRITE_ACCESS));
                      access++) {
                     for (method = 0; method < 4; method++) {
                         ioctl_val = CTL_CODE(devtype, function, method, access);

                         progress_counter++;
                         if ((flags&FLAGS_DO_IOCTL_NULL)) {
                            status = NtDeviceIoControlFile(devmap->handle,
                                                           NULL, NULL, NULL,
                                                           &static_iosb,
                                                           ioctl_val,
                                                           (PVOID) -1024,
                                                           0,
                                                           (PVOID) -1024,
                                                           0);
                            record_ioctl (&iorec, ioctl_val, status);
                         }
                         if ((flags&FLAGS_DO_FSCTL_NULL)) {
                            status = NtFsControlFile(devmap->handle, NULL, NULL, NULL,
                                                     &static_iosb,
                                                     ioctl_val,
                                                     (PVOID) -1024,
                                                     0,
                                                     (PVOID) -1024,
                                                     0);
                            record_ioctl (&fsrec, ioctl_val, status);
                        }
                        Sleep (0);
                     }
                 }
                 if (ret > (MAX_RET * 95 ) / 100) {
                    NtCancelIoFile (devmap->handle, &static_iosb);
                    //
                    // Report exceptions for method buffered.
                    // This may be ok but its worth knowing.
                    //
                    if (print_diags ((method == METHOD_BUFFERED)?0:DIAG_NOEXCEPTIONS, ret++)) {
                       printf ("IOCTL/FSCTL %x devtype %x (%d) function %x (%d) access %x method %x\n",
                               ioctl_val, devtype, devtype, function, function,
                               access, method);
                       hit_leak = TRUE;
                    }
                 }
                 NtCancelIoFile (devmap->handle, &static_iosb);
             } while ((hit_leak || print_diags ((method == METHOD_BUFFERED)?0:DIAG_NOEXCEPTIONS, ret++)) && ret < MAX_RET);
          }
       }
    }


    if (flags&(FLAGS_DO_IOCTL_RANDOM|FLAGS_DO_FSCTL_RANDOM) &&
        !crashes (path, L"IOCTL", L"random", L"", L"")) {
//       if (!(flags&FLAGS_DO_RANDOM_DEVICE)) {
//          status = NtResumeThread (randthread, &tmp);
//          if (!NT_SUCCESS (status)) {
//             printf ("NtResumeThread failed %x\n", status);
//          }
//       }
       for (i = 0; i < max_random_calls; i += RAND_REP) {
           if (ioctl_min_function >= ioctl_max_function)
              function = ioctl_min_function;
           else
              function = RandInRange( ioctl_min_function, ioctl_max_function );
           ret = 0;
           do {
              if (ret != 0) {
                 printf ("Re-doing random with function %x\n", function);
              }
              for (j = 0; j < RAND_REP; j++) {
                 if (ioctl_min_devtype >= ioctl_max_devtype)
                    devtype = ioctl_min_devtype;
                 else
                    devtype = RandInRange( ioctl_min_devtype, ioctl_max_devtype );

                 switch (rand ()&3) {
                    case 0 :
                       if (fsrec && fsrec->count - fsrec->start) {
                          devtype = fsrec->ioctl[fsrec->start + 
                             (rand()%(fsrec->count - fsrec->start))].ioctl>>16;
                       }
                    break;
                    case 1 :
                       if (iorec && iorec->count - iorec->start) {
                          devtype = iorec->ioctl[iorec->start + 
                             (rand()%(iorec->count - iorec->start))].ioctl>>16;
                       }
                    break;
                    case 2 :
                    case 3 :
                    break;
                 }
                 method = rand() & 0x3;

                 access = rand() & devmap->access&(FILE_READ_ACCESS|FILE_WRITE_ACCESS);

                 ioctl_val = CTL_CODE(devtype,
                                      function,
                                      method,
                                      access);

                 set_rand = 0;
                 switch(method) {
                     case METHOD_BUFFERED :

                         inlen  = RandInRange( ioctl_inbuf_min,  ioctl_inbuf_max );
                         outlen = RandInRange( ioctl_outbuf_min, ioctl_outbuf_max );

                         inbuf = bigbuf;
                         if (outlen == 0)
                            outbuf = (PVOID) -1024;
                         else
                            outbuf = bigbuf;

                         set_rand = 1;
                         break;

                     case METHOD_IN_DIRECT :
                     case METHOD_OUT_DIRECT :

                         inlen  = RandInRange( ioctl_inbuf_min,  ioctl_inbuf_max );
                         outlen = RandInRange( ioctl_outbuf_min, ioctl_outbuf_max );

                         inbuf = bigbuf;
                         outbuf = &bigbuf[BIGBUF_SIZE - outlen];
//                         printf ("%p %d %p %d\n", inbuf, inlen, outbuf, outlen);

                         set_rand = 1;
                         break;

                     case METHOD_NEITHER :

                         switch (rand ()&3) {
                            //
                            // Completely random pointers. We may corrupt ourselves here
//                            case 0 :
//                               inlen = rand32();
//                               outlen = rand32();
//
//                               inbuf = (PVOID)rand32();
//                               outbuf = (PVOID)rand32();
//                               break;

                            //
                            // Kernel crashing pointers with smallish lengths
                            case 1 :
                               inbuf = (PVOID) -1024;
                               outbuf = (PVOID) -1024;
                               inlen  = RandInRange( ioctl_inbuf_min,  ioctl_inbuf_max );
                               outlen = RandInRange( ioctl_outbuf_min, ioctl_outbuf_max );
                               break;
                            //
                            // Good pointers
                            case 0 :
                            case 2 :
                               inlen  = RandInRange( ioctl_inbuf_min,  ioctl_inbuf_max );
                               outlen = RandInRange( ioctl_outbuf_min, ioctl_outbuf_max );

                               inbuf = &bigbuf[BIGBUF_SIZE - outlen];
                               outbuf = &bigbuf[BIGBUF_SIZE - outlen];
                               set_rand = 1;
                               break;
                            //
                            // Bad user mode pointers
                            case 3 :
                               inlen = rand() & 0xFFFF;
                               outlen = rand() & 0xFFFF;
                               inbuf =   UintToPtr( rand() & 0xFFFF);
                               outbuf =  UintToPtr( rand() & 0xFFFF);
                               break;
                            break;
                     }
                 }

                 if (set_rand) {
                    randomize_buf (inbuf, inlen);
                 }
                 progress_counter++;
                 if (flags&FLAGS_DO_IOCTL_RANDOM) {
                    status = NtDeviceIoControlFile(devmap->handle,
                                                   NULL,
                                                   NULL,
                                                   NULL,
                                                   &static_iosb,
                                                   ioctl_val,
                                                   inbuf,
                                                   inlen,
                                                   outbuf,
                                                   outlen);
                    record_ioctl (&iorec, ioctl_val, status);
                 }

                 if (flags&FLAGS_DO_FSCTL_RANDOM) {
                    status = NtFsControlFile(devmap->handle,
                                             NULL,
                                             NULL,
                                             NULL,
                                             &static_iosb,
                                             ioctl_val,
                                             inbuf,
                                             inlen,
                                             outbuf,
                                             outlen);
                    record_ioctl (&fsrec, ioctl_val, status);
                }
             }

             NtCancelIoFile (devmap->handle, &static_iosb);
          } while (print_diags ((method == METHOD_BUFFERED)?0:DIAG_NOEXCEPTIONS, ret++) && ret < MAX_RET);
       }

//       if (!(flags&FLAGS_DO_RANDOM_DEVICE)) {
//          status = NtSuspendThread (randthread, &tmp);
//          if (!NT_SUCCESS (status)) {
//             printf ("NtSuspendThread failed %x\n", status);
//          }
//       }
    }
    if (flags&FLAGS_DO_IOCTL_RANDOM) {
       do_tailored_ioctl (devmap, path, iorec, 0);
    }
    if (flags&FLAGS_DO_FSCTL_RANDOM) {
       do_tailored_ioctl (devmap, path, fsrec, 1);
    }

    if (iorec) {
       free (iorec);
    }
    if (fsrec) {
       free (fsrec);
    }
    return 0;
}

PFILE_FULL_EA_INFORMATION build_adr_ea (PCHAR name, USHORT adrtype,
                                        USHORT adrl, PVOID padr,
                                        PULONG peal)
{
   PFILE_FULL_EA_INFORMATION ea = NULL;
   TRANSPORT_ADDRESS UNALIGNED *pta;
   TA_ADDRESS *ptaa;
   ULONG eal;
   ULONG nl;

   nl = strlen (name);
   eal = sizeof (FILE_FULL_EA_INFORMATION) + nl +
         sizeof (TRANSPORT_ADDRESS) - 1 + adrl;
   ea = malloc (eal);
   if (!ea) {
      printf ("Failed to allocate %d bytes\n", eal);
      exit (EXIT_FAILURE);
   }
   memset (ea, 0, eal);
   ea->EaNameLength = (UCHAR) nl;
   strcpy (ea->EaName, name);
   ea->EaValueLength = (USHORT)(eal - sizeof (*ea) - nl);
   pta = (TRANSPORT_ADDRESS UNALIGNED *)(ea->EaName + nl + 1);
   pta->TAAddressCount = 1;
   ptaa = (TA_ADDRESS *) pta->Address;
   ptaa->AddressLength = adrl;
   ptaa->AddressType = adrtype;
   memcpy (ptaa->Address, padr, adrl);
   *peal = eal;
   return ea;
}

PFILE_FULL_EA_INFORMATION build_con_ea (PCHAR name, CONNECTION_CONTEXT ctx,
                                        PULONG peal)
{
   PFILE_FULL_EA_INFORMATION ea = NULL;
   CONNECTION_CONTEXT UNALIGNED *pctx;
   ULONG eal;
   ULONG nl;

   nl = strlen (name);
   eal = sizeof (FILE_FULL_EA_INFORMATION) + nl +
         sizeof (ctx);
   ea = malloc (eal);
   if (!ea) {
      printf ("Failed to allocate %d bytes\n", eal);
      exit (EXIT_FAILURE);
   }
   memset (ea, 0, eal);
   ea->EaNameLength = (UCHAR) nl;
   strcpy (ea->EaName, name);
   ea->EaValueLength = (USHORT)(eal - sizeof (*ea) - nl);
   pctx = (CONNECTION_CONTEXT UNALIGNED*) (ea->EaName + nl + 1);
   *pctx = ctx;
   *peal = eal;
   return ea;
}

#define DRIVER_PREFIX1 L"\\Driver\\"
#define DRIVER_PREFIX2 L"\\FileSystem\\"

NTSTATUS
check_driver (HANDLE handle, 
              PUNICODE_STRING DriverName)
/*
   Check to see if the specified device object is associated with the driver object
*/
{
    PFILE_FS_DRIVER_PATH_INFORMATION fsDpInfo;
    ULONG   fsDpSize;
    ULONG   DriverBufferLength;
    UNICODE_STRING us;
    NTSTATUS status;
    IO_STATUS_BLOCK iosb;

    DriverBufferLength = sizeof (DRIVER_PREFIX1);
    if (sizeof (DRIVER_PREFIX2) >  DriverBufferLength) {
        DriverBufferLength = sizeof (DRIVER_PREFIX2);
    }
    DriverBufferLength += DriverName->Length;

    fsDpSize = sizeof(FILE_FS_DRIVER_PATH_INFORMATION) + DriverBufferLength;

    fsDpInfo = malloc (fsDpSize);
    if (fsDpInfo == 0) {
        printf ("Failed to allocate buffer for driver query\n");
        exit (EXIT_FAILURE);
    }

    ZeroMemory(fsDpInfo, fsDpSize);

    us.MaximumLength = (USHORT)DriverBufferLength;
    us.Length = 0;
    us.Buffer = fsDpInfo->DriverName;
    RtlAppendUnicodeToString (&us, DRIVER_PREFIX1);
    RtlAppendUnicodeStringToString (&us, DriverName);
    fsDpInfo->DriverNameLength = us.Length;


    status = NtQueryVolumeInformationFile(handle,
                                          &iosb,
                                          fsDpInfo,
                                          fsDpSize,
                                          FileFsDriverPathInformation);

    if (NT_SUCCESS (status)) {
        if (fsDpInfo->DriverInPath) {
            status = STATUS_SUCCESS;
        } else {
            status = -1;
        }
        free (fsDpInfo);
        return status;
    }

    us.MaximumLength = (USHORT)DriverBufferLength;
    us.Length = 0;
    us.Buffer = fsDpInfo->DriverName;
    RtlAppendUnicodeToString (&us, DRIVER_PREFIX2);
    RtlAppendUnicodeStringToString (&us, DriverName);
    fsDpInfo->DriverNameLength = us.Length;
    status = NtQueryVolumeInformationFile(handle,
                                          &iosb,
                                          fsDpInfo,
                                          fsDpSize,
                                          FileFsDriverPathInformation);

    if (!NT_SUCCESS (status)) {
        printf ("NtQueryVolumeInformationFile for FileFsDriverPathInformation failed %x\n", status);
        exit (EXIT_FAILURE);
    }
    if (fsDpInfo->DriverInPath) {
        status = STATUS_SUCCESS;
    } else {
        status = -1;
    }
    free (fsDpInfo);
    return status;
}

DWORD
WINAPI
do_open_close_thread (
    LPVOID arg)
{
    ULONG i;
    POBJECT_ATTRIBUTES oa = arg;
    NTSTATUS status;
    IO_STATUS_BLOCK iosb;
    HANDLE handle;

    for (i = 0; i < 10000; i++) {
        status = NtCreateFile (&handle,
                               MAXIMUM_ALLOWED,
                               oa,
                               &iosb,
                               NULL,
                               0,
                               FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
                               FILE_OPEN,
                               0,
                               NULL,
                               0);
        if (NT_SUCCESS (status)) {
            status = NtClose (handle);
            if (!NT_SUCCESS (status)) {
                printf ("NtClose failed %x\n", status);
                exit (EXIT_FAILURE);
            }
        }
    }
    return 0;
}

VOID
do_open_close (POBJECT_ATTRIBUTES oa,
               PUNICODE_STRING    name,
               PWCHAR             path)
/*
    Open and close the device rapidly to catch open close races
*/
{
#define MAX_OPEN_THREAD 4
    HANDLE Thread[MAX_OPEN_THREAD];
    ULONG i;
    DWORD id;

    if (crashes (path, L"OPENCLOSE", L"", L"", L"")) {
        return;
    }

    for (i = 0; i < MAX_OPEN_THREAD; i++) {
        Thread[i] = CreateThread (NULL, 0, do_open_close_thread, oa, 0, &id);
        if (Thread[i] == NULL) {
            printf ("CreateThread failed %d\n", GetLastError ());
            exit (EXIT_FAILURE);
        }
    }
    for (i = 0; i < MAX_OPEN_THREAD; i++) {
        WaitForSingleObject (Thread[i], INFINITE);
        CloseHandle (Thread[i]);
    }
}


/*
   Open device with various options
*/
NTSTATUS
open_device(
    HANDLE                  parent,
    PUNICODE_STRING         name,
    PDEVMAP                 devmap,
    BOOL                    synch,        // Do a synchronous open
    BOOL                    addbackslash, // Add trailing backslash to name
    BOOL                    direct,       // Do a direct open
    ULONG                   opentype,      // Connection, address etc.
    PWCHAR                  path
)
{
    NTSTATUS                  status;
    IO_STATUS_BLOCK           iosb;
    ULONG                     i, l, lw, first;
    WCHAR                     dn[1024];
    UNICODE_STRING            dnu;
    ULONG                     options;
    OBJECT_ATTRIBUTES         oa;
    ACCESS_MASK               am;
    ULONG                     share;
    PFILE_FULL_EA_INFORMATION pea = NULL;
    ULONG                     eal = 0;
    TDI_ADDRESS_IP            ipaddr;
    TDI_ADDRESS_NETBIOS       netbiosaddr;
    TDI_ADDRESS_IPX           ipxaddr;
    TDI_ADDRESS_APPLETALK     appleaddr;
    LARGE_INTEGER tmo;
    PCHAR api;

    if (direct && !(FLAGS_DO_DIRECT_DEVICE&flags)) {
       return STATUS_ACCESS_DENIED;
    }
    l = name->Length;
    if (l >= sizeof (dn))
       l = sizeof (dn) - 1;
    lw = l / sizeof (dn[0]);
    wcsncpy (dn, name->Buffer, lw);
    dn[lw] = '\0';
    RtlInitUnicodeString (&dnu, dn);
    if (addbackslash && dnu.Length < sizeof (dn)) {
       dn[min (dnu.Length, sizeof (dn) - sizeof (dn[0]))/sizeof(dn[0])] = '\\';
       dnu.Length += 2;
    }
    dn[min(dnu.Length, sizeof (dn) - sizeof (dn[0]))/sizeof(dn[0])] = 0;
    
    InitializeObjectAttributes(&oa,
                               &dnu,
                               OBJ_CASE_INSENSITIVE,
                               parent,
                               NULL);

    if ((flags2&FLAGS_DO_DRIVER) != 0) {
        HANDLE handle;
        status = NtCreateFile (&handle,
                               MAXIMUM_ALLOWED,
                               &oa,
                               &iosb,
                               NULL,
                               0,
                               FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
                               FILE_OPEN,
                               0,
                               NULL,
                               0);
        if (NT_SUCCESS (status)) {
            status = check_driver (handle, &DriverName);
            NtClose (handle);
            if (!NT_SUCCESS (status)) {
                return status;
            }
        } else {
            wprintf(L"Failed to open device %s for driver checking %x\n",
                    dn, status);
            return -1;
        }
    }

    if ((flags&FLAGS_DO_OPEN_CLOSE) && !synch && !direct && opentype == 0) {
       do_open_close (&oa,
                      name,
                      path);
    }

    devmap->handle = NULL;

    if (direct) {
        options = 0;
        am = SYNCHRONIZE|FILE_READ_ATTRIBUTES|READ_CONTROL|WRITE_OWNER|WRITE_DAC;
    } else if (synch) {
        options = FILE_SYNCHRONOUS_IO_ALERT;
        am = MAXIMUM_ALLOWED|SYNCHRONIZE;
    } else {
        options = 0;
        am = MAXIMUM_ALLOWED;
    }
    if (opentype == OPEN_TYPE_NAMED_PIPE) {
       share = FILE_SHARE_READ|FILE_SHARE_WRITE;
    } else if (opentype == OPEN_TYPE_TREE_CONNECT) {
       share = FILE_SHARE_READ|FILE_SHARE_WRITE;
       options |= FILE_CREATE_TREE_CONNECTION;
    } else {
       share = 0;
    }
    if (opentype == OPEN_TYPE_TDI_CONNECTION) {
       pea = build_con_ea (TdiConnectionContext, (CONNECTION_CONTEXT) 12345, &eal);
    } else if (opentype == OPEN_TYPE_TDI_ADDR_IP) {
      memset (&ipaddr, 0, sizeof (ipaddr));
      pea = build_adr_ea (TdiTransportAddress,
                          TDI_ADDRESS_TYPE_IP,
                          sizeof (ipaddr), &ipaddr,
                          &eal);
    } else if (opentype == OPEN_TYPE_TDI_ADDR_NETBIOS) {
      memset (&netbiosaddr, 0, sizeof (netbiosaddr));
      pea = build_adr_ea (TdiTransportAddress,
                          TDI_ADDRESS_TYPE_NETBIOS,
                          sizeof (netbiosaddr), &netbiosaddr,
                          &eal);
    } else if (opentype == OPEN_TYPE_TDI_ADDR_IPX) {
      memset (&ipxaddr, 0, sizeof (ipxaddr));
      pea = build_adr_ea (TdiTransportAddress,
                          TDI_ADDRESS_TYPE_IPX,
                          sizeof (ipxaddr), &ipxaddr,
                          &eal);
    } else if (opentype == OPEN_TYPE_TDI_ADDR_APPLE) {
      memset (&appleaddr, 0, sizeof (appleaddr));
      pea = build_adr_ea (TdiTransportAddress,
                          TDI_ADDRESS_TYPE_APPLETALK,
                          sizeof (appleaddr), &appleaddr,
                          &eal);
    }

    wprintf(L"Trying to open device %s %s %s %s\n",
            dn, direct?L"direct ":L"", synch?L"synchronous":L"",
            pea?L"TDI open":(opentype == OPEN_TYPE_NAMED_PIPE)?
                            L"NamedPipe":(opentype == OPEN_TYPE_MAIL_SLOT)?
                            L"Mailslot":L"");

    if (crashes (path, L"Open", direct?L"direct":L"", synch?L"synchronous":L"",
                 addbackslash?L"Add backslash":L""))
       status = STATUS_ACCESS_DENIED;
    else {
       do {
          if (opentype == OPEN_TYPE_NAMED_PIPE) {
             tmo.QuadPart = -1000;
             api = "NtCreateNamedPipeFile";
             status = NtCreateNamedPipeFile (&devmap->handle,
                                             am,
                                             &oa,
                                             &iosb,
                                             share,
                                             FILE_CREATE,
                                             options,
                                             FILE_PIPE_MESSAGE_TYPE,
                                             FILE_PIPE_MESSAGE_MODE,
                                             FILE_PIPE_COMPLETE_OPERATION,
                                             10,
                                             1000, 1000,
                                             &tmo);
          } else if (opentype == OPEN_TYPE_MAIL_SLOT) {
             tmo.QuadPart = -1000;
             api = "NtCreateMailslotFile";
             status = NtCreateMailslotFile (&devmap->handle,
                                            am,
                                            &oa,
                                            &iosb,
                                            options,
                                            1024,
                                            256,
                                            &tmo);

          } else {
             api = "NtCreateFile";
             status = NtCreateFile(&devmap->handle,
                                   am,
                                   &oa,
                                   &iosb,
                                   NULL,
                                   0,
                                   share,
                                   FILE_OPEN,
                                   options,
                                   pea,
                                   eal);
          }

          if (status == STATUS_SHARING_VIOLATION) {
             printf ("Hit file share violation for %X\n", share);
             if (share&FILE_SHARE_READ) {
                share &= ~FILE_SHARE_READ;
                if (share&FILE_SHARE_WRITE) {
                   share &= ~FILE_SHARE_WRITE;
                   if (share&FILE_SHARE_DELETE) {
                      break;
                   } else {
                      share |= FILE_SHARE_DELETE;
                   }
                } else {
                   share |= FILE_SHARE_WRITE;
                }
             } else {
                share |= FILE_SHARE_READ;
             }
          } else if (status == STATUS_ACCESS_DENIED) {
             printf ("Hit file protection violation for %X\n", am);
             if (am&MAXIMUM_ALLOWED) {
                am &= ~MAXIMUM_ALLOWED;
                am |= FILE_READ_DATA|FILE_WRITE_DATA|SYNCHRONIZE|FILE_READ_ATTRIBUTES|
                      READ_CONTROL|FILE_APPEND_DATA;
             } else if (am&FILE_WRITE_DATA) {
                am &= ~FILE_WRITE_DATA;
             } else if (am&FILE_APPEND_DATA) {
                am &= ~FILE_APPEND_DATA;
             } else if (am&FILE_READ_DATA) {
                am &= ~FILE_READ_DATA;
             } else if (am&WRITE_OWNER) {
                am &= ~WRITE_OWNER;
             } else if (am&WRITE_DAC) {
                am &= ~WRITE_DAC;
             } else if (am&WRITE_DAC) {
                am &= ~WRITE_DAC;
             } else if (am&READ_CONTROL) {
                am &= ~READ_CONTROL;
             } else if (am&FILE_READ_ATTRIBUTES) {
                am &= ~FILE_READ_ATTRIBUTES;
             } else {
                break;
             }
          }
       } while (status == STATUS_SHARING_VIOLATION || status == STATUS_ACCESS_DENIED);
    }
    if (pea)
       free (pea);
    if (NT_SUCCESS(status)) {
        status = get_handle_access (devmap->handle, &devmap->access);
    }
    if (NT_SUCCESS(status)) {
        wprintf(L"Opened file %s with access %x\n",
                dn,
                devmap->access);

        if (DELETE&devmap->access) {
           printf ("Got delete access to device\n");
        }
        if (WRITE_DAC&devmap->access) {
           printf ("Got write_dac access to device\n");
        }
        if (WRITE_OWNER&devmap->access) {
           printf ("Got write_owner access to device\n");
        }
        query_object(devmap->handle, devmap, path);
        misc_functions (devmap->handle, path, synch);
    } else {
        if (status != STATUS_INVALID_DEVICE_REQUEST &&
            status != STATUS_ACCESS_DENIED) {
            printf("%s failed %x\n", api,
                   status);
        }
    }
    if ((direct && synch) || (addbackslash & !synch && !direct)) // Only do this twice
       try_fast_query_delete_etc (&oa, path, L"Top open");


    return status;
}

/*
   Compare routine for sorting the object directory
*/
int __cdecl compare_unicode (const void *arg1, const void *arg2)
{
   POBJECT_DIRECTORY_INFORMATION s1, s2;
   ULONG i, j;

   s1 = (POBJECT_DIRECTORY_INFORMATION) arg1;
   s2 = (POBJECT_DIRECTORY_INFORMATION) arg2;
   for (i = 0, j = 0;
        (i < s1->Name.Length / sizeof (WCHAR)) &&
        (j < s2->Name.Length / sizeof (WCHAR)); i++, j++) {
      if (s1->Name.Buffer[i] < s2->Name.Buffer[j])
         return -1;
      else if (s1->Name.Buffer[i] > s2->Name.Buffer[j])
         return +1;
   }
   if (i > j)
      return -1;
   else if (i < j)
      return +1;
   else
      return 0;
}


/*
   Do all the various different opens looking for handles
*/
NTSTATUS do_device_opens (HANDLE          handle,
                          PUNICODE_STRING name,
                          PDEVMAP         devmap,
                          PULONG          devscount,
                          PWCHAR          path,
                          PWCHAR          devbuf)
{
   NTSTATUS status;
   ULONG do_tdi_opens=0;
   ULONG OrigDevCount;


   OrigDevCount = *devscount;

   if (flags&FLAGS_DO_SYNC) {
      status = open_device(handle,
                           name,
                           &devmap[*devscount],
                           TRUE,   /* Synchronous           */
                           FALSE,  /* No added backslash    */
                           FALSE,  /* No direct device open */
                           0,
                           path);
 
      if (NT_SUCCESS(status)) {
         if (NT_SUCCESS (check_tdi_handle (devmap[*devscount].handle))) {
            do_tdi_opens = 1;
         }
         try_funny_opens(devmap[*devscount].handle, path);

         do_ioctl(&devmap[*devscount], path);

         *devscount = *devscount + 1;

      } else {
          if (status == -1) {
              return status;
          } else {
              wprintf(L"Failed to open device %s status is %x\n",
                      devbuf,
                      status);
          }
      }
   }


   status = open_device(handle,
                        name,
                        &devmap[*devscount],
                        FALSE,  /* Synchronous             */
                        FALSE,  /* No added backslash      */
                        FALSE,  /* No direct device access */
                        0,
                        path);

   if (NT_SUCCESS(status)) {
      if (!do_tdi_opens && NT_SUCCESS (check_tdi_handle (devmap[*devscount].handle))) {
         do_tdi_opens = 1;
      }
      try_funny_opens(devmap[*devscount].handle, path);
      do_ioctl(&devmap[*devscount], path);

      *devscount = *devscount + 1;
   } else {
      if (status == -1) {
          return status;
      } else {
          wprintf(L"Failed to open device %s status is %x\n",
                  devbuf,
                  status);
      }
   }

   status = open_device(handle,
                        name,
                        &devmap[*devscount],
                        FALSE,  /* Synchronous             */
                        TRUE,   /* No added backslash      */
                        FALSE,  /* No direct device access */
                        0,
                        path);

   if (NT_SUCCESS(status)) {
      try_funny_opens(devmap[*devscount].handle, path);
      do_ioctl(&devmap[*devscount], path);

      *devscount = *devscount + 1;
   } else {
      if (status == -1) {
          return status;
      } else {
          wprintf(L"Failed to open device %s status is %x\n",
                  devbuf,
                  status);
      }
   }

   if (flags&FLAGS_DO_SYNC) {
      status = open_device(handle,
                           name,
                           &devmap[*devscount],
                           TRUE,  /* Synchronous          */
                           FALSE, /* No added backslash   */
                           TRUE,  /* Direct device access */
                           0,
                           path);

      if (NT_SUCCESS(status)) {
         try_funny_opens(devmap[*devscount].handle, path);
         do_ioctl(&devmap[*devscount], path);

         *devscount = *devscount + 1;
      } else {
         if (status == -1) {
             return status;
         } else {
             wprintf(L"Failed to open device %s status is %x\n",
                     devbuf,
                     status);
         }
      }
   }

   if (flags&FLAGS_DO_SYNC) {
      status = open_device(handle,
                           name,
                           &devmap[*devscount],
                           TRUE,  /* Synchronous          */
                           FALSE, /* No added backslash   */
                           FALSE, /* Direct device access */
                           OPEN_TYPE_NAMED_PIPE,
                           path);

      if (NT_SUCCESS(status)) {
         try_funny_opens(devmap[*devscount].handle, path);
         do_ioctl(&devmap[*devscount], path);

         *devscount = *devscount + 1;
      } else {
         if (status == -1) {
             return status;
         } else {
             wprintf(L"Failed to open device %s status is %x\n",
                     devbuf,
                     status);
         }
      }
      status = open_device(handle,
                           name,
                           &devmap[*devscount],
                           TRUE,  /* Synchronous          */
                           FALSE, /* No added backslash   */
                           FALSE, /* Direct device access */
                           OPEN_TYPE_MAIL_SLOT,
                           path);

      if (NT_SUCCESS(status)) {
         try_funny_opens(devmap[*devscount].handle, path);
         do_ioctl(&devmap[*devscount], path);

         *devscount = *devscount + 1;
      } else {
         if (status == -1) {
             return status;
         } else {
             wprintf(L"Failed to open device %s status is %x\n",
                     devbuf,
                     status);
         }
      }
   }
   status = open_device(handle,
                        name,
                        &devmap[*devscount],
                        FALSE, /* Synchronous          */
                        FALSE, /* No added backslash   */
                        FALSE, /* Direct device access */
                        OPEN_TYPE_NAMED_PIPE,
                        path);

   if (NT_SUCCESS(status)) {
      try_funny_opens(devmap[*devscount].handle, path);
      do_ioctl(&devmap[*devscount], path);

      *devscount = *devscount + 1;
   } else {
         if (status == -1) {
             return status;
         } else {
             wprintf(L"Failed to open device %s status is %x\n",
                     devbuf,
                     status);
         }
   }
   status = open_device(handle,
                        name,
                        &devmap[*devscount],
                        FALSE, /* Synchronous          */
                        FALSE, /* No added backslash   */
                        FALSE, /* Direct device access */
                        OPEN_TYPE_MAIL_SLOT,
                        path);

   if (NT_SUCCESS(status)) {
      try_funny_opens(devmap[*devscount].handle, path);
      do_ioctl(&devmap[*devscount], path);

      *devscount = *devscount + 1;
   } else {
         if (status == -1) {
             return status;
         } else {
             wprintf(L"Failed to open device %s status is %x\n",
                     devbuf,
                     status);
         }
   }

   status = open_device(handle,
                        name,
                        &devmap[*devscount],
                        FALSE, /* Synchronous          */
                        FALSE, /* No added backslash   */
                        FALSE, /* Direct device access */
                        OPEN_TYPE_TREE_CONNECT,
                        path);

   if (NT_SUCCESS(status)) {
      try_funny_opens(devmap[*devscount].handle, path);
      do_ioctl(&devmap[*devscount], path);

      *devscount = *devscount + 1;
   } else {
         if (status == -1) {
             return status;
         } else {
             wprintf(L"Failed to open device %s status is %x\n",
                     devbuf,
                     status);
         }
   }

   if (do_tdi_opens) {
      status = open_device(handle,
                           name,
                           &devmap[*devscount],
                           FALSE,  /* Synchronous          */
                           FALSE,  /* No added backslash   */
                           FALSE,  /* Direct device access */
                           OPEN_TYPE_TDI_CONNECTION,
                           path);

      if (NT_SUCCESS(status)) {
         try_funny_opens(devmap[*devscount].handle, path);
         do_ioctl(&devmap[*devscount], path);

         *devscount = *devscount + 1;
      } else {
         if (status == -1) {
             return status;
         } else {
             wprintf(L"Failed to open device %s status is %x\n",
                     devbuf,
                     status);
         }
      }

      status = open_device(handle,
                           name,
                           &devmap[*devscount],
                           FALSE,  /* Synchronous          */
                           FALSE,  /* No added backslash   */
                           FALSE,  /* Direct device access */
                           OPEN_TYPE_TDI_ADDR_IP,
                           path);

      if (NT_SUCCESS(status)) {
         try_funny_opens(devmap[*devscount].handle, path);
         do_ioctl(&devmap[*devscount], path);

         *devscount = *devscount + 1;
      } else {
         if (status == -1) {
             return status;
         } else {
             wprintf(L"Failed to open device %s status is %x\n",
                     devbuf,
                     status);
         }
      }
      status = open_device(handle,
                           name,
                           &devmap[*devscount],
                           FALSE,  /* Synchronous          */
                           FALSE,  /* No added backslash   */
                           FALSE,  /* Direct device access */
                           OPEN_TYPE_TDI_ADDR_IPX,
                           path);

      if (NT_SUCCESS(status)) {
         try_funny_opens(devmap[*devscount].handle, path);
         do_ioctl(&devmap[*devscount], path);

         *devscount = *devscount + 1;
      } else {
         if (status == -1) {
             return status;
         } else {
             wprintf(L"Failed to open device %s status is %x\n",
                     devbuf,
                     status);
         }
      }
      status = open_device(handle,
                           name,
                           &devmap[*devscount],
                           FALSE,  /* Synchronous          */
                           FALSE,  /* No added backslash   */
                           FALSE,  /* Direct device access */
                           OPEN_TYPE_TDI_ADDR_NETBIOS,
                           path);

      if (NT_SUCCESS(status)) {
         try_funny_opens(devmap[*devscount].handle, path);
         do_ioctl(&devmap[*devscount], path);

         *devscount = *devscount + 1;
      } else {
         if (status == -1) {
             return status;
         } else {
             wprintf(L"Failed to open device %s status is %x\n",
                     devbuf,
                     status);
         }
      }
      status = open_device(handle,
                           name,
                           &devmap[*devscount],
                           FALSE,  /* Synchronous          */
                           FALSE,  /* No added backslash   */
                           FALSE,  /* Direct device access */
                           OPEN_TYPE_TDI_ADDR_APPLE,
                           path);

      if (NT_SUCCESS(status)) {
         try_funny_opens(devmap[*devscount].handle, path);
         do_ioctl(&devmap[*devscount], path);

         *devscount = *devscount + 1;
      } else {
         if (status == -1) {
             return status;
         } else {
             wprintf(L"Failed to open device %s status is %x\n",
                     devbuf,
                     status);
         }
      }
   }

   if (OrigDevCount == *devscount) {
       crashes (path, L"FAILED all open attempts", L"", L"", L"");
   }

   crashes (path, L"DONE", L"", L"", L"");

   return status;
}

NTSTATUS
do_lpc_connect (HANDLE          handle,
                PUNICODE_STRING name,
                PDEVMAP         devmap,
                PULONG          devscount,
                PWCHAR          path,
                PWCHAR          devbuf)
{
   HANDLE nhandle;
   NTSTATUS status;
   SECURITY_QUALITY_OF_SERVICE qos;
   ULONG maxmsg, i, opened;
   UNICODE_STRING fullname;

   RtlInitUnicodeString (&fullname, path);
   qos.ImpersonationLevel = SecurityImpersonation;
   qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
   qos.EffectiveOnly = TRUE;
   opened = 0;
   for (i = 0; i < 1000; i++) {
      status = NtConnectPort (&nhandle, &fullname, &qos, NULL, NULL, &maxmsg, NULL, NULL);
      if (!NT_SUCCESS (status)) {
         wprintf (L"NtConnectPort failed to %s %x\n", path, status);
         return status;
      }
      opened++;
      status = NtClose (nhandle);
      if (!NT_SUCCESS (status)) {
         wprintf (L"NtClose of port handle failed %x\n", status);
      }
   }
   if (opened) {
      wprintf (L"Opened port %ws %d times\n", path, opened);
   }
   return status;
}

/*
   Traverse the object tree looking for devices
*/
NTSTATUS
recurse(
    HANDLE                  handle,
    PUNICODE_STRING         name,
    PDEVMAP                 devmap,
    PULONG                  devscount,
    PWCHAR                  path,
    ULONG                   depth
)
{
    HANDLE                  nhandle, shandle;
    NTSTATUS                status;
    OBJECT_ATTRIBUTES       oa;
    OBJECT_DIRECTORY_INFORMATION *od;
    PVOID                   buffer;
    ULONG                   bufferlen;
    ULONG                   context;
    ULONG                   retlen;
    ULONG                   ul;
    UNICODE_STRING          equiv;
    WCHAR                   buf[100], devbuf[200], objtype[80];
    PWCHAR                  npath = NULL;
    static WCHAR            ans[100]={0};
    ULONG                   c;

    if (depth > 10) {
        return STATUS_SUCCESS;
    }
    InitializeObjectAttributes(&oa,
                               name,
                               OBJ_CASE_INSENSITIVE,
                               handle,
                               NULL);

    wcsncpy(devbuf,
            name->Buffer,
            sizeof (devbuf) / sizeof (devbuf[0]));
    
    devbuf[sizeof (devbuf) - 1] = L'\0';

    status = NtOpenDirectoryObject(&nhandle,
                                   MAXIMUM_ALLOWED/*DIRECTORY_QUERY | DIRECTORY_TRAVERSE*/,
                                   &oa);

    if (NT_SUCCESS(status)) {
        context = 0;

        bufferlen = 0x10000;

        buffer = malloc(bufferlen);

        status = NtQueryDirectoryObject(nhandle,
                                        buffer,
                                        bufferlen,
                                        FALSE,
                                        TRUE,
                                        &context,
                                        &retlen);

        if (NT_SUCCESS(status)) {

            for (od = (POBJECT_DIRECTORY_INFORMATION) buffer, c = 0;
                 od->Name.Length != 0 || od->TypeName.Length != 0; od++, c++)
               ;

            qsort (buffer, c, sizeof (*od), compare_unicode);

            for (od = (POBJECT_DIRECTORY_INFORMATION) buffer;
                 *devscount < MAX_DEVICES; od++) {

                if (od->Name.Length == 0 &&
                    od->TypeName.Length == 0) {

                    break;
                }

                wcsncpy(devbuf,
                        od->Name.Buffer,
                        sizeof (devbuf) / sizeof (devbuf[0]));
                devbuf[sizeof (devbuf) / sizeof (devbuf[0]) - 1] = L'\0';

                wcsncpy(objtype,
                        od->TypeName.Buffer,
                        sizeof (objtype) / sizeof (objtype[0]));
                objtype[sizeof (objtype) / sizeof (objtype[0]) - 1] = L'\0';

                npath = realloc (npath,
                                 (wcslen (path) + 1 + wcslen (devbuf) + 1) *
                                    sizeof (WCHAR));
                if (!npath) {
                   printf ("Memory allocation failed for path buffer!");
                   exit (EXIT_FAILURE);
                }
                wcscpy (npath, path);
                if (path[wcslen (path)-1] != '\\' &&
                    devbuf[wcslen (devbuf)-1] != '\\')
                   wcscat (npath, L"\\");
                wcscat (npath, devbuf);
                if (wcsncmp(objtype,
                            L"Directory",
                            od->TypeName.Length / sizeof (WCHAR)) == 0) {

                    recurse(nhandle,
                            &od->Name,
                            devmap,
                            devscount,
                            npath,
                            depth+1);

                } else if (!(flags&FLAGS_DO_LPC) && (
                           wcsncmp(objtype,
                                   L"Device",
                                   od->TypeName.Length / sizeof (WCHAR)) == 0 ||
                           (wcsncmp(objtype,
                                    L"SymbolicLink",
                                    od->TypeName.Length / sizeof (WCHAR)) == 0 && (flags&FLAGS_DO_SYMBOLIC)))) {
                    if (!(flags&FLAGS_DO_ALLDEVS)) {
                       if (skipped == 0) {
                          ans[0] = 0;
                          skipped = 1;
                       }
                       if (ans[0] == '/') {
//                          wprintf (L"Matching \"%s\" against \"%s\"\n", &ans[1], devbuf);
                          if (_wcsnicmp (&ans[1], devbuf, wcslen (&ans[1])) != 0)
                             continue;
                       } else if (ans[0] == '?' || (flags&FLAGS_DO_PRINT_DEVS)) {
                          if (prefix_string) {
                             printf ("%s %ws\\%ws\n", prefix_string, path, devbuf);
                          } else {
                             printf ("%ws %ws\\%ws\n", objtype, path, devbuf);
                          }
                          continue;
                       }

                       progress_counter = 0;
                       if (flags & FLAGS_DO_RANDOM_DEVICE) {
                          if (random_device > 0) {
//                             wprintf (L"%s %s\\%s\n", objtype, path, devbuf);                         
                             random_device--;
                             continue;
                          }
                       } else if (ans[0] != 'a' && ans[0] != 'A') {
                          wprintf (L"Open device %s %s\\%s? ", objtype, path, devbuf);
                          wscanf (L"%100s", ans);
                          if (ans[0] == 'q' || ans[0] == 'Q') {
                             exit (EXIT_SUCCESS);
                          } else if (ans[0] != 'y' && ans[0] != 'Y' &&
                              ans[0] != 'a' && ans[0] != 'A') {
                             continue;
                          }
                       }
                    }
                    if (!(flags&FLAGS_DO_DISKS)) {
                       if (wcsstr (path, L"Harddisk") ||
                           wcsstr (path, L"Ide") ||
                           wcsstr (path, L"Scsi")) {
                          printf ("Skipping this device as it looks like a disk\n");
                          continue;
                       }
                    }

                    if (flags&FLAGS_DO_FAILURE_INJECTION) {
                       turn_on_fault_injection ();
                    }
                    if (flags&FLAGS_DO_IMPERSONATION) {
                        Impersonate_nonadmin ();
                    }

                    status = do_device_opens (nhandle,
                                              &od->Name,
                                              devmap,
                                              devscount,
                                              npath,
                                              devbuf);


                    if (flags&FLAGS_DO_IMPERSONATION) {
                       if (!Revert_from_admin ()) {
                          printf ("Revert_from_admin failed %d\n", GetLastError ());
                          exit (EXIT_FAILURE);
                       }
                    }
                    if (flags&FLAGS_DO_FAILURE_INJECTION) {
                       turn_off_fault_injection ();
                    }
                    print_diags (0, 0);
                    if (flags & FLAGS_DO_RANDOM_DEVICE) {
                       break;
                    }
                } else if ((flags&FLAGS_DO_LPC) &&
                           wcsncmp(od->TypeName.Buffer,
                                   L"Port",
                                   od->TypeName.Length / sizeof (WCHAR)) == 0) {

                     if (!(flags&FLAGS_DO_ALLDEVS)) {
                        if (skipped == 0) {
                           ans[0] = 0;
                           skipped = 1;
                        }
                        if (ans[0] == '/') {
                           wprintf (L"Matching \"%s\" against \"%s\"\n", &ans[1], devbuf);
                           if (_wcsnicmp (&ans[1], devbuf, wcslen (&ans[1])) != 0)
                              continue;
                        } else if (ans[0] == '?') {
                           wprintf (L"%s\\%s\n", path, devbuf);
                           continue;
                        } else if (ans[0] == 'q' || ans[0] == 'Q') {
                           exit (EXIT_SUCCESS);
                        }
                        if (ans[0] != 'a' && ans[0] != 'A') {
                           wprintf (L"Open LPC port %s\\%s? ", path, devbuf);
                           wscanf (L"%100s", ans);
                           if (ans[0] != 'y' && ans[0] != 'Y' &&
                               ans[0] != 'a' && ans[0] != 'A')
                              continue;
                        }
                        if (flags&FLAGS_DO_IMPERSONATION) {
                            Impersonate_nonadmin ();
                        }


                        status = do_lpc_connect (nhandle,
                                                 &od->Name,
                                                 devmap,
                                                 devscount,
                                                 npath,
                                                 devbuf);
                        if (flags&FLAGS_DO_IMPERSONATION) {
                           if (!Revert_from_admin ()) {
                               printf ("Revert_from_admin failed %d\n", GetLastError ());
                               exit (EXIT_FAILURE);
                            }
                        }
                    }
                }
            }
        } else {
            if (status != STATUS_NO_MORE_ENTRIES) {
                wprintf(L"NtQueryDirectoryObject failed %x for directory %s\n",
                        status,
                        devbuf);
            }
        }

        free(buffer);

        NtClose(nhandle);
    } else {
        wprintf(L"NtOpenDirectoryObject failed %x for directory %s\n",
                 status,
                 devbuf);
    }

    free (npath);
    return status;
}

/*
   Look who registers for WMI
*/
void do_wmi ()
{
   UNICODE_STRING name;
   OBJECT_ATTRIBUTES oa;
   NTSTATUS status;
   PKMREGINFO reginfo = NULL;
   HANDLE handle;
   IO_STATUS_BLOCK iosb;
   ULONG reginfol, i;

   RtlInitUnicodeString (&name, L"\\Device\\WMIServiceDevice\\");
   InitializeObjectAttributes (&oa, &name, OBJ_CASE_INSENSITIVE, NULL, NULL);
   status = NtOpenFile (&handle,
                        MAXIMUM_ALLOWED|SYNCHRONIZE,
                        &oa,
                        &iosb,
                        FILE_SHARE_READ|FILE_SHARE_WRITE,
                        FILE_SYNCHRONOUS_IO_NONALERT);
   if (NT_SUCCESS (status)) {
      status = iosb.Status;
   }
   if (!NT_SUCCESS (status)) {
//      printf ("Failed to open WMI device %x\n", status);
      return;
   }

   reginfo = malloc (reginfol = 4096);
   if (!reginfo) {
      printf ("Memory allocation failure for WMI buffer\n");
      exit (EXIT_FAILURE);
   }
   status = NtDeviceIoControlFile (handle,
                                   NULL,
                                   NULL,
                                   NULL,
                                   &iosb,
                                   IOCTL_WMI_GET_ALL_REGISTRANT,
                                   NULL,
                                   0,
                                   reginfo,
                                   reginfol);


   if (NT_SUCCESS (status)) {
      status = iosb.Status;
   }
   if (!NT_SUCCESS (status)) {
      printf ("NtDeviceIoControlFile failed %X\n", status);
   } else {
      for (i = 0; i < iosb.Information / sizeof (*reginfo); i++) {
         printf ("Device %x registered with WMI\n", reginfo[i].ProviderId);
      }
   }

   if (reginfo)
      free (reginfo);
   status = NtClose (handle);
   if (!NT_SUCCESS (status)) {
      printf ("NtClose failed %x\n", status);
   }
}

/*
   Thread to read from a socket so we can test out transmit file
*/
DWORD WINAPI do_listen (LPVOID arg)
{
   SOCKET s;
   struct sockaddr_in sockaddr;
   int l;
   int status;
   DWORD retlen;
   CHAR buf[100];

   l = sizeof (sockaddr);
   s = accept (ls, (struct sockaddr *) &sockaddr, &l);
   if (s == INVALID_SOCKET) {
      printf ("Socket failed %d\n", WSAGetLastError ());
      exit (EXIT_FAILURE);
   }
   while (1) {
      l = recv (s, buf, sizeof (buf), 0);
      if (l == 0) {
         break;
      }
      if (l == SOCKET_ERROR) {
         printf ("recv failed %d\n", WSAGetLastError ());
         exit (EXIT_FAILURE);
      }
   }
   return 0;
}

void do_handle_grab (PDEVMAP devmap,
                     PULONG  devscount)
{
   NTSTATUS status;
   CLIENT_ID clid;
   OBJECT_ATTRIBUTES oa;
   HANDLE handle;

   InitializeObjectAttributes (&oa, NULL, 0, NULL, NULL);
   clid.UniqueThread = 0;
   clid.UniqueProcess =  UlongToHandle( cid );
   status = NtOpenProcess (&handle, PROCESS_DUP_HANDLE, &oa, &clid);
   if (!NT_SUCCESS (status)) {
      printf ("NtOpenProcess for process %d failed %x\n", cid, status);
      return;
   }
   status = NtDuplicateObject (handle, process_handle, NtCurrentProcess (),
                               &devmap->handle, 0, 0, DUPLICATE_SAME_ACCESS);
   if (NT_SUCCESS (status)) {
      *devscount += 1;
      status = get_handle_access (devmap->handle, &devmap->access);
      if (NT_SUCCESS(status)) {
         wprintf(L"Grabbed handle with access %x\n",
                 devmap->access);
         if (DELETE&devmap->access) {
            printf ("Got delete access to device\n");
         }
         if (WRITE_DAC&devmap->access) {
            printf ("Got write_dac access to device\n");
         }
         if (WRITE_OWNER&devmap->access) {
            printf ("Got write_owner access to device\n");
         }
      }
      query_object(devmap->handle, devmap, L"HANDLE");
      misc_functions (devmap->handle, L"HANDLE", 0);
      InitializeObjectAttributes (&oa, NULL, 0, devmap->handle, NULL);
      try_fast_query_delete_etc (&oa, L"HANDLE", L"Top open");
      try_funny_opens(devmap->handle, L"HANDLE");
      do_ioctl(devmap, L"HANDLE");
   } else {
      printf ("NtDuplicateObject failed %x\n", status);
   }

   status = NtClose (handle);
   if (!NT_SUCCESS (status)) {
      printf ("NtClose for process handle failed %x\n", status);
      return;
   }
}

//
// Parse command line
//
void process_parameters(int argc, char *argv[], PUNICODE_STRING name, long *flags, long *flags2)
{
   char c, nc, *p, st;
   int i;
   NTSTATUS status;

   *flags = FLAGS_DO_IOCTL_NULL|FLAGS_DO_IOCTL_RANDOM|
            FLAGS_DO_FSCTL_NULL|FLAGS_DO_FSCTL_RANDOM|
            FLAGS_DO_LOGGING|FLAGS_DO_SKIPCRASH|FLAGS_DO_POOLCHECK|
            FLAGS_DO_MISC|FLAGS_DO_QUERY|FLAGS_DO_SUBOPENS|FLAGS_DO_ZEROEA|
            FLAGS_DO_DISKS|FLAGS_DO_SECURITY|FLAGS_DO_OPEN_CLOSE|
            FLAGS_DO_DIRECT_DEVICE|FLAGS_DO_SYMBOLIC|FLAGS_DO_IMPERSONATION;
   *flags2 = 0;

   name->Length = name->MaximumLength = 0;
   name->Buffer = NULL;
   while (--argc) {
      p = *++argv;
      st = *p;
      if (st == '/' || st == '-' || st == '+') {
         c = *++p;
         nc = *++p;
         nc = (char) toupper (nc);
         switch (toupper (c)) {
            case 'I' :
               if (nc == 'R') {
                  if (st == '-')
                     *flags &= ~FLAGS_DO_IOCTL_RANDOM;
                  else
                     *flags |= FLAGS_DO_IOCTL_RANDOM;
               } else if (nc == 'N') {
                  if (st == '-')
                     *flags &= ~FLAGS_DO_IOCTL_NULL;
                  else
                     *flags |= FLAGS_DO_IOCTL_NULL;
               } else if (nc == 'F') {
                  if (st == '-')
                     *flags &= ~(FLAGS_DO_IOCTL_NULL|FLAGS_DO_FSCTL_NULL|
                                 FLAGS_DO_IOCTL_RANDOM|FLAGS_DO_FSCTL_RANDOM);
                  else
                     *flags |= FLAGS_DO_IOCTL_NULL|FLAGS_DO_FSCTL_NULL|
                               FLAGS_DO_IOCTL_RANDOM|FLAGS_DO_FSCTL_RANDOM;
               } else if (nc == 'M') {
                  if (st == '-')
                     *flags &= ~FLAGS_DO_IMPERSONATION;
                  else
                     *flags |= FLAGS_DO_IMPERSONATION;
               } else if (nc == 'L') {
                  if (argc > 1) {
                     ioctl_inbuf_min = atoi (*++argv);
                     ioctl_inbuf_min = min (ioctl_inbuf_min, BIGBUF_SIZE);
                     argc--;
                     printf ("IOCTL/FSCTL lower input buffer limit %d\n", ioctl_inbuf_min);
                  } else {
                     printf ("IOCTL/FSCTL lower input limit missing\n");
                  }
               } else if (nc == 'U') {
                  if (argc > 1) {
                     ioctl_inbuf_max = atoi (*++argv);
                     ioctl_inbuf_max = min (ioctl_inbuf_max, BIGBUF_SIZE);
                     argc--;
                     printf ("IOCTL/FSCTL upper input buffer limit %d\n", ioctl_inbuf_max);
                  } else {
                     printf ("IOCTL/FSCTL upper input limit missing\n");
                  }
               } else if (!nc) {
                  if (st == '-')
                     *flags &= ~(FLAGS_DO_IOCTL_NULL|FLAGS_DO_IOCTL_RANDOM);
                  else
                     *flags |= FLAGS_DO_IOCTL_NULL|FLAGS_DO_IOCTL_RANDOM;
               }
               break;
            case 'F' :
               if (nc == 'L') {
                  if (argc > 1) {
                     ioctl_min_function = atoi (*++argv);
                     ioctl_min_function = min (ioctl_min_function, 0xFFF);
                     argc--;
                     printf ("IOCTL/FSCTL lower function limit %d\n", ioctl_min_function);
                  } else {
                     printf ("IOCTL/FSCTL lower function limit missing\n");
                  }
               } else if (nc == 'U') {
                  if (argc > 1) {
                     ioctl_max_function = atoi (*++argv);
                     ioctl_max_function = min (ioctl_max_function, 0xFFF);
                     argc--;
                     printf ("IOCTL/FSCTL upper function limit %d\n", ioctl_max_function);
                  } else {
                     printf ("IOCTL/FSCTL upper function limit missing\n");
                  }
               } else if (nc == 'R') {
                  if (st == '-')
                     *flags &= ~FLAGS_DO_FSCTL_RANDOM;
                  else
                     *flags |= FLAGS_DO_FSCTL_RANDOM;
               } else if (nc == 'N') {
                  if (st == '-')
                     *flags &= ~FLAGS_DO_FSCTL_NULL;
                  else
                     *flags |= FLAGS_DO_FSCTL_NULL;
               } else if (nc == 'I') {
                  if (st == '-')
                     *flags &= ~FLAGS_DO_FAILURE_INJECTION;
                  else
                     *flags |= FLAGS_DO_FAILURE_INJECTION;
               } else if (!nc) {
                  if (st == '-')
                     *flags &= ~(FLAGS_DO_FSCTL_NULL|FLAGS_DO_FSCTL_RANDOM);
                  else
                     *flags |= FLAGS_DO_FSCTL_NULL|FLAGS_DO_FSCTL_RANDOM;
               }
               break;
            case 'C' :
               if (st == '-')
                  *flags &= ~FLAGS_DO_SKIPCRASH;
               else
                  *flags |= FLAGS_DO_SKIPCRASH;
               break;
            case 'L' :
               if (st == '-')
                  *flags &= ~FLAGS_DO_LOGGING;
               else
                  *flags |= FLAGS_DO_LOGGING;
               break;
            case 'P' :
               if (!nc) {
                  if (st == '-')
                     *flags &= ~FLAGS_DO_POOLCHECK;
                  else
                     *flags |= FLAGS_DO_POOLCHECK;
               } else if (nc == 'R') {
                  if (st == '-')
                     *flags &= ~FLAGS_DO_PROT;
                  else
                     *flags |= FLAGS_DO_PROT;
               } else if (nc == 'D') {
                  if (st == '-')
                     *flags &= ~FLAGS_DO_PRINT_DEVS;
                  else
                     *flags |= FLAGS_DO_PRINT_DEVS;
               } else if (nc == 'S') {
                  if (argc > 1) {
                     prefix_string = *++argv;
                     argc--;
                  } else {
                     printf ("Prefix string missing\n");
                  }
               }
               break;
            case 'R' :
               if (!nc) {
                  if (st == '-')
                     *flags &= ~FLAGS_DO_SKIPDONE;
                  else
                     *flags |= FLAGS_DO_SKIPDONE;
               } else if (nc == 'D') {
                  if (st == '-')
                     *flags &= ~FLAGS_DO_RANDOM_DEVICE;
                  else
                     *flags |= FLAGS_DO_RANDOM_DEVICE;
               }
               break;
            case 'M' :
               if (st == '-')
                  *flags &= ~FLAGS_DO_MISC;
               else
                  *flags |= FLAGS_DO_MISC;
               break;
            case 'N' :
               if (st == '-')
                  *flags &= ~FLAGS_DO_MAPNULL;
               else
                  *flags |= FLAGS_DO_MAPNULL;
               break;
            case 'O' :
               if (nc == 'L') {
                  if (argc > 1) {
                     ioctl_outbuf_min = atoi (*++argv);
                     ioctl_outbuf_min = min (ioctl_outbuf_min, BIGBUF_SIZE);
                     argc--;
                     printf ("IOCTL/FSCTL lower output buffer limit %d\n", ioctl_outbuf_min);
                  } else {
                     printf ("IOCTL/FSCTL lower output limit missing\n");
                  }
               } else if (nc == 'U') {
                  if (argc > 1) {
                     ioctl_outbuf_max = atoi (*++argv);
                     ioctl_outbuf_max = min (ioctl_outbuf_max, BIGBUF_SIZE);
                     argc--;
                     printf ("IOCTL/FSCTL upper output buffer limit %d\n", ioctl_outbuf_max);
                  } else {
                     printf ("IOCTL/FSCTL upper output limit missing\n");
                  }
               } else if (nc == 'C') {
                   if (st == '-') {
                      *flags &= ~FLAGS_DO_OPEN_CLOSE;
                   } else {
                      *flags |= FLAGS_DO_OPEN_CLOSE;
                   }
               }
               break;
            case 'S' :
               if (nc == 'D') {
                  if (st == '-')
                     *flags &= ~FLAGS_DO_SECURITY;
                  else
                     *flags |= FLAGS_DO_SECURITY;
               } else if (nc == 'L'){
                  if (st == '-')
                     *flags &= ~FLAGS_DO_SYMBOLIC;
                  else
                     *flags |= FLAGS_DO_SYMBOLIC;
               } else {
                  if (st == '-')
                     *flags &= ~FLAGS_DO_SUBOPENS;
                  else
                     *flags |= FLAGS_DO_SUBOPENS;
               }
               break;
            case 'Q' :
               if (st == '-')
                  *flags &= ~FLAGS_DO_QUERY;
               else
                  *flags |= FLAGS_DO_QUERY;
               break;
            case 'A' :
               if (nc == 'L') {
                  if (st == '-')
                     *flags &= ~FLAGS_DO_ALERT;
                  else
                     *flags |= FLAGS_DO_ALERT;
               } else {
                  if (st == '-')
                     *flags &= ~FLAGS_DO_ALLDEVS;
                  else
                     *flags |= FLAGS_DO_ALLDEVS;
               }
               break;
            case 'E' :
               if (st == '-')
                  *flags &= ~FLAGS_DO_ZEROEA;
               else
                  *flags |= FLAGS_DO_ZEROEA;
               break;
            case 'Z' :
               if (st == '-')
                  *flags &= ~FLAGS_DO_LPC;
               else
                  *flags |= FLAGS_DO_LPC;
               break;
            case 'V' :
               if (st == '-')
                  *flags &= ~FLAGS_DO_ERRORS;
               else
                  *flags |= FLAGS_DO_ERRORS;
               break;
            case 'J' :
               if (st == '-')
                  *flags &= ~FLAGS_DO_STREAMS;
               else
                  *flags |= FLAGS_DO_STREAMS;
               break;
            case 'W' :
               if (st == '-')
                  *flags &= ~FLAGS_DO_WINSOCK;
               else
                  *flags |= FLAGS_DO_WINSOCK;
               break;
            case 'K' :
               if (st == '-')
                  *flags &= ~FLAGS_DO_SYNC;
               else
                  *flags |= FLAGS_DO_SYNC;
               break;
            case 'T' :
               if (nc == 'T') {
                  if (argc > 1) {
                     max_tailured_calls = atoi (*++argv);
                     argc--;
                     max_tailured_calls = ((max_tailured_calls + RAND_REP - 1) / RAND_REP) *
                                           RAND_REP;
                     printf ("IOCTL/FSCTL tailured calls %d\n", max_tailured_calls);
                  } else {
                     printf ("IOCTL/FSCTL tailured calls value is missing\n");
                  }
               } else {
                  if (argc > 1) {
                     max_random_calls = atoi (*++argv);
                     argc--;
                     max_random_calls = ((max_random_calls + RAND_REP - 1) / RAND_REP) *
                                        RAND_REP;
                     printf ("IOCTL/FSCTL random calls %d\n", max_random_calls);
                  } else {
                     printf ("IOCTL/FSCTL random calls value is missing\n");
                  }
               }
               break;
            case 'D' :
               if (nc == 'L') {
                  if (argc > 1) {
                     ioctl_min_devtype = atoi (*++argv);
                     argc--;
                     ioctl_min_devtype = min (ioctl_min_devtype, 0xFFFF);
                     printf ("IOCTL/FSCTL lower device type limit %d\n",
                             ioctl_min_devtype);
                  } else {
                     printf ("IOCTL/FSCTL lower device type limit missing\n");
                  }
               } else if (nc == 'U') {
                  if (argc > 1) {
                     ioctl_max_devtype = atoi (*++argv);
                     ioctl_max_devtype = min (ioctl_max_devtype, 0xFFFF);
                     argc--;
                     printf ("IOCTL/FSCTL upper device type limit %d\n",
                             ioctl_max_devtype);
                  } else {
                     printf ("IOCTL/FSCTL upper device type limit missing\n");
                  }
               } else if (nc == 'D') {
                  if (st == '-')
                     *flags &= ~FLAGS_DO_DIRECT_DEVICE;
                  else
                     *flags |= FLAGS_DO_DIRECT_DEVICE;
                  break;
               } else if (nc == 'R') {
                  if (st == '-') {
                      *flags2 &= ~FLAGS_DO_DRIVER;
                  } else {
                      *flags2 |= FLAGS_DO_DRIVER;
                      *flags |= FLAGS_DO_ALLDEVS;
                      if (argc > 1) {
                          status = RtlCreateUnicodeStringFromAsciiz (&DriverName, *++argv);
                          if (!NT_SUCCESS (status)) {
                              printf ("RtlCreateUnicodeStringFromAsciiz failed %x\n", status);
                              exit (EXIT_FAILURE);
                          }
                          argc--;
                      } else {
                          printf ("Input driver name missing on command line\n");
                      }
                  }
                  break;
               } else if (!nc) {
                  if (argc > 1) {
                     status = RtlCreateUnicodeStringFromAsciiz (name, *++argv);
                     if (!NT_SUCCESS (status)) {
                        printf ("RtlCreateUnicodeStringFromAsciiz failed %x\n", status);
                        exit (EXIT_FAILURE);
                     }
                     argc--;
                  } else {
                     printf ("Input device missing on command line\n");
                  }
               }
               break;
            case 'G':
               if (argc > 2) {
                  *flags |= FLAGS_DO_GRAB;
                  cid = atoi (*++argv);
                  process_handle = UlongToHandle( atoi (*++argv) );
                  argc -= 2;
               } else {
                  printf ("Client ID and handle missing\n");
               }
               break;
            case 'Y' :
               if (st == '-')
                  *flags &= ~FLAGS_DO_DISKS;
               else
                  *flags |= FLAGS_DO_DISKS;
               break;
            case '?':
            case 'H':
            default :
               *flags |= FLAGS_DO_USAGE;
               break;
         }
      } else {
         status = RtlCreateUnicodeStringFromAsciiz (name, *argv);
         if (!NT_SUCCESS (status)) {
            printf ("RtlCreateUnicodeStringFromAsciiz failed %x\n", status);
            exit (EXIT_FAILURE);
         }
      }
   }
   ioctl_inbuf_max = max (ioctl_inbuf_min, ioctl_inbuf_max);
   ioctl_outbuf_max = max (ioctl_outbuf_min, ioctl_outbuf_max);
   if (prefix_string && (*flags&FLAGS_DO_PRINT_DEVS) == 0) {
      prefix_string = NULL;
   }
   return;
}

/*
   Print out usage message
*/
void
do_usage (void)
{
   printf ("devctl [/i] [/l] [/il nn] [/iu mm] [devnam]\n\n");
   printf ("/ and + enable options, - disables options\n\n");
   printf ("/a\tDo all devices in system. Don't prompt for yes/no etc\n");
   printf ("/al\tAlert the main thread periodically\n");
   printf ("/c\tEnable or disable skipping operations that aborted or crashed\n");
   printf ("/dd\tEnable or disable the direct device open paths\n");
   printf ("/dl nn\tSets max for device type portion of IOCTL/FSCTL code, default 0\n");
   printf ("/dr drv\tOnly runs on device objects with driver <drv> in their stack\n");
   printf ("/du nn\tSets min limit for device type portion of IOCTL/FSCTL code, default 200\n");
   printf ("/e\tEnable or disable zero length EA's, needed on checked builds\n");
   printf ("/f\tEnable or disable all FSCTL paths\n");
   printf ("/fi\tEnable or disable turning on failure injection in the driver verifier\n");
   printf ("/fn\tEnable or disable FSCTL paths with null buffers\n");
   printf ("/fr\tEnable or disable FSCTL paths with random buffers\n");
   printf ("/fl nn\tSets max for function portion of IOCTL and FSCTL code, default 0\n");
   printf ("/fu nn\tSets min for function portion of IOCTL and FSCTL code, default 400\n");
   printf ("/g c h\tGrabs a handle from another process\n");
   printf ("/h /?\tPrints this message\n");
   printf ("/i\tEnable or disable all IOCTL paths\n");
   printf ("/if\tEnable or disable all FSCTL and IOCTL paths\n");
   printf ("/in\tEnable or disable IOCTL paths with null buffers\n");
   printf ("/il nnn\tSet lower input buffer size\n");
   printf ("/im\tEnable or disable the impersonation of a non-admin during the test\n");
   printf ("/iu nnn\tSet upper input buffer size\n");
   printf ("/ir\tEnable or disable IOCTL paths with random buffers\n");
   printf ("/j\tEnable or disable relative stream opens for filesystems\n");
   printf ("/k\tEnable or disable synchronous handles\n");
   printf ("/l\tEnable or disable logging and skipping failing functions\n");
   printf ("/m\tEnable or disable the misc functions\n");
   printf ("/n\tMap zero page so that NULL pointer de-references don't raise\n");
   printf ("/oc\tEnable or disable the multithreaded open and close stage\n");
   printf ("/ol nnn\tSet lower output buffer size\n");
   printf ("/ou nnn\tSet upper output buffer size\n");
   printf ("/p\tEnable or disable the checks on pool usage via tags and lookaside lists\n");
   printf ("/pd\tPrint out device objects and symbolic links and exit\n");
   printf ("/pr\tEnable or disable protection change tests\n");
   printf ("/ps sss\tSet prefix string for use with /pd\n");
   printf ("/q\tEnable or disable the normal handle query functions\n");
   printf ("/r\tEnable or disable skipping operations already logged as done\n");
   printf ("/rd\tSelect a random device object or symbolic link for testing\n");
   printf ("/s\tEnable or disable the sub or relative opens to obtain handles\n");
   printf ("/sd\tEnable or disable the query and set security functions\n");
   printf ("/sl\tEnable or disable the opening of symbolic links\n");
   printf ("/se nnn\tSet session id to nnn\n");
   printf ("/t nn\tSet max IOCTL/FSCTL calls made with random buffers, default 100000\n");
   printf ("/tt nn\tSet max tailured calls made for discovered IOCTLs/FSCTLs, default 10000\n");
   printf ("/v\tEnable or disable the printing of error status values for calls\n");
   printf ("/w\tEnable or disable the winsock TransmitFile test\n");
   printf ("/y\tEnable or disable touching disk devices\n");
   printf ("\n");
   printf ("Defaults: devctl -a -al +c +dd +dl 0 -dr +du 200 +e +fn +fr +fl 0 fu 400 +im\n");
   printf ("                 +il 0 +in +iu 512 +ir -j -k +l +m -n +oc +ol 0 +ou 512 +p -pr\n");
   printf ("                 +q +s +sd +sl +t 100000 +tt 10000 -v -w +y\n");
}

void change_session (ULONG sessionid)
{
   HANDLE token;

   if (!OpenProcessToken (NtCurrentProcess (), MAXIMUM_ALLOWED, &token)) {
      printf ("OpenProcessToken failed %d\n", GetLastError ());
      return;
   }
   if (SetTokenInformation (token, TokenSessionId, &sessionid, sizeof (sessionid))) {
      printf ("Token session ID changed to %d\n", sessionid);
   } else {
      printf ("SetTokenInformation failed %d\n", GetLastError ());
   }
   if (!CloseHandle (token)) {
      printf ("CloseHandle failed %d\n", GetLastError ());
      return;
   }
}

BOOL
EnableDebugPrivilege (
    )
{
    struct
    {
        DWORD Count;
        LUID_AND_ATTRIBUTES Privilege [1];

    } Info;

    HANDLE Token;
    BOOL Result;

    //
    // open the process token
    //

    Result = OpenProcessToken (
        GetCurrentProcess (),
        TOKEN_ADJUST_PRIVILEGES,
        & Token);

    if( Result != TRUE )
    {       
        return FALSE;
    }

    //
    // prepare the info structure
    //

    Info.Count = 1;
    Info.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;

    Result = LookupPrivilegeValue (
        NULL,
        SE_DEBUG_NAME,
        &(Info.Privilege[0].Luid));

    if( Result != TRUE )
    {
        CloseHandle( Token );

        return FALSE;
    }

    //
    // adjust the privileges
    //

    Result = AdjustTokenPrivileges (
        Token,
        FALSE,
        (PTOKEN_PRIVILEGES) &Info,
        0,
        NULL,
        NULL);

    if( Result != TRUE || GetLastError() != ERROR_SUCCESS )
    {
        CloseHandle( Token );

        return FALSE;
    }

    CloseHandle( Token );

    return TRUE;
}

void
__cdecl
main(
    int argc, char **argv
)
{
    ULONG                   seed, i;
    UNICODE_STRING          us;
    ULONG                   id, tmp;
    DWORD                   old;
    NTSTATUS                status;
    UNICODE_STRING          name;
    WSADATA wsadata;
    WORD version;
    int istatus;
    SOCKET s;
    struct sockaddr_in sockaddr;
    char nameb[255];
    HOSTENT *host;
    struct in_addr localaddr;
    DWORD retlen;
    int t;
    HANDLE listenthread;
    struct sockaddr_in laddr;
    SIZE_T size;
    PVOID base;

    //
    // Turn off popups for a missing floppy etc
    //
    SetErrorMode(SEM_FAILCRITICALERRORS);

    Create_nonadmin_token ();

    //
    // Create and event for any requests we really need to work on a async handle
    //
    status = NtCreateEvent (&sync_event, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE);
    if (!NT_SUCCESS (status)) {
       printf ("NtCreateEvent failed %x\n", status);
       exit (EXIT_FAILURE);
    }
    process_parameters (argc, argv, &name, &flags, &flags2);
    if (flags&FLAGS_DO_USAGE) {
       do_usage ();
       exit (EXIT_SUCCESS);
    }
    EnableDebugPrivilege ();

    /*
       Seed RNG
    */


//    printf("Seed? ");
//    scanf("%d", &seed);
//    srand(seed);
    srand ((unsigned) time (NULL));

    //
    // Build a socket etc to handle the TransmitFile API
    //
    do {
       if (!(flags&FLAGS_DO_WINSOCK))
          break;
       if (flags&FLAGS_DO_PRINT_DEVS)
          break;

       version = MAKEWORD (2, 0);
       istatus = WSAStartup (version, &wsadata);
       if (istatus != 0) {
          printf ("Failed to start winsock %x!\n", istatus);
          flags &= ~FLAGS_DO_WINSOCK;
          break;
       }

       istatus = gethostname (nameb, sizeof (nameb));
       if (istatus != 0) {
          printf ("Gethostname failed %d\n", WSAGetLastError ());
          flags &= ~FLAGS_DO_WINSOCK;
          break;
       }

       host = gethostbyname (nameb);
       if (!host) {
          printf ("Gethostbyname failed %d\n", WSAGetLastError ());
          flags &= ~FLAGS_DO_WINSOCK;
          break;
       }
       localaddr = *(struct in_addr *) host->h_addr_list[0];

       ls = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
       if (ls == INVALID_SOCKET) {
          printf ("Socket failed %d\n", WSAGetLastError ());
          flags &= ~FLAGS_DO_WINSOCK;
          break;
       }
       memset (&sockaddr, 0, sizeof (sockaddr));
       sockaddr.sin_family = AF_INET;
       sockaddr.sin_port = htons (0);
       sockaddr.sin_addr = localaddr;
       istatus = bind (ls, (struct sockaddr *) &sockaddr, sizeof (sockaddr));
       if (istatus != 0) {
          printf ("Bind failed %d\n", WSAGetLastError ());
          flags &= ~FLAGS_DO_WINSOCK;
          break;
       }

       istatus = listen (ls, 2);
       if (istatus != 0) {
          printf ("Listen failed %d\n", WSAGetLastError ());
          flags &= ~FLAGS_DO_WINSOCK;
          break;
       }

       t = sizeof (laddr);
       istatus = getsockname (ls, (struct sockaddr *) &laddr, &t);
       if (istatus != 0) {
          printf ("Getsockname failed %d\n", WSAGetLastError ());
          flags &= ~FLAGS_DO_WINSOCK;
          break;
       }
       printf ("Listen socket on port %d address %s\n",
               ntohs (laddr.sin_port), inet_ntoa (laddr.sin_addr));

       listenthread = CreateThread (NULL, 0, do_listen, NULL, 0, &id);
       if (!listenthread) {
          printf ("CreateThread failed %d\n", GetLastError ());
          flags &= ~FLAGS_DO_WINSOCK;
          break;
       }

       cs = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
       if (cs == INVALID_SOCKET) {
          printf ("Socket failed %d\n", WSAGetLastError ());
          flags &= ~FLAGS_DO_WINSOCK;
          break;
       }
       istatus = connect (cs, (struct sockaddr *) &laddr, sizeof (laddr));
       if (istatus != 0) {
          printf ("connect failed %d\n", WSAGetLastError ());
          flags &= ~FLAGS_DO_WINSOCK;
          break;
       }
    } while (FALSE);

    //
    // Build a big buffer to pass to requests
    //
    bigbuf = VirtualAlloc(NULL, BIGBUF_SIZE + SLOP, MEM_COMMIT, PAGE_READWRITE);
    if (!bigbuf) {
       printf ("Failed to allocate I/O buffers %x\n", GetLastError ());
       exit (EXIT_FAILURE);
    }
    //
    // Map the zero page and fill it with junk
    //
    if (flags&FLAGS_DO_MAPNULL) {
       base = (PVOID) 1;
       size = 1;
       status = NtAllocateVirtualMemory (NtCurrentProcess (), &base, 1, &size,
                                         MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
       if (!NT_SUCCESS (status)) {
         printf ("NtAllocateVirtualMemory for zero page failed %x\n", status);
         exit (EXIT_FAILURE);
       }
       for (i = 0; i < size; i++) {
          *((PUCHAR) base + i) = (UCHAR) rand ();
       }
    }
//    randthread = CreateThread (NULL, 0, randomize, bigbuf, CREATE_SUSPENDED, &id);
//    if (!randthread) {
//       printf ("Failed to create radomizing thread %x\n", GetLastError ());
//       exit (EXIT_FAILURE);
//    }
    //
    // Create a thread to change the page protection of our buffer if need be
    //
    if (flags&FLAGS_DO_PROT) {
       changethread = CreateThread (NULL, 0, changeprot, bigbuf, CREATE_SUSPENDED, &id);
       if (!changethread) {
          printf ("Failed to create protection change thread %x\n", GetLastError ());
          exit (EXIT_FAILURE);
       }
    }
    //
    // Get a real handle to our thread to pass to other threads
    //
    status = NtDuplicateObject (NtCurrentProcess (), NtCurrentThread (),
                                NtCurrentProcess (), &mainthread,
                                0, 0, DUPLICATE_SAME_ACCESS);
    if (!NT_SUCCESS (status)) {
       printf ("NtDuplcateObject failed %x\n", status);
       exit (EXIT_FAILURE);
    }
    //
    // Create a thread to alert this one periodicaly or when it thinks we are stuck
    //
    alertthread = CreateThread (NULL, 0, alerter, mainthread, 0, &id);
    if (!alertthread) {
       printf ("Failed to create alerting thread %x\n", GetLastError ());
       exit (EXIT_FAILURE);
    }
//    status = NtSuspendThread (changethread, &tmp);
//    if (!NT_SUCCESS (status)) {
//       printf ("NtSuspendThread failed %x\n", status);
//    }
//    status = NtSuspendThread (randthread, &tmp);
//    if (!NT_SUCCESS (status)) {
//       printf ("NtSuspendThread failed %x\n", status);
//    }
    if (!VirtualProtect (bigbuf, 1, PAGE_READWRITE, &old)) {
       printf ("VirtualProtect failed %d\n", GetLastError ());
    }

    devscount = 0;
    print_diags (0, 0);
    if (flags&FLAGS_DO_GRAB) {
       do_handle_grab (devmap,
                       &devscount);
    }

    /*
     * Crash the system.
     */
    if (name.Length > 0) {
       if (flags&FLAGS_DO_IMPERSONATION) {
           Impersonate_nonadmin ();
       }
 
       status = do_device_opens (NULL,
                                 &name,
                                 devmap,
                                 &devscount,
                                 name.Buffer,
                                 name.Buffer);

       if (flags&FLAGS_DO_IMPERSONATION) {
          if (!Revert_from_admin ()) {
             printf ("Revert_from_admin failed %d\n", GetLastError ());
             exit (EXIT_FAILURE);
          }
       }
    } else {
       do_wmi ();

       random_device = (rand () % 1345) + 1;

       while (1) {

           RtlInitUnicodeString(&us,
                                 L"\\");

           skipped = 0;
           recurse(0,
                    &us,
                    devmap,
                    &devscount,
                    L"",
                    1);

           for (i = 0; i < devscount; i++) {
               free (devmap[i].name);
               devmap[i].name = NULL;
               free (devmap[i].filename);
               devmap[i].filename = NULL;
               NtClose(devmap[i].handle);
           }
           devscount = 0;
           if (flags&(FLAGS_DO_ALLDEVS|FLAGS_DO_PRINT_DEVS)) {
              break;
           }
           if ((flags&FLAGS_DO_RANDOM_DEVICE) && (random_device == 0)) {
              Sleep (30*1000);
              random_device = (rand () % 1345) + 1;
           }
       }
    }
    if ((flags&FLAGS_DO_PRINT_DEVS) == 0) {
        crashes (L"DONE", L"", L"", L"", L"");
    }

    exit(EXIT_SUCCESS);
}