├── 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 |
--------------------------------------------------------------------------------