Skip to content

Instantly share code, notes, and snippets.

@CreepNT
Last active September 16, 2022 21:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CreepNT/b7bd861e2f6d01e992e54ac919982528 to your computer and use it in GitHub Desktop.
Save CreepNT/b7bd861e2f6d01e992e54ac919982528 to your computer and use it in GitHub Desktop.
CP eMMC part1 parsing/decryption tool
/**
* @file bootprof_parser.cpp
* @author CreepNT
* @brief Reads a raw Boot Profile from part1 and prints its different parts
* @date 16-09-2022
*
* Copyright (C) 2022 CreepNT
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cstdio>
#include <cstdlib>
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
typedef struct {
DWORD magic;
DWORD size;
} BootProfileHdr;
static_assert(sizeof(BootProfileHdr) == 8, "");
typedef struct {
BYTE type[2];
USHORT subtype;
USHORT unused;
USHORT len;
} BootProfileSectionHdr;
static_assert(sizeof(BootProfileSectionHdr) == 8, "");
#define ST_PROFILE_NAME 0x10 //Max size 0x20 - Creates a new profile and sets its name. Everything that follows is saved in that profile.
#define ST_KERNEL_PATH 0x11 //Max size 0x100 - Path to the kernel file (.bin/.bin.gz/.trf/.srec/.srec.gz/.elf)
#define ST_KERNEL_CMDLINE 0x12 //Max size 0x100 - Kernel command line
#define ST_INITRD_PATH 0x13 //Max size 0x100 - Path to the initrd file
#define ST_PREBOOT_PARAM 0x15 //Max size 0x100 - Preboot param. ?passed as a command to some kind of shell interpreter?
#define ST_TIMEOUT 0x16 //Integer - Accepted but unused.
#define ST_RETRY 0x17 //Integer - Maximum number of boot tries before switching to recovery profile.
#define ST_SIGN 0x18 //Needs to be 0x201 bytes (0x200+\0). Each character is the hex digit for 4 bits of the RSA signature of something.
#define ST_FSIMAGE_PATH 0x19 //Max size 0x100 - Path(es?) to fsimage files, mounted by Linux.
void parse_bp(const BootProfileHdr* hdr) {
if (hdr->magic != 0x42745066) {
fprintf(stderr, "Bad magic 0x%08X!\n", hdr->magic);
return;
}
fprintf(stderr, "Boot Profile is 0x%X bytes long, starting @ %p.\n\n", hdr->size, hdr);
DWORD processedSize = sizeof(BootProfileHdr);
while (processedSize < hdr->size) {
BootProfileSectionHdr* sectionHdr = (BootProfileSectionHdr*)((ULONG_PTR)hdr + processedSize);
DWORD type = (sectionHdr->type[1] << 8) | sectionHdr->type[0];
DWORD subtype = sectionHdr->subtype;
DWORD slen = sectionHdr->len;
void* payload = (void*)((ULONG_PTR)sectionHdr + sizeof(BootProfileSectionHdr));
/*
fprintf(stderr, "Section@0x%04X (payload@0x%04X, size 0x%X, aligned to 0x%X):\n",
processedSize, processedSize + 8, slen, (slen + 3) & ~3
);
*/
if (type == 1) {
if (subtype != 1 && subtype != 2) {
fprintf(stderr, "Unexpected subtype 0x%X for type %d @ 0x%X.\n", subtype, type, processedSize);
} else {
const char* profType = (subtype == 1) ? "Normal" : "Recovery";
fprintf(stderr, "%s Kernel Profile Name is \"%s\".\n", profType, (char*)payload);
}
} else if (type == 2) {
switch (subtype) {
case ST_PROFILE_NAME:
fprintf(stderr, "\n-----\nProfile \"%s\":\n", (char*)payload); break;
case ST_KERNEL_PATH:
fprintf(stderr, " Kernel file: %s\n", (char*)payload); break;
case ST_KERNEL_CMDLINE:
fprintf(stderr, " Kernel commandline: %s\n", (char*)payload); break;
case ST_INITRD_PATH:
fprintf(stderr, " initrd file: %s\n", (char*)payload); break;
case ST_PREBOOT_PARAM:
fprintf(stderr, " Preboot param: %s\n", (char*)payload); break;
case ST_TIMEOUT:
fprintf(stderr, " Timeout: %d\n", *(unsigned*)payload); break;
case ST_RETRY:
fprintf(stderr, " Retry count: %d\n", *(unsigned*)payload); break;
case ST_SIGN:
fprintf(stderr, " RSA Signature: %s\n", (char*)payload); break;
case ST_FSIMAGE_PATH:
fprintf(stderr, " fsimage file(s): %s\n", (char*)payload); break;
default:
{
fprintf(stderr, " [Unexpected subtype 0x%X for type %d @ 0x%X.]\n", subtype, type, processedSize);
break;
}
}
} else {
fprintf(stderr, "Unexpected type 0x%X @ 0x%X.\n", type, processedSize);
}
processedSize += 8 + ((slen + 3) & ~3);
}
if (processedSize > hdr->size) {
fprintf(stderr, "WARNING: processed %d more bytes than expected!\n", processedSize - hdr->size);
}
}
int wmain(int argc, WCHAR** argv) {
if (argc >= 2) {
fprintf(stderr, "Parsing %S...\n", argv[1]);
HANDLE fh = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (fh == INVALID_HANDLE_VALUE) {
fprintf(stderr, "CreateFile: %d\n", GetLastError());
return EXIT_FAILURE;
}
HANDLE map = CreateFileMapping(fh, NULL, PAGE_READONLY, 0, 0, NULL);
if (map == NULL) {
fprintf(stderr, "CreateFileMapping: %d\n", GetLastError());
CloseHandle(fh);
return EXIT_FAILURE;
}
LPVOID mapping = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
if (mapping == NULL) {
fprintf(stderr, "MapViewOfFile: %d\n", GetLastError());
CloseHandle(map);
CloseHandle(fh);
return EXIT_FAILURE;
}
parse_bp((BootProfileHdr*)mapping);
UnmapViewOfFile(mapping);
CloseHandle(map);
CloseHandle(fh);
return EXIT_SUCCESS;
}
fprintf(stderr, "Not enough arguments.\n");
return EXIT_FAILURE;
}
/**
* @file cpfwdec.cpp
* @author CreepNT
* @brief Decrypts stage2 from a CP eMMC's partition1
* @date 11-09-2022
*
* Copyright (C) 2022 CreepNT
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cstdint>
#include <cassert>
#include <cstdio>
#include <string>
typedef uint64_t sizetype_t;
#if defined(_WIN32) || defined(_WIN64)
#define _UNICODE
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <tchar.h>
#include <wchar.h>
#define MAIN_FUNCTION_NAME wmain
#define PRINTF wprintf
#define FOPEN _wfopen_s
#define FTELL _ftelli64
typedef wchar_t pathchar_t;
using string = std::wstring;
#else
#define MAIN_FUNCTION_NAME main
#define PRINTF printf
#define FOPEN fopen_s
#define FTELL ftell
typedef char pathchar_t;
using string = std::string;
#define _T(x) x
#endif
#define MINIBOOT_SIZE (0x1000ULL)
#define MINIBOOT_ENCBLOB_OFFSET (0xFE0)
//Helpers
static void aescbc_dec(uint32_t* buffer, size_t len, uint32_t riv, uint32_t erk) {
assert((len & 0x3) == 0 && "Misaligned buffer.");
uint32_t* end = &buffer[len/4];
uint32_t iv = riv;
for (;buffer != end; buffer++) {
uint32_t elem = *buffer;
*buffer = elem ^ erk ^ iv;
iv = elem;
}
}
static uint32_t CRC32_POLY[256] = {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
static uint32_t crc32(uint32_t seed, const void* buffer, sizetype_t len) {
const uint8_t* _buf = (const uint8_t*)buffer;
uint32_t _crc = ~seed;
for (sizetype_t i = 0; i < len; i++) {
unsigned index = (_buf[i] ^ _crc) & 0xFF;
_crc = CRC32_POLY[index] ^ (_crc >> 8);
}
return ~_crc;
}
static inline uint32_t _lcrng(uint32_t seed) {
return (seed * 1103515245) + 12345;
}
//Miniboot
typedef struct _miniboot_param {
uint32_t unk0;
uint32_t miniboot_crc32;
uint32_t emmc_blob_len;
uint32_t emmc_blob_crc32;
uint32_t emmc_blob_riv;
uint32_t emmc_blob_erk;
uint32_t emmc_blob_sz1;
uint32_t emmc_blob_sz2;
} miniboot_param;
static_assert(sizeof(miniboot_param) == 0x20, "Bad miniboot_param size!");
/**
* @brief Bruteforces the seed for the encrypted blob in miniboot and decrypts it in place
*
* @param miniboot Full Miniboot blob (0x1000 bytes)
* @return The seed on success, < 0 on failure
* @note On failure, miniboot blob is untouched.
*/
int decrypt_miniboot_encblob(void* miniboot) {
void* const blob_ptr = (void*)((char*)miniboot + MINIBOOT_ENCBLOB_OFFSET);
uint8_t encblob_backup[0x20];
memcpy(encblob_backup, blob_ptr, 0x20);
for (int seed = 0; seed <= 0xFF; seed++) {
//Restore fresh copy
memcpy(blob_ptr, encblob_backup, 0x20);
unsigned riv = _lcrng(seed);
unsigned erk = _lcrng(riv);
//Decrypt with this seed
aescbc_dec((uint32_t*)blob_ptr, 0x20, riv, erk);
//Is decryption valid? Check CRC32
//HACK: assume little-endian host
miniboot_param* param = (miniboot_param*)blob_ptr;
uint32_t blob_crc32 = param->miniboot_crc32;
param->miniboot_crc32 = 0;
uint32_t calc_crc32 = crc32(0, miniboot, 0x1000);
//The blob's CRC32 matches the file's!
//Decryption success!
if (blob_crc32 == calc_crc32) {
return seed;
}
}
memcpy(blob_ptr, encblob_backup, 0x20); //Restore the original encblob
return -1;
}
/**
* @brief Read the contents of a file in a buffer
*
* @param path Path to the file
* @param ppBuffer Receives pointer to buffer
* @param pBufferSize Receives size of file (=size of buffer)
* @return >= on success, < 0 on error
*
* @note Call delete[] to free the buffer.
*/
errno_t read_file(const pathchar_t* path, void** ppBuffer, sizetype_t* pBufferSize) {
*ppBuffer = nullptr;
*pBufferSize = 0;
FILE* fd;
errno_t res = FOPEN(&fd, path, _T("rb"));
if (res < 0) {
return res;
}
fseek(fd, 0L, SEEK_END);
sizetype_t fileSz = FTELL(fd);
fseek(fd, 0L, SEEK_SET);
void* buf = (void*)new char[fileSz];
sizetype_t rlen = fread_s(buf, fileSz, 1, fileSz, fd);
fclose(fd);
if (rlen != fileSz) {
printf("%llx %llx\n", rlen, fileSz);
delete[] buf;
int error = errno;
return (error) ? error : -EIO;
} else {
*ppBuffer = buf;
*pBufferSize = fileSz;
return 0;
}
}
/**
* @brief Write a buffer to a file
*
* @param path Path to the file
* @param buffer Buffer to be written
* @param len Length to write
* @return >= 0 on success, < 0 on error
*/
errno_t write_file(const pathchar_t* path, const void* buffer, sizetype_t len) {
FILE* fd;
errno_t res = FOPEN(&fd, path, _T("wb"));
if (res < 0) {
return res;
}
sizetype_t wlen = fwrite(buffer, 1, len, fd);
fclose(fd);
if (wlen != len) {
int error = errno;
return (error) ? error : -EIO;
}
return 0;
}
static const pathchar_t* infileName;
static const pathchar_t* outfileName;
static bool emitDecMinitboot = false;
bool parse_args(int argc, pathchar_t** argv) {
static string outfileNameString;
for (int i = 0; i < argc; i++) {
pathchar_t* arg = argv[i];
if (arg[0] == _T('-') && arg[1] == _T('m') && !emitDecMinitboot) {
emitDecMinitboot = true;
} else if (infileName == nullptr) {
infileName = arg;
} else if (outfileName == nullptr) {
outfileName = arg;
} else {
return false;
}
}
if (outfileName == nullptr) {
outfileNameString = infileName;
outfileNameString += _T(".dec");
outfileName = outfileNameString.c_str();
}
return true;
}
int MAIN_FUNCTION_NAME(int argc, pathchar_t** argv) {
if (argc < 2) {
PRINTF(_T("%s: Not enough arguments.\n"), (argc == 1) ? argv[0] : _T("cpfwdec"));
print_usage:
PRINTF(_T("Syntax: %s <path to part1.bin> [path to decrypted stage2] [-m]\n\n"), (argc >= 1) ? argv[0] : _T("cpfwdec"));
PRINTF(_T("Pass -m to write miniboot.bin with decrypted blob to <input>.miniboot_dec\n"));
return EXIT_SUCCESS;
}
if (!parse_args(argc - 1, &argv[1])) {
PRINTF(_T("Invalid arguments.\n"));
goto print_usage;
}
PRINTF(_T("Decrypting %s to %s...\n"), infileName, outfileName);
sizetype_t infileSz;
char* filebuffer;
errno_t res = read_file(infileName, (void**)&filebuffer, &infileSz);
if (res < 0) {
PRINTF(_T("Error reading %s: %d\n"), infileName, res);
return EXIT_FAILURE;
}
PRINTF(_T("%s: file size = 0x%llX (%llu) bytes\n"), infileName, infileSz, infileSz);
if (infileSz < MINIBOOT_SIZE) {
PRINTF(_T("Input file too small! The minimal file size is 0x1000. Are you sure this is a part1.bin?\n"));
delete[] filebuffer;
return EXIT_FAILURE;
}
PRINTF(_T("Bruteforcing Miniboot encblob's decryption seed...\n"));
int minibootSeed = decrypt_miniboot_encblob(filebuffer);
if (minibootSeed < 0) {
PRINTF(_T("Couldn't find Miniboot encblob's decryption seed!\n"));
delete[] filebuffer;
return EXIT_FAILURE;
}
PRINTF(_T("Bruteforced Miniboot encblob keyseed! Seed=0x%02X, encblob key RIV=0x%08X, ERK=0x%08X."),
minibootSeed, _lcrng(minibootSeed), _lcrng(_lcrng(minibootSeed))
);
if (emitDecMinitboot) {
string minibootDecName = infileName;
minibootDecName += _T(".miniboot_dec.bin");
const pathchar_t* const minibootDecPath = minibootDecName.c_str();
res = write_file(minibootDecPath, filebuffer, 0x1000);
if (res < 0) {
PRINTF(_T("Failed writing Miniboot with decrypted blob to %s: %d\n"), minibootDecPath, res);
} else {
PRINTF(_T("Wrote Miniboot with decrypted blob to %s.\n"), minibootDecPath);
}
}
//HACK: assume little-endian host
miniboot_param* blobPtr = (miniboot_param*)(filebuffer + MINIBOOT_ENCBLOB_OFFSET);
if ((infileSz - MINIBOOT_SIZE) < blobPtr->emmc_blob_len) {
PRINTF(_T("Cannot process stage2: file too small! The file must be at least 0x%llX bytes."),
blobPtr->emmc_blob_len + MINIBOOT_SIZE
);
delete[] filebuffer;
return EXIT_FAILURE;
}
uint32_t* const stage2 = (uint32_t*)(filebuffer + MINIBOOT_SIZE);
const sizetype_t stage2Sz = blobPtr->emmc_blob_len;
PRINTF(_T("Verifying stage2 (size 0x%llX)...\n"), stage2Sz);
uint32_t stage2CRC32 = crc32(0, stage2, stage2Sz);
if (stage2CRC32 != blobPtr->emmc_blob_crc32) {
PRINTF(_T("CRC32 mismatch! 0x%08X != 0x%08X\n"), stage2CRC32, blobPtr->emmc_blob_crc32);
PRINTF(_T("Are you sure stage2 hasn't been tampered with?\n"));
delete[] filebuffer;
return EXIT_FAILURE;
}
PRINTF(_T("Decrypting stage2...\n"));
PRINTF(_T(" (RIV=0x%08X, ERK=0x%08X)\n"), blobPtr->emmc_blob_riv, blobPtr->emmc_blob_erk);
aescbc_dec(stage2, blobPtr->emmc_blob_len, blobPtr->emmc_blob_riv, blobPtr->emmc_blob_erk);
res = write_file(outfileName, stage2, blobPtr->emmc_blob_len);
if (res < 0) {
PRINTF(_T("Failed writing decrypted stage2 to %s: %d\n"), outfileName, res);
} else {
PRINTF(_T("Wrote decrypted stage2 to %s.\n"), outfileName);
}
PRINTF(_T("Finished!\n"));
delete[] filebuffer;
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment