1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
|
/*
* BinInterop.cpp
*
* This file is a part of NSIS.
*
* Copyright (C) 2017-2018 Anders Kjersem
*
* Licensed under the zlib/libpng license (the "License");
* you may not use this file except in compliance with the License.
*
* Licence details can be found in the file COPYING.
*
* This software is provided 'as-is', without any express or implied
* warranty.
*
*/
#include "BinInterop.h"
#include "ResourceEditor.h"
#include "util.h"
#include <string.h> // strlen
#include <wchar.h> // _tcstoul
#include <stdexcept>
#define MKPTR(cast, base, offset) ( (cast) ( ((char*)(base)) + (offset) ) )
#define LE2HE16 FIX_ENDIAN_INT16 // Little-endian 2 Host-endian
#define LE2HE32 FIX_ENDIAN_INT32
#define HE2LE16 LE2HE16
const size_t invalid_res_id = ~(size_t)0;
FILE* MSTLB_fopen(const TCHAR*filepath, size_t*pResId)
{
size_t resid = invalid_res_id; // MSDN:"By default, the type library is extracted from the first resource of type ITypeLib"
FILE*f = FOPEN(filepath, ("rb"));
if (!f)
{
// Retry with the "filename.exe\1234" syntax supported by LoadTypeLib
tstring name = get_file_name(filepath), parent = get_dir_name(filepath);
const TCHAR *pStart;
TCHAR *pEnd;
resid = _tcstoul(pStart = name.c_str(), &pEnd, 10);
if (pEnd != pStart && !*pEnd && (f = FOPEN(parent.c_str(), ("rb"))))
{
USHORT mz, valid = false;
valid = fread(&mz, 1, sizeof(mz), f) == 2 && (mz == 0x4d5a || mz == 0x5a4d);
if (!valid) fclose(f), f = 0; // Only allow the special syntax on executable files
}
}
if (pResId) *pResId = resid;
return f;
}
#if !defined(_WIN32) || defined(NSIS_GETTLBVERSION_FORCEINTERNAL)
#if 0
// midl /DOLDTLB=1 /oldtlb /tlb SLTG.tlb test.idl && midl /DNEWTLB=1 /newtlb /tlb MSFT.tlb test.idl
#ifdef NEWTLB
import "unknwn.idl";
[ object, uuid(42424242-1111-1111-0001-424242424242) ] interface IInTeRfAcE1 : IUnknown { HRESULT I1_MeThOd1(); };
[ object, uuid(42424242-1111-1111-0002-424242424242) ] interface IInTeRfAcE2 : IUnknown { [idempotent] HRESULT I2_MeThOd1(); };
#endif
[ //msdn.microsoft.com/en-us/library/windows/desktop/aa367069
uuid(42424242-1234-1234-1234-424242424242), lcid(0x0809), version(666.1337),
helpstring("HeLpStRiNg"), // ICreateTypeInfo::SetDocString?
helpfile("HeLpFiLe"), helpcontext(0xBABEFACE),
#ifdef NEWTLB
helpstringdll("HeLpStRiNgDlL"), helpstringcontext(0xF00DBABE),
#endif
control, /* LIBFLAG_FCONTROL */ hidden, /* LIBFLAG_FHIDDEN */ restricted, /* LIBFLAG_FRESTRICTED */
]
library LiBnAmE
{
#ifdef NEWTLB
//importlib("stdole2.tlb");
#else
importlib("stdole32.tlb");
#endif
[ uuid(42424242-0000-0000-0000-424242424242), helpstring("CoClAsSHeLpStRiNg") ]
coclass CoClAsS
{
#ifdef NEWTLB
[default] interface IInTeRfAcE1; interface IInTeRfAcE2;
#else
[default] interface IUnknown;
#endif
};
};
#endif //~ .IDL
#pragma pack(1)
typedef struct {
UINT32 Sig; // 'MSFT'
USHORT FmtVerMaj, FmtVerMin; // This is just a guess, always seems to be 2.1?
UINT32 Unknown;
UINT32 UnkLocale; // idl:library:lcid
UINT32 Locale; // idl:library:lcid & ICreateTypeLib::SetLcid? This is the LCID returned in TLIBATTR.
UINT32 FlagsAndSK; // 0x03=tagSYSKIND mask. 0x0010=idl:library:helpfile 0x0100=idl:library:helpstringdll
USHORT VerMaj, VerMin; // ICreateTypeLib::SetVersion?
USHORT LibFlags; // tagLIBFLAGS (TLIBATTR.wLibFlags)
USHORT Unknown2;
UINT32 Unknown3; // Count of? This changes when the number of interfaces in the .idl changes.
UINT32 HelpString;
UINT32 HelpStringContext;
UINT32 HelpContext;
// ...?
} MSTLB_MSFT_MINIHEADER;
typedef struct {
UINT32 Sig; // 'SLTG'
USHORT Count; // Count of stream descriptors
USHORT CompObjFooterSize, CompObjHeaderSize;
USHORT First; // Index of the first stream (Streams must be enumerated in the correct order!)
char Guid[16]; // DEFINE_OLEGUID(CLSID_?, 0x000204ff, 0, 0)
} MSTLB_SLTG_HEADER;
typedef struct {
BYTE Sig[1+7+1+3+1]; // "\001CompObj\0dir" (Is "dir" part of the signature or is it the offset in one of the stream descriptors?)
} MSTLB_SLTG_COMPOBJ;
typedef struct {
UINT32 Size; // Size of stream
USHORT Unknown; // Offset to something in the COMPOBJ header?
USHORT Next; // Next stream index
} MSTLB_SLTG_SD;
typedef struct {
USHORT Len; // 0xffff if there is no string.
} MSTLB_SLTG_CSHDR; // Counted narrow string without \0 terminator.
typedef struct {
enum { SIG = 0x51cc, CSCOUNT = 3 };
USHORT Sig; // 'CC51'
USHORT Unknown[2];
MSTLB_SLTG_CSHDR Strings[CSCOUNT]; // (Unknown, idl:library:helpstring and idl:library:helpfile)
} MSTLB_SLTG_BLOCK_LIBATTR_HEADER;
typedef struct {
UINT32 HelpContext; // idl:library:helpcontext
USHORT SysKind; // tagSYSKIND (All 16-bits returned in TLIBATTR.syskind)
UINT32 Locale;
USHORT BadLocale; // If this is non-zero the returned TLIBATTR LCID is 0.
USHORT LibFlags; // tagLIBFLAGS (TLIBATTR.wLibFlags)
USHORT VerMaj, VerMin;
char LibGuid[16]; // idl:library:uuid
//...?
} MSTLB_SLTG_BLOCK_LIBATTR_FOOTER;
#pragma pack()
static bool MSTLB_GetVersion_MSFT(const void*pData, size_t cbData, DWORD &high, DWORD &low)
{
if (cbData >= sizeof(MSTLB_MSFT_MINIHEADER))
{
const MSTLB_MSFT_MINIHEADER &h = *(MSTLB_MSFT_MINIHEADER*) pData;
if (h.Sig == FIX_ENDIAN_INT32(0x5446534D))
{
if (FIX_ENDIAN_INT16(h.FmtVerMaj) == 2 && FIX_ENDIAN_INT16(h.FmtVerMin) == 1) // Is this always 2.1?
{
// lcid = FIX_ENDIAN_INT32(h.Locale);
// sysk = FIX_ENDIAN_INT32(h.FlagsAndSK) & 0x03;
// libf = FIX_ENDIAN_INT16(h.LibFlags) | 0x08; // 0x08 for LIBFLAG_FHASDISKIMAGE
high = FIX_ENDIAN_INT16(h.VerMaj), low = FIX_ENDIAN_INT16(h.VerMin);
return true;
}
}
}
return false;
}
static USHORT GetLenLEToHE(const MSTLB_SLTG_CSHDR &s)
{
USHORT len = FIX_ENDIAN_INT16(s.Len);
return len != 0xffff ? len : 0;
}
static bool MSTLB_IsSerializedOleGuid(const void*pData, UINT32 Bits1 = 0, UINT32 Mask1 = 0)
{
bool failed = false;
failed |= (FIX_ENDIAN_INT32(*MKPTR(UINT32*, pData, 4*0)) & Mask1) != Bits1;
failed |= (FIX_ENDIAN_INT32(*MKPTR(UINT32*, pData, 4*1))) != 0x00000000;
failed |= (FIX_ENDIAN_INT32(*MKPTR(UINT32*, pData, 4*2))) != 0x000000C0;
failed |= (FIX_ENDIAN_INT32(*MKPTR(UINT32*, pData, 4*3))) != 0x46000000;
return !failed; // Does it look like a DEFINE_OLEGUID() GUID?
}
static bool MSTLB_GetVersion_SLTG(const void*pData, size_t cbData, DWORD &high, DWORD &low)
{
if (cbData >= sizeof(MSTLB_SLTG_HEADER))
{
size_t eofPtr = MKPTR(size_t, pData, cbData);
const MSTLB_SLTG_HEADER &h = *(MSTLB_SLTG_HEADER*) pData;
if (h.Sig == FIX_ENDIAN_INT32(0x047544C53) && MSTLB_IsSerializedOleGuid(h.Guid, 0x00020400, 0xffffff00)) // 0x000204xx for IID_ITypeLib and friends
{
MSTLB_SLTG_SD *pSD = MKPTR(MSTLB_SLTG_SD*, &h, sizeof(MSTLB_SLTG_HEADER));
UINT32 streamCount = FIX_ENDIAN_INT16(h.Count);
// Check the data in each stream until we find the LIBATTR block
void *pFirstStreamData = MKPTR(void*, pSD, (sizeof(MSTLB_SLTG_SD) * streamCount) + FIX_ENDIAN_INT16(h.CompObjHeaderSize) + FIX_ENDIAN_INT16(h.CompObjFooterSize));
for (UINT32 tries = 0, i = FIX_ENDIAN_INT16(h.First), c = streamCount, o = 0; tries < c && i < c; ++tries)
{
MSTLB_SLTG_BLOCK_LIBATTR_HEADER *pBH = MKPTR(MSTLB_SLTG_BLOCK_LIBATTR_HEADER*, pFirstStreamData, o), *pD1 = pBH;
if (eofPtr < MKPTR(size_t, pBH, sizeof(USHORT))) break; // The stream must at least have a signature
if (FIX_ENDIAN_INT16(pBH->Sig) == pD1->SIG && eofPtr > MKPTR(size_t, pD1, sizeof(*pD1)))
{
unsigned long o2 = sizeof(USHORT) * 3; // Skip past the initial members
for (UINT32 strIdx = 0; strIdx < pD1->CSCOUNT; ++strIdx)
{
MSTLB_SLTG_CSHDR *pS = MKPTR(MSTLB_SLTG_CSHDR*, pD1, o2);
o2 += sizeof(MSTLB_SLTG_CSHDR) + GetLenLEToHE(*pS); // Skip past the embedded counted string
}
MSTLB_SLTG_BLOCK_LIBATTR_FOOTER *pD2 = MKPTR(MSTLB_SLTG_BLOCK_LIBATTR_FOOTER*, pD1, o2);
if (eofPtr < MKPTR(size_t, pD2, sizeof(*pD2))) break;
// lcid = FIX_ENDIAN_INT32(pD2->Locale);
// sysk = FIX_ENDIAN_INT16(pD2->SysKind);
// libf = FIX_ENDIAN_INT16(pD2->LibFlags);
high = FIX_ENDIAN_INT16(pD2->VerMaj), low = FIX_ENDIAN_INT16(pD2->VerMin);
return true;
}
o += FIX_ENDIAN_INT32(pSD[i].Size), i = FIX_ENDIAN_INT16(pSD[i].Next);
}
}
}
return false;
}
static bool MSTLB_GetVersion(const void*pData, size_t cbData, DWORD &high, DWORD &low)
{
bool rv = MSTLB_GetVersion_MSFT(pData, cbData, high, low);
if (!rv) rv = MSTLB_GetVersion_SLTG(pData, cbData, high, low);
return rv;
}
static BYTE* GetResource(CResourceEditor&RE, const TCHAR*RT, int RN, int RL, size_t&cbData)
{
BYTE *pResData = RE.GetResource(RT, RN, RL);
if (pResData) cbData = RE.GetResourceSize(RT, RN, RL);
return pResData;
}
static bool GetTLBVersionUsingRE(const void*pPEFile, size_t cbData, size_t resid, DWORD &high, DWORD &low)
{
bool result = false;
try
{
const TCHAR* rt = _T("TYPELIB");
int rn = (int) resid, rl = CResourceEditor::ANYLANGID;
CResourceEditor re((void*) pPEFile, (int) cbData);
BYTE *pResData = resid == invalid_res_id ? re.GetFirstResource(rt, cbData) : GetResource(re, rt, rn, rl, cbData);
if (pResData)
{
result = MSTLB_GetVersion(pResData, cbData, high, low);
re.FreeResource(pResData);
}
}
catch (std::exception&)
{
}
return result;
}
static bool GetTLBVersionInterop(const TCHAR *filepath, DWORD &high, DWORD &low)
{
unsigned long size;
size_t resid;
FILE *f = MSTLB_fopen(filepath, &resid);
bool result = false, resonly = invalid_res_id != resid;
void *pFileData = alloc_and_read_file(f, size);
if (f) fclose(f);
if (pFileData)
{
if (!result && !resonly) result = MSTLB_GetVersion(pFileData, size, high, low); // A raw TLB file?
if (!result) result = GetTLBVersionUsingRE(pFileData, size, resid, high, low); // A resource in a PE file?
// TODO: if (!result) result = GetTLBVersion16(pFileData, size, resid, high, low); // A resource in a 16-bit executable?
free(pFileData);
}
// Not supported: if (!result) result = GetTLBVersionFromMoniker(filepath, high, low);
return result;
}
#else // !_WIN32
static bool GetTLBVersionUsingAPI(const TCHAR *filepath, DWORD &high, DWORD &low)
{
bool found = false;
HRESULT hr;
ITypeLib *pTL;
TCHAR fullpath[1024], *p;
if (!GetFullPathName(filepath, COUNTOF(fullpath), fullpath, &p)) return false;
#ifdef _UNICODE
hr = LoadTypeLib(fullpath, &pTL);
#else
WCHAR *olepath = (WCHAR*) WinWStrDupFromTChar(fullpath);
if (!olepath) return false;
hr = LoadTypeLib(olepath, &pTL);
free(olepath);
#endif //~ _UNICODE
if (SUCCEEDED(hr))
{
TLIBATTR *tlatt;
hr = pTL->GetLibAttr(&tlatt);
if (SUCCEEDED(hr))
{
high = tlatt->wMajorVerNum, low = tlatt->wMinorVerNum;
found = true;
}
pTL->Release();
}
return found;
}
#endif //~ !_WIN32
bool GetTLBVersion(const TCHAR *filepath, DWORD &high, DWORD &low)
{
bool found = false;
#if defined(_WIN32) && !defined(NSIS_GETTLBVERSION_FORCEINTERNAL)
found = GetTLBVersionUsingAPI(filepath, high, low);
#else //! _WIN32
found = GetTLBVersionInterop(filepath, high, low);
#endif //~ _WIN32
return found;
}
static bool GetDLLVersionUsingRE(const TCHAR *filepath, DWORD &high, DWORD &low)
{
bool found = false;
LANGID anylangid = CResourceEditor::ANYLANGID;
unsigned long fileSize;
void*pFileData = alloc_and_read_file(filepath, fileSize);
if (!pFileData) return false;
try
{
CResourceEditor re(pFileData, fileSize);
LPBYTE resdata = re.GetResource(VS_FILE_INFO, VS_VERSION_INFO, anylangid);
if (resdata)
{
size_t ressize = re.GetResourceSize(VS_FILE_INFO, VS_VERSION_INFO, anylangid);
size_t vsvhdrsize = sizeof(WORD) * 3;
if (ressize > vsvhdrsize)
{
// Locate VS_FIXEDFILEINFO inside VS_VERSIONINFO
WINWCHAR *szKey = (WINWCHAR*)(resdata + vsvhdrsize);
size_t len = vsvhdrsize + (WinWStrLen(szKey) + 1) * sizeof(WINWCHAR);
len = (len + 3) & ~3; // Align on DWORD boundary
VS_FIXEDFILEINFO *verinfo = (VS_FIXEDFILEINFO*)(resdata + len);
if (ressize >= len + sizeof(VS_FIXEDFILEINFO) && verinfo->dwSignature == FIX_ENDIAN_INT32(VS_FFI_SIGNATURE))
{
high = FIX_ENDIAN_INT32(verinfo->dwFileVersionMS), low = FIX_ENDIAN_INT32(verinfo->dwFileVersionLS);
found = true;
}
}
re.FreeResource(resdata);
}
}
catch (std::exception&)
{
}
free(pFileData);
return found;
}
static bool GetDLLVersionUsingAPI(const TCHAR *filepath, DWORD &high, DWORD &low)
{
bool found = false;
#ifdef _WIN32
TCHAR path[1024], *name;
path[0] = 0;
GetFullPathName(filepath, 1024, path, &name);
DWORD ignore, verSize = GetFileVersionInfoSize(path, &ignore);
if (verSize)
{
void *buf = malloc(verSize);
if (buf)
{
UINT valSize;
VS_FIXEDFILEINFO *pvsf;
if (GetFileVersionInfo(path, 0, verSize, buf) && VerQueryValue(buf, _T("\\"), (void**) &pvsf, &valSize))
{
high = pvsf->dwFileVersionMS, low = pvsf->dwFileVersionLS;
found = true;
}
free(buf);
}
}
#endif
return found;
}
#pragma pack(push, 1)
typedef struct tagMINI_IMAGE_VXD_HEADER {
WORD e32_magic, endian;
BYTE data[180];
DWORD e32_winresoff, e32_winreslen;
WORD e32_devid, e32_ddkver;
} MINI_IMAGE_VXD_HEADER, *PMINI_IMAGE_VXD_HEADER;
#pragma pack(pop)
static bool GetDLLVersionFromVXD(const TCHAR *filepath, DWORD &high, DWORD &low)
{
bool found = false;
FILEVIEW map;
char *filedata = create_file_view_readonly(filepath, map);
if (filedata)
{
PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER) filedata;
if ((pDosHdr->e_magic == 0x5A4D) | (pDosHdr->e_magic == 0x4D5A))
{
PMINI_IMAGE_VXD_HEADER pVxdHdr = MKPTR(PMINI_IMAGE_VXD_HEADER, pDosHdr, LE2HE32(pDosHdr->e_lfanew));
if (pVxdHdr->e32_magic == HE2LE16(0x454C) && pVxdHdr->endian == 0) // Is it a little-endian VXD?
{
UINT minvsvi16 = 2 + 2 + 16, minvsffi = 52, minressecsize = ((1 + 2) + (1 + 2) + 2 + 4) + minvsvi16 + minvsffi;
PMINI_IMAGE_VXD_HEADER pLEHdr = pVxdHdr;
UINT ressecsize = LE2HE32(pLEHdr->e32_winreslen);
if (ressecsize >= minressecsize && pLEHdr->e32_winresoff != 0)
{
// MSKB201685 just assumes that the first item is the version and we do the same.
char *pRes = MKPTR(char*, pDosHdr, LE2HE32(pLEHdr->e32_winresoff));
UINT ressize, ofs = 3, succ = *MKPTR(BYTE*, pRes, 0) == 0xff && *MKPTR(WORD*, pRes, 1) == HE2LE16(16); // RT_VERSION
if (succ) succ = *MKPTR(BYTE*, pRes, ofs) == 0xff ? (ofs += (1 + 2) + 2) : (ofs += ((UINT)strlen(MKPTR(char*, pRes, ofs)) + !0) + 2); // Ordinal or string name
if (succ) succ = ofs + 4 < ressecsize;
if (succ) ressize = LE2HE32(*MKPTR(DWORD*, pRes, ofs)), ofs += 4;
if (succ && ressize >= minvsvi16 + minvsffi && ressize < ressecsize)
{
WORD *pVSVI = MKPTR(WORD*, pRes, ofs); // VS_VERSIONINFO (16-bit/ASCII version)
if (LE2HE16(pVSVI[0]) >= minvsvi16 + minvsffi && LE2HE16(pVSVI[1]) >= minvsffi && !memcmp(&pVSVI[2], "VS_VERSION_INFO", 16))
{
VS_FIXEDFILEINFO *pFFI = MKPTR(VS_FIXEDFILEINFO*, pVSVI, 2 + 2 + 16);
if (LE2HE32(pFFI->dwSignature) == 0xFEEF04BD)
{
high = LE2HE32(pFFI->dwFileVersionMS), low = LE2HE32(pFFI->dwFileVersionLS);
found = true;
}
}
}
}
}
}
close_file_view(map);
}
return found;
}
bool GetDLLVersion(const TCHAR *filepath, DWORD &high, DWORD &low)
{
bool result = GetDLLVersionUsingAPI(filepath, high, low);
if (!result) result = GetDLLVersionUsingRE(filepath, high, low);
if (!result) result = GetDLLVersionFromVXD(filepath, high, low);
return result;
}
|