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 }