Sy40r3n
    • 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 No publishing access yet

      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.

      Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Explore these features while you wait
      Complete general settings
      Bookmark and like published notes
      Write a few more notes
      Complete general settings
      Write a few more notes
      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 No publishing access yet

    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.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    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
    # Mở đầu Chào mừng các bạn đến với bài viết thứ 6 trong chuỗi bài viết về phát triển mã độc của mình! Trong bài viết này, chúng ta sẽ nói về file Portable Executable (PE), đây là định dạng file thực thi chủ yếu được sử dụng trong hệ điều hành Windows. Việc hiểu rõ về định dạng file PE đóng vai trò quan trọng trong việc nghiên cứu và phát triển mã độc. Yah, bắt đầu bài viết thôi! :face_with_cowboy_hat: <div style="text-align:center;"> <img src="https://steamuserimages-a.akamaihd.net/ugc/954101135156565426/21D9841F8E03ED30D91A7720388E1E8D3A464FC0/?imw=5000&imh=5000&ima=fit&impolicy=Letterbox&imcolor=%23000000&letterbox=false" alt=""> </div> # Portable Executable File Như các bạn đã biết, các file như Word, PowerPoint và Excel đều có định dạng riêng của nó. Định dạng này quy định cách thức dữ liệu được lưu trữ trong file. Thông thường, cấu trúc của một file được xác định bởi các phần đầu được gọi là **header**. Để lấy dữ liệu từ một file cụ thể, ta thường phải phân tích các giá trị trong header của file đó. Trong phần header của một file, thường sẽ chứa các giá trị được gọi là **magic byte**. Về cơ bản, magic byte là một dãy các giá trị byte, đóng vai trò như **signature** (chữ ký) để nhận diện loại file đó. Hình dưới là mô tả tổng quát về định dạng của một file. :alien: ![image](https://hackmd.io/_uploads/H1kyMUXcT.png) Để thực tế, mình sử dụng công cụ `CFF Explorer` để thử mở một file ZIP. Như các bạn có thể thấy, file ZIP này bắt đầu bằng hai kí tự **PK**, đây chính là hai magic byte của file ZIP. ![image](https://hackmd.io/_uploads/BJaVVIQ9p.png) Một ví dụ khác, mình thử mở một file PNG. Dựa vào hình dưới, có thể thấy các magic byte của file PNG là chuỗi **PNG** và chuỗi này bắt đầu từ kí tự thứ 2 tính từ đầu file. ![image](https://hackmd.io/_uploads/r1kMr8Qqa.png) Và hiển nhiên, cấu trúc của file PE cũng khá giống với cấu trúc của các file mà mình đã đề cập trước đó :grin:. Hình ảnh dưới đây minh họa tổng quan về cấu trúc của file PE, nó bao gồm hai thành phần chính là **header** và **section**. Header được thiết kế để lưu trữ thông tin siêu dữ liệu (meta information), trong khi các section được tạo ra để chứa mã nguồn (code), dữ liệu (data) và tài nguyên (resource) cần thiết cho quá trình thực thi file PE. Một số thông tin siêu dữ liệu được có trong các header bao gồm ngày, giờ, và phiên bản của file PE. Các header cũng chứa các con trỏ/offset để xác định vị trí nơi mã nguồn và dữ liệu được lưu trữ. ![image](https://hackmd.io/_uploads/S1mhylE5a.png) ## DOS Header và DOS Stub Bắt đầu của mỗi file PE đều là DOS header và tiếp theo là DOS stub. Mặc dù trong hệ điều hành Windows hiện đại, DOS header và DOS stub không còn đóng vai trò gì quan trọng trong quá trình thực thi file PE, nhưng nếu chúng không còn tác dụng, tại sao chúng vẫn được giữ lại trong file PE? :thinking_face: Để giải thích cho việc này, cùng nhắc lại thời kỳ đầu của thập kỷ 90, khi các dòng hệ điều hành DOS của Microsoft, đặc biệt là MS-DOS, đang phổ biến. Trong thời kỳ đó, hệ điều hành MS-DOS sử dụng định dạng file DOS làm file thực thi. Tuy nhiên, vào năm 1993, Microsoft đã giới thiệu hệ điều hành Windows NT và chuyển sang định dạng file PE cho các ứng dụng Windows. :smiling_imp: DOS header và DOS stub, tuy đã không còn tác dụng trong hệ điều hành Windows hiện đại nhưng nó vẫn được giữ lại trong file PE vì mục đích tương thích. Trong giai đoạn chuyển đổi từ MS-DOS sang Windows NT, việc này giúp đảm bảo rằng file PE có thể hiển thị thông điệp phù hợp khi cố gắng chạy trên hệ điều hành không tương thích, như MS-DOS. DOS header chứa thông tin cơ bản về tập tin PE, và DOS stub chứa một phần mã máy chạy được trên MS-DOS để thực hiện các công việc nhỏ. Nhìn vào hình ảnh dưới đây, ta có thể nhận ra vị trí bắt đầu của DOS header được đánh dấu bằng magic byte MZ, tương ứng với hai giá trị hexa 4D và 5A. Tiếp theo, chúng ta có thể thấy chuỗi "This program cannot be run in DOS mode" xuất hiện trong DOS stub. Chuỗi này được hiển thị để thông báo cho người dùng rằng nếu họ cố gắng chạy PE file trên hệ điều hành MS-DOS thì sẽ không thành công. ![image](https://hackmd.io/_uploads/rJWKxsmqT.png) Một trường quan trọng khác trong DOS header cần chú ý là e_lfanew, có vị trí tại offset 3C từ đầu file PE. Giá trị của nó là vị trí bắt đầu của PE header trong file PE. Thông tin này đóng vai trò trong việc xác định vị trí bắt đầu của PE header. Trong hình dưới, bạn có thể thấy giá trị của e_lfanew là F8. ![image](https://hackmd.io/_uploads/Hk08xxE9T.png) Thử kiểm tra offset tại vị trí F8, chúng ta thấy nó bắt đầu bằng hai kí tự **PE**, đây chính là signature của PE header. ![image](https://hackmd.io/_uploads/rJ_pgeV9p.png) ## PE Header/NT Header Tiếp theo, ta sẽ tìm hiểu các thành phần quan trọng của PE Header, hay còn được gọi là NT Header, dựa vào hình dưới có thể thấy PE Header bao gồm ba phần chính: Signature, File Header, và Optional Header. Bây giờ, hãy cùng mình xem xét chi tiết về từng thành phần này. ![image](https://hackmd.io/_uploads/SJJIbg4qT.png) ### Signature PE header bắt đầu bằng trường Signature, nó có độ lớn 4 byte và mang giá trị là **PE** (giá trị hexa là 0x5045). Nhưng bởi vì tool CFF Explorer hiển thị giá trị ở định dạng little-endian cho nên nó mới hiển thị ngược giá trị trường Signature là 0x4550. ![image](https://hackmd.io/_uploads/rJzqUx456.png) ### File Header Về File Header, nó có tổng cộng 7 trường thông tin, nhưng mình chỉ đề cập đến 5 trường quan trọng trong ngữ cảnh của mã độc. #### Machine Trong File Header, trường Machine giữ giá trị chỉ định loại bộ xử lý mà file PE này được thiết kế để chạy trên. Ở hình dưới, ta có thể thấy giá trị 0x014C sẽ tương ứng với kiểu bộ xử lý Intel i386. Ngoài ra, còn có các bộ xử lý phổ biến khác bao gồm AMD64 (giá trị hexa là 0x8664) cho kiến trúc 64-bit và ARM (giá trị hexa là 0x01C0) cho thiết bị di động và IoT. ![image](https://hackmd.io/_uploads/SJDyO6Xcp.png) #### NumberOfSections Trường NumberOfSections lưu trữ số lượng section có trong file PE. Như ở hình dưới, ta có thể thấy file PE đang được xét hiện tại có 5 section. ![image](https://hackmd.io/_uploads/BkE75pmcp.png) Các section thông thường xuất hiện trong file PE bao gồm: .text, .rdata, .data, .rsrc, .reloc. Dưới đây là chức năng chính của mỗi section: * **.text:** chứa mã thực thi của file PE. * **.rdata:** chứa các dữ liệu hằng (constant) trong file PE. * **.data:** chứa dữ liệu đã được khai báo và định nghĩa. * **.bss:** chứa dữ liệu đã được khai báo nhưng chưa được định nghĩa. * **.rsrc:** chứa các tài nguyên của file PE, có thể là icon, file âm thanh, hình ảnh, ... * **.reloc:** chứa thông tin cần thiết cho quá trình tái định vị (relocation). ![image](https://hackmd.io/_uploads/HkKVc6mcT.png) #### TimeDateStamp Trường TimeDateStamp, với độ dài là 4 byte, được sử dụng để ghi lại thời điểm mà file PE được tạo hoặc chỉnh sửa. Điều này là một phần quan trọng của header, giúp xác định thời điểm cuối cùng khi file được biên dịch hoặc sửa đổi. Tuy nhiên ta cũng phải lưu ý rằng trong khía cạnh bảo mật, các hacker có thể thay đổi giá trị của trường này để làm cho quá trình phân tích tĩnh của file PE trở nên khó khăn hơn. Hành động này có thể dẫn đến sự hiểu lầm về thời gian và gây khó khăn trong việc theo dõi sự thay đổi và cập nhật của file. ![image](https://hackmd.io/_uploads/rJ_v96X5p.png) #### SizeOfOptionalHeader Trường SizeOfOptionalHeader được sử dụng để lưu trữ kích thước của section Optional Header. Nguyên nhân của việc này là do Optional Header không có kích thước cố định; thay vào đó, kích thước của nó có thể thay đổi tùy thuộc vào kiến trúc của các tệp tin PE khác nhau và theo các tính năng cụ thể mà tệp tin PE có hoặc không có. ![image](https://hackmd.io/_uploads/r1-dsTmq6.png) #### Characteristics Trường thông tin cuối cùng của File Header là Characteristics với kích thước là 2 byte. Trường này đại diện cho một số thuộc tính của file PE. Để xem và hiểu rõ các thuộc tính này, ta có thể sử dụng công cụ như CFF Explorer. Công cụ này không chỉ giúp xem tất cả các thuộc tính có thể xuất hiện mà còn cho phép kiểm tra các thuộc tính mà file PE hiện tại đang sở hữu. ![image](https://hackmd.io/_uploads/S1BYhaX56.png) Ngoài ra, ta còn có thể chỉnh sửa các thuộc tính của file PE bằng cách nhấn vào các checkbox bằng công cụ CFF Explorer. Dưới đây là một số thuộc tính quan trọng về file PE: * **File is executable:** thuộc tính này chỉ ra rằng file PE hiện tại là một file thực thi. * **File is a DLL:** thuộc tính này chỉ ra rằng file PE hiện tại là một dynamic-link library (DLL). * **32 bit word machine:** thuộc tính này ta xác định xem file PE hiện tại là 32-bit hay 64-bit. ### Optional Header Còn về Optional Header, nó chứa nhiều trường thông tin quan trọng cho Windows loader khi thực hiện quá trình sao chép và ánh xạ file PE lên bộ nhớ của process. Mình sẽ đề cập đến một số trường quan trọng có trong Optional Header sau đây. #### Magic Trường đầu tiên là Magic có kích thước 2 byte, nếu giá trị của nó là 0x010B thì file PE là 32-bit còn nếu giá trị của nó là 0x020B thì file PE là 64-bit. ![image](https://hackmd.io/_uploads/Hke2hTX9a.png) #### ImageBase Khi Windows loader tạo một tiến trình, nó sẽ sao chép và tải file PE và các section của nó từ đĩa vào bộ nhớ ảo của process. Nhưng trước tiên, nó cần cấp phát không gian trong bộ nhớ ảo. Nhưng làm sao để Windows loader biết nên phân bổ không gian trong bộ nhớ ảo ở vị trí nào để sao chép file PE và các section của nó? Việc này xuất phát từ trường ImageBase trong file PE trong Optional Header. Như ở hình dưới, có thể thấy giá trị ImageBase của chương trình là 0x400000. ![image](https://hackmd.io/_uploads/SJg4Efr56.png) Nhưng khi mình chạy chương trình này lên và kiểm tra image base của nó thì kết quả lại là 0x710000. Hmm, tại sao giá trị ImageBase hiển thị là 0x400000 trong khi giá trị image base thật sự lại là 0x710000? Liệu rằng có sự nhầm lẫn gì ở đây không? :thinking_face: ![image](https://hackmd.io/_uploads/rJovVMrqT.png) Câu trả lời là không! Bản thân giá trị của trường ImageBase chỉ là một giá trị tham khảo thôi, Windows loader khi nạp file PE lên bộ nhớ của process có thể sử dụng giá trị này hoặc không tùy thuộc vào tình huống cụ thể. Vậy thì trong tình huống nào thì Windows loader không sử dụng giá trị của trường ImageBase? :thinking_face: Giả sử Windows loader muốn nạp một file PE lên bộ nhớ, giá trị ImageBase của file PE này là 0x400000. Tuy nhiên, trong quá trình tạo một process hoàn chỉnh, không chỉ Windows loader nạp module tương ứng với file PE mà còn phải nạp các module khác, chẳng hạn như DLL và mapped file. Điều này làm cho địa chỉ 0x400000 **có thể đã được sử dụng bởi một module khác**, khiến cho Windows loader phải tìm một vị trí khác để nạp file PE lên. Vì lý do này, giá trị của trường ImageBase chỉ được tham khảo bởi Windows loader và có thể được điều chỉnh để tránh xung đột địa chỉ khi nạp các module khác vào không gian bộ nhớ. #### AddressOfEntryPoint AddressOfEntryPoint là một trường trong Optional Header, với kích thước 4 byte. Nó chứa địa chỉ Relative Virtual Address (RVA) của điểm bắt đầu thực thi mã máy, đó là vị trí mà quá trình thực thi chương trình bắt đầu trong không gian bộ nhớ ảo. ![image](https://hackmd.io/_uploads/HyaJ6p7q6.png) Vừa rổi mình có nhắc đến từ khóa Relative Virtual Address (RVA), vậy thì nó là gì ? :thinking_face: Ý nghĩa của Relative Virtual Address (RVA) đúng như tên gọi của nó, đó là một địa chỉ ảo tương đối. Nó được tính toán từ vị trí Image Base của module trong không gian địa chỉ ảo của process. Ngoài ra, có một loại địa chỉ khác được gọi là Virtual Address (VA). Khác với RVA, VA là địa chỉ ảo tuyệt đối, được tính từ vị trí bắt đầu của không gian địa chỉ ảo. Hình dưới mô tả ý nghĩa của RVA cùng với VA một cách trực quan. ![image](https://hackmd.io/_uploads/SJZONeE5a.png) Dựa vào hình trên, ta có thể dễ dàng suy ra mối liên hệ giữa VA và RVA như sau: $$ VA = Image Base + RVA $$ #### SizeOfImage SizeOfImage là một trường trong Optional Header. Nó thể hiện độ lớn của module (file PE) khi được nạp vào bộ nhớ của process. Lưu ý rằng độ lớn của module tương ứng với file PE khi nó được ánh xạ vào bộ nhớ của process sẽ khác với kích thước của file PE trên đĩa. ![image](https://hackmd.io/_uploads/rJHWDCQ96.png) #### SizeOfHeaders Trường SizeOfHeaders có kích thước 4 byte, dùng để lưu trữ độ lớn của tất cả các header có trong file PE. ![image](https://hackmd.io/_uploads/BJ_1vRQca.png) #### Data Directory Data Directory chứa thông tin về độ lớn (size) và Relative Virtual Address (RVA) của một vị trí cụ thể trên bộ nhớ, thường là các bảng hoặc thư mục quan trọng trong file PE. File PE thường có tổng cộng 16 Data Directory. Trong trường hợp một Data Directory không tồn tại trong file PE, kích thước và RVA của nó được thiết lập là 0. Một số Data Directory quan trọng bao gồm Export Directory và Import Directory, có vai trò quan trọng trong quá trình loader của Windows khi nạp file PE lên bộ nhớ. Ngoài ra, Resource Directory cũng là một Data Directory quan trọng, chứa thông tin về tài nguyên như icon và dữ liệu ẩn trong file PE, là những phần mà chúng ta cũng nên theo dõi. Khi sử dụng công cụ CFF Explorer để xem thông tin của Data Directory, ngoài việc cung cấp độ lớn và Relative Virtual Address (RVA), nó cũng giúp xác định Data Directory đó thuộc về phần (section) nào trong tệp tin. Điều này làm cho quá trình tra cứu trở nên thuận tiện hơn, giúp ta dễ dàng định vị và theo dõi thông tin liên quan đến các Data Directory trong file PE. ![image](https://hackmd.io/_uploads/Hy_iEAXca.png) ### Section Table Section Table là một thành phần cực kỳ quan trọng trong file PE. Nhờ có nó, Windows loader mới xác định được vị trí của các section trong file PE, và cũng nhờ vào nó, Windows loader mới có thể biết được nên nạp các section vào những vị trí cụ thể nào trên bộ nhớ. Section Table bao gồm nhiều entry, mỗi entry chứa các trường thông tin mô tả một section cụ thể. Bây giờ, mình sẽ giải thích chi tiết các trường thông tin quan trọng có trong một entry của Section Table. #### Name Trường đầu tiên là Name, nó lưu trữ tên của section. Trường này có độ lớn đối đa là 8 byte cho nên việc này cũng đồng nghĩa là tên của section cũng chỉ có tối đa là 8 kí tự. #### Virtual Size & Virtual Address Virtual Size và Virtual Address lần lượt là độ lớn và RVA của section trên không gian địa chỉ ảo của process. RVA là gì thì mình đã có giải thích ở trên rồi :grin:. Hình dưới đây mô tả một cách trực quan về trường Virtual Size và Virtual Address của Section 2. ![image](https://hackmd.io/_uploads/r1w7AkN9a.png) #### Raw Size & Raw Address Thông tin tiếp theo là Raw Size và Raw Address. Raw Size là độ lớn của section và Raw Address là offset của section tính từ đầu file PE trên đĩa. Hình dưới mô tả ý nghĩa của Raw Size và Raw Address. ![image](https://hackmd.io/_uploads/Bye50kEqp.png) Dựa vào tính chất của Virtual Address và Raw Address, ta có thể suy ra mối liên hệ giữa chúng. Để hình dung dễ hơn, mình đã chuẩn bị sẵn hình minh họa dưới đây. ![image](https://hackmd.io/_uploads/HyGY37BqT.png) Nếu mình gọi Denta chính là **khoảng cách của một điểm dữ liệu nào đó trong Section 2 tính từ đầu Section 2** thì dựa vào hình vẽ ta có thể tính Denta theo công thức: $$ Denta = Offset - Raw\:Address $$ Hoặc $$ Denta = RVA - Virtual\:Address $$ Ở đây, Offset là offset của điểm dữ liệu trong Section 2, Raw Address là offset của Section 2. RVA là RVA của điểm dữ liệu, Virtual Address là RVA của Section 2. Bằng cách đặt hai biểu thức này bằng nhau, ta thu được mối liên hệ giữa Offset và RVA: $$ Offset - Raw\:Address = RVA - Virtual\:Address $$ Công thức trên được sử dụng để chuyển đổi giữa giá trị offset của một điểm dữ liệu và RVA, hoặc ngược lại. Công thức này sẽ xuất hiện thường xuyên trong các bài viết tiếp theo, vì vậy bạn đọc nên lưu ý đến nó. #### Characteristics Và cuối cùng là trường Characteristics, trường này sẽ giúp ta xác định các thuộc tính của một section. Các thuộc tính này có thể là các quyển truy cập của một section. Hình dưới mô tả tất cả các tính chất có sẵn của một section bằng công cụ CFF Explorer. ![image](https://hackmd.io/_uploads/SJXVQ4Hcp.png) Trong ngữ cảnh về mã độc, ta cần phải quan tâm một số thuộc tính như sau: * **Is shareable:** section có thể được chia sẻ giữa nhiều process. * **Is executable:** section chứa mã thực thi. * **Is readable:** section có thể đọc. * **Is writeable:** section có thể ghi. * **Contains code:** section chứa mã máy. * **Contains initialized data:** section chứa dữ liệu được khởi tạo. * **Contains Uninitialized data:** section chứa dữ liệu chưa được khởi tạo. ## Kết luận :::warning :zap: Lưu ý rằng, bài viết chỉ mang tính chất giáo dục và không khuyến khích việc sử dụng thông tin để thực hiện các hoạt động xấu hay bất hợp pháp. Nếu có thắc mắc hay ý kiến, đừng ngần ngại chia sẻ với mình để làm cho bài viết trở nên tốt hơn. ::: ## Tham khảo * [https://0xrick.github.io/win-internals/pe5/](https://0xrick.github.io/win-internals/pe5/) * [https://learn.microsoft.com/en-us/previous-versions/ms809762(v=msdn.10)?redirectedfrom=MSDN](https://learn.microsoft.com/en-us/previous-versions/ms809762(v=msdn.10)?redirectedfrom=MSDN) * [https://en.wikipedia.org/wiki/MS-DOS](https://en.wikipedia.org/wiki/MS-DOS) * [https://en.wikipedia.org/wiki/Windows_NT](https://en.wikipedia.org/wiki/Windows_NT)

    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
    Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    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