Window服务学习笔记

文章目录
  1. 1. 关于Windows服务
  2. 2. Windows服务的各种属性
  3. 3. 实例
  4. 4. 代码
    1. 4.1. 代码二

参考资料:
用 C 语言编写 Windows 服务程序的五个步骤
如何编写windows服务程序
用C创建并调用Windows服务(守护进程)
创建SvcHost.exe调用的服务原理与实践
黑客防线201301



关于Windows服务

Windows系统中的服务几乎是都是默默无闻的存在的,大部分是一些系统进程,负责电源管理、网络及安全方面等与用户无交互的工作,在客户服务模型中主要是作为服务端存在。




Windows服务主要由3个部分组成,包括服务应用、服务控制程序(scp)及服务控制管理器(scm)。
服务应用即运行的服务程序,与普通的Windows可执行程序类似,只是有一些与scm的接口函数,负责与scm通信。
服务控制程序(scp)即负责控制服务应用的程序,Windows系统内置了一些scm的功能,比如我们可以sc命令控制服务应用的启动、停止等,有一些服务也自带了scp提供一些自定义的共计功能。服务控制程序就是普通的Windows用户程序。
服务控制管理器(scm)就是系统中负责在系统中管理全部服务的应用的,包括安装服务、负责服务自启动。容易和服务控制程序(scp)混淆,scp只是一个工具而scm是一个系统,scp就好比一种方法,scm是一个机构[好吧。。。这个比方不好]。




SCM的可执行文件是Services.exe,Services.exe负责启动启动类型为SERVICE_AUTO_START的服务。这些服务一般都按照特定的顺序被Services.exe启动,启动顺序可以在ServiceGroupOrder和GroupOrder查询,这个顺序和DependOnGroup和DependOnService键值相关。
SCM通过命名管道控制各个服务。


Windows服务的各种属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//创建服务功能函数,也可以通过直接修改注册表创建服务
SC_HANDLE WINAPI CreateService(
_In_ SC_HANDLE hSCManager, // handle to SCM database
_In_ LPCTSTR lpServiceName, // name of service to start
_In_opt_ LPCTSTR lpDisplayName, // display name
_In_ DWORD dwDesiredAccess, // type of access to service
_In_ DWORD dwServiceType, // type of service
_In_ DWORD dwStartType, // when to start service
_In_ DWORD dwErrorControl, // severity of service failure
_In_opt_ LPCTSTR lpBinaryPathName, // name of binary file
_In_opt_ LPCTSTR lpLoadOrderGroup, // name of load ordering group
_Out_opt_ LPDWORD lpdwTagId, // tag identifier
_In_opt_ LPCTSTR lpDependencies, // array of dependency names
_In_opt_ LPCTSTR lpServiceStartName, // account name
_In_opt_ LPCTSTR lpPassword // account password);
)
;

//一个普通服务注册表项
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\MozillaMaintenance]
"Type"=dword:00000010
"Start"=dword:00000003
"ErrorControl"=dword:00000001
"ImagePath"=hex(2):22,00,43,00,3a,00,5c,00,50,00,72,00,6f,00,67,00,72,00,61,00,\
6d,00,20,00,46,00,69,00,6c,00,65,00,73,00,20,00,28,00,78,00,38,00,36,00,29,\
00,5c,00,4d,00,6f,00,7a,00,69,00,6c,00,6c,00,61,00,20,00,4d,00,61,00,69,00,\
6e,00,74,00,65,00,6e,00,61,00,6e,00,63,00,65,00,20,00,53,00,65,00,72,00,76,\
00,69,00,63,00,65,00,5c,00,6d,00,61,00,69,00,6e,00,74,00,65,00,6e,00,61,00,\
6e,00,63,00,65,00,73,00,65,00,72,00,76,00,69,00,63,00,65,00,2e,00,65,00,78,\
00,65,00,22,00,00,00
"DisplayName"="Mozilla Maintenance Service"
"WOW64"=dword:00000001
"ObjectName"="LocalSystem"
"Description"="Mozilla 维护服务能确保您的计算机上使用了最新且最安全的 Mozilla Firefox 版本。保持 Firefox 版本及时更新对您的网络安全是非常重要的,Mozilla 强烈建议您将这个服务保持开启状态。"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\MozillaMaintenance\Security]
"Security"=hex:01,00,14,80,a4,00,00,00,b0,00,00,00,14,00,00,00,30,00,00,00,02,\
00,1c,00,01,00,00,00,02,80,14,00,ff,01,0f,00,01,01,00,00,00,00,00,01,00,00,\
00,00,02,00,74,00,05,00,00,00,00,00,18,00,bd,00,02,00,01,02,00,00,00,00,00,\
05,20,00,00,00,21,02,00,00,00,00,14,00,fd,01,02,00,01,01,00,00,00,00,00,05,\
12,00,00,00,00,00,18,00,ff,01,0f,00,01,02,00,00,00,00,00,05,20,00,00,00,20,\
02,00,00,00,00,14,00,8d,01,02,00,01,01,00,00,00,00,00,05,04,00,00,00,00,00,\
14,00,8d,01,02,00,01,01,00,00,00,00,00,05,06,00,00,00,01,01,00,00,00,00,00,\
05,12,00,00,00,01,01,00,00,00,00,00,05,12,00,00,00
  • 自启动
    服务自启动应该是Windows提供给用户的启动最早的自启动接口了,当服务被设置为自启动后能够在不进入桌面就启动(所以Windows服务器中的网络服务都能在不登陆的情况下提供服务),并且很多系统服务默认自启动。
    dwStartType有一下几个选项:

    1
    2
    3
    4
    5
    6
    7
    #define SERVICE_BOOT_START             0x00000000  启动最早,提供给一些硬件驱动使用,
    由system启动由NTLDR加载
    #define SERVICE_SYSTEM_START 0x00000001 启动较早,提供给由IoInitSystem初始的驱动程序 内核初始化时加载
    #define SERVICE_AUTO_START 0x00000002 Windows服务使用,随系统自启动由service control manager启动
    #define SERVICE_DEMAND_START 0x00000003 Windows服务使用,服务需要手动启动
    #define SERVICE_DISABLED 0x00000004 Windows服务使用,该服务不能启动,
    如果启动会导致ERROR_SERVICE_DISABLED错误
  • 后台执行
    Windows服务运行在后台,无界面很少与用户交互。出于安全方面的考虑,在Windows vista之后服务和普通用户程序运行在不同的session(session隔离或则服务隔离),如果服务需要与用户交互需要启动一个用于程序然后通过进程间通信的方式将结果呈现给用户(这就和驱动程序类似)。

    1
    2
    3
    4
    5
    6
    7
    8
    //一个session隔离实验
    实验系统 Windows 7
    //创建一个与用户交互的cmd服务(注意等号与值中间有个空格)
    1.sc Create CmdService binPath= "cmd /K start" type= own type= interact
    //启动该服务
    2.sc start CmdService
    3.这时会看到一个消息弹出,选择查看消息。这是你会进入另一个世界,但这个世界还没有盘古开天 哈哈
    4.在命令行中输入 explorer 并执行,完成开天辟地了 新世界创建成功
  • 高权限
    我们知道Windows有个进程完整性级别这个东西,不同完整性界别有不同的权限。有Untrust、Low、Medium、Hight、System级别从低到高,而服务的进程性完整性级别就是最高的system,所以拥有的权限也是最多的。拥有debug、加载驱动、创建全局内存共享等权限。因此这也是各种恶意软件、防病毒软件都争夺的地方。

实例

一直一来都有种困惑在心头(其实现在也没有完全解决),就是各种防病毒软件怎么自启动的或则说怎么自启动后还拥有高权限的?通过注册表或则计划任务启动的都是运行在Medium完整性级别(就是普通用户进程权限很少)而服务虽然拥有高权限且启动早,但是运行在session 0存在服务隔离不能与用户交互。现在可能有个初步想法防病毒软件进程可能是通过服务或则驱动启动的。由服务启动可与用户交互的代码参考Windows安全机制学习笔记
更新:自启动获得高权限的另外一种方式就通过计划任务实现,计划任务在配置时候可以选择用户权限。实例:如何设置让 Everything 在 Win7 下开机启动](http://www.appinn.com/how-to-startup-everything-win7/)BYPASSING UAC ON WINDOWS 10 USING DISK CLEANUP

代码

这里总结了两种服务的代码,具体方式请看参考资料。第一种服务由自己的进程启动bin文件是个exe,创建服务只需要createservice就可以了。第二种由svchost启动的服务bin文件是个dll,创建服务除了需要createservice之外还需要增加修改一些注册表键值,包括HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost及服务自身注册表。

###代码一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//功能:一个进程一个服务,用于向E:
//1.txt文件写入每2000毫秒后的可用内存大小(MB)

#include <stdio.h>
#include <windows.h>

#define SLEEP_TIME 5000 //SLEEP_TIME 指定两次连续查询可用内存之间的毫秒间隔。
#define LOGFILE "C:\\1.txt" //日志文件的路径

SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;

bool brun = false;
int error;

void WINAPI ServiceMain(int argc, char** argv);
void WINAPI CtrlHandler(DWORD request);


int WriteToLog(char* str)
{

FILE* log;
log = fopen(LOGFILE, "a+");
if (log == NULL)
return -1;
fprintf(log, "%s\n", str);
fclose(log);
return 0;
}

void main()
{

//定义一个SERVICE_TABLE_ENTRY 结构
SERVICE_TABLE_ENTRY ServiceTable[2];

//一个程序可能包含若干个服务。每一个服务都必须列于专门的分派表中
//(为此该程序定义了一个 ServiceTable 结构数组)(每个数组相对应于每个服务(除了最后一个数组))
//这个表中的每一项都要在 SERVICE_TABLE_ENTRY 结构之中。

ServiceTable[0].lpServiceName = (LPWSTR)"testservice";
ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;

//分派表的最后一项必须是服务名和服务主函数域的 NULL 指针
ServiceTable[1].lpServiceName = NULL;
ServiceTable[1].lpServiceProc = NULL;

// 启动服务的控制分派机线程
StartServiceCtrlDispatcher(ServiceTable);
}

//服务控制管理器(SCM:Services Control Manager)是一个管理系统所有服务的进程。
//当 SCM 启动某个服务时,它等待某个进程的主线程来调用 StartServiceCtrlDispatcher 函数。
//将分派表传递给 StartServiceCtrlDispatcher。这将把调用进程的主线程转换为控制分派器。
//该分派器启动一个新线程,该线程运行分派表中每个服务的 ServiceMain 函数(本文例子中只有一个服务)
//分派器还监视程序中所有服务的执行情况。然后分派器将控制请求从 SCM 传给服务。
//注意:如果 StartServiceCtrlDispatcher 函数30秒没有被调用,便会报错,
//为了避免这种情况,我们必须在 ServiceMain 函数中(参见本文例子)
//或在非主函数的单独线程中初始化服务分派表。
//本文所描述的服务不需要防范这样的情况。
//分派表中所有的服务执行完之后(例如,用户通过“服务”控制面板程序停止它们)或者发生错误时,
//StartServiceCtrlDispatcher 调用返回。然后主进程终止。


/*ServiceMain(),该函数是服务的入口点。
它运行在一个单独的线程当中,这个线程是由控制分派器创建的。
ServiceMain 应该尽可能早早为服务注册控制处理器。
这要通过调用 RegisterServiceCtrlHadler 函数来实现。
你要将两个参数传递给此函数:服务名和指向 ControlHandlerfunction 的指针。
它指示控制分派器调用 ControlHandler 函数处理 SCM 控制请求。
注册完控制处理器之后,获得状态句柄(hStatus)。
通过调用 SetServiceStatus 函数,用 hStatus 向 SCM 报告服务的状态。

下面展示了如何指定服务特征和其当前状态来初始化 ServiceStatus 结构,
ServiceStatus 结构的每个域都有其用途:

dwServiceType:指示服务类型,创建 Win32 服务。赋值 SERVICE_WIN32;
dwCurrentState:指定服务的当前状态。因为服务的初始化在这里没有完成,所以这里的状态为 SERVICE_START_PENDING;
`dwWin32ExitCode 和 dwServiceSpecificExitCode:这两个域在你终止服务并报告退出细节时很有用。初始化服务时并不退出,因此,它们的值为 0;
dwCheckPoint 和 dwWaitHint:这两个域表示初始化某个服务进程时要30秒以上。本文例子服务的初始化过程很短,所以这两个域的值都为 0。

调用 SetServiceStatus 函数向 SCM 报告服务的状态时。要提供 hStatus 句柄和 ServiceStatus 结构。注意 ServiceStatus 一个全局变量,所以你可以跨多个函数使用它。ServiceMain 函数中,你给结构的几个域赋值,它们在服务运行的整个过程中都保持不变,比如:dwServiceType。

在报告了服务状态之后,你可以调用 InitService 函数来完成初始化。这个函数只是添加一个说明性字符串到日志文件。如下面代码所示:
服务初始化*/



//在 ServiceMain 中,检查 InitService 函数的返回值。
//如果初始化有错(因为有可能写日志文件失败),
//则将服务状态置为终止并退出 ServiceMain:

void WINAPI ServiceMain(int argc, char **argv){

ServiceStatus.dwServiceType = SERVICE_WIN32;
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN
| SERVICE_ACCEPT_STOP;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwServiceSpecificExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;

hStatus = ::RegisterServiceCtrlHandler((LPCWSTR)"testservice", CtrlHandler);

if (hStatus == 0)
{
WriteToLog("RegisterServiceCtrlHandler failed");
// 初始化失败,终止服务
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = -1;
SetServiceStatus(hStatus, &ServiceStatus);
// 退出 ServiceMain
return;
}

WriteToLog("RegisterServiceCtrlHandler success");

//向SCM 报告运行状态
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(hStatus, &ServiceStatus);

//下面就开始任务循环了,你可以添加你自己希望服务做的工作



brun = true;
MEMORYSTATUS memstatus;

char str[100];
memset(str, '\0', 100);
while (brun)
{
GlobalMemoryStatus(&memstatus);

int availmb = memstatus.dwAvailPhys / 1024 / 1024;

sprintf_s(str, 100, "available memory is %dMB", availmb);

WriteToLog(str);

Sleep(SLEEP_TIME);
}

WriteToLog("service stopped");

}
//循环一直到服务的状态为 SERVICE_RUNNING 或日志文件写入出错为止。
//状态可能在 ControlHandler 函数响应 SCM 控制请求时修改。




/*
前面用 ServiceMain 函数注册了控制处理器函数。
控制处理器检查 SCM 发送了什么请求并采取相应行动。
每次你调用 SetServiceStatus 函数的时候,必须指定服务接收 STOP 和 SHUTDOWN 请求。
而这些请求要在 ControlHandler 函数中处理它们。
STOP 请求是 SCM 终止服务的时候发送的。
例如,如果用户在“服务”控制面板中手动终止服务。
SHUTDOWN 请求是关闭机器时,由 SCM 发送给所有运行中服务的请求。
两种情况的处理方式相同:写日志文件,监视停止;向SCM 报告SERVICE_STOPPED 状态。

由于 ServiceStatus 结构对于整个程序而言为全局量,
ServiceStatus 中的工作循环在当前状态改变或服务终止后停止。
其它的控制请求如:PAUSE 和 CONTINUE 在本文的例子没有处理。
控制处理器函数必须报告服务状态,即便 SCM 每次发送控制请求的时候状态保持相同。
因此,不管响应什么请求,都要调用 SetServiceStatus。
*/



void WINAPI CtrlHandler(DWORD request)
{

switch (request)
{
case SERVICE_CONTROL_STOP:
brun = false;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
WriteToLog("SERVICE_CONTROL_STOP---SERVICE_STOPPED");
break;
case SERVICE_CONTROL_SHUTDOWN:
brun = false;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
WriteToLog("SERVICE_CONTROL_SHUTDOWN---SERVICE_STOPPED");
break;
default:
break;
}
SetServiceStatus(hStatus, &ServiceStatus);
}

代码二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
// SvcHostDLL.cpp : Demo for a service dll used by svchost.exe to host it.
//
// for detail comment see articles.
// by bingle_at_email.com.cn
// www.BingleSite.net
//
/* save following as a .def file to export function, only ServiceMain is needed.
other used to install & uninstall service.
or use /EXPORT: link option to export them.

EXPORTS
ServiceMain
InstallService
UninstallService
RundllUninstallA
RundllInstallA
*/

/*
To compile & link:
cl /MD /GX /LD svchostdll.cpp /link advapi32.lib /DLL /base:0x71000000 /export:ServiceMain /EXPORT:RundllUninstallA /EXPORT:RundllInstallA /EXPORT:InstallService /EXPORT:UninstallService
*/


//
// Articles:
// 1. HOWTO Create a service dll used by svchost.exe by bingle, at: http://www.BingleSite.net/article/svchost-dll-service.html
// 2. Inside Win32 Services, Part 2 by: Mark Russinovich, at: http://www.winnetmag.com/Articles/Index.cfm?ArticleID=8943&pg=3
// 3. Platform SDK: Tools - Rundll32, at: http://msdn.microsoft.com/library/en-us/tools/tools/rundll32.asp

#include <stdio.h>
#include <time.h>
#include <assert.h>
#include <windows.h>

#define DEFAULT_SERVICE "IPRIP"
#define MY_EXECUTE_NAME "SvcHostDLL.exe"

#pragma warning(disable:4996)

//main service process function
void __stdcall ServiceMain( int argc, wchar_t* argv[] );
//report service stat to the service control manager
int TellSCM( DWORD dwState, DWORD dwExitCode, DWORD dwProgress );
//service control handler, call back by service control manager
void __stdcall ServiceHandler( DWORD dwCommand );
//RealService just create a process
int RealService(char *cmd, int bInteract);

//Install this dll as a Service host by svchost.exe, service name is given by caller
int InstallService(char *name);
//unInstall a Service, be CARE FOR call this to delete a service
int UninstallService(char *name);
//Install this dll as a Service host by svchost.exe, used by RUNDLL32.EXE to call
void CALLBACK RundllInstallA(HWND hwnd, HINSTANCE hinst, char *param, int nCmdShow);
//unInstall a Service used by RUNDLL32.EXE to call, be CARE FOR call this to delete a service
void CALLBACK RundllUninstallA(HWND hwnd, HINSTANCE hinst, char *param, int nCmdShow);

//output the debug infor into log file(or stderr if a console program call me) & DbgPrint
void OutputString( char *lpFmt, ... );

//#pragma comment(linker, "/EXPORT:ServiceMain")

//dll module handle used to get dll path in InstallService
HANDLE hDll = NULL;
//Service HANDLE & STATUS used to get service state
SERVICE_STATUS_HANDLE hSrv;
DWORD dwCurrState;


BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)

{

switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
hDll = hModule;
#ifdef _DEBUG
AllocConsole();
OutputString("SvcHostDLL: DllMain called DLL_PROCESS_ATTACH");
break;

case DLL_THREAD_ATTACH:
OutputString("SvcHostDLL: DllMain called DLL_THREAD_ATTACH");
case DLL_THREAD_DETACH:
OutputString("SvcHostDLL: DllMain called DLL_THREAD_DETACH");
case DLL_PROCESS_DETACH:
TellSCM( SERVICE_STOP_PENDING, 0, 0 );
Sleep(1500);
TellSCM( SERVICE_STOPPED, 0, 0 );
OutputString("SvcHostDLL: DllMain called DLL_PROCESS_DETACH");
#endif
break;
}

return TRUE;
}

int GetSvcOption(char *svcname, char *file, int *bInteract)
{

// Open a handle to the SC Manager database.
int rc = 0, val = 0;
HKEY hkRoot = HKEY_LOCAL_MACHINE, hkParam = 0;

try{
if(!svcname || !svcname[0]) return 0;

//query service setting
char buff[500];
_snprintf(buff, sizeof buff, "SYSTEM\\CurrentControlSet\\Services\\%s\\Parameters", svcname);
rc = RegOpenKeyEx(hkRoot, buff, 0, KEY_QUERY_VALUE, &hkRoot);
if(ERROR_SUCCESS != rc)
{
OutputString("RegOpenKeyEx(%s) KEY_QUERY_VALUE error %d.", buff, rc);
throw "";
}

DWORD type, size = MAX_PATH;
rc = RegQueryValueEx(hkRoot, "program", 0, &type, (unsigned char*)file, &size);
SetLastError(rc);
if(ERROR_SUCCESS != rc)
throw "RegQueryValueEx(Parameters\\program)";

val++;
OutputString("program=%s", file);

size = sizeof *bInteract;
rc = RegQueryValueEx(hkRoot, "Interactive", 0, &type, (unsigned char*)bInteract, &size);
SetLastError(rc);
if(ERROR_SUCCESS != rc)
throw "RegQueryValueEx(Parameters\\Interactive)";

val++;
OutputString("Interactive=%d", *bInteract);


}catch(char *str)
{
if(str && str[0])
{
rc = GetLastError();
OutputString("%s error %d", str, rc);
}
}

RegCloseKey(hkRoot);

return val;
}

void __stdcall ServiceMain( int argc, wchar_t* argv[] )
{

// DebugBreak();
char svcname[256], file[MAX_PATH];
strncpy(svcname, (char*)argv[0], sizeof svcname); //it's should be unicode, but if it's ansi we do it well
wcstombs(svcname, argv[0], sizeof svcname);
OutputString("SvcHostDLL: ServiceMain(%d, %s) called", argc, svcname);

hSrv = RegisterServiceCtrlHandler( svcname, (LPHANDLER_FUNCTION)ServiceHandler );
if( hSrv == NULL )
{
OutputString("SvcHostDLL: RegisterServiceCtrlHandler %S failed", argv[0]);
return;
}else FreeConsole();

TellSCM( SERVICE_START_PENDING, 0, 1 );
TellSCM( SERVICE_RUNNING, 0, 0 );

// call Real Service function noew
int bInteract = argc > 2 ? 1 : 0;
if(argc > 1)
strncpy(file, (char*)argv[1], sizeof file),
wcstombs(file, argv[1], sizeof file);
else if(!GetSvcOption(svcname, file, &bInteract))
strncpy(file, MY_EXECUTE_NAME, sizeof file);

RealService(file, bInteract);

do{
Sleep(10);//not quit until receive stop command, otherwise the service will stop
}while(dwCurrState != SERVICE_STOP_PENDING && dwCurrState != SERVICE_STOPPED);

OutputString("SvcHostDLL: ServiceMain done");
return;
}

int TellSCM( DWORD dwState, DWORD dwExitCode, DWORD dwProgress )
{

SERVICE_STATUS srvStatus;
srvStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
srvStatus.dwCurrentState = dwCurrState = dwState;
srvStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
srvStatus.dwWin32ExitCode = dwExitCode;
srvStatus.dwServiceSpecificExitCode = 0;
srvStatus.dwCheckPoint = dwProgress;
srvStatus.dwWaitHint = 3000;
return SetServiceStatus( hSrv, &srvStatus );
}

void __stdcall ServiceHandler( DWORD dwCommand )
{

// not really necessary because the service stops quickly
switch( dwCommand )
{
case SERVICE_CONTROL_STOP:
TellSCM( SERVICE_STOP_PENDING, 0, 1 );
OutputString("SvcHostDLL: ServiceHandler called SERVICE_CONTROL_STOP");
Sleep(10);
TellSCM( SERVICE_STOPPED, 0, 0 );
break;
case SERVICE_CONTROL_PAUSE:
TellSCM( SERVICE_PAUSE_PENDING, 0, 1 );
OutputString("SvcHostDLL: ServiceHandler called SERVICE_CONTROL_PAUSE");
TellSCM( SERVICE_PAUSED, 0, 0 );
break;
case SERVICE_CONTROL_CONTINUE:
TellSCM( SERVICE_CONTINUE_PENDING, 0, 1 );
OutputString("SvcHostDLL: ServiceHandler called SERVICE_CONTROL_CONTINUE");
TellSCM( SERVICE_RUNNING, 0, 0 );
break;
case SERVICE_CONTROL_INTERROGATE:
OutputString("SvcHostDLL: ServiceHandler called SERVICE_CONTROL_INTERROGATE");
TellSCM( dwCurrState, 0, 0 );
break;
case SERVICE_CONTROL_SHUTDOWN:
OutputString("SvcHostDLL: ServiceHandler called SERVICE_CONTROL_SHUTDOWN");
TellSCM( SERVICE_STOPPED, 0, 0 );
break;
}
}


//RealService just create a process
int RealService(char *cmd, int bInteract)
{

OutputString("SvcHostDLL: RealService called '%s' %s", cmd, bInteract ? "Interact" : "");
STARTUPINFO si = {0};
PROCESS_INFORMATION pi;
si.cb = sizeof si;
if(bInteract) si.lpDesktop = "WinSta0\\Default";
if(!CreateProcess(NULL, cmd, NULL, NULL, false, 0, NULL, NULL, &si, &pi))
OutputString("SvcHostDLL: CreateProcess(%s) error:%d", cmd, GetLastError());
else OutputString("SvcHostDLL: CreateProcess(%s) to %d", cmd, pi.dwProcessId);

return 0;
}


int InstallService(char *name)
{

// Open a handle to the SC Manager database.
int rc = 0;
HKEY hkRoot = HKEY_LOCAL_MACHINE, hkParam = 0;
SC_HANDLE hscm = NULL, schService = NULL;

try{
char buff[500];
char *svcname = DEFAULT_SERVICE;
if(name && name[0]) svcname = name;

//query svchost setting
char *ptr, *pSvchost = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Svchost";
rc = RegOpenKeyEx(hkRoot, pSvchost, 0, KEY_QUERY_VALUE, &hkRoot);
if(ERROR_SUCCESS != rc)
{
OutputString("RegOpenKeyEx(%s) KEY_QUERY_VALUE error %d.", pSvchost, rc);
throw "";
}

DWORD type, size = sizeof buff;
rc = RegQueryValueEx(hkRoot, "netsvcs", 0, &type, (unsigned char*)buff, &size);
RegCloseKey(hkRoot);
SetLastError(rc);
if(ERROR_SUCCESS != rc)
throw "RegQueryValueEx(Svchost\\netsvcs)";

//实际情况服务存在创建失败
/*
for(ptr = buff; *ptr; ptr = strchr(ptr, 0)+1)
if(stricmp(ptr, svcname) == 0) break;

if(*ptr == 0)
{
OutputString("you specify service name not in Svchost\\netsvcs, must be one of following:");
for(ptr = buff; *ptr; ptr = strchr(ptr, 0)+1)
OutputString(" - %s", ptr);
throw "";
}*/


//install service
hscm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hscm == NULL)
throw "OpenSCManager()";

char *bin = "%SystemRoot%\\System32\\svchost.exe -k netsvcs";

schService = CreateService(
hscm, // SCManager database
svcname, // name of service
NULL, // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_SHARE_PROCESS, // service type
SERVICE_AUTO_START, // start type
SERVICE_ERROR_NORMAL, // error control type
bin, // service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // LocalSystem account
NULL); // no password

if (schService == NULL)
{
OutputString("CreateService(%s) error %d", svcname, rc = GetLastError());
throw "";
}
OutputString("CreateService(%s) SUCCESS. Config it", svcname);

CloseServiceHandle(schService);
CloseServiceHandle(hscm);

//config service
hkRoot = HKEY_LOCAL_MACHINE;
strncpy(buff, "SYSTEM\\CurrentControlSet\\Services\\", sizeof buff);
strncat(buff, svcname, 100);
rc = RegOpenKeyEx(hkRoot, buff, 0, KEY_ALL_ACCESS, &hkRoot);
if(ERROR_SUCCESS != rc)
{
OutputString("RegOpenKeyEx(%s) KEY_SET_VALUE error %d.", svcname, rc);
throw "";
}

rc = RegCreateKey(hkRoot, "Parameters", &hkParam);
SetLastError(rc);
if(ERROR_SUCCESS != rc)
throw "RegCreateKey(Parameters)";

if(!GetModuleFileName(HMODULE(hDll), buff, sizeof buff))
throw "GetModuleFileName() get dll path";

rc = RegSetValueEx(hkParam, "ServiceDll", 0, REG_EXPAND_SZ, (unsigned char*)buff, strlen(buff)+1);
SetLastError(rc);
if(ERROR_SUCCESS != rc)
throw "RegSetValueEx(ServiceDll)";

rc = RegSetValueEx(hkParam, "program", 0, REG_EXPAND_SZ, (unsigned char*)MY_EXECUTE_NAME, strlen(MY_EXECUTE_NAME)+1);
rc = 0;
rc = RegSetValueEx(hkParam, "Interactive", 0, REG_DWORD, (unsigned char*)&rc, sizeof rc);

OutputString("Config service %s ok.", svcname);
}catch(char *str)
{
if(str && str[0])
{
rc = GetLastError();
OutputString("%s error %d", str, rc);
}
}

RegCloseKey(hkRoot);
RegCloseKey(hkParam);
CloseServiceHandle(schService);
CloseServiceHandle(hscm);

return rc;
}

/*
used to install by rundll32.exe
Platform SDK: Tools - Rundll32
The Run DLL utility (Rundll32.exe) included in Windows enables you to call functions exported from a 32-bit DLL. These functions must have the following syntax:
*/

void CALLBACK RundllInstallA(
HWND hwnd, // handle to owner window
HINSTANCE hinst, // instance handle for the DLL
char *param, // string the DLL will parse
int nCmdShow // show state
)

{

InstallService(param);
}


int UninstallService(char *name)
{

int rc = 0;
SC_HANDLE schService;
SC_HANDLE hscm;

__try{
hscm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hscm == NULL)
{
OutputString("OpenSCManager() error %d", rc = GetLastError() );
return rc;
}

char *svcname = DEFAULT_SERVICE;
if(name && name[0]) svcname = name;

schService = OpenService(hscm, svcname, DELETE);
if (schService == NULL)
{
OutputString("OpenService(%s) error %d", svcname, rc = GetLastError() );
return rc;
}

if (!DeleteService(schService) )
{
OutputString("OpenService(%s) error %d", svcname, rc = GetLastError() );
return rc;
}

OutputString("DeleteService(%s) SUCCESS.", svcname);
}__except(1)
{
OutputString("Exception Catched 0x%X", GetExceptionCode());
}

CloseServiceHandle(schService);
CloseServiceHandle(hscm);
return rc;
}

/*
used to uninstall by rundll32.exe
Platform SDK: Tools - Rundll32
The Run DLL utility (Rundll32.exe) included in Windows enables you to call functions exported from a 32-bit DLL. These functions must have the following syntax:
*/

void CALLBACK RundllUninstallA(
HWND hwnd, // handle to owner window
HINSTANCE hinst, // instance handle for the DLL
char *param, // string the DLL will parse
int nCmdShow // show state
)

{

UninstallService(param);
}

//output the debug infor into log file & DbgPrint
void OutputString( char *lpFmt, ... )
{

char buff[1024];
va_list arglist;
va_start( arglist, lpFmt );
_vsnprintf( buff, sizeof buff, lpFmt, arglist );
va_end( arglist );

DWORD len;
HANDLE herr = GetStdHandle(STD_OUTPUT_HANDLE);
if(herr != INVALID_HANDLE_VALUE)
{
WriteFile(herr, buff, strlen(buff), &len, NULL);
WriteFile(herr, "\r\n", 2, &len, NULL);
}else
{
FILE *fp = fopen("SvcHost.DLL.log", "a");
if(fp)
{
char date[20], time[20];
fprintf(fp, "%s %s - %s\n", _strdate(date), _strtime(time), buff);
if(!stderr) fclose(fp);
}
}

OutputDebugString(buff);
}