Hook
Malware
Last updated
Malware
Last updated
Đã tìm hiểu trong task về Keylogger: Link
Cách thực hiện inline hook:
Đầu tiên, chúng ta sẽ lấy ra n-bytes đầu của function mà chúng ta muốn hook tới. Lưu nó vào trong một nơi trong memory. Nơi này sẽ được gọi là "trampoline".
Tiến hành ghi đè n-bytes đầu kia bằng lệnh JMP để nhảy tới function (hook code) chúng ta muốn.
Thường sẽ là 1 byte opcode của lệnh JMP (thường là 0xE9) + 4 bytes địa chỉ cần nhảy tới
Sau khi hook code được thực thi thành công, chúng ta sẽ thực thi các bytes được lưu trong trampoline, sau đó sử dụng một lệnh JMP để nhảy tới vị trí sau n-bytes ban đầu bị ghi đè. Điều này để giữ nguyên logic code và tránh đệ quy liên tục.
Tùy vào cách chúng ta muốn dùng inline hook, có thể sẽ chỉ cần trả lại các bytes đã ghi đè mà không cần thực hiện jmp (bỏ qua logic code gốc). Theo như tìm hiểu thì lúc này sẽ không gọi là trampoline đc.
Các function sử dụng cho các bước trên:
Với hàm MessageBox, nó có trong user32.dll: https://windows10dll.nirsoft.net/user32_dll.html
GetModuleHandleW: https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlew (lần này ko dùng được) -> LoadLibraryW: https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw
Có thể sử dụng push + ret thay cho jmp
Với x64, sử dụng mov rax, address và jmp rax
Việc đầu tiên ta cần làm là được địa chỉ của import directory. Địa chỉ của import directory sẽ bằng tổng base address của target process và RVA của nó. RVA của import directory sẽ được lấy từ DataDirectory tương ứng với nó trong optional header.
Sau khi tìm được địa chỉ của import directory, ta tiến hành tìm kiếm entry trong IAT chứa địa chỉ của API MessageBoxA(). Trước tiên, ta cùng tìm hiểu một số cấu trúc quan trọng để có thể hiểu rõ quá trình này được thực hiện như nào.
Import directory là một bảng chứa nhiều entry, trong đó entry cuối cùng trong bảng không được sử dụng. Mỗi entry sẽ tương ứng với một DLL được nạp lên chương trình tại thời điểm chạy và được biểu diễn bằng cấu trúc IMAGE_IMPORT_DESCRIPTOR.
Trong cấu trúc IMAGE_IMPORT_DESCRIPTOR ta cần quan tâm đến 3 trường là OriginalFirstThunk, FirstThunk, và Name. OriginalFirstThunk, FirstThunk, và Name lần lượt lưu trữ RVA của import name table (INT), import address table (IAT), và tên của DLL.
Mỗi entry trong INT và IAT đều được biểu diễn bởi cấu trúc IMAGE_THUNK_DATA. Đối với một entry trong INT thì trường AddressOfData sẽ lưu trữ RVA của một entry khác trong bảng hint/name. Còn với một entry trong IAT thì trường Function sẽ lưu trữ địa chỉ của API tương ứng.
Bảng hint/name gồm nhiều entry, mỗi entry được biểu diễn bởi cấu trúc IMAGE_IMPORT_BY_NAME. Trường Name trong cấu trúc này sẽ lưu trữ tên của một API nào đó được sử dụng bởi chương trình.
Sau khi tìm hiểu xong các cấu trúc quan trọng, ta tiến hành tìm kiếm entry trong IAT chứa địa chỉ của API MessageBoxA(). Nên nhớ rằng API MessageBoxA() thuộc về user32.dll nên đầu tiên ta phải cần tìm kiếm một entry trong import directory tương ứng với user32.dll bằng trường Name có trong cấu trúc IMAGE_IMPORT_DESCRIPTOR. Sau đó, ta truy xuất INT và IAT tương ứng với user32.dll lần lượt bằng trường OriginalFirstThunk và FirstThunk. Tiếp theo, ta duyệt qua từng entry có trong INT để tìm kiếm một entry tương ứng trong bảng hint/name mà trường Name của nó là chuỗi "MessageBoxA". Index của entry tìm được trong INT chính là index của entry trong IAT chứa địa chỉ của API MessageBoxA(). Ví dụ: entry thứ 4 trong bảng INT có chứa RVA của một entry trong bảng hint/name, mà trường Name của entry trong bảng hint/name này lại là chuỗi "MessageBoxA" cho nên entry thứ 4 trong bảng IAT sẽ chứa địa chỉ của API MessageBoxA().
Tương tự như IAT Hook nhưng thay vào đó ta sẽ thay đổi địa chỉ của hàm cần hook trong bảng EAT, nơi chứa địa chỉ của tất cả các hàm và biến mà một DLL cung cấp cho chương trình.
Một số vấn đề chúng ta cần chú ý:
Khi một exe thực thi, Win Loader sẽ import các hàm mà exe cần sử dụng vào bảng IAT. Vì vậy đầu tiên Win Loader sẽ loading DLL có các hàm cần import vào vùng nhớ của process exe. Sau đó nó sẽ GetProcAddress các hàm cần import trong bảng EAT của DLL và lấp vào bảng IAT của process exe.
Vì vậy muốn hooking một hàm trong bảng EAT của DLL ta cần một số điều kiện sau:
DLL bị inject đã được loaded vào vùng không gian địa chỉ process.
Hàm hook phải nằm trong vùng không gian địa chỉ process bị inject
Hàm bị hook phải GetProcAddress và gọi hàm sau khi bảng EAT của DLL bị hook. Điều này có nghĩa là nếu bạn hook EAT hàm API sau khi Win Loader loading hoàn tất ứng dụng, thì hàm hook của chúng ta không có tác dụng vì lúc đó bảng IAT của process đã được lấp giá trị xong (tức là lấy addr hàm từ EAT của DLL cho bị hook lấp vào IAT của process).
Minh họa:
Kernel mode và User mode: https://learn.microsoft.com/vi-vn/windows-hardware/drivers/gettingstarted/user-mode-and-kernel-mode
SSDT (System Service Dispatch Table) là một cấu trúc quan trọng trong hệ thống Windows, hoạt động như một bảng chứa địa chỉ (trong hệ điều hành 32 bit) hoặc các offset tương đối (trong hệ điều hành 64 bit) đến các hàm kernel. Những hàm này thực hiện các dịch vụ hệ thống mà các chương trình ở user space yêu cầu thông qua các lệnh gọi hệ thống (syscalls).
SSDT là thành viên đầu tiên trong cấu trúc Service Descriptor Table của kernel memory, chứa bốn SYSTEM_SERVICE_TABLE, với nt
là chỉ dẫn (pointer) đến SSDT chính.
ServiceTable: Là một mảng các con trỏ đến các hàm hệ thống (system service routines). Mỗi entry trong bảng này tương ứng với một syscall cụ thể.
CounterTable: Không được sử dụng trong các phiên bản hiện đại của Windows, nhưng vẫn có trong cấu trúc để duy trì tính tương thích.
ServiceLimit: Số lượng các mục tối đa trong ServiceTable. Điều này xác định số lượng các syscall mà Windows có thể xử lý.
ArgumentTable: Một mảng các byte, mỗi byte tương ứng với số lượng bytes được cấp phát cho các đối số của mỗi hàm tương ứng trong SSDT. Điều này giúp kernel biết được cần phải truyền bao nhiêu dữ liệu từ user mode sang kernel khi thực hiện một syscall.
Khi một syscall được thực hiện (ví dụ, qua lệnh sysenter
hoặc int 0x2e
), hệ điều hành sẽ sử dụng syscall number được lưu trong thanh ghi EAX
để xác định hàm cần được gọi. Số này được sử dụng như một chỉ số vào SSDT để tìm con trỏ hàm phù hợp. Sau đó, hệ điều hành chuyển đến hàm kernel này để xử lý syscall.
System Call Number được chia thành ba phần: 12 bit đầu tiên là định danh cho syscall; 2 bit tiếp theo xác định bảng mô tả dịch vụ (Service Descriptor Table - SDT) được sử dụng; và các bit còn lại không sử dụng.
Trong hệ thống Windows, có hai bảng SDT chính được sử dụng: KeServiceDescriptorTable
và KeServiceDescriptorTableShadow
. Cả hai đều chứa cấu trúc SST, nhưng KeServiceDescriptorTableShadow
có thể chứa các entry bổ sung không xuất hiện trong bảng chính.
Hook SSDT có nghĩa là ghi đè vào các hàm hệ thống trong kernel mode. Để làm điều này chúng ta cần có quyền ghi vào chúng. Có 3 cách:
WP Flag (Write Protect) trong thanh ghi CR0 kiểm soát việc có thể ghi vào các trang bộ nhớ chỉ đọc trong chế độ kernel hay không. Bằng cách tạm thời xóa cờ WP, mã kernel có thể được ghi đè lên bảng SSDT.
Ví dụ:
Trong ví dụ này, hàm DisableWP
sẽ tắt cờ WP, cho phép ghi vào bảng SSDT, và EnableWP
sẽ bật lại cờ WP sau khi thực hiện hook.
Bằng cách thay đổi khóa registry HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\EnforceWriteProtection
từ 1 sang 0, bạn có thể vô hiệu hóa cơ chế bảo vệ ghi cho toàn bộ hệ thống, cho phép ghi vào SSDT mà không cần phải vô hiệu hóa cờ WP.
Không nên sử dụng phương pháp này vì nó ảnh hưởng toàn hệ thống và có thể gây ra vấn đề bảo mật. Thay vào đó, hãy chỉnh sửa registry cẩn thận và biết rõ hậu quả.
MDL mô tả layout vật lý của các trang bộ nhớ cho một buffer bộ nhớ ảo. Bằng cách phân bổ MDL cho bộ nhớ nơi bảng SSDT được lưu trữ và thay đổi các cờ của nó, bạn có thể tạm thời cho phép ghi vào SSDT. Ví dụ:
Trong ví dụ trên, MDL được sử dụng để ánh xạ bảng SSDT vào bộ nhớ có thể ghi, cho phép thay đổi các con trỏ hàm mà không cần phải tắt cờ WP.
Tham khảo thêm: https://www.infosecinstitute.com/resources/hacking/hooking-system-service-dispatch-table-ssdt/
Hook filter driver trên Windows là một loại trình điều khiển thiết bị đặc biệt có khả năng chặn và sửa đổi các yêu cầu gửi đến hệ điều hành từ các ứng dụng hoặc trình điều khiển khác. Nói cách khác, nó hoạt động như một lớp trung gian giữa phần mềm và phần cứng, cho phép bạn can thiệp và thay đổi cách thức hoạt động của hệ thống.
Có hai loại hook filter driver chính:
Hook filter driver kernel-mode: Loại này chạy trong kernel của hệ điều hành, cho phép nó truy cập và sửa đổi các yêu cầu ở cấp độ thấp nhất. Tuy nhiên, việc phát triển và sử dụng loại hook filter driver này đòi hỏi kiến thức chuyên sâu về hệ điều hành và lập trình kernel.
Hook filter driver user-mode: Loại này chạy trong không gian người dùng, dễ phát triển và sử dụng hơn so với hook filter driver kernel-mode. Tuy nhiên, nó có quyền truy cập hạn chế hơn và không thể sửa đổi các yêu cầu ở cấp độ thấp.
Một số ứng dụng phổ biến của hook filter driver bao gồm:
Giám sát hệ thống: Theo dõi hoạt động của hệ thống, ghi lại các yêu cầu và phản hồi, v.v.
Bảo mật: Chặn các phần mềm độc hại và các hoạt động nguy hiểm khác.
Khả năng tương thích: Khắc phục các vấn đề tương thích giữa các ứng dụng hoặc trình điều khiển.
Tùy chỉnh: Thay đổi cách thức hoạt động của hệ thống theo nhu cầu của bạn.
Lưu ý:
Hook filter driver có thể ảnh hưởng đến hiệu suất hệ thống.
Việc sử dụng hook filter driver không đúng cách có thể gây ra lỗi hệ thống.
Nên cẩn thận khi sử dụng hook filter driver từ các nhà cung cấp không đáng tin cậy.
© 2024,Pham Quoc Trung. All rights reserved.