├── IOLI-crackme.zip ├── Intro-to-Reverse-Engineering-and-Debugging-with-Radare2.pdf ├── README.md ├── analysing-crackme03-with-radare2.md ├── defeating-IOLI-with-radare2.md ├── introduction_to_radare2.md ├── mystery-bin-with-radare2.md ├── mystery.c ├── radare2_tutorial.pdf └── simple-example-with-radare2.md /IOLI-crackme.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifding/radare2-tutorial/31d453715b0c8f2625ae5e7fc85822244b69ff25/IOLI-crackme.zip -------------------------------------------------------------------------------- /Intro-to-Reverse-Engineering-and-Debugging-with-Radare2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifding/radare2-tutorial/31d453715b0c8f2625ae5e7fc85822244b69ff25/Intro-to-Reverse-Engineering-and-Debugging-with-Radare2.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Radare2 Tutorial 2 | 3 | Reverse Engineering using Radare2 4 | 5 | > You should run the binary file on a VM and actually take a snapshot before you start. Specially if you do dynamic analysis and you do not know what the sampe does (backdoor, worm, virus, ...) 6 | 7 | 8 | - Basics 9 | - [Introduction to Raddare2](introduction_to_radare2.md) 10 | - [Gitbook: Radare2-explorations](https://monosource.gitbooks.io/radare2-explorations/content/introduction.html) 11 | - [Useful commands](http://exitno.de/reversing/) 12 | - [R2 Cheatsheet](https://github.com/zxgio/r2-cheatsheet) 13 | - [Radare2 tutorial](radare2_tutorial.pdf) 14 | - [A JOURNEY INTO RADARE 2 – PART 1: SIMPLE CRACKME](https://www.megabeets.net/a-journey-into-radare-2-part-1/) 15 | 16 | - Reverse Engineering with Radare2 17 | - [Intro-to-Reverse-Engineering-and-Debugging-with-Radare2](Intro-to-Reverse-Engineering-and-Debugging-with-Radare2.pdf) 18 | - [Simple example with Radare2](simple-example-with-radare2.md) 19 | - [Mystery bin with Radare2](mystery-bin-with-radare2.md) 20 | - [Defeating-IOLI-with-radare2](defeating-IOLI-with-radare2.md) 21 | - [Analysing-crackme03-with-radare2](analysing-crackme03-with-radare2.md) 22 | - [Diving Into Radare2](http://blog.devit.co/diving-into-radare2/) 23 | - [Binary Bomb with Radare2 - Prelude](https://unlogic.co.uk/2016/04/12/Binary%20Bomb%20with%20Radare2%20-%20Prelude/index.html) 24 | - [REVERSE ENGINEERING A GAMEBOY ROM WITH RADARE2](https://www.megabeets.net/reverse-engineering-a-gameboy-rom-with-radare2/) 25 | 26 | - Radare2 Explorations 27 | - [Tutorial files repository](https://github.com/monosource/radare2-explorations-binaries) 28 | - [Tutorial 1 - Simple Patch](https://monosource.gitbooks.io/radare2-explorations/content/tut1/tut1_-_simple_patch.html) 29 | - [Tutorial 2 - Memory Manipulation](https://monosource.gitbooks.io/radare2-explorations/content/tut2/tut2_-_mem_manip.html) 30 | - [Tutorial 3 - ESIL](https://monosource.gitbooks.io/radare2-explorations/content/tut3/tut3_-_esil.html) 31 | - [Tutorial 4 - Simple Exploit](https://monosource.gitbooks.io/radare2-explorations/content/tut4/tut4_-_exploit.html) 32 | 33 | - Training Materials 34 | - [LCA2015 radare2 tutorial](https://github.com/pastcompute/lca2015-radare2-tutorial) 35 | - [radare2-explorations-binaries](https://github.com/monosource/radare2-explorations-binaries) 36 | - [Training Materials of Practical Reverse Engineering using Radare2](https://github.com/s4n7h0/Practical-Reverse-Engineering-using-Radare2) 37 | - [A reversing series with radare2](https://github.com/bluec0re/reversing-radare2) 38 | - [Dirty COW (CVE-2016-5195)](https://dirtycow.ninja/) 39 | 40 | - Others 41 | - [Solving Radare2 Explorations Tutorial 2 with angr](https://monosource.github.io/2016/06/solving-tut2-angr) 42 | -------------------------------------------------------------------------------- /analysing-crackme03-with-radare2.md: -------------------------------------------------------------------------------- 1 | 2 | # Analysing crackme.03 with radare2 3 | 4 | In this post I am going to show how to analyse crackme with radare2 framework. The crackme is from [geslan](https://github.com/geyslan/crackmes). You can download the source code (crackme.03.asm) and create the binray file (crackme.03). 5 | ```sh 6 | # nasm -f bin crackme.03.asm 7 | # chmod +x crackme.03 8 | ./crackme.03 9 | Try to find the string of success and make me print it. 10 | ``` 11 | 12 | ## Preliminary analysis 13 | 14 | Before starting dynamic analysis we shall see what information we can extract from binary file using utility `rabin2`. 15 | ```sh 16 | # rabin2 -I crackme.03 17 | havecode true 18 | pic false 19 | canary false 20 | nx false 21 | crypto false 22 | va true 23 | bintype elf 24 | class ELF32 25 | lang c 26 | arch x86 27 | bits 32 28 | machine Intel 80386 29 | os linux 30 | minopsz 1 31 | maxopsz 16 32 | pcalign 0 33 | subsys linux 34 | endian little 35 | stripped false 36 | static true 37 | linenum true 38 | lsyms true 39 | relocs true 40 | rpath NONE 41 | binsz 372 42 | ``` 43 | 44 | Some basic information about binary can be obtained. It can be seen this is a ELF32 file format binary with enabled `nx` protection which hasn't been stripped, which means radare2 should be able to find main function starting address. 45 | 46 | Let's check main function starting by 47 | ```sh 48 | # rabin2 -MRsSz crackme.03 49 | Warning: Cannot initialize section headers 50 | Warning: Cannot initialize strings table 51 | Warning: Cannot initialize dynamic strings 52 | [Sections] 53 | idx=00 vaddr=0x00010000 paddr=0x00000000 sz=65568 vsz=65568 perm=m-r-- name=LOAD0 54 | idx=01 vaddr=0x00010000 paddr=0x00000000 sz=52 vsz=52 perm=m-rw- name=ehdr 55 | 56 | 2 sections 57 | [Symbols] 58 | 59 | 0 symbols 60 | [Relocations] 61 | 62 | 0 relocations 63 | ``` 64 | 65 | A small file, with no symbols, no strings, no sections. Looks like a hand-crafted binary! 66 | 67 | ## Dynamic analysis 68 | 69 | Now when we have some information about binary let's say with dynamic analysis. We'll load it up in r2 using the r2 command: 70 | ```nasm 71 | # r2 ./crackme.03 72 | [0x00010020]> aa 73 | [Cannot find function 'entry0' at 0x00010020 entry0 (aa) 74 | [0x00010020]> s entry0 75 | [0x00010020]> pdf 76 | p: Cannot find function at 0x00010020 77 | [0x00010020]> s 0x00010020 78 | [0x00010020]> pd 79 | | ;-- entry0: 80 | | 0x00010020 b32a mov bl, 0x2a ; '*' ; 42 81 | | 0x00010022 31c0 xor eax, eax 82 | | 0x00010024 40 inc eax 83 | ,==< 0x00010025 eb12 jmp 0x10039 84 | || 0x00010027 003400 add byte [eax + eax], dh 85 | || 0x0001002a 2000 and byte [eax], al 86 | || 0x0001002c 0100 add dword [eax], eax 87 | || 0x0001002e 0c14 or al, 0x14 88 | || 0x00010030 90 nop 89 | |`=< 0x00010031 7c97 jl 0xffca 90 | | 0x00010033 ad lodsd eax, dword [esi] 91 | | ;-- section_end.ehdr: 92 | | 0x00010034 b6b6 mov dh, 0xb6 ; 182 93 | | 0x00010036 c6c0bf mov al, 0xbf ; 191 94 | `--> 0x00010039 29c9 sub ecx, ecx 95 | 0x0001003b b900000100 mov ecx, 0x10000 ; section.ehdr 96 | 0x00010040 31d2 xor edx, edx 97 | 0x00010042 31db xor ebx, ebx 98 | .-> 0x00010044 8a19 mov bl, byte [ecx] 99 | | 0x00010046 01da add edx, ebx 100 | | 0x00010048 41 inc ecx 101 | | 0x00010049 81f92e000100 cmp ecx, 0x1002e 102 | `=< 0x0001004f 75f3 jne 0x10044 103 | 0x00010051 c1e202 shl edx, 2 104 | 0x00010054 663b152e0001. cmp dx, word [0x1002e] ; [0x1002e:2]=0x140c 105 | ,=< 0x0001005b 7529 jne 0x10086 106 | | 0x0001005d 31ed xor ebp, ebp 107 | | 0x0001005f 89d7 mov edi, edx 108 | | 0x00010061 45 inc ebp 109 | | 0x00010062 b810800000 mov eax, 0x8010 110 | | 0x00010067 45 inc ebp 111 | | 0x00010068 f7e5 mul ebp 112 | | 0x0001006a 96 xchg eax, esi 113 | | 0x0001006b 89f0 mov eax, esi 114 | | 0x0001006d 662b05140001. sub ax, word [0x10014] 115 | ,==< 0x00010074 7510 jne 0x10086 116 | || 0x00010076 29fe sub esi, edi 117 | || 0x00010078 6681f614ec xor si, 0xec14 118 | ,===< 0x0001007d 7507 jne 0x10086 119 | ,====< 0x0001007f eb01 jmp 0x10082 120 | |||| 0x00010081 d431 aam 0x31 121 | ||| 0x00010083 c0755e29 sal byte [ebp + 0x5e], 0x29 122 | 0x00010087 d2743854 sal byte [eax + edi + 0x54], cl 123 | ,=< 0x0001008b 7279 jb 0x10106 124 | | 0x0001008d 20746f20 and byte [edi + ebp*2 + 0x20], dh 125 | | 0x00010091 66696e642074 imul bp, word [esi + 0x64], 0x7420 126 | | 0x00010097 6865207374 push 0x74732065 127 | ,==< 0x0001009c 7269 jb 0x10107 128 | || 0x0001009e 6e outsb dx, byte [esi] 129 | || 0x0001009f 67206f66 and byte [bx + 0x66], ch 130 | || 0x000100a3 207375 and byte [ebx + 0x75], dh 131 | || 0x000100a6 636365 arpl word [ebx + 0x65], sp 132 | ,===< 0x000100a9 7373 jae 0x1011e 133 | ||| 0x000100ab 20616e and byte [ecx + 0x6e], ah 134 | ||| 0x000100ae 64206d61 and byte fs:[ebp + 0x61], ch 135 | ||| 0x000100b2 6b65206d imul esp, dword [ebp + 0x20], 0x6d 136 | ||| 0x000100b6 65207072 and byte gs:[eax + 0x72], dh 137 | ||| 0x000100ba 696e74206974. imul ebp, dword [esi + 0x74], 0x2e746920 138 | ||| 0x000100c1 0ab804000000 or bh, byte [eax + 4] 139 | ||| 0x000100c7 bb01000000 mov ebx, 1 140 | ||| 0x000100cc b98a000100 mov ecx, 0x1008a 141 | ||| 0x000100d1 ba38000000 mov edx, 0x38 ; '8' ; 56 142 | ||| 0x000100d6 cd80 int 0x80 143 | ||| 0x000100d8 b801000000 mov eax, 1 144 | ||| 0x000100dd bb00000000 mov ebx, 0 145 | ``` 146 | 147 | It seems that the block between 0x00010025 and 0x00010039 is not used. We can hide it with: 148 | ```nasm 149 | [0x00010020]> s 0x00010027 150 | [0x00010027]> Ch 0x00010039 - 0x00010027 151 | [0x00010027]> s- 152 | [0x00010020]> pdf 153 | p: Cannot find function at 0x00010020 154 | [0x00010020]> s 0x00010020 155 | [0x00010020]> pd 156 | ;-- entry0: 157 | 0x00010020 b32a mov bl, 0x2a ; '*' ; 42 158 | 0x00010022 31c0 xor eax, eax 159 | 0x00010024 40 inc eax 160 | ,=< 0x00010025 eb12 jmp 0x10039 161 | | 0x00010027 (18 bytes hidden) 162 | `-> 0x00010039 29c9 sub ecx, ecx 163 | 0x0001003b b900000100 mov ecx, 0x10000 ; section.ehdr 164 | 0x00010040 31d2 xor edx, edx 165 | 0x00010042 31db xor ebx, ebx 166 | .-> 0x00010044 8a19 mov bl, byte [ecx] 167 | | 0x00010046 01da add edx, ebx 168 | | 0x00010048 41 inc ecx 169 | | 0x00010049 81f92e000100 cmp ecx, 0x1002e 170 | `=< 0x0001004f 75f3 jne 0x10044 171 | 0x00010051 c1e202 shl edx, 2 172 | 0x00010054 663b152e0001. cmp dx, word [0x1002e] ; [0x1002e:2]=0x140c 173 | ,=< 0x0001005b 7529 jne 0x10086 174 | | 0x0001005d 31ed xor ebp, ebp 175 | | 0x0001005f 89d7 mov edi, edx 176 | | 0x00010061 45 inc ebp 177 | | 0x00010062 b810800000 mov eax, 0x8010 178 | | 0x00010067 45 inc ebp 179 | | 0x00010068 f7e5 mul ebp 180 | | 0x0001006a 96 xchg eax, esi 181 | | 0x0001006b 89f0 mov eax, esi 182 | | 0x0001006d 662b05140001. sub ax, word [0x10014] 183 | ,==< 0x00010074 7510 jne 0x10086 184 | || 0x00010076 29fe sub esi, edi 185 | || 0x00010078 6681f614ec xor si, 0xec14 186 | ,===< 0x0001007d 7507 jne 0x10086 187 | ,====< 0x0001007f eb01 jmp 0x10082 188 | |||| 0x00010081 d431 aam 0x31 189 | ||| 0x00010083 c0755e29 sal byte [ebp + 0x5e], 0x29 190 | 0x00010087 d2743854 sal byte [eax + edi + 0x54], cl 191 | ,=< 0x0001008b 7279 jb 0x10106 192 | | 0x0001008d 20746f20 and byte [edi + ebp*2 + 0x20], dh 193 | | 0x00010091 66696e642074 imul bp, word [esi + 0x64], 0x7420 194 | | 0x00010097 6865207374 push 0x74732065 195 | ,==< 0x0001009c 7269 jb 0x10107 196 | || 0x0001009e 6e outsb dx, byte [esi] 197 | || 0x0001009f 67206f66 and byte [bx + 0x66], ch 198 | || 0x000100a3 207375 and byte [ebx + 0x75], dh 199 | || 0x000100a6 636365 arpl word [ebx + 0x65], sp 200 | ,===< 0x000100a9 7373 jae 0x1011e 201 | ||| 0x000100ab 20616e and byte [ecx + 0x6e], ah 202 | ||| 0x000100ae 64206d61 and byte fs:[ebp + 0x61], ch 203 | ||| 0x000100b2 6b65206d imul esp, dword [ebp + 0x20], 0x6d 204 | ||| 0x000100b6 65207072 and byte gs:[eax + 0x72], dh 205 | ||| 0x000100ba 696e74206974. imul ebp, dword [esi + 0x74], 0x2e746920 206 | ||| 0x000100c1 0ab804000000 or bh, byte [eax + 4] 207 | ||| 0x000100c7 bb01000000 mov ebx, 1 208 | ||| 0x000100cc b98a000100 mov ecx, 0x1008a 209 | ||| 0x000100d1 ba38000000 mov edx, 0x38 ; '8' ; 56 210 | ||| 0x000100d6 cd80 int 0x80 211 | ||| 0x000100d8 b801000000 mov eax, 1 212 | ||| 0x000100dd bb00000000 mov ebx, 0 213 | ||| 0x000100e2 cd80 int 0x80 214 | ||| 0x000100e4 31d2 xor edx, edx 215 | ||| 0x000100e6 6839000100 push 0x10039 216 | ||| 0x000100eb 66832c240b sub word [esp], 0xb 217 | ||| 0x000100f0 5e pop esi 218 | ||| 0x000100f1 8d7601 lea esi, dword [esi + 1] ; 0x1 219 | ||| 0x000100f4 29c9 sub ecx, ecx 220 | ||| 0x000100f6 75ec jne 0x100e4 221 | ``` 222 | 223 | ### First checksum 224 | 225 | We can seee a short loop starting 0x00010044, that loads 0x10000. Since the headers are screwed to prevent loading in GNU Tools, this is likely a checksum to prevent modifications. Further, they are 3 jumps to 0x10086. This may be a badboy. 226 | ```nasm 227 | [0x00010020]> pd @0x10086 228 | 0x00010086 29d2 sub edx, edx 229 | ,=< 0x00010088 7438 je 0x100c2 230 | ``` 231 | 232 | Classic trick to fool automatic analyzers. Of cource `edx - edx` is always to zero, the jumb is taken. If arrows are annoying you, feel free to turn them off with e asm.lines = false. 233 | 234 | ### Badboy 235 | ```nasm 236 | [0x00010020]> pd @0x100c2 237 | | 0x000100c2 b804000000 mov eax, 4 238 | | 0x000100c7 bb01000000 mov ebx, 1 239 | | 0x000100cc b98a000100 mov ecx, 0x1008a 240 | | 0x000100d1 ba38000000 mov edx, 0x38 ; '8' ; 56 241 | | 0x000100d6 cd80 int 0x80 242 | .--> 0x000100d8 b801000000 mov eax, 1 243 | || 0x000100dd bb00000000 mov ebx, 0 244 | || 0x000100e2 cd80 int 0x80 245 | ``` 246 | 247 | Time to take a look at [the syscall reference](http://syscalls.kernelgrok.com/), you can also check your local syscall.h. Looks like the first one is a write, and the second one is an exit. Just to be sure, let's check what is printed sys_write takes: 248 | 249 | - Like every syscall, the call number in eax (4) 250 | - The file descriptor in ebx (1, aka stdout) 251 | - The buffer to print in ecx (0x1008a), this is an address, and the lenth in edx (0x38). 252 | 253 | What is at 0x1008a? 254 | ```nasm 255 | [0x00010020]> ps 0x38 @0x1008a 256 | Try to find the string of success and make me print it. 257 | 258 | [0x00010020]> 259 | ``` 260 | 261 | We should add a comment at 0x1008a: 262 | ```sh 263 | [0x00010020]> Cca 0x1008a BADBOY 264 | ``` 265 | 266 | We should focus on avoid jumps to this location. 267 | 268 | ### Reversing the checksum 269 | 270 | Since no input/output operations occurs until this jump, you can bet that all this part was a checksum. Let's reverse the checksum, first go back to 0x1003b. 271 | ```nasm 272 | [0x00010020]> pd @0x1003b 273 | 0x0001003b b900000100 mov ecx, 0x10000 ; section.ehdr 274 | 0x00010040 31d2 xor edx, edx 275 | 0x00010042 31db xor ebx, ebx 276 | .-> 0x00010044 8a19 mov bl, byte [ecx] 277 | | 0x00010046 01da add edx, ebx 278 | | 0x00010048 41 inc ecx 279 | | 0x00010049 81f92e000100 cmp ecx, 0x1002e 280 | `=< 0x0001004f 75f3 jne 0x10044 281 | 0x00010051 c1e202 shl edx, 2 282 | 0x00010054 663b152e0001. cmp dx, word [0x1002e] ; [0x1002e:2]=0x140c 283 | ,=< 0x0001005b 7529 jne 0x10086 284 | ``` 285 | 286 | This code will load the address 0x10000 (Pointing to the first of the binary) in ecx, ebx and ebx are set to zero, and the loop starts: 287 | ``` 288 | 1. bl = *ecx 289 | 2. edx = edx + ebx 290 | 3. ecx++ 291 | 4. goto 1. if ecx != 0x1002e 292 | 5. edx = edx * 2 293 | 6. goto 0x10086 (badboy) if edx != [0x1002e] 294 | ``` 295 | 296 | Did you notice that the loop is increasing ecx, and not the value 'pointed' by ecx? This loop will add every bytes between 0x10000 and 0x1002e, and the sum must be equal to the value at 0x1002e. 297 | ```nasm 298 | [0x00010020]> pfw @0x1002e 299 | 0x0001002e = 0x140c 300 | ``` 301 | 302 | This is indeed a checksum to check the integrity of the header. 303 | ```nasm 304 | ,=< 0x0001005b 7529 jne 0x10086 305 | | 0x0001005d 31ed xor ebp, ebp 306 | | 0x0001005f 89d7 mov edi, edx 307 | | 0x00010061 45 inc ebp 308 | | 0x00010062 b810800000 mov eax, 0x8010 309 | | 0x00010067 45 inc ebp 310 | | 0x00010068 f7e5 mul ebp 311 | | 0x0001006a 96 xchg eax, esi 312 | | 0x0001006b 89f0 mov eax, esi 313 | | 0x0001006d 662b05140001. sub ax, word [0x10014] 314 | ,==< 0x00010074 7510 jne 0x10086 315 | || 0x00010076 29fe sub esi, edi 316 | || 0x00010078 6681f614ec xor si, 0xec14 317 | ,===< 0x0001007d 7507 jne 0x10086 318 | ,====< 0x0001007f eb01 jmp 0x10082 319 | ``` 320 | 321 | 1. ebp = 0 322 | 2. ebp = ebp + 1 + 1 323 | 3. eax = 0x8010 324 | 4. eax = ebp*eax 325 | 5. eax = eax - [0x10014] = eax - 0x140C 326 | 6. goto badboy if eax != 0 327 | 328 | Note: `mul ebp` is equivalent to `mul eax, ebp`. 329 | 330 | 1. esi = eax = 0x8010 * 2 331 | 2. esi = esi - edx = esi - 0x140C 332 | 3. si = si^0xec14 333 | 4. goto badboy if si != 0 334 | 335 | ```nasm 336 | [0x00010020]> pd @0x10082 337 | 0x00010082 31c0 xor eax, eax 338 | ,=< 0x00010084 755e jne 0x100e4 339 | | 0x00010086 29d2 sub edx, edx 340 | ``` 341 | 342 | The jump is never taken, and we'll end up in badboy. It seems that we should patch here. To load the file within radare2 in write mode, you can use the `-w` option. If the jump was taken, we'll land right after the badboy block. 343 | 344 | ### Decryption 345 | 346 | Let's go to 0x100e4: 347 | ```nasm 348 | [0x00010320]> pd @0x100e4 349 | .---> 0x000100e4 31d2 xor edx, edx 350 | ||| 0x000100e6 6839000100 push 0x10039 351 | ||| 0x000100eb 66832c240b sub word [esp], 0xb 352 | ||| 0x000100f0 5e pop esi 353 | ||| 0x000100f1 8d7601 lea esi, dword [esi + 1] ; 0x1 354 | ||| 0x000100f4 29c9 sub ecx, ecx 355 | `===< 0x000100f6 75ec jne 0x100e4 356 | .---> 0x000100f8 46 inc esi 357 | ,====< 0x000100f9 eb01 jmp 0x100fc 358 | |||| 0x000100fb c3 ret 359 | `----> 0x000100fc 8a16 mov dl, byte [esi] 360 | ||| 0x000100fe 88140c mov byte [esp + ecx], dl 361 | ||| 0x00010101 41 inc ecx 362 | ||| 0x00010102 83f909 cmp ecx, 9 363 | `===< 0x00010105 75f1 jne 0x100f8 364 | ``` 365 | 366 | Fancy push/pop trick at 0x000100e6: 367 | 1. 0x10039 is pushed on the stack 368 | 2. 0xb is substracted from [esp], which is indeed 0x10039 369 | 3. The top of the stack (0x10039 - 0xb) is popped into esi. 370 | 371 | The routine looks roughly like: 372 | 1. esi = 0x10039 373 | 2. esi = esi - 0xb + 1 + 1 = 0x10030 374 | 375 | It seems that 9 bytes are also pushed on the stack "manually", at 0x000100fe, in a small loop. 376 | ```nasm 377 | || 0x00010107 29d2 sub edx, edx 378 | || 0x00010109 31c9 xor ecx, ecx 379 | || 0x0001010b 41 inc ecx 380 | || 0x0001010c 8a140c mov dl, byte [esp + ecx] 381 | || 0x0001010f 80ea09 sub dl, 9 382 | || 0x00010112 80f2ac xor dl, 0xac 383 | ,===< 0x00010115 eb02 jmp 0x10119 384 | ||| 0x00010117 e84132540c call 0xc55335d 385 | || 0x0001011c ff88140c83f9 dec dword [eax - 0x67cf3ec] 386 | || 0x00010122 0875e6 or byte [ebp - 0x1a], dh 387 | || 0x00010125 41 inc ecx 388 | || 0x00010126 c6040c0a mov byte [esp + ecx], 0xa 389 | || 0x0001012a 49 dec ecx 390 | || 0x0001012b 87d1 xchg ecx, edx 391 | || 0x0001012d 42 inc edx 392 | || 0x0001012e 44 inc esp 393 | ,===< 0x0001012f eb01 jmp 0x10132 394 | ``` 395 | 396 | ```nasm 397 | [0x00010320]> pd @0x10119 398 | ||| 0x00010119 32540cff xor dl, byte [esp + ecx - 1] 399 | ||| 0x0001011d 88140c mov byte [esp + ecx], dl 400 | ||| 0x00010120 83f908 cmp ecx, 8 401 | `===< 0x00010123 75e6 jne 0x1010b 402 | || 0x00010125 41 inc ecx 403 | || 0x00010126 c6040c0a mov byte [esp + ecx], 0xa 404 | || 0x0001012a 49 dec ecx 405 | || 0x0001012b 87d1 xchg ecx, edx 406 | || 0x0001012d 42 inc edx 407 | || 0x0001012e 44 inc esp 408 | ,===< 0x0001012f eb01 jmp 0x10132 409 | ``` 410 | 411 | This looks like a decryption one. Nothing complicated. 412 | 1. edx = 0 413 | 2. ecx = 0 414 | 3. ecx = ecx + 1 415 | 4. dl = esp + ecx 416 | 5. dl = dl - 9 417 | 6. dl = dl^0xac 418 | 7. dl = dl^(esp+ecx=1) 419 | 8. goto 3. if ecx != 8 420 | 421 | Because the crypted string is: `0x90,0x7c,0x97,0xad,0xb6,0xb6,0xc6,0xc0,0xbf`. We can use python to do the decryption process: 422 | ```py 423 | >>> array = [ 0x90, 0x7C, 0x97, 0xAD, 0xB6, 0xB6, 0xC6, 0xC0, 0xBF ] 424 | >>> for i in range(0,8): 425 | ... array[i+1] = (array[i+1] - 9) ^ 0xac ^ array[i] 426 | >>> print ''.join([chr(i) for i in array]) 427 | �Omedetou 428 | ``` 429 | 430 | This prints `�Omedetou`, a Japanese word meaning Congratulations. The weird char on the front will likely be skip later. Looks like we're on the right track. 431 | 432 | ### Another checksum 433 | 434 | Let's go the 0x10132: 435 | ```nasm 436 | [0x00010320]> pd @0x10132 437 | || 0x00010132 b804000000 mov eax, 4 438 | || 0x00010137 bb01000000 mov ebx, 1 439 | || 0x0001013c 89e1 mov ecx, esp 440 | || 0x0001013e 60 pushal 441 | || 0x0001013f 31c9 xor ecx, ecx 442 | || 0x00010141 51 push ecx 443 | || 0x00010142 b900000100 mov ecx, 0x10000 ; section.ehdr 444 | || 0x00010147 5a pop edx 445 | || 0x00010148 89d3 mov ebx, edx 446 | .---> 0x0001014a 8a19 mov bl, byte [ecx] 447 | ||| 0x0001014c 01da add edx, ebx 448 | ||| 0x0001014e 41 inc ecx 449 | ||| 0x0001014f 81f972010100 cmp ecx, 0x10172 450 | `===< 0x00010155 75f3 jne 0x1014a 451 | ,===< 0x00010157 eb01 jmp 0x1015a 452 | ||| 0x00010159 cd66 int 0x66 ; 'f' 453 | || 0x0001015b 3b1572010100 cmp edx, dword [0x10172] ; [0x10172:4]=0xffff7f6d 454 | |`=< 0x00010161 0f851fffffff jne 0x10086 455 | | 0x00010167 61 popal 456 | |,=< 0x00010168 eb01 jmp 0x1016b 457 | || 0x0001016a c9 leave 458 | |`-> 0x0001016b cd80 int 0x80 459 | `==< 0x0001016d e966ffffff jmp 0x100d8 460 | 0x00010172 6d insd dword es:[edi], dx 461 | ,=< 0x00010173 7fff jg 0x10174 462 | [0x00010320]> pd @0x100d8 463 | .--> 0x000100d8 b801000000 mov eax, 1 464 | || 0x000100dd bb00000000 mov ebx, 0 465 | || 0x000100e2 cd80 int 0x80 466 | [0x00010320]> pfw @0x10172 467 | 0x00010172 = 0x7f6d 468 | [0x00010320]> ?d pushad 469 | push all general-purpose registers 470 | ``` 471 | 472 | Once again, a pushad. Since the pushad opcode might not be obvious for everyone. This will push on the stack the following registers: 473 | ``` 474 | 1. eax = 0x4 475 | 2. ebx = 0x1 476 | 3. ecx = the previously deciphered text + 1 477 | 4. edx = 0x8 478 | ``` 479 | 480 | After the checksum, registers are poped back, and a syscall (0x80) occurs. It's likely a call to sys_write; Then a jump to the sys_exit of badboy. 481 | 482 | ### Patching 483 | 484 | To sum up, we need to invert the jump at 0x10084, bypass the final chechsum at 0x10161 (Since it will detect the modification at 0x10084). 485 | 486 | First patch: 487 | 488 | ```nasm 489 | [0x00010320]> s 0x10084 490 | [0x00010084]> pd 1 491 | ,=< 0x00010084 755e jne 0x100e4 492 | [0x00010084]> oo+ 493 | File ./crackme.03 reopened in read-write mode 494 | Warning: Cannot initialize section headers 495 | Warning: Cannot initialize strings table 496 | Warning: Cannot initialize dynamic strings 497 | [0x00010084]> wx 74 498 | [0x00010084]> pd 1 499 | ,=< 0x00010084 745e je 0x100e4 500 | [0x00010084]> oo 501 | File ./crackme.03 reopened in read-only mode 502 | Warning: Cannot initialize section headers 503 | Warning: Cannot initialize strings table 504 | Warning: Cannot initialize dynamic strings 505 | ``` 506 | 507 | If you open your intel manual, you'll see that the opcode for jnz is 75, and the one for jz is 74. But you may not know every correspondences. Fortunately, radare2 provides more convenients way. 508 | 509 | Second patch: 510 | 511 | ```nasm 512 | [0x00010020]> s 0x10161 513 | [0x00010161]> pd 1 514 | `=< 0x00010161 0f851fffffff jne 0x10086 515 | [0x00010161]> oo+ 516 | File ./crackme.03 reopened in read-write mode 517 | Warning: Cannot initialize section headers 518 | Warning: Cannot initialize strings table 519 | Warning: Cannot initialize dynamic strings 520 | [0x00010161]> wa je 0x10086 521 | Written 6 bytes (je 0x10086) = wx 0f841fffffff 522 | [0x00010161]> pd 1 523 | `=< 0x00010161 0f841fffffff je 0x10086 524 | [0x00010161]> oo 525 | File ./crackme.03 reopened in read-only mode 526 | Warning: Cannot initialize section headers 527 | Warning: Cannot initialize strings table 528 | Warning: Cannot initialize dynamic strings 529 | ``` 530 | 531 | Let's check that everything is working: 532 | ```sh 533 | # ./crackme.03 534 | Omedetou 535 | ``` 536 | 537 | ## Reference 538 | 539 | - [Defeating crackme03 with radare2](https://dustri.org/b/defeating-crackme03-with-radare2.html) 540 | -------------------------------------------------------------------------------- /defeating-IOLI-with-radare2.md: -------------------------------------------------------------------------------- 1 | 2 | # Defeating ioli with radare2 3 | 4 | This post is from [here](https://dustri.org/b/defeating-ioli-with-radare2.html) 5 | 6 | Enjoy a completely rewritten reverse-engeenering tutorial proudly powered by radare2 ! 7 | 8 | Grab [radare2](http://www.radare.org/y/), an [asm cheat sheet](http://www.jegerlehner.ch/intel/), 9 | the IOLI crackme bin file (linux, win32 and pocketPC) [download from here](IOLI-crackme.zip), and geat ready. 10 | 11 | ## Gathering information 12 | 13 | In this case it is not really needed, but in general, you will want to gather as much information about the target as you want. You may also want to run it on a VM and actually take a snapshot before you start. Specially if you do dynamic analysis and you do not know what the sample does (backdoor, worm, virus, ...) 14 | 15 | Some tools you may want to use: 16 | - file patchme 17 | - sttrings patchme 18 | - xxd patchme | less 19 | - readelf -h ./patchme |grep Entry 20 | - ogjdump -Mintel -D ./patchme | grep "main>:" -A 8 21 | 22 | In this case, we already know everything for this program. After all, we wrote it ourselves, so let's go straight into the reverse stuff. 23 | 24 | ## crackme 0x00 25 | 26 | This is the first crackme, the easiest one. 27 | 28 | $ ./crackme0x00 29 | OLI Crackme Level 0x00 30 | Password: 1234 31 | Invalid Password! 32 | 33 | Maybe the password is in plain text inside it. 34 | No need to disassemble here, we'll just use _rabin2_, the 35 | "binary program info extractor" from radare2. 36 | 37 | The rabin2's option to show strings contained in a binary is _-z_ (_man rabin2_) 38 | 39 | $ rabin2 -z ./crackme0x00 40 | [strings] 41 | addr=0x08048568 off=0x00000568 ordinal=000 sz=24 section=.rodata string=IOLICrackmeLevel0x00 42 | addr=0x08048581 off=0x00000581 ordinal=001 sz=11 section=.rodata string=Password 43 | addr=0x0804858f off=0x0000058f ordinal=002 sz=7 section=.rodata string=250382 44 | addr=0x08048596 off=0x00000596 ordinal=003 sz=18 section=.rodata string=InvalidPassword! 45 | addr=0x080485a9 off=0x000005a9 ordinal=004 sz=15 section=.rodata string=PasswordOK 46 | 47 | 5 strings 48 | 49 | What is 250382 ? 50 | 51 | $ ./crackme0x00 52 | IOLI Crackme Level 0x00 53 | Password: 250382 54 | Password OK :) 55 | 56 | ## crackme0x01 57 | This time, no luck with _rabin2 -z_. 58 | Let's check with _radare2_. 59 | 60 | $ r2 ./crackme0x01 61 | [0x08048330]> aa 62 | [0x08048330]> pdf@sym.main 63 | / function: sym.main (113) 64 | | 0x080483e4 sym.main: 65 | | 0x080483e4 55 push ebp 66 | | 0x080483e5 89e5 mov ebp, esp 67 | | 0x080483e7 83ec18 sub esp, 0x18 68 | | 0x080483ea 83e4f0 and esp, 0xfffffff0 69 | | 0x080483ed b800000000 mov eax, 0x0 70 | | 0x080483f2 83c00f add eax, 0xf 71 | | 0x080483f5 83c00f add eax, 0xf 72 | | 0x080483f8 c1e804 shr eax, 0x4 73 | | 0x080483fb c1e004 shl eax, 0x4 74 | | 0x080483fe 29c4 sub esp, eax 75 | | 0x08048400 c7042428850408 mov dword [esp], str.IOLICrackmeLevel0x01 76 | | 0x08048407 e810ffffff call dword imp.printf 77 | | ; imp.printf() 78 | | 0x0804840c c7042441850408 mov dword [esp], str.Password 79 | | 0x08048413 e804ffffff call dword imp.printf 80 | | ; imp.printf() 81 | | 0x08048418 8d45fc lea eax, [ebp-0x4] 82 | | 0x0804841b 89442404 mov [esp+0x4], eax 83 | | 0x0804841f c704244c850408 mov dword [esp], 0x804854c 84 | | 0x08048426 e8e1feffff call dword imp.scanf 85 | | ; imp.scanf() 86 | | 0x0804842b 817dfc9a140000 cmp dword [ebp-0x4], 0x149a 87 | | ,=< 0x08048432 740e jz loc.08048442 88 | | | 0x08048434 c704244f850408 mov dword [esp], str.InvalidPassword! 89 | | | 0x0804843b e8dcfeffff call dword imp.printf 90 | | | ; imp.printf() 91 | | ,==< 0x08048440 eb0c jmp loc.0804844e 92 | | || ; CODE (JMP) XREF 0x08048432 (sym.main) 93 | / loc: loc.08048442 (19) 94 | | || 0x08048442 loc.08048442: 95 | | |`-> 0x08048442 c7042462850408 mov dword [esp], str.PasswordOK 96 | | | 0x08048449 e8cefeffff call dword imp.printf 97 | | | ; imp.printf() 98 | | | ; CODE (JMP) XREF 0x08048440 (sym.main) 99 | / loc: loc.0804844e (7) 100 | | | 0x0804844e loc.0804844e: 101 | | `--> 0x0804844e b800000000 mov eax, 0x0 102 | | 0x08048453 c9 leave 103 | \ 0x08048454 c3 ret 104 | 105 | The "aa" commands tells r2 to analyse the whole binary. This will get you nice symbols names and fancy stuffs. 106 | "pdf" stands for 107 | 108 | - print 109 | - disassemble 110 | - function 111 | 112 | So, this will print the disassembly of sym.main function, aka the main() that every one knows. 113 | Back to the listing, you can see several stuffs: weird names, arrows, ... 114 | 115 | - imp. stands for imports. Those are _imported_ symbols, like printf() 116 | - str. stands for strings. Those are strings (no shit !). 117 | 118 | If you look carefully, you'll see a _cmp_ instruction, with a constant: 0x149a. 119 | The "0x" in front of it indicates that it's in base 16. You can use radare2's to get it 120 | in another base: 121 | 122 | [0x08048330]> ? 0x149a 123 | 5274 0x149a 012232 10011010 0.000000 124 | 125 | Ok, 0x149a is 5274. 126 | 127 | $ ./crackme0x01 128 | IOLI Crackme Level 0x01 129 | Password: 5274 130 | Password OK :) 131 | 132 | ## crackme0x03 133 | 134 | aa 135 | pdf@sym.main 136 | / function: sym.main (128) 137 | | 0x08048498 sym.main: 138 | | 0x08048498 55 push ebp 139 | | 0x08048499 89e5 mov ebp, esp 140 | | 0x0804849b 83ec18 sub esp, 0x18 141 | | 0x0804849e 83e4f0 and esp, 0xfffffff0 142 | | 0x080484a1 b800000000 mov eax, 0x0 143 | | 0x080484a6 83c00f add eax, 0xf 144 | | 0x080484a9 83c00f add eax, 0xf 145 | | 0x080484ac c1e804 shr eax, 0x4 146 | | 0x080484af c1e004 shl eax, 0x4 147 | | 0x080484b2 29c4 sub esp, eax 148 | | 0x080484b4 c7042410860408 mov dword [esp], str.IOLICrackmeLevel0x03 149 | | 0x080484bb e890feffff call dword imp.printf 150 | | ; imp.printf() 151 | | 0x080484c0 c7042429860408 mov dword [esp], str.Password 152 | | 0x080484c7 e884feffff call dword imp.printf 153 | | ; imp.printf() 154 | | 0x080484cc 8d45fc lea eax, [ebp-0x4] 155 | | 0x080484cf 89442404 mov [esp+0x4], eax 156 | | 0x080484d3 c7042434860408 mov dword [esp], 0x8048634 157 | | 0x080484da e851feffff call dword imp.scanf 158 | | ; imp.scanf() 159 | | 0x080484df c745f85a000000 mov dword [ebp-0x8], 0x5a 160 | | 0x080484e6 c745f4ec010000 mov dword [ebp-0xc], 0x1ec 161 | | 0x080484ed 8b55f4 mov edx, [ebp-0xc] ; edx = 0x1ec 162 | | 0x080484f0 8d45f8 lea eax, [ebp-0x8] ; eax -> ebp-0x8 163 | | 0x080484f3 0110 add [eax], edx ; ebp-0x8 = (0x5a + 0x1ec) 164 | | 0x080484f5 8b45f8 mov eax, [ebp-0x8] ; eax = 0x5a + 0x1ec = 0x246 165 | | 0x080484f8 0faf45f8 imul eax, [ebp-0x8] ; eax = 0x246 * 0x246 = 0x52b24 166 | | 0x080484fc 8945f4 mov [ebp-0xc], eax ; ebp-0xc = 0x52b24 167 | | 0x080484ff 8b45f4 mov eax, [ebp-0xc] ; eax = 0x52b24 168 | | 0x08048502 89442404 mov [esp+0x4], eax ; esp+0x4 = eax 169 | | 0x08048506 8b45fc mov eax, [ebp-0x4] 170 | | 0x08048509 890424 mov [esp], eax 171 | | 0x0804850c e85dffffff call dword sym.test 172 | | ; sym.test() 173 | | 0x08048511 b800000000 mov eax, 0x0 174 | | 0x08048516 c9 leave 175 | \ 0x08048517 c3 ret 176 | ; ------------ 177 | 178 | Ho, a call to a interesting function: sym.test, called with two parameters: 179 | Likely our password, and 0x52b24 (or 338724 if you prefer). 180 | 181 | pdf@sym.test 182 | ; CODE (CALL) XREF 0x0804850c (sym.main) 183 | / function: sym.test (42) 184 | | 0x0804846e sym.test: 185 | | 0x0804846e 55 push ebp 186 | | 0x0804846f 89e5 mov ebp, esp 187 | | 0x08048471 83ec08 sub esp, 0x8 188 | | 0x08048474 8b4508 mov eax, [ebp+0x8] 189 | | 0x08048477 3b450c cmp eax, [ebp+0xc] 190 | | ,=< 0x0804847a 740e jz loc.0804848a 191 | | | 0x0804847c c70424ec850408 mov dword [esp], str.LqydolgSdvvzrug$ 192 | | | 0x08048483 e88cffffff call dword sym.shift 193 | | | ; sym.shift(unk) 194 | | ,==< 0x08048488 eb0c jmp loc.08048496 195 | | || ; CODE (JMP) XREF 0x0804847a (sym.test) 196 | / loc: loc.0804848a (14) 197 | | || 0x0804848a loc.0804848a: 198 | | |`-> 0x0804848a c70424fe850408 mov dword [esp], str.SdvvzrugRN$$$=, 199 | | | 0x08048491 e87effffff call dword sym.shift 200 | | | ; sym.shift() 201 | | | ; CODE (JMP) XREF 0x08048488 (sym.test) 202 | / loc: loc.08048496 (2) 203 | | | 0x08048496 loc.08048496: 204 | | `--> 0x08048496 c9 leave 205 | \ 0x08048497 c3 ret 206 | 207 | And now, you should must be lazy. There is a cmp, and two _path_, 208 | with mangled strings. This seems to be a goodboy/badboy. 209 | 210 | $ ./crackme0x03 211 | IOLI Crackme Level 0x03 212 | Password: 338724 213 | Password OK!!! :) 214 | 215 | You can also reverse the sym.shift function: 216 | 217 | [0x08048360]> pdf@sym.shift 218 | ; CODE (CALL) XREF 0x08048491 (sym.test) 219 | ; CODE (CALL) XREF 0x08048483 (sym.test) 220 | / function: sym.shift (90) 221 | | 0x08048414 sym.shift: 222 | | 0x08048414 55 push ebp 223 | | 0x08048415 89e5 mov ebp, esp 224 | | 0x08048417 81ec98000000 sub esp, 0x98 225 | | 0x0804841d c7458400000000 mov dword [ebp-0x7c], 0x0 ; this seems to be a counter 226 | | . ; CODE (JMP) XREF 0x0804844e (sym.shift) 227 | / loc: loc.08048424 (74) 228 | | . 0x08048424 loc.08048424: 229 | | .--> 0x08048424 8b4508 mov eax, [ebp+0x8] ; ebp+0x8 = strlen(chain) 230 | | | 0x08048427 890424 mov [esp], eax 231 | | | 0x0804842a e811ffffff call dword imp.strlen 232 | | | ; imp.strlen() 233 | | | 0x0804842f 394584 cmp [ebp-0x7c], eax 234 | | |,=< 0x08048432 731c jae loc.08048450 235 | | || 0x08048434 8d4588 lea eax, [ebp-0x78] 236 | | || 0x08048437 89c2 mov edx, eax 237 | | || 0x08048439 035584 add edx, [ebp-0x7c] 238 | | || 0x0804843c 8b4584 mov eax, [ebp-0x7c] 239 | | || 0x0804843f 034508 add eax, [ebp+0x8] 240 | | || 0x08048442 0fb600 movzx eax, byte [eax] 241 | | || 0x08048445 2c03 sub al, 0x3 242 | | || 0x08048447 8802 mov [edx], al 243 | | || 0x08048449 8d4584 lea eax, [ebp-0x7c] 244 | | || 0x0804844c ff00 inc dword [eax] 245 | | `==< 0x0804844e ebd4 jmp loc.08048424 246 | | | ; CODE (JMP) XREF 0x08048432 (sym.shift) 247 | / loc: loc.08048450 (30) 248 | | | 0x08048450 loc.08048450: 249 | | `-> 0x08048450 8d4588 lea eax, [ebp-0x78] 250 | | 0x08048453 034584 add eax, [ebp-0x7c] 251 | | 0x08048456 c60000 mov byte [eax], 0x0 252 | | 0x08048459 8d4588 lea eax, [ebp-0x78] 253 | | 0x0804845c 89442404 mov [esp+0x4], eax 254 | | 0x08048460 c70424e8850408 mov dword [esp], 0x80485e8 255 | | 0x08048467 e8e4feffff call dword imp.printf 256 | | ; imp.printf() 257 | | 0x0804846c c9 leave 258 | \ 0x0804846d c3 ret 259 | ; ------------ 260 | 261 | A strlen, a comparison to a counter, ... This looks like a (simple) decryption loop ! 262 | And the only operation done is actually a ""dec 0x3". Since this function is named _shift_, 263 | this seems plausible. Let's check with some Python: 264 | 265 | :::python 266 | print ''.join([chr(ord(i)-0x3) for i in 'SdvvzrugRN$$$']) 267 | PasswordOK!!! 268 | print ''.join([chr(ord(i)-0x3) for i in 'LqydolgSdvvzrug$']) 269 | InvalidPassword! 270 | 271 | Woohoo, we where right. 272 | 273 | 274 | ## crackme0x04 275 | 276 | [0x080483d0]> aa 277 | [0x080483d0]> pdf@sym.main 278 | / function: sym.main (92) 279 | | 0x08048509 sym.main: 280 | | 0x08048509 55 push ebp 281 | | 0x0804850a 89e5 mov ebp, esp 282 | | 0x0804850c 81ec88000000 sub esp, 0x88 283 | | 0x08048512 83e4f0 and esp, 0xfffffff0 284 | | 0x08048515 b800000000 mov eax, 0x0 285 | | 0x0804851a 83c00f add eax, 0xf 286 | | 0x0804851d 83c00f add eax, 0xf 287 | | 0x08048520 c1e804 shr eax, 0x4 288 | | 0x08048523 c1e004 shl eax, 0x4 289 | | 0x08048526 29c4 sub esp, eax 290 | | 0x08048528 c704245e860408 mov dword [esp], str.IOLICrackmeLevel0x04 291 | | 0x0804852f e860feffff call dword imp.printf 292 | | ; imp.printf() 293 | | 0x08048534 c7042477860408 mov dword [esp], str.Password 294 | | 0x0804853b e854feffff call dword imp.printf 295 | | ; imp.printf() 296 | | 0x08048540 8d4588 lea eax, [ebp-0x78] 297 | | 0x08048543 89442404 mov [esp+0x4], eax 298 | | 0x08048547 c7042482860408 mov dword [esp], 0x8048682 299 | | 0x0804854e e821feffff call dword imp.scanf 300 | | ; imp.scanf() 301 | | 0x08048553 8d4588 lea eax, [ebp-0x78] 302 | | 0x08048556 890424 mov [esp], eax 303 | | 0x08048559 e826ffffff call dword sym.check 304 | | ; sym.check() 305 | | 0x0804855e b800000000 mov eax, 0x0 306 | | 0x08048563 c9 leave 307 | \ 0x08048564 c3 ret 308 | ; ------------ 309 | 310 | Nothing funky nor new. 311 | 312 | [0x080483d0]> pdf@sym.check 313 | ; CODE (CALL) XREF 0x08048559 (sym.main) 314 | / function: sym.check (133) 315 | | 0x08048484 sym.check: 316 | | 0x08048484 55 push ebp 317 | | 0x08048485 89e5 mov ebp, esp 318 | | 0x08048487 83ec28 sub esp, 0x28 319 | | 0x0804848a c745f800000000 mov dword [ebp-0x8], 0x0 ; smells like those lines 320 | | 0x08048491 c745f400000000 mov dword [ebp-0xc], 0x0 ; are counters ! 321 | | . ; CODE (JMP) XREF 0x080484f9 (sym.check) 322 | / loc: loc.08048498 (113) 323 | | . 0x08048498 loc.08048498: 324 | | .---> 0x08048498 8b4508 mov eax, [ebp+0x8] 325 | | | 0x0804849b 890424 mov [esp], eax 326 | | | 0x0804849e e8e1feffff call dword imp.strlen 327 | | | ; imp.strlen() 328 | | | 0x080484a3 3945f4 cmp [ebp-0xc], eax ; counter > strlen ? 329 | | | ,=< 0x080484a6 7353 jae loc.080484fb ; if yes, jumps to badboy 330 | | | | 0x080484a8 8b45f4 mov eax, [ebp-0xc] 331 | | | | 0x080484ab 034508 add eax, [ebp+0x8] 332 | | | | 0x080484ae 0fb600 movzx eax, byte [eax] 333 | | | | 0x080484b1 8845f3 mov [ebp-0xd], al 334 | | | | 0x080484b4 8d45fc lea eax, [ebp-0x4] 335 | | | | 0x080484b7 89442408 mov [esp+0x8], eax 336 | | | | 0x080484bb c744240438860408 mov dword [esp+0x4], 0x8048638 ; what is that ? 337 | | | | 0x080484c3 8d45f3 lea eax, [ebp-0xd] 338 | | | | 0x080484c6 890424 mov [esp], eax 339 | | | | 0x080484c9 e8d6feffff call dword imp.sscanf 340 | | | | ; imp.sscanf() 341 | | | | 0x080484ce 8b55fc mov edx, [ebp-0x4] ; edx = scanf()'s result 342 | | | | 0x080484d1 8d45f8 lea eax, [ebp-0x8] 343 | | | | 0x080484d4 0110 add [eax], edx ; ebp-0x8 is incremented 344 | | | | 0x080484d6 837df80f cmp dword [ebp-0x8], 0xf ; and compared to 0xf 345 | | |,==< 0x080484da 7518 jnz loc.080484f4 ; if not equals, jump ! 346 | | ||| 0x080484dc c704243b860408 mov dword [esp], str.PasswordOK! 347 | | ||| 0x080484e3 e8acfeffff call dword imp.printf 348 | | ||| ; imp.printf() 349 | | ||| 0x080484e8 c7042400000000 mov dword [esp], 0x0 350 | | ||| 0x080484ef e8c0feffff call dword imp.exit 351 | | ||| ; imp.exit() 352 | | || ; CODE (JMP) XREF 0x080484da (sym.check) 353 | / loc: loc.080484f4 (21) 354 | | || 0x080484f4 loc.080484f4: 355 | | |`--> 0x080484f4 8d45f4 lea eax, [ebp-0xc] 356 | | | | 0x080484f7 ff00 inc dword [eax] 357 | | `===< 0x080484f9 eb9d jmp loc.08048498 358 | | | ; CODE (JMP) XREF 0x080484a6 (sym.check) 359 | / loc: loc.080484fb (14) 360 | | | 0x080484fb loc.080484fb: 361 | | `-> 0x080484fb c7042449860408 mov dword [esp], str.PasswordIncorrect! 362 | | 0x08048502 e88dfeffff call dword imp.printf 363 | | ; imp.printf() 364 | | 0x08048507 c9 leave 365 | \ 0x08048508 c3 ret 366 | ; ------------ 367 | 368 | Strlen again, a loop, scanf, ... 369 | 370 | What is send to scanf ? 371 | 372 | [0x080483d0]> s 0x8048638 373 | [0x08048638]> ps 374 | %d 375 | [0x08048638]> 376 | 377 | This seems to be some kind of atoi(), but with scanf(). 378 | So, our password's sum must be equals to 0xf (aka 15) at some point. 379 | 380 | $ ./crackme0x04 381 | IOLI Crackme Level 0x04 382 | Password: 96 383 | Password OK! 384 | 385 | ## crackme0x05 386 | 387 | [0x080483d0]> aa 388 | [0x080483d0]> pdf@sym.main 389 | / function: sym.main (92) 390 | | 0x08048540 sym.main: 391 | | 0x08048540 55 push ebp 392 | | 0x08048541 89e5 mov ebp, esp 393 | | 0x08048543 81ec88000000 sub esp, 0x88 394 | | 0x08048549 83e4f0 and esp, 0xfffffff0 395 | | 0x0804854c b800000000 mov eax, 0x0 396 | | 0x08048551 83c00f add eax, 0xf 397 | | 0x08048554 83c00f add eax, 0xf 398 | | 0x08048557 c1e804 shr eax, 0x4 399 | | 0x0804855a c1e004 shl eax, 0x4 400 | | 0x0804855d 29c4 sub esp, eax 401 | | 0x0804855f c704248e860408 mov dword [esp], str.IOLICrackmeLevel0x05 402 | | 0x08048566 e829feffff call dword imp.printf 403 | | ; imp.printf() 404 | | 0x0804856b c70424a7860408 mov dword [esp], str.Password 405 | | 0x08048572 e81dfeffff call dword imp.printf 406 | | ; imp.printf() 407 | | 0x08048577 8d4588 lea eax, [ebp-0x78] 408 | | 0x0804857a 89442404 mov [esp+0x4], eax 409 | | 0x0804857e c70424b2860408 mov dword [esp], 0x80486b2 410 | | 0x08048585 e8eafdffff call dword imp.scanf 411 | | ; imp.scanf() 412 | | 0x0804858a 8d4588 lea eax, [ebp-0x78] 413 | | 0x0804858d 890424 mov [esp], eax 414 | | 0x08048590 e833ffffff call dword sym.check 415 | | ; sym.check() 416 | | 0x08048595 b800000000 mov eax, 0x0 417 | | 0x0804859a c9 leave 418 | \ 0x0804859b c3 ret 419 | ; ------------ 420 | 421 | Boring. 422 | 423 | [0x080483d0]> pdf@sym.check 424 | ; CODE (CALL) XREF 0x08048590 (sym.main) 425 | / function: sym.check (120) 426 | | 0x080484c8 sym.check: 427 | | 0x080484c8 55 push ebp 428 | | 0x080484c9 89e5 mov ebp, esp 429 | | 0x080484cb 83ec28 sub esp, 0x28 430 | | 0x080484ce c745f800000000 mov dword [ebp-0x8], 0x0 431 | | 0x080484d5 c745f400000000 mov dword [ebp-0xc], 0x0 432 | | . ; CODE (JMP) XREF 0x08048530 (sym.check) 433 | / loc: loc.080484dc (100) 434 | | . 0x080484dc loc.080484dc: 435 | | .---> 0x080484dc 8b4508 mov eax, [ebp+0x8] 436 | | | 0x080484df 890424 mov [esp], eax 437 | | | 0x080484e2 e89dfeffff call dword imp.strlen 438 | | | ; imp.strlen() 439 | | | 0x080484e7 3945f4 cmp [ebp-0xc], eax 440 | | | ,=< 0x080484ea 7346 jae loc.08048532 441 | | | | 0x080484ec 8b45f4 mov eax, [ebp-0xc] 442 | | | | 0x080484ef 034508 add eax, [ebp+0x8] 443 | | | | 0x080484f2 0fb600 movzx eax, byte [eax] 444 | | | | 0x080484f5 8845f3 mov [ebp-0xd], al 445 | | | | 0x080484f8 8d45fc lea eax, [ebp-0x4] 446 | | | | 0x080484fb 89442408 mov [esp+0x8], eax 447 | | | | 0x080484ff c744240468860408 mov dword [esp+0x4], 0x8048668 448 | | | | 0x08048507 8d45f3 lea eax, [ebp-0xd] 449 | | | | 0x0804850a 890424 mov [esp], eax 450 | | | | 0x0804850d e892feffff call dword imp.sscanf 451 | | | | ; imp.sscanf() 452 | | | | 0x08048512 8b55fc mov edx, [ebp-0x4] 453 | | | | 0x08048515 8d45f8 lea eax, [ebp-0x8] 454 | | | | 0x08048518 0110 add [eax], edx 455 | | | | 0x0804851a 837df810 cmp dword [ebp-0x8], 0x10 456 | | |,==< 0x0804851e 750b jnz loc.0804852b 457 | | ||| 0x08048520 8b4508 mov eax, [ebp+0x8] 458 | | ||| 0x08048523 890424 mov [esp], eax 459 | | ||| 0x08048526 e859ffffff call dword sym.parell 460 | | ||| ; sym.parell() 461 | | || ; CODE (JMP) XREF 0x0804851e (sym.check) 462 | / loc: loc.0804852b (21) 463 | | || 0x0804852b loc.0804852b: 464 | | |`--> 0x0804852b 8d45f4 lea eax, [ebp-0xc] 465 | | | | 0x0804852e ff00 inc dword [eax] 466 | | `===< 0x08048530 ebaa jmp loc.080484dc 467 | | | ; CODE (JMP) XREF 0x080484ea (sym.check) 468 | / loc: loc.08048532 (14) 469 | | | 0x08048532 loc.08048532: 470 | | `-> 0x08048532 c7042479860408 mov dword [esp], str.PasswordIncorrect! 471 | | 0x08048539 e856feffff call dword imp.printf 472 | | ; imp.printf() 473 | | 0x0804853e c9 leave 474 | \ 0x0804853f c3 ret 475 | ; ------------ 476 | 477 | Same function as the previous crackme, but this time, it's not compared to 15, but to 16. 478 | And instead of a printf("Password OK!"), there is a call to sym.pharell 479 | 480 | [0x080483d0]> pdf@sym.parell 481 | ; CODE (CALL) XREF 0x08048526 (sym.check) 482 | / function: sym.parell (68) 483 | | 0x08048484 sym.parell: 484 | | 0x08048484 55 push ebp 485 | | 0x08048485 89e5 mov ebp, esp 486 | | 0x08048487 83ec18 sub esp, 0x18 487 | | 0x0804848a 8d45fc lea eax, [ebp-0x4] 488 | | 0x0804848d 89442408 mov [esp+0x8], eax 489 | | 0x08048491 c744240468860408 mov dword [esp+0x4], 0x8048668 490 | | 0x08048499 8b4508 mov eax, [ebp+0x8] 491 | | 0x0804849c 890424 mov [esp], eax 492 | | 0x0804849f e800ffffff call dword imp.sscanf 493 | | ; imp.sscanf() 494 | | 0x080484a4 8b45fc mov eax, [ebp-0x4] 495 | | 0x080484a7 83e001 and eax, 0x1 496 | | 0x080484aa 85c0 test eax, eax 497 | | ,=< 0x080484ac 7518 jnz loc.080484c6 498 | | | 0x080484ae c704246b860408 mov dword [esp], str.PasswordOK! 499 | | | 0x080484b5 e8dafeffff call dword imp.printf 500 | | | ; imp.printf() 501 | | | 0x080484ba c7042400000000 mov dword [esp], 0x0 502 | | | 0x080484c1 e8eefeffff call dword imp.exit 503 | | | ; imp.exit() 504 | | | ; CODE (JMP) XREF 0x080484ac (sym.parell) 505 | / loc: loc.080484c6 (2) 506 | | | 0x080484c6 loc.080484c6: 507 | | `-> 0x080484c6 c9 leave 508 | \ 0x080484c7 c3 ret 509 | ; ------------ 510 | 511 | Another scanf(), used as an atoi(). It's return value is and'ed with 1, 512 | and if the result is 0, goodboy ! As everyone knows, and'ing with 1 is the 513 | same as testing is the number is odd. 514 | 515 | $ ./crackme0x05 516 | IOLI Crackme Level 0x05 517 | Password: 664 518 | Password OK! 519 | 520 | ## crackme0x06 521 | 522 | pdf@sym.main 523 | / function: sym.main (99) 524 | | 0x08048607 sym.main: 525 | | 0x08048607 55 push ebp 526 | | 0x08048608 89e5 mov ebp, esp 527 | | 0x0804860a 81ec88000000 sub esp, 0x88 528 | | 0x08048610 83e4f0 and esp, 0xfffffff0 529 | | 0x08048613 b800000000 mov eax, 0x0 530 | | 0x08048618 83c00f add eax, 0xf 531 | | 0x0804861b 83c00f add eax, 0xf 532 | | 0x0804861e c1e804 shr eax, 0x4 533 | | 0x08048621 c1e004 shl eax, 0x4 534 | | 0x08048624 29c4 sub esp, eax 535 | | 0x08048626 c7042463870408 mov dword [esp], str.IOLICrackmeLevel0x06 536 | | 0x0804862d e886fdffff call dword imp.printf 537 | | ; imp.printf() 538 | | 0x08048632 c704247c870408 mov dword [esp], str.Password 539 | | 0x08048639 e87afdffff call dword imp.printf 540 | | ; imp.printf() 541 | | 0x0804863e 8d4588 lea eax, [ebp-0x78] 542 | | 0x08048641 89442404 mov [esp+0x4], eax 543 | | 0x08048645 c7042487870408 mov dword [esp], 0x8048787 544 | | 0x0804864c e847fdffff call dword imp.scanf 545 | | ; imp.scanf() 546 | | 0x08048651 8b4510 mov eax, [ebp+0x10] 547 | | 0x08048654 89442404 mov [esp+0x4], eax 548 | | 0x08048658 8d4588 lea eax, [ebp-0x78] 549 | | 0x0804865b 890424 mov [esp], eax 550 | | 0x0804865e e825ffffff call dword sym.check 551 | | ; sym.check() 552 | | 0x08048663 b800000000 mov eax, 0x0 553 | | 0x08048668 c9 leave 554 | \ 0x08048669 c3 ret 555 | ; ------------ 556 | 557 | Blablabla, same stuff than previously, blablabla. 558 | Or is it ? 559 | Check again. 560 | 561 | You can see that this time, the _sym.check_ function takes 2 parameters. 562 | 563 | 1. The result of scanf(), ([ebp-0x78]) in esp 564 | 2. [ebp+10] in [esp+0x4] 565 | 566 | Since main() is a function, and this code is compiled with GCC, 567 | you can expect a stack like this: 568 | 569 | [esp + 0x10] - envp 570 | [esp + 0x0c] - argv 571 | [esp + 0x08] - argc 572 | [esp + 0x04] - return address 573 | 574 | So, our sym.check call looks like: 575 | 576 | check(int password, char* argv[]); 577 | 578 | Except this, the code is the same that the previous binary (except that envp is passed as an argument) for sym.main, sym.check, sym.parell, ... 579 | Or it is ? 580 | Check once again ;) 581 | The code is different in sym.parell. 582 | You can notice a call to sym.dummy. 583 | 584 | [0x08048400]> pdf@sym.dummy 585 | ; CODE (CALL) XREF 0x08048547 (sym.parell) 586 | / function: sym.dummy (102) 587 | | 0x080484b4 sym.dummy: 588 | | 0x080484b4 55 push ebp 589 | | 0x080484b5 89e5 mov ebp, esp 590 | | 0x080484b7 83ec18 sub esp, 0x18 591 | | 0x080484ba c745fc00000000 mov dword [ebp-0x4], 0x0 592 | | . ; CODE (JMP) XREF 0x08048503 (sym.dummy) 593 | / loc: loc.080484c1 (89) 594 | | . 0x080484c1 loc.080484c1: 595 | | .--> 0x080484c1 8b45fc mov eax, [ebp-0x4] 596 | | | 0x080484c4 8d148500000000 lea edx, [eax*4+0x0] 597 | | | 0x080484cb 8b450c mov eax, [ebp+0xc] 598 | | | 0x080484ce 833c0200 cmp dword [edx+eax], 0x0 599 | | |,=< 0x080484d2 743a jz loc.0804850e 600 | | || 0x080484d4 8b45fc mov eax, [ebp-0x4] 601 | | || 0x080484d7 8d0c8500000000 lea ecx, [eax*4+0x0] 602 | | || 0x080484de 8b550c mov edx, [ebp+0xc] 603 | | || 0x080484e1 8d45fc lea eax, [ebp-0x4] 604 | | || 0x080484e4 ff00 inc dword [eax] 605 | | || 0x080484e6 c744240803000000 mov dword [esp+0x8], 0x3 606 | | || 0x080484ee c744240438870408 mov dword [esp+0x4], str.LOLO 607 | | || 0x080484f6 8b0411 mov eax, [ecx+edx] 608 | | || 0x080484f9 890424 mov [esp], eax 609 | | || 0x080484fc e8d7feffff call dword imp.strncmp 610 | | || ; imp.strncmp() 611 | | || 0x08048501 85c0 test eax, eax 612 | | `==< 0x08048503 75bc jnz loc.080484c1 613 | | | 0x08048505 c745f801000000 mov dword [ebp-0x8], 0x1 614 | | ,===< 0x0804850c eb07 jmp loc.08048515 615 | | | | ; CODE (JMP) XREF 0x080484d2 (sym.dummy) 616 | / loc: loc.0804850e (12) 617 | | | | 0x0804850e loc.0804850e: 618 | | | `-> 0x0804850e c745f800000000 mov dword [ebp-0x8], 0x0 619 | | | ; CODE (JMP) XREF 0x0804850c (sym.dummy) 620 | / loc: loc.08048515 (5) 621 | | | 0x08048515 loc.08048515: 622 | | `---> 0x08048515 8b45f8 mov eax, [ebp-0x8] 623 | | 0x08048518 c9 leave 624 | \ 0x08048519 c3 ret 625 | ; ------------ 626 | 627 | Let's be clever lazy once again: 628 | 629 | 1. str.LOLO 630 | 2. strncmp() 631 | 3. no new input/output compared to the previous binary 632 | 4. the environnement pointer is passed form sym.main to sym.check to sym.parell ... 633 | 634 | Looks like the binary wants the same things that the previous one, _plus_ an environnement variable named "LOLO". 635 | 636 | $ LOLO= ./crackme0x06 637 | IOLI Crackme Level 0x06 638 | Password: 556 639 | Password OK! 640 | 641 | Maybe you asked yourself "How the hell am I supposed to recognize that this is GDB's output ?!". 642 | By experience. 643 | But, there is another way: 644 | 645 | $ rabin2 -S ./crackme0x06 646 | [Sections] 647 | idx=00 addr=0x08048000 off=0x00000000 sz=0 vsz=0 perm=---- name= 648 | idx=01 addr=0x08048154 off=0x00000154 sz=19 vsz=19 perm=-r-- name=.interp 649 | idx=02 addr=0x08048168 off=0x00000168 sz=32 vsz=32 perm=-r-- name=.note.ABItag 650 | idx=03 addr=0x08048188 off=0x00000188 sz=60 vsz=60 perm=-r-- name=.hash 651 | idx=04 addr=0x080481c4 off=0x000001c4 sz=32 vsz=32 perm=-r-- name=.gnu.hash 652 | idx=05 addr=0x080481e4 off=0x000001e4 sz=160 vsz=160 perm=-r-- name=.dynsym 653 | idx=06 addr=0x08048284 off=0x00000284 sz=103 vsz=103 perm=-r-- name=.dynstr 654 | idx=07 addr=0x080482ec off=0x000002ec sz=20 vsz=20 perm=-r-- name=.gnu.version 655 | idx=08 addr=0x08048300 off=0x00000300 sz=32 vsz=32 perm=-r-- name=.gnu.version_r 656 | idx=09 addr=0x08048320 off=0x00000320 sz=8 vsz=8 perm=-r-- name=.rel.dyn 657 | idx=10 addr=0x08048328 off=0x00000328 sz=56 vsz=56 perm=-r-- name=.rel.plt 658 | idx=11 addr=0x08048360 off=0x00000360 sz=23 vsz=23 perm=-r-x name=.init 659 | idx=12 addr=0x08048378 off=0x00000378 sz=128 vsz=128 perm=-r-x name=.plt 660 | idx=13 addr=0x08048400 off=0x00000400 sz=788 vsz=788 perm=-r-x name=.text 661 | idx=14 addr=0x08048714 off=0x00000714 sz=26 vsz=26 perm=-r-x name=.fini 662 | idx=15 addr=0x08048730 off=0x00000730 sz=90 vsz=90 perm=-r-- name=.rodata 663 | idx=16 addr=0x0804878c off=0x0000078c sz=4 vsz=4 perm=-r-- name=.eh_frame 664 | idx=17 addr=0x08049f0c off=0x00000f0c sz=8 vsz=8 perm=-rw- name=.ctors 665 | idx=18 addr=0x08049f14 off=0x00000f14 sz=8 vsz=8 perm=-rw- name=.dtors 666 | idx=19 addr=0x08049f1c off=0x00000f1c sz=4 vsz=4 perm=-rw- name=.jcr 667 | idx=20 addr=0x08049f20 off=0x00000f20 sz=208 vsz=208 perm=-rw- name=.dynamic 668 | idx=21 addr=0x08049ff0 off=0x00000ff0 sz=4 vsz=4 perm=-rw- name=.got 669 | idx=22 addr=0x08049ff4 off=0x00000ff4 sz=40 vsz=40 perm=-rw- name=.got.plt 670 | idx=23 addr=0x0804a01c off=0x0000101c sz=12 vsz=12 perm=-rw- name=.data 671 | idx=24 addr=0x0804a028 off=0x00001028 sz=4 vsz=4 perm=-rw- name=.bss 672 | idx=25 addr=0x08049028 off=0x00001028 sz=441 vsz=441 perm=---- name=.comment 673 | idx=26 addr=0x080491e1 off=0x000011e1 sz=219 vsz=219 perm=---- name=.shstrtab 674 | idx=27 addr=0x08049744 off=0x00001744 sz=1152 vsz=1152 perm=---- name=.symtab 675 | idx=28 addr=0x08049bc4 off=0x00001bc4 sz=609 vsz=609 perm=---- name=.strtab 676 | 677 | 29 sections 678 | 679 | Since this binary is not stripped (_man strip_), you can notice a ".comment" section. 680 | $ r2 ./crackme0x06 681 | [0x08048400]> s section..comment 682 | [0x08049028]> ps 128 683 | \x00GCC: (GNU) 3.4.6 (Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.10)\x00\x00GCC: (GNU) 3.4.6 (Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.10)\x00\x00G 684 | 685 | Yay, GCC 3.4.6 on a Gentoo 3.4.6-r2 ! 686 | 687 | 688 | ## crackme0x07 689 | 690 | 691 | [0x08048400]> aa 692 | [0x08048400]> pdf 693 | / function: section..text (34) 694 | | 0x08048400 section..text: 695 | | 0x08048400 31ed xor ebp, ebp ; [13] va=0x08048400 pa=0x00000400 sz=900 vsz=900 rwx=-r-x .text 696 | | 0x08048402 5e pop esi 697 | | 0x08048403 89e1 mov ecx, esp 698 | | 0x08048405 83e4f0 and esp, 0xfffffff0 699 | | 0x08048408 50 push eax 700 | | 0x08048409 54 push esp 701 | | 0x0804840a 52 push edx 702 | | 0x0804840b 6850870408 push dword 0x8048750 703 | | 0x08048410 68e0860408 push dword 0x80486e0 704 | | 0x08048415 51 push ecx 705 | | 0x08048416 56 push esi 706 | | 0x08048417 687d860408 push dword 0x804867d 707 | | 0x0804841c e867ffffff call dword imp.__libc_start_main 708 | | ; imp.__libc_start_main() 709 | \ 0x08048421 f4 hlt 710 | ; ------------ 711 | 712 | wat. 713 | What happened to symbols ?! 714 | 715 | $ rabin2 -I ./crackme0x07 716 | [File info] 717 | File=/home/jvoisin/dev/reverse/crackme/done/IOLI-crackme/bin-linux/./crackme0x07 718 | Type=EXEC (Executable file) 719 | HasVA=true 720 | RootClass=elf 721 | Class=ELF32 722 | Arch=x86 32 723 | Machine=Intel 80386 724 | OS=linux 725 | Subsystem=linux 726 | Big endian=false 727 | Stripped=true 728 | Static=false 729 | Line_nums=false 730 | Local_syms=false 731 | Relocs=false 732 | RPath=NONE 733 | 734 | This binary is stripped : no more symbols. 735 | 736 | Since this is GCC-produced code, the main is likely at 0x804867d (the last push before _imp.__libc_start_main_) 737 | 738 | $ r2 ./crackme0x07 739 | [0x08048400]> aa 740 | [0x08048400]> pdf 741 | / function: section..text (34) 742 | | 0x08048400 section..text: 743 | | 0x08048400 31ed xor ebp, ebp ; [13] va=0x08048400 pa=0x00000400 sz=900 vsz=900 rwx=-r-x .text 744 | | 0x08048402 5e pop esi 745 | | 0x08048403 89e1 mov ecx, esp 746 | | 0x08048405 83e4f0 and esp, 0xfffffff0 747 | | 0x08048408 50 push eax 748 | | 0x08048409 54 push esp 749 | | 0x0804840a 52 push edx 750 | | 0x0804840b 6850870408 push dword 0x8048750 751 | | 0x08048410 68e0860408 push dword 0x80486e0 752 | | 0x08048415 51 push ecx 753 | | 0x08048416 56 push esi 754 | | 0x08048417 687d860408 push dword 0x804867d 755 | | 0x0804841c e867ffffff call dword imp.__libc_start_main 756 | | ; imp.__libc_start_main() 757 | \ 0x08048421 f4 hlt 758 | ; ------------ 759 | 760 | By the way, this is the _start_ function. 761 | 762 | [0x08048400]> pdf@0x804867d 763 | / function: main (99) 764 | | 0x0804867d main: 765 | | 0x0804867d 55 push ebp 766 | | 0x0804867e 89e5 mov ebp, esp 767 | | 0x08048680 81ec88000000 sub esp, 0x88 768 | | 0x08048686 83e4f0 and esp, 0xfffffff0 769 | | 0x08048689 b800000000 mov eax, 0x0 770 | | 0x0804868e 83c00f add eax, 0xf 771 | | 0x08048691 83c00f add eax, 0xf 772 | | 0x08048694 c1e804 shr eax, 0x4 773 | | 0x08048697 c1e004 shl eax, 0x4 774 | | 0x0804869a 29c4 sub esp, eax 775 | | 0x0804869c c70424d9870408 mov dword [esp], str.IOLICrackmeLevel0x07 776 | | 0x080486a3 e810fdffff call dword imp.printf 777 | | ; imp.printf() 778 | | 0x080486a8 c70424f2870408 mov dword [esp], str.Password 779 | | 0x080486af e804fdffff call dword imp.printf 780 | | ; imp.printf() 781 | | 0x080486b4 8d4588 lea eax, [ebp-0x78] 782 | | 0x080486b7 89442404 mov [esp+0x4], eax 783 | | 0x080486bb c70424fd870408 mov dword [esp], 0x80487fd 784 | | 0x080486c2 e8d1fcffff call dword imp.scanf 785 | | ; imp.scanf() 786 | | 0x080486c7 8b4510 mov eax, [ebp+0x10] 787 | | 0x080486ca 89442404 mov [esp+0x4], eax 788 | | 0x080486ce 8d4588 lea eax, [ebp-0x78] 789 | | 0x080486d1 890424 mov [esp], eax 790 | | 0x080486d4 e8e0feffff call dword fcn.080485b9 791 | | ; fcn.080485b9() 792 | | 0x080486d9 b800000000 mov eax, 0x0 793 | | 0x080486de c9 leave 794 | \ 0x080486df c3 ret 795 | ; ------------ 796 | 797 | Our main(). 798 | 799 | [0x08048400]> pdf@fcn.080485b9 800 | ; CODE (CALL) XREF 0x080486d4 (main) 801 | / function: fcn.080485b9 (196) 802 | | 0x080485b9 fcn.080485b9: 803 | | 0x080485b9 55 push ebp 804 | | 0x080485ba 89e5 mov ebp, esp 805 | | 0x080485bc 83ec28 sub esp, 0x28 806 | | 0x080485bf c745f800000000 mov dword [ebp-0x8], 0x0 807 | | 0x080485c6 c745f400000000 mov dword [ebp-0xc], 0x0 808 | | . ; CODE (JMP) XREF 0x08048628 (fcn.080485b9) 809 | / loc: loc.080485cd (176) 810 | | . 0x080485cd loc.080485cd: 811 | | .---> 0x080485cd 8b4508 mov eax, [ebp+0x8] 812 | | | 0x080485d0 890424 mov [esp], eax 813 | | | 0x080485d3 e8d0fdffff call dword imp.strlen 814 | | | ; imp.strlen() 815 | | | 0x080485d8 3945f4 cmp [ebp-0xc], eax 816 | | | ,=< 0x080485db 734d jae loc.0804862a 817 | | | | 0x080485dd 8b45f4 mov eax, [ebp-0xc] 818 | | | | 0x080485e0 034508 add eax, [ebp+0x8] 819 | | | | 0x080485e3 0fb600 movzx eax, byte [eax] 820 | | | | 0x080485e6 8845f3 mov [ebp-0xd], al 821 | | | | 0x080485e9 8d45fc lea eax, [ebp-0x4] 822 | | | | 0x080485ec 89442408 mov [esp+0x8], eax 823 | | | | 0x080485f0 c7442404c2870408 mov dword [esp+0x4], 0x80487c2 824 | | | | 0x080485f8 8d45f3 lea eax, [ebp-0xd] 825 | | | | 0x080485fb 890424 mov [esp], eax 826 | | | | 0x080485fe e8c5fdffff call dword imp.sscanf 827 | | | | ; imp.sscanf() 828 | | | | 0x08048603 8b55fc mov edx, [ebp-0x4] 829 | | | | 0x08048606 8d45f8 lea eax, [ebp-0x8] 830 | | | | 0x08048609 0110 add [eax], edx 831 | | | | 0x0804860b 837df810 cmp dword [ebp-0x8], 0x10 832 | | |,==< 0x0804860f 7512 jnz loc.08048623 833 | | ||| 0x08048611 8b450c mov eax, [ebp+0xc] 834 | | ||| 0x08048614 89442404 mov [esp+0x4], eax 835 | | ||| 0x08048618 8b4508 mov eax, [ebp+0x8] 836 | | ||| 0x0804861b 890424 mov [esp], eax 837 | | ||| 0x0804861e e81fffffff call dword fcn.08048542 838 | | ||| ; fcn.08048542() 839 | | || ; CODE (JMP) XREF 0x0804860f (fcn.080485b9) 840 | / loc: loc.08048623 (90) 841 | | || 0x08048623 loc.08048623: 842 | | |`--> 0x08048623 8d45f4 lea eax, [ebp-0xc] 843 | | | | 0x08048626 ff00 inc dword [eax] 844 | | `===< 0x08048628 eba3 jmp loc.080485cd 845 | | | ; CODE (JMP) XREF 0x080485db (fcn.080485b9) 846 | / loc: loc.0804862a (83) 847 | | | 0x0804862a loc.0804862a: 848 | | `-> 0x0804862a e8f5feffff call dword fcn.08048524 849 | | | ; fcn.08048524() 850 | | 0x0804862f 8b450c mov eax, [ebp+0xc] 851 | | 0x08048632 89442404 mov [esp+0x4], eax 852 | | 0x08048636 8b45fc mov eax, [ebp-0x4] 853 | | 0x08048639 890424 mov [esp], eax 854 | | 0x0804863c e873feffff call dword fcn.080484b4 855 | | ; fcn.080484b4() 856 | | 0x08048641 85c0 test eax, eax 857 | | ,====< 0x08048643 7436 jz loc.0804867b 858 | | | 0x08048645 c745f400000000 mov dword [ebp-0xc], 0x0 859 | | | ; CODE (JMP) XREF 0x08048679 (fcn.080485b9) 860 | / loc: loc.0804864c (49) 861 | | | 0x0804864c loc.0804864c: 862 | | | 0x0804864c 837df409 cmp dword [ebp-0xc], 0x9 863 | | ,=====< 0x08048650 7f29 jg loc.0804867b 864 | | || 0x08048652 8b45fc mov eax, [ebp-0x4] 865 | | || 0x08048655 83e001 and eax, 0x1 866 | | || 0x08048658 85c0 test eax, eax 867 | | ,======< 0x0804865a 7518 jnz loc.08048674 868 | | ||| 0x0804865c c70424d3870408 mov dword [esp], str.wtf? 869 | | ||| 0x08048663 e850fdffff call dword imp.printf 870 | | ||| ; imp.printf() 871 | | ||| 0x08048668 c7042400000000 mov dword [esp], 0x0 872 | | ||| 0x0804866f e874fdffff call dword imp.exit 873 | | ||| ; imp.exit() 874 | | | ; CODE (JMP) XREF 0x0804865a (fcn.080485b9) 875 | / loc: loc.08048674 (9) 876 | | | 0x08048674 loc.08048674: 877 | | `------> 0x08048674 8d45f4 lea eax, [ebp-0xc] 878 | | || 0x08048677 ff00 inc dword [eax] 879 | | || 0x08048679 ebd1 jmp loc.0804864c 880 | | || ; CODE (JMP) XREF 0x08048643 (fcn.080485b9) 881 | | || ; CODE (JMP) XREF 0x08048650 (fcn.080485b9) 882 | / loc: loc.0804867b (2) 883 | | || 0x0804867b loc.0804867b: 884 | | ``----> 0x0804867b c9 leave 885 | \ 0x0804867c c3 ret 886 | ; ------------ 887 | 888 | This part looks like our previously seen sym.check function. 889 | But bigger. 890 | 891 | Don't be scared. 892 | You can recognize the key verification routine of the previous crackme: 893 | 894 | :::python 895 | s = 0 896 | for i in password: 897 | s += i 898 | if s == 0x10: 899 | sym.parell() 900 | print "BADBOY" 901 | 902 | As you may have guessed, _parell_ is 08048542 903 | 904 | pdf@08048542 905 | ; CODE (CALL) XREF 0x0804861e (fcn.080485b9) 906 | / function: fcn.08048542 (119) 907 | | 0x08048542 fcn.08048542: 908 | | 0x08048542 55 push ebp 909 | | 0x08048543 89e5 mov ebp, esp 910 | | 0x08048545 83ec18 sub esp, 0x18 911 | | 0x08048548 8d45fc lea eax, [ebp-0x4] 912 | | 0x0804854b 89442408 mov [esp+0x8], eax 913 | | 0x0804854f c7442404c2870408 mov dword [esp+0x4], 0x80487c2 914 | | 0x08048557 8b4508 mov eax, [ebp+0x8] 915 | | 0x0804855a 890424 mov [esp], eax 916 | | 0x0804855d e866feffff call dword imp.sscanf 917 | | ; imp.sscanf() 918 | | 0x08048562 8b450c mov eax, [ebp+0xc] 919 | | 0x08048565 89442404 mov [esp+0x4], eax 920 | | 0x08048569 8b45fc mov eax, [ebp-0x4] 921 | | 0x0804856c 890424 mov [esp], eax 922 | | 0x0804856f e840ffffff call dword fcn.080484b4 923 | | ; fcn.080484b4() 924 | | 0x08048574 85c0 test eax, eax 925 | | ,=< 0x08048576 743f jz loc.080485b7 926 | | | 0x08048578 c745f800000000 mov dword [ebp-0x8], 0x0 927 | | | ; CODE (JMP) XREF 0x080485b5 (fcn.08048524) 928 | / loc: loc.0804857f (58) 929 | | | 0x0804857f loc.0804857f: 930 | | | 0x0804857f 837df809 cmp dword [ebp-0x8], 0x9 931 | | ,==< 0x08048583 7f32 jg loc.080485b7 ; If greater than 0x9, jumps over GOODBOY 932 | | || 0x08048585 8b45fc mov eax, [ebp-0x4] 933 | | || 0x08048588 83e001 and eax, 0x1 934 | | || 0x0804858b 85c0 test eax, eax 935 | | ,===< 0x0804858d 7521 jnz loc.080485b0 936 | | ||| 0x0804858f 833d2ca0040801 cmp dword [0x804a02c], 0x1 937 | | ,====< 0x08048596 750c jnz loc.080485a4 938 | | |||| 0x08048598 c70424c5870408 mov dword [esp], str.PasswordOK! 939 | | |||| 0x0804859f e814feffff call dword imp.printf 940 | | |||| ; imp.printf() 941 | | | ; CODE (JMP) XREF 0x08048596 (fcn.08048524) 942 | / loc: loc.080485a4 (21) 943 | | | 0x080485a4 loc.080485a4: 944 | | `----> 0x080485a4 c7042400000000 mov dword [esp], 0x0 945 | | ||| 0x080485ab e838feffff call dword imp.exit 946 | | ||| ; imp.exit() 947 | | | ; CODE (JMP) XREF 0x0804858d (fcn.08048524) 948 | / loc: loc.080485b0 (9) 949 | | | 0x080485b0 loc.080485b0: 950 | | `---> 0x080485b0 8d45f8 lea eax, [ebp-0x8] 951 | | || 0x080485b3 ff00 inc dword [eax] 952 | | || 0x080485b5 ebc8 jmp loc.0804857f 953 | | || ; CODE (JMP) XREF 0x08048576 (fcn.08048524) 954 | | || ; CODE (JMP) XREF 0x08048583 (fcn.08048524) 955 | / loc: loc.080485b7 (2) 956 | | || 0x080485b7 loc.080485b7: 957 | | ``-> 0x080485b7 c9 leave 958 | \ 0x080485b8 c3 ret 959 | ; ------------ 960 | 961 | Looks roughly like the previous parell function. 962 | Did you noticed the _cmp 0x9_ instruction within a loop ? 963 | Which loop ? 964 | There are no upward arrows ! 965 | You should read the code, instead of looking for arrows. 966 | 967 | What about: 968 | 969 | 0x080485b5 ebc8 jmp loc.0804857f 970 | 971 | This is indeed part of a loop. 972 | No other input/ouput than the previous one. 973 | What must be inferior to 0x9 ? 974 | Maybe our password. 975 | 976 | $ LOLO= ./crackme0x07 977 | IOLI Crackme Level 0x07 978 | Password: 111111118 979 | Password OK! 980 | 981 | $ LOLO= ./crackme0x07 982 | IOLI Crackme Level 0x07 983 | Password: 1111111117 984 | Password Incorrect! 985 | 986 | :) 987 | 988 | ## crackme0x08 989 | Let's be lazy clever : our binary rouglhy share the same structure. 990 | It would be nice if we could _diff_ them, and focus on the differences, instead of 991 | having to reverse them from the start, to remember every routine, ... 992 | 993 | You can do that with radare2, using radiff2 (see the manpage). 994 | 995 | radiff2 -C crackme0x07 crackme0x08 996 | main 0x804867d | MATCH (1.000000) | 0x804867d sym.main 997 | fcn.080485b9 0x80485b9 | MATCH (1.000000) | 0x80485b9 sym.check 998 | fcn.08048524 0x8048524 | MATCH (1.000000) | 0x8048524 sym.che 999 | fcn.080484b4 0x80484b4 | MATCH (1.000000) | 0x80484b4 sym.dummy 1000 | fcn.08048542 0x8048542 | MATCH (1.000000) | 0x8048542 sym.parell 1001 | section..text 0x8048400 | MATCH (1.000000) | 0x8048400 section..text 1002 | sym.__do_global_dtors_aux 0x8048450 | NEW (0.000000) 1003 | sym.frame_dummy 0x8048480 | NEW (0.000000) 1004 | fcn.00000000 0x0 | NEW (0.000000) 1005 | sym.__do_global_ctors_aux 0x8048760 | NEW (0.000000) 1006 | sym.__libc_csu_fini 0x8048750 | NEW (0.000000) 1007 | section..fini 0x8048784 | NEW (0.000000) 1008 | fcn.0804878d 0x804878d | NEW (0.000000) 1009 | sym.__libc_csu_init 0x80486e0 | NEW (0.000000) 1010 | sym.__i686.get_pc_thunk.bx 0x8048755 | NEW (0.000000) 1011 | section..init 0x8048360 | NEW (0.000000) 1012 | fcn.08048424 0x8048424 | NEW (0.000000) 1013 | fcn.0804842d 0x804842d | NEW (0.000000) 1014 | 1015 | Surprise ! crackme0x08 is the same than crackme0x07. 1016 | But there are new functions ! 1017 | Indeed, but look where they are located: dtors, ctors, init, fini. 1018 | crackme0x07 seems to be the stripped version of crackme0x08. 1019 | 1020 | ## crackme0x09 1021 | 1022 | The last crackme is left as an exercise to the reader. 1023 | 1024 | ## Conclusion 1025 | 1026 | Now go break some [crackmes](http://crackmes.de) with radare2 ! 1027 | -------------------------------------------------------------------------------- /introduction_to_radare2.md: -------------------------------------------------------------------------------- 1 | 2 | # An Introduction to radare2 3 | 4 | Radare2 (also known as r2) is a complete framework for reverse engineering and analyzing binaries; composed of a set of small utilities that can be used together or independently from the command line. Other reverse engineering tools include [IDA](https://www.hex-rays.com/products/ida/) and [Hopper](http://www.hopperapp.com/). 5 | 6 | Official repository of radare2 is [here](https://github.com/radare/radare2). On Mac OSX, `brew install radare2` will do the job. For other OS, check out the installation page on radare.org. 7 | 8 | A full(?) feature list of r2 and comparison of r2 vs Hopper vs IDA can be found [here](http://rada.re/r/cmp.html) 9 | 10 | If everything goes well, you'll find multiple tools in your path: 11 | 12 | - r2 – the "main" binary 13 | - rabin2 – binary to analyze files (list imports, exports, strings, …) 14 | - rax2 – binary to convert between data formats 15 | - radiff2 – binary to do some diffing 16 | - rahash2 – creates hashes from file blocks (and whole file) 17 | - rasm2 – helps to play with assembler instructions 18 | 19 | ## Introduction 20 | 21 | **Links to other cheatsheets and documentations (which you may like):** 22 | 23 | * [Cheat sheet](https://github.com/radare/radare2/blob/master/doc/intro.md) 24 | * [Official Radare2 Book](http://maijin.gitbooks.io/radare2book/content/) 25 | * [Using radare2 for Pwning](http://radare.today/using-radare2/) 26 | * [radare2 blog](http://radare.today/) has some interesting articles to pwn ctf challenges using r2. 27 | * [radare2 Wiki](https://github.com/radare/radare2/wiki) 28 | * \#radare (official channel) on irc.freenode.net if you need any help from r2 folks anytime. 29 | 30 | r2 has a ton of features which takes a lot of time to explore and understand. Think of r2 like vim/emacs. Unfortunately it lacks a robust GUI. Feel free to try out the web GUI or [Bokken](https://inguma.eu/projects/bokken) 31 | 32 | It has a steep learning curve but we need only a few commands to do basic reversing (and for ctfs) and that is all we'll be seeing for today :) 33 | 34 | 35 | ## A (very) small tutorial for absolute newbies: 36 | 37 | Radare uses a tree structure like name of the commands so all commands which corresponds to analyzing someth 38 | ing start with a. If you want to print something you have to use... p. For example disassemble the current f 39 | unction: pdf [print disassembly function]. 40 | 41 | **Most Important tip for today (and as long as you use r2!):** What most people don't realise is that r2 is self-documenting. Whenever you don't know any command, its semantics, what it does etc. use `?`. A single ? shows you which command categories are available and some general help about specifying addresses and data. 42 | 43 | **Example:** Just running `?` will give you a list of all commands. 44 | Now look at `a`. The help menu says: *"Perform analysis of code"*. 45 | To get more information about commands starting from `a`, run `a?`. 46 | Use this to learn and discover r2. When in doubt feel free to consult wikis, guides and talk to people on \#radare. `q` is usually used to exit menus and eventually radare2 itself. 47 | 48 | A `p?` shows you the help about the different print commands, `pd?` which commands are available for printing disassemblies of different sources, etc. 49 | 50 | Also usually all mnemonics are derived from their longer form. 51 | 52 | Usually this is the workflow you would follow: 53 | 54 | - Start up r2 by using: `$ r2 ./hello_world` 55 | - Run `aa` to "Analyze All", or the newer `aaa`. 56 | - Enter `V` to enter "Visual Mode". (**Hint**: You can use `?` in Visual mode too) 57 | 58 | - To view the graph of a function hit `V`. If you don't see a graph when you enter into graph mode, it usually means that you forgot to run the analysis (rarely it could be a bug in r2, in which case please do report). 59 | 60 | - Hit `p` to show the disassembly at the current location. Hit `p` again to go into debugger mode which shows all register states. 61 | 62 | - `v` to enter code analysis menu. This menu shows all the functions analysed. Selecting one and pressing `g` "seeks" to that function. So the first thing to do is seek to the main function. This will usually be shown as 'main' or 'sym.main'. Normally you'll want to begin analysing the binary from here. 63 | 64 | - In visual mode, if you want to run a r2 command, simple hit `:`. This brings up the same shell that you would have access to outside of the visual mode. All commands that work there work here too. To close the command line, just hit enter with a blank line. 65 | 66 | - use `s ` (**Example:** `s sym.main` will take you to main directly) or `s ` to "seek" to locations. `s-` to undo seek, and `s+` to redo seek. This allows you to traverse the binary efficiently. Tab completion is available to help you out here :) 67 | 68 | - After some analysis, you might want to rename functions, to do so use `afn [offset]`. 69 | 70 | - To rename **local variables**, use `afvn [identifier] [new_name]`. This is the same for **function arguments**, but use `afan` instead. 71 | 72 | - Once you have done some analysis, you will want to save your work so that you can return to it later, use `Ps [name]` to save it as a project. Please check out `P?` for other project related commands. Most of them are self-explanatory. 73 | 74 | - To load a project, use `Po [name]`. Alternatively, you could also do this while starting up r2 buy using the -P option. (**Example:** `$ r2 -P [name]`) 75 | 76 | - If you're a little afraid of this huge amount of command line, you could also try the web interface: `r2 -c=H your_binary`. 77 | 78 | ### Additional: 79 | 80 | - To show all strings in a the data section of a binary, try: `iz`. 81 | - To show all strings in the entire binary try: `izz`. 82 | - Want to search for a string 'Foo' in the binary? Simple, do: `/ Foo`. 83 | - This will return something like: `> hit0_X "Foo"`. To quickly go to this location, `s hit0_X`. Again, tab-completion is available. 84 | - To help further with traversal, r2 offers vim style marks. To place a mark at an offset, use `mK`. Jump to a mark 'K' by using `'K` (exactly how it works in vim!). 85 | - Don't like a theme? Check out default themes using `eco?`. To select a theme run `eco [name] `. 86 | 87 | ### TODO 88 | 89 | - `o` to seek. 90 | - `u/U` to undo/redo seek. 91 | - `dr` and `d` in general in visual mode. 92 | 93 | ## Reference 94 | 95 | - [An Introduction to radare2](http://sushant94.me/2015/05/31/Introduction_to_radare2/) 96 | - [Reverse Engineering With Radare2 – Intro](https://insinuator.net/2016/08/reverse-engineering-with-radare2-intro/) 97 | -------------------------------------------------------------------------------- /mystery-bin-with-radare2.md: -------------------------------------------------------------------------------- 1 | 2 | # Mystery bin with Radare2 3 | 4 | Radare is an open source reversing framework. It comes with a ton of options, functionality, and a somewhat daunting learning curve. It's a powerful tool, and so [superkojiman](https://github.com/superkojiman) have come up with this guide to give people a kick start to the path of reversing with Radare2. 5 | 6 | The best way to learn is to just dive in, so get the `mystery.bin` from the [source code](mystery.c) follow along. I'll be using a lot of commands within Radare2, and if you need more information on a particular command, just add a `?` at the end of it for a description. For example, `p?` will tell you what the p series of commands do. 7 | 8 | ## File information 9 | 10 | Let's run the binary and see what we're up against: 11 | ```sh 12 | $ ./mystery.bin 13 | Enter password: foobar 14 | Got [foobar] 15 | Fail! 16 | ``` 17 | 18 | It prompts us for a password, and if we get it wrong, it print out "Fail!". We'll load it up in Radare2 using the r2 command: 19 | ```sh 20 | $ r2 mystery.bin 21 | [0x100000e50]> 22 | ``` 23 | 24 | The address in the prompt is the current location the cursor is on. We can get some information on the file using the `iI` command: 25 | ```sh 26 | [0x100000e50]> iI 27 | arch x86 28 | binsz 8716 29 | bintype mach0 30 | bits 64 31 | canary true 32 | class MACH064 33 | crypto false 34 | endian little 35 | havecode true 36 | intrp /usr/lib/dyld 37 | lang c 38 | linenum false 39 | lsyms false 40 | machine x86 64 all 41 | maxopsz 16 42 | minopsz 1 43 | nx false 44 | os osx 45 | pcalign 0 46 | pic true 47 | relocs false 48 | static false 49 | stripped false 50 | subsys darwin 51 | va true 52 | ``` 53 | 54 | Lots of information here. `pic true`, `canary true` and `nx false` tell us that it has PIE, stack canary, but has NX disenabled. It also tells us that it's Mach-O 64-bit executable x86_64 binary. 55 | 56 | To find the binary's entry point, as well as main's address, we can use the `ie` and `iM` commands respectively: 57 | ```sh 58 | [0x100000e50]> ie 59 | [Entrypoints] 60 | vaddr=0x100000e50 paddr=0x00000e50 baddr=0x100000000 laddr=0x00000000 haddr=0x00000508 type=program 61 | 62 | 1 entrypoints 63 | 64 | [0x100000e50]> iM 65 | [Main] 66 | vaddr=0x100000e50 paddr=0x100000e50 67 | ``` 68 | 69 | *vaddr* is the address of the entry point and of the main. The next thing we might be interested in are symbols in the binary. This can be examined with the `is` command: 70 | ```sh 71 | [0x100000e50]> is 72 | [Symbols] 73 | vaddr=0x100000000 paddr=0x00000000 ord=000 fwd=NONE sz=0 bind=GLOBAL type=FUNC name=__mh_execute_header 74 | vaddr=0x100000e10 paddr=0x00000e10 ord=001 fwd=NONE sz=0 bind=GLOBAL type=FUNC name=_check_pass_len 75 | vaddr=0x100000ce0 paddr=0x00000ce0 ord=002 fwd=NONE sz=0 bind=GLOBAL type=FUNC name=_check_password 76 | vaddr=0x100000e50 paddr=0x00000e50 ord=003 fwd=NONE sz=0 bind=GLOBAL type=FUNC name=_main 77 | vaddr=0x100000f2a paddr=0x00000f2a ord=004 fwd=NONE sz=0 bind=LOCAL type=FUNC name=imp.__stack_chk_fail 78 | vaddr=0x100000f30 paddr=0x00000f30 ord=005 fwd=NONE sz=0 bind=LOCAL type=FUNC name=imp.printf 79 | vaddr=0x100000f36 paddr=0x00000f36 ord=006 fwd=NONE sz=0 bind=LOCAL type=FUNC name=imp.scanf 80 | vaddr=0x100000ce0 paddr=0x00000ce0 ord=007 fwd=NONE sz=0 bind=LOCAL type=FUNC name=func.100000ce0 81 | vaddr=0x100000e10 paddr=0x00000e10 ord=008 fwd=NONE sz=0 bind=LOCAL type=FUNC name=func.100000e10 82 | vaddr=0x100000e50 paddr=0x00000e50 ord=009 fwd=NONE sz=0 bind=LOCAL type=FUNC name=func.100000e50 83 | 84 | 10 symbols 85 | ``` 86 | 87 | Here we see references to printf and scanf. 88 | 89 | We know the binary prints out "Fails!" when the incorrect password is provided. What other strings could it have? To check, we can use the `iz` command: 90 | ```sh 91 | [0x100000e50]> iz 92 | vaddr=0x100000f6a paddr=0x00000f6a ordinal=000 sz=17 len=16 section=3.__TEXT.__cstring type=ascii string=Enter password: 93 | vaddr=0x100000f7e paddr=0x00000f7e ordinal=001 sz=10 len=9 section=3.__TEXT.__cstring type=ascii string=Got [%s]\n 94 | vaddr=0x100000f88 paddr=0x00000f88 ordinal=002 sz=6 len=5 section=3.__TEXT.__cstring type=ascii string=Win!\n 95 | vaddr=0x100000f8e paddr=0x00000f8e ordinal=003 sz=7 len=6 section=3.__TEXT.__cstring type=ascii string=Fail!\n 96 | ``` 97 | 98 | We can also use the `/` operator to look for specific strings, or bytes: 99 | ```sh 100 | [0x100000e50]> / Win 101 | Searching 3 bytes from 0x100000ce0 to 0x100001030: 57 69 6e 102 | Searching 3 bytes in [0x100000ce0-0x100001030] 103 | hits: 1 104 | 0x100000f88 hit0_0 .: %sGot [%s]Win!Fail!. 105 | ``` 106 | 107 | Obviously we want to see the "%sGot [%s]Win!Fail!." message get printed, and that's the whole point of this reversing exercise. To find out where it's being referenced from, we need to analyze all the functions first using the `aaa` command. Once that's done, we can use the `axt` command: 108 | ``` 109 | [0x100000e50]> aaa 110 | [x] Analyze all flags starting with sym. and entry0 (aa) 111 | [x] Analyze len bytes of instructions for references (aar) 112 | [x] Analyze function calls (aac) 113 | [ ] [*] Use -AA or aaaa to perform additional experimental analysis. 114 | [x] Constructing a function name for fcn.* and sym.func.* functions (aan)) 115 | [0x100000e50]> axt 0x100000f88 116 | data 0x100000ee1 lea rdi, str.Win__n in entry0 117 | ``` 118 | 119 | Here we see that it's being used in main. Aside from variables, `axt` can be also used to find references to a function. For example, to find functions that call printf: 120 | ```sh 121 | [0x100000e50]> axt sym.imp.printf 122 | call 0x100000e87 call sym.imp.printf in entry0 123 | call 0x100000eb1 call sym.imp.printf in entry0 124 | call 0x100000f00 call sym.imp.printf in entry0 125 | call 0x100000eea call sym.imp.printf in entry0 126 | ``` 127 | 128 | So far so good. We've identified a `%sGot [%s]Win!Fail!.` message we want to end up in, and we know which function is referencing it. 129 | 130 | ## Working with functions 131 | 132 | The next step is to have a look at what functions are available in the binary. As before, we need to analyze all the functions first using `aaa`. Then we can use the `afl` command to list all analyzed functions: 133 | ```sh 134 | [0x100000e50]> aaa 135 | [0x100000e50]> afl 136 | 0x100000ce0 24 302 sym._check_password 137 | 0x100000e10 4 60 sym._check_pass_len 138 | 0x100000e50 7 217 entry0 139 | 0x100000f2a 1 6 sym.imp.__stack_chk_fail 140 | 0x100000f30 1 6 sym.imp.printf 141 | 0x100000f36 1 6 sym.imp.scanf 142 | ``` 143 | 144 | Several functions have been analyzed, and we can disassemble them using the `pdf` command. Let's start by analyzing `entry0` since it references the `%sGot [%s]Win!Fail!.` string we're interested in. 145 | 146 | We can seek to entry0's location first, and then run `pdf` without having to provide `@entry0`. Radare2's prompt shows the address of where the cursor is currently located. Seeking allows us to change the current location, so the commands will apply to the current location. Otherwise, we would need to use the `@address` syntax. Seeking is done with the `s` command: 147 | ```sh 148 | [0x00000000]> s entry0 149 | [0x100000e50]> pdf 150 | ;-- _main: 151 | ;-- func.100000e50: 152 | / (fcn) entry0 217 153 | | entry0 (); 154 | | ; var int local_40h @ rbp-0x40 155 | | ; var int local_3ch @ rbp-0x3c 156 | | ; var int local_38h @ rbp-0x38 157 | | ; var int local_34h @ rbp-0x34 158 | | ; var int local_30h @ rbp-0x30 159 | | ; var int local_2ch @ rbp-0x2c 160 | | ; var int local_28h @ rbp-0x28 161 | | ; var int local_1ch @ rbp-0x1c 162 | | ; var int local_18h @ rbp-0x18 163 | | ; var int local_12h @ rbp-0x12 164 | | ; var int local_8h @ rbp-0x8 165 | | 0x100000e50 55 push rbp 166 | | 0x100000e51 4889e5 mov rbp, rsp 167 | | 0x100000e54 4883ec40 sub rsp, 0x40 ; '@' 168 | | 0x100000e58 488d050b0100. lea rax, str.Enter_password: ; section.3.__TEXT.__cstring ; 0x100000f6a ; "Enter password: " 169 | | 0x100000e5f 488b0daa0100. mov rcx, qword [reloc.__stack_chk_guard_16] ; [0x100001010:8]=0 170 | | 0x100000e66 488b09 mov rcx, qword [rcx] 171 | | 0x100000e69 48894df8 mov qword [local_8h], rcx 172 | | 0x100000e6d c745e8000000. mov dword [local_18h], 0 173 | | 0x100000e74 897de4 mov dword [local_1ch], edi 174 | | 0x100000e77 488975d8 mov qword [local_28h], rsi 175 | | 0x100000e7b c745d4000000. mov dword [local_2ch], 0 176 | | 0x100000e82 4889c7 mov rdi, rax 177 | | 0x100000e85 b000 mov al, 0 178 | | 0x100000e87 e8a4000000 call sym.imp.printf ; int printf(const char *format) 179 | | 0x100000e8c 488d3de80000. lea rdi, 0x100000f7b ; "%s" 180 | | 0x100000e93 488d75ee lea rsi, [local_12h] 181 | | 0x100000e97 8945d0 mov dword [local_30h], eax 182 | | 0x100000e9a b000 mov al, 0 183 | | 0x100000e9c e895000000 call sym.imp.scanf ; int scanf(const char *format) 184 | | 0x100000ea1 488d3dd60000. lea rdi, str.Got___s__n ; 0x100000f7e ; "Got [%s]\n" 185 | | 0x100000ea8 488d75ee lea rsi, [local_12h] 186 | | 0x100000eac 8945cc mov dword [local_34h], eax 187 | | 0x100000eaf b000 mov al, 0 188 | | 0x100000eb1 e87a000000 call sym.imp.printf ; int printf(const char *format) 189 | | 0x100000eb6 488d7dee lea rdi, [local_12h] 190 | | 0x100000eba 8945c8 mov dword [local_38h], eax 191 | | 0x100000ebd e84effffff call sym._check_pass_len 192 | | 0x100000ec2 3d0a000000 cmp eax, 0xa 193 | | ,=< 0x100000ec7 0f852a000000 jne 0x100000ef7 194 | | | 0x100000ecd 488d7dee lea rdi, [local_12h] 195 | | | 0x100000ed1 e80afeffff call sym._check_password 196 | | | 0x100000ed6 3d00000000 cmp eax, 0 197 | | ,==< 0x100000edb 0f8516000000 jne 0x100000ef7 198 | | || 0x100000ee1 488d3da00000. lea rdi, str.Win__n ; hit0_0 ; 0x100000f88 ; "Win!\n" 199 | | || 0x100000ee8 b000 mov al, 0 200 | | || 0x100000eea e841000000 call sym.imp.printf ; int printf(const char *format) 201 | | || 0x100000eef 8945c4 mov dword [local_3ch], eax 202 | | ,===< 0x100000ef2 e911000000 jmp 0x100000f08 203 | | ||| ; JMP XREF from 0x100000ec7 (entry0) 204 | | ||| ; JMP XREF from 0x100000edb (entry0) 205 | | |``-> 0x100000ef7 488d3d900000. lea rdi, str.Fail__n ; 0x100000f8e ; "Fail!\n" 206 | | | 0x100000efe b000 mov al, 0 207 | | | 0x100000f00 e82b000000 call sym.imp.printf ; int printf(const char *format) 208 | | | 0x100000f05 8945c0 mov dword [local_40h], eax 209 | | | ; JMP XREF from 0x100000ef2 (entry0) 210 | | `---> 0x100000f08 488b05010100. mov rax, qword [reloc.__stack_chk_guard_16] ; [0x100001010:8]=0 211 | | 0x100000f0f 488b00 mov rax, qword [rax] 212 | | 0x100000f12 483b45f8 cmp rax, qword [local_8h] 213 | | ,=< 0x100000f16 0f8508000000 jne 0x100000f24 214 | | | 0x100000f1c 31c0 xor eax, eax 215 | | | 0x100000f1e 4883c440 add rsp, 0x40 ; '@' 216 | | | 0x100000f22 5d pop rbp 217 | | | 0x100000f23 c3 ret 218 | | | ; JMP XREF from 0x100000f16 (entry0) 219 | \ `-> 0x100000f24 e801000000 call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void) 220 | ``` 221 | 222 | The first column shows the address of each instruction, the second column shows the opcodes of the instruction, and the third column shows the instruction itself. A fourth column exists to display any available comments. It's also possible to display the first 10 lines of `entry0`, you could do `pd 10`. The arrows on the left of the addresses depict where execution branches off to when a jump instruction is encountered. 223 | 224 | At `0x100000ed1`, entry0 calls a function sym._check_password. We can see that a value is moved to `rdi` before the function is called. This implies that it takes an argument; and in this case, it's the password that we enter. Let's examine this function. 225 | ```sh 226 | [0x100000ce0]> s sym._check_pass_len 227 | [0x100000e10]> pdf 228 | ;-- func.100000e10: 229 | / (fcn) sym._check_pass_len 60 230 | | sym._check_pass_len (); 231 | | ; var int local_ch @ rbp-0xc 232 | | ; var int local_8h @ rbp-0x8 233 | | ; CALL XREF from 0x100000ebd (entry0) 234 | | 0x100000e10 55 push rbp 235 | | 0x100000e11 4889e5 mov rbp, rsp 236 | | 0x100000e14 48897df8 mov qword [local_8h], rdi 237 | | 0x100000e18 c745f4000000. mov dword [local_ch], 0 238 | | ; JMP XREF from 0x100000e42 (sym._check_pass_len) 239 | | .-> 0x100000e1f 486345f4 movsxd rax, dword [local_ch] 240 | | | 0x100000e23 488b4df8 mov rcx, qword [local_8h] 241 | | | 0x100000e27 0fbe1401 movsx edx, byte [rcx + rax] 242 | | | 0x100000e2b 81fa00000000 cmp edx, 0 243 | | ,==< 0x100000e31 0f8410000000 je 0x100000e47 244 | | || 0x100000e37 8b45f4 mov eax, dword [local_ch] 245 | | || 0x100000e3a 0501000000 add eax, 1 246 | | || 0x100000e3f 8945f4 mov dword [local_ch], eax 247 | | |`=< 0x100000e42 e9d8ffffff jmp 0x100000e1f 248 | | | ; JMP XREF from 0x100000e31 (sym._check_pass_len) 249 | | `--> 0x100000e47 8b45f4 mov eax, dword [local_ch] 250 | | 0x100000e4a 5d pop rbp 251 | \ 0x100000e4b c3 ret 252 | ``` 253 | 254 | So at first glance, we can see that 255 | - Radare2 identified two local variables, local_ch, and local_8h. 256 | - There are two branching conditions in the function. 257 | 258 | Let's see if we can figure out what these local variables are. The first reference to local_8h occurs at `0x100000e14`: 259 | ```nasm 260 | mov qword [local_8h], rdi 261 | ``` 262 | 263 | We know rdi contains the password we input, so it would seem that local_8h is a copy of that password. Right after that, we see that local_ch is set to 0: 264 | ```nasm 265 | mov dword [local_ch], 0 266 | ``` 267 | 268 | After initializing it to 0, it performs several instructions which basically checks to see it the first character in the password is a null byte. If it isn't, local_ch is then incremented by 1: 269 | ```nasm 270 | mov eax, dword [local_ch] 271 | add eax, 1 272 | mov dword [local_ch], eax 273 | ``` 274 | 275 | At this point, execution jumps to `0x100000e1f`. It then takes the second character in the password and repeats over again until it eventually finds a null byte, at which point it returns the value in local_ch to the calling function. So it'safe to assume that local_ch must be a loop counter, and this function's purpose is simply check the length of the entered password. 276 | 277 | Radare2 allows us to rename functions and variables to things that make sense to us. 278 | ```sh 279 | [0x100000ce0]> afn check_pass_len 280 | [0x100000ce0]> afvn local_ch counter 281 | [0x100000ce0]> afvn local_8h password 282 | ``` 283 | 284 | ## Graphs 285 | 286 | Now that we have a better understanding of what check_password_len does, let's move on to the next function: sym._check_password. 287 | ```sh 288 | [0x100000e50]> s sym._check_password 289 | [0x100000ce0]> pdf 290 | ;-- section.0.__TEXT.__text: 291 | ;-- func.100000ce0: 292 | / (fcn) sym._check_password 302 293 | | sym._check_password (); 294 | | ; var int local_14h @ rbp-0x14 295 | | ; var int local_10h @ rbp-0x10 296 | | ; var int local_4h @ rbp-0x4 297 | | ; CALL XREF from 0x100000ed1 (entry0) 298 | | 0x100000ce0 55 push rbp ; section 0 va=0x100000ce0 pa=0x00000ce0 sz=585 vsz=585 rwx=m-r-x 0.__TEXT.__text 299 | | 0x100000ce1 4889e5 mov rbp, rsp 300 | | 0x100000ce4 48897df0 mov qword [local_10h], rdi 301 | | 0x100000ce8 c745ec000000. mov dword [local_14h], 0 302 | | 0x100000cef 488b7df0 mov rdi, qword [local_10h] 303 | | 0x100000cf3 0fbe07 movsx eax, byte [rdi] 304 | | 0x100000cf6 3d68000000 cmp eax, 0x68 ; 'h' ; 'h' 305 | | ,=< 0x100000cfb 0f856b000000 jne 0x100000d6c 306 | | | 0x100000d01 488b45f0 mov rax, qword [local_10h] 307 | | | 0x100000d05 0fbe4801 movsx ecx, byte [rax + 1] ; [0x1:1]=250 308 | | | 0x100000d09 81f965000000 cmp ecx, 0x65 ; 'e' ; 'e' 309 | | ,==< 0x100000d0f 0f8552000000 jne 0x100000d67 310 | | || 0x100000d15 488b45f0 mov rax, qword [local_10h] 311 | | || 0x100000d19 0fbe4802 movsx ecx, byte [rax + 2] ; [0x2:1]=237 312 | | || 0x100000d1d 81f96c000000 cmp ecx, 0x6c ; 'l' ; 'l' ; "(." 313 | | ,===< 0x100000d23 0f8539000000 jne 0x100000d62 314 | | ||| 0x100000d29 488b45f0 mov rax, qword [local_10h] 315 | | ||| 0x100000d2d 0fbe4803 movsx ecx, byte [rax + 3] ; [0x3:1]=254 316 | | ||| 0x100000d31 81f96c000000 cmp ecx, 0x6c ; 'l' ; 'l' ; "(." 317 | | ,====< 0x100000d37 0f8520000000 jne 0x100000d5d 318 | | |||| 0x100000d3d 488b45f0 mov rax, qword [local_10h] 319 | | |||| 0x100000d41 0fbe4804 movsx ecx, byte [rax + 4] ; [0x4:1]=7 320 | | |||| 0x100000d45 81f96f000000 cmp ecx, 0x6f ; 'o' ; 'o' 321 | | ,=====< 0x100000d4b 0f8507000000 jne 0x100000d58 322 | | ||||| 0x100000d51 c745ec010000. mov dword [local_14h], 1 323 | | ||||| ; JMP XREF from 0x100000d4b (sym._check_password) 324 | | ,`-----> 0x100000d58 e900000000 jmp 0x100000d5d 325 | | | |||| ; JMP XREF from 0x100000d58 (sym._check_password) 326 | | | |||| ; JMP XREF from 0x100000d37 (sym._check_password) 327 | | `,`----> 0x100000d5d e900000000 jmp 0x100000d62 328 | | | ||| ; JMP XREF from 0x100000d5d (sym._check_password) 329 | | | ||| ; JMP XREF from 0x100000d23 (sym._check_password) 330 | | `,`---> 0x100000d62 e900000000 jmp 0x100000d67 331 | | | || ; JMP XREF from 0x100000d62 (sym._check_password) 332 | | | || ; JMP XREF from 0x100000d0f (sym._check_password) 333 | | `,`--> 0x100000d67 e900000000 jmp 0x100000d6c 334 | | | | ; JMP XREF from 0x100000d67 (sym._check_password) 335 | | | | ; JMP XREF from 0x100000cfb (sym._check_password) 336 | | `-`-> 0x100000d6c 817dec000000. cmp dword [local_14h], 0 337 | | ,=< 0x100000d73 0f8489000000 je 0x100000e02 338 | | | 0x100000d79 488b45f0 mov rax, qword [local_10h] 339 | | | 0x100000d7d 0fbe4805 movsx ecx, byte [rax + 5] ; [0x5:1]=0 340 | | | 0x100000d81 81f977000000 cmp ecx, 0x77 ; 'w' ; 'w' 341 | | ,==< 0x100000d87 0f8570000000 jne 0x100000dfd 342 | | || 0x100000d8d 488b45f0 mov rax, qword [local_10h] 343 | | || 0x100000d91 0fbe4806 movsx ecx, byte [rax + 6] ; [0x6:1]=0 344 | | || 0x100000d95 81f96f000000 cmp ecx, 0x6f ; 'o' ; 'o' 345 | | ,===< 0x100000d9b 0f8557000000 jne 0x100000df8 346 | | ||| 0x100000da1 488b45f0 mov rax, qword [local_10h] 347 | | ||| 0x100000da5 0fbe4807 movsx ecx, byte [rax + 7] ; [0x7:1]=1 348 | | ||| 0x100000da9 81f972000000 cmp ecx, 0x72 ; 'r' ; 'r' ; "TEXT" 349 | | ,====< 0x100000daf 0f853e000000 jne 0x100000df3 350 | | |||| 0x100000db5 488b45f0 mov rax, qword [local_10h] 351 | | |||| 0x100000db9 0fbe4808 movsx ecx, byte [rax + 8] ; [0x8:1]=3 352 | | |||| 0x100000dbd 81f96c000000 cmp ecx, 0x6c ; 'l' ; 'l' ; "(." 353 | | ,=====< 0x100000dc3 0f8525000000 jne 0x100000dee 354 | | ||||| 0x100000dc9 488b45f0 mov rax, qword [local_10h] 355 | | ||||| 0x100000dcd 0fbe4809 movsx ecx, byte [rax + 9] ; [0x9:1]=0 356 | | ||||| 0x100000dd1 81f964000000 cmp ecx, 0x64 ; 'd' ; 'd' 357 | | ,======< 0x100000dd7 0f850c000000 jne 0x100000de9 358 | | |||||| 0x100000ddd c745fc000000. mov dword [local_4h], 0 359 | | ,=======< 0x100000de4 e920000000 jmp 0x100000e09 360 | | ||||||| ; JMP XREF from 0x100000dd7 (sym._check_password) 361 | | =`------> 0x100000de9 e900000000 jmp 0x100000dee 362 | | | ||||| ; JMP XREF from 0x100000de9 (sym._check_password) 363 | | | ||||| ; JMP XREF from 0x100000dc3 (sym._check_password) 364 | | -,`-----> 0x100000dee e900000000 jmp 0x100000df3 365 | | || |||| ; JMP XREF from 0x100000dee (sym._check_password) 366 | | || |||| ; JMP XREF from 0x100000daf (sym._check_password) 367 | | |`,`----> 0x100000df3 e900000000 jmp 0x100000df8 368 | | | | ||| ; JMP XREF from 0x100000df3 (sym._check_password) 369 | | | | ||| ; JMP XREF from 0x100000d9b (sym._check_password) 370 | | | `,`---> 0x100000df8 e900000000 jmp 0x100000dfd 371 | | | | || ; JMP XREF from 0x100000df8 (sym._check_password) 372 | | | | || ; JMP XREF from 0x100000d87 (sym._check_password) 373 | | | `,`--> 0x100000dfd e907000000 jmp 0x100000e09 374 | | | | | ; JMP XREF from 0x100000d73 (sym._check_password) 375 | | | | `-> 0x100000e02 c745fcffffff. mov dword [local_4h], 0xffffffff 376 | | | | ; JMP XREF from 0x100000dfd (sym._check_password) 377 | | | | ; JMP XREF from 0x100000de4 (sym._check_password) 378 | | `---`---> 0x100000e09 8b45fc mov eax, dword [local_4h] 379 | | 0x100000e0c 5d pop rbp 380 | \ 0x100000e0d c3 ret 381 | ``` 382 | 383 | Look at all those arrows! There's a lot of branching goin on, so let's switch to Radare's visual mode to see a graph of what's happening. Use the `VV` command to enter visual mode. 384 | ```sh 385 | [0x100000ce0]> VV @ sym._check_password (nodes 24 edges 34 zoom 100%) BB-NORM mouse:canvas-y movements-speed:5 386 | 387 | 388 | .----------------------------------------------------------------------------------------. 389 | | 0x100000ce0 ;[gb] | 390 | | ; section 0 va=0x100000ce0 pa=0x00000ce0 sz=585 vsz=585 rwx=m-r-x 0.__TEXT.__text | 391 | | ;-- section.0.__TEXT.__text: | 392 | | ;-- func.100000ce0: | 393 | | (fcn) sym._check_password 302 | 394 | | sym._check_password (); | 395 | | ; var int local_14h @ rbp-0x14 | 396 | | ; var int local_10h @ rbp-0x10 | 397 | | ; var int local_4h @ rbp-0x4 | 398 | | ; CALL XREF from 0x100000ed1 (entry0) | 399 | | push rbp | 400 | | mov rbp, rsp | 401 | | mov qword [local_10h], rdi | 402 | | mov dword [local_14h], 0 | 403 | | mov rdi, qword [local_10h] | 404 | | movsx eax, byte [rdi] | 405 | | ; 'h' | 406 | | ; 'h' | 407 | | cmp eax, 0x68 | 408 | | jne 0x100000d6c;[ga] | 409 | `----------------------------------------------------------------------------------------' 410 | f t 411 | '-------------------.---------------------------. 412 | | | 413 | | | 414 | .----------------------------. | 415 | | [0x100000d01] ;[gd] | | 416 | | mov rax, qword [local_10h] | | 417 | | ; [0x1:1]=250 | | 418 | | movsx ecx, byte [rax + 1] | | 419 | | ; 'e' | | 420 | | ; 'e' | | 421 | | cmp ecx, 0x65 | | 422 | 423 | ``` 424 | 425 | Radare2 displays an ASCII graph of the function being analyzed. Now we can clearly see where the branching is taking place. Notice that Radare2 also puts a "t" and "f" under each condition to signify "true" and "false" respectively. Visual mode has its own set of commands, such as: 426 | - hjkl scroll canvas 427 | - HJKL move node 428 | - tab/TAB select next/previous node 429 | - t/f follow true/false edges 430 | - . center the graph 431 | - p press repeatedly to change graph view 432 | 433 | If we press p several times, we eventually get a mini-graph view. Using tab, we can move to the next node and Radare2 will display the instructions in that node on the top left corner. The graph depicts a series of nested if conditions where it checks to see it each character in the password we provided, matches a certain character. So basically, this function returns 0 if our input matches the expected password. 434 | 435 | ## Adding comments 436 | 437 | We can add a comment, which can make it easier to understand. For example, we add a comment for sym._check_password: 438 | ```sh 439 | [0x100000ce0]> CC Returns 0 when password is 'helloworld' 440 | [0x100000ce0]> pd 10 441 | ;-- section.0.__TEXT.__text: 442 | ;-- func.100000ce0: 443 | / (fcn) sym._check_password 302 444 | | sym._check_password (); 445 | | ; var int local_14h @ rbp-0x14 446 | | ; var int local_10h @ rbp-0x10 447 | | ; var int local_4h @ rbp-0x4 448 | | ; CALL XREF from 0x100000ed1 (entry0) 449 | | 0x100000ce0 55 push rbp ; section 0 va=0x100000ce0 pa=0x00000ce0 sz=585 vsz=585 rwx=m-r-x 0.__TEXT.__text Returns 0 when password is 'helloworld' 450 | | 0x100000ce1 4889e5 mov rbp, rsp 451 | | 0x100000ce4 48897df0 mov qword [local_10h], rdi 452 | | 0x100000ce8 c745ec000000. mov dword [local_14h], 0 453 | | 0x100000cef 488b7df0 mov rdi, qword [local_10h] 454 | | 0x100000cf3 0fbe07 movsx eax, byte [rdi] 455 | | 0x100000cf6 3d68000000 cmp eax, 0x68 ; 'h' ; 'h' 456 | | ,=< 0x100000cfb 0f856b000000 jne 0x100000d6c 457 | | | 0x100000d01 488b45f0 mov rax, qword [local_10h] 458 | | | 0x100000d05 0fbe4801 movsx ecx, byte [rax + 1] ; [0x1:1]=250 459 | ``` 460 | 461 | We've solved this easy binary challenge. Let's see it in action: 462 | ```sh 463 | $ ./mystery.bin 464 | Enter password: helloworld 465 | Got [helloworld] 466 | Win! 467 | ``` 468 | 469 | ## Conclusion 470 | 471 | Hopefully this this guide has given you a taste of Radare’s potential. Other things it can do include debugging the binary, looking for ROP gadgets, importing signatures, and so on. Radare also offers a help system, just type ? to get a list of commands. If you’ve found Radare interesting so far, I encourage you to play around with it some more. [Download](https://github.com/ctfs) some binary challenges and take Radare to town. 472 | 473 | ## Reference 474 | 475 | - [Radare 2 in 0x1E minutes](https://blog.techorganic.com/2016/03/08/radare-2-in-0x1e-minutes/) 476 | 477 | -------------------------------------------------------------------------------- /mystery.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Binary challenge used for the Radare 2 Primer. 3 | * By superkojiman - http://blog.techorganic.com 4 | * 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | int check_password(char *pass) { 11 | int stage2 = 0; 12 | 13 | /* stage 1, check the first 5 letters */ 14 | if (pass[0] == 'h') { 15 | if (pass[1] == 'e') { 16 | if (pass[2] == 'l') { 17 | if (pass[3] == 'l') { 18 | if (pass[4] == 'o') { 19 | stage2 = 1; 20 | } 21 | } 22 | } 23 | } 24 | } 25 | 26 | /* stage 2, check the next 5 letters */ 27 | if (stage2) { 28 | if (pass[5] == 'w') { 29 | if (pass[6] == 'o') { 30 | if (pass[7] == 'r') { 31 | if (pass[8] == 'l') { 32 | if (pass[9] == 'd') { 33 | return 0; 34 | } 35 | } 36 | } 37 | } 38 | } 39 | } else { 40 | return -1; 41 | } 42 | } 43 | 44 | int check_pass_len(char *pass) { 45 | int i = 0; 46 | while(pass[i] != '\0') { 47 | i++; 48 | } 49 | return i; 50 | } 51 | 52 | int main(int argc, char *argv[]) { 53 | char pass[10]; 54 | int stage2 = 0; 55 | 56 | printf("Enter password: "); 57 | scanf("%s", pass); 58 | printf("Got [%s]\n", pass); 59 | 60 | 61 | if ((check_pass_len(pass) == 10) && 62 | (check_password(pass) == 0)) { 63 | printf("Win!\n"); 64 | } else { 65 | printf("Fail!\n"); 66 | } 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /radare2_tutorial.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifding/radare2-tutorial/31d453715b0c8f2625ae5e7fc85822244b69ff25/radare2_tutorial.pdf -------------------------------------------------------------------------------- /simple-example-with-radare2.md: -------------------------------------------------------------------------------- 1 | 2 | # Reverse Engineering With Radare2 3 | 4 | In [the last article](introduction_to_radare2.md), we explore the basics of radare2. We are going to write a simple program, and then disassemble it, to see what is really doing in the processor. 5 | 6 | Not all architectures have the same set of instructions, the most important difference is between Reduced Instruction Set Computing (embedded systems, PMDs ...) and Complex Instruction Set Computing (clusters, desktop computing ...). An example of RISC could be ARM and CISC could be x86. 7 | 8 | Radare2 is an open source set of tools for reverse engineering and analysis of binary files (among other things, for example debugging). In this article we will cover two: rasm2 and r2. 9 | 10 | ## rasm2 11 | 12 | It is used to assemble or disassemble files or hexpair strings. You can see the help screen: 13 | ```sh 14 | $ rasm2 -h 15 | Usage: rasm2 [-ACdDehLBvw] [-a arch] [-b bits] [-o addr] [-s syntax] 16 | [-f file] [-F fil:ter] [-i skip] [-l len] 'code'|hex|- 17 | -a [arch] Set architecture to assemble/disassemble (see -L) 18 | -A Show Analysis information from given hexpairs 19 | -b [bits] Set cpu register size (8, 16, 32, 64) (RASM2_BITS) 20 | -c [cpu] Select specific CPU (depends on arch) 21 | -C Output in C format 22 | -d, -D Disassemble from hexpair bytes (-D show hexpairs) 23 | -e Use big endian instead of little endian 24 | -E Display ESIL expression (same input as in -d) 25 | -f [file] Read data from file 26 | -F [in:out] Specify input and/or output filters (att2intel, x86.pseudo, ...) 27 | -h, -hh Show this help, -hh for long 28 | -i [len] ignore/skip N bytes of the input buffer 29 | -k [kernel] Select operating system (linux, windows, darwin, ..) 30 | -l [len] Input/Output length 31 | -L List Asm plugins: (a=asm, d=disasm, A=analyze, e=ESIL) 32 | -o [offset] Set start address for code (default 0) 33 | -O [file] Output file name (rasm2 -Bf a.asm -O a) 34 | -p Run SPP over input for assembly 35 | -s [syntax] Select syntax (intel, att) 36 | -B Binary input/output (-l is mandatory for binary input) 37 | -v Show version information 38 | -w What's this instruction for? describe opcode 39 | -q quiet mode 40 | If '-l' value is greater than output length, output is padded with nops 41 | If the last argument is '-' reads from stdin 42 | Environment: 43 | RASM2_NOPLUGINS do not load shared plugins (speedup loading) 44 | R_DEBUG if defined, show error messages and crash signal 45 | ``` 46 | 47 | The option `-d`, disassemble from hexpair bytes. For example, 90 corresponds to a nop operation. To disassemble hexadecimal code, type: 48 | ```sh 49 | $ rasm2 -d 50 | $ rasm2 -d 90 51 | ``` 52 | 53 | If you want to get the hexadecimal code of an instruction: 54 | ```sh 55 | $ rasm2 "" 56 | $ rasm2 "nop" 57 | ``` 58 | 59 | ## r2 60 | 61 | let's write a simple code that adds two variables. 62 | ```sh 63 | $ cat test.c 64 | #include 65 | 66 | int main() 67 | { 68 | int a = 10; 69 | int b = 20; 70 | int c = a+b; 71 | return 0; 72 | } 73 | 74 | $ gcc -o test test.c 75 | ``` 76 | 77 | Once we have the binary file, let's disassemble it. 78 | ```sh 79 | $ r2 -vv 80 | radare2 1.5.0 0 @ darwin-x86-64 git.1.5.0 81 | $ r2 test 82 | ``` 83 | 84 | At this point, analyze the whole code: `aa` (analyze all). 85 | 86 | Let's see the main function: `pdf @ main` (print disassemble function) 87 | ```nasm 88 | ;-- main: 89 | ;-- section.0.__TEXT.__text: 90 | ;-- func.100000f70: 91 | / (fcn) entry0 38 92 | | entry0 (); 93 | | ; var int local_10h @ rbp-0x10 94 | | ; var int local_ch @ rbp-0xc 95 | | ; var int local_8h @ rbp-0x8 96 | | ; var int local_4h @ rbp-0x4 97 | | 0x100000f70 55 push rbp ; section 0 va=0x100000f70 pa=0x00000f70 sz=38 vsz=38 rwx=m-r-x 0.__TEXT.__text 98 | | 0x100000f71 4889e5 mov rbp, rsp 99 | | 0x100000f74 31c0 xor eax, eax 100 | | 0x100000f76 c745fc000000. mov dword [local_4h], 0 101 | | 0x100000f7d c745f80a0000. mov dword [local_8h], 0xa 102 | | 0x100000f84 c745f4140000. mov dword [local_ch], 0x14 103 | | 0x100000f8b 8b4df8 mov ecx, dword [local_8h] 104 | | 0x100000f8e 034df4 add ecx, dword [local_ch] 105 | | 0x100000f91 894df0 mov dword [local_10h], ecx 106 | | 0x100000f94 5d pop rbp 107 | \ 0x100000f95 c3 ret 108 | ``` 109 | 110 | ### Exampling assembly instructions 111 | 112 | The first two instructions are called prologue: 113 | 114 | - `push rbp` save the old base pointer in the stack to restore it later 115 | - `mov rbp, rsp` copy the stack pointer to the base pointer 116 | 117 | Now the base pointer points to the main frame. 118 | 119 | - `mov dword [local_4h], 0` load 0 into rbp-4 120 | - `mov dword [local_8h], 0xa` load 10 into rbp-8 121 | - `mov dword [local_ch], 0x14` load 20 into rpb-12 122 | 123 | The size of an integer in C is 4 bytes (32 bits), that's the reason why the pointer decrements in 4 (the stack grows downward). The first instruction simply say: laod value 0 below the base pointer, the second instruction says: load value 10 (0xa) below the previous value, the third instruction says: laod value 0x14(20) below the previous value. We have pushed the variable values into the stack. 124 | 125 | - `mov ecx, dword [local_8h]` load value 10 into ecx 126 | - `add ecx, dword [local_ch]` add ecx, rbp-12 and store result in ecx 127 | - `mov dword [local_10h], ecx` load the result into rbp-16. 128 | 129 | We load the values into general purpose registers, to perform the ALU operation (add). Finally we store the sum result below rbp-12. 130 | 131 | The last two instructions are called epilogue. We pop the old base pointer off the stack and store it in rbp, then we jump to the return address (which is also in the stack). 132 | ```nasm 133 | pop rbp 134 | ret 135 | ``` 136 | 137 | A final note: The assembly code generated is different depending on the compiler and system. 138 | 139 | ## Reference 140 | 141 | - [Reverse Engineering with Radare2 (A Quick Introduction)](https://null-byte.wonderhowto.com/how-to/reverse-engineering-with-radare2-a-quick-introduction-0165996/) 142 | --------------------------------------------------------------------------------