Windows系统服务的编写。

实验资源下载地址:点击打开链接

由于微软在Vista之后,对系统服务进行了隔离,即服务层为会话0 层,,而登录用户为1,2.。。。层,,因此,,如果需要服务层与用户进行交互,,只能绕过Session 0 层。。因此己不能进行复杂的UI操作。。

服务是整合在Microsoft Windows操作系统中的结构。服务与用户的应用程序不同,因为你可以对他们进行配置,不需要一个激活的用户登录,就可以使这些服务在系统启动的时候运行,直到系统关闭。Windows中的服务,负责所有种类的后台活动,但不包括从远程过程调用(RPC)服务到网络位置服务的用户。

一些服务可能会试图显示一些用户界面对话框,或者与用户的应用程序进行通信。那么这些服务将会遇到与Windows 7 的兼容性问题。可能你有一个服务,试图显示一个对话框,但是,可能仅仅是在任务栏中出现一个闪烁的图标。无独有偶,你的服务可能会多多少少遇到下面的一些现象。这些服务:

?              正在运行,但是没有做他们本希望去做的事情

?              正在运行,但是其他的一些进程却不能和这个服务进行通信

?              试图通过窗体消息,与用户的应用程序进行通信,但是窗体消息并没有被目标所接收到

?              在任务栏中,显示一个闪烁的图标,说明此服务希望与桌面信息进行交互

这些是Windows 7的服务的Session 0隔离机制的一些症状。它们可以大致分为两大类:

?              服务显示用户界面失败,或者仅仅显示了一个简化的用户界面

当一个服务试图去展示任何有关用户界面的元素(即使这个服务被允许与桌面信息进行交互),一个叫做Interactive services dialog detection的简单对话框也会弹出,用于提示用户。用户可以进入Session 0的安全桌面中查看服务的用户界面,但是,工作流中的干扰,使得这个变成了一个严重的应用程序兼容性问题。

?              很难实现服务和应用程序共享对象,并且这个对象将不可见

当一个对象由服务创建出来,并且允许标准应用程序访问(以标准用户权限运行),那么这个对象将不能在全局命名空间中找到(它将被Session 0 中私有)。此外,安全变更,也将保证即使对象是可见的,但它也是不能被访问的。这些将有可能影响其他的与你的服务进行交互的进程(比如普通用户的应用程序)。

真正的问题是Windows 7 服务的Session 0 隔离机制

在Windows XP, Windows Server 2003或者更早期的Windows操作系统中,所有的服务和应用程序都是运行在与第一个登录到控制台的用户得Session中。这个Session叫做Session 0。在Session 0 中一起运行服务和用户应用程序,由于服务是以高权限运行的,所以会造成一些安全风险。这些因素使得一些恶意代理利用这点,来寻找提升他们自身权限的结构。

在Windows Vista中,服务在一个叫做Session 0 的特殊Session中承载。由于应用程序运行在用户登录到系统后所创建的Session 0 之后的Session中,所以应用程序和服务也就隔离开来:第一个登录的用户在Session 1中,第二个在Session 2中,以此类推。事实上运行在不同的Session中,如果没有特别将其放入全局命名空间(并且设置了相应的访问控制配置),是不能互相传递窗体消息,共享UI元素或者共享kernel对象。下面的图例中,将进行图解:

了解更多关于Session 0 隔离机制,阅读Windows Vista 中的驱动和服务的Session 0 隔离机制的影响:http://www.microsoft.com/whdc/system/vista/services.mspx

解决方案

?              如果一个服务,需要向用户传递一条消息,那么可以使用WTSSendMessage方法。这个方法和MessageBox基本相同。它将可以给不需要复杂的UI界面的服务,一个简单而有效的解决方案,同时,由于所显示的消息对话框不能用于控制底层的服务,所以,这个解决方案也是安全的。

?              如果需要使用一个复杂的UI界面,那么可以使用CreateProcessAsUser方法,在提出请求的用户桌面中创建一个进程。

?         如果这两种交互方式都需要,那么可以使用诸如Windows Communication Foundation (WCF),.NET远程处理,命名管道或者其他的进程间通信(IPC)结构(除窗体消息之外)来跨Session通信。

?              安全通信和其他共享对象(比如,命名管道,文件地图),可以使用任意访问控制列表(DACL)来严格控制用户的权限设置。使用系统访问控制列表(SACL),来确保中低权限的进程依然可以访问由系统或高权限服务所创建的结构。

?              确保跨Session访问的kernel对象是以Global\字符串为首字母进行命名,这意味着他们是属于全局Session命名空间中的。

解决方案步骤

目前,我们已经遇到了所有的Windows服务的Session 0 隔离机制的情况,解释了什么是服务隔离,它是如何影响你的服务和应用程序的,并且提出了一些解决方案。下面的一些测试和一些操作,可以帮助你找到问题,并且解决它。

试验 #1

1.            打开进程浏览器。

a.            下载或了解更多关于进程浏览器,请在Microsoft TechNet网站参看Process Explorer Web site 。

2.            确保进程浏览器显示了所有的进程:

a.            点击 File.

b.            选择 Show processes from all users.

3.            定位到可疑的服务,并且查看它的属性:

a.            右键点击进程。

b.            选择 Properties 。

c.             导航到 Security 标签。

d.            请注意服务运行在哪个Session中(通常在Session 0),并且它的全部级别。

下面是两个进程的例子-其中一个以中级权限运行(在Session 1),另外一个则以系统权限运行(在Session 1):

 

如果你的服务是以高权限运行在Session 0中,那么它将不能直接显示UI。不过即使这样,当你与服务进行共享kernel对象或者文件的时候,你可能还会遇到一些问题。

试验 #2

1.            打开进程浏览器。

2.            确保进程浏览器显示了所有的进程:

a.            点击 File.

b.            选择 Show processes from all users.

3.            定位到可疑的服务。

4.            如果服务包含了你已知的与用户应用程序共享的对象,请在下面的Handles窗口中检查他们的句柄(使用CTRL+H查看,或者从View菜单中进入)。

a.            右键点击每一个可以的句柄,选择Properties。

b.            切换到Security标签页,查看当前用户和组是否允许使用当前句柄来访问对象。

下面的图中,展示了一个不管是否是在Session 0 中运行的系统服务中,所有人都可以访问的共享对象(“同步”权限):

下面的图中,展示了一个只允许管理员和系统组才能访问的共享对象:

5.            如果服务需要创建一个用户应用程序可以访问的文件,请依照以下步骤:

a.            打开命令行窗口:

i.              点击 Start

ii.             指向 Programs

iii.            点击 Accessories

iv.           点击  Command Prompt

b.            运行icacls工具查看文件或者目录的集合权限和DACL信息,并且修改它们。

(请在Microsoft TechNet网站查看关于icacls的文档,以获取更多信息。)

c.             运行icacls MyFile来显示叫MyFile的文件的访问控制列表。

d.            运行icacls MyFile /setintegritylevel Medium来将MyFile的集合权限级别修改为中级(这将使它可以访问大部分用户应用程序。)

工具

?              进程浏览器 –Windows进程的监视工具,可以显示进程的集合级别和对象的安全信息。

o             更多信息: http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx

o             下载: http://download.sysinternals.com/Files/ProcessExplorer.zip

?              icacls –Windows实用工具中的一个,用于更改文件系统对象的ACL和整合级别。

o             更多信息: http://technet.microsoft.com/en-us/library/cc753525.aspx

代码示例

下面包含了一些示例代码,用来演示如何:

?              使用WTSSendMessage从服务端向当前活动用户的桌面发送消息

?              当需要显示一个相对复杂的UI界面时,如何在当前活动用户的桌面创建一个进程

?              创建一个属于全局命名空间的事件,并且包含安全配置,以便于它可以被当前活动用户所访问。

使用WTSSendMessage 从服务端向当前活动用户的桌面发送消息:

void ShowMessage(LPWSTR lpszMessage, LPWSTR lpszTitle)

{

DWORD dwSession = WTSGetActiveConsoleSessionId();

DWORD dwResponse = 0;

WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, dwSession,

lpszTitle,

static_cast<DWORD>((wcslen(lpszTitle) + 1) * sizeof(wchar_t)),

lpszMessage,

static_cast<DWORD>((wcslen(lpszMessage) + 1) * sizeof(wchar_t)),

0, 0, &dwResponse, FALSE);

}

当需要显示一个相对复杂的UI 界面时,如何在当前活动用户的桌面创建一个进程:

BOOL bSuccess = FALSE;

STARTUPINFO si = {0};

PROCESS_INFORMATION pi = {0};

si.cb = sizeof(si);

DWORD dwSessionID = WTSGetActiveConsoleSessionId();

HANDLE hToken = NULL;

if (WTSQueryUserToken(dwSessionID, &hToken) == FALSE)

{

goto Cleanup;

}

HANDLE hDuplicatedToken = NULL;

if (DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hDuplicatedToken) == FALSE)

{

goto Cleanup;

}

LPVOID lpEnvironment = NULL;

if (CreateEnvironmentBlock(&lpEnvironment, hDuplicatedToken, FALSE) == FALSE)

{

goto Cleanup;

}

WCHAR lpszClientPath[MAX_PATH];

if (GetModuleFileName(NULL, lpszClientPath, MAX_PATH) == 0)

{

goto Cleanup;

}

PathRemoveFileSpec(lpszClientPath);

wcscat_s(lpszClientPath, sizeof(lpszClientPath)/sizeof(WCHAR), L"\\TimeServiceClient.exe");

if (CreateProcessAsUser(hDuplicatedToken, lpszClientPath, NULL, NULL, NULL, FALSE,

NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT,

lpEnvironment, NULL, &si, &pi) == FALSE)

{

goto Cleanup;

}

CloseHandle(pi.hProcess);

CloseHandle(pi.hThread);

bSuccess = TRUE;

Cleanup:

if (!bSuccess)

{

ShowMessage(L"An error occurred while creating fancy client UI", L"Error");

}

if (hToken != NULL)

CloseHandle(hToken);

if (hDuplicatedToken != NULL)

CloseHandle(hDuplicatedToken);

if (lpEnvironment != NULL)

DestroyEnvironmentBlock(lpEnvironment);

创建一个属于全局命名空间的事件,并且包含安全配置,以便于它可以被当前活动用户所访问(从DACL 和SACL ):

DWORD dwSessionID = WTSGetActiveConsoleSessionId();

HANDLE hToken = NULL;

if (WTSQueryUserToken(dwSessionID, &hToken) == FALSE)

{

goto Cleanup;

}

DWORD dwLength;

TOKEN_USER* account = NULL;

if (GetTokenInformation(hToken, TokenUser, NULL, 0,& dwLength) == FALSE &&

GetLastError() != ERROR_INSUFFICIENT_BUFFER)

{

goto Cleanup;

}

account = (TOKEN_USER*)new BYTE[dwLength];

if (GetTokenInformation(hToken, TokenUser, (LPVOID)account, dwLength, &dwLength) == FALSE)

{

goto Cleanup;

}

LPWSTR lpszSid = NULL;

if (ConvertSidToStringSid(account->User.Sid,& lpszSid) == FALSE)

{

goto Cleanup;

}

WCHAR sddl[1000];

wsprintf(sddl, L"O:SYG:BAD:(A;;GA;;;SY)(A;;GA;;;%s)S:(ML;;NW;;;ME)", lpszSid);

PSECURITY_DESCRIPTOR sd = NULL;

if (ConvertStringSecurityDescriptorToSecurityDescriptor(sddl, SDDL_REVISION_1,& sd, NULL) == FALSE)

{

goto Cleanup;

}

SECURITY_ATTRIBUTES sa;

sa.bInheritHandle = FALSE;

sa.lpSecurityDescriptor = sd;

sa.nLength = sizeof(sa);

g_hAlertEvent = CreateEvent(&sa, FALSE, FALSE, L"Global\\AlertServiceEvent");

if (g_hAlertEvent == NULL)

{

goto Cleanup;

}

while (!g_Stop)

{

Sleep(5000);

SetEvent(g_hAlertEvent);

}

Cleanup:

if (hToken != NULL)

CloseHandle(hToken);

if (account != NULL)

delete[] account;

if (lpszSid != NULL)

LocalFree(lpszSid);

if (sd != NULL)

LocalFree(sd);

if (g_hAlertEvent == NULL)

CloseHandle(g_hAlertEvent);

而同样的,,如果需要用共享内存进行与桌面的进程通信的话,,在创建共享内存过程中,也需要进行权限的设置。。代码如下。。

此为服务内建立共享内存

		HANDLE hFileMap;
		LPBYTE pcMap;
		//PSECURITY_DESCRIPTOR securityDescriptorPointer1 = NULL;
		//ServiceContext       serviceContext1 = {0};

		//serviceContext1.StatusHandle = RegisterServiceCtrlHandlerEx(argv[0], ControlHandlerEx, &serviceContext1);
		//if (!serviceContext1.StatusHandle) {
		//	goto cleanup;
		//}

		//	if (!ConvertStringSecurityDescriptorToSecurityDescriptor(L"D:(A;;0x00000002;;;IU)(A;;GA;;;LS)", SDDL_REVISION_1, & securityDescriptorPointer1, NULL)) {
		//		goto cleanup;
		//	}
		//	SECURITY_ATTRIBUTES sa1 = {sizeof(SECURITY_ATTRIBUTES), securityDescriptorPointer1, FALSE};
		//	WriteToLog((char*)&sa1);
		SECURITY_DESCRIPTOR secutityDese;
		::InitializeSecurityDescriptor(&secutityDese, SECURITY_DESCRIPTOR_REVISION);
		::SetSecurityDescriptorDacl(&secutityDese,TRUE,NULL,FALSE);
		SECURITY_ATTRIBUTES securityAttr;
		securityAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
		securityAttr.bInheritHandle = FALSE;
		securityAttr.lpSecurityDescriptor = &secutityDese;
// 		hMapping = CreateFileMappingW(INVALID_HANDLE_VALUE,&securityAttr,PAGE_READWRITE,0,0x100,L"Global\\z22");

		hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE,&securityAttr/*&sa1*/, PAGE_READWRITE, 0, 0x100, L"Global\\ShareData");
		if(hFileMap == NULL)
		{
	 		WriteToLog("CreateFileMapping shibai");
			return ;
		}
		// 	while(1)
		// 	{
		// 		WriteToLog("CreateFileMapping shibai");
		//
		// 	}
		pcMap = (LPBYTE)MapViewOfFile(hFileMap, FILE_MAP_READ|FILE_MAP_WRITE,0, 0, 0);
		if(pcMap == NULL)
		{
	 		WriteToLog("MapViewOfFile shibai");
			return ;
		}
		int i = 0;
		for(i = 0; i < len; i++)
		{
			pcMap[i] = IpaPath[i];
		}
		pcMap[i] = 0;
	 	WriteToLog((char*)pcMap);
		UnmapViewOfFile(pcMap);

		LPBYTE Get;
		Get = (LPBYTE)MapViewOfFile(hFileMap,FILE_MAP_READ,0,0,0);
	 	WriteToLog((char*)Get);
		//	CloseHandle(hFileMap);

		// 	delete []pChStr;



Windows系统服务的编写。

时间: 2024-08-06 07:37:05

Windows系统服务的编写。的相关文章

MongoDB安装,启动,注册为windows系统服务

MongoDB安装与启动 周建旭 2014-08-10 解压完后配置环境变量 下载Windows 32-bit或64-bit版本并解压缩,程序文件都在bin目录中,其它两个目录分别是C++调用是的头文件和库文件. bin目录中包含如下几个程序: 1. mongo.exe,命令行客户端工具. 2. mongod.exe,数据库服务程序. 3. mongodump.exe,数据库备份程序. 4. mongoexport.exe,数据导出工具. 5. mongofiles.exe,GridFS工具.

windows后台服务程序编写

Windows后台服务程序编写 1. 为什么要编写后台服务程序 工作中有一个程序需要写成后台服务的形式,摸索了一下,跟大家分享. 在windows操作系统中后台进程被称为 service. 服务是一种应用程序类型,它在后台运行,通常没有交互界面.服务应用程序通常可以 在本地和通过网络为用户提供一些功能,是为其它进程服务的.通过将程序注册为服务,可以使自己的程序随系统启动而最先运行,随系统关闭而最后停止.也可以windows服务管理器手动控制服务的启动.关闭. 2. 编写后台服务程序步骤 Wind

Windows Shell Code编写中级拔高

标 题: Windows Shell Code编写中级拔高 时 间: 2013-05-28,23:59:54 一.基础知识&基本原理&一些思想方法 关于Windows Shell Code的文章论坛里的介绍也不少了,不过授之以鱼不如授之以渔,虽然有部分文章也做了大量讲解,但是几乎都是对照一份现成的代码来介绍为何那样写,为何这样写.所以如果Windows系统原理的知识相对薄弱的同学看起来还是云里雾里,也没弄懂,干脆把代码照抄走了,结果会遇到各种意想不到的异常,这篇文章叫中级拔高,而没有叫高级

Windows 系统服务管理

/* 介绍:对Windows系统服务的状态获取,服务暂停,开启,停止操作代码 */ void CStartServiceDlg::OnBnClickedButton1() { // 打开服务管理对象 SC_HANDLE hSC = ::OpenSCManager( NULL, NULL, GENERIC_EXECUTE); if( hSC == NULL) { TRACE( "open SCManager error"); return; } // 打开服务. SC_HANDLE hS

枚举Windows系统服务,通过进程ID取服务名

 //枚举Windows系统服务 //使用到的函数以及MSDN的说明如下: //1.OpenSCManager说明 //http://msdn.microsoft.com/en-us/library/windows/desktop/ms684323(v=vs.85).aspx //2.EnumServicesStatusEx说明 //http://msdn.microsoft.com/en-us/library/windows/desktop/ms682640(v=vs.85).aspx /

VC Windows系统服务创建代码

Windows系统服务创建步骤,常用类封装,废话不多说,直接上代码 1 // ServceDemo.cpp : 定义控制台应用程序的入口点. 2 // 3 4 #include "stdafx.h" 5 #include "WindwosService.h" 6 7 unsigned __stdcall SvcRun (void *Params ) 8 { 9 while(1) 10 { 11 12 for (int i = 0;i < 100;i++) 13

WebLogic12c 注册windows系统服务

1.确认操作系统环境变量中的JAVA_HOME=D:\Oracle\Middleware\jdk160_29与安装部署的位置保持一致: 2.编辑D:\Oracle\Middleware\wlserver_12.1\server\bin\installSvc.cmd文件,增加标红部分内容: if "%WL_USE_AMD64DLL%" == "true" set EXTRAPATH=%WL_HOME%\server\native\win\x64\;%WL_HOME%\

Java程序注册为Windows系统服务

前段时间,总监提出将数据库中的数据通过接口发送给客户方.需要将Java程序注册为Windows系统服务,定时传送数据.总监提出用.NET写一个系统服务,将数据传送给客户是个不错的解决办法,出于对.NET了解甚少的无奈,没有选择这个解决方案.费了九牛二虎之力,终于找到了如何将Java程序注册为Windows系统服务. 话不多说,直接上方案: 一.下载第三方工具:Windows Service Wrapper(下载地址:https://github.com/kohsuke/winsw/relases

C#开发奇技淫巧一:调试windows系统服务

原文:C#开发奇技淫巧一:调试windows系统服务 windows系统服务不能直接运行,只能在安装完服务之后启动.暂停.继续.停止服务,导致服务的调试不能使用一般的断点调试. 要调试系统服务,可以采用附加到进程的方式:在VS的工具栏中找到“调试”这个选项,然后选取“附加到进程” 然后选择相应的服务附加,再启动服务即可调试(前提是已经在服务的代码中打上了断点) 不过以上方法不能调试OnStart方法,即服务的启动并不能被调试到,这个时候,需要在OnStart方法中加上Debugger.Launc