“Heaven’s Gate” là tên thường gọi của một
kỹ thuật cho phép binary 32-bit thực thi các lệnh 64-bit mà không cần tuân theo
luồng xử lý chuẩn trên môi trường (Windows
32-bit on Windows 64-bit) WoW64. Có thể hiểu đơn giản WoW64 như
là một sandbox nhằm hỗ trợ các ứng dụng 32-bit chạy liền mạch trên Windows 64-bit.
Các tiến trình WoW64 thường gồm hai phiên bản của ntdll.dll. Đầu tiên là phiên bản 32-bit (được tải từ thư
mục SysWow64), nó
giúp chuyển tiếp các lời gọi hệ thống (system calls) tới môi trường WoW64. Thứ
hai là phiên bản 64-bit (được tải từ thư mục System32), tiếp nhận thông tin từ môi trường WoW64 và chịu
trách nhiệm chuyển đổi user-mode sang kernel-mode. Bên cạnh đó là một số các
DLLs đặc biệt khác, bao gồm: wow64.dll, wow64cpu.dll, wow64win.dll.
Với
kiến trúc trên, có thể thấy bản thân một tiến trình 32-bit không thể nhìn phần
64-bit và bị giới hạn trong việc sử dụng các DLLs 32-bit. Do đó, nếu một mã độc
32-bit muốn thực hiện inject vào tiến trình 64-bit, bắt buộc phải sử dụng các các
hàm API 64-bit. Làm thế nào để các mã độc giải quyết được bài toán này? Khá đơn
giản, ở chế độ real mode, CPU có một thanh ghi đoạn (segment register) 16-bit,
thanh ghi này được nhân với 16 (bằng cách dịch trái 4 bit) sau đó cộng thêm với
địa chỉ đầu vào để cung cấp một địa chỉ 20 bit, cho phép khả năng truy cập toàn
bộ 1 MB bộ nhớ. Tuy nhiên, ở chế độ protected mode, việc phân chia có sự khác
biệt đáng kể. Thanh ghi đoạn lúc này không chiếm cả 16-bit, nó được chia thành
3 phần: Bộ chọn segment (Segment Selector), TI (Chỉ định bảng mô tả - Table
Indicator), và RPL (Mức đặc quyền yêu cầu – Request Privilege Level):
- Segment Selector (13 bits), quy định lúc nào GDT (Global Descriptor Table)/LDT (Local Descriptor Table) được sử dụng.
- TI (1 bit), quy định bảng mô tả nào sẽ được sử dụng (0 cho GDT, 1 cho LDT).
- RPL (2 bit), quy định mức bảo vệ hiện đang sử dụng bởi CPU. Đây là cách CPU theo dõi mức đặc quyền của các thao tác đang thực thi.
Các
process trên môi trường Windows 64-bit đều có 2 đoạn mã (code segment): segment
0x23
tương ứng với chế độ x86 và segment 0x33 tương ứng với chế độ x64. Để chuyển sang chế độ
64-bit, không cần thay đổi code segment thành 0x33, chỉ đơn giản bằng cách thay đổi bit trong segment
selector. Việc thay đổi segment selector sẽ không ảnh hưởng tới các trường TI
hay RPL nói trên.
- 0x23 biểu diễn ở nhị phân: 0010 0011
- 0x33 biểu diễn ở nhị phân: 0011 0011
Như
vậy, chỉ cần thay đổi phần segment selector từ 4 thành 6:
Do
đó, thông qua việc thay đổi lựa chọn segment, hoàn toàn có thể chuyển sang chế
độ 64-bit thông qua gọi lệnh “CALL 0x33:Address” hoặc “JMP 0x33:Address”. Kĩ thuật này đã được giới thiệu khá lâu và hiện tại vẫn đang được các mã độc như GuLoader,
Sodinokibi sử dụng
rất nhiều, bởi nó cho phép thực hiện inject code vào cả hai tiến trình 64-bit
và 32-bit từ một tiến trình 32-bit duy nhất trên môi trường WoW64.
Để
kiểm tra môi trường thực thi xem có phải 64-bit hay không, mã độc thường gọi
hàm API IsWow64Process. Nếu kết quả trả về
là TRUE,
mã độc sẽ biết được tiến trình 32-bit của nó đang thực thi trên Windows 64 bit.
Dựa
vào kết quả trả về, mã độc sẽ đưa ra lựa chọn rẽ nhánh thực hiện tương ứng:
Như
đã đề cập ở trên, để có thể inject được payload vào tiến trình 64-bit, mã độc cần
phải sử dụng các hàm 64-bit tương ứng. Để làm được điều này, mã độc sẽ thực hiện
lấy handle của 64-bit ntdll:
Đi
sâu vào hàm f_get_ntdll để tìm hiểu cách mã độc chuyển sang thực hiện lệnh ở chế độ 64-bit, tại
đây mã độc gọi hàm f_get_PEB64 để lấy địa chỉ của PEB (Process Environment Block):
PEB là một cấu trúc dữ liệu đặc biệt chứa thông tin mô tả của process.
Thông qua PEB, có thể lấy được danh sách các DLLs được nạp cùng process, các tham số
khởi động của processs, ImageBaseAddress, địa chỉ heap, kiểm tra xem có đang bị debug hay không, tìm địa chỉ base
của bất kỳ DLLs nào được nạp, … Chi tiết mã lệnh tại hàm f_get_PEB64 áp dụng kĩ thuật Heaven's Gate như sau:
Như
trên hình, segment 0x33 được đẩy lên Stack, sau đó mã độc gọi lệnh call $+5, thực chất là một lệnh call tới địa chỉ phía dưới (trong trường hợp này là 0x0040187D). Bằng lệnh call này, địa chỉ trở về là 0x0040187D sẽ nằm ở đỉnh Stack, sau đó được +5 thành 0x00401882. Cuối cùng, gọi lệnh retf, là một “far return”, nó nhận hai tham số DWORD từ Stack là segment:address. Vì vậy, khi retf thực thi, nó sẽ tương ứng với lệnh jmp với địa chỉ trở về thực tế là 0x33:0x00401882. Thông qua việc thay đổi code segment này, mã lệnh
tại địa chỉ được chỉ định là 0x00401882 sẽ được hiểu ở chế độ 64-bit như trên hình.
Các
lệnh từ 0x00401882 chịu trách nhiệm gán nội dung của thanh ghi r12 vào một biến trên Stack, sau đó chuyển lại về
chế độ 32-bit. Thanh ghi r12 lúc này chính là con trỏ tới cấu trúc TEB64.
Lệnh
retf tại
0x00401898 sẽ
chuyển hướng thực thi tới địa chỉ tiếp theo tại 0x00401899 ở chế độ 32-bit, có nhiệm vụ lấy offset tới PEB64 và chuyển sang thực hiện lệnh
ở chế độ 64-bit tại địa chỉ tiếp theo là 0x004018C3:
Đoạn
code tiếp theo thực hiện từ địa chỉ 0x004018C3, nhiệm vụ cuối cùng là lấy ra địa chỉ của PEB64 và gán cho cặp thanh ghi edx:eax.
Kết
quả, hai thanh ghi edx:eax sẽ trỏ tới cấu trúc PEB64.
Với
việc có được địa chỉ của PEB, mã độc sẽ thực hiện các bước tiếp theo:
- Lấy offset của Ldr (+0x018 Ldr).
- Thông qua Ldr có được cấu trúc _PEB_LDR_DATA.
- Từ _PEB_LDR_DATA lấy offset của InLoadOrderModuleList (+0x010 InLoadOrderModuleList)
- InLoadOrderModuleList là danh sách chứa tất cả các DLLs được nạp vào bộ nhớ cùng process. Dựa vào nó, mã độc lấy ra tên của DLL được nạp, so sánh với chuỗi “ntdll.dll”, nếu tìm thấy sẽ trả về địa chỉ base của thư viện ntdll.dll.
Như
vậy, bằng cách áp dụng kĩ thuật Heaven's Gate, mã độc sau khi thực hiện xong
hàm f_get_ntdll sẽ
có được edx:eax trỏ tới địa chỉ base của ntdll.dll như hình dưới đây:
Sau
khi có được địa chỉ của ntdll.dll, mã độc sẽ sử dụng các hàm native từ thư viện này để thực hiện công việc
còn lại là inject payload vào tiến trình 64-bit. Danh sách các hàm thường sử dụng
trong kĩ thuật inject bao gồm:
- NtGetContextThread
- NtReadVirtualMemory
- NtUnmapViewOfSection
- NtAllocateVirtualMemory
- NtWriteVirtualMemory
- NtSetContextThread
- NtResumeThread
Kết
quả sau khi thực thi toàn bộ đoạn code trên, mã độc đã inject payload thành công vào tiến
trình notepad.exe 64-bit:
Tài liệu tham khảo:
Tran Trung Kien (aka m4n0w4r)
R&D Center - VinCSS (a member of Vingroup)
No comments:
Post a Comment