diff --git a/README.md b/README.md index 8519b60..66c2d1c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,23 @@ -# Extractor of SpreadTrum firmware files with extension pac +# pacextractor + +**pacextractor** is a tool for extracting firmware files used for Spreadtrum/Unisoc-based Android devices. + +## Getting Started + +### Build Instructions + +To build the tool, run the following commands: + +```bash +git clone https://github.com/divinebird/pacextractor +cd pacextractor +make +sudo cp pacextractor /usr/bin/ +``` + +### Usage + +```bash +pacextractor +``` -Extract all data that I may recognize in package. \ No newline at end of file diff --git a/pacextractor.c b/pacextractor.c index 6992ed7..d15ff91 100644 --- a/pacextractor.c +++ b/pacextractor.c @@ -1,4 +1,3 @@ - #include #include #include @@ -33,112 +32,164 @@ typedef struct { int32_t dataArray[]; } PartitionHeader; -void getString(int16_t* baseString, char* resString) { - if(*baseString == 0) { - *resString = 0; +void getString(int16_t* source, char* destination) { + if (*source == 0) { + *destination = 0; return; } int length = 0; - do { - *resString = 0xFF & *baseString; - resString++; - baseString++; - if(++length > 256) - break; - } while(baseString > 0); - *resString = 0; + while (*source > 0 && length < 256) { + *destination = 0xFF & *source; + destination++; + source++; + length++; + } + *destination = 0; } int main(int argc, char** argv) { - if(argc < 2) { - printf("command format:\n capextractor .pac"); + if (argc < 2) { + printf("Usage:\n pacextractor .pac [output_directory]\n"); + printf("Extracts partitions from a Spreadtrum/Unisoc firmware file.\n"); + exit(EXIT_FAILURE); + } + + const char* outputDirectory = (argc >= 3) ? argv[2] : "."; + struct stat directoryStat; + if (stat(outputDirectory, &directoryStat) == -1 || !S_ISDIR(directoryStat.st_mode)) { + printf("Error: The specified output directory '%s' does not exist or is invalid.\n", outputDirectory); + exit(EXIT_FAILURE); + } + + int firmwareFile = open(argv[1], O_RDONLY); + if (firmwareFile == -1) { + perror("Error: Unable to open the firmware file"); exit(EXIT_FAILURE); } - - int fd = open(argv[1], O_RDONLY); - if (fd == -1) { - printf("file %s is not find", argv[1]); + + struct stat firmwareStat; + if (fstat(firmwareFile, &firmwareStat) == -1) { + perror("Error: Unable to retrieve firmware file information"); + close(firmwareFile); + exit(EXIT_FAILURE); + } + + if (firmwareStat.st_size < sizeof(PacHeader)) { + printf("Error: The firmware file '%s' is too small to be valid. Please verify the file.\n", argv[1]); + close(firmwareFile); exit(EXIT_FAILURE); } - -// fseek(fd, 0, SEEK_END); -// int firmwareSize = (fd); -// fseek(fd, 0, SEEK_SET); - struct stat st; - stat(argv[1], &st); - int firmwareSize = st.st_size; - if(firmwareSize < sizeof(PacHeader)) { - printf("file %s is not firmware", argv[1]); + + PacHeader firmwareHeader; + if (read(firmwareFile, &firmwareHeader, sizeof(PacHeader)) <= 0) { + perror("Error: Failed to read the firmware header"); + close(firmwareFile); exit(EXIT_FAILURE); } - - PacHeader pacHeader; - size_t rb =read(fd, &pacHeader, sizeof(PacHeader)); - if(rb <= 0) { - printf("Error while parsing PAC header"); + + char firmwareName[256], partitionFileName[256]; + getString(firmwareHeader.firmwareName, firmwareName); + printf("Firmware Name: %s\n", firmwareName); + + uint32_t partitionOffset = firmwareHeader.partitionsListStart; + PartitionHeader** partitionHeaders = malloc(sizeof(PartitionHeader*) * firmwareHeader.partitionCount); + if (!partitionHeaders) { + perror("Error: Memory allocation failed for partition headers"); + close(firmwareFile); exit(EXIT_FAILURE); } - char buffer[256]; - char buffer1[256]; - getString(pacHeader.firmwareName, buffer); - printf("Firmware name: %s\n", buffer); - uint32_t curPos = pacHeader.partitionsListStart; - PartitionHeader** partHeaders = malloc(sizeof(PartitionHeader**)*pacHeader.partitionCount); - int i; - for(i = 0; i < pacHeader.partitionCount; i++) { - lseek(fd, curPos, SEEK_SET); - uint32_t length; - rb =read(fd, &length, sizeof(uint32_t)); - if(rb <= 0) { - printf("Partition header error"); + for (int partitionIndex = 0; partitionIndex < firmwareHeader.partitionCount; partitionIndex++) { + lseek(firmwareFile, partitionOffset, SEEK_SET); + uint32_t partitionHeaderLength; + if (read(firmwareFile, &partitionHeaderLength, sizeof(uint32_t)) <= 0) { + perror("Error: Failed to read the partition header length"); + free(partitionHeaders); + close(firmwareFile); exit(EXIT_FAILURE); } - partHeaders[i] = malloc(length); - lseek(fd, curPos, SEEK_SET); - curPos += length; - rb =read(fd, partHeaders[i], length); - if(rb <= 0) { - printf("Partition header error"); + + partitionHeaders[partitionIndex] = malloc(partitionHeaderLength); + if (!partitionHeaders[partitionIndex]) { + perror("Error: Memory allocation failed for a partition header"); + free(partitionHeaders); + close(firmwareFile); exit(EXIT_FAILURE); } - getString(partHeaders[i]->partitionName, buffer); - getString(partHeaders[i]->fileName, buffer1); - printf("Partition name: %s\n\twith file name: %s\n\twith size %u\n", buffer, buffer1, partHeaders[i]->partitionSize); + + lseek(firmwareFile, partitionOffset, SEEK_SET); + partitionOffset += partitionHeaderLength; + if (read(firmwareFile, partitionHeaders[partitionIndex], partitionHeaderLength) <= 0) { + perror("Error: Failed to read the partition header"); + free(partitionHeaders[partitionIndex]); + free(partitionHeaders); + close(firmwareFile); + exit(EXIT_FAILURE); + } + + getString(partitionHeaders[partitionIndex]->partitionName, firmwareName); + getString(partitionHeaders[partitionIndex]->fileName, partitionFileName); + printf("Partition: %s\n\tFile Name: %s\n\tSize: %u bytes\n", firmwareName, partitionFileName, partitionHeaders[partitionIndex]->partitionSize); } - - for(i = 0; i < pacHeader.partitionCount; i++) { - if(partHeaders[i]->partitionSize == 0) { - free(partHeaders[i]); + + for (int partitionIndex = 0; partitionIndex < firmwareHeader.partitionCount; partitionIndex++) { + if (partitionHeaders[partitionIndex]->partitionSize == 0) { + free(partitionHeaders[partitionIndex]); continue; } - lseek(fd, partHeaders[i]->partitionAddrInPac, SEEK_SET); - getString(partHeaders[i]->fileName, buffer); - remove(buffer); - int fd_new = open(buffer, O_WRONLY | O_CREAT, 0666); - printf("Extract %s\n", buffer); - uint32_t dataSizeLeft = partHeaders[i]->partitionSize; - while(dataSizeLeft > 0) { - uint32_t copyLength = (dataSizeLeft > 256) ? 256 : dataSizeLeft; - dataSizeLeft -= copyLength; - rb =read(fd, buffer, copyLength); - if(rb != copyLength) { - printf("Partition image extraction error"); + + lseek(firmwareFile, partitionHeaders[partitionIndex]->partitionAddrInPac, SEEK_SET); + getString(partitionHeaders[partitionIndex]->fileName, partitionFileName); + + char outputFilePath[512]; + snprintf(outputFilePath, sizeof(outputFilePath), "%s/%s", outputDirectory, partitionFileName); + + if (access(outputFilePath, F_OK) == 0) { + printf("Error: The file '%s' already exists. Extraction aborted.\n", outputFilePath); + free(partitionHeaders[partitionIndex]); + free(partitionHeaders); + close(firmwareFile); + exit(EXIT_FAILURE); + } + + int outputFile = open(outputFilePath, O_WRONLY | O_CREAT, 0666); + if (outputFile == -1) { + perror("Error: Failed to create the output file"); + free(partitionHeaders[partitionIndex]); + free(partitionHeaders); + close(firmwareFile); + exit(EXIT_FAILURE); + } + + printf("Extracting: %s\n", outputFilePath); + uint32_t remainingDataSize = partitionHeaders[partitionIndex]->partitionSize; + while (remainingDataSize > 0) { + uint32_t chunkSize = (remainingDataSize > 256) ? 256 : remainingDataSize; + remainingDataSize -= chunkSize; + if (read(firmwareFile, firmwareName, chunkSize) != chunkSize) { + perror("Error: Failed to read partition data"); + close(outputFile); + free(partitionHeaders[partitionIndex]); + free(partitionHeaders); + close(firmwareFile); exit(EXIT_FAILURE); } - rb = write(fd_new, buffer, copyLength); - if(rb != copyLength) { - printf("Partition image extraction error"); + if (write(outputFile, firmwareName, chunkSize) != chunkSize) { + perror("Error: Failed to write partition data"); + close(outputFile); + free(partitionHeaders[partitionIndex]); + free(partitionHeaders); + close(firmwareFile); exit(EXIT_FAILURE); } - printf("\r\t%02d%%", (uint64_t)100 - (uint64_t)100*dataSizeLeft/partHeaders[i]->partitionSize); + printf("\r\tProgress: %02lu%%", (uint64_t)100 - (uint64_t)100 * remainingDataSize / partitionHeaders[partitionIndex]->partitionSize); } - printf("\n"); - close(fd_new); - free(partHeaders[i]); + close(outputFile); + free(partitionHeaders[partitionIndex]); } - free(partHeaders); - close(fd); - + + free(partitionHeaders); + close(firmwareFile); + printf("All partitions extracted successfully.\n"); return EXIT_SUCCESS; }