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"
28 #include "lib/sysdep/os/win/wdll_delay_load.h"
29 
30 #include "lib/sysdep/cpu.h"
31 #include "lib/sysdep/os/win/win.h"
32 #include "lib/sysdep/os/win/winit.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)
60 } ImgDelayDescr, * PImgDelayDescr;
61 
62 typedef const ImgDelayDescr *   PCImgDelayDescr;
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
72 	dliNoteStartProcessing = dliStartProcessing,
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 {
89 	BOOL                fImportByName;
90 	union {
91 		LPCSTR          szProcName;
92 		DWORD           dwOrdinal;
93 	};
94 } DelayLoadProc;
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)
106 } DelayLoadInfo, * PDelayLoadInfo;
107 
108 
109 typedef FARPROC (WINAPI *PfnDliHook)(unsigned dliNotify, PDelayLoadInfo  pdli);
110 
111 
112 //-----------------------------------------------------------------------------
113 // load notification
114 
115 static WdllLoadNotify* notify_list;
116 
wdll_add_notify(WdllLoadNotify * notify)117 void wdll_add_notify(WdllLoadNotify* notify)
118 {
119 	notify->next = notify_list;
120 	notify_list = notify;
121 }
122 
notify_hook(unsigned dliNotify,PDelayLoadInfo pdli)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 //
150 EXTERN_C PfnDliHook __pfnDliNotifyHook2 = notify_hook;
151 
152 // This is the failure hook, dliNotify = {dliFailLoadLib|dliFailGetProc}
153 EXTERN_C PfnDliHook __pfnDliFailureHook2 = 0;
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
IndexFromPImgThunkData(PCImgThunkData pitdCur,PCImgThunkData pitdBase)169 IndexFromPImgThunkData(PCImgThunkData pitdCur, PCImgThunkData pitdBase) {
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>
PFromRva(RVA rva)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;
186     PCImgDelayDescr pidd;
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
CountOfImports(PCImgThunkData pitdBase)192 CountOfImports(PCImgThunkData pitdBase) {
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 {
ULIULI206 	ULI(PCImgDelayDescr pidd_)
207 	{
208 		pidd = pidd_;
209 		Link();
210 	}
211 
~ULIULI212 	~ULI() { Unlink();	}
213 
operator newULI214 	void* operator new(size_t cb) {	return ::LocalAlloc(LPTR, cb); }
operator deleteULI215 	void operator delete(void* pv) { ::LocalFree(pv); }
216 
UnlinkULI217 	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 
LinkULI227 	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 //
238 struct InternalImgDelayDescr {
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 
250 typedef InternalImgDelayDescr *         PIIDD;
251 typedef const InternalImgDelayDescr *   PCIIDD;
252 
253 static inline PIMAGE_NT_HEADERS WINAPI
PinhFromImageBase(HMODULE hmod)254 PinhFromImageBase(HMODULE hmod) {
255     return PIMAGE_NT_HEADERS(PBYTE(hmod) + PIMAGE_DOS_HEADER(hmod)->e_lfanew);
256     }
257 
258 static inline void WINAPI
OverlayIAT(PImgThunkData pitdDst,PCImgThunkData pitdSrc)259 OverlayIAT(PImgThunkData pitdDst, PCImgThunkData pitdSrc) {
260     memcpy(pitdDst, pitdSrc, CountOfImports(pitdDst) * sizeof IMAGE_THUNK_DATA);
261     }
262 
263 static inline DWORD WINAPI
TimeStampOfImage(PIMAGE_NT_HEADERS pinh)264 TimeStampOfImage(PIMAGE_NT_HEADERS pinh) {
265     return pinh->FileHeader.TimeDateStamp;
266     }
267 
268 static inline bool WINAPI
FLoadedAtPreferredAddress(PIMAGE_NT_HEADERS pinh,HMODULE hmod)269 FLoadedAtPreferredAddress(PIMAGE_NT_HEADERS pinh, HMODULE hmod) {
270     return UINT_PTR(hmod) == pinh->OptionalHeader.ImageBase;
271     }
272 
273 
__delayLoadHelper2(PCImgDelayDescr pidd,FARPROC * ppfnIATEntry)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 
UnloadAllDlls()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 
wdll_Shutdown()472 static Status wdll_Shutdown()
473 {
474 	UnloadAllDlls();
475 	return INFO::OK;
476 }
477