File indexing completed on 2024-05-12 05:45:31

0001 /*
0002     Copyright Hannah von Reth <vonreth@kde.org>
0003 
0004     Redistribution and use in source and binary forms, with or without
0005     modification, are permitted provided that the following conditions
0006     are met:
0007     1. Redistributions of source code must retain the above copyright
0008        notice, this list of conditions and the following disclaimer.
0009     2. Redistributions in binary form must reproduce the above copyright
0010        notice, this list of conditions and the following disclaimer in the
0011        documentation and/or other materials provided with the distribution.
0012 
0013     THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
0014     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0015     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0016     ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
0017     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0018     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
0019     OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0020     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0021     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0022     OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0023     SUCH DAMAGE.
0024 */
0025 #include "kshimgen_p.h"
0026 
0027 #include <windows.h>
0028 #include <comdef.h>
0029 
0030 #include <filesystem>
0031 
0032 namespace {
0033 
0034 // https://docs.microsoft.com/en-us/previous-versions/ms997538(v=msdn.10)?redirectedfrom=MSDN
0035 // #pragmas are used here to insure that the structure's
0036 // packing in memory matches the packing of the EXE or DLL.
0037 #pragma pack(push)
0038 #pragma pack(2)
0039 typedef struct
0040 {
0041     BYTE bWidth; // Width, in pixels, of the image
0042     BYTE bHeight; // Height, in pixels, of the image
0043     BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
0044     BYTE bReserved; // Reserved
0045     WORD wPlanes; // Color Planes
0046     WORD wBitCount; // Bits per pixel
0047     DWORD dwBytesInRes; // how many bytes in this resource?
0048     WORD nID; // the ID
0049 } GRPICONDIRENTRY;
0050 
0051 typedef struct
0052 {
0053     WORD idReserved; // Reserved (must be 0)
0054     WORD idType; // Resource type (1 for icons)
0055     WORD idCount; // How many images?
0056     GRPICONDIRENTRY idEntries[1]; // The entries for each image
0057 } GRPICONDIR;
0058 #pragma pack(pop)
0059 
0060 struct KShimResource
0061 {
0062     void *resourceLock = nullptr;
0063     uint64_t size = 0;
0064     const wchar_t *id;
0065     const wchar_t *type;
0066 };
0067 
0068 std::wstring printableRCType(const wchar_t *type)
0069 {
0070     if (IS_INTRESOURCE(type)) {
0071         return std::to_wstring(reinterpret_cast<const int64_t>(type));
0072     }
0073     return type;
0074 }
0075 
0076 BOOL CALLBACK resourceCallback(HMODULE, wchar_t *, wchar_t *name, intptr_t *contextPtr)
0077 {
0078     auto dest = reinterpret_cast<wchar_t **>(contextPtr);
0079     if (IS_INTRESOURCE(name)) {
0080         *dest = name;
0081     } else {
0082         size_t len = wcslen(name) + 1;
0083         wchar_t *buffer = new wchar_t[len];
0084         wcscpy_s(buffer, len, name);
0085         *dest = buffer;
0086     }
0087     return false;
0088 }
0089 
0090 void updateResources(const std::filesystem::path &dest, const std::vector<KShimResource> &resources)
0091 {
0092     auto updateHandle = BeginUpdateResourceW(dest.wstring().data(), false);
0093     if (!updateHandle) {
0094         kLog2(KLog::Type::Error) << "Failed to BeginUpdateResource: " << dest;
0095         exit(1);
0096     }
0097     for (const auto &resource : resources) {
0098         if (resource.resourceLock) {
0099             if (!UpdateResourceW(updateHandle, resource.type, resource.id, 1033,
0100                                  resource.resourceLock, static_cast<DWORD>(resource.size))) {
0101                 kLog2(KLog::Type::Error)
0102                         << "Failed to update resource: " << printableRCType(resource.type) << ": "
0103                         << printableRCType(resource.id);
0104                 exit(1);
0105             }
0106         }
0107     }
0108     if (!EndUpdateResourceW(updateHandle, false)) {
0109         kLog2(KLog::Type::Error) << "Failed to EndUpdateResource: " << dest;
0110         exit(1);
0111     }
0112 }
0113 }
0114 namespace KShimGenPrivate {
0115 
0116 void updateIcon(const std::filesystem::path &src, const std::filesystem::path &dest)
0117 {
0118     auto exe = LoadLibraryExW(src.wstring().data(), nullptr, LOAD_LIBRARY_AS_DATAFILE);
0119     if (!exe) {
0120         kLog << "Failed to load exe for icon: " << src;
0121         return;
0122     }
0123     wchar_t *iconGroupName = nullptr;
0124     EnumResourceNamesW(exe, RT_GROUP_ICON, (ENUMRESNAMEPROCW)resourceCallback,
0125                        reinterpret_cast<intptr_t>(&iconGroupName));
0126     if (iconGroupName) {
0127         auto infoPos = FindResourceW(exe, iconGroupName, RT_GROUP_ICON);
0128         if (!infoPos) {
0129             return;
0130         }
0131         auto infoRc = LoadResource(exe, infoPos);
0132         if (!infoRc) {
0133             kLog2(KLog::Type::Error)
0134                     << "Failed to find icon info: " << printableRCType(iconGroupName);
0135             return;
0136         }
0137         auto lock = LockResource(infoRc);
0138         if (!lock) {
0139             kLog2(KLog::Type::Error)
0140                     << "Failed to lock icon info: " << printableRCType(iconGroupName);
0141             return;
0142         }
0143         kLog << "Found icon info: " << printableRCType(iconGroupName);
0144         auto info = reinterpret_cast<GRPICONDIR *>(lock);
0145         if (!info) {
0146             kLog2(KLog::Type::Error) << "Failed to load icon info";
0147         } else {
0148             auto getResource = [&exe](wchar_t *id, wchar_t *type) -> KShimResource {
0149                 auto iconPos = FindResourceW(exe, id, type);
0150                 if (!iconPos) {
0151                     kLog2(KLog::Type::Error) << "Failed to find resource: " << printableRCType(type)
0152                                              << ": " << printableRCType(id);
0153                     return {};
0154                 }
0155                 auto icon = LoadResource(exe, iconPos);
0156                 if (!icon) {
0157                     kLog2(KLog::Type::Error) << "Failed to load resource: " << printableRCType(type)
0158                                              << ": " << printableRCType(id);
0159                     return {};
0160                 }
0161                 auto lock = LockResource(icon);
0162                 if (!lock) {
0163 
0164                     kLog2(KLog::Type::Error) << "Failed to lock resource: " << printableRCType(type)
0165                                              << ": " << printableRCType(id);
0166                     return {};
0167                 }
0168                 return { lock, SizeofResource(exe, iconPos), id, type };
0169             };
0170             std::vector<KShimResource> resources;
0171             resources.reserve(info->idCount + 1);
0172             resources.push_back(getResource(iconGroupName, RT_GROUP_ICON));
0173             for (int64_t i = 0; i < info->idCount; ++i) {
0174                 resources.push_back(getResource(MAKEINTRESOURCEW(info->idEntries[i].nID), RT_ICON));
0175             }
0176             updateResources(dest, resources);
0177             kLog << "Copied: " << info->idCount << " icons";
0178 
0179             if (!IS_INTRESOURCE(iconGroupName)) {
0180                 delete[] iconGroupName;
0181             }
0182         }
0183     }
0184     if (!FreeLibrary(exe)) {
0185         kLog2(KLog::Type::Error) << "Failed to FreeLibrary: " << src;
0186         exit(1);
0187     }
0188 }
0189 
0190 void setPayload(const std::filesystem::path &dest, const std::vector<uint8_t> &payload)
0191 {
0192     updateResources(dest, { { (void *)payload.data(), payload.size(), KShimLib::PayLoadKey.data(), KShimLib::PayloadCategory.data() } });
0193 }
0194 }