From 7bae8e040c74387e20916c0cb40265f035c54b57 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Sat, 9 Jul 2016 08:31:08 +0200 Subject: [PATCH] UEFIDump 0.1.0 for Windows - Linux and OSX will be done in next commit --- UEFIDump/uefidump.cpp | 161 ++++++++++++++++--------------------- UEFIDump/uefidump.h | 8 +- UEFIDump/uefidump_main.cpp | 7 +- bstrlib/bstrwrap.h | 2 +- common/basetypes.h | 7 +- common/utility.cpp | 49 +++++++++++ common/utility.h | 13 +-- 7 files changed, 135 insertions(+), 112 deletions(-) diff --git a/UEFIDump/uefidump.cpp b/UEFIDump/uefidump.cpp index 08816e8..3aca67b 100644 --- a/UEFIDump/uefidump.cpp +++ b/UEFIDump/uefidump.cpp @@ -11,8 +11,6 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ -#include - #include "uefidump.h" #include "../common/ffs.h" #include @@ -20,16 +18,29 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include -USTATUS UEFIDumper::dump(const UByteArray & buffer, const std::wstring & inPath, const std::wstring & guid) -{ - //TODO: rework to support relative paths - std::wstring path = std::wstring(inPath).append(L".dump2"); - path = L"\\\\?\\" + path; - std::wstring reportPath = std::wstring(inPath).append(L".report2.txt"); - reportPath = L"\\\\?\\" + reportPath; - std::wcout << L"Dump path: " << path << std::endl; - std::wcout << L"Report path: " << reportPath << std::endl; +#ifdef WIN32 +#include +#include +bool isExistOnFs(const UString & path) { + struct _stat buf; + return (_stat((const char*)path.toLocal8Bit(), &buf) == 0); +} + +bool makeDirectory(const UString & dir) { + return (_mkdir((const char*)dir.toLocal8Bit()) == 0); +} + +bool changeDirectory(const UString & dir) { + return (_chdir((const char*)dir.toLocal8Bit()) == 0); +} +#else +#endif + +USTATUS UEFIDumper::dump(const UByteArray & buffer, const UString & inPath, const UString & guid) +{ + UString path = UString(inPath) + UString(".dump"); + UString reportPath = UString(inPath) + UString(".report.txt"); if (initialized) { // Check if called with a different buffer as before @@ -99,8 +110,19 @@ USTATUS UEFIDumper::dump(const UByteArray & buffer, const std::wstring & inPath, initialized = true; } + // Check for dump directory existence + if (isExistOnFs(path)) + return U_DIR_ALREADY_EXIST; + + // Create dump directory and cd to it + if (!makeDirectory(path)) + return U_DIR_CREATE; + + if (!changeDirectory(path)) + return U_DIR_CHANGE; + dumped = false; - UINT8 result = recursiveDump(model.index(0,0), path, guid); + UINT8 result = recursiveDump(model.index(0,0)); if (result) return result; else if (!dumped) @@ -109,77 +131,42 @@ USTATUS UEFIDumper::dump(const UByteArray & buffer, const std::wstring & inPath, return U_SUCCESS; } -std::wstring UEFIDumper::guidToWstring(const EFI_GUID & guid) -{ - std::wstringstream ws; - - ws << std::hex << std::uppercase << std::setfill(L'0'); - ws << std::setw(8) << *(const UINT32*)&guid.Data[0] << L"-"; - ws << std::setw(4) << *(const UINT16*)&guid.Data[4] << L"-"; - ws << std::setw(4) << *(const UINT16*)&guid.Data[6] << L"-"; - ws << std::setw(2) << guid.Data[8]; - ws << std::setw(2) << guid.Data[9] << L"-"; - ws << std::setw(2) << guid.Data[10]; - ws << std::setw(2) << guid.Data[11]; - ws << std::setw(2) << guid.Data[12]; - ws << std::setw(2) << guid.Data[13]; - ws << std::setw(2) << guid.Data[14]; - ws << std::setw(2) << guid.Data[15]; - - return ws.str(); -} - -bool UEFIDumper::createFullPath(const std::wstring & path) { - - // Break the path into parent\current, assuming the path is already full and converted into Windows native "\\?\" format - size_t pos = path.find_last_of(L'\\'); - - // Slash is not found, it's a bug - if (pos == path.npos) - return FALSE; - - std::wstring parent = path.substr(0, pos); - std::wstring current = path.substr(pos + 1); - - // Check if first exist, if so, create last and return true - UINT32 attributes = GetFileAttributesW(parent.c_str()); - if ((attributes != INVALID_FILE_ATTRIBUTES) && (attributes & FILE_ATTRIBUTE_DIRECTORY)) { - // first is already exist, just create last - return CreateDirectoryW(path.c_str(), NULL) != 0; - } - - // Perform recursive call - if (createFullPath(parent)) - return CreateDirectoryW(path.c_str(), NULL) != 0; - - return FALSE; -} - -USTATUS UEFIDumper::recursiveDump(const UModelIndex & index, const std::wstring & path, const std::wstring & guid) +USTATUS UEFIDumper::recursiveDump(const UModelIndex & index) { if (!index.isValid()) return U_INVALID_PARAMETER; - UByteArray itemHeader = model.header(index); - UByteArray fileHeader = model.header(model.findParentOfType(index, Types::File)); + //UByteArray itemHeader = model.header(index); + //UByteArray fileHeader = model.header(model.findParentOfType(index, Types::File)); - if (guid.length() == 0 || - (itemHeader.size() >= sizeof (EFI_GUID) && guidToWstring(*(const EFI_GUID*)itemHeader.constData()) == guid) || - (fileHeader.size() >= sizeof(EFI_GUID) && guidToWstring(*(const EFI_GUID*)fileHeader.constData()) == guid)) { + //if (guid.length() == 0 || + // (itemHeader.size() >= sizeof (EFI_GUID) && guidToUString(*(const EFI_GUID*)itemHeader.constData()) == guid) || + // (fileHeader.size() >= sizeof(EFI_GUID) && guidToUString(*(const EFI_GUID*)fileHeader.constData()) == guid)) { - if (SetCurrentDirectoryW(path.c_str())) - return U_DIR_ALREADY_EXIST; + // Construct file name + UString orgName = uniqueItemName(index); + UString name = orgName; + bool nameFound = false; + for (int i = 0; i < 0x10000; ++i) { + if (isExistOnFs(name)) { + name = orgName + UString("_") + usprintf("%04X", i); + } + else { + nameFound = true; + break; + } + } + if (!nameFound) + return U_INVALID_PARAMETER; //TODO: replace with proper errorCode - if (!createFullPath(path)) - return U_DIR_CREATE; - - //if (model.rowCount(index) == 0) { + // Add header and body only for leaf sections + if (model.rowCount(index) == 0) { // Header UByteArray data = model.header(index); if (!data.isEmpty()) { std::ofstream file; - std::wstring name = path + std::wstring(L"\\header.bin"); - file.open(name, std::ios::out | std::ios::binary); + std::string str = std::string((const char*)name) + std::string("_header.bin"); + file.open(str, std::ios::out | std::ios::binary); file.write(data.constData(), data.size()); file.close(); } @@ -188,12 +175,12 @@ USTATUS UEFIDumper::recursiveDump(const UModelIndex & index, const std::wstring data = model.body(index); if (!data.isEmpty()) { std::ofstream file; - std::wstring name = path + std::wstring(L"\\body.bin"); - file.open(name, std::ios::out | std::ios::binary); + std::string str = std::string((const char*)name) + std::string("_body.bin"); + file.open(str, std::ios::out | std::ios::binary); file.write(data.constData(), data.size()); file.close(); } - //} + } // Info UString info = "Type: " + itemTypeToUString(model.type(index)) + "\n" + "Subtype: " + itemSubtypeToUString(model.type(index), model.subtype(index)) + "\n"; @@ -202,30 +189,18 @@ USTATUS UEFIDumper::recursiveDump(const UModelIndex & index, const std::wstring info += model.info(index) + "\n"; std::ofstream file; - std::wstring name = path + std::wstring(L"\\info.txt"); - file.open(name, std::ios::out); + std::string str = std::string((const char*)name) + std::string("_info.txt"); + file.open(str, std::ios::out); file.write((const char*)info, info.length()); file.close(); dumped = true; - } - + //} + + // Process child items UINT8 result; for (int i = 0; i < model.rowCount(index); i++) { - UModelIndex childIndex = index.child(i, 0); - bool useText = false; - if (model.type(childIndex) != Types::Volume) - useText = (model.text(childIndex).length() > 0); - - UString name = useText ? (const char *)model.text(childIndex) : (const char *)model.name(childIndex); - std::string sName = std::string((const char*)name, name.length()); - std::wstring childPath = path + std::wstring(L"\\") + std::to_wstring(i) + std::wstring(L" ") + std::wstring(sName.begin(), sName.end()); - - // Workaround for paths with dot at the end, just add remove it - if (childPath.at(childPath.length() - 1) == L'.') - childPath.pop_back(); - - result = recursiveDump(childIndex, childPath, guid); + result = recursiveDump(index.child(i, 0)); if (result) return result; } diff --git a/UEFIDump/uefidump.h b/UEFIDump/uefidump.h index be456f4..b61b909 100644 --- a/UEFIDump/uefidump.h +++ b/UEFIDump/uefidump.h @@ -14,8 +14,6 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #ifndef UEFIDUMP_H #define UEFIDUMP_H -#include - #include "../common/basetypes.h" #include "../common/ustring.h" #include "../common/treemodel.h" @@ -30,12 +28,10 @@ public: explicit UEFIDumper() : model(), ffsParser(&model), ffsReport(&model), fitParser(&model), currentBuffer(), initialized(false), dumped(false) {} ~UEFIDumper() {} - USTATUS dump(const UByteArray & buffer, const std::wstring & path, const std::wstring & guid = std::wstring()); + USTATUS dump(const UByteArray & buffer, const UString & path, const UString & guid = UString()); private: - USTATUS recursiveDump(const UModelIndex & root, const std::wstring & path, const std::wstring & guid); - std::wstring guidToWstring(const EFI_GUID & guid); - bool createFullPath(const std::wstring & path); + USTATUS recursiveDump(const UModelIndex & root); TreeModel model; FfsParser ffsParser; diff --git a/UEFIDump/uefidump_main.cpp b/UEFIDump/uefidump_main.cpp index af160a7..48a7a28 100644 --- a/UEFIDump/uefidump_main.cpp +++ b/UEFIDump/uefidump_main.cpp @@ -15,7 +15,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "uefidump.h" -int wmain(int argc, wchar_t *argv[]) +int main(int argc, char *argv[]) { if (argc > 32) { std::cout << "Too many arguments" << std::endl; @@ -30,12 +30,11 @@ int wmain(int argc, wchar_t *argv[]) inputFile.close(); UEFIDumper uefidumper; - return (uefidumper.dump(buffer, std::wstring(argv[1])) != U_SUCCESS); + return (uefidumper.dump(buffer, UString(argv[1])) != U_SUCCESS); } else { std::cout << "UEFIDump 0.1.0" << std::endl << std::endl - << "Usage: UEFIExtract imagefile " << std::endl - << "Return value is a bit mask where 0 at position N means that file with GUID_N was found and unpacked, 1 otherwise" << std::endl; + << "Usage: UEFIDump imagefile" << std::endl; return 1; } diff --git a/bstrlib/bstrwrap.h b/bstrlib/bstrwrap.h index c3945de..88e0f14 100644 --- a/bstrlib/bstrwrap.h +++ b/bstrlib/bstrwrap.h @@ -362,7 +362,7 @@ struct CBString : public tagbstring { int read (bNread readPtr, void * parm); // QString compatibility methods - CBString toLocal8Bit() { return *this; } + CBString toLocal8Bit() const { return *this; } bool isEmpty() const { return slen == 0; } void clear() { *this = ""; } CBString left(int len) const { return midstr(0, len); } diff --git a/common/basetypes.h b/common/basetypes.h index 9adac9c..b4d4bc1 100644 --- a/common/basetypes.h +++ b/common/basetypes.h @@ -53,9 +53,10 @@ typedef uint8_t USTATUS; #define U_UNKNOWN_RELOCATION_TYPE 32 #define U_DIR_ALREADY_EXIST 33 #define U_DIR_CREATE 34 -#define U_TRUNCATED_IMAGE 35 -#define U_INVALID_CAPSULE 36 -#define U_STORES_NOT_FOUND 37 +#define U_DIR_CHANGE 35 +#define U_TRUNCATED_IMAGE 36 +#define U_INVALID_CAPSULE 37 +#define U_STORES_NOT_FOUND 38 #define U_NOT_IMPLEMENTED 0xFF // UDK porting definitions diff --git a/common/utility.cpp b/common/utility.cpp index 4425f5c..49529be 100644 --- a/common/utility.cpp +++ b/common/utility.cpp @@ -44,6 +44,54 @@ UByteArray parsingDataToUByteArray(const PARSING_DATA & pdata) return UByteArray((const char*)&pdata, sizeof(PARSING_DATA)); } +// Returns unique name string based for tree item +UString uniqueItemName(const UModelIndex & index) +{ + // Sanity check + if (!index.isValid()) + return UString("Invalid index"); + + // Get model from index + const TreeModel* model = index.model(); + + // Get data from parsing data + PARSING_DATA pdata = parsingDataFromUModelIndex(index); + + // Construct the name + UString itemName = model->name(index); + UString itemText = model->text(index); + + // Default name + UString name = itemName; + switch (model->type(index)) { + case Types::Volume: + if (pdata.volume.hasExtendedHeader) name = guidToUString(pdata.volume.extendedHeaderGuid); + break; + case Types::NvarEntry: + case Types::VssEntry: + case Types::FsysEntry: + case Types::EvsaEntry: + case Types::FlashMapEntry: + case Types::File: + name = itemText.isEmpty() ? itemName : itemText; + break; + case Types::Section: { + // Get parent file name + UModelIndex fileIndex = model->findParentOfType(index, Types::File); + UString fileText = model->text(fileIndex); + name = fileText.isEmpty() ? model->name(fileIndex) : fileText; + // Append section subtype name + name += '_' + itemName; + } break; + } + + name.findreplace(' ', '_'); + name.findreplace('/', '_'); + name.findreplace('-', '_'); + + return name; +} + // Returns text representation of error code UString errorCodeToUString(UINT8 errorCode) { @@ -88,6 +136,7 @@ UString errorCodeToUString(UINT8 errorCode) case U_COMPLEX_BLOCK_MAP: return UString("Block map structure too complex for correct analysis"); case U_DIR_ALREADY_EXIST: return UString("Directory already exists"); case U_DIR_CREATE: return UString("Directory can't be created"); + case U_DIR_CHANGE: return UString("Change directory failed"); //case U_UNKNOWN_PATCH_TYPE: return UString("Unknown patch type"); //case U_PATCH_OFFSET_OUT_OF_BOUNDS: return UString("Patch offset out of bounds"); //case U_INVALID_SYMBOL: return UString("Invalid symbol"); diff --git a/common/utility.h b/common/utility.h index beacde3..dcde3e5 100644 --- a/common/utility.h +++ b/common/utility.h @@ -25,22 +25,25 @@ PARSING_DATA parsingDataFromUModelIndex(const UModelIndex & index); // Converts parsing data to byte array UByteArray parsingDataToUByteArray(const PARSING_DATA & pdata); +// Returns unique name string based for tree item +UString uniqueItemName(const UModelIndex & index); + // Converts error code to UString -extern UString errorCodeToUString(UINT8 errorCode); +UString errorCodeToUString(UINT8 errorCode); // Decompression routine -extern USTATUS decompress(const UByteArray & compressed, UINT8 & algorithm, UByteArray & decompressed, UByteArray & efiDecompressed); +USTATUS decompress(const UByteArray & compressed, UINT8 & algorithm, UByteArray & decompressed, UByteArray & efiDecompressed); // Compression routine //USTATUS compress(const UByteArray & decompressed, UByteArray & compressed, const UINT8 & algorithm); // CRC32 calculation routine -extern UINT32 crc32(UINT32 initial, const UINT8* buffer, const UINT32 length); +UINT32 crc32(UINT32 initial, const UINT8* buffer, const UINT32 length); // 8bit checksum calculation routine -extern UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize); +UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize); // 16bit checksum calculation routine -extern UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize); +UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize); #endif // UTILITY_H