手擼一個安裝包製做工具(4) --解壓

在開寫解壓代碼以前,咱們要先知道尾部數據的位置:函數

我原本是準備直接寫入尾部一個區段,工具

而後在區段中設置大小或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 );
        }
    }
}

 

 

下一篇: 手擼一個安裝包製做工具(5) --生成器/項目連接

相關文章
相關標籤/搜索