全部博文(68)
分类: Windows平台
2020-08-18 19:41:24
1、vnc-4.0-winsrc 版本中实现模拟发送ATL+CTRL+DEL
在工程wrfb_win32m中找到模拟发送ATL+CTR_DEL 的代码
在Service.h中有
-
// -=- Routines used by the SInput Keyboard class to emulate Ctrl-Alt-Del
-
// Returns false under Win9x
-
bool emulateCtrlAltDel();
它的实现是:
-
bool
-
rfb::win32::emulateCtrlAltDel() {
-
if (!osVersion.isPlatformNT)
-
return false;
-
-
CADThread* cad_thread = new CADThread();
-
vlog.debug("emulate Ctrl-Alt-Del");
-
if (cad_thread) {
-
cad_thread->start();
-
cad_thread->join();
-
bool result = cad_thread->result;
-
delete cad_thread;
-
return result;
-
}
-
return false;
-
}
(1)首先判断是不是NT家族的操作系统
OSVersionInfo继续于系统的结构体OSVERSIONINFO(这样的设计是为了考虑到后面要调用系统API:GetVersionEx)
-
namespace rfb {
-
-
namespace win32 {
-
-
extern struct OSVersionInfo : OSVERSIONINFO {
-
OSVersionInfo();
-
-
// Is the OS one of the NT family (NT 3.51, NT4.0, 2K, XP, etc.)?
-
bool isPlatformNT;
-
// Is one of the Windows family?
-
bool isPlatformWindows;
-
-
// Is this OS one of those that blue-screens when grabbing another desktop (NT4 pre SP3)?
-
bool cannotSwitchDesktop;
-
-
} osVersion;
-
-
};
-
-
};
它的系统信息在OSVersionInfo构造函数时获得:
-
OSVersionInfo::OSVersionInfo() {
-
// Get OS Version Info
-
ZeroMemory(static_cast
(this), sizeof(this));
-
dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
-
if (!GetVersionEx(this))
-
throw rdr::SystemException("unable to get system version info", GetLastError());
-
-
// Set the special extra flags
-
isPlatformNT = dwPlatformId == VER_PLATFORM_WIN32_NT;
-
isPlatformWindows = dwPlatformId == VER_PLATFORM_WIN32_WINDOWS;
-
-
cannotSwitchDesktop = isPlatformNT && (dwMajorVersion==4) &&
-
((_tcscmp(szCSDVersion, _T("")) == 0) ||
-
(_tcscmp(szCSDVersion, _T("Service Pack 1")) == 0) ||
-
(_tcscmp(szCSDVersion, _T("Service Pack 2")) == 0));
-
-
}
(2)创建CADThread对象
CADThread* cad_thread = new CADThread();
CADThread类继续自Thread
Thread的设计是值得我们学习的。
-
class Thread {
-
public:
-
Thread(const char* name_=0);
-
virtual ~Thread();
-
-
virtual void run();
-
-
virtual void start();
-
virtual Thread* join();
-
-
const char* getName() const;
-
ThreadState getState() const;
-
-
// Determines whether the thread should delete itself when run() returns
-
// If you set this, you must NEVER call join()!
-
void setDeleteAfterRun() {deleteAfterRun = true;};
-
-
unsigned long getThreadId() const;
-
-
static Thread* self();
-
-
friend class Condition;
-
-
protected:
-
Thread(HANDLE thread_, DWORD thread_id_);
-
static DWORD WINAPI threadProc(LPVOID lpParameter);
-
-
HANDLE thread;
-
DWORD thread_id;
-
char* name;
-
ThreadState state;
-
Condition* sig;
-
Mutex mutex;
-
-
HANDLE cond_event;
-
Thread* cond_next;
-
-
bool deleteAfterRun;
-
};
CADThread重载了Thread类的run函数,CADThread的run函数先保存原有的桌面,然后切换到Winlogon桌面,然后发送ATL_CTRL_DEL,
最后把桌面还原。
-
class CADThread : public Thread {
-
public:
-
CADThread() : Thread("CtrlAltDel Emulator"), result(false) {}
-
virtual void run() {
-
HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId());
-
-
if (switchToDesktop(OpenDesktop(_T("Winlogon"), 0, FALSE, DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
-
DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
-
DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
-
DESKTOP_SWITCHDESKTOP | GENERIC_WRITE))) {
-
PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, MAKELONG(MOD_ALT | MOD_CONTROL, VK_DELETE));
-
switchToDesktop(old_desktop);
-
result = true;
-
}
-
}
-
bool result;
-
};
(3)使用(2)得到的对象调用功能
cad_thread->start(); cad_thread->join();
分别调用了start函数与join函数。有人可能会问,(2)定义的run接口怎么没调用到,其实是有的。
因为在Thread类的构造时就启动了此线程:
-
Thread::Thread(const char* name_) : sig(0), deleteAfterRun(false) {
-
sig = new Condition(mutex);
-
cond_event = CreateEvent(NULL, TRUE, FALSE, NULL);
-
if (!name_)
-
name_ = "Unnamed";
-
name = strDup(name_);
-
thread = CreateThread(NULL, 0, threadProc, this, CREATE_SUSPENDED, &thread_id);
-
state = ThreadCreated;
-
logAction(this, "created");
-
}
Thread::threadProc中就会调用到它:
2、tightvnc 客户端版本中实现模拟发送ATL+CTRL+DEL
在我的文章《XP、Wn7模拟发送ctrl+alt+delete组合键》中讲到在win7中实现模拟ATL+CTRL+DEL的方法,虽然我已经改在了会话1中发送窗口消息,可是还是没达到效果。
网友建议我去查看VNC代码,由于tightvnc代码是支持Win7 的,于是希望在这里找到解决方法。
在win-system工程中有Environment.h文件定义有类:
-
#ifndef _ENVIRONMENT_H_
-
#define _ENVIRONMENT_H_
-
-
#include "util/StringStorage.h"
-
-
#include
-
-
class Environment
-
{
-
public:
-
static const int APPLICATION_DATA_SPECIAL_FOLDER = 0x0;
-
static const int COMMON_APPLICATION_DATA_SPECIAL_FOLDER = 0x1;
-
public:
-
Environment();
-
~Environment();
-
-
static void getErrStr(StringStorage *out);
-
-
static void getErrStr(const TCHAR *specification, StringStorage *out);
-
-
static bool getSpecialFolderPath(int specialFolderId, StringStorage *out);
-
-
static bool getCurrentModulePath(StringStorage *out);
-
-
static bool getCurrentModuleFolderPath(StringStorage *out);
-
-
static bool getCurrentUserName(StringStorage *out);
-
-
static bool getComputerName(StringStorage *out);
-
-
static void restoreWallpaper();
-
static void disableWallpaper();
-
-
static bool isWinNTFamily();
-
static bool isWinXP();
-
static bool isWin2003Server();
-
static bool isVistaOrLater();
-
-
static void simulateCtrlAltDel();
-
-
private:
-
static void init();
-
static OSVERSIONINFO m_osVerInfo;
-
};
-
-
#endif
其中有 static void simulateCtrlAltDel();接口
-
void Environment::simulateCtrlAltDel()
-
{
-
if (isWinNTFamily()) {
-
CtrlAltDelSimulator cadSim;
-
cadSim.wait();
-
}
-
}
与vnc-4.0-winsrc 版本的CADThread类继承于Thread类类似,这里的CtrlAltDelSimulator类也继续Thread。
同样在thread类的构造时就启动了此线程:
-
Thread::Thread()
-
: m_terminated(false), m_active(false), m_hDesk(0)
-
{
-
m_hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) threadProc,
-
(LPVOID) this, CREATE_SUSPENDED, (LPDWORD) &m_threadID);
-
m_hDesk = DesktopSelector::getInputDesktop();
-
}
线程里会去调用execute方法:
-
DWORD WINAPI Thread::threadProc(LPVOID pThread)
-
{
-
Thread *_this = ((Thread *)pThread);
-
try {
-
DesktopSelector::setDesktopToCurrentThread(_this->m_hDesk);
-
DesktopSelector::closeDesktop(_this->m_hDesk);
-
_this->m_hDesk = 0;
-
_this->execute();
-
} catch (Exception &e) {
-
Log::error(_T("Abnormal thread termination.")
-
_T(" ThreadId = %x, message = \"%s\" \n"),
-
_this->m_threadID, e.getMessage());
-
}
-
_this->m_active = false;
-
return 0;
-
}
CtrlAltDelSimulator类重载了execute方法:
-
void CtrlAltDelSimulator::execute()
-
{
-
if (DesktopSelector::selectDesktop(&StringStorage(_T("Winlogon")))) {
-
HWND hwndCtrlAltDel = FindWindow(_T("SAS window class"), _T("SAS window"));
-
if (hwndCtrlAltDel == NULL) {
-
hwndCtrlAltDel = HWND_BROADCAST;
-
}
-
PostMessage(hwndCtrlAltDel, WM_HOTKEY, 0, MAKELONG(MOD_ALT | MOD_CONTROL, VK_DELETE));
-
}
-
}
往窗口发送消息是与VNC4.0版本是类似的,这里只讲selectDesktop的不同。
-
bool DesktopSelector::selectDesktop(const StringStorage *name)
-
{
-
HDESK desktop;
-
if (name) {
-
desktop = getDesktop(name);
-
} else {
-
desktop = getInputDesktop();
-
}
-
-
bool result = setDesktopToCurrentThread(desktop) != 0;
-
closeDesktop(desktop);
-
-
return result;
-
}
-
HDESK DesktopSelector::getDesktop(const StringStorage *name)
-
{
-
return OpenDesktop(name->getString(), 0, TRUE,
-
DESKTOP_CREATEMENU |
-
DESKTOP_CREATEWINDOW |
-
DESKTOP_ENUMERATE |
-
DESKTOP_HOOKCONTROL |
-
DESKTOP_WRITEOBJECTS |
-
DESKTOP_READOBJECTS |
-
DESKTOP_SWITCHDESKTOP |
-
GENERIC_WRITE);
-
}
-
bool DesktopSelector::setDesktopToCurrentThread(HDESK newDesktop)
-
{
-
return SetThreadDesktop(newDesktop) != 0;
-
}
3、1 在Vista版本前
参考我的文章《XP、Wn7模拟发送ctrl+alt+delete组合键》
3、2 在Vista版本后
同样是在Environment.h里的class Environment类中有:
static bool isWinNTFamily(); static bool isWinXP(); static bool isWin2003Server(); static bool isVistaOrLater(); static void simulateCtrlAltDel(); static void simulateCtrlAltDelUnderVista();
win7下的函数就是:simulateCtrlAltDelUnderVista
-
void Environment::simulateCtrlAltDelUnderVista()
-
{
-
Log::info(_T("Requested Ctrl+Alt+Del simulation under Vista or later"));
-
-
try {
-
DynamicLibrary sasLib(_T("sas.dll"));
-
SendSas sendSas = (SendSas)sasLib.getProcAddress("SendSAS");
-
if (sendSas == 0) {
-
throw Exception(_T("The SendSAS function has not been found"));
-
}
-
sendSas(FALSE);
-
} catch (Exception &e) {
-
Log::error(_T("The simulateCtrlAltDelUnderVista() function failed: %s"),
-
e.getMessage());
-
}
-
}
实现思路:
(1)从system32文件夹下的sas.dll里得到SendSAS接口,
(2)然后调用此接口sendSas(FALSE);
使用注意事项:
(1)
Simulates a secure attention sequence (SAS).
-
VOID WINAPI SendSAS(
-
_In_ BOOL AsUser
-
);
-
TRUE if the caller is running as the current user; otherwise,FALSE.
为了调用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
即增加以下注册表:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System] "SoftwareSASGeneration"=dword:00000003
(2)我把它放在Win7类型于GINA的凭证COM里(且是以起一线程的方式调用),发现没有效果。
这是因为SendSAS只能在服务中才起作用,而在凭据中是不能起作用的。
(3)
那么simulateCtrlAltDelUnderVista在哪里被调用呢?
-
void SasUserInput::setKeyboardEvent(UINT32 keySym, bool down)
-
{
-
bool delPressed = false;
-
-
if (m_underVista) {
-
switch (keySym) {
-
case XK_Alt_L:
-
case XK_Alt_R:
-
m_altPressed = down;
-
break;
-
case XK_Control_L:
-
case XK_Control_R:
-
m_ctrlPressed = down;
-
break;
-
case XK_Delete:
-
delPressed = down;
-
}
-
}
-
-
if (m_ctrlPressed && m_altPressed && delPressed && m_underVista) {
-
Environment::simulateCtrlAltDelUnderVista();
-
} else {
-
m_client->setKeyboardEvent(keySym, down);
-
}
-
}
我们再来看setKeyboardEvent在哪里被调用了:
(1)
-
void UserInputServer::applyKeyEvent(BlockingGate *backGate)
-
{
-
UINT32 keySym;
-
bool down;
-
readKeyEvent(&keySym, &down, backGate);
-
m_userInput->setKeyboardEvent(keySym, down);
-
}
applyKeyEvent调用readKeyEvent读出键值和按下或弹上的标志,然后再调用setKeyboardEvent。
applyKeyEvent的实现过程:
-
void DesktopServerProto::readKeyEvent(UINT32 *keySym, bool *down,
-
BlockingGate *gate)
-
{
-
*keySym = gate->readUInt32();
-
*down = gate->readUInt8() != 0;
-
}
(2)
-
void RfbClient::onKeyboardEvent(UINT32 keySym, bool down)
-
{
-
m_desktop->setKeyboardEvent(keySym, down);
-
}
(3)
-
void WinDesktop::setKeyboardEvent(UINT32 keySym, bool down)
-
{
-
Log::info(_T("set keyboard event (keySym = %u, down = %d)"), keySym, (int)down);
-
try {
-
if (isRemoteInputAllowed()) {
-
m_userInput->setKeyboardEvent(keySym, down);
-
}
-
} catch (Exception &e) {
-
Log::error(_T("setKeyboardEvent() crashed: %s"), e.getMessage());
-
m_extDeskTermListener->onAbnormalDesktopTerminate();
-
}
-
}
(4)