VC API常用函數簡單例子大全六

第五十一個SetWindowsHookEx安裝一個鉤子

WINDOWS是基於消息的系統,鼠標移動,單擊,鍵盤按鍵,窗口關閉等都會產生相應的消息,那麼鉤子是什麼意思呢,它可以監控一個消息,比如在一個窗口裏單擊了一下,首先獲得這個消息的,不是應用程序,而是系統,系統獲取這個消息後,就去查看這個消息是在哪個窗口產生的,找到窗口後,再把消息投遞到相應程序裏的消息隊列裏,這之間有一個傳遞過程,那麼鉤子的作用就是在消息到達應用程序之前截獲它,鉤子可以關聯一個函數(鉤子處理函數),也就是說,如果對一個進程安裝了一個鉤子,進程再接收到相應在消息之前,會先去執行鉤子所關聯的函數,

先來看一下這個函數定義:

HHOOK WINAPI SetWindowsHookEx(int idHook,HOOKPROC lpfn,HINSTANCE hmod,DWORD dwThreadId)

第一個參數idHook指明要安裝的鉤子類型,如WH_KEYBOARD(鍵盤鉤子),WH_MOUSE(鼠標鉤子),第二個參數是鉤子處理函數的地址,該函數必須是這種固定的格式:LRESULT WINAPI HookProc(int nCode,WPARAM wParam,LPARAM lParam)

第三個參數hmod是鉤子函數所在模塊的句柄,第四個參數dwThreadId是線程ID,待監視消息的ID,如果爲0,則爲全局鉤子,監視所有消息

好,接下來我們舉一個例子,鉤子類型爲WH_KEYBOARD,全局鉤子。截獲鍵盤按鍵消息,並扔掉該消息,讓鍵盤失靈。

由於是裝的是全局鉤子,所以鉤子處理函數必須放在動態鏈接庫裏。那麼我們就設計一個動態鏈接庫吧。

現給出動態鏈接庫的所有代碼:(KeyDll.dll)

#include "stdafx.h"
#include<windows.h>
BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
      )
{
    return TRUE;
}
HMODULE WINAPI ModuleFromAddress(PVOID pv)//該函數根據內存地址,獲得其所在的模塊句柄
{
 MEMORY_BASIC_INFORMATION mbi;
 VirtualQuery(pv,&mbi,sizeof(mbi));
 return (HMODULE)mbi.AllocationBase;
}
LRESULT CALLBACK HookKey(int nCode,WPARAM wParam,LPARAM lParam)
{
 return TRUE;//返回真,扔掉該消息
}
extern "C" __declspec(dllexport) void SetHook(void)
{
 SetWindowsHookEx(WH_KEYBOARD,HookKey,ModuleFromAddress(HookKey),0);
}

生成dll文件後,把它複製到相應的目錄下去。

再新建一個工程,調用用動態鏈接庫裏的函數,代碼如下:

#include<windows.h>
int main()
{
 HMODULE hMod=LoadLibrary("KeyDll.dll");
 typedef void(*pSetHook)(void);
 pSetHook SetHook=(pSetHook)GetProcAddress(hMod,"SetHook");
 SetHook();
 while(1)
 {
  Sleep(1000);//避免程序結束,自動釋放動態鏈接庫
 }
 return 0;
}

這樣當按下了一個鍵後,接收該按鍵消息的進程,會先去執行鉤子處理函數,然後再處理消息,而鉤子處理函數的幾個參數說明了按鍵的詳細信息,如按了哪個鍵,是按下(KEYDOWN)還是鬆開(KEYUP)。如果有興趣的話,把上面那鉤子處理函數的代碼換成下面這個

LRESULT CALLBACK HookKey(int nCode,WPARAM wParam,LPARAM lParam)
{
 char sz[25];
 sprintf(sz,"%c",wParam);//這個函數頭文件#include<stdio.h>
 MessageBox(NULL,sz,sz,MB_OK);
 return FALSE;
}

每按下一個鍵,就會彈出一個提示框,並輸出所按下的鍵,只對字符鍵有用。

第五十二個SHGetFileInfo獲取一個文件的各項信息(文件關聯圖標,屬性等)
函數定義: DWORD SHGetFileInfo(LPCSTR pszPath, DWORD dwFileAttributes, SHFILEINFOA FAR *psfi, UINT cbFileInfo, UINT uFlags);
pszPath是文件的路徑,dwFileAttributes一般取0,如果想要獲取文件夾信息的話,則取值爲FILE_ATTRIBUTE_DIRECTORY,psfi是一個SHFILEINFO結構的指針,該結構存儲文件信息,定義如下:
typedef struct _SHFILEINFOA
{
        HICON       hIcon;                      // 文件關聯圖標句柄
        int         iIcon;                      // 系統圖標列表索引
        DWORD       dwAttributes;               // 文件的屬性
        CHAR        szDisplayName[MAX_PATH];    // 文件的路徑名
        CHAR        szTypeName[80];             // 文件的類型名,如是bmp文件,還是執行文件exe,或者其它
} SHFILEINFO;
第四個參數cbFileInfo指明SHFILEINFO結構的大小,填sizoef(SHFILEINFO);
最後一個參數uFlags指定獲取文件的什麼信息,可選取值如下:(對應着SHFILEINFO裏的成員)
    SHGFI_ICON; //獲得圖標
  SHGFI_DISPLAYNAME; //獲得顯示名
  SHGFI_TYPENAME; //獲得類型名
  SHGFI_USEFILEATTRIBUTES; //獲得屬性
  SHGFI_LARGEICON; //獲得大圖標
  SHGFI_SMALLICON; //獲得小圖標
  SHGFI_PIDL; // pszPath是一個標識符
比如,我只要獲取文件圖標,那麼參數填SHGFI_LARGEICON就行了。如果又想獲取文件關聯的圖標,又想獲取文件類型名,那麼就是
SHGFI_LARGEICON|SHGFI_TYPENAME;
函數例子:
  SHFILEINFO   sfi;
  SHGetFileInfo("e:\\aa.bmp",0,&sfi,sizeof(sfi),
  SHGFI_ICON|SHGFI_LARGEICON|SHGFI_USEFILEATTRIBUTES|SHGFI_TYPENAME);

接着可以用DrawIcon函數畫出文件關聯圖標:該函數定義:BOOL DrawIcon(HDC hDC,int X,int Y, HICON hlcon );

第五十三個RegCreateKeyEx在註冊表裏創建一個子鍵,或獲取一個子鍵的句柄
在這裏我們先來了解一下註冊表的基本概念,打開運行對話框,輸入regedit,然後回車,便打開了註冊表編輯器,首先映入眼前的,便是五個根鍵
HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USER
HKEY_CURRENT_CONFIG

在根鍵下面便是主鍵了,如HKEY_CURRENT_CONFIG根鍵下有兩個主鍵,分別是Software和System(可能會不一樣),那麼主鍵下面是什麼呢,對了,就是跟 RegCreateKeyEx函數相關的子鍵,子鍵下面就是具體的鍵值項了,但也可以又是子鍵。鍵值有五種可選類型,分別是:字符串值(REG_SZ),二進制值(REG_BINARY),DWORD值(REG_DWORD),多字符串值(REG_MULTI_SZ)和可擴充字符值(REG_EXPAND_SZ)。鍵值項還有其它信息,它的名稱,數據。

瞭解了上面這些東西,接着就來了解下RegCreateKeyEx函數的各個參數吧,先來看一下函數定義:

LONG RegCreateKeyEx (

    HKEY hKey,//根鍵句柄,指明要在哪個根鍵下創建子鍵,填根鍵名既可
    LPCSTR lpSubKey,//子鍵名,包含完整路徑名
    DWORD Reserved,.//一般取0
    LPSTR lpClass,//一般取NULL
    DWORD dwOptions,//創建子鍵時的選項,可選值REG_OPTION_NON_VOLATILE,REG_OPTION_VOLATILE,這裏取0既可
    REGSAM samDesired,//打開方式,填KEY_ALL_ACCESS,在任何情況都行。
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,//指定繼承性,還是取0
    PHKEY phkResult,//子鍵對應句柄,待創建或打開的子鍵句柄將存儲在該句柄裏
    LPDWORD lpdwDisposition//打開還是創建子鍵,對應REG_CREATED_NEW_KEY和REG_OPENED_EXISTING_KEY
    );

在這裏舉一個例子,以便我們能更好的理解該函數。

在HKEY_CURRENT_CONFIG根鍵下的Software主鍵裏創建一個名爲MySelf的子鍵。

#include<windows.h>
int main()
{
  HKEY hroot;//子鍵句柄
 DWORD dwDisposition;//對應着最後一個參數
 RegCreateKeyEx(HKEY_CURRENT_CONFIG,"Software\\MySelf",0,NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);
  return 0;
}

第五十四個RegSetValueEx根據子鍵句柄在其下創建或修改一個鍵值

函數定義:LONG RegSetValueEx(
  HKEY hKey,           // 子鍵句柄
  LPCTSTR lpValueName, // 鍵值名稱,如果提供的子鍵下沒有該名稱,則創建
  DWORD Reserved,      // 保留,填0
  DWORD dwType,        // 鍵值類型,
  CONST BYTE *lpData,  // 鍵值的數據
  DWORD cbData         // 鍵值的數據的大小
);

接着我們以增加開機自啓動爲例,來看一下函數是如何創建一個鍵值的,我們知道,像程序添加開機自啓動一般都在

HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run下添加一個鍵值,鍵值類型爲二進制(REG_SZ),而鍵值的數據就爲要自啓動程序的路徑。

假設e盤下有一個AutoRun.exe的應用程序,讓電腦開機時自動運行它。

#include<windows.h>
int main()
{
  HKEY hroot;//子鍵句柄
  DWORD dwDisposition;
  RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
   NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);
  RegSetValueEx(hroot,"AutoRun",0,REG_SZ,(BYTE *)"e:\\AutoRun.exe",sizeof("e:\\AutoRun.exe"));
  return 0;
}

第五十五個RegDeleteValue根據子鍵句柄刪除其下的一個鍵值

這裏直接舉一個例子,刪除RegSetValueEx函數創建的鍵值

#include<windows.h>
int main()
{
  HKEY hroot;//子鍵句柄
  DWORD dwDisposition;
  RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
   NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);
  RegDeleteValue(hroot,"AutoRun");//刪除子鍵下名爲AutoRun的鍵值
  return 0;
}

第五十六個RegQueryValueEx根據子鍵句柄獲取一個鍵值數據,類型。

函數定義:LONG
RegQueryValueEx (
    HKEY hKey,//根鍵句柄
    LPCWSTR lpValueName,//鍵值名稱
    LPDWORD lpReserved,//預留,填0
    LPDWORD lpType,//接收鍵值類型
    LPBYTE lpData,//接收鍵值數據
    LPDWORD lpcbData//接收數據的大小
    );

例子,獲取RegSetValueEx函數創建的鍵值的類型,數據

#include<windows.h>
#include<stdio.h>
int main()
{
  char Data[52];
  DWORD Size,Type;
  HKEY hroot;//子鍵句柄
  DWORD dwDisposition;
  RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
   NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);//獲取根鍵句柄
  RegQueryValueEx(hroot,"AutoRun",0,&Type,(BYTE *)Data,&Size);//獲取AutoRun的信息
  printf("鍵值名稱:AutoRun ");
   switch(Type)
   {
   case REG_SZ:printf("鍵值類型:REG_SZ");break;
   case REG_BINARY:printf("鍵值類型:REG_BINARY");break;
   case REG_DWORD:printf("鍵值類型:REG_DWORD");break;
   case REG_MULTI_SZ:printf("鍵值類型:REG_MULTI_SZ");break;
   case REG_EXPAND_SZ:printf("鍵值類型:REG_EXPAND");break;
   }
   printf(" 鍵值數據:%s  %d\n",Data,Size);
  return 0;
}

第五十七個RegEnumValue根據子鍵句柄返回對應索引的鍵值信息(名稱,數據,類型,子鍵下第一個鍵值索引爲0,以此類推,函數成功執行返回ERROR_SUCCESS)

函數定義:LONG
RegEnumValue (
    HKEY hKey,//子鍵句柄
    DWORD dwIndex,//鍵值索引
    LPWSTR lpValueName,//接收鍵值名稱,字符數組
    LPDWORD lpcbValueName,//指明數組大小
    LPDWORD lpReserved,//預留,0
    LPDWORD lpType,//鍵值類型,填NULL,不獲取
    LPBYTE lpData,//鍵值數據,填NULL,不獲取
    LPDWORD lpcbData//接收數據的大小,如果鍵值數據那項參數爲NULL,則該項也爲NULL
    );

例子:輸出Run下的所有鍵值名

#include<windows.h>
#include<stdio.h>
int main()
{
  char Name[52];
  int Index=0;
  DWORD dwSize=52;
  DWORD Size,Type;
  HKEY hroot;//子鍵句柄
  DWORD dwDisposition;
  RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
   NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);//獲取根鍵句柄
  while(RegEnumValue(hroot,Index,Name,&dwSize,NULL,NULL,NULL,NULL)==ERROR_SUCCESS)
  {
   printf("%s\n",Name);
   Index++;//索引從0開始每次自增一,函數如果執行失敗,則索引已到頭
  }
  return 0;
}

其實也還可以擴充一下,可以像msconfig程序那樣列出當前計算機的所有開機自啓動程序,當然,註冊表也不只就前面的那一個子鍵下可以添加自啓動程序,在HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run下也可以添加,所以這些子鍵都需要去查看,更多添加自啓動程序的子鍵可以到百度裏去搜一下,大家如果掌握前面那幾個註冊表操作函數,可以結合起來試着做一個可以添加,查看,刪除開機自啓動程序的小程序。

第五十八個ExitWindowsEx關機,重啓,註銷計算機函數

這個函數只有兩個參數,後一個參數爲系統預留,填0就可以了,而第一個參數則,指明關機,還是重啓,或註銷,可選值如下:

EWX_LOGOFF//註銷    EWX_REBOOT//重啓 NT系統中需SE_SHUTDOWN_NAME 特權 EWX_SHUTDOWN//關機,需權限。

例子:關閉計算機,由於需要SE_SHUTDOWN_NAME權限,所以我們得先提升權限,代碼如下:

#include<windows.h>
int main()
{
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken);
LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&tkp.Privileges[0].Luid);
tkp.PrivilegeCount=1;
tkp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken,FALSE,&tkp,0,(PTOKEN_PRIVILEGES)NULL,0);
::ExitWindowsEx(EWX_SHUTDOWN,0);
  return 0;
}

第五十九個VirtualAllocEx在其它的進程中分配內存空間

函數定義:LPVOID
VirtualAllocEx(
    HANDLE hProcess,//進程句柄,將會在該進程句柄相關的進程分配空間
    LPVOID lpAddress,//默認爲系統指定,填NUL
    DWORD dwSize,//分配多大的內存
    DWORD flAllocationType,//填MEM_COMMIT
    DWORD flProtect//指定分配的內存屬性,爲PAGE_READWRITE,內存可讀寫
    );

函數返回分配的內存首地址,

第六十個CreateRemoteThread創建一個遠程線程(在其它進程中創建線程)

函數定義:HANDLE
WINAPI
CreateRemoteThread(HANDLE hProcess,//進程句柄,函數將在這個進程句柄關聯的進程創建線程

LPSECURITY_ATTRIBUTES lpThreadAttributes,

 DWORD dwStackSize,
    LPTHREAD_START_ROUTINE lpStartAddress,
    LPVOID lpParameter,
    DWORD dwCreationFlags,
    LPDWORD lpThreadId
    );

這個函數比CreateThread函數多了一個參數,就是這個函數的第一個hProcess(函數在該進程裏創建線程),後面的六個參數跟第三十九個函數CreateThread的六個參數一樣,這裏就不再解釋了。

例子:遠程線程注入

創建一個遠程線程,就必須得有一個線程函數供線程執行,而線程函數又不能在其它程序裏。那要怎麼辦呢?大家看一下線程函數的定義,和LoadLibrary函數的定義,它們的定義相似,都是隻有一個參數,而且每個程序都能調用LoadLibrary函數,這樣我們便能把LoadLibrary函數作爲線程函數。這樣創建的線程就會去執行LoadLibrary函數。因而我們就有了一次讓其它程序調用LoadLibrar函數的機會,並還可以指定LoadLibrary函數的參數(通過創建遠程線程函數傳遞)。前面在動態鏈接庫提到,一個程序如果調用LoadLibrary函數,它都會自動去執行相應動態鏈接庫裏的DllMain函數,所以我們自己可以編寫一個動態鏈接庫,在DllMain函數裏寫入想要其它程序執行的代碼。再通過CreateRemoteThread函數在其它程序創建一個線程去執行LoadLibary加載我們已經編寫好的動態鏈接庫,這樣就可以讓其它程序執行我們的代碼了。這裏還有一個問題,CreateRemoteThread函數傳遞過去的參數,因爲要供注入的那個程序訪問,所以參數數據所存儲的空間不能在調用CreateRemoteThread函數的程序裏。必須調用VirtualAllocEx函數,在注入程序裏分配一個空間,把數據(動態鏈接庫的名稱)存在裏面,而新分配空間的首地址則作爲CreateRemoteThread函數的參數傳過去。這樣注入程序訪問的是自己的地址空間。

遠程線程注入:

假設動態鏈接庫爲“ReCode.dll”它的代碼如下:

#include<windows.h>
BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
      )//DllMain函數,只要加載這個動態鏈接庫的程序,都會跑來執行這個函數
{//在這裏填讓其它程序執行的代碼
 while(1)
 {
 MessageBox(NULL,"aaaa","aaaa",MB_OK);//簡單的讓其它程序每隔3秒彈出一個提示框
 Sleep(3000);
 }
    return TRUE;
}

編譯運行,然後把生成的“ReCode.dll”文件複製到c:\\windows\\system23下去。

注入線程的代碼:

//選擇ctfmon.exe(輸入法管理)作爲我們要注入進線程的程序
#include<windows.h>
#include<tlhelp32.h>
#include<stdio.h>
int main()

  char DllName[25]="ReCode.dll";
 HANDLE hProcess;//用於存儲ctfmon.exe的進程句柄
 //先提升進程權限,使其能獲取任何進程句柄,並對其進行操作
  HANDLE hToken;
     OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken );
     TOKEN_PRIVILEGES tp;
     LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid );
     tp.PrivilegeCount = 1;
     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
     AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof( TOKEN_PRIVILEGES ), NULL, NULL );
  ////////////////////////////////////////////////////////////////////////////
     //Process32First和Process32Next函數結合(尋找)獲取ctfmon.exe進程ID號
  //再調用OpenProcess函數根據進程ID獲得進程句柄
     PROCESSENTRY32 pe32;//進程相關信息存儲這個結構裏
     pe32.dwSize=sizeof(pe32);
      //給系統內的所有進程拍一個快照
     HANDLE hProcessSnap=::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
     BOOL bMore=::Process32First(hProcessSnap,&pe32);
      while(bMore)
   {
      if(strcmp("ctfmon.exe",pe32.szExeFile)==0)//如果找到進程名爲ctfmon.exe
      hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pe32.th32ProcessID);//獲取句柄
       bMore=::Process32Next(hProcessSnap,&pe32);//尋找下一個
   }
 
   //在ctfmon進程中分配空間
  LPVOID lpBuf=VirtualAllocEx(hProcess,NULL,strlen(DllName),MEM_COMMIT, PAGE_READWRITE );
  DWORD WrSize;
  //把DllName裏的數據寫入到分配的空間裏
   WriteProcessMemory(hProcess, lpBuf, (LPVOID)DllName, strlen(DllName), &WrSize);
   //創建遠程線程
   CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)LoadLibraryA,lpBuf,0,NULL);
   return 0;//程序使命完成,結束
}

當然,給一個程序安裝鉤子,也可以讓指定的應用程序加載特定的動態鏈接庫,但要了解,加載動態鏈接庫的是是應用程序的主程序,你總不能讓應用程序不干它自己的事,而來一直執行DllMain函數裏的代碼吧!而且即使這樣,當安裝鉤子的程序退出或卸載鉤子的時候,那麼被系統強迫加載動態鏈接庫的程序,也會自動釋放動態鏈庫,退出DllMain函數。如此,那就沒有辦法了嗎?,辦法肯定是有的,用CreateThread函數。當其它程序主線程執行DllMain函數的時候,使其調用CreateThread再創建一個線程,就行了。

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