首页
社区
课程
招聘
[原创]读写U盘扇区
发表于: 2012-7-23 18:14 16332

[原创]读写U盘扇区

2012-7-23 18:14
16332
最近有个朋友想读写U盘扇区的内容,让我帮忙,本来以为挺简单的一件事情,用SetFilePointer API 函数来定位,在用ReadFile WriteFile 直接进行操作就可以了,在网上google了一下代码一大堆,拿过来直接用,的确好用,但是是在XP 环境下,到VISTA,windows 7环境下,即使用管理员权限来进行操作也不好用了,经过查资料才知道,自从VISTA以后对硬盘的读写都进行了保护,在也不能像XP 下那样直接读写了,被逼无奈,自己做了个小小的驱动,经过多次蓝屏,最后算是调稳定了,共享出来,如果能帮的到大家,也算没有白辛苦,同时写出来,也算对自己的一个总结。
本程序的基本思路是:
1.通过磁盘名字获得磁盘驱动的指针。
2.根据磁盘驱动的指针枚举此驱动的所有设备指针。
3.根据枚举出来的各个参数来进行比较是否是自己所要找的设备。
4.找到自己进行操作的设备,保存设备指针,以便后面进行操作。
5.因为此处操作的是U盘,U盘的ID 在进行拔插的时候ID 会变化。还有一个系统中存在多个U盘的可能性,所以在此处用了通过盘符获得U盘的硬盘的ID 逻辑号,把这个号传道驱动中进行区分是否是我们所要进行操作的设备。

要是高手的话,有了基本思路,难度就不大了,可以直接飘过

下面简单的写下过程:
1.找个简单的NT 式驱动来改,先设置驱动的派遣函数,创建驱动设备。(这里我是根据张帆的书上的helloddk来改的 驱动的基本知识)
2.通过内核函数 ObReferenceObjectByName 根据驱动的名字获得设备的对象指针,然后根据设备指针来得到来进行遍历来找到我们需要操作的设备,这里我们操作的是U盘 应从设备 的 DeviceType== FILE_DEVICE_DISK && (Flags &DO_DEVICE_HAS_NAME)&&(Characteristics&FILE_REMOVABLE_MEDIA 几方面来进行考虑 一是 磁盘设备类型 标志位是有名字的 是可移除的设备类型,找到我们想要进行操作的设备。代码如下:

 
#pragma PAGEDCODE
NTSTATUS GetUdiskDeviceObj( IN DWORD DiskID)
{ 
 NTSTATUS status = STATUS_SUCCESS;        //返回状态
 UNICODE_STRING DestinationString;        //硬盘驱动名称
 PDRIVER_OBJECT pDiskObject;         //驱动对象(这里是找到设备对象的接口)
 PDEVICE_OBJECT pDeviceObjectTemp;                              //磁盘设备的临时对象
// DWORD   dwDeviceNumber;                                 //磁盘下挂载设备的数目
 PDISK_GEOMETRY pDiskGeometry;                                  //用于装载磁盘信息的数据结构的一个对象(用来获得磁盘的扇区字节数)
// PSTORAGE_DEVICE_DESCRIPTOR pUdiskDescriptor;                    //磁盘设备的描述符根据这些来得到PID VID 来判断是不是我们要操作的设备
    DWORD   dwRetLength;                                    //设备名的长度
 POBJECT_NAME_INFORMATION pNameBuffer;                           //存放设备名字的缓冲区
    WCHAR   *pNameTemp;                                     //设备名单个字符的指针
 BOOLEAN   bIsFound = FALSE;                               //能否找到磁盘信息数据结构的标示
//    DWORD           SectorSize;                                   //每个扇区有多少个字节
//   DWORD i;
                                                                 /* All Disk Objects are created by disk.sys driver*/
 RtlInitUnicodeString(&DestinationString, L"[URL="file://driver//Disk"]\\Driver\\Disk[/URL]");    //字符串初始化
    DbgPrint("Enter  GetUdiskDeviceObj\n");
 
 
 // Not a documented function in DDK, see import definition in Driver.h
 if (ObReferenceObjectByName(
  &DestinationString,
  OBJ_CASE_INSENSITIVE,//64,
  0,
  0,
  *IoDriverObjectType,
  KernelMode,
  0,
  (PVOID*)&pDiskObject) >= 0) 
 {
        DbgPrint("Find drver object\n");
  pDeviceObjectTemp = pDiskObject->DeviceObject;
        //dwDeviceNumber = 0;
  if (pDeviceObjectTemp)                                        //如果不为空 则证明找到设备
        {
                                                                      //申请一块内存用来存放找到的磁盘信息
   pDiskGeometry =(PDISK_GEOMETRY)ExAllocatePool(NonPagedPool, sizeof(DISK_GEOMETRY));
            DbgPrint("Find pdisk\n");
   if (!pDiskGeometry) 
   {
                       return STATUS_INSUFFICIENT_RESOURCES;          //如果不能申请成功 则系统内存资源不足,返回状态标志
       
            }
            do
   {
    memset(pDiskGeometry, 0x00, sizeof(DISK_GEOMETRY));    //格式化申请的内存资源
//    memset(pUdiskDescriptor, 0x00, sizeof(STORAGE_DEVICE_DESCRIPTOR));    //格式化申请的内存资源
                                                           //开始查找我们需要的设备
    //我们查找的设备为U盘 也就是 硬盘设备类型 设备的FLAG 为有名字 设备的属性支持可以移除的
    if (pDeviceObjectTemp->DeviceType== FILE_DEVICE_DISK && (pDeviceObjectTemp->Flags &DO_DEVICE_HAS_NAME)&&(pDeviceObjectTemp->Characteristics&FILE_REMOVABLE_MEDIA))
                {   
     
     DbgPrint("Find my pdisk\n");
     //假如在win7系统下我们已经找到了我们想要的设备了,VISTA下未知没做过测试 但是XP下 分为
     //\\Device\\Harddisk0\\DP(0)和[URL="file://device//Harddisk0//DR0"]\\Device\\Harddisk0\\DR0[/URL] 我们需要找的是后者
     //DO_LOW_PRIORITY_FILESYSTEM  置 1  为[URL="file://device//Harddisk0//DP(0"]\\Device\\Harddisk0\\DP(0[/URL])*** 置0 为  [URL="file://device//Harddisk0//DR0"]\\Device\\Harddisk0\\DR0[/URL]
     //此处寻找的思路是通过 找到的设备对象转化成名字来判断是不是我们所要找的设备
     //先得到设备名的长度
     ObQueryNameString(
      pDeviceObjectTemp,
      NULL, 
      0, 
      &dwRetLength);
     //申请一块内存用来存放设备名
     pNameBuffer = (POBJECT_NAME_INFORMATION)ExAllocatePoolWithTag(PagedPool, dwRetLength, ' sFI');
     if (!pNameBuffer)
     {
      ExFreePool(pDiskGeometry);
      return STATUS_INSUFFICIENT_RESOURCES;
     }
     
     
     //这次真正得到设备名了
     if (ObQueryNameString(
      pDeviceObjectTemp, 
      pNameBuffer,
      dwRetLength, 
      &dwRetLength) == STATUS_SUCCESS && pNameBuffer->Name.Buffer)
     {   //把设备名打印出来 看找到的到底是什么设备
      
      DbgPrint("pNameBuffer->Name.Buffer:%S\n",pNameBuffer->Name.Buffer);
      
      
      //这里寻找的设备名有点技巧
      for (pNameTemp = pNameBuffer->Name.Buffer + wcslen(pNameBuffer->Name.Buffer); pNameTemp > pNameBuffer->Name.Buffer; pNameTemp--) 
      {   
       
       //字符串格式化
       int const arraysize = 30;
       WCHAR pszDest[arraysize]; 
       size_t cbDest = arraysize * sizeof(WCHAR);
       LPCWSTR pszFormat = L"%s%d%s";
       WCHAR* pszTxt = L"[URL="file://harddisk/"]\\Harddisk[/URL]";
                            status = RtlStringCbPrintfW(pszDest, cbDest, pszFormat, pszTxt,DiskID,L"[URL="file://dr/"]\\DR[/URL]");
       //DbgPrint("pNameTemp:%S  pszDest:%S \n",pNameTemp,pszDest);
       //if (!_wcsnicmp(pNameTemp, L"[URL="file://harddisk1//DR"]\\Harddisk1\\DR[/URL]", 13)) 
       if (!_wcsnicmp(pNameTemp, pszDest, 13))
       {
        //如果找到我们需要的的设备名字,做个标记
        bIsFound = TRUE;
        DbgPrint("Enter  here \n");
        break;
       }
       
      }
      if(bIsFound == TRUE)
      {
       status = GetGeometry(pDeviceObjectTemp, pDiskGeometry);
       if (!NT_SUCCESS(status)) 
       {
        //pDisk->bGeometryFound = FALSE;
        DbgPrint("GetGeometry Error\n");
       } 
       //这里得到每个扇区有多少个字节,大部分是512 但是有个别的也说不定
                            //这里我们打印出来看下是多少
       DbgPrint("SectorSize:%X\n",pDiskGeometry->BytesPerSector);
                            G_pDevice=pDeviceObjectTemp;
       DbgPrint("pDeviceObjectTemp:%lX\n",G_pDevice);
      }
      
     }
     
     ExFreePoolWithTag(pNameBuffer, 0); 
    }
                   //DDK 3790.1830 版本有问题,NTDDK.H里面竟然没有ExFreePoolWithTag这个函数,只能在.h文件里面声明个了
    
    pDeviceObjectTemp=pDeviceObjectTemp->NextDevice;
   }while(pDeviceObjectTemp);
           
   ExFreePool(pDiskGeometry);                                //释放申请的内存资源
   //ExFreePool(pUdiskDescriptor);                                //释放申请的内存资源
  }
  
 }
 
 return status;
}


在这里找到我们进行操作的设备指针之后,还要得到磁盘的一些信息,比如一个扇区多少个字节,现在大多数是512的,不过我看定义里面有不同的,不知道用在什么地方。
所以我这里得到磁盘的扇区数,函数如下:

 
#pragma PAGEDCODE
NTSTATUS GetGeometry(PDEVICE_OBJECT pDiskDevObj, PDISK_GEOMETRY pDiskGeo)
/*++
Routine Description:
   Returns the Geometry of Disk
Arguments:
   Target Device Object representing the disk and Pointer to geometry structure
Return Value:
    STATUS
--*/
{
 IO_STATUS_BLOCK  IoStatusBlock;
 KEVENT    Event;
 PIRP    pIrp;
 NTSTATUS   status;
 KeInitializeEvent(&Event, NotificationEvent, FALSE);
 pIrp = IoBuildDeviceIoControlRequest(
  IOCTL_DISK_GET_DRIVE_GEOMETRY,
  pDiskDevObj,
  NULL,
  0, 
  pDiskGeo,
  sizeof(DISK_GEOMETRY),
  FALSE, 
  &Event,
  &IoStatusBlock);
 
 if (!pIrp) 
 {
  return STATUS_INSUFFICIENT_RESOURCES;
 }
 status = IoCallDriver(pDiskDevObj, pIrp);
 if (status == STATUS_PENDING) 
 {
   KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
   status = IoStatusBlock.Status;
 }
 return status;
}


这样U盘的一些信息得到了,下面就是创建读写U盘的IRP,然后发送给磁盘的驱动,就可以进行磁盘的读写了

 
#pragma PAGEDCODE
NTSTATUS ReadAndWriteSectors(PDEVICE_OBJECT pDevObj,LARGE_INTEGER lDiskOffset,PVOID pbuf,BOOLEAN iSreadorWrite)
{
 NTSTATUS status=STATUS_SUCCESS;
// CHAR    *sBuf;
 KEVENT    Event;
    SIZE_T    size = 512;                             //每次读写的大小
 PIRP    pIrp = NULL;       //创建读写的IRP
 IO_STATUS_BLOCK  ioStatus;  
// DWORD               i;
 
 KeInitializeEvent(&Event, NotificationEvent, FALSE);        //创建一个通知事件来进行U盘扇区读写完成
// DbgPrint("ReadAndWriteSectors pDevObj :%lX\n",pDevObj);
// DbgPrint("ReadAndWriteSectors QuaPart :%lX \n",lDiskOffset->QuadPart);
 DbgPrint("ReadAndWriteSectors QuaPart:%X\n",lDiskOffset.QuadPart);
 lDiskOffset.QuadPart=lDiskOffset.QuadPart*512;
// sBuf =(CHAR *) ExAllocatePool(NonPagedPool, size);
// memset(sBuf, '0', size);
 if(iSreadorWrite==TRUE)
 {
  pIrp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, pDevObj, pbuf, size, &lDiskOffset, &Event, &ioStatus);
  DbgPrint("TRUE");
 }
 else
 {
  pIrp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE, pDevObj, pbuf, size, &lDiskOffset, &Event, &ioStatus);
  DbgPrint("FALSE");
 }
 
 
 if (!pIrp) 
 {
  
  //ExFreePool(sBuf);
  KdPrint(("Creat IRP faile\n"));
  return STATUS_INSUFFICIENT_RESOURCES;
 }
 
 status = IoCallDriver(pDevObj, pIrp);
 
 if (status == STATUS_PENDING) 
 {
  KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
  status = ioStatus.Status;
 }
 
/* 
 for (i=0;i<size;i++)
 {
  KdPrint(("%X \n",pbuf[i]&0XFF));
  if ((i % 16) == 15)
  {
   KdPrint(("\n"));
  }
 }
*/ 
// ExFreePool(sBuf);
 
 return status;
}


这样基本功能就实现了,一个简单的驱动就做出来了,然后把参数什么的都填好,把IOCTRL 和应用程序的通信假设好,一个完整的工程就OK了,在写上驱动安装,卸载函数,做成DLL 也可以供其他语言的程序来使用了,这些我都完善好了。但是总是感觉这不是最好的办法。如果大家有别的好的办法,请告知,谢谢,本人QQ:253229030 欢迎一起交流!

[课程]Linux pwn 探索篇!

上传的附件:
收藏
免费 6
支持
分享
最新回复 (10)
雪    币: 2993
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
可以跟楼主套个近乎么。。。貌似跟我暑假作业有点关系,如果有问题希望能请教LZ啊~
2012-7-23 23:21
0
雪    币: 227
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
其实我也遇到这个问题,后来查到不用驱动也能解决这个问题,Win7/Vista下只需要通过DeviceIoControl发送控制码FSCTL_LOCK_VOLUME先把卷锁然后发送FSCTL_DISMOUNT_VOLUME把卷卸掉就能直接通过ReadFile/WriteFile读写扇区了,以下是Delphi中写入分区引导扇区的代码代码

function TMain.WritePbr(hDevice: THandle; PartNum: Byte; PartitionType: Byte;
  LdrName: PChar; const BytesPerSector: DWORD): Boolean;
var
  OrgPbr, G4DPbr: PChar;
  ByteRet, PbrSize, PbrOfs: DWORD;
  Res: TResourceStream;
const
  GPTOffset = $01B8;
begin
  Result := False;
  if not DeviceIoControl(hDevice, FSCTL_LOCK_VOLUME, nil, 0, nil, 0, ByteRet,
    nil) then
    Exit;
  try
    OrgPbr := AllocMem(BytesPerSector);
    try
      SetFilePointer(hDevice, 0, nil, FILE_BEGIN);
      ReadFile(hDevice, OrgPbr[0], BytesPerSector, ByteRet, nil);
    except
      FreeMem(OrgPbr, BytesPerSector);
    end;

    try
      Res := TResourceStream.Create(HInstance, 'GRLDR', RT_RCDATA);
      try
        GetMem(G4DPbr, BytesPerSector * 16);
        Move(Res.Memory^, G4DPbr^, BytesPerSector * 16);
        ReplaceGrldr(G4DPbr, LdrName);
      finally
        Res.Free;
      end;
    except
      Exit;
    end;

    case PartitionType of
      PARTITION_FAT_12, PARTITION_FAT_16:
        begin
          PbrSize := BytesPerSector;
          PbrOfs := 3 * BytesPerSector;
          Move(OrgPbr[0], G4DPbr[PbrOfs], $3E);
          G4DPbr[PbrOfs + $41] := Char(PartNum);
        end;
      PARTITION_FAT32, PARTITION_FAT32_XINT13:
        begin
          PbrSize := BytesPerSector;
          PbrOfs := 2 * BytesPerSector;
          Move(OrgPbr[0], G4DPbr[PbrOfs], $5A);
          G4DPbr[PbrOfs + $5D] := Char(PartNum);
        end;
      PARTITION_IFS:
        begin
          PbrSize := BytesPerSector * 4;
          PbrOfs := 5 * BytesPerSector;
          Move(OrgPbr[0], G4DPbr[PbrOfs], $54);
          G4DPbr[PbrOfs + $57] := Char(PartNum);
        end;
    else
      PrintLog('分区标识 ' + IntToHex(PartitionType, 2) + '不被支持');
      Exit;
    end;

    try
      SetFilePointer(hDevice, 0, nil, FILE_BEGIN);
      if DeviceIoControl(hDevice, FSCTL_DISMOUNT_VOLUME, nil, 0, nil, 0,
        ByteRet, nil) then
        Result := WriteFile(hDevice, G4DPbr[PbrOfs], PbrSize, ByteRet, nil);
    finally
      FreeMem(G4DPbr, BytesPerSector * 16);
      FreeMem(OrgPbr, BytesPerSector);
    end;
  finally
    DeviceIoControl(hDevice, FSCTL_UNLOCK_VOLUME, nil, 0, nil, 0, ByteRet, nil);
  end;
end;
2012-7-24 10:31
0
雪    币: 168
活跃值: (40)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
4
原来如此,谢谢,又学了一种方法!
2012-7-24 11:01
0
雪    币: 227
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
貌似记错了,是需要发送 FSCTL_DISMOUNT_VOLUME 才能写,读是没问题
2012-7-24 11:18
0
雪    币: 13
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
CreateFile不可以???
用这个\\.\PHYSICALDRIVEx 是能打开的读的(能不能写没试)
2012-7-24 11:29
0
雪    币: 351
活跃值: (22)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
如果是系统分区的话 FSCTL_LOCK_VOLUME会失败吧 FSCTL_DISMOUNT_VOLUME也不好用...
2012-7-24 11:47
0
雪    币: 227
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
nt6下的NTFS如果直接CreateFile只能读写前64个扇区,后面的扇区貌似只能读不能写
2012-7-24 11:59
0
雪    币: 227
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
这里讨论的是U盘扇区的读写,即便是通过U盘引导启动的NT6 PE,也是映射到内存中的,不会占用U盘所在分区
2012-7-24 12:00
0
雪    币: 351
活跃值: (22)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
噢 u盘的话 可以
2012-7-24 13:47
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
[代码没显示呀
2012-7-24 14:54
0
游客
登录 | 注册 方可回帖
返回
//