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:
- Để gỡ bỏ override, trả lại hàm API như cũ.
- 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 ngồ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
ARRAY5BYTE FAR PASCAL Get5ByteOrgFunction( LPCSTR APIFunctionName) {
HINSTANCE hinstDll;
FARPROC fpFunction; UINT fpFunctionOff, fpFunctionSeg; BYTE Byte5temp[5];
ARRAY5BYTE Array5temp;
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);
// lay noi dung cua 5 bytes dau cho vao cau truc Byte5temp // 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:
Hàm Nội dung 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