genuine_
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # The Hitchhikers Guide to Windows Error Reporting ### What is Windows Error Reporting(WER)? Windows Error Reporting is a feature Microsoft introduced in Windows XP that allows the Operating System to notify Microsoft of Application faults. The feature has evolved to allow third party enterprises to set custom reporting servers, and application developers to build out custom reports for their applications. It is tightly integrated into the Windows Exception handling mechanism for both user mode and kernel mode components. WER contains several components working together in the OS. It starts with an application crash, then a series of events occur: 1. WER service is started 2. WER determines the cause of the crash and how to process it 3. WER gathers relevant information from the faulted process 4. WER prepares a report of the crash 5. WER queues this report 6. WER sends the report to a remote location A lot happens between 1-6 but we will not be focusing on the entire architecture of WER. To set expectations, we'll mostly be focused on numbers 1-3. If you want to get a head start and understand whats going to be discussed here today you can refer to the MSDN documentation of WER [here](https://docs.microsoft.com/en-us/windows/win32/wer/windows-error-reporting) #### What we will not discuss WER is a very involved process that goes through several layers of modules and services. What we will not discuss here today are the features that involve reporting generation of faults, and setting up corporate report servers. Anything related to the networking portion of WER we'll willfully ignore, however reference material will be provided for the curious. WER is also used to perform Kernel Level dumps but we will not go over this as well. #### What we will discuss We will discuss the internals of how the WER service and third party applications communicate. We will also go over some of the features WER provides application developers that can change the behavior of WER. Exception Handling will be reviewed as it is the foundation from where WER starts. We'll take special interest into how the out-of-process manipulation of a process is accomplished with WER. During this journey we'll also identify some undocumented behaviors and reveal some of the interesting internals of how the WER service operates with its client components. Our example will assume a 64bit OS (Windows 10 1909) and 64bit application, there are some unique differences between 32bit and 64bit applications with regards to Exception handling which we will not discuss. Besides, 32bit is on its way out anyways :) To conclude we'll go over what we did not get to deep dive into and offer some ideas for possible future research. ### An Overview of the WER architecture Before we begin lets familiarize ourselves with an overview of how the WER process looks. ![](https://i.imgur.com/iiJGE1s.png) We'll discuss more in depth most of the steps involved in the above flow graph. Let's review the steps: #### Step 1 An Application crashes and raises an Exception #### Step 2 The Windows Exception Handling mechanism takes over and determines whether WER will be instantiated. The Windows Exception Handling mechanism determines the fault, and performs a series of logical checks to see if the process is critical, an AppContainer, needs to fail fast (exit immediately) and if WER needs to be invoked based on the exception. A lot more happens here, but we'll discuss only what applies to WER. The WER Service is then initialized by the usermode native layer (ntdll). #### Step 3 The WER Service is initialized. It identifies the faulting process and what kind of crash will be reported and spawns the appropriate Werfault.exe or wermgr.exe executables. The WER Service starts the ETW Logging interfaces. The WER Service also establishes the ALPC Port Communication Channel for the WER clients (werfault.exe). #### Step 4 The WER Service then launches the client werfault.exe or wermgr.exe. Werfault.exe is used to perform a snapshot of the faulted application. #### Step 5 The service will then continue to process its input and subsequently load and initialize the WER reporing facilities and load yet another Werfault.exe, this time with the appropriate security context for the faulting process. #### Step 6 The Werfault.exe will now communicate accordingly with the Wer service and determine what needs to be reported and how based on configurations set by the user and application. ### Components of WER There are several components involved with Windows Error Reporting. This section will provide a quick guide as to what component houses what functionality: * **wersvc.dll**: The WER Service implementation. This is executed within an svchost executable and facilitates the reporting and dumping process. * **werfault.exe**: The WER Client application that handles the usermode applications and is the main client for the WER process. * **wermgr.exe**: The WER client involved with writing and uploading WER reports. * **wer.dll**: Implements a subset of the WER reporting and dumping features. * **faultrep.dll**: This DLL exposes functions for Reporting hangs in a user application, and setting exclusions and other Application specific settings. * **werui.dll**: Handles Graphical User Interface application hangs and reporting. The system DLLs `kernel32` and `ntdll` also play a role in initializing WER and we'll go over those in the coming paragraphs. There are several other components such as the control panel items for WER settings and other Operating System utilities like the registry but they won't be discussed thoroughly here. ### Exception Handling Review In order to understand WER we'll need to understand the basics of exception handling in Windows Applications; this will help us understand why and when WER gets involved. We'll start with the most basic example of an exception. The examples are specific to 64bit applications; 32bit exception handling is slightly different and not covered. ```cpp #include <windows.h> int main(int argc, char** argv) { std::print("Raising Error\n"); RaiseException(EXCEPTION_FLT_DIVIDE_BY_ZERO, 0, 0, NULL); std::getchar(); return 0; } ``` > This application throws a division by zero exception allowing the exception to be continuable (second parameter of `RaiseException` API). It uses the `RaiseException` API, however, most exceptions are thrown due to some undesired behavior. Ultimately, `RaiseException` is called to transfer control to the Exception handling mechanisms. > If we debug this application and follow the execution of `RaiseException` this is what we understand: `RaiseException` prepares a properly formatted `EXCEPTION_RECORD` structure and passes it to `RtlRaiseException`. The Exception Record contains the Exception code and location the exception occurred. ```cpp typedef struct _EXCEPTION_RECORD { DWORD ExceptionCode; DWORD ExceptionFlags; struct _EXCEPTION_RECORD *ExceptionRecord; PVOID ExceptionAddress; DWORD NumberParameters; ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; } EXCEPTION_RECORD; ``` > It's worth noting that you can use RaiseException to raise an exception with any information you'd like, from the exception code, flags, address and parameters, for example you can choose to raise an access violation on a variable g_data: > ```cpp DWORD numArgs = 2; const ULONG_PTR ulArgs[2] = { 1, // WRITE violation (ULONG_PTR)&g_data, }; RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, numArgs, ulArgs ); ``` > Args here identifies what kind of access violation ocurred, 0 for read and 1 for write and the location of the exception. > `RtlRaiseException` captures the current Context and saves it to return to later (if applicable). Next, it attempts to locate the Exception Handler for the offending Instruction Pointer through a table-based lookup process. This process is specific to 64bit applications. Refer to [[osronline]](http://www.osronline.com/article.cfm%5Earticle=469.htm), [[Exception Handling]](https://revers.engineering/applied-re-exceptions/) and [[msdn]](https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64?view=vs-2019) for more information on that process. Then it checks for the presence of a debugger, if present the exception is passed to the debugger first via `ZwRaiseException`. *This is typically referred to as the first chance exception for debuggers and exception handlers.* Finally, execution is then passed to `RtlDispatchException`. The screenshot below shows the stack at the time `RtlDispatchException` is about to be invoked, showing that the first parameter contains the `EXCEPTION_RECORD` and the first member `ExceptionCode` being our DIVISION_BY_ZERO exception code `#define STATUS_FLOAT_DIVIDE_BY_ZERO ((DWORD )0xC000008EL)`. ![](https://i.imgur.com/2w60zPw.png) `RtlDispatchException` is responsible for dispatching the exception to the appropriate handlers, this process includes: * Invoke Vectored Exception Handlers * Invoke second chance exception for a debugger to handle if applicable * Determines the Exception Handler for the Exception (yes, again but more involved) * Calls a series of default exception handlers if present via the `ntdll.RtlpExecuteHandlerForException` function: * kernel32.GSHandler * Applications C_default_handler * ntdll's default exception dispatcher * Unhandled Exception filter in kernel32 ** To put this is into a summarized form we can choose to say this: *A first chance exception occurs, then a second chance exception occurs, then the exception is considered unhandled and passed to `kernel32.UnhandledExceptionFilter`* For the usermode portion of exception handling this is pretty much all we need to know for now. WER is transferred control during the `kernel32!UnhandledExceptionFilter` invocation*. *Note: If you intend to debug the exception handling mechanism through a debugger, the debugger must be hidden from the process or you must pass the exception to the application at every chance* ### Where Windows Error Reporting fits in Now that we understand the basics of how an exception is dispatched in usermode, where does WER fit into all this? WER is an optional (opt-in) feature that allows application developers and Microsoft to gather information when applications fail. WER itself can be disabled entirely as a feature, or fine tuned to contain as little or as much information about the Application and the Operating System as possible upon failure of either. We can disable and enable WER altogether through the [registry directly](https://www.lifewire.com/how-do-i-disable-error-reporting-in-windows-2626074), via policy management consoles, or simply through [Powershell cmdlets](https://docs.microsoft.com/en-us/powershell/module/windowserrorreporting/?view=win10-ps). We can also programamatically opt out of WER at runtime using the `SetLastError()` API call. With that being said, WER is invoked when none of the aforementioned exception handlers have handled the exception/fault. WER was specifically designed to report this exception to Microsoft (or other enterprise reporting server). *WER is invoked at the end of exception unwinding when `kernel32.UnhandledExceptionFilter()` is called.* #### Unhandled Exception Filter to WER We now know that WER is transferred control through the `UnhandledExceptionFilter()` routine, let's see this process at a high level. ![](https://i.imgur.com/YB5Kaae.png) We've focused mainly on the decisions that may directly affect WER invocation. Inside of the UnhandledException there are several logical checks that determine whether WER will be invoked. The first is a check to see if the current exception is equal to the fast fail exception error code. Applications can call `RaiseFailFastException` in order to bypass all exception handling mechanisms described earlier. It does so by preparing the proper EXCEPTION_RECORD, signaling the WER Service directly, then terminating the process. If FastFail is not the case, it checks for a debugger and calls `Debugbreak()` if applicable. The current threads Error Mode is then checked. An application can choose to omit itself from WER via a call to `SetErrorMode(SEM_NOGPFAULTERRORBOX)`, in which case the Exception is dispatched and the process is terminated. If these conditions aren't met, then WER is invoked. #### WER Initialization The initializaion of WER begins in kernel32's `WerpReportFault` function. This function sends a signal to the kernel via `NtSetSystemInformation` with the `0xB1` (*SystemWin32WerStartCallout*) SystemInformation Parameter. This internally calls out to an index (0x20) into the win32k Dispatch table. Presumably this is for the werui portion of WER to recover a hung GUI application but would need further research to confirm this. Then `kernel32.WerpReportFaultInternal()` is invoked which takes an `EXCEPTION_RECORD` pointer as its argument. This function is rather large and contains several key pieces of information that's later used in other WER facilities we'll see later. Whats important for initialization is that it prepares a table of information to pass to `ntdll!ReportExceptionInternal()`. Among the data sent is the current Process ID, a handle to a Mapped Section Object and a Handle table containing the Thread handles of the current process. `ReportExceptionInternal` is responsible for starting and querying the WER Service. A partially reverse engineered pseudocode is pasted below: ```cpp __int64 __fastcall ReportExceptionInternal(DWORD ProcessId, HANDLE FileMappingPtr, const void *HandleTableArray, unsigned int HandleTableArraySize, unsigned int a5, _QWORD *a6) { __int64 size; // rbx __int64 result; // rax int nMessageId; // [rsp+20h] [rbp-E0h] BYREF _DWORD v12[349]; // [rsp+24h] [rbp-DCh] BYREF _QWORD v13[176]; // [rsp+5A0h] [rbp+4A0h] BYREF size = HandleTableArraySize; *a6 = 0i64; if ( HandleTableArraySize > 5 ) return 0xC000042Bi64; memset(v13, 0, 0x578ui64); v13[6] = __PAIR64__(ProcessId, a5); LODWORD(v13[0]) = 0x5780550; LODWORD(v13[5]) = 0x20000000; v13[7] = FileMappingPtr; if ( HandleTableArray && (_DWORD)size ) memmove(&v13[8], HandleTableArray, 8 * size); memset(v12, 0, sizeof(v12)); nMessageId = 0x5780550; result = SendMessageToWERService((__int64)v13, (__int64)&nMessageId); if ( (int)result >= 0 ) { if ( (_DWORD)result == 258 ) { result = 0xC0000240i64; } else { *a6 = *(_QWORD *)&v12[11]; result = 0i64; } } return result; } ``` This function prepares a message to send to the WER Service via the `SendMessageToWERService()` function. `SendMessageToWERService()` takes two arguments, the first is a pointer to a user-supplied `WERSVC_MSG` buffer, and the second is the Message ID, which in this case is `0x5780550`. We can see that the `WERSVC_MSG` is a rather large data structure of 0x578 bytes. ## Initializing the WER Service The WER Service (WERSvc) is a SYSTEM level service implemented in the `wersvc.dll` module and runs within the svchost.exe process context. `SendMessageToWERService()` initiates first contact with the WERSvc through an undocumented procedure related to the Windows Notification Facility. Ntdll queries the state of the WERSvc through `ntdll.ZwQueryWnfStateNameInformation()`, then starts the service by sending it the WNF_WER_SERVICE_START UUID via `ntdll.NtUpdateWnfStateData()`. Along with starting the Service, it opens an Event Object named `SystemErrorPortReady`, which the WERSvc uses to send a notification that it has initialized successfully. Then WERSvc opens an IPC channel via an ALPC Port named `\\WindowsErrorReportingServicePort`. Through this port many of the WER clients will send messages to the WERSvc. Now that the Windows Error Reporting Service has started, and it contains information about the faulted process through the `WERSVC_MSG` structure it was sent, it can continue the reporting process. ##### Processing IPC messages Once the LPC Server is spawned it listens for messages through the ALPC Port and processes them inside of the `_ProcessRequest()` function. It's worth noting that this server supports a number of messages and this presents future research opportunities, Specifically the `CheckIfValidPortMessage()` and `DispatchPortRequestWorkItem()` functions which validates and dispatches the received messages respectively. A handwritten pesudocode version of `_ProcessRequest()` is shown below, removing alot of the non essential telemetry. ```cpp bool CWerService::_ProcessRequest(CWerService *this, WERSVC_MSG* WerMsg, int Arg3) { bool result = true; if(!CWerService::CheckIfValidPortMessage(this, WerMsg)) { EtwEvent(); result = false; } WERSVC_MSG msg = new(0x580); this->WerMsgPtr = msg; memcpy(this->WerMsgPtr, WerMsg, sizeof(WERSVC_MSG)) // size = 0x578 if(!TrySubmitThreadpoolCallback(CWerService::StaticDispatchPortRequestWorkItem, this->WerMsgPtr, *(this*)+3)) { delete msg; result = false; //GetLastError and set result failure } return result; } ``` Internally the messages are disaptched by `CWerService::DispatchPortRequestWorkItem()`. In order to stay on track let us focus now what occurs when an application faults. ##### WerFault.exe and Snapshots As previously mentioned, the WerSvc dispatches work items via its `CWerService::DispatchPortRequestWorkItem()` function. When an application faults one of the attempts made is to create a Snapshot of the process. Process Snapshots were introduced in Windows 8.1 as a means to provide developers a more robust and stable way to fork and query another processes' current state, it is an evolution and improvement on the old ToolHelp API's. The WERSvc will attempt to acquire a snapshot of the faulted process by invoking `CWerService::TryCaptureSnapshot()`. It does so by spawning a WerFault.exe process with predefined arguments. In particular the argument `-pss` indicates to WerFault.exe to attempt a Process snapshot. A snippet is shown below: ```cpp result = StringCchPrintfW(ApplicationName, 0x104ui64, L"%ws\\%ws", Buffer, L"WerFault.exe"); if ( (int)result < 0 ) return result; v14 = OpenProcess(0x1040u, 0, a3); if ( v14 ) { TargetHandle = 0i64; v15 = GetCurrentProcess(); if ( DuplicateHandle(v14, a4, v15, &TargetHandle, 6u, 1, 0) ) { v17 = StringCchPrintfW( CommandLine, 0x104ui64, L"%ws %ws %ws %llu %ws %u %ws %u", ApplicationName, L"-pss", L"-s", TargetHandle, L"-p", a2, L"-ip", a3); if ( v17 >= 0 ) { memset_0(&StartupInfo, 0, 0x70ui64); StartupInfo.cb = 112; ProcessInformation.hProcess = 0i64; ProcessInformation.hThread = 0i64; *(_QWORD *)&ProcessInformation.dwProcessId = 0i64; StartupInfo.dwFlags = 1; StartupInfo.lpDesktop = (LPWSTR)&pActivityId; StartupInfo.wShowWindow = 0; v19 = CreateProcessW( ApplicationName, CommandLine, 0i64, 0i64, 1, 0x400u, 0i64, 0i64, &StartupInfo, &ProcessInformation); CloseHandle(v14); if ( v19 ) { v21 = WaitForSingleObject(ProcessInformation.hProcess, 0xFFFFFFFF); ``` >Like many other aspects of WER, allowing snapshots can be configured via the registry or directly in code. For example you can disable Snapshots for certain applications under the `Software\\Microsoft\\Windows\\Windows Error Reporting\\Debug` Key, with a value of `DisableSnapshots` set to 1. > WerFault.exe creates the Snapshot, adjusts the snapshot tokens security, then saves the handle for later. These handles are later used to create a minidump of the faulted application if configuration allows. ##### WerFault and Security Context After the snapshot is taken, the WERSvc will identify the current process' security context. It does this in order to launch a new WerFault.exe that follows the principle of least privilege and offers it just enough permissions to read and dump the memory of the faulted process. This is implemented inside of `faultrep.dll`. After WERSvc calls `CWerService::TryCaptureSnapshot()` it invokes `CWerService::TryReportCrash()`. This function eventually calls to `faultrep.WerpInitiateCrashReporting()`. Here, WER identifies the currently logged on user and Impersonates it, then identifies the current process' Token level and appropriately spawns a new Werfault.exe, passing the `-u` parameter to signify this is a usermode crash. Other parameters include the process ID and a handle to a mapped view. Werfault.exe invokes its `UserCrashMain()` function which will make another call to `faultrep.WerpInitiateCrashReporting()` to initiate the reporting of the faulted application. This process includes creating the directories that the reports go into and gathering the runtime information for those reports. #### Reporting and Communicating with the Wer Service We've gone over the main components involved with instantiating WER and subsequently preparing the faulted process for reporting. However, who is responsible for the actual dumping and reporting of the faulted process? This is done via the IPC mechanism we described earlier, the ALPC Port created by the WER Service. The different components of WER all implement a similar function called `WersvcSendMessage()`. You'll find it in werfault.exe, wer.dll, and faultrep.dll. This function is used when some action that requires elevated permissions needs to be taken, for example, preparing the report directories for the application, querying regsitry keys for configurations, and other elevated tasks. The screenshot below shows the cross referenced functions for `WersvcSendMessage()` inside of the wer.dll module. ![](https://i.imgur.com/acg6YrS.png) This function queries the state of the WER Service, starts it and sends messages using the `NtAlpcSendWaitReceivePort()` API. The messages are then dispatched by the `wersvc.CWerService::DispatchPortRequestWorkItem()` function. #### A note about Security Permissions Handling WER manages to accomplish its task of transferring control to several components that requires different levels of permissions by utilizing a common practice in Windows. Services usually expose interfaces for user mode applications and (IPC mechanisms) utilize Thread impersonation and Discretionary Access Control Lists (DACLs) when creating file system artifacts. Of course, designs like these lend themselves to Privilege Escalation attacks and the attack surface is large with regards to WER. In fact, such an attack was publicized not too long ago regarding the reporting directories here, [Directory Deletion to SYSTEM shell](https://secret.club/2020/04/23/directory-deletion-shell.html). ### Pulling it all together We've gone over how things work when a simple exception is raised and when and how WER is initialized. We've briefly discussed the IPC mechanism used to dispatch work items for elevated tasks and the different components involved during the Error Reporting process. Next we'll go over some of the ways applications can configure WER and how this affects the reporting process and introduces new attack surfaces and ideas.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully