紧接上一篇
MTK Logo 逆向解析之 bin 转 rawx
查看 zpipe 源码发现压缩使用 zlib 算法,其中也包含了解压的代码,但直接执行发现并不好使,有bug。很明显 mtk
并没有真正用过解压,那就需要我们自己来修复一下了,总比没有代码需要我们自己新写容易些。
知识储备,关于 zlib 使用介绍可以参考这篇
第三方库介绍——zlib库
对 zlib 有一定认知后,我们就来看看 zpipe 源码
一、分析源码
主看 main 函数,一开始做了参数类型判断,参数一 -l 就走 compress 逻辑,否则就 decompress 逻辑
源码中提示的执行指令为 compress -l 9 logo.raw battery.raw
input 对应 battery.raw output 对应 logo.raw
根据实际传参数量申请大小为 filenum*4 的 unsigned int pinfo ,然后将数据全部初始化为 0 memset((void)pinfo, 0, sizeof(int)*filenum);
关键代码来了
if(sizeof(int)filenum != fwrite(pinfo, 1, filenumsizeof(int), output)) 将文件数量数据写入 output 头部
pinfo[pinfo[0]+2]=ftell(output); 将指针移动到头部数据后一位,同时 pinfo[2] 存储头部数据大小
if (Z_OK != def(input, output, level)) 执行压缩算法,压缩数据跟在头部数据后面
{
ret = -2;
goto done;
}
pinfo[0] = pinfo[0]+1; 记录压缩了几次
for (i = 5; i < argc; i++)
{
fclose(input);
input = fopen(argv[i], "rb"); 重新赋值input,重复上面核心逻辑pinfo[pinfo[0]+2]=ftell(output);
if (Z_OK != def(input, output, level))
{fprintf(stderr, "compress error\n");ret = -2;goto done;
}
pinfo[0] = pinfo[0]+1;
}
done:
fseek(output, 0L, SEEK_END); 文件指针移动到末尾
pinfo[1] = ftell(output); 获取 output 完整数据
fseek(output, 0L, SEEK_SET); 恢复文件指针位置
fwrite(pinfo, 1, filenum*sizeof(int), output); 写入完整数据
假如执行指令为 ./zpipe -l 9 log.raw wxganl_bat_animation_01.raw wxganl_bat_animation_02.raw wxganl_bat_animation_03.raw
压缩过程为
wxganl_bat_animation_01.raw --> log.raw
wxganl_bat_animation_02.raw --> log.raw
wxganl_bat_animation_03.raw --> log.raw
pinfo[0]=压缩次数 pinfo[1]=第一次压缩后数据大小 pinfo[2]=要压缩文件数量大小 pinfo[3]=第一次压缩后数据大小 pinfo[4]=第一次压缩后数据大小
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "zlib.h"#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
# include <fcntl.h>
# include <io.h>
# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
#else
# define SET_BINARY_MODE(file)
#endif#define CHUNK 16384/* Compress from file source to file dest until EOF on source.def() returns Z_OK on success, Z_MEM_ERROR if memory could not beallocated for processing, Z_STREAM_ERROR if an invalid compressionlevel is supplied, Z_VERSION_ERROR if the version of zlib.h and theversion of the library linked do not match, or Z_ERRNO if there isan error reading or writing the files. */
int def(FILE *source, FILE *dest, int level)
{int ret, flush;unsigned have;z_stream strm;unsigned char in[CHUNK];unsigned char out[CHUNK];/* allocate deflate state */strm.zalloc = Z_NULL;strm.zfree = Z_NULL;strm.opaque = Z_NULL;ret = deflateInit(&strm, level);if (ret != Z_OK)return ret;/* compress until end of file */do {strm.avail_in = fread(in, 1, CHUNK, source);if (ferror(source)) {(void)deflateEnd(&strm);return Z_ERRNO;}flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;strm.next_in = in;/* run deflate() on input until output buffer not full, finishcompression if all of source has been read in */do {strm.avail_out = CHUNK;strm.next_out = out;ret = deflate(&strm, flush); /* no bad return value */assert(ret != Z_STREAM_ERROR); /* state not clobbered */have = CHUNK - strm.avail_out;if (fwrite(out, 1, have, dest) != have || ferror(dest)) {(void)inflateEnd(&strm);return Z_ERRNO;}} while (strm.avail_out == 0);assert(strm.avail_in == 0); /* all input will be used *//* done when last data in file processed */} while (flush != Z_FINISH);assert(ret == Z_STREAM_END); /* stream will be complete *//* clean up and return */(void)deflateEnd(&strm);return Z_OK;
}/* Decompress from file source to file dest until stream ends or EOF.inf() returns Z_OK on success, Z_MEM_ERROR if memory could not beallocated for processing, Z_DATA_ERROR if the deflate data isinvalid or incomplete, Z_VERSION_ERROR if the version of zlib.h andthe version of the library linked do not match, or Z_ERRNO if thereis an error reading or writing the files. */
int inf(FILE *source, FILE *dest)
{int ret;unsigned int have;z_stream strm;unsigned char in[CHUNK];unsigned char out[CHUNK];/* allocate inflate state */strm.zalloc = Z_NULL;strm.zfree = Z_NULL;strm.opaque = Z_NULL;strm.avail_in = 0;strm.next_in = Z_NULL;ret = inflateInit(&strm);if (ret != Z_OK)return ret;/* decompress until deflate stream ends or end of file */do {strm.avail_in = fread(in, 1, CHUNK, source);if (ferror(source)) {(void)inflateEnd(&strm);return Z_ERRNO;}if (strm.avail_in == 0)break;strm.next_in = in;/* run inflate() on input until output buffer not full */do {strm.avail_out = CHUNK;strm.next_out = out;ret = inflate(&strm, Z_NO_FLUSH);assert(ret != Z_STREAM_ERROR); /* state not clobbered */switch (ret) {case Z_NEED_DICT:ret = Z_DATA_ERROR; /* and fall through */case Z_DATA_ERROR:case Z_MEM_ERROR:(void)inflateEnd(&strm);return ret;}have = CHUNK - strm.avail_out;if (fwrite(out, 1, have, dest) != have || ferror(dest)) {(void)inflateEnd(&strm);return Z_ERRNO;}} while (strm.avail_out == 0);/* done when inflate() says it's done */} while (ret != Z_STREAM_END);/* clean up and return */(void)inflateEnd(&strm);return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}/* report a zlib or i/o error */
void zerr(int ret)
{fputs("zpipe: ", stderr);switch (ret) {case Z_ERRNO:if (ferror(stdin))fputs("error reading stdin\n", stderr);if (ferror(stdout))fputs("error writing stdout\n", stderr);break;case Z_STREAM_ERROR:fputs("invalid compression level\n", stderr);break;case Z_DATA_ERROR:fputs("invalid or incomplete deflate data\n", stderr);break;case Z_MEM_ERROR:fputs("out of memory\n", stderr);break;case Z_VERSION_ERROR:fputs("zlib version mismatch!\n", stderr);}
}/* compress or decompress from stdin to stdout */
int process(int argc, char **argv)
{int ret;/* avoid end-of-line conversions */SET_BINARY_MODE(stdin);SET_BINARY_MODE(stdout);/* do compression if no arguments */if (argc == 1) {ret = def(stdin, stdout, Z_DEFAULT_COMPRESSION);if (ret != Z_OK)zerr(ret);return ret;}/* do decompression if -d specified */else if (argc == 2 && strcmp(argv[1], "-d") == 0) {ret = inf(stdin, stdout);if (ret != Z_OK)zerr(ret);return ret;}/* otherwise, report usage */else {fputs("zpipe usage: zpipe [-d] < source > dest\n", stderr);return 1;}
}int main(int argc, char **argv)
{int ret = 0;int i;FILE *input, *output;if (argc < 4 || (strcmp(argv[1], "-l") && strcmp(argv[1], "-d"))) {printf("[Usage] compress -l n output input1 [input2]\n");printf("Example: compress -l 9 logo.raw battery.raw\n");return -1;}// compressif(!strcmp(argv[1], "-l")){char *ptemp;unsigned int *pinfo;int level = -1;int filenum = argc - 2;level = atoi(argv[2]);input = fopen(argv[4], "rb");output = fopen(argv[3], "wb");ptemp = (char*)malloc(1024*1024);pinfo = (unsigned int*)malloc(filenum*sizeof(int));if(input < 0 || output < 0 || ptemp == NULL || pinfo == NULL){fprintf(stderr, "open file and allocate temp buffer fail\n");fprintf(stderr, "input = %d, output = %d, ptemp = 0x%08x, pinfo = 0x%08x\n", input, output, ptemp, pinfo);ret = -1;goto done;}// structre of pinfo:// pinfo[0] ==> size of pinfo // pinfo[1] ==> offset of zip chunk[1]// pinfo[2] ==> offset of zip chunk[2]// ...// pinfo[n] ==> offset of zip chunk[n]// "offset" is the distance from the begining of the file, not the zip chunkmemset((void*)pinfo, 0, sizeof(int)*filenum);// write information header to output firstif(sizeof(int)*filenum != fwrite(pinfo, 1, filenum*sizeof(int), output)){ret = -2;goto done;}pinfo[pinfo[0]+2]=ftell(output);if (Z_OK != def(input, output, level)) {ret = -2;goto done;}pinfo[0] = pinfo[0]+1;for (i = 5; i < argc; i++) {fclose(input);input = fopen(argv[i], "rb");pinfo[pinfo[0]+2]=ftell(output);if (Z_OK != def(input, output, level)) {fprintf(stderr, "compress error\n");ret = -2;goto done;}pinfo[0] = pinfo[0]+1;}done:fseek(output, 0L, SEEK_END);pinfo[1] = ftell(output);fseek(output, 0L, SEEK_SET);fwrite(pinfo, 1, filenum*sizeof(int), output);fclose(input);fclose(output);free(ptemp);free(pinfo);return ret;}// decompresselse{unsigned int temp;unsigned int *pinfo;char outputfilename[256];input = fopen(argv[3], "rb");if(input < 0){printf("open file fail\n");printf("input = %d\n", input);ret = -1;goto done2;}fread(&temp, 1, sizeof(int), input);printf("temp=%d\n", temp);pinfo = malloc(temp*sizeof(int));if(pinfo == NULL){printf("allocate pinfo failed\n");ret = -1;goto done2;}fread(pinfo, 1, temp*sizeof(int), input);for(i=0;i<temp;i++)printf("pinfo[%d]=%d\n", i, pinfo[i]);for(i = 0;i < temp;i++){sprintf(outputfilename, "%d_%s", i, argv[2]);output = fopen(outputfilename, "wb");if(output < 0){printf("create output file %s fail\n", outputfilename);goto done2;}fseek(input, pinfo[i], SEEK_SET);if(Z_OK != inf(input, output)){printf("decompress error\n");ret = -2;goto done2;}fclose(output);output = 0;}
done2:fclose(input);if(output) fclose(output);return ret;}
}
二、改造源码
直接执行解压指令 ./zpipe -d xx ./logo.raw 会报错 decompress error
经过分析是解压文件数量计算不对导致循环出错,结合上面的压缩过程结论,
需要跳过包含 pinfo[x]=要压缩文件数量大小 无效数据
然后我们增加参数 -d 判断, xx 就是解压后文件命名,最终会得到 0_xx.raw 1_xx.raw 等
可以通过文件 md5 指令比对压缩前原文件和解压后的文件
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <zlib.h>#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
# include <fcntl.h>
# include <io.h>
# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
#else
# define SET_BINARY_MODE(file)
#endif#define CHUNK 16384/* Compress from file source to file dest until EOF on source.def() returns Z_OK on success, Z_MEM_ERROR if memory could not beallocated for processing, Z_STREAM_ERROR if an invalid compressionlevel is supplied, Z_VERSION_ERROR if the version of zlib.h and theversion of the library linked do not match, or Z_ERRNO if there isan error reading or writing the files. */
int def(FILE *source, FILE *dest, int level)
{int ret, flush;unsigned have;z_stream strm;unsigned char in[CHUNK];unsigned char out[CHUNK];/* allocate deflate state */strm.zalloc = Z_NULL;strm.zfree = Z_NULL;strm.opaque = Z_NULL;ret = deflateInit(&strm, level);if (ret != Z_OK)return ret;/* compress until end of file */do {strm.avail_in = fread(in, 1, CHUNK, source);if (ferror(source)) {(void)deflateEnd(&strm);return Z_ERRNO;}flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;strm.next_in = in;/* run deflate() on input until output buffer not full, finishcompression if all of source has been read in */do {strm.avail_out = CHUNK;strm.next_out = out;ret = deflate(&strm, flush); /* no bad return value */assert(ret != Z_STREAM_ERROR); /* state not clobbered */have = CHUNK - strm.avail_out;if (fwrite(out, 1, have, dest) != have || ferror(dest)) {(void)inflateEnd(&strm);return Z_ERRNO;}} while (strm.avail_out == 0);assert(strm.avail_in == 0); /* all input will be used *//* done when last data in file processed */} while (flush != Z_FINISH);assert(ret == Z_STREAM_END); /* stream will be complete *//* clean up and return */(void)deflateEnd(&strm);return Z_OK;
}/* Decompress from file source to file dest until stream ends or EOF.inf() returns Z_OK on success, Z_MEM_ERROR if memory could not beallocated for processing, Z_DATA_ERROR if the deflate data isinvalid or incomplete, Z_VERSION_ERROR if the version of zlib.h andthe version of the library linked do not match, or Z_ERRNO if thereis an error reading or writing the files. */
int inf(FILE *source, FILE *dest)
{int ret;unsigned int have;z_stream strm;unsigned char in[CHUNK];unsigned char out[CHUNK];/* allocate inflate state */strm.zalloc = Z_NULL;strm.zfree = Z_NULL;strm.opaque = Z_NULL;strm.avail_in = 0;strm.next_in = Z_NULL;ret = inflateInit(&strm);if (ret != Z_OK)return ret;/* decompress until deflate stream ends or end of file */do {strm.avail_in = fread(in, 1, CHUNK, source);if (ferror(source)) {(void)inflateEnd(&strm);return Z_ERRNO;}if (strm.avail_in == 0)break;strm.next_in = in;/* run inflate() on input until output buffer not full */do {strm.avail_out = CHUNK;strm.next_out = out;ret = inflate(&strm, Z_NO_FLUSH);assert(ret != Z_STREAM_ERROR); /* state not clobbered */switch (ret) {case Z_NEED_DICT:ret = Z_DATA_ERROR; /* and fall through */case Z_DATA_ERROR:case Z_MEM_ERROR:(void)inflateEnd(&strm);return ret;}have = CHUNK - strm.avail_out;if (fwrite(out, 1, have, dest) != have || ferror(dest)) {(void)inflateEnd(&strm);return Z_ERRNO;}} while (strm.avail_out == 0);/* done when inflate() says it's done */} while (ret != Z_STREAM_END);/* clean up and return */(void)inflateEnd(&strm);return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}/* report a zlib or i/o error */
void zerr(int ret)
{fputs("zpipe: ", stderr);switch (ret) {case Z_ERRNO:if (ferror(stdin))fputs("error reading stdin\n", stderr);if (ferror(stdout))fputs("error writing stdout\n", stderr);break;case Z_STREAM_ERROR:fputs("invalid compression level\n", stderr);break;case Z_DATA_ERROR:fputs("invalid or incomplete deflate data\n", stderr);break;case Z_MEM_ERROR:fputs("out of memory\n", stderr);break;case Z_VERSION_ERROR:fputs("zlib version mismatch!\n", stderr);}
}/* compress or decompress from stdin to stdout */
int process(int argc, char **argv)
{int ret;/* avoid end-of-line conversions */SET_BINARY_MODE(stdin);SET_BINARY_MODE(stdout);/* do compression if no arguments */if (argc == 1) {ret = def(stdin, stdout, Z_DEFAULT_COMPRESSION);if (ret != Z_OK)zerr(ret);return ret;}/* do decompression if -d specified */else if (argc == 2 && strcmp(argv[1], "-d") == 0) {ret = inf(stdin, stdout);if (ret != Z_OK)zerr(ret);return ret;}/* otherwise, report usage */else {fputs("zpipe usage: zpipe [-d] < source > dest\n", stderr);return 1;}
}int main(int argc, char **argv)
{int ret = 0;int i;FILE *input, *output;if (argc < 4 || (strcmp(argv[1], "-l") && strcmp(argv[1], "-d"))) {printf("[compress Example]: zpipe -l 9 logo.raw battery.raw\n");printf("[decompress Example]: zpipe -d xx.raw logo.raw\n");return -1;}// compressif(!strcmp(argv[1], "-l")){char *ptemp;unsigned int *pinfo;int level = -1;int filenum = argc - 2;level = atoi(argv[2]);input = fopen(argv[4], "rb");output = fopen(argv[3], "wb");ptemp = (char*)malloc(1024*1024);pinfo = (unsigned int*)malloc(filenum*sizeof(int));if(input < 0 || output < 0 || ptemp == NULL || pinfo == NULL){fprintf(stderr, "open file and allocate temp buffer fail\n");fprintf(stderr, "input = %d, output = %d, ptemp = 0x%08x, pinfo = 0x%08x\n", input, output, ptemp, pinfo);ret = -1;goto done;}// structre of pinfo:// pinfo[0] ==> size of pinfo // pinfo[1] ==> offset of zip chunk[1]// pinfo[2] ==> offset of zip chunk[2]// ...// pinfo[n] ==> offset of zip chunk[n]// "offset" is the distance from the begining of the file, not the zip chunkmemset((void*)pinfo, 0, sizeof(int)*filenum);// write information header to output first//将文件个数写入第一位if(sizeof(int)*filenum != fwrite(pinfo, 1, filenum*sizeof(int), output)){ret = -2;goto done;}//获取当前指针位置,也就是文件个数写入内容的实际长度pinfo[pinfo[0]+2]=ftell(output);printf("pinfo[0]=%d\n", pinfo[0]);printf("pinfo[pinfo[0]+2]=%d\n", pinfo[pinfo[0]+2]);//开始压缩第一个文件if (Z_OK != def(input, output, level)) {fprintf(stderr, "compress error 111\n");ret = -2;goto done;}pinfo[0] = pinfo[0]+1;printf("argc=%d\n", argc);for (i = 5; i < argc; i++) {fclose(input);//循环上面过程input = fopen(argv[i], "rb");pinfo[pinfo[0]+2]=ftell(output);if (Z_OK != def(input, output, level)) {fprintf(stderr, "compress error\n");ret = -2;goto done;}pinfo[0] = pinfo[0]+1;}done://将指针移动到文件末尾fseek(output, 0L, SEEK_END);//获取文件大小pinfo[1] = ftell(output);// printf("pinfo[1]=%d\n", pinfo[1]);//将指针恢复到原来位置fseek(output, 0L, SEEK_SET);//写入数据fwrite(pinfo, 1, filenum*sizeof(int), output);for (int i = 0; i < filenum; ++i){printf("pinfo[%d]=%d\n", i , pinfo[i]);}fclose(input);fclose(output);free(ptemp);free(pinfo);return ret;}// decompresselse{unsigned int temp;unsigned int *pinfo;char outputfilename[256];//打开要解压的文件例如 logo.rawinput = fopen(argv[3], "rb");if(input < 0){printf("open file fail\n");printf("input = %d\n", input);ret = -1;goto done2;}//将文件大小读取到 tempfread(&temp, 1, sizeof(int), input);printf("temp=%d\n", temp);/*int tempLength = (temp+2)*sizeof(int);printf("tempLength=%d\n", tempLength);*/temp = temp + 1;//core//申请指定大小空间pinfo = malloc(temp*sizeof(int));if(pinfo == NULL){printf("allocate pinfo failed\n");ret = -1;goto done2;}//将文件数据读取到 pinfofread(pinfo, 1, temp*sizeof(int), input);/*for(i=0;i<temp;i++)printf("pinfo[%d]===%d\n", i, pinfo[i]);*/// int seekIndex = 0;for(i = 0;i < temp;i++){if (i == 0){printf("skip pinfo 0\n");continue;//core}printf("pinfo[%d]=%d\n", i, pinfo[i]);//拼接要保存的文件名称,0_xxx.rawsprintf(outputfilename, "%d_%s", i, argv[2]);//打开要保存文件流output = fopen(outputfilename, "wb");if(output < 0){printf("create output file %s fail\n", outputfilename);goto done2;}//跳过 pinfo[i]fseek(input, pinfo[i], SEEK_SET);/*if (i == 0){seekIndex = tempLength;}printf("seekIndex=%d\n", seekIndex);fseek(input, seekIndex, SEEK_SET);*///解压读取数据写入if(Z_OK != inf(input, output)){printf("decompress error\n");ret = -2;goto done2;}fclose(output);output = 0;printf("decompress %s done\n", outputfilename);// seekIndex += pinfo[i];}
done2:fclose(input);if(output) fclose(output);return ret;}
}
三、编译指令
gcc -o zpipe zpipe.c -lz
zpipe下载
四、解压 rawx 文件
使用方法
[compress Example]: zpipe -l 9 logo.raw battery.raw
[decompress Example]: zpipe -d xx.raw logo.raw