From c81d0995038b8b39ef5a8bf1e81a5d3245cb61f7 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 17 May 2007 16:51:44 +0200 Subject: [PATCH] winedevice: Add a service process that loads and runs a kernel driver. --- .gitignore | 1 + Makefile.in | 2 + configure | 3 + configure.ac | 1 + programs/Makefile.in | 2 + programs/winedevice/Makefile.in | 14 ++ programs/winedevice/device.c | 237 ++++++++++++++++++++++++++++++++ 7 files changed, 260 insertions(+) create mode 100644 programs/winedevice/Makefile.in create mode 100644 programs/winedevice/device.c diff --git a/.gitignore b/.gitignore index e657194b539..af69a26d2be 100644 --- a/.gitignore +++ b/.gitignore @@ -809,6 +809,7 @@ programs/winedbg/dbg.tab.h programs/winedbg/debug.yy.c programs/winedbg/winedbg programs/winedbg/winedbg.man +programs/winedevice/winedevice programs/winefile/drivebar.bmp programs/winefile/images.bmp programs/winefile/rsrc.res diff --git a/Makefile.in b/Makefile.in index e7942ec61d0..3b16428c2b5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -469,6 +469,7 @@ ALL_MAKEFILES = \ programs/winecfg/Makefile \ programs/wineconsole/Makefile \ programs/winedbg/Makefile \ + programs/winedevice/Makefile \ programs/winefile/Makefile \ programs/winemenubuilder/Makefile \ programs/winemine/Makefile \ @@ -817,6 +818,7 @@ programs/winebrowser/Makefile: programs/winebrowser/Makefile.in programs/Makepro programs/winecfg/Makefile: programs/winecfg/Makefile.in programs/Makeprog.rules programs/wineconsole/Makefile: programs/wineconsole/Makefile.in programs/Makeprog.rules programs/winedbg/Makefile: programs/winedbg/Makefile.in programs/Makeprog.rules +programs/winedevice/Makefile: programs/winedevice/Makefile.in programs/Makeprog.rules programs/winefile/Makefile: programs/winefile/Makefile.in programs/Makeprog.rules programs/winemenubuilder/Makefile: programs/winemenubuilder/Makefile.in programs/Makeprog.rules programs/winemine/Makefile: programs/winemine/Makefile.in programs/Makeprog.rules diff --git a/configure b/configure index 59885a11436..f6034fe9b6f 100755 --- a/configure +++ b/configure @@ -21007,6 +21007,8 @@ ac_config_files="$ac_config_files programs/wineconsole/Makefile" ac_config_files="$ac_config_files programs/winedbg/Makefile" +ac_config_files="$ac_config_files programs/winedevice/Makefile" + ac_config_files="$ac_config_files programs/winefile/Makefile" ac_config_files="$ac_config_files programs/winemenubuilder/Makefile" @@ -21928,6 +21930,7 @@ do "programs/winecfg/Makefile") CONFIG_FILES="$CONFIG_FILES programs/winecfg/Makefile" ;; "programs/wineconsole/Makefile") CONFIG_FILES="$CONFIG_FILES programs/wineconsole/Makefile" ;; "programs/winedbg/Makefile") CONFIG_FILES="$CONFIG_FILES programs/winedbg/Makefile" ;; + "programs/winedevice/Makefile") CONFIG_FILES="$CONFIG_FILES programs/winedevice/Makefile" ;; "programs/winefile/Makefile") CONFIG_FILES="$CONFIG_FILES programs/winefile/Makefile" ;; "programs/winemenubuilder/Makefile") CONFIG_FILES="$CONFIG_FILES programs/winemenubuilder/Makefile" ;; "programs/winemine/Makefile") CONFIG_FILES="$CONFIG_FILES programs/winemine/Makefile" ;; diff --git a/configure.ac b/configure.ac index 4edb7fb7ad8..22de21f1a77 100644 --- a/configure.ac +++ b/configure.ac @@ -1799,6 +1799,7 @@ AC_CONFIG_FILES([programs/winebrowser/Makefile]) AC_CONFIG_FILES([programs/winecfg/Makefile]) AC_CONFIG_FILES([programs/wineconsole/Makefile]) AC_CONFIG_FILES([programs/winedbg/Makefile]) +AC_CONFIG_FILES([programs/winedevice/Makefile]) AC_CONFIG_FILES([programs/winefile/Makefile]) AC_CONFIG_FILES([programs/winemenubuilder/Makefile]) AC_CONFIG_FILES([programs/winemine/Makefile]) diff --git a/programs/Makefile.in b/programs/Makefile.in index ee2d3c7c277..9ea131beec4 100644 --- a/programs/Makefile.in +++ b/programs/Makefile.in @@ -35,6 +35,7 @@ SUBDIRS = \ winecfg \ wineconsole \ winedbg \ + winedevice \ winefile \ winemenubuilder \ winemine \ @@ -75,6 +76,7 @@ INSTALLSUBDIRS = \ winecfg \ wineconsole \ winedbg \ + winedevice \ winefile \ winemenubuilder \ winemine \ diff --git a/programs/winedevice/Makefile.in b/programs/winedevice/Makefile.in new file mode 100644 index 00000000000..7effe56c639 --- /dev/null +++ b/programs/winedevice/Makefile.in @@ -0,0 +1,14 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = winedevice.exe +APPMODE = -mwindows -municode +IMPORTS = advapi32 ntoskrnl.exe kernel32 ntdll + +C_SRCS = \ + device.c + +@MAKE_PROG_RULES@ + +@DEPENDENCIES@ # everything below this line is overwritten by make depend diff --git a/programs/winedevice/device.c b/programs/winedevice/device.c new file mode 100644 index 00000000000..226ba6b791b --- /dev/null +++ b/programs/winedevice/device.c @@ -0,0 +1,237 @@ +/* + * Service process to load a kernel driver + * + * Copyright 2007 Alexandre Julliard + * + * 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 + */ + +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "winreg.h" +#include "winnls.h" +#include "winsvc.h" +#include "ddk/wdm.h" +#include "wine/unicode.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(winedevice); +WINE_DECLARE_DEBUG_CHANNEL(relay); + +extern NTSTATUS wine_ntoskrnl_main_loop( HANDLE stop_event ); + +static WCHAR *driver_name; +static SERVICE_STATUS_HANDLE service_handle; +static HKEY driver_hkey; +static HANDLE stop_event; +static DRIVER_OBJECT driver_obj; +static DRIVER_EXTENSION driver_extension; + +/* find the LDR_MODULE corresponding to the driver module */ +static LDR_MODULE *find_ldr_module( HMODULE module ) +{ + LIST_ENTRY *entry, *list = &NtCurrentTeb()->Peb->LdrData->InMemoryOrderModuleList; + + for (entry = list->Flink; entry != list; entry = entry->Flink) + { + LDR_MODULE *ldr = CONTAINING_RECORD(entry, LDR_MODULE, InMemoryOrderModuleList); + if (ldr->BaseAddress == module) return ldr; + if (ldr->BaseAddress > (void *)module) break; + } + return NULL; +} + +/* call the driver init entry point */ +static NTSTATUS init_driver( HMODULE module, UNICODE_STRING *keyname ) +{ + unsigned int i; + NTSTATUS status; + const IMAGE_NT_HEADERS *nt = RtlImageNtHeader( module ); + + if (!nt->OptionalHeader.AddressOfEntryPoint) return STATUS_SUCCESS; + + driver_obj.Size = sizeof(driver_obj); + driver_obj.DriverSection = find_ldr_module( module ); + driver_obj.DriverInit = (PDRIVER_INITIALIZE)((char *)module + nt->OptionalHeader.AddressOfEntryPoint); + driver_obj.DriverExtension = &driver_extension; + + driver_extension.DriverObject = &driver_obj; + driver_extension.ServiceKeyName = *keyname; + + if (WINE_TRACE_ON(relay)) + WINE_DPRINTF( "%04x:Call driver init %p (obj=%p,str=%s)\n", GetCurrentThreadId(), + driver_obj.DriverInit, &driver_obj, wine_dbgstr_w(keyname->Buffer) ); + + status = driver_obj.DriverInit( &driver_obj, keyname ); + + if (WINE_TRACE_ON(relay)) + WINE_DPRINTF( "%04x:Ret driver init %p (obj=%p,str=%s) retval=%08x\n", GetCurrentThreadId(), + driver_obj.DriverInit, &driver_obj, wine_dbgstr_w(keyname->Buffer), status ); + + WINE_TRACE( "init done for %s obj %p\n", wine_dbgstr_w(driver_name), &driver_obj ); + WINE_TRACE( "- DriverInit = %p\n", driver_obj.DriverInit ); + WINE_TRACE( "- DriverStartIo = %p\n", driver_obj.DriverStartIo ); + WINE_TRACE( "- DriverUnload = %p\n", driver_obj.DriverUnload ); + for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) + WINE_TRACE( "- MajorFunction[%d] = %p\n", i, driver_obj.MajorFunction[i] ); + + return status; +} + +/* load the .sys module for a device driver */ +static BOOL load_driver(void) +{ + static const WCHAR ntprefixW[] = {'\\','?','?','\\',0}; + static const WCHAR ImagePathW[] = {'I','m','a','g','e','P','a','t','h',0}; + static const WCHAR servicesW[] = {'\\','R','e','g','i','s','t','r','y', + '\\','M','a','c','h','i','n','e', + '\\','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}; + + UNICODE_STRING keypath; + HMODULE module; + LPWSTR path = NULL, str; + DWORD type, size; + + str = HeapAlloc( GetProcessHeap(), 0, sizeof(servicesW) + strlenW(driver_name)*sizeof(WCHAR) ); + lstrcpyW( str, servicesW ); + lstrcatW( str, driver_name ); + + if (RegOpenKeyW( HKEY_LOCAL_MACHINE, str + 18 /* skip \registry\machine */, &driver_hkey )) + { + WINE_ERR( "cannot open key %s, err=%u\n", wine_dbgstr_w(str), GetLastError() ); + return FALSE; + } + RtlInitUnicodeString( &keypath, str ); + + /* read the executable path from memory */ + size = 0; + if (RegQueryValueExW( driver_hkey, ImagePathW, NULL, &type, NULL, &size )) return FALSE; + + str = HeapAlloc( GetProcessHeap(), 0, size ); + if (!RegQueryValueExW( driver_hkey, ImagePathW, NULL, &type, (LPBYTE)str, &size )) + { + size = ExpandEnvironmentStringsW(str,NULL,0); + path = HeapAlloc(GetProcessHeap(),0,size*sizeof(WCHAR)); + ExpandEnvironmentStringsW(str,path,size); + } + HeapFree( GetProcessHeap(), 0, str ); + if (!path) return FALSE; + + /* make sure msvcrt is loaded to resolve the ntoskrnl.exe forwards */ + LoadLibraryA( "msvcrt.dll" ); + + /* GameGuard uses an NT-style path name */ + str = path; + if (!strncmpW( path, ntprefixW, 4 )) str += 4; + + WINE_TRACE( "loading driver %s\n", wine_dbgstr_w(str) ); + + module = LoadLibraryW( str ); + HeapFree( GetProcessHeap(), 0, path ); + if (!module) return FALSE; + + init_driver( module, &keypath ); + return TRUE; +} + +static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_data, LPVOID context ) +{ + SERVICE_STATUS status; + + status.dwServiceType = SERVICE_WIN32; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP; + status.dwWin32ExitCode = 0; + status.dwServiceSpecificExitCode = 0; + status.dwCheckPoint = 0; + status.dwWaitHint = 0; + + switch(ctrl) + { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + WINE_TRACE( "shutting down %s\n", wine_dbgstr_w(driver_name) ); + status.dwCurrentState = SERVICE_STOP_PENDING; + status.dwControlsAccepted = 0; + SetServiceStatus( service_handle, &status ); + SetEvent( stop_event ); + return NO_ERROR; + default: + WINE_FIXME( "got service ctrl %x for %s\n", ctrl, wine_dbgstr_w(driver_name) ); + status.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus( service_handle, &status ); + return NO_ERROR; + } +} + +static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv ) +{ + SERVICE_STATUS status; + + WINE_TRACE( "starting service %s\n", wine_dbgstr_w(driver_name) ); + + stop_event = CreateEventW( NULL, TRUE, FALSE, NULL ); + + service_handle = RegisterServiceCtrlHandlerExW( driver_name, service_handler, NULL ); + + status.dwServiceType = SERVICE_WIN32; + status.dwCurrentState = SERVICE_START_PENDING; + status.dwControlsAccepted = 0; + status.dwWin32ExitCode = 0; + status.dwServiceSpecificExitCode = 0; + status.dwCheckPoint = 0; + status.dwWaitHint = 10000; + SetServiceStatus( service_handle, &status ); + + if (load_driver()) + { + status.dwCurrentState = SERVICE_RUNNING; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP; + SetServiceStatus( service_handle, &status ); + + wine_ntoskrnl_main_loop( stop_event ); + } + else WINE_ERR( "driver %s failed to load\n", wine_dbgstr_w(driver_name) ); + + status.dwCurrentState = SERVICE_STOPPED; + status.dwControlsAccepted = 0; + SetServiceStatus( service_handle, &status ); + WINE_TRACE( "service %s stopped\n", wine_dbgstr_w(driver_name) ); +} + +int wmain( int argc, WCHAR *argv[] ) +{ + SERVICE_TABLE_ENTRYW service_table[2]; + + if (!(driver_name = argv[1])) + { + WINE_ERR( "missing device name, winedevice isn't supposed to be run manually\n" ); + return 1; + } + + service_table[0].lpServiceName = argv[1]; + service_table[0].lpServiceProc = ServiceMain; + service_table[1].lpServiceName = NULL; + service_table[1].lpServiceProc = NULL; + + StartServiceCtrlDispatcherW( service_table ); + return 0; +}