diff --git a/.gitignore b/.gitignore index c76b767..226cf91 100644 --- a/.gitignore +++ b/.gitignore @@ -241,6 +241,7 @@ UEFIDump/UEFIDump UEFIExtract/UEFIExtract UEFIFind/UEFIFind UEFIReplace/UEFIReplace +UEFIPatch/UEFIPatch .qmake.stash CMakeCache.txt CMakeFiles diff --git a/UEFIPatch/CMakeLists.txt b/UEFIPatch/CMakeLists.txt new file mode 100644 index 0000000..8a800d6 --- /dev/null +++ b/UEFIPatch/CMakeLists.txt @@ -0,0 +1,69 @@ +cmake_minimum_required(VERSION 3.0) + +PROJECT(UEFIPatch) + +SET(PROJECT_SOURCES + uefipatch_main.cpp + uefipatch.cpp + ../common/guiddatabase.cpp + ../common/types.cpp + ../common/descriptor.cpp + ../common/ffs.cpp + ../common/nvram.cpp + ../common/nvramparser.cpp + ../common/ffsparser.cpp + ../common/ffsbuilder.cpp + ../common/ffsops.cpp + ../common/peimage.cpp + ../common/treeitem.cpp + ../common/treemodel.cpp + ../common/utility.cpp + ../common/LZMA/LzmaCompress.c + ../common/LZMA/LzmaDecompress.c + ../common/LZMA/SDK/C/LzFind.c + ../common/LZMA/SDK/C/LzmaDec.c + ../common/LZMA/SDK/C/LzmaEnc.c + ../common/Tiano/EfiTianoDecompress.c + ../common/Tiano/EfiTianoCompress.c + ../common/Tiano/EfiTianoCompressLegacy.c + ../common/ustring.cpp + ../common/sha256.c + ../common/bstrlib/bstrlib.c + ../common/bstrlib/bstrwrap.cpp +) + +SET(PROJECT_HEADERS + uefipatch.h + ../common/guiddatabase.h + ../common/basetypes.h + ../common/descriptor.h + ../common/gbe.h + ../common/me.h + ../common/ffs.h + ../common/nvram.h + ../common/nvramparser.h + ../common/ffsparser.h + ../common/ffsbuilder.h + ../common/ffsops.h + ../common/peimage.h + ../common/types.h + ../common/treeitem.h + ../common/treemodel.h + ../common/utility.h + ../common/LZMA/LzmaDecompress.h + ../common/Tiano/EfiTianoDecompress.h + ../common/LZMA/LzmaCompress.h + ../common/Tiano/EfiTianoCompress.h + ../common/ubytearray.h + ../common/ustring.h + ../common/bootguard.h + ../common/sha256.h + ../common/filesystem.h + ../common/bstrlib/bstrlib.h + ../common/bstrlib/bstrwrap.h + ../version.h +) + +ADD_DEFINITIONS(-DU_ENABLE_NVRAM_PARSING_SUPPORT -DU_ENABLE_FIT_PARSING_SUPPORT -DU_ENABLE_GUID_DATABASE_SUPPORT) + +ADD_EXECUTABLE(UEFIPatch ${PROJECT_SOURCES} ${PROJECT_HEADERS}) diff --git a/UEFIPatch/patches-misc.txt b/UEFIPatch/patches-misc.txt new file mode 100755 index 0000000..68305ce --- /dev/null +++ b/UEFIPatch/patches-misc.txt @@ -0,0 +1,37 @@ +# Patch string format +# FileGuid SectionType PatchType:FindPatternOrOffset:ReplacePattern +# Please ensure that the latest symbol in patch string is space + +# Possible section types: +# PE32 image 10 +# Position-independent code 11 +# TE Image 12 +# DXE Dependency 13 +# Version information 14 +# User interface string 15 +# 16-bit code 16 +# Guided freeform 18 +# Raw data 19 +# PEI Dependency 1B +# SMM Dependency 1C +# Please do not try another section types, it can make the resulting image broken + +# Possible patch types: +# P - pattern-based, first parameter is a pattern to find, second - a pattern to replace +# O - offset-based, first parameter is hexadecimal offset, second - a pattern to replace +# Patterns can have . as "any possible value" symbol + +#---------------------------------------------------------------------------------- +# Set IA32_FEATURE_CONTROL.SGX_LC +#---------------------------------------------------------------------------------- + +# SiInit | Gemini Lake +299D6F8B-2EC9-4E40-9EC6-DDAA7EBF5FD9 10 P:B93A0000000F320D00000400:B93A0000000F320D00000600 + +#---------------------------------------------------------------------------------- +# Broken EFI_SIMPLE_POINTER_PROTOCOL implementation patches +# Fixes incorrect pointer position update/click condition on Z87 chips +#---------------------------------------------------------------------------------- + +# UHCD | ASUS Z87-Pro +580DD900-385D-11D7-883A-00500473D4EB 10 P:807A3200745C807A4000:807A32007506807A4000 diff --git a/UEFIPatch/patches.txt b/UEFIPatch/patches.txt new file mode 100755 index 0000000..a097f03 --- /dev/null +++ b/UEFIPatch/patches.txt @@ -0,0 +1,59 @@ +# Patch string format +# FileGuid SectionType PatchType:FindPatternOrOffset:ReplacePattern +# Please ensure that the latest symbol in patch string is space + +# Possible section types: +# PE32 image 10 +# Position-independent code 11 +# TE Image 12 +# DXE Dependency 13 +# Version information 14 +# User interface string 15 +# 16-bit code 16 +# Guided freeform 18 +# Raw data 19 +# PEI Dependency 1B +# SMM Dependency 1C +# Please do not try another section types, it can make the resulting image broken + +# Possible patch types: +# P - pattern-based, first parameter is a pattern to find, second - a pattern to replace +# O - offset-based, first parameter is hexadecimal offset, second - a pattern to replace +# Patterns can have . as "any possible value" symbol + +#---------------------------------------------------------------------------------- +# OSX CPU Power Management patches +# Remove lock from MSR 0xE2 register +#---------------------------------------------------------------------------------- + +# PowerMgmtDxe | Haswell +F7731B4C-58A2-4DF4-8980-5645D39ECE58 10 P:75080FBAE80F89442430:EB080FBAE80F89442430 + +# PowerMgmtDxe | Haswell-E +F7731B4C-58A2-4DF4-8980-5645D39ECE58 10 P:0FBA6C24380F:0FBA7424380F + +# PowerManagement | Sandy Bridge with ME 8.xx, Ivy Bridge +8C783970-F02A-4A4D-AF09-8797A51EEC8D 10 P:75080FBAE80F89442430:EB080FBAE80F89442430 + +# PowerManagement | New SB-E/IB-E +8C783970-F02A-4A4D-AF09-8797A51EEC8D 10 P:0FBA6C24380F:0FBA7424380F + +# CpuPei | Sandy Bridge with ME 7.xx, old SB-E/IB-E +2BB5AFA9-FF33-417B-8497-CB773C2B93BF 10 P:800018EB050D0080:000018EB050D0000 + +# PpmInitialize | Broadwell-E +3FFCAE95-23CF-4967-94F5-16352F68E43B 10 P:0FBA6C24400F:0FBA7424400F + +# SiInit | Skylake +299D6F8B-2EC9-4E40-9EC6-DDAA7EBF5FD9 10 P:75080D00800000:EB080D00800000 +299D6F8B-2EC9-4E40-9EC6-DDAA7EBF5FD9 12 P:75080D00800000:EB080D00800000 + +# SiInit | Kaby Lake +299D6F8B-2EC9-4E40-9EC6-DDAA7EBF5FD9 10 P:81E10080000033C1:9090909090909090 +299D6F8B-2EC9-4E40-9EC6-DDAA7EBF5FD9 12 P:81E10080000033C1:9090909090909090 + +# PpmInitialize | Skylake-X +3FFCAE95-23CF-4967-94F5-16352F68E43B 10 P:742CB9E2000000:752CB9E2000000 + +# CpuInitPei | Skylake-X +01359D99-9446-456D-ADA4-50A711C03ADA 12 P:BE0080000023CE0B:BE0000000023CE0B diff --git a/UEFIPatch/uefipatch.cpp b/UEFIPatch/uefipatch.cpp new file mode 100755 index 0000000..d5210c5 --- /dev/null +++ b/UEFIPatch/uefipatch.cpp @@ -0,0 +1,156 @@ +/* uefipatch.cpp + +Copyright (c) 2018, LongSoft. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +*/ + +#include "uefipatch.h" + +UEFIPatch::UEFIPatch() +{ + model = new TreeModel(); + ffsParser = new FfsParser(model); + ffsBuilder = new FfsBuilder(model, ffsParser); + ffsOps = new FfsOperations(model); +} + +UEFIPatch::~UEFIPatch() +{ + delete ffsOps; + delete ffsBuilder; + delete ffsParser; + delete model; +} + +USTATUS UEFIPatch::patchFromFile(const UString & inPath, const UString & patches, const UString & outPath) +{ + if (!isExistOnFs(patches)) + return U_INVALID_FILE; + + std::ifstream patchesFile(patches.toLocal8Bit()); + if (!patchesFile) + return U_INVALID_FILE; + + UByteArray buffer; + USTATUS result = readFileIntoArray(inPath, buffer); + if (result) + return result; + + result = ffsParser->parse(buffer); + if (result) + return result; + + UINT8 counter = 0; + while (!patchesFile.eof()) { + std::string line; + std::getline(patchesFile, line); + // Use sharp symbol as commentary + if (line.size() == 0 || line[0] == '#') + continue; + + // Split the read line + std::vector list; + std::string::size_type prev = 0, curr = 0; + while ((curr = line.find(' ', curr)) != std::string::npos) { + std::string substring( line.substr(prev, curr-prev) ); + list.push_back(UString(substring.c_str())); + prev = ++curr; + } + list.push_back(UString(line.substr(prev, curr-prev).c_str())); + + if (list.size() < 3) + continue; + + EFI_GUID guid; + const char *sectionTypeStr = list[1].toLocal8Bit(); + char *converted = const_cast(sectionTypeStr); + UINT8 sectionType = (UINT8)std::strtol(sectionTypeStr, &converted, 16); + if (converted == sectionTypeStr || !ustringToGuid(list[0], guid)) + return U_INVALID_PARAMETER; + + std::vector patches; + + for (size_t i = 2; i < list.size(); i++) { + std::vector patchList; + std::string patchStr = list.at(i).toLocal8Bit(); + std::string::size_type prev = 0, curr = 0; + while ((curr = line.find(':', curr)) != std::string::npos) { + std::string substring( line.substr(prev, curr-prev) ); + patchList.push_back(UByteArray(substring)); + prev = ++curr; + } + patchList.push_back(UByteArray(line.substr(prev, curr-prev))); + + PatchData patch; + patch.type = *(UINT8*)patchList.at(0).constData(); + if (patch.type == PATCH_TYPE_PATTERN) { + patch.offset = 0xFFFFFFFF; + patch.hexFindPattern = patchList.at(1); + patch.hexReplacePattern = patchList.at(2); + patches.push_back(patch); + } + else if (patch.type == PATCH_TYPE_OFFSET) { + patch.offset = patchList.at(1).toUInt(NULL, 16); + patch.hexReplacePattern = patchList.at(2); + patches.push_back(patch); + } + else { + // Ignore unknown patch type + continue; + } + } + result = patchFile(model->index(0, 0), UByteArray((const char*)&guid, sizeof(EFI_GUID)), sectionType, patches); + if (result && result != U_NOTHING_TO_PATCH) + return result; + counter++; + } + + UByteArray reconstructed; + result = ffsBuilder->build(model->index(0,0), reconstructed); + if (result) + return result; + if (reconstructed == buffer) + return U_NOTHING_TO_PATCH; + + std::ofstream outputFile; + outputFile.open(outPath.toLocal8Bit(), std::ios::out | std::ios::binary); + outputFile.write(reconstructed.constData(), reconstructed.size()); + outputFile.close(); + + return U_SUCCESS; +} + +USTATUS UEFIPatch::patchFile(const UModelIndex & index, const UByteArray & fileGuid, const UINT8 sectionType, const std::vector & patches) +{ + if (!model || !index.isValid()) + return U_INVALID_PARAMETER; + if (model->type(index) == Types::Section && model->subtype(index) == sectionType) { + UModelIndex fileIndex = model->findParentOfType(index, Types::File); + if (model->type(fileIndex) == Types::File && + model->header(fileIndex).left(sizeof(EFI_GUID)) == fileGuid) + { + // return ffsOps->patch(index, patches); + #warning "ffsOps->patch is not implemented!" + return U_NOTHING_TO_PATCH; + } + } + + if (model->rowCount(index) > 0) { + for (int i = 0; i < model->rowCount(index); i++) { + USTATUS result = patchFile(index.child(i, 0), fileGuid, sectionType, patches); + if (!result) + break; + else if (result != U_NOTHING_TO_PATCH) + return result; + } + } + + return U_NOTHING_TO_PATCH; +} diff --git a/UEFIPatch/uefipatch.h b/UEFIPatch/uefipatch.h new file mode 100755 index 0000000..5bad070 --- /dev/null +++ b/UEFIPatch/uefipatch.h @@ -0,0 +1,52 @@ +/* uefipatch.h + +Copyright (c) 2018, LongSoft. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +*/ + +#ifndef __UEFIPATCH_H__ +#define __UEFIPATCH_H__ + +#include +#include +#include + +#include "../common/basetypes.h" +#include "../common/ustring.h" +#include "../common/filesystem.h" +#include "../common/ffs.h" +#include "../common/ffsparser.h" +#include "../common/ffsops.h" +#include "../common/ffsbuilder.h" +#include "../common/utility.h" + +struct PatchData { + UINT8 type; + UINT32 offset; + UByteArray hexFindPattern; + UByteArray hexReplacePattern; +}; + +class UEFIPatch +{ +public: + explicit UEFIPatch(); + ~UEFIPatch(); + + USTATUS patchFromFile(const UString & inPath, const UString & patches, const UString & outPath); +private: + USTATUS patchFile(const UModelIndex & index, const UByteArray & fileGuid, const UINT8 sectionType, const std::vector & patches); + TreeModel* model; + FfsParser* ffsParser; + FfsBuilder* ffsBuilder; + FfsOperations* ffsOps; +}; + +#endif diff --git a/UEFIPatch/uefipatch_main.cpp b/UEFIPatch/uefipatch_main.cpp new file mode 100755 index 0000000..d25a00c --- /dev/null +++ b/UEFIPatch/uefipatch_main.cpp @@ -0,0 +1,90 @@ +/* uefipatch_main.cpp + +Copyright (c) 2018, LongSoft. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +*/ +#include +#include +#include +#include +#include +#include + +#include "../version.h" +#include "uefipatch.h" + +int main(int argc, char *argv[]) +{ + UEFIPatch w; + USTATUS result = U_SUCCESS; + UINT32 argumentsCount = argc; + + if (argumentsCount < 2) { + std::cout << "UEFIPatch " PROGRAM_VERSION " - UEFI image file patching utility" << std::endl << std::endl << + "Usage: UEFIPatch image_file [patches.txt] [-o output]" << std::endl << std::endl << + "Patches will be read from patches.txt file by default\n"; + return U_SUCCESS; + } + + UString inputPath = argv[1]; + UString patches = "patches.txt"; + UString outputPath = inputPath + ".patched"; + for (UINT32 i = 2; i < argumentsCount; i++) { + if ((!std::strcmp(argv[i], "-o") || !std::strcmp(argv[i], "--output")) && i + 1 < argumentsCount) { + outputPath = argv[i+1]; + i++; + } else if (patches == UString("patches.txt")) { + patches = argv[i]; + } else { + result = U_INVALID_PARAMETER; + } + } + + if (result == U_SUCCESS) { + result = w.patchFromFile(inputPath, patches, outputPath); + } + + switch (result) { + case U_SUCCESS: + std::cout << "Image patched" << std::endl; + break; + case U_INVALID_PARAMETER: + std::cout << "Function called with invalid parameter" << std::endl; + break; + case U_NOTHING_TO_PATCH: + std::cout << "No patches can be applied to input file" << std::endl; + break; + case U_UNKNOWN_PATCH_TYPE: + std::cout << "Unknown patch type" << std::endl; + break; + case U_PATCH_OFFSET_OUT_OF_BOUNDS: + std::cout << "Patch offset out of bounds" << std::endl; + break; + case U_INVALID_SYMBOL: + std::cout << "Pattern format mismatch" << std::endl; + break; + case U_INVALID_FILE: + std::cout << patches.toLocal8Bit() << " file not found or can't be read" << std::endl; + break; + case U_FILE_OPEN: + std::cout << "Input file not found" << std::endl; + break; + case U_FILE_READ: + std::cout << "Input file can't be read" << std::endl; + break; + case U_FILE_WRITE: + std::cout << "Output file can't be written" << std::endl; + break; + default: + std::cout << "Error " << result << std::endl; + } + + return result; +} diff --git a/common/basetypes.h b/common/basetypes.h index c44b37c..b09ff66 100644 --- a/common/basetypes.h +++ b/common/basetypes.h @@ -71,6 +71,9 @@ typedef size_t USTATUS; #define U_INVALID_STORE_SIZE 49 #define U_UNKNOWN_COMPRESSION_ALGORITHM 50 #define U_NOTHING_TO_PATCH 51 +#define U_UNKNOWN_PATCH_TYPE 52 +#define U_PATCH_OFFSET_OUT_OF_BOUNDS 53 +#define U_INVALID_SYMBOL 54 #define U_NOT_IMPLEMENTED 0xFF // UDK porting definitions