手撸一个安装包制作工具(4)|手撸一个安装包制作工具(4) --解压

在开写解压代码之前,我们要先知道尾部数据的位置:
我本来是准备直接写入尾部一个区段,
然后在区段中设置大小或Flags来确定是否自己添加的.(和NSIS一样)
【手撸一个安装包制作工具(4)|手撸一个安装包制作工具(4) --解压】
但是在这里, 为了方便, 我选择直接写入文件尾部数据,
并且假设这个文件并没有任何的没有头信息的已添加区段

但是为了兼容之后的代码,仍然选择通过PE格式来获取最后区段所在的位置来确定尾部数据位置

先定义几个尾部数据的结构 (写入时也要按此结构写入):

struct addedSector { DWORD verifycode; // 0char 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 };

设置的数据很少,但是够用了.
addedSector结构就是刚开始时说到的'安装包信息'的结构体
这个结构的后面紧接着就是连续的每个文件块.
文件块遍历的话 直接使用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) --生成器/项目链接
转载于:https://my.oschina.net/tasker/blog/724653

    推荐阅读