Service windows dans une dll lancé par svchost.exe

4/5 (8 avis)

Vue 7 849 fois - Téléchargée 873 fois

Description

Ce code source montre comment implémenter un service Windows dans une DLL afin qu'il soit exécuté par un processus hôte svchost.exe. Au démarrage de Windows, le système examine une clé de la base de registres pour grouper des services dans diférentes instances de svchost.exe. Notre service fera partie du groupe netsvcs. Ce dernier présente l'avantage de s'exécuter en LocalSystem. Notre DLL exporte trois fonctions : ServiceMain(), obligatoire, InstallService() et RemoveService(). Les deux dernières servent à installer/désinstaller le service grâce à l'utilisation de RunDll32 en ligne de commandes. A noter que l'invite de commandes doit être lancé en mode administrateur. On peut mettre le fichier RacppService.dll dans le dossier System32 puis, pour installer le service, taper la ligne suivante:
rundll32 racppservice.dll InstallService
Pour supprimer le service on tape:
rundll32 racppservice.dll RemoveService
Si on choisit un autre emplacement que System32, il faudra taper le chemin complet de la dll. L'installation ou la suppression de notre service nécessite un redémarrage de Windows.
Tous les détails de l'installation et de la configuration de notre service figurent dans le code de la fonction InstallService().
Notre service utilise un timer pour écrire, toutes les minutes, dans un fichier log c:\racppservice.log, la date et l'heure du déclenchement du timer. Ceci est juste pour montrer le bon fonctionnement du service. Pour tous les détails, regarder le code source.
Le code est réalisé en c sous Visual C/C++ 2005 mais il est facilement adaptable pour les autres outils.
Pour préserver la clarté du code, la gestion des erreurs est minimale.
Le service a tété testé sans aucun problème sous Windows XP, Vista et 7.
La dll RacppService.dll est fournie dans le zip si vous vouler tester.
Voilà, j'espère n'avoir rien oublié.
Tous les commentaires ou remarques sont les bienvenus.

Source / Exemple :


#define  _WIN32_WINNT 0x0500 // Pour Windows 2000 et supérieur
#include <windows.h>

// Déclarations globales:
HINSTANCE hDll;
SERVICE_STATUS_HANDLE   hServiceStatusHandle; 
SERVICE_STATUS          ServiceStatus; 
HANDLE                  hEvent,hFile;
char buff[MAX_PATH]; 

// Fonction d'installation du service:
int WINAPI InstallService()
{
	HKEY hkService,hkSvchost;
	SC_HANDLE schScm ,schService;
	char* pData;
	DWORD dwError=0, dwLen,dwSize = 0;   
	// Ouvrir le gestionnaire de services:
	schScm = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
	if(schScm)
	{
		// Créer notre service:
		schService = CreateService(schScm, "RacppService", 0, SERVICE_ALL_ACCESS, SERVICE_WIN32_SHARE_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, "%SystemRoot%\\System32\\svchost.exe -k netsvcs", 0, 0, 0, 0, 0); 
		if(schService)
		{
			// Ajouter une description à notre service:
			SERVICE_DESCRIPTION sd;
			sd.lpDescription = "Service dans une DLL lancé par svchost.exe";
			ChangeServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, &sd);
			// Ajouter la sous-clé Parameters à la clé de notre service:
			RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\RacppService\\Parameters", 0, 0, REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS, 0, &hkService, 0);   
			// Récupérer le chemin complet de notre Dll:
			GetModuleFileName(hDll, buff, sizeof(buff));
			// Affecter le chemin de la Dll à la sous-clé Parameters sous le nom ServiceDll:
			RegSetValueEx(hkService, "ServiceDll", 0, REG_EXPAND_SZ, (BYTE*)buff, lstrlen(buff)+1);
			// Fermer la clé de notre service:
			RegCloseKey(hkService);
			// Ouvrir la clé indiquant la liste des groupes d'instances de svchost.exe:
			RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Svchost", 0, KEY_ALL_ACCESS, &hkSvchost); 
			// Obtenir la longueur du contenu du groupe netsvcs:
			RegQueryValueEx(hkSvchost, "netsvcs", 0, 0, 0, &dwSize); 
			// Fixer le nom de notre service pour la liste des services du groupe netsvcs:
			lstrcpy(buff,"RacppService");
			dwLen=lstrlen(buff);
			// Allouer la mémoire nécessaire pour la nouvelle liste des noms de services du groupe netsvcs:
			pData=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,dwSize + dwLen + 1);
			if(pData)
			{
				char *pNames;
				// Récupérer la liste des noms de services du groupe netsvcs:
				RegQueryValueEx(hkSvchost, "netsvcs", 0, 0, (BYTE*)pData, &dwSize);
				// Vérifier si le nom existe
				for (pNames = pData; *pNames; pNames=strchr(pNames,0)+1) if(!lstrcmpi(pNames, buff)) break;   
				// L'ajouter s'il n'existe pas:
				if (*pNames == 0)
				{
					memcpy(pData + dwSize - 1, buff, dwLen + 1);
					RegSetValueEx(hkSvchost, "netsvcs", 0, REG_MULTI_SZ, (BYTE*)pData, dwSize + dwLen + 1);
				}
				else dwError=ERROR_ALREADY_EXISTS;
				// Libérer la mémoire allouée:
				HeapFree(GetProcessHeap(),0,pData);
			}
			else dwError=ERROR_NOT_ENOUGH_MEMORY;
			// Fermer la clé svchost:
			RegCloseKey(hkSvchost);
			// Fermer le handle de notre service:
			CloseServiceHandle(schService); 
		}
		// Fermer le handle du gestionnaire de services:
		CloseServiceHandle(schScm);
	}
	// Retourner dwError si non nul, sinon GetLastError():
	return dwError? dwError : GetLastError();
}

// Fonction de désinstallation du service:
DWORD WINAPI RemoveService()
{
	HKEY hkSvchost;
	SC_HANDLE schScm ,schService;
	char* pData;
	DWORD dwError=0,dwSize = 0;   
	// Ouvrir le gestionnaire de services:
	schScm = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
	if(schScm)
	{
		// Ouvrir notre service:
		schService=OpenService(schScm,"RacppService",SERVICE_ALL_ACCESS);
		if(schService)
		{
			// Supprimer le service:
			if(DeleteService(schService))
			{
				// Ouvrir la clé indiquant la liste des groupes d'instances de svchost.exe:
				RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Svchost", 0, KEY_ALL_ACCESS, &hkSvchost); 
				// Obtenir la longueur du contenu du groupe netsvcs:
				RegQueryValueEx(hkSvchost, "netsvcs", 0, 0, 0, &dwSize); 
				// Fixer le nom de notre service pour la liste des services du groupe netsvcs:
				lstrcpy(buff,"RacppService");
				// Allouer la mémoire nécessaire pour tous les noms de services du groupe netsvcs:
				pData=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,dwSize);
				if(pData)
				{
					char *pNames;
					// Récupérer la liste des noms de services du groupe netsvcs:
					RegQueryValueEx(hkSvchost, "netsvcs", 0, 0, (BYTE*)pData, &dwSize);
					// Vérifier si le nom existe:
					for (pNames = pData; *pNames; pNames=strchr(pNames,0)+1) if(!lstrcmpi(pNames,buff)) break;   
					// Le supprimer s'il existe:
					if (*pNames)
					{
						char* pNext;
						pNext=strchr(pNames,0)+1;
						memcpy(pNames,pNext,dwSize-(pNext-pData));
						RegSetValueEx(hkSvchost, "netsvcs", 0, REG_MULTI_SZ, (BYTE*)pData, dwSize -(lstrlen(buff)+1));
					}
					else dwError=ERROR_NO_SUCH_MEMBER;
					// Libérer la mémoire allouée:
					HeapFree(GetProcessHeap(),0,pData);
				}
				else dwError=ERROR_NOT_ENOUGH_MEMORY;
				// Fermer la clé svchost:
				RegCloseKey(hkSvchost);
			}
			// Fermer le handle de notre service:
			CloseServiceHandle(schService); 
		}
		// Fermer le handle du gestionnaire de services:
		CloseServiceHandle(schScm);
	}
	// Retourner dwError si non nul, sinon GetLastError():
	return dwError? dwError : GetLastError();
}

// Fonction HandlerEx communiquant avec le gestionnaire de services:
DWORD WINAPI HandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
{
	switch(dwControl) 
	{
	case SERVICE_CONTROL_STOP:
	case SERVICE_CONTROL_SHUTDOWN:
		// Mettre le service à l'état "En cours d'arrêt":
		ServiceStatus.dwWaitHint      = 1000;
		ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
		SetServiceStatus(hServiceStatusHandle, &ServiceStatus );
		// Mettre l'Event à l'état signalé:
		SetEvent(hEvent);
		return NO_ERROR;
	default:
		ServiceStatus.dwWaitHint = 0;
		break;
	}
	SetServiceStatus(hServiceStatusHandle,  &ServiceStatus);// Obligatoire selon MSDN
	return NO_ERROR;
}

// Fonction Callback appelée toutes les minutes notre timer:
VOID CALLBACK TimerProc( LPVOID lpParameter,BOOLEAN TimerOrWaitFired)
{
	SYSTEMTIME st;
	DWORD dwLen;
	// Se positionner à la fin du fichier log:
	SetFilePointer(hFile,0,0,FILE_END);
	// Former la ligne à écrire dans le fichier log:
	lstrcpy(buff,"Le timer de RacppService a été déclenché le ");
	GetLocalTime(&st);
	GetDateFormat(0,0,&st,"dd/MM/yyyy",buff+lstrlen(buff),12);
	lstrcat(buff," à ");
	GetTimeFormat(0,0,&st,"HH:mm:ss",buff+lstrlen(buff),12);
	lstrcat(buff,"\r\n");
	// Ecrire la ligne formée dans le fichier log:
	WriteFile(hFile,buff,lstrlen(buff),&dwLen,0);
	return;
}

// ServiceMain
VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv)
{
	hServiceStatusHandle = RegisterServiceCtrlHandlerEx("RacppService", HandlerEx, NULL); 
	if (hServiceStatusHandle)
	{
		// Initialiser notre structure SERVICE_STATUS:
		ServiceStatus.dwServiceType        = SERVICE_WIN32;
		ServiceStatus.dwControlsAccepted   = 0;
		ServiceStatus.dwWin32ExitCode      = 0; 
		ServiceStatus.dwServiceSpecificExitCode = 0; 
		ServiceStatus.dwCheckPoint         = 0;
		// Mettre le service dans l'état "En cours de démarrage":
		ServiceStatus.dwWaitHint           = 1000;
		ServiceStatus.dwCurrentState       = SERVICE_START_PENDING; 
		SetServiceStatus(hServiceStatusHandle, &ServiceStatus);
		// Créer un Event avec "non signalé" comme état initial: 
		hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
		if (hEvent)
		{  
			// Ouvrir notre fichier log ou le créer si inexistant:
			hFile=CreateFile("C:\\racppservice.log", GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS, 0, 0);
			if(hFile!=INVALID_HANDLE_VALUE)
			{
				// Créer et lancer notre timer:
				HANDLE hTimerQueue,hTimer;
				hTimerQueue=CreateTimerQueue();
				CreateTimerQueueTimer(&hTimer,hTimerQueue,TimerProc,0,0,60000,WT_EXECUTEINTIMERTHREAD);
				// Mettre le service dans l'état "Démarré":
				ServiceStatus.dwWaitHint           = 0;  
				ServiceStatus.dwCurrentState       = SERVICE_RUNNING; 
				ServiceStatus.dwControlsAccepted   = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN ;
				SetServiceStatus(hServiceStatusHandle, &ServiceStatus);
				// Attendre que l'Event soit à l'état signalé:
				WaitForSingleObject(hEvent, INFINITE);
				// Détruire notre timer:
				DeleteTimerQueueTimer(hTimerQueue,hTimer,0);
				DeleteTimerQueueEx(hTimerQueue,0);
				// Fermer le handle de notre fichier log:
				CloseHandle(hFile);
			}
			// Fermer le handle de l'Event:
			CloseHandle(hEvent);
		}
		// Mettre le service dans l'état "Arrêté":
		ServiceStatus.dwWaitHint = 0; 
		ServiceStatus.dwCurrentState = SERVICE_STOPPED;
		SetServiceStatus(hServiceStatusHandle, &ServiceStatus);
	}
	return;
}

// DllMain
BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved)
{
	// Obtenir le HINSTANCE de notre dll:
	hDll=hInst;
	return TRUE;
}

Codes Sources

A voir également

Ajouter un commentaire Commentaires
belleney Messages postés 4 Date d'inscription dimanche 4 octobre 2009 Statut Membre Dernière intervention 6 mai 2010
6 mai 2010 à 12:02
Pour NT oui aussi; je ne sais pas pourquoi j'ai zappé dans l'énumération !
Mon expérience NT 4 a été très intéressante car je l'ai faite avec un HDD de récupération (provenant d'un poste client) qui n'avait pas été formaté et sortait d'un lot venant d'une grosse entreprise que je ne nommerai pas.
Par un coup de hasard extraordinaire, il se trouvait que ma machine avait la même config hard que la machine d'où provenait le HDD.
Ce qui fait que, mettant tout cela en marche, j'ai eu la bonne surprise de constater que NT se lançait !!!
J'en ai donc profité pour tenter le coup de prendre la main sur le login et sur l'administration. Et j'y suis arrivé relativement assez vite....
en restant évidemment hors connexion... J'ai aussi utilisé win 2000 pro sur poste client. Il est encore installé sur une machine que je mets en marche de temps en temps mais que je n'utilise pas vraiment.
Moralité : la meilleure façon de formater un HDD déclassé... c'est la massette !
racpp Messages postés 1910 Date d'inscription vendredi 18 juin 2004 Statut Modérateur Dernière intervention 14 novembre 2014 17
3 mai 2010 à 23:47
Belleney >> C'est bien, d'ailleurs pour "néophyte" je ne parlais pas de toi. Mais j'espère que tu n'as pas loupé la série Windows NT à partir de 1993 ( NT 3.1, NT 3.51, NT 4, Win 2000) qui permettait de voir Windows autrement. On comparait souvent, à tort, Linux à Windows 3.xx, 9x et Me. Ces derniers étaient sur le point d'être abandonnés au profit de la technologie NT reprise dans XP, Vista et Seven. Pareil pour toutes les versions Win serveur. D'ailleurs, j'aime beaucoup les versions Linux serveur. Mais en tant que programmeur, je trouve plus intéressant et plus profitable de travailler sur un système utilisé par 90% que sur un autre utilisé par seulement 1%. Les acquis théoriques, mis en pratique, permettent, en plus du gain de temps, de chasser certains préjugés ou les fausses impressions et de voir les choses telles qu'elles sont. C'est vrai que, même si Windows est abondamment documenté il reste quand même certains points sombres que Microsoft ne peut ou ne veut mettre en lumière. C'est justement le but de ce genre de code source proposant de petites trouvailles qui divulguent certaines fonctionnalités pas assez documentées.
belleney Messages postés 4 Date d'inscription dimanche 4 octobre 2009 Statut Membre Dernière intervention 6 mai 2010
3 mai 2010 à 20:28
RACPP >> Je fréquente Win depuis avant win si tu veux tout savoir. Dos 6 a été mon premier système. puis j'ai fais un assez court passage à PC GeOS et j'ai embrayé sur 3.1. Je viens d'achever mon parcours win avec windows 7 RC.
J'utilise encore accessoirement XP SP3.et j'ai aussi laissé tombé mes bricolages Bart PE parce que, tout compte fait, tout cela ne rimait à rien.
Administrer oui... mais un OS qui est suffisamment transparent pour avancer en connaissances.
Donc, petit à petit (j'ai dû commencer il y a une dizaine d'années) je suis passé à Linux. Voilà donc pour ce qui est des mains de néophyte... et je suis entièrement d'accord avec toi pour dire que le pire qui puisse arriver à un OS c'est qu'il permette à quiconque d'être, en permanence, utilisateur privilégié.
Je te prie de m'accorder le fait que je n'ai jamais autrement avancé que par une pratique quotidienne et non par des acquis théoriques qui, il faut le reconnaître, sont utiles pour moins perdre de temps.
racpp Messages postés 1910 Date d'inscription vendredi 18 juin 2004 Statut Modérateur Dernière intervention 14 novembre 2014 17
30 avril 2010 à 22:35
Belleney >> Il parait que tu ne connais pas assez Windows. En tant qu'administrateur, on peut tout faire que ce soit sur Windows ou autre. La plus grande faille de sécurité, qui concerne tous les systèmes sans exception, est de laisser un PC en mode administrateur entre les mains d'un néophyte. Seul un administrateur devrait avoir le droit d'installer ce genre de chose. Un utilisateur ayant un compte avec droits restreints ne pourra rien faire d'autre que ce que l'administrateur lui a préalablement installé.
belleney Messages postés 4 Date d'inscription dimanche 4 octobre 2009 Statut Membre Dernière intervention 6 mai 2010
28 avril 2010 à 16:07
ce n'est pas un peu craignos ce genre de petite chose ?
enfin bon.... j'utilise de moins en moins win.

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.