LJP-TW
    • 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
    --- title: Reversing.kr Write-up 1 description: 一些 Reversing.kr 篇幅不長的 Write-up 集中地 tags: CTF, Reversing lang: zh_tw --- # 🚩 Reversing.kr Write-up 1 [TOC] ## Easy_CrackMe ### 👀 Analyze the decompiled code 靜態分析嘛,先想辦法到比較關鍵點的 code 先執行看看 ![](https://i.imgur.com/SZmil9H.png) ok 有呼叫一個視窗出來講 Incorrect Password 這次用 Ghidra 這工具,跟它培養一下感情 ![](https://i.imgur.com/jTjJkit.png) 從旁邊的 Symbol Tree 找到 MessageBoxA 這個創造視窗的 API 對它右鍵 -> Show References to 找到哪邊有 call 到它 就找到了這邊 ![](https://i.imgur.com/XsHrt7I.png) 尾巴就看到了 Congratulation 跟 Incorrect Password 兩種結局 那要怎樣才能進到好結局勒 從 Line 27 可以知道 cStack99 要是 a 從 Line 49 知道 cStack100 要是 E 從 Line 28 知道 acStack98 兩個字要是 5y ![](https://i.imgur.com/i1zkVf7.png) 這樣就組出 Ea5y 了 在 Line 29 ~ 47 的 if 中定義 pbVar4 是 0x0040606c ![](https://i.imgur.com/clPrO0h.png) pbVar2 是 abStack96 這兩個字串會一直比到結束,一次比 2 bytes,都需一樣 所以知道 abStack96 必須是 R3versing 合起來整個從 cStack100 開始的字串就是 就是答案 ## Easy_KeygenMe 這支程式 run 一下 ![](https://i.imgur.com/VhpjZKO.png) 會要求輸入 Name 和 Serial 題目的 README 寫著要找到對應 Serial 為 **5B134977135E7D13** 的 Name ### 👀 Analyze the decompiled code OK,那先從找哪邊用到`Input Name:`跟`Input Serial:`這兩個字串開始 用 Ghidra 打開,跑一下 Auto Analyze ![](https://i.imgur.com/9BSyP4X.png) 主要要選到 ASCII Strings 跑完分析後,接著按一下 Window -> Defined Strings 搜尋字串 `Input Name` ![](https://i.imgur.com/gbcbCRq.png) 對 00408060 辣個 Address 點兩下左鍵 ![](https://i.imgur.com/WTaPosD.png) 再對 code 裡面的 00408060 辣個 Address 點右鍵 -> References -> Show References to Address 找到哪邊有用到這個字串ㄌ 就在 0040102e ![](https://i.imgur.com/FJyFNVE.png) 一樣對 Address 左鍵點兩下可以快速移動到該 Address 旁邊就有 Decompile 的 code 可以參考 嗯~ Ghidra 這免費分析工具不錯用 ![](https://i.imgur.com/TF2rRPG.png) ~~FUN_print FUN_scan 是我改名過後的~~ 可以看到 Line 87 ~ 101 的 do while 結束後 如果中間完全沒直接跳到 LAB_00401113,老老實實的執行完迴圈 會因為 Line 102 的 code,進入 Line 105 ~ 106 輸出 Correct 否則就會輸出 Wrong 所以**如何才會輸出 Correct** 這個問題就變成在問 **要怎樣才能老老實實的執行完迴圈 ?** Line 90 中, bVar2 的值其實是 \*pbVar3 所以 Line 90 其實在比對 pbVar3 跟 pbVar7 這兩個地址中的值 - 如果不一樣 - 若 bVar10 是 1 (true),最終 iVar4 會是 0 - 1 = -1 - 最終輸出 Wrong - 若 bVar10 是 0 (false),最終 iVar4 會是 1 - 0 = 1 - 最終輸出 Wrong 說到底,進到這裡就表示最終輸出都是 Wrong 所以輸出 Correct 的條件之一是 **\*pbVar3 跟 \*pbVar7 需相同** 接著看到 Line 95,比對到 \*pbVar3 是 0 就結束比對,輸出 Correct 不是 0 的話,後面就得還要繼續比對 Line 96 ~ 98 根本是 Line 88 ~ 94 的翻版 只是比較對象是 \*pbVar3\[1\] 跟 \*pbVar7\[1\] Line 99 ~ 100 將兩個地址 pbVar3 pbVar7 都直接加 2 --- 總結這一個 Block (Line 87 的 do while ~ 最後) 的 code 在幹嘛 翻譯為人類聽得懂的話,就是 **比對 pbVar3 pbVar7 這兩個地址上的字串,一次比對兩個字 都一樣就輸出 Correct,反之輸出 Wrong** --- 繼續往上看 Line 85 ~ 86 寫 pbVar7 為 abStack176 pbVar3 為 abStack276 所以我說 abStack176 abStack276 這兩個地址上的字串是啥勒? --- 先來看看 FUN_scan 是啥好了 owo ![](https://i.imgur.com/MWqNTV8.png) call 了 FUN_scan("%s", ESP + 0x10) ![](https://i.imgur.com/eMFoBK0.png) FUN_scan(param1, param2) 又繼續 call 了 FUN_00401c8e(00408090、param1、&param2) 也就是 FUN_00401c8e(00408090、"%s"、&param2) --- 進到 FUN_00401c8e 我傻眼 781 行 code 不過還是耐住性子看一下 整個 code 被一個超大 do {} while(true) 包住 ![](https://i.imgur.com/wcDzeVw.png) 首先第一個 block 目光來到 Line 70 ~ 79 bVar1 在 Line 63 可以看到,是第二個參數,也就是 "%s" ~~在此我有不祥的預感,該不會這裡實作了一個簡單的 scanf 吧~~ 如果 bVar1 等於 0,就會進這判斷式,不過因為我們ㄉ bVar1 不是 0 就先不看裡面做什麼了 ~~反正不會進去麻~~ 接著第二個 block 看到 Line 80 ~ 90 比較 \*(int \*)0x408680 裡的值是否 < 2 切到 0x408680 看到是 0x00000001 所以一開始會執行 Line 81 ~ 82 的 code this_02 在 Line 67 定義為 0x408468,而 \*0x408468 為 0x408472 而 bVar1 為 \*param_2,單位是 1 byte,所以這邊 bVar1 會是 0x25 也就是 `%` 的 ASCII code 從地址 (0x408472 + 0x25 \* 2) 取一個 byte 出來並且 and 8 這就是 uVar4 !! (好累 汗顏@@) --- 好到這邊 我已經不想靜態分析了 XD 直接動態看一下,搞不好還比較簡單 ### 💩 Dynamically Analyze 雖然說是動態分析,但若在靜態分析過後大概知道關鍵點是啥會更好 另外一點就是,Decompiler 只能參考,反編譯回去時可能跟原意有些許差距 好,現在還不知道 FUN_print 跟 FUN_scan 的作用 先在他們頭上設斷點 ![](https://i.imgur.com/3qN4H80.png) 來到 Call FUN_print 前 ![](https://i.imgur.com/ybshjAQ.png) 按一下 F8 步過 ![](https://i.imgur.com/StIa0U9.png) 可以發現 Terminal 輸出文字ㄌ 所以大膽猜測,FUN_print 就是輸出 Stack 中第一個 pointer 所指向的字串 --- 再來看看 FUN_scan 來到 Call FUN_scan 之前 ![](https://i.imgur.com/D9Wfeb1.png) 按一下 F8 步過 ![](https://i.imgur.com/nuc92HK.png) 發現 Terminal 可輸入東西 輸入進去後 ![](https://i.imgur.com/2yS0Sg4.png) 很像 scanf(format, param1, param2 ...) Stack 中第一個參數是 \"%s\" 第二個參數是 0x0018FE20 可以看到右下角 Stack 輸入字串被存到 0x0018FE20 了 --- 那後面的邏輯是啥勒 ? 還是參考一下 Decompiler ![](https://i.imgur.com/NwKIqJx.png) 剛剛下的斷點分別是 Line 46, 47 所以接下來會執行的 code 是從 Line 48 開始 首先看到 Line 52 ~ 57 就是在算 pcVar8 所指的字串的長度 長度會存到 \~uVar5 Line 58 的條件式形同 \~uVar5 > 1 再來 Line 59 ~ 74的 do while 中 第一個區塊是 Line 60 ~ 62 的 if 配合 Line 50、66,讓 iVar6 一直循環 0 1 2 0 1 2 ... if 過了之後 call FUN_00401150 這個 function 不知道在幹嘛,就很適合下斷點看看 Line 49、64 定義 iVar4 從 0 開始每 loop 一次就 ++ 一次 Line 68 ~ 73 又在算一次字串長度 最後 Line 74 目的是要把字串整個 loop 過一遍 --- 好,來下斷點 斷在要 call FUN_00401150 之前 不過下斷點時,發現了一件在 decompiler 沒發現的事 ![](https://i.imgur.com/Tyeqk3W.png) 0x0040107A 對應 Line 60 ~ 62 的 if 但是,but,西咖喜 0x0040107E 的 movsx ecx ... 到 0x00401088 的 xor ecx, edx 好像沒在 decompiler 中有對應的 code ?! 那就先動態分析看看這邊在幹嘛 新設一個斷點在 0x0040107E 的 movsx ecx, ... 並且執行到它 ![](https://i.imgur.com/wVEMBql.png) esp + esi + C 此時等於 0x0018FE1C,值是 0x10 注意到這邊,esi 會循環 0 1 2 0 1 2 ... 所以 \[esp + esi + C\] 的值會循環 10 20 30 10 20 30 ... 在來 esp + ebp + 10 此時等於 0x0018FE20,也就是輸入字串 所以輸入的字串,會跟 0x10 0x20 0x30 這把 key xor 然後 call 進辣個神秘 function FUN_00401150 --- 再來執行到 call FUN_00401150 之前 ![](https://i.imgur.com/fPDcW6e.png) 可以看到 Stack 中 第一跟第三個參數一樣,為0x0018FE84 ![](https://i.imgur.com/y4QvWhn.png) 那邊都是 0 第二個參數是一個像是 format 的東西 \"\%s\%02x\" 第四個參數是剛剛 xor 完的產物 0x71 按一下 F8 步過 ![](https://i.imgur.com/140PZrG.png) 歐歐歐歐歐 ![](https://i.imgur.com/09X2sjx.png) 剛剛 xor 的東西存來這了,而且是把 0x71 轉成字串 "71" 存起來 --- 我們看看這個 do while 進到第二圈 並且再度停在 call FUN_00401150 之前 ![](https://i.imgur.com/g8pjScc.png) Stack 中的參數第一二三個參數依舊沒變 第四個參數也是 0x61 (a 的 ascii code) 跟 0x20 xor 的結果 0x41 按一下 F8 步過 ![](https://i.imgur.com/BWTl1r6.png) 看看 0x0018FE84 ![](https://i.imgur.com/qlGkyun.png) 把 xor 的產物 0x41 轉成字串 "41" 黏在字串後面了 --- 整個 do while 都執行完 可以斷點斷在 0x004010B6 ![](https://i.imgur.com/7W2SI1o.png) 看看 0x0018FE84 ![](https://i.imgur.com/UbxHVby.png) 的確就是輸入字串 aaaabbbbccccdddd 跟 0x10 0x20 0x30 依序下去 xor 的產物轉成字串 --- 再來設斷點在輸入 serial ![](https://i.imgur.com/eunWECS.png) 按下去 F8 輸入 ![](https://i.imgur.com/hPC1LT0.png) ![](https://i.imgur.com/0YU8L5m.png) 最後可以看到是前一 part 有分析出來的比較字串的部分 兩個字串的頭分別是剛剛輸入的 serial 跟 0x0018FE84 ### 🚩 Got it 到目前為止已經知道怎解了 題目的 serial 是 **5B134977135E7D13** 就只要依序 xor 回去就好了 ## Easy_UnpackMe ### 🤔 Pack Unpack ~~完全不知道是啥的可以查一下 `UPX`~~ ~~Pack 中文翻加殼~~ ~~Unpack 為脫殼~~ ~~後面加 er 就變加殼器/脫殼器~~ --- 我們先講講 **流程** : 假設原本我們的程式是 A Packer 會把程式給**壓縮**起來,變成 B 並且把程式一開始的進入點改成一段**解壓縮**的 code,這裡用 C 來表示 關於程式進入點 Windows exe 可以 google `PE Header` Linux elf 則可以 google `ELF Header` | 程式名稱 | Binary 實際結構 | | -------- | -------- | | 原本程式 | A | | 加殼後的程式 | C, B | 運行時,會先執行**解壓縮**的code,將程式變回原來的樣子 | 程式名稱 | Binary 實際結構 | | -------- | -------- | | 執行完解壓縮 | C, A | 解壓縮完後,JMP 到原本程式 A 執行原本要做的事情 --- **為啥要 Pack ?** 1. 反防毒: 首先先問問 Anti-Virus 怎麼運作的 ? ~~雖然我也不是很了解,但我猜~~ 有一種方式是看有沒有特定的 pattern 出現 比如說某些 pattern 就是開後門的 code 的長相 那 Packer 壓縮原本的程式後,整個 pattern 就被打散了,達到反防毒的目的 2. 反逆向: 好饒舌,反逆向 原本沒 Pack 的程式,裡面的 OPcode 形同裸體,有心的人必能事成將它的邏輯搞懂 Pack 後的程式,逆向攻城屍得先想辦法將它 Unpack,Unpack 不出來就連逆都不用逆 3. 壓縮: Packer 不只形同加密般把原本程式變成亂碼,還可以將 size 壓縮變小 ### 👀 So, how to unpack ? - 第一種方法 看有沒有別人寫好的工具 不過你也得先知道對應的 Packer 是什麼,才能找 Unpacker PEiD 就是個 Packer Detector 查殼器,自己去下載ㄅ 不過它也不是萬用的,有些殼是程式創作者自己寫的,這種 Custom Packer 就查不到了 ![](https://i.imgur.com/5iW8iSw.png) 像是這題就查不到 - 第二種方法: 用 x32/x64dbg 執行到真正的進入點 套用前章節的說法 > 運行時,會先執行**解壓縮**的code,將程式變回原來的樣子 | 程式名稱 | Binary 實際結構 | | -------- | -------- | | 執行完解壓縮 | C, A | 一直逐步執行直到真正的進入點 (所謂真正的進入點,就是進入 A 的點) 此時程式都解壓縮回真正的樣貌了 再把整個程式從 Memory dump 出來,並且將 Header 裡的進入點修改到真正的進入點 就脫殼完成 ### 😐 Entry Point, but not OEP 這題呢,來示範第二個方法 首先用 Ghidra 打開程式,看看 PE Header,來實際算一次進入點在哪 參考 PE Header 架構圖 ![](https://upload.wikimedia.org/wikipedia/commons/1/1b/Portable_Executable_32_bit_Structure_in_SVG_fixed.svg) 好像很長很可怕,但實際上要看的只有 - offset 0x28 的 Address Of Entry Point - offset 0x34 的 Image Base 兩者相加,就是 Entry Point 看看 Ghidra 畫面 ![](https://i.imgur.com/ut2KVBa.png) 最前面這部分是 DOS Header 0x3c 的地方表示 Pointer to PE Header 看到 0x3c 上面的值是 0xd8 移駕到 0xd8 看一下 對著有地址的這個視窗按 G,並輸入 0x4000d8 就可以快速移動 ![](https://i.imgur.com/mrcHeO9.png) OK 我們要看的地方是 - offset 0x28 的 Address Of Entry Point - offset 0x34 的 Image Base 兩個地方分別加上 offset 0xd8 也就是 0x100 跟 0x10c ![](https://i.imgur.com/Z7lTAcF.png) ![](https://i.imgur.com/iV79Sdg.png) 注意 Little-endian 所以這兩個值分別是 - 0x00a04b: Address Of Entry Point - 0x400000: Image Base 相加就是 0x40a04b 看看 0x40a04b 的 code ![](https://i.imgur.com/VfUy1eF.png) Ghidra 很貼心的在上面用 entry(void) 命名這段區間的 code ### 🤘 OEP !! 不過剛剛算的那個不是真的 OEP(Original-Entry-Point) 蛤,你問我怎麼知道 ? ~~因為 0040a04b 不是 FLAG 啊~~ 那那那麼,0x40a04b 這個地址的 code 在幹嘛??? 動態分析看看好了,丟到 x32dbg ![](https://i.imgur.com/uLTnwWP.png) 剛開起來就自動停在 0x40a04b 了 因為 x32dbg 會很貼心自動在 entry 下一個斷點 你可能會說: 欸欸乾 剛剛entry**算心酸**的喔 沒有啦~ 想說,帶你了解一下 entry 真正是怎麼算的麻~~ 好,回正題,這邊的 code 我是沒有好好的 reverse 完 你可以好好的 reverse 這邊的 code 當練習 但我看到一堆 xor,我猜這邊就是在做前面說的**解壓縮**的步驟 只是如果只是 xor,那就不能說是**解壓縮**,而是**解密** anyway,這一段 code 我猜就是將 Packed 程式 B 還原回原本程式 A 的過程 那麼,最後要一個 JMP to A 的指令吧 ? 把這段 code 一直往下拉,拉阿拉.... ![](https://i.imgur.com/4aJh6lg.png) 看來很可疑的 JMP,斷點設在辣邊,然後看看會跳到哪邊 噢噢跳到了這裡 ![](https://i.imgur.com/xsk9COI.png) (碼掉了,自己的 flag 自己找) 我們為了方便下面行文,辣個 OEP 我們就說是: **004011XX** 基本上到這裡,這題就結束了,不過 你不覺得很好奇嗎 1. 那原本沒執行 C 時,**004011XX** 這邊長怎樣? 2. 前面方法 2 提到的 > 把整個程式從 Memory dump 出來,並且將 Header 裡的進入點修改到真正的進入點 怎麼做? ### 👀 The encrypted code at 004011XX 簡單一個截圖說明一切: ![](https://i.imgur.com/bQ0BLcC.png) 截自 Ghidra,還沒被還原時,長相長這樣 可以對比前章節已經還原後的 004011XX OPcode 根本不一樣,表示說還原的步驟的確有對原程式動到手腳 ### 😎 Dump the unpacked exe 這裡不得不說 x32dbg 真D好用 當 x32dbg 已跳到 OEP 時 從外掛程式(Plugin) -> Scylla ![](https://i.imgur.com/tz36rsU.png) 剛開起來長這樣 依序點 IAT Autosearch -> Get Imports -> Dump 假設輸出檔名為 Easy_UnpackMe_dump.exe 可以執行看看 ![](https://i.imgur.com/GBewCdq.png) 此時還執行不起來 沒關係,再點 Fix Dump 選擇 Easy_UnpackMe_dump.exe 可以看到下面 Log 多了一條 ![](https://i.imgur.com/96iOzRj.png) 再執行看看 Easy_UnpackMe_dump_SCY.exe 應該就是可以執行的了吧~~ 再把它丟到 x32dbg 就可以發現一開始就直接進到 **004011XX** 你也可以自己用 PE Header 的欄位算算看是否是這個位置 這表示這支程式進入點是直接到 **004011XX** 這支程式已經是原本的程式,而非 Packed 程式 已經不需要經過**還原回原本程式**的 code 了 ### Reference [Dump the unpacked exe](https://goggleheadedhacker.com/blog/post/6) [PE Header](https://upload.wikimedia.org/wikipedia/commons/1/1b/Portable_Executable_32_bit_Structure_in_SVG_fixed.svg) ## Easy_ELF ### 👀 Analyze 就在 Linux 中動態分析 發現一些簡單的 xor 囉 ![](https://i.imgur.com/WFy24Ii.png) 斷點斷在 0x8048380 然後一直 follow follow follow 追到 \_\_libc\_start\_main+237 時 會跳到主要的 code ![](https://i.imgur.com/cSzgaNq.png) 輸入 si 走進去看看 ![](https://i.imgur.com/eTxnCc3.png) 發現跳到 0x804851b 用 objdump 看一下這邊的 code ![](https://i.imgur.com/YvW75CH.png) 前面有三個連續 call 分別 call 了 - write@plt 輸出訊息 - 0x8048434 - 0x8048451 ### 👀 0x8048434 ![](https://i.imgur.com/xMp38Zq.png) 只是一個 scanf format 是 \"\%s\" 存到 0x804a020 ### 👀 0x8048451 ![](https://i.imgur.com/pTpBZIC.png) 這個小多 看看能不能變出一個圖形化的東西來看 這邊用 radare2 ``` r2 Easy_ELF ``` 輸入 s 跳到這個地址 ``` s 0x8048451 ``` 跑一下分析,等等才能產生圖 ``` aaa ``` 最後來看圖 ``` VV ``` ![](https://i.imgur.com/vBiJTSJ.jpg) 到這個畫面,可以用方向鍵來移動 ![](https://i.imgur.com/xH7tnTc.jpg) 目標是要進到右下角 mov eax, 1 的區塊 然後 return,表示成功驗證 到這邊之後,就很好解了 <!-- --> <style> /* fix mathjax rwd scroll * #Research-direction > simple model */ ul > li > .mathjax { overflow-x: scroll; overflow-y: hidden; overflow-wrap: break-word; display: inline-block; } #doc > ul:nth-child(38) > li:nth-child(4) > ul > li > .mathjax { width: 100%; } /* Dark mode */ /* <!-- todo: fix highlight.js blocks; some code blocks do not render correctly --> */ .navbar-default { background-color: #091a22; } .navbar-default .navbar-brand, .ui-infobar { color: #ebebeb; } body { background-color: #23272a !important; } .ui-view-area { background: #23272a; color: #ddd; } .ui-toc-dropdown { background-color: #23272A; border: 1px solid rgba(255,255,255,.15); box-shadow: 0 6px 12px rgba(255,255,255,.175); } .ui-toc-dropdown .nav > li > a { color: #ccc; } .ui-toc-dropdown .nav > .active:focus > a, .ui-toc-dropdown .nav > .active:hover > a, .ui-toc-dropdown .nav > .active > a { color: #bbb; } .ui-toc .open .ui-toc-label { color: #777; } table * { background-color: #424242; color: #c0c0c0 } button, a { color: #64B5F6; } a:hover, a:focus { color: #2196F3; } a.disable, a.disable:hover { color: #EEEEEE; } /* Dark mode code block */ /* Imported from titangene/hackmd-dark-theme */ .markdown-body pre { background-color: #1e1e1e; border: 1px solid #555 !important; color: #dfdfdf; font-weight: 600; } .token.operator, .token.entity, .token.url, .language-css .token.string, .style .token.string { background: unset; } /* Dark mode alert boxes */ .alert-info { color: #f3fdff; background: #40788A; border-color: #2F7A95; } .alert-warning { color: #fffaf2; background: #936C36; border-color: #AE8443; } .alert-danger { color: #fff4f4; background: #834040; border-color: #8C2F2F } .alert-success { color: #F4FFF2; background-color: #436643; border-color: #358A28; } /* Stylized alert boxes */ .alert-danger>p::before { content: "❌ Dangerous\A"; } .alert-warning>p::before { content: "⚠ Warning\A"; } .alert-info>p::before { content: "ℹ Information\A"; } .alert-warning>p::before, .alert-danger>p::before, .alert-info>p::before { white-space: pre; font-weight: bold; } </style> <style> /* * Visual Studio 2015 dark style * Author: Nicolas LLOBERA <nllobera@gmail.com> */ .hljs { display: block; overflow-x: auto; padding: 0.5em; background: #1E1E1E; color: #DCDCDC; } .hljs-keyword, .hljs-literal, .hljs-symbol, .hljs-name { color: #569CD6; } .hljs-link { color: #569CD6; text-decoration: underline; } .hljs-built_in, .hljs-type { color: #4EC9B0; } .hljs-number, .hljs-class { color: #B8D7A3; } .hljs-string, .hljs-meta-string { color: #D69D85; } .hljs-regexp, .hljs-template-tag { color: #9A5334; } .hljs-subst, .hljs-function, .hljs-title, .hljs-params, .hljs-formula { color: #DCDCDC; } .hljs-comment, .hljs-quote { color: #57A64A; font-style: italic; } .hljs-doctag { color: #608B4E; } .hljs-meta, .hljs-meta-keyword, .hljs-tag { color: #9B9B9B; } .hljs-variable, .hljs-template-variable { color: #BD63C5; } .hljs-attr, .hljs-attribute, .hljs-builtin-name { color: #9CDCFE; } .hljs-section { color: gold; } .hljs-emphasis { font-style: italic; } .hljs-strong { font-weight: bold; } /*.hljs-code { font-family:'Monospace'; }*/ .hljs-bullet, .hljs-selector-tag, .hljs-selector-id, .hljs-selector-class, .hljs-selector-attr, .hljs-selector-pseudo { color: #D7BA7D; } .hljs-addition { background-color: #144212; display: inline-block; width: 100%; } .hljs-deletion { background-color: #600; display: inline-block; width: 100%; } </style>

    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