UEFIPatch 0.1.0

- new command-line utility to patch files inside UEFI image
- corrected bug with wrong header size calculation for
GUID_DEFINED_SECTION with PROCESSING_REQUIRED attribute set
- patch routine implemented in ffsEngine, will be added to UEFITool soon
This commit is contained in:
Nikolaj Schlej 2014-06-19 05:45:20 +02:00
parent 02a240ba18
commit 1c34c1bf84
10 changed files with 408 additions and 11 deletions

17
UEFIPatch/patches.txt Normal file
View File

@ -0,0 +1,17 @@
# PowerMgmtDxe | Haswell
F7731B4C-58A2-4DF4-8980-5645D39ECE58 75080FBAE80F89442430 EB080FBAE80F89442430
# PowerMgmtDxe | Haswell-E
F7731B4C-58A2-4DF4-8980-5645D39ECE58 0FBA6C24380F 0FBA7424380F
# PowerManagement | Sandy Bridge with ME 8.xx, Ivy Bridge
8C783970-F02A-4A4D-AF09-8797A51EEC8D 75080FBAE80F89442430 EB080FBAE80F89442430
# PowerManagement | New SB-E/IB-E
8C783970-F02A-4A4D-AF09-8797A51EEC8D 0FBA6C24380F 0FBA7424380F
# CpuPei | Sandy Bridge with ME 7.xx, old SB-E/IB-E
2BB5AFA9-FF33-417B-8497-CB773C2B93BF 800018EB050D0080 000018EB050D0000
# You can add your own patch here
# Format is simple: "file_GUID search_pattern replace_pattern"
# String beginned with '#' symbol is a comment and will be ignored by the program

165
UEFIPatch/uefipatch.cpp Normal file
View File

@ -0,0 +1,165 @@
/* uefipatch.cpp
Copyright (c) 2014, Nikolaj Schlej. 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(QObject *parent) :
QObject(parent)
{
ffsEngine = new FfsEngine(this);
model = ffsEngine->treeModel();
}
UEFIPatch::~UEFIPatch()
{
delete ffsEngine;
}
UINT8 UEFIPatch::patchFromFile(QString path)
{
QFileInfo patchInfo = QFileInfo("patches.txt");
if (!patchInfo.exists())
return ERR_INVALID_FILE;
QFile file;
file.setFileName("patches.txt");
if (!file.open(QFile::ReadOnly | QFile::Text))
return ERR_INVALID_FILE;
QFileInfo fileInfo = QFileInfo(path);
if (!fileInfo.exists())
return ERR_FILE_OPEN;
QFile inputFile;
inputFile.setFileName(path);
if (!inputFile.open(QFile::ReadOnly))
return ERR_FILE_READ;
QByteArray buffer = inputFile.readAll();
inputFile.close();
UINT8 result = ffsEngine->parseImageFile(buffer);
if (result)
return result;
while (!file.atEnd()) {
QByteArray line = file.readLine();
// Use sharp sign as commentary
if (line.count() == 0 || line[0] == '#')
continue;
QList<QByteArray> list = line.split(' ');
if (list.count() < 3)
continue;
QUuid uuid = QUuid(list.at(0));
QByteArray guid = QByteArray::fromRawData((const char*)&uuid.data1, sizeof(EFI_GUID));
result = patchFile(model->index(0, 0), guid, QByteArray::fromHex(list.at(1)), QByteArray::fromHex(list.at(2)));
if (result)
return result;
}
QByteArray reconstructed;
result = ffsEngine->reconstructImageFile(reconstructed);
if (result)
return result;
if (reconstructed == buffer) {
return ERR_ITEM_NOT_FOUND;
}
QFile outputFile;
outputFile.setFileName(path.append(".patched"));
if (!outputFile.open(QFile::WriteOnly))
return ERR_FILE_WRITE;
outputFile.resize(0);
outputFile.write(reconstructed);
outputFile.close();
return ERR_SUCCESS;
}
UINT8 UEFIPatch::patch(QString path, QString fileGuid, QString findPattern, QString replacePattern)
{
QFileInfo fileInfo = QFileInfo(path);
if (!fileInfo.exists())
return ERR_FILE_OPEN;
QFile inputFile;
inputFile.setFileName(path);
if (!inputFile.open(QFile::ReadOnly))
return ERR_FILE_READ;
QByteArray buffer = inputFile.readAll();
inputFile.close();
UINT8 result = ffsEngine->parseImageFile(buffer);
if (result)
return result;
QUuid uuid = QUuid(fileGuid);
QByteArray guid = QByteArray::fromRawData((const char*)&uuid.data1, sizeof(EFI_GUID));
result = patchFile(model->index(0, 0), guid, QByteArray::fromHex(findPattern.toLatin1()), QByteArray::fromHex(replacePattern.toLatin1()));
if (result)
return result;
QByteArray reconstructed;
result = ffsEngine->reconstructImageFile(reconstructed);
if (result)
return result;
if (reconstructed == buffer) {
return ERR_ITEM_NOT_FOUND;
}
QFile outputFile;
outputFile.setFileName(path.append(".patched"));
if (!outputFile.open(QFile::WriteOnly))
return ERR_FILE_WRITE;
outputFile.resize(0);
outputFile.write(reconstructed);
outputFile.close();
return ERR_SUCCESS;
}
UINT8 UEFIPatch::patchFile(const QModelIndex & index, const QByteArray & fileGuid, const QByteArray & findPattern, const QByteArray & replacePattern)
{
if (!model || !index.isValid())
return ERR_INVALID_PARAMETER;
if (model->type(index) == Types::File && model->header(index).left(sizeof(EFI_GUID)) == fileGuid) {
return ffsEngine->patch(index, findPattern, replacePattern, PATCH_MODE_BODY);
}
int childCount = model->rowCount(index);
if (childCount > 0) {
UINT8 result;
for (int i = 0; i < childCount; i++) {
result = patchFile(index.child(i, 0), fileGuid, findPattern, replacePattern);
if (result)
return result;
}
}
return ERR_SUCCESS;
}

45
UEFIPatch/uefipatch.h Normal file
View File

@ -0,0 +1,45 @@
/* uefipatch.h
Copyright (c) 2014, Nikolaj Schlej. 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 <QObject>
#include <QByteArray>
#include <QString>
#include <QStringList>
#include <QFileInfo>
#include <QUuid>
#include "../basetypes.h"
#include "../ffs.h"
#include "../ffsengine.h"
class UEFIPatch : public QObject
{
Q_OBJECT
public:
explicit UEFIPatch(QObject *parent = 0);
~UEFIPatch();
UINT8 patchFromFile(QString path);
UINT8 patch(QString path, QString fileGuid, QString findPattern, QString replacePattern);
private:
UINT8 patchFile(const QModelIndex & index, const QByteArray & fileGuid, const QByteArray & findPattern, const QByteArray & replacePattern);
FfsEngine* ffsEngine;
TreeModel* model;
};
#endif

39
UEFIPatch/uefipatch.pro Normal file
View File

@ -0,0 +1,39 @@
QT += core
QT -= gui
TARGET = UEFIPatch
TEMPLATE = app
CONFIG += console
SOURCES += uefipatch_main.cpp \
uefipatch.cpp \
../types.cpp \
../descriptor.cpp \
../ffs.cpp \
../ffsengine.cpp \
../treeitem.cpp \
../treemodel.cpp \
../LZMA/LzmaCompress.c \
../LZMA/LzmaDecompress.c \
../LZMA/SDK/C/LzFind.c \
../LZMA/SDK/C/LzmaDec.c \
../LZMA/SDK/C/LzmaEnc.c \
../Tiano/EfiTianoDecompress.c \
../Tiano/EfiTianoCompress.c
HEADERS += uefipatch.h \
../basetypes.h \
../descriptor.h \
../gbe.h \
../me.h \
../ffs.h \
../peimage.h \
../types.h \
../ffsengine.h \
../treeitem.h \
../treemodel.h \
../LZMA/LzmaCompress.h \
../LZMA/LzmaDecompress.h \
../Tiano/EfiTianoDecompress.h \
../Tiano/EfiTianoCompress.h

View File

@ -0,0 +1,73 @@
/* uefipatch_main.cpp
Copyright (c) 2014, Nikolaj Schlej. 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 <QCoreApplication>
#include <QString>
#include <QStringList>
#include <iostream>
#include "uefipatch.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
a.setOrganizationName("CodeRush");
a.setOrganizationDomain("coderush.me");
a.setApplicationName("UEFIExtract");
UEFIPatch w;
UINT8 result = ERR_SUCCESS;
UINT32 argumentsCount = a.arguments().length();
if (argumentsCount == 2) {
result = w.patchFromFile(a.arguments().at(1));
}
else if (argumentsCount == 5) {
result = w.patch(a.arguments().at(1), a.arguments().at(2), a.arguments().at(3), a.arguments().at(4));
}
else
result = ERR_INVALID_PARAMETER;
switch (result) {
case ERR_INVALID_PARAMETER:
std::cout << "UEFIPatch 0.1.0 - UEFI image file patching utility" << std::endl << std::endl <<
"Usage: UEFIPatch image_file [ffs_file_guid search_pattern replace_pattern]" << std::endl << std::endl <<
"image_file - full or relative path to UEFI image file" << std::endl <<
"ffs_file_guid - GUID of FFS file to be patched" << std::endl <<
"search_pattern - pattern to search" << std::endl <<
"replace_pattern - pattern to replace" << std::endl << std::endl <<
"If only image_file parameter is specified, patches will be read from patches.txt file";
break;
case ERR_SUCCESS:
std::cout << "Image patched" << std::endl;
break;
case ERR_ITEM_NOT_FOUND:
std::cout << "FFS file or search pattern not found in input file" << std::endl;
break;
case ERR_INVALID_FILE:
std::cout << "patches.txt file not found or can't be read" << std::endl;
break;
case ERR_FILE_OPEN:
std::cout << "Input file not found" << std::endl;
break;
case ERR_FILE_READ:
std::cout << "Input file can't be read" << std::endl;
break;
case ERR_FILE_WRITE:
std::cout << "Output file can't be written" << std::endl;
break;
default:
std::cout << "Error " << result << std::endl;
}
return result;
}

View File

@ -85,6 +85,7 @@ typedef uint16_t CHAR16;
#define ERR_COMPLEX_BLOCK_MAP 35 #define ERR_COMPLEX_BLOCK_MAP 35
#define ERR_DIR_ALREADY_EXIST 36 #define ERR_DIR_ALREADY_EXIST 36
#define ERR_DIR_CREATE 37 #define ERR_DIR_CREATE 37
#define ERR_UNKNOWN_PATCH_MODE 38
#define ERR_NOT_IMPLEMENTED 0xFF #define ERR_NOT_IMPLEMENTED 0xFF
// Compression algorithms // Compression algorithms
@ -109,6 +110,10 @@ typedef uint16_t CHAR16;
#define REPLACE_MODE_AS_IS 0 #define REPLACE_MODE_AS_IS 0
#define REPLACE_MODE_BODY 1 #define REPLACE_MODE_BODY 1
// Item patch modes
#define PATCH_MODE_HEADER 0
#define PATCH_MODE_BODY 1
// Erase polarity types // Erase polarity types
#define ERASE_POLARITY_FALSE 0 #define ERASE_POLARITY_FALSE 0
#define ERASE_POLARITY_TRUE 1 #define ERASE_POLARITY_TRUE 1
@ -119,6 +124,8 @@ typedef uint16_t CHAR16;
#define SEARCH_MODE_BODY 2 #define SEARCH_MODE_BODY 2
#define SEARCH_MODE_ALL 3 #define SEARCH_MODE_ALL 3
// EFI GUID // EFI GUID
typedef struct { typedef struct {
UINT8 Data[16]; UINT8 Data[16];

13
ffs.cpp
View File

@ -165,14 +165,19 @@ QString sectionTypeToQString(const UINT8 type)
} }
} }
UINT32 sizeOfSectionHeaderOfType(const UINT8 type) UINT32 sizeOfSectionHeader(EFI_COMMON_SECTION_HEADER* header)
{ {
switch (type) if (!header)
return 0;
switch (header->Type)
{ {
case EFI_SECTION_COMPRESSION: case EFI_SECTION_COMPRESSION:
return sizeof(EFI_COMMON_SECTION_HEADER); return sizeof(EFI_COMMON_SECTION_HEADER);
case EFI_SECTION_GUID_DEFINED: case EFI_SECTION_GUID_DEFINED: {
return sizeof(EFI_GUID_DEFINED_SECTION); EFI_GUID_DEFINED_SECTION* gdsHeader = (EFI_GUID_DEFINED_SECTION*)header;
return gdsHeader->DataOffset;
}
case EFI_SECTION_DISPOSABLE: case EFI_SECTION_DISPOSABLE:
return sizeof(EFI_DISPOSABLE_SECTION); return sizeof(EFI_DISPOSABLE_SECTION);
case EFI_SECTION_PE32: case EFI_SECTION_PE32:

2
ffs.h
View File

@ -416,7 +416,7 @@ typedef EFI_COMMON_SECTION_HEADER EFI_FIRMWARE_VOLUME_IMAGE_SECTION;
typedef EFI_COMMON_SECTION_HEADER EFI_USER_INTERFACE_SECTION; typedef EFI_COMMON_SECTION_HEADER EFI_USER_INTERFACE_SECTION;
//Section routines //Section routines
extern UINT32 sizeOfSectionHeaderOfType(const UINT8 type); extern UINT32 sizeOfSectionHeader(EFI_COMMON_SECTION_HEADER* header);
// Restore previous packing rules // Restore previous packing rules
#pragma pack(pop) #pragma pack(pop)

View File

@ -1132,7 +1132,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c
case EFI_SECTION_PEI_DEPEX: case EFI_SECTION_PEI_DEPEX:
case EFI_SECTION_SMM_DEPEX: case EFI_SECTION_SMM_DEPEX:
case EFI_SECTION_COMPATIBILITY16: { case EFI_SECTION_COMPATIBILITY16: {
headerSize = sizeOfSectionHeaderOfType(sectionHeader->Type); headerSize = sizeOfSectionHeader(sectionHeader);
header = section.left(headerSize); header = section.left(headerSize);
body = section.mid(headerSize, sectionSize - headerSize); body = section.mid(headerSize, sectionSize - headerSize);
@ -1509,12 +1509,12 @@ UINT8 FfsEngine::insert(const QModelIndex & index, const QByteArray & object, co
else if (model->type(parent) == Types::File) { else if (model->type(parent) == Types::File) {
type = Types::Section; type = Types::Section;
EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)object.constData(); EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)object.constData();
headerSize = sizeOfSectionHeaderOfType(commonHeader->Type); headerSize = sizeOfSectionHeader(commonHeader);
} }
else if (model->type(parent) == Types::Section) { else if (model->type(parent) == Types::Section) {
type = Types::Section; type = Types::Section;
EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)object.constData(); EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)object.constData();
headerSize = sizeOfSectionHeaderOfType(commonHeader->Type); headerSize = sizeOfSectionHeader(commonHeader);
} }
else else
return ERR_NOT_IMPLEMENTED; return ERR_NOT_IMPLEMENTED;
@ -1549,7 +1549,7 @@ UINT8 FfsEngine::replace(const QModelIndex & index, const QByteArray & object, c
else if (model->type(index) == Types::Section) { else if (model->type(index) == Types::Section) {
if (mode == REPLACE_MODE_AS_IS) { if (mode == REPLACE_MODE_AS_IS) {
EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)object.constData(); EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)object.constData();
headerSize = sizeOfSectionHeaderOfType(commonHeader->Type); headerSize = sizeOfSectionHeader(commonHeader);
result = create(index, Types::Section, object.left(headerSize), object.right(object.size() - headerSize), CREATE_MODE_AFTER, Actions::Replace); result = create(index, Types::Section, object.left(headerSize), object.right(object.size() - headerSize), CREATE_MODE_AFTER, Actions::Replace);
} }
else if (mode == REPLACE_MODE_BODY) { else if (mode == REPLACE_MODE_BODY) {
@ -1754,7 +1754,7 @@ UINT8 FfsEngine::decompress(const QByteArray & compressedData, const UINT8 compr
// Shitty compressed section with a section header between COMPRESSED_SECTION_HEADER and LZMA_HEADER // Shitty compressed section with a section header between COMPRESSED_SECTION_HEADER and LZMA_HEADER
// We must determine section header size by checking it's type before we can unpack that non-standard compressed section // We must determine section header size by checking it's type before we can unpack that non-standard compressed section
shittySectionHeader = (EFI_COMMON_SECTION_HEADER*)data; shittySectionHeader = (EFI_COMMON_SECTION_HEADER*)data;
shittySectionSize = sizeOfSectionHeaderOfType(shittySectionHeader->Type); shittySectionSize = sizeOfSectionHeader(shittySectionHeader);
// Decompress section data once again // Decompress section data once again
data += shittySectionSize; data += shittySectionSize;
@ -1852,7 +1852,7 @@ UINT8 FfsEngine::compress(const QByteArray & data, const UINT8 algorithm, QByteA
{ {
QByteArray header = data.left(sizeof(EFI_COMMON_SECTION_HEADER)); QByteArray header = data.left(sizeof(EFI_COMMON_SECTION_HEADER));
EFI_COMMON_SECTION_HEADER* sectionHeader = (EFI_COMMON_SECTION_HEADER*)header.constData(); EFI_COMMON_SECTION_HEADER* sectionHeader = (EFI_COMMON_SECTION_HEADER*)header.constData();
UINT32 headerSize = sizeOfSectionHeaderOfType(sectionHeader->Type); UINT32 headerSize = sizeOfSectionHeader(sectionHeader);
header = data.left(headerSize); header = data.left(headerSize);
QByteArray newData = data.mid(headerSize); QByteArray newData = data.mid(headerSize);
if (LzmaCompress((UINT8*)newData.constData(), newData.size(), NULL, &compressedSize) != ERR_BUFFER_TOO_SMALL) if (LzmaCompress((UINT8*)newData.constData(), newData.size(), NULL, &compressedSize) != ERR_BUFFER_TOO_SMALL)
@ -3198,3 +3198,48 @@ UINT8 FfsEngine::dump(const QModelIndex & index, const QString path)
return ERR_SUCCESS; return ERR_SUCCESS;
} }
UINT8 FfsEngine::patch(const QModelIndex & index, const QByteArray & findPattern, const QByteArray & replacePattern, const UINT8 mode)
{
if (!index.isValid() || findPattern.isEmpty())
return ERR_INVALID_PARAMETER;
// Skip removed files
if (model->action(index) == Actions::Remove)
return ERR_SUCCESS;
// Patch header
if (mode == PATCH_MODE_HEADER && model->header(index).contains(findPattern)) {
QByteArray patched = model->header(index);
patched.replace(findPattern, replacePattern).append(model->body(index));
msg(tr("Header of %1 patched, %2 -> %3")
.arg(model->nameString(index))
.arg(QString(findPattern.toHex()))
.arg(QString(replacePattern.toHex())), index);
return replace(index, patched, REPLACE_MODE_AS_IS);
}
// Patch body
else if (mode == PATCH_MODE_BODY) {
if (model->rowCount(index)) {
UINT8 result;
for (int i = 0; i < model->rowCount(index); i++) {
result = patch(index.child(i, 0), findPattern, replacePattern, PATCH_MODE_BODY);
if (result)
return result;
}
}
else if (model->body(index).contains(findPattern)){
QByteArray patched = model->body(index);
patched.replace(findPattern, replacePattern);
patched.prepend(model->header(index));
msg(tr("Body of %1 patched, %2 -> %3")
.arg(model->nameString(index))
.arg(QString(findPattern.toHex()))
.arg(QString(replacePattern.toHex())), index);
return replace(index, patched, REPLACE_MODE_AS_IS);
}
}
else
return ERR_UNKNOWN_PATCH_MODE;
return ERR_SUCCESS;
}

View File

@ -86,6 +86,7 @@ public:
UINT8 remove(const QModelIndex & index); UINT8 remove(const QModelIndex & index);
UINT8 rebuild(const QModelIndex & index); UINT8 rebuild(const QModelIndex & index);
UINT8 dump(const QModelIndex & index, const QString path); UINT8 dump(const QModelIndex & index, const QString path);
UINT8 patch(const QModelIndex & index, const QByteArray & findPattern, const QByteArray & replacePattern, const UINT8 mode);
// Search routines // Search routines
UINT8 findHexPattern(const QByteArray & pattern, const UINT8 mode); UINT8 findHexPattern(const QByteArray & pattern, const UINT8 mode);