关键字 Ctrl,Alt,Delete,Windows 2000,hook,SAS,GINA,WINAPI,API,Thread
如何: 在Windows2000中动态禁用/启用Ctrl-Alt-Delete
--------------------------------------------------------------------------------
此文章的信息应用于:
Microsoft Windows 2000
--------------------------------------------------------------------------------
单击这里下载本文的代码。
概要
此文章的信息来自CSDN论坛VC/MFC版的讨论
在NT/2000中怎么禁用Ctrl+Alt+Delete?(不能用gina,键盘驱动)
在Windows2000中Ctrl-Alt-Delete组合键的处理如下:
Winlogon初始化的时候,在系统中注册了CTRL+ALT+DEL Secure Attention Sequence(SAS)热键,并且在WinSta0 Windows 系统中创建三个桌面。
SAS热键的注册使得Winlogon成为第一个处理CTRL+ALT+DEL的进程,所以保证了没有其他应用程序能够处理这个热键。
在 Windows NT/Windows 2000/Windows XP中, WinSta0 是表示物理屏幕、鼠标和键盘的Windows系统对象的名字。Winlogon在WinSta0 Windows系统中创建了SAS窗口(窗口标题是"SAS Window")和如下三个桌面。
Winlogon 桌面
应用程序 桌面
屏幕保护 桌面
当用户按下Ctrl-Alt-Delete组合键时,Winlogon桌面上的SAS窗口收到它注册的系统热键消息(WM_HOTKEY)
SAS Window窗口处理这个消息调用Graphical Identification and Authentication(GINA)动态连接库中的相关函数
要中断Ctrl-Alt-Delete组合键的处理,可以有以下方式
从键盘驱动层捕获Ctrl-Alt-Delete
替换Winlogon
替换GINA
Hook Winlogon 上SAS窗口的窗口过程(需要当前登录用户有调试权限)
Hook GINA里边的函数WlxLoggedOnSAS,然后返回WLX_SAS_ACTION_NONE(未研究)
更多信息
鉴于系统的更新可能造成我们替换的系统文件和其他系统文件不兼容(著名的DLL地狱),所以不推荐替换Winlogon.exe和GINA的方法。这里我们讨论Hook Winlogon 上的SAS窗口的窗口过程的方法。
因为SAS窗口和我们的程序内存地址空间不同,所以要写一个动态连接库,加载到SAS窗口的内存空间中。下面是动态连接库的源代码。
//---------------------------------------------------------------------------
//作者 :韦覃武
//网上呢称:BCB_FANS(四大名捕之追杀令)(此为CSDN和www.driverdevelop.com之帐号)
//E-Mail :slwqw@163.com
//日期 :2002-10-20
//
//功能 :在2000下屏蔽Ctrl + Alt + Del组合键。(在Windows 2000 Professional SP3
// 中文版平台下面测试通过)
//原理 :采用远程线程注入技术,装载一个DLL到Winlogon进程,然后截获SAS窗口的窗
// 口过程,接管WM_HOTKEY消息,以达到屏蔽Ctrl + Alt + Del之目的。
//开发语言:Borland C++Builder 5.0 Patch2
//技术比较:关于在2000下面如何屏蔽Ctrl + Alt + Del组合键,一种常被提到的解决方法就
// 是使用自己写的GINA去替换MSGINA.DLL,然后在WlxLoggedOnSAS里边直接返回
// WLX_SAS_ACTION_NONE。嘿嘿,说到底这并不是真正地屏蔽了这个组合键,只是
// 直接返回WLX_SAS_ACTION_NONE时,Winlogon进程又自动从"Winlogon"桌面切换
// 回原来的"Default"桌面了,而不是显示安全对话框,所以看起来被屏蔽了:),
// 使用那种方法明显地看到桌面在闪烁!但是使用本文的方法时,你不会看到任
// 何闪烁!
//鸣谢 :www.driverdevelop.com上的icube和lu0。
//版权 :转载请注明原作者:)
//---------------------------------------------------------------------------
#include "stdafx.h"
#include <string>
using namespace std;
//---------------------------------------------------------------------------
HWND hSASWnd;
FARPROC FOldProc;
LRESULT CALLBACK SASWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam);
//---------------------------------------------------------------------------
HANDLE hThread = NULL;
DWORD dwThreadId;
DWORD WINAPI ThreadFunc();
//---------------------------------------------------------------------------
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH :
hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,NULL,0,&dwThreadId);
break;
case DLL_PROCESS_DETACH :
if(FOldProc != NULL)
{
SetWindowLong(hSASWnd,GWL_WNDPROC,long(FOldProc));
}
CloseHandle(hThread);
break;
}
return TRUE;
}
//---------------------------------------------------------------------------
DWORD WINAPI ThreadFunc()
{
HDESK hDesk;
hDesk = OpenDesktop("Winlogon",0,false,MAXIMUM_ALLOWED);
FOldProc = NULL;
hSASWnd = NULL;
EnumDesktopWindows(hDesk,(WNDENUMPROC)EnumWindowsProc,0);
if(hSASWnd != NULL)
{
FOldProc = (FARPROC)SetWindowLong(hSASWnd,GWL_WNDPROC,long(SASWindowProc));
}
CloseHandle(hDesk);
return 1;
}
//---------------------------------------------------------------------------
//查找"Winlogon"桌面的窗口
BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam)
{
char ClassBuf[128];
GetWindowText(hwnd,ClassBuf,sizeof(ClassBuf));
//我自己写了一个系统服务,然后在里边查询"Winlogon"桌面上的窗口,发现桌面上存在
//窗口"SAS window"。
string ClassName(ClassBuf);
if(ClassName.find("SAS window") != -1)
{
hSASWnd = hwnd;
return false;
}
return true;
}
//---------------------------------------------------------------------------
//SAS窗口的窗口过程
LRESULT CALLBACK SASWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
//屏蔽Ctrl + Alt + Del
if(uMsg == WM_HOTKEY)
{
WORD wKey = HIWORD(lParam);
WORD wModifier = LOWORD(lParam);
bool IsCtrlDown = ((wModifier & VK_CONTROL) != 0);
bool IsAltDown = ((wModifier & VK_MENU) != 0);
bool IsShiftDown = ((wModifier & VK_SHIFT) != 0);
//按下Ctrl + Alt + Del组合键
if(IsCtrlDown && IsAltDown && wKey == VK_DELETE)
{
return 1;
}
//按下Ctrl + Shift + Esc组合键,这个组合键将显示任务管理器,可根据需要是否屏蔽。
else if(IsCtrlDown && IsShiftDown && wKey == VK_ESCAPE)
{
// Do nothing
}
}
return CallWindowProc((WNDPROC)FOldProc,hwnd,uMsg,wParam,lParam);
}
//---------------------------------------------------------------------------
这样,如果Winlogon加载了这个动态连接库,那么就替换了SAS窗口的窗口过程。如果Winlogon卸载了这个动态连接库,则恢复了SAS窗口的窗口过程。
为了让Winlogon加载我们的动态连接库,首先要找到Winlogon进程,然后在进程中分配空间存放我们的代码,再通过创建远程线程赖执行我们的代码。下面是Hook部分的代码
//---------------------------------------------------------------------------
//作者 :韦覃武,jiangsheng
//网上呢称:BCB_FANS(四大名捕之追杀令)(此为CSDN和www.driverdevelop.com之帐号)jiangsheng(此为CSDN帐号)
//E-Mail :slwqw@163.com
//日期 :2002-10-20
//2002-11-5 jingsheng修改
//功能 :在2000下屏蔽Ctrl + Alt + Del组合键。(在Windows 2000 Professional SP3
// 中文版平台下面测试通过)
//原理 :采用远程线程注入技术,装载一个DLL到Winlogon进程,然后截获SAS窗口的窗
// 口过程,接管WM_HOTKEY消息,以达到屏蔽Ctrl + Alt + Del之目的。
//开发语言:Borland C++Builder 5.0 Patch2,Visual C++ 6.0 SP5
//技术比较:关于在2000下面如何屏蔽Ctrl + Alt + Del组合键,一种常被提到的解决方法就
// 是使用自己写的GINA去替换MSGINA.DLL,然后在WlxLoggedOnSAS里边直接返回
// WLX_SAS_ACTION_NONE。嘿嘿,说到底这并不是真正地屏蔽了这个组合键,只是
// 直接返回WLX_SAS_ACTION_NONE时,Winlogon进程又自动从"Winlogon"桌面切换
// 回原来的"Default"桌面了,而不是显示安全对话框,所以看起来被屏蔽了:),
// 使用那种方法明显地看到桌面在闪烁!但是使用本文的方法时,你不会看到任
// 何闪烁!
//鸣谢 :www.driverdevelop.com上的icube和lu0。
//版权 :转载请注明原作者:)
//---------------------------------------------------------------------------
#include "stdafx.h"
#include <tlhelp32.h>
#include <lmerr.h>
#include "Hook.h"
//add by jiangsheng 2002-11-5
#include "TaskKeyMgr.h"
#include "Wrappers.h"//复制自MSDN杂志Windows XP Escape from DLL Hell with Custom Debugging and Instrumentation Tools and Utilities的代码
extern BOOL Is_Terminal_Services () ;//复制自Platform SDK文档: Windows System Information /Verifying the System Version
//end add by jiangsheng 2002-11-5
//---------------------------------------------------------------------------
//错误代码格式化函数
//replaced by jiangsheng 2002-11-5
//from Q149409 HOWTO: Get Message Text from Networking Error Codes
CString __fastcall SysErrorMessage(DWORD dwLastError )
{
CString strRet(_T("Unknown error"));
HMODULE hModule = NULL; // default to system source
LPSTR MessageBuffer;
DWORD dwBufferLength;
DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_SYSTEM ;
//
// If dwLastError is in the network range,
// load the message source.
//
if(dwLastError >= NERR_BASE && dwLastError <= MAX_NERR) {
hModule = LoadLibraryEx(TEXT("netmsg.dll"),NULL,LOAD_LIBRARY_AS_DATAFILE);
if(hModule != NULL)
dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE;
}
//
// Call FormatMessage() to allow for message
// text to be acquired from the system
// or from the supplied module handle.
//
if(dwBufferLength = FormatMessageA(
dwFormatFlags,
hModule, // module to get message from (NULL == system)
dwLastError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
(LPSTR) &MessageBuffer,
0,
NULL
))
{
//
// Output message string on stderr.
//
strRet=CString(MessageBuffer,dwBufferLength);
//
// Free the buffer allocated by the system.
//
LocalFree(MessageBuffer);
}
//
// If we loaded a message source, unload it.
//
if(hModule != NULL)
FreeLibrary(hModule);
return strRet;
}
//end replaced by jiangsheng 2002-11-5
//---------------------------------------------------------------------------
#ifdef UNICODE
LPCSTR LoadLibraryFuncStr = "LoadLibraryW";
LPCSTR GetModuleHandleFuncStr = "GetModuleHandleW";
#else
LPCSTR LoadLibraryFuncStr = "LoadLibraryA";
LPCSTR GetModuleHandleFuncStr = "GetModuleHandleA";
#endif
LPCSTR FreeLibraryFuncStr = "FreeLibrary";
LPCSTR GetProcAddressFuncStr = "GetProcAddress";
LPCSTR GetLastErrorFuncStr = "GetLastError";
//---------------------------------------------------------------------------
//removed by jiangsheng 2002-11-5
//const char* const RemoteDllName = "RemoteDll.Dll";
//end removed by jiangsheng 2002-11-5
LPCTSTR szRemoteProcessName = "Winlogon.exe";
typedef HINSTANCE (WINAPI *PLOADLIBRARY)(LPCTSTR );
typedef BOOL (WINAPI *PFREELIBRARY)(HINSTANCE);
typedef HMODULE (WINAPI* PGETMODULEHANDLE)(LPCTSTR );
typedef PVOID (WINAPI* PGETPROCADDRESS)(HINSTANCE,LPCSTR);
typedef DWORD (WINAPI* PGETLASTERROR)(VOID);
BOOL __fastcall EnablePrivilege(LPCTSTR lpszPrivilegeName,BOOL bEnable);
DWORD __fastcall GetPIDFromName(LPCTSTR lpszProcName);
//---------------------------------------------------------------------------
typedef struct
{
PLOADLIBRARY pfnLoadLibrary;
PGETLASTERROR pfnGetLastError;
TCHAR szDllName[1024];
DWORD dwReturnValue;
} INJECTLIBINFO;
typedef struct
{
PFREELIBRARY pfnFreeLibrary;
PGETMODULEHANDLE pfnGetModuleHandle;
PGETLASTERROR pfnGetLastError;
DWORD dwReturnValue;
TCHAR szDllName[1024];
} DEINJECTLIBINFO;
//---------------------------------------------------------------------------
//远程线程,用来装载DLL
static DWORD WINAPI ThreadFuncAttach(INJECTLIBINFO *pInfo)
{
HINSTANCE hDll=NULL;
pInfo->dwReturnValue = 0;
hDll = (HINSTANCE)pInfo->pfnLoadLibrary(pInfo->szDllName);
if(hDll == NULL)
pInfo->dwReturnValue = pInfo->pfnGetLastError();
return((DWORD)hDll);
}
//---------------------------------------------------------------------------
//占位函数,用来计算ThreadFuncAttach的大小
static void AfterThreadFuncAttach(void)
{
}
//---------------------------------------------------------------------------
//远程线程,用来卸载DLL
static DWORD WINAPI ThreadFuncDetach(DEINJECTLIBINFO *pInfo)
{
HINSTANCE hDll = NULL;
BOOL bResult=FALSE;
BOOL bHasFoundModule = FALSE;
pInfo->dwReturnValue = 0;//意味成功,如果这个值不是0,则是一个错误代码。
while((hDll = pInfo->pfnGetModuleHandle(pInfo->szDllName)) != NULL)
{
bHasFoundModule = TRUE;
bResult = pInfo->pfnFreeLibrary(hDll);
if(bResult == FALSE)
{
pInfo->dwReturnValue = pInfo->pfnGetLastError();
break;
}
}
if(pInfo->dwReturnValue == 0 && !bHasFoundModule)
{
pInfo->dwReturnValue = pInfo->pfnGetLastError();
}
return 1;
}
//---------------------------------------------------------------------------
//占位函数,用来计算ThreadFuncDetach的大小
static void AfterThreadFuncDetach(void)
{
}
//---------------------------------------------------------------------------
//修改本进程的权限
BOOL __fastcall EnablePrivilege(LPCTSTR lpszPrivilegeName,BOOL bEnable)
{
HANDLE hToken;
TOKEN_PRIVILEGES tp;
LUID luid;
if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES |
TOKEN_QUERY | TOKEN_READ,&hToken))
return FALSE;
if(!LookupPrivilegeValue(NULL, lpszPrivilegeName, &luid))
return TRUE;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = (bEnable) ? SE_PRIVILEGE_ENABLED : 0;
AdjustTokenPrivileges(hToken,FALSE,&tp,NULL,NULL,NULL);
CloseHandle(hToken);
return (GetLastError() == ERROR_SUCCESS);
}
//---------------------------------------------------------------------------
//通过进程名称得到进程的ID(这里使用方法Toolhelp函数,也可使用PSAPI)
DWORD __fastcall GetPIDFromName(LPCTSTR lpszProcName)
{
HANDLE hSnapshot;
PROCESSENTRY32 ProcStruct;
DWORD dwProcessID = -1;
//added by jiangsheng 2002-11-8
BOOL bIsTerminalServices=Is_Terminal_Services();
if(bIsTerminalServices){
//复制自MSDN杂志Windows XP Escape from DLL Hell with Custom Debugging and Instrumentation Tools and Utilities的代码
//get current session ID
CWTSWrapper WTS;
if (WTS.IsValid())
{
DWORD dwCurSessionID = -1;
LPTSTR pSessionInfo=NULL;
DWORD dwBytes;
if(WTS.WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,WTS_CURRENT_SESSION,
WTSSessionId, (LPTSTR*)&pSessionInfo, &dwBytes)){
dwCurSessionID =*((DWORD*)pSessionInfo);
// enumerate processes
PWTS_PROCESS_INFO pProcessInfo = NULL;
DWORD ProcessCount = 0;
BOOL bFound;
if (WTS.WTSEnumerateProcesses(WTS_CURRENT_SERVER_HANDLE, 0, 1,
&pProcessInfo, &ProcessCount)){
for (DWORD CurrentProcess = 0; CurrentProcess < ProcessCount; CurrentProcess++){
CString strCurExePath(pProcessInfo[CurrentProcess].pProcessName);
CString strRemoteProc(lpszProcName);
strCurExePath.MakeLower();
strRemoteProc.MakeLower();
bFound = (strCurExePath.Find(strRemoteProc) != -1);
if(bFound && dwCurSessionID==pProcessInfo[CurrentProcess].SessionId) {
dwProcessID = pProcessInfo[CurrentProcess].ProcessId;
break;
}
}
}
WTS.WTSFreeMemory(pSessionInfo);
}
}
}
else{
//end added by jiangsheng 2002-11-8
BOOL bResult;
hSnapshot = CreateToolhelp32Snapshot((DWORD)TH32CS_SNAPPROCESS,0);
ProcStruct.dwSize = sizeof(PROCESSENTRY32);
bResult = Process32First(hSnapshot,&ProcStruct);
while(bResult)
{
BOOL bFound;
CString strCurExePath(ProcStruct.szExeFile);
CString strRemoteProc(lpszProcName);
strCurExePath.MakeLower();
strRemoteProc.MakeLower();
bFound = (strCurExePath.Find(strRemoteProc) != -1);
if(bFound)
{
dwProcessID = ProcStruct.th32ProcessID;
break;
}
bResult = Process32Next(hSnapshot,&ProcStruct);
}
CloseHandle(hSnapshot);
}
return dwProcessID;
}
//---------------------------------------------------------------------------
// 插入代码
//---------------------------------------------------------------------------
//InjectFunc
void __fastcall InjectFunc()
{
HANDLE hRemoteProcess=NULL;
DWORD dwRemoteProcess=NULL;
DWORD dwThreadSize=0;
INJECTLIBINFO InjectLibInfo;
PVOID pRemoteThread=NULL;
PVOID pRemoteParam=NULL;
DWORD dwWriten=0;
DWORD dwRet=0;
//提升本进程权限然后打开目的进程
//当前用户必须具有调试权限
EnablePrivilege(SE_DEBUG_NAME,true);
dwRemoteProcess = GetPIDFromName(szRemoteProcessName);
if(dwRemoteProcess == (DWORD)-1)
{
MessageBox(NULL,_T("Failed to Query Process ID."),NULL,MB_OK | MB_APPLMODAL | MB_ICONWARNING);
return;
}
hRemoteProcess = OpenProcess(PROCESS_ALL_ACCESS,false,dwRemoteProcess);
if(hRemoteProcess == NULL)
{
MessageBox(NULL,_T("Failed to Open Process. Err = ") + SysErrorMessage(GetLastError()),
NULL,MB_OK | MB_APPLMODAL | MB_ICONWARNING);
return;
}
//初始化参数
ZeroMemory(&InjectLibInfo,sizeof(INJECTLIBINFO ));
InjectLibInfo.pfnLoadLibrary = (PLOADLIBRARY)GetProcAddress(GetModuleHandle("Kernel32.dll"),LoadLibraryFuncStr);
InjectLibInfo.pfnGetLastError = (PGETLASTERROR)GetProcAddress(GetModuleHandle("Kernel32.dll"),GetLastErrorFuncStr);
lstrcpyn(InjectLibInfo.szDllName,CTaskKeyMgr::strRemoteDllName,CTaskKeyMgr::strRemoteDllName.GetLength()+1);
//在远程线程分配内存来存放参数
pRemoteParam = VirtualAllocEx(hRemoteProcess,NULL,sizeof(INJECTLIBINFO),MEM_COMMIT,PAGE_READWRITE);
if(pRemoteParam == NULL)
{
MessageBox(NULL,_T("Failed to Allocate Memory at Remote Process for Param.Err = ") + SysErrorMessage(GetLastError()),
NULL,MB_OK | MB_APPLMODAL | MB_ICONWARNING);
return;
}
dwRet = WriteProcessMemory(hRemoteProcess,pRemoteParam,(LPVOID)&InjectLibInfo,sizeof(INJECTLIBINFO),&dwWriten);
if(dwRet == 0)
{
MessageBox(NULL,_T("Failed to Write Param to Remote Process.Err = ") + SysErrorMessage(GetLastError()),
NULL,MB_OK | MB_APPLMODAL | MB_ICONWARNING);
return;
}
//拷贝线程体
dwThreadSize = (int)AfterThreadFuncAttach - (int)ThreadFuncAttach + 1024 + sizeof(INJECTLIBINFO);
pRemoteThread = VirtualAllocEx(hRemoteProcess,NULL,dwThreadSize,MEM_COMMIT,PAGE_READWRITE);
if(pRemoteThread == NULL)
{
MessageBox(NULL,_T("Failed to Allocate Memory at Remote Process for Thread Code.Err = ") + SysErrorMessage(GetLastError()),
NULL,MB_OK | MB_APPLMODAL | MB_ICONWARNING);
return;
}
dwRet = WriteProcessMemory(hRemoteProcess,pRemoteThread,(LPVOID)ThreadFuncAttach,dwThreadSize,&dwWriten);
if(dwRet == 0)
{
MessageBox(NULL,_T("Failed to Write Thread Code to Remote Process.Err = ") + SysErrorMessage(GetLastError()),
NULL,MB_OK | MB_APPLMODAL | MB_ICONWARNING);
return;
}
//启动远程线程
HANDLE hRemoteThread;
hRemoteThread = CreateRemoteThread(hRemoteProcess,0,0,(DWORD(__stdcall *)(VOID*))pRemoteThread,(INJECTLIBINFO*)pRemoteParam,0,&dwWriten);
::WaitForSingleObject(hRemoteThread,INFINITE);
if(hRemoteThread == NULL)
{
MessageBox(NULL,_T("Failed to create unload thread.Err=") + SysErrorMessage(GetLastError()),NULL,MB_OK |MB_APPLMODAL | MB_ICONWARNING);
}
else
{
;
}
//读卸载返回值
dwRet =ReadProcessMemory(hRemoteProcess,pRemoteParam,(LPVOID)&InjectLibInfo,sizeof(INJECTLIBINFO),&dwWriten);
if(dwRet == 0)
{
MessageBox(NULL,_T("Unable to read load return value.Err=") + SysErrorMessage(GetLastError()),
NULL,MB_OK | MB_APPLMODAL | MB_ICONWARNING);
}
else
{
if(InjectLibInfo.dwReturnValue == 0)
{
;
}
else
{
MessageBox(NULL,_T("Failed to load library to Winlogon.Err=") +SysErrorMessage(InjectLibInfo.dwReturnValue),NULL,MB_OK | MB_APPLMODAL | MB_ICONWARNING);
}
}
//恢复权限
EnablePrivilege(SE_DEBUG_NAME,false);
CloseHandle(hRemoteProcess);
}
//---------------------------------------------------------------------------
// 卸载线程
//---------------------------------------------------------------------------
//DeinjectFunc
void __fastcall DeinjectFunc()
{
HANDLE hRemoteProcess=NULL;
DWORD dwRemoteProcess=0;
DWORD dwThreadSize=0;
DEINJECTLIBINFO DeinjectLibInfo;
PVOID pRemoteThread=NULL;
PVOID pRemoteParam=NULL;
DWORD dwWriten=0;
DWORD Ret=0;
//提升本进程权限然后打开目的进程
EnablePrivilege(SE_DEBUG_NAME,true);
dwRemoteProcess = GetPIDFromName(szRemoteProcessName);
if(dwRemoteProcess == (DWORD)-1)
{
MessageBox(NULL,_T("Failed to Query Process ID."),NULL,MB_OK | MB_APPLMODAL | MB_ICONWARNING);
return;
}
hRemoteProcess = OpenProcess(PROCESS_ALL_ACCESS,false,dwRemoteProcess);
if(hRemoteProcess == NULL)
{
MessageBox(NULL,_T("Failed to Open Process. Err = ") + SysErrorMessage(GetLastError()),
NULL,MB_OK | MB_APPLMODAL | MB_ICONWARNING);
return;
}
//初始化参数
ZeroMemory(&DeinjectLibInfo,sizeof(DEINJECTLIBINFO ));
DeinjectLibInfo.pfnFreeLibrary = (PFREELIBRARY)GetProcAddress(GetModuleHandle("Kernel32.dll"),FreeLibraryFuncStr);
DeinjectLibInfo.pfnGetModuleHandle = (PGETMODULEHANDLE)GetProcAddress(GetModuleHandle("Kernel32.dll"),GetModuleHandleFuncStr);
DeinjectLibInfo.pfnGetLastError = (PGETLASTERROR)GetProcAddress(GetModuleHandle("Kernel32.dll"),GetLastErrorFuncStr);
lstrcpyn(DeinjectLibInfo.szDllName,CTaskKeyMgr::strRemoteDllName,CTaskKeyMgr::strRemoteDllName.GetLength()+1);
//在远程线程分配内存来存放参数
pRemoteParam = VirtualAllocEx(hRemoteProcess,NULL,sizeof(DEINJECTLIBINFO),MEM_COMMIT,PAGE_READWRITE);
if(pRemoteParam == NULL)
{
MessageBox(NULL,_T("Failed to Allocate Memory at Remote Process.Err = ") + SysErrorMessage(GetLastError()),
NULL,MB_OK | MB_APPLMODAL | MB_ICONWARNING);
}
Ret = WriteProcessMemory(hRemoteProcess,pRemoteParam,(LPVOID)&DeinjectLibInfo,sizeof(DEINJECTLIBINFO),&dwWriten);
if(Ret == 0)
{
MessageBox(NULL,_T("Failed to Write Param to Remote Process.Err = ") + SysErrorMessage(GetLastError()),
NULL,MB_OK | MB_APPLMODAL | MB_ICONWARNING);
return;
}
//拷贝线程体
dwThreadSize = (int)AfterThreadFuncDetach - (int)ThreadFuncDetach + 1024 + sizeof(DEINJECTLIBINFO);
pRemoteThread = VirtualAllocEx(hRemoteProcess,NULL,dwThreadSize,MEM_COMMIT,PAGE_READWRITE);
if(pRemoteThread == NULL)
{
MessageBox(NULL,_T("Failed to Allocate Memory at Remote Process for Thread Code.Err = ") + SysErrorMessage(GetLastError()),
NULL,MB_OK | MB_APPLMODAL | MB_ICONWARNING);
return;
}
Ret = WriteProcessMemory(hRemoteProcess,pRemoteThread,(LPVOID)ThreadFuncDetach,dwThreadSize,&dwWriten);
if(Ret == 0)
{
MessageBox(NULL,_T("Failed to Write Thread Code to Remote Process.Err = ") + SysErrorMessage(GetLastError()),
NULL,MB_OK | MB_APPLMODAL | MB_ICONWARNING);
return;
}
//启动远程线程
HANDLE hRemoteThread;
hRemoteThread = CreateRemoteThread(hRemoteProcess ,0,0,(DWORD(__stdcall *)(VOID*))pRemoteThread,(DEINJECTLIBINFO*)pRemoteParam,0,&dwWriten);
if(hRemoteThread == NULL)
{
MessageBox(NULL,_T("Failed to create remote unload thread.Err=") + SysErrorMessage(GetLastError()),NULL,MB_OK | MB_APPLMODAL | MB_ICONWARNING);
}
else
{
CloseHandle(hRemoteThread);
}
//读卸载返回值
Ret = ReadProcessMemory(hRemoteProcess,pRemoteParam,(LPVOID)&DeinjectLibInfo,sizeof(DEINJECTLIBINFO),&dwWriten);
if(Ret == 0)
{
MessageBox(NULL,_T("Unable to read unload return value.Err=") + SysErrorMessage(GetLastError()),
NULL,MB_OK | MB_APPLMODAL | MB_ICONWARNING);
}
else
{
if(DeinjectLibInfo.dwReturnValue == 0)
{
}
else
{
MessageBox(NULL,_T("Failed to unload .Err=")+ SysErrorMessage(DeinjectLibInfo.dwReturnValue),NULL,MB_OK | MB_APPLMODAL | MB_ICONWARNING);
}
}
//恢复权限
CloseHandle(hRemoteProcess);
EnablePrivilege(SE_DEBUG_NAME,false);
}
//---------------------------------------------------------------------------
//使用方法
BOOL CTaskKeyMgr::IsCtrlAltDeleteDisabled(){return bInjectFuncLoaded;}
if (dwFlags & CTRLALTDEL) {
if(bDisable&&!IsCtrlAltDeleteDisabled()){
InjectFunc();
bInjectFuncLoaded=TRUE;
}
if(!bDisable&&IsCtrlAltDeleteDisabled()){
DeinjectFunc();
bInjectFuncLoaded=FALSE;
}
}
注意
如果Windows的后续版本更改了Ctrl+Alt+Delete的处理,本文所提供的技术可能不再工作。如果你在你的代码中使用了本文的技术,请注意你可能必须在未来修改你的代码。
已知问题
尚无Unicode版本
VirtualAllocEx分配的内存没有用VirtualFreeEx释放
在Debug方式下运行会造成Winlogon出错(出错后请不要确认或取消那个出错对话框,然后保存打开的所有文档,关闭所有程序,通过正常的途径关机,否则Windows会立刻关机)
参考
如果需要更多信息,单击下面的连接查看CSDN论坛中的讨论
在NT/2000中怎么禁用Ctrl+Alt+Delete?(不能用gina,键盘驱动) 。
MSDN文档库中的文章
Q226359 HOWTO: Disable Task Switching on Win32 Platforms
Q195027 STOP 0xC000021A in Winlogon Caused by PCAnywhere
Q229033 Programs That Replace Msgina.dll May Cause "STOP 0x0000001E" Error Message
Q192298 Third Party GINAs May Fail with Service Pack 4 Causing STOP 0x21A in WINLOGON
Q164486 Winlogon May Fail if the Third-Party Gina.dll File is Missing or Corrupted
Q180854 Access Violation in Winlogon with Third-Party Gina.dll
Q193361 MSGINA.DLL does not Reset WINLOGON Structure
MSDN杂志中的文章
MSDN Magazine > September 2002 > Typename, Disabling Keys in Windows XP with TrapKeys(Paul DiLascia)
MSDN Magazine > June 2002 > Windows XP Escape from DLL Hell with Custom Debugging and Instrumentation Tools and Utilities
VC知识库中的文章
Windows XP系统中如何屏蔽 Ctrl+Alt+Del、Alt+Tab以及Ctrl+Esc键序列
文章类型 : kbhowto
-------------------------------------------------
众所周知,在Windows 9X/Me系统中,屏蔽Ctrl+Del+Alt和各种任务开关键可以很轻松地通过API函数SystemParametersInfo(SPI_SETSCREENSAVERRUNNING,TRUE,&bOldState,0)解决,但在Windows NT/2K/XP系列操作系统中,Ctrl+Del+Alt升级为*别组合键序列,成为系统安全预留的紧急出口,上述操作就无法实现了,即使采用WM_KEYBOARD_LL低级键盘钩子也无法拦截,但也不是绝对没有办法,不然也就不会有本文了。
系统启动过程
在Windows NT/2K/XP中,用户登陆采用新版的WinLogon和GINA(Graphical Identification and Authentication)即图形化身份认证,此外还提供了一个网络服务的动态链接库Network Provider DLL。由于用户认证策略是在Gina DLL中独立设计的,我们可以通过替换此文件的方式进行智能卡或指纹等方面的认证开发。小知识:WinLogon是Windows操作系统的一部分,提供用户交互式登陆支持,而GINA则是WinLogon实现认证的一个DLL,实际文件名为:Windows NT/system32/msgina.dll。Windows则是用这个DLL实现用户名+密码进行身份认证。古老的GINA木马就是利用的这一点来获取系统的登陆密码的。
WinSta0是显示器、鼠标和键盘的Windows系统对象的名称,Winlogon初始化的时候,在WinSta0 Windows系统中创建了SAS窗口(窗口标题是“SAS Window”)并首先注册了CTRL+ALT+DEL Secure Attention Sequence(SAS)热键,接下来创建了三个桌面,即Winlogon 桌面(显示安全登陆界面),应用程序桌面(即正常进入所显示的我的电脑界面),屏幕保护桌面。由于SAS热键的注册使得Winlogon成为第一个处理CTRL+ALT+DEL的进程,这保证了没有其它应用程序能够处理这个热键。
当用户按下Ctrl+Alt+Del组合键时,Winlogon桌面上的SAS窗口收到它注册的系统热键消息(WM_HOTKEY)。SAS Window窗口处理这个消息调用GINA DLL中的WlxInitialize、WlxActivateUserShell函数进行初始化和调用用户外壳程序。Windows就是用这个机制进行用户和密码判断的,所以要中断Ctrl+Alt+Del组合键的处理,可以有以下方式:
1. 使用DDK(驱动程序开发包)编写键盘低层驱动程序捕获Ctrl+Alt+Del。
2. 替换Winlogon。
3. 编写GINA 代理DLL。
4. Hook Winlogon上SAS窗口的窗口过程(需要当前登录用户有调试权限)。Socket:由于操作系统的升级换代非常容易引起用户自编写的DLL文件与系统文件之间的不兼容(著名的DLL地狱),由此不建议使用Winlogon.exe和GINA的方法,倾向于Hook Winlogon 上的SAS窗口并进行窗口子类化的方法。
开发原理
动态嵌入技术是指将用户自己代码嵌入到正在运行的进程中技术。理论上讲,Windows每个进程都有自己私有内存空间,其它进程禁止访问其私有空间。但实际上我们可用种种方法访问并操作其它进程的私有空间,这就是动态嵌入的DLL木马所采用的技术。在多种嵌入技术(HOOK 窗口,API挂接,远程线程)中,远程线程嵌入技术对用户要求不高,只要有基本的进程线程概念和DLL相关知识即可。小知识:远程线程技术是指在一个进程中创建远程线程的手段进入目标进程的内存空间。通过CreateRemoteThread函数在目标进程内创建远程线程,共享目标进程的内存空间和其它资源,并且可以任意修改远程进程的数据,所以,此种作法风险极高。
通过将代码直接拷贝到远程线程内运行来完成动态嵌入,这种思想给了在应用程序中可动态屏蔽Ctrl+Alt+Del组合键的思路。在这之前我们要对Windows系统的内存偏移量也即相对内存进行修改,重点应用以下API函数:
1.首先我们要用FindWindow得到SAS窗口的句柄,因为我们要通过它获知运行后所在进程的ID,下面就是FindWindow的用法:
HWND FindWindow(
LPCTSTR lpClassName, // pointer to class name
LPCTSTR lpWindowName // pointer to window name
);
2.采用GetProcAddress得到窗口相对应的进程ID,函数用法如下:
FARPROC GetProcAddress(
HMODULE hModule, // handle to DLL module
LPCSTR lpProcName // function name
);
3.得到进程ID后,接下来的事是要以最高权限打开进程,所用到的函数OpenProcess的具体使用方法如下:
HANDLE OpenProcess(
DWORD dwDesiredAccess, // access flag
BOOL bInheritHandle, // handle inheritance flag
DWORD dwProcessId // process identifier
);
在DwDesiredAccess之处就是设存取方式的地方,可设的权限很多,这里只使用PROCESS_ALL_ACCESS来打开进程就可以,更详细可查阅MSDN。
4.打开进程后,使用WriteProcessMemory来对内存地址写入数据即可:
BOOL WriteProcessMemory(
HANDLE hProcess, // handle to process whose memory is written to
LPVOID lpBaseAddress, // address to start writing to
LPVOID lpBuffer, // pointer to buffer to write data to
DWORD nSize, // number of bytes to write
LPDWORD lpNumberOfBytesWritten // actual number of bytes written
);
5.创建远程线程以运行所嵌入的代码。
HANDLE CreateRemoteThread(
HANDLE hProcess, // handle to process
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
DWORD dwStackSize, // initial stack size
LPTHREAD_START_ROUTINE lpStartAddress, // thread function
LPVOID lpParameter, // thread argument
DWORD dwCreationFlags, // creation option
LPDWORD lpThreadId // thread identifier
);
6.CloseHandle(HANDLE hObject)关闭进程句柄。工程实践
在系统集成的项目中,由于所开发的专业软件往往采用独占方式运行,软件开发中对用户无操作的空闲时段处理是必须考虑的问题。笔者在这种类型的项目中,考虑到后期代码维护和模块集成调试方便,设计时摒弃了传统的DLL(动态链接库)嵌入形式,采用远程线程代码嵌入技术对核心部分也是最困难部分NT/2000平台下屏蔽Ctrl+Alt+Del组合键进行处理。完成后程序运行稳定,实验效果非常良好。以下是核心代码:
1. 定义基本函数
我们需要用来给自身进程加入DEBUG权限的函数(有足够权限对别的进程进行操作),一个通过进程名得到进程ID的函数(Toolhelp 函数和PSAPI函数返回进程标识符(PID)值两种实现方式)。
/*********** 允许/禁止特权 通过调用SE_DEBUG_NAME 参数. ****/
BOOL EnablePrivilege(LPCTSTR lpszPrivilegeName, BOOL bEnable)
{
HANDLE hToken;
TOKEN_PRIVILEGES tp;
LUID luid;
BOOL ret;if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_READ, &hToken))
return FALSE;if (!LookupPrivilegeValue(NULL, lpszPrivilegeName, &luid))
return FALSE;tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;ret = AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
CloseHandle(hToken);
return ret;
}
/********** 根据进程名称返回进程标识符PID **********/
DWORD GetPIDFromName(char *szProcessName)
{
DWORD dwPID;
OSVERSIONINFO info;info.dwOSVersionInfoSize = sizeof(info);
GetVersionEx(&info);// 根据Windows操作系统版本的不同选择不同函数进行处理
if (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
dwPID = GetPIDFromNameToolhelp(szProcessName);
else if (info.dwPlatformId == VER_PLATFORM_WIN32_NT)
dwPID = GetPIDFromNamePSAPI(szProcessName);
else
dwPID = -1;return dwPID;
}
2. 定义全局数据变量,准备线程的参数结构
定义对第一个处理Ctrl+Alt+Del组合键的WinLogon进程的文件名:
char *szProcessName = "winlogon.exe";
#pragma comment(linker, "/INCREMENTAL:NO") // 关闭增量链接模式
// 全局变量定义
DWORD PID; // 代码嵌入进程的PID
BYTE *pDataRemote; // INJECTDATA 数据结构在远程线程的地址
BYTE *pSASWinProcRemote; // 函数SASWindowProc()在远程线程的地址
#define DUMMY_ADDR 0x12345678 // INJECTDATA 数据结构的动态地址
// INJECTDATA: 每个远程代码嵌入函数所存贮的内存块,传递每个嵌入函数的地址或字符数据
typedef LONG (WINAPI *SETWINDOWLONG) (HWND, int, LONG);
typedef LRESULT (WINAPI *CALLWINDOWPROC) (WNDPROC, HWND, UINT, WPARAM, LPARAM);
typedef HWND (WINAPI *FINDWINDOW) (LPCTSTR, LPCTSTR);
定义线程所嵌入代码的地址结构:
typedef struct {
SETWINDOWLONG fnSetWindowLong; // SetWindowLong()函数地址
CALLWINDOWPROC fnCallWindowProc; // CallWindowProc()函数地址
FINDWINDOW fnFindWindow; // FindWindow()函数地址
char szClassName[50]; // 类名称为"SAS Window class"
char szWindowName[50]; // 窗口名称为"SAS window"
HWND hwnd; // 进程所嵌入的窗口句柄
WNDPROC fnSASWndProc; // 远程SASWindowProc 地址
WNDPROC fnOldSASWndProc; // 原始SASWindowProc 地址
} INJECTDATA, *PINJECTDATA;
3. 定义嵌入代码的功能函数
对远程线程嵌入的代码进行功能划分,子类化SAS窗口模块、子类化窗口进程的远程线程模块、恢复被子类化的窗口进程的远程线程设置模块、返回远程线程的窗口句柄(Winlogon)模块四个功能函数,下面以子类化SAS窗口进行嵌入代码为演示,其余函数编码与此类似:
/*********** 子类化SAS窗口以进行代码嵌入 ****************/
#pragma optimize("", off)
#pragma check_stack(off)
static LRESULT CALLBACK SASWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// INJECTDATA 结构指针必须在运行时处理
INJECTDATA* pData = (INJECTDATA *)DUMMY_ADDR;
if (uMsg == WM_HOTKEY)
{
// Ctrl+Alt+Del
if (lParam == MAKELONG(MOD_CONTROL | MOD_ALT, VK_DELETE))
return 1;// Ctrl+Shift+Esc
if (lParam == MAKELONG(MOD_CONTROL | MOD_SHIFT, VK_ESCAPE))
return 1;
}
// 缺省处理
return pData->fnCallWindowProc(pData->fnOldSASWndProc, hwnd, uMsg, wParam, lParam);
}
static int AfterSASWindowProc(void) { return 1; }Socket:假如这两个函数是上述排列,下面语句可得到嵌入线程代码的长度:
DWORD size = (PBYTE)AfterSASWindowProc - (PBYTE)SASWindowProc;4. 远程线程嵌入代码实现
此时进行内存分配操作,远程线程空间嵌入用户编写的代码。将上述功能函数和地址结构INJECTDATA复制到远程线程空间,在默认的WinLogon窗口的进程内,远程调用函数InjectFunc(),进行Ctrl+Alt+Del组合热键屏蔽。
具体实现是在远程线程地址空间内依次写入GetSASWnd()代码模块拷贝、SASWindowProc()模块拷贝和初始化的INJECTDATA地址结构的拷贝。上述操作成功后,进行InjectFunc()函数拷贝的执行。
// 在远程线程内分配内存并存放函数InjectFunc()的拷贝
size = (PBYTE)AfterInjectFunc - (PBYTE)InjectFunc;
pCodeRemote = (PBYTE) VirtualAllocEx(hProcess, 0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!pCodeRemote)
__leave;
if (!WriteProcessMemory(hProcess, pCodeRemote, &InjectFunc, size, &dwNumBytesCopied) || dwNumBytesCopied != size)
__leave;// 远程调用函数InjectFunc()的代码
hThread = CreateRemoteThread(hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE) pCodeRemote,
pDataRemote,
0 ,
&dwThreadId);
if (!hThread)
__leave;// 等待函数InjectFunc() 执行结束并得到返回码
WaitForSingleObject(hThread, INFINITE);
GetExitCodeThread(hThread, (PDWORD) &nSuccess);// 成功
if (nSuccess)
MessageBeep(MB_OK);
5. 复原系统设置
在这一步中进行出错处理和内存释放等系统复原工作,利用上述代码嵌入技术执行函数EjectFunc()完成清除卸载工作。// 释放结构INJECTDATA和函数SASWindowProc()所占内存
if (pDataRemote)
VirtualFreeEx(hProcess, pDataRemote, 0, MEM_RELEASE);
if (pSASWinProcRemote)
VirtualFreeEx(hProcess, pSASWinProcRemote, 0, MEM_RELEASE);
pDataRemote = NULL;
pSASWinProcRemote = NULL;
MessageBeep(MB_OK); // 成功END: // 如果API调用出错,程序转向此进行清除处理
if (hThread)
CloseHandle(hThread);
// 释放函数EjectFunc() 所占用的内存
if (pCodeRemote)
VirtualFreeEx(hProcess, pCodeRemote, 0, MEM_RELEASE);
CloseHandle(hProcess);
// 恢复原先权限,禁止调试权限
EnablePrivilege(SE_DEBUG_NAME, FALSE);
return nSuccess; // 0 = failure; 1 = success演示程序在Windows2K+SP4平台上用VC6.0+SP6调试通过,在NT/XP平台上测试正常。程序代码中根据需要实时对编译器环境作了调整,关闭了增量链接模式、优化和堆栈检查功能。由于DEBUG开发环境的设置出现了编译警告,但在RELEASE环境中编译正常。代码经过优化修改后已被某商用项目采用,较圆满实现了锁定NT/2K/XP的特殊组合键Ctrl+Del+Alt,最后感谢《黑客防线》编程类文章给予作者的灵感!