From d0682b385bc44a6be665a2b1bad5aa0ba6cad4a8 Mon Sep 17 00:00:00 2001 From: Wiktor Pyk <56189344+wiktorpp@users.noreply.github.com> Date: Thu, 17 Apr 2025 19:55:28 +0200 Subject: [PATCH 1/6] Update README.md --- README.md | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) 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 From e38773b00f6b19b46733c4c3caf935520faede1e Mon Sep 17 00:00:00 2001 From: Wiktor Pyk Date: Thu, 17 Apr 2025 20:21:18 +0200 Subject: [PATCH 2/6] Improve error messages and code formatting in pacextractor.c --- pacextractor.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/pacextractor.c b/pacextractor.c index 6992ed7..dd13a75 100644 --- a/pacextractor.c +++ b/pacextractor.c @@ -1,4 +1,3 @@ - #include #include #include @@ -51,31 +50,31 @@ void getString(int16_t* baseString, char* resString) { int main(int argc, char** argv) { if(argc < 2) { - printf("command format:\n capextractor .pac"); - exit(EXIT_FAILURE); + printf("Usage:\n capextractor .pac\n"); + exit(EXIT_FAILURE); } int fd = open(argv[1], O_RDONLY); if (fd == -1) { - printf("file %s is not find", argv[1]); + printf("Error: Unable to open file '%s'. Please check if the file exists.\n", argv[1]); exit(EXIT_FAILURE); } -// fseek(fd, 0, SEEK_END); -// int firmwareSize = (fd); -// fseek(fd, 0, SEEK_SET); + // 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]); + printf("Error: The file '%s' is too small to be a valid firmware file. Please check the file.\n", argv[1]); exit(EXIT_FAILURE); } PacHeader pacHeader; - size_t rb =read(fd, &pacHeader, sizeof(PacHeader)); + size_t rb = read(fd, &pacHeader, sizeof(PacHeader)); if(rb <= 0) { - printf("Error while parsing PAC header"); + printf("Error: Failed to read the PAC header from the file '%s'. The file may be corrupted.\n", argv[1]); exit(EXIT_FAILURE); } @@ -89,17 +88,17 @@ int main(int argc, char** argv) { for(i = 0; i < pacHeader.partitionCount; i++) { lseek(fd, curPos, SEEK_SET); uint32_t length; - rb =read(fd, &length, sizeof(uint32_t)); + rb = read(fd, &length, sizeof(uint32_t)); if(rb <= 0) { - printf("Partition header error"); + printf("Error: Failed to read the partition header length.\n"); exit(EXIT_FAILURE); } partHeaders[i] = malloc(length); lseek(fd, curPos, SEEK_SET); curPos += length; - rb =read(fd, partHeaders[i], length); + rb = read(fd, partHeaders[i], length); if(rb <= 0) { - printf("Partition header error"); + printf("Error: Failed to read the partition header.\n"); exit(EXIT_FAILURE); } getString(partHeaders[i]->partitionName, buffer); @@ -121,14 +120,14 @@ int main(int argc, char** argv) { while(dataSizeLeft > 0) { uint32_t copyLength = (dataSizeLeft > 256) ? 256 : dataSizeLeft; dataSizeLeft -= copyLength; - rb =read(fd, buffer, copyLength); + rb = read(fd, buffer, copyLength); if(rb != copyLength) { - printf("Partition image extraction error"); + printf("Error: Failed to extract partition data.\n"); exit(EXIT_FAILURE); } rb = write(fd_new, buffer, copyLength); if(rb != copyLength) { - printf("Partition image extraction error"); + printf("Error: Failed to write partition data.\n"); exit(EXIT_FAILURE); } printf("\r\t%02d%%", (uint64_t)100 - (uint64_t)100*dataSizeLeft/partHeaders[i]->partitionSize); From 62dae7fd859bdf08aca41094d62601edb52fcb94 Mon Sep 17 00:00:00 2001 From: Wiktor Pyk Date: Thu, 17 Apr 2025 20:27:59 +0200 Subject: [PATCH 3/6] Fix small indent error --- pacextractor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pacextractor.c b/pacextractor.c index dd13a75..205230e 100644 --- a/pacextractor.c +++ b/pacextractor.c @@ -51,7 +51,7 @@ void getString(int16_t* baseString, char* resString) { int main(int argc, char** argv) { if(argc < 2) { printf("Usage:\n capextractor .pac\n"); - exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } int fd = open(argv[1], O_RDONLY); From 7133abe45567d93ad66a839027402c6c8a1b2537 Mon Sep 17 00:00:00 2001 From: Wiktor Pyk Date: Mon, 21 Apr 2025 22:18:36 +0200 Subject: [PATCH 4/6] Added destination parameter --- pacextractor.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/pacextractor.c b/pacextractor.c index 205230e..297eee3 100644 --- a/pacextractor.c +++ b/pacextractor.c @@ -50,21 +50,27 @@ void getString(int16_t* baseString, char* resString) { int main(int argc, char** argv) { if(argc < 2) { - printf("Usage:\n capextractor .pac\n"); + printf("Usage:\n capextractor .pac [destination_directory]\n"); exit(EXIT_FAILURE); } - + + const char* destination = (argc >= 3) ? argv[2] : "."; + struct stat st; + if (stat(destination, &st) == -1 || !S_ISDIR(st.st_mode)) { + printf("Error: Destination '%s' is not a valid directory.\n", destination); + exit(EXIT_FAILURE); + } + int fd = open(argv[1], O_RDONLY); if (fd == -1) { printf("Error: Unable to open file '%s'. Please check if the file exists.\n", argv[1]); exit(EXIT_FAILURE); } - // fseek(fd, 0, SEEK_END); - // int firmwareSize = (fd); - // fseek(fd, 0, SEEK_SET); - struct stat st; - stat(argv[1], &st); + if (stat(argv[1], &st) == -1) { + printf("Error: Unable to stat file '%s'.\n", argv[1]); + exit(EXIT_FAILURE); + } int firmwareSize = st.st_size; if(firmwareSize < sizeof(PacHeader)) { printf("Error: The file '%s' is too small to be a valid firmware file. Please check the file.\n", argv[1]); @@ -113,9 +119,12 @@ int main(int argc, char** argv) { } 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); + + char outputPath[512]; + snprintf(outputPath, sizeof(outputPath), "%s/%s", destination, buffer); + remove(outputPath); + int fd_new = open(outputPath, O_WRONLY | O_CREAT, 0666); + printf("Extract %s\n", outputPath); uint32_t dataSizeLeft = partHeaders[i]->partitionSize; while(dataSizeLeft > 0) { uint32_t copyLength = (dataSizeLeft > 256) ? 256 : dataSizeLeft; @@ -130,7 +139,7 @@ int main(int argc, char** argv) { printf("Error: Failed to write partition data.\n"); exit(EXIT_FAILURE); } - printf("\r\t%02d%%", (uint64_t)100 - (uint64_t)100*dataSizeLeft/partHeaders[i]->partitionSize); + printf("\r\t%02lu%%", (uint64_t)100 - (uint64_t)100*dataSizeLeft/partHeaders[i]->partitionSize); } printf("\n"); close(fd_new); From 54c2743aff5380a228df63e6c1bd56c81e99e7d7 Mon Sep 17 00:00:00 2001 From: Wiktor Pyk Date: Mon, 21 Apr 2025 22:23:04 +0200 Subject: [PATCH 5/6] Prevent overwriting existing files during extraction --- pacextractor.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pacextractor.c b/pacextractor.c index 297eee3..bfe282a 100644 --- a/pacextractor.c +++ b/pacextractor.c @@ -122,9 +122,17 @@ int main(int argc, char** argv) { char outputPath[512]; snprintf(outputPath, sizeof(outputPath), "%s/%s", destination, buffer); - remove(outputPath); + + // Check if the file already exists + if (access(outputPath, F_OK) == 0) { + printf("Error: File '%s' already exists. Extraction aborted.\n", outputPath); + exit(EXIT_FAILURE); + } + + // Open a new file for writing the extracted partition data int fd_new = open(outputPath, O_WRONLY | O_CREAT, 0666); printf("Extract %s\n", outputPath); + uint32_t dataSizeLeft = partHeaders[i]->partitionSize; while(dataSizeLeft > 0) { uint32_t copyLength = (dataSizeLeft > 256) ? 256 : dataSizeLeft; From c289e6db0384653e3143b896e8ae9a714a97b9a2 Mon Sep 17 00:00:00 2001 From: Wiktor Pyk Date: Mon, 21 Apr 2025 22:48:12 +0200 Subject: [PATCH 6/6] Cleaned up and wrote better print strings --- pacextractor.c | 205 +++++++++++++++++++++++++++++-------------------- 1 file changed, 120 insertions(+), 85 deletions(-) diff --git a/pacextractor.c b/pacextractor.c index bfe282a..d15ff91 100644 --- a/pacextractor.c +++ b/pacextractor.c @@ -32,129 +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("Usage:\n capextractor .pac [destination_directory]\n"); + 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* destination = (argc >= 3) ? argv[2] : "."; - struct stat st; - if (stat(destination, &st) == -1 || !S_ISDIR(st.st_mode)) { - printf("Error: Destination '%s' is not a valid directory.\n", destination); + 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 fd = open(argv[1], O_RDONLY); - if (fd == -1) { - printf("Error: Unable to open file '%s'. Please check if the file exists.\n", argv[1]); + int firmwareFile = open(argv[1], O_RDONLY); + if (firmwareFile == -1) { + perror("Error: Unable to open the firmware file"); exit(EXIT_FAILURE); } - - if (stat(argv[1], &st) == -1) { - printf("Error: Unable to stat file '%s'.\n", 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); } - int firmwareSize = st.st_size; - if(firmwareSize < sizeof(PacHeader)) { - printf("Error: The file '%s' is too small to be a valid firmware file. Please check the file.\n", 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: Failed to read the PAC header from the file '%s'. The file may be corrupted.\n", argv[1]); + + 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("Error: Failed to read the partition header length.\n"); + 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("Error: Failed to read the partition header.\n"); + + 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); - char outputPath[512]; - snprintf(outputPath, sizeof(outputPath), "%s/%s", destination, buffer); + lseek(firmwareFile, partitionHeaders[partitionIndex]->partitionAddrInPac, SEEK_SET); + getString(partitionHeaders[partitionIndex]->fileName, partitionFileName); + + char outputFilePath[512]; + snprintf(outputFilePath, sizeof(outputFilePath), "%s/%s", outputDirectory, partitionFileName); - // Check if the file already exists - if (access(outputPath, F_OK) == 0) { - printf("Error: File '%s' already exists. Extraction aborted.\n", outputPath); + 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); } - // Open a new file for writing the extracted partition data - int fd_new = open(outputPath, O_WRONLY | O_CREAT, 0666); - printf("Extract %s\n", outputPath); - - 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("Error: Failed to extract partition data.\n"); + 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("Error: Failed to write partition data.\n"); + 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%02lu%%", (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; }