在開寫解壓代碼以前,咱們要先知道尾部數據的位置:函數
我原本是準備直接寫入尾部一個區段,工具
而後在區段中設置大小或Flags來肯定是否本身添加的.(和NSIS同樣)spa
可是在這裏, 爲了方便, 我選擇直接寫入文件尾部數據,.net
而且假設這個文件並無任何的沒有頭信息的已添加區段code
可是爲了兼容以後的代碼,仍然選擇經過PE格式來獲取最後區段所在的位置來肯定尾部數據位置blog
先定義幾個尾部數據的結構 (寫入時也要按此結構寫入):內存
struct addedSector { DWORD verifycode; // 0 char compressType[10]; // 壓縮方式 char programName[20]; // 安裝包的程序名 char autorun[150]; // 自動運行程序的相對路徑 int nfiles; // 總文件數量 // // +sizeof(addedSector) 以後就是每一個文件塊 fileblock }; struct fileblock { char relativepath[150]; // 臨時文件=".tmp" or 相對路徑 : aa/bb/ char filename[50]; // 文件名 : a.dll >> 那麼這個文件的路徑: setuppath/aa/bb/a.dll DWORD filesize; // 大小 // // +sizeof(fileblock) 以後就是文件內容 byte* filebuf };
設置的數據不多,可是夠用了.get
addedSector結構就是剛開始時說到的'安裝包信息'的結構體it
這個結構的後面緊接着就是連續的每一個文件塊.ast
文件塊遍歷的話 直接使用 filesize+sizeof(fileblock) 便可
模板EXE就能夠按照上面的結構, 找到尾部結構:
// 簡單的初始化PE信息的封裝類 ctPEFile::PEFile selfpe; // 獲取被添加的內存所在位置 addedSector* getAddedSector() { char tmp[260]; GetModuleFileNameA( NULL, tmp, 260 ); if(selfpe.loadfile( tmp )) { // 越過最後一個區段,就是添加數據的位置 PIMAGE_SECTION_HEADER lastsec = selfpe.secheader + selfpe.seccount - 1; return (addedSector*)(selfpe.filebuf + lastsec->PointerToRawData + lastsec->SizeOfRawData); } return nullptr; }
獲取尾部而且初始化結構中的信息後, 下一步就是解壓了,
而解壓最重要的是遍歷文件塊(fileblock):
// 獲取指定的文件塊位置 fileblock* getFileBlock( int ifile ) { if(ifile < addedsec->nfiles) { // 先到第一個 fileblock* fb = (fileblock*)((DWORD)addedsec + sizeof( addedSector )); // 一直遍歷到指定的位置 for(int i = 0; i < ifile; i++) fb = (fileblock*)((DWORD)fb + fb->filesize + sizeof( fileblock )); return fb; } return nullptr; }
拿到文件塊以後就很簡單了,直接寫入文件內容(filebuf)到文件中便可:
// // if relativepath exist, create folders // char dir[260]; wsprintfA( dir, "%s\\%s", setuppath.c_str(), fb->relativepath ); SHCreateDirectoryExA( NULL, dir, NULL ); wsprintfA( extractPath, "%s\\%s\\%s", setuppath.c_str(), fb->relativepath, fb->filename ); // writetoexfile ( filebuf , fb-> filesize );
再給這個解壓函數加上一些額外的功能, 好比界面顯示,臨時文件釋放等.
解壓函數就能夠正常運做了
// // 釋放文件到指定位置 // void extractFile( int ifile ) { fileblock* fb = getFileBlock( ifile ); if(fb) { // 建立要釋放的目錄/肯定文件全路徑 char extractPath[260] = {0}; if(strcmp( fb->relativepath, ".tmp" ) == 0) { // 臨時文件,直接釋放到臨時目錄 GetTempPathA( 260, extractPath ); strcat( extractPath, fb->filename ); } else { if(fb->relativepath[0]) { //if relativepath exist, create folders char dir[260]; wsprintfA( dir, "%s\\%s", setuppath.c_str(), fb->relativepath ); SHCreateDirectoryExA( NULL, dir, NULL ); wsprintfA( extractPath, "%s\\%s\\%s", setuppath.c_str(), fb->relativepath, fb->filename ); } else { wsprintfA( extractPath, "%s\\%s", setuppath.c_str(), fb->filename ); } } // 界面上顯示當前解壓文件的名稱 ctd.setText( "showpath", extractPath ); // 這裏直接寫入文件, 並無解壓相關 , 請去完整代碼中查看 byte* filebuf = (byte*)((DWORD)fb + sizeof( fileblock )); // FILE *f = fopen(extractPath,"wb"); if(f) { fwrite( filebuf, 1, fb->filesize, f ); fclose( f ); } } }