/* * NT basis DLL * * This file contains the Nt* API functions of NTDLL.DLL. * In the original ntdll.dll they all seem to just call int 0x2e (down to the NTOSKRNL) * * Copyright 1996-1998 Marcus Meissner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include "ntstatus.h" #define WIN32_NO_STATUS #include "wine/debug.h" #include "windef.h" #include "winternl.h" #include "ntdll_misc.h" #include "wine/server.h" WINE_DEFAULT_DEBUG_CHANNEL(ntdll); static ULONG execute_flags = MEM_EXECUTE_OPTION_DISABLE; /* * Process object */ /****************************************************************************** * NtTerminateProcess [NTDLL.@] * * Native applications must kill themselves when done */ NTSTATUS WINAPI NtTerminateProcess( HANDLE handle, LONG exit_code ) { NTSTATUS ret; BOOL self; SERVER_START_REQ( terminate_process ) { req->handle = wine_server_obj_handle( handle ); req->exit_code = exit_code; ret = wine_server_call( req ); self = !ret && reply->self; } SERVER_END_REQ; if (self) exit( exit_code ); return ret; } /****************************************************************************** * RtlGetCurrentPeb [NTDLL.@] * */ PEB * WINAPI RtlGetCurrentPeb(void) { return NtCurrentTeb()->Peb; } /*********************************************************************** * __wine_make_process_system (NTDLL.@) * * Mark the current process as a system process. * Returns the event that is signaled when all non-system processes have exited. */ HANDLE CDECL __wine_make_process_system(void) { HANDLE ret = 0; SERVER_START_REQ( make_process_system ) { if (!wine_server_call( req )) ret = wine_server_ptr_handle( reply->event ); } SERVER_END_REQ; return ret; } #define UNIMPLEMENTED_INFO_CLASS(c) \ case c: \ FIXME("(process=%p) Unimplemented information class: " #c "\n", ProcessHandle); \ ret = STATUS_INVALID_INFO_CLASS; \ break /****************************************************************************** * NtQueryInformationProcess [NTDLL.@] * ZwQueryInformationProcess [NTDLL.@] * */ NTSTATUS WINAPI NtQueryInformationProcess( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength) { NTSTATUS ret = STATUS_SUCCESS; ULONG len = 0; TRACE("(%p,0x%08x,%p,0x%08x,%p)\n", ProcessHandle,ProcessInformationClass, ProcessInformation,ProcessInformationLength, ReturnLength); switch (ProcessInformationClass) { UNIMPLEMENTED_INFO_CLASS(ProcessQuotaLimits); UNIMPLEMENTED_INFO_CLASS(ProcessBasePriority); UNIMPLEMENTED_INFO_CLASS(ProcessRaisePriority); UNIMPLEMENTED_INFO_CLASS(ProcessExceptionPort); UNIMPLEMENTED_INFO_CLASS(ProcessAccessToken); UNIMPLEMENTED_INFO_CLASS(ProcessLdtInformation); UNIMPLEMENTED_INFO_CLASS(ProcessLdtSize); UNIMPLEMENTED_INFO_CLASS(ProcessDefaultHardErrorMode); UNIMPLEMENTED_INFO_CLASS(ProcessIoPortHandlers); UNIMPLEMENTED_INFO_CLASS(ProcessPooledUsageAndLimits); UNIMPLEMENTED_INFO_CLASS(ProcessWorkingSetWatch); UNIMPLEMENTED_INFO_CLASS(ProcessUserModeIOPL); UNIMPLEMENTED_INFO_CLASS(ProcessEnableAlignmentFaultFixup); UNIMPLEMENTED_INFO_CLASS(ProcessPriorityClass); UNIMPLEMENTED_INFO_CLASS(ProcessWx86Information); UNIMPLEMENTED_INFO_CLASS(ProcessAffinityMask); UNIMPLEMENTED_INFO_CLASS(ProcessPriorityBoost); UNIMPLEMENTED_INFO_CLASS(ProcessDeviceMap); UNIMPLEMENTED_INFO_CLASS(ProcessSessionInformation); UNIMPLEMENTED_INFO_CLASS(ProcessForegroundInformation); UNIMPLEMENTED_INFO_CLASS(ProcessLUIDDeviceMapsEnabled); UNIMPLEMENTED_INFO_CLASS(ProcessBreakOnTermination); UNIMPLEMENTED_INFO_CLASS(ProcessDebugFlags); UNIMPLEMENTED_INFO_CLASS(ProcessHandleTracing); case ProcessBasicInformation: { PROCESS_BASIC_INFORMATION pbi; const ULONG_PTR affinity_mask = ((ULONG_PTR)1 << NtCurrentTeb()->Peb->NumberOfProcessors) - 1; if (ProcessInformationLength >= sizeof(PROCESS_BASIC_INFORMATION)) { if (!ProcessInformation) ret = STATUS_ACCESS_VIOLATION; else if (!ProcessHandle) ret = STATUS_INVALID_HANDLE; else { SERVER_START_REQ(get_process_info) { req->handle = wine_server_obj_handle( ProcessHandle ); if ((ret = wine_server_call( req )) == STATUS_SUCCESS) { pbi.ExitStatus = reply->exit_code; pbi.PebBaseAddress = wine_server_get_ptr( reply->peb ); pbi.AffinityMask = reply->affinity & affinity_mask; pbi.BasePriority = reply->priority; pbi.UniqueProcessId = reply->pid; pbi.InheritedFromUniqueProcessId = reply->ppid; } } SERVER_END_REQ; memcpy(ProcessInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION)); len = sizeof(PROCESS_BASIC_INFORMATION); } if (ProcessInformationLength > sizeof(PROCESS_BASIC_INFORMATION)) ret = STATUS_INFO_LENGTH_MISMATCH; } else { len = sizeof(PROCESS_BASIC_INFORMATION); ret = STATUS_INFO_LENGTH_MISMATCH; } } break; case ProcessIoCounters: { IO_COUNTERS pii; if (ProcessInformationLength >= sizeof(IO_COUNTERS)) { if (!ProcessInformation) ret = STATUS_ACCESS_VIOLATION; else if (!ProcessHandle) ret = STATUS_INVALID_HANDLE; else { /* FIXME : real data */ memset(&pii, 0 , sizeof(IO_COUNTERS)); memcpy(ProcessInformation, &pii, sizeof(IO_COUNTERS)); len = sizeof(IO_COUNTERS); } if (ProcessInformationLength > sizeof(IO_COUNTERS)) ret = STATUS_INFO_LENGTH_MISMATCH; } else { len = sizeof(IO_COUNTERS); ret = STATUS_INFO_LENGTH_MISMATCH; } } break; case ProcessVmCounters: { VM_COUNTERS pvmi; /* older Windows versions don't have the PrivatePageCount field */ if (ProcessInformationLength >= FIELD_OFFSET(VM_COUNTERS,PrivatePageCount)) { if (!ProcessInformation) ret = STATUS_ACCESS_VIOLATION; else if (!ProcessHandle) ret = STATUS_INVALID_HANDLE; else { /* FIXME : real data */ memset(&pvmi, 0 , sizeof(VM_COUNTERS)); len = ProcessInformationLength; if (len != FIELD_OFFSET(VM_COUNTERS,PrivatePageCount)) len = sizeof(VM_COUNTERS); memcpy(ProcessInformation, &pvmi, min(ProcessInformationLength,sizeof(VM_COUNTERS))); } if (ProcessInformationLength != FIELD_OFFSET(VM_COUNTERS,PrivatePageCount) && ProcessInformationLength != sizeof(VM_COUNTERS)) ret = STATUS_INFO_LENGTH_MISMATCH; } else { len = sizeof(pvmi); ret = STATUS_INFO_LENGTH_MISMATCH; } } break; case ProcessTimes: { KERNEL_USER_TIMES pti; if (ProcessInformationLength >= sizeof(KERNEL_USER_TIMES)) { if (!ProcessInformation) ret = STATUS_ACCESS_VIOLATION; else if (!ProcessHandle) ret = STATUS_INVALID_HANDLE; else { /* FIXME : User- and KernelTime have to be implemented */ memset(&pti, 0, sizeof(KERNEL_USER_TIMES)); SERVER_START_REQ(get_process_info) { req->handle = wine_server_obj_handle( ProcessHandle ); if ((ret = wine_server_call( req )) == STATUS_SUCCESS) { pti.CreateTime.QuadPart = reply->start_time; pti.ExitTime.QuadPart = reply->end_time; } } SERVER_END_REQ; memcpy(ProcessInformation, &pti, sizeof(KERNEL_USER_TIMES)); len = sizeof(KERNEL_USER_TIMES); } if (ProcessInformationLength > sizeof(KERNEL_USER_TIMES)) ret = STATUS_INFO_LENGTH_MISMATCH; } else { len = sizeof(KERNEL_USER_TIMES); ret = STATUS_INFO_LENGTH_MISMATCH; } } break; case ProcessDebugPort: /* "These are not the debuggers you are looking for." * * set it to 0 aka "no debugger" to satisfy copy protections */ len = sizeof(DWORD_PTR); if (ProcessInformationLength == len) { if (!ProcessInformation) ret = STATUS_ACCESS_VIOLATION; else if (!ProcessHandle) ret = STATUS_INVALID_HANDLE; else { SERVER_START_REQ(get_process_info) { req->handle = wine_server_obj_handle( ProcessHandle ); if ((ret = wine_server_call( req )) == STATUS_SUCCESS) { *(DWORD_PTR *)ProcessInformation = reply->debugger_present ? ~(DWORD_PTR)0 : 0; } } SERVER_END_REQ; } } else ret = STATUS_INFO_LENGTH_MISMATCH; break; case ProcessDebugObjectHandle: /* "These are not the debuggers you are looking for." * * set it to 0 aka "no debugger" to satisfy copy protections */ len = sizeof(HANDLE); if (ProcessInformationLength == len) { if (!ProcessInformation) ret = STATUS_ACCESS_VIOLATION; else if (!ProcessHandle) ret = STATUS_INVALID_HANDLE; else memset(ProcessInformation, 0, ProcessInformationLength); } else ret = STATUS_INFO_LENGTH_MISMATCH; break; case ProcessHandleCount: if (ProcessInformationLength >= 4) { if (!ProcessInformation) ret = STATUS_ACCESS_VIOLATION; else if (!ProcessHandle) ret = STATUS_INVALID_HANDLE; else { memset(ProcessInformation, 0, 4); len = 4; } if (ProcessInformationLength > 4) ret = STATUS_INFO_LENGTH_MISMATCH; } else { len = 4; ret = STATUS_INFO_LENGTH_MISMATCH; } break; case ProcessWow64Information: len = sizeof(DWORD); if (ProcessInformationLength == len) { DWORD val = 0; if (ProcessHandle == GetCurrentProcess()) val = is_wow64; else if (server_cpus & (1 << CPU_x86_64)) { SERVER_START_REQ( get_process_info ) { req->handle = wine_server_obj_handle( ProcessHandle ); if (!(ret = wine_server_call( req ))) val = (reply->cpu != CPU_x86_64); } SERVER_END_REQ; } *(DWORD *)ProcessInformation = val; } else ret = STATUS_INFO_LENGTH_MISMATCH; break; case ProcessImageFileName: /* FIXME: this will return a DOS path. Windows returns an NT path. Changing this would require also changing kernel32.QueryFullProcessImageName. * The latter may be harder because of the lack of RtlNtPathNameToDosPathName. */ SERVER_START_REQ(get_dll_info) { UNICODE_STRING *image_file_name_str = ProcessInformation; req->handle = wine_server_obj_handle( ProcessHandle ); req->base_address = 0; /* main module */ wine_server_set_reply( req, image_file_name_str ? image_file_name_str + 1 : NULL, ProcessInformationLength > sizeof(UNICODE_STRING) ? ProcessInformationLength - sizeof(UNICODE_STRING) : 0 ); ret = wine_server_call( req ); if (ret == STATUS_BUFFER_TOO_SMALL) ret = STATUS_INFO_LENGTH_MISMATCH; len = sizeof(UNICODE_STRING) + reply->filename_len; if (ret == STATUS_SUCCESS) { image_file_name_str->MaximumLength = image_file_name_str->Length = reply->filename_len; image_file_name_str->Buffer = (PWSTR)(image_file_name_str + 1); } } SERVER_END_REQ; break; case ProcessExecuteFlags: len = sizeof(ULONG); if (ProcessInformationLength == len) *(ULONG *)ProcessInformation = execute_flags; else ret = STATUS_INFO_LENGTH_MISMATCH; break; default: FIXME("(%p,info_class=%d,%p,0x%08x,%p) Unknown information class\n", ProcessHandle,ProcessInformationClass, ProcessInformation,ProcessInformationLength, ReturnLength); ret = STATUS_INVALID_INFO_CLASS; break; } if (ReturnLength) *ReturnLength = len; return ret; } /****************************************************************************** * NtSetInformationProcess [NTDLL.@] * ZwSetInformationProcess [NTDLL.@] */ NTSTATUS WINAPI NtSetInformationProcess( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, IN PVOID ProcessInformation, IN ULONG ProcessInformationLength) { NTSTATUS ret = STATUS_SUCCESS; switch (ProcessInformationClass) { case ProcessAffinityMask: if (ProcessInformationLength != sizeof(DWORD_PTR)) return STATUS_INVALID_PARAMETER; if (*(PDWORD_PTR)ProcessInformation & ~(((DWORD_PTR)1 << NtCurrentTeb()->Peb->NumberOfProcessors) - 1)) return STATUS_INVALID_PARAMETER; if (!*(PDWORD_PTR)ProcessInformation) return STATUS_INVALID_PARAMETER; SERVER_START_REQ( set_process_info ) { req->handle = wine_server_obj_handle( ProcessHandle ); req->affinity = *(PDWORD_PTR)ProcessInformation; req->mask = SET_PROCESS_INFO_AFFINITY; ret = wine_server_call( req ); } SERVER_END_REQ; break; case ProcessPriorityClass: if (ProcessInformationLength != sizeof(PROCESS_PRIORITY_CLASS)) return STATUS_INVALID_PARAMETER; else { PROCESS_PRIORITY_CLASS* ppc = ProcessInformation; SERVER_START_REQ( set_process_info ) { req->handle = wine_server_obj_handle( ProcessHandle ); /* FIXME Foreground isn't used */ req->priority = ppc->PriorityClass; req->mask = SET_PROCESS_INFO_PRIORITY; ret = wine_server_call( req ); } SERVER_END_REQ; } break; case ProcessExecuteFlags: if (ProcessInformationLength != sizeof(ULONG)) return STATUS_INVALID_PARAMETER; else if (execute_flags & MEM_EXECUTE_OPTION_PERMANENT) return STATUS_ACCESS_DENIED; else { BOOL enable; switch (*(ULONG *)ProcessInformation & (MEM_EXECUTE_OPTION_ENABLE|MEM_EXECUTE_OPTION_DISABLE)) { case MEM_EXECUTE_OPTION_ENABLE: enable = TRUE; break; case MEM_EXECUTE_OPTION_DISABLE: enable = FALSE; break; default: return STATUS_INVALID_PARAMETER; } execute_flags = *(ULONG *)ProcessInformation; VIRTUAL_SetForceExec( enable ); } break; default: FIXME("(%p,0x%08x,%p,0x%08x) stub\n", ProcessHandle,ProcessInformationClass,ProcessInformation, ProcessInformationLength); ret = STATUS_NOT_IMPLEMENTED; break; } return ret; } /****************************************************************************** * NtFlushInstructionCache [NTDLL.@] * ZwFlushInstructionCache [NTDLL.@] */ NTSTATUS WINAPI NtFlushInstructionCache( IN HANDLE ProcessHandle, IN LPCVOID BaseAddress, IN SIZE_T Size) { #ifdef __i386__ TRACE("%p %p %ld - no-op on x86\n", ProcessHandle, BaseAddress, Size ); #else FIXME("%p %p %ld\n", ProcessHandle, BaseAddress, Size ); #endif return STATUS_SUCCESS; } /****************************************************************** * NtOpenProcess [NTDLL.@] * ZwOpenProcess [NTDLL.@] */ NTSTATUS WINAPI NtOpenProcess(PHANDLE handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES* attr, const CLIENT_ID* cid) { NTSTATUS status; SERVER_START_REQ( open_process ) { req->pid = HandleToULong(cid->UniqueProcess); req->access = access; req->attributes = attr ? attr->Attributes : 0; status = wine_server_call( req ); if (!status) *handle = wine_server_ptr_handle( reply->handle ); } SERVER_END_REQ; return status; }