itprofes
Bạn có muốn phản ứng với tin nhắn này? Vui lòng đăng ký diễn đàn trong một vài cú nhấp chuột hoặc đăng nhập để tiếp tục.

Peering into WinNT Native API [1]

Go down

Peering into WinNT Native API [1] Empty Peering into WinNT Native API [1]

Bài gửi  admin 26/3/2010, 2:19 pm

1. Giới thiệu
Những ai làm việc với WinNT hẳn đã từng nghe nói rằng
có một API ẩn được NT sử dụng ngầm bên trong. API này, được gọi là
Native API, hầu như bị che giấu và chỉ được công bố rộng rãi 1 tập nhỏ.
Sự bí ẩn này khiến người ta tin rằng Native API có thể cung cấp 1 sức
mạnh kì diệu cho ứng dụng, thậm chí còn cho phép chúng vượt qua các giới
hạn an ninh được tạo ra bởi các API chuẩn như Win32.
Việc Native
API chưa bao giờ được Microsoft công bố rõ ràng khiến NT là hệ điều hành
thương mại còn lại duy nhất trên thế giới có tập các dịch vụ hệ thống
bản địa không được công bố. Nhưng cũng phải nói rằng cho tới giờ phút
này, NT API vẫn chưa gây ra vấn đề gì: các chương trình vẫn làm được mọi
thứ có thể bằng giao diện do các Hệ thống con môi trường Hệ điều hành
(OS Environment subsystem) cung cấp. Tuy nhiên, tìm hiểu về giao diện
bản địa của HĐH sẽ giúp ta rõ hơn về cách thức hệ thống làm việc. Hơn
nữa, một cái nhìn bao quát về native API sẽ giúp chúng ta xóa bỏ những
hiểu sai (nếu có) về việc Native API được sử dụng như thế nào, tại sao
chúng được sử dụng, và những API không công bố che giấu ta những gì.
Để
vấn đề được trình bày rõ hơn, tôi xin mô tả sơ qua kiến trúc WinNT
trước khi giới thiệu Native API là gì, nó được gọi trong 1 hoạt động
thông thường như thế nào và vì sao nó được sử dụng làm cơ sở hạ tầng hỗ
trợ API của các Hệ thống con môi trường HĐH. Tiếp đó tôi sẽ liệt kê một
số hàm Native API đã được khám phá cùng cách xây dựng 1 ứng dụng sử dụng
chúng.
2. Tổng quan kiến trúc WinNT
Việc tìm hiểu cặn kẽ kiến
trúc WinNT vượt quá phạm vi bài viết nho nhỏ này, và thật sự phải được
chứa trong hàng đống sách. Phần này chỉ miêu tả kiến trúc WinNT theo
quan điểm biểu đồ khối mức cao và cố gắng đưa ra 1 cách tổng quát kiến
trúc WinNT nhằm làm rõ hơn cho vấn đề sẽ được trình bày ở phần tiếp
theo. Nếu bạn đã hiểu về tổng quan kiến trúc WinNT thì có thể bỏ qua.
Cool)
Lịch sử phát trển máy tính chứng kiến sự ra đời của rất nhiều
kiểu kiến trúc HĐH: từ monolithique (DOS) đến phân lớp (UNIX),
Client/Server v.v… [1]. Kiến trúc hệ thống của WinNT là lai của các kiến
trúc client/server, phân lớp, hướng đối tượng, và đa xử lí. [3]

WinNT, trước tiên, là 1
hệ phân lớp: nó được phân ra làm 2 hệ hoạt động: Kernel-mode và
User-mode – cụ thể như sau:
Kernel-Mode
Trong hệ này, chương trình
có thể truy nhập trực tiếp vào phần cứng, dữ liệu hệ thống cũng như các
tài nguyên hệ thống khác. Kernel-mode bao gồm các thành phần:
-
Executive: Chứa các thành phần thực hiện việc quản lí bộ nhớ, quản lí
tiến trình và luồng, an ninh, nhập/xuất, giao tiếp liên tiến trình và
các dịch vụ HĐH cơ sở khác
- Microkernel: Có nhiệm vụ hàng đầu là
cung cấp đồng bộ hóa giữa nhiều bộ VXL, điều phối và phân phối luồng và
ngắt, lấy thông tin từ Registry khi hệ thống khởi động…
- Hardware
Abstraction Layer (HAL): HAL thay đổi tùy theo phần cứng hệ ĐH đang
chạy, vì thế tương thích với nhiều họ VXL. HAL quản lí phần cứng trực
tiếp
- Device drivers: Các trình điều khiển thiết bị
- Windowing
and graphics system: Thực hiện Giao diện người dùng đồ họa (GUI)
User-Mode
Các
chương trình chạy ở user-mode không thể truy nhập phần cứng trực tiếp.
Hệ thống con user-mode được bảo vệ và có 4 chức năng chính:
- Các
tiến trình hỗ trợ hệ thống đặc biệt, ví dụ tiến trình logon và session
manager.
- Các dịch vụ là các tiến trình server, ví dụ các dịch vụ
Event Log và Schedule
- Các hệ thống con môi trường cung cấp môi
trường HĐH qua các dịch vụ hệ thống bản địa (native system service).
Chúng bao gồm các hệ thống con Win32, POSIX, và OS/2.
- Các ứng dụng
người dùng – bao gồm Win32, Windows 3.1, MS-DOS, POSIX, hay OS/2.
Các
ứng dụng không gọi trực tiếp các dịch vụ hệ thống bản địa của WinNT;
thay vì thế, chúng truyền qua các thư viện liên kết động (DLL) của hệ
thống con. Đến lượt, các thư viện này dịch hàm được công bố thành các
lời gọi dịch vụ hệ thống không được công bố của WinNT – các native WinNT
API

3. Cơ chế hoạt động của NT API
Bất cứ
khi nào 1 chương trình chạy ở user-mode thực hiện I/O, cấp phát hay hủy
bỏ bộ nhớ, khởi tạo tiến trình/luồng hay tương tác với tài nguyên toàn
cục... nó phải gọi 1 hay nhiều các dịch vụ nằm trong kernel-mode. Trong
WinNT, các dịch vụ này bị che dấu khỏi LTV qua API của các Hệ thống con
môi trường, chỉ được thể hiện từ module kernel-mode ntoskrnl.exe tới các
ứng dụng user-mode qua 1 thành phần hệ thống duy nhất, đó là ntdll.dll
Trong
các Hệ thống con môi trường được WinNT hỗ trợ (Win32, OS/2, POSIX),
Win32 được coi là “ngôn ngữ quốc gia”. Hệ thống con môi trường HĐH Win32
được chia thành các tiến trình server (CSRSS.EXE - Client/Server
Runtime SubSystem) và các client-side DLL, được kết nối tới các ứng dụng
sử dụng Win32 API. Nòng cốt của Win32 API được chia thành 3 loại: “cửa
sổ và thông điệp”, “đồ họa”, và “các dịch vụ cơ sở”. Các API “cửa sổ và
thông điệp“, bao gồm CreateWindow() và SendMessage(), được xuất tới ứng
dụng Win32 qua thư viện USER32.DLL. BitBlt() và LineTo() là các hàm “đồ
họa” Win32 và được cung cấp trong GDI32.DLL. Cuối cùng, các dịch vụ cơ
sở bao gồm các hàm API thực hiện I/O, quản lí tiến trình và luồng, quản
lí bộ nhớ, đồng bộ hóa được thực hiện qua KERNEL32.DLL.
Khi ứng dụng
Win32 gọi 1 hàm API, điều khiển được truyền bên trong không gian địa chỉ
của nó tới 1 trong những client-side DLL. DLL này – sau khi kiểm tra
các thông tin trên tiến trình gọi – sẽ thực hiện 1 trong các lựa chọn
sau đây:
• Trả về ngay lập tức
• Gửi 1 thông điệp tới Win32
server yêu cầu giúp đỡ
• Kích hoạt Native API để thực hiện nhiệm
vụ
Lựa chọn dầu tiên hiếm khi xảy ra, chỉ khi DLL có thể thực hiện
nhiệm vụ mà không cần tới các dịch vụ hệ thống của HĐH, 1 ví dụ là hàm
GetCurrentProcess().
Lựa chọn thứ 2 cũng hiếm khi xảy ra. Chỉ khi nào
server có thể nhận biết và cùng tham gia thực thi nhiệm vụ. Ví dụ như
trong trường hợp CreateProcess(), được xuất bởi KERNEL32, Win32 server
sẽ gọi các hàm Native API để tạo 1 tiến trình thực sự và chuẩn bị không
gian địa chỉ cho client.
Lựa chọn cuối cùng thường xuyên xảy ra nhất.
Trước tiên ta hãy xem xét trường hợp các API USER32 và GDI32, sau đó
mới tới tới việc sử dụng Native API của KERNEL32. Trong các phiên bản NT
trước 4.0, các hàm “cửa sổ” và “đồ họa” nằm trong Win32 server
(CSRSS.EXE). Điều này có nghĩa rằng bất cứ khi nào 1 ứng dụng sử dụng
các hàm này, sẽ xuất hiện thông điệp gửi tới server. Từ NT 4.0, các hàm
“cửa sổ” và “đồ họa” đã được chuyển vào 1 thành phần kernel-mode là
WIN32K.SYS. Thay vì gửi thông điệp tới server, các client-side DLL sẽ
gọi trực tiếp tới kernel, tiết kiệm được chi phí gửi thông điệp và
chuyển đổi ngữ cảnh tiến trình. Điều này cải thiện đáng kể hiệu suất đồ
họa của WinNT (1 bằng chứng là trò chơi Pinball). Như vậy, có thể nói
các hàm GDI và USER là Native API thứ hai của WinNT, chỉ khác 1 điều là
chúng ít bí hiểm hơn Native API thường do được công bố rộng rãi.
Hình
2 mô tả dòng điều khiển từ ứng dụng Win32 gọi 1 hàm Win32 API
(CreateFile()), qua KERNEL32, NTDLL và vào kernel-mode, tại đó điều
khiển được chuyển tới dịch vụ hệ thống NtCreateFile.

Ntdll.dll cung cấp cho
kernel32.dll rất nhiều các hàm tiện ích, cơ bản có thể chia ra làm 2 tập
hợp:
• Các hàm runtime được chạy hoàn toàn tại user-mode. Tập hợp
này bao gồm cả 1 số phần của C runtime library chuẩn
• Các
kernel function wrapper thực hiện chuyển đổi từ user-mode tới
kernel-mode và ngược lại.
Các hàm ở tập thứ 2 cho phép các ứng dụng
user-mode gọi các dịch vụ hệ thống tại kernel-mode. Thật thú vị, vậy
việc này được thực hiện như thế nào?
Nếu bạn disassemble các hàm bắt
đầu bằng Nt hay Zw trong ntdll.dll, VD NtQuerySystemInformation() như ở
Mã dẫn 1, bạn sẽ thấy nó rất ngắn gọn. Chỉ đơn giản là 1 lời gọi ngắt?
Thực ra cũng không hẳn như vậy. Với những ai đã từng quen thuộc với lập
trình DOS, hầu hết các dịch vụ hệ thống của DOS được gọi bằng cách gán
mã dịch vụ cho thanh ghi AH, và tham số dữ liệu bổ sung (nếu có) cho
thanh ghi DX, theo sau là chỉ thị gọi ngắt INT 21h. Mã thực hiện trong
NtQuerySystemInformation() - hay các hàm họ Nt*() - cũng tương tự như
vậy: thanh ghi EAX chứa mã dịch vụ, EDX trỏ tới tham số đầu tiên trong
stack của hàm.

Mã dẫn 1 Mã thực thi của
ntdll.NtQuerySystemInformation()
NtQuerySystemInformation:
mov
eax, 97h
lea edx, [esp+4]
int 2Eh
ret 10h

Chỉ
thị đầu tiên nạp cho thanh ghi EAX chỉ số của hàm Native API - mỗi hàm
Native API có 1 chỉ số duy nhất. Chỉ thị thứ hai nạp cho thanh ghi EDX
con trỏ tới danh sách tham số của hàm. Tiếp theo là 1 chỉ thị gọi ngắt
mềm - trên họ x86, ngắt này là 2Eh. Chỉ thị cuối cùng đẩy tất cả tham số
ra khỏi stack của hàm gọi. Chúng ta hãy xem xét kĩ chỉ thị gọi ngắt
2Eh.
Nếu như trong DOS, ngắt 21h chỉ đơn giản bắt CPU nhảy tới 1 địa
chỉ trong Bảng Vector ngắt (IVT) thì trong HĐH được bảo vệ như WinNT,
các ngắt được thực hiện có hơi khác 1 chút. IVT được thay bằng Bảng Bộ
mô tả ngắt (IDT) chứa các bộ mô tả bộ nhớ của các địa chỉ đích chứ không
đơn thuần là các con trỏ. Trong trường hợp ngắt 2Eh, IDT cung cấp 1
cổng chuyển đổi CPU từ user-mode tới kernel-mode trước khi nhảy tới địa
chỉ đích, và chuyển về user-mode khi lời gọi kết thúc. Địa chỉ đích của
cổng INT 2Eh nằm trong module ntoskrnl.exe có tên là KiSystemService().
Nhiệm
vụ của KiSystemService() là kiểm tra chỉ số của hàm Native API, nếu hợp
lệ, nó sẽ chuyển điều khiển tới 1 dịch vụ hệ thống tương ứng trong
kernel-mode bằng cách: convert chỉ số hàm Native API thành chỉ số của 1
mảng bên trong kernel - gọi là KiSystemServiceTable. Mỗi chỉ mục trong
mảng này lại bao gồm 1 con trỏ tới hàm kernel tương ứng và số tham số
của hàm đó (với các hàm Win32 Native API, chỉ số lại trỏ tới 1 mảng thứ 2
của dịch vụ hệ thống. Các con trỏ trong mảng thứ hai này tham chiếu tới
các hàm nằm trong WIN32K.SYS). KiSystemService() sau đó sẽ lấy các tham
số trong user-mode stack (trỏ tới bởi thanh ghi EDX trong x86) đẩy vào
kernel-mode stack, rồi kích hoạt hàm kernel tương ứng (các hàm này
thường được cung cấp bởi các hệ thống con NT Executive, nằm hoàn toàn
trong kernel-mode, như Process Manager, Virtual Memory Manager hay I/O
Manager). Thực ra, cơ chế này có phức tạp hơn 1 chút. Nếu bạn quan tâm
thì có thể xem trong Chương 2 cuốn Undocumented Windows 2000 Secrets.
Khi
trở về, điều khiển sẽ được trả lại hàm user-mode bằng hàm
KiServiceExit() - cũng nằm trong ntoskrnl.exe.
Mỗi dịch vụ hệ thống
tất nhiên đều thực hiện 1 hoạt động nhất định phục vụ cho hàm API tương
ứng, tuy nhiên, hầu hết trong số chúng đều cần thẩm tra các tham số được
truyền tới từ user-mode. Một ít các tham số này là con trỏ, và việc
tham chiếu ngược 1 con trỏ bất hợp lệ từ kernel-mode có thể gây ra 1
thảm họa. Giả sử có 1 chương trình nào đó chặn các hàm Native API lại
rồi gài vào đó các tham số không thích hợp, 1 số dịch vụ hệ thống sẽ
thất bại trong việc thẩm tra các tham số này, kết quả là 1 màn hình xanh
xanh sẽ hiện ra... Microsoft đã nhận thức được vấn đề trên và tung ra
các miếng vá trong các bản Service Pack.
Một điều cần lưu ‎ là các
tham số của họ hàm Nt*() cũng giống như của họ Zw*() – mà một số được
công bố rõ trong Windows NT DDK. Theo Bảng chú giải Kernel Mode của NT
(NT’s Kernel Mode Glossary), họ hàm Nt*() sẽ thẩm tra các tham số rồi
thiết lập tường minh hệ truy nhập trước đó thành USER mode, còn họ hàm
Zw*() thì không. Vì thế NT Drivers sẽ gọi các hàm Zw*(), còn các Hệ
thống con môi trường HĐH (hay thực sự là các ứng dụng native NT) sẽ gọi
các hàm Nt*(), do chúng được gọi từ user-mode.

4.
Các hàm NTAPI
Như trên đã nói, các hàm NT API không được Microsoft
công bố, vậy làm sao chúng ta có thể biết – và sử dụng được – những hàm
này? Tất nhiên là các hàm native API đều nằm trong ntdll.dll, nhưng để
sử dụng được chúng ta phải biết được danh sách các definition (constant,
type…), các hàm cùng signature – các tham số và giá trị trả về - của
chúng. Những cái này thông thường nằm trong file .h do nhà sản xuất cung
cấp. Tuy nhiên, do thực sự không có cái gọi là Microsoft ntdll.h, các
file ntdll.h tồn tại hiện hay là đều do các hacker/craker bằng cách nào
đó khám phá ra, và vì thế rất khác nhau. Dưới đây xin giới thiệu 1 số
link để các bạn có thể tham khảo:
- Danh sách 1 số Native API cùng
các hàm Win32 API tương ứng của chúng:
http://www.sysinternals.com/ntw2k/info/ntdll.shtml
- Danh sách các
definition cùng các hàm và signatures tương ứng:
http://undocumented.ntinternals.net
- Một file ntdll.h:
http://cvs.kldp.net/cgi...l.h?cvsroot=mogua#rev1.5
(Tất cả cũng đã
được gói ở đây)
Note: 1 số definition bạn có thể tìm thấy trong các
DDK header file, tuy nhiên cần chú Ý khi #include những header files
trên vào ứng dụng user-mode thông thường viết bằng C do có thể có xung
đột với 1 số header file của Win32 SDK.

5. Xây dựng chương trình sử
dụng NT Native API
Có 2 cách để xây dựng ứng dụng sử dụng Native API,
tương ứng với 2 kiểu liên kết của thư viện: run-time và link-time
1.
Sử dụng kiểu run-time khi bạn không có, hoặc không muốn sử dụng các
file ntdll.lib/ntdll.h
- Định nghĩa các definition và kiểu dữ liệu
con trỏ hàm tương ứng với signature của hàm Native API, VD:
//for
NtQuerySystemInformation
typedef NTSTATUS (NTAPI
*NTQUERYSYSINFO)(SYSTEMINFOCLASS,PVOID,DWORD,PDWORD);
- Trong chương
trình, sử dụng GetProcAddress()để lấy con trỏ hàm này từ ntdll, VD:
NTQUERYSYSINFO
pNtQuerySysInfo= (NTQUERYSYSINFO)GetProcAddress( GetModuleHandle(
_T("ntdll")),"NtQuerySystemInformation");
2. Để sử dụng kiểu
link-time, bạn bắt buộc phải có 2 file ntdll.lib và ntdll.h có chứa hàm
Native API cần sử dụng (tài liệu có nói file ntdll.lib có trong Platform
SDK, nhưng tôi tìm mãi không thấy, tuy nhiên, cũng có cách để bạn tự
tạo file này)
- include ntdll.h
- import ntdll.lib vào danh sách
các import library (trong VS 6 bạn chọn Project/Settings/Link tab rồi
add ntdll.lib vào Object/library modules, trong VS 7:
Project/Properties/Linker/Input/Additional Dependenciess). Hoặc nếu
không, sử dụng #pragma comment (linker, "/defaultlib:ntdll.lib") trong
header file của chương trình.
Demo project

6. Kết luận
Vậy
liệu đây có phải là 1 cách mới để bạn viết các ứng dụng user-mode? Và
bạn được gì khi "going native" hay sử dụng trực tiếp các dịch vụ hệ
thống của Win NT?
Thực ra, bạn sẽ gặp nhiều rắc rối. Bởi vì NT API
không được công bố hay hỗ trợ, nên sẽ không có ai ở Microsoft để bạn
phàn nàn nếu như giao diện này thay đổi hay không làm việc như Ý. Thêm
nữa, bạn sẽ phải tự tay gánh vác rất nhiều việc, như chỉ ra đường dẫn
đầy đủ khi mở file chẳng hạn. Và nhớ rằng, NT API không phải được thiết
kế làm 1 giao diện người dùng cuối
Vậy cuối cùng thì có lí do nào để
ta sử dụng NT API trực tiếp? Tất nhiên là có. Ít nhất là cũng có 1 thứ
mà bạn không thể làm được nếu không sử dụng NT Native API, đó là việc
hủy bỏ các yêu cầu I/O (hàm NtCancelIoFile()) – một điều không thể đối
với Win32 API. Thêm nữa, tìm hiểu về NT API cũng khiến bạn hiểu rõ hơn
về WinNT cũng như cơ chế hoạt động của nó. Và cuối cùng, last but not
least: Native API có lẽ giờ đã không còn là điều gì đó bí ẩn đối với bạn
nữa Cool)
et tổng hợp

7. Tài liệu tham khảo:
[1] Giáo trình
Hệ điều hành nâng cao
[2] The Foundations of Microsoft Windows NT
System Architecture
[3] Windows 2000 Architecture
[4] Programming
WinNT4 Unleashed
[5] Using NT API for file I/O
[6] Inside Native
API
[7] Inside Native Applications
[8] Interfacing the the Native
API in Windows 2000
[9] Undocumented Windows 2000 Secrets
[10]
Inside Windows NT
admin
admin
Thiếu Úy III
Thiếu Úy III

Tổng số bài gửi : 627
Diem : 6517
Thank : 4
Join date : 24/03/2010
Đến từ : Bỉm Sơn - Thanh hóa

https://itprofes.forumvi.com

Về Đầu Trang Go down

Về Đầu Trang


 
Permissions in this forum:
Bạn không có quyền trả lời bài viết