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