--- title: 逆向(rev)的一些小心得 tags: 逆向工程學, CTF --- # 逆向(rev)的一些小心得 ## :memo: 閱讀目標 ### Step 1: 有哪些地方要加強 >- [ ] 組合語言閱讀能力 >- [ ] 資料結構/記憶體存放狀況...等等 >- [ ] 工具的選擇 >- [ ] IDA的使用 ### :rocket:來這裡吸收日月精華:rocket: ### [[探索 5 分鐘] stack 與 heap 的底層概念](https://nwpie.blogspot.com/2017/05/5-stack-heap.html) ```cpp public void Method1() { int i = 4; //Line1 int y = 2; //Line2 Class1 cls1 = new Class1(); //Line3 } ``` >==value type== 的變數, 包括指標變數會放在 stack ==reference type== 的變數 (如 string, object) 本身也會放 stack, 然而他的值 (value) 則是放 heap ==box== 就是 value types to reference types 的過程, 所以 value 會被放到 heap 中, 而產生一個 object 變數來指向這個 value, 變數指標則是在 stack ==unbox== 是 reference types to value types 的過程, 所以原本 object 所指向的值 (heap 中) 會被複製到 stack 中並賦予明確 value type 型別 ### [C++中BYTE、WORD、DWORD的作用以及區別](https://www.itread01.com/content/1542847693.html) >在VC++6.0中,char的1位元組,short是2位元組,int和long都是4位元組,因此可以認為BYTE、WORD、DWORD、QWORD定義的變數分別是1節,2位元組,4位元組、8位元組。 即:==BYTE = unsigned char,WORD = unsigned short,DWORD = unsigned long== >DWORD 通常用來儲存地址或者存放指標 >其中WORD和DWORD的區別 定義WORD和DWORD其實主要是為了:1、便於移植;2、更為嚴格的型別檢查 >WORD固定是2位元組,DWORD固定是4位元組 >int的話,隨著作業系統的不同,有著不一樣的位元組數,在32位作業系統中是4位元組,在16位作業系就是2位元組 在序列化的操作中,因為序列化是按照位元組流儲存的,為了保證不會錯位,要求使用具有明確位元組數目的資料型別 ### [Maple大神的RedpwnCTF writeups](https://blog.maple3142.net/2021/07/13/redpwnctf-2021-writeups/) >r2 gdb objdump gcc -s (編譯可保留組合語言 intel x86_64 arm [strlen() - C語言庫函數](http://tw.gitbook.net/c_standard_library/c_function_strlen.html) [[C++] 高效率記憶體配置 -- alloca](http://mqjing.blogspot.com/2008/07/c-alloca.html) [CSS 的 !important 意義](http://n.sfs.tw/content/index/10632) [linux 系統呼叫sysconf函式使用](https://www.itread01.com/content/1547165009.html) [Python strip() lstrip() rstrip() 函数 去除空格](https://blog.csdn.net/doiido/article/details/43164739) [x86 Assembly 的 Stack Frame 和函數的呼叫慣例](https://sites.google.com/view/28-assembly-language/x86-assembly-%E7%9A%84-stack-frame-%E5%92%8C%E5%87%BD%E6%95%B8%E7%9A%84%E5%91%BC%E5%8F%AB%E6%85%A3%E4%BE%8B) [gcb大量資料](https://www.cntofu.com/book/46/gdb/gdbming_ling_ji_chu_ff0c_rang_ni_de_cheng_xu_bug_w.md) >x86 x64 calling convention >peda [這是一個系列 PC Assembly Language 學習筆記](http://godleon.blogspot.com/2008/01/machine-language-cpu-machine-language.html) time --- ## [Online Tools to crack CTF Contests](https://dhanumaalaian.medium.com/online-tools-to-crack-ctf-contest-1ad7efa958da) ## [ctf-all-in-one](https://firmianay.gitbooks.io/ctf-all-in-one/content/doc/1.5.1_c_basic.html) ## [GDB命令基礎,讓你的程序bug無處躲藏](https://www.cntofu.com/book/46/gdb/gdbming_ling_ji_chu_ff0c_rang_ni_de_cheng_xu_bug_w.md) --- ## :computer: 學習例題 ### 目前已刷_picoCTF-rev - [x] Transformation - [x] keygenme-py - [x] crackme-py ### :mega: [Transformation](https://play.picoctf.org/practice/challenge/104?category=3&page=1) :mega: ok~最簡單的第一步 把檔案下載下來 (這裡使用網站自帶的_Webshell) ![](https://i.imgur.com/S0GJOtE.png) 我來執行看看哦...? (看來是不給執行呢) ![](https://i.imgur.com/OTZ2G7Z.png) >經過檢討後,應該要先下file看看是甚麼檔案類型 >下了file可以知道他是文字檔,當然不能執行:rage: >但如果是可執行檔還是不能執行的話 >可以下ls -al看看權限,再透過chmod 777把權限全開 >chmod(change mode) 777(111/111/111)即可達到全開的目的 ok 那我用py讓他讀出來 (其實IDA也可以看到 但當下我以為是我亂碼了ww) 那這段就滿有趣的 既然讀出來的東西跟IDA一樣 就代表這是作者故意為之 ![](https://i.imgur.com/VyQUSgR.png) 目前猜測是ASCII去做某種關於UTF的處理 所以這裡直接丟到[Cyber Chef](https://gchq.github.io/CyberChef/#recipe=Encode_text('UTF-16BE%20(1201)'))解看看 ![](https://i.imgur.com/0paUk1R.png) OK,it works. **picoCTF{16_bits_inst34d_of_8_e141a0f7}** 要用手動解的話就是先將每個字符轉換成hex再轉換成ASCII (記得要將0x...移除) ### :mega: [keygenme-py](https://play.picoctf.org/practice/challenge/121?category=3&page=1) :mega: 執行看看檔案哦 哦?長這樣啊? 可以看到選項b是[LOCKED]的 (選b會要求你要有完整版的檔案) ![](https://i.imgur.com/NiN9tSr.png) 我們進來看看他的code齁 可以看到key有三個part 而full_key是由三個part組成 看來中間那個part是關鍵 ![](https://i.imgur.com/xyEXmXO.png) 這裡滿有趣的 有加密欸 :))))) (不大妙...) ![](https://i.imgur.com/7c3qedh.png) 這裡看到選c選項會call ==enter_license()== (我們來追蹤下去看看) ![](https://i.imgur.com/pWaiFri.png) 我把三個part組在一起 作為暫時的key 方便我在選項c輸入License Key ![](https://i.imgur.com/I2GHCKf.png) ![](https://i.imgur.com/CCNeFGP.png) 這裡有個==check_key()== 的function (要求輸入user_key和byte版本的username) ![](https://i.imgur.com/YZQItH4.png) VScode可以監控目前的一些變數...等等 (賊tm方便 :+1:) 看到這裡輸入的key的確是我們剛才輸入的key username也被轉成b'FREEMAN' ![](https://i.imgur.com/ZNSqCcQ.png) 正式進入check_key()的環節~ 好 這裡看到有比對full_key的code ![](https://i.imgur.com/hBhgM9R.png) 這裡必須是False才會通過 (logic問題) ![](https://i.imgur.com/7B2ByqX.png) 迴圈執行從i=0~i=22都是在比對part.1的部分 ![](https://i.imgur.com/14aDvkw.png) ![](https://i.imgur.com/sYN1e94.png) 來到比對part.2 (這裡開始就是關鍵了) 接下來會比對到xxxxxxx的部分 ![](https://i.imgur.com/JIXJ7tz.png) 這裡我卡了一小段時間 我想更改key的內容 於是我複製key到new_list內 並[把new_list蓋到key做到取代/更改的動作](https://www.runoob.com/python/att-string-join.html) 而且key在第一個判斷式已經通過 ![](https://i.imgur.com/CS43txs.png) 我們可以透過debug console搜尋一下他需要的密碼是甚麼? (上下兩部分式融合在一起的動作) ![](https://i.imgur.com/9rERsTO.png) 已經進入到else了! (會隨著name改變而密碼有所改變) ![](https://i.imgur.com/70JgRrX.png) 由此可以得到一份License_key和完整版的py (License_key即是flag) ![](https://i.imgur.com/iDV7UyL.png) ==picoCTF{1n_7h3_\|<3y_of_0d208392}== ### :mega: [Easy Crack Me](https://ptr-yudai.hatenablog.com/entry/2019/09/02/100556#Reverse-88pts-Easy-Crack-Me) :mega: 這是個Reverse Warmup題 我們可以使用IDA來分析這題 可以看到,這段程式碼會去檢查flag format是否正確 其中a2是argv[] 簡單來說就是,會去讀取使用者所輸入的參數,並將輸入的參數分割後存入argv[]陣列 也可以再第10行可到整個flag的長度為39 (0~39) 且第39 = ascii 125 = "}" ```c= if ( a1 == 2 ) { s = a2[1]; if ( strlen(a2[1]) != 39 ) { puts("incorrect"); exit(0); } if ( memcmp(s, "TWCTF{", 6uLL) || s[38] != 125 ) { puts("incorrect"); exit(0); } ``` 再來看看這一段,他規定了{ }中能輸入什麼字元 : “0123456789abcdef” (v47 v48轉換成char) 補充 [memcmp()](http://tw.gitbook.net/c_standard_library/c_function_memcmp.html)、[strchr()](http://tw.gitbook.net/c_standard_library/c_function_strchr.html). ```c= s1 = 0LL; v39 = 0LL; v40 = 0LL; v41 = 0LL; v42 = 0LL; v43 = 0LL; v44 = 0LL; v45 = 0LL; v47 = 3978425819141910832LL; v48 = 7378413942531504440LL; for ( i = 0; i <= 15; ++i ) { for ( j = strchr(s, *((_BYTE *)&v47 + i)); j; j = strchr(j + 1, *((_BYTE *)&v47 + i)) ) ++*((_DWORD *)&s1 + i); } if ( memcmp(&s1, &unk_400F00, 0x40uLL) ) { puts("incorrect"); exit(0); } ``` *(&s[4 * k + 6] + l) 的值的異或運算和加法運算的值必須與表中的值相同。 (&unk_400F40 -> byte 29h -> array) ```c= for ( k = 0; k <= 7; ++k ) { v11 = 0; v12 = 0; for ( l = 0; l <= 3; ++l ) { v5 = *(&s[4 * k + 6] + l); v11 += v5; v12 ^= v5; } *((_DWORD *)&v22 + k) = v11; *((_DWORD *)&v26 + k) = v12; } if ( memcmp(&v22, &unk_400F40, 0x20uLL) || memcmp(&v26, &unk_400F60, 0x20uLL) ) { puts("incorrect"); exit(0); } ``` *(&s[8 * n + 6] + m)值去做xor運算和加法運算的值應該和表中的值一樣。 ```c= for ( m = 0; m <= 7; ++m ) { v15 = 0; v16 = 0; for ( n = 0; n <= 3; ++n ) { v6 = *(&s[8 * n + 6] + m); v15 += v6; v16 ^= v6; } *((_DWORD *)&v30 + m) = v15; *((_DWORD *)&v34 + m) = v16; } if ( memcmp(&v30, &unk_400FA0, 0x20uLL) || memcmp(&v34, &unk_400F80, 0x20uLL) ) { puts("incorrect"); exit(0); } ``` 這裡指定了s[]的範圍。 ```c= for ( ii = 0; ii <= 31; ++ii ) { v8 = s[ii + 6]; if ( v8 <= 47 || v8 > 57 ) { if ( v8 <= 96 || v8 > 102 ) v46[ii] = 0; else v46[ii] = 128; } else { v46[ii] = 255; } } if ( memcmp(v46, &unk_400FC0, 0x80uLL) ) { puts("incorrect"); exit(0); } ``` 在這裡可以看到 s[2 * (jj + 3)] 的值的和會是1160。 ```c= v19 = 0; for ( jj = 0; jj <= 15; ++jj ) v19 += s[2 * (jj + 3)]; if ( v19 != 1160 ) { puts("incorrect"); exit(0); } ``` 這裡需要s[]的索引值符合下列所要求的內容,以上都達成條件則彈出回答正確! ```c= if ( s[37] != 53 || s[7] != 102 || s[11] != 56 || s[12] != 55 || s[23] != 50 || s[31] != 52 ) { puts("incorrect"); exit(0); } printf("Correct: %s\n", s, a2); result = 0LL; } ``` 最後,根據以上線索來使用z3約束器求解 ```python= from z3 import * s = Solver() a1 = [BitVec('a%i'%i,8) for i in range(39)] s.add(a1[0] == ord('T')) s.add(a1[1] == ord('W')) s.add(a1[2] == ord('C')) s.add(a1[3] == ord('T')) s.add(a1[4] == ord('F')) s.add(a1[5] == ord('{')) s.add(a1[38] == ord('}')) s.add(a1[37] == 53 , a1[7] == 102 , a1[11] == 56 , a1[12] == 55 , a1[23] == 50 , a1[31] == 52) v45 = [0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x61,0x62,0x63,0x64,0x65,0x66] # 0 1 2 3 4 5 6 7 8 9 a b c d e f tb1 = [3, 2, 2, 0, 3, 2, 1, 3, 3, 1, 1, 3, 1, 2, 2, 3] tb2 = [0x015E, 0x00DA, 0x012F, 0x0131, 0x0100, 0x0131, 0x00FB, 0x0102] tb3 = [82, 12, 1, 15, 92, 5, 83, 88] tb4 = [1, 87, 7, 13, 13, 83, 81, 81] tb5 = [0x0129, 0x0103, 0x012B, 0x0131, 0x0135, 0x010B, 0x00FF, 0x00FF] tb6 = [128, 128, 255, 128, 255, 255, 255, 255, 128, 255, 255, 128, 128, 255, 255, 128, 255, 255, 128, 255, 128, 128, 255, 255, 255, 255, 128, 255, 255, 255, 128, 255] for k in range(8): v10 = 0 v11 = 0 for l in range(4): v5 = a1[4 * k + 6 + l] v10 += v5 v11 ^= v5 s.add(tb2[k] == v10) s.add(tb3[k] == v11) for m in range(8): v14 = 0 v15 = 0 for n in range(4): v6 = a1[8 * n + 6 + m] v14 += v6 v15 ^= v6 s.add(tb5[m] == v14) s.add(tb4[m] == v15) v18 = 0 for jj in range(16): v18 += a1[2 * (jj + 3)] s.add(v18 == 1160) for i in range(32): if(tb6[i]==128): s.add(a1[i+6]>=97) s.add(a1[i+6]<=102) else: s.add(a1[i+6]>=48) s.add(a1[i+6]<=57) for i,ch in enumerate("0123456789abcdef"): cnt = 0 for x in a1: cnt += If(x == ord(ch),1,0) s.add(cnt == tb1[i]) if s.check() == sat: m = s.model() print ''.join(chr(int(str(m.evaluate(a1[i]))))for i in range(39)) ``` ![](https://i.imgur.com/t0yLvTP.png) ==TWCTF{df2b4877e71bd91c02f8ef6004b584a5}== 這題的難度不高,但對於新手還是有挑戰性,需要足夠細心才能做好約束,得出答案。 對於目前的我來說,閱讀語言的過程還是不夠順暢,得多做一些題目。 --- # ..以下為範例檔.. ## BONUS: More cool ways to HackMD! - Table | Features | Tutorials | | ----------------- |:----------------------- | | GitHub Sync | [:link:][GitHub-Sync] | | Browser Extension | [:link:][HackMD-it] | | Book Mode | [:link:][Book-mode] | | Slide Mode | [:link:][Slide-mode] | | Share & Publish | [:link:][Share-Publish] | [GitHub-Sync]: https://hackmd.io/c/tutorials/%2Fs%2Flink-with-github [HackMD-it]: https://hackmd.io/c/tutorials/%2Fs%2Fhackmd-it [Book-mode]: https://hackmd.io/c/tutorials/%2Fs%2Fhow-to-create-book [Slide-mode]: https://hackmd.io/c/tutorials/%2Fs%2Fhow-to-create-slide-deck [Share-Publish]: https://hackmd.io/c/tutorials/%2Fs%2Fhow-to-publish-note - LaTeX for formulas $$ x = {-b \pm \sqrt{b^2-4ac} \over 2a} $$ - Code block with color and line numbers: ```javascript=16 var s = "JavaScript syntax highlighting"; alert(s); ``` - UML diagrams ```sequence Alice->Bob: Hello Bob, how are you? Note right of Bob: Bob thinks Bob-->Alice: I am good thanks! Note left of Alice: Alice responds Alice->Bob: Where have you been? ``` > Leave in-line comments! [color=#3b75c6] - Embed YouTube Videos {%youtube PJuNmlE74BQ %} > Put your cursor right behind an empty bracket {} :arrow_left: and see all your choices. - And MORE ➜ [HackMD Tutorials](https://hackmd.io/c/tutorials)