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 using namespace std; 39 40 /* 41 * [Property file] contains key/value pairs 42 * The swap tool uses these pairs to create new version resource 43 * 44 * See MSDN docs for VS_VERSIONINFO structure that 45 * depicts organization of data in this version resource 46 * https://msdn.microsoft.com/en-us/library/ms647001(v=vs.85).aspx 47 * 48 * The swap tool makes changes in [Executable file] 49 * The tool assumes that the executable file has no version resource 50 * and it adds new resource in the executable file. 51 * If the executable file has an existing version resource, then 52 * the existing version resource will be replaced with new one. 53 */ 54 55 VersionInfoSwap::VersionInfoSwap(wstring executableProperties, wstring launcher) { 56 m_executableProperties = executableProperties; 57 m_launcher = launcher; 58 } 59 60 bool VersionInfoSwap::PatchExecutable() { 61 bool b = LoadFromPropertyFile(); 62 if (!b) { 63 return false; 64 } 65 66 ByteBuffer buf; 67 CreateNewResource(&buf); 68 b = this->UpdateResource(buf.getPtr(), static_cast<DWORD> (buf.getPos())); 69 if (!b) { 70 return false; 71 } 72 return true; 73 } 74 75 bool VersionInfoSwap::LoadFromPropertyFile() { 76 bool result = false; 77 wifstream stream(m_executableProperties.c_str()); 78 79 const locale empty_locale = locale::empty(); 80 const locale utf8_locale = 81 locale(empty_locale, new codecvt_utf8<wchar_t>()); 82 stream.imbue(utf8_locale); 83 84 if (stream.is_open() == true) { 85 int lineNumber = 1; 86 while (stream.eof() == false) { 87 wstring line; 88 getline(stream, line); 89 90 // # at the first character will comment out the line. 91 if (line.empty() == false && line[0] != '#') { 92 wstring::size_type pos = line.find('='); 93 if (pos != wstring::npos) { 94 wstring name = line.substr(0, pos); 95 wstring value = line.substr(pos + 1); 96 m_props[name] = value; 97 } else { 98 printf("Unable to find delimiter at line %d\n", lineNumber); 99 } 100 } 101 lineNumber++; 102 } 103 result = true; 104 } else { 105 printf("Unable to read property file\n"); 106 } 107 108 return result; 109 } 110 111 /* 112 * Creates new version resource 113 * 114 * MSND docs for VS_VERSION_INFO structure 115 * https://msdn.microsoft.com/en-us/library/ms647001(v=vs.85).aspx 116 */ 117 void VersionInfoSwap::CreateNewResource(ByteBuffer *buf) { 118 size_t versionInfoStart = buf->getPos(); 119 buf->AppendWORD(0); 120 buf->AppendWORD(sizeof VS_FIXEDFILEINFO); 121 buf->AppendWORD(0); 122 buf->AppendString(TEXT("VS_VERSION_INFO")); 123 buf->Align(4); 124 125 VS_FIXEDFILEINFO fxi; 126 FillFixedFileInfo(&fxi); 127 buf->AppendBytes((BYTE*) & fxi, sizeof (VS_FIXEDFILEINFO)); 128 buf->Align(4); 129 130 // String File Info 131 size_t stringFileInfoStart = buf->getPos(); 132 buf->AppendWORD(0); 133 buf->AppendWORD(0); 134 buf->AppendWORD(1); 135 buf->AppendString(TEXT("StringFileInfo")); 136 buf->Align(4); 137 138 // String Table 139 size_t stringTableStart = buf->getPos(); 140 buf->AppendWORD(0); 141 buf->AppendWORD(0); 142 buf->AppendWORD(1); 143 144 // "040904B0" = LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP 145 buf->AppendString(TEXT("040904B0")); 146 buf->Align(4); 147 148 // Strings 149 vector<wstring> keys; 150 for (map<wstring, wstring>::const_iterator it = 151 m_props.begin(); it != m_props.end(); ++it) { 152 keys.push_back(it->first); 153 } 154 155 for (size_t index = 0; index < keys.size(); index++) { 156 wstring name = keys[index]; 157 wstring value = m_props[name]; 158 159 size_t stringStart = buf->getPos(); 160 buf->AppendWORD(0); 161 buf->AppendWORD(static_cast<WORD> (value.length())); 162 buf->AppendWORD(1); 163 buf->AppendString(name); 164 buf->Align(4); 165 buf->AppendString(value); 166 buf->ReplaceWORD(stringStart, 167 static_cast<WORD> (buf->getPos() - stringStart)); 168 buf->Align(4); 169 } 170 171 buf->ReplaceWORD(stringTableStart, 172 static_cast<WORD> (buf->getPos() - stringTableStart)); 173 buf->ReplaceWORD(stringFileInfoStart, 174 static_cast<WORD> (buf->getPos() - stringFileInfoStart)); 175 176 // VarFileInfo 177 size_t varFileInfoStart = buf->getPos(); 178 buf->AppendWORD(1); 179 buf->AppendWORD(0); 180 buf->AppendWORD(1); 181 buf->AppendString(TEXT("VarFileInfo")); 182 buf->Align(4); 183 184 buf->AppendWORD(0x24); 185 buf->AppendWORD(0x04); 186 buf->AppendWORD(0x00); 187 buf->AppendString(TEXT("Translation")); 188 buf->Align(4); 189 // "040904B0" = LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP 190 buf->AppendWORD(0x0409); 191 buf->AppendWORD(0x04B0); 192 193 buf->ReplaceWORD(varFileInfoStart, 194 static_cast<WORD> (buf->getPos() - varFileInfoStart)); 195 buf->ReplaceWORD(versionInfoStart, 196 static_cast<WORD> (buf->getPos() - versionInfoStart)); 197 } 198 199 void VersionInfoSwap::FillFixedFileInfo(VS_FIXEDFILEINFO *fxi) { 200 wstring fileVersion; 201 wstring productVersion; 202 int ret; 203 204 fileVersion = m_props[TEXT("FileVersion")]; 205 productVersion = m_props[TEXT("ProductVersion")]; 206 207 unsigned fv_1 = 0, fv_2 = 0, fv_3 = 0, fv_4 = 0; 208 unsigned pv_1 = 0, pv_2 = 0, pv_3 = 0, pv_4 = 0; 209 210 ret = _stscanf_s(fileVersion.c_str(), 211 TEXT("%d.%d.%d.%d"), &fv_1, &fv_2, &fv_3, &fv_4); 212 if (ret <= 0 || ret > 4) { 213 printf("Unable to parse FileVersion value\n"); 214 } 215 216 ret = _stscanf_s(productVersion.c_str(), 217 TEXT("%d.%d.%d.%d"), &pv_1, &pv_2, &pv_3, &pv_4); 218 if (ret <= 0 || ret > 4) { 219 printf("Unable to parse ProductVersion value\n"); 220 } 221 222 fxi->dwSignature = 0xFEEF04BD; 223 fxi->dwStrucVersion = 0x00010000; 224 225 fxi->dwFileVersionMS = MAKELONG(fv_2, fv_1); 226 fxi->dwFileVersionLS = MAKELONG(fv_4, fv_3); 227 fxi->dwProductVersionMS = MAKELONG(pv_2, pv_1); 228 fxi->dwProductVersionLS = MAKELONG(pv_4, pv_3); 229 230 fxi->dwFileFlagsMask = 0; 231 fxi->dwFileFlags = 0; 232 if (m_props.count(TEXT("PrivateBuild"))) { 233 fxi->dwFileFlags |= VS_FF_PRIVATEBUILD; 234 } 235 if (m_props.count(TEXT("SpecialBuild"))) { 236 fxi->dwFileFlags |= VS_FF_SPECIALBUILD; 237 } 238 fxi->dwFileOS = VOS_NT_WINDOWS32; 239 240 wstring exeExt = 241 m_launcher.substr(m_launcher.find_last_of(TEXT("."))); 242 if (exeExt == TEXT(".exe")) { 243 fxi->dwFileType = VFT_APP; 244 } else if (exeExt == TEXT(".dll")) { 245 fxi->dwFileType = VFT_DLL; 246 } else { 247 fxi->dwFileType = VFT_UNKNOWN; 248 } 249 fxi->dwFileSubtype = 0; 250 251 fxi->dwFileDateLS = 0; 252 fxi->dwFileDateMS = 0; 253 } 254 255 /* 256 * Adds new resource in the executable 257 */ 258 bool VersionInfoSwap::UpdateResource(LPVOID lpResLock, DWORD size) { 259 260 HANDLE hUpdateRes; 261 BOOL r; 262 263 hUpdateRes = ::BeginUpdateResource(m_launcher.c_str(), FALSE); 264 if (hUpdateRes == NULL) { 265 printf("Could not open file for writing\n"); 266 return false; 267 } 268 269 r = ::UpdateResource(hUpdateRes, 270 RT_VERSION, 271 MAKEINTRESOURCE(VS_VERSION_INFO), 272 MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 273 lpResLock, 274 size); 275 276 if (!r) { 277 printf("Could not add resource\n"); 278 return false; 279 } 280 281 if (!::EndUpdateResource(hUpdateRes, FALSE)) { 282 printf("Could not write changes to file\n"); 283 return false; 284 } 285 286 return true; 287 }