遠端注入執行緒,遮蔽Ctrl+Alt+Del
關鍵字 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 :
//日期 :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 :
//日期 :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,最後感謝《黑客防線》程式設計類文章給予作者的靈感!