Hiện thực kỹ thuật override trên Windows 16bits:

Một phần của tài liệu Nghiên cứu các phương pháp nhận dạng từ dưới Cursor mouse trên destop windows (Trang 46 - 51)

Phương pháp mà chúng tôi chọn để áp dụng vào chương trình là ở dạng front-end processing tức là 5 bytes lệnh đầu tiên của đoạn mã lệnh hàm API sẽ được ghi đè bằng 5 bytes của lệnh JMP address trong đó byte thứ nhất là mã hợp

ngữ của lệnh CALL, 4 byte kế tiếp là offset và segment tương ứng của đoạn mã lệnh hàm override.

Trình tự hiện thực bao gồm các bước sau:

1 - Xác định địa chỉ bộ nhớ nơi đoạn mã lệnh override thường trú:

Thường chúng được tập hợp dưới dạng các tập tin .DLL, .EXE của developer. Có 2 hàm để lấy địa chỉ logic trong bộ nhớ của bất kỳ hàm API nào: GetProcAddress và MakeProcInstance.

2 - Xác định địa chỉ bộ nhớ nơi bắt đầu đoạn mã tương ứng của hàm API cần override:

Đây cũng chính là điểm nhập của các hàm trong thư viện liên kết động đã được nạp vào bộ nhớ. Các thư viện này là thành phần cơ bản của Windows và được nạp vào bộ nhớ khi Windows khởi động. Tại địa chỉ bộ nhớ của hàm API có được ta có thể tạo một lệnh nhảy từ đây đến địa chỉ đoạn mã lệnh override. Nhưng Windows chỉ cho phép ghi lên data segment chứ không cho phép ghi lên code segment. Vì vậy ta phải có bước trung gian:

3 - Thay đổi thuộc tính của segment chứa mã hàm API trong Windows:

Bằng cách dùng hàm PrestoChangoSelector để có thể thay đổi thuộc tính segment theo 2 chiều: từ code sang data hoặc từ data sang code.

4 - Ghi mã lệnh nhảy vào địa chỉ bắt đầu đoạn mã hàm API:

Dùng lệnh nhảy không điều kiện JMP thì lúc đó stack của ứng dụng sẽ không đổi, đỉnh stack vẫn là địa chỉ trở về ứng dụng sau lệnh gọi hàm API, do vậy khi gặp lệnh return trong thân hàm override thì địa chỉ trở về là địa chỉ lệnh kế tiếp sau lệnh gọi hàm API trong ứng dụng. Như vậy để làm điều này thì chúng ta ghi đè 5 bytes đầu tiên tại địa chỉ bắt hàm API với dội dung là mã hợp ngữ của lệnh JMP address trong đó byte đầu tiên là mã lệnh JMP, có giá trị là 0EAh, 4 byte kế tiếp là địa chỉ logic dạng kiến trúc đoạn offset (2 byte)-segment (2 byte) nơi bắt đầu đoạn mã lệnh override của developer.

5 - Phục hồi 5 bytes nguyên thủy mã lệnh đầu tiên của hàm API:

- Giải thuật thực hiện nói chung giống hệt như việc cài đặt override, chỉ khác là thay vì ghi mã lệnh nhảy và địa chỉ thì bây giờ ghi 5 bytes nguyên thủy của hàm API.

- Năm bytes nguyên thủy của hàm API là hằng số, nên có thể dùng một trong hai cách sau:

* Ghi thẳng các mã lệnh này vào trong chương trình. Cách này đơn giản dễ làm nên việc hiện thực chương trình nhanh, và không phụ thuộc vào nhân tố bên ngoài (phần bộ nhớ lưu trữ 5 bytes nguyên thủy) nên tránh được việc chương trình chạy sai lệch nếu vô ý thay đổi 5 bytes này, nhưng không tổng quát.

* Khi cài đặt override thì đọc 5 bytes nguyên thủy, lưu vào trong DLL, đến khi gỡ bỏ thì lấy 5 bytes đó ghi trở lại. Cách này tổng quát hơn, nhưng chương trình sẽ phức tạp hơn và phải làm nhiều việc hơn một cách không cần thiết.

6 - Thiết kế hàm override API:

Điều trước nhất cần nhớ khi thiết kế hàm override API là việc đặt các parameters của nó: phải giống hệt như hàm API nguyên thủy, và từ các parameters này ta sẽ chứa các thông tin cần thiết. Đồng thời thông tin trả về cũng phải cùng kiểu dữ liệu với hàm API nguyên thủy.

Việc xử lý bên trong hàm override thì tùy ý đồ của ứng dụng, nói chung là có thể làm bất cứ việc gì. Chỉ có một việc đặc biệt và cần thiết là gọi lại hàm nguyên thủy, nói chung việc này cũng đơn giản, bao gồm 3 bước như sau:

- Gỡ bỏ override : Hàm API trở lại bình thường - Gọi lại hàm API

- Cài đặt override lại như cũ.

7 – Cách lấy nội dung 5 bytes đầu tiên của một hàm API:

Chúng tôi chọn phương pháp thứ nhất để phục hồi nội dung 5 bytes đầu của hàm API khi gỡ bỏ override. Muốn vậy phải xác định trước giá trị 5 bytes này.

Các bước hiện thực để lấy nội dung 5 bytes đầu tiên của một hàm API như sau:

- Lấy địa chỉ của hàm API thường trú trong bộ nhớ - Lấy địa chỉ offset và segment của hàm

- Sau đó dùng đoạn chương trình viết bằng hợp ngữ để chuyển nội dung từng byte một vào trong 1 biến có cấu trúc chỉ gồm 1 trường là một mảng 5 byte.

- Từ đó cho hiển thị để biết nội dung.

Như vậy ta có thể đọc nội dung của bất kỳ hàm API nào. Sau đây là chương trình thực hiện:

// kieu du lieu

struct First5 { BYTE memb[5]; }; typedef struct First5 ARRAY5BYTE;

// doan chuong trinh (adsbygoogle = window.adsbygoogle || []).push({});

ARRAY5BYTE FAR PASCAL Get5ByteOrgFunction( LPCSTR APIFunctionName) {

HINSTANCE hinstDll;

FARPROC fpFunction;

UINT fpFunctionOff, fpFunctionSeg;

BYTE Byte5temp[5];

ARRAY5BYTE Array5temp;

hinstDll= LoadLibrary("USER.EXE"); if (hinstDll < HINSTANCE_ERROR) {

MessageBox(NULL,"Can not load library", NULL,MB_ICONEXCLAMATION); return(NULL); } fpFunction= GetProcAddress(hinstDll,APIFunctionName); fpFunctionSeg=SELECTOROF(fpFunction); fpFunctionOff=OFFSETOF(fpFunction); FreeLibrary(hinstDll);

// bang ngon ngu Assembly _asm { push ds mov ds,fpFunctionSeg mov bx,fpFunctionOff inc fpFunctionOff inc fpFunctionOff mov ax,[bx] mov Byte5temp[0],al mov Byte5temp[1],ah mov bx,fpFunctionOff inc fpFunctionOff inc fpFunctionOff mov ax,[bx] mov Byte5temp[2],al mov Byte5temp[3],ah mov bx,fpFunctionOff mov ax,[bx] mov Byte5temp[4],al pop ds } Array5temp.memb[0]=Byte5temp[0]; Array5temp.memb[1]=Byte5temp[1]; Array5temp.memb[2]=Byte5temp[2]; Array5temp.memb[3]=Byte5temp[3]; Array5temp.memb[4]=Byte5temp[4]; return(Array5temp); }

Nội dung 5 bytes đầu tiên của 5 hàm xuất văn bản của Windows 3.x:

TextOut ExtTextOut TabbedTextOut DrawText GrayString 55 8B EC 68 DD h B8 8F 05 45 55 h 55 8B EC 68 66 h C8 10 00 00 1E h 66 58 6A 19 66 h

Một phần của tài liệu Nghiên cứu các phương pháp nhận dạng từ dưới Cursor mouse trên destop windows (Trang 46 - 51)