diff options
Diffstat (limited to 'build/win32')
-rw-r--r-- | build/win32/__init__.py | 0 | ||||
-rw-r--r-- | build/win32/autowinchecksec.py | 85 | ||||
-rw-r--r-- | build/win32/crashinject.cpp | 94 | ||||
-rw-r--r-- | build/win32/crashinjectdll/crashinjectdll.cpp | 29 | ||||
-rw-r--r-- | build/win32/crashinjectdll/crashinjectdll.def | 7 | ||||
-rw-r--r-- | build/win32/crashinjectdll/moz.build | 16 | ||||
-rw-r--r-- | build/win32/dummy_libs.py | 24 | ||||
-rw-r--r-- | build/win32/moz.build | 32 | ||||
-rw-r--r-- | build/win32/mozconfig.vs-latest | 3 | ||||
-rw-r--r-- | build/win32/mozconfig.vs2019 | 10 | ||||
-rw-r--r-- | build/win32/nsis-no-insert-timestamp.patch | 27 | ||||
-rw-r--r-- | build/win32/nsis-no-underscore.patch | 34 | ||||
-rw-r--r-- | build/win32/nsis-scons.patch | 23 |
13 files changed, 384 insertions, 0 deletions
diff --git a/build/win32/__init__.py b/build/win32/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/build/win32/__init__.py diff --git a/build/win32/autowinchecksec.py b/build/win32/autowinchecksec.py new file mode 100644 index 0000000000..1803d407e8 --- /dev/null +++ b/build/win32/autowinchecksec.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# run the Winchecksec tool (https://github.com/trailofbits/winchecksec) +# against a given Windows binary. + +import json +import subprocess +import sys + +import buildconfig + +# usage +if len(sys.argv) != 2: + print("""usage : autowinchecksec.by path_to_binary""") + sys.exit(0) + +binary_path = sys.argv[1] + +# execute winchecksec against the binary, using the WINCHECKSEC environment +# variable as the path to winchecksec.exe +try: + winchecksec_path = buildconfig.substs["WINCHECKSEC"] +except KeyError: + print( + "TEST-UNEXPECTED-FAIL | autowinchecksec.py | WINCHECKSEC environment variable is " + "not set, can't check DEP/ASLR etc. status." + ) + sys.exit(1) + +wine = buildconfig.substs.get("WINE") +if wine and winchecksec_path.lower().endswith(".exe"): + cmd = [wine, winchecksec_path] +else: + cmd = [winchecksec_path] + +try: + result = subprocess.check_output(cmd + ["-j", binary_path], universal_newlines=True) + +except subprocess.CalledProcessError as e: + print( + "TEST-UNEXPECTED-FAIL | autowinchecksec.py | Winchecksec returned error code %d:\n%s" + % (e.returncode, e.output) + ) + sys.exit(1) + + +result = json.loads(result) + +checks = [ + "aslr", + "cfg", + "dynamicBase", + "gs", + "isolation", + "nx", + "seh", +] + +if buildconfig.substs["TARGET_CPU"] == "x86": + checks += [ + "safeSEH", + ] +else: + checks += [ + "highEntropyVA", + ] + +failed = [c for c in checks if result.get(c) is False] + +if failed: + print( + "TEST-UNEXPECTED-FAIL | autowinchecksec.py | Winchecksec reported %d error(s) for %s" + % (len(failed), binary_path) + ) + print( + "TEST-UNEXPECTED-FAIL | autowinchecksec.py | The following check(s) failed: %s" + % (", ".join(failed)) + ) + sys.exit(1) +else: + print("TEST-PASS | autowinchecksec.py | %s succeeded" % binary_path) diff --git a/build/win32/crashinject.cpp b/build/win32/crashinject.cpp new file mode 100644 index 0000000000..076e5a12a2 --- /dev/null +++ b/build/win32/crashinject.cpp @@ -0,0 +1,94 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Given a PID, this program attempts to inject a DLL into the process + * with that PID. The DLL it attempts to inject, "crashinjectdll.dll", + * must exist alongside this exe. The DLL will then crash the process. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <windows.h> + +int main(int argc, char** argv) { + if (argc != 2) { + fprintf(stderr, "Usage: crashinject <PID>\n"); + return 1; + } + + int pid = atoi(argv[1]); + if (pid <= 0) { + fprintf(stderr, "Usage: crashinject <PID>\n"); + return 1; + } + + // find our DLL to inject + wchar_t filename[_MAX_PATH]; + if (GetModuleFileNameW(nullptr, filename, + sizeof(filename) / sizeof(wchar_t)) == 0) + return 1; + + wchar_t* slash = wcsrchr(filename, L'\\'); + if (slash == nullptr) return 1; + + slash++; + wcscpy(slash, L"crashinjectdll.dll"); + + // now find our target process + HANDLE targetProc = + OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | + PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION, + FALSE, pid); + if (targetProc == nullptr) { + fprintf(stderr, "Error %lu opening target process\n", GetLastError()); + return 1; + } + + /* + * This is sort of insane, but we're implementing a technique described here: + * http://www.codeproject.com/KB/threads/winspy.aspx#section_2 + * + * The gist is to use CreateRemoteThread to create a thread in the other + * process, but cheat and make the thread function kernel32!LoadLibrary, + * so that the only remote data we have to pass to the other process + * is the path to the library we want to load. The library we're loading + * will then do its dirty work inside the other process. + */ + HMODULE hKernel32 = GetModuleHandleW(L"Kernel32"); + // allocate some memory to hold the path in the remote process + void* pLibRemote = VirtualAllocEx(targetProc, nullptr, sizeof(filename), + MEM_COMMIT, PAGE_READWRITE); + if (pLibRemote == nullptr) { + fprintf(stderr, "Error %lu in VirtualAllocEx\n", GetLastError()); + CloseHandle(targetProc); + return 1; + } + + if (!WriteProcessMemory(targetProc, pLibRemote, (void*)filename, + sizeof(filename), nullptr)) { + fprintf(stderr, "Error %lu in WriteProcessMemory\n", GetLastError()); + VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE); + CloseHandle(targetProc); + return 1; + } + // Now create a thread in the target process that will load our DLL + HANDLE hThread = CreateRemoteThread( + targetProc, nullptr, 0, + (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "LoadLibraryW"), + pLibRemote, 0, nullptr); + if (hThread == nullptr) { + fprintf(stderr, "Error %lu in CreateRemoteThread\n", GetLastError()); + VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE); + CloseHandle(targetProc); + return 1; + } + WaitForSingleObject(hThread, INFINITE); + // Cleanup, not that it's going to matter at this point + CloseHandle(hThread); + VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE); + CloseHandle(targetProc); + + return 0; +} diff --git a/build/win32/crashinjectdll/crashinjectdll.cpp b/build/win32/crashinjectdll/crashinjectdll.cpp new file mode 100644 index 0000000000..7ee7e6812d --- /dev/null +++ b/build/win32/crashinjectdll/crashinjectdll.cpp @@ -0,0 +1,29 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <stdio.h> +#include <windows.h> + +// make sure we only ever spawn one thread +DWORD tid = -1; + +DWORD WINAPI CrashingThread(LPVOID lpParameter) { + // not a very friendly DLL + volatile int* x = (int*)0x0; + *x = 1; + return 0; +} + +BOOL WINAPI DllMain(HANDLE hinstDLL, DWORD dwReason, LPVOID lpvReserved) { + if (tid == (DWORD)-1) + // we have to crash on another thread because LoadLibrary() will + // catch memory access errors and return failure to the calling process + CreateThread(nullptr, // default security attributes + 0, // use default stack size + CrashingThread, // thread function name + nullptr, // argument to thread function + 0, // use default creation flags + &tid); // returns the thread identifier + return TRUE; +} diff --git a/build/win32/crashinjectdll/crashinjectdll.def b/build/win32/crashinjectdll/crashinjectdll.def new file mode 100644 index 0000000000..d1ea5602da --- /dev/null +++ b/build/win32/crashinjectdll/crashinjectdll.def @@ -0,0 +1,7 @@ +;+# This Source Code Form is subject to the terms of the Mozilla Public +;+# License, v. 2.0. If a copy of the MPL was not distributed with this +;+# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +LIBRARY crashinjectdll +EXPORTS + DllMain diff --git a/build/win32/crashinjectdll/moz.build b/build/win32/crashinjectdll/moz.build new file mode 100644 index 0000000000..ff113f61e6 --- /dev/null +++ b/build/win32/crashinjectdll/moz.build @@ -0,0 +1,16 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +SOURCES += [ + "crashinjectdll.cpp", +] + +SharedLibrary("crashinjectdll") + +DEFFILE = "crashinjectdll.def" + +USE_STATIC_LIBS = True +NO_PGO = True diff --git a/build/win32/dummy_libs.py b/build/win32/dummy_libs.py new file mode 100644 index 0000000000..16947c6c84 --- /dev/null +++ b/build/win32/dummy_libs.py @@ -0,0 +1,24 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import os +import subprocess + +from buildconfig import substs + + +def main(output, *other_libs): + output.close() + # ar doesn't like it when the file exists beforehand. + os.unlink(output.name) + libs = [output.name] + parent = os.path.dirname(output.name) + libs.extend(os.path.join(parent, l) for l in other_libs) + for lib in libs: + result = subprocess.run( + [substs["AR"]] + [f.replace("$@", lib) for f in substs["AR_FLAGS"]] + ) + if result.returncode != 0: + return result.returncode + return 0 diff --git a/build/win32/moz.build b/build/win32/moz.build new file mode 100644 index 0000000000..d8d61a22db --- /dev/null +++ b/build/win32/moz.build @@ -0,0 +1,32 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +TEST_DIRS += ["crashinjectdll"] + +if CONFIG["ENABLE_TESTS"]: + Program("crashinject") + SOURCES += [ + "crashinject.cpp", + ] + USE_STATIC_LIBS = True + +NO_PGO = True + +# See comment about *-windows-gnu targets in config/makefiles/rust.mk +if CONFIG["CC_TYPE"] == "clang": + GeneratedFile( + "libgcc.a", + "libgcc_eh.a", + "libpthread.a", + script="dummy_libs.py", + flags=["libgcc_eh.a", "libpthread.a"], + ) + + +if CONFIG["WIN32_REDIST_DIR"] and CONFIG["COMPILE_ENVIRONMENT"]: + for f in ["MSVC_C_RUNTIME_DLL", "MSVC_C_RUNTIME_1_DLL", "MSVC_CXX_RUNTIME_DLL"]: + if CONFIG[f]: + FINAL_TARGET_FILES += ["%%%s/%s" % (CONFIG["WIN32_REDIST_DIR"], CONFIG[f])] diff --git a/build/win32/mozconfig.vs-latest b/build/win32/mozconfig.vs-latest new file mode 100644 index 0000000000..1d5b73148d --- /dev/null +++ b/build/win32/mozconfig.vs-latest @@ -0,0 +1,3 @@ +. $topsrcdir/build/win32/mozconfig.vs2019 +. "$topsrcdir/build/mozconfig.clang-cl" +. "$topsrcdir/build/mozconfig.lld-link" diff --git a/build/win32/mozconfig.vs2019 b/build/win32/mozconfig.vs2019 new file mode 100644 index 0000000000..5dc7574744 --- /dev/null +++ b/build/win32/mozconfig.vs2019 @@ -0,0 +1,10 @@ +if [ -z "${VSPATH}" ]; then + VSPATH="$(cd ${MOZ_FETCHES_DIR} && pwd)/vs" +fi + +if [ -d "${VSPATH}" ]; then + export WIN32_REDIST_DIR="${VSPATH}/VC/Redist/MSVC/14.29.30133/x86/Microsoft.VC142.CRT" + export WINSYSROOT="${VSPATH}" +fi + +ac_add_options --target=i686-pc-windows-msvc diff --git a/build/win32/nsis-no-insert-timestamp.patch b/build/win32/nsis-no-insert-timestamp.patch new file mode 100644 index 0000000000..8053b820c6 --- /dev/null +++ b/build/win32/nsis-no-insert-timestamp.patch @@ -0,0 +1,27 @@ +diff -ur nsis-3.03-src/SCons/Config/gnu nsis-3.03-src.n/SCons/Config/gnu +--- nsis-3.03-src/SCons/Config/gnu 2017-10-06 15:30:20.000000000 -0400 ++++ nsis-3.03-src.n/SCons/Config/gnu 2018-06-17 13:26:05.945495151 -0400 +@@ -102,6 +102,7 @@ + stub_env.Append(LINKFLAGS = ['$NODEFLIBS_FLAG']) # no standard libraries + stub_env.Append(LINKFLAGS = ['$ALIGN_FLAG']) # 512 bytes align + stub_env.Append(LINKFLAGS = ['$MAP_FLAG']) # generate map file ++stub_env.Append(LINKFLAGS = ['-Wl,--no-insert-timestamp']) # remove timestamps for reproducible builds + + stub_uenv = stub_env.Clone() + stub_uenv.Append(CPPDEFINES = ['_UNICODE', 'UNICODE']) +@@ -142,6 +143,7 @@ + plugin_env.Append(LINKFLAGS = ['$MAP_FLAG']) # generate map file + plugin_env.Append(LINKFLAGS = ['-static-libgcc']) # remove libgcc*.dll dependency + plugin_env.Append(LINKFLAGS = ['-static-libstdc++']) # remove libstdc++*.dll dependency ++plugin_env.Append(LINKFLAGS = ['-Wl,--no-insert-timestamp']) # remove timestamps for reproducible builds + + plugin_uenv = plugin_env.Clone() + plugin_uenv.Append(CPPDEFINES = ['_UNICODE', 'UNICODE']) +@@ -181,6 +183,7 @@ + + util_env.Append(LINKFLAGS = ['-mwindows']) # build windows executables + util_env.Append(LINKFLAGS = ['$ALIGN_FLAG']) # 512 bytes align ++util_env.Append(LINKFLAGS = ['-Wl,--no-insert-timestamp']) # remove timestamps for reproducible builds + + + conf = FlagsConfigure(util_env) diff --git a/build/win32/nsis-no-underscore.patch b/build/win32/nsis-no-underscore.patch new file mode 100644 index 0000000000..79cff1d4b3 --- /dev/null +++ b/build/win32/nsis-no-underscore.patch @@ -0,0 +1,34 @@ +The `_` environment variable is a variable that is set by bash and some other +shells to point to the executable they use when executing a command. That is, +when executing `foo` from the command line, the shell sets `_` to +`/usr/bin/foo` (assuming that's where foo is). + +However, nothing else does the same, so when e.g. a python program uses +`subprocess.Popen` to run another program, it doesn't set `_`. Worse, if that +python program itself was invoked from a shell, `_` would be set to e.g. +`/usr/bin/python3`. + +So when nsis is invoked from a program that is not a shell, but the process +ancestry has a process that was a shell, `_` may be set to the first +intermediary program rather than nsis, which defeats nsis's assumption that `_` +would contain the nsis path. Ironically, nsis also has more reliable fallbacks +(using e.g. /proc/self/exe), but somehow prefers `_`. + +We remove the reliance of `_` entirely, for simplicity's sake. + + +diff -ruN nsis-3.07-src.orig/Source/util.cpp nsis-3.07-src/Source/util.cpp +--- nsis-3.07-src.orig/Source/util.cpp 2021-09-02 09:25:48.489016918 +0900 ++++ nsis-3.07-src/Source/util.cpp 2021-09-02 09:26:21.158814484 +0900 +@@ -810,10 +810,7 @@ + assert(rc == 0); + return tstring(CtoTString(temp_buf)); + #else /* Linux/BSD/POSIX/etc */ +- const TCHAR *envpath = _tgetenv(_T("_")); +- if (envpath) +- return get_full_path(envpath); +- else { ++ { + char *path = NULL, *pathtmp; + size_t len = 100; + int nchars; diff --git a/build/win32/nsis-scons.patch b/build/win32/nsis-scons.patch new file mode 100644 index 0000000000..d372ea99ea --- /dev/null +++ b/build/win32/nsis-scons.patch @@ -0,0 +1,23 @@ +Patch from https://sourceforge.net/p/nsis/code/7291/ + +diff -ruN nsis-3.07-src/Contrib/ExDLL/SConscript nsis-3.08-src/Contrib/ExDLL/SConscript +--- nsis-3.07-src/Contrib/ExDLL/SConscript 2015-06-22 01:13:45.000000000 +0900 ++++ nsis-3.08-src/Contrib/ExDLL/SConscript 2021-08-05 05:15:13.000000000 +0900 +@@ -47,7 +47,7 @@ + env.DistributeExamples(api_files, path='Plugin/nsis') + env.DistributeExamples(example, path='Plugin') + if env['PLATFORM'] != 'win32': +- if env.has_key('PREFIX_PLUGINAPI_INC'): ++ if 'PREFIX_PLUGINAPI_INC' in env: + env.Distribute(api_files, None, 'pluginapi_inc', '', 'nsis', 'pluginapi', 'pluginapi') + + +@@ -56,7 +56,7 @@ + else: + example += lib_files + +- if env.has_key('PREFIX_PLUGINAPI_LIB'): ++ if 'PREFIX_PLUGINAPI_LIB' in env: + env.Distribute(lib, None, 'pluginapi_lib', '', 'nsis', 'pluginapi', 'pluginapi') + + |