/* Currently supported flags (see sched.h): o CLONE_VM o CLONE_FS o CLONE_SIGHAND o CLONE_PTRACE o CLONE_VFORK o CLONE_PARENT o CLONE_THREAD o CLONE_NEWNS o CLONE_SYSVSEM o CLONE_SETTLS o CLONE_PARENT_SETTID o CLONE_CHILD_CLEARTID o CLONE_DETACHED o CLONE_UNTRACED * CLONE_CHILD_SETTID o CLONE_NEWUTS o CLONE_NEWIPC o CLONE_NEWUSER o CLONE_NEWPID o CLONE_NEWNET o CLONE_IO */ static pid_t fork_process(struct syscall_context *context, unsigned long flags, void *ptid, void *ctid) { wchar_t filename[MAX_PATH]; GetModuleFileNameW(NULL, filename, sizeof(filename) / sizeof(filename[0])); tls_beforefork(); PROCESS_INFORMATION info; STARTUPINFOW si = { 0 }; si.cb = sizeof(si); if (!CreateProcessW(filename, L"/?/fork", NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, NULL, &si, &info)) { log_warning("fork(): CreateProcessW() failed.\n"); return -1; } if (!mm_fork(info.hProcess)) goto fail; if (!vfs_fork(info.hProcess)) goto fail; /* Set up fork_info in child process */ void *stack_base = process_get_stack_base(); VirtualAllocEx(info.hProcess, FORK_INFO_BASE, BLOCK_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); WriteProcessMemory(info.hProcess, &fork->context, context, sizeof(struct syscall_context), NULL); WriteProcessMemory(info.hProcess, &fork->stack_base, &stack_base, sizeof(stack_base), NULL); if (flags & CLONE_CHILD_SETTID) WriteProcessMemory(info.hProcess, &fork->ctid, &ctid, sizeof(void*), NULL); /* Copy stack */ VirtualAllocEx(info.hProcess, stack_base, STACK_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); WriteProcessMemory(info.hProcess, context->esp, context->esp, (char *)stack_base + STACK_SIZE - context->esp, NULL); ResumeThread(info.hThread); CloseHandle(info.hThread); /* Process handled will be used for wait() */ log_info("Child pid: %d\n", info.dwProcessId); process_add_child(info.dwProcessId, info.hProcess); return info.dwProcessId; fail: TerminateProcess(info.hProcess, 0); CloseHandle(info.hThread); CloseHandle(info.hProcess); return -1; }
static void run(struct binfmt *binary, int argc, char *argv[], int env_size, char *envp[]) { /* Generate initial stack */ char *stack_base = process_get_stack_base(); char *stack = stack_base + STACK_SIZE; /* 16 random bytes for AT_RANDOM */ /* TODO: Fill in real content */ char *random_bytes = ALLOC(16); struct elf_header *executable = binary->executable; struct elf_header *interpreter = binary->interpreter; /* auxiliary vector */ PTR(NULL); AUX_VEC(AT_FLAGS, 0); AUX_VEC(AT_SECURE, 0); AUX_VEC(AT_RANDOM, random_bytes); AUX_VEC(AT_PAGESZ, PAGE_SIZE); AUX_VEC(AT_PHDR, executable->load_base + executable->eh.e_phoff); AUX_VEC(AT_PHENT, executable->eh.e_phentsize); AUX_VEC(AT_PHNUM, executable->eh.e_phnum); if (executable->eh.e_type == ET_DYN) AUX_VEC(AT_ENTRY, executable->load_base + executable->eh.e_entry); else AUX_VEC(AT_ENTRY, executable->eh.e_entry); AUX_VEC(AT_BASE, (binary->has_interpreter ? (void*)(interpreter->load_base - interpreter->low) : NULL)); /* environment variables */ PTR(NULL); for (int i = env_size - 1; i >= 0; i--) PTR(envp[i]); /* argv */ PTR(NULL); for (int i = argc - 1; i >= 0; i--) PTR(argv[i]); /* Insert additional arguments from special binfmt */ if (binary->argv1) { PTR(binary->argv1); argc++; } if (binary->argv0) { PTR(binary->argv0); argc++; } /* argc */ PTR(argc); /* Call executable entrypoint */ size_t entrypoint; struct elf_header *start = binary->has_interpreter? interpreter: executable; if (start->eh.e_type == ET_DYN) entrypoint = start->load_base + start->eh.e_entry; else entrypoint = start->eh.e_entry; log_info("Entrypoint: %p", entrypoint); /* TODO: The current way isn't bullet-proof * Basically our 'kernel' routines uses the application's stack * When doing an execve we are overwritting the upper part of the stack while relying on the bottom part!!! * To get proper behaviour, we first have to save and restore esp on kernel/app switches, which is left to be done */ dbt_run(entrypoint, (size_t)stack); }