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