---
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)

我來執行看看哦...? (看來是不給執行呢)

>經過檢討後,應該要先下file看看是甚麼檔案類型
>下了file可以知道他是文字檔,當然不能執行:rage:
>但如果是可執行檔還是不能執行的話
>可以下ls -al看看權限,再透過chmod 777把權限全開
>chmod(change mode) 777(111/111/111)即可達到全開的目的
ok 那我用py讓他讀出來 (其實IDA也可以看到 但當下我以為是我亂碼了ww)
那這段就滿有趣的 既然讀出來的東西跟IDA一樣
就代表這是作者故意為之

目前猜測是ASCII去做某種關於UTF的處理 所以這裡直接丟到[Cyber Chef](https://gchq.github.io/CyberChef/#recipe=Encode_text('UTF-16BE%20(1201)'))解看看

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會要求你要有完整版的檔案)

我們進來看看他的code齁 可以看到key有三個part
而full_key是由三個part組成 看來中間那個part是關鍵

這裡滿有趣的 有加密欸 :))))) (不大妙...)

這裡看到選c選項會call ==enter_license()== (我們來追蹤下去看看)

我把三個part組在一起 作為暫時的key 方便我在選項c輸入License Key


這裡有個==check_key()== 的function (要求輸入user_key和byte版本的username)

VScode可以監控目前的一些變數...等等 (賊tm方便 :+1:)
看到這裡輸入的key的確是我們剛才輸入的key
username也被轉成b'FREEMAN'

正式進入check_key()的環節~
好 這裡看到有比對full_key的code

這裡必須是False才會通過 (logic問題)

迴圈執行從i=0~i=22都是在比對part.1的部分


來到比對part.2 (這裡開始就是關鍵了)
接下來會比對到xxxxxxx的部分

這裡我卡了一小段時間 我想更改key的內容
於是我複製key到new_list內
並[把new_list蓋到key做到取代/更改的動作](https://www.runoob.com/python/att-string-join.html)
而且key在第一個判斷式已經通過

我們可以透過debug console搜尋一下他需要的密碼是甚麼? (上下兩部分式融合在一起的動作)

已經進入到else了! (會隨著name改變而密碼有所改變)

由此可以得到一份License_key和完整版的py (License_key即是flag)

==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))
```

==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)