鉤子的應用: 程序運行監視(轉自C語言帝國)

程序介紹:

利用這個程序:
1.可以監視在你的電腦運行的程序, 把在你的電腦運行過的程序的時間和名字記錄下來;
2.可以阻止你規定的禁用程序的執行, 比如不讓玩遊戲。
3.這個程序需要加入註冊表, 在系統啓動時就運行, 達到監視的目的。註冊表大概都不陌生,就是這裏:
HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/CurrentVersion /Run

程序的記錄格式:

2003-02-03 17:31:25 - [System Startup - Windows XP 5.01.2600]
2003-02-03 17:31:29 "CabinetWClass" -> "我的電腦"
2003-02-03 17:31:59 "Red Alert" -> "Red Alert" (關閉禁用程序)
2003-02-03 17:32:19 "掃雷" -> "掃雷" (關閉禁用程序)
2003-02-03 17:32:35 "OpusApp" -> "Microsoft Word"
2003-02-03 17:32:50 - [System Shutdown - 0 days, 0 hrs, 1 mins, 25 secs]

2003-02-03 17:35:37 - [System Startup - Windows 98 SE 4.10.2222]
2003-02-03 17:35:53 "掃雷" -> "掃雷" (關閉禁用程序)
2003-02-03 17:36:05 "CabinetWClass" -> ""
2003-02-03 17:36:31 "Red Alert" -> "Red Alert" (關閉禁用程序)
2003-02-03 17:36:56 "ExploreWClass" -> ""
2003-02-03 17:37:07 - [System Shutdown - 0 days, 0 hrs, 1 mins, 30 secs]

程序運行只需要3個文件:
  hwhpapp.exe 可執行文件
  hwhpdrv.dll 安裝鉤子的動態鏈接庫
  hwhpapp.cfg 禁用軟件黑名單, 可用記事本修改
程序運行會自動產生記錄文件:
  hwhpapp.sys 可以用記事本打開看

程序原理:

一.鉤子
利用 API 函數 SetWindowsHookEx() 安裝一個全局鉤子, 鉤子類型爲 WH_SHELL。
WH_SHELL 鉤子可監視所有應用程序的主窗口創建或者關閉。
最典型的應用就是 Windows 的狀態欄,當程序運行時,把主窗口的標題加入狀態欄,程序退出時,從狀態欄刪除。
如果你截獲這個鉤子,可以做到禁止狀態欄的顯示,也可以自己作一個狀態欄,或者作一個歷史記錄,把所有在你電腦曾經運行的程序記錄下來。如果運行的程序不是你希望的,你可以直接關閉這個程序,達到禁止運行的目的。

二.動態鏈接庫
由於鉤子是全局的,必須把這個鉤子定義到 .DLL 的動態鏈接庫裏,這就涉及到建立動態鏈接庫。

三.共享內存
由於鉤子是安裝到系統裏的,鉤子的運行是在操作系統裏面,因此這個鉤子不能使用你的程序所定義的任何全局變量!
既然如此,有什麼辦法解決呢?在本程序裏利用共享內存技術,利用 API 函數 CreateFileMapping() 可創建共享內存,這個內存可以在任何運行的程序中使用,也就是說任何運行的 .EXE、.DLL 和其他程序都可以使用這塊內存。在本程序中直接使用了 Victor 串口 VCL 控件裏的 TSharedMemory 共享內存類。

四.記錄文件和軟件黑名單文件
把所有在你的電腦執行的程序記錄保存在一個文本文件裏, 因爲擴展名爲 .txt 很容易被發現,因此採用擴展名 .sys
軟件黑名單文件保存在 .cfg 文件裏,同樣因爲 .ini 文件很容易發現並且打開修改。
這兩個文件都保存在與你的 .exe 文件的相同文件夾裏,並且與 .exe 文件同名.

五.保證你的程序只能同時運行一個
如果你同時運行兩個程序,記錄文件就會亂套,所以必須保證只能運行一個。
當你的程序剛開始運行的時候,就是在 WinMain() 函數的最開始,就要判斷是否已經運行了,如果已經運行,就直接退出。
判斷的方法很簡單,就是檢查程序共享的內存是否存在,如果檢查到共享的內存已經存在,那就是已經運行了。

六.程序隱身, 不能顯示在任務欄和任務管理器裏面
這個也很簡單,只要在主程序的 Application->Run(); 前面加一句話:
SetWindowLong(Application->Handle, GWL_EXSTYLE, GetWindowLong(Application->Handle, GWL_EXSTYLE)|WS_EX_TOOLWINDOW);
就可以了。


程序介紹
.DLL 文件:這是最關鍵的鉤子的代碼:

#include <vcl.h>
#include "yb_base.h" //Victor 串口控件裏面的一個頭文件

#define MYAPPMARK "VICTOR_APPMONI_20010612" //共享內存標誌

class __export THookedProcs
{
public:
 THookedProcs();
 ~THookedProcs();
 void WINAPI InitFuncs(void);
 void WINAPI UninitFuncs(void);
private:
 HHOOK hThisHook; //保存鉤子的句柄
 static LRESULT CALLBACK HookedShellProc(int nCode, WPARAM wParam, LPARAM lParam);
};

//定義共享的數據結構
typedef struct
{
 HHOOK hHook; //當前使用的 HOOK
 //... 此處可增加其他共享的數據
} THookSharedData;

THookedProcs::THookedProcs()
{
 hThisHook = NULL;
}

THookedProcs::~THookedProcs()
{
 UninitFuncs();
}

void WINAPI THookedProcs::InitFuncs(void)
{
 UninitFuncs();
 hThisHook = SetWindowsHookEx(WH_SHELL, (HOOKPROC) HookedShellProc, HInstance, 0);
 TSharedMemory AppMem(MYAPPMARK,4096); //在 EXE 文件裏共享的內存
 THookSharedData *HookSharedData = ((THookSharedData*)(AppMem.AppInfo->Data)); //共享的數據
 HookSharedData->hHook = hThisHook; //把 hThisHook 保存到共享內存裏
}

void WINAPI THookedProcs::UninitFuncs(void)
{
 if(hThisHook)
  {
   UnhookWindowsHookEx(hThisHook);
   hThisHook = NULL;
  }
}

LRESULT CALLBACK THookedProcs::HookedShellProc(int nCode, WPARAM wParam, LPARAM lParam)
{
 TSharedMemory AppMem(MYAPPMARK, 4096); //使用在 .EXE 文件裏共享的內存
 if(AppMem.Valid)if(AppMem.Exists) //如果共享內存存在
  {
   HWND hMainWnd = AppMem.AppInfo->hMainForm;

   if(hMainWnd)
    {
     if(nCode==HSHELL_WINDOWCREATED)
      {
        PostMessage(hMainWnd, WM_USERCMD, UC_WINHOOK, wParam);
      }
    }
  }

 //在 Hook 裏無法調用 hThisHook, 必須用共享內存裏面的 hHook
 THookSharedData *HookSharedData = ((THookSharedData*)(AppMem.AppInfo->Data)); //共享的數據
 return CallNextHookEx(HookSharedData->hHook, nCode, wParam, lParam);
}


EXE文件主程序的代碼:

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR lpCmdLine, int)
{
 if(!AppMem.Valid)
  {
   return 1;
  }
 if(AppMem.Exists) //已經存在 (程序以前已經運行過了, 並且正在運行)
  {
   if(stricmp(lpCmdLine, "/SHOW")==0) //如果監測到命令行參數 /SHOW 就顯示出已經運行的程序的主窗口
    {
     PostMessage(AppMem.AppInfo->hMainForm, WM_USERCMD, UC_SHOWWIN, 0);
    }
   return 0;
  }
 AppMem.ClearBuffer();

 try
  {
    Application->Initialize();
    Application->CreateForm(__classid(TFormMain), &FormMain);
    //下面的語句是防止顯示在狀態欄和任務管理器
    SetWindowLong(Application->Handle, GWL_EXSTYLE, GetWindowLong(Application->Handle, GWL_EXSTYLE)|WS_EX_TOOLWINDOW);
    Application->Run();
  }
 catch (Exception &exception)
  {
    Application->ShowException(&exception);
  }
 catch (...)
  {
    try
    {
      throw Exception("");
    }
    catch (Exception &exception)
    {
      Application->ShowException(&exception);
    }
  }
 return 0;
}


主窗口程序:

TSharedMemory AppMem(MYAPPMARK, 4096); //定義共享的內存,這個內存是真正存在的

__fastcall TFormMain::TFormMain(TComponent* Owner)
: TForm(Owner)
{
  AppMem.AppInfo->hMainWnd = Application->Handle;
  AppMem.AppInfo->hMainForm = Handle;
  WriteStartupMessage(); //在記錄文件里加入啓動信息
  PostMessage(Handle, WM_USERCMD, UC_INITWIN, 0);

  MyHook = new THookedProcs;
  MyHook->InitFuncs(); //安裝鉤子
}
//---------------------------------------------------------- -----------------
__fastcall TFormMain::~TFormMain()
{
  MyHook->UninitFuncs(); //刪除鉤子
  delete MyHook;
}
//---------------------------------------------------------- -----------------
void __fastcall TFormMain::FormCloseQuery(TObject *Sender, bool &CanClose)
{
  WriteShutdownMessage(); //在記錄文件里加入退出信息
}
//---------------------------------------------------------- -----------------
void __fastcall TFormMain::WndProc(Messages::TMessage &Message)
{
  if(Message.Msg == WM_USERCMD)
  {
    if(Message.WParam == UC_SHOWWIN)
    {
      Show();
      AppMem.ActiveAppWnd();
    }
    else if(Message.WParam == UC_INITWIN)
    {
      Hide();
      Left = (Screen->Width - Width) / 2;
      Top = (Screen->Height - Height) / 2;
    }
    else if(Message.WParam == UC_WINHOOK)
    {
      WinHookMessage(Message.LParam);
    }
  }
  TForm::WndProc(Message);
}
//---------------------------------------------------------- -----------------

void __fastcall TFormMain::BnExitClick(TObject *Sender)
{
  Close();
}
//---------------------------------------------------------- -----------------

void __fastcall TFormMain::BnHideClick(TObject *Sender)
{
  Hide();
}
//---------------------------------------------------------- -----------------
void __fastcall TFormMain::CreateParams(Controls::TCreateParams &Params)
{
  TForm::CreateParams(Params);
  Params.Style = WS_OVERLAPPED|WS_DLGFRAME|WS_CAPTION|WS_SYSMENU;
  Params.ExStyle |= WS_EX_TOPMOST;
  Params.X = Screen->Width - 8;
  Params.Y = Screen->Height - 8;
}
//---------------------------------------------------------- -----------------
void __fastcall TFormMain::WinHookMessage(long Param)
{
  HWND hWnd = (HWND) Param;

  char szWinCaption[256];
  char szWinClass[256];
  AnsiString s;

  DateTimeX x;
  s += AnsiString().sprintf("%04d-%02d-%02d %02d:%02d:%02d ", x.Year, x.Month, x.Day, x.Hour, x.Minute, x.Second);

  if(!GetWindowText(hWnd, szWinCaption, 256))
   *szWinCaption = 0;
  if(!GetClassName(hWnd,szWinClass,256))
   *szWinClass = 0;
  s += "/"" + AnsiString(szWinClass) + "/" -> /"" + AnsiString(szWinCaption) + "/"";

  if(!ValidAppCheck(hWnd, szWinClass,szWinCaption))
  s += " (關閉禁用程序)";
  s += "/r/n";

  TBinFileFuncs::WriteLogFileInfo(TRelPath().Extension("sys"). c_str(), s.c_str()); //與 .exe 同名的 .sys 文件
}
//---------------------------------------------------------- -----------------
void __fastcall TFormMain::WriteStartupMessage(void)
{
  AnsiString s;
  DateTimeX x = StartupTime;
  TSysInfo si;

  s += "========== Copyright (C) Victor Chen ===== Email: [email protected] ==========/r/n";
  s += AnsiString().sprintf("%04d-%02d-%02d %02d:%02d:%02d - ", x.Year, x.Month, x.Day, x.Hour, x.Minute, x.Second);
  s += AnsiString().sprintf("[System Startup - %s %d.%02d.%04d]", si.OS->OSName, si.OS->MajorVer, si.OS->MinorVer, si.OS->BuildNum);
  s += "/r/n";

  TBinFileFuncs::WriteLogFileInfo(TRelPath().Extension("sys"). c_str(), s.c_str()); //與 .exe 同名的 .sys 文件
}
//---------------------------------------------------------- -----------------
void __fastcall TFormMain::WriteShutdownMessage(void)
{
  AnsiString s;
  DateTimeX x;
  TimeX t; t.S000 = (x - StartupTime) % (24*60*60);
  int d = (x - StartupTime) / (24*60*60);

  s += AnsiString().sprintf("%04d-%02d-%02d %02d:%02d:%02d - ", x.Year, x.Month, x.Day, x.Hour, x.Minute, x.Second);
  s += AnsiString().sprintf("[System Shutdown - %d days, %d hrs, %d mins, %d secs]", d, t.Hour, t.Minute, t.Second);
  s += "/r/n";

  TBinFileFuncs::WriteLogFileInfo(TRelPath().Extension("sys"). c_str(), s.c_str()); //與 .exe 同名的 .sys 文件
}
//---------------------------------------------------------- -----------------
bool __fastcall TFormMain::ValidAppCheck(HWND hwnd, char *cls, char *cap)
{
  TBinFile f;
  f.FileName = TRelPath().Extension("cfg"); //與 .exe 同名的 .cfg 文件
  f.OpenMode = TBinFile::omRead; //準備讀文件

  if(f.Exists) //如果文件存在
  {
    try
    {
      f.Active = true; //打開文件
      char aLine[2048];
      while(fgets(aLine,2000,f)) //讀一行文本 (標準 C 函數)
      {
        if(strnicmp(aLine,"CLASS=",6)==0) //用類名識別
        {
          TBinFileFuncs::DeleteSpaces(aLine+6,1,1); //去除空格
          if(stricmp(aLine+6,cls)==0)
          {
             PostMessage(hwnd, WM_CLOSE, 0, 0); //關閉程序
             return false;
          }
        }
        else if(strnicmp(aLine,"CAPTION=",8)==0) //用標題識別
        {
          TBinFileFuncs::DeleteSpaces(aLine+8,1,1); //去除空格
          if(stricmp(aLine+8,cap)==0)
          {
             PostMessage(hwnd, WM_CLOSE, 0, 0); //關閉程序
             return false;
          }
        }
      }
    }
    catch(Exception &e)
    {
      //忽略錯誤提示, 也可以在此處把錯誤信息添加到一個錯誤信息文件
    }
  }
  return true;
}

 

原帖地址:http://www.vcgood.com/bbs/forum_posts.asp?tid=826

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章