【前期信息】
山東某公司,企業規模也不算小,但不是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盜版的痕跡挺重,呵呵),至此,肯定算法正確。數據恢復完成。