2 * Win32 utility routines
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 2006 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
12 #include <wsutil/array.h>
14 #include "win32-utils.h"
17 #include <versionhelpers.h>
19 /* Quote the argument element if necessary, so that it will get
20 * reconstructed correctly in the C runtime startup code. Note that
21 * the unquoting algorithm in the C runtime is really weird, and
22 * rather different than what Unix shells do. See stdargv.c in the C
23 * runtime sources (in the Platform SDK, in src/crt).
25 * Stolen from GLib's protect_argv(), an internal routine that quotes
26 * string in an argument list so that they arguments will be handled
27 * correctly in the command-line string passed to CreateProcess()
28 * if that string is constructed by gluing those strings together.
31 protect_arg (const char *argv
)
37 bool need_dblquotes
= false;
40 if (*p
== ' ' || *p
== '\t')
41 need_dblquotes
= true;
44 else if (*p
== '\\') {
56 q
= new_arg
= g_malloc (len
+ (need_dblquotes
? 2 : 0) + 1);
65 else if (*p
== '\\') {
84 #define PIPE_STR "\\pipe\\"
87 win32_is_pipe_name(const char *pipe_name
)
90 /* Under Windows, named pipes _must_ have the form
91 * "\\<server>\pipe\<pipename>". <server> may be "." for localhost.
92 * https://learn.microsoft.com/en-us/windows/win32/ipc/pipe-names
94 pncopy
= g_strdup(pipe_name
);
95 if ((pos
= strstr(pncopy
, "\\\\")) == pncopy
) {
96 pos
= strchr(pncopy
+ 3, '\\');
97 if (pos
&& g_ascii_strncasecmp(pos
, PIPE_STR
, strlen(PIPE_STR
)) != 0)
103 return (pos
!= NULL
);
107 * Generate a UTF-8 string for a Windows error.
111 * We make the buffer at least this big, under the assumption that doing
112 * so will reduce the number of reallocations to do. (Otherwise, why
113 * did Microsoft bother supporting a minimum buffer size?)
115 #define ERRBUF_SIZE 128
117 win32strerror(DWORD error
)
120 WCHAR
*utf16_message
;
126 * XXX - what language ID to use?
128 * For UN*Xes, g_strerror() may or may not return localized strings.
130 * We currently don't have localized strings, except for GUI items,
131 * but we might want to do so. On the other hand, if most of these
132 * messages are going to be read by Wireshark developers, English
133 * might be a better choice, so the developer doesn't have to get
134 * the message translated if it's in a language they don't happen
135 * to understand. Then again, we're including the error number,
136 * so the developer can just look that up.
138 retval
= FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_MAX_WIDTH_MASK
,
139 NULL
, error
, MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
140 (LPTSTR
)&utf16_message
, ERRBUF_SIZE
, NULL
);
143 tempmsg
= ws_strdup_printf("Couldn't get error message for error (%lu) (because %lu)",
144 error
, GetLastError());
145 msg
= g_intern_string(tempmsg
);
150 utf8_message
= g_utf16_to_utf8(utf16_message
, -1, NULL
, NULL
, NULL
);
151 LocalFree(utf16_message
);
152 if (utf8_message
== NULL
) {
153 /* Conversion failed. */
154 tempmsg
= ws_strdup_printf("Couldn't convert error message for error to UTF-8 (%lu) (because %lu)",
155 error
, GetLastError());
156 msg
= g_intern_string(tempmsg
);
160 tempmsg
= ws_strdup_printf("%s (%lu)", utf8_message
, error
);
161 g_free(utf8_message
);
162 msg
= g_intern_string(tempmsg
);
168 * Generate a string for a Win32 exception code.
171 win32strexception(DWORD exception
)
173 static char errbuf
[ERRBUF_SIZE
+1];
174 static const struct exception_msg
{
178 { EXCEPTION_ACCESS_VIOLATION
, "Access violation" },
179 { EXCEPTION_ARRAY_BOUNDS_EXCEEDED
, "Array bounds exceeded" },
180 { EXCEPTION_BREAKPOINT
, "Breakpoint" },
181 { EXCEPTION_DATATYPE_MISALIGNMENT
, "Data type misalignment" },
182 { EXCEPTION_FLT_DENORMAL_OPERAND
, "Denormal floating-point operand" },
183 { EXCEPTION_FLT_DIVIDE_BY_ZERO
, "Floating-point divide by zero" },
184 { EXCEPTION_FLT_INEXACT_RESULT
, "Floating-point inexact result" },
185 { EXCEPTION_FLT_INVALID_OPERATION
, "Invalid floating-point operation" },
186 { EXCEPTION_FLT_OVERFLOW
, "Floating-point overflow" },
187 { EXCEPTION_FLT_STACK_CHECK
, "Floating-point stack check" },
188 { EXCEPTION_FLT_UNDERFLOW
, "Floating-point underflow" },
189 { EXCEPTION_GUARD_PAGE
, "Guard page violation" },
190 { EXCEPTION_ILLEGAL_INSTRUCTION
, "Illegal instruction" },
191 { EXCEPTION_IN_PAGE_ERROR
, "Page-in error" },
192 { EXCEPTION_INT_DIVIDE_BY_ZERO
, "Integer divide by zero" },
193 { EXCEPTION_INT_OVERFLOW
, "Integer overflow" },
194 { EXCEPTION_INVALID_DISPOSITION
, "Invalid disposition" },
195 { EXCEPTION_INVALID_HANDLE
, "Invalid handle" },
196 { EXCEPTION_NONCONTINUABLE_EXCEPTION
, "Non-continuable exception" },
197 { EXCEPTION_PRIV_INSTRUCTION
, "Privileged instruction" },
198 { EXCEPTION_SINGLE_STEP
, "Single-step complete" },
199 { EXCEPTION_STACK_OVERFLOW
, "Stack overflow" },
202 #define N_EXCEPTIONS array_length(exceptions)
204 for (size_t i
= 0; i
< N_EXCEPTIONS
; i
++) {
205 if (exceptions
[i
].code
== exception
)
206 return exceptions
[i
].msg
;
208 snprintf(errbuf
, sizeof errbuf
, "Exception 0x%08lx", exception
);
212 // This appears to be the closest equivalent to SIGPIPE on Windows.
213 // https://devblogs.microsoft.com/oldnewthing/?p=2433
214 // https://stackoverflow.com/a/53214/82195
216 static void win32_kill_child_on_exit(HANDLE child_handle
) {
217 static HANDLE cjo_handle
= NULL
;
219 cjo_handle
= CreateJobObject(NULL
, NULL
);
222 ws_log(LOG_DOMAIN_CAPTURE
, LOG_LEVEL_DEBUG
, "Could not create child cleanup job object: %s",
223 win32strerror(GetLastError()));
227 JOBOBJECT_EXTENDED_LIMIT_INFORMATION cjo_jel_info
= { 0 };
228 cjo_jel_info
.BasicLimitInformation
.LimitFlags
= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
;
229 BOOL sijo_ret
= SetInformationJobObject(cjo_handle
, JobObjectExtendedLimitInformation
,
230 &cjo_jel_info
, sizeof(cjo_jel_info
));
232 ws_log(LOG_DOMAIN_CAPTURE
, LOG_LEVEL_DEBUG
, "Could not set child cleanup limits: %s",
233 win32strerror(GetLastError()));
237 BOOL aptjo_ret
= AssignProcessToJobObject(cjo_handle
, child_handle
);
239 ws_log(LOG_DOMAIN_CAPTURE
, LOG_LEVEL_DEBUG
, "Could not assign child cleanup process: %s",
240 win32strerror(GetLastError()));
244 BOOL
win32_create_process(const char *application_name
, const char *command_line
, LPSECURITY_ATTRIBUTES process_attributes
, LPSECURITY_ATTRIBUTES thread_attributes
, size_t n_inherit_handles
, HANDLE
*inherit_handles
, DWORD creation_flags
, LPVOID environment
, const char *current_directory
, LPSTARTUPINFO startup_info
, LPPROCESS_INFORMATION process_information
)
246 gunichar2
*wappname
= NULL
, *wcurrentdirectory
= NULL
;
247 gunichar2
*wcommandline
= g_utf8_to_utf16(command_line
, -1, NULL
, NULL
, NULL
);
248 LPPROC_THREAD_ATTRIBUTE_LIST attribute_list
= NULL
;
249 STARTUPINFOEX startup_infoex
;
251 // CREATE_SUSPENDED: Suspend the child so that we can cleanly call
252 // AssignProcessToJobObject.
253 DWORD wcreationflags
= creation_flags
|CREATE_SUSPENDED
;
254 // CREATE_BREAKAWAY_FROM_JOB: The main application might be associated with a job,
255 // e.g. if we're running under "Run As", ConEmu, or Visual Studio. On Windows
256 // <= 7 our child process needs to break away from it so that we can cleanly
257 // call AssignProcessToJobObject on *our* job.
258 // Windows >= 8 supports nested jobs so this isn't necessary there.
259 // https://blogs.msdn.microsoft.com/winsdk/2014/09/22/job-object-insanity/
261 if (! IsWindowsVersionOrGreater(6, 2, 0)) { // Windows 8
262 wcreationflags
|= CREATE_BREAKAWAY_FROM_JOB
;
265 if (application_name
) {
266 wappname
= g_utf8_to_utf16(application_name
, -1, NULL
, NULL
, NULL
);
268 if (current_directory
) {
269 wcurrentdirectory
= g_utf8_to_utf16(current_directory
, -1, NULL
, NULL
, NULL
);
271 if (n_inherit_handles
> 0) {
272 size_t attr_size
= 0;
274 success
= InitializeProcThreadAttributeList(NULL
, 1, 0, &attr_size
);
275 if (success
|| (GetLastError() == ERROR_INSUFFICIENT_BUFFER
)) {
276 attribute_list
= g_malloc(attr_size
);
277 success
= InitializeProcThreadAttributeList(attribute_list
, 1, 0, &attr_size
);
279 if (success
&& (attribute_list
!= NULL
)) {
280 success
= UpdateProcThreadAttribute(attribute_list
, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST
,
281 inherit_handles
, n_inherit_handles
* sizeof(HANDLE
), NULL
, NULL
);
283 if (!success
&& (attribute_list
!= NULL
)) {
284 DeleteProcThreadAttributeList(attribute_list
);
285 g_free(attribute_list
);
286 attribute_list
= NULL
;
289 memset(&startup_infoex
, 0, sizeof(startup_infoex
));
290 startup_infoex
.StartupInfo
= *startup_info
;
291 startup_infoex
.StartupInfo
.cb
= sizeof(startup_infoex
);
292 startup_infoex
.lpAttributeList
= attribute_list
;
293 wcreationflags
|= EXTENDED_STARTUPINFO_PRESENT
;
294 for (i
= 0; i
< n_inherit_handles
; i
++) {
295 SetHandleInformation(inherit_handles
[i
], HANDLE_FLAG_INHERIT
, HANDLE_FLAG_INHERIT
);
297 BOOL cp_res
= CreateProcess(wappname
, wcommandline
, process_attributes
, thread_attributes
,
298 (n_inherit_handles
> 0) ? true : false, wcreationflags
, environment
, wcurrentdirectory
,
299 &startup_infoex
.StartupInfo
, process_information
);
300 /* While this function makes the created process inherit only the explicitly
301 * listed handles, there can be other functions (in 3rd party libraries)
302 * that create processes inheriting all inheritable handles. To minimize
303 * number of unwanted handle duplicates (handle duplicate can extend object
304 * lifetime, e.g. pipe write end) created that way clear the inherit flag.
306 for (i
= 0; i
< n_inherit_handles
; i
++) {
307 SetHandleInformation(inherit_handles
[i
], HANDLE_FLAG_INHERIT
, 0);
310 win32_kill_child_on_exit(process_information
->hProcess
);
311 ResumeThread(process_information
->hThread
);
313 // XXX Else try again if CREATE_BREAKAWAY_FROM_JOB and GetLastError() == ERROR_ACCESS_DENIED?
315 if (attribute_list
) {
316 DeleteProcThreadAttributeList(attribute_list
);
317 g_free(attribute_list
);
320 g_free(wcommandline
);
321 g_free(wcurrentdirectory
);
326 * Editor modelines - https://www.wireshark.org/tools/modelines.html
331 * indent-tabs-mode: nil
334 * ex: set shiftwidth=4 tabstop=8 expandtab:
335 * :indentSize=4:tabSize=8:noTabs=true: