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