Chào mừng đến với Diễn đàn lập trình - Cộng đồng lập trình.
Kết quả 1 đến 7 của 7
  1. #1

    Hook – Ví dụ nhỏ dành cho "người mù"

    Hook là gì?

    Trong Windows, khi chúng ta thực hiện các thao tác nhấp chuột, nhấn phím… thì hệ điều hành sẽ chuyển các sự kiện này thành các thông điệp (message) rồi đưa vào hàng đợi (queue) của hệ thống. Sau đó, các thông điệp được trao lại cho từng ứng dụng cụ thể để xử lý.

    Hook là một kỹ thuật cho phép một hàm có thể chặn, theo dõi, xử lý, hoặc hủy bỏ các thông điệp trước khi chúng “mò” đến được ứng dụng.

    Hai ví dụ thường gặp của Hook là ứng dụng soạn thảo văn bản tiếng Việt (Unikey, Vietkey…) và ứng dụng tra từ điển trực tiếp trên màn hình (Click’n’See, Lạc Việt MTD, English Study…). Chúng xử lý thông điệp từ bàn phím để đổi văn bản sang tiếng Việt, hoặc xử lý thông điệp từ con chuột để lấy văn vản dưới con trỏ. Chương trình KeyLogger chuyên ăn cắp mật khẩu cũng sử dụng kỹ thuật này.

    Hook hoạt động như thế nào?

    Xét ở mặt phạm vi hoạt động thì có 2 loại Hook: Hook toàn cục (có phạm vi ảnh hưởng đến toàn hệ thống) và Hook cục bộ (chỉ có tác dụng trên ứng dụng được cài Hook).

    Xét về mặt chức năng, Hook có 15 loại ứng với nhóm sự kiện mà nó sẽ xử lý. Ví dụ:
    - WH_KEYBOARD: cho phép đón nhận các thông điệp từ bàn phím
    - WH_MOUSE cho phép đón nhận các sự kiện có liên quan đến con trỏ chuột
    - WH_MSGFILTER và WH_SYSMSGFILTER: cho phép theo dõi chính các thông điệp được xử lý bởi menu, scrollbar, dialog…
    Các loại còn lại bạn có thể tìm thấy trong CD MSDN tại /winui/winui/windowsuserinterface/windowing/hooks/abouthooks.htm

    Ứng với mỗi loại Hook, Windows sẽ có một chuỗi các hàm lọc (filter function) để xử lý. Ví dụ, khi người dùng nhấn phím, thông điệp này sẽ được truyền qua tất cả các hàm lọc thuộc nhóm WH_KEYBOARD.

    Các hàm lọc này sẽ do chúng ta tùy ý định nghĩa. Mục tiêu của hàm lọc và bắt lấy thông điệp để giám sát, sửa đổi và chuyển tiếp, hoặc ngăn chặn không cho nó đến được ứng dụng. Hàm lọc được gọi bởi Windows nên còn gọi là hàm CallBack.

    Muốn cài đặt một Hook toàn cục (Hook hệ thống), thì hàm lọc phải được đặt trong file DLL.

    Cài đặt Hook có khó không?

    Về cơ bản, chỉ cần biết chút kiến thức về hệ điều hành Windows (cơ chế hoạt động và quản lý thông điệp). Thêm một ít kiến thức về C++, API, và DLL (Dynamic Link Library). Tuy nhiên, Hook là một… nghệ thuật (và người Hook cũng là 1 nghệ sỹ [IMG]images/smilies/biggrin.png[/IMG]), và chúng ta có thể làm đủ trò tùy vào trí tưởng tượng và “nội công” của mình.

    Sơ lượt các bước cài đặt (một Hook hệ thống)

    1. Bước 1: tạo file DLL chứa hàm lọc. Mọi hàm lọc đều có dạng như sau:

    Mã:
    LRESULT CALLBACK <tên hàm>(int nCode, WPARAM wParam, LPARAM lParam)
    Bên trong hàm lọc, chúng ta có thể tùy ý làm đủ chuyện trời đất. Tuy nhiên, phải tuân thủ theo một số nguyên tắt sau:
    - Nếu nCode < 0 thì không được làm gì mà phải gọi ngay hàm CallNextHookEx() (sẽ trình bày sau) và trả về giá trị... return bởi nó.
    - Nếu có xử lý ít nhiều thì hàm lọc phải trả về giá trị khác 0

    2. Bước 2: tạo chương trình cài đặt Hook. Chương trình này sử dụng 3 hàm API sau đây để “móc” hàm lọc vào các sự kiện:

    a) Hàm cài đặt một hàm lọc

    Mã:
    HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId)
    Các tham số có ý nghĩa như sau:
    + idHook: xác định loại hook mà ta muốn cài đặt (ví dụ: WH_KEYBOARD, WH_MSGFILTER…)
    + lpfn : địa chỉ hàm lọc mà ta muốn gắn với hook
    + hMod : handle của module chứa hàm lọc (là handle của file DLL trong trường hợp Hook toàn cục)
    + dwThreadID : định danh của thread ứng với hook đang được cài đặt (nếu = 0 thì Hook có phạm vi toàn hệ thống và bao hảm cả mọi thread đang tồn tại)

    b) Hàm gỡ bỏ một hàm lọc

    Mã:
    BOOL UnhookWindowsHookEx(HHOOK hHook)
    Tham số:
    + hHook: handle của hook cần gỡ bỏ (chính là giá trị được trả vể bởi hàm SetWindowsHookEx khi cài đặt)

    c) Hàm gọi hàm lọc kế tiếp trong chuỗi
    Như đã nói ở trên, hệ thống duy trì một chuỗi các hàm lọc cho mỗi loại sự kiện. Do đó, sau khi một hàm lọc thực hiện xong “nghĩ vụ” của mình, nó sẽ gọi hàm lọc tiếp theo trong chuỗi. Nhờ đó, người ta có thể cài nhiều Hook tại một sự kiện.

    Mã:
    LRESULT CallNextHookEx(HHOOK hHook, int nCode, WPARAM wParam, LPARAM lParam)
    Ý nghĩa tham số:
    + hHook: là handle của hook hiện hành
    + nCode : hook code để gởi đến hook kế tiếp
    + wParam và lParam: chứa thông tin mở rộng của thông điệp

    Nhứt đầu quá! Ví dụ đi

    Ví dụ dưới đây (có file đính kèm) sẽ minh họa cách cài một cái Hook đơn giản bằng Visual C++: chặn bắt sự kiện nhấn phím. Khi chạy, chương trình sẽ “tàng hình” (chỉ nhìn thấy trong Task Manager). Nếu người dùng “lỡ tay” nhấn phím Enter (ở bất kỳ đâu) sẽ bị “bắn” ra một cái message box có dòng chữ “sonhn say hello” (rất dễ nổi khùng!).

    1. Bước 1:

    Trước hết, tạo một Project VC++ Win32 DLL (xin đừng nhầm với dạng DLL Class Library) với 1 file duy nhất có nội dung như sau:

    File “TestDLL.cpp”


    Mã:
    #include <windows.h> //vùng nhớ dùng chung, chứa biến handle của Hook#pragma data_seg("SHARED_DATA")HHOOK   hGlobalHook = NULL;#pragma data_seg() //hàm lọc sự kiện nhấn phím__declspec(dllexport) LRESULT CALLBACK FillKeyboard(int nCode, WPARAM wParam, LPARAM lParam) {    /ếu sự kiện là nhấn phím và mã phím là Enter    if ((nCode == HC_ACTION) && (wParam == 13))    {        MessageBox(0, "sonhn say hello :)", "Hello", 0);        return 1;    }     //gọi Filter Function kế tiếp trong chuỗi các Filter Function    return CallNextHookEx(hGlobalHook, nCode, wParam, lParam);} //hàm ấn định biến hGlobalHook tại vùng nhớ dùng chung__declspec(dllexport) void SetGlobalHookHandle(HHOOK hHook){   hGlobalHook = hHook;}
    Đoạn code trên có 3 điểm cần chú ý:
    - Đoạn khai báo vùng nhớ dùng chung “SHARED_DATA”: vùng nhớ này chứa handle của Hook được cài. Sở dĩ nó phải được đặt trong vùng nhớ dùng chung vì cả 2 chương trình (DLL và EXE) đều cần truy phải truy xuất vào.
    - Hàm lọc sự kiện nhấn phím: hàm này thực hiện nhiệm vụ: khi sự kiện là nhấn phím (nCode == HC_ACTION) và phím được nhấn là Enter (wParam == 13) thì “bắn” cái message box ra.
    - Hàm ấn định biến hGlobalHook: hàm này sẽ được gọi bởi chương trình EXE sau đây để ấn định handle cho Hook mà nó sẽ cài.

    Tạo file Module-Definition (.def) có nội dung như bên dưới để export các hàm bên trong file DLL và khai báo vùng nhớ dùng chung.

    File “TestDLL.def”


    Mã:
    LIBRARY "TestDLL"EXPORTS     FillKeyboard     SetGlobalHookHandle     SECTIONS  SHARED_DATA Read Write Shared
    Biên project trên để thu được file “TestDLL.dll”

    2. Bước 2:

    Tạo project VC++ dạng Win32 Application để thực hiện cài hàm lọc (trong file TestDLL.dll) vào sự kiện nhấn phím.

    File “TestHook.cpp”


    Mã:
    #include <windows.h> //các biến toàn cụcHHOOK hHook = NULL;     //handle của hookHMODULE hDll;           //handle của DLL //định nghĩa con trỏ hàm SetGlobalHookHandle() trong file DLLtypedef VOID (*LOADPROC)(HHOOK hHook); BOOL InstallHook(); int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) {    //cài đặt hook, exit nếu thất bại    if (InstallHook() == FALSE)    {        MessageBox(0, "Can not install hook!", "Error", 0);        return -1;    }     MSG msg;    BOOL bRet;    //vòng lặp đón và xử lý các message     while((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)    {         TranslateMessage(&msg);        DispatchMessage(&msg);    }     return 0;} //Hàm cài đặt hookBOOL InstallHook() {    //load file DLL    if (LoadLibrary("TestDLL.dll") == NULL)    {        MessageBox(0, "Can not load DLL file.", "Error", 0);        return FALSE;    }     //lấy handle của file DLL    HMODULE hDLL = GetModuleHandle("TestDLL");     //exit nếu load DLL không thành công    if (hDLL == NULL)    {        return FALSE;    }     //cài đặt hook, phạm vi toàn cục    hHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)GetProcAddress(hDLL,"FillKeyboard"), hDLL, 0);     //exit nếu cài đặt Hook không thành công    if (hHook == NULL)    {        return FALSE;    }     //lấy địa chỉ hàm SetGlobalHookHandle() trong file DLL    LOADPROC fPtrFcnt;    fPtrFcnt = (LOADPROC)GetProcAddress(hDLL, "SetGlobalHookHandle");    if (fPtrFcnt == NULL)    {        return FALSE;    }     //ấn định handle của hook vào vùng nhớ dùng chung (giữa DLL và ứng dụng này)    fPtrFcnt(hHook);        return TRUE;}
    Đừng hốt hoảng vì độ dài của nó. Đọc kỹ 1 chút, bạn sẽ hiểu được vấn đề vì chúng đã được ghi chú khá chi tiết rồi.

    Chú ý:
    - Nhằm đơn giản hóa quá trình cài đặt, ví dụ này không có thao tác gỡ bỏ Hook. Khi chạy, chương trình sẽ “tàng hình” và muốn tắt nó, bạn phải “end task” nó bằng Task Manager(!). Lúc đó, Hook cũng sẽ được tự động được gỡ bỏ.
    - Nếu bạn muốn cài đặt thao tác gỡ bỏ Hook, hãy viết thêm đoạn code xử lý sự kiện WM_DESTROY và gọi hàm dưới đây:

    Mã:
    //hàm gỡ bỏ hook, với hHook là handle của hookBOOL UninstallHook() {    if ((hHook != 0) && (UnhookWindowsHookEx(hHook) == TRUE))    {        hHook = NULL;        return TRUE;    }    return FALSE;}
    Kết luận

    Hook là một kỹ thuật tương đối khó và kiến thức về nó cũng khá rộng, khó có thể trình bày hết trong phạm vi một bài viết. Các bạn quan tâm có thể tìm hiểu thêm trên website http://msdn.microsoft.com và các tài liệu liên quan. Chúc thành công.

  2. #2
    Về phần hook mình không làm được phần hook hàm của win, đại loại kiểu hook để làm click&see ấy, bạn nào có kinh nghiệm có thể hướng dẫn mình phần này được không

  3. #3
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Xin tổng hợp thêm tài liệu sưu tầm ở đây
    Tài liệu sưu tầm trên blog của Benina</font>
    001 : _http://rootbiez.blogspot.com/2009/11/programming-co-che-hook-va-ung-dung-to.html

    [<font color="Red">Programming] Cơ chế Hook và ứng dụng to lớn của nó


    Cơ chế Hook và ứng dụng to lớn của nó

    Author: griever

    Link: _http://cnpmk51-bkhn.org/forums/archive/index.php?t-622.html

    Bài 1:

    hnay đang kiếm guide để làm autoPlay cho vltk thì đc biết đến cái này, ngồi 1 lúc ngẫm ra mấy thứ lên đây chia sẻ cùng a e.

    chắc mọi người đã nghe qua nhiều về nó, ví dụ làm Vietkey hay Unikey đều phải sử dụng cơ chế này

    ngoài ra cơ chế này còn sử dụng để làm keylogger, autoPlay của các game oline...

    đầu tiên, em xin nói qua về cơ chế thông điệp của Windows
    - khi 1 sự kiện xảy ra tại cửa sổ con (click chuột, gõ 1 phím...), HDH sẽ phát ra 1 thông điệp tới cửa sổ cha, ở đây thông điệp sẽ đc hàm xử lý của cửa sổ cha xử lý. Tùy theo thông điệp đc gửi đến là gì và mục đích của người lập trình mà cách thức xử lý sẽ khác nhau.

    - hàm xử lý là hàm dùng để xử lý các thông điệp đã đc chặn, bắt

    - có thể tác động đến 1 cửa sổ bất kỳ bằng cách gửi thông điệp đến chính cửa sổ đó. Khi đó nó sẽ tiếp nhận thông điệp này và thực hiện các yêu cầu của thông điệp.
    khi học các hàm API mọi người sẽ nghĩ ngay đến hàm SendMessage( )
    - 1 cửa sổ có thể là 1 button, 1 edit text, 1 hộp thoại,...

    tiếp theo, em xin nói về bản chất của Hook (đây là theo "ngu ý" của em thôi): Hook thực chất là bẫy & sửa các thông điệp của 1 cửa sổ bất kỳ trước khi các thông điệp này đc hàm xử lý của cửa sổ đó xử lý.
    nói cách khác, khi HDH gửi 1 thông điệp đến cửa sổ cha, Hook sẽ chặn các thông điệp này, thay đổi chúng...tùy theo mục đích của người lập trình.

    ví dụ khi 1 ứng dụng đc khởi động (ví dụ Yahoo chẳng hạn), thông điệp WM_CREATE sẽ đc phát ra, khi đó nếu ta dùng Hook để bắt lấy thông điệp này và thay nó bằng thông điệp WM_DESTROY thì ứng dụng đó hiển nhiên sẽ tự động exit! (giống kavo đó)

    vậy tại sao dùng cơ chế chặn bắt thông thường kô đc???:Smlie (105):
    sẽ có ý kiến cho rằng có thể dùng hàm SendMessage( ) để gửi thông điệp WM_DESTROY tới 1 ứng dụng nào đó để làm cho ứng dụng đó tự exit! Hoặc gửi thông điệp WM_GETTEXT tới 1 edit text để lấy đc nội dung của edit text đó! Như vậy thì làm keylogger hay virus sẽ đơn giản hơn bao nhiêu [IMG]images/smilies/biggrin.png[/IMG]
    nếu ai đã biết về hàm này thì sẽ biết là hàm này yêu cầu HANDLE của cửa sổ tiếp nhận thông điệp, hiểu nôm na thế này: thông điệp là 1 gói bưu kiện, HDH là ngưởi gửi bưu kiện, cửa sổ đang đc nói đến là người nhận bưu kiện, HANDLE của cửa sổ là dịa chỉ của ngươi nhận, còn hàm SendMessage( ) là công ty chuyển phát.
    tuy nhiên có những lúc ta kô chỉ cần hàm sendmessage này, bởi hàm này chỉ có tác dụng gửi thông điệp đi chứ kô có tác dụng "nghe" thông điệp được gửi đến cho chương trình.

    do đó, sử dụng cơ chế chặn bắt thông thường thì hầu như là vô tác dụng! => phải dùng Hook thôi [IMG]images/smilies/smile.png[/IMG]

    cách cài đặt Hook thì em kô nói ở đây vì kiếm trên google chắc không thiếu [IMG]images/smilies/biggrin.png[/IMG]

    bài sau em xin nói về 1 ứng dụng quan trọng khác của Hook đó là "lây" code (inject code) vào 1 ứng dụng bên ngoài

    Bài 2:

    h em xin trình bày 1 ứng dụng quan trọng khác của Hook là làm keylogger hay malware!
    phần này em nêu ra chỉ với mục đích "cọ xát" và "học hỏi" là chính, vì bản thân em cũng mới tìm hiểu về lĩnh vực này thôi, và cũg chưa hiểu tường tận lắm [IMG]images/smilies/biggrin.png[/IMG]

    Keylogger chắc ai cũg biết [IMG]images/smilies/biggrin.png[/IMG], đó là phần mêm dùng để thu thập thông tin vê password hay ID bằng cách bắt các thông điệp về bàn phím. ở đây em xin nói đến 1 loại keylogger khác, mục đích cũng giống như trên nhưng cách làm "nông dân" hơn. đó là lấy thông tin của edit text chứa pass hay ID.

    cách làm của Keylogger này khá là đơn giản, giống cách làm các loại autoplay bây h.
    trước hết cần phải quy ước 1 số thuật ngữ
    remote process: là ứng dụng chứa thông tin về pass và ID. Hoặc với các loại autoplay thì nó chính là các loại game oline(vltk...)

    cách thức chung của các loại này là chèn ("lây") code vào remote process. Gọi là "lây" bởi vì đoạn code đó thực sự kô nằm trong remote process. chỉ là remote process sẽ thực hiện các đoạn code đã bị "lây nhiễm". Có 3 cách để lây nhưng em xin chỉ nói 1 cách dùng Hook thôi, đó là:
    + đặt code cân thiết vào 1 file DLL, nghĩa là khi viết 1 hàm thay vì ta viết hàm đó vào chương trinh chính thì ta viết trong DLL.
    + dùng Hook để map file DLL của ta vào remote process.

    Sau khi đã thực hiện 2 bước trên thì remote process sẽ tự thực hiện các lệnh đã có trong đoạn code của ta
    --2 bước trên thực hiện đêu rất dễ--cách viết DLL thì search trên mạng kô thiếu và thực hiện cũng khá dễ

    đến đây thì có 2 hướng là keylogger và autoplay.
    trước tiên nói về keylogger:
    mục đích của keylogger là thu thập pass và ID, vậy trong file DLL ta chỉ cần xài hàm SendMessage là đủ. Khi đó ta tạo ra 1 hàm đệm trong DLL có các nhiệm vụ sau: cài Hook vào remote process, gọi hàm SendMessage với thông điệp WM_GETTEXT để lấy thông tin về pass và ID , bỏ cài đặt Hook (bước này giống như khi ta giải phóng 1 bộ nhớ đã đc cấp phát)

    sau đây là cách thức thực hiện chi tiết:
    B1: tạo 1 DLL có hàm đệm như trên
    B2: tạo chương trinh chính có các chức năng như sau:
    + lấy handle của edit text chứa pass hay ID
    + link chương trinh chính này với DLL ở trên (khi link 1 DLL với 1 ứng dụng thi ứng dụng sẽ sử dụng đc các hàm trong DLL--đó là lí do tại sao nó đc gọi la DLL - thư viện liên kết động)
    + gọi hàm đệm trong DLL ra. Đến đây thì ta đã lấy đc pass và ID rồi [IMG]images/smilies/biggrin.png[/IMG], chỉ việc gửi pass này về mail của mình là xong [IMG]images/smilies/biggrin.png[/IMG]

    B1 thì kô nói làm j nữa
    B2 có các chú ý sau:
    - làm sao lây đc handle của edit text chứa pass hay ID đây???
    trong sample em down trên mạng vê thì nó dùng cơ chế bắt chuột. Khi con chuột rê đến đâu nó sẽ dùng 1 số hàm API để định vị cửa sổ mà con chuột đang chỉ đến, vì muốn gõ pass thì các pác phải click chuột vào cái khung password, khi đó con chuột chỉ đúng edit text chứa password [IMG]images/smilies/biggrin.png[/IMG] tuy nhiên theo kinh nghiệm của em thi cái đó rất hiếm xảy ra. Khi gõ ID xong thì các pác gõ pass thế nào, dùng chuột click vào khung password hay ấn tab? Em nghĩ là 80% là ấn Tab [IMG]images/smilies/biggrin.png[/IMG] => làm keylogger theo cách này không ổn lắm, em cũng chỉ đưa ra loại này để làm quen hơn với Hook thôi. Pác nào quan tâm làm thử cũng hay [IMG]images/smilies/biggrin.png[/IMG]

    dưới đây là đoạn code dùng để định vị cửa sổ:


    Mã:
    HWND SmallestWindowFromPoint( const POINT point ){    RECT rect, rcTemp;    HWND hParent, hWnd, hTemp;     hWnd = ::WindowFromPoint( point );    if( hWnd != NULL )    {        ::GetWindowRect( hWnd, &rect );        hParent = ::GetParent( hWnd );         // Has window a parent?        if( hParent != NULL )        {            // Search down the Z-Order            hTemp = hWnd;            do{                hTemp = ::GetWindow( hTemp, GW_HWNDNEXT );                 // Search window contains the point, hase the same parent, and is visible?                ::GetWindowRect( hTemp, &rcTemp );                if(::PtInRect(&rcTemp, point) && ::GetParent(hTemp) == hParent && ::IsWindowVisible(hTemp))                {                    // Is it smaller?                    if(((rcTemp.right - rcTemp.left) * (rcTemp.bottom - rcTemp.top)) < ((rect.right - rect.left) * (rect.bottom - rect.top)))                    {                        // Found new smaller window!                        hWnd = hTemp;                        ::GetWindowRect(hWnd, &rect);                    }                }            }while( hTemp != NULL );        }    }     return hWnd;}
    như vậy là xong thằng keylogger [IMG]images/smilies/biggrin.png[/IMG]

    à, ở bài trước em có nhầm 1 chút: hàm SendMessage có thể gửi thông điệp đi bất kỳ đâu, kô cứ phải trong cùng 1 ưng dụng, nó chỉ kô thể gửi thông điệp WM_GETTEXT tới 1 cửa sổ có đặt flag ES_PASSWORD mà nằm ngoài ứng dụng gọi nó thôi, do vậy nếu ta chỉ dùng hàm SendMessage với mục đích khác thì kô cần xài Hook đâu.

    (*)flag ES_PASSWORD có tác dụng biến các ký tự nhập vào thành 1 ký tự đặc biệt duy nhất, thông thường là * ví dụ các pác gõ abcd thì nó hiển thị là ****

    còn thằng autoplay em cũng chưa tim hiểu nhiêu nên chưa dám nói [IMG]images/smilies/smile.png[/IMG], hẹn hum sau sẽ nói tiếp [IMG]images/smilies/biggrin.png[/IMG]

    cái này ngoài lề thôi, nó kô liên quan j đến Hook cả, nhưng nó sẽ giúp các pác tìm HANDLE 1 cách dễ dàng
    _http://msdn.microsoft.com/en-us/magazine/cc301495.aspx

    Bài 3:

    tiếp theo bài này em xin viết về cơ chế của AutoPlay

    các pm autoplay bây h đọc thông tin từ bộ nhớ game và dùng hook để thay đổi các thông tin cần thiết.

    đến đây em chia thành 2 phần, phần 1 là đọc thông tin từ bộ nhớ game, phần 2 là ghi thông tin.

    phần 1: đọc thông tin game
    phần này thì kô cần dùng đến Hook vì bản thân ta kô tác động gì đến game cả ("đọc" theo đúng nghĩa đen của nó)
    các thông tin trong bộ nhớ game được lưu trong các địa chỉ (có thể tĩnh hoặc động), vấn đề ở đây chỉ là làm sao "lấy" được các địa chỉ cần thiết và "đọc" nó như thế nào???
    để "lấy" đc các địa chỉ cần thiết là rất phức tạp, phải biết debug game may ra mới lấy đc nhưng ma thay các việc đó đã có các bậc tiền bối của chúng ta lo roài [IMG]images/smilies/biggrin.png[/IMG], thay vì phải ngồi hàng h để debug game, ta có thể search trên mạng trong vài giây là dc [IMG]images/smilies/biggrin.png[/IMG]

    lấy ví dụ với game VLTK, điều ta quan tâm nhất là thông tin vê char và mob (quái vật)

    Tất cả các nhân vật (người chơi, quái, Npc...) được lưu thông tin trong mảng gồm 256 đối tượng, mỗi đối tượng kích thước 0x7E4C, địa chỉ lưu địa chỉ của mảng trên là 0x00D3A570. Đối tượng 0 để trống, đối tượng 1 là người chơi, còn lại là quái & Npc.

    như vậy ta đã có đc các địa chỉ cần thiết rồi (trong mảng trên thì phần tử a[1] lưu thông tin về người chơi, các phân tử còn lại(từ a[2] đến a[255]) lưu thông tin về mob. mảng a có địa chỉ tĩnh là 0x00D3A570).
    với các game khác thi các pác tự search trên google sẽ thấy thôi

    bi h đến bước thứ 2 là đọc dữ liệu như thế nào?
    tất cả các nhân vật(người chơi, mob, npc...) đều thuộc cùng 1 lớp chung(không biết các nhà lập trình game gọi thế nào nhưng em tạm gọi đó là class CNpc
    cấu trúc của class này tùy từng game sẽ khác nhau, ở đây em xin lấy ví dụ về game VLTK


    Mã:
    class CNpc{    ...    DWORD m_NpcKind; //0x0020 1=mod, 1=player,...    ...    DWORD m_Doing; //0x00EC 1=stand, 2=walk, 3=run...    ...    int m_CurLife; //0x0B44    int m_CurLifeMax;    int m_CurLifeRep;    int m_CurMana;    int m_CurManaMax;    ...    BOOL m_bRideHorse; //0x0D58    char Name[32]; //0x0D5C Tên nhân vật    int m_n***;    ...    BOOL m_FightMode; //0x0EAC };
    vi kô tìm đc class đầy đủ nên các pác cần chú ý tới các địa chỉ OFFSET của từng biến thành viên để việc đọc dữ liệu đc chính xác, với các biến thành viên trên thì cũng đã đủ dùng để làm 1 autoplay tự buff máu, mana, và tự rao bán đồ.
    để đọc dữ liệu trong bộ nhớ game các pác dùng hàm API ReadProcessMemory

    nói qua về hàm này, hàm này có 5 tham số, tham số đâu tiên là handle của game vltk, tham số thứ 2 là địa chỉ offset mà ta sẽ lấy ra để đọc, tham số thứ 3 là buffer ta dùng để đọc dữ liệu, tham số thứ 4 là kích thước của dữ liệu ta sẽ đổ vào buffer, tham số cuối kô cân xét, cứ để NULL

    sau đây là các bước thực hiện chi tiết
    B1: tìm handle của process của game vltk, các pác có thể tham khảo cách tìm process ở đây: http://www.codeproject.com/KB/threads/getprocessid.aspx
    B2: dùng hàm ReadProcessMemory để đọc các thông tin cần thiết
    đây là ví dụ với game vltk


    Mã:
    CNpc Npc;LPBYTE lpBaseAdd, lpCurAdd;//đọc địa chỉ đầu của mảng NpcReadProcessMemory(m_hVLProcess, (LPBYTE)NPC_BASE_ADD, (LPVOID)&lpBaseAdd, 4, NULL);//tính toán địa chỉ của ngươi chơi (PLAYER_INDEX = 1)lpCurAdd = lpBaseAdd + PLAYER_INDEX*NPC_DATA_SIZE;//đọc thông tin vê ngươi chơiReadProcessMemory(m_hVLProcess, lpCurAdd, (LPVOID)&Npc, sizeof(CNpc), NULL);...
    trong đó m_hVLProcess là handle của process của game.
    lpBaseAdd là địa chỉ của mảng Npc, lpCurAdd là địa chỉ hiện tại, có đc băng cách + lpBaseAdd với (chỉ số trong mảng)*NPC_DATA_SIZE, NPC_DATA_SIZE chính là kích thước của 1 đối tượng trong mảng = 0x7E4C (đã nói ở trên), ở đây nếu muốn đọc thông tin của player thi + lpBaseAdd với 1*0x7E4C (do chỉ số của player trong mảng = 1)

    như vậy la ta đã có thông tin cơ bản về người chơi, mob...
    ngoài ra có thể sử dụng thêm 1 vài hàm API để rao, bán đồ


    Mã:
    void PostChatMessage(LPCTSTR szChatMsg){    //đặt focus cho cửa sổ chat    ::PostMessage(m_hVLWin, WM_KEYDOWN, VK_RETURN, 0x001C0001);    //Xóa nội dung trong cửa sổ chat    ::PostMessage(m_hVLWin, WM_KEYDOWN, VK_DOWN, 0x00500001);    while (szChatMsg[0])    {    ::PostMessage(m_hVLWin, WM_CHAR, LOBYTE(szChatMsg[0]), 0);    szChatMsg++;    }    //đặt focus cho cửa sổ game    ::PostMessage(m_hVLWin, WM_KEYDOWN, VK_RETURN, 0x001C0001);}
    ở đây m_hVLWin là handle của game vltk(khác với handle của process đấy nhé)

    để tìm handle của game vltk thì các pác chỉ cần dùng hàm FindWindow( )

    như vậy đến đây là quá đủ để làm cái AutoSell + AutoBuff máu& mana rồi đó!

    Bài 4:

    về vấn đề lấy Handle của cửa sổ và process (tiến trình) ở phần trên em viết hơi rời rạc, để em tổng hợp lại cho các pác tiện theo dõi

    I. Lấy handle của cửa sổ chính (main window)
    - dùng hàm FindWindow( ) là đơn giản nhất, để sử dụng hàm này chỉ cần biết đc title của cửa sổ đó, ví dụ đối với game vltk thì title là Vo Lam Truyen Ky

    II. Lấy handle của cửa sổ con, ví dụ lấy handle của edit text chứa password trong game vltk

    ở đây ta dùng hàm FindWindowEx( )
    prototype: FindWindowEx(handle của CS cha, handle của cửa sổ con, tên của class của cửa sổ con, tên của cửa sổ con)

    các pác để ý tham số thứ 2, đây chính là handle của cửa sổ con trước đó tính theo trục Z, nếu để NULL thì máy sẽ tìm từ trên cùng xuống dưới.
    Vì trong các cửa sổ con có thể có thêm cửa sổ con (con của con[IMG]images/smilies/biggrin.png[/IMG]) nữa vì thế cần 1 vòng lặp while ở đây để duyệt tất cả các cửa sổ con nằm trong main window.
    ví dụ ở đây cần tìm các cửa sổ con thỏa mãn đk chúng là edit text.

    B1 tìm handle của main window

    B2 duyệt lần lượt các cửa sổ con ( có lẽ phải dùng đệ quy )


    Mã:
    HWND hMainWnd; //lưu handle của cửa sổ chínhHWND hTemp; //lưu handle của 1 cửa sổ con hMainWnd = FindWindow(NULL,"Vo Lam Truyen Ky");if (hMainWnd == NULL) //chưa khởi động game???return;
    h thì phần duyệt các pác tự làm, vì em kô rành vê đệ qui lắm , nhưng nhớ dùng hàm FindWindowEx để tìm các cửa sổ con.
    Về phần duyệt đệ qui thì nó cũng giống như duyệt cái Window Explorer thôi, cứ duyệt lần lượt từ trên xuống dưới, duyệt đến đâu lại tim cửa sổ con của nó...

    III.lấy handle của 1 process(tiến trình)
    tham khảo tại đây
    _http://www.codeproject.com/KB/threads/getprocessid.aspx

    uốn chặn một cách đầy đủ, chính xác

  4. #4
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    (Tiếp , Sưu tầm trên blog Benina )

    003: _http://rootbiez.blogspot.com/2009/11/programming-ung-dung-co-che-hook-phan-2.html


    [Programming] Ứng dụng cơ chế Hook - Phần 2

    Ứng dụng cơ chế Hook để xây dựng chương trình hỗ trợ gõ Tiếng Việt trên Hệ Điều Hành Windows 32 Bit - Phần 2

    Phần II : Ứng Dụng Hook Để Xây Dựng Bộ Gõ Tiếng Việt

    I.Đặt vấn đề

    Nhúng tiếng Việt vào các ứng dụng chạy trên nền Windows luôn là vấn đề được nhiều người quan tâm. Và thực tế đã có khá nhiều tổ chức, cá nhân đưa ra các sản phẩm thuộc dạng này. Tính năng chủ yếu của những phần mềm này là cho phép gõ tiếng Việt trong các môi trường soạn thảo của ứng dụng. Bên cạnh đó, một số tác giả còn tích hợp thêm các chức năng khác như chuyển đổi font, kiểm tra chính tả,... Có thể thấy VietWare, VietKey là những phần mềm tiêu biểu cho lĩnh vực này, và trong thực tế, chúng đã đáp ứng được nhu cầu của phần lớn người sử dụng máy vi tính.

    II.Phương án giải quyết cho font ABC (.VnTime,…)

    II.1.Giới thiệu font ABC

    Đây là loại font 8 bit có các ký tự được thiết kế sẵn gồm cả dấu, không giống như loại font 2 byte có các ký tự chữ và ký tự dấu riêng. Bảng mã 8 bit có tất cả 256 ký tự, trong đó có 128 ký tự chuẩn không thể sửa đổi. Tuy nhiên, do tiếng Việt có 146 ký tự nên 128 ký tự còn lại của bảng mã này không đủ chỗ, đó là lý do làm cho font ABC có một số ký tự vi phạm các ký tự điều khiển chuẩn, và phải tách ra làm 2 file font cho chữ thường và chữ hoa của cùng một loại font. Điều này là một trong những điều bất tiện trong việc sử dụng loại font này.

    II.2. Ý tưởng chính

    Hãy bắt đầu bằng tình huống đơn giản sau : người dùng gõ phím ‘a’, tiếp theo là phím ‘1’ và ta đang dùng kiểu gõ VNI. Điều ta mong muốn lúc này là ký tự ‘a’ trên màn hình sẽ biến mất, thay vào đó sẽ là ký tự ‘á’.

    Một ý tưởng tự nhiên là cài đặt một hook WH_GETMESSAGE để hứng thông điệp WM_CHAR. Cách thực hiện như sau :

    · Hứng thông điệp WM_CHAR

    · Kiểm tra ký tự hiện tại là ‘1’, ký tự trước là ‘a’

    · Nếu đúng :

    · Dùng SendMessage gởi WM_CHAR với ký tự xoá

    · Sửa ký tự ‘1’ thành ký tự ‘á’

    · Nếu sai :

    · Không xử lý

    Ở đây phát sinh một vấn đề cần xem xét : có chắc là message WM_CHAR ta gởi sẽ đến được ứng dụng ? Với cách làm trên thì chỉ đúng với các ứng dụng đơn giản như Notepad, bởi thực chất Notepad chỉ là một EDIT_TEXT control được gắn vào một cửa sổ khung. Các thông điệp được gởi sẽ trực tiếp đến ứng dụng. Còn đối với các môi trường soạn thảo lớn và phức tạp hơn như WordPad hay Microsoft Word, chúng nhận thông điệp từ các sự kiện của người dùng qua nhiều cấp, do đó khi ta gởi message đến, không có gì đảm bảo message của chúng ta sẽ đến chính xác nơi cần đến.

    Còn bây giờ hãy xét cách tiếp cận sau đây : thay vì dùng các hàm để gởi thông điệp đến ứng dụng, ta sẽ làm phát sinh thông điệp một cách tự nhiên hơn, rồi sau đó thực hiện sự sửa đổi trên thông điệp để được ký tự có dấu mong muốn. Vẫn xét tình huống trên, cách làm bây giờ như sau :

    · Hứng message WM_KEYDOWN

    · Kiểm tra phím hiện tại là ‘1’, phím trước là ‘a’ (giá trị mã phím ảo)

    · Nếu đúng :

    · Sửa phím ‘1’ thành phím xóa (mã phím ảo VK_BACK).

    · Nếu sai :

    · Không xử lý.

    · Hứng message WM_CHAR

    · Kiểm tra ký tự hiện tại là ký tự xoá (có mã 8)

    · Nếu đúng:

    ·Dùng keybd_event để giả lập nhấn phím SpaceBar

    ·Nếu sai :

    · Không xử lý

    · Kiểm tra ký tự hiện tại là ký tự khoảng trắng (có mã 32)

    · Nếu đúng :

    · Sửa ký tự này thành ký tự ‘á’.

    · Nếu sai :

    ·Không xử lý

    Tất nhiên quá trình trên cần có một số thao tác với cờ hiệu để phân biệt khi nào phím BackSpace, SpaceBar được người dùng nhấn, khi nào 2 phím này là do chương trình của ta gởi đến. Tuy nhiên, để dễ thấy được ý tưởng chủ đạo của giải thuật vừa nêu, ta đã không đưa vào các thao tác này ở phần trên.

    Giải thích :

    Ý tưởng chính xuyên suốt giải thuật trên là thao tác giả lập gõ phím để phát sinh thông điệp trung gian, sau đó hứng lại thông điệp trung gian và thực hiện sửa đổi để có được ký tự mong muốn. Phím được chọn để giả lập trong trường hợp này là SpaceBar (Về nguyên tắc có thể chọn bất kỳ phím nào cho mục đích này, miễn là phím đó khi gõ có thể cho ta message WM_CHAR với mã ký tự hợp lệ).

    Có thể hình dung sự hoạt động của giải thuật trên như sau :

    · Khi người dùng gõ phím ’a’, Filter Function không làm gì cả.

    · Khi phím ‘1’ được gõ, điều kiện ở nhánh chặn message WM_KEYDOWN được thỏa, phím ‘1’ chuyển thành phím xóa (mã phím ảo VK_BACK) . Chính phím xóa này sẽ thực hiện thao tác xóa chữ ‘a’ trên màn hình soạn thảo.

    · WM_KEYDOWN đã được sửa đổi ở trên được chuyển thành WM_CHAR với mã ký tự 8, thoả điều kiện ở nhánh hứng WM_CHAR, SpaceBar được nhấn nhờ hàm keybd_event.

    · Thông điệp WM_KEYDOWN do phím SpaceBar sinh ra không bị chặn, tiếp tục chuyển thành WM_CHAR với mã ký tự 32. Lúc này, nó được Filter Function hứng lại, và sau khi kiểm tra các điều về cờ hiệu, Filter Function chuyển mã ký tự của message này thành mã ky tự của chữ ‘á’. Kết quả ta có là chữ ‘a’ bị xóa và chữ ‘á’ sẽ xuất hiện trên màn hình.

    Rõ ràng cách làm này an toàn hơn so với cách đầu tiên, bởi lẽ message được phát sinh y hệt như khi ta gõ phím thực sự (do được giả lập bằng hàm keybd_event), và chuyện lấy được các message này là chuyện của ứng dụng (bởi ta đã làm phát sinh message ở mức thấp), ta không cần phải can thiệp bằng lệnh SendMessage như phương án đầu tiên.

    Để thực hiện điều này, dĩ nhiên ta phải tổ chức Filter Function của hook WH_GETMESSAGE để có thể hứng được cả 2 thông điệp WM_KEYDOWN và WM_CHAR.

    II.3.Giải thuật chi tiết và cài đặt

    Dễ nhận thấy việc đầu tiên là phải ghi nhận các mã ký tự của các ký tự có dấu trong font ABC để thực hiện việc sửa đổi thông điệp sau này.

    Ta sẽ dùng tiện ích Character Map trong bộ System Tool của Windows để ghi nhận các ký tự và mã tương ứng của chúng, sau đó lưu các mã này vào một mảng toàn cục được khai báo trong DLL, lấy tên TCVN3 chẳng hạn. Nên tổ chức mảng này theo một thứ tự nhất định, ví dụ các mã của các ký tự có dấu của cùng một nguyên âm gốc nên đi liên tiếp nhau. Sau đó, phải ghi nhớ các chỉ số của các phần tử trong mảng tương ứng với từng ký tự, ví dụ phần tử mảng có giá trị là mã của chữ ‘á’ sẽ có chỉ số là bao nhiêu…

    Đến đây ta cần một biến toàn cục nữa, lấy tên Index chẳng hạn, để lưu các giá trị chỉ số vừa nêu khi người dùng nhấn phím, và các phím này nằm trong tầm vực bỏ dấu của chương trình. Ví dụ, khi họ gõ ‘a’, sau đó gõ ‘1’ thì giá trị của Index là chỉ số của phần tử mảng chứa mã ký tự của chữ ‘á’.

    Cách làm này sẽ tiện lợi khi ta thực hiện sửa đổi trên thông điệp WM_CHAR, rõ ràng lúc này giá trị wParam mới của message sẽ là TCVN3[Index].

    Khi cài đặt hook chặn message, nhánh chặn WM_KEYDOWN sẽ làm nhiệm vụ kiểm tra phím và xác định Index, chính nhánh hứng message WM_CHAR mới thực sự thi hành tác vụ sửa đổi thông tin trên thông điệp để sinh ra các ký tự có dấu.

    Sửa dấu trong khi gõ

    Với những thông tin trên , ta đã giải quyết được tình huống nêu ra ở phần đầu của giải thuật. Tất nhiên khi cài đặt ta sẽ áp dụng tổng quát cho các nguyên âm và các phím có thể sinh dấu từ ‘1’ đến ‘5’ (ta gọi phần này là Module_1) .

    Nhưng nếu người dùng muốn sửa dấu trong khi gõ thì sao ? Ví dụ, họ muốn có chữ ‘à’, nhưng các phím đã vô tình bị gõ là ‘a’ và ‘1’, lúc này trên màn hình xuất hiện chữ ‘á’, bây giờ khi họ gõ ‘2’, chữ ‘á’ phải bị xoá và thay vào đó là chữ ‘à’. Ta hãy xem giải pháp cho bài toán này :

    · Hứng message WM_KEYDOWN

    · Kiểm tra nếu phím hiện tại (CurrentKey) là các phím từ ‘1’ đến ‘5’, phím trước (Prev1Key) cũng cùng dạng này, và phím trước nữa (Prev2Key) là nguyên âm.

    · Nếu đúng :

    · Sửa phím hiện tại thành phím xóa (mã phím ảo VK_BACK)

    · Giả lập gõ 2 phím sau :

    · Prev2Key

    · CurrentKey

    · Nếu sai :

    · Không xử lý

    Giải thích (Xét trong ngữ cảnh tình huống nêu trên):

    Phím ‘2’ được sửa thành phím xóa sẽ thực hiện xóa ký tự ‘á’ trên màn hình. Sau đó 2 phím được giả lập sẽ tạo ra chữ ‘à’ như ta mong muốn. Ở dây ta thấy rõ hơn ý tưởng hứng lại sự kiện để xử lý : chỉ cần xoá ký tự sai, và giả lập gõ chính xác để tạo ký tự đúng. Lúc này chính Module_1 ta đã viết sẽ tiếp nhận các sự kiện giả lập và nó lại thi hành các tác vụ như ta đã mô tả : kiểm tra điều kiện để xác định Index, sửa message,… để cho ra ký tự mà người dùng muốn gõ.

    II.4. Áp dụng cho các luật bỏ dấu khác

    Với các ý tưởng trên, các luật bỏ dấu, sửa dấu trong khi gõ khác (bỏ dấu ô, ơ, ư, â, ă, ấ,…) được thực hiện với cách thức hoàn toàn tương tự. Có khác chăng là các điều kiện kiểm tra giá trị các phím trước phím hiện tại để xác định Index cho phù hợp.

    II.5. Ưu và nhược điểm của giải pháp

    Ta có thể nhận ra ưu điểm của giải pháp này ở tính đơn giản và an toàn của nó. Đơn giản trong quá trình cài đặt, mang tính tái sử dụng ; an toàn trong quá trình phát sinh các message cho ứng dụng.

    Tuy nhiên, cách làm này chỉ áp dụng được dễ dàng cho bài toán bỏ dấu, sửa dấu ngay sau nguyên âm mà thôi. Còn đối phó với vấn đề bỏ dấu tự do (bỏ dấu theo vần, theo từ), phương án này cần các thao tác kiểm tra rất phức tạp trên tập các giá trị phím trước, để xác định chính xác vị trí bỏ dấu. Bởi lẽ, nó phải duyệt trên giá trị của các mã phím ảo để xác định các nguyên âm có dấu, và điều này là rất khó khăn. Ta sẽ có một cách tiếp cận khác ưu việt hơn cho bài toán này ở phần kế sau đây, cách tiếp cận thao tác trực tiếp tên các ký tự.

    III.Phương án giải quyết cho font VNI-Win (VNI-Times,...)

    III.1.Cấu trúc font VNI-Win

    Khác với các font ABC(font 1 byte) có dấu và các nguyên âm được tổ hợp trong cùng một ký tự, font VNI-Win(font 2 byte) có dấu và các nguyên âm rời nhau. chẳng hạn ký tự ‘á’ là sự tổ hợp của hai ký tự ‘a’ và dấu sắc. Tuy nhiên, có một số ký tự do có cấu trúc đặt biệt nên dấu và ký tự được tổ hợp trong một ký tự như: đ, Đ, í, ì, ỉ, ĩ, ị, ỵ, Í, Ì, Ỉ, Ĩ, Ị, Ỵ, ư, ơ, Ư, Ơ.

    Chúng ta hãy bước vào phần sau để xem xét các phương án cho bộ gõ tiếng việt dùng cho font VNI-Win.

    III.2.Kiểu gõ dấu tại chổ

    Như đã đề cập ở phần trước, để xây dựng bộ gõ cho các phần mềm trong môi trường Windows ta sẽ cài đặt một hàm Hook có chức năng chặn ngắt bàn phím ở mức toàn cục. Và khi đó tất cả các phím nhấn từ bàn phím sẽ được chương trình gõ phím của chúng ta xử lý trước khi chúng được gởi tới ứng dụng. Như vậy để gõ được tiếng Việt trong các dụng dụng đang thi hành trong môi trường trong hàm Hook phải thực hiện thao tác thay đổi các ký tự khi người dùng gõ vào, nhờ đó ứng dụng sẽ nhận được các ký tự có dấu tiếng Việt.

    Giải thuật

    Ta sẽ cài đặt hàm Hook WH_GETMESSAGE hứng thông điệp WM_KEYDOWN và WM_CHAR để xử lý. Nhắc lại, khi người dùng nhấn phím ta sẽ lần lượt nhận được các thông điệp WM_KEYDOWN, WM_KEYUP, WM_CHAR. Chương trình ta sẽ xử lý ở thông điệp WM_KEYDOWN và WM_CHAR.

    Nguyên tắc khi người dùng nhấn một phím nằm trong phạm vi là phím bỏ dấu(có khả năng là phím dấu), 0-9 đối với kiểu gõ VNI và “s,f,r,x,j,a,o,e,w,d,z” đối với kiểu gõ TELEX. Ta xét ký tự trước đó, có 3 khả năng xảy ra:

    · Nếu là ký tự có thể bỏ dấua,e,i,o,u,y,d) nếu dấu phù hợp với kí tự cần bỏ dấu (ký tự liền trước) ta tiến hành bỏ dấu.

    · Nếu là một ký tự dấu rời hoặc một ký tự nguyên âm có dấu( chẳng hạn như “í” chỉ là một ký tự còn “á” là hai ký tự) ta xét là nên sửa dấu hay lập dấu và sau đó điều chỉnh lại cho phù hợp.

    · Nếu là một phụ âm thì ta không xử lý gì cả.

    Lưu ý ở đây có điểm khác biệt giửa font 1 byte và font 2 byte, khi bỏ dấu chẳng hạn chử “á” font 1 byte sẽ xóa ký tự “a” trước đó bằng cách sửa ký tự “1” vừa nhận được thành ký tự xóa VK_BACK và sau đó gởi một ký tự “á” còn font 2 byte sẽ gởi 2 ký tự “a” và dấu sắc sau khi xóa ký tự “a” trước đó.

    Bài toán lúc này trở nên đơn giản hơn và vấn đề lúc này là chương trình sẽ xử lý dấu như thế nào cho đơn giản và chính xác. Trước tiên ta cần phân biệt xử lý đối với các lọai dấu khác nhau:

    - loại 1: gồm các dấu: sắc, huyền, hỏi, ngã, nặng.

    - loại 2: gồm các dấu: dấu “^” và dấu “ă”.

    - lọai 3: gồm các dấu: dấu “^” kết hợp với một dấu loại 1(ví dụ: “ấ”, “ồ”, “ể”...)

    - loại 4: gồm các dấu: dấu “ă” kết hợp với một dấu loại 1(ví dụ: “ắ”, “ẵ”, “ặ”...”)

    Bài toán xử lý gõ tiếng việt cho font chữ 2 byte theo kiểu gõ cho phép bỏ dấu ngay đã được giải quyết thành công.

    Hàm Hook xử lý cho bài toán gõ tiếng việt font 2 byte kiểu gõ bỏ dấu tại chổ.


    Mã:
    EXPORT LRESULT CALLBACK HookProc2ByteNormalVNI(int nCode, WPARAM wParam, LPARAM lParam){    tagMSG *pMsg;    if (nCode>=0 )    {        pMsg = (MSG*)lParam;        if (wParam == PM_REMOVE)        {            if (pMsg->message == WM_KEYDOWN)            {                if (InScope(pMsg->wParam)||(pMsg- >wParam==VK_NUMPAD0&&DeleteTwo))                {                    switch (pMsg->wParam)                    {                        case VK_1:                        case VK_2:                        case VK_3:                        case VK_4:                        case VK_5:                            ProcessSign1(pMsg->wParam-VK_0);                            if (Del) pMsg->wParam = 8;                            break;                        case VK_6:                        case VK_8:                            ProcessSign2(pMsg->wParam-VK_0);                            if (Del) pMsg->wParam = 8;                        break;                        case VK_7:                            if Prev1Key==VK_o||Prev1Key==VK_O||Prev1Key==VK_u||Prev1Key==VK_U)                            {                                pMsg->wParam = 8;                                Del = TRUE;                                Flag = TRUE;                                switch (Prev1Key)                                {                                    case VK_o:                                        NextKey = o7;                                        break;                                    case VK_O:                                        NextKey = O7;                                        break;                                    case VK_u:                                        NextKey = u7;                                        break;                                    case VK_U:                                        NextKey = U7;                                        break;                                }                            }                         break;                        case VK_9:                            if (Prev1Key==VK_d||Prev1Key==VK_D)                            {                                pMsg->wParam = 8;                                Del = TRUE;                                Flag = TRUE;                                if (Prev1Key == VK_d) NextKey = a9;                                else NextKey = A9;                            }                            break;                        case VK_NUMPAD0:                            if (DeleteTwo)                            {                                pMsg->wParam = 8;                                Del = TRUE;                                Flag = TRUE;                                DeleteTwo = FALSE;                            }                    }                }            }            if (pMsg->message == WM_CHAR)            {                if (Flag)                {                    switch (pMsg->wParam)                    {                        case VK_1:                        case VK_2:                        case VK_3:                        case VK_4:                        case VK_5:                        case VK_6:                        case VK_7:                        case VK_8:                        case VK_9:                            if (!Del)                            {                                pMsg->wParam = NextKey;                                Flag =FALSE;                            }                            break;                        case VK_BACK:                            if (Del) keybd_event(VK_NUMPAD0, 0x45, KEYEVENTF_EXTENDEDKEY, 0);                            break;                        case VK_0:                            if (Del)                            {                                pMsg->wParam = NextKey;                                Flag = FALSE;                            }                            break;                    }//of switch                }//of if (Flag)                                if (!Del) // Normal                {                    Prev2Key = Prev1Key;                    Prev1Key = pMsg->wParam;                }                else                {                    Prev1Key = NextKey;                    Del=pMsg->wParam ==8;                }            }//of if (pMsg->message==WM_CHAR)        }    } // of if (nCode >= 0)    return CallNextHookEx(hhook, nCode, wParam, lParam);}
    III.3.2.Kiểu gõ dấu cải tiến:

    Khi gõ theo kiểu bỏ dấu tại chổ có những mặt hạn chế: thứ nhất cách gõ không được tự nhiên thoải mái như khi viết và bỏ dấu bằng tay, thứ hai rất khó cho người dùng sửa dấu sau khi đã đánh sang ký tự khác, thứ ba có thể người dùng sẽ bỏ dấu không đúng chính tả hay không đúng vần. Vấn đề dặt ra ờ đây là làm thế nào để hỗ trợ được những tính năng khiếm khuyết trên.

    Giải pháp gõ dấu sau

    Một ví dụ cụ thể về gõ dấu sau, chẳng hạn như khi người dùng gõ từ “làng” thì họ có thể gõ theo nhiều cách khác nhau như: la2ng, lan2g, lang2.

    · Giải pháp ban đầu

    Giải pháp ban đầu được đưa ra là sử dụng Clipboard, sau khi người dùng gõ một phím có khả năng là phím dấu ta giả lập một phím Shitf và các phím mủi tên ß để đánh dấu khoảng 8 ký tự vừa gõ, sau đó giả lập tổ hợp phím Ctrl X để cắt chuổi 8 ký tự được gõ trước ký tự hiện hành vào Clipboard, tiếp theo ta sẽ lấy dữ liệu ra từ Clipboard và đổ vào mảng ký tự và xử lý dấu trên chúng, dữ liệu được xử lý xong được đưa vào Clipboard, cuối cùng ta sẽ giả lập một tổ hợp phím Ctrl V để dán chuổi vừa xử lý ra màn hình ứng dụng.

    Việc áp dụng Clipboard theo hương tiếp cận này dẫn đến nhiều khiếm khuyết và khó khăn khi xử lý. D không phải tất cả các ứng dụng đều dùng phím Shift và các phím mủi tên để đánh dấu, và dùng phím Ctrl X, Ctrl V để cắt và dán. Hơn nửa, mổi khi một phím trong phạm vi xử lý dấu được nhấn thì ứng dụng phải thực hiện đồng thời 2 thao tác cắt và dán, việc này làm cho chương trình trở chậm chạp, và màn hình của ứng dụng chớp, nhấp nháy trong vùng bị cắt và dán lại. Một ý kiến hay là chỉ nên áp dụng Clipboard cho trường hợp dùng Sửa dấu nhanh(Chức năng này hổ trợ cho người sử dụng khi họ đã hoàn tất văn bản và kiểm tra lại văn bản từ đầu đến cuối một lần và sửa lỗi).

    · Giải pháp khác

    Một giải pháp khác là dùng một buffer thay thế cho Clipboard, tại mỗi thời điểm buffer sẽ lưu giữ giá trị của 8 ký tự(từ dài nhất) trước đó. Khi người dùng gõ một ký tự trong phạm vi bỏ dấu, ta sẽ cập nhật lại buffer cho phù hợp, xóa một số ký tự cần thiết và xuất một số ký tự đã được cập nhật trong buffer.

    Ví dụ:

    Khi gõ : lang giá trị buffer sẽ là:

    l
    a
    n
    g


    Khi gõ thêm ký tự: 2, có nghĩa là bỏ dấu huyền. buffer sẽ được cập nhật lại như sau:

    l
    a
    \
    n
    g

    Và khi đó ta phải xóa ngược lại 3 ký tự(xóa “ang”) và xuất ra 4 ký tự cuối của buffer(xuất “àng”).

    Như vậy mổi lần người dùng gõ dấu ta phải hiệu chỉnh lại buffer và xác định số ký tự cần phải xóa và số ký tự cần xuất ra từ buffer(tính từ vị trí cuối cùng).

    Đối với các ký tự khác không phải bỏ dấu thì ta không xử lý nhưng vẫn phải cập nhật lại trong buffer.

    Ví dụ:

    Các trạng thái của buffer khi gõ chữ “làng”.


    Gõ “l”
    l

    Gõ “a”
    l
    a

    Gõ “n”
    l
    a
    n

    Gõ “g”
    l
    a
    n
    g

    Gõ “2”
    l
    a
    \
    n
    G

    Lúc này DelNum = 3(số ký tự cần xóa) và OutNum =4(số ký tự cần xuất).

    Đến đây thì vấn đề gõ dấu sau được giải quyết khá hoàn hảo. Do sử dụng buffer nên chương trình xử lý nhanh hơn, và tại mổi thời điểm chỉ xóa đi số ký tự tố thiểu cần thiết nên giúp cho màn hình ứng dụng không bị nhấp nháy.

    · Nẩy sinh vấn đề

    Tuy giải pháp trên giải quyết rất tốt vần đề đặt ra nhưng bên cạnh đó lại nẩy sinh ra một khó khăn khác là làm thế nào để xác định vị trí bỏ dấu trong một vần tiếng việt, đây là vấn đề khó khăn và phức tạp bởi vì trong tiếng việt không có quy luật bỏ dấu cụ thể, chuẩn mực. Cuối cùng đưa đến việc xây dựng bộ luật bỏ dấu dành cho tiếng Việt.

    · Bộ luật bỏ dấu tiếng Việt

    Do trong tiếng việt dấu chỉ được bỏ ở các vị trí của các nguyên âm nên bộ luật sẽ được xây dựng dựa trên các nguyên âm, chú ý rằng trong một từ tiếng việt các nguyên âm luôn đi liền nhau và số lượng nguyên âm trong một từ không thể quá 3 nguyên âm. Với hai nhận xét trên giúp ta có thể xây dựng bộ luật bỏ dấu tiếng Việt một cách dễ dàng hơn.

    Phân chia các từ theo số lượng nguyên âm:

    Các từ có 1 nguyên âm:

    Nếu ký tự đứng trước nguyên âm là ký tự “q” hoặc “p” thì không được phép bỏ dấu.

    Ngược lại dấu sẽ được bỏ tại vị trí nguyên âm duy nhất này.

    Các từ có 2 nguyên âm:

    Nguyên âm đầu: a

    A


    vị trí bỏ dấu

    AE


    không có

    AI

    A

    AO


    A

    AU

    A

    AY


    A

    Nguyên âm đầu: e

    E


    vị trí bỏ dấu

    EA


    không có

    EI


    không có

    EO


    E

    EU


    E

    EY


    không có

    Nguyên âm đầu: i

    I


    vị trí bỏ dấu

    IA


    I

    IE


    E

    IO


    không có

    IU


    I

    IY


    không có

    Nguyên âm đầu: o

    O


    vị trí bỏ dấu

    OA


    O

    OE


    O

    OI


    O

    OU


    không có

    OY


    không có

    Nguyên âm đầu: u

    U


    vị trí bỏ dấu

    UA


    U

    UE


    E

    UI


    U

    UO


    O

    UY


    U

    Nguyên âm đầu: y

    Y


    vị trí bỏ dấu

    YA


    Không có

    YE


    Không có

    YI


    Không có

    YO


    Không có

    YU


    Không có

    Theo sơ đồ xử lý bên dưới.

    Các từ có 3 nguyên âm:

    Có các tổ hợp 3 nguyên âm sau:

    ươu rượu

    ieu liễu

    oai hoài

    uay khuấy

    uoi muỗi

    uya khuya

    uye khuyên

    yeu* yêu

    (*Đây là dạng đặt biệt được sử lý riêng, vần yêu chỉ đúng trong trường hợp đứng riêng lẻ không đi cùng với phụ âm đứng trước)

    Thông thường các từ có 3 nguyên âm thường được bỏ dấu ở nguyên âm giữa, chỉ trừ trường hợp vần “uyê” được bỏ dấu ở nguyên âm cuối.

    Trường hợp đặc biệt nếu bắt đầu bằng “qu” hoặc “gi” thì dấu sẽ bỏ ở một trong hai nguyên âm sau được xử lý như từ có hai nguyên âm.

    SƠ ĐỒ XỬ LÝ DẤU CHO CÁC TỪ CÓ 2 NGUYÊN ÂM:

    · Nếu nguyên âm đầu là:

    “a” thì

    nếu nguyên âm sau là “a”,”e”:không bỏ dấu

    ngược lại:bỏ dấu tại nguyên âm đầu a.

    “e” thì

    nếu nguyên âm sau là “o”,”u”:bỏ dấu tại nguyên âm đầu e.

    ngược lại:không bỏ dấu

    “i” thì

    nếu trước “i” là phụ âm “g”:bỏ dấu ở nguyêm âm sau.

    ngược lại

    nếu nguyên âm sau là “i”,“o”,”y”:không bỏ dấu

    ngược lại

    nếu nguyên âm sau là “a”,”u”:bỏ dấu ở nguyên âm đầu.

    ngược lại:bỏ dấu ở nguyên âm sau

    “o” thì

    nếu nguyên âm sau là “o”,”u”,”y”:không bỏ dấu

    ngược lại:bỏ dấu ở nguyên âm đầu.

    “u” thì

    nếu trước “u” là phụ âm “q”:bỏ dấu ở nguyêm âm sau.

    ngược lại

    nếu nguyêm âm sau là “a”,”i”,”y”:bỏ dấu ở nguyên âm đầu

    ngược lại

    nếu nguyên âm sau là “e”,”o”:bỏ dấu ở nguyên âm sau

    ngược lại:không bỏ dấu.

    “y” thì không bỏ dấu.

    Vấn đề giải quyết được có vẻ phức tạp hơn nhưng với bộ luật xử lý như trên ta có thể cài đặt bộ gõ có chức năng kiểm tra lổi chính tả, không cho phép gõ những từ sai lỗi chính tả và có khả năng bỏ dấu sau rất tốt. Chắc bạn sẽ rất ngạc nhiên nếu như tôi đưa ra một vấn đề vướng mắc khác cho giải pháp gần như hoàn hảo này. Thật vậy, khi bạn gõ “hoa” bạn gõ thêm dấu huyền dĩ nhiên bộ gõ này sẽ bỏ dấu ngay nguyên âm “o” trở thành “hòa” hoàn toàn chính xác. Nhưng điều gì sẽ xảy ra nếu bạn gõ thêm “n” nó sẽ trở thành “hòan” đây là một điểm mà một số bộ gõ phím như VietWare, VietKey,... và các bộ gõ phím thông dụng khác chưa xử lý.

    Vì vậy để chương trình gõ phím dễ dùng, tiện dụng và thông minh hơn, bạn nên cài thêm chức năng sửa dấu để điều chỉnh dấu khi phát hiện dấu đã được bỏ sai vị trí.Điều này cũng được thực hiện dễ dàng nhờ vào bộ luật mà chúng ta đã xây dựng từ trước nhưng phải kết hợp thêm một số điều kiện ngữ cảnh, có nghĩa là việc xác định vị trí bỏ dấu không chỉ phụ thuộc vào các nguyên âm mà còn phụ thuộc vào các phụ âm trước hoặc sau các nguyên âm đó.

  5. #5
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    hay wa. [IMG]images/smilies/laughing.gif[/IMG] sao không có ai like thế này thank mấy anh mong chia sẽ nhiều cho đàn em học hỏi

  6. #6
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Hi all,

    muốn hook multiple key trên keyboard thì làm sao vậy mọi người (Ctrl + .....)?

  7. #7
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Bạn ơi nếu sử dụng hook trong xử lý chuột để khi mình kéo và giữ chuột trên màn hình thì sẽ xác lập hình ảnh trên vùng màn hình mình vừa kéo ấy thì phải làm thế nào hả bạn

 

 

Quyền viết bài

  • Bạn Không thể gửi Chủ đề mới
  • Bạn Không thể Gửi trả lời
  • Bạn Không thể Gửi file đính kèm
  • Bạn Không thể Sửa bài viết của mình
  •