# 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 - 先跑一次: ![](https://i.imgur.com/TdXllnQ.png) - README.txt提示: strings is your good friend - 使用rabin2 或是直接在radare2使用i - -I : binary info ![](https://i.imgur.com/Rq0XWEJ.png) - -z : strings from data section `Note: -zz可看其他section的strings` ![](https://i.imgur.com/qLLJVah.png) - 其中250382很可能就是密碼 ![](https://i.imgur.com/L7CES0P.png) - [分析]用r2來看看: 一進去就會跳到入口函數 ![](https://i.imgur.com/Y81P5zx.png) `Note: p(rint)d(isassemble)f(unction),如果不清楚可以打'pdf?'看usage` - 前面不重要,重要的是main函數 `Note: @代表取地址` ![](https://i.imgur.com/EVhOHa8.png) - 接著就可以開始分析了~r2也可以修改參數名方便分析,不過在那之前先存好檔 ![](https://i.imgur.com/W5t4wBe.png) - 然後跳到main(s: seek) ![](https://i.imgur.com/Ye3PSJq.png) - 看一下local變數(a(分析)f(函數)v(變數)) ![](https://i.imgur.com/mwVDHXa.png) - 回去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的名字 ![](https://i.imgur.com/JgIGik2.png) - 之後strcmp時比較Input_pwd跟str.250382,如果一樣就會跳(je)0x8048480 ![](https://i.imgur.com/3mtJWup.png) - [另解]直接patch - 首先重新開啟/或是直接在上一種模式下執行eval cfg.write=true ![](https://i.imgur.com/E3fZYrQ.png) - 跳到想要修改的地方, pd查看machine code ![](https://i.imgur.com/zjoc0W8.png) - 利用rasm2來看看實際是怎麼跳,並查詢jmp的machine code ![](https://i.imgur.com/gggBvoc.png) ![](https://i.imgur.com/ane2XMv.png) =>也就是說我們只需要改一個byte(74改成eb) - 先hexdump印出當前位置的20byte,然後wx表示在當前位置寫入 ![](https://i.imgur.com/8mZknfb.png) - 然後儲存好再執行一次 ![](https://i.imgur.com/DUWxyE4.png) - [另解]這個執行檔有個bof漏洞,可以利用漏洞來直接跳到password ok ![](https://i.imgur.com/2sKQWkI.png) ## 2. crackme0x01 - 直接看assembly `Note: aa表示分析整個binary` ![](https://i.imgur.com/YQKlTJU.png) - [法I] 一樣直接把je patch成jmp ![](https://i.imgur.com/vO3P1s6.png) - [法II] 0x804842b這邊是拿輸入跟0x149a做比較,一樣就跳到Password Ok `Note: ? [num]可以對數字做各種分析` ![](https://i.imgur.com/nkIh6ba.png) ## 3. crackme0x02 - assembly > 可以看到程式流程無論如何都走不到print Password OK ![](https://i.imgur.com/aHJcqPe.png) - 所以只能直接patch成nop ![](https://i.imgur.com/JNYdwpE.png) ## 4. crackme0x03 - Assembly > main function沒有任何jump ![](https://i.imgur.com/n53ViuQ.png) - sym.test > 字串要經過shift ![](https://i.imgur.com/0bW4LP4.png) - [法I]再sym.test中直接patch把je改成jmp ![](https://i.imgur.com/Yy72Q6J.png) - [法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 ![](https://i.imgur.com/nvEQOJv.png) ## 5. crackme0x04 [法I]分析assembly - assembly > main ![](https://i.imgur.com/gbcZSVq.png) - 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 ::: ![](https://i.imgur.com/gbxoo8r.png) - 所以輸入的密碼只要把所有數字累加起來為0xf=15就是密碼 ![](https://i.imgur.com/eqswV1C.png) [法II] - 直接patch把jne改成je ![](https://i.imgur.com/58FdWQq.png) ## 6. crackme0x05 - Assembly > 跟0x04類似,差別在於check做完累加之後又多了一個parell(),而且累加之後為0x10 ![](https://i.imgur.com/TEXUDDi.png) - parell(&input) > 呼叫sscanf(&input, "%d", ebp-0x4) 會把整個input轉成整數存在ebp-0x4中,0x80484a7那行會取出轉成整數之後的最後一個bit,必須等於0才會跳password ok ![](https://i.imgur.com/Chaxc3z.png) - 也就是說,password的最後一個數字一定要是**偶數**(最後1bit才會是0) ![](https://i.imgur.com/yvfX5Dp.png) ## 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 ::: ![](https://i.imgur.com/oLzFVZ8.png) - 總結: :::success 1. 要傳入環境變數 2. 環境變數的前三個字必須是"LOL" 3. password由左到右累加,累加到0x10就停 4. 檢查最後被累加的數字是否為偶數 ::: ![](https://i.imgur.com/SSSJCsc.png)