svchost: Implementation of svchost (resend)

Roy Shea roy at cs.hmc.edu
Thu Nov 8 15:33:39 CST 2007


This updated version declares most functions as static.

Changelog:
* Implementation of svchost

-------------- next part --------------
 programs/svchost/Makefile.in |   15 ++
 programs/svchost/svchost.c   |  384 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 399 insertions(+), 0 deletions(-)
 create mode 100644 programs/svchost/Makefile.in
 create mode 100644 programs/svchost/svchost.c

diff --git a/programs/svchost/Makefile.in b/programs/svchost/Makefile.in
new file mode 100644
index 0000000..f9b7fdb
--- /dev/null
+++ b/programs/svchost/Makefile.in
@@ -0,0 +1,15 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = svchost.exe
+APPMODE   = -municode
+IMPORTS   = advapi32 kernel32 ole32
+
+C_SRCS = \
+	svchost.c
+
+ at MAKE_PROG_RULES@
+
+ at DEPENDENCIES@  # everything below this line is overwritten by make depend
+
diff --git a/programs/svchost/svchost.c b/programs/svchost/svchost.c
new file mode 100644
index 0000000..a0e5fc3
--- /dev/null
+++ b/programs/svchost/svchost.c
@@ -0,0 +1,384 @@
+/*
+ * Implementation of svchost.exe
+ *
+ * Copyright 2007 Google (Roy Shea)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/* Usage:
+ * Starting a service:
+ *
+ *      svchost /k service_name
+ *
+ *
+ * Building (to test) in Windows:
+ *
+ * (1) Remove WINE_TRACE and WINE_ERR
+ *
+ * (2) Build from command line:
+ *       cl -DSTANDALONE -D_X86_ -D_CONSOLE -D__WINESRC__
+ *              -DREGISTER_PROXY_DLL -D_REENTRANT -D_WIN32_WINNT=0x500
+ *              -DWIN32 -D_REENTRANT -wd4996 -c -I. -nologo svchost.c
+ *       cl -Fsvchost.exe svchost.obj advapi32.lib kernel32.lib ole32.lib
+ *
+ *
+ * Testing in Windows:
+ *
+ * (0) Beware.  While I don't know of any, finding a bug could leave your
+ * system in a non-bootable state.  Test in a VM image that you are okay
+ * corrupting.
+ *
+ * (1) Update the ImagePath value of the service you will use when testing svchost
+ * from the current value of:
+ *      %SystemRoot%\system32\svchost.exe -k <service_name>
+ * to the new value:
+ *      <path_to_this_svchost>\svchost.exe -k <service_name>
+ * A good (noncritical) service to test in WinXP is HTTPFilter.
+ *
+ * (2) Restart the service from the SCM or command line.
+ *
+ * (3) Restore your registry when done testing.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winsvc.h"
+#include "winnls.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(svchost);
+
+/* Static strings used throughout svchost */
+static const WCHAR kd[] = {'-','k',0};
+static const WCHAR ks[] = {'/','k',0};
+
+static const WCHAR reg_seperator[] = {'\\',0};
+
+static const WCHAR service_reg_path[] = {
+    'S','y','s','t','e','m',
+    '\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t',
+    '\\','S','e','r','v','i','c','e','s',0};
+
+static const WCHAR parameters[] = {
+    'P','a','r','a','m','e','t','e','r','s',0};
+
+static const WCHAR service_dll[] = {
+    'S','e','r','v','i','c','e','D','l','l',0};
+
+static const WCHAR svchost_path[] = {
+    'S','o','f','t','w','a','r','e',
+    '\\','M','i','c','r','o','s','o','f','t',
+    '\\','W','i','n','d','o','w','s',' ','N','T',
+    '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
+    '\\','S','v','c','h','o','s','t',0};
+
+static const WCHAR service_main[] = {
+    'S','e','r','v','i','c','e','M','a','i','n',0};
+
+/* Allocate and initializes an ANSI version of the Unicode string */
+static LPSTR UnicodeToAnsi(const WCHAR *unicode)
+{
+    int size;
+    LPSTR ansi;
+
+    size = WideCharToMultiByte(CP_UTF8, 0, unicode, -1, NULL, 0, NULL, NULL);
+    ansi = HeapAlloc(GetProcessHeap(), 0, size);
+    WideCharToMultiByte(CP_UTF8, 0, unicode, -1, ansi, size, NULL, NULL);
+    return ansi;
+}
+
+/* Allocate and initialize a WSTR containing the queried value */
+static LPWSTR GetRegValue(HKEY service_key, const WCHAR *value_name)
+{
+    DWORD type;
+    DWORD size;
+    LONG ret;
+    LPWSTR value;
+
+    WINE_TRACE("\n");
+
+    ret = RegQueryValueExW(service_key, value_name, NULL, &type,
+            NULL, &size);
+    if (ret != ERROR_SUCCESS)
+    {
+        return NULL;
+    }
+    value = HeapAlloc(GetProcessHeap(), 0, size);
+    ret = RegQueryValueExW(service_key, value_name, NULL, &type,
+            (LPBYTE)value, &size);
+    if (ret != ERROR_SUCCESS)
+    {
+        HeapFree(GetProcessHeap(), 0, value);
+        return NULL;
+    }
+    return value;
+}
+
+/* Allocate and initialize a WSTR containing the expanded string */
+static LPWSTR ExpandEnv(LPWSTR string)
+{
+    DWORD size;
+    LPWSTR expanded_string;
+
+    WINE_TRACE("\n");
+
+    size = 0;
+    size = ExpandEnvironmentStringsW(string, NULL, size);
+    if (size == 0)
+    {
+        WINE_ERR("cannot expand env vars in %s: %u\n",
+                wine_dbgstr_w(string), GetLastError());
+        return NULL;
+    }
+    expanded_string = HeapAlloc(GetProcessHeap(),
+            0, (size + 1) * sizeof(WCHAR));
+    if (ExpandEnvironmentStringsW(string, expanded_string, size) == 0)
+    {
+        WINE_ERR("cannot expand env vars in %s: %u\n",
+                wine_dbgstr_w(string), GetLastError());
+        HeapFree(GetProcessHeap(), 0, expanded_string);
+        return NULL;
+    }
+    return expanded_string;
+}
+
+/* Fill in service table entry for a specified service */
+static BOOL AddServiceElem(LPWSTR service_name,
+        SERVICE_TABLE_ENTRYW *service_table_entry)
+{
+    HKEY service_hkey = NULL;
+    LONG ret;
+    LPWSTR service_param_key = NULL;
+    LPWSTR dll_name_short = NULL;
+    LPWSTR dll_name_long = NULL;
+    LPWSTR dll_service_main = NULL;
+    LPSTR service_main_a = NULL;
+    HMODULE library = NULL;
+    LPSERVICE_MAIN_FUNCTIONW service_main_func = NULL;
+
+    WINE_TRACE("Adding element for %s\n", wine_dbgstr_w(service_name));
+
+    /* Construct registry path to the service's parameters key */
+    service_param_key = HeapAlloc( GetProcessHeap(), 0,
+            (lstrlenW(service_reg_path) +
+             lstrlenW(reg_seperator) +
+             lstrlenW(service_name) +
+             lstrlenW(reg_seperator) +
+             lstrlenW(parameters) +
+             1) * sizeof(WCHAR));
+    lstrcpyW(service_param_key, service_reg_path);
+    lstrcatW(service_param_key, reg_seperator);
+    lstrcatW(service_param_key, service_name);
+    lstrcatW(service_param_key, reg_seperator);
+    lstrcatW(service_param_key, parameters);
+    ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, service_param_key, 0,
+            KEY_READ, &service_hkey);
+    if (ret != ERROR_SUCCESS)
+    {
+        WINE_ERR("cannot open key %s, err=%d\n",
+                wine_dbgstr_w(service_param_key), ret);
+        HeapFree(GetProcessHeap(), 0, service_param_key);
+        return FALSE;
+    }
+
+    /* Find DLL associate with service from key */
+    dll_name_short = GetRegValue(service_hkey, service_dll);
+    if (!dll_name_short)
+    {
+        WINE_ERR("cannot find registry value %s for service %s\n",
+                wine_dbgstr_w(service_dll), wine_dbgstr_w(service_name));
+        HeapFree(GetProcessHeap(), 0, service_param_key);
+        return FALSE;
+    }
+
+    /* Expand environment variables in ServiceDll name*/
+    dll_name_long = ExpandEnv(dll_name_short);
+    if (!dll_name_long)
+    {
+        WINE_ERR("failed to expand string %s\n",
+                wine_dbgstr_w(dll_name_short));
+        HeapFree(GetProcessHeap(), 0, service_param_key);
+        HeapFree(GetProcessHeap(), 0, dll_name_short);
+        return FALSE;
+    }
+    HeapFree(GetProcessHeap(), 0, dll_name_short);
+    HeapFree(GetProcessHeap(), 0, service_param_key);
+
+    /* Look for alternate to default ServiceMain entry point */
+    dll_service_main = GetRegValue(service_hkey, service_main);
+    if (!dll_service_main)
+    {
+        service_main_a = UnicodeToAnsi(service_main);
+    }
+    else
+    {
+        service_main_a = UnicodeToAnsi(dll_service_main);
+        HeapFree(GetProcessHeap(), 0, dll_service_main);
+    }
+
+    /* Load the DLL and obtain a pointer to ServiceMain entry point */
+    library = LoadLibraryW(dll_name_long);
+    if (!library)
+    {
+        WINE_ERR("failed to load library %s, err=%u\n",
+                wine_dbgstr_w(dll_name_long), GetLastError());
+        HeapFree(GetProcessHeap(), 0, dll_name_long);
+        return FALSE;
+    }
+    HeapFree(GetProcessHeap(), 0, dll_name_long);
+    service_main_func =
+        (LPSERVICE_MAIN_FUNCTIONW) GetProcAddress(library, service_main_a);
+    if (!service_main_func)
+    {
+        WINE_ERR("cannot locate ServiceMain procedure in DLL for %s\n",
+                wine_dbgstr_w(service_name));
+        FreeLibrary(library);
+        return FALSE;
+    }
+
+    /* Fill in the service table entry */
+    service_table_entry->lpServiceName = service_name;
+    service_table_entry->lpServiceProc = service_main_func;
+    return TRUE;
+}
+
+/* Initialize the service table for a list (REG_MULTI_SZ) of services */
+static BOOL StartGroupServices(LPWSTR services)
+{
+    LPWSTR service_name = NULL;
+    SERVICE_TABLE_ENTRYW *service_table = NULL;
+    DWORD service_count;
+
+    /* Count the services to load */
+    service_count = 0;
+    service_name = services;
+    while (*service_name != '\0')
+    {
+        ++service_count;
+        service_name = service_name + lstrlenW(service_name);
+        ++service_name;
+    }
+    WINE_TRACE("Service group %s contains %d services\n",
+            wine_dbgstr_w(services), service_count);
+
+    /* Populate the service table */
+    service_table = HeapAlloc(GetProcessHeap(), 0,
+            (service_count + 1) * sizeof(SERVICE_TABLE_ENTRYW));
+    service_name = services;
+    service_count = 0;
+    while (*service_name != '\0')
+    {
+        if (!AddServiceElem(service_name, &service_table[service_count]))
+        {
+            HeapFree(GetProcessHeap(), 0, service_table);
+            return FALSE;
+        }
+        ++service_count;
+        service_name = service_name + lstrlenW(service_name);
+        ++service_name;
+    }
+    service_table[service_count].lpServiceName = NULL;
+    service_table[service_count].lpServiceProc = NULL;
+
+    /* Start the services */
+    if (!StartServiceCtrlDispatcherW(service_table))
+    {
+        WINE_ERR("StartServiceCtrlDispatcherW failed to start %s: %u\n",
+                wine_dbgstr_w(services), GetLastError());
+        HeapFree(GetProcessHeap(), 0, service_table);
+        return FALSE;
+    }
+    HeapFree(GetProcessHeap(), 0, service_table);
+    return TRUE;
+}
+
+/* Find the list of services associated with a group name and start those
+ * services */
+static BOOL LoadGroup(PWCHAR group_name)
+{
+    HKEY group_hkey = NULL;
+    LPWSTR services = NULL;
+    LONG ret;
+
+    WINE_TRACE("Loading service group for %s\n", wine_dbgstr_w(group_name));
+
+    /* Lookup group_name value of svchost registry entry */
+    ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, svchost_path, 0,
+            KEY_READ, &group_hkey);
+    if (ret != ERROR_SUCCESS)
+    {
+        WINE_ERR("cannot open key %s, err=%d\n",
+                wine_dbgstr_w(svchost_path), ret);
+        return FALSE;
+    }
+    services = GetRegValue(group_hkey, group_name);
+    if (!services)
+    {
+        WINE_ERR("cannot find registry value %s in %s\n",
+                wine_dbgstr_w(group_name), wine_dbgstr_w(svchost_path));
+        return FALSE;
+    }
+
+    /* Start services */
+    if (StartGroupServices(services) == FALSE)
+    {
+        WINE_TRACE("Failed to start service group\n");
+        HeapFree(GetProcessHeap(), 0, services);
+        return FALSE;
+    }
+    HeapFree(GetProcessHeap(), 0, services);
+    return TRUE;
+}
+
+
+int wmain(int argc, WCHAR *argv[])
+{
+    int option_index;
+
+    WINE_TRACE("\n");
+
+    for (option_index = 1; option_index < argc; option_index++)
+    {
+        if (lstrcmpiW(argv[option_index], ks) == 0 || lstrcmpiW(argv[option_index], kd) == 0)
+        {
+            ++option_index;
+            if (option_index >= argc)
+            {
+                WINE_ERR("Must specify group to initialize\n");
+                return 0;
+            }
+            if (!LoadGroup(argv[option_index]))
+            {
+                WINE_ERR("Failed to load requested group: %s\n",
+                        wine_dbgstr_w(argv[option_index]));
+                return 0;
+            }
+        }
+        else
+        {
+            WINE_FIXME("Unrecognized option: %s\n", wine_dbgstr_w(argv[option_index]));
+            return 0;
+        }
+    }
+
+    return 0;
+}
+


More information about the wine-patches mailing list