Trao đổi với tôi

www.hdphim.info

3/30/10

[Hooking] Inject Code Cave

Author : Benina (REA team) - 2010
Blog: Rootbiez.tk

Hi các bạn!

Hôm nay tôi sẽ mô tả kỹ thuật inject code cave để bổ sung cho bài viết của bác meoconlongvang về “Kỹ thuật Inject Code trong Windows”. Trong bài viết của bác meoconlongvang không nói đến pp này. Bài viết này ko có gì cao siêu cả, nó chỉ để cho tôi lưu trữ mà thôi.

Phương pháp này đã được Darawk mô tả trong bài viết “Dll Injection Tutorial” (link http://www.edgeofnowhere.cc/viewtopic.php?p=2483118) . Nhưng trong bài viết trên, không biết vô tình hay cố ý, tác giả coding ví dụ về phương pháp này bị sai nên code không chạy được. Vì vậy có rất nhiều câu hỏi trên google về phương pháp này. Nhưng tôi search không thấy câu trả lời nào làm cho code này chạy đúng. Hôm nay mạo mụi share với các bạn source code valid.

OK, nói thêm một chút về source trong tut này. Cũng như tất cả các bài viết của tôi. Source code đính kèm đều không chuẩn mực. Một mặt là do lười biếng viết chuẩn xác, một mặt là do kiến thức bản thân có hạn. Nhưng đều quan trọng là code trong các ví dụ đều run tốt trong hoàn cảnh thực nghiệm như hệ điều hành, những giả định ban đầu. Mong các bạn bỏ qua phê bình mảng này nhe.

http://www.mediafire.com/?niujynkm2zr

3/27/10

[Programming] SỰ TINH TẾ CỦA TYPEDEF

Với người lập trình C, việc bắt gặp và sử dụng TypeDef không có gì là lạ lẫm hay khó hiểu. Thế nhưng không phải ai cũng có thể thấy hết được sự tiện lợi và hiệu quả trong việc sừ dụng typedef cũng như bản chất bên trong của nó là gì. Chúng ta hãy cùng tìm hiểu vấn đề này.

Mọi người đầu biết typedef đơn thuần là tạo thêm một tên mới cho một cho kiểu dữ liệu có sẵn, có nghĩa là không tạo ra một kiểu dữ liệu mới. Ví dụ :

typedef unsigned long int uli;
uli i, j;
unsigned long int x, y;

Trong đọan mã khai báo trên, các biến i, j có kiểu hoàn tòan giống với biến x, y. Thực ra, về mặt chức năng, typedef giống với #define. Ngoại trừ một điều : lện typedef được thông dịch bởi trình biên dịch, nghĩa là typedef có thể có được tính năng mà các lệnh tiền xử lý thông thường không thể nào đạt đến.

Có 3 lý do khiến chúng ta dùng typedef :

1- Các câu lệnh khai báo phức tạp sẽ được viết dễ dàng hơn bằng cách dùng typedef. Với cách này, người lập trình loai trừ được phần lớn sự phức tạp của chương trình. Việc sử dụng typedef hợp lý sẽ khiến chương trình trong sáng hơn nhiều.

2- Hỗ trợ cho việc tạo các chương trình linh hoạt, dễ di chuyển từ máy này sang máy khác. Nghĩa là, nếu dùng typedef để định nghĩa một kiểu dữ liệu phụ thuộc vào một cấu trúc máy nào đó, khi chương trình được đem đến một máy mới, chúng ta chỉ cần thay đổi câu lệnh typedef tương ứng với kiểu dữ liệu trên máy này.
Ví dụ minh họa cho tính năng này là dùng typedef định nghĩa kiểu đại diện cho số nguyên (kích thước của kiểu này thay đổi phụ thuộc vào máy tính), sau đó chúng ta sẽ lựa chọn một kiểu phù hợp (vd : short, long hay int) tùy thuộc vào máy tính chứa chương trình.

3- Giúp chương trình trở nên dễ đọc, dễ hiểu. Ví dụ : một nút của danh sách liên kết đôi sẽ dễ hiểu hơn khi được viết dưới dạng ptr_to_list thay vì là một con trỏ trỏ đến một cấu trúc phức tạp.

Bây giờ, chúng ta đề cập đến một vài nét tinh tế trong viêc sử dụng typedef. Hãy xem đoạn mã sau :

typedef char * string_t;
#define string_d char *;

string_t s1, s2;
string_d s3, s4;

Bạn có thể nhìn thấy được sự khác biệt của cách dùng typedef và #define ?

Trong các câu lệnh khai báo trên, s1, s2 có kiểu là char*, nhưng s3, s4 có kiểu là char (kiểu ký tự), không phải là char* như mong muốn. Vì vậy, trong đa số trường hợp, typedef được ưa chuộng hơn #define vì nó có thể mã hóa kiểu pointer một cách chính xác. Không những vậy, typedef còn có ưu điểm là tuân theo các luật về tầm vực, nghĩa là typedef có thể được khai báo một cách cục bộ bên trong một chương trình con hay một khối lệnh trong khi #define luôn tạo ra môt tác động có tính toàn cục.

(theo baohoclaptrinh.com)

3/26/10

[Programming] Installing Windows SDK

This tutorial describes the process of the installation of the latest version of the Windows SDK

The Microsoft Windows SDK can be installed by downloading the selected version of the SDK from the Microsoft website online.

Contents

[hide]

Choosing a Proper Version

Two versions of Windows SDK currently exist. The first is the latest version of Windows SDK, simply known as the Microsoft Windows SDK, and the older version known as Microsoft Platform SDK. The Windows SDK has been released for Windows Vista but works on both 2003 and XP. The Platform SDK was released for Windows Server 2003 R2, but is compatible with XP and 2000. This information is presented in the following table:

SDK Version Windows Vista Windows Server 2003 Windows XP Windows 2000
Windows SDK Yes Yes Yes No
Platform SDK No Yes Yes Yes

Requirements

For operating system requirements, each version of the SDK has different requirements as shown in the table above. The Windows/Platform SDK also requires a version of Microsoft Visual Studio's Visual C++ to be installed on your machine. So, before installing the Windows SDK, make sure that you have installed Microsoft Visual Studio on your machine.

Downloading the SDK

1- There seems to be an error with the Web Installer that causes it to stop responding after the selection of which components to download. If the issue occurs on your computer, you must download the .ISO version.

Installing the SDK

Only the installation process of the Windows SDK will be described
Installing Windows SDK Components
If the (apparently flawed) web install setup executable does work, then it might potentially save you a lot of downloading time. Though ultimately it depends on the reason you're downloading the Windows SDK, you can potentially save up to 1 GB of downloading if you choose not to download and install Documentation and Samples. These are necessary if you wish to learn more about the Windows software developments locally, but can be replaced by other online sources easily.

Alternatively, you could download the entire .ISO file from the internet and burn it with appropriate burning software onto a DVD disc, and install the Windows SDK fully.

After choosing the directory, components, and all other necessary settings, you can now proceed to the installation (and, in the web install, downloading) process. This will install the SDK in the installation destination directories. This is however not enough! This will not configure header files to run with your copy of Microsoft Visual Studio.

For more instructions on how to burn an ISO file onto a DVD disc, see Burning ISO Files

Configuring the Windows SDK with Visual Studio

After making sure you have installed both Microsoft Visual C++ and the Windows SDK, you need to update the Visual C++ directories in the Projects and Solutions section in the Options dialog box.

After the Options dialog box has been open, we navigate to (A) VC++ Directories, found under Projects and Solutions. The then add three entries (B) using the new line button. The three entries added (C) are:

  1. Executable Files: %path_to_windows_sdk%\Bin
  2. Include Files: %path_to_windows_sdk%\Include
  3. Library Files: %path_to_windows_sdk%\Lib
If you are using Microsoft Visual C++ 2005, you must, navigate to the Microsoft Visual C++ 2005 directory, usually found here (C:\Program Files\Microsoft Visual Studio 8\VC)

Next, navigate to VCProjectDefaults found in the Visual C++ directory. All in all, this means that you should navigate to:

C:\Program Files\Microsoft Visual Studio 9.0\VC\VCProjectDefaults

Then, edit corewin_express.vsprops by changing:

AdditionalDependencies="kernel32.lib"

to

AdditionalDependencies="kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib"
In Visual C++ 2008, the change is already made.

Now, you can create Win32 application software that takes advantage of the Windows SDK header files, libraries, and other tools.


reflink: http://wiki.yoyogames.com/index.php/Installing_Windows_SDK

[Programming] Install Windows SDK

Viết bởi: Benina

Khi bạn biên dịch trong VC++ 6.0 gặp các lỗi sau:

fatal error C1083: Cannot open include file: 'Psapi.h”

Hay

C:\Documents and Settings\Da BuzZ\RageOGC\ugc-beta.dll\main.cpp(444) : error C2065: 'OpenThread' : undeclared identifier
C:\Documents and Settings\Da BuzZ\RageOGC\ugc-beta.dll\main.cpp(444) : error C2440: '=' : cannot convert from 'int' to 'void *'

Điều này chứng tỏ máy bạn ko cài đặt SDK. Để khắc phục các lỗi trên ta làm như sau:

1/Dowload SDK:

Windows XP SP2 SDK

http://www.microsoft.com/msdownload/platformsdk/sdkupdate/XPSP2FULLInstall.htm

Windows SDK for Windows Server 2008 and .NET Framework 3.5

http://www.microsoft.com/downloads/en/confirmation.aspx?familyId=f26b1aa4-741a-433a-9be5-fa919850bdbf&displayLang=en

2/Install SDK :

Cài đặt SDK vào thư mục:

C:\Program Files\Microsoft Platform SDK

3/Setup VC++ 6.0:

Trong VC++ 6.0 ta vào menu Tools/Option

Chỉnh bốn mục sau bằng cách thêm vào đường dẫn cài đặt SDK trên máy tính của bạn tương ứng với các thư mục BIN, Include, Lib và src (Source files):

Ví dụ:

Thêm vào đường dẫn C:\Program Files\Microsoft Platform SDK\Bin vào Show directories for: Executable files

clip_image002

Và thêm vào C:\Program Files\Microsoft Platform SDK\Include trong

clip_image004

Thêm C:\Program Files\Microsoft Platform SDK\Lib vào

clip_image006

Thêm các source file

clip_image008

Hoặc ta vào menu Start của Windows chọn như sau:

clip_image010

3/22/10

[Iczelion's Tuts] Tutorial 24: Windows Hooks

Chúng ta sẽ học về Windows hooks trong tut này. Windows hooks rất giàu năng lực. Với chúng, bạn có thể thọc sâu vào bên trong các processes khác và đôi khi biến đổi các hành vi của chúng.
Download the example [tut24.zip]


Lý thuyết:

Windows hooks có thể được xem xét như một trong các đặc trưng đầy năng lực nhất của Windows. Với chúng, bạn có thể bẫy các events (biến cố) sẽ xảy ra, hoặc trong process của chính bạn hoặc trong các processes khác. Bằng cách "hooking" (móc vào), bạn nói cho Windows biết một hàm function, đó là hàm filter function (hàm lọc) cũng được gọi là hook procedure (thủ tục hook), hàm này sẽ được gọi mọi lúc khi một event (biến cố) mà bạn quan tâm xảy ra. Chúng có 2 lọai: local hooks và remote hooks.

  • Local hooks bẫy các biến cố events mà nó sẽ xảy ra trong process của chính bạn.
  • Remote hooks bẫy các biến cố events mà nó sẽ xảy ra trong các process(es) khác. Có 2 lọai remote hooks
    • thread-specific (tuyến cụ thể) bẫy các biến cố events mà nó sẽ xảy ra trong một thread cụ thể trong process khác. Ngắn gọn là, bạn muốn quan sát các events trong một thread cụ thể trong một process cụ thể.
    • system-wide (tòan hệ thống) bẫy các biến cố events đã dự định cho tất cả các thread trong tất cả các processes trong hệ thống.

Khi bạn cài đặt hooks, hảy nhớ rằng chúng tác dụng đến họat động của hệ thống. System-wide hooks (các hook tòan hệ thống) là khét tiếng nhất. Khi ALL liên quan đến các events được gởi qua filter function (hàm lọc) của bạn, system của bạn có thể chậm lại một cách đáng quan tâm. Vì vậy nếu bạn dùng system-wide hook, bạn sẽ sử dụng nó một cách thận trọng và unhook nó ngay khi bạn ko cần nó. Cũng vậy, bạn có một sự rủi ro cao làm phá vỡ (crashing) các tiến trình processes khác, khi bạn có thể can thiệp vào các processes khác và nếu vài thứ bị sai trong filter function của bạn, nó có thể lôi kéo các processes khác trở nên bị lãng quên chung với nó. Hảy nhớ rằng: năng suất máy sẽ bị ảnh hưởng..
Bạn phải hiểu các công việc hook (móc vào) như thế nào trước khi bạn sử dụng nó một cách hiệu quả. Khi bạn cài đặt một hook, Windows cài đặt một cấu trúc data trong memory, chứa các thông tin về hook, và thêm nó vào một danh sách liên kết các hooks tồn tại sẳn. Một hook mới sẽ được thêm vào trước các hook cũ. Khi một biến cố xảy ra, nếu bạn install một local hook, hàm filter function trong process của bạn được gọi vì vậy nó “trực tiếp “ hơn. Nhưng nếu nó là một remote hook, system phải tiêm vào một code cho hook procedure trong vùng địa chỉ (address space(s) ) của các process(es) khác. Và hệ thống có thể chỉ thực hiện được nếu hàm function cư trú trong một DLL. Vì vậy , nếu bạn muốn dùng một remote hook, hook procedure của bạn phải cư trú trong một DLL. Có 2 ngọai lệ đối với quy tắc này: journal record hooks (các hook thu lại thường nhật) và journal playback hooks (các hook phát lại thường nhật). Hook procedures cho hai hooks này phải cư trú trong thread cài đặt của các hooks. Lý do tại sao nó phải là như vậy chính là: cả hai hooks đề cập sự chặn lớp thấp (low-level interception )của hardware input events (các biến cố nhập liệu phần cứng). Input events (biến cố nhập liệu) phải được recorded/playbacked (thu lại/phát lại) theo thứ tự mà chúng xuất hiện. Nếu code của hai hooks này ở trong một DLL, input events (các biến cố nhập liệu) có thể xuất hiện rãi rác trong vài threads nào đó và nó ko thể biết thứ tự của chúng. Hướng giải quyết: hook procedure của hai hooks này chỉ phải ở trong một thread đơn , có nghĩa là thread mà nó install các hooks này.
Có 14 lọai hooks:

  • WH_CALLWNDPROC called when SendMessage is called
  • WH_CALLWNDPROCRET called when SendMessage returns
  • WH_GETMESSAGE called when GetMessage or PeekMessage is called
  • WH_KEYBOARD called when GetMessage or PeekMessage retrieves(tìm lại) WM_KEYUP or WM_KEYDOWN from the message queue(hàng)
  • WH_MOUSE called when GetMessage or PeekMessage retrieves a mouse message from the message queue(hàng)
  • WH_HARDWARE called when GetMessage or PeekMessage (peek : nhìn trộm) retrieves some hardware message that is not related (liên quan,liên hệ) to keyboard or mouse.
  • WH_MSGFILTER called when a dialog box, menu or scrollbar is about to process a message. This hook is local. It's specifically (đặc trưng, riêng biệt) for those objects which have their own internal (bên trong) message loops.
  • WH_SYSMSGFILTER same as WH_MSGFILTER but system-wide (wide: mở rộng)
  • WH_JOURNALRECORD called when Windows retrieves (lấy lại, gọi ra) message from the hardware input queue (hàng)
  • WH_JOURNALPLAYBACK called when an event is requested (yêu cầu) from the system's hardware input (nhập liệu) queue.
  • WH_SHELL called when something interesting (quan tâm) about the shell occurs such as when the task (tác vụ) bar needs to redraw its button.
  • WH_CBT used specifically (đặc trưng) for computer-based training (huấn luyện)(CBT).
  • WH_FOREGROUNDIDLE used internally(bên trong) by Windows. Little (ít) use for general (thông thường) applications
  • WH_DEBUG used to debug the hooking procedure

Bây giờ chúng ta cần biết một chút về lý thuyết, Chúng ta có thể thực hiện như thế nào để install/uninstall các hooks.

Để install một hook, bạn gọi hàm SetWindowsHookEx theo cú pháp sau đây:

SetWindowsHookEx proto HookType:DWORD, pHookProc:DWORD, hInstance:DWORD, ThreadID:DWORD

  • HookType là một trong những giá trị đã liệt kê bên trên, ví dụ: WH_MOUSE, WH_KEYBOARD
  • pHookProc là address của hook procedure (thủ tục hook) mà chúng sẽ được gọi để xử lý các thông điệp messages cho hook đã chỉ định. Nếu hook này là một remote hook, nó phải cư trú trong một DLL. Nếu khác, nó phải ở trong process của bạn.
  • hInstance là instance handle của DLL mà hook procedure cư trú trong đó. Nếu hook là một local hook, giá trị này phải là NULL
  • ThreadID là chỉ mục ID của thread bạn muốn cài đặt hook để theo dõi (spy on). Tham số này là một tham số xác định hook là local hay là remote. Nếu tham số này là NULL, Windows sẽ hiểu hook như là một system-wide remote hook mà nó ảnh hưởng đến tất cả các threads trong system. Nếu bạn chỉ định thread ID của một thread trong process của riêng bạn, hook này là một local hook. Nếu bạn chỉ định thread ID từ process khác, hook là một thread-specific remote hook. Có 2 ngoại lệ đối với rule này: WH_JOURNALRECORDWH_JOURNALPLAYBACK luôn luôn là local system-wide hooks mà chúng ko yêu cầu nằm trong một DLL. Và WH_SYSMSGFILTER luôn là một system-wide remote hook. Thực chất nó chính là WH_MSGFILTER hook với ThreadID==0.

Nếu call thành công, nó sẽ trả về một hook handle trong eax. Nếu ko thành công, trả về giá trị NULL. Bạn phải save hook handle cho việc unhooking sau này.

Bạn có thể uninstall một hook bằng cách gọi hàm UnhookWindowsHookEx mà nó chỉ chấp nhận một tham số, đó là handle của hook mà bạn muốn uninstall. Nếu gọi thành công, nó trả về một giá trị non-zero trong eax. Nếu khác, nó returns giá trị NULL.

Bây giờ bạn biết như thế nào để install/uninstall hooks, chúng ta có thể nghiên cứu thủ tục hook procedure.
Hook procedure sẽ được gọi bất cứ khi nào một event (biến cố) xảy ra mà event đó được kết hợp với lọai hook mà bạn đã install .Cho ví dụ, nếu bạn install WH_MOUSE hook, khi một mouse event xảy ra, hook procedure của bạn sẽ được gọi . Bất chấp lọai hook mà bạn đã cài đặt installed, hook procedure luôn có prototype như sau:

HookProc proto nCode:DWORD, wParam:DWORD, lParam:DWORD

    • nCode chỉ ra hook code.
    • wParam và lParam chứa thông tin thêm về biến cố event

HookProc thực tế là một cái tên dùng để lưu giữ tên của hàm. Bạn có thể đặt tên nó bất kỳ gì mà bạn thích miễn là nó có prototype như trên. Giá trị thể hiện của nCode, wParam và lParam được dựa trên lọai hook mà bạn install. Cũng như giá trị trả về từ hook procedure. Cho ví dụ:

WH_CALLWNDPROC

  • nCode có thể chỉ là HC_ACTION có nghĩa là có một message được gởi từ window
  • wParam chứa message đang gởi đến, nếu nó ko là zero
  • lParam trỏ đến một cấu trúc CWPSTRUCT structure
  • return value: ko được sử dụng, return zero

WH_MOUSE

  • nCode có thể là HC_ACTION hay HC_NOREMOVE
  • wParam chứa mouse message
  • lParam trỏ đến một cấu trúc MOUSEHOOKSTRUCT structure
  • return value: zero nếu message đã được tiến hành. 1 nếu message được lọai bỏ.

Lời cuối cùng muốn nói với bạn là: bạn phải tra cứu win32 api reference để biết thêm chi tiết về ý nghĩa của các tham số và giá trị trả về của hook mà bạn muốn install.

Bây giờ bạn đã nắm bắt được một ít về hook procedure. Hảy nhớ rằng các hooks được trói lại (liên kết lại) trong một danh sách liên kết (linked list ) và hook được install gần nhất nằm tại đầu của danh sách liên kết hook. Khi một event xảy ra, Windows sẽ chỉ gọi hook đầu tiên trong chuổi mắt xích liên kết đó . Hook procedure của bạn sẽ chịu trách nhiệm gọi hook kế tiếp trong chuổi mắt xích (chain : chuổi mắt xích). Bạn có thể ko cần phải gọi hook kế tiếp nhưng bạn phải hiểu kỹ những gì bạn đang làm khi ko gọi hook kế tiếp. Phần lớn trong thực hành để đạt kế quả tốt là gọi procedure kế tiếp , do vậy các hooks khác có thể có tham gia đóng góp tại lúc biến cố event xảy ra. Bạn có thể gọi hook kế tiếp bằng cách gọi hàm CallNextHookEx mà nó có prototype như sau:

CallNextHookEx proto hHook:DWORD, nCode:DWORD, wParam:DWORD, lParam:DWORD

  • hHook là hook handle của riêng bạn. Hàm này sử dụng handle này để vượt qua linked list và tìm kiếm hook procedure mà nó sẽ gọi kế tiếp.
  • nCode, wParam lParam bạn có thể truyền ba giá trị này mà bạn nhận được từ Windows cho hàm CallNextHookEx.

Một chú ý quan trọng về remote hooks: hook procedure phải cư trú trong một DLL mà nó sẽ được mapped (ánh xạ) vào trong các processes khác. Khi Windows map DLL vào trong các processes khác, nó sẽ ko map data section(s) vào trong các processes khác đó . Nói tóm lại, tất cả các processes sẽ cùng chia sẽ một bản sao chép code thực thi nhưng chúng sẽ có bản copy riêng DLL's data section cho chính chúng! Đây có thể là một ngạc nhiên lớn làm ta mất cảnh giác. Bạn có thể nghĩ rằng, khi bạn chứa một giá trị vào trong một biến trong data section của một DLL, thì giá trị sẽ được chia sẽ giữa tất cả các processes mà chúng load DLL vào trong process address space của chúng. Điều đó ko đơn giản như thế. Trong một hòan cảnh thông thường, ta cứ tưởng là như vậy khi nó tạo ra một ảo giác mỗi process có chính bản copy DLL cho chính nó. Nhưng ko như thế khi liên quan đến Windows hook. Chúng ta muốn DLL giống hệt trong tất cả các processes, bao gồm cả data. Để giải quyết vấn đề này: bạn phải đánh dấu data section là shared. Bạn có thể làm điều này bởi chỉ định thuộc tính section(s) trong linker switch (bật tắt chế độ linker). Đối với MASM, bạn cần dùng switch này:

/SECTION:

, S

Tên của data section được khởi trị đầu (initialized data) là .data và tên của data section ko cho giá trị đầu (uninitialized data) là .bss. Ví dụ nếu bạn muốn biên dịch một DLL chứa một hook procedure và bạn muốn uninitialized data section được chia sẽ giữa các processes, bạn phải dùng dòng biên dịch như sau:

link /section:.bss,S /DLL /SUBSYSTEM:WINDOWS ..........

S thuộc tính đánh dấu section là shared (chia sẽ) .

Ví dụ :

Có hai modules: một là main program sẽ làm phần GUI và một là DLL sẽ install/uninstall hook.

clip_image001

;--------------------------------------------- This is the source code of the main program ----------------

.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include mousehook.inc
includelib mousehook.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

wsprintfA proto C :DWORD,:DWORD,:VARARG
wsprintf TEXTEQU

.const
IDD_MAINDLG equ 101
IDC_CLASSNAME equ 1000
IDC_HANDLE equ 1001
IDC_WNDPROC equ 1002
IDC_HOOK equ 1004
IDC_EXIT equ 1005
WM_MOUSEHOOK equ WM_USER+6

DlgFunc PROTO :DWORD,:DWORD,:DWORD,:DWORD

.data
HookFlag dd FALSE
HookText db "&Hook",0
UnhookText db "&Unhook",0
template db "%lx",0

.data?
hInstance dd ?
hHook dd ?
.code
start:
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke DialogBoxParam,hInstance,IDD_MAINDLG,NULL,addr DlgFunc,NULL
invoke ExitProcess,NULL

DlgFunc proc hDlg:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
LOCAL hLib:DWORD
LOCAL buffer[128]:byte
LOCAL buffer1[128]:byte
LOCAL rect:RECT
.if uMsg==WM_CLOSE
.if HookFlag==TRUE
invoke UninstallHook
.endif
invoke EndDialog,hDlg,NULL
.elseif uMsg==WM_INITDIALOG
invoke GetWindowRect,hDlg,addr rect
invoke SetWindowPos, hDlg, HWND_TOPMOST, rect.left, rect.top, rect.right, rect.bottom, SWP_SHOWWINDOW
.elseif uMsg==WM_MOUSEHOOK
invoke GetDlgItemText,hDlg,IDC_HANDLE,addr buffer1,128
invoke wsprintf,addr buffer,addr template,wParam
invoke lstrcmpi,addr buffer,addr buffer1
.if eax!=0
invoke SetDlgItemText,hDlg,IDC_HANDLE,addr buffer
.endif
invoke GetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer1,128
invoke GetClassName,wParam,addr buffer,128
invoke lstrcmpi,addr buffer,addr buffer1
.if eax!=0
invoke SetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer
.endif
invoke GetDlgItemText,hDlg,IDC_WNDPROC,addr buffer1,128
invoke GetClassLong,wParam,GCL_WNDPROC
invoke wsprintf,addr buffer,addr template,eax
invoke lstrcmpi,addr buffer,addr buffer1
.if eax!=0
invoke SetDlgItemText,hDlg,IDC_WNDPROC,addr buffer
.endif
.elseif uMsg==WM_COMMAND
.if lParam!=0
mov eax,wParam
mov edx,eax
shr edx,16
.if dx==BN_CLICKED
.if ax==IDC_EXIT
invoke SendMessage,hDlg,WM_CLOSE,0,0
.else
.if HookFlag==FALSE
invoke InstallHook,hDlg
.if eax!=NULL
mov HookFlag,TRUE
invoke SetDlgItemText,hDlg,IDC_HOOK,addr UnhookText
.endif
.else
invoke UninstallHook
invoke SetDlgItemText,hDlg,IDC_HOOK,addr HookText
mov HookFlag,FALSE
invoke SetDlgItemText,hDlg,IDC_CLASSNAME,NULL
invoke SetDlgItemText,hDlg,IDC_HANDLE,NULL
invoke SetDlgItemText,hDlg,IDC_WNDPROC,NULL
.endif
.endif
.endif
.endif
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
DlgFunc endp

end start

;----------------------------------------------------- This is the source code of the DLL --------------------

.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib

.const
WM_MOUSEHOOK equ WM_USER+6

.data
hInstance dd 0

.data?
hHook dd ?
hWnd dd ?

.code
DllEntry proc hInst:HINSTANCE, reason:DWORD, reserved1:DWORD
.if reason==DLL_PROCESS_ATTACH
push hInst
pop hInstance
.endif
mov eax,TRUE
ret
DllEntry Endp

MouseProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD
invoke CallNextHookEx,hHook,nCode,wParam,lParam
mov edx,lParam
assume edx:PTR MOUSEHOOKSTRUCT
invoke WindowFromPoint,[edx].pt.x,[edx].pt.y
invoke PostMessage,hWnd,WM_MOUSEHOOK,eax,0
assume edx:nothing
xor eax,eax
ret
MouseProc endp

InstallHook proc hwnd:DWORD
push hwnd
pop hWnd
invoke SetWindowsHookEx,WH_MOUSE,addr MouseProc,hInstance,NULL
mov hHook,eax
ret
InstallHook endp

UninstallHook proc
invoke UnhookWindowsHookEx,hHook
ret
UninstallHook endp

End DllEntry

;---------------------------------------------- This is the makefile of the DLL ------------------------------

NAME=mousehook
$(NAME).dll: $(NAME).obj
Link /SECTION:.bss,S /DLL /DEF:$(NAME).def /SUBSYSTEM:WINDOWS /LIBPATH:c:\masm\lib $(NAME).obj
$(NAME).obj: $(NAME).asm
ml /c /coff /Cp $(NAME).asm

Phân tích:

Ví dụ này sẽ hiển thị một dialog box với 3 edit controls mà chúng sẽ được lắp giá trị là class name, window handle và address của thủ tục window (window procedure) của cửa sổ window mà con trỏ chuột đang nằm trên đó. Có hai buttons, Hook và Exit. Khi bạn nhấn Hook button, chương trình hook mouse input và text trên button thay đổi là Unhook. Khi bạn move (di chuyển) con trỏ chuột (mouse cursor) trên một window, thông tin về window sẽ được hiển thị trong cửa sổ chính của chương trình ví dụ. Khi bạn nhấn Unhook button, chương trình rời bỏ mouse hook.

Chương trình chính dùng một dialog box như cửa sổ chính của nó. Nó định nghĩa một custom message là WM_MOUSEHOOK, thông điệp này sẽ được sử dụng trong main program và thư viện hook DLL. Khi main program nhận thông điệp này, wParam chứa handle của window mà mouse cursor đang trên đó. Tất nhiên , đây là một sự sắp đặt tùy ý (do tôi quy định như thế). Tôi quyết định gởi handle trong tham số wParam của thông điệp là vì đơn giản hóa công việc. Bạn có thể chọn phương pháp truyền tham số thông điệp khác của chính bạn để truyền thông giữa main program và hook DLL.

.if HookFlag==FALSE
invoke InstallHook,hDlg
.if eax!=NULL
mov HookFlag,TRUE
invoke SetDlgItemText,hDlg,IDC_HOOK,addr UnhookText
.endif

Chương trình duy trì một cờ flag (cờ), là HookFlag, để biểu thị trạng thái của hook. Nó là FALSE nếu hook ko được install và là TRUE nếu hook được install .

Khi người sử dụng nhấn Hook button, chương trình kiểm tra xem hook đã cài đặt chưa. Nếu nó chưa cài đặt, nó gọi InstallHook function trong thư viện hook DLL để install nó. Chú ý rằng chúng ta truyền handle của main dialog như một tham số của hàm InstallHook, vì vậy sau này hook DLL mới có thể gởi thông d9ie5p WM_MOUSEHOOK messages đến đúng window yêu cầu, nghĩa là chính window chương trình của bạn.

Khi chương trình được load, hook DLL cũng được loaded. Thực tế , các DLLs được load ngay lập tức sau khi program ở trong memory. Hàm entrypoint của DLL được gọi trước chỉ thị đầu tiên trong main program thực thi. Vì vậy khi main program thực thi các DLL(s) đã được khởi trị đầu tiên. Chúng ta đặt code sau đây trong DLL entrypoint function của hook DLL:

.if reason==DLL_PROCESS_ATTACH
push hInst
pop hInstance
.endif

Code này chỉ lưu handle instance của hook DLL vào một biến tòan cục có tên là hInstance dành để sử dụng trong hàm InstallHook function. Do DLL entrypoint function được gọi trước các hàm khác trong DLL, nên hInstance luôn luôn hợp lệ. Chúng ta đặt hInstance trong .data section để giá trị này được lưu giữ cho mỗi process cơ sở (per-process basis ). Từ đó, khi mouse cursor bay lượn trên một window, hook DLL được mapped vào trong process. Imagine mà ở đó thật sự DLL chiếm cứ vùng addr dự kiến load hook DLL vào, hook DLL sẽ được ánh xạ lại (remapped ) imagine đó vào một address khác. Giá trị của hInstance sẽ được updated đến vùng addr load vào mới ( new load address) của chúng. Khi user nhấn Unhook button và rồi Hook button, SetWindowsHookEx sẽ được gọi một lần nữa . Tuy nhiên, lần này, nó sẽ sử dụng địa chỉ load mới (new load address) như một instance handle mà chúng sẽ bị sai do ở trong process của chương trình ví dụ, vì vậy hook DLL's load address phải được thay đổi. Hook bây giờ sẽ là một hook local mà ở đó bạn chỉ có thể hook mouse events xảy ra trong chính window của bạn. Mong ước khó khăn nhỉ.

InstallHook proc hwnd:DWORD
push hwnd
pop hWnd
invoke SetWindowsHookEx,WH_MOUSE,addr MouseProc,hInstance,NULL
mov hHook,eax
ret
InstallHook endp

InstallHook function của nó rất đơn giản. Nó lưu window handle truyền như một parameter của nó đến một biến toàn cục global variable có tên là hWnd dành để sử dụng sau này. Rồi nó gọi hàm SetWindowsHookEx để install một mouse hook. Return value của SetWindowsHookEx được chứa trong một biến toàn cục global variable có tên là hHook dùng để sử dụng cho hàm UnhookWindowsHookEx.
Sau khi SetWindowsHookEx được gọi , mouse hook họat động. Bất cứ khi nào một mouse event xảy ra trong hệ thống, MouseProc (hook procedure của bạn ) được gọi ngay.

MouseProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD
invoke CallNextHookEx,hHook,nCode,wParam,lParam
mov edx,lParam
assume edx:PTR MOUSEHOOKSTRUCT
invoke WindowFromPoint,[edx].pt.x,[edx].pt.y
invoke PostMessage,hWnd,WM_MOUSEHOOK,eax,0
assume edx:nothing
xor eax,eax
ret
MouseProc endp

Điều đầu tiên nó làm là gọi hàm CallNextHookEx để cho các hooks khác thay đổi đến tiến trình thực hiện biến cố chuột (mouse event). Sau đó, nó gọi hàm WindowFromPoint function để nhận lại handle của window tại tọa độ màn hình chỉ định. Chú ý rằng chúng ta dùng cấu trúc POINT structure trong cấu trúc MOUSEHOOKSTRUCT structure trỏ đến bởi lParam như là tọa độ chuột hiện hành (current mouse coordinate). Sau đó chúng ta gởi window handle đến main program qua đường PostMessage với WM_MOUSEHOOK message. Một điều bạn nên nhớ: Bạn sẽ ko dùng SendMessage bên trong hook procedure, nó có thể gây ra đình chỉ thông điệp. PostMessage đã được gợi dùng ở đây. Cấu trúc MOUSEHOOKSTRUCT structure được định nghĩa dưới đây:

MOUSEHOOKSTRUCT STRUCT DWORD
pt POINT <>
hwnd DWORD ?
wHitTestCode DWORD ?
dwExtraInfo DWORD ?
MOUSEHOOKSTRUCT ENDS

  • pt là tọa độ màn hình hiện hành của mouse cursor
  • hwnd là handle của window sẽ được nhận thông điệp mouse message. Nó luôn là window dưới mouse cursor nhưng ko luôn như thế. Nếu một window gọi SetCapture, mouse input sẽ được chuyển hướng đến window đó. Bởi lý do đó, tôi ko dùng thành phần hwnd của cấu trúc này nhưng chọn dùng gọi hàm WindowFromPoint là như vậy .
  • wHitTestCode chỉ định giá trị hit-test value. Hit-test value cho nhiều thông tin về vị trí mouse cursor hiện hành. Nó chỉ định phần nào của window mà mouse cursor ở trên đó. Dể hòan tòan nắm rõ , hảy tra cứu win32 api reference với thông điệp WM_NCHITTEST message.
  • dwExtraInfo chứa thông tin mở rộng kết hợp message. Thông thường giá trị này được set bởi gọi mouse_event và nhận lại bằng cách gọi hàm GetMessageExtraInfo.

Khi main window nhận thông điệp WM_MOUSEHOOK message, nó sử dụng window handle trong wParam (do ta PostMessage với wParam=handle window dưới con chuột) để nhận lại thông tin về window.

.elseif uMsg==WM_MOUSEHOOK
invoke GetDlgItemText,hDlg,IDC_HANDLE,addr buffer1,128
invoke wsprintf,addr buffer,addr template,wParam
invoke lstrcmpi,addr buffer,addr buffer1
.if eax!=0
invoke SetDlgItemText,hDlg,IDC_HANDLE,addr buffer
.endif
invoke GetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer1,128
invoke GetClassName,wParam,addr buffer,128
invoke lstrcmpi,addr buffer,addr buffer1
.if eax!=0
invoke SetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer
.endif
invoke GetDlgItemText,hDlg,IDC_WNDPROC,addr buffer1,128
invoke GetClassLong,wParam,GCL_WNDPROC
invoke wsprintf,addr buffer,addr template,eax
invoke lstrcmpi,addr buffer,addr buffer1
.if eax!=0
invoke SetDlgItemText,hDlg,IDC_WNDPROC,addr buffer
.endif

Để tránh qua loa , chúng ta check text có sẳn trong edit controls và text chúng ta đặt vào trong chúng xem có giống nhau ko. Nếu chúng giống nhau, chúng ta qua mục khác. Vậy chúng ta nhận handle trong edit control chính là wParam.
Chúng ta nhận lại class name bằng cách gọi hàm GetClassName, nhận address của window procedure bằng cách gọi hàm GetClassLong với GCL_WNDPROC và rồi định dạng chúng vào trong strings và đặt chúng vào trong edit controls thích hợp.

invoke UninstallHook
invoke SetDlgItemText,hDlg,IDC_HOOK,addr HookText
mov HookFlag,FALSE
invoke SetDlgItemText,hDlg,IDC_CLASSNAME,NULL
invoke SetDlgItemText,hDlg,IDC_HANDLE,NULL
invoke SetDlgItemText,hDlg,IDC_WNDPROC,NULL

Khi user nhấn Unhook button, chương trình gọi hàm UninstallHook function trong hook DLL. UninstallHook chỉ gọi hàm UnhookWindowsHookEx. Sau đó, nó thay đổi text của button trở lại thành "Hook", HookFlag là FALSE và xóa nội dung của edit controls.

Chú ý linker switch trong the makefile.

Link /SECTION:.bss,S /DLL /DEF:$(NAME).def /SUBSYSTEM:WINDOWS

Nó chỉ định .bss section như một shared section để làm việc tất cả các processes chia sẽ section data ko định trị lúc đầu của hook DLL. Ngòai switch này , bạn hook DLL sẽ ko thực hiện đúng được .


[Iczelion's Win32 Assembly Homepage]

18/12/2005. Fixed 2010

Benina

[Programming] Về Memory leaks trong MFC/C++ trên Windows

Những ai lập trình C có kinh nghiệm chắc hẳn đều thừa nhận rằng việc C/C++ giao hẳn trách nhiệm quản lí bộ nhớ cho người lập trình thực sự là một ưu điểm mạnh mẽ, và cũng chính là khuyết điểm lớn của ngôn ngữ này. Với tư tưởng đó, lập trình viên C++ luôn phải làm một công việc khó chịu và mất nhiều thời gian là phát hiện và loại trừ memory leaks trong chương trình. Entry này không trình bày cặn kẽ và chi tiết về memory leaks, mà chỉ là ghi lại một số kinh nghiệm mà người viết rút ra sau một ngày vì chán coding quá nên chuyển sang leaks detection. Hi vọng bài viết sẽ có ích với những người (và chỉ những người) đã có kinh nghiệm làm việc với C++, đã có những hiểu biết cơ bản về memory leaks như: tại sao lại có memory leaks, tại sao phải tránh memory leaks v.v...

Entry được tổ chức thành 3 phần. Phần thứ nhất nói về các kĩ thuật phát hiện memory leaks với Microsoft Visual C++ (MSVC). Phần 2 nói về một số kinh nghiệm tránh memory leaks khi làm việc với C++, MFC, GDI, GDI+. Trong phần này sẽ có một số vấn đề là cụ thể trên MFC/GDI, nhưng cũng có một số phần có thể áp dụng cho C/C++ nói chung. Phần 3 sẽ là một số địa chỉ đến các bài viết và công cụ hỗ trợ.

1. Phát hiện memory leaks với MSVC:

Trình biên dịch C++ của Microsoft đi kèm với thư viện C Runtime Library (CRT). Thư viện này cung cấp một số hàm và macro rất tốt cho việc phát hiện memory leaks. Ta sẽ điểm qua những công cụ cơ bản của CRT và cả MFC.

1.1. macro DEBUG_NEW (chỉ có trong MFC):

Macro này định nghĩa sẵn trong file afx.h, lập trình viên có thể dùng nó để thay thế cho từ khóa new. Trong chế độ Debug, macro này sẽ lưu vết (tên file, số dòng) của tất cả các vùng nhớ đã tạo. Trong chế độ Release, DEBUG_NEW được định nghĩa thành toán tử new bình thường, như vậy sẽ không ảnh hưởng tới hiệu năng của chương trình khi build ở chế độ Release.

Với macro này, nếu build ở chế độ Debug, sau khi chạy xong chương trình, nếu có memory leaks xảy ra, thông tin về những đối tượng này sẽ được xuất ra cửa sổ Output, ví dụ:
{1707} normal block at 0x026122C8, 12 bytes long.
Data: <+ > 2B 00 00 00 02 00 00 00 C0 0A 02 03

Dòng thông tin trên cho biết có một vùng nhớ ở vị trí 0x026122C8, dài 12 bytes, được cấp phát ở lượt 1707, bị leaks. (Cách đọc log xin xem phần 1.4).

Để sử dụng macro này, thay vì phải Find-and-Replace tất cả các từ khóa new thành DEBUG_NEW, ta có thể define new thành DEBUG_NEW như sau (đặt ở đầu các file cpp):
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

Mặc định một số lớp do MFC tạo ra cũng được đặt sẵn các dòng define này.

1.2. Biến _crtBreakAlloc:

...

1.3. Snapshot bộ nhớ và dump đối tượng với lớp CMemoryState:

...

1.4. Cách đọc log khi dump object:

...

Trên đây là những mặt hỗ trợ chính của MSVC trong phát hiện memory leaks. Hiện giờ đang... lười nên chưa thể viết đầy đủ, người đọc quan tâm có thể xem trong [1].

2. Một số kĩ thuật để giảm memory leaks.

Điều quan trọng cần nhớ là lập trình viên phải đề phòng memory leaks ngay trong thời gian phát triển, vì mặc dù đã có nhiều công cụ hỗ trợ phát hiện leaks, nhưng thời gian và chi phí để phát hiện/loại trừ leaks sẽ tăng theo dung lượng và "mức độ rối rắm" của mã nguồn. Sau đây là một số kinh nghiệm mà người viết đúc kết được.

2.1. Kế thừa và các lớp tập hợp trong MFC:

Trong MFC hỗ trợ một số lớp tập hợp phục vụ cho mục đích tổng quát như CArray, CPtrArray... Ta phải hết sức thận trọng khi đưa con trỏ cácl ớp con vào các tập hợp này. Sau đây là một ví dụ:

class CParent
{
public:
~CParent(){TRACE("In Parent's destructor\r\n");}
};
class CChild : public CParent
{
public:
~CChild()
{
TRACE(L"In child's destructor.\r\n");
}
};

// cause mem. leaks!!!
CArray a;
a.Add(new CChild());
delete a.GetAt(0);


Khi gọi delete a.GetAt(0); trình biên dịch chỉ biết mọi phần tử của a đều là kiểu CParent*, do đó nó sẽ gọi trực tiếp destructor của lớp cha, bất kể con trỏ đang nằm tại vị trí 0 trong a có kiểu là CChild. Nếu như CChild có dữ liệu cần delete của riêng nó thì rõ ràng đoạn mã này sẽ gây memory leak.

Trước khi đọc tiếp, hãy thử nghĩ cách để hạn chế trường hợp này.

Cách giải quyết hợp lý nhất cho trường hợp này mà vẫn đảm bảo tính đa hình là: sửa lại khai báo destructor của lớp CParent thành virtual:
class CParent
{
public:
virtual ~CParent(){TRACE("In Parent's destructor\r\n");}
};

Sau khi sửa thành virtual, câu lệnh delete a.GetAt(0); sẽ thực thi destructor của CChild trước, rồi sau đó đến CParent, như đúng mong đợi.

Tuy nhiên hãy xem trường hợp sau, thay vì dùng CArray, ta dùng CPtrArray:
CPtrArray a;
a.Add(new CChild());
delete a.GetAt(0);

Kết quả sẽ còn tệ hại hơn, vì khi đó câu lệnh delete a.GetAt(0); không hề gọi destructor của CParent lẫn CChild. Lí do là CPtrArray không phải là lớp template, nó chỉ là danh sách các biến kiểu void*, do đó trình biên dịch không hề có thông tin gì về con trỏ đang nằm ở vị trí 0 trong mảng.
Cách duy nhất khả dĩ trong trường hợp này có lẽ là phải ép kiểu:
delete (CChild*)a.GetAt(0);
Rõ ràng đây là cách rất tệ, vì ta đang muốn tổng quát hóa các phần từ trong mảng a, nhưng đến khi delete lại phải biết thông tin cụ thể về các phần tử trong đó (thì mới ép kiểu rồi delete được).

Kinh nghiệm rút ra là:
  • Khi tạo lớp cha, luôn luôn đặt destructor là virtual.
  • Hạn chế dùng các lớp tập hợp dùng kiểu void* như CPtrArray, CPtrList, CMapWordToPtr... mà nên chuyển sang các lớp template như CTypedPtrList, CTypedPtrMap... vì các lớp này "type-safe" hơn. (Xem thêm về các lớp tập hợp trong MFC ở [2]). Hoặc thậm chí nên dùng STL để không phụ thuộc vào MFC.

2.2. GDI/GDI+:

Có một điều khó chịu là GDI và GDI+ không chịu làm việc với macro DEBUG_NEW đã nói ở phần 1.1. Lý do là bên trong GDI+ dùng các hàm GdipAlloc/GdipFree để cấp phát/giải phóng bộ nhớ. GDI thì còn phiền phức hơn, vì cách khởi tạo và xóa đối tượng tùy thuộc vào đối tượng ta đang thao tác (Ví dụ với HBITMAP thì dùng CreateBitmap/CreateCompatibleBitmap/DeleteObject... Với HMENU dùng CreateMenu/CreatePopupMenu/DestroyMenu... Với HICON dùng CreateIcon/DestroyIcon... Với HDC dùng CreateDC/GetDC/DeleteDC/ReleaseDC...) Chính vì vậy hình như có người viết hẳn một quyển (hay chương) sách chỉ nói về làm thế nào để lập trình GDI/GDI+ mà không bị leak.

Thực ra cá nhân người viết nhận thấy làm việc với GDI/GDI+ hoàn toàn không khó, nó chỉ yêu cầu sự tỉ mỉ và kiên trì. Ở đây liệt kê một số gợi ý:
  • Với GDI+, hạn chế dùng con trỏ ở mức tối đa. Ví dụ trong hàm OnPaint, khi cần vẽ bằng một SolidBrush thì đơn giản nên viết:
    SolidBrush br(...)
    chứ đừng nên màu mè:
    Brush* br = new SolidBrush(...)
    //...
    Như thế sẽ đỡ phải delete, và các đối tượng vẽ được tự giải phóng khi ra khỏi phạm vi của nó.
  • Với GDI, cần nhớ rõ khi tạo bằng hàm nào thì phải xóa bằng hàm tương ứng. Ví dụ tạo HDC bằng GetDC thì phải xóa bằng ReleaseDC, tạo bằng CreateDC/CreateCompatibleDC thì xóa bằng DeleteDC... Do các đối tượng trong GDI hầu hết đều là handle (như HPEN, HBRUSH, HBITMAP, HICON .v.v..) nên đã tạo thì buộc phải delete.
  • Các kĩ thuật tối ưu khi vẽ như double-buffer, cached... khi sử dụng phải lưu ý delete đúng lúc.
  • Ngoài ra có một số lập trình viên viết sẵn các lớp/hàm/macro hỗ trợ, điển hình là [3]

2.3. Làm việc với chuỗi


2.4. Các hàm thao tác trực tiếp lên bộ nhớ (memcpy, memmove...)


<Đuối wá, sẽ viết khi có thời gian ^^>

3. Tham khảo

[1] http://msdn.microsoft.com/en-us/library/c99kz476%28VS.80%29.aspx
[2] http://msdn.microsoft.com/en-us/library/942860sh%28VS.80%29.aspx
[3] http://www.codeproject.com/KB/GDI-plus/gdiplush.aspx

Có khá nhiều công cụ chuyên nghiệp và mạnh mẽ hỗ trợ performance profiling/memory tracing trong C++ như: AQTime ($518), Compuware, Deleaker. Tất cả đều là phần mềm thương mại. Cá nhân người viết từng dùng qua Deleaker, và kết quả khá hài lòng.

Lâu rồi mới viết một bài "ra hồn" ở đây. Hị vọng sau này sẽ khá hơn ^^

relink: http://phvu007.spaces.live.com/blog/cns!75F5081AE5375F06!283.entry

3/21/10

[MASM] Tổng hợp các lệnh nhảy cơ bản trong ASM.

Dưới đây là các lệnh nhảy cơ bản trong ASM em tổng hợp được, dành cho các bạn mới học ASM (như em chẳng hạn).

1. JB / JNAE
- Ý nghĩa : Nhảy nếu nhỏ hơn.
- Điều kiện : CF = 1
- Ghi chú : Áp dụng cho số không dấu.


2. JBE / JNA
- Ý nghĩa : Nhảy nếu nhỏ hơn hoặc bằng.
- Điều kiện : CF = 1 or ZF = 1
- Ghi chú : Áp dụng cho số không dấu.


3. JA / JNBE
- Ý nghĩa : Nhảy nếu lớn hơn.
- Điều kiện : CF = 0 and ZF = 0
- Ghi chú : Áp dụng cho số không dấu.


4. JAE / JNB
- Ý nghĩa : Nhảy nếu lớn hơn hoặc bằng.
- Điều kiện : CF = 0
- Ghi chú : Áp dụng cho số không dấu.


5. JE / JZ
- Ý nghĩa : Nhảy nếu bằng.
- Điều kiện : ZF = 1

6. JNE / JNZ
- Ý nghĩa : Nhảy nếu không bằng.
- Điều kiện : ZF = 0

7. JL / JNGE
- Ý nghĩa : Nhảy nếu nhỏ hơn.
- Điều kiện : SF != OF
- Ghi chú : Áp dụng cho số có dấu.


8. JLE / JNG
- Ý nghĩa : Nhảy nếu nhỏ hơn hoặc bằng.
- Điều kiện : SF != OF or ZF = 1
- Ghi chú : Áp dụng cho số có dấu.


9. JG / JNLE
- Ý nghĩa : Nhảy nếu lớn hơn.
- Điều kiện : SF = OF and ZF = 0
- Ghi chú : Áp dụng cho số có dấu.


10. JGE / JNL
- Ý nghĩa : Nhảy nếu lớn hơn hoặc bằng.
- Điều kiện : SF = OF
- Ghi chú : Áp dụng cho số có dấu.


11. JP / JPE
- Ý nghĩa : Nhảy nếu cờ chẵn lẽ được bật.
- Điều kiện : PF = 1

12. JNP / JPO
- Ý nghĩa : Nhảy nếu không được bật.
- Điều kiện : PF = 0

13. JS
- Ý nghĩa : Nhảy nếu cờ dấu được bật.
- Điều kiện : SF = 1

14. JNS
- Ý nghĩa : Nhảy nếu cờ dấu không được bật.
- Điều kiện : SF = 0

15. JO
- Ý nghĩa : Nhảy nếu cờ tràn được bật.
- Điều kiện : OF = 1

16. JNO
- Ý nghĩa : Nhảy nếu cờ tràn không được bật.
- Điều kiện : OF = 0

17. JC
- Ý nghĩa : Nhảy nếu cờ nhớ được bật.
- Điều kiện : CF = 1

18. JNC
- Ý nghĩa : Nhảy nếu cờ nhớ không được bật.
- Điều kiện : CF = 0

19. JCXZ
- Ý nghĩa : Nhảy nếu CX = 0
- Điều kiện : CX = 0 (nếu ECX != 0 vẫn nhảy, lệnh nhảy này không phụ thuộc vào các cờ)


20. JMP
- Nhảy vô điều kiện.

21. JECXZ
- Ý nghĩa : Nhảy nếu ECX = 0
- Điều kiện : ECX = 0 (lệnh nhảy này không phụ thuộc vào các cờ)

Reflink: http://reaonline.net/showthread.php?8618-T%E1%BB%95ng-h%E1%BB%A3p-c%C3%A1c-l%E1%BB%87nh-nh%E1%BA%A3y-c%C6%A1-b%E1%BA%A3n-trong-ASM

[Crypto] Mã hoá thông tin bằng thuật giải Blowfish

===========================================================================
Ặc ặc, keke.
(Đó là những từ cảm thán mà tui học được trên NET. Tui yêu NET và con người ảo của NET ở chổ này . hihi. )
Gác kiếm đã lâu đang ngâm trầm cái khác, híhí , bài này cũng đã viết lâu rồi (từ lúc mới wen NINI trên NET có send cho ẻm đọan vào đề :) ). Hôm nay lục ra ,thấy cũng giống như một bài luận nên post cho các bạn xem chơi. Đọan analysis thuật tóan và cracking cắt bỏ. Mong các bạn thông cảm vì em đã ....lỡ dại ...gác rồi

==========================================================================

VÀO ĐỀ:

Lúc bé, tui thường ở nhà với bà nội , vì mẹ tôi phải đi dạy học xa. Và tui là một trong những đứa cháu nội được yêu thương nhất . Bà nội tui lúc nào cũng muốn những điều tốt lành cho tôi. Bà thường mong muốn: Nếu lớn lên tôi sẽ được làm nghề “gỏ dây thép”, vì nghề này chỉ ngồi ở trong mát lại lương cao.Và ngày hôm nay khi nghiên cứu về mã hóa bổng tôi rất nhớ đến bà. TUT này ngòai việc chia sẽ cho các bạn, tui cũng muốn gởi tặng linh hồn Nội tôi. Rất rất nhớ bà !!!.huhu

Thưa các bạn, thật ra viết tut ko cần dài dòng như benina hay làm. Nhưng vì đối tượng đọc tut mà benina muốn share là các newbie như benina vậy. Thật là vất vả cho newbie khi trên các diễn đàn cracker tiếng Việt chưa nhiều tài liệu hướng dẫn cụ thể. Và thường , các newbie ko biết bắt đầu từ đâu . Vì vậy bắt buộc benina biết gì thì sẽ share ngay cho các bạn. Thật là vui vẽ khi chúng ta cùng tiến bộ.

Như trên tui có nói, bà nội tui rất thích nghề “gỏ dây thép”. Vì những năm 60, kỹ thuật còn lạc hậu so với bây giờ, nghề “gỏ dây thép” rất sang trọng, là một nghề “ngồi mát ăn bát vàng”. Nghề này chỉ có làm một việc là nhận 1 message text (như thư từ chẳng hạn), sau đó chuyển thông tin text đó thành 1 chuổi mật mã 0,1 theo một quy luật gọi là khóa “Key” và rồi “gỏ” tín hiệu 0,1 đó bằng 1 công cụ đặc biệt để chuyển đi bằng sóng điện từ (nên được gọi là gỏ dây thép). Người nhận tín hiệu bên kia ghi lại chuổi mã hóa 0,1 và dựa vào khóa “key” qui định mà chuyển ra tín hiệu text. Đấy là qui trình chuyển 1 tín hiệu mã hóa. Ví dụ này để các bạn dể hình dung ra cách mã hóa tin hiệu text như thế nào và nó cũng là một ví dụ thật “thô sơ” cho thuật tóan mã hóa Blowfish.


GIỚI THIỆU VÀ THUẬT NGỮ


Trong chu trình mã hóa, plaintext là tín hiệu mà bạn chuyển đi. Tín hiệu này có thể là một bản báo cáo kiểm tra sức khỏe, một bức thư tình…. Hay một cái gì đó mà nó có thể được diễn tả ra thành một luồng bits. Quá trình mã hóa là biến tín hiệu plaintext thành ciphertext (văn bản đã mã hóa), và quá trình giải mã thì ngược lại chuyển ciphertext thành plaintext
Thông thường người ta hay nói rằng, thuật tóan mã hóa có 2 lọai , symmetric (đối xứng) và public (công khai). Thuật tóan đối xứng như Blowfish chẳng hạn , dùng khóa bí mật giống nhau (same secret key) để mã hóa và giải mã. Bạn phải giấu key một cách bí mật chỉ cho người nhận và gởi tín hiệu biết mà thôi. Ví dụ như chúng ta thường sử dụng một password trong IT,thì chỉ có người nhận và người gởi biết mà thôi.

Thuật tóan mã hóa khóa công khai (public) là dùng 2 key, một cho mã hóa, khóa kia cho giải mã. Khóa được dùng cho mã hóa gọi là “public key” (khóa công khai) không cần giữ bí mật. Người gởi tín hiệu dùng khóa này để gởi tín hiệu của họ. và người nhận tín hiệu dùng khóa giải mã bí mật hay còn gọi là “private key” (khóa riêng) để đọc tín hiệu nhận được. Khóa public key nó “locks” tín hiệu, còn khóa private key thì “unclocks” tín hiệu: một tín hiệu được mã hóa bằng public key, thì ko ai ngọai trừ người giữ khóa private key có thể giải mã được tín hiệu. RSA là một thuật tóan mã hóa public key thông dụng.

Ở đây tui xin lấy một ví dụ để mô tả kiểu mã hóa key public và symmetric như sau (ko biết đúng ko nhe nhưng dễ hình dung,hihi). Nếu các bạn nào có đi du lịch ở khách sạn, hay gia đình khá giả chắc các bạn sẽ biết và đã từng xem truyền hình cáp. Thực ra nó cũng giống như lọai truyền hình bình thường, không phải nối cáp từ đài phát hình đến truyền hình của ta như là cái tên của nó dễ làm ta nhằm lẩn. Nó chỉ khác ở chổ, truyền hình bình thường, các bạn cứ tường tượng tín hiệu của nó là lọai tín hiệu được mã hóa đối xứng, tức là tín hiệu hình ảnh được mã hóa bằng một chuổi tín hiệu được truyền qua sóng điện từ đến truyền hình của ta. Lúc đó truyền hình sẽ giải mã chuổi tín hiệu đó thành hình ảnh bằng 1 khóa giống nhau (same secret key) khi mã hóa và giải mã. Đây là lọai mã hóa đối xứng symmetric . Còn truyền hình cáp thì khác, đài phát hình sẽ mã hóa hình ảnh bằng một khóa “public key” mà tất cả các ăn-ten truyền hình nào cũng nhận được. Nhưng bạn phải có một đầu giải mả tín hiệu (hình dáng giống như một cái đầu máy DVD), có nhiệm vụ giải mã tín hiệu truyền hình đã mã hóa thành tín hiệu hình ảnh thông qua một khóa riêng “private key” kết hợp với khóa “public key” để giải mả. Đó chính là lọai thuật tóan mã hóa công khai.

Vậy muốn giải mã một tín hiệu mã hóa bằng thuật tóan symmetric ta phải biết khóa “same secret key” . Và muốn giải mã một tín hiệu mã hóa bằng thuật tóan public key chúng ta bắt buộc phải biết khóa “private key”

Ngòai ví dụ trên, các thuật tóan mã hóa còn được sử dụng rất nhiều trong thực tế như : tín hiệu điện thọai di động, hay một vi chương trình cập nhật nâng cấp mà chúng ta thường thấy ,nó sẽ gởi 1 tín hiệu về trang chủ của soft để báo có cần nâng cấp soft ko khi có 1 bản upgrade mới,hoặc được sử dụng trong Embedded systems, vân..vân...nhiều lắm.

Phần lớn thuật tóan đáng tin cậy nhất đã được xuất bản free để cho phân tích (analysis) thỏai mái, bởi vì khóa bảo mật của chúng được sử dụng trong thực tế kiểm chứng rất an tòan. Một thuật tóan tốt nó giống như “ một cái hầm chứa tiền của nhà băng ”: Cái hầm này được làm rất hòan chỉnh , nó là lọai tốt nhất , bạn ko thể vào bên trong hầm mà ko có khóa. Blowfish chính là thuật tóan lọai này, nó rất đáng tin cậy đã được public công khai.

Đôi khi một thuật tóan mã hóa bị giới hạn ko free, có nghĩa là chính thuật tóan này được giử bí mật. Chính vì vậy bạn sẽ ko bao giờ biết chắc rằng thuật tóan bị giới hạn đó thật sự yếu kém như thế nào, bởi vì người phát triển ko cho bất cứ ai một cơ hội để phân tích nó.

Thuật tóan có thể được sử dụng bảo mật những lọai dữ liệu khác nhau. Đôi khi bạn muốn dữ liệu được “tòan vẹn” khi gởi đi, và tin chắc rằng người nhận sẽ nhận được những thông tin giống đúng như bạn đã gởi,lúc đó bạn cũng phải sử dụng đến thuật tóan mã hóa. Thuật tóan mã hóa cũng cung cấp những “xác nhận”, cho biết chắc rằng một tín hiệu đến từ một người nào đó đã gởi nó đến ....đó là một vài mục đích sử dụng của thuật tóan mã hóa .Một ứng dụng nữa của các thuật tóan mã hóa nói chung và Blowfish nói riêng là bảo mật phần mềm , ko cho các cracker chôm chỉa. Nó sẽ mã hóa chuổi password (là plaintext) thành chuổi mã hóa ciphertext. Và sao đó sẽ xử lý chuổi ciphertext này

Trên đây là những giới thiệu sơ khởi về thuật tóan mã hóa và các ứng dụng của nó trong thực tế nhằm mục đích khởi xứng cho việc ứng dụng vào cracking mà chúng ta quan tâm . Tui xin tóm tắt lại các ý chính trên để chúng ta bắt đầu đi vào phần chính của vấn đề là phân tích thuật tóan Blowfish và ứng dụng trong cracking như thế nào:

-plaintext là tín hiệu mà bạn chuyển đi
-Ciphertext: tín hiệu mã hóa
-Thuật tóan mã hóa có 2 lọai , symmetric (đối xứng) và public (công khai)
- Thuật tóan đối xứng (symmetric) như Blowfish chẳng hạn , dùng khóa bí mật giống nhau (same secret key) để mã hóa và giải mã
- Thuật tóan mã hóa khóa công khai (public) là dùng 2 key, một cho mã hóa, khóa kia cho giải mã. Khóa được dùng cho mã hóa gọi là “public key” (khóa công khai) không cần giữ bí mật.Khóa được dùng cho giải mã là “private key” (khóa riêng). RSA là một thuật tóan mã hóa public key thông dụng
- Vậy muốn giải mã một tín hiệu mã hóa bằng thuật tóan symmetric ta phải biết khóa “same secret key” . Và muốn giải mã một tín hiệu mã hóa bằng thuật tóan public key chúng ta bắt buộc phải biết khóa “private key”
-Encryption algorithm thuật tóan mã hóa ở đây khác thuật tóan hash chuổi MD5 là: Thuật tóan mã hóa sẽ mã hóa ra 1 Ciphertext và có thể giải mã ngược lại 1 ciphertext thành 1 plaintext. Còn thuật tóan MD5 thì sẽ hash chuổi thành 1 tín hiệu MD nhưng ko thể biến đổi từ 1 chuổi MD về tín hiệu gốc.

Benina 2004

[Programming] Mặc định PC Lint: phần mềm phát hiện lỗi trước khi Test

Every programmer, no matter how great he is, makes mistakes sometime or the other while coding. Although every compiler tries its best to put across every possible error during compilation,many mistakes skip the wrath of compiler. Some are seemingly very innocent and very tough to be caught even during code review, sometimes even get through the cycle of testing. The real face of these mistakes show up always on the customer side by crashing the system.

Consider the following example:

int multiply(int m, int n)
{
int result = 0;
result = m * n;
return result;
}

void func()
{
int m = 32767;
int n = 32767;
int result = 0;
result = multiply( m, n );
}


In this example, if you notice, the result always overrun the maximum value of an integer (int being of 16 bits). Now, for any compiler, this code seems to be perfect. But if you lint this code, the lint tool will definitely raise a warning about this potential bug. This bug if overlooked, can cause havoc in any system in crucial scenarios.

Similarly, there are many more example like these which can be caught while linting the code. Quite a few significant but obvious problems like buffer overrun, array index out of bounds, uninitialized variables causing junk in junk out can be caught using any of the good lint tools. This process of linting makes the code safe, secure and strong enough to withstand any kind of malicious input injections or buffer overrun attacks. Ofcourse, the complex scenarios can get skipped by some of the tools but still, it definitely is a better steo to catch the bug early. Quite a few tools are available in the market but i’ll recommend a tools can PC-Lint(Windows)/FlexLint(Linux). This tool is pretty good as it catches almost every obvious flaw which gets skipped by the developers or code reviewers eyes. It follows the guidelines given in MISRA (Motor Industry Software Reliability Assocation)standard and strictly adhers to that.

These linting tools generally have their properietary algorithms but in general, they all follow the same approach of static analysis of source code. Following are examples of some of the problems which these tools are capable of finding during the lint process.

  • Accidental assignment (= compared with ==)
  • Bad pointer arithmetic
  • Accidental booleans
  • Bad use of macros
  • Use of undefined external methods (ST20 compiler assumes int func(void))
  • Uninitialised variables
  • Unsafe array usage
  • Signed/unsigned data type mix-ups
  • Bad use of casts
  • Memory leaks (over-use of CMM and API heap). Too much reliance on dynamic memory allocation.
  • Linting your code during development is very important as it can make your code much safer. It definitely does add to the build time and it might take few extra seconds to get the final object file, but isn’t it worth the hassle if you are saved from deadly bugs?

    Link: http://www.safercode.com/blog/2009/03/23/lint-your-code-find-probable-mistakes-much-before-testing.html#more-37

    TQN: http://www.mediafire.com/?ngwzwzfgmwm
    Nó không có GUI, chỉ command line thôi. Cố add nó vào IDE của VS.
    Down về rồi tự đọc help mà dùng nhé, đừng có hỏi phải dùng làm sao. Tha cho em, em ghét hỏi vậy lắm


    Reflink: http://forums.congdongcviet.com/showthread.php?p=121620#post121620

    3/19/10

    [Hooking] Inject Code on the fly by benina

    Hi các bạn!

    Hôm nay tôi sẽ mô tả một kỹ thuật inject code vào trong 1 process đang runtime mà không sử dụng dll để inject code. Kỹ thuật này sẽ inject một thread mới vào trong một process on the fly và cho resumethread. Đặc điểm của code chạy thread mới này là do ta cấp phát vùng nhớ cho process target chứ không dùng code của một dll được load lên process.

    Thật ra phương pháp này có lâu rồi, và đã được Thomas Kruse mô tả chi tiết trong bài viết “Processless Applications - Remotethreads on Microsoft* Windows 2000, XP and 2003” rất nổi tiếng. Trong bài viết trên, tác giả coding các ví dụ mô tả bằng ngôn ngữ masm. Hôm này tôi sẽ thực hiện pp này trên VC++ 6.0 để các bạn yêu thích ngôn ngữ C++ có một template code sử dụng sau này. Đồng thời nó cũng là một bài lưu trữ cho tôi khi sau này cần nhớ lại.

    Đây là bản ver 1.1

    http://www.mediafire.com/?miz0lnjne1q

    3/16/10

    [Hacking] Overflow Stack Khai thác Lỗi Tràn Bộ đệm VC++

    Trích dẫn:
    Về cơ bản, tràn bộ nhớ đệm thường xuất phát từ một nguyên nhân duy nhất. Đó là do người dùng gửi quá nhiều dữ liệu tới một chương trình và một phần của dữ liệu này buộc phải lưu trữ ra ngoài bộ nhớ mà lập trình viên đã cấp cho chương trình đó. Quá trình tràn bộ nhớ đệm có thể gây ra nhiều vấn đề, trong đó có một vấn đề mà chúng ta cần quan tâm đó là khi bộ nhớ đệm lưu trữ dữ liệu tới một mức độ nhất định nào đó thì tin tặc có thể chạy các đoạn mã chương trình trên hệ thống.


    Trong bài viết này chúng ta sẽ tìm hiểu một tình huống tràn bộ nhớ đệm mà tin tặc có thể khai thác để chạy mã trên hệ thống. Sau đó chúng ta sẽ tìm hiểu Data Execution Preventions (DEP), một tính năng được tích hợp trong hệ điều hành Windows có chức năng ngăn tràn bộ nhớ đệm.

    Nhận biết hiện tượng tràn bộ nhớ đệm

    Để có thể hiểu rõ về hiện tượng tràn bộ nhớ đệm chúng ta phải nắm vững ngôn ngữ lập trình cao cấp như C hay C++, cũng như có một vốn kiến thức sâu về quá trình vận hành của các ngăn xếp bộ nhớ.

    Khi viết một chương trình, một trong những điều mà lập trình viên cần phải tính toán cẩn trọng đó là kích thước vùng trống của bộ đệm được phân bổ cho những hàm cụ thể. Bộ nhớ đệm là một vùng trống liền kề của bộ nhớ mà một chương trình có thể sử dụng để lưu trữ dữ liệu mà các chức năng khác có thể sử dụng. Chúng ta hãy xem xét đoan mã ví dụ sau:


    Hình 1: Hàm A C rất dễ bị tấn công khi tràn bộ nhớ đệm.

    Hàm này rất rõ ràng, bắt đầu bằng việc khai bào hai biến bufferA và bufferB có kích thước lần lượt là 50 và 16. Chương trình này hiển thị một câu hỏi tới người dùng yêu cầu nhập tên và sử dụng hàm gets đễn nhận thông tin nhập vào. Sau đó dữ liệu mà người dùng cung cấp được copy từ bufferA sang tham số bộ nhớ đệm và hàm này được hoàn thành.

    Với một chương trình đơn giản như vậy có thể hơi quá khi cho rằng nó có thể bị tác động bởi mọi cuộc tấn công. Tuy nhiên vấn đề ở đây nằm trong hàm gets. Vì hàm gets không tự kiểm tra giới hạn do đó khó có thể khẳng định rằng thông tin nhập vào bufferA không vượt quá 50 kí tự. Nếu người dùng nhập hơn 50 kí tự thì chương trình sẽ bị sập.

    Hàm strcopy sẽ copy dữ liệu trong bufferA sang bufferB. Tuy nhiên bufferB có kích thước nhỏ hơn bufferA, điều này có nghĩa là dù người dùng nhập vào ít hơn 50 kí tự nhưng vẫn có thể nhiều hơn 16 kí tự vào bufferA, và khi copy sang bufferB sẽ gây ra hiện tượng tràn bộ nhớ đệm và chương trình cũng sẽ bị sập. Chương trình nhỏ này không chỉ có một mà là hai lỗ hổng tràn bộ nhớ đệm.

    Khai thác tràn bộ nhớ

    Tiếp theo chúng ta sẽ tìm hiểu những điều kiện gây ra tràn bộ nhớ và vấn đề phát sinh từ tràn bộ nhớ? Trong những trường hợp bộ nhớ đệm bị tràn thì dữ liệu tràn ra ngoài bộ nhớ đệm được chỉ định sẽ phải lưu trữ vào một nơi khác. Dữ liệu này sẽ chảy vào các vùng nhớ lân cận, và thông thường khi đó chương trình sẽ bị lỗi vì nó không thể xử lý các dữ liệu bổ sung. Mặt khác, khi lỗi này bị một ai đó hiểu biết về ngôn ngữ Assembly (một bộ ngôn ngữ lập trình cấp thấp được sử dụng trong lập trình máy tính, vi xử lý, vi điều khiển và mạch tích hợp) và ngăn xếp bộ nhớ khai thác thì mọi chuyện còn tồi tệ hơn. Trong tình huống này, tin tặc có thể gây tràn bộ nhớ đệm theo phương pháp mà chúng có thể tạo những lệnh hệ thống riêng, rồi chuyển đổi những lệnh này thành mã byte cấp thấp sau đó gửi chúng tới chương trình này theo định dạng phù hợp thì những lệnh này sẽ được chạy.


    Hình 2: Một mẫu Shellcode của Assembly và C được viết để trả về
    một dấu nhắc C:\ của Windows.

    Lúc này đoạn mã được chạy trong ngữ cảnh ứng dụng dễ bị tấn công ban đầu của người dùng. Điều đó có nghĩa là nếu chương trình này được quản trị hệ thống chạy thì mã kết hợp cũng chạy trong ngữ cảnh của một quản trị viên hệ thống. Tùy thuộc vào kích thước của bộ nhớ đệm mà tin tặc có thể kết hợp nhiều loại mã khác nhau. Những loại mã thường được sử dụng là những loại được gọi là Shellcode. Mã này sẽ trả về một Shell (ví dụ một dấu nhắc C:\ của Windows) tới người đã chạy đoạn mã đó. Trong một ngữ cảnh phù hợp thì người chạy mã đó sẽ có toàn quyền kiểm soát với máy trạm này. Tràn bộ nhớ đệm có thể xảy ra dưới nhiều hình thức và quy mô, một người thành thạo trong việc điều khiển ngăn xếp sẽ chiếm được toàn quyền kiểm soát mọi hệ thống dễ bị tấn công.

    Data Execution Prevention

    Phương pháp đơn giản nhất để chặn khả năng khai thác lỗ hổng phát sinh do tràn bộ nhớ đệm mà các lập trình viên thường sử dụng là luôn đảm bảo mã lập trình được bảo mật. Thực ra đây không phải là một tiến trình được tự động hóa vì nó yêu cầu tiêu tốn nhiều thời gian và công sức cho việc kiểm tra lại mã để đảm bảo rằng tính toàn vẹn của mã chương trình được duy trì, do đó số lượng dòng lệnh tỉ lệ thuận với thời gian và công sức cần phải bỏ ra. Xuất phát từ yêu cầu đó Microsoft đã phát triển một tính năng có tên Data Execution Prevention (DEP).

    DEP, một tính năng bảo mật được giới thiệu trong Windows XP SP2, được thiết kế để chặn ứng dụng chạy mã trong vùng không thể chạy của bộ nhớ. DEP xuất hiện trong cả cấu hình Hardware-based DEP (nền tảng phần cứng) và Software-based DEP (nền tảng phần mềm).

    Hardware-based DEP

    DEP được cho là bảo mật nhất khi sử dụng Hardware-based DEP. Trong trường hợp này vi xử lý sẽ đánh dấu mọi vị trí nhớ là “không thể thực thi” nếu vị trí này không chứa mã thực thi. Mục đích của việc này là DEP sẽ chặn mọi mã chạy trong những vùng không thể thực thi.

    Vấn đề chính của việc sử dụng Hardware-based DEP là nó chỉ được hỗ trợ bởi một số ít tiến trình. Vi xử lý có thể thực hiện được điều này là nhờ có tính năng NX của bộ vi xử lý AMD và XD của Intel.

    Software-based DEP

    Khi Hardware-based DEP không tồn tại thì Software-based DEP phải được sử dụng. Loại DEP này được tích hợp trong hệ điều hành Windows. Software-based DEP vận hành bằng cách dò tìm thời điểm mà những ngoại lệ được các chương trình đưa vào và đảm bảo rằng những ngoại lệ này là một phần hợp lệ của chương trình này trước khi cho phép chúng xử lý.

    trích: http://www.perfect.com.vn/kien-thuc/...khac-phuc.aspx
    __________________

    3/15/10

    [Hooking] 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ỹ :D), 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:
    Code:

    LRESULT CALLBACK (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 < style="margin: 5px 20px 20px;">
    Code:

    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
    Code:

    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.
    Code:

    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”

    Code:

    #include

    //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)
    {
    //nế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”

    Code:

    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”

    Code:

    #include

    //các biến toàn cục
    HHOOK hHook = NULL; //handle của hook
    HMODULE hDll; //handle của DLL

    //định nghĩa con trỏ hàm SetGlobalHookHandle() trong file DLL
    typedef 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 hook
    BOOL 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:
    Code:

    //hàm gỡ bỏ hook, với hHook là handle của hook
    BOOL 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.

    reflink: http://forums.congdongcviet.com/printthread.php?t=3407