568 lines
13 KiB
C++
568 lines
13 KiB
C++
#include "npstub.h"
|
|
#include <netspi.h>
|
|
#include <npord.h>
|
|
#include <nphook.h>
|
|
#include <npstubx.h> /* message defs, class name */
|
|
|
|
#define ARRAYSIZE(x) (sizeof(x)/sizeof((x)[0]))
|
|
|
|
HINSTANCE hInstance = NULL;
|
|
HWND hwndMonitor = NULL;
|
|
CRITICAL_SECTION critsec;
|
|
ATOM aClass = NULL;
|
|
|
|
#define ENTERCRITICAL EnterCriticalSection(&::critsec);
|
|
#define LEAVECRITICAL LeaveCriticalSection(&::critsec);
|
|
|
|
UINT cCallsInProgress = 0;
|
|
BOOL fShouldUnload = FALSE;
|
|
|
|
|
|
/* This chunk of code invokes the entrypoint hooking feature of MPR. We hook
|
|
* things and immediately unhook ourselves. We don't really want to hook
|
|
* any functionality, this is just a way to kick MPR so he'll redetermine
|
|
* the capabilities (via NPGetCaps) of all the net providers, including
|
|
* ours.
|
|
*/
|
|
F_NPSHookMPR HookHookMPR;
|
|
F_UnHookMPR HookUnHookMPR;
|
|
F_LoadLibrary HookLoadLibrary;
|
|
F_FreeLibrary HookFreeLibrary;
|
|
F_GetProcAddress HookGetProcAddress;
|
|
F_LoadLibrary16 HookWMLoadWinnet16;
|
|
F_FreeLibrary16 HookWMFreeWinnet16;
|
|
F_GetProcAddressByName16 HookWMGetProcAddressByName;
|
|
F_GetProcAddressByOrdinal16 HookWMGetProcAddressByOrdinal;
|
|
|
|
MPRCALLS MPRCalls = { HookHookMPR,
|
|
HookUnHookMPR,
|
|
HookLoadLibrary,
|
|
HookFreeLibrary,
|
|
HookGetProcAddress,
|
|
HookWMLoadWinnet16,
|
|
HookWMFreeWinnet16,
|
|
HookWMGetProcAddressByName,
|
|
HookWMGetProcAddressByOrdinal };
|
|
|
|
|
|
DWORD NPSERVICE HookHookMPR ( PMPRCALLS pMPRCalls )
|
|
{
|
|
return ((PF_NPSHookMPR)(MPRCalls.pfNPSHookMPR))(pMPRCalls);
|
|
}
|
|
|
|
DWORD NPSERVICE HookUnHookMPR ( PF_NPSHookMPR pfReqNPSHookMPR,
|
|
PMPRCALLS pReqMPRCalls )
|
|
{
|
|
if (pfReqNPSHookMPR == HookHookMPR) {
|
|
|
|
// The unhook request has reached the hooker that issued
|
|
// the NPSUnHookMe call (us).
|
|
// In other words we are now sucessfully unhooked
|
|
// and may do our unhooking cleanup.
|
|
// In particular, we can release our tables that
|
|
// manage LoadLibrary/GetProcAddress.
|
|
// Note that this code may be executing on a different
|
|
// thread to the NPSUnHookMe call which may have returned
|
|
// a while ago.
|
|
|
|
return WN_SUCCESS;
|
|
}
|
|
else {
|
|
|
|
// Another hooker has requested to unhook by calling
|
|
// NPSUnHookMe which causes us to be called here.
|
|
// Pass the request on to the MPR service NPSUnHookMPR to
|
|
// process the request, giving it our MPRCALLS
|
|
// data structure so that it can figure out if
|
|
// we are the right hooker to update and otherwise
|
|
// MPR will pass the request on to the next hooker.
|
|
|
|
return NPSUnHookMPR ( pfReqNPSHookMPR,
|
|
pReqMPRCalls,
|
|
(PMPRCALLS)&MPRCalls );
|
|
}
|
|
}
|
|
|
|
HINSTANCE HookLoadLibrary(
|
|
LPCTSTR lpszLibFile
|
|
)
|
|
{
|
|
return MPRCalls.pfLoadLibrary(lpszLibFile);
|
|
}
|
|
|
|
BOOL HookFreeLibrary(
|
|
HMODULE hLibModule
|
|
)
|
|
{
|
|
return MPRCalls.pfFreeLibrary(hLibModule);
|
|
}
|
|
|
|
FARPROC HookGetProcAddress(
|
|
HMODULE hModule,
|
|
LPCSTR lpszProc
|
|
)
|
|
{
|
|
return MPRCalls.pfGetProcAddress(hModule, lpszProc);
|
|
}
|
|
|
|
HANDLE16 HookWMLoadWinnet16(
|
|
LPCTSTR lpszLibFile
|
|
)
|
|
{
|
|
return MPRCalls.pfLoadLibrary16(lpszLibFile);
|
|
}
|
|
|
|
VOID HookWMFreeWinnet16(
|
|
HANDLE16 hLibModule
|
|
)
|
|
{
|
|
MPRCalls.pfFreeLibrary16(hLibModule);
|
|
}
|
|
|
|
DWORD WINAPI HookWMGetProcAddressByName(
|
|
LPCSTR lpszProc,
|
|
HANDLE16 hModule
|
|
)
|
|
{
|
|
return MPRCalls.pfGetProcAddressByName16(lpszProc, hModule);
|
|
}
|
|
|
|
DWORD WINAPI HookWMGetProcAddressByOrdinal(
|
|
WORD wOrdinal,
|
|
HANDLE16 hModule
|
|
)
|
|
{
|
|
return MPRCalls.pfGetProcAddressByOrdinal16(wOrdinal, hModule);
|
|
}
|
|
|
|
void KickMPR(void)
|
|
{
|
|
if (NPSHookMPR((PMPRCALLS)&MPRCalls) == WN_SUCCESS) {
|
|
NPSUnHookMe(HookHookMPR, (PMPRCALLS)&MPRCalls);
|
|
}
|
|
}
|
|
/***** End MPR hooking code *****/
|
|
|
|
|
|
/***** Begin code to delay-load the real net provider DLL *****/
|
|
HMODULE hmodRealNP = NULL;
|
|
|
|
PF_NPGetCaps pfnNPGetCaps = NULL;
|
|
PF_NPGetUniversalName pfnNPGetUniversalName = NULL;
|
|
PF_NPGetUser pfnNPGetUser = NULL;
|
|
PF_NPValidLocalDevice pfnNPValidLocalDevice = NULL;
|
|
PF_NPAddConnection pfnNPAddConnection = NULL;
|
|
PF_NPCancelConnection pfnNPCancelConnection = NULL;
|
|
PF_NPGetConnection pfnNPGetConnection = NULL;
|
|
PF_NPGetConnectionPerformance pfnNPGetConnectionPerformance = NULL;
|
|
PF_NPFormatNetworkName pfnNPFormatNetworkName = NULL;
|
|
PF_NPOpenEnum pfnNPOpenEnum = NULL;
|
|
PF_NPEnumResource pfnNPEnumResource = NULL;
|
|
PF_NPCloseEnum pfnNPCloseEnum = NULL;
|
|
PF_NPGetResourceParent pfnNPGetResourceParent = NULL;
|
|
PF_NPGetResourceInformation pfnNPGetResourceInformation = NULL;
|
|
PF_NPLogon pfnNPLogon = NULL;
|
|
PF_NPLogoff pfnNPLogoff = NULL;
|
|
PF_NPGetHomeDirectory pfnNPGetHomeDirectory = NULL;
|
|
PF_NPGetPolicyPath pfnNPGetPolicyPath = NULL;
|
|
|
|
|
|
struct {
|
|
UINT nOrd;
|
|
FARPROC *ppfn;
|
|
} aProcs[] = {
|
|
{ ORD_GETCAPS, (FARPROC *)&pfnNPGetCaps },
|
|
{ ORD_GETUNIVERSALNAME, (FARPROC *)&pfnNPGetUniversalName },
|
|
{ ORD_GETUSER, (FARPROC *)&pfnNPGetUser },
|
|
{ ORD_VALIDDEVICE, (FARPROC *)&pfnNPValidLocalDevice },
|
|
{ ORD_ADDCONNECTION, (FARPROC *)&pfnNPAddConnection },
|
|
{ ORD_CANCELCONNECTION, (FARPROC *)&pfnNPCancelConnection },
|
|
{ ORD_GETCONNECTIONS, (FARPROC *)&pfnNPGetConnection },
|
|
{ ORD_GETCONNPERFORMANCE, (FARPROC *)&pfnNPGetConnectionPerformance },
|
|
{ ORD_FORMATNETWORKNAME, (FARPROC *)&pfnNPFormatNetworkName },
|
|
{ ORD_OPENENUM, (FARPROC *)&pfnNPOpenEnum },
|
|
{ ORD_ENUMRESOURCE, (FARPROC *)&pfnNPEnumResource },
|
|
{ ORD_CLOSEENUM, (FARPROC *)&pfnNPCloseEnum },
|
|
{ ORD_GETRESOURCEPARENT, (FARPROC *)&pfnNPGetResourceParent },
|
|
{ ORD_GETRESOURCEINFORMATION, (FARPROC *)&pfnNPGetResourceInformation },
|
|
{ ORD_LOGON, (FARPROC *)&pfnNPLogon },
|
|
{ ORD_LOGOFF, (FARPROC *)&pfnNPLogoff },
|
|
{ ORD_GETHOMEDIRECTORY, (FARPROC *)&pfnNPGetHomeDirectory },
|
|
{ ORD_GETPOLICYPATH, (FARPROC *)&pfnNPGetPolicyPath },
|
|
};
|
|
|
|
|
|
void LoadRealNP(void)
|
|
{
|
|
ENTERCRITICAL
|
|
|
|
if (::hmodRealNP == NULL) {
|
|
char szDLLName[MAX_PATH];
|
|
|
|
szDLLName[0] = '\0';
|
|
HKEY hkeySection;
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\NPSTUB\\NetworkProvider",
|
|
0, KEY_QUERY_VALUE, &hkeySection) == ERROR_SUCCESS) {
|
|
DWORD dwType;
|
|
DWORD cbData = sizeof(szDLLName);
|
|
RegQueryValueEx(hkeySection, "RealDLL", NULL, &dwType, (LPBYTE)szDLLName, &cbData);
|
|
RegCloseKey(hkeySection);
|
|
}
|
|
|
|
if (szDLLName[0] == '\0')
|
|
lstrcpy(szDLLName, "mslocusr.dll");
|
|
|
|
::hmodRealNP = LoadLibrary(szDLLName);
|
|
|
|
if (::hmodRealNP != NULL) {
|
|
for (UINT i=0; i<ARRAYSIZE(::aProcs); i++) {
|
|
*(aProcs[i].ppfn) = GetProcAddress(::hmodRealNP, (LPCSTR)aProcs[i].nOrd);
|
|
}
|
|
}
|
|
}
|
|
LEAVECRITICAL
|
|
}
|
|
|
|
|
|
void UnloadRealNP(void)
|
|
{
|
|
ENTERCRITICAL
|
|
{
|
|
if (cCallsInProgress > 0) {
|
|
fShouldUnload = TRUE;
|
|
}
|
|
else {
|
|
for (UINT i=0; i<ARRAYSIZE(::aProcs); i++) {
|
|
*(aProcs[i].ppfn) = NULL;
|
|
}
|
|
|
|
FreeLibrary(hmodRealNP);
|
|
hmodRealNP = NULL;
|
|
fShouldUnload = FALSE;
|
|
KickMPR();
|
|
}
|
|
}
|
|
LEAVECRITICAL
|
|
}
|
|
|
|
|
|
LRESULT MonitorWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (msg) {
|
|
case WM_NPSTUB_LOADDLL:
|
|
LoadRealNP();
|
|
KickMPR();
|
|
break;
|
|
|
|
case WM_NPSTUB_UNLOADDLL:
|
|
UnloadRealNP();
|
|
break;
|
|
}
|
|
|
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
|
|
void _ProcessAttach()
|
|
{
|
|
//
|
|
// All the per-instance initialization code should come here.
|
|
//
|
|
::DisableThreadLibraryCalls(::hInstance);
|
|
|
|
InitializeCriticalSection(&::critsec);
|
|
|
|
WNDCLASS wc;
|
|
|
|
wc.style = 0;
|
|
wc.lpfnWndProc = MonitorWndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = ::hInstance;
|
|
wc.hIcon = NULL;
|
|
wc.hCursor = NULL;
|
|
wc.hbrBackground = NULL;
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = szNPSTUBClassName;
|
|
|
|
::aClass = RegisterClass(&wc);
|
|
|
|
if (::aClass != NULL) {
|
|
::hwndMonitor = CreateWindow(szNPSTUBClassName, "",
|
|
WS_POPUP | WS_DISABLED,
|
|
0, 0, 0, 0,
|
|
NULL, NULL,
|
|
::hInstance, NULL);
|
|
}
|
|
|
|
LoadRealNP();
|
|
}
|
|
|
|
|
|
void _ProcessDetach()
|
|
{
|
|
if (::hwndMonitor != NULL)
|
|
DestroyWindow(::hwndMonitor);
|
|
if (::aClass != NULL)
|
|
UnregisterClass((LPSTR)(WORD)::aClass, ::hInstance);
|
|
|
|
DeleteCriticalSection(&::critsec);
|
|
}
|
|
|
|
|
|
extern "C" STDAPI_(BOOL) DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID reserved)
|
|
{
|
|
if (fdwReason == DLL_PROCESS_ATTACH)
|
|
{
|
|
::hInstance = hInstDll;
|
|
_ProcessAttach();
|
|
}
|
|
else if (fdwReason == DLL_PROCESS_DETACH)
|
|
{
|
|
_ProcessDetach();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void EnterSPI(void)
|
|
{
|
|
ENTERCRITICAL
|
|
{
|
|
::cCallsInProgress++;
|
|
}
|
|
LEAVECRITICAL
|
|
}
|
|
|
|
|
|
void LeaveSPI(void)
|
|
{
|
|
ENTERCRITICAL
|
|
{
|
|
::cCallsInProgress--;
|
|
|
|
if (::fShouldUnload && !::cCallsInProgress)
|
|
PostMessage(::hwndMonitor, WM_NPSTUB_UNLOADDLL, 0, 0);
|
|
}
|
|
LEAVECRITICAL
|
|
}
|
|
|
|
|
|
#define CALLNP(name,err,params) \
|
|
{ \
|
|
if (pfn##name == NULL) \
|
|
return err; \
|
|
DWORD dwRet = err; \
|
|
EnterSPI(); \
|
|
if (pfn##name != NULL) \
|
|
dwRet = (*pfn##name)params; \
|
|
LeaveSPI(); \
|
|
return dwRet; \
|
|
} //last line doesn't need a backslash
|
|
|
|
|
|
SPIENTRY NPGetCaps(
|
|
DWORD nIndex
|
|
)
|
|
{
|
|
CALLNP(NPGetCaps,0,(nIndex));
|
|
}
|
|
|
|
|
|
SPIENTRY NPGetUniversalName(
|
|
LPTSTR lpLocalPath,
|
|
DWORD dwInfoLevel,
|
|
LPVOID lpBuffer,
|
|
LPDWORD lpBufferSize
|
|
)
|
|
{
|
|
CALLNP(NPGetUniversalName,WN_NOT_SUPPORTED,
|
|
(lpLocalPath,dwInfoLevel,lpBuffer,lpBufferSize));
|
|
}
|
|
|
|
|
|
SPIENTRY NPGetUser(
|
|
LPTSTR lpName,
|
|
LPTSTR lpAuthenticationID,
|
|
LPDWORD lpBufferSize
|
|
)
|
|
{
|
|
CALLNP(NPGetUser,WN_NOT_SUPPORTED,
|
|
(lpName,lpAuthenticationID,lpBufferSize));
|
|
}
|
|
|
|
|
|
SPIENTRY NPValidLocalDevice(
|
|
DWORD dwType,
|
|
DWORD dwNumber
|
|
)
|
|
{
|
|
CALLNP(NPValidLocalDevice,WN_NOT_SUPPORTED,(dwType,dwNumber));
|
|
}
|
|
|
|
|
|
SPIENTRY NPAddConnection(
|
|
HWND hwndOwner,
|
|
LPNETRESOURCE lpNetResource,
|
|
LPTSTR lpPassword,
|
|
LPTSTR lpUserID,
|
|
DWORD dwFlags,
|
|
LPTSTR lpAccessName,
|
|
LPDWORD lpBufferSize,
|
|
LPDWORD lpResult
|
|
)
|
|
{
|
|
CALLNP(NPAddConnection,WN_NOT_SUPPORTED,
|
|
(hwndOwner,lpNetResource,lpPassword,lpUserID,dwFlags,lpAccessName,lpBufferSize,lpResult));
|
|
}
|
|
|
|
|
|
SPIENTRY NPCancelConnection(
|
|
LPTSTR lpName,
|
|
BOOL fForce,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
CALLNP(NPCancelConnection,WN_NOT_SUPPORTED,
|
|
(lpName,fForce,dwFlags));
|
|
}
|
|
|
|
|
|
SPIENTRY NPGetConnection(
|
|
LPTSTR lpLocalName,
|
|
LPTSTR lpRemoteName,
|
|
LPDWORD lpBufferSize
|
|
)
|
|
{
|
|
CALLNP(NPGetConnection,WN_NOT_SUPPORTED,
|
|
(lpLocalName,lpRemoteName,lpBufferSize));
|
|
}
|
|
|
|
|
|
SPIENTRY NPGetConnectionPerformance(
|
|
LPTSTR lpRemoteName,
|
|
LPNETCONNECTINFOSTRUCT lpNetConnectInfoStruct
|
|
)
|
|
{
|
|
CALLNP(NPGetConnectionPerformance,WN_NOT_SUPPORTED,
|
|
(lpRemoteName,lpNetConnectInfoStruct));
|
|
}
|
|
|
|
|
|
SPIENTRY NPFormatNetworkName(
|
|
LPTSTR lpRemoteName,
|
|
LPTSTR lpFormattedName,
|
|
LPDWORD lpnLength,
|
|
DWORD dwFlags,
|
|
DWORD dwAveCharPerLine
|
|
)
|
|
{
|
|
CALLNP(NPFormatNetworkName,WN_NOT_SUPPORTED,
|
|
(lpRemoteName,lpFormattedName,lpnLength,dwFlags,dwAveCharPerLine));
|
|
}
|
|
|
|
|
|
SPIENTRY NPOpenEnum(
|
|
DWORD dwScope,
|
|
DWORD dwType,
|
|
DWORD dwUsage,
|
|
LPNETRESOURCE lpNetResource,
|
|
LPHANDLE lphEnum
|
|
)
|
|
{
|
|
CALLNP(NPOpenEnum,WN_NOT_SUPPORTED,
|
|
(dwScope,dwType,dwUsage,lpNetResource,lphEnum));
|
|
}
|
|
|
|
|
|
SPIENTRY NPEnumResource(
|
|
HANDLE hEnum,
|
|
LPDWORD lpcCount,
|
|
LPVOID lpBuffer,
|
|
DWORD cbBuffer,
|
|
LPDWORD lpcbFree
|
|
)
|
|
{
|
|
CALLNP(NPEnumResource,WN_NOT_SUPPORTED,
|
|
(hEnum,lpcCount,lpBuffer,cbBuffer,lpcbFree));
|
|
}
|
|
|
|
|
|
SPIENTRY NPCloseEnum(
|
|
HANDLE hEnum
|
|
)
|
|
{
|
|
CALLNP(NPCloseEnum,WN_NOT_SUPPORTED,
|
|
(hEnum));
|
|
}
|
|
|
|
|
|
SPIENTRY NPGetResourceParent(
|
|
LPNETRESOURCE lpNetResource,
|
|
LPVOID lpBuffer,
|
|
LPDWORD cbBuffer
|
|
)
|
|
{
|
|
CALLNP(NPGetResourceParent,WN_NOT_SUPPORTED,
|
|
(lpNetResource,lpBuffer,cbBuffer));
|
|
}
|
|
|
|
|
|
SPIENTRY NPGetResourceInformation(
|
|
LPNETRESOURCE lpNetResource,
|
|
LPVOID lpBuffer,
|
|
LPDWORD cbBuffer,
|
|
LPSTR *lplpSystem
|
|
)
|
|
{
|
|
CALLNP(NPGetResourceInformation,WN_NOT_SUPPORTED,
|
|
(lpNetResource,lpBuffer,cbBuffer,lplpSystem));
|
|
}
|
|
|
|
|
|
SPIENTRY NPLogon(
|
|
HWND hwndOwner,
|
|
LPLOGONINFO lpAuthentInfo,
|
|
LPLOGONINFO lpPreviousAuthentInfo,
|
|
LPTSTR lpLogonScript,
|
|
DWORD dwBufferSize,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
CALLNP(NPLogon,WN_NOT_SUPPORTED,
|
|
(hwndOwner,lpAuthentInfo,lpPreviousAuthentInfo,lpLogonScript,dwBufferSize,dwFlags));
|
|
}
|
|
|
|
|
|
SPIENTRY NPLogoff(
|
|
HWND hwndOwner,
|
|
LPLOGONINFO lpAuthentInfo,
|
|
DWORD dwReason
|
|
)
|
|
{
|
|
CALLNP(NPLogoff,WN_NOT_SUPPORTED,
|
|
(hwndOwner,lpAuthentInfo,dwReason));
|
|
}
|
|
|
|
|
|
SPIENTRY NPGetHomeDirectory(
|
|
LPTSTR lpDirectory,
|
|
LPDWORD lpBufferSize
|
|
)
|
|
{
|
|
CALLNP(NPGetHomeDirectory,WN_NOT_SUPPORTED,
|
|
(lpDirectory,lpBufferSize));
|
|
}
|
|
|
|
|
|
SPIENTRY NPGetPolicyPath(
|
|
LPTSTR lpPath,
|
|
LPDWORD lpBufferSize,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
CALLNP(NPGetPolicyPath,WN_NOT_SUPPORTED,
|
|
(lpPath,lpBufferSize,dwFlags));
|
|
}
|
|
|