首页
社区
课程
招聘
[原创]猪头三Winapiex DISK接口分析
发表于: 2008-7-21 21:09 10603

[原创]猪头三Winapiex DISK接口分析

2008-7-21 21:09
10603

【文章标题】: 猪头三Winapiex DISK接口分析
【文章作者】: nevergone
【作者邮箱】: wangyongxina2004@163.com
【下载地址】: 自己搜索下载
【编写语言】: VC8
【使用工具】: IDA VC8
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
  猪头三大哥的winapiex项目中,整合了一系列的接口,包含磁盘操作类 文件操作类 进程操作类等
  我对磁盘操作类比较感兴趣,因此下载了DLL文件,并逆向了磁盘操作类的全部接口
  E_Disk_IsDynamicDisk                                [作者: 猪头三 性质: 闭源 修改时间: 2007-06-18 功能: 判断物理磁盘是否是动态磁盘]
  E_Disk_ReadData                                                [作者: 猪头三 性质: 授权 修改时间: 2007-06-18 功能: 读取物理磁盘数据]               

               
  E_Disk_WriteData                                        [作者: 猪头三 性质: 授权 修改时间: 2007-06-18 功能: 写入物理磁盘数据]                       

       
  E_Disk_GetDiskGeometry                                [作者: 猪头三 性质: 授权 修改时间: 2007-06-18 功能: 获取物理磁盘基本信息参数]               

                               
  E_Disk_GetPartitionType                                [作者: 猪头三 性质: 授权 修改时间: 2007-06-18 功能: 获取指定分区类型]
  E_Disk_GetDiskNumberByFilePath                [作者: 猪头三 性质: 授权 修改时间: 2007-06-19 功能: 获取指定分区所在的磁盘]
  E_Disk_GetPartitionInfoByFilePath        [作者: 猪头三 性质: 授权 修改时间: 2007-06-19 功能: 获取指定分区基本信息(分区类型 分区大小 分区所在磁

盘起始位置)]
  E_Disk_GetPartitionSpace                        [作者: 猪头三 性质: 授权 修改时间: 2007-06-19 功能: 获取指定分区实际空闲容量和实际总容量]
  E_Disk_GetDiskNumber                                [作者: 猪头三 性质: 授权 修改时间: 2007-06-21 功能: 获取物理磁盘数量]
  E_Disk_FormatPartition                                [作者: 猪头三 性质: 授权 修改时间: 2007-06-22 功能: 格式化指定分区]
  下面我将介绍其中的几个接口
  我逆向的第一个接口是:E_Disk_FormatPartition
  按猪头三提供的文档,此函数用于格式化指定分区.
  函数内部调用SHFormatDrive来执行格式化
  值得注意的是这个函数存在一个BUG, 看IDA
  ; int __cdecl E_Disk_FormatPartition(const char *pDriverName)
  .text:100013E0                 public E_Disk_FormatPartition
  .text:100013E0 E_Disk_FormatPartition proc near
  .text:100013E0
  .text:100013E0 pDriverName     = dword ptr  4
  .text:100013E0
  .text:100013E0                 mov     eax, [esp+pDriverName]
  .text:100013E4                 mov     dl, [eax]
  .text:100013E6                 xor     ecx, ecx
  .text:100013E8                 mov     eax, A          ; 'A'
  .text:100013ED                 movsx   edx, dl         ; Get pDriverName[0]
  .text:100013ED
  .text:100013F0
  .text:100013F0 begin_loop:                             ; CODE XREF: E_Disk_FormatPartition+1Dj
  .text:100013F0                 cmp     edx, eax
  .text:100013F2                 jz      short Do_Format ; 循环,用于取得SHFormatDrive的第二个参数
  .text:100013F2
  .text:100013F4                 add     eax, 1
  .text:100013F7                 add     ecx, 1
  .text:100013FA                 cmp     eax, Z          ; 'Z'
  .text:100013FD                 jbe     short begin_loop;比'Z'小时,跳转,当比'Z'大于循环不满足,执行Do_Format.
  .text:100013FD
  .text:100013FF
  .text:100013FF Do_Format:                              ; CODE XREF: E_Disk_FormatPartition+12j
  .text:100013FF                 push    SHFMT_OPT_FULL  ; options
  .text:10001401                 push    SHFMT_ID_DEFAULT ; fmtID
  .text:10001406                 push    ecx             ; drive
  .text:10001407                 call    ds:GetActiveWindow
  .text:10001407
  .text:1000140D                 push    eax             ; hwnd
  .text:1000140E                 call    ds:SHFormatDrive
  .text:1000140E
  .text:10001414                 retn
  当用户构造一个特殊的字符串pDriverName[0] > 'Z'时,会把用户的Z盘直接格式化,应该在函数入口检测用户输入参数
  是否合法.
  disk接口中比较有用的两个接口是E_Disk_ReadData 和 E_Disk_WriteData.
  这两个接口按猪头三提供的文档:读取物理磁盘数据和写入物理磁盘数据.
  下面的代码是逆向后的
  bool __cdecl MyReadData(IN unsigned long ulong_param_DiskNum,
                                                  IN const char *pchar_param_DriverName,
                                                  IN void *pvoid_param_DataBuffer,
                                                  IN unsigned long ulong_param_DataLength,
                                                  IN LONGLONG llg_param_ByteOffset)
  {
          char szBuffer[0x20] = { 0 };
          ULONG ulDiskNumber = 0;
  
          if (lstrlen(pchar_param_DriverName) > 1)
                  ulDiskNumber = MyGetDiskNumberByFilePath(pchar_param_DriverName);
          else
                  ulDiskNumber = ulong_param_DiskNum;
  
          wsprintf(szBuffer, "\\\\?\\PhysicalDrive%ld", ulDiskNumber);
         
          HANDLE hFile = CreateFile(szBuffer,
                                                                  GENERIC_READ,
                                                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
                                                                  NULL,
                                                                  OPEN_EXISTING,
                                                                  FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING,
                                                                  NULL);
  
          if (hFile == INVALID_HANDLE_VALUE)
          {
                  MessageBox(NULL, "E_ReadPhysicalDriveData()->OpenPhysicalDrive is failed !", "", MB_OK);
                  return false;
          }
  
          BOOL fOk = ReadFileInternal(hFile,
                                                                  pvoid_param_DataBuffer,
                                                                  ulong_param_DataLength,
                                                                  (LONG)llg_param_ByteOffset,
                                                                  *(PLONG)((PBYTE)(&llg_param_ByteOffset) + 4));
  
          if (!fOk)
          {
                  MessageBox(NULL, "E_ReadPhysicalDriveData()->ReadPhysicalDriveData is failed !", "", MB_OK);
          }
  
          CloseHandle(hFile);
          return (bool)fOk;
  }
  首先通过参数ulong_param_DiskNum 或 pchar_param_DriverName取得欲写入的硬盘编号
  在文档中:pchar_param_DriverName注明是必须是0,但从IDA分析中,如果pchar_param_DriverName不为0,则
  通过一个内部函数来取得硬盘编号,在我的CPP文件中,我用MyGetDiskNumberByFilePath(pchar_param_DriverName);
  取得了硬盘编号后,构造一个\\?\PhysicalDrive%ld的字符串,调用CreateFile 打开这个对象,最后再调用内部未导出的
  函数来执行读取操作,这个函数在我的CPP文件中我命名为ReadFileInternal.
  int __fastcall ReadFileInternal(HANDLE hFile,
                                                                  PVOID pvBuffer,
                                                                  DWORD long_param_nNumberOfBytesToRead,
                                                                  LONG long_param_lDistanceToMove,
                                                                  LONG long_param_lDistanceToMoveHigh)
  {
          DWORD dwNumberBytesRead = 0;
          LONG lDistanceToMove;
          LONG lDistanceToMoveHigh;
          char szBuffer[MAX_PATH];
  
          ZeroMemory(szBuffer, MAX_PATH * sizeof(char));
          lDistanceToMoveHigh = long_param_lDistanceToMoveHigh;
          lDistanceToMove = long_param_lDistanceToMove;
          DWORD dwPtrLow = SetFilePointer(hFile,
                                                                          lDistanceToMove,
                                                                          &lDistanceToMoveHigh,
                                                                          FILE_BEGIN);
  
          if (dwPtrLow == INVALID_SET_FILE_POINTER)
          {
                  ReportError();
                  return 0;
          }
  
          BOOL fOk = ReadFile(hFile,
                                                  pvBuffer,
                                                  long_param_nNumberOfBytesToRead,
                                                  &dwNumberBytesRead,
                                                  NULL);
  
          if (!fOk)
          {
                  wsprintf(szBuffer, "Read PhysicalDriveData Error %ld !", GetLastError());
                  MessageBox(NULL, szBuffer, "", MB_OK);
          }
          return fOk;
  }
  先调用SetFilePointer使得文件指针指向用户要求的区域,然后再调用ReadFile来执行操作.
  原来挺简单的
  
  E_Disk_WriteData的流程和E_Disk_ReadDisk差不多,请参看源码.
  这两个函数可以用来读取硬盘的MBR.
  PVOID        pvBuffer1 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 512);
  E_Disk_ReadData(0, NULL, pvBuffer1, 512, 0);
  这样就取得了MBR
  写入MBR的代码有点危险....嘿嘿...
  
  接下来是用来判断物理磁盘是否是动态磁盘的接口
  
  unsigned int MyIsDynamicDisk(IN const char *pchar_param_FilePath)
  {
          DWORD dwBytesReturned = 0;
          char szBuffer[0x20] = { 0 };
          UINT uiResult = 1;
  
          ULONG ulDiskNumber = MyGetDiskNumberByFilePath(pchar_param_FilePath);
  
          if (ulDiskNumber == 0xFF)
          {
                  MessageBox(NULL, "IsDynamicDisk()->Get disk number is failed !", "", MB_OK);
                  return 0;
          }
  
          wsprintf(szBuffer, "\\\\?\\PhysicalDrive%ld", ulDiskNumber);
  
          HANDLE hFile = CreateFile(szBuffer,
                                                                  GENERIC_READ,
                                                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
                                                                  NULL,
                                                                  OPEN_EXISTING,
                                                                  FILE_FLAG_WRITE_THROUGH |  FILE_FLAG_NO_BUFFERING,
                                                                  NULL);
  
          if (hFile == INVALID_HANDLE_VALUE)
          {
                  MessageBox(NULL, "IsDynamicDisk()->OpenPhysicalDrive() is failed !", "", 0);
                  return 0;
          }
  
          DeviceIoControl(hFile,
                                          FSCTL_LOCK_VOLUME,
                                          NULL,
                                          0,
                                          NULL,
                                          0,
                                          &dwBytesReturned,
                                          NULL);
  
          LPVOID pvBuffer = malloc(0x200);
          ZeroMemory(pvBuffer, 0x200);
  
          BOOL fOk = ReadFileInternal(hFile, pvBuffer, 0x200, 0, 0);
         
          if (!fOk)
          {
                  MessageBox(NULL, "IsDynamicDisk()->ReadPhysicalDriveData() is failed !", "", MB_OK);
                  uiResult = 0;
                  goto __Exit;
          }
  
          DWORD dwUnknown = *(LPDWORD)((LPBYTE)pvBuffer + 0x1c2);
  
  #if _DEBUG         // just for test
          BYTE        aByte;
          TCHAR        Buffer[20];
          aByte = *static_cast<LPBYTE>((LPBYTE)pvBuffer + 0x1c2);
          wsprintf(Buffer, "%x", aByte);
          OutputDebugStringA(Buffer);
  #endif
         
          if (dwUnknown == 0x42)
          {
                  MessageBox(NULL, "This disk is Dynamic Disk !\n", "", MB_OK);
                  uiResult = 3;
                  goto __Exit;
          }
  
  __Exit:
          DeviceIoControl(hFile,
                                          FSCTL_UNLOCK_VOLUME,
                                          NULL,
                                          0,
                                          NULL,       
                                          0,
                                          &dwBytesReturned,
                                          NULL);
  
          CloseHandle(hFile);
          free(pvBuffer);
          return uiResult;
  }
  
  这个函数是整个DISK接口中,代码最长的接口
  先根据pchar_param_FilePath取得硬盘编号.DLL通过一个未导出函数来实现,在我的CPP文件中为
  MyGetDiskNumberByFilePath

  /*        #define IOCTL_DISK_UNKNOWN 0x560000*/
  
          DWORD        dwBytesReturned = 0;
          /*char        szOutBuffer[0x20] = { 0 };*/
          VOLUME_DISK_EXTENTS disk_extents;
  
          char        szDest[0x20] = { 0 };
  
          wsprintfA(szDest, "\\\\.\\%c:", pchar_param_FilePath[0]);
          HANDLE hFile = CreateFile(szDest,
                                                                  GENERIC_READ,
                                                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
                                                                  NULL,
                                                                  OPEN_EXISTING,
                                                                  0,
                                                                  NULL);
          if (hFile == INVALID_HANDLE_VALUE)
          {
                  ReportError();
                  return 0xFF;
          }
         
          BOOL fOk = DeviceIoControl(hFile, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, &disk_extents, sizeof(disk_extents), &dwBytesReturned,

NULL);
  
          CloseHandle(hFile);
  
          return fOk ? disk_extents.Extents[0].DiskNumber : 0xFF;
  
  一开始我不知道DeviceIoControl函数参数dwIoControlCode==560000h时,是哪个IOCTL
  只能硬编码.后来通过查找MSDN,找到是IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS这个IOCTL,一下解决这个问题.
  取得了硬盘编号后,调用CreateFile来创建对象.然后调用DeviceIoControl以FSCTL_LOCK_VOLUME锁住.
  申请内存调用ReadFileInternal来读取头200h字节的内容,取偏移为0x1c2处的双字,如果此值为0x42,则此磁盘是动态
  磁盘.我猜想200h可能是一个结构,0x1c2是结构中的一个偏移.
  其他接口相对简单,不再多说,请参看源码.
  我的工程中包含了测试代码.
  
  
--------------------------------------------------------------------------------
【经验总结】
  引用一句牛人xIkug的话:代码逆向即是在没有源代码的情况下,对目标程序的行为、数据流、及编译器生成的代码进行分析,通
  过分析我们可以了解、发现程序的功能、流程、规则、及技术实现细节等,通过分析我们能对其进行优化、功能增强、漏洞
  填补、甚至还原成源代码等。这个分析过程我们可以称作逆向分析或逆向工程,简称逆向。
  偶觉得很中肯.
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2008年07月21日 20:53:59


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 7
支持
分享
最新回复 (12)
雪    币: 116
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
沙发?。。。。。。。。。。
2008-7-21 21:20
0
雪    币: 371
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hyp
3
不错啊 楼主加油啊
2008-7-22 08:19
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
不错,支持!我们需要原创的作品
2008-7-22 09:25
0
雪    币: 709
活跃值: (2420)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
5
学习,顶牛人啊~~~
2008-7-22 09:44
0
雪    币: 107
活跃值: (404)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
不错。。。。学习了。。。不过开头代码有点乱看了有点晕
2008-7-22 14:52
0
雪    币: 381
活跃值: (140)
能力值: ( LV13,RANK:330 )
在线值:
发帖
回帖
粉丝
7
感觉很不错,看看
2008-7-22 19:57
0
雪    币: 1852
活跃值: (504)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
8
支 持 下 !
2008-7-23 13:10
0
雪    币: 372
活跃值: (31)
能力值: ( LV12,RANK:410 )
在线值:
发帖
回帖
粉丝
9
学习,牛人啊~~~
2008-7-23 15:26
0
雪    币: 1312
活跃值: (5164)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10
学习啊~~~
2008-7-23 16:41
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
好文   学习。。~
2008-7-25 17:03
0
雪    币: 111
活跃值: (626)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
好文章, 收藏之 :D
2008-7-25 17:51
0
雪    币: 181
活跃值: (134)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
13
顶一下猪三哥的东西。还有隐去了兄台
2008-7-27 14:38
0
游客
登录 | 注册 方可回帖
返回
//