From 5bb70ce76b4e7a2a79750fe76e3b1fe3827dd5fc Mon Sep 17 00:00:00 2001 From: anders_k Date: Wed, 4 Oct 2017 12:25:35 +0000 Subject: [PATCH] Added POSIX TLB reader functions git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@6914 212acab6-be3b-0410-9dea-997c60f758d6 --- Source/BinInterop.cpp | 411 ++++++++++++++++++++++++++++++++++++++++++ Source/BinInterop.h | 28 +++ 2 files changed, 439 insertions(+) create mode 100644 Source/BinInterop.cpp create mode 100644 Source/BinInterop.h diff --git a/Source/BinInterop.cpp b/Source/BinInterop.cpp new file mode 100644 index 00000000..964c6bce --- /dev/null +++ b/Source/BinInterop.cpp @@ -0,0 +1,411 @@ +/* + * BinInterop.cpp + * + * This file is a part of NSIS. + * + * Copyright (C) 2017 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 // _tcstoul +#include + +enum { DEBUGDUMP = 0 }; // Dump debug info on little-endian systems? + +#define MKPTR(cast, base, offset) ( (cast) ( ((char*)(base)) + (offset) ) ) +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) + +unsigned long get_file_size32(FILE*f) +{ + unsigned long error = ~(0UL), result = error, cb, restoreseek = false; + fpos_t orgpos; + if (restoreseek ? 0 == fgetpos(f, &orgpos) : true) + if (0 == fseek(f, 0, SEEK_END)) + if ((cb = ftell(f)) != -1L) + if (restoreseek ? 0 == fsetpos(f, &orgpos) : true) + result = cb; + return result; +} + +void* alloc_and_read_file(FILE*f, unsigned long&size) +{ + void *result = 0, *mem = 0; + if (!f) return result; + size = get_file_size32(f); + mem = (~(0UL) != size) ? malloc(size) : 0; + if (mem) + if (0 == fseek(f, 0, SEEK_SET)) + if (fread(mem, 1, size, f) == size) + result = mem, mem = 0; + free(mem); + return result; +} + +/*void* alloc_and_read_file(const TCHAR*filepath, unsigned long&size) +{ + void *result = 0; + FILE*f = FOPEN(filepath, ("rb")); + if (f) + result = alloc_and_read_file(f, size), fclose(f); + return result; +}*/ + +#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), + //oleautomation, //TYPEFLAG_FOLEAUTOMATION? + //dual, //"Specifying dual on an interface implies that the interface is compatible with Automation, and therefore causes both the TYPEFLAG_FDUAL and TYPEFLAG_FOLEAUTOMATION flags to be set" +] +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 (FIX_ENDIAN_INT32(h.Sig) == 0x5446534D) + { + if (DEBUGDUMP) + { + for (UINT32 i = 0; i <= min(cbData / 4, 20) && DEBUGDUMP > 1; ++i) + _tprintf(_T("%2x=%.8x (%.4x:%.4x)\n"), i * 4, ((UINT32*)&h)[i], ((USHORT*)&h)[(i*2)+0], ((USHORT*)&h)[(i*2)+1]); + } + + if (FIX_ENDIAN_INT16(h.FmtVerMaj) == 2 && FIX_ENDIAN_INT16(h.FmtVerMin) == 1) // Is this always 2.1? + { + if (DEBUGDUMP) + { + UINT32 fask = FIX_ENDIAN_INT32(h.FlagsAndSK), lcid = FIX_ENDIAN_INT32(h.Locale), lf = FIX_ENDIAN_INT16(h.LibFlags); + _tprintf(_T("lcid=%#.8x sk=%#.8x lf=%#.4x hc=%#x\n"), lcid, fask & 0x03, lf | 0x08, FIX_ENDIAN_INT32(h.HelpContext)); // 0x08 for LIBFLAG_FHASDISKIMAGE because it is not stored in the .TLB + } + + // 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 inline void DebugDumpLE(const MSTLB_SLTG_CSHDR &s, const char *prefix = 0) +{ + if (DEBUGDUMP) + { + USHORT len = GetLenLEToHE(s); + if (len) + { + if (prefix) _tprintf(_T("%") NPRIns _T(": "), prefix); + TCHAR fmt[50]; + _stprintf(fmt, _T("\"%%.%u") NPRIns _T("\"\n"), len); + _tprintf(fmt, MKPTR(char*, &s, sizeof(USHORT))); + } + } +} + +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 (FIX_ENDIAN_INT32(h.Sig) == 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); + // compobjCount = streamCount >= 1 ? streamCount - 1 : 0, compobjHdrSize = 13; + if (DEBUGDUMP) + { + for (UINT32 i = 0; i <= min(cbData / 4, 100 / 4) && DEBUGDUMP > 1; ++i) + _tprintf(_T("%2x=%.8x (%.4x:%.4x)\n"), i * 4, ((UINT32*)&h)[i], ((USHORT*)&h)[(i*2)+0], ((USHORT*)&h)[(i*2)+1]); + _tprintf(_T("Count=%d First=%d\n"), FIX_ENDIAN_INT16(h.Count), FIX_ENDIAN_INT16(h.First)); + for (UINT32 i = 0, c = streamCount; i < c; ++i) + _tprintf(_T("S%2d: %.8x=%-4u %.4x=%-2u %.4x=%-2u\n"), i, pSD[i].Size, pSD[i].Size, pSD[i].Unknown, pSD[i].Unknown, pSD[i].Next, pSD[i].Next); + } + + // 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 (DEBUGDUMP) + { + _tprintf(_T("Sig=%#.4x Size=%#.6x @ %#.6x in stream %u\n"), FIX_ENDIAN_INT16(pBH->Sig), FIX_ENDIAN_INT32(pSD[i].Size), (unsigned int) ((size_t) pBH - (size_t) pData), i); + } + + 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); + if (DEBUGDUMP) DebugDumpLE(*pS); + 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; + + if (DEBUGDUMP) + { + UINT32 sk = FIX_ENDIAN_INT16(pD2->SysKind), lcid = FIX_ENDIAN_INT32(pD2->Locale), lf = FIX_ENDIAN_INT16(pD2->LibFlags); + _tprintf(_T("lcid=%#.8x sk=%#.8x lf=%#.4x hc=%#x\n"), lcid, sk, lf, FIX_ENDIAN_INT32(pD2->HelpContext)); + } + + // 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((BYTE*) 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 *filedata = alloc_and_read_file(f, size); + if (f) fclose(f); + if (filedata) + { + if (!result && !resonly) result = MSTLB_GetVersion(filedata, size, high, low); // A raw TLB file? + if (!result) result = GetTLBVersionUsingRE(filedata, size, resid, high, low); // A resource in a PE file? + // TODO: if (!result) result = GetTLBVersion16(filedata, size, resid, high, low); // A resouce in a 16-bit executable? + free(filedata); + } + // 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)) + { + if (DEBUGDUMP) + { + _tprintf(_T("lcid=%#.8x sk=%#.8x lf=%#.4x\n"), tlatt->lcid, tlatt->syskind, tlatt->wLibFlags); + } + + 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; +} diff --git a/Source/BinInterop.h b/Source/BinInterop.h new file mode 100644 index 00000000..de064518 --- /dev/null +++ b/Source/BinInterop.h @@ -0,0 +1,28 @@ +/* + * BinInterop.h + * + * This file is a part of NSIS. + * + * Copyright (C) 2017 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. + * + */ + +#ifndef NSIS_BININTEROP_H +#define NSIS_BININTEROP_H + +#include "Platform.h" +#include "tchar.h" +#include // FILE* + +FILE* MSTLB_fopen(const TCHAR*filepath, size_t*pResId = 0); +bool GetTLBVersion(const TCHAR *filepath, DWORD &high, DWORD &low); + +#endif //~ NSIS_BININTEROP_H