It has been a long time since I didn't write something about re. With the GSoC I mostly wrote python code over the past couple of months.
I want something more low-level now and what is better than some re? Plus, it is a good opportunity to use for the first time this awesome tool jvoisin is always bragging about: radare2!
The binary comes from an old CTF but I feel like I should not name it nor give the actual flag. I will use radare2 to analyze and reverse it so let's begin!
Information gathering
Since I am not familiar at all with radare2, let see the help:
-- I did it for the pwnz. [0x08048450]> ? Usage: [.][times][cmd][~grep][@[@iter]addr!size][|>pipe] ; ... Append '?' to any char command to get detailed help Prefix with number to repeat command N times (f.ex: 3x) |%var =valueAlias for 'env' command | *off[=[0x]value] Pointer read/write data/values (see ?v, wx, wv) | (macro arg0 arg1) Manage scripting macros | .[-|(m)|f|!sh|cmd] Define macro or load r2, cparse or rlang file | = [cmd] Run this command via rap:// | / Search for bytes, regexps, patterns, .. | ! [cmd] Run given command as in system(3) | # [algo] [len] Calculate hash checksum of current block | a Perform analysis of code | b Get or change block size | c [arg] Compare block with given data | C Code metadata management | d Debugger commands | e [a[=b]] List/get/set config evaluable vars | f [name][sz][at] Set flag at current address | g [arg] Go compile shellcodes with r_egg | i [file] Get info about opened file | k [sdb-query] Run sdb-query. see k? for help, 'k *', 'k **' ... | l [-] [num|msg] Log utility | m Mountpoints commands | o [file] ([offset]) Open file at optional address | p [len] Print current block with format and length | P Project management utilities | q [ret] Quit program with a return value | r [len] Resize file | s [addr] Seek to address (also for '0x', '0x1' == 's 0x1') | S Io section manipulation information | t Cparse types management | V Enter visual mode (vcmds=visualvisual keystrokes) | w [str] Multiple write operations | x [len] Alias for 'px' (print hexadecimal) | y [len] [[@]addr Yank/paste bytes from/to memory | z Zignatures management | ?[??][expr] Help or evaluate math expression | ?$? Show available '$' variables and aliases | ?@? Misc help for '@' (seek), '~' (grep) (see ~??)]
First thing first, I want to check the binary information and from the previous
output I can see that I should use the i [file]
command.
[0x08048450]> i? |Usage: i Get info from opened file | Output mode: | '*' Output in radare commands | 'j' Output in json | 'q' Simple quiet output | Actions: ... | iI Binary info ... | iz Strings
I removed the commands that don't interest me.
[0x08048450]> iI file [redacted] type EXEC (Executable file) pic false canary false nx true crypto false has_va true root elf class ELF32 lang c arch x86 bits 32 machine Intel 80386 os linux subsys linux endian little strip true static false linenum false lsyms false relocs false rpath NONE
From lines 10, 12, 13 and 18 I understand that I am dealing with an ELF32 bit binary that has been stripped. No surprise here.
[0x08048450]> iz vaddr=0x080488f0 paddr=0x000008f0 ordinal=000 sz=12 len=11 section=.rodata type=a string=Next time!\n vaddr=0x080488fc paddr=0x000008fc ordinal=001 sz=11 len=10 section=.rodata type=a string=Congrats!\n vaddr=0x08049b9f paddr=0x00000b9f ordinal=000 sz=5 len=4 section=.data type=a string=\eE[7
The two first strings that appear are likely the Good boy and the Bad boy. The last could be the encrypted flag or a key. Time to analyse the code now.
Code analysis
From ?
I saw some interesting commands.
[0x08048450]> a? |Usage: a ... | aa analyze all (fcns + bbs) ... Examples: f ts @ `S*~text:0[3]`; f t @ section..text f ds @ `S*~data:0[3]`; f d @ section..data .ad t t+ts @ d:ds [0x08048450]> p? |Usage: p[=68abcdDfiImrstuxz] [arg|len] ... | p[dD][lf] [l] disassemble N opcodes/bytes (see pd?) ... [0x08048450]> pd? |Usage: pd[f|i|l] [len] [arch] [bits] @ [addr] # Print Disassembly | NOTE:len parameter can be negative ... | pdf disassemble function ...
So let's use aa
and pdf
.
[0x08048450]> aa [0x08048450]> pdf ; [12] va=0x08048450 pa=0x00000450 sz=1156 vsz=1156 rwx=-r-x .text / (fcn) entry0 34 | ;-- section..text: | 0x08048450 31ed xor ebp, ebp | 0x08048452 5e pop esi | 0x08048453 89e1 mov ecx, esp | 0x08048455 83e4f0 and esp, 0xfffffff0 | 0x08048458 50 push eax | 0x08048459 54 push esp | 0x0804845a 52 push edx | 0x0804845b 68d0880408 push loc.080488d0 ; loc.080488d0 | 0x08048460 6860880408 push fcn.08048860 ; 0x08048860 | 0x08048465 51 push ecx | 0x08048466 56 push esi | 0x08048467 68c1860408 push main ; main | 0x0804846c e88fffffff call sym.imp.__libc_start_main | sym.imp.__libc_start_main(unk, unk, unk, unk, unk, unk, unk, unk) \ 0x08048471 f4 hlt
Though the binary has been stripped, radare2 recognizes the GCC convention
(lines 17 and 18) and named the main
function (line 17).
[0x08048450]> pdf@main | ; UNKNOWN XREF from 0x080486c1 (unk) | ; DATA XREF from 0x08048467 (entry0) / (fcn) main 412 | 0x080486c1 55 push ebp | 0x080486c2 89e5 mov ebp, esp | 0x080486c4 83e4f0 and esp, 0xfffffff0 | 0x080486c7 83ec40 sub esp, 0x40 | 0x080486ca c744242c508. mov dword [esp+0x2c], 0x8048550 ; 0x08048550 | 0x080486d2 c7442430000. mov dword [esp+0x30], 0x0 | 0x080486da c7442410557. mov dword [esp+0x10], 0x67617355 ; 0x67617355 | 0x080486e2 c7442414652. mov dword [esp+0x14], 0x203a2065 ; 0x203a2065 | 0x080486ea c74424182e2. mov dword [esp+0x18], 0x72632f2e ; 0x72632f2e | 0x080486f2 c744241c616. mov dword [esp+0x1c], 0x6d6b6361 ; 0x6d6b6361 | 0x080486fa c7442420652. mov dword [esp+0x20], 0x61702065 ; 0x61702065 | 0x08048702 c7442424737. mov dword [esp+0x24], 0x6f777373 ; 0x6f777373 | 0x0804870a c7442428726. mov dword [esp+0x28], 0xa6472 ; 0x000a6472 | 0x08048712 837d0802 cmp dword [ebp+0x8], 0x2 | ,=< 0x08048716 742e je loc.08048746 | | 0x08048718 8d442410 lea eax, [esp+0x10] | | 0x0804871c 890424 mov [esp], eax | | 0x0804871f e8ccfcffff call fcn.080483f0 | | fcn.080483f0(unk) | | 0x08048724 89442408 mov [esp+0x8], eax | | 0x08048728 8d442410 lea eax, [esp+0x10] | | 0x0804872c 89442404 mov [esp+0x4], eax | | 0x08048730 c7042401000. mov dword [esp], 0x1 ; 0x00000001 | | 0x08048737 e8d4fcffff call sym.imp.write | | sym.imp.write() | | 0x0804873c b800000000 mov eax, 0x0 | ,==< 0x08048741 e915010000 jmp loc.0804885b | || ; JMP XREF from 0x08048716 (unk) |- loc.08048746 279 | |`-> 0x08048746 8b450c mov eax, [ebp+0xc] | | 0x08048749 83c004 add eax, 0x4 | | 0x0804874c 8b00 mov eax, [eax] | | 0x0804874e 890424 mov [esp], eax | | 0x08048751 e89afcffff call fcn.080483f0 | | fcn.080483f0() | | 0x08048756 8944243c mov [esp+0x3c], eax | | 0x0804875a 837c243c15 cmp dword [esp+0x3c], 0x15 | ,===< 0x0804875f 7426 je fcn.08048787 | || 0x08048761 c74424080b0. mov dword [esp+0x8], 0xb ; 0x0000000b | || 0x08048769 c7442404f08. mov dword [esp+0x4], str.Next_time__n ; str.Next_time__n | || 0x08048771 c7042401000. mov dword [esp], 0x1 ; 0x00000001 | || 0x08048778 e893fcffff call sym.imp.write | || sym.imp.write() | || 0x0804877d b800000000 mov eax, 0x0 | ,====< 0x08048782 e9d4000000 jmp loc.0804885b | || ; JMP XREF from 0x0804875f (unk) |- fcn.08048787 214 | |`---> 0x08048787 c7442438000. mov dword [esp+0x38], 0x0 | | | 0x0804878f c7442434000. mov dword [esp+0x34], 0x0 | ,=====< 0x08048797 eb6d jmp 0x8048806 ; (main) | ; JMP XREF from 0x0804880e (unk) |- fcn.08048799 196 | --------> 0x08048799 8b542438 mov edx, [esp+0x38] | || | 0x0804879d 8b44242c mov eax, [esp+0x2c] | || | 0x080487a1 01d0 add eax, edx | || | 0x080487a3 0fb600 movzx eax, byte [eax] | || | 0x080487a6 84c0 test al, al | ,======< 0x080487a8 7411 je loc.080487bb |- fcn.080487aa 179 | ||| | 0x080487aa 8b542438 mov edx, [esp+0x38] | ||| | 0x080487ae 8b44242c mov eax, [esp+0x2c] | ||| | 0x080487b2 01d0 add eax, edx | ||| | 0x080487b4 0fb600 movzx eax, byte [eax] | ||| | 0x080487b7 3cff cmp al, 0xff | ,=======< 0x080487b9 7507 jne 0x80487c2 | || ; JMP XREF from 0x080487a8 (unk) |- loc.080487bb 179 | |`------> 0x080487bb 8344243c01 add dword [esp+0x3c], 0x1 | ========< 0x080487c0 eb3f jmp 0x8048801 ; (main) | | ; JMP XREF from 0x080487b9 (unk) | `-------> 0x080487c2 8b542438 mov edx, [esp+0x38] | || | 0x080487c6 8b44242c mov eax, [esp+0x2c] | || | 0x080487ca 01d0 add eax, edx | || | 0x080487cc 0fb610 movzx edx, byte [eax] | || | 0x080487cf 8b450c mov eax, [ebp+0xc] | || | 0x080487d2 83c004 add eax, 0x4 | || | 0x080487d5 8b08 mov ecx, [eax] | || | 0x080487d7 8b442434 mov eax, [esp+0x34] | || | 0x080487db 01c8 add eax, ecx | || | 0x080487dd 0fb608 movzx ecx, byte [eax] | || | 0x080487e0 8b442434 mov eax, [esp+0x34] | || | 0x080487e4 05909b0408 add eax, 0x8049b90 | || | 0x080487e9 0fb600 movzx eax, byte [eax] | || | 0x080487ec 31c8 xor eax, ecx | || | 0x080487ee 38c2 cmp dl, al | ========< 0x080487f0 740a je 0x80487fc | || | 0x080487f2 c7442430010. mov dword [esp+0x30], 0x1 ; 0x00000001 | ========< 0x080487fa eb14 jmp loc.08048810 | ; JMP XREF from 0x080487f0 (unk) | --------> 0x080487fc 8344243401 add dword [esp+0x34], 0x1 | ; JMP XREF from 0x080487c0 (unk) | --------> 0x08048801 8344243801 add dword [esp+0x38], 0x1 | | ; JMP XREF from 0x08048797 (unk) | `-----> 0x08048806 8b442438 mov eax, [esp+0x38] | | | 0x0804880a 3b44243c cmp eax, [esp+0x3c] | ========< 0x0804880e 7c89 jl fcn.08048799 | ; JMP XREF from 0x080487fa (unk) |- loc.08048810 77 | --------> 0x08048810 837c243000 cmp dword [esp+0x30], 0x0 | ========< 0x08048815 7523 jne loc.0804883a | | | 0x08048817 c74424080a0. mov dword [esp+0x8], 0xa ; 0x0000000a | | | 0x0804881f c7442404fc8. mov dword [esp+0x4], str.Congrats__n ; str.Congrats__n | | | 0x08048827 c7042401000. mov dword [esp], 0x1 ; 0x00000001 | | | 0x0804882e e8ddfbffff call sym.imp.write | | | sym.imp.write() | | | 0x08048833 b801000000 mov eax, 0x1 ; 0x00000001 | ========< 0x08048838 eb21 jmp loc.0804885b | ; JMP XREF from 0x08048815 (unk) |- loc.0804883a 35 | --------> 0x0804883a c74424080b0. mov dword [esp+0x8], 0xb ; 0x0000000b | | | 0x08048842 c7442404f08. mov dword [esp+0x4], str.Next_time__n ; str.Next_time__n | | | 0x0804884a c7042401000. mov dword [esp], 0x1 ; 0x00000001 | | | 0x08048851 e8bafbffff call sym.imp.write | | | sym.imp.write() | | | 0x08048856 b800000000 mov eax, 0x0 | | | ; JMP XREF from 0x08048838 (unk) | | | ; JMP XREF from 0x08048782 (unk) | | | ; JMP XREF from 0x08048741 (unk) |- loc.0804885b 2 | ---`-`--> 0x0804885b c9 leave \ 0x0804885c c3 ret
From the lines 106 and 115, I can read respectively the Good boy and the Bad boy strings I found earlier when looking for strings.
Step by step
It will be more readable for you if I go slow and detail the steps.
I read the beginning of the main
function.
[0x08048450]> pd 25@main ;-- main: 0x080486c1 55 push ebp 0x080486c2 89e5 mov ebp, esp 0x080486c4 83e4f0 and esp, 0xfffffff0 0x080486c7 83ec40 sub esp, 0x40 0x080486ca c744242c508. mov dword [esp+0x2c], 0x8048550 ; 0x08048550 0x080486d2 c7442430000. mov dword [esp+0x30], 0x0 0x080486da c7442410557. mov dword [esp+0x10], 0x67617355 ; 0x67617355 0x080486e2 c7442414652. mov dword [esp+0x14], 0x203a2065 ; 0x203a2065 0x080486ea c74424182e2. mov dword [esp+0x18], 0x72632f2e ; 0x72632f2e 0x080486f2 c744241c616. mov dword [esp+0x1c], 0x6d6b6361 ; 0x6d6b6361 0x080486fa c7442420652. mov dword [esp+0x20], 0x61702065 ; 0x61702065 0x08048702 c7442424737. mov dword [esp+0x24], 0x6f777373 ; 0x6f777373 0x0804870a c7442428726. mov dword [esp+0x28], 0xa6472 ; 0x000a6472 0x08048712 837d0802 cmp dword [ebp+0x8], 0x2 ,=< 0x08048716 742e je 0x8048746 | 0x08048718 8d442410 lea eax, [esp+0x10] | 0x0804871c 890424 mov [esp], eax | 0x0804871f e8ccfcffff call 0x80483f0 | 0x080483f0(unk) ; sym.imp.kill | 0x08048724 89442408 mov [esp+0x8], eax | 0x08048728 8d442410 lea eax, [esp+0x10] | 0x0804872c 89442404 mov [esp+0x4], eax | 0x08048730 c7042401000. mov dword [esp], 0x1 ; 0x00000001 | 0x08048737 e8d4fcffff call sym.imp.write | 0x08048410() ; sym.imp.write | 0x0804873c b800000000 mov eax, 0x0 | 0x08048741 e915010000 jmp 0x804885b
Two things are pushed onto the stack. The first one seems to be a function address (line 7) and the second one a string (lines 7 to 15).
[0x0804a450]> pdf@0x8048550 / (fcn) fcn.08048520 113 | ... | ; CALL XREF from 0x080485c2 (fcn.08048591) | ; DATA XREF from 0x080486ca (unk) | 0x08048550 55 push ebp | 0x08048551 89e5 mov ebp, esp | 0x08048553 83ec28 sub esp, 0x28 | 0x08048556 c745f000000. mov dword [ebp-0x10], 0x0 | 0x0804855d c745f400000. mov dword [ebp-0xc], 0x0 | ,=====< 0x08048564 eb20 jmp 0x8048586 ; (fcn.08048520) | | ; JMP XREF from 0x0804858a (fcn.08048520) | | 0x08048566 c7442404010. mov dword [esp+0x4], 0x1 ; 0x00000001 | | 0x0804856e 8b45f4 mov eax, [ebp-0xc] | | 0x08048571 890424 mov [esp], eax | | 0x08048574 e8a7feffff call sym.imp.fcntl | | sym.imp.fcntl(unk) | | 0x08048579 83f8ff cmp eax, 0xffffffff | ,======< 0x0804857c 7404 je 0x8048582 | ||| 0x0804857e 8345f001 add dword [ebp-0x10], 0x1 | || ; JMP XREF from 0x0804857c (fcn.08048520) | |`------> 0x08048582 8345f401 add dword [ebp-0xc], 0x1 | | | ; JMP XREF from 0x08048564 (fcn.08048520) | | `-----> 0x08048586 837df40a cmp dword [ebp-0xc], 0xa | 0x0804858a 7eda jle 0x8048566 | 0x0804858c 8b45f0 mov eax, [ebp-0x10] | 0x0804858f c9 leave \ 0x08048590 c3 ret
The 0x8048550
function uses fcntl
but I don't really get what it does.
Meh, I will come back on that later if needed. I am more interested in that
handmade string.
[0x08048450]> pxw 64 @0x080486da 0x080486da 0x102444c7 0x67617355 0x142444c7 0x203a2065 .D$.Usag.D$.e : 0x080486ea 0x182444c7 0x72632f2e 0x1c2444c7 0x6d6b6361 .D$../cr.D$.ackm 0x080486fa 0x202444c7 0x61702065 0x242444c7 0x6f777373 .D$ e pa.D$$sswo 0x0804870a 0x282444c7 0x000a6472 0x02087d83 0x448d2e74 .D$(rd...}..t..D
All together, that gives Usage : ./crackme password
which is the usage
string. Moving on.
| |`-> 0x08048746 8b450c mov eax, [ebp+0xc] ; from the stack | | 0x08048749 83c004 add eax, 0x4 ; retrieve the param | | 0x0804874c 8b00 mov eax, [eax] ; and dereference it | | 0x0804874e 890424 mov [esp], eax ; for strlen | | 0x08048751 e89afcffff call fcn.080483f0 | | fcn.080483f0() | | 0x08048756 8944243c mov [esp+0x3c], eax ; and save the result in esp+0x3c | | 0x0804875a 837c243c15 cmp dword [esp+0x3c], 0x15 | ,===< 0x0804875f 7426 je fcn.08048787 | || 0x08048761 c74424080b0. mov dword [esp+0x8], 0xb ; 0x0000000b | || 0x08048769 c7442404f08. mov dword [esp+0x4], str.Next_time__n ; str.Next_time__n | || 0x08048771 c7042401000. mov dword [esp], 0x1 ; 0x00000001 | || 0x08048778 e893fcffff call sym.imp.write | || sym.imp.write() | || 0x0804877d b800000000 mov eax, 0x0
Then the binary retrieves my input, calls a function and compares its result
with 0x15
(21). fcn.080483f0
is also called elsewhere in the code and if
I check the got entries, it is in fact strlen
. Let's rename it:
[0x08048450]> afn? Usage: afn newname [off] # set new name to given function [0x08048450]> afn strlen 0x080483f0 fr fcn.080483f0 strlen@ 0x80483f0
It is much more readable now:
| | 0x0804874e 890424 mov [esp], eax | | 0x08048751 e89afcffff call strlen | | strlen() | | 0x08048756 8944243c mov [esp+0x3c], eax | | 0x0804875a 837c243c15 cmp dword [esp+0x3c], 0x15 | ,===< 0x0804875f 7426 je fcn.08048787
Therefore the key has to be 21 characters long otherwise I would not take the jump but reach the bad boy instead.
| | 0x0804875a 837c243c15 cmp dword [esp+0x3c], 0x15 ; 21 chars long. | ,===< 0x0804875f 7426 je fcn.08048787 | ; . . . | |`---> 0x08048787 c7442438000. mov dword [esp+0x38], 0x0 ; init counter i. | | | 0x0804878f c7442434000. mov dword [esp+0x34], 0x0 ; init counter j. | ,=====< 0x08048797 eb6d jmp 0x8048806 ; (main) | ; . . . | `-----> 0x08048806 8b442438 mov eax, [esp+0x38] ; retrieve counter i. | | | 0x0804880a 3b44243c cmp eax, [esp+0x3c] ; compare it to 21. | ========< 0x0804880e 7c89 jl fcn.08048799
Then it enters into a loop which stops when 21 is reached. It it most likely going to check my key, character by character.
Crypto 101
I should analyze what this loop does.
| `-------> 0x080487c2 8b542438 mov edx, [esp+0x38] ; counter i. | || | 0x080487c6 8b44242c mov eax, [esp+0x2c] ; the mysterious function address, remember? | || | 0x080487ca 01d0 add eax, edx | || | 0x080487cc 0fb610 movzx edx, byte [eax] ; edx=function[i] - process next char (wut?) | || | 0x080487cf 8b450c mov eax, [ebp+0xc] | || | 0x080487d2 83c004 add eax, 0x4 | || | 0x080487d5 8b08 mov ecx, [eax] | || | 0x080487d7 8b442434 mov eax, [esp+0x34] ; counter j. | || | 0x080487db 01c8 add eax, ecx | || | 0x080487dd 0fb608 movzx ecx, byte [eax] ; ecx=input[j] - same with the user input. | || | 0x080487e0 8b442434 mov eax, [esp+0x34] ; counter j again. | || | 0x080487e4 05909b0408 add eax, 0x8049b90 ; the encrypted flag. | || | 0x080487e9 0fb600 movzx eax, byte [eax] ; eax=flag[j] - next char of encrypted flag. | || | 0x080487ec 31c8 xor eax, ecx ; xor user input and encrypted flag. | || | 0x080487ee 38c2 cmp dl, al ; check if equals to byte of function. | ========< 0x080487f0 740a je 0x80487fc
The first thing that is weird is the line 4. Why? Because the $edx
register
contains the dereferenced value of $eax
which is somewhere in the
0x8048550
code (line 2).
So this loop iterates over each character of my input the and encrypted flag, xor them and checks if the result gives the byte from the mysterious function (line 12).
Since A xor B = C <=> A xor C = B
I can retrieve the valid key. Let's ask
radare2 to print A (the mysterious function byte codes) and C (the encrypted
flag).
First the encrypted flag.
[0x0804a450]> p8 0x15@0x8049b90 34d6a8e28877aa049e983382da548f1b455b37bb1d [0x0804a450]> pcp 0x15@0x8049b90 import struct buf = struct.pack ("21B", 0x34,0xd6,0xa8,0xe2,0x88,0x77,0xaa,0x04,0x9e,0x98,0x33, 0x82,0xda,0x54,0x8f,0x1b,0x45,0x5b,0x37,0xbb,0x1d)
Then the function. I should be careful because of the line 6 not all bytes are used. Therefore I only keep the non-0x00 ones.
[0x0804a450]> pcp 0x20@0x8048550 import struct buf = struct.pack ("32B", 0x55,0x89,0xe5,0x83,0xzz,0xzz,0xzz,0xzz,0xf0,0x00,0x00, 0x00,0x00,0xc7,0x45,0xf4,0x00,0x00,0x00,0x00,0xeb,0x20, 0xc7,0x44,0x24,0x04,0x01,0x00,0x00,0x00,0xzz,0xzz)
(I redacted some bytes in order to avoid a complete spoil).
With radare2, I can even operate some xor operations from the prompt!
First I need to configure radare2 so it can have a write access to the binary.
[0x08048450]> e? |Usage: e[?] [var[=value]]Evaluable vars ... | e?? list config vars with description ... [0x08048450]> e?? ... io.cache: Enable cache for io changes ... [0x08048450]> e io.cache = 1
The io.cache
will not permanently save the modification.
[0x08048450]> wo? |Usage: wo[asmdxoArl24] [hexpairs] @ addr[:bsize] |Example: | wox 0x90 ; xor cur block with 0x90 | wox 90 ; xor cur block with 0x90 | wox 0x0203 ; xor cur block with 0203 | woa 02 03 ; add [0203][0203][...] to curblk | woe 02 03 |Supported operations: ... | wox ^= xor ... [0x08048450]> wox 0x34d6a8e2 @ 0x8048550 [0x08048450]> pxw 4 @0x8048550 0x08048550 0x614d5f61 a_Ma
That really looks like the beginning of a flag. Let's put that in a script.
import struct encrypted = struct.pack ("21B", 0x34,0xd6,0xa8,0xe2,0x88,0x77,0xaa,0x04,0x9e,0x98,0x33, 0x82,0xda,0x54,0x8f,0x1b,0x45,0x5b,0x37,0xbb,0x1d) key = struct.pack ("21B", 0x55,0x89,0xe5,0x83,0xzz,0xzz,0xzz,0xzz,0xf0,0xc7,0x45, 0xf4,0xeb,0x20,0xc7,0x44,0x24,0x04,0x01,0xzz,0xzz) print(''.join([chr(a ^ c) for a, c in zip(encrypted, key)]))
Hence the flag: a_Ma????n_vv1tH_a_6??
Conclusion
That was the first time I used radare2 to do some reverse. My first impression? It is really awesome.
Usually I take my gdb to do re stuff but radare2 is way (way) more user friendly (when run without using any custom configuration file because gdb with peda is handy as hell too).
I was also able to quickly find out how to rename a function and it gave me less headache than with gdb.
But radare2 offers even more with functions like pcp
which are really
awesome! Finding the pcp
function in a couple of second after running
radare2 was a really nice surprise and I can't wait to see what it can offer
more!
Plus, you can operate basic operations from within radare2 using wo
! It is
really helpful if you want to be sure before coding your script :)
Long story short? I can't wait to use radare2 again!