【编程技术- SandboxiePlus Service隔离代码分析】此文章归类为:编程技术。
最近写Sandboxieplus劫持com请求方法的例子,RpcSs和DcomLaunch都是服务类型,需要详细了解SandboxiePlus如何实现服务隔离的。即本文剖析一个问题:SandboxiePlus如何管理一个服务的。
Windows服务对应的文件有两种类型:
一个Windows服务必须存在存在两个处理逻辑:ServiceMain函数和ServiceCtrlHandler函数。如果该服务是由svchost.exe进行托管的话,这个dll必须导出ServiceMain函数,函数名不可以改变。ServiceMain函数是服务的主要逻辑代码,ServiceCtrlHandler函数主要响应来自SCM(服务控制管理器)各种控制命令,比如:STOP和SHUTDOWN。下面将使用如下代码生成服务ServiceTest.exe文件研究SandboxiePlus如何实现服务的隔离。
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 | #include <windows.h> #include <tchar.h> #include <strsafe.h> SERVICE_STATUS g_ServiceStatus = { 0 }; SERVICE_STATUS_HANDLE g_StatusHandle = NULL; HANDLE g_hStopEvent = NULL; VOID WINAPI ServiceMain( DWORD argc, LPTSTR * argv); VOID WINAPI ServiceCtrlHandler( DWORD ); DWORD WINAPI ServiceWorkerThread( LPVOID lpParam); #define SERVICE_NAME _T("SampleService") int main( int argc, char * argv[]) { SERVICE_TABLE_ENTRY DispatchTable[] = { { ( LPWSTR )SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain }, { NULL, NULL } }; if (!StartServiceCtrlDispatcher(DispatchTable)) { return GetLastError(); } return 0; } VOID WINAPI ServiceMain( DWORD argc, LPTSTR * argv) { DWORD Status = E_FAIL; HANDLE hThread = INVALID_HANDLE_VALUE; // Register our service control handler with the SCM g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler); if (g_StatusHandle == NULL) { goto EXIT; } // Tell the service controller we are starting ZeroMemory(&g_ServiceStatus, sizeof (g_ServiceStatus)); g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; g_ServiceStatus.dwControlsAccepted = 0; g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; g_ServiceStatus.dwWin32ExitCode = 0; g_ServiceStatus.dwServiceSpecificExitCode = 0; g_ServiceStatus.dwCheckPoint = 0; if (!SetServiceStatus(g_StatusHandle, &g_ServiceStatus)) { OutputDebugString(_T( "MySampleService: ServiceMain: SetServiceStatus returned error" )); } // Create stop event to wait on later g_hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (g_hStopEvent == NULL) { g_ServiceStatus.dwControlsAccepted = 0; g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; g_ServiceStatus.dwWin32ExitCode = GetLastError(); g_ServiceStatus.dwCheckPoint = 1; if (!SetServiceStatus(g_StatusHandle, &g_ServiceStatus)) { OutputDebugString(_T( "MySampleService: ServiceMain: SetServiceStatus returned error" )); } goto EXIT; } // Tell the service controller we are started g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; g_ServiceStatus.dwCurrentState = SERVICE_RUNNING; g_ServiceStatus.dwWin32ExitCode = 0; g_ServiceStatus.dwCheckPoint = 0; if (!SetServiceStatus(g_StatusHandle, &g_ServiceStatus)) { OutputDebugString(_T( "MySampleService: ServiceMain: SetServiceStatus returned error" )); } // Start the thread that will perform the main task of the service hThread = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL); if (hThread == NULL) { g_ServiceStatus.dwControlsAccepted = 0; g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; g_ServiceStatus.dwWin32ExitCode = GetLastError(); g_ServiceStatus.dwCheckPoint = 1; if (!SetServiceStatus(g_StatusHandle, &g_ServiceStatus)) { OutputDebugString(_T( "MySampleService: ServiceMain: SetServiceStatus returned error" )); } goto EXIT; } // Wait until our worker thread exits effectively signaling that the service needs to stop WaitForSingleObject(hThread, INFINITE); // Cleanup CloseHandle(hThread); CloseHandle(g_hStopEvent); // Tell the service controller we are stopped g_ServiceStatus.dwControlsAccepted = 0; g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; g_ServiceStatus.dwWin32ExitCode = 0; g_ServiceStatus.dwCheckPoint = 3; if (!SetServiceStatus(g_StatusHandle, &g_ServiceStatus)) { OutputDebugString(_T( "MySampleService: ServiceMain: SetServiceStatus returned error" )); } EXIT: return ; } VOID WINAPI ServiceCtrlHandler( DWORD Ctrl) { switch (Ctrl) { case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING) break ; g_ServiceStatus.dwControlsAccepted = 0; g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; g_ServiceStatus.dwWin32ExitCode = 0; g_ServiceStatus.dwCheckPoint = 4; if (!SetServiceStatus(g_StatusHandle, &g_ServiceStatus)) { OutputDebugString(_T( "MySampleService: ServiceCtrlHandler: SetServiceStatus returned error" )); } // This will signal the worker thread to start shutting down SetEvent(g_hStopEvent); break ; default : break ; } } DWORD WINAPI ServiceWorkerThread( LPVOID lpParam) { // 主服务逻辑在此实现 // 这是一个示例,每10秒检查一次停止事件 while (WaitForSingleObject(g_hStopEvent, 10000) != WAIT_OBJECT_0) { // 实现服务的核心功能 OutputDebugString(_T( "MySampleService: Doing stuff..." )); } return ERROR_SUCCESS; } |
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 | _FX SC_HANDLE Scm_CreateServiceW( SC_HANDLE hSCManager, WCHAR *lpServiceName, WCHAR *lpDisplayName, ULONG dwDesiredAccess, ULONG dwServiceType, ULONG dwStartType, ULONG dwErrorControl, WCHAR *lpBinaryPathName, WCHAR *lpLoadOrderGroup, WCHAR *lpdwTagId, WCHAR *lpDependencies, WCHAR *lpServiceStartName, WCHAR *lpPassword) { NTSTATUS status; SC_HANDLE hService; HANDLE hkey; UNICODE_STRING uni; WCHAR *name; // // verify the service does not exist (also verifies hSCManager, // and that the service name is not NULL) // // 检查服务是否已经存在,沙盒中的hSCManager必须是HANDLE_SERVICE_MANAGER hService = Scm_OpenServiceWImpl( hSCManager, lpServiceName, SERVICE_QUERY_STATUS); if (hService) { Scm_CloseServiceHandleImpl(hService); SetLastError(ERROR_SERVICE_EXISTS); return NULL; } if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST) return NULL; // // create a key for the new service // // 设置服务注册表内容,注册表位置HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lpServiceName hkey = Scm_OpenKeyForService(lpServiceName, TRUE); if (! hkey) { // SetLastError already called return NULL; } // Type => SERVICE_WIN32_OWN_PROCESS RtlInitUnicodeString(&uni, L "Type" ); status = NtSetValueKey( hkey, &uni, 0, REG_DWORD, &dwServiceType, sizeof ( ULONG )); if (! NT_SUCCESS(status)) goto abort ; // Start => auto RtlInitUnicodeString(&uni, L "Start" ); status = NtSetValueKey( hkey, &uni, 0, REG_DWORD, &dwStartType, sizeof ( ULONG )); if (! NT_SUCCESS(status)) goto abort ; // ErrorControl => 1, RtlInitUnicodeString(&uni, L "ErrorControl" ); status = NtSetValueKey( hkey, &uni, 0, REG_DWORD, &dwErrorControl, sizeof ( ULONG )); if (! NT_SUCCESS(status)) goto abort ; if (lpDisplayName) { // Service Test RtlInitUnicodeString(&uni, L "DisplayName" ); status = NtSetValueKey( hkey, &uni, 0, REG_SZ, lpDisplayName, (wcslen(lpDisplayName) + 1) * sizeof ( WCHAR )); if (! NT_SUCCESS(status)) goto abort ; } if (lpBinaryPathName) { // 可执行文件的路径 => D:\test\service_test_exe.exe RtlInitUnicodeString(&uni, L "ImagePath" ); status = NtSetValueKey( hkey, &uni, 0, REG_SZ, lpBinaryPathName, (wcslen(lpBinaryPathName) + 1) * sizeof ( WCHAR )); if (! NT_SUCCESS(status)) goto abort ; } if (lpServiceStartName) { RtlInitUnicodeString(&uni, L "ObjectName" ); status = NtSetValueKey( hkey, &uni, 0, REG_SZ, lpServiceStartName, (wcslen(lpServiceStartName) + 1) * sizeof ( WCHAR )); if (! NT_SUCCESS(status)) goto abort ; } // // add the service to SbieSvc list of sandboxed services // // 标记服务是一个沙盒化服务,将lpServiceName添加到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SbieSvc的SandboxedServices中 status = Scm_AddBoxedService(lpServiceName); if (! NT_SUCCESS(status)) goto abort ; NtClose(hkey); // // allocate a 'handle' that points to the service name // name = Dll_Alloc( sizeof ( ULONG ) + (wcslen(lpServiceName) + 1) * sizeof ( WCHAR )); *( ULONG *)name = tzuk; wcscpy(( WCHAR *)((( ULONG *)name) + 1), lpServiceName); _wcslwr(name); SetLastError(0); return ( SC_HANDLE )name; // // failure: delete the key for the new service // abort : NtDeleteKey(hkey); NtClose(hkey); SetLastError(ERROR_INVALID_PARAMETER); return NULL; } |
执行sc create ServiceTest binPath= "D:\test\service_test_exe.exe" DisplayName= "Service Test" start= auto
命令来创建服务,注册表方面的修改如下:
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 | _FX SC_HANDLE Scm_OpenServiceWImpl( SC_HANDLE hSCManager, const WCHAR *lpServiceName, DWORD dwDesiredAccess) { WCHAR *name; BOOLEAN ok = FALSE; if (hSCManager != HANDLE_SERVICE_MANAGER) { SetLastError(ERROR_INVALID_HANDLE); return ( SC_HANDLE )0; } if ((! lpServiceName) || (! *lpServiceName)) { SetLastError(ERROR_INVALID_PARAMETER); return ( SC_HANDLE )0; } // // open the service if we know its name, first check in the sandbox, // and if not found, outside the sandbox // Scm_DiscardKeyCache(lpServiceName); if (Scm_IsBoxedService(lpServiceName)) { // 调用NtCreateKey 或 NtOpenKey打开对应的服务 HANDLE hkey = Scm_OpenKeyForService(lpServiceName, FALSE); if (hkey) { NtClose(hkey); ok = TRUE; } } else { // 调用SbieDll_CallServer向SbieSvc服务发送MSGID_SERVICE_QUERY类型请求 SERVICE_QUERY_RPL *rpl = (SERVICE_QUERY_RPL *) Scm_QueryServiceByName(lpServiceName, FALSE, FALSE); if (rpl) { Dll_Free(rpl); ok = TRUE; } } if (! ok) { // either Scm_OpenKeyForService or Scm_QueryServiceByName // has already called SetLastError return ( SC_HANDLE )0; } // // allocate a 'handle' that points to the service name // name = Dll_Alloc( sizeof ( ULONG ) + (wcslen(lpServiceName) + 1) * sizeof ( WCHAR )); *( ULONG *)name = tzuk; wcscpy(( WCHAR *)((( ULONG *)name) + 1), lpServiceName); _wcslwr(name); SetLastError(0); return ( SC_HANDLE )name; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | _FX BOOL Scm_CloseServiceHandleImpl( SC_HANDLE hSCObject) { BOOL ok = FALSE; // 关闭Scm_OpenSCManagerW返回的结果HANDLE_SERVICE_MANAGER if (hSCObject == HANDLE_SERVICE_MANAGER) ok = TRUE; else if (Scm_GetHandleName(hSCObject)) { // 删除Scm_Notify_Global中和hSCObject相关的element,和NotifyServiceStatusChange函数相关 Scm_Notify_CloseHandle(hSCObject); Dll_Free(hSCObject); ok = TRUE; } if (ok) SetLastError(0); else SetLastError(ERROR_INVALID_HANDLE); return ok; } |
主要逻辑:首先查看是否是沙盒化服务,如果是调用SbieDll_StartBoxedService,向SbieSvc进程发送MSGID_SERVICE_RUN请求;否则调用SbieDll_CallServer函数,向SbieSvc进程发送MSGID_SERVICE_START请求,启动服务。
SbieDll_StartBoxedService对应的源码如下:
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 | _FX BOOL SbieDll_StartBoxedService( const WCHAR *ServiceName, BOOLEAN WithAdd) // WithAdd = False { HANDLE hkey; SERVICE_STATUS ss; SERVICE_QUERY_RPL *rpl; ULONG retries, error; WCHAR text[130]; Sbie_snwprintf(text, 130, L "StartBoxedService; name: '%s'" , ServiceName); SbieApi_MonitorPutMsg(MONITOR_SCM, text); // // when invoked from SandboxieRpcSs to handle StartProcess, // specify WithAdd to add the service to the sandbox // if (WithAdd) Scm_AddBoxedService(ServiceName); // // get service parameters and check that the service can be started // rpl = Scm_QueryBoxedServiceByName(ServiceName, TRUE, -1); if (! rpl) return FALSE; // 服务处于RUNNING状态,无需启动 if (rpl->service_status.dwCurrentState != SERVICE_STOPPED && rpl->service_status.dwCurrentState != SERVICE_START_PENDING) { Dll_Free(rpl); SetLastError(ERROR_SERVICE_ALREADY_RUNNING); return FALSE; } // 启动的服务是个驱动,无法启动,报错。 if (rpl->service_status.dwServiceType & SERVICE_DRIVER) { SbieApi_Log(2103, L "%S [%S] (StartService)" , ServiceName, Dll_BoxName); Dll_Free(rpl); SetLastError(ERROR_ACCESS_DENIED); return FALSE; } // 如果启动的服务不是一个exe或dll,暂时不支持,报错。 if (! (rpl->service_status.dwServiceType & SERVICE_WIN32)) { Dll_Free(rpl); SetLastError(ERROR_SERVICE_LOGON_FAILED); return FALSE; } // // set service status - start pending // hkey = Scm_OpenKeyForService(ServiceName, TRUE); if (! hkey) { error = GetLastError(); Dll_Free(rpl); SetLastError(error); return FALSE; } // // indicate the service is initializing // memzero(&ss, sizeof (SERVICE_STATUS)); ss.dwCurrentState = SERVICE_START_PENDING; ss.dwControlsAccepted = SERVICE_ACCEPT_STOP; ss.dwWaitHint = 5000; // 设置该服务的状态,其实就是设置注册表键对应的值 Scm_SetServiceStatus_Internal( hkey, HANDLE_SERVICE_STATUS, &ss, FALSE); CloseHandle(hkey); // // launch the service // error = Scm_StartBoxedService2(ServiceName, rpl); Dll_Free(rpl); // // wait for the service to indicate it has started // if (! error) { error = ERROR_SERVICE_LOGON_FAILED; for (retries = 0; retries < 40; ++retries) { Sleep(500); rpl = Scm_QueryBoxedServiceByName(ServiceName, TRUE, 0); if (! rpl) return FALSE; // 查看服务是否启动成功 if (rpl->service_status.dwCurrentState == SERVICE_RUNNING) { error = 0; break ; } Dll_Free(rpl); } } ...... SetLastError(0); return TRUE; } |
Scm_StartBoxedService2函数对应的源码如下:
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 | _FX ULONG Scm_StartBoxedService2( const WCHAR *name, SERVICE_QUERY_RPL *qrpl) { SERVICE_RUN_REQ *req; MSG_HEADER *rpl; ULONG error, path_len, req_len; WCHAR *path; BOOL free_path; // // special handling for Sandboxie services: // either run directly (for SandboxieCrypto) // or build a path to the Sandboxie executable // free_path = FALSE; // _bits -> 后台智能传输服务,_wuauserv -> Windows更新服务,Scm_CryptSvc -> 加密服务 // 这三个特殊服务特殊处理 if (_wcsicmp(name, _bits) == 0 || _wcsicmp(name, _wuauserv) == 0 || _wcsicmp(name, Scm_CryptSvc) == 0) { //PROCESS_INFORMATION pi; STARTUPINFO si; const WCHAR *ProcessName; //BOOLEAN use_sbiesvc = TRUE; if (_wcsicmp(name, _bits) == 0) { ProcessName = SandboxieBITS; Scm_DeletePermissions(L "69AD4AEE-51BE-439B-A92C-86AE490E8B30" ); } else if (_wcsicmp(name, _wuauserv) == 0) { ProcessName = SandboxieWUAU; Scm_DeletePermissions(L "653C5148-4DCE-4905-9CFD-1B23662D3D9E" ); } else if (_wcsicmp(name, Scm_CryptSvc) == 0) { ProcessName = SandboxieCrypto; //use_sbiesvc = FALSE; } else ProcessName = NULL; si.lpReserved = NULL; if (SbieDll_RunFromHome(ProcessName, NULL, &si, NULL)) { path = ( WCHAR *)si.lpReserved; if (path) free_path = TRUE; } } if (! free_path) { path = Scm_GetServiceConfigString(qrpl, 'P' ); } // // otherwise start service through SbieSvc // path_len = (wcslen(path) + 1) * sizeof ( WCHAR ); req_len = sizeof (SERVICE_RUN_REQ) + path_len; req = Dll_Alloc(req_len); req->h.length = req_len; req->h.msgid = MSGID_SERVICE_RUN; req->type = qrpl->service_status.dwServiceType; if (_wcsicmp(name, L "McAfee SiteAdvisor Service" ) == 0) { // // the McAfee SiteAdvisor Service is a WIN32_SHARE_PROCESS service // when it is part of the Total Protection suite, so we need to // make sure it will always be treated as SERVICE_WIN32_OWN_PROCESS // req->type = SERVICE_WIN32_OWN_PROCESS; } wcsncpy(req->name, name, 64); req->name[63] = L '\0' ; req->devmap[0] = L '\0' ; File_GetSetDeviceMap(req->devmap); req->path_len = path_len; memcpy (req->path, path, path_len); // 向SbieSvc发送MSGID_SERVICE_RUN请求,创建进程 rpl = (MSG_HEADER *)SbieDll_CallServer(&req->h); if (! rpl) error = ERROR_NOT_ENOUGH_MEMORY; else { error = rpl->status; Dll_Free(rpl); } // // finish // if (free_path) HeapFree(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, path); return error; } |
调用StartService之后,对应服务的注册表内容如下:
使用DebugView可以看到服务输出的内容,如下图:
启动的服务进程会被再次注入SbieDll,其对StartServiceCtrlDispatcher hook的代码如下:
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 | FX BOOL Scm_StartServiceCtrlDispatcherX( const void *ServiceTable, BOOLEAN IsUnicode) { static const WCHAR *ServiceName_EnvVar = L "00000000_" SBIE L "_SERVICE_NAME" ; WCHAR *ServiceName; WCHAR *Buffer; UNICODE_STRING uni; void *args[3]; ULONG ThreadId; HANDLE hEvent; SERVICE_STATUS ss; NTSTATUS status; IO_STATUS_BLOCK iosb; ...... // 通过环境变量获取服务的名称,设置注册表中服务的状态 // // launch ServiceMain to initialize // if (IsUnicode) { args[0] = ((SERVICE_TABLE_ENTRYW *)ServiceTable)->lpServiceProc; args[1] = ((SERVICE_TABLE_ENTRYW *)ServiceTable)->lpServiceName; args[2] = NULL; } else { args[0] = ((SERVICE_TABLE_ENTRYA *)ServiceTable)->lpServiceProc; args[1] = ((SERVICE_TABLE_ENTRYA *)ServiceTable)->lpServiceName; args[2] = NULL; } if (_wcsicmp(ServiceName, Scm_MsiServer) == 0) { Scm_IsMsiServer = TRUE; Scm_SetupMsiHooks(); } // 执行ServiceMain函数,ServiceMain函数会设置Scm_Handler或Scm_HandlerEx HANDLE ThreadHandle = CreateThread(NULL, 0, Scm_ServiceMainThread, args, 0, &ThreadId); if (ThreadHandle) CloseHandle(ThreadHandle); else Scm_Stopped = TRUE; // // main loop: wait for changes on the service key // hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (! hEvent) { SbieApi_Log(2211, ServiceName); return FALSE; } Buffer = Dll_Alloc(1024); RtlInitUnicodeString(&uni, SBIE L "_ControlCode" ); status = NtNotifyChangeKey( Scm_ServiceKey, hEvent, NULL, NULL, &iosb, REG_NOTIFY_CHANGE_LAST_SET, FALSE, Buffer, 1024, TRUE); if (!NT_SUCCESS(status)) { SbieApi_Log(2211, ServiceName); return FALSE; } // 当ServiceCtrlHandler函数设置服务的状态为STOP时,Scm_Stopped = TRUE,跳出while循环 while (! Scm_Stopped) { union { KEY_VALUE_PARTIAL_INFORMATION info; WCHAR info_space[64]; } u; ULONG len; // Wait for the reg value notification event, or 1 sec. status = WaitForSingleObject(hEvent, 1000); // 当服务所在的注册表键发生改变,进入到if中 if (NT_SUCCESS(status)) { Scm_DiscardKeyCache(ServiceName); status = NtQueryValueKey( Scm_ServiceKey, &uni, KeyValuePartialInformation, &u.info, sizeof (u), &len); // 发生控制命令给ServiceCtrlHandler函数 if (NT_SUCCESS(status) && u.info.Type == REG_DWORD && u.info.DataLength == 4) { // 执行ServiceCtrlHandler,参数是控制码 if (Scm_Handler) Scm_Handler(*( ULONG *)u.info.Data); // 执行ServiceCtrlHandler,参数是控制码 else if (Scm_HandlerEx) { Scm_HandlerEx( *( ULONG *)u.info.Data, 0, NULL, Scm_HandlerContext); } } status = NtDeleteValueKey(Scm_ServiceKey, &uni); if (NT_SUCCESS(status)) { // if key was actually deleted, we need to reissue the reg notification status = NtNotifyChangeKey( Scm_ServiceKey, hEvent, NULL, NULL, &iosb, REG_NOTIFY_CHANGE_LAST_SET, FALSE, Buffer, 1024, TRUE); } } } // // if the service never started, report error // Sbie_snwprintf(text, 130, L "StartServiceCtrlDispatcher; result: %s" , Scm_Started ? L "success" : L "failure" ); SbieApi_MonitorPutMsg(MONITOR_SCM, text); if (! Scm_Started) { SbieApi_Log(2211, ServiceName); return FALSE; } return TRUE; } |
对RegisterServiceCtrlHandler函数hook的源码主要设置Scm_Handler或Scm_HandlerEx,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | _FX SERVICE_STATUS_HANDLE Scm_RegisterServiceCtrlHandlerHelp( UCHAR *ServiceName, ULONG_PTR HandlerProc, void *Context, ULONG Flags) { if (Flags == 0) { Scm_Handler = (LPHANDLER_FUNCTION)HandlerProc; } else if (Flags == 2) { Scm_HandlerEx = (LPHANDLER_FUNCTION_EX)HandlerProc; Scm_HandlerContext = Context; } else { SbieApi_Log(2205, L "RegisterServiceCtrlHandlerHelp" ); SetLastError(ERROR_SERVICE_NOT_IN_EXE); return NULL; } return ( SERVICE_STATUS_HANDLE )HANDLE_SERVICE_STATUS; } |
该函数可产生暂停、停止和恢复服务等效果,主要逻辑:设置服务的注册表SBIE_ControlCode键实现。上文中的Scm_StartServiceCtrlDispatcherX函数设置了hEvent来监听是否设置了SBIE_ControlCode,如果设置了就调用Scm_Handler或Scm_HandlerEx,通过这种方式实现了模拟SCM发送各种控制码给ServiceCtrlHandler函数的过程。
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 | _FX BOOL Scm_ControlServiceImpl( SC_HANDLE hService, DWORD dwControl, SERVICE_STATUS *lpServiceStatus) { NTSTATUS status; WCHAR *ServiceName; HANDLE hkey; SERVICE_QUERY_RPL *rpl; UNICODE_STRING uni; ULONG val, retries; union { KEY_VALUE_PARTIAL_INFORMATION info; WCHAR info_space[64]; } u; // // get service name // ServiceName = Scm_GetHandleName(hService); if (! ServiceName) return FALSE; if (! Scm_IsBoxedService(ServiceName)) { if (dwControl == SERVICE_CONTROL_CONTINUE || dwControl == SERVICE_CONTROL_INTERROGATE) { return Scm_QueryServiceStatusImpl(hService, lpServiceStatus); } else { SetLastError(ERROR_ACCESS_DENIED); return FALSE; } } // // verify that the service is in the running or paused state // rpl = Scm_QueryBoxedServiceByName(ServiceName, TRUE, 0); if (! rpl) return FALSE; val = rpl->service_status.dwCurrentState; Dll_Free(rpl); if (val == SERVICE_STOPPED) { SetLastError(ERROR_SERVICE_NOT_ACTIVE); return FALSE; } if (val != SERVICE_RUNNING && val != SERVICE_PAUSED) { SetLastError(ERROR_SERVICE_CANNOT_ACCEPT_CTRL); return FALSE; } // // open the key for the service and transmit the control // hkey = Scm_OpenKeyForService(ServiceName, TRUE); if (! hkey) { SetLastError(ERROR_SERVICE_REQUEST_TIMEOUT); return FALSE; } RtlInitUnicodeString(&uni, SBIE L "_ControlCode" ); // val = dwControl; // 设置SBIE_ControlCode键对应的值,也就是ServiceCtrlHandler函数的Ctrl参数 status = NtSetValueKey( hkey, &uni, 0, REG_DWORD, ( BYTE *)&val, sizeof ( ULONG )); if (! NT_SUCCESS(status)) { NtClose(hkey); SetLastError(ERROR_SERVICE_REQUEST_TIMEOUT); return FALSE; } // // wait up for the value to disappear, this indicates the // service has picked up the control // // 多次循环,尝试等待Scm_Handler或Scm_HandlerEx回调函数执行完成 for (retries = 0; retries < 40; ++retries) { Scm_DiscardKeyCache(ServiceName); status = NtQueryValueKey( hkey, &uni, KeyValuePartialInformation, &u.info, sizeof (u), &val); if (NT_SUCCESS(status)) { Sleep(500); continue ; } break ; } NtClose(hkey); if (status != STATUS_OBJECT_NAME_NOT_FOUND) { SetLastError(ERROR_SERVICE_REQUEST_TIMEOUT); return FALSE; } return Scm_QueryServiceStatusImpl(hService, lpServiceStatus); } |
这些函数主要查询注册表中的信息,一些函数如下:
SubscribeServiceChangeNotifications和NotifyServiceStatusChange,这两个函数都是用于监控服务或SCM的状态改变。具体用法可以见微软的MSDN文档。这里举出两个使用情况:
对于SubscribeServiceChangeNotifications来说,SandboxiePlus对应的hook函数并没有实质的内容,目前是一个空函数,对应的代码如下:
1 2 3 4 5 6 7 8 9 10 | _FX ULONG_PTR Scm_SubscribeServiceChangeNotifications( ULONG_PTR Unknown1, ULONG_PTR Unknown2, ULONG_PTR Unknown3, ULONG_PTR Unknown4, ULONG_PTR Unknown5) { // // fake success for new unknown function in Windows 8, // SubscribeServiceChangeNotifications // return 0; } |
对于NotifyServiceStatusChange来说,SandboxiePlus对应的hook函数主要逻辑:
对应的源码如下:
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 | _FX DWORD Scm_NotifyServiceStatusChangeW( SC_HANDLE hService, DWORD dwNotifyMask, void *pNotifyBuffer) { WCHAR *ServiceNm; SCM_NOTIFY_ELEM *notify_elem; HANDLE handle; ULONG dwVersion; // // validate parameters // dwVersion = ((SERVICE_NOTIFY *)pNotifyBuffer)->dwVersion; if (dwVersion != 1 && dwVersion != 2) { SbieApi_Log(2205, L "NotifyServiceStatusChange (%d)" , dwVersion); return ERROR_INVALID_PARAMETER; } ServiceNm = Scm_GetHandleName(hService); if (! ServiceNm) return ERROR_SERVICE_DOES_NOT_EXIST; // // look for an existing notification entry // EnterCriticalSection(Scm_Notify_CritSec); if (! Scm_Notify_Global) { Scm_Notify_Global = Dll_Alloc( sizeof (SCM_NOTIFY_GLOBAL)); List_Init(&Scm_Notify_Global->list); Scm_Notify_Global->hThread = NULL; Scm_Notify_Global->hEvent = NULL; } notify_elem = List_Head(&Scm_Notify_Global->list); while (notify_elem) { if (notify_elem->hService == hService) break ; notify_elem = List_Next(notify_elem); } if (! notify_elem) { // // open a thread handle so we can call QueueUserAPC later on // handle = OpenThread(THREAD_SET_CONTEXT, FALSE, GetCurrentThreadId()); if (! handle) { ULONG LastError = GetLastError(); LeaveCriticalSection(Scm_Notify_CritSec); return LastError; } // // create a new notification entry // notify_elem = Dll_Alloc( sizeof (SCM_NOTIFY_ELEM)); notify_elem->hThread = handle; notify_elem->hService = hService; notify_elem->data = pNotifyBuffer; notify_elem->mask = dwNotifyMask; notify_elem->state = 0; notify_elem->active = TRUE; // 添加notify_elem到Scm_Notify_Global->list中 List_Insert_After(&Scm_Notify_Global->list, NULL, notify_elem); } notify_elem->active = TRUE; // // start the service watcher thread if necessary // if (! Scm_Notify_Global->hEvent) Scm_Notify_Global->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (! Scm_Notify_Global->hThread) { ULONG idThread; // 创建一个新线程,线程函数Scm_Notify_ThreadProc Scm_Notify_Global->hThread = CreateThread( NULL, 0, Scm_Notify_ThreadProc, NULL, 0, &idThread); } if (Scm_Notify_Global->hThread && Scm_Notify_Global->hEvent) SetEvent(Scm_Notify_Global->hEvent); LeaveCriticalSection(Scm_Notify_CritSec); SetLastError(0); return ERROR_SUCCESS; } |
Scm_Notify_ThreadProc函数的源码如下:
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 | _FX ULONG Scm_Notify_ThreadProc( void *lpParameter) { SCM_NOTIFY_ELEM *notify_elem; while (1) { // 无限循环 // // loop through notify handles and look for active ones // EnterCriticalSection(Scm_Notify_CritSec); notify_elem = List_Head(&Scm_Notify_Global->list); while (notify_elem) { if (notify_elem->active) { // 处理active的notify_elem Scm_Notify_ThreadProc2(notify_elem); } notify_elem = List_Next(notify_elem); } LeaveCriticalSection(Scm_Notify_CritSec); // // wait two seconds, or until the event is triggered // to indicate that a new element was added or updated // WaitForSingleObject(Scm_Notify_Global->hEvent, 2000); } return 0; } _FX void Scm_Notify_ThreadProc2(SCM_NOTIFY_ELEM *notify_elem) { SERVICE_QUERY_RPL *rpl = (SERVICE_QUERY_RPL *) Scm_QueryServiceByHandle(notify_elem->hService, TRUE, 0); if (rpl) { if (rpl->h.status == 0) { ULONG state = 0; SERVICE_STATUS_PROCESS *ss = &rpl->service_status; if (ss->dwCurrentState == SERVICE_STOPPED) state = SERVICE_NOTIFY_STOPPED; else if (ss->dwCurrentState == SERVICE_START_PENDING) state = SERVICE_NOTIFY_START_PENDING; else if (ss->dwCurrentState == SERVICE_STOP_PENDING) state = SERVICE_NOTIFY_STOP_PENDING; else if (ss->dwCurrentState == SERVICE_RUNNING) state = SERVICE_NOTIFY_RUNNING; else if (ss->dwCurrentState == SERVICE_CONTINUE_PENDING) state = SERVICE_NOTIFY_CONTINUE_PENDING; else if (ss->dwCurrentState == SERVICE_PAUSE_PENDING) state = SERVICE_NOTIFY_PAUSE_PENDING; else if (ss->dwCurrentState == SERVICE_PAUSED) state = SERVICE_NOTIFY_PAUSED; // 检查状态是否发生了改变 if ((notify_elem->mask & state) != 0 && (state != notify_elem->state)) { SERVICE_NOTIFY *data = notify_elem->data; memcpy (&data->ServiceStatus, ss, sizeof (SERVICE_STATUS_PROCESS)); data->dwNotificationStatus = ERROR_SUCCESS; if (data->dwVersion == 2) { data->dwNotificationTriggered = state; data->pszServiceNames = NULL; } notify_elem->state = state; notify_elem->active = FALSE; // 添加一个APC函数,调用注册的回调函数 QueueUserAPC(Scm_Notify_ApcProc, notify_elem->hThread, ( ULONG_PTR )data); } } Dll_Free(rpl); } } |
更多【编程技术- SandboxiePlus Service隔离代码分析】相关视频教程:www.yxfzedu.com