VOID WINAPI GetServiceMainFunctions ( _In_ PSVCHOST_SERVICE pService, _Out_ PVOID *pServiceMain, _Out_ PVOID *pPushServiceGlobals, _Out_ PDWORD lpdwError ) { DWORD dwError, cbDllLength, cbData, dwType; PSVCHOST_DLL pDll; ACTCTXW actCtx; LPCWSTR pszDllPath; HKEY hKey; HANDLE hActCtx; LPWSTR lpData; WCHAR szDllBuffer[MAX_PATH + 2], szManifestBuffer[MAX_PATH + 2]; /* Initialize the activation context we might need later */ RtlZeroMemory(&actCtx, sizeof(actCtx)); actCtx.cbSize = sizeof(actCtx); /* We clean these up in our failure path so initialize them to NULL here */ hActCtx = NULL; hKey = NULL; lpData = NULL; *lpdwError = ERROR_SUCCESS; /* Null terminate the string buffers */ szDllBuffer[0] = UNICODE_NULL; szManifestBuffer[0] = UNICODE_NULL; /* Do we already have a DLL ready to go for this service? */ pDll = pService->pDll; if (pDll != NULL) goto HaveDll; /* Nope, we're starting from scratch. Open a handle to parameters key */ dwError = OpenServiceParametersKey(pService->pszServiceName, &hKey); if (dwError) { *lpdwError = dwError; ASSERT(*lpdwError != NO_ERROR); goto Quickie; } /* Allocate enough space to hold a unicode path (NULL-terminated) */ cbData = MAX_PATH * sizeof(WCHAR) + sizeof(UNICODE_NULL); lpData = MemAlloc(0, cbData); if (lpData == NULL) { /* No memory, bail out */ *lpdwError = ERROR_NOT_ENOUGH_MEMORY; goto Quickie; } /* Query the DLL path */ lpData[0] = UNICODE_NULL; dwError = RegQueryValueExW(hKey, L"ServiceDll", 0, &dwType, (LPBYTE)lpData, &cbData); if (dwError != ERROR_SUCCESS) { *lpdwError = dwError; DBG_ERR("RegQueryValueEx for the ServiceDll parameter of the %ws " "service returned %u\n", pService->pszServiceName, dwError); goto Quickie; } /* Is the registry data valid and present? */ if ((dwType != REG_EXPAND_SZ) || (lpData[0] == UNICODE_NULL)) { /* Nope, bail out */ *lpdwError = ERROR_FILE_NOT_FOUND; DBG_ERR("The ServiceDll parameter for the %ws service is not of type " "REG_EXPAND_SZ\n", pService->pszServiceName); goto Quickie; } /* Convert the expandable path into an absolute path */ ExpandEnvironmentStringsW(lpData, szDllBuffer, MAX_PATH); SvchostCharLowerW(szDllBuffer); /* Check if the service has a manifest file associated with it */ cbData = MAX_PATH * sizeof(WCHAR) + sizeof(UNICODE_NULL); dwError = RegQueryValueExW(hKey, L"ServiceManifest", NULL, &dwType, (LPBYTE)lpData, &cbData); if (dwError != ERROR_SUCCESS) { /* Did we fail because one wasn't set? */ if ((dwError != ERROR_PATH_NOT_FOUND) && (dwError != ERROR_FILE_NOT_FOUND)) { /* We failed for some other reason, bail out */ *lpdwError = dwError; DBG_ERR("RegQueryValueEx for the ServiceManifest parameter of the " "%ws service returned %u\n", pService->pszServiceName, dwError); goto Quickie; } /* We have no manifest, make sure the buffer is empty */ szManifestBuffer[0] = UNICODE_NULL; /* We're done with this buffer */ MemFree(lpData); lpData = NULL; /* Use the whole DLL path, since we don't have a manifest */ pszDllPath = szDllBuffer; } else { /* Do we have invalid registry data? */ if ((dwType != REG_EXPAND_SZ) || (*lpData == UNICODE_NULL)) { /* Yes, pretend there's no manifest and bail out */ *lpdwError = ERROR_FILE_NOT_FOUND; DBG_ERR("The ServiceManifest parameter for the %ws service is not " "of type REG_EXPAND_SZ\n", pService->pszServiceName); goto Quickie; } /* Expand the string into our stack buffer */ ExpandEnvironmentStringsW(lpData, szManifestBuffer, MAX_PATH); /* We no longer need the heap buffer*/ MemFree(lpData); lpData = NULL; /* Lowercase the manifest path */ SvchostCharLowerW(szManifestBuffer); /* Now loop over the DLL path */ cbDllLength = lstrlenW(szDllBuffer); while (cbDllLength) { /* From the end, until we find the first path separator */ if (szDllBuffer[cbDllLength] == '\\' || szDllBuffer[cbDllLength] == '/') { /* Use just a short name (cut out the rest of the path) */ pszDllPath = &szDllBuffer[cbDllLength + 1]; break; } /* Try the next character */ cbDllLength--; } } /* See if we already have a matching DLL and manifest for this service */ pDll = FindDll(szManifestBuffer, pszDllPath, pService); if (pDll == NULL) { /* We don't have it yet -- does this DLL have a manifest? */ if (szManifestBuffer[0] != UNICODE_NULL) { /* Create an activation context for it */ actCtx.lpSource = szManifestBuffer; hActCtx = CreateActCtxW(&actCtx); if (hActCtx == INVALID_HANDLE_VALUE) { /* We've failed to create one, bail out */ *lpdwError = GetLastError(); DBG_ERR("CreateActCtxW(%ws) for the %ws service returned %u\n", szManifestBuffer, pService->pszServiceName, *lpdwError); goto Quickie; } } /* Add this new DLL into the database */ pDll = AddDll(szManifestBuffer, pszDllPath, pService, lpdwError); if (pDll) { /* Save the activation context and zero it so we don't release later */ pDll->hActCtx = hActCtx; hActCtx = NULL; } } /* We either found, added, or failed to add, the DLL for this service */ ASSERT(!pService->pDll); pService->pDll = pDll; /* In all cases, we will query the ServiceMain function, however*/ RegQueryStringA(hKey, L"ServiceMain", REG_SZ, &pService->pszServiceMain); /* And now we'll check if we were able to create it earlier (or find it) */ if (!pService->pDll) { /* We were not, so there's no point in going on */ ASSERT(*lpdwError != NO_ERROR); goto Quickie; } /* We do have a valid DLL, so now get the service main routine out of it */ HaveDll: *pServiceMain = GetServiceDllFunction(pDll, pService->pszServiceMain ? pService->pszServiceMain : "ServiceMain", lpdwError); /* And now get the globals routine out of it (this one is optional) */ *pPushServiceGlobals = GetServiceDllFunction(pDll, "SvchostPushServiceGlobals", NULL); Quickie: /* We're done, cleanup any resources we left behind */ if (hKey != NULL) RegCloseKey(hKey); if (lpData != NULL) MemFree(lpData); if ((hActCtx) && (hActCtx != INVALID_HANDLE_VALUE)) ReleaseActCtx(hActCtx); }
void Create() { if ( ActivationCtxHandle != INVALID_HANDLE_VALUE ) { return ; } HMODULE hKernel = GetModuleHandleW( L"KERNEL32" ); if ( GetProcAddress( hKernel, "CreateActCtxW" ) == NULL ) { // Pre XP OS return; } bool ManifestInFile = false; System::Reflection::Assembly^ CurrentAssembly = System::Reflection::Assembly::GetCallingAssembly(); String^ AssemblyPath = CurrentAssembly->Location; try { if ( AssemblyPath == "" ) { String^ TempPath = System::IO::Path::GetTempFileName(); System::IO::StreamWriter^ sw = System::IO::File::CreateText(TempPath); sw->Write ( "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n" " <dependency>\n" " <dependentAssembly>\n" " <assemblyIdentity\n" " type='win32'\n" #ifdef _DEBUG " name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX ".DebugCRT'\n" #else /* _DEBUG */ " name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX ".CRT'\n" #endif /* _DEBUG */ " version='" _CRT_ASSEMBLY_VERSION "'\n" #ifdef _M_IX86 " processorArchitecture='x86'\n" #endif /* _M_IX86 */ #ifdef _M_AMD64 " processorArchitecture='amd64'\n" #endif /* _M_AMD64 */ #ifdef _M_IA64 " processorArchitecture='ia64'\n" #endif /* _M_IA64 */ " publicKeyToken='" _VC_ASSEMBLY_PUBLICKEYTOKEN "'/>\n" " </dependentAssembly>\n" " </dependency>\n" "</assembly>\n" ); sw->Close(); ManifestInFile = true; AssemblyPath = TempPath; } cli::pin_ptr<const System::Char> pAssemblyPath = PtrToStringChars(AssemblyPath); ACTCTXW actctx; // Don't call memset. memset results in a call to msvcr*.dll which can be loaded from WinSXS // only after the activation context is activated. actctx.wProcessorArchitecture = 0; actctx.wLangId = 0; actctx.lpAssemblyDirectory = NULL; actctx.lpApplicationName = NULL; actctx.hModule = NULL; actctx.cbSize = sizeof( actctx ); actctx.lpSource = pAssemblyPath; if (ManifestInFile) { actctx.lpResourceName = 0; actctx.dwFlags = 0; } else { actctx.lpResourceName = MAKEINTRESOURCEW( 2 ); actctx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID; } ActivationCtxHandle = CreateActCtxW( &actctx ); if ( ActivationCtxHandle == INVALID_HANDLE_VALUE ) { if (!ManifestInFile) { actctx.lpResourceName = MAKEINTRESOURCEW( 1 ); ActivationCtxHandle = CreateActCtxW( &actctx ); } if ( ActivationCtxHandle == INVALID_HANDLE_VALUE ) { } } } finally { if (ManifestInFile) { System::IO::File::Delete(AssemblyPath); } } }