PageRenderTime 92ms CodeModel.GetById 19ms app.highlight 68ms RepoModel.GetById 1ms app.codeStats 0ms

/xbmc/cores/DllLoader/Win32DllLoader.cpp

http://github.com/xbmc/xbmc
C++ | 405 lines | 308 code | 57 blank | 40 comment | 51 complexity | 251b6d170751bc1d114833e8a46e0966 MD5 | raw file
  1/*
  2 *  Copyright (C) 2005-2018 Team Kodi
  3 *  This file is part of Kodi - https://kodi.tv
  4 *
  5 *  SPDX-License-Identifier: GPL-2.0-or-later
  6 *  See LICENSES/README.md for more information.
  7 */
  8
  9#include "Win32DllLoader.h"
 10
 11#include "DllLoader.h"
 12#include "DllLoaderContainer.h"
 13#include "dll_tracker_file.h"
 14#include "dll_tracker_library.h"
 15#include "exports/emu_msvcrt.h"
 16#include "filesystem/SpecialProtocol.h"
 17#include "utils/StringUtils.h"
 18#include "utils/log.h"
 19
 20#include "platform/win32/CharsetConverter.h"
 21
 22#include <limits>
 23
 24extern "C" FARPROC WINAPI dllWin32GetProcAddress(HMODULE hModule, LPCSTR function);
 25
 26//dllLoadLibraryA, dllFreeLibrary, dllGetProcAddress are from dllLoader,
 27//they are wrapper functions of COFF/PE32 loader.
 28extern "C" HMODULE WINAPI dllLoadLibraryA(LPCSTR libname);
 29extern "C" BOOL WINAPI dllFreeLibrary(HINSTANCE hLibModule);
 30
 31// our exports
 32Export win32_exports[] =
 33{
 34  { "LoadLibraryA",                                 -1, (void*)dllLoadLibraryA,                              (void*)track_LoadLibraryA },
 35  { "FreeLibrary",                                  -1, (void*)dllFreeLibrary,                               (void*)track_FreeLibrary },
 36// msvcrt
 37  { "_close",                     -1, (void*)dll_close,                     (void*)track_close},
 38  { "_lseek",                     -1, (void*)dll_lseek,                     NULL },
 39  { "_read",                      -1, (void*)dll_read,                      NULL },
 40  { "_write",                     -1, (void*)dll_write,                     NULL },
 41  { "_lseeki64",                  -1, (void*)dll_lseeki64,                  NULL },
 42  { "_open",                      -1, (void*)dll_open,                      (void*)track_open },
 43  { "fflush",                     -1, (void*)dll_fflush,                    NULL },
 44  { "fprintf",                    -1, (void*)dll_fprintf,                   NULL },
 45  { "fwrite",                     -1, (void*)dll_fwrite,                    NULL },
 46  { "putchar",                    -1, (void*)dll_putchar,                   NULL },
 47  { "_fstat",                     -1, (void*)dll_fstat,                     NULL },
 48  { "_mkdir",                     -1, (void*)dll_mkdir,                     NULL },
 49  { "_stat",                      -1, (void*)dll_stat,                      NULL },
 50  { "_fstat32",                   -1, (void*)dll_fstat,                     NULL },
 51  { "_stat32",                    -1, (void*)dll_stat,                      NULL },
 52  { "_findclose",                 -1, (void*)dll_findclose,                 NULL },
 53  { "_findfirst",                 -1, (void*)dll_findfirst,                 NULL },
 54  { "_findnext",                  -1, (void*)dll_findnext,                  NULL },
 55  { "_findfirst64i32",            -1, (void*)dll_findfirst64i32,            NULL },
 56  { "_findnext64i32",             -1, (void*)dll_findnext64i32,             NULL },
 57  { "fclose",                     -1, (void*)dll_fclose,                    (void*)track_fclose},
 58  { "feof",                       -1, (void*)dll_feof,                      NULL },
 59  { "fgets",                      -1, (void*)dll_fgets,                     NULL },
 60  { "fopen",                      -1, (void*)dll_fopen,                     (void*)track_fopen},
 61  { "fopen_s",                    -1, (void*)dll_fopen_s,                   NULL },
 62  { "putc",                       -1, (void*)dll_putc,                      NULL },
 63  { "fputc",                      -1, (void*)dll_fputc,                     NULL },
 64  { "fputs",                      -1, (void*)dll_fputs,                     NULL },
 65  { "fread",                      -1, (void*)dll_fread,                     NULL },
 66  { "fseek",                      -1, (void*)dll_fseek,                     NULL },
 67  { "ftell",                      -1, (void*)dll_ftell,                     NULL },
 68  { "getc",                       -1, (void*)dll_getc,                      NULL },
 69  { "fgetc",                      -1, (void*)dll_getc,                      NULL },
 70  { "rewind",                     -1, (void*)dll_rewind,                    NULL },
 71  { "vfprintf",                   -1, (void*)dll_vfprintf,                  NULL },
 72  { "fgetpos",                    -1, (void*)dll_fgetpos,                   NULL },
 73  { "fsetpos",                    -1, (void*)dll_fsetpos,                   NULL },
 74  { "_stati64",                   -1, (void*)dll_stati64,                   NULL },
 75  { "_stat64",                    -1, (void*)dll_stat64,                    NULL },
 76  { "_stat64i32",                 -1, (void*)dll_stat64i32,                 NULL },
 77  { "_fstati64",                  -1, (void*)dll_fstati64,                  NULL },
 78  { "_fstat64",                   -1, (void*)dll_fstat64,                   NULL },
 79  { "_fstat64i32",                -1, (void*)dll_fstat64i32,                NULL },
 80  { "_telli64",                   -1, (void*)dll_telli64,                   NULL },
 81  { "_tell",                      -1, (void*)dll_tell,                      NULL },
 82  { "_fileno",                    -1, (void*)dll_fileno,                    NULL },
 83  { "ferror",                     -1, (void*)dll_ferror,                    NULL },
 84  { "freopen",                    -1, (void*)dll_freopen,                   (void*)track_freopen},
 85  { "fscanf",                     -1, (void*)dll_fscanf,                    NULL },
 86  { "ungetc",                     -1, (void*)dll_ungetc,                    NULL },
 87  { "_fdopen",                    -1, (void*)dll_fdopen,                    NULL },
 88  { "clearerr",                   -1, (void*)dll_clearerr,                  NULL },
 89  // for debugging
 90  { "printf",                     -1, (void*)dllprintf,                     NULL },
 91  { "vprintf",                    -1, (void*)dllvprintf,                    NULL },
 92  { "perror",                     -1, (void*)dllperror,                     NULL },
 93  { "puts",                       -1, (void*)dllputs,                       NULL },
 94  // workarounds for non-win32 signals
 95  { "signal",                     -1, (void*)dll_signal,                    NULL },
 96
 97  // libdvdnav + python need this (due to us using dll_putenv() to put stuff only?)
 98  { "getenv",                     -1, (void*)dll_getenv,                    NULL },
 99  { "_environ",                   -1, (void*)&dll__environ,                 NULL },
100  { "_open_osfhandle",            -1, (void*)dll_open_osfhandle,            NULL },
101
102  { NULL,                          -1, NULL,                                NULL }
103};
104
105Win32DllLoader::Win32DllLoader(const std::string& dll, bool isSystemDll)
106  : LibraryLoader(dll)
107  , bIsSystemDll(isSystemDll)
108{
109  m_dllHandle = NULL;
110  DllLoaderContainer::RegisterDll(this);
111}
112
113Win32DllLoader::~Win32DllLoader()
114{
115  if (m_dllHandle)
116    Unload();
117  DllLoaderContainer::UnRegisterDll(this);
118}
119
120bool Win32DllLoader::Load()
121{
122  using namespace KODI::PLATFORM::WINDOWS;
123
124  if (m_dllHandle != NULL)
125    return true;
126
127  std::string strFileName = GetFileName();
128  auto strDllW = ToW(CSpecialProtocol::TranslatePath(strFileName));
129
130#ifdef TARGET_WINDOWS_STORE
131  // The path cannot be an absolute path or a relative path that contains ".." in the path.
132  auto appPath = winrt::Windows::ApplicationModel::Package::Current().InstalledLocation().Path();
133  size_t len = appPath.size();
134
135  if (!appPath.empty() && wcsnicmp(appPath.c_str(), strDllW.c_str(), len) == 0)
136  {
137    if (strDllW.at(len) == '\\' || strDllW.at(len) == '/')
138      len++;
139    std::wstring relative = strDllW.substr(len);
140    m_dllHandle = LoadPackagedLibrary(relative.c_str(), 0);
141  }
142  else
143    m_dllHandle = LoadPackagedLibrary(strDllW.c_str(), 0);
144#else
145  m_dllHandle = LoadLibraryExW(strDllW.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
146#endif
147
148  if (!m_dllHandle)
149  {
150    DWORD dw = GetLastError();
151    wchar_t* lpMsgBuf = NULL;
152    DWORD strLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPWSTR)&lpMsgBuf, 0, NULL);
153    if (strLen == 0)
154      strLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (LPWSTR)&lpMsgBuf, 0, NULL);
155
156    if (strLen != 0)
157    {
158      auto strMessage = FromW(lpMsgBuf, strLen);
159      CLog::Log(LOGERROR, "%s: Failed to load \"%s\" with error %lu: \"%s\"", __FUNCTION__, CSpecialProtocol::TranslatePath(strFileName).c_str(), dw, strMessage.c_str());
160    }
161    else
162      CLog::Log(LOGERROR, "%s: Failed to load \"%s\" with error %lu", __FUNCTION__, CSpecialProtocol::TranslatePath(strFileName).c_str(), dw);
163
164    LocalFree(lpMsgBuf);
165    return false;
166  }
167
168  // handle functions that the dll imports
169  if (NeedsHooking(strFileName.c_str()))
170    OverrideImports(strFileName);
171
172  return true;
173}
174
175void Win32DllLoader::Unload()
176{
177  // restore our imports
178  RestoreImports();
179
180  if (m_dllHandle)
181  {
182    if (!FreeLibrary(m_dllHandle))
183       CLog::Log(LOGERROR, "%s Unable to unload %s", __FUNCTION__, GetName());
184  }
185
186  m_dllHandle = NULL;
187}
188
189int Win32DllLoader::ResolveExport(const char* symbol, void** f, bool logging)
190{
191  if (!m_dllHandle && !Load())
192  {
193    if (logging)
194      CLog::Log(LOGWARNING, "%s - Unable to resolve: %s %s, reason: DLL not loaded", __FUNCTION__, GetName(), symbol);
195    return 0;
196  }
197
198  void *s = GetProcAddress(m_dllHandle, symbol);
199
200  if (!s)
201  {
202    if (logging)
203      CLog::Log(LOGWARNING, "%s - Unable to resolve: %s %s", __FUNCTION__, GetName(), symbol);
204    return 0;
205  }
206
207  *f = s;
208  return 1;
209}
210
211bool Win32DllLoader::IsSystemDll()
212{
213  return bIsSystemDll;
214}
215
216HMODULE Win32DllLoader::GetHModule()
217{
218  return m_dllHandle;
219}
220
221bool Win32DllLoader::HasSymbols()
222{
223  return false;
224}
225
226void Win32DllLoader::OverrideImports(const std::string &dll)
227{
228  using KODI::PLATFORM::WINDOWS::ToW;
229  auto strdllW = ToW(CSpecialProtocol::TranslatePath(dll));
230  auto image_base = reinterpret_cast<BYTE*>(m_dllHandle);
231
232  if (!image_base)
233  {
234    CLog::Log(LOGERROR, "%s - unable to GetModuleHandle for dll %s", __FUNCTION__, dll.c_str());
235    return;
236  }
237
238  auto dos_header = reinterpret_cast<PIMAGE_DOS_HEADER>(image_base);
239  auto nt_header = reinterpret_cast<PIMAGE_NT_HEADERS>(image_base + dos_header->e_lfanew); // e_lfanew = value at 0x3c
240
241  auto imp_desc = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(
242    image_base + nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
243
244  if (!imp_desc)
245  {
246    CLog::Log(LOGERROR, "%s - unable to get import directory for dll %s", __FUNCTION__, dll.c_str());
247    return;
248  }
249
250  // loop over all imported dlls
251  for (int i = 0; imp_desc[i].Characteristics != 0; i++)
252  {
253    auto dllName = reinterpret_cast<char*>(image_base + imp_desc[i].Name);
254
255    // check whether this is one of our dll's.
256    if (NeedsHooking(dllName))
257    {
258      // this will do a loadlibrary on it, which should effectively make sure that it's hooked
259      // Note that the library has obviously already been loaded by the OS (as it's implicitly linked)
260      // so all this will do is insert our hook and make sure our DllLoaderContainer knows about it
261      auto hModule = dllLoadLibraryA(dllName);
262      if (hModule)
263        m_referencedDlls.push_back(hModule);
264    }
265
266    PIMAGE_THUNK_DATA orig_first_thunk = reinterpret_cast<PIMAGE_THUNK_DATA>(image_base + imp_desc[i].OriginalFirstThunk);
267    PIMAGE_THUNK_DATA first_thunk = reinterpret_cast<PIMAGE_THUNK_DATA>(image_base + imp_desc[i].FirstThunk);
268
269    // and then loop over all imported functions
270    for (int j = 0; orig_first_thunk[j].u1.Function != 0; j++)
271    {
272      void *fixup = NULL;
273      if (orig_first_thunk[j].u1.Function & 0x80000000)
274        ResolveOrdinal(dllName, (orig_first_thunk[j].u1.Ordinal & 0x7fffffff), &fixup);
275      else
276      { // resolve by name
277        PIMAGE_IMPORT_BY_NAME orig_imports_by_name = (PIMAGE_IMPORT_BY_NAME)(
278          image_base + orig_first_thunk[j].u1.AddressOfData);
279
280        ResolveImport(dllName, (char*)orig_imports_by_name->Name, &fixup);
281      }/*
282      if (!fixup)
283      { // create a dummy function for tracking purposes
284        PIMAGE_IMPORT_BY_NAME orig_imports_by_name = (PIMAGE_IMPORT_BY_NAME)(
285          image_base + orig_first_thunk[j].u1.AddressOfData);
286        fixup = CreateDummyFunction(dllName, (char*)orig_imports_by_name->Name);
287      }*/
288      if (fixup)
289      {
290        // save the old function
291        Import import;
292        import.table = &first_thunk[j].u1.Function;
293        import.function = first_thunk[j].u1.Function;
294        m_overriddenImports.push_back(import);
295
296        DWORD old_prot = 0;
297
298        // change to protection settings so we can write to memory area
299        VirtualProtect((PVOID)&first_thunk[j].u1.Function, 4, PAGE_EXECUTE_READWRITE, &old_prot);
300
301        // patch the address of function to point to our overridden version
302        first_thunk[j].u1.Function = (uintptr_t)fixup;
303
304        // reset to old settings
305        VirtualProtect((PVOID)&first_thunk[j].u1.Function, 4, old_prot, &old_prot);
306      }
307    }
308  }
309}
310
311bool Win32DllLoader::NeedsHooking(const char *dllName)
312{
313  if ( !StringUtils::EndsWithNoCase(dllName, "libdvdcss-2.dll")
314  && !StringUtils::EndsWithNoCase(dllName, "libdvdnav.dll"))
315    return false;
316
317  LibraryLoader *loader = DllLoaderContainer::GetModule(dllName);
318  if (loader)
319  {
320    // may have hooked this already (we can have repeats in the import table)
321    for (unsigned int i = 0; i < m_referencedDlls.size(); i++)
322    {
323      if (loader->GetHModule() == m_referencedDlls[i])
324        return false;
325    }
326  }
327  return true;
328}
329
330void Win32DllLoader::RestoreImports()
331{
332  // first unhook any referenced dll's
333  for (auto& module : m_referencedDlls)
334    dllFreeLibrary(module);
335  m_referencedDlls.clear();
336
337  for (auto& import : m_overriddenImports)
338  {
339    // change to protection settings so we can write to memory area
340    DWORD old_prot = 0;
341    VirtualProtect(import.table, 4, PAGE_EXECUTE_READWRITE, &old_prot);
342
343    *static_cast<uintptr_t *>(import.table) = import.function;
344
345    // reset to old settings
346    VirtualProtect(import.table, 4, old_prot, &old_prot);
347  }
348}
349
350bool FunctionNeedsWrapping(Export *exports, const char *functionName, void **fixup)
351{
352  Export *exp = exports;
353  while (exp->name)
354  {
355    if (strcmp(exp->name, functionName) == 0)
356    { //! @todo Should we be tracking stuff?
357      if (0)
358        *fixup = exp->track_function;
359      else
360        *fixup = exp->function;
361      return true;
362    }
363    exp++;
364  }
365  return false;
366}
367
368bool Win32DllLoader::ResolveImport(const char *dllName, const char *functionName, void **fixup)
369{
370  return FunctionNeedsWrapping(win32_exports, functionName, fixup);
371}
372
373bool Win32DllLoader::ResolveOrdinal(const char *dllName, unsigned long ordinal, void **fixup)
374{
375  Export *exp = win32_exports;
376  while (exp->name)
377  {
378    if (exp->ordinal == ordinal)
379    { //! @todo Should we be tracking stuff?
380      if (0)
381        *fixup = exp->track_function;
382      else
383        *fixup = exp->function;
384      return true;
385    }
386    exp++;
387  }
388  return false;
389}
390
391extern "C" FARPROC __stdcall dllWin32GetProcAddress(HMODULE hModule, LPCSTR function)
392{
393  // if the high-order word is zero, then lpProcName is the function's ordinal value
394  if (reinterpret_cast<uintptr_t>(function) > std::numeric_limits<WORD>::max())
395  {
396    // first check whether this function is one of the ones we need to wrap
397    void *fixup = NULL;
398    if (FunctionNeedsWrapping(win32_exports, function, &fixup))
399      return (FARPROC)fixup;
400  }
401
402  // Nope
403  return GetProcAddress(hModule, function);
404}
405