---
# System prepended metadata

title: SSDT Rootkit
tags: [Malware Technique]

---

### 1. Giới Thiệu Tổng Quan
:::success
* sd
:::
### 2. Tải SSDT Rootkit Vào Kernel
:::info 
* Đầu tiên, mở IDA lên để xem mã assembly. Bắt đầu hàm main(), ta thấy malware dùng lần lượt 3 API **FindResourceA()**, **LoadResource()**, và **LockResource()**. Hmm, sau một lúc tìm hiểu thì mình biết rằng bộ 3 API này dùng để trích xuất một resource nào đó từ .rsrc section của chính malware. Tiếp theo, nó gọi API **SizeOfResource()** để lấy độ lớn của resource.
* Sau khi trích xuất resource, nó tiếp tục tạo một directory có đường dẫn **C:\hidden** bằng API **CreateDirectoryA()** :grin:. 
:::
![](https://hackmd.io/_uploads/SkjDtNUi3.png)

:::info
* Tiếp theo, nó tạo một file **rootkit.sys** vào directory vừa tạo bằng API **CreateFileA()** và đồng thời ghi nội dung resource vào file này bằng API **WriteFile()**.
* Vậy thì tới đây ta cũng đã phần nào dự đoán được cách hoạt động của con malware này :sunglasses:. Khi được thực thi, nó lấy rootkit từ .rsrc section của chính nó và lưu rootkit này vào file có đường dẫn **C:\hidden\rootkit.sys**.
:::

![](https://hackmd.io/_uploads/SymNqEIon.png)

:::info
* Sau đó, nó đóng các handle không còn được sử dụng nữa và gọi hàm **sub_4010B0**.
:::

![](https://hackmd.io/_uploads/HJH85NUi3.png)
:::info
* Mở đầu sub_4010B0, nó gọi API **OpenSCManagerA()** và nhảy đến vị trí **loc_4010CA**. Huh, khi API này được gọi thì có nghĩa rằng con malware này đang muốn đăng ký một service nào đó :thinking_face: ?
:::

![](https://hackmd.io/_uploads/Bkt9cELjh.png)
:::info
* Đúng như ta dự đoán :grin:, nó tiếp tục gọi API **CreateServiceA()** để đăng ký một service từ file rootkit.sys mà nó vừa tạo ra. 
* Nếu ta chú ý vào các tham số được sử dụng cho API này, thì ta thấy rằng tham số **dwServiceType** có giá trị bằng 1, giá trị này đại diện cho cờ **SERVICE_KERNEL_DRIVER**. Điều này có nghĩa rằng nó đang muốn đăng ký một kernel driver từ file rootkit.sys.
* Sau khi đăng ký driver xong, nó tiếp tục gọi API **StartServiceA()** để bắt đầu chạy driver. Driver được chạy bản chất chính là rootkit :smiling_face_with_smiling_eyes_and_hand_covering_mouth:.
* Vậy thì tới bước này, ta đã thực thi được kernel rootkit. Vậy ta cùng xem nó làm gì ở kernel nhé :grin:.
:::

![](https://hackmd.io/_uploads/rJbhyH8jn.png)
![](https://hackmd.io/_uploads/SkOvPBLj2.png)
### 2. Chức Năng SSDT rootkit
:diamond_shape_with_a_dot_inside: **Chức năng hàm DriverEntry()**
:::info 
* Tiếp theo, ta đi phân tích rootkit để tìm hiểu chức năng của nó :smiling_face_with_smiling_eyes_and_hand_covering_mouth:. Đầu tiên là hàm **DriverEntry()**, hàm này được thực thi đầu tiên khi ta chạy rootkit.
* Bắt đầu hàm **DriverEntry()**, rootkit đăng ký hàm unload cho **DriverObject** của nó. Hàm unload của rootkit được thực thi khi ta gỡ bỏ rootkit ra khỏi hệ thống. Nếu như ta không đăng ký hàm unload cho rootkit thì nó có thể được tải vào kernel nhưng sẽ không thể được gỡ bỏ, trong trường hợp này ta bắt buộc phải reboot máy tính để gỡ nó ra khỏi bộ nhớ :nerd_face:. Chức năng cụ thể của hàm unload sẽ được mình trình bày sau.
* Tiếp theo, DriverEntry() gọi liên tục 3 hàm **DisableWriteProtect()**, **SSDTHooking()**, và **EnableWriteProtect()**. 
:::
![](https://hackmd.io/_uploads/B1ZiVUIin.png)

---

:diamond_shape_with_a_dot_inside: **Chức năng hàm DisableWriteProtect()**
:::info
* **SSDT** được lưu ở kernel trên những page chỉ có quyền đọc (read-only), cho nên trước khi thực hiện quá trình hooking trên nó thì ta phải tắt cơ chế write protect của processor. Nếu tắt cơ chế write protect thì ta sẽ có quyền thay đổi nội dung của page chỉ có quyền đọc. Vậy làm thế nào ta có thể tắt cơ chế write protect :thinking_face: ?
*  Câu trả lời là trong thanh ghi điều khiển 32 bit CR0 của processor có một bit đại diện cho cơ chế write protect. Chỉ cần ta gán giá trị bit này bằng 0 thì cơ chế write protect sẽ bị tắt đi. Đây cũng chính là chức năng của hàm **DisableWriteProtect()**. 
:::
![](https://hackmd.io/_uploads/B1OYrI8sn.png)

---

:diamond_shape_with_a_dot_inside: **Chức năng hàm SSDTHooking()**
:::info
* Khi đã tắt cơ chế write protect, ta cần tìm vị trí của SSDT để thực hiện hooking. Trong kernel, địa chỉ của SSDT được lưu trữ trong trường **ServiceTableBase** của bảng **KeServiceDescriptorTable**.
* Việc bây giờ cần làm là ghi đè giá trị của những entry tương ứng với các API mà ta muốn hook có trong SSDT. Nhưng vấn đề là ta chỉ có địa chỉ của những API mà ta muốn hook thôi chứ không biết rằng entry tương ứng của nó trong SSDT nằm ở đâu :nerd_face:. Huh, vậy thì làm sao ta có thể tìm kiếm index của những entry này :thinking_face: ?
* Câu trả lời là trong kernel, tất cả API có định dạng là **Zw*** đều bắt đầu hàm bằng lệnh **mov eax, ULONG**. Trong đó, giá trị ULONG chính là index của API trong SSDT.
:::

![](https://hackmd.io/_uploads/SybZE_8sn.png)

:::info
* Dựa vào những kiến thức mà mình vừa đề cập ở trên, ta cùng đi tìm hiểu chức năng của hàm **SSDTHooking()**. 
* Đầu tiên, hàm tắt interrupt flag bằng lệnh cli tại địa chỉ **0x11215**, việc này khiến cho processor không xử lý các interrupt được gửi đến nó. Hmm, vậy việc này có ý nghĩa gì trong quá trình SSDT hooking của chúng ta :thinking_face: ?
* Ta phải hiểu rằng các benign program cũng có thể thao tác trên SSDT chứ không riêng gì rootkit :nerd_face:. Cho nên, để hiện tượng tranh chấp tài nguyên không diễn ra thì không được có bất cứ một benign program nào thao tác trên SSDT khi rootkit đang thực hiện hooking trong SSDT. Mặt khác, benign program dựa vào systemcall để thao tác trên SSDT, mà bản chất systemcall lại được thực hiện thông qua cơ chế trap (software interrupt). Cho nên khi rootkit thực hiện SSDT hooking thì bắt buộc không được có bất cứ một interrupt nào được xử lý bởi processor, bởi vì chức năng của những interrupt này có thể được kích hoạt từ các benign program có mục đích thao tác vào SSDT. Đây cũng chính là lí do vì sao rootkit phải dùng lệnh cli khi bắt đầu thực hiện việc hooking trong SSDT :kissing:. 
* Nhìn sơ vào source code, mình thấy rằng nó đề cập đến 2 API là **ZwCreateFile()** và **ZwQueryDirectoryFile()**. Hmm, việc này rất có thể chức năng của rootkit là hook 2 API này. Tiếp tục phân tích thôi.
* Tại địa chỉ **0x1121B**, lệnh **mov ecx, [eax + 1]** được thực thi. Giá trị eax hiện tại đang lưu địa chỉ của API ZwCreateFile() cho nên eax + 1 chính là địa chỉ của giá trị ULONG. Chức năng lệnh này là truy xuất index của API ZwCreateFile() trong SSDT và lưu vào ecx. 
* Sau khi có được index, ta tiếp tục tìm địa chỉ của SSDT thông qua cấu trúc KeServiceDescriptorTable. Địa chỉ SSDT được lưu trong thanh ghi eax khi thực thi lệnh tại địa chỉ **0x11224**.
* Trước khi hook API ZwCreateFile(), ta cần lưu lại địa chỉ của nó vào biến saveZwCreateFile nhằm mục đích unhooking SSDT sau khi rootkit được gỡ bỏ khỏi bộ nhớ :grin:. 
* Bây giờ thì ta hook API ZwCreateFile() bằng cách ghi địa chỉ của hàm fakeZwCreateFile() vào entry tương ứng với API ZwCreateFile(). Việc này được thực hiện bằng lệnh tại địa chỉ **0x11240**. Quá trình hook API ZwQueryDirectoryFile() cũng diễn ra tương tự nên mình sẽ không đề cập ở đây :face_with_hand_over_mouth:. 
:::

![](https://hackmd.io/_uploads/Skd8rIIo2.png)

---

:diamond_shape_with_a_dot_inside: **Chức năng hàm fakeZwCreateFile()**
:::info
* Chức năng hàm **fakeZwCreateFile()** khá đơn giản, nó không cho phép user truy cập vào bất cứ file nào thuộc directory C:\hidden. Việc này được thực hiện bằng cách sử dụng API **RtlCompareUnicodeString()** để kiểm tra đường đẫn của file mà ta muốn truy cập có bắt đầu bằng chuỗi unicode **"\\??\C:\hidden"** hay không ? Nếu như kết quả trả về khác 0 thì file mà ta muốn truy cập thông qua API ZwCreateFile() không thuộc directory C:\hidden, cho nên fakeZwCreateFile() sẽ cho phép user truy cập vào file này. Ngược lại, fakeZwCreateFile() sẽ trả về giá trị **0xC0000033**, giá trị này tương ứng với mã lỗi **STATUS_OBJECT_NAME_NOT_FOUND**, nó mang nghĩa là hàm gặp lỗi trong quá trình thực hiện và không thể tạo hoặc mở tập tin được yêu cầu :grin:.
:::

![](https://hackmd.io/_uploads/S1YoB_Loh.png)

---

:diamond_shape_with_a_dot_inside: **Chức năng hàm fakeZwQueryDirectoryFile()**
:::info
* Mục đích của fakeZwQueryDirectoryFile() và fakeZwCreateFile() là giống nhau nhưng cách thức thực hiện lại khác nhau. Hàm fakeZwQueryDirectoryFile() trích xuất đường dẫn của directory bằng API **ZwQueryInformationFile()**. Sau đó dùng hàm **CheckName()** để điểm tra đường dẫn này có bắt đầu bởi chuỗi "\secret" hay không ? Nếu đường dẫn bắt đầu bởi chuỗi "\secret" thì là như vậy thì hàm trả về giá trị **0xC0000005**, giá trị tương ứng với mã lỗi **STATUS_ACCESS_VIOLATION**, nó mang ý nghĩa là hàm đã gặp một vấn đề với việc truy cập bộ nhớ, ví dụ như việc truy cập một địa chỉ không hợp lệ hoặc không được phép. Ngược lại thì fakeZwQueryDirectoryFile() gọi API ZwQueryDirectoryFile() gốc để trả về thông tin cho user.
:::

![](https://hackmd.io/_uploads/HJzxWd8oh.png)

:::info
* Dưới đây là thông tin chi tiết của API **ZwQueryInformationFile()**, thông tin này được mình tìm hiểu thông qua trang microsoft chính thức :grin:. Chức năng của API ZwQueryInformationFile() là truy xuất thông tin cụ thể nào đó của một file dựa vào giá trị của trường **FileInformationClass**. 
:::

![](https://hackmd.io/_uploads/SkfZP_vo2.png)

:::info
* Bởi vì chúng ta muốn truy xuất dường dẫn của directory nên giá trị của trường FileInformationClass phải bằng 9.
:::

![](https://hackmd.io/_uploads/S1tEPdwo2.png)

:::info
* Khi thực hiện API ZwQueryInformationFile() xong, thì trường **FileInformation** được trỏ đến một đối tượng được biểu diễn bởi cấu trúc **FILE_NAME_INFORMATION**. Lúc này, ta chỉ cần trích xuất đường dẫn của directory thông qua trường **FileName** của cấu trúc này là xong :smiling_imp:. À mà chưa xong :smiling_face_with_smiling_eyes_and_hand_covering_mouth:, ta cần lưu ý là trường FileName được biểu diễn bởi chuỗi kí tự unicode chứ không phải ascii nhoa :grin:.
:::

![](https://hackmd.io/_uploads/SknGwuwon.png)

:::info
* Và đây là code của hàm **CheckName()** mà mình đã đề cập ở trên.
:::

![](https://hackmd.io/_uploads/HkLLbuIon.png)
:::info
* Tới thời điểm này, rootkit đã hooking SSDT thành công. Nên nó phải bật interrupt flag lại bằng lệnh sti để processor có thể xử lý các interrupt. 
:::

:diamond_shape_with_a_dot_inside: **Chức năng hàm EnableWriteProtect()**
:::info
* Sau khi thực hiện xong hàm SSDTHooking(), rootkit phải có nghĩa vụ bật lại cơ chế write protect bằng hàm **EnableWriteProtect()**. Cách triển khai cụ thể khá đơn giản nên mình sẽ không giải thích chi tiết, các bạn có thể đọc code ở dưới để tự thẩm :smiling_face_with_smiling_eyes_and_hand_covering_mouth:.
:::
![](https://hackmd.io/_uploads/HyPyUIIs3.png)

---

:diamond_shape_with_a_dot_inside: **Chức năng hàm OnUnload()**
:::info
* Oce, bây giờ thì ta cùng phân tích chức năng hàm unload của rootkit. Tại địa chỉ **0x112D2**, ta thấy hàm unload lại gọi một hàm khác có tên là **Unhooking()**.
:::
![](https://hackmd.io/_uploads/ByBHjO8sh.png)

:::info
* Chức năng của hàm Unhooking() thì ngược lại so với hàm SSDTHooking(), nó unhook hai entry tương ứng với 2 API ZwCreateFile() và ZwQueryDirectoryFile() trong SSDT bằng giá trị saveZwCreateFile và saveZwQueryDirectoryFile.
* Trước khi thực hiện unhooking thì rootkit cũng phải tắt interrupt flag và sau khi thực hiện xong quá trình này thì rootkit cũng phải bật lại interrupt flag.
:::

![](https://hackmd.io/_uploads/BJhghuUsh.png)
### 3. Triển Khai Cụ Thể
:::info
* Khi bắt đầu thực thi malware bằng quyền admin, nó hiển thị thông báo thách thức user rằng hãy mở thư mục tại đường dẫn **C:\hidden** xem được không ?
:::
![](https://hackmd.io/_uploads/HJqpwu8oh.png)
:::info
* Sau khi thực thi malware, mình dùng tool **DriverView** để kiểm tra các kernel module được tải lên trong hệ thống thì thấy có driver **rootkit.sys**. Vậy là rootkit đã được cài thành công trên hệ thống.
:::
![](https://hackmd.io/_uploads/r1moD_Ii2.png)

:::info
* Mình thử mở thư mục C:\hidden thì không được :grin:. Vậy là quá trình hooking API **ZwCreateFile()** đã thành công.
:::
![](https://hackmd.io/_uploads/SJd8sIIoh.png)
:::info
* Tiếp theo mình khởi tạo một directory có tên là secret tại C drive và cố gắng mở nó thì kết quả cũng không thành công. Tương tự, vậy là quá trình hooking API **ZwQueryDirectoryFile()** cũng diễn ra thành công :sunglasses:.
:::
![](https://hackmd.io/_uploads/HkeBRvLin.png)
### 4. Tham Khảo
* https://github.com/Apress/malware-analysis-detection-engineering/blob/master/samples_all_malware_analysis_and_detection_engineering/chapter_11/Sample-11-7-ssdt-rootkit


















