diff --git a/CMakeLists.txt b/CMakeLists.txt
index 82372af8417c126345c9ff31415d064163488196..96d8f10786bc48eefd59c2f7eb447b76e47b2f82 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -58,6 +58,7 @@ add_executable(groundstation
     src/shared/Core/QCustomPlot/QCustomPlot.cpp
     src/shared/Core/SkywardHubCore.cpp
     src/shared/Core/XmlObject.cpp
+    src/shared/Core/CrashLogger.cpp
     src/shared/Modules/CommandPad/CommandPad.cpp
     src/shared/Modules/CommandPad/MessageFormElement.cpp
     src/shared/Modules/CompactCommandPad/CompactCommandPad.cpp
@@ -93,8 +94,7 @@ add_executable(groundstation
     src/shared/Modules/ModuleInfo.cpp
     src/shared/Modules/ModulesList.cpp
     src/entrypoints/groundstation/application.qrc
-    src/entrypoints/groundstation/main.cpp
-)
+    src/entrypoints/groundstation/main.cpp)
 target_include_directories(groundstation PRIVATE src/shared)
 target_link_libraries(groundstation PUBLIC
     Qt5::Widgets
diff --git a/SkywardHub.pro b/SkywardHub.pro
index c5f3472aabce1eb94f5af97510dfac0dbd3c196f..9bcac9841bc346ae777612be54632dd982d064c0 100644
--- a/SkywardHub.pro
+++ b/SkywardHub.pro
@@ -19,6 +19,7 @@ INCLUDEPATH += \
     libs/mavlink-skyward-lib
 
 SOURCES += \
+    src/shared/Core/CrashLogger.cpp \
     src/shared/Modules/Empty/EmptyModule.cpp \
     src/shared/Modules/ValuesConverterViewer/ValuesViewerConfigPanel.cpp \
     src/shared/Modules/ValuesConverterViewer/ValueElement.cpp \
@@ -75,6 +76,7 @@ SOURCES += \
     src/entrypoints/groundstation/main.cpp
 
 HEADERS += \
+    src/shared/Core/CrashLogger.h \
     src/shared/Modules/Empty/EmptyModule.h \
     src/shared/Modules/ValuesConverterViewer/ValueElement.h \
     src/shared/Modules/ValuesConverterViewer/ValuesViewerConfigPanel.h \
@@ -157,3 +159,5 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin
 
 RESOURCES += \
     src/entrypoints/groundstation/application.qrc
+
+win32: LIBS += -lDbgHelp
diff --git a/src/entrypoints/groundstation/main.cpp b/src/entrypoints/groundstation/main.cpp
index 89623e0274aa2c1b041f5690085fd125d8f25156..2e835c33b56d937b70dbdee60e2437d82cae092e 100644
--- a/src/entrypoints/groundstation/main.cpp
+++ b/src/entrypoints/groundstation/main.cpp
@@ -17,6 +17,7 @@
  */
 
 #include <QApplication>
+#include <Core/CrashLogger.h>
 
 #include "Modules/MainWindow/SkywardHubMainWindow.h"
 
@@ -27,7 +28,19 @@ int main(int argc, char *argv[])
     SkywardHubMainWindow skywardHub;
     application.setStyleSheet(skywardHub.styleSheet());
     skywardHub.show();
+
+#ifdef Q_OS_WIN
+    [&]()
+    {
+        __try {
+            application.exec();
+        }
+        __except(sehFilter(GetExceptionInformation())) {}
+    }();
+#else
+    // TODO: add linux support
     application.exec();
+#endif
 
     return 0;
 }
diff --git a/src/shared/Core/CrashLogger.cpp b/src/shared/Core/CrashLogger.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..366e969371e4e52a46f1ac44eeb88f34da47e5e7
--- /dev/null
+++ b/src/shared/Core/CrashLogger.cpp
@@ -0,0 +1,195 @@
+#include "CrashLogger.h"
+
+/////////////////////// WINDOWS ///////////////////////
+#ifdef Q_OS_WIN
+
+int sehFilter(_EXCEPTION_POINTERS *ex)
+{
+    std::fstream file;
+    QDateTime timestamp = QDateTime::currentDateTimeUtc();
+
+    QString fileName =
+        "crash_log_" + timestamp.toString("dd.MM.yyyy_hh.mm.ss") + ".txt";
+
+    file.open(fileName.toStdString(), std::fstream::out);
+    writeException(file, ex);
+    file.close();
+
+    return EXCEPTION_EXECUTE_HANDLER;
+}
+
+void writeException(std::fstream &file, _EXCEPTION_POINTERS *ex)
+{
+    std::string date =
+        QDateTime::currentDateTimeUtc().toString(Qt::ISODate).toStdString();
+    file << "~~ Exception " << ex->ExceptionRecord->ExceptionAddress << " at "
+         << date << " ~~\n";
+    writeStack(file, ex);
+}
+
+void writeStack(std::fstream &file, _EXCEPTION_POINTERS *ex)
+{
+    constexpr int MaxNameLen = 256;
+
+    BOOL retrieved;
+    HANDLE process;
+    HANDLE thread;
+    HMODULE hModule;
+
+    STACKFRAME64 stack;
+    ULONG frame;
+    DWORD64 displacement;
+
+    DWORD disp;
+    IMAGEHLP_LINE64 *line;
+
+    char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
+    char module[MaxNameLen];
+    auto pSymbol = (PSYMBOL_INFO)buffer;
+
+    CONTEXT ctxCopy;
+    memcpy(&ctxCopy, ex->ContextRecord, sizeof(CONTEXT));
+    memset(&stack, 0, sizeof(STACKFRAME64));
+
+    process = GetCurrentProcess();
+    thread  = GetCurrentThread();
+
+#if !defined(_M_AMD64)
+    stack.AddrPC.Offset    = (*ex->ContextRecord).Eip;
+    stack.AddrPC.Mode      = AddrModeFlat;
+    stack.AddrStack.Offset = (*ex->ContextRecord).Esp;
+    stack.AddrStack.Mode   = AddrModeFlat;
+    stack.AddrFrame.Offset = (*ex->ContextRecord).Ebp;
+    stack.AddrFrame.Mode   = AddrModeFlat;
+#endif
+
+    // Initialize symbol handler for current process
+    SymInitialize(process, NULL, true);
+
+    for (frame = 0;; frame++)
+    {
+        retrieved = StackWalk64(
+#if defined(_M_AMD64)
+            IMAGE_FILE_MACHINE_AMD64,
+#else
+            IMAGE_FILE_MACHINE_I386,
+#endif
+            process,
+            thread,
+            &stack,
+            &ctxCopy,
+            NULL,
+            SymFunctionTableAccess64,
+            SymGetModuleBase64,
+            NULL);
+        if (!retrieved)
+            break;
+
+        pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+        pSymbol->MaxNameLen   = MaxNameLen;
+        SymFromAddr(process,
+                    (ULONG64)stack.AddrPC.Offset,
+                    &displacement,
+                    pSymbol);
+
+        line               = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64));
+        line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+
+        // Try to get line
+        if (SymGetLineFromAddr64(process, stack.AddrPC.Offset, &disp, line))
+        {
+            file << "\tat " << pSymbol->Name << " in " << line->FileName
+                 << ": line: " << line->LineNumber
+                 << " address: " << pSymbol->Address << "\n";
+        }
+        else
+        {
+            // Failed to get line
+            file << "\tat" << pSymbol->Name << ", address: " << pSymbol->Address
+                 << "\n";
+            hModule = NULL;
+            lstrcpyA(module, "");
+            GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+                                  GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+                              (LPCTSTR)(stack.AddrPC.Offset), &hModule);
+
+            // At least print module name
+            if (hModule != NULL)
+                GetModuleFileNameA(hModule, module, MaxNameLen);
+
+            file << "in " << module << "\n";
+        }
+
+        free(line);
+        line = NULL;
+    }
+}
+/////////////////////// LINUX & MACOS ///////////////////////
+#elif Q_OS_LINUX || Q_OS_MAC
+
+/*
+The code should work(untested) until the todo comment.
+
+void exceptionHandler(int sig, struct sigcontext ctx)
+{
+    QDateTime timestamp = QDateTime::currentDateTimeUtc();
+    QString fileName    = QString("crash_log_") +
+                       timestamp.toString("dd.MM.yyyy_hh.mm.ss") + ".txt";
+
+    auto isoDate = timestamp.toString(Qt::DateFormat::ISODate);
+    auto sigStr  = std::to_string(sig);
+    int fd       = open(fileName.toStdString().c_str(), O_WRONLY | O_CREAT);
+
+    // Generate file with current timestamp
+    std::fstream file;
+    void *trace[50];
+    char **messages = (char **)NULL;
+    int i, traceSize = 0;
+
+    write(fd, "~~ Exception at ", 16);
+    write(fd, isoDate.toStdString().c_str(), isoDate.length());
+    write(fd, " , signal: ", 11);
+    write(fd, sigStr.c_str(), sigStr.length());
+    write(fd, " ~~", 3);
+
+    // get void*'s for all entries on the stack
+    traceSize = backtrace(trace, 50);
+
+    // overwrite sigaction with caller's address
+    trace[1] = (void *)ctx.eip;
+    messages = backtrace_symbols(trace, trace_size);
+
+    for (i = 1; i < traceSize; i++)
+    {
+        auto iStr       = std::to_string(i);
+        auto messageStr = messages[i];
+        auto messageLen = strlen(messageStr);
+
+        write(fd, iStr.c_str(), iStr.length());
+        write(fd, ": ", 2);
+        write(fd, messageStr, messageLen);
+
+        /* find first occurrence of '(' or ' ' in message[i] and assume
+         * everything before that is the file name. (Don't go beyond 0 though
+         * (string terminator)*/
+        size_t p = 0;
+        while (messages[i][p] != '(' && messages[i][p] != ' ' &&
+               messages[i][p] != 0)
+            ++p;
+
+        // TODO: change the following code, as we cannot retrieve
+        // the line by running addr2line in the terminal.
+        // See: https://stackoverflow.com/questions/11556321/how-do-i-access-the-addr2line-functionality-within-my-c-program
+
+        char syscom[256];
+        sprintf(syscom, "addr2line %p -e %.*s", trace[i], p, messages[i]);
+        // last parameter is the file name of the symbol
+        system(syscom);
+    }
+
+    close(fd);
+}
+
+*/
+
+#endif
\ No newline at end of file
diff --git a/src/shared/Core/CrashLogger.h b/src/shared/Core/CrashLogger.h
new file mode 100644
index 0000000000000000000000000000000000000000..34ceb33972af5fc84c9de348b6b106c3ef9603d8
--- /dev/null
+++ b/src/shared/Core/CrashLogger.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include <fstream>
+#include <sstream>
+
+#include <QDateTime>
+
+/////////////////////// WINDOWS ///////////////////////
+#ifdef Q_OS_WIN
+
+#include <QDebug>
+#include <Windows.h>
+#include "winnt.h"
+#include <process.h>
+#include <DbgHelp.h>
+#include <format>
+
+/**
+ * Structured Exception Handling (SEH) filter. Used together with __try
+ * and __catch. See: https://learn.microsoft.com/en-us/cpp/cpp/try-except-statement?view=msvc-170
+ */
+int sehFilter(_EXCEPTION_POINTERS *ex);
+/**
+ * Write an exception information and a stacktrace to a file.
+ * @param file The file to write to.
+ * @param ex Exception info pointer (winnt.h).
+ */
+void writeException(std::fstream  &file, _EXCEPTION_POINTERS *ex);
+/**
+ * Write an exception stacktrace to the file. Used by writeException.
+ * @param file The file to write to.
+ * @param ex Exception info pointer (winnt.h).
+ */
+void writeStack(std::fstream &file, _EXCEPTION_POINTERS *ex);
+
+/////////////////////// LINUX & MACOS ///////////////////////
+#elif Q_OS_LINUX || Q_OS_MAC
+
+/**
+ * TODO: add linux support
+ *
+ * #include <io.h>
+ * #include <stdio.h>
+ * #include <execinfo.h>
+ * #include <signal.h>
+ * #include <stdlib.h>
+ * #include <unistd.h>
+ * #include <fcntl.h>
+ *
+ * void exceptionHandler(int sig);
+ */
+
+#endif
\ No newline at end of file