0%

调试练习-服务篇

服务程序由SCM(服务控制管理器)管理,运行服务程序时,需要由服务控制器执行启动命令。服务控制器向SCM提出服务控制请求,SCM向服务程序传递控制命令,并接受返回时参数。

服务启动过程:

所有服务程序都是由外部(服务控制器)调用StartService()API启动的(若程序为自启动,则SCM调用该函数)。

服务进程启动过程

1.服务控制器调用StartService()

SCM会创建相应服务进程,然后执行服务进程EP处代码。

2.服务进程调用StartServiceCtrlDispatcher()

服务进程内部调用该函数来注册服务主函数SvcMain()的地址。

3.服务进程调用SetServiceStatus()

此时服务进程被创建,但状态为SERVICE_START_PENDING.所以需要在服务函数内部调用SetServiceStatuc(SERVICE_RUNNING)API,才能以服务进程形式运行。

举例(DebugMe1.exe)

源代码

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
#include <windows.h>
#include <tchar.h>
#include <stdio.h>

#define SVCNAME L"SvcTest"

VOID InstallService(LPCTSTR szSvcName, LPCTSTR szPath);
VOID UninstallService(LPCTSTR szSvcName);
VOID WINAPI SvcMain(DWORD argc, LPCTSTR *argv);
VOID WINAPI SvcCtrlHandler( DWORD dwCtrl );

SERVICE_STATUS_HANDLE g_hServiceStatusHandle = NULL;
SERVICE_STATUS g_ServiceStatus = {SERVICE_WIN32_OWN_PROCESS, 0, 0xFF, 0, 0, 0, 0};

void _tmain(int argc, TCHAR *argv[])
{
TCHAR szPath[MAX_PATH] = {0,};
SERVICE_TABLE_ENTRY DispatchTable[] =
{
{ SVCNAME, (LPSERVICE_MAIN_FUNCTION)SvcMain },
{ NULL, NULL }
};

if( argc == 1 )
{
if (!StartServiceCtrlDispatcher( DispatchTable ))
{
_tprintf(L"StartServiceCtrlDispatcher() failed!!! [%d]\n",
GetLastError());
}
}
else if( argc == 2 )
{
if( !GetModuleFileName(NULL, szPath, MAX_PATH) )
{
_tprintf(L"GetModuleFileName() failed! [%d]\n",
GetLastError());
return;
}

if( _tcsicmp(argv[1], L"install") == 0 )
{
InstallService(SVCNAME, szPath);
return;
}
else if( _tcsicmp(argv[1], L"uninstall") == 0 )
{
UninstallService(SVCNAME);
return;
}
else
{
_tprintf(L"Wrong parameters!!!\n");
}
}

_tprintf(L"\nUSAGE : %s <install | uninstall>\n", argv[0]);
}

VOID InstallService(LPCTSTR szSvcName, LPCTSTR szPath)
{
SC_HANDLE schSCManager = NULL;
SC_HANDLE schService = NULL;
DWORD dwError = 0;

schSCManager = OpenSCManager(
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if( NULL == schSCManager )
{
_tprintf(L"InstallService() : OpenSCManager failed (%d)\n", GetLastError());
return;
}

schService = CreateService(
schSCManager, // SCM database
szSvcName, // name of service
szSvcName, // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS, // service type
SERVICE_DEMAND_START, // start type
SERVICE_ERROR_NORMAL, // error control type
szPath, // path to service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // LocalSystem account
NULL); // no password
if( NULL == schService )
{
dwError = GetLastError();
_tprintf(L"InstallService() : CreateService failed (%d)\n", dwError);
if( ERROR_SERVICE_EXISTS == dwError )
_tprintf(L" -> The specified service already exists.\n");
goto _EXIT;
}

_tprintf(L"InstallService() : Service installed successfully\n");

_EXIT:
if( schService ) CloseServiceHandle(schService);
if( schSCManager) CloseServiceHandle(schSCManager);
}

VOID UninstallService(LPCTSTR szSvcName)
{
SC_HANDLE schSCManager = NULL;
SC_HANDLE schService = NULL;
SERVICE_STATUS ss = {0,};
DWORD dwError = 0;

schSCManager = OpenSCManager(
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if( NULL == schSCManager )
{
_tprintf(L"UninstallService() : OpenSCManager failed (%d)\n", GetLastError());
return;
}

schService = OpenService(
schSCManager, // SCM database
szSvcName, // name of service
SERVICE_INTERROGATE |
DELETE); // need delete access
if( NULL == schService )
{
dwError = GetLastError();
if( dwError != ERROR_SERVICE_DOES_NOT_EXIST )
_tprintf(L"UninstallService() : OpenSCManager failed (%d)\n", dwError);

goto _EXIT;
}

ControlService(schService, SERVICE_CONTROL_INTERROGATE, &ss);
if( ss.dwCurrentState != SERVICE_STOPPED )
{
_tprintf(L" -> Service is running! Stop the service!!!\n");
goto _EXIT;
}

if( !DeleteService(schService) )
_tprintf(L"UninstallService() : DeleteService failed (%d)\n", GetLastError());
else
_tprintf(L"Service uninstalled successfully\n");

_EXIT:
if( schService ) CloseServiceHandle(schService);
if( schSCManager ) CloseServiceHandle(schSCManager);
}

VOID WINAPI SvcMain(DWORD argc, LPCTSTR *argv)
{
// Service Control Handler
g_hServiceStatusHandle = RegisterServiceCtrlHandler(
SVCNAME,
SvcCtrlHandler);
if( !g_hServiceStatusHandle )
{
OutputDebugString(L"RegisterServiceCtrlHandler() failed!!!");
return;
}

// Service Status -> SERVICE_RUNNING
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(g_hServiceStatusHandle, &g_ServiceStatus);

// Print debug string
while( TRUE )
{
OutputDebugString(L"[SvcTest] service is running...");
Sleep(3 * 1000); // 3 sec
}
}

VOID WINAPI SvcCtrlHandler(DWORD dwCtrl)
{
switch(dwCtrl)
{
case SERVICE_CONTROL_STOP:
g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(g_hServiceStatusHandle, &g_ServiceStatus);

g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(g_hServiceStatusHandle, &g_ServiceStatus);

OutputDebugString(L"[SvcTest] service is stopped...");
break;

default:
break;
}
}

服务进程调试

1.强制设置EIP

我们找到StartServiceCtrlDispatcher()API,他的pServiceTable参数为SERVICE_TABLE_ENTRY结构体指针.第一个成员为SvcTest字符串,第二个成员(401320)为SvcMain()函数地址。

然后我们找到该处,把EIP指定在该处就可以开始调试该处主函数了。

2.附加进程

为了 不让运行时跳过关键代码,我们在运行之前设置无限循环,等服务进程状态变为STATUS_RUNNING时,我们在修改EP处代码。更改服务状态时,需要的调用函数,可能出现服务启动超时的问题。需要对注册表进行修改,这里由于会对电脑受到影响,就不做演示了。