/* Copyright (c) 2010-2011 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // C/C++ Standard Library #include #include #include #include // libpit #include "libpit.h" // Heimdall #include "BridgeManager.h" #include "SetupSessionPacket.h" #include "SetupSessionResponse.h" #include "EndModemFileTransferPacket.h" #include "EndPhoneFileTransferPacket.h" #include "Interface.h" using namespace std; using namespace Heimdall; // Known partitions enum { kKnownPartitionPit = 0, kKnownPartitionFactoryFs, kKnownPartitionCache, kKnownPartitionDatabaseData, kKnownPartitionPrimaryBootloader, kKnownPartitionSecondaryBootloader, kKnownPartitionSecondaryBootloaderBackup, kKnownPartitionParam, kKnownPartitionKernel, kKnownPartitionRecovery, kKnownPartitionEfs, kKnownPartitionModem, kKnownPartitionNormalBoot, kKnownPartitionSystem, kKnownPartitionUserData, kKnownPartitionFota, kKnownPartitionHidden, kKnownPartitionMovinand, kKnownPartitionData, kKnownPartitionUms, kKnownPartitionEmmc, kKnownPartitionCount }; vector knownPartitionNames[kKnownPartitionCount]; struct PartitionInfo { unsigned int partitionType; string partitionName; FILE *file; PartitionInfo(unsigned int partitionType, const char *partitionName, FILE *file) { this->partitionType = partitionType; this->partitionName = partitionName; this->file = file; } }; void initialiseKnownPartitionNames(void) { knownPartitionNames[kKnownPartitionPit].push_back("PIT"); knownPartitionNames[kKnownPartitionFactoryFs].push_back("FACTORYFS"); knownPartitionNames[kKnownPartitionCache].push_back("CACHE"); knownPartitionNames[kKnownPartitionDatabaseData].push_back("DBDATAFS"); knownPartitionNames[kKnownPartitionPrimaryBootloader].push_back("IBL+PBL"); knownPartitionNames[kKnownPartitionPrimaryBootloader].push_back("BOOT"); knownPartitionNames[kKnownPartitionSecondaryBootloader].push_back("SBL"); knownPartitionNames[kKnownPartitionSecondaryBootloader].push_back("SBL1"); knownPartitionNames[kKnownPartitionSecondaryBootloaderBackup].push_back("SBL2"); knownPartitionNames[kKnownPartitionParam].push_back("PARAM"); knownPartitionNames[kKnownPartitionKernel].push_back("KERNEL"); knownPartitionNames[kKnownPartitionRecovery].push_back("RECOVERY"); knownPartitionNames[kKnownPartitionEfs].push_back("EFS"); knownPartitionNames[kKnownPartitionModem].push_back("MODEM"); knownPartitionNames[kKnownPartitionNormalBoot].push_back("NORMALBOOT"); knownPartitionNames[kKnownPartitionSystem].push_back("SYSTEM"); knownPartitionNames[kKnownPartitionUserData].push_back("USERDATA"); knownPartitionNames[kKnownPartitionFota].push_back("FOTA"); knownPartitionNames[kKnownPartitionHidden].push_back("HIDDEN"); knownPartitionNames[kKnownPartitionMovinand].push_back("MOVINAND"); knownPartitionNames[kKnownPartitionData].push_back("DATAFS"); knownPartitionNames[kKnownPartitionUms].push_back("UMS.EN"); knownPartitionNames[kKnownPartitionEmmc].push_back("GANG"); } bool isKnownPartition(const char *partitionName, unsigned int knownPartitionIndex) { for (unsigned int i = 0; i < knownPartitionNames[knownPartitionIndex].size(); i++) { if (strcmp(partitionName, knownPartitionNames[knownPartitionIndex][i]) == 0) return (true); } return (false); } bool isKnownBootPartition(const char *partitionName) { return (isKnownPartition(partitionName, kKnownPartitionPrimaryBootloader) || isKnownPartition(partitionName, kKnownPartitionSecondaryBootloader) || isKnownPartition(partitionName, kKnownPartitionSecondaryBootloaderBackup) || isKnownPartition(partitionName, kKnownPartitionParam) || isKnownPartition(partitionName, kKnownPartitionNormalBoot)); } bool openFiles(const map& argumentMap, map& argumentFileMap) { map::const_iterator it = argumentMap.begin(); for (it = argumentMap.begin(); it != argumentMap.end(); it++) { bool isFileArgument = false; int partitionIndex = atoi(it->first.substr(it->first.find_first_not_of('-')).c_str()); // Was the argument a partition index? if (partitionIndex > 0 || it->first.compare("-0") == 0) { isFileArgument = true; } else { // The argument wasn't a partition index, check if it's a known partition name. for (int knownPartition = 0; knownPartition < kKnownPartitionCount; knownPartition++) { if (it->first == Interface::actions[Interface::kActionFlash].valueArguments[knownPartition]) { isFileArgument = true; break; } } } if (!isFileArgument) continue; pair argumentFilePair; argumentFilePair.first = it->first; argumentFilePair.second = fopen(it->second.c_str(), "rb"); if (!argumentFilePair.second) { Interface::PrintError("Failed to open file \"%s\"\n", it->second.c_str()); return (false); } argumentFileMap.insert(argumentFilePair); } return (true); } bool mapFilesToPartitions(const map& argumentFileMap, const PitData *pitData, map& partitionInfoMap) { map::const_iterator it = argumentFileMap.begin(); for (it = argumentFileMap.begin(); it != argumentFileMap.end(); it++) { int partitionIndex = atoi(it->first.substr(it->first.find_first_not_of('-')).c_str()); const PitEntry *pitEntry = nullptr; // Was the argument a partition index? if (partitionIndex > 0 || it->first.compare("-0") == 0) { pitEntry = pitData->FindEntry(partitionIndex); } else { // The argument wasn't a partition index, so it must be a known partition name. int knownPartition; for (knownPartition = 0; knownPartition < kKnownPartitionCount; knownPartition++) { if (it->first == Interface::actions[Interface::kActionFlash].valueArguments[knownPartition]) break; } // Check for the partition in the PIT file using all known names. for (unsigned int i = 0; i < knownPartitionNames[knownPartition].size(); i++) { pitEntry = pitData->FindEntry(knownPartitionNames[knownPartition][i]); if (pitEntry) break; } if (!pitEntry && knownPartition == kKnownPartitionPit) { // NOTE: We're assuming a PIT file always has partitionType zero. PartitionInfo partitionInfo(0, knownPartitionNames[kKnownPartitionPit][0], it->second); partitionInfoMap.insert(pair(0xFFFFFFFF, partitionInfo)); return (true); } } if (!pitEntry) { Interface::PrintError("Partition corresponding to %s argument could not be located\n", it->first.c_str()); return (false); } PartitionInfo partitionInfo(pitEntry->GetPartitionType(), pitEntry->GetPartitionName(), it->second); partitionInfoMap.insert(pair(pitEntry->GetPartitionIdentifier(), partitionInfo)); } return (true); } void closeFiles(map argumentfileMap) { for (map::iterator it = argumentfileMap.begin(); it != argumentfileMap.end(); it++) fclose(it->second); argumentfileMap.clear(); } int downloadPitFile(BridgeManager *bridgeManager, unsigned char **pitBuffer) { Interface::Print("Downloading device's PIT file...\n"); int devicePitFileSize = bridgeManager->ReceivePitFile(pitBuffer); if (!*pitBuffer) { Interface::PrintError("Failed to download PIT file!\n"); return (-1); } Interface::Print("PIT file download sucessful\n\n"); return (devicePitFileSize); } bool flashFile(BridgeManager *bridgeManager, unsigned int partitionType, unsigned int partitionIndex, const char *partitionName, FILE *file) { // PIT files need to be handled differently, try determine if the partition we're flashing to is a PIT partition. bool isPit = false; for (unsigned int i = 0; i < knownPartitionNames[kKnownPartitionPit].size(); i++) { if (strcmp(partitionName, knownPartitionNames[kKnownPartitionPit][i]) == 0) { isPit = true; break; } } if (isPit) { Interface::Print("Uploading %s\n", partitionName); if (bridgeManager->SendPitFile(file)) { Interface::Print("%s upload successful\n", partitionName); return (true); } else { Interface::Print("%s upload failed!\n", partitionName); return (false); } } else { // Modems need to be handled differently, try determine if the partition we're flashing to is a modem partition. bool isModem = false; for (unsigned int i = 0; i < knownPartitionNames[kKnownPartitionModem].size(); i++) { if (strcmp(partitionName, knownPartitionNames[kKnownPartitionModem][i]) == 0) { isModem = true; break; } } if (isModem) { Interface::Print("Uploading %s\n", partitionName); //if (bridgeManager->SendFile(file, EndPhoneFileTransferPacket::kDestinationPhone, // <-- Kies method. WARNING: Doesn't work on Galaxy Tab! // EndPhoneFileTransferPacket::kFileModem)) if (bridgeManager->SendFile(file, EndModemFileTransferPacket::kDestinationModem, partitionType)) // <-- Odin method { Interface::Print("%s upload successful\n", partitionName); return (true); } else { Interface::Print("%s upload failed!\n", partitionName); return (false); } } else { // We're uploading to a phone partition Interface::Print("Uploading %s\n", partitionName); if (bridgeManager->SendFile(file, EndPhoneFileTransferPacket::kDestinationPhone, partitionType, partitionIndex)) { Interface::Print("%s upload successful\n", partitionName); return (true); } else { Interface::Print("%s upload failed!\n", partitionName); return (false); } } } return (true); } bool attemptFlash(BridgeManager *bridgeManager, map argumentFileMap, bool repartition) { bool success; // ------------- SEND TOTAL BYTES TO BE TRANSFERRED ------------- int totalBytes = 0; for (map::const_iterator it = argumentFileMap.begin(); it != argumentFileMap.end(); it++) { if (repartition || it->first != Interface::GetPitArgument()) { fseek(it->second, 0, SEEK_END); totalBytes += ftell(it->second); rewind(it->second); } } SetupSessionPacket *deviceInfoPacket = new SetupSessionPacket(SetupSessionPacket::kTotalBytes, totalBytes); success = bridgeManager->SendPacket(deviceInfoPacket); delete deviceInfoPacket; if (!success) { Interface::PrintError("Failed to send total bytes device info packet!\n"); return (false); } SetupSessionResponse *deviceInfoResponse = new SetupSessionResponse(); success = bridgeManager->ReceivePacket(deviceInfoResponse); int deviceInfoResult = deviceInfoResponse->GetUnknown(); delete deviceInfoResponse; if (!success) { Interface::PrintError("Failed to receive device info response!\n"); return (false); } if (deviceInfoResult != 0) { Interface::PrintError("Unexpected device info response!\nExpected: 0\nReceived:%d\n", deviceInfoResult); return (false); } // ----------------------------------------------------- PitData *pitData; PitData *localPitData = nullptr; FILE *localPitFile = nullptr; // If a PIT file was passed as an argument then we must unpack it. map::iterator it = argumentFileMap.find(Interface::actions[Interface::kActionFlash].valueArguments[Interface::kFlashValueArgPit]); if (it != argumentFileMap.end()) { localPitFile = it->second; // Load the local pit file into memory. unsigned char *pitFileBuffer = new unsigned char[4096]; memset(pitFileBuffer, 0, 4096); fseek(localPitFile, 0, SEEK_END); long localPitFileSize = ftell(localPitFile); rewind(localPitFile); // dataRead is discarded, it's here to remove warnings. int dataRead = fread(pitFileBuffer, 1, localPitFileSize, localPitFile); rewind(localPitFile); localPitData = new PitData(); localPitData->Unpack(pitFileBuffer); delete [] pitFileBuffer; } if (repartition) { // Use the local PIT file data. pitData = localPitData; } else { // If we're not repartitioning then we need to retrieve the device's PIT file and unpack it. unsigned char *pitFileBuffer; downloadPitFile(bridgeManager, &pitFileBuffer); pitData = new PitData(); pitData->Unpack(pitFileBuffer); delete [] pitFileBuffer; if (localPitData != nullptr) { // The user has specified a PIT without repartitioning, we should verify the local and device PIT data match! bool pitsMatch = pitData->Matches(localPitData); delete localPitData; if (!pitsMatch) { Interface::Print("Local and device PIT files don't match and repartition wasn't specified!\n"); Interface::PrintError("Flash aborted!\n"); delete pitData; return (false); } } } map partitionInfoMap; // Map the files being flashed to partitions stored in the PIT file. if (!mapFilesToPartitions(argumentFileMap, pitData, partitionInfoMap)) { delete pitData; return (false); } delete pitData; // If we're repartitioning then we need to flash the PIT file first. if (repartition) { for (map::iterator it = partitionInfoMap.begin(); it != partitionInfoMap.end(); it++) { if (it->second.file == localPitFile) { PartitionInfo *partitionInfo = &(it->second); if (!flashFile(bridgeManager, partitionInfo->partitionType, it->first, partitionInfo->partitionName.c_str(), partitionInfo->file)) return (false); break; } } } // Flash partitions not involved in the boot process second. for (map::iterator it = partitionInfoMap.begin(); it != partitionInfoMap.end(); it++) { if (!isKnownPartition(it->second.partitionName.c_str(), kKnownPartitionPit) && !isKnownBootPartition(it->second.partitionName.c_str())) { PartitionInfo *partitionInfo = &(it->second); if (!flashFile(bridgeManager, partitionInfo->partitionType, it->first, partitionInfo->partitionName.c_str(), partitionInfo->file)) return (false); } } // Flash boot partitions last. for (map::iterator it = partitionInfoMap.begin(); it != partitionInfoMap.end(); it++) { if (isKnownBootPartition(it->second.partitionName.c_str())) { PartitionInfo *partitionInfo = &(it->second); if (!flashFile(bridgeManager, partitionInfo->partitionType, it->first, partitionInfo->partitionName.c_str(), partitionInfo->file)) return (false); } } return (true); } int main(int argc, char **argv) { map argumentMap; int actionIndex; if (!Interface::GetArguments(argc, argv, argumentMap, &actionIndex)) { Sleep(250); return (0); } initialiseKnownPartitionNames(); switch (actionIndex) { case Interface::kActionFlash: if (argumentMap.find(Interface::actions[Interface::kActionFlash].valuelessArguments[Interface::kFlashValuelessArgRepartition]) != argumentMap.end() && argumentMap.find(Interface::actions[Interface::kActionFlash].valueArguments[Interface::kFlashValueArgPit]) == argumentMap.end()) { Interface::Print("If you wish to repartition then a PIT file must be specified.\n\n"); Interface::PrintUsage(); return (0); } break; case Interface::kActionDownloadPit: if (argumentMap.find(Interface::actions[Interface::kActionDownloadPit].valueArguments[Interface::kDownloadPitValueArgOutput]) == argumentMap.end()) { Interface::Print("Output file was not specified.\n\n"); Interface::PrintUsage(); return (0); } break; case Interface::kActionDump: { if (argumentMap.find(Interface::actions[Interface::kActionDump].valueArguments[Interface::kDumpValueArgOutput]) == argumentMap.end()) { Interface::Print("Output file was not specified.\n\n"); Interface::PrintUsage(); return (0); } if (argumentMap.find(Interface::actions[Interface::kActionDump].valueArguments[Interface::kDumpValueArgChipType]) == argumentMap.end()) { Interface::Print("You must specify a chip type.\n\n"); Interface::PrintUsage(); return (0); } string chipType = argumentMap.find(Interface::actions[Interface::kActionDump].valueArguments[Interface::kDumpValueArgChipType])->second; if (!(chipType == "RAM" || chipType == "ram" || chipType == "NAND" || chipType == "nand")) { Interface::Print("Unknown chip type: %s.\n\n", chipType.c_str()); Interface::PrintUsage(); return (0); } if (argumentMap.find(Interface::actions[Interface::kActionDump].valueArguments[Interface::kDumpValueArgChipId]) == argumentMap.end()) { Interface::Print("You must specify a Chip ID.\n\n"); Interface::PrintUsage(); return (0); } int chipId = atoi(argumentMap.find(Interface::actions[Interface::kActionDump].valueArguments[Interface::kDumpValueArgChipId])->second.c_str()); if (chipId < 0) { Interface::Print("Chip ID must be a non-negative integer.\n"); Interface::PrintUsage(); return (0); } break; } case Interface::kActionVersion: Interface::PrintVersion(); return (0); case Interface::kActionHelp: Interface::PrintUsage(); return (0); case Interface::kActionInfo: Interface::PrintFullInfo(); return (0); } bool verbose = argumentMap.find(Interface::commonValuelessArguments[Interface::kCommonValuelessArgVerbose]) != argumentMap.end(); bool reboot = argumentMap.find(Interface::commonValuelessArguments[Interface::kCommonValuelessArgNoReboot]) == argumentMap.end(); Interface::SetStdoutErrors(argumentMap.find(Interface::commonValuelessArguments[Interface::kCommonValuelessArgStdoutErrors]) != argumentMap.end()); int communicationDelay = BridgeManager::kCommunicationDelayDefault; if (argumentMap.find(Interface::commonValueArguments[Interface::kCommonValueArgDelay]) != argumentMap.end()) communicationDelay = atoi(argumentMap.find(Interface::commonValueArguments[Interface::kCommonValueArgDelay])->second.c_str()); BridgeManager *bridgeManager = new BridgeManager(verbose, communicationDelay); if (actionIndex == Interface::kActionDetect) { bool detected = bridgeManager->DetectDevice(); delete bridgeManager; return ((detected) ? 0 : 1); } Interface::PrintReleaseInfo(); Sleep(1000); int initialiseResult = bridgeManager->Initialise(); if (initialiseResult != 0) { delete bridgeManager; return ((initialiseResult == BridgeManager::kInitialiseDeviceNotDetected) ? 1 : 0); } bool success; switch (actionIndex) { case Interface::kActionFlash: { map argumentFileMap; // We open the files before doing anything else to ensure they exist. if (!openFiles(argumentMap, argumentFileMap)) { closeFiles(argumentFileMap); delete bridgeManager; return (0); } if (!bridgeManager->BeginSession()) { closeFiles(argumentFileMap); delete bridgeManager; return (-1); } bool repartition = argumentMap.find(Interface::actions[Interface::kActionFlash].valuelessArguments[Interface::kFlashValuelessArgRepartition]) != argumentMap.end(); success = attemptFlash(bridgeManager, argumentFileMap, repartition); success = bridgeManager->EndSession(reboot) && success; closeFiles(argumentFileMap); break; } case Interface::kActionClosePcScreen: { if (!bridgeManager->BeginSession()) { delete bridgeManager; return (-1); } Interface::Print("Attempting to close connect to pc screen...\n"); success = bridgeManager->EndSession(reboot); if (success) Interface::Print("Attempt complete\n"); break; } case Interface::kActionDownloadPit: { map::const_iterator it = argumentMap.find(Interface::actions[Interface::kActionDownloadPit].valueArguments[Interface::kDownloadPitValueArgOutput]); FILE *outputPitFile = fopen(it->second.c_str(), "wb"); if (!outputPitFile) { delete bridgeManager; return (0); } if (!bridgeManager->BeginSession()) { delete bridgeManager; fclose(outputPitFile); return (-1); } unsigned char *pitBuffer; int fileSize = downloadPitFile(bridgeManager, &pitBuffer); if (fileSize > 0) { success = fwrite(pitBuffer, 1, fileSize, outputPitFile) == fileSize; fclose(outputPitFile); if (!success) Interface::PrintError("Failed to write PIT data to output file.\n"); success = bridgeManager->EndSession(reboot) && success; } else { fclose(outputPitFile); success = false; bridgeManager->EndSession(reboot); } delete [] pitBuffer; break; } case Interface::kActionDump: { const char *outputFilename = argumentMap.find(Interface::actions[Interface::kActionDump].valueArguments[Interface::kDumpValueArgOutput])->second.c_str(); FILE *dumpFile = fopen(outputFilename, "wb"); if (!dumpFile) { Interface::PrintError("Failed to open file \"%s\"\n", outputFilename); delete bridgeManager; return (-1); } int chipType = 0; string chipTypeName = argumentMap.find(Interface::actions[Interface::kActionDump].valueArguments[Interface::kDumpValueArgChipType])->second; if (chipTypeName == "NAND" || chipTypeName == "nand") chipType = 1; int chipId = atoi(argumentMap.find(Interface::actions[Interface::kActionDump].valueArguments[Interface::kDumpValueArgChipId])->second.c_str()); if (!bridgeManager->BeginSession()) { fclose(dumpFile); delete bridgeManager; return (-1); } success = bridgeManager->ReceiveDump(chipType, chipId, dumpFile); fclose(dumpFile); success = bridgeManager->EndSession(reboot) && success; break; } case Interface::kActionPrintPit: { if (!bridgeManager->BeginSession()) { delete bridgeManager; return (-1); } unsigned char *devicePit; if (downloadPitFile(bridgeManager, &devicePit) < -1) { bridgeManager->EndSession(reboot); delete bridgeManager; return (-1); } PitData *pitData = new PitData(); if (pitData->Unpack(devicePit)) { Interface::PrintPit(pitData); success = true; } else { Interface::PrintError("Failed to unpack device's PIT file!\n"); success = false; } delete [] devicePit; delete pitData; success = bridgeManager->EndSession(reboot) && success; break; } } delete bridgeManager; return ((success) ? 0 : -1); }