Editing A Remote Hard Disk

Environment: VC6, MFC, Win32 SDK

Reading and Writing Disks in Windows 95+

Many methods exist to read disks in Win 95+. I have chosen the thunking of DLLs to read the disks. This is nothing but executing the real-mode BIOS Int 13h from within the protected mode. This way, we can read the disks as if we were in real-mode DOS. However, it requires two DLLs, called the thunk DLLs. One is a 32-bit DLL that thunks down to a 16-bit DLL; it communicates with the real-mode BIOS and fetches the required buffer of data from the hard disks.

The 32-bit DLL in the protected mode translates the linear pointers and sends them down to the 16-bit DLL, which in turn calls the real-mode BIOS using the DOS Protected Mode Interface (DPMI). We have to set up a part of real-mode memory for fetching the buffer, which is mapped to the memory of the 16-bit DLL in the protected mode, which is copied to the linear memory by the routine that handles reading the buffer. For writing, the process is just the reverse; first, the buffer in the linear memory is copied to the protected mode 16-bit memory, which is mapped to the real-mode memory, which is written to the disk.

In Windows 95+, there are no restrictions on how the system is being used. The Kernel and Application levels (Ring 0 and Ring 3) are seamlessly integrated where the memories can be allocated by anything and accessed by anything.

Reading and Writing Disks in Windows NT+

In NT, the reading and writing mechanisms are entirely different. Because of the security restrictions, we cannot call the BIOS or the real-mode interrupts but only access the disks using the APIs that NT offers. The API for accessing the disks is CreateFile, which opens a disk as if it were a file; then the disk is read or written as if it were a file except that it can be done only in chunks of sectors. The disk reads and writes are treated just like files except that it does direct disk IO.

Int 13h Extensions

BIOS offers Int 13h extensions in the latest motherboards, starting a few years back. This is necessary because in the old BIOS it is not possible to read beyond 8 Gb of hard disk because of register restrictions; this is the mechanism for reading and writing disks. I have handled int 13h extensions in the thunk 16-bit DLL. Certain BIOS interrupts are added; this allows up to two-power, 64-bit number of sectors to be accessed. This is way beyond anyone's imagination and can be used for decades without altering BIOS any more. NT internally handles int 13h extensions, so there is no need to handle the extensions separately.

Disk Organization in the FAT File System

In the FAT file system, the disk organization is straightforward and simple. The organization of the files ensures that the files are accessible through a simple walkthrough of pointers in a random fashion. This way, any file anywhere in the disk can be located with a simple mechanism. FAT12, or 12-bit FAT, is the lowest member of the FAT family, where the file pointers are just 1.5 bytes. FAT, or 16-bit FAT, is most widely used in hard disks that have 16-bits or 2 bytes per pointer. VFAT is used in Win95 on; it is the FAT with long file name support. FAT32, or 32-bit FAT, is the highest member that can handle huge hard disks as a single partition; it is used from Win95 OSR 2 on. NT 4 does not manipulate FAT32; hence, FAT is the lowest common member of the FAT file system. Starting from Win2000, the OS handles FAT32; hence, we manipulate this file system also.

The boot sector comes first in FAT, followed by hidden and reserved sectors. Then come the FAT tables. The FAT tables consist of pointers to the next cluster occupied by a file. The pointers point to the next clusters, finally terminated by a word or a double word, depending on the file system. The bad clusters are marked with a unique word or dword, thus they are skipped during the assigning process. FATs are stored in two copies for security reasons because this is the most important part of the file system. If any FAT sectors become unusable, they are skipped and the second copy is used automatically. The FAT is present in a fixed location of the disk at the start of the partition. Then, after the two copies of FAT, comes the root directory. This is a 32-byte packet with file information coded in it. The root directory is fixed in FAT12 and FAT16 but are just another sub-directory in FAT32. If they are fixed, only a certain number of files can be stored in the directory; otherwise, if, as in FAT32, they can store an enormous number of files.

Then come the files and sub-directories that are organized only with the help of the FAT tables. The starting cluster number is stored in the directory entry and it points to the next cluster in FAT table that is occupied by the file. This phenomenon continues until the end-of-file mark is reached.

Utility Program Overview to Editing Remote Hard Disks

The program for manipulating the sectors of the disk looks like the following figure. The program has two views, like Explorer. The left portion of the screen is the disks that can be edited locally; the right contains the sectors or clusters. The first pane in the status bar displays if the program is performing a calculation.

If you are connected remotely, it is displayed as "Remote - (Machine Name)". The items expanded within it will edit the remote hard disk.

There is a server portion for the utility that is to be installed in the remote machine, which should be in the same local intranet. Once the server portion is installed in the remote machine, the local machine can communicate with it to edit the remote hard disk.



Click here for a larger image.

Inside Programming

The utility was developed using Visual C++ 6 and DDK. It also uses some programs such as THUNK.EXE from the Platform SDK to compile the thunk script. The thunk DLLs can be used without changing them in any programs. There is also a device driver for reading CMOS memory to know the number of floppy disks in the system. I used BC++ 3.1 to compile the 16-bit thunk DLL. The main project uses MFC and was compiled using VC++ 6.

First, I would like to give the structure of the boot record completely, including the FAT32 version of it. I give the structure of the boot record and explain its fields:

typedef struct{
  char Jmp[3];
  char OEM_Name[8];
  WORD BytesPerSector;
  unsigned char SectorsPerCluster;
  WORD ReservedSectors;
  unsigned char NumFATs;
  WORD NumRoot;
  WORD TotalSectors;
  unsigned char MediaDB;
  WORD SectorsPerFAT;
  WORD SectorsPerTrack;
  WORD NumHeads;
  DWORD NumHidSect;
  DWORD ExtendedTotalSectors;
  DWORD ExtendedSectorsPerFAT;
  WORD Flags;
  WORD Version;
  DWORD RootStartCluster;
  WORD FSInfoSec;
  WORD BkUpBootSec;
  WORD Reserved;
}BOOT;

The RootStartCluster is new to the boot record. This field contains a 32-bit starting cluster number of the root directory of the FAT32 drive. This is because the root directory is just another sub-directory in FAT32. There is also a backup boot sector in FAT32, which is denoted in BkUpBootSec.

The partition table is the first sector in a disk and contains information on the whereabouts of the disks of the system. It can have four partitions; the value of 0x80 in a field signifies the active partition. I will explain the organization of the disk after showing you the partition table:

typedef struct{
  BYTE BootInd;
  BYTE Head;
  BYTE Sector;
  BYTE Cylinder;
  BYTE SysInd;
  BYTE LastHead;
  BYTE LastSector;
  BYTE LastCylinder;
  DWORD RelativeSector;
  DWORD NumberSectors;
}PARTITION;

The BootInd at the top should be 0x80 to denote the active partition. For other partitions, it should be 0. The SysInd has the following values and meanings:

#define PART_UNKNOWN 0x00        //Unknown.
#define PART_DOS2_FAT 0x01       //12-bit FAT.
#define PART_DOS3_FAT 0x04       //16-bit FAT. Partition smaller
                                 //than 32MB.
#define PART_EXTENDED 0x05       //Extended MS-DOS Partition.
#define PART_DOS4_FAT 0x06       //16-bit FAT. Partition larger
                                 //than or equal to 32MB.
#define PART_DOS32 0x0B          //32-bit FAT. Partition up to
                                 //2047GB.
#define PART_DOS32X 0x0C         //Same as PART_DOS32(0Bh), but
                                 //uses Logical Block Address Int
                                 //13h extensions.
#define PART_DOSX13 0x0E         //Same as PART_DOS4_FAT(06h), but
                                 //uses Logical Block Address
                                 //Int 13h extensions.
#define PART_DOSX13X 0x0F        //Same as PART_EXTENDED(05h), but
                                 //uses Logical Block Address Int
                                 //13h extensions.

The NTFS currently has a value as 7, but I am not dealing with it in this article.

The Head, Sector, and Cylinder have the values of the start of the partition. Note that these are byte values; hence, for disks greater than 8 GB, these values cannot be trusted and only the RelativeSector signifies the right values. The Last values signify the end of the partition. The NumberSectors is the number of sectors for a partition belonging to this structure definition.

The partitions have been extended to handle many drives in the partition. This is accomplished by the following method:

The partition points to an Extended partition table that has the same structure but with an arrangement like linked lists. There must be one Primary partition and an extended partition which has a drive and pointer to another (extended) partition table. This setup continues till there are no more extended partitions. I have given you the deciphering of this setup in the code. The program stores the item's data of the tree view in the memory as pointers to a structure which is described below.

typedef struct{
  WORD Drive;
  WORD Cylinder;
  WORD Head;
  WORD Sector;
  DWORD NumSectors;
  WORD Type;
  WORD FatType;
  WORD FatRelativeSector;
  DWORD NumFatSectors;
  char *Lfn;
  DWORD RelativeSector;
  WORD TotalHeads;
  DWORD StartCluster;
  WORD SectorsPerTrack;
  WORD SectorsPerCluster;
  WORD Attributes;
  DWORD TotalSectors;
  DWORD NTRelativeSector;
  DWORD DataAreaSector;
  BOOL Flag;
  BOOL Net;
}DRIVEPACKET;

A large chunk of memory is allocated during the init phase of the program. This memory is used to store all the packets of information.

The Drive is the drive number that this packet belongs to. The Cylinder, Head, and Sector point to the drive's starting location that this packet belongs to; NumSectors is the number of sectors. Type points to one of the following values:

#define PART_TABLE 0
#define BOOT_RECORD 1
#define EXTENDED_PART 2
#define FAT16 3
#define FAT12 4
#define ROOT_DIR 5
#define FILES_FOLDERS 6
#define SECTORS_DUMP 7
#define RAW_DUMP 8
#define FAT32 9
#define SUB_DIR 10
#define UNKNOWN 11

FatType points to FAT12, FAT16, or FAT32, depending on the FAT type. FatRelativeSector points to the start of the FAT table within the partition. NumFatSectors is the number of FAT sectors. The *Lfn points to the full name of the Long File Name that this packet points to. Note that not all packets that belong to different categories have all the fields pointing to some values. For some packets, the values are significant and for others it is not. For example, in a packet pointing to PART_TABLE, the Lfn is not pointing to anything and the value is undefined. The RelativeSector points to a sector within the partition that this packet points to. This value is offset from the start of the partition pointed to by Cylinder, Head, and Sector. The NTRelativeSector points to the relative sector from the start of the disk. It is for dealing with the disks in NT/2000/XP. However, this value comes in handy when dealing with disks bigger than 8 GB, in which case the Cylinder, Head, and Sector values are neglected and this value is used throughout. The Flag is the flag that signifies whether extension to Int 0x13 is available in the system. This value is used by the 16-bit DLL to know whether NTRelativeSector is to be used or the Cylinder, Head, and Sector are to be used.

Then when we come to the coding, the notification routine which does the display of information and creating packets of drives and folders is OnItemExpanding(...) in LeftView.cpp. The first case that is handled is the PART_TABLE which is the partition table.

  case PART_TABLE:{
    PrevRelSector = MainPrevRelSector = 0;
    hMemBufferDir = LocalAlloc(LMEM_MOVEABLE|LMEM_DISCARDABLE|
                               LMEM_ZEROINIT, DrivePacket->
                               NumSectors * 512);
    if(hMemBufferDir == NULL){
      AfxMessageBox("Fatal Error! Not able to allocate a
                     Buffer!");
      return;
    }
    BufferDir = (LPBYTE) LocalLock(hMemBufferDir);
    BufferNet = BufferDir;
    NetEvent = BUFFER_FILL;
    memcpy(&gDrivePacket, DrivePacket, sizeof(gDrivePacket));
    BytesToFill = DrivePacket->NumSectors * 512;
    if(DrivePacket->Net){
      NetCall.Function = LOAD_SECTOR;
      NetCall.Cylinder = DrivePacket->Cylinder;
      NetCall.Drive = DrivePacket->Drive;
      NetCall.Head = DrivePacket->Head;
      NetCall.Sector = DrivePacket->Sector;
      NetCall.NumSectors = DrivePacket->NumSectors;
      NetCall.NTRelativeSector = DrivePacket->RelativeSector;
      NetCall.RelativeSector = 0;
      NetCall.Flag = DrivePacket->Flag;
      send(SocketNum, (char *) &NetCall, sizeof(SECTOR_FUNC), 0);
      CallNet();
    }
    else if(gOSWin95){
      if(!(DllThunk32)(DrivePacket->Drive,
                       DrivePacket->Cylinder,
                       DrivePacket->Head,
                       DrivePacket->Sector,
                       DrivePacket->NumSectors,
                       (LPBYTE)BufferDir,
                       DrivePacket->NTRelativeSector,
                       DrivePacket->Flag)){
        AfxMessageBox("Partition table not accessible!");
        return;
      }
    }
    else{
      __int64 Tmp64 = ((__int64) DrivePacket->NTRelativeSector)
                    * 512;
      long TmpVal = Tmp64 & 0xFFFFFFFF;
      long TmpValHi = (Tmp64 >> 32);
      SetFilePointer(hDisk[DrivePacket->Drive], TmpVal,
                     &TmpValHi, FILE_BEGIN);
      TmpVal = 0;
      ReadFile(hDisk[DrivePacket->Drive],
               BufferDir, DrivePacket->NumSectors * 512,
              (DWORD *) &TmpVal, NULL);
      if(TmpVal != (DrivePacket->NumSectors * 512)){
        AfxMessageBox("Partition table not accessible!");
        return;
      }
    }
    PartitionTable = (PARTITION *) (BufferDir+0x1BE);
    UnknownPart = FALSE;
    for(i=0; i<4; i++){
      switch(PartitionTable->SysInd)
      {
      case 0:
        continue;
        break;
      case PART_DOS2_FAT:
        strcpy(TmpStr, "12-Bit FAT Disk");
        break;
      case PART_DOS3_FAT:
        strcpy(TmpStr, "16-Bit FAT Disk");
        break;
      case PART_EXTENDED:
        strcpy(TmpStr, "Extended DOS Partition");
        break;
      case PART_DOS4_FAT:
        strcpy(TmpStr, "16-Bit FAT Disk");
        break;
      case PART_DOS32:
        strcpy(TmpStr, "32-Bit FAT Disk");      //Normal FAT32
        break;
      case PART_DOS32X:
        strcpy(TmpStr, "32-Bit FAT Disk");      //FAT32 with int
                                                //13 extension
        break;
      case PART_DOSX13:
        strcpy(TmpStr, "16-Bit FAT Disk");      //Extended int 13
        break;
      case PART_DOSX13X:
        strcpy(TmpStr, "Extended 32-bit Partition");
                                                //Extended DOS
                                                //partition in
                                                //int 13
                                                //extension
        break;
      default:
        UnknownPart = TRUE;
        strcpy(TmpStr, "Unknown");
        break;
      }
      Packets->Cylinder = PartitionTable->Cylinder;
      Packets->Drive = DrivePacket->Drive;
      Packets->Flag = DrivePacket->Flag;
      Packets->Head = PartitionTable->Head;
      Packets->Sector = PartitionTable->Sector;
      Packets->NumSectors = 1;
      Packets->Type = ((PartitionTable->SysInd
                    == PART_EXTENDED) || (PartitionTable->
                       SysInd == PART_DOSX13X)) ?
                       EXTENDED_PART:BOOT_RECORD;
      if(UnknownPart)
        Packets->Type = UNKNOWN;
      Packets->FatType = ((PartitionTable->SysInd
                       == PART_DOS32) || (PartitionTable->
                          SysInd == PART_DOS32X)) ? FAT32:0;
      if((PartitionTable->SysInd == PART_EXTENDED) ||
         (PartitionTable->SysInd == PART_DOSX13X)){
        MainPrevRelSector = PartitionTable->RelativeSector;
        Packets->NTRelativeSector = MainPrevRelSector;
      }
      else{
        Packets->NTRelativeSector = MainPrevRelSector
                                     + PartitionTable->
                                       RelativeSector;
      }
      Packets->Net = DrivePacket->Net;

      TvI.mask = TVIF_CHILDREN | TVIF_TEXT | TVIF_PARAM;
      TvI.pszText = TmpStr;
      if(UnknownPart){
        Packets->RelativeSector = 1;
        TvI.cChildren = 0;
      }
      else{
        Packets->RelativeSector = 0;
        TvI.cChildren = 1;
      }
      TvI.lParam = (long)Packets++;

      TvIns.item = TvI;
      TvIns.hParent = (HTREEITEM) pNMTreeView->itemNew.hItem;
      TvIns.hInsertAfter = hPrev;
      hPrev = DiskTree->InsertItem((LPTVINSERTSTRUCT)
              &TvIns);
      PartitionTable++;
    }
    LocalUnlock(hMemBufferDir);
    LocalFree(hMemBufferDir);
    break;
    }

The following routine is responsible for connecting to and listening for the client machine.

BOOL ConnectAndListen(void)
{
  CSADDR_INFO CSABuf[10];
  DWORD dwCSABufsize = sizeof(CSABuf);
  GUID guidDNS = SVCID_TCP(1026);
  int lpTCP[10] = {IPPROTO_TCP, 0};
  BYTE Buffer[1000];
  DWORD Bytes;
  PPROTOCOL_INFO lpTCPINFO;
  // And create the socket descriptor
  lpTCPINFO = (PPROTOCOL_INFO) Buffer;
  Bytes = sizeof(Buffer);
  EnumProtocols((LPINT) lpTCP, lpTCPINFO, &Bytes);
  if ((SocketNum = socket(lpTCPINFO->iAddressFamily,
                          lpTCPINFO->iSocketType,
                          lpTCPINFO->iProtocol))
                                   == SOCKET_ERROR)
  {
    // ERROR
    return FALSE;           // This will reuse the current
                            // ServSocks structure
  }
  GetAddressByName(0,       // Since GUID is name space specific,
                            // we don't need to specify
            &guidDNS,       // GUID defined by TCP port number
            "UndeleteNet",  // This parameter is actually not
                            // used for RES_SERVICE calls
            NULL,           // GUID implies protocol so no need
                            // to specify
            RES_SERVICE,    // Specifies that we are trying to
                            // find local address to bind to
            NULL,           // Currently not supported
            CSABuf,         // Results buffer
            &dwCSABufsize,  // Size of results buffer
            NULL,           // Not supported
            NULL);          // Not supported
                            // Need at least one address
                            // returned in order to bind()
  GetLastError();
  if(bind(SocketNum, CSABuf[0].LocalAddr.lpSockaddr,
                     CSABuf[0].LocalAddr.iSockaddrLength)
                     ==SOCKET_ERROR){
    closesocket(SocketNum);
    return FALSE;
  }
  if (getsockname(SocketNum,
                 (PSOCKADDR) &CSABuf[0].LocalAddr.lpSockaddr,
                 (PINT) &CSABuf[0].LocalAddr.iSockaddrLength)
                 == SOCKET_ERROR){
    // Error -- better clean up
    closesocket(SocketNum);
    return FALSE;
  }
  // Listen on the socket
  if (listen(SocketNum, 5) == SOCKET_ERROR)
  {
    // Error -- clean up
    MessageBox(hDlg, "Error in listen", "LISTEN", MB_OK);
    closesocket(SocketNum);
    return FALSE;
  }
  if(WSAAsyncSelect(SocketNum,
                    hDlg,
                    MW_CONNECTED,
                    FD_ACCEPT) == SOCKET_ERROR)
  {
    // Error -- clean up
    MessageBox(hDlg, "Error in WSAAsyncSelect", "ERROR", MB_OK);
    closesocket(SocketNum);
    return FALSE;
  }
  return TRUE;

}

The following routine displays one sector from the local or remote hard disk differentiated by the Net flag in the DRIVEPACKET structure. If it is set to TRUE, the packet belongs to a remote machine. The remote machine will respond within a time by sending the packets of information from the server portion of the utility. The server will load the sector from the remote machine sent as a Net packet having the following structure:

typedef struct{
  BYTE Function;
  WORD Drive;
  WORD Cylinder;
  WORD Head;
  WORD Sector;
  DWORD NumSectors;
  DWORD NTRelativeSector;
  DWORD RelativeSector;
  BOOL Flag;
}SECTOR_FUNC, *PSECTOR_FUNC;

The above is as a single sector packet structure as understood by the server. For multiple sectors, it is sent as the following packet to the server.

typedef struct{
  BYTE Function;
  DRIVEPACKET DrivePacket;
}SECTORS_FUNC, *PSECTORS_FUNC;

typedef struct{
  WORD NumFloppies;
  WORD NumHards;
  BOOL Int13Extension;
}NUM_DISKS;

void DisplayOneSector(DRIVEPACKET *DrivePacket)
{
  DRIVEPACKET TmpDrivePacket;
  char TmpStr[100];
  memcpy(&TmpDrivePacket, DrivePacket, sizeof(DRIVEPACKET));
  TmpDrivePacket.NumSectors = 1;
  if(TmpDrivePacket.RelativeSector == 0){
    BufferNet = Buffer;
    NetEvent = BUFFER_FILL;
    memcpy(&gDrivePacket, DrivePacket, sizeof(gDrivePacket));
    BytesToFill = DrivePacket->NumSectors * 512;
    if(DrivePacket->Net){
      NetCall.Function = LOAD_SECTOR;
      NetCall.Cylinder = DrivePacket->Cylinder;
      NetCall.Drive = DrivePacket->Drive;
      NetCall.Head = DrivePacket->Head;
      NetCall.Sector = DrivePacket->Sector;
      NetCall.NumSectors = DrivePacket->NumSectors;
      NetCall.NTRelativeSector = DrivePacket->NTRelativeSector;
      NetCall.RelativeSector = DrivePacket->RelativeSector;
      NetCall.Flag = DrivePacket->Flag;
      send(SocketNum, (char *) &NetCall, sizeof(SECTOR_FUNC),
                                                0);
      CallNet();
    }
    else if(gOSWin95){
        if(!(DllThunk32)(TmpDrivePacket.Drive,
                         TmpDrivePacket.Cylinder,
                         TmpDrivePacket.Head,
                         TmpDrivePacket.Sector,
                         TmpDrivePacket.NumSectors,
                           (LPBYTE)Buffer,
                         TmpDrivePacket.NTRelativeSector,
                         TmpDrivePacket.Flag)){
        AfxMessageBox("Sector not accessible!");
        return;
      }
    }
    else{
      __int64 Tmp64 = ((__int64) DrivePacket->NTRelativeSector)
                    * 512;
      long TmpVal = Tmp64 & 0xFFFFFFFF;
      long TmpValHi = (Tmp64 >> 32);
      SetFilePointer(hDisk[DrivePacket->Drive], TmpVal,
                                                &TmpValHi,
                                                FILE_BEGIN);
      TmpVal = 0;
      ReadFile(hDisk[DrivePacket->Drive], Buffer,
                     DrivePacket->NumSectors * 512, (DWORD *)
                     &TmpVal, NULL);
      if((!TmpVal) && (DrivePacket->Drive < 0x80)){
        CloseHandle(hDisk[DrivePacket->Drive]);
        char TmpStr[100];
        wsprintf(TmpStr, "\\\\.\\%c:", DrivePacket->Drive+'A');
        if((hDisk[DrivePacket->Drive]=CreateFile(
            TmpStr, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,
                    NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING,
                    NULL))==INVALID_HANDLE_VALUE){
          return;
        }
        Tmp64 = (((__int64) DrivePacket->NTRelativeSector)
              + ((__int64) DrivePacket->RelativeSector)) * 512;
        TmpVal = Tmp64 & 0xFFFFFFFF;
        TmpValHi = (Tmp64 >> 32);
        SetFilePointer(hDisk[DrivePacket->Drive], TmpVal,
                                                  &TmpValHi,
                                                  FILE_BEGIN);
        TmpVal = 0;
        ReadFile(hDisk[DrivePacket->Drive], Buffer,
                       DrivePacket->NumSectors * 512, (DWORD *)
                       &TmpVal, NULL);
      }
      if(TmpVal != (DrivePacket->NumSectors * 512)){
        AfxMessageBox("Boot Record not accessible!");
        return;
      }
    }
  }
  else{
    TmpDrivePacket.RelativeSector += gCurrentIndex;
    if(!LoadSectors(&TmpDrivePacket, &CylHeadSect, Buffer)){
      AfxMessageBox("Sector not accessible!");
      return;
    }
  }
  for(int i=0; i<32; i++){
    ListView->InsertItem(LVIF_TEXT|LVIF_PARAM, i,
                         LPSTR_TEXTCALLBACK, 0, 0, i,
                        (LPARAM) i);
  }
  wsprintf(TmpStr, "Current Sector Number: %lu",
           TmpDrivePacket.RelativeSector);
  m_wndStatusBarCtrl->SetText(TmpStr, 1, 0);
}

The connection callback gets the following message that handles various messages for read requests. LOAD_SECTORS will load many sectors and LOAD_SECTOR will load just one sector.

  case MW_CONNECTED:
    switch(WSAGETSELECTEVENT(lParam)){
    case FD_READ:
      recv(SocketNum, (char *)Function, 1000, 0);
      switch(Function[0]){
      case LOAD_SECTORS:
        pSectorsFunction = (PSECTORS_FUNC) Function;
        if(!AllocateBuffer(pSectorsFunction->
                           DrivePacket.NumSectors)){
          MessageBox(hDlg, "Memory allocation error!",
                           "SERVER", MB_OK);
          return FALSE;
        }
        if(!LoadSectors(&pSectorsFunction->DrivePacket,
           Buffer)){
           MessageBox(hDlg, "LoadSectors Error!", "SERVER",
                      MB_OK);
        }
        while(SentBytes < pSectorsFunction->
                             DrivePacket.NumSectors*512){
          SentBytes += send(SocketNum, (char *)
                           (Buffer+SentBytes),
                       pSectorsFunction->
                       DrivePacket.NumSectors*512, 0);
        }
        break;
      case WRITE_SECTORS:
        break;
      case WRITE_SECTOR:
        break;
      case LOAD_SECTOR:
        pSectorFunction = (PSECTOR_FUNC) Function;
        if(!AllocateBuffer(pSectorFunction->NumSectors)){
          MessageBox(hDlg, "Memory allocation error, Sector!",
                           "SERVER", MB_OK);
          return FALSE;
        }
        if(gOSWin95){
          if(!(DllThunk32)(pSectorFunction->Drive,
                           pSectorFunction->Cylinder,
                           pSectorFunction->Head,
                           pSectorFunction->Sector,
                           pSectorFunction->NumSectors,
                           (LPBYTE)Buffer, pSectorFunction->
                           NTRelativeSector,
                           pSectorFunction->Flag)){
            MessageBox(hDlg, "LoadSectors Error!", "SERVER",
                       MB_OK);
          }
        }
        else{
          DRIVEPACKET TmpDP, TmpDrivePacket;
          memcpy(&TmpDP, pSectorFunction, sizeof(DRIVEPACKET));
          __int64 Tmp64 = (((__int64) pSectorFunction->
                                      NTRelativeSector)
                        + ((__int64) pSectorFunction->
                                      RelativeSector)) * 512;
          long TmpVal = Tmp64 & 0xFFFFFFFF;
          long TmpValHi = (Tmp64 >> 32);
          SetFilePointer(hDisk[pSectorFunction->Drive], TmpVal,
                         &TmpValHi, FILE_BEGIN);
          TmpVal = 0;
          ReadFile(hDisk[pSectorFunction->Drive], Buffer,
                         pSectorFunction->NumSectors * 512,
                         (DWORD *) &TmpVal, NULL);
          if((!TmpVal) && (pSectorFunction->Drive <
                             0x80)){
            CloseHandle(hDisk[pSectorFunction->Drive]);
            char TmpStr[100];
            wsprintf(TmpStr, "\\\\.\\%c:", pSectorFunction->
                                           Drive+'A');
            if((hDisk[pSectorFunction->Drive]
                      =CreateFile(TmpStr,
                       GENERIC_READ|GENERIC_WRITE,
                       FILE_SHARE_READ,
                       NULL, OPEN_EXISTING,
                       FILE_FLAG_NO_BUFFERING,
                       NULL))==INVALID_HANDLE_VALUE){
              return FALSE;
            }
            Tmp64 = (((__int64) pSectorFunction->
                       NTRelativeSector) + ((__int64)
                       pSectorFunction->RelativeSector)) * 512;
            TmpVal = Tmp64 & 0xFFFFFFFF;
            TmpValHi = (Tmp64 >> 32);
            SetFilePointer(hDisk[pSectorFunction->Drive],
                           TmpVal, &TmpValHi, FILE_BEGIN);
            TmpVal = 0;
            ReadFile(hDisk[pSectorFunction->Drive], Buffer,
                     pSectorFunction->NumSectors * 512,
                     (DWORD *) &TmpVal, NULL);
          }
          if(TmpVal != (pSectorFunction->NumSectors * 512)){
            DRIVEPACKET TmpDrivePacket;
             if (pSectorFunction->NumSectors == 1)
              return FALSE;
            memcpy(&TmpDrivePacket, &TmpDP,
                   sizeof(DRIVEPACKET));
            for(int i=0; i<pSectorFunction->NumSectors; i++){
              TmpDrivePacket.NumSectors = 1;
              if(!LoadSectors(&TmpDrivePacket, (LPBYTE)
                             (Buffer+i*512))){
                for(int j=0; j<512; j++){
                  Buffer[i*512+j] = 0xE5;
              }
              }
              TmpDrivePacket.RelativeSector++;
  }
            break;
          }
        }
        while(SentBytes < pSectorFunction->NumSectors*512){
          SentBytes += send(SocketNum, (char *) Buffer+SentBytes,
                       pSectorFunction->
                       NumSectors*512-SentBytes, 0);
        }
        break;
      case SAY_NUM_DISKS:
        NUM_DISKS NumDisks;
        if(gOSWin95)
          NumDisks.Int13Extension = (DllCheckInt13Extension)
                                    (0x80);
        else
          NumDisks.Int13Extension = 0;
        NumDisks.NumFloppies = NumFloppies;
        NumDisks.NumHards = NumHards;
        send(SocketNum, (char *) &NumDisks,
                         sizeof(NUM_DISKS), 0);
        break;
      default:
        MessageBox(hDlg, "Function unavailable", "ERROR",
                   MB_OK);
        break;
      }
      break;
    case FD_WRITE:
      break;
    case FD_ACCEPT:
      SocketNum = accept(SocketNum, NULL, NULL);
      if(WSAAsyncSelect(SocketNum,
        hDlg,
        MW_CONNECTED,
        FD_READ|FD_WRITE) == SOCKET_ERROR){
          // Error -- clean up
          MessageBox(hDlg, "WSAAsyncSelect Error", "ACCEPT",
                     MB_OK);
          closesocket(SocketNum);
          return FALSE;
        }
      break;
    }

The WSAAsyncSelect socket API is used for asynchronous packets handling. The preceding routine is responsible for the callback received when packets are requested from the client machine. The server portion responds to the client by gathering the packets of information using the LoadSectors and other routines in it.

Downloads

Download source - 53 Kb
Download Server and RemoteEdit Executibles - 198 Kb


About the Author

Vinoj Kumar

I have been programming for the past 16 years. I started programming in 1990. I came to Windows in 1993. I have authored a book called, "Classic Utilities Using Assembly Language" , 1995. In my free time I listen a lot to Kenny G sax all the albums and Valentine Classics Songs. I like to watch a lot of TinTin adventure Comics. I am currently working in K7 Computing antivirus company (www.k7computing.com) as Senior Technical Lead. My contact is: Phone: +91 944 411 7353

Comments

  • ? Here

    Posted by HenryAuyu on 10/04/2011 11:14am

    Can we contact the author?

    Reply
  • CD drive reading and writing?

    Posted by sayha on 02/13/2007 10:07pm

    nice article.. Anyway, can u help me do a simple program to perform like Nero ( writing and reading from CD drive ). tenx

    Reply
  • How can i explore NTFS ?

    Posted by Legacy on 07/16/2003 12:00am

    Originally posted by: boonsha

    
    

    i want to get the details of a NTFS area. using ur application i could explore remote hard disk.....but it could work only in FAT16 and FAT32.... not in NTFS...
    pls tell me any solution regarding my issue..

    Reply
  • Remote Floppy Disk Editing

    Posted by Legacy on 05/06/2003 12:00am

    Originally posted by: ABHAY

    Is there any other method so that i can remotly edit the
    floppy disk,this query is against the requirement of my
    project.if u know any thing related to this it will help me
    a lot.

    • good

      Posted by perrrin on 05/01/2006 12:05pm

      very good

      Reply
    Reply
  • Very Nice, Can we use the Hook in changing the Open & save of an EXE ?

    Posted by Legacy on 01/19/2003 12:00am

    Originally posted by: fadi

    Hi Very nice work, it's full of good information.
    I have a problem and I hope if you help me in resolving it:

    I have an exe file withou source code, I need to change the Open & save functions of the exe, that mean I need to modify the method of saving the data on the HardDisk, Can we use the hook function to resolve this problem ? and do you have any othe idea ?
    Thank you in advanced and best regards

    Reply
  • Disk16.dll ; Disk32.dll ; Disk.vxd

    Posted by Legacy on 01/18/2003 12:00am

    Originally posted by: Christiaan Ghijselinck


    Is their a reason for not publishing the source code of the Disk*.* files ?

    Reply
  • Nice Step Towards the horizons of Colossus Computer Hardware

    Posted by Legacy on 01/15/2003 12:00am

    Originally posted by: Imran Shaban Bhatti.

    I have just seen the intro which is effectively describe that how one can start the embark towards the system programming.
    
    

    It is very nice effort
    well done

    Reply
  • Editing Remote Hard Disk

    Posted by Legacy on 01/02/2003 12:00am

    Originally posted by: syedimranhaider

    It seems to me interesting and knowledge full.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • IBM Worklight is a mobile application development platform that lets you extend your business to mobile devices. It is designed to provide an open, comprehensive platform to build, run and manage HTML5, hybrid and native mobile apps.

  • In this webinar, IDC featured speaker Steve Conway, Vice President of High Performance Computing, will present an update on the global x86 HPC cluster market. The presentation will include IDC's five-year forecast for the medium- to large-scale technical computing and data analysis emerging markets by systems, processors and application middleware. Cray's featured speaker, John Lee, Vice President of Cray Cluster Advanced Technology Systems, will present the new Cray® CS400™ cluster series based on …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds