【前期信息】
山东某公司,企业规模也不算小,但不是IT公司,因此未作很好的数据存储规划。公司的重要数据经过WINDOWS网络共享放在一台PC上,同时也链接打印机,有不少人员直接拷贝数据文件到这台PC上打印。ios
前几天,突然F盘的全部文件均没法打开,表现为:算法
一、文件名称,时间,路径彻底正确,磁盘占用空间也正确。windows
二、全部的jpg图片文件打开都提示:“windows照片查看器没法打开此图片,由于照片查看器不支持此文件格式,或者您没有照片查看器的最新更新”安全
三、全部的doc打开时都提示:"请选择使文档可读的编码",选择任何一个编码后文件都是错误的。网络
四、全部的docx打开时都提示:"没法打开文件,由于内容有错误"ide
五、全部的xls打开时都提示:“您尝试打开的文件的格式与文件扩展名指定的格式不一致,打开文件前请验证文件没有损坏且来源可信”ui
六、全部的xlsx打开时都提示:"您没法打开文件,由于文件格式或文件扩展名无效,请肯定文件未损坏,而且文件扩展名与文件的格式匹配"编码
七、全部的PDF文档打开时均提示:“打开文档时发生错误,文档已损坏且没法修复”加密
八、其余全部类型文件均没法正常打开。(键入上述错误提示的目的在于网友能够根据错误提示搜索到本文,没有稿费,不凑字数) spa
用户首先经过网上下载的一些数据恢复软件进行恢复,但没任何结果。以后前后送修给当地几家数据恢复,均告之没法恢复,或须要送至北京处理,用户一想,反正是要送北京的,数据也很是重要(过后用户告诉咱们,这些数据文件若是恢复不了,损失至少在几十万元),因而,就向北京的数据恢复公司进行咨询。
咱们的分析和初检结论大体是这样的:
一、肯定存储是否RAID。获得的结论是只有一块500G单盘,排除可能的RAID旧盘同步后出现这种故障的可能。
二、肯定硬盘是否有物理故障,好比是否有异响,是否访问缓慢,是否提示IO错误等。获得的结论是除了F盘,其余分区数据彻底正常,硬盘在其余数据恢复公司检测也无物理故障。排除因物理故障(如硬盘固件缺陷表错误,坏道等)致使的相似故障。
三、肯定是否采用加密。获得的结论是无启用过任何加密。排除一些加密系统丢失加密链后直接访问密文致使的相似故障。
四、肯定是否采用第三方软件作过度区大小调整、合并。获得的结论是没有。排除因PQ(Norton PartitionMagic)、DiskGenius, Acronis Disk Director Suite等软件调整、合并分区出错致使的相似故障。
五、肯定是否操做系统故障。获得的结论是重装系统杀毒后依然。排除因病毒挟持全部可执行文件或加入文件系统拦截层致使的相似故障。
六、无其余异常操做。因而推断故障可能因病毒或***致使。
因以前遇到过多起相似案例,一般这种破坏并不复杂,并且可逆(一些不道德的小***会按此敲诈用户)。与用户沟通后,用户将硬盘送至北亚数据恢复中心。
【肯定方案】
将硬盘链接至安全的操做环境中(不加载盘符,不自动写数据,保证彻底只读),发现文件系统底层上彻底正常,但数据区所有错误。以一个PDF文件为例,在WINHEX中打开时以下图:
一个正常的PDF文件,二进制结构必定是以0x46445025(即ASCII的“%PDF”)作为开头标志。这个文件的开头以0x71736712开始。二者比较,显然是一种异或转换,经过计算,二者相差(异或)0x37。观察本PDF文件的尾部,发现一样作了篡改。
因而,在WINHEX中选中文件全部内容,对选中块以0x37作字节异或(xor):
保存出来后,打开,文件正常。
接下来对其余文件作分析,发现篡改的算法均是所有文件对某个值xor,但此值不肯定,按字节几率计算,应该有256种可能,加上文件数量及类型众多,显然不能手动进行修正。须要分析其xor加数的生成规律。
分析过程以下:
一、推断是否与路径相关:在同一路径下打开不一样的文件分析篡改的异或加数,发现不尽相同,排除。
二、推断是否与文件名称相关:查找全部文件,按名称排序,找到相同文件名称但大小不一样的文件,打开后分析篡改的异或加数,发现不相同,排除。
三、推断是否与类型相关:找到同一类型的几个不一样文件,分析篡改的异或加数,发现不相同,排除。
四、推断是否与存储的物理位置相关:在WINHEX中按不一样文件起始位置进行分析篡改的异或加数,未发现相关性,排除。
五、推断是否与文件头部相关:查找头部相同的文件(有同一文件的不一样更新,头部是相同的),进行分析,也排除。
六、推断尾部相关的可能性不大。(固然若是后面分析仍没法获得规律,则需返回此项再作验证)
七、推断是否与文件建立时间相关:分别查找相同建立时间、相同访问时间、相同最后一次访问时间的2个文件,进行分析,发现与此无关,排除。
八、推断是否与大小相关:简单验证后,未举出反例推翻,但须要彻底证实与大小相关,同时要获得算法,须要有足够多的样本。
对是否与大小相关的验证:
首先经过命令方式打印全部文件的大小,WINDOWS很不擅长此操做,改用LINUX处理:
find ./ |xargs ls -ld 2>/dev/null|awk '{printf($5"\t\t"$9"\n");}' >../list.txt
以后用excel打开此列表文件,以下图:
因篡改的异或加数只有一个字节,故推断,若是与大小相关,极有多是对文件大小mod 256后关系对应,因而在excel中计算全部文件大小值 的mod 256,以下图:
对mod 256的值进行排序,excel可能能够直接实现,不过,至少能够复制整列,再以数字方式粘贴:
排序后以下图:
对相同mod 256的文件进行篡改验证,未发现不符合规律者,基本判定篡改值与文件大小mod 256的值存在彻底映射关系。
对全部可能作抽样分析后,获得篡改异或加数的生成规律:
至此,篡改算法获得,同时修正算法也天然就容易多了。
【解决方案】
经过VS2010下编写程序解决,修复程序源码以下:
- //北亚数据恢复中心,张宇,www.datahf.net
- //文件夹遍历算法,来源于互联网,仅作了简单修正。
- #include "stdafx.h"
- #include <iostream>
- #include "windows.h"
- #include <string.h>
- using namespace std;
- #define BUFFSIZE (256*1024)
- byte buff[BUFFSIZE];
- bool xor_file(LPCTSTR swfile)
- {
- LARGE_INTEGER liSize;
- HANDLE hFile;
- hFile = CreateFile(swfile,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- FILE_FLAG_SEQUENTIAL_SCAN,
- NULL);
- if(hFile == INVALID_HANDLE_VALUE)
- {
- _tprintf(_T("%s canot open!\n"),swfile);
- return false;
- }
- GetFileSizeEx(hFile,&liSize);
- int t = liSize.QuadPart % (long long)256;
- int xor_value = 0;
- if( (t>=0) && (t<9))
- xor_value += (t+0x37);
- else if((t>=9) && (t<73))
- xor_value += (t-9+0xC0);
- else if((t>=73) && (t<137))
- xor_value += (t-73+0x80);
- else if((t>=137) && (t<201))
- xor_value += (t-137+0x40);
- else
- xor_value += (t-201+0x00);
- DWORD nb= liSize.QuadPart / (long long) BUFFSIZE;
- DWORD tb= liSize.QuadPart % (long long) BUFFSIZE;
- DWORD bRead;
- DWORD bWrite;
- DWORD i;
- for(i=0;i<nb;i++)
- {
- ReadFile(hFile,buff,BUFFSIZE,&bRead,NULL);
- if(bRead != BUFFSIZE)
- _tprintf(_T("%s canot read,Pos:%I64d!\n"),swfile,i*(long long)BUFFSIZE);
- for(int ii=0;ii<BUFFSIZE;ii++)
- buff[ii] ^= xor_value;
- SetFilePointer(hFile,-BUFFSIZE,NULL,FILE_CURRENT);
- WriteFile(hFile,buff,BUFFSIZE,&bWrite,NULL);
- if(bWrite != BUFFSIZE)
- _tprintf(_T("%s canot write,Pos:%I64d!\n"),swfile,i*(long long)BUFFSIZE);
- }
- {
- ReadFile(hFile,buff,tb,&bRead,NULL);
- if(tb != bRead)
- _tprintf(_T("%s canot read,Pos:%I64d!\n"),swfile,i*(long long)BUFFSIZE);
- for(int ii=0;ii<tb;ii++)
- buff[ii] ^= xor_value;
- SetFilePointer(hFile,-tb,NULL,FILE_CURRENT);
- WriteFile(hFile,buff,tb,&bWrite,NULL);
- if(tb != bWrite)
- _tprintf(_T("%s canot write,Pos:%I64d!\n"),swfile,i*(long long)BUFFSIZE);
- }
- CloseHandle(hFile);
- return true;
- }
- void TraverseDirectory(TCHAR Dir[MAX_PATH]);
- bool select_file(LPCTSTR Dir,WIN32_FIND_DATA &FindFileData)
- {
- if((FindFileData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)!=0&&_tcscmp(FindFileData.cFileName,L".")==0||_tcscmp(FindFileData.cFileName,L"..")==0) //判断是文件夹&&表示为"."||表示为"."
- {
- return false;
- }
- TCHAR DirAdd[MAX_PATH];
- StringCchCopy(DirAdd,MAX_PATH,Dir);
- StringCchCat(DirAdd,MAX_PATH,TEXT("\\"));
- StringCchCat(DirAdd,MAX_PATH,FindFileData.cFileName); //拼接获得此文件夹的完整路径
- if((FindFileData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)!=0) //判断若是是文件夹
- {
- TraverseDirectory(DirAdd); //实现递归调用
- }
- if((FindFileData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)==0) //若是不是文件夹
- {
- _tprintf (_T("%s\n"), DirAdd);
- xor_file(DirAdd);
- }
- return true;
- }
- //传入要遍历的文件夹路径,并遍历相应文件夹
- void TraverseDirectory(TCHAR Dir[MAX_PATH])
- {
- WIN32_FIND_DATA FindFileData;
- HANDLE hFind=INVALID_HANDLE_VALUE;
- TCHAR DirSpec[MAX_PATH]; //定义要遍历的文件夹的目录
- DWORD dwError;
- StringCchCopy(DirSpec,MAX_PATH,Dir);
- StringCchCat(DirSpec,MAX_PATH,TEXT("\\*")); //定义要遍历的文件夹的完整路径\*
- hFind=FindFirstFile(DirSpec,&FindFileData); //找到文件夹中的第一个文件
- if(hFind==INVALID_HANDLE_VALUE) //若是hFind句柄建立失败,输出错误信息
- {
- FindClose(hFind);
- return;
- }
- else
- {
- select_file(Dir,FindFileData);
- while(FindNextFile(hFind,&FindFileData)!=0) //当文件或者文件夹存在时
- {
- select_file(Dir,FindFileData);
- }
- FindClose(hFind);
- }
- }
- int _tmain( int argc, TCHAR *argv[] )
- {
- locale loc( "chs" ); //支持中文输出,不然wchar可能没法输出值为中文的变量
- cout.imbue( loc );
- if( argc != 2 )
- {
- _tprintf(_T("Usage: %s [workdir]\n"), argv[0]);
- return -1;
- }
- _tprintf (_T("work dir is %s\n"), argv[1]);
- TraverseDirectory(argv[1]);
- return 0;
- }
【验证】
程序运行完成后,对文件进行抽检,无报错,为进一步肯定可靠性,查找全部JPG文件,显示缩略图,无异常。
查找全部doc文件,显示做者,标题(这两个信息是经过内容部分获得的),未发现异常(只是OS盗版的痕迹挺重,呵呵),至此,肯定算法正确。数据恢复完成。