Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -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 <Firmware.pac>
```

Extract all data that I may recognize in package.
211 changes: 131 additions & 80 deletions pacextractor.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
Expand Down Expand Up @@ -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 <firmware name>.pac");
if (argc < 2) {
printf("Usage:\n pacextractor <firmware_file>.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;
}