程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 使用Windows鉤子獲取丟失的密碼

使用Windows鉤子獲取丟失的密碼

編輯:關於VC++

簡介

幾年前我在CodeGuru 上下載了一個叫 Eureka的程序,如果你忘記了密碼,你可以用程序把密碼“取”回來。它不是密碼破解程序,相反,它利用了一個Windows的安全漏洞來拷貝另外一個運行中的程序的密碼。我對這個程序很感興趣,決定寫一個自己的版本。後來,Windows 2000 發布,我失望地發現,微軟修補了那個漏洞,這樣一來那個程序在Windows 2000上也就不靈了。經過一番嘗試,我終於找到一個方法拷貝任何在32-位Windows 系統上運行程序的密碼。

本文例子程序:

使用方法:

PasswordSpy程序的使用非常簡單。你只要運行包含忘記了密碼的程序,再運行PasswordSpy。然後將放大鏡拖動到密碼輸入域上,PasswordSpy則會將密碼顯示出來。PasswordSpy程序並沒有惡意,開發它的目的只是想把密碼找回來,該程序在Win95/98 /ME and WinNT/2K/XP/Windows 2003 上測試通過。

功能說明:

除了PasswordSpy本身的用途之外,它還示范了一些有用和有趣的代碼:

單實例應用——如果用戶啟動了PasswordSpy的第二個實例,系統會找到第一個實例,並在所有窗口的最前端顯示出PasswordSpy界面;

Always on top——總是在最頂層,只用一行代碼就可以在你的程序中啟動和禁用這個功能;

進程間通訊——PasswordSpy 使用幾種形式的IPC,包括WM_COPYDATA消息以及內存映射文件;

設置窗口鉤子——為了在Windows 2000/Windows XP中吸取密碼,你得使用在遠程進程中置入一個鉤子。

代碼實現細節:

到目前為止,PasswordSpy 程序最有趣的部分其實是使用 SetWindowsHookEx API.函數設置Windows 鉤子。利用該函數你可以將鉤子安裝到操作系統中或者某個特定的進程中。鉤子的種類有很多種,每種鉤子作用也不盡相同,用來監視特定的一組事件。當某一類事件發生時,鉤子代碼被調用。PasswordSpy使用WH_GETMESSAGE鉤子,它監視對GetMessage 和PeekMessage 的調用。關於鉤子更詳細的信息請參考MSDN庫的SetWindowsHookEx。

我在網上、書本以及MSDN上找到幾個有關鉤子的例子,每個都至少有一個Bug。本文我用自己的方案解決了這些問題。使用Windows鉤子最難的部分是妥善存儲鉤子句柄。設置鉤子之前,你需要做兩件事情:

包含鉤子函數的DLL;

鉤子要作用的線程ID;

現在假設進程A要在進程B中設置鉤子。鉤子注入進程B以後,該鉤子句柄被返回到進程A,同時DLL被映射到進程B的地址空間。當進程B中某個鉤子事件發生時,你的鉤子代碼便在進程B中被調用。(應該注意的一點是你的鉤子代碼是從遠程進程空被調用的!鉤子代碼裡,如果調用 GetCurrentProcessId 函數,那麼獲得的是鉤子進程的ID,而不是設置鉤子的進程ID)。你可以在鉤子代碼中做任何想做的事情,但是在鉤子代碼退出之前,你應該調用 CallNextHookEx。如果這個函數調用失敗,其它任何已經安裝的鉤子無法獲得消息。因為CallNextHookEx需要該鉤子句柄,但我們當前是在進程B中,而句柄被返回到進程A,因此,此時需要進程間通訊來傳輸鉤子句柄。

通常解決這個問題的方法是通過在DLL中創建一個“共享”內存區:

#pragma data_seg("Shared")
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/section:Shared,rws")

其實,此處創建了一個變量,所有已經被加載的DLL實例共享這個變量。但這個方法有幾個問題,第一,有些編譯器不支持這個選項,第二,如果微軟要是在未來的Windows中改變“共享”內存區的工作模式該怎麼辦?這意味著這個技術不再適用。此外,這個方法不是線程同步的,由於有多個線程要訪問這個變量,線程同步就變得很重要。為了解決這些問題,我在進程間通訊(IPC)過程中使用了內存映射文件,並利用互斥機制進程線程同步。我將這方面的代碼全部封裝在一個類中,這個類就是 CIPC。通過使用內存映射文件,我解決了特定編譯器選項問題,不用再考慮編譯器是否支持共享內存區;僅僅借助Win32 API調用以及用MMFs來提供多進程間共享數據的機制就可以解決問題,在未來的Windows版本中,這些東西是不太可能改變的。互斥保證了線程存取的同步: //***********************************************
// IPC.h
//***********************************************
#ifndef _IPC_H_
#define _IPC_H_

#define IPC_SHARED_MMF _T("{34F673E0-878F-11D5-B98A-00B0D07B8C7C}")
#define IPC_MUTEX    _T("{34F673E1-878F-11D5-B98A-00B0D07B8C7C}")

// 使用內存映射文件進行進程間通訊的封裝類
class CIPC
{
public:
 CIPC();
 virtual ~CIPC();

 bool CreateIPCMMF(void);
 bool OpenIPCMMF(void);
 void CloseIPCMMF(void);

 bool IsOpen(void) const {return (m_hFileMap != NULL);}

 bool ReadIPCMMF(LPBYTE pBuf, DWORD &dwBufSize);
 bool WriteIPCMMF(const LPBYTE pBuf,
          const DWORD dwBufSize);

 bool Lock(void);
 void Unlock(void);

protected:
 HANDLE m_hFileMap;
 HANDLE m_hMutex;
};

#endif

//***********************************************
// IPC.cpp
//***********************************************
#include "IPC.h"

//***********************************************
CIPC::CIPC() : m_hFileMap(NULL), m_hMutex(NULL)
{
}

//***********************************************
CIPC::~CIPC()
{
  CloseIPCMMF();
  Unlock();
}

//***********************************************
bool CIPC::CreateIPCMMF(void)
{
 bool bCreated = false;

 try
 {
   if(m_hFileMap != NULL)
    return false;  // Already created

   // Create an in-memory 4KB memory mapped
   // file to share data
   m_hFileMap = CreateFileMapping((HANDLE)0xFFFFFFFF,
     NULL,
     PAGE_READWRITE,
     0,
     4096,
     IPC_SHARED_MMF);
   if(m_hFileMap != NULL)
    bCreated = true;
 }
 catch(...) {}

 return bCreated;
}

//***********************************************
bool CIPC::OpenIPCMMF(void)
{
  bool bOpened = false;

  try
  {
    if(m_hFileMap != NULL)
      return true;  // Already opened

    m_hFileMap =
     OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE,
      FALSE,
      IPC_SHARED_MMF);
    if(m_hFileMap != NULL)
      bOpened = true;
  }
  catch(...) {}

  return bOpened;
}

//***********************************************
void CIPC::CloseIPCMMF(void)
{
  try
  {
    if(m_hFileMap != NULL)
      CloseHandle(m_hFileMap), m_hFileMap = NULL;
  }
  catch(...) {}
}

//***********************************************
bool CIPC::ReadIPCMMF(LPBYTE pBuf, DWORD &dwBufSize)
{
 _ASSERTE(pBuf);

 bool bSuccess = true;

 try
 {
   if(m_hFileMap == NULL)
     return false;

   DWORD dwBaseMMF = (DWORD)MapViewOfFile(m_hFileMap,
        FILE_MAP_READ | FILE_MAP_WRITE,
     0, 0, 0);
   _ASSERTE(dwBaseMMF);

   // The first DWORD in the MMF contains the size of the data
   DWORD dwSizeofInBuf = dwBufSize;
   CopyMemory(&dwBufSize, (LPVOID)dwBaseMMF, sizeof(DWORD));

   if(dwSizeofInBuf != 0)
   {
     if(dwBufSize > dwSizeofInBuf)
       bSuccess = false;
     else
       CopyMemory(pBuf,
         (LPVOID)(dwBaseMMF + sizeof(DWORD)),
         dwBufSize);
   }

   UnmapViewOfFile((LPVOID)dwBaseMMF);
 }
 catch(...) {}

 return bSuccess;
}

//***********************************************
bool CIPC::WriteIPCMMF(const LPBYTE pBuf, const DWORD dwBufSize)
{
  _ASSERTE(pBuf);

  bool bSuccess = true;

  try
  {
    if(m_hFileMap == NULL)
      return false;

    DWORD dwBaseMMF = (DWORD)MapViewOfFile(m_hFileMap,
      FILE_MAP_READ | FILE_MAP_WRITE,
      0, 0, 0);
    _ASSERTE(dwBaseMMF);

    // The first DWORD in the MMF contains the size of the data
    CopyMemory((LPVOID)dwBaseMMF, &dwBufSize, sizeof(DWORD));
    CopyMemory((LPVOID)(dwBaseMMF + sizeof(DWORD)),
              pBuf,
              dwBufSize);

    UnmapViewOfFile((LPVOID)dwBaseMMF);
  }
  catch(...) {}

  return bSuccess;
}

//***********************************************
bool CIPC::Lock(void)
{
  bool bLocked = false;

  try
  {
    // First get the handle to the mutex
    m_hMutex = CreateMutex(NULL, FALSE, IPC_MUTEX);
    if(m_hMutex != NULL)
    {
      // Wait to get the lock on the mutex
      if(WaitForSingleObject(m_hMutex, INFINITE) ==
                  WAIT_OBJECT_0)
        bLocked = true;
    }
  }
  catch(...) {}

  return bLocked;
}

//***********************************************
void CIPC::Unlock(void)
{
  try
  {
    if(m_hMutex != NULL)
    {
      ReleaseMutex(m_hMutex);
      CloseHandle(m_hMutex);
      m_hMutex = NULL;
    }
  }
  catch(...) {}
}

PasswordSpy的反向工程

上述內容是關於通過編程途徑從其它程序“拷貝”密碼,下面的內容我們將討論:如何防止PasswordSpy這樣的程序從你的程序中獲取密碼信息?如果你的應用程序存儲並顯示密碼,你有特別關注安全問題,你可能會考慮讓應用堤防類似PasswordSpy這樣的程序。

因為PasswordSpy可以拷貝其它程序的密碼,為了防范這樣的程序,你首先就決不能將真正的密碼信息顯示出來。最佳辦法是在密碼框中顯示假密碼。這樣一來,如果有人使用PasswordSpy 獲取密碼,那他們得到的是一個假密碼。本文附帶的程序AntiPwdSpy示范了如何保護密碼框控件免遭“偵測”。AntiPwdSpy實際上類似於Windows NT服務對話框和Windows NT 用戶管理程序。

防范諸如PasswordSpy這類程序的其它方法還可以通過截獲WM_GETTEXT消息來實現。但使用虛假密碼的方法有額外的好處,用假密碼替代真密碼,僅檢查密碼控件是無法確定密碼的長度的。如果某個程序顯示密碼控件中的文本“***”,那麼就可以知道密碼是三位長度。這樣的信息肯定危及密碼的安全。但是,如果將密碼控件中的信息顯示成“**************”便可以有效地保護密碼,微軟的很多程序產品就是這麼做的。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved