Forensic Inspection of Hard Disks



Click here for a larger image.

Environment: VC6, MFC, DLL

This is a systems-side programming project. The forensic analysis tool helps find unique strings of various kinds to be found on a given hard disk. This project involves finding the hard disks and writing out the string occurrences in the searched hard disks. If the string is found, it will write out files to the destination drive with many segments and the pattern of the results file is as follows:

  • System Information
  • Computer Name
  • Logged User Name
  • Other details of the machine
  • Chunks of the words found with number of bytes before and after

The utility uses only a single thread during the process of searching and it uses PeekMessage to monitor activities. The project uses a modeless dialog box to display messages. For example, if the destination drive happens to be a floppy drive, the utility displays a modeless dialog box and waits for the user to insert the floppy disk and continues normally without the touch of the mouse or the keyboard.

Reading and Writing Disks in Windows 95+

There are many methods for reading 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 will communicate to the real-mode BIOS and fetch the required buffer of data from the hard disks. The 32-bit 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; then it is copied to the linear memory by the routine that handles reading of 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; the disk is then 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 some years back. This is necessary because in the old BIOS it is not possible to read beyond 8 Gb of hard disk due to register restrictions, which 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 the imagination and can be used for decades without altering the 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; they are 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 also manipulate this file system.

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 is just another sub-directory in FAT32. It is fixed; only a certain number of files can be stored in the directory. Otherwise, if as in FAT32, it can store enormous numbers of files.

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

System Information

This routine gets the system information from the Registry. The information is different in 95 and NT breeds. The querying is done using the Reg APIs. The RegEnumKeyEx searches for the hardware installed in the system.

  long KeyRes;
  DWORD TmpVal;
  HKEY hKey, hKeyTmp, hKeyTmp1;
  FILETIME Ft;
  char ClassStr[500] = "SYSTEM\\CurrentControlSet\\Control\\
      Class\\";
  char ClassStr95[500]= "SYSTEM\\CurrentControlSet\\Services\\
      Class\\";
  char ClassStrTmp[500], ClassStrTmp1[500];
  SysInfo[0]=0;
  SysInfoTmp = SysInfo;
  strcpy(SysInfo, "\nSystem Information\n\t");

  if(!gOSWin95)
    KeyRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
      "SYSTEM\\CurrentControlSet\\Control\\Class", 0, KEY_READ,
      &hKey);
  else
    KeyRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
      "SYSTEM\\CurrentControlSet\\Services\\Class", 0, KEY_READ,
      &hKey);
  if(KeyRes==ERROR_SUCCESS){
    i=0;
    while(KeyRes != ERROR_NO_MORE_ITEMS){
      TmpVal = sizeof(TmpStr);
      KeyRes = RegEnumKeyEx(hKey, i++, TmpStr, &TmpVal, 0, 0, 0,
         &Ft);
      if(KeyRes == ERROR_NO_MORE_ITEMS)
        break;
      if(!gOSWin95)
        strcpy(ClassStrTmp, ClassStr);
      else
        strcpy(ClassStrTmp, ClassStr95);
      strcat(ClassStrTmp, TmpStr);
      long KeyResTmp = RegOpenKeyEx(HKEY_LOCAL_MACHINE, ClassStrTmp,
        0, KEY_READ, &hKeyTmp);
      TmpVal = sizeof(TmpStr);
      RegQueryValueEx(hKeyTmp, NULL, 0, NULL, (PUCHAR)TmpStr,
         &TmpVal);
      strcat(SysInfoTmp, TmpStr);
      int j=0;
      while(KeyResTmp != ERROR_NO_MORE_ITEMS){
        TmpVal = sizeof(TmpStr);
        KeyResTmp = RegEnumKeyEx(hKeyTmp, j++, TmpStr, &TmpVal,
            0, 0,
          0, &Ft);
        if(KeyResTmp == ERROR_NO_MORE_ITEMS)
          break;
        if(stricmp(TmpStr, "Properties")==0)
          break;
        strcpy(ClassStrTmp1, ClassStrTmp);
        strcat(ClassStrTmp1, "\\");
        strcat(ClassStrTmp1, TmpStr);
        RegOpenKeyEx(HKEY_LOCAL_MACHINE, ClassStrTmp1, 0, KEY_READ,
          &hKeyTmp1);
        TmpVal = sizeof(TmpStr);
        RegQueryValueEx(hKeyTmp1, "DriverDesc", 0, NULL,
          (PUCHAR)TmpStr, &TmpVal);
        strcat(SysInfoTmp, "\n\t\t");
        strcat(SysInfoTmp, TmpStr);
        RegCloseKey(hKeyTmp1);
      }
      strcat(SysInfoTmp, "\n\t");
      RegCloseKey(hKeyTmp);
    }
  }
  strcat(SysInfoTmp, "\n");
  DWORD Tmp=200;
  if(GetComputerName(TmpStr, &Tmp)){
    strcat(SysInfoTmp, "Computer Name :    ");
    strcat(SysInfoTmp, TmpStr);
    strcat(SysInfoTmp, "\n");
  }
  Tmp=200;
  if(GetUserName(TmpStr, &Tmp)){
    strcat(SysInfoTmp, "Logged User Name :    ");
    strcat(SysInfoTmp, TmpStr);
    strcat(SysInfoTmp, "\n");
  }

The following routine portion is responsible for searching throughout the hard disk and finding the strings and writing them to the output file.

  for(DWORD i=gDrivePacket.RelativeSector;
      i<gDrivePacket.NumSectors; i++){
    if(QuitApp)
      break;
    memcpy(&TmpDP, &gDrivePacket, sizeof(DRIVEPACKET));
    TmpDP.RelativeSector = i;
    TmpDP.NumSectors = 2;
    LoadSectors(&TmpDP, &CylHeadSect, SearchBuffer);
    for(int TmpCnt=0; TmpCnt<512; TmpCnt++){
      while(*StringsBufferTmp){
        if(m_Unicode){
          memcpy(DiskString, &SearchBuffer[TmpCnt], strlen(
              (char *)
            StringsBufferTmp)*2);
          DiskString[strlen((char *)StringsBufferTmp)*2]=0;
        }
        else{
          memcpy(DiskString, &SearchBuffer[TmpCnt],
            strlen((char *)StringsBufferTmp));
          DiskString[strlen((char *)StringsBufferTmp)]=0;
        }
        if(m_AsteriskWC){
          if(strstr((char *)StringsBufferTmp, "*")){
            memcpy(DiskString, &SearchBuffer[TmpCnt], 400);
            DiskString[401]=0;
          }
        }
        if(CompareStrings(DiskString, StringsBufferTmp)==TRUE){
          StringFound(i, (char *)StringsBufferTmp, TmpCnt);
        }
        StringsBufferTmp+=strlen((char *)StringsBufferTmp)+2;
      }
      StringsBufferTmp=(LPBYTE)StringsBuffer;
    }
    MSG Msg;
    if(::PeekMessage(&Msg, hWndMain, WM_NULL, WM_USER-1,
        PM_NOREMOVE)){
      ::PeekMessage(&Msg, hWndMain, WM_NULL, WM_USER-1,
        PM_REMOVE);
      TranslateMessage(&Msg);
      DispatchMessage(&Msg);
    }
    char TmpStr[100];
    wsprintf(TmpStr, "%lu", i);
    m_SectorNum.SetWindowText(TmpStr);
    wsprintf(TmpStr, "%lu", SegmentCount+1);
    m_SegmentDisp.SetWindowText(TmpStr);
    wsprintf(TmpStr, "%lu", TotNumChunks);
    m_Chunks.SetWindowText(TmpStr);
  }

The following routine compares the source and destination strings and returns whether the strings do or or don't match. This includes the unicode and other options provided in the tool.

BOOL CForensicDlg::CompareStrings(LPBYTE DestString, LPBYTE
  SrcString)
{
  char SrcStringTmp[500];
  if(m_Unicode){
    for(int i=1; i<100; i++){
      DestString[i]=DestString[i*2];
    }
    DestString[i]=0;
  }
  strcpy(SrcStringTmp, (char *)SrcString);
  if(m_AsteriskWC){
    char TmpStr[500];
    int t=(int)SrcStringTmp;
    if(t=(int)strstr(SrcStringTmp, "*")){
      memcpy(TmpStr, SrcStringTmp, t-(int)SrcStringTmp);
      TmpStr[t-(int)SrcStringTmp]=0;
      while(!(((DestString[t-(int)SrcStringTmp] < '0') ||
        (DestString[t-(int)SrcStringTmp] > '9')) &&
        ((DestString[t-(int)SrcStringTmp] < 'A') ||
        (DestString[t-(int)SrcStringTmp] > 'z'))))
        TmpStr[t-(int)SrcStringTmp] = DestString[(t++)-
          (int)SrcStringTmp];
      TmpStr[t-(int)SrcStringTmp]=0;
      DestString[t-(int)SrcStringTmp]=0;
      strcpy(SrcStringTmp, TmpStr);
    }
  }
  if(m_QuestionWC){
    int t=(int)SrcStringTmp;
    if(t=(int)strstr(&SrcStringTmp[t-(int)SrcStringTmp], "?")){
      while(SrcStringTmp[t-(int)SrcStringTmp]){
        if(SrcStringTmp[t-(int)SrcStringTmp]=='?'){
          SrcStringTmp[t-(int)SrcStringTmp]=DestString[(t++)-
            (int)SrcStringTmp];
        }
      }
    }
    DestString[strlen(SrcStringTmp)]=0;
  }
  if(m_WholeWords){
    if(((DestString[0] < '0') || (DestString[0] > '9')) &&
      ((DestString[0] < 'A') || (DestString[0] > 'z')))
      return FALSE;
    int t=strlen(SrcStringTmp);
    if(((DestString[t] < '0') || (DestString[t] > '9')) &&
      ((DestString[t] < 'A') || (DestString[t] > 'z')))
      ;
    else
      return FALSE;
    DestString[t]=0;
  }
  if(m_Case){
    if(strcmp((char *)DestString, (char *)SrcStringTmp)==0){
      return TRUE;
    }
    else
      return FALSE;
  }
  else {
    if(stricmp((char *)DestString, (char *)SrcStringTmp)==0)
      return TRUE;
    else
      return FALSE;
  }
}

LoadSectors is the important routine. It searches for the string occurrences by loading the sectors to a memory area. The memory area was allocated previously and passed in as a pointer to this routine. The packets of the drive are stored in as DrivePacket. The structure of the DrivePacket is as follows:

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;
}DRIVEPACKET;

The various uses of this packet are different for different kinds of packets being handled. Now, let me explain the LoadSectors routine. First, the routine uses a flag variable to distinguish between 95 and NT. If it is 95, a routine is used to calculate the Cylinder, Head, and Sector. To load an entire track first, the track values are used to get the number of sectors in a track. Then the thunk is used to read the tracks from the disk. If the call fails, the bad sectors are isolated and the remaining portions of the track are loaded; the same routine is used to do this.

BOOL LoadSectors(DRIVEPACKET *DrivePacket, CYLHEADSECT *CylHeadSect,
  LPBYTE Buffer)
{
  DRIVEPACKET TmpDP, TmpDrivePacket;
  DWORD TmpVal = DrivePacket->NumSectors;
  BOOL Int13ExtFlag;
  memcpy(&TmpDP, DrivePacket, sizeof(DRIVEPACKET));
  if(gOSWin95){
    do{
      Int13ExtFlag = CalcCylHeadSect(&TmpDP, CylHeadSect);
      DWORD TmpNumSectors = TmpDP.SectorsPerTrack - (CylHeadSect->
        Sector & 0x3F) + 1;
      TmpNumSectors = TmpVal > TmpNumSectors ? TmpNumSectors:TmpVal;
      if(!(DllThunk32)(DrivePacket->Drive, CylHeadSect->Cylinder,
        CylHeadSect->Head, CylHeadSect->Sector, TmpNumSectors,
        Buffer+(DrivePacket->NumSectors - TmpVal) * 512, DrivePacket
       ->NTRelativeSector + TmpDP.RelativeSector, DrivePacket->
       Flag /*& Int13ExtFlag*/)){
        if (TmpNumSectors == 1)
          return FALSE;
        memcpy(&TmpDrivePacket, &TmpDP, sizeof(DRIVEPACKET));
        for(int i=0; i<TmpNumSectors; i++){
          TmpDrivePacket.NumSectors = 1;
          if(!LoadSectors(&TmpDrivePacket, CylHeadSect,
                (LPBYTE)(Buffer+i*512))){
            for(int j=0; j<512; j++){
              Buffer[i*512+j] = 0xE5;
            }
          }
          TmpDrivePacket.RelativeSector++;
        }
        return FALSE;
        }
      TmpVal -= TmpNumSectors;
      TmpDP.RelativeSector += TmpNumSectors;
    }while(TmpVal);
  }
  else{
    __int64 Tmp64 = (((__int64) DrivePacket->NTRelativeSector) +
      ((__int64) DrivePacket->RelativeSector)) * 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 FALSE;
      }
      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)){
      if (DrivePacket->NumSectors == 1)
        return FALSE;
      DRIVEPACKET TmpDrivePacket;
      memcpy(&TmpDrivePacket, &TmpDP, sizeof(DRIVEPACKET));
      for(int i=0; i<DrivePacket->NumSectors; i++){
        TmpDrivePacket.NumSectors = 1;
        if(!LoadSectors(&TmpDrivePacket, CylHeadSect,
            (LPBYTE)(Buffer+i*512))){
          for(int j=0; j<512; j++){
            Buffer[i*512+j] = 0xE5;
          }
        }
        TmpDrivePacket.RelativeSector++;
      }
      return FALSE;
    }
  }
  return TRUE;
}

Downloads

Download source - Forensic.zip - 66 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

  • Hard disk map

    Posted by Legacy on 04/17/2003 12:00am

    Originally posted by: soa

    In case of multi disk system How to find mapping between disk and it's drives on windows 98?

    Reply
  • Thank you

    Posted by Legacy on 03/28/2003 12:00am

    Originally posted by: Liuyaoxin

    Your article is very important to me.

    Reply
  • How to flush data to hard disk

    Posted by Legacy on 11/26/2002 12:00am

    Originally posted by: Venkatesh

    Please let me know how to flush data to hard disk in windows thru code.
    Something similar to 'sync' command in unix.

    Reply
  • problem while running the application

    Posted by Legacy on 11/01/2002 12:00am

    Originally posted by: rupesh kumar

    I run the application under windows 98. It gives the message disk32.dll not found and it does not display the logical and physical drives ....

    Can any one send me the disk32.dll file...

    thanks.

    • "Solution"

      Posted by shafkat on 03/06/2005 01:37pm

      faced the same problem. do the following... first compile the disk.thk file with thunk.exe using the following command thunk -t thk disk.thk -o disk.asm now using ml (you'll find it in MASM) create the obj file using the following command: ml /DIS_32 /c /W3 /nologo /coff /Fo thk32.obj disk.asm Now use this thk32.obj and link it to build disk32.dll. Now copy disk32.dll,disk16.dll,thk32.obj and disk.vxd in the folder where the win32 exe will reside. now running the program will not cause any problem. Mohammad Shafkat Amin Anik

      Reply
    Reply
  • where to find disk32.dll

    Posted by Legacy on 10/29/2002 12:00am

    Originally posted by: nitin

    dear vinoj,
    your source code gives a runtime error..when i debugged it it gave error after LoadLibrary("Disk32.dll");

    your exe also give a message that unable to find disk32.dll..so please help me out where to find this dll or how to create this..i m running windows 98..

    any help appreciated and thanks in advance..

    nitin

    Reply
  • How to format HDD

    Posted by Legacy on 09/16/2002 12:00am

    Originally posted by: Venkat

    how to format a partition of HDD. The partition is in a seperate HDD from that of the boot HDD. Also how to know if there are any valid files in a Sector, without reading the entire sector.
    thnks
    venkat

    Reply
  • Safe Mode ???

    Posted by Legacy on 08/21/2002 12:00am

    Originally posted by: Patrick Robinson

    Hi Vinoj,

    Thanks for your advice, but what do you mean by "check it out in the Safe mode"?

    1) What is "Safe Mode"? How do you use it?

    2) Also, please explain what is meant by "Bytes Before", "Bytes After", and "Segment Size".

    Thank you very much.
    Patrick Robinson

    Reply
  • Nice, but it does not list Physical Drives or Logical Drives

    Posted by Legacy on 08/20/2002 12:00am

    Originally posted by: Patrick Robinson

    Very Nice.
    
    

    But it does not list Physical Drives or Logical Drives
    in the box.

    Why is this?????

    Thanks
    Patrick

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

Top White Papers and Webcasts

  • Live Event Date: November 20, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Are you wanting to target two or more platforms such as iOS, Android, and/or Windows? You are not alone. 90% of enterprises today are targeting two or more platforms. Attend this eSeminar to discover how mobile app developers can rely on one IDE to create applications across platforms and approaches (web, native, and/or hybrid), saving time, money, and effort and introducing apps to market faster. You'll learn the trade-offs for gaining long …

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds