VNC源码研究(十)XP、Win7实现模拟发送ATL+CTRL+DEL

1、vnc-4.0-winsrc  版本中实现模拟发送ATL+CTRL+DEL

在工程wrfb_win32m中找到模拟发送ATL+CTR_DEL 的代码

 

在Service.h中有

[cpp]  view plain copy
  1. // -=- Routines used by the SInput Keyboard class to emulate Ctrl-Alt-Del  
  2.   //     Returns false under Win9x  
  3.   bool emulateCtrlAltDel();  


它的实现是:

[cpp]  view plain copy
  1. bool  
  2. rfb::win32::emulateCtrlAltDel() {  
  3.   if (!osVersion.isPlatformNT)  
  4.     return false;  
  5.   
  6.   CADThread* cad_thread = new CADThread();  
  7.   vlog.debug("emulate Ctrl-Alt-Del");  
  8.   if (cad_thread) {  
  9.     cad_thread->start();  
  10.     cad_thread->join();  
  11.     bool result = cad_thread->result;  
  12.     delete cad_thread;  
  13.     return result;  
  14.   }  
  15.   return false;  
  16. }  

(1)首先判断是不是NT家族的操作系统

OSVersionInfo继续于系统的结构体OSVERSIONINFO(这样的设计是为了考虑到后面要调用系统API:GetVersionEx)

[cpp]  view plain copy
  1. namespace rfb {  
  2.   
  3.   namespace win32 {  
  4.   
  5.     extern struct OSVersionInfo : OSVERSIONINFO {  
  6.       OSVersionInfo();  
  7.   
  8.       // Is the OS one of the NT family (NT 3.51, NT4.0, 2K, XP, etc.)?  
  9.       bool isPlatformNT;  
  10.       // Is one of the Windows family?  
  11.       bool isPlatformWindows;  
  12.   
  13.       // Is this OS one of those that blue-screens when grabbing another desktop (NT4 pre SP3)?  
  14.       bool cannotSwitchDesktop;  
  15.   
  16.     } osVersion;  
  17.   
  18.   };  
  19.   
  20. };  

它的系统信息在OSVersionInfo构造函数时获得:

[cpp]  view plain copy
  1. OSVersionInfo::OSVersionInfo() {  
  2.   // Get OS Version Info  
  3.   ZeroMemory(static_cast<OSVERSIONINFO*>(this), sizeof(this));  
  4.   dwOSVersionInfoSize = sizeof(OSVERSIONINFO);  
  5.   if (!GetVersionEx(this))  
  6.     throw rdr::SystemException("unable to get system version info", GetLastError());  
  7.   
  8.   // Set the special extra flags  
  9.   isPlatformNT = dwPlatformId == VER_PLATFORM_WIN32_NT;  
  10.   isPlatformWindows = dwPlatformId == VER_PLATFORM_WIN32_WINDOWS;  
  11.   
  12.   cannotSwitchDesktop = isPlatformNT && (dwMajorVersion==4) &&  
  13.     ((_tcscmp(szCSDVersion, _T("")) == 0) ||  
  14.      (_tcscmp(szCSDVersion, _T("Service Pack 1")) == 0) ||  
  15.      (_tcscmp(szCSDVersion, _T("Service Pack 2")) == 0));  
  16.   
  17. }  

(2)创建CADThread对象

[cpp]  view plain copy
  1. CADThread* cad_thread = new CADThread();  

CADThread类继续自Thread
Thread的设计是值得我们学习的。

[cpp]  view plain copy
  1. class Thread {  
  2.   public:  
  3.     Thread(const char* name_=0);  
  4.     virtual ~Thread();  
  5.   
  6.     virtual void run();  
  7.   
  8.     virtual void start();  
  9.     virtual Thread* join();  
  10.   
  11.     const char* getName() const;  
  12.     ThreadState getState() const;  
  13.   
  14.     // Determines whether the thread should delete itself when run() returns  
  15.     // If you set this, you must NEVER call join()!  
  16.     void setDeleteAfterRun() {deleteAfterRun = true;};  
  17.   
  18.     unsigned long getThreadId() const;  
  19.   
  20.     static Thread* self();  
  21.   
  22.     friend class Condition;  
  23.   
  24.   protected:  
  25.     Thread(HANDLE thread_, DWORD thread_id_);  
  26.     static DWORD WINAPI threadProc(LPVOID lpParameter);  
  27.   
  28.     HANDLE thread;  
  29.     DWORD thread_id;  
  30.     char* name;  
  31.     ThreadState state;  
  32.     Condition* sig;  
  33.     Mutex mutex;  
  34.   
  35.     HANDLE cond_event;  
  36.       Thread* cond_next;  
  37.   
  38.     bool deleteAfterRun;  
  39.   };  


CADThread重载了Thread类的run函数,CADThread的run函数先保存原有的桌面,然后切换到Winlogon桌面,然后发送ATL_CTRL_DEL,

最后把桌面还原。

[cpp]  view plain copy
  1. class CADThread : public Thread {  
  2. public:  
  3.   CADThread() : Thread("CtrlAltDel Emulator"), result(false) {}  
  4.   virtual void run() {  
  5.       HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId());  
  6.   
  7.     if (switchToDesktop(OpenDesktop(_T("Winlogon"), 0, FALSE, DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |  
  8.           DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |  
  9.           DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |  
  10.       DESKTOP_SWITCHDESKTOP | GENERIC_WRITE))) {  
  11.         PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, MAKELONG(MOD_ALT | MOD_CONTROL, VK_DELETE));  
  12.       switchToDesktop(old_desktop);  
  13.       result = true;  
  14.     }  
  15.   }  
  16.   bool result;  
  17. };  


 

(3)使用(2)得到的对象调用功能

[cpp]  view plain copy
  1. cad_thread->start();  
  2.     cad_thread->join();  


分别调用了start函数与join函数。有人可能会问,(2)定义的run接口怎么没调用到,其实是有的。

因为在Thread类的构造时就启动了此线程:

[cpp]  view plain copy
  1. Thread::Thread(const char* name_) : sig(0), deleteAfterRun(false) {  
  2.   sig = new Condition(mutex);  
  3.   cond_event = CreateEvent(NULL, TRUE, FALSE, NULL);  
  4.   if (!name_)  
  5.     name_ = "Unnamed";  
  6.   name = strDup(name_);  
  7.   thread = CreateThread(NULL, 0, threadProc, this, CREATE_SUSPENDED, &thread_id);  
  8.   state = ThreadCreated;  
  9.   logAction(this"created");  
  10. }  


 

Thread::threadProc中就会调用到它:


 

2、tightvnc  客户端版本中实现模拟发送ATL+CTRL+DEL

在我的文章《XP、Wn7模拟发送ctrl+alt+delete组合键》中讲到在win7中实现模拟ATL+CTRL+DEL的方法,虽然我已经改在了会话1中发送窗口消息,可是还是没达到效果。

网友建议我去查看VNC代码,由于tightvnc代码是支持Win7 的,于是希望在这里找到解决方法。

在win-system工程中有Environment.h文件定义有类:

[cpp]  view plain copy
  1. #ifndef _ENVIRONMENT_H_  
  2. #define _ENVIRONMENT_H_  
  3.   
  4. #include "util/StringStorage.h"  
  5.   
  6. #include <winnt.h>  
  7.   
  8. class Environment  
  9. {  
  10. public:  
  11.   static const int APPLICATION_DATA_SPECIAL_FOLDER = 0x0;   
  12.   static const int COMMON_APPLICATION_DATA_SPECIAL_FOLDER = 0x1;  
  13. public:  
  14.   Environment();  
  15.   ~Environment();  
  16.   
  17.   static void getErrStr(StringStorage *out);  
  18.   
  19.   static void getErrStr(const TCHAR *specification, StringStorage *out);  
  20.   
  21.   static bool getSpecialFolderPath(int specialFolderId, StringStorage *out);  
  22.   
  23.   static bool getCurrentModulePath(StringStorage *out);  
  24.   
  25.   static bool getCurrentModuleFolderPath(StringStorage *out);  
  26.   
  27.   static bool getCurrentUserName(StringStorage *out);  
  28.   
  29.   static bool getComputerName(StringStorage *out);  
  30.   
  31.   static void restoreWallpaper();  
  32.   static void disableWallpaper();  
  33.   
  34.   static bool isWinNTFamily();  
  35.   static bool isWinXP();  
  36.   static bool isWin2003Server();  
  37.   static bool isVistaOrLater();  
  38.   
  39.   static void simulateCtrlAltDel();  
  40.   
  41. private:  
  42.   static void init();  
  43.   static OSVERSIONINFO m_osVerInfo;  
  44. };  
  45.   
  46. #endif  


其中有 static void simulateCtrlAltDel();接口

[cpp]  view plain copy
  1. void Environment::simulateCtrlAltDel()  
  2. {  
  3.   if (isWinNTFamily()) {  
  4.     CtrlAltDelSimulator cadSim;  
  5.     cadSim.wait();  
  6.   }  
  7. }  


vnc-4.0-winsrc  版本的CADThread类继承于Thread类类似,这里的CtrlAltDelSimulator类也继续Thread。

同样在thread类的构造时就启动了此线程:

[cpp]  view plain copy
  1. Thread::Thread()  
  2. : m_terminated(false), m_active(false), m_hDesk(0)  
  3. {  
  4.   m_hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) threadProc,  
  5.                            (LPVOIDthis, CREATE_SUSPENDED, (LPDWORD) &m_threadID);  
  6.   m_hDesk = DesktopSelector::getInputDesktop();  
  7. }  


线程里会去调用execute方法:

[cpp]  view plain copy
  1. DWORD WINAPI Thread::threadProc(LPVOID pThread)  
  2. {  
  3.   Thread *_this = ((Thread *)pThread);  
  4.   try {  
  5.     DesktopSelector::setDesktopToCurrentThread(_this->m_hDesk);  
  6.     DesktopSelector::closeDesktop(_this->m_hDesk);  
  7.     _this->m_hDesk = 0;  
  8.     _this->execute();  
  9.   } catch (Exception &e) {  
  10.     Log::error(_T("Abnormal thread termination.")  
  11.                _T(" ThreadId = %x, message = \"%s\" \n"),  
  12.                _this->m_threadID, e.getMessage());  
  13.   }  
  14.   _this->m_active = false;  
  15.   return 0;  
  16. }  

CtrlAltDelSimulator类重载了execute方法:

[cpp]  view plain copy
  1. void CtrlAltDelSimulator::execute()  
  2. {  
  3.   if (DesktopSelector::selectDesktop(&StringStorage(_T("Winlogon")))) {  
  4.     HWND hwndCtrlAltDel = FindWindow(_T("SAS window class"), _T("SAS window"));  
  5.     if (hwndCtrlAltDel == NULL) {  
  6.       hwndCtrlAltDel = HWND_BROADCAST;  
  7.     }  
  8.     PostMessage(hwndCtrlAltDel, WM_HOTKEY, 0, MAKELONG(MOD_ALT | MOD_CONTROL, VK_DELETE));  
  9.   }  
  10. }  

 

往窗口发送消息是与VNC4.0版本是类似的,这里只讲selectDesktop的不同。

 

[cpp]  view plain copy
  1. bool DesktopSelector::selectDesktop(const StringStorage *name)  
  2. {  
  3.   HDESK desktop;  
  4.   if (name) {  
  5.     desktop = getDesktop(name);  
  6.   } else {  
  7.     desktop = getInputDesktop();  
  8.   }  
  9.   
  10.   bool result = setDesktopToCurrentThread(desktop) != 0;  
  11.   closeDesktop(desktop);  
  12.   
  13.   return result;  
  14. }  


 

[cpp]  view plain copy
  1. HDESK DesktopSelector::getDesktop(const StringStorage *name)  
  2. {  
  3.   return OpenDesktop(name->getString(), 0, TRUE,  
  4.                      DESKTOP_CREATEMENU |  
  5.                      DESKTOP_CREATEWINDOW |  
  6.                      DESKTOP_ENUMERATE |  
  7.                      DESKTOP_HOOKCONTROL |  
  8.                      DESKTOP_WRITEOBJECTS |  
  9.                      DESKTOP_READOBJECTS |  
  10.                      DESKTOP_SWITCHDESKTOP |  
  11.                      GENERIC_WRITE);  
  12. }  


 

[cpp]  view plain copy
  1. bool DesktopSelector::setDesktopToCurrentThread(HDESK newDesktop)  
  2. {  
  3.   return SetThreadDesktop(newDesktop) != 0;  
  4. }  

3、VNC服务端发送模拟CTRL+ATL+DEL的方法

3、1 在Vista版本前

 参考我的文章《XP、Wn7模拟发送ctrl+alt+delete组合键

 

3、2 在Vista版本后

同样是在Environment.h里的class Environment类中有:

[plain]  view plain copy
  1. static bool isWinNTFamily();  
  2. static bool isWinXP();  
  3. static bool isWin2003Server();  
  4. static bool isVistaOrLater();  
  5.   
  6. static void simulateCtrlAltDel();  
  7.   
  8. static void simulateCtrlAltDelUnderVista();  

win7下的函数就是:simulateCtrlAltDelUnderVista

[cpp]  view plain copy
  1. void Environment::simulateCtrlAltDelUnderVista()  
  2. {  
  3.   Log::info(_T("Requested Ctrl+Alt+Del simulation under Vista or later"));  
  4.   
  5.   try {  
  6.     DynamicLibrary sasLib(_T("sas.dll"));  
  7.     SendSas sendSas = (SendSas)sasLib.getProcAddress("SendSAS");  
  8.     if (sendSas == 0) {  
  9.       throw Exception(_T("The SendSAS function has not been found"));  
  10.     }  
  11.     sendSas(FALSE);   
  12.   } catch (Exception &e) {  
  13.     Log::error(_T("The simulateCtrlAltDelUnderVista() function failed: %s"),  
  14.                e.getMessage());  
  15.   }  
  16. }  


实现思路:

(1)从system32文件夹下的sas.dll里得到SendSAS接口,

(2)然后调用此接口sendSas(FALSE);

使用注意事项:

(1)

SendSAS function

Simulates a secure attention sequence (SAS).

[cpp]  view plain copy
  1. VOID WINAPI SendSAS(  
  2.   _In_  BOOL AsUser  
  3. );  


 

Parameters

AsUser [in]

TRUE if the caller is running as the current user; otherwise,FALSE.

Remarks

为了调用SendSAS成功,应用必然以服务方式运行或有uiAccess属性(用requestedExceptionLevel函数设置为true)。

如果不是以服务方式运行,那么得把User Account Control打开。

Important  Applications with the uiAccess attribute set to "true" must be signed by usingAuthenticode. In addition, the application must reside in a protected location in the file system. Currently, there are two allowable protected locations:

\Program Files\
\windows\system32\

本地的安全策略必须配置为允许服务和应用程序模拟SAS。

为了配置策略,修改组策略编辑器(GPE)中的设置。

控制 委托的GPE是在下面的位置:

Computer Configuration | Administrative Templates | Windows Components | Windows Logon Options | Disable or enable software Secure Attention Sequence

A service can impersonate the token of another process that calls that service. In this case, a call to theSendSAS function by that service simulates a SAS on the session associated with the impersonated token.

以上策略对应的注册表是:

reg add

 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v

SoftwareSASGeneration /t REG_DWORD /d 1 /f

即增加以下注册表:

[plain]  view plain copy
  1. [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System]  
  2. "SoftwareSASGeneration"=dword:00000003  


 

 

(2)我把它放在Win7类型于GINA的凭证COM里(且是以起一线程的方式调用),发现没有效果。

这是因为SendSAS只能在服务中才起作用,而在凭据中是不能起作用的。

(3) 

 

 那么simulateCtrlAltDelUnderVista在哪里被调用呢?

[cpp]  view plain copy
  1. void SasUserInput::setKeyboardEvent(UINT32 keySym, bool down)  
  2. {  
  3.   bool delPressed = false;  
  4.   
  5.   if (m_underVista) {  
  6.     switch (keySym) {  
  7.     case XK_Alt_L:  
  8.     case XK_Alt_R:  
  9.       m_altPressed = down;  
  10.       break;  
  11.     case XK_Control_L:  
  12.     case XK_Control_R:  
  13.       m_ctrlPressed = down;  
  14.       break;  
  15.     case XK_Delete:  
  16.       delPressed = down;  
  17.     }  
  18.   }  
  19.   
  20.   if (m_ctrlPressed && m_altPressed && delPressed && m_underVista) {  
  21.     Environment::simulateCtrlAltDelUnderVista();  
  22.   } else {  
  23.     m_client->setKeyboardEvent(keySym, down);  
  24.   }  
  25. }  


我们再来看setKeyboardEvent在哪里被调用了:

(1)

[cpp]  view plain copy
  1. void UserInputServer::applyKeyEvent(BlockingGate *backGate)  
  2. {  
  3.   UINT32 keySym;  
  4.   bool down;  
  5.   readKeyEvent(&keySym, &down, backGate);  
  6.   m_userInput->setKeyboardEvent(keySym, down);  
  7. }  

applyKeyEvent调用readKeyEvent读出键值和按下或弹上的标志,然后再调用setKeyboardEvent。

applyKeyEvent的实现过程:

[cpp]  view plain copy
  1. void DesktopServerProto::readKeyEvent(UINT32 *keySym, bool *down,  
  2.                                       BlockingGate *gate)  
  3. {  
  4.   *keySym = gate->readUInt32();  
  5.   *down = gate->readUInt8() != 0;  
  6. }  



 

(2)

[cpp]  view plain copy
  1. void RfbClient::onKeyboardEvent(UINT32 keySym, bool down)  
  2. {  
  3.   m_desktop->setKeyboardEvent(keySym, down);  
  4. }  


 

(3)

[cpp]  view plain copy
  1. void WinDesktop::setKeyboardEvent(UINT32 keySym, bool down)  
  2. {  
  3.   Log::info(_T("set keyboard event (keySym = %u, down = %d)"), keySym, (int)down);  
  4.   try {  
  5.     if (isRemoteInputAllowed()) {  
  6.       m_userInput->setKeyboardEvent(keySym, down);  
  7.     }  
  8.   } catch (Exception &e) {  
  9.     Log::error(_T("setKeyboardEvent() crashed: %s"), e.getMessage());  
  10.     m_extDeskTermListener->onAbnormalDesktopTerminate();  
  11.   }  
  12. }  


 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值