Từ 1 tới 1 trên tổng số 1 kết quả

Đề tài: Lập trình hook | Hooking in VC++ CLR

  1. #1
    Ngày gia nhập
    10 2007
    Bài viết
    169

    Mặc định Lập trình hook | Hooking in VC++ CLR

    Thực ra cũng hơi "ăn gian" một chút nhưng chắc cũng có ích nên chia sẻ với mọi người!

    Ở đây mình chỉ dùng WH_KEYBOARD_LL, còn với mấy cái khác như : WH_CBT thì củng tương tự!

    1. Tạo project :
    - Đầu tiên chúng ta sẻ tạo một project của CLR (.NET trong VC++ ấy mà) loại "Windows Form Application"! Bạn đặc tên gì cũng được!
    - Sau khi tạo xong thì thêm vào form 2 button, tạm đặc tên là "Hook" và "UnHook"!

    - Kế đến chúng ta sẻ tạo một project cho file DLL! Giống như ở trên nhưng lần này là "Class library"! Trong project của mình đặc tên nó là "MSl", nếu bạn đặc tên khác thì trong code phải sửa lại tí xíu!

    2. Coding :
    - Đầu tiên chùng ta sẻ code cho "MSl"!
    + Mở file "Msl.h" lên và thêm phần này vào sau "#pragma once"
    Code:
    #pragma region Windows Version Define
    // Dont forget this, alway needed in .NET
    //#ifndef VC_EXTRALEAN
    //#define VC_EXTRALEAN			// Exclude rarely-used stuff from Windows headers
    //#endif
    
    #ifndef WINVER					// Allow use of features specific to Windows XP or later.
    #define WINVER 0x0501			// Change this to the appropriate value to target other versions of Windows.
    #endif
    
    #ifndef _WIN32_WINNT			// Allow use of features specific to Windows XP or later.                   
    #define _WIN32_WINNT 0x0501		// Change this to the appropriate value to target other versions of Windows.
    #endif						
    
    #ifndef _WIN32_WINDOWS			// Allow use of features specific to Windows 98 or later.
    #define _WIN32_WINDOWS 0x0410	// Change this to the appropriate value to target Windows Me or later.
    #endif
    
    #ifndef _WIN32_IE				// Allow use of features specific to IE 6.0 or later.
    #define _WIN32_IE 0x0600		// Change this to the appropriate value to target other versions of IE.
    #endif
    #pragma endregion
    
    #pragma region Include library
    #include "Windows.h"
    #pragma endregion
    
    #pragma region Imports assemblies
    #using "System.Windows.Forms.dll"    // Chỉ để dùng MesageBox::Show() thôi!
    #pragma endregion
    
    #pragma region Using namespaces declare
    using namespace System;
    using namespace System::Windows;
    #pragma endregion
    + Kế đến là namespace :
    Code:
    namespace RadicalLight    // Tui nè ^_^!
    {
    	namespace System    // Nhớ dùng ::System nếu muốn dùng asm System.dll
    	{
    		public ref class Hooking
    		{
    		public:
    			Hooking();
    			~Hooking();
    
    		// Init library ______________________________________________________________________
    		public:
    			void SetHookModule(HMODULE hModule);	// Init hooking module,must be called first!
    			BOOL SetHookModule(LPTSTR lpDllPath);
    
    		// Set hooks funcs ___________________________________________________________________
    		public:
    			BOOL SetKeyboardHook(void* hParentWnd,void* lpThread,void* pPara,void* pHookProc);	// uCallWndProcMsg = WM_USER+100 ++
    			BOOL SetLowLevelKeyboardHook(void* hParentWnd,void* lpThread,void* pPara,void* pHookProc);
    
    		// Remove hooks funcs ________________________________________________________________
    		public:
    			BOOL RemoveKeyboardHook();
    			BOOL RemoveLowLevelKeyboardHook();
    
    		// Addition funcs ____________________________________________________________________
    		public:
    			void* GetKeyboardHookAddress();	// Be careful with this
    			void* GetKeyboardThreadAddress();
    			HWND GetKeyboardParentWnd();
    			
    			void* GetLowLevelKeyboardHookAddress();	// Be careful with this
    			void* GetLowLevelKeyboardThreadAddress();
    			HWND GetLowLevelKeyboardParentWnd();
    		};
    	}
    }
    + Phần code trên củng không có nhiều thứ để nói lắm nên chúng ta tiếp tục với file "MSl.cpp" (tại lười giải thích đó mà ^_^!)
    - Vì chúng ta dùng hook nên bắt buộc phải có một section riêng để share data giửa các file dll và để tránh bị reload! Vì vậy trước hết phải tạo một section thôi! (Có một số biến không cần giữ giá trị thì bạn không cần đưa vào đây!)
    Code:
    #pragma region Class Hooking
    #pragma region Class Hooking - Hooking Segment
    #pragma data_seg("HookingSegment")
    	// Module handle
    	HMODULE m_hModule = 0;
    
    	// Hook handle
    	HHOOK m_hLowLevelKeyboardHook = 0;
    
    	// ParentWnd handle
    	HWND m_hLowLevelKeyboardParentWnd = 0;
    
    	// TargetWnd handle
    
    	// Thread proc handle
    	HANDLE m_hLowLevelKeyboardThread;
    
    	// Additions vars
    	
    #pragma data_seg()
    #pragma comment(linker,"/SECTION:HookingSegment,rws")
    #pragma endregion
    Bạn có thể xem thêm phần "data_seg" trong MSDN! Đơn gian đó là vùng data được share giửa 2 app cùng dùng chung 1 file dll! VD : trong phần data_seg có một biến x = 0, nếu app 1 thay đổi giá trị x = 10 thì trong app 2 x cũng sẻ có giá trị là 10!

    + Tiếp đến sẻ là constructor và destructor :
    Code:
    #pragma region Class Hooking - Constructor and destructor
    Hooking::Hooking()
    {
    	SetHookModule(L"MSl.dll");    // Load dll và gán handle cho m_hModule
    }
    Hooking::~Hooking()
    {
    	if(m_hLowLevelKeyboardHook) RemoveLowLevelKeyboardHook();    // Remove hook
    }
    #pragma endregion
    + Trong code ở trên thì khi khởi tạo class thì nó sẻ load luôn file dll để đỡ mất công viết mã để load mỗi khi dùng đến file dll này! (biện minh cho việc làm biếng thôi ^_^! nếu bạn muốn kiễm tra lỗi tốt hơn thì không nên dùng cách này!)

    Code:
    #pragma region Class Hooking - Init library
    void Hooking::SetHookModule(HMODULE hModule)
    {
    	if(hModule) m_hModule = hModule;
    }
    BOOL Hooking::SetHookModule(LPTSTR lpDllPath)
    {
    	m_hModule = LoadLibrary(lpDllPath);
    	if(!m_hModule) return 0;
    	return 1;
    }
    #pragma endregion
    + Phần này khỏi giải thích hen ^_^!

    Kế đến là hookproc! Tuy nhiên đây chỉ là code vd thôi! Vì nếu bạn đặc hook proc trong file dll thì chương trình của bạn sẻ bị phụ thuộc vào file dll đó! Vì vậy cái này chỉ để viết để biết thôi chứ trong project hỏng có dùng!
    Code:
    #pragma region Class Hooking - Hook procs
    LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
    	if(nCode == HC_ACTION)
    	{
    		ResumeThread(m_hKeyboardThread);
    	}
    	return CallNextHookEx(m_hKeyboardHook,nCode,wParam,lParam);
    }
    LRESULT CALLBACK LowLevelKeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
    {
    	if(nCode == HC_ACTION)
    	{
    		ResumeThread(m_hKeyboardThread);
    	}
    	return CallNextHookEx(m_hKeyboardHook,nCode,wParam,lParam);
    }
    #pragma endregion
    #pragma region Class Hooking - Thread procs
    UINT WINAPI KeyboardThread(void* lpData)
    {
    	while(1)
    	{
    	//	Sleep(1000);
    		SuspendThread(m_hKeyboardThread);
    	}
    	return 1;
    }
    UINT WINAPI LowLevelKeyboardThread(void* lpData)
    {
    	while(1)
    	{
    	//	Sleep(1000);
    		SuspendThread(m_hLowLevelKeyboardThread);
    	}
    	return 1;
    }
    #pragma endregion
    + Kế đến là hàm để sethook :
    Code:
    #pragma region Class Hooking - Set hooks funcs
    BOOL Hooking::SetLowLevelKeyboardHook(void* hParentWnd,void* lpThread,void* pPara,void* pHookProc)
    {
    	if(!m_hModule || !lpThread || !pHookProc) return 0;
    	if(m_hLowLevelKeyboardHook && !UnhookWindowsHookEx(m_hLowLevelKeyboardHook)) return -1;
    	m_hLowLevelKeyboardParentWnd = (HWND)hParentWnd;
    	m_hLowLevelKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL,(HOOKPROC)pHookProc,m_hModule,0);
    	if(!m_hLowLevelKeyboardHook) return -2;
    	if(m_hLowLevelKeyboardThread)
    	{
    		DWORD DwExitCode = 0;
    		if(!GetExitCodeThread(m_hLowLevelKeyboardThread,&DwExitCode)) return -3;
    		if(!TerminateThread(m_hLowLevelKeyboardThread,DwExitCode)) return -4;
    		CloseHandle(m_hLowLevelKeyboardThread);
    		m_hLowLevelKeyboardThread = 0;
    	}
    	m_hLowLevelKeyboardThread = CreateThread(0,0,(LPTHREAD_START_ROUTINE)lpThread,pPara,CREATE_SUSPENDED,0);
    	if(!m_hLowLevelKeyboardThread) return -5;
    	return 1;
    }
    #pragma endregion
    Trong func ở trên có vài điểu phải nói trước với bạn :
    Code:
    BOOL Hooking::SetLowLevelKeyboardHook(void* hParentWnd,void* lpThread,void* pPara,void* pHookProc)
    {
    + hParentWnd : là wnd của chương trình chính (chương trình mà bạn dùng để hook vào chương trình khác đó mà!)
    + lpThread : thực ra là hàm dùng làm thread để sử lý data truyền bởi hook proc! Vì SendMessage hay PostMessage không dùng được trong .NET (thực ra có cách để "ăn gian" vụ này, trong MSDN cũng có chỉ nhưng người ta đã "not recommend" thì mình cũng chẵng dại gì mà dùng (vì microsoft có thể bỏ đi bất cứ lúc nào))!
    + pPara : là phần data passed vào thread khi tạo ra thread đó!
    + pHookProc : là hàm dùng làm hook proc! Bạn có thể dủng "LowLevelKeyboardThread" ở phần trên, có nghĩa là bỏ đi cái para này và chỉ cần dùng :
    Code:
    m_hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)pHookProc,m_hModule,0);
    Tiếp theo là code để unhook (đừng bao giờ quên cái này,nếu không bạn sẻ "thê thảm" ^_^!)

    Code:
    #pragma region Class Hooking - Remove hooks funcs
    BOOL Hooking::RemoveLowLevelKeyboardHook()
    {
    	if(!m_hModule || !m_hLowLevelKeyboardHook) return 1;	// Nothing to do
    	if(!UnhookWindowsHookEx(m_hLowLevelKeyboardHook)) return 0;
    	// Reset vars
    	m_hLowLevelKeyboardHook = 0;
    	m_hLowLevelKeyboardParentWnd = 0;
    	// Terminate thread proc
    	DWORD DwExitCode = 0;
    	if(!GetExitCodeThread(m_hLowLevelKeyboardThread,&DwExitCode)) return -1;
    	if(!TerminateThread(m_hLowLevelKeyboardThread,DwExitCode)) return -2;
    	CloseHandle(m_hLowLevelKeyboardThread);
    	m_hLowLevelKeyboardThread = 0;
    	return 2;
    }
    #pragma endregion
    Và có thêm một số func để lấy data trong section:
    Code:
    #pragma region Class Hooking - Addition funcs
    HWND Hooking::GetKeyboardParentWnd()
    {
    	return m_hKeyboardParentWnd;
    }
    void* Hooking::GetKeyboardHookAddress()
    {
    	return &m_hKeyboardHook;
    }
    void* Hooking::GetKeyboardThreadAddress()
    {
    	return &m_hKeyboardThread;
    }
    HWND Hooking::GetLowLevelKeyboardParentWnd()
    {
    	return m_hLowLevelKeyboardParentWnd;
    }
    void* Hooking::GetLowLevelKeyboardHookAddress()
    {
    	return &m_hLowLevelKeyboardHook;
    }
    void* Hooking::GetLowLevelKeyboardThreadAddress()
    {
    	return &m_hLowLevelKeyboardThread;
    }
    #pragma endregion
    Như vậy cơ bản đả đủ cho file DLL! Giờ đến chương trình chính nha!

    Trước tiên bạn hảy tạo một class mới để chứa hookproc cái đả ! Đặc trong namespace chính của chương trình cũng được nhưng sẻ khó quản lý lắm! "Chia để trị" là tốt nhất! Đây là nội dung của class :
    Code:
    #pragma once
    
    #ifndef VC_EXTRALEAN
    #define VC_EXTRALEAN			// Exclude rarely-used stuff from Windows headers
    #endif
    
    #ifndef WINVER					// Allow use of features specific to Windows XP or later.
    #define WINVER 0x0501			// Change this to the appropriate value to target other versions of Windows.
    #endif
    
    #ifndef _WIN32_WINNT			// Allow use of features specific to Windows XP or later.                   
    #define _WIN32_WINNT 0x0501		// Change this to the appropriate value to target other versions of Windows.
    #endif						
    
    #ifndef _WIN32_WINDOWS			// Allow use of features specific to Windows 98 or later.
    #define _WIN32_WINDOWS 0x0410	// Change this to the appropriate value to target Windows Me or later.
    #endif
    
    #ifndef _WIN32_IE				// Allow use of features specific to IE 6.0 or later.
    #define _WIN32_IE 0x0600		// Change this to the appropriate value to target other versions of IE.
    #endif
    
    #include "Windows.h"
    
    #using "System.Windows.Forms.dll"
    
    using namespace System;
    using namespace System::Windows;
    
    namespace RadicalLight
    {
    	namespace System
    	{
    		public ref class CTest
    		{
    		public:
    			void* GetThreadProcAddress();
    			void SetThreadHandle(void* pThread);
    			void SetHookHandle(void* pHook);
    			void* GetProcAddress();
    		};
    	}
    }
    + Hàm "GetThreadProcAddress" sẻ lấy address của thread và chúng ta sẻ dùng address này để truyền vào hàm :
    Code:
    BOOL Hooking::SetLowLevelKeyboardHook(void* hParentWnd,void* lpThread,void* pPara,void* pHookProc)
    + SetThreadHandle : Lấy handle của thread tạo bởi DLL (để còn biết đườn mà quản lý nó chớ ^_^!)
    + SetHookHandle : Lấy handle trả về bởi SetWindowsHookEx! Vì chúng ta tách hookproc ra khỏi file DLL nên phải biết handle của hook để gọi hàm :
    Code:
    CallNextHookEx(m_hHook,nCode,wParam,lParam);
    Bạn thử hỏng gọi hàm CallNextHookEx thử xem, sẻ có chuyện rất vui xảy ra (Lưu ý : Lưu lại tất cả dữ liệu trước khi dùng thử cái này! Có gì ráng chịu à nghen ^_^!)

    + GetProcAddress : Lấy địa chỉ của hookproc để pass vào "SetLowLevelKeyboardHook"

    Bây giờ đến file CPP của class

    Code:
    using namespace RadicalLight::System;
    
    HANDLE m_hThread = 0;
    HHOOK m_hHook = 0;
    
    UINT WINAPI ThreadProc(void* pData)
    {
    	if(!pData)
    	{
    		::System::Windows::Forms::MessageBox::Show("Invalid data");
    	}
    	/*String^ str = gcnew String((char*)pData);
    	::System::Windows::Forms::MessageBox::Show(str);
    	/*return 1;*/
    	/*if(!m_pThread)
    	{
    		::System::Windows::Forms::MessageBox::Show("Invalid thread handle");
    		return 1;
    	}*/
    //	HANDLE* p = (HANDLE*)pData;
    //	HANDLE m_hKeyboardThread = *m_pThread;//*p;
    	String^ Str = gcnew String((wchar_t*)pData);
    
    	if(!m_hThread) return 0;
    
    	while(1)
    	{
    		//Sleep(1000);
    	//	MessageBox(0,L"Message from thread",L"Msg",0);
    		//String^ s = "Hi,there...!";
    		//::System::Windows::Forms::MessageBox::Show(s);
    
    		::System::Windows::Forms::MessageBox::Show(Str);
    
    		SuspendThread(m_hThread/*m_hKeyboardThread*/);
    	}
    	return 1;
    }
    Thread này sẻ làm một chuyện là hiện lên một messagebox của string mà bạn truyền vào! (Bạn thích thì viết cái khác cũng được!)


    Dưới đây là hookproc:
    Code:
    LRESULT WINAPI KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
    	if(nCode == HC_ACTION)
    	{
    		if(wParam == (WPARAM)WM_KEYDOWN)
    		{
    			::KBDLLHOOKSTRUCT* kbs = (::KBDLLHOOKSTRUCT*)lParam;
    			WCHAR* s = new WCHAR[50];
    			wsprintf(s,L"You has pressed : %c",kbs->vkCode);
    			MessageBox(0,s,L"Msg",0);
    			delete [] s;
    
    			ResumeThread(m_hThread);
    		}
    	}
    	return CallNextHookEx(m_hHook,nCode,wParam,lParam);
    }
    Theo code trong hàm trên thì mỗi khi bạn nhấn một phím thì nó sẻ hiện kí tự vừa được nhấn và resume thread!

    Thêm một số thứ cần thiết nửa nè :
    Code:
    void* CTest::GetProcAddress()
    {
    	return KeyboardProc;
    }
    void CTest::SetThreadHandle(void* pThread)
    {
    	m_hThread = *(HANDLE*)pThread;
    }
    void CTest::SetHookHandle(void* pHook)
    {
    	m_hHook = *(HHOOK*)pHook;
    }
    void* CTest::GetThreadProcAddress()
    {
    	return &ThreadProc;
    }
    Bây giờ bắt đầu code cho form chính :
    DBclick vào hai button và chép cái này vào :
    + Button hook:
    Code:
    if(!pTest) pTest = gcnew RadicalLight::System::CTest();
    				 if(!p) p = gcnew RadicalLight::System::Hooking();
    
    String^ pStr = L"Hello,this is para's passed to thread";
    IntPtr pStrPtr = System::Runtime::InteropServices::Marshal::StringToBSTR(pStr);
     int i = p->SetLowLevelKeyboardHook((HWND)(void*)this->Handle,pTest->GetThreadProcAddress(),(void*)pStrPtr/*p->GetLowLevelKeyboardThreadAddress()*/,pTest->GetProcAddress());
    pTest->SetThreadHandle(p->GetLowLevelKeyboardThreadAddress());
     pTest->SetHookHandle(p->GetLowLevelKeyboardHookAddress());
    //m_pPara = p->GetKeyboardThread();
     MessageBox::Show(i.ToString());
    + button unhook:
    Code:
    if(!p) p = gcnew RadicalLight::System::Hooking();
    p->RemoveLowLevelKeyboardHook();
    Và cuối cùng là thêm public var vào :
    public: RadicalLight::System::Hooking^ p;
    public: RadicalLight::System::CTest^ pTest;

    Nhớ add reference đến file DLL trước khi dùng nha!

    Có thể bạn thấy rất dài nhưng đây là những đoạn mã cơ bản! Nếu sau này bạn cần dùng đến thì chỉ cần sửa lại "ThreadProc" , "KeyboardProc" và co trong các button là được!

    Có môt điều cần nói thêm là vì chúng ta dùng thread nên:
    + Máy sẻ khó bị treo khi bạn làm bậy!
    Tuy nhiên có một hạn chế lớn là khi một thread được tạo thì sẻ được cấp một vùng stack 1MB! Vì vậy bạn chỉ có thể tạo tối đa 32 thread mà thôi (nếu giảm stack size thì có thể tăng thêm!)!

    Nhưng mà cũng hỏng sao vì chỉ có 15 loại hook thôi mà và có chương trình nào dùng hết cùng một lúc đâu ^_^!!
    Đã được chỉnh sửa lần cuối bởi langman : 15-07-2010 lúc 12:42 AM. Lý do: xóa link die

Các đề tài tương tự

  1. Mouse hook system-wide lại chạy như local hook ???
    Gửi bởi nguoixanh trong diễn đàn Thắc mắc lập trình Visual C++
    Trả lời: 1
    Bài viết cuối: 12-11-2013, 04:02 PM
  2. Mouse hook system-wide lại chạy như local hook ???
    Gửi bởi nguoixanh trong diễn đàn Windows API, Hooking, xử lý Windows Message
    Trả lời: 1
    Bài viết cuối: 12-11-2013, 03:01 PM
  3. Hook keyboard và mouse hook trong c# không cần code
    Gửi bởi tienlbhoc trong diễn đàn Tutorials và Thủ thuật lập trình C#, ASP.NET
    Trả lời: 7
    Bài viết cuối: 23-06-2013, 01:27 PM
  4. IAT Hooking
    Gửi bởi benina trong diễn đàn Tutorials và Thủ thuật Visual C++
    Trả lời: 6
    Bài viết cuối: 07-03-2010, 10:15 AM
  5. [Kernel Driver] ShaDow SSDT Hook Có Phải là Hook đồ Họa
    Gửi bởi chàng trai dễ thương trong diễn đàn Windows API, Hooking, xử lý Windows Message
    Trả lời: 0
    Bài viết cuối: 11-10-2009, 07:48 AM

Quyền hạn của bạn

  • Bạn không thể gửi đề tài mới
  • Bạn không thể gửi bài trả lời
  • Bạn không thể gửi các đính kèm
  • Bạn không thể chỉnh sửa bài viết của bạn