实现在XP中使用Remote Desktop时也能得到ClearType 效果

时间:2022-12-05 17:35:39

转载请标明是引用于 http://blog.csdn.net/chenyujing1234 

欢迎大家提出意见,一起讨论!

源码请自行到参考文章中下载。

 

参考文章<<http://www.codeproject.com/Articles/20866/ClearType-over-Remote-Desktop-in-Windows-XP>>

 

一、介绍

我所感兴趣的有以下:

1)ClearType字体在XP系统中能光滑;

2)用远程桌面在家里面工作;

3)不运行在Vista;

总结成一句是: 通过在不运行Vista(如XP)系统中用RDP实现远程桌面,并且RDP的效果能达到ClearType的效果

不幸的是当我们运行RDP来连接到你的XP机器时,用ClearType的字体光滑是无效的。我猜想XP在2001年刚出来时,微软做了决定要让RDP上的ClearType被禁止。

在2007年,然而,百万级带宽非常普遍了,这时不要用户使用ClearType的决定看起来就已经过时了。(虽然后定Vista系统中的RDP支持了ClearType,但XP还是没有)

通过WinDBG,我认为可以通过内核下的bytes来解决这些问题,我决定在XP下使用RDP上的ClearType功能。

二、应用程序概述

 这里有两部分的问题:

1、内核驱动,它在win32k.sys中对一些bytes打补丁;

2、用户模式的应用程序,它载入驱动并告诉它运行和做Patch(打补丁);

内核的部分是容易的。基本上,在win32k.sys中有代码路径像这样的(多亏了WinDBG的反汇编)

bf811387 66393550399abf  cmp     word ptr [win32k!gProtocolType (bf9a3950)],si
bf81138e 0f85c2feffff    jne     win32k!LFONTOBJ::ppfeMapFont+0x77 (bf811256)


我发现在我的调试器,gProtocolType变量根据你在控制台或通过RDP访问机器是不一样的。

在控制器时,jne 分支 没有被包含;但RDP时它有。

简单的改变(且有一个是产生期望的结果)仅用来忽略jne组件。取而代之的nops意味着这个分支没有被跟从-----非RDP代码路径将一直是包含的一个。

程序的内核部分(RdpClearType.sys)简单地做了nop 的补丁。

然而,那里有一个警告:看起来好像每次登陆都获得win32k.sys的复制并映射到它的地址空间(在一个叫“Session Space”的空间)。当我安装驱动,并且无论我是

在开机时启动或是通过服务手去去启动它,它都不能工作。我猜是因为驱动没有被服务加载

我的解决方案是创建一个用户模式的加载程序,它把进程分为两个步骤:

1、通过正常的SCM接口启动驱动(CreateService和StartService)。

在SCM的线程中我的驱动的DriverEntry代码将会被调用并执行它自己的登陆段。

这是为什么我的DriverEntry没有做真实的补丁的原因........它简单的创建了一个设备,此设备对用户模式通过调用\DosDevices\RdpClearType. 是可见的。

SC_HANDLE hSvc = CreateService(
		hScm,
		L"RdpClearType",
		L"RdpClearType",
		SERVICE_ALL_ACCESS,
		SERVICE_KERNEL_DRIVER,
		SERVICE_DEMAND_START,
		SERVICE_ERROR_NORMAL,
		driverPath.c_str(),
		NULL,
		NULL,
		NULL,
		NULL,
		NULL
		);

	if (hSvc)
	{
		SERVICE_STATUS svcStatus = { 0 };

		if (StartService(hSvc, 0, NULL))
		{
			do
			{
				Sleep(250);
				if (!QueryServiceStatus(hSvc, &svcStatus))
				{
					ShowMessage(MAKE_STRING(L"RdpClearType: ERROR calling QueryServiceStatus(): " << GetLastError() << endl));
					break;
				}
			}
			while (svcStatus.dwCurrentState != SERVICE_RUNNING);

			HANDLE hFile = CreateFileW(
				L"\\\\.\\RdpClearType",
				GENERIC_READ | GENERIC_WRITE,
				0,
				NULL,
				OPEN_EXISTING,
				FILE_ATTRIBUTE_NORMAL,
				NULL
			);

			if (hFile == INVALID_HANDLE_VALUE)
			{
				ShowMessage(MAKE_STRING(L"RdpClearType: ERROR opening handle to RdpClearType device: " << GetLastError() << endl));
			}
			else
			{
				DWORD bytes = 0;
				BOOL result = DeviceIoControl(
					hFile,
					0,
					NULL,
					0,
					NULL,
					0,
					&bytes,
					NULL
				);

				if (result)
				{
					ShowMessage(MAKE_STRING(L"RdpClearType: ClearType should now be working inside of Remote Desktop connections." << endl));
					ValidateRect(NULL, NULL);
					retval = 0;
				}
				else
				{
					ShowMessage(MAKE_STRING(L"RdpClearType: ERROR calling DeviceIoControl: " << GetLastError() << endl));
				}

				CloseHandle(hFile);
			}

			if (ControlService(hSvc, SERVICE_CONTROL_STOP, &svcStatus))
			{
				do
				{
					Sleep(250);
					if (!QueryServiceStatus(hSvc, &svcStatus))
					{
						ShowMessage(MAKE_STRING(L"RdpClearType: ERROR calling QueryServiceStatus(): " << GetLastError() << endl));
						break;
					}
				}
				while (svcStatus.dwCurrentState != SERVICE_STOPPED);
			}
			else
			{
				ShowMessage(MAKE_STRING(L"RdpClearType: ERROR stopping service: " << GetLastError() << endl));
			}
		}
		else
		{
			ShowMessage(MAKE_STRING(L"RdpClearType: ERROR starting service: " << GetLastError() << endl));
		}

		DeleteService(hSvc);
	}
	else
	{
		ShowMessage(MAKE_STRING(L"RdpClearType: ERROR creating service: " << GetLastError() << endl));
	}


 

2、在驱动已经被加载并启动后,用户模式代码打开\DosDevices\RdpClearType设备并调用DeviceIoControl。

当正在服务这次调用时,驱动的代码是运行在我登陆段的我的线程里。在我的段空间里做补丁影响win32k.sys。

3、最后调用Window API  ValidateRect引入口屏幕刷新。所有的字体现在是用ClearType来纹理了。

为了让以上容易实现,我嵌入RedpCleargType.sys驱动文件到RdpClearType.exe中的资源里。当运行RdpClearType.exe时,sys文件被解压到临时目录中

wchar_t buffer[MAX_PATH];
	ExpandEnvironmentStrings(L"%SystemDrive%", &buffer[0], sizeof(buffer) / sizeof(wchar_t));

	wstring driverPath = &buffer[0];
	driverPath.append(L"\\RdpClearType.sys");

	DeleteFile(driverPath.c_str());

	if (!SaveResource(IDR_BIN1, driverPath))
	{
		ShowMessage(MAKE_STRING(L"RdpClearType: ERROR extracting driver from exe" << endl));
		return 1;
	}

// This function will extract one of the embedded PE files in this
// executable image and save it to disk.
bool SaveResource(WORD resId, wstring destPath)
{
	HRSRC hRes = FindResource(NULL, MAKEINTRESOURCE(resId), L"BIN");
	HGLOBAL hLoadedRes = LoadResource(NULL, hRes);

	char *pRes = static_cast<char*>(LockResource(hLoadedRes));
	DWORD dwResSize = SizeofResource(NULL, hRes);

	ofstream outstream(destPath.c_str(), ios::binary);
	if (!outstream)
		return false;

	outstream.write(pRes, dwResSize);
	outstream.close();
	return true;
}

三、驱动程序概述

很多人下了工程后不知资源中的RDPClearType.sys哪里来的,其实它是由里面的Driver.c文件通过DDK环境编译得来的。

(VS2008+DDK的编译环境可参考我的文章<<Window XP驱动开发(十六) XP下新建驱动程序工程并编译的第二种方法>>)

1、DriverEntry只是建立设备和链接文件名并分配接口而已。

NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath)
{
	UNICODE_STRING deviceNameUnicodeString;
	UNICODE_STRING linkNameUnicodeString;
	NTSTATUS retval = STATUS_SUCCESS;
	int i;
	
	pDriverObject->DriverUnload  = OnUnload;

	RtlInitUnicodeString(&deviceNameUnicodeString, deviceName);
	RtlInitUnicodeString(&linkNameUnicodeString, linkName);

	retval = IoCreateDevice(
		pDriverObject,
		0,
		&deviceNameUnicodeString,
		FILE_DEVICE_UNKNOWN,
		0,
		FALSE,
		&rdpClearTypeDevice
		);
		
	if (retval == STATUS_SUCCESS)
	{
		DbgPrint("RdpClearType: device created\n");
		
		for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
		{
			pDriverObject->MajorFunction[i] = OnStubDispatch;
		}
		
		pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = RdpClearTypePatch;
		
		retval = IoCreateSymbolicLink(&linkNameUnicodeString, &deviceNameUnicodeString);
		if (NT_SUCCESS(retval))
		{
			DbgPrint("RdpClearType: symbolic link created\n");
		}
		else
		{
			// Delete the device since the symbolic link didn't take
			IoDeleteDevice(rdpClearTypeDevice);
			DbgPrint("RdpClearType: device deleted\n");
			rdpClearTypeDevice = NULL;
		}
	}
	else
	{
		DbgPrint("RdpClearType: ERROR creating device: %x\n", retval);
		rdpClearTypeDevice = NULL;
	}

	DbgPrint("RdpClearType: DriverEntry done\n");
	return retval;
}

2、给实现ClearType打补丁

它是所有工作的重点,也是实现的难点。(说句心里话真佩服老外的能力,在驱动里加入汇编)

简单地讲是从5个备选的内存中选择1个存在Byte 的地方,并对它填充nop

NTSTATUS RdpClearTypePatch(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
{
	int i = 0;
	char *pPatch1 = (char*)0xBF810BD6;
	char *pPatch2 = (char*)0xBF81138E;
	char *pPatch3 = (char*)0xBF810C0E;
	char *pPatch4 = (char*)0xBF810BF6;
	char *pPatch5 = (char*)0xBF811251;
	
	char *pPatch = 0;

	pIrp->IoStatus.Status = STATUS_SUCCESS;
	pIrp->IoStatus.Information = 0;

	DbgPrint("RdpClearType: patch starting\n");
	
	if (CheckExistingBytes(pPatch1))
		pPatch = pPatch1;
	else if (CheckExistingBytes(pPatch2))
		pPatch = pPatch2;
	else if (CheckExistingBytes(pPatch3))
		pPatch = pPatch3;
	else if (CheckExistingBytes(pPatch4))
		pPatch = pPatch4;
	else if (CheckExistingBytesAlt1(pPatch5))
		pPatch = pPatch5;

	if (pPatch)
	{
		DbgPrint("RdpClearType: applying patch at %08x\n", (int)pPatch);
	
		__asm
		{
			push eax
			mov eax, CR0
			and eax, 0FFFEFFFFh
			mov CR0, eax
			pop eax
		}

		for (i = 0; i < 6; i++)
		{
			*pPatch = (char)0x90; // nop
			pPatch++;
		}
		
		__asm
		{
			push eax
			mov eax, CR0
			or eax, NOT 0FFFEFFFFh
			mov CR0, eax
			pop eax
		}
		DbgPrint("RdpClearType: patch has been applied\n");
	}
	else
	{
		DbgPrint("RdpClearType: didn't find expected bytes in the any target addresses\n");
	}
	
	IoCompleteRequest(pIrp, IO_NO_INCREMENT);
	return STATUS_SUCCESS;
}