1 /* 2 * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include "VersionInfoSwap.h" 27 28 #include <stdio.h> 29 #include <tchar.h> 30 31 #include <windows.h> 32 #include <stdio.h> 33 #include <Strsafe.h> 34 #include <fstream> 35 #include <locale> 36 #include <codecvt> 37 38 39 /* 40 * Usage: VersionInfoSwap.exe [Property file] [Executable file] 41 * 42 * [Property file] contains key/value pairs 43 * The swap tool uses these pairs to create new version resource 44 * 45 * See MSDN docs for VS_VERSIONINFO structure that 46 * depicts organization of data in this version resource 47 * https://msdn.microsoft.com/en-us/library/ms647001(v=vs.85).aspx 48 * 49 * The swap tool makes changes in [Executable file] 50 * The tool assumes that the executable file has no version resource 51 * and it adds new resource in the executable file. 52 * If the executable file has an existing version resource, then 53 * the existing version resource will be replaced with new one. 54 * 55 */ 56 57 bool VersionInfoSwap::PatchExecutable() { 58 bool b = LoadFromPropertyFile(); 59 if (!b) { 60 return false; 61 } 62 63 ByteBuffer buf; 64 CreateNewResource(&buf); 65 b = this->UpdateResource(buf.getPtr(), static_cast<DWORD>(buf.getPos())); 66 if (!b) { 67 return false; 68 } 69 return true; 70 } 71 72 bool VersionInfoSwap::LoadFromPropertyFile() { 73 74 bool result = false; 75 std::wifstream stream(m_propFileName.data()); 76 77 const std::locale empty_locale = std::locale::empty(); 78 const std::locale utf8_locale = 79 std::locale(empty_locale, new std::codecvt_utf8<wchar_t>()); 80 stream.imbue(utf8_locale); 81 82 if (stream.is_open() == true) { 83 int lineNumber = 1; 84 while (stream.eof() == false) { 85 wstring line; 86 std::getline(stream, line); 87 88 // # at the first character will comment out the line. 89 if (line.empty() == false && line[0] != '#') { 90 wstring::size_type pos = line.find('='); 91 if (pos != wstring::npos) { 92 wstring name = line.substr(0, pos); 93 wstring value = line.substr(pos + 1); 94 m_props[name] = value; 95 } else { 96 fwprintf(stderr, TEXT("Unable to find delimiter at line %d\n"), lineNumber); 97 } 98 } 99 lineNumber++; 100 } 101 result = true; 102 } else { 103 fwprintf(stderr, TEXT("Unable to read property file\n")); 104 } 105 106 return result; 107 } 108 109 110 /* 111 * Creates new version resource 112 * 113 * MSND docs for VS_VERSION_INFO structure 114 * https://msdn.microsoft.com/en-us/library/ms647001(v=vs.85).aspx 115 */ 116 void VersionInfoSwap::CreateNewResource(ByteBuffer *buf) { 117 size_t versionInfoStart = buf->getPos(); 118 buf->AppendWORD(0); 119 buf->AppendWORD(sizeof VS_FIXEDFILEINFO); 120 buf->AppendWORD(0); 121 buf->AppendString(TEXT("VS_VERSION_INFO")); 122 buf->Align(4); 123 124 VS_FIXEDFILEINFO fxi; 125 FillFixedFileInfo(&fxi); 126 buf->AppendBytes((BYTE*)&fxi, sizeof (VS_FIXEDFILEINFO)); 127 buf->Align(4); 128 129 // String File Info 130 size_t stringFileInfoStart = buf->getPos(); 131 buf->AppendWORD(0); 132 buf->AppendWORD(0); 133 buf->AppendWORD(1); 134 buf->AppendString(TEXT("StringFileInfo")); 135 buf->Align(4); 136 137 // String Table 138 size_t stringTableStart = buf->getPos(); 139 buf->AppendWORD(0); 140 buf->AppendWORD(0); 141 buf->AppendWORD(1); 142 143 // "040904B0" = LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP 144 buf->AppendString(TEXT("040904B0")); 145 buf->Align(4); 146 147 // Strings 148 std::vector<wstring> keys; 149 for (std::map<wstring, wstring>::const_iterator it = 150 m_props.begin(); it != m_props.end(); ++it) { 151 keys.push_back(it->first); 152 } 153 154 for (size_t index = 0; index < keys.size(); index++) { 155 wstring name = keys[index]; 156 wstring value = m_props[name]; 157 158 size_t stringStart = buf->getPos(); 159 buf->AppendWORD(0); 160 buf->AppendWORD(static_cast<WORD>(value.length())); 161 buf->AppendWORD(1); 162 buf->AppendString(name); 163 buf->Align(4); 164 buf->AppendString(value); 165 buf->ReplaceWORD(stringStart, 166 static_cast<WORD>(buf->getPos() - stringStart)); 167 buf->Align(4); 168 } 169 170 buf->ReplaceWORD(stringTableStart, 171 static_cast<WORD>(buf->getPos() - stringTableStart)); 172 buf->ReplaceWORD(stringFileInfoStart, 173 static_cast<WORD>(buf->getPos() - stringFileInfoStart)); 174 175 // VarFileInfo 176 size_t varFileInfoStart = buf->getPos(); 177 buf->AppendWORD(1); 178 buf->AppendWORD(0); 179 buf->AppendWORD(1); 180 buf->AppendString(TEXT("VarFileInfo")); 181 buf->Align(4); 182 183 buf->AppendWORD(0x24); 184 buf->AppendWORD(0x04); 185 buf->AppendWORD(0x00); 186 buf->AppendString(TEXT("Translation")); 187 buf->Align(4); 188 // "040904B0" = LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP 189 buf->AppendWORD(0x0409); 190 buf->AppendWORD(0x04B0); 191 192 buf->ReplaceWORD(varFileInfoStart, 193 static_cast<WORD>(buf->getPos() - varFileInfoStart)); 194 buf->ReplaceWORD(versionInfoStart, 195 static_cast<WORD>(buf->getPos() - versionInfoStart)); 196 } 197 198 void VersionInfoSwap::FillFixedFileInfo(VS_FIXEDFILEINFO *fxi) { 199 wstring fileVersion; 200 wstring productVersion; 201 int ret; 202 203 fileVersion = m_props[TEXT("FileVersion")]; 204 productVersion = m_props[TEXT("ProductVersion")]; 205 206 unsigned fv_1 = 0, fv_2 = 0, fv_3 = 0, fv_4 = 0; 207 unsigned pv_1 = 0, pv_2 = 0, pv_3 = 0, pv_4 = 0; 208 209 ret = _stscanf_s(fileVersion.c_str(), 210 TEXT("%d.%d.%d.%d"), &fv_1, &fv_2, &fv_3, &fv_4); 211 if (ret <= 0 || ret > 4) { 212 fwprintf(stderr, TEXT("Unable to parse FileVersion value\n")); 213 } 214 215 ret = _stscanf_s(productVersion.c_str(), 216 TEXT("%d.%d.%d.%d"), &pv_1, &pv_2, &pv_3, &pv_4); 217 if (ret <= 0 || ret > 4) { 218 fwprintf(stderr, TEXT("Unable to parse ProductVersion value\n")); 219 } 220 221 fxi->dwSignature = 0xFEEF04BD; 222 fxi->dwStrucVersion = 0x00010000; 223 224 fxi->dwFileVersionMS = MAKELONG(fv_2, fv_1); 225 fxi->dwFileVersionLS = MAKELONG(fv_4, fv_3); 226 fxi->dwProductVersionMS = MAKELONG(pv_2, pv_1); 227 fxi->dwProductVersionLS = MAKELONG(pv_4, pv_3); 228 229 fxi->dwFileFlagsMask = 0; 230 fxi->dwFileFlags = 0; 231 if (m_props.count(TEXT("PrivateBuild"))) { 232 fxi->dwFileFlags |= VS_FF_PRIVATEBUILD; 233 } 234 if (m_props.count(TEXT("SpecialBuild"))) { 235 fxi->dwFileFlags |= VS_FF_SPECIALBUILD; 236 } 237 fxi->dwFileOS = VOS_NT_WINDOWS32; 238 239 wstring exeExt = 240 m_exeFileName.substr(m_exeFileName.find_last_of(TEXT("."))); 241 if (exeExt == TEXT(".exe")) { 242 fxi->dwFileType = VFT_APP; 243 } 244 else if (exeExt == TEXT(".dll")) { 245 fxi->dwFileType = VFT_DLL; 246 } 247 else { 248 fxi->dwFileType = VFT_UNKNOWN; 249 } 250 fxi->dwFileSubtype = 0; 251 252 fxi->dwFileDateLS = 0; 253 fxi->dwFileDateMS = 0; 254 } 255 256 /* 257 * Adds new resource in the executable 258 */ 259 bool VersionInfoSwap::UpdateResource(LPVOID lpResLock, DWORD size) { 260 261 HANDLE hUpdateRes; 262 BOOL r; 263 264 hUpdateRes = ::BeginUpdateResource(m_exeFileName.c_str(), FALSE); 265 if (hUpdateRes == NULL) { 266 fwprintf(stderr, TEXT("Could not open file for writing\n")); 267 return false; 268 } 269 270 r = ::UpdateResource(hUpdateRes, 271 RT_VERSION, 272 MAKEINTRESOURCE(VS_VERSION_INFO), 273 MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 274 lpResLock, 275 size); 276 277 if (!r) { 278 fwprintf(stderr, TEXT("Could not add resource\n")); 279 return false; 280 } 281 282 if (!::EndUpdateResource(hUpdateRes, FALSE)) { 283 fwprintf(stderr, TEXT("Could not write changes to file\n")); 284 return false; 285 } 286 287 return true; 288 } 289 290 VersionInfoSwap::VersionInfoSwap(TCHAR *propFileName, TCHAR *exeFileName) 291 { 292 m_propFileName = propFileName; 293 m_exeFileName = exeFileName; 294 } 295 296 VersionInfoSwap::~VersionInfoSwap() 297 { 298 }