# Radare2逆向學習筆記
###### tags: `Reverse`
題目來源:
https://github.com/radare/radare2book/tree/master/crackmes/ioli/IOLI-crackme.tar.gz
## Radare2由一系列的工具組成:
- radare2:十六進制的編輯器和debugger的核心,通常通過他進入interactive介面
- rabin2:可從binary中取得info
- rasm2:assemble & disassemble
- rahash2:hash工具
- radiff2:binary或code的差異對比
- rafind2
- ragg2:r_egg 的前端,将高级语言编写的简单程序编译成x86、x86-64和ARM的二进制文件
- rarun2:用于在不同环境中运行程序
- rax2:数据格式转换
:::info
ref. https://www.bookstack.cn/read/CTF-All-In-One/doc-2.2.1_radare2.md
:::
## 1. crackme0x00
- 先跑一次:

- README.txt提示: strings is your good friend
- 使用rabin2 或是直接在radare2使用i
- -I : binary info

- -z : strings from data section
`Note: -zz可看其他section的strings`

- 其中250382很可能就是密碼

- [分析]用r2來看看: 一進去就會跳到入口函數

`Note: p(rint)d(isassemble)f(unction),如果不清楚可以打'pdf?'看usage`
- 前面不重要,重要的是main函數
`Note: @代表取地址`

- 接著就可以開始分析了~r2也可以修改參數名方便分析,不過在那之前先存好檔

- 然後跳到main(s: seek)

- 看一下local變數(a(分析)f(函數)v(變數))

- 回去main function 0x08048448-44b找發現local_4h放的是local_18h的位址(ie local_4h是個ptr), 而local_4h作為scanf的第二個參數使用,所以可以知道local_18h大概就是輸入的password惹
`Note : Ex. scanf(%s, &x), 而x86是用stack傳遞參數 >> esp是第一個參數,esp+0x4是第二個參數`
- 改一下local_18h的名字

- 之後strcmp時比較Input_pwd跟str.250382,如果一樣就會跳(je)0x8048480

- [另解]直接patch
- 首先重新開啟/或是直接在上一種模式下執行eval cfg.write=true

- 跳到想要修改的地方, pd查看machine code

- 利用rasm2來看看實際是怎麼跳,並查詢jmp的machine code


=>也就是說我們只需要改一個byte(74改成eb)
- 先hexdump印出當前位置的20byte,然後wx表示在當前位置寫入

- 然後儲存好再執行一次

- [另解]這個執行檔有個bof漏洞,可以利用漏洞來直接跳到password ok

## 2. crackme0x01
- 直接看assembly
`Note: aa表示分析整個binary`

- [法I] 一樣直接把je patch成jmp

- [法II] 0x804842b這邊是拿輸入跟0x149a做比較,一樣就跳到Password Ok
`Note: ? [num]可以對數字做各種分析`

## 3. crackme0x02
- assembly > 可以看到程式流程無論如何都走不到print Password OK

- 所以只能直接patch成nop

## 4. crackme0x03
- Assembly > main function沒有任何jump

- sym.test > 字串要經過shift

- [法I]再sym.test中直接patch把je改成jmp

- [法II]去逆程式流程
1. main()
```c=
sym.main ();
; var int local_ch @ ebp-0xc
; var int local_8h @ ebp-0x8
; var int local_4h @ ebp-0x4
; var int local_4h_2 @ esp+0x4
...
0x080484cc 8d45fc lea eax, dword [local_4h] //eax -> ebp-0x4
0x080484cf 89442404 mov dword [local_4h_2], eax //scanf參數2->input會放在ebp-0x4
0x080484d3 c70424348604. mov dword [esp], 0x8048634 //scanf參數1
0x080484da e851feffff call sym.imp.scanf
0x080484df c745f85a0000. mov dword [local_8h], 0x5a ; 'Z' ; 90
0x080484e6 c745f4ec0100. mov dword [local_ch], 0x1ec ; 492
0x080484ed 8b55f4 mov edx, dword [local_ch] //edx=0x1ec
0x080484f0 8d45f8 lea eax, dword [local_8h] //eax->ebp-0x8=0x5a
0x080484f3 0110 add dword [eax], edx //ebp-0x8=0x5a+0x1ec=0x246
0x080484f5 8b45f8 mov eax, dword [local_8h] //eax=ebp-0x8=0x246
0x080484f8 0faf45f8 imul eax, dword [local_8h] //eax=0x246*0x246=0x52b24
0x080484fc 8945f4 mov dword [local_ch], eax //ebp-0xc=0x52b24
0x080484ff 8b45f4 mov eax, dword [local_ch] //eax=0x52b24
0x08048502 89442404 mov dword [local_4h_2], eax //esp+0x4=0x52b24
0x08048506 8b45fc mov eax, dword [local_4h] //eax=ebp-0x4=Input (test()的參數2)
0x08048509 890424 mov dword [esp], eax //esp=0x52b24 (test()的參數1)
0x0804850c e85dffffff call sym.test //test(0x52b24, Input)
```
2. test()
```c=
sym.test (int arg_8h, int arg_ch);
; arg int arg_8h @ ebp+0x8 //參數1=0x52b24
; arg int arg_ch @ ebp+0xc //參數2=Input
; CALL XREF from 0x0804850c (sym.main)
0x0804846e 55 push ebp
0x0804846f 89e5 mov ebp, esp
0x08048471 83ec08 sub esp, 8
0x08048474 8b4508 mov eax, dword [arg_8h] //eax=0x52b24
0x08048477 3b450c cmp eax, dword [arg_ch] //cmp(0x52b24, Input)
0x0804847a 740e je 0x804848a //相同就跳
```
- 所以Password就是0x52b24=338724

## 5. crackme0x04
[法I]分析assembly
- assembly > main

- check() >
:::info
1. sscanf(input[i], "%d", ebp-0x4)=>把input[i]的內容當成%d(整數)解析,並放在ebp-0x4中(ebp-0x4 = int(input[i]))
2. 相當於for(i=0; i<password_length; i++)
ebp-0x8 += int(password[i]);
int(password[i])放在ebp-0x4中,累加結果放在ebp-0x8,counter i放在ebp-0xc中
3. 每次循環都比較ebp-0x8跟0xf,如果相同就會跳password correct
:::

- 所以輸入的密碼只要把所有數字累加起來為0xf=15就是密碼

[法II]
- 直接patch把jne改成je

## 6. crackme0x05
- Assembly > 跟0x04類似,差別在於check做完累加之後又多了一個parell(),而且累加之後為0x10

- parell(&input) > 呼叫sscanf(&input, "%d", ebp-0x4)
會把整個input轉成整數存在ebp-0x4中,0x80484a7那行會取出轉成整數之後的最後一個bit,必須等於0才會跳password ok

- 也就是說,password的最後一個數字一定要是**偶數**(最後1bit才會是0)

## 7. crackme0x06
- Print password的流程:
```c=
main(env){
...
scanf("%s", &input);
check(&input, &env);
}
check(&input, &env){
int i=0, sum=0;
while(i<strlen(input))
sum += input[i];
if ( sum == 0x10)
parell(&input, &env);
else
i++;
}
parell(&input, &env){
if (dummy(&input, &env)==1)
if (input最後一個數字是偶數)
print("Password OK!")
}
dummy(&env){
for (i=0; i<env_number; i++)
if (strcmp(env[i], "LOLO", 3)==0)
return 1;
else
return 0;
}
```
- dummy(arg_ch) -> arg_ch point to first env
:::info
1. 0x..4ce的cmp是檢查還有沒有環境變數可看,因為最初edx指向第一個環境變數
2. 0x4d4-4e1:
eax指向ebp-0x4放環境變數的index
ecx為4byte,用來跳到下一個環境變數(因為一個環境變數4byte)
edx指向第一個環境變數
把ebp-4--index加1
3. 0x4f6: ecx+edx = 4byte + 第一個環境變數的位址
=> 0x4fc: strcmp(第二個環境變數, "LOLO", 3)
如果不一樣(回傳1)就再從1開始
=> 總之就是比較每個環境變數直到strcmp通過(回傳0)或沒有環境變數可檢查了
4. 0x515: dummy如果執行到這裡,如果strcmp有找到就回傳1,沒有通過就回傳0
:::

- 總結:
:::success
1. 要傳入環境變數
2. 環境變數的前三個字必須是"LOL"
3. password由左到右累加,累加到0x10就停
4. 檢查最後被累加的數字是否為偶數
:::
