转载请标明是引用于 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; }