2 #include "gdipp_config/constant_hook.h"
3 #include "gdipp_lib/helper.h"
4 #include "gdipp_server/global.h"
5 #include "gdipp_server/rpc_server.h"
10 #define SVC_NAME L"gdipp_svc"
12 SERVICE_STATUS svc_status
= {};
13 SERVICE_STATUS_HANDLE h_svc_status
= NULL
;
15 HANDLE h_svc_events
, h_wait_cleanup
, h_rpc_thread
;
17 std::map
<ULONG
, HANDLE
> h_user_tokens
;
18 std::map
<ULONG
, PROCESS_INFORMATION
> pi_hooks_32
, pi_hooks_64
;
20 BOOL
hook_proc(HANDLE h_user_token
, char *hook_env_str
, const wchar_t *gdipp_hook_name
, PROCESS_INFORMATION
&pi
)
22 wchar_t gdipp_hook_path
[MAX_PATH
];
24 if (!get_dir_file_path(NULL
, gdipp_hook_name
, gdipp_hook_path
))
27 STARTUPINFOW si
= {sizeof(STARTUPINFO
)};
29 return CreateProcessAsUserW(h_user_token
, gdipp_hook_path
, NULL
, NULL
, NULL
, TRUE
, NULL
, hook_env_str
, NULL
, &si
, &pi
);
32 BOOL
start_hook(ULONG session_id
)
34 // make the event handle inheritable
35 SECURITY_ATTRIBUTES inheritable_sa
= {sizeof(SECURITY_ATTRIBUTES
), NULL
, TRUE
};
36 BOOL b_ret
, hook_success
= TRUE
;
39 b_ret
= WTSQueryUserToken(session_id
, &h_user_token
);
46 // use the linked token if exists
47 // needed in UAC-enabled scenarios and Run As Administrator
48 TOKEN_LINKED_TOKEN linked_token
;
50 b_ret
= GetTokenInformation(h_user_token
, TokenLinkedToken
, &linked_token
, sizeof(TOKEN_LINKED_TOKEN
), &token_info_len
);
53 CloseHandle(h_user_token
);
54 h_user_token
= linked_token
.LinkedToken
;
57 char hook_env_str
[64];
58 sprintf_s(hook_env_str
, "gdipp_svc_proc_id=%d%c", GetCurrentProcessId(), 0);
60 if (!!config_instance
.get_number(L
"/gdipp/hook/include/proc_32_bit/text()", static_cast<int>(gdipp::hook_config::PROC_32_BIT
)))
62 const wchar_t *gdipp_hook_name_32
= L
"gdipp_hook_32.exe";
63 PROCESS_INFORMATION pi
;
65 if (hook_proc(h_user_token
, hook_env_str
, gdipp_hook_name_32
, pi
))
66 pi_hooks_32
[session_id
] = pi
;
71 if (!!config_instance
.get_number(L
"/gdipp/hook/include/proc_64_bit/text()", static_cast<int>(gdipp::hook_config::PROC_64_BIT
)))
73 const wchar_t *gdipp_hook_name_64
= L
"gdipp_hook_64.exe";
74 PROCESS_INFORMATION pi
;
76 if (hook_proc(h_user_token
, hook_env_str
, gdipp_hook_name_64
, pi
))
77 pi_hooks_64
[session_id
] = pi
;
85 h_user_tokens
[session_id
] = h_user_token
;
90 CloseHandle(h_user_token
);
96 void stop_hook(ULONG session_id
)
98 std::map
<ULONG
, PROCESS_INFORMATION
>::const_iterator pi_iter_32
, pi_iter_64
;
99 HANDLE h_hook_processes
[2] = {};
101 pi_iter_32
= pi_hooks_32
.find(session_id
);
102 if (pi_iter_32
!= pi_hooks_32
.end())
103 h_hook_processes
[0] = pi_iter_32
->second
.hProcess
;
105 pi_iter_64
= pi_hooks_64
.find(session_id
);
106 if (pi_iter_64
!= pi_hooks_64
.end())
107 h_hook_processes
[1] = pi_iter_64
->second
.hProcess
;
109 // notify and wait hook subprocesses to exit
110 WaitForMultipleObjects(2, h_hook_processes
, TRUE
, INFINITE
);
114 if (pi_iter_32
!= pi_hooks_32
.end())
116 CloseHandle(pi_iter_32
->second
.hThread
);
117 CloseHandle(pi_iter_32
->second
.hProcess
);
118 pi_hooks_32
.erase(pi_iter_32
);
121 if (pi_iter_64
!= pi_hooks_64
.end())
123 CloseHandle(pi_iter_64
->second
.hThread
);
124 CloseHandle(pi_iter_64
->second
.hProcess
);
125 pi_hooks_64
.erase(pi_iter_64
);
128 CloseHandle(h_user_tokens
[session_id
]);
129 h_user_tokens
.erase(session_id
);
132 void set_svc_status(DWORD dwCurrentState
, DWORD dwWin32ExitCode
, DWORD dwWaitHint
)
134 static DWORD dwCheckPoint
= 1;
136 // fill in the SERVICE_STATUS structure
137 svc_status
.dwCurrentState
= dwCurrentState
;
138 svc_status
.dwWin32ExitCode
= dwWin32ExitCode
;
139 svc_status
.dwWaitHint
= dwWaitHint
;
141 if (dwCurrentState
== SERVICE_RUNNING
)
142 svc_status
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SESSIONCHANGE
;
144 svc_status
.dwControlsAccepted
= 0;
146 if (dwCurrentState
== SERVICE_RUNNING
|| dwCurrentState
== SERVICE_STOPPED
)
147 svc_status
.dwCheckPoint
= 0;
149 svc_status
.dwCheckPoint
= ++dwCheckPoint
;
151 // report the status of the service to the SCM
152 SetServiceStatus(h_svc_status
, &svc_status
);
155 DWORD WINAPI
svc_ctrl_handler(DWORD dwCtrl
, DWORD dwEventType
, LPVOID lpEventData
, LPVOID lpContext
)
159 // handle the requested control code
162 case SERVICE_CONTROL_STOP
:
163 set_svc_status(SERVICE_STOP_PENDING
, NO_ERROR
, 0);
165 b_ret
= SetEvent(h_svc_events
);
169 case SERVICE_CONTROL_INTERROGATE
:
171 case SERVICE_CONTROL_SESSIONCHANGE
:
172 if (dwEventType
== WTS_SESSION_LOGON
)
174 b_ret
= start_hook(reinterpret_cast<WTSSESSION_NOTIFICATION
*>(lpEventData
)->dwSessionId
);
179 return GetLastError();
181 else if (dwEventType
== WTS_SESSION_LOGOFF
)
183 stop_hook(reinterpret_cast<WTSSESSION_NOTIFICATION
*>(lpEventData
)->dwSessionId
);
188 return ERROR_CALL_NOT_IMPLEMENTED
;
190 return ERROR_CALL_NOT_IMPLEMENTED
;
194 VOID CALLBACK
exit_cleanup(PVOID lpParameter
, BOOLEAN TimerOrWaitFired
)
198 b_ret
= UnregisterWait(h_wait_cleanup
);
199 assert(b_ret
|| GetLastError() == ERROR_IO_PENDING
);
201 b_ret
= stop_gdipp_rpc_server();
204 const DWORD wait_ret
= WaitForSingleObject(h_rpc_thread
, INFINITE
);
205 assert(wait_ret
== WAIT_OBJECT_0
);
208 set_svc_status(SERVICE_STOPPED
, NO_ERROR
, 0);
213 h_svc_events
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
214 if (h_svc_events
== NULL
)
216 set_svc_status(SERVICE_STOPPED
, NO_ERROR
, 0);
220 // clean up when event is set
221 if (!RegisterWaitForSingleObject(&h_wait_cleanup
, h_svc_events
, exit_cleanup
, NULL
, INFINITE
, WT_EXECUTEDEFAULT
| WT_EXECUTEONLYONCE
))
223 set_svc_status(SERVICE_STOPPED
, NO_ERROR
, 0);
227 // report running status when initialization is complete
228 set_svc_status(SERVICE_RUNNING
, NO_ERROR
, 0);
230 // initialize RPC for font service
231 h_rpc_thread
= CreateThread(NULL
, 0, start_gdipp_rpc_server
, NULL
, 0, NULL
);
232 if (h_rpc_thread
== NULL
)
234 set_svc_status(SERVICE_STOPPED
, NO_ERROR
, 0);
239 service process and its child processes run in session 0
240 some functions in gdipp may require interactive session (session ID > 0)
241 use CreateProcessAsUser to create process in the active user's session
243 const DWORD active_session_id
= WTSGetActiveConsoleSessionId();
244 if (active_session_id
!= 0xFFFFFFFF)
245 start_hook(active_session_id
);
248 VOID WINAPI
svc_main(DWORD dwArgc
, LPTSTR
*lpszArgv
)
250 // register the handler function for the service
251 h_svc_status
= RegisterServiceCtrlHandlerExW(SVC_NAME
, svc_ctrl_handler
, NULL
);
252 if (h_svc_status
== NULL
)
255 // these SERVICE_STATUS members remain as set here
256 svc_status
.dwServiceType
= SERVICE_WIN32_OWN_PROCESS
;
257 svc_status
.dwWin32ExitCode
= NO_ERROR
;
259 // report initial status to the SCM
260 set_svc_status(SERVICE_START_PENDING
, NO_ERROR
, 3000);
269 int APIENTRY
wWinMain(HINSTANCE hInstance
, HINSTANCE hPrevInstance
, LPWSTR lpCmdLine
, int nCmdShow
)
275 SERVICE_TABLE_ENTRY dispatch_table
[] =
277 { SVC_NAME
, gdipp::svc_main
},
281 if (!StartServiceCtrlDispatcherW(dispatch_table
))