363 строки
9.9 KiB
C
363 строки
9.9 KiB
C
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include "common.h"
|
|
#include <dmg/dmg.h>
|
|
#include <xpwn/outputstate.h>
|
|
#include <zip.h>
|
|
#include <unzip.h>
|
|
|
|
#ifdef WIN32
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#define DEFAULT_BUFFER_SIZE (1 * 1024 * 1024)
|
|
|
|
uint64_t MaxLoadZipSize = UINT64_MAX;
|
|
|
|
void addToOutputQueue(OutputState** state, const char* fileName, void* buffer, const size_t bufferSize, char* tmpFileName) {
|
|
OutputState* leftNeighbor;
|
|
OutputState* rightNeighbor;
|
|
OutputState* next;
|
|
OutputState* newFile;
|
|
int ret;
|
|
char* myName;
|
|
|
|
myName = malloc(sizeof(char) * (strlen(fileName) + 1));
|
|
strcpy(myName, fileName);
|
|
|
|
leftNeighbor = NULL;
|
|
rightNeighbor = NULL;
|
|
|
|
next = *state;
|
|
while(next != NULL) {
|
|
ret = strcmp(next->fileName, myName);
|
|
if(ret > 0) {
|
|
leftNeighbor = next->prev;
|
|
rightNeighbor = next;
|
|
break;
|
|
}
|
|
|
|
leftNeighbor = next;
|
|
next = next->next;
|
|
}
|
|
|
|
if(leftNeighbor != NULL && strcasecmp(leftNeighbor->fileName, myName) == 0) {
|
|
newFile = leftNeighbor;
|
|
if(newFile->tmpFileName) {
|
|
unlink(newFile->tmpFileName);
|
|
free(newFile->tmpFileName);
|
|
}
|
|
free(newFile->fileName);
|
|
|
|
if(newFile->buffer) {
|
|
free(newFile->buffer);
|
|
}
|
|
} else {
|
|
newFile = (OutputState*) malloc(sizeof(OutputState));
|
|
newFile->next = rightNeighbor;
|
|
newFile->prev = leftNeighbor;
|
|
if(leftNeighbor == NULL) {
|
|
*state = newFile;
|
|
} else {
|
|
leftNeighbor->next = newFile;
|
|
}
|
|
if(rightNeighbor != NULL) {
|
|
rightNeighbor->prev = newFile;
|
|
}
|
|
}
|
|
|
|
newFile->fileName = myName;
|
|
newFile->tmpFileName = tmpFileName;
|
|
newFile->buffer = buffer;
|
|
newFile->bufferSize = bufferSize;
|
|
}
|
|
|
|
void addToOutput2(OutputState** state, const char* fileName, void* buffer, const size_t bufferSize, char* tmpFileName) {
|
|
char* fileNamePath;
|
|
char* fileNameNoPath;
|
|
|
|
fileNamePath = (char*) malloc(sizeof(char) * (strlen(fileName) + 1));
|
|
strcpy(fileNamePath, fileName);
|
|
fileNameNoPath = fileNamePath + strlen(fileNamePath);
|
|
|
|
if(*fileNamePath != '/') {
|
|
while(fileNameNoPath > fileNamePath) {
|
|
if(*fileNameNoPath == '/') {
|
|
*(fileNameNoPath + 1) = '\0';
|
|
addToOutputQueue(state, fileNamePath, malloc(1), 0, NULL);
|
|
}
|
|
|
|
fileNameNoPath--;
|
|
}
|
|
}
|
|
|
|
free(fileNamePath);
|
|
|
|
addToOutputQueue(state, fileName, buffer, bufferSize, tmpFileName);
|
|
}
|
|
|
|
void addToOutput(OutputState** state, const char* fileName, void* buffer, const size_t bufferSize) {
|
|
addToOutput2(state, fileName, buffer, bufferSize, NULL);
|
|
}
|
|
|
|
void removeFileFromOutputState(OutputState** state, const char* fileName) {
|
|
OutputState* curFile;
|
|
|
|
curFile = *state;
|
|
while(curFile != NULL) {
|
|
if(strcmp(curFile->fileName, fileName) == 0) {
|
|
if(curFile->prev == NULL) {
|
|
*state = curFile->next;
|
|
(*state)->next->prev = NULL;
|
|
} else {
|
|
curFile->prev->next = curFile->next;
|
|
curFile->next->prev = curFile->prev;
|
|
}
|
|
curFile->prev = NULL;
|
|
curFile->next = NULL;
|
|
releaseOutput(&curFile);
|
|
return;
|
|
}
|
|
curFile = curFile->next;
|
|
}
|
|
}
|
|
|
|
AbstractFile* getFileFromOutputState(OutputState** state, const char* fileName) {
|
|
OutputState* curFile;
|
|
|
|
curFile = *state;
|
|
while(curFile != NULL) {
|
|
if(strcmp(curFile->fileName, fileName) == 0) {
|
|
if(curFile->tmpFileName == NULL)
|
|
return createAbstractFileFromMemory(&(curFile->buffer), curFile->bufferSize);
|
|
else
|
|
return createAbstractFileFromFile(fopen(curFile->tmpFileName, "rb"));
|
|
}
|
|
curFile = curFile->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
AbstractFile* getFileFromOutputStateForOverwrite(OutputState** state, const char* fileName) {
|
|
OutputState* curFile;
|
|
size_t bufSize;
|
|
|
|
curFile = *state;
|
|
while(curFile != NULL) {
|
|
if(strcmp(curFile->fileName, fileName) == 0) {
|
|
if(curFile->tmpFileName == NULL) {
|
|
bufSize = curFile->bufferSize;
|
|
curFile->bufferSize = 0;
|
|
return createAbstractFileFromMemoryFileBuffer(&(curFile->buffer), &curFile->bufferSize, bufSize);
|
|
} else {
|
|
return createAbstractFileFromFile(fopen(curFile->tmpFileName, "r+b"));
|
|
}
|
|
}
|
|
curFile = curFile->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
AbstractFile* getFileFromOutputStateForReplace(OutputState** state, const char* fileName) {
|
|
OutputState* curFile;
|
|
size_t bufSize;
|
|
|
|
curFile = *state;
|
|
while(curFile != NULL) {
|
|
if(strcmp(curFile->fileName, fileName) == 0) {
|
|
if(curFile->tmpFileName == NULL) {
|
|
bufSize = curFile->bufferSize;
|
|
curFile->bufferSize = 0;
|
|
return createAbstractFileFromMemoryFileBuffer(&(curFile->buffer), &curFile->bufferSize, bufSize);
|
|
} else {
|
|
return createAbstractFileFromFile(fopen(curFile->tmpFileName, "wb"));
|
|
}
|
|
}
|
|
curFile = curFile->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void writeOutput(OutputState** state, char* ipsw) {
|
|
OutputState* curFile;
|
|
OutputState* next;
|
|
zip_fileinfo info;
|
|
struct tm* filedate;
|
|
time_t tm_t;
|
|
|
|
zipFile zip;
|
|
|
|
tm_t = time(NULL);
|
|
filedate = localtime(&tm_t);
|
|
info.tmz_date.tm_sec = filedate->tm_sec;
|
|
info.tmz_date.tm_min = filedate->tm_min;
|
|
info.tmz_date.tm_hour = filedate->tm_hour;
|
|
info.tmz_date.tm_mday = filedate->tm_mday;
|
|
info.tmz_date.tm_mon = filedate->tm_mon ;
|
|
info.tmz_date.tm_year = filedate->tm_year;
|
|
info.dosDate = 0;
|
|
info.internal_fa = 0;
|
|
info.external_fa = 0;
|
|
|
|
ASSERT(zip = zipOpen(ipsw, APPEND_STATUS_CREATE), "Cannot open output zip file");
|
|
|
|
next = *state;
|
|
|
|
while(next != NULL) {
|
|
curFile = next;
|
|
next = next->next;
|
|
printf("packing: %s (%ld)\n", curFile->fileName, (long) curFile->bufferSize); fflush(stdout);
|
|
|
|
if(curFile->bufferSize > 0) {
|
|
ASSERT(zipOpenNewFileInZip(zip, curFile->fileName, &info, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION) == 0, "error adding to zip");
|
|
if(curFile->tmpFileName == NULL) {
|
|
ASSERT(zipWriteInFileInZip(zip, curFile->buffer, curFile->bufferSize) == 0, "error writing to zip");
|
|
} else {
|
|
AbstractFile* tmpFile = createAbstractFileFromFile(fopen(curFile->tmpFileName, "rb"));
|
|
char* buffer = malloc(DEFAULT_BUFFER_SIZE);
|
|
size_t left = tmpFile->getLength(tmpFile);
|
|
while(left > 0) {
|
|
size_t toRead;
|
|
if(left > DEFAULT_BUFFER_SIZE)
|
|
toRead = DEFAULT_BUFFER_SIZE;
|
|
else
|
|
toRead = left;
|
|
ASSERT(tmpFile->read(tmpFile, buffer, toRead) == toRead, "error reading data");
|
|
ASSERT(zipWriteInFileInZip(zip, buffer, toRead) == 0, "error writing to zip");
|
|
left -= toRead;
|
|
}
|
|
tmpFile->close(tmpFile);
|
|
free(buffer);
|
|
}
|
|
} else {
|
|
ASSERT(zipOpenNewFileInZip(zip, curFile->fileName, &info, NULL, 0, NULL, 0, NULL, 0, 0) == 0, "error adding to zip");
|
|
}
|
|
ASSERT(zipCloseFileInZip(zip) == 0, "error closing file in zip");
|
|
if(curFile->tmpFileName) {
|
|
unlink(curFile->tmpFileName);
|
|
free(curFile->tmpFileName);
|
|
}
|
|
free(curFile->fileName);
|
|
|
|
if(curFile->buffer) {
|
|
free(curFile->buffer);
|
|
}
|
|
|
|
free(curFile);
|
|
}
|
|
|
|
zipClose(zip, NULL);
|
|
}
|
|
|
|
void releaseOutput(OutputState** state) {
|
|
OutputState* curFile;
|
|
OutputState* next;
|
|
|
|
next = *state;
|
|
while(next != NULL) {
|
|
curFile = next;
|
|
next = next->next;
|
|
|
|
if(curFile->tmpFileName) {
|
|
unlink(curFile->tmpFileName);
|
|
free(curFile->tmpFileName);
|
|
}
|
|
|
|
free(curFile->fileName);
|
|
|
|
if(curFile->buffer) {
|
|
free(curFile->buffer);
|
|
}
|
|
|
|
free(curFile);
|
|
}
|
|
*state = NULL;
|
|
}
|
|
|
|
char* createTempFile() {
|
|
char tmpFileBuffer[512];
|
|
#ifdef WIN32
|
|
char tmpFilePath[512];
|
|
GetTempPath(512, tmpFilePath);
|
|
GetTempFileName(tmpFilePath, "pwn", 0, tmpFileBuffer);
|
|
CloseHandle(CreateFile(tmpFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL));
|
|
#else
|
|
strcpy(tmpFileBuffer, "/tmp/pwnXXXXXX");
|
|
close(mkstemp(tmpFileBuffer));
|
|
FILE* tFile = fopen(tmpFileBuffer, "wb");
|
|
fclose(tFile);
|
|
#endif
|
|
|
|
return strdup(tmpFileBuffer);
|
|
}
|
|
|
|
OutputState* loadZip(const char* ipsw) {
|
|
return loadZip2(ipsw, FALSE);
|
|
}
|
|
|
|
OutputState* loadZip2(const char* ipsw, int useMemory) {
|
|
OutputState* toReturn = NULL;
|
|
|
|
loadZipFile2(ipsw, &toReturn, NULL, useMemory);
|
|
|
|
return toReturn;
|
|
}
|
|
|
|
void loadZipFile(const char* ipsw, OutputState** output, const char* file) {
|
|
loadZipFile2(ipsw, output, file, TRUE);
|
|
}
|
|
|
|
void loadZipFile2(const char* ipsw, OutputState** output, const char* file, int useMemory) {
|
|
char* fileName;
|
|
void* buffer;
|
|
unzFile zip;
|
|
unz_file_info pfile_info;
|
|
|
|
ASSERT(zip = unzOpen(ipsw), "cannot open input ipsw");
|
|
ASSERT(unzGoToFirstFile(zip) == UNZ_OK, "cannot seek to first file in input ipsw");
|
|
|
|
do {
|
|
ASSERT(unzGetCurrentFileInfo(zip, &pfile_info, NULL, 0, NULL, 0, NULL, 0) == UNZ_OK, "cannot get current file info from ipsw");
|
|
fileName = (char*) malloc(pfile_info.size_filename + 1);
|
|
ASSERT(unzGetCurrentFileInfo(zip, NULL, fileName, pfile_info.size_filename + 1, NULL, 0, NULL, 0) == UNZ_OK, "cannot get current file name from ipsw");
|
|
if(((file == NULL && fileName[strlen(fileName) - 1] != '/') || (file != NULL && strcmp(fileName, file)) == 0) && pfile_info.uncompressed_size <= MaxLoadZipSize) {
|
|
printf("loading: %s (%ld)\n", fileName, pfile_info.uncompressed_size); fflush(stdout);
|
|
ASSERT(unzOpenCurrentFile(zip) == UNZ_OK, "cannot open compressed file in IPSW");
|
|
if(useMemory) {
|
|
buffer = malloc((pfile_info.uncompressed_size > 0) ? pfile_info.uncompressed_size : 1);
|
|
ASSERT(unzReadCurrentFile(zip, buffer, pfile_info.uncompressed_size) == pfile_info.uncompressed_size, "cannot read file from ipsw");
|
|
addToOutput(output, fileName, buffer, pfile_info.uncompressed_size);
|
|
} else {
|
|
char* tmpFileName = createTempFile();
|
|
FILE* tmpFile = fopen(tmpFileName, "wb");
|
|
buffer = malloc(DEFAULT_BUFFER_SIZE);
|
|
size_t left = pfile_info.uncompressed_size;
|
|
while(left > 0) {
|
|
size_t toRead;
|
|
if(left > DEFAULT_BUFFER_SIZE)
|
|
toRead = DEFAULT_BUFFER_SIZE;
|
|
else
|
|
toRead = left;
|
|
ASSERT(unzReadCurrentFile(zip, buffer, toRead) == toRead, "cannot read file from ipsw");
|
|
fwrite(buffer, toRead, 1, tmpFile);
|
|
left -= toRead;
|
|
}
|
|
fclose(tmpFile);
|
|
free(buffer);
|
|
addToOutput2(output, fileName, NULL, pfile_info.uncompressed_size, tmpFileName);
|
|
}
|
|
ASSERT(unzCloseCurrentFile(zip) == UNZ_OK, "cannot close compressed file in IPSW");
|
|
}
|
|
free(fileName);
|
|
} while(unzGoToNextFile(zip) == UNZ_OK);
|
|
|
|
ASSERT(unzClose(zip) == UNZ_OK, "cannot close input ipsw file");
|
|
}
|
|
|
|
|