fork on WINDOWS
Introduction
Winnie-AFL is a fork of WinAFL that supports fuzzing using a fork()-like API.
笔者对 CSRSS 了解较少,目前没有能力分析到关键的部分,随意贴贴代码,以后有机会再来补充
Methodology
Fork 确实存在于 Windows 系统上,但现有工作未能提供稳定的实现。
我们的 Windows fork 实现纠正了与 CSRSS 相关的问题,CSRSS 是控制 Windows 环境底层的用户模式进程,这个守护进程负责分配控制台窗口和关闭进程。如果进程未连接到 CSRSS,则在尝试访问 Win32 API时会崩溃。
对于分叉进程来说,连接到CSRSS并非易事:为了使调用成功,我们必须在子进程连接之前手动取消初始化几个未记录的变量。
- 父进程使用适当的标志调用 NtCreateUserProcess,创建一个挂起的子进程,其中包含父地址空间的 CoW 副本(第 1 行)
- 我们保持子进程挂起,直到父进程调用 CsrClientCallServer 通知 CSRSS 一个新进程已创建(第 12 行)。
- 父级现在恢复子级,子级继续进行自我初始化(第 17 行)。 然后,父进程从 fork 中返回(第 14 行)。
- 在子进程中,由于地址空间与父进程匹配,因此已经设置了几个对于新进程为零的全局变量(例如,ntdll.dll 中的 CsrServerApiRoutine)。 孩子必须通过将它们归零(第 18 行)手动取消初始化它们,以避免在下一步中崩溃。
- 现在,孩子通过调用 CsrClientConnectToServer(第 20 行)连接到 CSRSS。 此步骤对于子进程正常运行至关重要。
- CSRSS 最终确认新创建的进程和线程,子进程从 fork 返回(第21行)。
Implementation
代理首先钩住 harness(target program)中的关键 function,一旦应用程序到达钩子,它就会停止,模糊代理会启动fork服务器。
forkserver
-
afl/forkserver.c: 拉起子进程并注入 agent.dll
CLIENT_ID spawn_child_with_injection(char* cmd, INJECTION_MODE injection_type, uint32_t timeout, uint32_t init_timeout) { // 以 CREATE_SUSPENDED 创建子进程,现在还不能注入,需要等进程完成初始化,所以之后会在其到达 entrypoint 之后注入 start_process(cmd); CONTEXT context; context.ContextFlags = CONTEXT_INTEGER; GetThreadContext(child_thread_handle, &context); uintptr_t pebAddr; #ifdef _WIN64 // peb 地址初始保存在 rdx 中 pebAddr = context.Rdx; ReadProcessMemory(child_handle, (PVOID)(pebAddr + 0x10), &base_address, sizeof(base_address), NULL); #else pebAddr = context.Ebx; ReadProcessMemory(child_handle, (PVOID)(pebAddr + 8), &base_address, sizeof(base_address), NULL); #endif debug_printf(" PEB=0x%p, Base address=0x%p\n", pebAddr, base_address); // 解析 pe 文件,获得 EP uintptr_t oep = get_entry_point(binary_name); debug_printf(" Binname: %s, OEP: %p\n", binary_name, oep); uintptr_t pEntryPoint = oep + base_address; if (!pEntryPoint) { dank_perror("GetEntryPoint"); } debug_printf(" Entrypoint = %p\n", pEntryPoint); // 把 EP patch 为无限循环 DWORD dwOldProtect; VirtualProtectEx(child_handle, (PVOID)pEntryPoint, 2, PAGE_EXECUTE_READWRITE, &dwOldProtect); BYTE oepBytes[2]; ReadProcessMemory(child_handle, (PVOID)pEntryPoint, oepBytes, 2, NULL); WriteProcessMemory(child_handle, (PVOID)pEntryPoint, "\xEB\xFE", 2, NULL); FlushInstructionCache(child_handle, (PVOID)pEntryPoint, 2); ResumeThread(child_thread_handle); // 等到程序执行到 EP for (int i = 0; context.INSTRUCTION_POINTER != pEntryPoint; Sleep(100)) { if (++i > 50) { FATAL("Entrypoint trap trimed out: the forkserver injection failed, or the target process never reached its entrypoint.\n"); } context.ContextFlags = CONTEXT_CONTROL; GetThreadContext(child_thread_handle, &context); } debug_printf(" Entrypoint trap hit, injecting the dll now!\n"); SuspendThread(child_thread_handle); // 通过 pid 来构建管道名称 DWORD pid = GetProcessId(child_handle); debug_printf(" PID is %d\n", pid); afl_pipe = alloc_printf(AFL_FORKSERVER_PIPE "-%d", pid); debug_printf(" Pipe name: %s\n", afl_pipe); // CreateRemoteThread 将 agent 注入到目标 char* injectedDll = FORKSERVER_DLL; char szDllFilename[MAX_PATH]; GetModuleFileNameA(NULL, szDllFilename, sizeof(szDllFilename)); PathRemoveFileSpecA(szDllFilename); strncat(szDllFilename, "\\", max(0, MAX_PATH - strlen(szDllFilename) - 1)); strncat(szDllFilename, injectedDll, max(0, MAX_PATH - strlen(szDllFilename) - 1)); debug_printf(" Injecting %s\n", szDllFilename); hModule = InjectDll(child_handle, szDllFilename); if (!hModule) { FATAL("InjectDll"); } debug_printf(" Forkserver dll injected, base address = %p\n", hModule); // 很长一段都是把参数信息传递给 agent HANDLE hMapping = INVALID_HANDLE_VALUE, hFile = INVALID_HANDLE_VALUE; BYTE* lpBase = NULL; PIMAGE_NT_HEADERS ntHeader = map_pe_file(szDllFilename, (LPVOID*)&lpBase, &hMapping, &hFile); if (!ntHeader) FATAL("Failed to parse PE header of %s", injectedDll); DWORD off_fuzzer_settings = get_proc_offset((char*)lpBase, "fuzzer_settings"); DWORD off_forkserver_state = get_proc_offset((char*)lpBase, "forkserver_state"); DWORD off_call_target = get_proc_offset((char*)lpBase, "call_target"); if (!off_fuzzer_settings || !off_call_target) FATAL("Fail to locate forkserver exports!\n"); debug_printf(" fuzzer_settings offset = %08x, call_target offset = %08x\n", off_fuzzer_settings, off_call_target); size_t nWritten; pFuzzer_settings = (LPVOID)((uintptr_t)hModule + off_fuzzer_settings); pForkserver_state = (LPVOID)((uintptr_t)hModule + off_forkserver_state); pCall_offset = (LPVOID)((uintptr_t)hModule + off_call_target); debug_printf(" fuzzer_settings = %p, forkserver_state = %p, call target = %p\n", pFuzzer_settings, pForkserver_state, pCall_offset); LPVOID pCovInfo; if (use_fullspeed) // Fullspeed mode { LPVOID pModuleNames; { size_t module_names_size; cov_modules_list module_names = serialize_coverage_modules(&module_names_size); pModuleNames = VirtualAllocEx(child_handle, NULL, module_names_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (!pModuleNames) { dank_perror("Allocating coverage modules list into child"); } if (!WriteProcessMemory(child_handle, pModuleNames, module_names, module_names_size, &nWritten) || nWritten < module_names_size) { dank_perror("Writing coverage modules list into child"); } free(module_names); } size_t cov_info_size; AFL_COVERAGE_INFO* cov_info = serialize_breakpoints(pModuleNames, &cov_info_size); pCovInfo = VirtualAllocEx(child_handle, NULL, cov_info_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (!pCovInfo) { dank_perror("Allocating basic blocks list into child"); } if (!WriteProcessMemory(child_handle, pCovInfo, cov_info, cov_info_size, &nWritten) || nWritten < cov_info_size) { dank_perror("Writing basic blocks list into child"); } free(cov_info); } else if (use_intelpt) { // Intelpt mode uses external tracing for coverage pCovInfo = NULL; } else { FATAL("Unsupported coverage mode"); } AFL_SETTINGS fuzzer_settings; strncpy(fuzzer_settings.harness_name, options.fuzz_harness, sizeof(fuzzer_settings.harness_name)); strncpy(fuzzer_settings.minidump_path, options.minidump_path, sizeof(fuzzer_settings.minidump_path)); fuzzer_settings.timeout = timeout; fuzzer_settings.mode = injection_type; fuzzer_settings.cov_info = pCovInfo; fuzzer_settings.enableWER = options.enable_wer; fuzzer_settings.cpuAffinityMask = cpu_aff; fuzzer_settings.debug = options.debug_mode; if (!WriteProcessMemory(child_handle, pFuzzer_settings, &fuzzer_settings, sizeof(AFL_SETTINGS), &nWritten) || nWritten < sizeof(AFL_SETTINGS)) { dank_perror("Writing fuzzer settings into child"); } if (lpBase) UnmapViewOfFile((LPCVOID)lpBase); if (hMapping != INVALID_HANDLE_VALUE) CloseHandle(hMapping); if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); // 参数复制完毕,告诉 agent 完成 forkserver 初始化 FORKSERVER_STATE ready = FORKSERVER_READY; if (!WriteProcessMemory(child_handle, pForkserver_state, &ready, sizeof(FORKSERVER_STATE), &nWritten) || nWritten < sizeof(FORKSERVER_STATE)) { dank_perror("Writing fuzzer settings into child"); } // afl_pipe 会在 agent 完成初始化后创建,等待 agent 完成初始化 debug_printf("Connecting to forkserver...\n"); DWORD timeElapsed = 0; do { hPipeChild = CreateFileA(afl_pipe, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hPipeChild == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_FILE_NOT_FOUND) { Sleep(10); timeElapsed += 10; if (timeElapsed > init_timeout) { FATAL("Forkserver failed to initialize!\n"); } continue; } dank_perror("CreateFileA"); } } while (hPipeChild == INVALID_HANDLE_VALUE); DWORD dwMode = PIPE_READMODE_MESSAGE; if (!SetNamedPipeHandleState(hPipeChild, &dwMode, NULL, NULL)) { dank_perror("SetNamedPipeHandleState"); } debug_printf("Connected to forkserver\n"); debug_printf("Ok, the forkserver is ready. Resuming the main thread now.\n"); debug_printf("Entrypoint: %p | OEP stolen bytes: %02x %02x\n", pEntryPoint, oepBytes[0], oepBytes[1]); // 恢复入口的原始字节,让目标得以开始执行 MEMORY_BASIC_INFORMATION memInfo; VirtualQueryEx(child_handle, (PVOID)pEntryPoint, &memInfo, sizeof(memInfo)); if (memInfo.Protect & PAGE_GUARD) { VirtualProtectEx(child_handle, (PVOID)pEntryPoint, 2, PAGE_EXECUTE_READWRITE, &dwOldProtect); debug_printf("VirtualProtectEx : temporarily removed guard page on entrypoint\n"); } WriteProcessMemory(child_handle, (PVOID)pEntryPoint, oepBytes, 2, NULL); FlushInstructionCache(child_handle, (PVOID)pEntryPoint, 2); DWORD trash; VirtualProtectEx(child_handle, (PVOID)pEntryPoint, 2, dwOldProtect, &trash); return (CLIENT_ID){ child_handle, child_thread_handle }; }
-
injected-harness/dllmain.cpp 在目标侧管理模糊进程和 forkserver 的 agent
首先初始化部分加载了用户提供的 harness.dll,这个 dll 主要是确定 forkserver 开始的地址。然后给这个地址设置 PAGE_GUARD,程序到达目标地址之后再跳转到初始化程序。
DWORD CALLBACK cbThreadStart(LPVOID hModule) { // Create a console for printf AllocConsole(); fuzzer_stdout = fopen("CONOUT$", "w+"); fuzzer_stdin = fopen("CONIN$", "r"); setvbuf(fuzzer_stdout, NULL, _IONBF, 0); setvbuf(fuzzer_stdin, NULL, _IONBF, 0); SetConsoleTitleA("Winnie -- Forkserver"); // 等待 afl 侧把参数拷贝过来 while (forkserver_state == FORKSERVER_NOT_READY) Sleep(10); MemoryBarrier(); switch(fuzzer_settings.mode) { case DRYRUN: report_coverage = afl_report_coverage; report_crashed = afl_report_crashed; report_end = afl_report_end; fuzzer_printf("Forkserver loaded - dryrun mode\n"); break; case FORK: report_coverage = fork_report_coverage; report_crashed = fork_report_crashed; report_end = fork_report_end; fuzzer_printf("Forkserver loaded - forkserver mode\n"); break; case PERSISTENT: report_coverage = afl_report_coverage; report_crashed = afl_report_crashed; report_end = persistent_report_end; fuzzer_printf("Forkserver loaded - persistent mode\n"); break; default: FATAL("Invalid fuzzer mode"); } // Get the name of pipe/event DWORD pid = GetCurrentProcessId(); fuzzer_printf("Forkserver PID: %d\n", pid); snprintf(afl_pipe, sizeof(afl_pipe), AFL_FORKSERVER_PIPE "-%d", pid); debug_printf("afl_pipe: %s\n", afl_pipe); SYSTEM_INFO sys_info = { 0 }; GetSystemInfo(&sys_info); DWORD cpu_core_count = sys_info.dwNumberOfProcessors; if (fuzzer_settings.mode == FORK) { snprintf(forkserver_child_pipe, sizeof(forkserver_child_pipe), "\\\\.\\pipe\\forkserver-children-%d", pid); childCpuAffinityMask = ~fuzzer_settings.cpuAffinityMask & ((1ULL << cpu_core_count) - 1ULL); } fuzzer_printf("Timeout: %dms\n", fuzzer_settings.timeout); fuzzer_printf("Minidumps (WER): %s\n", fuzzer_settings.enableWER ? "enabled" : "disabled"); fuzzer_printf("Processor affinity: 0x%llx (%d cores)\n", fuzzer_settings.cpuAffinityMask, cpu_core_count); if (fuzzer_settings.enableWER) { fuzzer_printf("Will look for minidumps in %s\n", fuzzer_settings.minidump_path); } if (!SetProcessAffinityMask(GetCurrentProcess(), fuzzer_settings.cpuAffinityMask)) { FATAL("Failed to set process affinity"); } // 装载用户的 harness.dll,内部会填充 target_method 和 fuzz_iter_func fuzzer_printf("Loading harness: %s\n", fuzzer_settings.harness_name); hHarness = LoadLibraryA((LPSTR) fuzzer_settings.harness_name); if (!hHarness) { FATAL("Failed to load harness"); } harness_info = (PHARNESS_INFO) GetProcAddress(hHarness, HARNESS_INFO_PROC); if (!harness_info) { FATAL("Missing harness info block!"); } fuzzer_printf("Waiting for the harness...\n"); // 等待 harnees.dll 执行完毕 while (!(harness_info->ready)) Sleep(10); MemoryBarrier(); target_address = (BYTE*)harness_info->target_method; fuzz_iter_address = harness_info->fuzz_iter_func; fuzzer_printf("Target address: 0x%p | Iter address: 0x%p\n", target_address, fuzz_iter_address); // Network fuzzing mode if (harness_info->network == TRUE) { // ... } // 用 PAGE_GUARD 保护目标地址,从而可以捕获执行到目标地址 MEMORY_BASIC_INFORMATION targetPageInfo; DWORD dwOldProtect; VirtualQuery(target_address, &targetPageInfo, sizeof(targetPageInfo)); VirtualProtect(target_address, 1, targetPageInfo.Protect | PAGE_GUARD, &dwOldProtect); // get NtCreateFile address pCreateFile = (LPVOID)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateFile"); // get TerminateProcess address pTerminateProcess = (LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "TerminateProcess"); pRtlExitUserProcess = (LPVOID)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlExitUserProcess"); pNtProtectVirtualMemory = (LPVOID)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtProtectVirtualMemory"); pRtlAddVectoredExceptionHandler = (LPVOID)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlAddVectoredExceptionHandler"); // 在 SuperEarlyExceptionHandler 中捕获 target address 是否到达 GetSystemInfo(&systemInfo); // get the page size superEarlyHandler = AddVectoredExceptionHandler(TRUE, SuperEarlyExceptionHandler); // 一些为了保证成功捕获 target address 的细节 fuzzer_printf("Early hooking critical functions...\n"); InlineHook(pNtProtectVirtualMemory, MyNtProtectVirtualMemory, (PVOID*)& pOrgNtProtectVirtualMemory, THUNK_SIZE); InlineHook(pRtlAddVectoredExceptionHandler, MyRtlAddVectoredExceptionHandler, (PVOID*)& pOrgRtlAddVectoredExceptionHandler, THUNK_SIZE); fuzzer_printf("-> OK!\n"); // 告诉 afl 准备好了,让其恢复程序执行 hPipeAfl = CreateNamedPipeA(afl_pipe, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, 4096, 4096, 0, NULL); if (hPipeAfl == INVALID_HANDLE_VALUE) { FATAL("CreateNamedPipe"); } fuzzer_printf("Connecting to AFL and returning control to main binary!\n"); fflush(fuzzer_stdout); if (!ConnectNamedPipe(hPipeAfl, NULL) && GetLastError() != ERROR_PIPE_CONNECTED) // This will block! { FATAL("ConnectNamedPipe"); } return 0; }
程序执行到 target address 后会跳转到 harness_main() 执行,调用 forkserver() 完成 forkserver 的创建。forkserver() 内部也是一个循环,不断接受 afl 的命令,完成 fork 子进程使用样例执行的过程。
_declspec(noreturn) void forkserver() { SetupChildPipe(); fuzzer_printf("Okay, spinning up the forkserver now.\n"); // forkserver int forkCount = 0; int done = false; PROCESS_INFORMATION curChildInfo = {0}; int childPending = 0; while (!done) { AFL_FORKSERVER_REQUEST aflRequest; DWORD nRead; if (!ReadFile(hPipeAfl, &aflRequest, sizeof(aflRequest), &nRead, NULL) || nRead != sizeof(aflRequest)) { FATAL("Broken AFL pipe, ReadFile (forkserver)"); } switch (aflRequest.Operation) { case AFL_CREATE_NEW_CHILD: { trace_printf("Fuzzer asked me to create new child\n"); if (childPending) { FATAL("Invalid request; a forked child is already standby for execution"); } forkCount++; curChildInfo = do_fork(); AFL_FORKSERVER_RESULT aflResponse; aflResponse.StatusCode = AFL_CHILD_CREATED; aflResponse.ChildInfo.ProcessId = curChildInfo.dwProcessId; aflResponse.ChildInfo.ThreadId = curChildInfo.dwThreadId; DWORD nWritten; if (!WriteFile(hPipeAfl, &aflResponse, sizeof(aflResponse), &nWritten, NULL) || nWritten != sizeof(aflResponse)) { FATAL("Broken AFL pipe, WriteFile"); } childPending = 1; break; } case AFL_RESUME_CHILD: { if (!childPending) { FATAL("Invalid request; no forked child to resume"); } trace_printf("Fuzzer asked me to resume the child\n"); // Wait for the forked child to suspend itself, then we will resume it. (In order to synchronize) while (1) { DWORD exitCode = 0; // If the fork fails somehow, the child will unexpectedly die without suspending itself. if (!GetExitCodeProcess(curChildInfo.hProcess, &exitCode) || exitCode != STILL_ACTIVE) { fuzzer_printf("The forked child died before we resumed it! Exit code: %d\n", exitCode); suicide(); } DWORD dwWaitResult = WaitForSingleObject(curChildInfo.hThread, 0); if (dwWaitResult == WAIT_OBJECT_0) { // Thread object is signaled -- thread died fuzzer_printf("The forked child thread died before we resumed it!\n"); suicide(); } DWORD dwResult = ResumeThread(curChildInfo.hThread); if (dwResult == (DWORD)-1) FATAL("Failed to resume the child"); if (dwResult == 0) { // Hasn't suspended itself yet Sleep(1); continue; } else if (dwResult == 1) break; else FATAL("Unexpected suspend count %d", dwResult); } AFL_FORKSERVER_RESULT aflResponse; CHILD_FATE childStatus = do_parent(curChildInfo); // return child's status from parent. CloseHandle(curChildInfo.hProcess); CloseHandle(curChildInfo.hThread); RtlZeroMemory(&curChildInfo, sizeof(curChildInfo)); switch (childStatus) { case CHILD_SUCCESS: aflResponse.StatusCode = AFL_CHILD_SUCCESS; break; case CHILD_CRASHED: aflResponse.StatusCode = AFL_CHILD_CRASHED; break; case CHILD_TIMEOUT: aflResponse.StatusCode = AFL_CHILD_TIMEOUT; break; default: FATAL("Child exited in an unexpected way?"); } DWORD nWritten; if (!WriteFile(hPipeAfl, &aflResponse, sizeof(aflResponse), &nWritten, NULL) || nWritten != sizeof(aflResponse)) { FATAL("Broken AFL pipe, WriteFile"); } childPending = 0; break; } case AFL_TERMINATE_FORKSERVER: debug_printf("Fuzzer asked me to kill the forkserver\n"); done = true; break; } } DisconnectNamedPipe(hPipeChild); DisconnectNamedPipe(hPipeAfl); CloseHandle(hPipeAfl); CloseHandle(hPipeChild); fuzzer_printf("Bye.\n"); suicide(); }
-
forklib/fork.cpp: fork 调用的实现部分
对 CSRSS 不太了解,以后再来补充,现在就感受代码吧。
extern "C" DWORD fork(LPPROCESS_INFORMATION lpProcessInformation) { printf("FORKLIB: Before the fork, my pid is %d\n", GetProcessId(GetCurrentProcess())); PS_CREATE_INFO procInfo; RtlZeroMemory(&procInfo, sizeof(procInfo)); HANDLE hProcess = NULL; HANDLE hThread = NULL; procInfo.Size = sizeof(PS_CREATE_INFO); #ifndef _WIN64 // WTF???? Discard *BIZARRE* segfault in ntdll from read fs:[0x18] that you can ignore??? LPTOP_LEVEL_EXCEPTION_FILTER oldFilter = SetUnhandledExceptionFilter(DiscardException); #endif // This is the part that actually does the forking. Everything else is just // to clean up after the mess that's created afterwards NTSTATUS result = NtCreateUserProcess( &hProcess, &hThread, MAXIMUM_ALLOWED, MAXIMUM_ALLOWED, NULL, NULL, PROCESS_CREATE_FLAGS_INHERIT_FROM_PARENT | PROCESS_CREATE_FLAGS_INHERIT_HANDLES, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, NULL, &procInfo, NULL); #ifndef _WIN64 // Clear the exception handler installed earlier. SetUnhandledExceptionFilter(oldFilter); #endif if (!result) { // Parent process printf("FORKLIB: I'm the parent\n"); printf("FORKLIB: hThread = %p, hProcess = %p\n", hThread, hProcess); printf("FORKLIB: Thread ID = %x\n", GetThreadId(hThread)); printf("FORKLIB: Result = %d\n", result); // Not needed?? if (!NotifyCsrssParent(hProcess, hThread)) { printf("FORKLIB: NotifyCsrssParent failed\n"); TerminateProcess(hProcess, 1); return -1; } if (lpProcessInformation) { lpProcessInformation->hProcess = hProcess; lpProcessInformation->hThread = hThread; lpProcessInformation->dwProcessId = GetProcessId(hProcess); lpProcessInformation->dwThreadId = GetThreadId(hThread); } ResumeThread(hThread); // allow the child to connect to Csr. return GetProcessId(hProcess); } else { // Child process FreeConsole(); // Remove these calls to improve performance, at the cost of losing stdio. #ifdef _DEBUG AllocConsole(); SetStdHandle(STD_INPUT_HANDLE, stdin); SetStdHandle(STD_OUTPUT_HANDLE, stdout); SetStdHandle(STD_ERROR_HANDLE, stderr); #endif printf("I'm the child\n"); if (!ConnectCsrChild()) { DebugBreak(); ExitProcess(1); } #ifdef _DEBUG // Not safe to do fopen until after ConnectCsrChild ReopenStdioHandles(); #endif return 0; } }
BOOL NotifyCsrssParent(HANDLE hProcess, HANDLE hThread) { PROCESS_BASIC_INFORMATION info; if (!NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessBasicInformation, &info, sizeof(info), 0))) { printf("FORKLIB: NtQueryInformationProcess failed!\n"); return FALSE; } BOOL bIsWow64; if (!IsWow64Process(GetCurrentProcess(), &bIsWow64)) { printf("FORKLIB: IsWow64Process failed!\n"); return FALSE; } NTSTATUS result; if (bIsWow64) { CSR_API_MSG64 csrmsg; RtlZeroMemory(&csrmsg, sizeof(csrmsg)); csrmsg.CreateProcessRequest.PebAddressNative = (ULONGLONG)info.PebBaseAddress; csrmsg.CreateProcessRequest.ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL; csrmsg.CreateProcessRequest.ProcessHandle = (ULONGLONG)hProcess; csrmsg.CreateProcessRequest.ThreadHandle = (ULONGLONG)hThread; csrmsg.CreateProcessRequest.ClientId.UniqueProcess = GetProcessId(hProcess); csrmsg.CreateProcessRequest.ClientId.UniqueThread = GetThreadId(hThread); //result = CsrClientCallServer64(&csrmsg, NULL, CSR_MAKE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCreateProcess), sizeof(csrmsg.CreateProcessRequest)); } else { CSR_API_MSG csrmsg; RtlZeroMemory(&csrmsg, sizeof(csrmsg)); csrmsg.CreateProcessRequest.PebAddressNative = info.PebBaseAddress; #ifdef _WIN64 csrmsg.CreateProcessRequest.ProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64; #else csrmsg.CreateProcessRequest.ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL; #endif csrmsg.CreateProcessRequest.ProcessHandle = hProcess; csrmsg.CreateProcessRequest.ThreadHandle = hThread; csrmsg.CreateProcessRequest.ClientId.UniqueProcess = (HANDLE)GetProcessId(hProcess); csrmsg.CreateProcessRequest.ClientId.UniqueThread = (HANDLE)GetThreadId(hThread); //result = CsrClientCallServer(&csrmsg, NULL, CSR_MAKE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCreateProcess), sizeof(csrmsg.CreateProcessRequest)); } /* if (!NT_SUCCESS(result)) { printf("CsrClientCallServer(BasepCreateThread) failed!\n"); return FALSE; } */ printf("FORKLIB: Successfully notified Csr of child!\n"); return TRUE; }
BOOL ConnectCsrChild() { BOOL bIsWow64; if (!IsWow64Process(GetCurrentProcess(), &bIsWow64)) { printf("FORKLIB: IsWow64Process failed!\n"); return FALSE; } // Zero Csr fields??? // Required or else Csr calls will crash printf("FORKLIB: De-initialize ntdll csr data\n"); HMODULE ntdll = GetModuleHandleA("ntdll.dll"); #ifdef _WIN64 void* pCsrData = (void*)((uintptr_t)ntdll + csrDataRva_x64); // HARDCODED OFFSET, see csrss_offsets.h printf("FORKLIB: Csr data = %p\n", pCsrData); memset(pCsrData, 0, csrDataSize_x64); #else void* pCsrData = (void*)((uintptr_t)ntdll + csrDataRva_x86); // HARDCODED OFFSET, see csrss_offsets.h printf("FORKLIB: Csr data = %p\n", pCsrData); memset(pCsrData, 0, csrDataSize_x86); if (bIsWow64) { DWORD64 ntdll64 = GetModuleHandle64(L"ntdll.dll"); printf("FORKLIB: ntdll 64 = %llx\n", ntdll64); char mem[csrDataSize_wow64]; memset(mem, 0, sizeof(mem)); DWORD64 pCsrData64 = ntdll64 + csrDataRva_wow64; // HARDCODED OFFSET, see csrss_offsets.h printf("FORKLIB: Csr data 64 = %llx\n", ntdll64); setMem64(pCsrData64, mem, sizeof(mem)); } #endif DWORD session_id; wchar_t ObjectDirectory[100]; ProcessIdToSessionId(GetProcessId(GetCurrentProcess()), &session_id); swprintf(ObjectDirectory, 100, L"\\Sessions\\%d\\Windows", session_id); printf("FORKLIB: Session_id: %d\n", session_id); // Not required? printf("FORKLIB: Link Console subsystem...\n"); void* pCtrlRoutine = (void*)GetProcAddress(GetModuleHandleA("kernelbase"), "CtrlRoutine"); BOOLEAN trash; //if (!NT_SUCCESS(CsrClientConnectToServer(L"\\Sessions\\" CSRSS_SESSIONID L"\\Windows", 1, &pCtrlRoutine, 8, &trash))) if (!NT_SUCCESS(CsrClientConnectToServer(ObjectDirectory, 1, &pCtrlRoutine, 8, &trash))) { printf("FORKLIB: CsrClientConnectToServer failed!\n"); return FALSE; } printf("FORKLIB: Link Windows subsystem...\n"); // passing &gfServerProcess is not necessary, actually? passing &trash is okay? char buf[0x240]; // this seem to just be all zero everytime? memset(buf, 0, sizeof(buf)); //if (!NT_SUCCESS(CsrClientConnectToServer(L"\\Sessions\\" CSRSS_SESSIONID L"\\Windows", 3, buf, 0x240, &trash))) if (!NT_SUCCESS(CsrClientConnectToServer(ObjectDirectory, 3, buf, 0x240, &trash))) { printf("FORKLIB: CsrClientConnectToServer failed!\n"); return FALSE; } printf("FORKLIB: Connect to Csr...\n"); if (!NT_SUCCESS(RtlRegisterThreadWithCsrss())) { printf("FORKLIB: RtlRegisterThreadWithCsrss failed!\n"); return FALSE; } printf("FORKLIB: Connected to Csr!\n"); return TRUE; }