void AndroidFormat::updateSha1Hash(BootImageHeader *hdr) { SHA_CTX ctx; SHA_init(&ctx); SHA_update(&ctx, mI10e->kernelImage.data(), mI10e->kernelImage.size()); SHA_update(&ctx, reinterpret_cast<char *>(&hdr->kernel_size), sizeof(hdr->kernel_size)); SHA_update(&ctx, mI10e->ramdiskImage.data(), mI10e->ramdiskImage.size()); SHA_update(&ctx, reinterpret_cast<char *>(&hdr->ramdisk_size), sizeof(hdr->ramdisk_size)); if (!mI10e->secondImage.empty()) { SHA_update(&ctx, mI10e->secondImage.data(), mI10e->secondImage.size()); } // Bug in AOSP? AOSP's mkbootimg adds the second bootloader size to the SHA1 // hash even if it's 0 SHA_update(&ctx, reinterpret_cast<char *>(&hdr->second_size), sizeof(hdr->second_size)); if (!mI10e->dtImage.empty()) { SHA_update(&ctx, mI10e->dtImage.data(), mI10e->dtImage.size()); SHA_update(&ctx, reinterpret_cast<char *>(&hdr->dt_size), sizeof(hdr->dt_size)); } std::memset(hdr->id, 0, sizeof(hdr->id)); memcpy(hdr->id, SHA_final(&ctx), SHA_DIGEST_SIZE); std::string hexDigest = StringUtils::toHex( reinterpret_cast<const unsigned char *>(hdr->id), SHA_DIGEST_SIZE); FLOGD("Computed new ID hash: %s", hexDigest.c_str()); }
PatchFilePatcher::PatchFilePatcher(const PatcherConfig * const pc, const FileInfo * const info, const PatchInfo::AutoPatcherArgs &args) : m_impl(new Impl()) { m_impl->pc = pc; m_impl->info = info; m_impl->args = args; // The arguments should have the patch to the file if (args.find(ArgFile) == args.end()) { LOGW("Arguments does not contain the path to a patch file"); return; } m_impl->patchFile = pc->dataDirectory() + "/patches/" + args.at(ArgFile); std::string contents; auto ret = FileUtils::readToString(m_impl->patchFile, &contents); if (!ret) { LOGW("Failed to read patch file"); return; } std::vector<std::string> lines; boost::split(lines, contents, boost::is_any_of("\n")); const std::regex reOrigFile("---\\s+(.+?)(?:$|\t)"); for (auto it = lines.begin(); it != lines.end(); ++it) { std::smatch what; if (std::regex_search(*it, what, reOrigFile)) { std::string file = what.str(1); if (boost::starts_with(file, "\"") && boost::ends_with(file, "\"")) { file.erase(file.begin()); file.erase(file.end() - 1); } // Skip files containing escaped characters if (file.find("\\") != std::string::npos) { FLOGW("Skipping file with escaped characters in filename: {}", file); continue; } // Strip leading slash (-p1) auto it = file.find("/"); if (it != std::string::npos) { file.erase(file.begin(), file.begin() + it + 1); } FLOGD("Found file in patch: {}", file); m_impl->files.push_back(file); } } }
bool LokiPatcher::patchImage(std::vector<unsigned char> *data, std::vector<unsigned char> aboot) { if (aboot.empty()) { FLOGE("[Loki] Aboot image cannot be empty"); return false; } // Prevent reading out of bounds data->resize((data->size() + 0x2000 + 0xfff) & ~0xfff); aboot.resize((aboot.size() + 0xfff) & ~0xfff); uint32_t target = 0; uint32_t abootBase = *reinterpret_cast<uint32_t *>(aboot.data() + 12) - 0x28; // Find the signature checking function via pattern matching for (const unsigned char *ptr = aboot.data(); ptr < aboot.data() + aboot.size() - 0x1000; ++ptr) { if (!memcmp(ptr, PATTERN1, 8) || !memcmp(ptr, PATTERN2, 8) || !memcmp(ptr, PATTERN3, 8) || !memcmp(ptr, PATTERN4, 8) || !memcmp(ptr, PATTERN5, 8)) { target = static_cast<uint32_t>(ptr - aboot.data() + abootBase); break; } } // Do a second pass for the second LG pattern. This is necessary because // apparently some LG models have both LG patterns, which throws off the // fingerprinting. if (!target) { for (const unsigned char *ptr = aboot.data(); ptr < aboot.data() + aboot.size() - 0x1000; ++ptr) { if (memcmp(ptr, PATTERN6, 8) == 0) { target = static_cast<uint32_t>(ptr - aboot.data() + abootBase); break; } } } if (!target) { LOGE("[Loki] Failed to find function to patch"); return false; } LokiTarget *tgt = nullptr; for (size_t i = 0; i < (sizeof(targets) / sizeof(targets[0])); ++i) { if (targets[i].check_sigs == target) { tgt = &targets[i]; break; } } if (!tgt) { LOGE("[Loki] Unsupported aboot image"); return false; } FLOGD("[Loki] Detected target %s %s build %s", tgt->vendor, tgt->device, tgt->build); BootImageHeader *hdr = reinterpret_cast<BootImageHeader *>(data->data()); LokiHeader *lokiHdr = reinterpret_cast<LokiHeader *>(data->data() + 0x400); // Set the Loki header memcpy(lokiHdr->magic, LOKI_MAGIC, LOKI_MAGIC_SIZE); lokiHdr->recovery = 0; strncpy(lokiHdr->build, tgt->build, sizeof(lokiHdr->build) - 1); uint32_t pageSize = hdr->page_size; uint32_t pageMask = hdr->page_size - 1; uint32_t origKernelSize = hdr->kernel_size; uint32_t origRamdiskSize = hdr->ramdisk_size; FLOGD("[Loki] Original kernel address: 0x%08x", hdr->kernel_addr); FLOGD("[Loki] Original ramdisk address: 0x%08x", hdr->ramdisk_addr); // Store the original values in unused fields of the header lokiHdr->orig_kernel_size = origKernelSize; lokiHdr->orig_ramdisk_size = origRamdiskSize; lokiHdr->ramdisk_addr = hdr->kernel_addr + ((hdr->kernel_size + pageMask) & ~pageMask); unsigned char patch[] = LOKI_SHELLCODE; if (!patchShellcode(tgt->hdr, hdr->ramdisk_addr, patch)) { LOGE("[Loki] Failed to patch shellcode"); return false; } // Ramdisk must be aligned to a page boundary hdr->kernel_size = ((hdr->kernel_size + pageMask) & ~pageMask) + hdr->ramdisk_size; // Guarantee 16-byte alignment int offset = tgt->check_sigs & 0xf; hdr->ramdisk_addr = tgt->check_sigs - offset; int fakeSize; if (tgt->lg) { fakeSize = pageSize; hdr->ramdisk_size = pageSize; } else { fakeSize = 0x200; hdr->ramdisk_size = 0; } std::vector<unsigned char> newImage; if (pageSize > data->size()) { FLOGE("[Loki] Header exceeds boot image size by %" PRIzu " bytes", pageSize - data->size()); return false; } // Write the image header newImage.insert(newImage.end(), data->data(), data->data() + pageSize); uint32_t pageKernelSize = (origKernelSize + pageMask) & ~pageMask; if (pageSize + pageKernelSize > data->size()) { FLOGE("[Loki] Kernel exceeds boot image size by %" PRIzu " bytes", pageSize + pageKernelSize - data->size()); return false; } // Write the kernel newImage.insert(newImage.end(), data->data() + pageSize, data->data() + pageSize + pageKernelSize); uint32_t pageRamdiskSize = (origRamdiskSize + pageMask) & ~pageMask; if (pageSize + pageKernelSize + pageRamdiskSize > data->size()) { FLOGE("[Loki] Ramdisk exceeds boot image size by %" PRIzu " bytes", pageSize + pageKernelSize + pageRamdiskSize - data->size()); return false; } // Write the ramdisk newImage.insert(newImage.end(), data->data() + pageSize + pageKernelSize, data->data() + pageSize + pageKernelSize + pageRamdiskSize); if (tgt->check_sigs - abootBase - offset + fakeSize > aboot.size()) { FLOGE("[Loki] Requested aboot segment exceeds aboot size by %" PRIzu " bytes", tgt->check_sigs - abootBase - offset + fakeSize - aboot.size()); return false; } // Write fake size bytes of original code to the output newImage.insert(newImage.end(), aboot.data() + tgt->check_sigs - abootBase - offset, aboot.data() + tgt->check_sigs - abootBase - offset + fakeSize); // Save this position for later uint32_t pos = newImage.size(); if (hdr->dt_size) { LOGD("[Loki] Writing device tree"); if (pageSize + pageKernelSize + pageRamdiskSize + hdr->dt_size > data->size()) { FLOGE("[Loki] Device tree image exceeds boot image size by %" PRIzu " bytes", pageSize + pageKernelSize + pageRamdiskSize + hdr->dt_size - data->size()); return false; } newImage.insert(newImage.end(), data->data() + pageSize + pageKernelSize + pageRamdiskSize, data->data() + pageSize + pageKernelSize + pageRamdiskSize + hdr->dt_size); } // Write the patch memcpy(newImage.data() + pos - (fakeSize - offset), patch, sizeof(patch)); LOGD("[Loki] Patching completed"); data->swap(newImage); return true; }
void EdifyTokenizer::dump(const std::vector<EdifyToken *> &tokens) { const char *tokenName = nullptr; for (std::size_t i = 0; i < tokens.size(); ++i) { EdifyToken *t = tokens[i]; switch (t->type()) { case EdifyTokenType::If: tokenName = "If"; break; case EdifyTokenType::Then: tokenName = "Then"; break; case EdifyTokenType::Else: tokenName = "Else"; break; case EdifyTokenType::Endif: tokenName = "Endif"; break; case EdifyTokenType::And: tokenName = "And"; break; case EdifyTokenType::Or: tokenName = "Or"; break; case EdifyTokenType::Equals: tokenName = "Equals"; break; case EdifyTokenType::NotEquals: tokenName = "NotEquals"; break; case EdifyTokenType::Not: tokenName = "Not"; break; case EdifyTokenType::LeftParen: tokenName = "LeftParen"; break; case EdifyTokenType::RightParen: tokenName = "RightParen"; break; case EdifyTokenType::Semicolon: tokenName = "Semicolon"; break; case EdifyTokenType::Comma: tokenName = "Comma"; break; case EdifyTokenType::Concat: tokenName = "Concat"; break; case EdifyTokenType::Newline: tokenName = "Newline"; break; case EdifyTokenType::Whitespace: tokenName = "Whitespace"; break; case EdifyTokenType::Comment: tokenName = "Comment"; break; case EdifyTokenType::String: tokenName = "String"; break; case EdifyTokenType::Unknown: tokenName = "Unknown"; break; } FLOGD("%" PRIzu ": %-20s: %s", i, tokenName, t->generate().c_str()); } }
bool AndroidFormat::loadImage(const unsigned char *data, std::size_t size) { std::size_t headerIndex; if (!findHeader(data, size, 512, &headerIndex)) { LOGE("Failed to find Android header in boot image"); return false; } FLOGD("Found Android boot image header at: %" PRIzu, headerIndex); if (!loadHeader(data, size, headerIndex)) { return false; } uint32_t pos = 0; // Save kernel image pos += sizeof(BootImageHeader); pos += skipPadding(sizeof(BootImageHeader), mI10e->pageSize); if (pos + mI10e->hdrKernelSize > size) { FLOGE("Kernel image exceeds boot image size by %" PRIzu " bytes", pos + mI10e->hdrKernelSize - size); return false; } mI10e->kernelImage.assign(data + pos, data + pos + mI10e->hdrKernelSize); // Save ramdisk image pos += mI10e->hdrKernelSize; pos += skipPadding(mI10e->hdrKernelSize, mI10e->pageSize); if (pos + mI10e->hdrRamdiskSize > size) { FLOGE("Ramdisk image exceeds boot image size by %" PRIzu " bytes", pos + mI10e->hdrRamdiskSize - size); return false; } mI10e->ramdiskImage.assign(data + pos, data + pos + mI10e->hdrRamdiskSize); // Save second bootloader image pos += mI10e->hdrRamdiskSize; pos += skipPadding(mI10e->hdrRamdiskSize, mI10e->pageSize); if (pos + mI10e->hdrSecondSize > size) { FLOGE("Second bootloader image exceeds boot image size by %" PRIzu " bytes", pos + mI10e->hdrSecondSize - size); return false; } // The second bootloader may not exist if (mI10e->hdrSecondSize > 0) { mI10e->secondImage.assign(data + pos, data + pos + mI10e->hdrSecondSize); } else { mI10e->secondImage.clear(); } // Save device tree image pos += mI10e->hdrSecondSize; pos += skipPadding(mI10e->hdrSecondSize, mI10e->pageSize); if (pos + mI10e->hdrDtSize > size) { std::size_t diff = pos + mI10e->hdrDtSize - size; FLOGE("WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING"); FLOGE("THIS BOOT IMAGE MAY NO LONGER BE BOOTABLE. YOU HAVE BEEN WARNED"); FLOGE("Device tree image exceeds boot image size by %" PRIzu " bytes and HAS BEEN TRUNCATED", diff); FLOGE("WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING"); mI10e->dtImage.assign(data + pos, data + pos + mI10e->hdrDtSize - diff); } else { mI10e->dtImage.assign(data + pos, data + pos + mI10e->hdrDtSize); } // The device tree image may not exist as well if (mI10e->hdrDtSize == 0) { mI10e->dtImage.clear(); } pos += mI10e->hdrDtSize; pos += skipPadding(mI10e->hdrDtSize, mI10e->pageSize); return true; }
void AndroidFormat::dumpHeader(const BootImageHeader *hdr) { FLOGD("- magic: %s", std::string(hdr->magic, hdr->magic + BOOT_MAGIC_SIZE).c_str()); FLOGD("- kernel_size: %u", hdr->kernel_size); FLOGD("- kernel_addr: 0x%08x", hdr->kernel_addr); FLOGD("- ramdisk_size: %u", hdr->ramdisk_size); FLOGD("- ramdisk_addr: 0x%08x", hdr->ramdisk_addr); FLOGD("- second_size: %u", hdr->second_size); FLOGD("- second_addr: 0x%08x", hdr->second_addr); FLOGD("- tags_addr: 0x%08x", hdr->tags_addr); FLOGD("- page_size: %u", hdr->page_size); FLOGD("- dt_size: %u", hdr->dt_size); FLOGD("- unused: 0x%08x", hdr->unused); FLOGD("- name: %s", StringUtils::toMaxString( reinterpret_cast<const char *>(hdr->name), BOOT_NAME_SIZE).c_str()); FLOGD("- cmdline: %s", StringUtils::toMaxString( reinterpret_cast<const char *>(hdr->cmdline), BOOT_ARGS_SIZE).c_str()); FLOGD("- id: %s", StringUtils::toHex( reinterpret_cast<const unsigned char *>(hdr->id), 32).c_str()); }