Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
wdll_delay_load.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2013 Wildfire Games
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 /*
24  * DLL delay loading and notification
25  */
26 
27 #include "precompiled.h"
29 
30 #include "lib/sysdep/cpu.h"
31 #include "lib/sysdep/os/win/win.h"
33 
34 WINIT_REGISTER_LATE_SHUTDOWN2(wdll_Shutdown); // last - DLLs are unloaded here
35 
36 //-----------------------------------------------------------------------------
37 // delay loading (modified from VC7 DelayHlp.cpp and DelayImp.h)
38 
39 #if MSC_VERSION && MSC_VERSION >= 1700
40 // FACILITY_VISUALCPP is already defined in winerror.h in VC2012
41 # undef FACILITY_VISUALCPP
42 #endif
43 #define FACILITY_VISUALCPP ((LONG)0x6d)
44 #define VcppException(sev,status) ((sev) | (FACILITY_VISUALCPP<<16) | status)
45 
46 typedef IMAGE_THUNK_DATA * PImgThunkData;
47 typedef const IMAGE_THUNK_DATA * PCImgThunkData;
48 typedef DWORD RVA;
49 
50 typedef struct ImgDelayDescr {
51  DWORD grAttrs; // attributes
52  RVA rvaDLLName; // RVA to dll name
53  RVA rvaHmod; // RVA of module handle
54  RVA rvaIAT; // RVA of the IAT
55  RVA rvaINT; // RVA of the INT
56  RVA rvaBoundIAT; // RVA of the optional bound IAT
57  RVA rvaUnloadIAT; // RVA of optional copy of original IAT
58  DWORD dwTimeStamp; // 0 if not bound,
59  // O.W. date/time stamp of DLL bound to (Old BIND)
61 
63 
64 enum DLAttr { // Delay Load Attributes
65  dlattrRva = 0x1 // RVAs are used instead of pointers
66  // Having this set indicates a VC7.0
67  // and above delay load descriptor.
68 };
69 
70 enum {
71  dliStartProcessing, // used to bypass or note helper only
73 
74  dliNotePreLoadLibrary, // called just before LoadLibrary, can
75  // override w/ new HMODULE return val
76  dliNotePreGetProcAddress, // called just before GetProcAddress, can
77  // override w/ new FARPROC return value
78  dliFailLoadLib, // failed to load library, fix it by
79  // returning a valid HMODULE
80  dliFailGetProc, // failed to get proc address, fix it by
81  // returning a valid FARPROC
82  dliNoteEndProcessing // called after all processing is done, no
83  // no bypass possible at this point except
84  // by longjmp()/throw()/RaiseException.
85 };
86 
87 
88 typedef struct DelayLoadProc {
90  union {
93  };
95 
96 
97 typedef struct DelayLoadInfo {
98  DWORD cb; // size of structure
99  PCImgDelayDescr pidd; // raw form of data (everything is there)
100  FARPROC * ppfn; // points to address of function to load
101  LPCSTR szDll; // name of dll
102  DelayLoadProc dlp; // name or ordinal of procedure
103  HMODULE hmodCur; // the hInstance of the library we have loaded
104  FARPROC pfnCur; // the actual function that will be called
105  DWORD dwLastError;// error received (if an error notification)
107 
108 
109 typedef FARPROC (WINAPI *PfnDliHook)(unsigned dliNotify, PDelayLoadInfo pdli);
110 
111 
112 //-----------------------------------------------------------------------------
113 // load notification
114 
116 
118 {
119  notify->next = notify_list;
120  notify_list = notify;
121 }
122 
123 static FARPROC WINAPI notify_hook(unsigned dliNotify, PDelayLoadInfo pdli)
124 {
125  if(dliNotify != dliNoteEndProcessing)
126  return 0;
127 
128  for(WdllLoadNotify* n = notify_list; n; n = n->next)
129  if(strncasecmp(pdli->szDll, n->dll_name, strlen(n->dll_name)) == 0)
130  n->func();
131 
132  return 0;
133 }
134 
135 
136 //-----------------------------------------------------------------------------
137 // hook
138 
139 // The "notify hook" gets called for every call to the
140 // delay load helper. This allows a user to hook every call and
141 // skip the delay load helper entirely.
142 //
143 // dliNotify == {
144 // dliStartProcessing |
145 // dliNotePreLoadLibrary |
146 // dliNotePreGetProc |
147 // dliNoteEndProcessing}
148 // on this call.
149 //
151 
152 // This is the failure hook, dliNotify = {dliFailLoadLib|dliFailGetProc}
154 
155 
156 
157 
158 
159 
160 
161 
162 #if !ICC_VERSION
163 #pragma intrinsic(strlen,memcmp,memcpy)
164 #endif
165 
166 // utility function for calculating the index of the current import
167 // for all the tables (INT, BIAT, UIAT, and IAT).
168 inline unsigned
170  return (unsigned) (pitdCur - pitdBase);
171  }
172 
173 // C++ template utility function for converting RVAs to pointers
174 //
175 extern "C" const IMAGE_DOS_HEADER __ImageBase;
176 
177 template <class X>
178 X PFromRva(RVA rva) {
179  return X(PBYTE(&__ImageBase) + rva);
180  }
181 
182 // structure definitions for the list of unload records
183 typedef struct UnloadInfo * PUnloadInfo;
184 typedef struct UnloadInfo {
185  PUnloadInfo puiNext;
187  } UnloadInfo;
188 
189 // utility function for calculating the count of imports given the base
190 // of the IAT. NB: this only works on a valid IAT!
191 inline unsigned
193  unsigned cRet = 0;
194  PCImgThunkData pitd = pitdBase;
195  while (pitd->u1.Function) {
196  pitd++;
197  cRet++;
198  }
199  return cRet;
200  }
201 
202 extern "C" PUnloadInfo __puiHead = 0;
203 
204 struct ULI : public UnloadInfo
205 {
207  {
208  pidd = pidd_;
209  Link();
210  }
211 
212  ~ULI() { Unlink(); }
213 
214  void* operator new(size_t cb) { return ::LocalAlloc(LPTR, cb); }
215  void operator delete(void* pv) { ::LocalFree(pv); }
216 
217  void Unlink()
218  {
219  PUnloadInfo* ppui = &__puiHead;
220 
221  while (*ppui && *ppui != this)
222  ppui = &((*ppui)->puiNext);
223  if (*ppui == this)
224  *ppui = puiNext;
225  }
226 
227  void Link()
228  {
229  puiNext = __puiHead;
230  __puiHead = this;
231  }
232 };
233 
234 
235 // For our own internal use, we convert to the old
236 // format for convenience.
237 //
239  DWORD grAttrs; // attributes
240  LPCSTR szName; // pointer to dll name
241  HMODULE * phmod; // address of module handle
242  PImgThunkData pIAT; // address of the IAT
243  PCImgThunkData pINT; // address of the INT
244  PCImgThunkData pBoundIAT; // address of the optional bound IAT
245  PCImgThunkData pUnloadIAT; // address of optional copy of original IAT
246  DWORD dwTimeStamp; // 0 if not bound,
247  // O.W. date/time stamp of DLL bound to (Old BIND)
248  };
249 
252 
253 static inline PIMAGE_NT_HEADERS WINAPI
254 PinhFromImageBase(HMODULE hmod) {
255  return PIMAGE_NT_HEADERS(PBYTE(hmod) + PIMAGE_DOS_HEADER(hmod)->e_lfanew);
256  }
257 
258 static inline void WINAPI
260  memcpy(pitdDst, pitdSrc, CountOfImports(pitdDst) * sizeof IMAGE_THUNK_DATA);
261  }
262 
263 static inline DWORD WINAPI
264 TimeStampOfImage(PIMAGE_NT_HEADERS pinh) {
265  return pinh->FileHeader.TimeDateStamp;
266  }
267 
268 static inline bool WINAPI
269 FLoadedAtPreferredAddress(PIMAGE_NT_HEADERS pinh, HMODULE hmod) {
270  return UINT_PTR(hmod) == pinh->OptionalHeader.ImageBase;
271  }
272 
273 
274 extern "C" FARPROC WINAPI __delayLoadHelper2(PCImgDelayDescr pidd, FARPROC* ppfnIATEntry)
275 {
276  // Set up some data we use for the hook procs but also useful for
277  // our own use
278  //
279  InternalImgDelayDescr idd = {
280  pidd->grAttrs,
281  PFromRva<LPCSTR>(pidd->rvaDLLName),
282  PFromRva<HMODULE*>(pidd->rvaHmod),
283  PFromRva<PImgThunkData>(pidd->rvaIAT),
284  PFromRva<PCImgThunkData>(pidd->rvaINT),
285  PFromRva<PCImgThunkData>(pidd->rvaBoundIAT),
286  PFromRva<PCImgThunkData>(pidd->rvaUnloadIAT),
287  pidd->dwTimeStamp
288  };
289 
290  DelayLoadInfo dli = {
291  sizeof(DelayLoadInfo), pidd, ppfnIATEntry, idd.szName,
292  { 0 }, 0, 0, 0
293  };
294 
295  if (!(idd.grAttrs & dlattrRva))
296  {
297  PDelayLoadInfo rgpdli[1] = { &dli };
298  RaiseException(VcppException(ERROR_SEVERITY_ERROR, ERROR_INVALID_PARAMETER), 0, 1, PULONG_PTR(rgpdli));
299  return 0;
300  }
301 
302  HMODULE hmod = *idd.phmod;
303 
304  // Calculate the index for the IAT entry in the import address table
305  // N.B. The INT entries are ordered the same as the IAT entries so
306  // the calculation can be done on the IAT side.
307  //
308  const unsigned iIAT = IndexFromPImgThunkData(PCImgThunkData(ppfnIATEntry), idd.pIAT);
309  const unsigned iINT = iIAT;
310 
311  PCImgThunkData pitd = &(idd.pINT[iINT]);
312 
313  dli.dlp.fImportByName = !IMAGE_SNAP_BY_ORDINAL(pitd->u1.Ordinal);
314 
315  if (dli.dlp.fImportByName)
316  dli.dlp.szProcName = LPCSTR(PFromRva<PIMAGE_IMPORT_BY_NAME>(RVA(UINT_PTR(pitd->u1.AddressOfData)))->Name);
317  else
318  dli.dlp.dwOrdinal = DWORD(IMAGE_ORDINAL(pitd->u1.Ordinal));
319 
320  // Call the initial hook. If it exists and returns a function pointer,
321  // abort the rest of the processing and just return it for the call.
322  //
323  FARPROC pfnRet = NULL;
324 
325  if (__pfnDliNotifyHook2) {
326  pfnRet = ((*__pfnDliNotifyHook2)(dliStartProcessing, &dli));
327 
328  if (pfnRet != NULL)
329  goto HookBypass;
330  }
331 
332  // Check to see if we need to try to load the library.
333  //
334  if (hmod == 0) {
335  if (__pfnDliNotifyHook2) {
336  hmod = HMODULE(((*__pfnDliNotifyHook2)(dliNotePreLoadLibrary, &dli)));
337  }
338  if (hmod == 0) {
339  hmod = ::LoadLibraryA(dli.szDll);
340  }
341  if (hmod == 0) {
342  dli.dwLastError = ::GetLastError();
343  if (__pfnDliFailureHook2) {
344  // when the hook is called on LoadLibrary failure, it will
345  // return 0 for failure and an hmod for the lib if it fixed
346  // the problem.
347  //
348  hmod = HMODULE((*__pfnDliFailureHook2)(dliFailLoadLib, &dli));
349  }
350 
351  if (hmod == 0) {
352  PDelayLoadInfo rgpdli[1] = { &dli };
353 
354  RaiseException(VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND),
355  0, 1, PULONG_PTR(rgpdli));
356 
357  // If we get to here, we blindly assume that the handler of the exception
358  // has magically fixed everything up and left the function pointer in
359  // dli.pfnCur.
360  //
361  return dli.pfnCur;
362  }
363  }
364 
365  // Store the library handle. If it is already there, we infer
366  // that another thread got there first, and we need to do a
367  // FreeLibrary() to reduce the refcount
368  //
369  HMODULE hmodT = HMODULE(InterlockedExchangePointer((PVOID *) idd.phmod, PVOID(hmod)));
370  if (hmodT != hmod) {
371  // add lib to unload list if we have unload data
372  if (pidd->rvaUnloadIAT) {
373  new ULI(pidd);
374  }
375  }
376  else {
377  ::FreeLibrary(hmod);
378  }
379 
380  }
381 
382  // Go for the procedure now.
383  //
384  dli.hmodCur = hmod;
385  if (__pfnDliNotifyHook2) {
386  pfnRet = (*__pfnDliNotifyHook2)(dliNotePreGetProcAddress, &dli);
387  }
388  if (pfnRet == 0) {
389  if (pidd->rvaBoundIAT && pidd->dwTimeStamp) {
390  // bound imports exist...check the timestamp from the target image
391  //
392  PIMAGE_NT_HEADERS pinh(PinhFromImageBase(hmod));
393 
394  if (pinh->Signature == IMAGE_NT_SIGNATURE &&
395  TimeStampOfImage(pinh) == idd.dwTimeStamp &&
396  FLoadedAtPreferredAddress(pinh, hmod)) {
397 
398  // Everything is good to go, if we have a decent address
399  // in the bound IAT!
400  //
401  pfnRet = FARPROC(UINT_PTR(idd.pBoundIAT[iIAT].u1.Function));
402  if (pfnRet != 0) {
403  goto SetEntryHookBypass;
404  }
405  }
406  }
407 
408  pfnRet = ::GetProcAddress(hmod, dli.dlp.szProcName);
409  }
410 
411  if (pfnRet == 0) {
412  dli.dwLastError = ::GetLastError();
413  if (__pfnDliFailureHook2) {
414  // when the hook is called on GetProcAddress failure, it will
415  // return 0 on failure and a valid proc address on success
416  //
417  pfnRet = (*__pfnDliFailureHook2)(dliFailGetProc, &dli);
418  }
419  if (pfnRet == 0) {
420  PDelayLoadInfo rgpdli[1] = { &dli };
421 
422  RaiseException(VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND),
423  0, 1, PULONG_PTR(rgpdli));
424 
425  // If we get to here, we blindly assume that the handler of the exception
426  // has magically fixed everything up and left the function pointer in
427  // dli.pfnCur.
428  //
429  pfnRet = dli.pfnCur;
430  }
431  }
432 
433 SetEntryHookBypass:
434  *ppfnIATEntry = pfnRet;
435 
436 HookBypass:
437  if (__pfnDliNotifyHook2) {
438  dli.dwLastError = 0;
439  dli.hmodCur = hmod;
440  dli.pfnCur = pfnRet;
441  (*__pfnDliNotifyHook2)(dliNoteEndProcessing, &dli);
442  }
443  return pfnRet;
444  }
445 
446 
447 static void UnloadAllDlls()
448 {
449  PUnloadInfo pui;
450 
451  // free all DLLs (avoid BoundsChecker warning)
452  while((pui = __puiHead) != 0)
453  if(pui->pidd->rvaUnloadIAT)
454  {
455  PCImgDelayDescr pidd = pui->pidd;
456  HMODULE* phmod = PFromRva<HMODULE*>(pidd->rvaHmod);
457  HMODULE hmod = *phmod;
458 
459  OverlayIAT(
460  PFromRva<PImgThunkData>(pidd->rvaIAT),
461  PFromRva<PCImgThunkData>(pidd->rvaUnloadIAT)
462  );
463  ::FreeLibrary(hmod);
464  *phmod = NULL;
465 
466  delete (ULI*)pui; // changes __puiHead!
467  }
468 }
469 
470 //-----------------------------------------------------------------------------
471 
473 {
474  UnloadAllDlls();
475  return INFO::OK;
476 }
PUnloadInfo __puiHead
static PIMAGE_NT_HEADERS WINAPI PinhFromImageBase(HMODULE hmod)
FARPROC WINAPI __delayLoadHelper2(PCImgDelayDescr pidd, FARPROC *ppfnIATEntry)
ULI(PCImgDelayDescr pidd_)
Definition: Decompose.h:22
#define EXTERN_C
const Status OK
Definition: status.h:386
const IMAGE_THUNK_DATA * PCImgThunkData
IMAGE_THUNK_DATA * PImgThunkData
void Unlink()
EXTERN_C PfnDliHook __pfnDliNotifyHook2
static Status wdll_Shutdown()
struct DelayLoadProc DelayLoadProc
unsigned CountOfImports(PCImgThunkData pitdBase)
#define WINAPI
Definition: zlib.h:35
int BOOL
Definition: wgl.h:51
struct UnloadInfo UnloadInfo
WdllLoadNotify * next
DelayLoadProc dlp
const InternalImgDelayDescr * PCIIDD
unsigned IndexFromPImgThunkData(PCImgThunkData pitdCur, PCImgThunkData pitdBase)
static void WINAPI OverlayIAT(PImgThunkData pitdDst, PCImgThunkData pitdSrc)
#define WINIT_REGISTER_LATE_SHUTDOWN2(func)
Definition: winit.h:158
unsigned long DWORD
Definition: wgl.h:56
struct DelayLoadInfo DelayLoadInfo
PCImgDelayDescr pidd
i64 Status
Error handling system.
Definition: status.h:171
static FARPROC WINAPI notify_hook(unsigned dliNotify, PDelayLoadInfo pdli)
PCImgDelayDescr pidd
PUnloadInfo puiNext
static WdllLoadNotify * notify_list
InternalImgDelayDescr * PIIDD
DWORD RVA
void wdll_add_notify(WdllLoadNotify *notify)
X PFromRva(RVA rva)
const ImgDelayDescr * PCImgDelayDescr
EXTERN_C PfnDliHook __pfnDliFailureHook2
static DWORD WINAPI TimeStampOfImage(PIMAGE_NT_HEADERS pinh)
static void UnloadAllDlls()
struct ImgDelayDescr * PImgDelayDescr
#define VcppException(sev, status)
void Link()
FARPROC(WINAPI * PfnDliHook)(unsigned dliNotify, PDelayLoadInfo pdli)
const char * LPCSTR
Definition: wgl.h:61
struct UnloadInfo * PUnloadInfo
static bool WINAPI FLoadedAtPreferredAddress(PIMAGE_NT_HEADERS pinh, HMODULE hmod)
const IMAGE_DOS_HEADER __ImageBase
struct DelayLoadInfo * PDelayLoadInfo
struct ImgDelayDescr ImgDelayDescr